]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/beast/example/echo-op/echo_op.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / beast / example / echo-op / echo_op.cpp
1 //
2 // Copyright (c) 2016-2017 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 #include <boost/beast/core.hpp>
11 #include <boost/asio.hpp>
12 #include <cstddef>
13 #include <iostream>
14 #include <memory>
15 #include <utility>
16
17 //[example_core_echo_op_1
18
19 template<
20 class AsyncStream,
21 class CompletionToken>
22 auto
23 async_echo(AsyncStream& stream, CompletionToken&& token)
24
25 //]
26 -> BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(boost::beast::error_code));
27
28 //[example_core_echo_op_2
29
30 /** Asynchronously read a line and echo it back.
31
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:
37
38 @li A line was read in and sent back on the stream
39
40 @li An error occurs.
41
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
48 silently discarded.
49
50 @param The stream to operate on. The type must meet the
51 requirements of @b AsyncReadStream and @AsyncWriteStream
52
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:
56 @code
57 void handler(
58 error_code ec // result of operation
59 );
60 @endcode
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`.
65 */
66 template<
67 class AsyncStream,
68 class CompletionToken>
69 BOOST_ASIO_INITFN_RESULT_TYPE( /*< `BOOST_ASIO_INITFN_RESULT_TYPE` customizes the return value based on the completion token >*/
70 CompletionToken,
71 void(boost::beast::error_code)) /*< This is the signature for the completion handler >*/
72 async_echo(
73 AsyncStream& stream,
74 CompletionToken&& token);
75
76 //]
77
78 //[example_core_echo_op_4
79
80 // This composed operation reads a line of input and echoes it back.
81 //
82 template<class AsyncStream, class Handler>
83 class echo_op
84 {
85 // This holds all of the state information required by the operation.
86 struct state
87 {
88 // The stream to read and write to
89 AsyncStream& stream;
90
91 // Indicates what step in the operation's state machine
92 // to perform next, starting from zero.
93 int step = 0;
94
95 // The buffer used to hold the input and output data.
96 //
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
100 // a continuation.
101 //
102 boost::asio::basic_streambuf<typename std::allocator_traits<
103 boost::asio::associated_allocator_t<Handler> >::
104 template rebind_alloc<char> > buffer;
105
106 // handler_ptr requires that the first parameter to the
107 // contained object constructor is a reference to the
108 // managed final completion handler.
109 //
110 explicit state(Handler& handler, AsyncStream& stream_)
111 : stream(stream_)
112 , buffer((std::numeric_limits<std::size_t>::max)(),
113 boost::asio::get_associated_allocator(handler))
114 {
115 }
116 };
117
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.
122 //
123 // `handler_ptr` uses the allocator associated with the final
124 // completion handler, in order to allocate the storage for `state`.
125 //
126 boost::beast::handler_ptr<state, Handler> p_;
127
128 public:
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.
132 //
133 echo_op(echo_op&&) = default;
134 echo_op(echo_op const&) = default;
135
136 // The constructor simply creates our state variables in
137 // the smart pointer container.
138 //
139 template<class DeducedHandler, class... Args>
140 echo_op(AsyncStream& stream, DeducedHandler&& handler)
141 : p_(std::forward<DeducedHandler>(handler), stream)
142 {
143 }
144
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.
150
151 using allocator_type =
152 boost::asio::associated_allocator_t<Handler>;
153
154 allocator_type
155 get_allocator() const noexcept
156 {
157 return boost::asio::get_associated_allocator(p_.handler());
158 }
159
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
164 // final handler.
165
166 using executor_type = boost::asio::associated_executor_t<
167 Handler, decltype(std::declval<AsyncStream&>().get_executor())>;
168
169 executor_type get_executor() const noexcept
170 {
171 return boost::asio::get_associated_executor(
172 p_.handler(), p_->stream.get_executor());
173 }
174
175 // The entry point for this handler. This will get called
176 // as our intermediate operations complete. Definition below.
177 //
178 void operator()(boost::beast::error_code ec, std::size_t bytes_transferred);
179 };
180
181 //]
182
183 //[example_core_echo_op_5
184
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.
187 //
188 template<class AsyncStream, class Handler>
189 void echo_op<AsyncStream, Handler>::
190 operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
191 {
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.
195 auto& p = *p_;
196
197 // Now perform the next step in the state machine
198 switch(ec ? 2 : p.step)
199 {
200 // initial entry
201 case 0:
202 // read up to the first newline
203 p.step = 1;
204 return boost::asio::async_read_until(p.stream, p.buffer, "\r", std::move(*this));
205
206 case 1:
207 // write everything back
208 p.step = 2;
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));
213
214 case 2:
215 p.buffer.consume(bytes_transferred);
216 break;
217 }
218
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
223 // customizations.
224 //
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.
228 //
229 p_.invoke(ec);
230 return;
231 }
232
233 //]
234
235 //[example_core_echo_op_3
236
237 template<class AsyncStream, class Handler>
238 class echo_op;
239
240 // Read a line and echo it back
241 //
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)
245 {
246 // Make sure stream meets the requirements. We use static_assert
247 // to cause a friendly message instead of an error novel.
248 //
249 static_assert(boost::beast::is_async_stream<AsyncStream>::value,
250 "AsyncStream requirements not met");
251
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.
255 //
256 boost::asio::async_completion<CompletionToken, void(boost::beast::error_code)> init{token};
257
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
262 // to be used.
263 //
264 echo_op<
265 AsyncStream,
266 BOOST_ASIO_HANDLER_TYPE(
267 CompletionToken, void(boost::beast::error_code))>{
268 stream,
269 init.completion_handler}(boost::beast::error_code{}, 0);
270
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.
275 //
276 return init.result.get();
277 }
278
279 //]
280
281 int main(int, char** argv)
282 {
283 using socket_type = boost::asio::ip::tcp::socket;
284 using endpoint_type = boost::asio::ip::tcp::endpoint;
285
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());
293 acceptor.bind(ep);
294 acceptor.listen();
295 acceptor.accept(sock);
296 async_echo(sock,
297 [&](boost::beast::error_code ec)
298 {
299 if(ec)
300 std::cerr << argv[0] << ": " << ec.message() << std::endl;
301 });
302 ioc.run();
303 return 0;
304 }