That might slow down downloads from other pages or processes, but as far as the download from the current page goes (which uses multiplexed requests and responses over a single connection) I don't see how that's any worse than it'd be without server push.
In the worst case scenario with server-push that you described, the server decides to push content the client already has cached before pushing content the client doesn't have, and begins transmitting that redundant content before the client has a chance to cancel the push. Once the client receives push promises for the content it doesn't need, it can immediately send a request to cancel the download of those resources. Once those cancellation requests reach the server, it immediately stops transmitting the resources the client has cached, and starts transmitting the data the client actually needs.
Contrast this with the same scenario without server push, where the client parses the HTML for the main page to determine what resources it needs, then send requests for those items which the server must receive before it starts transmitting them. In both cases a round-trip from the server to the client and back is needed before the server can start transmitting the necessary assets, but without server push the client needs to download and parse the main page before it can start telling the server which resources it needs, whereas with server push it can send cancellation notices before the current page is downloaded or parsed.
It can slow things down, as the push works that way:
The client requests some page (e.g. index.html), which will trigger the push on the server. E.g. one for asset1.png. The server initiates the push by sending a push promise frame down the wire, which contains the content of a simulated HTTP request for that exact resource - like if the client sent a GET request for asset1.png through a headers frame. The client can only react on this frame when it receives it and sees that it has asset1.png already in cache. It can then cancel the stream by sending a ResetStream frame. However obvisouly that takes some time, until this push promise frame arrives at the client. Meanwhile the server might decide to not only send the push promise header, but also parts of (or the complete) data. So asset1.png already goes down the wire, and blocks bandwidth that could be used for other things too.
The client can cancel the push. But yes, there's definitely wasted bandwidth here - the reason to still do it is that connections are now fast enough that the extra download time is small compared to the time required to parse HTML/send new HTTP request/receive response/render CSS.
Getting slightly off topic, but I'm confused about an aspect of server push (maybe you can help). Even on HTTP/1, the client typically only downloads assets on the first page load for a given website, then it caches them. Subsequent requests for other pages are much quicker because the assets don't need to be downloaded again. With server push, would the server typically send all linked assets on every request? Or can it somehow keep track of which clients have cached which assets, and avoid wasting bandwidth?
Exactly. The start of the push will already consume downstream bandwidth until it's canceled by the remote side. I guess in the worst case it consume about up to the maximum window size of the stream (typically 64kB). The "classic" approach compared to that is the client initiating another GET request for each asset after it has received the index page. This requires more upstream traffic. However the webserver could directly respond with a 304 for cached assets, which means less downstream traffic.
That's my understanding as well, but it's pretty fuzzy. How does the client stop the push, and what are the performance implications of this? Pages currently include dozens of external resources, and this will get worse if developers stop using bundles and sprites (because in theory HTTP/2 makes them pointless).
Let's say you have an HTML page which links to main.css. Ordinarily, the request goes:
Client: GET /index.html
Server: <index.html>
Client (after parsing): GET /main.css
Server: <main.css>
Loading the page thus takes 2 round trips, one for the main page and one for the content. (Or more, if you have e. g. includes in the CSS.) Here's what it would look like with HTTP/2 Push:
Client: GET /index.html
Server: <index.html>, <main.css> (PUSH)
This only takes 1 round trip; since the server knows that main.css will be required shortly it can preemptively send it. In particular, this might offer a significant speedup for high-latency connections; it also theoretically reduces the need for bundling tools since you can have the server just push all of the individual files.
The obvious problem with this scheme is that if the client already has main.css then it's a waste of bandwidth to send it again. The client can cancel the push, but by the time it finds out about it a bunch of data has already been sent. There is a proposal for 'Cache Digests' which will allow the client to send a Bloom filter of its cache so the server can tell whether or not it has the file already, but as far as I'm aware no major client or server has implemented this yet.
I have very fast mobile internet (4G, so I can download megabytes of traffic in one second) yet this traffic might cost a lot. And my pings to US server might easily be second or more. So basically I risk to download an entire website which is already in the my cache for each HTTP request.
Honestly I'm not sure that I like this server push. I think, the only appropriate use is: server push if client misses some cookie and set this cookie immediately.
> Another new concept is the ability for either side to push data over an established connection. While the concept itself is hardly revolutionary — this is after all how TCP itself functions – bringing this capability to the widespread HTTP world will be no small improvement and may help marry the simplicity of an HTTP API with the fully-duplexed world of TCP. While this is also useful for a server-to-server internal APIs, this functionality will provide an alternative to web sockets, long polling, or simply repeated requests back to the server – the traditional three ways to emulate a server pushing live data in the web world.
As far as I know, this is not true. Server Push is only for the server and can only be done as a response to a request. It's not a WebSocket alternative.
Server Push means that when a client sends a request (GET /index.html), the server can respond with responses for multiple resources (e.g. /index.html, /style.css and /app.js can be sent). This means the client doesn't have to explicitly GET those resources which saves bandwidth and latency.
Even then, the average case would still be worse because the server would be sending all these unnecessary stream headers and forcing the client to send so many unnecessary RST_STREAM frames back. Each PUSH_PROMISE is supposed to contain the full set of headers for each resource.
I guess I hadn’t realized that clients could RST_STREAM on these pushes, but it doesn’t change the outcome here.
What you describe isn’t a win for anyone except a client with a cold cache, and then they start losing immediately after that. That’s why it isn’t done. That’s why HTTP/2 Push is going away.
I think that if you stop hovering fast enough and the request is cancelled, you’ll end up only sending headers, thus not affecting bandwidth that much. Not sure though.
If not the case, note that it’s just HTML we’re wasting. In the grand scheme of things it seems to me it would not have that much of an impact on bandwidth usage.
I think cancelling HTTP requests is quite a useful in some scenarios.
Eg 1) Say you're fetching heavy json/binary data on an user action, and before data arrives, he changes his action. Not only do ypu download all of the extra data, but the fetch for the next action is slowed down like hell.
Not sure, but does this mean that I can't cancel a heavy file upload if a user changes his mind?
- The client can stop an incoming push (send reset on the pushed stream). Due to latency it might waste some bandwidth, but it's deemed to be an insignificant problem, since otherwise nothing else would be using the connection anyway.
- It's independent of the protocol. Each server can invent its own method. Personally I'm hoping servers/proxies will "upgrade" some HTTP/1.1 Link header to PUSH, e.g.:
Ideally, in my opinion, when the browser makes a request for example.com/something the server should send a continuous stream of data. It could work something like this:
1) The browser sends the request along with a list of files it already has cached (maybe a special type of cache that never expires so the browser would know that it wouldn't need that file ever again).
2) Then web server sends the actual HTML that needs to be rendered, followed by whatever files the browser may need (css, js, images, etc). The browser would know where the HTML block starts and where other pieces begin, so from its point of view it's the same as if it requested those pieces. The difference is that the server anticipates these requests and sends them along all in one batch. The very first piece would be a list of files the server is sending, so the browser would know what to expect. At the end of the stream, if there are any more files that are needed that haven't been included in the main stream, the browser would just request them as usual.
Doing something like this would effectively render most page accesses to a single request, going from 10 or more requests to a single request would have quite a bit of an impact. For this to work though, both the browser and server would need to support such a feature...
Your statement is only possibly true for a single request. In any real world application, the ability to issue many requests without having to open a new connection each time and to receive results out of order is a massive win over HTTP, even before you consider things like server push allowing resources like stylesheets or scripts to be delivered immediately rather than waiting for the client to finish parsing an HTML document.
The part I don’t get is why http push wasn’t cache friendly.
The idea of fetching a graph of objects is so common.
All I want to do as a web dev is a browser client requests a tip REST object or an entrypoint js script. When the client receives that, the client can send the object/script back and all the connnected objects back together to avoid multiple round trips.
It makes me sad that this still isn’t a thing (server can reply with more than one cacheable url)
To work around this we invent all sorts of tools like graphql, webpack with layers of complexity.
Sure there is the problem of what if the client already has the resource. Then the answer is the response can optionally send content or just the url. You then have a 2nd roundtrip to fetch the content the browser doesn’t have.
At the end of the day, I just want the browser to essentially rsync-ish a bunch of url/resources from the server.
I think this just crowdsources the server’s load. Servers will certainly have to handle fewer requests thanks to PoW, at the expense of clients’s CPU time.
The upside is that the server does not go down, so at least some users will be able to access the website, compared to zero users
I think your making a strong point here, that might not get the attention it deserves.
> asynchronous requests
Because we don't live in a world where one user loads one page and then loads the next one (and if they do we should be talking about how your not using cached static content).
If I can do the work in 20ms, and it takes 100ms with framework overhead what happens when these start stacking up?
Not only do those things impact the user they impact your server... every ms that it has to maintain that connection, do work is one more ms that it isn't doing work for someone else.
I can think of one reason, although it's not necessarily a good one, or the only way to do it.
Once the connection is established, the server could push down all required resources for the page, rather than wait for the browser to request them one by one.
Many servers specifically throttle single connections, which is why there's stuff like JDownloader and browser add-ons that download the same file in several parts over multiple connections
In the worst case scenario with server-push that you described, the server decides to push content the client already has cached before pushing content the client doesn't have, and begins transmitting that redundant content before the client has a chance to cancel the push. Once the client receives push promises for the content it doesn't need, it can immediately send a request to cancel the download of those resources. Once those cancellation requests reach the server, it immediately stops transmitting the resources the client has cached, and starts transmitting the data the client actually needs.
Contrast this with the same scenario without server push, where the client parses the HTML for the main page to determine what resources it needs, then send requests for those items which the server must receive before it starts transmitting them. In both cases a round-trip from the server to the client and back is needed before the server can start transmitting the necessary assets, but without server push the client needs to download and parse the main page before it can start telling the server which resources it needs, whereas with server push it can send cancellation notices before the current page is downloaded or parsed.
reply