Skip to content

Commit

Permalink
Add option to set the source IP address
Browse files Browse the repository at this point in the history
  • Loading branch information
nveloso committed Sep 17, 2024
1 parent 3cafb9f commit 26cd1c7
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 0 deletions.
2 changes: 2 additions & 0 deletions botocore/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ def get_client_args(
socket_options=socket_options,
client_cert=new_config.client_cert,
proxies_config=new_config.proxies_config,
source_address=new_config.source_address,
)

serializer = botocore.serialize.create_serializer(
Expand Down Expand Up @@ -271,6 +272,7 @@ def compute_client_args(
sigv4a_signing_region_set=(
client_config.sigv4a_signing_region_set
),
source_address=client_config.source_address,
)
self._compute_retry_config(config_kwargs)
self._compute_connect_timeout(config_kwargs)
Expand Down
8 changes: 8 additions & 0 deletions botocore/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ class Config:
documentation. Invalid parameters or ones that are not used by the
specified service will be ignored.
Defaults to None.
:type source_address: tuple
:param source_address: A tuple with 2 values (host, port) for the socket to
bind to as its source address before connecting. If host or port are '' or 0
respectively the OS default behaviour will be used.
Defaults to None.
"""

Expand Down Expand Up @@ -264,6 +271,7 @@ class Config:
('disable_request_compression', None),
('client_context_params', None),
('sigv4a_signing_region_set', None),
('source_address', None),
]
)

Expand Down
2 changes: 2 additions & 0 deletions botocore/endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ def create_endpoint(
socket_options=None,
client_cert=None,
proxies_config=None,
source_address=None,
):
if not is_valid_endpoint_url(
endpoint_url
Expand All @@ -420,6 +421,7 @@ def create_endpoint(
socket_options=socket_options,
client_cert=client_cert,
proxies_config=proxies_config,
source_address=source_address,
)

return Endpoint(
Expand Down
4 changes: 4 additions & 0 deletions botocore/httpsession.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ def __init__(
socket_options=None,
client_cert=None,
proxies_config=None,
source_address=None,
):
self._verify = verify
self._proxy_config = ProxyConfiguration(
Expand All @@ -319,6 +320,7 @@ def __init__(
self._socket_options = socket_options
if socket_options is None:
self._socket_options = []
self._source_address = source_address
self._proxy_managers = {}
self._manager = PoolManager(**self._get_pool_manager_kwargs())
self._manager.pool_classes_by_scheme = self._pool_classes_by_scheme
Expand All @@ -342,6 +344,8 @@ def _get_pool_manager_kwargs(self, **extra_kwargs):
'cert_file': self._cert_file,
'key_file': self._key_file,
}
if self._source_address:
pool_manager_kwargs['source_address'] = self._source_address
pool_manager_kwargs.update(**extra_kwargs)
return pool_manager_kwargs

Expand Down
1 change: 1 addition & 0 deletions tests/unit/test_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ def assert_create_endpoint_call(self, mock_endpoint, **override_kwargs):
'proxies_config': None,
'socket_options': self.default_socket_options,
'client_cert': None,
'source_address': None,
}
call_kwargs.update(**override_kwargs)
mock_endpoint.return_value.create_endpoint.assert_called_with(
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/test_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,15 @@ def test_socket_options(self):
)
session_args = self.mock_session.call_args[1]
self.assertEqual(session_args.get('socket_options'), socket_options)

def test_source_address(self):
source_address = ('192.168.1.1', 1234)
self.creator.create_endpoint(
self.service_model,
region_name='us-west-2',
endpoint_url='https://example.com',
http_session_cls=self.mock_session,
source_address=source_address,
)
session_args = self.mock_session.call_args[1]
self.assertEqual(session_args.get('source_address'), source_address)
19 changes: 19 additions & 0 deletions tests/unit/test_http_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,25 @@ def test_session_forwards_socket_options_to_proxy_manager(self):
socket_options=socket_options,
)

def test_session_forwards_source_address_to_pool_manager(self):
source_address = ('192.168.1.1', 1234)
URLLib3Session(source_address=source_address)
self.assert_pool_manager_call(source_address=source_address)

def test_session_forwards_source_address_to_proxy_manager(self):
proxies = {'http': 'http://proxy.com'}
source_address = ('192.168.1.1', 1234)
session = URLLib3Session(
proxies=proxies,
source_address=source_address,
)
session.send(self.request.prepare())
self.assert_proxy_manager_call(
proxies['http'],
proxy_headers={},
source_address=source_address,
)

def make_request_with_error(self, error):
self.connection.urlopen.side_effect = error
session = URLLib3Session()
Expand Down

0 comments on commit 26cd1c7

Please sign in to comment.