Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2024 Christian Mazakas
4 : // Copyright (c) 2024 Mohammad Nejati
5 : //
6 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 : //
9 : // Official repository: https://github.com/cppalliance/http_proto
10 : //
11 :
12 : #include <boost/http_proto/detail/except.hpp>
13 : #include <boost/http_proto/message_view_base.hpp>
14 : #include <boost/http_proto/serializer.hpp>
15 : #include <boost/http_proto/service/zlib_service.hpp>
16 :
17 : #include "src/detail/filter.hpp"
18 :
19 : #include <boost/buffers/copy.hpp>
20 : #include <boost/buffers/prefix.hpp>
21 : #include <boost/buffers/sans_prefix.hpp>
22 : #include <boost/buffers/sans_suffix.hpp>
23 : #include <boost/buffers/suffix.hpp>
24 : #include <boost/buffers/size.hpp>
25 : #include <boost/core/ignore_unused.hpp>
26 :
27 : #include <stddef.h>
28 :
29 : namespace boost {
30 : namespace http_proto {
31 :
32 : namespace {
33 :
34 : class deflator_filter
35 : : public http_proto::detail::filter
36 : {
37 : zlib::stream& deflator_;
38 :
39 : public:
40 73 : deflator_filter(
41 : context& ctx,
42 : http_proto::detail::workspace& ws,
43 : bool use_gzip)
44 292 : : deflator_{ ctx.get_service<zlib::service>()
45 73 : .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
46 : {
47 73 : }
48 :
49 : virtual filter::results
50 10525 : on_process(
51 : buffers::mutable_buffer out,
52 : buffers::const_buffer in,
53 : bool more) override
54 : {
55 10525 : auto flush =
56 10525 : more ? zlib::flush::none : zlib::flush::finish;
57 10525 : filter::results results;
58 :
59 : for(;;)
60 : {
61 17509 : auto params = zlib::params{in.data(), in.size(),
62 17509 : out.data(), out.size() };
63 17509 : auto ec = deflator_.write(params, flush);
64 :
65 17509 : results.in_bytes += in.size() - params.avail_in;
66 17509 : results.out_bytes += out.size() - params.avail_out;
67 :
68 17510 : if( ec.failed() &&
69 17510 : ec != zlib::error::buf_err)
70 : {
71 1 : results.ec = ec;
72 10525 : return results;
73 : }
74 :
75 17508 : if( ec == zlib::error::stream_end )
76 : {
77 72 : results.finished = true;
78 72 : return results;
79 : }
80 :
81 17436 : in = buffers::suffix(in, params.avail_in);
82 17436 : out = buffers::suffix(out, params.avail_out);
83 :
84 17436 : if( out.size() == 0 )
85 3404 : return results;
86 :
87 14032 : if( in.size() == 0 )
88 : {
89 14032 : if( results.out_bytes == 0 &&
90 : flush == zlib::flush::none )
91 : {
92 : // TODO: Is flush::block the right choice?
93 : // We might need a filter::flush() interface
94 : // so that the caller can decide when to flush.
95 6984 : flush = zlib::flush::block;
96 6984 : continue;
97 : }
98 7048 : return results;
99 : }
100 6984 : }
101 : }
102 : };
103 :
104 : //------------------------------------------------
105 :
106 : constexpr
107 : std::size_t
108 : crlf_len = 2;
109 :
110 : constexpr
111 : std::size_t
112 : chunk_header_len = 16 + crlf_len;
113 :
114 : constexpr
115 : std::size_t
116 : final_chunk_len = 1 + crlf_len + crlf_len;
117 :
118 : constexpr
119 : std::size_t
120 : chunked_overhead_ =
121 : chunk_header_len +
122 : crlf_len +
123 : final_chunk_len;
124 :
125 : template<class MutableBufferSequence>
126 : void
127 4519 : write_chunk_header(
128 : const MutableBufferSequence& mbs,
129 : std::size_t size) noexcept
130 : {
131 : static constexpr char hexdig[] =
132 : "0123456789ABCDEF";
133 : char buf[18];
134 4519 : auto p = buf + 16;
135 76823 : for(std::size_t i = 16; i--;)
136 : {
137 72304 : *--p = hexdig[size & 0xf];
138 72304 : size >>= 4;
139 : }
140 4519 : buf[16] = '\r';
141 4519 : buf[17] = '\n';
142 4519 : auto n = buffers::copy(
143 : mbs,
144 9038 : buffers::const_buffer(
145 : buf, sizeof(buf)));
146 : ignore_unused(n);
147 4519 : BOOST_ASSERT(n == 18);
148 4519 : }
149 :
150 : template<class MutableBufferSequence>
151 : void
152 4519 : write_crlf(
153 : const MutableBufferSequence& mbs) noexcept
154 : {
155 4519 : auto n = buffers::copy(
156 : mbs,
157 9038 : buffers::const_buffer(
158 : "\r\n", 2));
159 : ignore_unused(n);
160 4519 : BOOST_ASSERT(n == 2);
161 4519 : }
162 :
163 : template<class MutableBufferSequence>
164 : void
165 43 : write_final_chunk(
166 : const MutableBufferSequence& mbs) noexcept
167 : {
168 43 : auto n = buffers::copy(
169 : mbs,
170 86 : buffers::const_buffer(
171 : "0\r\n\r\n", 5));
172 : ignore_unused(n);
173 43 : BOOST_ASSERT(n == 5);
174 43 : }
175 :
176 : //------------------------------------------------
177 :
178 : class appender
179 : {
180 : buffers::circular_buffer& cb_;
181 : buffers::mutable_buffer_pair mbp_;
182 : std::size_t n_ = 0;
183 : bool is_chunked_ = false;
184 : bool more_input_ = true;
185 :
186 : public:
187 8975 : appender(
188 : buffers::circular_buffer& cb,
189 : bool is_chunked)
190 8975 : : cb_(cb)
191 8975 : , mbp_(cb.prepare(cb.capacity()))
192 8975 : , is_chunked_(is_chunked)
193 : {
194 8975 : }
195 :
196 : bool
197 13979 : is_full() const noexcept
198 : {
199 13979 : auto remaining = cb_.capacity() - n_;
200 13979 : if(is_chunked_)
201 7024 : return remaining <= chunked_overhead_;
202 :
203 6955 : return remaining == 0;
204 : }
205 :
206 : buffers::mutable_buffer_pair
207 10527 : prepare() noexcept
208 : {
209 10527 : if(is_chunked_)
210 : {
211 5280 : return buffers::sans_suffix(
212 5280 : buffers::sans_prefix(
213 5280 : mbp_,
214 5280 : chunk_header_len + n_)
215 5280 : , final_chunk_len + crlf_len);
216 : }
217 5247 : return buffers::sans_prefix(mbp_, n_);
218 : }
219 :
220 : void
221 10525 : commit(std::size_t n, bool more) noexcept
222 : {
223 10525 : BOOST_ASSERT(more_input_);
224 10525 : n_ += n;
225 10525 : more_input_ = more;
226 10525 : }
227 :
228 8975 : ~appender()
229 : {
230 8975 : if(is_chunked_)
231 : {
232 4504 : if(n_)
233 : {
234 4503 : write_chunk_header(mbp_, n_);
235 4503 : cb_.commit(n_ + chunk_header_len);
236 :
237 4503 : write_crlf(
238 4503 : cb_.prepare(crlf_len));
239 4503 : cb_.commit(crlf_len);
240 : }
241 :
242 4504 : if(!more_input_)
243 : {
244 38 : write_final_chunk(
245 38 : cb_.prepare(final_chunk_len));
246 38 : cb_.commit(final_chunk_len);
247 : }
248 : }
249 : else // is_chunked_ == false
250 : {
251 4471 : cb_.commit(n_);
252 : }
253 8975 : }
254 : };
255 :
256 : } // namespace
257 :
258 : //------------------------------------------------
259 :
260 57 : serializer::
261 : ~serializer()
262 : {
263 57 : }
264 :
265 0 : serializer::
266 : serializer(
267 : serializer&&) noexcept = default;
268 :
269 11 : serializer::
270 : serializer(
271 11 : context& ctx)
272 11 : : serializer(ctx, 65536)
273 : {
274 11 : }
275 :
276 57 : serializer::
277 : serializer(
278 : context& ctx,
279 57 : std::size_t buffer_size)
280 57 : : ctx_(ctx)
281 57 : , ws_(buffer_size)
282 : {
283 57 : }
284 :
285 : void
286 179 : serializer::
287 : reset() noexcept
288 : {
289 179 : ws_.clear();
290 179 : filter_ = nullptr;
291 179 : is_done_ = false;
292 179 : is_header_done_ = false;
293 179 : filter_done_ = false;
294 179 : }
295 :
296 : //------------------------------------------------
297 :
298 : auto
299 9042 : serializer::
300 : prepare() ->
301 : system::result<const_buffers_type>
302 : {
303 : // Precondition violation
304 9042 : if(is_done_)
305 1 : detail::throw_logic_error();
306 :
307 : // Expect: 100-continue
308 9041 : if(needs_exp100_continue_)
309 : {
310 4 : if(!is_header_done_)
311 4 : return const_buffers_type(
312 2 : prepped_.begin(),
313 2 : 1); // limit to header
314 :
315 2 : needs_exp100_continue_ = false;
316 :
317 2 : BOOST_HTTP_PROTO_RETURN_EC(
318 : error::expect_100_continue);
319 : }
320 :
321 9037 : if(!filter_)
322 : {
323 80 : switch(st_)
324 : {
325 3 : case style::empty:
326 6 : return const_buffers_type(
327 3 : prepped_.begin(),
328 6 : prepped_.size());
329 :
330 19 : case style::buffers:
331 : // add more buffers if prepped_ is half empty.
332 29 : if(more_input_ &&
333 10 : prepped_.capacity() >= prepped_.size())
334 : {
335 2 : prepped_.slide_to_front();
336 15 : while(prepped_.capacity() != 0)
337 : {
338 15 : auto buf = buf_gen_->next();
339 15 : if(buf.size() != 0)
340 : {
341 13 : prepped_.append(buf);
342 : }
343 : else // buf_gen_ is empty
344 : {
345 : // append crlf and final chunk
346 2 : if(is_chunked_)
347 : {
348 1 : prepped_.append(tmp_);
349 1 : more_input_ = false;
350 : }
351 2 : break;
352 : }
353 : }
354 2 : if(buf_gen_->is_empty() && !is_chunked_)
355 1 : more_input_ = false;
356 : }
357 38 : return const_buffers_type(
358 19 : prepped_.begin(),
359 38 : prepped_.size());
360 :
361 23 : case style::source:
362 : {
363 23 : if(!more_input_)
364 22 : break;
365 :
366 : // handles chunked payloads automatically
367 18 : appender apndr(cb0_, is_chunked_);
368 :
369 18 : if(apndr.is_full())
370 0 : break;
371 :
372 18 : const auto rs = source_->read(
373 18 : apndr.prepare());
374 :
375 18 : if(rs.ec.failed())
376 : {
377 1 : is_done_ = true;
378 1 : return rs.ec;
379 : }
380 :
381 17 : if(rs.finished)
382 6 : more_input_ = false;
383 :
384 17 : apndr.commit(rs.bytes, more_input_);
385 17 : break;
386 18 : }
387 :
388 35 : case style::stream:
389 35 : if(is_header_done_ && cb0_.size() == 0)
390 1 : BOOST_HTTP_PROTO_RETURN_EC(
391 : error::need_data);
392 34 : break;
393 : }
394 : }
395 : else // filter
396 : {
397 8957 : switch(st_)
398 : {
399 0 : case style::empty:
400 0 : return const_buffers_type(
401 0 : prepped_.begin(),
402 0 : prepped_.size());
403 :
404 1153 : case style::buffers:
405 : {
406 1153 : appender apndr(cb0_, is_chunked_);
407 2521 : while(!apndr.is_full() && !filter_done_)
408 : {
409 1369 : if(more_input_ && tmp_.size() == 0)
410 : {
411 401 : tmp_ = buf_gen_->next();
412 401 : if(tmp_.size() == 0) // buf_gen_ is empty
413 16 : more_input_ = false;
414 : }
415 :
416 1369 : const auto rs = filter_->process(
417 0 : apndr.prepare(),
418 1369 : tmp_,
419 1369 : more_input_);
420 :
421 1369 : if(rs.ec.failed())
422 : {
423 1 : is_done_ = true;
424 1 : return rs.ec;
425 : }
426 :
427 1368 : tmp_ = buffers::sans_prefix(tmp_, rs.in_bytes);
428 1368 : apndr.commit(rs.out_bytes, !rs.finished);
429 :
430 1368 : if(rs.finished)
431 24 : filter_done_ = true;
432 : }
433 1152 : break;
434 1153 : }
435 :
436 2300 : case style::source:
437 : {
438 2300 : appender apndr(cb0_, is_chunked_);
439 5936 : while(!apndr.is_full() && !filter_done_)
440 : {
441 3636 : if(more_input_ && cb1_.capacity() != 0)
442 : {
443 2696 : const auto rs = source_->read(
444 2696 : cb1_.prepare(cb1_.capacity()));
445 2696 : if(rs.ec.failed())
446 : {
447 0 : is_done_ = true;
448 0 : return rs.ec;
449 : }
450 2696 : if(rs.finished)
451 24 : more_input_ = false;
452 2696 : cb1_.commit(rs.bytes);
453 : }
454 :
455 3636 : const auto rs = filter_->process(
456 3636 : apndr.prepare(),
457 0 : cb1_.data(),
458 3636 : more_input_);
459 :
460 3636 : if(rs.ec.failed())
461 : {
462 0 : is_done_ = true;
463 0 : return rs.ec;
464 : }
465 :
466 3636 : cb1_.consume(rs.in_bytes);
467 3636 : apndr.commit(rs.out_bytes, !rs.finished);
468 :
469 3636 : if(rs.finished)
470 24 : filter_done_ = true;
471 : }
472 2300 : break;
473 2300 : }
474 :
475 5504 : case style::stream:
476 : {
477 5504 : appender apndr(cb0_, is_chunked_);
478 :
479 5504 : if(apndr.is_full() || filter_done_)
480 0 : break;
481 :
482 : // The stream object is expected to
483 : // have already populated cb1_
484 5504 : if(more_input_ && cb1_.size() == 0)
485 : {
486 0 : if(!prepped_.empty())
487 0 : break;
488 :
489 0 : BOOST_HTTP_PROTO_RETURN_EC(
490 : error::need_data);
491 : }
492 :
493 5504 : const auto rs = filter_->process(
494 5504 : apndr.prepare(),
495 0 : cb1_.data(),
496 5504 : more_input_);
497 :
498 5504 : if(rs.ec.failed())
499 : {
500 0 : is_done_ = true;
501 0 : return rs.ec;
502 : }
503 :
504 5504 : cb1_.consume(rs.in_bytes);
505 5504 : apndr.commit(rs.out_bytes, !rs.finished);
506 :
507 5504 : if(rs.finished)
508 24 : filter_done_ = true;
509 :
510 5504 : break;
511 5504 : }
512 : }
513 : }
514 :
515 9012 : prepped_.reset(!is_header_done_);
516 9012 : const auto cbp = cb0_.data();
517 9012 : if(cbp[0].size() != 0)
518 9009 : prepped_.append(cbp[0]);
519 9012 : if(cbp[1].size() != 0)
520 14 : prepped_.append(cbp[1]);
521 :
522 9012 : BOOST_ASSERT(
523 : buffers::size(prepped_) > 0);
524 :
525 18024 : return const_buffers_type(
526 9012 : prepped_.begin(),
527 18024 : prepped_.size());
528 : }
529 :
530 : void
531 10775 : serializer::
532 : consume(
533 : std::size_t n)
534 : {
535 : // Precondition violation
536 10775 : if(is_done_ && n != 0)
537 1 : detail::throw_logic_error();
538 :
539 10774 : if(!is_header_done_)
540 : {
541 : const auto header_remain =
542 100 : prepped_[0].size();
543 100 : if(n < header_remain)
544 : {
545 11 : prepped_.consume(n);
546 11 : return;
547 : }
548 89 : n -= header_remain;
549 89 : prepped_.consume(header_remain);
550 89 : is_header_done_ = true;
551 : }
552 :
553 10763 : prepped_.consume(n);
554 :
555 : // no-op when cb0_ is not in use
556 10763 : cb0_.consume(n);
557 :
558 10763 : if(!prepped_.empty())
559 1760 : return;
560 :
561 9003 : if(needs_exp100_continue_)
562 1 : return;
563 :
564 9002 : if(more_input_)
565 8897 : return;
566 :
567 105 : if(filter_ && !filter_done_)
568 16 : return;
569 :
570 89 : is_done_ = true;
571 : }
572 :
573 : //------------------------------------------------
574 :
575 : detail::array_of_const_buffers
576 99 : serializer::
577 : make_array(std::size_t n)
578 : {
579 99 : if(n > std::numeric_limits<std::uint16_t>::max())
580 0 : detail::throw_length_error();
581 :
582 : return {
583 99 : ws_.push_array(n,
584 0 : buffers::const_buffer{}),
585 99 : static_cast<std::uint16_t>(n) };
586 : }
587 :
588 : void
589 99 : serializer::
590 : start_init(
591 : message_view_base const& m)
592 : {
593 99 : reset();
594 :
595 : // VFALCO what do we do with
596 : // metadata error code failures?
597 : // m.ph_->md.maybe_throw();
598 :
599 99 : auto const& md = m.metadata();
600 99 : needs_exp100_continue_ = md.expect.is_100_continue;
601 :
602 : // Transfer-Encoding
603 99 : is_chunked_ = md.transfer_encoding.is_chunked;
604 :
605 : // Content-Encoding
606 99 : auto const& ce = md.content_encoding;
607 99 : if(ce.encoding == encoding::deflate)
608 : {
609 36 : filter_ = &ws_.emplace<
610 36 : deflator_filter>(ctx_, ws_, false);
611 : }
612 63 : else if(ce.encoding == encoding::gzip)
613 : {
614 37 : filter_ = &ws_.emplace<
615 37 : deflator_filter>(ctx_, ws_, true);
616 : }
617 99 : }
618 :
619 : void
620 4 : serializer::
621 : start_empty(
622 : message_view_base const& m)
623 : {
624 : using mutable_buffer =
625 : buffers::mutable_buffer;
626 :
627 4 : start_init(m);
628 4 : st_ = style::empty;
629 :
630 4 : if(!is_chunked_)
631 : {
632 3 : prepped_ = make_array(
633 : 1); // header
634 : }
635 : else
636 : {
637 1 : prepped_ = make_array(
638 : 1 + // header
639 : 1); // final chunk
640 :
641 : mutable_buffer final_chunk = {
642 1 : ws_.reserve_front(
643 : final_chunk_len),
644 1 : final_chunk_len };
645 1 : write_final_chunk(final_chunk);
646 :
647 1 : prepped_[1] = final_chunk;
648 : }
649 :
650 4 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
651 4 : more_input_ = false;
652 4 : }
653 :
654 : void
655 32 : serializer::
656 : start_buffers(
657 : message_view_base const& m)
658 : {
659 : using mutable_buffer =
660 : buffers::mutable_buffer;
661 :
662 : // start_init() already called
663 32 : st_ = style::buffers;
664 :
665 32 : const auto buffers_max = (std::min)(
666 64 : std::size_t{ 16 },
667 32 : buf_gen_->count());
668 :
669 32 : if(!filter_)
670 : {
671 7 : if(!is_chunked_)
672 : {
673 : // no filter and no chunked
674 :
675 6 : prepped_ = make_array(
676 : 1 + // header
677 : buffers_max ); // buffers
678 :
679 6 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
680 12 : std::generate(
681 6 : prepped_.begin() + 1,
682 : prepped_.end(),
683 16 : [this](){ return buf_gen_->next(); });
684 6 : more_input_ = !buf_gen_->is_empty();
685 6 : return;
686 : }
687 :
688 : // no filter and chunked
689 :
690 1 : if(buf_gen_->is_empty())
691 : {
692 0 : prepped_ = make_array(
693 : 1 + // header
694 : 1); // final chunk
695 :
696 : mutable_buffer final_chunk = {
697 0 : ws_.reserve_front(
698 : final_chunk_len),
699 0 : final_chunk_len };
700 0 : write_final_chunk(
701 : final_chunk);
702 :
703 0 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
704 0 : prepped_[1] = final_chunk;
705 0 : more_input_ = false;
706 0 : return;
707 : }
708 :
709 : // Write entire buffers as a single chunk
710 : // since total size is known
711 :
712 : mutable_buffer chunk_header = {
713 1 : ws_.reserve_front(
714 : chunk_header_len),
715 1 : chunk_header_len };
716 :
717 1 : write_chunk_header(
718 : chunk_header,
719 1 : buf_gen_->size());
720 :
721 : mutable_buffer crlf_and_final_chunk = {
722 1 : ws_.reserve_front(
723 : crlf_len + final_chunk_len),
724 1 : crlf_len + final_chunk_len };
725 :
726 1 : write_crlf(
727 1 : buffers::prefix(
728 : crlf_and_final_chunk,
729 : crlf_len));
730 :
731 1 : write_final_chunk(
732 1 : buffers::sans_prefix(
733 : crlf_and_final_chunk,
734 : crlf_len));
735 :
736 1 : prepped_ = make_array(
737 : 1 + // header
738 : 1 + // chunk header
739 : buffers_max + // buffers
740 : 1); // buffer or (crlf and final chunk)
741 :
742 1 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
743 1 : prepped_[1] = chunk_header;
744 1 : std::generate(
745 1 : prepped_.begin() + 2,
746 1 : prepped_.end() - 1,
747 16 : [this](){ return buf_gen_->next(); });
748 :
749 1 : more_input_ = !buf_gen_->is_empty();
750 : // assigning the last slot
751 1 : if(more_input_)
752 : {
753 1 : prepped_[prepped_.size() - 1] =
754 2 : buf_gen_->next();
755 :
756 : // deferred until buf_gen_ is drained
757 1 : tmp_ = crlf_and_final_chunk;
758 : }
759 : else
760 : {
761 0 : prepped_[prepped_.size() - 1] =
762 : crlf_and_final_chunk;
763 : }
764 1 : return;
765 : }
766 :
767 : // filter
768 :
769 25 : prepped_ = make_array(
770 : 1 + // header
771 : 2); // circular buffer
772 :
773 25 : const auto n = ws_.size() - 1;
774 25 : cb0_ = { ws_.reserve_front(n), n };
775 :
776 25 : if(is_chunked_)
777 : {
778 12 : if(cb0_.capacity() <= chunked_overhead_)
779 0 : detail::throw_length_error();
780 : }
781 : else
782 : {
783 13 : if(cb0_.capacity() == 0)
784 0 : detail::throw_length_error();
785 : }
786 :
787 25 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
788 25 : tmp_ = {};
789 25 : more_input_ = !buf_gen_->is_empty();
790 : }
791 :
792 : void
793 33 : serializer::
794 : start_source(
795 : message_view_base const& m)
796 : {
797 : // start_init() already called
798 33 : st_ = style::source;
799 :
800 33 : prepped_ = make_array(
801 : 1 + // header
802 : 2); // circular buffer
803 :
804 33 : if(filter_)
805 : {
806 : // TODO: Optimize buffer distribution
807 24 : const auto n = (ws_.size() - 1) / 2;
808 24 : cb0_ = { ws_.reserve_front(n), n };
809 24 : cb1_ = { ws_.reserve_front(n), n };
810 : }
811 : else
812 : {
813 9 : const auto n = ws_.size() - 1;
814 9 : cb0_ = { ws_.reserve_front(n), n };
815 : }
816 :
817 33 : if(is_chunked_)
818 : {
819 14 : if(cb0_.capacity() <= chunked_overhead_)
820 0 : detail::throw_length_error();
821 : }
822 : else
823 : {
824 19 : if(cb0_.capacity() == 0)
825 0 : detail::throw_length_error();
826 : }
827 :
828 33 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
829 33 : more_input_ = true;
830 33 : }
831 :
832 : auto
833 30 : serializer::
834 : start_stream(
835 : message_view_base const& m) ->
836 : stream
837 : {
838 30 : start_init(m);
839 30 : st_ = style::stream;
840 :
841 30 : prepped_ = make_array(
842 : 1 + // header
843 : 2); // circular buffer
844 :
845 30 : if(filter_)
846 : {
847 : // TODO: Optimize buffer distribution
848 24 : const auto n = (ws_.size() - 1) / 2;
849 24 : cb0_ = { ws_.reserve_front(n), n };
850 24 : cb1_ = { ws_.reserve_front(n), n };
851 : }
852 : else
853 : {
854 6 : const auto n = ws_.size() - 1;
855 6 : cb0_ = { ws_.reserve_front(n), n };
856 : }
857 :
858 30 : if(is_chunked_)
859 : {
860 15 : if(cb0_.capacity() <= chunked_overhead_)
861 0 : detail::throw_length_error();
862 : }
863 : else
864 : {
865 15 : if(cb0_.capacity() == 0)
866 0 : detail::throw_length_error();
867 : }
868 :
869 30 : prepped_[0] = { m.ph_->cbuf, m.ph_->size };
870 30 : more_input_ = true;
871 30 : return stream{ *this };
872 : }
873 :
874 : //------------------------------------------------
875 :
876 : std::size_t
877 69 : serializer::
878 : stream::
879 : capacity() const noexcept
880 : {
881 69 : if(sr_->filter_)
882 0 : return sr_->cb1_.capacity();
883 :
884 69 : if(!sr_->is_chunked_)
885 34 : return sr_->cb0_.capacity();
886 :
887 : // chunked with no filter
888 35 : const auto cap = sr_->cb0_.capacity();
889 35 : if(cap > chunked_overhead_)
890 21 : return cap - chunked_overhead_;
891 :
892 14 : return 0;
893 : }
894 :
895 : bool
896 61 : serializer::
897 : stream::
898 : is_full() const noexcept
899 : {
900 61 : return capacity() == 0;
901 : }
902 :
903 : auto
904 5511 : serializer::
905 : stream::
906 : prepare() const ->
907 : buffers_type
908 : {
909 5511 : if(sr_->filter_)
910 5480 : return sr_->cb1_.prepare(
911 10960 : sr_->cb1_.capacity());
912 :
913 31 : if(!sr_->is_chunked_)
914 15 : return sr_->cb0_.prepare(
915 30 : sr_->cb0_.capacity());
916 :
917 : // chunked with no filter
918 16 : const auto cap = sr_->cb0_.capacity();
919 16 : if(cap <= chunked_overhead_)
920 0 : detail::throw_length_error();
921 :
922 16 : return buffers::sans_prefix(
923 32 : sr_->cb0_.prepare(
924 : cap - crlf_len - final_chunk_len),
925 16 : chunk_header_len);
926 : }
927 :
928 : void
929 5511 : serializer::
930 : stream::
931 : commit(std::size_t n) const
932 : {
933 5511 : if(sr_->filter_)
934 5480 : return sr_->cb1_.commit(n);
935 :
936 31 : if(!sr_->is_chunked_)
937 15 : return sr_->cb0_.commit(n);
938 :
939 : // chunked with no filter
940 16 : if(n != 0)
941 : {
942 15 : write_chunk_header(
943 15 : sr_->cb0_.prepare(
944 : chunk_header_len),
945 : n);
946 15 : sr_->cb0_.commit(
947 : chunk_header_len);
948 :
949 15 : sr_->cb0_.prepare(n);
950 15 : sr_->cb0_.commit(n);
951 :
952 15 : write_crlf(
953 15 : sr_->cb0_.prepare(crlf_len));
954 15 : sr_->cb0_.commit(crlf_len);
955 : }
956 : }
957 :
958 : void
959 33 : serializer::
960 : stream::
961 : close() const
962 : {
963 : // Precondition violation
964 33 : if(!sr_->more_input_)
965 4 : detail::throw_logic_error();
966 :
967 29 : sr_->more_input_ = false;
968 :
969 29 : if(sr_->filter_)
970 24 : return;
971 :
972 5 : if(!sr_->is_chunked_)
973 2 : return;
974 :
975 : // chunked with no filter
976 3 : write_final_chunk(
977 3 : sr_->cb0_.prepare(
978 : final_chunk_len));
979 3 : sr_->cb0_.commit(final_chunk_len);
980 : }
981 :
982 : //------------------------------------------------
983 :
984 : } // http_proto
985 : } // boost
|