@@ -2,6 +2,8 @@ const std = @import("std");
2
2
const log = std .log .scoped (.@"zzz/http/router" );
3
3
const assert = std .debug .assert ;
4
4
5
+ const HTTPError = @import ("lib.zig" ).HTTPError ;
6
+
5
7
const _Route = @import ("router/route.zig" ).Route ;
6
8
7
9
const Capture = @import ("router/routing_trie.zig" ).Capture ;
@@ -13,7 +15,13 @@ const _Context = @import("context.zig").Context;
13
15
const _RoutingTrie = @import ("router/routing_trie.zig" ).RoutingTrie ;
14
16
const QueryMap = @import ("router/routing_trie.zig" ).QueryMap ;
15
17
16
- /// Default not found handler: send a plain text response.
18
+ /// Error handler type.
19
+ pub fn ErrorHandlerFn (comptime Server : type , comptime AppState : type ) type {
20
+ const Context = _Context (Server , AppState );
21
+ return * const fn (context : * Context , err : anyerror ) anyerror ! void ;
22
+ }
23
+
24
+ /// Create a default not found handler: send a plain text response.
17
25
pub fn default_not_found_handler (comptime Server : type , comptime AppState : type ) _Route (Server , AppState ).HandlerFn {
18
26
const Context = _Context (Server , AppState );
19
27
@@ -28,6 +36,87 @@ pub fn default_not_found_handler(comptime Server: type, comptime AppState: type)
28
36
}.not_found_handler ;
29
37
}
30
38
39
+ /// Create a default error handler: send a plain text response with the error, if known, internal server error otherwise.
40
+ pub fn default_error_handler (comptime Server : type , comptime AppState : type ) ErrorHandlerFn (Server , AppState ) {
41
+ const Context = _Context (Server , AppState );
42
+ return struct { fn f (ctx : * Context , err : anyerror ) ! void {
43
+ // Handle all default HTTP errors.
44
+ switch (err ) {
45
+ HTTPError .ContentTooLarge = > {
46
+ try ctx .respond (.{
47
+ .status = .@"Content Too Large" ,
48
+ .mime = Mime .TEXT ,
49
+ .body = "Request was too large." ,
50
+ });
51
+ },
52
+ HTTPError .HTTPVersionNotSupported = > {
53
+ try ctx .respond (.{
54
+ .status = .@"HTTP Version Not Supported" ,
55
+ .mime = Mime .HTML ,
56
+ .body = "HTTP version not supported." ,
57
+ });
58
+ },
59
+ HTTPError .InvalidMethod = > {
60
+ try ctx .respond (.{
61
+ .status = .@"Not Implemented" ,
62
+ .mime = Mime .TEXT ,
63
+ .body = "Not implemented." ,
64
+ });
65
+ },
66
+ HTTPError .LengthRequired = > {
67
+ try ctx .respond (.{
68
+ .status = .@"Length Required" ,
69
+ .mime = Mime .TEXT ,
70
+ .body = "Length required." ,
71
+ });
72
+ },
73
+ HTTPError .MalformedRequest = > {
74
+ try ctx .respond (.{
75
+ .status = .@"Bad Request" ,
76
+ .mime = Mime .TEXT ,
77
+ .body = "Malformed request." ,
78
+ });
79
+ },
80
+ HTTPError .MethodNotAllowed = > {
81
+ if (ctx .route ) | route | {
82
+ add_allow_header : {
83
+ // We also need to add to Allow header.
84
+ // This uses the connection's arena to allocate 64 bytes.
85
+ const allowed = route .get_allowed (ctx .provision .arena .allocator ()) catch break :add_allow_header ;
86
+ ctx .provision .response .headers .put_assume_capacity ("Allow" , allowed );
87
+ }
88
+ }
89
+ try ctx .respond (.{
90
+ .status = .@"Method Not Allowed" ,
91
+ .mime = Mime .TEXT ,
92
+ .body = "Method not allowed." ,
93
+ });
94
+ },
95
+ HTTPError .TooManyHeaders = > {
96
+ try ctx .respond (.{
97
+ .status = .@"Request Header Fields Too Large" ,
98
+ .mime = Mime .TEXT ,
99
+ .body = "Too many headers." ,
100
+ });
101
+ },
102
+ HTTPError .URITooLong = > {
103
+ try ctx .respond (.{
104
+ .status = .@"URI Too Long" ,
105
+ .mime = Mime .TEXT ,
106
+ .body = "URI too long." ,
107
+ });
108
+ },
109
+ else = > {
110
+ try ctx .respond (.{
111
+ .status = .@"Internal Server Error" ,
112
+ .mime = Mime .TEXT ,
113
+ .body = "Internal server error." ,
114
+ });
115
+ },
116
+ }
117
+ } }.f ;
118
+ }
119
+
31
120
/// Initialize a router with the given routes.
32
121
pub fn Router (comptime Server : type , comptime AppState : type ) type {
33
122
return struct {
@@ -40,17 +129,20 @@ pub fn Router(comptime Server: type, comptime AppState: type) type {
40
129
/// Router configuration structure.
41
130
pub const Configuration = struct {
42
131
not_found_handler : Route.HandlerFn = default_not_found_handler (Server , AppState ),
132
+ error_handler : ErrorHandlerFn (Server , AppState ) = default_error_handler (Server , AppState ),
43
133
};
44
134
45
135
routes : RoutingTrie ,
46
136
not_found_route : Route ,
137
+ error_handler : ErrorHandlerFn (Server , AppState ),
47
138
state : AppState ,
48
139
49
140
pub fn init (state : AppState , comptime _routes : []const Route , comptime configuration : Configuration ) Self {
50
141
const self = Self {
51
142
// Initialize the routing tree from the given routes.
52
143
.routes = comptime RoutingTrie .init (_routes ),
53
144
.not_found_route = comptime Route .init ("" ).all (configuration .not_found_handler ),
145
+ .error_handler = configuration .error_handler ,
54
146
.state = state ,
55
147
};
56
148
0 commit comments