1 //
2 // ssl/detail/io.hpp
3 // ~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 #include <boost/asio/detail/base_from_cancellation_state.hpp>
21 #include <boost/asio/detail/handler_tracking.hpp>
22 #include <boost/asio/ssl/detail/engine.hpp>
23 #include <boost/asio/ssl/detail/stream_core.hpp>
24 #include <boost/asio/write.hpp>
25
26 #include <boost/asio/detail/push_options.hpp>
27
28 namespace boost {
29 namespace asio {
30 namespace ssl {
31 namespace detail {
32
33 template <typename Stream, typename Operation>
io(Stream & next_layer,stream_core & core,const Operation & op,boost::system::error_code & ec)34 std::size_t io(Stream& next_layer, stream_core& core,
35 const Operation& op, boost::system::error_code& ec)
36 {
37 boost::system::error_code io_ec;
38 std::size_t bytes_transferred = 0;
39 do switch (op(core.engine_, ec, bytes_transferred))
40 {
41 case engine::want_input_and_retry:
42
43 // If the input buffer is empty then we need to read some more data from
44 // the underlying transport.
45 if (core.input_.size() == 0)
46 {
47 core.input_ = boost::asio::buffer(core.input_buffer_,
48 next_layer.read_some(core.input_buffer_, io_ec));
49 if (!ec)
50 ec = io_ec;
51 }
52
53 // Pass the new input data to the engine.
54 core.input_ = core.engine_.put_input(core.input_);
55
56 // Try the operation again.
57 continue;
58
59 case engine::want_output_and_retry:
60
61 // Get output data from the engine and write it to the underlying
62 // transport.
63 boost::asio::write(next_layer,
64 core.engine_.get_output(core.output_buffer_), io_ec);
65 if (!ec)
66 ec = io_ec;
67
68 // Try the operation again.
69 continue;
70
71 case engine::want_output:
72
73 // Get output data from the engine and write it to the underlying
74 // transport.
75 boost::asio::write(next_layer,
76 core.engine_.get_output(core.output_buffer_), io_ec);
77 if (!ec)
78 ec = io_ec;
79
80 // Operation is complete. Return result to caller.
81 core.engine_.map_error_code(ec);
82 return bytes_transferred;
83
84 default:
85
86 // Operation is complete. Return result to caller.
87 core.engine_.map_error_code(ec);
88 return bytes_transferred;
89
90 } while (!ec);
91
92 // Operation failed. Return result to caller.
93 core.engine_.map_error_code(ec);
94 return 0;
95 }
96
97 template <typename Stream, typename Operation, typename Handler>
98 class io_op
99 : public boost::asio::detail::base_from_cancellation_state<Handler>
100 {
101 public:
io_op(Stream & next_layer,stream_core & core,const Operation & op,Handler & handler)102 io_op(Stream& next_layer, stream_core& core,
103 const Operation& op, Handler& handler)
104 : boost::asio::detail::base_from_cancellation_state<Handler>(handler),
105 next_layer_(next_layer),
106 core_(core),
107 op_(op),
108 start_(0),
109 want_(engine::want_nothing),
110 bytes_transferred_(0),
111 handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler))
112 {
113 }
114
115 #if defined(BOOST_ASIO_HAS_MOVE)
io_op(const io_op & other)116 io_op(const io_op& other)
117 : boost::asio::detail::base_from_cancellation_state<Handler>(other),
118 next_layer_(other.next_layer_),
119 core_(other.core_),
120 op_(other.op_),
121 start_(other.start_),
122 want_(other.want_),
123 ec_(other.ec_),
124 bytes_transferred_(other.bytes_transferred_),
125 handler_(other.handler_)
126 {
127 }
128
io_op(io_op && other)129 io_op(io_op&& other)
130 : boost::asio::detail::base_from_cancellation_state<Handler>(
131 BOOST_ASIO_MOVE_CAST(
132 boost::asio::detail::base_from_cancellation_state<Handler>)(
133 other)),
134 next_layer_(other.next_layer_),
135 core_(other.core_),
136 op_(BOOST_ASIO_MOVE_CAST(Operation)(other.op_)),
137 start_(other.start_),
138 want_(other.want_),
139 ec_(other.ec_),
140 bytes_transferred_(other.bytes_transferred_),
141 handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_))
142 {
143 }
144 #endif // defined(BOOST_ASIO_HAS_MOVE)
145
operator ()(boost::system::error_code ec,std::size_t bytes_transferred=~std::size_t (0),int start=0)146 void operator()(boost::system::error_code ec,
147 std::size_t bytes_transferred = ~std::size_t(0), int start = 0)
148 {
149 switch (start_ = start)
150 {
151 case 1: // Called after at least one async operation.
152 do
153 {
154 switch (want_ = op_(core_.engine_, ec_, bytes_transferred_))
155 {
156 case engine::want_input_and_retry:
157
158 // If the input buffer already has data in it we can pass it to the
159 // engine and then retry the operation immediately.
160 if (core_.input_.size() != 0)
161 {
162 core_.input_ = core_.engine_.put_input(core_.input_);
163 continue;
164 }
165
166 // The engine wants more data to be read from input. However, we
167 // cannot allow more than one read operation at a time on the
168 // underlying transport. The pending_read_ timer's expiry is set to
169 // pos_infin if a read is in progress, and neg_infin otherwise.
170 if (core_.expiry(core_.pending_read_) == core_.neg_infin())
171 {
172 // Prevent other read operations from being started.
173 core_.pending_read_.expires_at(core_.pos_infin());
174
175 BOOST_ASIO_HANDLER_LOCATION((
176 __FILE__, __LINE__, Operation::tracking_name()));
177
178 // Start reading some data from the underlying transport.
179 next_layer_.async_read_some(
180 boost::asio::buffer(core_.input_buffer_),
181 BOOST_ASIO_MOVE_CAST(io_op)(*this));
182 }
183 else
184 {
185 BOOST_ASIO_HANDLER_LOCATION((
186 __FILE__, __LINE__, Operation::tracking_name()));
187
188 // Wait until the current read operation completes.
189 core_.pending_read_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
190 }
191
192 // Yield control until asynchronous operation completes. Control
193 // resumes at the "default:" label below.
194 return;
195
196 case engine::want_output_and_retry:
197 case engine::want_output:
198
199 // The engine wants some data to be written to the output. However, we
200 // cannot allow more than one write operation at a time on the
201 // underlying transport. The pending_write_ timer's expiry is set to
202 // pos_infin if a write is in progress, and neg_infin otherwise.
203 if (core_.expiry(core_.pending_write_) == core_.neg_infin())
204 {
205 // Prevent other write operations from being started.
206 core_.pending_write_.expires_at(core_.pos_infin());
207
208 BOOST_ASIO_HANDLER_LOCATION((
209 __FILE__, __LINE__, Operation::tracking_name()));
210
211 // Start writing all the data to the underlying transport.
212 boost::asio::async_write(next_layer_,
213 core_.engine_.get_output(core_.output_buffer_),
214 BOOST_ASIO_MOVE_CAST(io_op)(*this));
215 }
216 else
217 {
218 BOOST_ASIO_HANDLER_LOCATION((
219 __FILE__, __LINE__, Operation::tracking_name()));
220
221 // Wait until the current write operation completes.
222 core_.pending_write_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
223 }
224
225 // Yield control until asynchronous operation completes. Control
226 // resumes at the "default:" label below.
227 return;
228
229 default:
230
231 // The SSL operation is done and we can invoke the handler, but we
232 // have to keep in mind that this function might be being called from
233 // the async operation's initiating function. In this case we're not
234 // allowed to call the handler directly. Instead, issue a zero-sized
235 // read so the handler runs "as-if" posted using io_context::post().
236 if (start)
237 {
238 BOOST_ASIO_HANDLER_LOCATION((
239 __FILE__, __LINE__, Operation::tracking_name()));
240
241 next_layer_.async_read_some(
242 boost::asio::buffer(core_.input_buffer_, 0),
243 BOOST_ASIO_MOVE_CAST(io_op)(*this));
244
245 // Yield control until asynchronous operation completes. Control
246 // resumes at the "default:" label below.
247 return;
248 }
249 else
250 {
251 // Continue on to run handler directly.
252 break;
253 }
254 }
255
256 default:
257 if (bytes_transferred == ~std::size_t(0))
258 bytes_transferred = 0; // Timer cancellation, no data transferred.
259 else if (!ec_)
260 ec_ = ec;
261
262 switch (want_)
263 {
264 case engine::want_input_and_retry:
265
266 // Add received data to the engine's input.
267 core_.input_ = boost::asio::buffer(
268 core_.input_buffer_, bytes_transferred);
269 core_.input_ = core_.engine_.put_input(core_.input_);
270
271 // Release any waiting read operations.
272 core_.pending_read_.expires_at(core_.neg_infin());
273
274 // Check for cancellation before continuing.
275 if (this->cancelled() != cancellation_type::none)
276 {
277 ec_ = boost::asio::error::operation_aborted;
278 break;
279 }
280
281 // Try the operation again.
282 continue;
283
284 case engine::want_output_and_retry:
285
286 // Release any waiting write operations.
287 core_.pending_write_.expires_at(core_.neg_infin());
288
289 // Check for cancellation before continuing.
290 if (this->cancelled() != cancellation_type::none)
291 {
292 ec_ = boost::asio::error::operation_aborted;
293 break;
294 }
295
296 // Try the operation again.
297 continue;
298
299 case engine::want_output:
300
301 // Release any waiting write operations.
302 core_.pending_write_.expires_at(core_.neg_infin());
303
304 // Fall through to call handler.
305
306 default:
307
308 // Pass the result to the handler.
309 op_.call_handler(handler_,
310 core_.engine_.map_error_code(ec_),
311 ec_ ? 0 : bytes_transferred_);
312
313 // Our work here is done.
314 return;
315 }
316 } while (!ec_);
317
318 // Operation failed. Pass the result to the handler.
319 op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0);
320 }
321 }
322
323 //private:
324 Stream& next_layer_;
325 stream_core& core_;
326 Operation op_;
327 int start_;
328 engine::want want_;
329 boost::system::error_code ec_;
330 std::size_t bytes_transferred_;
331 Handler handler_;
332 };
333
334 template <typename Stream, typename Operation, typename Handler>
335 inline asio_handler_allocate_is_deprecated
asio_handler_allocate(std::size_t size,io_op<Stream,Operation,Handler> * this_handler)336 asio_handler_allocate(std::size_t size,
337 io_op<Stream, Operation, Handler>* this_handler)
338 {
339 #if defined(BOOST_ASIO_NO_DEPRECATED)
340 boost_asio_handler_alloc_helpers::allocate(size, this_handler->handler_);
341 return asio_handler_allocate_is_no_longer_used();
342 #else // defined(BOOST_ASIO_NO_DEPRECATED)
343 return boost_asio_handler_alloc_helpers::allocate(
344 size, this_handler->handler_);
345 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
346 }
347
348 template <typename Stream, typename Operation, typename Handler>
349 inline asio_handler_deallocate_is_deprecated
asio_handler_deallocate(void * pointer,std::size_t size,io_op<Stream,Operation,Handler> * this_handler)350 asio_handler_deallocate(void* pointer, std::size_t size,
351 io_op<Stream, Operation, Handler>* this_handler)
352 {
353 boost_asio_handler_alloc_helpers::deallocate(
354 pointer, size, this_handler->handler_);
355 #if defined(BOOST_ASIO_NO_DEPRECATED)
356 return asio_handler_deallocate_is_no_longer_used();
357 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
358 }
359
360 template <typename Stream, typename Operation, typename Handler>
asio_handler_is_continuation(io_op<Stream,Operation,Handler> * this_handler)361 inline bool asio_handler_is_continuation(
362 io_op<Stream, Operation, Handler>* this_handler)
363 {
364 return this_handler->start_ == 0 ? true
365 : boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_);
366 }
367
368 template <typename Function, typename Stream,
369 typename Operation, typename Handler>
370 inline asio_handler_invoke_is_deprecated
asio_handler_invoke(Function & function,io_op<Stream,Operation,Handler> * this_handler)371 asio_handler_invoke(Function& function,
372 io_op<Stream, Operation, Handler>* this_handler)
373 {
374 boost_asio_handler_invoke_helpers::invoke(
375 function, this_handler->handler_);
376 #if defined(BOOST_ASIO_NO_DEPRECATED)
377 return asio_handler_invoke_is_no_longer_used();
378 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
379 }
380
381 template <typename Function, typename Stream,
382 typename Operation, typename Handler>
383 inline asio_handler_invoke_is_deprecated
asio_handler_invoke(const Function & function,io_op<Stream,Operation,Handler> * this_handler)384 asio_handler_invoke(const Function& function,
385 io_op<Stream, Operation, Handler>* this_handler)
386 {
387 boost_asio_handler_invoke_helpers::invoke(
388 function, this_handler->handler_);
389 #if defined(BOOST_ASIO_NO_DEPRECATED)
390 return asio_handler_invoke_is_no_longer_used();
391 #endif // defined(BOOST_ASIO_NO_DEPRECATED)
392 }
393
394 template <typename Stream, typename Operation, typename Handler>
async_io(Stream & next_layer,stream_core & core,const Operation & op,Handler & handler)395 inline void async_io(Stream& next_layer, stream_core& core,
396 const Operation& op, Handler& handler)
397 {
398 io_op<Stream, Operation, Handler>(
399 next_layer, core, op, handler)(
400 boost::system::error_code(), 0, 1);
401 }
402
403 } // namespace detail
404 } // namespace ssl
405
406 template <template <typename, typename> class Associator,
407 typename Stream, typename Operation,
408 typename Handler, typename DefaultCandidate>
409 struct associator<Associator,
410 ssl::detail::io_op<Stream, Operation, Handler>,
411 DefaultCandidate>
412 : Associator<Handler, DefaultCandidate>
413 {
getboost::asio::associator414 static typename Associator<Handler, DefaultCandidate>::type get(
415 const ssl::detail::io_op<Stream, Operation, Handler>& h,
416 const DefaultCandidate& c = DefaultCandidate()) BOOST_ASIO_NOEXCEPT
417 {
418 return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
419 }
420 };
421
422 } // namespace asio
423 } // namespace boost
424
425 #include <boost/asio/detail/pop_options.hpp>
426
427 #endif // BOOST_ASIO_SSL_DETAIL_IO_HPP
428