mirror of
				https://github.com/lightpanda-io/browser.git
				synced 2025-10-30 15:41:48 +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
	 Francis Bouvier
					Francis Bouvier