Skip to content

Commit 76bb36d

Browse files
committed
fix: fixed most issues.
1 parent af9f37c commit 76bb36d

File tree

3 files changed

+149
-71
lines changed

3 files changed

+149
-71
lines changed

lightbug.🔥

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
from lightbug_http import *
22

3-
fn main() raises:
4-
var server = SysServer()
3+
struct HelloWorld(HTTPHandler):
4+
fn handle(self, context: Context) -> HTTPResponse:
5+
return OK(String("Hello, World!").as_bytes())
56

7+
fn main() raises:
68
var router = RouterMiddleware()
7-
router.add("GET", "/static", StaticMiddleware("static"))
9+
router.add("GET", "/hello", HelloWorld())
810

911
var middleware = MiddlewareChain()
1012
middleware.add(CompressionMiddleware())
1113
middleware.add(ErrorMiddleware())
1214
middleware.add(LoggerMiddleware())
1315
middleware.add(CorsMiddleware(allows_origin = "*"))
1416
middleware.add(BasicAuthMiddleware("admin", "password"))
17+
middleware.add(StaticMiddleware("static"))
1518
middleware.add(router)
1619
middleware.add(NotFoundMiddleware())
1720

21+
var server = SysServer()
1822
server.listen_and_serve("0.0.0.0:8080", middleware)
1923

lightbug_http/middleware.mojo

Lines changed: 138 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from lightbug_http.http import *
22
from lightbug_http.service import HTTPService
33

4+
5+
## Context is a container for the request and response data.
6+
## It is passed to each middleware in the chain.
7+
## It also contains a dictionary of parameters that can be shared between middleware.
48
@value
59
struct Context:
610
var request: HTTPRequest
@@ -10,51 +14,93 @@ struct Context:
1014
self.request = request
1115
self.params = Dict[String, String]()
1216

17+
18+
## Middleware is an interface for processing HTTP requests.
19+
## Each middleware in the chain can modify the request and response.
1320
trait Middleware:
21+
fn set_next(self, next: Middleware):
22+
...
1423
fn call(self, context: Context) -> HTTPResponse:
1524
...
1625

26+
## MiddlewareChain is a chain of middleware that processes the request.
27+
@value
28+
struct MiddlewareChain(HTTPService):
29+
var root: Middleware
30+
31+
fn add(self, middleware: Middleware):
32+
if self.root == nil:
33+
self.root = middleware
34+
else:
35+
var current = self.root
36+
while current.next != nil:
37+
current = current.next
38+
current.set_next(middleware)
39+
40+
fn func(self, request: HTTPRequest) raises -> HTTPResponse:
41+
var context = Context(request)
42+
return self.root.call(context)
43+
44+
45+
# Middleware implementations
46+
47+
## Error handler will catch any exceptions thrown by the other
48+
## middleware and return a 500 response.
49+
## It should be the first middleware in the chain.
50+
@value
1751
struct ErrorMiddleware(Middleware):
1852
var next: Middleware
1953

54+
fn set_next(self, next: Middleware):
55+
self.next = next
56+
2057
fn call(inout self, context: Context) -> HTTPResponse:
2158
try:
22-
return next.call(context)
23-
catch e: Exception:
24-
return InternalServerError()
59+
return self.next.call(context)
60+
except e:
61+
return InternalServerError(e.as_bytes())
2562

26-
struct LoggerMiddleware(Middleware):
63+
64+
## Compression middleware compresses the response body.
65+
@value
66+
struct CompressionMiddleware(Middleware):
2767
var next: Middleware
2868

69+
fn set_next(self, next: Middleware):
70+
self.next = next
71+
2972
fn call(self, context: Context) -> HTTPResponse:
30-
print(f"Request: {context.request}")
31-
return next.call(context)
73+
var response = self.next.call(context)
74+
response.body = self.compress(response.body)
75+
return response
3276

33-
struct StaticMiddleware(Middleware):
77+
fn compress(self, body: Bytes) -> Bytes:
78+
#TODO: implement compression
79+
return body
80+
81+
82+
## Logger middleware logs the request to the console.
83+
@value
84+
struct LoggerMiddleware(Middleware):
3485
var next: Middleware
35-
var path: String
3686

37-
fn __init__(self, path: String):
38-
self.path = path
87+
fn set_next(self, next: Middleware):
88+
self.next = next
3989

4090
fn call(self, context: Context) -> HTTPResponse:
41-
if context.request.uri().path() == "/":
42-
var file = File(path: path + "index.html")
43-
else:
44-
var file = File(path: path + context.request.uri().path())
91+
print(f"Request: {context.request}")
92+
return self.next.call(context)
4593

46-
if file.exists:
47-
var html: String
48-
with open(file, "r") as f:
49-
html = f.read()
50-
return OK(html.as_bytes(), "text/html")
51-
else:
52-
return next.call(context)
5394

95+
## CORS middleware adds the necessary headers to allow cross-origin requests.
96+
@value
5497
struct CorsMiddleware(Middleware):
5598
var next: Middleware
5699
var allow_origin: String
57100

101+
fn set_next(self, next: Middleware):
102+
self.next = next
103+
58104
fn __init__(self, allow_origin: String):
59105
self.allow_origin = allow_origin
60106

@@ -67,46 +113,21 @@ struct CorsMiddleware(Middleware):
67113
return response
68114

69115
if context.request.origin == allow_origin:
70-
return next.call(context)
116+
return self.next.call(context)
71117
else:
72-
return Unauthorized()
73-
74-
struct CompressionMiddleware(Middleware):
75-
var next: Middleware
76-
fn call(self, context: Context) -> HTTPResponse:
77-
var response = next.call(context)
78-
response.body = compress(response.body)
79-
return response
80-
81-
fn compress(self, body: Bytes) -> Bytes:
82-
#TODO: implement compression
83-
return body
84-
85-
struct RouterMiddleware(Middleware):
86-
var next: Middleware
87-
var routes: Dict[String, Middleware]
88-
89-
fn __init__(inout self):
90-
self.routes = Dict[String, Middleware]()
91-
92-
fn add(self, method: String, route: String, middleware: Middleware):
93-
self.routes[method + ":" + route] = middleware
118+
return Unauthorized("CORS not allowed")
94119

95-
fn call(self, context: Context) -> HTTPResponse:
96-
# TODO: create a more advanced router
97-
var method = context.request.header.method()
98-
var route = context.request.uri().path()
99-
var middleware = self.routes.find(method + ":" + route)
100-
if middleware:
101-
return middleware.value().call(context)
102-
else:
103-
return next.call(context)
104120

121+
## BasicAuth middleware requires basic authentication to access the route.
122+
@value
105123
struct BasicAuthMiddleware(Middleware):
106124
var next: Middleware
107125
var username: String
108126
var password: String
109127

128+
fn set_next(self, next: Middleware):
129+
self.next = next
130+
110131
fn __init__(self, username: String, password: String):
111132
self.username = username
112133
self.password = password
@@ -120,29 +141,78 @@ struct BasicAuthMiddleware(Middleware):
120141
else:
121142
return Unauthorized("Requires Basic Authentication")
122143

123-
# always add at the end of the middleware chain
124-
struct NotFoundMiddleware(Middleware):
144+
145+
## Static middleware serves static files from a directory.
146+
@value
147+
struct StaticMiddleware(Middleware):
148+
var next: Middleware
149+
var path: String
150+
151+
fn set_next(self, next: Middleware):
152+
self.next = next
153+
154+
fn __init__(self, path: String):
155+
self.path = path
156+
125157
fn call(self, context: Context) -> HTTPResponse:
126-
return NotFound(String("Not Found").as_bytes())
158+
var path = context.request.uri().path()
159+
if path.endswith("/"):
160+
path = path + "index.html"
127161

128-
struct MiddlewareChain(HTTPService):
129-
var middlewares: List[Middleware]
162+
try:
163+
var html: String
164+
with open(file, "r") as f:
165+
html = f.read()
166+
167+
return OK(html.as_bytes(), "text/html")
168+
except e:
169+
return self.next.call(context)
170+
171+
172+
173+
174+
## HTTPHandler is an interface for handling HTTP requests in the RouterMiddleware.
175+
## It is a leaf node in the middleware chain.
176+
trait HTTPHandler:
177+
fn handle(self, context: Context) -> HTTPResponse:
178+
...
179+
180+
181+
## Router middleware routes requests to different middleware based on the path.
182+
@value
183+
struct RouterMiddleware(Middleware):
184+
var next: Middleware
185+
var routes: Dict[String, HTTPHandler]
130186

131187
fn __init__(inout self):
132-
self.middlewares = Array[Middleware]()
188+
self.routes = Dict[String, HTTPHandler]()
133189

134-
fn add(self, middleware: Middleware):
135-
if self.middlewares.count == 0:
136-
self.middlewares.append(middleware)
190+
fn set_next(self, next: Middleware):
191+
self.next = next
192+
193+
fn add(self, method: String, route: String, handler: HTTPHandler):
194+
self.routes[method + ":" + route] = handler
195+
196+
fn call(self, context: Context) -> HTTPResponse:
197+
# TODO: create a more advanced router
198+
var method = context.request.header.method()
199+
var route = context.request.uri().path()
200+
var handler = self.routes.find(method + ":" + route)
201+
if handler:
202+
return handler.value().handle(context)
137203
else:
138-
var last = self.middlewares[middlewares.count - 1]
139-
last.next = middleware
140-
self.middlewares.append(middleware)
204+
return next.call(context)
205+
206+
207+
## NotFound middleware returns a 404 response if no other middleware handles the request. It is a leaf node and always add at the end of the middleware chain
208+
@value
209+
struct NotFoundMiddleware(Middleware):
210+
fn call(self, context: Context) -> HTTPResponse:
211+
return NotFound(String("Not Found").as_bytes())
212+
141213

142-
fn func(self, req: HTTPRequest) raises -> HTTPResponse:
143-
var context = Context(request)
144-
return self.middlewares[0].call(context)
145214

215+
### Helper functions to create HTTP responses
146216
fn OK(body: Bytes) -> HTTPResponse:
147217
return OK(body, String("text/plain"))
148218

lightbug_http/service.mojo

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ trait HTTPService:
55
fn func(self, req: HTTPRequest) raises -> HTTPResponse:
66
...
77

8+
@value
9+
struct HelloWorld(HTTPService):
10+
fn func(self, req: HTTPRequest) raises -> HTTPResponse:
11+
return OK("Hello, World!".as_bytes(), "text/plain")
812

913
@value
1014
struct Printer(HTTPService):

0 commit comments

Comments
 (0)