mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-15 15:58:57 +00:00
Headers and improved Request
This commit is contained in:
@@ -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"),
|
||||
|
||||
31
src/browser/tests/net/headers.html
Normal file
31
src/browser/tests/net/headers.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<script id=basic>
|
||||
{
|
||||
const headers = new Headers();
|
||||
testing.expectEqual(null, headers.get('Content-Type'));
|
||||
testing.expectEqual(false, headers.has('Content-Type'));
|
||||
|
||||
headers.set('Content-Type', 'application/json');
|
||||
testing.expectEqual('application/json', headers.get('Content-Type'));
|
||||
testing.expectEqual(true, headers.has('Content-Type'));
|
||||
|
||||
headers.set('Content-Type', 'text/html');
|
||||
testing.expectEqual('text/html', headers.get('Content-Type'));
|
||||
|
||||
headers.delete('Content-Type');
|
||||
testing.expectEqual(null, headers.get('Content-Type'));
|
||||
testing.expectEqual(false, headers.has('Content-Type'));
|
||||
}
|
||||
|
||||
{
|
||||
const headers = new Headers();
|
||||
headers.append('Accept', 'application/json');
|
||||
headers.append('Accept', 'text/html');
|
||||
|
||||
const all = headers.getAll('Accept');
|
||||
testing.expectEqual(2, all.length);
|
||||
testing.expectEqual('application/json', all[0]);
|
||||
testing.expectEqual('text/html', all[1]);
|
||||
}
|
||||
</script>
|
||||
104
src/browser/tests/net/request.html
Normal file
104
src/browser/tests/net/request.html
Normal file
@@ -0,0 +1,104 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../testing.js"></script>
|
||||
<script id=basic>
|
||||
{
|
||||
const req = new Request('https://example.com/api');
|
||||
testing.expectEqual('https://example.com/api', req.url);
|
||||
testing.expectEqual('GET', req.method);
|
||||
}
|
||||
|
||||
{
|
||||
const req = new Request('https://example.com/api', { method: 'POST' });
|
||||
testing.expectEqual('https://example.com/api', req.url);
|
||||
testing.expectEqual('POST', req.method);
|
||||
}
|
||||
|
||||
{
|
||||
const req = new Request('https://example.com/api', { method: 'post' });
|
||||
testing.expectEqual('POST', req.method);
|
||||
}
|
||||
|
||||
{
|
||||
const req = new Request('/path');
|
||||
testing.expectEqual(true, req.url.includes('/path'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=headers>
|
||||
{
|
||||
const req = new Request('https://example.com/api');
|
||||
const headers = req.headers;
|
||||
testing.expectEqual('object', typeof headers);
|
||||
|
||||
headers.set('Content-Type', 'application/json');
|
||||
testing.expectEqual('application/json', headers.get('Content-Type'));
|
||||
|
||||
const headers2 = req.headers;
|
||||
testing.expectEqual(true, headers === headers2);
|
||||
testing.expectEqual('application/json', headers2.get('Content-Type'));
|
||||
}
|
||||
|
||||
{
|
||||
const headers = new Headers();
|
||||
headers.set('X-Custom', 'value');
|
||||
|
||||
const req = new Request('https://example.com/api', { headers });
|
||||
testing.expectEqual('value', req.headers.get('X-Custom'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=request_input>
|
||||
{
|
||||
const req1 = new Request('https://example.com/api', { method: 'POST' });
|
||||
const req2 = new Request(req1);
|
||||
|
||||
testing.expectEqual('https://example.com/api', req2.url);
|
||||
testing.expectEqual('POST', req2.method);
|
||||
}
|
||||
|
||||
{
|
||||
const req1 = new Request('https://example.com/api', { method: 'POST' });
|
||||
const req2 = new Request(req1, { method: 'GET' });
|
||||
|
||||
testing.expectEqual('https://example.com/api', req2.url);
|
||||
testing.expectEqual('GET', req2.method);
|
||||
}
|
||||
|
||||
{
|
||||
const headers = new Headers();
|
||||
headers.set('X-Original', 'value1');
|
||||
const req1 = new Request('https://example.com/api', { headers });
|
||||
|
||||
const newHeaders = new Headers();
|
||||
newHeaders.set('X-New', 'value2');
|
||||
const req2 = new Request(req1, { headers: newHeaders });
|
||||
|
||||
testing.expectEqual(null, req2.headers.get('X-Original'));
|
||||
testing.expectEqual('value2', req2.headers.get('X-New'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=stringifier>
|
||||
{
|
||||
const url = new URL('https://example.com/api');
|
||||
const req = new Request(url);
|
||||
testing.expectEqual('https://example.com/api', req.url);
|
||||
}
|
||||
|
||||
{
|
||||
const anchor = document.createElement('a');
|
||||
anchor.href = 'https://example.com/test';
|
||||
const req = new Request(anchor);
|
||||
testing.expectEqual('https://example.com/test', req.url);
|
||||
}
|
||||
|
||||
{
|
||||
const obj = {
|
||||
toString() {
|
||||
return 'https://example.com/custom';
|
||||
}
|
||||
};
|
||||
const req = new Request(obj);
|
||||
testing.expectEqual('https://example.com/custom', req.url);
|
||||
}
|
||||
</script>
|
||||
@@ -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.* = .{
|
||||
|
||||
63
src/browser/webapi/net/Headers.zig
Normal file
63
src/browser/webapi/net/Headers.zig
Normal file
@@ -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", .{});
|
||||
}
|
||||
@@ -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", .{});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user