gunicorn/tests/test_invalid_requests.py
Benoit Chesneau 82d33d4c71 fix: reject asterisk-form request-target outside OPTIONS (RFC 9112 section 3.2.4)
The Python WSGI and ASGI parsers both accepted `GET *` and similar; RFC
9112 restricts asterisk-form to OPTIONS. Both now raise InvalidRequestLine.

The fast (C) parser in gunicorn_h1c does not yet enforce this, so the
fixture is marked python_only via a new sidecar flag honored by the WSGI
and ASGI invalid-request harnesses.
2026-04-19 10:43:01 +02:00

69 lines
2.3 KiB
Python

#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
import glob
import os
import pytest
from gunicorn.http.errors import (
InvalidRequestLine,
InvalidRequestMethod,
InvalidSchemeHeaders,
ObsoleteFolding,
)
import treq
dirname = os.path.dirname(__file__)
reqdir = os.path.join(dirname, "requests", "invalid")
httpfiles = glob.glob(os.path.join(reqdir, "*.http"))
# Flags incompatible with fast parser (require Python parser features)
_FAST_INCOMPATIBLE_FLAGS = ('permit_obsolete_folding', 'strip_header_spaces')
# Exceptions that only the Python parser raises (C parser has different validation)
_PYTHON_ONLY_EXCEPTIONS = (ObsoleteFolding, InvalidSchemeHeaders)
# C parser may raise different but valid exceptions for these cases
_FAST_PARSER_ALTERNATES = {
InvalidRequestMethod: (InvalidRequestLine,), # e.g. "GET:" raises InvalidRequestLine
}
@pytest.mark.parametrize("fname", httpfiles)
def test_http_parser(fname, http_parser):
"""Test invalid HTTP requests with both parser implementations."""
env = treq.load_py(os.path.splitext(fname)[0] + ".py", http_parser=http_parser)
expect = env["request"]
cfg = env["cfg"]
# Skip fast parser tests that use incompatible compatibility flags
if http_parser == 'fast':
for flag in _FAST_INCOMPATIBLE_FLAGS:
if getattr(cfg, flag, False):
pytest.skip(f"fast parser incompatible with {flag}")
# Skip tests expecting Python-only exceptions
if expect in _PYTHON_ONLY_EXCEPTIONS or (
isinstance(expect, type) and issubclass(expect, _PYTHON_ONLY_EXCEPTIONS)
):
pytest.skip(f"fast parser does not raise {expect.__name__}")
# Fixture-level opt-out for validations not (yet) implemented by
# the C parser. The sidecar sets `python_only = True`.
if env.get('python_only'):
pytest.skip("fixture marked python_only")
# Determine acceptable exceptions (fast parser may raise alternates)
if http_parser == 'fast' and expect in _FAST_PARSER_ALTERNATES:
acceptable = (expect,) + _FAST_PARSER_ALTERNATES[expect]
else:
acceptable = expect
req = treq.badrequest(fname)
with pytest.raises(acceptable):
req.check(cfg)