From 83b3c340b0dce219fe65a4dbca9c81bd9ea6dd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sam=E7=9A=84=E7=94=B5=E8=84=91?= Date: Wed, 25 Feb 2026 12:58:14 +0800 Subject: [PATCH 1/4] fix(httpx): enforce http11 retry path to avoid h2 fallback (#2240) --- common/httpx/httpx.go | 4 ++++ common/httpx/httpx_test.go | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 039f4c4ca..98dc35682 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -182,6 +182,10 @@ func New(options *Options) (*HTTPX, error) { Timeout: httpx.Options.Timeout, CheckRedirect: redirectFunc, }, retryablehttpOptions) + if httpx.Options.Protocol == HTTP11 { + // Keep retryablehttp-go on HTTP/1.1 as well when it retries internally. + httpx.client.HTTPClient2 = httpx.client.HTTPClient + } transport2 := &http2.Transport{ TLSClientConfig: &tls.Config{ diff --git a/common/httpx/httpx_test.go b/common/httpx/httpx_test.go index 7da6ad12d..ffd6a37bf 100644 --- a/common/httpx/httpx_test.go +++ b/common/httpx/httpx_test.go @@ -28,3 +28,12 @@ func TestDo(t *testing.T) { require.Greater(t, len(resp.Raw), 800) }) } + +func TestHTTP11DisablesRetryableHTTP2Fallback(t *testing.T) { + opts := DefaultOptions + opts.Protocol = HTTP11 + + ht, err := New(&opts) + require.NoError(t, err) + require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2) +} From ba8ea42c1859e2eb2995f9618e1bc9d6e812a903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sam=E7=9A=84=E7=94=B5=E8=84=91?= Date: Wed, 4 Mar 2026 19:23:00 +0800 Subject: [PATCH 2/4] test(httpx): restore GODEBUG in http11 fallback test isolation --- common/httpx/httpx_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/httpx/httpx_test.go b/common/httpx/httpx_test.go index ffd6a37bf..e403a7ad3 100644 --- a/common/httpx/httpx_test.go +++ b/common/httpx/httpx_test.go @@ -2,6 +2,7 @@ package httpx import ( "net/http" + "os" "testing" "github.com/projectdiscovery/retryablehttp-go" @@ -33,6 +34,19 @@ func TestHTTP11DisablesRetryableHTTP2Fallback(t *testing.T) { opts := DefaultOptions opts.Protocol = HTTP11 + originalGODEBUG, hadGODEBUG := os.LookupEnv("GODEBUG") + t.Cleanup(func() { + if hadGODEBUG { + if err := os.Setenv("GODEBUG", originalGODEBUG); err != nil { + t.Fatalf("failed to restore GODEBUG: %v", err) + } + return + } + if err := os.Unsetenv("GODEBUG"); err != nil { + t.Fatalf("failed to unset GODEBUG: %v", err) + } + }) + ht, err := New(&opts) require.NoError(t, err) require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2) From e1550f8853fe96ec4cbfcab3328d7908bb01ce93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sam=E7=9A=84=E7=94=B5=E8=84=91?= Date: Thu, 5 Mar 2026 05:18:57 +0800 Subject: [PATCH 3/4] test(httpx): harden http11 fallback regression coverage --- common/httpx/httpx.go | 4 +--- common/httpx/httpx_test.go | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 98dc35682..79b089d14 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -8,7 +8,6 @@ import ( "net" "net/http" "net/url" - "os" "strconv" "strings" "time" @@ -153,9 +152,8 @@ func New(options *Options) (*HTTPX, error) { DisableKeepAlives: true, } - if httpx.Options.Protocol == "http11" { + if httpx.Options.Protocol == HTTP11 { // disable http2 - _ = os.Setenv("GODEBUG", "http2client=0") transport.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{} } diff --git a/common/httpx/httpx_test.go b/common/httpx/httpx_test.go index e403a7ad3..6762de302 100644 --- a/common/httpx/httpx_test.go +++ b/common/httpx/httpx_test.go @@ -2,7 +2,6 @@ package httpx import ( "net/http" - "os" "testing" "github.com/projectdiscovery/retryablehttp-go" @@ -34,20 +33,15 @@ func TestHTTP11DisablesRetryableHTTP2Fallback(t *testing.T) { opts := DefaultOptions opts.Protocol = HTTP11 - originalGODEBUG, hadGODEBUG := os.LookupEnv("GODEBUG") - t.Cleanup(func() { - if hadGODEBUG { - if err := os.Setenv("GODEBUG", originalGODEBUG); err != nil { - t.Fatalf("failed to restore GODEBUG: %v", err) - } - return - } - if err := os.Unsetenv("GODEBUG"); err != nil { - t.Fatalf("failed to unset GODEBUG: %v", err) - } - }) - ht, err := New(&opts) require.NoError(t, err) require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2) } + +func TestDefaultProtocolKeepsRetryableHTTP2Fallback(t *testing.T) { + opts := DefaultOptions + + ht, err := New(&opts) + require.NoError(t, err) + require.NotSame(t, ht.client.HTTPClient, ht.client.HTTPClient2) +} From 8542673ac5fb49739d37a60f45ca1c6b569b25a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?sam=E7=9A=84=E7=94=B5=E8=84=91?= Date: Thu, 5 Mar 2026 05:36:06 +0800 Subject: [PATCH 4/4] fix(httpx): honor mixed-case http11 protocol checks --- common/httpx/httpx.go | 8 ++++++-- common/httpx/httpx_test.go | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 79b089d14..594c7003a 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -152,7 +152,7 @@ func New(options *Options) (*HTTPX, error) { DisableKeepAlives: true, } - if httpx.Options.Protocol == HTTP11 { + if isHTTP11Protocol(httpx.Options.Protocol) { // disable http2 transport.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{} } @@ -180,7 +180,7 @@ func New(options *Options) (*HTTPX, error) { Timeout: httpx.Options.Timeout, CheckRedirect: redirectFunc, }, retryablehttpOptions) - if httpx.Options.Protocol == HTTP11 { + if isHTTP11Protocol(httpx.Options.Protocol) { // Keep retryablehttp-go on HTTP/1.1 as well when it retries internally. httpx.client.HTTPClient2 = httpx.client.HTTPClient } @@ -214,6 +214,10 @@ func New(options *Options) (*HTTPX, error) { return httpx, nil } +func isHTTP11Protocol(protocol Proto) bool { + return strings.EqualFold(string(protocol), string(HTTP11)) +} + // Do http request func (h *HTTPX) Do(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (*Response, error) { timeStart := time.Now() diff --git a/common/httpx/httpx_test.go b/common/httpx/httpx_test.go index 6762de302..82e5e28a7 100644 --- a/common/httpx/httpx_test.go +++ b/common/httpx/httpx_test.go @@ -38,6 +38,15 @@ func TestHTTP11DisablesRetryableHTTP2Fallback(t *testing.T) { require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2) } +func TestHTTP11MixedCaseDisablesRetryableHTTP2Fallback(t *testing.T) { + opts := DefaultOptions + opts.Protocol = Proto("HTTP11") + + ht, err := New(&opts) + require.NoError(t, err) + require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2) +} + func TestDefaultProtocolKeepsRetryableHTTP2Fallback(t *testing.T) { opts := DefaultOptions