[sans-io] docs++

This commit is contained in:
Maximilian Hils 2020-12-12 21:55:58 +01:00
parent 8f49cde627
commit a768825fba
3 changed files with 46 additions and 20 deletions

View File

@ -1,15 +1,20 @@
"""
Experimental sans-io implementation of mitmproxy's protocol stack.
This module contains mitmproxy's core network proxy.
Most important primitives:
- layers: represent protocol layers, e.g. one for tcp, tls, and so on. Layers are nested, so
The most important primitives are:
- Layers: represent protocol layers, e.g. one for TCP, TLS, and so on. Layers are nested, so
a typical configuration might be ReverseProxy/TLS/TCP.
- server: the proxy server does all IO and communication with the mitmproxy master.
It creates the top/outermost layer for each incoming client connection.
- events: When IO actions occur at the proxy server, they are passed down to the top layer as events.
- commands: In the other direction, layers can emit commands to higher layers or the proxy server.
This is used to e.g. send data, request for new connections to be opened, or to use mitmproxy's
script hooks.
- context: The context is the connection context each layer is provided with. This is still very
much WIP, but this should expose stuff like Server Name Indication to lower layers.
Most importantly, layers are implemented using the sans-io pattern (https://sans-io.readthedocs.io/).
This means that calls return immediately, their is no blocking sync or async code.
- Server: the proxy server handles all I/O. This is implemented using asyncio, but could be done any other way.
The ConnectionHandler is subclassed in the Proxyserver addon, which handles the communication with the
rest of mitmproxy.
- Events: When I/O actions occur at the proxy server, they are passed to the outermost layer as events,
e.g. "DataReceived" or "ConnectionClosed".
- Commands: In the other direction, layers can emit commands to higher layers or the proxy server.
This is used to e.g. send data, request for new connections to be opened, or to call mitmproxy's
event hooks.
- Context: The context is the connection context each layer is provided with, which is always a client connection
and sometimes also a server connection.
"""

View File

@ -27,6 +27,22 @@ class Paused(NamedTuple):
class Layer:
"""
The base class for all protocol layers.
Layers interface with their child layer(s) by calling .handle_event(event),
which returns a list (more precisely: a generator) of commands.
Most layers only implement ._handle_event, which is called by the default implementation of .handle_event.
The default implementation allows layers to emulate blocking code:
When ._handle_event yields a command that has its blocking attribute set to True, .handle_event pauses
the execution of ._handle_event and waits until it is called with the corresponding CommandReply. All events
encountered in the meantime are buffered and replayed after execution is resumed.
The result is code that looks like blocking code, but is not blocking:
def _handle_event(self, event):
err = yield OpenConnection(server) # execution continues here after a connection has been established.
"""
__last_debug_message: ClassVar[str] = ""
context: Context
_paused: Optional[Paused]

View File

@ -130,33 +130,38 @@ def test_tunnel_handshake_command(tctx: Context, success):
def test_tunnel_default_impls(tctx: Context):
tctx.server.state = ConnectionState.OPEN
tl = tunnel.TunnelLayer(tctx, tctx.server, Server(None))
"""
Some tunnels don't need certain features, so the default behaviour
should be to be transparent.
"""
server = Server(None)
server.state = ConnectionState.OPEN
tl = tunnel.TunnelLayer(tctx, server, tctx.server)
tl.child_layer = TChildLayer(tctx)
playbook = Playbook(tl, logs=True)
assert (
playbook
<< Log("Got start. Server state: OPEN")
>> DataReceived(tctx.server, b"server-hello")
<< SendData(tctx.server, b"server-hello-reply")
>> DataReceived(server, b"server-hello")
<< SendData(server, b"server-hello-reply")
)
assert tl.tunnel_state is tunnel.TunnelState.OPEN
assert (
playbook
>> ConnectionClosed(tctx.server)
>> ConnectionClosed(server)
<< Log("Got server close.")
<< CloseConnection(tctx.server)
<< CloseConnection(server)
)
assert tl.tunnel_state is tunnel.TunnelState.CLOSED
assert (
playbook
>> DataReceived(tctx.client, b"open")
<< OpenConnection(tctx.server)
<< OpenConnection(server)
>> reply(None)
<< Log("Opened: err=None. Server state: OPEN")
>> DataReceived(tctx.server, b"half-close")
<< CloseConnection(tctx.server, half_close=True)
>> DataReceived(server, b"half-close")
<< CloseConnection(server, half_close=True)
)