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