From 1051f6ed68e810b6d61313304375e238fab7294d Mon Sep 17 00:00:00 2001 From: Mayank Raj Date: Mon, 26 Oct 2026 09:46:24 +0530 Subject: [PATCH] fix(dial): keep the default redirect limit when CheckRedirect is nil Dial always installs a CheckRedirect wrapper to rewrite ws/wss schemes, which disables net/http's default 10-redirect limit, so a client without its own CheckRedirect followed redirects without bound. The wrapper now applies the same limit when it has no callback to delegate to. --- dial.go | 5 +++++ dial_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/dial.go b/dial.go index f5e4544b..652f2443 100644 --- a/dial.go +++ b/dial.go @@ -8,6 +8,7 @@ import ( "context" "crypto/rand" "encoding/base64" + "errors" "fmt" "io" "net/http" @@ -97,6 +98,10 @@ func (opts *DialOptions) cloneWithDefaults(ctx context.Context) (context.Context if oldCheckRedirect != nil { return oldCheckRedirect(req, via) } + // net/http only applies its default limit when CheckRedirect is nil. + if len(via) >= 10 { + return errors.New("stopped after 10 redirects") + } return nil } o.HTTPClient = &newClient diff --git a/dial_test.go b/dial_test.go index 492ac6b3..96fd51d6 100644 --- a/dial_test.go +++ b/dial_test.go @@ -333,6 +333,33 @@ func TestDialRedirect(t *testing.T) { assert.Contains(t, err, "failed to WebSocket dial: WebSocket protocol violation: Upgrade header \"meow\" does not contain websocket") } +func TestDialRedirectLimit(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + var requests int + _, _, err := websocket.Dial(ctx, "ws://example.com", &websocket.DialOptions{ + HTTPClient: mockHTTPClient(func(r *http.Request) (*http.Response, error) { + requests++ + resp := &http.Response{ + Header: http.Header{}, + Body: http.NoBody, + } + if requests > 20 { // Bound the test if the limit is missing. + resp.StatusCode = http.StatusNotFound + return resp, nil + } + resp.Header.Set("Location", "wss://example.com") + resp.StatusCode = http.StatusFound + return resp, nil + }), + }) + assert.Contains(t, err, "stopped after 10 redirects") + assert.Equal(t, "requests", 10, requests) +} + type forwardProxy struct { hc *http.Client }