mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
add bodyUsed checks on Request and Response
This commit is contained in:
@@ -25,21 +25,24 @@ const Page = @import("../page.zig").Page;
|
|||||||
const Response = @import("./Response.zig");
|
const Response = @import("./Response.zig");
|
||||||
|
|
||||||
const Http = @import("../../http/Http.zig");
|
const Http = @import("../../http/Http.zig");
|
||||||
const HttpClient = @import("../../http/Client.zig");
|
|
||||||
const Mime = @import("../mime.zig").Mime;
|
|
||||||
|
|
||||||
const v8 = @import("v8");
|
const v8 = @import("v8");
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
|
|
||||||
|
const Headers = @import("Headers.zig");
|
||||||
|
const HeadersInit = @import("Headers.zig").HeadersInit;
|
||||||
|
|
||||||
pub const RequestInput = union(enum) {
|
pub const RequestInput = union(enum) {
|
||||||
string: []const u8,
|
string: []const u8,
|
||||||
request: Request,
|
request: *Request,
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/RequestInit
|
// https://developer.mozilla.org/en-US/docs/Web/API/RequestInit
|
||||||
pub const RequestInit = struct {
|
pub const RequestInit = struct {
|
||||||
method: ?[]const u8 = null,
|
method: ?[]const u8 = null,
|
||||||
body: ?[]const u8 = null,
|
body: ?[]const u8 = null,
|
||||||
|
integrity: ?[]const u8 = null,
|
||||||
|
headers: ?HeadersInit = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
|
// https://developer.mozilla.org/en-US/docs/Web/API/Request/Request
|
||||||
@@ -47,7 +50,10 @@ const Request = @This();
|
|||||||
|
|
||||||
method: Http.Method,
|
method: Http.Method,
|
||||||
url: [:0]const u8,
|
url: [:0]const u8,
|
||||||
|
headers: Headers,
|
||||||
body: ?[]const u8,
|
body: ?[]const u8,
|
||||||
|
body_used: bool = false,
|
||||||
|
integrity: []const u8,
|
||||||
|
|
||||||
pub fn constructor(input: RequestInput, _options: ?RequestInit, page: *Page) !Request {
|
pub fn constructor(input: RequestInput, _options: ?RequestInit, page: *Page) !Request {
|
||||||
const arena = page.arena;
|
const arena = page.arena;
|
||||||
@@ -77,165 +83,115 @@ pub fn constructor(input: RequestInput, _options: ?RequestInit, page: *Page) !Re
|
|||||||
};
|
};
|
||||||
|
|
||||||
const body = if (options.body) |body| try arena.dupe(u8, body) else null;
|
const body = if (options.body) |body| try arena.dupe(u8, body) else null;
|
||||||
|
const integrity = if (options.integrity) |integ| try arena.dupe(u8, integ) else "";
|
||||||
|
const headers = if (options.headers) |hdrs| try Headers.constructor(hdrs, page) else Headers{};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.method = method,
|
.method = method,
|
||||||
.url = url,
|
.url = url,
|
||||||
|
.headers = headers,
|
||||||
.body = body,
|
.body = body,
|
||||||
|
.integrity = integrity,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_url(self: *const Request) []const u8 {
|
|
||||||
return self.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_method(self: *const Request) []const u8 {
|
|
||||||
return @tagName(self.method);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub fn get_body(self: *const Request) ?[]const u8 {
|
// pub fn get_body(self: *const Request) ?[]const u8 {
|
||||||
// return self.body;
|
// return self.body;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const FetchContext = struct {
|
pub fn get_bodyUsed(self: *const Request) bool {
|
||||||
arena: std.mem.Allocator,
|
return self.body_used;
|
||||||
js_ctx: *Env.JsContext,
|
}
|
||||||
promise_resolver: v8.Persistent(v8.PromiseResolver),
|
|
||||||
|
pub fn get_headers(self: *Request) *Headers {
|
||||||
method: Http.Method,
|
return &self.headers;
|
||||||
url: []const u8,
|
}
|
||||||
body: std.ArrayListUnmanaged(u8) = .empty,
|
|
||||||
headers: std.ArrayListUnmanaged([]const u8) = .empty,
|
pub fn get_integrity(self: *const Request) []const u8 {
|
||||||
status: u16 = 0,
|
return self.integrity;
|
||||||
mime: ?Mime = null,
|
}
|
||||||
transfer: ?*HttpClient.Transfer = null,
|
|
||||||
|
// TODO: If we ever support the Navigation API, we need isHistoryNavigation
|
||||||
/// This effectively takes ownership of the FetchContext.
|
// https://developer.mozilla.org/en-US/docs/Web/API/Request/isHistoryNavigation
|
||||||
///
|
|
||||||
/// We just return the underlying slices used for `headers`
|
pub fn get_method(self: *const Request) []const u8 {
|
||||||
/// and for `body` here to avoid an allocation.
|
return @tagName(self.method);
|
||||||
pub fn toResponse(self: *const FetchContext) !Response {
|
}
|
||||||
return Response{
|
|
||||||
.status = self.status,
|
pub fn get_url(self: *const Request) []const u8 {
|
||||||
.headers = self.headers.items,
|
return self.url;
|
||||||
.mime = self.mime,
|
}
|
||||||
.body = self.body.items,
|
|
||||||
};
|
pub fn _clone(self: *Request, page: *Page) !Request {
|
||||||
|
// Not allowed to clone if the body was used.
|
||||||
|
if (self.body_used) {
|
||||||
|
return error.TypeError;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
|
|
||||||
pub fn fetch(input: RequestInput, options: ?RequestInit, page: *Page) !Env.Promise {
|
|
||||||
const arena = page.arena;
|
const arena = page.arena;
|
||||||
|
|
||||||
const req = try Request.constructor(input, options, page);
|
return Request{
|
||||||
|
.body = if (self.body) |body| try arena.dupe(u8, body) else null,
|
||||||
|
.body_used = self.body_used,
|
||||||
|
.headers = try self.headers.clone(arena),
|
||||||
|
.method = self.method,
|
||||||
|
.integrity = try arena.dupe(u8, self.integrity),
|
||||||
|
.url = try arena.dupeZ(u8, self.url),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _bytes(self: *Response, page: *Page) !Env.Promise {
|
||||||
|
if (self.body_used) {
|
||||||
|
return error.TypeError;
|
||||||
|
}
|
||||||
|
|
||||||
const resolver = Env.PromiseResolver{
|
const resolver = Env.PromiseResolver{
|
||||||
.js_context = page.main_context,
|
.js_context = page.main_context,
|
||||||
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||||
};
|
};
|
||||||
|
|
||||||
var headers = try Http.Headers.init();
|
try resolver.resolve(self.body);
|
||||||
try page.requestCookie(.{}).headersForRequest(arena, req.url, &headers);
|
self.body_used = true;
|
||||||
|
return resolver.promise();
|
||||||
|
}
|
||||||
|
|
||||||
const fetch_ctx = try arena.create(FetchContext);
|
pub fn _json(self: *Response, page: *Page) !Env.Promise {
|
||||||
fetch_ctx.* = .{
|
if (self.body_used) {
|
||||||
.arena = arena,
|
return error.TypeError;
|
||||||
.js_ctx = page.main_context,
|
}
|
||||||
.promise_resolver = v8.Persistent(v8.PromiseResolver).init(
|
|
||||||
page.main_context.isolate,
|
const resolver = Env.PromiseResolver{
|
||||||
resolver.resolver,
|
.js_context = page.main_context,
|
||||||
),
|
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||||
.method = req.method,
|
|
||||||
.url = req.url,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try page.http_client.request(.{
|
const p = std.json.parseFromSliceLeaky(
|
||||||
.ctx = @ptrCast(fetch_ctx),
|
std.json.Value,
|
||||||
.url = req.url,
|
page.arena,
|
||||||
.method = req.method,
|
self.body,
|
||||||
.headers = headers,
|
.{},
|
||||||
.body = req.body,
|
) catch |e| {
|
||||||
.cookie_jar = page.cookie_jar,
|
log.warn(.browser, "invalid json", .{ .err = e, .source = "Request" });
|
||||||
.resource_type = .fetch,
|
return error.SyntaxError;
|
||||||
|
|
||||||
.start_callback = struct {
|
|
||||||
fn startCallback(transfer: *HttpClient.Transfer) !void {
|
|
||||||
const self: *FetchContext = @alignCast(@ptrCast(transfer.ctx));
|
|
||||||
log.debug(.http, "request start", .{ .method = self.method, .url = self.url, .source = "fetch" });
|
|
||||||
|
|
||||||
self.transfer = transfer;
|
|
||||||
}
|
|
||||||
}.startCallback,
|
|
||||||
.header_callback = struct {
|
|
||||||
fn headerCallback(transfer: *HttpClient.Transfer) !void {
|
|
||||||
const self: *FetchContext = @alignCast(@ptrCast(transfer.ctx));
|
|
||||||
|
|
||||||
const header = &transfer.response_header.?;
|
|
||||||
|
|
||||||
log.debug(.http, "request header", .{
|
|
||||||
.source = "fetch",
|
|
||||||
.method = self.method,
|
|
||||||
.url = self.url,
|
|
||||||
.status = header.status,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (header.contentType()) |ct| {
|
|
||||||
self.mime = Mime.parse(ct) catch {
|
|
||||||
return error.MimeParsing;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var it = transfer.responseHeaderIterator();
|
|
||||||
while (it.next()) |hdr| {
|
|
||||||
const joined = try std.fmt.allocPrint(self.arena, "{s}: {s}", .{ hdr.name, hdr.value });
|
|
||||||
try self.headers.append(self.arena, joined);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.status = header.status;
|
|
||||||
}
|
|
||||||
}.headerCallback,
|
|
||||||
.data_callback = struct {
|
|
||||||
fn dataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void {
|
|
||||||
const self: *FetchContext = @alignCast(@ptrCast(transfer.ctx));
|
|
||||||
try self.body.appendSlice(self.arena, data);
|
|
||||||
}
|
|
||||||
}.dataCallback,
|
|
||||||
.done_callback = struct {
|
|
||||||
fn doneCallback(ctx: *anyopaque) !void {
|
|
||||||
const self: *FetchContext = @alignCast(@ptrCast(ctx));
|
|
||||||
|
|
||||||
log.info(.http, "request complete", .{
|
|
||||||
.source = "fetch",
|
|
||||||
.method = self.method,
|
|
||||||
.url = self.url,
|
|
||||||
.status = self.status,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = try self.toResponse();
|
|
||||||
const promise_resolver: Env.PromiseResolver = .{
|
|
||||||
.js_context = self.js_ctx,
|
|
||||||
.resolver = self.promise_resolver.castToPromiseResolver(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try promise_resolver.resolve(response);
|
try resolver.resolve(p);
|
||||||
|
self.body_used = true;
|
||||||
|
return resolver.promise();
|
||||||
}
|
}
|
||||||
}.doneCallback,
|
|
||||||
.error_callback = struct {
|
|
||||||
fn errorCallback(ctx: *anyopaque, err: anyerror) void {
|
|
||||||
const self: *FetchContext = @alignCast(@ptrCast(ctx));
|
|
||||||
|
|
||||||
self.transfer = null;
|
pub fn _text(self: *Response, page: *Page) !Env.Promise {
|
||||||
const promise_resolver: Env.PromiseResolver = .{
|
if (self.body_used) {
|
||||||
.js_context = self.js_ctx,
|
return error.TypeError;
|
||||||
.resolver = self.promise_resolver.castToPromiseResolver(),
|
}
|
||||||
|
|
||||||
|
const resolver = Env.PromiseResolver{
|
||||||
|
.js_context = page.main_context,
|
||||||
|
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||||
};
|
};
|
||||||
|
|
||||||
promise_resolver.reject(@errorName(err)) catch unreachable;
|
try resolver.resolve(self.body);
|
||||||
}
|
self.body_used = true;
|
||||||
}.errorCallback,
|
|
||||||
});
|
|
||||||
|
|
||||||
return resolver.promise();
|
return resolver.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ status: u16 = 0,
|
|||||||
headers: []const []const u8,
|
headers: []const []const u8,
|
||||||
mime: ?Mime = null,
|
mime: ?Mime = null,
|
||||||
body: []const u8,
|
body: []const u8,
|
||||||
|
body_used: bool = false,
|
||||||
|
redirected: bool = false,
|
||||||
|
|
||||||
const ResponseInput = union(enum) {
|
const ResponseInput = union(enum) {
|
||||||
string: []const u8,
|
string: []const u8,
|
||||||
@@ -72,17 +74,38 @@ pub fn get_ok(self: *const Response) bool {
|
|||||||
return self.status >= 200 and self.status <= 299;
|
return self.status >= 200 and self.status <= 299;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _text(self: *const Response, page: *Page) !Env.Promise {
|
pub fn get_bodyUsed(self: *const Response) bool {
|
||||||
|
return self.body_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_redirected(self: *const Response) bool {
|
||||||
|
return self.redirected;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_status(self: *const Response) u16 {
|
||||||
|
return self.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _bytes(self: *Response, page: *Page) !Env.Promise {
|
||||||
|
if (self.body_used) {
|
||||||
|
return error.TypeError;
|
||||||
|
}
|
||||||
|
|
||||||
const resolver = Env.PromiseResolver{
|
const resolver = Env.PromiseResolver{
|
||||||
.js_context = page.main_context,
|
.js_context = page.main_context,
|
||||||
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||||
};
|
};
|
||||||
|
|
||||||
try resolver.resolve(self.body);
|
try resolver.resolve(self.body);
|
||||||
|
self.body_used = true;
|
||||||
return resolver.promise();
|
return resolver.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _json(self: *const Response, page: *Page) !Env.Promise {
|
pub fn _json(self: *Response, page: *Page) !Env.Promise {
|
||||||
|
if (self.body_used) {
|
||||||
|
return error.TypeError;
|
||||||
|
}
|
||||||
|
|
||||||
const resolver = Env.PromiseResolver{
|
const resolver = Env.PromiseResolver{
|
||||||
.js_context = page.main_context,
|
.js_context = page.main_context,
|
||||||
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||||
@@ -99,6 +122,22 @@ pub fn _json(self: *const Response, page: *Page) !Env.Promise {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try resolver.resolve(p);
|
try resolver.resolve(p);
|
||||||
|
self.body_used = true;
|
||||||
|
return resolver.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _text(self: *Response, page: *Page) !Env.Promise {
|
||||||
|
if (self.body_used) {
|
||||||
|
return error.TypeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolver = Env.PromiseResolver{
|
||||||
|
.js_context = page.main_context,
|
||||||
|
.resolver = v8.PromiseResolver.init(page.main_context.v8_context),
|
||||||
|
};
|
||||||
|
|
||||||
|
try resolver.resolve(self.body);
|
||||||
|
self.body_used = true;
|
||||||
return resolver.promise();
|
return resolver.promise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user