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 1352 : is_done() const noexcept
194 : {
195 1352 : 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 25 : construct_source(Args&&... args)
239 : {
240 25 : return ws_.emplace<Source>(
241 25 : 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 22 : stream(
434 : serializer& sr) noexcept
435 22 : : sr_(&sr)
436 : {
437 22 : }
438 :
439 : serializer* sr_ = nullptr;
440 : };
441 :
442 : //---------------------------------------------------------
443 :
444 : class serializer::const_buf_gen_base
445 : {
446 : public:
447 : // Next non-empty buffer
448 : virtual
449 : buffers::const_buffer
450 : operator()() = 0;
451 :
452 : // Size of remaining buffers
453 : virtual
454 : std::size_t
455 : size() const = 0;
456 :
457 : // Count of remaining non-empty buffers
458 : virtual
459 : std::size_t
460 : count() const = 0;
461 :
462 : // Returns true when there is no buffer or
463 : // the remaining buffers are empty
464 : virtual
465 : bool
466 : is_empty() const = 0;
467 : };
468 :
469 : template<class ConstBufferSequence>
470 : class serializer::const_buf_gen
471 : : public const_buf_gen_base
472 : {
473 : using it_t = decltype(buffers::begin(
474 : std::declval<ConstBufferSequence>()));
475 :
476 : ConstBufferSequence cbs_;
477 : it_t current_;
478 : public:
479 : using const_buffer =
480 : buffers::const_buffer;
481 :
482 : explicit
483 24 : const_buf_gen(ConstBufferSequence cbs)
484 48 : : cbs_(std::move(cbs))
485 24 : , current_(buffers::begin(cbs_))
486 : {
487 24 : }
488 :
489 : const_buffer
490 433 : operator()() override
491 : {
492 433 : while(current_ != buffers::end(cbs_))
493 : {
494 431 : const_buffer buf = *current_++;
495 431 : if(buf.size() != 0)
496 431 : return buf;
497 : }
498 2 : return {};
499 : }
500 :
501 : std::size_t
502 1 : size() const override
503 : {
504 2 : return std::accumulate(
505 1 : current_,
506 1 : buffers::end(cbs_),
507 : std::size_t{},
508 23 : [](std::size_t sum, const_buffer cb) {
509 24 : return sum + cb.size(); });
510 : }
511 :
512 : std::size_t
513 24 : count() const override
514 : {
515 72 : return std::count_if(
516 24 : current_,
517 24 : buffers::end(cbs_),
518 458 : [](const_buffer cb) {
519 482 : return cb.size() != 0; });
520 : }
521 :
522 : bool
523 412 : is_empty() const override
524 : {
525 1236 : return std::all_of(
526 412 : current_,
527 412 : buffers::end(cbs_),
528 415 : [](const_buffer cb) {
529 827 : return cb.size() == 0; });
530 : }
531 : };
532 :
533 : //---------------------------------------------------------
534 :
535 : template<
536 : class ConstBufferSequence,
537 : class>
538 : void
539 24 : serializer::
540 : start(
541 : message_view_base const& m,
542 : ConstBufferSequence&& cbs)
543 : {
544 : static_assert(buffers::is_const_buffer_sequence<
545 : ConstBufferSequence>::value,
546 : "ConstBufferSequence type requirements not met");
547 :
548 24 : start_init(m);
549 48 : buf_gen_ = std::addressof(
550 : ws_.emplace<const_buf_gen<typename
551 24 : std::decay<ConstBufferSequence>::type>>(
552 : std::forward<ConstBufferSequence>(cbs)));
553 24 : start_buffers(m);
554 24 : }
555 :
556 : template<
557 : class Source,
558 : class... Args,
559 : class>
560 : Source&
561 25 : serializer::
562 : start(
563 : message_view_base const& m,
564 : Args&&... args)
565 : {
566 : static_assert(
567 : !std::is_abstract<Source>::value, "");
568 : static_assert(
569 : std::is_constructible<Source, Args...>::value ||
570 : std::is_constructible<Source, detail::workspace&, Args...>::value,
571 : "The Source cannot be constructed with the given arguments");
572 :
573 25 : start_init(m);
574 25 : auto& src = construct_source<Source>(
575 : std::forward<Args>(args)...);
576 25 : source_ = std::addressof(src);
577 25 : start_source(m);
578 25 : return src;
579 : }
580 :
581 : } // http_proto
582 : } // boost
583 :
584 : #endif
|