Merge pull request #3898 from Kriechi/websocket-docs

improve scripting docs
This commit is contained in:
Thomas Kriechbaumer 2020-04-04 15:54:00 +02:00 committed by GitHub
commit ac83db3b84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 155 additions and 115 deletions

View File

@ -21,4 +21,18 @@ header with a count of the number of responses seen:
Below is an addon class that implements stubs for all events. We've added Below is an addon class that implements stubs for all events. We've added
annotations to illustrate the argument types for the various events. annotations to illustrate the argument types for the various events.
### Generic Events
{{< example src="examples/addons/events.py" lang="py" >}} {{< 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" >}}

View File

@ -5,7 +5,7 @@ menu:
weight: 5 weight: 5
--- ---
# Scripting # Scripting HTTP/1.1 and HTTP/2.0
Sometimes, we would like to write a quick script without going through the Sometimes, we would like to write a quick script without going through the
trouble of creating a class. The addons mechanism has a shorthand that allows a trouble of creating a class. The addons mechanism has a shorthand that allows a
@ -13,7 +13,6 @@ module as a whole to be treated as an addon object. This lets us place event
handler functions in the module scope. For instance, here is a complete script handler functions in the module scope. For instance, here is a complete script
that adds a header to every request. that adds a header to every request.
{{< example src="examples/addons/scripting-headers.py" lang="py" >}} {{< example src="examples/addons/scripting-headers.py" lang="py" >}}
@ -22,11 +21,31 @@ an arbitrary response instead:
{{< example src="examples/simple/send_reply_from_proxy.py" lang="py" >}} {{< example src="examples/simple/send_reply_from_proxy.py" lang="py" >}}
All events around the HTTP protocol [can be found here]({{< relref "addons-events#http-events">}}).
You can look at the [http][] module, or the [Request][], and For HTTP-related objects, please look at the [http][] module, or the
[Response][] classes for other attributes that you can use when [Request][], and [Response][] classes for other attributes that you can use when
scripting. scripting.
[http]: https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/http.py # Scripting WebSocket
[Request]: https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/net/http/request.py
[Response]: https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/net/http/response.py The WebSocket protocol initially looks like a regular HTTP request, before the client and server agree to upgrade the connection to WebSocket. All scripting events for initial HTTP handshake, and also the dedicated WebSocket events [can be found here]({{< relref "addons-events#websocket-events">}}).
{{< example src="examples/simple/websocket_messages.py" lang="py" >}}
For WebSocket-related objects please look at the [websocket][] module to find
all attributes that you can use when scripting.
[websocket]: https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/websocket.py
# Scripting TCP
All events around the TCP protocol [can be found here]({{< relref "addons-events#tcp-events">}}).
{{< example src="examples/complex/tcp_message.py" lang="py" >}}
For WebSocket-related objects please look at the [tcp][] module to find
all attributes that you can use when scripting.
[tcp]: https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/tcp.py

View File

@ -60,13 +60,3 @@ just replay flows for a specific domain:
{{< highlight none >}} {{< highlight none >}}
:replay.client "~d google.com" :replay.client "~d google.com"
{{< /highlight >}} {{< /highlight >}}

View File

@ -20,6 +20,6 @@
{{ if .RSSLink -}} {{ if .RSSLink -}}
<link href="{{ .RSSLink }}" rel="feed" type="application/rss+xml" title="{{ .Site.Title }}"> <link href="{{ .RSSLink }}" rel="feed" type="application/rss+xml" title="{{ .Site.Title }}">
{{- end }} {{- end }}
{{ .Hugo.Generator }} {{ hugo.Generator }}
</head> </head>
<body> <body>

View File

@ -0,0 +1,42 @@
import mitmproxy.http
class Events:
# HTTP lifecycle
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.
"""

View File

@ -0,0 +1,25 @@
import mitmproxy.tcp
class Events:
# TCP lifecycle
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.
"""

View File

@ -0,0 +1,36 @@
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.
"""

View File

@ -10,99 +10,6 @@ import mitmproxy.proxy.protocol
class Events: class Events:
# HTTP lifecycle
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.
"""
# TCP lifecycle
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.
"""
# 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.
"""
# Network lifecycle # Network lifecycle
def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer): def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer):
""" """

View File

@ -13,7 +13,7 @@ class InjectWebSocketMessage:
i = 0 i = 0
while not flow.ended and not flow.error: while not flow.ended and not flow.error:
await asyncio.sleep(5) await asyncio.sleep(5)
flow.inject_message(flow.client_conn, 'This is the #{} an injected message!'.format(i)) flow.inject_message(flow.client_conn, 'This is the #{} injected message!'.format(i))
i += 1 i += 1
def websocket_start(self, flow): def websocket_start(self, flow):

View File

@ -6,8 +6,15 @@ def websocket_message(flow):
# get the latest message # get the latest message
message = flow.messages[-1] message = flow.messages[-1]
# simply print the content of the message # was the message sent from the client or server?
ctx.log.info(message.content) if message.from_client:
ctx.log.info("Client sent a message: {}".format(message.content))
else:
ctx.log.info("Server sent a message: {}".format(message.content))
# manipulate the message content # manipulate the message content
message.content = re.sub(r'^Hello', 'HAPPY', message.content) message.content = re.sub(r'^Hello', 'HAPPY', message.content)
if 'FOOBAR' in message.content:
# kill the message and not send it to the other endpoint
message.kill()