Files
xahaud/include/beast/http/impl/message.ipp
Vinnie Falco 7028579170 Squashed 'src/beast/' changes from 1ab7a2f..c00cd37
c00cd37 Set version to 1.0.0-b23
f662e36 Travis CI improvements:
b05fa33 Fix message constructor and special members
b4722cc Add copy special members
420d1c7 Better logging in async echo server
149e3a2 Add file and line number to thrown exceptions
3e88b83 Tune websocket echo server for performance

git-subtree-dir: src/beast
git-subtree-split: c00cd37b8a441a92755658014fdde97d515ec7ed
2017-01-17 14:50:38 -05:00

267 lines
6.9 KiB
C++

//
// Copyright (c) 2013-2016 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_IMPL_MESSAGE_IPP
#define BEAST_HTTP_IMPL_MESSAGE_IPP
#include <beast/core/error.hpp>
#include <beast/http/concepts.hpp>
#include <beast/http/rfc7230.hpp>
#include <beast/core/detail/ci_char_traits.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/assert.hpp>
#include <boost/optional.hpp>
#include <stdexcept>
namespace beast {
namespace http {
template<class Fields>
void
swap(
header<true, Fields>& m1,
header<true, Fields>& m2)
{
using std::swap;
swap(m1.version, m2.version);
swap(m1.method, m2.method);
swap(m1.url, m2.url);
swap(m1.fields, m2.fields);
}
template<class Fields>
void
swap(
header<false, Fields>& a,
header<false, Fields>& b)
{
using std::swap;
swap(a.version, b.version);
swap(a.status, b.status);
swap(a.reason, b.reason);
swap(a.fields, b.fields);
}
template<bool isRequest, class Body, class Fields>
void
swap(
message<isRequest, Body, Fields>& m1,
message<isRequest, Body, Fields>& m2)
{
using std::swap;
swap(m1.base(), m2.base());
swap(m1.body, m2.body);
}
template<bool isRequest, class Fields>
bool
is_keep_alive(header<isRequest, Fields> const& msg)
{
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
if(msg.version == 11)
{
if(token_list{msg.fields["Connection"]}.exists("close"))
return false;
return true;
}
if(token_list{msg.fields["Connection"]}.exists("keep-alive"))
return true;
return false;
}
template<bool isRequest, class Fields>
bool
is_upgrade(header<isRequest, Fields> const& msg)
{
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
if(msg.version == 10)
return false;
if(token_list{msg.fields["Connection"]}.exists("upgrade"))
return true;
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::writer w(msg);
// VFALCO This is a design problem!
error_code ec;
w.init(ec);
if(ec)
throw 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)
{
using beast::detail::make_exception;
// VFALCO TODO
static_assert(is_Body<Body>::value,
"Body requirements not met");
static_assert(has_writer<Body>::value,
"Body has no writer");
static_assert(is_Writer<typename Body::writer,
message<isRequest, Body, Fields>>::value,
"Writer requirements not met");
detail::prepare_info pi;
detail::prepare_content_length(pi, msg,
detail::has_content_length<typename Body::writer>{});
detail::prepare_options(pi, msg,
std::forward<Options>(options)...);
if(msg.fields.exists("Connection"))
throw make_exception<std::invalid_argument>(
"prepare called with Connection field set", __FILE__, __LINE__);
if(msg.fields.exists("Content-Length"))
throw make_exception<std::invalid_argument>(
"prepare called with Content-Length field set", __FILE__, __LINE__);
if(token_list{msg.fields["Transfer-Encoding"]}.exists("chunked"))
throw make_exception<std::invalid_argument>(
"prepare called with Transfer-Encoding: chunked set", __FILE__, __LINE__);
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 ||
ci_equal(msg.method, "POST"))
{
msg.fields.insert(
"Content-Length", *pi.content_length);
}
}
void
operator()(message<false, Body, Fields>& msg,
detail::prepare_info const& pi) const
{
if((msg.status / 100 ) != 1 &&
msg.status != 204 &&
msg.status != 304)
{
msg.fields.insert(
"Content-Length", *pi.content_length);
}
}
};
set_field{}(msg, pi);
}
else if(msg.version >= 11)
{
msg.fields.insert("Transfer-Encoding", "chunked");
}
}
auto const content_length =
msg.fields.exists("Content-Length");
if(pi.connection_value)
{
switch(*pi.connection_value)
{
case connection::upgrade:
msg.fields.insert("Connection", "upgrade");
break;
case connection::keep_alive:
if(msg.version < 11)
{
if(content_length)
msg.fields.insert("Connection", "keep-alive");
}
break;
case connection::close:
if(msg.version >= 11)
msg.fields.insert("Connection", "close");
break;
}
}
// rfc7230 6.7.
if(msg.version < 11 && token_list{
msg.fields["Connection"]}.exists("upgrade"))
throw make_exception<std::invalid_argument>(
"invalid version for Connection: upgrade", __FILE__, __LINE__);
}
} // http
} // beast
#endif