mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
// }
|
||||
|
||||
293
src/server.zig
293
src/server.zig
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user