0

How can I code an HTTP server where the responses are not sent synchronously?

I would like to enable:

  1. Request 1 arrives
  2. A synchronous service deals with request 1, but doesn't block the main thread
  3. During that, request 2 arrives, and is dealt with in its own unique way
  4. Request 1 returns to the client
  5. ... etc.

But it looks like as soon as the do_GET or similar methods reach their end, the server is sending a response to the client. It is too late for me to send my own response later.

For example

import time
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from threading import Thread


class HTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # I have some code that MUST run on the main thread during do_GET
        run_something_on_main_thread()
        # and some code that I really don't want to
        responder_thread = Thread(target=self._respond_later, args=(self.wfile,))

    def _respond_later(self, wfile):
        u""" Query some blocking service, then respond to the request """
        time.sleep(2)
        wfile.write('HTTP/1.0 204 No Content\n\n')
        wfile.close()

if __name__ == '__main__':
    server_address = (u'0.0.0.0', 8000)
    http_server = HTTPServer(server_address, HTTPRequestHandler)
    print u'Starting httpd server on {}:{}'.format(*server_address)
    http_server.serve_forever()

Querying this server always results in empty replies.

Ideally I would love a way to do this that allows me to use send_response, end_headers etc. — all the convenience methods from BaseHTTPRequestHandler. Above, I pass the wfile because I was wondering if it would be "frozen" in the new thread, I really don't expect passing self to work (because I think all its methods would be pointed at the new request, not the request relevant at the time of the creation of the thread).

So how do I delay the response to the client without blocking the main thread?

theonlygusti
  • 11,032
  • 11
  • 64
  • 119
  • Reason it's failing is because [right after calling that convenience method the wfile is flushed](https://github.com/python/cpython/blob/master/Lib/http/server.py#L415) and I assume cleaned up and stuff. I know [threaded versions exist](https://github.com/python/cpython/blob/master/Lib/socketserver.py#L682) but I don't think they are as high level as you'd like. – Tadhg McDonald-Jensen Feb 11 '20 at 01:20
  • @TadhgMcDonald-Jensen how can I stop the wfile from being flushed automatically? Adding a custom `handle_one_request` method to my own class doesn't seem to have fixed it. – theonlygusti Feb 11 '20 at 01:27
  • Note: The module you're using clearly isn't designed for non-blocking or high performance behavior, quoting its own docstring: "The classes in this module favor the server type that is simplest to write: a synchronous TCP/IP server. This is bad class design, but saves some typing." The short answer to your question is: Use a third party framework that *is* designed for this sort of thing. – ShadowRanger Feb 11 '20 at 01:32
  • as this is an uncommon pattern, I suggest it will be better off to write your own http server, juggling with all those threads. – georgexsh Feb 11 '20 at 01:45

0 Answers0