Big refacto

Signed-off-by: Francis Bouvier <francis@lightpanda.io>
This commit is contained in:
Francis Bouvier
2024-04-16 00:38:06 +02:00
parent 5e1fe656e8
commit 980571073d
4 changed files with 160 additions and 192 deletions

View File

@@ -1,8 +1,7 @@
const std = @import("std");
const server = @import("../server.zig");
const Ctx = server.CmdContext;
const SendFn = server.SendFn;
const Ctx = server.Cmd;
const result = @import("cdp.zig").result;
const getParams = @import("cdp.zig").getParams;
@@ -19,7 +18,7 @@ pub fn browser(
ctx: *Ctx,
) ![]const u8 {
const method = std.meta.stringToEnum(BrowserMethods, action) orelse
return error.UnknownBrowserMethod;
return error.UnknownMethod;
return switch (method) {
.getVersion => browserGetVersion(alloc, id, scanner, ctx),
.setDownloadBehavior => browserSetDownloadBehavior(alloc, id, scanner, ctx),

View File

@@ -1,11 +1,26 @@
const std = @import("std");
const server = @import("../server.zig");
const Ctx = server.CmdContext;
const SendFn = server.SendFn;
const Ctx = server.Cmd;
const browser = @import("browser.zig").browser;
const target = @import("target.zig").target;
pub const Error = error{
UnknonwDomain,
UnknownMethod,
};
pub fn isCdpError(err: anyerror) ?Error {
// see https://github.com/ziglang/zig/issues/2473
const errors = @typeInfo(Error).ErrorSet.?;
inline for (errors) |e| {
if (std.mem.eql(u8, e.name, @errorName(err))) {
return @errorCast(err);
}
}
return null;
}
const Domains = enum {
Browser,
Target,

View File

@@ -1,15 +1,13 @@
const std = @import("std");
const server = @import("../server.zig");
const Ctx = server.CmdContext;
const SendFn = server.SendFn;
const Ctx = server.Cmd;
const result = @import("cdp.zig").result;
const getParams = @import("cdp.zig").getParams;
const stringify = @import("cdp.zig").stringify;
const TargetMethods = enum {
setAutoAttach,
// attachedToTarget,
// getTargetInfo,
};
@@ -21,10 +19,10 @@ pub fn target(
ctx: *Ctx,
) ![]const u8 {
const method = std.meta.stringToEnum(TargetMethods, action) orelse
return error.UnknownTargetMethod;
return error.UnknownMethod;
return switch (method) {
.setAutoAttach => tagetSetAutoAttach(alloc, id, scanner, ctx),
// .getTargetInfo => tagetGetTargetInfo(alloc, id, scanner),
// .getTargetInfo => tagetGetTargetInfo(alloc, id, scanner, ctx),
};
}
@@ -63,7 +61,7 @@ fn tagetSetAutoAttach(
} = .{},
};
const attached = try stringify(alloc, AttachToTarget{});
try server.sendLater(ctx, attached, 0);
try server.sendSync(ctx, attached);
return result(alloc, id, null, null);
}
@@ -93,28 +91,3 @@ fn tagetGetTargetInfo(
_ = targetInfo;
return result(alloc, id, null, null);
}
// fn tagetGetTargetInfo(
// alloc: std.mem.Allocator,
// id: u64,
// scanner: *std.json.Scanner,
// ) ![]const u8 {
// _ = scanner;
// const TargetInfo = struct {
// targetId: []const u8,
// type: []const u8,
// title: []const u8,
// url: []const u8,
// attached: bool,
// canAccessOpener: bool,
// browserContextId: ?[]const u8 = null,
// };
// const targetInfo = TargetInfo{
// .targetId = TargetID,
// .type = "page",
// };
// _ = targetInfo;
// return result(alloc, id, null, null);
// }

View File

@@ -1,178 +1,165 @@
const std = @import("std");
const public = @import("jsruntime");
const Completion = public.IO.Completion;
const AcceptError = public.IO.AcceptError;
const RecvError = public.IO.RecvError;
const SendError = public.IO.SendError;
const TimeoutError = public.IO.TimeoutError;
const Window = @import("html/window.zig").Window;
const cdp = @import("cdp/cdp.zig");
pub var socket_fd: std.os.socket_t = undefined;
// I/O input command context
pub const CmdContext = struct {
js_env: *public.Env,
const NoError = error{NoError};
const Error = AcceptError || RecvError || SendError || TimeoutError || cdp.Error || NoError;
// I/O Recv
// --------
pub const Cmd = struct {
// internal fields
socket: std.os.socket_t,
completion: *public.IO.Completion,
read_buf: []u8,
write_buf: []const u8 = undefined,
close: bool = false,
buf: []u8, // only for read operations
err: ?Error = null,
// JS fields
js_env: *public.Env,
try_catch: public.TryCatch,
fn cbk(self: *Cmd, completion: *Completion, result: RecvError!usize) void {
const size = result catch |err| {
self.err = err;
return;
};
const input = self.buf[0..size];
// close on exit command
if (std.mem.eql(u8, input, "exit")) {
self.err = error.NoError;
return;
}
// input
if (std.log.defaultLogEnabled(.debug)) {
std.debug.print("\ninput {s}\n", .{input});
}
// cdp
const res = cdp.do(self.alloc(), input, self) catch |err| {
if (cdp.isCdpError(err)) |e| {
self.err = e;
return;
}
@panic(@errorName(err));
};
std.log.debug("res {s}", .{res});
sendAsync(self, res) catch unreachable;
// continue receving incomming messages asynchronously
self.loop().io.recv(*Cmd, self, cbk, completion, self.socket, self.buf);
}
// shortcuts
fn alloc(self: *CmdContext) std.mem.Allocator {
fn alloc(self: *Cmd) std.mem.Allocator {
return self.js_env.nat_ctx.alloc;
}
fn loop(self: *CmdContext) *public.Loop {
fn loop(self: *Cmd) *public.Loop {
return self.js_env.nat_ctx.loop;
}
};
fn respCallback(
ctx: *CmdContext,
_: *public.IO.Completion,
result: public.IO.SendError!usize,
) void {
_ = result catch |err| {
ctx.close = true;
std.debug.print("send error: {s}\n", .{@errorName(err)});
return;
};
std.log.debug("send ok", .{});
}
// I/O Send
// --------
const SendLaterContext = struct {
cmd_ctx: *CmdContext,
completion: *public.IO.Completion,
const Send = struct {
cmd: *Cmd,
buf: []const u8,
};
fn sendLaterCallback(
ctx: *SendLaterContext,
completion: *public.IO.Completion,
result: public.IO.TimeoutError!void,
) void {
std.log.debug("sending after", .{});
_ = result catch |err| {
ctx.cmd_ctx.close = true;
std.debug.print("timeout error: {s}\n", .{@errorName(err)});
return;
};
ctx.cmd_ctx.alloc().destroy(completion);
defer ctx.cmd_ctx.alloc().destroy(ctx);
send(ctx.cmd_ctx, ctx.buf) catch unreachable;
}
pub fn sendLater(ctx: *CmdContext, msg: []const u8, nanoseconds: u63) !void {
// NOTE: it seems we can't use the same completion for concurrent
// recv and timeout operations, that's why we create a new completion here
const completion = try ctx.alloc().create(public.IO.Completion);
// NOTE: to handle concurrent calls to sendLater we create each time a new context
// If no concurrent calls are required we could just use the main CmdContext
const sendLaterCtx = try ctx.alloc().create(SendLaterContext);
sendLaterCtx.* = .{
.cmd_ctx = ctx,
.completion = completion,
.buf = msg,
};
ctx.loop().io.timeout(*SendLaterContext, sendLaterCtx, sendLaterCallback, completion, nanoseconds);
}
fn send(ctx: *CmdContext, msg: []const u8) !void {
defer ctx.alloc().free(msg);
const s = try std.os.write(ctx.socket, msg);
std.log.debug("send ok {d}", .{s});
}
fn loopSend(ctx: *CmdContext, msg: []const u8) !void {
ctx.write_buf = msg;
ctx.loop().io.send(
*CmdContext,
ctx,
respCallback,
ctx.completion,
ctx.socket,
ctx.write_buf,
);
}
// I/O input command callback
fn cmdCallback(
ctx: *CmdContext,
completion: *public.IO.Completion,
result: public.IO.RecvError!usize,
) void {
// ctx.completion = completion;
const size = result catch |err| {
ctx.close = true;
std.debug.print("recv error: {s}\n", .{@errorName(err)});
return;
};
const input = ctx.read_buf[0..size];
// close on exit command
if (std.mem.eql(u8, input, "exit")) {
ctx.close = true;
return;
fn init(ctx: *Cmd, msg: []const u8) !struct {
ctx: *Send,
completion: *Completion,
} {
// NOTE: it seems we can't use the same completion for concurrent
// recv and timeout operations, that's why we create a new completion here
const completion = try ctx.alloc().create(Completion);
// NOTE: to handle concurrent calls we create each time a new context
// If no concurrent calls where required we could just use the main CmdCtx
const sd = try ctx.alloc().create(Send);
sd.* = .{
.cmd = ctx,
.buf = msg,
};
return .{ .ctx = sd, .completion = completion };
}
std.debug.print("\ninput {s}\n", .{input});
const res = cdp.do(ctx.alloc(), input, ctx) catch |err| {
std.log.debug("error: {any}\n", .{err});
send(ctx, "{}") catch unreachable;
// TODO: return proper error
return;
};
std.log.debug("res {s}", .{res});
fn deinit(self: *Send, completion: *Completion) void {
self.cmd.alloc().destroy(completion);
self.cmd.alloc().free(self.buf);
self.cmd.alloc().destroy(self);
}
send(ctx, res) catch unreachable;
std.log.debug("finish", .{});
fn laterCbk(self: *Send, completion: *Completion, result: TimeoutError!void) void {
std.log.debug("sending after", .{});
_ = result catch |err| {
self.cmd.err = err;
return;
};
// continue receving messages asynchronously
ctx.loop().io.recv(
*CmdContext,
ctx,
cmdCallback,
completion,
ctx.socket,
ctx.read_buf,
);
}
self.cmd.loop().io.send(*Send, self, Send.asyncCbk, completion, self.cmd.socket, self.buf);
}
// I/O connection context
const ConnContext = struct {
socket: std.os.socket_t,
fn asyncCbk(self: *Send, completion: *Completion, result: SendError!usize) void {
const size = result catch |err| {
self.cmd.err = err;
return;
};
cmdContext: *CmdContext,
std.log.debug("send async {d} bytes", .{size});
self.deinit(completion);
}
};
// I/O connection callback
fn connCallback(
ctx: *ConnContext,
completion: *public.IO.Completion,
result: public.IO.AcceptError!std.os.socket_t,
) void {
ctx.cmdContext.socket = result catch |err| @panic(@errorName(err));
// launch receving messages asynchronously
ctx.cmdContext.loop().io.recv(
*CmdContext,
ctx.cmdContext,
cmdCallback,
completion,
ctx.cmdContext.socket,
ctx.cmdContext.read_buf,
);
pub fn sendLater(ctx: *Cmd, msg: []const u8, ns: u63) !void {
const sd = try Send.init(ctx, msg);
ctx.loop().io.timeout(*Send, sd.ctx, Send.laterCbk, sd.completion, ns);
}
pub fn execJS(
alloc: std.mem.Allocator,
js_env: *public.Env,
) anyerror!void {
pub fn sendAsync(ctx: *Cmd, msg: []const u8) !void {
const sd = try Send.init(ctx, msg);
ctx.loop().io.send(*Send, sd.ctx, Send.asyncCbk, sd.completion, ctx.socket, msg);
}
pub fn sendSync(ctx: *Cmd, msg: []const u8) !void {
defer ctx.alloc().free(msg);
const s = try std.os.write(ctx.socket, msg);
std.log.debug("send sync {d} bytes", .{s});
}
// I/O Accept
// ----------
const Accept = struct {
cmd: *Cmd,
socket: std.os.socket_t,
fn cbk(self: *Accept, completion: *Completion, result: AcceptError!std.os.socket_t) void {
self.cmd.socket = result catch |err| {
self.cmd.err = err;
return;
};
// receving incomming messages asynchronously
self.cmd.loop().io.recv(*Cmd, self.cmd, Cmd.cbk, completion, self.cmd.socket, self.cmd.buf);
}
};
pub fn execJS(alloc: std.mem.Allocator, js_env: *public.Env) anyerror!void {
// start JS env
try js_env.start(alloc);
@@ -196,29 +183,22 @@ pub fn execJS(
// create I/O contexts and callbacks
// for accepting connections and receving messages
var completion: public.IO.Completion = undefined;
var input: [1024]u8 = undefined;
var cmd_ctx = CmdContext{
var cmd = Cmd{
.js_env = js_env,
.socket = undefined,
.read_buf = &input,
.buf = &input,
.try_catch = try_catch,
.completion = &completion,
};
var conn_ctx = ConnContext{
var accept = Accept{
.cmd = &cmd,
.socket = socket_fd,
.cmdContext = &cmd_ctx,
};
// launch accepting connection asynchronously on internal server
// accepting connection asynchronously on internal server
const loop = js_env.nat_ctx.loop;
loop.io.accept(
*ConnContext,
&conn_ctx,
connCallback,
&completion,
socket_fd,
);
var completion: Completion = undefined;
loop.io.accept(*Accept, &accept, Accept.cbk, &completion, socket_fd);
// infinite loop on I/O events, either:
// - cmd from incoming connection on server socket
@@ -232,7 +212,8 @@ pub fn execJS(
}
loop.cbk_error = false;
}
if (cmd_ctx.close) {
if (cmd.err) |err| {
if (err != error.NoError) std.log.err("Server error: {any}", .{err});
break;
}
}