Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/enhancement-HTTP-34569.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "enhancement",
"category": "HTTP",
"description": "Move 100-continue behavior to use `HTTPConnections` request interface."
}
5 changes: 5 additions & 0 deletions .changes/next-release/enhancement-urllib3-48038.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "enhancement",
"category": "urllib3",
"description": "Update urllib3 to version 2.6.3"
}
37 changes: 21 additions & 16 deletions awscli/botocore/awsrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,34 +66,34 @@ class AWSConnection:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._original_response_cls = self.response_class
# We'd ideally hook into httplib's states, but they're all
# __mangled_vars so we use our own state var. This variable is set
# when we receive an early response from the server. If this value is
# set to True, any calls to send() are noops. This value is reset to
# false every time _send_request is called. This is to workaround the
# fact that py2.6 (and only py2.6) has a separate send() call for the
# body in _send_request, as opposed to endheaders(), which is where the
# body is sent in all versions > 2.6.
# This variable is set when we receive an early response from the
# server. If this value is set to True, any calls to send() are noops.
# This value is reset to false every time _send_request is called.
# This is to workaround changes in urllib3 2.0 which uses separate
# send() calls in request() instead of delegating to endheaders(),
# which is where the body is sent in CPython's HTTPConnection.
self._response_received = False
self._expect_header_set = False
self._send_called = False

def close(self):
super().close()
# Reset all of our instance state we were tracking.
self._response_received = False
self._expect_header_set = False
self._send_called = False
self.response_class = self._original_response_cls

def _send_request(self, method, url, body, headers, *args, **kwargs):
def request(self, method, url, body=None, headers=None, *args, **kwargs):
if headers is None:
headers = {}
self._response_received = False
if headers.get('Expect', b'') == b'100-continue':
self._expect_header_set = True
else:
self._expect_header_set = False
self.response_class = self._original_response_cls
rval = super()._send_request(
method, url, body, headers, *args, **kwargs
)
rval = super().request(method, url, body, headers, *args, **kwargs)
self._expect_header_set = False
return rval

Expand Down Expand Up @@ -210,10 +210,15 @@ def _send_message_body(self, message_body):

def send(self, str):
if self._response_received:
logger.debug(
"send() called, but reseponse already received. "
"Not sending data."
)
if not self._send_called:
# urllib3 2.0 chunks and calls send potentially
# thousands of times inside `request` unlike the
# standard library. Only log this once for sanity.
logger.debug(
"send() called, but response already received. "
"Not sending data."
)
self._send_called = True
return
return super().send(str)

Expand Down
16 changes: 13 additions & 3 deletions awscli/botocore/httpsession.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import socket
import sys
from base64 import b64encode
from concurrent.futures import CancelledError

from urllib3 import PoolManager, Timeout, proxy_from_url
from urllib3.exceptions import (
Expand All @@ -17,6 +18,7 @@
)
from urllib3.exceptions import ReadTimeoutError as URLLib3ReadTimeoutError
from urllib3.exceptions import SSLError as URLLib3SSLError
from urllib3.poolmanager import PoolKey
from urllib3.util.retry import Retry
from urllib3.util.ssl_ import (
OP_NO_COMPRESSION,
Expand All @@ -28,8 +30,6 @@
)
from urllib3.util.url import parse_url

from concurrent.futures import CancelledError

try:
from urllib3.util.ssl_ import OP_NO_TICKET, PROTOCOL_TLS_CLIENT
except ImportError:
Expand Down Expand Up @@ -75,6 +75,15 @@
DEFAULT_TIMEOUT = 60
MAX_POOL_CONNECTIONS = 10
DEFAULT_CA_BUNDLE = os.path.join(os.path.dirname(__file__), 'cacert.pem')
BUFFER_SIZE = None
if hasattr(PoolKey, 'key_blocksize'):
# urllib3 2.0 implemented its own chunking logic and set
# a default blocksize of 16KB. This creates a noticeable
# performance bottleneck when transferring objects
# larger than 100MB. Based on experiments, a blocksize
# of 128KB significantly improves throughput before
# getting diminishing returns.
BUFFER_SIZE = 1024 * 128

try:
from certifi import where
Expand Down Expand Up @@ -330,14 +339,15 @@ def _proxies_kwargs(self, **kwargs):

def _get_pool_manager_kwargs(self, **extra_kwargs):
pool_manager_kwargs = {
'strict': True,
'timeout': self._timeout,
'maxsize': self._max_pool_connections,
'ssl_context': self._get_ssl_context(),
'socket_options': self._socket_options,
'cert_file': self._cert_file,
'key_file': self._key_file,
}
if BUFFER_SIZE:
pool_manager_kwargs['blocksize'] = BUFFER_SIZE
pool_manager_kwargs.update(**extra_kwargs)
return pool_manager_kwargs

Expand Down
Empty file added hssyoo.sh
Empty file.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ dependencies = [
"awscrt==0.29.1",
"python-dateutil>=2.1,<=2.9.0",
"jmespath>=0.7.1,<1.1.0",
"urllib3>=1.25.4,<1.27",
"urllib3>=1.25.4,<=2.6.3",
]
dynamic = ["version"]

Expand Down
12 changes: 6 additions & 6 deletions requirements/download-deps/portable-exe-lock.txt
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ pyinstaller==6.11.1 \
--hash=sha256:ddc0fddd75f07f7e423da1f0822e389a42af011f9589e0269b87e0d89aa48c1f \
--hash=sha256:e21c7806e34f40181e7606926a14579f848bfb1dc52cbca7eea66eccccbfe977
# via -r requirements/portable-exe-extras.txt
pyinstaller-hooks-contrib==2025.10 \
--hash=sha256:a1a737e5c0dccf1cf6f19a25e2efd109b9fec9ddd625f97f553dac16ee884881 \
--hash=sha256:aa7a378518772846221f63a84d6306d9827299323243db890851474dfd1231a9
pyinstaller-hooks-contrib==2025.11 \
--hash=sha256:777e163e2942474aa41a8e6d31ac1635292d63422c3646c176d584d04d971c34 \
--hash=sha256:dfe18632e06655fa88d218e0d768fd753e1886465c12a6d4bce04f1aaeec917d
# via pyinstaller
python-dateutil==2.9.0 \
--hash=sha256:78e73e19c63f5b20ffa567001531680d939dc042bf7850431877645523c66709 \
Expand Down Expand Up @@ -164,9 +164,9 @@ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
urllib3==1.26.20 \
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
urllib3==2.6.3 \
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
# via awscli (pyproject.toml)
wcwidth==0.2.14 \
--hash=sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605 \
Expand Down
12 changes: 6 additions & 6 deletions requirements/download-deps/portable-exe-win-lock.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ pyinstaller==6.11.1 \
--hash=sha256:ddc0fddd75f07f7e423da1f0822e389a42af011f9589e0269b87e0d89aa48c1f \
--hash=sha256:e21c7806e34f40181e7606926a14579f848bfb1dc52cbca7eea66eccccbfe977
# via -r D:/a/aws-cli/aws-cli/requirements/portable-exe-extras.txt
pyinstaller-hooks-contrib==2025.10 \
--hash=sha256:a1a737e5c0dccf1cf6f19a25e2efd109b9fec9ddd625f97f553dac16ee884881 \
--hash=sha256:aa7a378518772846221f63a84d6306d9827299323243db890851474dfd1231a9
pyinstaller-hooks-contrib==2025.11 \
--hash=sha256:777e163e2942474aa41a8e6d31ac1635292d63422c3646c176d584d04d971c34 \
--hash=sha256:dfe18632e06655fa88d218e0d768fd753e1886465c12a6d4bce04f1aaeec917d
# via pyinstaller
python-dateutil==2.9.0 \
--hash=sha256:78e73e19c63f5b20ffa567001531680d939dc042bf7850431877645523c66709 \
Expand Down Expand Up @@ -166,9 +166,9 @@ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
urllib3==1.26.20 \
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
urllib3==2.6.3 \
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
# via awscli (D:/a/aws-cli/aws-cli/pyproject.toml)
wcwidth==0.2.14 \
--hash=sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/download-deps/system-sandbox-lock.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
urllib3==1.26.20 \
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
urllib3==2.6.3 \
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
# via awscli (pyproject.toml)
wcwidth==0.2.14 \
--hash=sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605 \
Expand Down
6 changes: 3 additions & 3 deletions requirements/download-deps/system-sandbox-win-lock.txt
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ six==1.17.0 \
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
# via python-dateutil
urllib3==1.26.20 \
--hash=sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e \
--hash=sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32
urllib3==2.6.3 \
--hash=sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed \
--hash=sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4
# via awscli (D:/a/aws-cli/aws-cli/pyproject.toml)
wcwidth==0.2.14 \
--hash=sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605 \
Expand Down
6 changes: 4 additions & 2 deletions tests/unit/botocore/test_http_session.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import socket
from concurrent.futures import CancelledError

import pytest
from botocore.awsrequest import (
Expand All @@ -12,13 +13,13 @@
ProxyConnectionError,
)
from botocore.httpsession import (
BUFFER_SIZE,
ProxyConfiguration,
URLLib3Session,
get_cert_path,
mask_proxy_url,
)
from urllib3.exceptions import NewConnectionError, ProtocolError, ProxyError
from concurrent.futures import CancelledError

from tests import mock, unittest

Expand Down Expand Up @@ -149,14 +150,15 @@ def assert_request_sent(

def _assert_manager_call(self, manager, *assert_args, **assert_kwargs):
call_kwargs = {
'strict': True,
'maxsize': mock.ANY,
'timeout': mock.ANY,
'ssl_context': mock.ANY,
'socket_options': [],
'cert_file': None,
'key_file': None,
}
if BUFFER_SIZE:
call_kwargs['blocksize'] = BUFFER_SIZE
call_kwargs.update(assert_kwargs)
manager.assert_called_with(*assert_args, **call_kwargs)

Expand Down