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