fix: 修复代理问题
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from ._proxy import CurioProxy as Proxy
|
||||
|
||||
|
||||
__all__ = ('Proxy',)
|
||||
@@ -0,0 +1,17 @@
|
||||
from typing import Optional, Tuple
|
||||
|
||||
import curio
|
||||
import curio.io
|
||||
import curio.socket
|
||||
|
||||
|
||||
async def connect_tcp(
|
||||
host: str,
|
||||
port: int,
|
||||
local_addr: Optional[Tuple[str, int]] = None,
|
||||
) -> curio.io.Socket:
|
||||
return await curio.open_connection(
|
||||
host=host,
|
||||
port=port,
|
||||
source_addr=local_addr,
|
||||
)
|
||||
@@ -0,0 +1,132 @@
|
||||
from typing import Any, Optional
|
||||
import warnings
|
||||
import curio
|
||||
import curio.io
|
||||
|
||||
from ..._types import ProxyType
|
||||
from ..._helpers import parse_proxy_url
|
||||
from ..._errors import ProxyConnectionError, ProxyTimeoutError, ProxyError
|
||||
|
||||
from ._stream import CurioSocketStream
|
||||
from ._resolver import Resolver
|
||||
from ._connect import connect_tcp
|
||||
|
||||
from ..._protocols.errors import ReplyError
|
||||
from ..._connectors.factory_async import create_connector
|
||||
|
||||
|
||||
DEFAULT_TIMEOUT = 60
|
||||
|
||||
|
||||
class CurioProxy:
|
||||
def __init__(
|
||||
self,
|
||||
proxy_type: ProxyType,
|
||||
host: str,
|
||||
port: int,
|
||||
username: Optional[str] = None,
|
||||
password: Optional[str] = None,
|
||||
rdns: Optional[bool] = None,
|
||||
):
|
||||
self._proxy_type = proxy_type
|
||||
self._proxy_host = host
|
||||
self._proxy_port = port
|
||||
self._password = password
|
||||
self._username = username
|
||||
self._rdns = rdns
|
||||
|
||||
self._resolver = Resolver()
|
||||
|
||||
async def connect(
|
||||
self,
|
||||
dest_host: str,
|
||||
dest_port: int,
|
||||
timeout: Optional[float] = None,
|
||||
**kwargs: Any,
|
||||
) -> curio.io.Socket:
|
||||
if timeout is None:
|
||||
timeout = DEFAULT_TIMEOUT
|
||||
|
||||
_socket = kwargs.get('_socket')
|
||||
if _socket is not None:
|
||||
warnings.warn(
|
||||
"The '_socket' argument is deprecated and will be removed in the future",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
local_addr = kwargs.get('local_addr')
|
||||
try:
|
||||
return await curio.timeout_after(
|
||||
timeout,
|
||||
self._connect,
|
||||
dest_host,
|
||||
dest_port,
|
||||
_socket,
|
||||
local_addr,
|
||||
)
|
||||
except curio.TaskTimeout as e:
|
||||
raise ProxyTimeoutError(f'Proxy connection timed out: {timeout}') from e
|
||||
|
||||
async def _connect(
|
||||
self,
|
||||
dest_host: str,
|
||||
dest_port: int,
|
||||
_socket=None,
|
||||
local_addr=None,
|
||||
):
|
||||
if _socket is None:
|
||||
try:
|
||||
_socket = await connect_tcp(
|
||||
host=self._proxy_host,
|
||||
port=self._proxy_port,
|
||||
local_addr=local_addr,
|
||||
)
|
||||
except OSError as e:
|
||||
msg = 'Could not connect to proxy {}:{} [{}]'.format(
|
||||
self._proxy_host,
|
||||
self._proxy_port,
|
||||
e.strerror,
|
||||
)
|
||||
raise ProxyConnectionError(e.errno, msg) from e
|
||||
|
||||
stream = CurioSocketStream(_socket)
|
||||
|
||||
try:
|
||||
connector = create_connector(
|
||||
proxy_type=self._proxy_type,
|
||||
username=self._username,
|
||||
password=self._password,
|
||||
rdns=self._rdns,
|
||||
resolver=self._resolver,
|
||||
)
|
||||
await connector.connect(
|
||||
stream=stream,
|
||||
host=dest_host,
|
||||
port=dest_port,
|
||||
)
|
||||
return _socket
|
||||
|
||||
except ReplyError as e:
|
||||
await stream.close()
|
||||
raise ProxyError(e, error_code=e.error_code)
|
||||
except BaseException:
|
||||
await stream.close()
|
||||
raise
|
||||
|
||||
@property
|
||||
def proxy_host(self):
|
||||
return self._proxy_host
|
||||
|
||||
@property
|
||||
def proxy_port(self):
|
||||
return self._proxy_port
|
||||
|
||||
@classmethod
|
||||
def create(cls, *args, **kwargs): # for backward compatibility
|
||||
return cls(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_url(cls, url: str, **kwargs) -> 'CurioProxy':
|
||||
url_args = parse_proxy_url(url)
|
||||
return cls(*url_args, **kwargs)
|
||||
@@ -0,0 +1,25 @@
|
||||
import socket
|
||||
from curio.socket import getaddrinfo
|
||||
|
||||
from ... import _abc as abc
|
||||
|
||||
|
||||
class Resolver(abc.AsyncResolver):
|
||||
async def resolve(self, host, port=0, family=socket.AF_UNSPEC):
|
||||
try:
|
||||
infos = await getaddrinfo(
|
||||
host=host,
|
||||
port=port,
|
||||
family=family,
|
||||
type=socket.SOCK_STREAM,
|
||||
)
|
||||
except socket.gaierror: # pragma: no cover
|
||||
infos = None
|
||||
|
||||
if not infos: # pragma: no cover
|
||||
raise OSError('Can`t resolve address {}:{} [{}]'.format(host, port, family))
|
||||
|
||||
infos = sorted(infos, key=lambda info: info[0])
|
||||
|
||||
family, _, _, _, address = infos[0]
|
||||
return family, address[0]
|
||||
@@ -0,0 +1,32 @@
|
||||
import curio.io
|
||||
import curio.socket
|
||||
|
||||
from ... import _abc as abc
|
||||
from ..._errors import ProxyError
|
||||
|
||||
DEFAULT_RECEIVE_SIZE = 65536
|
||||
|
||||
|
||||
class CurioSocketStream(abc.AsyncSocketStream):
|
||||
_socket: curio.io.Socket = None
|
||||
|
||||
def __init__(self, sock: curio.io.Socket):
|
||||
self._socket = sock
|
||||
|
||||
async def write_all(self, data):
|
||||
await self._socket.sendall(data)
|
||||
|
||||
async def read(self, max_bytes=DEFAULT_RECEIVE_SIZE):
|
||||
return await self._socket.recv(max_bytes)
|
||||
|
||||
async def read_exact(self, n):
|
||||
data = bytearray()
|
||||
while len(data) < n:
|
||||
packet = await self._socket.recv(n - len(data))
|
||||
if not packet: # pragma: no cover
|
||||
raise ProxyError('Connection closed unexpectedly')
|
||||
data += packet
|
||||
return data
|
||||
|
||||
async def close(self):
|
||||
await self._socket.close()
|
||||
Reference in New Issue
Block a user