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