From 076aa31c6fb088ca56fbae23b829eb099b6a039e Mon Sep 17 00:00:00 2001 From: Mark Breedlove Date: Fri, 28 Sep 2018 11:23:56 -0400 Subject: [PATCH 1/2] Send HTTP 400 response for invalid request Given an invalid request, respond with an HTTP 400 error instead of closing the connection without a response. --- tests/protocols/test_http.py | 4 +- uvicorn/protocols/http/h11_impl.py | 53 +++++++++++++----------- uvicorn/protocols/http/httptools_impl.py | 48 +++++++++++---------- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/tests/protocols/test_http.py b/tests/protocols/test_http.py index b784c5e9b..6561867cd 100644 --- a/tests/protocols/test_http.py +++ b/tests/protocols/test_http.py @@ -742,5 +742,5 @@ def test_invalid_http_request(request_line, protocol_cls, caplog, event_loop): with get_connected_protocol(app, protocol_cls, event_loop) as protocol: protocol.data_received(request) - assert not protocol.transport.buffer - assert "Invalid HTTP request received." in caplog.messages + assert b"HTTP/1.1 400 Bad Request" in protocol.transport.buffer + assert b"Invalid HTTP request received." in protocol.transport.buffer diff --git a/uvicorn/protocols/http/h11_impl.py b/uvicorn/protocols/http/h11_impl.py index b4f4e1ac9..c0e91ef65 100644 --- a/uvicorn/protocols/http/h11_impl.py +++ b/uvicorn/protocols/http/h11_impl.py @@ -135,9 +135,9 @@ def handle_events(self): try: event = self.conn.next_event() except h11.RemoteProtocolError as exc: - msg = "Invalid HTTP request received." + msg = b"Invalid HTTP request received." self.logger.warning(msg, exc_info=exc) - self.transport.close() + self.send_400_response(msg) return event_type = type(event) @@ -234,30 +234,9 @@ def handle_upgrade(self, event): upgrade_value = value.lower() if upgrade_value != b"websocket" or self.ws_protocol_class is None: - msg = "Unsupported upgrade request." + msg = b"Unsupported upgrade request." self.logger.warning(msg) - - from uvicorn.protocols.websockets.auto import AutoWebSocketsProtocol - - if AutoWebSocketsProtocol is None: - msg = "No supported WebSocket library detected. Please use 'pip install uvicorn[standard]', or install 'websockets' or 'wsproto' manually." # noqa: E501 - self.logger.warning(msg) - - reason = STATUS_PHRASES[400] - headers = [ - (b"content-type", b"text/plain; charset=utf-8"), - (b"connection", b"close"), - ] - event = h11.Response(status_code=400, headers=headers, reason=reason) - output = self.conn.send(event) - self.transport.write(output) - event = h11.Data(data=b"Unsupported upgrade request.") - output = self.conn.send(event) - self.transport.write(output) - event = h11.EndOfMessage() - output = self.conn.send(event) - self.transport.write(output) - self.transport.close() + self.send_400_response(msg) return if self.logger.level <= TRACE_LOG_LEVEL: @@ -278,6 +257,30 @@ def handle_upgrade(self, event): protocol.data_received(b"".join(output)) self.transport.set_protocol(protocol) + def send_400_response(self, msg: bytes): + + from uvicorn.protocols.websockets.auto import AutoWebSocketsProtocol + + if AutoWebSocketsProtocol is None: + msg = "No supported WebSocket library detected. Please use 'pip install uvicorn[standard]', or install 'websockets' or 'wsproto' manually." # noqa: E501 + self.logger.warning(msg) + + reason = STATUS_PHRASES[400] + headers = [ + (b"content-type", b"text/plain; charset=utf-8"), + (b"connection", b"close"), + ] + event = h11.Response(status_code=400, headers=headers, reason=reason) + output = self.conn.send(event) + self.transport.write(output) + event = h11.Data(data=msg) + output = self.conn.send(event) + self.transport.write(output) + event = h11.EndOfMessage() + output = self.conn.send(event) + self.transport.write(output) + self.transport.close() + def on_response_complete(self): self.server_state.total_requests += 1 diff --git a/uvicorn/protocols/http/httptools_impl.py b/uvicorn/protocols/http/httptools_impl.py index bac061771..7bbd352e7 100644 --- a/uvicorn/protocols/http/httptools_impl.py +++ b/uvicorn/protocols/http/httptools_impl.py @@ -133,7 +133,8 @@ def data_received(self, data): except httptools.HttpParserError as exc: msg = "Invalid HTTP request received." self.logger.warning(msg, exc_info=exc) - self.transport.close() + self.send_400_response(msg) + return except httptools.HttpParserUpgrade: self.handle_upgrade() if data == b"" and not self.transport.is_closing(): @@ -148,27 +149,7 @@ def handle_upgrade(self): if upgrade_value != b"websocket" or self.ws_protocol_class is None: msg = "Unsupported upgrade request." self.logger.warning(msg) - - from uvicorn.protocols.websockets.auto import AutoWebSocketsProtocol - - if AutoWebSocketsProtocol is None: - msg = "No supported WebSocket library detected. Please use 'pip install uvicorn[standard]', or install 'websockets' or 'wsproto' manually." # noqa: E501 - self.logger.warning(msg) - - content = [STATUS_LINE[400]] - for name, value in self.default_headers: - content.extend([name, b": ", value, b"\r\n"]) - content.extend( - [ - b"content-type: text/plain; charset=utf-8\r\n", - b"content-length: " + str(len(msg)).encode("ascii") + b"\r\n", - b"connection: close\r\n", - b"\r\n", - msg.encode("ascii"), - ] - ) - self.transport.write(b"".join(content)) - self.transport.close() + self.send_400_response(msg) return if self.logger.level <= TRACE_LOG_LEVEL: @@ -190,6 +171,29 @@ def handle_upgrade(self): protocol.data_received(b"".join(output)) self.transport.set_protocol(protocol) + def send_400_response(self, msg: str): + + from uvicorn.protocols.websockets.auto import AutoWebSocketsProtocol + + if AutoWebSocketsProtocol is None: + msg = "No supported WebSocket library detected. Please use 'pip install uvicorn[standard]', or install 'websockets' or 'wsproto' manually." # noqa: E501 + self.logger.warning(msg) + + content = [STATUS_LINE[400]] + for name, value in self.default_headers: + content.extend([name, b": ", value, b"\r\n"]) + content.extend( + [ + b"content-type: text/plain; charset=utf-8\r\n", + b"content-length: " + str(len(msg)).encode("ascii") + b"\r\n", + b"connection: close\r\n", + b"\r\n", + msg.encode("ascii"), + ] + ) + self.transport.write(b"".join(content)) + self.transport.close() + # Parser callbacks def on_url(self, url): method = self.parser.get_method() From eea6d1a649bb1ffe0da4fdae82df8565de2c471c Mon Sep 17 00:00:00 2001 From: Reinhold Bertram Date: Thu, 10 Feb 2022 22:57:27 +0100 Subject: [PATCH 2/2] changed signature of send_400_response to msg as str --- uvicorn/protocols/http/h11_impl.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uvicorn/protocols/http/h11_impl.py b/uvicorn/protocols/http/h11_impl.py index c0e91ef65..ba114f107 100644 --- a/uvicorn/protocols/http/h11_impl.py +++ b/uvicorn/protocols/http/h11_impl.py @@ -135,7 +135,7 @@ def handle_events(self): try: event = self.conn.next_event() except h11.RemoteProtocolError as exc: - msg = b"Invalid HTTP request received." + msg = "Invalid HTTP request received." self.logger.warning(msg, exc_info=exc) self.send_400_response(msg) return @@ -234,7 +234,7 @@ def handle_upgrade(self, event): upgrade_value = value.lower() if upgrade_value != b"websocket" or self.ws_protocol_class is None: - msg = b"Unsupported upgrade request." + msg = "Unsupported upgrade request." self.logger.warning(msg) self.send_400_response(msg) return @@ -257,7 +257,7 @@ def handle_upgrade(self, event): protocol.data_received(b"".join(output)) self.transport.set_protocol(protocol) - def send_400_response(self, msg: bytes): + def send_400_response(self, msg: str): from uvicorn.protocols.websockets.auto import AutoWebSocketsProtocol @@ -273,7 +273,7 @@ def send_400_response(self, msg: bytes): event = h11.Response(status_code=400, headers=headers, reason=reason) output = self.conn.send(event) self.transport.write(output) - event = h11.Data(data=msg) + event = h11.Data(data=msg.encode("ascii")) output = self.conn.send(event) self.transport.write(output) event = h11.EndOfMessage()