Compare commits

..

No commits in common. "3d3dab9c792651477932e2b61c9f4794ac694dcb" and "c44ad2ba84df735421bef23a2ad612968fb50f06" have entirely different histories.

3 changed files with 48 additions and 205 deletions

View File

@ -14,11 +14,6 @@ pub const Response = extern struct {
headers: [*]Header, headers: [*]Header,
headers_len: usize, headers_len: usize,
status: usize,
reason_ptr: [*]u8,
reason_len: usize,
}; };
pub const Request = extern struct { pub const Request = extern struct {
@ -44,7 +39,7 @@ pub const ZigRequest = struct {
target: []const u8, target: []const u8,
method: [:0]u8, method: [:0]u8,
content: []u8, content: []u8,
headers: std.http.Headers, headers: []Header,
}; };
pub const ZigHeader = struct { pub const ZigHeader = struct {
@ -53,33 +48,11 @@ pub const ZigHeader = struct {
}; };
pub const ZigResponse = struct { pub const ZigResponse = struct {
status: std.http.Status = .ok,
reason: ?[]const u8 = null,
body: *std.ArrayList(u8), body: *std.ArrayList(u8),
headers: std.http.Headers, headers: *std.StringHashMap([]const u8),
request: ZigRequest,
prepend: std.ArrayList(u8),
pub fn write(res: *ZigResponse, bytes: []const u8) !usize {
return res.prepend.writer().write(bytes);
}
pub fn writeAll(res: *ZigResponse, bytes: []const u8) !void {
return res.prepend.writer().writeAll(bytes);
}
pub fn writer(res: *ZigResponse) std.io.Writer {
return res.prepend.writer().writer();
}
pub fn finish(res: *ZigResponse) !void {
if (res.prepend.items.len > 0)
try res.body.insertSlice(0, res.prepend.items);
res.prepend.deinit();
}
}; };
pub const ZigRequestHandler = *const fn (std.mem.Allocator, *ZigResponse) anyerror!void; pub const ZigRequestHandler = *const fn (std.mem.Allocator, ZigRequest, ZigResponse) anyerror!void;
/// This function is optional and can be exported by zig libraries for /// This function is optional and can be exported by zig libraries for
/// initialization. If exported, it will be called once in the beginning of /// initialization. If exported, it will be called once in the beginning of
@ -91,18 +64,26 @@ pub fn zigInit(parent_allocator: *anyopaque) callconv(.C) void {
allocator = @ptrCast(@alignCast(parent_allocator)); allocator = @ptrCast(@alignCast(parent_allocator));
} }
pub fn toZigHeader(header: Header) ZigHeader {
return .{
.name = header.name_ptr[0..header.name_len],
.value = header.value_ptr[0..header.value_len],
};
}
/// Converts a StringHashMap to the structure necessary for passing through the /// Converts a StringHashMap to the structure necessary for passing through the
/// C boundary. This will be called automatically for you via the handleRequest function /// C boundary. This will be called automatically for you via the handleRequest function
/// and is also used by the main processing loop to coerce request headers /// and is also used by the main processing loop to coerce request headers
fn toHeaders(alloc: std.mem.Allocator, headers: std.http.Headers) ![*]Header { fn toHeaders(alloc: std.mem.Allocator, headers: std.StringHashMap([]const u8)) ![*]Header {
var header_array = try std.ArrayList(Header).initCapacity(alloc, headers.list.items.len); var header_array = try std.ArrayList(Header).initCapacity(alloc, headers.count());
for (headers.list.items) |*field| { var iterator = headers.iterator();
while (iterator.next()) |kv| {
header_array.appendAssumeCapacity(.{ header_array.appendAssumeCapacity(.{
.name_ptr = @constCast(field.name.ptr), .name_ptr = @constCast(kv.key_ptr.*).ptr,
.name_len = field.name.len, .name_len = kv.key_ptr.*.len,
.value_ptr = @constCast(field.value.ptr), .value_ptr = @constCast(kv.value_ptr.*).ptr,
.value_len = field.value.len, .value_len = kv.value_ptr.*.len,
}); });
} }
return header_array.items.ptr; return header_array.items.ptr;
@ -123,73 +104,42 @@ pub fn handleRequest(request: *Request, zigRequestHandler: ZigRequestHandler) ?*
var response = std.ArrayList(u8).init(alloc); var response = std.ArrayList(u8).init(alloc);
// setup headers // setup headers
var request_headers = std.http.Headers.init(alloc); var headers = std.StringHashMap([]const u8).init(alloc);
for (0..request.headers_len) |i|
request_headers.append(
request.headers[i].name_ptr[0..request.headers[i].name_len],
request.headers[i].value_ptr[0..request.headers[i].value_len],
) catch |e| {
log.err("Unexpected error processing request: {any}", .{e});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
return null;
};
var prepend = std.ArrayList(u8).init(alloc);
var zig_response = ZigResponse{
.headers = .{ .allocator = alloc },
.body = &response,
.prepend = prepend,
.request = .{
.content = request.content[0..request.content_len],
.target = request.target[0..request.target_len],
.method = request.method[0..request.method_len :0],
.headers = request_headers,
},
};
zigRequestHandler( zigRequestHandler(
alloc, alloc,
&zig_response, .{
.target = request.target[0..request.target_len],
.method = request.method[0..request.method_len :0],
.content = request.content[0..request.content_len],
.headers = request.headers[0..request.headers_len],
},
.{
.body = &response,
.headers = &headers,
},
) catch |e| { ) catch |e| {
log.err("Unexpected error processing request: {any}", .{e}); log.err("Unexpected error processing request: {any}", .{e});
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*); std.debug.dumpStackTrace(trace.*);
} }
if (zig_response.status == .ok) // this was an unexpected throw return null;
zig_response.status = .internal_server_error;
return buildResponse(alloc, &zig_response);
}; };
// Marshall data back for handling by server // Marshall data back for handling by server
return buildResponse(alloc, &zig_response);
}
fn buildResponse(alloc: std.mem.Allocator, zig_response: *ZigResponse) ?*Response {
var rc = alloc.create(Response) catch { var rc = alloc.create(Response) catch {
log.err("Could not allocate memory for response object. This may be fatal", .{}); log.err("Could not allocate memory for response object. This may be fatal", .{});
return null; return null;
}; };
zig_response.finish() catch { rc.ptr = response.items.ptr;
log.err("Could not allocate memory for response object. This may be fatal", .{}); rc.len = response.items.len;
return null; rc.headers = toHeaders(alloc, headers) catch |e| {
};
rc.ptr = zig_response.body.items.ptr;
rc.len = zig_response.body.items.len;
rc.headers = toHeaders(alloc, zig_response.headers) catch |e| {
log.err("Unexpected error processing request: {any}", .{e}); log.err("Unexpected error processing request: {any}", .{e});
if (@errorReturnTrace()) |trace| { if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*); std.debug.dumpStackTrace(trace.*);
} }
return null; return null;
}; };
rc.headers_len = zig_response.headers.list.items.len; rc.headers_len = headers.count();
rc.status = if (zig_response.status == .ok) 0 else @intFromEnum(zig_response.status);
rc.reason_len = 0;
if (zig_response.reason) |*r| {
rc.reason_ptr = @constCast(r.ptr);
rc.reason_len = r.len;
}
return rc; return rc;
} }

View File

@ -57,33 +57,27 @@ export fn handle_request(request: *interface.Request) callconv(.C) ?*interface.R
// //
// handleRequest function here is the last line of boilerplate and the // handleRequest function here is the last line of boilerplate and the
// entry to a request // entry to a request
fn handleRequest(allocator: std.mem.Allocator, response: *interface.ZigResponse) !void { fn handleRequest(allocator: std.mem.Allocator, request: interface.ZigRequest, response: interface.ZigResponse) !void {
_ = allocator; _ = allocator;
// setup // setup
var response_writer = response.body.writer(); var response_writer = response.body.writer();
// real work // real work
if (response.request.headers.getFirstValue("host")) |host| { for (request.headers) |h| {
if (std.mem.startsWith(u8, host, "iam")) { const header = interface.toZigHeader(h);
// std.debug.print("\n{s}: {s}\n", .{ header.name, header.value });
if (std.ascii.eqlIgnoreCase(header.name, "host") and std.mem.startsWith(u8, header.value, "iam")) {
try response_writer.print("iam response", .{}); try response_writer.print("iam response", .{});
return; return;
} }
if (std.ascii.eqlIgnoreCase(header.name, "x-slow")) {
std.time.sleep(std.time.ns_per_ms * (std.fmt.parseInt(usize, header.value, 10) catch 1000));
try response_writer.print("i am slow\n\n", .{});
return;
}
} }
if (response.request.headers.getFirstValue("x-slow")) |ms| { try response_writer.print(" {d}", .{request.headers.len});
std.time.sleep(std.time.ns_per_ms * (std.fmt.parseInt(usize, ms, 10) catch 1000)); try response.headers.put("X-custom-foo", "bar");
try response_writer.print("i am slow\n\n", .{}); log.info("handlerequest header count {d}", .{response.headers.count()});
return;
}
if (response.request.headers.getFirstValue("x-log-this")) |l| {
try response.writeAll(l);
}
if (response.request.headers.getFirstValue("x-status")) |s| {
response.status = @enumFromInt(std.fmt.parseInt(u10, s, 10) catch 500);
}
if (response.request.headers.getFirstValue("x-throw")) |_| {
return error.Thrown;
}
try response_writer.print(" {d}", .{response.request.headers.list.items.len});
try response.headers.append("X-custom-foo", "bar");
} }
test "handle_request" { test "handle_request" {
@ -105,104 +99,10 @@ test "handle_request" {
.content = @ptrCast(@constCast("GET".ptr)), .content = @ptrCast(@constCast("GET".ptr)),
.content_len = 3, .content_len = 3,
.headers = headers.ptr, .headers = headers.ptr,
.headers_len = headers.len, .headers_len = 1,
}; };
const response = handle_request(&req).?; const response = handle_request(&req).?;
try testing.expectEqualStrings(" 1", response.ptr[0..response.len]); try testing.expectEqualStrings(" 1", response.ptr[0..response.len]);
try testing.expectEqual(@as(usize, 1), response.headers_len);
try testing.expectEqualStrings("X-custom-foo", response.headers[0].name_ptr[0..response.headers[0].name_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]); try testing.expectEqualStrings("bar", response.headers[0].value_ptr[0..response.headers[0].value_len]);
} }
test "lib can write data directly" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
var aa = arena.allocator();
interface.zigInit(&aa);
var headers: []interface.Header = @constCast(&[_]interface.Header{.{
.name_ptr = @ptrCast(@constCast("x-log-this".ptr)),
.name_len = "x-log-this".len,
.value_ptr = @ptrCast(@constCast("I am a teapot".ptr)),
.value_len = "I am a teapot".len,
}});
var req = interface.Request{
.target = @ptrCast(@constCast("/".ptr)),
.target_len = 1,
.method = @ptrCast(@constCast("GET".ptr)),
.method_len = 3,
.content = @ptrCast(@constCast("GET".ptr)),
.content_len = 3,
.headers = headers.ptr,
.headers_len = headers.len,
};
const response = handle_request(&req).?;
try testing.expectEqual(@as(usize, 1), response.headers_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]);
try testing.expectEqualStrings("I am a teapot 1", response.ptr[0..response.len]);
}
test "lib can write data directly and still throw" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
var aa = arena.allocator();
interface.zigInit(&aa);
var headers: []interface.Header = @constCast(&[_]interface.Header{ .{
.name_ptr = @ptrCast(@constCast("x-log-this".ptr)),
.name_len = "x-log-this".len,
.value_ptr = @ptrCast(@constCast("I am a teapot".ptr)),
.value_len = "I am a teapot".len,
}, .{
.name_ptr = @ptrCast(@constCast("x-throw".ptr)),
.name_len = "x-throw".len,
.value_ptr = @ptrCast(@constCast("I am a teapot".ptr)),
.value_len = "I am a teapot".len,
} });
var req = interface.Request{
.target = @ptrCast(@constCast("/".ptr)),
.target_len = 1,
.method = @ptrCast(@constCast("GET".ptr)),
.method_len = 3,
.content = @ptrCast(@constCast("GET".ptr)),
.content_len = 3,
.headers = headers.ptr,
.headers_len = headers.len,
};
const response = handle_request(&req).?;
try testing.expectEqual(@as(usize, 500), response.status);
try testing.expectEqualStrings("I am a teapot", response.ptr[0..response.len]);
}
test "lib can set status, update data directly and still throw" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
var aa = arena.allocator();
interface.zigInit(&aa);
var headers: []interface.Header = @constCast(&[_]interface.Header{ .{
.name_ptr = @ptrCast(@constCast("x-log-this".ptr)),
.name_len = "x-log-this".len,
.value_ptr = @ptrCast(@constCast("I am a teapot".ptr)),
.value_len = "I am a teapot".len,
}, .{
.name_ptr = @ptrCast(@constCast("x-throw".ptr)),
.name_len = "x-throw".len,
.value_ptr = @ptrCast(@constCast("I am a teapot".ptr)),
.value_len = "I am a teapot".len,
}, .{
.name_ptr = @ptrCast(@constCast("x-status".ptr)),
.name_len = "x-status".len,
.value_ptr = @ptrCast(@constCast("418".ptr)),
.value_len = "418".len,
} });
var req = interface.Request{
.target = @ptrCast(@constCast("/".ptr)),
.target_len = 1,
.method = @ptrCast(@constCast("GET".ptr)),
.method_len = 3,
.content = @ptrCast(@constCast("GET".ptr)),
.content_len = 3,
.headers = headers.ptr,
.headers_len = headers.len,
};
const response = handle_request(&req).?;
try testing.expectEqual(@as(usize, 418), response.status);
try testing.expectEqualStrings("I am a teapot", response.ptr[0..response.len]);
}

View File

@ -100,12 +100,6 @@ fn serve(allocator: *std.mem.Allocator, response: *std.http.Server.Response) !*F
var slice: []u8 = serve_result.ptr[0..serve_result.len]; var slice: []u8 = serve_result.ptr[0..serve_result.len];
log.debug("response body: {s}", .{slice}); log.debug("response body: {s}", .{slice});
if (serve_result.status != 0) {
response.status = @enumFromInt(serve_result.status);
if (serve_result.reason_len > 0) {
response.reason = serve_result.reason_ptr[0..serve_result.reason_len];
}
}
// Deal with results // Deal with results
var content_type_added = false; var content_type_added = false;
for (0..serve_result.headers_len) |inx| { for (0..serve_result.headers_len) |inx| {
@ -625,7 +619,6 @@ test {
std.testing.refAllDecls(@This()); std.testing.refAllDecls(@This());
std.testing.refAllDecls(config); std.testing.refAllDecls(config);
std.testing.refAllDecls(interface); std.testing.refAllDecls(interface);
std.testing.refAllDecls(@import("main-lib.zig"));
} }
var test_resp_buf: [1024]u8 = undefined; var test_resp_buf: [1024]u8 = undefined;
var test_resp_buf_len: usize = undefined; var test_resp_buf_len: usize = undefined;