authorgravatar for nwtgck@nwtgck.orgRyo Ota <nwtgck@nwtgck.org> 2023-04-21 01:08:15+09:00
committergravatar for nwtgck@nwtgck.orgRyo Ota <nwtgck@nwtgck.org> 2023-04-21 02:35:38+09:00
log39c0c24b5659bbcaec0a6f5ba4acc8666e3b8086
tree067b8084572490c7b29d4bbabdb0959ec4b59688
parent0f4f6078143e67d50bfd66840e31cf3b38987261

fix memory leaks and add an HTTP test


2 files changed, 73 insertions(+), 1 deletions(-)

lib/std/http/Client.zig+69
...@@ -1046,3 +1046,72 @@ test {...@@ -1046,3 +1046,72 @@ test {
10461046
1047 std.testing.refAllDecls(@This());1047 std.testing.refAllDecls(@This());
1048}1048}
1049
1050test "client requests server" {
1051 const builtin = @import("builtin");
1052
1053 // This test requires spawning threads.
1054 if (builtin.single_threaded) {
1055 return error.SkipZigTest;
1056 }
1057
1058 const native_endian = comptime builtin.cpu.arch.endian();
1059 if (builtin.zig_backend == .stage2_llvm and native_endian == .Big) {
1060 // https://github.com/ziglang/zig/issues/13782
1061 return error.SkipZigTest;
1062 }
1063
1064 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1065
1066 const allocator = std.testing.allocator;
1067
1068 const max_header_size = 8192;
1069 var server = std.http.Server.init(allocator, .{ .reuse_address = true });
1070 defer server.deinit();
1071
1072 const address = try std.net.Address.parseIp("127.0.0.1", 0);
1073 try server.listen(address);
1074 const server_port = server.socket.listen_address.in.getPort();
1075
1076 const thread = try std.Thread.spawn(.{}, (struct {
1077 fn apply(s: *std.http.Server) !void {
1078 const res = try s.accept(.{ .dynamic = max_header_size });
1079 defer res.reset();
1080 try res.wait();
1081
1082 const server_body: []const u8 = "message from server!\n";
1083 res.transfer_encoding = .{ .content_length = server_body.len };
1084 try res.headers.append("content-type", "text/plain");
1085 try res.headers.append("connection", "close");
1086 try res.do();
1087
1088 var buf: [128]u8 = undefined;
1089 const n = try res.readAll(&buf);
1090 try testing.expect(std.mem.eql(u8, buf[0..n], "Hello, World!\n"));
1091 _ = try res.writer().writeAll(server_body);
1092 try res.finish();
1093 }
1094 }).apply, .{&server});
1095
1096 var uri_buf: [22]u8 = undefined;
1097 const uri = std.Uri.parse(try std.fmt.bufPrint(&uri_buf, "http://127.0.0.1:{d}", .{server_port})) catch unreachable;
1098 var client = std.http.Client{ .allocator = allocator };
1099 defer client.deinit();
1100 var client_headers = std.http.Headers{ .allocator = allocator };
1101 defer client_headers.deinit();
1102 var client_req = try client.request(.POST, uri, client_headers, .{});
1103 defer client_req.deinit();
1104
1105 client_req.transfer_encoding = .{ .content_length = 14 }; // this will be checked to ensure you sent exactly 14 bytes
1106 try client_req.start(); // this sends the request
1107 try client_req.writeAll("Hello, ");
1108 try client_req.writeAll("World!\n");
1109 try client_req.finish();
1110 try client_req.do(); // this waits for a response
1111
1112 const body = try client_req.reader().readAllAlloc(allocator, 8192 * 1024);
1113 defer allocator.free(body);
1114 try testing.expect(std.mem.eql(u8, body, "message from server!\n"));
1115
1116 thread.join();
1117}
lib/std/http/Server.zig+4-1
...@@ -338,6 +338,9 @@ pub const Response = struct {...@@ -338,6 +338,9 @@ pub const Response = struct {
338338
339 /// Reset this response to its initial state. This must be called before handling a second request on the same connection.339 /// Reset this response to its initial state. This must be called before handling a second request on the same connection.
340 pub fn reset(res: *Response) void {340 pub fn reset(res: *Response) void {
341 res.request.headers.deinit();
342 res.headers.deinit();
343
341 switch (res.request.compression) {344 switch (res.request.compression) {
342 .none => {},345 .none => {},
343 .deflate => |*deflate| deflate.deinit(),346 .deflate => |*deflate| deflate.deinit(),
...@@ -357,7 +360,7 @@ pub const Response = struct {...@@ -357,7 +360,7 @@ pub const Response = struct {
357 res.request.parser.header_bytes.deinit(res.server.allocator);360 res.request.parser.header_bytes.deinit(res.server.allocator);
358 }361 }
359362
360 res.* = undefined;363 res.server.allocator.destroy(res);
361 } else {364 } else {
362 res.request.parser.reset();365 res.request.parser.reset();
363 }366 }