1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
import asyncio
import sys
import async_timeout
from ._errors import ProxyConnectionError, ProxyTimeoutError
from ._proxy_async import (
AsyncProxy,
Socks4ProxyNegotiator,
Socks5ProxyNegotiator,
HttpProxyNegotiator
)
from ._proxy_factory import ProxyFactory
from ._types import ProxyType
from ._stream_async_aio import AsyncioSocketStream
DEFAULT_TIMEOUT = 60
class AsyncioProxyConnection(AsyncProxy):
def __init__(self, proxy_host, proxy_port,
loop: asyncio.AbstractEventLoop = None):
if loop is None:
loop = asyncio.get_event_loop()
self._loop = loop
self._proxy_host = proxy_host
self._proxy_port = proxy_port
self._dest_host = None
self._dest_port = None
self._timeout = None
self._stream = AsyncioSocketStream(loop=loop)
async def connect(self, dest_host, dest_port, timeout=None, _socket=None):
if timeout is None:
timeout = DEFAULT_TIMEOUT
self._dest_host = dest_host
self._dest_port = dest_port
self._timeout = timeout
try:
await self._connect(_socket=_socket)
except asyncio.TimeoutError as e:
raise ProxyTimeoutError(
'Proxy connection timed out: %s'
% self._timeout) from e
return self._stream.socket
async def _connect(self, _socket=None):
async with async_timeout.timeout(self._timeout):
try:
await self._stream.open_connection(
host=self._proxy_host,
port=self._proxy_port,
timeout=self._timeout,
_socket=_socket
)
except OSError as e:
await self._stream.close()
msg = ('Can not connect to proxy %s:%s [%s]' %
(self._proxy_host, self._proxy_port, e.strerror))
raise ProxyConnectionError(e.errno, msg) from e
except Exception: # pragma: no cover
await self._stream.close()
raise
try:
await self.negotiate()
except asyncio.CancelledError: # pragma: no cover
# https://bugs.python.org/issue30064
# https://bugs.python.org/issue34795
if self._can_be_closed_safely():
await self._stream.close()
raise
except Exception:
await self._stream.close()
raise
def _can_be_closed_safely(self): # pragma: no cover
def is_proactor_event_loop():
try:
from asyncio import ProactorEventLoop # noqa
except ImportError:
return False
return isinstance(self._loop, ProactorEventLoop)
def is_uvloop_event_loop():
try:
from uvloop import Loop # noqa
except ImportError:
return False
return isinstance(self._loop, Loop)
return (sys.version_info[:2] >= (3, 8)
or is_proactor_event_loop()
or is_uvloop_event_loop())
async def negotiate(self):
raise NotImplementedError() # pragma: no cover
@property
def proxy_host(self):
return self._proxy_host
@property
def proxy_port(self):
return self._proxy_port
class Socks5Proxy(Socks5ProxyNegotiator, AsyncioProxyConnection):
def __init__(self, proxy_host, proxy_port,
username=None, password=None, rdns=None,
loop: asyncio.AbstractEventLoop = None):
super().__init__(proxy_host=proxy_host, proxy_port=proxy_port,
loop=loop)
self._username = username
self._password = password
self._rdns = rdns
class Socks4Proxy(Socks4ProxyNegotiator, AsyncioProxyConnection):
def __init__(self, proxy_host, proxy_port,
user_id=None, rdns=None,
loop: asyncio.AbstractEventLoop = None):
super().__init__(proxy_host=proxy_host, proxy_port=proxy_port,
loop=loop)
self._user_id = user_id
self._rdns = rdns
class HttpProxy(HttpProxyNegotiator, AsyncioProxyConnection):
def __init__(self, proxy_host, proxy_port,
username=None, password=None,
loop: asyncio.AbstractEventLoop = None):
super().__init__(proxy_host=proxy_host, proxy_port=proxy_port,
loop=loop)
self._username = username
self._password = password
class Proxy(ProxyFactory):
types = {
ProxyType.SOCKS4: Socks4Proxy,
ProxyType.SOCKS5: Socks5Proxy,
ProxyType.HTTP: HttpProxy,
}
|