mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
tcp: read server preferences from the config
This commit is contained in:
@ -1353,6 +1353,17 @@ keybind: Keybinds = .{},
|
||||
/// If `true`, the bold text will use the bright color palette.
|
||||
@"bold-is-bright": bool = false,
|
||||
|
||||
/// If set, Ghostty will start a TCP server on the specified socket path. Valid
|
||||
/// values can include a valid Unix socket path or an IP address and port. For
|
||||
/// example, `0.0.0.0:9393` will start a TCP server on all interfaces on port
|
||||
/// 9393. A value such as `/tmp/ghostty.sock` will start a socket on that path.
|
||||
@"remote-tcp-socket": ?[:0]const u8 = null,
|
||||
|
||||
/// The maximum number of connections (default 10) that can be made to the
|
||||
/// remote TCP server. If this limit is reached, new connections will be
|
||||
/// rejected with a message and the connection will be closed.
|
||||
@"remote-max-connections": u8 = 10,
|
||||
|
||||
/// This will be used to set the `TERM` environment variable.
|
||||
/// HACK: We set this with an `xterm` prefix because vim uses that to enable key
|
||||
/// protocols (specifically this will enable `modifyOtherKeys`), among other
|
||||
|
@ -2,7 +2,6 @@
|
||||
//! responding to TCP requests and dispatching them to the app's Mailbox.
|
||||
pub const Thread = @import("tcp/Thread.zig");
|
||||
pub const Server = @import("tcp/Server.zig");
|
||||
pub const Command = @import("tcp/Command.zig").Command;
|
||||
|
||||
test {
|
||||
@import("std").testing.refAllDecls(@This());
|
||||
|
@ -24,7 +24,6 @@ pub const Command = enum {
|
||||
// Not using .first() because it returns everything if there is no space
|
||||
// Instead we're doing what's equivalent to popping the first element
|
||||
const cmdName = iter.next() orelse return error.InvalidInput;
|
||||
log.debug("got command name={s}", .{cmdName});
|
||||
// TODO: Handle/support arguments
|
||||
|
||||
return std.meta.stringToEnum(Command, cmdName) orelse return error.InvalidCommand;
|
||||
|
@ -1,6 +1,6 @@
|
||||
const std = @import("std");
|
||||
const xev = @import("xev");
|
||||
const tcp = @import("../tcp.zig");
|
||||
const Config = @import("../Config.zig").Config;
|
||||
|
||||
const reject_client = @import("./handlers/reject.zig").reject_client;
|
||||
const read_client = @import("./handlers/reader.zig").read_client;
|
||||
@ -11,9 +11,6 @@ const SocketPool = std.heap.MemoryPool(xev.TCP);
|
||||
const BufferPool = std.heap.MemoryPool([1024]u8);
|
||||
const log = std.log.scoped(.tcp_thread);
|
||||
|
||||
/// Maximum connections we allow at once
|
||||
const MAX_CLIENTS = 2;
|
||||
|
||||
/// Acceptor polling rate in milliseconds
|
||||
const ACCEPTOR_RATE = 1;
|
||||
|
||||
@ -32,9 +29,6 @@ sock_pool: SocketPool,
|
||||
/// Memory pool that stores buffers for reading and writing
|
||||
buf_pool: BufferPool,
|
||||
|
||||
/// Stop flag
|
||||
stop: bool,
|
||||
|
||||
/// Event loop
|
||||
loop: xev.Loop,
|
||||
|
||||
@ -45,32 +39,75 @@ socket: xev.TCP,
|
||||
acceptor: xev.Timer,
|
||||
|
||||
/// Array of client sockets
|
||||
clients: [MAX_CLIENTS]*xev.TCP,
|
||||
/// TODO: Merge with `sock_pool`? or make a hashmap
|
||||
clients: SocketPool,
|
||||
|
||||
/// Number of clients connected
|
||||
clients_count: usize,
|
||||
|
||||
/// Initializes the server with the given allocator and address
|
||||
pub fn init(alloc: Allocator, addr: std.net.Address) !Server {
|
||||
const socket = try xev.TCP.init(addr);
|
||||
try socket.bind(addr);
|
||||
/// Address to bind to
|
||||
addr: std.net.Address,
|
||||
|
||||
/// Maximum clients allowed
|
||||
max_clients: u8,
|
||||
|
||||
/// Initializes the server with the given allocator and address
|
||||
pub fn init(
|
||||
alloc: Allocator,
|
||||
addr: std.net.Address,
|
||||
max_clients: u8,
|
||||
) !Server {
|
||||
return Server{
|
||||
.comp_pool = CompletionPool.init(alloc),
|
||||
.sock_pool = SocketPool.init(alloc),
|
||||
.buf_pool = BufferPool.init(alloc),
|
||||
.stop = false,
|
||||
.loop = try xev.Loop.init(.{}),
|
||||
.socket = socket,
|
||||
.socket = try xev.TCP.init(addr),
|
||||
.acceptor = try xev.Timer.init(),
|
||||
.clients = undefined,
|
||||
.clients_count = 0,
|
||||
.alloc = alloc,
|
||||
.max_clients = max_clients,
|
||||
.addr = addr,
|
||||
};
|
||||
}
|
||||
|
||||
const BindError = error{
|
||||
NoAddress,
|
||||
InvalidAddress,
|
||||
};
|
||||
|
||||
/// Tries to generate a valid address to bind to
|
||||
/// TODO: Maybe unix sockets should start with unix://
|
||||
pub fn parseAddress(raw_addr: ?[:0]const u8) BindError!std.net.Address {
|
||||
const addr = raw_addr orelse {
|
||||
return BindError.NoAddress;
|
||||
};
|
||||
|
||||
if (addr.len == 0) {
|
||||
return BindError.NoAddress;
|
||||
}
|
||||
|
||||
var iter = std.mem.splitScalar(u8, addr, ':');
|
||||
const host = iter.next() orelse return BindError.InvalidAddress;
|
||||
const port = iter.next() orelse return BindError.InvalidAddress;
|
||||
const numPort = std.fmt.parseInt(u16, port, 10) catch {
|
||||
return std.net.Address.initUnix(addr) catch BindError.InvalidAddress;
|
||||
};
|
||||
|
||||
const ip = std.net.Address.parseIp4(host, numPort) catch {
|
||||
return std.net.Address.initUnix(addr) catch BindError.InvalidAddress;
|
||||
};
|
||||
|
||||
return ip;
|
||||
}
|
||||
|
||||
/// Deinitializes the server
|
||||
pub fn deinit(self: *Server) void {
|
||||
log.info("shutting down server", .{});
|
||||
var c: xev.Completion = undefined;
|
||||
self.socket.close(&self.loop, &c, Server, self, closeHandler);
|
||||
|
||||
self.buf_pool.deinit();
|
||||
self.comp_pool.deinit();
|
||||
self.sock_pool.deinit();
|
||||
@ -80,7 +117,8 @@ pub fn deinit(self: *Server) void {
|
||||
|
||||
/// Starts the timer which tries to accept connections
|
||||
pub fn start(self: *Server) !void {
|
||||
try self.socket.listen(MAX_CLIENTS);
|
||||
try self.socket.bind(self.addr);
|
||||
try self.socket.listen(self.max_clients);
|
||||
log.info("bound server to socket={any}", .{self.socket});
|
||||
|
||||
// Each acceptor borrows a completion from the pool
|
||||
@ -91,7 +129,7 @@ pub fn start(self: *Server) !void {
|
||||
};
|
||||
|
||||
self.acceptor.run(&self.loop, c, ACCEPTOR_RATE, Server, self, acceptor);
|
||||
while (!self.stop) {
|
||||
while (true) {
|
||||
try self.loop.run(.until_done);
|
||||
}
|
||||
}
|
||||
@ -153,14 +191,14 @@ fn acceptHandler(
|
||||
return .disarm;
|
||||
};
|
||||
|
||||
if (self.clients_count == MAX_CLIENTS) {
|
||||
if (self.clients_count == self.max_clients) {
|
||||
log.warn("max clients reached, rejecting fd={d}", .{sock.fd});
|
||||
reject_client(self, sock) catch return .rearm;
|
||||
return .disarm;
|
||||
}
|
||||
|
||||
log.info("accepted connection fd={d}", .{sock.fd});
|
||||
self.clients[self.clients_count] = sock;
|
||||
// self.clients[self.clients_count] = sock;
|
||||
self.clients_count += 1;
|
||||
|
||||
read_client(self, sock, c) catch {
|
||||
|
@ -5,6 +5,7 @@ const std = @import("std");
|
||||
const xev = @import("xev");
|
||||
const tcp = @import("../tcp.zig");
|
||||
const App = @import("../App.zig");
|
||||
const Config = @import("../Config.zig").Config;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const log = std.log.scoped(.tcp_thread);
|
||||
@ -19,9 +20,17 @@ server: tcp.Server,
|
||||
/// up all the internal state necessary prior to starting the thread. It
|
||||
/// is up to the caller to start the thread with the threadMain entrypoint.
|
||||
pub fn init(alloc: Allocator) !Thread {
|
||||
// TODO: Configurable addresses and socket paths
|
||||
const addr = try std.net.Address.parseIp4("127.0.0.1", 9090);
|
||||
var server = try tcp.Server.init(alloc, addr);
|
||||
const config = try Config.load(alloc);
|
||||
const max_clients = config.@"remote-max-connections";
|
||||
const addr = config.@"remote-tcp-socket";
|
||||
|
||||
const parsedAddr = tcp.Server.parseAddress(addr) catch |err| {
|
||||
log.err("failed to parse address addr={any} err={any}", .{ addr, err });
|
||||
return err;
|
||||
};
|
||||
|
||||
log.debug("parsed address addr={any}", .{parsedAddr});
|
||||
var server = try tcp.Server.init(alloc, parsedAddr, max_clients);
|
||||
errdefer server.deinit();
|
||||
|
||||
return Thread{
|
||||
@ -45,6 +54,6 @@ pub fn threadMain(self: *Thread) void {
|
||||
|
||||
fn threadMain_(self: *Thread) !void {
|
||||
log.debug("starting tcp thread", .{});
|
||||
defer log.debug("tcp thread exited", .{});
|
||||
try self.server.start();
|
||||
errdefer log.debug("tcp thread exited", .{});
|
||||
}
|
||||
|
Reference in New Issue
Block a user