We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
v7 reverse proxying tcp protocols crashing
#### Problem Description

i'm trying to proxy tcp protocols and periodically getting a crash:

./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181

```console
% ./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181
Proxy server listening at http://*:8181
[::1]:64076: client connect
[::1]:64076: server connect pop.gmail.com (172.253.123.109:995)
[::1]:64076: mitmproxy has crashed!
Traceback (most recent call last):
  File "mitmproxy/proxy/server.py", line 279, in server_event
  File "mitmproxy/proxy/layer.py", line 144, in handle_event
  File "mitmproxy/proxy/layer.py", line 144, in handle_event
  File "mitmproxy/proxy/tunnel.py", line 72, in _handle_event
  File "mitmproxy/proxy/layers/tls.py", line 240, in receive_data
  File "mitmproxy/proxy/layers/tls.py", line 306, in event_to_child
  File "mitmproxy/proxy/tunnel.py", line 104, in event_to_child
  File "mitmproxy/proxy/layer.py", line 144, in handle_event
  File "mitmproxy/proxy/tunnel.py", line 72, in _handle_event
  File "mitmproxy/proxy/layers/tls.py", line 225, in receive_data
AttributeError: 'NoneType' object has no attribute 'bio_write'

[::1]:64076: client disconnect
[::1]:64076: closing transports...
[::1]:64076: server disconnect pop.gmail.com (172.253.123.109:995)
[::1]:64076: transports closed!
[::1]:64180: client connect
[::1]:64180: server connect pop.gmail.com (172.253.123.109:995)
[::1]:64180 <- tcp <- pop.gmail.com:995

    +OK Gpop ready for requests from 24.137.254.57 s4mb63968478vsi

[::1]:64180 -> tcp -> pop.gmail.com:995

    CAPA

[::1]:64180 <- tcp <- pop.gmail.com:995

    +OK Capability list follows
    USER
    RESP-CODES
    EXPIRE 0
    LOGIN-DELAY 300
    TOP
    UIDL
    X-GOOGLE-RICO
    SASL PLAIN XOAUTH2 OAUTHBEARER
    .

[::1]:64180: half-closing Server(pop.gmail.com:995, state=open, tls, src_port=64181)
[::1]:64180: server disconnect pop.gmail.com (172.253.123.109:995)
[::1]:64180: client disconnect
[::1]:64180: closing transports...
[::1]:64180: transports closed!
```

#### System Information

% ./mitmdump --version
Mitmproxy: 7.0.0.dev binary
Python:    3.9.5
OpenSSL:   OpenSSL 1.1.1k  25 Mar 2021
Platform:  macOS-11.2.3-x86_64-i386-64bit

v7 reverse proxying tcp protocols crashing
#### Problem Description

i'm trying to proxy tcp protocols and periodically getting a crash:

./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181

```console
% ./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181
Proxy server listening at http://*:8181
[::1]:64076: client connect
[::1]:64076: server connect pop.gmail.com (172.253.123.109:995)
[::1]:64076: mitmproxy has crashed!
Traceback (most recent call last):
  File "mitmproxy/proxy/server.py", line 279, in server_event
  File "mitmproxy/proxy/layer.py", line 144, in handle_event
  File "mitmproxy/proxy/layer.py", line 144, in handle_event
  File "mitmproxy/proxy/tunnel.py", line 72, in _handle_event
  File "mitmproxy/proxy/layers/tls.py", line 240, in receive_data
  File "mitmproxy/proxy/layers/tls.py", line 306, in event_to_child
  File "mitmproxy/proxy/tunnel.py", line 104, in event_to_child
  File "mitmproxy/proxy/layer.py", line 144, in handle_event
  File "mitmproxy/proxy/tunnel.py", line 72, in _handle_event
  File "mitmproxy/proxy/layers/tls.py", line 225, in receive_data
AttributeError: 'NoneType' object has no attribute 'bio_write'

[::1]:64076: client disconnect
[::1]:64076: closing transports...
[::1]:64076: server disconnect pop.gmail.com (172.253.123.109:995)
[::1]:64076: transports closed!
[::1]:64180: client connect
[::1]:64180: server connect pop.gmail.com (172.253.123.109:995)
[::1]:64180 <- tcp <- pop.gmail.com:995

    +OK Gpop ready for requests from 24.137.254.57 s4mb63968478vsi

[::1]:64180 -> tcp -> pop.gmail.com:995

    CAPA

[::1]:64180 <- tcp <- pop.gmail.com:995

    +OK Capability list follows
    USER
    RESP-CODES
    EXPIRE 0
    LOGIN-DELAY 300
    TOP
    UIDL
    X-GOOGLE-RICO
    SASL PLAIN XOAUTH2 OAUTHBEARER
    .

[::1]:64180: half-closing Server(pop.gmail.com:995, state=open, tls, src_port=64181)
[::1]:64180: server disconnect pop.gmail.com (172.253.123.109:995)
[::1]:64180: client disconnect
[::1]:64180: closing transports...
[::1]:64180: transports closed!
```

#### System Information

% ./mitmdump --version
Mitmproxy: 7.0.0.dev binary
Python:    3.9.5
OpenSSL:   OpenSSL 1.1.1k  25 Mar 2021
Platform:  macOS-11.2.3-x86_64-i386-64bit


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mitmproxy/addons/next_layer.py`
Content:
```
1 import re
2 from typing import Type, Sequence, Union, Tuple, Any, Iterable, Optional, List
3 
4 from mitmproxy import ctx, exceptions, connection
5 from mitmproxy.net.tls import is_tls_record_magic
6 from mitmproxy.proxy.layers.http import HTTPMode
7 from mitmproxy.proxy import context, layer, layers
8 from mitmproxy.proxy.layers import modes
9 from mitmproxy.proxy.layers.tls import HTTP_ALPNS, parse_client_hello
10 
11 LayerCls = Type[layer.Layer]
12 
13 
14 def stack_match(
15         context: context.Context,
16         layers: Sequence[Union[LayerCls, Tuple[LayerCls, ...]]]
17 ) -> bool:
18     if len(context.layers) != len(layers):
19         return False
20     return all(
21         expected is Any or isinstance(actual, expected)
22         for actual, expected in zip(context.layers, layers)
23     )
24 
25 
26 class NextLayer:
27     ignore_hosts: Iterable[re.Pattern] = ()
28     allow_hosts: Iterable[re.Pattern] = ()
29     tcp_hosts: Iterable[re.Pattern] = ()
30 
31     def configure(self, updated):
32         if "tcp_hosts" in updated:
33             self.tcp_hosts = [
34                 re.compile(x, re.IGNORECASE) for x in ctx.options.tcp_hosts
35             ]
36         if "allow_hosts" in updated or "ignore_hosts" in updated:
37             if ctx.options.allow_hosts and ctx.options.ignore_hosts:
38                 raise exceptions.OptionsError("The allow_hosts and ignore_hosts options are mutually exclusive.")
39             self.ignore_hosts = [
40                 re.compile(x, re.IGNORECASE) for x in ctx.options.ignore_hosts
41             ]
42             self.allow_hosts = [
43                 re.compile(x, re.IGNORECASE) for x in ctx.options.allow_hosts
44             ]
45 
46     def ignore_connection(self, server_address: Optional[connection.Address], data_client: bytes) -> Optional[bool]:
47         """
48         Returns:
49             True, if the connection should be ignored.
50             False, if it should not be ignored.
51             None, if we need to wait for more input data.
52         """
53         if not ctx.options.ignore_hosts and not ctx.options.allow_hosts:
54             return False
55 
56         hostnames: List[str] = []
57         if server_address is not None:
58             hostnames.append(server_address[0])
59         if is_tls_record_magic(data_client):
60             try:
61                 ch = parse_client_hello(data_client)
62                 if ch is None:  # not complete yet
63                     return None
64                 sni = ch.sni
65             except ValueError:
66                 pass
67             else:
68                 if sni:
69                     hostnames.append(sni)
70 
71         if not hostnames:
72             return False
73 
74         if ctx.options.ignore_hosts:
75             return any(
76                 re.search(rex, host, re.IGNORECASE)
77                 for host in hostnames
78                 for rex in ctx.options.ignore_hosts
79             )
80         elif ctx.options.allow_hosts:
81             return not any(
82                 re.search(rex, host, re.IGNORECASE)
83                 for host in hostnames
84                 for rex in ctx.options.allow_hosts
85             )
86         else:  # pragma: no cover
87             raise AssertionError()
88 
89     def next_layer(self, nextlayer: layer.NextLayer):
90         nextlayer.layer = self._next_layer(
91             nextlayer.context,
92             nextlayer.data_client(),
93             nextlayer.data_server(),
94         )
95 
96     def _next_layer(self, context: context.Context, data_client: bytes, data_server: bytes) -> Optional[layer.Layer]:
97         if len(context.layers) == 0:
98             return self.make_top_layer(context)
99 
100         if len(data_client) < 3 and not data_server:
101             return None
102 
103         client_tls = is_tls_record_magic(data_client)
104 
105         def s(*layers):
106             return stack_match(context, layers)
107 
108         # 1. check for --ignore/--allow
109         ignore = self.ignore_connection(context.server.address, data_client)
110         if ignore is True:
111             return layers.TCPLayer(context, ignore=True)
112         if ignore is None:
113             return None
114 
115         # 2. Check for TLS
116         if client_tls:
117             # client tls usually requires a server tls layer as parent layer, except:
118             #  - reverse proxy mode manages this itself.
119             #  - a secure web proxy doesn't have a server part.
120             if s(modes.ReverseProxy) or s(modes.HttpProxy):
121                 return layers.ClientTLSLayer(context)
122             else:
123                 # We already assign the next layer here os that ServerTLSLayer
124                 # knows that it can safely wait for a ClientHello.
125                 ret = layers.ServerTLSLayer(context)
126                 ret.child_layer = layers.ClientTLSLayer(context)
127                 return ret
128 
129         # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy.
130         if any([
131             s(modes.HttpProxy),
132             # or a "Secure Web Proxy", see https://www.chromium.org/developers/design-documents/secure-web-proxy
133             s(modes.HttpProxy, layers.ClientTLSLayer),
134         ]):
135             if ctx.options.mode == "regular":
136                 return layers.HttpLayer(context, HTTPMode.regular)
137             else:
138                 return layers.HttpLayer(context, HTTPMode.upstream)
139 
140         # 4. Check for --tcp
141         if any(
142                 (context.server.address and rex.search(context.server.address[0])) or
143                 (context.client.sni and rex.search(context.client.sni))
144                 for rex in self.tcp_hosts
145         ):
146             return layers.TCPLayer(context)
147 
148         # 5. Check for raw tcp mode.
149         very_likely_http = (
150                 context.client.alpn and context.client.alpn in HTTP_ALPNS
151         )
152         probably_no_http = not very_likely_http and (
153                 not data_client[:3].isalpha()  # the first three bytes should be the HTTP verb, so A-Za-z is expected.
154                 or data_server  # a server greeting would be uncharacteristic.
155         )
156         if ctx.options.rawtcp and probably_no_http:
157             return layers.TCPLayer(context)
158 
159         # 6. Assume HTTP by default.
160         return layers.HttpLayer(context, HTTPMode.transparent)
161 
162     def make_top_layer(self, context: context.Context) -> layer.Layer:
163         if ctx.options.mode == "regular" or ctx.options.mode.startswith("upstream:"):
164             return layers.modes.HttpProxy(context)
165 
166         elif ctx.options.mode == "transparent":
167             return layers.modes.TransparentProxy(context)
168 
169         elif ctx.options.mode.startswith("reverse:"):
170             return layers.modes.ReverseProxy(context)
171 
172         elif ctx.options.mode == "socks5":
173             return layers.modes.Socks5Proxy(context)
174 
175         else:  # pragma: no cover
176             raise AssertionError("Unknown mode.")
177
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mitmproxy/addons/next_layer.py b/mitmproxy/addons/next_layer.py
--- a/mitmproxy/addons/next_layer.py
+++ b/mitmproxy/addons/next_layer.py
@@ -115,9 +115,13 @@
         # 2. Check for TLS
         if client_tls:
             # client tls usually requires a server tls layer as parent layer, except:
-            #  - reverse proxy mode manages this itself.
             #  - a secure web proxy doesn't have a server part.
-            if s(modes.ReverseProxy) or s(modes.HttpProxy):
+            #  - reverse proxy mode manages this itself.
+            if (
+                s(modes.HttpProxy) or
+                s(modes.ReverseProxy) or
+                s(modes.ReverseProxy, layers.ServerTLSLayer)
+            ):
                 return layers.ClientTLSLayer(context)
             else:
                 # We already assign the next layer here os that ServerTLSLayer
@@ -127,11 +131,11 @@
                 return ret
 
         # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy.
-        if any([
-            s(modes.HttpProxy),
+        if (
+            s(modes.HttpProxy) or
             # or a "Secure Web Proxy", see https://www.chromium.org/developers/design-documents/secure-web-proxy
-            s(modes.HttpProxy, layers.ClientTLSLayer),
-        ]):
+            s(modes.HttpProxy, layers.ClientTLSLayer)
+        ):
             if ctx.options.mode == "regular":
                 return layers.HttpLayer(context, HTTPMode.regular)
             else:

{"golden_diff": "diff --git a/mitmproxy/addons/next_layer.py b/mitmproxy/addons/next_layer.py\n--- a/mitmproxy/addons/next_layer.py\n+++ b/mitmproxy/addons/next_layer.py\n@@ -115,9 +115,13 @@\n         # 2. Check for TLS\n         if client_tls:\n             # client tls usually requires a server tls layer as parent layer, except:\n-            #  - reverse proxy mode manages this itself.\n             #  - a secure web proxy doesn't have a server part.\n-            if s(modes.ReverseProxy) or s(modes.HttpProxy):\n+            #  - reverse proxy mode manages this itself.\n+            if (\n+                s(modes.HttpProxy) or\n+                s(modes.ReverseProxy) or\n+                s(modes.ReverseProxy, layers.ServerTLSLayer)\n+            ):\n                 return layers.ClientTLSLayer(context)\n             else:\n                 # We already assign the next layer here os that ServerTLSLayer\n@@ -127,11 +131,11 @@\n                 return ret\n \n         # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy.\n-        if any([\n-            s(modes.HttpProxy),\n+        if (\n+            s(modes.HttpProxy) or\n             # or a \"Secure Web Proxy\", see https://www.chromium.org/developers/design-documents/secure-web-proxy\n-            s(modes.HttpProxy, layers.ClientTLSLayer),\n-        ]):\n+            s(modes.HttpProxy, layers.ClientTLSLayer)\n+        ):\n             if ctx.options.mode == \"regular\":\n                 return layers.HttpLayer(context, HTTPMode.regular)\n             else:\n", "issue": "v7 reverse proxying tcp protocols crashing\n#### Problem Description\r\n\r\ni'm trying to proxy tcp protocols and periodically getting a crash:\r\n\r\n./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181\r\n\r\n```console\r\n% ./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181\r\nProxy server listening at http://*:8181\r\n[::1]:64076: client connect\r\n[::1]:64076: server connect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64076: mitmproxy has crashed!\r\nTraceback (most recent call last):\r\n  File \"mitmproxy/proxy/server.py\", line 279, in server_event\r\n  File \"mitmproxy/proxy/layer.py\", line 144, in handle_event\r\n  File \"mitmproxy/proxy/layer.py\", line 144, in handle_event\r\n  File \"mitmproxy/proxy/tunnel.py\", line 72, in _handle_event\r\n  File \"mitmproxy/proxy/layers/tls.py\", line 240, in receive_data\r\n  File \"mitmproxy/proxy/layers/tls.py\", line 306, in event_to_child\r\n  File \"mitmproxy/proxy/tunnel.py\", line 104, in event_to_child\r\n  File \"mitmproxy/proxy/layer.py\", line 144, in handle_event\r\n  File \"mitmproxy/proxy/tunnel.py\", line 72, in _handle_event\r\n  File \"mitmproxy/proxy/layers/tls.py\", line 225, in receive_data\r\nAttributeError: 'NoneType' object has no attribute 'bio_write'\r\n\r\n[::1]:64076: client disconnect\r\n[::1]:64076: closing transports...\r\n[::1]:64076: server disconnect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64076: transports closed!\r\n[::1]:64180: client connect\r\n[::1]:64180: server connect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64180 <- tcp <- pop.gmail.com:995\r\n\r\n    +OK Gpop ready for requests from 24.137.254.57 s4mb63968478vsi\r\n\r\n[::1]:64180 -> tcp -> pop.gmail.com:995\r\n\r\n    CAPA\r\n\r\n[::1]:64180 <- tcp <- pop.gmail.com:995\r\n\r\n    +OK Capability list follows\r\n    USER\r\n    RESP-CODES\r\n    EXPIRE 0\r\n    LOGIN-DELAY 300\r\n    TOP\r\n    UIDL\r\n    X-GOOGLE-RICO\r\n    SASL PLAIN XOAUTH2 OAUTHBEARER\r\n    .\r\n\r\n[::1]:64180: half-closing Server(pop.gmail.com:995, state=open, tls, src_port=64181)\r\n[::1]:64180: server disconnect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64180: client disconnect\r\n[::1]:64180: closing transports...\r\n[::1]:64180: transports closed!\r\n```\r\n\r\n#### System Information\r\n\r\n% ./mitmdump --version\r\nMitmproxy: 7.0.0.dev binary\r\nPython:    3.9.5\r\nOpenSSL:   OpenSSL 1.1.1k  25 Mar 2021\r\nPlatform:  macOS-11.2.3-x86_64-i386-64bit\r\n\nv7 reverse proxying tcp protocols crashing\n#### Problem Description\r\n\r\ni'm trying to proxy tcp protocols and periodically getting a crash:\r\n\r\n./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181\r\n\r\n```console\r\n% ./mitmdump --set termlog_verbosity=debug  --mode reverse:https://pop.gmail.com:995 --set connection_strategy=eager -w mail --set flow_detail=3 -p 8181\r\nProxy server listening at http://*:8181\r\n[::1]:64076: client connect\r\n[::1]:64076: server connect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64076: mitmproxy has crashed!\r\nTraceback (most recent call last):\r\n  File \"mitmproxy/proxy/server.py\", line 279, in server_event\r\n  File \"mitmproxy/proxy/layer.py\", line 144, in handle_event\r\n  File \"mitmproxy/proxy/layer.py\", line 144, in handle_event\r\n  File \"mitmproxy/proxy/tunnel.py\", line 72, in _handle_event\r\n  File \"mitmproxy/proxy/layers/tls.py\", line 240, in receive_data\r\n  File \"mitmproxy/proxy/layers/tls.py\", line 306, in event_to_child\r\n  File \"mitmproxy/proxy/tunnel.py\", line 104, in event_to_child\r\n  File \"mitmproxy/proxy/layer.py\", line 144, in handle_event\r\n  File \"mitmproxy/proxy/tunnel.py\", line 72, in _handle_event\r\n  File \"mitmproxy/proxy/layers/tls.py\", line 225, in receive_data\r\nAttributeError: 'NoneType' object has no attribute 'bio_write'\r\n\r\n[::1]:64076: client disconnect\r\n[::1]:64076: closing transports...\r\n[::1]:64076: server disconnect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64076: transports closed!\r\n[::1]:64180: client connect\r\n[::1]:64180: server connect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64180 <- tcp <- pop.gmail.com:995\r\n\r\n    +OK Gpop ready for requests from 24.137.254.57 s4mb63968478vsi\r\n\r\n[::1]:64180 -> tcp -> pop.gmail.com:995\r\n\r\n    CAPA\r\n\r\n[::1]:64180 <- tcp <- pop.gmail.com:995\r\n\r\n    +OK Capability list follows\r\n    USER\r\n    RESP-CODES\r\n    EXPIRE 0\r\n    LOGIN-DELAY 300\r\n    TOP\r\n    UIDL\r\n    X-GOOGLE-RICO\r\n    SASL PLAIN XOAUTH2 OAUTHBEARER\r\n    .\r\n\r\n[::1]:64180: half-closing Server(pop.gmail.com:995, state=open, tls, src_port=64181)\r\n[::1]:64180: server disconnect pop.gmail.com (172.253.123.109:995)\r\n[::1]:64180: client disconnect\r\n[::1]:64180: closing transports...\r\n[::1]:64180: transports closed!\r\n```\r\n\r\n#### System Information\r\n\r\n% ./mitmdump --version\r\nMitmproxy: 7.0.0.dev binary\r\nPython:    3.9.5\r\nOpenSSL:   OpenSSL 1.1.1k  25 Mar 2021\r\nPlatform:  macOS-11.2.3-x86_64-i386-64bit\r\n\n", "before_files": [{"content": "import re\nfrom typing import Type, Sequence, Union, Tuple, Any, Iterable, Optional, List\n\nfrom mitmproxy import ctx, exceptions, connection\nfrom mitmproxy.net.tls import is_tls_record_magic\nfrom mitmproxy.proxy.layers.http import HTTPMode\nfrom mitmproxy.proxy import context, layer, layers\nfrom mitmproxy.proxy.layers import modes\nfrom mitmproxy.proxy.layers.tls import HTTP_ALPNS, parse_client_hello\n\nLayerCls = Type[layer.Layer]\n\n\ndef stack_match(\n        context: context.Context,\n        layers: Sequence[Union[LayerCls, Tuple[LayerCls, ...]]]\n) -> bool:\n    if len(context.layers) != len(layers):\n        return False\n    return all(\n        expected is Any or isinstance(actual, expected)\n        for actual, expected in zip(context.layers, layers)\n    )\n\n\nclass NextLayer:\n    ignore_hosts: Iterable[re.Pattern] = ()\n    allow_hosts: Iterable[re.Pattern] = ()\n    tcp_hosts: Iterable[re.Pattern] = ()\n\n    def configure(self, updated):\n        if \"tcp_hosts\" in updated:\n            self.tcp_hosts = [\n                re.compile(x, re.IGNORECASE) for x in ctx.options.tcp_hosts\n            ]\n        if \"allow_hosts\" in updated or \"ignore_hosts\" in updated:\n            if ctx.options.allow_hosts and ctx.options.ignore_hosts:\n                raise exceptions.OptionsError(\"The allow_hosts and ignore_hosts options are mutually exclusive.\")\n            self.ignore_hosts = [\n                re.compile(x, re.IGNORECASE) for x in ctx.options.ignore_hosts\n            ]\n            self.allow_hosts = [\n                re.compile(x, re.IGNORECASE) for x in ctx.options.allow_hosts\n            ]\n\n    def ignore_connection(self, server_address: Optional[connection.Address], data_client: bytes) -> Optional[bool]:\n        \"\"\"\n        Returns:\n            True, if the connection should be ignored.\n            False, if it should not be ignored.\n            None, if we need to wait for more input data.\n        \"\"\"\n        if not ctx.options.ignore_hosts and not ctx.options.allow_hosts:\n            return False\n\n        hostnames: List[str] = []\n        if server_address is not None:\n            hostnames.append(server_address[0])\n        if is_tls_record_magic(data_client):\n            try:\n                ch = parse_client_hello(data_client)\n                if ch is None:  # not complete yet\n                    return None\n                sni = ch.sni\n            except ValueError:\n                pass\n            else:\n                if sni:\n                    hostnames.append(sni)\n\n        if not hostnames:\n            return False\n\n        if ctx.options.ignore_hosts:\n            return any(\n                re.search(rex, host, re.IGNORECASE)\n                for host in hostnames\n                for rex in ctx.options.ignore_hosts\n            )\n        elif ctx.options.allow_hosts:\n            return not any(\n                re.search(rex, host, re.IGNORECASE)\n                for host in hostnames\n                for rex in ctx.options.allow_hosts\n            )\n        else:  # pragma: no cover\n            raise AssertionError()\n\n    def next_layer(self, nextlayer: layer.NextLayer):\n        nextlayer.layer = self._next_layer(\n            nextlayer.context,\n            nextlayer.data_client(),\n            nextlayer.data_server(),\n        )\n\n    def _next_layer(self, context: context.Context, data_client: bytes, data_server: bytes) -> Optional[layer.Layer]:\n        if len(context.layers) == 0:\n            return self.make_top_layer(context)\n\n        if len(data_client) < 3 and not data_server:\n            return None\n\n        client_tls = is_tls_record_magic(data_client)\n\n        def s(*layers):\n            return stack_match(context, layers)\n\n        # 1. check for --ignore/--allow\n        ignore = self.ignore_connection(context.server.address, data_client)\n        if ignore is True:\n            return layers.TCPLayer(context, ignore=True)\n        if ignore is None:\n            return None\n\n        # 2. Check for TLS\n        if client_tls:\n            # client tls usually requires a server tls layer as parent layer, except:\n            #  - reverse proxy mode manages this itself.\n            #  - a secure web proxy doesn't have a server part.\n            if s(modes.ReverseProxy) or s(modes.HttpProxy):\n                return layers.ClientTLSLayer(context)\n            else:\n                # We already assign the next layer here os that ServerTLSLayer\n                # knows that it can safely wait for a ClientHello.\n                ret = layers.ServerTLSLayer(context)\n                ret.child_layer = layers.ClientTLSLayer(context)\n                return ret\n\n        # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy.\n        if any([\n            s(modes.HttpProxy),\n            # or a \"Secure Web Proxy\", see https://www.chromium.org/developers/design-documents/secure-web-proxy\n            s(modes.HttpProxy, layers.ClientTLSLayer),\n        ]):\n            if ctx.options.mode == \"regular\":\n                return layers.HttpLayer(context, HTTPMode.regular)\n            else:\n                return layers.HttpLayer(context, HTTPMode.upstream)\n\n        # 4. Check for --tcp\n        if any(\n                (context.server.address and rex.search(context.server.address[0])) or\n                (context.client.sni and rex.search(context.client.sni))\n                for rex in self.tcp_hosts\n        ):\n            return layers.TCPLayer(context)\n\n        # 5. Check for raw tcp mode.\n        very_likely_http = (\n                context.client.alpn and context.client.alpn in HTTP_ALPNS\n        )\n        probably_no_http = not very_likely_http and (\n                not data_client[:3].isalpha()  # the first three bytes should be the HTTP verb, so A-Za-z is expected.\n                or data_server  # a server greeting would be uncharacteristic.\n        )\n        if ctx.options.rawtcp and probably_no_http:\n            return layers.TCPLayer(context)\n\n        # 6. Assume HTTP by default.\n        return layers.HttpLayer(context, HTTPMode.transparent)\n\n    def make_top_layer(self, context: context.Context) -> layer.Layer:\n        if ctx.options.mode == \"regular\" or ctx.options.mode.startswith(\"upstream:\"):\n            return layers.modes.HttpProxy(context)\n\n        elif ctx.options.mode == \"transparent\":\n            return layers.modes.TransparentProxy(context)\n\n        elif ctx.options.mode.startswith(\"reverse:\"):\n            return layers.modes.ReverseProxy(context)\n\n        elif ctx.options.mode == \"socks5\":\n            return layers.modes.Socks5Proxy(context)\n\n        else:  # pragma: no cover\n            raise AssertionError(\"Unknown mode.\")\n", "path": "mitmproxy/addons/next_layer.py"}], "after_files": [{"content": "import re\nfrom typing import Type, Sequence, Union, Tuple, Any, Iterable, Optional, List\n\nfrom mitmproxy import ctx, exceptions, connection\nfrom mitmproxy.net.tls import is_tls_record_magic\nfrom mitmproxy.proxy.layers.http import HTTPMode\nfrom mitmproxy.proxy import context, layer, layers\nfrom mitmproxy.proxy.layers import modes\nfrom mitmproxy.proxy.layers.tls import HTTP_ALPNS, parse_client_hello\n\nLayerCls = Type[layer.Layer]\n\n\ndef stack_match(\n        context: context.Context,\n        layers: Sequence[Union[LayerCls, Tuple[LayerCls, ...]]]\n) -> bool:\n    if len(context.layers) != len(layers):\n        return False\n    return all(\n        expected is Any or isinstance(actual, expected)\n        for actual, expected in zip(context.layers, layers)\n    )\n\n\nclass NextLayer:\n    ignore_hosts: Iterable[re.Pattern] = ()\n    allow_hosts: Iterable[re.Pattern] = ()\n    tcp_hosts: Iterable[re.Pattern] = ()\n\n    def configure(self, updated):\n        if \"tcp_hosts\" in updated:\n            self.tcp_hosts = [\n                re.compile(x, re.IGNORECASE) for x in ctx.options.tcp_hosts\n            ]\n        if \"allow_hosts\" in updated or \"ignore_hosts\" in updated:\n            if ctx.options.allow_hosts and ctx.options.ignore_hosts:\n                raise exceptions.OptionsError(\"The allow_hosts and ignore_hosts options are mutually exclusive.\")\n            self.ignore_hosts = [\n                re.compile(x, re.IGNORECASE) for x in ctx.options.ignore_hosts\n            ]\n            self.allow_hosts = [\n                re.compile(x, re.IGNORECASE) for x in ctx.options.allow_hosts\n            ]\n\n    def ignore_connection(self, server_address: Optional[connection.Address], data_client: bytes) -> Optional[bool]:\n        \"\"\"\n        Returns:\n            True, if the connection should be ignored.\n            False, if it should not be ignored.\n            None, if we need to wait for more input data.\n        \"\"\"\n        if not ctx.options.ignore_hosts and not ctx.options.allow_hosts:\n            return False\n\n        hostnames: List[str] = []\n        if server_address is not None:\n            hostnames.append(server_address[0])\n        if is_tls_record_magic(data_client):\n            try:\n                ch = parse_client_hello(data_client)\n                if ch is None:  # not complete yet\n                    return None\n                sni = ch.sni\n            except ValueError:\n                pass\n            else:\n                if sni:\n                    hostnames.append(sni)\n\n        if not hostnames:\n            return False\n\n        if ctx.options.ignore_hosts:\n            return any(\n                re.search(rex, host, re.IGNORECASE)\n                for host in hostnames\n                for rex in ctx.options.ignore_hosts\n            )\n        elif ctx.options.allow_hosts:\n            return not any(\n                re.search(rex, host, re.IGNORECASE)\n                for host in hostnames\n                for rex in ctx.options.allow_hosts\n            )\n        else:  # pragma: no cover\n            raise AssertionError()\n\n    def next_layer(self, nextlayer: layer.NextLayer):\n        nextlayer.layer = self._next_layer(\n            nextlayer.context,\n            nextlayer.data_client(),\n            nextlayer.data_server(),\n        )\n\n    def _next_layer(self, context: context.Context, data_client: bytes, data_server: bytes) -> Optional[layer.Layer]:\n        if len(context.layers) == 0:\n            return self.make_top_layer(context)\n\n        if len(data_client) < 3 and not data_server:\n            return None\n\n        client_tls = is_tls_record_magic(data_client)\n\n        def s(*layers):\n            return stack_match(context, layers)\n\n        # 1. check for --ignore/--allow\n        ignore = self.ignore_connection(context.server.address, data_client)\n        if ignore is True:\n            return layers.TCPLayer(context, ignore=True)\n        if ignore is None:\n            return None\n\n        # 2. Check for TLS\n        if client_tls:\n            # client tls usually requires a server tls layer as parent layer, except:\n            #  - a secure web proxy doesn't have a server part.\n            #  - reverse proxy mode manages this itself.\n            if (\n                s(modes.HttpProxy) or\n                s(modes.ReverseProxy) or\n                s(modes.ReverseProxy, layers.ServerTLSLayer)\n            ):\n                return layers.ClientTLSLayer(context)\n            else:\n                # We already assign the next layer here os that ServerTLSLayer\n                # knows that it can safely wait for a ClientHello.\n                ret = layers.ServerTLSLayer(context)\n                ret.child_layer = layers.ClientTLSLayer(context)\n                return ret\n\n        # 3. Setup the HTTP layer for a regular HTTP proxy or an upstream proxy.\n        if (\n            s(modes.HttpProxy) or\n            # or a \"Secure Web Proxy\", see https://www.chromium.org/developers/design-documents/secure-web-proxy\n            s(modes.HttpProxy, layers.ClientTLSLayer)\n        ):\n            if ctx.options.mode == \"regular\":\n                return layers.HttpLayer(context, HTTPMode.regular)\n            else:\n                return layers.HttpLayer(context, HTTPMode.upstream)\n\n        # 4. Check for --tcp\n        if any(\n                (context.server.address and rex.search(context.server.address[0])) or\n                (context.client.sni and rex.search(context.client.sni))\n                for rex in self.tcp_hosts\n        ):\n            return layers.TCPLayer(context)\n\n        # 5. Check for raw tcp mode.\n        very_likely_http = (\n                context.client.alpn and context.client.alpn in HTTP_ALPNS\n        )\n        probably_no_http = not very_likely_http and (\n                not data_client[:3].isalpha()  # the first three bytes should be the HTTP verb, so A-Za-z is expected.\n                or data_server  # a server greeting would be uncharacteristic.\n        )\n        if ctx.options.rawtcp and probably_no_http:\n            return layers.TCPLayer(context)\n\n        # 6. Assume HTTP by default.\n        return layers.HttpLayer(context, HTTPMode.transparent)\n\n    def make_top_layer(self, context: context.Context) -> layer.Layer:\n        if ctx.options.mode == \"regular\" or ctx.options.mode.startswith(\"upstream:\"):\n            return layers.modes.HttpProxy(context)\n\n        elif ctx.options.mode == \"transparent\":\n            return layers.modes.TransparentProxy(context)\n\n        elif ctx.options.mode.startswith(\"reverse:\"):\n            return layers.modes.ReverseProxy(context)\n\n        elif ctx.options.mode == \"socks5\":\n            return layers.modes.Socks5Proxy(context)\n\n        else:  # pragma: no cover\n            raise AssertionError(\"Unknown mode.\")\n", "path": "mitmproxy/addons/next_layer.py"}]}