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,12 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from typing import Union
|
|
3
4
|
|
|
4
5
|
import anyio
|
|
5
6
|
import anyio.abc
|
|
6
7
|
from anyio.streams.tls import TLSStream
|
|
7
8
|
|
|
8
|
-
from ..._errors import ProxyError
|
|
9
9
|
from ... import _abc as abc
|
|
10
|
+
from ..._errors import IncompleteReadError
|
|
10
11
|
|
|
11
12
|
DEFAULT_RECEIVE_SIZE = 65536
|
|
12
13
|
|
|
@@ -14,44 +15,28 @@ AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream]
|
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class AnyioSocketStream(abc.AsyncSocketStream):
|
|
17
|
-
_stream: AnyioStreamType
|
|
18
|
-
|
|
19
18
|
def __init__(self, stream: AnyioStreamType) -> None:
|
|
20
19
|
self._stream = stream
|
|
21
20
|
|
|
22
|
-
async def
|
|
21
|
+
async def write(self, data: bytes) -> None:
|
|
23
22
|
await self._stream.send(item=data)
|
|
24
23
|
|
|
25
|
-
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE):
|
|
24
|
+
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
|
|
26
25
|
try:
|
|
27
26
|
return await self._stream.receive(max_bytes=max_bytes)
|
|
28
27
|
except anyio.EndOfStream: # pragma: no cover
|
|
29
28
|
return b""
|
|
30
29
|
|
|
31
|
-
async def
|
|
30
|
+
async def read_exactly(self, n: int) -> bytes:
|
|
32
31
|
data = bytearray()
|
|
33
32
|
while len(data) < n:
|
|
34
33
|
packet = await self.read(n - len(data))
|
|
35
34
|
if not packet: # pragma: no cover
|
|
36
|
-
raise
|
|
37
|
-
data
|
|
38
|
-
return data
|
|
39
|
-
|
|
40
|
-
async def
|
|
41
|
-
self,
|
|
42
|
-
hostname: str,
|
|
43
|
-
ssl_context: ssl.SSLContext,
|
|
44
|
-
) -> 'AnyioSocketStream':
|
|
45
|
-
ssl_stream = await TLSStream.wrap(
|
|
46
|
-
self._stream,
|
|
47
|
-
ssl_context=ssl_context,
|
|
48
|
-
hostname=hostname,
|
|
49
|
-
standard_compatible=False,
|
|
50
|
-
server_side=False,
|
|
51
|
-
)
|
|
52
|
-
return AnyioSocketStream(ssl_stream)
|
|
53
|
-
|
|
54
|
-
async def close(self):
|
|
35
|
+
raise IncompleteReadError("Connection closed unexpectedly")
|
|
36
|
+
data.extend(packet)
|
|
37
|
+
return data # type:ignore[return-value]
|
|
38
|
+
|
|
39
|
+
async def close(self) -> None:
|
|
55
40
|
await self._stream.aclose()
|
|
56
41
|
|
|
57
42
|
@property
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
2
5
|
import anyio
|
|
3
|
-
|
|
6
|
+
|
|
4
7
|
from ._stream import AnyioSocketStream
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
async def connect_tcp(
|
|
8
11
|
host: str,
|
|
9
12
|
port: int,
|
|
10
|
-
|
|
13
|
+
**kwargs: Any,
|
|
11
14
|
) -> AnyioSocketStream:
|
|
12
15
|
s = await anyio.connect_tcp(
|
|
13
16
|
remote_host=host,
|
|
14
17
|
remote_port=port,
|
|
15
|
-
|
|
18
|
+
**kwargs,
|
|
16
19
|
)
|
|
17
20
|
return AnyioSocketStream(s)
|
|
@@ -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 anyio
|
|
5
7
|
|
|
6
|
-
from .
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
from ...._connectors.factory_async import create_connector
|
|
9
|
+
from ...._errors import (
|
|
10
|
+
IncompleteReadError,
|
|
11
|
+
ProxyConnectionError,
|
|
12
|
+
ProxyError,
|
|
13
|
+
ProxyTimeoutError,
|
|
14
|
+
)
|
|
12
15
|
from ...._helpers import parse_proxy_url
|
|
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 AnyioSocketStream
|
|
16
21
|
|
|
17
22
|
DEFAULT_TIMEOUT = 60
|
|
18
23
|
|
|
@@ -23,12 +28,12 @@ class AnyioProxy:
|
|
|
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: AnyioProxy | None = None,
|
|
36
|
+
) -> None:
|
|
32
37
|
self._proxy_type = proxy_type
|
|
33
38
|
self._proxy_host = host
|
|
34
39
|
self._proxy_port = port
|
|
@@ -45,38 +50,37 @@ class AnyioProxy:
|
|
|
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
|
) -> AnyioSocketStream:
|
|
52
57
|
if timeout is None:
|
|
53
58
|
timeout = DEFAULT_TIMEOUT
|
|
54
59
|
|
|
55
|
-
local_host = kwargs.get('local_host')
|
|
56
60
|
try:
|
|
57
61
|
with anyio.fail_after(timeout):
|
|
58
62
|
return await self._connect(
|
|
59
63
|
dest_host=dest_host,
|
|
60
64
|
dest_port=dest_port,
|
|
61
65
|
dest_ssl=dest_ssl,
|
|
62
|
-
|
|
66
|
+
**kwargs,
|
|
63
67
|
)
|
|
64
68
|
except TimeoutError as e:
|
|
65
|
-
raise ProxyTimeoutError(
|
|
69
|
+
raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
|
|
66
70
|
|
|
67
71
|
async def _connect(
|
|
68
72
|
self,
|
|
69
73
|
dest_host: str,
|
|
70
74
|
dest_port: int,
|
|
71
|
-
dest_ssl:
|
|
72
|
-
|
|
75
|
+
dest_ssl: ssl.SSLContext | None = None,
|
|
76
|
+
**kwargs: Any,
|
|
73
77
|
) -> AnyioSocketStream:
|
|
74
78
|
if self._forward is None:
|
|
75
79
|
try:
|
|
76
80
|
stream = await connect_tcp(
|
|
77
81
|
host=self._proxy_host,
|
|
78
82
|
port=self._proxy_port,
|
|
79
|
-
|
|
83
|
+
**kwargs,
|
|
80
84
|
)
|
|
81
85
|
except OSError as e:
|
|
82
86
|
raise ProxyConnectionError(
|
|
@@ -88,6 +92,7 @@ class AnyioProxy:
|
|
|
88
92
|
stream = await self._forward.connect(
|
|
89
93
|
dest_host=self._proxy_host,
|
|
90
94
|
dest_port=self._proxy_port,
|
|
95
|
+
**kwargs,
|
|
91
96
|
)
|
|
92
97
|
|
|
93
98
|
try:
|
|
@@ -117,7 +122,10 @@ class AnyioProxy:
|
|
|
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:
|
|
122
130
|
with anyio.CancelScope(shield=True):
|
|
123
131
|
await stream.close()
|
|
@@ -126,10 +134,12 @@ class AnyioProxy:
|
|
|
126
134
|
return stream
|
|
127
135
|
|
|
128
136
|
@classmethod
|
|
129
|
-
def create(
|
|
137
|
+
def create(
|
|
138
|
+
cls, *args: Any, **kwargs: Any
|
|
139
|
+
) -> AnyioProxy: # 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) -> AnyioProxy:
|
|
134
144
|
url_args = parse_proxy_url(url)
|
|
135
145
|
return cls(*url_args, **kwargs)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import ssl
|
|
2
4
|
from typing import Union
|
|
3
5
|
|
|
@@ -5,8 +7,8 @@ import anyio
|
|
|
5
7
|
import anyio.abc
|
|
6
8
|
from anyio.streams.tls import TLSStream
|
|
7
9
|
|
|
8
|
-
from ...._errors import ProxyError
|
|
9
10
|
from .... import _abc as abc
|
|
11
|
+
from ...._errors import IncompleteReadError
|
|
10
12
|
|
|
11
13
|
DEFAULT_RECEIVE_SIZE = 65536
|
|
12
14
|
|
|
@@ -14,34 +16,32 @@ AnyioStreamType = Union[anyio.abc.SocketStream, TLSStream]
|
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class AnyioSocketStream(abc.AsyncSocketStream):
|
|
17
|
-
_stream: AnyioStreamType
|
|
18
|
-
|
|
19
19
|
def __init__(self, stream: AnyioStreamType) -> None:
|
|
20
20
|
self._stream = stream
|
|
21
21
|
|
|
22
|
-
async def
|
|
22
|
+
async def write(self, data: bytes) -> None:
|
|
23
23
|
await self._stream.send(item=data)
|
|
24
24
|
|
|
25
|
-
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE):
|
|
25
|
+
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
|
|
26
26
|
try:
|
|
27
27
|
return await self._stream.receive(max_bytes=max_bytes)
|
|
28
28
|
except anyio.EndOfStream: # pragma: no cover
|
|
29
29
|
return b""
|
|
30
30
|
|
|
31
|
-
async def
|
|
31
|
+
async def read_exactly(self, n: int) -> bytes:
|
|
32
32
|
data = bytearray()
|
|
33
33
|
while len(data) < n:
|
|
34
34
|
packet = await self.read(n - len(data))
|
|
35
35
|
if not packet: # pragma: no cover
|
|
36
|
-
raise
|
|
37
|
-
data
|
|
38
|
-
return data
|
|
36
|
+
raise IncompleteReadError("Connection closed unexpectedly")
|
|
37
|
+
data.extend(packet)
|
|
38
|
+
return data # type:ignore[return-value]
|
|
39
39
|
|
|
40
40
|
async def start_tls(
|
|
41
41
|
self,
|
|
42
42
|
hostname: str,
|
|
43
43
|
ssl_context: ssl.SSLContext,
|
|
44
|
-
) ->
|
|
44
|
+
) -> AnyioSocketStream:
|
|
45
45
|
ssl_stream = await TLSStream.wrap(
|
|
46
46
|
self._stream,
|
|
47
47
|
ssl_context=ssl_context,
|
|
@@ -51,7 +51,7 @@ class AnyioSocketStream(abc.AsyncSocketStream):
|
|
|
51
51
|
)
|
|
52
52
|
return AnyioSocketStream(ssl_stream)
|
|
53
53
|
|
|
54
|
-
async def close(self):
|
|
54
|
+
async def close(self) -> None:
|
|
55
55
|
await self._stream.aclose()
|
|
56
56
|
|
|
57
57
|
@property
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
import asyncio
|
|
3
|
-
|
|
4
|
+
import socket
|
|
4
5
|
|
|
5
|
-
from ._resolver import Resolver
|
|
6
6
|
from ..._helpers import is_ipv4_address, is_ipv6_address
|
|
7
|
+
from ..._types import ResolvedAddress
|
|
8
|
+
from ._resolver import Resolver
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
async def connect_tcp(
|
|
10
12
|
host: str,
|
|
11
13
|
port: int,
|
|
12
14
|
loop: asyncio.AbstractEventLoop,
|
|
13
|
-
local_addr:
|
|
15
|
+
local_addr: tuple[str, int] | None = None,
|
|
14
16
|
) -> socket.socket:
|
|
15
17
|
|
|
16
18
|
family, host = await _resolve_host(host, loop)
|
|
17
19
|
|
|
18
20
|
sock = socket.socket(family=family, type=socket.SOCK_STREAM)
|
|
19
|
-
sock.setblocking(False)
|
|
21
|
+
sock.setblocking(False) # noqa: FBT003
|
|
20
22
|
if local_addr is not None: # pragma: no cover
|
|
21
23
|
sock.bind(local_addr)
|
|
22
24
|
|
|
23
|
-
if is_ipv6_address(host):
|
|
25
|
+
if is_ipv6_address(host): # noqa: SIM108
|
|
24
26
|
address = (host, port, 0, 0) # to fix OSError: [WinError 10022]
|
|
25
27
|
else:
|
|
26
28
|
address = (host, port) # type: ignore[assignment]
|
|
@@ -33,7 +35,10 @@ async def connect_tcp(
|
|
|
33
35
|
return sock
|
|
34
36
|
|
|
35
37
|
|
|
36
|
-
async def _resolve_host(
|
|
38
|
+
async def _resolve_host(
|
|
39
|
+
host: str,
|
|
40
|
+
loop: asyncio.AbstractEventLoop,
|
|
41
|
+
) -> ResolvedAddress:
|
|
37
42
|
if is_ipv4_address(host):
|
|
38
43
|
return socket.AF_INET, host
|
|
39
44
|
if is_ipv6_address(host):
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import asyncio
|
|
2
4
|
import socket
|
|
3
5
|
import sys
|
|
4
|
-
from typing import Any
|
|
5
|
-
import warnings
|
|
6
|
+
from typing import Any
|
|
6
7
|
|
|
7
|
-
from ...
|
|
8
|
+
from ..._connectors.factory_async import create_connector
|
|
9
|
+
from ..._errors import (
|
|
10
|
+
IncompleteReadError,
|
|
11
|
+
ProxyConnectionError,
|
|
12
|
+
ProxyError,
|
|
13
|
+
ProxyTimeoutError,
|
|
14
|
+
)
|
|
8
15
|
from ..._helpers import parse_proxy_url
|
|
9
|
-
from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
|
|
10
|
-
from ._stream import AsyncioSocketStream
|
|
11
|
-
from ._resolver import Resolver
|
|
12
|
-
|
|
13
16
|
from ..._protocols.errors import ReplyError
|
|
14
|
-
from ...
|
|
15
|
-
|
|
17
|
+
from ..._types import ProxyType
|
|
16
18
|
from ._connect import connect_tcp
|
|
19
|
+
from ._resolver import Resolver
|
|
20
|
+
from ._stream import AsyncioSocketStream
|
|
17
21
|
|
|
18
22
|
if sys.version_info >= (3, 11):
|
|
19
|
-
import asyncio as async_timeout
|
|
23
|
+
import asyncio as async_timeout
|
|
20
24
|
else:
|
|
21
25
|
import async_timeout
|
|
22
26
|
|
|
@@ -29,11 +33,12 @@ class AsyncioProxy:
|
|
|
29
33
|
proxy_type: ProxyType,
|
|
30
34
|
host: str,
|
|
31
35
|
port: int,
|
|
32
|
-
username:
|
|
33
|
-
password:
|
|
34
|
-
rdns:
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
username: str | None = None,
|
|
37
|
+
password: str | None = None,
|
|
38
|
+
rdns: bool | None = None, # noqa: FBT001
|
|
39
|
+
forward: AsyncioProxy | None = None,
|
|
40
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
41
|
+
) -> None:
|
|
37
42
|
if loop is None:
|
|
38
43
|
loop = asyncio.get_event_loop()
|
|
39
44
|
|
|
@@ -45,6 +50,7 @@ class AsyncioProxy:
|
|
|
45
50
|
self._password = password
|
|
46
51
|
self._username = username
|
|
47
52
|
self._rdns = rdns
|
|
53
|
+
self._forward = forward
|
|
48
54
|
|
|
49
55
|
self._resolver = Resolver(loop=loop)
|
|
50
56
|
|
|
@@ -52,56 +58,52 @@ class AsyncioProxy:
|
|
|
52
58
|
self,
|
|
53
59
|
dest_host: str,
|
|
54
60
|
dest_port: int,
|
|
55
|
-
timeout:
|
|
61
|
+
timeout: float | None = None,
|
|
56
62
|
**kwargs: Any,
|
|
57
63
|
) -> socket.socket:
|
|
58
64
|
if timeout is None:
|
|
59
65
|
timeout = DEFAULT_TIMEOUT
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
if _socket is not None:
|
|
63
|
-
warnings.warn(
|
|
64
|
-
"The '_socket' argument is deprecated and will be removed in the future",
|
|
65
|
-
DeprecationWarning,
|
|
66
|
-
stacklevel=2,
|
|
67
|
-
)
|
|
67
|
+
local_addr = kwargs.get("local_addr")
|
|
68
68
|
|
|
69
|
-
local_addr = kwargs.get('local_addr')
|
|
70
69
|
try:
|
|
71
70
|
async with async_timeout.timeout(timeout):
|
|
72
71
|
return await self._connect(
|
|
73
72
|
dest_host=dest_host,
|
|
74
73
|
dest_port=dest_port,
|
|
75
|
-
_socket=_socket,
|
|
76
74
|
local_addr=local_addr,
|
|
77
75
|
)
|
|
78
76
|
except asyncio.TimeoutError as e:
|
|
79
|
-
raise ProxyTimeoutError(f
|
|
77
|
+
raise ProxyTimeoutError(f"Proxy connection timed out: {timeout}") from e
|
|
80
78
|
|
|
81
79
|
async def _connect(
|
|
82
80
|
self,
|
|
83
|
-
dest_host,
|
|
84
|
-
dest_port,
|
|
85
|
-
|
|
86
|
-
local_addr=None,
|
|
81
|
+
dest_host: str,
|
|
82
|
+
dest_port: int,
|
|
83
|
+
local_addr: tuple[str, int] | None = None,
|
|
87
84
|
) -> socket.socket:
|
|
88
|
-
if
|
|
85
|
+
if self._forward is not None:
|
|
86
|
+
sock = await self._forward.connect(
|
|
87
|
+
dest_host=self._proxy_host,
|
|
88
|
+
dest_port=self._proxy_port,
|
|
89
|
+
timeout=None,
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
89
92
|
try:
|
|
90
|
-
|
|
93
|
+
sock = await connect_tcp(
|
|
91
94
|
host=self._proxy_host,
|
|
92
95
|
port=self._proxy_port,
|
|
93
96
|
loop=self._loop,
|
|
94
97
|
local_addr=local_addr,
|
|
95
98
|
)
|
|
96
99
|
except OSError as e:
|
|
97
|
-
msg =
|
|
98
|
-
|
|
99
|
-
self._proxy_port
|
|
100
|
-
e.strerror,
|
|
100
|
+
msg = (
|
|
101
|
+
f"Could not connect to proxy "
|
|
102
|
+
f"{self._proxy_host}:{self._proxy_port} [{e.strerror}]"
|
|
101
103
|
)
|
|
102
104
|
raise ProxyConnectionError(e.errno, msg) from e
|
|
103
105
|
|
|
104
|
-
stream = AsyncioSocketStream(sock=
|
|
106
|
+
stream = AsyncioSocketStream(sock=sock, loop=self._loop)
|
|
105
107
|
|
|
106
108
|
try:
|
|
107
109
|
connector = create_connector(
|
|
@@ -117,27 +119,24 @@ class AsyncioProxy:
|
|
|
117
119
|
port=dest_port,
|
|
118
120
|
)
|
|
119
121
|
|
|
120
|
-
return
|
|
122
|
+
return sock
|
|
121
123
|
except ReplyError as e:
|
|
122
124
|
await stream.close()
|
|
123
|
-
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
|
|
124
129
|
except (asyncio.CancelledError, Exception): # pragma: no cover
|
|
125
130
|
await stream.close()
|
|
126
131
|
raise
|
|
127
132
|
|
|
128
|
-
@property
|
|
129
|
-
def proxy_host(self):
|
|
130
|
-
return self._proxy_host
|
|
131
|
-
|
|
132
|
-
@property
|
|
133
|
-
def proxy_port(self):
|
|
134
|
-
return self._proxy_port
|
|
135
|
-
|
|
136
133
|
@classmethod
|
|
137
|
-
def create(
|
|
134
|
+
def create(
|
|
135
|
+
cls, *args: Any, **kwargs: Any
|
|
136
|
+
) -> AsyncioProxy: # for backward compatibility
|
|
138
137
|
return cls(*args, **kwargs)
|
|
139
138
|
|
|
140
139
|
@classmethod
|
|
141
|
-
def from_url(cls, url: str, **kwargs) ->
|
|
140
|
+
def from_url(cls, url: str, **kwargs: Any) -> AsyncioProxy:
|
|
142
141
|
url_args = parse_proxy_url(url)
|
|
143
142
|
return cls(*url_args, **kwargs)
|
|
@@ -2,13 +2,19 @@ import asyncio
|
|
|
2
2
|
import socket
|
|
3
3
|
|
|
4
4
|
from ... import _abc as abc
|
|
5
|
+
from ..._types import ResolvedAddress
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
class Resolver(abc.AsyncResolver):
|
|
8
|
-
def __init__(self, loop: asyncio.AbstractEventLoop):
|
|
9
|
+
def __init__(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
9
10
|
self._loop = loop
|
|
10
11
|
|
|
11
|
-
async def resolve(
|
|
12
|
+
async def resolve(
|
|
13
|
+
self,
|
|
14
|
+
host: str,
|
|
15
|
+
port: int = 0,
|
|
16
|
+
family: socket.AddressFamily = socket.AF_UNSPEC,
|
|
17
|
+
) -> ResolvedAddress:
|
|
12
18
|
infos = await self._loop.getaddrinfo(
|
|
13
19
|
host=host,
|
|
14
20
|
port=port,
|
|
@@ -17,9 +23,10 @@ class Resolver(abc.AsyncResolver):
|
|
|
17
23
|
)
|
|
18
24
|
|
|
19
25
|
if not infos: # pragma: no cover
|
|
20
|
-
raise OSError(
|
|
26
|
+
raise OSError(f"Can`t resolve address {host}:{port} [{family}]")
|
|
21
27
|
|
|
28
|
+
# use IPv4 address first
|
|
22
29
|
infos = sorted(infos, key=lambda info: info[0])
|
|
23
30
|
|
|
24
31
|
family, _, _, _, address = infos[0]
|
|
25
|
-
return family, address[0]
|
|
32
|
+
return family, address[0] # type:ignore[return-value]
|
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import socket
|
|
3
3
|
|
|
4
|
-
from ..._errors import ProxyError
|
|
5
|
-
|
|
6
4
|
from ... import _abc as abc
|
|
5
|
+
from ..._errors import IncompleteReadError
|
|
7
6
|
|
|
8
7
|
DEFAULT_RECEIVE_SIZE = 65536
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class AsyncioSocketStream(abc.AsyncSocketStream):
|
|
12
|
-
|
|
13
|
-
_socket = None
|
|
14
|
-
|
|
15
|
-
def __init__(self, sock: socket.socket, loop: asyncio.AbstractEventLoop):
|
|
11
|
+
def __init__(self, sock: socket.socket, loop: asyncio.AbstractEventLoop) -> None:
|
|
16
12
|
self._loop = loop
|
|
17
13
|
self._socket = sock
|
|
18
14
|
|
|
19
|
-
async def
|
|
15
|
+
async def write(self, data: bytes) -> None:
|
|
20
16
|
await self._loop.sock_sendall(self._socket, data)
|
|
21
17
|
|
|
22
|
-
async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
|
|
18
|
+
async def read(self, max_bytes: int = DEFAULT_RECEIVE_SIZE) -> bytes:
|
|
23
19
|
return await self._loop.sock_recv(self._socket, max_bytes)
|
|
24
20
|
|
|
25
|
-
async def
|
|
21
|
+
async def read_exactly(self, n: int) -> bytes:
|
|
26
22
|
data = bytearray()
|
|
27
23
|
while len(data) < n:
|
|
28
24
|
packet = await self._loop.sock_recv(self._socket, n - len(data))
|
|
29
25
|
if not packet: # pragma: no cover
|
|
30
|
-
raise
|
|
31
|
-
data
|
|
32
|
-
return data
|
|
26
|
+
raise IncompleteReadError("Connection closed unexpectedly")
|
|
27
|
+
data.extend(packet)
|
|
28
|
+
return data # type:ignore[return-value]
|
|
33
29
|
|
|
34
|
-
async def close(self):
|
|
30
|
+
async def close(self) -> None:
|
|
35
31
|
if self._socket is not None:
|
|
36
32
|
self._socket.close()
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import asyncio
|
|
2
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
3
6
|
from ._stream import AsyncioSocketStream
|
|
4
7
|
|
|
5
8
|
|
|
@@ -7,16 +10,16 @@ async def connect_tcp(
|
|
|
7
10
|
host: str,
|
|
8
11
|
port: int,
|
|
9
12
|
loop: asyncio.AbstractEventLoop,
|
|
10
|
-
local_addr:
|
|
13
|
+
local_addr: tuple[str, int] | None = None,
|
|
11
14
|
) -> AsyncioSocketStream:
|
|
12
|
-
kwargs = {}
|
|
15
|
+
kwargs: dict[str, Any] = {}
|
|
13
16
|
if local_addr is not None:
|
|
14
|
-
kwargs[
|
|
17
|
+
kwargs["local_addr"] = local_addr # pragma: no cover
|
|
15
18
|
|
|
16
19
|
reader, writer = await asyncio.open_connection(
|
|
17
20
|
host=host,
|
|
18
21
|
port=port,
|
|
19
|
-
**kwargs,
|
|
22
|
+
**kwargs,
|
|
20
23
|
)
|
|
21
24
|
|
|
22
25
|
return AsyncioSocketStream(
|