]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) | |
3 | // | |
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) | |
6 | // | |
7 | ||
8 | #ifndef BEAST_HTTP_IMPL_MESSAGE_IPP | |
9 | #define BEAST_HTTP_IMPL_MESSAGE_IPP | |
10 | ||
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> | |
18 | #include <stdexcept> | |
19 | ||
20 | namespace beast { | |
21 | namespace http { | |
22 | ||
23 | template<class Fields> | |
24 | void | |
25 | swap( | |
26 | header<true, Fields>& m1, | |
27 | header<true, Fields>& m2) | |
28 | { | |
29 | using std::swap; | |
30 | swap(m1.version, m2.version); | |
31 | swap(m1.method, m2.method); | |
32 | swap(m1.url, m2.url); | |
33 | swap(m1.fields, m2.fields); | |
34 | } | |
35 | ||
36 | template<class Fields> | |
37 | void | |
38 | swap( | |
39 | header<false, Fields>& a, | |
40 | header<false, Fields>& b) | |
41 | { | |
42 | using std::swap; | |
43 | swap(a.version, b.version); | |
44 | swap(a.status, b.status); | |
45 | swap(a.reason, b.reason); | |
46 | swap(a.fields, b.fields); | |
47 | } | |
48 | ||
49 | template<bool isRequest, class Body, class Fields> | |
50 | void | |
51 | swap( | |
52 | message<isRequest, Body, Fields>& m1, | |
53 | message<isRequest, Body, Fields>& m2) | |
54 | { | |
55 | using std::swap; | |
56 | swap(m1.base(), m2.base()); | |
57 | swap(m1.body, m2.body); | |
58 | } | |
59 | ||
60 | template<bool isRequest, class Fields> | |
61 | bool | |
62 | is_keep_alive(header<isRequest, Fields> const& msg) | |
63 | { | |
64 | BOOST_ASSERT(msg.version == 10 || msg.version == 11); | |
65 | if(msg.version == 11) | |
66 | { | |
67 | if(token_list{msg.fields["Connection"]}.exists("close")) | |
68 | return false; | |
69 | return true; | |
70 | } | |
71 | if(token_list{msg.fields["Connection"]}.exists("keep-alive")) | |
72 | return true; | |
73 | return false; | |
74 | } | |
75 | ||
76 | template<bool isRequest, class Fields> | |
77 | bool | |
78 | is_upgrade(header<isRequest, Fields> const& msg) | |
79 | { | |
80 | BOOST_ASSERT(msg.version == 10 || msg.version == 11); | |
81 | if(msg.version == 10) | |
82 | return false; | |
83 | if(token_list{msg.fields["Connection"]}.exists("upgrade")) | |
84 | return true; | |
85 | return false; | |
86 | } | |
87 | ||
88 | namespace detail { | |
89 | ||
90 | struct prepare_info | |
91 | { | |
92 | boost::optional<connection> connection_value; | |
93 | boost::optional<std::uint64_t> content_length; | |
94 | }; | |
95 | ||
96 | template<bool isRequest, class Body, class Fields> | |
97 | inline | |
98 | void | |
99 | prepare_options(prepare_info& pi, | |
100 | message<isRequest, Body, Fields>& msg) | |
101 | { | |
102 | beast::detail::ignore_unused(pi, msg); | |
103 | } | |
104 | ||
105 | template<bool isRequest, class Body, class Fields> | |
106 | void | |
107 | prepare_option(prepare_info& pi, | |
108 | message<isRequest, Body, Fields>& msg, | |
109 | connection value) | |
110 | { | |
111 | beast::detail::ignore_unused(msg); | |
112 | pi.connection_value = value; | |
113 | } | |
114 | ||
115 | template< | |
116 | bool isRequest, class Body, class Fields, | |
117 | class Opt, class... Opts> | |
118 | void | |
119 | prepare_options(prepare_info& pi, | |
120 | message<isRequest, Body, Fields>& msg, | |
121 | Opt&& opt, Opts&&... opts) | |
122 | { | |
123 | prepare_option(pi, msg, opt); | |
124 | prepare_options(pi, msg, | |
125 | std::forward<Opts>(opts)...); | |
126 | } | |
127 | ||
128 | template<bool isRequest, class Body, class Fields> | |
129 | void | |
130 | prepare_content_length(prepare_info& pi, | |
131 | message<isRequest, Body, Fields> const& msg, | |
132 | std::true_type) | |
133 | { | |
134 | typename Body::writer w(msg); | |
135 | // VFALCO This is a design problem! | |
136 | error_code ec; | |
137 | w.init(ec); | |
138 | if(ec) | |
139 | throw system_error{ec}; | |
140 | pi.content_length = w.content_length(); | |
141 | } | |
142 | ||
143 | template<bool isRequest, class Body, class Fields> | |
144 | void | |
145 | prepare_content_length(prepare_info& pi, | |
146 | message<isRequest, Body, Fields> const& msg, | |
147 | std::false_type) | |
148 | { | |
149 | beast::detail::ignore_unused(msg); | |
150 | pi.content_length = boost::none; | |
151 | } | |
152 | ||
153 | } // detail | |
154 | ||
155 | template< | |
156 | bool isRequest, class Body, class Fields, | |
157 | class... Options> | |
158 | void | |
159 | prepare(message<isRequest, Body, Fields>& msg, | |
160 | Options&&... options) | |
161 | { | |
162 | using beast::detail::make_exception; | |
163 | ||
164 | // VFALCO TODO | |
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)...); | |
177 | ||
178 | if(msg.fields.exists("Connection")) | |
179 | throw make_exception<std::invalid_argument>( | |
180 | "prepare called with Connection field set", __FILE__, __LINE__); | |
181 | ||
182 | if(msg.fields.exists("Content-Length")) | |
183 | throw make_exception<std::invalid_argument>( | |
184 | "prepare called with Content-Length field set", __FILE__, __LINE__); | |
185 | ||
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__); | |
189 | ||
190 | if(pi.connection_value != connection::upgrade) | |
191 | { | |
192 | if(pi.content_length) | |
193 | { | |
194 | struct set_field | |
195 | { | |
196 | void | |
197 | operator()(message<true, Body, Fields>& msg, | |
198 | detail::prepare_info const& pi) const | |
199 | { | |
200 | using beast::detail::ci_equal; | |
201 | if(*pi.content_length > 0 || | |
202 | ci_equal(msg.method, "POST")) | |
203 | { | |
204 | msg.fields.insert( | |
205 | "Content-Length", *pi.content_length); | |
206 | } | |
207 | } | |
208 | ||
209 | void | |
210 | operator()(message<false, Body, Fields>& msg, | |
211 | detail::prepare_info const& pi) const | |
212 | { | |
213 | if((msg.status / 100 ) != 1 && | |
214 | msg.status != 204 && | |
215 | msg.status != 304) | |
216 | { | |
217 | msg.fields.insert( | |
218 | "Content-Length", *pi.content_length); | |
219 | } | |
220 | } | |
221 | }; | |
222 | set_field{}(msg, pi); | |
223 | } | |
224 | else if(msg.version >= 11) | |
225 | { | |
226 | msg.fields.insert("Transfer-Encoding", "chunked"); | |
227 | } | |
228 | } | |
229 | ||
230 | auto const content_length = | |
231 | msg.fields.exists("Content-Length"); | |
232 | ||
233 | if(pi.connection_value) | |
234 | { | |
235 | switch(*pi.connection_value) | |
236 | { | |
237 | case connection::upgrade: | |
238 | msg.fields.insert("Connection", "upgrade"); | |
239 | break; | |
240 | ||
241 | case connection::keep_alive: | |
242 | if(msg.version < 11) | |
243 | { | |
244 | if(content_length) | |
245 | msg.fields.insert("Connection", "keep-alive"); | |
246 | } | |
247 | break; | |
248 | ||
249 | case connection::close: | |
250 | if(msg.version >= 11) | |
251 | msg.fields.insert("Connection", "close"); | |
252 | break; | |
253 | } | |
254 | } | |
255 | ||
256 | // rfc7230 6.7. | |
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__); | |
261 | } | |
262 | ||
263 | } // http | |
264 | } // beast | |
265 | ||
266 | #endif |