Skip to content

Commit 1d1255b

Browse files
authored
Merge pull request #15416 from squeek502/http-response-headers-undefined
std.http: Always initialize `response.headers` in Client.request
2 parents ef0a020 + 0488c3c commit 1d1255b

File tree

2 files changed

+75
-14
lines changed

2 files changed

+75
-14
lines changed

lib/std/http/Client.zig

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,6 @@ pub const Request = struct {
645645
if (req.response.parser.state.isContent()) break;
646646
}
647647

648-
req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false };
649648
try req.response.parse(req.response.parser.header_bytes.items);
650649

651650
if (req.response.status == .switching_protocols) {
@@ -765,7 +764,7 @@ pub const Request = struct {
765764
}
766765

767766
if (has_trail) {
768-
req.response.headers = http.Headers{ .allocator = req.client.allocator, .owned = false };
767+
req.response.headers.clearRetainingCapacity();
769768

770769
// The response headers before the trailers are already guaranteed to be valid, so they will always be parsed again and cannot return an error.
771770
// 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
10191018
.status = undefined,
10201019
.reason = undefined,
10211020
.version = undefined,
1022-
.headers = undefined,
1021+
.headers = http.Headers{ .allocator = client.allocator, .owned = false },
10231022
.parser = switch (options.header_strategy) {
10241023
.dynamic => |max| proto.HeadersParser.initDynamic(max),
10251024
.static => |buf| proto.HeadersParser.initStatic(buf),

lib/std/http/Headers.zig

Lines changed: 73 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,7 @@ pub const Headers = struct {
6868
}
6969

7070
pub fn deinit(headers: *Headers) void {
71-
var it = headers.index.iterator();
72-
while (it.next()) |entry| {
73-
entry.value_ptr.deinit(headers.allocator);
74-
75-
if (headers.owned) headers.allocator.free(entry.key_ptr.*);
76-
}
77-
78-
for (headers.list.items) |entry| {
79-
if (headers.owned) headers.allocator.free(entry.value);
80-
}
81-
71+
headers.deallocateIndexListsAndFields();
8272
headers.index.deinit(headers.allocator);
8373
headers.list.deinit(headers.allocator);
8474

@@ -255,6 +245,39 @@ pub const Headers = struct {
255245

256246
try out_stream.writeAll("\r\n");
257247
}
248+
249+
/// Frees all `HeaderIndexList`s within `index`
250+
/// Frees names and values of all fields if they are owned.
251+
fn deallocateIndexListsAndFields(headers: *Headers) void {
252+
var it = headers.index.iterator();
253+
while (it.next()) |entry| {
254+
entry.value_ptr.deinit(headers.allocator);
255+
256+
if (headers.owned) headers.allocator.free(entry.key_ptr.*);
257+
}
258+
259+
if (headers.owned) {
260+
for (headers.list.items) |entry| {
261+
headers.allocator.free(entry.value);
262+
}
263+
}
264+
}
265+
266+
/// Clears and frees the underlying data structures.
267+
/// Frees names and values if they are owned.
268+
pub fn clearAndFree(headers: *Headers) void {
269+
headers.deallocateIndexListsAndFields();
270+
headers.index.clearAndFree(headers.allocator);
271+
headers.list.clearAndFree(headers.allocator);
272+
}
273+
274+
/// Clears the underlying data structures while retaining their capacities.
275+
/// Frees names and values if they are owned.
276+
pub fn clearRetainingCapacity(headers: *Headers) void {
277+
headers.deallocateIndexListsAndFields();
278+
headers.index.clearRetainingCapacity();
279+
headers.list.clearRetainingCapacity();
280+
}
258281
};
259282

260283
test "Headers.append" {
@@ -384,3 +407,42 @@ test "Headers consistency" {
384407
try h.formatCommaSeparated("foo", writer);
385408
try testing.expectEqualStrings("foo: bar, baz\r\n", fbs.getWritten());
386409
}
410+
411+
test "Headers.clearRetainingCapacity and clearAndFree" {
412+
var h = Headers.init(std.testing.allocator);
413+
defer h.deinit();
414+
415+
h.clearRetainingCapacity();
416+
417+
try h.append("foo", "bar");
418+
try h.append("bar", "world");
419+
try h.append("foo", "baz");
420+
try h.append("baz", "hello");
421+
try testing.expectEqual(@as(usize, 4), h.list.items.len);
422+
try testing.expectEqual(@as(usize, 3), h.index.count());
423+
const list_capacity = h.list.capacity;
424+
const index_capacity = h.index.capacity();
425+
426+
h.clearRetainingCapacity();
427+
try testing.expectEqual(@as(usize, 0), h.list.items.len);
428+
try testing.expectEqual(@as(usize, 0), h.index.count());
429+
try testing.expectEqual(list_capacity, h.list.capacity);
430+
try testing.expectEqual(index_capacity, h.index.capacity());
431+
432+
try h.append("foo", "bar");
433+
try h.append("bar", "world");
434+
try h.append("foo", "baz");
435+
try h.append("baz", "hello");
436+
try testing.expectEqual(@as(usize, 4), h.list.items.len);
437+
try testing.expectEqual(@as(usize, 3), h.index.count());
438+
// Capacity should still be the same since we shouldn't have needed to grow
439+
// when adding back the same fields
440+
try testing.expectEqual(list_capacity, h.list.capacity);
441+
try testing.expectEqual(index_capacity, h.index.capacity());
442+
443+
h.clearAndFree();
444+
try testing.expectEqual(@as(usize, 0), h.list.items.len);
445+
try testing.expectEqual(@as(usize, 0), h.index.count());
446+
try testing.expectEqual(@as(usize, 0), h.list.capacity);
447+
try testing.expectEqual(@as(usize, 0), h.index.capacity());
448+
}

0 commit comments

Comments
 (0)