Files
browser/src/main.zig
2026-02-04 17:57:18 +00:00

134 lines
4.6 KiB
Zig

// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
//
// Francis Bouvier <francis@lightpanda.io>
// Pierre Tachoire <pierre@lightpanda.io>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
const lp = @import("lightpanda");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const log = lp.log;
const App = lp.App;
const Config = lp.Config;
const SigHandler = @import("Sighandler.zig");
pub const panic = lp.crash_handler.panic;
pub fn main() !void {
// allocator
// - in Debug mode we use the General Purpose Allocator to detect memory leaks
// - in Release mode we use the c allocator
var gpa_instance: std.heap.DebugAllocator(.{}) = .init;
const gpa = if (builtin.mode == .Debug) gpa_instance.allocator() else std.heap.c_allocator;
defer if (builtin.mode == .Debug) {
if (gpa_instance.detectLeaks()) std.posix.exit(1);
};
// arena for main-specific allocations
var main_arena_instance = std.heap.ArenaAllocator.init(gpa);
const main_arena = main_arena_instance.allocator();
defer main_arena_instance.deinit();
var sighandler = SigHandler{ .arena = main_arena };
try sighandler.install();
run(gpa, main_arena, &sighandler) catch |err| {
log.fatal(.app, "exit", .{ .err = err });
std.posix.exit(1);
};
}
fn run(allocator: Allocator, main_arena: Allocator, sighandler: *SigHandler) !void {
const args = try Config.parseArgs(main_arena);
defer args.deinit(main_arena);
switch (args.mode) {
.help => {
args.printUsageAndExit(args.mode.help);
return std.process.cleanExit();
},
.version => {
std.debug.print("{s}\n", .{lp.build_config.git_commit});
return std.process.cleanExit();
},
else => {},
}
if (args.logLevel()) |ll| {
log.opts.level = ll;
}
if (args.logFormat()) |lf| {
log.opts.format = lf;
}
if (args.logFilterScopes()) |lfs| {
log.opts.filter_scopes = lfs;
}
// _app is global to handle graceful shutdown.
var app = try App.init(allocator, &args);
defer app.deinit();
app.telemetry.record(.{ .run = {} });
switch (args.mode) {
.serve => |opts| {
log.debug(.app, "startup", .{ .mode = "serve", .snapshot = app.snapshot.fromEmbedded() });
const address = std.net.Address.parseIp(opts.host, opts.port) catch |err| {
log.fatal(.app, "invalid server address", .{ .err = err, .host = opts.host, .port = opts.port });
return args.printUsageAndExit(false);
};
// _server is global to handle graceful shutdown.
var server = try lp.Server.init(allocator, app, address);
defer server.deinit();
try sighandler.on(lp.Server.stop, .{&server});
// max timeout of 1 week.
const timeout = if (opts.timeout > 604_800) 604_800_000 else @as(u32, opts.timeout) * 1000;
server.run(address, timeout) catch |err| {
log.fatal(.app, "server run error", .{ .err = err });
return err;
};
},
.fetch => |opts| {
const url = opts.url;
log.debug(.app, "startup", .{ .mode = "fetch", .dump = opts.dump, .url = url, .snapshot = app.snapshot.fromEmbedded() });
var fetch_opts = lp.FetchOpts{
.wait_ms = 5000,
.dump = .{
.strip = opts.strip,
.with_base = opts.withbase,
},
};
var stdout = std.fs.File.stdout();
var writer = stdout.writer(&.{});
if (opts.dump) {
fetch_opts.writer = &writer.interface;
}
lp.fetch(allocator, app, url, fetch_opts) catch |err| {
log.fatal(.app, "fetch error", .{ .err = err, .url = url });
return err;
};
},
else => unreachable,
}
}