Add message keep_alive, chunked, content_length members

This commit is contained in:
Vinnie Falco committed 2017-07-20 08:15:30 -07:00
1 parent aa473a6da4
commit 6a46e48424
15 files changed
+1025 -325

No files matched your search

+51 -17
View File
@@ -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
+51 -24
View File
@@ -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>
+324 -167
View File
@@ -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);
}
}
+15 -34
View File
@@ -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);
}
//------------------------------------------------------------------------------
+2 -2
View File
@@ -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;
+66
View File
@@ -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