Setting up an HTTP server that listens over a file-socket

Question:

How can I use HTTPServer (or some other class) to set up an HTTP server that listens to a filesystem socket instead of an actual network socket? By “filesystem socket” I mean sockets of the AF_UNIX type.

Asked By: user3286380

||

Answers:

HTTPServer inherits from SocketServer.TCPServer, so I think it’s fair to say that it isn’t intended for that use-case, and even if you try to work around it, you may run into problems since you are kind of “abusing” it.

That being said, however, it would be possible per se to define a subclass of HTTPServer that creates and binds Unix sockets quite simply, as such:

class UnixHTTPServer(HTTPServer):
    address_family = socket.AF_UNIX

    def server_bind(self):
        SocketServer.TCPServer.server_bind(self)
        self.server_name = "foo"
        self.server_port = 0

Then, just pass the path you want to bind to by the server_address argument to the constructor:

server = UnixHTTPServer("/tmp/http.socket", ...)

Again, though, I can’t guarantee that it will actually work well. You may have to implement your own HTTP server instead.

Answered By: Dolda2000

I followed the example from @Dolda2000 above in Python 3.5 and ran into an issue with the HTTP handler falling over with an invalid client address. You don’t have a client address with Unix sockets in the same way that you do with TCP, so the code below fakes it.

import socketserver

...

class UnixSocketHttpServer(socketserver.UnixStreamServer):
    def get_request(self):
        request, client_address = super(UnixSocketHttpServer, self).get_request()
        return (request, ["local", 0])

...

server = UnixSocketHttpServer((sock_file), YourHttpHandler)
server.serve_forever()

With these changes, you can perform an HTTP request against the Unix socket with tools such as cURL.

curl --unix-socket /run/test.sock http:/test
Answered By: Roger Lucas

Overview

In case it help anyone else, I have created a complete example (made for Python 3.8) based on Roger Lucas’s example:

Server

import socketserver

from http.server import BaseHTTPRequestHandler

class myHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type','text/html')
        self.end_headers()
        self.wfile.write(b"Hello world!")
        return

class UnixSocketHttpServer(socketserver.UnixStreamServer):
    def get_request(self):
        request, client_address = super(UnixSocketHttpServer, self).get_request()
        return (request, ["local", 0])

server = UnixSocketHttpServer(("/tmp/http.socket"), myHandler)
server.serve_forever()

This will listen on the unix socket and respond with "Hello World!" for all GET requests.

Client Request

You can send a request with:

curl --unix-socket /tmp/http.socket http://any_path/abc/123

Troubleshooting

If you run into this error:

OSError: [Errno 98] Address already in use

Then delete the socket file:

rm /tmp/http.socket 
Answered By: sazzy4o
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.