fix: canonicalize Host header into :authority for outbound HTTP/2#877
fix: canonicalize Host header into :authority for outbound HTTP/2#877magurotuna wants to merge 1 commit intohyperium:masterfrom
Conversation
When sending HTTP/2 requests or push promises, promote the first valid Host header value to :authority and strip all Host headers from regular fields. This prevents emitting conflicting :authority and Host on the wire, aligning with RFC 9113 and the behavior of other HTTP/2 client implementations such as curl, Go's std, and Python's httpx. Fixes hyperium#876
5508e4e to
bd48b82
Compare
@seanmonstar i wasn't around for that earlier consideration, but i pinged Oliver to take a look at this. i suspect he'll be better positioned to remember the details here 🙂 thank you for checking in with us! |
|
hi @seanmonstar, thank you for your patience. looking at what this function does... /// Canonicalize `Host` header into `:authority` pseudo-header for HTTP/2.
///
/// - If a `Host` header is present, attempt to parse its first value as a URI authority.
/// - On success, override `:authority` with the parsed value.
/// - Always remove all `Host` headers from the regular header map.
i believe the relevant portions of RFC 9113 are found in section 8.3.1. the strict rules i see for clients are:
so host headers are fine for clients to emit, so long as they do not differ from the ":authority" pseudo-header. removing them unconditionally would, i think, be going a step too far. for intermediaries, the rules i see are:
i think it's reasonable for h2 to enforce RFC 9113 compliance, and avoiding mismatched :authority and Host values seems like a wise decision, particularly because these MUST NOT differ. i don't think that we should always remove all ideally, it'd be nice to leave a way for intermediaries to retain the host header field, per https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1-2.3.6. that's likely important for intermediaries like us (linkerd) that transport HTTP/1 traffic over an HTTP/2 intermediary layer. |
| use http::header::{self, HeaderName, HeaderValue}; | ||
| use http::{uri, HeaderMap, Method, Request, StatusCode, Uri}; | ||
|
|
||
| /// Canonicalize `Host` header into `:authority` pseudo-header for HTTP/2. |
There was a problem hiding this comment.
nit: this is placed in the middle of the file's use imports. we should move this somewhere lower down. conventionally, following the pattern of other code in this repo, this should go after the struct definitions (including the eoponymous Headers type).
| /// - On success, override `:authority` with the parsed value. | ||
| /// - Always remove all `Host` headers from the regular header map. | ||
| /// | ||
| /// Callers should only invoke this in an HTTP/2 context. |
There was a problem hiding this comment.
for future maintainers, it would also be helpful to add mention of the specific passages of RFC 9113 that this relates to. that would help keep the rationale of this code less implicit.
When sending HTTP/2 requests or push promises, promote the first valid Host header value to :authority and strip all Host headers from regular fields. This prevents emitting conflicting :authority and Host on the wire, aligning with RFC 9113 and the behavior of other HTTP/2 client implementations such as curl, Go's std, and Python's httpx.
Fixes #876