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;