Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-17 09:00:18

0001 //
0002 // Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com),
0003 // Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
0004 //
0005 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0006 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0007 //
0008 
0009 #include <boost/redis/config.hpp>
0010 #include <boost/redis/detail/connect_fsm.hpp>
0011 #include <boost/redis/detail/coroutine.hpp>
0012 #include <boost/redis/error.hpp>
0013 #include <boost/redis/impl/log_utils.hpp>
0014 
0015 #include <boost/asio/cancellation_type.hpp>
0016 #include <boost/asio/error.hpp>
0017 #include <boost/asio/ip/tcp.hpp>
0018 #include <boost/assert.hpp>
0019 
0020 #include <string>
0021 
0022 namespace boost::redis::detail {
0023 
0024 // Logging
0025 inline void format_tcp_endpoint(const asio::ip::tcp::endpoint& ep, std::string& to)
0026 {
0027    // This formatting is inspired by Asio's endpoint operator<<
0028    const auto& addr = ep.address();
0029    if (addr.is_v6())
0030       to += '[';
0031    to += addr.to_string();
0032    if (addr.is_v6())
0033       to += ']';
0034    to += ':';
0035    to += std::to_string(ep.port());
0036 }
0037 
0038 template <>
0039 struct log_traits<asio::ip::tcp::endpoint> {
0040    static inline void log(std::string& to, const asio::ip::tcp::endpoint& value)
0041    {
0042       format_tcp_endpoint(value, to);
0043    }
0044 };
0045 
0046 template <>
0047 struct log_traits<asio::ip::tcp::resolver::results_type> {
0048    static inline void log(std::string& to, const asio::ip::tcp::resolver::results_type& value)
0049    {
0050       auto iter = value.cbegin();
0051       auto end = value.cend();
0052 
0053       if (iter != end) {
0054          format_tcp_endpoint(iter->endpoint(), to);
0055          ++iter;
0056          for (; iter != end; ++iter) {
0057             to += ", ";
0058             format_tcp_endpoint(iter->endpoint(), to);
0059          }
0060       }
0061    }
0062 };
0063 
0064 inline transport_type transport_from_config(const config& cfg)
0065 {
0066    if (cfg.unix_socket.empty()) {
0067       if (cfg.use_ssl) {
0068          return transport_type::tcp_tls;
0069       } else {
0070          return transport_type::tcp;
0071       }
0072    } else {
0073       BOOST_ASSERT(!cfg.use_ssl);
0074       return transport_type::unix_socket;
0075    }
0076 }
0077 
0078 inline system::error_code translate_timeout_error(
0079    system::error_code io_ec,
0080    asio::cancellation_type_t cancel_state,
0081    error code_if_cancelled)
0082 {
0083    // Translates cancellations and timeout errors into a single error_code.
0084    //   - Cancellation state set, and an I/O error: the entire operation was cancelled.
0085    //     The I/O code (probably operation_aborted) is appropriate.
0086    //   - Cancellation state set, and no I/O error: same as above, but the cancellation
0087    //     arrived after the operation completed and before the handler was called. Set the code here.
0088    //   - No cancellation state set, I/O error set to operation_aborted: since we use cancel_after,
0089    //     this means a timeout.
0090    //   - Otherwise, respect the I/O error.
0091    if ((cancel_state & asio::cancellation_type_t::terminal) != asio::cancellation_type_t::none) {
0092       return io_ec ? io_ec : asio::error::operation_aborted;
0093    }
0094    return io_ec == asio::error::operation_aborted ? code_if_cancelled : io_ec;
0095 }
0096 
0097 connect_action connect_fsm::resume(
0098    system::error_code ec,
0099    const asio::ip::tcp::resolver::results_type& resolver_results,
0100    redis_stream_state& st,
0101    asio::cancellation_type_t cancel_state)
0102 {
0103    // Translate error codes
0104    ec = translate_timeout_error(ec, cancel_state, error::resolve_timeout);
0105 
0106    // Log it
0107    if (ec) {
0108       log_info(*lgr_, "Error resolving the server hostname: ", ec);
0109    } else {
0110       log_info(*lgr_, "Resolve results: ", resolver_results);
0111    }
0112 
0113    // Delegate to the regular resume function
0114    return resume(ec, st, cancel_state);
0115 }
0116 
0117 connect_action connect_fsm::resume(
0118    system::error_code ec,
0119    const asio::ip::tcp::endpoint& selected_endpoint,
0120    redis_stream_state& st,
0121    asio::cancellation_type_t cancel_state)
0122 {
0123    // Translate error codes
0124    ec = translate_timeout_error(ec, cancel_state, error::connect_timeout);
0125 
0126    // Log it
0127    if (ec) {
0128       log_info(*lgr_, "Failed to connect to the server: ", ec);
0129    } else {
0130       log_info(*lgr_, "Connected to ", selected_endpoint);
0131    }
0132 
0133    // Delegate to the regular resume function
0134    return resume(ec, st, cancel_state);
0135 }
0136 
0137 connect_action connect_fsm::resume(
0138    system::error_code ec,
0139    redis_stream_state& st,
0140    asio::cancellation_type_t cancel_state)
0141 {
0142    switch (resume_point_) {
0143       BOOST_REDIS_CORO_INITIAL
0144 
0145       // Record the transport that we will be using
0146       st.type = transport_from_config(*cfg_);
0147 
0148       if (st.type == transport_type::unix_socket) {
0149          // Reset the socket, to discard any previous state. Ignore any errors
0150          BOOST_REDIS_YIELD(resume_point_, 1, connect_action_type::unix_socket_close)
0151 
0152          // Connect to the socket
0153          BOOST_REDIS_YIELD(resume_point_, 2, connect_action_type::unix_socket_connect)
0154 
0155          // Fix error codes. If we were cancelled and the code is operation_aborted,
0156          // it is because per-operation cancellation was activated. If we were not cancelled
0157          // but the operation failed with operation_aborted, it's a timeout.
0158          // Also check for cancellations that didn't cause a failure
0159          ec = translate_timeout_error(ec, cancel_state, error::connect_timeout);
0160 
0161          // Log it
0162          if (ec) {
0163             log_info(*lgr_, "Failed to connect to the server: ", ec);
0164          } else {
0165             log_info(*lgr_, "Connected to ", cfg_->unix_socket);
0166          }
0167 
0168          // If this failed, we can't continue
0169          if (ec) {
0170             return ec;
0171          }
0172 
0173          // Done
0174          return system::error_code();
0175       } else {
0176          // ssl::stream doesn't support being re-used. If we're to use
0177          // TLS and the stream has been used, re-create it.
0178          // Must be done before anything else is done on the stream.
0179          // We don't need to close the TCP socket if using plaintext TCP
0180          // because range-connect closes open sockets, while individual connect doesn't
0181          if (cfg_->use_ssl && st.ssl_stream_used) {
0182             BOOST_REDIS_YIELD(resume_point_, 3, connect_action_type::ssl_stream_reset)
0183          }
0184 
0185          // Resolve names. The continuation needs access to the returned
0186          // endpoints, and is a specialized resume() that will call this function
0187          BOOST_REDIS_YIELD(resume_point_, 4, connect_action_type::tcp_resolve)
0188 
0189          // If this failed, we can't continue (error code translation already performed here)
0190          if (ec) {
0191             return ec;
0192          }
0193 
0194          // Now connect to the endpoints returned by the resolver.
0195          // This has a specialized resume(), too
0196          BOOST_REDIS_YIELD(resume_point_, 5, connect_action_type::tcp_connect)
0197 
0198          // If this failed, we can't continue (error code translation already performed here)
0199          if (ec) {
0200             return ec;
0201          }
0202 
0203          if (cfg_->use_ssl) {
0204             // Mark the SSL stream as used
0205             st.ssl_stream_used = true;
0206 
0207             // Perform the TLS handshake
0208             BOOST_REDIS_YIELD(resume_point_, 6, connect_action_type::ssl_handshake)
0209 
0210             // Translate error codes
0211             ec = translate_timeout_error(ec, cancel_state, error::ssl_handshake_timeout);
0212 
0213             // Log it
0214             if (ec) {
0215                log_info(*lgr_, "Failed to perform SSL handshake: ", ec);
0216             } else {
0217                log_info(*lgr_, "Successfully performed SSL handshake");
0218             }
0219 
0220             // If this failed, we can't continue
0221             if (ec) {
0222                return ec;
0223             }
0224          }
0225 
0226          // Done
0227          return system::error_code();
0228       }
0229    }
0230 
0231    BOOST_ASSERT(false);
0232    return system::error_code();
0233 }
0234 
0235 }  // namespace boost::redis::detail