Skip to content

Commit c8f6f44

Browse files
committed
feat: support middleware
1 parent 99150cc commit c8f6f44

File tree

5 files changed

+89
-54
lines changed

5 files changed

+89
-54
lines changed

β€Žlightbug.πŸ”₯β€Ž

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,17 @@ from lightbug_http import *
22

33
fn main() raises:
44
var server = SysServer()
5-
var handler = Welcome()
6-
server.listen_and_serve("0.0.0.0:8080", handler)
5+
var middleware = MiddlewareChain()
6+
middleware.add(CompressionMiddleware())
7+
middleware.add(ErrorMiddleware())
8+
middleware.add(LoggerMiddleware())
9+
middleware.add(CorsMiddleware(allows_origin = "*"))
10+
middleware.add(BasicAuthMiddleware("admin", "password"))
11+
12+
var router = RouterMiddleware()
13+
router.add("GET", "/static", StaticMiddleware("static"))
14+
15+
middleware.add(router)
16+
17+
server.listen_and_serve("0.0.0.0:8080", middleware)
18+

β€Žlightbug_http/__init__.mojoβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ from lightbug_http.http import HTTPRequest, HTTPResponse, OK
22
from lightbug_http.service import HTTPService, Welcome
33
from lightbug_http.sys.server import SysServer
44
from lightbug_http.tests.run import run_tests
5+
from lightbug_http.middleware import *
56

67
trait DefaultConstructible:
78
fn __init__(inout self) raises:

β€Žlightbug_http/middleware.mojoβ€Ž

Lines changed: 71 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,139 @@
1+
from lightbug_http.http import HTTPRequest, HTTPResponse
2+
13
struct Context:
24
var request: Request
35
var params: Dict[String, AnyType]
46

5-
func __init__(request: Request):
7+
fn __init__(self, request: Request):
68
self.request = request
79
self.params = Dict[String, AnyType]()
810

911
trait Middleware:
1012
var next: Middleware
1113

12-
func call(context: Context) -> Response:
14+
fn call(self, context: Context) -> Response:
1315
...
1416

1517
struct ErrorMiddleware(Middleware):
16-
func call(context: Context) -> Response:
17-
do:
18+
fn call(self, context: Context) -> Response:
19+
try:
1820
return next.call(context: context)
1921
catch e: Exception:
2022
return InternalServerError()
2123

2224
struct LoggerMiddleware(Middleware):
23-
func call(context: Context) -> Response:
25+
fn call(self, context: Context) -> Response:
2426
print("Request: \(context.request)")
2527
return next.call(context: context)
2628

27-
struct RouterMiddleware(Middleware):
28-
var routes: Dict[String, Middleware]
29-
30-
func __init__():
31-
self.routes = Dict[String, Middleware]()
32-
33-
func add(route: String, middleware: Middleware):
34-
routes[route] = middleware
35-
36-
func call(context: Context) -> Response:
37-
# TODO: create a more advanced router
38-
39-
var route = context.request.path
40-
if middleware = routes[route]:
41-
return middleware.call(context: context)
42-
else:
43-
return NotFound()
44-
4529
struct StaticMiddleware(Middleware):
4630
var path: String
4731

48-
funct __init__(path: String):
32+
fnt __init__(self, path: String):
4933
self.path = path
5034

51-
func call(context: Context) -> Response:
52-
var file = File(path: path + context.request.path)
35+
fn call(self, context: Context) -> Response:
36+
if context.request.path == "/":
37+
var file = File(path: path + "index.html")
38+
else:
39+
var file = File(path: path + context.request.path)
40+
5341
if file.exists:
54-
return FileResponse(file: file)
42+
var html: String
43+
with open(file, "r") as f:
44+
html = f.read()
45+
return OK(html.as_bytes(), "text/html")
5546
else:
5647
return next.call(context: context)
5748

5849
struct CorsMiddleware(Middleware):
59-
func call(context: Context) -> Response:
60-
var response = next.call(context: context)
61-
response.headers["Access-Control-Allow-Origin"] = "*"
62-
return response
50+
var allow_origin: String
51+
52+
fn __init__(self, allow_origin: String):
53+
self.allow_origin = allow_origin
54+
55+
fn call(self, context: Context) -> Response:
56+
if context.request.method == "OPTIONS":
57+
var response = next.call(context: context)
58+
response.headers["Access-Control-Allow-Origin"] = allow_origin
59+
response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
60+
response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
61+
return response
62+
63+
if context.request.origin == allow_origin:
64+
return next.call(context: context)
65+
else:
66+
return Unauthorized()
6367

6468
struct CompressionMiddleware(Middleware):
65-
func call(context: Context) -> Response:
69+
fn call(self, context: Context) -> Response:
6670
var response = next.call(context: context)
6771
response.body = compress(response.body)
6872
return response
6973

70-
struct SessionMiddleware(Middleware):
71-
var session: Session
74+
fn compress(self, body: Bytes) -> Bytes:
75+
#TODO: implement compression
76+
return body
7277

73-
func call(context: Context) -> Response:
74-
var request = context.request
75-
var response = context.response
76-
var session = session.load(request)
77-
context.params["session"] = session
78-
response = next.call(context: context)
79-
session.save(response)
80-
return response
78+
79+
struct RouterMiddleware(Middleware):
80+
var routes: Dict[String, Middleware]
81+
82+
fn __init__(self):
83+
self.routes = Dict[String, Middleware]()
84+
85+
fn add(self, method: String, route: String, middleware: Middleware):
86+
routes[method + ":" + route] = middleware
87+
88+
fn call(self, context: Context) -> Response:
89+
# TODO: create a more advanced router
90+
var method = context.request.method
91+
var route = context.request.path
92+
if middleware = routes[method + ":" + route]:
93+
return middleware.call(context: context)
94+
else:
95+
return next.call(context: context)
8196

8297
struct BasicAuthMiddleware(Middleware):
8398
var username: String
8499
var password: String
85100

86-
func __init__(username: String, password: String):
101+
fn __init__(self, username: String, password: String):
87102
self.username = username
88103
self.password = password
89104

90-
func call(context: Context) -> Response:
105+
fn call(self, context: Context) -> Response:
91106
var request = context.request
92107
var auth = request.headers["Authorization"]
93108
if auth == "Basic \(username):\(password)":
109+
context.params["username"] = username
94110
return next.call(context: context)
95111
else:
96112
return Unauthorized()
97113

98-
struct MiddlewareChain:
114+
# always add at the end of the middleware chain
115+
struct NotFoundMiddleware(Middleware):
116+
fn call(self, context: Context) -> Response:
117+
return NotFound()
118+
119+
struct MiddlewareChain(HttpService):
99120
var middlewares: Array[Middleware]
100121

101-
func __init__():
122+
fn __init__(self):
102123
self.middlewares = Array[Middleware]()
103124

104-
func add(middleware: Middleware):
125+
fn add(self, middleware: Middleware):
105126
if middlewares.count == 0:
106127
middlewares.append(middleware)
107128
else:
108129
var last = middlewares[middlewares.count - 1]
109130
last.next = middleware
110131
middlewares.append(middleware)
111132

112-
func execute(request: Request) -> Response:
133+
fn func(self, request: Request) -> Response:
134+
self.add(NotFoundMiddleware())
113135
var context = Context(request: request, response: response)
114-
if middlewares.count > 0:
115-
return middlewares[0].call(context: context)
116-
else:
117-
return NotFound()
136+
return middlewares[0].call(context: context)
118137

119138
fn OK(body: Bytes) -> HTTPResponse:
120139
return OK(body, String("text/plain"))

β€Žlightbug_http/service.mojoβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ struct Printer(HTTPService):
1919
struct Welcome(HTTPService):
2020
fn func(self, req: HTTPRequest) raises -> HTTPResponse:
2121
var uri = req.uri()
22+
var html: String
23+
with open("static/index.html", "r") as f:
24+
html = f.read()
2225

2326
if uri.path() == "/":
2427
var html: Bytes
File renamed without changes.

0 commit comments

Comments
Β (0)