You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
336 lines
10 KiB
336 lines
10 KiB
// |
|
// ssl/detail/io.hpp |
|
// ~~~~~~~~~~~~~~~~~ |
|
// |
|
// Copyright (c) 2003-2011 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 BOOST_ASIO_SSL_DETAIL_IO_HPP |
|
#define BOOST_ASIO_SSL_DETAIL_IO_HPP |
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200) |
|
# pragma once |
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
|
|
|
#include <boost/asio/detail/config.hpp> |
|
|
|
#if !defined(BOOST_ASIO_ENABLE_OLD_SSL) |
|
# include <boost/asio/ssl/detail/engine.hpp> |
|
# include <boost/asio/ssl/detail/stream_core.hpp> |
|
# include <boost/asio/write.hpp> |
|
#endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) |
|
|
|
#include <boost/asio/detail/push_options.hpp> |
|
|
|
namespace boost { |
|
namespace asio { |
|
namespace ssl { |
|
namespace detail { |
|
|
|
#if !defined(BOOST_ASIO_ENABLE_OLD_SSL) |
|
|
|
template <typename Stream, typename Operation> |
|
std::size_t io(Stream& next_layer, stream_core& core, |
|
const Operation& op, boost::system::error_code& 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 (boost::asio::buffer_size(core.input_) == 0) |
|
core.input_ = boost::asio::buffer(core.input_buffer_, |
|
next_layer.read_some(core.input_buffer_, 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. |
|
boost::asio::write(next_layer, |
|
core.engine_.get_output(core.output_buffer_), ec); |
|
|
|
// Try the operation again. |
|
continue; |
|
|
|
case engine::want_output: |
|
|
|
// Get output data from the engine and write it to the underlying |
|
// transport. |
|
boost::asio::write(next_layer, |
|
core.engine_.get_output(core.output_buffer_), 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: |
|
io_op(Stream& next_layer, stream_core& core, |
|
const Operation& op, Handler& handler) |
|
: next_layer_(next_layer), |
|
core_(core), |
|
op_(op), |
|
bytes_transferred_(0), |
|
handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler)) |
|
{ |
|
} |
|
|
|
#if defined(BOOST_ASIO_HAS_MOVE) |
|
io_op(const io_op& other) |
|
: next_layer_(other.next_layer_), |
|
core_(other.core_), |
|
op_(other.op_), |
|
want_(other.want_), |
|
ec_(other.ec_), |
|
bytes_transferred_(other.bytes_transferred_), |
|
handler_(other.handler_) |
|
{ |
|
} |
|
|
|
io_op(io_op&& other) |
|
: next_layer_(other.next_layer_), |
|
core_(other.core_), |
|
op_(other.op_), |
|
want_(other.want_), |
|
ec_(other.ec_), |
|
bytes_transferred_(other.bytes_transferred_), |
|
handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_)) |
|
{ |
|
} |
|
#endif // defined(BOOST_ASIO_HAS_MOVE) |
|
|
|
void operator()(boost::system::error_code ec, |
|
std::size_t bytes_transferred = ~std::size_t(0), int start = 0) |
|
{ |
|
switch (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 (boost::asio::buffer_size(core_.input_) != 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_.pending_read_.expires_at() == boost::posix_time::neg_infin) |
|
{ |
|
// Prevent other read operations from being started. |
|
core_.pending_read_.expires_at(boost::posix_time::pos_infin); |
|
|
|
// Start reading some data from the underlying transport. |
|
next_layer_.async_read_some( |
|
boost::asio::buffer(core_.input_buffer_), |
|
BOOST_ASIO_MOVE_CAST(io_op)(*this)); |
|
} |
|
else |
|
{ |
|
// Wait until the current read operation completes. |
|
core_.pending_read_.async_wait(BOOST_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_.pending_write_.expires_at() == boost::posix_time::neg_infin) |
|
{ |
|
// Prevent other write operations from being started. |
|
core_.pending_write_.expires_at(boost::posix_time::pos_infin); |
|
|
|
// Start writing all the data to the underlying transport. |
|
boost::asio::async_write(next_layer_, |
|
core_.engine_.get_output(core_.output_buffer_), |
|
BOOST_ASIO_MOVE_CAST(io_op)(*this)); |
|
} |
|
else |
|
{ |
|
// Wait until the current write operation completes. |
|
core_.pending_write_.async_wait(BOOST_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_service::post(). |
|
if (start) |
|
{ |
|
next_layer_.async_read_some( |
|
boost::asio::buffer(core_.input_buffer_, 0), |
|
BOOST_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) && !ec_) |
|
ec_ = ec; |
|
|
|
switch (want_) |
|
{ |
|
case engine::want_input_and_retry: |
|
|
|
// Add received data to the engine's input. |
|
core_.input_ = boost::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(boost::posix_time::neg_infin); |
|
|
|
// Try the operation again. |
|
continue; |
|
|
|
case engine::want_output_and_retry: |
|
|
|
// Release any waiting write operations. |
|
core_.pending_write_.expires_at(boost::posix_time::neg_infin); |
|
|
|
// Try the operation again. |
|
continue; |
|
|
|
case engine::want_output: |
|
|
|
// Release any waiting write operations. |
|
core_.pending_write_.expires_at(boost::posix_time::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_; |
|
engine::want want_; |
|
boost::system::error_code ec_; |
|
std::size_t bytes_transferred_; |
|
Handler handler_; |
|
}; |
|
|
|
template <typename Stream, typename Operation, typename Handler> |
|
inline void* asio_handler_allocate(std::size_t size, |
|
io_op<Stream, Operation, Handler>* this_handler) |
|
{ |
|
return boost_asio_handler_alloc_helpers::allocate( |
|
size, this_handler->handler_); |
|
} |
|
|
|
template <typename Stream, typename Operation, typename Handler> |
|
inline void asio_handler_deallocate(void* pointer, std::size_t size, |
|
io_op<Stream, Operation, Handler>* this_handler) |
|
{ |
|
boost_asio_handler_alloc_helpers::deallocate( |
|
pointer, size, this_handler->handler_); |
|
} |
|
|
|
template <typename Function, typename Stream, |
|
typename Operation, typename Handler> |
|
inline void asio_handler_invoke(Function& function, |
|
io_op<Stream, Operation, Handler>* this_handler) |
|
{ |
|
boost_asio_handler_invoke_helpers::invoke( |
|
function, this_handler->handler_); |
|
} |
|
|
|
template <typename Function, typename Stream, |
|
typename Operation, typename Handler> |
|
inline void asio_handler_invoke(const Function& function, |
|
io_op<Stream, Operation, Handler>* this_handler) |
|
{ |
|
boost_asio_handler_invoke_helpers::invoke( |
|
function, this_handler->handler_); |
|
} |
|
|
|
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)( |
|
boost::system::error_code(), 0, 1); |
|
} |
|
|
|
#endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) |
|
|
|
} // namespace detail |
|
} // namespace ssl |
|
} // namespace asio |
|
} // namespace boost |
|
|
|
#include <boost/asio/detail/pop_options.hpp> |
|
|
|
#endif // BOOST_ASIO_SSL_DETAIL_IO_HPP
|
|
|