10#ifndef BOTAN_ASIO_ASYNC_OPS_H_
11#define BOTAN_ASIO_ASYNC_OPS_H_
13#include <botan/asio_compat.h>
14#if defined(BOTAN_FOUND_COMPATIBLE_BOOST_ASIO_VERSION)
16 #include <botan/asio_error.h>
20 #define BOOST_ASIO_DISABLE_SERIAL_PORT
21 #include <boost/asio.hpp>
22 #include <boost/asio/yield.hpp>
64template <
class Handler,
class Executor1,
class Allocator>
65class AsyncBase :
public boost::asio::coroutine {
67 using allocator_type = boost::asio::associated_allocator_t<Handler, Allocator>;
68 using executor_type = boost::asio::associated_executor_t<Handler, Executor1>;
70 allocator_type get_allocator() const noexcept {
return boost::asio::get_associated_allocator(m_handler); }
72 executor_type get_executor() const noexcept {
73 return boost::asio::get_associated_executor(m_handler, m_work_guard_1.get_executor());
77 template <
class HandlerT>
78 AsyncBase(HandlerT&& handler,
const Executor1& executor) :
79 m_handler(std::forward<HandlerT>(handler)), m_work_guard_1(executor) {}
88 template <
class... Args>
89 void complete_now(Args&&... args) {
90 m_work_guard_1.reset();
91 m_handler(std::forward<Args>(args)...);
95 boost::asio::executor_work_guard<Executor1> m_work_guard_1;
98template <
class Handler,
class Stream,
class MutableBufferSequence,
class Allocator = std::allocator<
void>>
99class AsyncReadOperation :
public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
109 template <
class HandlerT>
110 AsyncReadOperation(HandlerT&& handler,
112 const MutableBufferSequence& buffers,
113 const boost::system::error_code& ec = {}) :
114 AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
115 stream.get_executor()),
119 this->operator()(ec, std::size_t(0),
false);
122 AsyncReadOperation(
const AsyncReadOperation& other) =
delete;
123 AsyncReadOperation(AsyncReadOperation&& other) =
default;
124 AsyncReadOperation& operator=(
const AsyncReadOperation& other) =
delete;
125 AsyncReadOperation& operator=(AsyncReadOperation&& other) =
delete;
126 ~AsyncReadOperation() =
default;
143 void operator()(boost::system::error_code ec, std::size_t bytes_transferred,
bool isContinuation =
true) {
146 if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
147 ec = StreamError::StreamTruncated;
154 if(!ec && bytes_transferred > 0) {
155 boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
156 m_stream.process_encrypted_data(read_buffer);
161 if(!ec && !m_stream.has_received_data()) {
164 m_stream.handle_tls_protocol_errors(ec);
166 if(!ec && boost::asio::buffer_size(m_buffers) > 0) {
168 m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*
this));
176 if(!ec && m_stream.has_received_data()) {
177 m_decodedBytes = m_stream.copy_received_data(m_buffers);
180 if(!isContinuation) {
184 yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*
this));
188 this->complete_now(ec, m_decodedBytes);
194 MutableBufferSequence m_buffers;
195 std::size_t m_decodedBytes;
196 boost::system::error_code m_ec;
199template <
typename Handler,
class Stream,
class Allocator = std::allocator<
void>>
200class AsyncWriteOperation :
public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
213 template <
class HandlerT>
214 AsyncWriteOperation(HandlerT&& handler,
216 std::size_t plainBytesTransferred,
217 const boost::system::error_code& ec = {}) :
218 AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
219 stream.get_executor()),
221 m_plainBytesTransferred(plainBytesTransferred) {
222 this->operator()(ec, std::size_t(0),
false);
225 AsyncWriteOperation(
const AsyncWriteOperation& other) =
delete;
226 AsyncWriteOperation(AsyncWriteOperation&& other) =
default;
227 AsyncWriteOperation& operator=(
const AsyncWriteOperation& other) =
delete;
228 AsyncWriteOperation& operator=(AsyncWriteOperation&& other) =
delete;
229 ~AsyncWriteOperation() =
default;
242 void operator()(boost::system::error_code ec, std::size_t bytes_transferred,
bool isContinuation =
true) {
245 if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
246 ec = StreamError::StreamTruncated;
251 if(!ec && bytes_transferred > 0) {
252 m_stream.consume_send_buffer(bytes_transferred);
256 if(!ec && m_stream.has_data_to_send()) {
257 m_stream.next_layer().async_write_some(m_stream.send_buffer(), std::move(*
this));
261 if(!isContinuation) {
265 yield m_stream.next_layer().async_write_some(boost::asio::const_buffer(), std::move(*
this));
271 this->complete_now(ec, m_plainBytesTransferred);
277 std::size_t m_plainBytesTransferred;
278 boost::system::error_code m_ec;
281template <
class Handler,
class Stream,
class Allocator = std::allocator<
void>>
282class AsyncHandshakeOperation :
public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
291 template <
class HandlerT>
292 AsyncHandshakeOperation(HandlerT&& handler, Stream& stream,
const boost::system::error_code& ec = {}) :
293 AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
294 stream.get_executor()),
296 this->operator()(ec, std::size_t(0),
false);
299 AsyncHandshakeOperation(
const AsyncHandshakeOperation& other) =
delete;
300 AsyncHandshakeOperation(AsyncHandshakeOperation&& other) =
default;
301 AsyncHandshakeOperation& operator=(
const AsyncHandshakeOperation& other) =
delete;
302 AsyncHandshakeOperation& operator=(AsyncHandshakeOperation&& other) =
delete;
303 ~AsyncHandshakeOperation() =
default;
317 void operator()(boost::system::error_code ec, std::size_t bytesTransferred,
bool isContinuation =
true) {
322 if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
323 ec = StreamError::StreamTruncated;
330 if(!ec && bytesTransferred > 0) {
331 boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytesTransferred};
332 m_stream.process_encrypted_data(read_buffer);
338 if(!ec && m_stream.has_data_to_send()) {
343 AsyncWriteOperation<AsyncHandshakeOperation<std::decay_t<Handler>, Stream, Allocator>, Stream, Allocator>
344 op{std::move(*
this), m_stream, 0};
350 if(!ec && !m_stream.native_handle()->is_handshake_complete()) {
353 m_stream.handle_tls_protocol_errors(ec);
358 m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*
this));
363 if(!isContinuation) {
367 yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*
this));
371 this->complete_now(ec);
377 boost::system::error_code m_ec;
378 boost::system::error_code m_stashed_ec;
383 #include <boost/asio/unyield.hpp>