chore: wip refactor of FullResponse to use arena allocator to simplify memory management
This commit is contained in:
parent
bd2aede64e
commit
5a8cceaa0b
1 changed files with 117 additions and 114 deletions
231
src/aws.zig
231
src/aws.zig
|
@ -13,6 +13,9 @@ const xml_serializer = @import("xml_serializer.zig");
|
||||||
|
|
||||||
const scoped_log = std.log.scoped(.aws);
|
const scoped_log = std.log.scoped(.aws);
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||||
|
|
||||||
/// control all logs directly/indirectly used by aws sdk. Not recommended for
|
/// control all logs directly/indirectly used by aws sdk. Not recommended for
|
||||||
/// use under normal circumstances, but helpful for times when the zig logging
|
/// use under normal circumstances, but helpful for times when the zig logging
|
||||||
/// controls are insufficient (e.g. use in build script)
|
/// controls are insufficient (e.g. use in build script)
|
||||||
|
@ -92,7 +95,7 @@ pub const Options = struct {
|
||||||
pub const Diagnostics = struct {
|
pub const Diagnostics = struct {
|
||||||
http_code: i64,
|
http_code: i64,
|
||||||
response_body: []const u8,
|
response_body: []const u8,
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
pub fn deinit(self: *Diagnostics) void {
|
pub fn deinit(self: *Diagnostics) void {
|
||||||
self.allocator.free(self.response_body);
|
self.allocator.free(self.response_body);
|
||||||
|
@ -114,12 +117,12 @@ pub const ClientOptions = struct {
|
||||||
proxy: ?std.http.Client.Proxy = null,
|
proxy: ?std.http.Client.Proxy = null,
|
||||||
};
|
};
|
||||||
pub const Client = struct {
|
pub const Client = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
aws_http: awshttp.AwsHttp,
|
aws_http: awshttp.AwsHttp,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, options: ClientOptions) Self {
|
pub fn init(allocator: Allocator, options: ClientOptions) Self {
|
||||||
return Self{
|
return Self{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.aws_http = awshttp.AwsHttp.init(allocator, options.proxy),
|
.aws_http = awshttp.AwsHttp.init(allocator, options.proxy),
|
||||||
|
@ -229,7 +232,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
// 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.ArrayList(u8).init(options.client.allocator);
|
||||||
defer buffer.deinit();
|
defer buffer.deinit();
|
||||||
var nameAllocator = std.heap.ArenaAllocator.init(options.client.allocator);
|
var nameAllocator = ArenaAllocator.init(options.client.allocator);
|
||||||
defer nameAllocator.deinit();
|
defer nameAllocator.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)) {
|
||||||
|
@ -326,7 +329,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
// 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
|
||||||
//
|
//
|
||||||
var nameAllocator = std.heap.ArenaAllocator.init(options.client.allocator);
|
var nameAllocator = ArenaAllocator.init(options.client.allocator);
|
||||||
defer nameAllocator.deinit();
|
defer nameAllocator.deinit();
|
||||||
try json.stringify(request, .{ .whitespace = .{} }, buffer.writer());
|
try json.stringify(request, .{ .whitespace = .{} }, buffer.writer());
|
||||||
|
|
||||||
|
@ -359,13 +362,16 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
const continuation = if (buffer.items.len > 0) "&" else "";
|
const continuation = if (buffer.items.len > 0) "&" else "";
|
||||||
|
|
||||||
const query = if (Self.service_meta.aws_protocol == .query)
|
const query = if (Self.service_meta.aws_protocol == .query)
|
||||||
try std.fmt.allocPrint(options.client.allocator, "", .{})
|
""
|
||||||
else // EC2
|
else // EC2
|
||||||
try std.fmt.allocPrint(options.client.allocator, "?Action={s}&Version={s}", .{
|
try std.fmt.allocPrint(options.client.allocator, "?Action={s}&Version={s}", .{
|
||||||
action.action_name,
|
action.action_name,
|
||||||
Self.service_meta.version,
|
Self.service_meta.version,
|
||||||
});
|
});
|
||||||
defer options.client.allocator.free(query);
|
|
||||||
|
defer if (Self.service_meta.aws_protocol != .query) {
|
||||||
|
options.client.allocator.free(query);
|
||||||
|
};
|
||||||
|
|
||||||
// Note: EC2 avoided the Action={s}&Version={s} in the body, but it's
|
// Note: EC2 avoided the Action={s}&Version={s} in the body, but it's
|
||||||
// but it's required, so I'm not sure why that code was put in
|
// but it's required, so I'm not sure why that code was put in
|
||||||
|
@ -378,6 +384,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
buffer.items,
|
buffer.items,
|
||||||
});
|
});
|
||||||
defer options.client.allocator.free(body);
|
defer options.client.allocator.free(body);
|
||||||
|
|
||||||
return try Self.callAws(.{
|
return try Self.callAws(.{
|
||||||
.query = query,
|
.query = query,
|
||||||
.body = body,
|
.body = body,
|
||||||
|
@ -465,7 +472,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setHeaderValue(
|
fn setHeaderValue(
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
response: anytype,
|
response: anytype,
|
||||||
comptime field_name: []const u8,
|
comptime field_name: []const u8,
|
||||||
comptime field_type: type,
|
comptime field_type: type,
|
||||||
|
@ -491,22 +498,23 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
expected_body_field_len -= std.meta.fields(@TypeOf(action.Response.http_header)).len;
|
expected_body_field_len -= std.meta.fields(@TypeOf(action.Response.http_header)).len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buf_request_id: [256]u8 = undefined;
|
||||||
|
const request_id = try requestIdFromHeaders(&buf_request_id, options.client.allocator, aws_request, response);
|
||||||
|
|
||||||
if (@hasDecl(action.Response, "http_payload")) {
|
if (@hasDecl(action.Response, "http_payload")) {
|
||||||
var rc = FullResponseType{
|
var rc = try FullResponseType.init(.{
|
||||||
|
.arena = ArenaAllocator.init(options.client.allocator),
|
||||||
.response = .{},
|
.response = .{},
|
||||||
.response_metadata = .{
|
.request_id = request_id,
|
||||||
.request_id = try requestIdFromHeaders(aws_request, response, options),
|
|
||||||
},
|
|
||||||
.parser_options = .{ .json = .{} },
|
|
||||||
.raw_parsed = .{ .raw = .{} },
|
.raw_parsed = .{ .raw = .{} },
|
||||||
.allocator = options.client.allocator,
|
});
|
||||||
};
|
|
||||||
const body_field = @field(rc.response, action.Response.http_payload);
|
const body_field = @field(rc.response, action.Response.http_payload);
|
||||||
const BodyField = @TypeOf(body_field);
|
const BodyField = @TypeOf(body_field);
|
||||||
if (BodyField == []const u8 or BodyField == ?[]const u8) {
|
if (BodyField == []const u8 or BodyField == ?[]const u8) {
|
||||||
expected_body_field_len = 0;
|
expected_body_field_len = 0;
|
||||||
// We can't use body_field for this set - only @field will work
|
// We can't use body_field for this set - only @field will work
|
||||||
@field(rc.response, action.Response.http_payload) = try options.client.allocator.dupe(u8, response.body);
|
// @field(rc.response, action.Response.http_payload) = try rc.arena.allocator().dupe(u8, response.body);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
rc.deinit();
|
rc.deinit();
|
||||||
|
@ -515,15 +523,12 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
// We don't care about the body if there are no fields we expect there...
|
// We don't care about the body if there are no fields we expect there...
|
||||||
if (std.meta.fields(action.Response).len == 0 or expected_body_field_len == 0 or response.body.len == 0) {
|
if (std.meta.fields(action.Response).len == 0 or expected_body_field_len == 0 or response.body.len == 0) {
|
||||||
// Do we care if an unexpected body comes in?
|
// Do we care if an unexpected body comes in?
|
||||||
return FullResponseType{
|
return try FullResponseType.init(.{
|
||||||
|
.arena = ArenaAllocator.init(options.client.allocator),
|
||||||
.response = undefined,
|
.response = undefined,
|
||||||
.response_metadata = .{
|
.request_id = request_id,
|
||||||
.request_id = try requestIdFromHeaders(aws_request, response, options),
|
|
||||||
},
|
|
||||||
.parser_options = .{ .json = .{} },
|
|
||||||
.raw_parsed = .{ .raw = undefined },
|
.raw_parsed = .{ .raw = undefined },
|
||||||
.allocator = options.client.allocator,
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return switch (try getContentType(response.headers)) {
|
return switch (try getContentType(response.headers)) {
|
||||||
|
@ -570,26 +575,24 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
// We can grab index [0] as structs are guaranteed by zig to be returned in the order
|
// We can grab index [0] as structs are guaranteed by zig to be returned in the order
|
||||||
// declared, and we're declaring in that order in ServerResponse().
|
// declared, and we're declaring in that order in ServerResponse().
|
||||||
const real_response = @field(parsed_response, @typeInfo(response_types.NormalResponse).@"struct".fields[0].name);
|
const real_response = @field(parsed_response, @typeInfo(response_types.NormalResponse).@"struct".fields[0].name);
|
||||||
return FullResponseType{
|
|
||||||
|
return try FullResponseType.init(.{
|
||||||
|
.arena = ArenaAllocator.init(options.client.allocator),
|
||||||
.response = @field(real_response, @typeInfo(@TypeOf(real_response)).@"struct".fields[0].name),
|
.response = @field(real_response, @typeInfo(@TypeOf(real_response)).@"struct".fields[0].name),
|
||||||
.response_metadata = .{
|
.request_id = real_response.ResponseMetadata.RequestId,
|
||||||
.request_id = try options.client.allocator.dupe(u8, real_response.ResponseMetadata.RequestId),
|
|
||||||
},
|
|
||||||
.parser_options = .{ .json = parser_options },
|
|
||||||
.raw_parsed = .{ .server = parsed_response },
|
.raw_parsed = .{ .server = parsed_response },
|
||||||
.allocator = options.client.allocator,
|
});
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
// Conditions 2 or 3 (no wrapping)
|
// Conditions 2 or 3 (no wrapping)
|
||||||
return FullResponseType{
|
var buf_request_id: [256]u8 = undefined;
|
||||||
|
const request_id = try requestIdFromHeaders(&buf_request_id, options.client.allocator, aws_request, response);
|
||||||
|
|
||||||
|
return try FullResponseType.init(.{
|
||||||
|
.arena = ArenaAllocator.init(options.client.allocator),
|
||||||
.response = parsed_response,
|
.response = parsed_response,
|
||||||
.response_metadata = .{
|
.request_id = request_id,
|
||||||
.request_id = try requestIdFromHeaders(aws_request, response, options),
|
|
||||||
},
|
|
||||||
.parser_options = .{ .json = parser_options },
|
|
||||||
.raw_parsed = .{ .raw = parsed_response },
|
.raw_parsed = .{ .raw = parsed_response },
|
||||||
.allocator = options.client.allocator,
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,23 +665,21 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
defer if (free_body) options.client.allocator.free(body);
|
defer if (free_body) options.client.allocator.free(body);
|
||||||
const parsed = try xml_shaper.parse(action.Response, body, xml_options);
|
const parsed = try xml_shaper.parse(action.Response, body, xml_options);
|
||||||
errdefer parsed.deinit();
|
errdefer parsed.deinit();
|
||||||
// This needs to get into FullResponseType somehow: defer parsed.deinit();
|
|
||||||
const request_id = blk: {
|
|
||||||
if (parsed.document.root.getCharData("requestId")) |elem|
|
|
||||||
break :blk try options.client.allocator.dupe(u8, elem);
|
|
||||||
break :blk try requestIdFromHeaders(request, result, options);
|
|
||||||
};
|
|
||||||
defer options.client.allocator.free(request_id);
|
|
||||||
|
|
||||||
return FullResponseType{
|
var buf_request_id: [256]u8 = undefined;
|
||||||
.response = parsed.parsed_value,
|
const request_id = blk: {
|
||||||
.response_metadata = .{
|
if (parsed.document.root.getCharData("requestId")) |elem| {
|
||||||
.request_id = try options.client.allocator.dupe(u8, request_id),
|
break :blk elem;
|
||||||
},
|
}
|
||||||
.parser_options = .{ .xml = xml_options },
|
break :blk try requestIdFromHeaders(&buf_request_id, options.client.allocator, request, result);
|
||||||
.raw_parsed = .{ .xml = parsed },
|
|
||||||
.allocator = options.client.allocator,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return try FullResponseType.init(.{
|
||||||
|
.arena = ArenaAllocator.init(options.client.allocator),
|
||||||
|
.response = parsed.parsed_value,
|
||||||
|
.request_id = request_id,
|
||||||
|
.raw_parsed = .{ .xml = parsed },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const ServerResponseTypes = struct {
|
const ServerResponseTypes = struct {
|
||||||
NormalResponse: type,
|
NormalResponse: type,
|
||||||
|
@ -741,7 +742,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
fn ParsedJsonData(comptime T: type) type {
|
fn ParsedJsonData(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
parsed_response_ptr: *T,
|
parsed_response_ptr: *T,
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
const MySelf = @This();
|
const MySelf = @This();
|
||||||
|
|
||||||
|
@ -754,6 +755,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
fn parseJsonData(comptime response_types: ServerResponseTypes, data: []const u8, options: Options, parser_options: json.ParseOptions) !ParsedJsonData(response_types.NormalResponse) {
|
fn parseJsonData(comptime response_types: ServerResponseTypes, data: []const u8, options: Options, parser_options: json.ParseOptions) !ParsedJsonData(response_types.NormalResponse) {
|
||||||
// Now it's time to start looking at the actual data. Job 1 will
|
// Now it's time to start looking at the actual data. Job 1 will
|
||||||
// be to figure out if this is a raw response or wrapped
|
// be to figure out if this is a raw response or wrapped
|
||||||
|
const allocator = options.client.allocator;
|
||||||
|
|
||||||
// Extract the first json key
|
// Extract the first json key
|
||||||
const key = firstJsonKey(data);
|
const key = firstJsonKey(data);
|
||||||
|
@ -763,8 +765,8 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
isOtherNormalResponse(response_types.NormalResponse, key);
|
isOtherNormalResponse(response_types.NormalResponse, key);
|
||||||
var stream = json.TokenStream.init(data);
|
var stream = json.TokenStream.init(data);
|
||||||
const parsed_response_ptr = blk: {
|
const parsed_response_ptr = blk: {
|
||||||
const ptr = try options.client.allocator.create(response_types.NormalResponse);
|
const ptr = try allocator.create(response_types.NormalResponse);
|
||||||
errdefer options.client.allocator.destroy(ptr);
|
errdefer allocator.destroy(ptr);
|
||||||
|
|
||||||
if (!response_types.isRawPossible or found_normal_json_response) {
|
if (!response_types.isRawPossible or found_normal_json_response) {
|
||||||
ptr.* = (json.parse(response_types.NormalResponse, &stream, parser_options) catch |e| {
|
ptr.* = (json.parse(response_types.NormalResponse, &stream, parser_options) catch |e| {
|
||||||
|
@ -807,7 +809,7 @@ pub fn Request(comptime request_action: anytype) type {
|
||||||
};
|
};
|
||||||
return ParsedJsonData(response_types.NormalResponse){
|
return ParsedJsonData(response_types.NormalResponse){
|
||||||
.parsed_response_ptr = parsed_response_ptr,
|
.parsed_response_ptr = parsed_response_ptr,
|
||||||
.allocator = options.client.allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -861,14 +863,14 @@ fn parseInt(comptime T: type, val: []const u8) !T {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generalAllocPrint(allocator: std.mem.Allocator, val: anytype) !?[]const u8 {
|
fn generalAllocPrint(allocator: Allocator, val: anytype) !?[]const u8 {
|
||||||
switch (@typeInfo(@TypeOf(val))) {
|
switch (@typeInfo(@TypeOf(val))) {
|
||||||
.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 => return try std.fmt.allocPrint(allocator, "{s}", .{val}),
|
||||||
else => return try std.fmt.allocPrint(allocator, "{any}", .{val}),
|
else => return try std.fmt.allocPrint(allocator, "{any}", .{val}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn headersFor(allocator: std.mem.Allocator, request: anytype) ![]awshttp.Header {
|
fn headersFor(allocator: Allocator, request: anytype) ![]awshttp.Header {
|
||||||
log.debug("Checking for headers to include for type {}", .{@TypeOf(request)});
|
log.debug("Checking for headers to include for type {}", .{@TypeOf(request)});
|
||||||
if (!@hasDecl(@TypeOf(request), "http_header")) return &[_]awshttp.Header{};
|
if (!@hasDecl(@TypeOf(request), "http_header")) return &[_]awshttp.Header{};
|
||||||
const http_header = @TypeOf(request).http_header;
|
const http_header = @TypeOf(request).http_header;
|
||||||
|
@ -892,7 +894,7 @@ fn headersFor(allocator: std.mem.Allocator, request: anytype) ![]awshttp.Header
|
||||||
return headers.toOwnedSlice();
|
return headers.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn freeHeadersFor(allocator: std.mem.Allocator, request: anytype, headers: []const awshttp.Header) void {
|
fn freeHeadersFor(allocator: Allocator, request: anytype, headers: []const awshttp.Header) void {
|
||||||
if (!@hasDecl(@TypeOf(request), "http_header")) return;
|
if (!@hasDecl(@TypeOf(request), "http_header")) return;
|
||||||
const http_header = @TypeOf(request).http_header;
|
const http_header = @TypeOf(request).http_header;
|
||||||
const fields = std.meta.fields(@TypeOf(http_header));
|
const fields = std.meta.fields(@TypeOf(http_header));
|
||||||
|
@ -951,8 +953,9 @@ fn getContentType(headers: []const awshttp.Header) !ContentType {
|
||||||
|
|
||||||
return error.ContentTypeNotFound;
|
return error.ContentTypeNotFound;
|
||||||
}
|
}
|
||||||
/// Get request ID from headers. Caller responsible for freeing memory
|
/// Get request ID from headers.
|
||||||
fn requestIdFromHeaders(request: awshttp.HttpRequest, response: awshttp.HttpResult, options: Options) ![]u8 {
|
/// Allocation is only used in case of an error. Caller does not need to free the returned buffer.
|
||||||
|
fn requestIdFromHeaders(buf: []u8, allocator: Allocator, request: awshttp.HttpRequest, response: awshttp.HttpResult) ![]u8 {
|
||||||
var rid: ?[]const u8 = null;
|
var rid: ?[]const u8 = null;
|
||||||
// This "thing" is called:
|
// This "thing" is called:
|
||||||
// * Host ID
|
// * Host ID
|
||||||
|
@ -972,11 +975,14 @@ fn requestIdFromHeaders(request: awshttp.HttpRequest, response: awshttp.HttpResu
|
||||||
host_id = header.value;
|
host_id = header.value;
|
||||||
}
|
}
|
||||||
if (rid) |r| {
|
if (rid) |r| {
|
||||||
if (host_id) |h|
|
if (host_id) |h| {
|
||||||
return try std.fmt.allocPrint(options.client.allocator, "{s}, host_id: {s}", .{ r, h });
|
return try std.fmt.bufPrint(buf, "{s}, host_id: {s}", .{ r, h });
|
||||||
return try options.client.allocator.dupe(u8, r);
|
}
|
||||||
|
|
||||||
|
@memcpy(buf[0..r.len], r);
|
||||||
|
return buf[0..r.len];
|
||||||
}
|
}
|
||||||
try reportTraffic(options.client.allocator, "Request ID not found", request, response, log.err);
|
try reportTraffic(allocator, "Request ID not found", request, response, log.err);
|
||||||
return error.RequestIdNotFound;
|
return error.RequestIdNotFound;
|
||||||
}
|
}
|
||||||
fn ServerResponse(comptime action: anytype) type {
|
fn ServerResponse(comptime action: anytype) type {
|
||||||
|
@ -1029,65 +1035,62 @@ fn ServerResponse(comptime action: anytype) type {
|
||||||
}
|
}
|
||||||
fn FullResponse(comptime action: anytype) type {
|
fn FullResponse(comptime action: anytype) type {
|
||||||
return struct {
|
return struct {
|
||||||
response: action.Response,
|
pub const ResponseMetadata = struct {
|
||||||
response_metadata: struct {
|
request_id: []const u8,
|
||||||
request_id: []u8,
|
};
|
||||||
},
|
|
||||||
parser_options: union(enum) {
|
pub const RawParsed = union(enum) {
|
||||||
json: json.ParseOptions,
|
|
||||||
xml: xml_shaper.ParseOptions,
|
|
||||||
},
|
|
||||||
raw_parsed: union(enum) {
|
|
||||||
server: ServerResponse(action),
|
server: ServerResponse(action),
|
||||||
raw: action.Response,
|
raw: action.Response,
|
||||||
xml: xml_shaper.Parsed(action.Response),
|
xml: xml_shaper.Parsed(action.Response),
|
||||||
},
|
};
|
||||||
allocator: std.mem.Allocator,
|
|
||||||
|
pub const FullResponseOptions = struct {
|
||||||
|
response: action.Response = undefined,
|
||||||
|
request_id: []const u8,
|
||||||
|
raw_parsed: RawParsed = .{ .raw = undefined },
|
||||||
|
arena: ArenaAllocator,
|
||||||
|
};
|
||||||
|
|
||||||
|
response: action.Response = undefined,
|
||||||
|
raw_parsed: RawParsed = .{ .raw = undefined },
|
||||||
|
response_metadata: ResponseMetadata,
|
||||||
|
arena: ArenaAllocator,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub fn deinit(self: Self) void {
|
|
||||||
switch (self.raw_parsed) {
|
|
||||||
// Server is json only (so far)
|
|
||||||
.server => json.parseFree(ServerResponse(action), self.raw_parsed.server, self.parser_options.json),
|
|
||||||
// Raw is json only (so far)
|
|
||||||
.raw => json.parseFree(action.Response, self.raw_parsed.raw, self.parser_options.json),
|
|
||||||
.xml => |xml| xml.deinit(),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.allocator.free(self.response_metadata.request_id);
|
pub fn init(options: FullResponseOptions) !Self {
|
||||||
const Response = @TypeOf(self.response);
|
var arena = options.arena;
|
||||||
if (@hasDecl(Response, "http_header")) {
|
const request_id = try arena.allocator().dupe(u8, options.request_id);
|
||||||
inline for (std.meta.fields(@TypeOf(Response.http_header))) |f| {
|
|
||||||
safeFree(self.allocator, @field(self.response, f.name));
|
return Self{
|
||||||
}
|
.arena = arena,
|
||||||
}
|
.response = options.response,
|
||||||
if (@hasDecl(Response, "http_payload")) {
|
.raw_parsed = options.raw_parsed,
|
||||||
const body_field = @field(self.response, Response.http_payload);
|
.response_metadata = .{
|
||||||
const BodyField = @TypeOf(body_field);
|
.request_id = request_id,
|
||||||
if (BodyField == []const u8) {
|
},
|
||||||
self.allocator.free(body_field);
|
};
|
||||||
}
|
}
|
||||||
if (BodyField == ?[]const u8) {
|
|
||||||
if (body_field) |f|
|
pub fn deinit(self: Self) void {
|
||||||
self.allocator.free(f);
|
self.arena.deinit();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
fn safeFree(allocator: std.mem.Allocator, obj: anytype) void {
|
fn safeFree(allocator: Allocator, obj: anytype) void {
|
||||||
switch (@typeInfo(@TypeOf(obj))) {
|
switch (@typeInfo(@TypeOf(obj))) {
|
||||||
.pointer => allocator.free(obj),
|
.pointer => allocator.free(obj),
|
||||||
.optional => if (obj) |o| safeFree(allocator, o),
|
.optional => if (obj) |o| safeFree(allocator, o),
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn queryFieldTransformer(allocator: std.mem.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);
|
return try case.snakeToPascal(allocator, field_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildPath(
|
fn buildPath(
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
raw_uri: []const u8,
|
raw_uri: []const u8,
|
||||||
comptime ActionRequest: type,
|
comptime ActionRequest: type,
|
||||||
request: anytype,
|
request: anytype,
|
||||||
|
@ -1174,7 +1177,7 @@ fn uriEncodeByte(char: u8, writer: anytype, encode_slash: bool) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn buildQuery(allocator: std.mem.Allocator, request: anytype) ![]const u8 {
|
fn buildQuery(allocator: Allocator, request: anytype) ![]const u8 {
|
||||||
// query should look something like this:
|
// query should look something like this:
|
||||||
// pub const http_query = .{
|
// pub const http_query = .{
|
||||||
// .master_region = "MasterRegion",
|
// .master_region = "MasterRegion",
|
||||||
|
@ -1296,7 +1299,7 @@ pub fn IgnoringWriter(comptime WriterType: type) type {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reportTraffic(
|
fn reportTraffic(
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
info: []const u8,
|
info: []const u8,
|
||||||
request: awshttp.HttpRequest,
|
request: awshttp.HttpRequest,
|
||||||
response: awshttp.HttpResult,
|
response: awshttp.HttpResult,
|
||||||
|
@ -1498,7 +1501,7 @@ test "basic json request serialization" {
|
||||||
// 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
|
||||||
//
|
//
|
||||||
var nameAllocator = std.heap.ArenaAllocator.init(allocator);
|
var nameAllocator = ArenaAllocator.init(allocator);
|
||||||
defer nameAllocator.deinit();
|
defer nameAllocator.deinit();
|
||||||
try json.stringify(request, .{ .whitespace = .{} }, buffer.writer());
|
try json.stringify(request, .{ .whitespace = .{} }, buffer.writer());
|
||||||
try std.testing.expectEqualStrings(
|
try std.testing.expectEqualStrings(
|
||||||
|
@ -1582,8 +1585,8 @@ test {
|
||||||
std.testing.refAllDecls(xml_shaper);
|
std.testing.refAllDecls(xml_shaper);
|
||||||
}
|
}
|
||||||
const TestOptions = struct {
|
const TestOptions = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
arena: ?*std.heap.ArenaAllocator = null,
|
arena: ?*ArenaAllocator = null,
|
||||||
server_port: ?u16 = null,
|
server_port: ?u16 = null,
|
||||||
server_remaining_requests: usize = 1,
|
server_remaining_requests: usize = 1,
|
||||||
server_response: []const u8 = "unset",
|
server_response: []const u8 = "unset",
|
||||||
|
@ -1672,8 +1675,8 @@ const TestOptions = struct {
|
||||||
fn threadMain(options: *TestOptions) !void {
|
fn threadMain(options: *TestOptions) !void {
|
||||||
// https://github.com/ziglang/zig/blob/d2be725e4b14c33dbd39054e33d926913eee3cd4/lib/compiler/std-docs.zig#L22-L54
|
// https://github.com/ziglang/zig/blob/d2be725e4b14c33dbd39054e33d926913eee3cd4/lib/compiler/std-docs.zig#L22-L54
|
||||||
|
|
||||||
options.arena = try options.allocator.create(std.heap.ArenaAllocator);
|
options.arena = try options.allocator.create(ArenaAllocator);
|
||||||
options.arena.?.* = std.heap.ArenaAllocator.init(options.allocator);
|
options.arena.?.* = ArenaAllocator.init(options.allocator);
|
||||||
const allocator = options.arena.?.allocator();
|
const allocator = options.arena.?.allocator();
|
||||||
options.allocator = allocator;
|
options.allocator = allocator;
|
||||||
|
|
||||||
|
@ -1684,7 +1687,7 @@ fn threadMain(options: *TestOptions) !void {
|
||||||
options.test_server_runtime_uri = try std.fmt.allocPrint(options.allocator, "http://127.0.0.1:{d}", .{options.server_port.?});
|
options.test_server_runtime_uri = try std.fmt.allocPrint(options.allocator, "http://127.0.0.1:{d}", .{options.server_port.?});
|
||||||
log.debug("server listening at {s}", .{options.test_server_runtime_uri.?});
|
log.debug("server listening at {s}", .{options.test_server_runtime_uri.?});
|
||||||
log.info("starting server thread, tid {d}", .{std.Thread.getCurrentId()});
|
log.info("starting server thread, tid {d}", .{std.Thread.getCurrentId()});
|
||||||
// var arena = std.heap.ArenaAllocator.init(options.allocator);
|
// var arena = ArenaAllocator.init(options.allocator);
|
||||||
// defer arena.deinit();
|
// defer arena.deinit();
|
||||||
// var aa = arena.allocator();
|
// var aa = arena.allocator();
|
||||||
// We're in control of all requests/responses, so this flag will tell us
|
// We're in control of all requests/responses, so this flag will tell us
|
||||||
|
@ -1764,7 +1767,7 @@ fn serveRequest(options: *TestOptions, request: *std.http.Server.Request) !void
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const TestSetup = struct {
|
const TestSetup = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: Allocator,
|
||||||
request_options: TestOptions,
|
request_options: TestOptions,
|
||||||
server_thread: std.Thread = undefined,
|
server_thread: std.Thread = undefined,
|
||||||
creds: aws_auth.Credentials = undefined,
|
creds: aws_auth.Credentials = undefined,
|
||||||
|
|
Loading…
Add table
Reference in a new issue