milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
This commit is contained in:
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user