Merge pull request #3654 from lightpanda-io/cors-by-default

Enable CORS by default
This commit is contained in:
Pierre Tachoire authored and GitHub committed 2026-09-28 14:32:49 +02:00
commit f6dd43c346
9 files changed
+63 -16

No files matched your search

+1 -1
View File
@@ -69,7 +69,7 @@ jobs:
- name: run end to end integration tests
continue-on-error: true
run: |
./lightpanda serve --experimental-features cors --http-cache-dir /tmp/lp-cache --http-proxy ${{ secrets.MASSIVE_PROXY_RESIDENTIAL_US }} --log-level error & echo $! > LPD.pid
./lightpanda serve --http-cache-dir /tmp/lp-cache --http-proxy ${{ secrets.MASSIVE_PROXY_RESIDENTIAL_US }} --log-level error & echo $! > LPD.pid
go run integration/main.go |tee result.log
kill `cat LPD.pid`
+4 -6
View File
@@ -117,7 +117,7 @@ jobs:
echo "value=$args" >> "$GITHUB_OUTPUT"
- run: |
./lightpanda serve --experimental-features cors --port 9222 ${{ steps.args.outputs.value }} &
./lightpanda serve --port 9222 ${{ steps.args.outputs.value }} &
- run: |
go run runner/main.go
@@ -163,7 +163,7 @@ jobs:
- id: args
name: build LP args
run: |
args="--experimental-features cors --http-cache-dir /tmp/lp-cache --load-resources worker --load-resources iframe"
args="--http-cache-dir /tmp/lp-cache --load-resources worker --load-resources iframe"
args="$args --protocol cdp --protocol webdriver"
[ "${{ matrix.robotstxt }}" = "true" ] && args="$args --obey-robots"
[ "${{ matrix.wba }}" = "true" ] && args="$args --web-bot-auth-key-file private_key.pem"
@@ -227,7 +227,6 @@ jobs:
exec 3<<< "${{ secrets.WBA_PRIVATE_KEY_PEM }}"
./lightpanda fetch --dump http://127.0.0.1:8989/ \
--experimental-features cors \
--web-bot-auth-key-file /proc/self/fd/3 \
--web-bot-auth-keyid ${{ vars.WBA_KEY_ID }} \
--web-bot-auth-domain ${{ vars.WBA_DOMAIN }}
@@ -251,7 +250,6 @@ jobs:
exec 3<<< "${{ secrets.WBA_PRIVATE_KEY_PEM }}"
./lightpanda fetch --dump "http://127.0.0.1:8989/redirect?to=http://127.0.0.1:8990/" \
--experimental-features cors \
--web-bot-auth-key-file /proc/self/fd/3 \
--web-bot-auth-keyid ${{ vars.WBA_KEY_ID }} \
--web-bot-auth-domain ${{ vars.WBA_DOMAIN }}
@@ -430,7 +428,7 @@ jobs:
- run: chmod a+x ./lightpanda
- run: ./lightpanda fetch --experimental-features cors "https://demo-browser.lightpanda.io/campfire-commerce/"
- run: ./lightpanda fetch "https://demo-browser.lightpanda.io/campfire-commerce/"
agent-deterministic:
name: agent deterministic replay
@@ -483,7 +481,7 @@ jobs:
jq --version
timeout 30 ./lightpanda mcp --experimental-features cors > mcp.out <<'JSONRPC'
timeout 30 ./lightpanda mcp > mcp.out <<'JSONRPC'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci","version":"1.0.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized"}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
+2 -3
View File
@@ -258,7 +258,7 @@ Set `LIGHTPANDA_DISABLE_CORE_DUMP` (to any value) to suppress crash core dumps b
Here are the key features we have implemented.
For full details, see our [Web Platform Tests results](https://perf.lightpanda.io/wpt).
- [x] CORS (enable with `--experimental-features cors`)
- [x] CORS
- [x] HTTP loader ([Libcurl](https://curl.se/libcurl/))
- [x] HTML parser ([html5ever](https://github.com/servo/html5ever))
- [x] DOM tree
@@ -421,8 +421,7 @@ zig build -Dwpt_extensions run -- serve \
--load-resources iframe \
--load-resources image \
--load-resources worker \
--load-resources stylesheet \
--experimental-features cors
--load-resources stylesheet
```
Then you can start the wptrunner from the demo's clone dir:
+13 -1
View File
@@ -239,10 +239,14 @@ pub const LoadResources = packed struct(u4) {
};
pub const ExperimentalFeatures = packed struct(u2) {
cors: bool = false,
cors: bool = false, // ignored, kept only for backward compatibility.
serviceworker: bool = false,
};
pub const DisabledFeatures = packed struct(u1) {
cors: bool = false,
};
/// Common CLI args.
const CommonOptions = .{
.{ .name = "obey_robots", .type = bool },
@@ -283,6 +287,7 @@ const CommonOptions = .{
.{ .name = "disable_subframes", .type = bool, .deprecated = "subframes are now disabled by default, use \"--load-resources iframe\" to enable" },
.{ .name = "disable_workers", .type = bool, .deprecated = "workers are now disabled by default, use \"--load-resources worker\" to enable" },
.{ .name = "enable_external_stylesheets", .type = bool, .deprecated = "use \"--load-resources stylesheet\" to enable" },
.{ .name = "disable_features", .type = DisabledFeatures, .default = DisabledFeatures{} },
.{ .name = "experimental_features", .type = ExperimentalFeatures, .default = ExperimentalFeatures{} },
.{ .name = "load_resources", .type = LoadResources, .default = LoadResources{} },
.{ .name = "v8_flags_unsafe", .type = ?[]const u8 },
@@ -552,6 +557,13 @@ pub fn tlsVerifyHost(self: *const Config) bool {
};
}
pub fn obeyCors(self: *const Config) bool {
return switch (self.mode) {
inline .serve, .fetch, .mcp, .agent => |opts| opts.disable_features.cors == false,
else => unreachable,
};
}
pub fn obeyRobots(self: *const Config) bool {
return switch (self.mode) {
inline .serve, .fetch, .mcp, .agent => |opts| opts.obey_robots,
+4
View File
@@ -844,6 +844,10 @@ test "Env: Worker context " {
const frame = try testing.createFrame();
defer testing.test_session.closeAllPages();
// Navigate the frame first to avoid CORS blocking request for the worker.
try frame.navigate("http://localhost:9582/", .{});
try testing.waitForFrame();
const worker = try @import("../webapi/Worker.zig").init("http://localhost:9582/src/browser/tests/testing.js", null, frame);
var ls: js.Local.Scope = undefined;
+2
View File
@@ -766,6 +766,8 @@ pub fn Builder(comptime commands: anytype) type {
break :blk command.options;
};
// toKebabCase walks every byte of every name.
@setEvalBranchQuota(50_000);
iter_args: while (args.next()) |option_name| {
inline for (options) |option| {
const name = option.name;
+5 -4
View File
@@ -409,14 +409,15 @@
\\ --cors-store-entry-limit <INT>
\\ Maximum number of entries kept in the CorsStore. 0 means no limit.
\\ Defaults to 1000.
\\ --disable-features <FEATURE>
\\ Disable a feature that is on by default. Can be passed multiple times.
\\ Defaults to none disabled.
\\ Allowed values:
\\ cors Skip CORS checks on fetch/XHR requests.
\\ --experimental-features <FEATURE>
\\ Enable an experimental, unstable feature. Can be passed multiple times.
\\ Behavior may change or be removed without notice.
\\ Defaults to none enabled.
\\ Allowed values:
\\ cors Obey CORS (cross-origin resource sharing) checks
\\ on fetch/XHR requests instead of allowing them
\\ unconditionally.
\\ --load-resources <RESOURCE>
\\ Sub-resource to actually request. Can be passed multiple times.
\\ Defaults to requesting none of them.
+1 -1
View File
@@ -244,7 +244,7 @@ pub fn init(self: *Client, app: *lp.App) !void {
.serve_mode = config.mode == .serve,
.obey_robots = config.obeyRobots(),
.http_version = config.httpVersion(),
.obey_cors = config.experimentalFeatures().cors,
.obey_cors = config.obeyCors(),
.robots = .{
.network = network,
.single_flight = .init(allocator),
+31
View File
@@ -635,9 +635,28 @@ var serve_counts = [_]struct { name: []const u8, count: u32 = 0 }{
.{ .name = "prescan_module" },
};
fn origin(req: *std.http.Server.Request) ?[]const u8 {
var it = req.iterateHeaders();
while (it.next()) |h| {
if (std.mem.eql(u8, "origin", h.name)) {
return h.value;
}
}
return null;
}
fn testHTTPHandler(req: *std.http.Server.Request) !void {
const path = req.head.target;
if (std.mem.eql(u8, path, "/")) {
return req.respond("<html><head></head><body></body></html>", .{
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/html; charset=utf-8" },
},
});
}
if (std.mem.eql(u8, path, "/xhr")) {
return req.respond("1234567890" ** 10, .{
.extra_headers = &.{
@@ -1155,6 +1174,7 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
return req.respond(html, .{
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/html; charset=utf-8" },
.{ .name = "Access-Control-Allow-Origin", .value = origin(req) orelse "*" },
},
});
}
@@ -1201,6 +1221,16 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
}
if (std.mem.eql(u8, path, "/echo_headers")) {
if (req.head.method == .OPTIONS) {
return req.respond("", .{
.extra_headers = &.{
.{ .name = "Access-Control-Allow-Origin", .value = origin(req) orelse "*" },
.{ .name = "Access-Control-Allow-Methods", .value = "GET" },
.{ .name = "Access-Control-Allow-Headers", .value = "x-hop" },
},
});
}
// Echo every request header back as "name: value" lines, so tests
// can assert on the headers a request actually sent.
var buf: [8192]u8 = undefined;
@@ -1213,6 +1243,7 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void {
return req.respond(buf[0..pos], .{
.extra_headers = &.{
.{ .name = "Content-Type", .value = "text/plain; charset=utf-8" },
.{ .name = "Access-Control-Allow-Origin", .value = origin(req) orelse "*" },
},
});
}