mirror of
https://github.com/boostorg/beast.git
synced 2025-08-02 14:24:31 +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
|
* Add vector_body
|
||||||
* span, string, vector bodies are public
|
* span, string, vector bodies are public
|
||||||
* Fix spurious uninitialized warning
|
* 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/BodyReader.qbk]
|
||||||
[include concept/BodyWriter.qbk]
|
[include concept/BodyWriter.qbk]
|
||||||
[include concept/BufferSequence.qbk]
|
[include concept/BufferSequence.qbk]
|
||||||
[include concept/File.qbk]
|
|
||||||
[include concept/DynamicBuffer.qbk]
|
[include concept/DynamicBuffer.qbk]
|
||||||
[include concept/Fields.qbk]
|
[include concept/Fields.qbk]
|
||||||
[include concept/FieldsReader.qbk]
|
[include concept/FieldsReader.qbk]
|
||||||
|
[include concept/File.qbk]
|
||||||
[include concept/Streams.qbk]
|
[include concept/Streams.qbk]
|
||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
@@ -39,6 +39,7 @@ In this table:
|
|||||||
[heading Associated Types]
|
[heading Associated Types]
|
||||||
|
|
||||||
* __Body__
|
* __Body__
|
||||||
|
|
||||||
* [link beast.ref.beast__http__is_body_reader `is_body_reader`]
|
* [link beast.ref.beast__http__is_body_reader `is_body_reader`]
|
||||||
|
|
||||||
[heading BodyReader requirements]
|
[heading BodyReader requirements]
|
||||||
|
@@ -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
|
reason phrase as needed. Types which meet these requirements can always
|
||||||
be serialized.
|
be serialized.
|
||||||
|
|
||||||
|
[heading Associated Types]
|
||||||
|
|
||||||
|
* __FieldsReader__
|
||||||
|
|
||||||
|
* [link beast.ref.beast__http__is_fields `is_fields`]
|
||||||
|
|
||||||
|
[heading Requirements]
|
||||||
|
|
||||||
In this table:
|
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__.
|
* `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`.
|
* `c` denotes a (possibly const) value of type `F`.
|
||||||
|
|
||||||
* `s` is a value of type [link beast.ref.beast__string_view `string_view`].
|
|
||||||
|
|
||||||
* `b` is a value of type `bool`
|
* `b` is a value of type `bool`
|
||||||
|
|
||||||
* `n` is a value of type `boost::optional<std::uint64_t>`.
|
* `n` is a value of type `boost::optional<std::uint64_t>`.
|
||||||
|
|
||||||
[table Fields requirements
|
* `s` is a value of type [link beast.ref.beast__string_view `string_view`].
|
||||||
[[expression][type][semantics, pre/post-conditions]]
|
|
||||||
|
* `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`]
|
[`R`]
|
||||||
[
|
[
|
||||||
A type which meets the requirements of __FieldsReader__.
|
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
|
retrieving the reason text previously set with a call to
|
||||||
`set_reason_impl` using a non-empty string.
|
`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)`]
|
[`a.set_method_impl(s)`]
|
||||||
[]
|
[]
|
||||||
@@ -93,38 +138,81 @@ In this table:
|
|||||||
is not supported by the container.
|
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
|
Adjusts the
|
||||||
account for the payload metadata indicated by `b` and `n` as
|
[@https://tools.ietf.org/html/rfc7230#section-3.3.1 [*Transfer-Encoding]]
|
||||||
follows:
|
field as follows:
|
||||||
|
|
||||||
* If `b` is `true`, the chunked Transfer-Encoding should be applied
|
[itemized_list
|
||||||
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.
|
|
||||||
]
|
|
||||||
][
|
|
||||||
[`is_fields<X>`]
|
|
||||||
[`std::true_type`]
|
|
||||||
[
|
[
|
||||||
An alias for `std::true_type` for `X`, otherwise an alias
|
If `b` is `true`, the "chunked" token is appended
|
||||||
for `std::false_type`.
|
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.
|
||||||
|
]]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
][
|
||||||
|
[`a.set_content_length_impl(n)`]
|
||||||
|
[]
|
||||||
|
[
|
||||||
|
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]
|
[heading Exemplar]
|
||||||
|
|
||||||
[concept_Fields]
|
[concept_Fields]
|
||||||
|
@@ -64,19 +64,6 @@ In this table:
|
|||||||
if the [*Transfer-Encoding] field is present, and the last
|
if the [*Transfer-Encoding] field is present, and the last
|
||||||
token in its value is "chunked".
|
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()`]
|
[`a.get()`]
|
||||||
[`X::const_buffers_type`]
|
[`X::const_buffers_type`]
|
||||||
|
@@ -53,13 +53,17 @@ struct fields_model
|
|||||||
string_view target() const;
|
string_view target() const;
|
||||||
|
|
||||||
protected:
|
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_method_impl() const;
|
||||||
string_view get_target_impl() const;
|
string_view get_target_impl() const;
|
||||||
string_view get_reason_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<>>
|
template<class T, class = beast::detail::void_t<>>
|
||||||
@@ -91,60 +95,90 @@ struct is_fields_helper : T
|
|||||||
{
|
{
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f1(int) -> decltype(
|
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());
|
std::true_type());
|
||||||
static auto f1(...) -> std::false_type;
|
static auto f1(...) -> std::false_type;
|
||||||
using t1 = decltype(f1(0));
|
using t1 = decltype(f1(0));
|
||||||
|
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f2(int) -> decltype(
|
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());
|
std::true_type());
|
||||||
static auto f2(...) -> std::false_type;
|
static auto f2(...) -> std::false_type;
|
||||||
using t2 = decltype(f2(0));
|
using t2 = decltype(f2(0));
|
||||||
|
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f3(int) -> decltype(
|
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());
|
std::true_type());
|
||||||
static auto f3(...) -> std::false_type;
|
static auto f3(...) -> std::false_type;
|
||||||
using t3 = decltype(f3(0));
|
using t3 = decltype(f3(0));
|
||||||
|
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f4(int) -> decltype(
|
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());
|
std::true_type());
|
||||||
static auto f4(...) -> std::false_type;
|
static auto f4(...) -> std::false_type;
|
||||||
using t4 = decltype(f4(0));
|
using t4 = decltype(f4(0));
|
||||||
|
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f5(int) -> decltype(
|
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());
|
std::true_type());
|
||||||
static auto f5(...) -> std::false_type;
|
static auto f5(...) -> std::false_type;
|
||||||
using t5 = decltype(f5(0));
|
using t5 = decltype(f5(0));
|
||||||
|
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f6(int) -> decltype(
|
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());
|
std::true_type());
|
||||||
static auto f6(...) -> std::false_type;
|
static auto f6(...) -> std::false_type;
|
||||||
using t6 = decltype(f6(0));
|
using t6 = decltype(f6(0));
|
||||||
|
|
||||||
template<class U = is_fields_helper>
|
template<class U = is_fields_helper>
|
||||||
static auto f7(int) -> decltype(
|
static auto f7(int) -> decltype(
|
||||||
void(std::declval<U&>().prepare_payload_impl(
|
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
|
||||||
std::declval<bool>(),
|
|
||||||
std::declval<boost::optional<std::uint64_t>>())),
|
|
||||||
std::true_type());
|
std::true_type());
|
||||||
static auto f7(...) -> std::false_type;
|
static auto f7(...) -> std::false_type;
|
||||||
using t7 = decltype(f7(0));
|
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,
|
using type = std::integral_constant<bool,
|
||||||
t1::value && t2::value && t3::value &&
|
t1::value && t2::value && t3::value &&
|
||||||
t4::value && t5::value && t6::value &&
|
t4::value && t5::value && t6::value &&
|
||||||
t7::value
|
t7::value && t8::value && t9::value &&
|
||||||
>;
|
t10::value && t11::value>;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
@@ -567,47 +567,74 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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.
|
/** Returns the request-method string.
|
||||||
|
|
||||||
@note Only called for requests.
|
@note Only called for requests.
|
||||||
*/
|
*/
|
||||||
string_view get_method_impl() const;
|
string_view
|
||||||
|
get_method_impl() const;
|
||||||
|
|
||||||
/** Returns the request-target string.
|
/** Returns the request-target string.
|
||||||
|
|
||||||
@note Only called for requests.
|
@note Only called for requests.
|
||||||
*/
|
*/
|
||||||
string_view get_target_impl() const;
|
string_view
|
||||||
|
get_target_impl() const;
|
||||||
|
|
||||||
/** Returns the response reason-phrase string.
|
/** Returns the response reason-phrase string.
|
||||||
|
|
||||||
@note Only called for responses.
|
@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
|
void
|
||||||
prepare_payload_impl(bool chunked,
|
set_method_impl(string_view s);
|
||||||
boost::optional<std::uint64_t> size);
|
|
||||||
|
/** 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:
|
private:
|
||||||
template<class OtherAlloc>
|
template<class OtherAlloc>
|
||||||
|
@@ -19,6 +19,13 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#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 beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
@@ -133,17 +140,9 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
|
||||||
get_chunked() const;
|
|
||||||
|
|
||||||
bool
|
|
||||||
get_keep_alive(int version) const;
|
|
||||||
|
|
||||||
basic_fields const& f_;
|
basic_fields const& f_;
|
||||||
boost::asio::const_buffer cb_[3];
|
boost::asio::const_buffer cb_[3];
|
||||||
char buf_[13];
|
char buf_[13];
|
||||||
bool chunked_;
|
|
||||||
bool keep_alive_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using const_buffers_type =
|
using const_buffers_type =
|
||||||
@@ -160,18 +159,6 @@ public:
|
|||||||
reader(basic_fields const& f,
|
reader(basic_fields const& f,
|
||||||
unsigned version, unsigned code);
|
unsigned version, unsigned code);
|
||||||
|
|
||||||
bool
|
|
||||||
chunked()
|
|
||||||
{
|
|
||||||
return chunked_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
keep_alive()
|
|
||||||
{
|
|
||||||
return keep_alive_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_buffers_type
|
const_buffers_type
|
||||||
get() const
|
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>
|
template<class Allocator>
|
||||||
basic_fields<Allocator>::reader::
|
basic_fields<Allocator>::reader::
|
||||||
reader(basic_fields const& f,
|
reader(basic_fields const& f,
|
||||||
unsigned version, verb v)
|
unsigned version, verb v)
|
||||||
: f_(f)
|
: f_(f)
|
||||||
, chunked_(get_chunked())
|
|
||||||
, keep_alive_(get_keep_alive(version))
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
request
|
request
|
||||||
@@ -264,8 +214,6 @@ basic_fields<Allocator>::reader::
|
|||||||
reader(basic_fields const& f,
|
reader(basic_fields const& f,
|
||||||
unsigned version, unsigned code)
|
unsigned version, unsigned code)
|
||||||
: f_(f)
|
: f_(f)
|
||||||
, chunked_(get_chunked())
|
|
||||||
, keep_alive_(get_keep_alive(version))
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
response
|
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
|
// 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>
|
template<class Allocator>
|
||||||
inline
|
inline
|
||||||
string_view
|
string_view
|
||||||
@@ -823,73 +879,118 @@ get_reason_impl() const
|
|||||||
return target_or_reason_;
|
return target_or_reason_;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
template<class Allocator>
|
||||||
|
bool
|
||||||
// Builds a new string with "chunked" taken off the end if present
|
basic_fields<Allocator>::
|
||||||
template<class String>
|
get_chunked_impl() const
|
||||||
void
|
|
||||||
without_chunked_last(String& s, string_view const& tokens)
|
|
||||||
{
|
{
|
||||||
token_list te{tokens};
|
auto const te = token_list{
|
||||||
if(te.begin() != te.end())
|
(*this)[field::transfer_encoding]};
|
||||||
|
for(auto it = te.begin(); it != te.end();)
|
||||||
{
|
{
|
||||||
auto it = te.begin();
|
auto const next = std::next(it);
|
||||||
auto next = std::next(it);
|
|
||||||
if(next == te.end())
|
if(next == te.end())
|
||||||
{
|
return iequals(*it, "chunked");
|
||||||
if(! iequals(*it, "chunked"))
|
|
||||||
s.append(it->data(), it->size());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s.append(it->data(), it->size());
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
it = next;
|
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 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>
|
template<class Allocator>
|
||||||
void
|
void
|
||||||
basic_fields<Allocator>::
|
basic_fields<Allocator>::
|
||||||
prepare_payload_impl(bool chunked,
|
set_chunked_impl(bool value)
|
||||||
boost::optional<std::uint64_t> size)
|
|
||||||
{
|
{
|
||||||
if(chunked)
|
|
||||||
{
|
|
||||||
BOOST_ASSERT(! size);
|
|
||||||
erase(field::content_length);
|
|
||||||
auto it = find(field::transfer_encoding);
|
auto it = find(field::transfer_encoding);
|
||||||
|
if(value)
|
||||||
|
{
|
||||||
|
// append "chunked"
|
||||||
if(it == end())
|
if(it == end())
|
||||||
{
|
{
|
||||||
set(field::transfer_encoding, "chunked");
|
set(field::transfer_encoding, "chunked");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
auto const te = token_list{it->value()};
|
||||||
static_string<max_static_buffer> temp;
|
for(auto itt = te.begin();;)
|
||||||
if(it->value().size() <= temp.size() + 9)
|
|
||||||
{
|
{
|
||||||
temp.append(it->value().data(), it->value().size());
|
auto const next = std::next(itt);
|
||||||
temp.append(", chunked", 9);
|
if(next == te.end())
|
||||||
set(field::transfer_encoding, temp);
|
{
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
|
#ifdef BEAST_HTTP_NO_FIELDS_BASIC_STRING_ALLOCATOR
|
||||||
|
// Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437
|
||||||
std::string s;
|
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.reserve(it->value().size() + 9);
|
||||||
s.append(it->value().data(), it->value().size());
|
s.append(it->value().data(), it->value().size());
|
||||||
s.append(", chunked", 9);
|
s.append(", chunked", 9);
|
||||||
@@ -897,44 +998,100 @@ prepare_payload_impl(bool chunked,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto const clear_chunked =
|
// filter "chunked"
|
||||||
[this]()
|
|
||||||
{
|
|
||||||
auto it = find(field::transfer_encoding);
|
|
||||||
if(it == end())
|
if(it == end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// We have to just try it because we can't
|
|
||||||
// know ahead of time if there's enough room.
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
static_string<max_static_buffer> temp;
|
static_string<max_static_buffer> buf;
|
||||||
detail::without_chunked_last(temp, it->value());
|
detail::filter_token_list_last(buf, it->value(),
|
||||||
if(! temp.empty())
|
[](string_view const& s)
|
||||||
set(field::transfer_encoding, temp);
|
{
|
||||||
|
return iequals(s, "chunked");
|
||||||
|
});
|
||||||
|
if(! buf.empty())
|
||||||
|
set(field::transfer_encoding, buf);
|
||||||
else
|
else
|
||||||
erase(field::transfer_encoding);
|
erase(field::transfer_encoding);
|
||||||
}
|
}
|
||||||
catch(std::length_error const&)
|
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;
|
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());
|
s.reserve(it->value().size());
|
||||||
detail::without_chunked_last(s, it->value());
|
detail::filter_token_list_last(s, it->value(),
|
||||||
|
[](string_view const& s)
|
||||||
|
{
|
||||||
|
return iequals(s, "chunked");
|
||||||
|
});
|
||||||
if(! s.empty())
|
if(! s.empty())
|
||||||
set(field::transfer_encoding, s);
|
set(field::transfer_encoding, s);
|
||||||
else
|
else
|
||||||
erase(field::transfer_encoding);
|
erase(field::transfer_encoding);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
if(size)
|
|
||||||
{
|
|
||||||
clear_chunked();
|
|
||||||
set(field::content_length, *size);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_fields<Allocator>::
|
||||||
|
set_content_length_impl(
|
||||||
|
boost::optional<std::uint64_t> const& value)
|
||||||
{
|
{
|
||||||
clear_chunked();
|
if(! value)
|
||||||
erase(field::content_length);
|
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<bool isRequest, class Body, class Fields>
|
||||||
template<class BodyArg, class>
|
void
|
||||||
message<isRequest, Body, Fields>::
|
message<isRequest, Body, Fields>::
|
||||||
message(BodyArg&& body_arg)
|
chunked(bool value)
|
||||||
: body(std::forward<BodyArg>(body_arg))
|
|
||||||
{
|
{
|
||||||
|
this->set_chunked_impl(value);
|
||||||
|
this->set_content_length_impl(boost::none);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
template<bool isRequest, class Body, class Fields>
|
||||||
template<class BodyArg, class HeaderArg, class>
|
void
|
||||||
message<isRequest, Body, Fields>::
|
message<isRequest, Body, Fields>::
|
||||||
message(BodyArg&& body_arg, HeaderArg&& header_arg)
|
content_length(
|
||||||
: header_type(std::forward<HeaderArg>(header_arg))
|
boost::optional<std::uint64_t> const& value)
|
||||||
, body(std::forward<BodyArg>(body_arg))
|
|
||||||
{
|
{
|
||||||
|
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>
|
template<bool isRequest, class Body, class Fields>
|
||||||
boost::optional<std::uint64_t>
|
boost::optional<std::uint64_t>
|
||||||
message<isRequest, Body, Fields>::
|
message<isRequest, Body, Fields>::
|
||||||
@@ -371,20 +356,20 @@ prepare_payload(std::true_type)
|
|||||||
this->method() == verb::put ||
|
this->method() == verb::put ||
|
||||||
this->method() == verb::post)
|
this->method() == verb::post)
|
||||||
{
|
{
|
||||||
this->prepare_payload_impl(false, *n);
|
this->content_length(n);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->prepare_payload_impl(false, boost::none);
|
this->chunked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(this->version >= 11)
|
else if(this->version >= 11)
|
||||||
{
|
{
|
||||||
this->prepare_payload_impl(true, boost::none);
|
this->chunked(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->prepare_payload_impl(false, boost::none);
|
this->chunked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,13 +389,9 @@ prepare_payload(std::false_type)
|
|||||||
"invalid response body"});
|
"invalid response body"});
|
||||||
}
|
}
|
||||||
if(n)
|
if(n)
|
||||||
{
|
this->content_length(n);
|
||||||
this->prepare_payload_impl(false, *n);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
this->chunked(true);
|
||||||
this->prepare_payload_impl(true, boost::none);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@@ -88,8 +88,8 @@ next(error_code& ec, Visit&& visit)
|
|||||||
{
|
{
|
||||||
frdinit(std::integral_constant<bool,
|
frdinit(std::integral_constant<bool,
|
||||||
isRequest>{});
|
isRequest>{});
|
||||||
keep_alive_ = frd_->keep_alive();
|
keep_alive_ = m_.keep_alive();
|
||||||
chunked_ = frd_->chunked();
|
chunked_ = m_.chunked();
|
||||||
if(chunked_)
|
if(chunked_)
|
||||||
goto go_init_c;
|
goto go_init_c;
|
||||||
s_ = do_init;
|
s_ = do_init;
|
||||||
|
@@ -642,6 +642,72 @@ struct message : header<isRequest, Fields>
|
|||||||
return *this;
|
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.
|
/** Returns the payload size of the body in octets if possible.
|
||||||
|
|
||||||
This function invokes the @b Body algorithm to measure
|
This function invokes the @b Body algorithm to measure
|
||||||
|
@@ -190,52 +190,76 @@ public:
|
|||||||
struct reader;
|
struct reader;
|
||||||
|
|
||||||
protected:
|
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.
|
/** Returns the request-method string.
|
||||||
|
|
||||||
@note Only called for requests.
|
@note Only called for requests.
|
||||||
*/
|
*/
|
||||||
string_view get_method_impl() const;
|
string_view
|
||||||
|
get_method_impl() const;
|
||||||
|
|
||||||
/** Returns the request-target string.
|
/** Returns the request-target string.
|
||||||
|
|
||||||
@note Only called for requests.
|
@note Only called for requests.
|
||||||
*/
|
*/
|
||||||
string_view get_target_impl() const;
|
string_view
|
||||||
|
get_target_impl() const;
|
||||||
|
|
||||||
/** Returns the response reason-phrase string.
|
/** Returns the response reason-phrase string.
|
||||||
|
|
||||||
@note Only called for responses.
|
@note Only called for responses.
|
||||||
*/
|
*/
|
||||||
string_view get_reason_impl() const;
|
string_view
|
||||||
|
get_reason_impl() const;
|
||||||
|
|
||||||
/** Updates the payload metadata.
|
/** Returns the chunked Transfer-Encoding setting
|
||||||
|
|
||||||
@param b `true` if chunked
|
|
||||||
|
|
||||||
@param n The content length if known, otherwise `boost::none`
|
|
||||||
*/
|
*/
|
||||||
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");
|
||||||
|
|
||||||
//]
|
//]
|
||||||
|
|
||||||
|
@@ -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();
|
testMembers();
|
||||||
testHeaders();
|
testHeaders();
|
||||||
@@ -592,6 +908,10 @@ public:
|
|||||||
testErase();
|
testErase();
|
||||||
testContainer();
|
testContainer();
|
||||||
testPreparePayload();
|
testPreparePayload();
|
||||||
|
|
||||||
|
testKeepAlive();
|
||||||
|
testContentLength();
|
||||||
|
testChunked();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -191,13 +191,17 @@ public:
|
|||||||
|
|
||||||
test_fields() = delete;
|
test_fields() = delete;
|
||||||
test_fields(token) {}
|
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_method_impl() const { return {}; }
|
||||||
string_view get_target_impl() const { return target; }
|
string_view get_target_impl() const { return target; }
|
||||||
string_view get_reason_impl() const { return {}; }
|
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
|
void
|
||||||
|
Reference in New Issue
Block a user