Merge pull request #3591 from benoitc/test/rfc9112-compliance-corpus-phase1

test: codify RFC 9112 request-target and TE/CL vectors (phase 1)
This commit is contained in:
Benoit Chesneau 2026-04-19 10:12:33 +02:00 committed by GitHub
commit 369b8d7d2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 115 additions and 0 deletions

View File

@ -0,0 +1,7 @@
POST /p HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 3\r\n
Transfer-Encoding: chunked\r\n
\r\n
0\r\n
\r\n

View File

@ -0,0 +1,9 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 6.1: a message with both Content-Length and
# Transfer-Encoding: chunked is the classic CL.TE desync vector and MUST
# be rejected by the origin server. PortSwigger HTTP Desync corpus, CL.TE.
from gunicorn.http.errors import InvalidHeader
request = InvalidHeader

View File

@ -0,0 +1,8 @@
POST /upload HTTP/1.1\r\n
Host: example.com\r\n
Transfer-Encoding: gzip, chunked\r\n
\r\n
5\r\n
hello\r\n
0\r\n
\r\n

View File

@ -0,0 +1,16 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 6.1: Transfer-Encoding codings stack left-to-right;
# chunked must be the final coding. gzip before chunked is valid.
request = {
"method": "POST",
"uri": uri("/upload"),
"version": (1, 1),
"headers": [
("HOST", "example.com"),
("TRANSFER-ENCODING", "gzip, chunked"),
],
"body": b"hello",
}

View File

@ -0,0 +1,8 @@
POST /upload HTTP/1.1\r\n
Host: example.com\r\n
Transfer-Encoding: identity, chunked\r\n
\r\n
5\r\n
hello\r\n
0\r\n
\r\n

View File

@ -0,0 +1,16 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 6.1: identity is a no-op coding and may precede chunked.
# Worth codifying because proxies have historically disagreed on this form.
request = {
"method": "POST",
"uri": uri("/upload"),
"version": (1, 1),
"headers": [
("HOST", "example.com"),
("TRANSFER-ENCODING", "identity, chunked"),
],
"body": b"hello",
}

View File

@ -0,0 +1,3 @@
GET http://example.com/foo?q=1 HTTP/1.1\r\n
Host: example.com\r\n
\r\n

View File

@ -0,0 +1,14 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 3.2.2: absolute-form request-target.
request = {
"method": "GET",
"uri": uri("http://example.com/foo?q=1"),
"version": (1, 1),
"headers": [
("HOST", "example.com"),
],
"body": b"",
}

View File

@ -0,0 +1,3 @@
OPTIONS * HTTP/1.1\r\n
Host: example.com\r\n
\r\n

View File

@ -0,0 +1,14 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 3.2.4: asterisk-form, only valid with OPTIONS.
request = {
"method": "OPTIONS",
"uri": uri("*"),
"version": (1, 1),
"headers": [
("HOST", "example.com"),
],
"body": b"",
}

View File

@ -0,0 +1,3 @@
CONNECT example.com:443 HTTP/1.1\r\n
Host: example.com:443\r\n
\r\n

View File

@ -0,0 +1,14 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 3.2.3: authority-form, only valid with CONNECT.
request = {
"method": "CONNECT",
"uri": uri("example.com:443"),
"version": (1, 1),
"headers": [
("HOST", "example.com:443"),
],
"body": b"",
}