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