Welcome, guest | Sign In | My Account | Store | Cart
NOTE: Recipes have moved! Please visit GitHub.com/activestate/code for the current versions.

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, 32 lines
 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()

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.

3 comments

Rob Westgeest 11 years, 10 months ago  # | flag

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
Rob Westgeest 11 years, 10 months ago  # | flag

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()
ben timby 10 years, 1 month ago  # | flag

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