So far, we’ve made a small HTTP server that can stream an MP4 video to the browser. But our streams only work in Firefox and Chrome. They don’t work in Safari and we can’t skip ahead. We cannot request the different range. For our video to work in Safari, we have to implement range requests. And this will also allow us to skip ahead to different starting points using the HTML5 video player. So let’s go ahead and implement these range requests. I’m inside of chapter three, chapter three, lesson two. And in the Start folder, you’ll find an index file that has the server that we left off creating.
So you can start there or you can continue working with the file from the last chapter. Now, these range requests are actually handled via the headers. So if we want to see what a range request looks like, we need to get this range variable from the headers. So req.headers or request headers. Range will let us know if we have a request range. And we’ll go ahead and log that. And we’ll take a look at what our range looks like.
And I can come over here to the terminal and node dot to run our server. And then we can come over here to the browser and hit localhost:3000. And then I’ll go ahead and click somewhere out here on the bar. Notice we’re not skipping ahead when I make that click. But what I’ve done is I’ve sent a range request. So let’s take a look at what that might look like. And here, inside of the terminal, you see that there is a first range request for undefined. That undefined request is the request that the browser makes for the favorite icon that has nothing to do with the video, so obviously, it doesn’t have a range header.
But when I make a request for our video, notice that we have a range header. So we have this bytes equals zero through. And this should give us the range that we wanna request, bytes zero through the full video or bytes halfway through the video to the end of the video, so on and so forth. So in order to handle range requests, we need to parse this header. So I’m gonna go ahead and stop the server. Clear it out. And let’s get started implementing these range requests. So what we need to do is we need to make sure that we have a range request to deal with.
I’m gonna get rid of this console.log here and just check for the range variable. So if we have a range, we will handle the range request. Otherwise we’re just gonna go ahead and send the video back. So I will take everything from our writeHead response to where we’re streaming the fileName back to the client and just place that within the else statement. So if we don’t have a range request, we’ll just go ahead and send the full video back. But if we do have a range request, we wanna handle it. And the first thing we need to do is parse the start and end ranges. So I’m gonna go ahead and create a couple of variables here.
So we use some array destructuring. And I will get the first value of the array that’s going to be returned and I will set that to start. And the second value of the array that will be returned will be our end value. So I’m going to go ahead and take our range, which is a string, and I’m going to replace that byte string. So I want to make sure our range doesn’t contain that, so we’ll do a regular expression that looks for bytes or bytes equal. And we will replace it with nothing, with an empty string.
So the range header is a string and it says bytes equal from the start position to the end position, and both of these positions are separated with a hyphen. So what I’m going to do is I’m going to split this into an array and I’m going to split it on the hyphen. This array will have two values, a start and end value. The start value should be the first value in the array and the end value should be the second value. So we pulled both of those values out of here just by destructuring them. And then I want to make sure that these values are integers because it starts out as a string. So we’ll go ahead and do start equals parseInt just to make sure this variable is the integer and we’ll parse it as a base 10 int.
And then we’re going to take our end value. Now, the end value isn’t necessarily always going to be present because as we make range requests, we’re actually making a request from a starting position through the rest of the video, so it might not always be there. So what I want to do is search for the end value. Did we have an end value? And if we did, I’ll go ahead and make sure that’s an integer and we can parse that, so we’ll go ahead and parse the end value as a base 10. Otherwise if we don’t have an end value, we’re going to set up our own end value and the end value is going to be the size of the video.
So we’ll take our size variable and subtract one from it. So the end value will be the end of the entire video. So now that we have start and end values, what we can do is write our response header to respond to the range request. So we’ll do res.writeHead. And when we do writeHead this time, we will do 206. And what 206 means is partial content. So we’re going to be streaming a part of the video, not a full video, so that will let the browser know that we are handling this range request.
So I’m going to add a Content-Range key and in this Content-Range, we’re going to specify what the byte range is. So we’ll say bytes and we’ll take our start value and we will subtract it from our end value and then we can go ahead and divide that by our size value. So we’re telling the browser in this header what the bytes are that we’re going to start with, what we’re going to end with, and the size, the current full size of the video. I’m also going to add an Accept-Range as header.
And we want to know that we’re working with bytes, so we’re going to accept ranges as bytes. And then we will also add our Content-Length. Now, our Content-Length isn’t just the full size of the video. What it is is actually the end value minus the start value and plus one. So now, we have our Content-Length. If there is a range request, we will actually get our Content-Length by calculating the end value minus the start value. And if the range request doesn’t have an end value, that end value will be the full size of the video.
This actually tells the browser what is the size of each chunk that we’re going to be sending in this range. So now, I’m going to go ahead and just specify our Content-Type and this is video/mp4. All right, so we’ve written the header for the range request. Now, it’s time to actually stream the video file. So I’m going to go ahead and create a ReadStream to the filename. But we’re not going to create a stream for the full video. When I create this ReadStream, I’m going to send some additional options.
So the second argument that I can add here are additional options and they include a start and end values. So I can create a ReadStream that only streams part of the file, starting at the start point and ending at the requested end point. So this will actually handle our range request. All I need to do is now pipe this to our response and we should be good to go. So I’ll go ahead and save this code here and we’ll go into the terminal window and type node dot to run it. We have a server running on localhost:3000. Let’s take a look at it.
So here’s our video. And can I skip ahead? Yes, I can skip to different points of the video. So we are handling range requests. And we can also go ahead and give this a test out on Safari. Does this work in Safari now? Going to localhost:3000. Yes, it does. So Safari requires that we handle these range requests and because we are, our video now works. So now, our video handles range requests and we also introduced some stream options that we can add to file streams, specifically setting up a smaller piece of the stream using start and end values.
So now, we’ve streamed video content to the browser. In the next two lessons, we’re gonna go ahead and stream content from the browser.