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