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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With