python-hyper / python-hyper/h2

StreamIDTooLowError when trying to send headers into closed stream

オープン
#1,175 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

主要言語
Python
スター
1k
フォーク
187
PR マージ指標
30日以内にマージされた PR はありません

説明

Hi!

I'm getting errors like this on the server-side:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/grpclib/server.py", line 310, in request_handler
    await method.func(stream)
  File "/usr/local/lib/python3.6/dist-packages/featureflags/server/rpc/service.py", line 51, in Exchange
    await self._queue.StoreStats.add(task, timeout=timeout)
  File "/usr/local/lib/python3.6/dist-packages/taskqueue/client/queue.py", line 21, in add
    await self._task_queue_stub.Add(task, timeout=timeout)
  File "/usr/local/lib/python3.6/dist-packages/grpclib/client.py", line 473, in __call__
    return await stream.recv_message()
  File "/usr/local/lib/python3.6/dist-packages/grpclib/client.py", line 286, in recv_message
    await self.recv_initial_metadata()
  File "/usr/local/lib/python3.6/dist-packages/grpclib/client.py", line 221, in recv_initial_metadata
    headers = await self._stream.recv_headers()
  File "/usr/local/lib/python3.6/dist-packages/grpclib/protocol.py", line 215, in recv_headers
    return await self.__headers__.get()
  File "/usr/lib/python3.6/asyncio/queues.py", line 167, in get
    yield from getter
concurrent.futures._base.CancelledError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/h2/connection.py", line 585, in _get_or_create_stream
    return self.streams[stream_id]
KeyError: 48141

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/grpclib/server.py", line 325, in request_handler
    raise
  File "/usr/local/lib/python3.6/dist-packages/grpclib/server.py", line 220, in __aexit__
    status_message=status_message)
  File "/usr/local/lib/python3.6/dist-packages/grpclib/server.py", line 170, in send_trailing_metadata
    await self._stream.send_headers(headers, end_stream=True)
  File "/usr/local/lib/python3.6/dist-packages/grpclib/protocol.py", line 267, in send_headers
    end_stream=end_stream)
  File "/usr/local/lib/python3.6/dist-packages/h2/connection.py", line 763, in send_headers
    stream_id, AllowedStreamIDs(self.config.client_side)
  File "/usr/local/lib/python3.6/dist-packages/h2/connection.py", line 587, in _get_or_create_stream
    return self._begin_new_stream(stream_id, allowed_ids)
  File "/usr/local/lib/python3.6/dist-packages/h2/connection.py", line 454, in _begin_new_stream
    raise StreamIDTooLowError(stream_id, highest_stream_id)
h2.exceptions.StreamIDTooLowError: StreamIDTooLowError: 48141 is lower than 48143

This happens when client cancels stream with RST_STREAM, and on the server-side I have a logic which looks like this:

try:
    conn.send_headers(stream_id, [...])
except h2.exceptions.StreamClosedError:
    pass

And sometimes I'm getting StreamIDTooLowError exception instead of StreamClosedError, because this behavior depends on H2Connection.streams dictionary, where streams are removed nondeterministically.

I think that H2Connection._get_or_create_stream function should check not only H2Connection.streams but also H2Connection._closed_streams.

Of course I can refactor my code and check that stream is closed by myself, but I still think that there is a bug in H2Connection.send_headers method.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

h2/connection.py の H2Connection._get_or_create_stream と send_headers から始め、RST_STREAM 後にストリームと _closed_streams がどのように維持されるかを調べます。クローズ済みストリームのパスを再現し、既存の HTTP/2 テストを確認します。完了条件は、リセットされたストリームにヘッダーを送信すると、StreamIDTooLowError ではなく、期待されるクローズ済みストリームエラーが一貫して発生することです。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
networking
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
48/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。