mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
tcp: break up xev handlers into smaller files
This commit is contained in:
@ -1,6 +1,8 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const log = std.log.scoped(.tcp_thread);
|
const log = std.log.scoped(.tcp_thread);
|
||||||
|
|
||||||
|
const ping = @import("commands/ping.zig").ping;
|
||||||
|
|
||||||
pub const Command = enum {
|
pub const Command = enum {
|
||||||
ping,
|
ping,
|
||||||
|
|
||||||
@ -19,7 +21,7 @@ pub const Command = enum {
|
|||||||
|
|
||||||
var iter = std.mem.splitScalar(u8, trimmed, ' ');
|
var iter = std.mem.splitScalar(u8, trimmed, ' ');
|
||||||
|
|
||||||
// Not using .first() because it returns the remainder of the slice
|
// Not using .first() because it returns everything if there is no space
|
||||||
// Instead we're doing what's equivalent to popping the first element
|
// Instead we're doing what's equivalent to popping the first element
|
||||||
const cmdName = iter.next() orelse return error.InvalidInput;
|
const cmdName = iter.next() orelse return error.InvalidInput;
|
||||||
log.debug("got command name={s}", .{cmdName});
|
log.debug("got command name={s}", .{cmdName});
|
||||||
@ -42,9 +44,4 @@ pub const Command = enum {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Want to move this to different file, not sure how I want it organized yet
|
|
||||||
fn ping() []const u8 {
|
|
||||||
return "PONG\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: These need proper testing.
|
// TODO: These need proper testing.
|
||||||
|
@ -3,6 +3,7 @@ const xev = @import("xev");
|
|||||||
const tcp = @import("../tcp.zig");
|
const tcp = @import("../tcp.zig");
|
||||||
|
|
||||||
const reject_client = @import("./handlers/reject.zig").reject_client;
|
const reject_client = @import("./handlers/reject.zig").reject_client;
|
||||||
|
const read_client = @import("./handlers/reader.zig").read_client;
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const CompletionPool = std.heap.MemoryPool(xev.Completion);
|
const CompletionPool = std.heap.MemoryPool(xev.Completion);
|
||||||
@ -19,6 +20,9 @@ const ACCEPTOR_RATE = 1;
|
|||||||
/// A wrapper around the TCP server socket
|
/// A wrapper around the TCP server socket
|
||||||
pub const Server = @This();
|
pub const Server = @This();
|
||||||
|
|
||||||
|
/// Used for response formatting
|
||||||
|
alloc: Allocator,
|
||||||
|
|
||||||
/// Memory pool that stores the completions required by xev
|
/// Memory pool that stores the completions required by xev
|
||||||
comp_pool: CompletionPool,
|
comp_pool: CompletionPool,
|
||||||
|
|
||||||
@ -61,6 +65,7 @@ pub fn init(alloc: Allocator, addr: std.net.Address) !Server {
|
|||||||
.acceptor = try xev.Timer.init(),
|
.acceptor = try xev.Timer.init(),
|
||||||
.clients = undefined,
|
.clients = undefined,
|
||||||
.clients_count = 0,
|
.clients_count = 0,
|
||||||
|
.alloc = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +97,7 @@ pub fn start(self: *Server) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function to destroy a buffer in our pool
|
/// Convenience function to destroy a buffer in our pool
|
||||||
fn destroyBuffer(self: *Server, buf: []const u8) void {
|
pub fn destroyBuffer(self: *Server, buf: []const u8) void {
|
||||||
self.buf_pool.destroy(@alignCast(
|
self.buf_pool.destroy(@alignCast(
|
||||||
@as(*[1024]u8, @ptrFromInt(@intFromPtr(buf.ptr))),
|
@as(*[1024]u8, @ptrFromInt(@intFromPtr(buf.ptr))),
|
||||||
));
|
));
|
||||||
@ -132,7 +137,7 @@ fn acceptor(
|
|||||||
/// Accepts a new client connection and starts reading from it until EOF.
|
/// Accepts a new client connection and starts reading from it until EOF.
|
||||||
fn acceptHandler(
|
fn acceptHandler(
|
||||||
self_: ?*Server,
|
self_: ?*Server,
|
||||||
l: *xev.Loop,
|
_: *xev.Loop,
|
||||||
c: *xev.Completion,
|
c: *xev.Completion,
|
||||||
e: xev.TCP.AcceptError!xev.TCP,
|
e: xev.TCP.AcceptError!xev.TCP,
|
||||||
) xev.CallbackAction {
|
) xev.CallbackAction {
|
||||||
@ -148,17 +153,6 @@ fn acceptHandler(
|
|||||||
return .disarm;
|
return .disarm;
|
||||||
};
|
};
|
||||||
|
|
||||||
const comp_w = self.comp_pool.create() catch {
|
|
||||||
log.err("couldn't allocate completion in pool", .{});
|
|
||||||
return .disarm;
|
|
||||||
};
|
|
||||||
|
|
||||||
const buf_w = self.buf_pool.create() catch {
|
|
||||||
log.err("couldn't allocate buffer in pool", .{});
|
|
||||||
self.sock_pool.destroy(sock);
|
|
||||||
return .disarm;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (self.clients_count == MAX_CLIENTS) {
|
if (self.clients_count == MAX_CLIENTS) {
|
||||||
log.warn("max clients reached, rejecting fd={d}", .{sock.fd});
|
log.warn("max clients reached, rejecting fd={d}", .{sock.fd});
|
||||||
reject_client(self, sock) catch return .rearm;
|
reject_client(self, sock) catch return .rearm;
|
||||||
@ -169,113 +163,10 @@ fn acceptHandler(
|
|||||||
self.clients[self.clients_count] = sock;
|
self.clients[self.clients_count] = sock;
|
||||||
self.clients_count += 1;
|
self.clients_count += 1;
|
||||||
|
|
||||||
const buf_r = self.buf_pool.create() catch {
|
read_client(self, sock, c) catch {
|
||||||
log.err("couldn't allocate buffer in pool", .{});
|
log.err("couldn't read from client", .{});
|
||||||
return .disarm;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@memcpy(buf_w.ptr, "GHOSTTY\n");
|
|
||||||
sock.write(l, comp_w, .{ .slice = buf_w }, Server, self, writeHandler);
|
|
||||||
sock.read(l, c, .{ .slice = buf_r }, Server, self, readHandler);
|
|
||||||
return .disarm;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readHandler(
|
|
||||||
self_: ?*Server,
|
|
||||||
loop: *xev.Loop,
|
|
||||||
comp: *xev.Completion,
|
|
||||||
sock: xev.TCP,
|
|
||||||
buf: xev.ReadBuffer,
|
|
||||||
e: xev.TCP.ReadError!usize,
|
|
||||||
) xev.CallbackAction {
|
|
||||||
const self = self_.?;
|
|
||||||
const l = e catch |err| {
|
|
||||||
switch (err) {
|
|
||||||
error.EOF => {
|
|
||||||
log.info("disconnecting client fd={d}", .{sock.fd});
|
|
||||||
destroyBuffer(self, buf.slice);
|
|
||||||
self.clients_count -= 1;
|
|
||||||
sock.close(loop, comp, Server, self, closeHandler);
|
|
||||||
return .disarm;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => {
|
|
||||||
destroyBuffer(self, buf.slice);
|
|
||||||
log.err("read error", .{});
|
|
||||||
return .disarm;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const comp_w = self.comp_pool.create() catch {
|
|
||||||
log.err("couldn't allocate completion in pool", .{});
|
|
||||||
return .rearm;
|
|
||||||
};
|
|
||||||
|
|
||||||
const buf_w = self.buf_pool.create() catch {
|
|
||||||
log.err("couldn't allocate buffer in pool", .{});
|
|
||||||
return .rearm;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: What should we do with the rest of the buffer?
|
|
||||||
var iter = std.mem.splitScalar(u8, buf.slice[0..l], '\n');
|
|
||||||
while (iter.next()) |item| {
|
|
||||||
if (item.len == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const c = tcp.Command.parse(item) catch |err| {
|
|
||||||
const res = try tcp.Command.handleError(err);
|
|
||||||
@memcpy(buf_w.ptr, res.ptr[0..res.len]);
|
|
||||||
sock.write(loop, comp_w, .{ .slice = buf_w }, Server, self, writeHandler);
|
|
||||||
return .rearm;
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = try tcp.Command.handle(c);
|
|
||||||
@memcpy(buf_w.ptr, res.ptr[0..res.len]);
|
|
||||||
}
|
|
||||||
|
|
||||||
sock.write(loop, comp_w, .{ .slice = buf_w }, Server, self, writeHandler);
|
|
||||||
return .rearm;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn writeHandler(
|
|
||||||
self_: ?*Server,
|
|
||||||
loop: *xev.Loop,
|
|
||||||
comp: *xev.Completion,
|
|
||||||
sock: xev.TCP,
|
|
||||||
buf: xev.WriteBuffer,
|
|
||||||
e: xev.TCP.WriteError!usize,
|
|
||||||
) xev.CallbackAction {
|
|
||||||
_ = sock;
|
|
||||||
_ = comp;
|
|
||||||
_ = loop;
|
|
||||||
const self = self_.?;
|
|
||||||
_ = e catch |err| {
|
|
||||||
destroyBuffer(self, buf.slice);
|
|
||||||
log.err("write error {any}", .{err});
|
|
||||||
return .disarm;
|
|
||||||
};
|
|
||||||
|
|
||||||
return .disarm;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rejectHandler(
|
|
||||||
self_: ?*Server,
|
|
||||||
l: *xev.Loop,
|
|
||||||
c: *xev.Completion,
|
|
||||||
sock: xev.TCP,
|
|
||||||
buf: xev.WriteBuffer,
|
|
||||||
e: xev.TCP.WriteError!usize,
|
|
||||||
) xev.CallbackAction {
|
|
||||||
const self = self_.?;
|
|
||||||
_ = e catch |err| {
|
|
||||||
destroyBuffer(self, buf.slice);
|
|
||||||
log.err("write error {any}", .{err});
|
|
||||||
return .disarm;
|
|
||||||
};
|
|
||||||
|
|
||||||
sock.close(l, c, Server, self, closeHandler);
|
|
||||||
return .disarm;
|
return .disarm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,14 +190,11 @@ fn shutdownHandler(
|
|||||||
|
|
||||||
pub fn closeHandler(
|
pub fn closeHandler(
|
||||||
self_: ?*Server,
|
self_: ?*Server,
|
||||||
loop: *xev.Loop,
|
_: *xev.Loop,
|
||||||
comp: *xev.Completion,
|
comp: *xev.Completion,
|
||||||
sock: xev.TCP,
|
_: xev.TCP,
|
||||||
e: xev.TCP.CloseError!void,
|
e: xev.TCP.CloseError!void,
|
||||||
) xev.CallbackAction {
|
) xev.CallbackAction {
|
||||||
_ = sock;
|
|
||||||
_ = loop;
|
|
||||||
|
|
||||||
e catch {
|
e catch {
|
||||||
log.err("couldn't close socket", .{});
|
log.err("couldn't close socket", .{});
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const build_config = @import("../../build_config.zig");
|
||||||
|
|
||||||
|
pub fn ping() []const u8 {
|
||||||
|
return std.fmt.comptimePrint("PONG v={}\n", .{build_config.version});
|
||||||
|
}
|
84
src/tcp/handlers/reader.zig
Normal file
84
src/tcp/handlers/reader.zig
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
const xev = @import("xev");
|
||||||
|
const std = @import("std");
|
||||||
|
const Server = @import("../Server.zig").Server;
|
||||||
|
const Command = @import("../Command.zig").Command;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.tcp_thread);
|
||||||
|
|
||||||
|
pub fn read_client(
|
||||||
|
self: *Server,
|
||||||
|
client: *xev.TCP,
|
||||||
|
c: *xev.Completion,
|
||||||
|
) !void {
|
||||||
|
const buf_r = self.buf_pool.create() catch return error.OutOfMemory;
|
||||||
|
client.read(&self.loop, c, .{ .slice = buf_r }, Server, self, rHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rHandler(
|
||||||
|
self_: ?*Server,
|
||||||
|
l: *xev.Loop,
|
||||||
|
c: *xev.Completion,
|
||||||
|
s: xev.TCP,
|
||||||
|
b: xev.ReadBuffer,
|
||||||
|
e: xev.TCP.ReadError!usize,
|
||||||
|
) xev.CallbackAction {
|
||||||
|
const self = self_.?;
|
||||||
|
const len = e catch |err| {
|
||||||
|
Server.destroyBuffer(self, b.slice);
|
||||||
|
switch (err) {
|
||||||
|
error.EOF => {
|
||||||
|
log.info("client disconnected fd={d}", .{s.fd});
|
||||||
|
self.clients_count -= 1;
|
||||||
|
s.close(l, c, Server, self, Server.closeHandler);
|
||||||
|
return .disarm;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
log.err("client read error fd={d} err={any}", .{ s.fd, err });
|
||||||
|
return .disarm;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the completion task and buffer for our command responses
|
||||||
|
const c_w = self.comp_pool.create() catch return .rearm;
|
||||||
|
const b_w = self.buf_pool.create() catch return .rearm;
|
||||||
|
|
||||||
|
// Split commands by newline
|
||||||
|
var iter = std.mem.splitScalar(u8, b.slice[0..len], '\n');
|
||||||
|
while (iter.next()) |line| {
|
||||||
|
// Skip empty lines
|
||||||
|
if (line.len == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmd = Command.parse(line) catch |err| {
|
||||||
|
const res = try Command.handleError(err);
|
||||||
|
@memcpy(b_w.ptr, res.ptr[0..res.len]);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = try Command.handle(cmd);
|
||||||
|
@memcpy(b_w.ptr, res.ptr[0..res.len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.write(l, c_w, .{ .slice = b_w }, Server, self, wHandler);
|
||||||
|
return .rearm;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wHandler(
|
||||||
|
self_: ?*Server,
|
||||||
|
_: *xev.Loop,
|
||||||
|
_: *xev.Completion,
|
||||||
|
s: xev.TCP,
|
||||||
|
b: xev.WriteBuffer,
|
||||||
|
e: xev.TCP.WriteError!usize,
|
||||||
|
) xev.CallbackAction {
|
||||||
|
const self = self_.?;
|
||||||
|
_ = e catch |err| {
|
||||||
|
log.err("client write error fd={d} err={any}", .{ s.fd, err });
|
||||||
|
Server.destroyBuffer(self, b.slice);
|
||||||
|
};
|
||||||
|
|
||||||
|
return .disarm;
|
||||||
|
}
|
@ -4,12 +4,6 @@ const Server = @import("../Server.zig").Server;
|
|||||||
|
|
||||||
const log = std.log.scoped(.tcp_thread);
|
const log = std.log.scoped(.tcp_thread);
|
||||||
|
|
||||||
fn destroyBuffer(self: *Server, buf: []const u8) void {
|
|
||||||
self.buf_pool.destroy(@alignCast(
|
|
||||||
@as(*[1024]u8, @ptrFromInt(@intFromPtr(buf.ptr))),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
const RejectError = error{
|
const RejectError = error{
|
||||||
AllocError,
|
AllocError,
|
||||||
WriteError,
|
WriteError,
|
||||||
@ -19,7 +13,7 @@ const RejectError = error{
|
|||||||
pub fn reject_client(self: *Server, c: *xev.TCP) !void {
|
pub fn reject_client(self: *Server, c: *xev.TCP) !void {
|
||||||
const buf_w = self.buf_pool.create() catch return RejectError.AllocError;
|
const buf_w = self.buf_pool.create() catch return RejectError.AllocError;
|
||||||
const comp_w = self.comp_pool.create() catch {
|
const comp_w = self.comp_pool.create() catch {
|
||||||
destroyBuffer(self, buf_w);
|
Server.destroyBuffer(self, buf_w);
|
||||||
return RejectError.AllocError;
|
return RejectError.AllocError;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,7 +31,7 @@ fn wHandler(
|
|||||||
) xev.CallbackAction {
|
) xev.CallbackAction {
|
||||||
const self = self_.?;
|
const self = self_.?;
|
||||||
_ = e catch |err| {
|
_ = e catch |err| {
|
||||||
destroyBuffer(self, b.slice);
|
Server.destroyBuffer(self, b.slice);
|
||||||
log.err("write error {any}", .{err});
|
log.err("write error {any}", .{err});
|
||||||
return .disarm;
|
return .disarm;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user