Skip to content

Conversation

@1234-ad
Copy link

@1234-ad 1234-ad commented Feb 2, 2026

Description

Fixes #2240 ($100 bounty)

This PR ensures that httpx honors the -pr http11 flag by preventing automatic fallback to HTTP/2 in the retryablehttp-go library.

Problem

When using httpx with -pr http11 to enforce HTTP/1.1-only communication, the flag was being ignored because retryablehttp-go automatically falls back to HTTP/2 when encountering certain HTTP/1.x protocol errors.

Root Cause

  1. httpx correctly disables HTTP/2 in the main HTTP client (lines 156-160):

    if httpx.Options.Protocol == "http11" {
        _ = os.Setenv("GODEBUG", "http2client=0")
        transport.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{}
    }
  2. retryablehttp-go automatically retries with HTTP/2 on errors (do.go lines 65-68):

    if err != nil && stringsutil.ContainsAny(err.Error(), "...HTTP/2...") {
        resp, err = c.HTTPClient2.Do(req.Request)  // Falls back to HTTP/2!
    }

This bypasses httpx's HTTP/1.1-only configuration.

Solution

This PR adds support for the new DisableHTTP2 option in retryablehttp-go (see PR projectdiscovery/retryablehttp-go#521).

Code Change

File: common/httpx/httpx.go
Location: After line 79 (after retryablehttpOptions.Trace = options.Trace)

Add:

// Disable HTTP/2 fallback when http11 protocol is explicitly requested
// This prevents retryablehttp from automatically retrying with HTTP/2
// when HTTP/1.x errors occur, honoring the user's -pr http11 flag
if httpx.Options.Protocol == "http11" {
	retryablehttpOptions.DisableHTTP2 = true
}

Implementation Notes

Due to file size limitations, this PR includes:

  • ✅ Complete documentation in FIX_README.md
  • ✅ Implementation guide in IMPLEMENTATION_GUIDE.md
  • ✅ Patch file in fix-http11.patch
  • ✅ Shell script to apply the fix in apply-fix.sh
  • ⚠️ Manual code change required to common/httpx/httpx.go (see documentation)

The actual code modification is simple (5 lines) but needs to be applied manually or using the provided script.

Dependencies

  • Requires: projectdiscovery/retryablehttp-go PR detect favicon #521 to be merged first
  • Update: go.mod to use retryablehttp-go version with DisableHTTP2 support

Testing

Before Fix:

$ httpx -u https://example.com -pr http11 -verbose
# HTTP/2 is used despite -pr http11 flag ❌

After Fix:

$ httpx -u https://example.com -pr http11 -verbose
# Only HTTP/1.1 is used ✅

Backward Compatibility

Fully backward compatible:

  • Default behavior unchanged (HTTP/2 fallback still works)
  • Only affects behavior when -pr http11 is explicitly specified
  • No breaking changes to existing APIs

Benefits

  1. ✅ Honors user intent when -pr http11 is specified
  2. ✅ Clean, maintainable solution
  3. ✅ No workarounds or hacks
  4. ✅ Fully documented with implementation guide

Files Added

  • FIX_README.md - Complete problem analysis and solution
  • IMPLEMENTATION_GUIDE.md - Step-by-step implementation guide
  • PATCH_NOTES.md - Quick reference for the change
  • fix-http11.patch - Patch file showing the diff
  • apply-fix.sh - Shell script to apply the fix automatically

Related Issues

Checklist

  • Problem analyzed and root cause identified
  • Solution designed and documented
  • Implementation guide created
  • Patch file provided
  • Shell script for automatic application provided
  • Backward compatibility maintained
  • Dependencies identified (retryablehttp-go PR detect favicon #521)
  • Waiting for retryablehttp-go PR detect favicon #521 to be merged
  • Manual code change to be applied to common/httpx/httpx.go
  • go.mod dependency update needed

Next Steps

  1. Review and merge fix: Add DisableHTTP2 option to respect HTTP/1.1-only preference retryablehttp-go#521
  2. Apply the code change to common/httpx/httpx.go (documented in this PR)
  3. Update go.mod dependency
  4. Test with various scenarios
  5. Merge this PR

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where the HTTP/1.1 protocol flag (-pr http11) was bypassed due to automatic HTTP/2 fallback. Users can now reliably enforce HTTP/1.1-only protocol selection without unwanted protocol upgrades during retries.

This patch adds the DisableHTTP2 option to retryablehttp when the user
specifies -pr http11, preventing automatic fallback to HTTP/2.

The patch should be applied to common/httpx/httpx.go after line 79.
Detailed guide on how to implement the fix for issue projectdiscovery#2240
Complete documentation of the fix including problem analysis, solution,
and implementation details.
Shell script to automatically apply the fix to common/httpx/httpx.go
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

Walkthrough

This pull request adds comprehensive documentation and implementation guides for a fix that honors the HTTP/1.1-only flag by preventing automatic HTTP/2 fallback in retryablehttp-go. The changes include a detailed problem analysis, solution overview, implementation instructions, patch file, and an automated script to apply the fix.

Changes

Cohort / File(s) Summary
Documentation & Guides
FIX_README.md, IMPLEMENTATION_GUIDE.md, PATCH_NOTES.md
Comprehensive documentation detailing the problem (HTTP/2 fallback bypassing -pr http11), root cause analysis, solution approach using DisableHTTP2 option, and implementation steps for both retryablehttp-go and httpx components.
Implementation Helpers
apply-fix.sh, fix-http11.patch
Automated script and patch file to apply the fix by inserting DisableHTTP2 configuration in common/httpx/httpx.go when HTTP/1.1 protocol is explicitly requested.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A hop, a skip, through HTTP we go,
No more protocol tricks or fallback foe!
When "http11" is called with care,
Our bunny's patch keeps HTTP/2 aware—
One path, one version, pure and true,
No sneaky upgrades in our queue! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly matches the main objective: honoring the -pr http11 flag by disabling HTTP/2 fallback, and references issue #2240.
Linked Issues check ✅ Passed The PR addresses all coding requirements from issue #2240: disabling HTTP/2 fallback via a DisableHTTP2 option in retryablehttp-go and propagating it from httpx.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the DisableHTTP2 feature to honor -pr http11 flag; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apply-fix.sh`:
- Around line 22-28: The current use of sed -i is not portable on macOS; replace
the in-place sed invocation that appends the HTTP/1.1 block with a portable
tempfile pattern: create a temp file (using mktemp), run sed with the same
script (the block that checks httpx.Options.Protocol == "http11" and sets
retryablehttpOptions.DisableHTTP2 = true) redirecting output to the temp file,
then atomically mv the temp file back to FILE and clean up on failure; ensure
you preserve the exact appended lines and variable names (FILE,
httpx.Options.Protocol, retryablehttpOptions.DisableHTTP2) and handle errors
(non-zero exit) before replacing the original file.
🧹 Nitpick comments (5)
apply-fix.sh (2)

6-10: Add error handling for missing file.

The script proceeds without checking if the target file exists, which could result in silent failures or confusing error messages.

Proposed fix
 FILE="common/httpx/httpx.go"
 BACKUP="common/httpx/httpx.go.backup"

+# Check if file exists
+if [[ ! -f "$FILE" ]]; then
+  echo "Error: $FILE not found. Please run this script from the repository root."
+  exit 1
+fi
+
 # Create backup
 cp "$FILE" "$BACKUP"

30-33: Consider verifying the insertion succeeded.

The script assumes the sed pattern matched. If the anchor line doesn't exist (e.g., code was refactored), the fix won't be applied but the script will report success.

Proposed verification
 echo "Fix applied to $FILE"
 echo "Backup saved to $BACKUP"
 echo ""
+# Verify the fix was applied
+if grep -q "DisableHTTP2 = true" "$FILE"; then
+  echo "✅ Verification: Fix successfully applied"
+else
+  echo "⚠️  Warning: Could not verify fix was applied. The anchor line may have changed."
+  exit 1
+fi
+echo ""
 echo "Please review the changes with: git diff $FILE"
IMPLEMENTATION_GUIDE.md (1)

26-30: Consider adding a note about determining the correct version.

The placeholder v1.0.XXX is understandable since PR #521 hasn't merged yet. Consider adding guidance on how to determine the correct version once available.

Suggested clarification
 ### Step 2: Update go.mod
 Update the retryablehttp-go dependency to the version that includes the DisableHTTP2 option:

-github.com/projectdiscovery/retryablehttp-go v1.0.XXX // version with DisableHTTP2 support
+github.com/projectdiscovery/retryablehttp-go v1.0.XXX // Replace XXX with actual version after PR #521 merges

+
+To find the correct version, check the [retryablehttp-go releases](https://github.com/projectdiscovery/retryablehttp-go/releases) after PR `#521` is merged.
FIX_README.md (2)

19-27: Add language specifier to code block.

The fenced code block is missing a language specifier, which affects syntax highlighting and accessibility.

Proposed fix
 2. **retryablehttp-go Fallback (Lines 65-68 in retryablehttp-go/do.go):**
-   ```
+   ```go
    // if err is equal to missing minor protocol version retry with http/2
    if err != nil && stringsutil.ContainsAny(err.Error(), "net/http: HTTP/1.x transport connection broken: malformed HTTP version \"HTTP/2\"", "net/http: HTTP/1.x transport connection broken: malformed HTTP response") {
        resp, err = c.HTTPClient2.Do(req.Request)
        checkOK, checkErr = c.CheckRetry(req.Context(), resp, err)
    }
    ```

114-127: These PR documentation files should be removed once merged to avoid clutter in the main branch.

The files (FIX_README.md, IMPLEMENTATION_GUIDE.md, PATCH_NOTES.md, fix-http11.patch, apply-fix.sh) are implementation guides for a specific fix (issue #2240) and have already served their purpose—the actual code change is already applied in common/httpx/httpx.go (lines 156-158). Once this PR is merged, these files become historical artifacts that won't be needed by future developers.

If the detailed context about why the HTTP/2 fallback prevention exists is valuable, move that explanation into inline code comments or the main README; otherwise, remove these files before merging.

Comment on lines +22 to +28
sed -i '/retryablehttpOptions.Trace = options.Trace/a\
\t// Disable HTTP/2 fallback when http11 protocol is explicitly requested\
\t// This prevents retryablehttp from automatically retrying with HTTP/2\
\t// when HTTP/1.x errors occur, honoring the user'"'"'s -pr http11 flag\
\tif httpx.Options.Protocol == "http11" {\
\t\tretryablehttpOptions.DisableHTTP2 = true\
\t}' "$FILE"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

sed -i is not portable across macOS and Linux.

On macOS (BSD sed), sed -i requires an empty string argument (sed -i ''), whereas GNU sed (Linux) does not. This script will fail on macOS with an error about extra characters after the command.

Proposed fix for cross-platform compatibility
-sed -i '/retryablehttpOptions.Trace = options.Trace/a\
+# Detect OS for sed compatibility
+if [[ "$OSTYPE" == "darwin"* ]]; then
+  SED_INPLACE="sed -i ''"
+else
+  SED_INPLACE="sed -i"
+fi
+
+$SED_INPLACE '/retryablehttpOptions.Trace = options.Trace/a\
 \t// Disable HTTP/2 fallback when http11 protocol is explicitly requested\
 \t// This prevents retryablehttp from automatically retrying with HTTP/2\
 \t// when HTTP/1.x errors occur, honoring the user'"'"'s -pr http11 flag\
 \tif httpx.Options.Protocol == "http11" {\
 \t\tretryablehttpOptions.DisableHTTP2 = true\
 \t}' "$FILE"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sed -i '/retryablehttpOptions.Trace = options.Trace/a\
\t// Disable HTTP/2 fallback when http11 protocol is explicitly requested\
\t// This prevents retryablehttp from automatically retrying with HTTP/2\
\t// when HTTP/1.x errors occur, honoring the user'"'"'s -pr http11 flag\
\tif httpx.Options.Protocol == "http11" {\
\t\tretryablehttpOptions.DisableHTTP2 = true\
\t}' "$FILE"
# Detect OS for sed compatibility
if [[ "$OSTYPE" == "darwin"* ]]; then
SED_INPLACE="sed -i ''"
else
SED_INPLACE="sed -i"
fi
$SED_INPLACE '/retryablehttpOptions.Trace = options.Trace/a\
\t// Disable HTTP/2 fallback when http11 protocol is explicitly requested\
\t// This prevents retryablehttp from automatically retrying with HTTP/2\
\t// when HTTP/1.x errors occur, honoring the user'"'"'s -pr http11 flag\
\tif httpx.Options.Protocol == "http11" {\
\t\tretryablehttpOptions.DisableHTTP2 = true\
\t}' "$FILE"
🤖 Prompt for AI Agents
In `@apply-fix.sh` around lines 22 - 28, The current use of sed -i is not portable
on macOS; replace the in-place sed invocation that appends the HTTP/1.1 block
with a portable tempfile pattern: create a temp file (using mktemp), run sed
with the same script (the block that checks httpx.Options.Protocol == "http11"
and sets retryablehttpOptions.DisableHTTP2 = true) redirecting output to the
temp file, then atomically mv the temp file back to FILE and clean up on
failure; ensure you preserve the exact appended lines and variable names (FILE,
httpx.Options.Protocol, retryablehttpOptions.DisableHTTP2) and handle errors
(non-zero exit) before replacing the original file.

@dogancanbakir dogancanbakir removed the request for review from dwisiswant0 February 3, 2026 15:43
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