Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,26 @@ This exporter allows you to use a field of the metric as the (unix/epoch) timest

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)

## Custom request headers

You can set per-module request headers with `modules.<module_name>.headers`.

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.

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.

Example:

```yaml
modules:
ip_with_host_routing:
headers:
Host: my-service.example.com
http_client_config:
tls_config:
server_name: my-service.example.com
```

## Sending body content for HTTP `POST`

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.
Expand Down
33 changes: 33 additions & 0 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,39 @@ func TestHTTPHeaders(t *testing.T) {
}
}

func TestHostHeaderSetsRequestHost(t *testing.T) {
hostHeader := "integration-service.preventicedev.com"

target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got := r.Host; got != hostHeader {
t.Errorf("Unexpected request host: expected %q, got %q", hostHeader, got)
}
w.WriteHeader(http.StatusOK)
}))
defer target.Close()

req := httptest.NewRequest("GET", "http://example.com/foo"+"?module=default&target="+target.URL, nil)
recorder := httptest.NewRecorder()
c := config.Config{
Modules: map[string]config.Module{
"default": {
Headers: map[string]string{
"Host": hostHeader,
},
},
},
}

probeHandler(recorder, req, promslog.NewNopLogger(), c)

resp := recorder.Result()
body, _ := io.ReadAll(resp.Body)

if resp.StatusCode != http.StatusOK {
t.Fatalf("Setting host header failed unexpectedly. Got: %s", body)
}
}

// Test is the body template is correctly rendered
func TestBodyPostTemplate(t *testing.T) {
bodyTests := []struct {
Expand Down
4 changes: 4 additions & 0 deletions exporter/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ func (f *JSONFetcher) FetchJSON(endpoint string) ([]byte, error) {
}

for key, value := range f.module.Headers {
if strings.EqualFold(key, "Host") {
req.Host = value
continue
}
req.Header.Add(key, value)
}
if req.Header.Get("Accept") == "" {
Expand Down