/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 |