From 3f5d9d9542eddb288b70084902f80fff738e3a2a Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 6 Nov 2025 12:05:49 -0800 Subject: [PATCH] updates for PR 25592 (std.Io interface) and 25706 (remove Oracle Solaris) --- build.zig.zon | 8 +++---- codegen/src/main.zig | 23 +++++++++++------- lib/date/build.zig.zon | 4 ++-- lib/date/src/parsing.zig | 6 +++-- src/aws.zig | 3 ++- src/aws_credentials.zig | 28 +++++++++++----------- src/aws_http.zig | 10 ++++---- src/aws_signing.zig | 34 +++++++++++++++++++-------- src/aws_test.zig | 50 +++++++++++++++++++++++++++++----------- src/main.zig | 8 +++++-- 10 files changed, 113 insertions(+), 61 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index cf63e3e..ed687c9 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -22,10 +22,6 @@ .url = "https://github.com/aws/aws-sdk-go-v2/archive/refs/tags/release-2025-05-05.tar.gz", .hash = "N-V-__8AAKWdeiawujEcrfukQbb8lLAiQIRT0uG5gCcm4b7W", }, - .zeit = .{ - .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88", - .hash = "zeit-0.6.0-5I6bk0J9AgCVa0nnyL0lNY9Xa9F68hHq-ZarhuXNV-Jb", - }, .date = .{ .path = "lib/date", }, @@ -36,5 +32,9 @@ .url = "git+https://github.com/travisstaloch/case.git#f8003fe5f93b65f673d10d41323e347225e8cb87", .hash = "case-0.0.1-chGYqx_EAADaGJjmoln5M1iMBDTrMdd8to5wdEVpfXm4", }, + .zeit = .{ + .url = "git+https://github.com/elerch/zeit#8190461dc1f892f6370fa9d5cd76690aac0e1c71", + .hash = "zeit-0.6.0-5I6bk99-AgDNMIDuw2Zcoe_9QYIpzwZJqeqMpU54egTd", + }, }, } diff --git a/codegen/src/main.zig b/codegen/src/main.zig index a8aa1c7..94a96cb 100644 --- a/codegen/src/main.zig +++ b/codegen/src/main.zig @@ -33,6 +33,10 @@ pub fn main() anyerror!void { defer arena.deinit(); const allocator = arena.allocator(); + var threaded: std.Io.Threaded = .init(allocator); + defer threaded.deinit(); + const io = threaded.io(); + const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); var stdout_writer = std.fs.File.stdout().writer(&.{}); @@ -79,7 +83,7 @@ pub fn main() anyerror!void { skip_next = true; continue; } - try processFile(arg, output_dir, &manifest); + try processFile(io, arg, output_dir, &manifest); files_processed += 1; } if (files_processed == 0) { @@ -93,12 +97,12 @@ pub fn main() anyerror!void { defer cwd.setAsCwd() catch unreachable; try m.setAsCwd(); - try processDirectories(m, output_dir, &root_progress_node); + try processDirectories(io, m, output_dir, &root_progress_node); } } if (args.len == 0) - _ = try generateServices(allocator, ";", std.fs.File.stdin(), stdout); + _ = try generateServices(allocator, io, ";", std.fs.File.stdin(), stdout); if (verbose) { const output_path = try output_dir.realpathAlloc(allocator, "."); @@ -110,7 +114,7 @@ const OutputManifest = struct { model_dir_hash_digest: [Hasher.hex_multihash_len]u8, output_dir_hash_digest: [Hasher.hex_multihash_len]u8, }; -fn processDirectories(models_dir: std.fs.Dir, output_dir: std.fs.Dir, parent_progress: *const std.Progress.Node) !void { +fn processDirectories(io: std.Io, models_dir: std.fs.Dir, output_dir: std.fs.Dir, parent_progress: *const std.Progress.Node) !void { // Let's get ready to hash!! var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); @@ -154,7 +158,7 @@ fn processDirectories(models_dir: std.fs.Dir, output_dir: std.fs.Dir, parent_pro while (try mi.next()) |e| { if ((e.kind == .file or e.kind == .sym_link) and std.mem.endsWith(u8, e.name, ".json")) { - try processFile(e.name, output_dir, &manifest.interface); + try processFile(io, e.name, output_dir, &manifest.interface); generating_models_progress.completeOne(); } } @@ -210,7 +214,7 @@ fn calculateDigests(models_dir: std.fs.Dir, output_dir: std.fs.Dir, thread_pool: }, }; } -fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: *std.Io.Writer) !void { +fn processFile(io: std.Io, file_name: []const u8, output_dir: std.fs.Dir, manifest: *std.Io.Writer) !void { // It's probably best to create our own allocator here so we can deint at the end and // toss all allocations related to the services in this file // I can't guarantee we're not leaking something, and at the end of the @@ -237,6 +241,7 @@ fn processFile(file_name: []const u8, output_dir: std.fs.Dir, manifest: *std.Io. const service_names = generateServicesForFilePath( allocator, + io, ";", file_name, writer, @@ -288,13 +293,14 @@ fn zigFmt(allocator: std.mem.Allocator, buffer: [:0]const u8) ![]const u8 { fn generateServicesForFilePath( allocator: std.mem.Allocator, + io: std.Io, comptime terminator: []const u8, path: []const u8, writer: *std.Io.Writer, ) ![][]const u8 { const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); - return try generateServices(allocator, terminator, file, writer); + return try generateServices(allocator, io, terminator, file, writer); } fn addReference(id: []const u8, map: *std.StringHashMap(u64)) !void { @@ -396,12 +402,13 @@ fn countReferences( fn generateServices( allocator: std.mem.Allocator, + io: std.Io, comptime _: []const u8, file: std.fs.File, writer: *std.Io.Writer, ) ![][]const u8 { var fbuf: [1024]u8 = undefined; - var freader = file.reader(&fbuf); + var freader = file.reader(io, &fbuf); var reader = &freader.interface; const json = try reader.allocRemaining(allocator, .limited(1024 * 1024 * 1024)); defer allocator.free(json); diff --git a/lib/date/build.zig.zon b/lib/date/build.zig.zon index 1a01e35..7aca8e6 100644 --- a/lib/date/build.zig.zon +++ b/lib/date/build.zig.zon @@ -5,8 +5,8 @@ .minimum_zig_version = "0.14.0", .dependencies = .{ .zeit = .{ - .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#ed2ca60db118414bda2b12df2039e33bad3b0b88", - .hash = "zeit-0.6.0-5I6bk0J9AgCVa0nnyL0lNY9Xa9F68hHq-ZarhuXNV-Jb", + .url = "git+https://github.com/elerch/zeit#8190461dc1f892f6370fa9d5cd76690aac0e1c71", + .hash = "zeit-0.6.0-5I6bk99-AgDNMIDuw2Zcoe_9QYIpzwZJqeqMpU54egTd", }, .json = .{ .path = "../json", diff --git a/lib/date/src/parsing.zig b/lib/date/src/parsing.zig index 4713495..53afccd 100644 --- a/lib/date/src/parsing.zig +++ b/lib/date/src/parsing.zig @@ -83,8 +83,10 @@ fn printDateTime(dt: DateTime) void { }); } -pub fn printNowUtc() void { - printDateTime(timestampToDateTime(std.time.timestamp())); +pub fn printNowUtc(io: std.Io) void { + const now = std.Io.Clock.Timestamp.now(io, .awake) catch return; + const timestamp = @as(i64, @intCast(@divFloor(now.raw.nanoseconds, std.time.ns_per_s))); + printDateTime(timestampToDateTime(timestamp)); } test "Convert timestamp to datetime" { diff --git a/src/aws.zig b/src/aws.zig index 9b332f6..20ac4f8 100644 --- a/src/aws.zig +++ b/src/aws.zig @@ -113,6 +113,7 @@ pub const Services = servicemodel.Services; pub const ClientOptions = struct { proxy: ?std.http.Client.Proxy = null, + io: std.Io, }; pub const Client = struct { allocator: std.mem.Allocator, @@ -123,7 +124,7 @@ pub const Client = struct { pub fn init(allocator: std.mem.Allocator, options: ClientOptions) Self { return Self{ .allocator = allocator, - .aws_http = awshttp.AwsHttp.init(allocator, options.proxy), + .aws_http = awshttp.AwsHttp.init(allocator, options.io, options.proxy), }; } pub fn deinit(self: *Client) void { diff --git a/src/aws_credentials.zig b/src/aws_credentials.zig index fb1f7ae..5cdc9b9 100644 --- a/src/aws_credentials.zig +++ b/src/aws_credentials.zig @@ -77,7 +77,7 @@ pub const Options = struct { pub var static_credentials: ?auth.Credentials = null; -pub fn getCredentials(allocator: std.mem.Allocator, options: Options) !auth.Credentials { +pub fn getCredentials(allocator: std.mem.Allocator, io: std.Io, options: Options) !auth.Credentials { if (static_credentials) |c| return c; if (try getEnvironmentCredentials(allocator)) |cred| { log.debug("Found credentials in environment. Access key: {s}", .{cred.access_key}); @@ -87,11 +87,11 @@ pub fn getCredentials(allocator: std.mem.Allocator, options: Options) !auth.Cred // GetWebIdentity is not currently implemented. The rest are tested and gtg // Note: Lambda just sets environment variables if (try getWebIdentityToken(allocator)) |cred| return cred; - if (try getProfileCredentials(allocator, options.profile)) |cred| return cred; + if (try getProfileCredentials(allocator, io, options.profile)) |cred| return cred; - if (try getContainerCredentials(allocator)) |cred| return cred; + if (try getContainerCredentials(allocator, io)) |cred| return cred; // I don't think we need v1 at all? - if (try getImdsv2Credentials(allocator)) |cred| return cred; + if (try getImdsv2Credentials(allocator, io)) |cred| return cred; return error.CredentialsNotFound; } @@ -125,7 +125,7 @@ fn getWebIdentityToken(allocator: std.mem.Allocator) !?auth.Credentials { // TODO: implement return null; } -fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials { +fn getContainerCredentials(allocator: std.mem.Allocator, io: std.Io) !?auth.Credentials { // A note on testing: The best way I have found to test this process is // the following. Setup an ECS Fargate cluster and create a task definition // with the command ["/bin/bash","-c","while true; do sleep 10; done"]. @@ -171,7 +171,7 @@ fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials { const container_uri = try std.fmt.allocPrint(allocator, "http://169.254.170.2{s}", .{container_relative_uri}); defer allocator.free(container_uri); - var cl = std.http.Client{ .allocator = allocator }; + var cl = std.http.Client{ .allocator = allocator, .io = io }; defer cl.deinit(); // I don't belive connection pooling would help much here as it's non-ssl and local var aw: std.Io.Writer.Allocating = .init(allocator); defer aw.deinit(); @@ -218,10 +218,10 @@ fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials { ); } -fn getImdsv2Credentials(allocator: std.mem.Allocator) !?auth.Credentials { +fn getImdsv2Credentials(allocator: std.mem.Allocator, io: std.Io) !?auth.Credentials { var token: ?[]u8 = null; defer if (token) |t| allocator.free(t); - var cl = std.http.Client{ .allocator = allocator }; + var cl = std.http.Client{ .allocator = allocator, .io = io }; defer cl.deinit(); // I don't belive connection pooling would help much here as it's non-ssl and local // Get token { @@ -383,7 +383,7 @@ fn getImdsCredentials(allocator: std.mem.Allocator, client: *std.http.Client, ro } -fn getProfileCredentials(allocator: std.mem.Allocator, options: Profile) !?auth.Credentials { +fn getProfileCredentials(allocator: std.mem.Allocator, io: std.Io, options: Profile) !?auth.Credentials { var default_path: ?[]const u8 = null; defer if (default_path) |p| allocator.free(p); @@ -416,13 +416,13 @@ fn getProfileCredentials(allocator: std.mem.Allocator, options: Profile) !?auth. defer if (credentials_file) |f| f.close(); // It's much more likely that we'll find credentials in the credentials file // so we'll try that first - const creds_file_creds = try credsForFile(allocator, credentials_file, profile); + const creds_file_creds = try credsForFile(allocator, io, credentials_file, profile); var conf_file_creds = PartialCredentials{}; if (creds_file_creds.access_key == null or creds_file_creds.secret_key == null) { log.debug("Checking config file: {s}", .{config_file_path.evaluated_path}); const config_file = std.fs.openFileAbsolute(creds_file_path.evaluated_path, .{}) catch null; defer if (config_file) |f| f.close(); - conf_file_creds = try credsForFile(allocator, config_file, profile); + conf_file_creds = try credsForFile(allocator, io, config_file, profile); } const access_key = keyFrom(allocator, creds_file_creds.access_key, conf_file_creds.access_key); const secret_key = keyFrom(allocator, creds_file_creds.secret_key, conf_file_creds.secret_key); @@ -461,10 +461,10 @@ const PartialCredentials = struct { access_key: ?[]const u8 = null, secret_key: ?[]const u8 = null, }; -fn credsForFile(allocator: std.mem.Allocator, file: ?std.fs.File, profile: []const u8) !PartialCredentials { +fn credsForFile(allocator: std.mem.Allocator, io: std.Io, file: ?std.fs.File, profile: []const u8) !PartialCredentials { if (file == null) return PartialCredentials{}; var fbuf: [1024]u8 = undefined; - var freader = file.?.reader(&fbuf); + var freader = file.?.reader(io, &fbuf); var reader = &freader.interface; const text = try reader.allocRemaining(allocator, .unlimited); defer allocator.free(text); @@ -629,7 +629,7 @@ fn getHomeDir(allocator: std.mem.Allocator) ![]const u8 { else => return error.HomeDirUnavailable, }; }, - .macos, .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => { + .macos, .linux, .freebsd, .netbsd, .dragonfly, .openbsd, .illumos => { const home_dir = std.posix.getenv("HOME") orelse { // TODO look in /etc/passwd return error.HomeDirUnavailable; diff --git a/src/aws_http.zig b/src/aws_http.zig index bf14d1d..d50555b 100644 --- a/src/aws_http.zig +++ b/src/aws_http.zig @@ -144,13 +144,15 @@ const EndPoint = struct { pub const AwsHttp = struct { allocator: std.mem.Allocator, proxy: ?std.http.Client.Proxy, + io: std.Io, const Self = @This(); - pub fn init(allocator: std.mem.Allocator, proxy: ?std.http.Client.Proxy) Self { + pub fn init(allocator: std.mem.Allocator, io: std.Io, proxy: ?std.http.Client.Proxy) Self { return Self{ .allocator = allocator, .proxy = proxy, + .io = io, // .credentialsProvider = // creds provider could be useful }; } @@ -186,7 +188,7 @@ pub const AwsHttp = struct { defer endpoint.deinit(); log.debug("Calling endpoint {s}", .{endpoint.uri}); // TODO: Should we allow customization here? - const creds = try credentials.getCredentials(self.allocator, .{}); + const creds = try credentials.getCredentials(self.allocator, self.io, .{}); defer creds.deinit(); const signing_config: signing.Config = .{ .region = getRegion(service, options.region), @@ -241,7 +243,7 @@ pub const AwsHttp = struct { defer if (len) |l| self.allocator.free(l); request_cp.headers = request_headers.items; - if (signing_config) |opts| request_cp = try signing.signRequest(self.allocator, request_cp, opts); + if (signing_config) |opts| request_cp = try signing.signRequest(self.allocator, self.io, request_cp, opts); defer { if (signing_config) |opts| { signing.freeSignedRequest(self.allocator, &request_cp, opts); @@ -261,7 +263,7 @@ pub const AwsHttp = struct { defer self.allocator.free(url); log.debug("Request url: {s}", .{url}); // TODO: Fix this proxy stuff. This is all a kludge just to compile, but std.http.Client has it all built in now - var cl = std.http.Client{ .allocator = self.allocator, .https_proxy = if (self.proxy) |*p| @constCast(p) else null }; + var cl = std.http.Client{ .allocator = self.allocator, .io = self.io, .https_proxy = if (self.proxy) |*p| @constCast(p) else null }; defer cl.deinit(); // TODO: Connection pooling const method = std.meta.stringToEnum(std.http.Method, request_cp.method).?; diff --git a/src/aws_signing.zig b/src/aws_signing.zig index fa64788..f54ab9f 100644 --- a/src/aws_signing.zig +++ b/src/aws_signing.zig @@ -157,7 +157,7 @@ pub const SigningError = error{ XAmzExpiresHeaderInRequest, /// Used if the request headers already includes x-amz-region-set XAmzRegionSetHeaderInRequest, -} || error{OutOfMemory}; +} || error{OutOfMemory} || std.Io.Clock.Error; const forbidden_headers = .{ .{ .name = "x-amz-content-sha256", .err = SigningError.XAmzContentSha256HeaderInRequest }, @@ -185,7 +185,7 @@ const skipped_headers = .{ /// Signs a request. Only header signing is currently supported. Note that /// This adds two headers to the request, which will need to be freed by the /// caller. Use freeSignedRequest with the same parameters to free -pub fn signRequest(allocator: std.mem.Allocator, request: base.Request, config: Config) SigningError!base.Request { +pub fn signRequest(allocator: std.mem.Allocator, io: std.Io, request: base.Request, config: Config) SigningError!base.Request { try validateConfig(config); for (request.headers) |h| { inline for (forbidden_headers) |f| { @@ -195,7 +195,10 @@ pub fn signRequest(allocator: std.mem.Allocator, request: base.Request, config: } var rc = request; - const signing_time = config.signing_time orelse std.time.timestamp(); + const signing_time = config.signing_time orelse blk: { + const now = try std.Io.Clock.Timestamp.now(io, .awake); + break :blk @as(i64, @intCast(@divFloor(now.raw.nanoseconds, std.time.ns_per_s))); + }; const signed_date = date.timestampToDateTime(signing_time); @@ -352,10 +355,10 @@ pub fn freeSignedRequest(allocator: std.mem.Allocator, request: *base.Request, c pub const credentialsFn = *const fn ([]const u8) ?Credentials; -pub fn verifyServerRequest(allocator: std.mem.Allocator, request: *std.http.Server.Request, request_body_reader: *std.Io.Reader, credentials_fn: credentialsFn) !bool { +pub fn verifyServerRequest(allocator: std.mem.Allocator, io: std.Io, request: *std.http.Server.Request, request_body_reader: *std.Io.Reader, credentials_fn: credentialsFn) !bool { var unverified_request = try UnverifiedRequest.init(allocator, request); defer unverified_request.deinit(); - return verify(allocator, unverified_request, request_body_reader, credentials_fn); + return verify(allocator, io, unverified_request, request_body_reader, credentials_fn); } pub const UnverifiedRequest = struct { @@ -393,7 +396,7 @@ pub const UnverifiedRequest = struct { } }; -pub fn verify(allocator: std.mem.Allocator, request: UnverifiedRequest, request_body_reader: *std.Io.Reader, credentials_fn: credentialsFn) !bool { +pub fn verify(allocator: std.mem.Allocator, io: std.Io, request: UnverifiedRequest, request_body_reader: *std.Io.Reader, credentials_fn: credentialsFn) !bool { var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); const aa = arena.allocator(); @@ -425,6 +428,7 @@ pub fn verify(allocator: std.mem.Allocator, request: UnverifiedRequest, request_ if (signature == null) return error.AuthorizationHeaderMissingSignature; return verifyParsedAuthorization( aa, + io, request, credential.?, signed_headers.?, @@ -436,6 +440,7 @@ pub fn verify(allocator: std.mem.Allocator, request: UnverifiedRequest, request_ fn verifyParsedAuthorization( allocator: std.mem.Allocator, + io: std.Io, request: UnverifiedRequest, credential: []const u8, signed_headers: []const u8, @@ -502,7 +507,7 @@ fn verifyParsedAuthorization( signed_request.query = request.target[signed_request.path.len..]; // TODO: should this be +1? query here would include '?' signed_request.body = try request_body_reader.allocRemaining(allocator, .unlimited); defer allocator.free(signed_request.body); - signed_request = try signRequest(allocator, signed_request, config); + signed_request = try signRequest(allocator, io, signed_request, config); defer freeSignedRequest(allocator, &signed_request, config); return verifySignedRequest(signed_request, signature); } @@ -1100,6 +1105,9 @@ test "can sign" { // [debug] (awshttp): Content-Length: 43 const allocator = std.testing.allocator; + var threaded: std.Io.Threaded = .init(allocator); + defer threaded.deinit(); + const io = threaded.io(); var headers = try std.ArrayList(std.http.Header).initCapacity(allocator, 5); defer headers.deinit(allocator); try headers.append(allocator, .{ .name = "Content-Type", .value = "application/x-www-form-urlencoded; charset=utf-8" }); @@ -1131,7 +1139,7 @@ test "can sign" { .signing_time = 1440938160, // 20150830T123600Z }; // TODO: There is an x-amz-content-sha256. Investigate - var signed_req = try signRequest(allocator, req, config); + var signed_req = try signRequest(allocator, io, req, config); defer freeSignedRequest(allocator, &signed_req, config); try std.testing.expectEqualStrings("X-Amz-Date", signed_req.headers[signed_req.headers.len - 3].name); @@ -1151,6 +1159,9 @@ test "can sign" { var test_credential: ?Credentials = null; test "can verify server request" { const allocator = std.testing.allocator; + var threaded: std.Io.Threaded = .init(allocator); + defer threaded.deinit(); + const io = threaded.io(); const access_key = try allocator.dupe(u8, "ACCESS"); const secret_key = try allocator.dupe(u8, "SECRET"); @@ -1191,7 +1202,7 @@ test "can verify server request" { // const old_level = std.testing.log_level; // std.testing.log_level = .debug; // defer std.testing.log_level = old_level; - try std.testing.expect(try verifyServerRequest(allocator, &request, &body_reader, struct { + try std.testing.expect(try verifyServerRequest(allocator, io, &request, &body_reader, struct { cred: Credentials, const Self = @This(); @@ -1203,6 +1214,9 @@ test "can verify server request" { } test "can verify server request without x-amz-content-sha256" { const allocator = std.testing.allocator; + var threaded: std.Io.Threaded = .init(allocator); + defer threaded.deinit(); + const io = threaded.io(); const access_key = try allocator.dupe(u8, "ACCESS"); const secret_key = try allocator.dupe(u8, "SECRET"); @@ -1293,7 +1307,7 @@ test "can verify server request without x-amz-content-sha256" { } { // verification - try std.testing.expect(try verifyServerRequest(allocator, &request, &body_reader, struct { + try std.testing.expect(try verifyServerRequest(allocator, io, &request, &body_reader, struct { cred: Credentials, const Self = @This(); diff --git a/src/aws_test.zig b/src/aws_test.zig index 7ea3e5d..1d6a869 100644 --- a/src/aws_test.zig +++ b/src/aws_test.zig @@ -254,6 +254,8 @@ const TestOptions = struct { }; const TestSetup = struct { allocator: std.mem.Allocator, + threaded: std.Io.Threaded, + io: std.Io, options: TestOptions, creds: aws_auth.Credentials, client: aws.Client, @@ -306,6 +308,7 @@ const TestSetup = struct { allocator.free(self.trace); allocator.free(self.request_uri); allocator.destroy(self.request.reader.in); + allocator.destroy(self.request.client); allocator.destroy(self.request); } }; @@ -346,9 +349,19 @@ const TestSetup = struct { const reader = try self.allocator.create(std.Io.Reader); errdefer self.allocator.destroy(reader); reader.* = .fixed(self.options.server_response); + // Create a minimal mock client that only provides io for deinit + // By creating it with the allocator, we leave critical fields like + // connection_pool as undefined, which will fail spectacularly if + // a real request were to be attempted + const mock_client = try self.allocator.create(std.http.Client); + errdefer self.allocator.destroy(mock_client); + mock_client.* = .{ + .allocator = self.allocator, + .io = self.io, + }; req.* = .{ .uri = uri, - .client = undefined, + .client = mock_client, .connection = options.connection, .reader = .{ .in = reader, @@ -433,7 +446,9 @@ const TestSetup = struct { return self.request_actuals.?.request.reader.in; } fn init(options: TestOptions) !*Self { - const client = aws.Client.init(options.allocator, .{}); + var threaded: std.Io.Threaded = .init(options.allocator); + const io = threaded.io(); + const client = aws.Client.init(options.allocator, .{ .io = io }); const call_options = try options.allocator.create(aws.Options); const self = try options.allocator.create(Self); call_options.* = .{ @@ -453,6 +468,8 @@ const TestSetup = struct { self.* = .{ .options = options, .allocator = options.allocator, + .threaded = threaded, + .io = io, .creds = aws_auth.Credentials.init( options.allocator, try options.allocator.dupe(u8, "ACCESS"), @@ -476,6 +493,7 @@ const TestSetup = struct { } self.allocator.destroy(self.call_options); self.call_options = undefined; + self.threaded.deinit(); self.allocator.destroy(self); aws_creds.static_credentials = null; } @@ -1308,6 +1326,7 @@ test "jsonStringify nullable object" { test "works against a live server" { const Server = struct { allocator: std.mem.Allocator, + io: std.Io, ready: std.Thread.Semaphore = .{}, requests_received: usize = 0, thread: ?std.Thread = null, @@ -1339,7 +1358,7 @@ test "works against a live server" { pub fn stop(self: *Server) !void { if (self.thread == null) return; // thread not started, nothing to do // post stop message - var client = std.http.Client{ .allocator = self.allocator }; + var client = std.http.Client{ .allocator = self.allocator, .io = self.io }; _ = try client.fetch(.{ // we ignore return because that should just shut down .method = .POST, .payload = "quit", @@ -1350,10 +1369,10 @@ test "works against a live server" { } fn threadMain(self: *Server) !void { - const address = try std.net.Address.parseIp("127.0.0.1", 0); - var server = try address.listen(.{}); - defer server.deinit(); - const server_port = server.listen_address.in.getPort(); + const address = try std.Io.net.IpAddress.parseLiteral("127.0.0.1:0"); + var server = try address.listen(self.io, .{}); + defer server.deinit(self.io); + const server_port = server.socket.address.getPort(); self.listening_uri = try std.fmt.allocPrint(self.allocator, "http://127.0.0.1:{d}", .{server_port}); defer { self.allocator.free(self.listening_uri); @@ -1361,13 +1380,13 @@ test "works against a live server" { } self.ready.post(); while (true) { - var connection = try server.accept(); - defer connection.stream.close(); + var connection = try server.accept(self.io); + defer connection.close(self.io); var recv_buffer: [4000]u8 = undefined; var send_buffer: [4000]u8 = undefined; - var conn_reader = connection.stream.reader(&recv_buffer); - var conn_writer = connection.stream.writer(&send_buffer); - var http_server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); + var conn_reader = connection.reader(self.io, &recv_buffer); + var conn_writer = connection.writer(self.io, &send_buffer); + var http_server = std.http.Server.init(&conn_reader.interface, &conn_writer.interface); while (http_server.reader.state == .ready) { var req = try http_server.receiveHead(); if (req.head.content_length) |l| { @@ -1392,7 +1411,10 @@ test "works against a live server" { } }; const allocator = std.testing.allocator; - var server = Server{ .allocator = allocator }; + var threaded: std.Io.Threaded = .init(allocator); + defer threaded.deinit(); + const io = threaded.io(); + var server = Server{ .allocator = allocator, .io = io }; try server.start(); var stopped = false; defer if (!stopped) server.stop() catch log.err("error stopping server", .{}); @@ -1412,7 +1434,7 @@ test "works against a live server" { // } const sts = (Services(.{.sts}){}).sts; - const client = aws.Client.init(std.testing.allocator, .{}); + const client = aws.Client.init(std.testing.allocator, .{ .io = io }); const creds = aws_auth.Credentials.init( allocator, try allocator.dupe(u8, "ACCESS"), diff --git a/src/main.zig b/src/main.zig index 8adc49b..fe6d2a6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -111,7 +111,10 @@ pub fn main() anyerror!void { } std.log.info("Start\n", .{}); - const client_options = aws.ClientOptions{ .proxy = proxy }; + var threaded: std.Io.Threaded = .init(allocator); + defer threaded.deinit(); + const io = threaded.io(); + const client_options = aws.ClientOptions{ .proxy = proxy, .io = io }; var client = aws.Client.init(allocator, client_options); const options = aws.Options{ .region = "us-west-2", @@ -373,7 +376,8 @@ fn proxyFromString(string: []const u8) !std.http.Client.Proxy { rc.protocol = .tls; } else return error.InvalidScheme; var split_iterator = std.mem.splitScalar(u8, remaining, ':'); - rc.host = std.mem.trimRight(u8, split_iterator.first(), "/"); + const host_str = std.mem.trimRight(u8, split_iterator.first(), "/"); + rc.host = try std.Io.net.HostName.init(host_str); if (split_iterator.next()) |port| rc.port = try std.fmt.parseInt(u16, port, 10); return rc;