Files
rippled/include/xrpl/beast/rfc2616.h
2026-02-19 23:30:00 +00:00

370 lines
8.0 KiB
C++

#pragma once
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/rfc7230.hpp>
#include <boost/range/algorithm/equal.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/utility/string_ref.hpp>
#include <algorithm>
#include <cctype>
#include <iterator>
#include <string>
#include <vector>
namespace beast {
namespace rfc2616 {
namespace detail {
struct ci_equal_pred
{
explicit ci_equal_pred() = default;
bool
operator()(char c1, char c2)
{
// VFALCO TODO Use a table lookup here
return std::tolower(static_cast<unsigned char>(c1)) ==
std::tolower(static_cast<unsigned char>(c2));
}
};
/** 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;
}
template <class FwdIter>
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 <class String>
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};
}
} // namespace detail
/** 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 <
class FwdIt,
class Result = std::vector<std::basic_string<typename std::iterator_traits<FwdIt>::value_type>>,
class Char>
Result
split(FwdIt first, FwdIt last, Char delim)
{
using namespace detail;
using string = typename Result::value_type;
Result result;
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 <
class FwdIt,
class Result = std::vector<std::basic_string<typename std::iterator_traits<FwdIt>::value_type>>>
Result
split_commas(FwdIt first, FwdIt last)
{
return split(first, last, ',');
}
template <class Result = std::vector<std::string>>
Result
split_commas(boost::beast::string_view 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 <class = void>
void
increment();
};
template <class>
void
list_iterator::increment()
{
using namespace detail;
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<list_iterator>
make_list(boost::string_ref const& field)
{
return boost::iterator_range<list_iterator>{
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 <class = void>
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;
}
template <bool isRequest, class Body, class Fields>
bool
is_keep_alive(boost::beast::http::message<isRequest, Body, Fields> const& m)
{
if (m.version() <= 10)
return boost::beast::http::token_list{m[boost::beast::http::field::connection]}.exists(
"keep-alive");
return !boost::beast::http::token_list{m[boost::beast::http::field::connection]}.exists(
"close");
}
} // namespace rfc2616
} // namespace beast