Commit b9fc0d2908

Nameless <truemedian@gmail.com>
2023-07-07 22:08:19
std.http: fix leaked connections (#16341)
The early return in pool release was causing leaked connections. Closes #16282.
1 parent 80404cc
Changed files (2)
lib
std
test
standalone
lib/std/http/Client.zig
@@ -92,16 +92,15 @@ pub const ConnectionPool = struct {
 
         if (node.data.closing) {
             node.data.deinit(client);
-
             return client.allocator.destroy(node);
         }
 
-        if (pool.free_len + 1 >= pool.free_size) {
+        if (pool.free_len >= pool.free_size) {
             const popped = pool.free.popFirst() orelse unreachable;
+            pool.free_len -= 1;
 
             popped.data.deinit(client);
-
-            return client.allocator.destroy(popped);
+            client.allocator.destroy(popped);
         }
 
         if (node.data.proxied) {
test/standalone/http.zig
@@ -571,6 +571,30 @@ pub fn main() !void {
     // connection has been kept alive
     try testing.expect(client.connection_pool.free_len == 1);
 
+    { // issue 16282
+        const location = try std.fmt.allocPrint(calloc, "http://127.0.0.1:{d}/get", .{port});
+        defer calloc.free(location);
+        const uri = try std.Uri.parse(location);
+
+        const total_connections = client.connection_pool.free_size + 64;
+        var requests = try calloc.alloc(http.Client.Request, total_connections);
+        defer calloc.free(requests);
+
+        for (0..total_connections) |i| {
+            var req = try client.request(.GET, uri, .{ .allocator = calloc }, .{});
+            req.response.parser.done = true;
+            req.connection.?.data.closing = false;
+            requests[i] = req;
+        }
+
+        for (0..total_connections) |i| {
+            requests[i].deinit();
+        }
+
+        // free connections should be full now
+        try testing.expect(client.connection_pool.free_len == client.connection_pool.free_size);
+    }
+
     client.deinit();
 
     killServer(server.socket.listen_address);