aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/addons/addheader.py13
-rw-r--r--examples/addons/anatomy.py15
-rw-r--r--examples/addons/commands-flows.py21
-rw-r--r--examples/addons/commands-paths.py32
-rw-r--r--examples/addons/commands-simple.py17
-rw-r--r--examples/addons/events.py179
-rw-r--r--examples/addons/options-configure.py28
-rw-r--r--examples/addons/options-simple.py24
-rw-r--r--examples/addons/scripting.py3
-rw-r--r--examples/simple/custom_contentview.py4
-rw-r--r--examples/simple/custom_option.py10
-rw-r--r--examples/simple/log_events.py4
12 files changed, 343 insertions, 7 deletions
diff --git a/examples/addons/addheader.py b/examples/addons/addheader.py
new file mode 100644
index 00000000..f4b29268
--- /dev/null
+++ b/examples/addons/addheader.py
@@ -0,0 +1,13 @@
+
+class AddHeader:
+ def __init__(self):
+ self.num = 0
+
+ def response(self, flow):
+ self.num = self.num + 1
+ flow.response.headers["count"] = str(self.num)
+
+
+addons = [
+ AddHeader()
+]
diff --git a/examples/addons/anatomy.py b/examples/addons/anatomy.py
new file mode 100644
index 00000000..c60afeaa
--- /dev/null
+++ b/examples/addons/anatomy.py
@@ -0,0 +1,15 @@
+from mitmproxy import ctx
+
+
+class Counter:
+ def __init__(self):
+ self.num = 0
+
+ def request(self, flow):
+ self.num = self.num + 1
+ ctx.log.info("We've seen %d flows" % self.num)
+
+
+addons = [
+ Counter()
+]
diff --git a/examples/addons/commands-flows.py b/examples/addons/commands-flows.py
new file mode 100644
index 00000000..cebc8f9d
--- /dev/null
+++ b/examples/addons/commands-flows.py
@@ -0,0 +1,21 @@
+import typing
+
+from mitmproxy import command
+from mitmproxy import ctx
+from mitmproxy import flow
+
+
+class MyAddon:
+ def __init__(self):
+ self.num = 0
+
+ @command.command("myaddon.addheader")
+ def addheader(self, flows: typing.Sequence[flow.Flow]) -> None:
+ for f in flows:
+ f.request.headers["myheader"] = "value"
+ ctx.log.alert("done")
+
+
+addons = [
+ MyAddon()
+]
diff --git a/examples/addons/commands-paths.py b/examples/addons/commands-paths.py
new file mode 100644
index 00000000..f37a0fbc
--- /dev/null
+++ b/examples/addons/commands-paths.py
@@ -0,0 +1,32 @@
+import typing
+
+from mitmproxy import command
+from mitmproxy import ctx
+from mitmproxy import flow
+from mitmproxy import types
+
+
+class MyAddon:
+ def __init__(self):
+ self.num = 0
+
+ @command.command("myaddon.histogram")
+ def histogram(
+ self,
+ flows: typing.Sequence[flow.Flow],
+ path: types.Path,
+ ) -> None:
+ totals = {}
+ for f in flows:
+ totals[f.request.host] = totals.setdefault(f.request.host, 0) + 1
+
+ fp = open(path, "w+")
+ for cnt, dom in sorted([(v, k) for (k, v) in totals.items()]):
+ fp.write("%s: %s\n" % (cnt, dom))
+
+ ctx.log.alert("done")
+
+
+addons = [
+ MyAddon()
+]
diff --git a/examples/addons/commands-simple.py b/examples/addons/commands-simple.py
new file mode 100644
index 00000000..c9cd6341
--- /dev/null
+++ b/examples/addons/commands-simple.py
@@ -0,0 +1,17 @@
+from mitmproxy import command
+from mitmproxy import ctx
+
+
+class MyAddon:
+ def __init__(self):
+ self.num = 0
+
+ @command.command("myaddon.inc")
+ def inc(self) -> None:
+ self.num += 1
+ ctx.log.info("num = %s" % self.num)
+
+
+addons = [
+ MyAddon()
+]
diff --git a/examples/addons/events.py b/examples/addons/events.py
new file mode 100644
index 00000000..93664954
--- /dev/null
+++ b/examples/addons/events.py
@@ -0,0 +1,179 @@
+import typing
+
+import mitmproxy.addonmanager
+import mitmproxy.connections
+import mitmproxy.http
+import mitmproxy.log
+import mitmproxy.tcp
+import mitmproxy.websocket
+import mitmproxy.proxy.protocol
+
+
+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
+ 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.
+ """
+
+ 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 whenver 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 tick(self):
+ """
+ A regular ticker - called approximately once every 100ms.
+ """
+
+ 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.
+ """
diff --git a/examples/addons/options-configure.py b/examples/addons/options-configure.py
new file mode 100644
index 00000000..c7638e87
--- /dev/null
+++ b/examples/addons/options-configure.py
@@ -0,0 +1,28 @@
+import typing
+
+from mitmproxy import ctx
+from mitmproxy import exceptions
+
+
+class AddHeader:
+ def load(self, loader):
+ loader.add_option(
+ name = "addheader",
+ typespec = typing.Optional[int],
+ default = None,
+ help = "Add a header to responses",
+ )
+
+ def configure(self, updates):
+ if "addheader" in updates:
+ if ctx.options.addheader is not None and ctx.options.addheader > 100:
+ raise exceptions.OptionsError("addheader must be <= 100")
+
+ def response(self, flow):
+ if ctx.options.addheader is not None:
+ flow.response.headers["addheader"] = str(ctx.options.addheader)
+
+
+addons = [
+ AddHeader()
+]
diff --git a/examples/addons/options-simple.py b/examples/addons/options-simple.py
new file mode 100644
index 00000000..0acefb3f
--- /dev/null
+++ b/examples/addons/options-simple.py
@@ -0,0 +1,24 @@
+from mitmproxy import ctx
+
+
+class AddHeader:
+ def __init__(self):
+ self.num = 0
+
+ def load(self, loader):
+ loader.add_option(
+ name = "addheader",
+ typespec = bool,
+ default = False,
+ help = "Add a count header to responses",
+ )
+
+ def response(self, flow):
+ if ctx.options.addheader:
+ self.num = self.num + 1
+ flow.response.headers["count"] = str(self.num)
+
+
+addons = [
+ AddHeader()
+]
diff --git a/examples/addons/scripting.py b/examples/addons/scripting.py
new file mode 100644
index 00000000..8b23680e
--- /dev/null
+++ b/examples/addons/scripting.py
@@ -0,0 +1,3 @@
+
+def request(flow):
+ flow.request.headers["myheader"] = "value"
diff --git a/examples/simple/custom_contentview.py b/examples/simple/custom_contentview.py
index b958bdce..77d32474 100644
--- a/examples/simple/custom_contentview.py
+++ b/examples/simple/custom_contentview.py
@@ -7,10 +7,6 @@ from mitmproxy import contentviews
class ViewSwapCase(contentviews.View):
name = "swapcase"
-
- # We don't have a good solution for the keyboard shortcut yet -
- # you manually need to find a free letter. Contributions welcome :)
- prompt = ("swap case text", "z")
content_types = ["text/plain"]
def __call__(self, data, **metadata) -> contentviews.TViewResult:
diff --git a/examples/simple/custom_option.py b/examples/simple/custom_option.py
index 5b6070dd..8d0cfe7f 100644
--- a/examples/simple/custom_option.py
+++ b/examples/simple/custom_option.py
@@ -1,3 +1,13 @@
+"""
+This example shows how addons can register custom options
+that can be configured at startup or during execution
+from the options dialog within mitmproxy.
+
+Example:
+
+$ mitmproxy --set custom=true
+$ mitmproxy --set custom # shorthand for boolean options
+"""
from mitmproxy import ctx
diff --git a/examples/simple/log_events.py b/examples/simple/log_events.py
index 581b99f3..b9aa2c1f 100644
--- a/examples/simple/log_events.py
+++ b/examples/simple/log_events.py
@@ -1,8 +1,6 @@
"""
It is recommended to use `ctx.log` for logging within a script.
-This goes to the event log in mitmproxy and to stdout in mitmdump.
-
-If you want to help us out: https://github.com/mitmproxy/mitmproxy/issues/1530 :-)
+print() statements are equivalent to ctx.log.warn().
"""
from mitmproxy import ctx