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