HTTP 103 – An HTTP Status Code for Indicating Hints
datatracker.ietf.org
datatracker.ietf.org
HTTP/1.1 103 Early Hints
Link: </main.css>; rel=preload; as=style
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script
HTTP/1.1 200 OK
Date: Fri, 26 May 2017 10:02:11 GMT
Content-Length: 1234
Content-Type: text/html; charset=utf-8
Link: </main.css>; rel=preload; as=style
Link: </newstyle.css>; rel=preload; as=style
Link: </script.js>; rel=preload; as=script
The client can start preloading the CSS and JavaScript before the main headers arrive. This is a nice optimisation.There are various security risks with sending multiple headers to non-conforming clients hence: "Therefore, a server might refrain from sending Early Hints over HTTP/1.1 unless the client is known to handle informational responses correctly."
> HTTP/2 ([RFC7540]) server push can accelerate the delivery of resources, but only resources for which the server is authoritative. The other limitation of server push is that the response will be transmitted regardless of whether the client has the response cached. At the cost of spending one extra round-trip compared to server push in the worst case, delivering Link header fields in a timely fashion is more flexible and might consume less bandwidth.
See: https://h2o.examp1e.net/configure/http2_directives.html#http...
The server is going to be slowly building up a real reply. But it also knows that most likely, the real reply is going to need a list of assets that doesn't change regardless of what the reply is.
So to save time, the server is going to spit out a few packets early that say "Hey, while you're waiting on me, go load these".
Am I understanding this right?
It's like your server at the restaurant bringing you appetizers early because they're fast to cook and you were just going to be sitting there doing nothing anyways.
In the case of a Link header, it would thus be OK to fetch the resources, and if you don’t end up needing them, c’est la vie. In the case of CSS, it’d be perfectly legitimate to load the CSS in anticipation. It would even be OK to execute JavaScript up to the point of its needing to interact with the document or HTTP. But it would be not OK to execute anything that was not safe (in the terminology of the HTTP spec on methods).
I’d extend your parallel in appetisers, but it gets mildly disturbing quite quickly.
A recipe with a shopping list. Only you get the shopping list first and some of the items might be for another recipe (and you might already have them.)
Perhaps an alternative analogy is branch prediction? Browser is permitted to run along a particular path of execution because it applies in most cases. But you end up throwing away your work in the case that the prediction turns out to be wrong.
In terms of practical application for this - most modern sites run their content over an additional layer of application logic that runs in the browser e.g. React, or Angular or something. These hints can tell the browser that such-and-such an application framework is required to display the content, and to go get it before the content is available.
See https://tools.ietf.org/html/rfc7231#section-4.2 for the definitions.
(Keep in mind with 103’s semantics that we’re not talking about HTTP methods, we’re merely borrowing its definition of “safe”. Speculative JavaScript execution, for example, is perfectly feasible; you’d simply have to pause execution as soon as it tried to mutate its environment or make HTTP requests or things like that. How useful it is is another question.)
Idempotent means that one invocation has the same effect as more than one invocation.
Safe means that zero invocations has the same effect as one or more invocations.
"A client MUST be able to parse one or more 1xx responses received prior to a final response, even if the client does not expect one. A user agent MAY ignore unexpected 1xx responses."
They don't need to be requested. Supporting servers will just send them, unsupporting clients will just ignore them (barring the potential security issue with broken HTTP/1.1 implementations which is noted in the 103 spec).
I use tabix.ui for Clickhouse, clickhouse generates tons of extra headers as progress bars. Chrome will just cut the connection and says header too large error.
http 103 will just be abused like that. People will make megabytes sized headers
http/2 push sounds great at first glance for doing things like sending your CSS/JS bundles downstream with the HTML. The problem with this is that only the system that builds the HTML knows what needs to be pushed and only the system terminating the http/2 connection can send the assets. That means your app servers would need to terminate the http/2 connection and would be serving static assets. Serving static assets from your app server sucks for a lot of reasons. Instead what you want to do is tell some upstream proxy that is good at serving static assets what it should push. This is done by having the upstream proxy parse the Link HTTP headers in the response, which many services already do. But, today they can't start pushing assets until the main response is being sent since they need the headers from it to instruct them. Ideally you'd start sending assets ASAP, while the app server is building the page. This is exactly what the 103 response is most useful for.
e.g. You have a CDN front for your app servers, the app server sends an early 103 as soon as it knows what assets the page needs, the CDN intercepts this response and uses it to start pushing the assets to the client from the local edge node using http/2 push. This lets you benefit from http/2 push of your assets while processing the main response without your app server needing to serve static assets or even know about http/2 itself.
To me, I would say increasing efficiency (or decreasing latency) was a central design goal of HTTP2. So HTTP2 includes it's own framing, which allows multiplexing across the connection. This helps avoid the overhead of establishing multiple parallel connections to speed up page load, head of line blocking, running slow start on each connection, etc.
FWIW the use case I'm describing has already been added to Rails / Puma:
http://eileencodes.com/posts/http2-early-hints/
While the RFC does state that what you described can happen at a caching intermediary, I don't believe that's where the value lies or the primary use case. To me, much of the value added is to get the 103 to the client early, and allow it to begin making decisions on whether the content is in it's local cache, or to begin making requests to third party servers.
This helps eliminate some of the weaknesses with HTTP push, mainly being that only content the server is authoritative for can be pushed, and that the server doesn't know whether the resource to be pushed already exists in a client cache.
It's a balancing act of course, but if the resource being pushed is above a certain size, pushing the resource on each request could add overhead, not reduce it.
> This helps eliminate some of the weaknesses with HTTP push, mainly being that only content the server is authoritative for can be pushed, and that the server doesn't know whether the resource to be pushed already exists in a client cache.
When a server sends a push to the client it first sends `PUSH_PROMISE` command to the client and the client may cancel the push, e.g. if it already has the asset in cache based on url. Assuming things move forward the server sends a HEADERS frame which can also trigger to the client to cancel the push, e.g. in response to an etag, though at this point some DATA frames may be in flight.
You are correct that you can't push assets for another domain, but I don't see this as much of a problem as there are many other benefits to having all requests go a CDN/reverse proxy that can handle the static asset serving, like regional SSL termination. There are of course third party scripts that may be needed, but it's good practice make these non-blocking for page rendering anyway so if they are a bit behind that is probably ok, if not preferable.
We added early hints support to Puma:
https://github.com/puma/puma/pull/1403
Then added early hints to Rails:
https://github.com/rails/rails/pull/30744
Such that if you run H2O (or any webserver that supports early hints) in front of Rails, you can get asset push support for free. eileencodes wrote a great blog post about her work on this:
Sure, Google might get a millisecond or two less in their apps, but who in their right mind has the engineering time to implement this very, very small optimization when there's many other things to be done that probably have a higher priority?
Most modern SPAs also have a static index.html which shouldn't take long to serve anyway.
This is going to be abused by cargo cult brogrammers who heard that "103 will make your site faster!!!1" and put every single resource as a 103 making the time to 200 even longer than the time you actually potentily gain.
Now, implementation of this by browsers is going to take a long time, and then we will see incompatibilities trickle down all the way, with Safari taking a few years after everybody else has it and IE/Edge also taking forever.... I should rather work in a farm and harvest tomatoes...
Take a site like reddit, where the HTTP request can take a second or 2 to return when there is a really high server load.
Reddit could return HTTP 103 hints to the clients to start downloading useful assets ahead of time so when the HTML payload finally arrives everything is ready to start execution. And if this does become a standard, it can get built into web servers and will be as simple as adding a few lines to a config file.
Obviously the "correct" solution is to reduce the time it takes to return the HTML from the server, but if it can improve load times for a nonzero amount of people for not much work, it's a win in my book (even if it's not ideal).
What happens to hints in a redirect chain?, i.e. 301 103 301 103 200
What headers are applicable to the 103 beyond Link? If a 103 included Refresh, could this be used like a timeout (redirect after Refresh time if actual request did not respond?)
This...
> A server MAY use a 103 (Early Hints) response to indicate only some of the header fields that are expected to be found in the final response.
... implies that all possible HTTP headers from the final response may be in the 103.
RFCs need less ambiguity, lest clients and servers start to fill in the gaps and have divergent behaviour.
Given hints with SRI hashes, the client can fetch/verify them and be ready when the HTML arrives, or even better, consult the cache and verify to avoid fetching and save bandwidth.
This RFC very strongly points to hints as the purpose, but to me appears to be generically implemented. So HTTP 103 can send any header, but only the final headers are authoritative. So it's really up to wherever those headers are defined to indicate what can/cannot be indicated by the headers. In other words, what a header can represent is likely a different discussion.
Subresource integrity is actually a w3c recommendation and not an IETF recommendation, so the checksum doesn't appear to be conveyed through headers. But trying to think about it logically, I don't think it's necessary to have the hashes at this stage. The web browser get's a HTTP 103, and see's the link rel and begins requesting and caching the resource. The web browser then loads the HTML, which includes the subresource integrity, that it then uses to check the result it already downloaded and cached. To validate that resource, it would need to download it anyway, the only thing HTTP 103 did was allow the the content to start downloading sooner in the process.
If client intends to send a POST request with request body large enough to try to avoid wasting bandwidth (in case server might reply with 3xx,4xx or 5xx), the client will send only headers including "Expect: 100-continue" header, then server indenting to reply with "200 OK", will instead first reply with "100 Continue" than client will send POST body, and finally server sends "200 OK".
[1] https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8....
Also the exchange seems to be subtly different for each of them:
100 Continue: Client sends start of request, server sends 100, client sends rest of request.
101 Switching Protocols: Client sends request, server sends 101, client sends non-http request.
103 Early Hints: Client sends request, server sends 103, server sends response.
Really? Let's see, at least one extra DNS lookup can happen, a URL fetch can happen, this is totally enough for advertising tracking.
It's a sneaky unwanted network request un-controllable by end users. And it happens even before you load the page.
All ad-blocking tools/addons today works by parsing the webpage content, the 103 header extended the game from the body to the header.
Advertising tracking at its best.
[1] https://w3c.github.io/preload/
[2] https://bugs.chromium.org/p/chromium/issues/detail?id=471199
I think this is the case where X is innocent and Y is innocent, but combined X and Y leaves a huge exploitable surface.
No, no they don't. They primarily work by hooking network requests through e.g. chrome.webRequest[0] and blocking certain URLs from being loaded. This continues to work with the multiple header-based mechanisms for downloading things.
So yes, the file would get fetched, but no, it can't affect the page being rendered by the browser.
"Aside from performance optimizations, such evaluation of the 103 (Early Hints) response's header fields MUST NOT affect how the final response is processed."
But beside, if you are in a position to insert HTTP responses, you are in a position to inject arbitrary JS.
Is it common to serve something other than http 200 and to take a long time to do it -- but also be able to send something early sooner? Most of the the slow pages I've worked on were either slow to load all the details, but I knew it was 200 near instantly, and could get all the headers and the HTML head out pretty quickly, or had some structural problems that made them terribly slow, but we couldn't get anything out quickly anyway.
Still very dubious proposal with minimal benefit(s).
I guess it is related to preloading? Not quite getting the purpose.
Edit: ahh, the complete story is here: https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-0... (fixed). Note that someone fixed the capitalized "A" in "approved" in the title. It confused my initial reading of the article.
1. Client requests X.html (which requires Y.css and Z.js) from Proxy.
2. Proxy forwards client's X.html request to Server.
3. [Server takes a long time to respond]
4. Server sends a 200 response to Proxy with X.html.
5. Proxy, per some configuration, adds Preload headers to this response indicating that Y.css and Z.js should be immediately loaded once this response is received, and sends it to Client.
6. Client requests Y.css and Z.js.
With the 103 status code, this would look like:
1. Client requests X.html (which requires Y.css and Z.js) from Proxy.
2. Proxy forwards client's X.html request to server and, per some configuration, immediately sends a 103 response with Preload headers for Y.css and Z.js to Client.
3. [Server takes a long time to respond]
4. In parallel, Client requests Y.css and Z.js.
5. Server sends a 200 response to Proxy with X.html.
6. Proxy sends this response to Client.
Note that since Y.css and Z.js are requested sooner, the final load time should be less.
Someone else suggested https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-0..., which has more context without a need for a second click.
Let’s hope this becomes widely adopted soon. Does anyone know how long it took till other HTTP features that were added later (not HTTP2) were widely supported?
103 Early Hints
Not 103 ApprovedPersonally I think it is more complex than it was needed to be. stream priority can also be a PITA.
Ah and I basically forgot, due to the state it is practically not the best protocol for mobile clients over 2G/low end 3G.
It's not the worst, I think h2 would be fine if it would be 10 years earlier, since I think we can learn a lot from the behavior of h2 in real world scenarios and than use the best things from http/1.1 and h2 to make h3.
HTTP2 is like the good parts of SPDY and what was learned over that time period (I think).
I think h2 is perfect for RPC like architectures, interconnection between servers is a perfect fit for something like h2. but when it comes to real world traffic it depends on too many parameters. The sad part is, is that h2 will take forever to gain that data and to have widespread usage. the hard part will be 100% h2, a lot of stuff prolly only uses h2 at the edge.
Also most flaws of h2, will probably be fixed without a new protocol, i.e. in the next 10 years I hope that _all_ mobile environments have a widespread use of 3g networks (or to say it differently, I dream of) and hopefully all network people make world wide latency better and make all networks more reliable.
This is not particularly safe to use in an HTTP/1.1 environment unless you do UA sniffing.
[1] https://tools.ietf.org/html/draft-ietf-httpbis-early-hints-0...