diff --git a/README.md b/README.md index 3d90ce3..ee4dc6f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://drone.lerch.org/api/badges/lobo/aws-sdk-for-zig/status.svg?ref=refs/heads/master)](https://drone.lerch.org/api/badges/lobo/aws-sdk-for-zig/) -This SDK currently supports all AWS services except S3. See TODO list below. +This SDK currently supports all AWS services. S3 has basic support Current executable size for the demo is 1.7M (90k of which is the AWS PEM file, and approximately 600K for XML services) after compiling with -Drelease-safe and @@ -42,15 +42,13 @@ for posterity, and supports x86_64 linux. The old branch is deprecated. ## Limitations -There are many nuances of AWS V4 signature calculation. S3 is not supported -because it uses many of these edge cases. Also endpoint calculation is special -for S3. WebIdentityToken is not yet implemented. +There are many nuances of AWS V4 signature calculation, and not all edge cases +of S3 are handled. WebIdentityToken is not yet implemented. TODO List: -* Implement initial S3 support. This involves: - * Implementation of AWS SigV4 signature calculation for S3, which is unique - * Implementation of S3 endpoint calculation, which is also unique to this service +* Implement more robust S3 support. Keys with slashes in the name are currently + causing a SignatureDoesNotMatch error * Bump to zig 0.9.1. iguanaTLS, used in zFetch is still [working out 0.9.1 issues](https://github.com/alexnask/iguanaTLS/pull/29) * Implement sigv4a signing * Implement jitter/exponential backoff diff --git a/src/main.zig b/src/main.zig index 22ecdc9..2a1ab0c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -53,7 +53,7 @@ const Tests = enum { rest_json_1_work_with_lambda, rest_xml_no_input, rest_xml_anything_but_s3, - // rest_xml_work_with_s3, + rest_xml_work_with_s3, }; pub fn main() anyerror!void { @@ -234,6 +234,77 @@ pub fn main() anyerror!void { std.log.info("key group list max: {d}", .{list.max_items}); std.log.info("key group quantity: {d}", .{list.quantity}); }, + .rest_xml_work_with_s3 => { + // TODO: Fix signature calculation mismatch with slashes + // const key = "i/am/a/teapot/foo"; + const key = "foo"; + + const bucket = blk: { + const result = try client.call(services.s3.list_buckets.Request{}, options); + defer result.deinit(); + const bucket = result.response.buckets.?[result.response.buckets.?.len - 1]; + std.log.info("ListBuckets request id: {s}", .{result.response_metadata.request_id}); + std.log.info("bucket name: {s}", .{bucket.name.?}); + break :blk try allocator.dupe(u8, bucket.name.?); + }; + defer allocator.free(bucket); + const location = blk: { + const result = try aws.Request(services.s3.get_bucket_location).call(.{ + .bucket = bucket, + }, options); + defer result.deinit(); + const location = result.response.location_constraint.?; + std.log.info("GetBucketLocation request id: {s}", .{result.response_metadata.request_id}); + std.log.info("location: {s}", .{location}); + break :blk try allocator.dupe(u8, location); + }; + defer allocator.free(location); + const s3opts = aws.Options{ + .region = location, + .client = client, + }; + { + const result = try aws.Request(services.s3.put_object).call(.{ + .bucket = bucket, + .key = key, + .content_type = "text/plain", + .body = "bar", + .storage_class = "STANDARD", + }, s3opts); + std.log.info("PutObject Request id: {s}", .{result.response_metadata.request_id}); + std.log.info("PutObject etag: {s}", .{result.response.e_tag.?}); + defer result.deinit(); + } + { + // Note that boto appears to redirect by default, but java + // does not. We will not + const result = try aws.Request(services.s3.get_object).call(.{ + .bucket = bucket, + .key = key, + }, s3opts); + std.log.info("GetObject Request id: {s}", .{result.response_metadata.request_id}); + std.log.info("GetObject Body: {s}", .{result.response.body}); + std.log.info("GetObject etag: {s}", .{result.response.e_tag.?}); + std.log.info("GetObject last modified (seconds since epoch): {d}", .{result.response.last_modified.?}); + defer result.deinit(); + } + { + const result = try aws.Request(services.s3.delete_object).call(.{ + .bucket = bucket, + .key = key, + }, s3opts); + std.log.info("DeleteObject Request id: {s}", .{result.response_metadata.request_id}); + defer result.deinit(); + } + { + const result = try aws.Request(services.s3.list_objects).call(.{ + .bucket = bucket, + }, s3opts); + std.log.info("ListObject Request id: {s}", .{result.response_metadata.request_id}); + std.log.info("Object count: {d}", .{result.response.contents.?.len}); + defer result.deinit(); + } + }, } std.log.info("===== End Test: {s} =====\n", .{@tagName(t)}); }