4 ms·
I often have a need to serve a local directory via HTTP. In the old days the built-in Python webserver was enough, but at some point browsers became more aggres
by jmillikin 4y ago
I often have a need to serve a local directory via HTTP. In the old days the built-in Python webserver was enough, but at some point browsers became more aggressive about concurrent connections and the single-threaded `python -m SimpleHTTPServer` would just get stuck if it received two requests at once.
As a workaround, I wrote a small wrapper script that would enable multi-threading for SimpleHTTPServer.
~/bin/http-cwd , Python 2 version (original):
#!/usr/bin/python
import argparse
import BaseHTTPServer
import SimpleHTTPServer
import SocketServer
import sys
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument(
"--port", type = int, nargs = "?",
action = "store", default = 8000,
help = "Specify alternate port [default: 8000]",
)
parser.add_argument(
"--iface", type = str, nargs = "?",
action = "store", default = "127.0.0.1",
help = "Specify iface [default: 127.0.0.1]",
)
args = parser.parse_args(argv[1:])
server_address = (args.iface, args.port)
srv = ThreadedHTTPServer(server_address, SimpleHTTPServer.SimpleHTTPRequestHandler)
sa = srv.socket.getsockname()
print "Serving http://%s:%r ..." % (sa[0], sa[1])
srv.serve_forever()
if __name__ == "__main__":
sys.exit(main(sys.argv))
Python 3 version (necessary for platforms that have dropped Python 2, such as macOS):
#!/usr/bin/python3
import argparse
import http.server
import socketserver
import sys
class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
pass
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument(
"--port", type = int, nargs = "?",
action = "store", default = 8000,
help = "Specify alternate port [default: 8000]",
)
parser.add_argument(
"--iface", type = str, nargs = "?",
action = "store", default = "127.0.0.1",
help = "Specify iface [default: 127.0.0.1]",
)
args = parser.parse_args(argv[1:])
server_address = (args.iface, args.port)
srv = ThreadedHTTPServer(server_address, http.server.SimpleHTTPRequestHandler)
sa = srv.socket.getsockname()
print("Serving http://%s:%r ..." % (sa[0], sa[1]))
srv.serve_forever()
if __name__ == "__main__":
sys.exit(main(sys.argv))
- benji-york 4y agoHow does this differ from the stdlib approach? python -m http.server 8080 --bind 127.0.0.1 --directory your_directory
- jmillikin 4y agoMay I direct your attention to the second sentence of my post? It fixes an issue in the Python built-in HTTP server that causes it to hang under concurrent connections. First, run the built-in Python web server: [term1]$ python -m SimpleHTTPServer 8080 Then connect to it with a client that doesn't immediately send a request, such as `netcat`. This simulates the behavior of modern browsers, which seem to set up a pool of pre-established connections. [term2]$ nc localhost 8080 Now try to get a page from the server via Curl (or wget, etc). It will hang after sending the request, because the server's single thread is trying to serve the idle connection. [term3]$ curl -v http://127.0.0.1:8080 * Trying 127.0.0.1:8080... * TCP_NODELAY set * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > GET / HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.68.0 > Accept: */* > In real life, the behavior I saw was that I'd try to connect to the server with Chrome and it would hang after the pages had partially loaded.
- benji-york 4y agoI totally missed that! Thanks for the additional detail. This issue appears to have been fixed: https://github.com/python/cpython/issues/75820 https://github.com/python/cpython/issues/75820
- Beltalowda 4y agoI think this is a problem that's long been fixed; I tested your command and it seems to work as expected for me in Python 3.10 anyway. And I've been using the "python -mhttp.server" frequently for years, and never experienced any of these problems.