From 21475669810bf35862dfd8e937c4e40cc6c1e4e6 Mon Sep 17 00:00:00 2001 From: Muki Kiboigo Date: Thu, 10 Sep 2026 12:19:36 -0700 Subject: [PATCH] fix more WPT test edge cases for preflight cache --- src/network/CorsGate.zig | 39 +++++++++++++++++++-------------------- src/network/CorsStore.zig | 14 +++++++------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/network/CorsGate.zig b/src/network/CorsGate.zig index db065eed4..8511bc141 100644 --- a/src/network/CorsGate.zig +++ b/src/network/CorsGate.zig @@ -217,19 +217,17 @@ pub fn check(self: *CorsGate, transfer: *Transfer) !Result { return .allowed; } - if (try URL.getOrigin(transfer.arena.allocator(), req.url)) |target| { - if (self.network.cors_store.get(.{ .origin = origin, .target = target })) |cached| { - const authored = try collectAuthoredHeaders(transfer, transfer.arena.allocator()); - if (CorsStore.covers(cached, req.method, req.credentials_mode == .include, authored.items)) { - log.debug(.cors, "cross origin", .{ - .url = req.url, - .origin = origin, - .preflight = false, - .cached = true, - }); - lp.metrics.cors_check.incr(.cached); - return .allowed; - } + if (self.network.cors_store.get(.{ .origin = origin, .target = req.url })) |cached| { + const authored = try collectAuthoredHeaders(transfer, transfer.arena.allocator()); + if (CorsStore.covers(cached, req.method, req.credentials_mode == .include, authored.items)) { + log.debug(.cors, "cross origin", .{ + .url = req.url, + .origin = origin, + .preflight = false, + .cached = true, + }); + lp.metrics.cors_check.incr(.cached); + return .allowed; } } @@ -295,7 +293,6 @@ const CorsPreflightContext = struct { key: []const u8, url: [:0]const u8, origin: []const u8, - target: []const u8, method: http.Method, request_headers: []const []const u8, wants_credentials: bool, @@ -393,9 +390,13 @@ const CorsPreflightContext = struct { } fn cacheGrant(self: *CorsPreflightContext, acam: ?[]const u8, acah: ?[]const u8, acma: ?[]const u8) !void { - if (self.target.len == 0) return; + if (self.url.len == 0) return; - const max_age_s: u64 = if (acma) |v| std.fmt.parseUnsigned(u64, v, 10) catch return else return; + const max_age_s: u64 = blk: { + const v = acma orelse break :blk 5; + if (v.len == 0) break :blk 5; + break :blk std.fmt.parseUnsigned(u64, v, 10) catch return; + }; if (max_age_s == 0) return; const capped_s: u64 = @min(max_age_s, 7200); @@ -426,14 +427,14 @@ const CorsPreflightContext = struct { } try self.gate.network.cors_store.put( - .{ .origin = self.origin, .target = self.target }, + .{ .origin = self.origin, .target = self.url }, .{ .credentials = self.wants_credentials, .methods_wildcard = methods_wildcard, .methods = methods, .headers_wildcard = headers_wildcard, .headers = owned_headers, - .expires_at = lp.datetime.timestamp(.real) + capped_ms, + .expires_at = lp.datetime.milliTimestamp(.real) + capped_ms, }, ); } @@ -576,7 +577,6 @@ fn fetchThenResume(self: *CorsGate, transfer: *Transfer) !void { const owned_url = try arena.dupeZ(u8, transfer.req.url); const owned_key = try arena.dupe(u8, key); const owned_origin = try arena.dupe(u8, origin); - const owned_target = try URL.getOrigin(arena.allocator(), transfer.req.url) orelse ""; const owned_header_names = try arena.alloc([]const u8, header_names.items.len); for (header_names.items, 0..) |name, i| { @@ -591,7 +591,6 @@ fn fetchThenResume(self: *CorsGate, transfer: *Transfer) !void { .key = owned_key, .url = owned_url, .origin = owned_origin, - .target = owned_target, .method = transfer.req.method, .request_headers = owned_header_names, .wants_credentials = transfer.req.credentials_mode == .include, diff --git a/src/network/CorsStore.zig b/src/network/CorsStore.zig index 696c5676a..482639c0d 100644 --- a/src/network/CorsStore.zig +++ b/src/network/CorsStore.zig @@ -159,7 +159,7 @@ pub fn get(self: *CorsStore, key: Key) ?Entry { const entry = self.map.get(key) orelse return null; - if (entry.expires_at <= lp.datetime.timestamp(.real)) { + if (entry.expires_at <= lp.datetime.milliTimestamp(.real)) { const kv = self.map.fetchRemove(key).?; kv.key.deinit(self.allocator); kv.value.deinit(self.allocator); @@ -250,7 +250,7 @@ test "CorsStore: put then get, miss on different origin/target" { .methods = std.EnumSet(http.Method).initOne(.POST), .headers_wildcard = false, .headers = headers, - .expires_at = lp.datetime.timestamp(.real) + 60_000, + .expires_at = lp.datetime.milliTimestamp(.real) + 60_000, }); // store.get returns the store's own copy — not caller-owned, don't free it. @@ -273,7 +273,7 @@ test "CorsStore: expired entries are evicted on get" { .methods = .initEmpty(), .headers_wildcard = true, .headers = &.{}, // empty slice, nothing to free - .expires_at = lp.datetime.timestamp(.real) - 1, + .expires_at = lp.datetime.milliTimestamp(.real) - 1, }); try testing.expectEqual(null, store.get(.{ .origin = "https://a.example", .target = "https://api.example" })); @@ -295,7 +295,7 @@ test "CorsStore: put merges into existing entry rather than clobbering" { .methods = std.EnumSet(http.Method).initOne(.POST), .headers_wildcard = false, .headers = h1, - .expires_at = lp.datetime.timestamp(.real) + 60_000, + .expires_at = lp.datetime.milliTimestamp(.real) + 60_000, }); freeHeaders(allocator, h1); @@ -307,7 +307,7 @@ test "CorsStore: put merges into existing entry rather than clobbering" { .methods = std.EnumSet(http.Method).initOne(.PUT), .headers_wildcard = false, .headers = h2, - .expires_at = lp.datetime.timestamp(.real) + 60_000, + .expires_at = lp.datetime.milliTimestamp(.real) + 60_000, }); freeHeaders(allocator, h2); @@ -328,7 +328,7 @@ test "CorsStore: covers rejects credentialed request against uncredentialed wild .methods = .initEmpty(), .headers_wildcard = true, .headers = &.{}, - .expires_at = std.math.maxInt(i64), + .expires_at = std.math.maxInt(u64), }; try testing.expect(!CorsStore.covers(entry, .GET, true, &.{})); try testing.expect(CorsStore.covers(entry, .GET, false, &.{})); @@ -341,7 +341,7 @@ test "CorsStore: covers never lets a wildcard cover Authorization" { .methods = .initEmpty(), .headers_wildcard = true, .headers = &.{}, - .expires_at = std.math.maxInt(i64), + .expires_at = std.math.maxInt(u64), }; try testing.expect(!CorsStore.covers(entry, .GET, false, &.{"authorization"})); try testing.expect(CorsStore.covers(entry, .GET, false, &.{"x-anything"}));