From 5587d9091a549d16ea202093d25976b79f7fe76a Mon Sep 17 00:00:00 2001 From: Solari Systems Date: Mon, 9 Feb 2026 12:02:28 -0500 Subject: [PATCH 1/2] fix: honor -pr http11 by disabling retryablehttp HTTP/2 fallback --- common/httpx/httpx.go | 8 ++++++ common/httpx/httpx_test.go | 54 +++++++++++++++++++++++++++++++++++--- 2 files changed, 59 insertions(+), 3 deletions(-) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 039f4c4ca..6987ed6b9 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -183,6 +183,14 @@ func New(options *Options) (*HTTPX, error) { CheckRedirect: redirectFunc, }, retryablehttpOptions) + // When the user explicitly requested HTTP/1.1, prevent retryablehttp from + // falling back to its built-in HTTP/2 client on protocol-mismatch errors. + // retryablehttp keeps a secondary HTTPClient2 configured with native HTTP/2; + // pointing it at the same (HTTP/1.1-only) client disables that fallback path. + if httpx.Options.Protocol == HTTP11 { + httpx.client.HTTPClient2 = httpx.client.HTTPClient + } + transport2 := &http2.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, diff --git a/common/httpx/httpx_test.go b/common/httpx/httpx_test.go index 7da6ad12d..1bb5d52a5 100644 --- a/common/httpx/httpx_test.go +++ b/common/httpx/httpx_test.go @@ -2,9 +2,10 @@ package httpx import ( "net/http" + "net/http/httptest" "testing" - "github.com/projectdiscovery/retryablehttp-go" + retryablehttp "github.com/projectdiscovery/retryablehttp-go" "github.com/stretchr/testify/require" ) @@ -13,7 +14,13 @@ func TestDo(t *testing.T) { require.Nil(t, err) t.Run("content-length in header", func(t *testing.T) { - req, err := retryablehttp.NewRequest(http.MethodGet, "https://scanme.sh", nil) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Length", "2") + _, _ = w.Write([]byte("OK")) + })) + defer server.Close() + + req, err := retryablehttp.NewRequest(http.MethodGet, server.URL, nil) require.Nil(t, err) resp, err := ht.Do(req, UnsafeOptions{}) require.Nil(t, err) @@ -21,10 +28,51 @@ func TestDo(t *testing.T) { }) t.Run("content-length with binary body", func(t *testing.T) { - req, err := retryablehttp.NewRequest(http.MethodGet, "https://www.w3schools.com/images/favicon.ico", nil) + binary := make([]byte, 1024) + for i := range binary { + binary[i] = byte(i % 256) + } + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Length", "1024") + _, _ = w.Write(binary) + })) + defer server.Close() + + req, err := retryablehttp.NewRequest(http.MethodGet, server.URL, nil) require.Nil(t, err) resp, err := ht.Do(req, UnsafeOptions{}) require.Nil(t, err) require.Greater(t, len(resp.Raw), 800) + require.Equal(t, len(binary), resp.ContentLength) + }) +} + +func TestHTTP11DisablesHTTP2Fallback(t *testing.T) { + t.Run("http11 protocol pins fallback client", func(t *testing.T) { + opts := DefaultOptions + opts.Protocol = HTTP11 + + ht, err := New(&opts) + require.NoError(t, err) + require.NotNil(t, ht.client) + + // When HTTP/1.1 is requested, the fallback HTTP/2 client must be + // replaced with the primary (HTTP/1.1-only) client so that + // retryablehttp never silently upgrades the protocol. + require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2, + "HTTPClient2 must equal HTTPClient when protocol is http11") + }) + + t.Run("default protocol keeps separate fallback client", func(t *testing.T) { + opts := DefaultOptions + + ht, err := New(&opts) + require.NoError(t, err) + require.NotNil(t, ht.client) + + // With the default (unset) protocol, the two clients should remain + // independent so the normal HTTP/2 fallback path is preserved. + require.NotSame(t, ht.client.HTTPClient, ht.client.HTTPClient2, + "HTTPClient2 must differ from HTTPClient when protocol is default") }) } From 7b1e27073e5aee22196544c83a0c082a5b1aa49d Mon Sep 17 00:00:00 2001 From: Solari Systems Date: Mon, 9 Feb 2026 14:29:27 -0500 Subject: [PATCH 2/2] fix: use HTTP11 constant instead of string literal for consistency Co-Authored-By: Claude Opus 4.6 --- common/httpx/httpx.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 6987ed6b9..f1eb422a8 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -153,7 +153,7 @@ 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{}