Skip to content

Commit 953b9b5

Browse files
committed
Implement custom headers: #10200
Allows specifying extra headers to send on the command line
1 parent 0edc65d commit 953b9b5

File tree

3 files changed

+42
-0
lines changed

3 files changed

+42
-0
lines changed

cmd/logcli/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ func newQueryClient(app *kingpin.Application) client.Client {
593593
app.Flag("proxy-url", "The http or https proxy to use when making requests. Can also be set using LOKI_HTTP_PROXY_URL env var.").Default("").Envar("LOKI_HTTP_PROXY_URL").StringVar(&client.ProxyURL)
594594
app.Flag("compress", "Request that Loki compress returned data in transit. Can also be set using LOKI_HTTP_COMPRESSION env var.").Default("false").Envar("LOKI_HTTP_COMPRESSION").BoolVar(&client.Compression)
595595
app.Flag("envproxy", "Use ProxyFromEnvironment to use net/http ProxyFromEnvironment configuration, eg HTTP_PROXY").Default("false").Envar("LOKI_ENV_PROXY").BoolVar(&client.EnvironmentProxy)
596+
app.Flag("header", "Add custom HTTP headers to requests. Can be specified multiple times. Format: 'Header-Name: value'").StringsVar(&client.CustomHeaders)
596597

597598
return client
598599
}

pkg/logcli/client/client.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ type DefaultClient struct {
9494
BackoffConfig BackoffConfig
9595
Compression bool
9696
EnvironmentProxy bool
97+
CustomHeaders []string
9798
}
9899

99100
// Query uses the /api/v1/query endpoint to execute an instant query
@@ -648,6 +649,22 @@ func (c *DefaultClient) getHTTPRequestHeader() (http.Header, error) {
648649
h.Set(HTTPQueryTags, c.QueryTags)
649650
}
650651

652+
// Add custom headers
653+
if c.CustomHeaders != nil {
654+
for _, header := range c.CustomHeaders {
655+
parts := strings.SplitN(header, ":", 2)
656+
if len(parts) != 2 {
657+
return nil, fmt.Errorf("invalid header format: %q. Expected format: 'Header-Name: value'", header)
658+
}
659+
key := strings.TrimSpace(parts[0])
660+
value := strings.TrimSpace(parts[1])
661+
if key == "" {
662+
return nil, fmt.Errorf("header name cannot be empty in header: %q", header)
663+
}
664+
h.Set(key, value)
665+
}
666+
}
667+
651668
if (c.Username != "" || c.Password != "") && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) {
652669
return nil, fmt.Errorf("at most one of HTTP basic auth (username/password), bearer-token & bearer-token-file is allowed to be configured")
653670
}

pkg/logcli/client/client_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ func Test_getHTTPRequestHeader(t *testing.T) {
5959
}, http.Header{
6060
"Authorization": []string{"Bearer " + "secureToken"},
6161
}, false},
62+
{"custom-headers", DefaultClient{
63+
CustomHeaders: []string{"X-Custom-Header: custom-value", "X-Another-Header: another-value"},
64+
}, http.Header{
65+
"X-Custom-Header": []string{"custom-value"},
66+
"X-Another-Header": []string{"another-value"},
67+
}, false},
68+
{"custom-headers-with-spaces", DefaultClient{
69+
CustomHeaders: []string{"X-Custom-Header: custom-value ", "X-Another-Header: another-value "},
70+
}, http.Header{
71+
"X-Custom-Header": []string{"custom-value"},
72+
"X-Another-Header": []string{"another-value"},
73+
}, false},
74+
{"custom-headers-invalid-format", DefaultClient{
75+
CustomHeaders: []string{"InvalidHeader"},
76+
}, nil, true},
77+
{"custom-headers-empty-name", DefaultClient{
78+
CustomHeaders: []string{" : value"},
79+
}, nil, true},
6280
}
6381
for _, tt := range tests {
6482
t.Run(tt.name, func(t *testing.T) {
@@ -68,6 +86,12 @@ func Test_getHTTPRequestHeader(t *testing.T) {
6886
return
6987
}
7088

89+
// If we expect an error, we shouldn't have any headers
90+
if tt.wantErr {
91+
assert.Nil(t, got)
92+
return
93+
}
94+
7195
// User-Agent should be set all the time.
7296
assert.Equal(t, got["User-Agent"], []string{userAgent})
7397

0 commit comments

Comments
 (0)