Fix limit handling and add default max limit tests

- LimitRequestLine now accepts optional max_size parameter
- Use default max limits when limit_request_line or limit_request_field_size is 0
- Add tests validating default max enforcement (8190 bytes)
- Handle alternate exceptions from fast parser in test_invalid_requests
This commit is contained in:
Benoit Chesneau 2026-03-22 16:17:55 +01:00
parent 2bf2632944
commit 4ce6aa1f3e
9 changed files with 62 additions and 9 deletions

View File

@ -114,11 +114,13 @@ class ChunkMissingTerminator(IOError):
class LimitRequestLine(ParseException):
def __init__(self, size, max_size):
def __init__(self, size, max_size=None):
self.size = size
self.max_size = max_size
def __str__(self):
if self.max_size is None:
return str(self.size)
return "Request Line is too large (%s > %s)" % (self.size, self.max_size)

View File

@ -173,7 +173,7 @@ class Message:
or self.limit_request_fields > MAX_HEADERS):
self.limit_request_fields = MAX_HEADERS
self.limit_request_field_size = cfg.limit_request_field_size
if self.limit_request_field_size < 0:
if self.limit_request_field_size <= 0:
self.limit_request_field_size = DEFAULT_MAX_HEADERFIELD_SIZE
# set max header buffer size
@ -389,7 +389,7 @@ class Request(Message):
# get max request line size
self.limit_request_line = cfg.limit_request_line
if (self.limit_request_line < 0
if (self.limit_request_line <= 0
or self.limit_request_line >= MAX_REQUEST_LINE):
self.limit_request_line = MAX_REQUEST_LINE
@ -432,7 +432,7 @@ class Request(Message):
while True:
try:
# Pass all limit parameters (guaranteed >= 0.4.1)
# Pass all limit parameters to C parser
result = _fast_parser_module.parse_request(
data,
last_len=last_len,

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
from gunicorn.config import Config
from gunicorn.http.errors import LimitRequestHeaders
cfg = Config()
# Setting limit_request_field_size=0 should use default max (8190)
cfg.set('limit_request_field_size', 0)
request = LimitRequestHeaders

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,11 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
from gunicorn.config import Config
from gunicorn.http.errors import LimitRequestLine
cfg = Config()
# Setting limit_request_line=0 should use default max (8190)
cfg.set('limit_request_line', 0)
request = LimitRequestLine

View File

@ -5,8 +5,9 @@
from gunicorn.config import Config
cfg = Config()
cfg.set('limit_request_line', 0)
cfg.set('limit_request_field_size', 0)
# Request line is 8194 bytes, header line is 8209 bytes (both include CRLF)
cfg.set('limit_request_line', 8200)
cfg.set('limit_request_field_size', 8210)
request = {
"method": "PUT",
"uri":

View File

@ -5,7 +5,7 @@
from gunicorn.config import Config
cfg = Config()
cfg.set('limit_request_line', 0)
# Header line is 8209 bytes (name + ": " + value + CRLF)
cfg.set('limit_request_field_size', 8210)
request = {
"method": "GET",

View File

@ -7,15 +7,29 @@ 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
# 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):
@ -31,7 +45,19 @@ def test_http_parser(fname, http_parser):
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__}")
# 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(expect):
with pytest.raises(acceptable):
req.check(cfg)