Compare commits

...

2 Commits

Author SHA1 Message Date
e5b662873a
omit most automatically added headers
All checks were successful
AWS-Zig Build / build-zig-amd64-host (push) Successful in 1m28s
2024-08-19 09:42:00 -07:00
a9f99c0205
add failing test for duplicate header values 2024-08-19 09:41:39 -07:00
2 changed files with 58 additions and 1 deletions

View File

@ -1380,6 +1380,49 @@ const TestOptions = struct {
const Self = @This(); const Self = @This();
/// Builtin hashmap for strings as keys.
/// Key memory is managed by the caller. Keys and values
/// will not automatically be freed.
pub fn StringCaseInsensitiveHashMap(comptime V: type) type {
return std.HashMap([]const u8, V, StringInsensitiveContext, std.hash_map.default_max_load_percentage);
}
pub const StringInsensitiveContext = struct {
pub fn hash(self: @This(), s: []const u8) u64 {
_ = self;
return hashString(s);
}
pub fn eql(self: @This(), a: []const u8, b: []const u8) bool {
_ = self;
return eqlString(a, b);
}
};
pub fn eqlString(a: []const u8, b: []const u8) bool {
return std.ascii.eqlIgnoreCase(a, b);
}
pub fn hashString(s: []const u8) u64 {
var buf: [1024]u8 = undefined;
if (s.len > buf.len) unreachable; // tolower has a debug assert, but we want non-debug check too
const lower_s = std.ascii.lowerString(buf[0..], s);
return std.hash.Wyhash.hash(0, lower_s);
}
fn expectNoDuplicateHeaders(self: *Self) !void {
// As header keys are
var hm = StringCaseInsensitiveHashMap(void).init(self.allocator);
try hm.ensureTotalCapacity(@intCast(self.request_headers.len));
defer hm.deinit();
for (self.request_headers) |h| {
if (hm.getKey(h.name)) |_| {
log.err("Duplicate key detected. Key name: {s}", .{h.name});
return error.duplicateKeyDetected;
}
try hm.put(h.name, {});
}
}
fn expectHeader(self: *Self, name: []const u8, value: []const u8) !void { fn expectHeader(self: *Self, name: []const u8, value: []const u8) !void {
for (self.request_headers) |h| for (self.request_headers) |h|
if (std.ascii.eqlIgnoreCase(name, h.name) and if (std.ascii.eqlIgnoreCase(name, h.name) and
@ -1992,6 +2035,9 @@ test "rest_xml_anything_but_s3: CloudFront list key groups" {
try std.testing.expectEqual(@as(i64, 100), call.response.key_group_list.?.max_items); try std.testing.expectEqual(@as(i64, 100), call.response.key_group_list.?.max_items);
} }
test "rest_xml_with_input: S3 put object" { test "rest_xml_with_input: S3 put object" {
// const old = std.testing.log_level;
// defer std.testing.log_level = old;
// std.testing.log_level = .debug;
const allocator = std.testing.allocator; const allocator = std.testing.allocator;
var test_harness = TestSetup.init(.{ var test_harness = TestSetup.init(.{
.allocator = allocator, .allocator = allocator,
@ -2018,13 +2064,14 @@ test "rest_xml_with_input: S3 put object" {
.body = "bar", .body = "bar",
.storage_class = "STANDARD", .storage_class = "STANDARD",
}, s3opts); }, s3opts);
defer result.deinit();
for (test_harness.request_options.request_headers) |header| { for (test_harness.request_options.request_headers) |header| {
std.log.info("Request header: {s}: {s}", .{ header.name, header.value }); std.log.info("Request header: {s}: {s}", .{ header.name, header.value });
} }
try test_harness.request_options.expectNoDuplicateHeaders();
std.log.info("PutObject Request id: {s}", .{result.response_metadata.request_id}); std.log.info("PutObject Request id: {s}", .{result.response_metadata.request_id});
std.log.info("PutObject etag: {s}", .{result.response.e_tag.?}); std.log.info("PutObject etag: {s}", .{result.response.e_tag.?});
//mysfitszj3t6webstack-hostingbucketa91a61fe-1ep3ezkgwpxr0.s3.us-west-2.amazonaws.com //mysfitszj3t6webstack-hostingbucketa91a61fe-1ep3ezkgwpxr0.s3.us-west-2.amazonaws.com
defer result.deinit();
test_harness.stop(); test_harness.stop();
// Request expectations // Request expectations
try std.testing.expectEqual(std.http.Method.PUT, test_harness.request_options.request_method); try std.testing.expectEqual(std.http.Method.PUT, test_harness.request_options.request_method);

View File

@ -190,6 +190,16 @@ pub const AwsHttp = struct {
.response_storage = .{ .dynamic = &resp_payload }, .response_storage = .{ .dynamic = &resp_payload },
.raw_uri = true, .raw_uri = true,
.location = .{ .url = url }, .location = .{ .url = url },
// we need full control over most headers. I wish libraries would do a
// better job of having default headers as an opt-in...
.headers = .{
.host = .omit,
.authorization = .omit,
.user_agent = .omit,
.connection = .default, // we can let the client manage this...it has no impact to us
.accept_encoding = .default, // accept encoding (gzip, deflate) *should* be ok
.content_type = .omit,
},
.extra_headers = headers.items, .extra_headers = headers.items,
}); });
// TODO: Need to test for payloads > 2^14. I believe one of our tests does this, but not sure // TODO: Need to test for payloads > 2^14. I believe one of our tests does this, but not sure