File: server.py

package info (click to toggle)
python-tiny-proxy 0.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212 kB
  • sloc: python: 1,154; makefile: 3
file content (90 lines) | stat: -rw-r--r-- 2,298 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
import functools
import logging
import ssl
import sys
import time
from typing import Tuple, Optional

import anyio
import yaml
from anyio import create_tcp_listener, get_cancelled_exc_class, create_task_group
from anyio.streams.tls import TLSListener

from tiny_proxy import HttpProxyHandler, Socks4ProxyHandler, Socks5ProxyHandler

CLS_MAP = {
    'http': HttpProxyHandler,
    'socks4': Socks4ProxyHandler,
    'socks5': Socks5ProxyHandler,
}

logger = logging.getLogger(__name__)


def configure_logging():
    root_logger = logging.getLogger()
    root_logger.setLevel('INFO')

    fmt = '%(asctime)s [%(name)s:%(lineno)d] %(levelname)s : %(message)s'
    formatter = logging.Formatter(fmt)
    formatter.converter = time.gmtime

    stdout_handler = logging.StreamHandler(sys.stdout)
    stdout_handler.setFormatter(formatter)
    root_logger.addHandler(stdout_handler)


def load_settings(file_name='./settings.yml') -> dict:
    with open(file_name, 'r') as f:
        return yaml.safe_load(f)


async def serve(
    proxy_type: str,
    host: str,
    port: int,
    ssl_cert: Optional[Tuple[str, str]] = None,
    **kwargs,
):
    handler_cls = CLS_MAP.get(proxy_type)
    if not handler_cls:
        raise RuntimeError(f'Unsupported proxy type: {proxy_type}')

    if ssl_cert is not None:
        ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
        ssl_context.load_cert_chain(*ssl_cert)
    else:
        ssl_context = None

    logger.info(f'Starting {proxy_type} proxy on {host}:{port}...')

    handler = handler_cls(**kwargs)

    try:
        listener = await create_tcp_listener(local_host=host, local_port=port)
        if ssl_context is not None:
            listener = TLSListener(listener=listener, ssl_context=ssl_context)

        async with listener:
            await listener.serve(handler.handle)
    except get_cancelled_exc_class():  # noqa
        pass


async def start_server(configs: list[dict]):
    async with create_task_group() as tg:
        for cfg in configs:
            tg.start_soon(functools.partial(serve, **cfg))


def main():
    configure_logging()
    settings = load_settings()
    try:
        anyio.run(start_server, settings['proxies'])
    except (KeyboardInterrupt, SystemExit):
        pass


if __name__ == '__main__':
    main()