]>
git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/example/echo-op/echo_op.cpp
2 // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // Official repository: https://github.com/boostorg/beast
10 #include <boost/beast/core.hpp>
11 #include <boost/asio.hpp>
17 //[example_core_echo_op_1
21 class CompletionToken
>
23 async_echo(AsyncStream
& stream
, CompletionToken
&& token
)
26 -> BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken
, void(boost::beast::error_code
));
28 //[example_core_echo_op_2
30 /** Asynchronously read a line and echo it back.
32 This function is used to asynchronously read a line ending
33 in a carriage-return ("CR") from the stream, and then write
34 it back. The function call always returns immediately. The
35 asynchronous operation will continue until one of the
36 following conditions is true:
38 @li A line was read in and sent back on the stream
42 This operation is implemented in terms of one or more calls to
43 the stream's `async_read_some` and `async_write_some` functions,
44 and is known as a <em>composed operation</em>. The program must
45 ensure that the stream performs no other operations until this
46 operation completes. The implementation may read additional octets
47 that lie past the end of the line being read. These octets are
50 @param The stream to operate on. The type must meet the
51 requirements of @b AsyncReadStream and @AsyncWriteStream
53 @param token The completion token to use. If this is a
54 completion handler, copies will be made as required.
55 The equivalent signature of the handler must be:
58 error_code ec // result of operation
61 Regardless of whether the asynchronous operation completes
62 immediately or not, the handler will not be invoked from within
63 this function. Invocation of the handler will be performed in a
64 manner equivalent to using `boost::asio::io_context::post`.
68 class CompletionToken
>
69 BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/
71 void(boost::beast::error_code
)) /*< This is the signature for the completion handler >*/
74 CompletionToken
&& token
);
78 //[example_core_echo_op_4
80 // This composed operation reads a line of input and echoes it back.
82 template<class AsyncStream
, class Handler
>
85 // This holds all of the state information required by the operation.
88 // The stream to read and write to
91 // Indicates what step in the operation's state machine
92 // to perform next, starting from zero.
95 // The buffer used to hold the input and output data.
97 // We use a custom allocator for performance, this allows
98 // the implementation of the io_context to make efficient
99 // re-use of memory allocated by composed operations during
102 boost::asio::basic_streambuf
<typename
std::allocator_traits
<
103 boost::asio::associated_allocator_t
<Handler
> >::
104 template rebind_alloc
<char> > buffer
;
106 // handler_ptr requires that the first parameter to the
107 // contained object constructor is a reference to the
108 // managed final completion handler.
110 explicit state(Handler
& handler
, AsyncStream
& stream_
)
112 , buffer((std::numeric_limits
<std::size_t>::max
)(),
113 boost::asio::get_associated_allocator(handler
))
118 // The operation's data is kept in a cheap-to-copy smart
119 // pointer container called `handler_ptr`. This efficiently
120 // satisfies the CopyConstructible requirements of completion
121 // handlers with expensive-to-copy state.
123 // `handler_ptr` uses the allocator associated with the final
124 // completion handler, in order to allocate the storage for `state`.
126 boost::beast::handler_ptr
<state
, Handler
> p_
;
129 // Boost.Asio requires that handlers are CopyConstructible.
130 // In some cases, it takes advantage of handlers that are
131 // MoveConstructible. This operation supports both.
133 echo_op(echo_op
&&) = default;
134 echo_op(echo_op
const&) = default;
136 // The constructor simply creates our state variables in
137 // the smart pointer container.
139 template<class DeducedHandler
, class... Args
>
140 echo_op(AsyncStream
& stream
, DeducedHandler
&& handler
)
141 : p_(std::forward
<DeducedHandler
>(handler
), stream
)
145 // Associated allocator support. This is Asio's system for
146 // allowing the final completion handler to customize the
147 // memory allocation strategy used for composed operation
148 // states. A composed operation should use the same allocator
149 // as the final handler. These declarations achieve that.
151 using allocator_type
=
152 boost::asio::associated_allocator_t
<Handler
>;
155 get_allocator() const noexcept
157 return boost::asio::get_associated_allocator(p_
.handler());
160 // Executor hook. This is Asio's system for customizing the
161 // manner in which asynchronous completion handlers are invoked.
162 // A composed operation needs to use the same executor to invoke
163 // intermediate completion handlers as that used to invoke the
166 using executor_type
= boost::asio::associated_executor_t
<
167 Handler
, decltype(std::declval
<AsyncStream
&>().get_executor())>;
169 executor_type
get_executor() const noexcept
171 return boost::asio::get_associated_executor(
172 p_
.handler(), p_
->stream
.get_executor());
175 // The entry point for this handler. This will get called
176 // as our intermediate operations complete. Definition below.
178 void operator()(boost::beast::error_code ec
, std::size_t bytes_transferred
);
183 //[example_core_echo_op_5
185 // echo_op is callable with the signature void(error_code, bytes_transferred),
186 // allowing `*this` to be used as both a ReadHandler and a WriteHandler.
188 template<class AsyncStream
, class Handler
>
189 void echo_op
<AsyncStream
, Handler
>::
190 operator()(boost::beast::error_code ec
, std::size_t bytes_transferred
)
192 // Store a reference to our state. The address of the state won't
193 // change, and this solves the problem where dereferencing the
194 // data member is undefined after a move.
197 // Now perform the next step in the state machine
198 switch(ec
? 2 : p
.step
)
202 // read up to the first newline
204 return boost::asio::async_read_until(p
.stream
, p
.buffer
, "\r", std::move(*this));
207 // write everything back
209 // async_read_until could have read past the newline,
210 // use buffers_prefix to make sure we only send one line
211 return boost::asio::async_write(p
.stream
,
212 boost::beast::buffers_prefix(bytes_transferred
, p
.buffer
.data()), std::move(*this));
215 p
.buffer
.consume(bytes_transferred
);
219 // Invoke the final handler. The implementation of `handler_ptr`
220 // will deallocate the storage for the state before the handler
221 // is invoked. This is necessary to provide the
222 // destroy-before-invocation guarantee on handler memory
225 // If we wanted to pass any arguments to the handler which come
226 // from the `state`, they would have to be moved to the stack
227 // first or else undefined behavior results.
235 //[example_core_echo_op_3
237 template<class AsyncStream
, class Handler
>
240 // Read a line and echo it back
242 template<class AsyncStream
, class CompletionToken
>
243 BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken
, void(boost::beast::error_code
))
244 async_echo(AsyncStream
& stream
, CompletionToken
&& token
)
246 // Make sure stream meets the requirements. We use static_assert
247 // to cause a friendly message instead of an error novel.
249 static_assert(boost::beast::is_async_stream
<AsyncStream
>::value
,
250 "AsyncStream requirements not met");
252 // This helper manages some of the handler's lifetime and
253 // uses the result and handler specializations associated with
254 // the completion token to help customize the return value.
256 boost::asio::async_completion
<CompletionToken
, void(boost::beast::error_code
)> init
{token
};
258 // Create the composed operation and launch it. This is a constructor
259 // call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE
260 // to convert the completion token into the correct handler type,
261 // allowing user-defined specializations of the async_result template
266 BOOST_ASIO_HANDLER_TYPE(
267 CompletionToken
, void(boost::beast::error_code
))>{
269 init
.completion_handler
}(boost::beast::error_code
{}, 0);
271 // This hook lets the caller see a return value when appropriate.
272 // For example this might return std::future<error_code> if
273 // CompletionToken is boost::asio::use_future, or this might
274 // return an error code if CompletionToken specifies a coroutine.
276 return init
.result
.get();
281 int main(int, char** argv
)
283 using socket_type
= boost::asio::ip::tcp::socket
;
284 using endpoint_type
= boost::asio::ip::tcp::endpoint
;
286 // Create a listening socket, accept a connection, perform
287 // the echo, and then shut everything down and exit.
288 boost::asio::io_context ioc
;
289 socket_type sock
{ioc
};
290 boost::asio::ip::tcp::acceptor acceptor
{ioc
};
291 endpoint_type ep
{boost::asio::ip::make_address("0.0.0.0"), 0};
292 acceptor
.open(ep
.protocol());
295 acceptor
.accept(sock
);
297 [&](boost::beast::error_code ec
)
300 std::cerr
<< argv
[0] << ": " << ec
.message() << std::endl
;