• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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