mitmproxy官方提供了丰富的插件示例,并且mitmproxy核心的功能都是通过插件类实现的
一个基础的插件如下:
"""
Basic skeleton of a mitmproxy addon.
Run as follows: mitmproxy -s anatomy.py
"""
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()]
这是一个简单统计请求flow数量的插件,request方法是mitmproxy暴露出的公共方法,在客户端和mitmproxy建立链接产生request事件后,会统一调用所有插件的request。在addons中官方提供了丰富的event hook 可参考Event Hooks & API,比较常用的有:
def running(self):
mitmproxy启动时调用的方法,后续不会再触发
def configure(self, updated: set[str]):
每次配置变更时触发的方法,updated是变更配置的列表
def done(self):
mitmproxy在关闭时调用的方法
def request(self, flow: mitmproxy.http.HTTPFlow):
在获取到完整的请求后,会调用该方法,flow是请求结构体,通过修改flow可以实现如修改请求头、请求内容等操作
def response(self, flow: mitmproxy.http.HTTPFlow):
在获取到完整的响应后,会调用该方法,可修改响应内容和响应头等操作
在执行 -s addons_path 启动mitmproxy后mitmproxy/addons/script.py插件会将自定义的插件注册到运行时中:
def loadscript(self):
ctx.log.info("Loading script %s" % self.path)
if self.ns:
ctx.master.addons.remove(self.ns)
self.ns = None
with addonmanager.safecall():
ns = load_script(self.fullpath)
ctx.master.addons.register(ns)
self.ns = ns
if self.ns:
try:
ctx.master.addons.invoke_addon_sync(
self.ns, hooks.ConfigureHook(ctx.options.keys())
)
except exceptions.OptionsError as e:
script_error_handler(self.fullpath, e, msg=str(e))
if self.is_running:
# We're already running, so we call that on the addon now.
ctx.master.addons.invoke_addon_sync(self.ns, hooks.RunningHook())
同时注册的插件会附带一个watcher来检查文件本身的变更,当文件发生变化时重新载入:
async def watcher(self):
last_mtime = 0
while True:
try:
mtime = os.stat(self.fullpath).st_mtime
except FileNotFoundError:
ctx.log.info("Removing script %s" % self.path)
scripts = list(ctx.options.scripts)
scripts.remove(self.path)
ctx.options.update(scripts=scripts)
return
if mtime > last_mtime:
self.loadscript()
last_mtime = mtime
await asyncio.sleep(ReloadInterval)