mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 05:17:26 +02:00
Refactor prepare (API Change)
This commit is contained in:
@ -8,6 +8,7 @@ API Changes:
|
|||||||
|
|
||||||
* Remove header_parser
|
* Remove header_parser
|
||||||
* Add verb to on_request for parsers
|
* Add verb to on_request for parsers
|
||||||
|
* Refactor prepare
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ objects:
|
|||||||
```
|
```
|
||||||
][
|
][
|
||||||
```
|
```
|
||||||
200 OK HTTP/1.1\r\n
|
HTTP/1.1 200 OK\r\n
|
||||||
Server: Beast\r\n
|
Server: Beast\r\n
|
||||||
Content-Length: 13\r\n
|
Content-Length: 13\r\n
|
||||||
\r\n
|
\r\n
|
||||||
|
@ -62,7 +62,6 @@
|
|||||||
<member><link linkend="beast.ref.http__make_serializer">make_serializer</link></member>
|
<member><link linkend="beast.ref.http__make_serializer">make_serializer</link></member>
|
||||||
<member><link linkend="beast.ref.http__obsolete_reason">obsolete_reason</link></member>
|
<member><link linkend="beast.ref.http__obsolete_reason">obsolete_reason</link></member>
|
||||||
<member><link linkend="beast.ref.http__operator_ls_">operator<<</link></member>
|
<member><link linkend="beast.ref.http__operator_ls_">operator<<</link></member>
|
||||||
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
|
||||||
<member><link linkend="beast.ref.http__read">read</link></member>
|
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||||
<member><link linkend="beast.ref.http__read_header">read_header</link></member>
|
<member><link linkend="beast.ref.http__read_header">read_header</link></member>
|
||||||
<member><link linkend="beast.ref.http__read_some">read_some</link></member>
|
<member><link linkend="beast.ref.http__read_some">read_some</link></member>
|
||||||
|
@ -449,9 +449,10 @@ do_head_request(
|
|||||||
@param transform The header transformation to apply. The function will
|
@param transform The header transformation to apply. The function will
|
||||||
be called with this signature:
|
be called with this signature:
|
||||||
@code
|
@code
|
||||||
void transform(
|
template<class Body>
|
||||||
header<isRequest, Fields>&, // The header to transform
|
void transform(message<
|
||||||
error_code&); // Set to the error, if any
|
isRequest, Body, Fields>&, // The message to transform
|
||||||
|
error_code&); // Set to the error, if any
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
@param ec Set to the error if any occurred.
|
@param ec Set to the error if any occurred.
|
||||||
@ -496,8 +497,7 @@ relay(
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Apply the caller's header tranformation
|
// Apply the caller's header tranformation
|
||||||
// base() returns a reference to the header portion of the message.
|
transform(p.get(), ec);
|
||||||
transform(p.get().base(), ec);
|
|
||||||
if(ec)
|
if(ec)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -24,6 +24,16 @@ struct file_body
|
|||||||
{
|
{
|
||||||
using value_type = std::string;
|
using value_type = std::string;
|
||||||
|
|
||||||
|
/// Returns the content length of the body in a message.
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
static
|
||||||
|
std::uint64_t
|
||||||
|
size(
|
||||||
|
message<isRequest, file_body, Fields> const& m)
|
||||||
|
{
|
||||||
|
return boost::filesystem::file_size(m.body.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
class reader
|
class reader
|
||||||
{
|
{
|
||||||
std::uint64_t size_ = 0;
|
std::uint64_t size_ = 0;
|
||||||
@ -67,12 +77,6 @@ struct file_body
|
|||||||
size_ = boost::filesystem::file_size(path_);
|
size_ = boost::filesystem::file_size(path_);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
|
||||||
content_length() const
|
|
||||||
{
|
|
||||||
return size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::optional<std::pair<const_buffers_type, bool>>
|
boost::optional<std::pair<const_buffers_type, bool>>
|
||||||
get(error_code& ec)
|
get(error_code& ec)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +239,7 @@ private:
|
|||||||
res.insert("Server", "http_async_server");
|
res.insert("Server", "http_async_server");
|
||||||
res.insert("Content-Type", "text/html");
|
res.insert("Content-Type", "text/html");
|
||||||
res.body = "The file '" + path + "' was not found";
|
res.body = "The file '" + path + "' was not found";
|
||||||
prepare(res);
|
res.prepare();
|
||||||
async_write(sock_, std::move(res),
|
async_write(sock_, std::move(res),
|
||||||
std::bind(&peer::on_write, shared_from_this(),
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
std::placeholders::_1));
|
std::placeholders::_1));
|
||||||
@ -253,7 +253,7 @@ private:
|
|||||||
res.insert("Server", "http_async_server");
|
res.insert("Server", "http_async_server");
|
||||||
res.insert("Content-Type", mime_type(path));
|
res.insert("Content-Type", mime_type(path));
|
||||||
res.body = path;
|
res.body = path;
|
||||||
prepare(res);
|
res.prepare();
|
||||||
async_write(sock_, std::move(res),
|
async_write(sock_, std::move(res),
|
||||||
std::bind(&peer::on_write, shared_from_this(),
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
std::placeholders::_1));
|
std::placeholders::_1));
|
||||||
@ -267,7 +267,7 @@ private:
|
|||||||
res.insert("Content-Type", "text/html");
|
res.insert("Content-Type", "text/html");
|
||||||
res.body =
|
res.body =
|
||||||
std::string{"An internal error occurred"} + e.what();
|
std::string{"An internal error occurred"} + e.what();
|
||||||
prepare(res);
|
res.prepare();
|
||||||
async_write(sock_, std::move(res),
|
async_write(sock_, std::move(res),
|
||||||
std::bind(&peer::on_write, shared_from_this(),
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
std::placeholders::_1));
|
std::placeholders::_1));
|
||||||
|
@ -38,12 +38,12 @@ int main(int, char const*[])
|
|||||||
auto ep = sock.remote_endpoint();
|
auto ep = sock.remote_endpoint();
|
||||||
request<string_body> req;
|
request<string_body> req;
|
||||||
req.method(verb::get);
|
req.method(verb::get);
|
||||||
req.target("/");
|
|
||||||
req.version = 11;
|
req.version = 11;
|
||||||
|
req.target("/");
|
||||||
req.insert("Host", host + std::string(":") +
|
req.insert("Host", host + std::string(":") +
|
||||||
boost::lexical_cast<std::string>(ep.port()));
|
boost::lexical_cast<std::string>(ep.port()));
|
||||||
req.insert("User-Agent", "beast/http");
|
req.insert("User-Agent", "beast/http");
|
||||||
prepare(req);
|
req.prepare();
|
||||||
write(sock, req);
|
write(sock, req);
|
||||||
response<string_body> res;
|
response<string_body> res;
|
||||||
beast::multi_buffer b;
|
beast::multi_buffer b;
|
||||||
|
@ -32,7 +32,7 @@ int main()
|
|||||||
req.replace("Host", host + ":" +
|
req.replace("Host", host + ":" +
|
||||||
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||||
req.replace("User-Agent", "Beast");
|
req.replace("User-Agent", "Beast");
|
||||||
beast::http::prepare(req);
|
req.prepare();
|
||||||
beast::http::write(sock, req);
|
beast::http::write(sock, req);
|
||||||
|
|
||||||
// Receive and print HTTP response using beast
|
// Receive and print HTTP response using beast
|
||||||
|
@ -169,7 +169,7 @@ private:
|
|||||||
res.insert("Server", "http_sync_server");
|
res.insert("Server", "http_sync_server");
|
||||||
res.insert("Content-Type", "text/html");
|
res.insert("Content-Type", "text/html");
|
||||||
res.body = "The file '" + path + "' was not found";
|
res.body = "The file '" + path + "' was not found";
|
||||||
prepare(res);
|
res.prepare();
|
||||||
write(sock, res, ec);
|
write(sock, res, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
break;
|
break;
|
||||||
@ -184,7 +184,7 @@ private:
|
|||||||
res.insert("Server", "http_sync_server");
|
res.insert("Server", "http_sync_server");
|
||||||
res.insert("Content-Type", mime_type(path));
|
res.insert("Content-Type", mime_type(path));
|
||||||
res.body = path;
|
res.body = path;
|
||||||
prepare(res);
|
res.prepare();
|
||||||
write(sock, res, ec);
|
write(sock, res, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
break;
|
break;
|
||||||
@ -199,7 +199,7 @@ private:
|
|||||||
res.insert("Content-Type", "text/html");
|
res.insert("Content-Type", "text/html");
|
||||||
res.body =
|
res.body =
|
||||||
std::string{"An internal error occurred: "} + e.what();
|
std::string{"An internal error occurred: "} + e.what();
|
||||||
prepare(res);
|
res.prepare();
|
||||||
write(sock, res, ec);
|
write(sock, res, ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
break;
|
break;
|
||||||
|
@ -41,7 +41,7 @@ int main()
|
|||||||
req.insert("Host", host + ":" +
|
req.insert("Host", host + ":" +
|
||||||
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
boost::lexical_cast<std::string>(sock.remote_endpoint().port()));
|
||||||
req.insert("User-Agent", "Beast");
|
req.insert("User-Agent", "Beast");
|
||||||
beast::http::prepare(req);
|
req.prepare();
|
||||||
beast::http::write(stream, req);
|
beast::http::write(stream, req);
|
||||||
|
|
||||||
// Receive and print HTTP response using Beast
|
// Receive and print HTTP response using Beast
|
||||||
|
@ -12,9 +12,11 @@
|
|||||||
|
|
||||||
#include <beast/http/basic_parser.hpp>
|
#include <beast/http/basic_parser.hpp>
|
||||||
#include <beast/http/buffer_body.hpp>
|
#include <beast/http/buffer_body.hpp>
|
||||||
|
#include <beast/http/connection.hpp>
|
||||||
#include <beast/http/dynamic_body.hpp>
|
#include <beast/http/dynamic_body.hpp>
|
||||||
#include <beast/http/empty_body.hpp>
|
#include <beast/http/empty_body.hpp>
|
||||||
#include <beast/http/error.hpp>
|
#include <beast/http/error.hpp>
|
||||||
|
#include <beast/http/field.hpp>
|
||||||
#include <beast/http/fields.hpp>
|
#include <beast/http/fields.hpp>
|
||||||
#include <beast/http/message.hpp>
|
#include <beast/http/message.hpp>
|
||||||
#include <beast/http/parser.hpp>
|
#include <beast/http/parser.hpp>
|
||||||
|
88
include/beast/http/connection.hpp
Normal file
88
include/beast/http/connection.hpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_CONNECTION_HPP
|
||||||
|
#define BEAST_HTTP_CONNECTION_HPP
|
||||||
|
|
||||||
|
#include <beast/config.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/// The type of @ref connection::close
|
||||||
|
struct close_t {};
|
||||||
|
|
||||||
|
/// The type of @ref connection::upgrade
|
||||||
|
struct upgrade_t {};
|
||||||
|
|
||||||
|
/// The type of @ref connection::keep_alive
|
||||||
|
struct keep_alive_t {};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
struct connection_impl
|
||||||
|
{
|
||||||
|
static close_t constexpr close{};
|
||||||
|
static keep_alive_t constexpr keep_alive{};
|
||||||
|
static upgrade_t constexpr upgrade{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
constexpr
|
||||||
|
close_t
|
||||||
|
connection_impl<_>::close;
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
constexpr
|
||||||
|
keep_alive_t
|
||||||
|
connection_impl<_>::keep_alive;
|
||||||
|
|
||||||
|
template<class _>
|
||||||
|
constexpr
|
||||||
|
upgrade_t
|
||||||
|
connection_impl<_>::upgrade;
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** HTTP/1 Connection prepare options.
|
||||||
|
|
||||||
|
Each value is an individual settings, they can be combined.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
@code
|
||||||
|
request<empty_body> req;
|
||||||
|
req.version = 11;
|
||||||
|
req.method(verb::upgrade);
|
||||||
|
req.target("/");
|
||||||
|
req.insert("User-Agent", "Beast");
|
||||||
|
req.prepare(connection::close, connection::upgrade);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@note These values are used with @ref message::prepare.
|
||||||
|
*/
|
||||||
|
struct connection
|
||||||
|
#if ! BEAST_DOXYGEN
|
||||||
|
: detail::connection_impl<>
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if BEAST_DOXYGEN
|
||||||
|
/// Specify connection close semantics.
|
||||||
|
static close_t constexpr close;
|
||||||
|
|
||||||
|
/// Specify connection keep-alive semantics.
|
||||||
|
static keep_alive_t constexpr keep_alive;
|
||||||
|
|
||||||
|
/// Specify Connection: upgrade.
|
||||||
|
static upgrade_t constexpr upgrade;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
@ -59,19 +59,21 @@ struct has_value_type<T, beast::detail::void_t<
|
|||||||
typename T::value_type
|
typename T::value_type
|
||||||
> > : std::true_type {};
|
> > : std::true_type {};
|
||||||
|
|
||||||
template<class T, class = beast::detail::void_t<>>
|
/** Determine if a @b Body type has a size
|
||||||
struct has_content_length : std::false_type {};
|
|
||||||
|
|
||||||
template<class T>
|
This metafunction is equivalent to `std::true_type` if
|
||||||
struct has_content_length<T, beast::detail::void_t<decltype(
|
Body contains a static member function called `content_lengeth`.
|
||||||
std::declval<T>().content_length()
|
*/
|
||||||
)> > : std::true_type
|
template<class T, class M, class = void>
|
||||||
{
|
struct is_body_sized : std::false_type {};
|
||||||
static_assert(std::is_convertible<
|
|
||||||
decltype(std::declval<T>().content_length()),
|
template<class T, class M>
|
||||||
std::uint64_t>::value,
|
struct is_body_sized<T, M, beast::detail::void_t<
|
||||||
"Writer::content_length requirements not met");
|
typename T::value_type,
|
||||||
};
|
decltype(
|
||||||
|
std::declval<std::uint64_t&>() =
|
||||||
|
T::size(std::declval<M const&>()),
|
||||||
|
(void)0)>> : std::true_type {};
|
||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
} // http
|
} // http
|
||||||
|
@ -28,6 +28,16 @@ struct basic_dynamic_body
|
|||||||
/// The type of the body member when used in a message.
|
/// The type of the body member when used in a message.
|
||||||
using value_type = DynamicBuffer;
|
using value_type = DynamicBuffer;
|
||||||
|
|
||||||
|
/// Returns the content length of this body in a message.
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
static
|
||||||
|
std::uint64_t
|
||||||
|
size(message<isRequest,
|
||||||
|
basic_dynamic_body, Fields> const& m)
|
||||||
|
{
|
||||||
|
return m.body.size();
|
||||||
|
}
|
||||||
|
|
||||||
#if BEAST_DOXYGEN
|
#if BEAST_DOXYGEN
|
||||||
/// The algorithm to obtain buffers representing the body
|
/// The algorithm to obtain buffers representing the body
|
||||||
using reader = implementation_defined;
|
using reader = implementation_defined;
|
||||||
@ -55,12 +65,6 @@ struct basic_dynamic_body
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
|
||||||
content_length() const
|
|
||||||
{
|
|
||||||
return body_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::optional<std::pair<const_buffers_type, bool>>
|
boost::optional<std::pair<const_buffers_type, bool>>
|
||||||
get(error_code& ec)
|
get(error_code& ec)
|
||||||
{
|
{
|
||||||
|
@ -35,6 +35,15 @@ struct empty_body
|
|||||||
// for the content length here, set on init()
|
// for the content length here, set on init()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Returns the content length of the body in a message.
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
static
|
||||||
|
std::uint64_t
|
||||||
|
size(message<isRequest, empty_body, Fields> const& m)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#if BEAST_DOXYGEN
|
#if BEAST_DOXYGEN
|
||||||
/// The algorithm to obtain buffers representing the body
|
/// The algorithm to obtain buffers representing the body
|
||||||
using reader = implementation_defined;
|
using reader = implementation_defined;
|
||||||
@ -58,12 +67,6 @@ struct empty_body
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uint64_t
|
|
||||||
content_length() const
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::optional<std::pair<const_buffers_type, bool>>
|
boost::optional<std::pair<const_buffers_type, bool>>
|
||||||
get(error_code& ec)
|
get(error_code& ec)
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <beast/config.hpp>
|
#include <beast/config.hpp>
|
||||||
#include <beast/core/string_view.hpp>
|
#include <beast/core/string_view.hpp>
|
||||||
#include <beast/core/detail/ci_char_traits.hpp>
|
#include <beast/core/detail/ci_char_traits.hpp>
|
||||||
|
#include <beast/http/connection.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>
|
||||||
@ -276,6 +277,11 @@ protected:
|
|||||||
void method_impl(string_view s);
|
void method_impl(string_view s);
|
||||||
void target_impl(string_view s);
|
void target_impl(string_view s);
|
||||||
void reason_impl(string_view s);
|
void reason_impl(string_view s);
|
||||||
|
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();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct element
|
struct element
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#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/static_string.hpp>
|
||||||
#include <beast/http/detail/rfc7230.hpp>
|
#include <beast/http/detail/rfc7230.hpp>
|
||||||
#include <boost/throw_exception.hpp>
|
#include <boost/throw_exception.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -74,7 +75,7 @@ target_impl(string_view s)
|
|||||||
if(s.empty())
|
if(s.empty())
|
||||||
this->erase(":target");
|
this->erase(":target");
|
||||||
else
|
else
|
||||||
return this->replace(":target", s);
|
this->replace(":target", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
@ -89,6 +90,73 @@ reason_impl(string_view s)
|
|||||||
this->replace(":reason", s);
|
this->replace(":reason", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_fields<Allocator>::
|
||||||
|
content_length_impl(std::uint64_t n)
|
||||||
|
{
|
||||||
|
this->erase("Content-Length");
|
||||||
|
this->insert("Content-Length",
|
||||||
|
to_static_string(n));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_fields<Allocator>::
|
||||||
|
connection_impl(close_t)
|
||||||
|
{
|
||||||
|
auto it = find("Connection");
|
||||||
|
if(it == end())
|
||||||
|
this->insert("Connection", "close");
|
||||||
|
else
|
||||||
|
this->replace("Connection",
|
||||||
|
it->value().to_string() + ", close");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_fields<Allocator>::
|
||||||
|
connection_impl(keep_alive_t)
|
||||||
|
{
|
||||||
|
auto it = find("Connection");
|
||||||
|
if(it == end())
|
||||||
|
this->insert("Connection", "keep-alive");
|
||||||
|
else
|
||||||
|
this->replace("Connection",
|
||||||
|
it->value().to_string() + ", keep-alive");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_fields<Allocator>::
|
||||||
|
connection_impl(upgrade_t)
|
||||||
|
{
|
||||||
|
auto it = find("Connection");
|
||||||
|
if(it == end())
|
||||||
|
this->insert("Connection", "upgrade");
|
||||||
|
else
|
||||||
|
this->replace("Connection",
|
||||||
|
it->value().to_string() + ", upgrade");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_fields<Allocator>::
|
||||||
|
chunked_impl()
|
||||||
|
{
|
||||||
|
auto it = find("Transfer-Encoding");
|
||||||
|
if(it == end())
|
||||||
|
this->insert("Transfer-Encoding", "chunked");
|
||||||
|
else
|
||||||
|
this->replace("Transfer-Encoding",
|
||||||
|
it->value().to_string() + ", chunked");
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class Allocator>
|
template<class Allocator>
|
||||||
|
@ -9,14 +9,10 @@
|
|||||||
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
||||||
|
|
||||||
#include <beast/core/error.hpp>
|
#include <beast/core/error.hpp>
|
||||||
#include <beast/core/string_view.hpp>
|
|
||||||
#include <beast/http/type_traits.hpp>
|
|
||||||
#include <beast/http/rfc7230.hpp>
|
#include <beast/http/rfc7230.hpp>
|
||||||
#include <beast/core/detail/ci_char_traits.hpp>
|
#include <beast/core/detail/ci_char_traits.hpp>
|
||||||
#include <beast/core/detail/type_traits.hpp>
|
#include <beast/core/detail/type_traits.hpp>
|
||||||
#include <boost/assert.hpp>
|
#include <boost/assert.hpp>
|
||||||
#include <boost/optional.hpp>
|
|
||||||
#include <boost/throw_exception.hpp>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
@ -71,6 +67,170 @@ get_reason() const
|
|||||||
return obsolete_reason(result_);
|
return obsolete_reason(result_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
bool
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
chunked() 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 false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
boost::optional<std::uint64_t>
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
size() const
|
||||||
|
{
|
||||||
|
static_assert(is_body_reader<Body>::value,
|
||||||
|
"BodyReader requirements not met");
|
||||||
|
|
||||||
|
return size(detail::is_body_sized<
|
||||||
|
Body, decltype(*this)>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
template<class... Args>
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare(Args const&... args)
|
||||||
|
{
|
||||||
|
static_assert(is_body_reader<Body>::value,
|
||||||
|
"BodyReader requirements not met");
|
||||||
|
|
||||||
|
unsigned f = 0;
|
||||||
|
prepare_opt(f, args...);
|
||||||
|
|
||||||
|
if(f & 1)
|
||||||
|
{
|
||||||
|
if(this->version > 10)
|
||||||
|
this->connection_impl(connection::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(f & 2)
|
||||||
|
{
|
||||||
|
if(this->version < 11)
|
||||||
|
this->connection_impl(connection::keep_alive);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(f & 4)
|
||||||
|
{
|
||||||
|
if(this->version < 11)
|
||||||
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||||
|
"invalid connection upgrade"});
|
||||||
|
this->connection_impl(connection::upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_payload(typename header<
|
||||||
|
isRequest, Fields>::is_request{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
template<class Arg, class... Args>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare_opt(unsigned& f,
|
||||||
|
Arg const& arg, Args const&... args)
|
||||||
|
{
|
||||||
|
prepare_opt(f, arg);
|
||||||
|
prepare_opt(f, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare_opt(unsigned& f, close_t)
|
||||||
|
{
|
||||||
|
f |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare_opt(unsigned& f, keep_alive_t)
|
||||||
|
{
|
||||||
|
f |= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare_opt(unsigned& f, upgrade_t)
|
||||||
|
{
|
||||||
|
f |= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare_payload(std::true_type)
|
||||||
|
{
|
||||||
|
auto const n = size();
|
||||||
|
if(this->method_ == verb::trace &&
|
||||||
|
(! n || *n > 0))
|
||||||
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||||
|
"invalid request body"});
|
||||||
|
if(n)
|
||||||
|
{
|
||||||
|
if(*n > 0 ||
|
||||||
|
this->method_ == verb::options ||
|
||||||
|
this->method_ == verb::put ||
|
||||||
|
this->method_ == verb::post
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this->content_length_impl(*n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(this->version >= 11)
|
||||||
|
{
|
||||||
|
this->chunked_impl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Fields>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Fields>::
|
||||||
|
prepare_payload(std::false_type)
|
||||||
|
{
|
||||||
|
auto const n = size();
|
||||||
|
if((status_class(this->result_) ==
|
||||||
|
status_class::informational ||
|
||||||
|
this->result_ == status::no_content ||
|
||||||
|
this->result_ == status::not_modified))
|
||||||
|
{
|
||||||
|
if(! n || *n > 0)
|
||||||
|
// The response body MUST BE empty for this case
|
||||||
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||||
|
"invalid response body"});
|
||||||
|
}
|
||||||
|
if(n)
|
||||||
|
this->content_length_impl(*n);
|
||||||
|
else if(this->version >= 11)
|
||||||
|
this->chunked_impl();
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
template<class Fields>
|
template<class Fields>
|
||||||
void
|
void
|
||||||
swap(
|
swap(
|
||||||
@ -106,7 +266,9 @@ swap(
|
|||||||
message<isRequest, Body, Fields>& m2)
|
message<isRequest, Body, Fields>& m2)
|
||||||
{
|
{
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(m1.base(), m2.base());
|
swap(
|
||||||
|
static_cast<header<isRequest, Fields>&>(m1),
|
||||||
|
static_cast<header<isRequest, Fields>&>(m2));
|
||||||
swap(m1.body, m2.body);
|
swap(m1.body, m2.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,176 +300,6 @@ is_upgrade(header<isRequest, Fields> const& msg)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
struct prepare_info
|
|
||||||
{
|
|
||||||
boost::optional<connection> connection_value;
|
|
||||||
boost::optional<std::uint64_t> content_length;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
prepare_options(prepare_info& pi,
|
|
||||||
message<isRequest, Body, Fields>& msg)
|
|
||||||
{
|
|
||||||
beast::detail::ignore_unused(pi, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
|
||||||
void
|
|
||||||
prepare_option(prepare_info& pi,
|
|
||||||
message<isRequest, Body, Fields>& msg,
|
|
||||||
connection value)
|
|
||||||
{
|
|
||||||
beast::detail::ignore_unused(msg);
|
|
||||||
pi.connection_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<
|
|
||||||
bool isRequest, class Body, class Fields,
|
|
||||||
class Opt, class... Opts>
|
|
||||||
void
|
|
||||||
prepare_options(prepare_info& pi,
|
|
||||||
message<isRequest, Body, Fields>& msg,
|
|
||||||
Opt&& opt, Opts&&... opts)
|
|
||||||
{
|
|
||||||
prepare_option(pi, msg, opt);
|
|
||||||
prepare_options(pi, msg,
|
|
||||||
std::forward<Opts>(opts)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
|
||||||
void
|
|
||||||
prepare_content_length(prepare_info& pi,
|
|
||||||
message<isRequest, Body, Fields> const& msg,
|
|
||||||
std::true_type)
|
|
||||||
{
|
|
||||||
typename Body::reader w{msg};
|
|
||||||
// VFALCO This is a design problem!
|
|
||||||
error_code ec;
|
|
||||||
w.init(ec);
|
|
||||||
if(ec)
|
|
||||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
|
||||||
pi.content_length = w.content_length();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool isRequest, class Body, class Fields>
|
|
||||||
void
|
|
||||||
prepare_content_length(prepare_info& pi,
|
|
||||||
message<isRequest, Body, Fields> const& msg,
|
|
||||||
std::false_type)
|
|
||||||
{
|
|
||||||
beast::detail::ignore_unused(msg);
|
|
||||||
pi.content_length = boost::none;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // detail
|
|
||||||
|
|
||||||
template<
|
|
||||||
bool isRequest, class Body, class Fields,
|
|
||||||
class... Options>
|
|
||||||
void
|
|
||||||
prepare(message<isRequest, Body, Fields>& msg,
|
|
||||||
Options&&... options)
|
|
||||||
{
|
|
||||||
// VFALCO TODO
|
|
||||||
static_assert(is_body<Body>::value,
|
|
||||||
"Body requirements not met");
|
|
||||||
static_assert(is_body_reader<Body>::value,
|
|
||||||
"BodyReader requirements not met");
|
|
||||||
detail::prepare_info pi;
|
|
||||||
detail::prepare_content_length(pi, msg,
|
|
||||||
detail::has_content_length<typename Body::reader>{});
|
|
||||||
detail::prepare_options(pi, msg,
|
|
||||||
std::forward<Options>(options)...);
|
|
||||||
|
|
||||||
if(msg.exists("Connection"))
|
|
||||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
||||||
"prepare called with Connection field set"});
|
|
||||||
|
|
||||||
if(msg.exists("Content-Length"))
|
|
||||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
||||||
"prepare called with Content-Length field set"});
|
|
||||||
|
|
||||||
if(token_list{msg["Transfer-Encoding"]}.exists("chunked"))
|
|
||||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
||||||
"prepare called with Transfer-Encoding: chunked set"});
|
|
||||||
|
|
||||||
if(pi.connection_value != connection::upgrade)
|
|
||||||
{
|
|
||||||
if(pi.content_length)
|
|
||||||
{
|
|
||||||
struct set_field
|
|
||||||
{
|
|
||||||
void
|
|
||||||
operator()(message<true, Body, Fields>& msg,
|
|
||||||
detail::prepare_info const& pi) const
|
|
||||||
{
|
|
||||||
using beast::detail::ci_equal;
|
|
||||||
if(*pi.content_length > 0 ||
|
|
||||||
msg.method() == verb::post)
|
|
||||||
{
|
|
||||||
msg.insert(
|
|
||||||
"Content-Length", *pi.content_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
operator()(message<false, Body, Fields>& msg,
|
|
||||||
detail::prepare_info const& pi) const
|
|
||||||
{
|
|
||||||
if(to_status_class(msg.result()) != status_class::informational &&
|
|
||||||
msg.result() != status::no_content &&
|
|
||||||
msg.result() != status::not_modified)
|
|
||||||
{
|
|
||||||
msg.insert(
|
|
||||||
"Content-Length", *pi.content_length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
set_field{}(msg, pi);
|
|
||||||
}
|
|
||||||
else if(msg.version >= 11)
|
|
||||||
{
|
|
||||||
msg.insert("Transfer-Encoding", "chunked");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto const content_length =
|
|
||||||
msg.exists("Content-Length");
|
|
||||||
|
|
||||||
if(pi.connection_value)
|
|
||||||
{
|
|
||||||
switch(*pi.connection_value)
|
|
||||||
{
|
|
||||||
case connection::upgrade:
|
|
||||||
msg.insert("Connection", "upgrade");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case connection::keep_alive:
|
|
||||||
if(msg.version < 11)
|
|
||||||
{
|
|
||||||
if(content_length)
|
|
||||||
msg.insert("Connection", "keep-alive");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case connection::close:
|
|
||||||
if(msg.version >= 11)
|
|
||||||
msg.insert("Connection", "close");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rfc7230 6.7.
|
|
||||||
if(msg.version < 11 && token_list{
|
|
||||||
msg["Connection"]}.exists("upgrade"))
|
|
||||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
||||||
"invalid version for Connection: upgrade"});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
@ -29,8 +29,7 @@ parser<isRequest, Body, Fields>::
|
|||||||
parser(parser<isRequest, OtherBody, Fields>&& parser,
|
parser(parser<isRequest, OtherBody, Fields>&& parser,
|
||||||
Args&&... args)
|
Args&&... args)
|
||||||
: base_type(std::move(parser))
|
: base_type(std::move(parser))
|
||||||
, m_(parser.release().base(),
|
, m_(parser.release(), std::forward<Args>(args)...)
|
||||||
std::forward<Args>(args)...)
|
|
||||||
{
|
{
|
||||||
if(parser.wr_)
|
if(parser.wr_)
|
||||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||||
|
@ -9,11 +9,14 @@
|
|||||||
#define BEAST_HTTP_MESSAGE_HPP
|
#define BEAST_HTTP_MESSAGE_HPP
|
||||||
|
|
||||||
#include <beast/config.hpp>
|
#include <beast/config.hpp>
|
||||||
|
#include <beast/http/connection.hpp>
|
||||||
#include <beast/http/fields.hpp>
|
#include <beast/http/fields.hpp>
|
||||||
#include <beast/http/verb.hpp>
|
#include <beast/http/verb.hpp>
|
||||||
#include <beast/http/status.hpp>
|
#include <beast/http/status.hpp>
|
||||||
|
#include <beast/http/type_traits.hpp>
|
||||||
#include <beast/core/string_view.hpp>
|
#include <beast/core/string_view.hpp>
|
||||||
#include <beast/core/detail/integer_sequence.hpp>
|
#include <beast/core/detail/integer_sequence.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
#include <boost/throw_exception.hpp>
|
#include <boost/throw_exception.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -49,12 +52,7 @@ struct header<true, Fields> : Fields
|
|||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
/// Indicates if the header is a request or response.
|
/// Indicates if the header is a request or response.
|
||||||
#if BEAST_DOXYGEN
|
using is_request = std::true_type;
|
||||||
static bool constexpr is_request = isRequest;
|
|
||||||
|
|
||||||
#else
|
|
||||||
static bool constexpr is_request = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// The type representing the fields.
|
/// The type representing the fields.
|
||||||
using fields_type = Fields;
|
using fields_type = Fields;
|
||||||
@ -194,6 +192,9 @@ struct header<true, Fields> : Fields
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<bool, class, class>
|
||||||
|
friend struct message;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
friend
|
friend
|
||||||
void
|
void
|
||||||
@ -230,7 +231,7 @@ template<class Fields>
|
|||||||
struct header<false, Fields> : Fields
|
struct header<false, Fields> : Fields
|
||||||
{
|
{
|
||||||
/// Indicates if the header is a request or response.
|
/// Indicates if the header is a request or response.
|
||||||
static bool constexpr is_request = false;
|
using is_request = std::false_type;
|
||||||
|
|
||||||
/// The type representing the fields.
|
/// The type representing the fields.
|
||||||
using fields_type = Fields;
|
using fields_type = Fields;
|
||||||
@ -377,6 +378,9 @@ struct header<false, Fields> : Fields
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<bool, class, class>
|
||||||
|
friend struct message;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
friend
|
friend
|
||||||
void
|
void
|
||||||
@ -412,7 +416,7 @@ template<bool isRequest, class Body, class Fields = fields>
|
|||||||
struct message : header<isRequest, Fields>
|
struct message : header<isRequest, Fields>
|
||||||
{
|
{
|
||||||
/// The base class used to hold the header portion of the message.
|
/// The base class used to hold the header portion of the message.
|
||||||
using base_type = header<isRequest, Fields>;
|
using header_type = header<isRequest, Fields>;
|
||||||
|
|
||||||
/** The type providing the body traits.
|
/** The type providing the body traits.
|
||||||
|
|
||||||
@ -445,8 +449,8 @@ struct message : header<isRequest, Fields>
|
|||||||
*/
|
*/
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
explicit
|
explicit
|
||||||
message(base_type&& base, Args&&... args)
|
message(header_type&& base, Args&&... args)
|
||||||
: base_type(std::move(base))
|
: header_type(std::move(base))
|
||||||
, body(std::forward<Args>(args)...)
|
, body(std::forward<Args>(args)...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -458,8 +462,8 @@ struct message : header<isRequest, Fields>
|
|||||||
*/
|
*/
|
||||||
template<class... Args>
|
template<class... Args>
|
||||||
explicit
|
explicit
|
||||||
message(base_type const& base, Args&&... args)
|
message(header_type const& base, Args&&... args)
|
||||||
: base_type(base)
|
: header_type(base)
|
||||||
, body(std::forward<Args>(args)...)
|
, body(std::forward<Args>(args)...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -469,13 +473,13 @@ struct message : header<isRequest, Fields>
|
|||||||
@param u An argument forwarded to the body constructor.
|
@param u An argument forwarded to the body constructor.
|
||||||
|
|
||||||
@note This constructor participates in overload resolution
|
@note This constructor participates in overload resolution
|
||||||
only if `u` is not convertible to `base_type`.
|
only if `u` is not convertible to `header_type`.
|
||||||
*/
|
*/
|
||||||
template<class U
|
template<class U
|
||||||
#if ! BEAST_DOXYGEN
|
#if ! BEAST_DOXYGEN
|
||||||
, class = typename std::enable_if<
|
, class = typename std::enable_if<
|
||||||
! std::is_convertible<typename
|
! std::is_convertible<typename
|
||||||
std::decay<U>::type, base_type>::value>::type
|
std::decay<U>::type, header_type>::value>::type
|
||||||
#endif
|
#endif
|
||||||
>
|
>
|
||||||
explicit
|
explicit
|
||||||
@ -491,16 +495,16 @@ struct message : header<isRequest, Fields>
|
|||||||
@param v An argument forwarded to the fields constructor.
|
@param v An argument forwarded to the fields constructor.
|
||||||
|
|
||||||
@note This constructor participates in overload resolution
|
@note This constructor participates in overload resolution
|
||||||
only if `u` is not convertible to `base_type`.
|
only if `u` is not convertible to `header_type`.
|
||||||
*/
|
*/
|
||||||
template<class U, class V
|
template<class U, class V
|
||||||
#if ! BEAST_DOXYGEN
|
#if ! BEAST_DOXYGEN
|
||||||
,class = typename std::enable_if<! std::is_convertible<
|
,class = typename std::enable_if<! std::is_convertible<
|
||||||
typename std::decay<U>::type, base_type>::value>::type
|
typename std::decay<U>::type, header_type>::value>::type
|
||||||
#endif
|
#endif
|
||||||
>
|
>
|
||||||
message(U&& u, V&& v)
|
message(U&& u, V&& v)
|
||||||
: base_type(std::forward<V>(v))
|
: header_type(std::forward<V>(v))
|
||||||
, body(std::forward<U>(u))
|
, body(std::forward<U>(u))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -531,24 +535,71 @@ struct message : header<isRequest, Fields>
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the header portion of the message
|
/** Returns `true` if Transfer-Encoding is present, and chunked appears last.
|
||||||
base_type&
|
*/
|
||||||
base()
|
bool
|
||||||
{
|
chunked() const;
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the header portion of the message
|
/** Returns the payload size of the body in octets if possible.
|
||||||
base_type const&
|
|
||||||
base() const
|
This function invokes the @b Body algorithm to measure
|
||||||
{
|
the number of octets in the serialized body container. If
|
||||||
return *this;
|
there is no body, this will return zero. Otherwise, if the
|
||||||
}
|
body exists but is not known ahead of time, `boost::none`
|
||||||
|
is returned (usually indicating that a chunked Transfer-Encoding
|
||||||
|
will be used).
|
||||||
|
|
||||||
|
@note The value of the Content-Length field in the message
|
||||||
|
is not inspected.
|
||||||
|
*/
|
||||||
|
boost::optional<std::uint64_t>
|
||||||
|
size() const;
|
||||||
|
|
||||||
|
/** Set the Content-Length field.
|
||||||
|
|
||||||
|
The value of the Content-Length field will be unconditionally
|
||||||
|
set to the specified number of octets.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
content_length(std::uint64_t n);
|
||||||
|
|
||||||
|
/** Prepare some fields automatically.
|
||||||
|
|
||||||
|
This function will adjust the Connection, Content-Length
|
||||||
|
and Transfer-Encoding, fields of the message based on the
|
||||||
|
properties of the body and the options passed in.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
@code
|
||||||
|
request<empty_body> req;
|
||||||
|
req.version = 11;
|
||||||
|
req.method(verb::upgrade);
|
||||||
|
req.target("/");
|
||||||
|
req.insert("User-Agent", "Beast");
|
||||||
|
req.prepare(connection::close, connection::upgrade);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@param args An list of zero or more options to use.
|
||||||
|
|
||||||
|
@throw std::invalid_argument if the values of certain
|
||||||
|
fields detectably violate the semantic requirements of HTTP.
|
||||||
|
|
||||||
|
@note Undefined behavior if called more than once.
|
||||||
|
|
||||||
|
@see @ref connection
|
||||||
|
*/
|
||||||
|
template<class... Args>
|
||||||
|
void
|
||||||
|
prepare(Args const&... args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static_assert(is_body<Body>::value,
|
||||||
|
"Body requirements not met");
|
||||||
|
|
||||||
template<class... Un, size_t... IUn>
|
template<class... Un, size_t... IUn>
|
||||||
message(std::piecewise_construct_t,
|
message(std::piecewise_construct_t,
|
||||||
std::tuple<Un...>& tu, beast::detail::index_sequence<IUn...>)
|
std::tuple<Un...>& tu,
|
||||||
|
beast::detail::index_sequence<IUn...>)
|
||||||
: body(std::forward<Un>(std::get<IUn>(tu))...)
|
: body(std::forward<Un>(std::get<IUn>(tu))...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -559,10 +610,46 @@ private:
|
|||||||
std::tuple<Un...>& tu, std::tuple<Vn...>& tv,
|
std::tuple<Un...>& tu, std::tuple<Vn...>& tv,
|
||||||
beast::detail::index_sequence<IUn...>,
|
beast::detail::index_sequence<IUn...>,
|
||||||
beast::detail::index_sequence<IVn...>)
|
beast::detail::index_sequence<IVn...>)
|
||||||
: base_type(std::forward<Vn>(std::get<IVn>(tv))...)
|
: header_type(std::forward<Vn>(std::get<IVn>(tv))...)
|
||||||
, body(std::forward<Un>(std::get<IUn>(tu))...)
|
, body(std::forward<Un>(std::get<IUn>(tu))...)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<std::uint64_t>
|
||||||
|
size(std::true_type) const
|
||||||
|
{
|
||||||
|
return Body::size(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<std::uint64_t>
|
||||||
|
size(std::false_type) const
|
||||||
|
{
|
||||||
|
return boost::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Arg, class... Args>
|
||||||
|
void
|
||||||
|
prepare_opt(unsigned&, Arg const&, Args const&...);
|
||||||
|
|
||||||
|
void
|
||||||
|
prepare_opt(unsigned&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
prepare_opt(unsigned&, close_t);
|
||||||
|
|
||||||
|
void
|
||||||
|
prepare_opt(unsigned&, keep_alive_t);
|
||||||
|
|
||||||
|
void
|
||||||
|
prepare_opt(unsigned&, upgrade_t);
|
||||||
|
|
||||||
|
void
|
||||||
|
prepare_payload(std::true_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
prepare_payload(std::false_type);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A typical HTTP request
|
/// A typical HTTP request
|
||||||
@ -617,39 +704,6 @@ template<bool isRequest, class Fields>
|
|||||||
bool
|
bool
|
||||||
is_upgrade(header<isRequest, Fields> const& msg);
|
is_upgrade(header<isRequest, Fields> const& msg);
|
||||||
|
|
||||||
/** HTTP/1 connection prepare options.
|
|
||||||
|
|
||||||
@note These values are used with @ref prepare.
|
|
||||||
*/
|
|
||||||
enum class connection
|
|
||||||
{
|
|
||||||
/// Specify Connection: close.
|
|
||||||
close,
|
|
||||||
|
|
||||||
/// Specify Connection: keep-alive where possible.
|
|
||||||
keep_alive,
|
|
||||||
|
|
||||||
/// Specify Connection: upgrade.
|
|
||||||
upgrade
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Prepare an HTTP message.
|
|
||||||
|
|
||||||
This function will adjust the Content-Length, Transfer-Encoding,
|
|
||||||
and Connection fields of the message based on the properties of
|
|
||||||
the body and the options passed in.
|
|
||||||
|
|
||||||
@param msg The message to prepare. The fields may be modified.
|
|
||||||
|
|
||||||
@param options A list of prepare options.
|
|
||||||
*/
|
|
||||||
template<
|
|
||||||
bool isRequest, class Body, class Fields,
|
|
||||||
class... Options>
|
|
||||||
void
|
|
||||||
prepare(message<isRequest, Body, Fields>& msg,
|
|
||||||
Options&&... options);
|
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
@ -30,6 +30,16 @@ struct string_body
|
|||||||
/// The type of the body member when used in a message.
|
/// The type of the body member when used in a message.
|
||||||
using value_type = std::string;
|
using value_type = std::string;
|
||||||
|
|
||||||
|
/// Returns the content length of the body in a message.
|
||||||
|
template<bool isRequest, class Fields>
|
||||||
|
static
|
||||||
|
std::uint64_t
|
||||||
|
size(
|
||||||
|
message<isRequest, string_body, Fields> const& m)
|
||||||
|
{
|
||||||
|
return m.body.size();
|
||||||
|
}
|
||||||
|
|
||||||
#if BEAST_DOXYGEN
|
#if BEAST_DOXYGEN
|
||||||
/// The algorithm to obtain buffers representing the body
|
/// The algorithm to obtain buffers representing the body
|
||||||
using reader = implementation_defined;
|
using reader = implementation_defined;
|
||||||
@ -53,19 +63,12 @@ struct string_body
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init(error_code& ec)
|
init(error_code&)
|
||||||
{
|
{
|
||||||
beast::detail::ignore_unused(ec);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint64_t
|
|
||||||
content_length() const
|
|
||||||
{
|
|
||||||
return body_.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::pair<const_buffers_type, bool>>
|
boost::optional<std::pair<const_buffers_type, bool>>
|
||||||
get(error_code& ec)
|
get(error_code&)
|
||||||
{
|
{
|
||||||
return {{const_buffers_type{
|
return {{const_buffers_type{
|
||||||
body_.data(), body_.size()}, false}};
|
body_.data(), body_.size()}, false}};
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
|
template<bool, class, class>
|
||||||
|
struct message;
|
||||||
|
|
||||||
/** Determine if `T` meets the requirements of @b Body.
|
/** Determine if `T` meets the requirements of @b Body.
|
||||||
|
|
||||||
This metafunction is equivalent to `std::true_type`
|
This metafunction is equivalent to `std::true_type`
|
||||||
|
@ -213,10 +213,10 @@ build_response(http::header<true, Fields> const& req,
|
|||||||
[&](std::string const& text)
|
[&](std::string const& text)
|
||||||
{
|
{
|
||||||
response_type res;
|
response_type res;
|
||||||
res.result(http::status::bad_request);
|
|
||||||
res.version = req.version;
|
res.version = req.version;
|
||||||
|
res.result(http::status::bad_request);
|
||||||
res.body = text;
|
res.body = text;
|
||||||
prepare(res);
|
res.prepare();
|
||||||
decorate(res);
|
decorate(res);
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
@ -246,7 +246,7 @@ build_response(http::header<true, Fields> const& req,
|
|||||||
res.result(http::status::upgrade_required);
|
res.result(http::status::upgrade_required);
|
||||||
res.version = req.version;
|
res.version = req.version;
|
||||||
res.insert("Sec-WebSocket-Version", "13");
|
res.insert("Sec-WebSocket-Version", "13");
|
||||||
prepare(res);
|
res.prepare();
|
||||||
decorate(res);
|
decorate(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public:
|
|||||||
req.target("/");
|
req.target("/");
|
||||||
req.insert("User-Agent", "test");
|
req.insert("User-Agent", "test");
|
||||||
req.body = "Hello, world!";
|
req.body = "Hello, world!";
|
||||||
prepare(req);
|
req.prepare();
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
send_expect_100_continue(
|
send_expect_100_continue(
|
||||||
@ -105,7 +105,7 @@ public:
|
|||||||
req.target("/");
|
req.target("/");
|
||||||
req.insert("User-Agent", "test");
|
req.insert("User-Agent", "test");
|
||||||
req.body = "Hello, world!";
|
req.body = "Hello, world!";
|
||||||
prepare(req);
|
req.prepare();
|
||||||
|
|
||||||
test::pipe downstream{ios_};
|
test::pipe downstream{ios_};
|
||||||
downstream.server.read_size(3);
|
downstream.server.read_size(3);
|
||||||
|
@ -205,7 +205,7 @@ public:
|
|||||||
m.insert("Upgrade", "test");
|
m.insert("Upgrade", "test");
|
||||||
BEAST_EXPECT(! is_upgrade(m));
|
BEAST_EXPECT(! is_upgrade(m));
|
||||||
|
|
||||||
prepare(m, connection::upgrade);
|
m.prepare(connection::upgrade);
|
||||||
BEAST_EXPECT(is_upgrade(m));
|
BEAST_EXPECT(is_upgrade(m));
|
||||||
BEAST_EXPECT(m["Connection"] == "upgrade");
|
BEAST_EXPECT(m["Connection"] == "upgrade");
|
||||||
|
|
||||||
@ -214,49 +214,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
testPrepare()
|
|
||||||
{
|
|
||||||
request<string_body> m;
|
|
||||||
m.version = 10;
|
|
||||||
BEAST_EXPECT(! is_upgrade(m));
|
|
||||||
m.insert("Transfer-Encoding", "chunked");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
prepare(m);
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
catch(std::exception const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
m.erase("Transfer-Encoding");
|
|
||||||
m.insert("Content-Length", "0");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
prepare(m);
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
catch(std::exception const&)
|
|
||||||
{
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
m.erase("Content-Length");
|
|
||||||
m.insert("Connection", "keep-alive");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
prepare(m);
|
|
||||||
fail();
|
|
||||||
}
|
|
||||||
catch(std::exception const&)
|
|
||||||
{
|
|
||||||
pass();
|
|
||||||
}
|
|
||||||
m.version = 11;
|
|
||||||
m.erase("Connection");
|
|
||||||
m.insert("Connection", "close");
|
|
||||||
BEAST_EXPECT(! is_keep_alive(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
testSwap()
|
testSwap()
|
||||||
{
|
{
|
||||||
@ -357,7 +314,6 @@ public:
|
|||||||
testMessage();
|
testMessage();
|
||||||
testHeaders();
|
testHeaders();
|
||||||
testFreeFunctions();
|
testFreeFunctions();
|
||||||
testPrepare();
|
|
||||||
testSwap();
|
testSwap();
|
||||||
testSpecialMembers();
|
testSpecialMembers();
|
||||||
testMethod();
|
testMethod();
|
||||||
|
@ -55,9 +55,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
init(error_code& ec)
|
init(error_code&)
|
||||||
{
|
{
|
||||||
beast::detail::ignore_unused(ec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<std::pair<const_buffers_type, bool>>
|
boost::optional<std::pair<const_buffers_type, bool>>
|
||||||
@ -502,7 +501,7 @@ public:
|
|||||||
m.version = 10;
|
m.version = 10;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m);
|
m.prepare();
|
||||||
BEAST_EXPECT(str(m) ==
|
BEAST_EXPECT(str(m) ==
|
||||||
"GET / HTTP/1.0\r\n"
|
"GET / HTTP/1.0\r\n"
|
||||||
"User-Agent: test\r\n"
|
"User-Agent: test\r\n"
|
||||||
@ -519,12 +518,12 @@ public:
|
|||||||
m.version = 10;
|
m.version = 10;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m, connection::keep_alive);
|
m.prepare(connection::keep_alive);
|
||||||
BEAST_EXPECT(str(m) ==
|
BEAST_EXPECT(str(m) ==
|
||||||
"GET / HTTP/1.0\r\n"
|
"GET / HTTP/1.0\r\n"
|
||||||
"User-Agent: test\r\n"
|
"User-Agent: test\r\n"
|
||||||
"Content-Length: 1\r\n"
|
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: keep-alive\r\n"
|
||||||
|
"Content-Length: 1\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"*"
|
"*"
|
||||||
);
|
);
|
||||||
@ -539,7 +538,7 @@ public:
|
|||||||
m.body = "*";
|
m.body = "*";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
prepare(m, connection::upgrade);
|
m.prepare( connection::upgrade);
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
catch(std::exception const&)
|
catch(std::exception const&)
|
||||||
@ -555,7 +554,7 @@ public:
|
|||||||
m.version = 10;
|
m.version = 10;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m);
|
m.prepare();
|
||||||
test::string_ostream ss(ios_);
|
test::string_ostream ss(ios_);
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write(ss, m, ec);
|
write(ss, m, ec);
|
||||||
@ -575,7 +574,7 @@ public:
|
|||||||
m.version = 11;
|
m.version = 11;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m);
|
m.prepare();
|
||||||
BEAST_EXPECT(str(m) ==
|
BEAST_EXPECT(str(m) ==
|
||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
"User-Agent: test\r\n"
|
"User-Agent: test\r\n"
|
||||||
@ -592,7 +591,7 @@ public:
|
|||||||
m.version = 11;
|
m.version = 11;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m, connection::close);
|
m.prepare(connection::close);
|
||||||
test::string_ostream ss(ios_);
|
test::string_ostream ss(ios_);
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write(ss, m, ec);
|
write(ss, m, ec);
|
||||||
@ -600,8 +599,8 @@ public:
|
|||||||
BEAST_EXPECT(ss.str ==
|
BEAST_EXPECT(ss.str ==
|
||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
"User-Agent: test\r\n"
|
"User-Agent: test\r\n"
|
||||||
"Content-Length: 1\r\n"
|
|
||||||
"Connection: close\r\n"
|
"Connection: close\r\n"
|
||||||
|
"Content-Length: 1\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
"*"
|
"*"
|
||||||
);
|
);
|
||||||
@ -613,7 +612,7 @@ public:
|
|||||||
m.target("/");
|
m.target("/");
|
||||||
m.version = 11;
|
m.version = 11;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
prepare(m, connection::upgrade);
|
m.prepare(connection::upgrade);
|
||||||
BEAST_EXPECT(str(m) ==
|
BEAST_EXPECT(str(m) ==
|
||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
"User-Agent: test\r\n"
|
"User-Agent: test\r\n"
|
||||||
@ -629,7 +628,7 @@ public:
|
|||||||
m.version = 11;
|
m.version = 11;
|
||||||
m.insert("User-Agent", "test");
|
m.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m);
|
m.prepare();
|
||||||
test::string_ostream ss(ios_);
|
test::string_ostream ss(ios_);
|
||||||
error_code ec;
|
error_code ec;
|
||||||
write(ss, m, ec);
|
write(ss, m, ec);
|
||||||
@ -656,8 +655,6 @@ public:
|
|||||||
m.body = "*";
|
m.body = "*";
|
||||||
BEAST_EXPECT(boost::lexical_cast<std::string>(m) ==
|
BEAST_EXPECT(boost::lexical_cast<std::string>(m) ==
|
||||||
"GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n*");
|
"GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n*");
|
||||||
BEAST_EXPECT(boost::lexical_cast<std::string>(m.base()) ==
|
|
||||||
"GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure completion handlers are not leaked
|
// Ensure completion handlers are not leaked
|
||||||
|
Reference in New Issue
Block a user