I'm trying to build a truly non-blocking HTTPS server in Python. The following minimal code works just fine if everyone is playing nice:
import BaseHTTPServer
import SimpleHTTPServer
import SocketServer
import ssl
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
pass
httpd = ThreadedHTTPServer(('localhost', 4443), SimpleHTTPServer.SimpleHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(httpd.socket, keyfile="localhost.key", certfile="localhost.pem", server_side=True)
httpd.serve_forever()
However, the problem is that this server blocks at least during the TLS handshake.
Test with:
$ nc localhost 4443 # leave this open
And then (in another terminal):
$ wget --no-check-certificate https://localhost:4443/
--2014-10-23 16:55:54-- https://localhost:4443/
Resolving localhost (localhost)... 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:4443... connected.
The wget process blocks, indicating that something is blocked in the server. Once I close the nc process, wget continues. This is obviously not practical at all.
How do I get a truly non-blocking HTTPS server in Python, preferably without additional third-party software?
I should mention that the very same code works as expected without TLS (i.e., without the wrap_socket line).
Steffen Ullrich pointed out how to do it: pass do_handshake_on_connect=False
to wrap_socket
, then do the handshake yourself. In this case, subclass BaseHTTPServer.HTTPServer
, override handle
, and then do the handshake as shown in the Python docs (the socket is called self.request
) followed by calling the super method.
These are pure-python functions which perform non-blocking I/O in python. nonblock_read provides the ability to read anything available on a buffer, like a file or a pipe or a socket, in a non-blocking fashion. Methods like readline will block until a newline is printed, etc.
In Python, you use socket. setblocking(False) to make it non-blocking.
A Non-Blocking server means that it is able to have multiple requests in progress at the same time by the same process or thread because it uses Non-Blocking I/O. In the Non-Blocking approach – one thread can handle multiple queries at a time.
You have to do a non-blocking SSL accept by calling ssl.wrap_socket
with do_handshake_on_connect=False
and later calling do_handshake
yourself until it succeeds. See https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets.
You might also simply use Tornado which is a web server written in python and which also does fully non-blocking SSL handling. Even if you don't want to use it yourself you might have a look at the source code to see how this is done (search for do_handshake
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With