Emil Lerch
dcf00d8460
All checks were successful
AWS-Zig Build / build-zig-0.11.0-amd64-host (push) Successful in 1m50s
This commit is a significant refactor that fixes a number of things. 1. Replaces the optional helpers import (which was always weird) with a mandatory interface import on behalf of the application. This is actually a good thing as it enables all things below. 2. Removes the severely awkward union that was the lambda context. Now, no matter how your handler runs, a single object with everything you need is fully populated and (nearly always) works as you would expect. There is a slight exception to this with AWS Lambda that is related to the service itself. It is also possible that not everything is passed in correctly for Cloudflare, which, if true, will be addressed later. 3. Allows writes to the context object. These will be added to the output, but is implementation dependent, and I'm not 100% sure I've got it right yet, but the infrastructure is there. 4. Allows proper tests throughout this project. 5. Allows proper tests in the application too. 6. Removes the need for the handler to be public under flexlib. Flexilib handler registration now works just like everything else. Note, however, that flexilib is unique in that your handler registration function will return before the program ends. If this is important for resource cleanup, @import("build_options").build_type is your friend. 7. Request method can now be passed into console applications using -m or --method
119 lines
4.7 KiB
Zig
119 lines
4.7 KiB
Zig
const std = @import("std");
|
|
const build_options = @import("build_options");
|
|
const flexilib = @import("flexilib-interface");
|
|
const interface = @import("universal_lambda_interface");
|
|
|
|
const log = std.log.scoped(.universal_lambda);
|
|
|
|
const runFn = blk: {
|
|
switch (build_options.build_type) {
|
|
.awslambda => break :blk @import("lambda.zig").run,
|
|
.standalone_server => break :blk runStandaloneServer,
|
|
.flexilib => break :blk @import("flexilib.zig").run,
|
|
.exe_run, .cloudflare => break :blk @import("console.zig").run,
|
|
}
|
|
};
|
|
|
|
/// Starts the universal lambda framework. Handler will be called when an event is processing.
|
|
/// Depending on the serverless system used, from a practical sense, this may not return.
|
|
///
|
|
/// If an allocator is not provided, an approrpriate allocator will be selected and used
|
|
/// This function is intended to loop infinitely. If not used in this manner,
|
|
/// make sure to call the deinit() function
|
|
pub fn run(allocator: ?std.mem.Allocator, event_handler: interface.HandlerFn) !u8 { // TODO: remove inferred error set?
|
|
return try runFn(allocator, event_handler);
|
|
}
|
|
|
|
/// Will create a web server and marshall all requests back to our event handler
|
|
/// To keep things simple, we'll have this on a single thread, at least for now
|
|
fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: interface.HandlerFn) !u8 {
|
|
const alloc = allocator orelse std.heap.page_allocator;
|
|
|
|
var arena = std.heap.ArenaAllocator.init(alloc);
|
|
defer arena.deinit();
|
|
|
|
var aa = arena.allocator();
|
|
var server = std.http.Server.init(aa, .{ .reuse_address = true });
|
|
defer server.deinit();
|
|
const address = try std.net.Address.parseIp("127.0.0.1", 8080); // TODO: allow config
|
|
try server.listen(address);
|
|
const server_port = server.socket.listen_address.in.getPort();
|
|
var uri: ["http://127.0.0.1:99999".len]u8 = undefined;
|
|
_ = try std.fmt.bufPrint(&uri, "http://127.0.0.1:{d}", .{server_port});
|
|
log.info("server listening at {s}", .{uri});
|
|
|
|
// No threads, maybe later
|
|
//log.info("starting server thread, tid {d}", .{std.Thread.getCurrentId()});
|
|
while (true) {
|
|
defer {
|
|
if (!arena.reset(.{ .retain_with_limit = 1024 * 1024 })) {
|
|
// reallocation failed, arena is degraded
|
|
log.warn("Arena reset failed and is degraded. Resetting arena", .{});
|
|
arena.deinit();
|
|
arena = std.heap.ArenaAllocator.init(alloc);
|
|
aa = arena.allocator();
|
|
}
|
|
}
|
|
processRequest(aa, &server, event_handler) catch |e| {
|
|
log.err("Unexpected error processing request: {any}", .{e});
|
|
if (@errorReturnTrace()) |trace| {
|
|
std.debug.dumpStackTrace(trace.*);
|
|
}
|
|
};
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
fn processRequest(aa: std.mem.Allocator, server: *std.http.Server, event_handler: interface.HandlerFn) !void {
|
|
var res = try server.accept(.{ .allocator = aa });
|
|
defer {
|
|
_ = res.reset();
|
|
if (res.headers.owned and res.headers.list.items.len > 0) res.headers.deinit();
|
|
res.deinit();
|
|
}
|
|
try res.wait(); // wait for client to send a complete request head
|
|
|
|
const errstr = "Internal Server Error\n";
|
|
var errbuf: [errstr.len]u8 = undefined;
|
|
@memcpy(&errbuf, errstr);
|
|
var response_bytes: []const u8 = errbuf[0..];
|
|
|
|
var body =
|
|
if (res.request.content_length) |l|
|
|
try res.reader().readAllAlloc(aa, @as(usize, l))
|
|
else
|
|
try aa.dupe(u8, "");
|
|
// no need to free - will be handled by arena
|
|
|
|
response_bytes = event_handler(aa, body, .{ .web_request = &res }) catch |e| brk: {
|
|
if (res.status.class() == .success) res.status = .internal_server_error;
|
|
// TODO: more about this particular request
|
|
log.err("Unexpected error from executor processing request: {any}", .{e});
|
|
if (@errorReturnTrace()) |trace| {
|
|
std.debug.dumpStackTrace(trace.*);
|
|
}
|
|
break :brk "Unexpected error generating request to lambda";
|
|
};
|
|
res.transfer_encoding = .{ .content_length = response_bytes.len };
|
|
|
|
try res.do();
|
|
_ = try res.writer().writeAll(response_bytes);
|
|
try res.finish();
|
|
}
|
|
test {
|
|
std.testing.refAllDecls(@This());
|
|
// if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
|
if (@import("builtin").os.tag != .wasi) {
|
|
// these use http
|
|
std.testing.refAllDecls(@import("lambda.zig"));
|
|
std.testing.refAllDecls(@import("cloudflaredeploy.zig"));
|
|
std.testing.refAllDecls(@import("CloudflareDeployStep.zig"));
|
|
}
|
|
std.testing.refAllDecls(@import("console.zig"));
|
|
std.testing.refAllDecls(@import("flexilib.zig"));
|
|
|
|
// The following do not currently have tests
|
|
|
|
// TODO: Do we want build files here too?
|
|
}
|