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