| ... | @@ -1,5 +1,7 @@ | ... | @@ -1,5 +1,7 @@ |
| 1 | //! TODO: send connection: keep-alive and LRU cache a configurable number of | 1 | //! TODO: send connection: keep-alive and LRU cache a configurable number of |
| 2 | //! open connections to skip DNS and TLS handshake for subsequent requests. | 2 | //! open connections to skip DNS and TLS handshake for subsequent requests. |
| | 3 | //! |
| | 4 | //! This API is *not* thread safe. |
| 3 | | 5 | |
| 4 | const std = @import("../std.zig"); | 6 | const std = @import("../std.zig"); |
| 5 | const mem = std.mem; | 7 | const mem = std.mem; |
| ... | @@ -15,6 +17,9 @@ const testing = std.testing; | ... | @@ -15,6 +17,9 @@ const testing = std.testing; |
| 15 | /// managed buffer is not provided. | 17 | /// managed buffer is not provided. |
| 16 | allocator: Allocator, | 18 | allocator: Allocator, |
| 17 | ca_bundle: std.crypto.Certificate.Bundle = .{}, | 19 | ca_bundle: std.crypto.Certificate.Bundle = .{}, |
| | 20 | /// When this is `true`, the next time this client performs an HTTPS request, |
| | 21 | /// it will first rescan the system for root certificates. |
| | 22 | next_https_rescan_certs: bool = true, |
| 18 | | 23 | |
| 19 | pub const Connection = struct { | 24 | pub const Connection = struct { |
| 20 | stream: net.Stream, | 25 | stream: net.Stream, |
| ... | @@ -594,6 +599,7 @@ pub const Request = struct { | ... | @@ -594,6 +599,7 @@ pub const Request = struct { |
| 594 | CertificateTimeInvalid, | 599 | CertificateTimeInvalid, |
| 595 | CertificateHasUnrecognizedObjectId, | 600 | CertificateHasUnrecognizedObjectId, |
| 596 | CertificateHasInvalidBitString, | 601 | CertificateHasInvalidBitString, |
| | 602 | CertificateAuthorityBundleTooBig, |
| 597 | | 603 | |
| 598 | // TODO: convert to higher level errors | 604 | // TODO: convert to higher level errors |
| 599 | InvalidFormat, | 605 | InvalidFormat, |
| ... | @@ -648,6 +654,10 @@ pub const Request = struct { | ... | @@ -648,6 +654,10 @@ pub const Request = struct { |
| 648 | NetworkSubsystemFailed, | 654 | NetworkSubsystemFailed, |
| 649 | NotDir, | 655 | NotDir, |
| 650 | ReadOnlyFileSystem, | 656 | ReadOnlyFileSystem, |
| | 657 | Unseekable, |
| | 658 | MissingEndCertificateMarker, |
| | 659 | InvalidPadding, |
| | 660 | EndOfStream, |
| 651 | }; | 661 | }; |
| 652 | | 662 | |
| 653 | pub fn read(req: *Request, buffer: []u8) ReadError!usize { | 663 | pub fn read(req: *Request, buffer: []u8) ReadError!usize { |
| ... | @@ -837,10 +847,6 @@ pub fn deinit(client: *Client) void { | ... | @@ -837,10 +847,6 @@ pub fn deinit(client: *Client) void { |
| 837 | client.* = undefined; | 847 | client.* = undefined; |
| 838 | } | 848 | } |
| 839 | | 849 | |
| 840 | pub fn rescanRootCertificates(client: *Client) !void { | | |
| 841 | return client.ca_bundle.rescan(client.allocator); | | |
| 842 | } | | |
| 843 | | | |
| 844 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !Connection { | 850 | pub fn connect(client: *Client, host: []const u8, port: u16, protocol: Connection.Protocol) !Connection { |
| 845 | var conn: Connection = .{ | 851 | var conn: Connection = .{ |
| 846 | .stream = try net.tcpConnectToHost(client.allocator, host, port), | 852 | .stream = try net.tcpConnectToHost(client.allocator, host, port), |
| ... | @@ -876,6 +882,11 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req | ... | @@ -876,6 +882,11 @@ pub fn request(client: *Client, uri: Uri, headers: Request.Headers, options: Req |
| 876 | | 882 | |
| 877 | const host = uri.host orelse return error.UriMissingHost; | 883 | const host = uri.host orelse return error.UriMissingHost; |
| 878 | | 884 | |
| | 885 | if (client.next_https_rescan_certs and protocol == .tls) { |
| | 886 | try client.ca_bundle.rescan(client.allocator); |
| | 887 | client.next_https_rescan_certs = false; |
| | 888 | } |
| | 889 | |
| 879 | var req: Request = .{ | 890 | var req: Request = .{ |
| 880 | .client = client, | 891 | .client = client, |
| 881 | .headers = headers, | 892 | .headers = headers, |