Mercurial > repos > jpayne > bioproject_to_srr_2
comparison urllib3/contrib/socks.py @ 7:5eb2d5e3bf22
planemo upload for repository https://toolrepo.galaxytrakr.org/view/jpayne/bioproject_to_srr_2/556cac4fb538
author | jpayne |
---|---|
date | Sun, 05 May 2024 23:32:17 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
6:b2745907b1eb | 7:5eb2d5e3bf22 |
---|---|
1 """ | |
2 This module contains provisional support for SOCKS proxies from within | |
3 urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and | |
4 SOCKS5. To enable its functionality, either install PySocks or install this | |
5 module with the ``socks`` extra. | |
6 | |
7 The SOCKS implementation supports the full range of urllib3 features. It also | |
8 supports the following SOCKS features: | |
9 | |
10 - SOCKS4A (``proxy_url='socks4a://...``) | |
11 - SOCKS4 (``proxy_url='socks4://...``) | |
12 - SOCKS5 with remote DNS (``proxy_url='socks5h://...``) | |
13 - SOCKS5 with local DNS (``proxy_url='socks5://...``) | |
14 - Usernames and passwords for the SOCKS proxy | |
15 | |
16 .. note:: | |
17 It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in | |
18 your ``proxy_url`` to ensure that DNS resolution is done from the remote | |
19 server instead of client-side when connecting to a domain name. | |
20 | |
21 SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 | |
22 supports IPv4, IPv6, and domain names. | |
23 | |
24 When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` | |
25 will be sent as the ``userid`` section of the SOCKS request: | |
26 | |
27 .. code-block:: python | |
28 | |
29 proxy_url="socks4a://<userid>@proxy-host" | |
30 | |
31 When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion | |
32 of the ``proxy_url`` will be sent as the username/password to authenticate | |
33 with the proxy: | |
34 | |
35 .. code-block:: python | |
36 | |
37 proxy_url="socks5h://<username>:<password>@proxy-host" | |
38 | |
39 """ | |
40 | |
41 from __future__ import annotations | |
42 | |
43 try: | |
44 import socks # type: ignore[import-not-found] | |
45 except ImportError: | |
46 import warnings | |
47 | |
48 from ..exceptions import DependencyWarning | |
49 | |
50 warnings.warn( | |
51 ( | |
52 "SOCKS support in urllib3 requires the installation of optional " | |
53 "dependencies: specifically, PySocks. For more information, see " | |
54 "https://urllib3.readthedocs.io/en/latest/advanced-usage.html#socks-proxies" | |
55 ), | |
56 DependencyWarning, | |
57 ) | |
58 raise | |
59 | |
60 import typing | |
61 from socket import timeout as SocketTimeout | |
62 | |
63 from ..connection import HTTPConnection, HTTPSConnection | |
64 from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool | |
65 from ..exceptions import ConnectTimeoutError, NewConnectionError | |
66 from ..poolmanager import PoolManager | |
67 from ..util.url import parse_url | |
68 | |
69 try: | |
70 import ssl | |
71 except ImportError: | |
72 ssl = None # type: ignore[assignment] | |
73 | |
74 from typing import TypedDict | |
75 | |
76 | |
77 class _TYPE_SOCKS_OPTIONS(TypedDict): | |
78 socks_version: int | |
79 proxy_host: str | None | |
80 proxy_port: str | None | |
81 username: str | None | |
82 password: str | None | |
83 rdns: bool | |
84 | |
85 | |
86 class SOCKSConnection(HTTPConnection): | |
87 """ | |
88 A plain-text HTTP connection that connects via a SOCKS proxy. | |
89 """ | |
90 | |
91 def __init__( | |
92 self, | |
93 _socks_options: _TYPE_SOCKS_OPTIONS, | |
94 *args: typing.Any, | |
95 **kwargs: typing.Any, | |
96 ) -> None: | |
97 self._socks_options = _socks_options | |
98 super().__init__(*args, **kwargs) | |
99 | |
100 def _new_conn(self) -> socks.socksocket: | |
101 """ | |
102 Establish a new connection via the SOCKS proxy. | |
103 """ | |
104 extra_kw: dict[str, typing.Any] = {} | |
105 if self.source_address: | |
106 extra_kw["source_address"] = self.source_address | |
107 | |
108 if self.socket_options: | |
109 extra_kw["socket_options"] = self.socket_options | |
110 | |
111 try: | |
112 conn = socks.create_connection( | |
113 (self.host, self.port), | |
114 proxy_type=self._socks_options["socks_version"], | |
115 proxy_addr=self._socks_options["proxy_host"], | |
116 proxy_port=self._socks_options["proxy_port"], | |
117 proxy_username=self._socks_options["username"], | |
118 proxy_password=self._socks_options["password"], | |
119 proxy_rdns=self._socks_options["rdns"], | |
120 timeout=self.timeout, | |
121 **extra_kw, | |
122 ) | |
123 | |
124 except SocketTimeout as e: | |
125 raise ConnectTimeoutError( | |
126 self, | |
127 f"Connection to {self.host} timed out. (connect timeout={self.timeout})", | |
128 ) from e | |
129 | |
130 except socks.ProxyError as e: | |
131 # This is fragile as hell, but it seems to be the only way to raise | |
132 # useful errors here. | |
133 if e.socket_err: | |
134 error = e.socket_err | |
135 if isinstance(error, SocketTimeout): | |
136 raise ConnectTimeoutError( | |
137 self, | |
138 f"Connection to {self.host} timed out. (connect timeout={self.timeout})", | |
139 ) from e | |
140 else: | |
141 # Adding `from e` messes with coverage somehow, so it's omitted. | |
142 # See #2386. | |
143 raise NewConnectionError( | |
144 self, f"Failed to establish a new connection: {error}" | |
145 ) | |
146 else: | |
147 raise NewConnectionError( | |
148 self, f"Failed to establish a new connection: {e}" | |
149 ) from e | |
150 | |
151 except OSError as e: # Defensive: PySocks should catch all these. | |
152 raise NewConnectionError( | |
153 self, f"Failed to establish a new connection: {e}" | |
154 ) from e | |
155 | |
156 return conn | |
157 | |
158 | |
159 # We don't need to duplicate the Verified/Unverified distinction from | |
160 # urllib3/connection.py here because the HTTPSConnection will already have been | |
161 # correctly set to either the Verified or Unverified form by that module. This | |
162 # means the SOCKSHTTPSConnection will automatically be the correct type. | |
163 class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): | |
164 pass | |
165 | |
166 | |
167 class SOCKSHTTPConnectionPool(HTTPConnectionPool): | |
168 ConnectionCls = SOCKSConnection | |
169 | |
170 | |
171 class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): | |
172 ConnectionCls = SOCKSHTTPSConnection | |
173 | |
174 | |
175 class SOCKSProxyManager(PoolManager): | |
176 """ | |
177 A version of the urllib3 ProxyManager that routes connections via the | |
178 defined SOCKS proxy. | |
179 """ | |
180 | |
181 pool_classes_by_scheme = { | |
182 "http": SOCKSHTTPConnectionPool, | |
183 "https": SOCKSHTTPSConnectionPool, | |
184 } | |
185 | |
186 def __init__( | |
187 self, | |
188 proxy_url: str, | |
189 username: str | None = None, | |
190 password: str | None = None, | |
191 num_pools: int = 10, | |
192 headers: typing.Mapping[str, str] | None = None, | |
193 **connection_pool_kw: typing.Any, | |
194 ): | |
195 parsed = parse_url(proxy_url) | |
196 | |
197 if username is None and password is None and parsed.auth is not None: | |
198 split = parsed.auth.split(":") | |
199 if len(split) == 2: | |
200 username, password = split | |
201 if parsed.scheme == "socks5": | |
202 socks_version = socks.PROXY_TYPE_SOCKS5 | |
203 rdns = False | |
204 elif parsed.scheme == "socks5h": | |
205 socks_version = socks.PROXY_TYPE_SOCKS5 | |
206 rdns = True | |
207 elif parsed.scheme == "socks4": | |
208 socks_version = socks.PROXY_TYPE_SOCKS4 | |
209 rdns = False | |
210 elif parsed.scheme == "socks4a": | |
211 socks_version = socks.PROXY_TYPE_SOCKS4 | |
212 rdns = True | |
213 else: | |
214 raise ValueError(f"Unable to determine SOCKS version from {proxy_url}") | |
215 | |
216 self.proxy_url = proxy_url | |
217 | |
218 socks_options = { | |
219 "socks_version": socks_version, | |
220 "proxy_host": parsed.host, | |
221 "proxy_port": parsed.port, | |
222 "username": username, | |
223 "password": password, | |
224 "rdns": rdns, | |
225 } | |
226 connection_pool_kw["_socks_options"] = socks_options | |
227 | |
228 super().__init__(num_pools, headers, **connection_pool_kw) | |
229 | |
230 self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme |