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