GCC Code Coverage Report


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