Fixup cookies management

This commit is contained in:
Nikolay Govorov
2026-03-18 20:49:05 +00:00
parent 6e38aa9414
commit 8963aeb965
7 changed files with 38 additions and 85 deletions

View File

@@ -866,27 +866,6 @@ fn ensureNoActiveConnection(self: *const Client) !void {
}
}
pub const RequestCookie = struct {
is_http: bool,
jar: *CookieJar,
is_navigation: bool,
origin: [:0]const u8,
pub fn headersForRequest(self: *const RequestCookie, temp: Allocator, url: [:0]const u8, headers: *http.Headers) !void {
var arr: std.ArrayList(u8) = .{};
try self.jar.forRequest(url, arr.writer(temp), .{
.is_http = self.is_http,
.is_navigation = self.is_navigation,
.origin_url = self.origin,
});
if (arr.items.len > 0) {
try arr.append(temp, 0); //null terminate
headers.cookies = @as([*c]const u8, @ptrCast(arr.items.ptr));
}
}
};
pub const Request = struct {
frame_id: u32,
method: Method,
@@ -1075,9 +1054,20 @@ pub const Transfer = struct {
try wba.signRequest(self.arena.allocator(), &header_list, authority);
}
// Add cookies.
if (header_list.cookies) |cookies| {
try conn.setCookies(cookies);
// Add cookies from cookie jar.
if (req.cookie_jar) |jar| {
const arena = self.arena.allocator();
var aw: std.Io.Writer.Allocating = .init(arena);
try jar.forRequest(req.url, &aw.writer, .{
.is_http = true,
.origin_url = req.url,
.is_navigation = req.resource_type == .document,
});
const written = aw.written();
if (written.len > 0) {
try aw.writer.writeByte(0);
try conn.setCookies(@ptrCast(written.ptr));
}
}
try conn.setPrivate(self);
@@ -1150,13 +1140,10 @@ pub const Transfer = struct {
return error.TooManyRedirects;
}
lp.log.warn(.bug, "Redirecting...", .{});
// retrieve cookies from the redirect's response.
if (req.cookie_jar) |jar| {
var i: usize = 0;
while (conn.getResponseHeader("set-cookie", i)) |ct| : (i += 1) {
lp.log.warn(.bug, "set-cookie", .{ i, ct.value });
try jar.populateFromResponse(transfer.url, ct.value);
if (i >= ct.amount) {
@@ -1181,23 +1168,6 @@ pub const Transfer = struct {
req.method = .GET;
req.body = null;
}
// set cookies for the following request.
if (req.cookie_jar) |jar| {
var cookies: std.ArrayList(u8) = .{};
try jar.forRequest(url, cookies.writer(arena), .{
.is_http = true,
.origin_url = url,
.is_navigation = req.resource_type == .document,
});
if (cookies.items.len > 0) {
try cookies.append(arena, 0); // null terminate
req.headers.cookies = @ptrCast(cookies.items.ptr);
} else {
req.headers.cookies = null;
}
lp.log.warn(.bug, "cookie", .{cookies.items[0..]});
}
}
fn detectAuthChallenge(transfer: *Transfer, conn: *const http.Connection) void {

View File

@@ -371,12 +371,9 @@ pub fn getTitle(self: *Page) !?[]const u8 {
return null;
}
// Add comon headers for a request:
// * cookies
// Add common headers for a request:
// * referer
pub fn headersForRequest(self: *Page, temp: Allocator, url: [:0]const u8, headers: *HttpClient.Headers) !void {
try self.requestCookie(.{}).headersForRequest(temp, url, headers);
pub fn headersForRequest(self: *Page, headers: *HttpClient.Headers) !void {
// Build the referer
const referer = blk: {
if (self.referer_header == null) {
@@ -525,8 +522,6 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi
if (opts.header) |hdr| {
try headers.add(hdr);
}
try self.requestCookie(.{ .is_navigation = true }).headersForRequest(self.arena, self.url, &headers);
// We dispatch page_navigate event before sending the request.
// It ensures the event page_navigated is not dispatched before this one.
session.notification.dispatch(.page_navigate, &.{
@@ -3497,19 +3492,6 @@ pub fn insertText(self: *Page, v: []const u8) !void {
}
}
const RequestCookieOpts = struct {
is_http: bool = true,
is_navigation: bool = false,
};
pub fn requestCookie(self: *const Page, opts: RequestCookieOpts) HttpClient.RequestCookie {
return .{
.jar = &self._session.cookie_jar,
.origin = self.url,
.is_http = opts.is_http,
.is_navigation = opts.is_navigation,
};
}
fn asUint(comptime string: anytype) std.meta.Int(
.unsigned,
@bitSizeOf(@TypeOf(string.*)) - 8, // (- 8) to exclude sentinel 0

View File

@@ -138,9 +138,9 @@ fn clearList(list: *std.DoublyLinkedList) void {
}
}
fn getHeaders(self: *ScriptManager, arena: Allocator, url: [:0]const u8) !net_http.Headers {
fn getHeaders(self: *ScriptManager) !net_http.Headers {
var headers = try self.client.newHeaders();
try self.page.headersForRequest(arena, url, &headers);
try self.page.headersForRequest(&headers);
return headers;
}
@@ -280,7 +280,7 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e
.ctx = script,
.method = .GET,
.frame_id = page._frame_id,
.headers = try self.getHeaders(arena, url),
.headers = try self.getHeaders(),
.blocking = is_blocking,
.cookie_jar = &page._session.cookie_jar,
.resource_type = .script,
@@ -405,7 +405,7 @@ pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const
.ctx = script,
.method = .GET,
.frame_id = page._frame_id,
.headers = try self.getHeaders(arena, url),
.headers = try self.getHeaders(),
.cookie_jar = &page._session.cookie_jar,
.resource_type = .script,
.notification = page._session.notification,
@@ -508,7 +508,7 @@ pub fn getAsyncImport(self: *ScriptManager, url: [:0]const u8, cb: ImportAsync.C
.url = url,
.method = .GET,
.frame_id = page._frame_id,
.headers = try self.getHeaders(arena, url),
.headers = try self.getHeaders(),
.ctx = script,
.resource_type = .script,
.cookie_jar = &page._session.cookie_jar,

View File

@@ -69,7 +69,7 @@ pub fn init(input: Input, options: ?InitOpts, page: *Page) !js.Promise {
if (request._headers) |h| {
try h.populateHttpHeader(page.call_arena, &headers);
}
try page.headersForRequest(page.arena, request._url, &headers);
try page.headersForRequest(&headers);
if (comptime IS_DEBUG) {
log.debug(.http, "fetch", .{ .url = request._url });

View File

@@ -225,7 +225,7 @@ pub fn send(self: *XMLHttpRequest, body_: ?[]const u8) !void {
try self._request_headers.populateHttpHeader(page.call_arena, &headers);
if (cookie_support) {
try page.headersForRequest(self._arena, self._url, &headers);
try page.headersForRequest(&headers);
}
try http_client.request(.{

View File

@@ -340,6 +340,19 @@ pub const TransferAsRequestWriter = struct {
try jws.objectField(hdr.name);
try jws.write(hdr.value);
}
if (transfer.req.cookie_jar) |jar| {
var aw: std.Io.Writer.Allocating = .init(transfer.arena.allocator());
try jar.forRequest(transfer.req.url, &aw.writer, .{
.is_http = true,
.origin_url = transfer.req.url,
.is_navigation = transfer.req.resource_type == .document,
});
const cookie_str = aw.written();
if (cookie_str.len > 0) {
try jws.objectField("Cookie");
try jws.write(cookie_str);
}
}
try jws.endObject();
}
try jws.endObject();

View File

@@ -64,14 +64,13 @@ pub const Header = struct {
pub const Headers = struct {
headers: ?*libcurl.CurlSList,
cookies: ?[*c]const u8,
pub fn init(user_agent: [:0]const u8) !Headers {
const header_list = libcurl.curl_slist_append(null, user_agent);
if (header_list == null) {
return error.OutOfMemory;
}
return .{ .headers = header_list, .cookies = null };
return .{ .headers = header_list };
}
pub fn deinit(self: *const Headers) void {
@@ -102,20 +101,14 @@ pub const Headers = struct {
pub fn iterator(self: *Headers) Iterator {
return .{
.header = self.headers,
.cookies = self.cookies,
};
}
const Iterator = struct {
header: [*c]libcurl.CurlSList,
cookies: ?[*c]const u8,
pub fn next(self: *Iterator) ?Header {
const h = self.header orelse {
const cookies = self.cookies orelse return null;
self.cookies = null;
return .{ .name = "Cookie", .value = std.mem.span(@as([*:0]const u8, cookies)) };
};
const h = self.header orelse return null;
self.header = h.*.next;
return parseHeader(std.mem.span(@as([*:0]const u8, @ptrCast(h.*.data))));
@@ -458,11 +451,6 @@ pub const Connection = struct {
try self.secretHeaders(&header_list, http_headers);
try self.setHeaders(&header_list);
// Add cookies.
if (header_list.cookies) |cookies| {
try self.setCookies(cookies);
}
try libcurl.curl_easy_perform(self._easy);
return self.getResponseCode();
}