wolfson at gmail.com
Tue Feb 8 05:51:58 CET 2011
I've been using smtpd.py to implement a kind of cowboy SMTP server
(only overriding process_message), and inevitably after a certain time
the server stops accepting incoming connections: the socket on which
it was formerly listening gets closed.
I ran it using strace and discovered that it would get ENOTCONN on
some just-accepted socket in a call to getpeername(), and then close
the *listening* socket.
This seems to be caused by the line
channel = SMTPChannel(self, conn, addr)
in smtpd.py and handle_error() in the definition of
asyncore.dispatcher, which finishes by calling self.handle_close()
. The result of this is that when the call to getpeername() in the
constructor for SMTPChannel raises ENOTCONN (or any other exception),
it unwinds via asyncore.dispatcher.handle_read_event(), to
asyncore.read(), where the SMTPServer instance gets its handle_error
method called on it, eventually closing its listening socket. But the
error was in the socket we just accepted---not the listening socket.
 I'm actually using python2.4, since that's what's installed on the
server; there, the handle_error() method simply finishes by calling
self.close(). In fact, AFAICT, there's another problem in 3.1: neither
asyncore.dispatcher nor smtpd.SMTPServer (nor any of its subclasses in
smtpd.py) defines a method handle_close(); asyncore.dispatcher passes
attribute lookups to its socket object via __getattr__, but sockets
don't have a handle_close method either---so it seems as if, if
handle_close() is ever called, it'll just generate a new error.
More information about the Python-list