tiny.http.Router
Defined in tiny.http.
Source
Source: lib/http/src/router/runtime.zig:39
zig
pub fn Router(comptime Context: type) type { return struct { allocator: std.mem.Allocator, routes: RouteMap, not_found_handler: ?Handler, context: Context, const Self = @This(); pub const Handler = *const fn (Context, *Request, *Response) anyerror!void; pub const WsHandler = *const fn (Context, *WebSocket) anyerror!void; const RouteEntry: type = RouterRouteEntryType(Handler, WsHandler); const RouteMap: type = std.HashMap(RouterRouteKey, RouteEntry, RouterRouteKeyContext, 80); const WebSocketSession = struct { allocator: std.mem.Allocator, websocket: WebSocket, context: Context, handler: WsHandler, fn drive(self: *WebSocketSession) !void { self.handler(self.context, &self.websocket) catch |err| switch (err) { error.WouldBlock => return, else => { self.websocket.close(WebSocketCloseCode.internal_error, "Handler error") catch |close_err| { log.warn("failed to send websocket close after handler error: {s}", .{@errorName(close_err)}); }; self.websocket.connection.markClosing(); return err; }, }; if (self.websocket.state == .closed) self.websocket.connection.markClosing(); } fn destroy(self: *WebSocketSession) void { self.websocket.deinit(self.allocator); self.allocator.destroy(self); } }; pub fn init(allocator: std.mem.Allocator, context: Context) Self { return .{ .allocator = allocator, .routes = RouteMap.init(allocator), .not_found_handler = null, .context = context, }; } pub fn deinit(self: *Self) void { self.routes.deinit(); } pub fn route(self: *Self, method: Request.Method, path: []const u8, handler: Handler) !void { try self.routes.put(.{ .method = method, .path = path }, .{ .handler = handler, .is_websocket = false, .ws_handler = null, .websocket_limits = null, }); } pub fn get(self: *Self, path: []const u8, handler: Handler) !void { try self.route(.GET, path, handler); } pub fn head(self: *Self, path: []const u8, handler: Handler) !void { try self.route(.HEAD, path, handler); } pub fn post(self: *Self, path: []const u8, handler: Handler) !void { try self.route(.POST, path, handler); } pub fn put(self: *Self, path: []const u8, handler: Handler) !void { try self.route(.PUT, path, handler); } pub fn delete(self: *Self, path: []const u8, handler: Handler) !void { try self.route(.DELETE, path, handler); } pub fn websocket( self: *Self, path: []const u8, limits: WebSocket.Limits, upgrade_handler: Handler, ws_handler: WsHandler, ) !void { try self.routes.put(.{ .method = .GET, .path = path }, .{ .handler = upgrade_handler, .is_websocket = true, .ws_handler = ws_handler, .websocket_limits = limits, }); } pub fn notFound(self: *Self, handler: Handler) void { self.not_found_handler = handler; } pub fn handleRequest(self: *Self, conn: *Connection) !bool { switch (conn.currentState()) { .http => {}, .websocket => { conn.driveProtocol() catch |err| { conn.markClosing(); return err; }; if (conn.currentState() == .websocket) conn.waitForInput(); return false; }, .closing, .closed => return false, } const parse_result = while (true) { if (conn.hasBufferedInput()) { if (Request.parse( conn.requestScratch(), conn.bufferedInput(), )) |result| { break result; } else |err| { if (err != ParseError.IncompleteRequest) { try sendBadRequest(conn); conn.markClosing(); return false; } } } _ = conn.bufferInput() catch |err| switch (err) { error.ConnectionClosed => return false, error.WouldBlock => { conn.waitForRead(); return false; }, error.InputCapacityExceeded => { try sendBadRequest(conn); conn.markClosing(); return false; }, else => return err, }; }; defer conn.consumeBufferedInput(parse_result.consumed); var req = parse_result.request; var res = Response.init(conn.responseScratch()); defer res.deinit(); const key = RouterRouteKey{ .method = req.method, .path = req.pathOnly() }; if (self.routes.get(key)) |entry| { if (entry.is_websocket and req.isWebSocketUpgrade()) { return self.handleWebSocketUpgrade(conn, &req, entry); } else { entry.handler(self.context, &req, &res) catch |err| { try sendInternalError(conn, req.method); conn.markClosing(); return err; }; } } else if (self.not_found_handler) |handler| { handler(self.context, &req, &res) catch |err| { try sendInternalError(conn, req.method); conn.markClosing(); return err; }; } else { res.status = 404; res.status_text = "Not Found"; res.body = "Not Found"; } try writeResponse(conn, try res.serializeForMethod(req.method)); const connection_header = req.headers.get("Connection") orelse ""; const keep_alive = connectionWantsKeepAlive(req.version, connection_header); if (keep_alive) { conn.waitForInput(); } else { conn.markClosing(); } return keep_alive; } fn handleWebSocketUpgrade(self: *Self, conn: *Connection, req: *Request, entry: RouteEntry) !bool { const handshake = WebSocket.validateClientHandshake(req) catch { try sendBadRequest(conn); conn.markClosing(); return false; }; var res = Response.init(conn.responseScratch()); defer res.deinit(); entry.handler(self.context, req, &res) catch |err| { try sendInternalError(conn, req.method); conn.markClosing(); return err; }; if (res.status != 200 and res.status != 0) { try writeResponse(conn, try res.serializeForMethod(req.method)); conn.markClosing(); return false; } const session = try self.allocator.create(WebSocketSession); errdefer self.allocator.destroy(session); session.* = .{ .allocator = self.allocator, .websocket = try WebSocket.init( self.allocator, entry.websocket_limits.?, conn, ), .context = self.context, .handler = entry.ws_handler.?, }; errdefer session.websocket.deinit(self.allocator); const accept_key = handshake.accept(); var ws_res = try Response.switchingProtocols(conn.responseScratch(), &accept_key); defer ws_res.deinit(); try writeResponse(conn, try ws_res.serialize()); session.websocket.activate(); conn.installProtocol(session, WebSocketSession.drive, WebSocketSession.destroy); return false; } fn sendBadRequest(conn: *Connection) !void { var res = Response.init(conn.responseScratch()); defer res.deinit(); res.status = 400; res.status_text = "Bad Request"; res.body = "Bad Request"; try writeResponse(conn, try res.serialize()); } fn sendInternalError(conn: *Connection, method: Request.Method) !void { var res = Response.init(conn.responseScratch()); defer res.deinit(); res.status = 500; res.status_text = "Internal Server Error"; res.body = "Internal Server Error"; try writeResponse(conn, try res.serializeForMethod(method)); } };}Source: lib/http/src/root.zig:75
zig
pub const Router = router.Router;Complete call list
11 direct calls.
tiny.http.Request.parse[function] atlib/http/src/message.zig:337tiny.http.Response.deinit[method] atlib/http/src/response.zig:300tiny.http.Response.init[function] atlib/http/src/response.zig:289tiny.http.Response.serialize[method] atlib/http/src/response.zig:338tiny.http.Response.serializeForMethod[method] atlib/http/src/response.zig:342tiny.http.Response.switchingProtocols[function] atlib/http/src/response.zig:346lib.http.src.router.runtime.RouterRouteEntryType[function] — private source atlib/http/src/router/runtime.zig:30in nearest public ownerlib.http.src.router.runtimelib.http.src.router.runtime.connectionWantsKeepAlive[function] — private source atlib/http/src/router/runtime.zig:296in nearest public ownerlib.http.src.router.runtimelib.http.src.router.runtime.writeResponse[function] — private source atlib/http/src/router/runtime.zig:332in nearest public ownerlib.http.src.router.runtimetiny.http.WebSocket.init[function] atlib/http/src/websocket.zig:578tiny.http.WebSocket.validateClientHandshake[function] atlib/http/src/websocket.zig:604
Audit
| Definitions | 1 |
|---|---|
| Public names | 1 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |