python-socks 2.8.1__tar.gz → 2.8.2__tar.gz

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.
Files changed (103) hide show
  1. {python_socks-2.8.1/python_socks.egg-info → python_socks-2.8.2}/PKG-INFO +1 -1
  2. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/http.py +12 -2
  3. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/socks5.py +33 -26
  4. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_version.py +1 -1
  5. {python_socks-2.8.1 → python_socks-2.8.2/python_socks.egg-info}/PKG-INFO +1 -1
  6. python_socks-2.8.2/tests/test_misc.py +50 -0
  7. python_socks-2.8.1/tests/test_misc.py +0 -21
  8. {python_socks-2.8.1 → python_socks-2.8.2}/LICENSE.txt +0 -0
  9. {python_socks-2.8.1 → python_socks-2.8.2}/MANIFEST.in +0 -0
  10. {python_socks-2.8.1 → python_socks-2.8.2}/README.md +0 -0
  11. {python_socks-2.8.1 → python_socks-2.8.2}/pyproject.toml +0 -0
  12. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/__init__.py +0 -0
  13. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_abc.py +0 -0
  14. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/__init__.py +0 -0
  15. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/abc.py +0 -0
  16. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/factory_async.py +0 -0
  17. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/factory_sync.py +0 -0
  18. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/http_async.py +0 -0
  19. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/http_sync.py +0 -0
  20. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks4_async.py +0 -0
  21. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks4_sync.py +0 -0
  22. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks5_async.py +0 -0
  23. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_connectors/socks5_sync.py +0 -0
  24. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_errors.py +0 -0
  25. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_helpers.py +0 -0
  26. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/__init__.py +0 -0
  27. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/errors.py +0 -0
  28. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_protocols/socks4.py +0 -0
  29. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/_types.py +0 -0
  30. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/__init__.py +0 -0
  31. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/_proxy_chain.py +0 -0
  32. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/__init__.py +0 -0
  33. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_chain.py +0 -0
  34. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_connect.py +0 -0
  35. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_proxy.py +0 -0
  36. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_resolver.py +0 -0
  37. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/_stream.py +0 -0
  38. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/__init__.py +0 -0
  39. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_chain.py +0 -0
  40. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_connect.py +0 -0
  41. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_proxy.py +0 -0
  42. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/anyio/v2/_stream.py +0 -0
  43. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/__init__.py +0 -0
  44. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_connect.py +0 -0
  45. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_proxy.py +0 -0
  46. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_resolver.py +0 -0
  47. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/_stream.py +0 -0
  48. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/__init__.py +0 -0
  49. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_chain.py +0 -0
  50. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_connect.py +0 -0
  51. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_proxy.py +0 -0
  52. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/asyncio/v2/_stream.py +0 -0
  53. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/__init__.py +0 -0
  54. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_connect.py +0 -0
  55. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_proxy.py +0 -0
  56. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_resolver.py +0 -0
  57. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/curio/_stream.py +0 -0
  58. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/__init__.py +0 -0
  59. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_connect.py +0 -0
  60. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_proxy.py +0 -0
  61. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_resolver.py +0 -0
  62. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/_stream.py +0 -0
  63. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/__init__.py +0 -0
  64. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_chain.py +0 -0
  65. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_connect.py +0 -0
  66. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_proxy.py +0 -0
  67. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/async_/trio/v2/_stream.py +0 -0
  68. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/py.typed +0 -0
  69. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/__init__.py +0 -0
  70. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_chain.py +0 -0
  71. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_connect.py +0 -0
  72. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_proxy.py +0 -0
  73. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_resolver.py +0 -0
  74. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/_stream.py +0 -0
  75. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/__init__.py +0 -0
  76. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_chain.py +0 -0
  77. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_connect.py +0 -0
  78. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_proxy.py +0 -0
  79. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_ssl_transport.py +0 -0
  80. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks/sync/v2/_stream.py +0 -0
  81. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/SOURCES.txt +0 -0
  82. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/dependency_links.txt +0 -0
  83. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/requires.txt +0 -0
  84. {python_socks-2.8.1 → python_socks-2.8.2}/python_socks.egg-info/top_level.txt +0 -0
  85. {python_socks-2.8.1 → python_socks-2.8.2}/setup.cfg +0 -0
  86. {python_socks-2.8.1 → python_socks-2.8.2}/tests/__init__.py +0 -0
  87. {python_socks-2.8.1 → python_socks-2.8.2}/tests/config.py +0 -0
  88. {python_socks-2.8.1 → python_socks-2.8.2}/tests/conftest.py +0 -0
  89. {python_socks-2.8.1 → python_socks-2.8.2}/tests/http_app.py +0 -0
  90. {python_socks-2.8.1 → python_socks-2.8.2}/tests/http_server.py +0 -0
  91. {python_socks-2.8.1 → python_socks-2.8.2}/tests/mocks.py +0 -0
  92. {python_socks-2.8.1 → python_socks-2.8.2}/tests/proxy_server.py +0 -0
  93. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_aio.py +0 -0
  94. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_aio_v2.py +0 -0
  95. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_anyio.py +0 -0
  96. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_anyio_v2.py +0 -0
  97. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_curio.py +0 -0
  98. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_trio.py +0 -0
  99. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_async_trio_v2.py +0 -0
  100. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_sync.py +0 -0
  101. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_proxy_sync_v2.py +0 -0
  102. {python_socks-2.8.1 → python_socks-2.8.2}/tests/test_resolvers.py +0 -0
  103. {python_socks-2.8.1 → python_socks-2.8.2}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-socks
3
- Version: 2.8.1
3
+ Version: 2.8.2
4
4
  Summary: Proxy (SOCKS4, SOCKS5, HTTP CONNECT) client for Python
5
5
  Author-email: Roman Snegirev <snegiryev@gmail.com>
6
6
  License: Apache-2.0
@@ -6,6 +6,7 @@ from collections import namedtuple
6
6
  from typing import Optional
7
7
 
8
8
  from .._version import __title__, __version__
9
+ from .._helpers import is_ipv6_address
9
10
 
10
11
  from .errors import ReplyError
11
12
 
@@ -94,8 +95,17 @@ class ConnectRequest:
94
95
 
95
96
  def dumps(self) -> bytes:
96
97
  buff = _Buffer()
97
- buff.append_line(f'CONNECT {self.host}:{self.port} HTTP/1.1')
98
- buff.append_line(f'Host: {self.host}:{self.port}')
98
+ # RFC 3986 § 3.2.2 / RFC 7230 § 5.4: a literal IPv6 address in an
99
+ # authority component must be enclosed in square brackets so the
100
+ # `:` between literal and port is unambiguous. Without brackets,
101
+ # `2001:db8::1:443` could be parsed as either the address
102
+ # `2001:db8::1` with port `443` or the address `2001:db8::1:443`
103
+ # with no port — proxies disagree, and several misroute the
104
+ # unbracketed form (typically returning `200 Connection
105
+ # established` against a connection that was never actually made).
106
+ host = f'[{self.host}]' if is_ipv6_address(self.host) else self.host
107
+ buff.append_line(f'CONNECT {host}:{self.port} HTTP/1.1')
108
+ buff.append_line(f'Host: {host}:{self.port}')
99
109
  buff.append_line(f'User-Agent: {DEFAULT_USER_AGENT}')
100
110
 
101
111
  if self.username and self.password:
@@ -1,13 +1,13 @@
1
1
  import enum
2
+ from functools import singledispatchmethod
2
3
  import ipaddress
3
4
  import socket
4
- from typing import Optional, Union
5
+ from typing import Optional, Type, Union
5
6
  from dataclasses import dataclass, field
6
7
 
7
8
  from .errors import ReplyError
8
9
  from .._helpers import is_ip_address
9
10
 
10
-
11
11
  RSV = NULL = AUTH_GRANTED = 0x00
12
12
  SOCKS_VER = 0x05
13
13
 
@@ -302,34 +302,38 @@ ConnectionState = Union[
302
302
 
303
303
 
304
304
  class Connection:
305
- _state: ConnectionState
306
-
307
305
  def __init__(self):
308
306
  self._state = StateServerWaitingForAuthMethods()
309
307
 
308
+ @singledispatchmethod
310
309
  def send(self, request: Request) -> bytes:
311
- if type(request) is AuthMethodsRequest:
312
- if type(self._state) is not StateServerWaitingForAuthMethods:
313
- raise RuntimeError('Server is not currently waiting for auth methods')
314
- self._state = StateClientSentAuthMethods(request)
315
- return request.dumps()
316
-
317
- if type(request) is AuthRequest:
318
- if type(self._state) is not StateServerWaitingForAuth:
319
- raise RuntimeError('Server is not currently waiting for authentication')
320
- self._state = StateClientSentAuthRequest(request)
321
- return request.dumps()
322
-
323
- if type(request) is ConnectRequest:
324
- if type(self._state) is not StateClientAuthenticated:
325
- raise RuntimeError('Client is not authenticated')
326
- self._state = StateClientSentConnectRequest(request)
327
- return request.dumps()
328
-
329
- raise RuntimeError(f'Invalid request type: {type(request)}')
310
+ raise RuntimeError(f'Invalid request type: {request.__class__}')
311
+
312
+ @send.register
313
+ def _send_auth_methods(self, request: AuthMethodsRequest) -> bytes:
314
+ if not self._state_is(StateServerWaitingForAuthMethods):
315
+ raise RuntimeError('Server is not currently waiting for auth methods')
316
+ self._state = StateClientSentAuthMethods(request)
317
+ return request.dumps()
318
+
319
+ @send.register
320
+ def _send_auth(self, request: AuthRequest) -> bytes:
321
+ if not self._state_is(StateServerWaitingForAuth):
322
+ raise RuntimeError('Server is not currently waiting for authentication')
323
+ self._state = StateClientSentAuthRequest(request)
324
+ return request.dumps()
325
+
326
+ @send.register
327
+ def _send_connect(self, request: ConnectRequest) -> bytes:
328
+ if not self._state_is(StateClientAuthenticated):
329
+ raise RuntimeError('Client is not authenticated')
330
+ self._state = StateClientSentConnectRequest(request)
331
+ return request.dumps()
330
332
 
331
333
  def receive(self, data: bytes) -> Reply:
332
- if type(self._state) is StateClientSentAuthMethods:
334
+ reply: Reply
335
+
336
+ if self._state_is(StateClientSentAuthMethods):
333
337
  reply = AuthMethodReply.loads(data)
334
338
  reply.validate(self._state.data)
335
339
  if reply.method == AuthMethod.USERNAME_PASSWORD:
@@ -338,18 +342,21 @@ class Connection:
338
342
  self._state = StateClientAuthenticated()
339
343
  return reply
340
344
 
341
- if type(self._state) is StateClientSentAuthRequest:
345
+ if self._state_is(StateClientSentAuthRequest):
342
346
  reply = AuthReply.loads(data)
343
347
  self._state = StateClientAuthenticated(data=reply)
344
348
  return reply
345
349
 
346
- if type(self._state) is StateClientSentConnectRequest:
350
+ if self._state_is(StateClientSentConnectRequest):
347
351
  reply = ConnectReply.loads(data)
348
352
  self._state = StateServerConnected(data=reply)
349
353
  return reply
350
354
 
351
355
  raise RuntimeError(f'Invalid connection state: {self._state}')
352
356
 
357
+ def _state_is(self, state_cls: Type[ConnectionState]):
358
+ return self.state.__class__ is state_cls
359
+
353
360
  @property
354
361
  def state(self):
355
362
  return self._state
@@ -1,2 +1,2 @@
1
1
  __title__ = 'python-socks'
2
- __version__ = '2.8.1'
2
+ __version__ = '2.8.2'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-socks
3
- Version: 2.8.1
3
+ Version: 2.8.2
4
4
  Summary: Proxy (SOCKS4, SOCKS5, HTTP CONNECT) client for Python
5
5
  Author-email: Roman Snegirev <snegiryev@gmail.com>
6
6
  License: Apache-2.0
@@ -0,0 +1,50 @@
1
+ # noinspection PyPackageRequirements
2
+ import pytest
3
+
4
+ from python_socks._helpers import is_ip_address # noqa
5
+ from python_socks._protocols.http import BasicAuth, ConnectRequest # noqa
6
+
7
+
8
+ @pytest.mark.parametrize('address', ('::1', b'::1', '127.0.0.1', b'127.0.0.1'))
9
+ def test_is_ip_address(address):
10
+ assert is_ip_address(address)
11
+
12
+
13
+ def test_basic_auth():
14
+ login = 'login'
15
+ password = 'password'
16
+
17
+ auth1 = BasicAuth(login=login, password=password)
18
+ auth2 = BasicAuth.decode(auth1.encode())
19
+
20
+ assert auth2.login == login
21
+ assert auth2.password == password
22
+
23
+
24
+ def test_connect_request_brackets_ipv6_host():
25
+ """RFC 7230 § 5.4: literal IPv6 in host:port MUST be bracketed."""
26
+ req = ConnectRequest(
27
+ host='2001:db8::1', port=443, username=None, password=None,
28
+ )
29
+ raw = req.dumps()
30
+ first_line = raw.split(b'\r\n', 1)[0]
31
+ assert first_line == b'CONNECT [2001:db8::1]:443 HTTP/1.1'
32
+ assert b'Host: [2001:db8::1]:443\r\n' in raw
33
+
34
+
35
+ def test_connect_request_does_not_bracket_ipv4_host():
36
+ req = ConnectRequest(
37
+ host='192.0.2.1', port=443, username=None, password=None,
38
+ )
39
+ raw = req.dumps()
40
+ assert raw.split(b'\r\n', 1)[0] == b'CONNECT 192.0.2.1:443 HTTP/1.1'
41
+ assert b'Host: 192.0.2.1:443\r\n' in raw
42
+
43
+
44
+ def test_connect_request_does_not_bracket_hostname():
45
+ req = ConnectRequest(
46
+ host='example.com', port=443, username=None, password=None,
47
+ )
48
+ raw = req.dumps()
49
+ assert raw.split(b'\r\n', 1)[0] == b'CONNECT example.com:443 HTTP/1.1'
50
+ assert b'Host: example.com:443\r\n' in raw
@@ -1,21 +0,0 @@
1
- # noinspection PyPackageRequirements
2
- import pytest
3
-
4
- from python_socks._helpers import is_ip_address # noqa
5
- from python_socks._protocols.http import BasicAuth # noqa
6
-
7
-
8
- @pytest.mark.parametrize('address', ('::1', b'::1', '127.0.0.1', b'127.0.0.1'))
9
- def test_is_ip_address(address):
10
- assert is_ip_address(address)
11
-
12
-
13
- def test_basic_auth():
14
- login = 'login'
15
- password = 'password'
16
-
17
- auth1 = BasicAuth(login=login, password=password)
18
- auth2 = BasicAuth.decode(auth1.encode())
19
-
20
- assert auth2.login == login
21
- assert auth2.password == password
File without changes
File without changes
File without changes
File without changes