2017-05-10 12:03:00 -07:00
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
2017-07-20 13:40:34 -07:00
// Official repository: https://github.com/boostorg/beast
//
2017-05-10 12:03:00 -07:00
2017-07-20 13:40:34 -07:00
# include <boost/beast/core.hpp>
2017-05-10 12:03:00 -07:00
# include <boost/asio.hpp>
# include <cstddef>
2017-06-22 10:27:45 -07:00
# include <iostream>
2017-05-10 12:03:00 -07:00
# include <memory>
# include <utility>
2017-06-14 20:26:44 -07:00
//[example_core_echo_op_1
2017-06-04 17:25:55 -07:00
2017-06-20 21:28:17 -07:00
template <
class AsyncStream ,
class CompletionToken >
auto
async_echo ( AsyncStream & stream , CompletionToken & & token )
//]
2017-07-20 13:40:34 -07:00
- > boost : : beast : : async_return_type < CompletionToken , void ( boost : : beast : : error_code ) > ;
2017-06-20 21:28:17 -07:00
//[example_core_echo_op_2
2017-06-12 05:01:24 -07:00
/** Asynchronously read a line and echo it back.
This function is used to asynchronously read a line ending
2017-06-20 21:28:17 -07:00
in a carriage - return ( " CR " ) from the stream , and then write
it back . The function call always returns immediately . The
asynchronous operation will continue until one of the
following conditions is true :
2017-06-12 05:01:24 -07:00
@ li A line was read in and sent back on the stream
@ li An error occurs .
This operation is implemented in terms of one or more calls to
the stream ' s ` async_read_some ` and ` async_write_some ` functions ,
and is known as a < em > composed operation < / em > . The program must
ensure that the stream performs no other operations until this
operation completes . The implementation may read additional octets
that lie past the end of the line being read . These octets are
silently discarded .
@ param The stream to operate on . The type must meet the
requirements of @ b AsyncReadStream and @ AsyncWriteStream
@ param token The completion token to use . If this is a
completion handler , copies will be made as required .
2017-06-20 21:28:17 -07:00
The equivalent signature of the handler must be :
2017-06-12 05:01:24 -07:00
@ code
void handler (
2017-06-20 21:28:17 -07:00
error_code ec // result of operation
2017-06-12 05:01:24 -07:00
) ;
@ endcode
Regardless of whether the asynchronous operation completes
immediately or not , the handler will not be invoked from within
this function . Invocation of the handler will be performed in a
manner equivalent to using ` boost : : asio : : io_service : : post ` .
*/
template <
class AsyncStream ,
class CompletionToken >
2017-07-23 22:49:57 -07:00
boost : : beast : : async_return_type < /*< The [link beast.ref.boost__beast__async_return_type `async_return_type`] customizes the return value based on the completion token >*/
2017-06-12 05:01:24 -07:00
CompletionToken ,
2017-07-20 13:40:34 -07:00
void ( boost : : beast : : error_code ) > /*< This is the signature for the completion handler >*/
2017-06-12 05:01:24 -07:00
async_echo (
AsyncStream & stream ,
CompletionToken & & token ) ;
2017-06-04 17:25:55 -07:00
//]
2017-06-20 21:28:17 -07:00
//[example_core_echo_op_4
2017-06-04 17:25:55 -07:00
2017-05-10 12:03:00 -07:00
// This composed operation reads a line of input and echoes it back.
//
template < class AsyncStream , class Handler >
class echo_op
{
// This holds all of the state information required by the operation.
struct state
{
// The stream to read and write to
AsyncStream & stream ;
// Indicates what step in the operation's state machine
// to perform next, starting from zero.
int step = 0 ;
// The buffer used to hold the input and output data.
2017-06-20 21:28:17 -07:00
//
// We use a custom allocator for performance, this allows
// the implementation of the io_service to make efficient
// re-use of memory allocated by composed operations during
// a continuation.
2017-05-10 12:03:00 -07:00
//
2017-07-20 13:40:34 -07:00
boost : : asio : : basic_streambuf < boost : : beast : : handler_alloc < char , Handler > > buffer ;
2017-05-10 12:03:00 -07:00
// handler_ptr requires that the first parameter to the
// contained object constructor is a reference to the
// managed final completion handler.
//
explicit state ( Handler & handler , AsyncStream & stream_ )
: stream ( stream_ )
, buffer ( ( std : : numeric_limits < std : : size_t > : : max ) ( ) ,
2017-07-20 13:40:34 -07:00
boost : : beast : : handler_alloc < char , Handler > { handler } )
2017-05-10 12:03:00 -07:00
{
}
} ;
2017-06-20 21:28:17 -07:00
// The operation's data is kept in a cheap-to-copy smart
// pointer container called `handler_ptr`. This efficiently
// satisfies the CopyConstructible requirements of completion
// handlers.
//
// `handler_ptr` uses these memory allocation hooks associated
// with the final completion handler, in order to allocate the
// storage for `state`:
//
// asio_handler_allocate
// asio_handler_deallocate
2017-05-10 12:03:00 -07:00
//
2017-07-20 13:40:34 -07:00
boost : : beast : : handler_ptr < state , Handler > p_ ;
2017-05-10 12:03:00 -07:00
public :
// Boost.Asio requires that handlers are CopyConstructible.
// In some cases, it takes advantage of handlers that are
// MoveConstructible. This operation supports both.
//
echo_op ( echo_op & & ) = default ;
echo_op ( echo_op const & ) = default ;
// The constructor simply creates our state variables in
// the smart pointer container.
//
template < class DeducedHandler , class . . . Args >
echo_op ( AsyncStream & stream , DeducedHandler & & handler )
: p_ ( std : : forward < DeducedHandler > ( handler ) , stream )
{
}
2017-06-20 21:28:17 -07:00
// The entry point for this handler. This will get called
// as our intermediate operations complete. Definition below.
2017-05-10 12:03:00 -07:00
//
2017-07-20 13:40:34 -07:00
void operator ( ) ( boost : : beast : : error_code ec , std : : size_t bytes_transferred ) ;
2017-05-10 12:03:00 -07:00
2017-06-20 21:28:17 -07:00
// The next four functions are required for our class
// to meet the requirements for composed operations.
// Definitions and exposition will follow.
2017-05-10 12:03:00 -07:00
2017-06-20 21:28:17 -07:00
template < class AsyncStream_ , class Handler_ , class Function >
friend void asio_handler_invoke (
Function & & f , echo_op < AsyncStream_ , Handler_ > * op ) ;
2017-05-10 12:03:00 -07:00
2017-06-20 21:28:17 -07:00
template < class AsyncStream_ , class Handler_ >
friend void * asio_handler_allocate (
std : : size_t size , echo_op < AsyncStream_ , Handler_ > * op ) ;
2017-05-10 12:03:00 -07:00
2017-06-20 21:28:17 -07:00
template < class AsyncStream_ , class Handler_ >
friend void asio_handler_deallocate (
void * p , std : : size_t size , echo_op < AsyncStream_ , Handler_ > * op ) ;
2017-05-10 12:03:00 -07:00
2017-06-20 21:28:17 -07:00
template < class AsyncStream_ , class Handler_ >
friend bool asio_handler_is_continuation (
echo_op < AsyncStream_ , Handler_ > * op ) ;
2017-05-10 12:03:00 -07:00
} ;
2017-06-04 17:25:55 -07:00
//]
2017-06-20 21:28:17 -07:00
//[example_core_echo_op_5
2017-06-04 17:25:55 -07:00
2017-06-07 18:18:50 -07:00
// echo_op is callable with the signature void(error_code, bytes_transferred),
// allowing `*this` to be used as both a ReadHandler and a WriteHandler.
//
2017-05-10 12:03:00 -07:00
template < class AsyncStream , class Handler >
void echo_op < AsyncStream , Handler > : :
2017-07-20 13:40:34 -07:00
operator ( ) ( boost : : beast : : error_code ec , std : : size_t bytes_transferred )
2017-05-10 12:03:00 -07:00
{
// Store a reference to our state. The address of the state won't
// change, and this solves the problem where dereferencing the
// data member is undefined after a move.
auto & p = * p_ ;
// Now perform the next step in the state machine
switch ( ec ? 2 : p . step )
{
// initial entry
case 0 :
// read up to the first newline
p . step = 1 ;
2017-06-20 21:28:17 -07:00
return boost : : asio : : async_read_until ( p . stream , p . buffer , " \r " , std : : move ( * this ) ) ;
2017-05-10 12:03:00 -07:00
case 1 :
// write everything back
p . step = 2 ;
2017-05-28 19:49:41 -07:00
// async_read_until could have read past the newline,
// use buffer_prefix to make sure we only send one line
return boost : : asio : : async_write ( p . stream ,
2017-07-20 13:40:34 -07:00
boost : : beast : : buffer_prefix ( bytes_transferred , p . buffer . data ( ) ) , std : : move ( * this ) ) ;
2017-05-10 12:03:00 -07:00
case 2 :
2017-05-28 19:49:41 -07:00
p . buffer . consume ( bytes_transferred ) ;
2017-05-10 12:03:00 -07:00
break ;
}
2017-06-20 21:28:17 -07:00
// Invoke the final handler. The implementation of `handler_ptr`
// will deallocate the storage for the state before the handler
// is invoked. This is necessary to provide the
// destroy-before-invocation guarantee on handler memory
// customizations.
//
// If we wanted to pass any arguments to the handler which come
// from the `state`, they would have to be moved to the stack
// first or else undefined behavior results.
2017-05-10 12:03:00 -07:00
//
p_ . invoke ( ec ) ;
return ;
}
2017-06-04 17:25:55 -07:00
//]
2017-06-20 21:28:17 -07:00
//[example_core_echo_op_6
// Handler hook forwarding. These free functions invoke the hooks
// associated with the final completion handler. In effect, they
// make the Asio implementation treat our composed operation the
// same way it would treat the final completion handler for the
// purpose of memory allocation and invocation.
//
// Our implementation just passes the call through to the hook
// associated with the final handler. The "using" statements are
// structured to permit argument dependent lookup. Always use
// `std::addressof` or its equivalent to pass the pointer to the
// handler, otherwise an unwanted overload of `operator&` may be
// called instead.
template < class AsyncStream , class Handler , class Function >
void asio_handler_invoke (
Function & & f , echo_op < AsyncStream , Handler > * op )
{
using boost : : asio : : asio_handler_invoke ;
return asio_handler_invoke ( f , std : : addressof ( op - > p_ . handler ( ) ) ) ;
}
template < class AsyncStream , class Handler >
void * asio_handler_allocate (
std : : size_t size , echo_op < AsyncStream , Handler > * op )
{
using boost : : asio : : asio_handler_allocate ;
return asio_handler_allocate ( size , std : : addressof ( op - > p_ . handler ( ) ) ) ;
}
template < class AsyncStream , class Handler >
void asio_handler_deallocate (
void * p , std : : size_t size , echo_op < AsyncStream , Handler > * op )
{
using boost : : asio : : asio_handler_deallocate ;
return asio_handler_deallocate ( p , size ,
std : : addressof ( op - > p_ . handler ( ) ) ) ;
}
// Determines if the next asynchronous operation represents a
// continuation of the asynchronous flow of control associated
// with the final handler. If we are past step one, it means
// we have performed an asynchronous operation therefore any
// subsequent operation would represent a continuation.
// Otherwise, we propagate the handler's associated value of
// is_continuation. Getting this right means the implementation
// may schedule the invokation of the invoked functions more
// efficiently.
//
template < class AsyncStream , class Handler >
bool asio_handler_is_continuation ( echo_op < AsyncStream , Handler > * op )
{
// This next call is structured to permit argument
// dependent lookup to take effect.
using boost : : asio : : asio_handler_is_continuation ;
// Always use std::addressof to pass the pointer to the handler,
// otherwise an unwanted overload of operator& may be called instead.
return op - > p_ - > step > 1 | |
asio_handler_is_continuation ( std : : addressof ( op - > p_ . handler ( ) ) ) ;
}
//]
//[example_core_echo_op_3
2017-06-04 17:25:55 -07:00
template < class AsyncStream , class Handler >
class echo_op ;
2017-05-10 12:03:00 -07:00
// Read a line and echo it back
//
template < class AsyncStream , class CompletionToken >
2017-07-20 13:40:34 -07:00
boost : : beast : : async_return_type < CompletionToken , void ( boost : : beast : : error_code ) >
2017-05-10 12:03:00 -07:00
async_echo ( AsyncStream & stream , CompletionToken & & token )
{
// Make sure stream meets the requirements. We use static_assert
2017-05-28 19:49:41 -07:00
// to cause a friendly message instead of an error novel.
2017-05-10 12:03:00 -07:00
//
2017-07-20 13:40:34 -07:00
static_assert ( boost : : beast : : is_async_stream < AsyncStream > : : value ,
2017-05-10 12:03:00 -07:00
" AsyncStream requirements not met " ) ;
// This helper manages some of the handler's lifetime and
// uses the result and handler specializations associated with
// the completion token to help customize the return value.
//
2017-07-20 13:40:34 -07:00
boost : : beast : : async_completion < CompletionToken , void ( boost : : beast : : error_code ) > init { token } ;
2017-05-10 12:03:00 -07:00
// Create the composed operation and launch it. This is a constructor
2017-05-28 19:49:41 -07:00
// call followed by invocation of operator(). We use handler_type
2017-05-10 12:03:00 -07:00
// to convert the completion token into the correct handler type,
2017-06-20 21:28:17 -07:00
// allowing user-defined specializations of the async_result template
// to be used.
2017-05-10 12:03:00 -07:00
//
2017-07-20 13:40:34 -07:00
echo_op < AsyncStream , boost : : beast : : handler_type < CompletionToken , void ( boost : : beast : : error_code ) > > {
stream , init . completion_handler } ( boost : : beast : : error_code { } , 0 ) ;
2017-05-10 12:03:00 -07:00
// This hook lets the caller see a return value when appropriate.
// For example this might return std::future<error_code> if
// CompletionToken is boost::asio::use_future, or this might
// return an error code if CompletionToken specifies a coroutine.
//
return init . result . get ( ) ;
}
2017-06-04 17:25:55 -07:00
//]
2017-06-22 10:27:45 -07:00
int main ( int , char * * argv )
2017-05-10 12:03:00 -07:00
{
using address_type = boost : : asio : : ip : : address ;
using socket_type = boost : : asio : : ip : : tcp : : socket ;
using endpoint_type = boost : : asio : : ip : : tcp : : endpoint ;
// Create a listening socket, accept a connection, perform
// the echo, and then shut everything down and exit.
boost : : asio : : io_service ios ;
socket_type sock { ios } ;
2017-05-28 19:49:41 -07:00
boost : : asio : : ip : : tcp : : acceptor acceptor { ios } ;
endpoint_type ep { address_type : : from_string ( " 0.0.0.0 " ) , 0 } ;
acceptor . open ( ep . protocol ( ) ) ;
acceptor . bind ( ep ) ;
acceptor . listen ( ) ;
acceptor . accept ( sock ) ;
2017-05-10 12:03:00 -07:00
async_echo ( sock ,
2017-07-20 13:40:34 -07:00
[ & ] ( boost : : beast : : error_code ec )
2017-05-10 12:03:00 -07:00
{
2017-06-22 10:27:45 -07:00
if ( ec )
std : : cerr < < argv [ 0 ] < < " : " < < ec . message ( ) < < std : : endl ;
2017-05-10 12:03:00 -07:00
} ) ;
ios . run ( ) ;
return 0 ;
}