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_IMPL_SERIALIZER_IPP
11 : #define BOOST_HTTP_PROTO_IMPL_SERIALIZER_IPP
12 :
13 : #include <boost/http_proto/serializer.hpp>
14 : #include <boost/http_proto/detail/except.hpp>
15 : #include <boost/core/ignore_unused.hpp>
16 : #include <stddef.h>
17 :
18 : namespace boost {
19 : namespace http_proto {
20 :
21 : //------------------------------------------------
22 :
23 : void
24 13 : consume_buffers(
25 : const_buffer*& p,
26 : std::size_t& n,
27 : std::size_t bytes)
28 : {
29 13 : while(n > 0)
30 : {
31 7 : if(bytes < p->size())
32 : {
33 0 : *p += bytes;
34 0 : return;
35 : }
36 7 : bytes -= p->size();
37 7 : ++p;
38 7 : --n;
39 : }
40 :
41 : // Precondition violation
42 6 : if(bytes > 0)
43 0 : detail::throw_invalid_argument();
44 : }
45 :
46 : template<class MutableBuffers>
47 : void
48 3 : write_chunk_header(
49 : MutableBuffers const& dest0,
50 : std::size_t size) noexcept
51 : {
52 : static constexpr char hexdig[] =
53 : "0123456789ABCDEF";
54 : char buf[18];
55 3 : auto p = buf + 16;
56 51 : for(std::size_t i = 16; i--;)
57 : {
58 48 : *--p = hexdig[size & 0xf];
59 48 : size >>= 4;
60 : }
61 3 : buf[16] = '\r';
62 3 : buf[17] = '\n';
63 3 : auto n = buffer_copy(
64 : (make_buffers)(dest0),
65 : const_buffer(buf, sizeof(buf)));
66 : ignore_unused(n);
67 3 : BOOST_ASSERT(n == 18);
68 3 : BOOST_ASSERT(
69 : buffer_size(dest0) == n);
70 3 : }
71 :
72 : //------------------------------------------------
73 :
74 : class serializer::
75 : reserve
76 : : public source::reserve_fn
77 : {
78 : serializer& sr_;
79 : std::size_t limit_;
80 :
81 : public:
82 6 : reserve(
83 : serializer& sr,
84 : std::size_t limit) noexcept
85 6 : : sr_(sr)
86 6 : , limit_(limit)
87 : {
88 6 : }
89 :
90 : void*
91 0 : operator()(
92 : std::size_t n) const override
93 : {
94 : // You can only call reserve() once!
95 0 : if(! sr_.is_reserving_ )
96 0 : detail::throw_logic_error();
97 :
98 : // Requested n exceeds the limit
99 0 : if(n > limit_)
100 0 : detail::throw_length_error();
101 :
102 0 : sr_.is_reserving_ = false;
103 0 : return sr_.ws_.reserve(n);
104 : }
105 : };
106 :
107 : //------------------------------------------------
108 :
109 15 : serializer::
110 15 : ~serializer()
111 : {
112 15 : }
113 :
114 10 : serializer::
115 10 : serializer()
116 10 : : serializer(65536)
117 : {
118 10 : }
119 :
120 15 : serializer::
121 : serializer(
122 15 : std::size_t buffer_size)
123 15 : : ws_(buffer_size)
124 : {
125 15 : }
126 :
127 : //------------------------------------------------
128 :
129 : auto
130 14 : serializer::
131 : prepare() ->
132 : result<output>
133 : {
134 : // Precondition violation
135 14 : if(is_done_)
136 0 : detail::throw_logic_error();
137 :
138 : // Expect: 100-continue
139 14 : if(is_expect_continue_)
140 : {
141 4 : BOOST_ASSERT(hp_ != nullptr);
142 4 : if(hp_->size() > 0)
143 2 : return output(hp_, 1);
144 2 : is_expect_continue_ = false;
145 2 : hp_ = nullptr;
146 2 : BOOST_HTTP_PROTO_RETURN_EC(
147 : error::expect_100_continue);
148 : }
149 :
150 10 : if(st_ == style::empty)
151 : {
152 3 : return output(pp_, pn_);
153 : }
154 :
155 7 : if(st_ == style::buffers)
156 : {
157 3 : return output(pp_, pn_);
158 : }
159 :
160 4 : if(st_ == style::source)
161 : {
162 4 : if(! is_chunked_)
163 : {
164 3 : auto rv = src_->read(
165 : dat1_.prepare(
166 3 : dat1_.capacity() -
167 3 : dat1_.size()));
168 3 : dat1_.commit(rv.bytes);
169 3 : if(rv.ec.failed())
170 0 : return rv.ec;
171 3 : more_ = rv.more;
172 : }
173 : else
174 : {
175 1 : if((dat1_.capacity() -
176 1 : dat1_.size()) >
177 : chunked_overhead_)
178 : {
179 1 : auto dest = dat1_.prepare(18);
180 1 : write_chunk_header(dest, 0);
181 1 : dat1_.commit(18);
182 1 : auto rv = src_->read(
183 : dat1_.prepare(
184 1 : dat1_.capacity() -
185 : 2 - // CRLF
186 1 : 5 - // final chunk
187 1 : dat1_.size()));
188 1 : dat1_.commit(rv.bytes);
189 1 : if(rv.bytes == 0)
190 0 : dat1_.uncommit(18); // undo
191 1 : if(rv.ec.failed())
192 0 : return rv.ec;
193 1 : if(rv.bytes > 0)
194 : {
195 : // rewrite with correct size
196 1 : write_chunk_header(
197 : dest, rv.bytes);
198 : // terminate chunk
199 1 : dat1_.commit(buffer_copy(
200 1 : dat1_.prepare(2),
201 2 : const_buffer(
202 : "\r\n", 2)));
203 : }
204 1 : if(! rv.more)
205 : {
206 1 : dat1_.commit(buffer_copy(
207 1 : dat1_.prepare(5),
208 2 : const_buffer(
209 : "0\r\n\r\n", 5)));
210 : }
211 1 : more_ = rv.more;
212 : }
213 : }
214 :
215 4 : std::size_t n = 0;
216 4 : if(hp_ != nullptr)
217 3 : ++n;
218 8 : for(const_buffer b : dat1_.data())
219 4 : pp_[n++] = b;
220 4 : return output(pp_, n);
221 : }
222 :
223 0 : if(st_ == style::stream)
224 : {
225 0 : std::size_t n = 0;
226 0 : if(hp_ != nullptr)
227 0 : ++n;
228 0 : if(dat1_.empty() && more_)
229 : {
230 0 : BOOST_HTTP_PROTO_RETURN_EC(
231 : error::need_data);
232 : }
233 0 : for(const_buffer b : dat1_.data())
234 0 : pp_[n++] = b;
235 0 : return output(pp_, n);
236 : }
237 :
238 : // should never get here
239 0 : detail::throw_logic_error();
240 : }
241 :
242 : void
243 12 : serializer::
244 : consume(
245 : std::size_t n)
246 : {
247 : // Precondition violation
248 12 : if(is_done_)
249 0 : detail::throw_logic_error();
250 :
251 12 : if(is_expect_continue_)
252 : {
253 2 : BOOST_ASSERT(hp_ != nullptr);
254 :
255 : // Precondition violation
256 2 : if(n > hp_->size())
257 0 : detail::throw_invalid_argument();
258 :
259 : // consume header
260 2 : if(n < hp_->size())
261 : {
262 0 : *hp_ += n;
263 0 : return;
264 : }
265 2 : *hp_ = {};
266 2 : return;
267 : }
268 10 : else if(hp_ != nullptr)
269 : {
270 : // consume header
271 8 : if(n < hp_->size())
272 : {
273 0 : *hp_ += n;
274 0 : return;
275 : }
276 8 : n -= hp_->size();
277 8 : hp_ = nullptr;
278 8 : ++pp_;
279 8 : --pn_;
280 : }
281 :
282 10 : switch(st_)
283 : {
284 6 : default:
285 : case style::empty:
286 : case style::buffers:
287 6 : consume_buffers(
288 6 : pp_, pn_, n);
289 6 : if(pn_ == 0)
290 6 : is_done_ = true;
291 6 : return;
292 :
293 4 : case style::source:
294 : case style::stream:
295 4 : dat1_.consume(n);
296 8 : if( dat1_.empty() &&
297 4 : ! more_)
298 4 : is_done_ = true;
299 4 : return;
300 : }
301 : }
302 :
303 : //------------------------------------------------
304 :
305 : void
306 0 : serializer::
307 : apply_param(
308 : brotli_decoder_t const&)
309 : {
310 0 : }
311 :
312 : void
313 0 : serializer::
314 : apply_param(
315 : brotli_encoder_t const&)
316 : {
317 0 : }
318 :
319 : void
320 0 : serializer::
321 : apply_param(
322 : deflate_decoder_t const&)
323 : {
324 0 : }
325 :
326 : void
327 0 : serializer::
328 : apply_param(
329 : deflate_encoder_t const&)
330 : {
331 0 : }
332 :
333 : void
334 2 : serializer::
335 : apply_param(
336 : gzip_decoder_t const&)
337 : {
338 2 : }
339 :
340 : void
341 2 : serializer::
342 : apply_param(
343 : gzip_encoder_t const&)
344 : {
345 2 : }
346 :
347 : //------------------------------------------------
348 :
349 : void
350 6 : serializer::
351 : do_reserve(
352 : source& src,
353 : std::size_t limit)
354 : {
355 : struct cleanup
356 : {
357 : bool& is_reserving;
358 :
359 6 : ~cleanup()
360 6 : {
361 6 : is_reserving = false;
362 6 : }
363 : };
364 :
365 6 : BOOST_ASSERT(! is_reserving_);
366 6 : cleanup c{is_reserving_};
367 6 : is_reserving_ = true;
368 12 : reserve fn(*this, limit);
369 6 : src.maybe_reserve(limit, fn);
370 6 : }
371 :
372 : void
373 4 : serializer::
374 : reset_empty_impl(
375 : message_view_base const& m)
376 : {
377 : // Precondition violation
378 4 : if(ws_.size() < chunked_overhead_)
379 0 : detail::throw_length_error();
380 :
381 4 : ws_.clear();
382 :
383 4 : is_done_ = false;
384 : // VFALCO what about the error codes?
385 : // m.ph_->md.maybe_throw();
386 4 : is_chunked_ =
387 4 : m.ph_->md.transfer_encoding.is_chunked;
388 4 : is_expect_continue_ =
389 4 : m.ph_->md.expect.is_100_continue;
390 : //---
391 4 : st_ = style::empty;
392 4 : pp_ = ws_.push_array(
393 4 : 2, const_buffer{});
394 4 : pn_ = 1;
395 4 : hp_ = pp_;
396 8 : *hp_ = {
397 4 : m.ph_->cbuf,
398 4 : m.ph_->size };
399 4 : if(is_chunked_)
400 : {
401 : detail::circular_buffer tmp(
402 1 : ws_.data(), ws_.size());
403 1 : tmp.commit(buffer_copy(
404 1 : tmp.prepare(5),
405 1 : const_buffer(
406 : "0\r\n\r\n", 5)));
407 1 : auto data = tmp.data();
408 1 : BOOST_ASSERT(
409 : data[1].size() == 0);
410 1 : pp_[pn_++] = data[0];
411 : }
412 4 : }
413 :
414 : void
415 11 : serializer::
416 : reset_buffers_impl(
417 : message_view_base const& m,
418 : const_buffer* pp,
419 : std::size_t pn)
420 : {
421 : // Precondition violation
422 11 : if(ws_.size() < chunked_overhead_)
423 0 : detail::throw_length_error();
424 :
425 11 : is_done_ = false;
426 : // VFALCO what about the error codes?
427 : // m.ph_->md.maybe_throw();
428 11 : is_chunked_ =
429 11 : m.ph_->md.transfer_encoding.is_chunked;
430 11 : is_expect_continue_ =
431 11 : m.ph_->md.expect.is_100_continue;
432 : //---
433 11 : st_ = style::buffers;
434 11 : if(! is_chunked_)
435 : {
436 10 : pn_ = pn - 2;
437 10 : pp_ = pp + 1;
438 10 : hp_ = pp_;
439 10 : *hp_ = {
440 10 : m.ph_->cbuf,
441 10 : m.ph_->size };
442 : }
443 : else
444 : {
445 1 : std::size_t n = 0;
446 1 : for(std::size_t i = 2;
447 2 : i < pn - 1; ++i)
448 1 : n += pp[i].size();
449 :
450 1 : pn_ = pn;
451 1 : pp_ = pp;
452 1 : hp_ = pp_;
453 2 : *hp_ = {
454 1 : m.ph_->cbuf,
455 1 : m.ph_->size };
456 :
457 : detail::circular_buffer tmp{
458 1 : ws_.data(), ws_.size() };
459 : {
460 1 : auto dest = tmp.prepare(18);
461 1 : BOOST_ASSERT(dest[0].size() == 18);
462 1 : BOOST_ASSERT(dest[1].size() == 0);
463 1 : write_chunk_header(dest, n);
464 1 : tmp.commit(18);
465 1 : pp_[1] = dest[0];
466 : const_buffer fc(
467 : "\r\n"
468 : "0\r\n"
469 1 : "\r\n", 7);
470 1 : dest = tmp.prepare(fc.size());
471 1 : buffer_copy(dest, fc);
472 1 : BOOST_ASSERT(dest[0].size() == 7);
473 1 : BOOST_ASSERT(dest[1].size() == 0);
474 1 : pp_[pn_ - 1] = dest[0];
475 : }
476 : }
477 11 : }
478 :
479 : void
480 6 : serializer::
481 : reset_source_impl(
482 : message_view_base const& m,
483 : source* src)
484 : {
485 : // Precondition violation
486 6 : if(ws_.size() <
487 : chunked_overhead_ +
488 : 128) // reasonable lower limit
489 0 : detail::throw_length_error();
490 :
491 6 : is_done_ = false;
492 : // VFALCO what about the error codes?
493 : // m.ph_->md.maybe_throw();
494 6 : is_chunked_ =
495 6 : m.ph_->md.transfer_encoding.is_chunked;
496 6 : is_expect_continue_ =
497 6 : m.ph_->md.expect.is_100_continue;
498 : //---
499 6 : st_ = style::source;
500 6 : src_ = src;
501 6 : do_reserve(
502 : *src,
503 6 : ws_.size() / 2); // VFALCO can this underflow?
504 6 : pn_ =
505 : 1 + // header
506 : 2; // dat1
507 6 : pp_ = ws_.push_array(
508 6 : pn_, const_buffer{});
509 6 : hp_ = pp_;
510 12 : *hp_ = {
511 6 : m.ph_->cbuf,
512 6 : m.ph_->size };
513 6 : dat1_ = {
514 : ws_.data(),
515 : ws_.size() };
516 6 : }
517 :
518 : //------------------------------------------------
519 :
520 : std::size_t
521 0 : serializer::
522 : stream::
523 : capacity() const
524 : {
525 0 : auto const n =
526 : chunked_overhead_ +
527 : 2 + // CRLF
528 : 5; // final chunk
529 0 : return sr_->dat1_.capacity() -
530 0 : n - 0;
531 : }
532 :
533 : std::size_t
534 0 : serializer::
535 : stream::
536 : size() const
537 : {
538 0 : return sr_->dat1_.size();
539 : }
540 :
541 : auto
542 0 : serializer::
543 : stream::
544 : prepare(
545 : std::size_t n) const ->
546 : buffers_type
547 : {
548 0 : return sr_->dat1_.prepare(n);
549 : }
550 :
551 : void
552 0 : serializer::
553 : stream::
554 : commit(std::size_t n) const
555 : {
556 0 : sr_->dat1_.commit(n);
557 0 : }
558 :
559 : void
560 0 : serializer::
561 : stream::
562 : close() const
563 : {
564 : // Precondition violation
565 0 : if(! sr_->more_)
566 0 : detail::throw_logic_error();
567 0 : sr_->more_ = false;
568 0 : }
569 :
570 : void
571 0 : serializer::
572 : reset_stream_impl(
573 : message_view_base const& m,
574 : source& src)
575 : {
576 : // Precondition violation
577 0 : if(ws_.size() <
578 : chunked_overhead_ +
579 : 128) // reasonable lower limit
580 0 : detail::throw_length_error();
581 :
582 0 : is_done_ = false;
583 : // VFALCO what about the error codes?
584 : // m.ph_->md.maybe_throw();
585 0 : is_chunked_ =
586 0 : m.ph_->md.transfer_encoding.is_chunked;
587 0 : is_expect_continue_ =
588 0 : m.ph_->md.expect.is_100_continue;
589 : //---
590 0 : st_ = style::stream;
591 0 : do_reserve(
592 : src,
593 0 : ws_.size() / 2); // VFALCO can this underflow?
594 0 : pn_ =
595 : 1 + // header
596 : 2; // dat1
597 0 : pp_ = ws_.push_array(
598 0 : pn_, const_buffer{});
599 0 : hp_ = pp_;
600 0 : *hp_ = {
601 0 : m.ph_->cbuf,
602 0 : m.ph_->size };
603 0 : dat1_ = {
604 : ws_.data(),
605 : ws_.size() };
606 0 : more_ = true;
607 0 : }
608 :
609 : //------------------------------------------------
610 :
611 : } // http_proto
612 : } // boost
613 :
614 : #endif
|