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