|
| 1 | +"""unit tests for ordinary vs extended CONNECT validation on the client side.""" |
| 2 | + |
| 3 | +from __future__ import annotations |
| 4 | + |
| 5 | +import pytest |
| 6 | + |
| 7 | +from h2.config import H2Configuration |
| 8 | +from h2.connection import H2Connection |
| 9 | +from h2.utilities import HeaderValidationFlags, validate_outbound_headers |
| 10 | + |
| 11 | + |
| 12 | +def _new_conn() -> H2Connection: |
| 13 | + c = H2Connection( |
| 14 | + config=H2Configuration(client_side=True, header_encoding="utf-8") |
| 15 | + ) |
| 16 | + c.initiate_connection() |
| 17 | + # settings ack frame: length=0, type=4, flags=1(ACK), stream=0 |
| 18 | + c.receive_data(b"\x00\x00\x00\x04\x01\x00\x00\x00\x00") |
| 19 | + return c |
| 20 | + |
| 21 | + |
| 22 | +def _client_req_flags() -> HeaderValidationFlags: |
| 23 | + # client, not trailers, not response, not push |
| 24 | + return HeaderValidationFlags( |
| 25 | + is_client=True, |
| 26 | + is_trailer=False, |
| 27 | + is_response_header=False, |
| 28 | + is_push_promise=False, |
| 29 | + ) |
| 30 | + |
| 31 | + |
| 32 | +def test_ordinary_connect_allows_no_scheme_no_path_and_send_headers_ok() -> None: |
| 33 | + # ---- bytes for validate_outbound_headers ---- |
| 34 | + hdrs_bytes = [ |
| 35 | + (b":method", b"CONNECT"), |
| 36 | + (b":authority", b"example.com:443"), |
| 37 | + ] |
| 38 | + # should not raise |
| 39 | + list(validate_outbound_headers(hdrs_bytes, _client_req_flags())) |
| 40 | + |
| 41 | + # ---- str is fine for send_headers due to header_encoding ---- |
| 42 | + hdrs_str = [ |
| 43 | + (":method", "CONNECT"), |
| 44 | + (":authority", "example.com:443"), |
| 45 | + ] |
| 46 | + conn = _new_conn() |
| 47 | + # should not raise |
| 48 | + conn.send_headers(1, hdrs_str, end_stream=True) |
| 49 | + |
| 50 | + |
| 51 | +def test_ordinary_connect_rejects_path_or_scheme() -> None: |
| 52 | + bad1 = [ |
| 53 | + (b":method", b"CONNECT"), |
| 54 | + (b":authority", b"example.com:443"), |
| 55 | + (b":path", b"/"), |
| 56 | + ] |
| 57 | + bad2 = [ |
| 58 | + (b":method", b"CONNECT"), |
| 59 | + (b":authority", b"example.com:443"), |
| 60 | + (b":scheme", b"https"), |
| 61 | + ] |
| 62 | + with pytest.raises(Exception): |
| 63 | + list(validate_outbound_headers(bad1, _client_req_flags())) |
| 64 | + with pytest.raises(Exception): |
| 65 | + list(validate_outbound_headers(bad2, _client_req_flags())) |
| 66 | + |
| 67 | + |
| 68 | +def test_extended_connect_requires_regular_tuple_and_send_headers_ok() -> None: |
| 69 | + hdrs_bytes = [ |
| 70 | + (b":method", b"CONNECT"), |
| 71 | + (b":protocol", b"websocket"), |
| 72 | + (b":scheme", b"https"), |
| 73 | + (b":path", b"/chat?room=1"), |
| 74 | + (b":authority", b"ws.example.com"), |
| 75 | + ] |
| 76 | + # should not raise |
| 77 | + list(validate_outbound_headers(hdrs_bytes, _client_req_flags())) |
| 78 | + |
| 79 | + hdrs_str = [ |
| 80 | + (":method", "CONNECT"), |
| 81 | + (":protocol", "websocket"), |
| 82 | + (":scheme", "https"), |
| 83 | + (":path", "/chat?room=1"), |
| 84 | + (":authority", "ws.example.com"), |
| 85 | + ] |
| 86 | + conn = _new_conn() |
| 87 | + # should not raise |
| 88 | + conn.send_headers(3, hdrs_str, end_stream=True) |
| 89 | + |
| 90 | + |
| 91 | +def test_non_connect_still_requires_scheme_and_path() -> None: |
| 92 | + hdrs_bytes = [ |
| 93 | + (b":method", b"GET"), |
| 94 | + (b":authority", b"example.com"), |
| 95 | + # omit :scheme and :path -> should raise |
| 96 | + ] |
| 97 | + with pytest.raises(Exception): |
| 98 | + list(validate_outbound_headers(hdrs_bytes, _client_req_flags())) |
| 99 | + |
0 commit comments