Add custom error handler callback

This commit is contained in:
Hugo Mårdbrink 2025-11-14 12:02:14 +01:00
parent 595872f81c
commit e739557f8e
3 changed files with 221 additions and 34 deletions

View file

@ -1,12 +1,15 @@
package fjord
import "base:intrinsics"
import "base:runtime"
import "core:bufio"
import "core:bytes"
import "core:fmt"
import "core:io"
import "core:log"
import "core:net"
import "core:path/slashpath"
import "core:reflect"
import "core:strconv"
import "core:strings"
import "core:sync"
@ -15,7 +18,10 @@ import "core:unicode"
import http "http"
Server :: struct($Error_Type: typeid) {
Server :: struct(
$Error_Type: typeid
) where intrinsics.type_is_union(Error_Type)
{
endpoint: net.Endpoint,
not_found_handler: #type proc(
request: ^http.Request,
@ -23,6 +29,7 @@ Server :: struct($Error_Type: typeid) {
http.Response,
Error_Type,
),
error_handler: #type proc(error: Error_Type) -> http.Response,
running: bool,
router: Router(Error_Type),
thread_pool: thread.Pool,
@ -66,7 +73,7 @@ server_init :: proc(
endpoint: net.Endpoint,
thread_count: uint,
allocator := context.allocator,
) {
) where intrinsics.type_is_union(Error_Type) {
server^ = Server(Error_Type) {
endpoint = endpoint,
not_found_handler = proc(
@ -83,6 +90,19 @@ server_init :: proc(
)
return http_response, nil
},
error_handler = proc(error: Error_Type) -> http.Response {
error_string := fmt.tprint(error)
body := strings.concatenate(
{"<div>Error: ", error_string, "</div>"},
context.temp_allocator,
)
http_response := http.make_response(
.BadRequest,
transmute([]byte)body,
.Html,
)
return http_response
},
thread_count = thread_count,
running = false,
allocator = allocator,
@ -95,7 +115,7 @@ server_init :: proc(
server_destroy :: proc(server: ^Server($Error_Type)) {
if sync.atomic_load(&server.thread_pool.is_running) {
thread.pool_join(&server.thread_pool)
thread.pool_finish(&server.thread_pool)
}
thread.pool_destroy(&server.thread_pool)
@ -115,6 +135,14 @@ server_set_not_found_handler :: proc(
server.not_found_handler = not_found_handler
}
// This is used instead of default server_make param until compiler bug is fixed: https://github.com/odin-lang/Odin/issues/5792
server_set_error_handler :: proc(
server: ^Server($Error_Type),
error_handler: #type proc(error: Error_Type) -> http.Response,
) {
server.error_handler = error_handler
}
server_add_route :: proc(
server: ^Server($Error_Type),
method: http.Method,
@ -171,9 +199,9 @@ serve :: proc(server: ^Server($Error_Type), client_socket: net.TCP_Socket) {
http_response, handler_err := handler(&http_request)
log.info(handler_err)
if handler_err != nil {
log.warnf("Handler failed with error: %s", handler_err)
return
http_response = server.error_handler(handler_err)
}
marshalled_response := http.marshall_response(
@ -183,27 +211,26 @@ serve :: proc(server: ^Server($Error_Type), client_socket: net.TCP_Socket) {
net.send_tcp(client_socket, marshalled_response)
}
RequestThreadData :: struct($Error_Type: typeid) {
server: ^Server(Error_Type),
client_socket: net.TCP_Socket,
}
@(private)
request_thread_task :: proc(t: thread.Task) {
task_data := cast(^RequestThreadData(any))t.data
defer free(task_data, t.allocator)
serve(task_data.server, task_data.client_socket)
net.close(task_data.client_socket)
// context.temp_allocator is freed automatically on thread death.
}
@(private)
spawn_request_thread :: proc(
server: ^Server($Error_Type),
client_socket: net.TCP_Socket,
) {
RequestThreadData :: struct($Error_Type: typeid) {
server: ^Server(Error_Type),
client_socket: net.TCP_Socket,
}
request_thread_task :: proc(t: thread.Task) {
task_data := cast(^RequestThreadData(Error_Type))t.data
defer free(task_data, t.allocator)
serve(task_data.server, task_data.client_socket)
net.close(task_data.client_socket)
// context.temp_allocator is freed automatically on thread death.
}
task_data := new(RequestThreadData(Error_Type))
task_data^ = RequestThreadData(Error_Type) {
server = server,
@ -237,5 +264,4 @@ server_listen_and_serve :: proc(server: ^Server($Error_Type)) {
}
thread.pool_finish(&server.thread_pool)
thread.pool_join(&server.thread_pool)
}