Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2024 Christian Mazakas
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/http_proto
9 : //
10 :
11 : #include <boost/http_proto/serializer.hpp>
12 : #include <boost/http_proto/message_view_base.hpp>
13 : #include <boost/http_proto/filter.hpp>
14 : #include <boost/http_proto/service/zlib_service.hpp>
15 : #include <boost/http_proto/detail/except.hpp>
16 : #include <boost/buffers/algorithm.hpp>
17 : #include <boost/buffers/buffer_copy.hpp>
18 : #include <boost/buffers/buffer_size.hpp>
19 : #include <boost/core/ignore_unused.hpp>
20 : #include <stddef.h>
21 : #include <iostream>
22 :
23 : namespace boost {
24 : namespace http_proto {
25 :
26 : //------------------------------------------------
27 :
28 : void
29 0 : consume_buffers(
30 : buffers::const_buffer*& p,
31 : std::size_t& n,
32 : std::size_t bytes)
33 : {
34 0 : while(n > 0)
35 : {
36 0 : if(bytes < p->size())
37 : {
38 0 : *p += bytes;
39 0 : return;
40 : }
41 0 : bytes -= p->size();
42 0 : ++p;
43 0 : --n;
44 : }
45 :
46 : // Precondition violation
47 0 : if(bytes > 0)
48 0 : detail::throw_invalid_argument();
49 : }
50 :
51 : template<class MutableBuffers>
52 : void
53 27 : write_chunk_header(
54 : MutableBuffers const& dest0,
55 : std::size_t size) noexcept
56 : {
57 : static constexpr char hexdig[] =
58 : "0123456789ABCDEF";
59 : char buf[18];
60 27 : auto p = buf + 16;
61 459 : for(std::size_t i = 16; i--;)
62 : {
63 432 : *--p = hexdig[size & 0xf];
64 432 : size >>= 4;
65 : }
66 27 : buf[16] = '\r';
67 27 : buf[17] = '\n';
68 27 : auto n = buffers::buffer_copy(
69 : dest0,
70 54 : buffers::const_buffer(
71 : buf, sizeof(buf)));
72 : ignore_unused(n);
73 27 : BOOST_ASSERT(n == 18);
74 27 : BOOST_ASSERT(
75 : buffers::buffer_size(dest0) == n);
76 27 : }
77 :
78 : template<class DynamicBuffer>
79 : void
80 19 : write_chunk_close(DynamicBuffer& db)
81 : {
82 19 : db.commit(
83 : buffers::buffer_copy(
84 19 : db.prepare(2),
85 19 : buffers::const_buffer("\r\n", 2)));
86 19 : }
87 :
88 : template<class DynamicBuffer>
89 : void
90 3 : write_last_chunk(DynamicBuffer& db)
91 : {
92 3 : db.commit(
93 : buffers::buffer_copy(
94 3 : db.prepare(5),
95 3 : buffers::const_buffer("0\r\n\r\n", 5)));
96 3 : }
97 :
98 : //------------------------------------------------
99 :
100 20 : serializer::
101 : ~serializer()
102 : {
103 20 : }
104 :
105 9 : serializer::
106 9 : serializer()
107 9 : : serializer(65536)
108 : {
109 9 : }
110 :
111 0 : serializer::
112 : serializer(
113 : serializer&&) noexcept = default;
114 :
115 20 : serializer::
116 : serializer(
117 20 : std::size_t buffer_size)
118 20 : : ws_(buffer_size)
119 : {
120 20 : }
121 :
122 0 : serializer::
123 : serializer(
124 : context& ctx,
125 0 : std::size_t buffer_size)
126 0 : : ws_(buffer_size)
127 0 : , ctx_(&ctx)
128 : {
129 0 : if( ctx_->has_service<zlib::deflate_decoder_service>() )
130 : {
131 0 : filter_ws_.allocate(1024);
132 0 : tmp1_ = { filter_ws_.data(), filter_ws_.size() };
133 : }
134 0 : }
135 :
136 : void
137 0 : serializer::
138 : reset() noexcept
139 : {
140 0 : }
141 :
142 : //------------------------------------------------
143 :
144 : auto
145 67 : serializer::
146 : prepare() ->
147 : system::result<
148 : const_buffers_type>
149 : {
150 : // Precondition violation
151 67 : if(is_done_)
152 1 : detail::throw_logic_error();
153 :
154 : // Expect: 100-continue
155 66 : if(is_expect_continue_)
156 : {
157 4 : if(out_.data() == hp_)
158 2 : return const_buffers_type(hp_, 1);
159 2 : is_expect_continue_ = false;
160 2 : BOOST_HTTP_PROTO_RETURN_EC(
161 : error::expect_100_continue);
162 : }
163 :
164 62 : if( is_compressed_ )
165 : {
166 0 : auto on_end = [&]
167 : {
168 0 : std::size_t n = 0;
169 0 : if(out_.data() == hp_)
170 0 : ++n;
171 :
172 0 : for(buffers::const_buffer const& b : tmp0_.data())
173 0 : out_[n++] = b;
174 :
175 : auto cbs = const_buffers_type(
176 0 : out_.data(), out_.size());
177 :
178 0 : BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
179 :
180 0 : return cbs;
181 0 : };
182 :
183 0 : if(! zlib_filter_ )
184 : {
185 : auto& svc =
186 0 : ctx_->get_service<
187 0 : zlib::deflate_decoder_service>();
188 :
189 0 : zlib_filter_ = &svc.make_filter(filter_ws_);
190 0 : tmp1_ =
191 0 : { filter_ws_.data(), filter_ws_.size() };
192 : }
193 :
194 0 : auto& zbuf = tmp1_;
195 :
196 0 : if( st_ == style::source )
197 : {
198 0 : auto results = src_->read(
199 0 : zbuf.prepare(zbuf.capacity()));
200 0 : more_ = !results.finished;
201 0 : zbuf.commit(results.bytes);
202 : }
203 :
204 0 : buffers::mutable_buffer chunk_header;
205 0 : if( is_chunked_ )
206 : {
207 0 : auto mbs = tmp0_.prepare(detail::chunk_header_len_);
208 0 : auto buf = *mbs.begin();
209 0 : if( buf.size() == 0 )
210 : {
211 0 : auto p = mbs.begin();
212 0 : ++p;
213 0 : buf = *p;
214 : }
215 :
216 0 : if( buf.size() < detail::chunk_header_len_ )
217 0 : detail::throw_length_error();
218 :
219 0 : std::memset(
220 : buf.data(), 0x00, detail::chunk_header_len_);
221 0 : tmp0_.commit(detail::chunk_header_len_);
222 :
223 0 : chunk_header = buf;
224 : }
225 :
226 0 : auto get_output = [&]() -> buffers::mutable_buffer
227 : {
228 0 : auto mbs = tmp0_.prepare(tmp0_.capacity());
229 0 : auto buf = *mbs.begin();
230 0 : if( buf.size() == 0 )
231 : {
232 0 : auto p = mbs.begin();
233 0 : ++p;
234 0 : buf = *p;
235 : }
236 :
237 0 : if( is_chunked_ )
238 : {
239 0 : if( buf.size() <
240 : detail::crlf_len_ + detail::last_chunk_len_ + 1 )
241 0 : return {};
242 :
243 : auto n =
244 0 : buf.size() -
245 : detail::crlf_len_ -
246 0 : detail::last_chunk_len_;
247 :
248 0 : buf = { buf.data(), n };
249 : }
250 :
251 : // BOOST_ASSERT(buf.size() > 0);
252 0 : return buf;
253 0 : };
254 :
255 0 : auto get_input = [&]() -> buffers::const_buffer
256 : {
257 0 : if( st_ == style::buffers )
258 : {
259 0 : if( buffers::buffer_size(buf_) == 0 )
260 0 : return {};
261 :
262 0 : auto buf = *(buf_.data());
263 0 : BOOST_ASSERT(buf.size() > 0);
264 0 : return buf;
265 : }
266 : else
267 : {
268 0 : if( zbuf.size() == 0 )
269 0 : return {};
270 :
271 0 : auto cbs = zbuf.data();
272 0 : auto buf = *cbs.begin();
273 0 : if( buf.size() == 0 )
274 : {
275 0 : auto p = cbs.begin();
276 0 : ++p;
277 0 : buf = *p;
278 : }
279 0 : BOOST_ASSERT(buf.size() > 0);
280 0 : return buf;
281 : }
282 0 : };
283 :
284 0 : std::size_t num_written = 0;
285 : while( true )
286 : {
287 0 : auto in = get_input();
288 0 : auto out = get_output();
289 0 : if( out.size() < 6 + 1 )
290 : {
291 0 : BOOST_ASSERT(tmp0_.size() > 0);
292 0 : break;
293 : }
294 :
295 0 : auto results = zlib_filter_->on_process(
296 0 : out, in, more_);
297 :
298 0 : if( results.finished )
299 0 : filter_done_ = true;
300 :
301 0 : if( st_ == style::buffers )
302 : {
303 0 : buf_.consume(results.in_bytes);
304 0 : if( buffers::buffer_size(buf_) == 0 )
305 0 : more_ = false;
306 : }
307 : else
308 0 : zbuf.consume(results.in_bytes);
309 :
310 0 : if( results.out_bytes == 0 )
311 0 : break;
312 :
313 0 : BOOST_ASSERT(results.out_bytes > 0);
314 :
315 0 : num_written += results.out_bytes;
316 0 : tmp0_.commit(results.out_bytes);
317 0 : }
318 :
319 0 : BOOST_ASSERT(tmp0_.size() > 0);
320 0 : if( is_chunked_ )
321 : {
322 0 : write_chunk_header(
323 : chunk_header, num_written);
324 :
325 0 : buffers::buffer_copy(
326 0 : tmp0_.prepare(2),
327 0 : buffers::const_buffer("\r\n", 2));
328 0 : tmp0_.commit(2);
329 :
330 0 : if( filter_done_ )
331 : {
332 0 : buffers::buffer_copy(
333 0 : tmp0_.prepare(5),
334 0 : buffers::const_buffer(
335 : "0\r\n\r\n", 5));
336 0 : tmp0_.commit(5);
337 : }
338 : }
339 0 : return on_end();
340 : }
341 :
342 62 : if(st_ == style::empty)
343 : {
344 9 : return const_buffers_type(
345 3 : out_.data(),
346 3 : out_.size());
347 : }
348 :
349 59 : if(st_ == style::buffers)
350 : {
351 9 : return const_buffers_type(
352 3 : out_.data(),
353 3 : out_.size());
354 : }
355 :
356 56 : if(st_ == style::source)
357 : {
358 22 : if(more_)
359 : {
360 17 : if(! is_chunked_)
361 : {
362 9 : auto rv = src_->read(
363 9 : tmp0_.prepare(tmp0_.capacity()));
364 9 : tmp0_.commit(rv.bytes);
365 9 : if(rv.ec.failed())
366 0 : return rv.ec;
367 9 : more_ = ! rv.finished;
368 : }
369 : else
370 : {
371 8 : if(tmp0_.capacity() > detail::chunked_overhead_)
372 : {
373 8 : auto dest = tmp0_.prepare(
374 8 : tmp0_.capacity() -
375 : 2 - // CRLF
376 : 5); // final chunk
377 :
378 8 : auto rv = src_->read(
379 8 : buffers::sans_prefix(dest, 18));
380 :
381 8 : if(rv.ec.failed())
382 0 : return rv.ec;
383 :
384 8 : if(rv.bytes != 0)
385 : {
386 7 : write_chunk_header(
387 7 : buffers::prefix(dest, 18), rv.bytes);
388 7 : tmp0_.commit(rv.bytes + 18);
389 : // terminate chunk
390 7 : tmp0_.commit(
391 : buffers::buffer_copy(
392 7 : tmp0_.prepare(2),
393 14 : buffers::const_buffer(
394 : "\r\n", 2)));
395 : }
396 :
397 8 : if(rv.finished)
398 : {
399 2 : tmp0_.commit(
400 : buffers::buffer_copy(
401 2 : tmp0_.prepare(5),
402 2 : buffers::const_buffer(
403 : "0\r\n\r\n", 5)));
404 2 : more_ = false;
405 : }
406 : }
407 : }
408 : }
409 :
410 22 : std::size_t n = 0;
411 22 : if(out_.data() == hp_)
412 5 : ++n;
413 66 : for(buffers::const_buffer const& b : tmp0_.data())
414 44 : out_[n++] = b;
415 :
416 66 : return const_buffers_type(
417 22 : out_.data(),
418 22 : out_.size());
419 : }
420 :
421 34 : if(st_ == style::stream)
422 : {
423 34 : std::size_t n = 0;
424 34 : if(out_.data() == hp_)
425 6 : ++n;
426 34 : if(tmp0_.size() == 0 && more_)
427 : {
428 1 : BOOST_HTTP_PROTO_RETURN_EC(
429 : error::need_data);
430 : }
431 99 : for(buffers::const_buffer const& b : tmp0_.data())
432 66 : out_[n++] = b;
433 :
434 99 : return const_buffers_type(
435 33 : out_.data(),
436 33 : out_.size());
437 : }
438 :
439 : // should never get here
440 0 : detail::throw_logic_error();
441 : }
442 :
443 : void
444 1745 : serializer::
445 : consume(
446 : std::size_t n)
447 : {
448 : // Precondition violation
449 1745 : if(is_done_)
450 1 : detail::throw_logic_error();
451 :
452 1744 : if(is_expect_continue_)
453 : {
454 : // Cannot consume more than
455 : // the header on 100-continue
456 3 : if(n > hp_->size())
457 1 : detail::throw_invalid_argument();
458 :
459 2 : out_.consume(n);
460 2 : return;
461 : }
462 1741 : else if(out_.data() == hp_)
463 : {
464 : // consume header
465 26 : if(n < hp_->size())
466 : {
467 11 : out_.consume(n);
468 11 : return;
469 : }
470 15 : n -= hp_->size();
471 15 : out_.consume(hp_->size());
472 : }
473 :
474 1730 : switch(st_)
475 : {
476 3 : default:
477 : case style::empty:
478 3 : out_.consume(n);
479 3 : if(out_.empty())
480 3 : is_done_ = true;
481 3 : return;
482 :
483 3 : case style::buffers:
484 3 : if( is_compressed_ )
485 : {
486 0 : tmp0_.consume(n);
487 0 : if( tmp0_.size() == 0 &&
488 0 : filter_done_ )
489 0 : is_done_ = true;
490 0 : return;
491 : }
492 3 : out_.consume(n);
493 3 : if(out_.empty())
494 3 : is_done_ = true;
495 3 : return;
496 :
497 1724 : case style::source:
498 : case style::stream:
499 1724 : tmp0_.consume(n);
500 1724 : if( !is_compressed_ &&
501 3448 : tmp0_.size() == 0 &&
502 39 : ! more_)
503 11 : is_done_ = true;
504 :
505 0 : if( is_compressed_ &&
506 1724 : tmp0_.size() == 0 &&
507 0 : filter_done_ )
508 0 : is_done_ = true;
509 1724 : return;
510 : }
511 : }
512 :
513 : //------------------------------------------------
514 :
515 : void
516 7 : serializer::
517 : copy(
518 : buffers::const_buffer* dest,
519 : buffers::const_buffer const* src,
520 : std::size_t n) noexcept
521 : {
522 14 : while(n--)
523 7 : *dest++ = *src++;
524 7 : }
525 :
526 : void
527 26 : serializer::
528 : start_init(
529 : message_view_base const& m)
530 : {
531 26 : ws_.clear();
532 :
533 : // VFALCO what do we do with
534 : // metadata error code failures?
535 : // m.ph_->md.maybe_throw();
536 :
537 26 : is_done_ = false;
538 :
539 26 : is_expect_continue_ =
540 26 : m.ph_->md.expect.is_100_continue;
541 :
542 : // Transfer-Encoding
543 : {
544 26 : auto const& te =
545 26 : m.ph_->md.transfer_encoding;
546 26 : is_chunked_ = te.is_chunked;
547 : }
548 :
549 26 : if( m.compressed() )
550 : {
551 0 : is_compressed_ = true;
552 : auto& svc =
553 0 : ctx_->get_service<
554 0 : zlib::deflate_decoder_service>();
555 :
556 0 : BOOST_ASSERT(!zlib_filter_);
557 0 : zlib_filter_ = &svc.make_filter(filter_ws_);
558 : }
559 26 : }
560 :
561 : void
562 4 : serializer::
563 : start_empty(
564 : message_view_base const& m)
565 : {
566 4 : start_init(m);
567 :
568 4 : st_ = style::empty;
569 :
570 4 : if(! is_chunked_)
571 : {
572 3 : out_ = make_array(
573 : 1); // header
574 : }
575 : else
576 : {
577 1 : out_ = make_array(
578 : 1 + // header
579 : 1); // final chunk
580 :
581 : // Buffer is too small
582 1 : if(ws_.size() < 5)
583 0 : detail::throw_length_error();
584 :
585 : buffers::mutable_buffer dest(
586 1 : ws_.data(), 5);
587 1 : buffers::buffer_copy(
588 : dest,
589 1 : buffers::const_buffer(
590 : "0\r\n\r\n", 5));
591 1 : out_[1] = dest;
592 : }
593 :
594 4 : hp_ = &out_[0];
595 4 : *hp_ = { m.ph_->cbuf, m.ph_->size };
596 4 : }
597 :
598 : void
599 7 : serializer::
600 : start_buffers(
601 : message_view_base const& m)
602 : {
603 7 : st_ = style::buffers;
604 7 : if( is_compressed_ )
605 : {
606 0 : out_ = make_array(
607 : 1 + // header
608 : 2); // tmp
609 :
610 0 : hp_ = &out_[0];
611 0 : *hp_ = { m.ph_->cbuf, m.ph_->size };
612 0 : tmp0_ = { ws_.data(), ws_.size() };
613 0 : more_ = true;
614 0 : return;
615 : }
616 :
617 7 : if(! is_chunked_)
618 : {
619 : //if(! cod_)
620 : {
621 6 : out_ = make_array(
622 : 1 + // header
623 6 : buf_.size()); // body
624 12 : copy(&out_[1],
625 6 : buf_.data(), buf_.size());
626 : }
627 : #if 0
628 : else
629 : {
630 : out_ = make_array(
631 : 1 + // header
632 : 2); // tmp1
633 : }
634 : #endif
635 : }
636 : else
637 : {
638 : //if(! cod_)
639 : {
640 1 : out_ = make_array(
641 : 1 + // header
642 : 1 + // chunk size
643 1 : buf_.size() + // body
644 : 1); // final chunk
645 2 : copy(&out_[2],
646 1 : buf_.data(), buf_.size());
647 :
648 : // Buffer is too small
649 1 : if(ws_.size() < 18 + 7)
650 0 : detail::throw_length_error();
651 1 : buffers::mutable_buffer s1(ws_.data(), 18);
652 1 : buffers::mutable_buffer s2(ws_.data(), 18 + 7);
653 1 : s2 += 18; // VFALCO HACK
654 1 : write_chunk_header(
655 : s1,
656 1 : buffers::buffer_size(buf_));
657 1 : buffers::buffer_copy(s2, buffers::const_buffer(
658 : "\r\n"
659 : "0\r\n"
660 : "\r\n", 7));
661 1 : out_[1] = s1;
662 1 : out_[out_.size() - 1] = s2;
663 : }
664 : #if 0
665 : else
666 : {
667 : out_ = make_array(
668 : 1 + // header
669 : 2); // tmp1
670 : }
671 : #endif
672 : }
673 :
674 7 : hp_ = &out_[0];
675 7 : *hp_ = { m.ph_->cbuf, m.ph_->size };
676 : }
677 :
678 : void
679 8 : serializer::
680 : start_source(
681 : message_view_base const& m,
682 : source* src)
683 : {
684 8 : st_ = style::source;
685 8 : src_ = src;
686 8 : out_ = make_array(
687 : 1 + // header
688 : 2); // tmp
689 : //if(! cod_)
690 : {
691 8 : tmp0_ = { ws_.data(), ws_.size() };
692 8 : if(tmp0_.capacity() <
693 : 18 + // chunk size
694 : 1 + // body (1 byte)
695 : 2 + // CRLF
696 : 5) // final chunk
697 0 : detail::throw_length_error();
698 : }
699 : #if 0
700 : else
701 : {
702 : buffers::buffered_base::allocator a(
703 : ws_.data(), ws_.size()/3, false);
704 : src->init(a);
705 : ws_.reserve(a.size_used());
706 :
707 : auto const n = ws_.size() / 2;
708 :
709 : tmp0_ = { ws_.data(), ws_.size() / 2 };
710 : ws_.reserve(n);
711 :
712 : // Buffer is too small
713 : if(ws_.size() < 1)
714 : detail::throw_length_error();
715 :
716 : tmp1_ = { ws_.data(), ws_.size() };
717 : }
718 : #endif
719 :
720 8 : hp_ = &out_[0];
721 8 : *hp_ = { m.ph_->cbuf, m.ph_->size };
722 8 : more_ = true;
723 8 : }
724 :
725 : auto
726 7 : serializer::
727 : start_stream(
728 : message_view_base const& m) ->
729 : stream
730 : {
731 7 : start_init(m);
732 :
733 7 : st_ = style::stream;
734 7 : out_ = make_array(
735 : 1 + // header
736 : 2); // tmp
737 : //if(! cod_)
738 : {
739 7 : tmp0_ = { ws_.data(), ws_.size() };
740 7 : if(tmp0_.capacity() <
741 : 18 + // chunk size
742 : 1 + // body (1 byte)
743 : 2 + // CRLF
744 : 5) // final chunk
745 0 : detail::throw_length_error();
746 : }
747 : #if 0
748 : else
749 : {
750 : auto const n = ws_.size() / 2;
751 : tmp0_ = { ws_.data(), n };
752 : ws_.reserve(n);
753 :
754 : // Buffer is too small
755 : if(ws_.size() < 1)
756 : detail::throw_length_error();
757 :
758 : tmp1_ = { ws_.data(), ws_.size() };
759 : }
760 : #endif
761 :
762 7 : hp_ = &out_[0];
763 7 : *hp_ = { m.ph_->cbuf, m.ph_->size };
764 :
765 7 : more_ = true;
766 :
767 7 : return stream{*this};
768 : }
769 :
770 : //------------------------------------------------
771 :
772 : std::size_t
773 140 : serializer::
774 : stream::
775 : capacity() const noexcept
776 : {
777 140 : return sr_->tmp0_.capacity();
778 : }
779 :
780 : std::size_t
781 132 : serializer::
782 : stream::
783 : size() const noexcept
784 : {
785 132 : return sr_->tmp0_.size();
786 : }
787 :
788 : bool
789 66 : serializer::
790 : stream::
791 : is_full() const noexcept
792 : {
793 66 : if( sr_->is_chunked_ )
794 36 : return capacity() < detail::chunked_overhead_ + 1;
795 :
796 30 : return capacity() == 0;
797 : }
798 :
799 : auto
800 36 : serializer::
801 : stream::
802 : prepare() const ->
803 : buffers_type
804 : {
805 36 : if( sr_->is_compressed_ )
806 0 : return sr_->tmp1_.prepare(
807 0 : sr_->tmp1_.capacity());
808 :
809 36 : auto n = sr_->tmp0_.capacity();
810 36 : if( sr_->is_chunked_ )
811 : {
812 : // for chunked encoding, we want to unconditionally
813 : // reserve space for the complete chunk and the
814 : // last-chunk
815 : // this enables users to call:
816 : //
817 : // stream.commit(n); stream.close();
818 : //
819 : // without needing to worry about draining the
820 : // serializer via `consume()` calls
821 21 : if( n < detail::chunked_overhead_ + 1 )
822 1 : detail::throw_length_error();
823 :
824 20 : n -= detail::chunked_overhead_;
825 20 : return buffers::sans_prefix(
826 40 : sr_->tmp0_.prepare(
827 : detail::chunk_header_len_ + n),
828 20 : detail::chunk_header_len_);
829 : }
830 :
831 15 : return sr_->tmp0_.prepare(n);
832 : }
833 :
834 : void
835 35 : serializer::
836 : stream::
837 : commit(std::size_t n) const
838 : {
839 35 : if( sr_->is_compressed_ )
840 : {
841 0 : sr_->tmp1_.commit(n);
842 0 : return;
843 : }
844 :
845 35 : if(! sr_->is_chunked_ )
846 : {
847 15 : sr_->tmp0_.commit(n);
848 : }
849 : else
850 : {
851 : // Zero sized chunks are not valid. Call close()
852 : // if the intent is to signal the end of the body.
853 20 : if( n == 0 )
854 1 : detail::throw_logic_error();
855 :
856 : // TODO: this needs to be relocated
857 : //
858 19 : auto m = n + detail::chunk_header_len_;
859 19 : auto dest = sr_->tmp0_.prepare(m);
860 19 : write_chunk_header(
861 19 : buffers::prefix(
862 : dest, detail::chunk_header_len_),
863 : n);
864 19 : sr_->tmp0_.commit(m);
865 19 : write_chunk_close(sr_->tmp0_);
866 : }
867 : }
868 :
869 : void
870 9 : serializer::
871 : stream::
872 : close() const
873 : {
874 : // Precondition violation
875 9 : if(! sr_->more_ )
876 4 : detail::throw_logic_error();
877 :
878 5 : if( sr_->is_chunked_ && !sr_->is_compressed_ )
879 3 : write_last_chunk(sr_->tmp0_);
880 :
881 5 : sr_->more_ = false;
882 5 : }
883 :
884 : //------------------------------------------------
885 :
886 : } // http_proto
887 : } // boost
|