2 // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 #ifndef BEAST_HTTP_IMPL_MESSAGE_IPP
9 #define BEAST_HTTP_IMPL_MESSAGE_IPP
11 #include <beast/core/error.hpp>
12 #include <beast/http/concepts.hpp>
13 #include <beast/http/rfc7230.hpp>
14 #include <beast/core/detail/ci_char_traits.hpp>
15 #include <beast/core/detail/type_traits.hpp>
16 #include <boost/assert.hpp>
17 #include <boost/optional.hpp>
23 template<class Fields>
26 header<true, Fields>& m1,
27 header<true, Fields>& m2)
30 swap(m1.version, m2.version);
31 swap(m1.method, m2.method);
33 swap(m1.fields, m2.fields);
36 template<class Fields>
39 header<false, Fields>& a,
40 header<false, Fields>& b)
43 swap(a.version, b.version);
44 swap(a.status, b.status);
45 swap(a.reason, b.reason);
46 swap(a.fields, b.fields);
49 template<bool isRequest, class Body, class Fields>
52 message<isRequest, Body, Fields>& m1,
53 message<isRequest, Body, Fields>& m2)
56 swap(m1.base(), m2.base());
57 swap(m1.body, m2.body);
60 template<bool isRequest, class Fields>
62 is_keep_alive(header<isRequest, Fields> const& msg)
64 BOOST_ASSERT(msg.version == 10 || msg.version == 11);
67 if(token_list{msg.fields["Connection"]}.exists("close"))
71 if(token_list{msg.fields["Connection"]}.exists("keep-alive"))
76 template<bool isRequest, class Fields>
78 is_upgrade(header<isRequest, Fields> const& msg)
80 BOOST_ASSERT(msg.version == 10 || msg.version == 11);
83 if(token_list{msg.fields["Connection"]}.exists("upgrade"))
92 boost::optional<connection> connection_value;
93 boost::optional<std::uint64_t> content_length;
96 template<bool isRequest, class Body, class Fields>
99 prepare_options(prepare_info& pi,
100 message<isRequest, Body, Fields>& msg)
102 beast::detail::ignore_unused(pi, msg);
105 template<bool isRequest, class Body, class Fields>
107 prepare_option(prepare_info& pi,
108 message<isRequest, Body, Fields>& msg,
111 beast::detail::ignore_unused(msg);
112 pi.connection_value = value;
116 bool isRequest, class Body, class Fields,
117 class Opt, class... Opts>
119 prepare_options(prepare_info& pi,
120 message<isRequest, Body, Fields>& msg,
121 Opt&& opt, Opts&&... opts)
123 prepare_option(pi, msg, opt);
124 prepare_options(pi, msg,
125 std::forward<Opts>(opts)...);
128 template<bool isRequest, class Body, class Fields>
130 prepare_content_length(prepare_info& pi,
131 message<isRequest, Body, Fields> const& msg,
134 typename Body::writer w(msg);
135 // VFALCO This is a design problem!
139 throw system_error{ec};
140 pi.content_length = w.content_length();
143 template<bool isRequest, class Body, class Fields>
145 prepare_content_length(prepare_info& pi,
146 message<isRequest, Body, Fields> const& msg,
149 beast::detail::ignore_unused(msg);
150 pi.content_length = boost::none;
156 bool isRequest, class Body, class Fields,
159 prepare(message<isRequest, Body, Fields>& msg,
160 Options&&... options)
162 using beast::detail::make_exception;
165 static_assert(is_Body<Body>::value,
166 "Body requirements not met");
167 static_assert(has_writer<Body>::value,
168 "Body has no writer");
169 static_assert(is_Writer<typename Body::writer,
170 message<isRequest, Body, Fields>>::value,
171 "Writer requirements not met");
172 detail::prepare_info pi;
173 detail::prepare_content_length(pi, msg,
174 detail::has_content_length<typename Body::writer>{});
175 detail::prepare_options(pi, msg,
176 std::forward<Options>(options)...);
178 if(msg.fields.exists("Connection"))
179 throw make_exception<std::invalid_argument>(
180 "prepare called with Connection field set", __FILE__, __LINE__);
182 if(msg.fields.exists("Content-Length"))
183 throw make_exception<std::invalid_argument>(
184 "prepare called with Content-Length field set", __FILE__, __LINE__);
186 if(token_list{msg.fields["Transfer-Encoding"]}.exists("chunked"))
187 throw make_exception<std::invalid_argument>(
188 "prepare called with Transfer-Encoding: chunked set", __FILE__, __LINE__);
190 if(pi.connection_value != connection::upgrade)
192 if(pi.content_length)
197 operator()(message<true, Body, Fields>& msg,
198 detail::prepare_info const& pi) const
200 using beast::detail::ci_equal;
201 if(*pi.content_length > 0 ||
202 ci_equal(msg.method, "POST"))
205 "Content-Length", *pi.content_length);
210 operator()(message<false, Body, Fields>& msg,
211 detail::prepare_info const& pi) const
213 if((msg.status / 100 ) != 1 &&
218 "Content-Length", *pi.content_length);
222 set_field{}(msg, pi);
224 else if(msg.version >= 11)
226 msg.fields.insert("Transfer-Encoding", "chunked");
230 auto const content_length =
231 msg.fields.exists("Content-Length");
233 if(pi.connection_value)
235 switch(*pi.connection_value)
237 case connection::upgrade:
238 msg.fields.insert("Connection", "upgrade");
241 case connection::keep_alive:
245 msg.fields.insert("Connection", "keep-alive");
249 case connection::close:
250 if(msg.version >= 11)
251 msg.fields.insert("Connection", "close");
257 if(msg.version < 11 && token_list{
258 msg.fields["Connection"]}.exists("upgrade"))
259 throw make_exception<std::invalid_argument>(
260 "invalid version for Connection: upgrade", __FILE__, __LINE__);