GCC Code Coverage Report


Directory: libs/http_proto/
File: include/boost/http_proto/serializer.hpp
Date: 2025-05-26 06:53:21
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 detail::workspace&,
275 Args...>::value>::type* = nullptr>
276 Source&
277 construct_source(Args&&... args)
278 {
279 return ws_.emplace<Source>(
280 ws_, std::forward<Args>(args)...);
281 }
282
283 BOOST_HTTP_PROTO_DECL void start_init(message_view_base const&);
284 BOOST_HTTP_PROTO_DECL void start_empty(message_view_base const&);
285 BOOST_HTTP_PROTO_DECL void start_buffers(message_view_base const&);
286 BOOST_HTTP_PROTO_DECL void start_source(message_view_base const&, source*);
287
288 enum class style
289 {
290 empty,
291 buffers,
292 source,
293 stream
294 };
295
296 // chunked-body = *chunk
297 // last-chunk
298 // trailer-section
299 // CRLF
300
301 static
302 constexpr
303 std::size_t
304 crlf_len_ = 2;
305
306 // chunk = chunk-size [ chunk-ext ] CRLF
307 // chunk-data CRLF
308 static
309 constexpr
310 std::size_t
311 chunk_header_len_ =
312 16 + // 16 hex digits => 64 bit number
313 crlf_len_;
314
315 // last-chunk = 1*("0") [ chunk-ext ] CRLF
316 static
317 constexpr
318 std::size_t
319 last_chunk_len_ =
320 1 + // "0"
321 crlf_len_ +
322 crlf_len_; // chunked-body termination requires an extra CRLF
323
324 static
325 constexpr
326 std::size_t
327 chunked_overhead_ =
328 chunk_header_len_ +
329 crlf_len_ + // closing chunk data
330 last_chunk_len_;
331
332 detail::workspace ws_;
333 detail::array_of_const_buffers buf_;
334 detail::filter* filter_ = nullptr;
335 source* src_;
336 context& ctx_;
337 buffers::circular_buffer tmp0_;
338 buffers::circular_buffer tmp1_;
339 detail::array_of_const_buffers prepped_;
340
341 buffers::mutable_buffer chunk_header_;
342 buffers::mutable_buffer chunk_close_;
343 buffers::mutable_buffer last_chunk_;
344
345 buffers::circular_buffer* in_ = nullptr;
346 buffers::circular_buffer* out_ = nullptr;
347
348 buffers::const_buffer* hp_; // header
349
350 style st_;
351 bool more_;
352 bool is_done_;
353 bool is_header_done_;
354 bool is_chunked_;
355 bool is_expect_continue_;
356 bool is_compressed_ = false;
357 bool filter_done_ = false;
358 };
359
360 //------------------------------------------------
361
362 /** The type used for caller-provided body data during
363 serialization.
364
365 @code{.cpp}
366 http_proto::serializer sr(128);
367
368 http_proto::request req;
369 auto stream = sr.start_stream(req);
370
371 std::string_view msg = "Hello, world!";
372 auto n = buffers::copy(
373 stream.prepare(),
374 buffers::make_buffer(
375 msg.data(), msg.size()));
376
377 stream.commit(n);
378
379 auto cbs = sr.prepare().value();
380 (void)cbs;
381 @endcode
382 */
383 struct serializer::stream
384 {
385 /** Constructor.
386
387 The only valid operations on default constructed
388 streams are assignment and destruction.
389 */
390 stream() = default;
391
392 /** Constructor.
393
394 The constructed stream will share the same
395 serializer as `other`.
396 */
397 stream(stream const& other) = default;
398
399 /** Assignment.
400
401 The current stream will share the same serializer
402 as `other`.
403 */
404 stream& operator= (
405 stream const& other) = default;
406
407 /** A MutableBufferSequence consisting of a buffer pair.
408 */
409 using buffers_type =
410 buffers::mutable_buffer_pair;
411
412 /** Returns the remaining available capacity.
413
414 The returned value represents the available free
415 space in the backing fixed-sized buffers used by the
416 serializer associated with this stream.
417
418 The capacity is absolute and does not do any
419 accounting for any octets required by a chunked
420 transfer encoding.
421 */
422 BOOST_HTTP_PROTO_DECL
423 std::size_t
424 capacity() const noexcept;
425
426 /** Returns the number of octets serialized by this
427 stream.
428
429 The associated serializer stores stream output in its
430 internal buffers. The stream returns the size of this
431 output.
432 */
433 BOOST_HTTP_PROTO_DECL
434 std::size_t
435 size() const noexcept;
436
437 /** Return true if the stream cannot currently hold
438 additional output data.
439
440 The fixed-sized buffers maintained by the associated
441 serializer can be sufficiently full from previous
442 calls to @ref stream::commit.
443
444 This function can be called to determine if the caller
445 should drain the serializer via @ref serializer::consume calls
446 before attempting to fill the buffer sequence
447 returned from @ref stream::prepare.
448 */
449 BOOST_HTTP_PROTO_DECL
450 bool
451 is_full() const noexcept;
452
453 /** Returns a MutableBufferSequence for storing
454 serializer input. If `n` bytes are written to the
455 buffer sequence, @ref stream::commit must be called
456 with `n` to update the backing serializer's buffers.
457
458 The returned buffer sequence is as wide as is
459 possible.
460
461 @exception std::length_error Thrown if the stream
462 has insufficient capacity and a chunked transfer
463 encoding is being used
464 */
465 BOOST_HTTP_PROTO_DECL
466 buffers_type
467 prepare() const;
468
469 /** Make `n` bytes available to the serializer.
470
471 Once the buffer sequence returned from @ref stream::prepare
472 has been filled, the input can be marked as ready
473 for serialization by using this function.
474
475 @exception std::logic_error Thrown if `commit` is
476 called with 0.
477 */
478 BOOST_HTTP_PROTO_DECL
479 void
480 commit(std::size_t n) const;
481
482 /** Indicate that no more data is coming and that the
483 body should be treated as complete.
484
485 @excpeption std::logic_error Thrown if the stream
486 has been previously closed.
487 */
488 BOOST_HTTP_PROTO_DECL
489 void
490 close() const;
491
492 private:
493 friend class serializer;
494
495 explicit
496 22 stream(
497 serializer& sr) noexcept
498 22 : sr_(&sr)
499 {
500 22 }
501
502 serializer* sr_ = nullptr;
503 };
504
505 //---------------------------------------------------------
506
507 template<
508 class ConstBufferSequence,
509 class>
510 void
511 48 serializer::
512 start(
513 message_view_base const& m,
514 ConstBufferSequence&& body)
515 {
516 48 start_init(m);
517 auto const& bs =
518 48 ws_.emplace<ConstBufferSequence>(
519 std::forward<ConstBufferSequence>(body));
520
521 48 std::size_t n = std::distance(
522 buffers::begin(bs),
523 buffers::end(bs));
524
525 48 buf_ = make_array(n);
526 48 auto p = buf_.data();
527
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))
528 784 *p++ = b;
529
530 48 start_buffers(m);
531 48 }
532
533 template<
534 class Source,
535 class... Args,
536 class>
537 Source&
538 34 serializer::
539 start(
540 message_view_base const& m,
541 Args&&... args)
542 {
543 static_assert(
544 !std::is_abstract<Source>::value, "");
545 static_assert(
546 std::is_constructible<Source, Args...>::value ||
547 std::is_constructible<Source, detail::workspace&, Args...>::value,
548 "The Source cannot be constructed with the given arguments");
549
550 34 start_init(m);
551 34 auto& src = construct_source<Source>(
552 std::forward<Args>(args)...);
553 34 start_source(m, std::addressof(src));
554 34 return src;
555 }
556
557 //------------------------------------------------
558
559 inline
560 auto
561 99 serializer::
562 make_array(std::size_t n) ->
563 detail::array_of_const_buffers
564 {
565 return {
566 99 ws_.push_array(n,
567 99 buffers::const_buffer{}),
568
1/2
✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
198 n };
569 }
570
571 } // http_proto
572 } // boost
573
574 #endif
575