An HTTP/1.1 server for zig
0

Configure Feed

Select the types of activity you want to include in your feed.

http.zig / examples / 04_action_context.zig
3.4 kB 92 lines
1const std = @import("std"); 2const httpz = @import("httpz"); 3const Allocator = std.mem.Allocator; 4 5const PORT = 8804; 6 7// This example is very similar to 03_dispatch.zig, but shows how the action 8// state can be a different type than the handler. 9 10pub fn main(init: std.process.Init) !void { 11 const allocator = init.gpa; 12 13 var handler = Handler{}; 14 var server = try httpz.Server(*Handler).init(init.io, allocator, .{ .address = .localhost(PORT) }, &handler); 15 16 defer server.deinit(); 17 18 // ensures a clean shutdown, finishing off any existing requests 19 // see 09_shutdown.zig for how to to break server.listen with an interrupt 20 defer server.stop(); 21 22 var router = try server.router(.{}); 23 24 const restricted_route = &RouteData{ .restricted = true }; 25 26 // We can register arbitrary data to a route, which we can retrieve 27 // via req.route_data. This is stored as a `*const anyopaque`. 28 router.get("/", index, .{}); 29 router.get("/admin", admin, .{ .data = restricted_route }); 30 31 std.debug.print("listening http://localhost:{d}/\n", .{PORT}); 32 33 // Starts the server, this is blocking. 34 try server.listen(); 35} 36 37const Handler = struct { 38 // In example_3, our action type was: httpz.Action(*Handler). 39 // In this example, we've changed it to: httpz.Action(*Env) 40 // This allows our handler to be a general app-wide "state" while our actions 41 // received a request-specific context 42 pub fn dispatch(self: *Handler, action: httpz.Action(*Env), req: *httpz.Request, res: *httpz.Response) !void { 43 const user = (try req.query()).get("auth"); 44 45 // RouteData can be anything, but since it's stored as a *const anyopaque 46 // you'll need to restore the type/alignment. 47 48 // (You could also use a per-route handler, or middleware, to achieve 49 // the same thing. Using route data is a bit ugly due to the type erasure 50 // but it can be convenient!). 51 if (req.route_data) |rd| { 52 const route_data: *const RouteData = @ptrCast(@alignCast(rd)); 53 if (route_data.restricted and (user == null or user.?.len == 0)) { 54 res.status = 401; 55 res.body = "permission denied"; 56 return; 57 } 58 } 59 60 var env = Env{ 61 .user = user, // todo: this is not very good security! 62 .handler = self, 63 }; 64 65 try action(&env, req, res); 66 } 67}; 68 69const RouteData = struct { 70 restricted: bool, 71}; 72 73const Env = struct { 74 handler: *Handler, 75 user: ?[]const u8, 76}; 77 78fn index(_: *Env, _: *httpz.Request, res: *httpz.Response) !void { 79 res.content_type = .HTML; 80 res.body = 81 \\<!DOCTYPE html> 82 \\ <p>The <code>Handler.dispatch</code> method takes an <code>httpz.Action(*Env)</code>. 83 \\ <p>This allows the handler method to create a request-specific value to pass into actions. 84 \\ <p>For example, dispatch might load a User (using a request header value maybe) and make it available to the action. 85 \\ <p>Goto <a href="/admin?auth=superuser">admin</a> to simulate a (very insecure) authentication. 86 ; 87} 88 89// because of our dispatch method, this can only be called when env.user != null 90fn admin(env: *Env, _: *httpz.Request, res: *httpz.Response) !void { 91 res.body = try std.fmt.allocPrint(res.arena, "Welcome to the admin portal, {s}", .{env.user.?}); 92}