Search code examples
pythonhttpbasehttpserver

How to stop BaseHTTPServer.serve_forever() in a BaseHTTPRequestHandler subclass?


I am running my HTTPServer in a separate thread (using the threading module which has no way to stop threads...) and want to stop serving requests when the main thread also shuts down.

The Python documentation states that BaseHTTPServer.HTTPServer is a subclass of SocketServer.TCPServer, which supports a shutdown method, but it is missing in HTTPServer.

The whole BaseHTTPServer module has very little documentation :(


Solution

  • I should start by saying that "I probably wouldn't do this myself, but I have in the past". The serve_forever (from SocketServer.py) method looks like this:

    def serve_forever(self):
        """Handle one request at a time until doomsday."""
        while 1:
            self.handle_request()
    

    You could replace (in subclass) while 1 with while self.should_be_running, and modify that value from a different thread. Something like:

    def stop_serving_forever(self):
        """Stop handling requests"""
        self.should_be_running = 0
        # Make a fake request to the server, to really force it to stop.
        # Otherwise it will just stop on the next request.
        # (Exercise for the reader.)
        self.make_a_fake_request_to_myself()
    

    Edit: I dug up the actual code I used at the time:

    class StoppableRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
    
        stopped = False
        allow_reuse_address = True
    
        def __init__(self, *args, **kw):
            SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, *args, **kw)
            self.register_function(lambda: 'OK', 'ping')
    
        def serve_forever(self):
            while not self.stopped:
                self.handle_request()
    
        def force_stop(self):
            self.server_close()
            self.stopped = True
            self.create_dummy_request()
    
        def create_dummy_request(self):
            server = xmlrpclib.Server('http://%s:%s' % self.server_address)
            server.ping()