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.
解决方案
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).