Skip to content

Commit 4daa652

Browse files
Set request host authority from Host header
Apply special handling for configured Host by setting req.Host, add regression test coverage, and document header behavior and SNI guidance in README. Signed-off-by: Jeremiah Harbach <jeremiah.harbach@gmail.com>
1 parent f5e4661 commit 4daa652

3 files changed

Lines changed: 57 additions & 0 deletions

File tree

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,26 @@ This exporter allows you to use a field of the metric as the (unix/epoch) timest
6868

6969
TLS configuration supported by this exporter can be found at [exporter-toolkit/web](https://github.com/prometheus/exporter-toolkit/blob/v0.9.0/docs/web-configuration.md)
7070

71+
## Custom request headers
72+
73+
You can set per-module request headers with `modules.<module_name>.headers`.
74+
75+
The `Host` header is handled specially. If a module config contains `Host`, the exporter sets the request host authority (`req.Host`) to that value. This is useful when probing an IP target while routing by hostname through a gateway or proxy.
76+
77+
When probing HTTPS endpoints by IP and overriding host authority, also set TLS server name under `http_client_config.tls_config.server_name` so SNI and certificate validation use the same hostname.
78+
79+
Example:
80+
81+
```yaml
82+
modules:
83+
ip_with_host_routing:
84+
headers:
85+
Host: my-service.example.com
86+
http_client_config:
87+
tls_config:
88+
server_name: my-service.example.com
89+
```
90+
7191
## Sending body content for HTTP `POST`
7292

7393
If `modules.<module_name>.body` paramater is set in config, it will be sent by the exporter as the body content in the scrape request. The HTTP method will also be set as 'POST' in this case.

cmd/main_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,39 @@ func TestHTTPHeaders(t *testing.T) {
307307
}
308308
}
309309

310+
func TestHostHeaderSetsRequestHost(t *testing.T) {
311+
hostHeader := "integration-service.preventicedev.com"
312+
313+
target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
314+
if got := r.Host; got != hostHeader {
315+
t.Errorf("Unexpected request host: expected %q, got %q", hostHeader, got)
316+
}
317+
w.WriteHeader(http.StatusOK)
318+
}))
319+
defer target.Close()
320+
321+
req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
322+
recorder := httptest.NewRecorder()
323+
c := config.Config{
324+
Modules: map[string]config.Module{
325+
"default": {
326+
Headers: map[string]string{
327+
"Host": hostHeader,
328+
},
329+
},
330+
},
331+
}
332+
333+
probeHandler(recorder, req, promslog.NewNopLogger(), c)
334+
335+
resp := recorder.Result()
336+
body, _ := io.ReadAll(resp.Body)
337+
338+
if resp.StatusCode != http.StatusOK {
339+
t.Fatalf("Setting host header failed unexpectedly. Got: %s", body)
340+
}
341+
}
342+
310343
// Test is the body template is correctly rendered
311344
func TestBodyPostTemplate(t *testing.T) {
312345
bodyTests := []struct {

exporter/util.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ func (f *JSONFetcher) FetchJSON(endpoint string) ([]byte, error) {
177177
}
178178

179179
for key, value := range f.module.Headers {
180+
if strings.EqualFold(key, "Host") {
181+
req.Host = value
182+
continue
183+
}
180184
req.Header.Add(key, value)
181185
}
182186
if req.Header.Get("Accept") == "" {

0 commit comments

Comments
 (0)