diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 3de15774d1..cc01a181f0 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -645,7 +645,6 @@ pub const Request = struct { if (req.response.parser.state.isContent()) break; } - req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false }; try req.response.parse(req.response.parser.header_bytes.items); if (req.response.status == .switching_protocols) { @@ -765,7 +764,7 @@ pub const Request = struct { } if (has_trail) { - req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false }; + req.response.headers.clearRetainingCapacity(); // The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error. // This will *only* fail for a malformed trailer. @@ -1019,7 +1018,7 @@ pub fn request(client: *Client, method: http.Method, uri: Uri, headers: http.Hea .status = undefined, .reason = undefined, .version = undefined, - .headers = undefined, + .headers = http.Headers{ .allocator = client.allocator, .owned = false }, .parser = switch (options.header_strategy) { .dynamic => |max| proto.HeadersParser.initDynamic(max), .static => |buf| proto.HeadersParser.initStatic(buf), diff --git a/lib/std/http/Headers.zig b/lib/std/http/Headers.zig index e84a890862..0322c32481 100644 --- a/lib/std/http/Headers.zig +++ b/lib/std/http/Headers.zig @@ -68,17 +68,7 @@ pub const Headers = struct { } pub fn deinit(headers: *Headers) void { - var it = headers.index.iterator(); - while (it.next()) |entry| { - entry.value_ptr.deinit(headers.allocator); - - if (headers.owned) headers.allocator.free(entry.key_ptr.*); - } - - for (headers.list.items) |entry| { - if (headers.owned) headers.allocator.free(entry.value); - } - + headers.deallocateIndexListsAndFields(); headers.index.deinit(headers.allocator); headers.list.deinit(headers.allocator); @@ -255,6 +245,39 @@ pub const Headers = struct { try out_stream.writeAll("\r\n"); } + + /// Frees all `HeaderIndexList`s within `index` + /// Frees names and values of all fields if they are owned. + fn deallocateIndexListsAndFields(headers: *Headers) void { + var it = headers.index.iterator(); + while (it.next()) |entry| { + entry.value_ptr.deinit(headers.allocator); + + if (headers.owned) headers.allocator.free(entry.key_ptr.*); + } + + if (headers.owned) { + for (headers.list.items) |entry| { + headers.allocator.free(entry.value); + } + } + } + + /// Clears and frees the underlying data structures. + /// Frees names and values if they are owned. + pub fn clearAndFree(headers: *Headers) void { + headers.deallocateIndexListsAndFields(); + headers.index.clearAndFree(headers.allocator); + headers.list.clearAndFree(headers.allocator); + } + + /// Clears the underlying data structures while retaining their capacities. + /// Frees names and values if they are owned. + pub fn clearRetainingCapacity(headers: *Headers) void { + headers.deallocateIndexListsAndFields(); + headers.index.clearRetainingCapacity(); + headers.list.clearRetainingCapacity(); + } }; test "Headers.append" { @@ -384,3 +407,42 @@ test "Headers consistency" { try h.formatCommaSeparated("foo", writer); try testing.expectEqualStrings("foo: bar, baz\r\n", fbs.getWritten()); } + +test "Headers.clearRetainingCapacity and clearAndFree" { + var h = Headers.init(std.testing.allocator); + defer h.deinit(); + + h.clearRetainingCapacity(); + + try h.append("foo", "bar"); + try h.append("bar", "world"); + try h.append("foo", "baz"); + try h.append("baz", "hello"); + try testing.expectEqual(@as(usize, 4), h.list.items.len); + try testing.expectEqual(@as(usize, 3), h.index.count()); + const list_capacity = h.list.capacity; + const index_capacity = h.index.capacity(); + + h.clearRetainingCapacity(); + try testing.expectEqual(@as(usize, 0), h.list.items.len); + try testing.expectEqual(@as(usize, 0), h.index.count()); + try testing.expectEqual(list_capacity, h.list.capacity); + try testing.expectEqual(index_capacity, h.index.capacity()); + + try h.append("foo", "bar"); + try h.append("bar", "world"); + try h.append("foo", "baz"); + try h.append("baz", "hello"); + try testing.expectEqual(@as(usize, 4), h.list.items.len); + try testing.expectEqual(@as(usize, 3), h.index.count()); + // Capacity should still be the same since we shouldn't have needed to grow + // when adding back the same fields + try testing.expectEqual(list_capacity, h.list.capacity); + try testing.expectEqual(index_capacity, h.index.capacity()); + + h.clearAndFree(); + try testing.expectEqual(@as(usize, 0), h.list.items.len); + try testing.expectEqual(@as(usize, 0), h.index.count()); + try testing.expectEqual(@as(usize, 0), h.list.capacity); + try testing.expectEqual(@as(usize, 0), h.index.capacity()); +}