ActiveState Code

Recipe 425210: Simple stoppable server using socket timeout


The usual Python HTTP server never stops. Since there is the timout option in the socket module, there is an easy way to do a clean shutdown of a running server.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import SimpleHTTPServer, BaseHTTPServer
import socket
import thread

class StoppableHTTPServer(BaseHTTPServer.HTTPServer):

    def server_bind(self):
        BaseHTTPServer.HTTPServer.server_bind(self)
        self.socket.settimeout(1)
        self.run = True

    def get_request(self):
        while self.run:
            try:
                sock, addr = self.socket.accept()
                sock.settimeout(None)
                return (sock, addr)
            except socket.timeout:
                pass

    def stop(self):
        self.run = False

    def serve(self):
        while self.run:
            self.handle_request()

if __name__=="__main__":
    httpd = StoppableHTTPServer(("127.0.0.1",8080), SimpleHTTPServer.SimpleHTTPRequestHandler)
    thread.start_new_thread(httpd.serve, ())
    raw_input("Press <RETURN> to stop server\n")
    httpd.stop()

Discussion

We override the server_bind method to set a timeout on the main socket. Then we override the get_request and serve methods to break if the attribute run is set to False. The rest is just for convenience. In the sample script we start the server in a new thread and then wait for stop request by pressing the return key.

This method can be used for all types of servers based on the SocketServer module.

Comments

  1. 1. At 2:09 a.m. on 15 feb 2006, Rob Westgeest said:

    Exception after stopping the server. It took me a while to find this solution for a stoppable server. So I am very happy with it. However...

    The server ends with an exception because get_request returns nothing with a timeout and handle_request tries to handle that nothing.

    result:

    Exception in thread Thread-1:
    Traceback (most recent call last):
      File "C:\Python24\lib\threading.py", line 442, in __bootstrap
        self.run()
      File "C:\Python24\lib\threading.py", line 422, in run
        self.__target(*self.__args, **self.__kwargs)
      File "../../lib/src\webservice.py", line 79, in serve
        self.handle_request()
      File "C:\Python24\lib\SocketServer.py", line 217, in handle_request
        request, client_address = self.get_request()
    TypeError: unpack non-sequence
    
  2. 2. At 2:29 a.m. on 15 feb 2006, Rob Westgeest said:

    Solution. The problem above might be caused by the fact that my implementation is a bit different (it is a SimpleXMLRpcServer i.s.o. a HTTPServer).

    The/a solution to my problem is to return (None,None) in get_request and handle a None request in also override close_request and process_request to handle the special case where the request is None. Heres the result.

    Questions remain: Has anyone found a better solution for this (it seems cumbersome)? Is there something like an empty request - making it possible to remove some ifs?

    class XMLRPCWebService(SimpleXMLRPCServer.SimpleXMLRPCServer):
        allow_reuse_address = True
        def __init__( self, host, port):
            SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self,(host,port))
    
        def server_bind(self):
            SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
            self.socket.settimeout(1)
            self.stop_request = False
    
        def get_request(self):
            while not self.stop_request:
                try:
                    sock, addr = self.socket.accept()
                    sock.settimeout(None)
                    return (sock, addr)
                except socket.timeout:
                    pass
                return (None,None)
    
        def close_request(self, request):
            if (request is None): return
            SimpleXMLRPCServer.SimpleXMLRPCServer.close_request(request)
    
        def process_request(self, request, client_address):
            if (request is None): return
            SimpleXMLRPCServer.SimpleXMLRPCServer.process_request(request, client_address)
    
        def start(self):
            threading.Thread(target=self.serve).start()
    
        def stop(self):
            self.stop_request = True
    
        def serve(self):
            while not self.stop_request:
                self.handle_request()
    
  3. 3. At 11:10 a.m. on 25 oct 2007, ben timby said:

    Yes, there is a very simple solutions to kill the ifs... If you look at the implementation of SocketServer, you will see that handle_request does:

    try:
        request, client_address = self.get_request()
    except socket.error:
        return
    if self.verify_request(request, client_address):
        try:
            self.process_request(request, client_address)
        except:
            self.handle_error(request, client_address)
            self.close_request(request)
    

    So, if get_request raises a socket exception, then it exits cleanly and does not bother to call verify/process/close_request. So, just do this:

    def get_request(self):
        while self.run:
            try:
                sock, addr = self.socket.accept()
                sock.settimeout(None)
                return (sock, addr)
            except socket.timeout:
                if not self.run:
                    raise socket.error
    

Sign in to comment