| 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(x: 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 | If the handler has an associated immediate executor, |
| 312 | an immediate completion will be dispatched to it. |
| 313 | Otherwise, the handler will not be invoked from within |
| 314 | this function. Invocation of the handler will be performed in a |
| 315 | manner equivalent to using `net::post`. |
| 316 | */ |
| 317 | template< |
| 318 | class AsyncReadStream, |
| 319 | class DynamicBuffer, |
| 320 | class CompletionToken = |
| 321 | net::default_completion_token_t<beast::executor_type<AsyncReadStream>> |
| 322 | > |
| 323 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) |
| 324 | async_detect_ssl( |
| 325 | AsyncReadStream& stream, |
| 326 | DynamicBuffer& buffer, |
| 327 | CompletionToken&& token = net::default_completion_token_t< |
| 328 | beast::executor_type<AsyncReadStream>>{}); |
| 329 | //] |
| 330 | |
| 331 | //[example_core_detect_ssl_5 |
| 332 | |
| 333 | // These implementation details don't need to be public |
| 334 | |
| 335 | namespace detail { |
| 336 | |
| 337 | // The composed operation object |
| 338 | template< |
| 339 | class DetectHandler, |
| 340 | class AsyncReadStream, |
| 341 | class DynamicBuffer> |
| 342 | class detect_ssl_op; |
| 343 | |
| 344 | // This is a function object which `net::async_initiate` can use to launch |
| 345 | // our composed operation. This is a relatively new feature in networking |
| 346 | // which allows the asynchronous operation to be "lazily" executed (meaning |
| 347 | // that it is launched later). Users don't need to worry about this, but |
| 348 | // authors of composed operations need to write it this way to get the |
| 349 | // very best performance, for example when using Coroutines TS (`co_await`). |
| 350 | |
| 351 | struct run_detect_ssl_op |
| 352 | { |
| 353 | // The implementation of `net::async_initiate` captures the |
| 354 | // arguments of the initiating function, and then calls this |
| 355 | // function object later with the captured arguments in order |
| 356 | // to launch the composed operation. All we need to do here |
| 357 | // is take those arguments and construct our composed operation |
| 358 | // object. |
| 359 | // |
| 360 | // `async_initiate` takes care of transforming the completion |
| 361 | // token into the "real handler" which must have the correct |
| 362 | // signature, in this case `void(error_code, boost::tri_bool)`. |
| 363 | |
| 364 | template< |
| 365 | class DetectHandler, |
| 366 | class AsyncReadStream, |
| 367 | class DynamicBuffer> |
| 368 | void operator()( |
| 369 | DetectHandler&& h, |
| 370 | AsyncReadStream* s, // references are passed as pointers |
| 371 | DynamicBuffer* b) |
| 372 | { |
| 373 | detect_ssl_op< |
| 374 | typename std::decay<DetectHandler>::type, |
| 375 | AsyncReadStream, |
| 376 | DynamicBuffer>( |
| 377 | std::forward<DetectHandler>(h), *s, *b); |
| 378 | } |
| 379 | }; |
| 380 | |
| 381 | } // detail |
| 382 | |
| 383 | //] |
| 384 | |
| 385 | //[example_core_detect_ssl_6 |
| 386 | |
| 387 | // Here is the implementation of the asynchronous initiation function |
| 388 | template< |
| 389 | class AsyncReadStream, |
| 390 | class DynamicBuffer, |
| 391 | class CompletionToken> |
| 392 | BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void(error_code, bool)) |
| 393 | async_detect_ssl( |
| 394 | AsyncReadStream& stream, |
| 395 | DynamicBuffer& buffer, |
| 396 | CompletionToken&& token) |
| 397 | { |
| 398 | // Make sure arguments meet the type requirements |
| 399 | |
| 400 | static_assert( |
| 401 | is_async_read_stream<AsyncReadStream>::value, |
| 402 | "SyncReadStream type requirements not met" ); |
| 403 | |
| 404 | static_assert( |
| 405 | net::is_dynamic_buffer<DynamicBuffer>::value, |
| 406 | "DynamicBuffer type requirements not met" ); |
| 407 | |
| 408 | // The function `net::async_initate` uses customization points |
| 409 | // to allow one asynchronous initiating function to work with |
| 410 | // all sorts of notification systems, such as callbacks but also |
| 411 | // fibers, futures, coroutines, and user-defined types. |
| 412 | // |
| 413 | // It works by capturing all of the arguments using perfect |
| 414 | // forwarding, and then depending on the specialization of |
| 415 | // `net::async_result` for the type of `CompletionToken`, |
| 416 | // the `initiation` object will be invoked with the saved |
| 417 | // parameters and the actual completion handler. Our |
| 418 | // initiating object is `run_detect_ssl_op`. |
| 419 | // |
| 420 | // Non-const references need to be passed as pointers, |
| 421 | // since we don't want a decay-copy. |
| 422 | |
| 423 | return net::async_initiate< |
| 424 | CompletionToken, |
| 425 | void(error_code, bool)>( |
| 426 | detail::run_detect_ssl_op{}, |
| 427 | token, |
| 428 | &stream, // pass the reference by pointer |
| 429 | &buffer); |
| 430 | } |
| 431 | |
| 432 | //] |
| 433 | |
| 434 | //[example_core_detect_ssl_7 |
| 435 | |
| 436 | namespace detail { |
| 437 | |
| 438 | // Read from a stream, calling is_tls_client_hello on the data |
| 439 | // data to determine if the TLS client handshake is present. |
| 440 | // |
| 441 | // This will be implemented using Asio's "stackless coroutines" |
| 442 | // which are based on macros forming a switch statement. The |
| 443 | // operation is derived from `coroutine` for this reason. |
| 444 | // |
| 445 | // The library type `async_base` takes care of all of the |
| 446 | // boilerplate for writing composed operations, including: |
| 447 | // |
| 448 | // * Storing the user's completion handler |
| 449 | // * Maintaining the work guard for the handler's associated executor |
| 450 | // * Propagating the associated allocator of the handler |
| 451 | // * Propagating the associated executor of the handler |
| 452 | // * Deallocating temporary storage before invoking the handler |
| 453 | // * Posting the handler to the executor on an immediate completion |
| 454 | // |
| 455 | // `async_base` needs to know the type of the handler, as well |
| 456 | // as the executor of the I/O object being used. The metafunction |
| 457 | // `executor_type` returns the type of executor used by an |
| 458 | // I/O object. |
| 459 | // |
| 460 | template< |
| 461 | class DetectHandler, |
| 462 | class AsyncReadStream, |
| 463 | class DynamicBuffer> |
| 464 | class detect_ssl_op |
| 465 | : public boost::asio::coroutine |
| 466 | , public async_base< |
| 467 | DetectHandler, executor_type<AsyncReadStream>> |
| 468 | { |
| 469 | // This composed operation has trivial state, |
| 470 | // so it is just kept inside the class and can |
| 471 | // be cheaply copied as needed by the implementation. |
| 472 | |
| 473 | AsyncReadStream& stream_; |
| 474 | |
| 475 | // The callers buffer is used to hold all received data |
| 476 | DynamicBuffer& buffer_; |
| 477 | |
| 478 | // We're going to need this in case we have to post the handler |
| 479 | error_code ec_; |
| 480 | |
| 481 | boost::tribool result_ = false; |
| 482 | |
| 483 | public: |
| 484 | // Completion handlers must be MoveConstructible. |
| 485 | detect_ssl_op(detect_ssl_op&&) = default; |
| 486 | |
| 487 | // Construct the operation. The handler is deduced through |
| 488 | // the template type `DetectHandler_`, this lets the same constructor |
| 489 | // work properly for both lvalues and rvalues. |
| 490 | // |
| 491 | template<class DetectHandler_> |
| 492 | detect_ssl_op( |
| 493 | DetectHandler_&& handler, |
| 494 | AsyncReadStream& stream, |
| 495 | DynamicBuffer& buffer) |
| 496 | : beast::async_base< |
| 497 | DetectHandler, |
| 498 | beast::executor_type<AsyncReadStream>>( |
| 499 | std::forward<DetectHandler_>(handler), |
| 500 | stream.get_executor()) |
| 501 | , stream_(stream) |
| 502 | , buffer_(buffer) |
| 503 | { |
| 504 | // This starts the operation. We pass `false` to tell the |
| 505 | // algorithm that it needs to use net::post if it wants to |
| 506 | // complete immediately. This is required by Networking, |
| 507 | // as initiating functions are not allowed to invoke the |
| 508 | // completion handler on the caller's thread before |
| 509 | // returning. |
| 510 | (*this)({}, 0, false); |
| 511 | } |
| 512 | |
| 513 | // Our main entry point. This will get called as our |
| 514 | // intermediate operations complete. Definition below. |
| 515 | // |
| 516 | // The parameter `cont` indicates if we are being called subsequently |
| 517 | // from the original invocation |
| 518 | // |
| 519 | void operator()( |
| 520 | error_code ec, |
| 521 | std::size_t bytes_transferred, |
| 522 | bool cont = true); |
| 523 | }; |
| 524 | |
| 525 | } // detail |
| 526 | |
| 527 | //] |
| 528 | |
| 529 | //[example_core_detect_ssl_8 |
| 530 | |
| 531 | namespace detail { |
| 532 | |
| 533 | // This example uses the Asio's stackless "fauxroutines", implemented |
| 534 | // using a macro-based solution. It makes the code easier to write and |
| 535 | // easier to read. This include file defines the necessary macros and types. |
| 536 | #include <boost/asio/yield.hpp> |
| 537 | |
| 538 | // detect_ssl_op is callable with the signature void(error_code, bytes_transferred), |
| 539 | // allowing `*this` to be used as a ReadHandler |
| 540 | // |
| 541 | template< |
| 542 | class AsyncStream, |
| 543 | class DynamicBuffer, |
| 544 | class Handler> |
| 545 | void |
| 546 | detect_ssl_op<AsyncStream, DynamicBuffer, Handler>:: |
| 547 | operator()(error_code ec, std::size_t bytes_transferred, bool cont) |
| 548 | { |
| 549 | namespace beast = boost::beast; |
| 550 | |
| 551 | // This introduces the scope of the stackless coroutine |
| 552 | reenter(*this) |
| 553 | { |
| 554 | // Loop until an error occurs or we get a definitive answer |
| 555 | for(;;) |
| 556 | { |
| 557 | // There could already be a hello in the buffer so check first |
| 558 | result_ = is_tls_client_hello(buffer_.data()); |
| 559 | |
| 560 | // If we got an answer, then the operation is complete |
| 561 | if(! boost::indeterminate(x: result_)) |
| 562 | break; |
| 563 | |
| 564 | // Try to fill our buffer by reading from the stream. |
| 565 | // The function read_size calculates a reasonable size for the |
| 566 | // amount to read next, using existing capacity if possible to |
| 567 | // avoid allocating memory, up to the limit of 1536 bytes which |
| 568 | // is the size of a normal TCP frame. |
| 569 | // |
| 570 | // `async_read_some` expects a ReadHandler as the completion |
| 571 | // handler. The signature of a read handler is void(error_code, size_t), |
| 572 | // and this function matches that signature (the `cont` parameter has |
| 573 | // a default of true). We pass `std::move(*this)` as the completion |
| 574 | // handler for the read operation. This transfers ownership of this |
| 575 | // entire state machine back into the `async_read_some` operation. |
| 576 | // Care must be taken with this idiom, to ensure that parameters |
| 577 | // passed to the initiating function which could be invalidated |
| 578 | // by the move, are first moved to the stack before calling the |
| 579 | // initiating function. |
| 580 | |
| 581 | yield |
| 582 | { |
| 583 | // This macro facilitates asynchrnous handler tracking and |
| 584 | // debugging when the preprocessor macro |
| 585 | // BOOST_ASIO_CUSTOM_HANDLER_TRACKING is defined. |
| 586 | |
| 587 | BOOST_ASIO_HANDLER_LOCATION(( |
| 588 | __FILE__, __LINE__, |
| 589 | "async_detect_ssl" )); |
| 590 | |
| 591 | stream_.async_read_some(buffer_.prepare( |
| 592 | read_size(buffer_, 1536)), std::move(*this)); |
| 593 | } |
| 594 | |
| 595 | // Commit what we read into the buffer's input area. |
| 596 | buffer_.commit(bytes_transferred); |
| 597 | |
| 598 | // Check for an error |
| 599 | if(ec) |
| 600 | break; |
| 601 | } |
| 602 | |
| 603 | // If `cont` is true, the handler will be invoked directly. |
| 604 | // |
| 605 | // Otherwise, the handler cannot be invoked directly, because |
| 606 | // initiating functions are not allowed to call the handler |
| 607 | // before returning. Instead, the handler must be posted to |
| 608 | // the I/O context. We issue a zero-byte read using the same |
| 609 | // type of buffers used in the ordinary read above, to prevent |
| 610 | // the compiler from creating an extra instantiation of the |
| 611 | // function template. This reduces compile times and the size |
| 612 | // of the program executable. |
| 613 | |
| 614 | if(! cont) |
| 615 | { |
| 616 | // Save the error, otherwise it will be overwritten with |
| 617 | // a successful error code when this read completes |
| 618 | // immediately. |
| 619 | ec_ = ec; |
| 620 | |
| 621 | // Zero-byte reads and writes are guaranteed to complete |
| 622 | // immediately with succcess. The type of buffers and the |
| 623 | // type of handler passed here need to exactly match the types |
| 624 | // used in the call to async_read_some above, to avoid |
| 625 | // instantiating another version of the function template. |
| 626 | |
| 627 | yield |
| 628 | { |
| 629 | BOOST_ASIO_HANDLER_LOCATION(( |
| 630 | __FILE__, __LINE__, |
| 631 | "async_detect_ssl" )); |
| 632 | |
| 633 | stream_.async_read_some(buffer_.prepare(0), std::move(*this)); |
| 634 | } |
| 635 | |
| 636 | // Restore the saved error code |
| 637 | BOOST_BEAST_ASSIGN_EC(ec, ec_); |
| 638 | } |
| 639 | |
| 640 | // Invoke the final handler. |
| 641 | // At this point, we are guaranteed that the original initiating |
| 642 | // function is no longer on our stack frame. |
| 643 | |
| 644 | this->complete_now(ec, static_cast<bool>(result_)); |
| 645 | } |
| 646 | } |
| 647 | |
| 648 | // Including this file undefines the macros used by the stackless fauxroutines. |
| 649 | #include <boost/asio/unyield.hpp> |
| 650 | |
| 651 | } // detail |
| 652 | |
| 653 | //] |
| 654 | |
| 655 | } // beast |
| 656 | } // boost |
| 657 | |
| 658 | #endif |
| 659 | |