Skip to content

Commit cc126c3

Browse files
[Multiport] Prepare for listening on multiple ports (#1031)
* Convert `--port` to list of integers * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Prepare for multiport listener * Multi listeners, but will fail in theory as no port override is currently performed * Separate `listener` module * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update flags in readme * Fix imports in listener test * Fix flag parsing for `port` and `ports` * Fix tests Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 1569146 commit cc126c3

File tree

14 files changed

+343
-186
lines changed

14 files changed

+343
-186
lines changed

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2251,8 +2251,8 @@ usage: -m [-h] [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT]
22512251
[--tunnel-remote-port TUNNEL_REMOTE_PORT] [--enable-events]
22522252
[--threadless] [--threaded] [--num-workers NUM_WORKERS]
22532253
[--local-executor LOCAL_EXECUTOR] [--backlog BACKLOG]
2254-
[--hostname HOSTNAME] [--port PORT] [--port-file PORT_FILE]
2255-
[--unix-socket-path UNIX_SOCKET_PATH]
2254+
[--hostname HOSTNAME] [--port PORT] [--ports PORTS [PORTS ...]]
2255+
[--port-file PORT_FILE] [--unix-socket-path UNIX_SOCKET_PATH]
22562256
[--num-acceptors NUM_ACCEPTORS] [--version] [--log-level LOG_LEVEL]
22572257
[--log-file LOG_FILE] [--log-format LOG_FORMAT]
22582258
[--open-file-limit OPEN_FILE_LIMIT]
@@ -2270,13 +2270,14 @@ usage: -m [-h] [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT]
22702270
[--proxy-pool PROXY_POOL] [--enable-web-server]
22712271
[--enable-static-server] [--static-server-dir STATIC_SERVER_DIR]
22722272
[--min-compression-length MIN_COMPRESSION_LENGTH]
2273-
[--pac-file PAC_FILE] [--pac-file-url-path PAC_FILE_URL_PATH]
2273+
[--enable-reverse-proxy] [--pac-file PAC_FILE]
2274+
[--pac-file-url-path PAC_FILE_URL_PATH]
22742275
[--cloudflare-dns-mode CLOUDFLARE_DNS_MODE]
22752276
[--filtered-upstream-hosts FILTERED_UPSTREAM_HOSTS]
22762277
[--filtered-client-ips FILTERED_CLIENT_IPS]
22772278
[--filtered-url-regex-config FILTERED_URL_REGEX_CONFIG]
22782279

2279-
proxy.py v2.4.0rc7.dev28+gfbd7b46.d20220120
2280+
proxy.py v2.4.0rc8.dev7+g1871027
22802281

22812282
options:
22822283
-h, --help show this help message and exit
@@ -2315,9 +2316,12 @@ options:
23152316
executors, instead of using underlying OS kernel
23162317
scheduling algorithm.
23172318
--backlog BACKLOG Default: 100. Maximum number of pending connections to
2318-
proxy server
2319+
proxy server.
23192320
--hostname HOSTNAME Default: 127.0.0.1. Server IP address.
2320-
--port PORT Default: 8899. Server port.
2321+
--port PORT Default: 8899. Server port. To listen on more ports,
2322+
pass them using --ports flag.
2323+
--ports PORTS [PORTS ...]
2324+
Default: None. Additional ports to listen on.
23212325
--port-file PORT_FILE
23222326
Default: None. Save server port numbers. Useful when
23232327
using --port=0 ephemeral mode.
@@ -2420,6 +2424,8 @@ options:
24202424
--min-compression-length MIN_COMPRESSION_LENGTH
24212425
Default: 20 bytes. Sets the minimum length of a
24222426
response that will be compressed (gzipped).
2427+
--enable-reverse-proxy
2428+
Default: False. Whether to enable reverse proxy core.
24232429
--pac-file PAC_FILE A file (Proxy Auto Configuration) or string to serve
24242430
when the server receives a direct file request. Using
24252431
this option enables proxy.HttpWebServerPlugin.

proxy/common/flag.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ def initialize(
302302
# assert args.unix_socket_path is None
303303
args.family = socket.AF_INET6 if args.hostname.version == 6 else socket.AF_INET
304304
args.port = cast(int, opts.get('port', args.port))
305+
args.ports = cast(Optional[List[int]], opts.get('ports', args.ports))
305306
args.backlog = cast(int, opts.get('backlog', args.backlog))
306307
num_workers = opts.get('num_workers', args.num_workers)
307308
args.num_workers = cast(

proxy/core/acceptor/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
"""
1515
from .pool import AcceptorPool
1616
from .acceptor import Acceptor
17-
from .listener import Listener
1817

1918

2019
__all__ = [
21-
'Listener',
2220
'Acceptor',
2321
'AcceptorPool',
2422
]

proxy/core/acceptor/listener.py

Lines changed: 0 additions & 125 deletions
This file was deleted.

proxy/core/acceptor/pool.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from multiprocessing.reduction import send_handle
2323

2424
from .acceptor import Acceptor
25-
from .listener import Listener
25+
from ..listener import ListenerPool
2626
from ...common.flag import flags
2727
from ...common.constants import DEFAULT_NUM_ACCEPTORS
2828

@@ -64,15 +64,15 @@ class AcceptorPool:
6464
def __init__(
6565
self,
6666
flags: argparse.Namespace,
67-
listener: Listener,
67+
listeners: ListenerPool,
6868
executor_queues: List[connection.Connection],
6969
executor_pids: List[int],
7070
executor_locks: List['multiprocessing.synchronize.Lock'],
7171
event_queue: Optional['EventQueue'] = None,
7272
) -> None:
7373
self.flags = flags
7474
# File descriptor to use for accepting new work
75-
self.listener: Listener = listener
75+
self.listeners: ListenerPool = listeners
7676
# Available executors
7777
self.executor_queues: List[connection.Connection] = executor_queues
7878
self.executor_pids: List[int] = executor_pids
@@ -109,14 +109,16 @@ def setup(self) -> None:
109109
),
110110
)
111111
# Send file descriptor to all acceptor processes.
112-
fd = self.listener.fileno()
113-
for index in range(self.flags.num_acceptors):
114-
send_handle(
115-
self.fd_queues[index],
116-
fd,
117-
self.acceptors[index].pid,
118-
)
119-
self.fd_queues[index].close()
112+
for listener in self.listeners.pool:
113+
fd = listener.fileno()
114+
assert fd is not None
115+
for index in range(self.flags.num_acceptors):
116+
send_handle(
117+
self.fd_queues[index],
118+
fd,
119+
self.acceptors[index].pid,
120+
)
121+
self.fd_queues[index].close()
120122

121123
def shutdown(self) -> None:
122124
logger.info('Shutting down %d acceptors' % self.flags.num_acceptors)

proxy/core/listener/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
11+
.. spelling::
12+
13+
pre
14+
"""
15+
from .tcp import TcpSocketListener
16+
from .pool import ListenerPool
17+
from .unix import UnixSocketListener
18+
19+
20+
__all__ = [
21+
'UnixSocketListener',
22+
'TcpSocketListener',
23+
'ListenerPool',
24+
]

proxy/core/listener/base.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
import os
12+
import socket
13+
import logging
14+
import argparse
15+
from abc import ABC, abstractmethod
16+
from typing import Any, Optional
17+
18+
from ...common.flag import flags
19+
from ...common.constants import DEFAULT_BACKLOG
20+
21+
22+
flags.add_argument(
23+
'--backlog',
24+
type=int,
25+
default=DEFAULT_BACKLOG,
26+
help='Default: 100. Maximum number of pending connections to proxy server.',
27+
)
28+
29+
logger = logging.getLogger(__name__)
30+
31+
32+
class BaseListener(ABC):
33+
"""Base listener class.
34+
35+
For usage provide a listen method implementation."""
36+
37+
def __init__(self, flags: argparse.Namespace) -> None:
38+
self.flags = flags
39+
self._socket: Optional[socket.socket] = None
40+
41+
@abstractmethod
42+
def listen(self) -> socket.socket:
43+
raise NotImplementedError()
44+
45+
def __enter__(self) -> 'BaseListener':
46+
self.setup()
47+
return self
48+
49+
def __exit__(self, *args: Any) -> None:
50+
self.shutdown()
51+
52+
def fileno(self) -> Optional[int]:
53+
if not self._socket:
54+
return None
55+
return self._socket.fileno()
56+
57+
def setup(self) -> None:
58+
self._socket = self.listen()
59+
60+
def shutdown(self) -> None:
61+
assert self._socket
62+
self._socket.close()
63+
if self.flags.unix_socket_path:
64+
os.remove(self.flags.unix_socket_path)

proxy/core/listener/pool.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
import argparse
12+
from typing import TYPE_CHECKING, Any, List
13+
14+
from .tcp import TcpSocketListener
15+
from .unix import UnixSocketListener
16+
17+
18+
if TYPE_CHECKING:
19+
from .base import BaseListener
20+
21+
22+
class ListenerPool:
23+
"""Provides abstraction around starting multiple listeners
24+
based upon flags."""
25+
26+
def __init__(self, flags: argparse.Namespace) -> None:
27+
self.flags = flags
28+
self.pool: List['BaseListener'] = []
29+
30+
def __enter__(self) -> 'ListenerPool':
31+
self.setup()
32+
return self
33+
34+
def __exit__(self, *args: Any) -> None:
35+
self.shutdown()
36+
37+
def setup(self) -> None:
38+
if self.flags.unix_socket_path:
39+
ulistener = UnixSocketListener(flags=self.flags)
40+
ulistener.setup()
41+
self.pool.append(ulistener)
42+
else:
43+
listener = TcpSocketListener(flags=self.flags)
44+
listener.setup()
45+
self.pool.append(listener)
46+
47+
def shutdown(self) -> None:
48+
for listener in self.pool:
49+
listener.shutdown()
50+
self.pool.clear()

0 commit comments

Comments
 (0)