srctree

Gregory Mullen parent 029cac1e 79aa3eef
convert header LL into an array

the intent of this change is to 'flatten' out the api a bit to move the zWSGI list into this, and then move many of params from frame/request into headers as the single source of truth.
inlinesplit
src/auth/cookie.zig added: 61, removed: 71, total 0
@@ -120,8 +120,8 @@ pub fn CookieAuth(HMAC: type) type {
if (headers.getCustom("Cookie")) |cookies| {
// This actually isn't technically invalid, it's only
// currently not implemented.
if (cookies.value_list.next != null) return error.InvalidAuth;
const cookie = cookies.value_list.value;
if (cookies.list.len > 1) return error.InvalidAuth;
const cookie = cookies.list[0];
var itr = tokenizeSequence(u8, cookie, "; ");
while (itr.next()) |tkn| {
if (startsWith(u8, tkn, ca.cookie_name)) {
 
src/auth/mtls.zig added: 61, removed: 71, total 0
@@ -15,11 +15,11 @@ pub fn authenticate(ptr: *anyopaque, headers: *const Headers) Error!User {
const mtls: *MTLS = @ptrCast(@alignCast(ptr));
var success: bool = false;
if (headers.getCustom("MTLS_ENABLED")) |enabled| {
if (enabled.value_list.next) |_| return error.InvalidAuth;
if (enabled.list.len > 1) return error.InvalidAuth;
// MTLS validation as currently supported here is done by the
// reverse proxy. Constant time compare would provide no security
// benefits here.
if (std.mem.eql(u8, enabled.value_list.value, "SUCCESS")) {
if (std.mem.eql(u8, enabled.list[0], "SUCCESS")) {
success = true;
}
}
@@ -31,8 +31,8 @@ pub fn authenticate(ptr: *anyopaque, headers: *const Headers) Error!User {
// Verse does not specify an order for which is valid so it is
// an error if there is ever more than a single value for the
// mTLS fingerprint
if (enabled.value_list.next != null) return error.InvalidAuth;
return base.lookupUser(enabled.value_list.value);
if (enabled.list.len > 1) return error.InvalidAuth;
return base.lookupUser(enabled.list[0]);
}
}
return .{ .user_ptr = null };
 
src/headers.zig added: 61, removed: 71, total 0
@@ -1,10 +1,10 @@
alloc: Allocator,
known: KnownMap,
custom: CustomMap,
extra: ExtraMap,
 
const Headers = @This();
 
pub const KnownHeaders = enum {
pub const Expected = enum {
accept,
accept_encoding,
host,
@@ -20,37 +20,26 @@ pub const Header = struct {
/// Unstable API that may get removed
pub const HeaderList = struct {
name: []const u8,
value_list: *ValueList,
list: [][]const u8,
};
 
const ValueList = struct {
value: []const u8,
next: ?*ValueList = null,
};
 
const KnownMap = EnumMap(KnownHeaders, []const u8);
const CustomMap = std.StringArrayHashMapUnmanaged(*ValueList);
const KnownMap = std.EnumMap(Expected, []const u8);
const ExtraMap = std.StringArrayHashMapUnmanaged(HeaderList);
 
pub fn init(a: Allocator) Headers {
return .{
.alloc = a,
.known = KnownMap{},
.custom = CustomMap{},
.extra = ExtraMap{},
};
}
 
pub fn raze(h: *Headers) void {
const values = h.custom.values();
const values = h.extra.values();
for (values) |val| {
var next: ?*ValueList = val.*.next;
h.alloc.destroy(val);
while (next != null) {
const destroy = next.?;
next = next.?.next;
h.alloc.destroy(destroy);
}
h.alloc.free(val.list);
}
h.custom.deinit(h.alloc);
h.extra.deinit(h.alloc);
}
 
fn normalize(_: []const u8) !void {
@@ -59,27 +48,28 @@ fn normalize(_: []const u8) !void {
 
pub fn addCustom(h: *Headers, name: []const u8, value: []const u8) !void {
// TODO normalize lower
const gop = try h.custom.getOrPut(h.alloc, name);
const gop = try h.extra.getOrPut(h.alloc, name);
const hl: *HeaderList = gop.value_ptr;
if (gop.found_existing) {
var end: *ValueList = gop.value_ptr.*;
while (end.*.next != null) {
end = end.next.?;
if (!h.alloc.resize(hl.list, hl.list.len + 1)) {
hl.list = try h.alloc.realloc(hl.list, hl.list.len + 1);
}
end.next = try h.alloc.create(ValueList);
end.next.?.value = value;
end.next.?.next = null;
hl.list[hl.list.len - 1] = value;
} else {
gop.value_ptr.* = try h.alloc.create(ValueList);
gop.value_ptr.*.value = value;
gop.value_ptr.*.next = null;
hl.* = .{
.name = name,
.list = try h.alloc.alloc([]const u8, 1),
};
hl.list[0] = value;
}
}
 
pub fn getCustom(h: *const Headers, name: []const u8) ?HeaderList {
if (h.custom.get(name)) |header| {
// TODO fix me
if (h.extra.get(name)) |header| {
return .{
.name = name,
.value_list = header,
.list = header.list,
};
} else return null;
}
@@ -92,34 +82,34 @@ pub fn iterator(h: *Headers) Iterator {
 
pub const Iterator = struct {
header: *Headers,
inner: CustomMap.Iterator,
entry: ?CustomMap.Entry = null,
current: ?*ValueList = null,
current_name: ?[]const u8 = null,
inner: ExtraMap.Iterator,
entry: ?ExtraMap.Entry = null,
current: ?*HeaderList = null,
current_idx: usize = 0,
 
pub fn init(h: *Headers) Iterator {
h.custom.lockPointers();
h.extra.lockPointers();
return .{
.header = h,
.inner = h.custom.iterator(),
.inner = h.extra.iterator(),
};
}
 
pub fn next(i: *Iterator) ?Header {
if (i.current) |current| {
defer i.current = current.next;
defer i.current_idx += 1;
return .{
.name = i.current_name.?,
.value = current.value,
.name = current.name,
.value = current.list[i.current_idx],
};
} else {
i.current_name = null;
i.current = null;
i.entry = i.inner.next();
i.current_idx = 0;
if (i.entry) |entry| {
i.current = entry.value_ptr.*;
i.current_name = entry.key_ptr.*;
i.current = entry.value_ptr;
} else {
i.header.custom.unlockPointers();
i.header.extra.unlockPointers();
return null;
}
return i.next();
@@ -132,7 +122,7 @@ pub const Iterator = struct {
}
};
 
pub fn toSlice(h: Headers, a: Allocator) ![]Header {
pub fn toSlice(h: *Headers, a: Allocator) ![]Header {
var itr = h.iterator();
var count: usize = 0;
while (itr.next()) |_| count += 1;
@@ -147,18 +137,18 @@ pub fn toSlice(h: Headers, a: Allocator) ![]Header {
 
pub fn format(h: Headers, comptime fmts: []const u8, _: std.fmt.FormatOptions, out: anytype) !void {
comptime if (fmts.len > 0) @compileError("Header format string must be empty");
var iter = h.custom.iterator();
var iter = h.extra.iterator();
 
while (iter.next()) |next| {
var old: ?*ValueList = next.value_ptr.*;
while (old) |this| {
try out.print("{s}: {s}\n", .{ next.key_ptr.*, this.value });
old = this.next;
for (next.value_ptr.list) |this| {
try out.print("{s}: {s}\n", .{ next.value_ptr.name, this });
}
}
}
 
test Headers {
_ = std.testing.refAllDecls(Headers);
 
const a = std.testing.allocator;
var hmap = init(a);
defer hmap.raze();
@@ -167,13 +157,13 @@ test Headers {
try hmap.addCustom("first", "3");
try hmap.addCustom("second", "4");
 
try std.testing.expectEqual(2, hmap.custom.count());
const first = hmap.custom.get("first");
try std.testing.expectEqualStrings(first.?.value, "1");
try std.testing.expectEqualStrings(first.?.next.?.value, "2");
try std.testing.expectEqualStrings(first.?.next.?.next.?.value, "3");
const second = hmap.custom.get("second");
try std.testing.expectEqualStrings(second.?.value, "4");
try std.testing.expectEqual(2, hmap.extra.count());
const first = hmap.extra.get("first");
try std.testing.expectEqualStrings(first.?.list[0], "1");
try std.testing.expectEqualStrings(first.?.list[1], "2");
try std.testing.expectEqualStrings(first.?.list[2], "3");
const second = hmap.extra.get("second");
try std.testing.expectEqualStrings(second.?.list[0], "4");
}
 
const std = @import("std");
 
src/request.zig added: 61, removed: 71, total 0
@@ -135,7 +135,7 @@ fn initCommon(
) !Request {
var method = _method;
if (headers.getCustom("Upgrade")) |val| {
std.debug.print("Upgrade: {s}\n", .{val.value_list.value});
std.debug.print("Upgrade: {s}\n", .{val.list[0]});
method = Methods.WEBSOCKET;
}
 
 
src/websocket.zig added: 61, removed: 71, total 0
@@ -4,7 +4,7 @@ const Websocket = @This();
 
pub fn accept(frame: *Frame) !Websocket {
const key = if (frame.request.headers.getCustom("Sec-WebSocket-Key")) |key|
key.value_list.value
key.list[0]
else
return error.InvalidWebsocketRequest;