Merge lp:~futatuki/mailman/2.1-add-smtp-timeout into lp:mailman/2.1

Proposed by Yasuhito FUTATSUKI at POEM
Status: Needs review
Proposed branch: lp:~futatuki/mailman/2.1-add-smtp-timeout
Merge into: lp:mailman/2.1
Diff against target: 46 lines (+18/-1)
2 files modified
Mailman/Defaults.py.in (+6/-0)
Mailman/Handlers/SMTPDirect.py (+12/-1)
To merge this branch: bzr merge lp:~futatuki/mailman/2.1-add-smtp-timeout
Reviewer Review Type Date Requested Status
Mailman Coders Pending
Review via email: mp+337353@code.launchpad.net

Description of the change

This add a feature to specify timeout for SMTP response to avoid waiting response forever, to SMTPDirect Handler. To specify timeout, set SMTP_TIMEOUT in mm_cfg.py.

By default, this is disabled(waiting response until respond the MTA).

To test this feature, set mm_cfg.SMTP_TIMEOUT to small value and setup MTA to wait responding (by using greet pause feature, etc) and run smtp test or post message to mailing list to deliver it.

To post a comment you must log in.
Revision history for this message
Mark Sapiro (msapiro) wrote :

Thank you for this contribution. I'm still considering how to handle this. I'd like to make Python 2.7 or at least Python 2.6 a minimum requirement, but that's not really an issue as your code handles older versions.

But there is an issue in how socket.timeout is handled in OutgoingRunner. Currently, OutgoingRunner treats all socket exceptions as a failure to connect. I think I'd want to catch socket.timeout in SMTPDirect, log the fact and then raise SomeRecipientsFailed instead.

Also I think it would be appropriate to set the default other than None. Maybe something like one minute or five minutes, but I'm not sure what a good value would be.

Revision history for this message
Yasuhito FUTATSUKI at POEM (futatuki) wrote :

Thank you for your consideration for this issue.

> But there is an issue in how socket.timeout is handled in OutgoingRunner.
> Currently, OutgoingRunner treats all socket exceptions as a failure to
> connect. I think I'd want to catch socket.timeout in SMTPDirect, log the fact
> and then raise SomeRecipientsFailed instead.

To accomplish it, there seems to be some different ways.
(1) inspect SMTPDirect exception to find what causes it.
(2) overriding methods of smtplib.SMTP catches socket.error (send() and getreply(), on current implementation of smtplib module (of CPython 2.x). socket.timeout can be caused in connect(), but it is not caught in connect()).
(3) write our own implementation for SMTP, instead of smtplib
(4) other...

(1) and (2) depend on undocumented part of smtplib (of current implementation in CPython 2) as Python standard library, though.

What kind of way to take do you think? (I'd like to help you for this issue if I can do something for it.)

> Also I think it would be appropriate to set the default other than None. Maybe
> something like one minute or five minutes, but I'm not sure what a good value
> would be.

I agree with None is not appropriate for default, but I also have no idea what is good. It is why I choose None for default value :-)
I think one minute is short even if the MTA trust QutgoingRunner, if checking message body, etc, but of course, this is only my personal opinion.

Unmerged revisions

1745. By Yasuhito FUTATSUKI at POEM

Add SMTP timeout feature to SMTPDirect handler

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Mailman/Defaults.py.in'
--- Mailman/Defaults.py.in 2018-01-30 04:06:24 +0000
+++ Mailman/Defaults.py.in 2018-02-08 12:35:39 +0000
@@ -591,6 +591,12 @@
591# uses DEFAULT_URL_HOST. Normally, you should not change this.591# uses DEFAULT_URL_HOST. Normally, you should not change this.
592SMTP_HELO_HOST = DEFAULT_URL_HOST592SMTP_HELO_HOST = DEFAULT_URL_HOST
593593
594# The time to wait for a response from MTA for DELIVERY_MODULE = 'SMTPDirect',
595# in positive float seconds or None. Setting a timeout of None disables
596# timeouts on socket operations.
597# Note: too short timeout will often cause duplicate message delivery.
598SMTP_TIMEOUT = None
599
594# Set these variables if you need to authenticate to your NNTP server for600# Set these variables if you need to authenticate to your NNTP server for
595# Usenet posting or reading. If no authentication is necessary, specify None601# Usenet posting or reading. If no authentication is necessary, specify None
596# for both variables.602# for both variables.
597603
=== modified file 'Mailman/Handlers/SMTPDirect.py'
--- Mailman/Handlers/SMTPDirect.py 2017-05-23 19:45:06 +0000
+++ Mailman/Handlers/SMTPDirect.py 2018-02-08 12:35:39 +0000
@@ -55,13 +55,24 @@
5555
5656
5757
5858
59if mm_cfg.SMTP_TIMEOUT:
60 # wrapper SMTP class to specify timeout (for Python < 2.6 compatibility)
61 class SMTP(smtplib.SMTP):
62 def connect(self, host='localhost', port=0):
63 sv_to = socket.getdefaulttimeout()
64 socket.setdefaulttimeout(mm_cfg.SMTP_TIMEOUT)
65 smtplib.SMTP.connect(self, host, port)
66 socket.setdefaulttimeout(sv_to)
67else:
68 SMTP = smtplib.SMTP
69
59# Manage a connection to the SMTP server70# Manage a connection to the SMTP server
60class Connection:71class Connection:
61 def __init__(self):72 def __init__(self):
62 self.__conn = None73 self.__conn = None
6374
64 def __connect(self):75 def __connect(self):
65 self.__conn = smtplib.SMTP()76 self.__conn = SMTP()
66 self.__conn.set_debuglevel(mm_cfg.SMTPLIB_DEBUG_LEVEL)77 self.__conn.set_debuglevel(mm_cfg.SMTPLIB_DEBUG_LEVEL)
67 self.__conn.connect(mm_cfg.SMTPHOST, mm_cfg.SMTPPORT)78 self.__conn.connect(mm_cfg.SMTPHOST, mm_cfg.SMTPPORT)
68 if mm_cfg.SMTP_AUTH:79 if mm_cfg.SMTP_AUTH: