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 : Objects of this type operate using an "input area" and an
46 : "output area". Callers provide data to the input area
47 : using one of the @ref start or @ref start_stream member
48 : functions. After input is provided, serialized data
49 : becomes available in the serializer's output area in the
50 : form of a constant buffer sequence.
51 :
52 : Callers alternate between filling the input area and
53 : consuming the output area until all the input has been
54 : provided and all the output data has been consumed, or
55 : an error occurs.
56 :
57 : After calling @ref start, the caller must ensure that the
58 : contents of the associated message are not changed or
59 : destroyed until @ref is_done returns true, @ref reset is
60 : called, or the serializer is destroyed, otherwise the
61 : behavior is undefined.
62 : */
63 : class BOOST_SYMBOL_VISIBLE
64 : serializer
65 : {
66 : public:
67 : class const_buffers_type;
68 :
69 : struct stream;
70 :
71 : /** Destructor
72 : */
73 : BOOST_HTTP_PROTO_DECL
74 : ~serializer();
75 :
76 : /** Constructor
77 : */
78 : BOOST_HTTP_PROTO_DECL
79 : serializer();
80 :
81 : /** Constructor
82 : */
83 : BOOST_HTTP_PROTO_DECL
84 : serializer(
85 : serializer&&) noexcept;
86 :
87 : /** Constructor
88 : */
89 : BOOST_HTTP_PROTO_DECL
90 : explicit
91 : serializer(
92 : std::size_t buffer_size);
93 :
94 : //--------------------------------------------
95 :
96 : /** Prepare the serializer for a new stream
97 : */
98 : BOOST_HTTP_PROTO_DECL
99 : void
100 : reset() noexcept;
101 :
102 : /** Prepare the serializer for a new message
103 :
104 : The message will not contain a body.
105 : Changing the contents of the message
106 : after calling this function and before
107 : @ref is_done returns `true` results in
108 : undefined behavior.
109 : */
110 : void
111 4 : start(
112 : message_view_base const& m)
113 : {
114 4 : start_empty(m);
115 4 : }
116 :
117 : /** Prepare the serializer for a new message
118 :
119 : Changing the contents of the message
120 : after calling this function and before
121 : @ref is_done returns `true` results in
122 : undefined behavior.
123 :
124 : @par Constraints
125 : @code
126 : is_const_buffers< ConstBuffers >::value == true
127 : @endcode
128 : */
129 : template<
130 : class ConstBufferSequence
131 : #ifndef BOOST_HTTP_PROTO_DOCS
132 : ,class = typename
133 : std::enable_if<
134 : buffers::is_const_buffer_sequence<
135 : ConstBufferSequence>::value
136 : >::type
137 : #endif
138 : >
139 : void
140 : start(
141 : message_view_base const& m,
142 : ConstBufferSequence&& body);
143 :
144 : /** Prepare the serializer for a new message
145 :
146 : Changing the contents of the message
147 : after calling this function and before
148 : @ref is_done returns `true` results in
149 : undefined behavior.
150 : */
151 : template<
152 : class Source,
153 : class... Args
154 : #ifndef BOOST_HTTP_PROTO_DOCS
155 : ,class = typename std::enable_if<
156 : is_source<Source>::value>::type
157 : #endif
158 : >
159 : Source&
160 : start(
161 : message_view_base const& m,
162 : Args&&... args);
163 :
164 : //--------------------------------------------
165 :
166 : /** Return a new stream for this serializer.
167 :
168 : After the serializer is destroyed, @ref reset is called,
169 : or @ref is_done returns true, the only valid operation
170 : on the stream is destruction.
171 :
172 : A stream may be used to invert the flow of control
173 : when the caller is supplying body data as a series
174 : of buffers.
175 : */
176 : BOOST_HTTP_PROTO_DECL
177 : stream
178 : start_stream(
179 : message_view_base const& m);
180 :
181 : //--------------------------------------------
182 :
183 : /** Return true if serialization is complete.
184 : */
185 : bool
186 78 : is_done() const noexcept
187 : {
188 78 : return is_done_;
189 : }
190 :
191 : /** Return the output area.
192 :
193 : This function will serialize some or
194 : all of the content and return the
195 : corresponding output buffers.
196 :
197 : @par Preconditions
198 : @code
199 : this->is_done() == false
200 : @endcode
201 : */
202 : BOOST_HTTP_PROTO_DECL
203 : auto
204 : prepare() ->
205 : system::result<
206 : const_buffers_type>;
207 :
208 : /** Consume bytes from the output area.
209 : */
210 : BOOST_HTTP_PROTO_DECL
211 : void
212 : consume(std::size_t n);
213 :
214 : private:
215 : static void copy(
216 : buffers::const_buffer*,
217 : buffers::const_buffer const*,
218 : std::size_t n) noexcept;
219 : auto
220 : make_array(std::size_t n) ->
221 : detail::array_of_const_buffers;
222 :
223 : template<
224 : class Source,
225 : class... Args,
226 : typename std::enable_if<
227 : std::is_constructible<
228 : Source,
229 : Args...>::value>::type* = nullptr>
230 : Source&
231 8 : construct_source(Args&&... args)
232 : {
233 8 : return ws_.emplace<Source>(
234 8 : std::forward<Args>(args)...);
235 : }
236 :
237 : template<
238 : class Source,
239 : class... Args,
240 : typename std::enable_if<
241 : std::is_constructible<
242 : Source,
243 : buffered_base::allocator&,
244 : Args...>::value>::type* = nullptr>
245 : Source&
246 : construct_source(Args&&... args)
247 : {
248 : buffered_base::allocator a(
249 : ws_.data(),
250 : (ws_.size() - ws_.space_needed<Source>()) / 2,
251 : false);
252 : auto& src = ws_.emplace<Source>(
253 : a, std::forward<Args>(args)...);
254 : ws_.reserve_front(a.size_used());
255 : return src;
256 : }
257 :
258 : BOOST_HTTP_PROTO_DECL void start_init(message_view_base const&);
259 : BOOST_HTTP_PROTO_DECL void start_empty(message_view_base const&);
260 : BOOST_HTTP_PROTO_DECL void start_buffers(message_view_base const&);
261 : BOOST_HTTP_PROTO_DECL void start_source(message_view_base const&, source*);
262 :
263 : enum class style
264 : {
265 : empty,
266 : buffers,
267 : source,
268 : stream
269 : };
270 :
271 : // chunked-body = *chunk
272 : // last-chunk
273 : // trailer-section
274 : // CRLF
275 :
276 : static
277 : constexpr
278 : std::size_t
279 : crlf_len_ = 2;
280 :
281 : // chunk = chunk-size [ chunk-ext ] CRLF
282 : // chunk-data CRLF
283 : static
284 : constexpr
285 : std::size_t
286 : chunk_header_len_ =
287 : 16 + // 16 hex digits => 64 bit number
288 : crlf_len_;
289 :
290 : // last-chunk = 1*("0") [ chunk-ext ] CRLF
291 : static
292 : constexpr
293 : std::size_t
294 : last_chunk_len_ =
295 : 1 + // "0"
296 : crlf_len_ +
297 : crlf_len_; // chunked-body termination requires an extra CRLF
298 :
299 : static
300 : constexpr
301 : std::size_t
302 : chunked_overhead_ =
303 : chunk_header_len_ +
304 : crlf_len_ + // closing chunk data
305 : last_chunk_len_;
306 :
307 : detail::workspace ws_;
308 : detail::array_of_const_buffers buf_;
309 : source* src_;
310 :
311 : buffers::circular_buffer tmp0_;
312 : buffers::circular_buffer tmp1_;
313 : detail::array_of_const_buffers out_;
314 :
315 : buffers::const_buffer* hp_; // header
316 :
317 : style st_;
318 : bool more_;
319 : bool is_done_;
320 : bool is_chunked_;
321 : bool is_expect_continue_;
322 : };
323 :
324 : //------------------------------------------------
325 :
326 : /** The type used for caller-provided body data during
327 : serialization.
328 :
329 : @code{.cpp}
330 : http_proto::serializer sr(128);
331 :
332 : http_proto::request req;
333 : auto stream = sr.start_stream(req);
334 :
335 : std::string_view msg = "Hello, world!";
336 : auto n = buffers::buffer_copy(
337 : stream.prepare(),
338 : buffers::make_buffer(
339 : msg.data(), msg.size()));
340 :
341 : stream.commit(n);
342 :
343 : auto cbs = sr.prepare().value();
344 : (void)cbs;
345 : @endcode
346 : */
347 : struct serializer::stream
348 : {
349 : /** Constructor.
350 :
351 : The only valid operations on default constructed
352 : streams are assignment and destruction.
353 : */
354 : stream() = default;
355 :
356 : /** Constructor.
357 :
358 : The constructed stream will share the same
359 : serializer as `other`.
360 : */
361 : stream(stream const& other) = default;
362 :
363 : /** Assignment.
364 :
365 : The current stream will share the same serializer
366 : as `other`.
367 : */
368 : stream& operator= (
369 : stream const& other) = default;
370 :
371 : /** A MutableBufferSequence consisting of a buffer pair.
372 : */
373 : using buffers_type =
374 : buffers::mutable_buffer_pair;
375 :
376 : /** Returns the remaining available capacity.
377 :
378 : The returned value represents the available free
379 : space in the backing fixed-sized buffers used by the
380 : serializer associated with this stream.
381 :
382 : The capacity is absolute and does not do any
383 : accounting for any octets required by a chunked
384 : transfer encoding.
385 : */
386 : BOOST_HTTP_PROTO_DECL
387 : std::size_t
388 : capacity() const noexcept;
389 :
390 : /** Returns the number of octets serialized by this
391 : stream.
392 :
393 : The associated serializer stores stream output in its
394 : internal buffers. The stream returns the size of this
395 : output.
396 : */
397 : BOOST_HTTP_PROTO_DECL
398 : std::size_t
399 : size() const noexcept;
400 :
401 : /** Return true if the stream cannot currently hold
402 : additional output data.
403 :
404 : The fixed-sized buffers maintained by the associated
405 : serializer can be sufficiently full from previous
406 : calls to @ref stream::commit.
407 :
408 : This function can be called to determine if the caller
409 : should drain the serializer via @ref serializer::consume calls
410 : before attempting to fill the buffer sequence
411 : returned from @ref stream::prepare.
412 : */
413 : BOOST_HTTP_PROTO_DECL
414 : bool
415 : is_full() const noexcept;
416 :
417 : /** Returns a MutableBufferSequence for storing
418 : serializer input. If `n` bytes are written to the
419 : buffer sequence, @ref stream::commit must be called
420 : with `n` to update the backing serializer's buffers.
421 :
422 : The returned buffer sequence is as wide as is
423 : possible.
424 :
425 : @exception std::length_error Thrown if the stream
426 : has insufficient capacity and a chunked transfer
427 : encoding is being used
428 : */
429 : BOOST_HTTP_PROTO_DECL
430 : buffers_type
431 : prepare() const;
432 :
433 : /** Make `n` bytes available to the serializer.
434 :
435 : Once the buffer sequence returned from @ref stream::prepare
436 : has been filled, the input can be marked as ready
437 : for serialization by using this function.
438 :
439 : @exception std::logic_error Thrown if `commit` is
440 : called with 0.
441 : */
442 : BOOST_HTTP_PROTO_DECL
443 : void
444 : commit(std::size_t n) const;
445 :
446 : /** Indicate that no more data is coming and that the
447 : body should be treated as complete.
448 :
449 : @excpeption std::logic_error Thrown if the stream
450 : has been previously closed.
451 : */
452 : BOOST_HTTP_PROTO_DECL
453 : void
454 : close() const;
455 :
456 : private:
457 : friend class serializer;
458 :
459 : explicit
460 7 : stream(
461 : serializer& sr) noexcept
462 7 : : sr_(&sr)
463 : {
464 7 : }
465 :
466 : serializer* sr_ = nullptr;
467 : };
468 :
469 : //---------------------------------------------------------
470 :
471 : /** A ConstBufferSequence representing the output
472 : */
473 : class serializer::
474 : const_buffers_type
475 : {
476 : std::size_t n_ = 0;
477 : buffers::const_buffer const* p_ = nullptr;
478 :
479 : friend class serializer;
480 :
481 63 : const_buffers_type(
482 : buffers::const_buffer const* p,
483 : std::size_t n) noexcept
484 63 : : n_(n)
485 63 : , p_(p)
486 : {
487 63 : }
488 :
489 : public:
490 : using iterator = buffers::const_buffer const*;
491 : using const_iterator = iterator;
492 : using value_type = buffers::const_buffer;
493 : using reference = buffers::const_buffer;
494 : using const_reference = buffers::const_buffer;
495 : using size_type = std::size_t;
496 : using difference_type = std::ptrdiff_t;
497 :
498 : const_buffers_type() = default;
499 : const_buffers_type(
500 : const_buffers_type const&) = default;
501 : const_buffers_type& operator=(
502 : const_buffers_type const&) = default;
503 :
504 : iterator
505 126 : begin() const noexcept
506 : {
507 126 : return p_;
508 : }
509 :
510 : iterator
511 126 : end() const noexcept
512 : {
513 126 : return p_ + n_;
514 : }
515 : };
516 :
517 : //------------------------------------------------
518 :
519 : template<
520 : class ConstBufferSequence,
521 : class>
522 : void
523 7 : serializer::
524 : start(
525 : message_view_base const& m,
526 : ConstBufferSequence&& body)
527 : {
528 7 : start_init(m);
529 : auto const& bs =
530 7 : ws_.emplace<ConstBufferSequence>(
531 : std::forward<ConstBufferSequence>(body));
532 7 : std::size_t n = std::distance(
533 : buffers::begin(bs),
534 : buffers::end(bs));
535 7 : buf_ = make_array(n);
536 7 : auto p = buf_.data();
537 14 : for(buffers::const_buffer b :
538 7 : buffers::range(bs))
539 7 : *p++ = b;
540 7 : start_buffers(m);
541 7 : }
542 :
543 : template<
544 : class Source,
545 : class... Args,
546 : class>
547 : Source&
548 8 : serializer::
549 : start(
550 : message_view_base const& m,
551 : Args&&... args)
552 : {
553 : static_assert(
554 : std::is_constructible<Source, Args...>::value ||
555 : std::is_constructible<Source, buffered_base::allocator&, Args...>::value,
556 : "The Source cannot be constructed with the given arguments");
557 :
558 8 : start_init(m);
559 8 : auto& src = construct_source<Source>(
560 : std::forward<Args>(args)...);
561 8 : start_source(m, std::addressof(src));
562 8 : return src;
563 : }
564 :
565 : //------------------------------------------------
566 :
567 : inline
568 : auto
569 33 : serializer::
570 : make_array(std::size_t n) ->
571 : detail::array_of_const_buffers
572 : {
573 : return {
574 33 : ws_.push_array(n,
575 33 : buffers::const_buffer{}),
576 33 : n };
577 : }
578 :
579 : } // http_proto
580 : } // boost
581 :
582 : #endif
|