Using mitmproxy to terminate TLS for HTTP/2

I have a custom HTTP server that talks direct cleartext HTTP/2. I want to wrap it in TLS in such a way that a Web browser could communicate with this server in HTTP/2 terms. This means, for example, that when my server sends a PUSH_PROMISE, the Web browser must receive that PUSH_PROMISE, etc.

Can I do this with mitmproxy?

I tried using mitmproxy in TCP proxy mode, as explained in Intercepting tcp connections with TLS 1.2 and custom certificates

So, I start up my server on port 8081, and start mitmproxy like this:

mitmdump --http2 --tcp localhost:8081 --reverse http://localhost:8081

Then I open https://localhost:8080/ in my Web browser. After clicking through certificate errors, it works — except it’s HTTP/1.1 instead of HTTP/2. With Wireshark, I can see that the browser’s TLS client hello has ALPN for h2 and http/1.1, but mitmproxy responds with ALPN for http/1.1. As a result, my server on port 8081 receives cleartext HTTP/1.1 instead of the desired cleartext HTTP/2.

The issue with this is that we can’t use offer h2 if we don’t know if the remote server would support it. This is our current logic for deciding which protocol should be negotiated. I’m open to adding a force_alpn option once https://github.com/mitmproxy/mitmproxy/pull/2100 has landed, for now you could just patch that function according to your needs. Does that work for you?

Yes, that sounds right. I’ll see if I can contribute such an option eventually. Thank you.