Skip to content

Commit ebbeee2

Browse files
v2.4.0 (#1081)
* Update description for `--max-sendbuf-size` * Add `--cache-by-content-type` flag (#1070) Add `--cache-by-content-type` flag * Enhance content type cache (#1072) * Fix dispatcher exception during unsubs (#1073) * [EventDispatcher] Guard against broken pipe and eof (#1074) * Guard against broken pipe and eof * refactor * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * [EventCore] Use `no_wait` when publishing to queues (#1076) * Use `no_wait` when publishing to the event core queues * Update readme flag version * [Examples] Fix broken examples (#1077) * Fix broken examples * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix mypy * Pylint ignore Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Allow multiport with ephemeral & unix socket support (#1078) * Allow multiport with ephemeral & unix socket support * Fix unix tests * `Run in detached (background) mode` instructions * Update README.md Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2 parents 558a430 + 235c5e4 commit ebbeee2

40 files changed

+523
-191
lines changed

Makefile

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@ PROXYPY_CONTAINER_VERSION := latest
77
# Used by container build and run targets
88
PROXYPY_CONTAINER_TAG := $(NS)/$(IMAGE_NAME):$(PROXYPY_CONTAINER_VERSION)
99

10-
HTTPS_KEY_FILE_PATH := https-key.pem
11-
HTTPS_CERT_FILE_PATH := https-cert.pem
12-
HTTPS_CSR_FILE_PATH := https-csr.pem
13-
HTTPS_SIGNED_CERT_FILE_PATH := https-signed-cert.pem
10+
CERT_DIR :=
11+
12+
HTTPS_KEY_FILE_PATH := $(CERT_DIR)https-key.pem
13+
HTTPS_CERT_FILE_PATH := $(CERT_DIR)https-cert.pem
14+
HTTPS_CSR_FILE_PATH := $(CERT_DIR)https-csr.pem
15+
HTTPS_SIGNED_CERT_FILE_PATH := $(CERT_DIR)https-signed-cert.pem
1416

1517
CA_CERT_SUFFIX :=
16-
CA_KEY_FILE_PATH := ca-key$(CA_CERT_SUFFIX).pem
17-
CA_CERT_FILE_PATH := ca-cert$(CA_CERT_SUFFIX).pem
18-
CA_SIGNING_KEY_FILE_PATH := ca-signing-key$(CA_CERT_SUFFIX).pem
18+
CA_KEY_FILE_PATH := $(CERT_DIR)ca-key$(CA_CERT_SUFFIX).pem
19+
CA_CERT_FILE_PATH := $(CERT_DIR)ca-cert$(CA_CERT_SUFFIX).pem
20+
CA_SIGNING_KEY_FILE_PATH := $(CERT_DIR)ca-signing-key$(CA_CERT_SUFFIX).pem
1921

2022
# Dummy invalid hardcoded value
2123
PROXYPY_PKG_PATH := dist/proxy.py.whl

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,8 +2286,9 @@ usage: -m [-h] [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT]
22862286
[--ca-key-file CA_KEY_FILE] [--ca-cert-dir CA_CERT_DIR]
22872287
[--ca-cert-file CA_CERT_FILE] [--ca-file CA_FILE]
22882288
[--ca-signing-key-file CA_SIGNING_KEY_FILE]
2289-
[--auth-plugin AUTH_PLUGIN] [--cache-dir CACHE_DIR]
2290-
[--cache-requests] [--proxy-pool PROXY_POOL] [--enable-web-server]
2289+
[--auth-plugin AUTH_PLUGIN] [--cache-requests]
2290+
[--cache-by-content-type] [--cache-dir CACHE_DIR]
2291+
[--proxy-pool PROXY_POOL] [--enable-web-server]
22912292
[--enable-static-server] [--static-server-dir STATIC_SERVER_DIR]
22922293
[--min-compression-length MIN_COMPRESSION_LENGTH]
22932294
[--enable-reverse-proxy] [--pac-file PAC_FILE]
@@ -2297,7 +2298,7 @@ usage: -m [-h] [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT]
22972298
[--filtered-client-ips FILTERED_CLIENT_IPS]
22982299
[--filtered-url-regex-config FILTERED_URL_REGEX_CONFIG]
22992300

2300-
proxy.py v2.4.0rc9.dev8+gea0253d.d20220126
2301+
proxy.py v2.4.0rc10.dev13+g96428ae.d20220126
23012302

23022303
options:
23032304
-h, --help show this help message and exit
@@ -2393,8 +2394,8 @@ options:
23932394
Default: 128 KB. Maximum amount of data received from
23942395
the server in a single recv() operation.
23952396
--max-sendbuf-size MAX_SENDBUF_SIZE
2396-
Default: 64 KB. Maximum amount of data to dispatch in
2397-
a single send() operation.
2397+
Default: 64 KB. Maximum amount of data to flush in a
2398+
single send() operation.
23982399
--timeout TIMEOUT Default: 10.0. Number of seconds after which an
23992400
inactive connection must be dropped. Inactivity is
24002401
defined by no data sent or received by the client.
@@ -2427,11 +2428,16 @@ options:
24272428
--auth-plugin AUTH_PLUGIN
24282429
Default: proxy.http.proxy.auth.AuthPlugin. Auth plugin
24292430
to use instead of default basic auth plugin.
2431+
--cache-requests Default: False. Whether to also write request packets
2432+
in the cache file.
2433+
--cache-by-content-type
2434+
Default: False. Whether to extract content by type
2435+
from responses. Extracted content type is written to
2436+
the cache directory e.g. video.mp4.
24302437
--cache-dir CACHE_DIR
24312438
Default: /Users/abhinavsingh/.proxy/cache. Flag only
24322439
applicable when cache plugin is used with on-disk
24332440
storage.
2434-
--cache-requests Default: False. Whether to also cache request packets.
24352441
--proxy-pool PROXY_POOL
24362442
List of upstream proxies to use in the pool
24372443
--enable-web-server Default: False. Whether to enable

examples/pubsub_eventing.py

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -66,42 +66,37 @@ def publisher_process(
6666
# Create a subscriber.
6767
# Internally, subscribe will start a separate thread
6868
# to receive incoming published messages.
69-
subscriber = EventSubscriber(event_manager.queue, callback=on_event)
70-
subscriber.setup()
71-
72-
# Start a publisher process to demonstrate safe exchange
73-
# of messages between processes.
74-
publisher_shutdown_event = multiprocessing.Event()
75-
publisher = multiprocessing.Process(
76-
target=publisher_process, args=(
77-
publisher_shutdown_event, event_manager.queue, ),
78-
)
79-
publisher.start()
80-
81-
# Dispatch event from main process too
82-
# to demonstrate safe exchange of messages
83-
# between threads.
84-
try:
85-
while True:
86-
event_manager.queue.publish(
87-
request_id='1234',
88-
event_name=eventNames.WORK_STARTED,
89-
event_payload={'time': time.time()},
90-
publisher_id='eventing_pubsub_main',
91-
)
92-
except KeyboardInterrupt:
93-
logger.info('bye!!!')
94-
finally:
95-
# Stop publisher process
96-
publisher_shutdown_event.set()
97-
publisher.join()
98-
# Stop subscriber thread
99-
subscriber.unsubscribe()
100-
logger.info(
101-
'Received {0} events from main thread, {1} events from another process, in {2} seconds'.format(
102-
num_events_received[0], num_events_received[1], time.time(
103-
) - start_time,
104-
),
69+
with EventSubscriber(event_manager.queue, callback=on_event) as subscriber:
70+
# Start a publisher process to demonstrate safe exchange
71+
# of messages between processes.
72+
publisher_shutdown_event = multiprocessing.Event()
73+
publisher = multiprocessing.Process(
74+
target=publisher_process, args=(
75+
publisher_shutdown_event, event_manager.queue, ),
10576
)
106-
if subscriber:
107-
subscriber.shutdown(do_unsubscribe=False)
77+
publisher.start()
78+
79+
# Dispatch event from main process too
80+
# to demonstrate safe exchange of messages
81+
# between threads.
82+
try:
83+
while True:
84+
event_manager.queue.publish(
85+
request_id='1234',
86+
event_name=eventNames.WORK_STARTED,
87+
event_payload={'time': time.time()},
88+
publisher_id='eventing_pubsub_main',
89+
)
90+
except KeyboardInterrupt:
91+
logger.info('KBE!!!')
92+
finally:
93+
# Stop publisher process
94+
publisher_shutdown_event.set()
95+
publisher.join()
96+
logger.info(
97+
'Received {0} events from main thread, {1} events from another process, in {2} seconds'.format(
98+
num_events_received[0], num_events_received[1], time.time(
99+
) - start_time,
100+
),
101+
)
102+
logger.info('Done!!!')

examples/ssl_echo_client.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,29 @@
88
:copyright: (c) 2013-present by Abhinav Singh and contributors.
99
:license: BSD, see LICENSE for more details.
1010
"""
11+
import ssl
1112
import logging
1213

1314
from proxy.core.connection import TcpServerConnection
14-
from proxy.common.constants import DEFAULT_BUFFER_SIZE
15+
from proxy.common.constants import DEFAULT_LOG_FORMAT, DEFAULT_BUFFER_SIZE
1516

1617

18+
logging.basicConfig(level=logging.INFO, format=DEFAULT_LOG_FORMAT)
19+
1720
logger = logging.getLogger(__name__)
1821

1922
if __name__ == '__main__':
20-
client = TcpServerConnection('::', 12345)
23+
client = TcpServerConnection('127.0.0.1', 12345)
2124
client.connect()
22-
client.wrap('example.com', ca_file='ca-cert.pem')
23-
# wrap() will by default set connection to nonblocking
24-
# flip it back to blocking
25-
client.connection.setblocking(True)
25+
client.wrap(
26+
None, # 'localhost',
27+
ca_file='ca-cert.pem',
28+
# For self-signed certs you will have
29+
# to disable verification. Or you can
30+
# add your CA certificate in the CA bundle
31+
# and then enable verify.
32+
verify_mode=ssl.VerifyMode.CERT_NONE,
33+
)
2634
try:
2735
while True:
2836
client.send(b'hello')

examples/ssl_echo_server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def main() -> None:
5252
threadless=True,
5353
num_workers=1,
5454
port=12345,
55-
keyfile='https-key.pem',
56-
certfile='https-signed-cert.pem',
55+
key_file='https-key.pem',
56+
cert_file='https-signed-cert.pem',
5757
):
5858
try:
5959
while True:

examples/tcp_echo_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
import logging
1212

1313
from proxy.common.utils import socket_connection
14-
from proxy.common.constants import DEFAULT_BUFFER_SIZE
14+
from proxy.common.constants import DEFAULT_LOG_FORMAT, DEFAULT_BUFFER_SIZE
1515

1616

17+
logging.basicConfig(level=logging.INFO, format=DEFAULT_LOG_FORMAT)
18+
1719
logger = logging.getLogger(__name__)
1820

1921
if __name__ == '__main__':
20-
with socket_connection(('::', 12345)) as client:
22+
with socket_connection(('127.0.0.1', 12345)) as client:
2123
while True:
2224
client.send(b'hello')
2325
data = client.recv(DEFAULT_BUFFER_SIZE)

examples/websocket_client.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
from proxy.http.websocket import (
1515
WebsocketFrame, WebsocketClient, websocketOpcodes,
1616
)
17+
from proxy.common.constants import DEFAULT_LOG_FORMAT
1718

1819

20+
logging.basicConfig(level=logging.INFO, format=DEFAULT_LOG_FORMAT)
21+
1922
# globals
2023
client: WebsocketClient
2124
last_dispatch_time: float
@@ -47,9 +50,9 @@ def on_message(frame: WebsocketFrame) -> None:
4750
if __name__ == '__main__':
4851
# Constructor establishes socket connection
4952
client = WebsocketClient(
50-
b'echo.websocket.org',
51-
80,
52-
b'/',
53+
b'localhost',
54+
8899,
55+
b'/ws-route-example',
5356
on_message=on_message,
5457
)
5558
# Perform handshake

proxy/common/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ def _env_threadless_compliant() -> bool:
149149
DEFAULT_DATA_DIRECTORY_PATH, 'cache',
150150
)
151151
DEFAULT_CACHE_REQUESTS = False
152+
DEFAULT_CACHE_BY_CONTENT_TYPE = False
152153

153154
# Cor plugins enabled by default or via flags
154155
DEFAULT_ABC_PLUGINS = [

proxy/common/flag.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,8 @@ def initialize(
398398
# FIXME: Necessary here until flags framework provides a way
399399
# for flag owners to initialize
400400
os.makedirs(args.cache_dir, exist_ok=True)
401-
os.makedirs(os.path.join(args.cache_dir, 'response'), exist_ok=True)
401+
os.makedirs(os.path.join(args.cache_dir, 'responses'), exist_ok=True)
402+
os.makedirs(os.path.join(args.cache_dir, 'content'), exist_ok=True)
402403

403404
return args
404405

proxy/common/utils.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def build_http_response(
120120
headers: Optional[Dict[bytes, bytes]] = None,
121121
body: Optional[bytes] = None,
122122
conn_close: bool = False,
123+
no_cl: bool = False,
123124
) -> bytes:
124125
"""Build and returns a HTTP response packet."""
125126
line = [protocol_version, bytes_(status_code)]
@@ -131,7 +132,7 @@ def build_http_response(
131132
if k.lower() == b'transfer-encoding':
132133
has_transfer_encoding = True
133134
break
134-
if not has_transfer_encoding:
135+
if not has_transfer_encoding and not no_cl:
135136
headers[b'Content-Length'] = bytes_(len(body)) if body else b'0'
136137
return build_http_pkt(line, headers, body, conn_close)
137138

@@ -212,12 +213,15 @@ def find_http_line(raw: bytes) -> Tuple[Optional[bytes], bytes]:
212213

213214

214215
def wrap_socket(
215-
conn: socket.socket, keyfile: str,
216-
certfile: str,
216+
conn: socket.socket,
217+
keyfile: str,
218+
certfile: str,
219+
cafile: Optional[str] = None,
217220
) -> ssl.SSLSocket:
218221
"""Use this to upgrade server_side socket to TLS."""
219222
ctx = ssl.create_default_context(
220223
ssl.Purpose.CLIENT_AUTH,
224+
cafile=cafile,
221225
)
222226
ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
223227
ctx.verify_mode = ssl.CERT_NONE

proxy/core/base/tcp_server.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474
type=int,
7575
default=DEFAULT_MAX_SEND_SIZE,
7676
help='Default: ' + str(int(DEFAULT_MAX_SEND_SIZE / 1024)) +
77-
' KB. Maximum amount of data to dispatch in a single send() operation.',
77+
' KB. Maximum amount of data to flush in a single send() operation.',
7878
)
7979

8080
flags.add_argument(
@@ -185,8 +185,19 @@ async def handle_readables(self, readables: Readables) -> bool:
185185
if self.work.connection.fileno() in readables:
186186
try:
187187
data = self.work.recv(self.flags.client_recvbuf_size)
188+
except ConnectionResetError:
189+
logger.info(
190+
'Connection reset by client {0}'.format(
191+
self.work.address,
192+
),
193+
)
194+
return True
188195
except TimeoutError:
189-
logger.info('Client recv timeout error')
196+
logger.info(
197+
'Client recv timeout error {0}'.format(
198+
self.work.address,
199+
),
200+
)
190201
return True
191202
if data is None:
192203
logger.debug(

proxy/core/connection/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def connection(self) -> TcpOrTlsSocket:
4949

5050
def send(self, data: Union[memoryview, bytes]) -> int:
5151
"""Users must handle BrokenPipeError exceptions"""
52-
# logger.info(data)
52+
# logger.info(data.tobytes())
5353
return self.connection.send(data)
5454

5555
def recv(

proxy/core/connection/server.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,19 @@ def connect(
4545

4646
def wrap(
4747
self,
48-
hostname: str,
48+
hostname: Optional[str] = None,
4949
ca_file: Optional[str] = None,
5050
as_non_blocking: bool = False,
51+
# Ref https://github.com/PyCQA/pylint/issues/3691
52+
verify_mode: ssl.VerifyMode = ssl.VerifyMode.CERT_REQUIRED, # pylint: disable=E1101
5153
) -> None:
5254
ctx = ssl.create_default_context(
53-
ssl.Purpose.SERVER_AUTH, cafile=ca_file,
55+
ssl.Purpose.SERVER_AUTH,
56+
cafile=ca_file,
5457
)
5558
ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
56-
ctx.check_hostname = True
59+
ctx.check_hostname = hostname is not None
60+
ctx.verify_mode = verify_mode
5761
self.connection.setblocking(True)
5862
self._conn = ctx.wrap_socket(
5963
self.connection,

0 commit comments

Comments
 (0)