mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-25 09:37:37 +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)
|
* New Proxy Core based on sans-io pattern (@mhils)
|
||||||
* mitmproxy's command line interface now supports Windows (@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)
|
* Use pyca/cryptography to generate certificates, not pyOpenSSL (@mhils)
|
||||||
* Remove the legacy protocol stack (@Kriechi)
|
* Remove the legacy protocol stack (@Kriechi)
|
||||||
* Remove all deprecated pathod and pathoc tools and modules (@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
|
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
|
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
|
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
|
Start by loading the addon into mitmproxy and sending some traffic through so we
|
||||||
have flows to work with:
|
have flows to work with:
|
||||||
|
@ -18,21 +18,7 @@ header with a count of the number of responses seen:
|
|||||||
|
|
||||||
## Supported Events
|
## Supported Events
|
||||||
|
|
||||||
Below is an addon class that implements stubs for all events. We've added
|
Below we list events supported by mitmproxy. We've added
|
||||||
annotations to illustrate the argument types for the various events.
|
annotations to illustrate the argument types.
|
||||||
|
|
||||||
### Generic Events
|
{{< readfile file="/generated/events.html" markdown="true" >}}
|
||||||
|
|
||||||
{{< 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" >}}
|
|
||||||
|
@ -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