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_FIELDS_HPP
11 #define BOOST_BEAST_HTTP_IMPL_FIELDS_HPP
12 
13 #include <boost/beast/core/buffers_cat.hpp>
14 #include <boost/beast/core/string.hpp>
15 #include <boost/beast/core/detail/buffers_ref.hpp>
16 #include <boost/beast/core/detail/clamp.hpp>
17 #include <boost/beast/core/detail/static_string.hpp>
18 #include <boost/beast/core/detail/temporary_buffer.hpp>
19 #include <boost/beast/core/static_string.hpp>
20 #include <boost/beast/http/verb.hpp>
21 #include <boost/beast/http/rfc7230.hpp>
22 #include <boost/beast/http/status.hpp>
23 #include <boost/beast/http/chunk_encode.hpp>
24 #include <boost/core/exchange.hpp>
25 #include <boost/throw_exception.hpp>
26 #include <stdexcept>
27 #include <string>
28 
29 namespace boost {
30 namespace beast {
31 namespace http {
32 
33 template<class Allocator>
34 class basic_fields<Allocator>::writer
35 {
36 public:
37     using iter_type = typename list_t::const_iterator;
38 
39     struct field_iterator
40     {
41         iter_type it_;
42 
43         using value_type = net::const_buffer;
44         using pointer = value_type const*;
45         using reference = value_type const;
46         using difference_type = std::ptrdiff_t;
47         using iterator_category =
48             std::bidirectional_iterator_tag;
49 
50         field_iterator() = default;
51         field_iterator(field_iterator&& other) = default;
52         field_iterator(field_iterator const& other) = default;
53         field_iterator& operator=(field_iterator&& other) = default;
54         field_iterator& operator=(field_iterator const& other) = default;
55 
56         explicit
field_iteratorboost::beast::http::basic_fields::writer::field_iterator57         field_iterator(iter_type it)
58             : it_(it)
59         {
60         }
61 
62         bool
operator ==boost::beast::http::basic_fields::writer::field_iterator63         operator==(field_iterator const& other) const
64         {
65             return it_ == other.it_;
66         }
67 
68         bool
operator !=boost::beast::http::basic_fields::writer::field_iterator69         operator!=(field_iterator const& other) const
70         {
71             return !(*this == other);
72         }
73 
74         reference
operator *boost::beast::http::basic_fields::writer::field_iterator75         operator*() const
76         {
77             return it_->buffer();
78         }
79 
80         field_iterator&
operator ++boost::beast::http::basic_fields::writer::field_iterator81         operator++()
82         {
83             ++it_;
84             return *this;
85         }
86 
87         field_iterator
operator ++boost::beast::http::basic_fields::writer::field_iterator88         operator++(int)
89         {
90             auto temp = *this;
91             ++(*this);
92             return temp;
93         }
94 
95         field_iterator&
operator --boost::beast::http::basic_fields::writer::field_iterator96         operator--()
97         {
98             --it_;
99             return *this;
100         }
101 
102         field_iterator
operator --boost::beast::http::basic_fields::writer::field_iterator103         operator--(int)
104         {
105             auto temp = *this;
106             --(*this);
107             return temp;
108         }
109     };
110 
111     class field_range
112     {
113         field_iterator first_;
114         field_iterator last_;
115 
116     public:
117         using const_iterator =
118             field_iterator;
119 
120         using value_type =
121             typename const_iterator::value_type;
122 
field_range(iter_type first,iter_type last)123         field_range(iter_type first, iter_type last)
124             : first_(first)
125             , last_(last)
126         {
127         }
128 
129         const_iterator
begin() const130         begin() const
131         {
132             return first_;
133         }
134 
135         const_iterator
end() const136         end() const
137         {
138             return last_;
139         }
140     };
141 
142     using view_type = buffers_cat_view<
143         net::const_buffer,
144         net::const_buffer,
145         net::const_buffer,
146         field_range,
147         chunk_crlf>;
148 
149     basic_fields const& f_;
150     boost::optional<view_type> view_;
151     char buf_[13];
152 
153 public:
154     using const_buffers_type =
155         beast::detail::buffers_ref<view_type>;
156 
157     writer(basic_fields const& f,
158         unsigned version, verb v);
159 
160     writer(basic_fields const& f,
161         unsigned version, unsigned code);
162 
163     writer(basic_fields const& f);
164 
165     const_buffers_type
get() const166     get() const
167     {
168         return const_buffers_type(*view_);
169     }
170 };
171 
172 template<class Allocator>
173 basic_fields<Allocator>::writer::
writer(basic_fields const & f)174 writer(basic_fields const& f)
175     : f_(f)
176 {
177     view_.emplace(
178         net::const_buffer{nullptr, 0},
179         net::const_buffer{nullptr, 0},
180         net::const_buffer{nullptr, 0},
181         field_range(f_.list_.begin(), f_.list_.end()),
182         chunk_crlf());
183 }
184 
185 template<class Allocator>
186 basic_fields<Allocator>::writer::
writer(basic_fields const & f,unsigned version,verb v)187 writer(basic_fields const& f,
188         unsigned version, verb v)
189     : f_(f)
190 {
191 /*
192     request
193         "<method>"
194         " <target>"
195         " HTTP/X.Y\r\n" (11 chars)
196 */
197     string_view sv;
198     if(v == verb::unknown)
199         sv = f_.get_method_impl();
200     else
201         sv = to_string(v);
202 
203     // target_or_reason_ has a leading SP
204 
205     buf_[0] = ' ';
206     buf_[1] = 'H';
207     buf_[2] = 'T';
208     buf_[3] = 'T';
209     buf_[4] = 'P';
210     buf_[5] = '/';
211     buf_[6] = '0' + static_cast<char>(version / 10);
212     buf_[7] = '.';
213     buf_[8] = '0' + static_cast<char>(version % 10);
214     buf_[9] = '\r';
215     buf_[10]= '\n';
216 
217     view_.emplace(
218         net::const_buffer{sv.data(), sv.size()},
219         net::const_buffer{
220             f_.target_or_reason_.data(),
221             f_.target_or_reason_.size()},
222         net::const_buffer{buf_, 11},
223         field_range(f_.list_.begin(), f_.list_.end()),
224         chunk_crlf());
225 }
226 
227 template<class Allocator>
228 basic_fields<Allocator>::writer::
writer(basic_fields const & f,unsigned version,unsigned code)229 writer(basic_fields const& f,
230         unsigned version, unsigned code)
231     : f_(f)
232 {
233 /*
234     response
235         "HTTP/X.Y ### " (13 chars)
236         "<reason>"
237         "\r\n"
238 */
239     buf_[0] = 'H';
240     buf_[1] = 'T';
241     buf_[2] = 'T';
242     buf_[3] = 'P';
243     buf_[4] = '/';
244     buf_[5] = '0' + static_cast<char>(version / 10);
245     buf_[6] = '.';
246     buf_[7] = '0' + static_cast<char>(version % 10);
247     buf_[8] = ' ';
248     buf_[9] = '0' + static_cast<char>(code / 100);
249     buf_[10]= '0' + static_cast<char>((code / 10) % 10);
250     buf_[11]= '0' + static_cast<char>(code % 10);
251     buf_[12]= ' ';
252 
253     string_view sv;
254     if(! f_.target_or_reason_.empty())
255         sv = f_.target_or_reason_;
256     else
257         sv = obsolete_reason(static_cast<status>(code));
258 
259     view_.emplace(
260         net::const_buffer{buf_, 13},
261         net::const_buffer{sv.data(), sv.size()},
262         net::const_buffer{"\r\n", 2},
263         field_range(f_.list_.begin(), f_.list_.end()),
264         chunk_crlf{});
265 }
266 
267 //------------------------------------------------------------------------------
268 
269 template<class Allocator>
270 char*
271 basic_fields<Allocator>::
272 value_type::
data() const273 data() const
274 {
275     return const_cast<char*>(
276         reinterpret_cast<char const*>(
277             static_cast<element const*>(this) + 1));
278 }
279 
280 template<class Allocator>
281 net::const_buffer
282 basic_fields<Allocator>::
283 value_type::
buffer() const284 buffer() const
285 {
286     return net::const_buffer{data(),
287         static_cast<std::size_t>(off_) + len_ + 2};
288 }
289 
290 template<class Allocator>
291 basic_fields<Allocator>::
292 value_type::
value_type(field name,string_view sname,string_view value)293 value_type(field name,
294     string_view sname, string_view value)
295     : off_(static_cast<off_t>(sname.size() + 2))
296     , len_(static_cast<off_t>(value.size()))
297     , f_(name)
298 {
299     //BOOST_ASSERT(name == field::unknown ||
300     //    iequals(sname, to_string(name)));
301     char* p = data();
302     p[off_-2] = ':';
303     p[off_-1] = ' ';
304     p[off_ + len_] = '\r';
305     p[off_ + len_ + 1] = '\n';
306     sname.copy(p, sname.size());
307     value.copy(p + off_, value.size());
308 }
309 
310 template<class Allocator>
311 field
312 basic_fields<Allocator>::
313 value_type::
name() const314 name() const
315 {
316     return f_;
317 }
318 
319 template<class Allocator>
320 string_view const
321 basic_fields<Allocator>::
322 value_type::
name_string() const323 name_string() const
324 {
325     return {data(),
326         static_cast<std::size_t>(off_ - 2)};
327 }
328 
329 template<class Allocator>
330 string_view const
331 basic_fields<Allocator>::
332 value_type::
value() const333 value() const
334 {
335     return {data() + off_,
336         static_cast<std::size_t>(len_)};
337 }
338 
339 template<class Allocator>
340 basic_fields<Allocator>::
341 element::
element(field name,string_view sname,string_view value)342 element(field name,
343     string_view sname, string_view value)
344     : value_type(name, sname, value)
345 {
346 }
347 
348 //------------------------------------------------------------------------------
349 
350 template<class Allocator>
351 basic_fields<Allocator>::
~basic_fields()352 ~basic_fields()
353 {
354     delete_list();
355     realloc_string(method_, {});
356     realloc_string(
357         target_or_reason_, {});
358 }
359 
360 template<class Allocator>
361 basic_fields<Allocator>::
basic_fields(Allocator const & alloc)362 basic_fields(Allocator const& alloc) noexcept
363     : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
364 {
365 }
366 
367 template<class Allocator>
368 basic_fields<Allocator>::
basic_fields(basic_fields && other)369 basic_fields(basic_fields&& other) noexcept
370     : boost::empty_value<Allocator>(boost::empty_init_t(),
371         std::move(other.get()))
372     , set_(std::move(other.set_))
373     , list_(std::move(other.list_))
374     , method_(boost::exchange(other.method_, {}))
375     , target_or_reason_(boost::exchange(other.target_or_reason_, {}))
376 {
377 }
378 
379 template<class Allocator>
380 basic_fields<Allocator>::
basic_fields(basic_fields && other,Allocator const & alloc)381 basic_fields(basic_fields&& other, Allocator const& alloc)
382     : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
383 {
384     if(this->get() != other.get())
385     {
386         copy_all(other);
387     }
388     else
389     {
390         set_ = std::move(other.set_);
391         list_ = std::move(other.list_);
392         method_ = other.method_;
393         target_or_reason_ = other.target_or_reason_;
394     }
395 }
396 
397 template<class Allocator>
398 basic_fields<Allocator>::
basic_fields(basic_fields const & other)399 basic_fields(basic_fields const& other)
400     : boost::empty_value<Allocator>(boost::empty_init_t(), alloc_traits::
401         select_on_container_copy_construction(other.get()))
402 {
403     copy_all(other);
404 }
405 
406 template<class Allocator>
407 basic_fields<Allocator>::
basic_fields(basic_fields const & other,Allocator const & alloc)408 basic_fields(basic_fields const& other,
409         Allocator const& alloc)
410     : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
411 {
412     copy_all(other);
413 }
414 
415 template<class Allocator>
416 template<class OtherAlloc>
417 basic_fields<Allocator>::
basic_fields(basic_fields<OtherAlloc> const & other)418 basic_fields(basic_fields<OtherAlloc> const& other)
419 {
420     copy_all(other);
421 }
422 
423 template<class Allocator>
424 template<class OtherAlloc>
425 basic_fields<Allocator>::
basic_fields(basic_fields<OtherAlloc> const & other,Allocator const & alloc)426 basic_fields(basic_fields<OtherAlloc> const& other,
427         Allocator const& alloc)
428     : boost::empty_value<Allocator>(boost::empty_init_t(), alloc)
429 {
430     copy_all(other);
431 }
432 
433 template<class Allocator>
434 auto
435 basic_fields<Allocator>::
operator =(basic_fields && other)436 operator=(basic_fields&& other) noexcept(
437     alloc_traits::propagate_on_container_move_assignment::value)
438       -> basic_fields&
439 {
440     static_assert(is_nothrow_move_assignable<Allocator>::value,
441         "Allocator must be noexcept assignable.");
442     if(this == &other)
443         return *this;
444     move_assign(other, std::integral_constant<bool,
445         alloc_traits:: propagate_on_container_move_assignment::value>{});
446     return *this;
447 }
448 
449 template<class Allocator>
450 auto
451 basic_fields<Allocator>::
operator =(basic_fields const & other)452 operator=(basic_fields const& other) ->
453     basic_fields&
454 {
455     copy_assign(other, std::integral_constant<bool,
456         alloc_traits::propagate_on_container_copy_assignment::value>{});
457     return *this;
458 }
459 
460 template<class Allocator>
461 template<class OtherAlloc>
462 auto
463 basic_fields<Allocator>::
operator =(basic_fields<OtherAlloc> const & other)464 operator=(basic_fields<OtherAlloc> const& other) ->
465     basic_fields&
466 {
467     clear_all();
468     copy_all(other);
469     return *this;
470 }
471 
472 //------------------------------------------------------------------------------
473 //
474 // Element access
475 //
476 //------------------------------------------------------------------------------
477 
478 template<class Allocator>
479 string_view const
480 basic_fields<Allocator>::
at(field name) const481 at(field name) const
482 {
483     BOOST_ASSERT(name != field::unknown);
484     auto const it = find(name);
485     if(it == end())
486         BOOST_THROW_EXCEPTION(std::out_of_range{
487             "field not found"});
488     return it->value();
489 }
490 
491 template<class Allocator>
492 string_view const
493 basic_fields<Allocator>::
at(string_view name) const494 at(string_view name) const
495 {
496     auto const it = find(name);
497     if(it == end())
498         BOOST_THROW_EXCEPTION(std::out_of_range{
499             "field not found"});
500     return it->value();
501 }
502 
503 template<class Allocator>
504 string_view const
505 basic_fields<Allocator>::
operator [](field name) const506 operator[](field name) const
507 {
508     BOOST_ASSERT(name != field::unknown);
509     auto const it = find(name);
510     if(it == end())
511         return {};
512     return it->value();
513 }
514 
515 template<class Allocator>
516 string_view const
517 basic_fields<Allocator>::
operator [](string_view name) const518 operator[](string_view name) const
519 {
520     auto const it = find(name);
521     if(it == end())
522         return {};
523     return it->value();
524 }
525 
526 //------------------------------------------------------------------------------
527 //
528 // Modifiers
529 //
530 //------------------------------------------------------------------------------
531 
532 template<class Allocator>
533 void
534 basic_fields<Allocator>::
clear()535 clear()
536 {
537     delete_list();
538     set_.clear();
539     list_.clear();
540 }
541 
542 template<class Allocator>
543 inline
544 void
545 basic_fields<Allocator>::
insert(field name,string_view const & value)546 insert(field name, string_view const& value)
547 {
548     BOOST_ASSERT(name != field::unknown);
549     insert(name, to_string(name), value);
550 }
551 
552 template<class Allocator>
553 void
554 basic_fields<Allocator>::
insert(string_view sname,string_view const & value)555 insert(string_view sname, string_view const& value)
556 {
557     auto const name =
558         string_to_field(sname);
559     insert(name, sname, value);
560 }
561 
562 template<class Allocator>
563 void
564 basic_fields<Allocator>::
insert(field name,string_view sname,string_view const & value)565 insert(field name,
566     string_view sname, string_view const& value)
567 {
568     auto& e = new_element(name, sname,
569         static_cast<string_view>(value));
570     auto const before =
571         set_.upper_bound(sname, key_compare{});
572     if(before == set_.begin())
573     {
574         BOOST_ASSERT(count(sname) == 0);
575         set_.insert_before(before, e);
576         list_.push_back(e);
577         return;
578     }
579     auto const last = std::prev(before);
580     // VFALCO is it worth comparing `field name` first?
581     if(! beast::iequals(sname, last->name_string()))
582     {
583         BOOST_ASSERT(count(sname) == 0);
584         set_.insert_before(before, e);
585         list_.push_back(e);
586         return;
587     }
588     // keep duplicate fields together in the list
589     set_.insert_before(before, e);
590     list_.insert(++list_.iterator_to(*last), e);
591 }
592 
593 template<class Allocator>
594 void
595 basic_fields<Allocator>::
set(field name,string_view const & value)596 set(field name, string_view const& value)
597 {
598     BOOST_ASSERT(name != field::unknown);
599     set_element(new_element(name, to_string(name),
600         static_cast<string_view>(value)));
601 }
602 
603 template<class Allocator>
604 void
605 basic_fields<Allocator>::
set(string_view sname,string_view const & value)606 set(string_view sname, string_view const& value)
607 {
608     set_element(new_element(
609         string_to_field(sname), sname, value));
610 }
611 
612 template<class Allocator>
613 auto
614 basic_fields<Allocator>::
erase(const_iterator pos)615 erase(const_iterator pos) ->
616     const_iterator
617 {
618     auto next = pos;
619     auto& e = *next++;
620     set_.erase(set_.iterator_to(e));
621     list_.erase(pos);
622     delete_element(const_cast<element&>(e));
623     return next;
624 }
625 
626 template<class Allocator>
627 std::size_t
628 basic_fields<Allocator>::
erase(field name)629 erase(field name)
630 {
631     BOOST_ASSERT(name != field::unknown);
632     return erase(to_string(name));
633 }
634 
635 template<class Allocator>
636 std::size_t
637 basic_fields<Allocator>::
erase(string_view name)638 erase(string_view name)
639 {
640     std::size_t n =0;
641     set_.erase_and_dispose(name, key_compare{},
642         [&](element* e)
643         {
644             ++n;
645             list_.erase(list_.iterator_to(*e));
646             delete_element(*e);
647         });
648     return n;
649 }
650 
651 template<class Allocator>
652 void
653 basic_fields<Allocator>::
swap(basic_fields<Allocator> & other)654 swap(basic_fields<Allocator>& other)
655 {
656     swap(other, std::integral_constant<bool,
657         alloc_traits::propagate_on_container_swap::value>{});
658 }
659 
660 template<class Allocator>
661 void
swap(basic_fields<Allocator> & lhs,basic_fields<Allocator> & rhs)662 swap(
663     basic_fields<Allocator>& lhs,
664     basic_fields<Allocator>& rhs)
665 {
666     lhs.swap(rhs);
667 }
668 
669 //------------------------------------------------------------------------------
670 //
671 // Lookup
672 //
673 //------------------------------------------------------------------------------
674 
675 template<class Allocator>
676 inline
677 std::size_t
678 basic_fields<Allocator>::
count(field name) const679 count(field name) const
680 {
681     BOOST_ASSERT(name != field::unknown);
682     return count(to_string(name));
683 }
684 
685 template<class Allocator>
686 std::size_t
687 basic_fields<Allocator>::
count(string_view name) const688 count(string_view name) const
689 {
690     return set_.count(name, key_compare{});
691 }
692 
693 template<class Allocator>
694 inline
695 auto
696 basic_fields<Allocator>::
find(field name) const697 find(field name) const ->
698     const_iterator
699 {
700     BOOST_ASSERT(name != field::unknown);
701     return find(to_string(name));
702 }
703 
704 template<class Allocator>
705 auto
706 basic_fields<Allocator>::
find(string_view name) const707 find(string_view name) const ->
708     const_iterator
709 {
710     auto const it = set_.find(
711         name, key_compare{});
712     if(it == set_.end())
713         return list_.end();
714     return list_.iterator_to(*it);
715 }
716 
717 template<class Allocator>
718 inline
719 auto
720 basic_fields<Allocator>::
equal_range(field name) const721 equal_range(field name) const ->
722     std::pair<const_iterator, const_iterator>
723 {
724     BOOST_ASSERT(name != field::unknown);
725     return equal_range(to_string(name));
726 }
727 
728 template<class Allocator>
729 auto
730 basic_fields<Allocator>::
equal_range(string_view name) const731 equal_range(string_view name) const ->
732     std::pair<const_iterator, const_iterator>
733 {
734     auto result =
735         set_.equal_range(name, key_compare{});
736     if(result.first == result.second)
737         return {list_.end(), list_.end()};
738     return {
739         list_.iterator_to(*result.first),
740         ++list_.iterator_to(*(--result.second))};
741 }
742 
743 //------------------------------------------------------------------------------
744 
745 namespace detail {
746 
747 struct iequals_predicate
748 {
749     bool
operator ()boost::beast::http::detail::iequals_predicate750     operator()(string_view s) const
751     {
752         return beast::iequals(s, sv1) || beast::iequals(s, sv2);
753     }
754 
755     string_view sv1;
756     string_view sv2;
757 };
758 
759 // Filter the last item in a token list
760 BOOST_BEAST_DECL
761 void
762 filter_token_list_last(
763     beast::detail::temporary_buffer& s,
764     string_view value,
765     iequals_predicate const& pred);
766 
767 BOOST_BEAST_DECL
768 void
769 keep_alive_impl(
770     beast::detail::temporary_buffer& s, string_view value,
771     unsigned version, bool keep_alive);
772 
773 } // detail
774 
775 //------------------------------------------------------------------------------
776 
777 // Fields
778 
779 template<class Allocator>
780 inline
781 string_view
782 basic_fields<Allocator>::
get_method_impl() const783 get_method_impl() const
784 {
785     return method_;
786 }
787 
788 template<class Allocator>
789 inline
790 string_view
791 basic_fields<Allocator>::
get_target_impl() const792 get_target_impl() const
793 {
794     if(target_or_reason_.empty())
795         return target_or_reason_;
796     return {
797         target_or_reason_.data() + 1,
798         target_or_reason_.size() - 1};
799 }
800 
801 template<class Allocator>
802 inline
803 string_view
804 basic_fields<Allocator>::
get_reason_impl() const805 get_reason_impl() const
806 {
807     return target_or_reason_;
808 }
809 
810 template<class Allocator>
811 bool
812 basic_fields<Allocator>::
get_chunked_impl() const813 get_chunked_impl() const
814 {
815     auto const te = token_list{
816         (*this)[field::transfer_encoding]};
817     for(auto it = te.begin(); it != te.end();)
818     {
819         auto const next = std::next(it);
820         if(next == te.end())
821             return beast::iequals(*it, "chunked");
822         it = next;
823     }
824     return false;
825 }
826 
827 template<class Allocator>
828 bool
829 basic_fields<Allocator>::
get_keep_alive_impl(unsigned version) const830 get_keep_alive_impl(unsigned version) const
831 {
832     auto const it = find(field::connection);
833     if(version < 11)
834     {
835         if(it == end())
836             return false;
837         return token_list{
838             it->value()}.exists("keep-alive");
839     }
840     if(it == end())
841         return true;
842     return ! token_list{
843         it->value()}.exists("close");
844 }
845 
846 template<class Allocator>
847 bool
848 basic_fields<Allocator>::
has_content_length_impl() const849 has_content_length_impl() const
850 {
851     return count(field::content_length) > 0;
852 }
853 
854 template<class Allocator>
855 inline
856 void
857 basic_fields<Allocator>::
set_method_impl(string_view s)858 set_method_impl(string_view s)
859 {
860     realloc_string(method_, s);
861 }
862 
863 template<class Allocator>
864 inline
865 void
866 basic_fields<Allocator>::
set_target_impl(string_view s)867 set_target_impl(string_view s)
868 {
869     realloc_target(
870         target_or_reason_, s);
871 }
872 
873 template<class Allocator>
874 inline
875 void
876 basic_fields<Allocator>::
set_reason_impl(string_view s)877 set_reason_impl(string_view s)
878 {
879     realloc_string(
880         target_or_reason_, s);
881 }
882 
883 template<class Allocator>
884 void
885 basic_fields<Allocator>::
set_chunked_impl(bool value)886 set_chunked_impl(bool value)
887 {
888     beast::detail::temporary_buffer buf;
889     auto it = find(field::transfer_encoding);
890     if(value)
891     {
892         // append "chunked"
893         if(it == end())
894         {
895             set(field::transfer_encoding, "chunked");
896             return;
897         }
898         auto const te = token_list{it->value()};
899         for(auto itt = te.begin();;)
900         {
901             auto const next = std::next(itt);
902             if(next == te.end())
903             {
904                 if(beast::iequals(*itt, "chunked"))
905                     return; // already set
906                 break;
907             }
908             itt = next;
909         }
910 
911         buf.append(it->value(), ", chunked");
912         set(field::transfer_encoding, buf.view());
913         return;
914     }
915     // filter "chunked"
916     if(it == end())
917         return;
918 
919     detail::filter_token_list_last(buf, it->value(), {"chunked", {}});
920     if(! buf.empty())
921         set(field::transfer_encoding, buf.view());
922     else
923         erase(field::transfer_encoding);
924 }
925 
926 template<class Allocator>
927 void
928 basic_fields<Allocator>::
set_content_length_impl(boost::optional<std::uint64_t> const & value)929 set_content_length_impl(
930     boost::optional<std::uint64_t> const& value)
931 {
932     if(! value)
933         erase(field::content_length);
934     else
935     {
936         set(field::content_length,
937             to_static_string(*value));
938     }
939 }
940 
941 template<class Allocator>
942 void
943 basic_fields<Allocator>::
set_keep_alive_impl(unsigned version,bool keep_alive)944 set_keep_alive_impl(
945     unsigned version, bool keep_alive)
946 {
947     // VFALCO What about Proxy-Connection ?
948     auto const value = (*this)[field::connection];
949     beast::detail::temporary_buffer buf;
950     detail::keep_alive_impl(buf, value, version, keep_alive);
951     if(buf.empty())
952         erase(field::connection);
953     else
954         set(field::connection, buf.view());
955 }
956 
957 //------------------------------------------------------------------------------
958 
959 template<class Allocator>
960 auto
961 basic_fields<Allocator>::
new_element(field name,string_view sname,string_view value)962 new_element(field name,
963     string_view sname, string_view value) ->
964         element&
965 {
966     if(sname.size() + 2 >
967             (std::numeric_limits<off_t>::max)())
968         BOOST_THROW_EXCEPTION(std::length_error{
969             "field name too large"});
970     if(value.size() + 2 >
971             (std::numeric_limits<off_t>::max)())
972         BOOST_THROW_EXCEPTION(std::length_error{
973             "field value too large"});
974     value = detail::trim(value);
975     std::uint16_t const off =
976         static_cast<off_t>(sname.size() + 2);
977     std::uint16_t const len =
978         static_cast<off_t>(value.size());
979     auto a = rebind_type{this->get()};
980     auto const p = alloc_traits::allocate(a,
981         (sizeof(element) + off + len + 2 + sizeof(align_type) - 1) /
982             sizeof(align_type));
983     return *(::new(p) element(name, sname, value));
984 }
985 
986 template<class Allocator>
987 void
988 basic_fields<Allocator>::
delete_element(element & e)989 delete_element(element& e)
990 {
991     auto a = rebind_type{this->get()};
992     auto const n =
993         (sizeof(element) + e.off_ + e.len_ + 2 + sizeof(align_type) - 1) /
994             sizeof(align_type);
995     e.~element();
996     alloc_traits::deallocate(a,
997         reinterpret_cast<align_type*>(&e), n);
998 }
999 
1000 template<class Allocator>
1001 void
1002 basic_fields<Allocator>::
set_element(element & e)1003 set_element(element& e)
1004 {
1005     auto it = set_.lower_bound(
1006         e.name_string(), key_compare{});
1007     if(it == set_.end() || ! beast::iequals(
1008         e.name_string(), it->name_string()))
1009     {
1010         set_.insert_before(it, e);
1011         list_.push_back(e);
1012         return;
1013     }
1014     for(;;)
1015     {
1016         auto next = it;
1017         ++next;
1018         set_.erase(it);
1019         list_.erase(list_.iterator_to(*it));
1020         delete_element(*it);
1021         it = next;
1022         if(it == set_.end() ||
1023             ! beast::iequals(e.name_string(), it->name_string()))
1024             break;
1025     }
1026     set_.insert_before(it, e);
1027     list_.push_back(e);
1028 }
1029 
1030 template<class Allocator>
1031 void
1032 basic_fields<Allocator>::
realloc_string(string_view & dest,string_view s)1033 realloc_string(string_view& dest, string_view s)
1034 {
1035     if(dest.empty() && s.empty())
1036         return;
1037     auto a = typename beast::detail::allocator_traits<
1038         Allocator>::template rebind_alloc<
1039             char>(this->get());
1040     char* p = nullptr;
1041     if(! s.empty())
1042     {
1043         p = a.allocate(s.size());
1044         s.copy(p, s.size());
1045     }
1046     if(! dest.empty())
1047         a.deallocate(const_cast<char*>(
1048             dest.data()), dest.size());
1049     if(p)
1050         dest = {p, s.size()};
1051     else
1052         dest = {};
1053 }
1054 
1055 template<class Allocator>
1056 void
1057 basic_fields<Allocator>::
realloc_target(string_view & dest,string_view s)1058 realloc_target(
1059     string_view& dest, string_view s)
1060 {
1061     // The target string are stored with an
1062     // extra space at the beginning to help
1063     // the writer class.
1064     if(dest.empty() && s.empty())
1065         return;
1066     auto a = typename beast::detail::allocator_traits<
1067         Allocator>::template rebind_alloc<
1068             char>(this->get());
1069     char* p = nullptr;
1070     if(! s.empty())
1071     {
1072         p = a.allocate(1 + s.size());
1073         p[0] = ' ';
1074         s.copy(p + 1, s.size());
1075     }
1076     if(! dest.empty())
1077         a.deallocate(const_cast<char*>(
1078             dest.data()), dest.size());
1079     if(p)
1080         dest = {p, 1 + s.size()};
1081     else
1082         dest = {};
1083 }
1084 
1085 template<class Allocator>
1086 template<class OtherAlloc>
1087 void
1088 basic_fields<Allocator>::
copy_all(basic_fields<OtherAlloc> const & other)1089 copy_all(basic_fields<OtherAlloc> const& other)
1090 {
1091     for(auto const& e : other.list_)
1092         insert(e.name(), e.name_string(), e.value());
1093     realloc_string(method_, other.method_);
1094     realloc_string(target_or_reason_,
1095         other.target_or_reason_);
1096 }
1097 
1098 template<class Allocator>
1099 void
1100 basic_fields<Allocator>::
clear_all()1101 clear_all()
1102 {
1103     clear();
1104     realloc_string(method_, {});
1105     realloc_string(target_or_reason_, {});
1106 }
1107 
1108 template<class Allocator>
1109 void
1110 basic_fields<Allocator>::
delete_list()1111 delete_list()
1112 {
1113     for(auto it = list_.begin(); it != list_.end();)
1114         delete_element(*it++);
1115 }
1116 
1117 //------------------------------------------------------------------------------
1118 
1119 template<class Allocator>
1120 inline
1121 void
1122 basic_fields<Allocator>::
move_assign(basic_fields & other,std::true_type)1123 move_assign(basic_fields& other, std::true_type)
1124 {
1125     clear_all();
1126     set_ = std::move(other.set_);
1127     list_ = std::move(other.list_);
1128     method_ = other.method_;
1129     target_or_reason_ = other.target_or_reason_;
1130     other.method_ = {};
1131     other.target_or_reason_ = {};
1132     this->get() = other.get();
1133 }
1134 
1135 template<class Allocator>
1136 inline
1137 void
1138 basic_fields<Allocator>::
move_assign(basic_fields & other,std::false_type)1139 move_assign(basic_fields& other, std::false_type)
1140 {
1141     clear_all();
1142     if(this->get() != other.get())
1143     {
1144         copy_all(other);
1145     }
1146     else
1147     {
1148         set_ = std::move(other.set_);
1149         list_ = std::move(other.list_);
1150         method_ = other.method_;
1151         target_or_reason_ = other.target_or_reason_;
1152         other.method_ = {};
1153         other.target_or_reason_ = {};
1154     }
1155 }
1156 
1157 template<class Allocator>
1158 inline
1159 void
1160 basic_fields<Allocator>::
copy_assign(basic_fields const & other,std::true_type)1161 copy_assign(basic_fields const& other, std::true_type)
1162 {
1163     clear_all();
1164     this->get() = other.get();
1165     copy_all(other);
1166 }
1167 
1168 template<class Allocator>
1169 inline
1170 void
1171 basic_fields<Allocator>::
copy_assign(basic_fields const & other,std::false_type)1172 copy_assign(basic_fields const& other, std::false_type)
1173 {
1174     clear_all();
1175     copy_all(other);
1176 }
1177 
1178 template<class Allocator>
1179 inline
1180 void
1181 basic_fields<Allocator>::
swap(basic_fields & other,std::true_type)1182 swap(basic_fields& other, std::true_type)
1183 {
1184     using std::swap;
1185     swap(this->get(), other.get());
1186     swap(set_, other.set_);
1187     swap(list_, other.list_);
1188     swap(method_, other.method_);
1189     swap(target_or_reason_, other.target_or_reason_);
1190 }
1191 
1192 template<class Allocator>
1193 inline
1194 void
1195 basic_fields<Allocator>::
swap(basic_fields & other,std::false_type)1196 swap(basic_fields& other, std::false_type)
1197 {
1198     BOOST_ASSERT(this->get() == other.get());
1199     using std::swap;
1200     swap(set_, other.set_);
1201     swap(list_, other.list_);
1202     swap(method_, other.method_);
1203     swap(target_or_reason_, other.target_or_reason_);
1204 }
1205 
1206 } // http
1207 } // beast
1208 } // boost
1209 
1210 #ifdef BOOST_BEAST_HEADER_ONLY
1211 #include <boost/beast/http/impl/fields.ipp>
1212 #endif
1213 
1214 #endif
1215