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