GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/parser.ipp
Date: 2023-02-21 17:23:31
Exec Total Coverage
Lines: 109 202 54.0%
Functions: 10 18 55.6%
Branches: 32 92 34.8%

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/parser.hpp>
14 #include <boost/http_proto/context.hpp>
15 #include <boost/http_proto/error.hpp>
16 #include <boost/http_proto/service/zlib_service.hpp>
17 #include <boost/http_proto/detail/except.hpp>
18 #include <boost/buffers/buffer_copy.hpp>
19 #include <boost/url/grammar/ci_string.hpp>
20 #include <boost/assert.hpp>
21 #include <boost/none.hpp>
22 #include <memory>
23
24 namespace boost {
25 namespace http_proto {
26
27 //------------------------------------------------
28 /*
29 Four body styles for `parser`
30 * Specify a DynamicBuffer
31 * Specify a Sink
32 * Read from a parser::stream
33 * in-place
34
35 Buffer Usage
36
37 | | begin
38 | H | p | | f | read headers
39 | H | p | | T | f | set T body
40 | H | p | | C | T | f | make codec C
41 | H | p | b | C | T | f | decode p into b
42 | H | p | b | C | T | f | read/parse loop
43 | H | | T | f | destroy codec
44 | H | | T | f | finished
45
46 H headers
47 C codec
48 T body
49 f table
50 p partial payload
51 b body data
52
53 - We can compact the headers:
54 move the table downwards to
55 squeeze out the unused space
56
57 */
58 //------------------------------------------------
59
60 struct parser_service
61 : service
62 {
63 parser::config_base cfg;
64 std::size_t space_needed = 0;
65 zlib::deflate_decoder_service const*
66 deflate_svc = nullptr;
67
68 parser_service(
69 context& ctx,
70 parser::config_base const& cfg_);
71 };
72
73 6 parser_service::
74 parser_service(
75 context& ctx,
76 6 parser::config_base const& cfg_)
77 6 : cfg(cfg_)
78 {
79 /*
80 | fb | cb0 | cb1 | C | T | f |
81
82 fb flat_buffer headers.max_size
83 cb0 circular_buffer min_in_place_body
84 cb1 circular_buffer min_in_place_body
85 C codec max_codec
86 T body max_type_erase
87 f table max_table_space
88
89 */
90 // validate
91 //if(cfg.min_prepare > cfg.max_prepare)
92 //detail::throw_invalid_argument();
93
94 // fb, f
95 6 space_needed +=
96
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 cfg.headers.valid_space_needed();
97
98 // cb0, cb1
99 6 space_needed +=
100 6 2 * cfg.min_in_place_body;
101
102 // T
103 6 space_needed += cfg.max_type_erase;
104
105 // max(C...)
106 6 std::size_t C = 0;
107 {
108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(cfg.apply_deflate_decoder)
109 {
110 deflate_svc = &ctx.get_service<
111 zlib::deflate_decoder_service>();
112 auto const n =
113 deflate_svc->space_needed();
114 if( C < n)
115 C = n;
116 }
117 }
118 6 space_needed += C;
119 6 }
120
121 void
122 6 install_parser_service(
123 context& ctx,
124 parser::config_base const& cfg)
125 {
126 ctx.make_service<
127 6 parser_service>(cfg);
128 6 }
129
130 //------------------------------------------------
131
132 736 parser::
133 parser(
134 context& ctx,
135 736 detail::kind k)
136 : ctx_(ctx)
137 , svc_(ctx.get_service<
138 1472 parser_service>())
139 736 , h_(detail::empty{k})
140 {
141 736 auto const n =
142 736 svc_.space_needed;
143
1/2
✓ Branch 1 taken 736 times.
✗ Branch 2 not taken.
736 ws_.allocate(n);
144 736 h_.cap = n;
145 736 reset();
146 736 }
147
148 //------------------------------------------------
149 //
150 // Special Members
151 //
152 //------------------------------------------------
153
154 736 parser::
155 736 ~parser()
156 {
157 736 }
158
159 parser::
160 parser(
161 parser&&) noexcept = default;
162
163 //------------------------------------------------
164 //
165 // Modifiers
166 //
167 //------------------------------------------------
168
169 // prepare for a new stream
170 void
171 736 parser::
172 reset() noexcept
173 {
174 736 ws_.clear();
175 736 st_ = state::need_start;
176 736 body_ = body::in_place;
177 736 head_response_ = false;
178 736 got_eof_ = false;
179 736 }
180
181 void
182 767 parser::
183 start_impl(
184 bool head_response)
185 {
186 767 std::size_t leftover = 0;
187
2/4
✓ Branch 0 taken 734 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
767 switch(st_)
188 {
189 734 default:
190 case state::need_start:
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 734 times.
734 BOOST_ASSERT(h_.size == 0);
192
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 734 times.
734 BOOST_ASSERT(fb_.size() == 0);
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 734 times.
734 BOOST_ASSERT(! got_eof_);
194 734 break;
195
196 case state::headers:
197 // can't call start() twice
198 detail::throw_logic_error();
199
200 case state::headers_done:
201 case state::body:
202 // previous message was unfinished
203 detail::throw_logic_error();
204
205 33 case state::complete:
206
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 if(fb_.size() > 0)
207 {
208 // headers with no body
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 BOOST_ASSERT(h_.size > 0);
210 33 fb_.consume(h_.size);
211 33 leftover = fb_.size();
212 // move unused octets to front
213 33 buffers::buffer_copy(
214 33 buffers::mutable_buffer(
215 ws_.data(),
216 leftover),
217 66 fb_.data());
218 }
219 else
220 {
221 // leftover data after body
222 }
223 33 break;
224 }
225 /*
226 | fb | cb0 | cb1 | C | T | f |
227 */
228 // start with fb+cb0
229 767 fb_ = {
230 ws_.data(), // VFALCO this might need an offset
231 767 svc_.cfg.headers.max_size +
232 767 svc_.cfg.min_in_place_body,
233 leftover };
234
235 1534 h_ = detail::header(
236 767 detail::empty{h_.kind});
237 767 h_.buf = reinterpret_cast<
238 767 char*>(ws_.data());
239 767 h_.cbuf = h_.buf;
240 767 h_.cap = ws_.size();
241
242 767 st_ = state::headers;
243 767 body_ = body::in_place;
244
245
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 767 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
767 BOOST_ASSERT(! head_response ||
246 h_.kind == detail::kind::response);
247 767 head_response_ = head_response;
248 767 }
249
250 auto
251 3185 parser::
252 prepare() ->
253 mutable_buffers_type
254 {
255
1/5
✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3185 switch(st_)
256 {
257 default:
258 case state::need_start:
259 // forgot to call start()
260 detail::throw_logic_error();
261
262 3185 case state::headers:
263 {
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
3185 BOOST_ASSERT(h_.size <
265 svc_.cfg.headers.max_size);
266 3185 auto n = fb_.capacity() - fb_.size();
267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
3185 if( n > svc_.cfg.max_prepare)
268 n = svc_.cfg.max_prepare;
269 return {
270 3185 fb_.prepare(n),
271 3185 buffers::mutable_buffer{} };
272 }
273
274 case state::headers_done:
275 {
276 // reserve headers
277 ws_.reserve_front(h_.size);
278
279 // reserve table at the back
280 ws_.reserve_back(h_.table_space());
281
282 st_ = state::body;
283 // VFALCO set up body buffer
284 BOOST_FALLTHROUGH;
285 }
286
287 case state::body:
288 {
289 //if(body_ == body::dynamic)
290 auto n = cb0_.capacity() -
291 cb0_.size();
292 if( n > svc_.cfg.max_prepare)
293 n = svc_.cfg.max_prepare;
294 return cb0_.prepare(n);
295 }
296
297 case state::complete:
298 // forgot to call start()
299 detail::throw_logic_error();
300 }
301 }
302
303 void
304 3185 parser::
305 commit(
306 std::size_t n)
307 {
308 // Can't commit after eof
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
3185 if(got_eof_)
310 detail::throw_logic_error();
311
312
1/5
✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3185 switch(st_)
313 {
314 default:
315 case state::need_start:
316 // forgot to call start()
317 detail::throw_logic_error();
318
319 3185 case state::headers:
320 3185 fb_.commit(n);
321 3185 break;
322
323 case state::headers_done:
324 // forgot to call prepare()
325 detail::throw_logic_error();
326
327 case state::body:
328 cb0_.commit(n);
329 break;
330
331 case state::complete:
332 // forgot to call start()
333 detail::throw_logic_error();
334 }
335 3185 }
336
337 void
338 parser::
339 commit_eof()
340 {
341 switch(st_)
342 {
343 default:
344 case state::need_start:
345 // forgot to call prepare()
346 detail::throw_logic_error();
347
348 case state::headers:
349 got_eof_ = true;
350 break;
351
352 case state::headers_done:
353 // forgot to call prepare()
354 detail::throw_logic_error();
355
356 case state::body:
357 got_eof_ = true;
358 break;
359
360 case state::complete:
361 // Can't commit eof when
362 // message is complete.
363 detail::throw_logic_error();
364 }
365 }
366
367 //-----------------------------------------------
368
369 // process input data then
370 // eof if input data runs out.
371 void
372 3185 parser::
373 parse(
374 error_code& ec)
375 {
376
1/5
✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
3185 switch(st_)
377 {
378 default:
379 case state::need_start:
380 // forgot to call start()
381 detail::throw_logic_error();
382
383 3185 case state::headers:
384 {
385
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3185 times.
3185 BOOST_ASSERT(h_.buf == ws_.data());
386
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3185 times.
3185 BOOST_ASSERT(h_.cbuf == ws_.data());
387 3185 auto const new_size = fb_.size();
388 3185 h_.parse(new_size, svc_.cfg.headers, ec);
389
2/2
✓ Branch 1 taken 584 times.
✓ Branch 2 taken 2601 times.
3185 if(! ec.failed())
390 {
391
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 581 times.
584 if( h_.md.payload != payload::none &&
392
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 ! head_response_)
393 {
394
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if(h_.md.payload == payload::size)
395 2 remain_ = h_.md.payload_size;
396
397 // Deliver headers to caller
398 3 st_ = state::headers_done;
399 3 break;
400 }
401 // no payload
402 581 st_ = state::complete;
403 581 break;
404 }
405
2/2
✓ Branch 2 taken 2473 times.
✓ Branch 3 taken 128 times.
2601 if(ec == grammar::error::need_more)
406 {
407
1/2
✓ Branch 0 taken 2473 times.
✗ Branch 1 not taken.
2473 if(! got_eof_)
408 2473 break;
409 if(h_.size > 0)
410 {
411 // Connection closed before
412 // message is complete.
413 ec = BOOST_HTTP_PROTO_ERR(
414 error::incomplete);
415 return;
416 }
417
418 // Connection closed
419 // cleanly.
420 ec = BOOST_HTTP_PROTO_ERR(
421 error::end_of_stream);
422 return;
423 }
424
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
128 BOOST_ASSERT(ec.failed());
425 128 return;
426 }
427
428 case state::headers_done:
429 {
430 // This is a no-op
431 // VFALCO Is this right?
432 ec = {};
433 break;
434 }
435
436 case state::body:
437 {
438 parse_body(ec);
439 if(ec.failed())
440 return;
441 st_ = state::complete;
442 break;
443 }
444
445 case state::complete:
446 break;
447 }
448 }
449
450 //------------------------------------------------
451
452 auto
453 parser::
454 get_stream() ->
455 stream
456 {
457 // body type already chosen
458 if(body_ != body::in_place)
459 detail::throw_logic_error();
460
461 body_ = body::stream;
462 return stream(*this);
463 }
464
465 //------------------------------------------------
466
467 string_view
468 parser::
469 release_buffered_data() noexcept
470 {
471 return {};
472 }
473
474 //------------------------------------------------
475 //
476 // Implementation
477 //
478 //------------------------------------------------
479
480 auto
481 37 parser::
482 safe_get_header() const ->
483 detail::header const*
484 {
485
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
37 switch(st_)
486 {
487 default:
488 case state::need_start:
489 case state::headers:
490 // Headers not received yet
491 detail::throw_logic_error();
492
493 3 case state::headers_done:
494 3 break;
495
496 case state::body:
497 // Headers received and discarded
498 detail::throw_logic_error();
499
500 34 case state::complete:
501 // VFALCO Could be OK
502 34 break;
503 }
504 37 return &h_;
505 }
506
507 void
508 parser::
509 on_set_body()
510 {
511 }
512
513 void
514 parser::
515 parse_body(
516 error_code& ec)
517 {
518 if(body_ == body::in_place)
519 {
520 ec = {};
521 return;
522 }
523 if(body_ == body::dynamic)
524 {
525 ec = {};
526 return;
527 }
528 if(body_ == body::sink)
529 {
530 ec = {};
531 return;
532 }
533 if(body_ == body::stream)
534 {
535 ec = {};
536 return;
537 }
538 }
539
540 void
541 parser::
542 parse_chunk(
543 error_code& ec)
544 {
545 ec = {};
546 }
547
548 } // http_proto
549 } // boost
550
551 #endif
552