diff --git a/src/browser/tests/legacy/fetch/headers.html b/src/browser/tests/legacy/fetch/headers.html index 57d6ce2e..0626ad3d 100644 --- a/src/browser/tests/legacy/fetch/headers.html +++ b/src/browser/tests/legacy/fetch/headers.html @@ -1,3 +1,4 @@ + + + + + + + diff --git a/src/browser/webapi/KeyValueList.zig b/src/browser/webapi/KeyValueList.zig index 4ec85f20..f2282fca 100644 --- a/src/browser/webapi/KeyValueList.zig +++ b/src/browser/webapi/KeyValueList.zig @@ -33,6 +33,7 @@ pub fn registerTypes() []const type { }; } +const Normalizer = *const fn([]const u8, *Page) []const u8; pub const KeyValueList = @This(); _entries: std.ArrayListUnmanaged(Entry) = .empty, @@ -50,7 +51,7 @@ pub fn copy(arena: Allocator, original: KeyValueList) !KeyValueList { return list; } -pub fn fromJsObject(arena: Allocator, js_obj: js.Object) !KeyValueList { +pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?Normalizer, page: *Page) !KeyValueList { var it = js_obj.nameIterator(); var list = KeyValueList.init(); try list.ensureTotalCapacity(arena, it.count); @@ -58,9 +59,10 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object) !KeyValueList { while (try it.next()) |name| { const js_value = try js_obj.get(name); const value = try js_value.toString(arena); + const normalized = if (comptime normalizer) |n| n(name, page) else name; - try list._entries.append(arena, .{ - .name = try String.init(arena, name, .{}), + list._entries.appendAssumeCapacity(.{ + .name = try String.init(arena, normalized, .{}), .value = try String.init(arena, value, .{}), }); } @@ -68,6 +70,21 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object) !KeyValueList { return list; } +pub fn fromArray(arena: Allocator, kvs: []const [2][]const u8, comptime normalizer: ?Normalizer, page: *Page) !KeyValueList { + var list = KeyValueList.init(); + try list.ensureTotalCapacity(arena, kvs.len); + + for (kvs) |pair| { + const normalized = if (comptime normalizer) |n| n(pair[0], page) else pair[0]; + + list._entries.appendAssumeCapacity(.{ + .name = try String.init(arena, normalized, .{}), + .value = try String.init(arena, pair[1], .{}), + }); + } + return list; +} + pub const Entry = struct { name: String, value: String, diff --git a/src/browser/webapi/net/Headers.zig b/src/browser/webapi/net/Headers.zig index 63377179..8c80c591 100644 --- a/src/browser/webapi/net/Headers.zig +++ b/src/browser/webapi/net/Headers.zig @@ -1,6 +1,7 @@ const std = @import("std"); const js = @import("../../js/js.zig"); const log = @import("../../../log.zig"); +const String = @import("../../../string.zig").String; const Page = @import("../../Page.zig"); const KeyValueList = @import("../KeyValueList.zig"); @@ -13,13 +14,15 @@ _list: KeyValueList, pub const InitOpts = union(enum) { obj: *Headers, + strings: []const [2][]const u8, js_obj: js.Object, }; pub fn init(opts_: ?InitOpts, page: *Page) !*Headers { const list = if (opts_) |opts| switch (opts) { .obj => |obj| try KeyValueList.copy(page.arena, obj._list), - .js_obj => |js_obj| try KeyValueList.fromJsObject(page.arena, js_obj), + .js_obj => |js_obj| try KeyValueList.fromJsObject(page.arena, js_obj, normalizeHeaderName, page), + .strings => |kvs| try KeyValueList.fromArray(page.arena, kvs, normalizeHeaderName, page), } else KeyValueList.init(); return page._factory.create(Headers{ @@ -27,12 +30,6 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*Headers { }); } -// pub fn fromJsObject(js_obj: js.Object, page: *Page) !*Headers { -// return page._factory.create(Headers{ -// ._list = try KeyValueList.fromJsObject(page.arena, js_obj), -// }); -// } - pub fn append(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void { const normalized_name = normalizeHeaderName(name, page); try self._list.append(page.arena, normalized_name, value); @@ -43,9 +40,17 @@ pub fn delete(self: *Headers, name: []const u8, page: *Page) void { self._list.delete(normalized_name, null); } -pub fn get(self: *const Headers, name: []const u8, page: *Page) ?[]const u8 { +pub fn get(self: *const Headers, name: []const u8, page: *Page) !?[]const u8 { const normalized_name = normalizeHeaderName(name, page); - return self._list.get(normalized_name); + const all_values = try self._list.getAll(normalized_name, page); + + if (all_values.len == 0) { + return null; + } + if (all_values.len == 1) { + return all_values[0]; + } + return try std.mem.join(page.call_arena, ", ", all_values); } pub fn has(self: *const Headers, name: []const u8, page: *Page) bool { @@ -97,6 +102,7 @@ fn normalizeHeaderName(name: []const u8, page: *Page) []const u8 { return std.ascii.lowerString(&page.buf, name); } + pub const JsApi = struct { pub const bridge = js.Bridge(Headers); diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index 73e5e110..f3069531 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -45,7 +45,7 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*URLSearchParams { .query_string => |qs| break :blk try paramsFromString(arena, qs, &page.buf), .value => |js_val| { if (js_val.isObject()) { - break :blk try KeyValueList.fromJsObject(arena, js_val.toObject()); + break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, page); } if (js_val.isString()) { break :blk try paramsFromString(arena, try js_val.toString(arena), &page.buf);