gunicorn/tests/test_asgi_valid_requests.py
Benoit Chesneau e90b1c2c1e fix: address six WSGI/ASGI parser and protocol findings
- WSGI fast parser now applies the same per-header policy as the Python
  parser (Expect, secure_scheme_headers, forwarded_allow_ips trust gate,
  forwarder_headers / header_map). Shared helpers extracted on Message.

- ASGI keepalive no longer resets the parser when the previous request
  body was not fully framed; the connection closes instead, preventing
  request smuggling on pipelined connections.

- BodyReceiver._wait_for_data timeout flips _closed and yields
  http.disconnect rather than synthesizing more_body=False. Timeout
  honors cfg.timeout.

- ASGI chunked encoding now skips HEAD, 204, and 304 (matches
  Response.is_chunked in the WSGI path) via a small helper.

- _setup_callback_parser passes proxy_protocol to PythonProtocol; auto
  falls back to the Python parser when proxy_protocol != off (the C
  parser does not implement PROXY framing). _effective_peername swaps
  the transport peer with the PROXY-supplied client address.

- Parser.finish_body accepts a deadline and a 64KiB byte cap; gthread
  passes a deadline and abandons keepalive on incomplete drain so a
  stalled client cannot tie up a worker thread.
2026-05-03 18:19:08 +02:00

53 lines
1.7 KiB
Python

#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
"""Test valid HTTP requests against ASGI callback parser.
Runs the same .http test files as test_valid_requests.py but using
the ASGI PythonProtocol callback parser.
"""
import glob
import os
import pytest
import treq_asgi
dirname = os.path.dirname(__file__)
reqdir = os.path.join(dirname, "requests", "valid")
httpfiles = glob.glob(os.path.join(reqdir, "*.http"))
# Tests that require features not supported by callback parser:
# - 040.http, 040_compat.http: WSGI-specific underscore header handling
# - 099.http: Content-Length body with incomplete data in test file
SKIP_TESTS = {'040.http', '040_compat.http', '099.http'}
# Tests that use config options incompatible with callback parser
# (these are WSGI-specific behaviors)
INCOMPATIBLE_BOOL_FLAGS = ('permit_obsolete_folding', 'strip_header_spaces', 'casefold_http_method')
@pytest.mark.parametrize("fname", httpfiles)
def test_asgi_parser(fname):
"""Test valid HTTP requests with ASGI callback parser."""
basename = os.path.basename(fname)
if basename in SKIP_TESTS:
pytest.skip(f"Test {basename} not supported by callback parser")
env = treq_asgi.load_py(os.path.splitext(fname)[0] + ".py")
expect = env['request']
cfg = env['cfg']
# Skip tests that use incompatible config flags
for flag in INCOMPATIBLE_BOOL_FLAGS:
if getattr(cfg, flag, False):
pytest.skip(f"Callback parser incompatible with {flag}")
req = treq_asgi.request(fname, expect)
# Test with different sending strategies
for sender in [req.send_all, req.send_lines, req.send_random]:
req.check(cfg, sender)