Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Threaded BaseHTTPServer, one thread per request

I'm trying to create an multi-threaded web server using BaseHttpServer and ThreadingMixIn (as seen on various examples). Pseudo code would be something like:

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
             pass
    def do_POST(self):
             pass

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""

if __name__ == '__main__':
    server = ThreadedHTTPServer(('localhost', 9999), Handler)
    print 'Starting server, use <Ctrl-C> to stop'
    server.serve_forever()

This works as expected, but my problem is that not every request gets a thread, but threading is done per URL. I've tested it like this: I have an URL bound to execute the following method:

import time
import datetime

def request_with_pause(self):
    print datetime.datetime.now().strftime("%H:%M:%S.%f"), 'REQUEST RECEIVED'
    time.sleep(10)
    print datetime.datetime.now().strftime("%H:%M:%S.%f"), 'SENT RESPONSE'

It works fine, except when I call the url twice with a 5 second pause (click the URL, wait 5 seconds and click it another time) - both "responses" arrive after 10 seconds (response of first click).

like image 913
ivica Avatar asked Sep 02 '26 14:09

ivica


1 Answers

In Python 2.7:

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from threading import Thread

class ThreadedHTTPServer(HTTPServer):
    def process_request(self, request, client_address):
        thread = Thread(target=self.__new_request, args=(self.RequestHandlerClass, request, client_address, self))
        thread.start()
    def __new_request(self, handlerClass, request, address, server):
        handlerClass(request, address, server)
        self.shutdown_request(request)

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()
        self.wfile.write("hello world")

server = ThreadedHTTPServer(('', 80), Handler)
#server.serve_forever()

You can find the main source code of the HTTPServer class in SocketServer.py which you can find in the Lib folder in the Python directory. (HTTPServer is inherited from TCPServer, TCPServer is inherited from BaseServer.)

The important line is 315:

def process_request(self, request, client_address):
    self.finish_request(request, client_address)
    self.shutdown_request(request)

def finish_request(self, request, client_address):
    self.RequestHandlerClass(request, client_address, self)

In this point the server create new request object with your Handler class. The BaseRequestHandler constructor automatically call the self.setup(), self.handle() and the self.finish() method.

So what I did was to override the process_request method to move this stuff in a new thread.

like image 92
Martin Wantke Avatar answered Sep 05 '26 04:09

Martin Wantke