Skip to content

Commit 34d5ffc

Browse files
committed
httputil: Add test for 400 vs 405 method validation
1 parent 88344ae commit 34d5ffc

File tree

1 file changed

+33
-1
lines changed

1 file changed

+33
-1
lines changed

tornado/test/httpserver_test.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from tornado.test.util import abstract_base_test
3131
from tornado.web import Application, RequestHandler, stream_request_body
3232

33-
from contextlib import closing
33+
from contextlib import closing, contextmanager
3434
import datetime
3535
import gzip
3636
import logging
@@ -634,6 +634,38 @@ def test_invalid_content_length(self):
634634
)
635635
yield stream.read_until_close()
636636

637+
@gen_test
638+
def test_invalid_methods(self):
639+
# RFC 9110 distinguishes between syntactically invalid methods and those that are
640+
# valid but unknown. The former must give a 400 status code, while the latter should
641+
# give a 405.
642+
test_cases = [
643+
("FOO", 405, None),
644+
("FOO,BAR", 400, ".*Malformed HTTP request line"),
645+
]
646+
for method, code, log_msg in test_cases:
647+
if log_msg is not None:
648+
expect_log = ExpectLog(gen_log, log_msg, level=logging.INFO)
649+
else:
650+
651+
@contextmanager
652+
def noop_context():
653+
yield
654+
655+
expect_log = noop_context() # type: ignore
656+
with (
657+
self.subTest(method=method),
658+
closing(IOStream(socket.socket())) as stream,
659+
expect_log,
660+
):
661+
yield stream.connect(("127.0.0.1", self.get_http_port()))
662+
stream.write(utf8(f"{method} /echo HTTP/1.1\r\n\r\n"))
663+
resp = yield stream.read_until(b"\r\n\r\n")
664+
self.assertTrue(
665+
resp.startswith(b"HTTP/1.1 %d" % code),
666+
f"expected status code {code} in {resp!r}",
667+
)
668+
637669

638670
class XHeaderTest(HandlerBaseTestCase):
639671
class Handler(RequestHandler):

0 commit comments

Comments
 (0)