diff --git a/cheroot/server.py b/cheroot/server.py index 284cf17c72..4fac3de97f 100644 --- a/cheroot/server.py +++ b/cheroot/server.py @@ -162,6 +162,7 @@ FORWARD_SLASH = b'/' QUOTED_SLASH = b'%2F' QUOTED_SLASH_REGEX = re.compile(b''.join((b'(?i)', QUOTED_SLASH))) +HTTP_TOKEN_RE = re.compile(rb'^[!#$%&\'*+\-.^_`|~0-9A-Za-z]+$') _STOPPING_FOR_INTERRUPT = Exception() # sentinel used during shutdown @@ -826,6 +827,10 @@ def read_request_line(self): # noqa: C901 # FIXME self.simple_response('400 Bad Request', 'Malformed Request-Line') return False + if not HTTP_TOKEN_RE.match(method): + self.simple_response('400 Bad Request', 'Malformed method name') + return False + self.uri = uri self.method = method.upper() diff --git a/cheroot/test/test_core.py b/cheroot/test/test_core.py index cd3841d428..aa7ffe2c3a 100644 --- a/cheroot/test/test_core.py +++ b/cheroot/test/test_core.py @@ -420,6 +420,17 @@ def test_garbage_in(test_client): raise +def test_invalid_character_in_http_method(test_client): + """Check that methods with invalid token characters are rejected.""" + c = test_client.get_connection() + c._output(b'GE(T / HTTP/1.1\r\nHost: localhost\r\n\r\n') + c._send_output() + response = _get_http_response(c, method='GET') + response.begin() + assert response.status == HTTP_BAD_REQUEST + c.close() + + class CloseController: """Controller for testing the close callback.""" diff --git a/docs/changelog-fragments.d/833.bugfix.rst b/docs/changelog-fragments.d/833.bugfix.rst new file mode 100644 index 0000000000..a99169ce0a --- /dev/null +++ b/docs/changelog-fragments.d/833.bugfix.rst @@ -0,0 +1 @@ +Rejected HTTP methods containing characters that are invalid in HTTP tokens.