[Zope-dev] Zope closes connection if the client closes the write-end of connection

Izak Burger izak at upfrontsystems.co.za
Fri Oct 16 10:15:38 EDT 2009


Hi again,

Izak Burger wrote:
> I eventually distilled the check varnish uses into a small C program, 
> and an interesting problem shows up. When you call shutdown(fd, SHUT_WR) 
> on your socket connection, in effect telling zope that you're done 
> talking to it, it looks like zope responds in kind by not talking to you 
> either. I deduce this from the fact that poll() returns POLLHUP in 
> revents and read() returns zero (EOF). POLLHUP, if I understand this 
> correctly, means the other side (zope) has hung up.

It seems this might not be a bug in zope. I'd like to describe what I've 
found this morning and where this is most possibly going wrong. Please 
tell me if I'm mistaken about something.

This goes all the way to what happens when you call poll() on a file 
descriptor. If you ask to be notified for POLLIN events only, it does 
not necessarily guarantee that POLLIN (or a timeout) are the only events 
that can cause poll() to return. The man page does not properly document 
this, but the header file does:

/* Event types always implicitly polled for.  These bits need not be set
    in `events', but they will appear in `revents' to indicate the status
   of the file descriptor.  */
#define POLLERR     0x008       /* Error condition.  */
#define POLLHUP     0x010       /* Hung up.  */
#define POLLNVAL    0x020       /* Invalid polling request.  */

In other words, a POLLHUP event can cause your poll() call to return 
even though the POLLIN event you were looking for didn't occur.

If we inspect Modules/socketmodule.c in the python (2.4.4) source code 
we see that it is assumed that if poll() returns a value greater than 
zero, it means data is available for reading.

If the client calls shutdown(SHUT_WR), this will cause a POLLHUP, which 
fools python into thinking there is data to be read. When it attempts to 
read that data, it ends up with nothing, that is, recv() returns 0.

Moving on to asyncore.py, we see this:

             data = self.socket.recv(buffer_size)
             if not data:
                 # a closed connection is indicated by signaling
                 # a read condition, and having recv() return 0.
                 self.handle_close()
                 return ''

And handle_close eventually ends up telling zope to close the connection.

This has two possible fixes, but both are probably required. Firstly, it 
seems the latest python still has this problem, so I probably need to 
report it as a bug. Secondly, since zope still requires older python 
versions to run, a workaround probably needs to be found. I suppose just 
rebuilding the _socket native module will probably do it and I will test 
this as soon as I have more time.

regards,
Izak


More information about the Zope-Dev mailing list