Commit 038ed32cff

Nameless <truemedian@gmail.com>
2023-04-13 05:48:03
add explicit error union for Bundle.rescan and associated functions
1 parent 40e1fca
Changed files (4)
lib
std
crypto
http
lib/std/crypto/Certificate/Bundle/macos.zig
@@ -5,7 +5,9 @@ const mem = std.mem;
 const Allocator = std.mem.Allocator;
 const Bundle = @import("../Bundle.zig");
 
-pub fn rescanMac(cb: *Bundle, gpa: Allocator) !void {
+pub const RescanMacError = Allocator.Error || fs.File.OpenError || fs.File.ReadError || fs.File.SeekError || Bundle.ParseCertError || error{EndOfStream};
+
+pub fn rescanMac(cb: *Bundle, gpa: Allocator) RescanMacError!void {
     cb.bytes.clearRetainingCapacity();
     cb.map.clearRetainingCapacity();
 
lib/std/crypto/Certificate/Bundle.zig
@@ -50,11 +50,13 @@ pub fn deinit(cb: *Bundle, gpa: Allocator) void {
     cb.* = undefined;
 }
 
+pub const RescanError = RescanLinuxError || RescanMacError || RescanWindowsError;
+
 /// Clears the set of certificates and then scans the host operating system
 /// file system standard locations for certificates.
 /// For operating systems that do not have standard CA installations to be
 /// found, this function clears the set of certificates.
-pub fn rescan(cb: *Bundle, gpa: Allocator) !void {
+pub fn rescan(cb: *Bundle, gpa: Allocator) RescanError!void {
     switch (builtin.os.tag) {
         .linux => return rescanLinux(cb, gpa),
         .macos => return rescanMac(cb, gpa),
@@ -64,8 +66,11 @@ pub fn rescan(cb: *Bundle, gpa: Allocator) !void {
 }
 
 pub const rescanMac = @import("Bundle/macos.zig").rescanMac;
+pub const RescanMacError = @import("Bundle/macos.zig").RescanMacError;
+
+pub const RescanLinuxError = AddCertsFromFilePathError || AddCertsFromDirPathError;
 
-pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {
+pub fn rescanLinux(cb: *Bundle, gpa: Allocator) RescanLinuxError!void {
     // Possible certificate files; stop after finding one.
     const cert_file_paths = [_][]const u8{
         "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
@@ -107,7 +112,9 @@ pub fn rescanLinux(cb: *Bundle, gpa: Allocator) !void {
     cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
 }
 
-pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {
+pub const RescanWindowsError = Allocator.Error || ParseCertError || std.os.UnexpectedError || error{FileNotFound};
+
+pub fn rescanWindows(cb: *Bundle, gpa: Allocator) RescanWindowsError!void {
     cb.bytes.clearRetainingCapacity();
     cb.map.clearRetainingCapacity();
 
@@ -132,12 +139,14 @@ pub fn rescanWindows(cb: *Bundle, gpa: Allocator) !void {
     cb.bytes.shrinkAndFree(gpa, cb.bytes.items.len);
 }
 
+pub const AddCertsFromDirPathError = fs.File.OpenError || AddCertsFromDirError;
+
 pub fn addCertsFromDirPath(
     cb: *Bundle,
     gpa: Allocator,
     dir: fs.Dir,
     sub_dir_path: []const u8,
-) !void {
+) AddCertsFromDirPathError!void {
     var iterable_dir = try dir.openIterableDir(sub_dir_path, .{});
     defer iterable_dir.close();
     return addCertsFromDir(cb, gpa, iterable_dir);
@@ -147,14 +156,16 @@ pub fn addCertsFromDirPathAbsolute(
     cb: *Bundle,
     gpa: Allocator,
     abs_dir_path: []const u8,
-) !void {
+) AddCertsFromDirPathError!void {
     assert(fs.path.isAbsolute(abs_dir_path));
     var iterable_dir = try fs.openIterableDirAbsolute(abs_dir_path, .{});
     defer iterable_dir.close();
     return addCertsFromDir(cb, gpa, iterable_dir);
 }
 
-pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir) !void {
+pub const AddCertsFromDirError = AddCertsFromFilePathError;
+
+pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir) AddCertsFromDirError!void {
     var it = iterable_dir.iterate();
     while (try it.next()) |entry| {
         switch (entry.kind) {
@@ -166,11 +177,13 @@ pub fn addCertsFromDir(cb: *Bundle, gpa: Allocator, iterable_dir: fs.IterableDir
     }
 }
 
+pub const AddCertsFromFilePathError = fs.File.OpenError || AddCertsFromFileError;
+
 pub fn addCertsFromFilePathAbsolute(
     cb: *Bundle,
     gpa: Allocator,
     abs_file_path: []const u8,
-) !void {
+) AddCertsFromFilePathError!void {
     assert(fs.path.isAbsolute(abs_file_path));
     var file = try fs.openFileAbsolute(abs_file_path, .{});
     defer file.close();
@@ -182,13 +195,15 @@ pub fn addCertsFromFilePath(
     gpa: Allocator,
     dir: fs.Dir,
     sub_file_path: []const u8,
-) !void {
+) AddCertsFromFilePathError!void {
     var file = try dir.openFile(sub_file_path, .{});
     defer file.close();
     return addCertsFromFile(cb, gpa, file);
 }
 
-pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) !void {
+pub const AddCertsFromFileError = Allocator.Error || fs.File.GetSeekPosError || fs.File.ReadError || ParseCertError || std.base64.Error || error{ CertificateAuthorityBundleTooBig, MissingEndCertificateMarker };
+
+pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) AddCertsFromFileError!void {
     const size = try file.getEndPos();
 
     // We borrow `bytes` as a temporary buffer for the base64-encoded data.
@@ -222,7 +237,9 @@ pub fn addCertsFromFile(cb: *Bundle, gpa: Allocator, file: fs.File) !void {
     }
 }
 
-pub fn parseCert(cb: *Bundle, gpa: Allocator, decoded_start: u32, now_sec: i64) !void {
+pub const ParseCertError = Allocator.Error || Certificate.ParseError;
+
+pub fn parseCert(cb: *Bundle, gpa: Allocator, decoded_start: u32, now_sec: i64) ParseCertError!void {
     // Even though we could only partially parse the certificate to find
     // the subject name, we pre-parse all of them to make sure and only
     // include in the bundle ones that we know will parse. This way we can
lib/std/crypto/Certificate.zig
@@ -371,7 +371,9 @@ test "Parsed.checkHostName" {
     try expectEqual(false, Parsed.checkHostName("lang.org", "zig*.org"));
 }
 
-pub fn parse(cert: Certificate) !Parsed {
+pub const ParseError = der.Element.ParseElementError || ParseVersionError || ParseTimeError || ParseEnumError || ParseBitStringError;
+
+pub fn parse(cert: Certificate) ParseError!Parsed {
     const cert_bytes = cert.buffer;
     const certificate = try der.Element.parse(cert_bytes, cert.index);
     const tbs_certificate = try der.Element.parse(cert_bytes, certificate.slice.start);
@@ -514,14 +516,18 @@ pub fn contents(cert: Certificate, elem: der.Element) []const u8 {
     return cert.buffer[elem.slice.start..elem.slice.end];
 }
 
+pub const ParseBitStringError = error{ CertificateFieldHasWrongDataType, CertificateHasInvalidBitString };
+
 pub fn parseBitString(cert: Certificate, elem: der.Element) !der.Element.Slice {
     if (elem.identifier.tag != .bitstring) return error.CertificateFieldHasWrongDataType;
     if (cert.buffer[elem.slice.start] != 0) return error.CertificateHasInvalidBitString;
     return .{ .start = elem.slice.start + 1, .end = elem.slice.end };
 }
 
+pub const ParseTimeError = error{ CertificateTimeInvalid, CertificateFieldHasWrongDataType };
+
 /// Returns number of seconds since epoch.
-pub fn parseTime(cert: Certificate, elem: der.Element) !u64 {
+pub fn parseTime(cert: Certificate, elem: der.Element) ParseTimeError!u64 {
     const bytes = cert.contents(elem);
     switch (elem.identifier.tag) {
         .utc_time => {
@@ -647,34 +653,38 @@ test parseYear4 {
     try expectError(error.CertificateTimeInvalid, parseYear4("crap"));
 }
 
-pub fn parseAlgorithm(bytes: []const u8, element: der.Element) !Algorithm {
+pub fn parseAlgorithm(bytes: []const u8, element: der.Element) ParseEnumError!Algorithm {
     return parseEnum(Algorithm, bytes, element);
 }
 
-pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) !AlgorithmCategory {
+pub fn parseAlgorithmCategory(bytes: []const u8, element: der.Element) ParseEnumError!AlgorithmCategory {
     return parseEnum(AlgorithmCategory, bytes, element);
 }
 
-pub fn parseAttribute(bytes: []const u8, element: der.Element) !Attribute {
+pub fn parseAttribute(bytes: []const u8, element: der.Element) ParseEnumError!Attribute {
     return parseEnum(Attribute, bytes, element);
 }
 
-pub fn parseNamedCurve(bytes: []const u8, element: der.Element) !NamedCurve {
+pub fn parseNamedCurve(bytes: []const u8, element: der.Element) ParseEnumError!NamedCurve {
     return parseEnum(NamedCurve, bytes, element);
 }
 
-pub fn parseExtensionId(bytes: []const u8, element: der.Element) !ExtensionId {
+pub fn parseExtensionId(bytes: []const u8, element: der.Element) ParseEnumError!ExtensionId {
     return parseEnum(ExtensionId, bytes, element);
 }
 
-fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) !E {
+pub const ParseEnumError = error{ CertificateFieldHasWrongDataType, CertificateHasUnrecognizedObjectId };
+
+fn parseEnum(comptime E: type, bytes: []const u8, element: der.Element) ParseEnumError!E {
     if (element.identifier.tag != .object_identifier)
         return error.CertificateFieldHasWrongDataType;
     const oid_bytes = bytes[element.slice.start..element.slice.end];
     return E.map.get(oid_bytes) orelse return error.CertificateHasUnrecognizedObjectId;
 }
 
-pub fn parseVersion(bytes: []const u8, version_elem: der.Element) !Version {
+pub const ParseVersionError = error{ UnsupportedCertificateVersion, CertificateFieldHasInvalidLength };
+
+pub fn parseVersion(bytes: []const u8, version_elem: der.Element) ParseVersionError!Version {
     if (@bitCast(u8, version_elem.identifier) != 0xa0)
         return .v1;
 
@@ -861,9 +871,9 @@ pub const der = struct {
             pub const empty: Slice = .{ .start = 0, .end = 0 };
         };
 
-        pub const ParseError = error{CertificateFieldHasInvalidLength};
+        pub const ParseElementError = error{CertificateFieldHasInvalidLength};
 
-        pub fn parse(bytes: []const u8, index: u32) ParseError!Element {
+        pub fn parse(bytes: []const u8, index: u32) ParseElementError!Element {
             var i = index;
             const identifier = @bitCast(Identifier, bytes[i]);
             i += 1;
lib/std/http/Client.zig
@@ -29,34 +29,16 @@ connection_pool: ConnectionPool = .{},
 last_error: ?ExtraError = null,
 
 pub const ExtraError = union(enum) {
-    fn impliedErrorSet(comptime f: anytype) type {
-        const set = @typeInfo(@typeInfo(@TypeOf(f)).Fn.return_type.?).ErrorUnion.error_set;
-        if (@typeName(set)[0] != '@') @compileError(@typeName(f) ++ " doesn't have an implied error set any more.");
-        return set;
-    }
-
-    // There's apparently a dependency loop with using Client.DeflateDecompressor.
-    const FakeTransferError = proto.HeadersParser.ReadError || error{ReadFailed};
-    const FakeTransferReader = std.io.Reader(void, FakeTransferError, fakeRead);
-    fn fakeRead(ctx: void, buf: []u8) FakeTransferError!usize {
-        _ = .{ buf, ctx };
-        return 0;
-    }
-
-    const FakeDeflateDecompressor = std.compress.zlib.ZlibStream(FakeTransferReader);
-    const FakeGzipDecompressor = std.compress.gzip.Decompress(FakeTransferReader);
-    const FakeZstdDecompressor = std.compress.zstd.DecompressStream(FakeTransferReader, .{});
-
     pub const TcpConnectError = std.net.TcpConnectToHostError;
     pub const TlsError = std.crypto.tls.Client.InitError(net.Stream);
     pub const WriteError = BufferedConnection.WriteError;
     pub const ReadError = BufferedConnection.ReadError || error{HttpChunkInvalid};
-    pub const CaBundleError = impliedErrorSet(std.crypto.Certificate.Bundle.rescan);
+    pub const CaBundleError = std.crypto.Certificate.Bundle.RescanError;
 
     pub const ZlibInitError = error{ BadHeader, InvalidCompression, InvalidWindowSize, Unsupported, EndOfStream, OutOfMemory } || Request.TransferReadError;
     pub const GzipInitError = error{ BadHeader, InvalidCompression, OutOfMemory, WrongChecksum, EndOfStream, StreamTooLong } || Request.TransferReadError;
-    // pub const DecompressError = Client.DeflateDecompressor.Error || Client.GzipDecompressor.Error || Client.ZstdDecompressor.Error;
-    pub const DecompressError = FakeDeflateDecompressor.Error || FakeGzipDecompressor.Error || FakeZstdDecompressor.Error;
+    // pub const DecompressError = Compression.DeflateDecompressor.Error || Compression.GzipDecompressor.Error || Compression.ZstdDecompressor.Error;
+    pub const DecompressError = anyerror; // FIXME: the above line causes a false positive dependency loop
 
     zlib_init: ZlibInitError, // error.CompressionInitializationFailed
     gzip_init: GzipInitError, // error.CompressionInitializationFailed