diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index c49d7e1f..f9f98a8f 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -399,8 +399,10 @@ fn fetchRobotsThenProcessRequest(self: *Client, robots_url: [:0]const u8, req: R try entry.value_ptr.append(self.allocator, req); } -fn robotsHeaderCallback(transfer: *Transfer) !bool { - const ctx: *RobotsRequestContext = @ptrCast(@alignCast(transfer.ctx)); +fn robotsHeaderCallback(response: Response) !bool { + const ctx: *RobotsRequestContext = @ptrCast(@alignCast(response.ctx)); + // Robots callbacks only happen on real live requests. + const transfer = response.inner.transfer; if (transfer.response_header) |hdr| { log.debug(.browser, "robots status", .{ .status = hdr.status, .robots_url = ctx.robots_url }); @@ -414,8 +416,8 @@ fn robotsHeaderCallback(transfer: *Transfer) !bool { return true; } -fn robotsDataCallback(transfer: *Transfer, data: []const u8) !void { - const ctx: *RobotsRequestContext = @ptrCast(@alignCast(transfer.ctx)); +fn robotsDataCallback(response: Response, data: []const u8) !void { + const ctx: *RobotsRequestContext = @ptrCast(@alignCast(response.ctx)); try ctx.buffer.appendSlice(ctx.client.allocator, data); } @@ -634,13 +636,43 @@ fn makeTransfer(self: *Client, req: Request) !*Transfer { .id = id, .url = req.url, .req = req, - .ctx = req.ctx, .client = self, .max_response_size = self.network.config.httpMaxResponseSize(), }; return transfer; } +fn requestFailed(transfer: *Transfer, err: anyerror, comptime execute_callback: bool) void { + if (transfer._notified_fail) { + // we can force a failed request within a callback, which will eventually + // result in this being called again in the more general loop. We do this + // because we can raise a more specific error inside a callback in some cases + return; + } + + transfer._notified_fail = true; + + transfer.req.notification.dispatch(.http_request_fail, &.{ + .transfer = transfer, + .err = err, + }); + + if (execute_callback) { + transfer.req.error_callback(transfer.req.ctx, err); + } else if (transfer.req.shutdown_callback) |cb| { + cb(transfer.req.ctx); + } +} + +// Same restriction as changeProxy. Should be ok since this is only called on +// BrowserContext deinit. +pub fn restoreOriginalProxy(self: *Client) !void { + try self.ensureNoActiveConnection(); + + self.http_proxy = self.network.config.httpProxy(); + self.use_proxy = self.http_proxy != null; +} + fn makeRequest(self: *Client, conn: *http.Connection, transfer: *Transfer) anyerror!void { { // Reset per-response state for retries (auth challenge, queue). @@ -674,7 +706,7 @@ fn makeRequest(self: *Client, conn: *http.Connection, transfer: *Transfer) anyer self.active += 1; if (transfer.req.start_callback) |cb| { - cb(transfer) catch |err| { + cb(Response.fromTransfer(transfer)) catch |err| { transfer.deinit(); return err; }; @@ -742,7 +774,10 @@ fn processOneMessage(self: *Client, msg: http.Handles.MultiMessage, transfer: *T // TODO give a way to configure the number of auth retries. if (transfer._auth_challenge != null and transfer._tries < 10) { var wait_for_interception = false; - transfer.req.notification.dispatch(.http_request_auth_required, &.{ .transfer = transfer, .wait_for_interception = &wait_for_interception }); + transfer.req.notification.dispatch( + .http_request_auth_required, + &.{ .transfer = transfer, .wait_for_interception = &wait_for_interception }, + ); if (wait_for_interception) { self.intercepted += 1; if (comptime IS_DEBUG) { @@ -844,7 +879,7 @@ fn processOneMessage(self: *Client, msg: http.Handles.MultiMessage, transfer: *T // Replay buffered body through user's data_callback. if (transfer._stream_buffer.items.len > 0) { const body = transfer._stream_buffer.items; - try transfer.req.data_callback(transfer, body); + try transfer.req.data_callback(Response.fromTransfer(transfer), body); transfer.req.notification.dispatch(.http_response_data, &.{ .data = body, @@ -861,7 +896,7 @@ fn processOneMessage(self: *Client, msg: http.Handles.MultiMessage, transfer: *T // will load more resources. transfer.releaseConn(); - try transfer.req.done_callback(transfer.ctx); + try transfer.req.done_callback(transfer.req.ctx); transfer.req.notification.dispatch(.http_request_done, &.{ .transfer = transfer, }); @@ -939,9 +974,9 @@ pub const Request = struct { // arbitrary data that can be associated with this request ctx: *anyopaque = undefined, - start_callback: ?*const fn (transfer: *Transfer) anyerror!void = null, - header_callback: *const fn (transfer: *Transfer) anyerror!bool, - data_callback: *const fn (transfer: *Transfer, data: []const u8) anyerror!void, + start_callback: ?*const fn (response: Response) anyerror!void = null, + header_callback: *const fn (response: Response) anyerror!bool, + data_callback: *const fn (response: Response, data: []const u8) anyerror!void, done_callback: *const fn (ctx: *anyopaque) anyerror!void, error_callback: *const fn (ctx: *anyopaque, err: anyerror) void, shutdown_callback: ?*const fn (ctx: *anyopaque) void = null, @@ -967,12 +1002,66 @@ pub const Request = struct { }; }; +pub const Response = struct { + ctx: *anyopaque, + inner: union(enum) { + transfer: *Transfer, + }, + + pub fn fromTransfer(transfer: *Transfer) Response { + return .{ .ctx = transfer.req.ctx, .inner = .{ .transfer = transfer } }; + } + + pub fn status(self: Response) ?u16 { + return switch (self.inner) { + .transfer => |t| if (t.response_header) |rh| rh.status else null, + }; + } + + pub fn contentType(self: Response) ?[]const u8 { + return switch (self.inner) { + .transfer => |t| if (t.response_header) |*rh| rh.contentType() else null, + }; + } + + pub fn contentLength(self: Response) ?u32 { + return switch (self.inner) { + .transfer => |t| t.getContentLength(), + }; + } + + pub fn redirectCount(self: Response) ?u32 { + return switch (self.inner) { + .transfer => |t| if (t.response_header) |rh| rh.redirect_count else null, + }; + } + + pub fn url(self: Response) [:0]const u8 { + return switch (self.inner) { + .transfer => |t| t.url, + }; + } + + // TODO: Headers Iterator. + + pub fn abort(self: Response, err: anyerror) void { + switch (self.inner) { + .transfer => |t| t.abort(err), + } + } + + pub fn terminate(self: Response) void { + switch (self.inner) { + .transfer => |t| t.terminate(), + } + } +}; + pub const Transfer = struct { arena: ArenaAllocator, id: u32 = 0, req: Request, url: [:0]const u8, - ctx: *anyopaque, // copied from req.ctx to make it easier for callback handlers client: *Client, // total bytes received in the response, including the response status line, // the headers, and the [encoded] body. @@ -1065,7 +1154,7 @@ pub const Transfer = struct { // as abort (doesn't send a notification, doesn't invoke an error callback) fn kill(self: *Transfer) void { if (self.req.shutdown_callback) |cb| { - cb(self.ctx); + cb(self.req.ctx); } if (self._performing or self.client.performing) { @@ -1101,7 +1190,7 @@ pub const Transfer = struct { }); if (execute_callback) { - self.req.error_callback(self.ctx, err); + self.req.error_callback(self.req.ctx, err); } else if (self.req.shutdown_callback) |cb| { cb(self.ctx); } @@ -1348,7 +1437,7 @@ pub const Transfer = struct { } } - const proceed = transfer.req.header_callback(transfer) catch |err| { + const proceed = transfer.req.header_callback(Response.fromTransfer(transfer)) catch |err| { log.err(.http, "header_callback", .{ .err = err, .req = transfer }); return err; }; @@ -1455,7 +1544,7 @@ pub const Transfer = struct { fn _fulfill(transfer: *Transfer, status: u16, headers: []const http.Header, body: ?[]const u8) !void { const req = &transfer.req; if (req.start_callback) |cb| { - try cb(transfer); + try cb(Response.fromTransfer(transfer)); } transfer.response_header = .{ @@ -1474,13 +1563,13 @@ pub const Transfer = struct { } 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.fromTransfer(transfer)) == false) { transfer.abort(error.Abort); return; } if (body) |b| { - try req.data_callback(transfer, b); + try req.data_callback(Response.fromTransfer(transfer), b); } try req.done_callback(req.ctx); @@ -1517,10 +1606,10 @@ pub const Transfer = struct { }; const Noop = struct { - fn headerCallback(_: *Transfer) !bool { + fn headerCallback(_: Response) !bool { return true; } - fn dataCallback(_: *Transfer, _: []const u8) !void {} + fn dataCallback(_: Response, _: []const u8) !void {} fn doneCallback(_: *anyopaque) !void {} fn errorCallback(_: *anyopaque, _: anyerror) void {} }; diff --git a/src/browser/Page.zig b/src/browser/Page.zig index 314d20f0..c5947c0a 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -854,12 +854,10 @@ fn notifyParentLoadComplete(self: *Page) void { parent.iframeCompletedLoading(self.iframe.?); } -fn pageHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { - var self: *Page = @ptrCast(@alignCast(transfer.ctx)); +fn pageHeaderDoneCallback(response: HttpClient.Response) !bool { + var self: *Page = @ptrCast(@alignCast(response.ctx)); - const header = &transfer.response_header.?; - - const response_url = std.mem.span(header.url); + const response_url = response.url(); if (std.mem.eql(u8, response_url, self.url) == false) { // would be different than self.url in the case of a redirect self.url = try self.arena.dupeZ(u8, response_url); @@ -873,8 +871,8 @@ fn pageHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { if (comptime IS_DEBUG) { log.debug(.page, "navigate header", .{ .url = self.url, - .status = header.status, - .content_type = header.contentType(), + .status = response.status(), + .content_type = response.contentType(), .type = self._type, }); } @@ -882,14 +880,14 @@ fn pageHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { return true; } -fn pageDataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void { - var self: *Page = @ptrCast(@alignCast(transfer.ctx)); +fn pageDataCallback(response: HttpClient.Response, data: []const u8) !void { + var self: *Page = @ptrCast(@alignCast(response.ctx)); if (self._parse_state == .pre) { // we lazily do this, because we might need the first chunk of data // to sniff the content type var mime: Mime = blk: { - if (transfer.response_header.?.contentType()) |ct| { + if (response.contentType()) |ct| { break :blk try Mime.parse(ct); } break :blk Mime.sniff(data); diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index 0ebf222c..e2fc8563 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -694,82 +694,85 @@ pub const Script = struct { self.manager.page.releaseArena(self.arena); } - fn startCallback(transfer: *HttpClient.Transfer) !void { - log.debug(.http, "script fetch start", .{ .req = transfer }); + fn startCallback(response: HttpClient.Response) !void { + log.debug(.http, "script fetch start", .{ .req = response }); } - fn headerCallback(transfer: *HttpClient.Transfer) !bool { - const self: *Script = @ptrCast(@alignCast(transfer.ctx)); - const header = &transfer.response_header.?; - self.status = header.status; - if (header.status != 200) { + fn headerCallback(response: HttpClient.Response) !bool { + const self: *Script = @ptrCast(@alignCast(response.ctx)); + + self.status = response.status().?; + if (response.status() != 200) { log.info(.http, "script header", .{ - .req = transfer, - .status = header.status, - .content_type = header.contentType(), + .req = response, + .status = response.status(), + .content_type = response.contentType(), }); return false; } if (comptime IS_DEBUG) { log.debug(.http, "script header", .{ - .req = transfer, - .status = header.status, - .content_type = header.contentType(), + .req = response, + .status = response.status(), + .content_type = response.contentType(), }); } - { - // temp debug, trying to figure out why the next assert sometimes - // fails. Is the buffer just corrupt or is headerCallback really - // being called twice? - lp.assert(self.header_callback_called == false, "ScriptManager.Header recall", .{ - .m = @tagName(std.meta.activeTag(self.mode)), - .a1 = self.debug_transfer_id, - .a2 = self.debug_transfer_tries, - .a3 = self.debug_transfer_aborted, - .a4 = self.debug_transfer_bytes_received, - .a5 = self.debug_transfer_notified_fail, - .a7 = self.debug_transfer_intercept_state, - .a8 = self.debug_transfer_auth_challenge, - .a9 = self.debug_transfer_easy_id, - .b1 = transfer.id, - .b2 = transfer._tries, - .b3 = transfer.aborted, - .b4 = transfer.bytes_received, - .b5 = transfer._notified_fail, - .b7 = @intFromEnum(transfer._intercept_state), - .b8 = transfer._auth_challenge != null, - .b9 = if (transfer._conn) |c| @intFromPtr(c._easy) else 0, - }); - self.header_callback_called = true; - self.debug_transfer_id = transfer.id; - self.debug_transfer_tries = transfer._tries; - self.debug_transfer_aborted = transfer.aborted; - self.debug_transfer_bytes_received = transfer.bytes_received; - self.debug_transfer_notified_fail = transfer._notified_fail; - self.debug_transfer_intercept_state = @intFromEnum(transfer._intercept_state); - self.debug_transfer_auth_challenge = transfer._auth_challenge != null; - self.debug_transfer_easy_id = if (transfer._conn) |c| @intFromPtr(c._easy) else 0; + switch (response.inner) { + .transfer => |transfer| { + // temp debug, trying to figure out why the next assert sometimes + // fails. Is the buffer just corrupt or is headerCallback really + // being called twice? + lp.assert(self.header_callback_called == false, "ScriptManager.Header recall", .{ + .m = @tagName(std.meta.activeTag(self.mode)), + .a1 = self.debug_transfer_id, + .a2 = self.debug_transfer_tries, + .a3 = self.debug_transfer_aborted, + .a4 = self.debug_transfer_bytes_received, + .a5 = self.debug_transfer_notified_fail, + .a7 = self.debug_transfer_intercept_state, + .a8 = self.debug_transfer_auth_challenge, + .a9 = self.debug_transfer_easy_id, + .b1 = transfer.id, + .b2 = transfer._tries, + .b3 = transfer.aborted, + .b4 = transfer.bytes_received, + .b5 = transfer._notified_fail, + .b7 = @intFromEnum(transfer._intercept_state), + .b8 = transfer._auth_challenge != null, + .b9 = if (transfer._conn) |c| @intFromPtr(c._easy) else 0, + }); + self.header_callback_called = true; + self.debug_transfer_id = transfer.id; + self.debug_transfer_tries = transfer._tries; + self.debug_transfer_aborted = transfer.aborted; + self.debug_transfer_bytes_received = transfer.bytes_received; + self.debug_transfer_notified_fail = transfer._notified_fail; + self.debug_transfer_intercept_state = @intFromEnum(transfer._intercept_state); + self.debug_transfer_auth_challenge = transfer._auth_challenge != null; + 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 }); var buffer: std.ArrayList(u8) = .empty; - if (transfer.getContentLength()) |cl| { + if (response.contentLength()) |cl| { try buffer.ensureTotalCapacity(self.arena, cl); } self.source = .{ .remote = buffer }; return true; } - fn dataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void { - const self: *Script = @ptrCast(@alignCast(transfer.ctx)); - self._dataCallback(transfer, data) catch |err| { - log.err(.http, "SM.dataCallback", .{ .err = err, .transfer = transfer, .len = data.len }); + fn dataCallback(response: HttpClient.Response, data: []const u8) !void { + const self: *Script = @ptrCast(@alignCast(response.ctx)); + self._dataCallback(response, data) catch |err| { + log.err(.http, "SM.dataCallback", .{ .err = err, .transfer = response, .len = data.len }); return err; }; } - fn _dataCallback(self: *Script, _: *HttpClient.Transfer, data: []const u8) !void { + + fn _dataCallback(self: *Script, _: HttpClient.Response, data: []const u8) !void { try self.source.remote.appendSlice(self.arena, data); } diff --git a/src/browser/webapi/net/Fetch.zig b/src/browser/webapi/net/Fetch.zig index acda1f86..dc42f9b2 100644 --- a/src/browser/webapi/net/Fetch.zig +++ b/src/browser/webapi/net/Fetch.zig @@ -127,16 +127,16 @@ fn handleBlobUrl(url: []const u8, resolver: js.PromiseResolver, page: *Page) !js return resolver.promise(); } -fn httpStartCallback(transfer: *HttpClient.Transfer) !void { - const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); +fn httpStartCallback(response: HttpClient.Response) !void { + const self: *Fetch = @ptrCast(@alignCast(response.ctx)); if (comptime IS_DEBUG) { log.debug(.http, "request start", .{ .url = self._url, .source = "fetch" }); } - self._response._transfer = transfer; + self._response._http_response = response; } -fn httpHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { - const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); +fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { + const self: *Fetch = @ptrCast(@alignCast(response.ctx)); if (self._signal) |signal| { if (signal._aborted) { @@ -145,25 +145,24 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { } const arena = self._response._arena; - if (transfer.getContentLength()) |cl| { + if (response.contentLength()) |cl| { try self._buf.ensureTotalCapacity(arena, cl); } const res = self._response; - const header = transfer.response_header.?; if (comptime IS_DEBUG) { log.debug(.http, "request header", .{ .source = "fetch", .url = self._url, - .status = header.status, + .status = response.status(), }); } - res._status = header.status; - res._status_text = std.http.Status.phrase(@enumFromInt(header.status)) orelse ""; - res._url = try arena.dupeZ(u8, std.mem.span(header.url)); - res._is_redirected = header.redirect_count > 0; + res._status = response.status().?; + res._status_text = std.http.Status.phrase(@enumFromInt(response.status().?)) orelse ""; + res._url = try arena.dupeZ(u8, response.url()); + res._is_redirected = response.redirectCount().? > 0; // Determine response type based on origin comparison const page_origin = URL.getOrigin(arena, self._page.url) catch null; @@ -183,16 +182,17 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { res._type = .basic; } - var it = transfer.responseHeaderIterator(); - while (it.next()) |hdr| { - try res._headers.append(hdr.name, hdr.value, self._page); - } + // TODO: Header Iterator + // var it = transfer.responseHeaderIterator(); + // while (it.next()) |hdr| { + // try res._headers.append(hdr.name, hdr.value, self._page); + // } return true; } -fn httpDataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void { - const self: *Fetch = @ptrCast(@alignCast(transfer.ctx)); +fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void { + const self: *Fetch = @ptrCast(@alignCast(response.ctx)); // Check if aborted if (self._signal) |signal| { @@ -207,7 +207,7 @@ fn httpDataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void { fn httpDoneCallback(ctx: *anyopaque) !void { const self: *Fetch = @ptrCast(@alignCast(ctx)); var response = self._response; - response._transfer = null; + response._http_response = null; response._body = self._buf.items; log.info(.http, "request complete", .{ @@ -230,7 +230,7 @@ fn httpErrorCallback(ctx: *anyopaque, _: anyerror) void { const self: *Fetch = @ptrCast(@alignCast(ctx)); 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 // clear this. (defer since `self is in the response's arena). @@ -256,7 +256,7 @@ fn httpShutdownCallback(ctx: *anyopaque) void { if (self._owns_response) { var response = self._response; - response._transfer = null; + response._http_response = null; response.deinit(self._page._session); // Do not access `self` after this point: the Fetch struct was // allocated from response._arena which has been released. diff --git a/src/browser/webapi/net/Response.zig b/src/browser/webapi/net/Response.zig index b9df006e..e4fbd46d 100644 --- a/src/browser/webapi/net/Response.zig +++ b/src/browser/webapi/net/Response.zig @@ -48,7 +48,7 @@ _type: Type, _status_text: []const u8, _url: [:0]const u8, _is_redirected: bool, -_transfer: ?*HttpClient.Transfer = null, +_http_response: ?HttpClient.Response = null, const InitOpts = struct { status: u16 = 200, @@ -81,9 +81,9 @@ pub fn init(body_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*Response { } pub fn deinit(self: *Response, session: *Session) void { - if (self._transfer) |transfer| { - transfer.abort(error.Abort); - self._transfer = null; + if (self._http_response) |resp| { + resp.abort(error.Abort); + self._http_response = null; } session.releaseArena(self._arena); } @@ -191,7 +191,7 @@ pub fn clone(self: *const Response, page: *Page) !*Response { ._type = self._type, ._is_redirected = self._is_redirected, ._headers = try Headers.init(.{ .obj = self._headers }, page), - ._transfer = null, + ._http_response = null, }; return cloned; } diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 22587347..61ac724c 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -43,7 +43,7 @@ _rc: lp.RC(u8) = .{}, _page: *Page, _proto: *XMLHttpRequestEventTarget, _arena: Allocator, -_transfer: ?*HttpClient.Transfer = null, +_http_response: ?HttpClient.Response = null, _url: [:0]const u8 = "", _method: net_http.Method = .GET, @@ -99,9 +99,9 @@ pub fn init(page: *Page) !*XMLHttpRequest { } pub fn deinit(self: *XMLHttpRequest, session: *Session) void { - if (self._transfer) |transfer| { - transfer.abort(error.Abort); - self._transfer = null; + if (self._http_response) |resp| { + resp.abort(error.Abort); + self._http_response = null; } if (self._on_ready_state_change) |func| { @@ -175,9 +175,9 @@ pub fn setWithCredentials(self: *XMLHttpRequest, value: bool) !void { // 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 { // Abort any in-progress request - if (self._transfer) |transfer| { + if (self._http_response) |transfer| { transfer.abort(error.Abort); - self._transfer = null; + self._http_response = null; } // Reset internal state @@ -387,35 +387,33 @@ pub fn getResponseXML(self: *XMLHttpRequest, page: *Page) !?*Node.Document { }; } -fn httpStartCallback(transfer: *HttpClient.Transfer) !void { - const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); +fn httpStartCallback(response: HttpClient.Response) !void { + const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx)); if (comptime IS_DEBUG) { log.debug(.http, "request start", .{ .method = self._method, .url = self._url, .source = "xhr" }); } - self._transfer = transfer; + self._http_response = response; self.acquireRef(); } -fn httpHeaderCallback(transfer: *HttpClient.Transfer, header: net_http.Header) !void { - const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); +fn httpHeaderCallback(response: HttpClient.Response, header: net_http.Header) !void { + const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx)); const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ header.name, header.value }); try self._response_headers.append(self._arena, joined); } -fn httpHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { - const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); - - const header = &transfer.response_header.?; +fn httpHeaderDoneCallback(response: HttpClient.Response) !bool { + const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx)); if (comptime IS_DEBUG) { log.debug(.http, "request header", .{ .source = "xhr", .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| { log.info(.http, "invalid content type", .{ .content_Type = ct, @@ -426,18 +424,19 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { }; } - var it = transfer.responseHeaderIterator(); - while (it.next()) |hdr| { - const joined = try std.fmt.allocPrint(self._arena, "{s}: {s}", .{ hdr.name, hdr.value }); - try self._response_headers.append(self._arena, joined); - } + // TODO: Header Iterator + // var it = transfer.responseHeaderIterator(); + // while (it.next()) |hdr| { + // 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; - if (transfer.getContentLength()) |cl| { + self._response_status = response.status().?; + if (response.contentLength()) |cl| { self._response_len = 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; @@ -452,8 +451,8 @@ fn httpHeaderDoneCallback(transfer: *HttpClient.Transfer) !bool { return true; } -fn httpDataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void { - const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); +fn httpDataCallback(response: HttpClient.Response, data: []const u8) !void { + const self: *XMLHttpRequest = @ptrCast(@alignCast(response.ctx)); try self._response_data.appendSlice(self._arena, data); const page = self._page; @@ -476,7 +475,7 @@ fn httpDoneCallback(ctx: *anyopaque) !void { // Not that the request is done, the http/client will free the transfer // object. It isn't safe to keep it around. - self._transfer = null; + self._http_response = null; const page = self._page; @@ -499,22 +498,22 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void { const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); // http client will close it after an error, it isn't safe to keep around self.handleError(err); - if (self._transfer != null) { - self._transfer = null; + if (self._http_response != null) { + self._http_response = null; self.releaseRef(self._page._session); } } fn httpShutdownCallback(ctx: *anyopaque) void { const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); - self._transfer = null; + self._http_response = null; } pub fn abort(self: *XMLHttpRequest) void { self.handleError(error.Abort); - if (self._transfer) |transfer| { - self._transfer = null; - transfer.abort(error.Abort); + if (self._http_response) |resp| { + self._http_response = null; + resp.abort(error.Abort); self.releaseRef(self._page._session); } }