-
Notifications
You must be signed in to change notification settings - Fork 674
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: router problem removing Router. It also, make everything faster
- Loading branch information
Showing
6 changed files
with
53 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ a | |
*.so | ||
*.dylib | ||
*.dll | ||
*v | ||
|
||
# Ignore binary output folders | ||
bin/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,43 @@ | ||
module main | ||
|
||
import strings | ||
|
||
const h_response_body = '{"message": "Hello, world!"}' | ||
const http_ok_response = 'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n'.bytes() | ||
const http_ok_response = 'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 1\r\nConnection: keep-alive\r\n\r\n1'.bytes() | ||
|
||
const http_created_response = 'HTTP/1.1 201 Created\r\nContent-Type: application/json\r\nContent-Length: 0\r\nConnection: keep-alive\r\n\r\n'.bytes() | ||
|
||
fn home_controller(params map[string]string) ![]u8 { | ||
fn home_controller(params []string) ![]u8 { | ||
return http_ok_response | ||
} | ||
|
||
fn get_users_controller(params map[string]string) ![]u8 { | ||
fn get_users_controller(params []string) ![]u8 { | ||
return http_ok_response | ||
} | ||
|
||
@[manualfree] | ||
fn get_user_controller(params map[string]string) ![]u8 { | ||
id := params['id'] or { return error('User ID required') } | ||
@[direct_array_access; manualfree] | ||
fn get_user_controller(params []string) ![]u8 { | ||
if params.len == 0 { | ||
return tiny_bad_request_response | ||
} | ||
id := params[0] | ||
response_body := id | ||
|
||
response := 'HTTP/1.1 200 OK\r | ||
Content-Type: text/plain\r | ||
Content-Length: ${response_body.len}\r | ||
Connection: keep-alive\r | ||
\r | ||
${response_body}'.bytes() | ||
mut sb := strings.new_builder(200) | ||
sb.write_string('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: ') | ||
sb.write_string(response_body.len.str()) | ||
sb.write_string('\r\nConnection: keep-alive\r\n\r\n') | ||
sb.write_string(response_body) | ||
|
||
defer { | ||
unsafe { | ||
id.free() | ||
response_body.free() | ||
response.free() | ||
params.free() | ||
} | ||
} | ||
|
||
return response | ||
return sb | ||
} | ||
|
||
fn create_user_controller(params map[string]string) ![]u8 { | ||
return http_ok_response | ||
fn create_user_controller(params []string) ![]u8 { | ||
return http_created_response | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,23 @@ | ||
module main | ||
|
||
// RadixNode represents a node in the Radix Trie used for routing. | ||
// It contains a map of children nodes, a handler function, and information | ||
// about whether the node represents a parameterized segment. | ||
// [see](https://en.wikipedia.org/wiki/Radix_tree) | ||
@[heap] | ||
struct RadixNode { | ||
mut: | ||
children map[string]&RadixNode | ||
handler fn (params map[string]string) ![]u8 = unsafe { nil } // avoid the use of optional | ||
is_param bool | ||
param_name string | ||
} | ||
|
||
// Router represents a Radix Trie router with support for parameterized routes. | ||
struct Router { | ||
mut: | ||
root RadixNode // Root node of the Radix Trie | ||
params map[string]string // Map to store route parameters | ||
} | ||
|
||
// add_route adds a route to the Radix Trie with support for parameters | ||
fn (mut router Router) add_route(method string, path string, handler fn (params map[string]string) ![]u8) { | ||
segments := path.split('/').filter(it.len > 0) | ||
mut node := &router.root | ||
|
||
for segment in segments { | ||
is_param := segment.starts_with(':') | ||
segment_key := if is_param { ':' } else { segment } | ||
if segment_key !in node.children { | ||
node.children[segment_key] = &RadixNode{ | ||
children: map[string]&RadixNode{} | ||
is_param: is_param | ||
param_name: if is_param { segment[1..] } else { '' } | ||
} | ||
} | ||
node = node.children[segment_key] or { panic('Unexpected radix trie error') } | ||
} | ||
node.handler = handler | ||
} | ||
|
||
// handle_request finds and executes the handler for a given route. | ||
// It takes an HttpRequest object as an argument and returns the response as a byte array. | ||
fn (mut router Router) handle_request(req HttpRequest) ![]u8 { | ||
path := req.buffer[req.path.start..req.path.start + req.path.len] | ||
segments := path.bytestr().split('/').filter(it.len > 0) | ||
mut node := &router.root | ||
// router.params.clear() | ||
|
||
// FIXME: race condition here in router.params | ||
router.params = map[string]string{} | ||
|
||
for segment in segments { | ||
mut matched := false | ||
|
||
for key, child in node.children { | ||
if child.is_param { | ||
// FIXME: race condition here in router.params | ||
router.params[child.param_name] = segment | ||
|
||
node = child | ||
matched = true | ||
break | ||
} else if key == segment { | ||
node = child | ||
matched = true | ||
break | ||
} | ||
fn handle_request(req HttpRequest) ![]u8 { | ||
method := unsafe { tos(&req.buffer[req.method.start], req.method.len) } | ||
path := unsafe { tos(&req.buffer[req.path.start], req.path.len) } | ||
|
||
if method == 'GET' { | ||
if path == '/' { | ||
return home_controller([]) | ||
} else if path.starts_with('/user/') { | ||
id := path[6..] | ||
return get_user_controller([id]) | ||
} | ||
|
||
if !matched { | ||
dump(req.buffer.bytestr()) | ||
return error('Route not matched. segment: ${segment}, path ${path.bytestr()}') | ||
} else if method == 'POST' { | ||
if path == '/user' { | ||
return create_user_controller([]) | ||
} | ||
} | ||
|
||
if node.handler == unsafe { nil } { | ||
dump(req.buffer.bytestr()) | ||
return error('Route does not have handler for path ${path.bytestr()}') | ||
} | ||
handler := node.handler | ||
|
||
return handler(router.params)! | ||
} | ||
|
||
// setup_router initializes the router and adds predefined routes with their handler functions. | ||
// It returns the initialized Router object. | ||
fn setup_router() Router { | ||
mut router := Router{ | ||
root: RadixNode{ | ||
children: map[string]&RadixNode{} | ||
} | ||
} | ||
|
||
// Adding routes with handler functions/controllers | ||
router.add_route('GET', '/', home_controller) | ||
router.add_route('GET', '/user', get_users_controller) | ||
router.add_route('GET', '/user/:id', get_user_controller) | ||
router.add_route('POST', '/user', create_user_controller) | ||
|
||
return router | ||
return tiny_bad_request_response | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters