Skip to content

Commit

Permalink
more examples
Browse files Browse the repository at this point in the history
  • Loading branch information
karlseguin committed Aug 30, 2024
1 parent 0872ba1 commit 30195d6
Show file tree
Hide file tree
Showing 14 changed files with 322 additions and 227 deletions.
4 changes: 4 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ pub fn build(b: *std.Build) !void {
.{.file = "examples/02_handler.zig", .name = "example_2"},
.{.file = "examples/03_dispatch.zig", .name = "example_3"},
.{.file = "examples/04_action_context.zig", .name = "example_4"},
.{.file = "examples/05_request_takeover.zig", .name = "example_5"},
.{.file = "examples/06_middleware.zig", .name = "example_6"},
.{.file = "examples/07_advanced_routing.zig", .name = "example_7"},
.{.file = "examples/08_websocket.zig", .name = "example_8"},
};

{
Expand Down
13 changes: 12 additions & 1 deletion examples/01_basic.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const std = @import("std");
const httpz = @import("httpz");
const Allocator = std.mem.Allocator;

const PORT = 8800;
const PORT = 8801;

// This example demonstrates basic httpz usage, with focus on using the
// httpz.Request and httpz.Response objects.
Expand Down Expand Up @@ -37,6 +37,7 @@ pub fn main() !void {
router.get("/metrics", metrics, .{});
router.get("/form_data", formShow, .{});
router.post("/form_data", formPost, .{});
router.get("/explicit_write", explicitWrite, .{});

std.debug.print("listening http://localhost:{d}/\n", .{PORT});
// Starts the server, this is blocking.
Expand All @@ -52,6 +53,7 @@ fn index(_: *httpz.Request, res: *httpz.Response) !void {
\\ <li><a href="/json/hello/Duncan">Path parameter + json writer</a>
\\ <li><a href="/metrics">Internal metrics</a>
\\ <li><a href="/form_data">Form Data</a>
\\ <li><a href="/explicit_write">Explicit Write</a>
;
}

Expand Down Expand Up @@ -107,3 +109,12 @@ fn formPost(req: *httpz.Request, res: *httpz.Response) !void {
try std.fmt.format(w, "{s}={s}\n", .{kv.key, kv.value});
}
}

fn explicitWrite(_: *httpz.Request, res: *httpz.Response) !void {
res.body =
\\ There may be cases where your response is tied to data which
\\ required cleanup. If `res.arena` and `res.writer()` can't solve
\\ the issue, you can always call `res.write()` explicitly
;
return res.write();
}
6 changes: 5 additions & 1 deletion examples/02_handler.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const std = @import("std");
const httpz = @import("httpz");
const Allocator = std.mem.Allocator;

const PORT = 8801;
const PORT = 8802;

// This example demonstrates using a custom Handler. It shows how to have
// global state (here we show a counter, but it could be a more complex struct
Expand Down Expand Up @@ -64,6 +64,10 @@ const Handler = struct {
fn index(_: *Handler, _: *httpz.Request, res: *httpz.Response) !void {
res.body =
\\<!DOCTYPE html>
\\ <p>Except in very simple cases, you'll want to use a custom Handler.
\\ <p>A custom Handler is how you share app-specific data with your actions (like a DB pool)
\\ and define a custom not found and error function.
\\ <p>Other examples show more advanced things you can do with a custom Handler.
\\ <ul>
\\ <li><a href="/hits">Shared global hit counter</a>
\\ <li><a href="/not_found">Custom not found handler</a>
Expand Down
13 changes: 10 additions & 3 deletions examples/03_dispatch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const std = @import("std");
const httpz = @import("httpz");
const Allocator = std.mem.Allocator;

const PORT = 8802;
const PORT = 8803;

// This example uses a custom dispatch method on our handler for greater control
// in how actions are executed.
Expand Down Expand Up @@ -37,10 +37,17 @@ const Handler = struct {
// we could do authentication and set the response directly on error.
try action(self, req, res);

std.debug.print("{d}\t{d}us\t{s}\n", .{std.time.timestamp(), start.lap() / 1000, req.url.path});
std.debug.print("ts={d} us={d} path={s}\n", .{std.time.timestamp(), start.lap() / 1000, req.url.path});
}
};

fn index(_: *Handler, _: *httpz.Request, res: *httpz.Response) !void {
res.body = "see the console logs";
res.body =
\\ If defied, the dispatch method will be invoked for every request with a matching route.
\\ It is up to dispatch to decide how/if the action should be called. While httpz
\\ supports middleware, most cases can be more easily and cleanly handled with
\\ a custom dispatch alone (you can always use both middlewares and a custom dispatch though).
\\
\\ Check out the console, our custom dispatch function times & logs each request.
;
}
9 changes: 6 additions & 3 deletions examples/04_action_context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const std = @import("std");
const httpz = @import("httpz");
const Allocator = std.mem.Allocator;

const PORT = 8803;
const PORT = 8804;

// This example is very similar to 03_dispatch.zig, but shows how the action
// state can be a different type than the handler.
Expand Down Expand Up @@ -72,10 +72,13 @@ const Env = struct{
};

fn index(_: *Env, _: *httpz.Request, res: *httpz.Response) !void {
res.content_type = .HTML;
res.body =
\\<!DOCTYPE html>
\\ <ul>
\\ <li><a href="/admin?auth=sudo">admin</a>
\\ <p>The <code>Handler.dispatch</code> method takes an <code>httpz.Action(*Env)</code>.
\\ <p>This allows the handler method to create a request-specific value to pass into actions.
\\ <p>For example, dispatch might load a User (using a request header value maybe) and make it available to the action.
\\ <p>Goto <a href="/admin?auth=superuser">admin</a> to simulate a (very insecure) authentication.
;
}

Expand Down
37 changes: 37 additions & 0 deletions examples/05_request_takeover.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const std = @import("std");
const httpz = @import("httpz");
const Allocator = std.mem.Allocator;

const PORT = 8805;

// This example uses the Handler's "handle" function to completely takeover
// request processing from httpz.

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

var handler = Handler{};
var server = try httpz.Server(*Handler).init(allocator, .{.port = PORT}, &handler);
defer server.deinit();

// Routes aren't used in this mode

std.debug.print("listening http://localhost:{d}/\n", .{PORT});

// Starts the server, this is blocking.
try server.listen();
}

const Handler = struct {
pub fn handle(_: *Handler, _: *httpz.Request, res: *httpz.Response) void {
res.body =
\\ If defined, the "handle" function is called early in httpz' request
\\ processing. Routing, middlewares, not found and error handling are all skipped.
\\ This is an advanced option and is used by frameworks like JetZig to provide
\\ their own flavor and enhancement ontop of httpz.
\\ If you define this, the special "dispatch", "notFound" and "uncaughtError"
\\ functions have no meaning as far as httpz is concerned.
;
}
};
59 changes: 59 additions & 0 deletions examples/06_middleware.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const std = @import("std");
const httpz = @import("httpz");
const Logger = @import("middleware/Logger.zig");

const Allocator = std.mem.Allocator;

const PORT = 8806;

// This example show how to use and create middleware. There's overlap between
// what you can achieve with a custom dispatch function and per-route
// configuration (shown in the next example) and middleware.
// see

// See middleware/Logger.zig for an example of how to write a middleware

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

var server = try httpz.Server(void).init(allocator, .{.port = PORT}, {});
defer server.deinit();

// creates an instance of the middleware with the given configuration
// see example/middleware/Logger.zig
const logger = try server.middleware(Logger, .{.query = true});

var router = server.router();

// Apply middleware to all routes created from this point on
router.middlewares = &.{logger};

router.get("/", index, .{});
router.get("/other", other, .{.middlewares = &.{}});

std.debug.print("listening http://localhost:{d}/\n", .{PORT});

// Starts the server, this is blocking.
try server.listen();
}

fn index(_: *httpz.Request, res: *httpz.Response) !void {
res.content_type = .HTML;
res.body =
\\<!DOCTYPE html>
\\<p>There's overlap between a custom dispatch function and middlewares.
\\<p>This page includes the example Logger middleware, so requesting it logs information.
\\<p>The <a href="/other">other</a> endpoint uses a custom route config which
\\ has no middleware, effectively disabling the Logger for that route.
;
}

fn other(_: *httpz.Request, res: *httpz.Response) !void {
// Called with a custom config which had no middlewares
// (effectively disabling the logging middleware)
res.body =
\\ Accessing this endpoint will NOT generate a log line in the console,
\\ because the Logger middleware is disabled.
;
}
87 changes: 87 additions & 0 deletions examples/07_advanced_routing.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
const std = @import("std");
const httpz = @import("httpz");

const Allocator = std.mem.Allocator;

const PORT = 8807;

// This example shows more advanced routing example, namely route groups
// and route configuration. (The previous example, with middleware, also showed
// per-route configuration for middleware specifically).

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

var default_handler = Handler{
.log = true,
};

var nolog_handler = Handler{
.log = false,
};

var server = try httpz.Server(*Handler).init(allocator, .{.port = PORT}, &default_handler);
defer server.deinit();

var router = server.router();

router.get("/", index, .{});

// We can define a dispatch function per-route. This will be used instead of Handler.dispatch
// But, sadly, every dispatch method must have the same signature (they must all accept the same type of action)
router.get("/page1", page1, .{.dispatcher = Handler.infoDispatch});

// We can define a handler instance per-route. This will be used instead of the
// handler instance passed to the init method above.
router.get("/page2", page2, .{.handler = &nolog_handler});

std.debug.print("listening http://localhost:{d}/\n", .{PORT});

// Starts the server, this is blocking.
try server.listen();
}

const Handler = struct {
log: bool,

// special dispatch set in the info route
pub fn infoDispatch(h: *Handler, action: httpz.Action(*Handler), req: *httpz.Request, res: *httpz.Response) !void {
return action(h, req, res);
}

pub fn dispatch(h: *Handler, action: httpz.Action(*Handler), req: *httpz.Request, res: *httpz.Response) !void {
try action(h, req, res);
if (h.log) {
std.debug.print("ts={d} path={s} status={d}\n", .{std.time.timestamp(), req.url.path, res.status});
}
}
};

fn index(_: *Handler, _: *httpz.Request, res: *httpz.Response) !void {
res.content_type = .HTML;
res.body =
\\<!DOCTYPE html>
\\ <p>It's possible to define a custom dispatch method, custom handler instance and/or custom middleware per-route.
\\ <p>It's also possible to create a route group, which is a group of routes who share a common prefix and/or a custom configration.
\\ <ul>
\\ <li><a href="/page1">page with custom dispatch</a>
\\ <li><a href="/page2">page with custom handler</a>
;
}

fn page1(_: *Handler, _: *httpz.Request, res: *httpz.Response) !void {
// Called with a custom config which specified a custom dispatch method
res.body =
\\ Accessing this endpoint will NOT generate a log line in the console,
\\ because a custom dispatch method is used
;
}

fn page2(_: *Handler, _: *httpz.Request, res: *httpz.Response) !void {
// Called with a custom config which specified a custom handler instance
res.body =
\\ Accessing this endpoint will NOT generate a log line in the console,
\\ because a custom handler instance is used
;
}
Loading

0 comments on commit 30195d6

Please sign in to comment.