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 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 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 : // 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 7 : serializer::
394 : start(
395 : message_view_base const& m,
396 : ConstBufferSequence&& body)
397 : {
398 7 : start_init(m);
399 7 : auto const& bs =
400 : ws_.emplace<ConstBufferSequence>(
401 : std::forward<ConstBufferSequence>(body));
402 7 : std::size_t n = std::distance(
403 : buffers::begin(bs),
404 : buffers::end(bs));
405 7 : buf_ = make_array(n);
406 7 : auto p = buf_.data();
407 14 : for(buffers::const_buffer b :
408 7 : buffers::range(bs))
409 7 : *p++ = b;
410 7 : start_buffers(m);
411 7 : }
412 :
413 : template<
414 : class Source,
415 : class... Args,
416 : class>
417 : Source&
418 8 : 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 8 : start_init(m);
429 8 : auto& src = construct_source<Source>(
430 : std::forward<Args>(args)...);
431 8 : start_source(m, std::addressof(src));
432 8 : 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 32 : n };
447 : }
448 :
449 : } // http_proto
450 : } // boost
451 :
452 : #endif
|