]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
1 | // |
2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | // Official repository: https://github.com/boostorg/beast | |
8 | // | |
9 | ||
10 | #ifndef BOOST_BEAST_CORE_DETECT_SSL_HPP | |
11 | #define BOOST_BEAST_CORE_DETECT_SSL_HPP | |
12 | ||
13 | #include <boost/beast/core/detail/config.hpp> | |
14 | #include <boost/beast/core/async_base.hpp> | |
15 | #include <boost/beast/core/error.hpp> | |
16 | #include <boost/beast/core/read_size.hpp> | |
17 | #include <boost/beast/core/stream_traits.hpp> | |
18 | #include <boost/logic/tribool.hpp> | |
19 | #include <boost/asio/async_result.hpp> | |
20 | #include <boost/asio/coroutine.hpp> | |
21 | #include <type_traits> | |
22 | ||
23 | namespace boost { | |
24 | namespace beast { | |
25 | ||
26 | //------------------------------------------------------------------------------ | |
27 | // | |
28 | // Example: Detect TLS client_hello | |
29 | // | |
30 | // This is an example and also a public interface. It implements | |
31 | // an algorithm for determining if a "TLS client_hello" message | |
32 | // is received. It can be used to implement a listening port that | |
33 | // can handle both plain and TLS encrypted connections. | |
34 | // | |
35 | //------------------------------------------------------------------------------ | |
36 | ||
37 | //[example_core_detect_ssl_1 | |
38 | ||
39 | // By convention, the "detail" namespace means "not-public." | |
40 | // Identifiers in a detail namespace are not visible in the documentation, | |
41 | // and users should not directly use those identifiers in programs, otherwise | |
42 | // their program may break in the future. | |
43 | // | |
44 | // Using a detail namespace gives the library writer the freedom to change | |
45 | // the interface or behavior later, and maintain backward-compatibility. | |
46 | ||
47 | namespace detail { | |
48 | ||
49 | /** Return `true` if the buffer contains a TLS Protocol client_hello message. | |
50 | ||
51 | This function analyzes the bytes at the beginning of the buffer | |
52 | and compares it to a valid client_hello message. This is the | |
53 | message required to be sent by a client at the beginning of | |
54 | any TLS (encrypted communication) session, including when | |
55 | resuming a session. | |
56 | ||
57 | The return value will be: | |
58 | ||
59 | @li `true` if the contents of the buffer unambiguously define | |
60 | contain a client_hello message, | |
61 | ||
62 | @li `false` if the contents of the buffer cannot possibly | |
63 | be a valid client_hello message, or | |
64 | ||
65 | @li `boost::indeterminate` if the buffer contains an | |
66 | insufficient number of bytes to determine the result. In | |
67 | this case the caller should read more data from the relevant | |
68 | stream, append it to the buffers, and call this function again. | |
69 | ||
70 | @param buffers The buffer sequence to inspect. | |
71 | This type must meet the requirements of <em>ConstBufferSequence</em>. | |
72 | ||
73 | @return `boost::tribool` indicating whether the buffer contains | |
74 | a TLS client handshake, does not contain a handshake, or needs | |
75 | additional bytes to determine an outcome. | |
76 | ||
77 | @see | |
78 | ||
79 | <a href="https://tools.ietf.org/html/rfc2246#section-7.4">7.4. Handshake protocol</a> | |
80 | (RFC2246: The TLS Protocol) | |
81 | */ | |
82 | template <class ConstBufferSequence> | |
83 | boost::tribool | |
84 | is_tls_client_hello (ConstBufferSequence const& buffers); | |
85 | ||
86 | } // detail | |
87 | ||
88 | //] | |
89 | ||
90 | //[example_core_detect_ssl_2 | |
91 | ||
92 | namespace detail { | |
93 | ||
94 | template <class ConstBufferSequence> | |
95 | boost::tribool | |
96 | is_tls_client_hello (ConstBufferSequence const& buffers) | |
97 | { | |
98 | // Make sure buffers meets the requirements | |
99 | static_assert( | |
100 | net::is_const_buffer_sequence<ConstBufferSequence>::value, | |
101 | "ConstBufferSequence type requirements not met"); | |
102 | ||
103 | /* | |
104 | The first message on a TLS connection must be the client_hello, | |
105 | which is a type of handshake record, and it cannot be compressed | |
106 | or encrypted. A plaintext record has this format: | |
107 | ||
108 | 0 byte record_type // 0x16 = handshake | |
109 | 1 byte major // major protocol version | |
110 | 2 byte minor // minor protocol version | |
111 | 3-4 uint16 length // size of the payload | |
112 | 5 byte handshake_type // 0x01 = client_hello | |
113 | 6 uint24 length // size of the ClientHello | |
114 | 9 byte major // major protocol version | |
115 | 10 byte minor // minor protocol version | |
116 | 11 uint32 gmt_unix_time | |
117 | 15 byte random_bytes[28] | |
118 | ... | |
119 | */ | |
120 | ||
121 | // Flatten the input buffers into a single contiguous range | |
122 | // of bytes on the stack to make it easier to work with the data. | |
123 | unsigned char buf[9]; | |
124 | auto const n = net::buffer_copy( | |
125 | net::mutable_buffer(buf, sizeof(buf)), buffers); | |
126 | ||
127 | // Can't do much without any bytes | |
128 | if(n < 1) | |
129 | return boost::indeterminate; | |
130 | ||
131 | // Require the first byte to be 0x16, indicating a TLS handshake record | |
132 | if(buf[0] != 0x16) | |
133 | return false; | |
134 | ||
135 | // We need at least 5 bytes to know the record payload size | |
136 | if(n < 5) | |
137 | return boost::indeterminate; | |
138 | ||
139 | // Calculate the record payload size | |
140 | std::uint32_t const length = (buf[3] << 8) + buf[4]; | |
141 | ||
142 | // A ClientHello message payload is at least 34 bytes. | |
143 | // There can be multiple handshake messages in the same record. | |
144 | if(length < 34) | |
145 | return false; | |
146 | ||
147 | // We need at least 6 bytes to know the handshake type | |
148 | if(n < 6) | |
149 | return boost::indeterminate; | |
150 | ||
151 | // The handshake_type must be 0x01 == client_hello | |
152 | if(buf[5] != 0x01) | |
153 | return false; | |
154 | ||
155 | // We need at least 9 bytes to know the payload size | |
156 | if(n < 9) | |
157 | return boost::indeterminate; | |
158 | ||
159 | // Calculate the message payload size | |
160 | std::uint32_t const size = | |
161 | (buf[6] << 16) + (buf[7] << 8) + buf[8]; | |
162 | ||
163 | // The message payload can't be bigger than the enclosing record | |
164 | if(size + 4 > length) | |
165 | return false; | |
166 | ||
167 | // This can only be a TLS client_hello message | |
168 | return true; | |
169 | } | |
170 | ||
171 | } // detail | |
172 | ||
173 | //] | |
174 | ||
175 | //[example_core_detect_ssl_3 | |
176 | ||
177 | /** Detect a TLS client handshake on a stream. | |
178 | ||
179 | This function reads from a stream to determine if a client | |
180 | handshake message is being received. | |
181 | ||
182 | The call blocks until one of the following is true: | |
183 | ||
184 | @li A TLS client opening handshake is detected, | |
185 | ||
186 | @li The received data is invalid for a TLS client handshake, or | |
187 | ||
188 | @li An error occurs. | |
189 | ||
190 | The algorithm, known as a <em>composed operation</em>, is implemented | |
191 | in terms of calls to the next layer's `read_some` function. | |
192 | ||
193 | Bytes read from the stream will be stored in the passed dynamic | |
194 | buffer, which may be used to perform the TLS handshake if the | |
195 | detector returns true, or be otherwise consumed by the caller based | |
196 | on the expected protocol. | |
197 | ||
198 | @param stream The stream to read from. This type must meet the | |
199 | requirements of <em>SyncReadStream</em>. | |
200 | ||
201 | @param buffer The dynamic buffer to use. This type must meet the | |
202 | requirements of <em>DynamicBuffer</em>. | |
203 | ||
204 | @param ec Set to the error if any occurred. | |
205 | ||
206 | @return `true` if the buffer contains a TLS client handshake and | |
207 | no error occurred, otherwise `false`. | |
208 | */ | |
209 | template< | |
210 | class SyncReadStream, | |
211 | class DynamicBuffer> | |
212 | bool | |
213 | detect_ssl( | |
214 | SyncReadStream& stream, | |
215 | DynamicBuffer& buffer, | |
216 | error_code& ec) | |
217 | { | |
218 | namespace beast = boost::beast; | |
219 | ||
220 | // Make sure arguments meet the requirements | |
221 | ||
222 | static_assert( | |
223 | is_sync_read_stream<SyncReadStream>::value, | |
224 | "SyncReadStream type requirements not met"); | |
225 | ||
226 | static_assert( | |
227 | net::is_dynamic_buffer<DynamicBuffer>::value, | |
228 | "DynamicBuffer type requirements not met"); | |
229 | ||
230 | // Loop until an error occurs or we get a definitive answer | |
231 | for(;;) | |
232 | { | |
233 | // There could already be data in the buffer | |
234 | // so we do this first, before reading from the stream. | |
235 | auto const result = detail::is_tls_client_hello(buffer.data()); | |
236 | ||
237 | // If we got an answer, return it | |
238 | if(! boost::indeterminate(result)) | |
239 | { | |
240 | // A definite answer is a success | |
241 | ec = {}; | |
242 | return static_cast<bool>(result); | |
243 | } | |
244 | ||
245 | // Try to fill our buffer by reading from the stream. | |
246 | // The function read_size calculates a reasonable size for the | |
247 | // amount to read next, using existing capacity if possible to | |
248 | // avoid allocating memory, up to the limit of 1536 bytes which | |
249 | // is the size of a normal TCP frame. | |
250 | ||
251 | std::size_t const bytes_transferred = stream.read_some( | |
252 | buffer.prepare(beast::read_size(buffer, 1536)), ec); | |
253 | ||
254 | // Commit what we read into the buffer's input area. | |
255 | buffer.commit(bytes_transferred); | |
256 | ||
257 | // Check for an error | |
258 | if(ec) | |
259 | break; | |
260 | } | |
261 | ||
262 | // error | |
263 | return false; | |
264 | } | |
265 | ||
266 | //] | |
267 | ||
268 | //[example_core_detect_ssl_4 | |
269 | ||
270 | /** Detect a TLS/SSL handshake asynchronously on a stream. | |
271 | ||
272 | This function reads asynchronously from a stream to determine | |
273 | if a client handshake message is being received. | |
274 | ||
275 | This call always returns immediately. The asynchronous operation | |
276 | will continue until one of the following conditions is true: | |
277 | ||
278 | @li A TLS client opening handshake is detected, | |
279 | ||
280 | @li The received data is invalid for a TLS client handshake, or | |
281 | ||
282 | @li An error occurs. | |
283 | ||
284 | The algorithm, known as a <em>composed asynchronous operation</em>, | |
285 | is implemented in terms of calls to the next layer's `async_read_some` | |
286 | function. The program must ensure that no other calls to | |
287 | `async_read_some` are performed until this operation completes. | |
288 | ||
289 | Bytes read from the stream will be stored in the passed dynamic | |
290 | buffer, which may be used to perform the TLS handshake if the | |
291 | detector returns true, or be otherwise consumed by the caller based | |
292 | on the expected protocol. | |
293 | ||
294 | @param stream The stream to read from. This type must meet the | |
295 | requirements of <em>AsyncReadStream</em>. | |
296 | ||
297 | @param buffer The dynamic buffer to use. This type must meet the | |
298 | requirements of <em>DynamicBuffer</em>. | |
299 | ||
300 | @param token The completion token used to determine the method | |
301 | used to provide the result of the asynchronous operation. If | |
302 | this is a completion handler, the implementation takes ownership | |
303 | of the handler by performing a decay-copy, and the equivalent | |
304 | function signature of the handler must be: | |
305 | @code | |
306 | void handler( | |
307 | error_code const& error, // Set to the error, if any | |
308 | bool result // The result of the detector | |
309 | ); | |
310 | @endcode | |
311 | Regardless of whether the asynchronous operation completes | |
312 | immediately or not, the handler will not be invoked from within | |
313 | this function. Invocation of the handler will be performed in a | |
314 | manner equivalent to using `net::post`. | |
315 | */ | |
316 | template< | |
317 | class AsyncReadStream, | |
318 | class DynamicBuffer, | |
319 | class CompletionToken = | |
320 | net::default_completion_token_t<beast::executor_type<AsyncReadStream>> | |
321 | > | |
322 | #if BOOST_BEAST_DOXYGEN | |
323 | BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool)) | |
324 | #else | |
325 | auto | |
326 | #endif | |
327 | async_detect_ssl( | |
328 | AsyncReadStream& stream, | |
329 | DynamicBuffer& buffer, | |
330 | CompletionToken&& token = net::default_completion_token_t< | |
331 | beast::executor_type<AsyncReadStream>>{}) -> | |
332 | typename net::async_result< | |
333 | typename std::decay<CompletionToken>::type, /*< `async_result` customizes the return value based on the completion token >*/ | |
334 | void(error_code, bool)>::return_type; /*< This is the signature for the completion handler >*/ | |
335 | //] | |
336 | ||
337 | //[example_core_detect_ssl_5 | |
338 | ||
339 | // These implementation details don't need to be public | |
340 | ||
341 | namespace detail { | |
342 | ||
343 | // The composed operation object | |
344 | template< | |
345 | class DetectHandler, | |
346 | class AsyncReadStream, | |
347 | class DynamicBuffer> | |
348 | class detect_ssl_op; | |
349 | ||
350 | // This is a function object which `net::async_initiate` can use to launch | |
351 | // our composed operation. This is a relatively new feature in networking | |
352 | // which allows the asynchronous operation to be "lazily" executed (meaning | |
353 | // that it is launched later). Users don't need to worry about this, but | |
354 | // authors of composed operations need to write it this way to get the | |
355 | // very best performance, for example when using Coroutines TS (`co_await`). | |
356 | ||
357 | struct run_detect_ssl_op | |
358 | { | |
359 | // The implementation of `net::async_initiate` captures the | |
360 | // arguments of the initiating function, and then calls this | |
361 | // function object later with the captured arguments in order | |
362 | // to launch the composed operation. All we need to do here | |
363 | // is take those arguments and construct our composed operation | |
364 | // object. | |
365 | // | |
366 | // `async_initiate` takes care of transforming the completion | |
367 | // token into the "real handler" which must have the correct | |
368 | // signature, in this case `void(error_code, boost::tri_bool)`. | |
369 | ||
370 | template< | |
371 | class DetectHandler, | |
372 | class AsyncReadStream, | |
373 | class DynamicBuffer> | |
374 | void operator()( | |
375 | DetectHandler&& h, | |
376 | AsyncReadStream* s, // references are passed as pointers | |
20effc67 | 377 | DynamicBuffer* b) |
92f5a8d4 TL |
378 | { |
379 | detect_ssl_op< | |
380 | typename std::decay<DetectHandler>::type, | |
381 | AsyncReadStream, | |
382 | DynamicBuffer>( | |
20effc67 | 383 | std::forward<DetectHandler>(h), *s, *b); |
92f5a8d4 TL |
384 | } |
385 | }; | |
386 | ||
387 | } // detail | |
388 | ||
389 | //] | |
390 | ||
391 | //[example_core_detect_ssl_6 | |
392 | ||
393 | // Here is the implementation of the asynchronous initiation function | |
394 | template< | |
395 | class AsyncReadStream, | |
396 | class DynamicBuffer, | |
397 | class CompletionToken> | |
398 | #if BOOST_BEAST_DOXYGEN | |
399 | BOOST_ASIO_INITFN_RESULT_TYPE(CompletionToken, void(error_code, bool)) | |
400 | #else | |
401 | auto | |
402 | #endif | |
403 | async_detect_ssl( | |
404 | AsyncReadStream& stream, | |
405 | DynamicBuffer& buffer, | |
406 | CompletionToken&& token) | |
407 | -> typename net::async_result< | |
408 | typename std::decay<CompletionToken>::type, | |
409 | void(error_code, bool)>::return_type | |
410 | { | |
411 | // Make sure arguments meet the type requirements | |
412 | ||
413 | static_assert( | |
414 | is_async_read_stream<AsyncReadStream>::value, | |
415 | "SyncReadStream type requirements not met"); | |
416 | ||
417 | static_assert( | |
418 | net::is_dynamic_buffer<DynamicBuffer>::value, | |
419 | "DynamicBuffer type requirements not met"); | |
420 | ||
421 | // The function `net::async_initate` uses customization points | |
422 | // to allow one asynchronous initiating function to work with | |
423 | // all sorts of notification systems, such as callbacks but also | |
424 | // fibers, futures, coroutines, and user-defined types. | |
425 | // | |
426 | // It works by capturing all of the arguments using perfect | |
427 | // forwarding, and then depending on the specialization of | |
428 | // `net::async_result` for the type of `CompletionToken`, | |
429 | // the `initiation` object will be invoked with the saved | |
430 | // parameters and the actual completion handler. Our | |
431 | // initiating object is `run_detect_ssl_op`. | |
432 | // | |
433 | // Non-const references need to be passed as pointers, | |
434 | // since we don't want a decay-copy. | |
435 | ||
436 | return net::async_initiate< | |
437 | CompletionToken, | |
438 | void(error_code, bool)>( | |
439 | detail::run_detect_ssl_op{}, | |
440 | token, | |
441 | &stream, // pass the reference by pointer | |
20effc67 | 442 | &buffer); |
92f5a8d4 TL |
443 | } |
444 | ||
445 | //] | |
446 | ||
447 | //[example_core_detect_ssl_7 | |
448 | ||
449 | namespace detail { | |
450 | ||
451 | // Read from a stream, calling is_tls_client_hello on the data | |
452 | // data to determine if the TLS client handshake is present. | |
453 | // | |
454 | // This will be implemented using Asio's "stackless coroutines" | |
455 | // which are based on macros forming a switch statement. The | |
456 | // operation is derived from `coroutine` for this reason. | |
457 | // | |
458 | // The library type `async_base` takes care of all of the | |
459 | // boilerplate for writing composed operations, including: | |
460 | // | |
461 | // * Storing the user's completion handler | |
462 | // * Maintaining the work guard for the handler's associated executor | |
463 | // * Propagating the associated allocator of the handler | |
464 | // * Propagating the associated executor of the handler | |
465 | // * Deallocating temporary storage before invoking the handler | |
466 | // * Posting the handler to the executor on an immediate completion | |
467 | // | |
468 | // `async_base` needs to know the type of the handler, as well | |
469 | // as the executor of the I/O object being used. The metafunction | |
470 | // `executor_type` returns the type of executor used by an | |
471 | // I/O object. | |
472 | // | |
473 | template< | |
474 | class DetectHandler, | |
475 | class AsyncReadStream, | |
476 | class DynamicBuffer> | |
477 | class detect_ssl_op | |
478 | : public boost::asio::coroutine | |
479 | , public async_base< | |
480 | DetectHandler, executor_type<AsyncReadStream>> | |
481 | { | |
482 | // This composed operation has trivial state, | |
483 | // so it is just kept inside the class and can | |
484 | // be cheaply copied as needed by the implementation. | |
485 | ||
486 | AsyncReadStream& stream_; | |
487 | ||
488 | // The callers buffer is used to hold all received data | |
489 | DynamicBuffer& buffer_; | |
490 | ||
491 | // We're going to need this in case we have to post the handler | |
492 | error_code ec_; | |
493 | ||
494 | boost::tribool result_ = false; | |
495 | ||
496 | public: | |
497 | // Completion handlers must be MoveConstructible. | |
498 | detect_ssl_op(detect_ssl_op&&) = default; | |
499 | ||
500 | // Construct the operation. The handler is deduced through | |
501 | // the template type `DetectHandler_`, this lets the same constructor | |
502 | // work properly for both lvalues and rvalues. | |
503 | // | |
504 | template<class DetectHandler_> | |
505 | detect_ssl_op( | |
506 | DetectHandler_&& handler, | |
507 | AsyncReadStream& stream, | |
508 | DynamicBuffer& buffer) | |
509 | : beast::async_base< | |
510 | DetectHandler, | |
511 | beast::executor_type<AsyncReadStream>>( | |
512 | std::forward<DetectHandler_>(handler), | |
513 | stream.get_executor()) | |
514 | , stream_(stream) | |
515 | , buffer_(buffer) | |
516 | { | |
517 | // This starts the operation. We pass `false` to tell the | |
518 | // algorithm that it needs to use net::post if it wants to | |
519 | // complete immediately. This is required by Networking, | |
520 | // as initiating functions are not allowed to invoke the | |
521 | // completion handler on the caller's thread before | |
522 | // returning. | |
523 | (*this)({}, 0, false); | |
524 | } | |
525 | ||
526 | // Our main entry point. This will get called as our | |
527 | // intermediate operations complete. Definition below. | |
528 | // | |
529 | // The parameter `cont` indicates if we are being called subsequently | |
530 | // from the original invocation | |
531 | // | |
532 | void operator()( | |
533 | error_code ec, | |
534 | std::size_t bytes_transferred, | |
535 | bool cont = true); | |
536 | }; | |
537 | ||
538 | } // detail | |
539 | ||
540 | //] | |
541 | ||
542 | //[example_core_detect_ssl_8 | |
543 | ||
544 | namespace detail { | |
545 | ||
546 | // This example uses the Asio's stackless "fauxroutines", implemented | |
547 | // using a macro-based solution. It makes the code easier to write and | |
548 | // easier to read. This include file defines the necessary macros and types. | |
549 | #include <boost/asio/yield.hpp> | |
550 | ||
551 | // detect_ssl_op is callable with the signature void(error_code, bytes_transferred), | |
552 | // allowing `*this` to be used as a ReadHandler | |
553 | // | |
554 | template< | |
555 | class AsyncStream, | |
556 | class DynamicBuffer, | |
557 | class Handler> | |
558 | void | |
559 | detect_ssl_op<AsyncStream, DynamicBuffer, Handler>:: | |
560 | operator()(error_code ec, std::size_t bytes_transferred, bool cont) | |
561 | { | |
562 | namespace beast = boost::beast; | |
563 | ||
564 | // This introduces the scope of the stackless coroutine | |
565 | reenter(*this) | |
566 | { | |
567 | // Loop until an error occurs or we get a definitive answer | |
568 | for(;;) | |
569 | { | |
570 | // There could already be a hello in the buffer so check first | |
571 | result_ = is_tls_client_hello(buffer_.data()); | |
572 | ||
573 | // If we got an answer, then the operation is complete | |
574 | if(! boost::indeterminate(result_)) | |
575 | break; | |
576 | ||
577 | // Try to fill our buffer by reading from the stream. | |
578 | // The function read_size calculates a reasonable size for the | |
579 | // amount to read next, using existing capacity if possible to | |
580 | // avoid allocating memory, up to the limit of 1536 bytes which | |
581 | // is the size of a normal TCP frame. | |
582 | // | |
583 | // `async_read_some` expects a ReadHandler as the completion | |
584 | // handler. The signature of a read handler is void(error_code, size_t), | |
585 | // and this function matches that signature (the `cont` parameter has | |
586 | // a default of true). We pass `std::move(*this)` as the completion | |
587 | // handler for the read operation. This transfers ownership of this | |
588 | // entire state machine back into the `async_read_some` operation. | |
589 | // Care must be taken with this idiom, to ensure that parameters | |
590 | // passed to the initiating function which could be invalidated | |
591 | // by the move, are first moved to the stack before calling the | |
592 | // initiating function. | |
593 | ||
20effc67 TL |
594 | yield |
595 | { | |
596 | // This macro facilitates asynchrnous handler tracking and | |
597 | // debugging when the preprocessor macro | |
598 | // BOOST_ASIO_CUSTOM_HANDLER_TRACKING is defined. | |
599 | ||
600 | BOOST_ASIO_HANDLER_LOCATION(( | |
601 | __FILE__, __LINE__, | |
602 | "async_detect_ssl")); | |
603 | ||
604 | stream_.async_read_some(buffer_.prepare( | |
605 | read_size(buffer_, 1536)), std::move(*this)); | |
606 | } | |
92f5a8d4 TL |
607 | |
608 | // Commit what we read into the buffer's input area. | |
609 | buffer_.commit(bytes_transferred); | |
610 | ||
611 | // Check for an error | |
612 | if(ec) | |
613 | break; | |
614 | } | |
615 | ||
616 | // If `cont` is true, the handler will be invoked directly. | |
617 | // | |
618 | // Otherwise, the handler cannot be invoked directly, because | |
619 | // initiating functions are not allowed to call the handler | |
620 | // before returning. Instead, the handler must be posted to | |
621 | // the I/O context. We issue a zero-byte read using the same | |
622 | // type of buffers used in the ordinary read above, to prevent | |
623 | // the compiler from creating an extra instantiation of the | |
624 | // function template. This reduces compile times and the size | |
625 | // of the program executable. | |
626 | ||
627 | if(! cont) | |
628 | { | |
629 | // Save the error, otherwise it will be overwritten with | |
630 | // a successful error code when this read completes | |
631 | // immediately. | |
632 | ec_ = ec; | |
633 | ||
634 | // Zero-byte reads and writes are guaranteed to complete | |
635 | // immediately with succcess. The type of buffers and the | |
636 | // type of handler passed here need to exactly match the types | |
637 | // used in the call to async_read_some above, to avoid | |
638 | // instantiating another version of the function template. | |
639 | ||
20effc67 TL |
640 | yield |
641 | { | |
642 | BOOST_ASIO_HANDLER_LOCATION(( | |
643 | __FILE__, __LINE__, | |
644 | "async_detect_ssl")); | |
645 | ||
646 | stream_.async_read_some(buffer_.prepare(0), std::move(*this)); | |
647 | } | |
92f5a8d4 TL |
648 | |
649 | // Restore the saved error code | |
650 | ec = ec_; | |
651 | } | |
652 | ||
653 | // Invoke the final handler. | |
654 | // At this point, we are guaranteed that the original initiating | |
655 | // function is no longer on our stack frame. | |
656 | ||
657 | this->complete_now(ec, static_cast<bool>(result_)); | |
658 | } | |
659 | } | |
660 | ||
661 | // Including this file undefines the macros used by the stackless fauxroutines. | |
662 | #include <boost/asio/unyield.hpp> | |
663 | ||
664 | } // detail | |
665 | ||
666 | //] | |
667 | ||
668 | } // beast | |
669 | } // boost | |
670 | ||
671 | #endif |