diff --git a/config/config.go b/config/config.go index 2bd5d946..aac188cd 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io/ioutil" + "os" "runtime" "sync" "time" @@ -52,19 +53,21 @@ type Module struct { type HTTPProbe struct { // Defaults to 2xx. - ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"` - ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"` - IPProtocol string `yaml:"preferred_ip_protocol,omitempty"` - IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"` - NoFollowRedirects bool `yaml:"no_follow_redirects,omitempty"` - FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` - FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` - Method string `yaml:"method,omitempty"` - Headers map[string]string `yaml:"headers,omitempty"` - FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` - FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` - Body string `yaml:"body,omitempty"` - HTTPClientConfig config.HTTPClientConfig `yaml:"http_client_config,inline"` + ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"` + ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"` + IPProtocol string `yaml:"preferred_ip_protocol,omitempty"` + IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"` + NoFollowRedirects bool `yaml:"no_follow_redirects,omitempty"` + FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` + FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` + Method string `yaml:"method,omitempty"` + Headers map[string]string `yaml:"headers,omitempty"` + FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` + FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` + Body string `yaml:"body,omitempty"` + BodyFile string `yaml:"body_file,omitempty"` + + HTTPClientConfig config.HTTPClientConfig `yaml:"http_client_config,inline"` } type QueryResponse struct { @@ -135,6 +138,16 @@ func (s *HTTPProbe) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := s.HTTPClientConfig.Validate(); err != nil { return err } + if len(s.Body) > 0 && len(s.BodyFile) > 0 { + return errors.New("at most one of body & body_file may be configured") + } + if s.BodyFile != "" { + file, err := os.Open(s.BodyFile) + if err != nil { + return err + } + defer file.Close() + } return nil } diff --git a/prober/http.go b/prober/http.go index 542f3129..7f647aef 100644 --- a/prober/http.go +++ b/prober/http.go @@ -24,6 +24,7 @@ import ( "net/http/cookiejar" "net/http/httptrace" "net/url" + "os" "regexp" "strconv" "strings" @@ -257,10 +258,20 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr var body io.Reader - // If a body is configured, add it to the request. + // If a body string is configured, add it to the request. if httpConfig.Body != "" { body = strings.NewReader(httpConfig.Body) } + // If a body path is configured, add it to the request. + if httpConfig.BodyFile != "" { + file, err := os.Open(httpConfig.BodyFile) + if err != nil { + level.Error(logger).Log("msg", "Error opening body file", "err", err) + return + } + defer file.Close() + body = file + } request, err := http.NewRequest(httpConfig.Method, targetURL.String(), body) request.Host = origHost diff --git a/prober/http_test.go b/prober/http_test.go index a7deaa89..d65037ec 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -164,6 +164,33 @@ func TestPost(t *testing.T) { } } +func TestPostBody(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(http.StatusBadRequest) + } + })) + defer ts.Close() + + recorder := httptest.NewRecorder() + registry := prometheus.NewRegistry() + testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + result := ProbeHTTP(testCTX, ts.URL, + config.Module{ + Timeout: time.Second, + HTTP: config.HTTPProbe{ + IPProtocolFallback: true, + Method: "POST", + BodyFile: "./testdata/http_body.txt", + }, + }, registry, log.NewNopLogger()) + body := recorder.Body.String() + if !result { + t.Fatalf("Post test failed unexpectedly, got %s", body) + } +} + func TestBasicAuth(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { })) diff --git a/prober/testdata/http_body.txt b/prober/testdata/http_body.txt new file mode 100644 index 00000000..4bc5dd81 --- /dev/null +++ b/prober/testdata/http_body.txt @@ -0,0 +1 @@ +TEST HTTP BODY \ No newline at end of file