Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion common/httpx/httpx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -183,6 +183,15 @@ func New(options *Options) (*HTTPX, error) {
CheckRedirect: redirectFunc,
}, retryablehttpOptions)

// When HTTP/1.1-only mode is enforced via -pr http11, prevent retryablehttp-go
// from silently upgrading to HTTP/2 on retry. retryablehttp-go falls back to
// HTTPClient2 (an HTTP/2-capable client) when it encounters "malformed HTTP
// version" errors from servers that speak HTTP/2. Pointing HTTPClient2 at the
// same HTTP/1.1-only client neutralises the fallback. See: #2240
if httpx.Options.Protocol == HTTP11 {
httpx.client.HTTPClient2 = httpx.client.HTTPClient
}

transport2 := &http2.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
Expand Down
26 changes: 26 additions & 0 deletions common/httpx/httpx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,29 @@ func TestDo(t *testing.T) {
require.Greater(t, len(resp.Raw), 800)
})
}

// TestHTTP11ProtocolEnforcement verifies that -pr http11 prevents retryablehttp-go's
// HTTP/2 fallback from bypassing the protocol restriction (#2240).
func TestHTTP11ProtocolEnforcement(t *testing.T) {
t.Run("http11 mode neutralises HTTPClient2 fallback", func(t *testing.T) {
opts := DefaultOptions
opts.Protocol = HTTP11
ht, err := New(&opts)
require.Nil(t, err)

// HTTPClient2 must be the same as HTTPClient so the fallback
// path still uses HTTP/1.1-only transport.
require.Same(t, ht.client.HTTPClient, ht.client.HTTPClient2,
"HTTPClient2 must equal HTTPClient in http11 mode to prevent HTTP/2 fallback")
})

t.Run("default mode keeps separate HTTPClient2 for HTTP/2", func(t *testing.T) {
ht, err := New(&DefaultOptions)
require.Nil(t, err)

// In default mode the two clients must be distinct — HTTPClient2
// is the HTTP/2-capable client used for protocol detection/fallback.
require.NotSame(t, ht.client.HTTPClient, ht.client.HTTPClient2,
"HTTPClient2 must differ from HTTPClient in default mode")
})
}