Skip to content

Commit 11b79d7

Browse files
committed
storage cookies
1 parent d03b73f commit 11b79d7

File tree

4 files changed

+265
-114
lines changed

4 files changed

+265
-114
lines changed

src/browser/storage/cookie.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,8 @@ pub const Cookie = struct {
263263
arena: ArenaAllocator,
264264
name: []const u8,
265265
value: []const u8,
266-
path: []const u8,
267266
domain: []const u8,
267+
path: []const u8,
268268
expires: ?i64,
269269
secure: bool = false,
270270
http_only: bool = false,

src/cdp/cdp.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
204204
asUint("Browser") => return @import("domains/browser.zig").processMessage(command),
205205
asUint("Runtime") => return @import("domains/runtime.zig").processMessage(command),
206206
asUint("Network") => return @import("domains/network.zig").processMessage(command),
207+
asUint("Storage") => return @import("domains/storage.zig").processMessage(command),
207208
else => {},
208209
},
209210
8 => switch (@as(u64, @bitCast(domain[0..8].*))) {

src/cdp/domains/network.zig

Lines changed: 8 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1818

1919
const std = @import("std");
20+
const Allocator = std.mem.Allocator;
21+
2022
const Notification = @import("../../notification.zig").Notification;
2123
const log = @import("../../log.zig");
22-
23-
const Allocator = std.mem.Allocator;
24+
const CdpStorage = @import("storage.zig");
2425

2526
pub fn processMessage(cmd: anytype) !void {
2627
const action = std.meta.stringToEnum(enum {
@@ -79,13 +80,7 @@ fn setExtraHTTPHeaders(cmd: anytype) !void {
7980
return cmd.sendResult(null, .{});
8081
}
8182

82-
const CookiePartitionKey = struct {
83-
topLevelSite: []const u8,
84-
hasCrossSiteAncestor: bool,
85-
};
86-
8783
const Cookie = @import("../../browser/storage/storage.zig").Cookie;
88-
const CookieJar = @import("../../browser/storage/storage.zig").CookieJar;
8984

9085
fn cookieMatches(cookie: *const Cookie, name: []const u8, domain: ?[]const u8, path: ?[]const u8) bool {
9186
if (!std.mem.eql(u8, cookie.name, name)) return false;
@@ -116,7 +111,7 @@ fn deleteCookies(cmd: anytype) !void {
116111
while (index > 0) {
117112
index -= 1;
118113
const cookie = &cookies.items[index];
119-
const domain = try percentEncodedDomain(cmd.arena, params.url, params.domain);
114+
const domain = try CdpStorage.percentEncodedDomain(cmd.arena, params.url, params.domain);
120115
// TBD does chrome take the path from the url as default? (unlike setCookies)
121116
if (cookieMatches(cookie, params.name, domain, params.path)) {
122117
cookies.swapRemove(index).deinit();
@@ -134,130 +129,30 @@ fn clearBrowserCookies(cmd: anytype) !void {
134129
return cmd.sendResult(null, .{});
135130
}
136131

137-
const SameSite = enum {
138-
Strict,
139-
Lax,
140-
None,
141-
};
142-
const CookiePriority = enum {
143-
Low,
144-
Medium,
145-
High,
146-
};
147-
const CookieSourceScheme = enum {
148-
Unset,
149-
NonSecure,
150-
Secure,
151-
};
152-
153-
fn isHostChar(c: u8) bool {
154-
return switch (c) {
155-
'A'...'Z', 'a'...'z', '0'...'9', '-', '.', '_', '~' => true,
156-
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' => true,
157-
':' => true,
158-
'[', ']' => true,
159-
else => false,
160-
};
161-
}
162-
163-
const CdpCookie = struct {
164-
name: []const u8,
165-
value: []const u8,
166-
url: ?[]const u8 = null,
167-
domain: ?[]const u8 = null,
168-
path: ?[]const u8 = null,
169-
secure: bool = false, // default: https://www.rfc-editor.org/rfc/rfc6265#section-5.3
170-
httpOnly: bool = false, // default: https://www.rfc-editor.org/rfc/rfc6265#section-5.3
171-
sameSite: SameSite = .None, // default: https://datatracker.ietf.org/doc/html/draft-west-first-party-cookies
172-
expires: ?i64 = null, // -1? says google
173-
priority: CookiePriority = .Medium, // default: https://datatracker.ietf.org/doc/html/draft-west-cookie-priority-00
174-
sameParty: ?bool = null,
175-
sourceScheme: ?CookieSourceScheme = null,
176-
// sourcePort: Temporary ability and it will be removed from CDP
177-
partitionKey: ?CookiePartitionKey = null,
178-
};
179-
180132
fn setCookie(cmd: anytype) !void {
181133
const params = (try cmd.params(
182-
CdpCookie,
134+
CdpStorage.CdpCookie,
183135
)) orelse return error.InvalidParams;
184136

185137
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
186-
try setCdpCookie(&bc.session.cookie_jar, params);
138+
try CdpStorage.setCdpCookie(&bc.session.cookie_jar, params);
187139

188140
try cmd.sendResult(.{ .success = true }, .{});
189141
}
190142

191143
fn setCookies(cmd: anytype) !void {
192144
const params = (try cmd.params(struct {
193-
cookies: []const CdpCookie,
145+
cookies: []const CdpStorage.CdpCookie,
194146
})) orelse return error.InvalidParams;
195147

196148
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
197149
for (params.cookies) |param| {
198-
try setCdpCookie(&bc.session.cookie_jar, param);
150+
try CdpStorage.setCdpCookie(&bc.session.cookie_jar, param);
199151
}
200152

201153
try cmd.sendResult(null, .{});
202154
}
203155

204-
fn setCdpCookie(cookie_jar: *CookieJar, param: CdpCookie) !void {
205-
if (param.priority != .Medium or param.sameParty != null or param.sourceScheme != null or param.partitionKey != null) {
206-
return error.NotYetImplementedParams;
207-
}
208-
if (param.name.len == 0) return error.InvalidParams;
209-
if (param.value.len == 0) return error.InvalidParams;
210-
211-
var arena = std.heap.ArenaAllocator.init(cookie_jar.allocator);
212-
errdefer arena.deinit();
213-
const a = arena.allocator();
214-
215-
// NOTE: The param.url can affect the default domain, path, source port, and source scheme.
216-
const domain = try percentEncodedDomain(a, param.url, param.domain) orelse return error.InvalidParams;
217-
218-
const cookie = Cookie{
219-
.arena = arena,
220-
.name = try a.dupe(u8, param.name),
221-
.value = try a.dupe(u8, param.value),
222-
.path = if (param.path) |path| try a.dupe(u8, path) else "/", // Chrome does not actually take the path from the url and just defaults to "/".
223-
.domain = domain,
224-
.expires = param.expires,
225-
.secure = param.secure,
226-
.http_only = param.httpOnly,
227-
.same_site = switch (param.sameSite) {
228-
.Strict => .strict,
229-
.Lax => .lax,
230-
.None => .none,
231-
},
232-
};
233-
try cookie_jar.add(cookie, std.time.timestamp());
234-
}
235-
236-
// Note: Chrome does not apply rules like removing a leading `.` from the domain.
237-
fn percentEncodedDomain(allocator: Allocator, default_url: ?[]const u8, domain: ?[]const u8) !?[]const u8 {
238-
const toLower = @import("../../browser/storage/cookie.zig").toLower;
239-
if (domain) |domain_| {
240-
const output = try allocator.dupe(u8, domain_);
241-
return toLower(output);
242-
} else if (default_url) |url| {
243-
const uri = std.Uri.parse(url) catch return error.InvalidParams;
244-
245-
var output: []u8 = undefined;
246-
switch (uri.host orelse return error.InvalidParams) {
247-
.raw => |str| {
248-
var list = std.ArrayList(u8).init(allocator);
249-
try list.ensureTotalCapacity(str.len); // Expect no precents needed
250-
try std.Uri.Component.percentEncode(list.writer(), str, isHostChar);
251-
output = list.items; // @memory retains memory used before growing
252-
},
253-
.percent_encoded => |str| {
254-
output = try allocator.dupe(u8, str);
255-
},
256-
}
257-
return toLower(output);
258-
} else return null;
259-
}
260-
261156
// Upsert a header into the headers array.
262157
// returns true if the header was added, false if it was updated
263158
fn putAssumeCapacity(headers: *std.ArrayListUnmanaged(std.http.Header), extra: std.http.Header) bool {

0 commit comments

Comments
 (0)