From 4988feb3143c3782a053d382525fba7f23b3cbfb Mon Sep 17 00:00:00 2001 From: Louis Pearson Date: Wed, 24 Apr 2024 21:53:41 -0600 Subject: [PATCH] fix: simplify accept header api somewhat --- examples/accept/accept.zig | 72 +++++++++++++++++++++++++++++ src/request.zig | 94 ++++++++------------------------------ src/zap.zig | 10 ++++ 3 files changed, 102 insertions(+), 74 deletions(-) create mode 100644 examples/accept/accept.zig diff --git a/examples/accept/accept.zig b/examples/accept/accept.zig new file mode 100644 index 0000000..4fe6d26 --- /dev/null +++ b/examples/accept/accept.zig @@ -0,0 +1,72 @@ +const std = @import("std"); +const zap = @import("zap"); + +var gpa = std.heap.GeneralPurposeAllocator(.{ + .thread_safe = true, +}){}; + +fn on_request_verbose(r: zap.Request) void { + const allocator = gpa.allocator(); + const content_type: zap.ContentType = content_type: { + var accept_list = std.ArrayList(zap.Request.AcceptItem).init(allocator); + defer accept_list.deinit(); + r.parseAccept(&accept_list) catch break :content_type .HTML; + for (accept_list.items) |accept| { + break :content_type accept.toContentType() orelse continue; + } + break :content_type .HTML; + }; + + r.setContentType(content_type) catch return; + switch (content_type) { + .TEXT => { + r.sendBody("Hello from ZAP!!!") catch return; + }, + .HTML => { + r.sendBody("

Hello from ZAP!!!

") catch return; + }, + .XML => { + r.sendBody( + \\ + \\ + \\ + \\ Hello from ZAP!!! + \\ + \\ + ) catch return; + }, + .JSON => { + var buffer: [128]u8 = undefined; + const json = zap.stringifyBuf(&buffer, .{ .message = "Hello from ZAP!!!" }, .{}) orelse return; + r.sendJson(json) catch return; + }, + .XHTML => { + r.sendBody( + \\ + \\ + \\ + \\

Hello from ZAP!!!

+ \\ + \\ + ) catch return; + }, + } +} + +pub fn main() !void { + var listener = zap.HttpListener.init(.{ + .port = 3000, + .on_request = on_request_verbose, + .log = true, + .max_clients = 100000, + }); + try listener.listen(); + + std.debug.print("Listening on 0.0.0.0:3000\n", .{}); + + // start worker threads + zap.start(.{ + .threads = 2, + .workers = 2, + }); +} diff --git a/src/request.zig b/src/request.zig index fe071b2..0f97877 100644 --- a/src/request.zig +++ b/src/request.zig @@ -577,76 +577,30 @@ pub fn parseCookies(self: *const Self, url_encoded: bool) void { fio.http_parse_cookies(self.h, if (url_encoded) 1 else 0); } -pub const MIMEType = struct { +pub const AcceptItem = struct { + raw: []const u8, type: Fragment, subtype: Fragment, + q: f64, const Fragment = union(enum) { glob, value: []const u8, }; -}; - -pub const AcceptItem = struct { - mimetype: MIMEType, - q: f64, pub fn lessThan(_: void, lhs: AcceptItem, rhs: AcceptItem) bool { return lhs.q < rhs.q; } - pub fn asCommon(item: AcceptItem) ?Common { - if (item.mimetype.type == .glob) { - if (item.mimetype.subtype == .glob) return .@"*/*"; - return null; + pub fn toContentType(item: AcceptItem) ?ContentType { + if (ContentType.string_map.get(item.raw)) |common| { + return common; } - if (std.mem.eql(u8, "text", item.mimetype.type.value)) { - if (item.mimetype.subtype == .glob) { - return .@"text/*"; - } else if (std.mem.eql(u8, "html", item.mimetype.subtype.value)) { - return .@"text/html"; - } else if (std.mem.eql(u8, "plain", item.mimetype.subtype.value)) { - return .@"text/plain"; - } - } else if (std.mem.eql(u8, "application", item.mimetype.type.value)) { - if (item.mimetype.subtype == .glob) { - return .@"application/*"; - } else if (std.mem.eql(u8, "xml", item.mimetype.subtype.value)) { - return .@"application/xml"; - } else if (std.mem.eql(u8, "json", item.mimetype.subtype.value)) { - return .@"application/json"; - } else if (std.mem.eql(u8, "xhtml+xml", item.mimetype.subtype.value)) { - return .@"application/xhtml+xml"; - } - } else if (std.mem.eql(u8, "image", item.mimetype.type.value)) { - if (item.mimetype.subtype == .glob) { - return .@"image/*"; - } else if (std.mem.eql(u8, "avif", item.mimetype.subtype.value)) { - return .@"image/avif"; - } else if (std.mem.eql(u8, "webp", item.mimetype.subtype.value)) { - return .@"image/webp"; - } - } - return null; } - - const Common = enum { - @"*/*", - @"text/*", - @"text/html", - @"text/plain", - @"application/*", - @"application/xhtml+xml", - @"application/xml", - @"application/json", - @"image/*", - @"image/avif", - @"image/webp", - }; }; -/// Parses `Accept:` http header into `list`. +/// Parses `Accept:` http header into `list`, ordered from highest q factor to lowest pub fn parseAccept(self: *const Self, list: *std.ArrayList(AcceptItem)) !void { const accept_str = self.getHeaderCommon(.accept) orelse return error.NoAccept; @@ -669,31 +623,23 @@ pub fn parseAccept(self: *const Self, list: *std.ArrayList(AcceptItem)) !void { const mimetype_type_str = type_split_iter.next() orelse continue; const mimetype_subtype_str = type_split_iter.next() orelse continue; - const mimetype_type: MIMEType.Fragment = if (std.mem.eql(u8, "*", mimetype_type_str)) - .glob - else - .{ .value = mimetype_type_str }; - - const mimetype_subtype: MIMEType.Fragment = if (std.mem.eql(u8, "*", mimetype_subtype_str)) - .glob - else - .{ .value = mimetype_subtype_str }; - - try list.append(.{ - .mimetype = .{ - .type = mimetype_type, - .subtype = mimetype_subtype, - }, + const new_item: AcceptItem = .{ + .raw = mimetype_str, + .type = if (std.mem.eql(u8, "*", mimetype_type_str)) .glob else .{ .value = mimetype_type_str }, + .subtype = if (std.mem.eql(u8, "*", mimetype_subtype_str)) .glob else .{ .value = mimetype_subtype_str }, .q = q_factor, - }); + }; + for (list.items, 1..) |item, i| { + if (AcceptItem.lessThan({}, new_item, item)) { + try list.insert(i, new_item); + break; + } + } else { + try list.append(new_item); + } } } -/// Sorts list from `parseAccept` -pub fn sortAccept(accept_list: []AcceptItem) void { - std.sort.insertion(AcceptItem, accept_list, {}, AcceptItem.lessThan); -} - /// Set a response cookie pub fn setCookie(self: *const Self, args: CookieArgs) HttpError!void { const c: fio.http_cookie_args_s = .{ diff --git a/src/zap.zig b/src/zap.zig index 695c4b5..d733cd8 100644 --- a/src/zap.zig +++ b/src/zap.zig @@ -159,8 +159,18 @@ pub const HttpError = error{ pub const ContentType = enum { TEXT, HTML, + XML, JSON, + XHTML, // TODO: more content types + + pub const string_map = std.ComptimeStringMap(ContentType, .{ + .{ "text/plain", .TEXT }, + .{ "text/html", .HTML }, + .{ "application/xml", .XML }, + .{ "application/json", .JSON }, + .{ "application/xhtml+xml", .XHTML }, + }); }; /// Used internally: facilio Http request callback function type