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