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