top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

HTTP over Asynchronous AF_UNIX Socket in python

+2 votes
958 views

The problem description:

There are set of processes running on my system say process_set_1. Build a process agent which runs an asynchronous socket listening to incoming requests from process_set_1. Each process sends an id. The agent stores these ids in a dictionary and sends response that ur id has been accepted. Now agent process receives some data from another process (which is some sort of sister process for the agent). This data contains id along with a command which is to be sent by the agent to the process_set_1 through HTTP client over AF_UNIX socket, since each process in process_set_1 has a HTTP listening CLI. The process agent sends an HTTP request by recognizing id stored in dictionary to the process_set_1. A service running in the process_set_1 routes this HTTP command to the respective process.

Now my problem is the HTTP request which to be sent must go through AF_UNIX socket. I got this solution.

class UnixStreamHTTPConnection(httplib.HTTPConnection):

    def __init__(self, path, host='localhost/rg_cli',port=None, strict=None,
                 timeout=None):
        httplib.HTTPConnection.__init__(self, host, port=port, strict=strict,
                                        timeout=timeout)
        self.path=path

    def connect(self):
        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.sock.connect(self.path)

But this cannot work since normal socket will not work and thus i thought of asyncore module in python. To use asyncore module again i will have to subclass asyncore.dispatcher. This class also contains connect() method.

Another problem is I don't know how asyncore module works and thus not able to find a way to mix the work of 1) listening forever, accept the connection, store id and the sock_fd.
2) accept data from the process' agent sister process, retrieve the sock_fd by matching the id in the dictionary and send it through the AF_UNIX socket.

Please help since i have already spent 2 days digging it out. Sorry if could explain my problem very well.

posted Jan 26, 2015 by Prakash

Share this question
Facebook Share Button Twitter Share Button LinkedIn Share Button

4 Answers

+2 votes

Now my problem is the HTTP request which to be sent must go through AF_UNIX socket.

The Python Twisted framework may be of use to you. It's got a very steep learning curve, but it's fairly easy to change the underlying byte transport for any protocol they have implemented. In fact in the Twisted docs, they show a web client accessing an HTTP server over a unix socket (scroll down to the bottom of the page):

http://twisted.readthedocs.org/en/latest/web/howto/client.html

They show a client connecting to a Docker server which is speaking HTTP over a unix socket.

Twisted is very powerful and flexible, and event-driven.

answer Jan 26, 2015 by anonymous
+1 vote

Asyncore was a nice idea, but you shouldn't build anything on top of it. The newest Python versions have moved to a scheme called asyncio. Its programming model is a bit unconventional and hasn't won me over yet.

Personally, I have found select.epoll(EPOLLET) plus a timer implementation enough of a framework for all of my async needs.

Then comes HTTP support. The stdlib networking facilities are great for quick scripting, but not really compatible with the asynchronous paradigm. I have coded my protocols by hand myself.

answer Jan 26, 2015 by anonymous
Nice reply. The point about HTTP support is really gud. This is what i think
is correct. I also think that i will have to write the protocol by myself.
+1 vote

The problem you're working on is one that lots of people have spent lots of time working on. Unfortunately, there isn't yet an ideal solution. As you have observed the async modules in Python's standard library are limited and not very well documented.

Probably the most complete library to help with what you're doing is Twisted:

https://twistedmatrix.com

The documentation has to be better than it was the last time I looked at it.

The best advice I can give you regarding it is to start with something very small and work up from there.

Actually, the best advice I can give you is to see if what you want to do can be done with xinetd and enough hardware.

answer Jan 27, 2015 by anonymous
+1 vote

I came up with using select and epoll modules. Here is the complete code. Updating it incase in future this might help someone.

import socket
import select
import os

EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response  = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'

class Server():
    def __init__(self,filename):
        self.filename=filename
        self.serversocket=None
    def str__(self):
        return 'unix-domain-server on %s' % self.path

    def listen(self, backlog = 5, blocking = 0):
        try:
            os.unlink(self.filename)
        except OSError:
            if os.path.exists(self.filename):
                raise
        self.serversocket=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.serversocket.bind(self.filename)
        self.serversocket.listen(1)
        self.serversocket.setblocking(blocking)
        self.epoll = select.epoll()
        self.epoll.register(self.serversocket.fileno(), select.EPOLLIN)

    def start(self):
        try:
            connections = {}; requests = {}; responses = {}
            while True:
                events = self.epoll.poll(1)
                for fileno, event in events:
                    if fileno == self.serversocket.fileno():
                        connection, address = self.serversocket.accept()
                        connection.setblocking(0)
                        self.epoll.register(connection.fileno(), select.EPOLLIN)
                        connections[connection.fileno()] = connection
                        requests[connection.fileno()] = b''
                        responses[connection.fileno()] = response
                        print str(connection) + "from server"
                    elif event & select.EPOLLIN:
                        requests[fileno] += connections[fileno].recv(1024)
                        if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
                            self.epoll.modify(fileno, select.EPOLLOUT)
                            print('-'*40 + '\n' + requests[fileno].decode()[:-2])
                    elif event & select.EPOLLOUT:
                        byteswritten = connections[fileno].send(responses[fileno])
                        responses[fileno] = responses[fileno][byteswritten:]
                        if len(responses[fileno]) == 0:
                            self.epoll.modify(fileno, 0)
                            connections[fileno].shutdown(socket.SHUT_RDWR)
                    elif event & select.EPOLLHUP:
                        self.epoll.unregister(fileno)
                        connections[fileno].close()
                        del connections[fileno]
        finally:
            self.epoll.unregister(self.serversocket.fileno())
            self.epoll.close()
            self.serversocket.close()

if __name__=="__main__":
    ser_obj=Server('./tcp_socket')
    ser_obj.listen()
    ser_obj.start()
answer Jan 27, 2015 by Prakash
Similar Questions
+1 vote

In Socket-Programming, I want to send whole ".txt" file from server to client. is it possible?

+2 votes

As per my understanding Receive system calls will be a blocking call of the code... it will not go ahead unless it receives the response from client .. my question is how would i set time in that? i want my code should wait only for few seconds then it has to come out from that

0 votes

For testing purposes I want my code to raise a socket "connection reset by peer" error, so that I can test how I handle it, but I am not sure how to raise the error.

+3 votes

I want to do getpeername() on stdin. I know I can do this by wrapping a socket object around stdin, with

s = socket.fromfd(sys.stdin.fileno(), family, type)

but that requires that I know what the family and type are. What I want to do is discover the family and type by looking at what getpeername() and/or getsockname() return. Can this be done with the standard library?

...