GCC Code Coverage Report


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