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_fields and call sites
* Use field in basic_parser * Use field in basic_parser
* Tidy up basic_fields, header, and field concepts * Tidy up basic_fields, header, and field concepts
* Fields concept work
API Changes: API Changes:

View File

@@ -52,7 +52,8 @@
[def __BodyReader__ [link beast.concept.BodyReader [*BodyReader]]] [def __BodyReader__ [link beast.concept.BodyReader [*BodyReader]]]
[def __BodyWriter__ [link beast.concept.BodyWriter [*BodyWriter]]] [def __BodyWriter__ [link beast.concept.BodyWriter [*BodyWriter]]]
[def __DynamicBuffer__ [link beast.concept.DynamicBuffer [*DynamicBuffer]]] [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 __Stream__ [link beast.concept.streams [*Stream]]]
[def __SyncStream__ [link beast.concept.streams.SyncStream [*SyncStream]]] [def __SyncStream__ [link beast.concept.streams.SyncStream [*SyncStream]]]

View File

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

View File

@@ -19,7 +19,7 @@ In this table:
[[expression][type][semantics, pre/post-conditions]] [[expression][type][semantics, pre/post-conditions]]
[ [
[`a.name()`] [`a.name()`]
[`boost::string_ref`] [[link beast.ref.string_view `string_view`]]
[ [
This function returns a value implicitly convertible to This function returns a value implicitly convertible to
`boost::string_ref` containing the case-insensitive field `boost::string_ref` containing the case-insensitive field
@@ -28,7 +28,7 @@ In this table:
] ]
[ [
[`a.value()`] [`a.value()`]
[`boost::string_ref`] [[link beast.ref.string_view `string_view`]]
[ [
This function returns a value implicitly convertible to This function returns a value implicitly convertible to
`boost::string_ref` containing the value for the field. The `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.BodyReader">BodyReader</link></member>
<member><link linkend="beast.concept.BodyWriter">BodyWriter</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.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> </simplelist>
</entry> </entry>
<entry valign="top"> <entry valign="top">

View File

@@ -21,6 +21,12 @@ namespace beast {
template<class... Buffers> template<class... Buffers>
class buffer_cat_view 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_; std::tuple<Buffers...> bn_;
public: public:

View File

@@ -24,6 +24,12 @@ namespace beast {
template<class... Bn> template<class... Bn>
class buffer_cat_view<Bn...>::const_iterator 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::size_t n_;
std::tuple<Bn...> const* bn_; std::tuple<Bn...> const* bn_;
std::array<char, detail::max_sizeof< std::array<char, detail::max_sizeof<

View File

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

View File

@@ -8,8 +8,13 @@
#ifndef BEAST_HTTP_IMPL_FIELDS_IPP #ifndef BEAST_HTTP_IMPL_FIELDS_IPP
#define 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/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 <boost/throw_exception.hpp>
#include <algorithm> #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> template<class Allocator>
basic_fields<Allocator>:: basic_fields<Allocator>::
element:: element::
@@ -150,6 +326,18 @@ value() const
static_cast<std::size_t>(len_)}; 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> template<class Allocator>
@@ -361,31 +549,50 @@ replace(string_view name, string_view value)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Fields
template<class Allocator> template<class Allocator>
inline bool
string_view
basic_fields<Allocator>:: 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> template<class Allocator>
inline bool
string_view
basic_fields<Allocator>:: 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> template<class Allocator>
inline bool
string_view
basic_fields<Allocator>:: 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> template<class Allocator>
@@ -415,6 +622,33 @@ set_reason_impl(string_view s)
realloc_string(target_or_reason_, 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> template<class Allocator>
@@ -474,8 +708,9 @@ template<class Allocator>
inline inline
void void
basic_fields<Allocator>:: basic_fields<Allocator>::
chunked_impl() set_chunked_impl(bool v)
{ {
BOOST_ASSERT(v);
auto it = find("Transfer-Encoding"); auto it = find("Transfer-Encoding");
if(it == end()) if(it == end())
this->insert("Transfer-Encoding", "chunked"); this->insert("Transfer-Encoding", "chunked");
@@ -682,7 +917,6 @@ swap(basic_fields& other, std::false_type)
swap(target_or_reason_, other.target_or_reason_); swap(target_or_reason_, other.target_or_reason_);
} }
} // http } // http
} // beast } // beast

View File

@@ -61,7 +61,7 @@ method_string() const
template<class Fields> template<class Fields>
void void
header<true, Fields>:: header<true, Fields>::
method(string_view s) method_string(string_view s)
{ {
method_ = string_to_verb(s); method_ = string_to_verb(s);
if(method_ != verb::unknown) if(method_ != verb::unknown)
@@ -190,21 +190,85 @@ swap(
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
template<bool isRequest, class Body, class Fields> 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 bool
message<isRequest, Body, Fields>:: message<isRequest, Body, Fields>::
chunked() const has_close() const
{ {
auto const it0 = this->find("Transfer-Encoding"); return this->has_close_impl();
if(it0 == this->end()) }
return false;
token_list value{*it0}; template<bool isRequest, class Body, class Fields>
for(auto it = value.begin(); it != value.end();) inline
{ bool
auto cur = it++; message<isRequest, Body, Fields>::
if(it == value.end()) has_chunked() const
return *cur == "chunked"; {
} return this->has_chunked_impl();
return false; }
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> template<bool isRequest, class Body, class Fields>
@@ -326,7 +390,7 @@ prepare_payload(std::true_type)
} }
else if(this->version >= 11) else if(this->version >= 11)
{ {
this->chunked_impl(); this->set_chunked_impl(true);
} }
} }
@@ -350,7 +414,7 @@ prepare_payload(std::false_type)
if(n) if(n)
this->content_length_impl(*n); this->content_length_impl(*n);
else if(this->version >= 11) else if(this->version >= 11)
this->chunked_impl(); this->set_chunked_impl(true);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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