Skip to content

Commit

Permalink
Merge pull request #678 from gagoman/feature/socket-binding
Browse files Browse the repository at this point in the history
local socket binding for TCPConnector
  • Loading branch information
asvetlov committed Feb 1, 2016
2 parents 6571ecb + 3e3bf3f commit a72c912
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 3 deletions.
7 changes: 5 additions & 2 deletions aiohttp/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,13 +405,14 @@ class TCPConnector(BaseConnector):
https://en.wikipedia.org/wiki/Transport_Layer_Security#Certificate_pinning
:param bool resolve: Set to True to do DNS lookup for host name.
:param family: socket address family
:param local_addr: local :class:`tuple` of (host, port) to bind socket to
:param args: see :class:`BaseConnector`
:param kwargs: see :class:`BaseConnector`
"""

def __init__(self, *, verify_ssl=True, fingerprint=None,
resolve=_marker, use_dns_cache=_marker,
family=0, ssl_context=None,
family=0, ssl_context=None, local_addr=None,
**kwargs):
super().__init__(**kwargs)

Expand Down Expand Up @@ -450,6 +451,7 @@ def __init__(self, *, verify_ssl=True, fingerprint=None,
self._cached_hosts = {}
self._ssl_context = ssl_context
self._family = family
self._local_addr = local_addr

@property
def verify_ssl(self):
Expand Down Expand Up @@ -575,7 +577,8 @@ def _create_connection(self, req):
self._factory, host, port,
ssl=sslcontext, family=hinfo['family'],
proto=hinfo['proto'], flags=hinfo['flags'],
server_hostname=hinfo['hostname'] if sslcontext else None)
server_hostname=hinfo['hostname'] if sslcontext else None,
local_addr=self._local_addr)
has_cert = transp.get_extra_info('sslcontext')
if has_cert and self._fingerprint:
sock = transp.get_extra_info('socket')
Expand Down
5 changes: 4 additions & 1 deletion docs/client_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -752,7 +752,7 @@ TCPConnector
family=0, \
ssl_context=None, conn_timeout=None, \
keepalive_timeout=30, limit=None, share_cookies=False, \
force_close=False, loop=None)
force_close=False, loop=None, local_addr=None)

Connector for working with *HTTP* and *HTTPS* via *TCP* sockets.

Expand Down Expand Up @@ -806,6 +806,9 @@ TCPConnector
*ssl_context* may be used for configuring certification
authority channel, supported SSL options etc.

:param tuple local_addr: tuple of ``(local_host, local_port)`` used to bind
socket locally.

.. attribute:: verify_ssl

Check *ssl certifications* if ``True``.
Expand Down
26 changes: 26 additions & 0 deletions tests/test_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,32 @@ def handler(request):
r.close()
conn.close()

def test_tcp_connector_uses_provided_local_addr(self):
@asyncio.coroutine
def handler(request):
return web.HTTPOk()

app, srv, url = self.loop.run_until_complete(
self.create_server('get', '/', handler)
)

port = self.find_unused_port()
conn = aiohttp.TCPConnector(loop=self.loop,
local_addr=('127.0.0.1', port))

r = self.loop.run_until_complete(
aiohttp.request(
'get', url,
connector=conn
))

self.loop.run_until_complete(r.release())
first_conn = next(iter(conn._conns.values()))[0][0]
self.assertEqual(first_conn._sock.getsockname(), ('127.0.0.1', port))
r.close()

conn.close()

@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'requires unix')
def test_unix_connector(self):
@asyncio.coroutine
Expand Down

0 comments on commit a72c912

Please sign in to comment.