// transport is a custom transport keeping traces for each HTTP roundtrip.
type transport struct {
- Transport http.RoundTripper
- logger log.Logger
- traces []*roundTripTrace
- current *roundTripTrace
+ Transport http.RoundTripper
+ NoServerNameTransport http.RoundTripper
+ firstHost string
+ logger log.Logger
+ traces []*roundTripTrace
+ current *roundTripTrace
}
-func newTransport(rt http.RoundTripper, logger log.Logger) *transport {
+func newTransport(rt, noServerName http.RoundTripper, logger log.Logger) *transport {
return &transport{
- Transport: rt,
- logger: logger,
- traces: []*roundTripTrace{},
+ Transport: rt,
+ NoServerNameTransport: noServerName,
+ logger: logger,
+ traces: []*roundTripTrace{},
}
}
// RoundTrip switches to a new trace, then runs embedded RoundTripper.
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
+ level.Info(t.logger).Log("msg", "Making HTTP request", "url", req.URL.String(), "host", req.Host)
+
trace := &roundTripTrace{}
if req.URL.Scheme == "https" {
trace.tls = true
}
t.current = trace
t.traces = append(t.traces, trace)
+
+ if t.firstHost == "" {
+ t.firstHost = req.URL.Host
+ }
+
+ if t.firstHost != req.URL.Host {
+ // This is a redirect to something other than the initial host,
+ // so TLS ServerName should not be set.
+ level.Info(t.logger).Log("msg", "Address does not match first address, not sending TLS ServerName", "first", t.firstHost, "address", req.URL.Host)
+ return t.NoServerNameTransport.RoundTrip(req)
+ }
+
return t.Transport.RoundTrip(req)
}
return false
}
+ httpClientConfig.TLSConfig.ServerName = ""
+ noServerName, err := pconfig.NewRoundTripperFromConfig(httpClientConfig, "http_probe", true)
+ if err != nil {
+ level.Error(logger).Log("msg", "Error generating HTTP client without ServerName", "err", err)
+ return false
+ }
+
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
level.Error(logger).Log("msg", "Error generating cookiejar", "err", err)
}
client.Jar = jar
- // Inject transport that tracks trace for each redirect.
- tt := newTransport(client.Transport, logger)
+ // Inject transport that tracks traces for each redirect,
+ // and does not set TLS ServerNames on redirect if needed.
+ tt := newTransport(client.Transport, noServerName, logger)
client.Transport = tt
client.CheckRedirect = func(r *http.Request, via []*http.Request) error {
- level.Info(logger).Log("msg", "Received redirect", "url", r.URL.String())
+ level.Info(logger).Log("msg", "Received redirect", "location", r.Response.Header.Get("Location"))
redirects = len(via)
if redirects > 10 || httpConfig.NoFollowRedirects {
level.Info(logger).Log("msg", "Not following redirect")
request.Header.Set(key, value)
}
- level.Info(logger).Log("msg", "Making HTTP request", "url", request.URL.String(), "host", request.Host)
-
trace := &httptrace.ClientTrace{
DNSStart: tt.DNSStart,
DNSDone: tt.DNSDone,
}
}
+func TestRedirectToTLSHostWorks(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping network dependant test")
+ }
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.Redirect(w, r, "https://prometheus.io", http.StatusFound)
+ }))
+ defer ts.Close()
+
+ // Follow redirect, should succeed with 200.
+ 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}}, registry, log.NewNopLogger())
+ if !result {
+ t.Fatalf("Redirect test failed unexpectedly")
+ }
+
+}
+
func TestHTTPPhases(t *testing.T) {
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
}))