Line data Source code
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 0 : 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 8 : construct_source(Args&&... args)
206 : {
207 : return ws_.emplace<Source>(
208 8 : 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 7 : serializer::
408 : start(
409 : message_view_base const& m,
410 : ConstBufferSequence&& body)
411 : {
412 7 : start_init(m);
413 7 : auto const& bs =
414 : ws_.emplace<ConstBufferSequence>(
415 : std::forward<ConstBufferSequence>(body));
416 7 : std::size_t n = std::distance(
417 : buffers::begin(bs),
418 : buffers::end(bs));
419 7 : buf_ = make_array(n);
420 7 : auto p = buf_.data();
421 14 : for(buffers::const_buffer b :
422 7 : buffers::range(bs))
423 7 : *p++ = b;
424 7 : start_buffers(m);
425 7 : }
426 :
427 : template<
428 : class Source,
429 : class... Args,
430 : class>
431 : Source&
432 8 : 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 8 : start_init(m);
443 8 : auto& src = construct_source<Source>(
444 : std::forward<Args>(args)...);
445 8 : start_source(m, std::addressof(src));
446 8 : 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 33 : n };
461 : }
462 :
463 : } // http_proto
464 : } // boost
465 :
466 : #endif
|