Streaming requests with fetch - HTTP 203

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
JAKE: Three, two, one. SURMA: Three, two, one. [MUSIC PLAYING] JAKE: Hello, Surma. SURMA: Hello, Jake. JAKE: This is weird, isn't it. This is very different. SURMA: It is. JAKE: I mean-- SURMA: I can't even see you. JAKE: --it's exactly the same as we did for web.dev Live, so-- SURMA: Yeah. I feel like in web.dev Live, I could actually see your face. But right now I can't, because I can see your screen and-- JAKE: Well, do you know what, the screen is going to be more interesting, because-- yeah. Do you know what, it's been a while since I spoke about streams. SURMA: It's-- JAKE: And you know how much I love streams. SURMA: Do you remember which year was the year of streaming, declared by you? JAKE: [LAUGHS] So for everyone watching, I, in 2016-- SURMA: 2016. JAKE: --wrote an article called, "2016 is the Year of Streams." And it was for me. SURMA: [LAUGHS] And that's all that counts. JAKE: Not sure the rest of the world was that bothered. But you know, streams are continuing to happen and get more exciting, and that's what we're going to talk about today. This is the fetch API. You've seen it before. This is making a request with a requestBody post request. The body can be a number of different formats. So it can be a string, which means-- SURMA: Yes. JAKE: --it can be sent as text, right. SURMA: Well, encoded to UTF-8, more implicitly, and then send those back-- JAKE: Oh, OK, Mr. Pedantic. SURMA: Yeah, we had an episode on encoding. I'm not going to-- that's what I do now. JAKE: And the nice thing is it will add the content type for you as well. SURMA: It will. JAKE: And it will add the UTF-8 part as well. And same with a blob. It'll just send that as binary, but it'll take the content type from the blob itself. SURMA: Mm-hmm. JAKE: Uint8Array, it will send that as just-- SURMA: Application/octet-stream? JAKE: Application/octet. Yes, that one. Well remembered. SURMA: [LAUGHS] JAKE: Form data, that's a more interesting one, because it's multi-part encoding, because it could contain everything a form can contain, including files. And then there's the ye olde super basic URLSearchParams. SURMA: That one I didn't know, actually. Cool. JAKE: OK, so one of the nice things about the form data is the form data can take a form object in, and it will just represent that form as multi-part form data. If you're wanting to convert it down to URLSearchParams, you'll have to do it yourself. It's like two lines of code. It's not much. And of course, you would have to-- if your form contains a file, you would have to throw. But this will give you the standard URL encoding stuff. But with all of these formats, you need all of the data up front, ready. SURMA: Yeah. You have all the forms and all the data, and then it turns it basically into a binary encoding under the hood. And then once that binary encoding representation is done, that's what gets sent to-- along with the fetch for the request, isn't it? JAKE: Exactly. But, ooh, look at this. This is a Chrome 85 with experimental platform features flag. There's an origin trial as well, so you can use it on live. You can do this, which changes stuff. It's a new feature for the web. OK, to describe why, let's just talk about streams, because I love-- SURMA: It's interesting, because fetch responses have always been streams, right. Like the body of a response has always been the stream that you can process as the-- JAKE: Ish. SURMA: Ish? Oh. OK. JAKE: There was a time. We released fetch, and then sometime later, we put streams in. SURMA: Oh, OK. JAKE: Yeah, they lagged behind by, I'm going to say a year, because that's a number that I've heard of. But I can't-- that might not be 100% correct. But it was some-- SURMA: How did that work in interop? Like what was body before-- oh, it just didn't have body, basically? You just had to-- JAKE: Just didn't have body. SURMA: Just had the methods where you say, text JSON ArrayBuffer. JAKE: Yes. SURMA: Ah, OK. JAKE: Exactly that, which is what requests are like right now. SURMA: OK. JAKE: So it's the same thing going on. But yeah, like you said, the web is a streaming thing by default, right. If you go to a page and it serves HTML, you'll start seeing it as it's downloaded, same with images and video. And this is great, right, because you can do something with just a little bit of the response. I've actually-- I've spoken to teams, and they're like, oh, we're pulling like 100 search results down the wire, but we think we're going to detect if the user's on 2G and instead serve just 10 results so they'll get it quicker. It's like, no, no, no, just serve a streaming format, because then, as soon as you get that first result, you can do something with it. And you don't need to worry about the user's connection speed, because you'll just get those things one by one, because that-- SURMA: For me-- JAKE: --that's why I love streams. SURMA: --the fact that the web is streaming is the biggest fundamental difference to all the other platforms, most notably Android and iOS. Because on Android, you install your entire app, and everything is there, while on the web, you have nothing. And you have to press the most critical resources through this tiny straw that is the network. And that's what we optimize so that whatever is needed first arrives first in the best order possible so that it can be shown and you only download what you need. I think that's the biggest difference and why so many assumptions and patterns from native land don't really carry over to the web, because we have to stream it. JAKE: Yeah, And that's-- ah. So yeah, on native, you have to download everything before you can do anything, which is not true on the web. But that's why-- I'm not going to turn-- this is not going to turn into a framework rant, but I-- SURMA: [LAUGHS] JAKE: It's one of the things that I get annoyed at is when I see sites just throw away this huge benefit the web has. SURMA: And it is a benefit, right. It's not just a constraint or something that makes it harder. It's a good thing. JAKE: Absolutely. Absolutely. And yes, we've had it on responses for a few years now in fetch. So here's what it looks like. So you do a fetch, and you get this. You get the body. You can get a reader for the body. And then I'm just going to do a while (true) here. Get some data. When you read data from a stream, you get these two variables, so you get value and done. If it's done, that means you've received the whole-- SURMA: You're done. JAKE: --response. Otherwise-- yeah. Everything's received. Otherwise, you get this value. And when you've received them all, that means you've got everything. So these values are Uint8 arrays of bytes. And the number you get and the size of those arrays, that depends on the network conditions, right. Like if you've got a fast connection, you'll get a few big arrays. If it's a slower connection, it will drip feed lots of smaller arrays just to represent the same data. Quite often, you don't want to be dealing with binary data. Quite often, you're receiving a text response-- SURMA: Yeah. JAKE: --is one of the more simple ones. So yes, you can use a TransformStream, which is-- well, that's only in Chrome right now still, unfortunately. But you can use a lower level text encoder, decoder thing. But I really like the TransformStream, because it's nice. You just do this, and now all the chunks are text. Yay, that's much more easy to deal with. Anyway, like you said, we've had this for a long time, cross-browser, the whole response streams thing. The new bit that I want to talk about is request streams. And here is how they work. So I've got a content type header in there. That's optional, but it's good practice, just to tell your server what you're sending. And then yeah, there's a stream, and that's it. And that just works. So inside the readable stream, this starts callback. It's called straight away. And from then on, I can just start pushing stuff into the stream. But this demo is actually going to fail, because requests, they expect the data to be Uint8 arrays. SURMA: I was about to ask, because the body could be a string, but does it also actually take streams of strings? And you're saying, no, it doesn't. JAKE: No. No. So problem solved. There you go. We've got these TransformStreams. SURMA: Yeah. JAKE: Sorts everything out. And so yeah, there you go. That's going to send the UTF-8 bites for hello down the wire. And then five seconds later, I can send world and close the stream. And that's it. That's how streaming requests work. The uses cases I can think of is like you could warm up a connection. So say you've got a chat app. As soon as the user focuses the text input to start typing, you could start that request up and get all the headers and stuff out of the way and then just send a text once they hit send. Obviously, then on top of that, there's more genuine streaming formats. You could send audio data, video data. So you can just start building up the request body like that. So in this example, I've created a readable, and I'm giving that to fetch. But sometimes it's easier to use a writable, and you can do that as well. You can make your own TransformStreams, like we've seen with the text encoder, text decoder. But this one here, this doesn't have an actual transform defined. It's just an empty TransformStream. And what that does, that becomes an identity stream. And all that means is, anything that goes in the writable comes out the readable, and that's it. But that's useful, because now I can send the readable part, is the body, to fetch. And now anything I send to the writable is what's going to actually be sent over the wire. And you can do some fun stream composition stuff with this, right. Like I'm going to fetch something completely different here, and I'm going to pipe its body to the writable. So now I'm fetching from one URL and sending it to another. I'm using the user's machine as a kind of proxy, all in the streaming way. The other user doesn't have to buffer all the data. It doesn't have to end up all in their memory. And I can even compress that data on the fly. So this is using the compression TransformStream. This is a Chrome only thing as well right now. It's a web standard, but it's only implemented by Chrome. But you can see how composable streams are just plugging them together like this. SURMA: Yeah, I mean, I wrote an entire library where I was trying to mimic observables with streams, because they basically give you the same param. There are some differences, and I wrote a blog post, blah, blah, blah. But basically, you can write really complex, or declare really complex data flow structures with streams, which is exactly what they're for. And TransformStreams are the heart of that, and it always breaks my heart that only a few-- not all browsers have TransformStreams yet, but once they do, be it's going to be very interesting. JAKE: Also, it's good that you mentioned your library, because now we have to put a link to that in the description. SURMA: Ah. JAKE: I see what you did there. Very smart. Yeah. Oh, well. But then you also mentioned my blog post in 2016. Although it was to embarrass me, it's still clicks on my blog. So there we go. SURMA: Well done, us. JAKE: Fair's fair. [LAUGHS] OK, so that was the good news. There are some catches, and I find this stuff really fascinating. There are some cases where these new request streams are going to be limited, or, I don't know, things might go wrong. HTTP redirects are an interesting one. SURMA: Oh, god. JAKE: If you make a request and you hit a redirect other then a 303-- 303s are fine. SURMA: OK. JAKE: But anything else, it will fail. SURMA: Right. JAKE: And the reason for this is the other redirect codes. Well, 307 and 308, they require the body to be re-transmitted to the new URL. And with streams, it's gone, right. SURMA: Of course, yes. JAKE: So it doesn't work. 301 and 302 sometimes don't send the body again but sometimes do. So I think just for consistency, we've decided-- I mean, in general, avoid 301 and 302, because they're weird and legacy. There's is always a better one. You've always got 303 and 307, which are better equivalents. But yeah, the stream can't be restarted. Maybe someday we'll find a mechanism for that, but right now, it doesn't work. One more thing-- I say one more thing. I've got loads more to talk about. By default, this is only going to work over HTTP/2. If you want it to work on HTTP/1, we've made a little opt-in for it. SURMA: Oh, that's interesting. JAKE: And this is non-standard as well. This is just during the experiments. And the reason we're doing this, this is because of a key difference between HTTP/1.1 and HTTP/2. SURMA: Oh, 1.1 has chunking, doesn't it? JAKE: Yes, it does. So here's how you post something over HTTP. SURMA: Mm-hmm. JAKE: So in this case, the content length is important, because that's how the other side knows how much data it's going to receive. It also then knows when the end of the message is, because it's when you sent that amount of data. But in a streaming world, we don't know the length in advance. That's the whole point. So we need a different way to do it. And like you said, one way of doing this arrived in HTTP/1.1, and that is chunked transfer encoding. It's difficult to say. But this is the same message now in two chunks. SURMA: Yeah. But if I specify the content length, would it work over H v1.0 as well? JAKE: So content length is one of those headers that you're not allowed to send in fetch. SURMA: Ah. OK, fine. JAKE: Yeah, it's one of the things that you've not been able to do it before, so letting you do it now is-- there's that security question of-- I think if we exposed a way to do it, it wouldn't be just by saying the header. It will be by giving us a number, because then that would at least prevent you saying content length foo or-- SURMA: Yeah, I guess. JAKE: --which might cause servers to do something weird. SURMA: Why is that a question of security? A server must always expect to get malformed requests, right? JAKE: Well, do you know what, Surma, not all servers behave. So-- SURMA: True. JAKE: --this is part of the problem. Like if you send unexpected data, some servers will accidentally do things that they weren't meant to do. And the web always plays it safe in terms of that-- SURMA: Eh, all right. JAKE: --and tries not to cause problems. But yeah, so in chunked encoding, you get the length of a chunk, and that's a hex number, and then the content. And off it goes until you get a 0 for size, and that's the signal that that's the end of the response. So that's been around forever, HTTP/1.1. It's really common in responses, but this is the first time browsers have been able to do it with requests. SURMA: I see. OK. JAKE: Going back to that whole thing about servers not expecting it, we're worried about compatibility. So we've made it opt-in for now. It's not a problem in HTTP/2. It doesn't have chunked encoding at all. It has frames, which are kind of like chunks, and it uses them everywhere, so there's no compatibility concern at all. So no worries. SURMA: I see. OK. JAKE: So yeah, it's just a worry of, what are HTTP servers was going to make of this? Is it going to work? Is it going to break things? But we've put that in there as a test so developers can try it out and report back. Another gotcha slash difference slash restriction, HTTP is bi-directional, and that's something I always forget about HTTP. You can start receiving the response before you finish sending the request. And actually, it depends on who you-- SURMA: I thought that was like, many servers do, but it's actually not necessarily specced that way or something like that. JAKE: Yeah, it depends who you ask, whether that's what was intended by the spec or not. But yeah, lots of servers support it. I know that Node server supports it really well, probably quite a few others. But yeah, a lot of implementations don't. A lot of front end servers don't support it, and neither does Chrome. And I don't think the other browsers do under the hood either. So that kind of leaks onto this. So in this model, you need to complete the request before you get any of the response. SURMA: Interesting. JAKE: It'll buffer any response that you get before that. SURMA: So you can't just pretend to build your own WebSockets with-- on top of fetch and HTTP/2. JAKE: Well. SURMA: Well? JAKE: You can sort of hack around it. So you could have two fetches, one for sending and one for receiving. SURMA: Yeah, OK. JAKE: So you can create a writable and then send that as a streamed request, and then make another request and use that as the streamed response. SURMA: Mm-hmm. JAKE: And now you've got a readable and a writable for bi-directional communication. But it's actually two HTTP requests. But yeah, this is kind of-- SURMA: Which, over H/2, is still just one connection, so it's kind of fine. So yeah, it is pretty much equivalent. JAKE: Yeah, exactly. The only thing you need is something on the server side to tie the two things together and know that it's part of the same thing. SURMA: Right. But you wanted-- you probably needed that anyway, even if it had been possible to do it via one request. You need to do some bookkeeping there anyway. JAKE: Well, if it's single request, the server just does that by default, right. SURMA: Oh, OK. Yeah, yeah, OK, I see what you mean. JAKE: But I've actually built a demo of this. I'm glad you asked about the WebSocket thing, because that's the one demo I've got. SURMA: [LAUGHS] JAKE: Yeah, and it's just yeah, a super stupid demo. I just start typing in this text field, and it starts appearing in the page. But it's actually making a round trip to the server-- SURMA: [LAUGHS] JAKE: --to do that. SURMA: [LAUGHS] Well done. JAKE: It's really pointless, I know. I just wanted to show, in as little code as possible, how you could recreate the WebSocket kind of thing. SURMA: Yeah. JAKE: But it shows how you could do something different. SURMA: I like it. JAKE: But yes, we have worries around compatibility. So what could go wrong? You need a server that can handle streaming requests, like Node. Like Node.js-- loads of servers do, right. SURMA: Mm-hmm. JAKE: It's pretty common. I don't know if PHP does, but Node, definitely does, and that's what I've used in this demo. But you often end up with a chain of servers before you get to Node. Like you'll have a front end server, something like Apache or Nginx sitting ahead of it, and then you might have a CDN. And if any of those decide to buffer-- SURMA: Yeah. JAKE: --the whole request, then game over. SURMA: For example, I think I know that most of the serverless architectures buffer the entire body before the function is invoked. JAKE: Yes. SURMA: Which is sad. JAKE: Absolutely. Also, if you are running HTTP/1.1 somewhere in that chain, it might get confused. It might not be expecting that chunked request. So the only HTTP/1 server I was using in that demo was Node. Then the front end server was HTTP/2, and it all worked fine. But yeah, it could go wrong somewhere else. But this is something that's under your control. So well, you might not control the front end server if you're a client side developer. But it's at least owned by the company you're working for or something like that. It could get worse, though. It could be problems on the client side. Now if you're running HTTPS, you don't have to worry about intermediate proxies. So that's one way to avoid that. SURMA: Which you should be doing anyway. JAKE: --the user-- yeah, absolutely. But the user might still be running a local proxy. SURMA: Yeah. JAKE: A lot of antivirus software or internet security software, it will install a certificate so it can sit as an intermediary between the user and the web, so it can monitor all the traffic and, I don't know, filter it or whatever. And again, if that buffers, it's not going to work. And if it gets confused by the chunked encoding thing, it might just break. Bad luck. SURMA: This is giving me anxiety with all the things that could go wrong in the road from your machine to the server and back. JAKE: Yeah, and that's why we made HTTP/1.1 opt-in-- SURMA: Yeah. JAKE: --because that's where it's more likely to go wrong. The buffering thing, there's not a lot we can do about that. So you really just-- you can sort of feature test it. Just do that WebSocket-y thing, and send a message on one, and if you get the server pings back, then yay, it's working. But yeah, so that's what-- it's a new feature. It's an origin trial, so you can try it on live. It's in Chrome 85 with the experimental web platform features. It could go wrong. It could be exciting. So we want people to play with this. We want people to feedback. Like, ah, there was a bug in Chrome once that it turns out-- we changed somewhere we made HTTP connections. SURMA: Mm-hmm. JAKE: It made it all the way to-- I think it made it to dev or beta until it was actually a VP in Chrome suddenly went, my internet's not working anymore, or my Chrome's not working. And it turned-- actually, it was the whole internet for him wasn't working. And it turns out one particular brand of router in the US, a really popular one, this particular way Chrome was making these connections just caused it to crash and flip out and not make any more connections. So-- SURMA: Oh, great. JAKE: --when we change-- this is why we're so cautious about how-- when we change how requests are made-- SURMA: Yeah. JAKE: --in the browser that we're all like, ah, we need to do this very, very carefully. So yeah, we want people to try it out. We want to find out if there's a router somewhere that sets on fire when this happens. Please report that. And also, we're sorry if that happens in advance. We don't expect that to happen, and I don't think we're liable. I don't know, I'm getting into very difficult legal territory now. SURMA: Yeah, it's like let's move on, move along. JAKE: [LAUGHS] But yeah, that's all I've got. Try it out. Let us know what you build with it. And hopefully-- well, let us know if it goes wrong, because we're definitely interested to hear-- SURMA: Yeah. JAKE: --why and how. But yeah, let us know what you build with that. SURMA: Definitely. All to your DMs and Twitter mentions, please. Or even if you don't build anything with it, just ping Jake. JAKE: Just-- thanks, mate. SURMA: You're welcome. JAKE: So we're going to make a stream, and we're going to fetch it. SURMA: Mm-hmm. JAKE: So I've got a content hy-- type header in. Try that again. I've got a contype-- third time's the charm. That's going to be-- it's going to send UTF-8 bate-- bytes. [BLEEP]
Info
Channel: Google Chrome Developers
Views: 24,708
Rating: 4.9447398 out of 5
Keywords: GDS: Yes, streaming request, experiment landing in Chrome 85, experiment landing, Chrome 85, compression streams, reactive programming, the year of streams, origin trial details, social distancing, chrome trailer, HTTP203, HTTP203 podcast, funny web videos, funny tech videos, tech, videos, socially distant, web videos from home, tech videos from home, new in tech, new videos from Chrome, Chrome, Chrome Developers, Web, Chrome devs, developers, Google, HTTP, Jake and Surma
Id: G9PpImUEeUA
Channel Id: undefined
Length: 22min 24sec (1344 seconds)
Published: Tue Aug 18 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.