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