commit 1d1255b43315912f63281f8b35cb1bed95960bf2 (tree)
parent ef0a02081d0506dc497ffc89f06e16db7d4b8184
Author: Andrew Kelley <andrew@ziglang.org>
Date: Mon, 24 Apr 2023 15:15:17 -0700
Merge pull request #15416 from squeek502/http-response-headers-undefined
std.http: Always initialize `response.headers` in Client.request
Diffstat:
2 files changed, 75 insertions(+), 14 deletions(-)
diff --git 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
@@ -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());
+}