proxy.py/proxy/core/base/tcp_server.py

107 lines
3.9 KiB
Python

# -*- coding: utf-8 -*-
"""
proxy.py
~~~~~~~~
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
Network monitoring, controls & Application development, testing, debugging.
:copyright: (c) 2013-present by Abhinav Singh and contributors.
:license: BSD, see LICENSE for more details.
"""
from abc import abstractmethod
import socket
import selectors
from typing import Dict, Any, Optional
from proxy.core.acceptor import Work
from proxy.common.types import Readables, Writables
class BaseTcpServerHandler(Work):
"""BaseTcpServerHandler implements Work interface.
An instance of BaseTcpServerHandler is created for each client
connection. BaseServerHandler lifecycle is controlled by
Threadless core using asyncio.
BaseServerHandler ensures that pending buffers are flushed
before client connection is closed.
Implementations must provide:
a) handle_data(data: memoryview)
c) (optionally) intialize, is_inactive and shutdown methods
"""
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.must_flush_before_shutdown = False
print('Connection accepted from {0}'.format(self.client.addr))
@abstractmethod
def handle_data(self, data: memoryview) -> Optional[bool]:
"""Optionally return True to close client connection."""
pass # pragma: no cover
def get_events(self) -> Dict[socket.socket, int]:
events = {}
# We always want to read from client
# Register for EVENT_READ events
if self.must_flush_before_shutdown is False:
events[self.client.connection] = selectors.EVENT_READ
# If there is pending buffer for client
# also register for EVENT_WRITE events
if self.client.has_buffer():
if self.client.connection in events:
events[self.client.connection] |= selectors.EVENT_WRITE
else:
events[self.client.connection] = selectors.EVENT_WRITE
return events
def handle_events(
self,
readables: Readables,
writables: Writables) -> bool:
"""Return True to shutdown work."""
do_shutdown = False
if self.client.connection in readables:
try:
data = self.client.recv()
if data is None:
# Client closed connection, signal shutdown
print(
'Connection closed by client {0}'.format(
self.client.addr))
do_shutdown = True
else:
r = self.handle_data(data)
if isinstance(r, bool) and r is True:
print(
'Implementation signaled shutdown for client {0}'.format(
self.client.addr))
if self.client.has_buffer():
print(
'Client {0} has pending buffer, will be flushed before shutting down'.format(
self.client.addr))
self.must_flush_before_shutdown = True
else:
do_shutdown = True
except ConnectionResetError:
print(
'Connection reset by client {0}'.format(
self.client.addr))
do_shutdown = True
if self.client.connection in writables:
print('Flushing buffer to client {0}'.format(self.client.addr))
self.client.flush()
if self.must_flush_before_shutdown is True:
do_shutdown = True
self.must_flush_before_shutdown = False
if do_shutdown:
print(
'Shutting down client {0} connection'.format(
self.client.addr))
return do_shutdown