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_BASIC_STREAM_HPP
11 #define BOOST_BEAST_CORE_BASIC_STREAM_HPP
12 
13 #include <boost/beast/core/detail/config.hpp>
14 #include <boost/beast/core/detail/stream_base.hpp>
15 #include <boost/beast/core/error.hpp>
16 #include <boost/beast/core/rate_policy.hpp>
17 #include <boost/beast/core/role.hpp>
18 #include <boost/beast/core/stream_traits.hpp>
19 #include <boost/asio/async_result.hpp>
20 #include <boost/asio/basic_stream_socket.hpp>
21 #include <boost/asio/connect.hpp>
22 #include <boost/asio/executor.hpp>
23 #include <boost/asio/is_executor.hpp>
24 #include <boost/core/empty_value.hpp>
25 #include <boost/config/workaround.hpp>
26 #include <boost/enable_shared_from_this.hpp>
27 #include <boost/shared_ptr.hpp>
28 #include <chrono>
29 #include <limits>
30 #include <memory>
31 
32 #if ! BOOST_BEAST_DOXYGEN
33 namespace boost {
34 namespace asio {
35 namespace ssl {
36 template<typename> class stream;
37 } // ssl
38 } // asio
39 } // boost
40 #endif
41 
42 namespace boost {
43 namespace beast {
44 
45 /** A stream socket wrapper with timeouts, an executor, and a rate limit policy.
46 
47     This stream wraps a `net::basic_stream_socket` to provide
48     the following features:
49 
50     @li An <em>Executor</em> may be associated with the stream, which will
51     be used to invoke any completion handlers which do not already have
52     an associated executor. This achieves support for
53     <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>.
54 
55     @li Timeouts may be specified for each logical asynchronous operation
56     performing any reading, writing, or connecting.
57 
58     @li A <em>RatePolicy</em> may be associated with the stream, to implement
59     rate limiting through the policy's interface.
60 
61     Although the stream supports multiple concurrent outstanding asynchronous
62     operations, the stream object is not thread-safe. The caller is responsible
63     for ensuring that the stream is accessed from only one thread at a time.
64     This includes the times when the stream, and its underlying socket, are
65     accessed by the networking implementation. To meet this thread safety
66     requirement, all asynchronous operations must be performed by the stream
67     within the same implicit strand (only one thread `net::io_context::run`)
68     or within the same explicit strand, such as an instance of `net::strand`.
69 
70     Completion handlers with explicit associated executors (such as those
71     arising from use of `net::bind_executor`) will be invoked by the stream
72     using the associated executor. Otherwise, the completion handler will
73     be invoked by the executor associated with the stream upon construction.
74     The type of executor used with this stream must meet the following
75     requirements:
76 
77     @li Function objects submitted to the executor shall never run
78         concurrently with each other.
79 
80     The executor type `net::strand` meets these requirements. Use of a
81     strand as the executor in the stream class template offers an additional
82     notational convenience: the strand does not need to be specified in
83     each individual initiating function call.
84 
85     Unlike other stream wrappers, the underlying socket is accessed
86     through the @ref socket member function instead of `next_layer`.
87     This causes the @ref basic_stream to be returned in calls
88     to @ref get_lowest_layer.
89 
90     @par Usage
91 
92     To use this stream declare an instance of the class. Then, before
93     each logical operation for which a timeout is desired, call
94     @ref expires_after with a duration, or call @ref expires_at with a
95     time point. Alternatively, call @ref expires_never to disable the
96     timeout for subsequent logical operations. A logical operation
97     is any series of one or more direct or indirect calls to the timeout
98     stream's asynchronous read, asynchronous write, or asynchronous connect
99     functions.
100 
101     When a timeout is set and a mixed operation is performed (one that
102     includes both reads and writes, for example) the timeout applies
103     to all of the intermediate asynchronous operations used in the
104     enclosing operation. This allows timeouts to be applied to stream
105     algorithms which were not written specifically to allow for timeouts,
106     when those algorithms are passed a timeout stream with a timeout set.
107 
108     When a timeout occurs the socket will be closed, canceling any
109     pending I/O operations. The completion handlers for these canceled
110     operations will be invoked with the error @ref beast::error::timeout.
111 
112     @par Examples
113 
114     This function reads an HTTP request with a timeout, then sends the
115     HTTP response with a different timeout.
116 
117     @code
118     void process_http_1 (tcp_stream& stream, net::yield_context yield)
119     {
120         flat_buffer buffer;
121         http::request<http::empty_body> req;
122 
123         // Read the request, with a 15 second timeout
124         stream.expires_after(std::chrono::seconds(15));
125         http::async_read(stream, buffer, req, yield);
126 
127         // Calculate the response
128         http::response<http::string_body> res = make_response(req);
129 
130         // Send the response, with a 30 second timeout.
131         stream.expires_after (std::chrono::seconds(30));
132         http::async_write (stream, res, yield);
133     }
134     @endcode
135 
136     The example above could be expressed using a single timeout with a
137     simple modification. The function that follows first reads an HTTP
138     request then sends the HTTP response, with a single timeout that
139     applies to the entire combined operation of reading and writing:
140 
141     @code
142     void process_http_2 (tcp_stream& stream, net::yield_context yield)
143     {
144         flat_buffer buffer;
145         http::request<http::empty_body> req;
146 
147         // Require that the read and write combined take no longer than 30 seconds
148         stream.expires_after(std::chrono::seconds(30));
149 
150         http::async_read(stream, buffer, req, yield);
151 
152         http::response<http::string_body> res = make_response(req);
153         http::async_write (stream, res, yield);
154     }
155     @endcode
156 
157     Some stream algorithms, such as `ssl::stream::async_handshake` perform
158     both reads and writes. A timeout set before calling the initiating function
159     of such composite stream algorithms will apply to the entire composite
160     operation. For example, a timeout may be set on performing the SSL handshake
161     thusly:
162 
163     @code
164     void do_ssl_handshake (net::ssl::stream<tcp_stream>& stream, net::yield_context yield)
165     {
166         // Require that the SSL handshake take no longer than 10 seconds
167         stream.expires_after(std::chrono::seconds(10));
168 
169         stream.async_handshake(net::ssl::stream_base::client, yield);
170     }
171     @endcode
172 
173     @par Blocking I/O
174 
175     Synchronous functions behave identically as that of the wrapped
176     `net::basic_stream_socket`. Timeouts are not available when performing
177     blocking calls.
178 
179     @tparam Protocol A type meeting the requirements of <em>Protocol</em>
180     representing the protocol the protocol to use for the basic stream socket.
181     A common choice is `net::ip::tcp`.
182 
183     @tparam Executor A type meeting the requirements of <em>Executor</em> to
184     be used for submitting all completion handlers which do not already have an
185     associated executor. If this type is omitted, the default of `net::any_io_executor`
186     will be used.
187 
188     @par Thread Safety
189     <em>Distinct objects</em>: Safe.@n
190     <em>Shared objects</em>: Unsafe. The application must also ensure
191     that all asynchronous operations are performed within the same
192     implicit or explicit strand.
193 
194     @see
195 
196     @li <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1322r0.html">[P1322R0] Networking TS enhancement to enable custom I/O executors</a>.
197 */
198 template<
199     class Protocol,
200     class Executor = net::any_io_executor,
201     class RatePolicy = unlimited_rate_policy
202 >
203 class basic_stream
204 #if ! BOOST_BEAST_DOXYGEN
205     : private detail::stream_base
206 #endif
207 {
208 public:
209     /// The type of the underlying socket.
210     using socket_type =
211         net::basic_stream_socket<Protocol, Executor>;
212 
213     /** The type of the executor associated with the stream.
214 
215         This will be the type of executor used to invoke completion
216         handlers which do not have an explicit associated executor.
217     */
218     using executor_type = beast::executor_type<socket_type>;
219 
220     /// Rebinds the stream type to another executor.
221     template<class Executor1>
222     struct rebind_executor
223     {
224         /// The stream type when rebound to the specified executor.
225         using other = basic_stream<
226             Protocol, Executor1, RatePolicy>;
227     };
228 
229     /// The protocol type.
230     using protocol_type = Protocol;
231 
232     /// The endpoint type.
233     using endpoint_type = typename Protocol::endpoint;
234 
235 private:
236     static_assert(
237         net::is_executor<Executor>::value || net::execution::is_executor<Executor>::value,
238         "Executor type requirements not met");
239 
240     struct impl_type
241         : boost::enable_shared_from_this<impl_type>
242         , boost::empty_value<RatePolicy>
243     {
244         // must come first
245         net::basic_stream_socket<
246             Protocol, Executor> socket;
247 
248         op_state read;
249         op_state write;
250 #if 0
251         net::basic_waitable_timer<
252             std::chrono::steady_clock,
253             net::wait_traits<
254                 std::chrono::steady_clock>,
255             Executor> timer; // rate timer;
256 #else
257         net::steady_timer timer;
258 #endif
259         int waiting = 0;
260 
261         impl_type(impl_type&&) = default;
262 
263         template<class... Args>
264         explicit
265         impl_type(std::false_type, Args&&...);
266 
267         template<class RatePolicy_, class... Args>
268         explicit
269         impl_type(std::true_type,
270             RatePolicy_&& policy, Args&&...);
271 
272         impl_type& operator=(impl_type&&) = delete;
273 
274         beast::executor_type<socket_type>
exboost::beast::basic_stream::impl_type275         ex() noexcept
276         {
277             return this->socket.get_executor();
278         }
279 
280         RatePolicy&
policyboost::beast::basic_stream::impl_type281         policy() noexcept
282         {
283             return this->boost::empty_value<RatePolicy>::get();
284         }
285 
286         RatePolicy const&
policyboost::beast::basic_stream::impl_type287         policy() const noexcept
288         {
289             return this->boost::empty_value<RatePolicy>::get();
290         }
291 
292         template<class Executor2>
293         void on_timer(Executor2 const& ex2);
294 
295         void reset();           // set timeouts to never
296         void close() noexcept;  // cancel everything
297     };
298 
299     // We use shared ownership for the state so it can
300     // outlive the destruction of the stream_socket object,
301     // in the case where there is no outstanding read or write
302     // but the implementation is still waiting on a timer.
303     boost::shared_ptr<impl_type> impl_;
304 
305     template<class Executor2>
306     struct timeout_handler;
307 
308     struct ops;
309 
310 #if ! BOOST_BEAST_DOXYGEN
311     // boost::asio::ssl::stream needs these
312     // DEPRECATED
313     template<class>
314     friend class boost::asio::ssl::stream;
315     // DEPRECATED
316     using lowest_layer_type = socket_type;
317     // DEPRECATED
318     lowest_layer_type&
lowest_layer()319     lowest_layer() noexcept
320     {
321         return impl_->socket;
322     }
323     // DEPRECATED
324     lowest_layer_type const&
lowest_layer() const325     lowest_layer() const noexcept
326     {
327         return impl_->socket;
328     }
329 #endif
330 
331 public:
332     /** Destructor
333 
334         This function destroys the stream, cancelling any outstanding
335         asynchronous operations associated with the socket as if by
336         calling cancel.
337     */
338     ~basic_stream();
339 
340     /** Constructor
341 
342         This constructor creates the stream by forwarding all arguments
343         to the underlying socket. The socket then needs to be open and
344         connected or accepted before data can be sent or received on it.
345 
346         @param args A list of parameters forwarded to the constructor of
347         the underlying socket.
348     */
349 #if BOOST_BEAST_DOXYGEN
350     template<class... Args>
351     explicit
352     basic_stream(Args&&... args);
353 #else
354     template<class Arg0, class... Args,
355         class = typename std::enable_if<
356         ! std::is_constructible<RatePolicy, Arg0>::value>::type>
357     explicit
358     basic_stream(Arg0&& argo, Args&&... args);
359 #endif
360 
361     /** Constructor
362 
363         This constructor creates the stream with the specified rate
364         policy, and forwards all remaining arguments to the underlying
365         socket. The socket then needs to be open and connected or
366         accepted before data can be sent or received on it.
367 
368         @param policy The rate policy object to use. The stream will
369         take ownership of this object by decay-copy.
370 
371         @param args A list of parameters forwarded to the constructor of
372         the underlying socket.
373     */
374 #if BOOST_BEAST_DOXYGEN
375     template<class RatePolicy_, class... Args>
376     explicit
377     basic_stream(RatePolicy_&& policy, Args&&... args);
378 #else
379     template<class RatePolicy_, class Arg0, class... Args,
380         class = typename std::enable_if<
381             std::is_constructible<
382                 RatePolicy, RatePolicy_>::value>::type>
383     basic_stream(
384         RatePolicy_&& policy, Arg0&& arg, Args&&... args);
385 #endif
386 
387     /** Move constructor
388 
389         @param other The other object from which the move will occur.
390 
391         @note Following the move, the moved-from object is in the
392         same state as if newly constructed.
393     */
394     basic_stream(basic_stream&& other);
395 
396     /// Move assignment (deleted).
397     basic_stream& operator=(basic_stream&&) = delete;
398 
399     /// Return a reference to the underlying socket
400     socket_type&
socket()401     socket() noexcept
402     {
403         return impl_->socket;
404     }
405 
406     /// Return a reference to the underlying socket
407     socket_type const&
socket() const408     socket() const noexcept
409     {
410         return impl_->socket;
411     }
412 
413     /** Release ownership of the underlying socket.
414 
415         This function causes all outstanding asynchronous connect,
416         read, and write operations to be canceled as if by a call
417         to @ref cancel. Ownership of the underlying socket is then
418         transferred to the caller.
419     */
420     socket_type
421     release_socket();
422 
423     //--------------------------------------------------------------------------
424 
425     /// Returns the rate policy associated with the object
426     RatePolicy&
rate_policy()427     rate_policy() noexcept
428     {
429         return impl_->policy();
430     }
431 
432     /// Returns the rate policy associated with the object
433     RatePolicy const&
rate_policy() const434     rate_policy() const noexcept
435     {
436         return impl_->policy();
437     }
438 
439     /** Set the timeout for the next logical operation.
440 
441         This sets either the read timer, the write timer, or
442         both timers to expire after the specified amount of time
443         has elapsed. If a timer expires when the corresponding
444         asynchronous operation is outstanding, the stream will be
445         closed and any outstanding operations will complete with the
446         error @ref beast::error::timeout. Otherwise, if the timer
447         expires while no operations are outstanding, and the expiraton
448         is not set again, the next operation will time out immediately.
449 
450         The timer applies collectively to any asynchronous reads
451         or writes initiated after the expiration is set, until the
452         expiration is set again. A call to @ref async_connect
453         counts as both a read and a write.
454 
455         @param expiry_time The amount of time after which a logical
456         operation should be considered timed out.
457     */
458     void
459     expires_after(
460         net::steady_timer::duration expiry_time);
461 
462     /** Set the timeout for the next logical operation.
463 
464         This sets either the read timer, the write timer, or both
465         timers to expire at the specified time point. If a timer
466         expires when the corresponding asynchronous operation is
467         outstanding, the stream will be closed and any outstanding
468         operations will complete with the error @ref beast::error::timeout.
469         Otherwise, if the timer expires while no operations are outstanding,
470         and the expiraton is not set again, the next operation will time out
471         immediately.
472 
473         The timer applies collectively to any asynchronous reads
474         or writes initiated after the expiration is set, until the
475         expiration is set again. A call to @ref async_connect
476         counts as both a read and a write.
477 
478         @param expiry_time The time point after which a logical
479         operation should be considered timed out.
480     */
481     void
482     expires_at(net::steady_timer::time_point expiry_time);
483 
484     /// Disable the timeout for the next logical operation.
485     void
486     expires_never();
487 
488     /** Cancel all asynchronous operations associated with the socket.
489 
490         This function causes all outstanding asynchronous connect,
491         read, and write operations to finish immediately. Completion
492         handlers for cancelled operations will receive the error
493         `net::error::operation_aborted`. Completion handlers not
494         yet invoked whose operations have completed, will receive
495         the error corresponding to the result of the operation (which
496         may indicate success).
497     */
498     void
499     cancel();
500 
501     /** Close the timed stream.
502 
503         This cancels all of the outstanding asynchronous operations
504         as if by calling @ref cancel, and closes the underlying socket.
505     */
506     void
507     close();
508 
509     //--------------------------------------------------------------------------
510 
511     /** Get the executor associated with the object.
512 
513         This function may be used to obtain the executor object that the
514         stream uses to dispatch completion handlers without an assocaited
515         executor.
516 
517         @return A copy of the executor that stream will use to dispatch handlers.
518     */
519     executor_type
get_executor()520     get_executor() noexcept
521     {
522         return impl_->ex();
523     }
524 
525     /** Connect the stream to the specified endpoint.
526 
527         This function is used to connect the underlying socket to the
528         specified remote endpoint. The function call will block until
529         the connection is successfully made or an error occurs.
530         The underlying socket is automatically opened if needed.
531         An automatically opened socket is not returned to the
532         closed state upon failure.
533 
534         @param ep The remote endpoint to connect to.
535 
536         @throws system_error Thrown on failure.
537 
538         @see connect
539     */
540     void
connect(endpoint_type const & ep)541     connect(endpoint_type const& ep)
542     {
543         socket().connect(ep);
544     }
545 
546     /** Connect the stream to the specified endpoint.
547 
548         This function is used to connect the underlying socket to the
549         specified remote endpoint. The function call will block until
550         the connection is successfully made or an error occurs.
551         The underlying socket is automatically opened if needed.
552         An automatically opened socket is not returned to the
553         closed state upon failure.
554 
555         @param ep The remote endpoint to connect to.
556 
557         @param ec Set to indicate what error occurred, if any.
558 
559         @see connect
560     */
561     void
connect(endpoint_type const & ep,error_code & ec)562     connect(endpoint_type const& ep, error_code& ec)
563     {
564         socket().connect(ep, ec);
565     }
566 
567     /** Establishes a connection by trying each endpoint in a sequence.
568 
569         This function attempts to connect the stream to one of a sequence of
570         endpoints by trying each endpoint until a connection is successfully
571         established.
572         The underlying socket is automatically opened if needed.
573         An automatically opened socket is not returned to the
574         closed state upon failure.
575 
576         The algorithm, known as a <em>composed operation</em>, is implemented
577         in terms of calls to the underlying socket's `connect` function.
578 
579         @param endpoints A sequence of endpoints.
580 
581         @returns The successfully connected endpoint.
582 
583         @throws system_error Thrown on failure. If the sequence is
584         empty, the associated error code is `net::error::not_found`.
585         Otherwise, contains the error from the last connection attempt.
586     */
587     template<class EndpointSequence
588     #if ! BOOST_BEAST_DOXYGEN
589         ,class = typename std::enable_if<
590             net::is_endpoint_sequence<
591                 EndpointSequence>::value>::type
592     #endif
593     >
594     typename Protocol::endpoint
connect(EndpointSequence const & endpoints)595     connect(EndpointSequence const& endpoints)
596     {
597         return net::connect(socket(), endpoints);
598     }
599 
600     /** Establishes a connection by trying each endpoint in a sequence.
601 
602         This function attempts to connect the stream to one of a sequence of
603         endpoints by trying each endpoint until a connection is successfully
604         established.
605         The underlying socket is automatically opened if needed.
606         An automatically opened socket is not returned to the
607         closed state upon failure.
608 
609         The algorithm, known as a <em>composed operation</em>, is implemented
610         in terms of calls to the underlying socket's `connect` function.
611 
612         @param endpoints A sequence of endpoints.
613 
614         @param ec Set to indicate what error occurred, if any. If the sequence is
615         empty, set to `net::error::not_found`. Otherwise, contains the error
616         from the last connection attempt.
617 
618         @returns On success, the successfully connected endpoint. Otherwise, a
619         default-constructed endpoint.
620     */
621     template<class EndpointSequence
622     #if ! BOOST_BEAST_DOXYGEN
623         ,class = typename std::enable_if<
624             net::is_endpoint_sequence<
625                 EndpointSequence>::value>::type
626     #endif
627     >
628     typename Protocol::endpoint
connect(EndpointSequence const & endpoints,error_code & ec)629     connect(
630         EndpointSequence const& endpoints,
631         error_code& ec
632     )
633     {
634         return net::connect(socket(), endpoints, ec);
635     }
636 
637     /** Establishes a connection by trying each endpoint in a sequence.
638 
639         This function attempts to connect the stream to one of a sequence of
640         endpoints by trying each endpoint until a connection is successfully
641         established.
642         The underlying socket is automatically opened if needed.
643         An automatically opened socket is not returned to the
644         closed state upon failure.
645 
646         The algorithm, known as a <em>composed operation</em>, is implemented
647         in terms of calls to the underlying socket's `connect` function.
648 
649         @param begin An iterator pointing to the start of a sequence of endpoints.
650 
651         @param end An iterator pointing to the end of a sequence of endpoints.
652 
653         @returns An iterator denoting the successfully connected endpoint.
654 
655         @throws system_error Thrown on failure. If the sequence is
656         empty, the associated error code is `net::error::not_found`.
657         Otherwise, contains the error from the last connection attempt.
658     */
659     template<class Iterator>
660     Iterator
connect(Iterator begin,Iterator end)661     connect(
662         Iterator begin, Iterator end)
663     {
664         return net::connect(socket(), begin, end);
665     }
666 
667     /** Establishes a connection by trying each endpoint in a sequence.
668 
669         This function attempts to connect the stream to one of a sequence of
670         endpoints by trying each endpoint until a connection is successfully
671         established.
672         The underlying socket is automatically opened if needed.
673         An automatically opened socket is not returned to the
674         closed state upon failure.
675 
676         The algorithm, known as a <em>composed operation</em>, is implemented
677         in terms of calls to the underlying socket's `connect` function.
678 
679         @param begin An iterator pointing to the start of a sequence of endpoints.
680 
681         @param end An iterator pointing to the end of a sequence of endpoints.
682 
683         @param ec Set to indicate what error occurred, if any. If the sequence is
684         empty, set to boost::asio::error::not_found. Otherwise, contains the error
685         from the last connection attempt.
686 
687         @returns On success, an iterator denoting the successfully connected
688         endpoint. Otherwise, the end iterator.
689     */
690     template<class Iterator>
691     Iterator
connect(Iterator begin,Iterator end,error_code & ec)692     connect(
693         Iterator begin, Iterator end,
694         error_code& ec)
695     {
696         return net::connect(socket(), begin, end, ec);
697     }
698 
699     /** Establishes a connection by trying each endpoint in a sequence.
700 
701         This function attempts to connect the stream to one of a sequence of
702         endpoints by trying each endpoint until a connection is successfully
703         established.
704         The underlying socket is automatically opened if needed.
705         An automatically opened socket is not returned to the
706         closed state upon failure.
707 
708         The algorithm, known as a <em>composed operation</em>, is implemented
709         in terms of calls to the underlying socket's `connect` function.
710 
711         @param endpoints A sequence of endpoints.
712 
713         @param connect_condition A function object that is called prior to each
714         connection attempt. The signature of the function object must be:
715         @code
716         bool connect_condition(
717             error_code const& ec,
718             typename Protocol::endpoint const& next);
719         @endcode
720         The @c ec parameter contains the result from the most recent connect
721         operation. Before the first connection attempt, @c ec is always set to
722         indicate success. The @c next parameter is the next endpoint to be tried.
723         The function object should return true if the next endpoint should be tried,
724         and false if it should be skipped.
725 
726         @returns The successfully connected endpoint.
727 
728         @throws boost::system::system_error Thrown on failure. If the sequence is
729         empty, the associated error code is `net::error::not_found`.
730         Otherwise, contains the error from the last connection attempt.
731     */
732     template<
733         class EndpointSequence, class ConnectCondition
734     #if ! BOOST_BEAST_DOXYGEN
735         ,class = typename std::enable_if<
736             net::is_endpoint_sequence<
737                 EndpointSequence>::value>::type
738     #endif
739     >
740     typename Protocol::endpoint
connect(EndpointSequence const & endpoints,ConnectCondition connect_condition)741     connect(
742         EndpointSequence const& endpoints,
743         ConnectCondition connect_condition
744     )
745     {
746         return net::connect(socket(), endpoints, connect_condition);
747     }
748 
749     /** Establishes a connection by trying each endpoint in a sequence.
750 
751         This function attempts to connect the stream to one of a sequence of
752         endpoints by trying each endpoint until a connection is successfully
753         established.
754         The underlying socket is automatically opened if needed.
755         An automatically opened socket is not returned to the
756         closed state upon failure.
757 
758         The algorithm, known as a <em>composed operation</em>, is implemented
759         in terms of calls to the underlying socket's `connect` function.
760 
761         @param endpoints A sequence of endpoints.
762 
763         @param connect_condition A function object that is called prior to each
764         connection attempt. The signature of the function object must be:
765         @code
766         bool connect_condition(
767             error_code const& ec,
768             typename Protocol::endpoint const& next);
769         @endcode
770         The @c ec parameter contains the result from the most recent connect
771         operation. Before the first connection attempt, @c ec is always set to
772         indicate success. The @c next parameter is the next endpoint to be tried.
773         The function object should return true if the next endpoint should be tried,
774         and false if it should be skipped.
775 
776         @param ec Set to indicate what error occurred, if any. If the sequence is
777         empty, set to `net::error::not_found`. Otherwise, contains the error
778         from the last connection attempt.
779 
780         @returns On success, the successfully connected endpoint. Otherwise, a
781         default-constructed endpoint.
782     */
783     template<
784         class EndpointSequence, class ConnectCondition
785     #if ! BOOST_BEAST_DOXYGEN
786         ,class = typename std::enable_if<
787             net::is_endpoint_sequence<
788                 EndpointSequence>::value>::type
789     #endif
790     >
791     typename Protocol::endpoint
connect(EndpointSequence const & endpoints,ConnectCondition connect_condition,error_code & ec)792     connect(
793         EndpointSequence const& endpoints,
794         ConnectCondition connect_condition,
795         error_code& ec)
796     {
797         return net::connect(socket(), endpoints, connect_condition, ec);
798     }
799 
800     /** Establishes a connection by trying each endpoint in a sequence.
801 
802         This function attempts to connect the stream to one of a sequence of
803         endpoints by trying each endpoint until a connection is successfully
804         established.
805         The underlying socket is automatically opened if needed.
806         An automatically opened socket is not returned to the
807         closed state upon failure.
808 
809         The algorithm, known as a <em>composed operation</em>, is implemented
810         in terms of calls to the underlying socket's `connect` function.
811 
812         @param begin An iterator pointing to the start of a sequence of endpoints.
813 
814         @param end An iterator pointing to the end of a sequence of endpoints.
815 
816         @param connect_condition A function object that is called prior to each
817         connection attempt. The signature of the function object must be:
818         @code
819         bool connect_condition(
820             error_code const& ec,
821             typename Protocol::endpoint const& next);
822         @endcode
823         The @c ec parameter contains the result from the most recent connect
824         operation. Before the first connection attempt, @c ec is always set to
825         indicate success. The @c next parameter is the next endpoint to be tried.
826         The function object should return true if the next endpoint should be tried,
827         and false if it should be skipped.
828 
829         @returns An iterator denoting the successfully connected endpoint.
830 
831         @throws boost::system::system_error Thrown on failure. If the sequence is
832         empty, the associated @c error_code is `net::error::not_found`.
833         Otherwise, contains the error from the last connection attempt.
834     */
835     template<
836         class Iterator, class ConnectCondition>
837     Iterator
838     connect(
839         Iterator begin, Iterator end,
840         ConnectCondition connect_condition)
841     {
842         return net::connect(socket(), begin, end, connect_condition);
843     }
844 
845     /** Establishes a connection by trying each endpoint in a sequence.
846 
847         This function attempts to connect the stream to one of a sequence of
848         endpoints by trying each endpoint until a connection is successfully
849         established.
850         The underlying socket is automatically opened if needed.
851         An automatically opened socket is not returned to the
852         closed state upon failure.
853 
854         The algorithm, known as a <em>composed operation</em>, is implemented
855         in terms of calls to the underlying socket's `connect` function.
856 
857         @param begin An iterator pointing to the start of a sequence of endpoints.
858 
859         @param end An iterator pointing to the end of a sequence of endpoints.
860 
861         @param connect_condition A function object that is called prior to each
862         connection attempt. The signature of the function object must be:
863         @code
864         bool connect_condition(
865             error_code const& ec,
866             typename Protocol::endpoint const& next);
867         @endcode
868         The @c ec parameter contains the result from the most recent connect
869         operation. Before the first connection attempt, @c ec is always set to
870         indicate success. The @c next parameter is the next endpoint to be tried.
871         The function object should return true if the next endpoint should be tried,
872         and false if it should be skipped.
873 
874         @param ec Set to indicate what error occurred, if any. If the sequence is
875         empty, set to `net::error::not_found`. Otherwise, contains the error
876         from the last connection attempt.
877 
878         @returns On success, an iterator denoting the successfully connected
879         endpoint. Otherwise, the end iterator.
880     */
881     template<
882         class Iterator, class ConnectCondition>
883     Iterator
884     connect(
885         Iterator begin, Iterator end,
886         ConnectCondition connect_condition,
887         error_code& ec)
888     {
889         return net::connect(socket(), begin, end, connect_condition, ec);
890     }
891 
892     /** Connect the stream to the specified endpoint asynchronously.
893 
894         This function is used to asynchronously connect the underlying
895         socket to the specified remote endpoint. The function call always
896         returns immediately.
897         The underlying socket is automatically opened if needed.
898         An automatically opened socket is not returned to the
899         closed state upon failure.
900 
901         If the timeout timer expires while the operation is outstanding,
902         the operation will be canceled and the completion handler will be
903         invoked with the error @ref error::timeout.
904 
905         @param ep The remote endpoint to which the underlying socket will be
906         connected. Copies will be made of the endpoint object as required.
907 
908         @param handler The completion handler to invoke when the operation
909         completes. The implementation takes ownership of the handler by
910         performing a decay-copy. The equivalent function signature of
911         the handler must be:
912         @code
913         void handler(
914             error_code ec         // Result of operation
915         );
916         @endcode
917         Regardless of whether the asynchronous operation completes
918         immediately or not, the handler will not be invoked from within
919         this function. Invocation of the handler will be performed in a
920         manner equivalent to using `net::post`.
921 
922         @see async_connect
923     */
924     template<
925         BOOST_BEAST_ASYNC_TPARAM1 ConnectHandler =
926             net::default_completion_token_t<executor_type>
927     >
928     BOOST_BEAST_ASYNC_RESULT1(ConnectHandler)
929     async_connect(
930         endpoint_type const& ep,
931         ConnectHandler&& handler =
932             net::default_completion_token_t<
933                 executor_type>{});
934 
935     /** Establishes a connection by trying each endpoint in a sequence asynchronously.
936 
937         This function attempts to connect the stream to one of a sequence of
938         endpoints by trying each endpoint until a connection is successfully
939         established.
940         The underlying socket is automatically opened if needed.
941         An automatically opened socket is not returned to the
942         closed state upon failure.
943 
944         The algorithm, known as a <em>composed asynchronous operation</em>, is
945         implemented in terms of calls to the underlying socket's `async_connect`
946         function.
947 
948         If the timeout timer expires while the operation is outstanding,
949         the current connection attempt will be canceled and the completion
950         handler will be invoked with the error @ref error::timeout.
951 
952         @param endpoints A sequence of endpoints. This this object must meet
953         the requirements of <em>EndpointSequence</em>.
954 
955         @param handler The completion handler to invoke when the operation
956         completes. The implementation takes ownership of the handler by
957         performing a decay-copy. The equivalent function signature of
958         the handler must be:
959         @code
960         void handler(
961             // Result of operation. if the sequence is empty, set to
962             // net::error::not_found. Otherwise, contains the
963             // error from the last connection attempt.
964             error_code const& error,
965 
966             // On success, the successfully connected endpoint.
967             // Otherwise, a default-constructed endpoint.
968             typename Protocol::endpoint const& endpoint
969         );
970         @endcode
971         Regardless of whether the asynchronous operation completes
972         immediately or not, the handler will not be invoked from within
973         this function. Invocation of the handler will be performed in a
974         manner equivalent to using `net::post`.
975     */
976     template<
977         class EndpointSequence,
978         BOOST_ASIO_COMPLETION_TOKEN_FOR(
979             void(error_code, typename Protocol::endpoint))
980             RangeConnectHandler =
981                 net::default_completion_token_t<executor_type>
982     #if ! BOOST_BEAST_DOXYGEN
983         ,class = typename std::enable_if<
984             net::is_endpoint_sequence<
985                 EndpointSequence>::value>::type
986     #endif
987     >
988     BOOST_ASIO_INITFN_RESULT_TYPE(
989         RangeConnectHandler,
990         void(error_code, typename Protocol::endpoint))
991     async_connect(
992         EndpointSequence const& endpoints,
993         RangeConnectHandler&& handler =
994             net::default_completion_token_t<executor_type>{});
995 
996     /** Establishes a connection by trying each endpoint in a sequence asynchronously.
997 
998         This function attempts to connect the stream to one of a sequence of
999         endpoints by trying each endpoint until a connection is successfully
1000         established.
1001         The underlying socket is automatically opened if needed.
1002         An automatically opened socket is not returned to the
1003         closed state upon failure.
1004 
1005         The algorithm, known as a <em>composed asynchronous operation</em>, is
1006         implemented in terms of calls to the underlying socket's `async_connect`
1007         function.
1008 
1009         If the timeout timer expires while the operation is outstanding,
1010         the current connection attempt will be canceled and the completion
1011         handler will be invoked with the error @ref error::timeout.
1012 
1013         @param endpoints A sequence of endpoints. This this object must meet
1014         the requirements of <em>EndpointSequence</em>.
1015 
1016         @param connect_condition A function object that is called prior to each
1017         connection attempt. The signature of the function object must be:
1018         @code
1019         bool connect_condition(
1020             error_code const& ec,
1021             typename Protocol::endpoint const& next);
1022         @endcode
1023         The @c ec parameter contains the result from the most recent connect
1024         operation. Before the first connection attempt, @c ec is always set to
1025         indicate success. The @c next parameter is the next endpoint to be tried.
1026         The function object should return true if the next endpoint should be tried,
1027         and false if it should be skipped.
1028 
1029         @param handler The completion handler to invoke when the operation
1030         completes. The implementation takes ownership of the handler by
1031         performing a decay-copy. The equivalent function signature of
1032         the handler must be:
1033         @code
1034         void handler(
1035             // Result of operation. if the sequence is empty, set to
1036             // net::error::not_found. Otherwise, contains the
1037             // error from the last connection attempt.
1038             error_code const& error,
1039 
1040             // On success, the successfully connected endpoint.
1041             // Otherwise, a default-constructed endpoint.
1042             typename Protocol::endpoint const& endpoint
1043         );
1044         @endcode
1045         Regardless of whether the asynchronous operation completes
1046         immediately or not, the handler will not be invoked from within
1047         this function. Invocation of the handler will be performed in a
1048         manner equivalent to using `net::post`.
1049 
1050         @par Example
1051         The following connect condition function object can be used to output
1052         information about the individual connection attempts:
1053         @code
1054         struct my_connect_condition
1055         {
1056             bool operator()(
1057                 error_code const& ec,
1058                 net::ip::tcp::endpoint const& next)
1059             {
1060                 if (ec)
1061                     std::cout << "Error: " << ec.message() << std::endl;
1062                 std::cout << "Trying: " << next << std::endl;
1063                 return true;
1064             }
1065         };
1066         @endcode
1067     */
1068     template<
1069         class EndpointSequence,
1070         class ConnectCondition,
1071         BOOST_ASIO_COMPLETION_TOKEN_FOR(
1072             void(error_code, typename Protocol::endpoint))
1073             RangeConnectHandler =
1074                 net::default_completion_token_t<executor_type>
1075     #if ! BOOST_BEAST_DOXYGEN
1076         ,class = typename std::enable_if<
1077             net::is_endpoint_sequence<
1078                 EndpointSequence>::value>::type
1079     #endif
1080     >
1081     BOOST_ASIO_INITFN_RESULT_TYPE(
1082         RangeConnectHandler,
1083         void(error_code, typename Protocol::endpoint))
1084     async_connect(
1085         EndpointSequence const& endpoints,
1086         ConnectCondition connect_condition,
1087         RangeConnectHandler&& handler =
1088             net::default_completion_token_t<
1089                 executor_type>{});
1090 
1091     /** Establishes a connection by trying each endpoint in a sequence asynchronously.
1092 
1093         This function attempts to connect the stream to one of a sequence of
1094         endpoints by trying each endpoint until a connection is successfully
1095         established.
1096         The underlying socket is automatically opened if needed.
1097         An automatically opened socket is not returned to the
1098         closed state upon failure.
1099 
1100         The algorithm, known as a <em>composed asynchronous operation</em>, is
1101         implemented in terms of calls to the underlying socket's `async_connect`
1102         function.
1103 
1104         If the timeout timer expires while the operation is outstanding,
1105         the current connection attempt will be canceled and the completion
1106         handler will be invoked with the error @ref error::timeout.
1107 
1108         @param begin An iterator pointing to the start of a sequence of endpoints.
1109 
1110         @param end An iterator pointing to the end of a sequence of endpoints.
1111 
1112         @param handler The completion handler to invoke when the operation
1113         completes. The implementation takes ownership of the handler by
1114         performing a decay-copy. The equivalent function signature of
1115         the handler must be:
1116         @code
1117         void handler(
1118             // Result of operation. if the sequence is empty, set to
1119             // net::error::not_found. Otherwise, contains the
1120             // error from the last connection attempt.
1121             error_code const& error,
1122 
1123             // On success, an iterator denoting the successfully
1124             // connected endpoint. Otherwise, the end iterator.
1125             Iterator iterator
1126         );
1127         @endcode
1128         Regardless of whether the asynchronous operation completes
1129         immediately or not, the handler will not be invoked from within
1130         this function. Invocation of the handler will be performed in a
1131         manner equivalent to using `net::post`.
1132     */
1133     template<
1134         class Iterator,
1135         BOOST_ASIO_COMPLETION_TOKEN_FOR(
1136             void(error_code, Iterator))
1137             IteratorConnectHandler =
1138                 net::default_completion_token_t<executor_type>>
1139     BOOST_ASIO_INITFN_RESULT_TYPE(
1140         IteratorConnectHandler,
1141         void(error_code, Iterator))
1142     async_connect(
1143         Iterator begin, Iterator end,
1144         IteratorConnectHandler&& handler =
1145             net::default_completion_token_t<executor_type>{});
1146 
1147     /** Establishes a connection by trying each endpoint in a sequence asynchronously.
1148 
1149         This function attempts to connect the stream to one of a sequence of
1150         endpoints by trying each endpoint until a connection is successfully
1151         established.
1152         The algorithm, known as a <em>composed asynchronous operation</em>, is
1153         implemented in terms of calls to the underlying socket's `async_connect`
1154         function.
1155 
1156         If the timeout timer expires while the operation is outstanding,
1157         the current connection attempt will be canceled and the completion
1158         handler will be invoked with the error @ref error::timeout.
1159 
1160         @param begin An iterator pointing to the start of a sequence of endpoints.
1161 
1162         @param end An iterator pointing to the end of a sequence of endpoints.
1163 
1164         @param connect_condition A function object that is called prior to each
1165         connection attempt. The signature of the function object must be:
1166         @code
1167         bool connect_condition(
1168             error_code const& ec,
1169             typename Protocol::endpoint const& next);
1170         @endcode
1171 
1172         @param handler The completion handler to invoke when the operation
1173         completes. The implementation takes ownership of the handler by
1174         performing a decay-copy. The equivalent function signature of
1175         the handler must be:
1176         @code
1177         void handler(
1178             // Result of operation. if the sequence is empty, set to
1179             // net::error::not_found. Otherwise, contains the
1180             // error from the last connection attempt.
1181             error_code const& error,
1182 
1183             // On success, an iterator denoting the successfully
1184             // connected endpoint. Otherwise, the end iterator.
1185             Iterator iterator
1186         );
1187         @endcode
1188         Regardless of whether the asynchronous operation completes
1189         immediately or not, the handler will not be invoked from within
1190         this function. Invocation of the handler will be performed in a
1191         manner equivalent to using `net::post`.
1192     */
1193     template<
1194         class Iterator,
1195         class ConnectCondition,
1196         BOOST_ASIO_COMPLETION_TOKEN_FOR(
1197             void(error_code, Iterator))
1198             IteratorConnectHandler =
1199                 net::default_completion_token_t<executor_type>>
1200     BOOST_ASIO_INITFN_RESULT_TYPE(
1201         IteratorConnectHandler,
1202         void(error_code, Iterator))
1203     async_connect(
1204         Iterator begin, Iterator end,
1205         ConnectCondition connect_condition,
1206         IteratorConnectHandler&& handler =
1207             net::default_completion_token_t<executor_type>{});
1208 
1209     //--------------------------------------------------------------------------
1210 
1211     /** Read some data.
1212 
1213         This function is used to read some data from the stream.
1214 
1215         The call blocks until one of the following is true:
1216 
1217         @li One or more bytes are read from the stream.
1218 
1219         @li An error occurs.
1220 
1221         @param buffers The buffers into which the data will be read. If the
1222         size of the buffers is zero bytes, the call always returns
1223         immediately with no error.
1224 
1225         @returns The number of bytes read.
1226 
1227         @throws system_error Thrown on failure.
1228 
1229         @note The `read_some` operation may not receive all of the requested
1230         number of bytes. Consider using the function `net::read` if you need
1231         to ensure that the requested amount of data is read before the
1232         blocking operation completes.
1233     */
1234     template<class MutableBufferSequence>
1235     std::size_t
read_some(MutableBufferSequence const & buffers)1236     read_some(MutableBufferSequence const& buffers)
1237     {
1238         return impl_->socket.read_some(buffers);
1239     }
1240 
1241     /** Read some data.
1242 
1243         This function is used to read some data from the underlying socket.
1244 
1245         The call blocks until one of the following is true:
1246 
1247         @li One or more bytes are read from the stream.
1248 
1249         @li An error occurs.
1250 
1251         @param buffers The buffers into which the data will be read. If the
1252         size of the buffers is zero bytes, the call always returns
1253         immediately with no error.
1254 
1255         @param ec Set to indicate what error occurred, if any.
1256 
1257         @returns The number of bytes read.
1258 
1259         @note The `read_some` operation may not receive all of the requested
1260         number of bytes. Consider using the function `net::read` if you need
1261         to ensure that the requested amount of data is read before the
1262         blocking operation completes.
1263     */
1264     template<class MutableBufferSequence>
1265     std::size_t
read_some(MutableBufferSequence const & buffers,error_code & ec)1266     read_some(
1267         MutableBufferSequence const& buffers,
1268         error_code& ec)
1269     {
1270         return impl_->socket.read_some(buffers, ec);
1271     }
1272 
1273     /** Read some data asynchronously.
1274 
1275         This function is used to asynchronously read data from the stream.
1276 
1277         This call always returns immediately. The asynchronous operation
1278         will continue until one of the following conditions is true:
1279 
1280         @li One or more bytes are read from the stream.
1281 
1282         @li An error occurs.
1283 
1284         The algorithm, known as a <em>composed asynchronous operation</em>,
1285         is implemented in terms of calls to the next layer's `async_read_some`
1286         function. The program must ensure that no other calls to @ref read_some
1287         or @ref async_read_some are performed until this operation completes.
1288 
1289         If the timeout timer expires while the operation is outstanding,
1290         the operation will be canceled and the completion handler will be
1291         invoked with the error @ref error::timeout.
1292 
1293         @param buffers The buffers into which the data will be read. If the size
1294         of the buffers is zero bytes, the operation always completes immediately
1295         with no error.
1296         Although the buffers object may be copied as necessary, ownership of the
1297         underlying memory blocks is retained by the caller, which must guarantee
1298         that they remain valid until the handler is called.
1299 
1300         @param handler The completion handler to invoke when the operation
1301         completes. The implementation takes ownership of the handler by
1302         performing a decay-copy. The equivalent function signature of
1303         the handler must be:
1304         @code
1305         void handler(
1306             error_code error,               // Result of operation.
1307             std::size_t bytes_transferred   // Number of bytes read.
1308         );
1309         @endcode
1310         Regardless of whether the asynchronous operation completes
1311         immediately or not, the handler will not be invoked from within
1312         this function. Invocation of the handler will be performed in a
1313         manner equivalent to using `net::post`.
1314 
1315         @note The `async_read_some` operation may not receive all of the requested
1316         number of bytes. Consider using the function `net::async_read` if you need
1317         to ensure that the requested amount of data is read before the asynchronous
1318         operation completes.
1319     */
1320     template<
1321         class MutableBufferSequence,
1322         BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
1323             net::default_completion_token_t<executor_type>
1324     >
1325     BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
1326     async_read_some(
1327         MutableBufferSequence const& buffers,
1328         ReadHandler&& handler =
1329             net::default_completion_token_t<executor_type>{}
1330     );
1331 
1332     /** Write some data.
1333 
1334         This function is used to write some data to the stream.
1335 
1336         The call blocks until one of the following is true:
1337 
1338         @li One or more bytes are written to the stream.
1339 
1340         @li An error occurs.
1341 
1342         @param buffers The buffers from which the data will be written. If the
1343         size of the buffers is zero bytes, the call always returns immediately
1344         with no error.
1345 
1346         @returns The number of bytes written.
1347 
1348         @throws system_error Thrown on failure.
1349 
1350         @note The `write_some` operation may not transmit all of the requested
1351         number of bytes. Consider using the function `net::write` if you need
1352         to ensure that the requested amount of data is written before the
1353         blocking operation completes.
1354     */
1355     template<class ConstBufferSequence>
1356     std::size_t
write_some(ConstBufferSequence const & buffers)1357     write_some(ConstBufferSequence const& buffers)
1358     {
1359         return impl_->socket.write_some(buffers);
1360     }
1361 
1362     /** Write some data.
1363 
1364         This function is used to write some data to the stream.
1365 
1366         The call blocks until one of the following is true:
1367 
1368         @li One or more bytes are written to the stream.
1369 
1370         @li An error occurs.
1371 
1372         @param buffers The buffers from which the data will be written. If the
1373         size of the buffers is zero bytes, the call always returns immediately
1374         with no error.
1375 
1376         @param ec Set to indicate what error occurred, if any.
1377 
1378         @returns The number of bytes written.
1379 
1380         @throws system_error Thrown on failure.
1381 
1382         @note The `write_some` operation may not transmit all of the requested
1383         number of bytes. Consider using the function `net::write` if you need
1384         to ensure that the requested amount of data is written before the
1385         blocking operation completes.
1386     */
1387     template<class ConstBufferSequence>
1388     std::size_t
write_some(ConstBufferSequence const & buffers,error_code & ec)1389     write_some(
1390         ConstBufferSequence const& buffers,
1391         error_code& ec)
1392     {
1393         return impl_->socket.write_some(buffers, ec);
1394     }
1395 
1396     /** Write some data asynchronously.
1397 
1398         This function is used to asynchronously write data to the underlying socket.
1399 
1400         This call always returns immediately. The asynchronous operation
1401         will continue until one of the following conditions is true:
1402 
1403         @li One or more bytes are written to the stream.
1404 
1405         @li An error occurs.
1406 
1407         The algorithm, known as a <em>composed asynchronous operation</em>,
1408         is implemented in terms of calls to the next layer's `async_write_some`
1409         function. The program must ensure that no other calls to @ref async_write_some
1410         are performed until this operation completes.
1411 
1412         If the timeout timer expires while the operation is outstanding,
1413         the operation will be canceled and the completion handler will be
1414         invoked with the error @ref error::timeout.
1415 
1416         @param buffers The buffers from which the data will be written. If the
1417         size of the buffers is zero bytes, the operation  always completes
1418         immediately with no error.
1419         Although the buffers object may be copied as necessary, ownership of the
1420         underlying memory blocks is retained by the caller, which must guarantee
1421         that they remain valid until the handler is called.
1422 
1423         @param handler The completion handler to invoke when the operation
1424         completes. The implementation takes ownership of the handler by
1425         performing a decay-copy. The equivalent function signature of
1426         the handler must be:
1427         @code
1428         void handler(
1429             error_code error,               // Result of operation.
1430             std::size_t bytes_transferred   // Number of bytes written.
1431         );
1432         @endcode
1433         Regardless of whether the asynchronous operation completes
1434         immediately or not, the handler will not be invoked from within
1435         this function. Invocation of the handler will be performed in a
1436         manner equivalent to using `net::post`.
1437 
1438         @note The `async_write_some` operation may not transmit all of the requested
1439         number of bytes. Consider using the function `net::async_write` if you need
1440         to ensure that the requested amount of data is sent before the asynchronous
1441         operation completes.
1442     */
1443     template<
1444         class ConstBufferSequence,
1445         BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
1446             net::default_completion_token_t<Executor>
1447     >
1448     BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
1449     async_write_some(
1450         ConstBufferSequence const& buffers,
1451         WriteHandler&& handler =
1452             net::default_completion_token_t<Executor>{});
1453 };
1454 
1455 } // beast
1456 } // boost
1457 
1458 #include <boost/beast/core/impl/basic_stream.hpp>
1459 
1460 #endif
1461