436 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
| //
 | |
| // ssl/detail/io.hpp
 | |
| // ~~~~~~~~~~~~~~~~~
 | |
| //
 | |
| // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
 | |
| //
 | |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying
 | |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | |
| //
 | |
| 
 | |
| #ifndef ASIO_SSL_DETAIL_IO_HPP
 | |
| #define ASIO_SSL_DETAIL_IO_HPP
 | |
| 
 | |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| # pragma once
 | |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
 | |
| 
 | |
| #include "asio/detail/config.hpp"
 | |
| 
 | |
| #include "asio/detail/base_from_cancellation_state.hpp"
 | |
| #include "asio/detail/handler_tracking.hpp"
 | |
| #include "asio/ssl/detail/engine.hpp"
 | |
| #include "asio/ssl/detail/stream_core.hpp"
 | |
| #include "asio/write.hpp"
 | |
| 
 | |
| #include "asio/detail/push_options.hpp"
 | |
| 
 | |
| namespace asio {
 | |
| namespace ssl {
 | |
| namespace detail {
 | |
| 
 | |
| template <typename Stream, typename Operation>
 | |
| std::size_t io(Stream& next_layer, stream_core& core,
 | |
|     const Operation& op, asio::error_code& ec)
 | |
| {
 | |
|   asio::error_code io_ec;
 | |
|   std::size_t bytes_transferred = 0;
 | |
|   do switch (op(core.engine_, ec, bytes_transferred))
 | |
|   {
 | |
|   case engine::want_input_and_retry:
 | |
| 
 | |
|     // If the input buffer is empty then we need to read some more data from
 | |
|     // the underlying transport.
 | |
|     if (core.input_.size() == 0)
 | |
|     {
 | |
|       core.input_ = asio::buffer(core.input_buffer_,
 | |
|           next_layer.read_some(core.input_buffer_, io_ec));
 | |
|       if (!ec)
 | |
|         ec = io_ec;
 | |
|     }
 | |
| 
 | |
|     // Pass the new input data to the engine.
 | |
|     core.input_ = core.engine_.put_input(core.input_);
 | |
| 
 | |
|     // Try the operation again.
 | |
|     continue;
 | |
| 
 | |
|   case engine::want_output_and_retry:
 | |
| 
 | |
|     // Get output data from the engine and write it to the underlying
 | |
|     // transport.
 | |
|     asio::write(next_layer,
 | |
|         core.engine_.get_output(core.output_buffer_), io_ec);
 | |
|     if (!ec)
 | |
|       ec = io_ec;
 | |
| 
 | |
|     // Try the operation again.
 | |
|     continue;
 | |
| 
 | |
|   case engine::want_output:
 | |
| 
 | |
|     // Get output data from the engine and write it to the underlying
 | |
|     // transport.
 | |
|     asio::write(next_layer,
 | |
|         core.engine_.get_output(core.output_buffer_), io_ec);
 | |
|     if (!ec)
 | |
|       ec = io_ec;
 | |
| 
 | |
|     // Operation is complete. Return result to caller.
 | |
|     core.engine_.map_error_code(ec);
 | |
|     return bytes_transferred;
 | |
| 
 | |
|   default:
 | |
| 
 | |
|     // Operation is complete. Return result to caller.
 | |
|     core.engine_.map_error_code(ec);
 | |
|     return bytes_transferred;
 | |
| 
 | |
|   } while (!ec);
 | |
| 
 | |
|   // Operation failed. Return result to caller.
 | |
|   core.engine_.map_error_code(ec);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| template <typename Stream, typename Operation, typename Handler>
 | |
| class io_op
 | |
|   : public asio::detail::base_from_cancellation_state<Handler>
 | |
| {
 | |
| public:
 | |
|   io_op(Stream& next_layer, stream_core& core,
 | |
|       const Operation& op, Handler& handler)
 | |
|     : asio::detail::base_from_cancellation_state<Handler>(handler),
 | |
|       next_layer_(next_layer),
 | |
|       core_(core),
 | |
|       op_(op),
 | |
|       start_(0),
 | |
|       want_(engine::want_nothing),
 | |
|       bytes_transferred_(0),
 | |
|       handler_(ASIO_MOVE_CAST(Handler)(handler))
 | |
|   {
 | |
|   }
 | |
| 
 | |
| #if defined(ASIO_HAS_MOVE)
 | |
|   io_op(const io_op& other)
 | |
|     : asio::detail::base_from_cancellation_state<Handler>(other),
 | |
|       next_layer_(other.next_layer_),
 | |
|       core_(other.core_),
 | |
|       op_(other.op_),
 | |
|       start_(other.start_),
 | |
|       want_(other.want_),
 | |
|       ec_(other.ec_),
 | |
|       bytes_transferred_(other.bytes_transferred_),
 | |
|       handler_(other.handler_)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   io_op(io_op&& other)
 | |
|     : asio::detail::base_from_cancellation_state<Handler>(
 | |
|         ASIO_MOVE_CAST(
 | |
|           asio::detail::base_from_cancellation_state<Handler>)(
 | |
|             other)),
 | |
|       next_layer_(other.next_layer_),
 | |
|       core_(other.core_),
 | |
|       op_(ASIO_MOVE_CAST(Operation)(other.op_)),
 | |
|       start_(other.start_),
 | |
|       want_(other.want_),
 | |
|       ec_(other.ec_),
 | |
|       bytes_transferred_(other.bytes_transferred_),
 | |
|       handler_(ASIO_MOVE_CAST(Handler)(other.handler_))
 | |
|   {
 | |
|   }
 | |
| #endif // defined(ASIO_HAS_MOVE)
 | |
| 
 | |
|   void operator()(asio::error_code ec,
 | |
|       std::size_t bytes_transferred = ~std::size_t(0), int start = 0)
 | |
|   {
 | |
|     switch (start_ = start)
 | |
|     {
 | |
|     case 1: // Called after at least one async operation.
 | |
|       do
 | |
|       {
 | |
|         switch (want_ = op_(core_.engine_, ec_, bytes_transferred_))
 | |
|         {
 | |
|         case engine::want_input_and_retry:
 | |
| 
 | |
|           // If the input buffer already has data in it we can pass it to the
 | |
|           // engine and then retry the operation immediately.
 | |
|           if (core_.input_.size() != 0)
 | |
|           {
 | |
|             core_.input_ = core_.engine_.put_input(core_.input_);
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           // The engine wants more data to be read from input. However, we
 | |
|           // cannot allow more than one read operation at a time on the
 | |
|           // underlying transport. The pending_read_ timer's expiry is set to
 | |
|           // pos_infin if a read is in progress, and neg_infin otherwise.
 | |
|           if (core_.expiry(core_.pending_read_) == core_.neg_infin())
 | |
|           {
 | |
|             // Prevent other read operations from being started.
 | |
|             core_.pending_read_.expires_at(core_.pos_infin());
 | |
| 
 | |
|             ASIO_HANDLER_LOCATION((
 | |
|                   __FILE__, __LINE__, Operation::tracking_name()));
 | |
| 
 | |
|             // Start reading some data from the underlying transport.
 | |
|             next_layer_.async_read_some(
 | |
|                 asio::buffer(core_.input_buffer_),
 | |
|                 ASIO_MOVE_CAST(io_op)(*this));
 | |
|           }
 | |
|           else
 | |
|           {
 | |
|             ASIO_HANDLER_LOCATION((
 | |
|                   __FILE__, __LINE__, Operation::tracking_name()));
 | |
| 
 | |
|             // Wait until the current read operation completes.
 | |
|             core_.pending_read_.async_wait(ASIO_MOVE_CAST(io_op)(*this));
 | |
|           }
 | |
| 
 | |
|           // Yield control until asynchronous operation completes. Control
 | |
|           // resumes at the "default:" label below.
 | |
|           return;
 | |
| 
 | |
|         case engine::want_output_and_retry:
 | |
|         case engine::want_output:
 | |
| 
 | |
|           // The engine wants some data to be written to the output. However, we
 | |
|           // cannot allow more than one write operation at a time on the
 | |
|           // underlying transport. The pending_write_ timer's expiry is set to
 | |
|           // pos_infin if a write is in progress, and neg_infin otherwise.
 | |
|           if (core_.expiry(core_.pending_write_) == core_.neg_infin())
 | |
|           {
 | |
|             // Prevent other write operations from being started.
 | |
|             core_.pending_write_.expires_at(core_.pos_infin());
 | |
| 
 | |
|             ASIO_HANDLER_LOCATION((
 | |
|                   __FILE__, __LINE__, Operation::tracking_name()));
 | |
| 
 | |
|             // Start writing all the data to the underlying transport.
 | |
|             asio::async_write(next_layer_,
 | |
|                 core_.engine_.get_output(core_.output_buffer_),
 | |
|                 ASIO_MOVE_CAST(io_op)(*this));
 | |
|           }
 | |
|           else
 | |
|           {
 | |
|             ASIO_HANDLER_LOCATION((
 | |
|                   __FILE__, __LINE__, Operation::tracking_name()));
 | |
| 
 | |
|             // Wait until the current write operation completes.
 | |
|             core_.pending_write_.async_wait(ASIO_MOVE_CAST(io_op)(*this));
 | |
|           }
 | |
| 
 | |
|           // Yield control until asynchronous operation completes. Control
 | |
|           // resumes at the "default:" label below.
 | |
|           return;
 | |
| 
 | |
|         default:
 | |
| 
 | |
|           // The SSL operation is done and we can invoke the handler, but we
 | |
|           // have to keep in mind that this function might be being called from
 | |
|           // the async operation's initiating function. In this case we're not
 | |
|           // allowed to call the handler directly. Instead, issue a zero-sized
 | |
|           // read so the handler runs "as-if" posted using io_context::post().
 | |
|           if (start)
 | |
|           {
 | |
|             ASIO_HANDLER_LOCATION((
 | |
|                   __FILE__, __LINE__, Operation::tracking_name()));
 | |
| 
 | |
|             next_layer_.async_read_some(
 | |
|                 asio::buffer(core_.input_buffer_, 0),
 | |
|                 ASIO_MOVE_CAST(io_op)(*this));
 | |
| 
 | |
|             // Yield control until asynchronous operation completes. Control
 | |
|             // resumes at the "default:" label below.
 | |
|             return;
 | |
|           }
 | |
|           else
 | |
|           {
 | |
|             // Continue on to run handler directly.
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         default:
 | |
|         if (bytes_transferred == ~std::size_t(0))
 | |
|           bytes_transferred = 0; // Timer cancellation, no data transferred.
 | |
|         else if (!ec_)
 | |
|           ec_ = ec;
 | |
| 
 | |
|         switch (want_)
 | |
|         {
 | |
|         case engine::want_input_and_retry:
 | |
| 
 | |
|           // Add received data to the engine's input.
 | |
|           core_.input_ = asio::buffer(
 | |
|               core_.input_buffer_, bytes_transferred);
 | |
|           core_.input_ = core_.engine_.put_input(core_.input_);
 | |
| 
 | |
|           // Release any waiting read operations.
 | |
|           core_.pending_read_.expires_at(core_.neg_infin());
 | |
| 
 | |
|           // Check for cancellation before continuing.
 | |
|           if (this->cancelled() != cancellation_type::none)
 | |
|           {
 | |
|             ec_ = asio::error::operation_aborted;
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           // Try the operation again.
 | |
|           continue;
 | |
| 
 | |
|         case engine::want_output_and_retry:
 | |
| 
 | |
|           // Release any waiting write operations.
 | |
|           core_.pending_write_.expires_at(core_.neg_infin());
 | |
| 
 | |
|           // Check for cancellation before continuing.
 | |
|           if (this->cancelled() != cancellation_type::none)
 | |
|           {
 | |
|             ec_ = asio::error::operation_aborted;
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|           // Try the operation again.
 | |
|           continue;
 | |
| 
 | |
|         case engine::want_output:
 | |
| 
 | |
|           // Release any waiting write operations.
 | |
|           core_.pending_write_.expires_at(core_.neg_infin());
 | |
| 
 | |
|           // Fall through to call handler.
 | |
| 
 | |
|         default:
 | |
| 
 | |
|           // Pass the result to the handler.
 | |
|           op_.call_handler(handler_,
 | |
|               core_.engine_.map_error_code(ec_),
 | |
|               ec_ ? 0 : bytes_transferred_);
 | |
| 
 | |
|           // Our work here is done.
 | |
|           return;
 | |
|         }
 | |
|       } while (!ec_);
 | |
| 
 | |
|       // Operation failed. Pass the result to the handler.
 | |
|       op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| //private:
 | |
|   Stream& next_layer_;
 | |
|   stream_core& core_;
 | |
|   Operation op_;
 | |
|   int start_;
 | |
|   engine::want want_;
 | |
|   asio::error_code ec_;
 | |
|   std::size_t bytes_transferred_;
 | |
|   Handler handler_;
 | |
| };
 | |
| 
 | |
| template <typename Stream, typename Operation, typename Handler>
 | |
| inline asio_handler_allocate_is_deprecated
 | |
| asio_handler_allocate(std::size_t size,
 | |
|     io_op<Stream, Operation, Handler>* this_handler)
 | |
| {
 | |
| #if defined(ASIO_NO_DEPRECATED)
 | |
|   asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
 | |
|   return asio_handler_allocate_is_no_longer_used();
 | |
| #else // defined(ASIO_NO_DEPRECATED)
 | |
|   return asio_handler_alloc_helpers::allocate(
 | |
|       size, this_handler->handler_);
 | |
| #endif // defined(ASIO_NO_DEPRECATED)
 | |
| }
 | |
| 
 | |
| template <typename Stream, typename Operation, typename Handler>
 | |
| inline asio_handler_deallocate_is_deprecated
 | |
| asio_handler_deallocate(void* pointer, std::size_t size,
 | |
|     io_op<Stream, Operation, Handler>* this_handler)
 | |
| {
 | |
|   asio_handler_alloc_helpers::deallocate(
 | |
|       pointer, size, this_handler->handler_);
 | |
| #if defined(ASIO_NO_DEPRECATED)
 | |
|   return asio_handler_deallocate_is_no_longer_used();
 | |
| #endif // defined(ASIO_NO_DEPRECATED)
 | |
| }
 | |
| 
 | |
| template <typename Stream, typename Operation, typename Handler>
 | |
| inline bool asio_handler_is_continuation(
 | |
|     io_op<Stream, Operation, Handler>* this_handler)
 | |
| {
 | |
|   return this_handler->start_ == 0 ? true
 | |
|     : asio_handler_cont_helpers::is_continuation(this_handler->handler_);
 | |
| }
 | |
| 
 | |
| template <typename Function, typename Stream,
 | |
|     typename Operation, typename Handler>
 | |
| inline asio_handler_invoke_is_deprecated
 | |
| asio_handler_invoke(Function& function,
 | |
|     io_op<Stream, Operation, Handler>* this_handler)
 | |
| {
 | |
|   asio_handler_invoke_helpers::invoke(
 | |
|       function, this_handler->handler_);
 | |
| #if defined(ASIO_NO_DEPRECATED)
 | |
|   return asio_handler_invoke_is_no_longer_used();
 | |
| #endif // defined(ASIO_NO_DEPRECATED)
 | |
| }
 | |
| 
 | |
| template <typename Function, typename Stream,
 | |
|     typename Operation, typename Handler>
 | |
| inline asio_handler_invoke_is_deprecated
 | |
| asio_handler_invoke(const Function& function,
 | |
|     io_op<Stream, Operation, Handler>* this_handler)
 | |
| {
 | |
|   asio_handler_invoke_helpers::invoke(
 | |
|       function, this_handler->handler_);
 | |
| #if defined(ASIO_NO_DEPRECATED)
 | |
|   return asio_handler_invoke_is_no_longer_used();
 | |
| #endif // defined(ASIO_NO_DEPRECATED)
 | |
| }
 | |
| 
 | |
| template <typename Stream, typename Operation, typename Handler>
 | |
| inline void async_io(Stream& next_layer, stream_core& core,
 | |
|     const Operation& op, Handler& handler)
 | |
| {
 | |
|   io_op<Stream, Operation, Handler>(
 | |
|     next_layer, core, op, handler)(
 | |
|       asio::error_code(), 0, 1);
 | |
| }
 | |
| 
 | |
| } // namespace detail
 | |
| } // namespace ssl
 | |
| 
 | |
| template <template <typename, typename> class Associator,
 | |
|     typename Stream, typename Operation,
 | |
|     typename Handler, typename DefaultCandidate>
 | |
| struct associator<Associator,
 | |
|     ssl::detail::io_op<Stream, Operation, Handler>,
 | |
|     DefaultCandidate>
 | |
|   : Associator<Handler, DefaultCandidate>
 | |
| {
 | |
|   static typename Associator<Handler, DefaultCandidate>::type
 | |
|   get(const ssl::detail::io_op<Stream, Operation, Handler>& h)
 | |
|     ASIO_NOEXCEPT
 | |
|   {
 | |
|     return Associator<Handler, DefaultCandidate>::get(h.handler_);
 | |
|   }
 | |
| 
 | |
|   static ASIO_AUTO_RETURN_TYPE_PREFIX2(
 | |
|       typename Associator<Handler, DefaultCandidate>::type)
 | |
|   get(const ssl::detail::io_op<Stream, Operation, Handler>& h,
 | |
|       const DefaultCandidate& c) ASIO_NOEXCEPT
 | |
|     ASIO_AUTO_RETURN_TYPE_SUFFIX((
 | |
|       Associator<Handler, DefaultCandidate>::get(h.handler_, c)))
 | |
|   {
 | |
|     return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // namespace asio
 | |
| 
 | |
| #include "asio/detail/pop_options.hpp"
 | |
| 
 | |
| #endif // ASIO_SSL_DETAIL_IO_HPP
 |