1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
11 #define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
12
13 #include <boost/beast/core/buffer_traits.hpp>
14 #include <boost/beast/core/detail/varint.hpp>
15 #include <boost/beast/http/error.hpp>
16 #include <boost/beast/http/detail/rfc7230.hpp>
17 #include <algorithm>
18
19 namespace boost {
20 namespace beast {
21 namespace http {
22
23 inline
24 chunk_header::
chunk_header(std::size_t size)25 chunk_header(std::size_t size)
26 : view_(
27 size,
28 net::const_buffer{nullptr, 0},
29 chunk_crlf{})
30 {
31 BOOST_ASSERT(size > 0);
32 }
33
34 inline
35 chunk_header::
chunk_header(std::size_t size,string_view extensions)36 chunk_header(
37 std::size_t size,
38 string_view extensions)
39 : view_(
40 size,
41 net::const_buffer{
42 extensions.data(), extensions.size()},
43 chunk_crlf{})
44 {
45 BOOST_ASSERT(size > 0);
46 }
47
48 template<class ChunkExtensions, class>
49 chunk_header::
chunk_header(std::size_t size,ChunkExtensions && extensions)50 chunk_header(
51 std::size_t size,
52 ChunkExtensions&& extensions)
53 : exts_(std::make_shared<detail::chunk_extensions_impl<
54 typename std::decay<ChunkExtensions>::type>>(
55 std::forward<ChunkExtensions>(extensions)))
56 , view_(
57 size,
58 exts_->str(),
59 chunk_crlf{})
60 {
61 static_assert(
62 detail::is_chunk_extensions<ChunkExtensions>::value,
63 "ChunkExtensions requirements not met");
64 BOOST_ASSERT(size > 0);
65 }
66
67 template<class ChunkExtensions, class Allocator, class>
68 chunk_header::
chunk_header(std::size_t size,ChunkExtensions && extensions,Allocator const & allocator)69 chunk_header(
70 std::size_t size,
71 ChunkExtensions&& extensions,
72 Allocator const& allocator)
73 : exts_(std::allocate_shared<detail::chunk_extensions_impl<
74 typename std::decay<ChunkExtensions>::type>>(allocator,
75 std::forward<ChunkExtensions>(extensions)))
76 , view_(
77 size,
78 exts_->str(),
79 chunk_crlf{})
80 {
81 static_assert(
82 detail::is_chunk_extensions<ChunkExtensions>::value,
83 "ChunkExtensions requirements not met");
84 BOOST_ASSERT(size > 0);
85 }
86
87 //------------------------------------------------------------------------------
88
89 template<class ConstBufferSequence>
90 chunk_body<ConstBufferSequence>::
chunk_body(ConstBufferSequence const & buffers)91 chunk_body(ConstBufferSequence const& buffers)
92 : view_(
93 buffer_bytes(buffers),
94 net::const_buffer{nullptr, 0},
95 chunk_crlf{},
96 buffers,
97 chunk_crlf{})
98 {
99 }
100
101 template<class ConstBufferSequence>
102 chunk_body<ConstBufferSequence>::
chunk_body(ConstBufferSequence const & buffers,string_view extensions)103 chunk_body(
104 ConstBufferSequence const& buffers,
105 string_view extensions)
106 : view_(
107 buffer_bytes(buffers),
108 net::const_buffer{
109 extensions.data(), extensions.size()},
110 chunk_crlf{},
111 buffers,
112 chunk_crlf{})
113 {
114 }
115
116 template<class ConstBufferSequence>
117 template<class ChunkExtensions, class>
118 chunk_body<ConstBufferSequence>::
chunk_body(ConstBufferSequence const & buffers,ChunkExtensions && extensions)119 chunk_body(
120 ConstBufferSequence const& buffers,
121 ChunkExtensions&& extensions)
122 : exts_(std::make_shared<detail::chunk_extensions_impl<
123 typename std::decay<ChunkExtensions>::type>>(
124 std::forward<ChunkExtensions>(extensions)))
125 , view_(
126 buffer_bytes(buffers),
127 exts_->str(),
128 chunk_crlf{},
129 buffers,
130 chunk_crlf{})
131 {
132 }
133
134 template<class ConstBufferSequence>
135 template<class ChunkExtensions, class Allocator, class>
136 chunk_body<ConstBufferSequence>::
chunk_body(ConstBufferSequence const & buffers,ChunkExtensions && extensions,Allocator const & allocator)137 chunk_body(
138 ConstBufferSequence const& buffers,
139 ChunkExtensions&& extensions,
140 Allocator const& allocator)
141 : exts_(std::allocate_shared<detail::chunk_extensions_impl<
142 typename std::decay<ChunkExtensions>::type>>(allocator,
143 std::forward<ChunkExtensions>(extensions)))
144 , view_(
145 buffer_bytes(buffers),
146 exts_->str(),
147 chunk_crlf{},
148 buffers,
149 chunk_crlf{})
150 {
151 }
152
153 //------------------------------------------------------------------------------
154
155 template<class Trailer>
156 template<class Allocator>
157 auto
158 chunk_last<Trailer>::
prepare(Trailer const & trailer,Allocator const & allocator)159 prepare(Trailer const& trailer, Allocator const& allocator) ->
160 buffers_type
161 {
162 auto sp = std::allocate_shared<typename
163 Trailer::writer>(allocator, trailer);
164 sp_ = sp;
165 return sp->get();
166 }
167
168 template<class Trailer>
169 auto
170 chunk_last<Trailer>::
prepare(Trailer const & trailer,std::true_type)171 prepare(Trailer const& trailer, std::true_type) ->
172 buffers_type
173 {
174 auto sp = std::make_shared<
175 typename Trailer::writer>(trailer);
176 sp_ = sp;
177 return sp->get();
178 }
179
180 template<class Trailer>
181 auto
182 chunk_last<Trailer>::
prepare(Trailer const & trailer,std::false_type)183 prepare(Trailer const& trailer, std::false_type) ->
184 buffers_type
185 {
186 return trailer;
187 }
188
189 template<class Trailer>
190 chunk_last<Trailer>::
chunk_last()191 chunk_last()
192 : view_(
193 detail::chunk_size0{},
194 Trailer{})
195 {
196 }
197
198 template<class Trailer>
199 chunk_last<Trailer>::
chunk_last(Trailer const & trailer)200 chunk_last(Trailer const& trailer)
201 : view_(
202 detail::chunk_size0{},
203 prepare(trailer, is_fields<Trailer>{}))
204 {
205 }
206
207 template<class Trailer>
208 template<class DeducedTrailer, class Allocator, class>
209 chunk_last<Trailer>::
chunk_last(DeducedTrailer const & trailer,Allocator const & allocator)210 chunk_last(
211 DeducedTrailer const& trailer, Allocator const& allocator)
212 : view_(
213 detail::chunk_size0{},
214 prepare(trailer, allocator))
215 {
216 }
217
218 //------------------------------------------------------------------------------
219
220 template<class Allocator>
221 class basic_chunk_extensions<Allocator>::const_iterator
222 {
223 friend class basic_chunk_extensions;
224
225 using iter_type = char const*;
226
227 iter_type it_;
228 typename basic_chunk_extensions::value_type value_;
229
230 explicit
const_iterator(iter_type it)231 const_iterator(iter_type it)
232 : it_(it)
233 {
234 }
235
236 void
237 increment();
238
239 public:
240 using value_type = typename
241 basic_chunk_extensions::value_type;
242 using pointer = value_type const*;
243 using reference = value_type const&;
244 using difference_type = std::ptrdiff_t;
245 using iterator_category =
246 std::forward_iterator_tag;
247
248 const_iterator() = default;
249 const_iterator(const_iterator&& other) = default;
250 const_iterator(const_iterator const& other) = default;
251 const_iterator& operator=(const_iterator&& other) = default;
252 const_iterator& operator=(const_iterator const& other) = default;
253
254 bool
operator ==(const_iterator const & other) const255 operator==(const_iterator const& other) const
256 {
257 return it_ == other.it_;
258 }
259
260 bool
operator !=(const_iterator const & other) const261 operator!=(const_iterator const& other) const
262 {
263 return !(*this == other);
264 }
265
266 reference
267 operator*();
268
269 pointer
operator ->()270 operator->()
271 {
272 return &(**this);
273 }
274
275 const_iterator&
operator ++()276 operator++()
277 {
278 increment();
279 return *this;
280 }
281
282 const_iterator
operator ++(int)283 operator++(int)
284 {
285 auto temp = *this;
286 increment();
287 return temp;
288 }
289 };
290
291 template<class Allocator>
292 void
293 basic_chunk_extensions<Allocator>::
294 const_iterator::
increment()295 increment()
296 {
297 using beast::detail::varint_read;
298 auto n = varint_read(it_);
299 it_ += n;
300 n = varint_read(it_);
301 it_ += n;
302 }
303
304 template<class Allocator>
305 auto
306 basic_chunk_extensions<Allocator>::
307 const_iterator::
operator *()308 operator*() ->
309 reference
310 {
311 using beast::detail::varint_read;
312 auto it = it_;
313 auto n = varint_read(it);
314 value_.first = string_view{it, n};
315 it += n;
316 n = varint_read(it);
317 value_.second = string_view{it, n};
318 return value_;
319 }
320
321 //------------------------------------------------------------------------------
322
323 template<class Allocator>
324 template<class FwdIt>
325 FwdIt
326 basic_chunk_extensions<Allocator>::
do_parse(FwdIt it,FwdIt last,error_code & ec)327 do_parse(FwdIt it, FwdIt last, error_code& ec)
328 {
329 /*
330 chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
331 BWS = *( SP / HTAB ) ; "Bad White Space"
332 chunk-ext-name = token
333 chunk-ext-val = token / quoted-string
334 token = 1*tchar
335 quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
336 qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
337 quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
338 obs-text = %x80-FF
339
340 https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
341 */
342 using beast::detail::varint_size;
343 using beast::detail::varint_write;
344 using CharT = char;
345 using Traits = std::char_traits<CharT>;
346 range_.reserve(static_cast<std::size_t>(
347 std::distance(it, last) * 1.2));
348 range_.resize(0);
349 auto const emit_string =
350 [this](FwdIt from, FwdIt to)
351 {
352 auto const len =
353 std::distance(from, to);
354 auto const offset = range_.size();
355 range_.resize(
356 offset +
357 varint_size(len) +
358 len);
359 auto dest = &range_[offset];
360 varint_write(dest, len);
361 Traits::copy(dest, from, len);
362 };
363 auto const emit_string_plus_empty =
364 [this](FwdIt from, FwdIt to)
365 {
366 auto const len =
367 std::distance(from, to);
368 auto const offset = range_.size();
369 range_.resize(
370 offset +
371 varint_size(len) +
372 len +
373 varint_size(0));
374 auto dest = &range_[offset];
375 varint_write(dest, len);
376 Traits::copy(dest, from, len);
377 dest += len;
378 varint_write(dest, 0);
379 };
380 auto const emit_empty_string =
381 [this]
382 {
383 auto const offset = range_.size();
384 range_.resize(offset + varint_size(0));
385 auto dest = &range_[offset];
386 varint_write(dest, 0);
387 };
388 loop:
389 if(it == last)
390 {
391 ec = {};
392 return it;
393 }
394 // BWS
395 if(*it == ' ' || *it == '\t')
396 {
397 for(;;)
398 {
399 ++it;
400 if(it == last)
401 {
402 ec = error::bad_chunk_extension;
403 return it;
404 }
405 if(*it != ' ' && *it != '\t')
406 break;
407 }
408 }
409 // ';'
410 if(*it != ';')
411 {
412 ec = error::bad_chunk_extension;
413 return it;
414 }
415 semi:
416 ++it; // skip ';'
417 // BWS
418 for(;;)
419 {
420 if(it == last)
421 {
422 ec = error::bad_chunk_extension;
423 return it;
424 }
425 if(*it != ' ' && *it != '\t')
426 break;
427 ++it;
428 }
429 // chunk-ext-name
430 {
431 if(! detail::is_token_char(*it))
432 {
433 ec = error::bad_chunk_extension;
434 return it;
435 }
436 auto const first = it;
437 for(;;)
438 {
439 ++it;
440 if(it == last)
441 {
442 emit_string_plus_empty(first, it);
443 return it;
444 }
445 if(! detail::is_token_char(*it))
446 break;
447 }
448 emit_string(first, it);
449 }
450 // BWS [ ";" / "=" ]
451 for(;;)
452 {
453 if(*it != ' ' && *it != '\t')
454 break;
455 ++it;
456 if(it == last)
457 {
458 ec = error::bad_chunk_extension;
459 return it;
460 }
461 }
462 if(*it == ';')
463 {
464 emit_empty_string();
465 goto semi;
466 }
467 if(*it != '=')
468 {
469 ec = error::bad_chunk_extension;
470 return it;
471 }
472 ++it; // skip '='
473 // BWS
474 for(;;)
475 {
476 if(it == last)
477 {
478 ec = error::bad_chunk_extension;
479 return it;
480 }
481 if(*it != ' ' && *it != '\t')
482 break;
483 ++it;
484 }
485 // chunk-ext-val
486 if(*it != '"')
487 {
488 // token
489 if(! detail::is_token_char(*it))
490 {
491 ec = error::bad_chunk_extension;
492 return it;
493 }
494 auto const first = it;
495 for(;;)
496 {
497 ++it;
498 if(it == last)
499 break;
500 if(! detail::is_token_char(*it))
501 break;
502 }
503 emit_string(first, it);
504 if(it == last)
505 return it;
506 }
507 else
508 {
509 // quoted-string
510 auto const first = ++it; // skip DQUOTE
511 // first pass, count chars
512 std::size_t len = 0;
513 for(;;)
514 {
515 if(it == last)
516 {
517 ec = error::bad_chunk_extension;
518 return it;
519 }
520 if(*it == '"')
521 break;
522 if(*it == '\\')
523 {
524 ++it;
525 if(it == last)
526 {
527 ec = error::bad_chunk_extension;
528 return it;
529 }
530 }
531 ++len;
532 ++it;
533 }
534 // now build the string
535 auto const offset = range_.size();
536 range_.resize(
537 offset +
538 varint_size(len) +
539 len);
540 auto dest = &range_[offset];
541 varint_write(dest, len);
542 it = first;
543 for(;;)
544 {
545 BOOST_ASSERT(it != last);
546 if(*it == '"')
547 break;
548 if(*it == '\\')
549 {
550 ++it;
551 BOOST_ASSERT(it != last);
552 }
553 Traits::assign(*dest++, *it++);
554 }
555 ++it; // skip DQUOTE
556 }
557 goto loop;
558 }
559
560 template<class Allocator>
561 void
562 basic_chunk_extensions<Allocator>::
do_insert(string_view name,string_view value)563 do_insert(string_view name, string_view value)
564 {
565 /*
566 chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
567 chunk-ext-name = token
568 chunk-ext-val = token / quoted-string
569 token = 1*tchar
570 quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
571 qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
572 quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
573 obs-text = %x80-FF
574 */
575 if(value.empty())
576 {
577 s_.reserve(1 + name.size());
578 s_.push_back(';');
579 s_.append(name.data(), name.size());
580 return;
581 }
582
583 bool is_token = true;
584 for(auto const c : value)
585 {
586 if(! detail::is_token_char(c))
587 {
588 is_token = false;
589 break;
590 }
591 }
592 if(is_token)
593 {
594 // token
595 s_.reserve(1 + name.size() + 1 + value.size());
596 s_.push_back(';');
597 s_.append(name.data(), name.size());
598 s_.push_back('=');
599 s_.append(value.data(), value.size());
600 }
601 else
602 {
603 // quoted-string
604 s_.reserve(
605 1 + name.size() + 1 +
606 1 + value.size() + 20 + 1);
607 s_.push_back(';');
608 s_.append(name.data(), name.size());
609 s_.append("=\"", 2);
610 for(auto const c : value)
611 {
612 if(c == '\\')
613 s_.append(R"(\\)", 2);
614 else if(c == '\"')
615 s_.append(R"(\")", 2);
616 else
617 s_.push_back(c);
618 }
619 s_.push_back('"');
620 }
621 }
622
623 template<class Allocator>
624 void
625 basic_chunk_extensions<Allocator>::
parse(string_view s,error_code & ec)626 parse(string_view s, error_code& ec)
627 {
628 do_parse(s.data(), s.data() + s.size(), ec);
629 if(! ec)
630 {
631 s_.clear();
632 for(auto const& v : *this)
633 do_insert(v.first, v.second);
634 }
635 }
636
637 template<class Allocator>
638 void
639 basic_chunk_extensions<Allocator>::
insert(string_view name)640 insert(string_view name)
641 {
642 do_insert(name, {});
643
644 using beast::detail::varint_size;
645 using beast::detail::varint_write;
646 auto const offset = range_.size();
647 range_.resize(
648 offset +
649 varint_size(name.size()) +
650 name.size() +
651 varint_size(0));
652 auto dest = &range_[offset];
653 varint_write(dest, name.size());
654 std::memcpy(dest, name.data(), name.size());
655 dest += name.size();
656 varint_write(dest, 0);
657 }
658
659 template<class Allocator>
660 void
661 basic_chunk_extensions<Allocator>::
insert(string_view name,string_view value)662 insert(string_view name, string_view value)
663 {
664 do_insert(name, value);
665
666 using beast::detail::varint_size;
667 using beast::detail::varint_write;
668 auto const offset = range_.size();
669 range_.resize(
670 offset +
671 varint_size(name.size()) +
672 name.size() +
673 varint_size(value.size()) +
674 value.size());
675 auto dest = &range_[offset];
676 varint_write(dest, name.size());
677 std::memcpy(dest, name.data(), name.size());
678 dest += name.size();
679 varint_write(dest, value.size());
680 std::memcpy(dest, value.data(), value.size());
681 }
682
683 template<class Allocator>
684 auto
685 basic_chunk_extensions<Allocator>::
begin() const686 begin() const ->
687 const_iterator
688 {
689 return const_iterator{range_.data()};
690 }
691
692 template<class Allocator>
693 auto
694 basic_chunk_extensions<Allocator>::
end() const695 end() const ->
696 const_iterator
697 {
698 return const_iterator{
699 range_.data() + range_.size()};
700 }
701
702 } // http
703 } // beast
704 } // boost
705
706 #endif
707