-
Notifications
You must be signed in to change notification settings - Fork 168
Description
Summary
h2 Enforces :path as mandatory for an ordinary HTTP/2 CONNECT request. Per RFC 9113 (HTTP/2) s8.3, CONNECT requests MUST include :method = "CONNECT" and :authority, and MUST NOT include :scheme and :path. In my test, h2 raises ProtocolError: Header block missing mandatory b':path' header when I send an ordinary CONNECT without :path.
Baseline GET is accepted (as expected), and extended CONNECT for WebSocket (RFC 8441 s4) is also accepted (as expected). Only the ordinary CONNECT case seems overly strict.
Environment
- h2: 4.3.0
- Python: 3.11.x
- OS: Linux (local dev)
- Install method:
pip3 install h2in a fresh venv
Minimal Reproducer
# repro_h2_headers.py
from h2.config import H2Configuration
from h2.connection import H2Connection
def send(stream_id, headers):
conn = H2Connection(config=H2Configuration(client_side=True, header_encoding="utf-8"))
conn.initiate_connection()
# mock a SETTINGS ack so we can try sending headers without a real socket:
try:
conn.data_to_send()
# Empty SETTINGS ack frame: length=0, type=4, flags=1(ACK), stream=0
conn.receive_data(b"\x00\x00\x00\x04\x01\x00\x00\x00\x00")
except Exception:
pass
try:
conn.send_headers(stream_id=stream_id, headers=headers, end_stream=True)
print("OK: headers accepted")
except Exception as e:
print("ERROR:", type(e).__name__, str(e))
# A) Ordinary GET (baseline)
send(1, [
(":method", "GET"),
(":scheme", "https"),
(":authority", "example.com"),
(":path", "/"),
])
# B) Ordinary CONNECT (RFC 9113 s8.3): only :method=CONNECT and :authority; no :scheme/:path
send(3, [
(":method", "CONNECT"),
(":authority", "example.com:443"),
])
# C) Extended CONNECT for WebSocket (RFC 8441 s4): :method=CONNECT, :protocol=websocket, plus :scheme/:path/:authority
send(5, [
(":method", "CONNECT"),
(":protocol", "websocket"),
(":scheme", "https"),
(":path", "/chat?room=1"),
(":authority", "ws.example.com"),
])Actual results
OK: headers accepted
ERROR: ProtocolError Header block missing mandatory b':path' header
OK: headers accepted
- Baseline
GETis accepted - Ordinary
CONNECT without :pathis REJECTED: (ProtocolError … missing mandatory ':path') - Extended
CONNECT (WebSocket) with :scheme/:path/:authority: accepted
Expected results
- Ordinary CONNECT (RFC 9113 s8.3) - The request header block MUST include
:method = CONNECTand:authority, and MUST NOT include:schemeor:path. A header block that omits:pathfor ordinary CONNECT should be accepted. - Extended CONNECT / WebSocket (RFC 8441 s4) - The request MUST
include :method= CONNECT,:protocol = "websocket", and the regular pseudo-header tuple:scheme,:path,:authority. The library already accepts this (good).
Given the above, it looks like h2 is applying a generic ":path is mandatory" rule even to ordinary CONNECT, which conflicts with RFC 9113 s8.3.
Why this matters
Implementations that speak HTTP/2 CONNECT (f.e., proxies, gateways) rely on the ordinary CONNECT shape to establish opaque TCP tunnels. If client libraries forbid the spec-compliant header set (no :path), they can’t originate legal CONNECT exchanges without workarounds.
Suggested paths forward
- Relax header validation for ordinary CONNECT so that
:path/:schemeare not required (and ideally rejected if present). - Keep stricter validation for extended CONNECT (where :
scheme/:pathare required alongside:protocol). - Optionally, improve the exception message to mention which CONNECT mode was detected and which pseudo-header was unexpected/missing.