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/detail/except.hpp>
15 : #include <boost/buffers/algorithm.hpp>
16 : #include <boost/buffers/buffer_copy.hpp>
17 : #include <boost/buffers/buffer_size.hpp>
18 : #include <boost/core/ignore_unused.hpp>
19 : #include <stddef.h>
20 :
21 : #include "zlib_service.hpp"
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 6296 : 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 6296 : auto p = buf + 16;
61 107032 : for(std::size_t i = 16; i--;)
62 : {
63 100736 : *--p = hexdig[size & 0xf];
64 100736 : size >>= 4;
65 : }
66 6296 : buf[16] = '\r';
67 6296 : buf[17] = '\n';
68 6296 : auto n = buffers::buffer_copy(
69 : dest0,
70 12592 : buffers::const_buffer(
71 : buf, sizeof(buf)));
72 : ignore_unused(n);
73 6296 : BOOST_ASSERT(n == 18);
74 6296 : BOOST_ASSERT(
75 : buffers::buffer_size(dest0) == n);
76 6296 : }
77 :
78 : template<class DynamicBuffer>
79 : void
80 : write_chunk_close(DynamicBuffer& db)
81 : {
82 : db.commit(
83 : buffers::buffer_copy(
84 : db.prepare(2),
85 : buffers::const_buffer("\r\n", 2)));
86 : }
87 :
88 : template<class DynamicBuffer>
89 : void
90 : write_last_chunk(DynamicBuffer& db)
91 : {
92 : db.commit(
93 : buffers::buffer_copy(
94 : db.prepare(5),
95 : buffers::const_buffer("0\r\n\r\n", 5)));
96 : }
97 :
98 : //------------------------------------------------
99 :
100 43 : serializer::
101 : ~serializer()
102 : {
103 43 : }
104 :
105 0 : serializer::
106 : serializer(
107 : serializer&&) noexcept = default;
108 :
109 9 : serializer::
110 : serializer(
111 9 : context& ctx)
112 9 : : serializer(ctx, 65536)
113 : {
114 9 : }
115 :
116 43 : serializer::
117 : serializer(
118 : context& ctx,
119 43 : std::size_t buffer_size)
120 43 : : ws_(buffer_size)
121 43 : , ctx_(ctx)
122 : {
123 43 : }
124 :
125 : void
126 56 : serializer::
127 : reset() noexcept
128 : {
129 56 : chunk_header_ = {};
130 56 : chunk_close_ = {};
131 56 : last_chunk_ = {};
132 56 : filter_ = nullptr;
133 56 : more_ = false;
134 56 : is_done_ = false;
135 56 : is_chunked_ = false;
136 56 : is_expect_continue_ = false;
137 56 : is_compressed_ = false;
138 56 : filter_done_ = false;
139 56 : in_ = nullptr;
140 56 : out_ = nullptr;
141 56 : ws_.clear();
142 56 : }
143 :
144 : //------------------------------------------------
145 :
146 : auto
147 12572 : serializer::
148 : prepare() ->
149 : system::result<
150 : const_buffers_type>
151 : {
152 : // Precondition violation
153 12572 : if( is_done_ )
154 1 : detail::throw_logic_error();
155 :
156 : // Expect: 100-continue
157 12571 : if( is_expect_continue_ )
158 : {
159 4 : if( !is_header_done_ )
160 2 : return const_buffers_type(hp_, 1);
161 2 : is_expect_continue_ = false;
162 2 : BOOST_HTTP_PROTO_RETURN_EC(
163 : error::expect_100_continue);
164 : }
165 :
166 12567 : if( st_ == style::empty )
167 9 : return const_buffers_type(
168 6 : prepped_.data(), prepped_.size());
169 :
170 12564 : if( st_ == style::buffers && !filter_ )
171 9 : return const_buffers_type(
172 6 : prepped_.data(), prepped_.size());
173 :
174 : // callers must consume() everything before invoking
175 : // prepare() again
176 12620 : if( !is_header_done_ &&
177 59 : buffers::buffer_size(prepped_) != prepped_[0].size() )
178 0 : detail::throw_logic_error();
179 :
180 25063 : if( is_header_done_ &&
181 12502 : buffers::buffer_size(prepped_) > 0 )
182 0 : detail::throw_logic_error();
183 :
184 12561 : auto& input = *in_;
185 12561 : auto& output = *out_;
186 12561 : if( st_ == style::source && more_ )
187 : {
188 5490 : auto results = src_->read(
189 5490 : input.prepare(input.capacity()));
190 5490 : more_ = !results.finished;
191 5490 : input.commit(results.bytes);
192 : }
193 :
194 30653 : if( st_ == style::stream &&
195 18071 : more_ &&
196 5510 : in_->size() == 0 )
197 1 : BOOST_HTTP_PROTO_RETURN_EC(error::need_data);
198 :
199 : bool has_avail_out =
200 25081 : ((!filter_ && (more_ || input.size() > 0)) ||
201 12521 : (filter_ && !filter_done_));
202 :
203 25248 : auto get_input = [&]() -> buffers::const_buffer
204 : {
205 25248 : if( st_ == style::buffers )
206 : {
207 3296 : if( buffers::buffer_size(buf_) == 0 )
208 64 : return {};
209 :
210 3232 : auto buf = *(buf_.data());
211 3232 : BOOST_ASSERT(buf.size() > 0);
212 3232 : return buf;
213 : }
214 : else
215 : {
216 21952 : if( input.size() == 0 )
217 10992 : return {};
218 :
219 10960 : auto cbs = input.data();
220 10960 : auto buf = *cbs.begin();
221 10960 : if( buf.size() == 0 )
222 : {
223 0 : auto p = cbs.begin();
224 0 : ++p;
225 0 : buf = *p;
226 : }
227 10960 : if( buf.size() == 0 )
228 0 : detail::throw_logic_error();
229 10960 : return buf;
230 : }
231 12560 : };
232 :
233 25248 : auto get_output = [&]() -> buffers::mutable_buffer
234 : {
235 25248 : auto mbs = output.prepare(output.capacity());
236 25248 : auto buf = *mbs.begin();
237 25248 : if( buf.size() == 0 )
238 : {
239 1524 : auto p = mbs.begin();
240 1524 : ++p;
241 1524 : buf = *p;
242 : }
243 25248 : return buf;
244 12560 : };
245 :
246 23724 : auto consume = [&](std::size_t n)
247 : {
248 23724 : if( st_ == style::buffers )
249 : {
250 1772 : buf_.consume(n);
251 1772 : if( buffers::buffer_size(buf_) == 0 )
252 64 : more_ = false;
253 : }
254 : else
255 21952 : input.consume(n);
256 36284 : };
257 :
258 12560 : std::size_t num_written = 0;
259 12560 : if( !filter_ )
260 44 : num_written += input.size();
261 : else
262 : {
263 : for(;;)
264 : {
265 25248 : auto in = get_input();
266 25248 : auto out = get_output();
267 25248 : if( out.size() == 0 )
268 : {
269 1524 : if( output.size() == 0 )
270 0 : detail::throw_logic_error();
271 12516 : break;
272 : }
273 :
274 23724 : auto rs = filter_->on_process(
275 23724 : out, in, more_);
276 :
277 23724 : if( rs.finished )
278 96 : filter_done_ = true;
279 :
280 23724 : consume(rs.in_bytes);
281 :
282 23724 : if( rs.out_bytes == 0 )
283 10992 : break;
284 :
285 12732 : num_written += rs.out_bytes;
286 12732 : output.commit(rs.out_bytes);
287 12732 : }
288 : }
289 :
290 : // end:
291 12560 : std::size_t n = 0;
292 12560 : if( !is_header_done_ )
293 : {
294 58 : BOOST_ASSERT(hp_ == &prepped_[0]);
295 58 : ++n;
296 : }
297 : else
298 12502 : prepped_.reset(prepped_.capacity());
299 :
300 12560 : if( !is_chunked_ )
301 : {
302 18786 : for(buffers::const_buffer const& b : output.data())
303 12524 : prepped_[n++] = b;
304 : }
305 : else
306 : {
307 6298 : if( has_avail_out )
308 : {
309 6295 : write_chunk_header(
310 6295 : chunk_header_, num_written);
311 6295 : prepped_[n++] = chunk_header_;
312 :
313 18885 : for(buffers::const_buffer const& b : output.data())
314 12590 : prepped_[n++] = b;
315 :
316 6295 : prepped_[n++] = chunk_close_;
317 : }
318 :
319 6298 : if( (filter_ && filter_done_) ||
320 6274 : (!filter_ && !more_) )
321 29 : prepped_[n++] = last_chunk_;
322 : }
323 :
324 : auto cbs = const_buffers_type(
325 12560 : prepped_.data(), prepped_.size());
326 :
327 12560 : BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
328 12560 : return cbs;
329 : }
330 :
331 : void
332 14313 : serializer::
333 : consume(
334 : std::size_t n)
335 : {
336 : // Precondition violation
337 14313 : if( is_done_ )
338 1 : detail::throw_logic_error();
339 :
340 14312 : if( is_expect_continue_ )
341 : {
342 : // Cannot consume more than
343 : // the header on 100-continue
344 3 : if( n > hp_->size() )
345 1 : detail::throw_invalid_argument();
346 : }
347 :
348 14311 : if( !is_header_done_ )
349 : {
350 : // consume header
351 76 : if( n < hp_->size() )
352 : {
353 11 : prepped_.consume(n);
354 11 : return;
355 : }
356 65 : n -= hp_->size();
357 65 : prepped_.consume(hp_->size());
358 65 : is_header_done_ = true;
359 : }
360 :
361 14300 : prepped_.consume(n);
362 14300 : auto is_empty = (buffers::buffer_size(prepped_) == 0);
363 :
364 14300 : if( st_ == style::buffers && !filter_ && is_empty )
365 3 : more_ = false;
366 :
367 14300 : if( st_ == style::empty &&
368 4 : is_empty &&
369 4 : !is_expect_continue_ )
370 3 : more_ = false;
371 :
372 14300 : if( is_empty )
373 : {
374 12568 : if( out_ && out_->size() )
375 : {
376 12555 : BOOST_ASSERT(st_ != style::empty);
377 12555 : out_->consume(out_->size());
378 : }
379 12568 : is_done_ = filter_ ? filter_done_ : !more_;
380 : }
381 : }
382 :
383 : void
384 24 : serializer::
385 : use_deflate_encoding()
386 : {
387 : // can only apply one encoding
388 24 : if( filter_ )
389 0 : detail::throw_logic_error();
390 :
391 24 : BOOST_ASSERT(!filter_);
392 :
393 24 : is_compressed_ = true;
394 : auto& svc =
395 24 : ctx_.get_service<
396 24 : zlib::detail::deflate_decoder_service>();
397 24 : filter_ = &svc.make_deflate_filter(ws_);
398 24 : }
399 :
400 : void
401 24 : serializer::
402 : use_gzip_encoding()
403 : {
404 : // can only apply one encoding
405 24 : if( filter_ )
406 0 : detail::throw_logic_error();
407 :
408 24 : BOOST_ASSERT(!filter_);
409 :
410 24 : is_compressed_ = true;
411 : auto& svc =
412 24 : ctx_.get_service<
413 24 : zlib::detail::deflate_decoder_service>();
414 24 : filter_ = &svc.make_gzip_filter(ws_);
415 24 : }
416 :
417 : //------------------------------------------------
418 :
419 : void
420 7 : serializer::
421 : copy(
422 : buffers::const_buffer* dest,
423 : buffers::const_buffer const* src,
424 : std::size_t n) noexcept
425 : {
426 14 : while(n--)
427 7 : *dest++ = *src++;
428 7 : }
429 :
430 : void
431 73 : serializer::
432 : start_init(
433 : message_view_base const& m)
434 : {
435 : // VFALCO what do we do with
436 : // metadata error code failures?
437 : // m.ph_->md.maybe_throw();
438 :
439 73 : auto const& md = m.metadata();
440 :
441 73 : is_done_ = false;
442 73 : is_header_done_ = false;
443 73 : is_expect_continue_ = md.expect.is_100_continue;
444 :
445 : // Transfer-Encoding
446 : {
447 73 : auto const& te = md.transfer_encoding;
448 73 : is_chunked_ = te.is_chunked;
449 : }
450 :
451 73 : if( is_chunked_)
452 : {
453 31 : auto* p = ws_.reserve_front(chunked_overhead_);
454 31 : chunk_header_ =
455 31 : buffers::mutable_buffer(p, chunk_header_len_);
456 31 : chunk_close_ =
457 62 : buffers::mutable_buffer(
458 31 : p + chunk_header_len_, crlf_len_);
459 31 : last_chunk_ =
460 62 : buffers::mutable_buffer(
461 31 : p + chunk_header_len_ + crlf_len_,
462 : last_chunk_len_);
463 :
464 31 : buffers::buffer_copy(
465 31 : chunk_close_, buffers::const_buffer("\r\n", 2));
466 31 : buffers::buffer_copy(
467 31 : last_chunk_,
468 62 : buffers::const_buffer("0\r\n\r\n", 5));
469 : }
470 73 : }
471 :
472 : void
473 4 : serializer::
474 : start_empty(
475 : message_view_base const& m)
476 : {
477 4 : start_init(m);
478 :
479 4 : st_ = style::empty;
480 4 : more_ = true;
481 :
482 4 : if(! is_chunked_)
483 : {
484 3 : prepped_ = make_array(
485 : 1); // header
486 : }
487 : else
488 : {
489 1 : prepped_ = make_array(
490 : 1 + // header
491 : 1); // final chunk
492 :
493 : // Buffer is too small
494 1 : if(ws_.size() < 5)
495 0 : detail::throw_length_error();
496 :
497 : buffers::mutable_buffer dest(
498 1 : ws_.data(), 5);
499 1 : buffers::buffer_copy(
500 : dest,
501 1 : buffers::const_buffer(
502 : "0\r\n\r\n", 5));
503 1 : prepped_[1] = dest;
504 : }
505 :
506 4 : hp_ = &prepped_[0];
507 4 : *hp_ = { m.ph_->cbuf, m.ph_->size };
508 4 : }
509 :
510 : void
511 23 : serializer::
512 : start_buffers(
513 : message_view_base const& m)
514 : {
515 23 : st_ = style::buffers;
516 23 : tmp1_ = {};
517 :
518 23 : if( !filter_ && !is_chunked_ )
519 : {
520 6 : prepped_ = make_array(
521 : 1 + // header
522 6 : buf_.size()); // user input
523 :
524 6 : hp_ = &prepped_[0];
525 6 : *hp_ = { m.ph_->cbuf, m.ph_->size };
526 :
527 6 : copy(&prepped_[1], buf_.data(), buf_.size());
528 :
529 6 : more_ = (buffers::buffer_size(buf_) > 0);
530 6 : return;
531 : }
532 :
533 17 : if( !filter_ && is_chunked_ )
534 : {
535 1 : if( buffers::buffer_size(buf_) == 0 )
536 : {
537 0 : prepped_ = make_array(
538 : 1 + // header
539 : 1); // last chunk
540 :
541 0 : hp_ = &prepped_[0];
542 0 : *hp_ = { m.ph_->cbuf, m.ph_->size };
543 0 : prepped_[1] = last_chunk_;
544 0 : more_ = false;
545 0 : return;
546 : }
547 :
548 2 : write_chunk_header(
549 1 : chunk_header_, buffers::buffer_size(buf_));
550 :
551 1 : prepped_ = make_array(
552 : 1 + // header
553 : 1 + // chunk header
554 1 : buf_.size() + // user input
555 : 1 + // chunk close
556 : 1); // last chunk
557 :
558 1 : hp_ = &prepped_[0];
559 1 : *hp_ = { m.ph_->cbuf, m.ph_->size };
560 1 : prepped_[1] = chunk_header_;
561 1 : copy(&prepped_[2], buf_.data(), buf_.size());
562 :
563 1 : prepped_[prepped_.size() - 2] = chunk_close_;
564 1 : prepped_[prepped_.size() - 1] = last_chunk_;
565 1 : more_ = true;
566 1 : return;
567 : }
568 :
569 16 : if( is_chunked_ )
570 : {
571 8 : prepped_ = make_array(
572 : 1 + // header
573 : 1 + // chunk header
574 : 2 + // tmp
575 : 1 + // chunk close
576 : 1); // last chunk
577 : }
578 : else
579 8 : prepped_ = make_array(
580 : 1 + // header
581 : 2); // tmp
582 :
583 16 : hp_ = &prepped_[0];
584 16 : *hp_ = { m.ph_->cbuf, m.ph_->size };
585 16 : tmp0_ = { ws_.data(), ws_.size() };
586 16 : out_ = &tmp0_;
587 16 : in_ = out_;
588 16 : more_ = true;
589 : }
590 :
591 : void
592 24 : serializer::
593 : start_source(
594 : message_view_base const& m,
595 : source* src)
596 : {
597 24 : st_ = style::source;
598 24 : src_ = src;
599 :
600 24 : if( is_chunked_ )
601 : {
602 10 : prepped_ = make_array(
603 : 1 + // header
604 : 1 + // chunk header
605 : 2 + // tmp
606 : 1 + // chunk close
607 : 1); // last chunk
608 : }
609 : else
610 14 : prepped_ = make_array(
611 : 1 + // header
612 : 2); // tmp
613 :
614 24 : if( !filter_ )
615 : {
616 8 : tmp0_ = { ws_.data(), ws_.size() };
617 8 : if( tmp0_.capacity() < 1 )
618 0 : detail::throw_length_error();
619 :
620 8 : in_ = &tmp0_;
621 8 : out_ = &tmp0_;
622 : }
623 : else
624 : {
625 16 : auto n = ws_.size() / 2;
626 16 : auto* p = ws_.reserve_front(n);
627 16 : tmp1_ = buffers::circular_buffer(p, n);
628 :
629 16 : tmp0_ = { ws_.data(), ws_.size() };
630 16 : if( tmp0_.capacity() < 1 )
631 0 : detail::throw_length_error();
632 :
633 16 : in_ = &tmp1_;
634 16 : out_ = &tmp0_;
635 : }
636 :
637 24 : hp_ = &prepped_[0];
638 24 : *hp_ = { m.ph_->cbuf, m.ph_->size };
639 24 : more_ = true;
640 24 : }
641 :
642 : auto
643 22 : serializer::
644 : start_stream(
645 : message_view_base const& m) ->
646 : stream
647 : {
648 22 : start_init(m);
649 :
650 22 : st_ = style::stream;
651 22 : if( is_chunked_ )
652 : {
653 11 : prepped_ = make_array(
654 : 1 + // header
655 : 1 + // chunk header
656 : 2 + // tmp
657 : 1 + // chunk close
658 : 1); // last chunk
659 : }
660 : else
661 11 : prepped_ = make_array(
662 : 1 + // header
663 : 2); // tmp
664 :
665 22 : if( !filter_ )
666 : {
667 6 : tmp0_ = { ws_.data(), ws_.size() };
668 6 : if( tmp0_.capacity() < 1 )
669 0 : detail::throw_length_error();
670 :
671 6 : in_ = &tmp0_;
672 6 : out_ = &tmp0_;
673 : }
674 : else
675 : {
676 16 : auto n = ws_.size() / 2;
677 16 : auto* p = ws_.reserve_front(n);
678 16 : tmp1_ = buffers::circular_buffer(p, n);
679 :
680 16 : tmp0_ = { ws_.data(), ws_.size() };
681 16 : if( tmp0_.capacity() < 1 )
682 0 : detail::throw_length_error();
683 :
684 16 : in_ = &tmp1_;
685 16 : out_ = &tmp0_;
686 : }
687 :
688 22 : hp_ = &prepped_[0];
689 22 : *hp_ = { m.ph_->cbuf, m.ph_->size };
690 22 : more_ = true;
691 22 : return stream{*this};
692 : }
693 :
694 : //------------------------------------------------
695 :
696 : std::size_t
697 139 : serializer::
698 : stream::
699 : capacity() const noexcept
700 : {
701 139 : return sr_->in_->capacity();
702 : }
703 :
704 : std::size_t
705 72 : serializer::
706 : stream::
707 : size() const noexcept
708 : {
709 72 : return sr_->in_->size();
710 : }
711 :
712 : bool
713 63 : serializer::
714 : stream::
715 : is_full() const noexcept
716 : {
717 63 : return capacity() == 0;
718 : }
719 :
720 : auto
721 5512 : serializer::
722 : stream::
723 : prepare() const ->
724 : buffers_type
725 : {
726 5512 : return sr_->in_->prepare(sr_->in_->capacity());
727 : }
728 :
729 : void
730 5512 : serializer::
731 : stream::
732 : commit(std::size_t n) const
733 : {
734 : // the stream must make a non-zero amount of bytes
735 : // available to the serializer
736 5512 : if( n == 0 )
737 1 : detail::throw_logic_error();
738 :
739 5511 : sr_->in_->commit(n);
740 5511 : }
741 :
742 : void
743 25 : serializer::
744 : stream::
745 : close() const
746 : {
747 : // Precondition violation
748 25 : if(! sr_->more_ )
749 4 : detail::throw_logic_error();
750 21 : sr_->more_ = false;
751 21 : }
752 :
753 : //------------------------------------------------
754 :
755 : } // http_proto
756 : } // boost
|