Chris@16: // Chris@16: // ssl/detail/io.hpp Chris@16: // ~~~~~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_SSL_DETAIL_IO_HPP Chris@16: #define BOOST_ASIO_SSL_DETAIL_IO_HPP Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: # pragma once Chris@16: #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: Chris@16: #include Chris@16: Chris@16: #if !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: namespace ssl { Chris@16: namespace detail { Chris@16: Chris@16: #if !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: template Chris@16: std::size_t io(Stream& next_layer, stream_core& core, Chris@16: const Operation& op, boost::system::error_code& ec) Chris@16: { Chris@16: std::size_t bytes_transferred = 0; Chris@16: do switch (op(core.engine_, ec, bytes_transferred)) Chris@16: { Chris@16: case engine::want_input_and_retry: Chris@16: Chris@16: // If the input buffer is empty then we need to read some more data from Chris@16: // the underlying transport. Chris@16: if (boost::asio::buffer_size(core.input_) == 0) Chris@16: core.input_ = boost::asio::buffer(core.input_buffer_, Chris@16: next_layer.read_some(core.input_buffer_, ec)); Chris@16: Chris@16: // Pass the new input data to the engine. Chris@16: core.input_ = core.engine_.put_input(core.input_); Chris@16: Chris@16: // Try the operation again. Chris@16: continue; Chris@16: Chris@16: case engine::want_output_and_retry: Chris@16: Chris@16: // Get output data from the engine and write it to the underlying Chris@16: // transport. Chris@16: boost::asio::write(next_layer, Chris@16: core.engine_.get_output(core.output_buffer_), ec); Chris@16: Chris@16: // Try the operation again. Chris@16: continue; Chris@16: Chris@16: case engine::want_output: Chris@16: Chris@16: // Get output data from the engine and write it to the underlying Chris@16: // transport. Chris@16: boost::asio::write(next_layer, Chris@16: core.engine_.get_output(core.output_buffer_), ec); Chris@16: Chris@16: // Operation is complete. Return result to caller. Chris@16: core.engine_.map_error_code(ec); Chris@16: return bytes_transferred; Chris@16: Chris@16: default: Chris@16: Chris@16: // Operation is complete. Return result to caller. Chris@16: core.engine_.map_error_code(ec); Chris@16: return bytes_transferred; Chris@16: Chris@16: } while (!ec); Chris@16: Chris@16: // Operation failed. Return result to caller. Chris@16: core.engine_.map_error_code(ec); Chris@16: return 0; Chris@16: } Chris@16: Chris@16: template Chris@16: class io_op Chris@16: { Chris@16: public: Chris@16: io_op(Stream& next_layer, stream_core& core, Chris@16: const Operation& op, Handler& handler) Chris@16: : next_layer_(next_layer), Chris@16: core_(core), Chris@16: op_(op), Chris@16: start_(0), Chris@16: want_(engine::want_nothing), Chris@16: bytes_transferred_(0), Chris@16: handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)) Chris@16: { Chris@16: } Chris@16: Chris@16: #if defined(BOOST_ASIO_HAS_MOVE) Chris@16: io_op(const io_op& other) Chris@16: : next_layer_(other.next_layer_), Chris@16: core_(other.core_), Chris@16: op_(other.op_), Chris@16: start_(other.start_), Chris@16: want_(other.want_), Chris@16: ec_(other.ec_), Chris@16: bytes_transferred_(other.bytes_transferred_), Chris@16: handler_(other.handler_) Chris@16: { Chris@16: } Chris@16: Chris@16: io_op(io_op&& other) Chris@16: : next_layer_(other.next_layer_), Chris@16: core_(other.core_), Chris@16: op_(other.op_), Chris@16: start_(other.start_), Chris@16: want_(other.want_), Chris@16: ec_(other.ec_), Chris@16: bytes_transferred_(other.bytes_transferred_), Chris@16: handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_)) Chris@16: { Chris@16: } Chris@16: #endif // defined(BOOST_ASIO_HAS_MOVE) Chris@16: Chris@16: void operator()(boost::system::error_code ec, Chris@16: std::size_t bytes_transferred = ~std::size_t(0), int start = 0) Chris@16: { Chris@16: switch (start_ = start) Chris@16: { Chris@16: case 1: // Called after at least one async operation. Chris@16: do Chris@16: { Chris@16: switch (want_ = op_(core_.engine_, ec_, bytes_transferred_)) Chris@16: { Chris@16: case engine::want_input_and_retry: Chris@16: Chris@16: // If the input buffer already has data in it we can pass it to the Chris@16: // engine and then retry the operation immediately. Chris@16: if (boost::asio::buffer_size(core_.input_) != 0) Chris@16: { Chris@16: core_.input_ = core_.engine_.put_input(core_.input_); Chris@16: continue; Chris@16: } Chris@16: Chris@16: // The engine wants more data to be read from input. However, we Chris@16: // cannot allow more than one read operation at a time on the Chris@16: // underlying transport. The pending_read_ timer's expiry is set to Chris@16: // pos_infin if a read is in progress, and neg_infin otherwise. Chris@16: if (core_.pending_read_.expires_at() == core_.neg_infin()) Chris@16: { Chris@16: // Prevent other read operations from being started. Chris@16: core_.pending_read_.expires_at(core_.pos_infin()); Chris@16: Chris@16: // Start reading some data from the underlying transport. Chris@16: next_layer_.async_read_some( Chris@16: boost::asio::buffer(core_.input_buffer_), Chris@16: BOOST_ASIO_MOVE_CAST(io_op)(*this)); Chris@16: } Chris@16: else Chris@16: { Chris@16: // Wait until the current read operation completes. Chris@16: core_.pending_read_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this)); Chris@16: } Chris@16: Chris@16: // Yield control until asynchronous operation completes. Control Chris@16: // resumes at the "default:" label below. Chris@16: return; Chris@16: Chris@16: case engine::want_output_and_retry: Chris@16: case engine::want_output: Chris@16: Chris@16: // The engine wants some data to be written to the output. However, we Chris@16: // cannot allow more than one write operation at a time on the Chris@16: // underlying transport. The pending_write_ timer's expiry is set to Chris@16: // pos_infin if a write is in progress, and neg_infin otherwise. Chris@16: if (core_.pending_write_.expires_at() == core_.neg_infin()) Chris@16: { Chris@16: // Prevent other write operations from being started. Chris@16: core_.pending_write_.expires_at(core_.pos_infin()); Chris@16: Chris@16: // Start writing all the data to the underlying transport. Chris@16: boost::asio::async_write(next_layer_, Chris@16: core_.engine_.get_output(core_.output_buffer_), Chris@16: BOOST_ASIO_MOVE_CAST(io_op)(*this)); Chris@16: } Chris@16: else Chris@16: { Chris@16: // Wait until the current write operation completes. Chris@16: core_.pending_write_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this)); Chris@16: } Chris@16: Chris@16: // Yield control until asynchronous operation completes. Control Chris@16: // resumes at the "default:" label below. Chris@16: return; Chris@16: Chris@16: default: Chris@16: Chris@16: // The SSL operation is done and we can invoke the handler, but we Chris@16: // have to keep in mind that this function might be being called from Chris@16: // the async operation's initiating function. In this case we're not Chris@16: // allowed to call the handler directly. Instead, issue a zero-sized Chris@16: // read so the handler runs "as-if" posted using io_service::post(). Chris@16: if (start) Chris@16: { Chris@16: next_layer_.async_read_some( Chris@16: boost::asio::buffer(core_.input_buffer_, 0), Chris@16: BOOST_ASIO_MOVE_CAST(io_op)(*this)); Chris@16: Chris@16: // Yield control until asynchronous operation completes. Control Chris@16: // resumes at the "default:" label below. Chris@16: return; Chris@16: } Chris@16: else Chris@16: { Chris@16: // Continue on to run handler directly. Chris@16: break; Chris@16: } Chris@16: } Chris@16: Chris@16: default: Chris@101: if (bytes_transferred == ~std::size_t(0)) Chris@101: bytes_transferred = 0; // Timer cancellation, no data transferred. Chris@101: else if (!ec_) Chris@16: ec_ = ec; Chris@16: Chris@16: switch (want_) Chris@16: { Chris@16: case engine::want_input_and_retry: Chris@16: Chris@16: // Add received data to the engine's input. Chris@16: core_.input_ = boost::asio::buffer( Chris@16: core_.input_buffer_, bytes_transferred); Chris@16: core_.input_ = core_.engine_.put_input(core_.input_); Chris@16: Chris@16: // Release any waiting read operations. Chris@16: core_.pending_read_.expires_at(core_.neg_infin()); Chris@16: Chris@16: // Try the operation again. Chris@16: continue; Chris@16: Chris@16: case engine::want_output_and_retry: Chris@16: Chris@16: // Release any waiting write operations. Chris@16: core_.pending_write_.expires_at(core_.neg_infin()); Chris@16: Chris@16: // Try the operation again. Chris@16: continue; Chris@16: Chris@16: case engine::want_output: Chris@16: Chris@16: // Release any waiting write operations. Chris@16: core_.pending_write_.expires_at(core_.neg_infin()); Chris@16: Chris@16: // Fall through to call handler. Chris@16: Chris@16: default: Chris@16: Chris@16: // Pass the result to the handler. Chris@16: op_.call_handler(handler_, Chris@16: core_.engine_.map_error_code(ec_), Chris@16: ec_ ? 0 : bytes_transferred_); Chris@16: Chris@16: // Our work here is done. Chris@16: return; Chris@16: } Chris@16: } while (!ec_); Chris@16: Chris@16: // Operation failed. Pass the result to the handler. Chris@16: op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0); Chris@16: } Chris@16: } Chris@16: Chris@16: //private: Chris@16: Stream& next_layer_; Chris@16: stream_core& core_; Chris@16: Operation op_; Chris@16: int start_; Chris@16: engine::want want_; Chris@16: boost::system::error_code ec_; Chris@16: std::size_t bytes_transferred_; Chris@16: Handler handler_; Chris@16: }; Chris@16: Chris@16: template Chris@16: inline void* asio_handler_allocate(std::size_t size, Chris@16: io_op* this_handler) Chris@16: { Chris@16: return boost_asio_handler_alloc_helpers::allocate( Chris@16: size, this_handler->handler_); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void asio_handler_deallocate(void* pointer, std::size_t size, Chris@16: io_op* this_handler) Chris@16: { Chris@16: boost_asio_handler_alloc_helpers::deallocate( Chris@16: pointer, size, this_handler->handler_); Chris@16: } Chris@16: Chris@16: template Chris@16: inline bool asio_handler_is_continuation( Chris@16: io_op* this_handler) Chris@16: { Chris@16: return this_handler->start_ == 0 ? true Chris@16: : boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void asio_handler_invoke(Function& function, Chris@16: io_op* this_handler) Chris@16: { Chris@16: boost_asio_handler_invoke_helpers::invoke( Chris@16: function, this_handler->handler_); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void asio_handler_invoke(const Function& function, Chris@16: io_op* this_handler) Chris@16: { Chris@16: boost_asio_handler_invoke_helpers::invoke( Chris@16: function, this_handler->handler_); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void async_io(Stream& next_layer, stream_core& core, Chris@16: const Operation& op, Handler& handler) Chris@16: { Chris@16: io_op( Chris@16: next_layer, core, op, handler)( Chris@16: boost::system::error_code(), 0, 1); Chris@16: } Chris@16: Chris@16: #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: } // namespace detail Chris@16: } // namespace ssl Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_ASIO_SSL_DETAIL_IO_HPP