mirror of
https://github.com/boostorg/beast.git
synced 2025-08-01 22:04:34 +02:00
Add HTTP field value parsers:
ext_list: Iterable container of comma separated extensions, where each extension is a token followed an optional list of semicolon delimited parameters, with each parameter consisting of a name / value pair. The value can be a token or quoted-string. param_list: Iterable container of semicolon delimited parameters, where each parameter is a name / value pair. The value can be a token or quoted-string. token_list Iterable container of comma delimited tokens. * Remove obsolete rfc2616 functions * Refactor and consolidate case-insensitive string helpers
This commit is contained in:
10
CHANGELOG
10
CHANGELOG
@@ -1,8 +1,16 @@
|
|||||||
1.0.0-b6
|
1.0.0-b6
|
||||||
|
|
||||||
* Add HTTP field value parsers
|
|
||||||
* Use SFINAE on return values
|
* Use SFINAE on return values
|
||||||
* Use beast::error_code instead of nested types
|
* Use beast::error_code instead of nested types
|
||||||
* Tidy up use of GENERATING_DOCS
|
* Tidy up use of GENERATING_DOCS
|
||||||
|
* Remove obsolete RFC2616 functions
|
||||||
|
* Add HTTP field value parser containers:
|
||||||
|
- ext_list
|
||||||
|
- param_list
|
||||||
|
- token_list
|
||||||
|
|
||||||
|
API Changes:
|
||||||
|
|
||||||
|
* ci_equal is moved to beast::http namespace, in rfc7230.hpp
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
7
TODO.txt
7
TODO.txt
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
Boost.Http
|
Boost.Http
|
||||||
* Use enum instead of bool in isRequest
|
* Use enum instead of bool in isRequest
|
||||||
* move version to a subclass of message
|
|
||||||
|
|
||||||
Docs:
|
Docs:
|
||||||
* Include Example program listings in the docs
|
* Include Example program listings in the docs
|
||||||
@@ -33,21 +32,15 @@ HTTP:
|
|||||||
* Define Parser concept in HTTP
|
* Define Parser concept in HTTP
|
||||||
- Need parse version of read() so caller can set parser options
|
- Need parse version of read() so caller can set parser options
|
||||||
like maximum size of headers, maximum body size, etc
|
like maximum size of headers, maximum body size, etc
|
||||||
* trim public interface of rfc2616.h to essentials only
|
|
||||||
* add bool should_close(message_v1 const&) to replace the use
|
* add bool should_close(message_v1 const&) to replace the use
|
||||||
of eof return value from write and async_write
|
of eof return value from write and async_write
|
||||||
* http type_check, e.g. is_WritableBody
|
|
||||||
* More fine grained parser errors
|
* More fine grained parser errors
|
||||||
* HTTP parser size limit with test (configurable?)
|
* HTTP parser size limit with test (configurable?)
|
||||||
* HTTP parser trailers with test
|
* HTTP parser trailers with test
|
||||||
* Decode chunk encoding parameters
|
* Decode chunk encoding parameters
|
||||||
* URL parser, strong URL character checking in HTTP parser
|
* URL parser, strong URL character checking in HTTP parser
|
||||||
* Update for rfc7230
|
|
||||||
* Consider rename to MessageBody concept
|
|
||||||
* Fix prepare() calling content_length() without init()
|
* Fix prepare() calling content_length() without init()
|
||||||
* Use construct,destroy allocator routines in basic_headers
|
|
||||||
* Complete allocator testing in basic_streambuf, basic_headers
|
* Complete allocator testing in basic_streambuf, basic_headers
|
||||||
* Add tests for writer using the resume function / coros
|
|
||||||
* Custom HTTP error codes for various situations
|
* Custom HTTP error codes for various situations
|
||||||
* Branch prediction hints in parser
|
* Branch prediction hints in parser
|
||||||
* Check basic_parser_v1 against rfc7230 for leading message whitespace
|
* Check basic_parser_v1 against rfc7230 for leading message whitespace
|
||||||
|
@@ -8,76 +8,96 @@
|
|||||||
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
#ifndef BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||||
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
#define BEAST_DETAIL_CI_CHAR_TRAITS_HPP
|
||||||
|
|
||||||
|
#include <boost/range/algorithm/equal.hpp>
|
||||||
#include <boost/utility/string_ref.hpp>
|
#include <boost/utility/string_ref.hpp>
|
||||||
#include <algorithm>
|
#include <array>
|
||||||
#include <type_traits>
|
#include <cstdint>
|
||||||
#include <cctype>
|
|
||||||
#include <iterator>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
/** Case-insensitive function object for performing less than comparisons. */
|
inline
|
||||||
|
char
|
||||||
|
tolower(char c)
|
||||||
|
{
|
||||||
|
static std::array<std::uint8_t, 256> constexpr tab = {{
|
||||||
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||||
|
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||||
|
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||||
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95,
|
||||||
|
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
||||||
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
||||||
|
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
|
||||||
|
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
|
||||||
|
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
|
||||||
|
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
|
||||||
|
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
|
||||||
|
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
|
||||||
|
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
|
||||||
|
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
|
||||||
|
}};
|
||||||
|
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
inline
|
||||||
|
boost::string_ref
|
||||||
|
string_helper(const char (&s)[N])
|
||||||
|
{
|
||||||
|
return boost::string_ref{s, N-1};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline
|
||||||
|
T const&
|
||||||
|
string_helper(T const& t)
|
||||||
|
{
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Case-insensitive less
|
||||||
struct ci_less
|
struct ci_less
|
||||||
{
|
{
|
||||||
static bool const is_transparent = true;
|
static bool const is_transparent = true;
|
||||||
|
|
||||||
|
template<class S1, class S2>
|
||||||
bool
|
bool
|
||||||
operator()(boost::string_ref const& lhs,
|
operator()(S1 const& lhs, S2 const& rhs) const noexcept
|
||||||
boost::string_ref const& rhs) const noexcept
|
|
||||||
{
|
{
|
||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
auto const s1 = string_helper(lhs);
|
||||||
|
auto const s2 = string_helper(rhs);
|
||||||
return std::lexicographical_compare(
|
return std::lexicographical_compare(
|
||||||
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
begin(s1), end(s1), begin(s2), end(s2),
|
||||||
[](char lhs, char rhs)
|
[](char lhs, char rhs)
|
||||||
{
|
{
|
||||||
return std::tolower(lhs) < std::tolower(rhs);
|
return tolower(lhs) < tolower(rhs);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
// Case-insensitive equal
|
||||||
|
struct ci_equal_pred
|
||||||
|
{
|
||||||
|
bool
|
||||||
|
operator()(char c1, char c2) const noexcept
|
||||||
|
{
|
||||||
|
return tolower(c1) == tolower(c2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Case-insensitive equal
|
||||||
|
template<class S1, class S2>
|
||||||
bool
|
bool
|
||||||
ci_equal(std::pair<const char*, std::size_t> lhs,
|
ci_equal(S1 const& lhs, S2 const& rhs)
|
||||||
std::pair<const char*, std::size_t> rhs)
|
|
||||||
{
|
{
|
||||||
if(lhs.second != rhs.second)
|
return boost::range::equal(
|
||||||
return false;
|
string_helper(lhs), string_helper(rhs),
|
||||||
return std::equal (lhs.first, lhs.first + lhs.second,
|
ci_equal_pred{});
|
||||||
rhs.first,
|
|
||||||
[] (char lhs, char rhs)
|
|
||||||
{
|
|
||||||
return std::tolower(lhs) == std::tolower(rhs);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
inline
|
|
||||||
std::pair<const char*, std::size_t>
|
|
||||||
view(const char (&s)[N])
|
|
||||||
{
|
|
||||||
return {s, N-1};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
std::pair<const char*, std::size_t>
|
|
||||||
view(std::string const& s)
|
|
||||||
{
|
|
||||||
return {s.data(), s.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns `true` if strings are case-insensitive equal. */
|
|
||||||
template <class String1, class String2>
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
ci_equal(String1 const& lhs, String2 const& rhs)
|
|
||||||
{
|
|
||||||
return ci_equal(view(lhs), view(rhs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
#include <beast/http/reason.hpp>
|
#include <beast/http/reason.hpp>
|
||||||
#include <beast/http/resume_context.hpp>
|
#include <beast/http/resume_context.hpp>
|
||||||
#include <beast/http/rfc2616.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/http/streambuf_body.hpp>
|
#include <beast/http/streambuf_body.hpp>
|
||||||
#include <beast/http/string_body.hpp>
|
#include <beast/http/string_body.hpp>
|
||||||
#include <beast/http/write.hpp>
|
#include <beast/http/write.hpp>
|
||||||
|
@@ -246,8 +246,7 @@ public:
|
|||||||
Field names are stored as-is, but comparison are case-insensitive.
|
Field names are stored as-is, but comparison are case-insensitive.
|
||||||
The container preserves the order of insertion of fields with
|
The container preserves the order of insertion of fields with
|
||||||
different names. For fields with the same name, the implementation
|
different names. For fields with the same name, the implementation
|
||||||
concatenates values inserted with duplicate names as per the
|
concatenates values inserted with duplicate names as per rfc7230.
|
||||||
rules in rfc2616 section 4.2.
|
|
||||||
|
|
||||||
@note Meets the requirements of @b `FieldSequence`.
|
@note Meets the requirements of @b `FieldSequence`.
|
||||||
*/
|
*/
|
||||||
@@ -393,8 +392,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
// VFALCO TODO Consider allowing rvalue references for std::move?
|
// VFALCO TODO Consider allowing rvalue references for std::move?
|
||||||
void
|
void
|
||||||
insert(boost::string_ref const& name,
|
insert(boost::string_ref const& name, boost::string_ref value);
|
||||||
boost::string_ref const& value);
|
|
||||||
|
|
||||||
/** Insert a field value.
|
/** Insert a field value.
|
||||||
|
|
||||||
@@ -416,8 +414,7 @@ public:
|
|||||||
specified value is inserted as if by `insert(field, value)`.
|
specified value is inserted as if by `insert(field, value)`.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
replace(boost::string_ref const& name,
|
replace(boost::string_ref const& name, boost::string_ref value);
|
||||||
boost::string_ref const& value);
|
|
||||||
|
|
||||||
/** Replace a field value.
|
/** Replace a field value.
|
||||||
|
|
||||||
|
261
include/beast/http/detail/rfc7230.hpp
Normal file
261
include/beast/http/detail/rfc7230.hpp
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
//
|
||||||
|
// 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_DETAIL_RFC7230_HPP
|
||||||
|
#define BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||||
|
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_tchar(char c)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
tchar = "!" | "#" | "$" | "%" | "&" |
|
||||||
|
"'" | "*" | "+" | "-" | "." |
|
||||||
|
"^" | "_" | "`" | "|" | "~" |
|
||||||
|
DIGIT | ALPHA
|
||||||
|
*/
|
||||||
|
static std::array<bool, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_qdchar(char c)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||||
|
*/
|
||||||
|
static std::array<bool, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_qpchar(char c)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||||
|
obs-text = %x80-FF
|
||||||
|
*/
|
||||||
|
static std::array<bool, 256> constexpr tab = {{
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||||
|
}};
|
||||||
|
return tab[static_cast<std::uint8_t>(c)];
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FwdIt>
|
||||||
|
void
|
||||||
|
skip_ows(FwdIt& it, FwdIt const& end)
|
||||||
|
{
|
||||||
|
while(it != end)
|
||||||
|
{
|
||||||
|
auto const c = *it;
|
||||||
|
if(c != ' ' && c != '\t')
|
||||||
|
break;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
boost::string_ref
|
||||||
|
trim(boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
auto first = s.begin();
|
||||||
|
auto last = s.end();
|
||||||
|
skip_ows(first, last);
|
||||||
|
while(first != last)
|
||||||
|
{
|
||||||
|
auto const c = *std::prev(last);
|
||||||
|
if(c != ' ' && c != '\t')
|
||||||
|
break;
|
||||||
|
--last;
|
||||||
|
}
|
||||||
|
if(first == last)
|
||||||
|
return {};
|
||||||
|
return {&*first,
|
||||||
|
static_cast<std::size_t>(last - first)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct param_iter
|
||||||
|
{
|
||||||
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
|
iter_type it;
|
||||||
|
iter_type begin;
|
||||||
|
iter_type end;
|
||||||
|
std::pair<boost::string_ref, boost::string_ref> v;
|
||||||
|
|
||||||
|
bool
|
||||||
|
empty() const
|
||||||
|
{
|
||||||
|
return begin == it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
increment();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
void
|
||||||
|
param_iter::
|
||||||
|
increment()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||||
|
ext = token param-list
|
||||||
|
param-list = *( OWS ";" OWS param )
|
||||||
|
param = token OWS "=" OWS ( token / quoted-string )
|
||||||
|
|
||||||
|
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||||
|
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||||
|
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||||
|
obs-text = %x80-FF
|
||||||
|
|
||||||
|
Example:
|
||||||
|
chunked;a=b;i=j,gzip;windowBits=12
|
||||||
|
x,y
|
||||||
|
*/
|
||||||
|
auto const err =
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
it = begin;
|
||||||
|
};
|
||||||
|
v.first.clear();
|
||||||
|
v.second.clear();
|
||||||
|
detail::skip_ows(it, end);
|
||||||
|
begin = it;
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
if(*it != ';')
|
||||||
|
return err();
|
||||||
|
++it;
|
||||||
|
detail::skip_ows(it, end);
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
// param
|
||||||
|
if(! detail::is_tchar(*it))
|
||||||
|
return err();
|
||||||
|
auto const p0 = it;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it;
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
if(! detail::is_tchar(*it))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto const p1 = it;
|
||||||
|
detail::skip_ows(it, end);
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
if(*it != '=')
|
||||||
|
return err();
|
||||||
|
++it;
|
||||||
|
detail::skip_ows(it, end);
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
if(*it == '"')
|
||||||
|
{
|
||||||
|
// quoted-string
|
||||||
|
auto const p2 = it;
|
||||||
|
++it;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
auto c = *it++;
|
||||||
|
if(c == '"')
|
||||||
|
break;
|
||||||
|
if(detail::is_qdchar(c))
|
||||||
|
continue;
|
||||||
|
if(c != '\\')
|
||||||
|
return err();
|
||||||
|
if(it == end)
|
||||||
|
return err();
|
||||||
|
c = *it++;
|
||||||
|
if(! detail::is_qpchar(c))
|
||||||
|
return err();
|
||||||
|
}
|
||||||
|
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
||||||
|
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// token
|
||||||
|
if(! detail::is_tchar(*it))
|
||||||
|
return err();
|
||||||
|
auto const p2 = it;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
if(it == end)
|
||||||
|
break;
|
||||||
|
if(! detail::is_tchar(*it))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
||||||
|
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@@ -8,6 +8,8 @@
|
|||||||
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
#ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||||
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
#define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP
|
||||||
|
|
||||||
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
@@ -257,12 +259,13 @@ template<class Allocator>
|
|||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_headers<Allocator>::
|
||||||
insert(boost::string_ref const& name,
|
insert(boost::string_ref const& name,
|
||||||
boost::string_ref const& value)
|
boost::string_ref value)
|
||||||
{
|
{
|
||||||
|
value = detail::trim(value);
|
||||||
typename set_t::insert_commit_data d;
|
typename set_t::insert_commit_data d;
|
||||||
auto const result =
|
auto const result =
|
||||||
set_.insert_check(name, less{}, d);
|
set_.insert_check(name, less{}, d);
|
||||||
if (result.second)
|
if(result.second)
|
||||||
{
|
{
|
||||||
auto const p = alloc_traits::allocate(
|
auto const p = alloc_traits::allocate(
|
||||||
this->member(), 1);
|
this->member(), 1);
|
||||||
@@ -284,8 +287,9 @@ template<class Allocator>
|
|||||||
void
|
void
|
||||||
basic_headers<Allocator>::
|
basic_headers<Allocator>::
|
||||||
replace(boost::string_ref const& name,
|
replace(boost::string_ref const& name,
|
||||||
boost::string_ref const& value)
|
boost::string_ref value)
|
||||||
{
|
{
|
||||||
|
value = detail::trim(value);
|
||||||
erase(name);
|
erase(name);
|
||||||
insert(name, value);
|
insert(name, value);
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
#ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP
|
#ifndef BEAST_HTTP_IMPL_MESSAGE_V1_IPP
|
||||||
#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP
|
#define BEAST_HTTP_IMPL_MESSAGE_V1_IPP
|
||||||
|
|
||||||
#include <beast/http/rfc2616.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/http/detail/has_content_length.hpp>
|
#include <beast/http/detail/has_content_length.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@@ -22,13 +22,11 @@ is_keep_alive(message_v1<isRequest, Body, Headers> const& msg)
|
|||||||
{
|
{
|
||||||
if(msg.version >= 11)
|
if(msg.version >= 11)
|
||||||
{
|
{
|
||||||
if(rfc2616::token_in_list(
|
if(token_list{msg.headers["Connection"]}.exists("close"))
|
||||||
msg.headers["Connection"], "close"))
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(rfc2616::token_in_list(
|
if(token_list{msg.headers["Connection"]}.exists("keep-alive"))
|
||||||
msg.headers["Connection"], "keep-alive"))
|
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -39,8 +37,7 @@ is_upgrade(message_v1<isRequest, Body, Headers> const& msg)
|
|||||||
{
|
{
|
||||||
if(msg.version < 11)
|
if(msg.version < 11)
|
||||||
return false;
|
return false;
|
||||||
if(rfc2616::token_in_list(
|
if(token_list{msg.headers["Connection"]}.exists("upgrade"))
|
||||||
msg.headers["Connection"], "upgrade"))
|
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -129,8 +126,7 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
|
|||||||
throw std::invalid_argument(
|
throw std::invalid_argument(
|
||||||
"prepare called with Content-Length field set");
|
"prepare called with Content-Length field set");
|
||||||
|
|
||||||
if(rfc2616::token_in_list(
|
if(token_list{msg.headers["Transfer-Encoding"]}.exists("chunked"))
|
||||||
msg.headers["Transfer-Encoding"], "chunked"))
|
|
||||||
throw std::invalid_argument(
|
throw std::invalid_argument(
|
||||||
"prepare called with Transfer-Encoding: chunked set");
|
"prepare called with Transfer-Encoding: chunked set");
|
||||||
|
|
||||||
@@ -175,8 +171,8 @@ prepare(message_v1<isRequest, Body, Headers>& msg,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// rfc7230 6.7.
|
// rfc7230 6.7.
|
||||||
if(msg.version < 11 && rfc2616::token_in_list(
|
if(msg.version < 11 && token_list{
|
||||||
msg.headers["Connection"], "upgrade"))
|
msg.headers["Connection"]}.exists("upgrade"))
|
||||||
throw std::invalid_argument(
|
throw std::invalid_argument(
|
||||||
"invalid version for Connection: upgrade");
|
"invalid version for Connection: upgrade");
|
||||||
}
|
}
|
||||||
|
548
include/beast/http/impl/rfc7230.ipp
Normal file
548
include/beast/http/impl/rfc7230.ipp
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
//
|
||||||
|
// 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_IMPL_RFC7230_IPP
|
||||||
|
#define BEAST_HTTP_IMPL_RFC7230_IPP
|
||||||
|
|
||||||
|
#include <beast/core/detail/ci_char_traits.hpp>
|
||||||
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
class param_list::const_iterator
|
||||||
|
{
|
||||||
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
|
std::string s_;
|
||||||
|
detail::param_iter pi_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = param_list::value_type;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
|
||||||
|
const_iterator() = default;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
other.pi_.it == pi_.it &&
|
||||||
|
other.pi_.end == pi_.end &&
|
||||||
|
other.pi_.begin == pi_.begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return pi_.v;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &*(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator&
|
||||||
|
operator++()
|
||||||
|
{
|
||||||
|
increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class param_list;
|
||||||
|
|
||||||
|
const_iterator(iter_type begin, iter_type end)
|
||||||
|
{
|
||||||
|
pi_.it = begin;
|
||||||
|
pi_.begin = begin;
|
||||||
|
pi_.end = end;
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
unquote(boost::string_ref const& sr);
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
increment();
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
param_list::
|
||||||
|
begin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.begin(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
param_list::
|
||||||
|
end() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.end(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
param_list::
|
||||||
|
cbegin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.begin(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
param_list::
|
||||||
|
cend() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.end(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
std::string
|
||||||
|
param_list::const_iterator::
|
||||||
|
unquote(boost::string_ref const& sr)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
s.reserve(sr.size());
|
||||||
|
auto it = sr.begin() + 1;
|
||||||
|
auto end = sr.end() - 1;
|
||||||
|
while(it != end)
|
||||||
|
{
|
||||||
|
if(*it == '\\')
|
||||||
|
++it;
|
||||||
|
s.push_back(*it);
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
void
|
||||||
|
param_list::const_iterator::
|
||||||
|
increment()
|
||||||
|
{
|
||||||
|
s_.clear();
|
||||||
|
pi_.increment();
|
||||||
|
if(pi_.empty())
|
||||||
|
{
|
||||||
|
pi_.it = pi_.end;
|
||||||
|
pi_.begin = pi_.end;
|
||||||
|
}
|
||||||
|
else if(pi_.v.second.front() == '"')
|
||||||
|
{
|
||||||
|
s_ = unquote(pi_.v.second);
|
||||||
|
pi_.v.second = boost::string_ref{
|
||||||
|
s_.data(), s_.size()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class ext_list::const_iterator
|
||||||
|
{
|
||||||
|
ext_list::value_type v_;
|
||||||
|
iter_type it_;
|
||||||
|
iter_type begin_;
|
||||||
|
iter_type end_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = ext_list::value_type;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
const_iterator() = default;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
other.it_ == it_ &&
|
||||||
|
other.begin_ == begin_ &&
|
||||||
|
other.end_ == end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return v_;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &*(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator&
|
||||||
|
operator++()
|
||||||
|
{
|
||||||
|
increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class ext_list;
|
||||||
|
|
||||||
|
const_iterator(iter_type begin, iter_type end)
|
||||||
|
{
|
||||||
|
it_ = begin;
|
||||||
|
begin_ = begin;
|
||||||
|
end_ = end;
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
increment();
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
ext_list::
|
||||||
|
begin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.begin(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
ext_list::
|
||||||
|
end() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.end(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
ext_list::
|
||||||
|
cbegin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.begin(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
ext_list::
|
||||||
|
cend() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.end(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
auto
|
||||||
|
ext_list::
|
||||||
|
find(T const& s) ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return std::find_if(begin(), end(),
|
||||||
|
[&s](value_type const& v)
|
||||||
|
{
|
||||||
|
return beast::detail::ci_equal(s, v.first);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool
|
||||||
|
ext_list::
|
||||||
|
exists(T const& s)
|
||||||
|
{
|
||||||
|
return find(s) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
void
|
||||||
|
ext_list::const_iterator::
|
||||||
|
increment()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||||
|
ext = token param-list
|
||||||
|
param-list = *( OWS ";" OWS param )
|
||||||
|
param = token OWS "=" OWS ( token / quoted-string )
|
||||||
|
|
||||||
|
chunked;a=b;i=j,gzip;windowBits=12
|
||||||
|
x,y
|
||||||
|
,,,,,chameleon
|
||||||
|
*/
|
||||||
|
auto const err =
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
it_ = end_;
|
||||||
|
begin_ = end_;
|
||||||
|
};
|
||||||
|
auto need_comma = it_ != begin_;
|
||||||
|
v_.first = {};
|
||||||
|
begin_ = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
detail::skip_ows(it_, end_);
|
||||||
|
if(it_ == end_)
|
||||||
|
return err();
|
||||||
|
auto const c = *it_;
|
||||||
|
if(detail::is_tchar(c))
|
||||||
|
{
|
||||||
|
if(need_comma)
|
||||||
|
return err();
|
||||||
|
auto const p0 = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_)
|
||||||
|
break;
|
||||||
|
if(! detail::is_tchar(*it_))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v_.first = boost::string_ref{&*p0,
|
||||||
|
static_cast<std::size_t>(it_ - p0)};
|
||||||
|
detail::param_iter pi;
|
||||||
|
pi.it = it_;
|
||||||
|
pi.begin = it_;
|
||||||
|
pi.end = end_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
pi.increment();
|
||||||
|
if(pi.empty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v_.second = param_list{boost::string_ref{&*it_,
|
||||||
|
static_cast<std::size_t>(pi.it - it_)}};
|
||||||
|
it_ = pi.it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(c != ',')
|
||||||
|
return err();
|
||||||
|
need_comma = false;
|
||||||
|
++it_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class token_list::const_iterator
|
||||||
|
{
|
||||||
|
token_list::value_type v_;
|
||||||
|
iter_type it_;
|
||||||
|
iter_type begin_;
|
||||||
|
iter_type end_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = token_list::value_type;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
const_iterator() = default;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
other.it_ == it_ &&
|
||||||
|
other.begin_ == begin_ &&
|
||||||
|
other.end_ == end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return v_;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &*(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator&
|
||||||
|
operator++()
|
||||||
|
{
|
||||||
|
increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class token_list;
|
||||||
|
|
||||||
|
const_iterator(iter_type begin, iter_type end)
|
||||||
|
{
|
||||||
|
it_ = begin;
|
||||||
|
begin_ = begin;
|
||||||
|
end_ = end;
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
increment();
|
||||||
|
};
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
token_list::
|
||||||
|
begin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.begin(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
token_list::
|
||||||
|
end() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.end(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
token_list::
|
||||||
|
cbegin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.begin(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
token_list::
|
||||||
|
cend() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return const_iterator{s_.end(), s_.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
void
|
||||||
|
token_list::const_iterator::
|
||||||
|
increment()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||||
|
*/
|
||||||
|
auto const err =
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
it_ = end_;
|
||||||
|
begin_ = end_;
|
||||||
|
};
|
||||||
|
auto need_comma = it_ != begin_;
|
||||||
|
v_ = {};
|
||||||
|
begin_ = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
detail::skip_ows(it_, end_);
|
||||||
|
if(it_ == end_)
|
||||||
|
return err();
|
||||||
|
auto const c = *it_;
|
||||||
|
if(detail::is_tchar(c))
|
||||||
|
{
|
||||||
|
if(need_comma)
|
||||||
|
return err();
|
||||||
|
auto const p0 = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_)
|
||||||
|
break;
|
||||||
|
if(! detail::is_tchar(*it_))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
v_ = boost::string_ref{&*p0,
|
||||||
|
static_cast<std::size_t>(it_ - p0)};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(c != ',')
|
||||||
|
return err();
|
||||||
|
need_comma = false;
|
||||||
|
++it_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool
|
||||||
|
token_list::
|
||||||
|
exists(T const& s)
|
||||||
|
{
|
||||||
|
return std::find_if(begin(), end(),
|
||||||
|
[&s](value_type const& v)
|
||||||
|
{
|
||||||
|
return beast::detail::ci_equal(s, v);
|
||||||
|
}
|
||||||
|
) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@@ -97,10 +97,10 @@ struct write_preparation
|
|||||||
message_v1<isRequest, Body, Headers> const& msg_)
|
message_v1<isRequest, Body, Headers> const& msg_)
|
||||||
: msg(msg_)
|
: msg(msg_)
|
||||||
, w(msg)
|
, w(msg)
|
||||||
, chunked(rfc2616::token_in_list(
|
, chunked(token_list{
|
||||||
msg.headers["Transfer-Encoding"], "chunked"))
|
msg.headers["Transfer-Encoding"]}.exists("chunked"))
|
||||||
, close(rfc2616::token_in_list(
|
, close(token_list{
|
||||||
msg.headers["Connection"], "close") ||
|
msg.headers["Connection"]}.exists("close") ||
|
||||||
(msg.version < 11 && ! msg.headers.exists(
|
(msg.version < 11 && ! msg.headers.exists(
|
||||||
"Content-Length")))
|
"Content-Length")))
|
||||||
{
|
{
|
||||||
|
@@ -124,8 +124,6 @@ private:
|
|||||||
{
|
{
|
||||||
if(! value_.empty())
|
if(! value_.empty())
|
||||||
{
|
{
|
||||||
rfc2616::trim_right_in_place(value_);
|
|
||||||
// VFALCO could std::move
|
|
||||||
m_.headers.insert(field_, value_);
|
m_.headers.insert(field_, value_);
|
||||||
field_.clear();
|
field_.clear();
|
||||||
value_.clear();
|
value_.clear();
|
||||||
|
@@ -1,464 +0,0 @@
|
|||||||
//
|
|
||||||
// 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 <boost/range/algorithm/equal.hpp>
|
|
||||||
#include <boost/range/iterator_range.hpp>
|
|
||||||
#include <boost/utility/string_ref.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <string>
|
|
||||||
#include <iterator>
|
|
||||||
#include <tuple> // for std::tie, remove ASAP
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
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 <class FwdIter>
|
|
||||||
FwdIter
|
|
||||||
trim_left (FwdIter first, FwdIter last)
|
|
||||||
{
|
|
||||||
return std::find_if_not (first, last,
|
|
||||||
is_white);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 CharT, class Traits, class Allocator>
|
|
||||||
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 <class FwdIter>
|
|
||||||
std::pair <FwdIter, FwdIter>
|
|
||||||
trim (FwdIter first, FwdIter last)
|
|
||||||
{
|
|
||||||
first = trim_left (first, last);
|
|
||||||
last = trim_right (first, last);
|
|
||||||
return std::make_pair (first, last);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String>
|
|
||||||
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 <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 };
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
std::string
|
|
||||||
trim (std::string const& s)
|
|
||||||
{
|
|
||||||
return trim <std::string> (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 <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)
|
|
||||||
{
|
|
||||||
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 <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::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<class = void>
|
|
||||||
void
|
|
||||||
increment();
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class>
|
|
||||||
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<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // rfc2616
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@@ -8,16 +8,238 @@
|
|||||||
#ifndef BEAST_HTTP_RFC7230_HPP
|
#ifndef BEAST_HTTP_RFC7230_HPP
|
||||||
#define BEAST_HTTP_RFC7230_HPP
|
#define BEAST_HTTP_RFC7230_HPP
|
||||||
|
|
||||||
#include <array>
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace rfc7230 {
|
namespace http {
|
||||||
|
|
||||||
|
/** A list of parameters in a HTTP extension field value.
|
||||||
|
|
||||||
|
This container allows iteration of the parameter list
|
||||||
|
in a HTTP extension. The parameter list is a series
|
||||||
|
of "name = value" pairs with each pair starting with
|
||||||
|
a semicolon.
|
||||||
|
|
||||||
} // rfc7230
|
BNF:
|
||||||
|
@code
|
||||||
|
param-list = *( OWS ";" OWS param )
|
||||||
|
param = token OWS "=" OWS ( token / quoted-string )
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
If a parsing error is encountered while iterating the string,
|
||||||
|
the behavior of the container will be as if a string containing
|
||||||
|
only characters up to but excluding the first invalid character
|
||||||
|
was used to construct the list.
|
||||||
|
*/
|
||||||
|
class param_list
|
||||||
|
{
|
||||||
|
boost::string_ref s_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** The type of each element in the list.
|
||||||
|
|
||||||
|
The first string in the pair is the name of the
|
||||||
|
parameter, and the second string in the pair is its value.
|
||||||
|
*/
|
||||||
|
using value_type =
|
||||||
|
std::pair<boost::string_ref, boost::string_ref>;
|
||||||
|
|
||||||
|
/// A constant iterator to the list
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using const_iterator = implementation_defined;
|
||||||
|
#else
|
||||||
|
class const_iterator;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
param_list() = default;
|
||||||
|
|
||||||
|
/** Construct a list.
|
||||||
|
|
||||||
|
@param s A string containing the list contents. The string
|
||||||
|
must remain valid for the lifetime of the container.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
param_list(boost::string_ref const& s)
|
||||||
|
: s_(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a const iterator to the beginning of the list
|
||||||
|
const_iterator begin() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the end of the list
|
||||||
|
const_iterator end() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the beginning of the list
|
||||||
|
const_iterator cbegin() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the end of the list
|
||||||
|
const_iterator cend() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** A list of extensions in a comma separated HTTP field value.
|
||||||
|
|
||||||
|
This container allows iteration of the extensions in a HTTP
|
||||||
|
field value. The extension list is a comma separated list of
|
||||||
|
token parameter list pairs.
|
||||||
|
|
||||||
|
BNF:
|
||||||
|
@code
|
||||||
|
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||||
|
ext = token param-list
|
||||||
|
param-list = *( OWS ";" OWS param )
|
||||||
|
param = token OWS "=" OWS ( token / quoted-string )
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
If a parsing error is encountered while iterating the string,
|
||||||
|
the behavior of the container will be as if a string containing
|
||||||
|
only characters up to but excluding the first invalid character
|
||||||
|
was used to construct the list.
|
||||||
|
*/
|
||||||
|
class ext_list
|
||||||
|
{
|
||||||
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
|
boost::string_ref s_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** The type of each element in the list.
|
||||||
|
|
||||||
|
The first element of the pair is the extension token, and the
|
||||||
|
second element of the pair is an iterable container holding the
|
||||||
|
extension's name/value parameters.
|
||||||
|
*/
|
||||||
|
using value_type = std::pair<boost::string_ref, param_list>;
|
||||||
|
|
||||||
|
/// A constant iterator to the list
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using const_iterator = implementation_defined;
|
||||||
|
#else
|
||||||
|
class const_iterator;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Construct a list.
|
||||||
|
|
||||||
|
@param s A string containing the list contents. The string
|
||||||
|
must remain valid for the lifetime of the container.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
ext_list(boost::string_ref const& s)
|
||||||
|
: s_(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a const iterator to the beginning of the list
|
||||||
|
const_iterator begin() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the end of the list
|
||||||
|
const_iterator end() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the beginning of the list
|
||||||
|
const_iterator cbegin() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the end of the list
|
||||||
|
const_iterator cend() const;
|
||||||
|
|
||||||
|
/** Find a token in the list.
|
||||||
|
|
||||||
|
@param s The token to find. A case-insensitive comparison is used.
|
||||||
|
|
||||||
|
@return An iterator to the matching token, or `end()` if no
|
||||||
|
token exists.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
const_iterator
|
||||||
|
find(T const& s);
|
||||||
|
|
||||||
|
/** Return `true` if a token is present in the list.
|
||||||
|
|
||||||
|
@param s The token to find. A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
bool
|
||||||
|
exists(T const& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** A list of tokens in a comma separated HTTP field value.
|
||||||
|
|
||||||
|
This container allows iteration of the extensions in a HTTP
|
||||||
|
field value. The extension list is a comma separated list of
|
||||||
|
token parameter list pairs.
|
||||||
|
|
||||||
|
BNF:
|
||||||
|
@code
|
||||||
|
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
If a parsing error is encountered while iterating the string,
|
||||||
|
the behavior of the container will be as if a string containing
|
||||||
|
only characters up to but excluding the first invalid character
|
||||||
|
was used to construct the list.
|
||||||
|
*/
|
||||||
|
class token_list
|
||||||
|
{
|
||||||
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
|
boost::string_ref s_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** The type of each element in the token list.
|
||||||
|
|
||||||
|
The first element of the pair is the extension token, and the
|
||||||
|
second element of the pair is an iterable container holding the
|
||||||
|
extension's name/value parameters.
|
||||||
|
*/
|
||||||
|
using value_type = boost::string_ref;
|
||||||
|
|
||||||
|
/// A constant iterator to the list
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
using const_iterator = implementation_defined;
|
||||||
|
#else
|
||||||
|
class const_iterator;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Construct a list.
|
||||||
|
|
||||||
|
@param s A string containing the list contents. The string
|
||||||
|
must remain valid for the lifetime of the container.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
token_list(boost::string_ref const& s)
|
||||||
|
: s_(s)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a const iterator to the beginning of the list
|
||||||
|
const_iterator begin() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the end of the list
|
||||||
|
const_iterator end() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the beginning of the list
|
||||||
|
const_iterator cbegin() const;
|
||||||
|
|
||||||
|
/// Return a const iterator to the end of the list
|
||||||
|
const_iterator cend() const;
|
||||||
|
|
||||||
|
/** Return `true` if a token is present in the list.
|
||||||
|
|
||||||
|
@param s The token to find. A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
|
template<class T>
|
||||||
|
bool
|
||||||
|
exists(T const& s);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/rfc7230.ipp>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@
|
|||||||
#include <beast/http/read.hpp>
|
#include <beast/http/read.hpp>
|
||||||
#include <beast/http/write.hpp>
|
#include <beast/http/write.hpp>
|
||||||
#include <beast/http/reason.hpp>
|
#include <beast/http/reason.hpp>
|
||||||
#include <beast/http/rfc2616.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/core/buffer_cat.hpp>
|
#include <beast/core/buffer_cat.hpp>
|
||||||
#include <beast/core/buffer_concepts.hpp>
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
#include <beast/core/consuming_buffers.hpp>
|
#include <beast/core/consuming_buffers.hpp>
|
||||||
@@ -951,8 +951,7 @@ build_response(http::request_v1<Body, Headers> const& req)
|
|||||||
return err("Missing Host");
|
return err("Missing Host");
|
||||||
if(! req.headers.exists("Sec-WebSocket-Key"))
|
if(! req.headers.exists("Sec-WebSocket-Key"))
|
||||||
return err("Missing Sec-WebSocket-Key");
|
return err("Missing Sec-WebSocket-Key");
|
||||||
if(! rfc2616::token_in_list(
|
if(! http::token_list{req.headers["Upgrade"]}.exists("websocket"))
|
||||||
req.headers["Upgrade"], "websocket"))
|
|
||||||
return err("Missing websocket Upgrade token");
|
return err("Missing websocket Upgrade token");
|
||||||
{
|
{
|
||||||
auto const version =
|
auto const version =
|
||||||
@@ -1005,8 +1004,7 @@ do_response(http::response_v1<Body, Headers> const& res,
|
|||||||
return fail();
|
return fail();
|
||||||
if(! is_upgrade(res))
|
if(! is_upgrade(res))
|
||||||
return fail();
|
return fail();
|
||||||
if(! rfc2616::ci_equal(
|
if(! http::token_list{res.headers["Upgrade"]}.exists("websocket"))
|
||||||
res.headers["Upgrade"], "websocket"))
|
|
||||||
return fail();
|
return fail();
|
||||||
if(! res.headers.exists("Sec-WebSocket-Accept"))
|
if(! res.headers.exists("Sec-WebSocket-Accept"))
|
||||||
return fail();
|
return fail();
|
||||||
|
@@ -120,7 +120,7 @@ decorate(Decorator&& d)
|
|||||||
This setting only affects the behavior of HTTP requests that
|
This setting only affects the behavior of HTTP requests that
|
||||||
implicitly or explicitly ask for a keepalive. For HTTP requests
|
implicitly or explicitly ask for a keepalive. For HTTP requests
|
||||||
that indicate the connection should be closed, the connection is
|
that indicate the connection should be closed, the connection is
|
||||||
closed as per rfc2616.
|
closed as per rfc7230.
|
||||||
|
|
||||||
The default setting is to close connections after a failed
|
The default setting is to close connections after a failed
|
||||||
upgrade request.
|
upgrade request.
|
||||||
|
@@ -54,7 +54,6 @@ unit-test http-tests :
|
|||||||
http/read.cpp
|
http/read.cpp
|
||||||
http/reason.cpp
|
http/reason.cpp
|
||||||
http/resume_context.cpp
|
http/resume_context.cpp
|
||||||
http/rfc2616.cpp
|
|
||||||
http/rfc7230.cpp
|
http/rfc7230.cpp
|
||||||
http/status.cpp
|
http/status.cpp
|
||||||
http/streambuf_body.cpp
|
http/streambuf_body.cpp
|
||||||
|
@@ -20,7 +20,6 @@ add_executable (http-tests
|
|||||||
read.cpp
|
read.cpp
|
||||||
reason.cpp
|
reason.cpp
|
||||||
resume_context.cpp
|
resume_context.cpp
|
||||||
rfc2616.cpp
|
|
||||||
rfc7230.cpp
|
rfc7230.cpp
|
||||||
status.cpp
|
status.cpp
|
||||||
streambuf_body.cpp
|
streambuf_body.cpp
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
#include <beast/core/streambuf.hpp>
|
#include <beast/core/streambuf.hpp>
|
||||||
#include <beast/core/write_streambuf.hpp>
|
#include <beast/core/write_streambuf.hpp>
|
||||||
#include <beast/core/detail/ci_char_traits.hpp>
|
#include <beast/core/detail/ci_char_traits.hpp>
|
||||||
#include <beast/http/rfc2616.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/unit_test/suite.hpp>
|
#include <beast/unit_test/suite.hpp>
|
||||||
#include <boost/utility/string_ref.hpp>
|
#include <boost/utility/string_ref.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -135,6 +135,13 @@ public:
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
str(boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
return std::string{s.data(), s.size()};
|
||||||
|
}
|
||||||
|
|
||||||
template<bool isRequest>
|
template<bool isRequest>
|
||||||
class test_parser :
|
class test_parser :
|
||||||
public basic_parser_v1<isRequest, test_parser<isRequest>>
|
public basic_parser_v1<isRequest, test_parser<isRequest>>
|
||||||
@@ -146,8 +153,7 @@ public:
|
|||||||
{
|
{
|
||||||
if(! value_.empty())
|
if(! value_.empty())
|
||||||
{
|
{
|
||||||
rfc2616::trim_right_in_place(value_);
|
fields.emplace(field_, str(detail::trim(value_)));
|
||||||
fields.emplace(field_, value_);
|
|
||||||
field_.clear();
|
field_.clear();
|
||||||
value_.clear();
|
value_.clear();
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
#include "nodejs-parser/http_parser.h"
|
#include "nodejs-parser/http_parser.h"
|
||||||
|
|
||||||
#include <beast/http/message_v1.hpp>
|
#include <beast/http/message_v1.hpp>
|
||||||
#include <beast/http/rfc2616.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/core/buffer_concepts.hpp>
|
#include <beast/core/buffer_concepts.hpp>
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
@@ -611,7 +611,7 @@ nodejs_basic_parser<Derived>::check_header()
|
|||||||
{
|
{
|
||||||
if (! value_.empty())
|
if (! value_.empty())
|
||||||
{
|
{
|
||||||
rfc2616::trim_right_in_place(value_);
|
//detail::trim(value_);
|
||||||
call_on_field(field_, value_,
|
call_on_field(field_, value_,
|
||||||
has_on_field<Derived>{});
|
has_on_field<Derived>{});
|
||||||
field_.clear();
|
field_.clear();
|
||||||
|
@@ -1,115 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
//
|
|
||||||
|
|
||||||
// Test that header file is self-contained.
|
|
||||||
#include <beast/http/rfc2616.hpp>
|
|
||||||
|
|
||||||
#include <beast/unit_test/suite.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace rfc2616 {
|
|
||||||
namespace test {
|
|
||||||
|
|
||||||
class rfc2616_test : public beast::unit_test::suite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void
|
|
||||||
checkSplit(std::string const& s,
|
|
||||||
std::vector <std::string> const& expected)
|
|
||||||
{
|
|
||||||
auto const parsed = split_commas(s.begin(), s.end());
|
|
||||||
expect (parsed == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testSplit()
|
|
||||||
{
|
|
||||||
checkSplit("", {});
|
|
||||||
checkSplit(" ", {});
|
|
||||||
checkSplit(" ", {});
|
|
||||||
checkSplit("\t", {});
|
|
||||||
checkSplit(" \t ", {});
|
|
||||||
checkSplit(",", {});
|
|
||||||
checkSplit(",,", {});
|
|
||||||
checkSplit(" ,", {});
|
|
||||||
checkSplit(" , ,", {});
|
|
||||||
checkSplit("x", {"x"});
|
|
||||||
checkSplit(" x", {"x"});
|
|
||||||
checkSplit(" \t x", {"x"});
|
|
||||||
checkSplit("x ", {"x"});
|
|
||||||
checkSplit("x \t", {"x"});
|
|
||||||
checkSplit(" \t x \t ", {"x"});
|
|
||||||
checkSplit("\"\"", {});
|
|
||||||
checkSplit(" \"\"", {});
|
|
||||||
checkSplit("\"\" ", {});
|
|
||||||
checkSplit("\"x\"", {"x"});
|
|
||||||
checkSplit("\" \"", {" "});
|
|
||||||
checkSplit("\" x\"", {" x"});
|
|
||||||
checkSplit("\"x \"", {"x "});
|
|
||||||
checkSplit("\" x \"", {" x "});
|
|
||||||
checkSplit("\"\tx \"", {"\tx "});
|
|
||||||
checkSplit("x,y", {"x", "y"});
|
|
||||||
checkSplit("x ,\ty ", {"x", "y"});
|
|
||||||
checkSplit("x, y, z", {"x","y","z"});
|
|
||||||
checkSplit("x, \"y\", z", {"x","y","z"});
|
|
||||||
checkSplit(",,x,,\"y\",,", {"x","y"});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
checkIter(std::string const& s,
|
|
||||||
std::vector<std::string> const& expected)
|
|
||||||
{
|
|
||||||
std::vector<std::string> got;
|
|
||||||
for(auto const& v : make_list(s))
|
|
||||||
got.emplace_back(v);
|
|
||||||
expect(got == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
testIter()
|
|
||||||
{
|
|
||||||
checkIter("x", {"x"});
|
|
||||||
checkIter(" x", {"x"});
|
|
||||||
checkIter("x\t", {"x"});
|
|
||||||
checkIter("\tx ", {"x"});
|
|
||||||
checkIter(",x", {"x"});
|
|
||||||
checkIter("x,", {"x"});
|
|
||||||
checkIter(",x,", {"x"});
|
|
||||||
checkIter(" , x\t,\t", {"x"});
|
|
||||||
checkIter("x,y", {"x", "y"});
|
|
||||||
checkIter("x, ,y ", {"x", "y"});
|
|
||||||
checkIter("\"x\"", {"x"});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
testList()
|
|
||||||
{
|
|
||||||
expect(token_in_list("x", "x"));
|
|
||||||
expect(token_in_list("x,y", "x"));
|
|
||||||
expect(token_in_list("x,y", "y"));
|
|
||||||
expect(token_in_list("x, y ", "y"));
|
|
||||||
expect(token_in_list("x", "X"));
|
|
||||||
expect(token_in_list("Y", "y"));
|
|
||||||
expect(token_in_list("close, keepalive", "close"));
|
|
||||||
expect(token_in_list("close, keepalive", "keepalive"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
run()
|
|
||||||
{
|
|
||||||
testSplit();
|
|
||||||
testIter();
|
|
||||||
testList();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
|
|
||||||
|
|
||||||
} // test
|
|
||||||
} // rfc2616
|
|
||||||
} // beast
|
|
@@ -7,3 +7,240 @@
|
|||||||
|
|
||||||
// Test that header file is self-contained.
|
// Test that header file is self-contained.
|
||||||
#include <beast/http/rfc7230.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
|
|
||||||
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
|
#include <beast/unit_test/suite.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class rfc7230_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
fmt(std::string const& s)
|
||||||
|
{
|
||||||
|
return '\'' + s + '\'';
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
str(boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
return std::string(s.data(), s.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
str(param_list const& c)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
for(auto const& p : c)
|
||||||
|
{
|
||||||
|
s.push_back(';');
|
||||||
|
s.append(str(p.first));
|
||||||
|
s.push_back('=');
|
||||||
|
s.append(str(p.second));
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testParamList()
|
||||||
|
{
|
||||||
|
auto const ce =
|
||||||
|
[&](std::string const& s)
|
||||||
|
{
|
||||||
|
auto const got = str(param_list{s});
|
||||||
|
expect(got == s, fmt(got));
|
||||||
|
};
|
||||||
|
auto const cs =
|
||||||
|
[&](std::string const& s, std::string const& good)
|
||||||
|
{
|
||||||
|
ce(good);
|
||||||
|
auto const got = str(param_list{s});
|
||||||
|
ce(got);
|
||||||
|
expect(got == good, fmt(got));
|
||||||
|
};
|
||||||
|
auto const cq =
|
||||||
|
[&](std::string const& s, std::string const& good)
|
||||||
|
{
|
||||||
|
auto const got = str(param_list{s});
|
||||||
|
expect(got == good, fmt(got));
|
||||||
|
};
|
||||||
|
|
||||||
|
ce("");
|
||||||
|
cs(" ;\t i =\t 1 \t", ";i=1");
|
||||||
|
cq("\t; \t xyz=1 ; ijk=\"q\\\"t\"", ";xyz=1;ijk=q\"t");
|
||||||
|
|
||||||
|
// invalid strings
|
||||||
|
cs(";", "");
|
||||||
|
cs(";,", "");
|
||||||
|
cs(";xy", "");
|
||||||
|
cs(";xy", "");
|
||||||
|
cs(";xy ", "");
|
||||||
|
cs(";xy,", "");
|
||||||
|
|
||||||
|
cq(";x=,", "");
|
||||||
|
cq(";xy=\"", "");
|
||||||
|
cq(";xy=\"\x7f", "");
|
||||||
|
cq(";xy=\"\\", "");
|
||||||
|
cq(";xy=\"\\\x01\"", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
str(ext_list const& ex)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
for(auto const& e : ex)
|
||||||
|
{
|
||||||
|
if(! s.empty())
|
||||||
|
s += ',';
|
||||||
|
s.append(str(e.first));
|
||||||
|
s += str(e.second);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testExtList()
|
||||||
|
{
|
||||||
|
auto const ce =
|
||||||
|
[&](std::string const& s)
|
||||||
|
{
|
||||||
|
auto const got = str(ext_list{s});
|
||||||
|
expect(got == s, fmt(got));
|
||||||
|
};
|
||||||
|
auto const cs =
|
||||||
|
[&](std::string const& s, std::string const& good)
|
||||||
|
{
|
||||||
|
ce(good);
|
||||||
|
auto const got = str(ext_list{s});
|
||||||
|
ce(got);
|
||||||
|
expect(got == good, fmt(got));
|
||||||
|
};
|
||||||
|
auto const cq =
|
||||||
|
[&](std::string const& s, std::string const& good)
|
||||||
|
{
|
||||||
|
auto const got = str(ext_list{s});
|
||||||
|
expect(got == good, fmt(got));
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||||
|
ext = token param-list
|
||||||
|
param-list = *( OWS ";" OWS param )
|
||||||
|
param = token OWS "=" OWS ( token / quoted-string )
|
||||||
|
*/
|
||||||
|
ce("");
|
||||||
|
cs(",", "");
|
||||||
|
cs(", ", "");
|
||||||
|
cs(",\t", "");
|
||||||
|
cs(", \t", "");
|
||||||
|
cs(" ", "");
|
||||||
|
cs(" ,", "");
|
||||||
|
cs("\t,", "");
|
||||||
|
cs("\t , \t", "");
|
||||||
|
cs(",,", "");
|
||||||
|
cs(" , \t,, \t,", "");
|
||||||
|
|
||||||
|
ce("a");
|
||||||
|
ce("ab");
|
||||||
|
ce("a,b");
|
||||||
|
cs(" a ", "a");
|
||||||
|
cs("\t a, b\t , c\t", "a,b,c");
|
||||||
|
|
||||||
|
cs("a; \t i\t=\t \t1\t ", "a;i=1");
|
||||||
|
ce("a;i=1;j=2;k=3");
|
||||||
|
ce("a;i=1;j=2;k=3,b;i=4;j=5;k=6");
|
||||||
|
|
||||||
|
cq("ab;x=\" \"", "ab;x= ");
|
||||||
|
cq("ab;x=\"\\\"\"", "ab;x=\"");
|
||||||
|
|
||||||
|
expect(ext_list{"a,b;i=1,c;j=2;k=3"}.exists("A"));
|
||||||
|
expect(ext_list{"a,b;i=1,c;j=2;k=3"}.exists("b"));
|
||||||
|
expect(! ext_list{"a,b;i=1,c;j=2;k=3"}.exists("d"));
|
||||||
|
|
||||||
|
// invalid strings
|
||||||
|
cs("i j", "i");
|
||||||
|
cs(";", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
str(token_list const& c)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
std::string s;
|
||||||
|
for(auto const& p : c)
|
||||||
|
{
|
||||||
|
if(! first)
|
||||||
|
s.push_back(',');
|
||||||
|
s.append(str(p));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testTokenList()
|
||||||
|
{
|
||||||
|
auto const ce =
|
||||||
|
[&](std::string const& s)
|
||||||
|
{
|
||||||
|
auto const got = str(token_list{s});
|
||||||
|
expect(got == s, fmt(got));
|
||||||
|
};
|
||||||
|
auto const cs =
|
||||||
|
[&](std::string const& s, std::string const& good)
|
||||||
|
{
|
||||||
|
ce(good);
|
||||||
|
auto const got = str(token_list{s});
|
||||||
|
ce(got);
|
||||||
|
expect(got == good, fmt(got));
|
||||||
|
};
|
||||||
|
|
||||||
|
cs("", "");
|
||||||
|
cs(" ", "");
|
||||||
|
cs(" ", "");
|
||||||
|
cs("\t", "");
|
||||||
|
cs(" \t ", "");
|
||||||
|
cs(",", "");
|
||||||
|
cs(",,", "");
|
||||||
|
cs(" ,", "");
|
||||||
|
cs(" , ,", "");
|
||||||
|
cs(" x", "x");
|
||||||
|
cs(" \t x", "x");
|
||||||
|
cs("x ", "x");
|
||||||
|
cs("x \t", "x");
|
||||||
|
cs(" \t x \t ", "x");
|
||||||
|
ce("x,y");
|
||||||
|
cs("x ,\ty ", "x,y");
|
||||||
|
cs("x, y, z", "x,y,z");
|
||||||
|
|
||||||
|
expect(token_list{"a,b,c"}.exists("A"));
|
||||||
|
expect(token_list{"a,b,c"}.exists("b"));
|
||||||
|
expect(! token_list{"a,b,c"}.exists("d"));
|
||||||
|
|
||||||
|
// invalid
|
||||||
|
cs("x y", "x");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
testParamList();
|
||||||
|
testExtList();
|
||||||
|
testTokenList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(rfc7230,http,beast);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
Reference in New Issue
Block a user