1 //
2 // ssl/detail/io.hpp
3 // ~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #ifndef BOOST_ASIO_SSL_DETAIL_IO_HPP
12 #define BOOST_ASIO_SSL_DETAIL_IO_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/config.hpp>
19 
20 #if !defined(BOOST_ASIO_ENABLE_OLD_SSL)
21 # include <boost/asio/ssl/detail/engine.hpp>
22 # include <boost/asio/ssl/detail/stream_core.hpp>
23 # include <boost/asio/write.hpp>
24 #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL)
25 
26 #include <boost/asio/detail/push_options.hpp>
27 
28 namespace boost {
29 namespace asio {
30 namespace ssl {
31 namespace detail {
32 
33 #if !defined(BOOST_ASIO_ENABLE_OLD_SSL)
34 
35 template <typename Stream, typename Operation>
io(Stream & next_layer,stream_core & core,const Operation & op,boost::system::error_code & ec)36 std::size_t io(Stream& next_layer, stream_core& core,
37     const Operation& op, boost::system::error_code& ec)
38 {
39   std::size_t bytes_transferred = 0;
40   do switch (op(core.engine_, ec, bytes_transferred))
41   {
42   case engine::want_input_and_retry:
43 
44     // If the input buffer is empty then we need to read some more data from
45     // the underlying transport.
46     if (boost::asio::buffer_size(core.input_) == 0)
47       core.input_ = boost::asio::buffer(core.input_buffer_,
48           next_layer.read_some(core.input_buffer_, ec));
49 
50     // Pass the new input data to the engine.
51     core.input_ = core.engine_.put_input(core.input_);
52 
53     // Try the operation again.
54     continue;
55 
56   case engine::want_output_and_retry:
57 
58     // Get output data from the engine and write it to the underlying
59     // transport.
60     boost::asio::write(next_layer,
61         core.engine_.get_output(core.output_buffer_), ec);
62 
63     // Try the operation again.
64     continue;
65 
66   case engine::want_output:
67 
68     // Get output data from the engine and write it to the underlying
69     // transport.
70     boost::asio::write(next_layer,
71         core.engine_.get_output(core.output_buffer_), ec);
72 
73     // Operation is complete. Return result to caller.
74     core.engine_.map_error_code(ec);
75     return bytes_transferred;
76 
77   default:
78 
79     // Operation is complete. Return result to caller.
80     core.engine_.map_error_code(ec);
81     return bytes_transferred;
82 
83   } while (!ec);
84 
85   // Operation failed. Return result to caller.
86   core.engine_.map_error_code(ec);
87   return 0;
88 }
89 
90 template <typename Stream, typename Operation, typename Handler>
91 class io_op
92 {
93 public:
io_op(Stream & next_layer,stream_core & core,const Operation & op,Handler & handler)94   io_op(Stream& next_layer, stream_core& core,
95       const Operation& op, Handler& handler)
96     : next_layer_(next_layer),
97       core_(core),
98       op_(op),
99       bytes_transferred_(0),
100       handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler))
101   {
102   }
103 
104 #if defined(BOOST_ASIO_HAS_MOVE)
io_op(const io_op & other)105   io_op(const io_op& other)
106     : next_layer_(other.next_layer_),
107       core_(other.core_),
108       op_(other.op_),
109       want_(other.want_),
110       ec_(other.ec_),
111       bytes_transferred_(other.bytes_transferred_),
112       handler_(other.handler_)
113   {
114   }
115 
io_op(io_op && other)116   io_op(io_op&& other)
117     : next_layer_(other.next_layer_),
118       core_(other.core_),
119       op_(other.op_),
120       want_(other.want_),
121       ec_(other.ec_),
122       bytes_transferred_(other.bytes_transferred_),
123       handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_))
124   {
125   }
126 #endif // defined(BOOST_ASIO_HAS_MOVE)
127 
operator ()(boost::system::error_code ec,std::size_t bytes_transferred=~std::size_t (0),int start=0)128   void operator()(boost::system::error_code ec,
129       std::size_t bytes_transferred = ~std::size_t(0), int start = 0)
130   {
131     switch (start)
132     {
133     case 1: // Called after at least one async operation.
134       do
135       {
136         switch (want_ = op_(core_.engine_, ec_, bytes_transferred_))
137         {
138         case engine::want_input_and_retry:
139 
140           // If the input buffer already has data in it we can pass it to the
141           // engine and then retry the operation immediately.
142           if (boost::asio::buffer_size(core_.input_) != 0)
143           {
144             core_.input_ = core_.engine_.put_input(core_.input_);
145             continue;
146           }
147 
148           // The engine wants more data to be read from input. However, we
149           // cannot allow more than one read operation at a time on the
150           // underlying transport. The pending_read_ timer's expiry is set to
151           // pos_infin if a read is in progress, and neg_infin otherwise.
152           if (core_.pending_read_.expires_at() == boost::posix_time::neg_infin)
153           {
154             // Prevent other read operations from being started.
155             core_.pending_read_.expires_at(boost::posix_time::pos_infin);
156 
157             // Start reading some data from the underlying transport.
158             next_layer_.async_read_some(
159                 boost::asio::buffer(core_.input_buffer_),
160                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
161           }
162           else
163           {
164             // Wait until the current read operation completes.
165             core_.pending_read_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
166           }
167 
168           // Yield control until asynchronous operation completes. Control
169           // resumes at the "default:" label below.
170           return;
171 
172         case engine::want_output_and_retry:
173         case engine::want_output:
174 
175           // The engine wants some data to be written to the output. However, we
176           // cannot allow more than one write operation at a time on the
177           // underlying transport. The pending_write_ timer's expiry is set to
178           // pos_infin if a write is in progress, and neg_infin otherwise.
179           if (core_.pending_write_.expires_at() == boost::posix_time::neg_infin)
180           {
181             // Prevent other write operations from being started.
182             core_.pending_write_.expires_at(boost::posix_time::pos_infin);
183 
184             // Start writing all the data to the underlying transport.
185             boost::asio::async_write(next_layer_,
186                 core_.engine_.get_output(core_.output_buffer_),
187                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
188           }
189           else
190           {
191             // Wait until the current write operation completes.
192             core_.pending_write_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
193           }
194 
195           // Yield control until asynchronous operation completes. Control
196           // resumes at the "default:" label below.
197           return;
198 
199         default:
200 
201           // The SSL operation is done and we can invoke the handler, but we
202           // have to keep in mind that this function might be being called from
203           // the async operation's initiating function. In this case we're not
204           // allowed to call the handler directly. Instead, issue a zero-sized
205           // read so the handler runs "as-if" posted using io_service::post().
206           if (start)
207           {
208             next_layer_.async_read_some(
209                 boost::asio::buffer(core_.input_buffer_, 0),
210                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
211 
212             // Yield control until asynchronous operation completes. Control
213             // resumes at the "default:" label below.
214             return;
215           }
216           else
217           {
218             // Continue on to run handler directly.
219             break;
220           }
221         }
222 
223         default:
224         if (bytes_transferred != ~std::size_t(0) && !ec_)
225           ec_ = ec;
226 
227         switch (want_)
228         {
229         case engine::want_input_and_retry:
230 
231           // Add received data to the engine's input.
232           core_.input_ = boost::asio::buffer(
233               core_.input_buffer_, bytes_transferred);
234           core_.input_ = core_.engine_.put_input(core_.input_);
235 
236           // Release any waiting read operations.
237           core_.pending_read_.expires_at(boost::posix_time::neg_infin);
238 
239           // Try the operation again.
240           continue;
241 
242         case engine::want_output_and_retry:
243 
244           // Release any waiting write operations.
245           core_.pending_write_.expires_at(boost::posix_time::neg_infin);
246 
247           // Try the operation again.
248           continue;
249 
250         case engine::want_output:
251 
252           // Release any waiting write operations.
253           core_.pending_write_.expires_at(boost::posix_time::neg_infin);
254 
255           // Fall through to call handler.
256 
257         default:
258 
259           // Pass the result to the handler.
260           op_.call_handler(handler_,
261               core_.engine_.map_error_code(ec_),
262               ec_ ? 0 : bytes_transferred_);
263 
264           // Our work here is done.
265           return;
266         }
267       } while (!ec_);
268 
269       // Operation failed. Pass the result to the handler.
270       op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0);
271     }
272   }
273 
274 //private:
275   Stream& next_layer_;
276   stream_core& core_;
277   Operation op_;
278   engine::want want_;
279   boost::system::error_code ec_;
280   std::size_t bytes_transferred_;
281   Handler handler_;
282 };
283 
284 template <typename Stream, typename Operation,  typename Handler>
asio_handler_allocate(std::size_t size,io_op<Stream,Operation,Handler> * this_handler)285 inline void* asio_handler_allocate(std::size_t size,
286     io_op<Stream, Operation, Handler>* this_handler)
287 {
288   return boost_asio_handler_alloc_helpers::allocate(
289       size, this_handler->handler_);
290 }
291 
292 template <typename Stream, typename Operation, typename Handler>
asio_handler_deallocate(void * pointer,std::size_t size,io_op<Stream,Operation,Handler> * this_handler)293 inline void asio_handler_deallocate(void* pointer, std::size_t size,
294     io_op<Stream, Operation, Handler>* this_handler)
295 {
296   boost_asio_handler_alloc_helpers::deallocate(
297       pointer, size, this_handler->handler_);
298 }
299 
300 template <typename Function, typename Stream,
301     typename Operation, typename Handler>
asio_handler_invoke(Function & function,io_op<Stream,Operation,Handler> * this_handler)302 inline void asio_handler_invoke(Function& function,
303     io_op<Stream, Operation, Handler>* this_handler)
304 {
305   boost_asio_handler_invoke_helpers::invoke(
306       function, this_handler->handler_);
307 }
308 
309 template <typename Function, typename Stream,
310     typename Operation, typename Handler>
asio_handler_invoke(const Function & function,io_op<Stream,Operation,Handler> * this_handler)311 inline void asio_handler_invoke(const Function& function,
312     io_op<Stream, Operation, Handler>* this_handler)
313 {
314   boost_asio_handler_invoke_helpers::invoke(
315       function, this_handler->handler_);
316 }
317 
318 template <typename Stream, typename Operation, typename Handler>
async_io(Stream & next_layer,stream_core & core,const Operation & op,Handler handler)319 inline void async_io(Stream& next_layer, stream_core& core,
320     const Operation& op, Handler handler)
321 {
322   io_op<Stream, Operation, Handler>(
323     next_layer, core, op, handler)(
324       boost::system::error_code(), 0, 1);
325 }
326 
327 #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL)
328 
329 } // namespace detail
330 } // namespace ssl
331 } // namespace asio
332 } // namespace boost
333 
334 #include <boost/asio/detail/pop_options.hpp>
335 
336 #endif // BOOST_ASIO_SSL_DETAIL_IO_HPP
337