milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
CI / test (push) Successful in 1m22s
CI / test-aarch64 (push) Successful in 5m6s
CI / frontend (push) Successful in 45s
CI / cross (push) Successful in 7m53s
CI / docker (push) Failing after 1h10m57s

This commit is contained in:
2026-08-07 18:20:30 +02:00
parent c50c6d285a
commit 6f67940995
82 changed files with 3167 additions and 3114 deletions
+15 -62
View File
@@ -135,13 +135,13 @@ pub const ForwardClient = struct {
const socket = local.bind(io, .{ .mode = .dgram }) catch |err| {
log.debug("forward resolver: udp bind failed: {s}", .{@errorName(err)});
return mapPhase(err, error.ConnectFailed);
return transport.mapPhase(err, error.ConnectFailed);
};
defer closeSocket(io, &socket);
defer transport.closeBlocked(io, &socket);
socket.send(io, &dest, query) catch |err| {
log.debug("forward resolver: udp send failed: {s}", .{@errorName(err)});
return mapPhase(err, error.SendFailed);
return transport.mapPhase(err, error.SendFailed);
};
// A deadline, not a duration: a discarded foreign datagram restarts the
@@ -152,7 +152,7 @@ pub const ForwardClient = struct {
const msg = socket.receiveTimeout(io, response_buf, deadline) catch |err| switch (err) {
error.Timeout => return error.Timeout,
error.ConcurrencyUnavailable => return error.SystemResources,
else => return mapPhase(err, error.ReceiveFailed),
else => return transport.mapPhase(err, error.ReceiveFailed),
};
// Off-path spoofing is the reason the source address is checked at
@@ -183,35 +183,16 @@ pub const ForwardClient = struct {
}
}
/// No stream read or write in 0.16.0 takes a timeout, so the budget is a
/// second task and the loser is canceled. `ConnectOptions.timeout` is never
/// set: the Threaded backend panics on it (Threaded.zig:12076).
/// The read budget bounds the whole TCP exchange through
/// `transport.raceWithin`. `ConnectOptions.timeout` is never set: the
/// Threaded backend panics on it (Threaded.zig:12076).
fn exchangeTcp(
self: *ForwardClient,
io: std.Io,
query: []const u8,
response_buf: []u8,
) transport.ExchangeError![]u8 {
var outcomes: [2]Outcome = undefined;
var race: std.Io.Select(Outcome) = .init(io, &outcomes);
defer race.cancelDiscard();
race.concurrent(.exchange, tcpOnce, .{ self, io, query, response_buf }) catch |err| switch (err) {
error.ConcurrencyUnavailable => return error.SystemResources,
};
race.concurrent(.expiry, expire, .{ io, self.read_timeout }) catch |err| switch (err) {
error.ConcurrencyUnavailable => return error.SystemResources,
};
switch (try race.await()) {
.exchange => |result| return result,
.expiry => |result| {
// A canceled sleep means this whole task is being torn down,
// not that the resolver is slow.
try result;
return error.Timeout;
},
}
return transport.raceWithin(io, self.read_timeout, tcpOnce, .{ self, io, query, response_buf });
}
fn tcpOnce(
@@ -224,9 +205,9 @@ pub const ForwardClient = struct {
const stream = dest.connect(io, .{ .mode = .stream }) catch |err| {
log.debug("forward resolver: tcp connect failed: {s}", .{@errorName(err)});
return mapPhase(err, error.ConnectFailed);
return transport.mapPhase(err, error.ConnectFailed);
};
defer closeStream(io, &stream);
defer transport.closeBlocked(io, &stream);
const split = self.frame_buf.len / 2;
var stream_writer = stream.writer(io, self.frame_buf[0..split]);
@@ -257,15 +238,6 @@ pub const ForwardClient = struct {
}
};
const Outcome = union(enum) {
exchange: transport.ExchangeError![]u8,
expiry: std.Io.Cancelable!void,
};
fn expire(io: std.Io, duration: std.Io.Clock.Duration) std.Io.Cancelable!void {
return duration.sleep(io);
}
/// The local address a datagram to `dest` is sent from: same family, port
/// chosen by the kernel.
fn wildcardFor(dest: net.IpAddress) net.IpAddress {
@@ -275,25 +247,6 @@ fn wildcardFor(dest: net.IpAddress) net.IpAddress {
};
}
/// The TCP budget cancels the exchange task. The next cancelable `Io` call in
/// the `defer` chain would then return `error.Canceled` and skip the close,
/// leaking the descriptor, so both closes run with cancellation blocked.
fn closeStream(io: std.Io, stream: *const net.Stream) void {
const prev = io.swapCancelProtection(.blocked);
defer _ = io.swapCancelProtection(prev);
stream.close(io);
}
fn closeSocket(io: std.Io, socket: *const net.Socket) void {
const prev = io.swapCancelProtection(.blocked);
defer _ = io.swapCancelProtection(prev);
socket.close(io);
}
fn mapPhase(err: anyerror, phase: transport.PeerFault) transport.ExchangeError {
return transport.mapLocal(err) orelse phase;
}
/// `Io.Writer` collapses everything to `error.WriteFailed` and stashes the
/// cause. Unwrapping it is what keeps `error.Canceled` and the local resource
/// errors out of the peer fault group.
@@ -302,7 +255,7 @@ fn sendFailure(stream_writer: *const net.Stream.Writer, err: anyerror) transport
stream_writer.err.?
else
err;
return mapPhase(cause, error.SendFailed);
return transport.mapPhase(cause, error.SendFailed);
}
fn receiveFailure(stream_reader: *const net.Stream.Reader, err: anyerror) transport.ExchangeError {
@@ -310,7 +263,7 @@ fn receiveFailure(stream_reader: *const net.Stream.Reader, err: anyerror) transp
stream_reader.err.?
else
err;
return mapPhase(cause, error.ReceiveFailed);
return transport.mapPhase(cause, error.ReceiveFailed);
}
const testing = std.testing;
@@ -408,19 +361,19 @@ test "mapPhase keeps local resource and cancellation errors out of the peer faul
for (local) |err| {
try testing.expectEqual(
transport.Group.local_resource,
transport.group(mapPhase(err, error.ReceiveFailed)),
transport.group(transport.mapPhase(err, error.ReceiveFailed)),
);
}
try testing.expectEqual(
transport.ExchangeError.Canceled,
mapPhase(error.Canceled, error.ConnectFailed),
transport.mapPhase(error.Canceled, error.ConnectFailed),
);
// A refused connection is the resolver's side, so it stays a peer fault.
try testing.expectEqual(
transport.ExchangeError.ConnectFailed,
mapPhase(error.ConnectionRefused, error.ConnectFailed),
transport.mapPhase(error.ConnectionRefused, error.ConnectFailed),
);
}