Imported Upstream version 1.64.0
[platform/upstream/boost.git] / boost / asio / ssl / detail / io.hpp
1 //
2 // ssl/detail/io.hpp
3 // ~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2017 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>
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:
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       start_(0),
100       want_(engine::want_nothing),
101       bytes_transferred_(0),
102       handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler))
103   {
104   }
105
106 #if defined(BOOST_ASIO_HAS_MOVE)
107   io_op(const io_op& other)
108     : next_layer_(other.next_layer_),
109       core_(other.core_),
110       op_(other.op_),
111       start_(other.start_),
112       want_(other.want_),
113       ec_(other.ec_),
114       bytes_transferred_(other.bytes_transferred_),
115       handler_(other.handler_)
116   {
117   }
118
119   io_op(io_op&& other)
120     : next_layer_(other.next_layer_),
121       core_(other.core_),
122       op_(other.op_),
123       start_(other.start_),
124       want_(other.want_),
125       ec_(other.ec_),
126       bytes_transferred_(other.bytes_transferred_),
127       handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_))
128   {
129   }
130 #endif // defined(BOOST_ASIO_HAS_MOVE)
131
132   void operator()(boost::system::error_code ec,
133       std::size_t bytes_transferred = ~std::size_t(0), int start = 0)
134   {
135     switch (start_ = start)
136     {
137     case 1: // Called after at least one async operation.
138       do
139       {
140         switch (want_ = op_(core_.engine_, ec_, bytes_transferred_))
141         {
142         case engine::want_input_and_retry:
143
144           // If the input buffer already has data in it we can pass it to the
145           // engine and then retry the operation immediately.
146           if (boost::asio::buffer_size(core_.input_) != 0)
147           {
148             core_.input_ = core_.engine_.put_input(core_.input_);
149             continue;
150           }
151
152           // The engine wants more data to be read from input. However, we
153           // cannot allow more than one read operation at a time on the
154           // underlying transport. The pending_read_ timer's expiry is set to
155           // pos_infin if a read is in progress, and neg_infin otherwise.
156           if (core_.pending_read_.expires_at() == core_.neg_infin())
157           {
158             // Prevent other read operations from being started.
159             core_.pending_read_.expires_at(core_.pos_infin());
160
161             // Start reading some data from the underlying transport.
162             next_layer_.async_read_some(
163                 boost::asio::buffer(core_.input_buffer_),
164                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
165           }
166           else
167           {
168             // Wait until the current read operation completes.
169             core_.pending_read_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
170           }
171
172           // Yield control until asynchronous operation completes. Control
173           // resumes at the "default:" label below.
174           return;
175
176         case engine::want_output_and_retry:
177         case engine::want_output:
178
179           // The engine wants some data to be written to the output. However, we
180           // cannot allow more than one write operation at a time on the
181           // underlying transport. The pending_write_ timer's expiry is set to
182           // pos_infin if a write is in progress, and neg_infin otherwise.
183           if (core_.pending_write_.expires_at() == core_.neg_infin())
184           {
185             // Prevent other write operations from being started.
186             core_.pending_write_.expires_at(core_.pos_infin());
187
188             // Start writing all the data to the underlying transport.
189             boost::asio::async_write(next_layer_,
190                 core_.engine_.get_output(core_.output_buffer_),
191                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
192           }
193           else
194           {
195             // Wait until the current write operation completes.
196             core_.pending_write_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
197           }
198
199           // Yield control until asynchronous operation completes. Control
200           // resumes at the "default:" label below.
201           return;
202
203         default:
204
205           // The SSL operation is done and we can invoke the handler, but we
206           // have to keep in mind that this function might be being called from
207           // the async operation's initiating function. In this case we're not
208           // allowed to call the handler directly. Instead, issue a zero-sized
209           // read so the handler runs "as-if" posted using io_service::post().
210           if (start)
211           {
212             next_layer_.async_read_some(
213                 boost::asio::buffer(core_.input_buffer_, 0),
214                 BOOST_ASIO_MOVE_CAST(io_op)(*this));
215
216             // Yield control until asynchronous operation completes. Control
217             // resumes at the "default:" label below.
218             return;
219           }
220           else
221           {
222             // Continue on to run handler directly.
223             break;
224           }
225         }
226
227         default:
228         if (bytes_transferred == ~std::size_t(0))
229           bytes_transferred = 0; // Timer cancellation, no data transferred.
230         else if (!ec_)
231           ec_ = ec;
232
233         switch (want_)
234         {
235         case engine::want_input_and_retry:
236
237           // Add received data to the engine's input.
238           core_.input_ = boost::asio::buffer(
239               core_.input_buffer_, bytes_transferred);
240           core_.input_ = core_.engine_.put_input(core_.input_);
241
242           // Release any waiting read operations.
243           core_.pending_read_.expires_at(core_.neg_infin());
244
245           // Try the operation again.
246           continue;
247
248         case engine::want_output_and_retry:
249
250           // Release any waiting write operations.
251           core_.pending_write_.expires_at(core_.neg_infin());
252
253           // Try the operation again.
254           continue;
255
256         case engine::want_output:
257
258           // Release any waiting write operations.
259           core_.pending_write_.expires_at(core_.neg_infin());
260
261           // Fall through to call handler.
262
263         default:
264
265           // Pass the result to the handler.
266           op_.call_handler(handler_,
267               core_.engine_.map_error_code(ec_),
268               ec_ ? 0 : bytes_transferred_);
269
270           // Our work here is done.
271           return;
272         }
273       } while (!ec_);
274
275       // Operation failed. Pass the result to the handler.
276       op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0);
277     }
278   }
279
280 //private:
281   Stream& next_layer_;
282   stream_core& core_;
283   Operation op_;
284   int start_;
285   engine::want want_;
286   boost::system::error_code ec_;
287   std::size_t bytes_transferred_;
288   Handler handler_;
289 };
290
291 template <typename Stream, typename Operation,  typename Handler>
292 inline void* asio_handler_allocate(std::size_t size,
293     io_op<Stream, Operation, Handler>* this_handler)
294 {
295   return boost_asio_handler_alloc_helpers::allocate(
296       size, this_handler->handler_);
297 }
298
299 template <typename Stream, typename Operation, typename Handler>
300 inline void asio_handler_deallocate(void* pointer, std::size_t size,
301     io_op<Stream, Operation, Handler>* this_handler)
302 {
303   boost_asio_handler_alloc_helpers::deallocate(
304       pointer, size, this_handler->handler_);
305 }
306
307 template <typename Stream, typename Operation, typename Handler>
308 inline bool asio_handler_is_continuation(
309     io_op<Stream, Operation, Handler>* this_handler)
310 {
311   return this_handler->start_ == 0 ? true
312     : boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_);
313 }
314
315 template <typename Function, typename Stream,
316     typename Operation, typename Handler>
317 inline void asio_handler_invoke(Function& function,
318     io_op<Stream, Operation, Handler>* this_handler)
319 {
320   boost_asio_handler_invoke_helpers::invoke(
321       function, this_handler->handler_);
322 }
323
324 template <typename Function, typename Stream,
325     typename Operation, typename Handler>
326 inline void asio_handler_invoke(const Function& function,
327     io_op<Stream, Operation, Handler>* this_handler)
328 {
329   boost_asio_handler_invoke_helpers::invoke(
330       function, this_handler->handler_);
331 }
332
333 template <typename Stream, typename Operation, typename Handler>
334 inline void async_io(Stream& next_layer, stream_core& core,
335     const Operation& op, Handler& handler)
336 {
337   io_op<Stream, Operation, Handler>(
338     next_layer, core, op, handler)(
339       boost::system::error_code(), 0, 1);
340 }
341
342 #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL)
343
344 } // namespace detail
345 } // namespace ssl
346 } // namespace asio
347 } // namespace boost
348
349 #include <boost/asio/detail/pop_options.hpp>
350
351 #endif // BOOST_ASIO_SSL_DETAIL_IO_HPP