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