2 // Copyright (c) 2016-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)
7 // Official repository: https://github.com/boostorg/beast
10 #ifndef BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
11 #define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
13 #include <boost/beast/websocket/detail/type_traits.hpp>
14 #include <boost/beast/http/empty_body.hpp>
15 #include <boost/beast/http/parser.hpp>
16 #include <boost/beast/http/read.hpp>
17 #include <boost/beast/http/string_body.hpp>
18 #include <boost/beast/http/write.hpp>
19 #include <boost/beast/core/buffers_prefix.hpp>
20 #include <boost/beast/core/handler_ptr.hpp>
21 #include <boost/beast/core/detail/type_traits.hpp>
22 #include <boost/asio/coroutine.hpp>
23 #include <boost/asio/associated_allocator.hpp>
24 #include <boost/asio/associated_executor.hpp>
25 #include <boost/asio/handler_continuation_hook.hpp>
26 #include <boost/asio/post.hpp>
27 #include <boost/assert.hpp>
28 #include <boost/throw_exception.hpp>
30 #include <type_traits>
36 // Respond to an upgrade HTTP request
37 template<class NextLayer>
38 template<class Handler>
39 class stream<NextLayer>::response_op
40 : public boost::asio::coroutine
44 stream<NextLayer>& ws;
47 template<class Body, class Allocator, class Decorator>
48 data(Handler&, stream<NextLayer>& ws_, http::request<
49 Body, http::basic_fields<Allocator>> const& req,
50 Decorator const& decorator)
52 , res(ws_.build_response(req, decorator))
57 handler_ptr<data, Handler> d_;
60 response_op(response_op&&) = default;
61 response_op(response_op const&) = default;
63 template<class DeducedHandler, class... Args>
64 response_op(DeducedHandler&& h,
65 stream<NextLayer>& ws, Args&&... args)
66 : d_(std::forward<DeducedHandler>(h),
67 ws, std::forward<Args>(args)...)
71 using allocator_type =
72 boost::asio::associated_allocator_t<Handler>;
75 get_allocator() const noexcept
77 return boost::asio::get_associated_allocator(d_.handler());
80 using executor_type = boost::asio::associated_executor_t<
81 Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
84 get_executor() const noexcept
86 return boost::asio::get_associated_executor(
87 d_.handler(), d_->ws.get_executor());
92 std::size_t bytes_transferred = 0);
95 bool asio_handler_is_continuation(response_op* op)
97 using boost::asio::asio_handler_is_continuation;
98 return asio_handler_is_continuation(
99 std::addressof(op->d_.handler()));
103 template<class NextLayer>
104 template<class Handler>
107 response_op<Handler>::
113 BOOST_ASIO_CORO_REENTER(*this)
116 BOOST_ASIO_CORO_YIELD
117 http::async_write(d.ws.next_layer(),
118 d.res, std::move(*this));
119 if(! ec && d.res.result() !=
120 http::status::switching_protocols)
121 ec = error::handshake_failed;
124 pmd_read(d.ws.pmd_config_, d.res);
125 d.ws.open(role_type::server);
131 //------------------------------------------------------------------------------
133 // read and respond to an upgrade request
135 template<class NextLayer>
136 template<class Decorator, class Handler>
137 class stream<NextLayer>::accept_op
138 : public boost::asio::coroutine
142 stream<NextLayer>& ws;
144 http::request_parser<http::empty_body> p;
145 data(Handler&, stream<NextLayer>& ws_,
146 Decorator const& decorator_)
148 , decorator(decorator_)
153 handler_ptr<data, Handler> d_;
156 accept_op(accept_op&&) = default;
157 accept_op(accept_op const&) = default;
159 template<class DeducedHandler, class... Args>
160 accept_op(DeducedHandler&& h,
161 stream<NextLayer>& ws, Args&&... args)
162 : d_(std::forward<DeducedHandler>(h),
163 ws, std::forward<Args>(args)...)
167 using allocator_type =
168 boost::asio::associated_allocator_t<Handler>;
171 get_allocator() const noexcept
173 return boost::asio::get_associated_allocator(d_.handler());
176 using executor_type = boost::asio::associated_executor_t<
177 Handler, decltype(std::declval<stream<NextLayer>&>().get_executor())>;
180 get_executor() const noexcept
182 return boost::asio::get_associated_executor(
183 d_.handler(), d_->ws.get_executor());
186 template<class Buffers>
187 void run(Buffers const& buffers);
191 std::size_t bytes_used = 0);
194 bool asio_handler_is_continuation(accept_op* op)
196 using boost::asio::asio_handler_is_continuation;
197 return asio_handler_is_continuation(
198 std::addressof(op->d_.handler()));
202 template<class NextLayer>
203 template<class Decorator, class Handler>
204 template<class Buffers>
207 accept_op<Decorator, Handler>::
208 run(Buffers const& buffers)
210 using boost::asio::buffer_copy;
211 using boost::asio::buffer_size;
214 boost::optional<typename
215 static_buffer_base::mutable_buffers_type> mb;
216 auto const len = buffer_size(buffers);
219 mb.emplace(d.ws.rd_buf_.prepare(len));
221 catch(std::length_error const&)
223 ec = error::buffer_overflow;
227 buffer_copy(*mb, buffers));
231 template<class NextLayer>
232 template<class Decorator, class Handler>
235 accept_op<Decorator, Handler>::
236 operator()(error_code ec, std::size_t)
239 BOOST_ASIO_CORO_REENTER(*this)
243 BOOST_ASIO_CORO_YIELD
246 bind_handler(std::move(*this), ec));
250 BOOST_ASIO_CORO_YIELD
252 d.ws.next_layer(), d.ws.rd_buf_,
253 d.p, std::move(*this));
254 if(ec == http::error::end_of_stream)
258 // Arguments from our state must be
259 // moved to the stack before releasing
262 auto const req = d.p.release();
263 auto const decorator = d.decorator;
265 return response_op<Handler>{
266 d_.release_handler(),
267 ws, req, decorator}(ec);
269 // VFALCO This *should* work but breaks
270 // coroutine invariants in the unit test.
271 // Also it calls reset() when it shouldn't.
272 return ws.async_accept_ex(
273 req, decorator, d_.release_handler());
281 //------------------------------------------------------------------------------
283 template<class NextLayer>
288 static_assert(is_sync_stream<next_layer_type>::value,
289 "SyncStream requirements not met");
293 BOOST_THROW_EXCEPTION(system_error{ec});
296 template<class NextLayer>
297 template<class ResponseDecorator>
300 accept_ex(ResponseDecorator const& decorator)
302 static_assert(is_sync_stream<next_layer_type>::value,
303 "SyncStream requirements not met");
304 static_assert(detail::is_ResponseDecorator<
305 ResponseDecorator>::value,
306 "ResponseDecorator requirements not met");
308 accept_ex(decorator, ec);
310 BOOST_THROW_EXCEPTION(system_error{ec});
313 template<class NextLayer>
316 accept(error_code& ec)
318 static_assert(is_sync_stream<next_layer_type>::value,
319 "SyncStream requirements not met");
321 do_accept(&default_decorate_res, ec);
324 template<class NextLayer>
325 template<class ResponseDecorator>
328 accept_ex(ResponseDecorator const& decorator, error_code& ec)
330 static_assert(is_sync_stream<next_layer_type>::value,
331 "SyncStream requirements not met");
332 static_assert(detail::is_ResponseDecorator<
333 ResponseDecorator>::value,
334 "ResponseDecorator requirements not met");
336 do_accept(decorator, ec);
339 template<class NextLayer>
340 template<class ConstBufferSequence>
341 typename std::enable_if<! http::detail::is_header<
342 ConstBufferSequence>::value>::type
344 accept(ConstBufferSequence const& buffers)
346 static_assert(is_sync_stream<next_layer_type>::value,
347 "SyncStream requirements not met");
348 static_assert(boost::asio::is_const_buffer_sequence<
349 ConstBufferSequence>::value,
350 "ConstBufferSequence requirements not met");
354 BOOST_THROW_EXCEPTION(system_error{ec});
357 template<class NextLayer>
359 class ConstBufferSequence,
360 class ResponseDecorator>
361 typename std::enable_if<! http::detail::is_header<
362 ConstBufferSequence>::value>::type
365 ConstBufferSequence const& buffers,
366 ResponseDecorator const &decorator)
368 static_assert(is_sync_stream<next_layer_type>::value,
369 "SyncStream requirements not met");
370 static_assert(boost::asio::is_const_buffer_sequence<
371 ConstBufferSequence>::value,
372 "ConstBufferSequence requirements not met");
373 static_assert(detail::is_ResponseDecorator<
374 ResponseDecorator>::value,
375 "ResponseDecorator requirements not met");
377 accept_ex(buffers, decorator, ec);
379 BOOST_THROW_EXCEPTION(system_error{ec});
382 template<class NextLayer>
383 template<class ConstBufferSequence>
384 typename std::enable_if<! http::detail::is_header<
385 ConstBufferSequence>::value>::type
388 ConstBufferSequence const& buffers, error_code& ec)
390 static_assert(is_sync_stream<next_layer_type>::value,
391 "SyncStream requirements not met");
392 static_assert(boost::asio::is_const_buffer_sequence<
393 ConstBufferSequence>::value,
394 "ConstBufferSequence requirements not met");
395 using boost::asio::buffer_copy;
396 using boost::asio::buffer_size;
398 boost::optional<typename
399 static_buffer_base::mutable_buffers_type> mb;
402 mb.emplace(rd_buf_.prepare(
403 buffer_size(buffers)));
405 catch(std::length_error const&)
407 ec = error::buffer_overflow;
411 buffer_copy(*mb, buffers));
412 do_accept(&default_decorate_res, ec);
415 template<class NextLayer>
417 class ConstBufferSequence,
418 class ResponseDecorator>
419 typename std::enable_if<! http::detail::is_header<
420 ConstBufferSequence>::value>::type
423 ConstBufferSequence const& buffers,
424 ResponseDecorator const& decorator,
427 static_assert(is_sync_stream<next_layer_type>::value,
428 "SyncStream requirements not met");
429 static_assert(boost::asio::is_const_buffer_sequence<
430 ConstBufferSequence>::value,
431 "ConstBufferSequence requirements not met");
432 static_assert(boost::asio::is_const_buffer_sequence<
433 ConstBufferSequence>::value,
434 "ConstBufferSequence requirements not met");
435 using boost::asio::buffer_copy;
436 using boost::asio::buffer_size;
438 boost::optional<typename
439 static_buffer_base::mutable_buffers_type> mb;
442 mb.emplace(rd_buf_.prepare(
443 buffer_size(buffers)));
445 catch(std::length_error const&)
447 ec = error::buffer_overflow;
450 rd_buf_.commit(buffer_copy(*mb, buffers));
451 do_accept(decorator, ec);
454 template<class NextLayer>
455 template<class Body, class Allocator>
460 http::basic_fields<Allocator>> const& req)
462 static_assert(is_sync_stream<next_layer_type>::value,
463 "SyncStream requirements not met");
467 BOOST_THROW_EXCEPTION(system_error{ec});
470 template<class NextLayer>
472 class Body, class Allocator,
473 class ResponseDecorator>
478 http::basic_fields<Allocator>> const& req,
479 ResponseDecorator const& decorator)
481 static_assert(is_sync_stream<next_layer_type>::value,
482 "SyncStream requirements not met");
483 static_assert(detail::is_ResponseDecorator<
484 ResponseDecorator>::value,
485 "ResponseDecorator requirements not met");
487 accept_ex(req, decorator, ec);
489 BOOST_THROW_EXCEPTION(system_error{ec});
492 template<class NextLayer>
493 template<class Body, class Allocator>
498 http::basic_fields<Allocator>> const& req,
501 static_assert(is_sync_stream<next_layer_type>::value,
502 "SyncStream requirements not met");
504 do_accept(req, &default_decorate_res, ec);
507 template<class NextLayer>
509 class Body, class Allocator,
510 class ResponseDecorator>
515 http::basic_fields<Allocator>> const& req,
516 ResponseDecorator const& decorator,
519 static_assert(is_sync_stream<next_layer_type>::value,
520 "SyncStream requirements not met");
521 static_assert(detail::is_ResponseDecorator<
522 ResponseDecorator>::value,
523 "ResponseDecorator requirements not met");
525 do_accept(req, decorator, ec);
528 //------------------------------------------------------------------------------
530 template<class NextLayer>
533 BOOST_ASIO_INITFN_RESULT_TYPE(
534 AcceptHandler, void(error_code))
537 AcceptHandler&& handler)
539 static_assert(is_async_stream<next_layer_type>::value,
540 "AsyncStream requirements requirements not met");
541 boost::asio::async_completion<AcceptHandler,
542 void(error_code)> init{handler};
545 decltype(&default_decorate_res),
546 BOOST_ASIO_HANDLER_TYPE(
547 AcceptHandler, void(error_code))>{
548 init.completion_handler,
550 &default_decorate_res}({});
551 return init.result.get();
554 template<class NextLayer>
556 class ResponseDecorator,
558 BOOST_ASIO_INITFN_RESULT_TYPE(
559 AcceptHandler, void(error_code))
562 ResponseDecorator const& decorator,
563 AcceptHandler&& handler)
565 static_assert(is_async_stream<next_layer_type>::value,
566 "AsyncStream requirements requirements not met");
567 static_assert(detail::is_ResponseDecorator<
568 ResponseDecorator>::value,
569 "ResponseDecorator requirements not met");
570 boost::asio::async_completion<AcceptHandler,
571 void(error_code)> init{handler};
575 BOOST_ASIO_HANDLER_TYPE(
576 AcceptHandler, void(error_code))>{
577 init.completion_handler,
580 return init.result.get();
583 template<class NextLayer>
585 class ConstBufferSequence,
587 typename std::enable_if<
588 ! http::detail::is_header<ConstBufferSequence>::value,
589 BOOST_ASIO_INITFN_RESULT_TYPE(
590 AcceptHandler, void(error_code))>::type
593 ConstBufferSequence const& buffers,
594 AcceptHandler&& handler)
596 static_assert(is_async_stream<next_layer_type>::value,
597 "AsyncStream requirements requirements not met");
598 static_assert(boost::asio::is_const_buffer_sequence<
599 ConstBufferSequence>::value,
600 "ConstBufferSequence requirements not met");
601 boost::asio::async_completion<AcceptHandler,
602 void(error_code)> init{handler};
605 decltype(&default_decorate_res),
606 BOOST_ASIO_HANDLER_TYPE(
607 AcceptHandler, void(error_code))>{
608 init.completion_handler,
610 &default_decorate_res}.run(buffers);
611 return init.result.get();
614 template<class NextLayer>
616 class ConstBufferSequence,
617 class ResponseDecorator,
619 typename std::enable_if<
620 ! http::detail::is_header<ConstBufferSequence>::value,
621 BOOST_ASIO_INITFN_RESULT_TYPE(
622 AcceptHandler, void(error_code))>::type
625 ConstBufferSequence const& buffers,
626 ResponseDecorator const& decorator,
627 AcceptHandler&& handler)
629 static_assert(is_async_stream<next_layer_type>::value,
630 "AsyncStream requirements requirements not met");
631 static_assert(boost::asio::is_const_buffer_sequence<
632 ConstBufferSequence>::value,
633 "ConstBufferSequence requirements not met");
634 static_assert(detail::is_ResponseDecorator<
635 ResponseDecorator>::value,
636 "ResponseDecorator requirements not met");
637 boost::asio::async_completion<AcceptHandler,
638 void(error_code)> init{handler};
642 BOOST_ASIO_HANDLER_TYPE(
643 AcceptHandler, void(error_code))>{
644 init.completion_handler,
646 decorator}.run(buffers);
647 return init.result.get();
650 template<class NextLayer>
652 class Body, class Allocator,
654 BOOST_ASIO_INITFN_RESULT_TYPE(
655 AcceptHandler, void(error_code))
658 http::request<Body, http::basic_fields<Allocator>> const& req,
659 AcceptHandler&& handler)
661 static_assert(is_async_stream<next_layer_type>::value,
662 "AsyncStream requirements requirements not met");
663 boost::asio::async_completion<AcceptHandler,
664 void(error_code)> init{handler};
666 using boost::asio::asio_handler_is_continuation;
668 BOOST_ASIO_HANDLER_TYPE(
669 AcceptHandler, void(error_code))>{
670 init.completion_handler,
673 &default_decorate_res}();
674 return init.result.get();
677 template<class NextLayer>
679 class Body, class Allocator,
680 class ResponseDecorator,
682 BOOST_ASIO_INITFN_RESULT_TYPE(
683 AcceptHandler, void(error_code))
686 http::request<Body, http::basic_fields<Allocator>> const& req,
687 ResponseDecorator const& decorator,
688 AcceptHandler&& handler)
690 static_assert(is_async_stream<next_layer_type>::value,
691 "AsyncStream requirements requirements not met");
692 static_assert(detail::is_ResponseDecorator<
693 ResponseDecorator>::value,
694 "ResponseDecorator requirements not met");
695 boost::asio::async_completion<AcceptHandler,
696 void(error_code)> init{handler};
698 using boost::asio::asio_handler_is_continuation;
700 BOOST_ASIO_HANDLER_TYPE(
701 AcceptHandler, void(error_code))>{
702 init.completion_handler,
706 return init.result.get();
709 //------------------------------------------------------------------------------
711 template<class NextLayer>
712 template<class Decorator>
716 Decorator const& decorator,
719 http::request_parser<http::empty_body> p;
720 http::read(next_layer(), rd_buf_, p, ec);
721 if(ec == http::error::end_of_stream)
725 do_accept(p.get(), decorator, ec);
728 template<class NextLayer>
729 template<class Body, class Allocator,
734 http::request<Body,http::basic_fields<Allocator>> const& req,
735 Decorator const& decorator,
738 auto const res = build_response(req, decorator);
739 http::write(stream_, res, ec);
742 if(res.result() != http::status::switching_protocols)
744 ec = error::handshake_failed;
745 // VFALCO TODO Respect keep alive setting, perform
746 // teardown if Connection: close.
749 pmd_read(pmd_config_, res);
750 open(role_type::server);