pythonsocketsunix-socketbasehttprequesthandler

python - using BaseHTTPRequestHandler with UnixStreamServer causes exception


I'm trying to create a basic HTTP server bound to a Unix Domain Socket, but using a BaseHTTPRequestHandler subclass with UnixStreamServer is creating an exception that suggests they cannot work together.

Toy server.py:

from SocketServer import UnixStreamServer
from BaseHTTPServer import BaseHTTPRequestHandler

class MyHandler(BaseHTTPRequestHandler):
  def do_GET(self):
    self.send_response(200)
    self.send_header("Content-Type", "text/plain")
    self.end_headers()
    self.wfile.write("Hi!")

server = UnixStreamServer('./mysocket.sock', MyHandler)
server.serve_forever()

I then send a request with curl --unix-socket ./mysocket.sock http:/hello, which results in the following exception on the server (using Python 2.7.11):

Exception happened during processing of request from 
Traceback (most recent call last):
  File "/usr/lib/python2.7/SocketServer.py", line 290, in _handle_request_noblock
    self.process_request(request, client_address)
  File "/usr/lib/python2.7/SocketServer.py", line 318, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python2.7/SocketServer.py", line 331, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.7/SocketServer.py", line 652, in __init__
    self.handle()
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 340, in handle
    self.handle_one_request()
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 328, in handle_one_request
    method()
  File "server.py", line 6, in do_GET
    self.send_response(200)
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 385, in send_response
    self.log_request(code)
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 422, in log_request
    self.requestline, str(code), str(size))
  File "/usr/lib/python2.7/BaseHTTPServer.py", line 456, in log_message
    (self.client_address[0],
IndexError: string index out of range

I did some digging and the issue is related to the fact that sock.accept() returns the empty string as the address, presumably because there's no concept of client address on UDS calls. BaseHTTPRequestHandler, however, expects one for logging (and maybe other things)

Is there a way to workaround this issue that doesn't involve monkeypatching or re-implementing BaseHTTPServer with this one small change? Is BaseHTTPRequestHandler even intended to work with UnixDataStream? Better suggestions for a lightweight UDS HTTP server in Python?

Thanks!


Solution

  • I ended up subclassing UnixStreamServer and overriding get_request (which is one of the TCPServer methods intended to be overridden):

    class UnixHTTPServer(UnixStreamServer):
      def get_request(self):
        request, client_address = self.socket.accept()
        # BaseHTTPRequestHandler expects a tuple with the client address at index
        # 0, so we fake one
        if len(client_address) == 0:
          client_address = (self.server_address,)
        return (request, client_address)
    

    (Note that self.server_address gives the path to the file descriptor being used for the UDS)

    It's not the cleanest solution, but not the hackiest either.