Skip to content

Commit 4fbedf5

Browse files
Merge pull request #948 from lightpanda-io/redirect-cookies
handle cookies on redirection manually
2 parents 38a0b69 + f7eee0d commit 4fbedf5

File tree

2 files changed

+51
-27
lines changed

2 files changed

+51
-27
lines changed

src/http/Client.zig

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub const Headers = Http.Headers;
2424
const Notification = @import("../notification.zig").Notification;
2525
const storage = @import("../browser/storage/storage.zig");
2626

27+
const urlStitch = @import("../url.zig").stitch;
28+
2729
const c = Http.c;
2830

2931
const Allocator = std.mem.Allocator;
@@ -83,12 +85,6 @@ blocking: Handle,
8385
// To notify registered subscribers of events, the browser sets/nulls this for us.
8486
notification: ?*Notification = null,
8587

86-
// The only place this is meant to be used is in `makeRequest` BEFORE `perform`
87-
// is called. It is used to generate our Cookie header. It can be used for other
88-
// purposes, but keep in mind that, while single-threaded, calls like makeRequest
89-
// can result in makeRequest being re-called (from a doneCallback).
90-
arena: ArenaAllocator,
91-
9288
// only needed for CDP which can change the proxy and then restore it. When
9389
// restoring, this originally-configured value is what it goes to.
9490
http_proxy: ?[:0]const u8 = null,
@@ -126,7 +122,6 @@ pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Clie
126122
.http_proxy = opts.http_proxy,
127123
.transfer_pool = transfer_pool,
128124
.queue_node_pool = queue_node_pool,
129-
.arena = ArenaAllocator.init(allocator),
130125
};
131126

132127
return client;
@@ -141,7 +136,6 @@ pub fn deinit(self: *Client) void {
141136

142137
self.transfer_pool.deinit();
143138
self.queue_node_pool.deinit();
144-
self.arena.deinit();
145139
self.allocator.destroy(self);
146140
}
147141

@@ -242,6 +236,7 @@ fn makeTransfer(self: *Client, req: Request) !*Transfer {
242236
const id = self.next_request_id + 1;
243237
self.next_request_id = id;
244238
transfer.* = .{
239+
.arena = ArenaAllocator.init(self.allocator),
245240
.id = id,
246241
.uri = uri,
247242
.req = req,
@@ -321,8 +316,6 @@ fn makeRequest(self: *Client, handle: *Handle, transfer: *Transfer) !void {
321316
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list.headers));
322317

323318
// Add cookies.
324-
// Clear cookies from Curl's engine.
325-
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_COOKIELIST, "ALL"));
326319
if (header_list.cookies) |cookies| {
327320
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_COOKIE, cookies));
328321
}
@@ -542,6 +535,7 @@ pub const Request = struct {
542535
};
543536

544537
pub const Transfer = struct {
538+
arena: ArenaAllocator,
545539
id: usize = 0,
546540
req: Request,
547541
uri: std.Uri, // used for setting/getting the cookie
@@ -561,6 +555,7 @@ pub const Transfer = struct {
561555
if (self._handle) |handle| {
562556
self.client.handles.release(handle);
563557
}
558+
self.arena.deinit();
564559
self.client.transfer_pool.destroy(self);
565560
}
566561

@@ -595,6 +590,42 @@ pub const Transfer = struct {
595590
self.deinit();
596591
}
597592

593+
// redirectionCookies manages cookies during redirections handled by Curl.
594+
// It sets the cookies from the current response to the cookie jar.
595+
// It also immediately sets cookies for the following request.
596+
fn redirectionCookies(arena: Allocator, easy: *c.CURL, cookie_jar: *storage.CookieJar, origin: *const std.Uri) !void {
597+
// retrieve cookies from the redirect's response.
598+
var i: usize = 0;
599+
while (true) {
600+
const ct = getResponseHeader(easy, "set-cookie", i);
601+
if (ct == null) break;
602+
try cookie_jar.populateFromResponse(origin, ct.?.value);
603+
i += 1;
604+
if (i >= ct.?.amount) break;
605+
}
606+
607+
// set cookies for the following redirection's request.
608+
const hlocation = getResponseHeader(easy, "location", 0);
609+
if (hlocation == null) {
610+
return error.LocationNotFound;
611+
}
612+
613+
var baseurl: [*c]u8 = undefined;
614+
try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &baseurl));
615+
616+
const url = try urlStitch(arena, hlocation.?.value, std.mem.span(baseurl), .{});
617+
const uri = try std.Uri.parse(url);
618+
619+
var cookies: std.ArrayListUnmanaged(u8) = .{};
620+
try cookie_jar.forRequest(&uri, cookies.writer(arena), .{
621+
.is_http = true,
622+
.is_navigation = true,
623+
.origin_uri = origin,
624+
});
625+
try cookies.append(arena, 0); //null terminate
626+
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_COOKIE, @as([*c]const u8, @ptrCast(cookies.items.ptr))));
627+
}
628+
598629
fn headerCallback(buffer: [*]const u8, header_count: usize, buf_len: usize, data: *anyopaque) callconv(.c) usize {
599630
// libcurl should only ever emit 1 header at a time
600631
std.debug.assert(header_count == 1);
@@ -611,18 +642,16 @@ pub const Transfer = struct {
611642

612643
if (transfer.response_header == null) {
613644
if (transfer._redirecting and buf_len == 2) {
614-
// retrieve cookies from the redirect's response.
615-
var i: usize = 0;
616-
while (true) {
617-
const ct = getResponseHeader(easy, "set-cookie", i);
618-
if (ct == null) break;
619-
transfer.req.cookie_jar.populateFromResponse(&transfer.uri, ct.?.value) catch |err| {
620-
log.err(.http, "set cookie", .{ .err = err, .req = transfer });
621-
};
622-
i += 1;
623-
if (i >= ct.?.amount) break;
624-
}
625-
645+
// parse and set cookies for the redirection.
646+
redirectionCookies(
647+
transfer.arena.allocator(),
648+
easy,
649+
transfer.req.cookie_jar,
650+
&transfer.uri,
651+
) catch |err| {
652+
log.debug(.http, "redirection cookies", .{ .err = err });
653+
return 0;
654+
};
626655
return buf_len;
627656
}
628657

src/http/Http.zig

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,6 @@ pub const Connection = struct {
110110
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_FOLLOWLOCATION, @as(c_long, 2)));
111111
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
112112

113-
// enable cookie engine for redirections handled directly by Curl.
114-
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_COOKIEFILE, ""));
115-
116113
// proxy
117114
if (opts.http_proxy) |proxy| {
118115
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_PROXY, proxy.ptr));
@@ -205,8 +202,6 @@ pub const Connection = struct {
205202
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HTTPHEADER, header_list.headers));
206203

207204
// Add cookies.
208-
// Clear cookies from Curl's engine.
209-
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_COOKIELIST, "ALL"));
210205
if (header_list.cookies) |cookies| {
211206
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_COOKIE, cookies));
212207
}

0 commit comments

Comments
 (0)