summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiampaolo Rodolà <g.rodola@gmail.com>2010-10-04 21:08:36 (GMT)
committerGiampaolo Rodolà <g.rodola@gmail.com>2010-10-04 21:08:36 (GMT)
commit977c707b425ee753d54f3e9010f07ec77ef61274 (patch)
tree580e12fd41103a684b4c24b53d43569eb69bc6a8
parent4c94c5363091350ed56bfbdbc6cc00e1048b456c (diff)
downloadcpython-977c707b425ee753d54f3e9010f07ec77ef61274.zip
cpython-977c707b425ee753d54f3e9010f07ec77ef61274.tar.gz
cpython-977c707b425ee753d54f3e9010f07ec77ef61274.tar.bz2
Fix issue 6706: adds new handle_accepted() method to asyncore.dispatcher
-rw-r--r--Doc/library/asyncore.rst76
-rw-r--r--Doc/whatsnew/3.2.rst8
-rw-r--r--Lib/asyncore.py17
-rwxr-xr-xLib/smtpd.py16
-rw-r--r--Lib/test/test_asyncore.py33
-rw-r--r--Lib/test/test_ftplib.py3
-rw-r--r--Lib/test/test_poplib.py3
-rw-r--r--Lib/test/test_smtplib.py3
-rw-r--r--Lib/test/test_ssl.py3
-rw-r--r--Misc/NEWS6
10 files changed, 126 insertions, 42 deletions
diff --git a/Doc/library/asyncore.rst b/Doc/library/asyncore.rst
index 1ba8e95..1ffa132 100644
--- a/Doc/library/asyncore.rst
+++ b/Doc/library/asyncore.rst
@@ -86,7 +86,7 @@ any that have been added to the map during asynchronous service) is closed.
| ``handle_close()`` | Implied by a read event with no data |
| | available |
+----------------------+----------------------------------------+
- | ``handle_accept()`` | Implied by a read event on a listening |
+ | ``handle_accepted()``| Implied by a read event on a listening |
| | socket |
+----------------------+----------------------------------------+
@@ -144,8 +144,20 @@ any that have been added to the map during asynchronous service) is closed.
Called on listening channels (passive openers) when a connection can be
established with a new remote endpoint that has issued a :meth:`connect`
- call for the local endpoint.
+ call for the local endpoint. Deprecated in version 3.2; use
+ :meth:`handle_accepted` instead.
+ .. deprecated:: 3.2
+
+ .. method:: handle_accepted(sock, addr)
+
+ Called on listening channels (passive openers) when a connection has been
+ established with a new remote endpoint that has issued a :meth:`connect`
+ call for the local endpoint. *conn* is a *new* socket object usable to
+ send and receive data on the connection, and *address* is the address
+ bound to the socket on the other end of the connection.
+
+ .. versionadded:: 3.2
.. method:: readable()
@@ -210,10 +222,13 @@ any that have been added to the map during asynchronous service) is closed.
.. method:: accept()
Accept a connection. The socket must be bound to an address and listening
- for connections. The return value is a pair ``(conn, address)`` where
- *conn* is a *new* socket object usable to send and receive data on the
- connection, and *address* is the address bound to the socket on the other
- end of the connection.
+ for connections. The return value can be either ``None`` or a pair
+ ``(conn, address)`` where *conn* is a *new* socket object usable to send
+ and receive data on the connection, and *address* is the address bound to
+ 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()
@@ -223,6 +238,13 @@ any that have been added to the map during asynchronous service) is closed.
flushed). Sockets are automatically closed when they are
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()
A file_dispatcher takes a file descriptor or :term:`file object` along
@@ -239,7 +261,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.
-.. _asyncore-example:
+.. _asyncore-example-1:
asyncore Example basic HTTP client
----------------------------------
@@ -249,7 +271,7 @@ implement its socket handling::
import asyncore, socket
- class http_client(asyncore.dispatcher):
+ class HTTPClient(asyncore.dispatcher):
def __init__(self, host, path):
asyncore.dispatcher.__init__(self)
@@ -273,6 +295,40 @@ implement its socket handling::
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
- c = http_client('www.python.org', '/')
- asyncore.loop()
+ client = HTTPClient('www.python.org', '/')
+ 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_accepted(self, sock, addr):
+ print('Incoming connection from %s' % repr(addr))
+ handler = EchoHandler(sock)
+
+ server = EchoServer('localhost', 8080)
+ asyncore.loop()
+
diff --git a/Doc/whatsnew/3.2.rst b/Doc/whatsnew/3.2.rst
index 609511b..533d038 100644
--- a/Doc/whatsnew/3.2.rst
+++ b/Doc/whatsnew/3.2.rst
@@ -434,6 +434,14 @@ New, Improved, and Deprecated Modules
(Contributed by Giampaolo Rodolà; :issue:`9794`.)
+* :class:`asyncore.dispatcher` now provides a
+ :meth:`~asyncore.dispatcher.handle_accepted()` method
+ returning a `(sock, addr)` pair which is called when a connection has actually
+ been established with a new remote endpoint. This is supposed to be used as a
+ replacement for old :meth:`~asyncore.dispatcher.handle_accept()` and avoids
+ the user to call :meth:`~asyncore.dispatcher.accept()` directly.
+
+ (Contributed by Giampaolo Rodolà; :issue:`6706`.)
Multi-threading
===============
diff --git a/Lib/asyncore.py b/Lib/asyncore.py
index 8745276..a277bdd 100644
--- a/Lib/asyncore.py
+++ b/Lib/asyncore.py
@@ -352,12 +352,15 @@ class dispatcher:
# XXX can return either an address pair or None
try:
conn, addr = self.socket.accept()
- return conn, addr
+ except TypeError:
+ return None
except socket.error as why:
- if why.args[0] == EWOULDBLOCK:
- pass
+ if why.args[0] in (EWOULDBLOCK, ECONNABORTED):
+ return None
else:
raise
+ else:
+ return conn, addr
def send(self, data):
try:
@@ -506,7 +509,13 @@ class dispatcher:
self.log_info('unhandled connect event', 'warning')
def handle_accept(self):
- self.log_info('unhandled accept event', 'warning')
+ pair = self.accept()
+ if pair is not None:
+ self.handle_accepted(*pair)
+
+ def handle_accepted(self, sock, addr):
+ sock.close()
+ self.log_info('unhandled accepted event', 'warning')
def handle_close(self):
self.log_info('unhandled close event', 'warning')
diff --git a/Lib/smtpd.py b/Lib/smtpd.py
index 179a1b9..23787fd 100755
--- a/Lib/smtpd.py
+++ b/Lib/smtpd.py
@@ -421,21 +421,7 @@ class SMTPServer(asyncore.dispatcher):
self.__class__.__name__, time.ctime(time.time()),
localaddr, remoteaddr), file=DEBUGSTREAM)
- def handle_accept(self):
- try:
- conn, addr = self.accept()
- except TypeError:
- # sometimes accept() might return None
- return
- except socket.error as err:
- # ECONNABORTED might be thrown
- if err.args[0] != errno.ECONNABORTED:
- raise
- return
- else:
- # sometimes addr == None instead of (ip, port)
- if addr == None:
- return
+ def handle_accepted(self, conn, addr):
print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM)
channel = self.channel_class(self, conn, addr)
diff --git a/Lib/test/test_asyncore.py b/Lib/test/test_asyncore.py
index ce0a84f..205efb9 100644
--- a/Lib/test/test_asyncore.py
+++ b/Lib/test/test_asyncore.py
@@ -296,7 +296,6 @@ class DispatcherTests(unittest.TestCase):
d.handle_read()
d.handle_write()
d.handle_connect()
- d.handle_accept()
finally:
sys.stdout = stdout
@@ -304,8 +303,7 @@ class DispatcherTests(unittest.TestCase):
expected = ['warning: unhandled incoming priority event',
'warning: unhandled read event',
'warning: unhandled write event',
- 'warning: unhandled connect event',
- 'warning: unhandled accept event']
+ 'warning: unhandled connect event']
self.assertEqual(lines, expected)
def test_issue_8594(self):
@@ -451,6 +449,9 @@ class BaseTestHandler(asyncore.dispatcher):
def handle_accept(self):
raise Exception("handle_accept not supposed to be called")
+ def handle_accepted(self):
+ raise Exception("handle_accepted not supposed to be called")
+
def handle_connect(self):
raise Exception("handle_connect not supposed to be called")
@@ -481,8 +482,7 @@ class TCPServer(asyncore.dispatcher):
def address(self):
return self.socket.getsockname()[:2]
- def handle_accept(self):
- sock, addr = self.accept()
+ def handle_accepted(self, sock, addr):
self.handler(sock)
def handle_error(self):
@@ -546,6 +546,29 @@ class BaseTestAPI(unittest.TestCase):
client = BaseClient(server.address)
self.loop_waiting_for_flag(server)
+ def test_handle_accepted(self):
+ # make sure handle_accepted() is called when a client connects
+
+ class TestListener(BaseTestHandler):
+
+ def __init__(self):
+ BaseTestHandler.__init__(self)
+ self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.bind((HOST, 0))
+ self.listen(5)
+ self.address = self.socket.getsockname()[:2]
+
+ def handle_accept(self):
+ asyncore.dispatcher.handle_accept(self)
+
+ def handle_accepted(self, sock, addr):
+ self.flag = True
+
+ server = TestListener()
+ client = BaseClient(server.address)
+ self.loop_waiting_for_flag(server)
+
+
def test_handle_read(self):
# make sure handle_read is called on data received
diff --git a/Lib/test/test_ftplib.py b/Lib/test/test_ftplib.py
index 8164ede..c9bb06e 100644
--- a/Lib/test/test_ftplib.py
+++ b/Lib/test/test_ftplib.py
@@ -244,8 +244,7 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
self.active = False
self.join()
- def handle_accept(self):
- conn, addr = self.accept()
+ def handle_accepted(self, conn, addr):
self.handler_instance = self.handler(conn)
def handle_connect(self):
diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py
index 2adc849..81af569 100644
--- a/Lib/test/test_poplib.py
+++ b/Lib/test/test_poplib.py
@@ -144,8 +144,7 @@ class DummyPOP3Server(asyncore.dispatcher, threading.Thread):
self.active = False
self.join()
- def handle_accept(self):
- conn, addr = self.accept()
+ def handle_accepted(self, conn, addr):
self.handler_instance = self.handler(conn)
def handle_connect(self):
diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py
index 831f2d7..230899b 100644
--- a/Lib/test/test_smtplib.py
+++ b/Lib/test/test_smtplib.py
@@ -379,8 +379,7 @@ class SimSMTPServer(smtpd.SMTPServer):
self._extra_features = []
smtpd.SMTPServer.__init__(self, *args, **kw)
- def handle_accept(self):
- conn, addr = self.accept()
+ def handle_accepted(self, conn, addr):
self._SMTPchannel = SimSMTPChannel(self._extra_features,
self, conn, addr)
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 6b79615..4f29a64 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -838,8 +838,7 @@ else:
asyncore.dispatcher.__init__(self, sock)
self.listen(5)
- def handle_accept(self):
- sock_obj, addr = self.accept()
+ def handle_accepted(self, sock_obj, addr):
if support.verbose:
sys.stdout.write(" server: new connection from %s:%s\n" %addr)
self.ConnectionHandler(sock_obj, self.certfile)
diff --git a/Misc/NEWS b/Misc/NEWS
index 1bf3acd..3dfff9b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -88,6 +88,12 @@ Core and Builtins
Library
-------
+- Issue 6706: asyncore.dispatcher now provides a handle_accepted() method
+ returning a (sock, addr) pair which is called when a connection has been
+ established with a new remote endpoint. This is supposed to be used as a
+ replacement for old handle_accept() and avoids the user to call accept()
+ directly.
+
- Issue #9065: tarfile no longer uses "root" as the default for the uname and
gname field.