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