diff options
author | Vinay Sajip <vinay_sajip@yahoo.co.uk> | 2013-06-07 14:21:41 (GMT) |
---|---|---|
committer | Vinay Sajip <vinay_sajip@yahoo.co.uk> | 2013-06-07 14:21:41 (GMT) |
commit | 30298b468b1c702bf3082b57fea13247e53e2ae2 (patch) | |
tree | 4ea40ceefc58a21b612ee0da3de1a32663f28ca1 | |
parent | 4e694d6fa9f1fbdf1de137214deceac4cd87e091 (diff) | |
download | cpython-30298b468b1c702bf3082b57fea13247e53e2ae2.zip cpython-30298b468b1c702bf3082b57fea13247e53e2ae2.tar.gz cpython-30298b468b1c702bf3082b57fea13247e53e2ae2.tar.bz2 |
Closes #11959: SMTPServer and SMTPChannel now take an optional map, use of which avoids affecting global state.
-rw-r--r-- | Doc/library/smtpd.rst | 19 | ||||
-rwxr-xr-x | Lib/smtpd.py | 12 | ||||
-rw-r--r-- | Lib/test/test_logging.py | 64 | ||||
-rw-r--r-- | Misc/NEWS | 3 |
4 files changed, 30 insertions, 68 deletions
diff --git a/Doc/library/smtpd.rst b/Doc/library/smtpd.rst index 2ca71ff..2c2df8a 100644 --- a/Doc/library/smtpd.rst +++ b/Doc/library/smtpd.rst @@ -27,7 +27,8 @@ SMTPServer Objects ------------------ -.. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432) +.. class:: SMTPServer(localaddr, remoteaddr, data_size_limit=33554432, + map=None) Create a new :class:`SMTPServer` object, which binds to local address *localaddr*. It will treat *remoteaddr* as an upstream SMTP relayer. It @@ -38,6 +39,8 @@ SMTPServer Objects accepted in a ``DATA`` command. A value of ``None`` or ``0`` means no limit. + A dictionary can be specified in *map* to avoid using a global socket map. + .. method:: process_message(peer, mailfrom, rcpttos, data) Raise :exc:`NotImplementedError` exception. Override this in subclasses to @@ -53,6 +56,9 @@ SMTPServer Objects Override this in subclasses to use a custom :class:`SMTPChannel` for managing SMTP clients. + .. versionchanged:: 3.4 + The *map* argument was added. + DebuggingServer Objects ----------------------- @@ -90,11 +96,20 @@ MailmanProxy Objects SMTPChannel Objects ------------------- -.. class:: SMTPChannel(server, conn, addr) +.. class:: SMTPChannel(server, conn, addr, data_size_limit=33554432, + map=None)) Create a new :class:`SMTPChannel` object which manages the communication between the server and a single SMTP client. + *conn* and *addr* are as per the instance variables described below. + + *data_size_limit* specifies the maximum number of bytes that will be + accepted in a ``DATA`` command. A value of ``None`` or ``0`` means no + limit. + + A dictionary can be specified in *map* to avoid using a global socket map. + To use a custom SMTPChannel implementation you need to override the :attr:`SMTPServer.channel_class` of your :class:`SMTPServer`. diff --git a/Lib/smtpd.py b/Lib/smtpd.py index 3cab264..f28036a 100755 --- a/Lib/smtpd.py +++ b/Lib/smtpd.py @@ -121,8 +121,9 @@ class SMTPChannel(asynchat.async_chat): }) max_command_size_limit = max(command_size_limits.values()) - def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT): - asynchat.async_chat.__init__(self, conn) + def __init__(self, server, conn, addr, data_size_limit=DATA_SIZE_DEFAULT, + map=None): + asynchat.async_chat.__init__(self, conn, map=map) self.smtp_server = server self.conn = conn self.addr = addr @@ -576,11 +577,11 @@ class SMTPServer(asyncore.dispatcher): channel_class = SMTPChannel def __init__(self, localaddr, remoteaddr, - data_size_limit=DATA_SIZE_DEFAULT): + data_size_limit=DATA_SIZE_DEFAULT, map=None): self._localaddr = localaddr self._remoteaddr = remoteaddr self.data_size_limit = data_size_limit - asyncore.dispatcher.__init__(self) + asyncore.dispatcher.__init__(self, map=map) try: self.create_socket(socket.AF_INET, socket.SOCK_STREAM) # try to re-use a server port if possible @@ -597,7 +598,8 @@ class SMTPServer(asyncore.dispatcher): def handle_accepted(self, conn, addr): print('Incoming connection from %s' % repr(addr), file=DEBUGSTREAM) - channel = self.channel_class(self, conn, addr, self.data_size_limit) + channel = self.channel_class(self, conn, addr, self.data_size_limit, + self._map) # API for "doing something useful with the message" def process_message(self, peer, mailfrom, rcpttos, data): diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index aa878da..63d49fe 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -659,41 +659,6 @@ class StreamHandlerTest(BaseTest): # -- if it proves to be of wider utility than just test_logging if threading: - class TestSMTPChannel(smtpd.SMTPChannel): - """ - This derived class has had to be created because smtpd does not - support use of custom channel maps, although they are allowed by - asyncore's design. Issue #11959 has been raised to address this, - and if resolved satisfactorily, some of this code can be removed. - """ - def __init__(self, server, conn, addr, sockmap): - asynchat.async_chat.__init__(self, conn, sockmap) - self.smtp_server = server - self.conn = conn - self.addr = addr - self.data_size_limit = None - self.received_lines = [] - self.smtp_state = self.COMMAND - self.seen_greeting = '' - self.mailfrom = None - self.rcpttos = [] - self.received_data = '' - self.fqdn = socket.getfqdn() - self.num_bytes = 0 - try: - self.peer = conn.getpeername() - except OSError as err: - # a race condition may occur if the other end is closing - # before we can get the peername - self.close() - if err.args[0] != errno.ENOTCONN: - raise - return - self.push('220 %s %s' % (self.fqdn, smtpd.__version__)) - self.set_terminator(b'\r\n') - self.extended_smtp = False - - class TestSMTPServer(smtpd.SMTPServer): """ This class implements a test SMTP server. @@ -714,37 +679,14 @@ if threading: :func:`asyncore.loop`. This avoids changing the :mod:`asyncore` module's global state. """ - channel_class = TestSMTPChannel def __init__(self, addr, handler, poll_interval, sockmap): - self._localaddr = addr - self._remoteaddr = None - self.data_size_limit = None - self.sockmap = sockmap - asyncore.dispatcher.__init__(self, map=sockmap) - try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.setblocking(0) - self.set_socket(sock, map=sockmap) - # try to re-use a server port if possible - self.set_reuse_addr() - self.bind(addr) - self.port = sock.getsockname()[1] - self.listen(5) - except: - self.close() - raise + smtpd.SMTPServer.__init__(self, addr, None, map=sockmap) + self.port = self.socket.getsockname()[1] self._handler = handler self._thread = None self.poll_interval = poll_interval - def handle_accepted(self, conn, addr): - """ - Redefined only because the base class does not pass in a - map, forcing use of a global in :mod:`asyncore`. - """ - channel = self.channel_class(self, conn, addr, self.sockmap) - def process_message(self, peer, mailfrom, rcpttos, data): """ Delegates to the handler passed in to the server's constructor. @@ -775,7 +717,7 @@ if threading: :func:`asyncore.loop`. """ try: - asyncore.loop(poll_interval, map=self.sockmap) + asyncore.loop(poll_interval, map=self._map) except OSError: # On FreeBSD 8, closing the server repeatably # raises this error. We swallow it if the @@ -112,6 +112,9 @@ Core and Builtins Library ------- +- Issue #11959: SMTPServer and SMTPChannel now take an optional map, use of + which avoids affecting global state. + - Issue #18109: os.uname() now decodes fields from the locale encoding, and socket.gethostname() now decodes the hostname from the locale encoding, instead of using the UTF-8 encoding in strict mode. |