drop duplicate Host header on pooled HTTP/2 path (bump 0.7.0) - #32
Merged
Conversation
build_request unconditionally added a Host header derived from the URI. For the low-level http1 dispatch paths (dispatch_raw, forward proxy) that's correct -- hyper's raw SendRequest does not auto-populate Host and HTTP/1.1 requires it. For the pooled high-level client, hyper already populates Host (HTTP/1.1) or :authority (HTTP/2) from the URI itself. Adding a Host on top means HTTP/2 requests go out with both :authority and host in the HPACK block, which some origin servers and WAFs reject as a protocol violation (RFC 9113 §8.3.1). Discovered via api.hackertarget.com, which returned 400 Bad Request to every blasthttp request while curl --http2 to the same host succeeded. HPACK decoding of blasthttp's HEADERS frame showed both :authority and host pointing at the same value. build_request now takes a manual_host_header flag. dispatch_request (pooled path) passes false; dispatch_raw and dispatch_forward_proxy (low-level http1 paths) pass true. Bump 0.6.1 -> 0.7.0.
ausmaster
approved these changes
May 20, 2026
This was referenced May 20, 2026
Merged
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
build_requestunconditionally added aHostheader derived from the URI for every request. For the low-level http1 dispatch paths (dispatch_direct, forward proxy) that's correct —hyper::client::conn::http1::SendRequestdoes not auto-populate Host and HTTP/1.1 requires it. For the pooled high-level client,hyper_util::client::legacy::Clientalready populatesHost(HTTP/1.1) or:authority(HTTP/2) from the URI itself. Adding our own Host on top meant every HTTP/2 request went out with both:authorityandhostin the HPACK block, which some origin servers / WAFs reject as a protocol violation (RFC 9113 §8.3.1 implies:authorityis the canonical source and most servers expect only one).How it was found
api.hackertarget.comreturned 400 Bad Request to every blasthttp request whilecurl --http2to the same host returned 200 with identical headers. HPACK-decoding blasthttp's HEADERS frame showed:curl's HPACK had only
:authority, nohost.Fix
build_requestnow takes amanual_host_header: boolflag.dispatch_request(pooled path) →false(let hyper populate the canonical Host/:authority from the URI).dispatch_direct(low-level http1, used byresolve_ip/request_target) →true(raw SendRequest needs the manual Host for HTTP/1.1).dispatch_forward_proxy(low-level http1, forward proxy for HTTP targets) →true(same reason).Caller-supplied custom
Hostheaders continue to pass through unchanged on every path — virtualhost/host-header probes that override the URI-derived value still work.Tests
test_build_request_auto_host_from_uri/_custom_host_overrides_auto/_multiple_host_headers/_origin_form_strips_authority/_absolute_form_preserves_uri/_request_target_absolute_formupdated to pass the new flag with the matching expectations for each path.test_build_request_no_manual_host_for_pooled_path: verifies the pooled path emits no Host header frombuild_request(hyper does it).test_build_request_custom_host_passes_through_pooled_path: verifies a caller-supplied Host on the pooled path is still preserved (so virtualhost probes work).Verification
Against
api.hackertarget.comfrom a venv with the built wheel: status 200, valid CSV body, no behavior change to other hosts.Version
Bump 0.6.1 → 0.7.0.