Recently I needed to mitmproxy a local nginx server setup with HTTP/2 enabled, so I went through the Modes Of operation documentation page of the mitmproxy and started it like this:
./mitmdump --mode reverse:https://localhost -p 8080 --ssl-insecure
This command starts it in the reverse proxy mode which listens on port 8080. Additionally, it doesn't verify upstream TLS certificates.
After accessing https://localhost:8080 through Firefox, I saw that nginx answered using HTTP/1.1. Moreover, in Wireshark I saw that TLS ClientHello from mitmproxy to the nginx server didn't contain ALPN extension field, whereas ClientHello from Firefox to mitmproxy did contain it. My assumption was that mitmproxy doesn't mirror ALPN negotiation correctly, so I started to look for a solution and found the following mitmproxy option:
connection_strategy - Determine when server connections should be established. When set to lazy, mitmproxy tries to defer establishing an upstream connection as long as possible. This makes it possible to use server replay while being offline. When set to eager, mitmproxy can detect protocols with server-side greetings, as well as accurately mirror TLS ALPN negotiation. Default: eager Choices: eager, lazy
In my understanding the "accurately mirror TLS ALPN negotiation" is exactly what I needed, but since it is the default behavior of mitmproxy and as I described it didn't work, I decided to check what will happen if I set it to lazy like this:
./mitmdump --mode reverse:https://localhost -p 8080 --ssl-insecure --set connection_strategy=lazy
And it worked. I could see that nginx responds using HTTP/2.
Could someone point out to me why my understanding of the connecttion_strategy option is wrong? Does it have to do something with the way reverse proxies work?
You have understood everything correctly, not mirroring the ALPN is a bug in our current implementation. I've filed an issue here: https://github.com/mitmproxy/mitmproxy/issues/5369. :)