GCC Code Coverage Report


Directory: libs/http_proto/
File: boost/http_proto/serializer.hpp
Date: 2024-04-22 17:18:35
Exec Total Coverage
Lines: 37 38 97.4%
Functions: 16 17 94.1%
Branches: 3 4 75.0%

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