add basic CorsGate scaffold

This commit is contained in:
Muki Kiboigo committed 2026-09-04 06:59:37 -07:00
1 parent b114382c44
commit 212c806be5
3 files changed
+158 -4

No files matched your search

+37 -4
View File
@@ -35,6 +35,7 @@ const http = @import("http.zig");
const Network = @import("Network.zig");
const Cache = @import("cache/Cache.zig");
const RobotsGate = @import("RobotsGate.zig");
const CorsGate = @import("CorsGate.zig");
const UrlBlocklist = @import("UrlBlocklist.zig");
pub const BlockPattern = UrlBlocklist.Pattern;
@@ -187,6 +188,7 @@ obey_robots: bool,
http_version: lp.Config.HttpVersion,
robots: RobotsGate,
cors: CorsGate,
url_blocklist: ?UrlBlocklist,
pub fn init(self: *Client, app: *lp.App) !void {
@@ -233,6 +235,10 @@ pub fn init(self: *Client, app: *lp.App) !void {
.network = network,
.single_flight = .init(allocator),
},
.cors = .{
.network = network,
.single_flight = .init(allocator),
},
.url_blocklist = url_blocklist,
.arena_pool = &app.arena_pool,
};
@@ -264,6 +270,7 @@ pub fn deinit(self: *Client) void {
self.clearUrlBlocklist();
self.robots.deinit();
self.cors.deinit();
self.blocking_requests.deinit(self.allocator);
self.transfers.deinit(self.allocator);
self.cache.maintenance(lp.datetime.timestamp(.real));
@@ -461,6 +468,7 @@ pub fn abort(self: *Client) void {
// - self.robots.pending : each robots fetch's shutdown_callback
// drops its entry; parked waiters unlink in their own deinit.
std.debug.assert(self.robots.single_flight.count() == 0);
std.debug.assert(self.cors.single_flight.count() == 0);
}
}
@@ -962,6 +970,7 @@ const SubmitFrom = enum {
start, // Transfer.submit — a brand new request.
redirect, // Followed 3xx. Same as .start, but a distinct name (e.g. for CDP)
after_intercept, // Released by CDP
after_cors, // cors allowed the request.
throttle, // the robots gate allowed the request.
network, // released by throttle
};
@@ -1015,9 +1024,18 @@ fn pipeline(self: *Client, transfer: *Transfer, from: SubmitFrom) !void {
return transfer.failAsync(error.UrlBlocked);
}
if (try self.cacheLookup(transfer)) {
// response came from the cache, we're done
return;
}
if (!transfer.req.internal) {
switch (try self.cors.check(transfer)) {
.allowed => {},
.blocked => return transfer.failAsync(error.CorsBlocked),
.pending => return,
}
}
continue :sw SubmitFrom.after_cors;
},
.after_cors => {
if (self.obey_robots and !transfer.req.internal) {
switch (try self.robots.check(transfer)) {
.allowed => {
@@ -1059,6 +1077,12 @@ pub fn resumeAfterRobots(self: *Client, transfer: *Transfer) !void {
return self.pipeline(transfer, .throttle);
}
// CorsGate resumption after a preflight resolves as allowed. Re-enters
// right after the CORS step (not .after_intercept)
pub fn resumeAfterCors(self: *Client, transfer: *Transfer) !void {
return self.pipeline(transfer, .after_cors);
}
fn findHeader(headers: []const http.Header, name: []const u8) ?[]const u8 {
for (headers) |hdr| {
if (std.ascii.eqlIgnoreCase(hdr.name, name)) {
@@ -2162,6 +2186,8 @@ pub const Transfer = struct {
// everything and sits on client.graveyard
_retired: bool = false,
_cors_cross_origin: bool = false,
pub const State = union(enum) {
// Pre-commit. Only valid inside the request flow (Client.request
// or a re-entry like continueTransfer / unpark) before any commit
@@ -2214,6 +2240,9 @@ pub const Transfer = struct {
// RobotsGate holds the transfer pending a robots.txt fetch.
robots,
// CorsGate holds the tranfer pending a CORS preflight.
cors,
};
pub const HeaderResult = enum {
@@ -2249,7 +2278,7 @@ pub const Transfer = struct {
return;
}
switch (self.state.parked) {
.robots => {},
.robots, .cors => {},
.intercept_request, .intercept_auth => {
lp.assert(self.client.intercepted > 0, "Transfer.leaveIntercept", .{ .value = self.client.intercepted });
self.client.intercepted -= 1;
@@ -2388,8 +2417,12 @@ pub const Transfer = struct {
// And for the robots gate: RobotsGate.pending holds a raw *Transfer
// while we're parked.
if (self.state == .parked and self.state.parked == .robots) {
self.client.robots.remove(self);
if (self.state == .parked) {
switch (self.state.parked) {
.cors => self.client.cors.remove(self),
.robots => self.client.robots.remove(self),
.intercept_auth, .intercept_request => {},
}
}
// A pending revalidation entry owns cache resources (possibly an