python-socks 2.1.1__py3-none-any.whl → 2.3.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.
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import ssl
3
+ import warnings
3
4
 
4
5
  import async_timeout
5
6
 
@@ -21,8 +22,15 @@ class AsyncioProxy:
21
22
  proxy_host: str,
22
23
  proxy_port: int,
23
24
  proxy_ssl: ssl.SSLContext = None,
25
+ forward: 'AsyncioProxy' = None,
24
26
  loop: asyncio.AbstractEventLoop = None,
25
27
  ):
28
+ if loop is not None: # pragma: no cover
29
+ warnings.warn(
30
+ 'The loop argument is deprecated and scheduled for removal in the future.',
31
+ DeprecationWarning,
32
+ stacklevel=2,
33
+ )
26
34
 
27
35
  if loop is None:
28
36
  loop = asyncio.get_event_loop()
@@ -32,13 +40,8 @@ class AsyncioProxy:
32
40
  self._proxy_host = proxy_host
33
41
  self._proxy_port = proxy_port
34
42
  self._proxy_ssl = proxy_ssl
43
+ self._forward = forward
35
44
 
36
- self._dest_host = None
37
- self._dest_port = None
38
- self._dest_ssl = None
39
- self._timeout = None
40
-
41
- self._stream = None
42
45
  self._resolver = Resolver(loop=loop)
43
46
 
44
47
  async def connect(
@@ -47,83 +50,77 @@ class AsyncioProxy:
47
50
  dest_port: int,
48
51
  dest_ssl: ssl.SSLContext = None,
49
52
  timeout: float = None,
50
- _stream: AsyncioSocketStream = None,
51
53
  ) -> AsyncioSocketStream:
52
-
53
54
  if timeout is None:
54
55
  timeout = DEFAULT_TIMEOUT
55
56
 
56
- self._dest_host = dest_host
57
- self._dest_port = dest_port
58
- self._dest_ssl = dest_ssl
59
- self._timeout = timeout
60
-
61
57
  try:
62
- return await self._connect(_stream)
58
+ async with async_timeout.timeout(timeout):
59
+ return await self._connect(
60
+ dest_host=dest_host,
61
+ dest_port=dest_port,
62
+ dest_ssl=dest_ssl,
63
+ )
63
64
  except asyncio.TimeoutError as e:
64
- raise ProxyTimeoutError('Proxy connection timed out: {}'.format(self._timeout)) from e
65
-
66
- async def _connect(self, _stream: AsyncioSocketStream) -> AsyncioSocketStream:
67
- async with async_timeout.timeout(self._timeout):
68
- try:
69
- if _stream is None:
70
- reader, writer = await connect_tcp(
71
- host=self._proxy_host,
72
- port=self._proxy_port,
73
- )
74
- self._stream = AsyncioSocketStream(
75
- loop=self._loop,
76
- reader=reader,
77
- writer=writer,
78
- )
79
- else:
80
- self._stream = _stream
81
-
82
- if self._proxy_ssl is not None: # pragma: no cover
83
- self._stream = await self._stream.start_tls(
84
- hostname=self._proxy_host,
85
- ssl_context=self._proxy_ssl,
86
- )
87
- except OSError as e:
88
- await self._close()
89
- msg = 'Could not connect to proxy {}:{} [{}]'.format(
90
- self._proxy_host,
91
- self._proxy_port,
92
- e.strerror,
65
+ raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
66
+
67
+ async def _connect(
68
+ self,
69
+ dest_host: str,
70
+ dest_port: int,
71
+ dest_ssl: ssl.SSLContext = None,
72
+ ) -> AsyncioSocketStream:
73
+ try:
74
+ if self._forward is None:
75
+ stream = await connect_tcp(
76
+ host=self._proxy_host,
77
+ port=self._proxy_port,
78
+ loop=self._loop,
93
79
  )
94
- raise ProxyConnectionError(e.errno, msg) from e
95
- except (asyncio.CancelledError, Exception):
96
- await self._close()
97
- raise
98
-
99
- try:
100
- await self._negotiate()
101
-
102
- if self._dest_ssl is not None:
103
- self._stream = await self._stream.start_tls(
104
- hostname=self._dest_host,
105
- ssl_context=self._dest_ssl,
106
- )
107
- except (asyncio.CancelledError, Exception):
108
- await self._close()
109
- raise
110
-
111
- return self._stream
112
-
113
- async def _negotiate(self):
114
- raise NotImplementedError()
80
+ else:
81
+ stream = await self._forward.connect(
82
+ dest_host=self._proxy_host,
83
+ dest_port=self._proxy_port,
84
+ )
85
+ except OSError as e:
86
+ raise ProxyConnectionError(
87
+ e.errno,
88
+ f"Couldn't connect to proxy {self._proxy_host}:{self._proxy_port} [{e.strerror}]",
89
+ ) from e
90
+ except (asyncio.CancelledError, Exception):
91
+ raise
115
92
 
116
- async def _close(self):
117
- if self._stream is not None:
118
- await self._stream.close()
93
+ try:
94
+ if self._proxy_ssl is not None: # pragma: no cover
95
+ stream = await stream.start_tls(
96
+ hostname=self._proxy_host,
97
+ ssl_context=self._proxy_ssl,
98
+ )
119
99
 
120
- @property
121
- def proxy_host(self):
122
- return self._proxy_host
100
+ await self._negotiate(
101
+ stream=stream,
102
+ dest_host=dest_host,
103
+ dest_port=dest_port,
104
+ )
123
105
 
124
- @property
125
- def proxy_port(self):
126
- return self._proxy_port
106
+ if dest_ssl is not None:
107
+ stream = await stream.start_tls(
108
+ hostname=dest_host,
109
+ ssl_context=dest_ssl,
110
+ )
111
+ except (asyncio.CancelledError, Exception):
112
+ await stream.close()
113
+ raise
114
+
115
+ return stream
116
+
117
+ async def _negotiate(
118
+ self,
119
+ stream: AsyncioSocketStream,
120
+ dest_host: str,
121
+ dest_port: int,
122
+ ):
123
+ raise NotImplementedError()
127
124
 
128
125
 
129
126
  class Socks5Proxy(AsyncioProxy):
@@ -135,24 +132,31 @@ class Socks5Proxy(AsyncioProxy):
135
132
  password=None,
136
133
  rdns=None,
137
134
  proxy_ssl=None,
135
+ forward: 'AsyncioProxy' = None,
138
136
  loop: asyncio.AbstractEventLoop = None,
139
137
  ):
140
138
  super().__init__(
141
139
  proxy_host=proxy_host,
142
140
  proxy_port=proxy_port,
143
141
  proxy_ssl=proxy_ssl,
142
+ forward=forward,
144
143
  loop=loop,
145
144
  )
146
145
  self._username = username
147
146
  self._password = password
148
147
  self._rdns = rdns
149
148
 
150
- async def _negotiate(self):
149
+ async def _negotiate(
150
+ self,
151
+ stream: AsyncioSocketStream,
152
+ dest_host: str,
153
+ dest_port: int,
154
+ ):
151
155
  proto = Socks5Proto(
152
- stream=self._stream,
156
+ stream=stream,
153
157
  resolver=self._resolver,
154
- dest_host=self._dest_host,
155
- dest_port=self._dest_port,
158
+ dest_host=dest_host,
159
+ dest_port=dest_port,
156
160
  username=self._username,
157
161
  password=self._password,
158
162
  rdns=self._rdns,
@@ -168,23 +172,30 @@ class Socks4Proxy(AsyncioProxy):
168
172
  user_id=None,
169
173
  rdns=None,
170
174
  proxy_ssl=None,
175
+ forward: 'AsyncioProxy' = None,
171
176
  loop: asyncio.AbstractEventLoop = None,
172
177
  ):
173
178
  super().__init__(
174
179
  proxy_host=proxy_host,
175
180
  proxy_port=proxy_port,
176
181
  proxy_ssl=proxy_ssl,
182
+ forward=forward,
177
183
  loop=loop,
178
184
  )
179
185
  self._user_id = user_id
180
186
  self._rdns = rdns
181
187
 
182
- async def _negotiate(self):
188
+ async def _negotiate(
189
+ self,
190
+ stream: AsyncioSocketStream,
191
+ dest_host: str,
192
+ dest_port: int,
193
+ ):
183
194
  proto = Socks4Proto(
184
- stream=self._stream,
195
+ stream=stream,
185
196
  resolver=self._resolver,
186
- dest_host=self._dest_host,
187
- dest_port=self._dest_port,
197
+ dest_host=dest_host,
198
+ dest_port=dest_port,
188
199
  user_id=self._user_id,
189
200
  rdns=self._rdns,
190
201
  )
@@ -199,22 +210,29 @@ class HttpProxy(AsyncioProxy):
199
210
  username=None,
200
211
  password=None,
201
212
  proxy_ssl=None,
213
+ forward: 'AsyncioProxy' = None,
202
214
  loop: asyncio.AbstractEventLoop = None,
203
215
  ):
204
216
  super().__init__(
205
217
  proxy_host=proxy_host,
206
218
  proxy_port=proxy_port,
207
219
  proxy_ssl=proxy_ssl,
220
+ forward=forward,
208
221
  loop=loop,
209
222
  )
210
223
  self._username = username
211
224
  self._password = password
212
225
 
213
- async def _negotiate(self):
226
+ async def _negotiate(
227
+ self,
228
+ stream: AsyncioSocketStream,
229
+ dest_host: str,
230
+ dest_port: int,
231
+ ):
214
232
  proto = HttpProto(
215
- stream=self._stream, # noqa
216
- dest_host=self._dest_host,
217
- dest_port=self._dest_port,
233
+ stream=stream,
234
+ dest_host=dest_host,
235
+ dest_port=dest_port,
218
236
  username=self._username,
219
237
  password=self._password,
220
238
  )
@@ -17,7 +17,7 @@ async def backport_start_tls(
17
17
  ssl_handshake_timeout=None,
18
18
  ): # pragma: no cover
19
19
  """
20
- Python 3.6 asyncio doesn't have a start_tls() method on the loop
20
+ Python 3.6 asyncio doesn't have a start_tls() method on the loop,
21
21
  so we use this function in place of the loop's start_tls() method.
22
22
  Adapted from this comment:
23
23
  https://github.com/urllib3/urllib3/issues/1323#issuecomment-362494839
@@ -1,31 +1,26 @@
1
- from typing import Iterable
1
+ from typing import Sequence
2
2
  from ._proxy import TrioProxy
3
3
 
4
4
 
5
5
  class ProxyChain:
6
- def __init__(self, proxies: Iterable[TrioProxy]):
6
+ def __init__(self, proxies: Sequence[TrioProxy]):
7
7
  self._proxies = proxies
8
8
 
9
- async def connect(self, dest_host, dest_port,
10
- dest_ssl=None, timeout=None,):
11
- _stream = None
12
- proxies = list(self._proxies)
9
+ async def connect(
10
+ self,
11
+ dest_host,
12
+ dest_port,
13
+ dest_ssl=None,
14
+ timeout=None,
15
+ ):
16
+ forward = None
17
+ for proxy in self._proxies:
18
+ proxy._forward = forward
19
+ forward = proxy
13
20
 
14
- length = len(proxies) - 1
15
- for i in range(length):
16
- _stream = await proxies[i].connect(
17
- dest_host=proxies[i + 1].proxy_host,
18
- dest_port=proxies[i + 1].proxy_port,
19
- timeout=timeout,
20
- _stream=_stream
21
- )
22
-
23
- _stream = await proxies[length].connect(
21
+ return await forward.connect(
24
22
  dest_host=dest_host,
25
23
  dest_port=dest_port,
26
24
  dest_ssl=dest_ssl,
27
25
  timeout=timeout,
28
- _stream=_stream
29
26
  )
30
-
31
- return _stream
@@ -1,15 +1,17 @@
1
1
  from typing import Optional, Tuple
2
2
 
3
3
  import trio
4
+ from ._stream import TrioSocketStream
4
5
 
5
6
 
6
7
  async def connect_tcp(
7
8
  host: str,
8
9
  port: int,
9
10
  local_addr: Optional[Tuple[str, int]] = None,
10
- ) -> trio.SocketStream:
11
- return await trio.open_tcp_stream(
11
+ ) -> TrioSocketStream:
12
+ trio_stream = await trio.open_tcp_stream(
12
13
  host=host,
13
14
  port=port,
14
15
  local_address=local_addr,
15
16
  )
17
+ return TrioSocketStream(trio_stream)
@@ -2,35 +2,30 @@ import ssl
2
2
 
3
3
  import trio
4
4
 
5
+ from ._connect import connect_tcp
6
+ from ._stream import TrioSocketStream
7
+ from .._resolver import Resolver
5
8
  from ...._errors import ProxyConnectionError, ProxyTimeoutError
6
9
  from ...._proto.http_async import HttpProto
7
10
  from ...._proto.socks4_async import Socks4Proto
8
11
  from ...._proto.socks5_async import Socks5Proto
9
- from .._resolver import Resolver
10
- from ._stream import TrioSocketStream
11
- from ._connect import connect_tcp
12
- from .... import _abc as abc
13
12
 
14
13
  DEFAULT_TIMEOUT = 60
15
14
 
16
15
 
17
- class TrioProxy(abc.AsyncProxy):
16
+ class TrioProxy:
18
17
  def __init__(
19
18
  self,
20
19
  proxy_host: str,
21
20
  proxy_port: int,
22
21
  proxy_ssl: ssl.SSLContext = None,
22
+ forward: 'TrioProxy' = None,
23
23
  ):
24
24
  self._proxy_host = proxy_host
25
25
  self._proxy_port = proxy_port
26
26
  self._proxy_ssl = proxy_ssl
27
+ self._forward = forward
27
28
 
28
- self._dest_host = None
29
- self._dest_port = None
30
- self._dest_ssl = None
31
- self._timeout = None
32
-
33
- self._stream = None
34
29
  self._resolver = Resolver()
35
30
 
36
31
  async def connect(
@@ -39,74 +34,75 @@ class TrioProxy(abc.AsyncProxy):
39
34
  dest_port: int,
40
35
  dest_ssl: ssl.SSLContext = None,
41
36
  timeout: float = None,
42
- _stream: TrioSocketStream = None,
43
37
  ) -> TrioSocketStream:
44
-
45
38
  if timeout is None:
46
39
  timeout = DEFAULT_TIMEOUT
47
40
 
48
- self._dest_host = dest_host
49
- self._dest_port = dest_port
50
- self._dest_ssl = dest_ssl
51
- self._timeout = timeout
52
-
53
41
  try:
54
- with trio.fail_after(self._timeout):
55
- if _stream is None:
56
- self._stream = TrioSocketStream(
57
- await connect_tcp(
58
- host=self._proxy_host,
59
- port=self._proxy_port,
60
- )
61
- )
62
- else:
63
- self._stream = _stream
64
-
65
- if self._proxy_ssl is not None:
66
- self._stream = await self._stream.start_tls(
67
- hostname=self._proxy_host,
68
- ssl_context=self._proxy_ssl,
69
- )
70
-
71
- await self._negotiate()
72
-
73
- if self._dest_ssl is not None:
74
- self._stream = await self._stream.start_tls(
75
- hostname=self._dest_host,
76
- ssl_context=self._dest_ssl,
77
- )
78
-
79
- return self._stream
42
+ with trio.fail_after(timeout):
43
+ return await self._connect(
44
+ dest_host=dest_host,
45
+ dest_port=dest_port,
46
+ dest_ssl=dest_ssl,
47
+ )
48
+ except trio.TooSlowError as e:
49
+ raise ProxyTimeoutError('Proxy connection timed out: {}'.format(timeout)) from e
80
50
 
51
+ async def _connect(
52
+ self,
53
+ dest_host: str,
54
+ dest_port: int,
55
+ dest_ssl: ssl.SSLContext = None,
56
+ ) -> TrioSocketStream:
57
+ try:
58
+ if self._forward is None:
59
+ stream = await connect_tcp(
60
+ host=self._proxy_host,
61
+ port=self._proxy_port,
62
+ )
63
+ else:
64
+ stream = await self._forward.connect(
65
+ dest_host=self._proxy_host,
66
+ dest_port=self._proxy_port,
67
+ )
81
68
  except OSError as e:
82
- await self._close()
83
- msg = 'Could not connect to proxy {}:{} [{}]'.format(
84
- self._proxy_host,
85
- self._proxy_port,
86
- e.strerror,
87
- )
88
- raise ProxyConnectionError(e.errno, msg) from e
89
- except trio.TooSlowError as e:
90
- await self._close()
91
- raise ProxyTimeoutError('Proxy connection timed out: {}'.format(self._timeout)) from e
92
- except Exception:
93
- await self._close()
94
- raise
69
+ raise ProxyConnectionError(
70
+ e.errno,
71
+ f"Couldn't connect to proxy {self._proxy_host}:{self._proxy_port} [{e.strerror}]",
72
+ ) from e
95
73
 
96
- async def _negotiate(self):
97
- raise NotImplementedError
74
+ try:
75
+ if self._proxy_ssl is not None:
76
+ stream = await stream.start_tls(
77
+ hostname=self._proxy_host,
78
+ ssl_context=self._proxy_ssl,
79
+ )
80
+
81
+ await self._negotiate(
82
+ stream=stream,
83
+ dest_host=dest_host,
84
+ dest_port=dest_port,
85
+ )
98
86
 
99
- async def _close(self):
100
- if self._stream is not None:
101
- await self._stream.close()
87
+ if dest_ssl is not None:
88
+ stream = await stream.start_tls(
89
+ hostname=dest_host,
90
+ ssl_context=dest_ssl,
91
+ )
92
+ except BaseException: # trio.Cancelled...
93
+ with trio.CancelScope(shield=True):
94
+ await stream.close()
95
+ raise
102
96
 
103
- @property
104
- def proxy_host(self):
105
- return self._proxy_host
97
+ return stream
106
98
 
107
- @property
108
- def proxy_port(self):
109
- return self._proxy_port
99
+ async def _negotiate(
100
+ self,
101
+ stream: TrioSocketStream,
102
+ dest_host: str,
103
+ dest_port: int,
104
+ ):
105
+ raise NotImplementedError
110
106
 
111
107
 
112
108
  class Socks5Proxy(TrioProxy):
@@ -118,22 +114,29 @@ class Socks5Proxy(TrioProxy):
118
114
  password=None,
119
115
  rdns=None,
120
116
  proxy_ssl=None,
117
+ forward: 'TrioProxy' = None,
121
118
  ):
122
119
  super().__init__(
123
120
  proxy_host=proxy_host,
124
121
  proxy_port=proxy_port,
125
122
  proxy_ssl=proxy_ssl,
123
+ forward=forward,
126
124
  )
127
125
  self._username = username
128
126
  self._password = password
129
127
  self._rdns = rdns
130
128
 
131
- async def _negotiate(self):
129
+ async def _negotiate(
130
+ self,
131
+ stream: TrioSocketStream,
132
+ dest_host: str,
133
+ dest_port: int,
134
+ ):
132
135
  proto = Socks5Proto(
133
- stream=self._stream,
136
+ stream=stream,
134
137
  resolver=self._resolver,
135
- dest_host=self._dest_host,
136
- dest_port=self._dest_port,
138
+ dest_host=dest_host,
139
+ dest_port=dest_port,
137
140
  username=self._username,
138
141
  password=self._password,
139
142
  rdns=self._rdns,
@@ -149,21 +152,28 @@ class Socks4Proxy(TrioProxy):
149
152
  user_id=None,
150
153
  rdns=None,
151
154
  proxy_ssl=None,
155
+ forward: 'TrioProxy' = None,
152
156
  ):
153
157
  super().__init__(
154
158
  proxy_host=proxy_host,
155
159
  proxy_port=proxy_port,
156
160
  proxy_ssl=proxy_ssl,
161
+ forward=forward,
157
162
  )
158
163
  self._user_id = user_id
159
164
  self._rdns = rdns
160
165
 
161
- async def _negotiate(self):
166
+ async def _negotiate(
167
+ self,
168
+ stream: TrioSocketStream,
169
+ dest_host: str,
170
+ dest_port: int,
171
+ ):
162
172
  proto = Socks4Proto(
163
- stream=self._stream,
173
+ stream=stream,
164
174
  resolver=self._resolver,
165
- dest_host=self._dest_host,
166
- dest_port=self._dest_port,
175
+ dest_host=dest_host,
176
+ dest_port=dest_port,
167
177
  user_id=self._user_id,
168
178
  rdns=self._rdns,
169
179
  )
@@ -178,20 +188,27 @@ class HttpProxy(TrioProxy):
178
188
  username=None,
179
189
  password=None,
180
190
  proxy_ssl=None,
191
+ forward: 'TrioProxy' = None,
181
192
  ):
182
193
  super().__init__(
183
194
  proxy_host=proxy_host,
184
195
  proxy_port=proxy_port,
185
196
  proxy_ssl=proxy_ssl,
197
+ forward=forward,
186
198
  )
187
199
  self._username = username
188
200
  self._password = password
189
201
 
190
- async def _negotiate(self):
202
+ async def _negotiate(
203
+ self,
204
+ stream: TrioSocketStream,
205
+ dest_host: str,
206
+ dest_port: int,
207
+ ):
191
208
  proto = HttpProto(
192
- stream=self._stream,
193
- dest_host=self._dest_host,
194
- dest_port=self._dest_port,
209
+ stream=stream,
210
+ dest_host=dest_host,
211
+ dest_port=dest_port,
195
212
  username=self._username,
196
213
  password=self._password,
197
214
  )
@@ -13,24 +13,14 @@ class ProxyChain:
13
13
  dest_ssl=None,
14
14
  timeout=None,
15
15
  ):
16
- stream = None
17
- proxies = list(self._proxies)
16
+ forward = None
17
+ for proxy in self._proxies:
18
+ proxy._forward = forward
19
+ forward = proxy
18
20
 
19
- length = len(proxies) - 1
20
- for i in range(length):
21
- stream = proxies[i].connect(
22
- dest_host=proxies[i + 1].proxy_host,
23
- dest_port=proxies[i + 1].proxy_port,
24
- timeout=timeout,
25
- _stream=stream,
26
- )
27
-
28
- stream = proxies[length].connect(
21
+ return forward.connect(
29
22
  dest_host=dest_host,
30
23
  dest_port=dest_port,
31
24
  dest_ssl=dest_ssl,
32
25
  timeout=timeout,
33
- _stream=stream,
34
26
  )
35
-
36
- return stream
@@ -0,0 +1,19 @@
1
+ import socket
2
+ from typing import Optional, Tuple
3
+ from ._stream import SyncSocketStream
4
+
5
+
6
+ def connect_tcp(
7
+ host: str,
8
+ port: int,
9
+ timeout: float = None,
10
+ local_addr: Optional[Tuple[str, int]] = None,
11
+ ) -> SyncSocketStream:
12
+ address = (host, port)
13
+ sock = socket.create_connection(
14
+ address,
15
+ timeout,
16
+ source_address=local_addr,
17
+ )
18
+
19
+ return SyncSocketStream(sock)