move intercept_state and extra_headers from CDP instance to BrowserContext

This commit is contained in:
Karl Seguin
2025-08-18 13:23:17 +08:00
parent c1319d1f27
commit 211012d367
4 changed files with 57 additions and 22 deletions

View File

@@ -74,11 +74,6 @@ pub fn CDPT(comptime TypeProvider: type) type {
// Used for processing notifications within a browser context. // Used for processing notifications within a browser context.
notification_arena: std.heap.ArenaAllocator, notification_arena: std.heap.ArenaAllocator,
// Extra headers to add to all requests. TBD under which conditions this should be reset.
extra_headers: std.ArrayListUnmanaged([*c]const u8) = .empty,
intercept_state: InterceptState,
const Self = @This(); const Self = @This();
pub fn init(app: *App, client: TypeProvider.Client) !Self { pub fn init(app: *App, client: TypeProvider.Client) !Self {
@@ -93,7 +88,6 @@ pub fn CDPT(comptime TypeProvider: type) type {
.browser_context = null, .browser_context = null,
.message_arena = std.heap.ArenaAllocator.init(allocator), .message_arena = std.heap.ArenaAllocator.init(allocator),
.notification_arena = std.heap.ArenaAllocator.init(allocator), .notification_arena = std.heap.ArenaAllocator.init(allocator),
.intercept_state = try InterceptState.init(allocator), // TBD or browser session arena?
}; };
} }
@@ -101,7 +95,6 @@ pub fn CDPT(comptime TypeProvider: type) type {
if (self.browser_context) |*bc| { if (self.browser_context) |*bc| {
bc.deinit(); bc.deinit();
} }
self.intercept_state.deinit(); // TBD Should this live in BC?
self.browser.deinit(); self.browser.deinit();
self.message_arena.deinit(); self.message_arena.deinit();
self.notification_arena.deinit(); self.notification_arena.deinit();
@@ -346,6 +339,11 @@ pub fn BrowserContext(comptime CDP_T: type) type {
http_proxy_changed: bool = false, http_proxy_changed: bool = false,
// Extra headers to add to all requests.
extra_headers: std.ArrayListUnmanaged([*c]const u8) = .empty,
intercept_state: InterceptState,
const Self = @This(); const Self = @This();
fn init(self: *Self, id: []const u8, cdp: *CDP_T) !void { fn init(self: *Self, id: []const u8, cdp: *CDP_T) !void {
@@ -375,6 +373,7 @@ pub fn BrowserContext(comptime CDP_T: type) type {
.isolated_world = null, .isolated_world = null,
.inspector = inspector, .inspector = inspector,
.notification_arena = cdp.notification_arena.allocator(), .notification_arena = cdp.notification_arena.allocator(),
.intercept_state = try InterceptState.init(allocator),
}; };
self.node_search_list = Node.Search.List.init(allocator, &self.node_registry); self.node_search_list = Node.Search.List.init(allocator, &self.node_registry);
errdefer self.deinit(); errdefer self.deinit();
@@ -407,6 +406,11 @@ pub fn BrowserContext(comptime CDP_T: type) type {
log.warn(.http, "restoreOriginalProxy", .{ .err = err }); log.warn(.http, "restoreOriginalProxy", .{ .err = err });
}; };
} }
for (self.intercept_state.pendingTransfers()) |transfer| {
transfer.abort();
}
self.intercept_state.deinit();
} }
pub fn reset(self: *Self) void { pub fn reset(self: *Self) void {

View File

@@ -70,6 +70,10 @@ pub const InterceptState = struct {
pub fn deinit(self: *InterceptState) void { pub fn deinit(self: *InterceptState) void {
self.waiting.deinit(self.allocator); self.waiting.deinit(self.allocator);
} }
pub fn pendingTransfers(self: *const InterceptState) []*Transfer {
return self.waiting.values();
}
}; };
const RequestPattern = struct { const RequestPattern = struct {
@@ -134,11 +138,13 @@ fn disable(cmd: anytype) !void {
fn enable(cmd: anytype) !void { fn enable(cmd: anytype) !void {
const params = (try cmd.params(EnableParam)) orelse EnableParam{}; const params = (try cmd.params(EnableParam)) orelse EnableParam{};
if (params.patterns.len != 0) { if (!arePatternsSupported(params.patterns)) {
log.warn(.cdp, "not implemented", .{ .feature = "Fetch.enable No patterns yet" }); log.warn(.cdp, "not implemented", .{ .feature = "Fetch.enable advanced patterns are not" });
return cmd.sendResult(null, .{});
} }
if (params.handleAuthRequests) { if (params.handleAuthRequests) {
log.warn(.cdp, "not implemented", .{ .feature = "Fetch.enable No auth yet" }); log.warn(.cdp, "not implemented", .{ .feature = "Fetch.enable handleAuthRequests is not supported yet" });
} }
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded; const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
@@ -147,9 +153,33 @@ fn enable(cmd: anytype) !void {
return cmd.sendResult(null, .{}); return cmd.sendResult(null, .{});
} }
pub fn requestIntercept(arena: Allocator, bc: anytype, intercept: *const Notification.RequestIntercept) !void { fn arePatternsSupported(patterns: []RequestPattern) bool {
var cdp = bc.cdp; if (patterns.len == 0) {
return true;
}
if (patterns.len > 1) {
return false;
}
// While we don't support patterns, yet, both Playwright and Puppeteer send
// a default pattern which happens to be what we support:
// [{"urlPattern":"*","requestStage":"Request"}]
// So, rather than erroring on this case because we don't support patterns,
// we'll allow it, because this pattern is how it works as-is.
const pattern = patterns[0];
if (!std.mem.eql(u8, pattern.urlPattern, "*")) {
return false;
}
if (pattern.resourceType != null) {
return false;
}
if (pattern.requestStage != .Request) {
return false;
}
return true;
}
pub fn requestIntercept(arena: Allocator, bc: anytype, intercept: *const Notification.RequestIntercept) !void {
// unreachable because we _have_ to have a page. // unreachable because we _have_ to have a page.
const session_id = bc.session_id orelse unreachable; const session_id = bc.session_id orelse unreachable;
const target_id = bc.target_id orelse unreachable; const target_id = bc.target_id orelse unreachable;
@@ -160,9 +190,9 @@ pub fn requestIntercept(arena: Allocator, bc: anytype, intercept: *const Notific
// TODO: What to do when receiving replies for a previous page's requests? // TODO: What to do when receiving replies for a previous page's requests?
const transfer = intercept.transfer; const transfer = intercept.transfer;
try cdp.intercept_state.put(transfer); try bc.intercept_state.put(transfer);
try cdp.sendEvent("Fetch.requestPaused", .{ try bc.cdp.sendEvent("Fetch.requestPaused", .{
.requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{transfer.id}), .requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{transfer.id}),
.request = network.TransferAsRequestWriter.init(transfer), .request = network.TransferAsRequestWriter.init(transfer),
.frameId = target_id, .frameId = target_id,
@@ -202,7 +232,7 @@ fn continueRequest(cmd: anytype) !void {
const page = bc.session.currentPage() orelse return error.PageNotLoaded; const page = bc.session.currentPage() orelse return error.PageNotLoaded;
var intercept_state = &bc.cdp.intercept_state; var intercept_state = &bc.intercept_state;
const request_id = try idFromRequestId(params.requestId); const request_id = try idFromRequestId(params.requestId);
const transfer = intercept_state.remove(request_id) orelse return error.RequestNotFound; const transfer = intercept_state.remove(request_id) orelse return error.RequestNotFound;
@@ -238,7 +268,7 @@ fn failRequest(cmd: anytype) !void {
const page = bc.session.currentPage() orelse return error.PageNotLoaded; const page = bc.session.currentPage() orelse return error.PageNotLoaded;
var intercept_state = &bc.cdp.intercept_state; var intercept_state = &bc.intercept_state;
const request_id = try idFromRequestId(params.requestId); const request_id = try idFromRequestId(params.requestId);
const transfer = intercept_state.remove(request_id) orelse return error.RequestNotFound; const transfer = intercept_state.remove(request_id) orelse return error.RequestNotFound;

View File

@@ -83,7 +83,7 @@ fn setExtraHTTPHeaders(cmd: anytype) !void {
// Copy the headers onto the browser context arena // Copy the headers onto the browser context arena
const arena = bc.arena; const arena = bc.arena;
const extra_headers = &bc.cdp.extra_headers; const extra_headers = &bc.extra_headers;
extra_headers.clearRetainingCapacity(); extra_headers.clearRetainingCapacity();
try extra_headers.ensureTotalCapacity(arena, params.headers.map.count()); try extra_headers.ensureTotalCapacity(arena, params.headers.map.count());
@@ -235,7 +235,7 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, data: *const Notification
const page = bc.session.currentPage() orelse unreachable; const page = bc.session.currentPage() orelse unreachable;
// Modify request with extra CDP headers // Modify request with extra CDP headers
for (cdp.extra_headers.items) |extra| { for (bc.extra_headers.items) |extra| {
try data.transfer.req.headers.add(extra); try data.transfer.req.headers.add(extra);
} }
@@ -429,10 +429,10 @@ test "cdp.network setExtraHTTPHeaders" {
}); });
const bc = ctx.cdp().browser_context.?; const bc = ctx.cdp().browser_context.?;
try testing.expectEqual(bc.cdp.extra_headers.items.len, 1); try testing.expectEqual(bc.extra_headers.items.len, 1);
try ctx.processMessage(.{ .id = 5, .method = "Target.attachToTarget", .params = .{ .targetId = bc.target_id.? } }); try ctx.processMessage(.{ .id = 5, .method = "Target.attachToTarget", .params = .{ .targetId = bc.target_id.? } });
try testing.expectEqual(bc.cdp.extra_headers.items.len, 0); try testing.expectEqual(bc.extra_headers.items.len, 0);
} }
test "cdp.Network: cookies" { test "cdp.Network: cookies" {

View File

@@ -409,8 +409,9 @@ fn doAttachtoTarget(cmd: anytype, target_id: []const u8) !void {
std.debug.assert(bc.session_id == null); std.debug.assert(bc.session_id == null);
const session_id = cmd.cdp.session_id_gen.next(); const session_id = cmd.cdp.session_id_gen.next();
// extra_headers should not be kept on a new page or tab, currently we have only 1 page, we clear it just in case // extra_headers should not be kept on a new page or tab,
bc.cdp.extra_headers.clearRetainingCapacity(); // currently we have only 1 page, we clear it just in case
bc.extra_headers.clearRetainingCapacity();
try cmd.sendEvent("Target.attachedToTarget", AttachToTarget{ try cmd.sendEvent("Target.attachedToTarget", AttachToTarget{
.sessionId = session_id, .sessionId = session_id,