Skip to content

Commit ca9e850

Browse files
committed
Create Client.Transfer earlier.
On client.request(req) we now immediately wrap the request into a Transfer. This results in less copying of the Request object. It also makes the transfer.uri available, so CDP no longer needs to std.Uri(request.url) anymore. The main advantage is that it's easier to manage resources. There was a use- after free before due to the sensitive nature of the tranfer's lifetime. There were also corner cases where some resources might not be freed. This is hopefully fixed with the lifetime of Transfer being extended.
1 parent 2dc09c7 commit ca9e850

File tree

5 files changed

+145
-109
lines changed

5 files changed

+145
-109
lines changed

src/cdp/domains/fetch.zig

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ const std = @import("std");
2020
const Allocator = std.mem.Allocator;
2121
const Notification = @import("../../notification.zig").Notification;
2222
const log = @import("../../log.zig");
23-
const Request = @import("../../http/Client.zig").Request;
2423
const Method = @import("../../http/Client.zig").Method;
24+
const Transfer = @import("../../http/Client.zig").Transfer;
2525

2626
pub fn processMessage(cmd: anytype) !void {
2727
const action = std.meta.stringToEnum(enum {
@@ -42,11 +42,11 @@ pub fn processMessage(cmd: anytype) !void {
4242
// Stored in CDP
4343
pub const InterceptState = struct {
4444
const Self = @This();
45-
waiting: std.AutoArrayHashMap(u64, Request),
45+
waiting: std.AutoArrayHashMap(u64, *Transfer),
4646

4747
pub fn init(allocator: Allocator) !InterceptState {
4848
return .{
49-
.waiting = std.AutoArrayHashMap(u64, Request).init(allocator),
49+
.waiting = std.AutoArrayHashMap(u64, *Transfer).init(allocator),
5050
};
5151
}
5252

@@ -135,10 +135,11 @@ pub fn requestPaused(arena: Allocator, bc: anytype, intercept: *const Notificati
135135
// NOTE: we assume whomever created the request created it with a lifetime of the Page.
136136
// TODO: What to do when receiving replies for a previous page's requests?
137137

138-
try cdp.intercept_state.waiting.put(intercept.request.id.?, intercept.request.*);
138+
const transfer = intercept.transfer;
139+
try cdp.intercept_state.waiting.put(transfer.id, transfer);
139140

140141
// NOTE: .request data preparation is duped from network.zig
141-
const full_request_url = try std.Uri.parse(intercept.request.url);
142+
const full_request_url = transfer.uri;
142143
const request_url = try @import("network.zig").urlToString(arena, &full_request_url, .{
143144
.scheme = true,
144145
.authentication = true,
@@ -149,21 +150,21 @@ pub fn requestPaused(arena: Allocator, bc: anytype, intercept: *const Notificati
149150
const request_fragment = try @import("network.zig").urlToString(arena, &full_request_url, .{
150151
.fragment = true,
151152
});
152-
const headers = try intercept.request.headers.asHashMap(arena);
153+
const headers = try transfer.req.headers.asHashMap(arena);
153154
// End of duped code
154155

155156
try cdp.sendEvent("Fetch.requestPaused", .{
156-
.requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{intercept.request.id.?}),
157+
.requestId = try std.fmt.allocPrint(arena, "INTERCEPT-{d}", .{transfer.id}),
157158
.request = .{
158159
.url = request_url,
159160
.urlFragment = request_fragment,
160-
.method = @tagName(intercept.request.method),
161-
.hasPostData = intercept.request.body != null,
161+
.method = @tagName(transfer.req.method),
162+
.hasPostData = transfer.req.body != null,
162163
.headers = std.json.ArrayHashMap([]const u8){ .map = headers },
163164
},
164165
.frameId = target_id,
165166
.resourceType = ResourceType.Document, // TODO!
166-
.networkId = try std.fmt.allocPrint(arena, "REQ-{d}", .{intercept.request.id.?}),
167+
.networkId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}),
167168
}, .{ .session_id = session_id });
168169

169170
// Await either continueRequest, failRequest or fulfillRequest
@@ -188,19 +189,20 @@ fn continueRequest(cmd: anytype) !void {
188189
if (params.postData != null or params.headers != null or params.interceptResponse) return error.NotYetImplementedParams;
189190

190191
const request_id = try idFromRequestId(params.requestId);
191-
var waiting_request = (bc.cdp.intercept_state.waiting.fetchSwapRemove(request_id) orelse return error.RequestNotFound).value;
192+
const entry = bc.cdp.intercept_state.waiting.fetchSwapRemove(request_id) orelse return error.RequestNotFound;
193+
const transfer = entry.value;
192194

193195
// Update the request with the new parameters
194196
if (params.url) |url| {
195197
// The request url must be modified in a way that's not observable by page. So page.url is not updated.
196-
waiting_request.url = try bc.cdp.browser.page_arena.allocator().dupeZ(u8, url);
198+
try transfer.updateURL(try bc.cdp.browser.page_arena.allocator().dupeZ(u8, url));
197199
}
198200
if (params.method) |method| {
199-
waiting_request.method = std.meta.stringToEnum(Method, method) orelse return error.InvalidParams;
201+
transfer.req.method = std.meta.stringToEnum(Method, method) orelse return error.InvalidParams;
200202
}
201203

202204
log.info(.cdp, "Request continued by intercept", .{ .id = params.requestId });
203-
try bc.cdp.browser.http_client.request(waiting_request);
205+
try bc.cdp.browser.http_client.process(transfer);
204206

205207
return cmd.sendResult(null, .{});
206208
}
@@ -214,7 +216,9 @@ fn failRequest(cmd: anytype) !void {
214216
})) orelse return error.InvalidParams;
215217

216218
const request_id = try idFromRequestId(params.requestId);
217-
if (state.waiting.fetchSwapRemove(request_id) == null) return error.RequestNotFound;
219+
const entry = state.waiting.fetchSwapRemove(request_id) orelse return error.RequestNotFound;
220+
// entry.value is the transfer
221+
entry.value.abort();
218222

219223
log.info(.cdp, "Request aborted by intercept", .{ .reason = params.errorReason });
220224
return cmd.sendResult(null, .{});

src/cdp/domains/network.zig

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ pub fn httpRequestFail(arena: Allocator, bc: anytype, data: *const Notification.
215215

216216
// We're missing a bunch of fields, but, for now, this seems like enough
217217
try bc.cdp.sendEvent("Network.loadingFailed", .{
218-
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{data.request.id.?}),
218+
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{data.transfer.id}),
219219
// Seems to be what chrome answers with. I assume it depends on the type of error?
220220
.type = "Ping",
221221
.errorText = data.err,
@@ -251,7 +251,8 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, data: *const Notification
251251
.query = true,
252252
});
253253

254-
const full_request_url = try std.Uri.parse(data.request.url);
254+
const transfer = data.transfer;
255+
const full_request_url = transfer.uri;
255256
const request_url = try urlToString(arena, &full_request_url, .{
256257
.scheme = true,
257258
.authentication = true,
@@ -263,19 +264,19 @@ pub fn httpRequestStart(arena: Allocator, bc: anytype, data: *const Notification
263264
.fragment = true, // TODO since path is false, this likely does not work as intended
264265
});
265266

266-
const headers = try data.request.headers.asHashMap(arena);
267+
const headers = try transfer.req.headers.asHashMap(arena);
267268

268269
// We're missing a bunch of fields, but, for now, this seems like enough
269270
try cdp.sendEvent("Network.requestWillBeSent", .{
270-
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{data.request.id.?}),
271+
.requestId = try std.fmt.allocPrint(arena, "REQ-{d}", .{transfer.id}),
271272
.frameId = target_id,
272273
.loaderId = bc.loader_id,
273274
.documentUrl = document_url,
274275
.request = .{
275276
.url = request_url,
276277
.urlFragment = request_fragment,
277-
.method = @tagName(data.request.method),
278-
.hasPostData = data.request.body != null,
278+
.method = @tagName(transfer.req.method),
279+
.hasPostData = transfer.req.body != null,
279280
.headers = std.json.ArrayHashMap([]const u8){ .map = headers },
280281
},
281282
}, .{ .session_id = session_id });

0 commit comments

Comments
 (0)