python-socks 2.8.2__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_socks/__init__.py +11 -13
- python_socks/_abc.py +35 -22
- python_socks/_connectors/abc.py +5 -3
- python_socks/_connectors/factory_async.py +8 -8
- python_socks/_connectors/factory_sync.py +8 -8
- python_socks/_connectors/http_async.py +7 -7
- python_socks/_connectors/http_sync.py +7 -7
- python_socks/_connectors/socks4_async.py +10 -10
- python_socks/_connectors/socks4_sync.py +10 -10
- python_socks/_connectors/socks5_async.py +29 -26
- python_socks/_connectors/socks5_sync.py +36 -28
- python_socks/_errors.py +15 -2
- python_socks/_helpers.py +35 -32
- python_socks/_protocols/errors.py +10 -1
- python_socks/_protocols/http.py +55 -60
- python_socks/_protocols/socks4.py +22 -26
- python_socks/_protocols/socks5.py +77 -70
- python_socks/_types.py +4 -0
- python_socks/_version.py +2 -2
- python_socks/async_/__init__.py +0 -3
- python_socks/async_/anyio/__init__.py +1 -2
- python_socks/async_/anyio/_connect.py +6 -4
- python_socks/async_/anyio/_proxy.py +85 -92
- python_socks/async_/anyio/_resolver.py +10 -3
- python_socks/async_/anyio/_stream.py +11 -26
- python_socks/async_/anyio/v2/__init__.py +1 -5
- python_socks/async_/anyio/v2/_connect.py +7 -4
- python_socks/async_/anyio/v2/_proxy.py +36 -26
- python_socks/async_/anyio/v2/_stream.py +11 -11
- python_socks/async_/asyncio/__init__.py +1 -2
- python_socks/async_/asyncio/_connect.py +12 -7
- python_socks/async_/asyncio/_proxy.py +48 -49
- python_socks/async_/asyncio/_resolver.py +11 -4
- python_socks/async_/asyncio/_stream.py +9 -13
- python_socks/async_/asyncio/v2/__init__.py +1 -2
- python_socks/async_/asyncio/v2/_connect.py +8 -5
- python_socks/async_/asyncio/v2/_proxy.py +38 -28
- python_socks/async_/asyncio/v2/_stream.py +16 -18
- python_socks/async_/trio/__init__.py +1 -1
- python_socks/async_/trio/_connect.py +5 -4
- python_socks/async_/trio/_proxy.py +46 -48
- python_socks/async_/trio/_resolver.py +11 -3
- python_socks/async_/trio/_stream.py +9 -9
- python_socks/async_/trio/v2/__init__.py +1 -5
- python_socks/async_/trio/v2/_connect.py +3 -2
- python_socks/async_/trio/v2/_proxy.py +34 -24
- python_socks/async_/trio/v2/_stream.py +13 -13
- python_socks/sync/__init__.py +1 -3
- python_socks/sync/_connect.py +4 -3
- python_socks/sync/_proxy.py +42 -43
- python_socks/sync/_resolver.py +17 -5
- python_socks/sync/_stream.py +9 -9
- python_socks/sync/v2/__init__.py +1 -5
- python_socks/sync/v2/_connect.py +5 -3
- python_socks/sync/v2/_proxy.py +35 -26
- python_socks/sync/v2/_ssl_transport.py +3 -0
- python_socks/sync/v2/_stream.py +19 -14
- python_socks-3.0.0.dist-info/METADATA +346 -0
- python_socks-3.0.0.dist-info/RECORD +65 -0
- {python_socks-2.8.2.dist-info → python_socks-3.0.0.dist-info}/WHEEL +1 -1
- python_socks/async_/_proxy_chain.py +0 -34
- python_socks/async_/anyio/_chain.py +0 -42
- python_socks/async_/anyio/v2/_chain.py +0 -32
- python_socks/async_/asyncio/v2/_chain.py +0 -32
- python_socks/async_/curio/__init__.py +0 -4
- python_socks/async_/curio/_connect.py +0 -17
- python_socks/async_/curio/_proxy.py +0 -132
- python_socks/async_/curio/_resolver.py +0 -25
- python_socks/async_/curio/_stream.py +0 -32
- python_socks/async_/trio/v2/_chain.py +0 -32
- python_socks/sync/_chain.py +0 -32
- python_socks/sync/v2/_chain.py +0 -26
- python_socks-2.8.2.dist-info/METADATA +0 -295
- python_socks-2.8.2.dist-info/RECORD +0 -77
- {python_socks-2.8.2.dist-info → python_socks-3.0.0.dist-info}/licenses/LICENSE.txt +0 -0
- {python_socks-2.8.2.dist-info → python_socks-3.0.0.dist-info}/top_level.txt +0 -0
|
@@ -1,23 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import asyncio
|
|
2
4
|
import ssl
|
|
3
|
-
from typing import Any, Optional, Tuple
|
|
4
|
-
import warnings
|
|
5
5
|
import sys
|
|
6
|
+
import warnings
|
|
7
|
+
from typing import Any
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
from ....
|
|
9
|
+
from ...._connectors.factory_async import create_connector
|
|
10
|
+
from ...._errors import (
|
|
11
|
+
IncompleteReadError,
|
|
12
|
+
ProxyConnectionError,
|
|
13
|
+
ProxyError,
|
|
14
|
+
ProxyTimeoutError,
|
|
15
|
+
)
|
|
9
16
|
from ...._helpers import parse_proxy_url
|
|
10
|
-
from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
|
|
11
|
-
|
|
12
17
|
from ...._protocols.errors import ReplyError
|
|
13
|
-
from ....
|
|
14
|
-
|
|
18
|
+
from ...._types import ProxyType
|
|
15
19
|
from .._resolver import Resolver
|
|
16
|
-
from ._stream import AsyncioSocketStream
|
|
17
20
|
from ._connect import connect_tcp
|
|
21
|
+
from ._stream import AsyncioSocketStream
|
|
18
22
|
|
|
19
23
|
if sys.version_info >= (3, 11):
|
|
20
|
-
import asyncio as async_timeout
|
|
24
|
+
import asyncio as async_timeout
|
|
21
25
|
else:
|
|
22
26
|
import async_timeout
|
|
23
27
|
|
|
@@ -26,21 +30,22 @@ DEFAULT_TIMEOUT = 60
|
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
class AsyncioProxy:
|
|
29
|
-
def __init__(
|
|
33
|
+
def __init__( # noqa: PLR0913, PLR0917
|
|
30
34
|
self,
|
|
31
35
|
proxy_type: ProxyType,
|
|
32
36
|
host: str,
|
|
33
37
|
port: int,
|
|
34
|
-
username:
|
|
35
|
-
password:
|
|
36
|
-
rdns:
|
|
37
|
-
proxy_ssl:
|
|
38
|
-
forward:
|
|
39
|
-
loop:
|
|
40
|
-
):
|
|
38
|
+
username: str | None = None,
|
|
39
|
+
password: str | None = None,
|
|
40
|
+
rdns: bool | None = None, # noqa: FBT001
|
|
41
|
+
proxy_ssl: ssl.SSLContext | None = None,
|
|
42
|
+
forward: AsyncioProxy | None = None,
|
|
43
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
44
|
+
) -> None:
|
|
41
45
|
if loop is not None: # pragma: no cover
|
|
42
46
|
warnings.warn(
|
|
43
|
-
|
|
47
|
+
"The loop argument is deprecated "
|
|
48
|
+
"and scheduled for removal in the future.",
|
|
44
49
|
DeprecationWarning,
|
|
45
50
|
stacklevel=2,
|
|
46
51
|
)
|
|
@@ -66,14 +71,14 @@ class AsyncioProxy:
|
|
|
66
71
|
self,
|
|
67
72
|
dest_host: str,
|
|
68
73
|
dest_port: int,
|
|
69
|
-
dest_ssl:
|
|
70
|
-
timeout:
|
|
74
|
+
dest_ssl: ssl.SSLContext | None = None,
|
|
75
|
+
timeout: float | None = None,
|
|
71
76
|
**kwargs: Any,
|
|
72
77
|
) -> AsyncioSocketStream:
|
|
73
78
|
if timeout is None:
|
|
74
79
|
timeout = DEFAULT_TIMEOUT
|
|
75
80
|
|
|
76
|
-
local_addr = kwargs.get(
|
|
81
|
+
local_addr = kwargs.get("local_addr")
|
|
77
82
|
try:
|
|
78
83
|
async with async_timeout.timeout(timeout):
|
|
79
84
|
return await self._connect(
|
|
@@ -83,14 +88,14 @@ class AsyncioProxy:
|
|
|
83
88
|
local_addr=local_addr,
|
|
84
89
|
)
|
|
85
90
|
except asyncio.TimeoutError as e:
|
|
86
|
-
raise ProxyTimeoutError(
|
|
91
|
+
raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
|
|
87
92
|
|
|
88
93
|
async def _connect(
|
|
89
94
|
self,
|
|
90
95
|
dest_host: str,
|
|
91
96
|
dest_port: int,
|
|
92
|
-
dest_ssl:
|
|
93
|
-
local_addr:
|
|
97
|
+
dest_ssl: ssl.SSLContext | None = None,
|
|
98
|
+
local_addr: tuple[str, int] | None = None,
|
|
94
99
|
) -> AsyncioSocketStream:
|
|
95
100
|
if self._forward is None:
|
|
96
101
|
try:
|
|
@@ -140,7 +145,10 @@ class AsyncioProxy:
|
|
|
140
145
|
)
|
|
141
146
|
except ReplyError as e:
|
|
142
147
|
await stream.close()
|
|
143
|
-
raise ProxyError(e, error_code=e.error_code)
|
|
148
|
+
raise ProxyError(e, error_code=e.error_code) from e
|
|
149
|
+
except IncompleteReadError as e:
|
|
150
|
+
await stream.close()
|
|
151
|
+
raise ProxyError(e) from e
|
|
144
152
|
except (asyncio.CancelledError, Exception):
|
|
145
153
|
await stream.close()
|
|
146
154
|
raise
|
|
@@ -148,10 +156,12 @@ class AsyncioProxy:
|
|
|
148
156
|
return stream
|
|
149
157
|
|
|
150
158
|
@classmethod
|
|
151
|
-
def create(
|
|
159
|
+
def create(
|
|
160
|
+
cls, *args: Any, **kwargs: Any
|
|
161
|
+
) -> AsyncioProxy: # for backward compatibility
|
|
152
162
|
return cls(*args, **kwargs)
|
|
153
163
|
|
|
154
164
|
@classmethod
|
|
155
|
-
def from_url(cls, url: str, **kwargs) ->
|
|
165
|
+
def from_url(cls, url: str, **kwargs: Any) -> AsyncioProxy:
|
|
156
166
|
url_args = parse_proxy_url(url)
|
|
157
167
|
return cls(*url_args, **kwargs)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import asyncio
|
|
2
4
|
import ssl
|
|
3
5
|
|
|
@@ -7,37 +9,33 @@ DEFAULT_RECEIVE_SIZE = 65536
|
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class AsyncioSocketStream(abc.AsyncSocketStream):
|
|
10
|
-
_loop: asyncio.AbstractEventLoop
|
|
11
|
-
_reader: asyncio.StreamReader
|
|
12
|
-
_writer: asyncio.StreamWriter
|
|
13
|
-
|
|
14
12
|
def __init__(
|
|
15
13
|
self,
|
|
16
14
|
loop: asyncio.AbstractEventLoop,
|
|
17
15
|
reader: asyncio.StreamReader,
|
|
18
16
|
writer: asyncio.StreamWriter,
|
|
19
|
-
):
|
|
17
|
+
) -> None:
|
|
20
18
|
self._loop = loop
|
|
21
19
|
self._reader = reader
|
|
22
20
|
self._writer = writer
|
|
23
21
|
|
|
24
|
-
async def
|
|
22
|
+
async def write(self, data: bytes) -> None:
|
|
25
23
|
self._writer.write(data)
|
|
26
24
|
await self._writer.drain()
|
|
27
25
|
|
|
28
|
-
async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
|
|
26
|
+
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
|
|
29
27
|
return await self._reader.read(max_bytes)
|
|
30
28
|
|
|
31
|
-
async def
|
|
29
|
+
async def read_exactly(self, n: int) -> bytes:
|
|
32
30
|
return await self._reader.readexactly(n)
|
|
33
31
|
|
|
34
32
|
async def start_tls(
|
|
35
33
|
self,
|
|
36
34
|
hostname: str,
|
|
37
35
|
ssl_context: ssl.SSLContext,
|
|
38
|
-
ssl_handshake_timeout=None,
|
|
39
|
-
) ->
|
|
40
|
-
if hasattr(self._writer,
|
|
36
|
+
ssl_handshake_timeout: float | None = None,
|
|
37
|
+
) -> AsyncioSocketStream:
|
|
38
|
+
if hasattr(self._writer, "start_tls"): # Python>=3.11
|
|
41
39
|
await self._writer.start_tls(
|
|
42
40
|
ssl_context,
|
|
43
41
|
server_hostname=hostname,
|
|
@@ -49,7 +47,7 @@ class AsyncioSocketStream(abc.AsyncSocketStream):
|
|
|
49
47
|
protocol = asyncio.StreamReaderProtocol(reader)
|
|
50
48
|
|
|
51
49
|
transport: asyncio.Transport = await self._loop.start_tls(
|
|
52
|
-
self._writer.transport, # type: ignore
|
|
50
|
+
self._writer.transport, # type: ignore[assignment]
|
|
53
51
|
protocol,
|
|
54
52
|
ssl_context,
|
|
55
53
|
server_side=False,
|
|
@@ -61,7 +59,7 @@ class AsyncioSocketStream(abc.AsyncSocketStream):
|
|
|
61
59
|
|
|
62
60
|
# Initialize the protocol, so it is made aware of being tied to
|
|
63
61
|
# a TLS connection.
|
|
64
|
-
# See: https://github.com/encode/httpx/issues/859
|
|
62
|
+
# See also: https://github.com/encode/httpx/issues/859
|
|
65
63
|
protocol.connection_made(transport)
|
|
66
64
|
|
|
67
65
|
writer = asyncio.StreamWriter(
|
|
@@ -75,17 +73,17 @@ class AsyncioSocketStream(abc.AsyncSocketStream):
|
|
|
75
73
|
# When we return a new SocketStream with new StreamReader/StreamWriter instances
|
|
76
74
|
# we need to keep references to the old StreamReader/StreamWriter so that they
|
|
77
75
|
# are not garbage collected and closed while we're still using them.
|
|
78
|
-
stream._inner = self # type: ignore
|
|
76
|
+
stream._inner = self # type: ignore[attr-defined]
|
|
79
77
|
return stream
|
|
80
78
|
|
|
81
|
-
async def close(self):
|
|
79
|
+
async def close(self) -> None:
|
|
82
80
|
self._writer.close()
|
|
83
|
-
self._writer.transport.abort()
|
|
81
|
+
self._writer.transport.abort()
|
|
84
82
|
|
|
85
83
|
@property
|
|
86
|
-
def reader(self):
|
|
84
|
+
def reader(self) -> asyncio.StreamReader:
|
|
87
85
|
return self._reader # pragma: no cover
|
|
88
86
|
|
|
89
87
|
@property
|
|
90
|
-
def writer(self):
|
|
88
|
+
def writer(self) -> asyncio.StreamWriter:
|
|
91
89
|
return self._writer # pragma: no cover
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import trio
|
|
4
4
|
|
|
5
|
-
from ._resolver import Resolver
|
|
6
5
|
from ..._helpers import is_ipv4_address, is_ipv6_address
|
|
6
|
+
from ..._types import ResolvedAddress
|
|
7
|
+
from ._resolver import Resolver
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
async def connect_tcp(
|
|
10
11
|
host: str,
|
|
11
12
|
port: int,
|
|
12
|
-
local_addr:
|
|
13
|
+
local_addr: tuple[str, int] | None = None,
|
|
13
14
|
) -> trio.socket.SocketType:
|
|
14
15
|
|
|
15
16
|
family, host = await _resolve_host(host)
|
|
@@ -26,7 +27,7 @@ async def connect_tcp(
|
|
|
26
27
|
return sock
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
async def _resolve_host(host):
|
|
30
|
+
async def _resolve_host(host: str) -> ResolvedAddress:
|
|
30
31
|
if is_ipv4_address(host):
|
|
31
32
|
return trio.socket.AF_INET, host
|
|
32
33
|
if is_ipv6_address(host):
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
from
|
|
2
|
-
import warnings
|
|
3
|
-
import trio
|
|
1
|
+
from __future__ import annotations
|
|
4
2
|
|
|
5
|
-
from
|
|
6
|
-
from ..._helpers import parse_proxy_url
|
|
7
|
-
from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
|
|
3
|
+
from typing import Any
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
from ._resolver import Resolver
|
|
11
|
-
from ._connect import connect_tcp
|
|
5
|
+
import trio
|
|
12
6
|
|
|
13
|
-
from ..._protocols.errors import ReplyError
|
|
14
7
|
from ..._connectors.factory_async import create_connector
|
|
15
|
-
|
|
8
|
+
from ..._errors import (
|
|
9
|
+
IncompleteReadError,
|
|
10
|
+
ProxyConnectionError,
|
|
11
|
+
ProxyError,
|
|
12
|
+
ProxyTimeoutError,
|
|
13
|
+
)
|
|
14
|
+
from ..._helpers import parse_proxy_url
|
|
15
|
+
from ..._protocols.errors import ReplyError
|
|
16
|
+
from ..._types import ProxyType
|
|
17
|
+
from ._connect import connect_tcp
|
|
18
|
+
from ._resolver import Resolver
|
|
19
|
+
from ._stream import TrioSocketStream
|
|
16
20
|
|
|
17
21
|
DEFAULT_TIMEOUT = 60
|
|
18
22
|
|
|
@@ -23,16 +27,18 @@ class TrioProxy:
|
|
|
23
27
|
proxy_type: ProxyType,
|
|
24
28
|
host: str,
|
|
25
29
|
port: int,
|
|
26
|
-
username:
|
|
27
|
-
password:
|
|
28
|
-
rdns:
|
|
29
|
-
|
|
30
|
+
username: str | None = None,
|
|
31
|
+
password: str | None = None,
|
|
32
|
+
rdns: bool | None = None, # noqa: FBT001
|
|
33
|
+
forward: TrioProxy | None = None,
|
|
34
|
+
) -> None:
|
|
30
35
|
self._proxy_type = proxy_type
|
|
31
36
|
self._proxy_host = host
|
|
32
37
|
self._proxy_port = port
|
|
33
38
|
self._password = password
|
|
34
39
|
self._username = username
|
|
35
40
|
self._rdns = rdns
|
|
41
|
+
self._forward = forward
|
|
36
42
|
|
|
37
43
|
self._resolver = Resolver()
|
|
38
44
|
|
|
@@ -40,55 +46,50 @@ class TrioProxy:
|
|
|
40
46
|
self,
|
|
41
47
|
dest_host: str,
|
|
42
48
|
dest_port: int,
|
|
43
|
-
timeout:
|
|
49
|
+
timeout: float | None = None,
|
|
44
50
|
**kwargs: Any,
|
|
45
51
|
) -> trio.socket.SocketType:
|
|
46
52
|
if timeout is None:
|
|
47
53
|
timeout = DEFAULT_TIMEOUT
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
if _socket is not None:
|
|
51
|
-
warnings.warn(
|
|
52
|
-
"The '_socket' argument is deprecated and will be removed in the future",
|
|
53
|
-
DeprecationWarning,
|
|
54
|
-
stacklevel=2,
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
local_addr = kwargs.get('local_addr')
|
|
55
|
+
local_addr = kwargs.get("local_addr")
|
|
58
56
|
try:
|
|
59
57
|
with trio.fail_after(timeout):
|
|
60
58
|
return await self._connect(
|
|
61
59
|
dest_host=dest_host,
|
|
62
60
|
dest_port=dest_port,
|
|
63
|
-
_socket=_socket,
|
|
64
61
|
local_addr=local_addr,
|
|
65
62
|
)
|
|
66
63
|
except trio.TooSlowError as e:
|
|
67
|
-
raise ProxyTimeoutError(
|
|
64
|
+
raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
|
|
68
65
|
|
|
69
66
|
async def _connect(
|
|
70
67
|
self,
|
|
71
68
|
dest_host: str,
|
|
72
69
|
dest_port: int,
|
|
73
|
-
|
|
74
|
-
local_addr=None,
|
|
70
|
+
local_addr: tuple[str, int] | None = None,
|
|
75
71
|
) -> trio.socket.SocketType:
|
|
76
|
-
if
|
|
72
|
+
if self._forward is not None:
|
|
73
|
+
sock = await self._forward.connect(
|
|
74
|
+
dest_host=self._proxy_host,
|
|
75
|
+
dest_port=self._proxy_port,
|
|
76
|
+
timeout=None,
|
|
77
|
+
)
|
|
78
|
+
else:
|
|
77
79
|
try:
|
|
78
|
-
|
|
80
|
+
sock = await connect_tcp(
|
|
79
81
|
host=self._proxy_host,
|
|
80
82
|
port=self._proxy_port,
|
|
81
83
|
local_addr=local_addr,
|
|
82
84
|
)
|
|
83
85
|
except OSError as e:
|
|
84
|
-
msg =
|
|
85
|
-
|
|
86
|
-
self._proxy_port
|
|
87
|
-
e.strerror,
|
|
86
|
+
msg = (
|
|
87
|
+
f"Could not connect to proxy "
|
|
88
|
+
f"{self._proxy_host}:{self._proxy_port} [{e.strerror}]"
|
|
88
89
|
)
|
|
89
90
|
raise ProxyConnectionError(e.errno, msg) from e
|
|
90
91
|
|
|
91
|
-
stream = TrioSocketStream(sock=
|
|
92
|
+
stream = TrioSocketStream(sock=sock)
|
|
92
93
|
|
|
93
94
|
try:
|
|
94
95
|
connector = create_connector(
|
|
@@ -103,29 +104,26 @@ class TrioProxy:
|
|
|
103
104
|
host=dest_host,
|
|
104
105
|
port=dest_port,
|
|
105
106
|
)
|
|
106
|
-
return
|
|
107
|
+
return sock
|
|
107
108
|
|
|
108
109
|
except ReplyError as e:
|
|
109
110
|
await stream.close()
|
|
110
|
-
raise ProxyError(e, error_code=e.error_code)
|
|
111
|
+
raise ProxyError(e, error_code=e.error_code) from e
|
|
112
|
+
except IncompleteReadError as e:
|
|
113
|
+
await stream.close()
|
|
114
|
+
raise ProxyError(e) from e
|
|
111
115
|
except BaseException: # trio.Cancelled...
|
|
112
116
|
with trio.CancelScope(shield=True):
|
|
113
117
|
await stream.close()
|
|
114
118
|
raise
|
|
115
119
|
|
|
116
|
-
@property
|
|
117
|
-
def proxy_host(self):
|
|
118
|
-
return self._proxy_host
|
|
119
|
-
|
|
120
|
-
@property
|
|
121
|
-
def proxy_port(self):
|
|
122
|
-
return self._proxy_port
|
|
123
|
-
|
|
124
120
|
@classmethod
|
|
125
|
-
def create(
|
|
121
|
+
def create(
|
|
122
|
+
cls, *args: Any, **kwargs: Any
|
|
123
|
+
) -> TrioProxy: # for backward compatibility
|
|
126
124
|
return cls(*args, **kwargs)
|
|
127
125
|
|
|
128
126
|
@classmethod
|
|
129
|
-
def from_url(cls, url: str, **kwargs) ->
|
|
127
|
+
def from_url(cls, url: str, **kwargs: Any) -> TrioProxy:
|
|
130
128
|
url_args = parse_proxy_url(url)
|
|
131
129
|
return cls(*url_args, **kwargs)
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
|
|
1
3
|
import trio
|
|
2
4
|
|
|
3
5
|
from ... import _abc as abc
|
|
6
|
+
from ..._types import ResolvedAddress
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
class Resolver(abc.AsyncResolver):
|
|
7
|
-
async def resolve(
|
|
10
|
+
async def resolve(
|
|
11
|
+
self,
|
|
12
|
+
host: str,
|
|
13
|
+
port: int = 0,
|
|
14
|
+
family: socket.AddressFamily = trio.socket.AF_UNSPEC,
|
|
15
|
+
) -> ResolvedAddress:
|
|
8
16
|
infos = await trio.socket.getaddrinfo(
|
|
9
17
|
host=host,
|
|
10
18
|
port=port,
|
|
@@ -13,9 +21,9 @@ class Resolver(abc.AsyncResolver):
|
|
|
13
21
|
)
|
|
14
22
|
|
|
15
23
|
if not infos: # pragma: no cover
|
|
16
|
-
raise OSError(
|
|
24
|
+
raise OSError(f"Can`t resolve address {host}:{port} [{family}]")
|
|
17
25
|
|
|
18
26
|
infos = sorted(infos, key=lambda info: info[0])
|
|
19
27
|
|
|
20
28
|
family, _, _, _, address = infos[0]
|
|
21
|
-
return family, address[0]
|
|
29
|
+
return family, address[0] # type:ignore[return-value]
|
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
import trio
|
|
2
2
|
|
|
3
|
-
from ..._errors import ProxyError
|
|
4
3
|
from ... import _abc as abc
|
|
4
|
+
from ..._errors import IncompleteReadError
|
|
5
5
|
|
|
6
6
|
DEFAULT_RECEIVE_SIZE = 65536
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class TrioSocketStream(abc.AsyncSocketStream):
|
|
10
|
-
def __init__(self, sock):
|
|
10
|
+
def __init__(self, sock: trio.socket.SocketType) -> None:
|
|
11
11
|
self._socket = sock
|
|
12
12
|
|
|
13
|
-
async def
|
|
13
|
+
async def write(self, data: bytes) -> None:
|
|
14
14
|
total_sent = 0
|
|
15
15
|
while total_sent < len(data):
|
|
16
16
|
remaining = data[total_sent:]
|
|
17
17
|
sent = await self._socket.send(remaining)
|
|
18
18
|
total_sent += sent
|
|
19
19
|
|
|
20
|
-
async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
|
|
20
|
+
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
|
|
21
21
|
return await self._socket.recv(max_bytes)
|
|
22
22
|
|
|
23
|
-
async def
|
|
23
|
+
async def read_exactly(self, n: int) -> bytes:
|
|
24
24
|
data = bytearray()
|
|
25
25
|
while len(data) < n:
|
|
26
26
|
packet = await self._socket.recv(n - len(data))
|
|
27
27
|
if not packet: # pragma: no cover
|
|
28
|
-
raise
|
|
29
|
-
data
|
|
30
|
-
return data
|
|
28
|
+
raise IncompleteReadError("Connection closed unexpectedly")
|
|
29
|
+
data.extend(packet)
|
|
30
|
+
return data # type:ignore[return-value]
|
|
31
31
|
|
|
32
|
-
async def close(self):
|
|
32
|
+
async def close(self) -> None:
|
|
33
33
|
if self._socket is not None:
|
|
34
34
|
self._socket.close()
|
|
35
35
|
await trio.lowlevel.checkpoint()
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import trio
|
|
4
|
+
|
|
4
5
|
from ._stream import TrioSocketStream
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
async def connect_tcp(
|
|
8
9
|
host: str,
|
|
9
10
|
port: int,
|
|
10
|
-
local_addr:
|
|
11
|
+
local_addr: str | None = None,
|
|
11
12
|
) -> TrioSocketStream:
|
|
12
13
|
trio_stream = await trio.open_tcp_stream(
|
|
13
14
|
host=host,
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import ssl
|
|
2
|
-
from typing import Any
|
|
4
|
+
from typing import Any
|
|
3
5
|
|
|
4
6
|
import trio
|
|
5
7
|
|
|
6
|
-
from .
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
from ...._connectors.factory_async import create_connector
|
|
9
|
+
from ...._errors import (
|
|
10
|
+
IncompleteReadError,
|
|
11
|
+
ProxyConnectionError,
|
|
12
|
+
ProxyError,
|
|
13
|
+
ProxyTimeoutError,
|
|
14
|
+
)
|
|
11
15
|
from ...._helpers import parse_proxy_url
|
|
12
|
-
from ...._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
|
|
13
|
-
|
|
14
16
|
from ...._protocols.errors import ReplyError
|
|
15
|
-
from ....
|
|
17
|
+
from ...._types import ProxyType
|
|
18
|
+
from .._resolver import Resolver
|
|
19
|
+
from ._connect import connect_tcp
|
|
20
|
+
from ._stream import TrioSocketStream
|
|
16
21
|
|
|
17
22
|
DEFAULT_TIMEOUT = 60
|
|
18
23
|
|
|
@@ -23,12 +28,12 @@ class TrioProxy:
|
|
|
23
28
|
proxy_type: ProxyType,
|
|
24
29
|
host: str,
|
|
25
30
|
port: int,
|
|
26
|
-
username:
|
|
27
|
-
password:
|
|
28
|
-
rdns:
|
|
29
|
-
proxy_ssl:
|
|
30
|
-
forward:
|
|
31
|
-
):
|
|
31
|
+
username: str | None = None,
|
|
32
|
+
password: str | None = None,
|
|
33
|
+
rdns: bool | None = None, # noqa: FBT001
|
|
34
|
+
proxy_ssl: ssl.SSLContext | None = None,
|
|
35
|
+
forward: TrioProxy | None = None,
|
|
36
|
+
) -> None:
|
|
32
37
|
self._proxy_type = proxy_type
|
|
33
38
|
self._proxy_host = host
|
|
34
39
|
self._proxy_port = port
|
|
@@ -45,14 +50,14 @@ class TrioProxy:
|
|
|
45
50
|
self,
|
|
46
51
|
dest_host: str,
|
|
47
52
|
dest_port: int,
|
|
48
|
-
dest_ssl:
|
|
49
|
-
timeout:
|
|
53
|
+
dest_ssl: ssl.SSLContext | None = None,
|
|
54
|
+
timeout: float | None = None,
|
|
50
55
|
**kwargs: Any,
|
|
51
56
|
) -> TrioSocketStream:
|
|
52
57
|
if timeout is None:
|
|
53
58
|
timeout = DEFAULT_TIMEOUT
|
|
54
59
|
|
|
55
|
-
local_addr = kwargs.get(
|
|
60
|
+
local_addr = kwargs.get("local_addr")
|
|
56
61
|
try:
|
|
57
62
|
with trio.fail_after(timeout):
|
|
58
63
|
return await self._connect(
|
|
@@ -62,14 +67,14 @@ class TrioProxy:
|
|
|
62
67
|
local_addr=local_addr,
|
|
63
68
|
)
|
|
64
69
|
except trio.TooSlowError as e:
|
|
65
|
-
raise ProxyTimeoutError(f
|
|
70
|
+
raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
|
|
66
71
|
|
|
67
72
|
async def _connect(
|
|
68
73
|
self,
|
|
69
74
|
dest_host: str,
|
|
70
75
|
dest_port: int,
|
|
71
|
-
dest_ssl:
|
|
72
|
-
local_addr:
|
|
76
|
+
dest_ssl: ssl.SSLContext | None = None,
|
|
77
|
+
local_addr: str | None = None,
|
|
73
78
|
) -> TrioSocketStream:
|
|
74
79
|
if self._forward is None:
|
|
75
80
|
try:
|
|
@@ -117,7 +122,10 @@ class TrioProxy:
|
|
|
117
122
|
)
|
|
118
123
|
except ReplyError as e:
|
|
119
124
|
await stream.close()
|
|
120
|
-
raise ProxyError(e, error_code=e.error_code)
|
|
125
|
+
raise ProxyError(e, error_code=e.error_code) from e
|
|
126
|
+
except IncompleteReadError as e:
|
|
127
|
+
await stream.close()
|
|
128
|
+
raise ProxyError(e) from e
|
|
121
129
|
except BaseException: # trio.Cancelled...
|
|
122
130
|
with trio.CancelScope(shield=True):
|
|
123
131
|
await stream.close()
|
|
@@ -126,10 +134,12 @@ class TrioProxy:
|
|
|
126
134
|
return stream
|
|
127
135
|
|
|
128
136
|
@classmethod
|
|
129
|
-
def create(
|
|
137
|
+
def create(
|
|
138
|
+
cls, *args: Any, **kwargs: Any
|
|
139
|
+
) -> TrioProxy: # for backward compatibility
|
|
130
140
|
return cls(*args, **kwargs)
|
|
131
141
|
|
|
132
142
|
@classmethod
|
|
133
|
-
def from_url(cls, url: str, **kwargs) ->
|
|
143
|
+
def from_url(cls, url: str, **kwargs: Any) -> TrioProxy:
|
|
134
144
|
url_args = parse_proxy_url(url)
|
|
135
145
|
return cls(*url_args, **kwargs)
|