Skip to content

Commit 609f4a2

Browse files
authored
Merge pull request #247 from Lightbug-HQ/fix/incomplete-requests-handling
Fix/incomplete requests handling
2 parents 20f8e7e + 052633f commit 609f4a2

File tree

5 files changed

+78
-38
lines changed

5 files changed

+78
-38
lines changed

lightbug_http/http/common_response.mojo

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,11 @@ fn InternalError() -> HTTPResponse:
4141
headers=Headers(Header(HeaderKey.CONTENT_TYPE, "text/plain")),
4242
status_text="Internal Server Error",
4343
)
44+
45+
fn BadRequest() -> HTTPResponse:
46+
return HTTPResponse(
47+
bytes("Bad Request"),
48+
status_code=400,
49+
headers=Headers(Header(HeaderKey.CONTENT_TYPE, "text/plain")),
50+
status_text="Bad Request",
51+
)

lightbug_http/http/request.mojo

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,20 @@ struct HTTPRequest(Writable, Stringable):
128128
if content_length > max_body_size:
129129
raise Error("Request body too large")
130130

131-
self.body_raw = r.read_bytes(content_length).to_bytes()
132-
self.set_content_length(content_length)
131+
try:
132+
self.body_raw = r.read_bytes(content_length).to_bytes()
133+
self.set_content_length(len(self.body_raw))
134+
except OutOfBoundsError:
135+
logger.debug("Failed to read full request body as per content-length header. Proceeding with the available bytes.")
136+
var available_bytes = len(r._inner) - r.read_pos
137+
if available_bytes > 0:
138+
self.body_raw = r.read_bytes(available_bytes).to_bytes()
139+
self.set_content_length(len(self.body_raw))
140+
else:
141+
logger.debug("No body bytes available. Setting content-length to 0.")
142+
self.body_raw = Bytes()
143+
self.set_content_length(0)
144+
133145

134146
fn write_to[T: Writer, //](self, mut writer: T):
135147
path = self.uri.path if len(self.uri.path) > 1 else strSlash

lightbug_http/server.mojo

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ from lightbug_http._logger import logger
66
from lightbug_http.connection import NoTLSListener, default_buffer_size, TCPConnection, ListenConfig
77
from lightbug_http.socket import Socket
88
from lightbug_http.http import HTTPRequest, encode
9-
from lightbug_http.http.common_response import InternalError
9+
from lightbug_http.http.common_response import InternalError, BadRequest
1010
from lightbug_http.uri import URI
1111
from lightbug_http.header import Headers
1212
from lightbug_http.service import HTTPService
@@ -159,47 +159,50 @@ struct Server(Movable):
159159
if String(e) == "EOF":
160160
return
161161
else:
162-
logger.error(e)
163-
raise Error("Server.serve_connection: Failed to read request")
162+
logger.error("Server.serve_connection: Failed to read request. Expected EOF, got:", String(e))
163+
return
164164

165165
var request: HTTPRequest
166166
try:
167167
request = HTTPRequest.from_bytes(self.address(), max_request_body_size, request_buffer)
168+
var response: HTTPResponse
169+
var close_connection = (not self.tcp_keep_alive) or request.connection_close()
170+
try:
171+
response = handler.func(request)
172+
if close_connection:
173+
response.set_connection_close()
174+
logger.debug(
175+
conn.socket._remote_address.ip,
176+
String(conn.socket._remote_address.port),
177+
request.method,
178+
request.uri.path,
179+
response.status_code,
180+
)
181+
try:
182+
_ = conn.write(encode(response^))
183+
except e:
184+
logger.error("Failed to write encoded response to the connection:", String(e))
185+
conn.teardown()
186+
break
187+
188+
if close_connection:
189+
conn.teardown()
190+
break
191+
except e:
192+
logger.error("Handler error:", String(e))
193+
if not conn.is_closed():
194+
try:
195+
_ = conn.write(encode(InternalError()))
196+
except e:
197+
raise Error("Failed to send InternalError response")
198+
finally:
199+
conn.teardown()
200+
return
168201
except e:
169202
logger.error("Failed to parse HTTPRequest:", String(e))
170-
raise Error("Server.serve_connection: Failed to parse request")
171-
172-
var response: HTTPResponse
173-
var close_connection = (not self.tcp_keep_alive) or request.connection_close()
174-
try:
175-
response = handler.func(request)
176-
if close_connection:
177-
response.set_connection_close()
178-
logger.debug(
179-
conn.socket._remote_address.ip,
180-
String(conn.socket._remote_address.port),
181-
request.method,
182-
request.uri.path,
183-
response.status_code,
184-
)
185203
try:
186-
_ = conn.write(encode(response^))
204+
_ = conn.write(encode(BadRequest()))
187205
except e:
188-
logger.error("Failed to write encoded response to the connection:", String(e))
189-
conn.teardown()
190-
break
191-
192-
if close_connection:
206+
logger.error("Failed to write BadRequest response to the connection:", String(e))
193207
conn.teardown()
194208
break
195-
except e:
196-
logger.error("Handler error:", String(e))
197-
198-
if not conn.is_closed():
199-
try:
200-
_ = conn.write(encode(InternalError()))
201-
except e:
202-
raise Error("Failed to send InternalError response")
203-
finally:
204-
conn.teardown()
205-
return

tests/integration/integration_client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import requests
2+
import socket
3+
import time
24

35
session = requests.Session()
46

@@ -23,3 +25,16 @@
2325
}
2426
response = session.get('http://127.0.0.1:8080/large-headers', headers=large_headers)
2527
assert response.status_code == 200
28+
29+
print("\n~~~ Testing content-length mismatch (smaller) ~~~")
30+
def test_content_length_smaller():
31+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
32+
s.connect(('127.0.0.1', 8080))
33+
s.sendall(b'POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 100\r\n\r\nOnly sending 20 bytes')
34+
time.sleep(1)
35+
s.close()
36+
37+
test_content_length_smaller()
38+
time.sleep(1)
39+
40+
print("\n~~~ All tests completed ~~~")

tests/integration/integration_test_server.mojo

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ from lightbug_http import *
55
struct IntegrationTestService(HTTPService):
66
fn func(mut self, req: HTTPRequest) raises -> HTTPResponse:
77
var p = req.uri.path
8-
if p == "/redirect":
8+
if p == "/":
9+
return OK("hello")
10+
elif p == "/redirect":
911
return HTTPResponse(
1012
"get off my lawn".as_bytes(),
1113
headers=Headers(Header(HeaderKey.LOCATION, "/rd-destination")),

0 commit comments

Comments
 (0)