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_CORE_IMPL_BASIC_STREAM_HPP
11 #define BOOST_BEAST_CORE_IMPL_BASIC_STREAM_HPP
12
13 #include <boost/beast/core/async_base.hpp>
14 #include <boost/beast/core/buffer_traits.hpp>
15 #include <boost/beast/core/buffers_prefix.hpp>
16 #include <boost/beast/websocket/teardown.hpp>
17 #include <boost/asio/coroutine.hpp>
18 #include <boost/assert.hpp>
19 #include <boost/make_shared.hpp>
20 #include <boost/core/exchange.hpp>
21 #include <cstdlib>
22 #include <type_traits>
23 #include <utility>
24
25 namespace boost {
26 namespace beast {
27
28 //------------------------------------------------------------------------------
29
30 template<class Protocol, class Executor, class RatePolicy>
31 template<class... Args>
32 basic_stream<Protocol, Executor, RatePolicy>::
33 impl_type::
impl_type(std::false_type,Args &&...args)34 impl_type(std::false_type, Args&&... args)
35 : socket(std::forward<Args>(args)...)
36 , read(ex())
37 , write(ex())
38 , timer(ex())
39 {
40 reset();
41 }
42
43 template<class Protocol, class Executor, class RatePolicy>
44 template<class RatePolicy_, class... Args>
45 basic_stream<Protocol, Executor, RatePolicy>::
46 impl_type::
impl_type(std::true_type,RatePolicy_ && policy,Args &&...args)47 impl_type(std::true_type,
48 RatePolicy_&& policy, Args&&... args)
49 : boost::empty_value<RatePolicy>(
50 boost::empty_init_t{},
51 std::forward<RatePolicy_>(policy))
52 , socket(std::forward<Args>(args)...)
53 , read(ex())
54 , write(ex())
55 , timer(ex())
56 {
57 reset();
58 }
59
60 template<class Protocol, class Executor, class RatePolicy>
61 template<class Executor2>
62 void
63 basic_stream<Protocol, Executor, RatePolicy>::
64 impl_type::
on_timer(Executor2 const & ex2)65 on_timer(Executor2 const& ex2)
66 {
67 BOOST_ASSERT(waiting > 0);
68
69 // the last waiter starts the new slice
70 if(--waiting > 0)
71 return;
72
73 // update the expiration time
74 BOOST_VERIFY(timer.expires_after(
75 std::chrono::seconds(1)) == 0);
76
77 rate_policy_access::on_timer(policy());
78
79 struct handler : boost::empty_value<Executor2>
80 {
81 boost::weak_ptr<impl_type> wp;
82
83 using executor_type = Executor2;
84
85 executor_type
86 get_executor() const noexcept
87 {
88 return this->get();
89 }
90
91 handler(
92 Executor2 const& ex2,
93 boost::shared_ptr<impl_type> const& sp)
94 : boost::empty_value<Executor2>(
95 boost::empty_init_t{}, ex2)
96 , wp(sp)
97 {
98 }
99
100 void
101 operator()(error_code ec)
102 {
103 auto sp = wp.lock();
104 if(! sp)
105 return;
106 if(ec == net::error::operation_aborted)
107 return;
108 BOOST_ASSERT(! ec);
109 if(ec)
110 return;
111 sp->on_timer(this->get());
112 }
113 };
114
115 // wait on the timer again
116 ++waiting;
117 timer.async_wait(handler(ex2, this->shared_from_this()));
118 }
119
120 template<class Protocol, class Executor, class RatePolicy>
121 void
122 basic_stream<Protocol, Executor, RatePolicy>::
123 impl_type::
reset()124 reset()
125 {
126 // If assert goes off, it means that there are
127 // already read or write (or connect) operations
128 // outstanding, so there is nothing to apply
129 // the expiration time to!
130 //
131 BOOST_ASSERT(! read.pending || ! write.pending);
132
133 if(! read.pending)
134 BOOST_VERIFY(
135 read.timer.expires_at(never()) == 0);
136
137 if(! write.pending)
138 BOOST_VERIFY(
139 write.timer.expires_at(never()) == 0);
140 }
141
142 template<class Protocol, class Executor, class RatePolicy>
143 void
144 basic_stream<Protocol, Executor, RatePolicy>::
145 impl_type::
close()146 close() noexcept
147 {
148 {
149 error_code ec;
150 socket.close(ec);
151 }
152 try
153 {
154 timer.cancel();
155 }
156 catch(...)
157 {
158 }
159 }
160
161 //------------------------------------------------------------------------------
162
163 template<class Protocol, class Executor, class RatePolicy>
164 template<class Executor2>
165 struct basic_stream<Protocol, Executor, RatePolicy>::
166 timeout_handler
167 {
168 using executor_type = Executor2;
169
170 op_state& state;
171 boost::weak_ptr<impl_type> wp;
172 tick_type tick;
173 executor_type ex;
174
get_executorboost::beast::basic_stream::timeout_handler175 executor_type get_executor() const noexcept
176 {
177 return ex;
178 }
179
180 void
operator ()boost::beast::basic_stream::timeout_handler181 operator()(error_code ec)
182 {
183 // timer canceled
184 if(ec == net::error::operation_aborted)
185 return;
186 BOOST_ASSERT(! ec);
187
188 auto sp = wp.lock();
189
190 // stream destroyed
191 if(! sp)
192 return;
193
194 // stale timer
195 if(tick < state.tick)
196 return;
197 BOOST_ASSERT(tick == state.tick);
198
199 // timeout
200 BOOST_ASSERT(! state.timeout);
201 sp->close();
202 state.timeout = true;
203 }
204 };
205
206 //------------------------------------------------------------------------------
207
208 template<class Protocol, class Executor, class RatePolicy>
209 struct basic_stream<Protocol, Executor, RatePolicy>::ops
210 {
211
212 template<bool isRead, class Buffers, class Handler>
213 class transfer_op
214 : public async_base<Handler, Executor>
215 , public boost::asio::coroutine
216 {
217 boost::shared_ptr<impl_type> impl_;
218 pending_guard pg_;
219 Buffers b_;
220
221 using is_read = std::integral_constant<bool, isRead>;
222
223 op_state&
state()224 state()
225 {
226 if (isRead)
227 return impl_->read;
228 else
229 return impl_->write;
230 }
231
232 std::size_t
available_bytes()233 available_bytes()
234 {
235 if (isRead)
236 return rate_policy_access::
237 available_read_bytes(impl_->policy());
238 else
239 return rate_policy_access::
240 available_write_bytes(impl_->policy());
241 }
242
243 void
transfer_bytes(std::size_t n)244 transfer_bytes(std::size_t n)
245 {
246 if (isRead)
247 rate_policy_access::
248 transfer_read_bytes(impl_->policy(), n);
249 else
250 rate_policy_access::
251 transfer_write_bytes(impl_->policy(), n);
252 }
253
254 void
async_perform(std::size_t amount,std::true_type)255 async_perform(
256 std::size_t amount, std::true_type)
257 {
258 impl_->socket.async_read_some(
259 beast::buffers_prefix(amount, b_),
260 std::move(*this));
261 }
262
263 void
async_perform(std::size_t amount,std::false_type)264 async_perform(
265 std::size_t amount, std::false_type)
266 {
267 impl_->socket.async_write_some(
268 beast::buffers_prefix(amount, b_),
269 std::move(*this));
270 }
271
272 public:
273 template<class Handler_>
transfer_op(Handler_ && h,basic_stream & s,Buffers const & b)274 transfer_op(
275 Handler_&& h,
276 basic_stream& s,
277 Buffers const& b)
278 : async_base<Handler, Executor>(
279 std::forward<Handler_>(h), s.get_executor())
280 , impl_(s.impl_)
281 , pg_(state().pending)
282 , b_(b)
283 {
284 (*this)({});
285 }
286
287 void
operator ()(error_code ec,std::size_t bytes_transferred=0)288 operator()(
289 error_code ec,
290 std::size_t bytes_transferred = 0)
291 {
292 BOOST_ASIO_CORO_REENTER(*this)
293 {
294 // handle empty buffers
295 if(detail::buffers_empty(b_))
296 {
297 // make sure we perform the no-op
298 BOOST_ASIO_CORO_YIELD
299 async_perform(0, is_read{});
300 // apply the timeout manually, otherwise
301 // behavior varies across platforms.
302 if(state().timer.expiry() <= clock_type::now())
303 {
304 impl_->close();
305 ec = beast::error::timeout;
306 }
307 goto upcall;
308 }
309
310 // if a timeout is active, wait on the timer
311 if(state().timer.expiry() != never())
312 state().timer.async_wait(
313 timeout_handler<decltype(this->get_executor())>{
314 state(),
315 impl_,
316 state().tick,
317 this->get_executor()});
318
319 // check rate limit, maybe wait
320 std::size_t amount;
321 amount = available_bytes();
322 if(amount == 0)
323 {
324 ++impl_->waiting;
325 BOOST_ASIO_CORO_YIELD
326 impl_->timer.async_wait(std::move(*this));
327 if(ec)
328 {
329 // socket was closed, or a timeout
330 BOOST_ASSERT(ec ==
331 net::error::operation_aborted);
332 // timeout handler invoked?
333 if(state().timeout)
334 {
335 // yes, socket already closed
336 ec = beast::error::timeout;
337 state().timeout = false;
338 }
339 goto upcall;
340 }
341 impl_->on_timer(this->get_executor());
342
343 // Allow at least one byte, otherwise
344 // bytes_transferred could be 0.
345 amount = std::max<std::size_t>(
346 available_bytes(), 1);
347 }
348
349 BOOST_ASIO_CORO_YIELD
350 async_perform(amount, is_read{});
351
352 if(state().timer.expiry() != never())
353 {
354 ++state().tick;
355
356 // try cancelling timer
357 auto const n =
358 state().timer.cancel();
359 if(n == 0)
360 {
361 // timeout handler invoked?
362 if(state().timeout)
363 {
364 // yes, socket already closed
365 ec = beast::error::timeout;
366 state().timeout = false;
367 }
368 }
369 else
370 {
371 BOOST_ASSERT(n == 1);
372 BOOST_ASSERT(! state().timeout);
373 }
374 }
375
376 upcall:
377 pg_.reset();
378 transfer_bytes(bytes_transferred);
379 this->complete_now(ec, bytes_transferred);
380 }
381 }
382 };
383
384 template<class Handler>
385 class connect_op
386 : public async_base<Handler, Executor>
387 {
388 boost::shared_ptr<impl_type> impl_;
389 pending_guard pg0_;
390 pending_guard pg1_;
391
392 op_state&
state()393 state() noexcept
394 {
395 return impl_->write;
396 }
397
398 public:
399 template<class Handler_>
connect_op(Handler_ && h,basic_stream & s,endpoint_type ep)400 connect_op(
401 Handler_&& h,
402 basic_stream& s,
403 endpoint_type ep)
404 : async_base<Handler, Executor>(
405 std::forward<Handler_>(h), s.get_executor())
406 , impl_(s.impl_)
407 , pg0_(impl_->read.pending)
408 , pg1_(impl_->write.pending)
409 {
410 if(state().timer.expiry() != stream_base::never())
411 impl_->write.timer.async_wait(
412 timeout_handler<decltype(this->get_executor())>{
413 state(),
414 impl_,
415 state().tick,
416 this->get_executor()});
417
418 impl_->socket.async_connect(
419 ep, std::move(*this));
420 // *this is now moved-from
421 }
422
423 template<
424 class Endpoints, class Condition,
425 class Handler_>
connect_op(Handler_ && h,basic_stream & s,Endpoints const & eps,Condition const & cond)426 connect_op(
427 Handler_&& h,
428 basic_stream& s,
429 Endpoints const& eps,
430 Condition const& cond)
431 : async_base<Handler, Executor>(
432 std::forward<Handler_>(h), s.get_executor())
433 , impl_(s.impl_)
434 , pg0_(impl_->read.pending)
435 , pg1_(impl_->write.pending)
436 {
437 if(state().timer.expiry() != stream_base::never())
438 impl_->write.timer.async_wait(
439 timeout_handler<decltype(this->get_executor())>{
440 state(),
441 impl_,
442 state().tick,
443 this->get_executor()});
444
445 net::async_connect(impl_->socket,
446 eps, cond, std::move(*this));
447 // *this is now moved-from
448 }
449
450 template<
451 class Iterator, class Condition,
452 class Handler_>
connect_op(Handler_ && h,basic_stream & s,Iterator begin,Iterator end,Condition const & cond)453 connect_op(
454 Handler_&& h,
455 basic_stream& s,
456 Iterator begin, Iterator end,
457 Condition const& cond)
458 : async_base<Handler, Executor>(
459 std::forward<Handler_>(h), s.get_executor())
460 , impl_(s.impl_)
461 , pg0_(impl_->read.pending)
462 , pg1_(impl_->write.pending)
463 {
464 if(state().timer.expiry() != stream_base::never())
465 impl_->write.timer.async_wait(
466 timeout_handler<decltype(this->get_executor())>{
467 state(),
468 impl_,
469 state().tick,
470 this->get_executor()});
471
472 net::async_connect(impl_->socket,
473 begin, end, cond, std::move(*this));
474 // *this is now moved-from
475 }
476
477 template<class... Args>
478 void
operator ()(error_code ec,Args &&...args)479 operator()(error_code ec, Args&&... args)
480 {
481 if(state().timer.expiry() != stream_base::never())
482 {
483 ++state().tick;
484
485 // try cancelling timer
486 auto const n =
487 impl_->write.timer.cancel();
488 if(n == 0)
489 {
490 // timeout handler invoked?
491 if(state().timeout)
492 {
493 // yes, socket already closed
494 ec = beast::error::timeout;
495 state().timeout = false;
496 }
497 }
498 else
499 {
500 BOOST_ASSERT(n == 1);
501 BOOST_ASSERT(! state().timeout);
502 }
503 }
504
505 pg0_.reset();
506 pg1_.reset();
507 this->complete_now(ec, std::forward<Args>(args)...);
508 }
509 };
510
511 struct run_read_op
512 {
513 template<class ReadHandler, class Buffers>
514 void
operator ()boost::beast::basic_stream::ops::run_read_op515 operator()(
516 ReadHandler&& h,
517 basic_stream* s,
518 Buffers const& b)
519 {
520 // If you get an error on the following line it means
521 // that your handler does not meet the documented type
522 // requirements for the handler.
523
524 static_assert(
525 detail::is_invocable<ReadHandler,
526 void(error_code, std::size_t)>::value,
527 "ReadHandler type requirements not met");
528
529 transfer_op<
530 true,
531 Buffers,
532 typename std::decay<ReadHandler>::type>(
533 std::forward<ReadHandler>(h), *s, b);
534 }
535 };
536
537 struct run_write_op
538 {
539 template<class WriteHandler, class Buffers>
540 void
operator ()boost::beast::basic_stream::ops::run_write_op541 operator()(
542 WriteHandler&& h,
543 basic_stream* s,
544 Buffers const& b)
545 {
546 // If you get an error on the following line it means
547 // that your handler does not meet the documented type
548 // requirements for the handler.
549
550 static_assert(
551 detail::is_invocable<WriteHandler,
552 void(error_code, std::size_t)>::value,
553 "WriteHandler type requirements not met");
554
555 transfer_op<
556 false,
557 Buffers,
558 typename std::decay<WriteHandler>::type>(
559 std::forward<WriteHandler>(h), *s, b);
560 }
561 };
562
563 struct run_connect_op
564 {
565 template<class ConnectHandler>
566 void
operator ()boost::beast::basic_stream::ops::run_connect_op567 operator()(
568 ConnectHandler&& h,
569 basic_stream* s,
570 endpoint_type const& ep)
571 {
572 // If you get an error on the following line it means
573 // that your handler does not meet the documented type
574 // requirements for the handler.
575
576 static_assert(
577 detail::is_invocable<ConnectHandler,
578 void(error_code)>::value,
579 "ConnectHandler type requirements not met");
580
581 connect_op<typename std::decay<ConnectHandler>::type>(
582 std::forward<ConnectHandler>(h), *s, ep);
583 }
584 };
585
586 struct run_connect_range_op
587 {
588 template<
589 class RangeConnectHandler,
590 class EndpointSequence,
591 class Condition>
592 void
operator ()boost::beast::basic_stream::ops::run_connect_range_op593 operator()(
594 RangeConnectHandler&& h,
595 basic_stream* s,
596 EndpointSequence const& eps,
597 Condition const& cond)
598 {
599 // If you get an error on the following line it means
600 // that your handler does not meet the documented type
601 // requirements for the handler.
602
603 static_assert(
604 detail::is_invocable<RangeConnectHandler,
605 void(error_code, typename Protocol::endpoint)>::value,
606 "RangeConnectHandler type requirements not met");
607
608 connect_op<typename std::decay<RangeConnectHandler>::type>(
609 std::forward<RangeConnectHandler>(h), *s, eps, cond);
610 }
611 };
612
613 struct run_connect_iter_op
614 {
615 template<
616 class IteratorConnectHandler,
617 class Iterator,
618 class Condition>
619 void
operator ()boost::beast::basic_stream::ops::run_connect_iter_op620 operator()(
621 IteratorConnectHandler&& h,
622 basic_stream* s,
623 Iterator begin, Iterator end,
624 Condition const& cond)
625 {
626 // If you get an error on the following line it means
627 // that your handler does not meet the documented type
628 // requirements for the handler.
629
630 static_assert(
631 detail::is_invocable<IteratorConnectHandler,
632 void(error_code, Iterator)>::value,
633 "IteratorConnectHandler type requirements not met");
634
635 connect_op<typename std::decay<IteratorConnectHandler>::type>(
636 std::forward<IteratorConnectHandler>(h), *s, begin, end, cond);
637 }
638 };
639
640 };
641
642 //------------------------------------------------------------------------------
643
644 template<class Protocol, class Executor, class RatePolicy>
645 basic_stream<Protocol, Executor, RatePolicy>::
~basic_stream()646 ~basic_stream()
647 {
648 // the shared object can outlive *this,
649 // cancel any operations so the shared
650 // object is destroyed as soon as possible.
651 impl_->close();
652 }
653
654 template<class Protocol, class Executor, class RatePolicy>
655 template<class Arg0, class... Args, class>
656 basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(Arg0 && arg0,Args &&...args)657 basic_stream(Arg0&& arg0, Args&&... args)
658 : impl_(boost::make_shared<impl_type>(
659 std::false_type{},
660 std::forward<Arg0>(arg0),
661 std::forward<Args>(args)...))
662 {
663 }
664
665 template<class Protocol, class Executor, class RatePolicy>
666 template<class RatePolicy_, class Arg0, class... Args, class>
667 basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(RatePolicy_ && policy,Arg0 && arg0,Args &&...args)668 basic_stream(
669 RatePolicy_&& policy, Arg0&& arg0, Args&&... args)
670 : impl_(boost::make_shared<impl_type>(
671 std::true_type{},
672 std::forward<RatePolicy_>(policy),
673 std::forward<Arg0>(arg0),
674 std::forward<Args>(args)...))
675 {
676 }
677
678 template<class Protocol, class Executor, class RatePolicy>
679 basic_stream<Protocol, Executor, RatePolicy>::
basic_stream(basic_stream && other)680 basic_stream(basic_stream&& other)
681 : impl_(boost::make_shared<impl_type>(
682 std::move(*other.impl_)))
683 {
684 // Explainer: Asio's sockets provide the guarantee that a moved-from socket
685 // will be in a state as-if newly created. i.e.:
686 // * having the same (valid) executor
687 // * the socket shall not be open
688 // We provide the same guarantee by moving the impl rather than the pointer
689 // controlling its lifetime.
690 }
691
692 //------------------------------------------------------------------------------
693
694 template<class Protocol, class Executor, class RatePolicy>
695 auto
696 basic_stream<Protocol, Executor, RatePolicy>::
release_socket()697 release_socket() ->
698 socket_type
699 {
700 this->cancel();
701 return std::move(impl_->socket);
702 }
703
704 template<class Protocol, class Executor, class RatePolicy>
705 void
706 basic_stream<Protocol, Executor, RatePolicy>::
expires_after(net::steady_timer::duration expiry_time)707 expires_after(net::steady_timer::duration expiry_time)
708 {
709 // If assert goes off, it means that there are
710 // already read or write (or connect) operations
711 // outstanding, so there is nothing to apply
712 // the expiration time to!
713 //
714 BOOST_ASSERT(
715 ! impl_->read.pending ||
716 ! impl_->write.pending);
717
718 if(! impl_->read.pending)
719 BOOST_VERIFY(
720 impl_->read.timer.expires_after(
721 expiry_time) == 0);
722
723 if(! impl_->write.pending)
724 BOOST_VERIFY(
725 impl_->write.timer.expires_after(
726 expiry_time) == 0);
727 }
728
729 template<class Protocol, class Executor, class RatePolicy>
730 void
731 basic_stream<Protocol, Executor, RatePolicy>::
expires_at(net::steady_timer::time_point expiry_time)732 expires_at(
733 net::steady_timer::time_point expiry_time)
734 {
735 // If assert goes off, it means that there are
736 // already read or write (or connect) operations
737 // outstanding, so there is nothing to apply
738 // the expiration time to!
739 //
740 BOOST_ASSERT(
741 ! impl_->read.pending ||
742 ! impl_->write.pending);
743
744 if(! impl_->read.pending)
745 BOOST_VERIFY(
746 impl_->read.timer.expires_at(
747 expiry_time) == 0);
748
749 if(! impl_->write.pending)
750 BOOST_VERIFY(
751 impl_->write.timer.expires_at(
752 expiry_time) == 0);
753 }
754
755 template<class Protocol, class Executor, class RatePolicy>
756 void
757 basic_stream<Protocol, Executor, RatePolicy>::
expires_never()758 expires_never()
759 {
760 impl_->reset();
761 }
762
763 template<class Protocol, class Executor, class RatePolicy>
764 void
765 basic_stream<Protocol, Executor, RatePolicy>::
cancel()766 cancel()
767 {
768 error_code ec;
769 impl_->socket.cancel(ec);
770 impl_->timer.cancel();
771 }
772
773 template<class Protocol, class Executor, class RatePolicy>
774 void
775 basic_stream<Protocol, Executor, RatePolicy>::
close()776 close()
777 {
778 impl_->close();
779 }
780
781 //------------------------------------------------------------------------------
782
783 template<class Protocol, class Executor, class RatePolicy>
784 template<BOOST_BEAST_ASYNC_TPARAM1 ConnectHandler>
BOOST_BEAST_ASYNC_RESULT1(ConnectHandler)785 BOOST_BEAST_ASYNC_RESULT1(ConnectHandler)
786 basic_stream<Protocol, Executor, RatePolicy>::
787 async_connect(
788 endpoint_type const& ep,
789 ConnectHandler&& handler)
790 {
791 return net::async_initiate<
792 ConnectHandler,
793 void(error_code)>(
794 typename ops::run_connect_op{},
795 handler,
796 this,
797 ep);
798 }
799
800 template<class Protocol, class Executor, class RatePolicy>
801 template<
802 class EndpointSequence,
803 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, typename Protocol::endpoint)) RangeConnectHandler,
804 class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code,typename Protocol::endpoint))805 BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void(error_code, typename Protocol::endpoint))
806 basic_stream<Protocol, Executor, RatePolicy>::
807 async_connect(
808 EndpointSequence const& endpoints,
809 RangeConnectHandler&& handler)
810 {
811 return net::async_initiate<
812 RangeConnectHandler,
813 void(error_code, typename Protocol::endpoint)>(
814 typename ops::run_connect_range_op{},
815 handler,
816 this,
817 endpoints,
818 detail::any_endpoint{});
819 }
820
821 template<class Protocol, class Executor, class RatePolicy>
822 template<
823 class EndpointSequence,
824 class ConnectCondition,
825 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, typename Protocol::endpoint)) RangeConnectHandler,
826 class>
BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code,typename Protocol::endpoint))827 BOOST_ASIO_INITFN_RESULT_TYPE(RangeConnectHandler,void (error_code, typename Protocol::endpoint))
828 basic_stream<Protocol, Executor, RatePolicy>::
829 async_connect(
830 EndpointSequence const& endpoints,
831 ConnectCondition connect_condition,
832 RangeConnectHandler&& handler)
833 {
834 return net::async_initiate<
835 RangeConnectHandler,
836 void(error_code, typename Protocol::endpoint)>(
837 typename ops::run_connect_range_op{},
838 handler,
839 this,
840 endpoints,
841 connect_condition);
842 }
843
844 template<class Protocol, class Executor, class RatePolicy>
845 template<
846 class Iterator,
847 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, Iterator)) IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code,Iterator))848 BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator))
849 basic_stream<Protocol, Executor, RatePolicy>::
850 async_connect(
851 Iterator begin, Iterator end,
852 IteratorConnectHandler&& handler)
853 {
854 return net::async_initiate<
855 IteratorConnectHandler,
856 void(error_code, Iterator)>(
857 typename ops::run_connect_iter_op{},
858 handler,
859 this,
860 begin, end,
861 detail::any_endpoint{});
862 }
863
864 template<class Protocol, class Executor, class RatePolicy>
865 template<
866 class Iterator,
867 class ConnectCondition,
868 BOOST_ASIO_COMPLETION_TOKEN_FOR(void(error_code, Iterator)) IteratorConnectHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code,Iterator))869 BOOST_ASIO_INITFN_RESULT_TYPE(IteratorConnectHandler,void (error_code, Iterator))
870 basic_stream<Protocol, Executor, RatePolicy>::
871 async_connect(
872 Iterator begin, Iterator end,
873 ConnectCondition connect_condition,
874 IteratorConnectHandler&& handler)
875 {
876 return net::async_initiate<
877 IteratorConnectHandler,
878 void(error_code, Iterator)>(
879 typename ops::run_connect_iter_op{},
880 handler,
881 this,
882 begin, end,
883 connect_condition);
884 }
885
886 //------------------------------------------------------------------------------
887
888 template<class Protocol, class Executor, class RatePolicy>
889 template<class MutableBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)890 BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
891 basic_stream<Protocol, Executor, RatePolicy>::
892 async_read_some(
893 MutableBufferSequence const& buffers,
894 ReadHandler&& handler)
895 {
896 static_assert(net::is_mutable_buffer_sequence<
897 MutableBufferSequence>::value,
898 "MutableBufferSequence type requirements not met");
899 return net::async_initiate<
900 ReadHandler,
901 void(error_code, std::size_t)>(
902 typename ops::run_read_op{},
903 handler,
904 this,
905 buffers);
906 }
907
908 template<class Protocol, class Executor, class RatePolicy>
909 template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)910 BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
911 basic_stream<Protocol, Executor, RatePolicy>::
912 async_write_some(
913 ConstBufferSequence const& buffers,
914 WriteHandler&& handler)
915 {
916 static_assert(net::is_const_buffer_sequence<
917 ConstBufferSequence>::value,
918 "ConstBufferSequence type requirements not met");
919 return net::async_initiate<
920 WriteHandler,
921 void(error_code, std::size_t)>(
922 typename ops::run_write_op{},
923 handler,
924 this,
925 buffers);
926 }
927
928 //------------------------------------------------------------------------------
929 //
930 // Customization points
931 //
932
933 #if ! BOOST_BEAST_DOXYGEN
934
935 template<
936 class Protocol, class Executor, class RatePolicy>
937 void
beast_close_socket(basic_stream<Protocol,Executor,RatePolicy> & stream)938 beast_close_socket(
939 basic_stream<Protocol, Executor, RatePolicy>& stream)
940 {
941 error_code ec;
942 stream.socket().close(ec);
943 }
944
945 template<
946 class Protocol, class Executor, class RatePolicy>
947 void
teardown(role_type role,basic_stream<Protocol,Executor,RatePolicy> & stream,error_code & ec)948 teardown(
949 role_type role,
950 basic_stream<Protocol, Executor, RatePolicy>& stream,
951 error_code& ec)
952 {
953 using beast::websocket::teardown;
954 teardown(role, stream.socket(), ec);
955 }
956
957 template<
958 class Protocol, class Executor, class RatePolicy,
959 class TeardownHandler>
960 void
async_teardown(role_type role,basic_stream<Protocol,Executor,RatePolicy> & stream,TeardownHandler && handler)961 async_teardown(
962 role_type role,
963 basic_stream<Protocol, Executor, RatePolicy>& stream,
964 TeardownHandler&& handler)
965 {
966 using beast::websocket::async_teardown;
967 async_teardown(role, stream.socket(),
968 std::forward<TeardownHandler>(handler));
969 }
970
971 #endif
972
973 } // beast
974 } // boost
975
976 #endif
977