mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-25 01:29:48 +00:00
generate event documentation from source
This commit is contained in:
parent
f9d18745c9
commit
667cacba3f
@ -39,6 +39,8 @@ If you depend on these features, please raise your voice in
|
||||
|
||||
* New Proxy Core based on sans-io pattern (@mhils)
|
||||
* mitmproxy's command line interface now supports Windows (@mhils)
|
||||
* The `clientconnect`, `clientdisconnect`, `serverconnect`, `serverdisconnect`, and `log`
|
||||
events have been replaced with new events, see addon documentation for details (@mhils)
|
||||
* Use pyca/cryptography to generate certificates, not pyOpenSSL (@mhils)
|
||||
* Remove the legacy protocol stack (@Kriechi)
|
||||
* Remove all deprecated pathod and pathoc tools and modules (@Kriechi)
|
||||
|
141
docs/scripts/events.py
Normal file
141
docs/scripts/events.py
Normal file
@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env python3
|
||||
import inspect
|
||||
import textwrap
|
||||
from typing import List, Type
|
||||
|
||||
import mitmproxy.addons.next_layer # noqa
|
||||
from mitmproxy import events, log, addonmanager
|
||||
from mitmproxy.proxy import server_hooks, layer
|
||||
from mitmproxy.proxy.layers import http, tcp, tls, websocket
|
||||
|
||||
known = set()
|
||||
|
||||
|
||||
def category(name: str, hooks: List[Type[events.MitmproxyEvent]]) -> None:
|
||||
print(f"### {name} Events")
|
||||
print("```python")
|
||||
|
||||
all_params = [
|
||||
list(inspect.signature(hook.__init__).parameters.values())[1:]
|
||||
for hook in hooks
|
||||
]
|
||||
|
||||
# slightly overengineered, but this was fun to write. ¯\_(ツ)_/¯
|
||||
imports = set()
|
||||
types = set()
|
||||
for params in all_params:
|
||||
for param in params:
|
||||
try:
|
||||
mod = inspect.getmodule(param.annotation).__name__
|
||||
if mod == "typing":
|
||||
# this is ugly, but can be removed once we are on Python 3.9+ only
|
||||
imports.add(inspect.getmodule(param.annotation.__args__[0]).__name__)
|
||||
types.add(param.annotation._name)
|
||||
else:
|
||||
imports.add(mod)
|
||||
except AttributeError:
|
||||
raise ValueError(f"Missing type annotation: {params}")
|
||||
imports.discard("builtins")
|
||||
if types:
|
||||
print(f"from typing import {', '.join(sorted(types))}")
|
||||
print("from mitmproxy import ctx")
|
||||
for imp in sorted(imports):
|
||||
print(f"import {imp}")
|
||||
print()
|
||||
|
||||
first = True
|
||||
for hook, params in zip(hooks, all_params):
|
||||
if first:
|
||||
first = False
|
||||
else:
|
||||
print()
|
||||
assert hook.name not in known
|
||||
known.add(hook.name)
|
||||
doc = inspect.getdoc(hook)
|
||||
print(f"def {hook.name}({', '.join(str(p) for p in params)}):")
|
||||
print(textwrap.indent(f'"""\n{doc}\n"""', " "))
|
||||
if params:
|
||||
print(f' ctx.log(f"{hook.name}: {" ".join("{" + p.name + "=}" for p in params)}")')
|
||||
else:
|
||||
print(f' ctx.log("{hook.name}")')
|
||||
print("```")
|
||||
|
||||
|
||||
category(
|
||||
"Lifecycle",
|
||||
[
|
||||
addonmanager.LoadEvent,
|
||||
events.RunningEvent,
|
||||
events.ConfigureEvent,
|
||||
events.DoneEvent,
|
||||
]
|
||||
)
|
||||
|
||||
category(
|
||||
"Connection",
|
||||
[
|
||||
server_hooks.ClientConnectedHook,
|
||||
server_hooks.ClientDisconnectedHook,
|
||||
server_hooks.ServerConnectHook,
|
||||
server_hooks.ServerConnectedHook,
|
||||
server_hooks.ServerDisconnectedHook,
|
||||
]
|
||||
)
|
||||
|
||||
category(
|
||||
"HTTP",
|
||||
[
|
||||
http.HttpRequestHeadersHook,
|
||||
http.HttpRequestHook,
|
||||
http.HttpResponseHeadersHook,
|
||||
http.HttpResponseHook,
|
||||
http.HttpErrorHook,
|
||||
http.HttpConnectHook,
|
||||
]
|
||||
)
|
||||
|
||||
category(
|
||||
"TCP",
|
||||
[
|
||||
tcp.TcpStartHook,
|
||||
tcp.TcpMessageHook,
|
||||
tcp.TcpEndHook,
|
||||
tcp.TcpErrorHook,
|
||||
]
|
||||
)
|
||||
|
||||
category(
|
||||
"TLS",
|
||||
[
|
||||
tls.TlsClienthelloHook,
|
||||
tls.TlsStartHook,
|
||||
]
|
||||
)
|
||||
|
||||
category(
|
||||
"WebSocket",
|
||||
[
|
||||
websocket.WebsocketStartHook,
|
||||
websocket.WebsocketMessageHook,
|
||||
websocket.WebsocketEndHook,
|
||||
websocket.WebsocketErrorHook,
|
||||
]
|
||||
)
|
||||
|
||||
category(
|
||||
"Advanced Lifecycle",
|
||||
[
|
||||
layer.NextLayerHook,
|
||||
events.UpdateEvent,
|
||||
log.AddLogEvent,
|
||||
]
|
||||
)
|
||||
|
||||
not_documented = set(events.all_events.keys()) - known
|
||||
if not_documented:
|
||||
raise RuntimeError(f"Not documented: {not_documented}")
|
||||
|
||||
# print("<table class=\"table filtertable\"><tbody>")
|
||||
# for i in flowfilter.help:
|
||||
# print("<tr><th>%s</th><td>%s</td></tr>" % i)
|
||||
# print("</tbody></table>")
|
@ -62,7 +62,7 @@ and adds a header to every request. The really interesting aspect of this
|
||||
example is how users specify flows. Because mitmproxy can inspect the type
|
||||
signature, it can expand a text flow selector into a sequence of flows for us
|
||||
transparently. This means that the user has the full flexibility of [flow
|
||||
filters]({{< relref addons-options >}}) available. Let's try it out.
|
||||
filters]({{< relref concepts-filters >}}) available. Let's try it out.
|
||||
|
||||
Start by loading the addon into mitmproxy and sending some traffic through so we
|
||||
have flows to work with:
|
||||
|
@ -18,21 +18,7 @@ header with a count of the number of responses seen:
|
||||
|
||||
## Supported Events
|
||||
|
||||
Below is an addon class that implements stubs for all events. We've added
|
||||
annotations to illustrate the argument types for the various events.
|
||||
Below we list events supported by mitmproxy. We've added
|
||||
annotations to illustrate the argument types.
|
||||
|
||||
### Generic Events
|
||||
|
||||
{{< example src="examples/addons/events.py" lang="py" >}}
|
||||
|
||||
### HTTP Events
|
||||
|
||||
{{< example src="examples/addons/events-http-specific.py" lang="py" >}}
|
||||
|
||||
### WebSocket Events
|
||||
|
||||
{{< example src="examples/addons/events-websocket-specific.py" lang="py" >}}
|
||||
|
||||
### TCP Events
|
||||
|
||||
{{< example src="examples/addons/events-tcp-specific.py" lang="py" >}}
|
||||
{{< readfile file="/generated/events.html" markdown="true" >}}
|
||||
|
@ -1,42 +0,0 @@
|
||||
"""HTTP-specific events."""
|
||||
import mitmproxy.http
|
||||
|
||||
|
||||
class Events:
|
||||
def http_connect(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
An HTTP CONNECT request was received. Setting a non 2xx response on
|
||||
the flow will return the response to the client abort the
|
||||
connection. CONNECT requests and responses do not generate the usual
|
||||
HTTP handler events. CONNECT requests are only valid in regular and
|
||||
upstream proxy modes.
|
||||
"""
|
||||
|
||||
def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
HTTP request headers were successfully read. At this point, the body
|
||||
is empty.
|
||||
"""
|
||||
|
||||
def request(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
The full HTTP request has been read.
|
||||
"""
|
||||
|
||||
def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
HTTP response headers were successfully read. At this point, the body
|
||||
is empty.
|
||||
"""
|
||||
|
||||
def response(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
The full HTTP response has been read.
|
||||
"""
|
||||
|
||||
def error(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
An HTTP error has occurred, e.g. invalid server responses, or
|
||||
interrupted connections. This is distinct from a valid server HTTP
|
||||
error response, which is simply a response with an HTTP error code.
|
||||
"""
|
@ -1,25 +0,0 @@
|
||||
"""TCP-specific events."""
|
||||
import mitmproxy.tcp
|
||||
|
||||
|
||||
class Events:
|
||||
def tcp_start(self, flow: mitmproxy.tcp.TCPFlow):
|
||||
"""
|
||||
A TCP connection has started.
|
||||
"""
|
||||
|
||||
def tcp_message(self, flow: mitmproxy.tcp.TCPFlow):
|
||||
"""
|
||||
A TCP connection has received a message. The most recent message
|
||||
will be flow.messages[-1]. The message is user-modifiable.
|
||||
"""
|
||||
|
||||
def tcp_error(self, flow: mitmproxy.tcp.TCPFlow):
|
||||
"""
|
||||
A TCP error has occurred.
|
||||
"""
|
||||
|
||||
def tcp_end(self, flow: mitmproxy.tcp.TCPFlow):
|
||||
"""
|
||||
A TCP connection has ended.
|
||||
"""
|
@ -1,37 +0,0 @@
|
||||
"""WebSocket-specific events."""
|
||||
import mitmproxy.http
|
||||
import mitmproxy.websocket
|
||||
|
||||
|
||||
class Events:
|
||||
# WebSocket lifecycle
|
||||
def websocket_handshake(self, flow: mitmproxy.http.HTTPFlow):
|
||||
"""
|
||||
Called when a client wants to establish a WebSocket connection. The
|
||||
WebSocket-specific headers can be manipulated to alter the
|
||||
handshake. The flow object is guaranteed to have a non-None request
|
||||
attribute.
|
||||
"""
|
||||
|
||||
def websocket_start(self, flow: mitmproxy.websocket.WebSocketFlow):
|
||||
"""
|
||||
A WebSocket connection has commenced.
|
||||
"""
|
||||
|
||||
def websocket_message(self, flow: mitmproxy.websocket.WebSocketFlow):
|
||||
"""
|
||||
Called when a WebSocket message is received from the client or
|
||||
server. The most recent message will be flow.messages[-1]. The
|
||||
message is user-modifiable. Currently there are two types of
|
||||
messages, corresponding to the BINARY and TEXT frame types.
|
||||
"""
|
||||
|
||||
def websocket_error(self, flow: mitmproxy.websocket.WebSocketFlow):
|
||||
"""
|
||||
A WebSocket connection has had an error.
|
||||
"""
|
||||
|
||||
def websocket_end(self, flow: mitmproxy.websocket.WebSocketFlow):
|
||||
"""
|
||||
A WebSocket connection has ended.
|
||||
"""
|
@ -1,83 +0,0 @@
|
||||
"""Generic event hooks."""
|
||||
import typing
|
||||
|
||||
import mitmproxy.addonmanager
|
||||
import mitmproxy.connections
|
||||
import mitmproxy.log
|
||||
import mitmproxy.proxy.protocol
|
||||
|
||||
|
||||
class Events:
|
||||
# Network lifecycle
|
||||
def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer):
|
||||
"""
|
||||
A client has connected to mitmproxy. Note that a connection can
|
||||
correspond to multiple HTTP requests.
|
||||
"""
|
||||
|
||||
def clientdisconnect(self, layer: mitmproxy.proxy.protocol.Layer):
|
||||
"""
|
||||
A client has disconnected from mitmproxy.
|
||||
"""
|
||||
|
||||
def serverconnect(self, conn: mitmproxy.connections.ServerConnection):
|
||||
"""
|
||||
Mitmproxy has connected to a server. Note that a connection can
|
||||
correspond to multiple requests.
|
||||
"""
|
||||
|
||||
def serverdisconnect(self, conn: mitmproxy.connections.ServerConnection):
|
||||
"""
|
||||
Mitmproxy has disconnected from a server.
|
||||
"""
|
||||
|
||||
def next_layer(self, layer: mitmproxy.proxy.protocol.Layer):
|
||||
"""
|
||||
Network layers are being switched. You may change which layer will
|
||||
be used by returning a new layer object from this event.
|
||||
"""
|
||||
|
||||
# General lifecycle
|
||||
def configure(self, updated: typing.Set[str]):
|
||||
"""
|
||||
Called when configuration changes. The updated argument is a
|
||||
set-like object containing the keys of all changed options. This
|
||||
event is called during startup with all options in the updated set.
|
||||
"""
|
||||
|
||||
def done(self):
|
||||
"""
|
||||
Called when the addon shuts down, either by being removed from
|
||||
the mitmproxy instance, or when mitmproxy itself shuts down. On
|
||||
shutdown, this event is called after the event loop is
|
||||
terminated, guaranteeing that it will be the final event an addon
|
||||
sees. Note that log handlers are shut down at this point, so
|
||||
calls to log functions will produce no output.
|
||||
"""
|
||||
|
||||
def load(self, entry: mitmproxy.addonmanager.Loader):
|
||||
"""
|
||||
Called when an addon is first loaded. This event receives a Loader
|
||||
object, which contains methods for adding options and commands. This
|
||||
method is where the addon configures itself.
|
||||
"""
|
||||
|
||||
def log(self, entry: mitmproxy.log.LogEntry):
|
||||
"""
|
||||
Called whenever a new log entry is created through the mitmproxy
|
||||
context. Be careful not to log from this event, which will cause an
|
||||
infinite loop!
|
||||
"""
|
||||
|
||||
def running(self):
|
||||
"""
|
||||
Called when the proxy is completely up and running. At this point,
|
||||
you can expect the proxy to be bound to a port, and all addons to be
|
||||
loaded.
|
||||
"""
|
||||
|
||||
def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):
|
||||
"""
|
||||
Update is called when one or more flow objects have been modified,
|
||||
usually from a different addon.
|
||||
"""
|
Loading…
Reference in New Issue
Block a user