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 | 88 | is_done() const noexcept | |
161 | { | ||
162 | 88 | 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 | // chunk = chunk-size [ chunk-ext ] CRLF | ||
251 | // chunk-data CRLF | ||
252 | static | ||
253 | constexpr | ||
254 | std::size_t | ||
255 | chunk_header_len_ = 16 + 2; // chunk-size + CRLF | ||
256 | |||
257 | // last-chunk = 1*("0") [ chunk-ext ] CRLF | ||
258 | static | ||
259 | constexpr | ||
260 | std::size_t | ||
261 | last_chunk_len_ = 1 + 2 + 2; // chunked-body termination requires an extra CRLF | ||
262 | |||
263 | static | ||
264 | constexpr | ||
265 | std::size_t | ||
266 | chunked_overhead_ = | ||
267 | chunk_header_len_ + | ||
268 | 2 + // CRLF | ||
269 | last_chunk_len_; | ||
270 | |||
271 | detail::workspace ws_; | ||
272 | detail::array_of_const_buffers buf_; | ||
273 | source* src_; | ||
274 | |||
275 | buffers::circular_buffer tmp0_; | ||
276 | buffers::circular_buffer tmp1_; | ||
277 | detail::array_of_const_buffers out_; | ||
278 | |||
279 | buffers::const_buffer* hp_; // header | ||
280 | |||
281 | style st_; | ||
282 | bool more_; | ||
283 | bool is_done_; | ||
284 | bool is_chunked_; | ||
285 | bool is_expect_continue_; | ||
286 | }; | ||
287 | |||
288 | //------------------------------------------------ | ||
289 | |||
290 | struct serializer::stream | ||
291 | { | ||
292 | /** Constructor. | ||
293 | */ | ||
294 | stream() = default; | ||
295 | |||
296 | /** Constructor. | ||
297 | */ | ||
298 | stream(stream const&) = default; | ||
299 | |||
300 | /** Constructor. | ||
301 | */ | ||
302 | stream& operator= | ||
303 | (stream const&) = default; | ||
304 | |||
305 | using buffers_type = | ||
306 | buffers::mutable_buffer_pair; | ||
307 | |||
308 | BOOST_HTTP_PROTO_DECL | ||
309 | std::size_t | ||
310 | capacity() const; | ||
311 | |||
312 | BOOST_HTTP_PROTO_DECL | ||
313 | std::size_t | ||
314 | size() const; | ||
315 | |||
316 | BOOST_HTTP_PROTO_DECL | ||
317 | buffers_type | ||
318 | prepare(std::size_t n) const; | ||
319 | |||
320 | BOOST_HTTP_PROTO_DECL | ||
321 | void | ||
322 | commit(std::size_t n) const; | ||
323 | |||
324 | BOOST_HTTP_PROTO_DECL | ||
325 | void | ||
326 | close() const; | ||
327 | |||
328 | private: | ||
329 | friend class serializer; | ||
330 | |||
331 | explicit | ||
332 | 6 | stream( | |
333 | serializer& sr) noexcept | ||
334 | 6 | : sr_(&sr) | |
335 | { | ||
336 | 6 | } | |
337 | |||
338 | serializer* sr_ = nullptr; | ||
339 | }; | ||
340 | |||
341 | //--------------------------------------------------------- | ||
342 | |||
343 | class serializer:: | ||
344 | const_buffers_type | ||
345 | { | ||
346 | std::size_t n_ = 0; | ||
347 | buffers::const_buffer const* p_ = nullptr; | ||
348 | |||
349 | friend class serializer; | ||
350 | |||
351 | 74 | const_buffers_type( | |
352 | buffers::const_buffer const* p, | ||
353 | std::size_t n) noexcept | ||
354 | 74 | : n_(n) | |
355 | 74 | , p_(p) | |
356 | { | ||
357 | 74 | } | |
358 | |||
359 | public: | ||
360 | using iterator = buffers::const_buffer const*; | ||
361 | using const_iterator = iterator; | ||
362 | using value_type = buffers::const_buffer; | ||
363 | using reference = buffers::const_buffer; | ||
364 | using const_reference = buffers::const_buffer; | ||
365 | using size_type = std::size_t; | ||
366 | using difference_type = std::ptrdiff_t; | ||
367 | |||
368 | const_buffers_type() = default; | ||
369 | const_buffers_type( | ||
370 | const_buffers_type const&) = default; | ||
371 | const_buffers_type& operator=( | ||
372 | const_buffers_type const&) = default; | ||
373 | |||
374 | iterator | ||
375 | 148 | begin() const noexcept | |
376 | { | ||
377 | 148 | return p_; | |
378 | } | ||
379 | |||
380 | iterator | ||
381 | 148 | end() const noexcept | |
382 | { | ||
383 | 148 | return p_ + n_; | |
384 | } | ||
385 | }; | ||
386 | |||
387 | //------------------------------------------------ | ||
388 | |||
389 | template< | ||
390 | class ConstBufferSequence, | ||
391 | class> | ||
392 | void | ||
393 | 14 | serializer:: | |
394 | start( | ||
395 | message_view_base const& m, | ||
396 | ConstBufferSequence&& body) | ||
397 | { | ||
398 | 14 | start_init(m); | |
399 | 14 | auto const& bs = | |
400 | ws_.emplace<ConstBufferSequence>( | ||
401 | std::forward<ConstBufferSequence>(body)); | ||
402 | 14 | std::size_t n = std::distance( | |
403 | buffers::begin(bs), | ||
404 | buffers::end(bs)); | ||
405 | 14 | buf_ = make_array(n); | |
406 | 14 | auto p = buf_.data(); | |
407 |
2/2✓ Branch 2 taken 7 times.
✓ Branch 3 taken 7 times.
|
28 | for(buffers::const_buffer b : |
408 | 14 | buffers::range(bs)) | |
409 | 14 | *p++ = b; | |
410 | 14 | start_buffers(m); | |
411 | 14 | } | |
412 | |||
413 | template< | ||
414 | class Source, | ||
415 | class... Args, | ||
416 | class> | ||
417 | Source& | ||
418 | 16 | serializer:: | |
419 | start( | ||
420 | message_view_base const& m, | ||
421 | Args&&... args) | ||
422 | { | ||
423 | static_assert( | ||
424 | std::is_constructible<Source, Args...>::value || | ||
425 | std::is_constructible<Source, buffered_base::allocator&, Args...>::value, | ||
426 | "The Source cannot be constructed with the given arguments"); | ||
427 | |||
428 | 16 | start_init(m); | |
429 | 16 | auto& src = construct_source<Source>( | |
430 | std::forward<Args>(args)...); | ||
431 | 16 | start_source(m, std::addressof(src)); | |
432 | 16 | return src; | |
433 | } | ||
434 | |||
435 | //------------------------------------------------ | ||
436 | |||
437 | inline | ||
438 | auto | ||
439 | 32 | serializer:: | |
440 | make_array(std::size_t n) -> | ||
441 | detail::array_of_const_buffers | ||
442 | { | ||
443 | return { | ||
444 | ws_.push_array(n, | ||
445 | 64 | buffers::const_buffer{}), | |
446 |
1/2✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
|
32 | n }; |
447 | } | ||
448 | |||
449 | } // http_proto | ||
450 | } // boost | ||
451 | |||
452 | #endif | ||
453 |