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);