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