Fields concept work

This commit is contained in:
Vinnie Falco
2017-06-06 17:26:11 -07:00
parent f363654300
commit 943830f6ed
20 changed files with 675 additions and 343 deletions

View File

@@ -7,6 +7,7 @@ Version 50
* Use field in basic_fields and call sites
* Use field in basic_parser
* Tidy up basic_fields, header, and field concepts
* Fields concept work
API Changes:

View File

@@ -52,7 +52,8 @@
[def __BodyReader__ [link beast.concept.BodyReader [*BodyReader]]]
[def __BodyWriter__ [link beast.concept.BodyWriter [*BodyWriter]]]
[def __DynamicBuffer__ [link beast.concept.DynamicBuffer [*DynamicBuffer]]]
[def __FieldSequence__ [link beast.concept.FieldSequence [*FieldSequence]]]
[def __Fields__ [link beast.concept.Fields [*Fields]]]
[def __FieldsReader__ [link beast.concept.FieldsReader [*FieldsReader]]]
[def __Stream__ [link beast.concept.streams [*Stream]]]
[def __SyncStream__ [link beast.concept.streams.SyncStream [*SyncStream]]]

View File

@@ -15,7 +15,8 @@
[include concept/BufferSequence.qbk]
[include concept/DynamicBuffer.qbk]
[include concept/Field.qbk]
[include concept/FieldSequence.qbk]
[include concept/Fields.qbk]
[include concept/FieldsReader.qbk]
[include concept/Streams.qbk]
[endsect]

View File

@@ -19,7 +19,7 @@ In this table:
[[expression][type][semantics, pre/post-conditions]]
[
[`a.name()`]
[`boost::string_ref`]
[[link beast.ref.string_view `string_view`]]
[
This function returns a value implicitly convertible to
`boost::string_ref` containing the case-insensitive field
@@ -28,7 +28,7 @@ In this table:
]
[
[`a.value()`]
[`boost::string_ref`]
[[link beast.ref.string_view `string_view`]]
[
This function returns a value implicitly convertible to
`boost::string_ref` containing the value for the field. The

View File

@@ -1,133 +0,0 @@
[/
Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:FieldSequence FieldSequence]
A [*FieldSequence] is an iterable container whose value type meets
the requirements of [link beast.concept.Field [*Field]]. Objects that meet
these requirements become serializable by the implementation.
In this table:
* `X` denotes a type that meets the requirements of [*FieldSequence].
* `c` is a value of type `X const`.
[table FieldSequence requirements
[[expression][type][semantics, pre/post-conditions]]
[
[`X::value_type`]
[]
[
A type that meets the requirements of
[link beast.concept.Field [*Field]].
]
]
[
[`X::const_iterator`]
[]
[
An iterator type whose `reference` type meets the
requirements of [link beast.concept.Field [*Field]], and which
satisfies all the requirements of [*ForwardIterator],
except that:
[ordered_list
[there is no requirement that `operator->` is provided, and]
[there is no requirement that `reference` be a reference type.]
]
]
]
[
[`c.begin()`]
[`X::const_iterator`]
[
Returns an iterator to the beginning of the field sequence.
]
]
[
[`c.end()`]
[`X::const_iterator`]
[
Returns an iterator to the end of the field sequence.
]
]
]
[endsect]
[/
/** OutputFields
Can be serialized by `beast::http::serializer`
*/
public:
// This algorithm produces a set of buffers
// corresponding to the serialized representation
// of the fields
//
struct reader
{
using const_buffers_type = implementation-defined
...
};
protected:
string_view method_impl() const;
string_view target_impl() const;
string_view reason_impl() const;
bool close_impl () const;
bool chunked_impl () const;
bool content_length_impl () const;
/** InputFields
Can be parsed by `beast::http::parser`
*/
public:
void insert (field f, string_view value);
void insert (string_view name, string_view value);
protected:
void method_impl (string_view s);
void target_impl (string_view s);
void reason_impl (string_view s);
/** Fields
Has a container interface and supports input and output.
`beast::http:basic_fields` is a model.
*/
public:
bool exists(field f) const;
bool exists(string_view s) const;
iterator find(field f) const;
iterator find(string_view s);
std::size_t erase (field f);
std::size_t erase (string_view s);
iterator erase (iterator);
protected:
void content_length_impl(std::uint64_t n);
void connection_impl(close_t);
void connection_impl(keep_alive_t);
void connection_impl(upgrade_t);
void chunked_impl();
]

112
doc/concept/Fields.qbk Normal file
View File

@@ -0,0 +1,112 @@
[/
Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:Fields Fields]
An instance of [*Fields] is a container for holding HTTP header fields
and their values. The implementation also calls upon the container to
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.
In this table:
* `X` denotes a type that meets the requirements of [*Fields].
* `R` denotes a type meeting the requiremnets of __FieldsReader__.
* `a` denotes a value of type `X`.
* `c` denotes a (possibly const) value of type `X`.
* `s` is a value of type [link beast.ref.string_view `string_view`].
[table Fields requirements
[[expression][type][semantics, pre/post-conditions]]
[
[`X::reader`]
[`R`]
[
A type which meets the requirements of __FieldsReader__.
]
][
[`c.has_close_impl()`]
[`bool`]
[
Returns `true` if the value for Connection has "close" in the list.
]
][
[`c.has_chunked_impl()`]
[`bool`]
[
Returns `true` if "chunked" is the last Transfer-Encoding.
]
][
[`c.has_content_length_impl()`]
[`bool`]
[
Returns `true` if the Content-Length field is present.
]
][
[`c.get_method_impl()`]
[`string_view`]
[
Returns the method text.
The implementation only calls this function for request
headers when retrieving the method text previously set
with a call to `set_method_impl` using a non-empty string.
]
][
[`c.get_target_impl()`]
[`string_view`]
[
Returns the target string.
The implementation only calls this function for request headers.
]
][
[`c.get_reason_impl()`]
[`string_view`]
[
Returns the obsolete request text.
The implementation only calls this for response headers when
retrieving the reason text previously set with a call to
`set_reason_impl` using a non-empty string.
]
][
[`a.set_method_impl(s)`]
[]
[
Stores a copy of `s` as the method text, or erases the previously
stored value if `s` is empty.
The implementation only calls this function for request headers.
This function may throw `std::invalid_argument` if the operation
is not supported by the container.
]
][
[`a.set_target_impl(s)`]
[]
[
Stores a copy of `s` as the target, or erases the previously
stored value if `s` is empty.
The implementation only calls this function for request headers.
This function may throw `std::invalid_argument` if the operation
is not supported by the container.
]
][
[`a.set_reason_impl(s)`]
[]
[
Stores a copy of `s` as the reason text, or erases the previously
stored value of the reason text if `s` is empty.
The implementation only calls this function for request headers.
This function may throw `std::invalid_argument` if the operation
is not supported by the container.
]
]
]
[endsect]

View File

@@ -0,0 +1,78 @@
[/
Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:FieldsReader FieldsReader]
A [*FieldsReader] provides a algorithm to obtain a sequence of buffers
representing the complete serialized HTTP/1 header for a set of fields.
The implementation constructs an instance of this type when needed, and
calls into it once to retrieve the buffers.
In this table:
* `X` denotes a type that meets the requirements of [*FieldsReader].
* `F` denotes a __Fields__ where
`std::is_same<X, F::reader>::value == `true`.
* `a` is a value of type `X`.
* `f` is a value of type `F`.
* `v` is an integer representing the HTTP version.
* `c` is an integer representing the HTTP status-code.
* `m` is a value of type [link beast.ref.http__verb `verb`].
[table FieldsReader requirements
[[expression][type][semantics, pre/post-conditions]]
[
[`X::const_buffers_type`]
[]
[
A type which meets the requirements of __ConstBufferSequence__.
This is the type of buffer returned by `X::get`.
]
][
[`X(f,v,m)`]
[]
[
The implementatation uses this constructor to indicate
that the fields being serialized form part of an HTTP
request. The lifetime of `f` is guaranteed
to end no earlier than after the `X` is destroyed.
]
][
[`X(f,v,c)`]
[]
[
The implementatation uses this constructor to indicate
that the fields being serialized form part of an HTTP
response. The lifetime of `f` is guaranteed
to end no earlier than after the `X` is destroyed.
]
][
[`a.get()`]
[X::const_buffers_type]
[
Called once after construction, this function returns
a constant buffer sequence containing the serialized
representation of the HTTP request or response including
the final carriage return linefeed sequence (`"\r\n"`).
]
][
[`is_fields_reader<F>`]
[`std::true_type`]
[
An alias for `std::true_type` for `F`, otherwise an alias
for `std::false_type`.
]
]
]
[endsect]

View File

@@ -103,7 +103,8 @@
<member><link linkend="beast.concept.BodyReader">BodyReader</link></member>
<member><link linkend="beast.concept.BodyWriter">BodyWriter</link></member>
<member><link linkend="beast.concept.Field">Field</link></member>
<member><link linkend="beast.concept.FieldSequence">FieldSequence</link></member>
<member><link linkend="beast.concept.Fields">Fields</link></member>
<member><link linkend="beast.concept.FieldsReader">FieldsReader</link></member>
</simplelist>
</entry>
<entry valign="top">

View File

@@ -21,6 +21,12 @@ namespace beast {
template<class... Buffers>
class buffer_cat_view
{
#if 0
static_assert(
detail::is_all_const_buffer_sequence<Buffers...>::value,
"BufferSequence requirements not met");
#endif
std::tuple<Buffers...> bn_;
public:

View File

@@ -24,6 +24,12 @@ namespace beast {
template<class... Bn>
class buffer_cat_view<Bn...>::const_iterator
{
#if 0
static_assert(
detail::is_all_const_buffer_sequence<Bn...>::value,
"BufferSequence requirements not met");
#endif
std::size_t n_;
std::tuple<Bn...> const* bn_;
std::array<char, detail::max_sizeof<

View File

@@ -13,6 +13,7 @@
#include <beast/core/detail/ci_char_traits.hpp>
#include <beast/http/connection.hpp>
#include <beast/http/field.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/intrusive/list.hpp>
#include <boost/intrusive/set.hpp>
@@ -40,7 +41,7 @@ namespace http {
as a `std::multiset`; there will be a separate value for each occurrence
of the field name.
@note Meets the requirements of @b FieldSequence.
@note Meets the requirements of @b Fields
*/
template<class Allocator>
class basic_fields
@@ -259,8 +260,6 @@ public:
@param f The known field constant.
@param name The name of the field.
@param value A string holding the value of the field.
*/
void
@@ -337,54 +336,55 @@ public:
void
swap(basic_fields<Alloc>& lhs, basic_fields<Alloc>& rhs);
/// The algorithm used to serialize the header
class reader;
protected:
/// Returns `true` if the value for Connection has "close" in the list.
bool has_close_impl() const;
//--------------------------------------------------------------------------
//
// for serializing
//
/// Returns `true` if "chunked" is the last Transfer-Encoding
bool has_chunked_impl() const;
/** Returns the stored request-method string.
/// Returns `true` if the Content-Length field is present
bool has_content_length_impl() const;
@note This is called by the @ref header implementation.
*/
string_view get_method_impl() const;
/** Set or clear the method string.
/** Returns the stored request-target string.
@note This is called by the @ref header implementation.
*/
string_view get_target_impl() const;
/** Returns the stored obsolete reason-phrase string.
@note This is called by the @ref header implementation.
*/
string_view get_reason_impl() const;
//--------------------------------------------------------------------------
//
// for parsing
//
/** Set or clear the stored request-method string.
@note This is called by the @ref header implementation.
@note Only called for requests.
*/
void set_method_impl(string_view s);
/** Set or clear the stored request-target string.
/** Set or clear the target string.
@note This is called by the @ref header implementation.
@note Only called for requests.
*/
void set_target_impl(string_view s);
/** Set or clear the stored obsolete reason-phrase string.
/** Set or clear the reason string.
@note This is called by the @ref header implementation.
@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;
/** Returns the request-target string.
@note Only called for requests.
*/
string_view get_target_impl() const;
/** Returns the response reason-phrase string.
@note Only called for responses.
*/
string_view get_reason_impl() const;
//--------------------------------------------------------------------------
//
// for container
@@ -418,7 +418,7 @@ protected:
@note This is called by the @ref header implementation.
*/
void chunked_impl();
void set_chunked_impl(bool v);
private:
class element
@@ -441,6 +441,9 @@ private:
string_view
value() const;
boost::asio::const_buffer
buffer() const;
value_type data;
};

View File

@@ -8,8 +8,13 @@
#ifndef BEAST_HTTP_IMPL_FIELDS_IPP
#define BEAST_HTTP_IMPL_FIELDS_IPP
#include <beast/core/buffer_cat.hpp>
#include <beast/core/static_string.hpp>
#include <beast/http/detail/rfc7230.hpp>
#include <beast/core/detail/ci_char_traits.hpp>
#include <beast/http/verb.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/http/status.hpp>
#include <beast/http/detail/chunk_encode.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
@@ -106,6 +111,177 @@ public:
//------------------------------------------------------------------------------
template<class Allocator>
class basic_fields<Allocator>::reader
{
public:
using iter_type = typename list_t::const_iterator;
struct field_iterator
{
iter_type it_;
using value_type = boost::asio::const_buffer;
using pointer = value_type const*;
using reference = value_type const;
using difference_type = std::ptrdiff_t;
using iterator_category =
std::bidirectional_iterator_tag;
field_iterator() = default;
field_iterator(field_iterator&& other) = default;
field_iterator(field_iterator const& other) = default;
field_iterator& operator=(field_iterator&& other) = default;
field_iterator& operator=(field_iterator const& other) = default;
explicit
field_iterator(iter_type it)
: it_(it)
{
}
bool
operator==(field_iterator const& other) const
{
return it_ == other.it_;
}
bool
operator!=(field_iterator const& other) const
{
return !(*this == other);
}
reference
operator*() const
{
return it_->buffer();
}
field_iterator&
operator++()
{
++it_;
return *this;
}
field_iterator
operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
field_iterator&
operator--()
{
--it_;
return *this;
}
field_iterator
operator--(int)
{
auto temp = *this;
--(*this);
return temp;
}
};
class field_range
{
field_iterator first_;
field_iterator last_;
public:
using const_iterator =
field_iterator;
using value_type =
typename const_iterator::value_type;
field_range(field_range const&) = default;
field_range(iter_type first, iter_type last)
: first_(first)
, last_(last)
{
}
const_iterator
begin() const
{
return first_;
}
const_iterator
end() const
{
return last_;
}
};
basic_fields const& f_;
std::string s_;
public:
using const_buffers_type =
buffer_cat_view<
boost::asio::const_buffers_1,
field_range,
boost::asio::const_buffers_1>;
reader(basic_fields const& f, int version, verb v)
: f_(f)
{
s_ = v == verb::unknown ?
f_.get_method_impl().to_string() :
to_string(v).to_string();
s_ += " ";
s_ += f_.get_target_impl().to_string();
if(version == 11)
s_ += " HTTP/1.1";
else if(version == 10)
s_ += " HTTP/1.0";
else
s_ += " HTTP/" +
std::to_string(version / 10) + "." +
std::to_string(version % 10);
s_ += "\r\n";
}
reader(basic_fields const& f, int version, int code)
: f_(f)
{
if(version == 11)
s_ += "HTTP/1.1 ";
else if(version == 10)
s_ += "HTTP/1.0 ";
else
s_ += "HTTP/" +
std::to_string(version / 10) + "." +
std::to_string(version % 10) + " ";
s_ += std::to_string(code) + " ";
if(int_to_status(code) == status::unknown)
s_ += f_.get_reason_impl().to_string();
else
s_ += obsolete_reason(int_to_status(code)).to_string();
s_ += "\r\n";
}
const_buffers_type
get() const
{
return buffer_cat(
boost::asio::buffer(s_.data(), s_.size()),
field_range(f_.list_.begin(), f_.list_.end()),
detail::chunk_crlf());
}
};
//------------------------------------------------------------------------------
template<class Allocator>
basic_fields<Allocator>::
element::
@@ -150,6 +326,18 @@ value() const
static_cast<std::size_t>(len_)};
}
template<class Allocator>
inline
boost::asio::const_buffer
basic_fields<Allocator>::
element::
buffer() const
{
return boost::asio::const_buffer{
reinterpret_cast<char const*>(this + 1),
static_cast<std::size_t>(off_) + len_ + 2};
}
//------------------------------------------------------------------------------
template<class Allocator>
@@ -361,31 +549,50 @@ replace(string_view name, string_view value)
//------------------------------------------------------------------------------
// Fields
template<class Allocator>
inline
string_view
bool
basic_fields<Allocator>::
get_method_impl() const
has_close_impl() const
{
return method_;
auto const fit = set_.find(
to_string(field::connection), less{});
if(fit == set_.end())
return false;
return token_list{fit->value()}.exists("close");
}
template<class Allocator>
inline
string_view
bool
basic_fields<Allocator>::
get_target_impl() const
has_chunked_impl() const
{
return target_or_reason_;
auto const fit = set_.find(to_string(
field::transfer_encoding), less{});
if(fit == set_.end())
return false;
token_list const v{fit->value()};
auto it = v.begin();
if(it == v.end())
return false;
for(;;)
{
auto cur = it++;
if(it == v.end())
return beast::detail::ci_equal(
*cur, "chunked");
}
}
template<class Allocator>
inline
string_view
bool
basic_fields<Allocator>::
get_reason_impl() const
has_content_length_impl() const
{
return target_or_reason_;
auto const fit = set_.find(
to_string(field::content_length), less{});
return fit != set_.end();
}
template<class Allocator>
@@ -415,6 +622,33 @@ set_reason_impl(string_view s)
realloc_string(target_or_reason_, s);
}
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
get_method_impl() const
{
return method_;
}
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
get_target_impl() const
{
return target_or_reason_;
}
template<class Allocator>
inline
string_view
basic_fields<Allocator>::
get_reason_impl() const
{
return target_or_reason_;
}
//---
template<class Allocator>
@@ -474,8 +708,9 @@ template<class Allocator>
inline
void
basic_fields<Allocator>::
chunked_impl()
set_chunked_impl(bool v)
{
BOOST_ASSERT(v);
auto it = find("Transfer-Encoding");
if(it == end())
this->insert("Transfer-Encoding", "chunked");
@@ -682,7 +917,6 @@ swap(basic_fields& other, std::false_type)
swap(target_or_reason_, other.target_or_reason_);
}
} // http
} // beast

View File

@@ -61,7 +61,7 @@ method_string() const
template<class Fields>
void
header<true, Fields>::
method(string_view s)
method_string(string_view s)
{
method_ = string_to_verb(s);
if(method_ != verb::unknown)
@@ -190,21 +190,85 @@ swap(
//------------------------------------------------------------------------------
template<bool isRequest, class Body, class Fields>
template<class... Args>
message<isRequest, Body, Fields>::
message(header_type&& base, Args&&... args)
: header_type(std::move(base))
, body(std::forward<Args>(args)...)
{
}
template<bool isRequest, class Body, class Fields>
template<class... Args>
message<isRequest, Body, Fields>::
message(header_type const& base, Args&&... args)
: header_type(base)
, body(std::forward<Args>(args)...)
{
}
template<bool isRequest, class Body, class Fields>
template<class U, class>
message<isRequest, Body, Fields>::
message(U&& u)
: body(std::forward<U>(u))
{
}
template<bool isRequest, class Body, class Fields>
template<class U, class V, class>
message<isRequest, Body, Fields>::
message(U&& u, V&& v)
: header_type(std::forward<V>(v))
, body(std::forward<U>(u))
{
}
template<bool isRequest, class Body, class Fields>
template<class... Un>
message<isRequest, Body, Fields>::
message(std::piecewise_construct_t, std::tuple<Un...> un)
: message(std::piecewise_construct, un,
beast::detail::make_index_sequence<sizeof...(Un)>{})
{
}
template<bool isRequest, class Body, class Fields>
template<class... Un, class... Vn>
message<isRequest, Body, Fields>::
message(std::piecewise_construct_t,
std::tuple<Un...>&& un, std::tuple<Vn...>&& vn)
: message(std::piecewise_construct, un, vn,
beast::detail::make_index_sequence<sizeof...(Un)>{},
beast::detail::make_index_sequence<sizeof...(Vn)>{})
{
}
template<bool isRequest, class Body, class Fields>
inline
bool
message<isRequest, Body, Fields>::
chunked() const
has_close() const
{
auto const it0 = this->find("Transfer-Encoding");
if(it0 == this->end())
return false;
token_list value{*it0};
for(auto it = value.begin(); it != value.end();)
{
auto cur = it++;
if(it == value.end())
return *cur == "chunked";
return this->has_close_impl();
}
return false;
template<bool isRequest, class Body, class Fields>
inline
bool
message<isRequest, Body, Fields>::
has_chunked() const
{
return this->has_chunked_impl();
}
template<bool isRequest, class Body, class Fields>
inline
bool
message<isRequest, Body, Fields>::
has_content_length() const
{
return this->has_content_length_impl();
}
template<bool isRequest, class Body, class Fields>
@@ -326,7 +390,7 @@ prepare_payload(std::true_type)
}
else if(this->version >= 11)
{
this->chunked_impl();
this->set_chunked_impl(true);
}
}
@@ -350,7 +414,7 @@ prepare_payload(std::false_type)
if(n)
this->content_length_impl(*n);
else if(this->version >= 11)
this->chunked_impl();
this->set_chunked_impl(true);
}
//------------------------------------------------------------------------------

View File

@@ -15,58 +15,26 @@
namespace beast {
namespace http {
namespace detail {
template<class Fields>
template<bool isRequest, class Body, class Fields,
class ChunkDecorator, class Allocator>
void
write_start_line(std::ostream& os,
header<true, Fields> const& msg)
serializer<isRequest, Body, Fields,
ChunkDecorator, Allocator>::
frdinit(std::true_type)
{
// VFALCO This should all be done without dynamic allocation
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
os << msg.method() << " " << msg.target();
switch(msg.version)
{
case 10: os << " HTTP/1.0\r\n"; break;
case 11: os << " HTTP/1.1\r\n"; break;
}
frd_.emplace(m_, m_.version, m_.method());
}
template<class Fields>
template<bool isRequest, class Body, class Fields,
class ChunkDecorator, class Allocator>
void
write_start_line(std::ostream& os,
header<false, Fields> const& msg)
serializer<isRequest, Body, Fields,
ChunkDecorator, Allocator>::
frdinit(std::false_type)
{
// VFALCO This should all be done without dynamic allocation
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
switch(msg.version)
{
case 10: os << "HTTP/1.0 "; break;
case 11: os << "HTTP/1.1 "; break;
frd_.emplace(m_, m_.version, m_.result_int());
}
os << msg.result_int() << " " << msg.reason() << "\r\n";
}
template<class FieldSequence>
void
write_fields(std::ostream& os,
FieldSequence const& fields)
{
//static_assert(is_FieldSequence<FieldSequence>::value,
// "FieldSequence requirements not met");
for(auto const& field : fields)
{
auto const name = field.name();
BOOST_ASSERT(! name.empty());
if(name[0] == ':')
continue;
os << field.name() << ": " << field.value() << "\r\n";
}
}
} // detail
//------------------------------------------------------------------------------
template<bool isRequest, class Body, class Fields,
class ChunkDecorator, class Allocator>
@@ -76,7 +44,6 @@ serializer(message<isRequest, Body, Fields> const& m,
ChunkDecorator const& d, Allocator const& alloc)
: m_(m)
, d_(d)
, b_(1024, alloc)
{
}
@@ -93,15 +60,11 @@ get(error_code& ec, Visit&& visit)
{
case do_construct:
{
chunked_ = token_list{
m_["Transfer-Encoding"]}.exists("chunked");
close_ = token_list{m_["Connection"]}.exists("close") ||
(m_.version < 11 && ! m_.exists("Content-Length"));
auto os = ostream(b_);
detail::write_start_line(os, m_);
detail::write_fields(os, m_);
os << "\r\n";
if(chunked_)
frdinit(std::integral_constant<bool,
isRequest>{});
close_ = m_.has_close() || (
m_.version < 11 && ! m_.has_content_length());
if(m_.has_chunked())
goto go_init_c;
s_ = do_init;
// [[fallthrough]]
@@ -127,7 +90,7 @@ get(error_code& ec, Visit&& visit)
more_ = result->second;
v_ = cb0_t{
boost::in_place_init,
b_.data(),
frd_->get(),
result->first};
s_ = do_header;
// [[fallthrough]]
@@ -138,9 +101,10 @@ get(error_code& ec, Visit&& visit)
break;
go_header_only:
v_ = ch_t{frd_->get()};
s_ = do_header_only;
case do_header_only:
visit(ec, b_.data());
visit(ec, boost::get<ch_t>(v_));
break;
case do_body:
@@ -193,7 +157,7 @@ get(error_code& ec, Visit&& visit)
more_ = result->second;
v_ = ch0_t{
boost::in_place_init,
b_.data(),
frd_->get(),
detail::chunk_header{
buffer_size(result->first)},
[&]()
@@ -215,9 +179,10 @@ get(error_code& ec, Visit&& visit)
break;
go_header_only_c:
v_ = ch_t{frd_->get()};
s_ = do_header_only_c;
case do_header_only_c:
visit(ec, b_.data());
visit(ec, boost::get<ch_t>(v_));
break;
case do_body_c:
@@ -311,18 +276,18 @@ consume(std::size_t n)
break;
header_done_ = true;
v_ = boost::blank{};
b_.consume(b_.size()); // VFALCO delete b_?
if(! more_)
goto go_complete;
s_ = do_body + 1;
break;
case do_header_only:
BOOST_ASSERT(n <= b_.size());
b_.consume(n);
if(buffer_size(b_.data()) > 0)
BOOST_ASSERT(n <= buffer_size(
boost::get<ch_t>(v_)));
boost::get<ch_t>(v_).consume(n);
if(buffer_size(boost::get<ch_t>(v_)) > 0)
break;
// VFALCO delete b_?
frd_ = boost::none;
header_done_ = true;
if(! split_)
goto go_complete;
@@ -353,7 +318,6 @@ consume(std::size_t n)
break;
header_done_ = true;
v_ = boost::blank{};
b_.consume(b_.size()); // VFALCO delete b_?
if(more_)
s_ = do_body_c + 1;
else
@@ -362,11 +326,12 @@ consume(std::size_t n)
case do_header_only_c:
{
BOOST_ASSERT(n <= buffer_size(b_.data()));
b_.consume(n);
if(buffer_size(b_.data()) > 0)
BOOST_ASSERT(n <= buffer_size(
boost::get<ch_t>(v_)));
boost::get<ch_t>(v_).consume(n);
if(buffer_size(boost::get<ch_t>(v_)) > 0)
break;
// VFALCO delete b_?
frd_ = boost::none;
header_done_ = true;
if(! split_)
{

View File

@@ -822,16 +822,15 @@ public:
} // detail
#if 0
template<bool isRequest, class Fields>
std::ostream&
operator<<(std::ostream& os,
header<isRequest, Fields> const& msg)
{
detail::write_start_line(os, msg);
detail::write_fields(os, msg);
os << "\r\n";
return os;
// VFALCO TODO
}
#endif
template<bool isRequest, class Body, class Fields>
std::ostream&

View File

@@ -119,7 +119,7 @@ struct header<true, Fields> : Fields
verb
method() const;
/** Set the request-method verb.
/** Set the request-method.
This function will set the method for requests to a known verb.
@@ -127,11 +127,13 @@ struct header<true, Fields> : Fields
This may not be @ref verb::unknown.
@throw std::invalid_argument when `v == verb::unknown`.
@note This function is only available when `isRequest == true`.
*/
void
method(verb v);
/** Return the request-method string.
/** Return the request-method as a string.
@note This function is only available when `isRequest == true`.
@@ -140,18 +142,18 @@ struct header<true, Fields> : Fields
string_view
method_string() const;
/** Set the request-method string.
/** Set the request-method.
This function will set the method for requests to a verb
if the string matches a known verb, otherwise it will
store a copy of the passed string as the method.
This function will set the request-method a known verb
if the string matches, otherwise it will store a copy of
the passed string.
@param s A string representing the request-method.
@note This function is only available when `isRequest == true`.
*/
void
method(string_view s);
method_string(string_view s);
/** Returns the request-target string.
@@ -382,31 +384,27 @@ struct message : header<isRequest, Fields>
/// Copy assignment
message& operator=(message const&) = default;
/** Construct a message from a header.
/** Constructor.
Additional arguments, if any, are forwarded to
the constructor of the body member.
@param h The header to move construct from.
@param args Optional arguments forwarded
to the body constructor.
*/
template<class... Args>
explicit
message(header_type&& base, Args&&... args)
: header_type(std::move(base))
, body(std::forward<Args>(args)...)
{
}
message(header_type&& h, Args&&... args);
/** Construct a message from a header.
/** Constructor.
Additional arguments, if any, are forwarded to
the constructor of the body member.
@param h The header to copy construct from.
@param args Optional arguments forwarded
to the body constructor.
*/
template<class... Args>
explicit
message(header_type const& base, Args&&... args)
: header_type(base)
, body(std::forward<Args>(args)...)
{
}
message(header_type const& h, Args&&... args);
/** Construct a message.
@@ -423,10 +421,7 @@ struct message : header<isRequest, Fields>
#endif
>
explicit
message(U&& u)
: body(std::forward<U>(u))
{
}
message(U&& u);
/** Construct a message.
@@ -443,22 +438,14 @@ struct message : header<isRequest, Fields>
typename std::decay<U>::type, header_type>::value>::type
#endif
>
message(U&& u, V&& v)
: header_type(std::forward<V>(v))
, body(std::forward<U>(u))
{
}
message(U&& u, V&& v);
/** Construct a message.
@param un A tuple forwarded as a parameter pack to the body constructor.
*/
template<class... Un>
message(std::piecewise_construct_t, std::tuple<Un...> un)
: message(std::piecewise_construct, un,
beast::detail::make_index_sequence<sizeof...(Un)>{})
{
}
message(std::piecewise_construct_t, std::tuple<Un...> un);
/** Construct a message.
@@ -468,17 +455,25 @@ struct message : header<isRequest, Fields>
*/
template<class... Un, class... Vn>
message(std::piecewise_construct_t,
std::tuple<Un...>&& un, std::tuple<Vn...>&& vn)
: message(std::piecewise_construct, un, vn,
beast::detail::make_index_sequence<sizeof...(Un)>{},
beast::detail::make_index_sequence<sizeof...(Vn)>{})
{
}
std::tuple<Un...>&& un, std::tuple<Vn...>&& vn);
/** Returns `true` if Transfer-Encoding is present, and chunked appears last.
/// Returns `true` if "close" is specified in the Connection field.
bool
has_close() const;
/// Returns `true` if "chunked" is the last Transfer-Encoding.
bool
has_chunked() const;
/** Returns `true` if the Content-Length field is present.
This function checks the fields to determine if the content
length field is present, regardless of the actual value.
@note The contents of the body payload are not inspected.
*/
bool
chunked() const;
has_content_length() const;
/** Returns the payload size of the body in octets if possible.

View File

@@ -189,7 +189,7 @@ private:
if(method != verb::unknown)
m_.method(method);
else
m_.method(method_str);
m_.method_string(method_str);
m_.version = version;
}
@@ -241,17 +241,15 @@ private:
}
void
on_data(string_view s,
error_code& ec)
on_data(string_view s, error_code& ec)
{
wr_->put(boost::asio::buffer(
s.data(), s.size()), ec);
}
void
on_chunk(
std::uint64_t, string_view,
error_code&)
on_chunk(std::uint64_t,
string_view, error_code&)
{
}

View File

@@ -12,7 +12,6 @@
#include <beast/core/async_result.hpp>
#include <beast/core/buffer_cat.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/core/string_view.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/http/message.hpp>
@@ -155,24 +154,26 @@ class serializer
void split(bool, std::true_type) {}
void split(bool v, std::false_type) { split_ = v; }
using buffer_type =
basic_multi_buffer<Allocator>;
void frdinit(std::true_type);
void frdinit(std::false_type);
using reader = typename Body::reader;
using is_deferred =
typename reader::is_deferred;
using ch_t = consuming_buffers<typename
Fields::reader::const_buffers_type>; // header
using cb0_t = consuming_buffers<buffer_cat_view<
typename buffer_type::const_buffers_type, // header
typename Fields::reader::const_buffers_type,// header
typename reader::const_buffers_type>>; // body
using cb1_t = consuming_buffers<
typename reader::const_buffers_type>; // body
using ch0_t = consuming_buffers<buffer_cat_view<
typename buffer_type::const_buffers_type, // header
typename Fields::reader::const_buffers_type,// header
detail::chunk_header, // chunk-header
boost::asio::const_buffers_1, // chunk-ext
boost::asio::const_buffers_1, // crlf
@@ -192,11 +193,11 @@ class serializer
boost::asio::const_buffers_1>>; // crlf
message<isRequest, Body, Fields> const& m_;
boost::optional<typename Fields::reader> frd_;
ChunkDecorator d_;
boost::optional<reader> rd_;
buffer_type b_;
boost::variant<boost::blank,
cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
ch_t, cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
int s_ = do_construct;
bool split_ = is_deferred::value;
bool header_done_ = false;

View File

@@ -68,7 +68,7 @@ public:
flat_buffer buffer;
request<string_body> req;
req.version = 11;
req.method("POST");
req.method_string("POST");
req.target("/");
req.insert(field::user_agent, "test");
req.body = "Hello, world!";
@@ -101,7 +101,7 @@ public:
{
request<string_body> req;
req.version = 11;
req.method("POST");
req.method_string("POST");
req.target("/");
req.insert(field::user_agent, "test");
req.body = "Hello, world!";

View File

@@ -134,7 +134,7 @@ public:
m1.target("u");
m1.body = "1";
m1.insert("h", "v");
m2.method("G");
m2.method_string("G");
m2.body = "2";
swap(m1, m2);
BEAST_EXPECT(m1.method_string() == "G");
@@ -268,7 +268,7 @@ public:
auto const scheck =
[&](string_view s)
{
h.method(s);
h.method_string(s);
BEAST_EXPECT(h.method() == string_to_verb(s));
BEAST_EXPECT(h.method_string() == s);
};