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
27namespace boost {
28namespace beast {
29namespace websocket {
30
31//------------------------------------------------------------------------------
32
33// send the upgrade request and process the response
34//
35template<class NextLayer, bool deflateSupported>
36template<class Handler>
37class 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
53 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
64public:
65 template<class Handler_>
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
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 BOOST_BEAST_ASSIGN_EC(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 {
109 BOOST_ASIO_HANDLER_LOCATION((
110 __FILE__, __LINE__,
111 "websocket::async_handshake"));
112
113 http::async_write(impl.stream(),
114 d_.req, std::move(*this));
115 }
116 if(impl.check_stop_now(ec))
117 goto upcall;
118
119 // read HTTP response
120 BOOST_ASIO_CORO_YIELD
121 {
122 BOOST_ASIO_HANDLER_LOCATION((
123 __FILE__, __LINE__,
124 "websocket::async_handshake"));
125
126 http::async_read(impl.stream(),
127 impl.rd_buf, d_.p,
128 std::move(*this));
129 }
130 if(ec == http::error::buffer_overflow)
131 {
132 // If the response overflows the internal
133 // read buffer, switch to a dynamically
134 // allocated flat buffer.
135
136 d_.fb.commit(net::buffer_copy(
137 d_.fb.prepare(impl.rd_buf.size()),
138 impl.rd_buf.data()));
139 impl.rd_buf.clear();
140
141 BOOST_ASIO_CORO_YIELD
142 {
143 BOOST_ASIO_HANDLER_LOCATION((
144 __FILE__, __LINE__,
145 "websocket::async_handshake"));
146
147 http::async_read(impl.stream(),
148 d_.fb, d_.p, std::move(*this));
149 }
150
151 if(! ec)
152 {
153 // Copy any leftovers back into the read
154 // buffer, since this represents websocket
155 // frame data.
156
157 if(d_.fb.size() <= impl.rd_buf.capacity())
158 {
159 impl.rd_buf.commit(net::buffer_copy(
160 impl.rd_buf.prepare(d_.fb.size()),
161 d_.fb.data()));
162 }
163 else
164 {
165 BOOST_BEAST_ASSIGN_EC(ec, http::error::buffer_overflow);
166 }
167 }
168
169 // Do this before the upcall
170 d_.fb.clear();
171 }
172 if(impl.check_stop_now(ec))
173 goto upcall;
174
175 // success
176 impl.reset_idle();
177 impl.on_response(d_.p.get(), key_, ec);
178 if(res_p_)
179 swap(d_.p.get(), *res_p_);
180
181 upcall:
182 this->complete(cont ,ec);
183 }
184 }
185};
186
187template<class NextLayer, bool deflateSupported>
188struct stream<NextLayer, deflateSupported>::
189 run_handshake_op
190{
191 template<class HandshakeHandler>
192 void operator()(
193 HandshakeHandler&& h,
194 boost::shared_ptr<impl_type> const& sp,
195 request_type&& req,
196 detail::sec_ws_key_type key,
197 response_type* res_p)
198 {
199 // If you get an error on the following line it means
200 // that your handler does not meet the documented type
201 // requirements for the handler.
202
203 static_assert(
204 beast::detail::is_invocable<HandshakeHandler,
205 void(error_code)>::value,
206 "HandshakeHandler type requirements not met");
207
208 handshake_op<
209 typename std::decay<HandshakeHandler>::type>(
210 std::forward<HandshakeHandler>(h),
211 sp, std::move(req), key, res_p);
212 }
213};
214
215//------------------------------------------------------------------------------
216
217template<class NextLayer, bool deflateSupported>
218template<class RequestDecorator>
219void
220stream<NextLayer, deflateSupported>::
221do_handshake(
222 response_type* res_p,
223 string_view host,
224 string_view target,
225 RequestDecorator const& decorator,
226 error_code& ec)
227{
228 if(res_p)
229 res_p->result(v: http::status::internal_server_error);
230
231 auto& impl = *impl_;
232 impl.change_status(status::handshake);
233 impl.reset();
234 detail::sec_ws_key_type key;
235 {
236 auto const req = impl.build_request(
237 key, host, target, decorator);
238 impl.do_pmd_config(req);
239 http::write(impl.stream(), req, ec);
240 }
241 if(impl.check_stop_now(ec))
242 return;
243 http::response_parser<
244 typename response_type::body_type> p;
245 http::read(next_layer(), impl.rd_buf, p, ec);
246 if(ec == http::error::buffer_overflow)
247 {
248 // If the response overflows the internal
249 // read buffer, switch to a dynamically
250 // allocated flat buffer.
251
252 flat_buffer fb;
253 fb.commit(n: net::buffer_copy(
254 fb.prepare(n: impl.rd_buf.size()),
255 impl.rd_buf.data()));
256 impl.rd_buf.clear();
257
258 http::read(next_layer(), fb, p, ec);;
259
260 if(! ec)
261 {
262 // Copy any leftovers back into the read
263 // buffer, since this represents websocket
264 // frame data.
265
266 if(fb.size() <= impl.rd_buf.capacity())
267 {
268 impl.rd_buf.commit(net::buffer_copy(
269 impl.rd_buf.prepare(fb.size()),
270 fb.data()));
271 }
272 else
273 {
274 BOOST_BEAST_ASSIGN_EC(ec, http::error::buffer_overflow);
275 }
276 }
277 }
278 if(impl.check_stop_now(ec))
279 return;
280
281 if (res_p)
282 {
283 // If res_p is not null, move parser's response into it.
284 *res_p = p.release();
285 }
286 else
287 {
288 // Otherwise point res_p at the response in the parser.
289 res_p = &p.get();
290 }
291
292 impl.on_response(*res_p, key, ec);
293}
294
295//------------------------------------------------------------------------------
296
297template<class NextLayer, bool deflateSupported>
298template<BOOST_BEAST_ASYNC_TPARAM1 HandshakeHandler>
299BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
300stream<NextLayer, deflateSupported>::
301async_handshake(
302 string_view host,
303 string_view target,
304 HandshakeHandler&& handler)
305{
306 static_assert(is_async_stream<next_layer_type>::value,
307 "AsyncStream type requirements not met");
308 detail::sec_ws_key_type key;
309 auto req = impl_->build_request(
310 key, host, target, &default_decorate_req);
311 return net::async_initiate<
312 HandshakeHandler,
313 void(error_code)>(
314 run_handshake_op{},
315 handler,
316 impl_,
317 std::move(req),
318 key,
319 nullptr);
320}
321
322template<class NextLayer, bool deflateSupported>
323template<BOOST_BEAST_ASYNC_TPARAM1 HandshakeHandler>
324BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler)
325stream<NextLayer, deflateSupported>::
326async_handshake(
327 response_type& res,
328 string_view host,
329 string_view target,
330 HandshakeHandler&& handler)
331{
332 static_assert(is_async_stream<next_layer_type>::value,
333 "AsyncStream type requirements not met");
334 detail::sec_ws_key_type key;
335 auto req = impl_->build_request(
336 key, host, target, &default_decorate_req);
337 res.result(v: http::status::internal_server_error);
338 return net::async_initiate<
339 HandshakeHandler,
340 void(error_code)>(
341 run_handshake_op{},
342 handler,
343 impl_,
344 std::move(req),
345 key,
346 &res);
347}
348
349template<class NextLayer, bool deflateSupported>
350void
351stream<NextLayer, deflateSupported>::
352handshake(string_view host,
353 string_view target)
354{
355 static_assert(is_sync_stream<next_layer_type>::value,
356 "SyncStream type requirements not met");
357 error_code ec;
358 handshake(
359 host, target, ec);
360 if(ec)
361 BOOST_THROW_EXCEPTION(system_error{ec});
362}
363
364template<class NextLayer, bool deflateSupported>
365void
366stream<NextLayer, deflateSupported>::
367handshake(response_type& res,
368 string_view host,
369 string_view target)
370{
371 static_assert(is_sync_stream<next_layer_type>::value,
372 "SyncStream type requirements not met");
373 error_code ec;
374 handshake(res, host, target, ec);
375 if(ec)
376 BOOST_THROW_EXCEPTION(system_error{ec});
377}
378
379template<class NextLayer, bool deflateSupported>
380void
381stream<NextLayer, deflateSupported>::
382handshake(string_view host,
383 string_view target, error_code& ec)
384{
385 static_assert(is_sync_stream<next_layer_type>::value,
386 "SyncStream type requirements not met");
387 do_handshake(nullptr,
388 host, target, &default_decorate_req, ec);
389}
390
391template<class NextLayer, bool deflateSupported>
392void
393stream<NextLayer, deflateSupported>::
394handshake(response_type& res,
395 string_view host,
396 string_view target,
397 error_code& ec)
398{
399 static_assert(is_sync_stream<next_layer_type>::value,
400 "SyncStream type requirements not met");
401 do_handshake(&res,
402 host, target, &default_decorate_req, ec);
403}
404
405} // websocket
406} // beast
407} // boost
408
409#endif
410

source code of boost/libs/beast/include/boost/beast/websocket/impl/handshake.hpp