allow handler more control in status reporting
All checks were successful
AWS-Zig Build / build-zig-0.11.0-amd64-host (push) Successful in 1m46s
All checks were successful
AWS-Zig Build / build-zig-0.11.0-amd64-host (push) Successful in 1m46s
This commit is contained in:
parent
d8b5366515
commit
47e4b0d54c
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.flexilib = .{
|
.flexilib = .{
|
||||||
.url = "https://git.lerch.org/lobo/flexilib/archive/c44ad2ba84df735421bef23a2ad612968fb50f06.tar.gz",
|
.url = "https://git.lerch.org/lobo/flexilib/archive/3d3dab9c792651477932e2b61c9f4794ac694dcb.tar.gz",
|
||||||
.hash = "122051fdfeefdd75653d3dd678c8aa297150c2893f5fad0728e0d953481383690dbc",
|
.hash = "1220fd7a614fe3c9f6006b630bba528e2ec9dca9c66f5ff10f7e471ad2bdd41b6c89",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,19 +84,7 @@ pub const Headers = struct {
|
||||||
pub fn allHeaders(allocator: std.mem.Allocator, context: universal_lambda.Context) !Headers {
|
pub fn allHeaders(allocator: std.mem.Allocator, context: universal_lambda.Context) !Headers {
|
||||||
switch (context) {
|
switch (context) {
|
||||||
.web_request => |res| return Headers.init(allocator, &res.request.headers, false),
|
.web_request => |res| return Headers.init(allocator, &res.request.headers, false),
|
||||||
.flexilib => |ctx| {
|
.flexilib => |ctx| return Headers.init(allocator, &ctx.request.headers, false),
|
||||||
var headers = try allocator.create(std.http.Headers);
|
|
||||||
errdefer allocator.destroy(headers);
|
|
||||||
headers.allocator = allocator;
|
|
||||||
headers.list = .{};
|
|
||||||
headers.index = .{};
|
|
||||||
headers.owned = true;
|
|
||||||
errdefer headers.deinit();
|
|
||||||
for (ctx.request.headers) |hdr| {
|
|
||||||
try headers.append(hdr.name_ptr[0..hdr.name_len], hdr.value_ptr[0..hdr.value_len]);
|
|
||||||
}
|
|
||||||
return Headers.init(allocator, headers, true);
|
|
||||||
},
|
|
||||||
.none => return headersWithoutContext(allocator),
|
.none => return headersWithoutContext(allocator),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,8 +152,9 @@ test "can get headers" {
|
||||||
// leaks. There doesn't seem to be a way to ignore leak detection
|
// leaks. There doesn't seem to be a way to ignore leak detection
|
||||||
if (@import("builtin").os.tag == .wasi) return error.SkipZigTest;
|
if (@import("builtin").os.tag == .wasi) return error.SkipZigTest;
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
var response = universal_lambda.Response.init(allocator);
|
||||||
const context = universal_lambda.Context{
|
const context = universal_lambda.Context{
|
||||||
.none = {},
|
.none = &response,
|
||||||
};
|
};
|
||||||
var headers = try allHeaders(allocator, context);
|
var headers = try allHeaders(allocator, context);
|
||||||
defer headers.deinit();
|
defer headers.deinit();
|
||||||
|
|
|
@ -3,6 +3,7 @@ const builtin = @import("builtin");
|
||||||
|
|
||||||
const HandlerFn = @import("universal_lambda.zig").HandlerFn;
|
const HandlerFn = @import("universal_lambda.zig").HandlerFn;
|
||||||
const Context = @import("universal_lambda.zig").Context;
|
const Context = @import("universal_lambda.zig").Context;
|
||||||
|
const UniversalLambdaResponse = @import("universal_lambda.zig").Response;
|
||||||
|
|
||||||
const log = std.log.scoped(.lambda);
|
const log = std.log.scoped(.lambda);
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ pub fn deinit() void {
|
||||||
/// If an allocator is not provided, an approrpriate allocator will be selected and used
|
/// 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,
|
/// This function is intended to loop infinitely. If not used in this manner,
|
||||||
/// make sure to call the deinit() function
|
/// make sure to call the deinit() function
|
||||||
pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void { // TODO: remove inferred error set?
|
pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !u8 { // TODO: remove inferred error set?
|
||||||
const lambda_runtime_uri = std.os.getenv("AWS_LAMBDA_RUNTIME_API") orelse test_lambda_runtime_uri.?;
|
const lambda_runtime_uri = std.os.getenv("AWS_LAMBDA_RUNTIME_API") orelse test_lambda_runtime_uri.?;
|
||||||
// TODO: If this is null, go into single use command line mode
|
// TODO: If this is null, go into single use command line mode
|
||||||
|
|
||||||
|
@ -70,15 +71,20 @@ pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void { // T
|
||||||
// Lambda does not have context, just environment variables. API Gateway
|
// Lambda does not have context, just environment variables. API Gateway
|
||||||
// might be configured to pass in lots of context, but this comes through
|
// might be configured to pass in lots of context, but this comes through
|
||||||
// event data, not context.
|
// event data, not context.
|
||||||
const event_response = event_handler(req_allocator, event.event_data, .{ .none = {} }) catch |err| {
|
var response = UniversalLambdaResponse.init(allocator.?);
|
||||||
|
response.output_file = std.io.getStdOut();
|
||||||
|
const event_response = event_handler(req_allocator, event.event_data, .{ .none = &response }) catch |err| {
|
||||||
|
response.finish() catch unreachable;
|
||||||
event.reportError(@errorReturnTrace(), err, lambda_runtime_uri) catch unreachable;
|
event.reportError(@errorReturnTrace(), err, lambda_runtime_uri) catch unreachable;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
response.finish() catch unreachable;
|
||||||
event.postResponse(lambda_runtime_uri, event_response) catch |err| {
|
event.postResponse(lambda_runtime_uri, event_response) catch |err| {
|
||||||
event.reportError(@errorReturnTrace(), err, lambda_runtime_uri) catch unreachable;
|
event.reportError(@errorReturnTrace(), err, lambda_runtime_uri) catch unreachable;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Event = struct {
|
const Event = struct {
|
||||||
|
@ -409,10 +415,13 @@ fn handler(allocator: std.mem.Allocator, event_data: []const u8, context: Contex
|
||||||
_ = context;
|
_ = context;
|
||||||
return event_data;
|
return event_data;
|
||||||
}
|
}
|
||||||
|
fn thread_run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void {
|
||||||
|
_ = try run(allocator, event_handler);
|
||||||
|
}
|
||||||
fn test_run(allocator: std.mem.Allocator, event_handler: HandlerFn) !std.Thread {
|
fn test_run(allocator: std.mem.Allocator, event_handler: HandlerFn) !std.Thread {
|
||||||
return try std.Thread.spawn(
|
return try std.Thread.spawn(
|
||||||
.{},
|
.{},
|
||||||
run,
|
thread_run,
|
||||||
.{ allocator, event_handler },
|
.{ allocator, event_handler },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,56 @@ pub const HandlerFn = *const fn (std.mem.Allocator, []const u8, Context) anyerro
|
||||||
|
|
||||||
const log = std.log.scoped(.universal_lambda);
|
const log = std.log.scoped(.universal_lambda);
|
||||||
|
|
||||||
const FakeResponse = struct {
|
pub const Response = struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
headers: std.http.Headers,
|
||||||
|
output_file: ?std.fs.File = null,
|
||||||
|
status: std.http.Status = .ok,
|
||||||
|
reason: ?[]const u8 = null,
|
||||||
request: struct {
|
request: struct {
|
||||||
target: []const u8,
|
target: []const u8,
|
||||||
headers: std.http.Headers,
|
headers: std.http.Headers,
|
||||||
},
|
},
|
||||||
|
al: std.ArrayList(u8),
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) Response {
|
||||||
|
return .{
|
||||||
|
.allocator = allocator,
|
||||||
|
.headers = .{ .allocator = allocator },
|
||||||
|
.request = .{
|
||||||
|
.target = "/",
|
||||||
|
.headers = .{ .allocator = allocator },
|
||||||
|
},
|
||||||
|
.al = std.ArrayList(u8).init(allocator),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
pub fn write(res: *Response, bytes: []const u8) !usize {
|
||||||
|
return res.al.writer().write(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writeAll(res: *Response, bytes: []const u8) !void {
|
||||||
|
return res.al.writer().writeAll(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn writer(res: *Response) std.io.Writer {
|
||||||
|
return res.al.writer().writer();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(res: *Response) !void {
|
||||||
|
if (res.output_file) |f| {
|
||||||
|
try f.writer().writeAll(res.al.items);
|
||||||
|
}
|
||||||
|
res.al.deinit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const Context = union(enum) {
|
pub const Context = union(enum) {
|
||||||
web_request: switch (build_options.build_type) {
|
web_request: switch (build_options.build_type) {
|
||||||
.exe_run, .cloudflare => *FakeResponse,
|
.exe_run, .cloudflare => *Response,
|
||||||
else => *std.http.Server.Response,
|
else => *std.http.Server.Response,
|
||||||
},
|
},
|
||||||
flexilib: struct {
|
flexilib: *flexilib.ZigResponse,
|
||||||
request: flexilib.ZigRequest,
|
none: *Response,
|
||||||
response: flexilib.ZigResponse,
|
|
||||||
},
|
|
||||||
none: void,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const runFn = blk: {
|
const runFn = blk: {
|
||||||
|
@ -42,11 +76,11 @@ fn deinit() void {
|
||||||
/// If an allocator is not provided, an approrpriate allocator will be selected and used
|
/// 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,
|
/// This function is intended to loop infinitely. If not used in this manner,
|
||||||
/// make sure to call the deinit() function
|
/// make sure to call the deinit() function
|
||||||
pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void { // TODO: remove inferred error set?
|
pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !u8 { // TODO: remove inferred error set?
|
||||||
try runFn(allocator, event_handler);
|
return try runFn(allocator, event_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runExe(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void {
|
fn runExe(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !u8 {
|
||||||
var arena = std.heap.ArenaAllocator.init(allocator orelse std.heap.page_allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator orelse std.heap.page_allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|
||||||
|
@ -56,14 +90,30 @@ fn runExe(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void {
|
||||||
// We're setting up an arena allocator. While we could use a gpa and get
|
// We're setting up an arena allocator. While we could use a gpa and get
|
||||||
// some additional safety, this is now "production" runtime, and those
|
// some additional safety, this is now "production" runtime, and those
|
||||||
// things are better handled by unit tests
|
// things are better handled by unit tests
|
||||||
const writer = std.io.getStdOut().writer();
|
var response = Response.init(aa);
|
||||||
try writer.writeAll(try event_handler(aa, data, .{ .none = {} }));
|
|
||||||
|
// Note here we are throwing out the status and reason. This is to make
|
||||||
|
// the console experience less "webby" and more "consoly", at the potential
|
||||||
|
// cost of data loss for not outputting the http status/reason
|
||||||
|
const output = event_handler(aa, data, .{ .none = &response }) catch |err| {
|
||||||
|
response.output_file = std.io.getStdErr();
|
||||||
|
try response.finish();
|
||||||
|
return err;
|
||||||
|
};
|
||||||
|
|
||||||
|
response.output_file = if (response.status.class() == .success) std.io.getStdOut() else std.io.getStdErr();
|
||||||
|
const writer = response.output_file.?.writer();
|
||||||
|
try response.finish();
|
||||||
|
try writer.writeAll(output);
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
|
// We might have gotten an error message managed directly by the event handler
|
||||||
|
// If that's the case, we will need to report back an error code
|
||||||
|
return if (response.status.class() == .success) 0 else 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Will create a web server and marshall all requests back to our 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
|
/// To keep things simple, we'll have this on a single thread, at least for now
|
||||||
fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void {
|
fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !u8 {
|
||||||
const alloc = allocator orelse std.heap.page_allocator;
|
const alloc = allocator orelse std.heap.page_allocator;
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(alloc);
|
var arena = std.heap.ArenaAllocator.init(alloc);
|
||||||
|
@ -98,6 +148,7 @@ fn runStandaloneServer(allocator: ?std.mem.Allocator, event_handler: HandlerFn)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn processRequest(aa: std.mem.Allocator, server: *std.http.Server, event_handler: HandlerFn) !void {
|
fn processRequest(aa: std.mem.Allocator, server: *std.http.Server, event_handler: HandlerFn) !void {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user