h2 icon indicating copy to clipboard operation
h2 copied to clipboard

ProtocolError on receive_data after connection is closed

Open Tronic opened this issue 6 years ago • 3 comments

I am randomly getting this error on h2 3.1.1 (about once a week on a busy H2 client):

Traceback (most recent call last):
  File "site-packages/h2/connection.py", line 224, in process_input
    func, target_state = self._transitions[(self.state, input_)]
KeyError: (<ConnectionState.CLOSED: 3>, <ConnectionInputs.RECV_PING: 14>)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "nameclient.py", line 84, in recv_task
    for event in self.conn.receive_data(data):
  File "site-packages/h2/connection.py", line 1463, in receive_data
    events.extend(self._receive_frame(frame))
  File "site-packages/h2/connection.py", line 1486, in _receive_frame
    frames, events = self._frame_dispatch_table[frame.__class__](frame)
  File "site-packages/h2/connection.py", line 1725, in _receive_ping_frame
    ConnectionInputs.RECV_PING
  File "site-packages/h2/connection.py", line 229, in process_input
    "Invalid input %s in state %s" % (input_, old_state)
h2.exceptions.ProtocolError: Invalid input ConnectionInputs.RECV_PING in state ConnectionState.CLOSED

Unfortunately the code is somewhat complicated but I believe the connection closing logic to be correct. I never call close_connection and I quit receiving once ConnectionTerminated appears.

Possibly H2 is internally closing the connection due to ping timeout or other issue, and then throws me an error when the peer still sends something. Quite likely this actually happens when my laptop is in sleep, and H2 going ping-timeout meanwhile, with some data still in receive buffers. I would expect a ConnectionTerminated event in that case, after any remaining events are relayed, not an exception.

Tronic avatar Sep 03 '19 04:09 Tronic

Just want to chime in, we're seeing those errors some times too, we have a gRPC client with HTTP2 proxy, and the client throws this:

 Traceback (most recent call last):
   File "/usr/lib/python3.5/asyncio/events.py", line 127, in _run
     self._callback(*self._args)
   File "/usr/lib/python3.5/asyncio/selector_events.py", line 730, in _read_ready
     self._protocol.data_received(data)
   File "/usr/local/lib/python3.5/dist-packages/aioh2/protocol.py", line 256, in data_received
     events_ = self._conn.receive_data(data)
   File "/usr/local/lib/python3.5/dist-packages/h2/connection.py", line 1463, in receive_data
     events.extend(self._receive_frame(frame))
   File "/usr/local/lib/python3.5/dist-packages/h2/connection.py", line 1486, in _receive_frame
     frames, events = self._frame_dispatch_table[frame.__class__](frame)
   File "/usr/local/lib/python3.5/dist-packages/h2/connection.py", line 1725, in _receive_ping_frame
     ConnectionInputs.RECV_PING
   File "/usr/local/lib/python3.5/dist-packages/h2/connection.py", line 229, in process_input
     "Invalid input %s in state %s" % (input_, old_state)
 h2.exceptions.ProtocolError: Invalid input ConnectionInputs.RECV_PING in state ConnectionState.CLOSED

I'm guessing this is related to the connection being terminated but RECV_PING frames are still being sent?

ardzoht avatar Dec 10 '19 20:12 ardzoht

I believe we are also seeing this in a relatively busy HTTP2 client in https://github.com/PrefectHQ/prefect/issues/7442 — happy to investigate contributing a fix but I'm not sure how this should be handled.

zanieb avatar Nov 06 '22 18:11 zanieb

I believe this is related to https://github.com/python-hyper/h2/issues/1181 H2 is closing the connection immeditietly after a GO_AWAY frame is received instead of waiting for a graceful termination after sending the connection terminated.

stephenc-pace avatar Nov 18 '22 12:11 stephenc-pace