• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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