Skip to content

Commit 613904e

Browse files
committed
Make HTTP Response header values mutable
The HTTP response values _are_ mutable, but because we're using std.http.Header the type is a `[]const u8`. This introduce a custom `Header` type where the value is `[]u8`. The goal is largely to allow more efficient value-comparison, by allowing calling code to lower-case in-place. I specifically have the Mime parser in mind: https://github.com/lightpanda-io/browser/blob/25dcae76487ab2aaa8ac8b468f465e55007a76ee/src/browser/mime.zig#L134
1 parent 25dcae7 commit 613904e

File tree

1 file changed

+25
-13
lines changed

1 file changed

+25
-13
lines changed

src/http/client.zig

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

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

115113
// List of request headers
116-
headers: HeaderList,
114+
headers: std.ArrayListUnmanaged(std.http.Header),
117115

118116
// Used to limit the # of redirects we'll follow
119117
_redirect_count: u16,
@@ -1437,9 +1435,10 @@ const Reader = struct {
14371435
pub const ResponseHeader = struct {
14381436
status: u16 = 0,
14391437
keepalive: bool = false,
1440-
headers: HeaderList = .{},
1438+
headers: std.ArrayListUnmanaged(Header) = .{},
14411439

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

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

1470-
pub fn next(self: *HeaderIterator) ?[]const u8 {
1480+
pub fn next(self: *HeaderIterator) ?[]u8 {
14711481
const name = self.name;
14721482
const index = self.index;
14731483
for (self.headers.items[index..], index..) |h, i| {
@@ -2107,11 +2117,13 @@ test "HttpClient: HeaderIterator" {
21072117
try testing.expectEqual(null, it.next());
21082118
}
21092119

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

21162128
{
21172129
var it = header.iterate("nope");
@@ -2148,7 +2160,7 @@ const TestResponse = struct {
21482160
keepalive: ?bool,
21492161
arena: std.heap.ArenaAllocator,
21502162
body: std.ArrayListUnmanaged(u8),
2151-
headers: std.ArrayListUnmanaged(std.http.Header),
2163+
headers: std.ArrayListUnmanaged(Header),
21522164

21532165
fn init() TestResponse {
21542166
return .{

0 commit comments

Comments
 (0)