Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Browsers close socket before the response is fully downloaded

I have a simple Python server that uses http.server. The goal is not to show the video in a html page, or to download the video file, but to display the video in the browser directly. This is what I have so far:

import http.server

class SuperHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        path = self.path
        encodedFilePath = 'file.mp4'

        with open(encodedFilePath, 'rb') as videoFile:
            self.send_response(200)
            self.send_header('Content-type', 'video/mp4')
            self.end_headers()
            self.wfile.write(videoFile.read())
            print('File sent: ' + videoFile.name)

server_address = ('', 8000)
handler_class = SuperHandler
httpd = http.server.HTTPServer(server_address, handler_class)
httpd.serve_forever()

The problem that I have is that the response doesn't contain the full video. file.mp4 is 50MB, but when I look in the network tab of either Chrome or Firefox, it says that the response is only 1MB. Is there a reason why the full file isn't transferred? Do I need to add some sort of HTTP header to make this work?

EDIT:

This is my code now:

server_address = ('', 8000)
handler_class = http.server.SimpleHTTPRequestHandler
httpd = http.server.HTTPServer(server_address, handler_class)

httpd.serve_forever()

I am now using the default SimpleHTTPRequestHandler's do_GET, but it's still not working (although the response is now 40MB/30MB instead of 1MB).

When I request file.mp4 on Chrome, the socket connexion is closed after ~7 seconds (~5 seconds on Firefox), which makes the script throw a BrokenPipeError: [Errno 32] Broken pipe, because the server is still trying to write the rest of the video file on a closed socket.

So my question is: how can I make the browser download the full response before it closes the socket?

Additional info

HTTP Response headers sent to the client:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.0
Date: Mon, 28 Dec 2015 02:36:39 GMT
Content-type: video/mp4
Content-Length: 53038876
Last-Modified: Fri, 25 Dec 2015 02:09:52 GMT
like image 718
Maxime Dupré Avatar asked Dec 25 '15 02:12

Maxime Dupré


2 Answers

It turns out that the video file I was using was corrupted (maybe it had some dropped frames or something like that). I tested with multiple other .mp4 and it worked like a charm.

All you really need to play a video file (or more accurately stream a video file as pointed by @hrunting) as far as http headers for the response are concerned is:

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.5.0
Date: Sat, 02 Jan 2016 02:45:34 GMT
Content-type: video/mp4
Content-Length: 33455269
Last-Modified: Sat, 02 Jan 2016 02:45:27 GMT

I think even the Server, Date and Last-Modified headers aren't mandatory (they are sent automatically by SimpleHTTPRequestHandler).

As pointed by @qarma and @hrunting, if you want to enable the user to jump to a specific time in video, you should support the Range header. It's simply a good idea to support the Range header as it is sent by default by Chrome.

like image 87
Maxime Dupré Avatar answered Sep 20 '22 16:09

Maxime Dupré


To stream videos you should support at least range requests and transfer-encoding: chunked

As far as I can see http.server does not support that directly. You can of course implement that on top.

Alternatively use a simple framework, e.g. bottle (one file, already supports both) or cherrypy (more solid, multi-threaded, etc)

You can also get by without any Python code, e.g. if you use nginx

like image 23
Dima Tisnek Avatar answered Sep 22 '22 16:09

Dima Tisnek