Commit 19e9fefc authored by Giampaolo Rodolà's avatar Giampaolo Rodolà

Fix Issue 6706: return None on connect() in case of EWOULDBLOCK/ECONNABORTED error.

parent abacccc5
...@@ -211,10 +211,13 @@ any that have been added to the map during asynchronous service) is closed. ...@@ -211,10 +211,13 @@ any that have been added to the map during asynchronous service) is closed.
.. method:: accept() .. method:: accept()
Accept a connection. The socket must be bound to an address and listening Accept a connection. The socket must be bound to an address and listening
for connections. The return value is a pair ``(conn, address)`` where for connections. The return value can be either ``None`` or a pair
*conn* is a *new* socket object usable to send and receive data on the ``(conn, address)`` where *conn* is a *new* socket object usable to send
connection, and *address* is the address bound to the socket on the other and receive data on the connection, and *address* is the address bound to
end of the connection. the socket on the other end of the connection.
When ``None`` is returned it means the connection didn't take place, in
which case the server should just ignore this event and keep listening
for further incoming connections.
.. method:: close() .. method:: close()
...@@ -224,6 +227,12 @@ any that have been added to the map during asynchronous service) is closed. ...@@ -224,6 +227,12 @@ any that have been added to the map during asynchronous service) is closed.
flushed). Sockets are automatically closed when they are flushed). Sockets are automatically closed when they are
garbage-collected. garbage-collected.
.. class:: dispatcher_with_send()
A :class:`dispatcher` subclass which adds simple buffered output capability,
useful for simple clients. For more sophisticated usage use
:class:`asynchat.async_chat`.
.. class:: file_dispatcher() .. class:: file_dispatcher()
A file_dispatcher takes a file descriptor or file object along with an A file_dispatcher takes a file descriptor or file object along with an
...@@ -240,7 +249,7 @@ any that have been added to the map during asynchronous service) is closed. ...@@ -240,7 +249,7 @@ any that have been added to the map during asynchronous service) is closed.
socket for use by the :class:`file_dispatcher` class. Availability: UNIX. socket for use by the :class:`file_dispatcher` class. Availability: UNIX.
.. _asyncore-example: .. _asyncore-example-1:
asyncore Example basic HTTP client asyncore Example basic HTTP client
---------------------------------- ----------------------------------
...@@ -250,7 +259,7 @@ implement its socket handling:: ...@@ -250,7 +259,7 @@ implement its socket handling::
import asyncore, socket import asyncore, socket
class http_client(asyncore.dispatcher): class HTTPClient(asyncore.dispatcher):
def __init__(self, host, path): def __init__(self, host, path):
asyncore.dispatcher.__init__(self) asyncore.dispatcher.__init__(self)
...@@ -274,6 +283,45 @@ implement its socket handling:: ...@@ -274,6 +283,45 @@ implement its socket handling::
sent = self.send(self.buffer) sent = self.send(self.buffer)
self.buffer = self.buffer[sent:] self.buffer = self.buffer[sent:]
c = http_client('www.python.org', '/')
client = HTTPClient('www.python.org', '/')
asyncore.loop() asyncore.loop()
.. _asyncore-example-2:
asyncore Example basic echo server
----------------------------------
Here is abasic echo server that uses the :class:`dispatcher` class to accept
connections and dispatches the incoming connections to a handler::
import asyncore
import socket
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
data = self.recv(8192)
self.send(data)
class EchoServer(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind((host, port))
self.listen(5)
def handle_accept(self):
pair = self.accept()
if pair is None:
pass
else:
sock, addr = pair
print 'Incoming connection from %s' % repr(addr)
handler = EchoHandler(sock)
server = EchoServer('localhost', 8080)
asyncore.loop()
...@@ -350,12 +350,15 @@ class dispatcher: ...@@ -350,12 +350,15 @@ class dispatcher:
# XXX can return either an address pair or None # XXX can return either an address pair or None
try: try:
conn, addr = self.socket.accept() conn, addr = self.socket.accept()
return conn, addr except TypeError:
except socket.error, why: return None
if why.args[0] == EWOULDBLOCK: except socket.error as why:
pass if why.args[0] in (EWOULDBLOCK, ECONNABORTED):
return None
else: else:
raise raise
else:
return conn, addr
def send(self, data): def send(self, data):
try: try:
......
...@@ -35,7 +35,6 @@ given then 8025 is used. If remotehost is not given then `localhost' is used, ...@@ -35,7 +35,6 @@ given then 8025 is used. If remotehost is not given then `localhost' is used,
and if remoteport is not given, then 25 is used. and if remoteport is not given, then 25 is used.
""" """
# Overview: # Overview:
# #
# This file implements the minimal SMTP protocol as defined in RFC 821. It # This file implements the minimal SMTP protocol as defined in RFC 821. It
...@@ -96,7 +95,6 @@ EMPTYSTRING = '' ...@@ -96,7 +95,6 @@ EMPTYSTRING = ''
COMMASPACE = ', ' COMMASPACE = ', '
def usage(code, msg=''): def usage(code, msg=''):
print >> sys.stderr, __doc__ % globals() print >> sys.stderr, __doc__ % globals()
if msg: if msg:
...@@ -104,7 +102,6 @@ def usage(code, msg=''): ...@@ -104,7 +102,6 @@ def usage(code, msg=''):
sys.exit(code) sys.exit(code)
class SMTPChannel(asynchat.async_chat): class SMTPChannel(asynchat.async_chat):
COMMAND = 0 COMMAND = 0
DATA = 1 DATA = 1
...@@ -276,7 +273,6 @@ class SMTPChannel(asynchat.async_chat): ...@@ -276,7 +273,6 @@ class SMTPChannel(asynchat.async_chat):
self.push('354 End data with <CR><LF>.<CR><LF>') self.push('354 End data with <CR><LF>.<CR><LF>')
class SMTPServer(asyncore.dispatcher): class SMTPServer(asyncore.dispatcher):
def __init__(self, localaddr, remoteaddr): def __init__(self, localaddr, remoteaddr):
self._localaddr = localaddr self._localaddr = localaddr
...@@ -299,22 +295,11 @@ class SMTPServer(asyncore.dispatcher): ...@@ -299,22 +295,11 @@ class SMTPServer(asyncore.dispatcher):
localaddr, remoteaddr) localaddr, remoteaddr)
def handle_accept(self): def handle_accept(self):
try: pair = self.accept()
conn, addr = self.accept() if pair is not None:
except TypeError: conn, addr = pair
# sometimes accept() might return None print >> DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
return channel = SMTPChannel(self, conn, addr)
except socket.error, err:
# ECONNABORTED might be thrown
if err[0] != errno.ECONNABORTED:
raise
return
else:
# sometimes addr == None instead of (ip, port)
if addr == None:
return
print >> DEBUGSTREAM, 'Incoming connection from %s' % repr(addr)
channel = SMTPChannel(self, conn, addr)
# API for "doing something useful with the message" # API for "doing something useful with the message"
def process_message(self, peer, mailfrom, rcpttos, data): def process_message(self, peer, mailfrom, rcpttos, data):
...@@ -342,7 +327,6 @@ class SMTPServer(asyncore.dispatcher): ...@@ -342,7 +327,6 @@ class SMTPServer(asyncore.dispatcher):
raise NotImplementedError raise NotImplementedError
class DebuggingServer(SMTPServer): class DebuggingServer(SMTPServer):
# Do something with the gathered message # Do something with the gathered message
def process_message(self, peer, mailfrom, rcpttos, data): def process_message(self, peer, mailfrom, rcpttos, data):
...@@ -358,7 +342,6 @@ class DebuggingServer(SMTPServer): ...@@ -358,7 +342,6 @@ class DebuggingServer(SMTPServer):
print '------------ END MESSAGE ------------' print '------------ END MESSAGE ------------'
class PureProxy(SMTPServer): class PureProxy(SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data): def process_message(self, peer, mailfrom, rcpttos, data):
lines = data.split('\n') lines = data.split('\n')
...@@ -399,7 +382,6 @@ class PureProxy(SMTPServer): ...@@ -399,7 +382,6 @@ class PureProxy(SMTPServer):
return refused return refused
class MailmanProxy(PureProxy): class MailmanProxy(PureProxy):
def process_message(self, peer, mailfrom, rcpttos, data): def process_message(self, peer, mailfrom, rcpttos, data):
from cStringIO import StringIO from cStringIO import StringIO
...@@ -478,13 +460,11 @@ class MailmanProxy(PureProxy): ...@@ -478,13 +460,11 @@ class MailmanProxy(PureProxy):
msg.Enqueue(mlist, torequest=1) msg.Enqueue(mlist, torequest=1)
class Options: class Options:
setuid = 1 setuid = 1
classname = 'PureProxy' classname = 'PureProxy'
def parseargs(): def parseargs():
global DEBUGSTREAM global DEBUGSTREAM
try: try:
...@@ -541,7 +521,6 @@ def parseargs(): ...@@ -541,7 +521,6 @@ def parseargs():
return options return options
if __name__ == '__main__': if __name__ == '__main__':
options = parseargs() options = parseargs()
# Become nobody # Become nobody
......
...@@ -66,6 +66,9 @@ Core and Builtins ...@@ -66,6 +66,9 @@ Core and Builtins
Library Library
------- -------
- Issue 6706: asyncore accept() method no longer raises EWOULDBLOCK/ECONNABORTED
on incomplete connection attempt but returns None instead.
- Issue #10266: uu.decode didn't close in_file explicitly when it was given - Issue #10266: uu.decode didn't close in_file explicitly when it was given
as a filename. Patch by Brian Brazil. as a filename. Patch by Brian Brazil.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment