Skip to content
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

Let hyper automatically set the host header if needed #25294

Merged
merged 1 commit into from Dec 17, 2019

Conversation

@Darkspirit
Copy link
Contributor

Darkspirit commented Dec 14, 2019

Google's gws web server did not expect to receive an unneeded Host header via HTTP/2, therefore it responded with 400 Bad Request over a working HTTP/2 connection.

https://tools.ietf.org/html/rfc7540#page-55

Clients that generate HTTP/2 requests directly SHOULD use the ":authority" pseudo-header field instead of the Host header field.

It's hyper's job to take care of this for the HTTP/1 case, therefore we can remove old code.
When calling Client::builder() we do not disable hyper's default set_host config option, therefore hyper automatically adds a Host header for non-HTTP/2 connections based on the URI.

r? @jdm


  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes fix #25286.
@highfive
Copy link

highfive commented Dec 14, 2019

Heads up! This PR modifies the following files:

  • @KiChjang: components/net/resource_thread.rs, components/net/http_loader.rs
@highfive
Copy link

highfive commented Dec 14, 2019

warning Warning warning

  • These commits modify net code, but no tests are modified. Please consider adding a test!
@Darkspirit Darkspirit force-pushed the Darkspirit:h2 branch 2 times, most recently from e4d6f39 to 4eb6e71 Dec 14, 2019
@Darkspirit
Copy link
Contributor Author

Darkspirit commented Dec 14, 2019

Two tests failed before. These tests compare request headers before hyper plays its role. When building a request we don't yet know if legacy HTTP/1.1 or modern HTTP/2 will be used when the actual connection is established, therefore it's rather an internal matter of hyper to add a Host header in case HTTP/2 is not used.

---- fetch::test_fetch_with_devtools stdout ----
thread 'fetch::test_fetch_with_devtools' panicked at 'assertion failed: (left == right)
left: HttpRequest { url: "http://localhost:45979/", method: GET, headers: {"accept": "*/*", "accept-language": "en-US, en; q=0.5", "user-agent": "Such Browser. Very Layout. Wow.", "accept-encoding": "gzip, deflate, br"}, body: Some([]), pipeline_id: PipelineId { namespace_id: PipelineNamespaceId(1234), index: PipelineIndex(5678) }, startedDateTime: Tm { tm_sec: 12, tm_min: 3, tm_hour: 16, tm_mday: 14, tm_mon: 11, tm_year: 119, tm_wday: 6, tm_yday: 347, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 893941184 }, timeStamp: 1576339392, connect_time: 0, send_time: 36, is_xhr: true },
right: HttpRequest { url: "http://localhost:45979/", method: GET, headers: {"accept-encoding": "gzip, deflate, br", "host": "localhost:45979", "accept": "*/*", "accept-language": "en-US, en; q=0.5", "user-agent": "Such Browser. Very Layout. Wow."}, body: Some([]), pipeline_id: PipelineId { namespace_id: PipelineNamespaceId(1234), index: PipelineIndex(5678) }, startedDateTime: Tm { tm_sec: 12, tm_min: 3, tm_hour: 16, tm_mday: 14, tm_mon: 11, tm_year: 119, tm_wday: 6, tm_yday: 347, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 893941184 }, timeStamp: 1576339392, connect_time: 0, send_time: 36, is_xhr: true }', components/net/tests/fetch.rs:1114:5

request expectation (right): host header containing localhost:45979
actual request (left): no host header

Explanation:
Now removed code previously derived a Host header from URI containing localhost:45979. Now it is gone. Host header should be removed from expectation. Host is still compared as part of HttpRequest.url.

---- http_loader::test_request_and_response_data_with_network_messages stdout ----
thread 'http_loader::test_request_and_response_data_with_network_messages' panicked at 'assertion failed: (left == right)
left: HttpRequest { url: "http://localhost:37201/", method: GET, headers: {"host": "bar.foo", "accept": "text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8", "accept-language": "en-US, en; q=0.5", "user-agent": "Such Browser. Very Layout. Wow.", "accept-encoding": "gzip, deflate, br"}, body: Some([]), pipeline_id: PipelineId { namespace_id: PipelineNamespaceId(1234), index: PipelineIndex(5678) }, startedDateTime: Tm { tm_sec: 13, tm_min: 3, tm_hour: 16, tm_mday: 14, tm_mon: 11, tm_year: 119, tm_wday: 6, tm_yday: 347, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 174672457 }, timeStamp: 1576339393, connect_time: 1, send_time: 3, is_xhr: false },
right: HttpRequest { url: "http://localhost:37201/", method: GET, headers: {"accept-encoding": "gzip, deflate, br", "host": "localhost:37201", "accept": "text/html, application/xhtml+xml, application/xml; q=0.9, */*; q=0.8", "accept-language": "en-US, en; q=0.5", "user-agent": "Such Browser. Very Layout. Wow."}, body: Some([]), pipeline_id: PipelineId { namespace_id: PipelineNamespaceId(1234), index: PipelineIndex(5678) }, startedDateTime: Tm { tm_sec: 13, tm_min: 3, tm_hour: 16, tm_mday: 14, tm_mon: 11, tm_year: 119, tm_wday: 6, tm_yday: 347, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 174672457 }, timeStamp: 1576339393, connect_time: 1, send_time: 3, is_xhr: false }', components/net/tests/http_loader.rs:314:5

request expectation (right): host header containing localhost:37201
actual request (left): host header containing bar.foo

Explanation:
Now removed code replaced a possibly existing Host header with the one derived from URI. We should make sure to remove a potentially existing Host header before handling over to hyper as hyper would otherwise always respect the existing one and not derive one from URI only when needed. Host header should be removed from expectation.

@Darkspirit
Copy link
Contributor Author

Darkspirit commented Dec 14, 2019

Current PR modifies code at https://fetch.spec.whatwg.org/#http-network-or-cache-fetch step 5.16, the spec says:

It would be great if we could make this more normative somehow. At this point headers such as Accept-Encoding, Connection, DNT, and Host, are to be appended if necessary.

Now the question is whether it is necessary or not. It could make sense to remove the host header only as late as possible before actually calling hyper. Probably around this line:

*request.headers_mut() = headers.clone();

@Darkspirit Darkspirit force-pushed the Darkspirit:h2 branch from 4eb6e71 to 19226c5 Dec 16, 2019
@Darkspirit
Copy link
Contributor Author

Darkspirit commented Dec 16, 2019

So far I have not found a reason that would speak against the proposed patch:

  • In contrast to the two tests changed by this PR, WPT is testing the full stack including hyper. With this patch applied, ./mach test-wpt tests/wpt/web-platform-tests/referrer-policy/generic/subresource-test/xhr-messaging.html only fails because of a missing Connection header (#22517). Hyper automatically adds a Host header because this is a plaintext HTTP/1.1 request.
  • Fetch can not set a Host header because it is on the list of forbidden header names. It doesn't seem one could access all headers of the actual request, but only one's own entries of a custom Headers object. HTTP/2 requests would anyway not contain a Host header.
  • Firefox DevTools always uses HTTP/1.1 terminology: It incorrectly lists a Host header even none is sent via HTTP/2. Chrome DevTools precisely shows a HTTP/1.1 Host request header or a HTTP/2 :authority pseudo request header.
    Using a warp tls server with let routes = warp::header::headers_cloned().map(|headers: HeaderMap| { format!("{:?}", headers) }); I have confirmed that Firefox and Chrome indeed do not send a Host request header via HTTP/2. (If I enforce HTTP/1.1 by setting network.http.spdy.enabled to false, Firefox sends a Host header as expected.)

    {"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8", "accept-language": "de-DE,de;q=0.8,en-US;q=0.5,en;q=0.3", "accept-encoding": "gzip, deflate, br", "dnt": "1", "upgrade-insecure-requests": "1", "te": "trailers"}

@jdm
Copy link
Member

jdm commented Dec 16, 2019

I appreciate the detail you've provided here! I expect we're going to merge this as-is, but I'm waiting to merge any changes to fundamental parts of the engine like the network stack until we're finished submitting our Servo-based browser to the MS app store this week.

@jdm
jdm approved these changes Dec 17, 2019
@jdm
Copy link
Member

jdm commented Dec 17, 2019

@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

📌 Commit 19226c5 has been approved by jdm

@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

Testing commit 19226c5 with merge c3e6ee4...

bors-servo added a commit that referenced this pull request Dec 17, 2019
Let hyper automatically set the host header if needed

Google's gws web server did not expect to receive an unneeded Host header via HTTP/2, therefore it responded with 400 Bad Request over a working HTTP/2 connection.

https://tools.ietf.org/html/rfc7540#page-55
> Clients that generate HTTP/2 requests directly SHOULD use the ":authority" pseudo-header field instead of the Host header field.

It's hyper's job to take care of this for the HTTP/1 case, therefore we can remove old code.
When calling [Client::builder()](https://github.com/servo/servo/blob/1974c875a1fb12103a6916c58ed2cbef8c3e32a2/components/net/connector.rs#L116-L119) we do not disable hyper's default [set_host](https://github.com/hyperium/hyper/blob/4b6099c7aa558e6b1fda146ce6179cb0c67858d7/src/client/mod.rs#L1019-L1024) config option, therefore hyper [automatically adds a Host header for non-HTTP/2 connections](https://github.com/hyperium/hyper/blob/4b6099c7aa558e6b1fda146ce6179cb0c67858d7/src/client/mod.rs#L289-L292)  based on the [URI](https://github.com/servo/servo/blob/3f663d7ab216a841e6250b5b10ce64d34caff97c/components/net/http_loader.rs#L418-L421).

r? @jdm

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #25286.
@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

💔 Test failed - status-taskcluster

@jdm
Copy link
Member

jdm commented Dec 17, 2019

bors-servo added a commit that referenced this pull request Dec 17, 2019
Let hyper automatically set the host header if needed

Google's gws web server did not expect to receive an unneeded Host header via HTTP/2, therefore it responded with 400 Bad Request over a working HTTP/2 connection.

https://tools.ietf.org/html/rfc7540#page-55
> Clients that generate HTTP/2 requests directly SHOULD use the ":authority" pseudo-header field instead of the Host header field.

It's hyper's job to take care of this for the HTTP/1 case, therefore we can remove old code.
When calling [Client::builder()](https://github.com/servo/servo/blob/1974c875a1fb12103a6916c58ed2cbef8c3e32a2/components/net/connector.rs#L116-L119) we do not disable hyper's default [set_host](https://github.com/hyperium/hyper/blob/4b6099c7aa558e6b1fda146ce6179cb0c67858d7/src/client/mod.rs#L1019-L1024) config option, therefore hyper [automatically adds a Host header for non-HTTP/2 connections](https://github.com/hyperium/hyper/blob/4b6099c7aa558e6b1fda146ce6179cb0c67858d7/src/client/mod.rs#L289-L292)  based on the [URI](https://github.com/servo/servo/blob/3f663d7ab216a841e6250b5b10ce64d34caff97c/components/net/http_loader.rs#L418-L421).

r? @jdm

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #25286.
@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

Testing commit 19226c5 with merge e8edc6e...

@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

💔 Test failed - status-taskcluster

@jdm
Copy link
Member

jdm commented Dec 17, 2019

@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

Testing commit 19226c5 with merge 79408fa...

bors-servo added a commit that referenced this pull request Dec 17, 2019
Let hyper automatically set the host header if needed

Google's gws web server did not expect to receive an unneeded Host header via HTTP/2, therefore it responded with 400 Bad Request over a working HTTP/2 connection.

https://tools.ietf.org/html/rfc7540#page-55
> Clients that generate HTTP/2 requests directly SHOULD use the ":authority" pseudo-header field instead of the Host header field.

It's hyper's job to take care of this for the HTTP/1 case, therefore we can remove old code.
When calling [Client::builder()](https://github.com/servo/servo/blob/1974c875a1fb12103a6916c58ed2cbef8c3e32a2/components/net/connector.rs#L116-L119) we do not disable hyper's default [set_host](https://github.com/hyperium/hyper/blob/4b6099c7aa558e6b1fda146ce6179cb0c67858d7/src/client/mod.rs#L1019-L1024) config option, therefore hyper [automatically adds a Host header for non-HTTP/2 connections](https://github.com/hyperium/hyper/blob/4b6099c7aa558e6b1fda146ce6179cb0c67858d7/src/client/mod.rs#L289-L292)  based on the [URI](https://github.com/servo/servo/blob/3f663d7ab216a841e6250b5b10ce64d34caff97c/components/net/http_loader.rs#L418-L421).

r? @jdm

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #25286.
@bors-servo
Copy link
Contributor

bors-servo commented Dec 17, 2019

☀️ Test successful - status-taskcluster
Approved by: jdm
Pushing 79408fa to master...

@bors-servo bors-servo merged commit 19226c5 into servo:master Dec 17, 2019
2 checks passed
2 checks passed
Community-TC (pull_request) TaskGroup: success
Details
homu Test successful
Details
@Darkspirit Darkspirit deleted the Darkspirit:h2 branch Dec 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

4 participants
You can’t perform that action at this time.