Skip to content

Commit ef96004

Browse files
authored
fix retries and refactor httpsession (#877)
1 parent d3db4f3 commit ef96004

21 files changed

+296
-208
lines changed

CHANGES.rst

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,44 @@
11
Changes
22
-------
3+
1.4.0 (2021-08-20)
4+
^^^^^^^^^^^^^^^^^^
5+
* fix retries via config `#877 <https://github.com/aio-libs/aiobotocore/pull/877>`_
6+
* remove AioSession and get_session top level names to match botocore_
7+
* change exceptions raised to match those of botocore_, see `mappings <https://github.com/aio-libs/aiobotocore/pull/877/files#diff-b1675e1eb4276bfae81107cda919ba446e4ce1b1e228a9e878d65dd1f474bf8cR162-R181>`_
8+
39
1.3.3 (2021-07-12)
410
^^^^^^^^^^^^^^^^^^
5-
* fix AioJSONParser #872
11+
* fix AioJSONParser `#872 <https://github.com/aio-libs/aiobotocore/issues/872>`_
612

713
1.3.2 (2021-07-07)
814
^^^^^^^^^^^^^^^^^^
9-
* Bump to botocore 1.20.106
15+
* Bump to botocore_ to `1.20.106 <https://github.com/boto/botocore/tree/1.20.106>`_
1016

1117
1.3.1 (2021-06-11)
1218
^^^^^^^^^^^^^^^^^^
1319
* TCPConnector: change deprecated ssl_context to ssl
14-
* fix non awaited generate presigned url calls #868
20+
* fix non awaited generate presigned url calls `#868 <https://github.com/aio-libs/aiobotocore/issues/868>`_
1521

1622
1.3.0 (2021-04-09)
1723
^^^^^^^^^^^^^^^^^^
18-
* Bump to botocore 1.20.49 #856
24+
* Bump to botocore_ to `1.20.49 <https://github.com/boto/botocore/tree/1.20.49>`_ `#856 <https://github.com/aio-libs/aiobotocore/pull/856>`_
1925

2026
1.2.2 (2021-03-11)
2127
^^^^^^^^^^^^^^^^^^
22-
* Await call to async method _load_creds_via_assume_role #851 (thanks @puzza007)
28+
* Await call to async method _load_creds_via_assume_role `#858 <https://github.com/aio-libs/aiobotocore/pull/858>`_ (thanks `@puzza007 <https://github.com/puzza007>`_)
2329

2430
1.2.1 (2021-02-10)
2531
^^^^^^^^^^^^^^^^^^
26-
* verify strings are now correctly passed to aiohttp.TCPConnector #851 (thanks @FHTMitchell)
32+
* verify strings are now correctly passed to aiohttp.TCPConnector `#851 <https://github.com/aio-libs/aiobotocore/pull/851>`_ (thanks `@FHTMitchell <https://github.com/FHTMitchell>`_)
2733

2834
1.2.0 (2021-01-11)
2935
^^^^^^^^^^^^^^^^^^
30-
* bump botocore to 1.19.52
31-
* use passed in http_session_cls param to create_client (#797)
36+
* bump botocore to `1.19.52 <https://github.com/boto/botocore/tree/1.19.52>`_
37+
* use passed in http_session_cls param to create_client `#797 <https://github.com/aio-libs/aiobotocore/issues/797>`_
3238

3339
1.1.2 (2020-10-07)
3440
^^^^^^^^^^^^^^^^^^
35-
* fix AioPageIterator search method #831 (thanks @joseph-jones)
41+
* fix AioPageIterator search method #831 (thanks `@joseph-jones <https://github.com/joseph-jones>`_)
3642

3743
1.1.1 (2020-08-31)
3844
^^^^^^^^^^^^^^^^^^

README.rst

+14-18
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ aiobotocore
1717

1818
Async client for amazon services using botocore_ and aiohttp_/asyncio_.
1919

20-
Main purpose of this library to support amazon s3 api, but other services
21-
should work (may be with minor fixes). For now we have tested
22-
only upload/download api for s3, other users report that SQS and Dynamo
23-
services work also. More tests coming soon.
20+
This library is a mostly full featured asynchronous version of botocore.
2421

2522

2623
Install
@@ -36,7 +33,7 @@ Basic Example
3633
.. code:: python
3734
3835
import asyncio
39-
import aiobotocore
36+
from aiobotocore.session import get_session
4037
4138
AWS_ACCESS_KEY_ID = "xxx"
4239
AWS_SECRET_ACCESS_KEY = "xxx"
@@ -48,7 +45,7 @@ Basic Example
4845
folder = 'aiobotocore'
4946
key = '{}/{}'.format(folder, filename)
5047
51-
session = aiobotocore.get_session()
48+
session = get_session()
5249
async with session.create_client('s3', region_name='us-west-2',
5350
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
5451
aws_access_key_id=AWS_ACCESS_KEY_ID) as client:
@@ -90,36 +87,36 @@ Context Manager Examples
9087
.. code:: python
9188
9289
from contextlib import AsyncExitStack
93-
90+
9491
from aiobotocore.session import AioSession
95-
96-
92+
93+
9794
# How to use in existing context manager
9895
class Manager:
9996
def __init__(self):
10097
self._exit_stack = AsyncExitStack()
10198
self._s3_client = None
102-
99+
103100
async def __aenter__(self):
104101
session = AioSession()
105102
self._s3_client = await self._exit_stack.enter_async_context(session.create_client('s3'))
106-
103+
107104
async def __aexit__(self, exc_type, exc_val, exc_tb):
108105
await self._exit_stack.__aexit__(exc_type, exc_val, exc_tb)
109-
106+
110107
# How to use with an external exit_stack
111108
async def create_s3_client(session: AioSession, exit_stack: AsyncExitStack):
112109
# Create client and add cleanup
113110
client = await exit_stack.enter_async_context(session.create_client('s3'))
114111
return client
115-
116-
112+
113+
117114
async def non_manager_example():
118115
session = AioSession()
119-
116+
120117
async with AsyncExitStack() as exit_stack:
121118
s3_client = await create_s3_client(session, exit_stack)
122-
119+
123120
# do work with s3_client
124121
125122
@@ -186,8 +183,7 @@ Requirements
186183
.. _Python: https://www.python.org
187184
.. _asyncio: https://docs.python.org/3/library/asyncio.html
188185
.. _botocore: https://github.com/boto/botocore
189-
.. _aiohttp: https://github.com/KeepSafe/aiohttp
190-
186+
.. _aiohttp: https://github.com/aio-libs/aiohttp
191187

192188
awscli
193189
------

aiobotocore/__init__.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
from .session import get_session, AioSession
2-
3-
__all__ = ['get_session', 'AioSession']
4-
__version__ = '1.3.3'
1+
__version__ = '1.4.0'

aiobotocore/endpoint.py

+16-112
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,17 @@
11
import aiohttp
22
import asyncio
3-
import io
4-
import pathlib
5-
import os
6-
import ssl
7-
import sys
3+
84
import aiohttp.http_exceptions
9-
from aiohttp.client import URL
105
from botocore.endpoint import EndpointCreator, Endpoint, DEFAULT_TIMEOUT, \
116
MAX_POOL_CONNECTIONS, logger, history_recorder, create_request_object
127
from botocore.exceptions import ConnectionClosedError
138
from botocore.hooks import first_non_none_response
149
from botocore.utils import is_valid_endpoint_url
15-
from multidict import MultiDict
16-
from urllib.parse import urlparse
1710
from urllib3.response import HTTPHeaderDict
11+
12+
from aiobotocore.httpsession import AIOHTTPSession
1813
from aiobotocore.response import StreamingBody
19-
from aiobotocore._endpoint_helpers import _text, _IOBaseWrapper, \
20-
ClientResponseProxy
14+
from aiobotocore._endpoint_helpers import ClientResponseProxy # noqa: F401, E501 lgtm [py/unused-import]
2115

2216

2317
async def convert_to_response_dict(http_response, operation_model):
@@ -62,10 +56,6 @@ async def convert_to_response_dict(http_response, operation_model):
6256

6357

6458
class AioEndpoint(Endpoint):
65-
def __init__(self, *args, proxies=None, **kwargs):
66-
super().__init__(*args, **kwargs)
67-
self.proxies = proxies or {}
68-
6959
async def create_request(self, params, operation_model=None):
7060
request = create_request_object(params)
7161
if operation_model:
@@ -237,53 +227,15 @@ async def _needs_retry(self, attempts, operation_model, request_dict,
237227
return True
238228

239229
async def _send(self, request):
240-
# Note: When using aiobotocore with dynamodb, requests fail on crc32
241-
# checksum computation as soon as the response data reaches ~5KB.
242-
# When AWS response is gzip compressed:
243-
# 1. aiohttp is automatically decompressing the data
244-
# (http://aiohttp.readthedocs.io/en/stable/client.html#binary-response-content)
245-
# 2. botocore computes crc32 on the uncompressed data bytes and fails
246-
# cause crc32 has been computed on the compressed data
247-
# The following line forces aws not to use gzip compression,
248-
# if there is a way to configure aiohttp not to perform decompression,
249-
# we can remove the following line and take advantage of
250-
# aws gzip compression.
251-
# https://github.com/boto/botocore/issues/1255
252-
url = request.url
253-
headers = request.headers
254-
data = request.body
255-
256-
headers['Accept-Encoding'] = 'identity'
257-
headers_ = MultiDict(
258-
(z[0], _text(z[1], encoding='utf-8')) for z in headers.items())
259-
260-
# botocore does this during the request so we do this here as well
261-
# TODO: this should be part of the ClientSession, perhaps make wrapper
262-
proxy = self.proxies.get(urlparse(url.lower()).scheme)
263-
264-
if isinstance(data, io.IOBase):
265-
data = _IOBaseWrapper(data)
266-
267-
url = URL(url, encoded=True)
268-
resp = await self.http_session.request(
269-
request.method, url=url, headers=headers_, data=data, proxy=proxy)
270-
271-
# If we're not streaming, read the content so we can retry any timeout
272-
# errors, see:
273-
# https://github.com/boto/botocore/blob/develop/botocore/vendored/requests/sessions.py#L604
274-
if not request.stream_output:
275-
await resp.read()
276-
277-
return resp
230+
return await self.http_session.send(request)
278231

279232

280233
class AioEndpointCreator(EndpointCreator):
281-
# TODO: handle socket_options
282234
def create_endpoint(self, service_model, region_name, endpoint_url,
283235
verify=None, response_parser_factory=None,
284236
timeout=DEFAULT_TIMEOUT,
285237
max_pool_connections=MAX_POOL_CONNECTIONS,
286-
http_session_cls=aiohttp.ClientSession,
238+
http_session_cls=AIOHTTPSession,
287239
proxies=None,
288240
socket_options=None,
289241
client_cert=None,
@@ -297,68 +249,20 @@ def create_endpoint(self, service_model, region_name, endpoint_url,
297249
endpoint_prefix = service_model.endpoint_prefix
298250

299251
logger.debug('Setting %s timeout as %s', endpoint_prefix, timeout)
300-
301-
if isinstance(timeout, (list, tuple)):
302-
conn_timeout, read_timeout = timeout
303-
else:
304-
conn_timeout = read_timeout = timeout
305-
306-
if connector_args is None:
307-
# AWS has a 20 second idle timeout:
308-
# https://forums.aws.amazon.com/message.jspa?messageID=215367
309-
# aiohttp default timeout is 30s so set something reasonable here
310-
connector_args = dict(keepalive_timeout=12)
311-
312-
timeout = aiohttp.ClientTimeout(
313-
sock_connect=conn_timeout,
314-
sock_read=read_timeout
315-
)
316-
317-
verify = self._get_verify_value(verify)
318-
ssl_context = None
319-
if client_cert:
320-
if isinstance(client_cert, str):
321-
key_file = None
322-
cert_file = client_cert
323-
elif isinstance(client_cert, tuple):
324-
cert_file, key_file = client_cert
325-
else:
326-
raise TypeError("client_cert must be str or tuple, not %s" %
327-
client_cert.__class__.__name__)
328-
329-
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
330-
ssl_context.load_cert_chain(cert_file, key_file)
331-
elif isinstance(verify, (str, pathlib.Path)):
332-
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH,
333-
cafile=str(verify))
334-
335-
if ssl_context:
336-
# Enable logging of TLS session keys via defacto standard environment variable # noqa: E501
337-
# 'SSLKEYLOGFILE', if the feature is available (Python 3.8+). Skip empty values. # noqa: E501
338-
if hasattr(ssl_context, 'keylog_filename'):
339-
keylogfile = os.environ.get('SSLKEYLOGFILE')
340-
if keylogfile and not sys.flags.ignore_environment:
341-
ssl_context.keylog_filename = keylogfile
342-
343-
# TODO: add support for proxies_config
344-
345-
connector = aiohttp.TCPConnector(
346-
limit=max_pool_connections,
347-
verify_ssl=bool(verify),
348-
ssl=ssl_context,
349-
**connector_args)
350-
351-
aio_session = http_session_cls(
352-
connector=connector,
252+
http_session = http_session_cls(
353253
timeout=timeout,
354-
skip_auto_headers={'CONTENT-TYPE'},
355-
response_class=ClientResponseProxy,
356-
auto_decompress=False)
254+
proxies=proxies,
255+
verify=self._get_verify_value(verify),
256+
max_pool_connections=max_pool_connections,
257+
socket_options=socket_options,
258+
client_cert=client_cert,
259+
proxies_config=proxies_config,
260+
connector_args=connector_args
261+
)
357262

358263
return AioEndpoint(
359264
endpoint_url,
360265
endpoint_prefix=endpoint_prefix,
361266
event_emitter=self._event_emitter,
362267
response_parser_factory=response_parser_factory,
363-
http_session=aio_session,
364-
proxies=proxies)
268+
http_session=http_session)

0 commit comments

Comments
 (0)