authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-05 02:00:30 -08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-02-05 02:00:30 -08:00
log42ac83cb327249a36550eae6e204da69e7164cd6
tree9f18bf6153093b85c653e940e5640850b2f9208e
parentd646e7d74311bb3ac477d7ede3ccc3d645f7849d

add HeadersMap


3 files changed, 46 insertions(+), 12 deletions(-)

http.zig+42-9
......@@ -3,6 +3,7 @@ const builtin = @import("builtin");
33const net = @import("net");
44const url = @import("url");
55const nio = @import("nio");
6const extras = @import("extras");
67
78pub const Method = enum {
89 GET,
......@@ -214,7 +215,7 @@ pub fn open(allocator: std.mem.Allocator, method: Method, input: []const u8) !Cl
214215 .writer = bufw,
215216 .reader = .init(conn),
216217 .status = @enumFromInt(0),
217 .headers_raw = "",
218 .headers = .init(allocator),
218219 };
219220}
220221
......@@ -224,11 +225,11 @@ pub const ClientRequest = struct {
224225 writer: nio.BufferedWriter(4096, net.Stream),
225226 reader: nio.BufferedReader(4096, net.Stream),
226227 status: Status,
227 headers_raw: []const u8,
228 headers: HeadersMap,
228229
229 pub fn close(req: *const ClientRequest, allocator: std.mem.Allocator) void {
230 pub fn close(req: *ClientRequest) void {
230231 req.stream.close();
231 allocator.free(req.headers_raw);
232 req.headers.deinit();
232233 }
233234
234235 pub fn writeHeader(req: *ClientRequest, name: []const u8, value: []const u8) !void {
......@@ -258,13 +259,19 @@ pub const ClientRequest = struct {
258259 _ = try req.readUntilDelimitersBuf(&phrase_buf, "\r\n");
259260 req.status = status;
260261
261 var headers_list = std.ArrayList(u8).init(req.allocator);
262 errdefer headers_list.deinit();
262 var headers_list = req.headers.data.list.toManaged(req.allocator);
263 defer req.headers.data.list = headers_list.moveToUnmanaged();
263264 while (true) {
264 const header_len = try req.readUntilDelimitersArrayList(&headers_list, "\r\n", 1024);
265 if (header_len == 0) break;
265 const header_line = try req.readUntilDelimitersArrayList(&headers_list, "\r\n", 1024);
266 if (header_line.len == 0) break;
267 const colon_pos = std.mem.indexOfScalar(u8, header_line, ':') orelse return error.Bad;
268 const name = header_line[0..colon_pos];
269 if (!extras.matchesAll(u8, name, std.ascii.isAscii)) return error.Bad;
270 for (name) |*c| c.* = std.ascii.toLower(c.*);
271 if (header_line.len == colon_pos or header_line[colon_pos + 1] != ' ') return error.Bad;
272 const value = header_line[colon_pos + 2 ..];
273 try req.headers.data.lengths.appendSlice(req.allocator, &.{ name.len, 2, value.len, 2 });
266274 }
267 req.headers_raw = try headers_list.toOwnedSlice();
268275 }
269276
270277 pub const ReadError = net.Stream.ReadError;
......@@ -285,3 +292,29 @@ pub const ClientRequest = struct {
285292 };
286293 }
287294};
295
296pub const HeadersMap = struct {
297 data: extras.ManyArrayList(u8),
298
299 pub fn init(allocator: std.mem.Allocator) HeadersMap {
300 return .{
301 .data = .init(allocator),
302 };
303 }
304
305 pub fn deinit(map: *HeadersMap) void {
306 map.data.deinit();
307 }
308
309 pub fn count(map: *const HeadersMap) usize {
310 return map.data.lengths.items.len / 4;
311 }
312
313 pub fn name(map: *const HeadersMap, idx: usize) []const u8 {
314 return map.data.items(idx * 4 + 0);
315 }
316
317 pub fn value(map: *const HeadersMap, idx: usize) []const u8 {
318 return map.data.items(idx * 4 + 2);
319 }
320};
test.zig+3-3
......@@ -8,7 +8,7 @@ const Scheme = enum { http };
88test {
99 const allocator = std.testing.allocator;
1010 var req = try http.open(allocator, .GET, "http://he.net/");
11 defer req.close(allocator);
11 defer req.close();
1212 try req.writeUA();
1313 try req.send();
1414}
......@@ -26,7 +26,7 @@ fn httpbinMethod(comptime scheme: Scheme, comptime method: http.Method) !void {
2626 const allocator = std.testing.allocator;
2727 const url = @tagName(scheme) ++ "://httpbin.org/" ++ comptime extras.asciiLowerComptime(@tagName(method));
2828 var req = try http.open(allocator, method, url);
29 defer req.close(allocator);
29 defer req.close();
3030 try req.writeUA();
3131 try req.send();
3232 try expect(req.status).toEqual(.ok);
......@@ -40,7 +40,7 @@ fn httpbinUserAgent(comptime scheme: Scheme) !void {
4040 const allocator = std.testing.allocator;
4141 const url = @tagName(scheme) ++ "://httpbin.org/user-agent";
4242 var req = try http.open(allocator, .GET, url);
43 defer req.close(allocator);
43 defer req.close();
4444 try req.writeUA();
4545 try req.send();
4646 try expect(req.status).toEqual(.ok);
zigmod.yml+1
......@@ -7,6 +7,7 @@ dependencies:
77 - src: git https://github.com/nektro/zig-net
88 - src: git https://github.com/nektro/zig-whatwg-url
99 - src: git https://github.com/nektro/zig-nio
10 - src: git https://github.com/nektro/zig-extras
1011root_dependencies:
1112 - src: git https://github.com/nektro/zig-extras
1213 - src: git https://github.com/nektro/zig-json