Author giampaolo.rodola
Recipients giampaolo.rodola, josiahcarlson
Date 2009-08-14.23:03:34
SpamBayes Score 5.56503e-10
Marked as misclassified No
Message-id <1250291017.2.0.719693187742.issue6706@psf.upfronthosting.co.za>
In-reply-to
Content
An old bad design choice in asyncore is how it forces the user to 
override handle_accept() and then call self.accept() to obtain a socket 
pair.

    def handle_accept(self):
        conn, addr = self.accept()

The documentation itself shows the code above as an example of how an 
asyncore-based server should handle an incoming connection.
What the doc doesn't say is that the user calling self.accept() is 
exposed to different risks:

- self.accept() can return None instead of a socket pair in which case 
TypeError is raised (see pyftpdlib bug: 
http://code.google.com/p/pyftpdlib/issues/detail?id=91)

- ECONNABORTED can be raised. This is reproducible on Linux by hammering 
the server with nmap (see pyftpdlib bug: 
http://code.google.com/p/pyftpdlib/issues/detail?id=105)

- EAGAIN can be raised too. I never experienced this error condition 
myself in pyftpdlib but by looking at twisted/internet/tcp.py it seems 
reasonable to catch EAGAIN too.

- The resulting address can be None, which means that the connection 
didn't take place.
This is reproducible by hammering the server with nmap (see pyftpdlib 
bug http://code.google.com/p/pyftpdlib/issues/detail?id=104).


The right thing to do when one of such events occur is to "return" as no 
connection has really been established between client and server.

Now, altough handling these kind of conditions is quite easy, the API 
design remains fundamentally wrong, as it forces the user to call 
accept().
As asyncore.py is structured right now the only chance the user has to 
properly accepting a connection is manually handling all these cases in 
his subclass.


My patch in attachment tries to solve this problem by defining a new 
"handle_accept_event()" method which takes care of all the error 
conditions described above resulting in handle_accept() be called *only 
when a connection really took place*.
When the connection is established handle_accept_event() stores the 
socket pair as an attribute and the next call to accept() returns that 
pair.
This preserves the current implementation without breaking any code as 
the user will have to override handle_accept() and then call accept() in 
the same manner [1], with the difference that self.accept() will always 
return a valid socket pair.

[1] 
    def handle_accept(self):
        conn, addr = self.accept()
History
Date User Action Args
2009-08-14 23:03:37giampaolo.rodolasetrecipients: + giampaolo.rodola, josiahcarlson
2009-08-14 23:03:37giampaolo.rodolasetmessageid: <1250291017.2.0.719693187742.issue6706@psf.upfronthosting.co.za>
2009-08-14 23:03:35giampaolo.rodolalinkissue6706 messages
2009-08-14 23:03:34giampaolo.rodolacreate