master
1bytes: []const u8,
2index: usize,
3is_trailer: bool,
4
5pub fn init(bytes: []const u8) HeaderIterator {
6 return .{
7 .bytes = bytes,
8 .index = std.mem.indexOfPosLinear(u8, bytes, 0, "\r\n").? + 2,
9 .is_trailer = false,
10 };
11}
12
13pub fn next(it: *HeaderIterator) ?std.http.Header {
14 const end = std.mem.indexOfPosLinear(u8, it.bytes, it.index, "\r\n").?;
15 if (it.index == end) { // found the trailer boundary (\r\n\r\n)
16 if (it.is_trailer) return null;
17
18 const next_end = std.mem.indexOfPosLinear(u8, it.bytes, end + 2, "\r\n") orelse
19 return null;
20
21 var kv_it = std.mem.splitScalar(u8, it.bytes[end + 2 .. next_end], ':');
22 const name = kv_it.first();
23 const value = kv_it.rest();
24
25 it.is_trailer = true;
26 it.index = next_end + 2;
27 if (name.len == 0)
28 return null;
29
30 return .{
31 .name = name,
32 .value = std.mem.trim(u8, value, " \t"),
33 };
34 } else { // normal header
35 var kv_it = std.mem.splitScalar(u8, it.bytes[it.index..end], ':');
36 const name = kv_it.first();
37 const value = kv_it.rest();
38
39 it.index = end + 2;
40 if (name.len == 0)
41 return null;
42
43 return .{
44 .name = name,
45 .value = std.mem.trim(u8, value, " \t"),
46 };
47 }
48}
49
50test next {
51 var it = HeaderIterator.init("200 OK\r\na: b\r\nc: \r\nd:e\r\n\r\nf: g\r\n\r\n");
52 try std.testing.expect(!it.is_trailer);
53 {
54 const header = it.next().?;
55 try std.testing.expect(!it.is_trailer);
56 try std.testing.expectEqualStrings("a", header.name);
57 try std.testing.expectEqualStrings("b", header.value);
58 }
59 {
60 const header = it.next().?;
61 try std.testing.expect(!it.is_trailer);
62 try std.testing.expectEqualStrings("c", header.name);
63 try std.testing.expectEqualStrings("", header.value);
64 }
65 {
66 const header = it.next().?;
67 try std.testing.expect(!it.is_trailer);
68 try std.testing.expectEqualStrings("d", header.name);
69 try std.testing.expectEqualStrings("e", header.value);
70 }
71 {
72 const header = it.next().?;
73 try std.testing.expect(it.is_trailer);
74 try std.testing.expectEqualStrings("f", header.name);
75 try std.testing.expectEqualStrings("g", header.value);
76 }
77 try std.testing.expectEqual(null, it.next());
78
79 it = HeaderIterator.init("200 OK\r\n: ss\r\n\r\n");
80 try std.testing.expect(!it.is_trailer);
81 try std.testing.expectEqual(null, it.next());
82
83 it = HeaderIterator.init("200 OK\r\na:b\r\n\r\n: ss\r\n\r\n");
84 try std.testing.expect(!it.is_trailer);
85 {
86 const header = it.next().?;
87 try std.testing.expect(!it.is_trailer);
88 try std.testing.expectEqualStrings("a", header.name);
89 try std.testing.expectEqualStrings("b", header.value);
90 }
91 try std.testing.expectEqual(null, it.next());
92 try std.testing.expect(it.is_trailer);
93}
94
95const HeaderIterator = @This();
96const std = @import("../std.zig");
97const assert = std.debug.assert;