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 #include "snippets.hpp"
11
12 #include <boost/beast/_experimental/unit_test/suite.hpp>
13 #include <boost/beast/_experimental/test/stream.hpp>
14 #include <boost/beast/core/flat_buffer.hpp>
15 #include <boost/asio/buffer.hpp>
16 #include <boost/asio/buffers_iterator.hpp>
17 #include <boost/asio/ip/tcp.hpp>
18 #include <boost/asio/spawn.hpp>
19 #include <boost/asio/use_future.hpp>
20 #include <boost/asio/write.hpp>
21 #include <algorithm>
22 #include <assert.h>
23
24 namespace boost {
25 namespace beast {
26
27 namespace {
28
29 void
snippets()30 snippets()
31 {
32 #include "snippets.ipp"
33 {
34 //[code_core_1_refresher_1s
35 net::const_buffer cb("Hello, world!", 13);
36 assert(string_view(reinterpret_cast<char const*>(
37 cb.data()), cb.size()) == "Hello, world!");
38
39 char storage[13];
40 net::mutable_buffer mb(storage, sizeof(storage));
41 std::memcpy(mb.data(), cb.data(), mb.size());
42 assert(string_view(reinterpret_cast<char const*>(
43 mb.data()), mb.size()) == "Hello, world!");
44 //]
45 }
46 {
47 //[code_core_1_refresher_2s
48 net::const_buffer b1; // a ConstBufferSequence by definition
49 net::mutable_buffer b2; // a MutableBufferSequence by definition
50 std::array<net::const_buffer, 3> b3; // A ConstBufferSequence by named requirements
51 //]
52 }
53 {
54 //[code_core_1_refresher_3s
55 // initiate an asynchronous write operation
56 net::async_write(sock, net::const_buffer("Hello, world!", 13),
57 [](error_code ec, std::size_t bytes_transferred)
58 {
59 // this lambda is invoked when the write operation completes
60 if(! ec)
61 assert(bytes_transferred == 13);
62 else
63 std::cerr << "Error: " << ec.message() << "\n";
64 });
65 // meanwhile, the operation is outstanding and execution continues from here
66 //]
67 }
68 {
69 //[code_core_1_refresher_4s
70 std::future<std::size_t> f = net::async_write(sock,
71 net::const_buffer("Hello, world!", 13), net::use_future);
72 //]
73 }
74 {
75 //[code_core_1_refresher_5s
76 asio::spawn(
77 [&sock](net::yield_context yield)
78 {
79 std::size_t bytes_transferred = net::async_write(sock,
80 net::const_buffer("Hello, world!", 13), yield);
81 (void)bytes_transferred;
82 });
83 //]
84 }
85 }
86
87 //------------------------------------------------------------------------------
88
89 //[code_core_1_refresher_1
90 template <class ConstBufferSequence>
string_from_buffers(ConstBufferSequence const & buffers)91 std::string string_from_buffers (ConstBufferSequence const& buffers)
92 {
93 // check that the type meets the requirements using the provided type traits
94 static_assert(
95 net::is_const_buffer_sequence<ConstBufferSequence>::value,
96 "ConstBufferSequence type requirements not met");
97
98 // optimization: reserve all the space for the string first
99 std::string result;
100 result.reserve(beast::buffer_bytes(buffers)); // beast version of net::buffer_size
101
102 // iterate over each buffer in the sequence and append it to the string
103 for(auto it = net::buffer_sequence_begin(buffers); // returns an iterator to beginning of the sequence
104 it != net::buffer_sequence_end(buffers);) // returns a past-the-end iterator to the sequence
105 {
106 // A buffer sequence iterator's value_type is always convertible to net::const_buffer
107 net::const_buffer buffer = *it++;
108
109 // A cast is always required to out-out of type-safety
110 result.append(static_cast<char const*>(buffer.data()), buffer.size());
111 }
112 return result;
113 }
114 //]
115
116 //------------------------------------------------------------------------------
117
118 //[code_core_1_refresher_2
119 // Read a line ending in '\n' from a socket, returning
120 // the number of characters up to but not including the newline
121 template <class DynamicBuffer>
read_line(net::ip::tcp::socket & sock,DynamicBuffer & buffer)122 std::size_t read_line(net::ip::tcp::socket& sock, DynamicBuffer& buffer)
123 {
124 // this alias keeps things readable
125 using range = net::buffers_iterator<
126 typename DynamicBuffer::const_buffers_type>;
127
128 for(;;)
129 {
130 // get iterators representing the range of characters in the buffer
131 auto begin = range::begin(buffer.data());
132 auto end = range::end(buffer.data());
133
134 // search for "\n" and return if found
135 auto pos = std::find(begin, end, '\n');
136 if(pos != range::end(buffer.data()))
137 return std::distance(begin, end);
138
139 // Determine the number of bytes to read,
140 // using available capacity in the buffer first.
141 std::size_t bytes_to_read = std::min<std::size_t>(
142 std::max<std::size_t>(512, // under 512 is too little,
143 buffer.capacity() - buffer.size()),
144 std::min<std::size_t>(65536, // and over 65536 is too much.
145 buffer.max_size() - buffer.size()));
146
147 // Read up to bytes_to_read bytes into the dynamic buffer
148 buffer.commit(sock.read_some(buffer.prepare(bytes_to_read)));
149 }
150 }
151 //]
152
153 //------------------------------------------------------------------------------
154
155 //[code_core_1_refresher_3
156 // Meets the requirements of SyncReadStream
157 struct sync_read_stream
158 {
159 // Returns the number of bytes read upon success, otherwise throws an exception
160 template <class MutableBufferSequence>
161 std::size_t read_some(MutableBufferSequence const& buffers);
162
163 // Returns the number of bytes read successfully, sets the error code if a failure occurs
164 template <class MutableBufferSequence>
165 std::size_t read_some(MutableBufferSequence const& buffers, error_code& ec);
166 };
167
168 // Meets the requirements of SyncWriteStream
169 struct sync_write_stream
170 {
171 // Returns the number of bytes written upon success, otherwise throws an exception
172 template <class ConstBufferSequence>
173 std::size_t write_some(ConstBufferSequence const& buffers);
174
175 // Returns the number of bytes written successfully, sets the error code if a failure occurs
176 template <class ConstBufferSequence>
177 std::size_t write_some(ConstBufferSequence const& buffers, error_code& ec);
178 };
179 //]
180
181 template<class MutableBufferSequence>
read_some(MutableBufferSequence const &)182 std::size_t sync_read_stream::read_some(MutableBufferSequence const&)
183 {
184 return 0;
185 }
186 template<class MutableBufferSequence>
read_some(MutableBufferSequence const &,error_code &)187 std::size_t sync_read_stream::read_some(MutableBufferSequence const&, error_code&)
188 {
189 return 0;
190 }
191 template<class ConstBufferSequence>
write_some(ConstBufferSequence const &)192 std::size_t sync_write_stream::write_some(ConstBufferSequence const&)
193 {
194 return 0;
195 }
196 template<class ConstBufferSequence>
write_some(ConstBufferSequence const &,error_code &)197 std::size_t sync_write_stream::write_some(ConstBufferSequence const&, error_code&)
198 {
199 return 0;
200 }
201 BOOST_STATIC_ASSERT(is_sync_read_stream<sync_read_stream>::value);
202 BOOST_STATIC_ASSERT(is_sync_write_stream<sync_write_stream>::value);
203
204 //------------------------------------------------------------------------------
205
206 //[code_core_1_refresher_4
207 template <class SyncWriteStream>
hello(SyncWriteStream & stream)208 void hello (SyncWriteStream& stream)
209 {
210 net::const_buffer cb("Hello, world!", 13);
211 do
212 {
213 auto bytes_transferred = stream.write_some(cb); // may throw
214 cb += bytes_transferred; // adjust the pointer and size
215 }
216 while (cb.size() > 0);
217 }
218 //]
219
220 //------------------------------------------------------------------------------
221
222 //[code_core_1_refresher_5
223 template <class SyncWriteStream>
hello(SyncWriteStream & stream,error_code & ec)224 void hello (SyncWriteStream& stream, error_code& ec)
225 {
226 net::const_buffer cb("Hello, world!", 13);
227 do
228 {
229 auto bytes_transferred = stream.write_some(cb, ec);
230 cb += bytes_transferred; // adjust the pointer and size
231 }
232 while (cb.size() > 0 && ! ec);
233 }
234 //]
235
236 //------------------------------------------------------------------------------
237
238 } // (anon)
239 } // beast
240 } // boost
241
242 //[code_core_1_refresher_6
243 // The following is a completion handler expressed
244 // as a function object, with a nested associated
245 // allocator and a nested associated executor.
246 struct handler
247 {
248 using allocator_type = std::allocator<char>;
249 allocator_type get_allocator() const noexcept;
250
251 using executor_type = boost::asio::io_context::executor_type;
252 executor_type get_executor() const noexcept;
253
254 void operator()(boost::beast::error_code, std::size_t);
255 };
256 //]
get_allocator() const257 inline auto handler::get_allocator() const noexcept ->
258 allocator_type
259 {
260 return {};
261 }
get_executor() const262 inline auto handler::get_executor() const noexcept ->
263 executor_type
264 {
265 static boost::asio::io_context ioc;
266 return ioc.get_executor();
267 }
operator ()(boost::beast::error_code,std::size_t)268 inline void handler::operator()(
269 boost::beast::error_code, std::size_t)
270 {
271 }
272
273 //[code_core_1_refresher_7
274 namespace boost {
275 namespace asio {
276
277 template<class Allocator>
278 struct associated_allocator<handler, Allocator>
279 {
280 using type = std::allocator<void>;
281
282 static
283 type
284 get(handler const& h,
285 Allocator const& alloc = Allocator{}) noexcept;
286 };
287
288 template<class Executor>
289 struct associated_executor<handler, Executor>
290 {
291 using type = any_io_executor;
292
293 static
294 type
295 get(handler const& h,
296 Executor const& ex = Executor{}) noexcept;
297 };
298
299 } // boost
300 } // asio
301 //]
302
303 template<class Allocator>
304 auto
305 boost::asio::associated_allocator<handler, Allocator>::
get(handler const &,Allocator const &)306 get(handler const&, Allocator const&) noexcept -> type
307 {
308 return {};
309 }
310 template<class Executor>
311 auto
312 boost::asio::associated_executor<handler, Executor>::
get(handler const &,Executor const &)313 get(handler const&, Executor const&) noexcept -> type
314 {
315 return {};
316 }
317
318 //------------------------------------------------------------------------------
319
320 namespace boost {
321 namespace beast {
322
323 namespace {
324
325 //------------------------------------------------------------------------------
326
327 //[code_core_1_refresher_8
328 template <class AsyncWriteStream, class WriteHandler>
async_hello(AsyncWriteStream & stream,WriteHandler && handler)329 void async_hello (AsyncWriteStream& stream, WriteHandler&& handler)
330 {
331 net::async_write (stream,
332 net::buffer("Hello, world!", 13),
333 std::forward<WriteHandler>(handler));
334 }
335 //]
336
337 //------------------------------------------------------------------------------
338
339 //[code_core_1_refresher_9
340 template<
341 class AsyncWriteStream,
342 class ConstBufferSequence,
343 class CompletionToken>
344 auto
345 async_write(
346 AsyncWriteStream* stream, // references are passed as pointers
347 ConstBufferSequence const& buffers,
348 CompletionToken&& token) // a handler, or a special object.
349 ->
350 typename net::async_result< // return-type customization point.
351 typename std::decay<CompletionToken>::type, // type used to specialize async_result.
352 void(error_code, std::size_t) // underlying completion handler signature.
353 >::return_type;
354 //]
355 struct run_async_write
356 {
357 template<class... Args>
358 void
operator ()boost::beast::__anone1eee1320411::run_async_write359 operator()(Args&&...)
360 {
361 }
362 };
363 template<
364 class AsyncWriteStream,
365 class ConstBufferSequence,
366 class CompletionToken>
367 auto
async_write(AsyncWriteStream & stream,ConstBufferSequence const & buffers,CompletionToken && token)368 async_write(
369 AsyncWriteStream& stream,
370 ConstBufferSequence const& buffers,
371 CompletionToken&& token) ->
372 typename net::async_result<
373 typename std::decay<CompletionToken>::type,
374 void(error_code, std::size_t)
375 >::return_type
376 {
377 //[code_core_1_refresher_10
378
379 return net::async_initiate<
380 CompletionToken,
381 void(error_code, std::size_t)>(
382 run_async_write{}, // The "initiation" object.
383 token, // Token must come before other arguments.
384 &stream, // Additional captured arguments are
385 buffers); // forwarded to the initiation object.
386
387 //]
388 }
389
390 //------------------------------------------------------------------------------
391
392 } // (anon)
393
394 struct core_1_refresher_test
395 : public beast::unit_test::suite
396 {
397 void
runboost::beast::core_1_refresher_test398 run() override
399 {
400 BEAST_EXPECT(&snippets);
401
402 BEAST_EXPECT((static_cast<
403 std::string(*)(net::const_buffer const&)>(
404 &string_from_buffers<net::const_buffer>)));
405
406 BEAST_EXPECT(static_cast<
407 std::size_t(*)(net::ip::tcp::socket&, flat_buffer&)>(
408 &read_line<flat_buffer>));
409
410 BEAST_EXPECT(static_cast<
411 std::size_t(sync_read_stream::*)(
412 net::mutable_buffer const&)>(
413 &sync_read_stream::read_some));
414 BEAST_EXPECT(static_cast<
415 std::size_t(sync_read_stream::*)(
416 net::mutable_buffer const&, error_code&)>(
417 &sync_read_stream::read_some));
418 BEAST_EXPECT(static_cast<
419 std::size_t(sync_write_stream::*)(
420 net::const_buffer const&)>(
421 &sync_write_stream::write_some));
422 BEAST_EXPECT(static_cast<
423 std::size_t(sync_write_stream::*)(
424 net::const_buffer const&, error_code&)>(
425 &sync_write_stream::write_some));
426
427 BEAST_EXPECT(static_cast<
428 void(*)(test::stream&)>(
429 &hello<test::stream>));
430
431 BEAST_EXPECT(static_cast<
432 void(*)(test::stream&, error_code&)>(
433 &hello<test::stream>));
434
435 handler h;
436 h.get_allocator();
437 h.get_executor();
438
439 BEAST_EXPECT((&async_hello<test::stream, handler>));
440 }
441 };
442
443 BEAST_DEFINE_TESTSUITE(beast,doc,core_1_refresher);
444
445 } // beast
446 } // boost
447