From 83e8a1d99ca5056e20f5740e9c14a881c07a2cc2 Mon Sep 17 00:00:00 2001 From: Haarith Imran Date: Sat, 7 Feb 2026 22:33:09 +0500 Subject: [PATCH] fix(httpx): honor http11 by disabling retryablehttp http2 fallback --- common/httpx/httpx.go | 4 ++++ common/httpx/httpx_test.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index 039f4c4ca..542e229db 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" { + // Explicitly prevent retryablehttp from falling back to a native HTTP/2 client. + 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..3aef872cc 100644 --- a/common/httpx/httpx_test.go +++ b/common/httpx/httpx_test.go @@ -2,6 +2,7 @@ package httpx import ( "net/http" + "net/http/httptest" "testing" "github.com/projectdiscovery/retryablehttp-go" @@ -13,7 +14,14 @@ 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) { + body := []byte("OK") + w.Header().Set("Content-Length", "2") + _, _ = w.Write(body) + })) + 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 +29,33 @@ 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) { + options := DefaultOptions + options.Protocol = HTTP11 + + ht, err := New(&options) + require.NoError(t, err) + require.NotNil(t, ht.client) + + // When protocol is http11, retryablehttp should not use a separate HTTP/2 fallback client. + require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2) +}