diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig
index f9ba64f6..7acb8473 100644
--- a/src/browser/js/bridge.zig
+++ b/src/browser/js/bridge.zig
@@ -550,6 +550,7 @@ pub const JsApis = flattenTypes(&.{
@import("../webapi/Location.zig"),
@import("../webapi/Navigator.zig"),
@import("../webapi/net/FormData.zig"),
+ @import("../webapi/net/Headers.zig"),
@import("../webapi/net/Request.zig"),
@import("../webapi/net/Response.zig"),
@import("../webapi/net/URLSearchParams.zig"),
diff --git a/src/browser/tests/net/headers.html b/src/browser/tests/net/headers.html
new file mode 100644
index 00000000..d0d1c35e
--- /dev/null
+++ b/src/browser/tests/net/headers.html
@@ -0,0 +1,31 @@
+
+
+
diff --git a/src/browser/tests/net/request.html b/src/browser/tests/net/request.html
new file mode 100644
index 00000000..c0028cf8
--- /dev/null
+++ b/src/browser/tests/net/request.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/browser/webapi/net/Fetch.zig b/src/browser/webapi/net/Fetch.zig
index 7bbc2da9..547a6ab1 100644
--- a/src/browser/webapi/net/Fetch.zig
+++ b/src/browser/webapi/net/Fetch.zig
@@ -39,7 +39,7 @@ pub const Input = Request.Input;
// @ZIGDOM just enough to get campire demo working
pub fn init(input: Input, page: *Page) !js.Promise {
- const request = try Request.init(input, page);
+ const request = try Request.init(input, null, page);
const fetch = try page.arena.create(Fetch);
fetch.* = .{
diff --git a/src/browser/webapi/net/Headers.zig b/src/browser/webapi/net/Headers.zig
new file mode 100644
index 00000000..2f2fa68f
--- /dev/null
+++ b/src/browser/webapi/net/Headers.zig
@@ -0,0 +1,63 @@
+const std = @import("std");
+const js = @import("../../js/js.zig");
+
+const Page = @import("../../Page.zig");
+const KeyValueList = @import("../KeyValueList.zig");
+
+const Headers = @This();
+
+_list: KeyValueList,
+
+pub fn init(page: *Page) !*Headers {
+ return page._factory.create(Headers{
+ ._list = KeyValueList.init(),
+ });
+}
+
+
+pub fn append(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void {
+ try self._list.append(page.arena, name, value);
+}
+
+pub fn delete(self: *Headers, name: []const u8) void {
+ self._list.delete(name, null);
+}
+
+pub fn get(self: *const Headers, name: []const u8) ?[]const u8 {
+ return self._list.get(name);
+}
+
+pub fn getAll(self: *const Headers, name: []const u8, page: *Page) ![]const []const u8 {
+ return self._list.getAll(name, page);
+}
+
+pub fn has(self: *const Headers, name: []const u8) bool {
+ return self._list.has(name);
+}
+
+pub fn set(self: *Headers, name: []const u8, value: []const u8, page: *Page) !void {
+ try self._list.set(page.arena, name, value);
+}
+
+pub const JsApi = struct {
+ pub const bridge = js.Bridge(Headers);
+
+ pub const Meta = struct {
+ pub const name = "Headers";
+ pub const prototype_chain = bridge.prototypeChain();
+ pub var class_id: bridge.ClassId = undefined;
+ };
+
+ pub const constructor = bridge.constructor(Headers.init, .{});
+ pub const append = bridge.function(Headers.append, .{});
+ pub const delete = bridge.function(Headers.delete, .{});
+ pub const get = bridge.function(Headers.get, .{});
+ pub const getAll = bridge.function(Headers.getAll, .{});
+ pub const has = bridge.function(Headers.has, .{});
+ pub const set = bridge.function(Headers.set, .{});
+};
+
+const testing = @import("../../../testing.zig");
+test "WebApi: Headers" {
+ try testing.htmlRunner("net/headers.html", .{});
+}
diff --git a/src/browser/webapi/net/Request.zig b/src/browser/webapi/net/Request.zig
index d715c53b..d1524afe 100644
--- a/src/browser/webapi/net/Request.zig
+++ b/src/browser/webapi/net/Request.zig
@@ -22,28 +22,92 @@ const js = @import("../../js/js.zig");
const URL = @import("../URL.zig");
const Page = @import("../../Page.zig");
+const Headers = @import("Headers.zig");
const Allocator = std.mem.Allocator;
const Request = @This();
_url: [:0]const u8,
+_method: std.http.Method,
+_headers: ?*Headers,
_arena: Allocator,
pub const Input = union(enum) {
+ request: *Request,
url: [:0]const u8,
- // request: *Request, TODO
};
-pub fn init(input: Input, page: *Page) !*Request {
+pub const Options = struct {
+ method: ?[]const u8 = null,
+ headers: ?*Headers = null,
+};
+
+pub fn init(input: Input, opts_: ?Options, page: *Page) !*Request {
const arena = page.arena;
- const url = try URL.resolve(arena, page.url, input.url, .{ .always_dupe = true });
+ const url = switch (input) {
+ .url => |u| try URL.resolve(arena, page.url, u, .{ .always_dupe = true }),
+ .request => |r| try arena.dupeZ(u8, r._url),
+ };
+
+ const opts = opts_ orelse Options{};
+ const method = if (opts.method) |m|
+ try parseMethod(m, page)
+ else switch (input) {
+ .url => .GET,
+ .request => |r| r._method,
+ };
+
+ const headers = if (opts.headers) |h|
+ h
+ else switch (input) {
+ .url => null,
+ .request => |r| r._headers,
+ };
return page._factory.create(Request{
._url = url,
._arena = arena,
+ ._method = method,
+ ._headers = headers,
});
}
+fn parseMethod(method: []const u8, page: *Page) !std.http.Method {
+ if (method.len > "options".len) {
+ return error.InvalidMethod;
+ }
+
+ const lower = std.ascii.lowerString(&page.buf, method);
+
+ if (std.mem.eql(u8, lower, "get")) return .GET;
+ if (std.mem.eql(u8, lower, "post")) return .POST;
+ if (std.mem.eql(u8, lower, "delete")) return .DELETE;
+ if (std.mem.eql(u8, lower, "put")) return .PUT;
+ if (std.mem.eql(u8, lower, "patch")) return .PATCH;
+ if (std.mem.eql(u8, lower, "head")) return .HEAD;
+ if (std.mem.eql(u8, lower, "options")) return .OPTIONS;
+
+ return error.InvalidMethod;
+}
+
+pub fn getUrl(self: *const Request) []const u8 {
+ return self._url;
+}
+
+pub fn getMethod(self: *const Request) []const u8 {
+ return @tagName(self._method);
+}
+
+pub fn getHeaders(self: *Request, page: *Page) !*Headers {
+ if (self._headers) |headers| {
+ return headers;
+ }
+
+ const headers = try Headers.init(page);
+ self._headers = headers;
+ return headers;
+}
+
pub const JsApi = struct {
pub const bridge = js.Bridge(Request);
@@ -54,4 +118,12 @@ pub const JsApi = struct {
};
pub const constructor = bridge.constructor(Request.init, .{});
+ pub const url = bridge.accessor(Request.getUrl, null, .{});
+ pub const method = bridge.accessor(Request.getMethod, null, .{});
+ pub const headers = bridge.accessor(Request.getHeaders, null, .{});
};
+
+const testing = @import("../../../testing.zig");
+test "WebApi: Request" {
+ try testing.htmlRunner("net/request.html", .{});
+}