Skip to content

Commit 305460d

Browse files
authored
Merge pull request #768 from lightpanda-io/setExtraHTTPHeaders
setExtraHTTPHeaders
2 parents f789c84 + bacef41 commit 305460d

File tree

5 files changed

+84
-5
lines changed

5 files changed

+84
-5
lines changed

src/cdp/cdp.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ pub fn CDPT(comptime TypeProvider: type) type {
7171
// Used for processing notifications within a browser context.
7272
notification_arena: std.heap.ArenaAllocator,
7373

74+
// Extra headers to add to all requests. TBD under which conditions this should be reset.
75+
extra_headers: std.ArrayListUnmanaged(std.http.Header) = .empty,
76+
7477
const Self = @This();
7578

7679
pub fn init(app: *App, client: TypeProvider.Client) !Self {

src/cdp/domains/network.zig

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
const std = @import("std");
2020
const Notification = @import("../../notification.zig").Notification;
21+
const log = @import("../../log.zig");
2122

2223
const Allocator = std.mem.Allocator;
2324

@@ -26,12 +27,14 @@ pub fn processMessage(cmd: anytype) !void {
2627
enable,
2728
disable,
2829
setCacheDisabled,
30+
setExtraHTTPHeaders,
2931
}, cmd.input.action) orelse return error.UnknownMethod;
3032

3133
switch (action) {
3234
.enable => return enable(cmd),
3335
.disable => return disable(cmd),
3436
.setCacheDisabled => return cmd.sendResult(null, .{}),
37+
.setExtraHTTPHeaders => return setExtraHTTPHeaders(cmd),
3538
}
3639
}
3740

@@ -47,6 +50,40 @@ fn disable(cmd: anytype) !void {
4750
return cmd.sendResult(null, .{});
4851
}
4952

53+
fn setExtraHTTPHeaders(cmd: anytype) !void {
54+
const params = (try cmd.params(struct {
55+
headers: std.json.ArrayHashMap([]const u8),
56+
})) orelse return error.InvalidParams;
57+
58+
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
59+
60+
// Copy the headers onto the browser context arena
61+
const arena = bc.arena;
62+
const extra_headers = &bc.cdp.extra_headers;
63+
64+
extra_headers.clearRetainingCapacity();
65+
try extra_headers.ensureTotalCapacity(arena, params.headers.map.count());
66+
var it = params.headers.map.iterator();
67+
while (it.next()) |header| {
68+
extra_headers.appendAssumeCapacity(.{ .name = try arena.dupe(u8, header.key_ptr.*), .value = try arena.dupe(u8, header.value_ptr.*) });
69+
}
70+
71+
return cmd.sendResult(null, .{});
72+
}
73+
74+
// Upsert a header into the headers array.
75+
// returns true if the header was added, false if it was updated
76+
fn putAssumeCapacity(headers: *std.ArrayListUnmanaged(std.http.Header), extra: std.http.Header) bool {
77+
for (headers.items) |*header| {
78+
if (std.mem.eql(u8, header.name, extra.name)) {
79+
header.value = extra.value;
80+
return false;
81+
}
82+
}
83+
headers.appendAssumeCapacity(extra);
84+
return true;
85+
}
86+
5087
pub fn httpRequestStart(arena: Allocator, bc: anytype, request: *const Notification.RequestStart) !void {
5188
// Isn't possible to do a network request within a Browser (which our
5289
// notification is tied to), without a page.
@@ -59,6 +96,13 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, request: *const Notificat
5996
const target_id = bc.target_id orelse unreachable;
6097
const page = bc.session.currentPage() orelse unreachable;
6198

99+
// Modify request with extra CDP headers
100+
try request.headers.ensureTotalCapacity(request.arena, request.headers.items.len + cdp.extra_headers.items.len);
101+
for (cdp.extra_headers.items) |extra| {
102+
const new = putAssumeCapacity(request.headers, extra);
103+
if (!new) log.debug(.cdp, "request header overwritten", .{ .name = extra.name });
104+
}
105+
62106
const document_url = try urlToString(arena, &page.url.uri, .{
63107
.scheme = true,
64108
.authentication = true,
@@ -80,8 +124,8 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, request: *const Notificat
80124
});
81125

82126
var headers: std.StringArrayHashMapUnmanaged([]const u8) = .empty;
83-
try headers.ensureTotalCapacity(arena, request.headers.len);
84-
for (request.headers) |header| {
127+
try headers.ensureTotalCapacity(arena, request.headers.items.len);
128+
for (request.headers.items) |header| {
85129
headers.putAssumeCapacity(header.name, header.value);
86130
}
87131

@@ -129,13 +173,13 @@ pub fn httpRequestComplete(arena: Allocator, bc: anytype, request: *const Notifi
129173
// We're missing a bunch of fields, but, for now, this seems like enough
130174
try cdp.sendEvent("Network.responseReceived", .{
131175
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{request.id}),
132-
.frameId = target_id,
133176
.loaderId = bc.loader_id,
134177
.response = .{
135178
.url = url,
136179
.status = request.status,
137180
.headers = std.json.ArrayHashMap([]const u8){ .map = headers },
138181
},
182+
.frameId = target_id,
139183
}, .{ .session_id = session_id });
140184
}
141185

@@ -144,3 +188,30 @@ fn urlToString(arena: Allocator, url: *const std.Uri, opts: std.Uri.WriteToStrea
144188
try url.writeToStream(opts, buf.writer(arena));
145189
return buf.items;
146190
}
191+
192+
const testing = @import("../testing.zig");
193+
test "cdp.network setExtraHTTPHeaders" {
194+
var ctx = testing.context();
195+
defer ctx.deinit();
196+
197+
// _ = try ctx.loadBrowserContext(.{ .id = "NID-A", .session_id = "NESI-A" });
198+
try ctx.processMessage(.{ .id = 10, .method = "Target.createTarget", .params = .{ .url = "about/blank" } });
199+
200+
try ctx.processMessage(.{
201+
.id = 3,
202+
.method = "Network.setExtraHTTPHeaders",
203+
.params = .{ .headers = .{ .foo = "bar" } },
204+
});
205+
206+
try ctx.processMessage(.{
207+
.id = 4,
208+
.method = "Network.setExtraHTTPHeaders",
209+
.params = .{ .headers = .{ .food = "bars" } },
210+
});
211+
212+
const bc = ctx.cdp().browser_context.?;
213+
try testing.expectEqual(bc.cdp.extra_headers.items.len, 1);
214+
215+
try ctx.processMessage(.{ .id = 5, .method = "Target.attachToTarget", .params = .{ .targetId = bc.target_id.? } });
216+
try testing.expectEqual(bc.cdp.extra_headers.items.len, 0);
217+
}

src/cdp/domains/target.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ fn doAttachtoTarget(cmd: anytype, target_id: []const u8) !void {
389389
std.debug.assert(bc.session_id == null);
390390
const session_id = cmd.cdp.session_id_gen.next();
391391

392+
// extra_headers should not be kept on a new page or tab, currently we have only 1 page, we clear it just in case
393+
bc.cdp.extra_headers.clearRetainingCapacity();
394+
392395
try cmd.sendEvent("Target.attachedToTarget", AttachToTarget{
393396
.sessionId = session_id,
394397
.targetInfo = TargetInfo{

src/http/client.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,10 +838,11 @@ pub const Request = struct {
838838
}
839839
self._notified_start = true;
840840
notification.dispatch(.http_request_start, &.{
841+
.arena = self.arena,
841842
.id = self.id,
842843
.url = self.request_uri,
843844
.method = self.method,
844-
.headers = self.headers.items,
845+
.headers = &self.headers,
845846
.has_body = self.body != null,
846847
});
847848
}

src/notification.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,11 @@ pub const Notification = struct {
8989
};
9090

9191
pub const RequestStart = struct {
92+
arena: Allocator,
9293
id: usize,
9394
url: *const std.Uri,
9495
method: http_client.Request.Method,
95-
headers: []std.http.Header,
96+
headers: *std.ArrayListUnmanaged(std.http.Header),
9697
has_body: bool,
9798
};
9899

0 commit comments

Comments
 (0)