Skip to content

Commit cf22edc

Browse files
committed
Refactor request, response and header parsing and data structure to be more Pythonic
Signed-off-by: Brian Grenier <grenierb96@gmail.com>
1 parent 313595b commit cf22edc

25 files changed

+814
-2190
lines changed

bench.mojo

Lines changed: 108 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,119 @@
1-
import benchmark
2-
from lightbug_http.sys.server import SysServer
3-
from lightbug_http.python.server import PythonServer
4-
from lightbug_http.service import TechEmpowerRouter
1+
from benchmark import *
2+
from lightbug_http.io.bytes import bytes, Bytes
3+
from lightbug_http.header import Headers, Header
4+
from lightbug_http.utils import ByteReader, ByteWriter
5+
from lightbug_http.http import HTTPRequest, HTTPResponse, encode
6+
from lightbug_http.uri import URI
57
from tests.utils import (
68
TestStruct,
79
FakeResponder,
810
new_fake_listener,
911
FakeServer,
10-
getRequest,
1112
)
1213

14+
alias headers = bytes('''GET /index.html HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/html\r\nContent-Length: 1234\r\nConnection: close\r\nTrailer: end-of-message\r\n\r\n''')
15+
alias body = bytes(String("I am the body of an HTTP request") * 5)
16+
alias Request = bytes('''GET /index.html HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Mozilla/5.0\r\nContent-Type: text/html\r\nContent-Length: 1234\r\nConnection: close\r\nTrailer: end-of-message\r\n\r\n''') + body
17+
alias Response = bytes("HTTP/1.1 200 OK\r\nserver: lightbug_http\r\ncontent-type: application/octet-stream\r\nconnection: keep-alive\r\ncontent-length: 13\r\ndate: 2024-06-02T13:41:50.766880+00:00\r\n\r\n") + body
1318

1419
fn main():
20+
run_benchmark()
21+
22+
fn run_benchmark():
1523
try:
16-
var server = SysServer(tcp_keep_alive=True)
17-
var handler = TechEmpowerRouter()
18-
server.listen_and_serve("0.0.0.0:8080", handler)
19-
except e:
20-
print("Error starting server: " + e.__str__())
21-
return
24+
var config = BenchConfig(warmup_iters=100)
25+
config.verbose_timing = True
26+
config.tabular_view = True
27+
var m = Bench(config)
28+
m.bench_function[lightbug_benchmark_header_encode](BenchId("HeaderEncode"))
29+
m.bench_function[lightbug_benchmark_header_parse](BenchId("HeaderParse"))
30+
m.bench_function[lightbug_benchmark_request_encode](BenchId("RequestEncode"))
31+
m.bench_function[lightbug_benchmark_request_parse](BenchId("RequestParse"))
32+
m.bench_function[lightbug_benchmark_response_encode](BenchId("ResponseEncode"))
33+
m.bench_function[lightbug_benchmark_response_parse](BenchId("ResponseParse"))
34+
m.dump_report()
35+
except:
36+
print("failed to start benchmark")
37+
38+
39+
var headers_struct = Headers(
40+
Header("Content-Type", "application/json"),
41+
Header("Content-Length", "1234"),
42+
Header("Connection", "close"),
43+
Header("Date", "some-datetime"),
44+
Header("SomeHeader", "SomeValue")
45+
)
46+
47+
@parameter
48+
fn lightbug_benchmark_response_encode(inout b: Bencher):
49+
@always_inline
50+
@parameter
51+
fn response_encode():
52+
var res = HTTPResponse(body, headers = headers_struct)
53+
_ = encode(res^)
54+
b.iter[response_encode]()
2255

56+
@parameter
57+
fn lightbug_benchmark_response_parse(inout b: Bencher):
58+
@always_inline
59+
@parameter
60+
fn response_parse():
61+
var res = Response
62+
try:
63+
_ = HTTPResponse.from_bytes(res^)
64+
except:
65+
pass
66+
b.iter[response_parse]()
67+
68+
@parameter
69+
fn lightbug_benchmark_request_parse(inout b: Bencher):
70+
@always_inline
71+
@parameter
72+
fn request_parse():
73+
var r = Request
74+
try:
75+
_ = HTTPRequest.from_bytes("127.0.0.1/path", 4096, r^)
76+
except:
77+
pass
78+
b.iter[request_parse]()
79+
80+
@parameter
81+
fn lightbug_benchmark_request_encode(inout b: Bencher):
82+
@always_inline
83+
@parameter
84+
fn request_encode():
85+
var req = HTTPRequest(
86+
URI.parse("http://127.0.0.1:8080/some-path")[URI],
87+
headers = headers_struct,
88+
body = body
89+
)
90+
_ = encode(req^)
91+
b.iter[request_encode]()
92+
93+
@parameter
94+
fn lightbug_benchmark_header_encode(inout b: Bencher):
95+
@always_inline
96+
@parameter
97+
fn header_encode():
98+
var b = ByteWriter()
99+
var h = headers_struct
100+
h.encode_to(b)
101+
b.iter[header_encode]()
102+
103+
@parameter
104+
fn lightbug_benchmark_header_parse(inout b: Bencher):
105+
@always_inline
106+
@parameter
107+
fn header_parse():
108+
try:
109+
var b = headers
110+
var header = Headers()
111+
var reader = ByteReader(b^)
112+
_ = header.parse_raw(reader)
113+
except:
114+
print("failed")
115+
116+
b.iter[header_parse]()
23117

24118
fn lightbug_benchmark_server():
25119
var server_report = benchmark.run[run_fake_server](max_iters=1)
@@ -38,9 +132,11 @@ fn lightbug_benchmark_misc() -> None:
38132
recreating_set_report.print(benchmark.Unit.ms)
39133

40134

135+
var GetRequest = HTTPRequest(URI.parse("http://127.0.0.1/path")[URI])
136+
41137
fn run_fake_server():
42138
var handler = FakeResponder()
43-
var listener = new_fake_listener(2, getRequest)
139+
var listener = new_fake_listener(2, encode(GetRequest))
44140
var server = FakeServer(listener, handler)
45141
server.serve()
46142

bench_server.mojo

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from lightbug_http.sys.server import SysServer
2+
from lightbug_http.service import TechEmpowerRouter
3+
4+
def main():
5+
try:
6+
var server = SysServer(tcp_keep_alive=True)
7+
var handler = TechEmpowerRouter()
8+
server.listen_and_serve("0.0.0.0:8080", handler)
9+
except e:
10+
print("Error starting server: " + e.__str__())
11+
return

client.mojo

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,24 @@ from lightbug_http import *
22
from lightbug_http.sys.client import MojoClient
33

44
fn test_request(inout client: MojoClient) raises -> None:
5-
var uri = URI("http://httpbin.org/status/404")
6-
try:
7-
uri.parse()
8-
except e:
9-
print("error parsing uri: " + e.__str__())
5+
var uri = URI.parse_raises("http://httpbin.org/status/404")
106

117

128
var request = HTTPRequest(uri)
13-
var response = client.do(request)
9+
var response = client.do(request^)
1410

1511
# print status code
16-
print("Response:", response.header.status_code())
12+
print("Response:", response.status_code)
1713

1814
# print parsed headers (only some are parsed for now)
19-
print("Content-Type:", to_string(response.header.content_type()))
20-
print("Content-Length", response.header.content_length())
21-
print("Server:", to_string(response.header.server()))
15+
print("Content-Type:", response.headers["Content-Type"])
16+
print("Content-Length", response.headers["Content-Length"])
17+
print("Server:", to_string(response.headers["Server"]))
2218

23-
print("Is connection set to connection-close? ", response.header.connection_close())
19+
print("Is connection set to connection-close? ", response.connection_close())
2420

2521
# print body
26-
print(to_string(response.get_body_bytes()))
22+
print(to_string(response.body_raw))
2723

2824

2925
fn main() raises -> None:

lightbug.🔥

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from lightbug_http import *
1+
from lightbug_http import Welcome, SysServer
22

33
fn main() raises:
44
var server = SysServer()

lightbug_http/client.mojo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ trait Client:
88
fn __init__(inout self, host: StringLiteral, port: Int) raises:
99
...
1010

11-
fn do(self, req: HTTPRequest) raises -> HTTPResponse:
11+
fn do(self, owned req: HTTPRequest) raises -> HTTPResponse:
1212
...

lightbug_http/error.mojo

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from lightbug_http.http import HTTPResponse
2-
from lightbug_http.header import ResponseHeader
32
from lightbug_http.io.bytes import bytes
43

54
alias TODO_MESSAGE = String("TODO").as_bytes()
@@ -8,4 +7,4 @@ alias TODO_MESSAGE = String("TODO").as_bytes()
87
@value
98
struct ErrorHandler:
109
fn Error(self) -> HTTPResponse:
11-
return HTTPResponse(ResponseHeader(), TODO_MESSAGE)
10+
return HTTPResponse(TODO_MESSAGE)

0 commit comments

Comments
 (0)