Making mitmproxy more easily debuggable
04 Oct 2023Mitmproxy is just what it says on the tin: a proxy that can act as a man-in-the-middle. By default it will re-sign HTTPS traffic with its own root CA. It can also modify other requests in-place, using Python hooks.
In this post I show how I add a main to mitmproxy
hook scripts themselves.
This way both your hook and the mitmproxy invocation are contained within one file.
I think a Python module without an if __name__ == '__main__'
always is a bit of a missed opportunity.
Even if a module is nested deep into your application, it might still be a suitable place to write some examples how to use the code in the module.
Normally, when you run a mitmproxy
and want to set some hooks, you supply the script as a command line argument to the CLI tool.
mitmdump -q -s intercept.py
# or for the Command Line UI:
mitmproxy -s intercept.py
But, when running mitmproxy from the command line, you will not actually the __main__
of the mitmproxy module.
The place where the CLI tool actually lives in a code base can usually be found in setup.py
or pyproject.toml
.
There is often a parameter or section called scripts
, console_scripts
, or something similar, depending on the packaging tools.
For mitmproxy, it was in pyproject.toml
in the project.scripts
section:
[project.scripts]
mitmproxy = "mitmproxy.tools.main:mitmproxy"
mitmdump = "mitmproxy.tools.main:mitmdump"
mitmweb = "mitmproxy.tools.main:mitmweb"
In the code below I import the method that contains the CLI tool.
I also use the special __file__
variable present in Python.
This contains the full filename of the script it’s called from.
from mitmproxy import http
from mitmproxy.tools.main import mitmdump
def websocket_message(flow: http.HTTPFlow):
""" Hook on websocket messages """
last_message = flow.websocket.messages[-1]
print(last_message.content)
if __name__ == "__main__":
mitmdump(
[
"-q", # quiet flag, only script's output
"-s", # script flag
__file__, # use the same file as the hook
]
)
This way of adding a main is a bit similar to what I did earlier with streamlit. That solution turned out to have some unforeseen implications: Streamlit was convinced a form was nested in itself. So, stay tuned for the trouble that this mitmproxy hack might cause later.