This made me feel nostalgic for the early web where just about everything worked this way, but the author largely missed why we stopped: error handling. When you start a stream, you can’t retroactively say “we just got a database error, so it should be HTTP 500 rather than the 200 I sent 50ms ago”. This makes it hard to make nicer error pages (most web users were familiar with the PHP “headers already sent” error message) and that was especially a problem for anything where it wasn’t entirely obvious that something had failed part way through — it wasn’t uncommon for people to think a page was only supposed to have cursory information because the interactive features were failing & HTML output had just halted when that happened. The worst case scenarios were something like a generated CSV response being truncated but the user not realizing that they were missing some data. Since it started successfully, you’d see a 200 in your logs and client side error handling wouldn’t trigger but your users would have a bad experience. Yes, you can set things up to log errors better but approximately 2% of the PHP world did that.
The main question I had was how that’s changed with HTTP/2 in terms of being able to send something like RST_STREAM to trigger client side error handlers on e.g. a fetch promise so the normal failure paths would be triggered.
The other reason things like this became moot for many sites was that fronting caches became pervasive. For things like product pages, the content would be served by an intermediary as quickly as the network could deliver it so the benefits mentioned about seeing things like script or style tags faster are less significant outside of dynamic or uncommon pages, which people tend to see only after they’ve cached core static assets. At that point, the big performance wins would come from shipping less code and making it more efficient but they’re using React so that ship has sailed.
I like Remix's paradigm of waiting to stream until the primary content is rendered and available, and then only streaming secondary content. So for example, on an e-commerce product page, all the markup representing the product would not stream. Secondary content like related products would. This allows you to 500 error if the primary content fails rendering for one reason or another. And if the secondary content fails to render during streaming, you can easily hide the content or render a placeholder.
That’s really the big one for me: can my server easily do something to cause e.g. curl to fail with a non-zero status or a JavaScript fetch to hit the error path? Satisfying that would make this approach easier to use safely.
You can terminate connection before response body is fully served (assuming you have content-length or transfer-encoding chunked). curl will exit with error code in this case. Not sure about JS fetch. You can't carry any additional error codes or messages this way, though, just to signal that something's wrong. And you can't do anything if you already sent full response. And you would need access to sockets which might not be available in all HTTP libraries.
> You can terminate connection before response body is fully served (assuming you have content-length or transfer-encoding chunked). curl will exit with error code in this case.
Those qualifiers were what I was thinking about - in most cases where this is relevant you don’t have the content length in advance, and transfer encoding chunked might not trigger this if the fault aligns with chunks or it’s being done by some middle layer.
Yes, but if you have middle layers doing that it might be done even in error states. I had that happen in multiple applications where it meant something like a big CSV file was successfully downloaded but incomplete (or, IIRC, had a Python traceback at the end). Having a simple, unambiguous way to force a failure reduces the odds of an error being easy to miss.
The main question I had was how that’s changed with HTTP/2 in terms of being able to send something like RST_STREAM to trigger client side error handlers on e.g. a fetch promise so the normal failure paths would be triggered.
The other reason things like this became moot for many sites was that fronting caches became pervasive. For things like product pages, the content would be served by an intermediary as quickly as the network could deliver it so the benefits mentioned about seeing things like script or style tags faster are less significant outside of dynamic or uncommon pages, which people tend to see only after they’ve cached core static assets. At that point, the big performance wins would come from shipping less code and making it more efficient but they’re using React so that ship has sailed.