GCC Code Coverage Report


Directory: libs/http_proto/
File: boost/http_proto/serializer.hpp
Date: 2024-05-23 18:56:45
Exec Total Coverage
Lines: 41 41 100.0%
Functions: 17 17 100.0%
Branches: 6 9 66.7%

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_SERIALIZER_HPP
11 #define BOOST_HTTP_PROTO_SERIALIZER_HPP
12
13 #include <boost/http_proto/detail/config.hpp>
14 #include <boost/http_proto/context.hpp>
15 #include <boost/http_proto/filter.hpp>
16 #include <boost/http_proto/source.hpp>
17 #include <boost/http_proto/detail/array_of_buffers.hpp>
18 #include <boost/http_proto/detail/except.hpp>
19 #include <boost/http_proto/detail/header.hpp>
20 #include <boost/http_proto/detail/workspace.hpp>
21 #include <boost/buffers/circular_buffer.hpp>
22 #include <boost/buffers/flat_buffer.hpp>
23 #include <boost/buffers/range.hpp>
24 #include <boost/buffers/type_traits.hpp>
25 #include <boost/system/result.hpp>
26 #include <cstdint>
27 #include <memory>
28 #include <type_traits>
29 #include <utility>
30
31 namespace boost {
32 namespace http_proto {
33 namespace detail {
34 // chunked-body = *chunk
35 // last-chunk
36 // trailer-section
37 // CRLF
38
39 static
40 constexpr
41 std::size_t
42 crlf_len_ = 2;
43
44 // chunk = chunk-size [ chunk-ext ] CRLF
45 // chunk-data CRLF
46 static
47 constexpr
48 std::size_t
49 chunk_header_len_ =
50 16 + // 16 hex digits => 64 bit number
51 crlf_len_;
52
53 // last-chunk = 1*("0") [ chunk-ext ] CRLF
54 static
55 constexpr
56 std::size_t
57 last_chunk_len_ =
58 1 + // "0"
59 crlf_len_ +
60 crlf_len_; // chunked-body termination requires an extra CRLF
61
62 static
63 constexpr
64 std::size_t
65 chunked_overhead_ =
66 chunk_header_len_ +
67 crlf_len_ + // closing chunk data
68 last_chunk_len_;
69 }
70
71
72 #ifndef BOOST_HTTP_PROTO_DOCS
73 class request;
74 class response;
75 class request_view;
76 class response_view;
77 class message_view_base;
78 #endif
79
80 /** A serializer for HTTP/1 messages
81
82 This is used to serialize one or more complete
83 HTTP/1 messages. Each message consists of a
84 required header followed by an optional body.
85 */
86 class BOOST_SYMBOL_VISIBLE
87 serializer
88 {
89 public:
90 class const_buffers_type;
91
92 struct stream;
93
94 /** Destructor
95 */
96 BOOST_HTTP_PROTO_DECL
97 ~serializer();
98
99 /** Constructor
100 */
101 BOOST_HTTP_PROTO_DECL
102 serializer();
103
104 /** Constructor
105 */
106 BOOST_HTTP_PROTO_DECL
107 serializer(
108 serializer&&) noexcept;
109
110 /** Constructor
111 */
112 BOOST_HTTP_PROTO_DECL
113 explicit
114 serializer(
115 std::size_t buffer_size);
116
117 BOOST_HTTP_PROTO_DECL
118 serializer(
119 context& ctx,
120 std::size_t buffer_size);
121
122 //--------------------------------------------
123
124 /** Prepare the serializer for a new stream
125 */
126 BOOST_HTTP_PROTO_DECL
127 void
128 reset() noexcept;
129
130 /** Prepare the serializer for a new message
131
132 The message will not contain a body.
133 Changing the contents of the message
134 after calling this function and before
135 @ref is_done returns `true` results in
136 undefined behavior.
137 */
138 void
139 4 start(
140 message_view_base const& m)
141 {
142 4 start_empty(m);
143 4 }
144
145 /** Prepare the serializer for a new message
146
147 Changing the contents of the message
148 after calling this function and before
149 @ref is_done returns `true` results in
150 undefined behavior.
151
152 @par Constraints
153 @code
154 is_const_buffers< ConstBuffers >::value == true
155 @endcode
156 */
157 template<
158 class ConstBufferSequence
159 #ifndef BOOST_HTTP_PROTO_DOCS
160 ,class = typename
161 std::enable_if<
162 buffers::is_const_buffer_sequence<
163 ConstBufferSequence>::value
164 >::type
165 #endif
166 >
167 void
168 start(
169 message_view_base const& m,
170 ConstBufferSequence&& body);
171
172 /** Prepare the serializer for a new message
173
174 Changing the contents of the message
175 after calling this function and before
176 @ref is_done returns `true` results in
177 undefined behavior.
178 */
179 template<
180 class Source,
181 class... Args
182 #ifndef BOOST_HTTP_PROTO_DOCS
183 ,class = typename std::enable_if<
184 is_source<Source>::value>::type
185 #endif
186 >
187 Source&
188 start(
189 message_view_base const& m,
190 Args&&... args);
191
192 //--------------------------------------------
193
194 /** Create a new stream object associated with the
195 serializer.
196
197 The returned stream must not outlive its backing
198 serializer object.
199
200 Streams permit a user to supply input data to
201 the serializer using a bounded sequence of mutable
202 buffers.
203
204 \code{.cpp}
205 std::string msg = "Hello, world!";
206 std::size_t buf_size = 16 * 1024;
207 http_proto::serializer sr(buf_size);
208
209 auto stream = sr.start_stream();
210 auto bufs = stream.prepare();
211 auto s = buffers::buffer_size(bufs);
212 auto n = buffers::buffer_copy(
213 bufs,
214 buffers::make_buffer(
215 msg.data(),
216 std::min(s, msg.size())));
217 stream.commit(n);
218
219 auto cbs = sr.prepare().value();
220 // `cbs` contains the serialized octets corresponding
221 // to our `msg`
222 \endcode
223 */
224 BOOST_HTTP_PROTO_DECL
225 stream
226 start_stream(
227 message_view_base const& m);
228
229 //--------------------------------------------
230
231 /** Return true if serialization is complete.
232 */
233 bool
234 78 is_done() const noexcept
235 {
236 78 return is_done_;
237 }
238
239 /** Return the output area.
240
241 This function will serialize some or
242 all of the content and return the
243 corresponding output buffers.
244
245 @par Preconditions
246 @code
247 this->is_done() == false
248 @endcode
249 */
250 BOOST_HTTP_PROTO_DECL
251 auto
252 prepare() ->
253 system::result<
254 const_buffers_type>;
255
256 /** Consume bytes from the output area.
257 */
258 BOOST_HTTP_PROTO_DECL
259 void
260 consume(std::size_t n);
261
262 private:
263 static void copy(
264 buffers::const_buffer*,
265 buffers::const_buffer const*,
266 std::size_t n) noexcept;
267 auto
268 make_array(std::size_t n) ->
269 detail::array_of_const_buffers;
270
271 auto
272 make_array(detail::workspace& ws, std::size_t n) ->
273 detail::array_of_const_buffers;
274
275 template<
276 class Source,
277 class... Args,
278 typename std::enable_if<
279 std::is_constructible<
280 Source,
281 Args...>::value>::type* = nullptr>
282 Source&
283 16 construct_source(Args&&... args)
284 {
285 16 return ws_.emplace<Source>(
286 16 std::forward<Args>(args)...);
287 }
288
289 template<
290 class Source,
291 class... Args,
292 typename std::enable_if<
293 std::is_constructible<
294 Source,
295 buffered_base::allocator&,
296 Args...>::value>::type* = nullptr>
297 Source&
298 construct_source(Args&&... args)
299 {
300 buffered_base::allocator a(
301 ws_.data(),
302 (ws_.size() - ws_.space_needed<Source>()) / 2,
303 false);
304 auto& src = ws_.emplace<Source>(
305 a, std::forward<Args>(args)...);
306 ws_.reserve_front(a.size_used());
307 return src;
308 }
309
310 BOOST_HTTP_PROTO_DECL void start_init(message_view_base const&);
311 BOOST_HTTP_PROTO_DECL void start_empty(message_view_base const&);
312 BOOST_HTTP_PROTO_DECL void start_buffers(message_view_base const&);
313 BOOST_HTTP_PROTO_DECL void start_source(message_view_base const&, source*);
314
315 enum class style
316 {
317 empty,
318 buffers,
319 source,
320 stream
321 };
322
323 detail::workspace ws_;
324 detail::workspace filter_ws_;
325 detail::array_of_const_buffers buf_;
326 filter* zlib_filter_ = nullptr;
327 bool filter_done_ = false;
328 source* src_;
329 context* ctx_ = nullptr;
330 buffers::circular_buffer tmp0_;
331 buffers::circular_buffer tmp1_;
332 detail::array_of_const_buffers out_;
333
334 buffers::const_buffer* hp_; // header
335
336 style st_;
337 bool more_;
338 bool is_done_;
339 bool is_chunked_;
340 bool is_expect_continue_;
341 bool is_compressed_ = false;
342 };
343
344 //------------------------------------------------
345
346 /**
347 A proxy type used to pass bounded input to the
348 associated serializer.
349 */
350 struct serializer::stream
351 {
352 /** Default constructor.
353
354 Creates a stream without an associated serializer
355 object.
356 */
357 stream() = default;
358
359 /** Copy constructor.
360
361 The constructed stream will share the same
362 serializer as `other`.
363 */
364 stream(stream const& other) = default;
365
366 /** Assignment operator
367
368 The current stream will share the same serializer
369 as `other`.
370 */
371 stream& operator= (
372 stream const& other) = default;
373
374 /**
375 A MutableBufferSequence consisting of a buffer pair.
376 */
377 using buffers_type =
378 buffers::mutable_buffer_pair;
379
380 /**
381 Returns the remaining available capacity.
382
383 The returned value represents the available free
384 space in the backing fixed-sized buffers used by the
385 serializer associated with this stream.
386
387 The capacity is absolute and does not do any
388 accounting for any octets required by a chunked
389 transfer encoding.
390 */
391 BOOST_HTTP_PROTO_DECL
392 std::size_t
393 capacity() const noexcept;
394
395 /**
396 Returns the number of octets serialized by this
397 stream.
398
399 The associated serializer stores stream output in its
400 internal buffers. The stream returns the size of this
401 output.
402 */
403 BOOST_HTTP_PROTO_DECL
404 std::size_t
405 size() const noexcept;
406
407 /**
408 Returns a boolean indicating if the stream can
409 receive more input.
410
411 The fixed-sized buffers maintained by the associated
412 serializer can be sufficiently full from previous
413 calls to \ref stream::commit.
414
415 This function can be called to determine if the user
416 should drain the serializer via \ref serializer::consume calls
417 before attempting to fill the buffer sequence
418 returned from \ref stream::prepare.
419 */
420 BOOST_HTTP_PROTO_DECL
421 bool
422 is_full() const noexcept;
423
424 /**
425 Returns a MutableBufferSequence capable of storing
426 input from the user
427
428 The returned buffer sequence is as wide as is
429 possible. If a non-chunked transfer encoding is
430 being used than the returned sequence encompasses
431 the unused area of the serializer's fixed-sized
432 buffers.
433
434 If a chunked transer encoding is used then space is
435 reserved for the chunk header in addition to the
436 closing CRLF required for the chunk data. In
437 addition to this, space is also reserved for the
438 last-chunk.
439
440 This is done so that users can chain calls to
441 \ref stream::commit and \ref stream::close
442 without having to drain the serializer via
443 \ref serializer::consume calls.
444
445 \exception std::length_error Thrown if the stream
446 has insufficient capacity and a chunked transfer
447 encoding is being used
448 */
449 BOOST_HTTP_PROTO_DECL
450 buffers_type
451 prepare() const;
452
453 /**
454 Serialize and commit `n` bytes.
455
456 Once the sequence returned from \ref prepare has been
457 filled, the input can be serialized and committed to the
458 associated serializer's output area via a call to `commit(n)`.
459
460 If a chunked transfer encoding is being used then commit
461 is responsible for writing the chunk-header and also the
462 closing CRLF for the chunk-data. `n` denotes the size
463 of the chunk.
464
465 \exception std::logic_error Thrown if commit is
466 called with 0. Instead, the closing chunk must be
467 written by a call to \ref stream::close.
468 */
469 BOOST_HTTP_PROTO_DECL
470 void
471 commit(std::size_t n) const;
472
473 /**
474 Close the stream.
475
476 close() writes the last-chunk to the underlying buffers
477 of the stream's associated serializer, i.e. `0\r\n\r\n`.
478
479 \excpeption std::logic_error Thrown if the stream
480 has been previously closed.
481 */
482 BOOST_HTTP_PROTO_DECL
483 void
484 close() const;
485
486 private:
487 friend class serializer;
488
489 explicit
490 7 stream(
491 serializer& sr) noexcept
492 7 : sr_(&sr)
493 {
494 7 }
495
496 serializer* sr_ = nullptr;
497 };
498
499 //---------------------------------------------------------
500
501 /** A ConstBufferSequence representing the output
502 */
503 class serializer::
504 const_buffers_type
505 {
506 std::size_t n_ = 0;
507 buffers::const_buffer const* p_ = nullptr;
508
509 friend class serializer;
510
511 63 const_buffers_type(
512 buffers::const_buffer const* p,
513 std::size_t n) noexcept
514 63 : n_(n)
515 63 , p_(p)
516 {
517 63 }
518
519 public:
520 using iterator = buffers::const_buffer const*;
521 using const_iterator = iterator;
522 using value_type = buffers::const_buffer;
523 using reference = buffers::const_buffer;
524 using const_reference = buffers::const_buffer;
525 using size_type = std::size_t;
526 using difference_type = std::ptrdiff_t;
527
528 const_buffers_type() = default;
529 const_buffers_type(
530 const_buffers_type const&) = default;
531 const_buffers_type& operator=(
532 const_buffers_type const&) = default;
533
534 iterator
535 126 begin() const noexcept
536 {
537 126 return p_;
538 }
539
540 iterator
541 126 end() const noexcept
542 {
543 126 return p_ + n_;
544 }
545 };
546
547 //------------------------------------------------
548
549 template<
550 class ConstBufferSequence,
551 class>
552 void
553 14 serializer::
554 start(
555 message_view_base const& m,
556 ConstBufferSequence&& body)
557 {
558 14 start_init(m);
559
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
14 auto& ws = (is_compressed_ ? filter_ws_ : ws_);
560
561 auto const& bs =
562 14 ws.emplace<ConstBufferSequence>(
563 std::forward<ConstBufferSequence>(body));
564
565 14 std::size_t n = std::distance(
566 buffers::begin(bs),
567 buffers::end(bs));
568
569 14 buf_ = make_array(ws, n);
570 14 auto p = buf_.data();
571
3/3
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 2 times.
28 for(buffers::const_buffer b : buffers::range(bs))
572 14 *p++ = b;
573
574 14 start_buffers(m);
575 14 }
576
577 template<
578 class Source,
579 class... Args,
580 class>
581 Source&
582 16 serializer::
583 start(
584 message_view_base const& m,
585 Args&&... args)
586 {
587 static_assert(
588 !std::is_abstract<Source>::value,
589 "The Source must be non-abstract, i.e. implements: `auto on_read(buffers::mutable_buffer b) -> http_proto::results;`");
590 static_assert(
591 std::is_constructible<Source, Args...>::value ||
592 std::is_constructible<Source, buffered_base::allocator&, Args...>::value,
593 "The Source cannot be constructed with the given arguments");
594
595 16 start_init(m);
596 16 auto& src = construct_source<Source>(
597 std::forward<Args>(args)...);
598 16 start_source(m, std::addressof(src));
599 16 return src;
600 }
601
602 //------------------------------------------------
603
604 inline
605 auto
606 26 serializer::
607 make_array(std::size_t n) ->
608 detail::array_of_const_buffers
609 {
610 return {
611 26 ws_.push_array(n,
612 26 buffers::const_buffer{}),
613
1/2
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
26 n };
614 }
615
616 inline
617 auto
618 7 serializer::
619 make_array(detail::workspace& ws, std::size_t n) ->
620 detail::array_of_const_buffers
621 {
622
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
7 return {ws.push_array(n, buffers::const_buffer{}), n};
623 }
624
625 } // http_proto
626 } // boost
627
628 #endif
629