2016-04-19 20:29:16 -04:00
|
|
|
//
|
2017-02-06 20:07:03 -05:00
|
|
|
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
2016-04-19 20:29:16 -04:00
|
|
|
//
|
|
|
|
|
// 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)
|
|
|
|
|
//
|
2016-03-11 06:40:37 -05:00
|
|
|
|
2016-10-09 06:34:35 -04:00
|
|
|
#ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
|
|
|
|
|
#define BEAST_HTTP_IMPL_MESSAGE_IPP
|
2016-03-11 06:40:37 -05:00
|
|
|
|
2016-06-20 09:40:35 -04:00
|
|
|
#include <beast/core/error.hpp>
|
2017-05-07 16:11:12 -07:00
|
|
|
#include <beast/core/string_view.hpp>
|
2017-05-28 09:05:29 -07:00
|
|
|
#include <beast/http/type_traits.hpp>
|
2016-05-24 06:17:04 -04:00
|
|
|
#include <beast/http/rfc7230.hpp>
|
2016-09-30 08:44:49 -04:00
|
|
|
#include <beast/core/detail/ci_char_traits.hpp>
|
2016-10-13 12:32:01 +10:00
|
|
|
#include <beast/core/detail/type_traits.hpp>
|
2016-10-09 06:34:35 -04:00
|
|
|
#include <boost/assert.hpp>
|
2016-04-29 06:04:40 -04:00
|
|
|
#include <boost/optional.hpp>
|
2017-05-22 15:30:12 -07:00
|
|
|
#include <boost/throw_exception.hpp>
|
2016-04-29 06:04:40 -04:00
|
|
|
#include <stdexcept>
|
2016-03-11 06:40:37 -05:00
|
|
|
|
|
|
|
|
namespace beast {
|
|
|
|
|
namespace http {
|
|
|
|
|
|
2017-06-04 10:03:36 -07:00
|
|
|
template<class Fields>
|
|
|
|
|
template<class>
|
|
|
|
|
string_view
|
|
|
|
|
header<true, Fields>::
|
|
|
|
|
get_method_string() const
|
|
|
|
|
{
|
|
|
|
|
if(method_ != verb::unknown)
|
|
|
|
|
return to_string(method_);
|
2017-06-04 12:38:42 -07:00
|
|
|
return fields.method_impl();
|
2017-06-04 10:03:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Fields>
|
|
|
|
|
template<class>
|
|
|
|
|
void
|
|
|
|
|
header<true, Fields>::
|
|
|
|
|
set_method(verb v)
|
|
|
|
|
{
|
|
|
|
|
if(v == verb::unknown)
|
|
|
|
|
BOOST_THROW_EXCEPTION(
|
|
|
|
|
std::invalid_argument{"unknown verb"});
|
|
|
|
|
method_ = v;
|
2017-06-04 12:38:42 -07:00
|
|
|
fields.method_impl({});
|
2017-06-04 10:03:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Fields>
|
|
|
|
|
template<class>
|
|
|
|
|
void
|
|
|
|
|
header<true, Fields>::
|
|
|
|
|
set_method(string_view s)
|
|
|
|
|
{
|
|
|
|
|
method_ = string_to_verb(s);
|
|
|
|
|
if(method_ != verb::unknown)
|
2017-06-04 12:38:42 -07:00
|
|
|
fields.method_impl({});
|
2017-06-04 10:03:36 -07:00
|
|
|
else
|
2017-06-04 12:38:42 -07:00
|
|
|
fields.method_impl(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class Fields>
|
|
|
|
|
template<class>
|
|
|
|
|
string_view
|
|
|
|
|
header<false, Fields>::
|
|
|
|
|
get_reason() const
|
|
|
|
|
{
|
|
|
|
|
auto const s = fields.reason_impl();
|
|
|
|
|
if(! s.empty())
|
|
|
|
|
return s;
|
|
|
|
|
return obsolete_reason(result_);
|
2017-06-04 10:03:36 -07:00
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<class Fields>
|
2016-11-07 12:20:39 -05:00
|
|
|
void
|
|
|
|
|
swap(
|
2016-11-10 05:34:49 -05:00
|
|
|
header<true, Fields>& m1,
|
|
|
|
|
header<true, Fields>& m2)
|
2016-11-07 12:20:39 -05:00
|
|
|
{
|
|
|
|
|
using std::swap;
|
|
|
|
|
swap(m1.version, m2.version);
|
2016-11-10 05:34:49 -05:00
|
|
|
swap(m1.fields, m2.fields);
|
2017-06-04 10:03:36 -07:00
|
|
|
swap(m1.method_, m2.method_);
|
2016-11-07 12:20:39 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<class Fields>
|
2016-11-07 12:20:39 -05:00
|
|
|
void
|
|
|
|
|
swap(
|
2016-11-10 05:34:49 -05:00
|
|
|
header<false, Fields>& a,
|
|
|
|
|
header<false, Fields>& b)
|
2016-11-07 12:20:39 -05:00
|
|
|
{
|
|
|
|
|
using std::swap;
|
|
|
|
|
swap(a.version, b.version);
|
2016-11-10 05:34:49 -05:00
|
|
|
swap(a.fields, b.fields);
|
2017-06-04 12:38:42 -07:00
|
|
|
swap(a.result_, b.result_);
|
2016-11-07 12:20:39 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Body, class Fields>
|
2016-11-07 12:20:39 -05:00
|
|
|
void
|
|
|
|
|
swap(
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields>& m1,
|
|
|
|
|
message<isRequest, Body, Fields>& m2)
|
2016-11-07 12:20:39 -05:00
|
|
|
{
|
|
|
|
|
using std::swap;
|
|
|
|
|
swap(m1.base(), m2.base());
|
|
|
|
|
swap(m1.body, m2.body);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Fields>
|
2016-03-11 06:40:37 -05:00
|
|
|
bool
|
2016-11-10 05:34:49 -05:00
|
|
|
is_keep_alive(header<isRequest, Fields> const& msg)
|
2016-03-11 06:40:37 -05:00
|
|
|
{
|
2016-10-09 06:34:35 -04:00
|
|
|
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
|
|
|
|
if(msg.version == 11)
|
2016-03-11 06:40:37 -05:00
|
|
|
{
|
2016-11-10 05:34:49 -05:00
|
|
|
if(token_list{msg.fields["Connection"]}.exists("close"))
|
2016-03-11 06:40:37 -05:00
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-11-10 05:34:49 -05:00
|
|
|
if(token_list{msg.fields["Connection"]}.exists("keep-alive"))
|
2016-03-11 06:40:37 -05:00
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Fields>
|
2016-03-11 06:40:37 -05:00
|
|
|
bool
|
2016-11-10 05:34:49 -05:00
|
|
|
is_upgrade(header<isRequest, Fields> const& msg)
|
2016-03-11 06:40:37 -05:00
|
|
|
{
|
2016-10-09 06:34:35 -04:00
|
|
|
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
|
|
|
|
if(msg.version == 10)
|
2016-03-11 06:40:37 -05:00
|
|
|
return false;
|
2016-11-10 05:34:49 -05:00
|
|
|
if(token_list{msg.fields["Connection"]}.exists("upgrade"))
|
2016-03-11 06:40:37 -05:00
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-29 06:04:40 -04:00
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
|
|
struct prepare_info
|
|
|
|
|
{
|
|
|
|
|
boost::optional<connection> connection_value;
|
|
|
|
|
boost::optional<std::uint64_t> content_length;
|
|
|
|
|
};
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Body, class Fields>
|
2016-04-29 06:04:40 -04:00
|
|
|
inline
|
|
|
|
|
void
|
|
|
|
|
prepare_options(prepare_info& pi,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields>& msg)
|
2016-04-29 06:04:40 -04:00
|
|
|
{
|
2016-10-13 12:32:01 +10:00
|
|
|
beast::detail::ignore_unused(pi, msg);
|
2016-04-29 06:04:40 -04:00
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Body, class Fields>
|
2016-04-29 06:04:40 -04:00
|
|
|
void
|
|
|
|
|
prepare_option(prepare_info& pi,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields>& msg,
|
2016-04-29 06:04:40 -04:00
|
|
|
connection value)
|
|
|
|
|
{
|
2016-10-13 12:32:01 +10:00
|
|
|
beast::detail::ignore_unused(msg);
|
2016-04-29 06:04:40 -04:00
|
|
|
pi.connection_value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields,
|
2016-04-29 06:04:40 -04:00
|
|
|
class Opt, class... Opts>
|
|
|
|
|
void
|
|
|
|
|
prepare_options(prepare_info& pi,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields>& msg,
|
2016-04-29 06:04:40 -04:00
|
|
|
Opt&& opt, Opts&&... opts)
|
|
|
|
|
{
|
|
|
|
|
prepare_option(pi, msg, opt);
|
|
|
|
|
prepare_options(pi, msg,
|
|
|
|
|
std::forward<Opts>(opts)...);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Body, class Fields>
|
2016-04-29 06:04:40 -04:00
|
|
|
void
|
|
|
|
|
prepare_content_length(prepare_info& pi,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> const& msg,
|
2016-04-29 06:04:40 -04:00
|
|
|
std::true_type)
|
|
|
|
|
{
|
2017-05-28 09:05:29 -07:00
|
|
|
typename Body::reader w{msg};
|
2016-06-20 09:40:35 -04:00
|
|
|
// VFALCO This is a design problem!
|
|
|
|
|
error_code ec;
|
|
|
|
|
w.init(ec);
|
|
|
|
|
if(ec)
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(system_error{ec});
|
2016-04-29 06:04:40 -04:00
|
|
|
pi.content_length = w.content_length();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
template<bool isRequest, class Body, class Fields>
|
2016-04-29 06:04:40 -04:00
|
|
|
void
|
|
|
|
|
prepare_content_length(prepare_info& pi,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> const& msg,
|
2016-04-29 06:04:40 -04:00
|
|
|
std::false_type)
|
|
|
|
|
{
|
2016-10-13 12:32:01 +10:00
|
|
|
beast::detail::ignore_unused(msg);
|
2016-04-29 06:04:40 -04:00
|
|
|
pi.content_length = boost::none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // detail
|
|
|
|
|
|
|
|
|
|
template<
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields,
|
2016-04-29 06:04:40 -04:00
|
|
|
class... Options>
|
|
|
|
|
void
|
2016-11-10 05:34:49 -05:00
|
|
|
prepare(message<isRequest, Body, Fields>& msg,
|
2016-04-29 06:04:40 -04:00
|
|
|
Options&&... options)
|
|
|
|
|
{
|
|
|
|
|
// VFALCO TODO
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body<Body>::value,
|
2016-10-15 13:40:17 -04:00
|
|
|
"Body requirements not met");
|
2017-05-28 09:05:29 -07:00
|
|
|
static_assert(is_body_reader<Body>::value,
|
|
|
|
|
"BodyReader requirements not met");
|
2016-04-29 06:04:40 -04:00
|
|
|
detail::prepare_info pi;
|
|
|
|
|
detail::prepare_content_length(pi, msg,
|
2017-05-28 09:05:29 -07:00
|
|
|
detail::has_content_length<typename Body::reader>{});
|
2016-04-29 06:04:40 -04:00
|
|
|
detail::prepare_options(pi, msg,
|
|
|
|
|
std::forward<Options>(options)...);
|
|
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
if(msg.fields.exists("Connection"))
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
|
|
|
"prepare called with Connection field set"});
|
2016-04-29 06:04:40 -04:00
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
if(msg.fields.exists("Content-Length"))
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
|
|
|
"prepare called with Content-Length field set"});
|
2016-04-29 06:04:40 -04:00
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
if(token_list{msg.fields["Transfer-Encoding"]}.exists("chunked"))
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
|
|
|
"prepare called with Transfer-Encoding: chunked set"});
|
2016-04-29 06:04:40 -04:00
|
|
|
|
|
|
|
|
if(pi.connection_value != connection::upgrade)
|
|
|
|
|
{
|
|
|
|
|
if(pi.content_length)
|
|
|
|
|
{
|
2016-09-30 08:44:49 -04:00
|
|
|
struct set_field
|
|
|
|
|
{
|
|
|
|
|
void
|
2016-11-10 05:34:49 -05:00
|
|
|
operator()(message<true, Body, Fields>& msg,
|
2016-09-30 08:44:49 -04:00
|
|
|
detail::prepare_info const& pi) const
|
|
|
|
|
{
|
|
|
|
|
using beast::detail::ci_equal;
|
|
|
|
|
if(*pi.content_length > 0 ||
|
2017-06-04 10:03:36 -07:00
|
|
|
msg.method() == verb::post)
|
2016-09-30 08:44:49 -04:00
|
|
|
{
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.insert(
|
2016-11-09 10:40:09 -05:00
|
|
|
"Content-Length", *pi.content_length);
|
2016-09-30 08:44:49 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2016-11-10 05:34:49 -05:00
|
|
|
operator()(message<false, Body, Fields>& msg,
|
2016-09-30 08:44:49 -04:00
|
|
|
detail::prepare_info const& pi) const
|
|
|
|
|
{
|
2017-06-04 12:38:42 -07:00
|
|
|
if(to_status_class(msg.result()) != status_class::informational &&
|
|
|
|
|
msg.result() != status::no_content &&
|
|
|
|
|
msg.result() != status::not_modified)
|
2016-09-30 08:44:49 -04:00
|
|
|
{
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.insert(
|
2016-11-09 10:40:09 -05:00
|
|
|
"Content-Length", *pi.content_length);
|
2016-09-30 08:44:49 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
set_field{}(msg, pi);
|
2016-04-29 06:04:40 -04:00
|
|
|
}
|
|
|
|
|
else if(msg.version >= 11)
|
|
|
|
|
{
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.insert("Transfer-Encoding", "chunked");
|
2016-04-29 06:04:40 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto const content_length =
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.exists("Content-Length");
|
2016-04-29 06:04:40 -04:00
|
|
|
|
|
|
|
|
if(pi.connection_value)
|
|
|
|
|
{
|
|
|
|
|
switch(*pi.connection_value)
|
|
|
|
|
{
|
|
|
|
|
case connection::upgrade:
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.insert("Connection", "upgrade");
|
2016-04-29 06:04:40 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case connection::keep_alive:
|
|
|
|
|
if(msg.version < 11)
|
|
|
|
|
{
|
|
|
|
|
if(content_length)
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.insert("Connection", "keep-alive");
|
2016-04-29 06:04:40 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case connection::close:
|
|
|
|
|
if(msg.version >= 11)
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields.insert("Connection", "close");
|
2016-04-29 06:04:40 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// rfc7230 6.7.
|
2016-05-24 06:17:04 -04:00
|
|
|
if(msg.version < 11 && token_list{
|
2016-11-10 05:34:49 -05:00
|
|
|
msg.fields["Connection"]}.exists("upgrade"))
|
2017-05-22 15:30:12 -07:00
|
|
|
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
|
|
|
|
"invalid version for Connection: upgrade"});
|
2016-04-29 06:04:40 -04:00
|
|
|
}
|
|
|
|
|
|
2016-03-11 06:40:37 -05:00
|
|
|
} // http
|
|
|
|
|
} // beast
|
|
|
|
|
|
|
|
|
|
#endif
|