mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-26 02:12:50 -04:00
Depends on https://github.com/lightpanda-io/browser/pull/993 There's currently 3 ways to execute a page: 1 - page.navigate (as used in both the 'fetch' and 'serve' commands) 2 - jsRunner as used in unit tests 3 - main_wpt as used in the WPT runner Both jsRunner and main_wpt replicate the page.navigate code, but in their own hack-ish way. main_wpt re-implements the DOM walking in order to extract and execute <script> tags, as well as the needed page lifecycle events. This PR replaces the existing main_wpt loader with a call to page.navigate. To support this, a test HTTP server was added. (The test HTTP server is extracted from the existing unit test test server, and re-used between the two). There are benefits to this approach: 1 - The code is simpler 2 - More of the actual code and flow is tested 3 - There's 1 way to do things (page.navigate) 4 - Having an HTTP server might unlock some WPT tests Technically, we're replacing file IO with network IO i.e. http requests). This has potential downsides: 1 - The tests might be more brittle 2 - The tests might be slower I think we need to run it for a while to see if we get flaky behavior. The goal for following PRs is to bring this unification to the jsRunner.
118 lines
3.3 KiB
Zig
118 lines
3.3 KiB
Zig
const std = @import("std");
|
|
|
|
const TestHTTPServer = @This();
|
|
|
|
shutdown: bool,
|
|
listener: ?std.net.Server,
|
|
handler: Handler,
|
|
|
|
const Handler = *const fn (req: *std.http.Server.Request) anyerror!void;
|
|
|
|
pub fn init(handler: Handler) TestHTTPServer {
|
|
return .{
|
|
.shutdown = true,
|
|
.listener = null,
|
|
.handler = handler,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *TestHTTPServer) void {
|
|
self.shutdown = true;
|
|
if (self.listener) |*listener| {
|
|
listener.deinit();
|
|
}
|
|
}
|
|
|
|
pub fn run(self: *TestHTTPServer, wg: *std.Thread.WaitGroup) !void {
|
|
const address = try std.net.Address.parseIp("127.0.0.1", 9582);
|
|
|
|
self.listener = try address.listen(.{ .reuse_address = true });
|
|
var listener = &self.listener.?;
|
|
|
|
wg.finish();
|
|
|
|
while (true) {
|
|
const conn = listener.accept() catch |err| {
|
|
if (self.shutdown) {
|
|
return;
|
|
}
|
|
return err;
|
|
};
|
|
const thrd = try std.Thread.spawn(.{}, handleConnection, .{ self, conn });
|
|
thrd.detach();
|
|
}
|
|
}
|
|
|
|
fn handleConnection(self: *TestHTTPServer, conn: std.net.Server.Connection) !void {
|
|
defer conn.stream.close();
|
|
|
|
var req_buf: [2048]u8 = undefined;
|
|
var conn_reader = conn.stream.reader(&req_buf);
|
|
var conn_writer = conn.stream.writer(&req_buf);
|
|
|
|
var http_server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface);
|
|
|
|
while (true) {
|
|
var req = http_server.receiveHead() catch |err| switch (err) {
|
|
error.ReadFailed => continue,
|
|
error.HttpConnectionClosing => continue,
|
|
else => {
|
|
std.debug.print("Test HTTP Server error: {}\n", .{err});
|
|
return err;
|
|
},
|
|
};
|
|
self.handler(&req) catch |err| {
|
|
std.debug.print("test http error '{s}': {}\n", .{ req.head.target, err });
|
|
try req.respond("server error", .{ .status = .internal_server_error });
|
|
return;
|
|
};
|
|
}
|
|
}
|
|
|
|
pub fn sendFile(req: *std.http.Server.Request, file_path: []const u8) !void {
|
|
var file = std.fs.cwd().openFile(file_path, .{}) catch |err| switch (err) {
|
|
error.FileNotFound => return req.respond("server error", .{ .status = .not_found }),
|
|
else => return err,
|
|
};
|
|
|
|
const stat = try file.stat();
|
|
var send_buffer: [4096]u8 = undefined;
|
|
|
|
var res = try req.respondStreaming(&send_buffer, .{
|
|
.content_length = stat.size,
|
|
.respond_options = .{
|
|
.extra_headers = &.{
|
|
.{ .name = "content-type", .value = getContentType(file_path) },
|
|
},
|
|
},
|
|
});
|
|
|
|
var read_buffer: [4096]u8 = undefined;
|
|
var reader = file.reader(&read_buffer);
|
|
_ = try res.writer.sendFileAll(&reader, .unlimited);
|
|
try res.writer.flush();
|
|
try res.end();
|
|
}
|
|
|
|
fn getContentType(file_path: []const u8) []const u8 {
|
|
if (std.mem.endsWith(u8, file_path, ".js")) {
|
|
return "application/json";
|
|
}
|
|
|
|
if (std.mem.endsWith(u8, file_path, ".html")) {
|
|
return "text/html";
|
|
}
|
|
|
|
if (std.mem.endsWith(u8, file_path, ".htm")) {
|
|
return "text/html";
|
|
}
|
|
|
|
if (std.mem.endsWith(u8, file_path, ".xml")) {
|
|
// some wpt tests do this
|
|
return "text/xml";
|
|
}
|
|
|
|
std.debug.print("TestHTTPServer asked to serve an unknown file type: {s}\n", .{file_path});
|
|
return "text/html";
|
|
}
|