GCC Code Coverage Report


Directory: libs/http_proto/
File: boost/http_proto/serializer.hpp
Date: 2024-04-15 15:38:04
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 /** A ConstBuffers representing the output
50 */
51 class const_buffers_type;
52
53 struct stream;
54
55 /** Destructor
56 */
57 BOOST_HTTP_PROTO_DECL
58 ~serializer();
59
60 /** Constructor
61 */
62 BOOST_HTTP_PROTO_DECL
63 serializer();
64
65 /** Constructor
66 */
67 BOOST_HTTP_PROTO_DECL
68 serializer(
69 serializer&&) noexcept;
70
71 /** Constructor
72 */
73 BOOST_HTTP_PROTO_DECL
74 explicit
75 serializer(
76 std::size_t buffer_size);
77
78 //--------------------------------------------
79
80 /** Prepare the serializer for a new stream
81 */
82 BOOST_HTTP_PROTO_DECL
83 void
84 reset() noexcept;
85
86 /** Prepare the serializer for a new message
87
88 The message will not contain a body.
89 Changing the contents of the message
90 after calling this function and before
91 @ref is_done returns `true` results in
92 undefined behavior.
93 */
94 void
95 4 start(
96 message_view_base const& m)
97 {
98 4 start_empty(m);
99 4 }
100
101 /** Prepare the serializer for a new message
102
103 Changing the contents of the message
104 after calling this function and before
105 @ref is_done returns `true` results in
106 undefined behavior.
107
108 @par Constraints
109 @code
110 is_const_buffers< ConstBuffers >::value == true
111 @endcode
112 */
113 template<
114 class ConstBufferSequence
115 #ifndef BOOST_HTTP_PROTO_DOCS
116 ,class = typename
117 std::enable_if<
118 buffers::is_const_buffer_sequence<
119 ConstBufferSequence>::value
120 >::type
121 #endif
122 >
123 void
124 start(
125 message_view_base const& m,
126 ConstBufferSequence&& body);
127
128 /** Prepare the serializer for a new message
129
130 Changing the contents of the message
131 after calling this function and before
132 @ref is_done returns `true` results in
133 undefined behavior.
134 */
135 template<
136 class Source,
137 class... Args
138 #ifndef BOOST_HTTP_PROTO_DOCS
139 ,class = typename std::enable_if<
140 is_source<Source>::value>::type
141 #endif
142 >
143 Source&
144 start(
145 message_view_base const& m,
146 Args&&... args);
147
148 //--------------------------------------------
149
150 BOOST_HTTP_PROTO_DECL
151 stream
152 start_stream(
153 message_view_base const& m);
154
155 //--------------------------------------------
156
157 /** Return true if serialization is complete.
158 */
159 bool
160 78 is_done() const noexcept
161 {
162 78 return is_done_;
163 }
164
165 /** Return the output area.
166
167 This function will serialize some or
168 all of the content and return the
169 corresponding output buffers.
170
171 @par Preconditions
172 @code
173 this->is_done() == false
174 @endcode
175 */
176 BOOST_HTTP_PROTO_DECL
177 auto
178 prepare() ->
179 system::result<
180 const_buffers_type>;
181
182 /** Consume bytes from the output area.
183 */
184 BOOST_HTTP_PROTO_DECL
185 void
186 consume(std::size_t n);
187
188 private:
189 static void copy(
190 buffers::const_buffer*,
191 buffers::const_buffer const*,
192 std::size_t n) noexcept;
193 auto
194 make_array(std::size_t n) ->
195 detail::array_of_const_buffers;
196
197 template<
198 class Source,
199 class... Args,
200 typename std::enable_if<
201 std::is_constructible<
202 Source,
203 Args...>::value>::type* = nullptr>
204 Source&
205 16 construct_source(Args&&... args)
206 {
207 return ws_.emplace<Source>(
208 16 std::forward<Args>(args)...);
209 }
210
211 template<
212 class Source,
213 class... Args,
214 typename std::enable_if<
215 std::is_constructible<
216 Source,
217 buffered_base::allocator&,
218 Args...>::value>::type* = nullptr>
219 Source&
220 construct_source(Args&&... args)
221 {
222 buffered_base::allocator a(
223 ws_.data(),
224 (ws_.size() - ws_.space_needed<Source>()) / 2,
225 false);
226 auto& src = ws_.emplace<Source>(
227 a, std::forward<Args>(args)...);
228 ws_.reserve_front(a.size_used());
229 return src;
230 }
231
232 BOOST_HTTP_PROTO_DECL void start_init(message_view_base const&);
233 BOOST_HTTP_PROTO_DECL void start_empty(message_view_base const&);
234 BOOST_HTTP_PROTO_DECL void start_buffers(message_view_base const&);
235 BOOST_HTTP_PROTO_DECL void start_source(message_view_base const&, source*);
236
237 enum class style
238 {
239 empty,
240 buffers,
241 source,
242 stream
243 };
244
245 // chunked-body = *chunk
246 // last-chunk
247 // trailer-section
248 // CRLF
249
250 static
251 constexpr
252 std::size_t
253 crlf_len_ = 2;
254
255 // chunk = chunk-size [ chunk-ext ] CRLF
256 // chunk-data CRLF
257 static
258 constexpr
259 std::size_t
260 chunk_header_len_ =
261 16 + // 16 hex digits => 64 bit number
262 crlf_len_;
263
264 // last-chunk = 1*("0") [ chunk-ext ] CRLF
265 static
266 constexpr
267 std::size_t
268 last_chunk_len_ =
269 1 + // "0"
270 crlf_len_ +
271 crlf_len_; // chunked-body termination requires an extra CRLF
272
273 static
274 constexpr
275 std::size_t
276 chunked_overhead_ =
277 chunk_header_len_ +
278 crlf_len_ + // closing chunk data
279 last_chunk_len_;
280
281 detail::workspace ws_;
282 detail::array_of_const_buffers buf_;
283 source* src_;
284
285 buffers::circular_buffer tmp0_;
286 buffers::circular_buffer tmp1_;
287 detail::array_of_const_buffers out_;
288
289 buffers::const_buffer* hp_; // header
290
291 style st_;
292 bool more_;
293 bool is_done_;
294 bool is_chunked_;
295 bool is_expect_continue_;
296 };
297
298 //------------------------------------------------
299
300 struct serializer::stream
301 {
302 /** Constructor.
303 */
304 stream() = default;
305
306 /** Constructor.
307 */
308 stream(stream const&) = default;
309
310 /** Constructor.
311 */
312 stream& operator=
313 (stream const&) = default;
314
315 using buffers_type =
316 buffers::mutable_buffer_pair;
317
318 BOOST_HTTP_PROTO_DECL
319 std::size_t
320 capacity() const noexcept;
321
322 BOOST_HTTP_PROTO_DECL
323 std::size_t
324 size() const noexcept;
325
326 BOOST_HTTP_PROTO_DECL
327 bool
328 is_full() const noexcept;
329
330 BOOST_HTTP_PROTO_DECL
331 buffers_type
332 prepare() const;
333
334 BOOST_HTTP_PROTO_DECL
335 void
336 commit(std::size_t n) const;
337
338 BOOST_HTTP_PROTO_DECL
339 void
340 close() const;
341
342 private:
343 friend class serializer;
344
345 explicit
346 7 stream(
347 serializer& sr) noexcept
348 7 : sr_(&sr)
349 {
350 7 }
351
352 serializer* sr_ = nullptr;
353 };
354
355 //---------------------------------------------------------
356
357 class serializer::
358 const_buffers_type
359 {
360 std::size_t n_ = 0;
361 buffers::const_buffer const* p_ = nullptr;
362
363 friend class serializer;
364
365 63 const_buffers_type(
366 buffers::const_buffer const* p,
367 std::size_t n) noexcept
368 63 : n_(n)
369 63 , p_(p)
370 {
371 63 }
372
373 public:
374 using iterator = buffers::const_buffer const*;
375 using const_iterator = iterator;
376 using value_type = buffers::const_buffer;
377 using reference = buffers::const_buffer;
378 using const_reference = buffers::const_buffer;
379 using size_type = std::size_t;
380 using difference_type = std::ptrdiff_t;
381
382 const_buffers_type() = default;
383 const_buffers_type(
384 const_buffers_type const&) = default;
385 const_buffers_type& operator=(
386 const_buffers_type const&) = default;
387
388 iterator
389 126 begin() const noexcept
390 {
391 126 return p_;
392 }
393
394 iterator
395 126 end() const noexcept
396 {
397 126 return p_ + n_;
398 }
399 };
400
401 //------------------------------------------------
402
403 template<
404 class ConstBufferSequence,
405 class>
406 void
407 14 serializer::
408 start(
409 message_view_base const& m,
410 ConstBufferSequence&& body)
411 {
412 14 start_init(m);
413 14 auto const& bs =
414 ws_.emplace<ConstBufferSequence>(
415 std::forward<ConstBufferSequence>(body));
416 14 std::size_t n = std::distance(
417 buffers::begin(bs),
418 buffers::end(bs));
419 14 buf_ = make_array(n);
420 14 auto p = buf_.data();
421
2/2
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 7 times.
28 for(buffers::const_buffer b :
422 14 buffers::range(bs))
423 14 *p++ = b;
424 14 start_buffers(m);
425 14 }
426
427 template<
428 class Source,
429 class... Args,
430 class>
431 Source&
432 16 serializer::
433 start(
434 message_view_base const& m,
435 Args&&... args)
436 {
437 static_assert(
438 std::is_constructible<Source, Args...>::value ||
439 std::is_constructible<Source, buffered_base::allocator&, Args...>::value,
440 "The Source cannot be constructed with the given arguments");
441
442 16 start_init(m);
443 16 auto& src = construct_source<Source>(
444 std::forward<Args>(args)...);
445 16 start_source(m, std::addressof(src));
446 16 return src;
447 }
448
449 //------------------------------------------------
450
451 inline
452 auto
453 33 serializer::
454 make_array(std::size_t n) ->
455 detail::array_of_const_buffers
456 {
457 return {
458 ws_.push_array(n,
459 66 buffers::const_buffer{}),
460
1/2
✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
33 n };
461 }
462
463 } // http_proto
464 } // boost
465
466 #endif
467