diff --git a/tests/requests/invalid/rfc9112_smuggle_cl_te_chunked_01.http b/tests/requests/invalid/rfc9112_smuggle_cl_te_chunked_01.http new file mode 100644 index 00000000..fec66ac1 --- /dev/null +++ b/tests/requests/invalid/rfc9112_smuggle_cl_te_chunked_01.http @@ -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 diff --git a/tests/requests/invalid/rfc9112_smuggle_cl_te_chunked_01.py b/tests/requests/invalid/rfc9112_smuggle_cl_te_chunked_01.py new file mode 100644 index 00000000..60f408ff --- /dev/null +++ b/tests/requests/invalid/rfc9112_smuggle_cl_te_chunked_01.py @@ -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 diff --git a/tests/requests/valid/rfc9112_smuggle_gzip_chunked_01.http b/tests/requests/valid/rfc9112_smuggle_gzip_chunked_01.http new file mode 100644 index 00000000..10c359e9 --- /dev/null +++ b/tests/requests/valid/rfc9112_smuggle_gzip_chunked_01.http @@ -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 diff --git a/tests/requests/valid/rfc9112_smuggle_gzip_chunked_01.py b/tests/requests/valid/rfc9112_smuggle_gzip_chunked_01.py new file mode 100644 index 00000000..f3b91981 --- /dev/null +++ b/tests/requests/valid/rfc9112_smuggle_gzip_chunked_01.py @@ -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", +} diff --git a/tests/requests/valid/rfc9112_smuggle_te_identity_chunked_01.http b/tests/requests/valid/rfc9112_smuggle_te_identity_chunked_01.http new file mode 100644 index 00000000..250800aa --- /dev/null +++ b/tests/requests/valid/rfc9112_smuggle_te_identity_chunked_01.http @@ -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 diff --git a/tests/requests/valid/rfc9112_smuggle_te_identity_chunked_01.py b/tests/requests/valid/rfc9112_smuggle_te_identity_chunked_01.py new file mode 100644 index 00000000..e6030007 --- /dev/null +++ b/tests/requests/valid/rfc9112_smuggle_te_identity_chunked_01.py @@ -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", +} diff --git a/tests/requests/valid/rfc9112_target_absolute_01.http b/tests/requests/valid/rfc9112_target_absolute_01.http new file mode 100644 index 00000000..2bc68bd6 --- /dev/null +++ b/tests/requests/valid/rfc9112_target_absolute_01.http @@ -0,0 +1,3 @@ +GET http://example.com/foo?q=1 HTTP/1.1\r\n +Host: example.com\r\n +\r\n diff --git a/tests/requests/valid/rfc9112_target_absolute_01.py b/tests/requests/valid/rfc9112_target_absolute_01.py new file mode 100644 index 00000000..fa482c9d --- /dev/null +++ b/tests/requests/valid/rfc9112_target_absolute_01.py @@ -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"", +} diff --git a/tests/requests/valid/rfc9112_target_asterisk_options_01.http b/tests/requests/valid/rfc9112_target_asterisk_options_01.http new file mode 100644 index 00000000..1105704d --- /dev/null +++ b/tests/requests/valid/rfc9112_target_asterisk_options_01.http @@ -0,0 +1,3 @@ +OPTIONS * HTTP/1.1\r\n +Host: example.com\r\n +\r\n diff --git a/tests/requests/valid/rfc9112_target_asterisk_options_01.py b/tests/requests/valid/rfc9112_target_asterisk_options_01.py new file mode 100644 index 00000000..92e90c3f --- /dev/null +++ b/tests/requests/valid/rfc9112_target_asterisk_options_01.py @@ -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"", +} diff --git a/tests/requests/valid/rfc9112_target_authority_connect_01.http b/tests/requests/valid/rfc9112_target_authority_connect_01.http new file mode 100644 index 00000000..29d805f6 --- /dev/null +++ b/tests/requests/valid/rfc9112_target_authority_connect_01.http @@ -0,0 +1,3 @@ +CONNECT example.com:443 HTTP/1.1\r\n +Host: example.com:443\r\n +\r\n diff --git a/tests/requests/valid/rfc9112_target_authority_connect_01.py b/tests/requests/valid/rfc9112_target_authority_connect_01.py new file mode 100644 index 00000000..e027bfcb --- /dev/null +++ b/tests/requests/valid/rfc9112_target_authority_connect_01.py @@ -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"", +}