From: Julien Pivotto Date: Wed, 13 Jul 2022 12:15:28 +0000 (+0200) Subject: HTTP: Add Skip Resolve Phase With Proxy option (#944) X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=d396ff7f6a1a4595effb216fa7e3053440216859;p=blackbox_exporter.git HTTP: Add Skip Resolve Phase With Proxy option (#944) * HTTP: Add Skip Resolve Phase With Proxy option Signed-off-by: Julien Pivotto Co-authored-by: Suraj Nath <9503187+electron0zero@users.noreply.github.com> --- diff --git a/CONFIGURATION.md b/CONFIGURATION.md index 35e8446..1143c89 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -114,6 +114,9 @@ The other placeholders are specified separately. # HTTP proxy server to use to connect to the targets. [ proxy_url: ] + # Skip DNS resolution and URL change when an HTTP proxy (proxy_url) is set. + [ skip_resolve_phase_with_proxy: | default = false ] + # OAuth 2.0 configuration to use to connect to the targets. oauth2: [ ] @@ -128,7 +131,6 @@ The other placeholders are specified separately. # The body of the HTTP request used in probe. body: [ ] - ``` #### diff --git a/config/config.go b/config/config.go index 8094b29..7a23824 100644 --- a/config/config.go +++ b/config/config.go @@ -210,6 +210,7 @@ type HTTPProbe struct { ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"` IPProtocol string `yaml:"preferred_ip_protocol,omitempty"` IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"` + SkipResolvePhaseWithProxy bool `yaml:"skip_resolve_phase_with_proxy,omitempty"` NoFollowRedirects *bool `yaml:"no_follow_redirects,omitempty"` FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` diff --git a/prober/http.go b/prober/http.go index 99d38a2..802bdc9 100644 --- a/prober/http.go +++ b/prober/http.go @@ -333,11 +333,15 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr targetHost := targetURL.Hostname() targetPort := targetURL.Port() - ip, lookupTime, err := chooseProtocol(ctx, module.HTTP.IPProtocol, module.HTTP.IPProtocolFallback, targetHost, registry, logger) - durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) - if err != nil { - level.Error(logger).Log("msg", "Error resolving address", "err", err) - return false + var ip *net.IPAddr + if !module.HTTP.SkipResolvePhaseWithProxy || module.HTTP.HTTPClientConfig.ProxyURL.URL == nil { + var lookupTime float64 + ip, lookupTime, err = chooseProtocol(ctx, module.HTTP.IPProtocol, module.HTTP.IPProtocolFallback, targetHost, registry, logger) + durationGaugeVec.WithLabelValues("resolve").Add(lookupTime) + if err != nil { + level.Error(logger).Log("msg", "Error resolving address", "err", err) + return false + } } // Do not move the following variable to global scope. The cases.Caser returned by @@ -404,16 +408,18 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr httpConfig.Method = "GET" } - // Replace the host field in the URL with the IP we resolved. origHost := targetURL.Host - if targetPort == "" { - if strings.Contains(ip.String(), ":") { - targetURL.Host = "[" + ip.String() + "]" + if ip != nil { + // Replace the host field in the URL with the IP we resolved. + if targetPort == "" { + if strings.Contains(ip.String(), ":") { + targetURL.Host = "[" + ip.String() + "]" + } else { + targetURL.Host = ip.String() + } } else { - targetURL.Host = ip.String() + targetURL.Host = net.JoinHostPort(ip.String(), targetPort) } - } else { - targetURL.Host = net.JoinHostPort(ip.String(), targetPort) } var body io.Reader diff --git a/prober/http_test.go b/prober/http_test.go index cd27761..078475c 100644 --- a/prober/http_test.go +++ b/prober/http_test.go @@ -25,6 +25,7 @@ import ( "io/ioutil" "net/http" "net/http/httptest" + "net/url" "os" "strconv" "strings" @@ -1338,3 +1339,72 @@ func TestCookieJar(t *testing.T) { t.Fatalf("Redirect test failed unexpectedly, got %s", body) } } + +func TestSkipResolvePhase(t *testing.T) { + if testing.Short() { + t.Skip("skipping network dependent test") + } + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNoContent) + })) + defer ts.Close() + + t.Run("Without Proxy", func(t *testing.T) { + 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, HTTPClientConfig: pconfig.DefaultHTTPClientConfig, SkipResolvePhaseWithProxy: true}}, registry, log.NewNopLogger()) + if !result { + t.Fatalf("Probe unsuccessful") + } + mfs, err := registry.Gather() + if err != nil { + t.Fatal(err) + } + expectedMetrics := map[string]map[string]map[string]struct{}{ + "probe_http_duration_seconds": { + "phase": { + "connect": {}, + "processing": {}, + "resolve": {}, + "transfer": {}, + "tls": {}, + }, + }, + } + + checkMetrics(expectedMetrics, mfs, t) + }) + t.Run("With Proxy", func(t *testing.T) { + registry := prometheus.NewRegistry() + testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + httpCfg := pconfig.DefaultHTTPClientConfig + u, err := url.Parse("http://127.0.0.1:3128") + if err != nil { + t.Fatalf(err.Error()) + } + httpCfg.ProxyURL = pconfig.URL{ + URL: u, + } + ProbeHTTP(testCTX, ts.URL, + config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: httpCfg, SkipResolvePhaseWithProxy: true}}, registry, log.NewNopLogger()) + mfs, err := registry.Gather() + if err != nil { + t.Fatal(err) + } + expectedMetrics := map[string]map[string]map[string]struct{}{ + "probe_http_duration_seconds": { + "phase": { + "connect": {}, + "processing": {}, + "transfer": {}, + "tls": {}, + }, + }, + } + + checkMetrics(expectedMetrics, mfs, t) + }) +}