Repository: mitmproxy/mitmproxy

Issue:
Crash when connecting to HTTP/1.1 server via mitmdump and changing host to a server that supports h2
##### Steps to reproduce the problem:

1. Run mitmdump with a simple inline script that changes the host to one capable of h2. 
2. Open https://holmusk.com (any website that only supports http1.1) via Chrome connected to proxy server
3. Observe mitmdump stdout for error like below - 
![observed error](https://cloud.githubusercontent.com/assets/2168895/20243145/69b1f368-a942-11e6-8de8-d5f339b31d20.png)


##### Any other comments? What have you tried so far?
1) Here is the script I am using to change the host - 
![sample-script](https://cloud.githubusercontent.com/assets/2168895/20243130/cf5e276e-a941-11e6-8c4d-a358536d3606.png)
2) Changing host to a server that supports HTTP1.1 only helps to bypass the issue. So does passing in the disable http2 flag when starting mitmdump. Also force setting scheme and port to http and 80 helps to mask the issue (since h2 works on https only).
3) I have already created an AWS CloudFront distribution that is capable of h2. `flow.request.host = 'dvmu1lzlfqifb.cloudfront.net'`. Please pass in the original website hostname as a request header `flow.request.headers['Dex-Client-Domain'] = 'https://holmusk.com/'` if you want to use my distribution.
##### System information

Mitmproxy version: 0.19
Python version: 3.5.2
Platform: Linux-4.4.0-45-generic-x86_64-with-Ubuntu-16.04-xenial
SSL version: OpenSSL 1.0.2g  1 Mar 2016
Linux distro: Ubuntu 16.04 xenial



Discussion:

Resolving patch:
diff --git a/mitmproxy/proxy/protocol/tls.py b/mitmproxy/proxy/protocol/tls.py
--- a/mitmproxy/proxy/protocol/tls.py
+++ b/mitmproxy/proxy/protocol/tls.py
@@ -432,7 +432,6 @@ def alpn_for_client_connection(self):
     def __alpn_select_callback(self, conn_, options):
         # This gets triggered if we haven't established an upstream connection yet.
         default_alpn = b'http/1.1'
-        # alpn_preference = b'h2'
 
         if self.alpn_for_client_connection in options:
             choice = bytes(self.alpn_for_client_connection)
@@ -504,6 +503,17 @@ def _establish_tls_with_server(self):
                 if alpn and b"h2" in alpn and not self.config.options.http2:
                     alpn.remove(b"h2")
 
+            if self.client_conn.ssl_established:
+                # If the client has already negotiated an ALP, then force the
+                # server to use the same. This can only happen if the host gets
+                # changed after the initial connection was established. E.g.:
+                #   * the client offers http/1.1 and h2,
+                #   * the initial host is only capable of http/1.1,
+                #   * then the first server connection negotiates http/1.1,
+                #   * but after the server_conn change, the new host offers h2
+                #   * which results in garbage because the layers don' match.
+                alpn = [self.client_conn.connection.get_alpn_proto_negotiated()]
+
             ciphers_server = self.config.options.ciphers_server
             if not ciphers_server and self._client_tls:
                 ciphers_server = []


Test patch: