From 42ac83cb327249a36550eae6e204da69e7164cd6 Mon Sep 17 00:00:00 2001 From: Meghan Denny Date: Thu, 5 Feb 2026 02:00:30 -0800 Subject: [PATCH] add HeadersMap --- http.zig | 51 ++++++++++++++++++++++++++++++++++++++++++--------- test.zig | 6 +++--- zigmod.yml | 1 + 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/http.zig b/http.zig index eebe6965a17c3b47f2a2a532af7fd8e46b533e57..a815aaefb78b52baca7eeb0da22bfa4c5205cc22 100644 --- a/http.zig +++ b/http.zig @@ -3,6 +3,7 @@ const builtin = @import("builtin"); const net = @import("net"); const url = @import("url"); const nio = @import("nio"); +const extras = @import("extras"); pub const Method = enum { GET, @@ -214,7 +215,7 @@ pub fn open(allocator: std.mem.Allocator, method: Method, input: []const u8) !Cl .writer = bufw, .reader = .init(conn), .status = @enumFromInt(0), - .headers_raw = "", + .headers = .init(allocator), }; } @@ -224,11 +225,11 @@ pub const ClientRequest = struct { writer: nio.BufferedWriter(4096, net.Stream), reader: nio.BufferedReader(4096, net.Stream), status: Status, - headers_raw: []const u8, + headers: HeadersMap, - pub fn close(req: *const ClientRequest, allocator: std.mem.Allocator) void { + pub fn close(req: *ClientRequest) void { req.stream.close(); - allocator.free(req.headers_raw); + req.headers.deinit(); } pub fn writeHeader(req: *ClientRequest, name: []const u8, value: []const u8) !void { @@ -258,13 +259,19 @@ pub const ClientRequest = struct { _ = try req.readUntilDelimitersBuf(&phrase_buf, "\r\n"); req.status = status; - var headers_list = std.ArrayList(u8).init(req.allocator); - errdefer headers_list.deinit(); + var headers_list = req.headers.data.list.toManaged(req.allocator); + defer req.headers.data.list = headers_list.moveToUnmanaged(); while (true) { - const header_len = try req.readUntilDelimitersArrayList(&headers_list, "\r\n", 1024); - if (header_len == 0) break; + const header_line = try req.readUntilDelimitersArrayList(&headers_list, "\r\n", 1024); + if (header_line.len == 0) break; + const colon_pos = std.mem.indexOfScalar(u8, header_line, ':') orelse return error.Bad; + const name = header_line[0..colon_pos]; + if (!extras.matchesAll(u8, name, std.ascii.isAscii)) return error.Bad; + for (name) |*c| c.* = std.ascii.toLower(c.*); + if (header_line.len == colon_pos or header_line[colon_pos + 1] != ' ') return error.Bad; + const value = header_line[colon_pos + 2 ..]; + try req.headers.data.lengths.appendSlice(req.allocator, &.{ name.len, 2, value.len, 2 }); } - req.headers_raw = try headers_list.toOwnedSlice(); } pub const ReadError = net.Stream.ReadError; @@ -285,3 +292,29 @@ pub const ClientRequest = struct { }; } }; + +pub const HeadersMap = struct { + data: extras.ManyArrayList(u8), + + pub fn init(allocator: std.mem.Allocator) HeadersMap { + return .{ + .data = .init(allocator), + }; + } + + pub fn deinit(map: *HeadersMap) void { + map.data.deinit(); + } + + pub fn count(map: *const HeadersMap) usize { + return map.data.lengths.items.len / 4; + } + + pub fn name(map: *const HeadersMap, idx: usize) []const u8 { + return map.data.items(idx * 4 + 0); + } + + pub fn value(map: *const HeadersMap, idx: usize) []const u8 { + return map.data.items(idx * 4 + 2); + } +}; diff --git a/test.zig b/test.zig index a9d102111c4fef916bb6598d4f7de09bbbe80f18..ad0d2c74d59df09c17c199d42d0e9677c125f830 100644 --- a/test.zig +++ b/test.zig @@ -8,7 +8,7 @@ const Scheme = enum { http }; test { const allocator = std.testing.allocator; var req = try http.open(allocator, .GET, "http://he.net/"); - defer req.close(allocator); + defer req.close(); try req.writeUA(); try req.send(); } @@ -26,7 +26,7 @@ fn httpbinMethod(comptime scheme: Scheme, comptime method: http.Method) !void { const allocator = std.testing.allocator; const url = @tagName(scheme) ++ "://httpbin.org/" ++ comptime extras.asciiLowerComptime(@tagName(method)); var req = try http.open(allocator, method, url); - defer req.close(allocator); + defer req.close(); try req.writeUA(); try req.send(); try expect(req.status).toEqual(.ok); @@ -40,7 +40,7 @@ fn httpbinUserAgent(comptime scheme: Scheme) !void { const allocator = std.testing.allocator; const url = @tagName(scheme) ++ "://httpbin.org/user-agent"; var req = try http.open(allocator, .GET, url); - defer req.close(allocator); + defer req.close(); try req.writeUA(); try req.send(); try expect(req.status).toEqual(.ok); diff --git a/zigmod.yml b/zigmod.yml index ed8188cbea1b9accb578150593cc25151000c158..b5bff2103dda9b290ae648809f052a0818f1f16c 100644 --- a/zigmod.yml +++ b/zigmod.yml @@ -7,6 +7,7 @@ dependencies: - src: git https://github.com/nektro/zig-net - src: git https://github.com/nektro/zig-whatwg-url - src: git https://github.com/nektro/zig-nio + - src: git https://github.com/nektro/zig-extras root_dependencies: - src: git https://github.com/nektro/zig-extras - src: git https://github.com/nektro/zig-json -- 2.54.0