// // Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #ifndef BEAST_HTTP_BASIC_HEADERS_HPP #define BEAST_HTTP_BASIC_HEADERS_HPP #include #include #include #include #include #include #include #include #include #include #include #include namespace beast { namespace http { template class basic_headers; namespace detail { class basic_headers_base { public: struct value_type { std::string first; std::string second; value_type(boost::string_ref const& name_, boost::string_ref const& value_) : first(name_) , second(value_) { } boost::string_ref name() const { return first; } boost::string_ref value() const { return second; } }; protected: template friend class beast::http::basic_headers; struct element : boost::intrusive::set_base_hook < boost::intrusive::link_mode < boost::intrusive::normal_link>> , boost::intrusive::list_base_hook < boost::intrusive::link_mode < boost::intrusive::normal_link>> { value_type data; element(boost::string_ref const& name, boost::string_ref const& value) : data(name, value) { } }; struct less : private beast::detail::ci_less { template bool operator()(String const& lhs, element const& rhs) const { return ci_less::operator()(lhs, rhs.data.first); } template bool operator()(element const& lhs, String const& rhs) const { return ci_less::operator()(lhs.data.first, rhs); } bool operator()(element const& lhs, element const& rhs) const { return ci_less::operator()( lhs.data.first, rhs.data.first); } }; using list_t = typename boost::intrusive::make_list< element, boost::intrusive::constant_time_size>::type; using set_t = typename boost::intrusive::make_set< element, boost::intrusive::constant_time_size, boost::intrusive::compare>::type; // data set_t set_; list_t list_; basic_headers_base(set_t&& set, list_t&& list) : set_(std::move(set)) , list_(std::move(list)) { } public: class const_iterator; using iterator = const_iterator; basic_headers_base() = default; /// Returns an iterator to the beginning of the field sequence. iterator begin() const; /// Returns an iterator to the end of the field sequence. iterator end() const; /// Returns an iterator to the beginning of the field sequence. iterator cbegin() const; /// Returns an iterator to the end of the field sequence. iterator cend() const; }; //------------------------------------------------------------------------------ class basic_headers_base::const_iterator { using iter_type = list_t::const_iterator; iter_type it_; template friend class beast::http::basic_headers; friend class basic_headers_base; const_iterator(iter_type it) : it_(it) { } public: using value_type = typename basic_headers_base::value_type; using pointer = value_type const*; using reference = value_type const&; using difference_type = std::ptrdiff_t; using iterator_category = std::bidirectional_iterator_tag; const_iterator() = default; const_iterator(const_iterator&& other) = default; const_iterator(const_iterator const& other) = default; const_iterator& operator=(const_iterator&& other) = default; const_iterator& operator=(const_iterator const& other) = default; bool operator==(const_iterator const& other) const { return it_ == other.it_; } bool operator!=(const_iterator const& other) const { return !(*this == other); } reference operator*() const { return it_->data; } pointer operator->() const { return &**this; } const_iterator& operator++() { ++it_; return *this; } const_iterator operator++(int) { auto temp = *this; ++(*this); return temp; } const_iterator& operator--() { --it_; return *this; } const_iterator operator--(int) { auto temp = *this; --(*this); return temp; } }; } // detail //------------------------------------------------------------------------------ /** A container for storing HTTP headers. This container is designed to store the field value pairs that make up the headers and trailers in a HTTP message. Objects of this type are iterable, which each element holding the field name and field value. Field names are stored as-is, but comparison are case-insensitive. The container preserves the order of insertion of fields with different names. For fields with the same name, the implementation concatenates values inserted with duplicate names as per the rules in rfc2616 section 4.2. @note Meets the requirements of @b `FieldSequence`. */ template class basic_headers #if ! GENERATING_DOCS : private beast::detail::empty_base_optimization< typename std::allocator_traits:: template rebind_alloc< detail::basic_headers_base::element>> , public detail::basic_headers_base #endif { using alloc_type = typename std::allocator_traits:: template rebind_alloc< detail::basic_headers_base::element>; using alloc_traits = std::allocator_traits; using size_type = typename std::allocator_traits::size_type; void delete_all(); void move_assign(basic_headers&, std::false_type); void move_assign(basic_headers&, std::true_type); void copy_assign(basic_headers const&, std::false_type); void copy_assign(basic_headers const&, std::true_type); template void copy_from(FieldSequence const& fs) { for(auto const& e : fs) insert(e.first, e.second); } public: /// The type of allocator used. using allocator_type = Allocator; /// Default constructor. basic_headers() = default; /// Destructor ~basic_headers(); /** Construct the headers. @param alloc The allocator to use. */ explicit basic_headers(Allocator const& alloc); /** Move constructor. The moved-from object becomes an empty field sequence. @param other The object to move from. */ basic_headers(basic_headers&& other); /** Move assignment. The moved-from object becomes an empty field sequence. @param other The object to move from. */ basic_headers& operator=(basic_headers&& other); /// Copy constructor. basic_headers(basic_headers const&); /// Copy assignment. basic_headers& operator=(basic_headers const&); /// Copy constructor. template basic_headers(basic_headers const&); /// Copy assignment. template basic_headers& operator=(basic_headers const&); /// Construct from a field sequence. template basic_headers(FwdIt first, FwdIt last); /// Returns `true` if the field sequence contains no elements. bool empty() const { return set_.empty(); } /// Returns the number of elements in the field sequence. std::size_t size() const { return set_.size(); } /** Returns `true` if the specified field exists. */ bool exists(boost::string_ref const& name) const { return set_.find(name, less{}) != set_.end(); } /** Returns an iterator to the case-insensitive matching header. */ iterator find(boost::string_ref const& name) const; /** Returns the value for a case-insensitive matching header, or "" */ boost::string_ref operator[](boost::string_ref const& name) const; /** Clear the contents of the basic_headers. */ void clear() noexcept; /** Remove a field. @return The number of fields removed. */ std::size_t erase(boost::string_ref const& name); /** Insert a field value. If a field value already exists the new value will be extended as per RFC2616 Section 4.2. */ // VFALCO TODO Consider allowing rvalue references for std::move? void insert(boost::string_ref const& name, boost::string_ref const& value); /** Insert a field value. If a field value already exists the new value will be extended as per RFC2616 Section 4.2. */ template::value>::type> void insert(boost::string_ref name, T const& value) { insert(name, boost::lexical_cast(value)); } /** Replace a field value. The current field value, if any, is removed. Then the specified value is inserted as if by insert(field, value). */ void replace(boost::string_ref const& name, boost::string_ref const& value); /** Replace a field value. The current field value, if any, is removed. Then the specified value is inserted as if by insert(field, value). */ template::value>::type> void replace(boost::string_ref const& name, T const& value) { replace(name, boost::lexical_cast(value)); } }; } // http } // beast #include #endif