Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) | ||
3 | // | ||
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
6 | // | ||
7 | // Official repository: https://github.com/CPPAlliance/http_proto | ||
8 | // | ||
9 | |||
10 | #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP | ||
11 | #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP | ||
12 | |||
13 | #include <boost/http_proto/error.hpp> | ||
14 | #include <boost/http_proto/parser.hpp> | ||
15 | #include <boost/url/grammar/ci_string.hpp> | ||
16 | #include <boost/assert.hpp> | ||
17 | #include <boost/none.hpp> | ||
18 | #include <memory> | ||
19 | |||
20 | namespace boost { | ||
21 | namespace http_proto { | ||
22 | |||
23 | //------------------------------------------------ | ||
24 | |||
25 | 707 | parser:: | |
26 | parser( | ||
27 | detail::kind k, | ||
28 | config const& cfg, | ||
29 | 707 | std::size_t buffer_bytes) | |
30 | : cfg_(cfg) | ||
31 | , h_(detail::empty{k}) | ||
32 | , committed_(0) | ||
33 | , state_(state::empty) | ||
34 | , got_eof_(false) | ||
35 | 707 | , m_{} | |
36 | { | ||
37 | // buffer must be large enough to | ||
38 | // hold a complete header. | ||
39 | 707 | if( buffer_bytes < | |
40 |
1/2✓ Branch 0 taken 707 times.
✗ Branch 1 not taken.
|
707 | cfg.max_header_size) |
41 | 707 | buffer_bytes = | |
42 | cfg.max_header_size; | ||
43 | |||
44 | 707 | h_.buf = new char[buffer_bytes]; | |
45 | 707 | h_.cbuf = h_.buf; | |
46 | 707 | h_.cap = buffer_bytes; | |
47 | 707 | h_.size = 0; | |
48 | 707 | h_.prefix = 0; | |
49 | 707 | } | |
50 | |||
51 | //------------------------------------------------ | ||
52 | // | ||
53 | // Special Members | ||
54 | // | ||
55 | //------------------------------------------------ | ||
56 | |||
57 | 707 | parser:: | |
58 | 707 | ~parser() | |
59 | { | ||
60 |
1/2✓ Branch 0 taken 707 times.
✗ Branch 1 not taken.
|
707 | delete[] h_.buf; |
61 | 707 | } | |
62 | |||
63 | //------------------------------------------------ | ||
64 | // | ||
65 | // Observers | ||
66 | // | ||
67 | //------------------------------------------------ | ||
68 | |||
69 | string_view | ||
70 | ✗ | parser:: | |
71 | body() const noexcept | ||
72 | { | ||
73 | // VFALCO How about some | ||
74 | // asserts or exceptions? | ||
75 | ✗ | if(! m_.got_chunked) | |
76 | ✗ | return string_view( | |
77 | ✗ | h_.buf + h_.size, | |
78 | ✗ | m_.n_payload); | |
79 | ✗ | return string_view( | |
80 | ✗ | h_.buf + | |
81 | ✗ | h_.size + | |
82 | ✗ | m_.n_chunk, | |
83 | ✗ | m_.n_payload); | |
84 | } | ||
85 | |||
86 | //------------------------------------------------ | ||
87 | // | ||
88 | // Input | ||
89 | // | ||
90 | //------------------------------------------------ | ||
91 | |||
92 | void | ||
93 | ✗ | parser:: | |
94 | clear() noexcept | ||
95 | { | ||
96 | ✗ | reset(); | |
97 | } | ||
98 | |||
99 | void | ||
100 | ✗ | parser:: | |
101 | reset() | ||
102 | { | ||
103 | ✗ | if(got_eof_) | |
104 | { | ||
105 | // new connection, throw | ||
106 | // out all previous state. | ||
107 | ✗ | committed_ = 0; | |
108 | ✗ | got_eof_ = false; | |
109 | } | ||
110 | else | ||
111 | { | ||
112 | // Throwing out partial data | ||
113 | // will desync the HTTP stream | ||
114 | //BOOST_ASSERT(is_complete()); | ||
115 | ✗ | if( committed_ > h_.size && | |
116 | ✗ | h_.size > 0) | |
117 | { | ||
118 | // move unused octets to front | ||
119 | ✗ | std::memcpy( | |
120 | ✗ | h_.buf, | |
121 | ✗ | h_.buf + h_.size, | |
122 | ✗ | committed_ - h_.size); | |
123 | ✗ | committed_ -= h_.size; | |
124 | ✗ | h_.size = 0; | |
125 | } | ||
126 | else | ||
127 | { | ||
128 | ✗ | committed_ = 0; | |
129 | } | ||
130 | } | ||
131 | |||
132 | // reset the header but | ||
133 | // preserve the capacity | ||
134 | detail::header h( | ||
135 | ✗ | detail::empty{h_.kind}); | |
136 | ✗ | h.assign_to(h_); | |
137 | |||
138 | ✗ | m_ = message{}; | |
139 | ✗ | state_ = state::empty; | |
140 | } | ||
141 | |||
142 | mutable_buffers | ||
143 | 2969 | parser:: | |
144 | prepare() | ||
145 | { | ||
146 | // Can't commit bytes after eof | ||
147 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2969 times.
|
2969 | BOOST_ASSERT(! got_eof_); |
148 | |||
149 | 5938 | mb_ = { | |
150 | 2969 | h_.buf + committed_, | |
151 | 2969 | h_.cap - committed_ }; | |
152 | 2969 | return { &mb_, 1 }; | |
153 | } | ||
154 | |||
155 | void | ||
156 | 2969 | parser:: | |
157 | commit( | ||
158 | std::size_t n, | ||
159 | error_code& ec) | ||
160 | { | ||
161 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2969 times.
|
2969 | BOOST_ASSERT(! got_eof_); |
162 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2969 times.
|
2969 | BOOST_ASSERT( |
163 | n <= h_.cap - committed_); | ||
164 | 2969 | committed_ += n; | |
165 | 2969 | parse(ec); | |
166 | 2969 | } | |
167 | |||
168 | void | ||
169 | ✗ | parser:: | |
170 | commit_eof( | ||
171 | error_code &ec) | ||
172 | { | ||
173 | ✗ | got_eof_ = true; | |
174 | ✗ | parse(ec); | |
175 | } | ||
176 | |||
177 | //------------------------------------------------ | ||
178 | // | ||
179 | // | ||
180 | // | ||
181 | //------------------------------------------------ | ||
182 | |||
183 | void | ||
184 | ✗ | parser:: | |
185 | discard_header() noexcept | ||
186 | { | ||
187 | } | ||
188 | |||
189 | // discard all of the body, | ||
190 | // but don't discard chunk-ext | ||
191 | void | ||
192 | ✗ | parser:: | |
193 | discard_body() noexcept | ||
194 | { | ||
195 | } | ||
196 | |||
197 | // discard all of the payload | ||
198 | // including any chunk-ext | ||
199 | void | ||
200 | ✗ | parser:: | |
201 | discard_chunk() noexcept | ||
202 | { | ||
203 | } | ||
204 | |||
205 | //------------------------------------------------ | ||
206 | // | ||
207 | // Implementation | ||
208 | // | ||
209 | //------------------------------------------------ | ||
210 | |||
211 | void | ||
212 | 2969 | parser:: | |
213 | parse( | ||
214 | error_code& ec) | ||
215 | { | ||
216 | 2969 | ec.clear(); | |
217 |
2/2✓ Branch 0 taken 2969 times.
✓ Branch 1 taken 579 times.
|
3548 | while(state_ != state::complete) |
218 | { | ||
219 |
3/5✓ Branch 0 taken 707 times.
✓ Branch 1 taken 1215 times.
✓ Branch 2 taken 1047 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2969 | switch(state_) |
220 | { | ||
221 | 707 | case state::empty: | |
222 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 707 times.
|
707 | if(got_eof_) |
223 | { | ||
224 | ✗ | BOOST_ASSERT(h_.size == 0); | |
225 | ✗ | ec = error::end_of_stream; | |
226 | ✗ | return; | |
227 | } | ||
228 | 707 | state_ = state::start_line; | |
229 | BOOST_FALLTHROUGH; | ||
230 | |||
231 | 1922 | case state::start_line: | |
232 | 1922 | parse_start_line(ec); | |
233 |
2/2✓ Branch 1 taken 1215 times.
✓ Branch 2 taken 707 times.
|
1922 | if(ec.failed()) |
234 | 1215 | return; | |
235 | 707 | state_ = state::header_fields; | |
236 | BOOST_FALLTHROUGH; | ||
237 | |||
238 | 1754 | case state::header_fields: | |
239 | 1754 | parse_fields(ec); | |
240 |
2/2✓ Branch 1 taken 1175 times.
✓ Branch 2 taken 579 times.
|
1754 | if(ec.failed()) |
241 | 1175 | return; | |
242 | 579 | state_ = state::body; | |
243 | BOOST_FALLTHROUGH; | ||
244 | |||
245 | 579 | case state::body: | |
246 | 579 | parse_body(ec); | |
247 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 579 times.
|
579 | if(ec.failed()) |
248 | ✗ | return; | |
249 | 579 | state_ = state::complete; | |
250 | BOOST_FALLTHROUGH; | ||
251 | |||
252 | 579 | default: | |
253 | 579 | break; | |
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | void | ||
259 | 1922 | parser:: | |
260 | parse_start_line( | ||
261 | error_code& ec) | ||
262 | { | ||
263 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1922 times.
|
1922 | BOOST_ASSERT(h_.size == 0); |
264 | 1922 | std::size_t const new_size = | |
265 | 1922 | committed_ <= cfg_.max_header_size | |
266 |
1/2✓ Branch 0 taken 1922 times.
✗ Branch 1 not taken.
|
1922 | ? committed_ |
267 | : cfg_.max_header_size; | ||
268 | string_view s( | ||
269 | 1922 | this->h_.buf, new_size); | |
270 | auto rv = | ||
271 | 1922 | detail::parse_start_line(h_, s); | |
272 |
3/4✓ Branch 2 taken 1922 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1215 times.
✓ Branch 5 taken 707 times.
|
1922 | if(rv == grammar::error::need_more) |
273 | { | ||
274 | 1215 | if( new_size < | |
275 |
1/2✓ Branch 0 taken 1215 times.
✗ Branch 1 not taken.
|
1215 | cfg_.max_header_size) |
276 | { | ||
277 | 1215 | ec = rv.error(); | |
278 | 1215 | return; | |
279 | } | ||
280 | ✗ | ec = error::header_too_large; | |
281 | ✗ | return; | |
282 | } | ||
283 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 707 times.
|
707 | if(! rv) |
284 | { | ||
285 | ✗ | ec = rv.error(); | |
286 | ✗ | return; | |
287 | } | ||
288 | 707 | ec = {}; | |
289 | } | ||
290 | |||
291 | void | ||
292 | 1754 | parser:: | |
293 | parse_fields( | ||
294 | error_code& ec) | ||
295 | { | ||
296 | 1754 | std::size_t const new_size = | |
297 | 1754 | committed_ <= cfg_.max_header_size | |
298 |
1/2✓ Branch 0 taken 1754 times.
✗ Branch 1 not taken.
|
1754 | ? committed_ |
299 | : cfg_.max_header_size; | ||
300 | field id; | ||
301 | 1754 | string_view v; | |
302 | for(;;) | ||
303 | { | ||
304 | 2351 | auto const size0 = h_.size; | |
305 | 2351 | auto got_one = detail::parse_field( | |
306 | 2351 | h_, new_size, id, v, ec); | |
307 |
2/2✓ Branch 2 taken 1047 times.
✓ Branch 3 taken 1304 times.
|
2351 | if(ec == grammar::error::need_more) |
308 | { | ||
309 | 1047 | if(new_size >= | |
310 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1047 times.
|
1047 | cfg_.max_header_size) |
311 | ✗ | ec = error::header_too_large; | |
312 | } | ||
313 |
2/2✓ Branch 0 taken 1754 times.
✓ Branch 1 taken 597 times.
|
2351 | if(! got_one) |
314 | 1754 | return; | |
315 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 597 times.
|
597 | if(h_.count > cfg_.max_field_count) |
316 | { | ||
317 | ✗ | ec = error::too_many_fields; | |
318 | ✗ | return; | |
319 | } | ||
320 | 597 | if(h_.size > | |
321 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 597 times.
|
597 | cfg_.max_field_size + size0) |
322 | { | ||
323 | ✗ | ec = error::field_too_large; | |
324 | ✗ | return; | |
325 | } | ||
326 |
4/5✓ Branch 0 taken 32 times.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 533 times.
|
597 | switch(id) |
327 | { | ||
328 | 32 | case field::connection: | |
329 | case field::proxy_connection: | ||
330 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | do_connection(v, ec); |
331 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
|
32 | if(ec.failed()) |
332 | ✗ | return; | |
333 | 32 | break; | |
334 | 31 | case field::content_length: | |
335 |
1/2✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
|
31 | do_content_length(v, ec); |
336 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
|
31 | if(ec.failed()) |
337 | ✗ | return; | |
338 | 31 | break; | |
339 | 1 | case field::transfer_encoding: | |
340 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | do_transfer_encoding(v, ec); |
341 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if(ec.failed()) |
342 | ✗ | return; | |
343 | 1 | break; | |
344 | ✗ | case field::upgrade: | |
345 | ✗ | do_upgrade(v, ec); | |
346 | ✗ | if(ec.failed()) | |
347 | ✗ | return; | |
348 | ✗ | break; | |
349 | 533 | default: | |
350 | 533 | break; | |
351 | } | ||
352 | 597 | } | |
353 | } | ||
354 | |||
355 | void | ||
356 | 579 | parser:: | |
357 | parse_body( | ||
358 | error_code& ec) | ||
359 | { | ||
360 | (void)ec; | ||
361 | 579 | return; | |
362 | // VFALCO TODO | ||
363 | #if 0 | ||
364 | BOOST_ASSERT(state_ == state::body); | ||
365 | |||
366 | if(h_.kind == detail::kind::request) | ||
367 | { | ||
368 | // https://tools.ietf.org/html/rfc7230#section-3.3 | ||
369 | if(m_.skip_body) | ||
370 | return; | ||
371 | if(m_.content_len.has_value()) | ||
372 | { | ||
373 | if(*m_.content_len > cfg_.body_too_large) | ||
374 | { | ||
375 | ec = error::body_too_large; | ||
376 | return; | ||
377 | } | ||
378 | if(*m_.content_len == 0) | ||
379 | return; | ||
380 | } | ||
381 | else if(m_.got_chunked) | ||
382 | { | ||
383 | // VFALCO TODO | ||
384 | return; | ||
385 | } | ||
386 | else | ||
387 | { | ||
388 | // Content-Length: 0 | ||
389 | return; | ||
390 | } | ||
391 | } | ||
392 | else | ||
393 | { | ||
394 | BOOST_ASSERT(h_.kind == | ||
395 | detail::kind::response); | ||
396 | |||
397 | // https://tools.ietf.org/html/rfc7230#section-3.3 | ||
398 | if((h_.res.status_int / 100 == 1) || // 1xx e.g. Continue | ||
399 | h_.res.status_int == 204 || // No Content | ||
400 | h_.res.status_int == 304) // Not Modified | ||
401 | { | ||
402 | // Content-Length may be present, but we | ||
403 | // treat the message as not having a body. | ||
404 | } | ||
405 | else if(m_.content_len.has_value()) | ||
406 | { | ||
407 | if(*m_.content_len > 0) | ||
408 | { | ||
409 | if(*m_.content_len > cfg_.body_too_large) | ||
410 | { | ||
411 | ec = error::body_too_large; | ||
412 | return; | ||
413 | } | ||
414 | } | ||
415 | } | ||
416 | else | ||
417 | { | ||
418 | // No Content-Length | ||
419 | return; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | auto avail = committed_ - size_; | ||
424 | if(m_.content_len.has_value()) | ||
425 | { | ||
426 | // known payload length | ||
427 | BOOST_ASSERT(! m_.got_chunked); | ||
428 | BOOST_ASSERT(m_.n_remain > 0); | ||
429 | BOOST_ASSERT(m_.content_len < | ||
430 | cfg_.body_too_large); | ||
431 | if(avail == 0) | ||
432 | { | ||
433 | if(! got_eof_) | ||
434 | { | ||
435 | ec = grammar::error::need_more; | ||
436 | return; | ||
437 | } | ||
438 | ec = error::need_more; | ||
439 | return; | ||
440 | } | ||
441 | if( avail > m_.n_remain) | ||
442 | avail = static_cast< | ||
443 | std::size_t>(m_.n_remain); | ||
444 | size_ += avail; | ||
445 | m_.payload_seen += avail; | ||
446 | m_.n_payload += avail; | ||
447 | m_.n_remain -= avail; | ||
448 | if(m_.n_remain > 0) | ||
449 | { | ||
450 | ec = {}; | ||
451 | return; | ||
452 | } | ||
453 | state_ = state::complete; | ||
454 | ec = error::end_of_message; | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | if(! m_.got_chunked) | ||
459 | { | ||
460 | // end of body indicated by EOF | ||
461 | if(avail > 0) | ||
462 | { | ||
463 | if(avail > (std::size_t( | ||
464 | -1) - m_.n_payload)) | ||
465 | { | ||
466 | // overflow size_t | ||
467 | // VFALCO revisit this | ||
468 | ec = error::numeric_overflow; | ||
469 | return; | ||
470 | } | ||
471 | size_ += avail; | ||
472 | m_.n_payload += avail; | ||
473 | ec = {}; | ||
474 | return; | ||
475 | } | ||
476 | if(! got_eof_) | ||
477 | { | ||
478 | ec = grammar::error::need_more; | ||
479 | return; | ||
480 | } | ||
481 | state_ = state::complete; | ||
482 | ec = error::end_of_message; | ||
483 | return; | ||
484 | } | ||
485 | #if 0 | ||
486 | if(m_.payload_left == 0) | ||
487 | { | ||
488 | // start of chunk | ||
489 | bnf::chunk_part p; | ||
490 | auto it = p.parse( | ||
491 | h_.buf + size_, | ||
492 | h_.buf + ( | ||
493 | committed_ - size_), | ||
494 | ec); | ||
495 | if(ec) | ||
496 | return; | ||
497 | auto const v = | ||
498 | p.value(); | ||
499 | m_.chunk.size = v.size; | ||
500 | m_.chunk.ext = v.ext; | ||
501 | m_.chunk.trailer = v.trailer; | ||
502 | m_.chunk.fresh = true; | ||
503 | m_.payload_left = | ||
504 | v.size - v.data.size(); | ||
505 | } | ||
506 | else | ||
507 | { | ||
508 | // continuation of chunk | ||
509 | |||
510 | } | ||
511 | #endif | ||
512 | #endif | ||
513 | } | ||
514 | |||
515 | void | ||
516 | ✗ | parser:: | |
517 | parse_chunk( | ||
518 | error_code& ec) | ||
519 | { | ||
520 | (void)ec; | ||
521 | #if 0 | ||
522 | switch(state_) | ||
523 | { | ||
524 | case state::start_line: | ||
525 | case state::header_fields: | ||
526 | parse_header(ec); | ||
527 | if(ec.failed()) | ||
528 | return; | ||
529 | BOOST_ASSERT(state_ > | ||
530 | state::header_fields); | ||
531 | break; | ||
532 | case state::body: | ||
533 | if(! m_.got_chunked) | ||
534 | return parse_body(ec); | ||
535 | break; | ||
536 | case state::complete: | ||
537 | ec = error::end_of_message; | ||
538 | if(! got_eof_) | ||
539 | return; | ||
540 | state_ = state::end_of_stream; | ||
541 | return; | ||
542 | case state::end_of_stream: | ||
543 | ec = error::end_of_stream; | ||
544 | return; | ||
545 | } | ||
546 | |||
547 | auto const avail = committed_ - size_; | ||
548 | auto const start = h_.buf + size_; | ||
549 | if(m_.payload_left == 0) | ||
550 | { | ||
551 | // start of chunk | ||
552 | // VFALCO What about chunk_part_next? | ||
553 | BOOST_ASSERT( | ||
554 | size_ == m_.header_size); | ||
555 | bnf::chunk_part p; | ||
556 | auto it = p.parse(start, | ||
557 | h_.buf + ( | ||
558 | committed_ - size_), ec); | ||
559 | BOOST_ASSERT(it == start); | ||
560 | if(ec) | ||
561 | return; | ||
562 | auto const v = p.value(); | ||
563 | m_.chunk.size = v.size; | ||
564 | m_.chunk.ext = v.ext; | ||
565 | m_.chunk.fresh = true; | ||
566 | if(v.size > 0) | ||
567 | { | ||
568 | // chunk | ||
569 | m_.chunk.trailer = {}; | ||
570 | m_.payload_left = | ||
571 | v.size - v.data.size(); | ||
572 | size_ += it - start; // excludes CRLF | ||
573 | return; | ||
574 | } | ||
575 | // last-chunk | ||
576 | BOOST_ASSERT( | ||
577 | v.data.empty()); | ||
578 | m_.chunk.trailer = | ||
579 | v.trailer; | ||
580 | m_.body = {}; | ||
581 | size_ += it - start; // excludes CRLF | ||
582 | state_ = state::complete; | ||
583 | } | ||
584 | else | ||
585 | { | ||
586 | // continuation of chunk | ||
587 | |||
588 | } | ||
589 | #endif | ||
590 | } | ||
591 | |||
592 | //------------------------------------------------ | ||
593 | |||
594 | // https://datatracker.ietf.org/doc/html/rfc7230#section-6.1 | ||
595 | void | ||
596 | 32 | parser:: | |
597 | do_connection( | ||
598 | string_view s, error_code& ec) | ||
599 | { | ||
600 | (void)ec; | ||
601 | (void)s; | ||
602 | #if 0 | ||
603 | for(auto v : bnf::range<bnf::connection>(s)) | ||
604 | { | ||
605 | if(grammar::ci_is_equal(v, "close")) | ||
606 | { | ||
607 | m_.got_close = true; | ||
608 | } | ||
609 | else if(grammar::ci_is_equal(v, "keep-alive")) | ||
610 | { | ||
611 | m_.got_keep_alive = true; | ||
612 | } | ||
613 | else if(grammar::ci_is_equal(v, "upgrade")) | ||
614 | { | ||
615 | m_.got_upgrade = true; | ||
616 | } | ||
617 | } | ||
618 | #endif | ||
619 | 32 | } | |
620 | |||
621 | void | ||
622 | 31 | parser:: | |
623 | do_content_length( | ||
624 | string_view s, | ||
625 | error_code& ec) | ||
626 | { | ||
627 | (void)ec; | ||
628 | (void)s; | ||
629 | #if 0 | ||
630 | // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 | ||
631 | // Content-Length can be a comma separated | ||
632 | // list, with all the same values, as well | ||
633 | // as having multiple Content-Length fields | ||
634 | // with the same number. We handle these here. | ||
635 | auto list = bnf::range< | ||
636 | bnf::list_of_one_or_more< | ||
637 | bnf::dec_number>>(s); | ||
638 | auto it = list.begin(ec); | ||
639 | if(ec.failed()) | ||
640 | { | ||
641 | ec = error::bad_content_length; | ||
642 | return; | ||
643 | } | ||
644 | BOOST_ASSERT(it != list.end()); | ||
645 | auto v = *it; | ||
646 | if(! m_.content_len.has_value()) | ||
647 | { | ||
648 | if(v > cfg_.max_body_size) | ||
649 | { | ||
650 | ec = error::body_too_large; | ||
651 | return; | ||
652 | } | ||
653 | m_.content_len = v; | ||
654 | } | ||
655 | else if(*m_.content_len != v) | ||
656 | { | ||
657 | // differing values | ||
658 | ec = error::bad_content_length; | ||
659 | return; | ||
660 | } | ||
661 | for(;;) | ||
662 | { | ||
663 | it.increment(ec); | ||
664 | if(ec.failed()) | ||
665 | { | ||
666 | ec = error::bad_content_length; | ||
667 | return; | ||
668 | } | ||
669 | if(it == list.end()) | ||
670 | break; | ||
671 | v = *it; | ||
672 | if(m_.content_len != v) | ||
673 | { | ||
674 | // differing lengths | ||
675 | ec = error::bad_content_length; | ||
676 | return; | ||
677 | } | ||
678 | } | ||
679 | if(! m_.skip_body) | ||
680 | m_.n_remain = *m_.content_len; | ||
681 | #endif | ||
682 | 31 | } | |
683 | |||
684 | void | ||
685 | 1 | parser:: | |
686 | do_transfer_encoding( | ||
687 | string_view s, error_code& ec) | ||
688 | { | ||
689 | (void)s; | ||
690 | (void)ec; | ||
691 | #if 0 | ||
692 | using namespace detail::string_literals; | ||
693 | |||
694 | bnf::range<bnf::transfer_encoding> te(s); | ||
695 | auto const end = te.end(); | ||
696 | auto it = te.begin(ec); | ||
697 | if(ec.failed()) | ||
698 | { | ||
699 | // expected at least one encoding | ||
700 | ec = error::bad_transfer_encoding; | ||
701 | return; | ||
702 | } | ||
703 | BOOST_ASSERT(it != end); | ||
704 | // handle multiple | ||
705 | // Transfer-Encoding header lines | ||
706 | m_.got_chunked = false; | ||
707 | // get last encoding | ||
708 | for(;;) | ||
709 | { | ||
710 | auto prev = it++; | ||
711 | if(it == end) | ||
712 | { | ||
713 | if(grammar::ci_is_equal( | ||
714 | prev->name, "chunked")) | ||
715 | m_.got_chunked = true; | ||
716 | break; | ||
717 | } | ||
718 | } | ||
719 | #endif | ||
720 | 1 | } | |
721 | |||
722 | void | ||
723 | ✗ | parser:: | |
724 | do_upgrade( | ||
725 | string_view s, error_code& ec) | ||
726 | { | ||
727 | (void)s; | ||
728 | (void)ec; | ||
729 | } | ||
730 | |||
731 | } // http_proto | ||
732 | } // boost | ||
733 | |||
734 | #endif | ||
735 |