Coverage Report

Created: 2026-11-12 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/uWebSockets/src/ChunkedEncoding.h
Line
Count
Source
1
/*
2
 * Authored by Alex Hultman, 2018-2026.
3
 * Intellectual property of third-party.
4
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#ifndef UWS_CHUNKEDENCODING_H
19
#define UWS_CHUNKEDENCODING_H
20
21
/* Independent chunked encoding parser, used by HttpParser. */
22
23
#include <cstdint>
24
#include <string>
25
#include <cstring>
26
#include <cstdlib>
27
#include <algorithm>
28
#include <string_view>
29
#include "MoveOnlyFunction.h"
30
#include <optional>
31
32
namespace uWS {
33
34
    constexpr uint64_t STATE_HAS_SIZE = 1ull << (sizeof(uint64_t) * 8 - 1);
35
    constexpr uint64_t STATE_IS_CHUNKED = 1ull << (sizeof(uint64_t) * 8 - 2);
36
    
37
    // Internal sub-states encoded in top bits reserved for control state
38
    constexpr uint64_t STATE_EXTENSION_MODE         = 1ull << (sizeof(uint64_t) * 8 - 3);
39
    constexpr uint64_t STATE_TRAILER_MODE           = 1ull << (sizeof(uint64_t) * 8 - 4);
40
    constexpr uint64_t STATE_EXTENSION_EXPECTS_NAME = 1ull << (sizeof(uint64_t) * 8 - 5);
41
    constexpr uint64_t STATE_EXTENSION_QUOTED       = 1ull << (sizeof(uint64_t) * 8 - 6);
42
    constexpr uint64_t STATE_EXTENSION_EXPECTS_LF   = 1ull << (sizeof(uint64_t) * 8 - 7);
43
    constexpr uint64_t STATE_EXTENSION_IN_NAME      = 1ull << (sizeof(uint64_t) * 8 - 8);
44
    
45
    constexpr uint64_t STATE_SIZE_MASK = ~(0xFFull << (sizeof(uint64_t) * 8 - 8));
46
    constexpr uint64_t STATE_IS_ERROR = ~0ull;
47
    constexpr uint64_t STATE_SIZE_OVERFLOW = 0x0Full << (sizeof(uint64_t) * 8 - 12);
48
49
    /* Helper: RFC 9110 Section 5.6.2 Token character check */
50
177k
    inline bool isValidTokenChar(unsigned char c) {
51
177k
        if (c < 0x20 || c >= 0x7F) return false;
52
173k
        switch (c) {
53
4.80k
            case '(': case ')': case '<': case '>': case '@':
54
9.39k
            case ',': case ';': case ':': case '\\': case '"':
55
14.8k
            case '/': case '[': case ']': case '?': case '=':
56
17.4k
            case '{': case '}': case ' ': case '\t':
57
17.4k
                return false;
58
156k
            default:
59
156k
                return true;
60
173k
        }
61
173k
    }
62
63
3.87M
    inline uint64_t chunkSize(uint64_t state) {
64
3.87M
        return state & STATE_SIZE_MASK;
65
3.87M
    }
66
67
14.3k
    inline void decChunkSize(uint64_t &state, unsigned int by) {
68
14.3k
        state = (state & ~STATE_SIZE_MASK) | (chunkSize(state) - by);
69
14.3k
    }
70
71
250k
    inline bool hasChunkSize(uint64_t state) {
72
250k
        return state & STATE_HAS_SIZE;
73
250k
    }
74
75
67.0k
    inline bool isParsingChunkedEncoding(uint64_t state) {
76
67.0k
        return state & ~STATE_SIZE_MASK;
77
67.0k
    }
78
79
192k
    inline bool isParsingInvalidChunkedEncoding(uint64_t state) {
80
192k
        return state == STATE_IS_ERROR;
81
192k
    }
82
83
98.1k
    inline void consumeHexNumber(std::string_view &data, uint64_t &state) {
84
769k
        while (data.length()) {
85
755k
            unsigned char c = (unsigned char)data.data()[0];
86
87
755k
            if (!(state & STATE_EXTENSION_MODE)) {
88
232k
                if (c == ';') { 
89
38.7k
                    if (!hasChunkSize(state) && (state & STATE_SIZE_MASK) == 0 && !(state & STATE_IS_CHUNKED)) {
90
1.09k
                        state = STATE_IS_ERROR;
91
1.09k
                        return;
92
1.09k
                    }
93
37.6k
                    state |= STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME;
94
37.6k
                    data.remove_prefix(1);
95
37.6k
                    continue;
96
38.7k
                }
97
98
193k
                if (c == '\r') {
99
16.9k
                    state |= STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_LF;
100
16.9k
                    data.remove_prefix(1);
101
16.9k
                    continue;
102
16.9k
                }
103
104
176k
                if (c == '\n') {
105
22.1k
                    data.remove_prefix(1);
106
22.1k
                    state += 2; 
107
22.1k
                    state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
108
22.1k
                    state &= ~(STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME | STATE_EXTENSION_IN_NAME | STATE_EXTENSION_QUOTED | STATE_EXTENSION_EXPECTS_LF);
109
22.1k
                    return;
110
22.1k
                }
111
112
154k
                unsigned int number = 0;
113
154k
                if (c >= '0' && c <= '9') number = c - '0';
114
92.2k
                else if (c >= 'a' && c <= 'f') number = c - 'a' + 10;
115
41.5k
                else if (c >= 'A' && c <= 'F') number = c - 'A' + 10;
116
7.08k
                else {
117
7.08k
                    state = STATE_IS_ERROR;
118
7.08k
                    return;
119
7.08k
                }
120
121
147k
                if (chunkSize(state) & STATE_SIZE_OVERFLOW) {
122
1.12k
                    state = STATE_IS_ERROR;
123
1.12k
                    return;
124
1.12k
                }
125
126
146k
                uint64_t bits = state & STATE_IS_CHUNKED;
127
146k
                state = (state & STATE_SIZE_MASK) * 16ull + number;
128
146k
                state |= bits;
129
146k
                data.remove_prefix(1);
130
522k
            } else {
131
522k
                if (state & STATE_EXTENSION_EXPECTS_LF) {
132
25.2k
                    if (c != '\n') {
133
1.47k
                        state = STATE_IS_ERROR;
134
1.47k
                        return;
135
1.47k
                    }
136
23.7k
                    data.remove_prefix(1);
137
23.7k
                    state += 2; 
138
23.7k
                    state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
139
23.7k
                    state &= ~(STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME | STATE_EXTENSION_IN_NAME | STATE_EXTENSION_QUOTED | STATE_EXTENSION_EXPECTS_LF);
140
23.7k
                    return;
141
25.2k
                }
142
143
497k
                if (c == 0x00 || (c < 0x20 && c != '\r' && c != '\n' && c != '\t')) {
144
2.13k
                    state = STATE_IS_ERROR;
145
2.13k
                    return;
146
2.13k
                }
147
148
495k
                if (c == '\r') {
149
9.44k
                    if (state & STATE_EXTENSION_EXPECTS_NAME) {
150
1.37k
                        state = STATE_IS_ERROR;
151
1.37k
                        return;
152
1.37k
                    }
153
8.06k
                    state |= STATE_EXTENSION_EXPECTS_LF;
154
8.06k
                    data.remove_prefix(1);
155
8.06k
                    continue;
156
9.44k
                }
157
158
485k
                if (state & STATE_EXTENSION_EXPECTS_NAME) {
159
42.2k
                    if (!isValidTokenChar(c)) {
160
6.99k
                        state = STATE_IS_ERROR;
161
6.99k
                        return;
162
6.99k
                    }
163
35.2k
                    state &= ~STATE_EXTENSION_EXPECTS_NAME;
164
35.2k
                    state |= STATE_EXTENSION_IN_NAME;
165
443k
                } else if (state & STATE_EXTENSION_IN_NAME) {
166
151k
                    if (c == '=') state &= ~STATE_EXTENSION_IN_NAME;
167
140k
                    else if (c == ';') {
168
5.08k
                        state &= ~STATE_EXTENSION_IN_NAME;
169
5.08k
                        state |= STATE_EXTENSION_EXPECTS_NAME;
170
134k
                    } else if (!isValidTokenChar(c)) {
171
13.6k
                        state = STATE_IS_ERROR;
172
13.6k
                        return;
173
13.6k
                    }
174
292k
                } else {
175
292k
                    if (c == '"') state ^= STATE_EXTENSION_QUOTED;
176
292k
                    if (c == ';' && !(state & STATE_EXTENSION_QUOTED)) state |= STATE_EXTENSION_EXPECTS_NAME;
177
292k
                }
178
179
465k
                data.remove_prefix(1);
180
465k
                if (c == '\n' && !(state & STATE_EXTENSION_QUOTED)) {
181
3.14k
                    if (state & STATE_EXTENSION_EXPECTS_NAME) {
182
0
                        state = STATE_IS_ERROR;
183
0
                        return;
184
0
                    }
185
3.14k
                    state += 2; 
186
3.14k
                    state |= STATE_HAS_SIZE | STATE_IS_CHUNKED;
187
3.14k
                    state &= ~(STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME | STATE_EXTENSION_IN_NAME | STATE_EXTENSION_QUOTED | STATE_EXTENSION_EXPECTS_LF);
188
3.14k
                    return;
189
3.14k
                }
190
465k
            }
191
755k
        }
192
98.1k
    }
193
194
139k
    static std::optional<std::string_view> getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
195
192k
        while (data.length()) {
196
            
197
            // Standard-compliant Trailer parsing state machine
198
163k
            if (state & STATE_TRAILER_MODE) {
199
993k
                while (data.length()) {
200
986k
                    char c = data.data()[0];
201
986k
                    data.remove_prefix(1);
202
203
986k
                    if (chunkSize(state) == 0) {
204
                        // Start of line: \r means final empty line, else it's a trailer header
205
23.8k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1;
206
11.5k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
207
10.6k
                        else state = (state & ~STATE_SIZE_MASK) | 2;
208
962k
                    } else if (chunkSize(state) == 1) {
209
                        // Expecting \n of final empty line
210
12.0k
                        if (c == '\n') {
211
10.8k
                            state = 0; 
212
10.8k
                            return std::nullopt;
213
10.8k
                        }
214
1.21k
                        state = STATE_IS_ERROR;
215
1.21k
                        return std::nullopt;
216
950k
                    } else if (chunkSize(state) == 2) {
217
                        // Inside trailer header, wait for \r
218
378k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3;
219
366k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
220
572k
                    } else if (chunkSize(state) == 3) {
221
                        // Expecting \n to terminate the header line
222
13.2k
                        if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0;
223
4.76k
                        else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2;
224
13.2k
                    }
225
986k
                }
226
6.76k
                return std::nullopt;
227
20.8k
            }
228
229
            // Drop Trailer Mode (Legacy fallback)
230
142k
            if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
231
0
                while (data.length() && chunkSize(state)) {
232
0
                    data.remove_prefix(1);
233
0
                    decChunkSize(state, 1);
234
235
0
                    if (chunkSize(state) == 0) {
236
0
                        state = 0;
237
0
                        return std::nullopt;
238
0
                    }
239
0
                }
240
0
                continue;
241
0
            }
242
243
142k
            if (!hasChunkSize(state)) {
244
98.1k
                consumeHexNumber(data, state);
245
98.1k
                if (isParsingInvalidChunkedEncoding(state)) {
246
34.9k
                    return std::nullopt;
247
34.9k
                }
248
63.2k
                if (hasChunkSize(state) && chunkSize(state) == 2) {
249
15.3k
                    if (trailer) {
250
15.3k
                        state = STATE_TRAILER_MODE | 0; 
251
15.3k
                    } else {
252
0
                        state = 2 | STATE_HAS_SIZE;
253
0
                    }
254
15.3k
                    return std::string_view(nullptr, 0);
255
15.3k
                }
256
47.9k
                continue;
257
63.2k
            }
258
259
            // Emit Body Payload Data with strict CRLF enforcement
260
44.0k
            if (data.length() >= chunkSize(state)) {
261
27.7k
                std::string_view emitSoon;
262
27.7k
                bool shouldEmit = false;
263
                
264
27.7k
                if (chunkSize(state) > 2) {
265
19.3k
                    if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') {
266
2.91k
                        state = STATE_IS_ERROR;
267
2.91k
                        return std::nullopt;
268
2.91k
                    }
269
16.4k
                    emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
270
16.4k
                    shouldEmit = true;
271
16.4k
                } else if (chunkSize(state) == 2) {
272
2.98k
                    if (data[0] != '\r' || data[1] != '\n') {
273
1.65k
                        state = STATE_IS_ERROR;
274
1.65k
                        return std::nullopt;
275
1.65k
                    }
276
5.33k
                } else if (chunkSize(state) == 1) {
277
3.70k
                    if (data[0] != '\n') {
278
989
                        state = STATE_IS_ERROR;
279
989
                        return std::nullopt;
280
989
                    }
281
3.70k
                }
282
283
22.1k
                data.remove_prefix(chunkSize(state));
284
22.1k
                state = STATE_IS_CHUNKED;
285
22.1k
                if (shouldEmit) return emitSoon;
286
5.67k
                continue;
287
22.1k
            } else {
288
16.3k
                std::string_view emitSoon;
289
16.3k
                if (chunkSize(state) > 2) {
290
14.4k
                    uint64_t maximalAppEmit = chunkSize(state) - 2;
291
14.4k
                    if (data.length() > maximalAppEmit) {
292
                        // Enforce partial CRLF boundary safety limit
293
4.55k
                        if (data[maximalAppEmit] != '\r') {
294
986
                            state = STATE_IS_ERROR;
295
986
                            return std::nullopt;
296
986
                        }
297
3.57k
                        emitSoon = data.substr(0, maximalAppEmit);
298
9.90k
                    } else {
299
9.90k
                        emitSoon = data;
300
9.90k
                    }
301
14.4k
                } else if (chunkSize(state) == 2) {
302
1.87k
                    if (data[0] != '\r') {
303
976
                        state = STATE_IS_ERROR;
304
976
                        return std::nullopt;
305
976
                    }
306
1.87k
                }
307
308
14.3k
                decChunkSize(state, (unsigned int) data.length());
309
14.3k
                state |= STATE_IS_CHUNKED;
310
14.3k
                data.remove_prefix(data.length());
311
14.3k
                if (emitSoon.length()) return emitSoon;
312
898
                else return std::nullopt;
313
14.3k
            }
314
44.0k
        }
315
316
29.6k
        return std::nullopt;
317
139k
    }
EpollEchoServerPubSub.cpp:uWS::getNextChunk(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, unsigned long&, bool)
Line
Count
Source
194
29.1k
    static std::optional<std::string_view> getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
195
43.2k
        while (data.length()) {
196
            
197
            // Standard-compliant Trailer parsing state machine
198
33.1k
            if (state & STATE_TRAILER_MODE) {
199
41.5k
                while (data.length()) {
200
40.1k
                    char c = data.data()[0];
201
40.1k
                    data.remove_prefix(1);
202
203
40.1k
                    if (chunkSize(state) == 0) {
204
                        // Start of line: \r means final empty line, else it's a trailer header
205
2.62k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1;
206
1.89k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
207
1.68k
                        else state = (state & ~STATE_SIZE_MASK) | 2;
208
37.5k
                    } else if (chunkSize(state) == 1) {
209
                        // Expecting \n of final empty line
210
725
                        if (c == '\n') {
211
278
                            state = 0; 
212
278
                            return std::nullopt;
213
278
                        }
214
447
                        state = STATE_IS_ERROR;
215
447
                        return std::nullopt;
216
36.8k
                    } else if (chunkSize(state) == 2) {
217
                        // Inside trailer header, wait for \r
218
34.6k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3;
219
32.9k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
220
34.6k
                    } else if (chunkSize(state) == 3) {
221
                        // Expecting \n to terminate the header line
222
2.18k
                        if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0;
223
1.01k
                        else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2;
224
2.18k
                    }
225
40.1k
                }
226
1.34k
                return std::nullopt;
227
2.64k
            }
228
229
            // Drop Trailer Mode (Legacy fallback)
230
30.5k
            if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
231
0
                while (data.length() && chunkSize(state)) {
232
0
                    data.remove_prefix(1);
233
0
                    decChunkSize(state, 1);
234
235
0
                    if (chunkSize(state) == 0) {
236
0
                        state = 0;
237
0
                        return std::nullopt;
238
0
                    }
239
0
                }
240
0
                continue;
241
0
            }
242
243
30.5k
            if (!hasChunkSize(state)) {
244
22.6k
                consumeHexNumber(data, state);
245
22.6k
                if (isParsingInvalidChunkedEncoding(state)) {
246
7.97k
                    return std::nullopt;
247
7.97k
                }
248
14.6k
                if (hasChunkSize(state) && chunkSize(state) == 2) {
249
1.47k
                    if (trailer) {
250
1.47k
                        state = STATE_TRAILER_MODE | 0; 
251
1.47k
                    } else {
252
0
                        state = 2 | STATE_HAS_SIZE;
253
0
                    }
254
1.47k
                    return std::string_view(nullptr, 0);
255
1.47k
                }
256
13.2k
                continue;
257
14.6k
            }
258
259
            // Emit Body Payload Data with strict CRLF enforcement
260
7.83k
            if (data.length() >= chunkSize(state)) {
261
4.56k
                std::string_view emitSoon;
262
4.56k
                bool shouldEmit = false;
263
                
264
4.56k
                if (chunkSize(state) > 2) {
265
3.07k
                    if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') {
266
760
                        state = STATE_IS_ERROR;
267
760
                        return std::nullopt;
268
760
                    }
269
2.31k
                    emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
270
2.31k
                    shouldEmit = true;
271
2.31k
                } else if (chunkSize(state) == 2) {
272
605
                    if (data[0] != '\r' || data[1] != '\n') {
273
411
                        state = STATE_IS_ERROR;
274
411
                        return std::nullopt;
275
411
                    }
276
880
                } else if (chunkSize(state) == 1) {
277
534
                    if (data[0] != '\n') {
278
220
                        state = STATE_IS_ERROR;
279
220
                        return std::nullopt;
280
220
                    }
281
534
                }
282
283
3.17k
                data.remove_prefix(chunkSize(state));
284
3.17k
                state = STATE_IS_CHUNKED;
285
3.17k
                if (shouldEmit) return emitSoon;
286
854
                continue;
287
3.27k
            } else {
288
3.27k
                std::string_view emitSoon;
289
3.27k
                if (chunkSize(state) > 2) {
290
2.87k
                    uint64_t maximalAppEmit = chunkSize(state) - 2;
291
2.87k
                    if (data.length() > maximalAppEmit) {
292
                        // Enforce partial CRLF boundary safety limit
293
1.37k
                        if (data[maximalAppEmit] != '\r') {
294
280
                            state = STATE_IS_ERROR;
295
280
                            return std::nullopt;
296
280
                        }
297
1.09k
                        emitSoon = data.substr(0, maximalAppEmit);
298
1.49k
                    } else {
299
1.49k
                        emitSoon = data;
300
1.49k
                    }
301
2.87k
                } else if (chunkSize(state) == 2) {
302
397
                    if (data[0] != '\r') {
303
203
                        state = STATE_IS_ERROR;
304
203
                        return std::nullopt;
305
203
                    }
306
397
                }
307
308
2.78k
                decChunkSize(state, (unsigned int) data.length());
309
2.78k
                state |= STATE_IS_CHUNKED;
310
2.78k
                data.remove_prefix(data.length());
311
2.78k
                if (emitSoon.length()) return emitSoon;
312
194
                else return std::nullopt;
313
2.78k
            }
314
7.83k
        }
315
316
10.1k
        return std::nullopt;
317
29.1k
    }
EpollHelloWorld.cpp:uWS::getNextChunk(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, unsigned long&, bool)
Line
Count
Source
194
20.3k
    static std::optional<std::string_view> getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
195
27.5k
        while (data.length()) {
196
            
197
            // Standard-compliant Trailer parsing state machine
198
23.6k
            if (state & STATE_TRAILER_MODE) {
199
13.8k
                while (data.length()) {
200
13.4k
                    char c = data.data()[0];
201
13.4k
                    data.remove_prefix(1);
202
203
13.4k
                    if (chunkSize(state) == 0) {
204
                        // Start of line: \r means final empty line, else it's a trailer header
205
2.45k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1;
206
1.94k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
207
1.70k
                        else state = (state & ~STATE_SIZE_MASK) | 2;
208
10.9k
                    } else if (chunkSize(state) == 1) {
209
                        // Expecting \n of final empty line
210
504
                        if (c == '\n') {
211
208
                            state = 0; 
212
208
                            return std::nullopt;
213
208
                        }
214
296
                        state = STATE_IS_ERROR;
215
296
                        return std::nullopt;
216
10.4k
                    } else if (chunkSize(state) == 2) {
217
                        // Inside trailer header, wait for \r
218
8.66k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3;
219
7.00k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
220
8.66k
                    } else if (chunkSize(state) == 3) {
221
                        // Expecting \n to terminate the header line
222
1.81k
                        if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0;
223
497
                        else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2;
224
1.81k
                    }
225
13.4k
                }
226
424
                return std::nullopt;
227
1.38k
            }
228
229
            // Drop Trailer Mode (Legacy fallback)
230
22.2k
            if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
231
0
                while (data.length() && chunkSize(state)) {
232
0
                    data.remove_prefix(1);
233
0
                    decChunkSize(state, 1);
234
235
0
                    if (chunkSize(state) == 0) {
236
0
                        state = 0;
237
0
                        return std::nullopt;
238
0
                    }
239
0
                }
240
0
                continue;
241
0
            }
242
243
22.2k
            if (!hasChunkSize(state)) {
244
15.3k
                consumeHexNumber(data, state);
245
15.3k
                if (isParsingInvalidChunkedEncoding(state)) {
246
7.75k
                    return std::nullopt;
247
7.75k
                }
248
7.57k
                if (hasChunkSize(state) && chunkSize(state) == 2) {
249
1.17k
                    if (trailer) {
250
1.17k
                        state = STATE_TRAILER_MODE | 0; 
251
1.17k
                    } else {
252
0
                        state = 2 | STATE_HAS_SIZE;
253
0
                    }
254
1.17k
                    return std::string_view(nullptr, 0);
255
1.17k
                }
256
6.40k
                continue;
257
7.57k
            }
258
259
            // Emit Body Payload Data with strict CRLF enforcement
260
6.91k
            if (data.length() >= chunkSize(state)) {
261
3.96k
                std::string_view emitSoon;
262
3.96k
                bool shouldEmit = false;
263
                
264
3.96k
                if (chunkSize(state) > 2) {
265
2.59k
                    if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') {
266
442
                        state = STATE_IS_ERROR;
267
442
                        return std::nullopt;
268
442
                    }
269
2.15k
                    emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
270
2.15k
                    shouldEmit = true;
271
2.15k
                } else if (chunkSize(state) == 2) {
272
599
                    if (data[0] != '\r' || data[1] != '\n') {
273
404
                        state = STATE_IS_ERROR;
274
404
                        return std::nullopt;
275
404
                    }
276
771
                } else if (chunkSize(state) == 1) {
277
571
                    if (data[0] != '\n') {
278
208
                        state = STATE_IS_ERROR;
279
208
                        return std::nullopt;
280
208
                    }
281
571
                }
282
283
2.90k
                data.remove_prefix(chunkSize(state));
284
2.90k
                state = STATE_IS_CHUNKED;
285
2.90k
                if (shouldEmit) return emitSoon;
286
758
                continue;
287
2.95k
            } else {
288
2.95k
                std::string_view emitSoon;
289
2.95k
                if (chunkSize(state) > 2) {
290
2.55k
                    uint64_t maximalAppEmit = chunkSize(state) - 2;
291
2.55k
                    if (data.length() > maximalAppEmit) {
292
                        // Enforce partial CRLF boundary safety limit
293
631
                        if (data[maximalAppEmit] != '\r') {
294
232
                            state = STATE_IS_ERROR;
295
232
                            return std::nullopt;
296
232
                        }
297
399
                        emitSoon = data.substr(0, maximalAppEmit);
298
1.92k
                    } else {
299
1.92k
                        emitSoon = data;
300
1.92k
                    }
301
2.55k
                } else if (chunkSize(state) == 2) {
302
397
                    if (data[0] != '\r') {
303
203
                        state = STATE_IS_ERROR;
304
203
                        return std::nullopt;
305
203
                    }
306
397
                }
307
308
2.52k
                decChunkSize(state, (unsigned int) data.length());
309
2.52k
                state |= STATE_IS_CHUNKED;
310
2.52k
                data.remove_prefix(data.length());
311
2.52k
                if (emitSoon.length()) return emitSoon;
312
194
                else return std::nullopt;
313
2.52k
            }
314
6.91k
        }
315
316
3.86k
        return std::nullopt;
317
20.3k
    }
EpollEchoServer.cpp:uWS::getNextChunk(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, unsigned long&, bool)
Line
Count
Source
194
26.9k
    static std::optional<std::string_view> getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
195
35.0k
        while (data.length()) {
196
            
197
            // Standard-compliant Trailer parsing state machine
198
29.9k
            if (state & STATE_TRAILER_MODE) {
199
218k
                while (data.length()) {
200
215k
                    char c = data.data()[0];
201
215k
                    data.remove_prefix(1);
202
203
215k
                    if (chunkSize(state) == 0) {
204
                        // Start of line: \r means final empty line, else it's a trailer header
205
3.52k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1;
206
2.47k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
207
2.27k
                        else state = (state & ~STATE_SIZE_MASK) | 2;
208
211k
                    } else if (chunkSize(state) == 1) {
209
                        // Expecting \n of final empty line
210
770
                        if (c == '\n') {
211
542
                            state = 0; 
212
542
                            return std::nullopt;
213
542
                        }
214
228
                        state = STATE_IS_ERROR;
215
228
                        return std::nullopt;
216
210k
                    } else if (chunkSize(state) == 2) {
217
                        // Inside trailer header, wait for \r
218
208k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3;
219
205k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
220
208k
                    } else if (chunkSize(state) == 3) {
221
                        // Expecting \n to terminate the header line
222
2.66k
                        if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0;
223
1.12k
                        else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2;
224
2.66k
                    }
225
215k
                }
226
3.46k
                return std::nullopt;
227
4.76k
            }
228
229
            // Drop Trailer Mode (Legacy fallback)
230
25.1k
            if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
231
0
                while (data.length() && chunkSize(state)) {
232
0
                    data.remove_prefix(1);
233
0
                    decChunkSize(state, 1);
234
235
0
                    if (chunkSize(state) == 0) {
236
0
                        state = 0;
237
0
                        return std::nullopt;
238
0
                    }
239
0
                }
240
0
                continue;
241
0
            }
242
243
25.1k
            if (!hasChunkSize(state)) {
244
17.7k
                consumeHexNumber(data, state);
245
17.7k
                if (isParsingInvalidChunkedEncoding(state)) {
246
8.40k
                    return std::nullopt;
247
8.40k
                }
248
9.30k
                if (hasChunkSize(state) && chunkSize(state) == 2) {
249
2.00k
                    if (trailer) {
250
2.00k
                        state = STATE_TRAILER_MODE | 0; 
251
2.00k
                    } else {
252
0
                        state = 2 | STATE_HAS_SIZE;
253
0
                    }
254
2.00k
                    return std::string_view(nullptr, 0);
255
2.00k
                }
256
7.30k
                continue;
257
9.30k
            }
258
259
            // Emit Body Payload Data with strict CRLF enforcement
260
7.43k
            if (data.length() >= chunkSize(state)) {
261
3.85k
                std::string_view emitSoon;
262
3.85k
                bool shouldEmit = false;
263
                
264
3.85k
                if (chunkSize(state) > 2) {
265
2.46k
                    if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') {
266
1.00k
                        state = STATE_IS_ERROR;
267
1.00k
                        return std::nullopt;
268
1.00k
                    }
269
1.46k
                    emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
270
1.46k
                    shouldEmit = true;
271
1.46k
                } else if (chunkSize(state) == 2) {
272
593
                    if (data[0] != '\r' || data[1] != '\n') {
273
399
                        state = STATE_IS_ERROR;
274
399
                        return std::nullopt;
275
399
                    }
276
798
                } else if (chunkSize(state) == 1) {
277
451
                    if (data[0] != '\n') {
278
210
                        state = STATE_IS_ERROR;
279
210
                        return std::nullopt;
280
210
                    }
281
451
                }
282
283
2.24k
                data.remove_prefix(chunkSize(state));
284
2.24k
                state = STATE_IS_CHUNKED;
285
2.24k
                if (shouldEmit) return emitSoon;
286
782
                continue;
287
3.57k
            } else {
288
3.57k
                std::string_view emitSoon;
289
3.57k
                if (chunkSize(state) > 2) {
290
3.08k
                    uint64_t maximalAppEmit = chunkSize(state) - 2;
291
3.08k
                    if (data.length() > maximalAppEmit) {
292
                        // Enforce partial CRLF boundary safety limit
293
654
                        if (data[maximalAppEmit] != '\r') {
294
202
                            state = STATE_IS_ERROR;
295
202
                            return std::nullopt;
296
202
                        }
297
452
                        emitSoon = data.substr(0, maximalAppEmit);
298
2.43k
                    } else {
299
2.43k
                        emitSoon = data;
300
2.43k
                    }
301
3.08k
                } else if (chunkSize(state) == 2) {
302
490
                    if (data[0] != '\r') {
303
214
                        state = STATE_IS_ERROR;
304
214
                        return std::nullopt;
305
214
                    }
306
490
                }
307
308
3.15k
                decChunkSize(state, (unsigned int) data.length());
309
3.15k
                state |= STATE_IS_CHUNKED;
310
3.15k
                data.remove_prefix(data.length());
311
3.15k
                if (emitSoon.length()) return emitSoon;
312
276
                else return std::nullopt;
313
3.15k
            }
314
7.43k
        }
315
316
5.10k
        return std::nullopt;
317
26.9k
    }
AsyncEpollHelloWorld.cpp:uWS::getNextChunk(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, unsigned long&, bool)
Line
Count
Source
194
27.3k
    static std::optional<std::string_view> getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
195
38.6k
        while (data.length()) {
196
            
197
            // Standard-compliant Trailer parsing state machine
198
34.6k
            if (state & STATE_TRAILER_MODE) {
199
7.26k
                while (data.length()) {
200
6.78k
                    char c = data.data()[0];
201
6.78k
                    data.remove_prefix(1);
202
203
6.78k
                    if (chunkSize(state) == 0) {
204
                        // Start of line: \r means final empty line, else it's a trailer header
205
1.55k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1;
206
1.10k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
207
839
                        else state = (state & ~STATE_SIZE_MASK) | 2;
208
5.22k
                    } else if (chunkSize(state) == 1) {
209
                        // Expecting \n of final empty line
210
447
                        if (c == '\n') {
211
211
                            state = 0; 
212
211
                            return std::nullopt;
213
211
                        }
214
236
                        state = STATE_IS_ERROR;
215
236
                        return std::nullopt;
216
4.77k
                    } else if (chunkSize(state) == 2) {
217
                        // Inside trailer header, wait for \r
218
3.79k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3;
219
2.98k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
220
3.79k
                    } else if (chunkSize(state) == 3) {
221
                        // Expecting \n to terminate the header line
222
984
                        if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0;
223
526
                        else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2;
224
984
                    }
225
6.78k
                }
226
486
                return std::nullopt;
227
1.44k
            }
228
229
            // Drop Trailer Mode (Legacy fallback)
230
33.2k
            if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
231
0
                while (data.length() && chunkSize(state)) {
232
0
                    data.remove_prefix(1);
233
0
                    decChunkSize(state, 1);
234
235
0
                    if (chunkSize(state) == 0) {
236
0
                        state = 0;
237
0
                        return std::nullopt;
238
0
                    }
239
0
                }
240
0
                continue;
241
0
            }
242
243
33.2k
            if (!hasChunkSize(state)) {
244
21.5k
                consumeHexNumber(data, state);
245
21.5k
                if (isParsingInvalidChunkedEncoding(state)) {
246
10.5k
                    return std::nullopt;
247
10.5k
                }
248
11.0k
                if (hasChunkSize(state) && chunkSize(state) == 2) {
249
1.13k
                    if (trailer) {
250
1.13k
                        state = STATE_TRAILER_MODE | 0; 
251
1.13k
                    } else {
252
0
                        state = 2 | STATE_HAS_SIZE;
253
0
                    }
254
1.13k
                    return std::string_view(nullptr, 0);
255
1.13k
                }
256
9.86k
                continue;
257
11.0k
            }
258
259
            // Emit Body Payload Data with strict CRLF enforcement
260
11.7k
            if (data.length() >= chunkSize(state)) {
261
7.92k
                std::string_view emitSoon;
262
7.92k
                bool shouldEmit = false;
263
                
264
7.92k
                if (chunkSize(state) > 2) {
265
5.80k
                    if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') {
266
615
                        state = STATE_IS_ERROR;
267
615
                        return std::nullopt;
268
615
                    }
269
5.18k
                    emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
270
5.18k
                    shouldEmit = true;
271
5.18k
                } else if (chunkSize(state) == 2) {
272
680
                    if (data[0] != '\r' || data[1] != '\n') {
273
412
                        state = STATE_IS_ERROR;
274
412
                        return std::nullopt;
275
412
                    }
276
1.44k
                } else if (chunkSize(state) == 1) {
277
1.19k
                    if (data[0] != '\n') {
278
338
                        state = STATE_IS_ERROR;
279
338
                        return std::nullopt;
280
338
                    }
281
1.19k
                }
282
283
6.55k
                data.remove_prefix(chunkSize(state));
284
6.55k
                state = STATE_IS_CHUNKED;
285
6.55k
                if (shouldEmit) return emitSoon;
286
1.37k
                continue;
287
6.55k
            } else {
288
3.79k
                std::string_view emitSoon;
289
3.79k
                if (chunkSize(state) > 2) {
290
3.22k
                    uint64_t maximalAppEmit = chunkSize(state) - 2;
291
3.22k
                    if (data.length() > maximalAppEmit) {
292
                        // Enforce partial CRLF boundary safety limit
293
1.45k
                        if (data[maximalAppEmit] != '\r') {
294
262
                            state = STATE_IS_ERROR;
295
262
                            return std::nullopt;
296
262
                        }
297
1.18k
                        emitSoon = data.substr(0, maximalAppEmit);
298
1.77k
                    } else {
299
1.77k
                        emitSoon = data;
300
1.77k
                    }
301
3.22k
                } else if (chunkSize(state) == 2) {
302
569
                    if (data[0] != '\r') {
303
337
                        state = STATE_IS_ERROR;
304
337
                        return std::nullopt;
305
337
                    }
306
569
                }
307
308
3.19k
                decChunkSize(state, (unsigned int) data.length());
309
3.19k
                state |= STATE_IS_CHUNKED;
310
3.19k
                data.remove_prefix(data.length());
311
3.19k
                if (emitSoon.length()) return emitSoon;
312
232
                else return std::nullopt;
313
3.19k
            }
314
11.7k
        }
315
316
3.92k
        return std::nullopt;
317
27.3k
    }
Http.cpp:uWS::getNextChunk(std::__1::basic_string_view<char, std::__1::char_traits<char> >&, unsigned long&, bool)
Line
Count
Source
194
35.3k
    static std::optional<std::string_view> getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) {
195
48.4k
        while (data.length()) {
196
            
197
            // Standard-compliant Trailer parsing state machine
198
41.7k
            if (state & STATE_TRAILER_MODE) {
199
712k
                while (data.length()) {
200
711k
                    char c = data.data()[0];
201
711k
                    data.remove_prefix(1);
202
203
711k
                    if (chunkSize(state) == 0) {
204
                        // Start of line: \r means final empty line, else it's a trailer header
205
13.7k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1;
206
4.13k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
207
4.13k
                        else state = (state & ~STATE_SIZE_MASK) | 2;
208
697k
                    } else if (chunkSize(state) == 1) {
209
                        // Expecting \n of final empty line
210
9.59k
                        if (c == '\n') {
211
9.58k
                            state = 0; 
212
9.58k
                            return std::nullopt;
213
9.58k
                        }
214
12
                        state = STATE_IS_ERROR;
215
12
                        return std::nullopt;
216
687k
                    } else if (chunkSize(state) == 2) {
217
                        // Inside trailer header, wait for \r
218
122k
                        if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3;
219
117k
                        else if (c == '\n') { state = STATE_IS_ERROR; return std::nullopt; }
220
564k
                    } else if (chunkSize(state) == 3) {
221
                        // Expecting \n to terminate the header line
222
5.58k
                        if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0;
223
1.59k
                        else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2;
224
5.58k
                    }
225
711k
                }
226
1.04k
                return std::nullopt;
227
10.6k
            }
228
229
            // Drop Trailer Mode (Legacy fallback)
230
31.1k
            if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) {
231
0
                while (data.length() && chunkSize(state)) {
232
0
                    data.remove_prefix(1);
233
0
                    decChunkSize(state, 1);
234
235
0
                    if (chunkSize(state) == 0) {
236
0
                        state = 0;
237
0
                        return std::nullopt;
238
0
                    }
239
0
                }
240
0
                continue;
241
0
            }
242
243
31.1k
            if (!hasChunkSize(state)) {
244
20.9k
                consumeHexNumber(data, state);
245
20.9k
                if (isParsingInvalidChunkedEncoding(state)) {
246
279
                    return std::nullopt;
247
279
                }
248
20.6k
                if (hasChunkSize(state) && chunkSize(state) == 2) {
249
9.54k
                    if (trailer) {
250
9.54k
                        state = STATE_TRAILER_MODE | 0; 
251
9.54k
                    } else {
252
0
                        state = 2 | STATE_HAS_SIZE;
253
0
                    }
254
9.54k
                    return std::string_view(nullptr, 0);
255
9.54k
                }
256
11.1k
                continue;
257
20.6k
            }
258
259
            // Emit Body Payload Data with strict CRLF enforcement
260
10.1k
            if (data.length() >= chunkSize(state)) {
261
7.41k
                std::string_view emitSoon;
262
7.41k
                bool shouldEmit = false;
263
                
264
7.41k
                if (chunkSize(state) > 2) {
265
5.46k
                    if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') {
266
93
                        state = STATE_IS_ERROR;
267
93
                        return std::nullopt;
268
93
                    }
269
5.36k
                    emitSoon = std::string_view(data.data(), chunkSize(state) - 2);
270
5.36k
                    shouldEmit = true;
271
5.36k
                } else if (chunkSize(state) == 2) {
272
505
                    if (data[0] != '\r' || data[1] != '\n') {
273
33
                        state = STATE_IS_ERROR;
274
33
                        return std::nullopt;
275
33
                    }
276
1.44k
                } else if (chunkSize(state) == 1) {
277
955
                    if (data[0] != '\n') {
278
13
                        state = STATE_IS_ERROR;
279
13
                        return std::nullopt;
280
13
                    }
281
955
                }
282
283
7.27k
                data.remove_prefix(chunkSize(state));
284
7.27k
                state = STATE_IS_CHUNKED;
285
7.27k
                if (shouldEmit) return emitSoon;
286
1.90k
                continue;
287
7.27k
            } else {
288
2.74k
                std::string_view emitSoon;
289
2.74k
                if (chunkSize(state) > 2) {
290
2.72k
                    uint64_t maximalAppEmit = chunkSize(state) - 2;
291
2.72k
                    if (data.length() > maximalAppEmit) {
292
                        // Enforce partial CRLF boundary safety limit
293
445
                        if (data[maximalAppEmit] != '\r') {
294
10
                            state = STATE_IS_ERROR;
295
10
                            return std::nullopt;
296
10
                        }
297
435
                        emitSoon = data.substr(0, maximalAppEmit);
298
2.27k
                    } else {
299
2.27k
                        emitSoon = data;
300
2.27k
                    }
301
2.72k
                } else if (chunkSize(state) == 2) {
302
21
                    if (data[0] != '\r') {
303
19
                        state = STATE_IS_ERROR;
304
19
                        return std::nullopt;
305
19
                    }
306
21
                }
307
308
2.71k
                decChunkSize(state, (unsigned int) data.length());
309
2.71k
                state |= STATE_IS_CHUNKED;
310
2.71k
                data.remove_prefix(data.length());
311
2.71k
                if (emitSoon.length()) return emitSoon;
312
2
                else return std::nullopt;
313
2.71k
            }
314
10.1k
        }
315
316
6.64k
        return std::nullopt;
317
35.3k
    }
318
319
    struct ChunkIterator {
320
        std::string_view *data;
321
        std::optional<std::string_view> chunk;
322
        uint64_t *state;
323
        bool trailer;
324
325
93.9k
        ChunkIterator(std::string_view *data, uint64_t *state, bool trailer = false) : data(data), state(state), trailer(trailer) {
326
93.9k
            chunk = uWS::getNextChunk(*data, *state, trailer);
327
93.9k
        }
328
329
93.9k
        ChunkIterator() {}
330
331
93.9k
        ChunkIterator begin() { return *this; }
332
93.9k
        ChunkIterator end() { return ChunkIterator(); }
333
334
45.2k
        std::string_view operator*() {
335
45.2k
            if (!chunk.has_value()) std::abort();
336
45.2k
            return chunk.value();
337
45.2k
        }
338
339
139k
        bool operator!=(const ChunkIterator &other) const {
340
139k
            return other.chunk.has_value() != chunk.has_value();
341
139k
        }
342
343
45.2k
        ChunkIterator &operator++() {
344
45.2k
            chunk = uWS::getNextChunk(*data, *state, trailer);
345
45.2k
            return *this;
346
45.2k
        }
347
    };
348
}
349
350
#endif // UWS_CHUNKEDENCODING_H