/pyrogram/connection/transport/tcp/tcp.py
https://github.com/pyrogram/pyrogram · Python · 120 lines · 81 code · 20 blank · 19 comment · 23 complexity · 9d4420f6f50bdb0e52d6db00a3843a27 MD5 · raw file
- # Pyrogram - Telegram MTProto API Client Library for Python
- # Copyright (C) 2017-2020 Dan <https://github.com/delivrance>
- #
- # This file is part of Pyrogram.
- #
- # Pyrogram is free software: you can redistribute it and/or modify
- # it under the terms of the GNU Lesser General Public License as published
- # by the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Pyrogram is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU Lesser General Public License for more details.
- #
- # You should have received a copy of the GNU Lesser General Public License
- # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
- import asyncio
- import ipaddress
- import logging
- import socket
- import time
- try:
- import socks
- except ImportError as e:
- e.msg = (
- "PySocks is missing and Pyrogram can't run without. "
- "Please install it using \"pip3 install pysocks\"."
- )
- raise e
- log = logging.getLogger(__name__)
- class TCP:
- TIMEOUT = 10
- def __init__(self, ipv6: bool, proxy: dict):
- self.socket = None
- self.reader = None # type: asyncio.StreamReader
- self.writer = None # type: asyncio.StreamWriter
- self.lock = asyncio.Lock()
- if proxy.get("enabled", False):
- hostname = proxy.get("hostname", None)
- port = proxy.get("port", None)
- try:
- ip_address = ipaddress.ip_address(hostname)
- except ValueError:
- self.socket = socks.socksocket(socket.AF_INET)
- else:
- if isinstance(ip_address, ipaddress.IPv6Address):
- self.socket = socks.socksocket(socket.AF_INET6)
- else:
- self.socket = socks.socksocket(socket.AF_INET)
- self.socket.set_proxy(
- proxy_type=socks.SOCKS5,
- addr=hostname,
- port=port,
- username=proxy.get("username", None),
- password=proxy.get("password", None)
- )
- log.info(f"Using proxy {hostname}:{port}")
- else:
- self.socket = socks.socksocket(
- socket.AF_INET6 if ipv6
- else socket.AF_INET
- )
- self.socket.settimeout(TCP.TIMEOUT)
- async def connect(self, address: tuple):
- self.socket.connect(address)
- self.reader, self.writer = await asyncio.open_connection(sock=self.socket)
- def close(self):
- try:
- self.writer.close()
- except AttributeError:
- try:
- self.socket.shutdown(socket.SHUT_RDWR)
- except OSError:
- pass
- finally:
- # A tiny sleep placed here helps avoiding .recv(n) hanging until the timeout.
- # This is a workaround that seems to fix the occasional delayed stop of a client.
- time.sleep(0.001)
- self.socket.close()
- async def send(self, data: bytes):
- async with self.lock:
- self.writer.write(data)
- await self.writer.drain()
- async def recv(self, length: int = 0):
- data = b""
- while len(data) < length:
- try:
- chunk = await asyncio.wait_for(
- self.reader.read(length - len(data)),
- TCP.TIMEOUT
- )
- except (OSError, asyncio.TimeoutError):
- return None
- else:
- if chunk:
- data += chunk
- else:
- return None
- return data