File: _proxy_async_aio.py

package info (click to toggle)
electrum 4.0.9-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 35,248 kB
  • sloc: python: 222,785; sh: 165; java: 73; javascript: 10; makefile: 9
file content (152 lines) | stat: -rw-r--r-- 4,792 bytes parent folder | download
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,
    }