authorgravatar for truemedian@gmail.comNameless <truemedian@gmail.com> 2023-07-07 15:08:19-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-07 20:08:19+00:00
logb9fc0d2908371dc4f7c95c03972d42e290d6e1e0
tree3d32c4377d02ca5365c7c8cc58840428cee6a550
parent80404cc928c758f7063da42a0d68669998613969
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.http: fix leaked connections (#16341)

The early return in pool release was causing leaked connections. Closes #16282.

2 files changed, 27 insertions(+), 4 deletions(-)

lib/std/http/Client.zig+3-4
...@@ -92,16 +92,15 @@ pub const ConnectionPool = struct {...@@ -92,16 +92,15 @@ pub const ConnectionPool = struct {
9292
93 if (node.data.closing) {93 if (node.data.closing) {
94 node.data.deinit(client);94 node.data.deinit(client);
95
96 return client.allocator.destroy(node);95 return client.allocator.destroy(node);
97 }96 }
9897
99 if (pool.free_len + 1 >= pool.free_size) {98 if (pool.free_len >= pool.free_size) {
100 const popped = pool.free.popFirst() orelse unreachable;99 const popped = pool.free.popFirst() orelse unreachable;
100 pool.free_len -= 1;
101101
102 popped.data.deinit(client);102 popped.data.deinit(client);
103103 client.allocator.destroy(popped);
104 return client.allocator.destroy(popped);
105 }104 }
106105
107 if (node.data.proxied) {106 if (node.data.proxied) {
test/standalone/http.zig+24
...@@ -571,6 +571,30 @@ pub fn main() !void {...@@ -571,6 +571,30 @@ pub fn main() !void {
571 // connection has been kept alive571 // connection has been kept alive
572 try testing.expect(client.connection_pool.free_len == 1);572 try testing.expect(client.connection_pool.free_len == 1);
573573
574 { // issue 16282
575 const location = try std.fmt.allocPrint(calloc, "http://127.0.0.1:{d}/get", .{port});
576 defer calloc.free(location);
577 const uri = try std.Uri.parse(location);
578
579 const total_connections = client.connection_pool.free_size + 64;
580 var requests = try calloc.alloc(http.Client.Request, total_connections);
581 defer calloc.free(requests);
582
583 for (0..total_connections) |i| {
584 var req = try client.request(.GET, uri, .{ .allocator = calloc }, .{});
585 req.response.parser.done = true;
586 req.connection.?.data.closing = false;
587 requests[i] = req;
588 }
589
590 for (0..total_connections) |i| {
591 requests[i].deinit();
592 }
593
594 // free connections should be full now
595 try testing.expect(client.connection_pool.free_len == client.connection_pool.free_size);
596 }
597
574 client.deinit();598 client.deinit();
575599
576 killServer(server.socket.listen_address);600 killServer(server.socket.listen_address);