zig build compiles using zig 0.15.1
Some checks failed
AWS-Zig Build / build-zig-amd64-host (push) Failing after 2m27s
Some checks failed
AWS-Zig Build / build-zig-amd64-host (push) Failing after 2m27s
This commit is contained in:
parent
5334cc3bfe
commit
8d399cb8a6
14 changed files with 336 additions and 297 deletions
|
@ -17,10 +17,10 @@ pub const Timestamp = enum(zeit.Nanoseconds) {
|
||||||
}) catch std.debug.panic("Failed to parse timestamp to instant: {d}", .{value});
|
}) catch std.debug.panic("Failed to parse timestamp to instant: {d}", .{value});
|
||||||
|
|
||||||
const fmt = "Mon, 02 Jan 2006 15:04:05 GMT";
|
const fmt = "Mon, 02 Jan 2006 15:04:05 GMT";
|
||||||
var buf = std.mem.zeroes([fmt.len]u8);
|
var buf: [fmt.len]u8 = undefined;
|
||||||
|
|
||||||
var fbs = std.io.fixedBufferStream(&buf);
|
var fbs = std.Io.Writer.fixed(&buf);
|
||||||
instant.time().gofmt(fbs.writer(), fmt) catch std.debug.panic("Failed to format instant: {d}", .{instant.timestamp});
|
instant.time().gofmt(&fbs, fmt) catch std.debug.panic("Failed to format instant: {d}", .{instant.timestamp});
|
||||||
|
|
||||||
try jw.write(&buf);
|
try jw.write(&buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1772,12 +1772,12 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
|
||||||
.slice => {
|
.slice => {
|
||||||
switch (token) {
|
switch (token) {
|
||||||
.ArrayBegin => {
|
.ArrayBegin => {
|
||||||
var arraylist = std.ArrayList(ptrInfo.child).init(allocator);
|
var arraylist = std.ArrayList(ptrInfo.child){};
|
||||||
errdefer {
|
errdefer {
|
||||||
while (arraylist.pop()) |v| {
|
while (arraylist.pop()) |v| {
|
||||||
parseFree(ptrInfo.child, v, options);
|
parseFree(ptrInfo.child, v, options);
|
||||||
}
|
}
|
||||||
arraylist.deinit();
|
arraylist.deinit(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -1787,11 +1787,11 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try arraylist.ensureTotalCapacity(arraylist.items.len + 1);
|
try arraylist.ensureTotalCapacity(allocator, arraylist.items.len + 1);
|
||||||
const v = try parseInternal(ptrInfo.child, tok, tokens, options);
|
const v = try parseInternal(ptrInfo.child, tok, tokens, options);
|
||||||
arraylist.appendAssumeCapacity(v);
|
arraylist.appendAssumeCapacity(v);
|
||||||
}
|
}
|
||||||
return arraylist.toOwnedSlice();
|
return arraylist.toOwnedSlice(allocator);
|
||||||
},
|
},
|
||||||
.String => |stringToken| {
|
.String => |stringToken| {
|
||||||
if (ptrInfo.child != u8) return error.UnexpectedToken;
|
if (ptrInfo.child != u8) return error.UnexpectedToken;
|
||||||
|
@ -1817,12 +1817,12 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
|
||||||
if (key_type == null) return error.UnexpectedToken;
|
if (key_type == null) return error.UnexpectedToken;
|
||||||
const value_type = typeForField(ptrInfo.child, "value");
|
const value_type = typeForField(ptrInfo.child, "value");
|
||||||
if (value_type == null) return error.UnexpectedToken;
|
if (value_type == null) return error.UnexpectedToken;
|
||||||
var arraylist = std.ArrayList(ptrInfo.child).init(allocator);
|
var arraylist = std.ArrayList(ptrInfo.child){};
|
||||||
errdefer {
|
errdefer {
|
||||||
while (arraylist.pop()) |v| {
|
while (arraylist.pop()) |v| {
|
||||||
parseFree(ptrInfo.child, v, options);
|
parseFree(ptrInfo.child, v, options);
|
||||||
}
|
}
|
||||||
arraylist.deinit();
|
arraylist.deinit(allocator);
|
||||||
}
|
}
|
||||||
while (true) {
|
while (true) {
|
||||||
const key = (try tokens.next()) orelse return error.UnexpectedEndOfJson;
|
const key = (try tokens.next()) orelse return error.UnexpectedEndOfJson;
|
||||||
|
@ -1831,13 +1831,13 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
try arraylist.ensureTotalCapacity(arraylist.items.len + 1);
|
try arraylist.ensureTotalCapacity(allocator, arraylist.items.len + 1);
|
||||||
const key_val = try parseInternal(key_type.?, key, tokens, options);
|
const key_val = try parseInternal(key_type.?, key, tokens, options);
|
||||||
const val = (try tokens.next()) orelse return error.UnexpectedEndOfJson;
|
const val = (try tokens.next()) orelse return error.UnexpectedEndOfJson;
|
||||||
const val_val = try parseInternal(value_type.?, val, tokens, options);
|
const val_val = try parseInternal(value_type.?, val, tokens, options);
|
||||||
arraylist.appendAssumeCapacity(.{ .key = key_val, .value = val_val });
|
arraylist.appendAssumeCapacity(.{ .key = key_val, .value = val_val });
|
||||||
}
|
}
|
||||||
return arraylist.toOwnedSlice();
|
return arraylist.toOwnedSlice(allocator);
|
||||||
},
|
},
|
||||||
else => return error.UnexpectedToken,
|
else => return error.UnexpectedToken,
|
||||||
}
|
}
|
||||||
|
|
222
src/aws.zig
222
src/aws.zig
|
@ -1,11 +1,11 @@
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const case = @import("case");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const zeit = @import("zeit");
|
const zeit = @import("zeit");
|
||||||
|
|
||||||
const awshttp = @import("aws_http.zig");
|
const awshttp = @import("aws_http.zig");
|
||||||
const json = @import("json");
|
const json = @import("json");
|
||||||
const url = @import("url.zig");
|
const url = @import("url.zig");
|
||||||
const case = @import("case.zig");
|
|
||||||
const date = @import("date");
|
const date = @import("date");
|
||||||
const servicemodel = @import("servicemodel.zig");
|
const servicemodel = @import("servicemodel.zig");
|
||||||
const xml_shaper = @import("xml_shaper.zig");
|
const xml_shaper = @import("xml_shaper.zig");
|
||||||
|
@ -196,8 +196,8 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
log.debug("Rest method: '{s}'", .{aws_request.method});
|
log.debug("Rest method: '{s}'", .{aws_request.method});
|
||||||
log.debug("Rest success code: '{d}'", .{Action.http_config.success_code});
|
log.debug("Rest success code: '{d}'", .{Action.http_config.success_code});
|
||||||
log.debug("Rest raw uri: '{s}'", .{Action.http_config.uri});
|
log.debug("Rest raw uri: '{s}'", .{Action.http_config.uri});
|
||||||
var al = std.ArrayList([]const u8).init(options.client.allocator);
|
var al = std.ArrayList([]const u8){};
|
||||||
defer al.deinit();
|
defer al.deinit(options.client.allocator);
|
||||||
aws_request.path = try buildPath(
|
aws_request.path = try buildPath(
|
||||||
options.client.allocator,
|
options.client.allocator,
|
||||||
Action.http_config.uri,
|
Action.http_config.uri,
|
||||||
|
@ -230,14 +230,13 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
log.debug("Rest query: '{s}'", .{aws_request.query});
|
log.debug("Rest query: '{s}'", .{aws_request.query});
|
||||||
defer options.client.allocator.free(aws_request.query);
|
defer options.client.allocator.free(aws_request.query);
|
||||||
// We don't know if we need a body...guessing here, this should cover most
|
// We don't know if we need a body...guessing here, this should cover most
|
||||||
var buffer = std.ArrayList(u8).init(options.client.allocator);
|
var buffer = std.Io.Writer.Allocating.init(options.client.allocator);
|
||||||
defer buffer.deinit();
|
defer buffer.deinit();
|
||||||
if (Self.service_meta.aws_protocol == .rest_json_1) {
|
if (Self.service_meta.aws_protocol == .rest_json_1) {
|
||||||
if (std.mem.eql(u8, "PUT", aws_request.method) or std.mem.eql(u8, "POST", aws_request.method)) {
|
if (std.mem.eql(u8, "PUT", aws_request.method) or std.mem.eql(u8, "POST", aws_request.method))
|
||||||
try std.json.stringify(request, .{ .whitespace = .indent_4 }, buffer.writer());
|
try buffer.writer.print("{f}", .{std.json.fmt(request, .{ .whitespace = .indent_4 })});
|
||||||
}
|
}
|
||||||
}
|
aws_request.body = buffer.written();
|
||||||
aws_request.body = buffer.items;
|
|
||||||
var rest_xml_body: ?[]const u8 = null;
|
var rest_xml_body: ?[]const u8 = null;
|
||||||
defer if (rest_xml_body) |b| options.client.allocator.free(b);
|
defer if (rest_xml_body) |b| options.client.allocator.free(b);
|
||||||
if (Self.service_meta.aws_protocol == .rest_xml) {
|
if (Self.service_meta.aws_protocol == .rest_xml) {
|
||||||
|
@ -315,9 +314,6 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
});
|
});
|
||||||
defer options.client.allocator.free(target);
|
defer options.client.allocator.free(target);
|
||||||
|
|
||||||
var buffer = std.ArrayList(u8).init(options.client.allocator);
|
|
||||||
defer buffer.deinit();
|
|
||||||
|
|
||||||
// The transformer needs to allocate stuff out of band, but we
|
// The transformer needs to allocate stuff out of band, but we
|
||||||
// can guarantee we don't need the memory after this call completes,
|
// can guarantee we don't need the memory after this call completes,
|
||||||
// so we'll use an arena allocator to whack everything.
|
// so we'll use an arena allocator to whack everything.
|
||||||
|
@ -326,7 +322,13 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
// smithy spec, "A null value MAY be provided or omitted
|
// smithy spec, "A null value MAY be provided or omitted
|
||||||
// for a boxed member with no observable difference." But we're
|
// for a boxed member with no observable difference." But we're
|
||||||
// seeing a lot of differences here between spec and reality
|
// seeing a lot of differences here between spec and reality
|
||||||
try std.json.stringify(request, .{ .whitespace = .indent_4 }, buffer.writer());
|
|
||||||
|
const body = try std.fmt.allocPrint(
|
||||||
|
options.client.allocator,
|
||||||
|
"{f}",
|
||||||
|
.{std.json.fmt(request, .{ .whitespace = .indent_4 })},
|
||||||
|
);
|
||||||
|
defer options.client.allocator.free(body);
|
||||||
|
|
||||||
var content_type: []const u8 = undefined;
|
var content_type: []const u8 = undefined;
|
||||||
switch (Self.service_meta.aws_protocol) {
|
switch (Self.service_meta.aws_protocol) {
|
||||||
|
@ -336,7 +338,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
}
|
}
|
||||||
return try Self.callAws(.{
|
return try Self.callAws(.{
|
||||||
.query = "",
|
.query = "",
|
||||||
.body = buffer.items,
|
.body = body,
|
||||||
.content_type = content_type,
|
.content_type = content_type,
|
||||||
.headers = @constCast(&[_]awshttp.Header{.{ .name = "X-Amz-Target", .value = target }}),
|
.headers = @constCast(&[_]awshttp.Header{.{ .name = "X-Amz-Target", .value = target }}),
|
||||||
}, options);
|
}, options);
|
||||||
|
@ -348,13 +350,13 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
// handle lists and maps properly anyway yet, so we'll go for it and see
|
// handle lists and maps properly anyway yet, so we'll go for it and see
|
||||||
// where it breaks. PRs and/or failing test cases appreciated.
|
// where it breaks. PRs and/or failing test cases appreciated.
|
||||||
fn callQuery(request: ActionRequest, options: Options) !FullResponseType {
|
fn callQuery(request: ActionRequest, options: Options) !FullResponseType {
|
||||||
var buffer = std.ArrayList(u8).init(options.client.allocator);
|
var aw: std.Io.Writer.Allocating = .init(options.client.allocator);
|
||||||
defer buffer.deinit();
|
defer aw.deinit();
|
||||||
const writer = buffer.writer();
|
const writer = &aw.writer;
|
||||||
try url.encode(options.client.allocator, request, writer, .{
|
try url.encode(options.client.allocator, request, writer, .{
|
||||||
.field_name_transformer = queryFieldTransformer,
|
.field_name_transformer = queryFieldTransformer,
|
||||||
});
|
});
|
||||||
const continuation = if (buffer.items.len > 0) "&" else "";
|
const continuation = if (aw.written().len > 0) "&" else "";
|
||||||
|
|
||||||
const query = if (Self.service_meta.aws_protocol == .query)
|
const query = if (Self.service_meta.aws_protocol == .query)
|
||||||
""
|
""
|
||||||
|
@ -376,7 +378,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
action.action_name,
|
action.action_name,
|
||||||
Self.service_meta.version.?, // Version required for the protocol, we should panic if it is not present
|
Self.service_meta.version.?, // Version required for the protocol, we should panic if it is not present
|
||||||
continuation,
|
continuation,
|
||||||
buffer.items,
|
aw.written(),
|
||||||
});
|
});
|
||||||
defer options.client.allocator.free(body);
|
defer options.client.allocator.free(body);
|
||||||
|
|
||||||
|
@ -889,9 +891,25 @@ fn parseInt(comptime T: type, val: []const u8) !T {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generalAllocPrint(allocator: Allocator, val: anytype) !?[]const u8 {
|
fn generalAllocPrint(allocator: Allocator, val: anytype) !?[]const u8 {
|
||||||
switch (@typeInfo(@TypeOf(val))) {
|
const T = @TypeOf(val);
|
||||||
|
switch (@typeInfo(T)) {
|
||||||
.optional => if (val) |v| return generalAllocPrint(allocator, v) else return null,
|
.optional => if (val) |v| return generalAllocPrint(allocator, v) else return null,
|
||||||
.array, .pointer => return try std.fmt.allocPrint(allocator, "{s}", .{val}),
|
.array, .pointer => switch (@typeInfo(T)) {
|
||||||
|
.array => return try std.fmt.allocPrint(allocator, "{s}", .{val}),
|
||||||
|
.pointer => |info| switch (info.size) {
|
||||||
|
.one => return try std.fmt.allocPrint(allocator, "{s}", .{val}),
|
||||||
|
.many => return try std.fmt.allocPrint(allocator, "{s}", .{val}),
|
||||||
|
.slice => {
|
||||||
|
log.warn(
|
||||||
|
"printing object of type [][]const u8...pretty sure this is wrong: {any}",
|
||||||
|
.{val},
|
||||||
|
);
|
||||||
|
return try std.fmt.allocPrint(allocator, "{any}", .{val});
|
||||||
|
},
|
||||||
|
.c => return try std.fmt.allocPrint(allocator, "{s}", .{val}),
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
else => return try std.fmt.allocPrint(allocator, "{any}", .{val}),
|
else => return try std.fmt.allocPrint(allocator, "{any}", .{val}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -904,6 +922,7 @@ fn headersFor(allocator: Allocator, request: anytype) ![]awshttp.Header {
|
||||||
// It would be awesome to have a fixed array, but we can't because
|
// It would be awesome to have a fixed array, but we can't because
|
||||||
// it depends on a runtime value based on whether these variables are null
|
// it depends on a runtime value based on whether these variables are null
|
||||||
var headers = try std.ArrayList(awshttp.Header).initCapacity(allocator, fields.len);
|
var headers = try std.ArrayList(awshttp.Header).initCapacity(allocator, fields.len);
|
||||||
|
defer headers.deinit(allocator);
|
||||||
inline for (fields) |f| {
|
inline for (fields) |f| {
|
||||||
// Header name = value of field
|
// Header name = value of field
|
||||||
// Header value = value of the field of the request based on field name
|
// Header value = value of the field of the request based on field name
|
||||||
|
@ -916,7 +935,7 @@ fn headersFor(allocator: Allocator, request: anytype) ![]awshttp.Header {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return headers.toOwnedSlice();
|
return headers.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn freeHeadersFor(allocator: Allocator, request: anytype, headers: []const awshttp.Header) void {
|
fn freeHeadersFor(allocator: Allocator, request: anytype, headers: []const awshttp.Header) void {
|
||||||
|
@ -1027,14 +1046,14 @@ fn ServerResponse(comptime action: anytype) type {
|
||||||
.type = T,
|
.type = T,
|
||||||
.default_value_ptr = null,
|
.default_value_ptr = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = 0,
|
.alignment = std.meta.alignment(T),
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.name = "ResponseMetadata",
|
.name = "ResponseMetadata",
|
||||||
.type = ResponseMetadata,
|
.type = ResponseMetadata,
|
||||||
.default_value_ptr = null,
|
.default_value_ptr = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = 0,
|
.alignment = std.meta.alignment(ResponseMetadata),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.decls = &[_]std.builtin.Type.Declaration{},
|
.decls = &[_]std.builtin.Type.Declaration{},
|
||||||
|
@ -1050,7 +1069,7 @@ fn ServerResponse(comptime action: anytype) type {
|
||||||
.type = Result,
|
.type = Result,
|
||||||
.default_value_ptr = null,
|
.default_value_ptr = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = 0,
|
.alignment = std.meta.alignment(Result),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.decls = &[_]std.builtin.Type.Declaration{},
|
.decls = &[_]std.builtin.Type.Declaration{},
|
||||||
|
@ -1111,7 +1130,13 @@ fn safeFree(allocator: Allocator, obj: anytype) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn queryFieldTransformer(allocator: Allocator, field_name: []const u8) anyerror![]const u8 {
|
fn queryFieldTransformer(allocator: Allocator, field_name: []const u8) anyerror![]const u8 {
|
||||||
return try case.snakeToPascal(allocator, field_name);
|
var reader = std.Io.Reader.fixed(field_name);
|
||||||
|
var aw = try std.Io.Writer.Allocating.initCapacity(allocator, 100);
|
||||||
|
defer aw.deinit();
|
||||||
|
const writer = &aw.writer;
|
||||||
|
try case.to(.pascal, &reader, writer);
|
||||||
|
return aw.toOwnedSlice();
|
||||||
|
// return try case.snakeToPascal(allocator, field_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildPath(
|
fn buildPath(
|
||||||
|
@ -1123,8 +1148,7 @@ fn buildPath(
|
||||||
replaced_fields: *std.ArrayList([]const u8),
|
replaced_fields: *std.ArrayList([]const u8),
|
||||||
) ![]const u8 {
|
) ![]const u8 {
|
||||||
var buffer = try std.ArrayList(u8).initCapacity(allocator, raw_uri.len);
|
var buffer = try std.ArrayList(u8).initCapacity(allocator, raw_uri.len);
|
||||||
// const writer = buffer.writer();
|
defer buffer.deinit(allocator);
|
||||||
defer buffer.deinit();
|
|
||||||
var in_label = false;
|
var in_label = false;
|
||||||
var start: usize = 0;
|
var start: usize = 0;
|
||||||
for (raw_uri, 0..) |c, inx| {
|
for (raw_uri, 0..) |c, inx| {
|
||||||
|
@ -1142,40 +1166,42 @@ fn buildPath(
|
||||||
const replacement_label = raw_uri[start..end];
|
const replacement_label = raw_uri[start..end];
|
||||||
inline for (std.meta.fields(ActionRequest)) |field| {
|
inline for (std.meta.fields(ActionRequest)) |field| {
|
||||||
if (std.mem.eql(u8, request.fieldNameFor(field.name), replacement_label)) {
|
if (std.mem.eql(u8, request.fieldNameFor(field.name), replacement_label)) {
|
||||||
try replaced_fields.append(replacement_label);
|
try replaced_fields.append(allocator, replacement_label);
|
||||||
var replacement_buffer = try std.ArrayList(u8).initCapacity(allocator, raw_uri.len);
|
var replacement_buffer = try std.ArrayList(u8).initCapacity(allocator, raw_uri.len);
|
||||||
defer replacement_buffer.deinit();
|
defer replacement_buffer.deinit(allocator);
|
||||||
var encoded_buffer = try std.ArrayList(u8).initCapacity(allocator, raw_uri.len);
|
|
||||||
|
var encoded_buffer = std.Io.Writer.Allocating.init(allocator);
|
||||||
defer encoded_buffer.deinit();
|
defer encoded_buffer.deinit();
|
||||||
const replacement_writer = replacement_buffer.writer();
|
|
||||||
// std.mem.replacementSize
|
try (&encoded_buffer.writer).print(
|
||||||
try std.json.stringify(
|
"{f}",
|
||||||
|
.{std.json.fmt(
|
||||||
@field(request, field.name),
|
@field(request, field.name),
|
||||||
.{ .whitespace = .indent_4 },
|
.{ .whitespace = .indent_4 },
|
||||||
replacement_writer,
|
)},
|
||||||
);
|
);
|
||||||
const trimmed_replacement_val = std.mem.trim(u8, replacement_buffer.items, "\"");
|
const trimmed_replacement_val = std.mem.trim(u8, replacement_buffer.items, "\"");
|
||||||
// NOTE: We have to encode here as it is a portion of the rest JSON protocol.
|
// NOTE: We have to encode here as it is a portion of the rest JSON protocol.
|
||||||
// This makes the encoding in the standard library wrong
|
// This makes the encoding in the standard library wrong
|
||||||
try uriEncode(trimmed_replacement_val, encoded_buffer.writer(), encode_slash);
|
try uriEncode(trimmed_replacement_val, &encoded_buffer.writer, encode_slash);
|
||||||
try buffer.appendSlice(encoded_buffer.items);
|
try buffer.appendSlice(allocator, encoded_buffer.written());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => if (!in_label) {
|
else => if (!in_label) {
|
||||||
try buffer.append(c);
|
try buffer.append(allocator, c);
|
||||||
} else {},
|
} else {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buffer.toOwnedSlice();
|
return buffer.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uriEncode(input: []const u8, writer: anytype, encode_slash: bool) !void {
|
fn uriEncode(input: []const u8, writer: *std.Io.Writer, encode_slash: bool) !void {
|
||||||
for (input) |c|
|
for (input) |c|
|
||||||
try uriEncodeByte(c, writer, encode_slash);
|
try uriEncodeByte(c, writer, encode_slash);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uriEncodeByte(char: u8, writer: anytype, encode_slash: bool) !void {
|
fn uriEncodeByte(char: u8, writer: *std.Io.Writer, encode_slash: bool) !void {
|
||||||
switch (char) {
|
switch (char) {
|
||||||
'!' => _ = try writer.write("%21"),
|
'!' => _ = try writer.write("%21"),
|
||||||
'#' => _ = try writer.write("%23"),
|
'#' => _ = try writer.write("%23"),
|
||||||
|
@ -1209,9 +1235,9 @@ fn buildQuery(allocator: Allocator, request: anytype) ![]const u8 {
|
||||||
// .function_version = "FunctionVersion",
|
// .function_version = "FunctionVersion",
|
||||||
// .marker = "Marker",
|
// .marker = "Marker",
|
||||||
// };
|
// };
|
||||||
var buffer = std.ArrayList(u8).init(allocator);
|
var buffer = std.Io.Writer.Allocating.init(allocator);
|
||||||
const writer = buffer.writer();
|
|
||||||
defer buffer.deinit();
|
defer buffer.deinit();
|
||||||
|
const writer = &buffer.writer;
|
||||||
var prefix = "?";
|
var prefix = "?";
|
||||||
if (@hasDecl(@TypeOf(request), "http_query")) {
|
if (@hasDecl(@TypeOf(request), "http_query")) {
|
||||||
const query_arguments = @field(@TypeOf(request), "http_query");
|
const query_arguments = @field(@TypeOf(request), "http_query");
|
||||||
|
@ -1224,7 +1250,7 @@ fn buildQuery(allocator: Allocator, request: anytype) ![]const u8 {
|
||||||
return buffer.toOwnedSlice();
|
return buffer.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addQueryArg(comptime ValueType: type, prefix: []const u8, key: []const u8, value: anytype, writer: anytype) !bool {
|
fn addQueryArg(comptime ValueType: type, prefix: []const u8, key: []const u8, value: anytype, writer: *std.Io.Writer) !bool {
|
||||||
switch (@typeInfo(@TypeOf(value))) {
|
switch (@typeInfo(@TypeOf(value))) {
|
||||||
.optional => {
|
.optional => {
|
||||||
if (value) |v|
|
if (value) |v|
|
||||||
|
@ -1259,69 +1285,77 @@ fn addQueryArg(comptime ValueType: type, prefix: []const u8, key: []const u8, va
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn addBasicQueryArg(prefix: []const u8, key: []const u8, value: anytype, writer: anytype) !bool {
|
fn addBasicQueryArg(prefix: []const u8, key: []const u8, value: anytype, writer: *std.Io.Writer) !bool {
|
||||||
_ = try writer.write(prefix);
|
_ = try writer.write(prefix);
|
||||||
// TODO: url escaping
|
// TODO: url escaping
|
||||||
try uriEncode(key, writer, true);
|
try uriEncode(key, writer, true);
|
||||||
_ = try writer.write("=");
|
_ = try writer.write("=");
|
||||||
var encoding_writer = uriEncodingWriter(writer);
|
var encoding_writer = UriEncodingWriter.init(writer);
|
||||||
var ignoring_writer = ignoringWriter(encoding_writer.writer(), '"');
|
var ignoring_writer = IgnoringWriter.init(&encoding_writer.writer, '"');
|
||||||
try std.json.stringify(value, .{}, ignoring_writer.writer());
|
try ignoring_writer.writer.print("{f}", .{std.json.fmt(value, .{})});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
pub fn uriEncodingWriter(child_stream: anytype) UriEncodingWriter(@TypeOf(child_stream)) {
|
|
||||||
return .{ .child_stream = child_stream };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Writer that ignores a character
|
const UriEncodingWriter = struct {
|
||||||
pub fn UriEncodingWriter(comptime WriterType: type) type {
|
child_writer: *std.Io.Writer,
|
||||||
return struct {
|
writer: std.Io.Writer,
|
||||||
child_stream: WriterType,
|
|
||||||
|
|
||||||
pub const Error = WriterType.Error;
|
pub fn init(child: *std.Io.Writer) UriEncodingWriter {
|
||||||
pub const Writer = std.io.Writer(*Self, Error, write);
|
return .{
|
||||||
|
.child_writer = child,
|
||||||
const Self = @This();
|
.writer = .{
|
||||||
|
.buffer = &.{},
|
||||||
pub fn write(self: *Self, bytes: []const u8) Error!usize {
|
.vtable = &.{
|
||||||
try uriEncode(bytes, self.child_stream, true);
|
.drain = drain,
|
||||||
return bytes.len; // We say that all bytes are "written", even if they're not, as caller may be retrying
|
},
|
||||||
}
|
},
|
||||||
|
|
||||||
pub fn writer(self: *Self) Writer {
|
|
||||||
return .{ .context = self };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ignoringWriter(child_stream: anytype, ignore: u8) IgnoringWriter(@TypeOf(child_stream)) {
|
fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
|
||||||
return .{ .child_stream = child_stream, .ignore = ignore };
|
if (splat > 0) return error.WriteFailed; // no splat support
|
||||||
}
|
const self: *UriEncodingWriter = @fieldParentPtr("writer", w);
|
||||||
|
var total: usize = 0;
|
||||||
|
for (data) |bytes| {
|
||||||
|
try uriEncode(bytes, self.child_writer, true);
|
||||||
|
total += bytes.len;
|
||||||
|
}
|
||||||
|
return total; // We say that all bytes are "written", even if they're not, as caller may be retrying
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// A Writer that ignores a character
|
/// A Writer that ignores a character
|
||||||
pub fn IgnoringWriter(comptime WriterType: type) type {
|
const IgnoringWriter = struct {
|
||||||
return struct {
|
child_writer: *std.Io.Writer,
|
||||||
child_stream: WriterType,
|
|
||||||
ignore: u8,
|
ignore: u8,
|
||||||
|
writer: std.Io.Writer,
|
||||||
|
|
||||||
pub const Error = WriterType.Error;
|
pub fn init(child: *std.Io.Writer, ignore: u8) IgnoringWriter {
|
||||||
pub const Writer = std.io.Writer(*Self, Error, write);
|
return .{
|
||||||
|
.child_writer = child,
|
||||||
const Self = @This();
|
.ignore = ignore,
|
||||||
|
.writer = .{
|
||||||
pub fn write(self: *Self, bytes: []const u8) Error!usize {
|
.buffer = &.{},
|
||||||
for (bytes) |b| {
|
.vtable = &.{
|
||||||
if (b != self.ignore)
|
.drain = drain,
|
||||||
try self.child_stream.writeByte(b);
|
},
|
||||||
}
|
},
|
||||||
return bytes.len; // We say that all bytes are "written", even if they're not, as caller may be retrying
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writer(self: *Self) Writer {
|
|
||||||
return .{ .context = self };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
|
||||||
|
if (splat > 0) return error.WriteFailed; // no splat support
|
||||||
|
const self: *IgnoringWriter = @fieldParentPtr("writer", w);
|
||||||
|
var total: usize = 0;
|
||||||
|
for (data) |bytes| {
|
||||||
|
for (bytes) |b|
|
||||||
|
if (b != self.ignore)
|
||||||
|
try self.child_writer.writeByte(b);
|
||||||
|
total += bytes.len;
|
||||||
|
}
|
||||||
|
return total; // We say that all bytes are "written", even if they're not, as caller may be retrying
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn reportTraffic(
|
fn reportTraffic(
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
@ -1330,9 +1364,9 @@ fn reportTraffic(
|
||||||
response: awshttp.HttpResult,
|
response: awshttp.HttpResult,
|
||||||
comptime reporter: fn (comptime []const u8, anytype) void,
|
comptime reporter: fn (comptime []const u8, anytype) void,
|
||||||
) !void {
|
) !void {
|
||||||
var msg = std.ArrayList(u8).init(allocator);
|
var msg = try std.Io.Writer.Allocating.initCapacity(allocator, 256);
|
||||||
defer msg.deinit();
|
defer msg.deinit();
|
||||||
const writer = msg.writer();
|
const writer = &msg.writer;
|
||||||
try writer.print("{s}\n\n", .{info});
|
try writer.print("{s}\n\n", .{info});
|
||||||
try writer.print("Return status: {d}\n\n", .{response.response_code});
|
try writer.print("Return status: {d}\n\n", .{response.response_code});
|
||||||
if (request.query.len > 0) try writer.print("Request Query:\n \t{s}\n", .{request.query});
|
if (request.query.len > 0) try writer.print("Request Query:\n \t{s}\n", .{request.query});
|
||||||
|
@ -1354,7 +1388,7 @@ fn reportTraffic(
|
||||||
_ = try writer.write("Response Body:\n");
|
_ = try writer.write("Response Body:\n");
|
||||||
try writer.print("--------------\n{s}\n", .{response.body});
|
try writer.print("--------------\n{s}\n", .{response.body});
|
||||||
_ = try writer.write("--------------\n");
|
_ = try writer.write("--------------\n");
|
||||||
reporter("{s}\n", .{msg.items});
|
reporter("{s}\n", .{msg.written()});
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -25,7 +25,7 @@ pub const Credentials = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn deinit(self: Self) void {
|
pub fn deinit(self: Self) void {
|
||||||
std.crypto.utils.secureZero(u8, self.secret_key);
|
std.crypto.secureZero(u8, self.secret_key);
|
||||||
self.allocator.free(self.secret_key);
|
self.allocator.free(self.secret_key);
|
||||||
self.allocator.free(self.access_key);
|
self.allocator.free(self.access_key);
|
||||||
if (self.session_token) |t| self.allocator.free(t);
|
if (self.session_token) |t| self.allocator.free(t);
|
||||||
|
|
|
@ -173,11 +173,12 @@ fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
||||||
|
|
||||||
var cl = std.http.Client{ .allocator = allocator };
|
var cl = std.http.Client{ .allocator = allocator };
|
||||||
defer cl.deinit(); // I don't belive connection pooling would help much here as it's non-ssl and local
|
defer cl.deinit(); // I don't belive connection pooling would help much here as it's non-ssl and local
|
||||||
var resp_payload = std.ArrayList(u8).init(allocator);
|
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||||
defer resp_payload.deinit();
|
defer aw.deinit();
|
||||||
|
const response_payload = &aw.writer;
|
||||||
const req = try cl.fetch(.{
|
const req = try cl.fetch(.{
|
||||||
.location = .{ .url = container_uri },
|
.location = .{ .url = container_uri },
|
||||||
.response_storage = .{ .dynamic = &resp_payload },
|
.response_writer = response_payload,
|
||||||
});
|
});
|
||||||
if (req.status != .ok and req.status != .not_found) {
|
if (req.status != .ok and req.status != .not_found) {
|
||||||
log.warn("Bad status code received from container credentials endpoint: {}", .{@intFromEnum(req.status)});
|
log.warn("Bad status code received from container credentials endpoint: {}", .{@intFromEnum(req.status)});
|
||||||
|
@ -185,8 +186,8 @@ fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
||||||
}
|
}
|
||||||
if (req.status == .not_found) return null;
|
if (req.status == .not_found) return null;
|
||||||
|
|
||||||
log.debug("Read {d} bytes from container credentials endpoint", .{resp_payload.items.len});
|
log.debug("Read {d} bytes from container credentials endpoint", .{aw.written().len});
|
||||||
if (resp_payload.items.len == 0) return null;
|
if (aw.written().len == 0) return null;
|
||||||
|
|
||||||
const CredsResponse = struct {
|
const CredsResponse = struct {
|
||||||
AccessKeyId: []const u8,
|
AccessKeyId: []const u8,
|
||||||
|
@ -196,8 +197,8 @@ fn getContainerCredentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
||||||
Token: []const u8,
|
Token: []const u8,
|
||||||
};
|
};
|
||||||
const creds_response = blk: {
|
const creds_response = blk: {
|
||||||
const res = std.json.parseFromSlice(CredsResponse, allocator, resp_payload.items, .{}) catch |e| {
|
const res = std.json.parseFromSlice(CredsResponse, allocator, aw.written(), .{}) catch |e| {
|
||||||
log.err("Unexpected Json response from container credentials endpoint: {s}", .{resp_payload.items});
|
log.err("Unexpected Json response from container credentials endpoint: {s}", .{aw.written()});
|
||||||
log.err("Error parsing json: {}", .{e});
|
log.err("Error parsing json: {}", .{e});
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpStackTrace(trace.*);
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
@ -224,26 +225,27 @@ fn getImdsv2Credentials(allocator: std.mem.Allocator) !?auth.Credentials {
|
||||||
defer cl.deinit(); // I don't belive connection pooling would help much here as it's non-ssl and local
|
defer cl.deinit(); // I don't belive connection pooling would help much here as it's non-ssl and local
|
||||||
// Get token
|
// Get token
|
||||||
{
|
{
|
||||||
var resp_payload = std.ArrayList(u8).init(allocator);
|
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||||
defer resp_payload.deinit();
|
defer aw.deinit();
|
||||||
|
const response_payload = &aw.writer;
|
||||||
const req = try cl.fetch(.{
|
const req = try cl.fetch(.{
|
||||||
.method = .PUT,
|
.method = .PUT,
|
||||||
.location = .{ .url = "http://169.254.169.254/latest/api/token" },
|
.location = .{ .url = "http://169.254.169.254/latest/api/token" },
|
||||||
.extra_headers = &[_]std.http.Header{
|
.extra_headers = &[_]std.http.Header{
|
||||||
.{ .name = "X-aws-ec2-metadata-token-ttl-seconds", .value = "21600" },
|
.{ .name = "X-aws-ec2-metadata-token-ttl-seconds", .value = "21600" },
|
||||||
},
|
},
|
||||||
.response_storage = .{ .dynamic = &resp_payload },
|
.response_writer = response_payload,
|
||||||
});
|
});
|
||||||
if (req.status != .ok) {
|
if (req.status != .ok) {
|
||||||
log.warn("Bad status code received from IMDS v2: {}", .{@intFromEnum(req.status)});
|
log.warn("Bad status code received from IMDS v2: {}", .{@intFromEnum(req.status)});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (resp_payload.items.len == 0) {
|
if (aw.written().len == 0) {
|
||||||
log.warn("Unexpected zero response from IMDS v2", .{});
|
log.warn("Unexpected zero response from IMDS v2", .{});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
token = try resp_payload.toOwnedSlice();
|
token = try aw.toOwnedSlice();
|
||||||
errdefer if (token) |t| allocator.free(t);
|
errdefer if (token) |t| allocator.free(t);
|
||||||
}
|
}
|
||||||
std.debug.assert(token != null);
|
std.debug.assert(token != null);
|
||||||
|
@ -265,15 +267,16 @@ fn getImdsRoleName(allocator: std.mem.Allocator, client: *std.http.Client, imds_
|
||||||
// "InstanceProfileArn" : "arn:aws:iam::550620852718:instance-profile/ec2-dev",
|
// "InstanceProfileArn" : "arn:aws:iam::550620852718:instance-profile/ec2-dev",
|
||||||
// "InstanceProfileId" : "AIPAYAM4POHXCFNKZ7HU2"
|
// "InstanceProfileId" : "AIPAYAM4POHXCFNKZ7HU2"
|
||||||
// }
|
// }
|
||||||
var resp_payload = std.ArrayList(u8).init(allocator);
|
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||||
defer resp_payload.deinit();
|
defer aw.deinit();
|
||||||
|
const response_payload = &aw.writer;
|
||||||
const req = try client.fetch(.{
|
const req = try client.fetch(.{
|
||||||
.method = .GET,
|
.method = .GET,
|
||||||
.location = .{ .url = "http://169.254.169.254/latest/meta-data/iam/info" },
|
.location = .{ .url = "http://169.254.169.254/latest/meta-data/iam/info" },
|
||||||
.extra_headers = &[_]std.http.Header{
|
.extra_headers = &[_]std.http.Header{
|
||||||
.{ .name = "X-aws-ec2-metadata-token", .value = imds_token },
|
.{ .name = "X-aws-ec2-metadata-token", .value = imds_token },
|
||||||
},
|
},
|
||||||
.response_storage = .{ .dynamic = &resp_payload },
|
.response_writer = response_payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (req.status != .ok and req.status != .not_found) {
|
if (req.status != .ok and req.status != .not_found) {
|
||||||
|
@ -281,7 +284,7 @@ fn getImdsRoleName(allocator: std.mem.Allocator, client: *std.http.Client, imds_
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (req.status == .not_found) return null;
|
if (req.status == .not_found) return null;
|
||||||
if (resp_payload.items.len == 0) {
|
if (aw.written().len == 0) {
|
||||||
log.warn("Unexpected empty response from IMDS endpoint post token", .{});
|
log.warn("Unexpected empty response from IMDS endpoint post token", .{});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -292,8 +295,8 @@ fn getImdsRoleName(allocator: std.mem.Allocator, client: *std.http.Client, imds_
|
||||||
InstanceProfileArn: []const u8,
|
InstanceProfileArn: []const u8,
|
||||||
InstanceProfileId: []const u8,
|
InstanceProfileId: []const u8,
|
||||||
};
|
};
|
||||||
const imds_response = std.json.parseFromSlice(ImdsResponse, allocator, resp_payload.items, .{}) catch |e| {
|
const imds_response = std.json.parseFromSlice(ImdsResponse, allocator, aw.written(), .{}) catch |e| {
|
||||||
log.err("Unexpected Json response from IMDS endpoint: {s}", .{resp_payload.items});
|
log.err("Unexpected Json response from IMDS endpoint: {s}", .{aw.written()});
|
||||||
log.err("Error parsing json: {}", .{e});
|
log.err("Error parsing json: {}", .{e});
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpStackTrace(trace.*);
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
@ -315,15 +318,16 @@ fn getImdsRoleName(allocator: std.mem.Allocator, client: *std.http.Client, imds_
|
||||||
fn getImdsCredentials(allocator: std.mem.Allocator, client: *std.http.Client, role_name: []const u8, imds_token: []u8) !?auth.Credentials {
|
fn getImdsCredentials(allocator: std.mem.Allocator, client: *std.http.Client, role_name: []const u8, imds_token: []u8) !?auth.Credentials {
|
||||||
const url = try std.fmt.allocPrint(allocator, "http://169.254.169.254/latest/meta-data/iam/security-credentials/{s}/", .{role_name});
|
const url = try std.fmt.allocPrint(allocator, "http://169.254.169.254/latest/meta-data/iam/security-credentials/{s}/", .{role_name});
|
||||||
defer allocator.free(url);
|
defer allocator.free(url);
|
||||||
var resp_payload = std.ArrayList(u8).init(allocator);
|
var aw: std.Io.Writer.Allocating = .init(allocator);
|
||||||
defer resp_payload.deinit();
|
defer aw.deinit();
|
||||||
|
const response_payload = &aw.writer;
|
||||||
const req = try client.fetch(.{
|
const req = try client.fetch(.{
|
||||||
.method = .GET,
|
.method = .GET,
|
||||||
.location = .{ .url = url },
|
.location = .{ .url = url },
|
||||||
.extra_headers = &[_]std.http.Header{
|
.extra_headers = &[_]std.http.Header{
|
||||||
.{ .name = "X-aws-ec2-metadata-token", .value = imds_token },
|
.{ .name = "X-aws-ec2-metadata-token", .value = imds_token },
|
||||||
},
|
},
|
||||||
.response_storage = .{ .dynamic = &resp_payload },
|
.response_writer = response_payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (req.status != .ok and req.status != .not_found) {
|
if (req.status != .ok and req.status != .not_found) {
|
||||||
|
@ -331,7 +335,7 @@ fn getImdsCredentials(allocator: std.mem.Allocator, client: *std.http.Client, ro
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (req.status == .not_found) return null;
|
if (req.status == .not_found) return null;
|
||||||
if (resp_payload.items.len == 0) {
|
if (aw.written().len == 0) {
|
||||||
log.warn("Unexpected empty response from IMDS role endpoint", .{});
|
log.warn("Unexpected empty response from IMDS role endpoint", .{});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -346,8 +350,8 @@ fn getImdsCredentials(allocator: std.mem.Allocator, client: *std.http.Client, ro
|
||||||
Token: []const u8,
|
Token: []const u8,
|
||||||
Expiration: []const u8,
|
Expiration: []const u8,
|
||||||
};
|
};
|
||||||
const imds_response = std.json.parseFromSlice(ImdsResponse, allocator, resp_payload.items, .{}) catch |e| {
|
const imds_response = std.json.parseFromSlice(ImdsResponse, allocator, aw.written(), .{}) catch |e| {
|
||||||
log.err("Unexpected Json response from IMDS endpoint: {s}", .{resp_payload.items});
|
log.err("Unexpected Json response from IMDS endpoint: {s}", .{aw.written()});
|
||||||
log.err("Error parsing json: {}", .{e});
|
log.err("Error parsing json: {}", .{e});
|
||||||
if (@errorReturnTrace()) |trace| {
|
if (@errorReturnTrace()) |trace| {
|
||||||
std.debug.dumpStackTrace(trace.*);
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
|
111
src/aws_http.zig
111
src/aws_http.zig
|
@ -199,8 +199,8 @@ pub const AwsHttp = struct {
|
||||||
// We will use endpoint instead
|
// We will use endpoint instead
|
||||||
request_cp.path = endpoint.path;
|
request_cp.path = endpoint.path;
|
||||||
|
|
||||||
var request_headers = std.ArrayList(std.http.Header).init(self.allocator);
|
var request_headers = std.ArrayList(std.http.Header){};
|
||||||
defer request_headers.deinit();
|
defer request_headers.deinit(self.allocator);
|
||||||
|
|
||||||
const len = try addHeaders(self.allocator, &request_headers, endpoint.host, request_cp.body, request_cp.content_type, request_cp.headers);
|
const len = try addHeaders(self.allocator, &request_headers, endpoint.host, request_cp.body, request_cp.content_type, request_cp.headers);
|
||||||
defer if (len) |l| self.allocator.free(l);
|
defer if (len) |l| self.allocator.free(l);
|
||||||
|
@ -213,10 +213,10 @@ pub const AwsHttp = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var headers = std.ArrayList(std.http.Header).init(self.allocator);
|
var headers = std.ArrayList(std.http.Header){};
|
||||||
defer headers.deinit();
|
defer headers.deinit(self.allocator);
|
||||||
for (request_cp.headers) |header|
|
for (request_cp.headers) |header|
|
||||||
try headers.append(.{ .name = header.name, .value = header.value });
|
try headers.append(self.allocator, .{ .name = header.name, .value = header.value });
|
||||||
log.debug("All Request Headers:", .{});
|
log.debug("All Request Headers:", .{});
|
||||||
for (headers.items) |h| {
|
for (headers.items) |h| {
|
||||||
log.debug("\t{s}: {s}", .{ h.name, h.value });
|
log.debug("\t{s}: {s}", .{ h.name, h.value });
|
||||||
|
@ -230,16 +230,10 @@ pub const AwsHttp = struct {
|
||||||
defer cl.deinit(); // TODO: Connection pooling
|
defer cl.deinit(); // TODO: Connection pooling
|
||||||
|
|
||||||
const method = std.meta.stringToEnum(std.http.Method, request_cp.method).?;
|
const method = std.meta.stringToEnum(std.http.Method, request_cp.method).?;
|
||||||
var server_header_buffer: [16 * 1024]u8 = undefined;
|
|
||||||
var resp_payload = std.ArrayList(u8).init(self.allocator);
|
// Fetch API in 0.15.1 is insufficient as it does not provide
|
||||||
defer resp_payload.deinit();
|
// server headers. We'll construct and send the request ourselves
|
||||||
const req = try cl.fetch(.{
|
var req = try cl.request(method, try std.Uri.parse(url), .{
|
||||||
.server_header_buffer = &server_header_buffer,
|
|
||||||
.method = method,
|
|
||||||
.payload = if (request_cp.body.len > 0) request_cp.body else null,
|
|
||||||
.response_storage = .{ .dynamic = &resp_payload },
|
|
||||||
.raw_uri = true,
|
|
||||||
.location = .{ .url = url },
|
|
||||||
// we need full control over most headers. I wish libraries would do a
|
// we need full control over most headers. I wish libraries would do a
|
||||||
// better job of having default headers as an opt-in...
|
// better job of having default headers as an opt-in...
|
||||||
.headers = .{
|
.headers = .{
|
||||||
|
@ -266,33 +260,64 @@ pub const AwsHttp = struct {
|
||||||
// }
|
// }
|
||||||
// try req.wait();
|
// try req.wait();
|
||||||
|
|
||||||
|
if (request_cp.body.len > 0) {
|
||||||
|
// This seems a bit silly, but we can't have a []const u8 here
|
||||||
|
// because when it sends, it's using a writer, and this becomes
|
||||||
|
// the buffer of the writer. It's conceivable that something
|
||||||
|
// in the chain then does actually modify the body of the request
|
||||||
|
// so we'll need to duplicate it here
|
||||||
|
const req_body = try self.allocator.dupe(u8, request_cp.body);
|
||||||
|
try req.sendBodyComplete(req_body);
|
||||||
|
} else try req.sendBodiless();
|
||||||
|
|
||||||
|
var response = try req.receiveHead(&.{});
|
||||||
|
|
||||||
// TODO: Timeout - is this now above us?
|
// TODO: Timeout - is this now above us?
|
||||||
log.debug(
|
log.debug(
|
||||||
"Request Complete. Response code {d}: {?s}",
|
"Request Complete. Response code {d}: {?s}",
|
||||||
.{ @intFromEnum(req.status), req.status.phrase() },
|
.{ @intFromEnum(response.head.status), response.head.status.phrase() },
|
||||||
);
|
);
|
||||||
log.debug("Response headers:", .{});
|
log.debug("Response headers:", .{});
|
||||||
var resp_headers = std.ArrayList(Header).init(
|
var resp_headers = std.ArrayList(Header){};
|
||||||
self.allocator,
|
defer resp_headers.deinit(self.allocator);
|
||||||
);
|
var it = response.head.iterateHeaders();
|
||||||
defer resp_headers.deinit();
|
|
||||||
var it = std.http.HeaderIterator.init(server_header_buffer[0..]);
|
|
||||||
while (it.next()) |h| { // even though we don't expect to fill the buffer,
|
while (it.next()) |h| { // even though we don't expect to fill the buffer,
|
||||||
// we don't get a length, but looks via stdlib source
|
// we don't get a length, but looks via stdlib source
|
||||||
// it should be ok to call next on the undefined memory
|
// it should be ok to call next on the undefined memory
|
||||||
log.debug(" {s}: {s}", .{ h.name, h.value });
|
log.debug(" {s}: {s}", .{ h.name, h.value });
|
||||||
try resp_headers.append(.{
|
try resp_headers.append(self.allocator, .{
|
||||||
.name = try (self.allocator.dupe(u8, h.name)),
|
.name = try (self.allocator.dupe(u8, h.name)),
|
||||||
.value = try (self.allocator.dupe(u8, h.value)),
|
.value = try (self.allocator.dupe(u8, h.value)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// This is directly lifted from fetch, as there is no function in
|
||||||
|
// 0.15.1 client to negotiate decompression
|
||||||
|
const decompress_buffer: []u8 = switch (response.head.content_encoding) {
|
||||||
|
.identity => &.{},
|
||||||
|
.zstd => try self.allocator.alloc(u8, std.compress.zstd.default_window_len),
|
||||||
|
.deflate, .gzip => try self.allocator.alloc(u8, std.compress.flate.max_window_len),
|
||||||
|
.compress => return error.UnsupportedCompressionMethod,
|
||||||
|
};
|
||||||
|
defer self.allocator.free(decompress_buffer);
|
||||||
|
|
||||||
log.debug("raw response body:\n{s}", .{resp_payload.items});
|
var transfer_buffer: [64]u8 = undefined;
|
||||||
|
var decompress: std.http.Decompress = undefined;
|
||||||
|
const reader = response.readerDecompressing(&transfer_buffer, &decompress, decompress_buffer);
|
||||||
|
|
||||||
|
// Not sure on optimal size here, but should definitely be > 0
|
||||||
|
var aw = try std.Io.Writer.Allocating.initCapacity(self.allocator, 128);
|
||||||
|
defer aw.deinit();
|
||||||
|
const response_writer = &aw.writer;
|
||||||
|
_ = reader.streamRemaining(response_writer) catch |err| switch (err) {
|
||||||
|
error.ReadFailed => return response.bodyErr().?,
|
||||||
|
else => |e| return e,
|
||||||
|
};
|
||||||
|
log.debug("raw response body:\n{s}", .{aw.written()});
|
||||||
|
|
||||||
const rc = HttpResult{
|
const rc = HttpResult{
|
||||||
.response_code = @intFromEnum(req.status),
|
.response_code = @intFromEnum(response.head.status),
|
||||||
.body = try resp_payload.toOwnedSlice(),
|
.body = try aw.toOwnedSlice(),
|
||||||
.headers = try resp_headers.toOwnedSlice(),
|
.headers = try resp_headers.toOwnedSlice(self.allocator),
|
||||||
.allocator = self.allocator,
|
.allocator = self.allocator,
|
||||||
};
|
};
|
||||||
return rc;
|
return rc;
|
||||||
|
@ -305,15 +330,21 @@ fn getRegion(service: []const u8, region: []const u8) []const u8 {
|
||||||
return region;
|
return region;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Header), host: []const u8, body: []const u8, content_type: []const u8, additional_headers: []const Header) !?[]const u8 {
|
fn addHeaders(
|
||||||
// We don't need allocator and body because they were to add a
|
allocator: std.mem.Allocator,
|
||||||
// Content-Length header. But that is being added by the client send()
|
headers: *std.ArrayList(std.http.Header),
|
||||||
// function, so we don't want it on the request twice. But I also feel
|
host: []const u8,
|
||||||
// pretty strongly that send() should be providing us control, because
|
body: []const u8,
|
||||||
// I think if we don't add it here, it won't get signed, and we would
|
content_type: []const u8,
|
||||||
// really prefer it to be signed. So, we will wait and watch for this
|
additional_headers: []const Header,
|
||||||
// situation to change in stdlib
|
) !?[]const u8 {
|
||||||
_ = allocator;
|
// We don't need body because they were to add a Content-Length header. But
|
||||||
|
// that is being added by the client send() function, so we don't want it
|
||||||
|
// on the request twice. But I also feel pretty strongly that send() should
|
||||||
|
// be providing us control, because I think if we don't add it here, it
|
||||||
|
// won't get signed, and we would really prefer it to be signed. So, we
|
||||||
|
// will wait and watch for this situation to change in stdlib
|
||||||
|
|
||||||
_ = body;
|
_ = body;
|
||||||
var has_content_type = false;
|
var has_content_type = false;
|
||||||
for (additional_headers) |h| {
|
for (additional_headers) |h| {
|
||||||
|
@ -322,12 +353,12 @@ fn addHeaders(allocator: std.mem.Allocator, headers: *std.ArrayList(std.http.Hea
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try headers.append(.{ .name = "Accept", .value = "application/json" });
|
try headers.append(allocator, .{ .name = "Accept", .value = "application/json" });
|
||||||
try headers.append(.{ .name = "Host", .value = host });
|
try headers.append(allocator, .{ .name = "Host", .value = host });
|
||||||
try headers.append(.{ .name = "User-Agent", .value = "zig-aws 1.0" });
|
try headers.append(allocator, .{ .name = "User-Agent", .value = "zig-aws 1.0" });
|
||||||
if (!has_content_type)
|
if (!has_content_type)
|
||||||
try headers.append(.{ .name = "Content-Type", .value = content_type });
|
try headers.append(allocator, .{ .name = "Content-Type", .value = content_type });
|
||||||
try headers.appendSlice(additional_headers);
|
try headers.appendSlice(allocator, additional_headers);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ pub const SigningError = error{
|
||||||
XAmzExpiresHeaderInRequest,
|
XAmzExpiresHeaderInRequest,
|
||||||
/// Used if the request headers already includes x-amz-region-set
|
/// Used if the request headers already includes x-amz-region-set
|
||||||
XAmzRegionSetHeaderInRequest,
|
XAmzRegionSetHeaderInRequest,
|
||||||
} || std.fmt.AllocPrintError;
|
} || error{OutOfMemory};
|
||||||
|
|
||||||
const forbidden_headers = .{
|
const forbidden_headers = .{
|
||||||
.{ .name = "x-amz-content-sha256", .err = SigningError.XAmzContentSha256HeaderInRequest },
|
.{ .name = "x-amz-content-sha256", .err = SigningError.XAmzContentSha256HeaderInRequest },
|
||||||
|
@ -312,12 +312,12 @@ pub fn signRequest(allocator: std.mem.Allocator, request: base.Request, config:
|
||||||
.name = "Authorization",
|
.name = "Authorization",
|
||||||
.value = try std.fmt.allocPrint(
|
.value = try std.fmt.allocPrint(
|
||||||
allocator,
|
allocator,
|
||||||
"AWS4-HMAC-SHA256 Credential={s}/{s}, SignedHeaders={s}, Signature={s}",
|
"AWS4-HMAC-SHA256 Credential={s}/{s}, SignedHeaders={s}, Signature={x}",
|
||||||
.{
|
.{
|
||||||
config.credentials.access_key,
|
config.credentials.access_key,
|
||||||
scope,
|
scope,
|
||||||
canonical_request.headers.signed_headers,
|
canonical_request.headers.signed_headers,
|
||||||
std.fmt.fmtSliceHexLower(signature),
|
signature,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
@ -545,7 +545,7 @@ fn getSigningKey(allocator: std.mem.Allocator, signing_date: []const u8, config:
|
||||||
defer {
|
defer {
|
||||||
// secureZero avoids compiler optimizations that may say
|
// secureZero avoids compiler optimizations that may say
|
||||||
// "WTF are you doing this thing? Looks like nothing to me. It's silly and we will remove it"
|
// "WTF are you doing this thing? Looks like nothing to me. It's silly and we will remove it"
|
||||||
std.crypto.utils.secureZero(u8, secret); // zero our copy of secret
|
std.crypto.secureZero(u8, secret); // zero our copy of secret
|
||||||
allocator.free(secret);
|
allocator.free(secret);
|
||||||
}
|
}
|
||||||
// log.debug("secret: {s}", .{secret});
|
// log.debug("secret: {s}", .{secret});
|
||||||
|
@ -673,7 +673,7 @@ fn canonicalUri(allocator: std.mem.Allocator, path: []const u8, double_encode: b
|
||||||
fn encodeParamPart(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
|
fn encodeParamPart(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
|
||||||
const unreserved_marks = "-_.!~*'()";
|
const unreserved_marks = "-_.!~*'()";
|
||||||
var encoded = try std.ArrayList(u8).initCapacity(allocator, path.len);
|
var encoded = try std.ArrayList(u8).initCapacity(allocator, path.len);
|
||||||
defer encoded.deinit();
|
defer encoded.deinit(allocator);
|
||||||
for (path) |c| {
|
for (path) |c| {
|
||||||
var should_encode = true;
|
var should_encode = true;
|
||||||
for (unreserved_marks) |r|
|
for (unreserved_marks) |r|
|
||||||
|
@ -685,16 +685,16 @@ fn encodeParamPart(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
|
||||||
should_encode = false;
|
should_encode = false;
|
||||||
|
|
||||||
if (!should_encode) {
|
if (!should_encode) {
|
||||||
try encoded.append(c);
|
try encoded.append(allocator, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Whatever remains, encode it
|
// Whatever remains, encode it
|
||||||
try encoded.append('%');
|
try encoded.append(allocator, '%');
|
||||||
const hex = try std.fmt.allocPrint(allocator, "{s}", .{std.fmt.fmtSliceHexUpper(&[_]u8{c})});
|
const hex = try std.fmt.allocPrint(allocator, "{X}", .{&[_]u8{c}});
|
||||||
defer allocator.free(hex);
|
defer allocator.free(hex);
|
||||||
try encoded.appendSlice(hex);
|
try encoded.appendSlice(allocator, hex);
|
||||||
}
|
}
|
||||||
return encoded.toOwnedSlice();
|
return encoded.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// URI encode every byte except the unreserved characters:
|
// URI encode every byte except the unreserved characters:
|
||||||
|
@ -715,7 +715,7 @@ fn encodeUri(allocator: std.mem.Allocator, path: []const u8) ![]u8 {
|
||||||
const reserved_characters = ";,/?:@&=+$#";
|
const reserved_characters = ";,/?:@&=+$#";
|
||||||
const unreserved_marks = "-_.!~*'()";
|
const unreserved_marks = "-_.!~*'()";
|
||||||
var encoded = try std.ArrayList(u8).initCapacity(allocator, path.len);
|
var encoded = try std.ArrayList(u8).initCapacity(allocator, path.len);
|
||||||
defer encoded.deinit();
|
defer encoded.deinit(allocator);
|
||||||
// if (std.mem.startsWith(u8, path, "/2017-03-31/tags/arn")) {
|
// if (std.mem.startsWith(u8, path, "/2017-03-31/tags/arn")) {
|
||||||
// try encoded.appendSlice("/2017-03-31/tags/arn%25253Aaws%25253Alambda%25253Aus-west-2%25253A550620852718%25253Afunction%25253Aawsome-lambda-LambdaStackawsomeLambda");
|
// try encoded.appendSlice("/2017-03-31/tags/arn%25253Aaws%25253Alambda%25253Aus-west-2%25253A550620852718%25253Afunction%25253Aawsome-lambda-LambdaStackawsomeLambda");
|
||||||
// return encoded.toOwnedSlice();
|
// return encoded.toOwnedSlice();
|
||||||
|
@ -738,16 +738,16 @@ fn encodeUri(allocator: std.mem.Allocator, path: []const u8) ![]u8 {
|
||||||
should_encode = false;
|
should_encode = false;
|
||||||
|
|
||||||
if (!should_encode) {
|
if (!should_encode) {
|
||||||
try encoded.append(c);
|
try encoded.append(allocator, c);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Whatever remains, encode it
|
// Whatever remains, encode it
|
||||||
try encoded.append('%');
|
try encoded.append(allocator, '%');
|
||||||
const hex = try std.fmt.allocPrint(allocator, "{s}", .{std.fmt.fmtSliceHexUpper(&[_]u8{c})});
|
const hex = try std.fmt.allocPrint(allocator, "{X}", .{&[_]u8{c}});
|
||||||
defer allocator.free(hex);
|
defer allocator.free(hex);
|
||||||
try encoded.appendSlice(hex);
|
try encoded.appendSlice(allocator, hex);
|
||||||
}
|
}
|
||||||
return encoded.toOwnedSlice();
|
return encoded.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canonicalQueryString(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
|
fn canonicalQueryString(allocator: std.mem.Allocator, path: []const u8) ![]const u8 {
|
||||||
|
@ -800,25 +800,25 @@ fn canonicalQueryString(allocator: std.mem.Allocator, path: []const u8) ![]const
|
||||||
|
|
||||||
// Split this by component
|
// Split this by component
|
||||||
var portions = std.mem.splitScalar(u8, query, '&');
|
var portions = std.mem.splitScalar(u8, query, '&');
|
||||||
var sort_me = std.ArrayList([]const u8).init(allocator);
|
var sort_me = std.ArrayList([]const u8){};
|
||||||
defer sort_me.deinit();
|
defer sort_me.deinit(allocator);
|
||||||
while (portions.next()) |item|
|
while (portions.next()) |item|
|
||||||
try sort_me.append(item);
|
try sort_me.append(allocator, item);
|
||||||
std.sort.pdq([]const u8, sort_me.items, {}, lessThanBinary);
|
std.sort.pdq([]const u8, sort_me.items, {}, lessThanBinary);
|
||||||
|
|
||||||
var normalized = try std.ArrayList(u8).initCapacity(allocator, path.len);
|
var normalized = try std.ArrayList(u8).initCapacity(allocator, path.len);
|
||||||
defer normalized.deinit();
|
defer normalized.deinit(allocator);
|
||||||
var first = true;
|
var first = true;
|
||||||
for (sort_me.items) |i| {
|
for (sort_me.items) |i| {
|
||||||
if (!first) try normalized.append('&');
|
if (!first) try normalized.append(allocator, '&');
|
||||||
first = false;
|
first = false;
|
||||||
const first_equals = std.mem.indexOf(u8, i, "=");
|
const first_equals = std.mem.indexOf(u8, i, "=");
|
||||||
if (first_equals == null) {
|
if (first_equals == null) {
|
||||||
// Rare. This is "foo="
|
// Rare. This is "foo="
|
||||||
const normed_item = try encodeUri(allocator, i);
|
const normed_item = try encodeUri(allocator, i);
|
||||||
defer allocator.free(normed_item);
|
defer allocator.free(normed_item);
|
||||||
try normalized.appendSlice(i); // This should be encoded
|
try normalized.appendSlice(allocator, i); // This should be encoded
|
||||||
try normalized.append('=');
|
try normalized.append(allocator, '=');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -831,12 +831,12 @@ fn canonicalQueryString(allocator: std.mem.Allocator, path: []const u8) ![]const
|
||||||
// Double-encode any = in the value. But not anything else?
|
// Double-encode any = in the value. But not anything else?
|
||||||
const weird_equals_in_value_thing = try replace(allocator, value, "%3D", "%253D");
|
const weird_equals_in_value_thing = try replace(allocator, value, "%3D", "%253D");
|
||||||
defer allocator.free(weird_equals_in_value_thing);
|
defer allocator.free(weird_equals_in_value_thing);
|
||||||
try normalized.appendSlice(key);
|
try normalized.appendSlice(allocator, key);
|
||||||
try normalized.append('=');
|
try normalized.append(allocator, '=');
|
||||||
try normalized.appendSlice(weird_equals_in_value_thing);
|
try normalized.appendSlice(allocator, weird_equals_in_value_thing);
|
||||||
}
|
}
|
||||||
|
|
||||||
return normalized.toOwnedSlice();
|
return normalized.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace(allocator: std.mem.Allocator, haystack: []const u8, needle: []const u8, replacement_value: []const u8) ![]const u8 {
|
fn replace(allocator: std.mem.Allocator, haystack: []const u8, needle: []const u8, replacement_value: []const u8) ![]const u8 {
|
||||||
|
@ -875,7 +875,7 @@ fn canonicalHeaders(allocator: std.mem.Allocator, headers: []const std.http.Head
|
||||||
allocator.free(h.name);
|
allocator.free(h.name);
|
||||||
allocator.free(h.value);
|
allocator.free(h.value);
|
||||||
}
|
}
|
||||||
dest.deinit();
|
dest.deinit(allocator);
|
||||||
}
|
}
|
||||||
var total_len: usize = 0;
|
var total_len: usize = 0;
|
||||||
var total_name_len: usize = 0;
|
var total_name_len: usize = 0;
|
||||||
|
@ -905,15 +905,15 @@ fn canonicalHeaders(allocator: std.mem.Allocator, headers: []const std.http.Head
|
||||||
defer allocator.free(value);
|
defer allocator.free(value);
|
||||||
const n = try std.ascii.allocLowerString(allocator, h.name);
|
const n = try std.ascii.allocLowerString(allocator, h.name);
|
||||||
const v = try std.fmt.allocPrint(allocator, "{s}", .{value});
|
const v = try std.fmt.allocPrint(allocator, "{s}", .{value});
|
||||||
try dest.append(.{ .name = n, .value = v });
|
try dest.append(allocator, .{ .name = n, .value = v });
|
||||||
}
|
}
|
||||||
|
|
||||||
std.sort.pdq(std.http.Header, dest.items, {}, lessThan);
|
std.sort.pdq(std.http.Header, dest.items, {}, lessThan);
|
||||||
|
|
||||||
var dest_str = try std.ArrayList(u8).initCapacity(allocator, total_len);
|
var dest_str = try std.ArrayList(u8).initCapacity(allocator, total_len);
|
||||||
defer dest_str.deinit();
|
defer dest_str.deinit(allocator);
|
||||||
var signed_headers = try std.ArrayList(u8).initCapacity(allocator, total_name_len);
|
var signed_headers = try std.ArrayList(u8).initCapacity(allocator, total_name_len);
|
||||||
defer signed_headers.deinit();
|
defer signed_headers.deinit(allocator);
|
||||||
var first = true;
|
var first = true;
|
||||||
for (dest.items) |h| {
|
for (dest.items) |h| {
|
||||||
dest_str.appendSliceAssumeCapacity(h.name);
|
dest_str.appendSliceAssumeCapacity(h.name);
|
||||||
|
@ -926,8 +926,8 @@ fn canonicalHeaders(allocator: std.mem.Allocator, headers: []const std.http.Head
|
||||||
signed_headers.appendSliceAssumeCapacity(h.name);
|
signed_headers.appendSliceAssumeCapacity(h.name);
|
||||||
}
|
}
|
||||||
return CanonicalHeaders{
|
return CanonicalHeaders{
|
||||||
.str = try dest_str.toOwnedSlice(),
|
.str = try dest_str.toOwnedSlice(allocator),
|
||||||
.signed_headers = try signed_headers.toOwnedSlice(),
|
.signed_headers = try signed_headers.toOwnedSlice(allocator),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,7 +972,7 @@ fn hash(allocator: std.mem.Allocator, payload: []const u8, sig_type: SignatureTy
|
||||||
};
|
};
|
||||||
var out: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
|
var out: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
|
||||||
std.crypto.hash.sha2.Sha256.hash(to_hash, &out, .{});
|
std.crypto.hash.sha2.Sha256.hash(to_hash, &out, .{});
|
||||||
return try std.fmt.allocPrint(allocator, "{s}", .{std.fmt.fmtSliceHexLower(&out)});
|
return try std.fmt.allocPrint(allocator, "{x}", .{out});
|
||||||
}
|
}
|
||||||
// SignedHeaders + '\n' +
|
// SignedHeaders + '\n' +
|
||||||
// HexEncode(Hash(RequestPayload))
|
// HexEncode(Hash(RequestPayload))
|
||||||
|
|
47
src/case.zig
47
src/case.zig
|
@ -1,47 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
||||||
|
|
||||||
pub fn snakeToCamel(allocator: std.mem.Allocator, name: []const u8) ![]u8 {
|
|
||||||
var utf8_name = (std.unicode.Utf8View.init(name) catch unreachable).iterator();
|
|
||||||
var target_inx: usize = 0;
|
|
||||||
var previous_ascii: u8 = 0;
|
|
||||||
var rc = try allocator.alloc(u8, name.len);
|
|
||||||
while (utf8_name.nextCodepoint()) |cp| {
|
|
||||||
if (cp > 0xff) return error.UnicodeNotSupported;
|
|
||||||
const ascii_char: u8 = @truncate(cp);
|
|
||||||
if (ascii_char != '_') {
|
|
||||||
if (previous_ascii == '_' and ascii_char >= 'a' and ascii_char <= 'z') {
|
|
||||||
const uppercase_char = ascii_char - ('a' - 'A');
|
|
||||||
rc[target_inx] = uppercase_char;
|
|
||||||
} else {
|
|
||||||
rc[target_inx] = ascii_char;
|
|
||||||
}
|
|
||||||
target_inx = target_inx + 1;
|
|
||||||
}
|
|
||||||
previous_ascii = ascii_char;
|
|
||||||
}
|
|
||||||
// Do we care if the allocator refuses resize?
|
|
||||||
_ = allocator.resize(rc, target_inx);
|
|
||||||
return rc[0..target_inx];
|
|
||||||
}
|
|
||||||
pub fn snakeToPascal(allocator: std.mem.Allocator, name: []const u8) ![]u8 {
|
|
||||||
const rc = try snakeToCamel(allocator, name);
|
|
||||||
if (rc[0] >= 'a' and rc[0] <= 'z') {
|
|
||||||
const uppercase_char = rc[0] - ('a' - 'A');
|
|
||||||
rc[0] = uppercase_char;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
test "converts from snake to camelCase" {
|
|
||||||
const allocator = std.testing.allocator;
|
|
||||||
const camel = try snakeToCamel(allocator, "access_key_id");
|
|
||||||
defer allocator.free(camel);
|
|
||||||
try expectEqualStrings("accessKeyId", camel);
|
|
||||||
}
|
|
||||||
test "single word" {
|
|
||||||
const allocator = std.testing.allocator;
|
|
||||||
const camel = try snakeToCamel(allocator, "word");
|
|
||||||
defer allocator.free(camel);
|
|
||||||
try expectEqualStrings("word", camel);
|
|
||||||
}
|
|
23
src/main.zig
23
src/main.zig
|
@ -34,7 +34,8 @@ pub fn log(
|
||||||
// Print the message to stderr, silently ignoring any errors
|
// Print the message to stderr, silently ignoring any errors
|
||||||
std.debug.lockStdErr();
|
std.debug.lockStdErr();
|
||||||
defer std.debug.unlockStdErr();
|
defer std.debug.unlockStdErr();
|
||||||
const stderr = std.io.getStdErr().writer();
|
var stderr_writer = std.fs.File.stderr().writer(&.{});
|
||||||
|
const stderr = &stderr_writer.interface;
|
||||||
nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
|
nosuspend stderr.print(prefix ++ format ++ "\n", args) catch return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,14 +63,14 @@ pub fn main() anyerror!void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
var tests = std.ArrayList(Tests).init(allocator);
|
var tests = try std.ArrayList(Tests).initCapacity(allocator, @typeInfo(Tests).@"enum".fields.len);
|
||||||
defer tests.deinit();
|
defer tests.deinit(allocator);
|
||||||
var args = try std.process.argsWithAllocator(allocator);
|
var args = try std.process.argsWithAllocator(allocator);
|
||||||
defer args.deinit();
|
defer args.deinit();
|
||||||
const stdout_raw = std.io.getStdOut().writer();
|
var stdout_buf: [4096]u8 = undefined;
|
||||||
var bw = std.io.bufferedWriter(stdout_raw);
|
const stdout_raw = std.fs.File.stdout().writer(&stdout_buf);
|
||||||
defer bw.flush() catch unreachable;
|
var stdout = stdout_raw.interface;
|
||||||
const stdout = bw.writer();
|
defer stdout.flush() catch @panic("could not flush stdout");
|
||||||
var arg0: ?[]const u8 = null;
|
var arg0: ?[]const u8 = null;
|
||||||
var proxy: ?std.http.Client.Proxy = null;
|
var proxy: ?std.http.Client.Proxy = null;
|
||||||
while (args.next()) |arg| {
|
while (args.next()) |arg| {
|
||||||
|
@ -99,14 +100,14 @@ pub fn main() anyerror!void {
|
||||||
}
|
}
|
||||||
inline for (@typeInfo(Tests).@"enum".fields) |f| {
|
inline for (@typeInfo(Tests).@"enum".fields) |f| {
|
||||||
if (std.mem.eql(u8, f.name, arg)) {
|
if (std.mem.eql(u8, f.name, arg)) {
|
||||||
try tests.append(@field(Tests, f.name));
|
try tests.append(allocator, @field(Tests, f.name));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tests.items.len == 0) {
|
if (tests.items.len == 0) {
|
||||||
inline for (@typeInfo(Tests).@"enum".fields) |f|
|
inline for (@typeInfo(Tests).@"enum".fields) |f|
|
||||||
try tests.append(@field(Tests, f.name));
|
try tests.append(allocator, @field(Tests, f.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
std.log.info("Start\n", .{});
|
std.log.info("Start\n", .{});
|
||||||
|
@ -193,7 +194,7 @@ pub fn main() anyerror!void {
|
||||||
const arn = func.function_arn.?;
|
const arn = func.function_arn.?;
|
||||||
// This is a bit ugly. Maybe a helper function in the library would help?
|
// This is a bit ugly. Maybe a helper function in the library would help?
|
||||||
var tags = try std.ArrayList(aws.services.lambda.TagKeyValue).initCapacity(allocator, 1);
|
var tags = try std.ArrayList(aws.services.lambda.TagKeyValue).initCapacity(allocator, 1);
|
||||||
defer tags.deinit();
|
defer tags.deinit(allocator);
|
||||||
tags.appendAssumeCapacity(.{ .key = "Foo", .value = "Bar" });
|
tags.appendAssumeCapacity(.{ .key = "Foo", .value = "Bar" });
|
||||||
const req = services.lambda.tag_resource.Request{ .resource = arn, .tags = tags.items };
|
const req = services.lambda.tag_resource.Request{ .resource = arn, .tags = tags.items };
|
||||||
const addtag = try aws.Request(services.lambda.tag_resource).call(req, options);
|
const addtag = try aws.Request(services.lambda.tag_resource).call(req, options);
|
||||||
|
@ -262,7 +263,7 @@ pub fn main() anyerror!void {
|
||||||
defer result.deinit();
|
defer result.deinit();
|
||||||
std.log.info("request id: {s}", .{result.response_metadata.request_id});
|
std.log.info("request id: {s}", .{result.response_metadata.request_id});
|
||||||
const list = result.response.key_group_list.?;
|
const list = result.response.key_group_list.?;
|
||||||
std.log.info("key group list max: {?d}", .{list.max_items});
|
std.log.info("key group list max: {d}", .{list.max_items});
|
||||||
std.log.info("key group quantity: {d}", .{list.quantity});
|
std.log.info("key group quantity: {d}", .{list.quantity});
|
||||||
},
|
},
|
||||||
.rest_xml_work_with_s3 => {
|
.rest_xml_work_with_s3 => {
|
||||||
|
|
|
@ -14,7 +14,7 @@ pub fn Services(comptime service_imports: anytype) type {
|
||||||
.type = @TypeOf(import_field),
|
.type = @TypeOf(import_field),
|
||||||
.default_value_ptr = &import_field,
|
.default_value_ptr = &import_field,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = 0,
|
.alignment = std.meta.alignment(@TypeOf(import_field)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/url.zig
20
src/url.zig
|
@ -11,7 +11,7 @@ pub const EncodingOptions = struct {
|
||||||
field_name_transformer: fieldNameTransformerFn = defaultTransformer,
|
field_name_transformer: fieldNameTransformerFn = defaultTransformer,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn encode(allocator: std.mem.Allocator, obj: anytype, writer: anytype, comptime options: EncodingOptions) !void {
|
pub fn encode(allocator: std.mem.Allocator, obj: anytype, writer: *std.Io.Writer, comptime options: EncodingOptions) !void {
|
||||||
_ = try encodeInternal(allocator, "", "", true, obj, writer, options);
|
_ = try encodeInternal(allocator, "", "", true, obj, writer, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ fn encodeStruct(
|
||||||
parent: []const u8,
|
parent: []const u8,
|
||||||
first: bool,
|
first: bool,
|
||||||
obj: anytype,
|
obj: anytype,
|
||||||
writer: anytype,
|
writer: *std.Io.Writer,
|
||||||
comptime options: EncodingOptions,
|
comptime options: EncodingOptions,
|
||||||
) !bool {
|
) !bool {
|
||||||
var rc = first;
|
var rc = first;
|
||||||
|
@ -41,7 +41,7 @@ pub fn encodeInternal(
|
||||||
field_name: []const u8,
|
field_name: []const u8,
|
||||||
first: bool,
|
first: bool,
|
||||||
obj: anytype,
|
obj: anytype,
|
||||||
writer: anytype,
|
writer: *std.Io.Writer,
|
||||||
comptime options: EncodingOptions,
|
comptime options: EncodingOptions,
|
||||||
) !bool {
|
) !bool {
|
||||||
// @compileLog(@typeName(@TypeOf(obj)));
|
// @compileLog(@typeName(@TypeOf(obj)));
|
||||||
|
@ -56,10 +56,18 @@ pub fn encodeInternal(
|
||||||
} else {
|
} else {
|
||||||
if (!first) _ = try writer.write("&");
|
if (!first) _ = try writer.write("&");
|
||||||
// @compileLog(@typeInfo(@TypeOf(obj)));
|
// @compileLog(@typeInfo(@TypeOf(obj)));
|
||||||
if (ti.child == []const u8 or ti.child == u8)
|
switch (ti.child) {
|
||||||
try writer.print("{s}{s}={s}", .{ parent, field_name, obj })
|
// TODO: not sure this first one is valid. How should [][]const u8 be serialized here?
|
||||||
else
|
[]const u8 => {
|
||||||
|
std.log.warn(
|
||||||
|
"encoding object of type [][]const u8...pretty sure this is wrong {s}{s}={any}",
|
||||||
|
.{ parent, field_name, obj },
|
||||||
|
);
|
||||||
try writer.print("{s}{s}={any}", .{ parent, field_name, obj });
|
try writer.print("{s}{s}={any}", .{ parent, field_name, obj });
|
||||||
|
},
|
||||||
|
u8 => try writer.print("{s}{s}={s}", .{ parent, field_name, obj }),
|
||||||
|
else => try writer.print("{s}{s}={any}", .{ parent, field_name, obj }),
|
||||||
|
}
|
||||||
rc = false;
|
rc = false;
|
||||||
},
|
},
|
||||||
.@"struct" => if (std.mem.eql(u8, "", field_name)) {
|
.@"struct" => if (std.mem.eql(u8, "", field_name)) {
|
||||||
|
|
10
src/xml.zig
10
src/xml.zig
|
@ -26,12 +26,14 @@ pub const Element = struct {
|
||||||
attributes: AttributeList,
|
attributes: AttributeList,
|
||||||
children: ContentList,
|
children: ContentList,
|
||||||
next_sibling: ?*Element = null,
|
next_sibling: ?*Element = null,
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
|
||||||
fn init(tag: []const u8, alloc: Allocator) Element {
|
fn init(tag: []const u8, alloc: Allocator) Element {
|
||||||
return .{
|
return .{
|
||||||
.tag = tag,
|
.tag = tag,
|
||||||
.attributes = AttributeList.init(alloc),
|
.attributes = AttributeList{},
|
||||||
.children = ContentList.init(alloc),
|
.children = ContentList{},
|
||||||
|
.allocator = alloc,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,7 +456,7 @@ fn tryParseElement(ctx: *ParseContext, alloc: Allocator, parent: ?*Element) !?*E
|
||||||
|
|
||||||
while (ctx.eatWs()) {
|
while (ctx.eatWs()) {
|
||||||
const attr = (try tryParseAttr(ctx, alloc)) orelse break;
|
const attr = (try tryParseAttr(ctx, alloc)) orelse break;
|
||||||
try element.attributes.append(attr);
|
try element.attributes.append(element.allocator, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.eatStr("/>")) {
|
if (ctx.eatStr("/>")) {
|
||||||
|
@ -471,7 +473,7 @@ fn tryParseElement(ctx: *ParseContext, alloc: Allocator, parent: ?*Element) !?*E
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = try parseContent(ctx, alloc, element);
|
const content = try parseContent(ctx, alloc, element);
|
||||||
try element.children.append(content);
|
try element.children.append(element.allocator, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
const closing_tag = try parseNameNoDupe(ctx);
|
const closing_tag = try parseNameNoDupe(ctx);
|
||||||
|
|
|
@ -53,7 +53,7 @@ pub const XmlSerializeError = error{
|
||||||
pub fn stringify(
|
pub fn stringify(
|
||||||
value: anytype,
|
value: anytype,
|
||||||
options: StringifyOptions,
|
options: StringifyOptions,
|
||||||
writer: anytype,
|
writer: *std.Io.Writer,
|
||||||
) !void {
|
) !void {
|
||||||
// Write XML declaration if requested
|
// Write XML declaration if requested
|
||||||
if (options.include_declaration)
|
if (options.include_declaration)
|
||||||
|
@ -62,9 +62,9 @@ pub fn stringify(
|
||||||
// Start serialization with the root element
|
// Start serialization with the root element
|
||||||
const root_name = options.root_name;
|
const root_name = options.root_name;
|
||||||
if (@typeInfo(@TypeOf(value)) != .optional or value == null)
|
if (@typeInfo(@TypeOf(value)) != .optional or value == null)
|
||||||
try serializeValue(value, root_name, options, writer.any(), 0)
|
try serializeValue(value, root_name, options, writer, 0)
|
||||||
else
|
else
|
||||||
try serializeValue(value.?, root_name, options, writer.any(), 0);
|
try serializeValue(value.?, root_name, options, writer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes a value to XML and returns an allocated string
|
/// Serializes a value to XML and returns an allocated string
|
||||||
|
@ -73,10 +73,10 @@ pub fn stringifyAlloc(
|
||||||
value: anytype,
|
value: anytype,
|
||||||
options: StringifyOptions,
|
options: StringifyOptions,
|
||||||
) ![]u8 {
|
) ![]u8 {
|
||||||
var list = std.ArrayList(u8).init(allocator);
|
var list = std.Io.Writer.Allocating.init(allocator);
|
||||||
errdefer list.deinit();
|
defer list.deinit();
|
||||||
|
|
||||||
try stringify(value, options, list.writer());
|
try stringify(value, options, &list.writer);
|
||||||
return list.toOwnedSlice();
|
return list.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ fn serializeValue(
|
||||||
value: anytype,
|
value: anytype,
|
||||||
element_name: ?[]const u8,
|
element_name: ?[]const u8,
|
||||||
options: StringifyOptions,
|
options: StringifyOptions,
|
||||||
writer: anytype,
|
writer: *std.Io.Writer,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) !void {
|
) !void {
|
||||||
const T = @TypeOf(value);
|
const T = @TypeOf(value);
|
||||||
|
@ -274,7 +274,7 @@ fn serializeValue(
|
||||||
try writeClose(writer, element_name);
|
try writeClose(writer, element_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeClose(writer: anytype, element_name: ?[]const u8) !void {
|
fn writeClose(writer: *std.Io.Writer, element_name: ?[]const u8) !void {
|
||||||
// Close element tag
|
// Close element tag
|
||||||
if (element_name) |n| {
|
if (element_name) |n| {
|
||||||
try writer.writeAll("</");
|
try writer.writeAll("</");
|
||||||
|
@ -284,7 +284,7 @@ fn writeClose(writer: anytype, element_name: ?[]const u8) !void {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes indentation based on depth and indent level
|
/// Writes indentation based on depth and indent level
|
||||||
fn writeIndent(writer: anytype, depth: usize, whitespace: StringifyOptions.Whitespace) @TypeOf(writer).Error!void {
|
fn writeIndent(writer: *std.Io.Writer, depth: usize, whitespace: StringifyOptions.Whitespace) std.Io.Writer.Error!void {
|
||||||
var char: u8 = ' ';
|
var char: u8 = ' ';
|
||||||
const n_chars = switch (whitespace) {
|
const n_chars = switch (whitespace) {
|
||||||
.minified => return,
|
.minified => return,
|
||||||
|
@ -298,16 +298,16 @@ fn writeIndent(writer: anytype, depth: usize, whitespace: StringifyOptions.White
|
||||||
break :blk depth;
|
break :blk depth;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
try writer.writeByteNTimes(char, n_chars);
|
try writer.splatBytesAll(&.{char}, n_chars);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serializeString(
|
fn serializeString(
|
||||||
writer: anytype,
|
writer: *std.Io.Writer,
|
||||||
element_name: ?[]const u8,
|
element_name: ?[]const u8,
|
||||||
value: []const u8,
|
value: []const u8,
|
||||||
options: StringifyOptions,
|
options: StringifyOptions,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) @TypeOf(writer).Error!void {
|
) error{ WriteFailed, OutOfMemory }!void {
|
||||||
if (options.emit_strings_as_arrays) {
|
if (options.emit_strings_as_arrays) {
|
||||||
// if (true) return error.seestackrun;
|
// if (true) return error.seestackrun;
|
||||||
for (value) |c| {
|
for (value) |c| {
|
||||||
|
@ -333,7 +333,7 @@ fn serializeString(
|
||||||
try escapeString(writer, value);
|
try escapeString(writer, value);
|
||||||
}
|
}
|
||||||
/// Escapes special characters in XML strings
|
/// Escapes special characters in XML strings
|
||||||
fn escapeString(writer: anytype, value: []const u8) @TypeOf(writer).Error!void {
|
fn escapeString(writer: *std.Io.Writer, value: []const u8) std.Io.Writer.Error!void {
|
||||||
for (value) |c| {
|
for (value) |c| {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
'&' => try writer.writeAll("&"),
|
'&' => try writer.writeAll("&"),
|
||||||
|
|
|
@ -381,14 +381,17 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
||||||
|
|
||||||
log.debug("type = {s}, style = {s}, ptr_info.child == {s}, element = {s}", .{ @typeName(T), @tagName(array_style), @typeName(ptr_info.child), element.tag });
|
log.debug("type = {s}, style = {s}, ptr_info.child == {s}, element = {s}", .{ @typeName(T), @tagName(array_style), @typeName(ptr_info.child), element.tag });
|
||||||
|
|
||||||
var children = std.ArrayList(ptr_info.child).init(allocator);
|
var children = std.ArrayList(ptr_info.child){};
|
||||||
defer children.deinit();
|
defer children.deinit(allocator);
|
||||||
|
|
||||||
switch (array_style) {
|
switch (array_style) {
|
||||||
.collection => {
|
.collection => {
|
||||||
var iterator = element.elements();
|
var iterator = element.elements();
|
||||||
while (iterator.next()) |child_element| {
|
while (iterator.next()) |child_element| {
|
||||||
try children.append(try parseInternal(ptr_info.child, child_element, options));
|
try children.append(
|
||||||
|
allocator,
|
||||||
|
try parseInternal(ptr_info.child, child_element, options),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.repeated_root => {
|
.repeated_root => {
|
||||||
|
@ -396,12 +399,15 @@ fn parseInternal(comptime T: type, element: *xml.Element, options: ParseOptions)
|
||||||
while (current) |el| : (current = el.next_sibling) {
|
while (current) |el| : (current = el.next_sibling) {
|
||||||
if (!std.mem.eql(u8, el.tag, element.tag)) continue;
|
if (!std.mem.eql(u8, el.tag, element.tag)) continue;
|
||||||
|
|
||||||
try children.append(try parseInternal(ptr_info.child, el, options));
|
try children.append(
|
||||||
|
allocator,
|
||||||
|
try parseInternal(ptr_info.child, el, options),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return children.toOwnedSlice();
|
return children.toOwnedSlice(allocator);
|
||||||
}
|
}
|
||||||
return try allocator.dupe(u8, element.children.items[0].CharData);
|
return try allocator.dupe(u8, element.children.items[0].CharData);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue