Repository: mitmproxy/mitmproxy

Issue:
AssertionError: File "mitmproxy/proxy/server.py", line 282, in server_event
#### Problem Description

I'm currently putting mitmproxy in front of an FTP server in reverse mode. The FTP server has a limit of five connections. If I try to connect a sixth time mitmproxy runs into the following assertion:

```
error: *.*.*.*:47774: mitmproxy has crashed!
Traceback (most recent call last):
  File "mitmproxy/proxy/server.py", line 282, in server_event
AssertionError
```

https://github.com/mitmproxy/mitmproxy/blob/64961232e6addec4abb27835d1b4efd0cdb7bb10/mitmproxy/proxy/server.py#L282

#### Steps to reproduce the behavior:

FTP Server on port 10022 with a connection limit of 1:

```sh
docker run -it --rm -p 10022:21 -p 30000-30009:30000-30009 \
  -e FTP_USER_NAME=prinzhorn \
  -e FTP_USER_PASS=prinzhorn \
  -e FTP_USER_HOME=/home/prinzhorn \
  -e FTP_PASSIVE_PORTS=30000:30009 \
  -e FTP_MAX_CLIENTS=1 \
  -e "ADDED_FLAGS=-d -d" \
  stilliard/pure-ftpd
```

Mitmproxy on 10021 in reverse:

```sh
./mitmproxy \
  --ssl-insecure \
  --listen-port 10021 \
  --listen-host 0 \
  --mode reverse:http://localhost:10022 \
  --set connection_strategy=eager \
  --set block_global=false \
  --tcp-hosts '.*'
```

Now connect twice to 10021, e.g. I was using two FileZilla tabs. The second connection triggers the error.

#### System Information

```
Mitmproxy: 7.0.0.dev binary
Python:    3.9.5
OpenSSL:   OpenSSL 1.1.1k  25 Mar 2021
Platform:  Linux-5.4.0-52-generic-x86_64-with-glibc2.31
```

I just downloaded this binary from wget https://snapshots.mitmproxy.org/branches/main/mitmproxy-main-linux.tar.gz






Discussion:
Event log with `-vvv --set proxy_debug`:

```
Proxy server listening at http://*:10021
[::1]:57280: client connect
[::1]:57280:   >> Start({})
[::1]:57280:   << NextLayerHook(data=NextLayer:None)
[::1]:57280:   >> Reply(NextLayerHook(data=NextLayer:ReverseProxy()),None)
[::1]:57280:   [nextlayer] ReverseProxy()
[::1]:57280:   >> Start({})
[::1]:57280:   << OpenConnection({'connection': Server({'id': '…ae9b46', 'address': ('localhost', 10022), 'state': <ConnectionState.CLOSED: 0>})})
[::1]:57280:   << OpenConnection({'connection': Server({'id': '…ae9b46', 'address': ('localhost', 10022), 'state': <ConnectionState.CLOSED: 0>})})
[::1]:57280: server connect localhost ([::1]:10022)
[::1]:57280:   >> Reply(OpenConnection({'connection': Server({'id': '…ae9b46', 'address': ('localhost', 10022), 'state': <ConnectionState.OPEN: 3>, 'timestamp_start': 1624542764.7953513, 'timestamp_tcp_setup': 1624542764.8006616, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57281, 0, 0)})}),None)
[::1]:57280:     >> Start({})
[::1]:57280:   >> DataReceived(server, b'220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n220-You are user number 1 of 1 allowed.\r\n220-Local time is now 13:52. Server port: 21.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections are also welcome on this server.\r\n220 You will be disconnected after 15 minutes of inactivity.\r\n')
[::1]:57280:     >> DataReceived(server, b'220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n220-You are user number 1 of 1 allowed.\r\n220-Local time is now 13:52. Server port: 21.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections…
[::1]:57280:     << NextLayerHook(data=NextLayer:None)
[::1]:57280:   << NextLayerHook(data=NextLayer:None)
[::1]:57280:   >> Reply(NextLayerHook(data=NextLayer:TCPLayer(state: start)),None)
[::1]:57280:     >> Reply(NextLayerHook(data=NextLayer:TCPLayer(state: start)),None)
[::1]:57280:     [nextlayer] TCPLayer(state: start)
[::1]:57280:     >> Start({})
[::1]:57280:     << TcpStartHook(flow=<TCPFlow (0 messages)>)
[::1]:57280:     << TcpStartHook(flow=<TCPFlow (0 messages)>)
[::1]:57280:   << TcpStartHook(flow=<TCPFlow (0 messages)>)
[::1]:57280:     >! DataReceived(server, b'220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n220-You are user number 1 of 1 allowed.\r\n220-Local time is now 13:52. Server port: 21.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections are also welcome on this server.\r\n220 You will be disconnected after 15 minutes of inactivity.\r\n')
[::1]:57280:   >> Reply(TcpStartHook(flow=<TCPFlow (0 messages)>),None)
[::1]:57280:     >> Reply(TcpStartHook(flow=<TCPFlow (0 messages)>),None)
[::1]:57280:     !> DataReceived(server, b'220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n220-You are user number 1 of 1 allowed.\r\n220-Local time is now 13:52. Server port: 21.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections are also welcome on this server.\r\n220 You will be disconnected after 15 minutes of inactivity.\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (1 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (1 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (1 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (1 messages)>),None)
[::1]:57280:     << SendData(client, b'220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n220-You are user number 1 of 1 allowed.\r\n220-Local time is now 13:52. Server port: 21.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections are also welcome on this server.\r\n220 You will be disconnected after 15 minutes of inactivity.\r\n')
[::1]:57280:   << SendData(client, b'220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------\r\n220-You are user number 1 of 1 allowed.\r\n220-Local time is now 13:52. Server port: 21.\r\n220-This is a private system - No anonymous login\r\n220-IPv6 connections are…
[::1]:57280:   >> DataReceived(client, b'AUTH TLS\r\n')
[::1]:57280:     >> DataReceived(client, b'AUTH TLS\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (2 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (2 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (2 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (2 messages)>),None)
[::1]:57280:     << SendData(server, b'AUTH TLS\r\n')
[::1]:57280:   << SendData(server, b'AUTH TLS\r\n')
[::1]:57280:   >> DataReceived(server, b'500 This security scheme is not implemented\r\n')
[::1]:57280:     >> DataReceived(server, b'500 This security scheme is not implemented\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (3 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (3 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (3 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (3 messages)>),None)
[::1]:57280:     << SendData(client, b'500 This security scheme is not implemented\r\n')
[::1]:57280:   << SendData(client, b'500 This security scheme is not implemented\r\n')
[::1]:57280:   >> DataReceived(client, b'AUTH SSL\r\n')
[::1]:57280:     >> DataReceived(client, b'AUTH SSL\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (4 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (4 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (4 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (4 messages)>),None)
[::1]:57280:     << SendData(server, b'AUTH SSL\r\n')
[::1]:57280:   << SendData(server, b'AUTH SSL\r\n')
[::1]:57280:   >> DataReceived(server, b'500 This security scheme is not implemented\r\n')
[::1]:57280:     >> DataReceived(server, b'500 This security scheme is not implemented\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (5 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (5 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (5 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (5 messages)>),None)
[::1]:57280:     << SendData(client, b'500 This security scheme is not implemented\r\n')
[::1]:57280:   << SendData(client, b'500 This security scheme is not implemented\r\n')
[::1]:57280:   >> DataReceived(client, b'USER prinzhorn\r\n')
[::1]:57280:     >> DataReceived(client, b'USER prinzhorn\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (6 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (6 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (6 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (6 messages)>),None)
[::1]:57280:     << SendData(server, b'USER prinzhorn\r\n')
[::1]:57280:   << SendData(server, b'USER prinzhorn\r\n')
[::1]:57280:   >> DataReceived(server, b'331 User prinzhorn OK. Password required\r\n')
[::1]:57280:     >> DataReceived(server, b'331 User prinzhorn OK. Password required\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (7 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (7 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (7 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (7 messages)>),None)
[::1]:57280:     << SendData(client, b'331 User prinzhorn OK. Password required\r\n')
[::1]:57280:   << SendData(client, b'331 User prinzhorn OK. Password required\r\n')
[::1]:57280:   >> DataReceived(client, b'PASS prinzhorn\r\n')
[::1]:57280:     >> DataReceived(client, b'PASS prinzhorn\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (8 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (8 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (8 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (8 messages)>),None)
[::1]:57280:     << SendData(server, b'PASS prinzhorn\r\n')
[::1]:57280:   << SendData(server, b'PASS prinzhorn\r\n')
[::1]:57280:   >> DataReceived(server, b'230 OK. Current directory is /\r\n')
[::1]:57280:     >> DataReceived(server, b'230 OK. Current directory is /\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (9 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (9 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (9 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (9 messages)>),None)
[::1]:57280:     << SendData(client, b'230 OK. Current directory is /\r\n')
[::1]:57280:   << SendData(client, b'230 OK. Current directory is /\r\n')
[::1]:57280:   >> DataReceived(client, b'SYST\r\n')
[::1]:57280:     >> DataReceived(client, b'SYST\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (10 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (10 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (10 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (10 messages)>),None)
[::1]:57280:     << SendData(server, b'SYST\r\n')
[::1]:57280:   << SendData(server, b'SYST\r\n')
[::1]:57280:   >> DataReceived(server, b'215 UNIX Type: L8\r\n')
[::1]:57280:     >> DataReceived(server, b'215 UNIX Type: L8\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (11 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (11 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (11 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (11 messages)>),None)
[::1]:57280:     << SendData(client, b'215 UNIX Type: L8\r\n')
[::1]:57280:   << SendData(client, b'215 UNIX Type: L8\r\n')
[::1]:57280:   >> DataReceived(client, b'FEAT\r\n')
[::1]:57280:     >> DataReceived(client, b'FEAT\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (12 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (12 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (12 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (12 messages)>),None)
[::1]:57280:     << SendData(server, b'FEAT\r\n')
[::1]:57280:   << SendData(server, b'FEAT\r\n')
[::1]:57280:   >> DataReceived(server, b'211-Extensions supported:\r\n EPRT\r\n IDLE\r\n MDTM\r\n SIZE\r\n MFMT\r\n REST STREAM\r\n MLST type*;size*;sizd*;modify*;UNIX.mode*;UNIX.uid*;UNIX.gid*;unique*;\r\n MLSD\r\n AUTH TLS\r\n PBSZ\r\n PROT\r\n UTF8\r\n TVFS\r\n ESTA\r\n PASV\r\n EPSV\r\n SPSV\r\r\n211 End.\r\n')
[::1]:57280:     >> DataReceived(server, b'211-Extensions supported:\r\n EPRT\r\n IDLE\r\n MDTM\r\n SIZE\r\n MFMT\r\n REST STREAM\r\n MLST type*;size*;sizd*;modify*;UNIX.mode*;UNIX.uid*;UNIX.gid*;unique*;\r\n MLSD\r\n AUTH TLS\r\n PBSZ\r\n PROT\r\n UTF8\r\n TVFS\r\n ESTA\r…
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (13 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (13 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (13 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (13 messages)>),None)
[::1]:57280:     << SendData(client, b'211-Extensions supported:\r\n EPRT\r\n IDLE\r\n MDTM\r\n SIZE\r\n MFMT\r\n REST STREAM\r\n MLST type*;size*;sizd*;modify*;UNIX.mode*;UNIX.uid*;UNIX.gid*;unique*;\r\n MLSD\r\n AUTH TLS\r\n PBSZ\r\n PROT\r\n UTF8\r\n TVFS\r\n ESTA\r\n PASV\r\n EPSV\r\n SPSV\r\r\n211 End.\r\n')
[::1]:57280:   << SendData(client, b'211-Extensions supported:\r\n EPRT\r\n IDLE\r\n MDTM\r\n SIZE\r\n MFMT\r\n REST STREAM\r\n MLST type*;size*;sizd*;modify*;UNIX.mode*;UNIX.uid*;UNIX.gid*;unique*;\r\n MLSD\r\n AUTH TLS\r\n PBSZ\r\n PROT\r\n UTF8\r\n TVFS\r\n ESTA\r\n P…
[::1]:57280:   >> DataReceived(client, b'OPTS UTF8 ON\r\n')
[::1]:57280:     >> DataReceived(client, b'OPTS UTF8 ON\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (14 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (14 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (14 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (14 messages)>),None)
[::1]:57280:     << SendData(server, b'OPTS UTF8 ON\r\n')
[::1]:57280:   << SendData(server, b'OPTS UTF8 ON\r\n')
[::1]:57280:   >> DataReceived(server, b'200 OK, UTF-8 enabled\r\n')
[::1]:57280:     >> DataReceived(server, b'200 OK, UTF-8 enabled\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (15 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (15 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (15 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (15 messages)>),None)
[::1]:57280:     << SendData(client, b'200 OK, UTF-8 enabled\r\n')
[::1]:57280:   << SendData(client, b'200 OK, UTF-8 enabled\r\n')
[::1]:57280:   >> DataReceived(client, b'PWD\r\n')
[::1]:57280:     >> DataReceived(client, b'PWD\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (16 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (16 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (16 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (16 messages)>),None)
[::1]:57280:     << SendData(server, b'PWD\r\n')
[::1]:57280:   << SendData(server, b'PWD\r\n')
[::1]:57280:   >> DataReceived(server, b'257 "/" is your current location\r\n')
[::1]:57280:     >> DataReceived(server, b'257 "/" is your current location\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (17 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (17 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (17 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (17 messages)>),None)
[::1]:57280:     << SendData(client, b'257 "/" is your current location\r\n')
[::1]:57280:   << SendData(client, b'257 "/" is your current location\r\n')
[::1]:57280:   >> DataReceived(client, b'TYPE I\r\n')
[::1]:57280:     >> DataReceived(client, b'TYPE I\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (18 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (18 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (18 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (18 messages)>),None)
[::1]:57280:     << SendData(server, b'TYPE I\r\n')
[::1]:57280:   << SendData(server, b'TYPE I\r\n')
[::1]:57280:   >> DataReceived(server, b'200 TYPE is now 8-bit binary\r\n')
[::1]:57280:     >> DataReceived(server, b'200 TYPE is now 8-bit binary\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (19 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (19 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (19 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (19 messages)>),None)
[::1]:57280:     << SendData(client, b'200 TYPE is now 8-bit binary\r\n')
[::1]:57280:   << SendData(client, b'200 TYPE is now 8-bit binary\r\n')
[::1]:57280:   >> DataReceived(client, b'EPSV\r\n')
[::1]:57280:     >> DataReceived(client, b'EPSV\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (20 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (20 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (20 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (20 messages)>),None)
[::1]:57280:     << SendData(server, b'EPSV\r\n')
[::1]:57280:   << SendData(server, b'EPSV\r\n')
[::1]:57280:   >> DataReceived(server, b'229 Extended Passive mode OK (|||30007|)\r\n')
[::1]:57280:     >> DataReceived(server, b'229 Extended Passive mode OK (|||30007|)\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (21 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (21 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (21 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (21 messages)>),None)
[::1]:57280:     << SendData(client, b'229 Extended Passive mode OK (|||30007|)\r\n')
[::1]:57280:   << SendData(client, b'229 Extended Passive mode OK (|||30007|)\r\n')
[::1]:57280:   >> DataReceived(client, b'MLSD\r\n')
[::1]:57280:     >> DataReceived(client, b'MLSD\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (22 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (22 messages)>)
[::1]:57280 -> tcp -> localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (22 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (22 messages)>),None)
[::1]:57280:     << SendData(server, b'MLSD\r\n')
[::1]:57280:   << SendData(server, b'MLSD\r\n')
[::1]:57280:   >> DataReceived(server, b'150 Accepted data connection\r\n226-Options: -a -l \r\n226 2 matches total\r\n')
[::1]:57280:     >> DataReceived(server, b'150 Accepted data connection\r\n226-Options: -a -l \r\n226 2 matches total\r\n')
[::1]:57280:     << TcpMessageHook(flow=<TCPFlow (23 messages)>)
[::1]:57280:   << TcpMessageHook(flow=<TCPFlow (23 messages)>)
[::1]:57280 <- tcp <- localhost:10022
[::1]:57280:   >> Reply(TcpMessageHook(flow=<TCPFlow (23 messages)>),None)
[::1]:57280:     >> Reply(TcpMessageHook(flow=<TCPFlow (23 messages)>),None)
[::1]:57280:     << SendData(client, b'150 Accepted data connection\r\n226-Options: -a -l \r\n226 2 matches total\r\n')
[::1]:57280:   << SendData(client, b'150 Accepted data connection\r\n226-Options: -a -l \r\n226 2 matches total\r\n')
[::1]:57283: client connect
[::1]:57283:   >> Start({})
[::1]:57283:   << NextLayerHook(data=NextLayer:None)
[::1]:57283:   >> Reply(NextLayerHook(data=NextLayer:ReverseProxy()),None)
[::1]:57283:   [nextlayer] ReverseProxy()
[::1]:57283:   >> Start({})
[::1]:57283:   << OpenConnection({'connection': Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CLOSED: 0>})})
[::1]:57283:   << OpenConnection({'connection': Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CLOSED: 0>})})
[::1]:57283: server connect localhost ([::1]:10022)
[::1]:57283:   >> Reply(OpenConnection({'connection': Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.OPEN: 3>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57284, 0, 0)})}),None)
[::1]:57283:     >> Start({})
[::1]:57283:   >> DataReceived(server, b'421 1 users (the maximum) are already logged in, sorry\r\n')
[::1]:57283:     >> DataReceived(server, b'421 1 users (the maximum) are already logged in, sorry\r\n')
[::1]:57283:     << NextLayerHook(data=NextLayer:None)
[::1]:57283:   << NextLayerHook(data=NextLayer:None)
[::1]:57283:   >> ConnectionClosed(connection=Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CAN_WRITE: 2>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57284, 0, 0)}))
[::1]:57283:     >! ConnectionClosed(connection=Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CAN_WRITE: 2>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57284, 0, 0)}))
[::1]:57283:   >> Reply(NextLayerHook(data=NextLayer:TCPLayer(state: start)),None)
[::1]:57283:     >> Reply(NextLayerHook(data=NextLayer:TCPLayer(state: start)),None)
[::1]:57283:     [nextlayer] TCPLayer(state: start)
[::1]:57283:     >> Start({})
[::1]:57283:     << TcpStartHook(flow=<TCPFlow (0 messages)>)
[::1]:57283:     << TcpStartHook(flow=<TCPFlow (0 messages)>)
[::1]:57283:   << TcpStartHook(flow=<TCPFlow (0 messages)>)
[::1]:57283:     >! DataReceived(server, b'421 1 users (the maximum) are already logged in, sorry\r\n')
[::1]:57283:     !> ConnectionClosed(connection=Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CAN_WRITE: 2>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57284, 0, 0)}))
[::1]:57283:     >! ConnectionClosed(connection=Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CAN_WRITE: 2>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57284, 0, 0)}))
[::1]:57283:   >> Reply(TcpStartHook(flow=<TCPFlow (0 messages)>),None)
[::1]:57283:     >> Reply(TcpStartHook(flow=<TCPFlow (0 messages)>),None)
[::1]:57283:     << OpenConnection({'connection': Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CAN_WRITE: 2>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname': ('::1', 57284, 0, 0)})})
[::1]:57283:   << OpenConnection({'connection': Server({'id': '…3e6328', 'address': ('localhost', 10022), 'state': <ConnectionState.CAN_WRITE: 2>, 'timestamp_start': 1624542780.254956, 'timestamp_tcp_setup': 1624542780.2567515, 'peername': ('::1', 10022, 0, 0), 'sockname…
[::1]:57283: mitmproxy has crashed!
Traceback (most recent call last):
  File "c:\users\user\git\mitmproxy\mitmproxy\proxy\server.py", line 282, in server_event
    assert command.connection not in self.transports
AssertionError
```

Resolving patch:
diff --git a/mitmproxy/connection.py b/mitmproxy/connection.py
--- a/mitmproxy/connection.py
+++ b/mitmproxy/connection.py
@@ -91,6 +91,7 @@ class Connection(serializable.Serializable, metaclass=ABCMeta):
     For server connections, this value may also be set to `True`, which means "use `Server.address`".
     """
 
+    timestamp_start: Optional[float]
     timestamp_end: Optional[float] = None
     """*Timestamp:* Connection has been closed."""
     timestamp_tls_setup: Optional[float] = None
diff --git a/mitmproxy/proxy/layers/tcp.py b/mitmproxy/proxy/layers/tcp.py
--- a/mitmproxy/proxy/layers/tcp.py
+++ b/mitmproxy/proxy/layers/tcp.py
@@ -70,7 +70,7 @@ def start(self, _) -> layer.CommandGenerator[None]:
         if self.flow:
             yield TcpStartHook(self.flow)
 
-        if not self.context.server.connected:
+        if self.context.server.timestamp_start is None:
             err = yield commands.OpenConnection(self.context.server)
             if err:
                 if self.flow:


Test patch:
diff --git a/test/mitmproxy/proxy/layers/test_tcp.py b/test/mitmproxy/proxy/layers/test_tcp.py
--- a/test/mitmproxy/proxy/layers/test_tcp.py
+++ b/test/mitmproxy/proxy/layers/test_tcp.py
@@ -1,7 +1,6 @@
 import pytest
 
 from mitmproxy.proxy.commands import CloseConnection, OpenConnection, SendData
-from mitmproxy.connection import ConnectionState
 from mitmproxy.proxy.events import ConnectionClosed, DataReceived
 from mitmproxy.proxy.layers import tcp
 from mitmproxy.proxy.layers.tcp import TcpMessageInjected
@@ -19,7 +18,7 @@ def test_open_connection(tctx):
             << OpenConnection(tctx.server)
     )
 
-    tctx.server.state = ConnectionState.OPEN
+    tctx.server.timestamp_start = 1624544785
     assert (
             Playbook(tcp.TCPLayer(tctx, True))
             << None
diff --git a/test/mitmproxy/proxy/test_layer.py b/test/mitmproxy/proxy/test_layer.py
--- a/test/mitmproxy/proxy/test_layer.py
+++ b/test/mitmproxy/proxy/test_layer.py
@@ -52,7 +52,8 @@ def state_bar(self, event: events.Event) -> layer.CommandGenerator[None]:
                 << commands.Log(" >! DataReceived(client, b'foo')", "debug")
                 >> tutils.reply(None, to=-3)
                 << commands.Log(" >> Reply(OpenConnection({'connection': Server("
-                                "{'id': '…rverid', 'address': None, 'state': <ConnectionState.OPEN: 3>})}),None)", "debug")
+                                "{'id': '…rverid', 'address': None, 'state': <ConnectionState.OPEN: 3>, "
+                                "'timestamp_start': 1624544785})}),None)", "debug")
                 << commands.Log(" !> DataReceived(client, b'foo')", "debug")
 
                 << commands.Log("baz", "info")
diff --git a/test/mitmproxy/proxy/tutils.py b/test/mitmproxy/proxy/tutils.py
--- a/test/mitmproxy/proxy/tutils.py
+++ b/test/mitmproxy/proxy/tutils.py
@@ -193,8 +193,10 @@ def __bool__(self):
                         setattr(x, name, value())
                 if isinstance(x, events.OpenConnectionCompleted) and not x.reply:
                     x.command.connection.state = ConnectionState.OPEN
+                    x.command.connection.timestamp_start = 1624544785
                 elif isinstance(x, events.ConnectionClosed):
                     x.connection.state &= ~ConnectionState.CAN_READ
+                    x.connection.timestamp_end = 1624544787
 
                 self.actual.append(x)
                 try: