milestone 19: hygiene sweep - dead ecs surface, single-source constants, tls classification, frontend state hazards, docker smoke network fix
This commit is contained in:
@@ -162,11 +162,34 @@ pub const DohClient = struct {
|
||||
/// time two phases shared an error.
|
||||
const Phase = enum { connect, send, receive };
|
||||
|
||||
/// Every TLS failure this client can reach, named rather than matched by
|
||||
/// prefix. Two groups:
|
||||
///
|
||||
/// - `std.http.Client.RequestError` collapses every handshake and bundle fault
|
||||
/// into `TlsInitializationFailed` / `CertificateBundleLoadFailure`
|
||||
/// (Client.zig:1470, :1717).
|
||||
/// - `std.crypto.tls.Client.ReadError` is what `headCause`/`bodyCause` unwrap
|
||||
/// out of a collapsed `error.ReadFailed`, through
|
||||
/// `Connection.getReadError` (Client.zig:392), so its record-layer members
|
||||
/// arrive here as themselves. Without them a decode error or a bad record MAC
|
||||
/// would be reported as a plain receive failure.
|
||||
fn mapError(err: anyerror, phase: Phase) transport.ExchangeError {
|
||||
if (transport.mapLocal(err)) |local| return local;
|
||||
const err_name = @errorName(err);
|
||||
if (std.mem.startsWith(u8, err_name, "Tls") or
|
||||
std.mem.startsWith(u8, err_name, "Certificate")) return error.TlsFailed;
|
||||
switch (err) {
|
||||
error.TlsInitializationFailed,
|
||||
error.CertificateBundleLoadFailure,
|
||||
error.TlsAlert,
|
||||
error.TlsBadLength,
|
||||
error.TlsBadRecordMac,
|
||||
error.TlsConnectionTruncated,
|
||||
error.TlsDecodeError,
|
||||
error.TlsRecordOverflow,
|
||||
error.TlsUnexpectedMessage,
|
||||
error.TlsIllegalParameter,
|
||||
error.TlsSequenceOverflow,
|
||||
=> return error.TlsFailed,
|
||||
else => {},
|
||||
}
|
||||
return switch (phase) {
|
||||
.connect => error.ConnectFailed,
|
||||
.send => error.SendFailed,
|
||||
@@ -174,6 +197,27 @@ fn mapError(err: anyerror, phase: Phase) transport.ExchangeError {
|
||||
};
|
||||
}
|
||||
|
||||
// `error.X` in an expression names a member into existence rather than
|
||||
// referring to one, so the switch above would keep compiling — and silently
|
||||
// stop matching — if std renamed any of these. This block is what fails the
|
||||
// build instead. Every member of the read-cause set must be classified, and the
|
||||
// two collapsed names must still exist.
|
||||
comptime {
|
||||
for (@typeInfo(std.crypto.tls.Client.ReadError).error_set.?) |member| {
|
||||
const value: anyerror = @field(std.crypto.tls.Client.ReadError, member.name);
|
||||
if (mapError(value, .receive) != error.TlsFailed) {
|
||||
@compileError("unclassified TLS read cause: " ++ member.name);
|
||||
}
|
||||
}
|
||||
for ([_][]const u8{ "TlsInitializationFailed", "CertificateBundleLoadFailure" }) |name| {
|
||||
var found = false;
|
||||
for (@typeInfo(std.http.Client.RequestError).error_set.?) |member| {
|
||||
if (std.mem.eql(u8, member.name, name)) found = true;
|
||||
}
|
||||
if (!found) @compileError("std.http.Client.RequestError no longer names " ++ name);
|
||||
}
|
||||
}
|
||||
|
||||
const Connection = std.http.Client.Connection;
|
||||
const Request = std.http.Client.Request;
|
||||
const Response = std.http.Client.Response;
|
||||
@@ -307,10 +351,21 @@ test "mapError maps local errors before phase errors" {
|
||||
try testing.expectEqual(error.Unexpected, mapError(error.Unexpected, .send));
|
||||
}
|
||||
|
||||
test "mapError maps tls errors regardless of phase" {
|
||||
test "mapError maps the collapsed tls errors regardless of phase" {
|
||||
try testing.expectEqual(error.TlsFailed, mapError(error.TlsInitializationFailed, .connect));
|
||||
try testing.expectEqual(error.TlsFailed, mapError(error.TlsAlert, .receive));
|
||||
try testing.expectEqual(error.TlsFailed, mapError(error.CertificateExpired, .connect));
|
||||
try testing.expectEqual(error.TlsFailed, mapError(error.TlsInitializationFailed, .receive));
|
||||
try testing.expectEqual(error.TlsFailed, mapError(error.CertificateBundleLoadFailure, .connect));
|
||||
}
|
||||
|
||||
test "mapError maps every unwrapped record-layer cause to TlsFailed" {
|
||||
// The set is the one `Connection.getReadError` can hand back, so the loop
|
||||
// fails the day std adds a member the switch does not name.
|
||||
inline for (@typeInfo(std.crypto.tls.Client.ReadError).error_set.?) |member| {
|
||||
try testing.expectEqual(
|
||||
transport.ExchangeError.TlsFailed,
|
||||
mapError(@field(std.crypto.tls.Client.ReadError, member.name), .receive),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test "mapError maps remaining errors by phase" {
|
||||
|
||||
Reference in New Issue
Block a user