fix: 修复代理问题
This commit is contained in:
23
.venv/lib/python3.9/site-packages/httpx_socks/__init__.py
Normal file
23
.venv/lib/python3.9/site-packages/httpx_socks/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
__title__ = 'httpx-socks'
|
||||
__version__ = '0.9.2'
|
||||
|
||||
from python_socks import (
|
||||
ProxyError,
|
||||
ProxyTimeoutError,
|
||||
ProxyConnectionError,
|
||||
ProxyType
|
||||
)
|
||||
|
||||
from ._sync_transport import SyncProxyTransport
|
||||
from ._async_transport import AsyncProxyTransport
|
||||
|
||||
__all__ = (
|
||||
'__title__',
|
||||
'__version__',
|
||||
'SyncProxyTransport',
|
||||
'AsyncProxyTransport',
|
||||
'ProxyError',
|
||||
'ProxyTimeoutError',
|
||||
'ProxyConnectionError',
|
||||
'ProxyType',
|
||||
)
|
||||
258
.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py
Normal file
258
.venv/lib/python3.9/site-packages/httpx_socks/_async_proxy.py
Normal file
@@ -0,0 +1,258 @@
|
||||
import ssl
|
||||
|
||||
import sniffio
|
||||
from httpcore import (
|
||||
AsyncConnectionPool,
|
||||
Origin,
|
||||
AsyncConnectionInterface,
|
||||
Request,
|
||||
Response,
|
||||
default_ssl_context,
|
||||
AsyncHTTP11Connection,
|
||||
ConnectionNotAvailable,
|
||||
)
|
||||
from httpcore import AsyncNetworkStream
|
||||
from httpcore._synchronization import AsyncLock
|
||||
from python_socks import ProxyType, parse_proxy_url
|
||||
|
||||
|
||||
class AsyncProxy(AsyncConnectionPool):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
proxy_type: ProxyType,
|
||||
proxy_host: str,
|
||||
proxy_port: int,
|
||||
username=None,
|
||||
password=None,
|
||||
rdns=None,
|
||||
proxy_ssl: ssl.SSLContext = None,
|
||||
loop=None,
|
||||
**kwargs,
|
||||
):
|
||||
self._proxy_type = proxy_type
|
||||
self._proxy_host = proxy_host
|
||||
self._proxy_port = proxy_port
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._rdns = rdns
|
||||
self._proxy_ssl = proxy_ssl
|
||||
self._loop = loop
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def create_connection(self, origin: Origin) -> AsyncConnectionInterface:
|
||||
return AsyncProxyConnection(
|
||||
proxy_type=self._proxy_type,
|
||||
proxy_host=self._proxy_host,
|
||||
proxy_port=self._proxy_port,
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
rdns=self._rdns,
|
||||
proxy_ssl=self._proxy_ssl,
|
||||
loop=self._loop,
|
||||
remote_origin=origin,
|
||||
ssl_context=self._ssl_context,
|
||||
keepalive_expiry=self._keepalive_expiry,
|
||||
http1=self._http1,
|
||||
http2=self._http2,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_url(cls, url, **kwargs):
|
||||
proxy_type, host, port, username, password = parse_proxy_url(url)
|
||||
return cls(
|
||||
proxy_type=proxy_type,
|
||||
proxy_host=host,
|
||||
proxy_port=port,
|
||||
username=username,
|
||||
password=password,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class AsyncProxyConnection(AsyncConnectionInterface):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
proxy_type: ProxyType,
|
||||
proxy_host: str,
|
||||
proxy_port: int,
|
||||
username=None,
|
||||
password=None,
|
||||
rdns=None,
|
||||
proxy_ssl: ssl.SSLContext = None,
|
||||
loop=None,
|
||||
remote_origin: Origin,
|
||||
ssl_context: ssl.SSLContext,
|
||||
keepalive_expiry: float = None,
|
||||
http1: bool = True,
|
||||
http2: bool = False,
|
||||
) -> None:
|
||||
|
||||
if ssl_context is None: # pragma: no cover
|
||||
ssl_context = default_ssl_context()
|
||||
|
||||
self._proxy_type = proxy_type
|
||||
self._proxy_host = proxy_host
|
||||
self._proxy_port = proxy_port
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._rdns = rdns
|
||||
self._proxy_ssl = proxy_ssl
|
||||
self._loop = loop
|
||||
|
||||
self._remote_origin = remote_origin
|
||||
self._ssl_context = ssl_context
|
||||
self._keepalive_expiry = keepalive_expiry
|
||||
self._http1 = http1
|
||||
self._http2 = http2
|
||||
|
||||
self._connect_lock = AsyncLock()
|
||||
self._connection = None
|
||||
self._connect_failed: bool = False
|
||||
|
||||
async def handle_async_request(self, request: Request) -> Response:
|
||||
timeouts = request.extensions.get('timeout', {})
|
||||
timeout = timeouts.get('connect', None)
|
||||
|
||||
try:
|
||||
async with self._connect_lock:
|
||||
if self._connection is None:
|
||||
stream = await self._connect_via_proxy(
|
||||
origin=self._remote_origin,
|
||||
connect_timeout=timeout,
|
||||
)
|
||||
|
||||
ssl_object = stream.get_extra_info("ssl_object")
|
||||
http2_negotiated = (
|
||||
ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2"
|
||||
)
|
||||
if http2_negotiated or (self._http2 and not self._http1):
|
||||
from httpcore import AsyncHTTP2Connection
|
||||
|
||||
self._connection = AsyncHTTP2Connection(
|
||||
origin=self._remote_origin,
|
||||
stream=stream,
|
||||
keepalive_expiry=self._keepalive_expiry,
|
||||
)
|
||||
else:
|
||||
self._connection = AsyncHTTP11Connection(
|
||||
origin=self._remote_origin,
|
||||
stream=stream,
|
||||
keepalive_expiry=self._keepalive_expiry,
|
||||
)
|
||||
elif not self._connection.is_available(): # pragma: no cover
|
||||
raise ConnectionNotAvailable()
|
||||
except BaseException as exc:
|
||||
self._connect_failed = True
|
||||
raise exc
|
||||
|
||||
return await self._connection.handle_async_request(request)
|
||||
|
||||
async def _connect_via_proxy(self, origin, connect_timeout) -> AsyncNetworkStream:
|
||||
scheme, hostname, port = origin.scheme, origin.host, origin.port
|
||||
|
||||
ssl_context = self._ssl_context if scheme == b'https' else None
|
||||
host = hostname.decode('ascii') # ?
|
||||
|
||||
return await self._open_stream(
|
||||
host=host,
|
||||
port=port,
|
||||
connect_timeout=connect_timeout,
|
||||
ssl_context=ssl_context,
|
||||
)
|
||||
|
||||
async def _open_stream(self, host, port, connect_timeout, ssl_context):
|
||||
backend = sniffio.current_async_library()
|
||||
|
||||
if backend == 'asyncio':
|
||||
return await self._open_aio_stream(host, port, connect_timeout, ssl_context)
|
||||
|
||||
if backend == 'trio':
|
||||
return await self._open_trio_stream(host, port, connect_timeout, ssl_context)
|
||||
|
||||
# Curio support has been dropped in httpcore 0.14.0
|
||||
# if backend == 'curio':
|
||||
# return await self._open_curio_stream(host, port, connect_timeout, ssl_context)
|
||||
|
||||
raise RuntimeError(f'Unsupported concurrency backend {backend!r}') # pragma: no cover
|
||||
|
||||
async def _open_aio_stream(self, host, port, connect_timeout, ssl_context):
|
||||
from httpcore._backends.anyio import AnyIOStream
|
||||
from python_socks.async_.anyio import Proxy
|
||||
|
||||
proxy = Proxy.create(
|
||||
proxy_type=self._proxy_type,
|
||||
host=self._proxy_host,
|
||||
port=self._proxy_port,
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
rdns=self._rdns,
|
||||
proxy_ssl=self._proxy_ssl,
|
||||
)
|
||||
|
||||
proxy_stream = await proxy.connect(
|
||||
host,
|
||||
port,
|
||||
dest_ssl=ssl_context,
|
||||
timeout=connect_timeout,
|
||||
)
|
||||
|
||||
return AnyIOStream(proxy_stream.anyio_stream)
|
||||
|
||||
async def _open_trio_stream(self, host, port, connect_timeout, ssl_context):
|
||||
from httpcore._backends.trio import TrioStream
|
||||
from python_socks.async_.trio.v2 import Proxy
|
||||
|
||||
proxy = Proxy.create(
|
||||
proxy_type=self._proxy_type,
|
||||
host=self._proxy_host,
|
||||
port=self._proxy_port,
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
rdns=self._rdns,
|
||||
proxy_ssl=self._proxy_ssl,
|
||||
)
|
||||
|
||||
proxy_stream = await proxy.connect(
|
||||
host,
|
||||
port,
|
||||
dest_ssl=ssl_context,
|
||||
timeout=connect_timeout,
|
||||
)
|
||||
|
||||
return TrioStream(proxy_stream.trio_stream)
|
||||
|
||||
async def aclose(self) -> None:
|
||||
if self._connection is not None:
|
||||
await self._connection.aclose()
|
||||
|
||||
def can_handle_request(self, origin: Origin) -> bool:
|
||||
return origin == self._remote_origin
|
||||
|
||||
def is_available(self) -> bool:
|
||||
if self._connection is None: # pragma: no cover
|
||||
# return self._http2 and (self._remote_origin.scheme == b"https" or not self._http1)
|
||||
return False
|
||||
return self._connection.is_available()
|
||||
|
||||
def has_expired(self) -> bool:
|
||||
if self._connection is None:
|
||||
return self._connect_failed
|
||||
return self._connection.has_expired()
|
||||
|
||||
def is_idle(self) -> bool:
|
||||
if self._connection is None:
|
||||
return self._connect_failed
|
||||
return self._connection.is_idle()
|
||||
|
||||
def is_closed(self) -> bool:
|
||||
if self._connection is None:
|
||||
return self._connect_failed
|
||||
return self._connection.is_closed()
|
||||
|
||||
def info(self) -> str: # pragma: no cover
|
||||
if self._connection is None:
|
||||
return "CONNECTION FAILED" if self._connect_failed else "CONNECTING"
|
||||
return self._connection.info()
|
||||
@@ -0,0 +1,104 @@
|
||||
import ssl
|
||||
import typing
|
||||
|
||||
import httpcore
|
||||
from httpx import AsyncBaseTransport, Request, Response, AsyncByteStream, Limits
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
from httpx._config import DEFAULT_LIMITS, create_ssl_context
|
||||
# noinspection PyProtectedMember
|
||||
from httpx._transports.default import AsyncResponseStream, map_httpcore_exceptions
|
||||
from python_socks import ProxyType, parse_proxy_url
|
||||
|
||||
from ._async_proxy import AsyncProxy
|
||||
|
||||
|
||||
class AsyncProxyTransport(AsyncBaseTransport):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
proxy_type: ProxyType,
|
||||
proxy_host: str,
|
||||
proxy_port: int,
|
||||
username=None,
|
||||
password=None,
|
||||
rdns=None,
|
||||
proxy_ssl: ssl.SSLContext = None,
|
||||
verify=True,
|
||||
cert=None,
|
||||
trust_env: bool = True,
|
||||
limits: Limits = DEFAULT_LIMITS,
|
||||
**kwargs,
|
||||
):
|
||||
ssl_context = create_ssl_context(
|
||||
verify=verify,
|
||||
cert=cert,
|
||||
trust_env=trust_env,
|
||||
http2=kwargs.get('http2', False),
|
||||
)
|
||||
|
||||
self._pool = AsyncProxy(
|
||||
proxy_type=proxy_type,
|
||||
proxy_host=proxy_host,
|
||||
proxy_port=proxy_port,
|
||||
username=username,
|
||||
password=password,
|
||||
rdns=rdns,
|
||||
proxy_ssl=proxy_ssl,
|
||||
ssl_context=ssl_context,
|
||||
max_connections=limits.max_connections,
|
||||
max_keepalive_connections=limits.max_keepalive_connections,
|
||||
keepalive_expiry=limits.keepalive_expiry,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
async def handle_async_request(self, request: Request) -> Response:
|
||||
assert isinstance(request.stream, AsyncByteStream)
|
||||
|
||||
req = httpcore.Request(
|
||||
method=request.method,
|
||||
url=httpcore.URL(
|
||||
scheme=request.url.raw_scheme,
|
||||
host=request.url.raw_host,
|
||||
port=request.url.port,
|
||||
target=request.url.raw_path,
|
||||
),
|
||||
headers=request.headers.raw,
|
||||
content=request.stream,
|
||||
extensions=request.extensions,
|
||||
)
|
||||
|
||||
with map_httpcore_exceptions():
|
||||
resp = await self._pool.handle_async_request(req)
|
||||
|
||||
assert isinstance(resp.stream, typing.AsyncIterable)
|
||||
|
||||
return Response(
|
||||
status_code=resp.status,
|
||||
headers=resp.headers,
|
||||
stream=AsyncResponseStream(resp.stream),
|
||||
extensions=resp.extensions,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_url(cls, url, **kwargs):
|
||||
proxy_type, host, port, username, password = parse_proxy_url(url)
|
||||
return cls(
|
||||
proxy_type=proxy_type,
|
||||
proxy_host=host,
|
||||
proxy_port=port,
|
||||
username=username,
|
||||
password=password,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
async def aclose(self) -> None:
|
||||
await self._pool.aclose() # pragma: no cover
|
||||
|
||||
async def __aenter__(self):
|
||||
await self._pool.__aenter__()
|
||||
return self
|
||||
|
||||
async def __aexit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
with map_httpcore_exceptions():
|
||||
await self._pool.__aexit__(exc_type, exc_value, traceback)
|
||||
206
.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py
Normal file
206
.venv/lib/python3.9/site-packages/httpx_socks/_sync_proxy.py
Normal file
@@ -0,0 +1,206 @@
|
||||
import ssl
|
||||
|
||||
from httpcore import (
|
||||
ConnectionPool,
|
||||
Origin,
|
||||
ConnectionInterface,
|
||||
Request,
|
||||
Response,
|
||||
default_ssl_context,
|
||||
HTTP11Connection,
|
||||
ConnectionNotAvailable,
|
||||
)
|
||||
# from httpcore.backends.sync import SyncStream
|
||||
from ._sync_stream import SyncStream
|
||||
from httpcore._synchronization import Lock
|
||||
|
||||
from python_socks import ProxyType, parse_proxy_url
|
||||
from python_socks.sync.v2 import Proxy
|
||||
|
||||
|
||||
class SyncProxy(ConnectionPool):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
proxy_type: ProxyType,
|
||||
proxy_host: str,
|
||||
proxy_port: int,
|
||||
username=None,
|
||||
password=None,
|
||||
rdns=None,
|
||||
proxy_ssl: ssl.SSLContext = None,
|
||||
**kwargs,
|
||||
):
|
||||
self._proxy_type = proxy_type
|
||||
self._proxy_host = proxy_host
|
||||
self._proxy_port = proxy_port
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._rdns = rdns
|
||||
self._proxy_ssl = proxy_ssl
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def create_connection(self, origin: Origin) -> ConnectionInterface:
|
||||
return SyncProxyConnection(
|
||||
proxy_type=self._proxy_type,
|
||||
proxy_host=self._proxy_host,
|
||||
proxy_port=self._proxy_port,
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
rdns=self._rdns,
|
||||
proxy_ssl=self._proxy_ssl,
|
||||
remote_origin=origin,
|
||||
ssl_context=self._ssl_context,
|
||||
keepalive_expiry=self._keepalive_expiry,
|
||||
http1=self._http1,
|
||||
http2=self._http2,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_url(cls, url, **kwargs):
|
||||
proxy_type, host, port, username, password = parse_proxy_url(url)
|
||||
return cls(
|
||||
proxy_type=proxy_type,
|
||||
proxy_host=host,
|
||||
proxy_port=port,
|
||||
username=username,
|
||||
password=password,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
class SyncProxyConnection(ConnectionInterface):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
proxy_type: ProxyType,
|
||||
proxy_host: str,
|
||||
proxy_port: int,
|
||||
username=None,
|
||||
password=None,
|
||||
rdns=None,
|
||||
proxy_ssl: ssl.SSLContext = None,
|
||||
remote_origin: Origin,
|
||||
ssl_context: ssl.SSLContext,
|
||||
keepalive_expiry: float = None,
|
||||
http1: bool = True,
|
||||
http2: bool = False,
|
||||
) -> None:
|
||||
|
||||
if ssl_context is None: # pragma: no cover
|
||||
ssl_context = default_ssl_context()
|
||||
|
||||
self._proxy_type = proxy_type
|
||||
self._proxy_host = proxy_host
|
||||
self._proxy_port = proxy_port
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._rdns = rdns
|
||||
self._proxy_ssl = proxy_ssl
|
||||
|
||||
self._remote_origin = remote_origin
|
||||
self._ssl_context = ssl_context
|
||||
self._keepalive_expiry = keepalive_expiry
|
||||
self._http1 = http1
|
||||
self._http2 = http2
|
||||
|
||||
self._connect_lock = Lock()
|
||||
self._connection = None
|
||||
self._connect_failed: bool = False
|
||||
|
||||
def handle_request(self, request: Request) -> Response:
|
||||
timeouts = request.extensions.get('timeout', {})
|
||||
timeout = timeouts.get('connect', None)
|
||||
|
||||
try:
|
||||
with self._connect_lock:
|
||||
if self._connection is None:
|
||||
stream = self._connect_via_proxy(
|
||||
origin=self._remote_origin,
|
||||
connect_timeout=timeout,
|
||||
)
|
||||
|
||||
ssl_object = stream.get_extra_info('ssl_object')
|
||||
http2_negotiated = (
|
||||
ssl_object is not None and ssl_object.selected_alpn_protocol() == "h2"
|
||||
)
|
||||
if http2_negotiated or (self._http2 and not self._http1):
|
||||
from httpcore import HTTP2Connection
|
||||
|
||||
self._connection = HTTP2Connection(
|
||||
origin=self._remote_origin,
|
||||
stream=stream,
|
||||
keepalive_expiry=self._keepalive_expiry,
|
||||
)
|
||||
else:
|
||||
self._connection = HTTP11Connection(
|
||||
origin=self._remote_origin,
|
||||
stream=stream,
|
||||
keepalive_expiry=self._keepalive_expiry,
|
||||
)
|
||||
elif not self._connection.is_available(): # pragma: no cover
|
||||
raise ConnectionNotAvailable()
|
||||
except BaseException as exc:
|
||||
self._connect_failed = True
|
||||
raise exc
|
||||
|
||||
return self._connection.handle_request(request)
|
||||
|
||||
def _connect_via_proxy(self, origin: Origin, connect_timeout: int):
|
||||
scheme, hostname, port = origin.scheme, origin.host, origin.port
|
||||
|
||||
ssl_context = self._ssl_context if scheme == b'https' else None
|
||||
host = hostname.decode('ascii')
|
||||
|
||||
proxy = Proxy.create(
|
||||
proxy_type=self._proxy_type,
|
||||
host=self._proxy_host,
|
||||
port=self._proxy_port,
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
rdns=self._rdns,
|
||||
proxy_ssl=self._proxy_ssl,
|
||||
)
|
||||
|
||||
proxy_stream = proxy.connect(
|
||||
host,
|
||||
port,
|
||||
dest_ssl=ssl_context,
|
||||
timeout=connect_timeout,
|
||||
)
|
||||
|
||||
return SyncStream(sock=proxy_stream.socket)
|
||||
|
||||
def close(self) -> None:
|
||||
if self._connection is not None:
|
||||
self._connection.close()
|
||||
|
||||
def can_handle_request(self, origin: Origin) -> bool:
|
||||
return origin == self._remote_origin
|
||||
|
||||
def is_available(self) -> bool:
|
||||
if self._connection is None: # pragma: no cover
|
||||
# return self._http2 and (self._remote_origin.scheme == b"https" or not self._http1)
|
||||
return False
|
||||
return self._connection.is_available()
|
||||
|
||||
def has_expired(self) -> bool:
|
||||
if self._connection is None:
|
||||
return self._connect_failed
|
||||
return self._connection.has_expired()
|
||||
|
||||
def is_idle(self) -> bool:
|
||||
if self._connection is None:
|
||||
return self._connect_failed
|
||||
return self._connection.is_idle()
|
||||
|
||||
def is_closed(self) -> bool:
|
||||
if self._connection is None:
|
||||
return self._connect_failed
|
||||
return self._connection.is_closed()
|
||||
|
||||
def info(self) -> str: # pragma: no cover
|
||||
if self._connection is None:
|
||||
return "CONNECTION FAILED" if self._connect_failed else "CONNECTING"
|
||||
return self._connection.info()
|
||||
@@ -0,0 +1,40 @@
|
||||
import ssl
|
||||
import typing
|
||||
|
||||
from httpcore._backends.sync import SyncStream as CoreSyncStream
|
||||
from httpcore._utils import is_socket_readable
|
||||
from python_socks.sync.v2._ssl_transport import SSLTransport
|
||||
|
||||
|
||||
class SyncStream(CoreSyncStream):
|
||||
def get_extra_info(self, info: str) -> typing.Any:
|
||||
if info == "ssl_object":
|
||||
if isinstance(self._sock, ssl.SSLSocket):
|
||||
# noinspection PyProtectedMember
|
||||
return self._sock._sslobj # type: ignore
|
||||
if isinstance(self._sock, SSLTransport):
|
||||
return self._sock.sslobj # type: ignore
|
||||
return None
|
||||
|
||||
if info == "client_addr": # pragma: nocover
|
||||
if isinstance(self._sock, SSLTransport):
|
||||
return self._sock.socket.getsockname()
|
||||
else:
|
||||
return self._sock.getsockname()
|
||||
|
||||
if info == "server_addr": # pragma: nocover
|
||||
if isinstance(self._sock, SSLTransport):
|
||||
return self._sock.socket.getpeername()
|
||||
else:
|
||||
return self._sock.getpeername()
|
||||
|
||||
if info == "socket": # pragma: nocover
|
||||
return self._sock # ???
|
||||
|
||||
if info == "is_readable":
|
||||
if isinstance(self._sock, SSLTransport):
|
||||
return is_socket_readable(self._sock.socket)
|
||||
else:
|
||||
return is_socket_readable(self._sock)
|
||||
|
||||
return None # pragma: nocover
|
||||
105
.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py
Normal file
105
.venv/lib/python3.9/site-packages/httpx_socks/_sync_transport.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import ssl
|
||||
import typing
|
||||
|
||||
import httpcore
|
||||
|
||||
from httpx import BaseTransport, Request, Response, SyncByteStream, Limits
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
from httpx._config import DEFAULT_LIMITS, create_ssl_context
|
||||
# noinspection PyProtectedMember
|
||||
from httpx._transports.default import ResponseStream, map_httpcore_exceptions
|
||||
|
||||
from ._sync_proxy import SyncProxy
|
||||
from python_socks import ProxyType, parse_proxy_url
|
||||
|
||||
|
||||
class SyncProxyTransport(BaseTransport):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
proxy_type: ProxyType,
|
||||
proxy_host: str,
|
||||
proxy_port: int,
|
||||
username=None,
|
||||
password=None,
|
||||
rdns=None,
|
||||
proxy_ssl: ssl.SSLContext = None,
|
||||
verify=True,
|
||||
cert=None,
|
||||
trust_env: bool = True,
|
||||
limits: Limits = DEFAULT_LIMITS,
|
||||
**kwargs,
|
||||
):
|
||||
ssl_context = create_ssl_context(
|
||||
verify=verify,
|
||||
cert=cert,
|
||||
trust_env=trust_env,
|
||||
http2=kwargs.get('http2', False),
|
||||
)
|
||||
|
||||
self._pool = SyncProxy(
|
||||
proxy_type=proxy_type,
|
||||
proxy_host=proxy_host,
|
||||
proxy_port=proxy_port,
|
||||
username=username,
|
||||
password=password,
|
||||
rdns=rdns,
|
||||
proxy_ssl=proxy_ssl,
|
||||
ssl_context=ssl_context,
|
||||
max_connections=limits.max_connections,
|
||||
max_keepalive_connections=limits.max_keepalive_connections,
|
||||
keepalive_expiry=limits.keepalive_expiry,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def handle_request(self, request: Request) -> Response:
|
||||
assert isinstance(request.stream, SyncByteStream)
|
||||
|
||||
req = httpcore.Request(
|
||||
method=request.method,
|
||||
url=httpcore.URL(
|
||||
scheme=request.url.raw_scheme,
|
||||
host=request.url.raw_host,
|
||||
port=request.url.port,
|
||||
target=request.url.raw_path,
|
||||
),
|
||||
headers=request.headers.raw,
|
||||
content=request.stream,
|
||||
extensions=request.extensions,
|
||||
)
|
||||
|
||||
with map_httpcore_exceptions():
|
||||
resp = self._pool.handle_request(req)
|
||||
|
||||
assert isinstance(resp.stream, typing.Iterable)
|
||||
|
||||
return Response(
|
||||
status_code=resp.status,
|
||||
headers=resp.headers,
|
||||
stream=ResponseStream(resp.stream),
|
||||
extensions=resp.extensions,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_url(cls, url, **kwargs):
|
||||
proxy_type, host, port, username, password = parse_proxy_url(url)
|
||||
return cls(
|
||||
proxy_type=proxy_type,
|
||||
proxy_host=host,
|
||||
proxy_port=port,
|
||||
username=username,
|
||||
password=password,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def close(self) -> None:
|
||||
self._pool.close() # pragma: no cover
|
||||
|
||||
def __enter__(self):
|
||||
self._pool.__enter__()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
with map_httpcore_exceptions():
|
||||
self._pool.__exit__(exc_type, exc_value, traceback)
|
||||
Reference in New Issue
Block a user