Skip to content

Commit

Permalink
fix: simplify accept header api somewhat
Browse files Browse the repository at this point in the history
  • Loading branch information
desttinghim committed Apr 25, 2024
1 parent 6d646b6 commit 4988feb
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 74 deletions.
72 changes: 72 additions & 0 deletions examples/accept/accept.zig
Original file line number Diff line number Diff line change
@@ -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("<html><body><h1>Hello from ZAP!!!</h1></body></html>") catch return;
},
.XML => {
r.sendBody(
\\<?xml version="1.0" encoding="UTF-8"?>
\\<message>
\\ <warning>
\\ Hello from ZAP!!!
\\ </warning>
\\</message>
) 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(
\\<?xml version="1.0" encoding="UTF-8"?>
\\<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
\\ <body>
\\ <h1>Hello from ZAP!!!</h1>
\\ </body>
\\</html>
) 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,
});
}
94 changes: 20 additions & 74 deletions src/request.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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 = .{
Expand Down
10 changes: 10 additions & 0 deletions src/zap.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 4988feb

Please sign in to comment.