Skip to content

Commit

Permalink
experiment with new tardy Context style
Browse files Browse the repository at this point in the history
  • Loading branch information
mookums committed Oct 26, 2024
1 parent 47841c9 commit 1ba6282
Show file tree
Hide file tree
Showing 8 changed files with 1,106 additions and 1,139 deletions.
5 changes: 3 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
.minimum_zig_version = "0.13.0",
.dependencies = .{
.tardy = .{
.url = "git+https://github.com/mookums/tardy#12a2bcae25b34c4eb34ab5e3b5db101823a61cd6",
.hash = "122073200a2412251ad1e7eb322d9d04868a1444f98bdb4d47bb630491806c8d36d4",
//.url = "git+https://github.com/mookums/tardy#12a2bcae25b34c4eb34ab5e3b5db101823a61cd6",
//.hash = "122073200a2412251ad1e7eb322d9d04868a1444f98bdb4d47bb630491806c8d36d4",
.path = "../tardy",
},
.bearssl = .{
.url = "https://github.com/mookums/bearssl-zig/archive/37a96eee56fe2543579bbc6da148ca886f3dd32b.tar.gz",
Expand Down
11 changes: 8 additions & 3 deletions examples/http/basic/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ const zzz = @import("zzz");
const http = zzz.HTTP;
const log = std.log.scoped(.@"examples/basic");

const Server = http.Server(.plain, .auto);
const Router = Server.Router;
const Context = Server.Context;
const Route = Server.Route;

pub fn main() !void {
const host: []const u8 = "0.0.0.0";
const port: u16 = 9862;
Expand All @@ -11,11 +16,11 @@ pub fn main() !void {
const allocator = gpa.allocator();
defer _ = gpa.deinit();

var router = http.Router.init(allocator);
var router = Router.init(allocator);
defer router.deinit();

try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(ctx: *http.Context) void {
try router.serve_route("/", Route.init().get(struct {
pub fn handler_fn(ctx: *Context) void {
const body =
\\ <!DOCTYPE html>
\\ <html>
Expand Down
91 changes: 34 additions & 57 deletions src/http/context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ const log = std.log.scoped(.@"zzz/http/context");

const Capture = @import("routing_trie.zig").Capture;
const QueryMap = @import("routing_trie.zig").QueryMap;

const Provision = @import("provision.zig").Provision;

const Request = @import("request.zig").Request;
Expand All @@ -13,69 +12,47 @@ const ResponseSetOptions = Response.ResponseSetOptions;

const Runtime = @import("tardy").Runtime;
const Task = @import("tardy").Task;
// Needed here to prevent a dependency loop.
const TaskFn = *const fn (*Runtime, *const Task, ?*anyopaque) anyerror!void;

const raw_respond = @import("server.zig").raw_respond;

pub const Context = struct {
allocator: std.mem.Allocator,
trigger: TaskFn,
runtime: *Runtime,
/// The Request that triggered this handler.
request: *const Request,
/// The Response that will be returned.
/// To actually trigger the send, use `Context.respond`.
response: *Response,
path: []const u8,
captures: []Capture,
queries: *QueryMap,
provision: *Provision,
triggered: bool = false,

pub fn init(
// Context is dependent on the server that gets created.
// This is because the trigger_task ends up being dependent.
pub fn Context(comptime Server: type) type {
return struct {
const Self = @This();
allocator: std.mem.Allocator,
trigger: TaskFn,
runtime: *Runtime,
ctx: *Provision,
/// The Request that triggered this handler.
request: *const Request,
/// The Response that will be returned.
response: *Response,
path: []const u8,
captures: []Capture,
queries: *QueryMap,
) Context {
return Context{
.allocator = allocator,
.trigger = trigger,
.runtime = runtime,
.provision = ctx,
.request = request,
.response = response,
.path = path,
.captures = captures,
.queries = queries,
};
}

pub fn respond(self: *Context, options: ResponseSetOptions) void {
assert(!self.triggered);
self.triggered = true;
self.response.set(options);

// this will write the data into the appropriate places.
const status = raw_respond(self.provision) catch unreachable;

self.provision.job = .{
.send = .{
.count = 0,
.slice = status.send,
.security = undefined,
},
};

self.runtime.spawn(.{
.func = self.trigger,
.ctx = self.provision,
}) catch unreachable;
}
};
provision: *Provision,
triggered: bool = false,

pub fn respond(self: *Self, options: ResponseSetOptions) void {
assert(!self.triggered);
self.triggered = true;
self.response.set(options);

// this will write the data into the appropriate places.
const status = raw_respond(self.provision) catch unreachable;

self.provision.job = .{
.send = .{
.count = 0,
.slice = status.send,
.security = undefined,
},
};

self.runtime.spawn(
*Provision,
Server.trigger_task,
self.provision,
) catch unreachable;
}
};
}
4 changes: 0 additions & 4 deletions src/http/lib.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ pub const Request = @import("request.zig").Request;
pub const Response = @import("response.zig").Response;
pub const Mime = @import("mime.zig").Mime;
pub const Date = @import("date.zig").Date;
pub const Route = @import("route.zig").Route;
pub const Router = @import("router.zig").Router;
pub const RouteHandlerFn = @import("route.zig").RouteHandlerFn;
pub const Context = @import("context.zig").Context;
pub const Headers = @import("headers.zig").Headers;

pub const Server = @import("server.zig").Server;
Expand Down
208 changes: 105 additions & 103 deletions src/http/route.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,112 +6,114 @@ const Response = @import("response.zig").Response;

const Context = @import("context.zig").Context;

pub const RouteHandlerFn = *const fn (context: *Context) void;

pub const Route = struct {
handlers: [9]?RouteHandlerFn = [_]?RouteHandlerFn{null} ** 9,

fn method_to_index(method: Method) u32 {
return switch (method) {
.GET => 0,
.HEAD => 1,
.POST => 2,
.PUT => 3,
.DELETE => 4,
.CONNECT => 5,
.OPTIONS => 6,
.TRACE => 7,
.PATCH => 8,
};
}

pub fn init() Route {
return Route{ .handlers = [_]?RouteHandlerFn{null} ** 9 };
}

/// Returns a comma delinated list of allowed Methods for this route. This
/// is meant to be used as the value for the 'Allow' header in the Response.
pub fn get_allowed(self: Route, allocator: std.mem.Allocator) ![]const u8 {
// This gets allocated within the context of the connection's arena.
const allowed_size = comptime blk: {
var size = 0;
for (std.meta.tags(Method)) |method| {
size += @tagName(method).len + 1;
}
break :blk size;
};
pub fn Route(comptime Server: type) type {
return struct {
const Self = @This();
pub const HandlerFn = *const fn (context: *Context(Server)) void;
handlers: [9]?HandlerFn = [_]?HandlerFn{null} ** 9,

fn method_to_index(method: Method) u32 {
return switch (method) {
.GET => 0,
.HEAD => 1,
.POST => 2,
.PUT => 3,
.DELETE => 4,
.CONNECT => 5,
.OPTIONS => 6,
.TRACE => 7,
.PATCH => 8,
};
}

pub fn init() Self {
return Self{ .handlers = [_]?HandlerFn{null} ** 9 };
}

const buffer = try allocator.alloc(u8, allowed_size);
/// Returns a comma delinated list of allowed Methods for this route. This
/// is meant to be used as the value for the 'Allow' header in the Response.
pub fn get_allowed(self: Self, allocator: std.mem.Allocator) ![]const u8 {
// This gets allocated within the context of the connection's arena.
const allowed_size = comptime blk: {
var size = 0;
for (std.meta.tags(Method)) |method| {
size += @tagName(method).len + 1;
}
break :blk size;
};

const buffer = try allocator.alloc(u8, allowed_size);

var current: []u8 = "";
inline for (std.meta.tags(Method)) |method| {
if (self.handlers[@intFromEnum(method)] != null) {
current = std.fmt.bufPrint(buffer, "{s},{s}", .{ @tagName(method), current }) catch unreachable;
}
}

var current: []u8 = "";
inline for (std.meta.tags(Method)) |method| {
if (self.handlers[@intFromEnum(method)] != null) {
current = std.fmt.bufPrint(buffer, "{s},{s}", .{ @tagName(method), current }) catch unreachable;
if (current.len == 0) {
return current;
} else {
return current[0 .. current.len - 1];
}
}

if (current.len == 0) {
return current;
} else {
return current[0 .. current.len - 1];
pub fn get_handler(self: Self, method: Method) ?HandlerFn {
return self.handlers[method_to_index(method)];
}

pub fn get(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.GET)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn head(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.HEAD)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn post(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.POST)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn put(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.PUT)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn delete(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.DELETE)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn connect(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.CONNECT)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn options(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.OPTIONS)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn trace(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.TRACE)] = handler_fn;
return Self{ .handlers = new_handlers };
}

pub fn patch(self: Self, handler_fn: HandlerFn) Self {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.PATCH)] = handler_fn;
return Self{ .handlers = new_handlers };
}
}

pub fn get_handler(self: Route, method: Method) ?RouteHandlerFn {
return self.handlers[method_to_index(method)];
}

pub fn get(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.GET)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn head(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.HEAD)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn post(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.POST)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn put(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.PUT)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn delete(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.DELETE)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn connect(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.CONNECT)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn options(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.OPTIONS)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn trace(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.TRACE)] = handler_fn;
return Route{ .handlers = new_handlers };
}

pub fn patch(self: Route, handler_fn: RouteHandlerFn) Route {
var new_handlers = self.handlers;
new_handlers[comptime method_to_index(.PATCH)] = handler_fn;
return Route{ .handlers = new_handlers };
}
};
};
}
Loading

0 comments on commit 1ba6282

Please sign in to comment.