-
Notifications
You must be signed in to change notification settings - Fork 1k
fix: Honor -pr http11 flag by disabling HTTP/2 fallback (#2240) #2387
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
1234-ad
wants to merge
5
commits into
projectdiscovery:dev
Choose a base branch
from
1234-ad:fix/honor-http11-protocol-flag-2240
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
669e4f0
docs: Add patch notes for HTTP/1.1-only fix
1234-ad 65db22f
feat: Add patch to honor -pr http11 flag by disabling HTTP/2 fallback
1234-ad 3b77b1b
docs: Add implementation guide for HTTP/1.1-only fix
1234-ad 13c93a2
docs: Add comprehensive README for issue #2240 fix
1234-ad f9916b3
feat: Add script to apply HTTP/1.1-only fix
1234-ad File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| # Fix for Issue #2240: -pr http11 Flag Ignored | ||
|
|
||
| ## Problem Statement | ||
|
|
||
| When using httpx with the `-pr http11` flag to enforce HTTP/1.1-only communication, the flag is being ignored due to automatic HTTP/2 fallback in the retryablehttp-go library. | ||
|
|
||
| ### Root Cause Analysis | ||
|
|
||
| 1. **httpx Configuration (Lines 156-160 in common/httpx/httpx.go):** | ||
| ```go | ||
| if httpx.Options.Protocol == "http11" { | ||
| // disable http2 | ||
| _ = os.Setenv("GODEBUG", "http2client=0") | ||
| transport.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{} | ||
| } | ||
| ``` | ||
| httpx correctly disables HTTP/2 in the main HTTP client. | ||
|
|
||
| 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) | ||
| } | ||
| ``` | ||
| retryablehttp-go automatically falls back to HTTP/2 on certain errors, bypassing httpx's HTTP/1.1-only configuration. | ||
|
|
||
| ## Solution | ||
|
|
||
| The fix involves two components: | ||
|
|
||
| ### Component 1: retryablehttp-go (PR #521) | ||
| Add a `DisableHTTP2` option to prevent automatic HTTP/2 fallback: | ||
|
|
||
| **Changes to client.go:** | ||
| - Add `DisableHTTP2 bool` field to `Options` struct | ||
| - Skip creating `HTTPClient2` when `DisableHTTP2 = true` | ||
|
|
||
| **Changes to do.go:** | ||
| - Check `!c.options.DisableHTTP2 && c.HTTPClient2 != nil` before HTTP/2 fallback | ||
| - Handle nil `HTTPClient2` safely in `closeIdleConnections()` | ||
|
|
||
| ### Component 2: httpx (This PR) | ||
| Set the `DisableHTTP2` option when `-pr http11` is specified: | ||
|
|
||
| **Change to common/httpx/httpx.go (after line 79):** | ||
| ```go | ||
| var retryablehttpOptions = retryablehttp.DefaultOptionsSpraying | ||
| retryablehttpOptions.Timeout = httpx.Options.Timeout | ||
| retryablehttpOptions.RetryMax = httpx.Options.RetryMax | ||
| retryablehttpOptions.Trace = options.Trace | ||
| // ADD THESE LINES: | ||
| // Disable HTTP/2 fallback when http11 protocol is explicitly requested | ||
| if httpx.Options.Protocol == "http11" { | ||
| retryablehttpOptions.DisableHTTP2 = true | ||
| } | ||
| ``` | ||
|
|
||
| ## Implementation | ||
|
|
||
| ### Manual Code Change Required | ||
|
|
||
| Due to the limitations of automated file modification, the following manual change is required: | ||
|
|
||
| **File:** `common/httpx/httpx.go` | ||
| **Location:** After line 79 (after `retryablehttpOptions.Trace = options.Trace`) | ||
|
|
||
| **Insert:** | ||
| ```go | ||
| // 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 | ||
| } | ||
| ``` | ||
|
|
||
| ### Dependency Update | ||
|
|
||
| Update `go.mod` to use the version of retryablehttp-go that includes the `DisableHTTP2` option (after PR #521 is merged). | ||
|
|
||
| ## Testing | ||
|
|
||
| ### Before Fix: | ||
| ```bash | ||
| $ httpx -u https://example.com -pr http11 -verbose | ||
| # Output shows HTTP/2 being used despite -pr http11 flag | ||
| ``` | ||
|
|
||
| ### After Fix: | ||
| ```bash | ||
| $ httpx -u https://example.com -pr http11 -verbose | ||
| # Output shows only HTTP/1.1 being used | ||
| ``` | ||
|
|
||
| ## Benefits | ||
|
|
||
| 1. ✅ **Honors User Intent:** Respects the `-pr http11` flag | ||
| 2. ✅ **Backward Compatible:** Default behavior unchanged | ||
| 3. ✅ **Clean Solution:** No workarounds or hacks | ||
| 4. ✅ **Maintainable:** Clear, documented code | ||
|
|
||
| ## Related Issues | ||
|
|
||
| - Fixes: projectdiscovery/httpx#2240 | ||
| - Depends on: projectdiscovery/retryablehttp-go#521 | ||
| - Related: projectdiscovery/retryablehttp-go#3 | ||
|
|
||
| ## Bounty | ||
|
|
||
| This issue has a **$100 bounty** from ProjectDiscovery. | ||
|
|
||
| ## Files in This Branch | ||
|
|
||
| - `FIX_README.md` - This file | ||
| - `IMPLEMENTATION_GUIDE.md` - Detailed implementation steps | ||
| - `PATCH_NOTES.md` - Quick reference for the required change | ||
| - `fix-http11.patch` - Patch file showing the diff | ||
|
|
||
| ## Next Steps | ||
|
|
||
| 1. Wait for retryablehttp-go PR #521 to be merged | ||
| 2. Apply the code change to `common/httpx/httpx.go` as documented | ||
| 3. Update `go.mod` dependency | ||
| 4. Test with various scenarios | ||
| 5. Submit PR to projectdiscovery/httpx |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # Implementation Guide: Fix for Issue #2240 | ||
|
|
||
| ## Overview | ||
| This fix ensures that httpx honors the `-pr http11` flag by preventing automatic fallback to HTTP/2 in the retryablehttp-go library. | ||
|
|
||
| ## Prerequisites | ||
| 1. The retryablehttp-go library must have the `DisableHTTP2` option available (PR #521) | ||
| 2. Update go.mod to use the version of retryablehttp-go that includes this option | ||
|
|
||
| ## Implementation Steps | ||
|
|
||
| ### Step 1: Update common/httpx/httpx.go | ||
|
|
||
| **Location:** After line 79 (after `retryablehttpOptions.Trace = options.Trace`) | ||
|
|
||
| **Add the following code:** | ||
| ```go | ||
| // 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 | ||
| } | ||
| ``` | ||
|
|
||
| ### 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 | ||
| ``` | ||
|
|
||
| ## How It Works | ||
|
|
||
| ### Before the Fix: | ||
| 1. User runs: `httpx -u https://example.com -pr http11` | ||
| 2. httpx sets `GODEBUG=http2client=0` and clears `TLSNextProto` | ||
| 3. Request is made with HTTP/1.1 | ||
| 4. If server responds with HTTP/2 error, retryablehttp-go automatically retries with HTTPClient2 (HTTP/2) | ||
| 5. **Result:** HTTP/2 is used despite `-pr http11` flag ❌ | ||
|
|
||
| ### After the Fix: | ||
| 1. User runs: `httpx -u https://example.com -pr http11` | ||
| 2. httpx sets `GODEBUG=http2client=0`, clears `TLSNextProto`, AND sets `DisableHTTP2 = true` | ||
| 3. Request is made with HTTP/1.1 | ||
| 4. If server responds with HTTP/2 error, retryablehttp-go does NOT fall back to HTTP/2 | ||
| 5. **Result:** Only HTTP/1.1 is used, honoring the `-pr http11` flag ✅ | ||
|
|
||
| ## Testing | ||
|
|
||
| ### Test Case 1: HTTP/1.1-only mode | ||
| ```bash | ||
| httpx -u https://example.com -pr http11 -verbose | ||
| ``` | ||
| Expected: All requests use HTTP/1.1, no HTTP/2 fallback | ||
|
|
||
| ### Test Case 2: Default mode (HTTP/2 allowed) | ||
| ```bash | ||
| httpx -u https://example.com -verbose | ||
| ``` | ||
| Expected: HTTP/2 fallback still works when not explicitly disabled | ||
|
|
||
| ### Test Case 3: HTTP/2-only mode | ||
| ```bash | ||
| httpx -u https://example.com -pr http2 -verbose | ||
| ``` | ||
| Expected: All requests use HTTP/2 | ||
|
|
||
| ## Code Changes Summary | ||
|
|
||
| **File:** `common/httpx/httpx.go` | ||
| **Lines:** Insert after line 79 | ||
| **Lines Added:** 5 | ||
| **Lines Modified:** 0 | ||
| **Lines Deleted:** 0 | ||
|
|
||
| ## Dependencies | ||
| - Requires: projectdiscovery/retryablehttp-go PR #521 to be merged | ||
| - Related Issue: projectdiscovery/httpx#2240 | ||
| - Related Issue: projectdiscovery/retryablehttp-go#3 | ||
|
|
||
| ## Backward Compatibility | ||
| ✅ Fully backward compatible | ||
| - Default behavior unchanged (HTTP/2 fallback still enabled) | ||
| - Only affects behavior when `-pr http11` is explicitly specified | ||
| - No breaking changes to existing APIs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| # Fix for Issue #2240: Honor -pr http11 flag | ||
|
|
||
| ## Changes Required | ||
|
|
||
| This fix requires updating `common/httpx/httpx.go` to set the `DisableHTTP2` option in retryablehttp-go when the user specifies `-pr http11`. | ||
|
|
||
| ### Location | ||
| File: `common/httpx/httpx.go` | ||
| After line 79 (where `retryablehttpOptions.Trace = options.Trace` is set) | ||
|
|
||
| ### Code to Add | ||
| ```go | ||
| // Disable HTTP/2 fallback when http11 protocol is explicitly requested | ||
| if httpx.Options.Protocol == "http11" { | ||
| retryablehttpOptions.DisableHTTP2 = true | ||
| } | ||
| ``` | ||
|
|
||
| This ensures that when users specify `-pr http11`, the retryablehttp client will not automatically fall back to HTTP/2 on protocol errors, respecting the user's explicit HTTP/1.1-only preference. | ||
|
|
||
| ## Dependencies | ||
| This fix depends on the DisableHTTP2 option being added to retryablehttp-go (PR #521). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #!/bin/bash | ||
|
|
||
| # Script to apply the fix for issue #2240 | ||
| # This adds the DisableHTTP2 option when -pr http11 is specified | ||
|
|
||
| FILE="common/httpx/httpx.go" | ||
| BACKUP="common/httpx/httpx.go.backup" | ||
|
|
||
| # Create backup | ||
| cp "$FILE" "$BACKUP" | ||
|
|
||
| # The fix: Insert after line 79 (after retryablehttpOptions.Trace = options.Trace) | ||
| # We need to 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 | ||
| # } | ||
|
|
||
| # Using sed to insert after the line containing "retryablehttpOptions.Trace = options.Trace" | ||
| 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" | ||
|
|
||
| echo "Fix applied to $FILE" | ||
| echo "Backup saved to $BACKUP" | ||
| echo "" | ||
| echo "Please review the changes with: git diff $FILE" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go | ||
| index original..fixed | ||
| --- a/common/httpx/httpx.go | ||
| +++ b/common/httpx/httpx.go | ||
| @@ -76,6 +76,11 @@ func New(options *Options) (*HTTPX, error) { | ||
| var retryablehttpOptions = retryablehttp.DefaultOptionsSpraying | ||
| retryablehttpOptions.Timeout = httpx.Options.Timeout | ||
| retryablehttpOptions.RetryMax = httpx.Options.RetryMax | ||
| retryablehttpOptions.Trace = options.Trace | ||
| + // 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 | ||
| + } | ||
| handleHSTS := func(req *http.Request) { | ||
| if req.Response.Header.Get("Strict-Transport-Security") == "" { | ||
| return |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sed -iis not portable across macOS and Linux.On macOS (BSD sed),
sed -irequires 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
📝 Committable suggestion
🤖 Prompt for AI Agents