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
 |