How to write a non blocking SimpleHTTPRequestHandler ?

Yassine Chaouche yacinechaouche at yahoo.com
Sun Feb 8 09:13:31 EST 2015


On Tuesday, February 3, 2015 at 3:17:37 PM UTC+1, Amirouche Boubekki wrote:
> What you want is to prevent the socket to wait indefinetly for data (based on strace output) which is done with socket.setblocking/settimeout [1]. asynchronous (asyncio) is something else, and you would still need to handle blocking I think.

I have installed Faulthandler, a beautiful tool written by Victor "Haypo" Stinner, and thanks to it I could determine precisely where the program hangs. It is in ssl.py::SSLSocket::read


    def read(self, len=1024):

        """Read up to LEN bytes and return them.                                                                                                                                                                                 
        Return zero-length string on EOF."""

        try:
>            return self._sslobj.read(len) >*< python hangs on this line 
        except SSLError, x:
            if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
                return ''
            else:
                raise

>From the traceback given by faulthandler it seems to me that the problem isn't from my webserver trying to receive connections from clients, but from my server (acting as a client) trying a request on a distant server (https URL -> use of ssl.py). Here's the traceback : 

     1	Current thread 0x00007fb9cb41f700 (most recent call first):
     2	File "/usr/lib/python2.7/ssl.py", line 160 in read
     3	File "/usr/lib/python2.7/ssl.py", line 241 in recv
     4	File "/usr/lib/python2.7/socket.py", line 447 in readline
     5	File "/usr/lib/python2.7/httplib.py", line 365 in _read_status
     6	File "/usr/lib/python2.7/httplib.py", line 407 in begin
     7	File "/usr/lib/python2.7/httplib.py", line 1034 in getresponse
     8	File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py", line 353 in _make_request
     9	File "/usr/local/lib/python2.7/dist-packages/requests/packages/urllib3/connectionpool.py", line 518 in urlopen
    10	File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 370 in send
    11	File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 573 in send
    12	File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 461 in request
    13	File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 473 in get
    14	File "/usr/local/lib/python2.7/dist-packages/infomaniak/infomaniak.py", line 29 in getFlux
    15	File "/usr/local/lib/python2.7/dist-packages/infomaniak/server.py", line 52 in do_GET
    16	File "/usr/lib/python2.7/BaseHTTPServer.py", line 328 in handle_one_request
    17	File "/usr/lib/python2.7/BaseHTTPServer.py", line 340 in handle
    18	File "/usr/lib/python2.7/SocketServer.py", line 649 in __init__
    19	File "/usr/lib/python2.7/SocketServer.py", line 334 in finish_request
    20	File "/usr/lib/python2.7/SocketServer.py", line 321 in process_request
    21	File "/usr/lib/python2.7/SocketServer.py", line 295 in _handle_request_noblock
    22	File "/usr/lib/python2.7/SocketServer.py", line 238 in serve_forever
    23	File "/usr/local/lib/python2.7/dist-packages/infomaniak/server.py", line 71 in <module>
    24	File "/usr/bin/infomaniak", line 2 in <module>
    25	 

If you look at line 13, it shows that the server is actually doing a get to an external URL via the requests library, which is itself relying on urllib3, which in turn is using httplib.py

Below is the code of the last three functions to have been called, in chronoligical order : 

In socket.py::_fileobject::readline
           [...]
            while True:
                try:
                    data = self._sock.recv(self._rbufsize) #<------------
                except error, e:
                    if e.args[0] == EINTR:
                        continue
                    raise
                if not data:
                    break
                nl = data.find('\n')
                if nl >= 0:
                    nl += 1
                    buf.write(data[:nl])
                    self._rbuf.write(data[nl:])
                    del data
                    break
                buf.write(data)
            return buf.getvalue()
           [...]

In ssl.py::SSLSocket::recv 

    def recv(self, buflen=1024, flags=0):
        if self._sslobj:
            if flags != 0:
                raise ValueError(
                    "non-zero flags not allowed in calls to recv() on %s" %
                    self.__class__)
            return self.read(buflen)                                  #<-------
        else:
            return self._sock.recv(buflen, flags)


In ssl.py::SSLSocket::read

    def read(self, len=1024):

        """Read up to LEN bytes and return them.                                                                                                                                                                                 
        Return zero-length string on EOF."""

        try:
            return self._sslobj.read(len) # >*< python hangs on this line 
        except SSLError, x:
            if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs:
                return ''
            else:
                raise

I can't go any further because the _sslobj is create via the _ssl.so library, it is very likely C code.

Do you have any idea about how I can investigate this any further ?



More information about the Python-list mailing list