summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2009-10-18 20:05:15 -0400
committerBarry Warsaw2009-10-18 20:05:15 -0400
commit34e27b816aa120e81a8e970ac1d8847384bd3edf (patch)
treeed28c04e1b0a15cef0cc528031ef243df21d3210
parent9f6eaef1d836a4234773c0f64dbb09b901ccf21f (diff)
downloadmailman-34e27b816aa120e81a8e970ac1d8847384bd3edf.tar.gz
mailman-34e27b816aa120e81a8e970ac1d8847384bd3edf.tar.zst
mailman-34e27b816aa120e81a8e970ac1d8847384bd3edf.zip
-rw-r--r--src/mailman/mta/connection.py6
-rw-r--r--src/mailman/mta/docs/connection.txt130
-rw-r--r--src/mailman/testing/layers.py4
-rw-r--r--src/mailman/testing/mta.py34
4 files changed, 144 insertions, 30 deletions
diff --git a/src/mailman/mta/connection.py b/src/mailman/mta/connection.py
index 922c492e9..105d25afb 100644
--- a/src/mailman/mta/connection.py
+++ b/src/mailman/mta/connection.py
@@ -54,6 +54,7 @@ class Connection:
self._host = host
self._port = port
self._sessions_per_connection = sessions_per_connection
+ self._session_count = None
self._connection = None
def _connect(self):
@@ -61,6 +62,7 @@ class Connection:
self._connection = smtplib.SMTP()
log.debug('Connecting to %s:%s', self._host, self._port)
self._connection.connect(self._host, self._port)
+ self._session_count = self._sessions_per_connection
def sendmail(self, envsender, recips, msgtext):
"""Mimic `smtplib.SMTP.sendmail`."""
@@ -74,11 +76,11 @@ class Connection:
self.quit()
raise
# This session has been successfully completed.
- self._sessions_per_connection -= 1
+ self._session_count -= 1
# By testing exactly for equality to 0, we automatically handle the
# case for SMTP_MAX_SESSIONS_PER_CONNECTION <= 0 meaning never close
# the connection. We won't worry about wraparound <wink>.
- if self._sessions_per_connection == 0:
+ if self._session_count == 0:
self.quit()
return results
diff --git a/src/mailman/mta/docs/connection.txt b/src/mailman/mta/docs/connection.txt
index d57e301d6..8924826c8 100644
--- a/src/mailman/mta/docs/connection.txt
+++ b/src/mailman/mta/docs/connection.txt
@@ -4,22 +4,23 @@ MTA connections
Outgoing connections to the outgoing mail transport agent (MTA) are mitigated
through a Connection class, which can transparently manage multiple sessions
-to a single physical MTA.
+in a single connection.
>>> from mailman.mta.connection import Connection
-The number of connections per session is specified when the Connection object
-is created, as is the host and port number of the SMTP server.
+The number of sessions per connections is specified when the Connection object
+is created, as is the host and port number of the SMTP server. Zero means
+there's an unlimited number of sessions per connection.
>>> connection = Connection(
- ... config.mta.smtp_host, int(config.mta.smtp_port), 1)
+ ... config.mta.smtp_host, int(config.mta.smtp_port), 0)
At the start, there have been no connections to the server.
- >>> smtpd.get_session_count()
+ >>> smtpd.get_connection_count()
0
-By sending a message to the server, a session is started.
+By sending a message to the server, a connection is opened.
>>> connection.sendmail('anne@example.com', ['bart@example.com'], """\
... From: anne@example.com
@@ -29,10 +30,10 @@ By sending a message to the server, a session is started.
... """)
{}
- >>> smtpd.get_session_count()
+ >>> smtpd.get_connection_count()
1
-We can reset the session count back to zero.
+We can reset the connection count back to zero.
>>> from smtplib import SMTP
>>> def reset():
@@ -41,5 +42,116 @@ We can reset the session count back to zero.
... smtpd.docmd('RSET')
>>> reset()
- >>> smtpd.get_session_count()
+ >>> smtpd.get_connection_count()
0
+
+ >>> connection.quit()
+
+
+Sessions per connection
+=======================
+
+Let's say we specify a maximum number of sessions per connection of 2. When
+the third message is sent, the connection is torn down and a new one is
+created.
+
+The connection count starts at zero.
+
+ >>> connection = Connection(
+ ... config.mta.smtp_host, int(config.mta.smtp_port), 2)
+
+ >>> smtpd.get_connection_count()
+ 0
+
+We send two messages through the Connection object. Only one connection is
+opened.
+
+ >>> connection.sendmail('anne@example.com', ['bart@example.com'], """\
+ ... From: anne@example.com
+ ... To: bart@example.com
+ ... Subject: aardvarks
+ ...
+ ... """)
+ {}
+
+ >>> smtpd.get_connection_count()
+ 1
+
+ >>> connection.sendmail('anne@example.com', ['bart@example.com'], """\
+ ... From: anne@example.com
+ ... To: bart@example.com
+ ... Subject: aardvarks
+ ...
+ ... """)
+ {}
+
+ >>> smtpd.get_connection_count()
+ 1
+
+The third message causes a third session, which exceeds the maximum. So the
+current connection is closed and a new one opened.
+
+ >>> connection.sendmail('anne@example.com', ['bart@example.com'], """\
+ ... From: anne@example.com
+ ... To: bart@example.com
+ ... Subject: aardvarks
+ ...
+ ... """)
+ {}
+
+ >>> smtpd.get_connection_count()
+ 2
+
+A fourth message does not cause a new connection to be made.
+
+ >>> connection.sendmail('anne@example.com', ['bart@example.com'], """\
+ ... From: anne@example.com
+ ... To: bart@example.com
+ ... Subject: aardvarks
+ ...
+ ... """)
+ {}
+
+ >>> smtpd.get_connection_count()
+ 2
+
+But a fifth one does.
+
+ >>> connection.sendmail('anne@example.com', ['bart@example.com'], """\
+ ... From: anne@example.com
+ ... To: bart@example.com
+ ... Subject: aardvarks
+ ...
+ ... """)
+ {}
+
+ >>> smtpd.get_connection_count()
+ 3
+
+
+No maximum
+==========
+
+A value of zero means that there is an unlimited number of sessions per
+connection.
+
+ >>> connection = Connection(
+ ... config.mta.smtp_host, int(config.mta.smtp_port), 0)
+ >>> reset()
+
+Even after ten messages are sent, there's still been only one connection to
+the server.
+
+ >>> connection.debug = True
+ >>> for i in range(10):
+ ... # Ignore the results.
+ ... results = connection.sendmail(
+ ... 'anne@example.com', ['bart@example.com'], """\
+ ... From: anne@example.com
+ ... To: bart@example.com
+ ... Subject: aardvarks
+ ...
+ ... """)
+
+ >>> smtpd.get_connection_count()
+ 1
diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py
index 034e26f83..70b0ae9b1 100644
--- a/src/mailman/testing/layers.py
+++ b/src/mailman/testing/layers.py
@@ -47,7 +47,7 @@ from mailman.i18n import _
from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.messages import IMessageStore
from mailman.testing.helpers import TestableMaster
-from mailman.testing.mta import SessionCountingController
+from mailman.testing.mta import ConnectionCountingController
from mailman.utilities.datetime import factory
from mailman.utilities.string import expand
@@ -220,7 +220,7 @@ class SMTPLayer(ConfigLayer):
assert cls.smtpd is None, 'Layer already set up'
host = config.mta.smtp_host
port = int(config.mta.smtp_port)
- cls.smtpd = SessionCountingController(host, port)
+ cls.smtpd = ConnectionCountingController(host, port)
cls.smtpd.start()
@classmethod
diff --git a/src/mailman/testing/mta.py b/src/mailman/testing/mta.py
index 28e5ec77c..62070cc5d 100644
--- a/src/mailman/testing/mta.py
+++ b/src/mailman/testing/mta.py
@@ -56,8 +56,8 @@ class FakeMTA:
-class SessionCountingChannel(Channel):
- """Count the number of SMTP sessions opened and closed."""
+class StatisticsChannel(Channel):
+ """A channel that can answers to the fake STAT command."""
def smtp_STAT(self, arg):
"""Cause the server to send statistics to its controller."""
@@ -66,13 +66,13 @@ class SessionCountingChannel(Channel):
-class SessionCountingServer(QueueServer):
- """Count the number of SMTP sessions opened and closed."""
+class ConnectionCountingServer(QueueServer):
+ """Count the number of SMTP connections opened."""
def __init__(self, host, port, queue, oob_queue):
"""See `lazr.smtptest.server.QueueServer`."""
QueueServer.__init__(self, host, port, queue)
- self.session_count = 0
+ self._connection_count = 0
# The out-of-band queue is where the server sends statistics to the
# controller upon request.
self._oob_queue = oob_queue
@@ -80,25 +80,25 @@ class SessionCountingServer(QueueServer):
def handle_accept(self):
"""See `lazr.smtp.server.Server`."""
connection, address = self.accept()
- self.session_count += 1
- log.info('[SessionCountingServer] accepted: %s', address)
- SessionCountingChannel(self, connection, address)
+ self._connection_count += 1
+ log.info('[ConnectionCountingServer] accepted: %s', address)
+ StatisticsChannel(self, connection, address)
def reset(self):
"""See `lazr.smtp.server.Server`."""
QueueServer.reset(self)
- self.session_count = 0
+ self._connection_count = 0
def send_statistics(self):
"""Send the current connection statistics to the controller."""
# Do not count the connection caused by the STAT connect.
- self.session_count -= 1
- self._oob_queue.put(self.session_count)
+ self._connection_count -= 1
+ self._oob_queue.put(self._connection_count)
-class SessionCountingController(QueueController):
- """Count the number of SMTP sessions opened and closed."""
+class ConnectionCountingController(QueueController):
+ """Count the number of SMTP connections opened."""
def __init__(self, host, port):
"""See `lazr.smtptest.controller.QueueController`."""
@@ -107,7 +107,7 @@ class SessionCountingController(QueueController):
def _make_server(self, host, port):
"""See `lazr.smtptest.controller.QueueController`."""
- self.server = SessionCountingServer(
+ self.server = ConnectionCountingServer(
host, port, self.queue, self.oob_queue)
def start(self):
@@ -117,10 +117,10 @@ class SessionCountingController(QueueController):
# method causes a connection to occur.
self.reset()
- def get_session_count(self):
- """Retrieve the number of sessions.
+ def get_connection_count(self):
+ """Retrieve the number of connections.
- :return: The number of sessions that have been opened.
+ :return: The number of connections to the server that have been made.
:rtype: integer
"""
smtpd = self._connect()