use Response instead of LiveTransfer in callbacks

This commit is contained in:
Muki Kiboigo
2026-03-18 11:26:30 -07:00
parent 4228c664f7
commit 9315e8bcb8
6 changed files with 150 additions and 152 deletions

View File

@@ -376,8 +376,10 @@ fn fetchRobotsThenProcessRequest(self: *Client, robots_url: [:0]const u8, req: R
try entry.value_ptr.append(self.allocator, req); try entry.value_ptr.append(self.allocator, req);
} }
fn robotsHeaderCallback(transfer: *LiveTransfer) !bool { fn robotsHeaderCallback(response: Response) !bool {
const ctx: *RobotsRequestContext = @ptrCast(@alignCast(transfer.ctx)); const ctx: *RobotsRequestContext = @ptrCast(@alignCast(response.ctx));
// Robots callbacks only happen on real live requests.
const transfer = response.inner.live;
if (transfer.response_header) |hdr| { if (transfer.response_header) |hdr| {
log.debug(.browser, "robots status", .{ .status = hdr.status, .robots_url = ctx.robots_url }); log.debug(.browser, "robots status", .{ .status = hdr.status, .robots_url = ctx.robots_url });
@@ -391,8 +393,8 @@ fn robotsHeaderCallback(transfer: *LiveTransfer) !bool {
return true; return true;
} }
fn robotsDataCallback(transfer: *LiveTransfer, data: []const u8) !void { fn robotsDataCallback(response: Response, data: []const u8) !void {
const ctx: *RobotsRequestContext = @ptrCast(@alignCast(transfer.ctx)); const ctx: *RobotsRequestContext = @ptrCast(@alignCast(response.ctx));
try ctx.buffer.appendSlice(ctx.client.allocator, data); try ctx.buffer.appendSlice(ctx.client.allocator, data);
} }
@@ -611,7 +613,6 @@ fn makeTransfer(self: *Client, req: Request) !*LiveTransfer {
.id = id, .id = id,
.url = req.url, .url = req.url,
.req = req, .req = req,
.ctx = req.ctx,
.client = self, .client = self,
.max_response_size = self.network.config.httpMaxResponseSize(), .max_response_size = self.network.config.httpMaxResponseSize(),
}; };
@@ -634,9 +635,9 @@ fn requestFailed(transfer: *LiveTransfer, err: anyerror, comptime execute_callba
}); });
if (execute_callback) { if (execute_callback) {
transfer.req.error_callback(transfer.ctx, err); transfer.req.error_callback(transfer.req.ctx, err);
} else if (transfer.req.shutdown_callback) |cb| { } else if (transfer.req.shutdown_callback) |cb| {
cb(transfer.ctx); cb(transfer.req.ctx);
} }
} }
@@ -743,7 +744,7 @@ fn makeRequest(self: *Client, conn: *Net.Connection, transfer: *LiveTransfer) an
}; };
if (req.start_callback) |cb| { if (req.start_callback) |cb| {
cb(transfer) catch |err| { cb(Response.fromLive(transfer)) catch |err| {
transfer.deinit(); transfer.deinit();
return err; return err;
}; };
@@ -875,7 +876,7 @@ fn processMessages(self: *Client) !bool {
break :blk; break :blk;
} }
} }
transfer.req.done_callback(transfer.ctx) catch |err| { transfer.req.done_callback(transfer.req.ctx) catch |err| {
// transfer isn't valid at this point, don't use it. // transfer isn't valid at this point, don't use it.
log.err(.http, "done_callback", .{ .err = err }); log.err(.http, "done_callback", .{ .err = err });
requestFailed(transfer, err, true); requestFailed(transfer, err, true);
@@ -962,9 +963,9 @@ pub const Request = struct {
// arbitrary data that can be associated with this request // arbitrary data that can be associated with this request
ctx: *anyopaque = undefined, ctx: *anyopaque = undefined,
start_callback: ?*const fn (transfer: *LiveTransfer) anyerror!void = null, start_callback: ?*const fn (response: Response) anyerror!void = null,
header_callback: *const fn (transfer: *LiveTransfer) anyerror!bool, header_callback: *const fn (response: Response) anyerror!bool,
data_callback: *const fn (transfer: *LiveTransfer, data: []const u8) anyerror!void, data_callback: *const fn (response: Response, data: []const u8) anyerror!void,
done_callback: *const fn (ctx: *anyopaque) anyerror!void, done_callback: *const fn (ctx: *anyopaque) anyerror!void,
error_callback: *const fn (ctx: *anyopaque, err: anyerror) void, error_callback: *const fn (ctx: *anyopaque, err: anyerror) void,
shutdown_callback: ?*const fn (ctx: *anyopaque) void = null, shutdown_callback: ?*const fn (ctx: *anyopaque) void = null,
@@ -992,40 +993,41 @@ pub const Request = struct {
const AuthChallenge = Net.AuthChallenge; const AuthChallenge = Net.AuthChallenge;
pub const Transfer = struct { pub const Response = struct {
ctx: *anyopaque,
inner: union(enum) { inner: union(enum) {
live: LiveTransfer, live: *LiveTransfer,
}, },
pub fn fromLive(transfer: *LiveTransfer) Transfer { pub fn fromLive(transfer: *LiveTransfer) Response {
return .{ .ctx = transfer.req.ctx, .inner = .{ .live = transfer } }; return .{ .ctx = transfer.req.ctx, .inner = .{ .live = transfer } };
} }
pub fn status(self: Transfer) ?u16 { pub fn status(self: Response) ?u16 {
return switch (self.inner) { return switch (self.inner) {
.live => |live| if (live.response_header) |rh| rh.status else null, .live => |live| if (live.response_header) |rh| rh.status else null,
}; };
} }
pub fn contentType(self: Transfer) ?[]const u8 { pub fn contentType(self: Response) ?[]const u8 {
return switch (self.inner) { return switch (self.inner) {
.live => |live| if (live.response_header) |*rh| rh.contentType() else null, .live => |live| if (live.response_header) |*rh| rh.contentType() else null,
}; };
} }
pub fn contentLength(self: Transfer) ?u32 { pub fn contentLength(self: Response) ?u32 {
return switch (self.inner) { return switch (self.inner) {
.live => |live| live.getContentLength(), .live => |live| live.getContentLength(),
}; };
} }
pub fn redirectCount(self: Transfer) ?u32 { pub fn redirectCount(self: Response) ?u32 {
return switch (self.inner) { return switch (self.inner) {
.live => |live| if (live.response_header) |rh| rh.redirect_count else null, .live => |live| if (live.response_header) |rh| rh.redirect_count else null,
}; };
} }
pub fn url(self: Transfer) [:0]const u8 { pub fn url(self: Response) [:0]const u8 {
return switch (self.inner) { return switch (self.inner) {
.live => |live| live.url, .live => |live| live.url,
}; };
@@ -1033,13 +1035,13 @@ pub const Transfer = struct {
// TODO: Headers Iterator. // TODO: Headers Iterator.
pub fn abort(self: Transfer, err: anyerror) void { pub fn abort(self: Response, err: anyerror) void {
switch (self.inner) { switch (self.inner) {
.live => |live| live.abort(err), .live => |live| live.abort(err),
} }
} }
pub fn terminate(self: Transfer) void { pub fn terminate(self: Response) void {
switch (self.inner) { switch (self.inner) {
.live => |live| live.terminate(), .live => |live| live.terminate(),
} }
@@ -1047,7 +1049,6 @@ pub const Transfer = struct {
}; };
pub const LiveTransfer = struct { pub const LiveTransfer = struct {
ctx: *anyopaque,
arena: ArenaAllocator, arena: ArenaAllocator,
id: u32 = 0, id: u32 = 0,
req: Request, req: Request,
@@ -1215,7 +1216,7 @@ pub const LiveTransfer = struct {
self.client.endTransfer(self); self.client.endTransfer(self);
} }
if (self.req.shutdown_callback) |cb| { if (self.req.shutdown_callback) |cb| {
cb(self.ctx); cb(self.req.ctx);
} }
self.deinit(); self.deinit();
} }
@@ -1317,7 +1318,7 @@ pub const LiveTransfer = struct {
} }
} }
const proceed = transfer.req.header_callback(transfer) catch |err| { const proceed = transfer.req.header_callback(Response.fromLive(transfer)) catch |err| {
log.err(.http, "header_callback", .{ .err = err, .req = transfer }); log.err(.http, "header_callback", .{ .err = err, .req = transfer });
return err; return err;
}; };
@@ -1482,7 +1483,7 @@ pub const LiveTransfer = struct {
} }
const chunk = buffer[0..chunk_len]; const chunk = buffer[0..chunk_len];
transfer.req.data_callback(transfer, chunk) catch |err| { transfer.req.data_callback(Response.fromLive(transfer), chunk) catch |err| {
log.err(.http, "data_callback", .{ .err = err, .req = transfer }); log.err(.http, "data_callback", .{ .err = err, .req = transfer });
return Net.writefunc_error; return Net.writefunc_error;
}; };
@@ -1534,7 +1535,7 @@ pub const LiveTransfer = struct {
fn _fulfill(transfer: *LiveTransfer, status: u16, headers: []const Net.Header, body: ?[]const u8) !void { fn _fulfill(transfer: *LiveTransfer, status: u16, headers: []const Net.Header, body: ?[]const u8) !void {
const req = &transfer.req; const req = &transfer.req;
if (req.start_callback) |cb| { if (req.start_callback) |cb| {
try cb(transfer); try cb(Response.fromLive(transfer));
} }
transfer.response_header = .{ transfer.response_header = .{
@@ -1553,13 +1554,13 @@ pub const LiveTransfer = struct {
} }
lp.assert(transfer._header_done_called == false, "Transfer.fulfill header_done_called", .{}); lp.assert(transfer._header_done_called == false, "Transfer.fulfill header_done_called", .{});
if (try req.header_callback(transfer) == false) { if (try req.header_callback(Response.fromLive(transfer)) == false) {
transfer.abort(error.Abort); transfer.abort(error.Abort);
return; return;
} }
if (body) |b| { if (body) |b| {
try req.data_callback(transfer, b); try req.data_callback(Response.fromLive(transfer), b);
} }
try req.done_callback(req.ctx); try req.done_callback(req.ctx);

View File

@@ -821,12 +821,10 @@ fn notifyParentLoadComplete(self: *Page) void {
parent.iframeCompletedLoading(self.iframe.?); parent.iframeCompletedLoading(self.iframe.?);
} }
fn pageHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool { fn pageHeaderDoneCallback(response: HttpClient.Response) !bool {
var self: *Page = @ptrCast(@alignCast(transfer.ctx)); var self: *Page = @ptrCast(@alignCast(response.ctx));
const header = &transfer.response_header.?; const response_url = response.url();
const response_url = std.mem.span(header.url);
if (std.mem.eql(u8, response_url, self.url) == false) { if (std.mem.eql(u8, response_url, self.url) == false) {
// would be different than self.url in the case of a redirect // would be different than self.url in the case of a redirect
self.url = try self.arena.dupeZ(u8, response_url); self.url = try self.arena.dupeZ(u8, response_url);
@@ -840,8 +838,8 @@ fn pageHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool {
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
log.debug(.page, "navigate header", .{ log.debug(.page, "navigate header", .{
.url = self.url, .url = self.url,
.status = header.status, .status = response.status(),
.content_type = header.contentType(), .content_type = response.contentType(),
.type = self._type, .type = self._type,
}); });
} }
@@ -849,14 +847,14 @@ fn pageHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool {
return true; return true;
} }
fn pageDataCallback(transfer: *HttpClient.LiveTransfer, data: []const u8) !void { fn pageDataCallback(response: HttpClient.Response, data: []const u8) !void {
var self: *Page = @ptrCast(@alignCast(transfer.ctx)); var self: *Page = @ptrCast(@alignCast(response.ctx));
if (self._parse_state == .pre) { if (self._parse_state == .pre) {
// we lazily do this, because we might need the first chunk of data // we lazily do this, because we might need the first chunk of data
// to sniff the content type // to sniff the content type
var mime: Mime = blk: { var mime: Mime = blk: {
if (transfer.response_header.?.contentType()) |ct| { if (response.contentType()) |ct| {
break :blk try Mime.parse(ct); break :blk try Mime.parse(ct);
} }
break :blk Mime.sniff(data); break :blk Mime.sniff(data);

View File

@@ -694,85 +694,85 @@ pub const Script = struct {
self.manager.page.releaseArena(self.arena); self.manager.page.releaseArena(self.arena);
} }
fn startCallback(transfer: *HttpClient.LiveTransfer) !void { fn startCallback(response: HttpClient.Response) !void {
log.debug(.http, "script fetch start", .{ .req = transfer }); log.debug(.http, "script fetch start", .{ .req = response });
} }
fn headerCallback(transfer: *HttpClient.LiveTransfer) !bool { fn headerCallback(response: HttpClient.Response) !bool {
const self: *Script = @ptrCast(@alignCast(transfer.ctx)); const self: *Script = @ptrCast(@alignCast(response.ctx));
const header = &transfer.response_header.?;
self.status = header.status; self.status = response.status().?;
if (header.status != 200) { if (response.status() != 200) {
log.info(.http, "script header", .{ log.info(.http, "script header", .{
.req = transfer, .req = response,
.status = header.status, .status = response.status(),
.content_type = header.contentType(), .content_type = response.contentType(),
}); });
return false; return false;
} }
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
log.debug(.http, "script header", .{ log.debug(.http, "script header", .{
.req = transfer, .req = response,
.status = header.status, .status = response.status(),
.content_type = header.contentType(), .content_type = response.contentType(),
}); });
} }
{ // {
// temp debug, trying to figure out why the next assert sometimes // // temp debug, trying to figure out why the next assert sometimes
// fails. Is the buffer just corrupt or is headerCallback really // // fails. Is the buffer just corrupt or is headerCallback really
// being called twice? // // being called twice?
lp.assert(self.header_callback_called == false, "ScriptManager.Header recall", .{ // lp.assert(self.header_callback_called == false, "ScriptManager.Header recall", .{
.m = @tagName(std.meta.activeTag(self.mode)), // .m = @tagName(std.meta.activeTag(self.mode)),
.a1 = self.debug_transfer_id, // .a1 = self.debug_transfer_id,
.a2 = self.debug_transfer_tries, // .a2 = self.debug_transfer_tries,
.a3 = self.debug_transfer_aborted, // .a3 = self.debug_transfer_aborted,
.a4 = self.debug_transfer_bytes_received, // .a4 = self.debug_transfer_bytes_received,
.a5 = self.debug_transfer_notified_fail, // .a5 = self.debug_transfer_notified_fail,
.a6 = self.debug_transfer_redirecting, // .a6 = self.debug_transfer_redirecting,
.a7 = self.debug_transfer_intercept_state, // .a7 = self.debug_transfer_intercept_state,
.a8 = self.debug_transfer_auth_challenge, // .a8 = self.debug_transfer_auth_challenge,
.a9 = self.debug_transfer_easy_id, // .a9 = self.debug_transfer_easy_id,
.b1 = transfer.id, // .b1 = transfer.id,
.b2 = transfer._tries, // .b2 = transfer._tries,
.b3 = transfer.aborted, // .b3 = transfer.aborted,
.b4 = transfer.bytes_received, // .b4 = transfer.bytes_received,
.b5 = transfer._notified_fail, // .b5 = transfer._notified_fail,
.b6 = transfer._redirecting, // .b6 = transfer._redirecting,
.b7 = @intFromEnum(transfer._intercept_state), // .b7 = @intFromEnum(transfer._intercept_state),
.b8 = transfer._auth_challenge != null, // .b8 = transfer._auth_challenge != null,
.b9 = if (transfer._conn) |c| @intFromPtr(c.easy) else 0, // .b9 = if (transfer._conn) |c| @intFromPtr(c.easy) else 0,
}); // });
self.header_callback_called = true; // self.header_callback_called = true;
self.debug_transfer_id = transfer.id; // self.debug_transfer_id = transfer.id;
self.debug_transfer_tries = transfer._tries; // self.debug_transfer_tries = transfer._tries;
self.debug_transfer_aborted = transfer.aborted; // self.debug_transfer_aborted = transfer.aborted;
self.debug_transfer_bytes_received = transfer.bytes_received; // self.debug_transfer_bytes_received = transfer.bytes_received;
self.debug_transfer_notified_fail = transfer._notified_fail; // self.debug_transfer_notified_fail = transfer._notified_fail;
self.debug_transfer_redirecting = transfer._redirecting; // self.debug_transfer_redirecting = transfer._redirecting;
self.debug_transfer_intercept_state = @intFromEnum(transfer._intercept_state); // self.debug_transfer_intercept_state = @intFromEnum(transfer._intercept_state);
self.debug_transfer_auth_challenge = transfer._auth_challenge != null; // self.debug_transfer_auth_challenge = transfer._auth_challenge != null;
self.debug_transfer_easy_id = if (transfer._conn) |c| @intFromPtr(c.easy) else 0; // self.debug_transfer_easy_id = if (transfer._conn) |c| @intFromPtr(c.easy) else 0;
} // }
lp.assert(self.source.remote.capacity == 0, "ScriptManager.Header buffer", .{ .capacity = self.source.remote.capacity }); lp.assert(self.source.remote.capacity == 0, "ScriptManager.Header buffer", .{ .capacity = self.source.remote.capacity });
var buffer: std.ArrayList(u8) = .empty; var buffer: std.ArrayList(u8) = .empty;
if (transfer.getContentLength()) |cl| { if (response.contentLength()) |cl| {
try buffer.ensureTotalCapacity(self.arena, cl); try buffer.ensureTotalCapacity(self.arena, cl);
} }
self.source = .{ .remote = buffer }; self.source = .{ .remote = buffer };
return true; return true;
} }
fn dataCallback(transfer: *HttpClient.LiveTransfer, data: []const u8) !void { fn dataCallback(response: HttpClient.Response, data: []const u8) !void {
const self: *Script = @ptrCast(@alignCast(transfer.ctx)); const self: *Script = @ptrCast(@alignCast(response.ctx));
self._dataCallback(transfer, data) catch |err| { self._dataCallback(response, data) catch |err| {
log.err(.http, "SM.dataCallback", .{ .err = err, .transfer = transfer, .len = data.len }); log.err(.http, "SM.dataCallback", .{ .err = err, .transfer = response, .len = data.len });
return err; return err;
}; };
} }
fn _dataCallback(self: *Script, _: *HttpClient.LiveTransfer, data: []const u8) !void { fn _dataCallback(self: *Script, _: HttpClient.Response, data: []const u8) !void {
try self.source.remote.appendSlice(self.arena, data); try self.source.remote.appendSlice(self.arena, data);
} }

View File

@@ -126,16 +126,16 @@ fn handleBlobUrl(url: []const u8, resolver: js.PromiseResolver, page: *Page) !js
return resolver.promise(); return resolver.promise();
} }
fn httpStartCallback(transfer: *HttpClient.LiveTransfer) !void { fn httpStartCallback(response: HttpClient.Response) !void {
const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); const self: *Fetch = @ptrCast(@alignCast(response.ctx));
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
log.debug(.http, "request start", .{ .url = self._url, .source = "fetch" }); log.debug(.http, "request start", .{ .url = self._url, .source = "fetch" });
} }
self._response._transfer = transfer; self._response._http_response = response;
} }
fn httpHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool { fn httpHeaderDoneCallback(response: HttpClient.Response) !bool {
const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); const self: *Fetch = @ptrCast(@alignCast(response.ctx));
if (self._signal) |signal| { if (self._signal) |signal| {
if (signal._aborted) { if (signal._aborted) {
@@ -144,25 +144,24 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool {
} }
const arena = self._response._arena; const arena = self._response._arena;
if (transfer.getContentLength()) |cl| { if (response.contentLength()) |cl| {
try self._buf.ensureTotalCapacity(arena, cl); try self._buf.ensureTotalCapacity(arena, cl);
} }
const res = self._response; const res = self._response;
const header = transfer.response_header.?;
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
log.debug(.http, "request header", .{ log.debug(.http, "request header", .{
.source = "fetch", .source = "fetch",
.url = self._url, .url = self._url,
.status = header.status, .status = response.status(),
}); });
} }
res._status = header.status; res._status = response.status().?;
res._status_text = std.http.Status.phrase(@enumFromInt(header.status)) orelse ""; res._status_text = std.http.Status.phrase(@enumFromInt(response.status().?)) orelse "";
res._url = try arena.dupeZ(u8, std.mem.span(header.url)); res._url = try arena.dupeZ(u8, response.url());
res._is_redirected = header.redirect_count > 0; res._is_redirected = response.redirectCount().? > 0;
// Determine response type based on origin comparison // Determine response type based on origin comparison
const page_origin = URL.getOrigin(arena, self._page.url) catch null; const page_origin = URL.getOrigin(arena, self._page.url) catch null;
@@ -182,16 +181,17 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool {
res._type = .basic; res._type = .basic;
} }
var it = transfer.responseHeaderIterator(); // TODO: Header Iterator
while (it.next()) |hdr| { // var it = transfer.responseHeaderIterator();
try res._headers.append(hdr.name, hdr.value, self._page); // while (it.next()) |hdr| {
} // try res._headers.append(hdr.name, hdr.value, self._page);
// }
return true; return true;
} }
fn httpDataCallback(transfer: *HttpClient.LiveTransfer, data: []const u8) !void { fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void {
const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); const self: *Fetch = @ptrCast(@alignCast(response.ctx));
// Check if aborted // Check if aborted
if (self._signal) |signal| { if (self._signal) |signal| {
@@ -206,7 +206,7 @@ fn httpDataCallback(transfer: *HttpClient.LiveTransfer, data: []const u8) !void
fn httpDoneCallback(ctx: *anyopaque) !void { fn httpDoneCallback(ctx: *anyopaque) !void {
const self: *Fetch = @ptrCast(@alignCast(ctx)); const self: *Fetch = @ptrCast(@alignCast(ctx));
var response = self._response; var response = self._response;
response._transfer = null; response._http_response = null;
response._body = self._buf.items; response._body = self._buf.items;
log.info(.http, "request complete", .{ log.info(.http, "request complete", .{
@@ -229,7 +229,7 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
const self: *Fetch = @ptrCast(@alignCast(ctx)); const self: *Fetch = @ptrCast(@alignCast(ctx));
var response = self._response; var response = self._response;
response._transfer = null; response._http_response = null;
// the response is only passed on v8 on success, if we're here, it's safe to // the response is only passed on v8 on success, if we're here, it's safe to
// clear this. (defer since `self is in the response's arena). // clear this. (defer since `self is in the response's arena).
@@ -255,7 +255,7 @@ fn httpShutdownCallback(ctx: *anyopaque) void {
if (self._owns_response) { if (self._owns_response) {
var response = self._response; var response = self._response;
response._transfer = null; response._http_response = null;
response.deinit(true, self._page._session); response.deinit(true, self._page._session);
// Do not access `self` after this point: the Fetch struct was // Do not access `self` after this point: the Fetch struct was
// allocated from response._arena which has been released. // allocated from response._arena which has been released.

View File

@@ -46,7 +46,7 @@ _type: Type,
_status_text: []const u8, _status_text: []const u8,
_url: [:0]const u8, _url: [:0]const u8,
_is_redirected: bool, _is_redirected: bool,
_transfer: ?*HttpClient.LiveTransfer = null, _http_response: ?HttpClient.Response = null,
const InitOpts = struct { const InitOpts = struct {
status: u16 = 200, status: u16 = 200,
@@ -79,13 +79,13 @@ pub fn init(body_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*Response {
} }
pub fn deinit(self: *Response, shutdown: bool, session: *Session) void { pub fn deinit(self: *Response, shutdown: bool, session: *Session) void {
if (self._transfer) |transfer| { if (self._http_response) |resp| {
if (shutdown) { if (shutdown) {
transfer.terminate(); resp.terminate();
} else { } else {
transfer.abort(error.Abort); resp.abort(error.Abort);
} }
self._transfer = null; self._http_response = null;
} }
session.releaseArena(self._arena); session.releaseArena(self._arena);
} }
@@ -185,7 +185,7 @@ pub fn clone(self: *const Response, page: *Page) !*Response {
._type = self._type, ._type = self._type,
._is_redirected = self._is_redirected, ._is_redirected = self._is_redirected,
._headers = try Headers.init(.{ .obj = self._headers }, page), ._headers = try Headers.init(.{ .obj = self._headers }, page),
._transfer = null, ._http_response = null,
}; };
return cloned; return cloned;
} }

View File

@@ -42,7 +42,7 @@ const XMLHttpRequest = @This();
_page: *Page, _page: *Page,
_proto: *XMLHttpRequestEventTarget, _proto: *XMLHttpRequestEventTarget,
_arena: Allocator, _arena: Allocator,
_transfer: ?*HttpClient.LiveTransfer = null, _http_response: ?HttpClient.Response = null,
_url: [:0]const u8 = "", _url: [:0]const u8 = "",
_method: net_http.Method = .GET, _method: net_http.Method = .GET,
@@ -97,13 +97,13 @@ pub fn init(page: *Page) !*XMLHttpRequest {
} }
pub fn deinit(self: *XMLHttpRequest, shutdown: bool, session: *Session) void { pub fn deinit(self: *XMLHttpRequest, shutdown: bool, session: *Session) void {
if (self._transfer) |transfer| { if (self._http_response) |transfer| {
if (shutdown) { if (shutdown) {
transfer.terminate(); transfer.terminate();
} else { } else {
transfer.abort(error.Abort); transfer.abort(error.Abort);
} }
self._transfer = null; self._http_response = null;
} }
if (self._on_ready_state_change) |func| { if (self._on_ready_state_change) |func| {
@@ -169,9 +169,9 @@ pub fn setWithCredentials(self: *XMLHttpRequest, value: bool) !void {
// TODO: url should be a union, as it can be multiple things // TODO: url should be a union, as it can be multiple things
pub fn open(self: *XMLHttpRequest, method_: []const u8, url: [:0]const u8) !void { pub fn open(self: *XMLHttpRequest, method_: []const u8, url: [:0]const u8) !void {
// Abort any in-progress request // Abort any in-progress request
if (self._transfer) |transfer| { if (self._http_response) |transfer| {
transfer.abort(error.Abort); transfer.abort(error.Abort);
self._transfer = null; self._http_response = null;
} }
// Reset internal state // Reset internal state
@@ -382,34 +382,32 @@ pub fn getResponseXML(self: *XMLHttpRequest, page: *Page) !?*Node.Document {
}; };
} }
fn httpStartCallback(transfer: *HttpClient.LiveTransfer) !void { fn httpStartCallback(response: HttpClient.Response) !void {
const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx));
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" }); log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" });
} }
self._transfer = transfer; self._http_response = response;
} }
fn httpHeaderCallback(transfer: *HttpClient.LiveTransfer, header: net_http.Header) !void { fn httpHeaderCallback(response: HttpClient.Response, header: net_http.Header) !void {
const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx));
const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ header.name, header.value }); const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ header.name, header.value });
try self._response_headers.append(self._arena, joined); try self._response_headers.append(self._arena, joined);
} }
fn httpHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool { fn httpHeaderDoneCallback(response: HttpClient.Response) !bool {
const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx));
const header = &transfer.response_header.?;
if (comptime IS_DEBUG) { if (comptime IS_DEBUG) {
log.debug(.http, "request header", .{ log.debug(.http, "request header", .{
.source = "xhr", .source = "xhr",
.url = self._url, .url = self._url,
.status = header.status, .status = response.status(),
}); });
} }
if (header.contentType()) |ct| { if (response.contentType()) |ct| {
self._response_mime = Mime.parse(ct) catch |e| { self._response_mime = Mime.parse(ct) catch |e| {
log.info(.http, "invalid content type", .{ log.info(.http, "invalid content type", .{
.content_Type = ct, .content_Type = ct,
@@ -420,18 +418,19 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool {
}; };
} }
var it = transfer.responseHeaderIterator(); // TODO: Header Iterator
while (it.next()) |hdr| { // var it = transfer.responseHeaderIterator();
const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ hdr.name, hdr.value }); // while (it.next()) |hdr| {
try self._response_headers.append(self._arena, joined); // const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ hdr.name, hdr.value });
} // try self._response_headers.append(self._arena, joined);
// }
self._response_status = header.status; self._response_status = response.status().?;
if (transfer.getContentLength()) |cl| { if (response.contentLength()) |cl| {
self._response_len = cl; self._response_len = cl;
try self._response_data.ensureTotalCapacity(self._arena, cl); try self._response_data.ensureTotalCapacity(self._arena, cl);
} }
self._response_url = try self._arena.dupeZ(u8, std.mem.span(header.url)); self._response_url = try self._arena.dupeZ(u8, response.url());
const page = self._page; const page = self._page;
@@ -446,8 +445,8 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.LiveTransfer) !bool {
return true; return true;
} }
fn httpDataCallback(transfer: *HttpClient.LiveTransfer, data: []const u8) !void { fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void {
const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx));
try self._response_data.appendSlice(self._arena, data); try self._response_data.appendSlice(self._arena, data);
const page = self._page; const page = self._page;
@@ -470,7 +469,7 @@ fn httpDoneCallback(ctx: *anyopaque) !void {
// Not that the request is done, the http/client will free the transfer // Not that the request is done, the http/client will free the transfer
// object. It isn't safe to keep it around. // object. It isn't safe to keep it around.
self._transfer = null; self._http_response = null;
const page = self._page; const page = self._page;
@@ -492,21 +491,21 @@ fn httpDoneCallback(ctx: *anyopaque) !void {
fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void { fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx));
// http client will close it after an error, it isn't safe to keep around // http client will close it after an error, it isn't safe to keep around
self._transfer = null; self._http_response = null;
self.handleError(err); self.handleError(err);
self._page.js.weakRef(self); self._page.js.weakRef(self);
} }
fn httpShutdownCallback(ctx: *anyopaque) void { fn httpShutdownCallback(ctx: *anyopaque) void {
const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx));
self._transfer = null; self._http_response = null;
} }
pub fn abort(self: *XMLHttpRequest) void { pub fn abort(self: *XMLHttpRequest) void {
self.handleError(error.Abort); self.handleError(error.Abort);
if (self._transfer) |transfer| { if (self._http_response) |transfer| {
transfer.abort(error.Abort); transfer.abort(error.Abort);
self._transfer = null; self._http_response = null;
} }
self._page.js.weakRef(self); self._page.js.weakRef(self);
} }