From c631c443b9522a3197e3be4eb8884dc26794d3f3 Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Sat, 1 Feb 2025 13:52:37 -0600 Subject: [PATCH 1/4] fix reader not skipping over all CRLF --- lightbug_http/http/request.mojo | 11 +++++++---- lightbug_http/io/bytes.mojo | 1 + lightbug_http/service.mojo | 1 + tests/lightbug_http/http/test_request.mojo | 10 +++++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lightbug_http/http/request.mojo b/lightbug_http/http/request.mojo index b6332519..0e83f409 100644 --- a/lightbug_http/http/request.mojo +++ b/lightbug_http/http/request.mojo @@ -56,10 +56,13 @@ struct HTTPRequest(Writable, Stringable): var request = HTTPRequest( URI.parse(addr + uri), headers=headers, method=method, protocol=protocol, cookies=cookies ) - try: - request.read_body(reader, content_length, max_body_size) - except e: - raise Error("HTTPRequest.from_bytes: Failed to read request body: " + str(e)) + + if content_length > 0: + try: + reader.skip_carriage_return() + request.read_body(reader, content_length, max_body_size) + except e: + raise Error("HTTPRequest.from_bytes: Failed to read request body: " + str(e)) return request diff --git a/lightbug_http/io/bytes.mojo b/lightbug_http/io/bytes.mojo index 007fbdf4..0bc0a70d 100644 --- a/lightbug_http/io/bytes.mojo +++ b/lightbug_http/io/bytes.mojo @@ -205,6 +205,7 @@ struct ByteReader[origin: Origin]: raise OutOfBoundsError self.read_pos += count + print(start, start + count) return self._inner[start : start + count] fn read_until(mut self, char: Byte) -> ByteView[origin]: diff --git a/lightbug_http/service.mojo b/lightbug_http/service.mojo index 05a20a2d..687b05aa 100644 --- a/lightbug_http/service.mojo +++ b/lightbug_http/service.mojo @@ -2,6 +2,7 @@ from lightbug_http.http import HTTPRequest, HTTPResponse, OK, NotFound from lightbug_http.io.bytes import Bytes, bytes from lightbug_http.strings import to_string from lightbug_http.header import HeaderKey +from utils import StringSlice trait HTTPService: diff --git a/tests/lightbug_http/http/test_request.mojo b/tests/lightbug_http/http/test_request.mojo index d9e6fdfb..8268da50 100644 --- a/tests/lightbug_http/http/test_request.mojo +++ b/tests/lightbug_http/http/test_request.mojo @@ -18,7 +18,15 @@ def test_request_from_bytes(): def test_read_body(): - ... + alias data = "GET /redirect HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nUser-Agent: python-requests/2.32.3\r\nAccept-Encoding: gzip, deflate, br, zstd\r\nAccept: */\r\nContent-Length: 17\r\nconnection: keep-alive\r\n\r\nThis is the body!" + var request = HTTPRequest.from_bytes("127.0.0.1", 4096, data.as_bytes()) + testing.assert_equal(request.protocol, "HTTP/1.1") + testing.assert_equal(request.method, "GET") + testing.assert_equal(request.uri.request_uri, "/redirect") + testing.assert_equal(request.headers["Host"], "127.0.0.1:8080") + testing.assert_equal(request.headers["User-Agent"], "python-requests/2.32.3") + + testing.assert_equal(request.get_body(), "This is the body!") def test_encode(): From 952d039ed8bc11d78421d25d6d58d59a71264ca1 Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Sat, 1 Feb 2025 13:54:15 -0600 Subject: [PATCH 2/4] Update service.mojo --- lightbug_http/service.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/lightbug_http/service.mojo b/lightbug_http/service.mojo index 687b05aa..05a20a2d 100644 --- a/lightbug_http/service.mojo +++ b/lightbug_http/service.mojo @@ -2,7 +2,6 @@ from lightbug_http.http import HTTPRequest, HTTPResponse, OK, NotFound from lightbug_http.io.bytes import Bytes, bytes from lightbug_http.strings import to_string from lightbug_http.header import HeaderKey -from utils import StringSlice trait HTTPService: From 353ce4d13a4a89913e5926fc1f751f549a2399ca Mon Sep 17 00:00:00 2001 From: Mikhail Tavarez Date: Sat, 1 Feb 2025 13:54:38 -0600 Subject: [PATCH 3/4] Update bytes.mojo --- lightbug_http/io/bytes.mojo | 1 - 1 file changed, 1 deletion(-) diff --git a/lightbug_http/io/bytes.mojo b/lightbug_http/io/bytes.mojo index 0bc0a70d..007fbdf4 100644 --- a/lightbug_http/io/bytes.mojo +++ b/lightbug_http/io/bytes.mojo @@ -205,7 +205,6 @@ struct ByteReader[origin: Origin]: raise OutOfBoundsError self.read_pos += count - print(start, start + count) return self._inner[start : start + count] fn read_until(mut self, char: Byte) -> ByteView[origin]: From dfcf13db24806429df21defaae088ae5cf1898ac Mon Sep 17 00:00:00 2001 From: Val Date: Sun, 2 Feb 2025 13:27:49 +0100 Subject: [PATCH 4/4] always log internal server error --- README.md | 27 ++++++++++++--------------- lightbug_http/server.mojo | 5 +++-- lightbug_http/service.mojo | 6 ++++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4e272b99..ec4ba5b5 100644 --- a/README.md +++ b/README.md @@ -97,25 +97,22 @@ Once you have a Mojo project set up locally, For example, to make a `Printer` service that prints some details about the request to console: ```mojo - from lightbug_http import * + from lightbug_http.http import HTTPRequest, HTTPResponse, OK + from lightbug_http.strings import to_string + from lightbug_http.header import HeaderKey @value struct Printer(HTTPService): fn func(mut self, req: HTTPRequest) raises -> HTTPResponse: - var uri = req.uri - print("Request URI: ", to_string(uri.request_uri)) - - var header = req.headers - print("Request protocol: ", req.protocol) - print("Request method: ", req.method) - print( - "Request Content-Type: ", to_string(header[HeaderKey.CONTENT_TYPE]) - ) - - var body = req.body_raw - print("Request Body: ", to_string(body)) - - return OK(body) + print("Request URI:", req.uri.request_uri) + print("Request protocol:", req.protocol) + print("Request method:", req.method) + if HeaderKey.CONTENT_TYPE in req.headers: + print("Request Content-Type:", req.headers[HeaderKey.CONTENT_TYPE]) + if req.body_raw: + print("Request Body:", to_string(req.body_raw)) + + return OK(req.body_raw) ``` 6. Start a server listening on a port with your service like so. diff --git a/lightbug_http/server.mojo b/lightbug_http/server.mojo index 1832d413..e3579c83 100644 --- a/lightbug_http/server.mojo +++ b/lightbug_http/server.mojo @@ -161,14 +161,15 @@ struct Server(Movable): var response: HTTPResponse try: response = handler.func(request) - except: + except e: + logger.error("Unexpected error in the handler:", e) + if not conn.is_closed(): # Try to send back an internal server error, but always attempt to teardown the connection. try: # TODO: Move InternalError response to an alias when Mojo can support Dict operations at compile time. (@thatstoasty) _ = conn.write(encode(InternalError())) except e: - logger.error(e) raise Error("Failed to send InternalError response") finally: conn.teardown() diff --git a/lightbug_http/service.mojo b/lightbug_http/service.mojo index 05a20a2d..339c646f 100644 --- a/lightbug_http/service.mojo +++ b/lightbug_http/service.mojo @@ -15,8 +15,10 @@ struct Printer(HTTPService): print("Request URI:", req.uri.request_uri) print("Request protocol:", req.protocol) print("Request method:", req.method) - print("Request Content-Type:", req.headers[HeaderKey.CONTENT_TYPE]) - print("Request Body:", to_string(req.body_raw)) + if HeaderKey.CONTENT_TYPE in req.headers: + print("Request Content-Type:", req.headers[HeaderKey.CONTENT_TYPE]) + if req.body_raw: + print("Request Body:", to_string(req.body_raw)) return OK(req.body_raw)