// // 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_RFC2616_HPP #define BEAST_HTTP_RFC2616_HPP #include #include #include #include #include #include #include #include // for std::tie, remove ASAP #include #include namespace beast { #if ! GENERATING_DOCS /** Routines for performing RFC2616 compliance. RFC2616: Hypertext Transfer Protocol -- HTTP/1.1 http://www.w3.org/Protocols/rfc2616/rfc2616 */ namespace rfc2616 { namespace detail { struct ci_equal_pred { bool operator()(char c1, char c2) { // VFALCO TODO Use a table lookup here return std::tolower(c1) == std::tolower(c2); } }; } // detail /** Returns `true` if `c` is linear white space. This excludes the CRLF sequence allowed for line continuations. */ inline bool is_lws(char c) { return c == ' ' || c == '\t'; } /** Returns `true` if `c` is any whitespace character. */ inline bool is_white(char c) { switch (c) { case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': return true; }; return false; } /** Returns `true` if `c` is a control character. */ inline bool is_control(char c) { return c <= 31 || c >= 127; } /** Returns `true` if `c` is a separator. */ inline bool is_separator(char c) { // VFALCO Could use a static table switch (c) { case '(': case ')': case '<': case '>': case '@': case ',': case ';': case ':': case '\\': case '"': case '{': case '}': case ' ': case '\t': return true; }; return false; } /** Returns `true` if `c` is a character. */ inline bool is_char(char c) { return c >= 0 && c <= 127; } template FwdIter trim_left (FwdIter first, FwdIter last) { return std::find_if_not (first, last, is_white); } template FwdIter trim_right (FwdIter first, FwdIter last) { if (first == last) return last; do { --last; if (! is_white (*last)) return ++last; } while (last != first); return first; } template void trim_right_in_place (std::basic_string < CharT, Traits, Allocator>& s) { s.resize (std::distance (s.begin(), trim_right (s.begin(), s.end()))); } template std::pair trim (FwdIter first, FwdIter last) { first = trim_left (first, last); last = trim_right (first, last); return std::make_pair (first, last); } template String trim (String const& s) { using std::begin; using std::end; auto first = begin(s); auto last = end(s); std::tie (first, last) = trim (first, last); return { first, last }; } template String trim_right (String const& s) { using std::begin; using std::end; auto first (begin(s)); auto last (end(s)); last = trim_right (first, last); return { first, last }; } inline std::string trim (std::string const& s) { return trim (s); } /** Parse a character sequence of values separated by commas. Double quotes and escape sequences will be converted. Excess white space, commas, double quotes, and empty elements are not copied. Format: #(token|quoted-string) Reference: http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2 */ template ::value_type>>, class Char> Result split(FwdIt first, FwdIt last, Char delim) { Result result; using string = typename Result::value_type; FwdIt iter = first; string e; while (iter != last) { if (*iter == '"') { // quoted-string ++iter; while (iter != last) { if (*iter == '"') { ++iter; break; } if (*iter == '\\') { // quoted-pair ++iter; if (iter != last) e.append (1, *iter++); } else { // qdtext e.append (1, *iter++); } } if (! e.empty()) { result.emplace_back(std::move(e)); e.clear(); } } else if (*iter == delim) { e = trim_right (e); if (! e.empty()) { result.emplace_back(std::move(e)); e.clear(); } ++iter; } else if (is_lws (*iter)) { ++iter; } else { e.append (1, *iter++); } } if (! e.empty()) { e = trim_right (e); if (! e.empty()) result.emplace_back(std::move(e)); } return result; } template ::value_type>>> Result split_commas(FwdIt first, FwdIt last) { return split(first, last, ','); } template > Result split_commas(boost::string_ref const& s) { return split_commas(s.begin(), s.end()); } //------------------------------------------------------------------------------ /** Iterates through a comma separated list. Meets the requirements of ForwardIterator. List defined in rfc2616 2.1. @note Values returned may contain backslash escapes. */ class list_iterator { using iter_type = boost::string_ref::const_iterator; iter_type it_; iter_type end_; boost::string_ref value_; public: using value_type = boost::string_ref; using pointer = value_type const*; using reference = value_type const&; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; list_iterator(iter_type begin, iter_type end) : it_(begin) , end_(end) { if(it_ != end_) increment(); } bool operator==(list_iterator const& other) const { return other.it_ == it_ && other.end_ == end_ && other.value_.size() == value_.size(); } bool operator!=(list_iterator const& other) const { return !(*this == other); } reference operator*() const { return value_; } pointer operator->() const { return &*(*this); } list_iterator& operator++() { increment(); return *this; } list_iterator operator++(int) { auto temp = *this; ++(*this); return temp; } private: template void increment(); }; template void list_iterator::increment() { value_.clear(); while(it_ != end_) { if(*it_ == '"') { // quoted-string ++it_; if(it_ == end_) return; if(*it_ != '"') { auto start = it_; for(;;) { ++it_; if(it_ == end_) { value_ = boost::string_ref( &*start, std::distance(start, it_)); return; } if(*it_ == '"') { value_ = boost::string_ref( &*start, std::distance(start, it_)); ++it_; return; } } } ++it_; } else if(*it_ == ',') { it_++; continue; } else if(is_lws(*it_)) { ++it_; continue; } else { auto start = it_; for(;;) { ++it_; if(it_ == end_ || *it_ == ',' || is_lws(*it_)) { value_ = boost::string_ref( &*start, std::distance(start, it_)); return; } } } } } /** Returns true if two strings are equal. A case-insensitive comparison is used. */ inline bool ci_equal(boost::string_ref s1, boost::string_ref s2) { return boost::range::equal(s1, s2, detail::ci_equal_pred{}); } /** Returns a range representing the list. */ inline boost::iterator_range make_list(boost::string_ref const& field) { return boost::iterator_range{ list_iterator{field.begin(), field.end()}, list_iterator{field.end(), field.end()}}; } /** Returns true if the specified token exists in the list. A case-insensitive comparison is used. */ template bool token_in_list(boost::string_ref const& value, boost::string_ref const& token) { for(auto const& item : make_list(value)) if(ci_equal(item, token)) return true; return false; } } // rfc2616 #endif } // beast #endif