mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Add message keep_alive, chunked, content_length members
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -10,6 +10,17 @@ HTTP:
|
||||
* Add vector_body
|
||||
* span, string, vector bodies are public
|
||||
* Fix spurious uninitialized warning
|
||||
* fields temp string uses allocator
|
||||
|
||||
API Changes:
|
||||
|
||||
* Add message::keep_alive()
|
||||
* Add message::chunked() and message::content_length()
|
||||
|
||||
Actions Required:
|
||||
|
||||
* Change user defined implementations of Fields and
|
||||
FieldsReader to meet the new requirements.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -13,10 +13,10 @@ This section describes all of the concepts defined by the library.
|
||||
[include concept/BodyReader.qbk]
|
||||
[include concept/BodyWriter.qbk]
|
||||
[include concept/BufferSequence.qbk]
|
||||
[include concept/File.qbk]
|
||||
[include concept/DynamicBuffer.qbk]
|
||||
[include concept/Fields.qbk]
|
||||
[include concept/FieldsReader.qbk]
|
||||
[include concept/File.qbk]
|
||||
[include concept/Streams.qbk]
|
||||
|
||||
[endsect]
|
||||
|
@@ -39,9 +39,10 @@ In this table:
|
||||
[heading Associated Types]
|
||||
|
||||
* __Body__
|
||||
|
||||
* [link beast.ref.beast__http__is_body_reader `is_body_reader`]
|
||||
|
||||
[heading BodyReader requirements]
|
||||
[heading BodyReader requirements]
|
||||
[table Valid Expressions
|
||||
[[Expression] [Type] [Semantics, Pre/Post-conditions]]
|
||||
[
|
||||
|
@@ -13,26 +13,36 @@ store the request target and non-standard strings for method and obsolete
|
||||
reason phrase as needed. Types which meet these requirements can always
|
||||
be serialized.
|
||||
|
||||
[heading Associated Types]
|
||||
|
||||
* __FieldsReader__
|
||||
|
||||
* [link beast.ref.beast__http__is_fields `is_fields`]
|
||||
|
||||
[heading Requirements]
|
||||
|
||||
In this table:
|
||||
|
||||
* `X` denotes a type that meets the requirements of [*Fields].
|
||||
* `F` denotes a type that meets the requirements of [*Fields].
|
||||
|
||||
* `R` denotes a type meeting the requirements of __FieldsReader__.
|
||||
|
||||
* `a` denotes a value of type `X`.
|
||||
* `a` denotes a value of type `F`.
|
||||
|
||||
* `c` denotes a (possibly const) value of type `X`.
|
||||
|
||||
* `s` is a value of type [link beast.ref.beast__string_view `string_view`].
|
||||
* `c` denotes a (possibly const) value of type `F`.
|
||||
|
||||
* `b` is a value of type `bool`
|
||||
|
||||
* `n` is a value of type `boost::optional<std::uint64_t>`.
|
||||
|
||||
[table Fields requirements
|
||||
[[expression][type][semantics, pre/post-conditions]]
|
||||
* `s` is a value of type [link beast.ref.beast__string_view `string_view`].
|
||||
|
||||
* `v` is a value of type `unsigned int` representing the HTTP-version.
|
||||
|
||||
[table Valid expressions
|
||||
[[Expression] [Type] [Semantics, Pre/Post-conditions]]
|
||||
[
|
||||
[`X::reader`]
|
||||
[`F::reader`]
|
||||
[`R`]
|
||||
[
|
||||
A type which meets the requirements of __FieldsReader__.
|
||||
@@ -62,6 +72,41 @@ In this table:
|
||||
retrieving the reason text previously set with a call to
|
||||
`set_reason_impl` using a non-empty string.
|
||||
]
|
||||
][
|
||||
[`c.get_chunked_impl()`]
|
||||
[`bool`]
|
||||
[
|
||||
Returns `true` if the
|
||||
[@https://tools.ietf.org/html/rfc7230#section-3.3.1 [*Transfer-Encoding]]
|
||||
field value indicates that the payload is chunk encoded. Both
|
||||
of these conditions must be true:
|
||||
[itemized_list
|
||||
[
|
||||
The Transfer-Encoding field is present in the message.
|
||||
][
|
||||
The last item the value of the field is "chunked".
|
||||
]]
|
||||
]
|
||||
][
|
||||
[`c.get_keep_alive_impl(v)`]
|
||||
[`bool`]
|
||||
[
|
||||
Returns `true` if the semantics of the
|
||||
[@https://tools.ietf.org/html/rfc7230#section-6.1 [*Connection]]
|
||||
field and version indicate that the connection should remain
|
||||
open after the corresponding response is transmitted or received:
|
||||
|
||||
[itemized_list
|
||||
[
|
||||
If `(v < 11)` the function returns `true` if the "keep-alive"
|
||||
token is present in the Connection field value. Otherwise the
|
||||
function returns `false`.
|
||||
][
|
||||
If `(v == 11)`, the function returns `false` if the "close"
|
||||
token is present in the Connection field value. Otherwise the
|
||||
function returns `true`.
|
||||
]]
|
||||
]
|
||||
][
|
||||
[`a.set_method_impl(s)`]
|
||||
[]
|
||||
@@ -93,37 +138,80 @@ In this table:
|
||||
is not supported by the container.
|
||||
]
|
||||
][
|
||||
[`a.prepare_payload_impl(b,n)`]
|
||||
[`a.set_chunked_impl(b)`]
|
||||
[]
|
||||
[
|
||||
Adjusts the Content-Length and Transfer-Encoding fields to
|
||||
account for the payload metadata indicated by `b` and `n` as
|
||||
follows:
|
||||
Adjusts the
|
||||
[@https://tools.ietf.org/html/rfc7230#section-3.3.1 [*Transfer-Encoding]]
|
||||
field as follows:
|
||||
|
||||
* If `b` is `true`, the chunked Transfer-Encoding should be applied
|
||||
to the end of the list of encodings if it is not already there
|
||||
or if the field was not present. Any Content-Length fields should
|
||||
be removed.
|
||||
|
||||
* If `b` is `false` and `n` contains a value, set the Content-Length
|
||||
field to `*n`, replacing any previous Content-Length fields. Remove
|
||||
the chunked encoding from the end of the Transfer-Encoding field
|
||||
if the field is present ahd chunked is the last encoding.
|
||||
|
||||
* If `b` is `false` and `n` contains no value, remove all Content-Length
|
||||
fields, and remove the chunked encoding from the end of the
|
||||
Transfer-Encoding field if the field is present ahd chunked is the
|
||||
last encoding.
|
||||
[itemized_list
|
||||
[
|
||||
If `b` is `true`, the "chunked" token is appended
|
||||
to the list of encodings if it does not already appear
|
||||
last in the list.
|
||||
If the Transfer-Encoding field is absent, the field will
|
||||
be inserted to the container with the value "chunked".
|
||||
][
|
||||
If `b` is `false, the "chunked" token is removed from the
|
||||
list of encodings if it appears last in the list.
|
||||
If the result of the removal leaves the list of encodings
|
||||
empty, the Transfer-Encoding field shall not appear when
|
||||
the associated __FieldsReader__ serializes the fields.
|
||||
]]
|
||||
]
|
||||
|
||||
][
|
||||
[`is_fields<X>`]
|
||||
[`std::true_type`]
|
||||
[`a.set_content_length_impl(n)`]
|
||||
[]
|
||||
[
|
||||
An alias for `std::true_type` for `X`, otherwise an alias
|
||||
for `std::false_type`.
|
||||
Adjusts the
|
||||
[@https://tools.ietf.org/html/rfc7230#section-3.3.2 [*Content-Length]]
|
||||
field as follows:
|
||||
|
||||
[itemized_list
|
||||
[
|
||||
If `n` contains a value, the Content-Length field
|
||||
will be set to the text representation of the value.
|
||||
Any previous Content-Length fields are removed from
|
||||
the container.
|
||||
][
|
||||
If `n` does not contain a value, any present Content-Length
|
||||
fields are removed from the container.
|
||||
]]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
][
|
||||
[`a.set_keep_alive_impl(v,b)`]
|
||||
[]
|
||||
[
|
||||
Adjusts the
|
||||
[@https://tools.ietf.org/html/rfc7230#section-6.1 [*Connection]]
|
||||
field value depending on the values of `v` and `b`. The field
|
||||
value is treated as
|
||||
[@https://tools.ietf.org/html/rfc7230#section-6.1 ['connection-option]]
|
||||
(rfc7230).
|
||||
|
||||
[itemized_list
|
||||
[
|
||||
If `(v < 11 && b)`, then all "close" tokens present in the
|
||||
value are removed, and the "keep-alive" token is added to
|
||||
the valueif it is not already present.
|
||||
][
|
||||
If `(v < 11 && ! b)`, then all "close" and "keep-alive"
|
||||
tokens present in the value are removed.
|
||||
|
||||
][
|
||||
If `(v == 11 && b)`, then all "keep-alive" and "close"
|
||||
tokens present in the value are removed.
|
||||
][
|
||||
If `(v == 11 && ! b)`, then all "keep-alive" tokens present
|
||||
in the value are removed, and the "close" token is added to
|
||||
the value if it is not already present.
|
||||
]]
|
||||
]
|
||||
|
||||
]]
|
||||
|
||||
[heading Exemplar]
|
||||
|
||||
|
@@ -64,19 +64,6 @@ In this table:
|
||||
if the [*Transfer-Encoding] field is present, and the last
|
||||
token in its value is "chunked".
|
||||
]
|
||||
][
|
||||
[`a.keep_alive()`]
|
||||
[`bool`]
|
||||
[
|
||||
Called once after construction, this function returns `true`
|
||||
if the semantics of the mesage indicate that the connection
|
||||
should remain open after processing the message. When the
|
||||
HTTP version is below 1.1, the function returns `true` only
|
||||
if the [*Connection] field is present, and its value contains
|
||||
the "keep-alive" token. For HTTP versions 1.1 and above, the
|
||||
function returns `true` if the [*Connection] field is absent,
|
||||
or if the field value does not contain the "close" token.
|
||||
]
|
||||
][
|
||||
[`a.get()`]
|
||||
[`X::const_buffers_type`]
|
||||
|
@@ -53,13 +53,17 @@ struct fields_model
|
||||
string_view target() const;
|
||||
|
||||
protected:
|
||||
void set_method_impl(string_view s);
|
||||
void set_target_impl(string_view s);
|
||||
void set_reason_impl(string_view s);
|
||||
string_view get_method_impl() const;
|
||||
string_view get_target_impl() const;
|
||||
string_view get_reason_impl() const;
|
||||
void prepare_payload_impl(bool b, boost::optional<std::uint64_t> n);
|
||||
bool get_chunked_impl() const;
|
||||
bool get_keep_alive_impl(unsigned) const;
|
||||
void set_method_impl(string_view);
|
||||
void set_target_impl(string_view);
|
||||
void set_reason_impl(string_view);
|
||||
void set_chunked_impl(bool);
|
||||
void set_content_length_impl(boost::optional<std::uint64_t>);
|
||||
void set_keep_alive_impl(unsigned, bool);
|
||||
};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
@@ -91,60 +95,90 @@ struct is_fields_helper : T
|
||||
{
|
||||
template<class U = is_fields_helper>
|
||||
static auto f1(int) -> decltype(
|
||||
void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_method_impl(),
|
||||
std::true_type());
|
||||
static auto f1(...) -> std::false_type;
|
||||
using t1 = decltype(f1(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f2(int) -> decltype(
|
||||
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_target_impl(),
|
||||
std::true_type());
|
||||
static auto f2(...) -> std::false_type;
|
||||
using t2 = decltype(f2(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f3(int) -> decltype(
|
||||
void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_reason_impl(),
|
||||
std::true_type());
|
||||
static auto f3(...) -> std::false_type;
|
||||
using t3 = decltype(f3(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f4(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U&>().get_method_impl(),
|
||||
std::declval<bool&>() = std::declval<U const&>().get_chunked_impl(),
|
||||
std::true_type());
|
||||
static auto f4(...) -> std::false_type;
|
||||
using t4 = decltype(f4(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f5(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U&>().get_target_impl(),
|
||||
std::declval<bool&>() = std::declval<U const&>().get_keep_alive_impl(
|
||||
std::declval<unsigned>()),
|
||||
std::true_type());
|
||||
static auto f5(...) -> std::false_type;
|
||||
using t5 = decltype(f5(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f6(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U&>().get_reason_impl(),
|
||||
void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f6(...) -> std::false_type;
|
||||
using t6 = decltype(f6(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f7(int) -> decltype(
|
||||
void(std::declval<U&>().prepare_payload_impl(
|
||||
std::declval<bool>(),
|
||||
std::declval<boost::optional<std::uint64_t>>())),
|
||||
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f7(...) -> std::false_type;
|
||||
using t7 = decltype(f7(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f8(int) -> decltype(
|
||||
void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f8(...) -> std::false_type;
|
||||
using t8 = decltype(f8(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f9(int) -> decltype(
|
||||
void(std::declval<U&>().set_chunked_impl(std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f9(...) -> std::false_type;
|
||||
using t9 = decltype(f9(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f10(int) -> decltype(
|
||||
void(std::declval<U&>().set_content_length_impl(
|
||||
std::declval<boost::optional<std::uint64_t>>())),
|
||||
std::true_type());
|
||||
static auto f10(...) -> std::false_type;
|
||||
using t10 = decltype(f10(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f11(int) -> decltype(
|
||||
void(std::declval<U&>().set_keep_alive_impl(
|
||||
std::declval<unsigned>(),
|
||||
std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f11(...) -> std::false_type;
|
||||
using t11 = decltype(f11(0));
|
||||
|
||||
using type = std::integral_constant<bool,
|
||||
t1::value && t2::value && t3::value &&
|
||||
t4::value && t5::value && t6::value &&
|
||||
t7::value
|
||||
>;
|
||||
t1::value && t2::value && t3::value &&
|
||||
t4::value && t5::value && t6::value &&
|
||||
t7::value && t8::value && t9::value &&
|
||||
t10::value && t11::value>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
@@ -567,47 +567,74 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void set_reason_impl(string_view s);
|
||||
|
||||
/** Returns the request-method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view get_method_impl() const;
|
||||
string_view
|
||||
get_method_impl() const;
|
||||
|
||||
/** Returns the request-target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view get_target_impl() const;
|
||||
string_view
|
||||
get_target_impl() const;
|
||||
|
||||
/** Returns the response reason-phrase string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
string_view get_reason_impl() const;
|
||||
string_view
|
||||
get_reason_impl() const;
|
||||
|
||||
/** Adjusts the payload related fields
|
||||
/** Returns the chunked Transfer-Encoding setting
|
||||
*/
|
||||
bool
|
||||
get_chunked_impl() const;
|
||||
|
||||
/** Returns the keep-alive setting
|
||||
*/
|
||||
bool
|
||||
get_keep_alive_impl(unsigned version) const;
|
||||
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
prepare_payload_impl(bool chunked,
|
||||
boost::optional<std::uint64_t> size);
|
||||
set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void
|
||||
set_reason_impl(string_view s);
|
||||
|
||||
/** Adjusts the chunked Transfer-Encoding value
|
||||
*/
|
||||
void
|
||||
set_chunked_impl(bool value);
|
||||
|
||||
/** Sets or clears the Content-Length field
|
||||
*/
|
||||
void
|
||||
set_content_length_impl(
|
||||
boost::optional<std::uint64_t> const& value);
|
||||
|
||||
/** Adjusts the Connection field
|
||||
*/
|
||||
void
|
||||
set_keep_alive_impl(
|
||||
unsigned version, bool keep_alive);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
|
@@ -19,6 +19,13 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000
|
||||
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
|
||||
#ifndef BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
|
||||
#define BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
@@ -133,17 +140,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
get_chunked() const;
|
||||
|
||||
bool
|
||||
get_keep_alive(int version) const;
|
||||
|
||||
basic_fields const& f_;
|
||||
boost::asio::const_buffer cb_[3];
|
||||
char buf_[13];
|
||||
bool chunked_;
|
||||
bool keep_alive_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
@@ -160,18 +159,6 @@ public:
|
||||
reader(basic_fields const& f,
|
||||
unsigned version, unsigned code);
|
||||
|
||||
bool
|
||||
chunked()
|
||||
{
|
||||
return chunked_;
|
||||
}
|
||||
|
||||
bool
|
||||
keep_alive()
|
||||
{
|
||||
return keep_alive_;
|
||||
}
|
||||
|
||||
const_buffers_type
|
||||
get() const
|
||||
{
|
||||
@@ -184,48 +171,11 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
bool
|
||||
basic_fields<Allocator>::reader::
|
||||
get_chunked() const
|
||||
{
|
||||
auto const te = token_list{
|
||||
f_[field::transfer_encoding]};
|
||||
for(auto it = te.begin(); it != te.end();)
|
||||
{
|
||||
auto const next = std::next(it);
|
||||
if(next == te.end())
|
||||
return iequals(*it, "chunked");
|
||||
it = next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
bool
|
||||
basic_fields<Allocator>::reader::
|
||||
get_keep_alive(int version) const
|
||||
{
|
||||
if(version < 11)
|
||||
{
|
||||
auto const it = f_.find(field::connection);
|
||||
if(it == f_.end())
|
||||
return false;
|
||||
return token_list{it->value()}.exists("keep-alive");
|
||||
}
|
||||
auto const it = f_.find(field::connection);
|
||||
if(it == f_.end())
|
||||
return true;
|
||||
return ! token_list{it->value()}.exists("close");
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
basic_fields<Allocator>::reader::
|
||||
reader(basic_fields const& f,
|
||||
unsigned version, verb v)
|
||||
: f_(f)
|
||||
, chunked_(get_chunked())
|
||||
, keep_alive_(get_keep_alive(version))
|
||||
{
|
||||
/*
|
||||
request
|
||||
@@ -264,8 +214,6 @@ basic_fields<Allocator>::reader::
|
||||
reader(basic_fields const& f,
|
||||
unsigned version, unsigned code)
|
||||
: f_(f)
|
||||
, chunked_(get_chunked())
|
||||
, keep_alive_(get_keep_alive(version))
|
||||
{
|
||||
/*
|
||||
response
|
||||
@@ -761,37 +709,145 @@ equal_range(string_view name) const ->
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Filter a token list
|
||||
//
|
||||
template<class String, class Pred>
|
||||
void
|
||||
filter_token_list(
|
||||
String& s,
|
||||
string_view const& value,
|
||||
Pred&& pred)
|
||||
{
|
||||
token_list te{value};
|
||||
auto it = te.begin();
|
||||
auto last = te.end();
|
||||
if(it == last)
|
||||
return;
|
||||
while(pred(*it))
|
||||
if(++it == last)
|
||||
return;
|
||||
s.append(it->data(), it->size());
|
||||
while(++it != last)
|
||||
{
|
||||
if(! pred(*it))
|
||||
{
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the last item in a token list
|
||||
template<class String, class Pred>
|
||||
void
|
||||
filter_token_list_last(
|
||||
String& s,
|
||||
string_view const& value,
|
||||
Pred&& pred)
|
||||
{
|
||||
token_list te{value};
|
||||
if(te.begin() != te.end())
|
||||
{
|
||||
auto it = te.begin();
|
||||
auto next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! pred(*it))
|
||||
s.append(it->data(), it->size());
|
||||
return;
|
||||
}
|
||||
s.append(it->data(), it->size());
|
||||
for(;;)
|
||||
{
|
||||
it = next;
|
||||
next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! pred(*it))
|
||||
{
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class String>
|
||||
void
|
||||
keep_alive_impl(
|
||||
String& s, string_view const& value,
|
||||
unsigned version, bool keep_alive)
|
||||
{
|
||||
if(version < 11)
|
||||
{
|
||||
if(keep_alive)
|
||||
{
|
||||
// remove close
|
||||
filter_token_list(s, value,
|
||||
[](string_view s)
|
||||
{
|
||||
return iequals(s, "close");
|
||||
});
|
||||
// add keep-alive
|
||||
if(s.empty())
|
||||
s.append("keep-alive");
|
||||
else if(! token_list{value}.exists("keep-alive"))
|
||||
s.append(", keep-alive");
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove close and keep-alive
|
||||
filter_token_list(s, value,
|
||||
[](string_view s)
|
||||
{
|
||||
return
|
||||
iequals(s, "close") ||
|
||||
iequals(s, "keep-alive");
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(keep_alive)
|
||||
{
|
||||
// remove close and keep-alive
|
||||
filter_token_list(s, value,
|
||||
[](string_view s)
|
||||
{
|
||||
return
|
||||
iequals(s, "close") ||
|
||||
iequals(s, "keep-alive");
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove keep-alive
|
||||
filter_token_list(s, value,
|
||||
[](string_view s)
|
||||
{
|
||||
return iequals(s, "keep-alive");
|
||||
});
|
||||
// add close
|
||||
if(s.empty())
|
||||
s.append("close");
|
||||
else if(! token_list{value}.exists("close"))
|
||||
s.append(", close");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Fields
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_method_impl(string_view s)
|
||||
{
|
||||
realloc_string(method_, s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_target_impl(string_view s)
|
||||
{
|
||||
realloc_target(
|
||||
target_or_reason_, s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_reason_impl(string_view s)
|
||||
{
|
||||
realloc_string(
|
||||
target_or_reason_, s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
string_view
|
||||
@@ -823,73 +879,118 @@ get_reason_impl() const
|
||||
return target_or_reason_;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Builds a new string with "chunked" taken off the end if present
|
||||
template<class String>
|
||||
void
|
||||
without_chunked_last(String& s, string_view const& tokens)
|
||||
template<class Allocator>
|
||||
bool
|
||||
basic_fields<Allocator>::
|
||||
get_chunked_impl() const
|
||||
{
|
||||
token_list te{tokens};
|
||||
if(te.begin() != te.end())
|
||||
auto const te = token_list{
|
||||
(*this)[field::transfer_encoding]};
|
||||
for(auto it = te.begin(); it != te.end();)
|
||||
{
|
||||
auto it = te.begin();
|
||||
auto next = std::next(it);
|
||||
auto const next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! iequals(*it, "chunked"))
|
||||
s.append(it->data(), it->size());
|
||||
return;
|
||||
}
|
||||
s.append(it->data(), it->size());
|
||||
for(;;)
|
||||
{
|
||||
it = next;
|
||||
next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! iequals(*it, "chunked"))
|
||||
{
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.append(", ");
|
||||
s.append(it->data(), it->size());
|
||||
}
|
||||
return iequals(*it, "chunked");
|
||||
it = next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // detail
|
||||
template<class Allocator>
|
||||
bool
|
||||
basic_fields<Allocator>::
|
||||
get_keep_alive_impl(unsigned version) const
|
||||
{
|
||||
auto const it = find(field::connection);
|
||||
if(version < 11)
|
||||
{
|
||||
if(it == end())
|
||||
return false;
|
||||
return token_list{
|
||||
it->value()}.exists("keep-alive");
|
||||
}
|
||||
if(it == end())
|
||||
return true;
|
||||
return ! token_list{
|
||||
it->value()}.exists("close");
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_method_impl(string_view s)
|
||||
{
|
||||
realloc_string(method_, s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_target_impl(string_view s)
|
||||
{
|
||||
realloc_target(
|
||||
target_or_reason_, s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
inline
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_reason_impl(string_view s)
|
||||
{
|
||||
realloc_string(
|
||||
target_or_reason_, s);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
prepare_payload_impl(bool chunked,
|
||||
boost::optional<std::uint64_t> size)
|
||||
set_chunked_impl(bool value)
|
||||
{
|
||||
if(chunked)
|
||||
auto it = find(field::transfer_encoding);
|
||||
if(value)
|
||||
{
|
||||
BOOST_ASSERT(! size);
|
||||
erase(field::content_length);
|
||||
auto it = find(field::transfer_encoding);
|
||||
// append "chunked"
|
||||
if(it == end())
|
||||
{
|
||||
set(field::transfer_encoding, "chunked");
|
||||
return;
|
||||
}
|
||||
|
||||
static_string<max_static_buffer> temp;
|
||||
if(it->value().size() <= temp.size() + 9)
|
||||
auto const te = token_list{it->value()};
|
||||
for(auto itt = te.begin();;)
|
||||
{
|
||||
temp.append(it->value().data(), it->value().size());
|
||||
temp.append(", chunked", 9);
|
||||
set(field::transfer_encoding, temp);
|
||||
auto const next = std::next(itt);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(iequals(*itt, "chunked"))
|
||||
return; // already set
|
||||
break;
|
||||
}
|
||||
itt = next;
|
||||
}
|
||||
static_string<max_static_buffer> buf;
|
||||
if(it->value().size() <= buf.size() + 9)
|
||||
{
|
||||
buf.append(it->value().data(), it->value().size());
|
||||
buf.append(", chunked", 9);
|
||||
set(field::transfer_encoding, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
|
||||
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
|
||||
std::string s;
|
||||
#else
|
||||
using rebind_type =
|
||||
typename std::allocator_traits<
|
||||
Allocator>::template rebind_alloc<char>;
|
||||
std::basic_string<
|
||||
char,
|
||||
std::char_traits<char>,
|
||||
rebind_type> s{rebind_type{alloc_}};
|
||||
#endif
|
||||
s.reserve(it->value().size() + 9);
|
||||
s.append(it->value().data(), it->value().size());
|
||||
s.append(", chunked", 9);
|
||||
@@ -897,44 +998,100 @@ prepare_payload_impl(bool chunked,
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto const clear_chunked =
|
||||
[this]()
|
||||
{
|
||||
auto it = find(field::transfer_encoding);
|
||||
if(it == end())
|
||||
return;
|
||||
|
||||
// We have to just try it because we can't
|
||||
// know ahead of time if there's enough room.
|
||||
try
|
||||
{
|
||||
static_string<max_static_buffer> temp;
|
||||
detail::without_chunked_last(temp, it->value());
|
||||
if(! temp.empty())
|
||||
set(field::transfer_encoding, temp);
|
||||
else
|
||||
erase(field::transfer_encoding);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
std::string s;
|
||||
s.reserve(it->value().size());
|
||||
detail::without_chunked_last(s, it->value());
|
||||
if(! s.empty())
|
||||
set(field::transfer_encoding, s);
|
||||
else
|
||||
erase(field::transfer_encoding);
|
||||
}
|
||||
};
|
||||
if(size)
|
||||
// filter "chunked"
|
||||
if(it == end())
|
||||
return;
|
||||
try
|
||||
{
|
||||
clear_chunked();
|
||||
set(field::content_length, *size);
|
||||
static_string<max_static_buffer> buf;
|
||||
detail::filter_token_list_last(buf, it->value(),
|
||||
[](string_view const& s)
|
||||
{
|
||||
return iequals(s, "chunked");
|
||||
});
|
||||
if(! buf.empty())
|
||||
set(field::transfer_encoding, buf);
|
||||
else
|
||||
erase(field::transfer_encoding);
|
||||
}
|
||||
else
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
clear_chunked();
|
||||
#ifdef BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
|
||||
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
|
||||
std::string s;
|
||||
#else
|
||||
using rebind_type =
|
||||
typename std::allocator_traits<
|
||||
Allocator>::template rebind_alloc<char>;
|
||||
std::basic_string<
|
||||
char,
|
||||
std::char_traits<char>,
|
||||
rebind_type> s{rebind_type{alloc_}};
|
||||
#endif
|
||||
s.reserve(it->value().size());
|
||||
detail::filter_token_list_last(s, it->value(),
|
||||
[](string_view const& s)
|
||||
{
|
||||
return iequals(s, "chunked");
|
||||
});
|
||||
if(! s.empty())
|
||||
set(field::transfer_encoding, s);
|
||||
else
|
||||
erase(field::transfer_encoding);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_content_length_impl(
|
||||
boost::optional<std::uint64_t> const& value)
|
||||
{
|
||||
if(! value)
|
||||
erase(field::content_length);
|
||||
else
|
||||
set(field::content_length, *value);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_fields<Allocator>::
|
||||
set_keep_alive_impl(
|
||||
unsigned version, bool keep_alive)
|
||||
{
|
||||
// VFALCO What about Proxy-Connection ?
|
||||
auto const value = (*this)[field::connection];
|
||||
try
|
||||
{
|
||||
static_string<max_static_buffer> buf;
|
||||
detail::keep_alive_impl(
|
||||
buf, value, version, keep_alive);
|
||||
if(buf.empty())
|
||||
erase(field::connection);
|
||||
else
|
||||
set(field::connection, buf);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
#ifdef BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
|
||||
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
|
||||
std::string s;
|
||||
#else
|
||||
using rebind_type =
|
||||
typename std::allocator_traits<
|
||||
Allocator>::template rebind_alloc<char>;
|
||||
std::basic_string<
|
||||
char,
|
||||
std::char_traits<char>,
|
||||
rebind_type> s{rebind_type{alloc_}};
|
||||
#endif
|
||||
s.reserve(value.size());
|
||||
detail::keep_alive_impl(
|
||||
s, value, version, keep_alive);
|
||||
if(s.empty())
|
||||
erase(field::connection);
|
||||
else
|
||||
set(field::connection, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -313,40 +313,25 @@ message(std::piecewise_construct_t,
|
||||
{
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class BodyArg, class>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
message(BodyArg&& body_arg)
|
||||
: body(std::forward<BodyArg>(body_arg))
|
||||
chunked(bool value)
|
||||
{
|
||||
this->set_chunked_impl(value);
|
||||
this->set_content_length_impl(boost::none);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class BodyArg, class HeaderArg, class>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
message(BodyArg&& body_arg, HeaderArg&& header_arg)
|
||||
: header_type(std::forward<HeaderArg>(header_arg))
|
||||
, body(std::forward<BodyArg>(body_arg))
|
||||
content_length(
|
||||
boost::optional<std::uint64_t> const& value)
|
||||
{
|
||||
this->set_content_length_impl(value);
|
||||
this->set_chunked_impl(false);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs, class... HeaderArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...>&& body_args,
|
||||
std::tuple<HeaderArgs...>&& header_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args, header_args,
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{},
|
||||
beast::detail::make_index_sequence<
|
||||
sizeof...(HeaderArgs)>{})
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
boost::optional<std::uint64_t>
|
||||
message<isRequest, Body, Fields>::
|
||||
@@ -371,20 +356,20 @@ prepare_payload(std::true_type)
|
||||
this->method() == verb::put ||
|
||||
this->method() == verb::post)
|
||||
{
|
||||
this->prepare_payload_impl(false, *n);
|
||||
this->content_length(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->prepare_payload_impl(false, boost::none);
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
else if(this->version >= 11)
|
||||
{
|
||||
this->prepare_payload_impl(true, boost::none);
|
||||
this->chunked(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->prepare_payload_impl(false, boost::none);
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,13 +389,9 @@ prepare_payload(std::false_type)
|
||||
"invalid response body"});
|
||||
}
|
||||
if(n)
|
||||
{
|
||||
this->prepare_payload_impl(false, *n);
|
||||
}
|
||||
this->content_length(n);
|
||||
else
|
||||
{
|
||||
this->prepare_payload_impl(true, boost::none);
|
||||
}
|
||||
this->chunked(true);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@@ -88,8 +88,8 @@ next(error_code& ec, Visit&& visit)
|
||||
{
|
||||
frdinit(std::integral_constant<bool,
|
||||
isRequest>{});
|
||||
keep_alive_ = frd_->keep_alive();
|
||||
chunked_ = frd_->chunked();
|
||||
keep_alive_ = m_.keep_alive();
|
||||
chunked_ = m_.chunked();
|
||||
if(chunked_)
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
|
@@ -642,6 +642,72 @@ struct message : header<isRequest, Fields>
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns `true` if the chunked Transfer-Encoding is specified
|
||||
bool
|
||||
chunked() const
|
||||
{
|
||||
return this->get_chunked_impl();
|
||||
}
|
||||
|
||||
/** Set or clear the chunked Transfer-Encoding
|
||||
|
||||
This function will set or removed the "chunked" transfer
|
||||
encoding as the last item in the list of encodings in the
|
||||
field.
|
||||
|
||||
If the result of removing the chunked token results in an
|
||||
empty string, the field is erased.
|
||||
|
||||
The Content-Length field is erased unconditionally.
|
||||
*/
|
||||
void
|
||||
chunked(bool value);
|
||||
|
||||
/** Set or clear the Content-Length field
|
||||
|
||||
This function adjusts the Content-Length field as follows:
|
||||
|
||||
@li If `value` specifies a value, the Content-Length field
|
||||
is set to the value. Otherwise
|
||||
|
||||
@li The Content-Length field is erased.
|
||||
|
||||
If "chunked" token appears as the last item in the
|
||||
Transfer-Encoding field it is unconditionally removed.
|
||||
|
||||
@param value The value to set for Content-Length.
|
||||
*/
|
||||
void
|
||||
content_length(boost::optional<std::uint64_t> const& value);
|
||||
|
||||
/** Returns `true` if the message semantics indicate keep-alive
|
||||
|
||||
The value depends on the version in the message, which must
|
||||
be set to the final value before this function is called or
|
||||
else the return value is unreliable.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const
|
||||
{
|
||||
return this->get_keep_alive_impl(this->version);
|
||||
}
|
||||
|
||||
/** Set the keep-alive message semantic option
|
||||
|
||||
This function adjusts the Connection field to indicate
|
||||
whether or not the connection should be kept open after
|
||||
the corresponding response. The result depends on the
|
||||
version set on the message, which must be set to the
|
||||
final value before making this call.
|
||||
|
||||
@param value `true` if the connection should persist.
|
||||
*/
|
||||
void
|
||||
keep_alive(bool value)
|
||||
{
|
||||
this->set_keep_alive_impl(this->version, value);
|
||||
}
|
||||
|
||||
/** Returns the payload size of the body in octets if possible.
|
||||
|
||||
This function invokes the @b Body algorithm to measure
|
||||
|
@@ -190,52 +190,76 @@ public:
|
||||
struct reader;
|
||||
|
||||
protected:
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void set_reason_impl(string_view s);
|
||||
|
||||
/** Returns the request-method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view get_method_impl() const;
|
||||
string_view
|
||||
get_method_impl() const;
|
||||
|
||||
/** Returns the request-target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view get_target_impl() const;
|
||||
string_view
|
||||
get_target_impl() const;
|
||||
|
||||
/** Returns the response reason-phrase string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
string_view get_reason_impl() const;
|
||||
string_view
|
||||
get_reason_impl() const;
|
||||
|
||||
/** Updates the payload metadata.
|
||||
|
||||
@param b `true` if chunked
|
||||
|
||||
@param n The content length if known, otherwise `boost::none`
|
||||
/** Returns the chunked Transfer-Encoding setting
|
||||
*/
|
||||
void prepare_payload_impl(bool b, boost::optional<std::uint64_t> n);
|
||||
bool
|
||||
get_chunked_impl() const;
|
||||
|
||||
/** Returns the keep-alive setting
|
||||
*/
|
||||
bool
|
||||
get_keep_alive_impl(unsigned version) const;
|
||||
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void
|
||||
set_reason_impl(string_view s);
|
||||
|
||||
/** Sets or clears the chunked Transfer-Encoding value
|
||||
*/
|
||||
void
|
||||
set_chunked_impl(bool value);
|
||||
|
||||
/** Sets or clears the Content-Length field
|
||||
*/
|
||||
void
|
||||
set_content_length_impl(boost::optional<std::uint64_t>);
|
||||
|
||||
/** Adjusts the Connection field
|
||||
*/
|
||||
void
|
||||
set_keep_alive_impl(unsigned version, bool keep_alive);
|
||||
};
|
||||
|
||||
static_assert(is_fields<Fields>::value, "");
|
||||
static_assert(is_fields<Fields>::value,
|
||||
"Fields requirements not met");
|
||||
|
||||
//]
|
||||
|
||||
|
@@ -1124,7 +1124,7 @@ public:
|
||||
0x0A,0x49,0x66,0x3A,0x20,0x40,0xC1,0x50,0x5C,0xD6,0xC3,0x86,0xFC,0x8D,0x5C,0x7C,0x96,0x45,0x0D,0x0A,0x4D,0x4D,0x48,0x53,
|
||||
0x2D,0x45,0x78,0x65,0x6D,0x70,0x74,0x65,0x64,0x2D,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x3A,0x0D,0x0A,0x49,0x6E,0x6A,0x65,
|
||||
0x63,0x74,0x69,0x6F,0x6E,0x2D,0x49,0x6E,0x66,0x6F,0x3A,0x20,0x0D,0x0A,0x43,0x6F,0x6E,0x74,0x65,0x74,0x6E,0x2D,0x4C,0x65,
|
||||
0x6E,0x67,0x74,0x68,0x3A,0x20,0x30,0x0D,0x0A,0x0D,0x0A
|
||||
0x6E,0x67,0x74,0x68,0x3A,0x20,0x30,0x0D,0x0A,0x0D,0x0A
|
||||
};
|
||||
|
||||
error_code ec;
|
||||
|
@@ -584,7 +584,323 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void run() override
|
||||
void
|
||||
testKeepAlive()
|
||||
{
|
||||
response<empty_body> res;
|
||||
auto const keep_alive =
|
||||
[&](bool v)
|
||||
{
|
||||
res.keep_alive(v);
|
||||
BEAST_EXPECT(
|
||||
(res.keep_alive() && v) ||
|
||||
(! res.keep_alive() && ! v));
|
||||
};
|
||||
|
||||
BOOST_STATIC_ASSERT(fields::max_static_buffer == 4096);
|
||||
std::string const big(4096 + 1, 'a');
|
||||
|
||||
// HTTP/1.0
|
||||
res.version = 10;
|
||||
res.erase(field::connection);
|
||||
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.set(field::connection, "close");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.set(field::connection, "keep-alive");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.set(field::connection, "keep-alive, close");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.erase(field::connection);
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == "keep-alive");
|
||||
|
||||
res.set(field::connection, "close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == "keep-alive");
|
||||
|
||||
res.set(field::connection, "keep-alive");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == "keep-alive");
|
||||
|
||||
res.set(field::connection, "keep-alive, close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == "keep-alive");
|
||||
|
||||
auto const test10 =
|
||||
[&](std::string s)
|
||||
{
|
||||
res.set(field::connection, s);
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, s + ", close");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s);
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s + ", close");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, s);
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == s + ", keep-alive");
|
||||
|
||||
res.set(field::connection, s + ", close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == s + ", keep-alive");
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s);
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == "keep-alive, " + s);
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s+ ", close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == "keep-alive, " + s);
|
||||
};
|
||||
|
||||
test10("foo");
|
||||
test10(big);
|
||||
|
||||
// HTTP/1.1
|
||||
res.version = 11;
|
||||
|
||||
res.erase(field::connection);
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.set(field::connection, "close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.set(field::connection, "keep-alive");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.set(field::connection, "keep-alive, close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res.count(field::connection) == 0);
|
||||
|
||||
res.erase(field::connection);
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == "close");
|
||||
|
||||
res.set(field::connection, "close");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == "close");
|
||||
|
||||
res.set(field::connection, "keep-alive");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == "close");
|
||||
|
||||
res.set(field::connection, "keep-alive, close");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == "close");
|
||||
|
||||
auto const test11 =
|
||||
[&](std::string s)
|
||||
{
|
||||
res.set(field::connection, s);
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, s + ", close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s);
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s + ", close");
|
||||
keep_alive(true);
|
||||
BEAST_EXPECT(res[field::connection] == s);
|
||||
|
||||
res.set(field::connection, s);
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == s + ", close");
|
||||
|
||||
res.set(field::connection, "close, " + s);
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == "close, " + s);
|
||||
|
||||
res.set(field::connection, "keep-alive, " + s);
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == s + ", close");
|
||||
|
||||
res.set(field::connection, "close, " + s + ", keep-alive");
|
||||
keep_alive(false);
|
||||
BEAST_EXPECT(res[field::connection] == "close, " + s);
|
||||
};
|
||||
|
||||
test11("foo");
|
||||
test11(big);
|
||||
}
|
||||
|
||||
void
|
||||
testContentLength()
|
||||
{
|
||||
response<empty_body> res{status::ok, 11};
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
res.content_length(0);
|
||||
BEAST_EXPECT(res[field::content_length] == "0");
|
||||
|
||||
res.content_length(100);
|
||||
BEAST_EXPECT(res[field::content_length] == "100");
|
||||
|
||||
res.content_length(boost::none);
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked");
|
||||
res.content_length(0);
|
||||
BEAST_EXPECT(res[field::content_length] == "0");
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked");
|
||||
res.content_length(100);
|
||||
BEAST_EXPECT(res[field::content_length] == "100");
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked");
|
||||
res.content_length(boost::none);
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
auto const check = [&](std::string s)
|
||||
{
|
||||
res.set(field::transfer_encoding, s);
|
||||
res.content_length(0);
|
||||
BEAST_EXPECT(res[field::content_length] == "0");
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == s);
|
||||
|
||||
res.set(field::transfer_encoding, s);
|
||||
res.content_length(100);
|
||||
BEAST_EXPECT(res[field::content_length] == "100");
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == s);
|
||||
|
||||
res.set(field::transfer_encoding, s);
|
||||
res.content_length(boost::none);
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == s);
|
||||
|
||||
res.set(field::transfer_encoding, s + ", chunked");
|
||||
res.content_length(0);
|
||||
BEAST_EXPECT(res[field::content_length] == "0");
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == s);
|
||||
|
||||
res.set(field::transfer_encoding, s + ", chunked");
|
||||
res.content_length(100);
|
||||
BEAST_EXPECT(res[field::content_length] == "100");
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == s);
|
||||
|
||||
res.set(field::transfer_encoding, s + ", chunked");
|
||||
res.content_length(boost::none);
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == s);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, " + s);
|
||||
res.content_length(0);
|
||||
BEAST_EXPECT(res[field::content_length] == "0");
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, " + s);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, " + s);
|
||||
res.content_length(100);
|
||||
BEAST_EXPECT(res[field::content_length] == "100");
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, " + s);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, " + s);
|
||||
res.content_length(boost::none);
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, " + s);
|
||||
};
|
||||
|
||||
check("foo");
|
||||
|
||||
BOOST_STATIC_ASSERT(fields::max_static_buffer == 4096);
|
||||
std::string const big(4096 + 1, 'a');
|
||||
|
||||
check(big);
|
||||
}
|
||||
|
||||
void
|
||||
testChunked()
|
||||
{
|
||||
response<empty_body> res{status::ok, 11};
|
||||
BEAST_EXPECT(res.count(field::content_length) == 0);
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
auto const chunked =
|
||||
[&](bool v)
|
||||
{
|
||||
res.chunked(v);
|
||||
BEAST_EXPECT(
|
||||
(res.chunked() && v) ||
|
||||
(! res.chunked() && ! v));
|
||||
BEAST_EXPECT(res.count(
|
||||
field::content_length) == 0);
|
||||
};
|
||||
|
||||
res.erase(field::transfer_encoding);
|
||||
res.set(field::content_length, 32);
|
||||
chunked(true);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked");
|
||||
|
||||
res.set(field::transfer_encoding, "chunked");
|
||||
chunked(true);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked");
|
||||
|
||||
res.erase(field::transfer_encoding);
|
||||
res.set(field::content_length, 32);
|
||||
chunked(false);
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
res.set(field::transfer_encoding, "chunked");
|
||||
chunked(false);
|
||||
BEAST_EXPECT(res.count(field::transfer_encoding) == 0);
|
||||
|
||||
|
||||
|
||||
res.set(field::transfer_encoding, "foo");
|
||||
chunked(true);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "foo, chunked");
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, foo");
|
||||
chunked(true);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, foo, chunked");
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, foo, chunked");
|
||||
chunked(true);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, foo, chunked");
|
||||
|
||||
res.set(field::transfer_encoding, "foo, chunked");
|
||||
chunked(false);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "foo");
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, foo");
|
||||
chunked(false);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, foo");
|
||||
|
||||
res.set(field::transfer_encoding, "chunked, foo, chunked");
|
||||
chunked(false);
|
||||
BEAST_EXPECT(res[field::transfer_encoding] == "chunked, foo");
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testMembers();
|
||||
testHeaders();
|
||||
@@ -592,6 +908,10 @@ public:
|
||||
testErase();
|
||||
testContainer();
|
||||
testPreparePayload();
|
||||
|
||||
testKeepAlive();
|
||||
testContentLength();
|
||||
testChunked();
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -191,13 +191,17 @@ public:
|
||||
|
||||
test_fields() = delete;
|
||||
test_fields(token) {}
|
||||
void set_method_impl(string_view) {}
|
||||
void set_target_impl(string_view s) { target = s.to_string(); }
|
||||
void set_reason_impl(string_view) {}
|
||||
string_view get_method_impl() const { return {}; }
|
||||
string_view get_target_impl() const { return target; }
|
||||
string_view get_reason_impl() const { return {}; }
|
||||
void prepare_payload_impl(bool, boost::optional<std::uint64_t>) {}
|
||||
bool get_chunked_impl() const { return false; }
|
||||
bool get_keep_alive_impl(unsigned) const { return true; }
|
||||
void set_method_impl(string_view) {}
|
||||
void set_target_impl(string_view s) { target = s.to_string(); }
|
||||
void set_reason_impl(string_view) {}
|
||||
void set_chunked_impl(bool) {}
|
||||
void set_content_length_impl(boost::optional<std::uint64_t>) {}
|
||||
void set_keep_alive_impl(unsigned, bool) {}
|
||||
};
|
||||
|
||||
void
|
||||
|
Reference in New Issue
Block a user