Skip to content

Commit

Permalink
http/server: handlers in async context
Browse files Browse the repository at this point in the history
  • Loading branch information
mookums committed Nov 7, 2024
1 parent 5fac596 commit 919164f
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 72 deletions.
4 changes: 2 additions & 2 deletions examples/http/basic/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn main() !void {
defer router.deinit();

try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
pub fn handler_fn(ctx: *http.Context) void {
const body =
\\ <!DOCTYPE html>
\\ <html>
Expand All @@ -25,7 +25,7 @@ pub fn main() !void {
\\ </html>
;

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body[0..],
Expand Down
10 changes: 5 additions & 5 deletions examples/http/benchmark/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ pub const std_options = .{
.log_level = .err,
};

fn hi_handler(_: http.Request, response: *http.Response, context: http.Context) void {
const name = context.captures[0].string;
fn hi_handler(ctx: *http.Context) void {
const name = ctx.captures[0].string;

const body = std.fmt.allocPrint(context.allocator,
const body = std.fmt.allocPrint(ctx.allocator,
\\ <!DOCTYPE html>
\\ <html>
\\ <body>
Expand All @@ -28,15 +28,15 @@ fn hi_handler(_: http.Request, response: *http.Response, context: http.Context)
\\ </body>
\\ </html>
, .{name}) catch {
response.set(.{
ctx.respond(.{
.status = .@"Internal Server Error",
.mime = http.Mime.HTML,
.body = "Out of Memory!",
});
return;
};

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body,
Expand Down
8 changes: 5 additions & 3 deletions examples/http/minram/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ pub fn main() !void {
const host: []const u8 = "0.0.0.0";
const port: u16 = 9862;

var gpa = std.heap.GeneralPurposeAllocator(.{ .enable_memory_limit = true }){ .requested_memory_limit = 1024 * 300 };
var gpa = std.heap.GeneralPurposeAllocator(
.{ .enable_memory_limit = true },
){ .requested_memory_limit = 1024 * 300 };
const allocator = gpa.allocator();
defer _ = gpa.deinit();

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

try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
pub fn handler_fn(ctx: *http.Context) void {
const body =
\\ <!DOCTYPE html>
\\ <html>
Expand All @@ -25,7 +27,7 @@ pub fn main() !void {
\\ </html>
;

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body[0..],
Expand Down
39 changes: 17 additions & 22 deletions examples/http/multithread/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ const zzz = @import("zzz");
const http = zzz.HTTP;
const log = std.log.scoped(.@"examples/multithread");

fn hi_handler(_: http.Request, response: *http.Response, context: http.Context) void {
const name = context.captures[0].string;
const greeting = context.queries.get("greeting") orelse "Hi";
fn hi_handler(ctx: *http.Context) void {
const name = ctx.captures[0].string;
const greeting = ctx.queries.get("greeting") orelse "Hi";

const body = std.fmt.allocPrint(context.allocator,
const body = std.fmt.allocPrint(ctx.allocator,
\\ <!DOCTYPE html>
\\ <html>
\\ <body>
Expand All @@ -25,43 +25,34 @@ fn hi_handler(_: http.Request, response: *http.Response, context: http.Context)
\\ </body>
\\ </html>
, .{ greeting, name }) catch {
response.set(.{
ctx.respond(.{
.status = .@"Internal Server Error",
.mime = http.Mime.HTML,
.body = "Out of Memory!",
});
return;
};

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body,
});
}

fn redir_handler(_: http.Request, response: *http.Response, context: http.Context) void {
_ = context;
response.set(.{
fn redir_handler(ctx: *http.Context) void {
ctx.response.headers.add("Location", "/hi/redirect") catch unreachable;
ctx.respond(.{
.status = .@"Permanent Redirect",
.mime = http.Mime.HTML,
.body = "",
});

response.headers.add("Location", "/hi/redirect") catch {
response.set(.{
.status = .@"Internal Server Error",
.mime = http.Mime.HTML,
.body = "Redirect Handler Failed",
});
return;
};
}

fn post_handler(request: http.Request, response: *http.Response, _: http.Context) void {
log.debug("Body: {s}", .{request.body});
fn post_handler(ctx: *http.Context) void {
log.debug("Body: {s}", .{ctx.request.body});

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = "",
Expand All @@ -73,7 +64,11 @@ pub fn main() !void {
const port: u16 = 9862;

// if multithreaded, you need a thread-safe allocator.
const allocator = std.heap.page_allocator;
var gpa = std.heap.GeneralPurposeAllocator(
.{ .thread_safe = true },
){};
const allocator = gpa.allocator();
defer _ = gpa.deinit();

var router = http.Router.init(allocator);
defer router.deinit();
Expand Down
14 changes: 9 additions & 5 deletions examples/http/tls/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ pub fn main() !void {
const host: []const u8 = "0.0.0.0";
const port: u16 = 9862;

const allocator = std.heap.c_allocator;
var gpa = std.heap.GeneralPurposeAllocator(
.{ .thread_safe = true },
){ .backing_allocator = std.heap.c_allocator };
const allocator = gpa.allocator();
defer _ = gpa.deinit();

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

try router.serve_embedded_file("/embed/pico.min.css", http.Mime.CSS, @embedFile("embed/pico.min.css"));

try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
pub fn handler_fn(ctx: *http.Context) void {
const body =
\\ <!DOCTYPE html>
\\ <html>
Expand All @@ -27,7 +31,7 @@ pub fn main() !void {
\\ </html>
;

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body[0..],
Expand All @@ -36,8 +40,8 @@ pub fn main() !void {
}.handler_fn));

try router.serve_route("/kill", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
response.set(.{
pub fn handler_fn(ctx: *http.Context) void {
ctx.respond(.{
.status = .Kill,
.mime = http.Mime.HTML,
.body = "",
Expand Down
12 changes: 7 additions & 5 deletions examples/http/valgrind/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ pub fn main() !void {
const host: []const u8 = "0.0.0.0";
const port: u16 = 9862;

const allocator = std.heap.c_allocator;
var gpa = std.heap.GeneralPurposeAllocator(.{}){ .backing_allocator = std.heap.c_allocator };
const allocator = gpa.allocator();
defer _ = gpa.deinit();

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

try router.serve_route("/", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
pub fn handler_fn(ctx: *http.Context) void {
const body =
\\ <!DOCTYPE html>
\\ <html>
Expand All @@ -23,7 +25,7 @@ pub fn main() !void {
\\ </html>
;

response.set(.{
ctx.respond(.{
.status = .OK,
.mime = http.Mime.HTML,
.body = body[0..],
Expand All @@ -32,8 +34,8 @@ pub fn main() !void {
}.handler_fn));

try router.serve_route("/kill", http.Route.init().get(struct {
pub fn handler_fn(_: http.Request, response: *http.Response, _: http.Context) void {
response.set(.{
pub fn handler_fn(ctx: *http.Context) void {
ctx.respond(.{
.status = .Kill,
.mime = http.Mime.HTML,
.body = "",
Expand Down
77 changes: 75 additions & 2 deletions src/core/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const Pool = @import("tardy").Pool;
pub const Threading = @import("tardy").TardyThreading;
pub const Runtime = @import("tardy").Runtime;
pub const Task = @import("tardy").Task;
const TaskFn = @import("tardy").TaskFn;
pub const AsyncIOType = @import("tardy").AsyncIOType;
const TardyCreator = @import("tardy").Tardy;
const Cross = @import("tardy").Cross;
Expand All @@ -22,9 +23,10 @@ pub const RecvStatus = union(enum) {
kill,
recv,
send: Pseudoslice,
spawned,
};

/// Security Model to use.
/// Security Model to use.chinp acas
///
/// Default: .plain (plaintext)
pub const Security = union(enum) {
Expand Down Expand Up @@ -94,6 +96,7 @@ pub const zzzConfig = struct {
fn RecvFn(comptime ProtocolData: type, comptime ProtocolConfig: type) type {
return *const fn (
rt: *Runtime,
trigger_task: TaskFn,
provision: *ZProvision(ProtocolData),
p_config: *const ProtocolConfig,
z_config: *const zzzConfig,
Expand Down Expand Up @@ -340,6 +343,75 @@ pub fn Server(
}
}

/// This is the task you MUST trigger if the `recv_fn` returns `.spawned`.
fn trigger_task(rt: *Runtime, _: *const Task, ctx: ?*anyopaque) !void {
const provision: *Provision = @ptrCast(@alignCast(ctx.?));

switch (provision.job) {
else => unreachable,
.recv => {
try rt.net.recv(.{
.socket = provision.socket,
.buffer = provision.buffer,
.func = recv_task,
.ctx = provision,
});
},
.send => |*send_job| {
const z_config: *const zzzConfig = @ptrCast(@alignCast(rt.storage.get("z_config").?));
const plain_buffer = send_job.slice.get(0, z_config.size_socket_buffer);

switch (comptime security) {
.tls => |_| {
const tls_slice: []TLSType = @as(
[*]TLSType,
@ptrCast(@alignCast(rt.storage.get("tls_slice").?)),
)[0..z_config.size_connections_max];

const tls_ptr: *?TLS = &tls_slice[provision.index];
assert(tls_ptr.* != null);

const encrypted_buffer = tls_ptr.*.?.encrypt(plain_buffer) catch |e| {
log.err("{d} - encrypt failed: {any}", .{ provision.index, e });
provision.job = .close;
try rt.net.close(.{
.fd = provision.socket,
.func = close_task,
.ctx = provision,
});
return error.TLSEncryptFailed;
};

send_job.count = plain_buffer.len;
send_job.security = .{
.tls = .{
.encrypted = encrypted_buffer,
.encrypted_count = 0,
},
};

try rt.net.send(.{
.socket = provision.socket,
.buffer = encrypted_buffer,
.func = send_task,
.ctx = provision,
});
},
.plain => {
send_job.security = .plain;

try rt.net.send(.{
.socket = provision.socket,
.buffer = plain_buffer,
.func = send_task,
.ctx = provision,
});
},
}
},
}
}

fn recv_task(rt: *Runtime, t: *const Task, ctx: ?*anyopaque) !void {
const provision: *Provision = @ptrCast(@alignCast(ctx.?));
assert(provision.job == .recv);
Expand Down Expand Up @@ -390,9 +462,10 @@ pub fn Server(
}
};

var status: RecvStatus = recv_fn(rt, provision, p_config, z_config, recv_buffer);
var status: RecvStatus = @call(.auto, recv_fn, .{ rt, trigger_task, provision, p_config, z_config, recv_buffer });

switch (status) {
.spawned => return,
.kill => {
rt.stop();
return error.Killed;
Expand Down
Loading

0 comments on commit 919164f

Please sign in to comment.