authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-24 15:15:17-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-04-24 15:15:17-07:00
log1d1255b43315912f63281f8b35cb1bed95960bf2
tree42b795ea0746995c90ae76aa986b96c1cad84016
parentef0a02081d0506dc497ffc89f06e16db7d4b8184
parent0488c3cb52a75ffa4fa73b385d3d6e79cc6da774
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15416 from squeek502/http-response-headers-undefined

std.http: Always initialize `response.headers` in Client.request

2 files changed, 75 insertions(+), 14 deletions(-)

lib/std/http/Client.zig+2-3
......@@ -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+73-11
......@@ -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
260283test "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
411test "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}