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/buffers/buffer_copy.hpp>
17 : #include <boost/buffers/buffer_size.hpp>
18 : #include <boost/core/ignore_unused.hpp>
19 : #include <stddef.h>
20 :
21 : namespace boost {
22 : namespace http_proto {
23 :
24 : //------------------------------------------------
25 :
26 : void
27 0 : consume_buffers(
28 : buffers::const_buffer*& p,
29 : std::size_t& n,
30 : std::size_t bytes)
31 : {
32 0 : while(n > 0)
33 : {
34 0 : if(bytes < p->size())
35 : {
36 0 : *p += bytes;
37 0 : return;
38 : }
39 0 : bytes -= p->size();
40 0 : ++p;
41 0 : --n;
42 : }
43 :
44 : // Precondition violation
45 0 : if(bytes > 0)
46 0 : detail::throw_invalid_argument();
47 : }
48 :
49 : template<class MutableBuffers>
50 : void
51 3 : write_chunk_header(
52 : MutableBuffers const& dest0,
53 : std::size_t size) noexcept
54 : {
55 : static constexpr char hexdig[] =
56 : "0123456789ABCDEF";
57 : char buf[18];
58 3 : auto p = buf + 16;
59 51 : for(std::size_t i = 16; i--;)
60 : {
61 48 : *--p = hexdig[size & 0xf];
62 48 : size >>= 4;
63 : }
64 3 : buf[16] = '\r';
65 3 : buf[17] = '\n';
66 3 : auto n = buffers::buffer_copy(
67 : dest0,
68 : buffers::const_buffer(
69 : buf, sizeof(buf)));
70 : ignore_unused(n);
71 3 : BOOST_ASSERT(n == 18);
72 3 : BOOST_ASSERT(
73 : buffers::buffer_size(dest0) == n);
74 3 : }
75 :
76 : //------------------------------------------------
77 :
78 11 : serializer::
79 77 : ~serializer()
80 : {
81 11 : }
82 :
83 10 : serializer::
84 10 : serializer()
85 10 : : serializer(65536)
86 : {
87 10 : }
88 :
89 : serializer::
90 : serializer(
91 : serializer&&) noexcept = default;
92 :
93 11 : serializer::
94 : serializer(
95 11 : std::size_t buffer_size)
96 11 : : ws_(buffer_size)
97 : {
98 11 : }
99 :
100 : void
101 0 : serializer::
102 : reset() noexcept
103 : {
104 0 : }
105 :
106 : //------------------------------------------------
107 :
108 : auto
109 14 : serializer::
110 : prepare() ->
111 : result<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 1 : if(rv.bytes == 0)
171 0 : 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 17 : cod_ = nullptr;
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 6 : 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 : else
380 : {
381 : out_ = make_array(
382 : 1 + // header
383 0 : 2); // tmp1
384 : }
385 : }
386 : else
387 : {
388 1 : if(! cod_)
389 : {
390 : out_ = make_array(
391 : 1 + // header
392 : 1 + // chunk size
393 1 : buf_.size() + // body
394 1 : 1); // final chunk
395 2 : copy(&out_[2],
396 1 : buf_.data(), buf_.size());
397 :
398 : // Buffer is too small
399 1 : if(ws_.size() < 18 + 7)
400 0 : detail::throw_length_error();
401 1 : buffers::mutable_buffer s1(ws_.data(), 18);
402 1 : buffers::mutable_buffer s2(ws_.data(), 18 + 7);
403 1 : s2 += 18; // VFALCO HACK
404 1 : write_chunk_header(
405 : s1,
406 1 : buffers::buffer_size(buf_));
407 1 : buffers::buffer_copy(s2, buffers::const_buffer(
408 : "\r\n"
409 : "0\r\n"
410 : "\r\n", 7));
411 1 : out_[1] = s1;
412 1 : out_[out_.size() - 1] = s2;
413 : }
414 : else
415 : {
416 : out_ = make_array(
417 : 1 + // header
418 0 : 2); // tmp1
419 : }
420 : }
421 :
422 7 : hp_ = &out_[0];
423 7 : *hp_ = { m.ph_->cbuf, m.ph_->size };
424 7 : }
425 :
426 : void
427 6 : serializer::
428 : start_source(
429 : message_view_base const& m,
430 : buffers::source* src)
431 : {
432 6 : st_ = style::source;
433 6 : src_ = src;
434 : out_ = make_array(
435 : 1 + // header
436 6 : 2); // tmp
437 6 : if(! cod_)
438 : {
439 : buffers::buffered_base::allocator a(
440 6 : ws_.data(), ws_.size()/2, false);
441 6 : src->init(a);
442 6 : ws_.reserve(a.size_used());
443 :
444 6 : tmp0_ = { ws_.data(), ws_.size() };
445 6 : if(tmp0_.capacity() <
446 : 18 + // chunk size
447 : 1 + // body (1 byte)
448 : 2 + // CRLF
449 : 5) // final chunk
450 0 : detail::throw_length_error();
451 : }
452 : else
453 : {
454 : buffers::buffered_base::allocator a(
455 0 : ws_.data(), ws_.size()/3, false);
456 0 : src->init(a);
457 0 : ws_.reserve(a.size_used());
458 :
459 0 : auto const n = ws_.size() / 2;
460 :
461 0 : tmp0_ = { ws_.data(), ws_.size() / 2 };
462 0 : ws_.reserve(n);
463 :
464 : // Buffer is too small
465 0 : if(ws_.size() < 1)
466 0 : detail::throw_length_error();
467 :
468 0 : tmp1_ = { ws_.data(), ws_.size() };
469 : }
470 :
471 6 : hp_ = &out_[0];
472 6 : *hp_ = { m.ph_->cbuf, m.ph_->size };
473 6 : }
474 :
475 : auto
476 0 : serializer::
477 : start_stream(
478 : message_view_base const& m) ->
479 : stream
480 : {
481 0 : start_init(m);
482 :
483 0 : st_ = style::stream;
484 : out_ = make_array(
485 : 1 + // header
486 0 : 2); // tmp
487 0 : if(! cod_)
488 : {
489 0 : tmp0_ = { ws_.data(), ws_.size() };
490 0 : if(tmp0_.capacity() <
491 : 18 + // chunk size
492 : 1 + // body (1 byte)
493 : 2 + // CRLF
494 : 5) // final chunk
495 0 : detail::throw_length_error();
496 : }
497 : else
498 : {
499 0 : auto const n = ws_.size() / 2;
500 0 : tmp0_ = { ws_.data(), n };
501 0 : ws_.reserve(n);
502 :
503 : // Buffer is too small
504 0 : if(ws_.size() < 1)
505 0 : detail::throw_length_error();
506 :
507 0 : tmp1_ = { ws_.data(), ws_.size() };
508 : }
509 :
510 0 : hp_ = &out_[0];
511 0 : *hp_ = { m.ph_->cbuf, m.ph_->size };
512 :
513 0 : more_ = true;
514 :
515 0 : return stream{*this};
516 : }
517 :
518 : //------------------------------------------------
519 :
520 : std::size_t
521 0 : serializer::
522 : stream::
523 : capacity() const
524 : {
525 0 : auto const n =
526 : chunked_overhead_ +
527 : 2 + // CRLF
528 : 5; // final chunk
529 0 : return sr_->tmp0_.capacity() - n; // VFALCO ?
530 : }
531 :
532 : std::size_t
533 0 : serializer::
534 : stream::
535 : size() const
536 : {
537 0 : return sr_->tmp0_.size();
538 : }
539 :
540 : auto
541 0 : serializer::
542 : stream::
543 : prepare(
544 : std::size_t n) const ->
545 : buffers_type
546 : {
547 0 : return sr_->tmp0_.prepare(n);
548 : }
549 :
550 : void
551 0 : serializer::
552 : stream::
553 : commit(std::size_t n) const
554 : {
555 0 : sr_->tmp0_.commit(n);
556 0 : }
557 :
558 : void
559 0 : serializer::
560 : stream::
561 : close() const
562 : {
563 : // Precondition violation
564 0 : if(! sr_->more_)
565 0 : detail::throw_logic_error();
566 0 : sr_->more_ = false;
567 0 : }
568 :
569 : //------------------------------------------------
570 :
571 : } // http_proto
572 : } // boost
573 :
574 : #endif
|