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>
24namespace Botan::TLS::detail {
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(AsyncReadOperation&&) =
default;
139 void operator()(boost::system::error_code ec, std::size_t bytes_transferred,
bool isContinuation =
true) {
142 if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
143 ec = StreamError::StreamTruncated;
150 if(!ec && bytes_transferred > 0) {
151 boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytes_transferred};
152 m_stream.process_encrypted_data(read_buffer);
157 if(!ec && !m_stream.has_received_data()) {
160 m_stream.handle_tls_protocol_errors(ec);
162 if(!ec && boost::asio::buffer_size(m_buffers) > 0) {
164 m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*
this));
172 if(!ec && m_stream.has_received_data()) {
173 m_decodedBytes = m_stream.copy_received_data(m_buffers);
176 if(!isContinuation) {
180 yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*
this));
184 this->complete_now(ec, m_decodedBytes);
190 MutableBufferSequence m_buffers;
191 std::size_t m_decodedBytes;
192 boost::system::error_code m_ec;
195template <
typename Handler,
class Stream,
class Allocator = std::allocator<
void>>
196class AsyncWriteOperation :
public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
209 template <
class HandlerT>
210 AsyncWriteOperation(HandlerT&& handler,
212 std::size_t plainBytesTransferred,
213 const boost::system::error_code& ec = {}) :
214 AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
215 stream.get_executor()),
217 m_plainBytesTransferred(plainBytesTransferred) {
218 this->operator()(ec, std::size_t(0),
false);
221 AsyncWriteOperation(AsyncWriteOperation&&) =
default;
234 void operator()(boost::system::error_code ec, std::size_t bytes_transferred,
bool isContinuation =
true) {
237 if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
238 ec = StreamError::StreamTruncated;
243 if(!ec && bytes_transferred > 0) {
244 m_stream.consume_send_buffer(bytes_transferred);
248 if(!ec && m_stream.has_data_to_send()) {
249 m_stream.next_layer().async_write_some(m_stream.send_buffer(), std::move(*
this));
253 if(!isContinuation) {
257 yield m_stream.next_layer().async_write_some(boost::asio::const_buffer(), std::move(*
this));
263 this->complete_now(ec, m_plainBytesTransferred);
269 std::size_t m_plainBytesTransferred;
270 boost::system::error_code m_ec;
273template <
class Handler,
class Stream,
class Allocator = std::allocator<
void>>
274class AsyncHandshakeOperation :
public AsyncBase<Handler, typename Stream::executor_type, Allocator> {
283 template <
class HandlerT>
284 AsyncHandshakeOperation(HandlerT&& handler, Stream& stream,
const boost::system::error_code& ec = {}) :
285 AsyncBase<Handler, typename Stream::executor_type, Allocator>(std::forward<HandlerT>(handler),
286 stream.get_executor()),
288 this->operator()(ec, std::size_t(0),
false);
291 AsyncHandshakeOperation(AsyncHandshakeOperation&&) =
default;
305 void operator()(boost::system::error_code ec, std::size_t bytesTransferred,
bool isContinuation =
true) {
310 if(ec == boost::asio::error::eof && !m_stream.shutdown_received()) {
311 ec = StreamError::StreamTruncated;
318 if(!ec && bytesTransferred > 0) {
319 boost::asio::const_buffer read_buffer{m_stream.input_buffer().data(), bytesTransferred};
320 m_stream.process_encrypted_data(read_buffer);
326 if(!ec && m_stream.has_data_to_send()) {
331 AsyncWriteOperation<AsyncHandshakeOperation<typename std::decay<Handler>::type, Stream, Allocator>,
334 op{std::move(*
this), m_stream, 0};
340 if(!ec && !m_stream.native_handle()->is_handshake_complete()) {
343 m_stream.handle_tls_protocol_errors(ec);
348 m_stream.next_layer().async_read_some(m_stream.input_buffer(), std::move(*
this));
353 if(!isContinuation) {
357 yield m_stream.next_layer().async_read_some(boost::asio::mutable_buffer(), std::move(*
this));
361 this->complete_now(ec);
367 boost::system::error_code m_ec;
368 boost::system::error_code m_stashed_ec;
373 #include <boost/asio/unyield.hpp>