• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

llnl / dftracer-utils / 29787659139

20 Jul 2026 11:34PM UTC coverage: 51.578% (-1.1%) from 52.66%
29787659139

Pull #99

github

web-flow
Merge ee805adeb into 06bc84ec9
Pull Request #99: Support CM time_metric (NS/MS/SEC/US) across reader and viz

34832 of 86278 branches covered (40.37%)

Branch coverage included in aggregate %.

1034 of 1319 new or added lines in 30 files covered. (78.39%)

5193 existing lines in 197 files now uncovered.

35349 of 49791 relevant lines covered (70.99%)

9765.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.01
/src/dftracer/utils/server/http_parser.cpp
1
// HTTP/1.1 request parser.
2
//
3
// Inspired by picohttpparser by Kazuho Oku, Tokuhiro Matsuno,
4
// Daisuke Murase, Shigeo Mitsunari (MIT license).
5
// https://github.com/h2o/picohttpparser
6
//
7
// Simplified C++ rewrite: only parses HTTP/1.x requests (not
8
// responses or chunked encoding). Zero-copy — all string_views
9
// point into the caller's buffer.
10

11
#include <dftracer/utils/server/http_parser.h>
12

13
#include <cctype>
14
#include <cstddef>
15

16
namespace dftracer::utils::server::parser {
17

18
namespace {
19

20
// Characters valid in an HTTP token (RFC 9110 Section 5.6.2).
21
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" /
22
//         "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
23
constexpr bool is_token_char(unsigned char c) {
1,336✔
24
    // clang-format off
25
    constexpr bool table[256] = {
1,336✔
26
        // 0-31: control
27
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
28
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
29
        // 32-47: SP ! " # $ % & ' ( ) * + , - . /
30
        0,1,0,1,1,1,1,1, 0,0,1,1,0,1,1,0,
31
        // 48-63: 0-9 : ; < = > ?
32
        1,1,1,1,1,1,1,1, 1,1,0,0,0,0,0,0,
33
        // 64-79: @ A-O
34
        0,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
35
        // 80-95: P-Z [ \ ] ^ _
36
        1,1,1,1,1,1,1,1, 1,1,1,0,0,0,1,1,
37
        // 96-111: ` a-o
38
        1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
39
        // 112-127: p-z { | } ~ DEL
40
        1,1,1,1,1,1,1,1, 1,1,1,0,1,0,1,0,
41
        // 128-255: high bytes
42
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
43
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
44
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
45
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
46
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
47
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
48
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
49
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
50
    };
51
    // clang-format on
52
    return table[c];
1,336✔
53
}
54

55
constexpr bool is_printable(unsigned char c) { return c >= 0x20 && c != 0x7F; }
1,173!
56

57
struct Cursor {
58
    const char* buf;
59
    const char* end;
60

61
    bool eof() const { return buf >= end; }
6,359✔
62
    char peek() const { return *buf; }
10,108✔
63
    void advance() { ++buf; }
5,671✔
64

65
    // Advance past a run of spaces.
66
    void skip_spaces() {
154✔
67
        while (!eof() && peek() == ' ') advance();
154!
68
    }
154✔
69
};
70

71
// Read a token (method, header name) up to a delimiter.
72
// Returns false on EOF or bad character.
73
static bool read_token(Cursor& c, const char*& start, std::size_t& len,
229✔
74
                       char delim) {
75
    start = c.buf;
229✔
76
    while (!c.eof()) {
1,564!
77
        if (c.peek() == delim) {
1,564✔
78
            len = static_cast<std::size_t>(c.buf - start);
228✔
79
            return len > 0;
228✔
80
        }
81
        if (!is_token_char(static_cast<unsigned char>(c.peek()))) return false;
1,336✔
82
        c.advance();
1,335✔
83
    }
84
    return false;  // EOF before delimiter
×
85
}
229✔
86

87
// Read the request-target (path) up to a space.
88
static bool read_request_target(Cursor& c, const char*& start,
78✔
89
                                std::size_t& len) {
90
    start = c.buf;
78✔
91
    while (!c.eof()) {
2,186!
92
        if (c.peek() == ' ') {
2,186✔
93
            len = static_cast<std::size_t>(c.buf - start);
76✔
94
            return len > 0;
76✔
95
        }
96
        auto ch = static_cast<unsigned char>(c.peek());
2,110✔
97
        if (ch < 0x21 || ch == 0x7F) return false;
2,110!
98
        c.advance();
2,108✔
99
    }
100
    return false;
×
101
}
78✔
102

103
// Read a header value up to CRLF or LF.
104
// Returns false on EOF, sets start/len.
105
static bool read_header_value(Cursor& c, const char*& start, std::size_t& len) {
150✔
106
    start = c.buf;
150✔
107
    while (!c.eof()) {
1,323!
108
        auto ch = static_cast<unsigned char>(c.peek());
1,323✔
109
        if (ch == '\r') {
1,323✔
110
            len = static_cast<std::size_t>(c.buf - start);
149✔
111
            c.advance();
149✔
112
            if (c.eof() || c.peek() != '\n') return false;
149!
113
            c.advance();
149✔
114
            // Trim trailing whitespace
115
            while (len > 0 && (start[len - 1] == ' ' || start[len - 1] == '\t'))
301!
116
                --len;
3✔
117
            return true;
149✔
118
        }
119
        if (ch == '\n') {
1,174✔
120
            len = static_cast<std::size_t>(c.buf - start);
1✔
121
            c.advance();
1✔
122
            while (len > 0 && (start[len - 1] == ' ' || start[len - 1] == '\t'))
2!
123
                --len;
×
124
            return true;
1✔
125
        }
126
        // Allow printable ASCII + HT (0x09)
127
        if (ch != '\t' && !is_printable(ch)) return false;
1,173!
128
        c.advance();
1,173✔
129
    }
130
    return false;
×
131
}
150✔
132

133
// Consume CRLF or bare LF.  Returns false if neither found.
134
static bool consume_eol(Cursor& c) {
150✔
135
    if (c.eof()) return false;
150!
136
    if (c.peek() == '\r') {
150✔
137
        c.advance();
148✔
138
        if (c.eof() || c.peek() != '\n') return false;
148!
139
        c.advance();
148✔
140
        return true;
148✔
141
    }
142
    if (c.peek() == '\n') {
2!
143
        c.advance();
2✔
144
        return true;
2✔
145
    }
146
    return false;
×
147
}
150✔
148

149
// Check whether the buffer contains a complete request (double CRLF).
150
static bool has_complete_request(const char* buf, std::size_t len) {
83✔
151
    if (len < 4) return false;
83✔
152
    // Scan for \r\n\r\n or \n\n
153
    for (std::size_t i = 0; i + 1 < len; ++i) {
6,226✔
154
        if (buf[i] == '\n') {
6,223✔
155
            if (buf[i + 1] == '\n') return true;
231✔
156
            if (i + 2 < len && buf[i + 1] == '\r' && buf[i + 2] == '\n')
230!
157
                return true;
78✔
158
        }
152✔
159
    }
6,144✔
160
    return false;
3✔
161
}
83✔
162

163
}  // namespace
164

165
int parse_request(const char* buf, std::size_t len, ParsedRequest& out) {
83✔
166
    // Quick completeness check to avoid partial parsing work.
167
    if (!has_complete_request(buf, len)) return -2;
83✔
168

169
    Cursor c{buf, buf + len};
79✔
170

171
    // Skip optional leading CRLF (some clients send it after POST body).
172
    if (!c.eof() && c.peek() == '\r') {
79!
173
        c.advance();
1✔
174
        if (c.eof() || c.peek() != '\n') return -1;
1!
175
        c.advance();
1✔
176
    } else if (!c.eof() && c.peek() == '\n') {
79!
177
        c.advance();
×
UNCOV
178
    }
×
179

180
    // Method
181
    const char* method_start = nullptr;
79✔
182
    std::size_t method_len = 0;
79✔
183
    if (!read_token(c, method_start, method_len, ' ')) return -1;
79✔
184
    c.advance();  // consume space
78✔
185
    c.skip_spaces();
78✔
186

187
    // Request-target (path)
188
    const char* path_start = nullptr;
78✔
189
    std::size_t path_len = 0;
78✔
190
    if (!read_request_target(c, path_start, path_len)) return -1;
78✔
191
    c.advance();  // consume space
76✔
192
    c.skip_spaces();
76✔
193

194
    // HTTP version: "HTTP/1.X"
195
    if (c.end - c.buf < 8) return -1;
76!
196
    if (c.buf[0] != 'H' || c.buf[1] != 'T' || c.buf[2] != 'T' ||
76!
197
        c.buf[3] != 'P' || c.buf[4] != '/' || c.buf[5] != '1' ||
75!
198
        c.buf[6] != '.')
75✔
199
        return -1;
1✔
200
    char ver_digit = c.buf[7];
75✔
201
    if (ver_digit < '0' || ver_digit > '9') return -1;
75!
202
    int minor_ver = ver_digit - '0';
75✔
203
    c.buf += 8;
75✔
204

205
    if (!consume_eol(c)) return -1;
75!
206

207
    // Headers
208
    out.method = std::string_view(method_start, method_len);
75✔
209
    out.path = std::string_view(path_start, path_len);
75✔
210
    out.minor_version = minor_ver;
75✔
211
    out.headers.clear();
75✔
212

213
    constexpr std::size_t MAX_HEADERS = 64;
75✔
214

215
    while (!c.eof()) {
225!
216
        // Empty line = end of headers
217
        if (c.peek() == '\r' || c.peek() == '\n') {
225✔
218
            if (!consume_eol(c)) return -1;
75!
219
            break;
75✔
220
        }
221

222
        if (out.headers.size() >= MAX_HEADERS) return -1;
150!
223

224
        // Header name
225
        const char* name_start = nullptr;
150✔
226
        std::size_t name_len = 0;
150✔
227
        if (!read_token(c, name_start, name_len, ':')) return -1;
150!
228
        c.advance();  // consume ':'
150✔
229

230
        // Skip OWS after colon
231
        while (!c.eof() && (c.peek() == ' ' || c.peek() == '\t')) c.advance();
452!
232

233
        // Header value
234
        const char* val_start = nullptr;
150✔
235
        std::size_t val_len = 0;
150✔
236
        if (!read_header_value(c, val_start, val_len)) return -1;
150!
237

238
        out.headers.push_back({std::string_view(name_start, name_len),
300✔
239
                               std::string_view(val_start, val_len)});
150✔
240
    }
241

242
    return static_cast<int>(c.buf - buf);
75✔
243
}
83✔
244

245
}  // namespace dftracer::utils::server::parser
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc