handle library side of headers
This commit is contained in:
parent
289fb8d46c
commit
ce5fc4e99f
42
src/interface.zig
Normal file
42
src/interface.zig
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Header = extern struct {
|
||||||
|
name_ptr: [*]u8,
|
||||||
|
name_len: usize,
|
||||||
|
|
||||||
|
value_ptr: [*]u8,
|
||||||
|
value_len: usize,
|
||||||
|
};
|
||||||
|
pub const Response = extern struct {
|
||||||
|
ptr: [*]u8,
|
||||||
|
len: usize,
|
||||||
|
|
||||||
|
headers: [*]Header,
|
||||||
|
headers_len: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Request = extern struct {
|
||||||
|
method: [*]u8,
|
||||||
|
method_len: usize,
|
||||||
|
|
||||||
|
content: [*]u8,
|
||||||
|
content_len: usize,
|
||||||
|
|
||||||
|
headers: [*]Header,
|
||||||
|
headers_len: usize,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn toHeaders(allocator: std.mem.Allocator, headers: std.StringHashMap([]const u8)) ![*]Header {
|
||||||
|
var header_array = try std.ArrayList(Header).initCapacity(allocator, headers.count());
|
||||||
|
var iterator = headers.iterator();
|
||||||
|
while (iterator.next()) |kv| {
|
||||||
|
header_array.appendAssumeCapacity(.{
|
||||||
|
.name_ptr = @constCast(kv.key_ptr.*).ptr,
|
||||||
|
.name_len = kv.key_ptr.*.len,
|
||||||
|
|
||||||
|
.value_ptr = @constCast(kv.value_ptr.*).ptr,
|
||||||
|
.value_len = kv.value_ptr.*.len,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return header_array.items.ptr;
|
||||||
|
}
|
|
@ -1,40 +1,76 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const interface = @import("interface.zig");
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
const Response = extern struct {
|
const log = std.log.scoped(.@"main-lib");
|
||||||
ptr: [*]u8,
|
|
||||||
len: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
var child_allocator = std.heap.raw_c_allocator; // raw allocator recommended for use in arenas
|
var child_allocator = std.heap.raw_c_allocator; // raw allocator recommended for use in arenas
|
||||||
var arena: std.heap.ArenaAllocator = undefined;
|
var arena: std.heap.ArenaAllocator = undefined;
|
||||||
|
|
||||||
export fn handle_request() *Response {
|
const Response = struct {
|
||||||
|
body: *std.ArrayList(u8),
|
||||||
|
headers: *std.StringHashMap([]const u8),
|
||||||
|
};
|
||||||
|
|
||||||
|
export fn handle_request() ?*interface.Response {
|
||||||
arena = std.heap.ArenaAllocator.init(child_allocator);
|
arena = std.heap.ArenaAllocator.init(child_allocator);
|
||||||
var allocator = arena.allocator();
|
var allocator = arena.allocator();
|
||||||
var al = std.ArrayList(u8).init(allocator);
|
|
||||||
var writer = al.writer();
|
|
||||||
|
|
||||||
writer.print(" 2.", .{}) catch unreachable;
|
// setup response body
|
||||||
|
var response = std.ArrayList(u8).init(allocator);
|
||||||
|
|
||||||
// Cannot simply return &Blah. Need to assign to var first
|
// setup headers
|
||||||
var rc = &Response{
|
var headers = std.StringHashMap([]const u8).init(allocator);
|
||||||
.ptr = al.items.ptr,
|
handleRequest(.{
|
||||||
.len = al.items.len,
|
.body = &response,
|
||||||
|
.headers = &headers,
|
||||||
|
}) catch |e| {
|
||||||
|
log.err("Unexpected error processing request: {any}", .{e});
|
||||||
|
if (@errorReturnTrace()) |trace| {
|
||||||
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
// Marshall data back for handling by server
|
||||||
|
var rc = &interface.Response{
|
||||||
|
.ptr = response.items.ptr,
|
||||||
|
.len = response.items.len,
|
||||||
|
|
||||||
|
.headers = interface.toHeaders(allocator, headers) catch |e| {
|
||||||
|
log.err("Unexpected error processing request: {any}", .{e});
|
||||||
|
if (@errorReturnTrace()) |trace| {
|
||||||
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
.headers_len = headers.count(),
|
||||||
};
|
};
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// having request_deinit allows for a general deinit as well
|
/// having request_deinit allows for a general deinit as well
|
||||||
export fn request_deinit() void {
|
export fn request_deinit() void {
|
||||||
std.log.debug("deinit", .{});
|
|
||||||
arena.deinit();
|
arena.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn add(a: i32, b: i32) i32 {
|
// ************************************************************************
|
||||||
return a + b;
|
// Boilerplate ^^, Custom code below
|
||||||
|
// ************************************************************************
|
||||||
|
//
|
||||||
|
// handleRequest function here is the last line of boilerplate and the
|
||||||
|
// entry to a request
|
||||||
|
fn handleRequest(response: Response) !void {
|
||||||
|
// setup
|
||||||
|
var response_writer = response.body.writer();
|
||||||
|
// real work
|
||||||
|
response_writer.print(" 2.", .{}) catch unreachable;
|
||||||
|
try response.headers.put("X-custom-foo", "bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
test "basic add functionality" {
|
test "handle_request" {
|
||||||
try testing.expect(add(3, 7) == 10);
|
defer request_deinit();
|
||||||
|
child_allocator = std.testing.allocator;
|
||||||
|
const response = handle_request().?;
|
||||||
|
try testing.expectEqualStrings(" 2.", response.ptr[0..response.len]);
|
||||||
|
try testing.expectEqualStrings("X-custom-foo", response.headers[0].name_ptr[0..response.headers[0].name_len]);
|
||||||
|
try testing.expectEqualStrings("bar", response.headers[0].value_ptr[0..response.headers[0].value_len]);
|
||||||
}
|
}
|
||||||
|
|
75
src/main.zig
75
src/main.zig
|
@ -1,32 +1,34 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const interface = @import("interface.zig");
|
||||||
const Watch = @import("Watch.zig");
|
const Watch = @import("Watch.zig");
|
||||||
const serveFn = *const fn () *ServeReturn;
|
const serveFn = *const fn () ?*interface.Response;
|
||||||
const requestDeinitFn = *const fn () void;
|
const requestDeinitFn = *const fn () void;
|
||||||
|
|
||||||
const timeout = 250;
|
const timeout = 250;
|
||||||
|
|
||||||
const ServeReturn = extern struct {
|
|
||||||
ptr: [*]u8,
|
|
||||||
len: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
const FullReturn = struct {
|
const FullReturn = struct {
|
||||||
response: []u8,
|
response: []u8,
|
||||||
executor: *Executor,
|
executor: *Executor,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Executor = struct {
|
const Executor = struct {
|
||||||
|
// configuration
|
||||||
path: [:0]const u8,
|
path: [:0]const u8,
|
||||||
|
|
||||||
|
// fields used at runtime to do real work
|
||||||
library: ?*anyopaque = null,
|
library: ?*anyopaque = null,
|
||||||
serveFn: ?serveFn = null,
|
serveFn: ?serveFn = null,
|
||||||
requestDeinitFn: ?requestDeinitFn = null,
|
requestDeinitFn: ?requestDeinitFn = null,
|
||||||
|
|
||||||
|
// fields used for internal accounting
|
||||||
watch: ?usize = null,
|
watch: ?usize = null,
|
||||||
reload_lock: bool = false,
|
reload_lock: bool = false,
|
||||||
in_request_lock: bool = false,
|
in_request_lock: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: This should be in a file that maybe gets reloaded with SIGHUP
|
||||||
|
// rather than a file watch. Touching this config is pretty dangerous
|
||||||
var executors = [_]Executor{
|
var executors = [_]Executor{
|
||||||
.{ .path = "zig-out/lib/libfaas-proxy-sample-lib.so" },
|
.{ .path = "zig-out/lib/libfaas-proxy-sample-lib.so" },
|
||||||
.{ .path = "zig-out/lib/libfaas-proxy-sample-lib2.so" },
|
.{ .path = "zig-out/lib/libfaas-proxy-sample-lib2.so" },
|
||||||
|
@ -47,25 +49,16 @@ const SERVE_FN_NAME = "handle_request";
|
||||||
const PORT = 8069;
|
const PORT = 8069;
|
||||||
|
|
||||||
fn serve(allocator: std.mem.Allocator, response: *std.http.Server.Response) !*FullReturn {
|
fn serve(allocator: std.mem.Allocator, response: *std.http.Server.Response) !*FullReturn {
|
||||||
var null_server = std.http.Server.init(allocator, .{});
|
// pub const Request = extern struct {
|
||||||
defer null_server.deinit();
|
// method: [*]u8,
|
||||||
var data: [14]u8 = @constCast(&[_]u8{0x00} ** 14).*;
|
// method_len: usize,
|
||||||
var child_response = std.http.Server.Response{
|
//
|
||||||
.server = &null_server,
|
// content: [*]u8,
|
||||||
.request = response.request,
|
// content_len: usize,
|
||||||
.connection = .{
|
//
|
||||||
.conn = .{
|
// headers: [*]Header,
|
||||||
.stream = .{ .handle = 0 },
|
// headers_len: usize,
|
||||||
.protocol = .plain,
|
// };
|
||||||
},
|
|
||||||
},
|
|
||||||
.address = .{ .any = .{
|
|
||||||
.data = data,
|
|
||||||
.family = 0,
|
|
||||||
} },
|
|
||||||
.headers = response.headers,
|
|
||||||
};
|
|
||||||
_ = child_response;
|
|
||||||
// if (some path routing thing) {
|
// if (some path routing thing) {
|
||||||
// TODO: Get request body into executor
|
// TODO: Get request body into executor
|
||||||
// TODO: Get headers back from executor
|
// TODO: Get headers back from executor
|
||||||
|
@ -74,14 +67,27 @@ fn serve(allocator: std.mem.Allocator, response: *std.http.Server.Response) !*Fu
|
||||||
executor.in_request_lock = true;
|
executor.in_request_lock = true;
|
||||||
errdefer executor.in_request_lock = false;
|
errdefer executor.in_request_lock = false;
|
||||||
// Call external library
|
// Call external library
|
||||||
var serve_result = executor.serveFn.?();
|
var serve_result = executor.serveFn.?().?; // ok for this pointer deref to fail
|
||||||
|
|
||||||
// Deal with results
|
// Deal with results
|
||||||
|
// var content_type_added = false;
|
||||||
|
// for (0..serve_result.headers_len) |inx| {
|
||||||
|
// const head = serve_result.headers[inx];
|
||||||
|
// try response.headers.append(
|
||||||
|
// head.name_ptr[0..head.name_len],
|
||||||
|
// head.value_ptr[0..head.value_len],
|
||||||
|
// );
|
||||||
|
//
|
||||||
|
// // TODO: are these headers case insensitive?
|
||||||
|
// content_type_added = std.mem.eql(u8, head.name_ptr[0..head.name_len], "content-type");
|
||||||
|
// }
|
||||||
|
// if (!content_type_added)
|
||||||
|
// try response.headers.append("content-type", "text/plain");
|
||||||
|
_ = response;
|
||||||
var slice: []u8 = serve_result.ptr[0..serve_result.len];
|
var slice: []u8 = serve_result.ptr[0..serve_result.len];
|
||||||
var rc = &FullReturn{
|
var rc = try allocator.create(FullReturn);
|
||||||
.executor = executor,
|
rc.executor = executor;
|
||||||
.response = slice,
|
rc.response = slice;
|
||||||
};
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
fn getExecutor(key: usize) !*Executor {
|
fn getExecutor(key: usize) !*Executor {
|
||||||
|
@ -231,7 +237,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
try installSignalHandler();
|
try installSignalHandler();
|
||||||
while (true) {
|
while (true) {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|
||||||
processRequest(arena.allocator(), &server) catch |e| {
|
processRequest(arena.allocator(), &server) catch |e| {
|
||||||
|
@ -283,7 +289,6 @@ fn processRequest(allocator: std.mem.Allocator, server: *std.http.Server) !void
|
||||||
if (full_response) |f|
|
if (full_response) |f|
|
||||||
response_bytes = f.response;
|
response_bytes = f.response;
|
||||||
res.transfer_encoding = .{ .content_length = response_bytes.len };
|
res.transfer_encoding = .{ .content_length = response_bytes.len };
|
||||||
try res.headers.append("content-type", "text/plain");
|
|
||||||
try res.headers.append("connection", "close");
|
try res.headers.append("connection", "close");
|
||||||
if (builtin.is_test) writeToTestBuffers(response_bytes, res);
|
if (builtin.is_test) writeToTestBuffers(response_bytes, res);
|
||||||
try res.do();
|
try res.do();
|
||||||
|
@ -306,6 +311,8 @@ fn writeToTestBuffers(response: []const u8, res: *std.http.Server.Response) void
|
||||||
}
|
}
|
||||||
fn testRequest(request_bytes: []const u8) !void {
|
fn testRequest(request_bytes: []const u8) !void {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
|
||||||
var server = std.http.Server.init(allocator, .{ .reuse_address = true });
|
var server = std.http.Server.init(allocator, .{ .reuse_address = true });
|
||||||
defer server.deinit();
|
defer server.deinit();
|
||||||
|
@ -317,7 +324,7 @@ fn testRequest(request_bytes: []const u8) !void {
|
||||||
const server_thread = try std.Thread.spawn(
|
const server_thread = try std.Thread.spawn(
|
||||||
.{},
|
.{},
|
||||||
processRequest,
|
processRequest,
|
||||||
.{ allocator, &server },
|
.{ arena.allocator(), &server },
|
||||||
);
|
);
|
||||||
|
|
||||||
const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port);
|
const stream = try std.net.tcpConnectToHost(allocator, "127.0.0.1", server_port);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user