diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 1d7f19be..f0d3b682 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -455,12 +455,14 @@ pub fn BrowserContext(comptime CDP_T: type) type { try self.cdp.browser.notification.register(.http_request_fail, self, onHttpRequestFail); try self.cdp.browser.notification.register(.http_request_start, self, onHttpRequestStart); try self.cdp.browser.notification.register(.http_headers_done, self, onHttpHeadersDone); + try self.cdp.browser.notification.register(.http_request_done, self, onHttpRequestDone); } pub fn networkDisable(self: *Self) void { self.cdp.browser.notification.unregister(.http_request_fail, self); self.cdp.browser.notification.unregister(.http_request_start, self); self.cdp.browser.notification.unregister(.http_headers_done, self); + self.cdp.browser.notification.unregister(.http_request_done, self); } pub fn fetchEnable(self: *Self) !void { @@ -516,6 +518,12 @@ pub fn BrowserContext(comptime CDP_T: type) type { return @import("domains/network.zig").httpHeadersDone(self.notification_arena, self, data); } + pub fn onHttpRequestDone(ctx: *anyopaque, data: *const Notification.RequestDone) !void { + const self: *Self = @alignCast(@ptrCast(ctx)); + defer self.resetNotificationArena(); + return @import("domains/network.zig").httpRequestDone(self.notification_arena, self, data); + } + fn resetNotificationArena(self: *Self) void { defer _ = self.cdp.notification_arena.reset(.{ .retain_with_limit = 1024 * 64 }); } diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index 81329ff0..a9c39b60 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -36,6 +36,7 @@ pub fn processMessage(cmd: anytype) !void { setCookie, setCookies, getCookies, + getResponseBody, }, cmd.input.action) orelse return error.UnknownMethod; switch (action) { @@ -49,6 +50,7 @@ pub fn processMessage(cmd: anytype) !void { .setCookie => return setCookie(cmd), .setCookies => return setCookies(cmd), .getCookies => return getCookies(cmd), + .getResponseBody => return getResponseBody(cmd), } } @@ -202,6 +204,19 @@ fn getCookies(cmd: anytype) !void { try cmd.sendResult(.{ .cookies = writer }, .{}); } +fn getResponseBody(cmd: anytype) !void { + const params = (try cmd.params(struct { + requestId: []const u8, // "REQ-{d}" + })) orelse return error.InvalidParams; + + _ = params; + + try cmd.sendResult(.{ + .body = "TODO", + .base64Encoded = false, + }, .{}); +} + pub fn httpRequestFail(arena: Allocator, bc: anytype, data: *const Notification.RequestFail) !void { // It's possible that the request failed because we aborted when the client // sent Target.closeTarget. In that case, bc.session_id will be cleared @@ -264,6 +279,22 @@ pub fn httpHeadersDone(arena: Allocator, bc: anytype, data: *const Notification. }, .{ .session_id = session_id }); } +pub fn httpRequestDone(arena: Allocator, bc: anytype, data: *const Notification.RequestDone) !void { + // Isn't possible to do a network request within a Browser (which our + // notification is tied to), without a page. + std.debug.assert(bc.session.page != null); + + var cdp = bc.cdp; + + // all unreachable because we _have_ to have a page. + const session_id = bc.session_id orelse unreachable; + + try cdp.sendEvent("Network.loadingFinished", .{ + .requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{data.transfer.id}), + .encodedDataLength = data.transfer.bytes_received, + }, .{ .session_id = session_id }); +} + pub const TransferAsRequestWriter = struct { transfer: *Transfer, diff --git a/src/http/Client.zig b/src/http/Client.zig index bbadf977..d6415ac8 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -376,8 +376,14 @@ fn perform(self: *Client, timeout_ms: c_int) !void { // transfer isn't valid at this point, don't use it. log.err(.http, "done_callback", .{ .err = err }); self.requestFailed(transfer, err); + continue; }; - // self.requestComplete(transfer); + + if (transfer.client.notification) |notification| { + notification.dispatch(.http_request_done, &.{ + .transfer = transfer, + }); + } } else |err| { self.requestFailed(transfer, err); } @@ -551,11 +557,15 @@ pub const Transfer = struct { uri: std.Uri, // used for setting/getting the cookie ctx: *anyopaque, // copied from req.ctx to make it easier for callback handlers client: *Client, - _notified_fail: bool = false, + // total bytes received in the response, including the response status line, + // the headers, and the [encoded] body. + bytes_received: usize = 0, // We'll store the response header here response_header: ?ResponseHeader = null, + _notified_fail: bool = false, + _handle: ?*Handle = null, _redirecting: bool = false, @@ -715,9 +725,11 @@ pub const Transfer = struct { .url = url, .status = status, }; + transfer.bytes_received = buf_len; return buf_len; } + transfer.bytes_received += buf_len; if (buf_len == 2) { if (getResponseHeader(easy, "content-type", 0)) |ct| { var hdr = &transfer.response_header.?; @@ -767,6 +779,7 @@ pub const Transfer = struct { return chunk_len; } + transfer.bytes_received += chunk_len; transfer.req.data_callback(transfer, buffer[0..chunk_len]) catch |err| { log.err(.http, "data_callback", .{ .err = err, .req = transfer }); return c.CURL_WRITEFUNC_ERROR; diff --git a/src/notification.zig b/src/notification.zig index c499c584..0ed650dc 100644 --- a/src/notification.zig +++ b/src/notification.zig @@ -64,6 +64,7 @@ pub const Notification = struct { http_request_start: List = .{}, http_request_intercept: List = .{}, http_headers_done: List = .{}, + http_request_done: List = .{}, notification_created: List = .{}, }; @@ -76,6 +77,7 @@ pub const Notification = struct { http_request_start: *const RequestStart, http_request_intercept: *const RequestIntercept, http_headers_done: *const ResponseHeadersDone, + http_request_done: *const RequestDone, notification_created: *Notification, }; const EventType = std.meta.FieldEnum(Events); @@ -106,6 +108,10 @@ pub const Notification = struct { transfer: *Transfer, }; + pub const RequestDone = struct { + transfer: *Transfer, + }; + pub const RequestFail = struct { transfer: *Transfer, err: anyerror,