Skip to content

Commit 94be7a0

Browse files
committed
Make TCP server websocket-aware
Adding HTTP & websocket awareness to the TCP server. HTTP server handles `GET /json/version` and websocket upgrade requests. Conceptually, websocket handling is the same code as before, but receiving data will parse the websocket frames and writing data will wrap it in a websocket frame. The previous `Ctx` was split into a `Server` and a `Client`. This was largely done to make it easy to write unit tests, since the `Client` is a generic, all its dependencies (i.e. the server) can be mocked out. This also makes it a bit nicer to know if there is or isn't a client (via the server's client optional). Added a MemoryPool for the Send object (I thought that was a nice touch!) Removed MacOS hack on accept/conn completion usage. Known issues: - When framing an outgoing message, the entire message has to be duped. This is no worse than how it was before, but it should be possible to eliminate this in the future. Probably not part of this PR. - Websocket parsing will reject continuation frames. I don't know of a single client that will send a fragmented message (websocket has its own message fragmentation), but we should probably still support this just in case. - I don't think the receive, timeout and close completions can safely be re-used like we're doing. I believe they need to be associated with a specific client socket. - A new connection creates a new browser session. I think this is right (??), but for the very first, we're throwing out a perfectly usable session. I'm thinking this might be a change to how Browser/Sessions work. - zig build test won't compile. This branch reproduces the issue with none of these changes: https://github.com/karlseguin/browser/tree/broken_test_build (or, as a diff to main): main...karlseguin:broken_test_build
1 parent 0814daf commit 94be7a0

File tree

10 files changed

+1355
-678
lines changed

10 files changed

+1355
-678
lines changed

.gitmodules

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,3 @@
2828
[submodule "vendor/zig-async-io"]
2929
path = vendor/zig-async-io
3030
url = https://github.com/lightpanda-io/zig-async-io.git/
31-
[submodule "vendor/websocket.zig"]
32-
path = vendor/websocket.zig
33-
url = https://github.com/lightpanda-io/websocket.zig.git/
34-
branch = lightpanda

build.zig

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,6 @@ fn common(
189189
.root_source_file = b.path("vendor/tls.zig/src/main.zig"),
190190
});
191191
step.root_module.addImport("tls", tlsmod);
192-
193-
const wsmod = b.addModule("websocket", .{
194-
.root_source_file = b.path("vendor/websocket.zig/src/websocket.zig"),
195-
});
196-
step.root_module.addImport("websocket", wsmod);
197192
}
198193

199194
fn moduleNetSurf(b: *std.Build, target: std.Build.ResolvedTarget) !*std.Build.Module {

src/cdp/runtime.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,12 @@ fn sendInspector(
131131
const buf = try alloc.alloc(u8, msg.json.len + 1);
132132
defer alloc.free(buf);
133133
_ = std.mem.replace(u8, msg.json, "\"awaitPromise\":true", "\"awaitPromise\":false", buf);
134-
ctx.sendInspector(buf);
134+
try ctx.sendInspector(buf);
135135
return "";
136136
}
137137
}
138138

139-
ctx.sendInspector(msg.json);
139+
try ctx.sendInspector(msg.json);
140140

141141
if (msg.id == null) return "";
142142

src/handler.zig

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/main.zig

Lines changed: 6 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ const std = @import("std");
2020
const builtin = @import("builtin");
2121

2222
const jsruntime = @import("jsruntime");
23-
const websocket = @import("websocket");
2423

2524
const Browser = @import("browser/browser.zig").Browser;
2625
const server = @import("server.zig");
27-
const handler = @import("handler.zig");
28-
const MaxSize = @import("msg.zig").MaxSize;
2926

3027
const parser = @import("netsurf");
3128
const apiweb = @import("apiweb.zig");
@@ -86,11 +83,9 @@ const CliMode = union(CliModeTag) {
8683
const Server = struct {
8784
execname: []const u8 = undefined,
8885
args: *std.process.ArgIterator = undefined,
89-
addr: std.net.Address = undefined,
9086
host: []const u8 = Host,
9187
port: u16 = Port,
9288
timeout: u8 = Timeout,
93-
tcp: bool = false, // undocumented TCP mode
9489

9590
// default options
9691
const Host = "127.0.0.1";
@@ -160,10 +155,6 @@ const CliMode = union(CliModeTag) {
160155
return printUsageExit(execname, 1);
161156
}
162157
}
163-
if (std.mem.eql(u8, "--tcp", opt)) {
164-
_server.tcp = true;
165-
continue;
166-
}
167158

168159
// unknown option
169160
if (std.mem.startsWith(u8, opt, "--")) {
@@ -186,10 +177,6 @@ const CliMode = union(CliModeTag) {
186177
if (default_mode == .server) {
187178

188179
// server mode
189-
_server.addr = std.net.Address.parseIp4(_server.host, _server.port) catch |err| {
190-
log.err("address (host:port) {any}\n", .{err});
191-
return printUsageExit(execname, 1);
192-
};
193180
_server.execname = execname;
194181
_server.args = args;
195182
return CliMode{ .server = _server };
@@ -247,65 +234,19 @@ pub fn main() !void {
247234

248235
switch (cli_mode) {
249236
.server => |opts| {
250-
251-
// Stream server
252-
const addr = blk: {
253-
if (opts.tcp) {
254-
break :blk opts.addr;
255-
} else {
256-
const unix_path = "/tmp/lightpanda";
257-
std.fs.deleteFileAbsolute(unix_path) catch {}; // file could not exists
258-
break :blk try std.net.Address.initUnix(unix_path);
259-
}
260-
};
261-
const socket = server.listen(addr) catch |err| {
262-
log.err("Server listen error: {any}\n", .{err});
237+
const address = std.net.Address.parseIp4(opts.host, opts.port) catch |err| {
238+
log.err("address (host:port) {any}\n", .{err});
263239
return printUsageExit(opts.execname, 1);
264240
};
265-
defer std.posix.close(socket);
266-
log.debug("Server opts: listening internally on {any}...", .{addr});
267-
268-
const timeout = std.time.ns_per_s * @as(u64, opts.timeout);
269241

270-
// loop
271242
var loop = try jsruntime.Loop.init(alloc);
272243
defer loop.deinit();
273244

274-
// TCP server mode
275-
if (opts.tcp) {
276-
return server.handle(alloc, &loop, socket, null, timeout);
277-
}
278-
279-
// start stream server in separate thread
280-
var stream = handler.Stream{
281-
.ws_host = opts.host,
282-
.ws_port = opts.port,
283-
.addr = addr,
245+
const timeout = std.time.ns_per_s * @as(u64, opts.timeout);
246+
server.run(alloc, address, timeout, &loop) catch |err| {
247+
log.err("Server error", .{});
248+
return err;
284249
};
285-
const cdp_thread = try std.Thread.spawn(
286-
.{ .allocator = alloc },
287-
server.handle,
288-
.{ alloc, &loop, socket, &stream, timeout },
289-
);
290-
291-
// Websocket server
292-
var ws = try websocket.Server(handler.Handler).init(alloc, .{
293-
.port = opts.port,
294-
.address = opts.host,
295-
.max_message_size = MaxSize + 14, // overhead websocket
296-
.max_conn = 1,
297-
.handshake = .{
298-
.timeout = 3,
299-
.max_size = 1024,
300-
// since we aren't using hanshake.headers
301-
// we can set this to 0 to save a few bytes.
302-
.max_headers = 0,
303-
},
304-
});
305-
defer ws.deinit();
306-
307-
try ws.listen(&stream);
308-
cdp_thread.join();
309250
},
310251

311252
.fetch => |opts| {

src/main_tests.zig

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,6 @@ const kb = 1024;
314314
const ms = std.time.ns_per_ms;
315315

316316
test {
317-
const msgTest = @import("msg.zig");
318-
std.testing.refAllDecls(msgTest);
319-
320317
const dumpTest = @import("browser/dump.zig");
321318
std.testing.refAllDecls(dumpTest);
322319

@@ -340,6 +337,7 @@ test {
340337

341338
std.testing.refAllDecls(@import("generate.zig"));
342339
std.testing.refAllDecls(@import("cdp/msg.zig"));
340+
std.testing.refAllDecls(@import("server.zig"));
343341
}
344342

345343
fn testJSRuntime(alloc: std.mem.Allocator) !void {

0 commit comments

Comments
 (0)