11from lightbug_http.http import *
22from 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
59struct 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.
1320trait 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
1751struct 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
5497struct 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
105123struct 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
146216fn OK (body : Bytes) -> HTTPResponse:
147217 return OK(body, String(" text/plain" ))
148218
0 commit comments