Skip to content

test_http2_websocket error "Ordinary CONNECT MUST NOT include :scheme or :path" #370

Description

@Joanna-D

hi,when i build package with python3-h2-4.4.1, i meet the testcases error as follow

2026-08-13 02:04:56 _____________________________ test_http2_websocket _____________________________
2026-08-13 02:04:56 
2026-08-13 02:04:56     @pytest.mark.asyncio
2026-08-13 02:04:56     async def test_http2_websocket() -> None:
2026-08-13 02:04:56         event_loop: asyncio.AbstractEventLoop = asyncio.get_running_loop()
2026-08-13 02:04:56     
2026-08-13 02:04:56         server = TCPServer(
2026-08-13 02:04:56             ASGIWrapper(sanity_framework),
2026-08-13 02:04:56             event_loop,
2026-08-13 02:04:56             Config(),
2026-08-13 02:04:56             WorkerContext(None),
2026-08-13 02:04:56             {},
2026-08-13 02:04:56             MemoryReader(),  # type: ignore
2026-08-13 02:04:56             MemoryWriter(http2=True),  # type: ignore
2026-08-13 02:04:56         )
2026-08-13 02:04:56         task = event_loop.create_task(server.run())
2026-08-13 02:04:56         h2_client = h2.connection.H2Connection()
2026-08-13 02:04:56         h2_client.initiate_connection()
2026-08-13 02:04:56         await server.reader.send(h2_client.data_to_send())  # type: ignore
2026-08-13 02:04:56         stream_id = h2_client.get_next_available_stream_id()
2026-08-13 02:04:56 >       h2_client.send_headers(
2026-08-13 02:04:56             stream_id,
2026-08-13 02:04:56             [
2026-08-13 02:04:56                 (":method", "CONNECT"),
2026-08-13 02:04:56                 (":path", "/"),
2026-08-13 02:04:56                 (":authority", "hypercorn"),
2026-08-13 02:04:56                 (":scheme", "https"),
2026-08-13 02:04:56                 ("sec-websocket-version", "13"),
2026-08-13 02:04:56             ],
2026-08-13 02:04:56         )
2026-08-13 02:04:56 
2026-08-13 02:04:56 event_loop = <_UnixSelectorEventLoop running=False closed=False debug=False>
2026-08-13 02:04:56 h2_client  = <h2.connection.H2Connection object at 0x7ff63177c610>
2026-08-13 02:04:56 server     = <hypercorn.asyncio.tcp_server.TCPServer object at 0x7ff63177eb50>
2026-08-13 02:04:56 stream_id  = 1
2026-08-13 02:04:56 task       = <Task pending name='Task-93' coro=<TCPServer.run() running at /home/lkp/rpmbuild/BUILDROOT/python-hypercorn-0.18.0-1.o...usr/lib/python3.11/site-packages/hypercorn/asyncio/tcp_server.py:73> wait_for=<Future pending cb=[Task.task_wakeup()]>>
2026-08-13 02:04:56 
2026-08-13 02:04:56 tests/asyncio/test_sanity.py:202: 
2026-08-13 02:04:56 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/connection.py:809: in send_headers
2026-08-13 02:04:56     frames.extend(stream.send_headers(
2026-08-13 02:04:56         end_stream = False
2026-08-13 02:04:56         frames     = []
2026-08-13 02:04:56         headers    = [(':method', 'CONNECT'), (':path', '/'), (':authority', 'hypercorn'), (':scheme', 'https'), ('sec-websocket-version', '13')]
2026-08-13 02:04:56         max_open_streams = 4294967297
2026-08-13 02:04:56         priority_depends_on = None
2026-08-13 02:04:56         priority_exclusive = None
2026-08-13 02:04:56         priority_weight = None
2026-08-13 02:04:56         self       = <h2.connection.H2Connection object at 0x7ff63177c610>
2026-08-13 02:04:56         stream     = <H2Stream id:1 state:<StreamState.OPEN: 3>>
2026-08-13 02:04:56         stream_id  = 1
2026-08-13 02:04:56         value      = 0
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/stream.py:891: in send_headers
2026-08-13 02:04:56     frames = self._build_headers_frames(
2026-08-13 02:04:56         bytes_headers = [(b':method', b'CONNECT'), (b':path', b'/'), (b':authority', b'hypercorn'), (b':scheme', b'https'), (b'sec-websocket-version', b'13')]
2026-08-13 02:04:56         encoder    = <hpack.hpack.Encoder object at 0x7ff63177ce10>
2026-08-13 02:04:56         end_stream = False
2026-08-13 02:04:56         events     = [<h2.events._RequestSent object at 0x7ff6318b0790>]
2026-08-13 02:04:56         hdr_validation_flags = HeaderValidationFlags(is_client=True, is_trailer=False, is_response_header=False, is_push_promise=False)
2026-08-13 02:04:56         headers    = [(':method', 'CONNECT'), (':path', '/'), (':authority', 'hypercorn'), (':scheme', 'https'), ('sec-websocket-version', '13')]
2026-08-13 02:04:56         hf         = HeadersFrame(stream_id=1, flags=[]): exclusive=False, depends_on=0, stream_weight=0, data=None
2026-08-13 02:04:56         input_     = <StreamInputs.SEND_HEADERS: 0>
2026-08-13 02:04:56         self       = <H2Stream id:1 state:<StreamState.OPEN: 3>>
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/stream.py:1309: in _build_headers_frames
2026-08-13 02:04:56     encoded_headers = encoder.encode(headers)
2026-08-13 02:04:56                       ^^^^^^^^^^^^^^^^^^^^^^^
2026-08-13 02:04:56         encoder    = <hpack.hpack.Encoder object at 0x7ff63177ce10>
2026-08-13 02:04:56         first_frame = HeadersFrame(stream_id=1, flags=[]): exclusive=False, depends_on=0, stream_weight=0, data=None
2026-08-13 02:04:56         hdr_validation_flags = HeaderValidationFlags(is_client=True, is_trailer=False, is_response_header=False, is_push_promise=False)
2026-08-13 02:04:56         headers    = <generator object _check_path_header.<locals>.inner at 0x7ff63179ece0>
2026-08-13 02:04:56         self       = <H2Stream id:1 state:<StreamState.OPEN: 3>>
2026-08-13 02:04:56         should_split_outbound_cookies = False
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/hpack/hpack.py:280: in encode
2026-08-13 02:04:56     for header in hpack_headers:
2026-08-13 02:04:56         header     = (b'sec-websocket-version', b'13')
2026-08-13 02:04:56         header_block = [b'B\x87\xbd\xabN\x9c\x17\xb7\xff', b'\x84', b'A\x87\x9f\xd5e\xb0\x87\xb2\xaf', b'\x87', b'@\x8fAH\xb7\x82\xc6\x83\x93\xa9R\xb7r\xd8\x83\x1e\xaf\x82\x0b?']
2026-08-13 02:04:56         headers    = <generator object _check_path_header.<locals>.inner at 0x7ff63179ece0>
2026-08-13 02:04:56         hpack_headers = <generator object _check_path_header.<locals>.inner at 0x7ff63179ece0>
2026-08-13 02:04:56         huffman    = True
2026-08-13 02:04:56         new_header = (b'sec-websocket-version', b'13')
2026-08-13 02:04:56         self       = <hpack.hpack.Encoder object at 0x7ff63177ce10>
2026-08-13 02:04:56         sensitive  = False
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/utilities.py:506: in inner
2026-08-13 02:04:56     for header in headers:
2026-08-13 02:04:56         header     = (b'sec-websocket-version', b'13')
2026-08-13 02:04:56         headers    = <generator object _validate_host_authority_header at 0x7ff631784140>
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/utilities.py:448: in _validate_host_authority_header
2026-08-13 02:04:56     for header in headers:
2026-08-13 02:04:56         authority_header_val = b'hypercorn'
2026-08-13 02:04:56         header     = (b'sec-websocket-version', b'13')
2026-08-13 02:04:56         headers    = <generator object _reject_pseudo_header_fields at 0x7ff631899250>
2026-08-13 02:04:56         host_header_val = None
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/utilities.py:359: in _reject_pseudo_header_fields
2026-08-13 02:04:56     _check_pseudo_header_field_acceptability(
2026-08-13 02:04:56         hdr_validation_flags = HeaderValidationFlags(is_client=True, is_trailer=False, is_response_header=False, is_push_promise=False)
2026-08-13 02:04:56         header     = (b'sec-websocket-version', b'13')
2026-08-13 02:04:56         headers    = <generator object _reject_connection_header at 0x7ff6317c4220>
2026-08-13 02:04:56         method     = b'CONNECT'
2026-08-13 02:04:56         seen_pseudo_header_fields = {b':authority', b':method', b':path', b':scheme'}
2026-08-13 02:04:56         seen_regular_header = True
2026-08-13 02:04:56 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
2026-08-13 02:04:56 
2026-08-13 02:04:56 pseudo_headers = {b':authority', b':method', b':path', b':scheme'}
2026-08-13 02:04:56 method = b'CONNECT'
2026-08-13 02:04:56 hdr_validation_flags = HeaderValidationFlags(is_client=True, is_trailer=False, is_response_header=False, is_push_promise=False)
2026-08-13 02:04:56 
2026-08-13 02:04:56     def _check_pseudo_header_field_acceptability(  # noqa: C901
2026-08-13 02:04:56             pseudo_headers: set[bytes | str] | set[bytes] | set[str],
2026-08-13 02:04:56             method: bytes | None,
2026-08-13 02:04:56             hdr_validation_flags: HeaderValidationFlags,
2026-08-13 02:04:56     ) -> None:
2026-08-13 02:04:56         """
2026-08-13 02:04:56         Given the set of pseudo-headers present in a header block and the
2026-08-13 02:04:56         validation flags, confirms that RFC 7540 allows them.
2026-08-13 02:04:56         """
2026-08-13 02:04:56         # Pseudo-header fields MUST NOT appear in trailers - RFC 7540 § 8.1.2.1
2026-08-13 02:04:56         if hdr_validation_flags.is_trailer and pseudo_headers:
2026-08-13 02:04:56             msg = f"Received pseudo-header in trailer {pseudo_headers}"
2026-08-13 02:04:56             raise ProtocolError(msg)
2026-08-13 02:04:56     
2026-08-13 02:04:56         # If ':status' pseudo-header is not there in a response header, reject it.
2026-08-13 02:04:56         # Similarly, if ':path', ':method', or ':scheme' are not there in a request
2026-08-13 02:04:56         # header, reject it. Additionally, if a response contains any request-only
2026-08-13 02:04:56         # headers or vice-versa, reject it.
2026-08-13 02:04:56         # Relevant RFC section: RFC 7540 § 8.1.2.4
2026-08-13 02:04:56         # https://tools.ietf.org/html/rfc7540#section-8.1.2.4
2026-08-13 02:04:56         if hdr_validation_flags.is_response_header:
2026-08-13 02:04:56             _assert_header_in_set(b":status", pseudo_headers)
2026-08-13 02:04:56             invalid_response_headers = pseudo_headers & _REQUEST_ONLY_HEADERS
2026-08-13 02:04:56             if invalid_response_headers:
2026-08-13 02:04:56                 msg = f"Encountered request-only headers {invalid_response_headers}"
2026-08-13 02:04:56                 raise ProtocolError(msg)
2026-08-13 02:04:56         elif (not hdr_validation_flags.is_response_header and
2026-08-13 02:04:56               not hdr_validation_flags.is_trailer):
2026-08-13 02:04:56             # Request header block.
2026-08-13 02:04:56             _assert_header_in_set(b":method", pseudo_headers)
2026-08-13 02:04:56     
2026-08-13 02:04:56             is_connect = (method == b"CONNECT")
2026-08-13 02:04:56             is_extended_connect = is_connect and (b":protocol" in pseudo_headers)
2026-08-13 02:04:56     
2026-08-13 02:04:56             if is_connect and not is_extended_connect:
2026-08-13 02:04:56                 # Ordinary CONNECT (RFC 9113 s8.3):
2026-08-13 02:04:56                 # MUST NOT include :scheme or :path.
2026-08-13 02:04:56                 if b":scheme" in pseudo_headers or b":path" in pseudo_headers:
2026-08-13 02:04:56                     msg = "Ordinary CONNECT MUST NOT include :scheme or :path"
2026-08-13 02:04:56 >                   raise ProtocolError(msg)
2026-08-13 02:04:56 E                   h2.exceptions.ProtocolError: Ordinary CONNECT MUST NOT include :scheme or :path
2026-08-13 02:04:56 
2026-08-13 02:04:56 hdr_validation_flags = HeaderValidationFlags(is_client=True, is_trailer=False, is_response_header=False, is_push_promise=False)
2026-08-13 02:04:56 is_connect = True
2026-08-13 02:04:56 is_extended_connect = False
2026-08-13 02:04:56 method     = b'CONNECT'
2026-08-13 02:04:56 msg        = 'Ordinary CONNECT MUST NOT include :scheme or :path'
2026-08-13 02:04:56 pseudo_headers = {b':authority', b':method', b':path', b':scheme'}
2026-08-13 02:04:56 
2026-08-13 02:04:56 /usr/lib/python3.11/site-packages/h2/utilities.py:403: ProtocolError
2026-08-13 02:04:56 _____________________________ test_http2_websocket _____________________________
2026-08-13 02:04:56   + Exception Group Traceback (most recent call last):
2026-08-13 02:04:56   |   File "/usr/lib/python3.11/site-packages/trio/_core/_run.py", line 1125, in __aexit__
2026-08-13 02:04:56   |     raise combined_error_from_nursery
2026-08-13 02:04:56   | ExceptionGroup: Exceptions from Trio nursery (1 sub-exception)
2026-08-13 02:04:56   +-+---------------- 1 ----------------
2026-08-13 02:04:56     | Traceback (most recent call last):
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/pytest_trio/plugin.py", line 195, in _fixture_manager
2026-08-13 02:04:56     |     yield nursery_fixture
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/pytest_trio/plugin.py", line 250, in run
2026-08-13 02:04:56     |     await self._func(**resolved_kwargs)
2026-08-13 02:04:56     |   File "/home/lkp/rpmbuild/BUILD/hypercorn-0.18.0/tests/trio/test_sanity.py", line 174, in test_http2_websocket
2026-08-13 02:04:56     |     h2_client.send_headers(
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/connection.py", line 809, in send_headers
2026-08-13 02:04:56     |     frames.extend(stream.send_headers(
2026-08-13 02:04:56     |                   ^^^^^^^^^^^^^^^^^^^^
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/stream.py", line 891, in send_headers
2026-08-13 02:04:56     |     frames = self._build_headers_frames(
2026-08-13 02:04:56     |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/stream.py", line 1309, in _build_headers_frames
2026-08-13 02:04:56     |     encoded_headers = encoder.encode(headers)
2026-08-13 02:04:56     |                       ^^^^^^^^^^^^^^^^^^^^^^^
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/hpack/hpack.py", line 280, in encode
2026-08-13 02:04:56     |     for header in hpack_headers:
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/utilities.py", line 506, in inner
2026-08-13 02:04:56     |     for header in headers:
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/utilities.py", line 448, in _validate_host_authority_header
2026-08-13 02:04:56     |     for header in headers:
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/utilities.py", line 359, in _reject_pseudo_header_fields
2026-08-13 02:04:56     |     _check_pseudo_header_field_acceptability(
2026-08-13 02:04:56     |   File "/usr/lib/python3.11/site-packages/h2/utilities.py", line 403, in _check_pseudo_header_field_acceptability
2026-08-13 02:04:56     |     raise ProtocolError(msg)
2026-08-13 02:04:56     | h2.exceptions.ProtocolError: Ordinary CONNECT MUST NOT include :scheme or :path
2026-08-13 02:04:56     +------------------------------------
2026-08-13 02:04:56 =========================== short test summary info ============================
2026-08-13 02:04:56 FAILED tests/asyncio/test_sanity.py::test_http2_websocket - h2.exceptions.Pro...
2026-08-13 02:04:56 FAILED tests/trio/test_sanity.py::test_http2_websocket - ProtocolError('Ordin...
2026-08-13 02:04:56 ======================== 2 failed, 194 passed in 7.82s =========================

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions