429 lines
10 KiB
C++
429 lines
10 KiB
C++
//
|
|
// Copyright (c) 2016-2019 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)
|
|
//
|
|
// Official repository: https://github.com/boostorg/beast
|
|
//
|
|
|
|
#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP
|
|
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP
|
|
|
|
#include <boost/beast/core/error.hpp>
|
|
#include <boost/assert.hpp>
|
|
#include <boost/throw_exception.hpp>
|
|
#include <stdexcept>
|
|
|
|
namespace boost {
|
|
namespace beast {
|
|
namespace http {
|
|
|
|
template<class Fields>
|
|
template<class Arg1, class... ArgN, class>
|
|
header<true, Fields>::
|
|
header(Arg1&& arg1, ArgN&&... argn)
|
|
: Fields(std::forward<Arg1>(arg1),
|
|
std::forward<ArgN>(argn)...)
|
|
{
|
|
}
|
|
|
|
template<class Fields>
|
|
verb
|
|
header<true, Fields>::
|
|
method() const
|
|
{
|
|
return method_;
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
header<true, Fields>::
|
|
method(verb v)
|
|
{
|
|
if(v == verb::unknown)
|
|
BOOST_THROW_EXCEPTION(
|
|
std::invalid_argument{"unknown method"});
|
|
method_ = v;
|
|
this->set_method_impl({});
|
|
}
|
|
|
|
template<class Fields>
|
|
string_view
|
|
header<true, Fields>::
|
|
method_string() const
|
|
{
|
|
if(method_ != verb::unknown)
|
|
return to_string(method_);
|
|
return this->get_method_impl();
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
header<true, Fields>::
|
|
method_string(string_view s)
|
|
{
|
|
method_ = string_to_verb(s);
|
|
if(method_ != verb::unknown)
|
|
this->set_method_impl({});
|
|
else
|
|
this->set_method_impl(s);
|
|
}
|
|
|
|
template<class Fields>
|
|
string_view
|
|
header<true, Fields>::
|
|
target() const
|
|
{
|
|
return this->get_target_impl();
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
header<true, Fields>::
|
|
target(string_view s)
|
|
{
|
|
this->set_target_impl(s);
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
swap(
|
|
header<true, Fields>& h1,
|
|
header<true, Fields>& h2)
|
|
{
|
|
using std::swap;
|
|
swap(
|
|
static_cast<Fields&>(h1),
|
|
static_cast<Fields&>(h2));
|
|
swap(h1.version_, h2.version_);
|
|
swap(h1.method_, h2.method_);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
template<class Fields>
|
|
template<class Arg1, class... ArgN, class>
|
|
header<false, Fields>::
|
|
header(Arg1&& arg1, ArgN&&... argn)
|
|
: Fields(std::forward<Arg1>(arg1),
|
|
std::forward<ArgN>(argn)...)
|
|
{
|
|
}
|
|
|
|
template<class Fields>
|
|
status
|
|
header<false, Fields>::
|
|
result() const
|
|
{
|
|
return int_to_status(
|
|
static_cast<int>(result_));
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
header<false, Fields>::
|
|
result(status v)
|
|
{
|
|
result_ = v;
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
header<false, Fields>::
|
|
result(unsigned v)
|
|
{
|
|
if(v > 999)
|
|
BOOST_THROW_EXCEPTION(
|
|
std::invalid_argument{
|
|
"invalid status-code"});
|
|
result_ = static_cast<status>(v);
|
|
}
|
|
|
|
template<class Fields>
|
|
unsigned
|
|
header<false, Fields>::
|
|
result_int() const
|
|
{
|
|
return static_cast<unsigned>(result_);
|
|
}
|
|
|
|
template<class Fields>
|
|
string_view
|
|
header<false, Fields>::
|
|
reason() const
|
|
{
|
|
auto const s = this->get_reason_impl();
|
|
if(! s.empty())
|
|
return s;
|
|
return obsolete_reason(result_);
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
header<false, Fields>::
|
|
reason(string_view s)
|
|
{
|
|
this->set_reason_impl(s);
|
|
}
|
|
|
|
template<class Fields>
|
|
void
|
|
swap(
|
|
header<false, Fields>& h1,
|
|
header<false, Fields>& h2)
|
|
{
|
|
using std::swap;
|
|
swap(
|
|
static_cast<Fields&>(h1),
|
|
static_cast<Fields&>(h2));
|
|
swap(h1.version_, h2.version_);
|
|
swap(h1.result_, h2.result_);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class... BodyArgs>
|
|
message<isRequest, Body, Fields>::
|
|
message(header_type&& h, BodyArgs&&... body_args)
|
|
: header_type(std::move(h))
|
|
, boost::empty_value<
|
|
typename Body::value_type>(boost::empty_init_t(),
|
|
std::forward<BodyArgs>(body_args)...)
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class... BodyArgs>
|
|
message<isRequest, Body, Fields>::
|
|
message(header_type const& h, BodyArgs&&... body_args)
|
|
: header_type(h)
|
|
, boost::empty_value<
|
|
typename Body::value_type>(boost::empty_init_t(),
|
|
std::forward<BodyArgs>(body_args)...)
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class Version, class>
|
|
message<isRequest, Body, Fields>::
|
|
message(verb method, string_view target, Version version)
|
|
: header_type(method, target, version)
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class Version, class BodyArg, class>
|
|
message<isRequest, Body, Fields>::
|
|
message(verb method, string_view target,
|
|
Version version, BodyArg&& body_arg)
|
|
: header_type(method, target, version)
|
|
, boost::empty_value<
|
|
typename Body::value_type>(boost::empty_init_t(),
|
|
std::forward<BodyArg>(body_arg))
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class Version, class BodyArg, class FieldsArg, class>
|
|
message<isRequest, Body, Fields>::
|
|
message(
|
|
verb method, string_view target, Version version,
|
|
BodyArg&& body_arg,
|
|
FieldsArg&& fields_arg)
|
|
: header_type(method, target, version,
|
|
std::forward<FieldsArg>(fields_arg))
|
|
, boost::empty_value<
|
|
typename Body::value_type>(boost::empty_init_t(),
|
|
std::forward<BodyArg>(body_arg))
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class Version, class>
|
|
message<isRequest, Body, Fields>::
|
|
message(status result, Version version)
|
|
: header_type(result, version)
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class Version, class BodyArg, class>
|
|
message<isRequest, Body, Fields>::
|
|
message(status result, Version version,
|
|
BodyArg&& body_arg)
|
|
: header_type(result, version)
|
|
, boost::empty_value<
|
|
typename Body::value_type>(boost::empty_init_t(),
|
|
std::forward<BodyArg>(body_arg))
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class Version, class BodyArg, class FieldsArg, class>
|
|
message<isRequest, Body, Fields>::
|
|
message(status result, Version version,
|
|
BodyArg&& body_arg, FieldsArg&& fields_arg)
|
|
: header_type(result, version,
|
|
std::forward<FieldsArg>(fields_arg))
|
|
, boost::empty_value<
|
|
typename Body::value_type>(boost::empty_init_t(),
|
|
std::forward<BodyArg>(body_arg))
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
message<isRequest, Body, Fields>::
|
|
message(std::piecewise_construct_t)
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class... BodyArgs>
|
|
message<isRequest, Body, Fields>::
|
|
message(std::piecewise_construct_t,
|
|
std::tuple<BodyArgs...> body_args)
|
|
: message(std::piecewise_construct,
|
|
body_args,
|
|
mp11::make_index_sequence<
|
|
sizeof...(BodyArgs)>{})
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
template<class... BodyArgs, class... FieldsArgs>
|
|
message<isRequest, Body, Fields>::
|
|
message(std::piecewise_construct_t,
|
|
std::tuple<BodyArgs...> body_args,
|
|
std::tuple<FieldsArgs...> fields_args)
|
|
: message(std::piecewise_construct,
|
|
body_args,
|
|
fields_args,
|
|
mp11::make_index_sequence<
|
|
sizeof...(BodyArgs)>{},
|
|
mp11::make_index_sequence<
|
|
sizeof...(FieldsArgs)>{})
|
|
{
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
void
|
|
message<isRequest, Body, Fields>::
|
|
chunked(bool value)
|
|
{
|
|
this->set_chunked_impl(value);
|
|
this->set_content_length_impl(boost::none);
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
void
|
|
message<isRequest, Body, Fields>::
|
|
content_length(
|
|
boost::optional<std::uint64_t> const& value)
|
|
{
|
|
this->set_content_length_impl(value);
|
|
this->set_chunked_impl(false);
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
boost::optional<std::uint64_t>
|
|
message<isRequest, Body, Fields>::
|
|
payload_size() const
|
|
{
|
|
return payload_size(detail::is_body_sized<Body>{});
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
bool
|
|
message<isRequest, Body, Fields>::
|
|
need_eof(std::false_type) const
|
|
{
|
|
// VFALCO Do we need a way to let the caller say "the body is intentionally skipped"?
|
|
if( this->result() == status::no_content ||
|
|
this->result() == status::not_modified ||
|
|
to_status_class(this->result()) ==
|
|
status_class::informational ||
|
|
has_content_length() ||
|
|
chunked())
|
|
return ! keep_alive();
|
|
return true;
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
void
|
|
message<isRequest, Body, Fields>::
|
|
prepare_payload(std::true_type)
|
|
{
|
|
auto const n = payload_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(n);
|
|
}
|
|
else
|
|
{
|
|
this->chunked(false);
|
|
}
|
|
}
|
|
else if(this->version() == 11)
|
|
{
|
|
this->chunked(true);
|
|
}
|
|
else
|
|
{
|
|
this->chunked(false);
|
|
}
|
|
}
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
void
|
|
message<isRequest, Body, Fields>::
|
|
prepare_payload(std::false_type)
|
|
{
|
|
auto const n = payload_size();
|
|
if( (! n || *n > 0) && (
|
|
(status_class(this->result()) == status_class::informational ||
|
|
this->result() == status::no_content ||
|
|
this->result() == status::not_modified)))
|
|
{
|
|
// The response body MUST be empty for this case
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
"invalid response body"});
|
|
}
|
|
if(n)
|
|
this->content_length(n);
|
|
else if(this->version() == 11)
|
|
this->chunked(true);
|
|
else
|
|
this->chunked(false);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
template<bool isRequest, class Body, class Fields>
|
|
void
|
|
swap(
|
|
message<isRequest, Body, Fields>& m1,
|
|
message<isRequest, Body, Fields>& m2)
|
|
{
|
|
using std::swap;
|
|
swap(
|
|
static_cast<header<isRequest, Fields>&>(m1),
|
|
static_cast<header<isRequest, Fields>&>(m2));
|
|
swap(m1.body(), m2.body());
|
|
}
|
|
|
|
} // http
|
|
} // beast
|
|
} // boost
|
|
|
|
#endif
|