add header parsing support
This commit is contained in:
parent
723c483544
commit
21b04317bd
98
src/aws.zig
98
src/aws.zig
|
@ -4,6 +4,7 @@ const awshttp = @import("aws_http.zig");
|
||||||
const json = @import("json.zig");
|
const json = @import("json.zig");
|
||||||
const url = @import("url.zig");
|
const url = @import("url.zig");
|
||||||
const case = @import("case.zig");
|
const case = @import("case.zig");
|
||||||
|
const date = @import("date.zig");
|
||||||
const servicemodel = @import("servicemodel.zig");
|
const servicemodel = @import("servicemodel.zig");
|
||||||
const xml_shaper = @import("xml_shaper.zig");
|
const xml_shaper = @import("xml_shaper.zig");
|
||||||
|
|
||||||
|
@ -263,31 +264,78 @@ pub fn Request(comptime action: anytype) type {
|
||||||
return error.HttpFailure;
|
return error.HttpFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullResponse = try getFullResponseFromBody(aws_request, response, options);
|
var full_response = try getFullResponseFromBody(aws_request, response, options);
|
||||||
|
errdefer full_response.deinit();
|
||||||
|
|
||||||
// Fill in any fields that require a header. Note doing it post-facto
|
// Fill in any fields that require a header. Note doing it post-facto
|
||||||
// assumes all response header fields are optional, which may be incorrect
|
// assumes all response header fields are optional, which may be incorrect
|
||||||
if (@hasDecl(action.Response, "http_header")) {
|
if (@hasDecl(action.Response, "http_header")) {
|
||||||
inline for (std.meta.fields(@TypeOf(action.Response.http_header))) |f| {
|
log.debug("Checking headers based on type: {s}", .{@typeName(action.Response)});
|
||||||
const header_name = @field(action.Response.http_header, f.name);
|
const HeaderInfo = struct {
|
||||||
for (response.headers) |h| {
|
name: []const u8,
|
||||||
if (std.ascii.eqlIgnoreCase(h.name, header_name)) {
|
T: type,
|
||||||
log.debug("Response header {s} configured for field. Setting {s} = {s}", .{ h.name, f.name, h.value });
|
header_name: []const u8,
|
||||||
const field_type = @TypeOf(@field(fullResponse.response, f.name));
|
};
|
||||||
// TODO: Fix this. We need to make this much more robust
|
comptime var fields = [_]?HeaderInfo{null} ** std.meta.fields(@TypeOf(action.Response.http_header)).len;
|
||||||
// The deal is we have to do the dupe though
|
inline for (std.meta.fields(@TypeOf(action.Response.http_header))) |f, inx| {
|
||||||
// Also, this is a memory leak atm
|
fields[inx] = HeaderInfo{
|
||||||
if (field_type == ?[]const u8) {
|
.name = f.name,
|
||||||
@field(fullResponse.response, f.name) = try options.client.allocator.dupe(u8, (try coerceFromString(field_type, h.value)).?);
|
.T = @TypeOf(@field(full_response.response, f.name)),
|
||||||
} else {
|
.header_name = @field(action.Response.http_header, f.name),
|
||||||
@field(fullResponse.response, f.name) = try coerceFromString(field_type, h.value);
|
};
|
||||||
}
|
}
|
||||||
|
inline for (fields) |f| {
|
||||||
|
for (response.headers) |header| {
|
||||||
|
if (std.mem.eql(u8, header.name, f.?.header_name)) {
|
||||||
|
log.debug("Response header {s} configured for field. Setting {s} = {s}", .{ header.name, f.?.name, header.value });
|
||||||
|
// TODO: Revisit return for this function. At the moment, there
|
||||||
|
// is something in the compiler that is causing the inline for
|
||||||
|
// surrounding this to start repeating elements
|
||||||
|
//
|
||||||
|
// https://github.com/ziglang/zig/issues/10507
|
||||||
|
//
|
||||||
|
// This bug is also relevant to some of the many,
|
||||||
|
// many different methods used to try to work around:
|
||||||
|
// https://github.com/ziglang/zig/issues/10029
|
||||||
|
//
|
||||||
|
// Note: issues found on zig 0.9.0
|
||||||
|
setHeaderValue(
|
||||||
|
options.client.allocator,
|
||||||
|
&full_response.response,
|
||||||
|
f.?.name,
|
||||||
|
f.?.T,
|
||||||
|
header.value,
|
||||||
|
) catch |e| {
|
||||||
|
log.err("Could not set header value: Response header {s}. Field {s}. Value {s}", .{ header.name, f.?.name, header.value });
|
||||||
|
log.err("Error: {}", .{e});
|
||||||
|
if (@errorReturnTrace()) |trace| {
|
||||||
|
std.debug.dumpStackTrace(trace.*);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fullResponse;
|
return full_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setHeaderValue(
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
response: anytype,
|
||||||
|
comptime field_name: []const u8,
|
||||||
|
comptime field_type: type,
|
||||||
|
value: []const u8,
|
||||||
|
) !void {
|
||||||
|
// TODO: Fix this. We need to make this much more robust
|
||||||
|
// The deal is we have to do the dupe though
|
||||||
|
// Also, this is a memory leak atm
|
||||||
|
if (field_type == ?[]const u8) {
|
||||||
|
@field(response, field_name) = try allocator.dupe(u8, value);
|
||||||
|
} else {
|
||||||
|
@field(response, field_name) = try coerceFromString(field_type, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getFullResponseFromBody(aws_request: awshttp.HttpRequest, response: awshttp.HttpResult, options: Options) !FullResponseType {
|
fn getFullResponseFromBody(aws_request: awshttp.HttpRequest, response: awshttp.HttpResult, options: Options) !FullResponseType {
|
||||||
|
@ -568,15 +616,31 @@ pub fn Request(comptime action: anytype) type {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerceFromString(comptime T: type, val: []const u8) !T {
|
fn coerceFromString(comptime T: type, val: []const u8) anyerror!T {
|
||||||
if (@typeInfo(T) == .Optional) return try coerceFromString(@typeInfo(T).Optional.child, val);
|
if (@typeInfo(T) == .Optional) return try coerceFromString(@typeInfo(T).Optional.child, val);
|
||||||
// TODO: This is terrible...fix it
|
// TODO: This is terrible...fix it
|
||||||
switch (T) {
|
switch (T) {
|
||||||
bool => return std.ascii.eqlIgnoreCase(val, "true"),
|
bool => return std.ascii.eqlIgnoreCase(val, "true"),
|
||||||
i64 => return try std.fmt.parseInt(T, val, 10),
|
i64 => return parseInt(T, val) catch |e| {
|
||||||
|
log.err("Invalid string representing i64: {s}", .{val});
|
||||||
|
return e;
|
||||||
|
},
|
||||||
else => return val,
|
else => return val,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn parseInt(comptime T: type, val: []const u8) !T {
|
||||||
|
const rc = std.fmt.parseInt(T, val, 10);
|
||||||
|
if (!std.meta.isError(rc)) return rc;
|
||||||
|
|
||||||
|
if (T == i64) {
|
||||||
|
return date.parseEnglishToTimestamp(val) catch |e| {
|
||||||
|
log.err("Error coercing date string '{s}' to timestamp value", .{val});
|
||||||
|
return e;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
log.err("Error parsing string '{s}' to integer", .{val});
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
fn generalAllocPrint(allocator: std.mem.Allocator, val: anytype) !?[]const u8 {
|
fn generalAllocPrint(allocator: std.mem.Allocator, val: anytype) !?[]const u8 {
|
||||||
switch (@typeInfo(@TypeOf(val))) {
|
switch (@typeInfo(@TypeOf(val))) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user