mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
replace zig-js-runtime
This commit is contained in:
277
src/main.zig
277
src/main.zig
@@ -20,25 +20,19 @@ const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const jsruntime = @import("jsruntime");
|
||||
|
||||
const App = @import("app.zig").App;
|
||||
const Browser = @import("browser/browser.zig").Browser;
|
||||
const server = @import("server.zig");
|
||||
const App = @import("app.zig").App;
|
||||
const Platform = @import("runtime/js.zig").Platform;
|
||||
const Browser = @import("browser/browser.zig").Browser;
|
||||
|
||||
const parser = @import("netsurf");
|
||||
const apiweb = @import("apiweb.zig");
|
||||
|
||||
pub const Types = jsruntime.reflect(apiweb.Interfaces);
|
||||
pub const UserContext = apiweb.UserContext;
|
||||
pub const IO = @import("asyncio").Wrapper(jsruntime.Loop);
|
||||
const parser = @import("browser/netsurf.zig");
|
||||
const version = @import("build_info").git_commit;
|
||||
|
||||
const log = std.log.scoped(.cli);
|
||||
|
||||
pub const std_options = std.Options{
|
||||
// Set the log level to info
|
||||
.log_level = .debug,
|
||||
.log_level = .info,
|
||||
|
||||
// Define logFn to override the std implementation
|
||||
.logFn = logFn,
|
||||
@@ -60,23 +54,34 @@ pub fn main() !void {
|
||||
const args = try parseArgs(args_arena.allocator());
|
||||
|
||||
switch (args.mode) {
|
||||
.help => args.printUsageAndExit(args.mode.help),
|
||||
.help => {
|
||||
args.printUsageAndExit(args.mode.help);
|
||||
return std.process.cleanExit();
|
||||
},
|
||||
.version => {
|
||||
std.debug.print("{s}\n", .{version});
|
||||
return std.process.cleanExit();
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
const platform = Platform.init();
|
||||
defer platform.deinit();
|
||||
|
||||
var app = try App.init(alloc, .{
|
||||
.run_mode = args.mode,
|
||||
.gc_hints = args.gcHints(),
|
||||
.tls_verify_host = args.tlsVerifyHost(),
|
||||
});
|
||||
defer app.deinit();
|
||||
app.telemetry.record(.{ .run = {} });
|
||||
|
||||
switch (args.mode) {
|
||||
.serve => |opts| {
|
||||
const address = std.net.Address.parseIp4(opts.host, opts.port) catch |err| {
|
||||
log.err("address (host:port) {any}\n", .{err});
|
||||
return args.printUsageAndExit(false);
|
||||
};
|
||||
var app = try App.init(alloc, .{
|
||||
.run_mode = args.mode,
|
||||
.tls_verify_host = opts.tls_verify_host,
|
||||
});
|
||||
defer app.deinit();
|
||||
|
||||
app.telemetry.record(.{ .run = {} });
|
||||
|
||||
const timeout = std.time.ns_per_s * @as(u64, opts.timeout);
|
||||
server.run(app, address, timeout) catch |err| {
|
||||
@@ -88,19 +93,8 @@ pub fn main() !void {
|
||||
log.debug("Fetch mode: url {s}, dump {any}", .{ opts.url, opts.dump });
|
||||
const url = try @import("url.zig").URL.parse(opts.url, null);
|
||||
|
||||
var app = try App.init(alloc, .{
|
||||
.run_mode = args.mode,
|
||||
.tls_verify_host = opts.tls_verify_host,
|
||||
});
|
||||
defer app.deinit();
|
||||
app.telemetry.record(.{ .run = {} });
|
||||
|
||||
// vm
|
||||
const vm = jsruntime.VM.init();
|
||||
defer vm.deinit();
|
||||
|
||||
// browser
|
||||
var browser = Browser.init(app);
|
||||
var browser = try Browser.init(app);
|
||||
defer browser.deinit();
|
||||
|
||||
var session = try browser.newSession({});
|
||||
@@ -126,6 +120,7 @@ pub fn main() !void {
|
||||
try page.dump(std.io.getStdOut());
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +128,21 @@ const Command = struct {
|
||||
mode: Mode,
|
||||
exec_name: []const u8,
|
||||
|
||||
fn gcHints(self: *const Command) bool {
|
||||
return switch (self.mode) {
|
||||
.serve => |opts| opts.gc_hints,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn tlsVerifyHost(self: *const Command) bool {
|
||||
return switch (self.mode) {
|
||||
.serve => |opts| opts.tls_verify_host,
|
||||
.fetch => |opts| opts.tls_verify_host,
|
||||
else => true,
|
||||
};
|
||||
}
|
||||
|
||||
const Mode = union(App.RunMode) {
|
||||
help: bool, // false when being printed because of an error
|
||||
fetch: Fetch,
|
||||
@@ -144,6 +154,7 @@ const Command = struct {
|
||||
host: []const u8,
|
||||
port: u16,
|
||||
timeout: u16,
|
||||
gc_hints: bool,
|
||||
tls_verify_host: bool,
|
||||
};
|
||||
|
||||
@@ -187,6 +198,9 @@ const Command = struct {
|
||||
\\--timeout Inactivity timeout in seconds before disconnecting clients
|
||||
\\ Defaults to 3 (seconds)
|
||||
\\
|
||||
\\--gc_hints Encourage V8 to cleanup garbage for each new browser context.
|
||||
\\ Defaults to false
|
||||
\\
|
||||
\\--insecure_disable_tls_host_verification
|
||||
\\ Disables host verification on all HTTP requests.
|
||||
\\ This is an advanced option which should only be
|
||||
@@ -266,6 +280,11 @@ fn inferMode(opt: []const u8) ?App.RunMode {
|
||||
if (std.mem.eql(u8, opt, "--timeout")) {
|
||||
return .serve;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, opt, "--gc_hints")) {
|
||||
return .serve;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -276,6 +295,7 @@ fn parseServeArgs(
|
||||
var host: []const u8 = "127.0.0.1";
|
||||
var port: u16 = 9222;
|
||||
var timeout: u16 = 3;
|
||||
var gc_hints = false;
|
||||
var tls_verify_host = true;
|
||||
|
||||
while (args.next()) |opt| {
|
||||
@@ -319,6 +339,11 @@ fn parseServeArgs(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.mem.eql(u8, "--gc_hints", opt)) {
|
||||
gc_hints = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
log.err("Unknown option to serve command: '{s}'", .{opt});
|
||||
return error.UnkownOption;
|
||||
}
|
||||
@@ -327,6 +352,7 @@ fn parseServeArgs(
|
||||
.host = host,
|
||||
.port = port,
|
||||
.timeout = timeout,
|
||||
.gc_hints = gc_hints,
|
||||
.tls_verify_host = tls_verify_host,
|
||||
};
|
||||
}
|
||||
@@ -388,3 +414,192 @@ fn logFn(
|
||||
// default std log function.
|
||||
std.log.defaultLog(level, scope, format, args);
|
||||
}
|
||||
|
||||
test {
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
var test_wg: std.Thread.WaitGroup = .{};
|
||||
test "tests:beforeAll" {
|
||||
try parser.init();
|
||||
test_wg.startMany(3);
|
||||
_ = Platform.init();
|
||||
|
||||
{
|
||||
const address = try std.net.Address.parseIp("127.0.0.1", 9582);
|
||||
const thread = try std.Thread.spawn(.{}, serveHTTP, .{address});
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
{
|
||||
const address = try std.net.Address.parseIp("127.0.0.1", 9581);
|
||||
const thread = try std.Thread.spawn(.{}, serveHTTPS, .{address});
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
{
|
||||
const address = try std.net.Address.parseIp("127.0.0.1", 9583);
|
||||
const thread = try std.Thread.spawn(.{}, serveCDP, .{address});
|
||||
thread.detach();
|
||||
}
|
||||
|
||||
// need to wait for the servers to be listening, else tests will fail because
|
||||
// they aren't able to connect.
|
||||
test_wg.wait();
|
||||
}
|
||||
|
||||
test "tests:afterAll" {
|
||||
parser.deinit();
|
||||
}
|
||||
|
||||
fn serveHTTP(address: std.net.Address) !void {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var listener = try address.listen(.{ .reuse_address = true });
|
||||
defer listener.deinit();
|
||||
|
||||
test_wg.finish();
|
||||
|
||||
var read_buffer: [1024]u8 = undefined;
|
||||
ACCEPT: while (true) {
|
||||
defer _ = arena.reset(.{ .free_all = {} });
|
||||
const aa = arena.allocator();
|
||||
|
||||
var conn = try listener.accept();
|
||||
defer conn.stream.close();
|
||||
var http_server = std.http.Server.init(conn, &read_buffer);
|
||||
|
||||
while (http_server.state == .ready) {
|
||||
var request = http_server.receiveHead() catch |err| switch (err) {
|
||||
error.HttpConnectionClosing => continue :ACCEPT,
|
||||
else => {
|
||||
std.debug.print("Test HTTP Server error: {}\n", .{err});
|
||||
return err;
|
||||
},
|
||||
};
|
||||
|
||||
const path = request.head.target;
|
||||
if (std.mem.eql(u8, path, "/loader")) {
|
||||
try request.respond("Hello!", .{});
|
||||
} else if (std.mem.eql(u8, path, "/http_client/simple")) {
|
||||
try request.respond("", .{});
|
||||
} else if (std.mem.eql(u8, path, "/http_client/redirect")) {
|
||||
try request.respond("", .{
|
||||
.status = .moved_permanently,
|
||||
.extra_headers = &.{.{ .name = "LOCATION", .value = "../http_client/echo" }},
|
||||
});
|
||||
} else if (std.mem.eql(u8, path, "/http_client/redirect/secure")) {
|
||||
try request.respond("", .{
|
||||
.status = .moved_permanently,
|
||||
.extra_headers = &.{.{ .name = "LOCATION", .value = "https://127.0.0.1:9581/http_client/body" }},
|
||||
});
|
||||
} else if (std.mem.eql(u8, path, "/http_client/echo")) {
|
||||
var headers: std.ArrayListUnmanaged(std.http.Header) = .{};
|
||||
|
||||
var it = request.iterateHeaders();
|
||||
while (it.next()) |hdr| {
|
||||
try headers.append(aa, .{
|
||||
.name = try std.fmt.allocPrint(aa, "_{s}", .{hdr.name}),
|
||||
.value = hdr.value,
|
||||
});
|
||||
}
|
||||
|
||||
try request.respond("over 9000!", .{
|
||||
.status = .created,
|
||||
.extra_headers = headers.items,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a lot of work for testing TLS, but the TLS (async) code is complicated
|
||||
// This "server" is written specifically to test the client. It assumes the client
|
||||
// isn't a jerk.
|
||||
fn serveHTTPS(address: std.net.Address) !void {
|
||||
const tls = @import("tls");
|
||||
|
||||
var listener = try address.listen(.{ .reuse_address = true });
|
||||
defer listener.deinit();
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
test_wg.finish();
|
||||
|
||||
var seed: u64 = undefined;
|
||||
std.posix.getrandom(std.mem.asBytes(&seed)) catch unreachable;
|
||||
var r = std.Random.DefaultPrng.init(seed);
|
||||
const rand = r.random();
|
||||
|
||||
var read_buffer: [1024]u8 = undefined;
|
||||
while (true) {
|
||||
// defer _ = arena.reset(.{ .retain_with_limit = 1024 });
|
||||
// const aa = arena.allocator();
|
||||
|
||||
const stream = blk: {
|
||||
const conn = try listener.accept();
|
||||
break :blk conn.stream;
|
||||
};
|
||||
defer stream.close();
|
||||
|
||||
var conn = try tls.server(stream, .{ .auth = null });
|
||||
defer conn.close() catch {};
|
||||
|
||||
var pos: usize = 0;
|
||||
while (true) {
|
||||
const n = try conn.read(read_buffer[pos..]);
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
pos += n;
|
||||
const header_end = std.mem.indexOf(u8, read_buffer[0..pos], "\r\n\r\n") orelse {
|
||||
continue;
|
||||
};
|
||||
var it = std.mem.splitScalar(u8, read_buffer[0..header_end], ' ');
|
||||
_ = it.next() orelse unreachable; // method
|
||||
const path = it.next() orelse unreachable;
|
||||
|
||||
var response: []const u8 = undefined;
|
||||
if (std.mem.eql(u8, path, "/http_client/simple")) {
|
||||
response = "HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n";
|
||||
} else if (std.mem.eql(u8, path, "/http_client/body")) {
|
||||
response = "HTTP/1.1 201 CREATED\r\nContent-Length: 20\r\n Another : HEaDer \r\n\r\n1234567890abcdefhijk";
|
||||
} else if (std.mem.eql(u8, path, "/http_client/redirect/insecure")) {
|
||||
response = "HTTP/1.1 307 GOTO\r\nLocation: http://127.0.0.1:9582/http_client/redirect\r\n\r\n";
|
||||
} else if (std.mem.eql(u8, path, "/xhr")) {
|
||||
response = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nContent-Length: 100\r\n\r\n" ++ ("1234567890" ** 10);
|
||||
} else if (std.mem.eql(u8, path, "/xhr/json")) {
|
||||
response = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 18\r\n\r\n{\"over\":\"9000!!!\"}";
|
||||
} else {
|
||||
// should not have an unknown path
|
||||
unreachable;
|
||||
}
|
||||
|
||||
var unsent = response;
|
||||
while (unsent.len > 0) {
|
||||
const to_send = rand.intRangeAtMost(usize, 1, unsent.len);
|
||||
const sent = try conn.write(unsent[0..to_send]);
|
||||
unsent = unsent[sent..];
|
||||
std.time.sleep(std.time.ns_per_us * 5);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serveCDP(address: std.net.Address) !void {
|
||||
var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init;
|
||||
var app = try App.init(gpa.allocator(), .{
|
||||
.run_mode = .serve,
|
||||
.tls_verify_host = false,
|
||||
});
|
||||
defer app.deinit();
|
||||
|
||||
test_wg.finish();
|
||||
server.run(app, address, std.time.ns_per_s * 2) catch |err| {
|
||||
std.debug.print("CDP server error: {}", .{err});
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user