@@ -27,8 +27,6 @@ const server = @import("server.zig");
2727const parser = @import ("netsurf" );
2828const apiweb = @import ("apiweb.zig" );
2929
30- const log = std .log .scoped (.server );
31-
3230pub const Types = jsruntime .reflect (apiweb .Interfaces );
3331pub const UserContext = apiweb .UserContext ;
3432
@@ -38,13 +36,18 @@ const Port = 3245;
3836const Timeout = 3 ; // in seconds
3937
4038const usage =
41- \\usage: {s} [options]
42- \\ start Lightpanda browser in CDP server mode
39+ \\usage: {s} [options] [URL]
40+ \\
41+ \\ start Lightpanda browser
42+ \\
43+ \\ * if an url is provided the browser will fetch the page and exit
44+ \\ * otherwhise the browser starts a CDP server
4345 \\
4446 \\ -h, --help Print this help message and exit.
45- \\ --host Host of the server (default "127.0.0.1")
46- \\ --port Port of the server (default "3245")
47- \\ --timeout Timeout for incoming connections in seconds (default "3")
47+ \\ --host Host of the CDP server (default "127.0.0.1")
48+ \\ --port Port of the CDP server (default "3245")
49+ \\ --timeout Timeout for incoming connections of the CDP server (in seconds, default "3")
50+ \\ --dump Dump document in stdout (fetch mode only)
4851 \\
4952;
5053
@@ -146,7 +149,7 @@ pub const StreamServer = struct {
146149
147150fn printUsageExit (execname : []const u8 , res : u8 ) void {
148151 std .io .getStdErr ().writer ().print (usage , .{execname }) catch | err | {
149- log .err ("Print usage error: {any}" , .{err });
152+ std . log .err ("Print usage error: {any}" , .{err });
150153 std .posix .exit (1 );
151154 };
152155 std .posix .exit (res );
@@ -157,12 +160,20 @@ pub fn main() !void {
157160 // allocator
158161 var arena = std .heap .ArenaAllocator .init (std .heap .page_allocator );
159162 defer arena .deinit ();
163+ const alloc = arena .allocator ();
160164
161165 // args
162166 var args = try std .process .argsWithAllocator (arena .allocator ());
163167 defer args .deinit ();
164168
165169 const execname = args .next ().? ;
170+ var server_mode = true ;
171+
172+ // fetch mode variables
173+ var url : []const u8 = "" ;
174+ var dump : bool = false ;
175+
176+ // server mode variables
166177 var host : []const u8 = Host ;
167178 var port : u16 = Port ;
168179 var addr : std.net.Address = undefined ;
@@ -172,64 +183,126 @@ pub fn main() !void {
172183 if (std .mem .eql (u8 , "-h" , opt ) or std .mem .eql (u8 , "--help" , opt )) {
173184 printUsageExit (execname , 0 );
174185 }
186+ if (std .mem .eql (u8 , "--dump" , opt )) {
187+ dump = true ;
188+ continue ;
189+ }
175190 if (std .mem .eql (u8 , "--host" , opt )) {
176191 if (args .next ()) | arg | {
177192 host = arg ;
178193 continue ;
179194 } else {
180- log .err ("--host not provided\n " , .{});
195+ std . log .err ("--host not provided\n " , .{});
181196 return printUsageExit (execname , 1 );
182197 }
183198 }
184199 if (std .mem .eql (u8 , "--port" , opt )) {
185200 if (args .next ()) | arg | {
186201 port = std .fmt .parseInt (u16 , arg , 10 ) catch | err | {
187- log .err ("--port {any}\n " , .{err });
202+ std . log .err ("--port {any}\n " , .{err });
188203 return printUsageExit (execname , 1 );
189204 };
190205 continue ;
191206 } else {
192- log .err ("--port not provided\n " , .{});
207+ std . log .err ("--port not provided\n " , .{});
193208 return printUsageExit (execname , 1 );
194209 }
195210 }
196211 if (std .mem .eql (u8 , "--timeout" , opt )) {
197212 if (args .next ()) | arg | {
198213 timeout = std .fmt .parseInt (u8 , arg , 10 ) catch | err | {
199- log .err ("--timeout {any}\n " , .{err });
214+ std . log .err ("--timeout {any}\n " , .{err });
200215 return printUsageExit (execname , 1 );
201216 };
202217 continue ;
203218 } else {
204- log .err ("--timeout not provided\n " , .{});
219+ std . log .err ("--timeout not provided\n " , .{});
205220 return printUsageExit (execname , 1 );
206221 }
207222 }
223+
224+ // unknown option
225+ if (std .mem .startsWith (u8 , opt , "--" )) {
226+ std .log .err ("unknown option\n " , .{});
227+ return printUsageExit (execname , 1 );
228+ }
229+
230+ // other argument is considered to be an URL, ie. fetch mode
231+ server_mode = false ;
232+
233+ // allow only one url
234+ if (url .len != 0 ) {
235+ std .log .err ("more than 1 url provided\n " , .{});
236+ return printUsageExit (execname , 1 );
237+ }
238+
239+ url = opt ;
208240 }
209- addr = std .net .Address .parseIp4 (host , port ) catch | err | {
210- log .err ("address (host:port) {any}\n " , .{err });
211- return printUsageExit (execname , 1 );
212- };
213241
214- // server
215- var srv = StreamServer .init (.{
216- .reuse_address = true ,
217- .reuse_port = true ,
218- .nonblocking = true ,
219- });
220- defer srv .deinit ();
221-
222- srv .listen (addr ) catch | err | {
223- log .err ("address (host:port) {any}\n " , .{err });
224- return printUsageExit (execname , 1 );
225- };
226- defer srv .close ();
227- log .info ("Listening on: {s}:{d}..." , .{ host , port });
242+ if (server_mode ) {
243+
244+ // server mode
245+ addr = std .net .Address .parseIp4 (host , port ) catch | err | {
246+ std .log .err ("address (host:port) {any}\n " , .{err });
247+ return printUsageExit (execname , 1 );
248+ };
228249
229- // loop
230- var loop = try jsruntime .Loop .init (arena .allocator ());
231- defer loop .deinit ();
250+ // server
251+ var srv = StreamServer .init (.{
252+ .reuse_address = true ,
253+ .reuse_port = true ,
254+ .nonblocking = true ,
255+ });
256+ defer srv .deinit ();
257+
258+ srv .listen (addr ) catch | err | {
259+ std .log .err ("address (host:port) {any}\n " , .{err });
260+ return printUsageExit (execname , 1 );
261+ };
262+ defer srv .close ();
263+ std .log .info ("Listening on: {s}:{d}..." , .{ host , port });
264+
265+ // loop
266+ var loop = try jsruntime .Loop .init (arena .allocator ());
267+ defer loop .deinit ();
268+
269+ // listen
270+ try server .listen (alloc , & loop , srv .sockfd .? , std .time .ns_per_s * @as (u64 , timeout ));
271+ } else {
272+
273+ // fetch mode
274+ if (url .len == 0 ) {
275+ try std .io .getStdErr ().writer ().print (usage , .{execname });
276+ std .posix .exit (1 );
277+ }
278+
279+ const vm = jsruntime .VM .init ();
280+ defer vm .deinit ();
281+
282+ var loop = try jsruntime .Loop .init (arena .allocator ());
283+ defer loop .deinit ();
284+
285+ var browser = Browser {};
286+ try Browser .init (& browser , alloc , & loop , vm );
287+ defer browser .deinit ();
232288
233- // listen
234- try server .listen (arena .allocator (), & loop , srv .sockfd .? , std .time .ns_per_s * @as (u64 , timeout ));
289+ const page = try browser .session .createPage ();
290+
291+ _ = page .navigate (url , null ) catch | err | switch (err ) {
292+ error .UnsupportedUriScheme , error .UriMissingHost = > {
293+ std .log .err ("'{s}' is not a valid URL ({any})\n " , .{ url , err });
294+ return printUsageExit (execname , 1 );
295+ },
296+ else = > {
297+ std .log .err ("'{s}' fetching error ({any})s\n " , .{ url , err });
298+ return printUsageExit (execname , 1 );
299+ },
300+ };
301+
302+ try page .wait ();
303+
304+ if (dump ) {
305+ try page .dump (std .io .getStdOut ());
306+ }
307+ }
235308}
0 commit comments