Fixed #21271 -- Added timeout parameter to SMTP EmailBackend.
Thanks Tobias McNulty and Tim Graham for discussions and code review. Thanks Andre Cruz the suggestion and initial patch.
This commit is contained in:
parent
9eecb91695
commit
4e0a2fe59c
|
@ -15,7 +15,8 @@ class EmailBackend(BaseEmailBackend):
|
||||||
A wrapper that manages the SMTP network connection.
|
A wrapper that manages the SMTP network connection.
|
||||||
"""
|
"""
|
||||||
def __init__(self, host=None, port=None, username=None, password=None,
|
def __init__(self, host=None, port=None, username=None, password=None,
|
||||||
use_tls=None, fail_silently=False, use_ssl=None, **kwargs):
|
use_tls=None, fail_silently=False, use_ssl=None, timeout=None,
|
||||||
|
**kwargs):
|
||||||
super(EmailBackend, self).__init__(fail_silently=fail_silently)
|
super(EmailBackend, self).__init__(fail_silently=fail_silently)
|
||||||
self.host = host or settings.EMAIL_HOST
|
self.host = host or settings.EMAIL_HOST
|
||||||
self.port = port or settings.EMAIL_PORT
|
self.port = port or settings.EMAIL_PORT
|
||||||
|
@ -23,6 +24,7 @@ class EmailBackend(BaseEmailBackend):
|
||||||
self.password = settings.EMAIL_HOST_PASSWORD if password is None else password
|
self.password = settings.EMAIL_HOST_PASSWORD if password is None else password
|
||||||
self.use_tls = settings.EMAIL_USE_TLS if use_tls is None else use_tls
|
self.use_tls = settings.EMAIL_USE_TLS if use_tls is None else use_tls
|
||||||
self.use_ssl = settings.EMAIL_USE_SSL if use_ssl is None else use_ssl
|
self.use_ssl = settings.EMAIL_USE_SSL if use_ssl is None else use_ssl
|
||||||
|
self.timeout = timeout
|
||||||
if self.use_ssl and self.use_tls:
|
if self.use_ssl and self.use_tls:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set "
|
"EMAIL_USE_TLS/EMAIL_USE_SSL are mutually exclusive, so only set "
|
||||||
|
@ -38,24 +40,22 @@ class EmailBackend(BaseEmailBackend):
|
||||||
if self.connection:
|
if self.connection:
|
||||||
# Nothing to do if the connection is already open.
|
# Nothing to do if the connection is already open.
|
||||||
return False
|
return False
|
||||||
try:
|
|
||||||
|
connection_class = smtplib.SMTP_SSL if self.use_ssl else smtplib.SMTP
|
||||||
# If local_hostname is not specified, socket.getfqdn() gets used.
|
# If local_hostname is not specified, socket.getfqdn() gets used.
|
||||||
# For performance, we use the cached FQDN for local_hostname.
|
# For performance, we use the cached FQDN for local_hostname.
|
||||||
if self.use_ssl:
|
connection_params = {'local_hostname': DNS_NAME.get_fqdn()}
|
||||||
self.connection = smtplib.SMTP_SSL(self.host, self.port,
|
if self.timeout is not None:
|
||||||
local_hostname=DNS_NAME.get_fqdn())
|
connection_params['timeout'] = self.timeout
|
||||||
else:
|
try:
|
||||||
self.connection = smtplib.SMTP(self.host, self.port,
|
self.connection = connection_class(self.host, self.port, **connection_params)
|
||||||
local_hostname=DNS_NAME.get_fqdn())
|
|
||||||
# TLS/SSL are mutually exclusive, so only attempt TLS over
|
# TLS/SSL are mutually exclusive, so only attempt TLS over
|
||||||
# non-secure connections.
|
# non-secure connections.
|
||||||
if self.use_tls:
|
if not self.use_ssl and self.use_tls:
|
||||||
self.connection.ehlo()
|
self.connection.ehlo()
|
||||||
self.connection.starttls()
|
self.connection.starttls()
|
||||||
self.connection.ehlo()
|
self.connection.ehlo()
|
||||||
if self.username and self.password:
|
|
||||||
self.connection.login(self.username, self.password)
|
|
||||||
return True
|
|
||||||
except smtplib.SMTPException:
|
except smtplib.SMTPException:
|
||||||
if not self.fail_silently:
|
if not self.fail_silently:
|
||||||
raise
|
raise
|
||||||
|
|
|
@ -248,6 +248,8 @@ Email
|
||||||
|
|
||||||
* :func:`~django.core.mail.send_mail` now accepts an ``html_message``
|
* :func:`~django.core.mail.send_mail` now accepts an ``html_message``
|
||||||
parameter for sending a multipart ``text/plain`` and ``text/html`` email.
|
parameter for sending a multipart ``text/plain`` and ``text/html`` email.
|
||||||
|
* The SMTP :class:`~django.core.mail.backends.smtp.EmailBackend` now accepts a
|
||||||
|
:attr:`~django.core.mail.backends.smtp.EmailBackend.timeout` parameter.
|
||||||
|
|
||||||
File Uploads
|
File Uploads
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
|
@ -424,17 +424,39 @@ can :ref:`write your own email backend <topic-custom-email-backend>`.
|
||||||
SMTP backend
|
SMTP backend
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
This is the default backend. Email will be sent through a SMTP server.
|
.. class:: backends.smtp.EmailBackend([host=None, port=None, username=None, password=None, use_tls=None, fail_silently=False, use_ssl=None, timeout=None, **kwargs])
|
||||||
The server address and authentication credentials are set in the
|
|
||||||
:setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
|
|
||||||
:setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS` and
|
|
||||||
:setting:`EMAIL_USE_SSL` settings in your settings file.
|
|
||||||
|
|
||||||
The SMTP backend is the default configuration inherited by Django. If you
|
This is the default backend. Email will be sent through a SMTP server.
|
||||||
want to specify it explicitly, put the following in your settings::
|
The server address and authentication credentials are set in the
|
||||||
|
:setting:`EMAIL_HOST`, :setting:`EMAIL_PORT`, :setting:`EMAIL_HOST_USER`,
|
||||||
|
:setting:`EMAIL_HOST_PASSWORD`, :setting:`EMAIL_USE_TLS` and
|
||||||
|
:setting:`EMAIL_USE_SSL` settings in your settings file.
|
||||||
|
|
||||||
|
The SMTP backend is the default configuration inherited by Django. If you
|
||||||
|
want to specify it explicitly, put the following in your settings::
|
||||||
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||||
|
|
||||||
|
Here is an attribute which doesn't have a corresponding settting like the
|
||||||
|
others described above:
|
||||||
|
|
||||||
|
.. attribute:: timeout
|
||||||
|
|
||||||
|
.. versionadded:: 1.7
|
||||||
|
|
||||||
|
This backend contains a ``timeout`` parameter, which can be set with
|
||||||
|
the following sample code::
|
||||||
|
|
||||||
|
from django.core.mail.backends import smtp
|
||||||
|
|
||||||
|
class MyEmailBackend(smtp.EmailBackend):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs.setdefault('timeout', 42)
|
||||||
|
super(MyEmailBackend, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
Then point the :setting:`EMAIL_BACKEND` setting at your custom backend as
|
||||||
|
described above.
|
||||||
|
|
||||||
.. _topic-email-console-backend:
|
.. _topic-email-console-backend:
|
||||||
|
|
||||||
Console backend
|
Console backend
|
||||||
|
|
|
@ -933,3 +933,20 @@ class SMTPBackendTests(BaseEmailBackendTests, SimpleTestCase):
|
||||||
backend = smtp.EmailBackend()
|
backend = smtp.EmailBackend()
|
||||||
self.assertTrue(backend.use_ssl)
|
self.assertTrue(backend.use_ssl)
|
||||||
self.assertRaises(SSLError, backend.open)
|
self.assertRaises(SSLError, backend.open)
|
||||||
|
|
||||||
|
def test_connection_timeout_default(self):
|
||||||
|
"""Test that the connection's timeout value is None by default."""
|
||||||
|
connection = mail.get_connection('django.core.mail.backends.smtp.EmailBackend')
|
||||||
|
self.assertEqual(connection.timeout, None)
|
||||||
|
|
||||||
|
def test_connection_timeout_custom(self):
|
||||||
|
"""Test that the timeout parameter can be customized."""
|
||||||
|
class MyEmailBackend(smtp.EmailBackend):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs.setdefault('timeout', 42)
|
||||||
|
super(MyEmailBackend, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
myemailbackend = MyEmailBackend()
|
||||||
|
myemailbackend.open()
|
||||||
|
self.assertEqual(myemailbackend.timeout, 42)
|
||||||
|
self.assertEqual(myemailbackend.connection.timeout, 42)
|
||||||
|
|
Loading…
Reference in New Issue