3.7 KiB
3.7 KiB
HTTP 1/1 server library
Routing rules
- Static first routing
- Path variables defined with colon prefix:
:foo - Example:
/odin/:version/download - Wild card routing not implemented
Compile time user errors included
When defining the server you can pass a custom error union type for the server. All the handler function will, of course, need to return these in as a pair with a response.
Memory allocation
The server will free the context temp allocators memory after the handler is run, thus, you are free
to use context.temp_allocator within your handlers and expect the memory to be freed after.
This becomes useful when you want to pass the response back to the server but is also ergonomic
if you find yourself in need of a temp allocator in your handlers.
Example
package main
import "core:log"
import "core:net"
import "core:strings"
import fjord "fjord"
import http "fjord/http"
InternalError :: enum {
DatabaseError,
}
BadRequestError :: enum {
UserUnauthorised,
InputFormatWrong,
}
Error :: union {
InternalError,
BadRequestError,
}
// Handler for GET /hello/:name
hello_handler :: proc(request: ^http.Request) -> (http.Response, Error) {
name := request.path_variables["name"]
body := strings.concatenate(
{"<h1>Hello, ", name, "!</h1>"},
context.temp_allocator,
)
response := http.make_response(
.Ok,
transmute([]byte)body,
.Html,
context.temp_allocator,
)
return response, nil
}
// Handler for GET /users/:id/posts/:post_id
user_post_handler :: proc(request: ^http.Request) -> (http.Response, Error) {
user_id := request.path_variables["id"]
post_id := request.path_variables["post-id"]
body := strings.concatenate(
{"<p>User ", user_id, " - Post ", post_id, "</p>"},
context.temp_allocator,
)
response := http.make_response(
.Ok,
transmute([]byte)body,
.Html,
context.temp_allocator,
)
return response, nil
}
error_callback :: proc(error: Error) -> http.Response {
switch err_type in error {
case BadRequestError:
switch err in err_type {
case UserUnauthorised: fallthrough
case InputFormatWrong:
body := "Bad request error"
return http.make_response(
.BadRequest,
transmute([]byte)body,
.Html,
context.temp_allocator,
)
}
case InternalError:
switch err in err_type {
case DataBaseError:
body := "Internal server error"
return http.make_response(
.InternalServerError,
transmute([]byte)body,
.Html,
context.temp_allocator,
)
}
}
}
main :: proc() {
context.logger = log.create_console_logger(.Info)
defer log.destroy_console_logger(context.logger)
endpoint := net.Endpoint {
address = net.IP4_Address{127, 0, 0, 1},
port = 8080,
}
server: fjord.Server(UserError)
fjord.server_init(&server, endpoint, context.allocator)
defer fjord.server_destroy(&server)
fjord.server_add_route(&server, .GET, {"hello", ":name"}, hello_handler)
fjord.server_add_route(&server, .GET, {"users", ":id", "posts", ":post-id"}, user_post_handler)
server_set_error_handler(&server, error_callback)
log.infof("Server listening on http://127.0.0.1:%d", endpoint.port)
fjord.server_listen_and_serve(&server)
}
Dev
Run the test suite to check functionality, curl is a required dependency for the test suite to work.
odin test .