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 |