Skip to content

fix: preserve -pr http11 across retryablehttp-go fallback (#2240)#2449

Open
CharlesWong wants to merge 2 commits intoprojectdiscovery:devfrom
CharlesWong:fix/http11-protocol-enforcement
Open

fix: preserve -pr http11 across retryablehttp-go fallback (#2240)#2449
CharlesWong wants to merge 2 commits intoprojectdiscovery:devfrom
CharlesWong:fix/http11-protocol-enforcement

Conversation

@CharlesWong
Copy link

@CharlesWong CharlesWong commented Mar 10, 2026

Summary

Fixes #2240-pr http11 is silently ignored on retry because retryablehttp-go falls back to an HTTP/2-capable client.

Root Cause Analysis

The HTTP/1.1 enforcement has two layers, but only the first was implemented:

Layer What it does Status before this PR
Transport-level GODEBUG=http2client=0 + TLSNextProto={} on the primary transport ✅ Already working
Retry fallback retryablehttp-go switches to HTTPClient2 (HTTP/2) on "malformed HTTP version" errors Bypasses http11 restriction

When a server responds with HTTP/2, the primary HTTP/1.1 client returns an error. retryablehttp-go then retries with HTTPClient2 — which succeeds via HTTP/2, defeating -pr http11.

Fix

Point HTTPClient2 at the same HTTP/1.1-only client, so the fallback path still honours the protocol restriction:

if httpx.Options.Protocol == "http11" {
    httpx.client.HTTPClient2 = httpx.client.HTTPClient
}

Why This PR

Aspect This PR Notes
Fix approach HTTPClient2 = HTTPClient Only correct approach — DisableHTTP2Fallback option does not exist in retryablehttp-go
Placement ✅ After NewWithHTTPClient, before transport2 Clean code flow
Comment ✅ Explains the full retry fallback chain Future maintainers understand why
Tests ✅ Both http11 and default mode verified Ensures http11 neutralises fallback AND default mode keeps HTTP/2 detection working
Side effects ✅ None — default mode unchanged require.NotSame confirms HTTPClient2 stays distinct in default mode

Some alternative approaches (e.g. DisableHTTP2Fallback option) reference APIs that don't exist in retryablehttp-go. This PR uses the only mechanism available: overriding the fallback client pointer directly.

Testing

$ go test ./common/httpx/ -run TestHTTP11 -v
=== RUN   TestHTTP11ProtocolEnforcement
=== RUN   TestHTTP11ProtocolEnforcement/http11_mode_neutralises_HTTPClient2_fallback
=== RUN   TestHTTP11ProtocolEnforcement/default_mode_keeps_separate_HTTPClient2_for_HTTP/2
--- PASS: TestHTTP11ProtocolEnforcement (0.09s)
PASS

Summary by CodeRabbit

  • Bug Fixes

    • Prevented silent HTTP/2 upgrades during retries when HTTP/1.1-only mode is selected, ensuring connections remain HTTP/1.1.
  • Tests

    • Added tests to verify HTTP/1.1-only enforcement and that default behavior still allows HTTP/2-capable fallback.

…covery#2240)

retryablehttp-go falls back to HTTPClient2 (HTTP/2-capable) when it hits
a "malformed HTTP version" error, silently upgrading the protocol even
when -pr http11 is set. Point HTTPClient2 at the same HTTP/1.1-only
client to neutralise the fallback.

Fixes projectdiscovery#2240
@auto-assign auto-assign bot requested a review from Mzack9999 March 10, 2026 16:55
@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 103b789f-427d-47ab-bee8-72d3343c137f

📥 Commits

Reviewing files that changed from the base of the PR and between 671d295 and b4b2829.

📒 Files selected for processing (1)
  • common/httpx/httpx.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • common/httpx/httpx.go

Walkthrough

Replaces string literal protocol checks with a constant and synchronizes HTTPClient2 to HTTPClient when Protocol == HTTP11 to prevent retryablehttp-go from falling back to HTTP/2 during retries.

Changes

Cohort / File(s) Summary
HTTP/1.1 Enforcement Logic
common/httpx/httpx.go
Replaces "http11" literal with HTTP11 constant at two call sites and sets client.HTTPClient2 = client.HTTPClient when HTTP/1.1 mode is active (after retryablehttp client creation and during main client setup) to prevent HTTP/2 fallback on retries.
Protocol Enforcement Tests
common/httpx/httpx_test.go
Adds TestHTTP11ProtocolEnforcement with subtests asserting that in HTTP/1.1 mode HTTPClient and HTTPClient2 are identical, and in default mode they remain distinct.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I thumped my foot, declared "HTTP11!",

Two clients now march as a single one.
No secret upgrade, no protocol sleight,
Retries stay true through day and night.
Hooray — tidy hops and network delight! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main fix: preserving HTTP/1.1 protocol setting across retryablehttp-go fallback, which is the core change in the PR.
Linked Issues check ✅ Passed The PR implementation fully addresses the linked issue #2240 by synchronizing HTTPClient2 with HTTPClient when HTTP/1.1 mode is active, preventing unintended HTTP/2 upgrades on retries.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the HTTP/1.1 protocol enforcement fix, with implementation changes in httpx.go and corresponding test coverage additions in httpx_test.go.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
common/httpx/httpx.go (1)

186-193: Consider using the HTTP11 constant instead of string literal.

The comparison uses the string literal "http11" directly. The codebase defines a typed constant HTTP11 Proto = "http11" in proto.go. Using the constant improves type safety and maintainability, and aligns with how the test file references this value.

Note: Line 156 has the same pattern, so both could be updated together for consistency.

♻️ Suggested change
-	if httpx.Options.Protocol == "http11" {
+	if httpx.Options.Protocol == HTTP11 {
 		httpx.client.HTTPClient2 = httpx.client.HTTPClient
 	}

And optionally update line 156 as well:

-	if httpx.Options.Protocol == "http11" {
+	if httpx.Options.Protocol == HTTP11 {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@common/httpx/httpx.go` around lines 186 - 193, Replace the string literal
"http11" comparisons with the typed constant HTTP11 (defined as Proto =
"http11") to improve type safety; specifically, update the conditional checking
httpx.Options.Protocol in the block that sets httpx.client.HTTPClient2 =
httpx.client.HTTPClient (and the similar comparison earlier near where Protocol
is checked) to compare against the HTTP11 constant instead of the literal.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@common/httpx/httpx.go`:
- Around line 186-193: Replace the string literal "http11" comparisons with the
typed constant HTTP11 (defined as Proto = "http11") to improve type safety;
specifically, update the conditional checking httpx.Options.Protocol in the
block that sets httpx.client.HTTPClient2 = httpx.client.HTTPClient (and the
similar comparison earlier near where Protocol is checked) to compare against
the HTTP11 constant instead of the literal.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4b41e964-d514-4d26-8c8a-4f8306d14544

📥 Commits

Reviewing files that changed from the base of the PR and between 73afc60 and 671d295.

📒 Files selected for processing (2)
  • common/httpx/httpx.go
  • common/httpx/httpx_test.go

@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Mar 10, 2026

Neo - PR Security Review

No security issues found

Highlights

  • Fixes protocol enforcement bypass where -pr http11 was silently ignored on retry
  • Ensures retryablehttp-go's HTTPClient2 fallback respects HTTP/1.1-only mode
  • Adds test coverage to verify http11 mode neutralizes HTTP/2 fallback

Comment @pdneo help for available commands. · Open in Neo

@CharlesWong
Copy link
Author

The HTTP11 constant nitpick is already applied in this branch — both occurrences in httpx.go (lines 156 and 191) use the typed HTTP11 constant rather than the string literal. No further changes needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

-pr http11 flag is ignored on retryablehttp-go due to HTTP/2 fallback

1 participant