Fix issue 6706: adds new handle_accepted() method to asyncore.dispatcher
This commit is contained in:
parent
4c94c53630
commit
977c707b42
|
@ -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 |
|
| ``handle_close()`` | Implied by a read event with no data |
|
||||||
| | available |
|
| | available |
|
||||||
+----------------------+----------------------------------------+
|
+----------------------+----------------------------------------+
|
||||||
| ``handle_accept()`` | Implied by a read event on a listening |
|
| ``handle_accepted()``| Implied by a read event on a listening |
|
||||||
| | socket |
|
| | 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
|
Called on listening channels (passive openers) when a connection can be
|
||||||
established with a new remote endpoint that has issued a :meth:`connect`
|
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()
|
.. method:: readable()
|
||||||
|
|
||||||
|
@ -210,10 +222,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()
|
||||||
|
@ -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
|
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 :term:`file object` along
|
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.
|
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
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
@ -249,7 +271,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)
|
||||||
|
@ -273,6 +295,40 @@ 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_accepted(self, sock, addr):
|
||||||
|
print('Incoming connection from %s' % repr(addr))
|
||||||
|
handler = EchoHandler(sock)
|
||||||
|
|
||||||
|
server = EchoServer('localhost', 8080)
|
||||||
|
asyncore.loop()
|
||||||
|
|
||||||
|
|
|
@ -434,6 +434,14 @@ New, Improved, and Deprecated Modules
|
||||||
|
|
||||||
(Contributed by Giampaolo Rodolà; :issue:`9794`.)
|
(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
|
Multi-threading
|
||||||
===============
|
===============
|
||||||
|
|
|
@ -352,12 +352,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:
|
||||||
|
return None
|
||||||
except socket.error as why:
|
except socket.error as why:
|
||||||
if why.args[0] == EWOULDBLOCK:
|
if why.args[0] in (EWOULDBLOCK, ECONNABORTED):
|
||||||
pass
|
return None
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
else:
|
||||||
|
return conn, addr
|
||||||
|
|
||||||
def send(self, data):
|
def send(self, data):
|
||||||
try:
|
try:
|
||||||
|
@ -506,7 +509,13 @@ class dispatcher:
|
||||||
self.log_info('unhandled connect event', 'warning')
|
self.log_info('unhandled connect event', 'warning')
|
||||||
|
|
||||||
def handle_accept(self):
|
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):
|
def handle_close(self):
|
||||||
self.log_info('unhandled close event', 'warning')
|
self.log_info('unhandled close event', 'warning')
|
||||||
|
|
16
Lib/smtpd.py
16
Lib/smtpd.py
|
@ -421,21 +421,7 @@ class SMTPServer(asyncore.dispatcher):
|
||||||
self.__class__.__name__, time.ctime(time.time()),
|
self.__class__.__name__, time.ctime(time.time()),
|
||||||
localaddr, remoteaddr), file=DEBUGSTREAM)
|
localaddr, remoteaddr), file=DEBUGSTREAM)
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accepted(self, conn, addr):
|
||||||
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
|
|
||||||
print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM)
|
print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM)
|
||||||
channel = self.channel_class(self, conn, addr)
|
channel = self.channel_class(self, conn, addr)
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,6 @@ class DispatcherTests(unittest.TestCase):
|
||||||
d.handle_read()
|
d.handle_read()
|
||||||
d.handle_write()
|
d.handle_write()
|
||||||
d.handle_connect()
|
d.handle_connect()
|
||||||
d.handle_accept()
|
|
||||||
finally:
|
finally:
|
||||||
sys.stdout = stdout
|
sys.stdout = stdout
|
||||||
|
|
||||||
|
@ -304,8 +303,7 @@ class DispatcherTests(unittest.TestCase):
|
||||||
expected = ['warning: unhandled incoming priority event',
|
expected = ['warning: unhandled incoming priority event',
|
||||||
'warning: unhandled read event',
|
'warning: unhandled read event',
|
||||||
'warning: unhandled write event',
|
'warning: unhandled write event',
|
||||||
'warning: unhandled connect event',
|
'warning: unhandled connect event']
|
||||||
'warning: unhandled accept event']
|
|
||||||
self.assertEqual(lines, expected)
|
self.assertEqual(lines, expected)
|
||||||
|
|
||||||
def test_issue_8594(self):
|
def test_issue_8594(self):
|
||||||
|
@ -451,6 +449,9 @@ class BaseTestHandler(asyncore.dispatcher):
|
||||||
def handle_accept(self):
|
def handle_accept(self):
|
||||||
raise Exception("handle_accept not supposed to be called")
|
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):
|
def handle_connect(self):
|
||||||
raise Exception("handle_connect not supposed to be called")
|
raise Exception("handle_connect not supposed to be called")
|
||||||
|
|
||||||
|
@ -481,8 +482,7 @@ class TCPServer(asyncore.dispatcher):
|
||||||
def address(self):
|
def address(self):
|
||||||
return self.socket.getsockname()[:2]
|
return self.socket.getsockname()[:2]
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accepted(self, sock, addr):
|
||||||
sock, addr = self.accept()
|
|
||||||
self.handler(sock)
|
self.handler(sock)
|
||||||
|
|
||||||
def handle_error(self):
|
def handle_error(self):
|
||||||
|
@ -546,6 +546,29 @@ class BaseTestAPI(unittest.TestCase):
|
||||||
client = BaseClient(server.address)
|
client = BaseClient(server.address)
|
||||||
self.loop_waiting_for_flag(server)
|
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):
|
def test_handle_read(self):
|
||||||
# make sure handle_read is called on data received
|
# make sure handle_read is called on data received
|
||||||
|
|
||||||
|
|
|
@ -244,8 +244,7 @@ class DummyFTPServer(asyncore.dispatcher, threading.Thread):
|
||||||
self.active = False
|
self.active = False
|
||||||
self.join()
|
self.join()
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accepted(self, conn, addr):
|
||||||
conn, addr = self.accept()
|
|
||||||
self.handler_instance = self.handler(conn)
|
self.handler_instance = self.handler(conn)
|
||||||
|
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
|
|
|
@ -144,8 +144,7 @@ class DummyPOP3Server(asyncore.dispatcher, threading.Thread):
|
||||||
self.active = False
|
self.active = False
|
||||||
self.join()
|
self.join()
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accepted(self, conn, addr):
|
||||||
conn, addr = self.accept()
|
|
||||||
self.handler_instance = self.handler(conn)
|
self.handler_instance = self.handler(conn)
|
||||||
|
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
|
|
|
@ -379,8 +379,7 @@ class SimSMTPServer(smtpd.SMTPServer):
|
||||||
self._extra_features = []
|
self._extra_features = []
|
||||||
smtpd.SMTPServer.__init__(self, *args, **kw)
|
smtpd.SMTPServer.__init__(self, *args, **kw)
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accepted(self, conn, addr):
|
||||||
conn, addr = self.accept()
|
|
||||||
self._SMTPchannel = SimSMTPChannel(self._extra_features,
|
self._SMTPchannel = SimSMTPChannel(self._extra_features,
|
||||||
self, conn, addr)
|
self, conn, addr)
|
||||||
|
|
||||||
|
|
|
@ -838,8 +838,7 @@ else:
|
||||||
asyncore.dispatcher.__init__(self, sock)
|
asyncore.dispatcher.__init__(self, sock)
|
||||||
self.listen(5)
|
self.listen(5)
|
||||||
|
|
||||||
def handle_accept(self):
|
def handle_accepted(self, sock_obj, addr):
|
||||||
sock_obj, addr = self.accept()
|
|
||||||
if support.verbose:
|
if support.verbose:
|
||||||
sys.stdout.write(" server: new connection from %s:%s\n" %addr)
|
sys.stdout.write(" server: new connection from %s:%s\n" %addr)
|
||||||
self.ConnectionHandler(sock_obj, self.certfile)
|
self.ConnectionHandler(sock_obj, self.certfile)
|
||||||
|
|
|
@ -88,6 +88,12 @@ Core and Builtins
|
||||||
Library
|
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
|
- Issue #9065: tarfile no longer uses "root" as the default for the uname and
|
||||||
gname field.
|
gname field.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue