Skip to content

Commit 1c08b3e

Browse files
Merge pull request #534 from lightpanda-io/mutable_response_header_value
Make HTTP Response header values mutable
2 parents 8c489c2 + 4a6bf38 commit 1c08b3e

File tree

1 file changed

+26
-14
lines changed

1 file changed

+26
-14
lines changed

src/http/client.zig

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ const BUFFER_LEN = 32 * 1024;
3737
// The longest individual header line that we support
3838
const MAX_HEADER_LINE_LEN = 4096;
3939

40-
const HeaderList = std.ArrayListUnmanaged(std.http.Header);
41-
4240
// Thread-safe. Holds our root certificate, connection pool and state pool
4341
// Used to create Requests.
4442
pub const Client = struct {
@@ -114,7 +112,7 @@ pub const Request = struct {
114112
arena: Allocator,
115113

116114
// List of request headers
117-
headers: HeaderList,
115+
headers: std.ArrayListUnmanaged(std.http.Header),
118116

119117
// Used to limit the # of redirects we'll follow
120118
_redirect_count: u16,
@@ -1438,10 +1436,11 @@ const Reader = struct {
14381436
pub const ResponseHeader = struct {
14391437
status: u16 = 0,
14401438
keepalive: bool = false,
1441-
headers: HeaderList = .{},
1439+
headers: std.ArrayListUnmanaged(Header) = .{},
14421440

1443-
// Stored header has already been lower-cased, we expect name to be lowercased
1444-
pub fn get(self: *const ResponseHeader, name: []const u8) ?[]const u8 {
1441+
// Stored header has already been lower-cased
1442+
// `name` parameter should be passed in lower-cased
1443+
pub fn get(self: *const ResponseHeader, name: []const u8) ?[]u8 {
14451444
for (self.headers.items) |h| {
14461445
if (std.mem.eql(u8, name, h.name)) {
14471446
return h.value;
@@ -1463,12 +1462,23 @@ pub const ResponseHeader = struct {
14631462
}
14641463
};
14651464

1465+
// We don't want to use std.http.Header, because the value is `[]const u8`.
1466+
// We _could_ use it and @constCast, but this gives us more safety.
1467+
// The main reason we want to do this is that a caller could lower-case the
1468+
// value in-place.
1469+
// The value (and key) are both safe to mutate because they're cloned from
1470+
// the byte stream by our arena.
1471+
const Header = struct {
1472+
name: []const u8,
1473+
value: []u8,
1474+
};
1475+
14661476
const HeaderIterator = struct {
14671477
index: usize,
14681478
name: []const u8,
1469-
headers: HeaderList,
1479+
headers: std.ArrayListUnmanaged(Header),
14701480

1471-
pub fn next(self: *HeaderIterator) ?[]const u8 {
1481+
pub fn next(self: *HeaderIterator) ?[]u8 {
14721482
const name = self.name;
14731483
const index = self.index;
14741484
for (self.headers.items[index..], index..) |h, i| {
@@ -2108,11 +2118,13 @@ test "HttpClient: HeaderIterator" {
21082118
try testing.expectEqual(null, it.next());
21092119
}
21102120

2111-
try header.headers.append(testing.allocator, .{ .name = "h1", .value = "value1" });
2112-
try header.headers.append(testing.allocator, .{ .name = "h2", .value = "value2" });
2113-
try header.headers.append(testing.allocator, .{ .name = "h3", .value = "value3" });
2114-
try header.headers.append(testing.allocator, .{ .name = "h1", .value = "value4" });
2115-
try header.headers.append(testing.allocator, .{ .name = "h1", .value = "value5" });
2121+
// @constCast is totally unsafe here, but it's just a test, and we know
2122+
// nothing is going to write to it, so it works.
2123+
try header.headers.append(testing.allocator, .{ .name = "h1", .value = @constCast("value1") });
2124+
try header.headers.append(testing.allocator, .{ .name = "h2", .value = @constCast("value2") });
2125+
try header.headers.append(testing.allocator, .{ .name = "h3", .value = @constCast("value3") });
2126+
try header.headers.append(testing.allocator, .{ .name = "h1", .value = @constCast("value4") });
2127+
try header.headers.append(testing.allocator, .{ .name = "h1", .value = @constCast("value5") });
21162128

21172129
{
21182130
var it = header.iterate("nope");
@@ -2149,7 +2161,7 @@ const TestResponse = struct {
21492161
keepalive: ?bool,
21502162
arena: std.heap.ArenaAllocator,
21512163
body: std.ArrayListUnmanaged(u8),
2152-
headers: std.ArrayListUnmanaged(std.http.Header),
2164+
headers: std.ArrayListUnmanaged(Header),
21532165

21542166
fn init() TestResponse {
21552167
return .{

0 commit comments

Comments
 (0)