issue3972: HTTPConnection and HTTPSConnection now support a

source_address parameter.

Also cleans up an annotation in the socket documentation.
This commit is contained in:
Gregory P. Smith 2010-01-03 02:06:07 +00:00
parent 79a3eb1058
commit 9d3252154f
5 changed files with 61 additions and 12 deletions

View file

@ -34,7 +34,7 @@ uses it to handle URLs that use HTTP and HTTPS.
The module provides the following classes: The module provides the following classes:
.. class:: HTTPConnection(host[, port[, strict[, timeout]]]) .. class:: HTTPConnection(host[, port[, strict[, timeout[, source_address]]]])
An :class:`HTTPConnection` instance represents one transaction with an HTTP An :class:`HTTPConnection` instance represents one transaction with an HTTP
server. It should be instantiated passing it a host and optional port server. It should be instantiated passing it a host and optional port
@ -46,6 +46,8 @@ The module provides the following classes:
status line. If the optional *timeout* parameter is given, blocking status line. If the optional *timeout* parameter is given, blocking
operations (like connection attempts) will timeout after that many seconds operations (like connection attempts) will timeout after that many seconds
(if it is not given, the global default timeout setting is used). (if it is not given, the global default timeout setting is used).
The optional *source_address* parameter may be a tuple of a (host, port)
to use as the source address the HTTP connection is made from.
For example, the following calls all create instances that connect to the server For example, the following calls all create instances that connect to the server
at the same host and port:: at the same host and port::
@ -60,8 +62,11 @@ The module provides the following classes:
.. versionchanged:: 2.6 .. versionchanged:: 2.6
*timeout* was added. *timeout* was added.
.. versionchanged:: 2.7
*source_address* was added.
.. class:: HTTPSConnection(host[, port[, key_file[, cert_file[, strict[, timeout]]]]])
.. class:: HTTPSConnection(host[, port[, key_file[, cert_file[, strict[, timeout[, source_address]]]]]])
A subclass of :class:`HTTPConnection` that uses SSL for communication with A subclass of :class:`HTTPConnection` that uses SSL for communication with
secure servers. Default port is ``443``. *key_file* is the name of a PEM secure servers. Default port is ``443``. *key_file* is the name of a PEM
@ -77,6 +82,9 @@ The module provides the following classes:
.. versionchanged:: 2.6 .. versionchanged:: 2.6
*timeout* was added. *timeout* was added.
.. versionchanged:: 2.7
*source_address* was added.
.. class:: HTTPResponse(sock[, debuglevel=0][, strict=0]) .. class:: HTTPResponse(sock[, debuglevel=0][, strict=0])

View file

@ -213,13 +213,14 @@ The module :mod:`socket` exports the following constants and functions:
*timeout* is supplied, the global default timeout setting returned by *timeout* is supplied, the global default timeout setting returned by
:func:`getdefaulttimeout` is used. :func:`getdefaulttimeout` is used.
.. versionadded:: 2.6
If supplied, *source_address* must be a 2-tuple ``(host, port)`` for the If supplied, *source_address* must be a 2-tuple ``(host, port)`` for the
socket to bind to as its source address before connecting. If host or port socket to bind to as its source address before connecting. If host or port
are '' or 0 respectively the OS default behavior will be used. are '' or 0 respectively the OS default behavior will be used.
.. versionadded:: 2.7 .. versionadded:: 2.6
.. versionchanged:: 2.7
*source_address* was added.
.. function:: getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]]) .. function:: getaddrinfo(host, port[, family[, socktype[, proto[, flags]]]])

View file

@ -658,8 +658,9 @@ class HTTPConnection:
strict = 0 strict = 0
def __init__(self, host, port=None, strict=None, def __init__(self, host, port=None, strict=None,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT): timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None):
self.timeout = timeout self.timeout = timeout
self.source_address = source_address
self.sock = None self.sock = None
self._buffer = [] self._buffer = []
self.__response = None self.__response = None
@ -728,7 +729,7 @@ class HTTPConnection:
def connect(self): def connect(self):
"""Connect to the host and port specified in __init__.""" """Connect to the host and port specified in __init__."""
self.sock = socket.create_connection((self.host,self.port), self.sock = socket.create_connection((self.host,self.port),
self.timeout) self.timeout, self.source_address)
if self._tunnel_host: if self._tunnel_host:
self._tunnel() self._tunnel()
@ -1133,15 +1134,18 @@ else:
default_port = HTTPS_PORT default_port = HTTPS_PORT
def __init__(self, host, port=None, key_file=None, cert_file=None, def __init__(self, host, port=None, key_file=None, cert_file=None,
strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
HTTPConnection.__init__(self, host, port, strict, timeout) source_address=None):
HTTPConnection.__init__(self, host, port, strict, timeout,
source_address)
self.key_file = key_file self.key_file = key_file
self.cert_file = cert_file self.cert_file = cert_file
def connect(self): def connect(self):
"Connect to a host on a given (SSL) port." "Connect to a host on a given (SSL) port."
sock = socket.create_connection((self.host, self.port), self.timeout) sock = socket.create_connection((self.host, self.port),
self.timeout, self.source_address)
if self._tunnel_host: if self._tunnel_host:
self.sock = sock self.sock = sock
self._tunnel() self._tunnel()

View file

@ -3,7 +3,8 @@ import httplib
import StringIO import StringIO
import socket import socket
from unittest import TestCase import unittest
TestCase = unittest.TestCase
from test import test_support from test import test_support
@ -237,6 +238,38 @@ class OfflineTest(TestCase):
def test_responses(self): def test_responses(self):
self.assertEquals(httplib.responses[httplib.NOT_FOUND], "Not Found") self.assertEquals(httplib.responses[httplib.NOT_FOUND], "Not Found")
class SourceAddressTest(TestCase):
def setUp(self):
self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.port = test_support.bind_port(self.serv)
self.source_port = test_support.find_unused_port()
self.serv.listen(5)
self.conn = None
def tearDown(self):
if self.conn:
self.conn.close()
self.conn = None
self.serv.close()
self.serv = None
def testHTTPConnectionSourceAddress(self):
self.conn = httplib.HTTPConnection(HOST, self.port,
source_address=('', self.source_port))
self.conn.connect()
self.assertEqual(self.conn.sock.getsockname()[1], self.source_port)
@unittest.skipIf(not hasattr(httplib, 'HTTPSConnection'),
'httplib.HTTPSConnection not defined')
def testHTTPSConnectionSourceAddress(self):
self.conn = httplib.HTTPSConnection(HOST, self.port,
source_address=('', self.source_port))
# We don't test anything here other the constructor not barfing as
# this code doesn't deal with setting up an active running SSL server
# for an ssl_wrapped connect() to actually return from.
class TimeoutTest(TestCase): class TimeoutTest(TestCase):
PORT = None PORT = None
@ -294,7 +327,7 @@ class HTTPSTimeoutTest(TestCase):
def test_main(verbose=None): def test_main(verbose=None):
test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, test_support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest,
HTTPSTimeoutTest) HTTPSTimeoutTest, SourceAddressTest)
if __name__ == '__main__': if __name__ == '__main__':
test_main() test_main()

View file

@ -62,6 +62,9 @@ Core and Builtins
Library Library
------- -------
_ Issue #3972: httplib.HTTPConnection now accepts an optional source_address
parameter to allow specifying where your connections come from.
- socket.create_connection now accepts an optional source_address parameter. - socket.create_connection now accepts an optional source_address parameter.
- Issue #5511: now zipfile.ZipFile can be used as a context manager. - Issue #5511: now zipfile.ZipFile can be used as a context manager.