Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix connecting to npipe://, tcp://, and unix:// urls #8632

Merged
merged 35 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
53d2b3e
Fix connecting to unix:// urls
bdraco Aug 7, 2024
7c01a11
changelog
bdraco Aug 7, 2024
fe4833e
fix "npipe" as well
bdraco Aug 7, 2024
b6cd175
Update CHANGES/8632.bugfix.rst
bdraco Aug 7, 2024
c8ae1f2
Make allowed protocols per connector, allow all in base connector
bdraco Aug 7, 2024
9de9578
Merge remote-tracking branch 'upstream/fix_unix_tcp' into fix_unix_tcp
bdraco Aug 7, 2024
857b6c7
Make allowed protocols per connector, allow all in base connector
bdraco Aug 7, 2024
64292c0
Update aiohttp/connector.py
bdraco Aug 7, 2024
1d79ef2
Make allowed protocols per connector, allow all in base connector
bdraco Aug 7, 2024
d713232
ensure base connector allows all protocols by default
bdraco Aug 7, 2024
83fd6f0
ensure base connector allows all protocols by default
bdraco Aug 7, 2024
9d2fe1a
fixes
bdraco Aug 7, 2024
883c2f6
fixes
bdraco Aug 7, 2024
1b65087
fixes
bdraco Aug 7, 2024
fba214f
type
bdraco Aug 7, 2024
af4cc59
fix tcp:// as well
bdraco Aug 7, 2024
ce827f1
Update CHANGES/8632.bugfix.rst
bdraco Aug 7, 2024
709f824
fix tcp:// as well
bdraco Aug 7, 2024
a0aeb7c
Merge remote-tracking branch 'upstream/fix_unix_tcp' into fix_unix_tcp
bdraco Aug 7, 2024
5967e5a
rename to BASE_PROTOCOL_SCHEMA_SET
bdraco Aug 7, 2024
412e12f
Merge remote-tracking branch 'upstream/master' into fix_unix_tcp
bdraco Aug 7, 2024
71bf4ae
drop now
bdraco Aug 7, 2024
9c95012
drop now
bdraco Aug 7, 2024
ad51c59
Update aiohttp/client.py
Dreamsorcerer Aug 7, 2024
2256884
drop ones that are no longer in the base connector
bdraco Aug 7, 2024
cde13a6
Update aiohttp/connector.py
bdraco Aug 7, 2024
374821b
kiss
bdraco Aug 7, 2024
232d377
preen
bdraco Aug 7, 2024
f7996bf
Update tests/test_client_session.py
bdraco Aug 7, 2024
258eae9
explict unix test
bdraco Aug 7, 2024
4652d6b
Merge remote-tracking branch 'upstream/fix_unix_tcp' into fix_unix_tcp
bdraco Aug 7, 2024
f5d3bb4
Update tests/test_client_session.py
bdraco Aug 7, 2024
83db4a1
fix type
bdraco Aug 7, 2024
5b22c58
Merge remote-tracking branch 'upstream/fix_unix_tcp' into fix_unix_tcp
bdraco Aug 7, 2024
3bbf448
fix type
bdraco Aug 7, 2024
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
1 change: 1 addition & 0 deletions CHANGES/8632.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed connecting to ``npipe://`` and ``unix://`` urls -- by :user:`bdraco`.
bdraco marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 3 additions & 4 deletions aiohttp/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
from .cookiejar import CookieJar
from .helpers import (
_SENTINEL,
HTTP_SCHEMA_SET,
BasicAuth,
TimeoutHandle,
ceil_timeout,
Expand Down Expand Up @@ -210,9 +211,7 @@ class ClientTimeout:

# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2
IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"})
HTTP_SCHEMA_SET = frozenset({"http", "https", ""})
WS_SCHEMA_SET = frozenset({"ws", "wss"})
ALLOWED_PROTOCOL_SCHEMA_SET = HTTP_SCHEMA_SET | WS_SCHEMA_SET

Dreamsorcerer marked this conversation as resolved.
Show resolved Hide resolved

_RetType = TypeVar("_RetType")
_CharsetResolver = Callable[[ClientResponse, bytes], str]
Expand Down Expand Up @@ -455,7 +454,7 @@ async def _request(
except ValueError as e:
raise InvalidUrlClientError(str_or_url) from e

if url.scheme not in ALLOWED_PROTOCOL_SCHEMA_SET:
if url.scheme not in self._connector.allowed_protocol_schema_set:
raise NonHttpUrlClientError(url)

skip_headers = set(self._skip_auto_headers)
Expand Down
39 changes: 37 additions & 2 deletions aiohttp/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
import sys
import traceback
import warnings
from collections import defaultdict, deque
from collections import defaultdict, deque, frozenset
from contextlib import suppress
from functools import cached_property
from http import HTTPStatus
from http.cookies import SimpleCookie
from itertools import cycle, islice
Expand Down Expand Up @@ -50,7 +51,18 @@
)
from .client_proto import ResponseHandler
from .client_reqrep import SSL_ALLOWED_TYPES, ClientRequest, Fingerprint
from .helpers import _SENTINEL, ceil_timeout, is_ip_address, sentinel, set_result
from .helpers import (
_SENTINEL,
ALLOWED_PROTOCOL_SCHEMA_SET,
HTTP_SCHEMA_SET,
NAMED_PIPE_PROTOCOL_SCHEMA_SET,
UNIX_PROTCOL_SCHEMA_SET,
WS_SCHEMA_SET,
ceil_timeout,
is_ip_address,
sentinel,
set_result,
)
from .locks import EventResultOrError
from .resolver import DefaultResolver

Expand Down Expand Up @@ -243,6 +255,14 @@ def __init__(
self._cleanup_closed_transports: List[Optional[asyncio.Transport]] = []
self._cleanup_closed()

@cached_property
def allowed_protocol_schema_set(self) -> frozenset[str]:
"""Return allowed protocol schema set.

By default we allow all protocols.
bdraco marked this conversation as resolved.
Show resolved Hide resolved
"""
return ALLOWED_PROTOCOL_SCHEMA_SET
Dreamsorcerer marked this conversation as resolved.
Show resolved Hide resolved

def __del__(self, _warnings: Any = warnings) -> None:
if self._closed:
return
Expand Down Expand Up @@ -790,6 +810,11 @@ def _close_immediately(self) -> List["asyncio.Future[None]"]:
ev.cancel()
return super()._close_immediately()

@cached_property
def allowed_protocol_schema_set(self) -> frozenset[str]:
"""Return allowed protocol schema set."""
return HTTP_SCHEMA_SET | WS_SCHEMA_SET

@property
def family(self) -> int:
"""Socket family like AF_INET."""
Expand Down Expand Up @@ -1357,6 +1382,11 @@ def __init__(
)
self._path = path

@cached_property
def allowed_protocol_schema_set(self) -> frozenset[str]:
"""Return allowed protocol schema set."""
return UNIX_PROTCOL_SCHEMA_SET

@property
def path(self) -> str:
"""Path to unix socket."""
Expand Down Expand Up @@ -1417,6 +1447,11 @@ def __init__(
)
self._path = path

@cached_property
def allowed_protocol_schema_set(self) -> frozenset[str]:
"""Return allowed protocol schema set."""
return NAMED_PIPE_PROTOCOL_SCHEMA_SET

@property
def path(self) -> str:
"""Path to the named pipe."""
Expand Down
11 changes: 11 additions & 0 deletions aiohttp/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,17 @@
}
TOKEN = CHAR ^ CTL ^ SEPARATORS

HTTP_SCHEMA_SET = frozenset({"http", "https", ""})
WS_SCHEMA_SET = frozenset({"ws", "wss"})
UNIX_PROTCOL_SCHEMA_SET = frozenset({"unix"})
NAMED_PIPE_PROTOCOL_SCHEMA_SET = frozenset({"npipe"})
ALLOWED_PROTOCOL_SCHEMA_SET = (
HTTP_SCHEMA_SET
| WS_SCHEMA_SET
| UNIX_PROTCOL_SCHEMA_SET
| NAMED_PIPE_PROTOCOL_SCHEMA_SET
)


json_re = re.compile(r"(?:application/|[\w.-]+/[\w.+-]+?\+)json$", re.IGNORECASE)

Expand Down
6 changes: 3 additions & 3 deletions tests/test_client_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ async def create_connection(req, traces, timeout):
c.__del__()
Dismissed Show dismissed Hide dismissed


@pytest.mark.parametrize("protocol", ["http", "https", "ws", "wss"])
@pytest.mark.parametrize("protocol", ["http", "https", "npipe", "ws", "wss", "unix"])
async def test_ws_connect_allowed_protocols(
create_session: Any,
create_mocked_conn: Any,
Expand All @@ -482,7 +482,7 @@ async def test_ws_connect_allowed_protocols(
hdrs.CONNECTION: "upgrade",
hdrs.SEC_WEBSOCKET_ACCEPT: ws_key,
}
resp.url = URL(f"{protocol}://example.com")
resp.url = URL(f"{protocol}://example")
resp.cookies = SimpleCookie()
resp.start = mock.AsyncMock()

Expand Down Expand Up @@ -510,7 +510,7 @@ async def create_connection(req, traces, timeout):
"aiohttp.client.os"
) as m_os:
m_os.urandom.return_value = key_data
await session.ws_connect(f"{protocol}://example.com")
await session.ws_connect(f"{protocol}://example")

# normally called during garbage collection. triggers an exception
# if the connection wasn't already closed
Expand Down