1 //
2 // Copyright (c) 2016-2019 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 // Official repository: https://github.com/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
11 #define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP
12
13 #include <boost/beast/websocket/impl/stream_impl.hpp>
14 #include <boost/beast/websocket/detail/type_traits.hpp>
15 #include <boost/beast/http/empty_body.hpp>
16 #include <boost/beast/http/message.hpp>
17 #include <boost/beast/http/read.hpp>
18 #include <boost/beast/http/write.hpp>
19 #include <boost/beast/core/async_base.hpp>
20 #include <boost/beast/core/flat_buffer.hpp>
21 #include <boost/beast/core/stream_traits.hpp>
22 #include <boost/asio/coroutine.hpp>
23 #include <boost/assert.hpp>
24 #include <boost/throw_exception.hpp>
25 #include <memory>
26
27 namespace boost {
28 namespace beast {
29 namespace websocket {
30
31 //------------------------------------------------------------------------------
32
33 // send the upgrade request and process the response
34 //
35 template<class NextLayer, bool deflateSupported>
36 template<class Handler>
37 class stream<NextLayer, deflateSupported>::handshake_op
38 : public beast::stable_async_base<Handler,
39 beast::executor_type<stream>>
40 , public asio::coroutine
41 {
42 struct data
43 {
44 // VFALCO This really should be two separate
45 // composed operations, to save on memory
46 request_type req;
47 http::response_parser<
48 typename response_type::body_type> p;
49 flat_buffer fb;
50 bool overflow = false; // could be a member of the op
51
52 explicit
databoost::beast::websocket::stream::handshake_op::data53 data(request_type&& req_)
54 : req(std::move(req_))
55 {
56 }
57 };
58
59 boost::weak_ptr<impl_type> wp_;
60 detail::sec_ws_key_type key_;
61 response_type* res_p_;
62 data& d_;
63
64 public:
65 template<class Handler_>
handshake_op(Handler_ && h,boost::shared_ptr<impl_type> const & sp,request_type && req,detail::sec_ws_key_type key,response_type * res_p)66 handshake_op(
67 Handler_&& h,
68 boost::shared_ptr<impl_type> const& sp,
69 request_type&& req,
70 detail::sec_ws_key_type key,
71 response_type* res_p)
72 : stable_async_base<Handler,
73 beast::executor_type<stream>>(
74 std::forward<Handler_>(h),
75 sp->stream().get_executor())
76 , wp_(sp)
77 , key_(key)
78 , res_p_(res_p)
79 , d_(beast::allocate_stable<data>(
80 *this, std::move(req)))
81 {
82 sp->reset(); // VFALCO I don't like this
83 (*this)({}, 0, false);
84 }
85
86 void
operator ()(error_code ec={},std::size_t bytes_used=0,bool cont=true)87 operator()(
88 error_code ec = {},
89 std::size_t bytes_used = 0,
90 bool cont = true)
91 {
92 boost::ignore_unused(bytes_used);
93 auto sp = wp_.lock();
94 if(! sp)
95 {
96 ec = net::error::operation_aborted;
97 return this->complete(cont, ec);
98 }
99 auto& impl = *sp;
100 BOOST_ASIO_CORO_REENTER(*this)
101 {
102 impl.change_status(status::handshake);
103 impl.update_timer(this->get_executor());
104
105 // write HTTP request
106 impl.do_pmd_config(d_.req);
107 BOOST_ASIO_CORO_YIELD
108 http::async_write(impl.stream(),
109 d_.req, std::move(*this));
110 if(impl.check_stop_now(ec))
111 goto upcall;
112
113 // read HTTP response
114 BOOST_ASIO_CORO_YIELD
115 http::async_read(impl.stream(),
116 impl.rd_buf, d_.p,
117 std::move(*this));
118 if(ec == http::error::buffer_overflow)
119 {
120 // If the response overflows the internal
121 // read buffer, switch to a dynamically
122 // allocated flat buffer.
123
124 d_.fb.commit(net::buffer_copy(
125 d_.fb.prepare(impl.rd_buf.size()),
126 impl.rd_buf.data()));
127 impl.rd_buf.clear();
128
129 BOOST_ASIO_CORO_YIELD
130 http::async_read(impl.stream(),
131 d_.fb, d_.p, std::move(*this));
132
133 if(! ec)
134 {
135 // Copy any leftovers back into the read
136 // buffer, since this represents websocket
137 // frame data.
138
139 if(d_.fb.size() <= impl.rd_buf.capacity())
140 {
141 impl.rd_buf.commit(net::buffer_copy(
142 impl.rd_buf.prepare(d_.fb.size()),
143 d_.fb.data()));
144 }
145 else
146 {
147 ec = http::error::buffer_overflow;
148 }
149 }
150
151 // Do this before the upcall
152 d_.fb.clear();
153 }
154 if(impl.check_stop_now(ec))
155 goto upcall;
156
157 // success
158 impl.reset_idle();
159 impl.on_response(d_.p.get(), key_, ec);
160 if(res_p_)
161 swap(d_.p.get(), *res_p_);
162
163 upcall:
164 this->complete(cont ,ec);
165 }
166 }
167 };
168
169 template<class NextLayer, bool deflateSupported>
170 struct stream<NextLayer, deflateSupported>::
171 run_handshake_op
172 {
173 template<class HandshakeHandler>
operator ()boost::beast::websocket::stream::run_handshake_op174 void operator()(
175 HandshakeHandler&& h,
176 boost::shared_ptr<impl_type> const& sp,
177 request_type&& req,
178 detail::sec_ws_key_type key,
179 response_type* res_p)
180 {
181 // If you get an error on the following line it means
182 // that your handler does not meet the documented type
183 // requirements for the handler.
184
185 static_assert(
186 beast::detail::is_invocable<HandshakeHandler,
187 void(error_code)>::value,
188 "HandshakeHandler type requirements not met");
189
190 handshake_op<
191 typename std::decay<HandshakeHandler>::type>(
192 std::forward<HandshakeHandler>(h),
193 sp, std::move(req), key, res_p);
194 }
195 };
196
197 //------------------------------------------------------------------------------
198
199 template<class NextLayer, bool deflateSupported>
200 template<class RequestDecorator>
201 void
202 stream<NextLayer, deflateSupported>::
do_handshake(response_type * res_p,string_view host,string_view target,RequestDecorator const & decorator,error_code & ec)203 do_handshake(
204 response_type* res_p,
205 string_view host,
206 string_view target,
207 RequestDecorator const& decorator,
208 error_code& ec)
209 {
210 auto& impl = *impl_;
211 impl.change_status(status::handshake);
212 impl.reset();
213 detail::sec_ws_key_type key;
214 {
215 auto const req = impl.build_request(
216 key, host, target, decorator);
217 impl.do_pmd_config(req);
218 http::write(impl.stream(), req, ec);
219 }
220 if(impl.check_stop_now(ec))
221 return;
222 http::response_parser<
223 typename response_type::body_type> p;
224 http::read(next_layer(), impl.rd_buf, p, ec);
225 if(ec == http::error::buffer_overflow)
226 {
227 // If the response overflows the internal
228 // read buffer, switch to a dynamically
229 // allocated flat buffer.
230
231 flat_buffer fb;
232 fb.commit(net::buffer_copy(
233 fb.prepare(impl.rd_buf.size()),
234 impl.rd_buf.data()));
235 impl.rd_buf.clear();
236
237 http::read(next_layer(), fb, p, ec);;
238
239 if(! ec)
240 {
241 // Copy any leftovers back into the read
242 // buffer, since this represents websocket
243 // frame data.
244
245 if(fb.size() <= impl.rd_buf.capacity())
246 {
247 impl.rd_buf.commit(net::buffer_copy(
248 impl.rd_buf.prepare(fb.size()),
249 fb.data()));
250 }
251 else
252 {
253 ec = http::error::buffer_overflow;
254 }
255 }
256 }
257 if(impl.check_stop_now(ec))
258 return;
259
260 impl.on_response(p.get(), key, ec);
261 if(impl.check_stop_now(ec))
262 return;
263
264 if(res_p)
265 *res_p = p.release();
266 }
267
268 //------------------------------------------------------------------------------
269
270 template<class NextLayer, bool deflateSupported>
271 template<BOOST_BEAST_ASYNC_TPARAM1 HandshakeHandler>
BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)272 BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
273 stream<NextLayer, deflateSupported>::
274 async_handshake(
275 string_view host,
276 string_view target,
277 HandshakeHandler&& handler)
278 {
279 static_assert(is_async_stream<next_layer_type>::value,
280 "AsyncStream type requirements not met");
281 detail::sec_ws_key_type key;
282 auto req = impl_->build_request(
283 key, host, target, &default_decorate_req);
284 return net::async_initiate<
285 HandshakeHandler,
286 void(error_code)>(
287 run_handshake_op{},
288 handler,
289 impl_,
290 std::move(req),
291 key,
292 nullptr);
293 }
294
295 template<class NextLayer, bool deflateSupported>
296 template<BOOST_BEAST_ASYNC_TPARAM1 HandshakeHandler>
BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)297 BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
298 stream<NextLayer, deflateSupported>::
299 async_handshake(
300 response_type& res,
301 string_view host,
302 string_view target,
303 HandshakeHandler&& handler)
304 {
305 static_assert(is_async_stream<next_layer_type>::value,
306 "AsyncStream type requirements not met");
307 detail::sec_ws_key_type key;
308 auto req = impl_->build_request(
309 key, host, target, &default_decorate_req);
310 return net::async_initiate<
311 HandshakeHandler,
312 void(error_code)>(
313 run_handshake_op{},
314 handler,
315 impl_,
316 std::move(req),
317 key,
318 &res);
319 }
320
321 template<class NextLayer, bool deflateSupported>
322 void
323 stream<NextLayer, deflateSupported>::
handshake(string_view host,string_view target)324 handshake(string_view host,
325 string_view target)
326 {
327 static_assert(is_sync_stream<next_layer_type>::value,
328 "SyncStream type requirements not met");
329 error_code ec;
330 handshake(
331 host, target, ec);
332 if(ec)
333 BOOST_THROW_EXCEPTION(system_error{ec});
334 }
335
336 template<class NextLayer, bool deflateSupported>
337 void
338 stream<NextLayer, deflateSupported>::
handshake(response_type & res,string_view host,string_view target)339 handshake(response_type& res,
340 string_view host,
341 string_view target)
342 {
343 static_assert(is_sync_stream<next_layer_type>::value,
344 "SyncStream type requirements not met");
345 error_code ec;
346 handshake(res, host, target, ec);
347 if(ec)
348 BOOST_THROW_EXCEPTION(system_error{ec});
349 }
350
351 template<class NextLayer, bool deflateSupported>
352 void
353 stream<NextLayer, deflateSupported>::
handshake(string_view host,string_view target,error_code & ec)354 handshake(string_view host,
355 string_view target, error_code& ec)
356 {
357 static_assert(is_sync_stream<next_layer_type>::value,
358 "SyncStream type requirements not met");
359 do_handshake(nullptr,
360 host, target, &default_decorate_req, ec);
361 }
362
363 template<class NextLayer, bool deflateSupported>
364 void
365 stream<NextLayer, deflateSupported>::
handshake(response_type & res,string_view host,string_view target,error_code & ec)366 handshake(response_type& res,
367 string_view host,
368 string_view target,
369 error_code& ec)
370 {
371 static_assert(is_sync_stream<next_layer_type>::value,
372 "SyncStream type requirements not met");
373 do_handshake(&res,
374 host, target, &default_decorate_req, ec);
375 }
376
377 } // websocket
378 } // beast
379 } // boost
380
381 #endif
382