From 7f338cc6ffc1cecc8e308935ee948891bb1670c6 Mon Sep 17 00:00:00 2001 From: "Zezheng.Li" Date: Tue, 19 Aug 2025 14:07:57 +0800 Subject: [PATCH 01/38] [metric] fix dynamic summary refresh --- include/ylt/metric/summary.hpp | 23 +++++++------- src/metric/tests/test_metric.cpp | 54 ++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/include/ylt/metric/summary.hpp b/include/ylt/metric/summary.hpp index 57f3e9df3..32b13c927 100644 --- a/include/ylt/metric/summary.hpp +++ b/include/ylt/metric/summary.hpp @@ -60,7 +60,7 @@ class summary_t : public static_metric { summary_t(std::string name, std::string help, std::vector quantiles, std::map static_labels, - std::chrono::seconds max_age = std::chrono::seconds{60}) + std::chrono::seconds max_age = std::chrono::seconds{0}) : static_metric(MetricType::Summary, std::move(name), std::move(help), std::move(static_labels)), quantiles_(std::move(quantiles)), @@ -162,10 +162,10 @@ class basic_dynamic_summary dynamic_metric_impl, N>; public: - basic_dynamic_summary( - std::string name, std::string help, std::vector quantiles, - std::array labels_name, - std::chrono::milliseconds max_age = std::chrono::seconds{60}) + basic_dynamic_summary(std::string name, std::string help, + std::vector quantiles, + std::array labels_name, + std::chrono::seconds max_age = std::chrono::seconds{0}) : Base(MetricType::Summary, std::move(name), std::move(help), std::move(labels_name)), quantiles_(std::move(quantiles)), @@ -175,33 +175,34 @@ class basic_dynamic_summary } void observe(const std::array& labels_value, float value) { - Base::try_emplace(labels_value, quantiles_).first->value.insert(value); + Base::try_emplace(labels_value, quantiles_, max_age_) + .first->value.insert(value); } std::vector get_rates(const std::array& labels_value) { double sum; uint64_t count; - return Base::try_emplace(labels_value, quantiles_) + return Base::try_emplace(labels_value, quantiles_, max_age_) .first->value.get_rates(sum, count); } std::vector get_rates(const std::array& labels_value, uint64_t& count) { double sum; - return Base::try_emplace(labels_value, quantiles_) + return Base::try_emplace(labels_value, quantiles_, max_age_) .first->value.get_rates(sum, count); } std::vector get_rates(const std::array& labels_value, double& sum) { uint64_t count; - return Base::try_emplace(labels_value, quantiles_) + return Base::try_emplace(labels_value, quantiles_, max_age_) .first->value.get_rates(sum, count); } std::vector get_rates(const std::array& labels_value, double& sum, uint64_t& count) { - return Base::try_emplace(labels_value, quantiles_) + return Base::try_emplace(labels_value, quantiles_, max_age_) .first->value.stat(sum, count); } @@ -265,7 +266,7 @@ class basic_dynamic_summary private: std::vector quantiles_; - std::chrono::milliseconds max_age_; + std::chrono::seconds max_age_; }; using dynamic_summary_1 = basic_dynamic_summary<1>; diff --git a/src/metric/tests/test_metric.cpp b/src/metric/tests/test_metric.cpp index b926ed9e2..a86f795b1 100644 --- a/src/metric/tests/test_metric.cpp +++ b/src/metric/tests/test_metric.cpp @@ -1070,6 +1070,7 @@ TEST_CASE("test summary with many quantities") { TEST_CASE("test summary refresh") { summary_t summary{"test_summary", "summary help", {0.5, 0.9, 0.95, 1.1}, 1s}; + std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> distr(1, 100); @@ -1119,6 +1120,59 @@ TEST_CASE("test summary refresh") { CHECK(str.size() > 0); } +TEST_CASE("test dynamic summary refresh") { + auto summary = std::make_shared( + "jindosdk_testSummary_dynamic", "help", std::vector{0.5, 0.9, 0.95, 1.1}, + std::array{std::string{"bucket"}}, 1s); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distr(1, 100); + std::string str; + summary->serialize_to_json(str); + CHECK(str.size() == 0); + str = ""; + summary->serialize(str); + CHECK(str.size() == 0); + + std::this_thread::sleep_for(1001ms); + summary->serialize_to_json(str); + CHECK(str.size() == 0); + str = ""; + summary->serialize(str); + CHECK(str.size() == 0); + + for (int i = 0; i < 50; i++) { + summary->observe(std::array{std::string{"1"}}, i); + } + + double sum; + uint64_t cnt; + summary->get_rates(std::array{std::string{"1"}}, sum, cnt); + CHECK(cnt == 50); + std::this_thread::sleep_for(1s); + summary->get_rates(std::array{std::string{"1"}}, sum, cnt); + CHECK(cnt == 0); + for (int i = 0; i < 50; i++) { + summary->observe(std::array{std::string{"1"}}, i); + } + std::this_thread::sleep_for(500ms); + for (int i = 0; i < 10; i++) { + summary->observe(std::array{std::string{"1"}}, i); + } + summary->get_rates(std::array{std::string{"1"}}, sum, cnt); + CHECK(cnt == 60); + std::this_thread::sleep_for(500ms); + summary->get_rates(std::array{std::string{"1"}}, sum, cnt); + CHECK(cnt == 10); + std::this_thread::sleep_for(500ms); + summary->get_rates(std::array{std::string{"1"}}, sum, cnt); + summary->serialize_to_json(str); + CHECK(str.size() > 0); + str = ""; + summary->serialize(str); + CHECK(str.size() > 0); +} + TEST_CASE("test register metric") { auto c = std::make_shared(std::string("get_count"), std::string("get counter")); From 47412c9994520d482f64c2c4a2e5f4dc0413a874 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 17 Sep 2025 10:39:20 +0800 Subject: [PATCH 02/38] feat: rpc http support ntls --- include/ylt/coro_rpc/impl/common_service.hpp | 265 ++++++++++++++++++ include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 126 ++++++++- include/ylt/coro_rpc/impl/coro_rpc_server.hpp | 15 + .../impl/default_config/coro_rpc_config.hpp | 4 + .../standalone/cinatra/coro_http_client.hpp | 116 +++++++- .../cinatra/coro_http_connection.hpp | 166 +++++++++++ .../standalone/cinatra/coro_http_server.hpp | 70 +++++ 7 files changed, 759 insertions(+), 3 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index 3b5379a3a..d5cc35749 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -36,6 +36,54 @@ struct ssl_configure { std::string key_file; //!< relative path of private key file std::string dh_file; //!< relative path of tmp dh file (optional) }; +#ifndef OPENSSL_NO_NTLS +///*! +// * NTLS config for Tongsuo support +// */ +// struct ntls_configure { +// std::string base_path; //!< all config files base path +// std::string sign_cert_file; //!< relative path of SM2 signing +// certificate file std::string sign_key_file; //!< relative path of SM2 +// signing private key file std::string enc_cert_file; //!< relative path +// of SM2 encryption certificate file std::string enc_key_file; //!< +// relative path of SM2 encryption private key file std::string ca_cert_file; +// //!< relative path of CA certificate file (optional) bool +// enable_client_verify = false; //!< enable client certificate verification +// bool enable_ntls = true; //!< enable NTLS mode (国密SSL) +//}; + +/*! + * Extended SSL config that supports both SSL and NTLS + */ +struct ssl_ntls_configure { + std::string base_path; //!< all config files base path + + // Traditional SSL/TLS configuration + std::string cert_file; //!< relative path of certificate chain file + std::string key_file; //!< relative path of private key file + std::string dh_file; //!< relative path of tmp dh file (optional) + +#ifndef OPENSSL_NO_NTLS + // NTLS configuration for Tongsuo + std::string + sign_cert_file; //!< relative path of SM2 signing certificate file + std::string sign_key_file; //!< relative path of SM2 signing private key file + std::string + enc_cert_file; //!< relative path of SM2 encryption certificate file + std::string + enc_key_file; //!< relative path of SM2 encryption private key file + std::string + ca_cert_file; //!< relative path of CA certificate file (optional) + std::string cipher_suites; //!< NTLS cipher suites (e.g., + //!< "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3") + + bool enable_ntls = false; //!< enable NTLS mode + bool enable_client_verify = + false; //!< enable client certificate verification + bool enable_dual_mode = false; //!< enable both SSL and NTLS support +#endif +}; +#endif OPENSSL_NO_NTLS /*! * Check file (not a folder) exist @@ -113,5 +161,222 @@ inline bool init_ssl_context_helper(asio::ssl::context &context, return false; } } + +#ifndef OPENSSL_NO_NTLS +/*! + * Initialize SSL Context for NTLS with Tongsuo + * + * @param context instance of asio::ssl::context + * @param conf object of ssl_ntls_configure + * @return true if init success, otherwise false + */ +inline bool init_ntls_context_helper(asio::ssl::context &context, + const ssl_ntls_configure &conf) { + namespace fs = std::filesystem; + try { + // Set context options via asio + context.set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_sslv2 | + asio::ssl::context::single_dh_use); + + // Configure NTLS via Tongsuo native APIs + SSL_CTX* ctx = context.native_handle(); + if (!ctx) { + ELOG_ERROR << "SSL_CTX native_handle is null"; + return false; + } + + SSL_CTX_enable_ntls(ctx); + ELOG_INFO << "NTLS mode enabled successfully"; + // Set NTLS cipher suites (SM2/SM3/SM4) + std::string cipher_suites = conf.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : conf.cipher_suites; + if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_WARN << "Failed to set NTLS cipher suites '" << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + } else { + ELOG_INFO << "NTLS cipher suites set to: " << cipher_suites; + } + context.set_password_callback( + [](std::size_t size, + asio::ssl::context_base::password_purpose purpose) { + return "test"; + }); + auto sign_cert_file = fs::path(conf.base_path).append(conf.sign_cert_file); + auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); + auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); + auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); + + ELOG_INFO << "current path " << fs::current_path().string(); + + // Load SM2 signing certificate and key + if (file_exists(sign_cert_file)) { + if (SSL_CTX_use_sign_certificate_file(ctx, sign_cert_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 signing certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + } else { + ELOG_ERROR << "no SM2 signing certificate file " + << sign_cert_file.string(); + return false; + } + + if (file_exists(sign_key_file)) { + if (SSL_CTX_use_sign_PrivateKey_file(ctx, sign_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 signing private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + } else { + ELOG_ERROR << "no SM2 signing key file " + << sign_key_file.string(); + return false; + } + + // Load SM2 encryption certificate and key + if (file_exists(enc_cert_file)) { + if (SSL_CTX_use_enc_certificate_file(ctx, enc_cert_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 encryption certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + } else { + ELOG_ERROR << "no SM2 encryption certificate file " + << enc_cert_file.string(); + return false; + } + + if (file_exists(enc_key_file)) { + if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 encryption private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + } else { + ELOG_ERROR << "no SM2 encryption key file " + << enc_key_file.string(); + return false; + } + + // Load CA certificate if provided (ASIO wrapper is fine) + asio::error_code ec; + if (!conf.ca_cert_file.empty()) { + auto ca_cert_file = fs::path(conf.base_path).append(conf.ca_cert_file); + if (file_exists(ca_cert_file)) { + context.load_verify_file(ca_cert_file.string(), ec); + if (ec) { + ELOG_WARN << "failed to load CA certificate: " + << ec.message() << ", continuing without it"; + } + } + } + + // Set verification mode + if (conf.enable_client_verify) { + context.set_verify_mode(asio::ssl::verify_peer, ec); + } else { + context.set_verify_mode(asio::ssl::verify_none, ec); + } + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + + ELOG_INFO << "NTLS server context initialized successfully"; + return true; + } catch (std::exception &e) { + ELOG_ERROR << "NTLS context init error: " << e.what(); + return false; + } +} + +///*! +// * Initialize SSL Context with extended SSL/NTLS config +// * +// * @param context instance of asio::ssl::context +// * @param conf object of ssl_ntls_configure +// * @return true if init success, otherwise false +// */ +// inline bool init_ssl_ntls_context_helper(asio::ssl::context &context, +// const ssl_ntls_configure &conf) { +// if (conf.enable_ntls) { +// // Convert to ntls_configure and use NTLS initialization +// ntls_configure ntls_conf; +// ntls_conf.base_path = conf.base_path; +// ntls_conf.sign_cert_file = conf.sign_cert_file; +// ntls_conf.sign_key_file = conf.sign_key_file; +// ntls_conf.enc_cert_file = conf.enc_cert_file; +// ntls_conf.enc_key_file = conf.enc_key_file; +// ntls_conf.ca_cert_file = conf.ca_cert_file; +// ntls_conf.enable_client_verify = conf.enable_client_verify; +// ntls_conf.enable_ntls = conf.enable_ntls; +// +// return init_ntls_context_helper(context, ntls_conf); +// } else { +// // Use traditional SSL initialization +// ssl_configure ssl_conf; +// ssl_conf.base_path = conf.base_path; +// ssl_conf.cert_file = conf.cert_file; +// ssl_conf.key_file = conf.key_file; +// ssl_conf.dh_file = conf.dh_file; +// +// return init_ssl_context_helper(context, ssl_conf); +// } +//} +// +///*! +// * Initialize SSL Context with TLCP method for NTLS +// * +// * @param conf object of ntls_configure +// * @return initialized asio::ssl::context for NTLS +// */ +// inline std::unique_ptr create_ntls_context(const +// ntls_configure &conf) { +// try { +// // Create SSL context with TLCP server method +// auto context = +// std::make_unique(asio::ssl::context::tlcp_server); +// +// if (init_ntls_context_helper(*context, conf)) { +// return context; +// } +// } catch (const std::exception &e) { +// ELOG_ERROR << "Failed to create NTLS context: " << e.what(); +// } +// return nullptr; +//} +// +///*! +// * Initialize SSL Context with TLCP client method for NTLS +// * +// * @param conf object of ntls_configure +// * @return initialized asio::ssl::context for NTLS client +// */ +// inline std::unique_ptr create_ntls_client_context(const +// ntls_configure &conf) { +// try { +// // Create SSL context with TLCP client method +// auto context = +// std::make_unique(asio::ssl::context::tlcp_client); +// +// if (init_ntls_context_helper(*context, conf)) { +// return context; +// } +// } catch (const std::exception &e) { +// ELOG_ERROR << "Failed to create NTLS client context: " << e.what(); +// } +// return nullptr; +//} +#endif // OPENSSL_NO_NTLS #endif } // namespace coro_rpc \ No newline at end of file diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index 746a47224..23d16a40b 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -185,6 +185,16 @@ class coro_rpc_client { std::filesystem::path ssl_cert_path{}; std::string ssl_domain{}; }; +#ifndef OPENSSL_NO_NTLS + struct tcp_with_ntls_config { + bool enable_tcp_no_delay = true; + std::filesystem::path sign_cert_path{}; + std::filesystem::path enc_cert_path{}; + std::filesystem::path ca_cert_path{}; + std::string ssl_domain{}; + bool enable_client_verify = false; + }; +#endif // OPENSSL_NO_NTLS #endif struct config { static inline uint64_t get_global_client_id() { @@ -201,6 +211,10 @@ class coro_rpc_client { #ifdef YLT_ENABLE_SSL , tcp_with_ssl_config +#ifndef OPENSSL_NO_NTLS + , + tcp_with_ntls_config +#endif // OPENSSL_NO_NTLS #endif #ifdef YLT_ENABLE_IBV , @@ -286,6 +300,77 @@ class coro_rpc_client { } return ssl_init_ret_; } +#ifndef OPENSSL_NO_NTLS + [[nodiscard]] bool init_socket_wrapper(const tcp_with_ntls_config &config) { + try { + ssl_init_ret_ = false; + ELOG_INFO << "init NTLS: " << config.ssl_domain; + + // Create SSL context with TLCP client method for NTLS + ssl_ctx_ = asio::ssl::context(SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode - Tongsuo will handle protocol version automatically + SSL_CTX_enable_ntls(ssl_ctx_.native_handle()); + ELOG_INFO << "NTLS mode enabled successfully"; + + // Set cipher suites for NTLS (SM2/SM3/SM4) + if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + ELOG_WARN << "failed to set NTLS cipher suites, using default"; + } + + // Load certificates if provided + if (!config.sign_cert_path.empty() && + file_exists(config.sign_cert_path)) { + ELOG_INFO << "load SM2 signing cert " << config.sign_cert_path.string(); + if (!SSL_CTX_use_sign_certificate_file( + ssl_ctx_.native_handle(), + config.sign_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 signing certificate"; + return false; + } + } + + if (!config.enc_cert_path.empty() && file_exists(config.enc_cert_path)) { + ELOG_INFO << "load SM2 encryption cert " + << config.enc_cert_path.string(); + if (!SSL_CTX_use_enc_certificate_file( + ssl_ctx_.native_handle(), config.enc_cert_path.string().c_str(), + SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 encryption certificate"; + return false; + } + } + + if (!config.ca_cert_path.empty() && file_exists(config.ca_cert_path)) { + ELOG_INFO << "load CA cert " << config.ca_cert_path.string(); + ssl_ctx_.load_verify_file(config.ca_cert_path.string()); + } + + if (config.enable_client_verify) { + ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); + ssl_ctx_.set_verify_callback( + asio::ssl::host_name_verification(config.ssl_domain)); + } + else { + ssl_ctx_.set_verify_mode(asio::ssl::verify_none); + } + + auto init_result = control_->socket_wrapper_.init_client( + ssl_ctx_, config.enable_tcp_no_delay); + if (!init_result) { + return false; + } + ssl_init_ret_ = true; + + ELOG_INFO << "NTLS client context initialized successfully - protocol " + "version will be negotiated automatically"; + } catch (const std::exception &e) { + ELOG_ERROR << "init NTLS failed: " << e.what(); + } + return ssl_init_ret_; + } +#endif // OPENSSL_NO_NTLS #endif [[nodiscard]] bool init_config(const config &conf) { create_tp_ = std::chrono::steady_clock::now(); @@ -386,7 +471,7 @@ class coro_rpc_client { std::string_view domain = "localhost") { std::string ssl_domain = std::string{domain}; std::string ssl_cert_path = - std::filesystem::path(cert_base_path).append(cert_file_name); + std::filesystem::path(cert_base_path).append(cert_file_name).string(); if (config_.socket_config.index() != 1) { config_.socket_config = tcp_with_ssl_config{.ssl_cert_path = std::move(ssl_cert_path), @@ -400,6 +485,45 @@ class coro_rpc_client { return init_socket_wrapper( std::get(config_.socket_config)); } +#ifndef OPENSSL_NO_NTLS + [[nodiscard]] bool init_ntls(std::string_view cert_base_path, + std::string_view sign_cert_file, + std::string_view enc_cert_file, + std::string_view ca_cert_file = "", + std::string_view domain = "localhost", + bool enable_client_verify = false) { + std::string ssl_domain = std::string{domain}; + std::string sign_cert_path = + std::filesystem::path(cert_base_path).append(sign_cert_file).string(); + std::string enc_cert_path = + std::filesystem::path(cert_base_path).append(enc_cert_file).string(); + std::string ca_cert_path; + if (!ca_cert_file.empty()) { + ca_cert_path = + std::filesystem::path(cert_base_path).append(ca_cert_file).string(); + } + + if (config_.socket_config.index() != 2) { + config_.socket_config = + tcp_with_ntls_config{.enable_tcp_no_delay = true, + .sign_cert_path = std::move(sign_cert_path), + .enc_cert_path = std::move(enc_cert_path), + .ca_cert_path = std::move(ca_cert_path), + .ssl_domain = std::move(ssl_domain), + .enable_client_verify = enable_client_verify}; + } + else { + auto &conf = std::get(config_.socket_config); + conf.sign_cert_path = std::move(sign_cert_path); + conf.enc_cert_path = std::move(enc_cert_path); + conf.ca_cert_path = std::move(ca_cert_path); + conf.ssl_domain = std::move(ssl_domain); + conf.enable_client_verify = enable_client_verify; + } + return init_socket_wrapper( + std::get(config_.socket_config)); + } +#endif // OPENSSL_NO_NTLS #endif #ifdef YLT_ENABLE_IBV [[nodiscard]] bool init_ibv( diff --git a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp index 11c6452e1..f1dfa3221 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp @@ -114,6 +114,12 @@ class coro_rpc_server_base { if (config.ssl_config) { init_ssl_context_helper(context_, config.ssl_config.value()); } +#ifndef OPENSSL_NO_NTLS + else if (config.ssl_ntls_config) { + init_ssl_ntls_context_helper(context_, config.ssl_ntls_config.value()); + use_ssl_ = true; + } +#endif // OPENSSL_NO_NTLS else if (config.ibv_config) { init_ibv(config.ibv_config); } @@ -130,6 +136,15 @@ class coro_rpc_server_base { void init_ssl(const ssl_configure &conf) { use_ssl_ = init_ssl_context_helper(context_, conf); } +#ifndef OPENSSL_NO_NTLS + void init_ntls(const ssl_ntls_configure &conf) { + use_ssl_ = init_ntls_context_helper(context_, conf); + } + + // void init_ssl_ntls(const ssl_ntls_configure &conf) { + // use_ssl_ = init_ssl_ntls_context_helper(context_, conf); + // } +#endif // OPENSSL_NO_NTLS #endif #ifdef YLT_ENABLE_IBV void init_ibv(const coro_io::ib_socket_t::config_t &conf = {}) { diff --git a/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp b/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp index cfe8871a8..0b51c1688 100644 --- a/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp +++ b/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp @@ -39,6 +39,10 @@ struct config_base { std::string address = "0.0.0.0"; #ifdef YLT_ENABLE_SSL std::optional ssl_config = std::nullopt; +#ifndef OPENSSL_NO_NTLS + // std::optional ntls_config = std::nullopt; + std::optional ssl_ntls_config = std::nullopt; +#endif OPENSSL_NO_NTLS #endif #ifdef YLT_ENABLE_IBV std::optional ibv_config = std::nullopt; diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index c1bf0ee1d..674fec702 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -137,6 +137,10 @@ class coro_http_client : public std::enable_shared_from_this { #ifdef CINATRA_ENABLE_SSL bool use_ssl = false; // if set use_ssl true, cinatra will add https automaticlly. +#ifndef OPENSSL_NO_NTLS + bool use_ntls = + false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. +#endif // OPENSSL_NO_NTLS #endif }; @@ -211,8 +215,26 @@ class coro_http_client : public std::enable_shared_from_this { } try { - ssl_ctx_ = - std::make_unique(asio::ssl::context::sslv23); +#ifndef OPENSSL_NO_NTLS + if (use_ntls_) { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + + // Set NTLS cipher suites + if (!SSL_CTX_set_cipher_list( + ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + } + else { + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); + } +#endif // OPENSSL_NO_NTLS auto full_cert_file = std::filesystem::path(base_path).append(cert_file); if (std::filesystem::exists(full_cert_file)) { ssl_ctx_->load_verify_file(full_cert_file.string()); @@ -1464,6 +1486,93 @@ class coro_http_client : public std::enable_shared_from_this { #ifdef CINATRA_ENABLE_SSL void set_ssl_schema(bool r) { is_ssl_schema_ = r; } +#ifndef OPENSSL_NO_NTLS + void set_ntls_schema(bool r) { use_ntls_ = r; } + + /*! + * Initialize NTLS client with dual certificates + */ + bool init_ntls_client(const std::string &sign_cert_file, + const std::string &sign_key_file, + const std::string &enc_cert_file, + const std::string &enc_key_file, + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none) { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + + // Set NTLS cipher suites + if (!SSL_CTX_set_cipher_list(ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + + // Load client certificates if provided (for mutual authentication) + if (!sign_cert_file.empty() && !sign_key_file.empty()) { + if (!SSL_CTX_use_sign_certificate_file(ssl_ctx_->native_handle(), + sign_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client SM2 signing certificate"; + return false; + } + + if (!SSL_CTX_use_sign_PrivateKey_file(ssl_ctx_->native_handle(), + sign_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client SM2 signing private key"; + return false; + } + } + + if (!enc_cert_file.empty() && !enc_key_file.empty()) { + if (!SSL_CTX_use_enc_certificate_file(ssl_ctx_->native_handle(), + enc_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR + << "failed to load client SM2 encryption certificate"; + return false; + } + + if (!SSL_CTX_use_enc_PrivateKey_file(ssl_ctx_->native_handle(), + enc_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR + << "failed to load client SM2 encryption private key"; + return false; + } + } + + // Load CA certificate if provided + if (!ca_cert_file.empty()) { + if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), + ca_cert_file.c_str(), nullptr)) { + CINATRA_LOG_WARNING << "failed to load CA certificate"; + } + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + has_init_ssl_ = true; + use_ntls_ = true; + return true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init NTLS client failed: " << e.what(); + return false; + } + } +#endif // OPENSSL_NO_NTLS #endif std::string get_redirect_uri() { return redirect_uri_; } @@ -2466,6 +2575,9 @@ class coro_http_client : public std::enable_shared_from_this { std::unique_ptr ssl_ctx_ = nullptr; bool has_init_ssl_ = false; bool is_ssl_schema_ = false; +#ifndef OPENSSL_NO_NTLS + bool use_ntls_ = false; +#endif // OPENSSL_NO_NTLS bool need_set_sni_host_ = true; #endif std::string redirect_uri_; diff --git a/include/ylt/standalone/cinatra/coro_http_connection.hpp b/include/ylt/standalone/cinatra/coro_http_connection.hpp index ef42022b7..0d9b31d87 100644 --- a/include/ylt/standalone/cinatra/coro_http_connection.hpp +++ b/include/ylt/standalone/cinatra/coro_http_connection.hpp @@ -97,6 +97,172 @@ class coro_http_connection } return true; } + +#ifndef OPENSSL_NO_NTLS + /*! + * Initialize NTLS SSL context with dual certificates (Tongsuo native API) + */ + bool init_ntls(const auto &ntls_config) { + // Set context options + ssl_ctx_->set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_sslv2 | + asio::ssl::context::single_dh_use); + try { + // Create SSL context with TLCP server method + ssl_ctx_ = + std::make_unique(SSL_CTX_new(NTLS_method())); + + // Enable NTLS using Tongsuo native API + SSL_CTX* ctx = ssl_ctx_->native_handle(); + if (!ctx) { + CINATRA_LOG_ERROR << "SSL_CTX native_handle is null"; + return false; + } + if (SSL_CTX_enable_ntls(ctx) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "SSL_CTX_enable_ntls failed: " + << ::ERR_error_string(err, nullptr); + return false; + } + + // Set NTLS cipher suites + std::string cipher_suites = + ntls_config.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : ntls_config.cipher_suites; + if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_WARNING << "Failed to set NTLS cipher suites '" + << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + } + + + namespace fs = std::filesystem; + + // Build full paths + auto sign_cert_path = ntls_config.base_path.empty() + ? ntls_config.sign_cert_file + : fs::path(ntls_config.base_path) + .append(ntls_config.sign_cert_file); + auto sign_key_path = ntls_config.base_path.empty() + ? ntls_config.sign_key_file + : fs::path(ntls_config.base_path) + .append(ntls_config.sign_key_file); + auto enc_cert_path = ntls_config.base_path.empty() + ? ntls_config.enc_cert_file + : fs::path(ntls_config.base_path) + .append(ntls_config.enc_cert_file); + auto enc_key_path = ntls_config.base_path.empty() + ? ntls_config.enc_key_file + : fs::path(ntls_config.base_path) + .append(ntls_config.enc_key_file); + + std::error_code file_ec; + + // Load SM2 signing certificate and key + if (fs::exists(sign_cert_path, file_ec)) { + if (SSL_CTX_use_sign_certificate_file(ctx, sign_cert_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 signing certificate: " + << sign_cert_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + } else { + CINATRA_LOG_ERROR << "SM2 signing certificate file not found: " + << sign_cert_path.string(); + return false; + } + + if (fs::exists(sign_key_path, file_ec)) { + if (SSL_CTX_use_sign_PrivateKey_file(ctx, sign_key_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 signing private key: " + << sign_key_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + } else { + CINATRA_LOG_ERROR << "SM2 signing key file not found: " + << sign_key_path.string(); + return false; + } + + // Load SM2 encryption certificate and key + if (fs::exists(enc_cert_path, file_ec)) { + if (SSL_CTX_use_enc_certificate_file(ctx, enc_cert_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 encryption certificate: " + << enc_cert_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + } else { + CINATRA_LOG_ERROR << "SM2 encryption certificate file not found: " + << enc_cert_path.string(); + return false; + } + + if (fs::exists(enc_key_path, file_ec)) { + if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 encryption private key: " + << enc_key_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + } else { + CINATRA_LOG_ERROR << "SM2 encryption key file not found: " + << enc_key_path.string(); + return false; + } + // Load CA certificate if provided + if (!ntls_config.ca_cert_file.empty()) { + auto ca_cert_path = ntls_config.base_path.empty() + ? ntls_config.ca_cert_file + : fs::path(ntls_config.base_path) + .append(ntls_config.ca_cert_file); + if (fs::exists(ca_cert_path, file_ec)) { + asio::error_code ec; + ssl_ctx_->load_verify_file(ca_cert_path.string(), ec); + if (ec) { + CINATRA_LOG_WARNING << "failed to load CA certificate: " + << ca_cert_path.string() + << ", error: " << ec.message(); + } + } + } + + // Set verification mode + asio::error_code ec; + if (ntls_config.enable_client_verify) { + ssl_ctx_->set_verify_mode(asio::ssl::verify_peer, ec); + } + else { + ssl_ctx_->set_verify_mode(asio::ssl::verify_none, ec); + } + if (ec) { + CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); + } + + // Create SSL stream + socket_wrapper_.ssl_stream() = + std::make_unique>( + *socket_wrapper_.socket(), *ssl_ctx_); + + CINATRA_LOG_INFO << "NTLS server context initialized successfully"; + return true; + } catch (const std::exception &e) { + CINATRA_LOG_ERROR << "NTLS context init error: " << e.what(); + return false; + } + } +#endif // OPENSSL_NO_NTLS #endif void add_head(std::string_view msg) { diff --git a/include/ylt/standalone/cinatra/coro_http_server.hpp b/include/ylt/standalone/cinatra/coro_http_server.hpp index 2d7b9f8e0..b63b3b6b2 100644 --- a/include/ylt/standalone/cinatra/coro_http_server.hpp +++ b/include/ylt/standalone/cinatra/coro_http_server.hpp @@ -76,6 +76,61 @@ class coro_http_server { passwd_ = passwd; use_ssl_ = true; } + +#ifndef OPENSSL_NO_NTLS + /*! + * Initialize NTLS with dual certificates (signing and encryption) + * @param sign_cert_file SM2 signing certificate file path + * @param sign_key_file SM2 signing private key file path + * @param enc_cert_file SM2 encryption certificate file path + * @param enc_key_file SM2 encryption private key file path + * @param ca_cert_file CA certificate file path (optional) + * @param enable_client_verify enable client certificate verification + */ + void init_ntls(const std::string &sign_cert_file, + const std::string &sign_key_file, + const std::string &enc_cert_file, + const std::string &enc_key_file, + const std::string &ca_cert_file = "", + bool enable_client_verify = false) { + ntls_config_.sign_cert_file = sign_cert_file; + ntls_config_.sign_key_file = sign_key_file; + ntls_config_.enc_cert_file = enc_cert_file; + ntls_config_.enc_key_file = enc_key_file; + ntls_config_.ca_cert_file = ca_cert_file; + ntls_config_.enable_client_verify = enable_client_verify; + ntls_config_.enable_ntls = true; + use_ntls_ = true; + } + + /*! + * Initialize NTLS with base path and relative file paths + */ + void init_ntls(const std::string &base_path, + const std::string &sign_cert_file, + const std::string &sign_key_file, + const std::string &enc_cert_file, + const std::string &enc_key_file, + const std::string &ca_cert_file = "", + bool enable_client_verify = false) { + ntls_config_.base_path = base_path; + ntls_config_.sign_cert_file = sign_cert_file; + ntls_config_.sign_key_file = sign_key_file; + ntls_config_.enc_cert_file = enc_cert_file; + ntls_config_.enc_key_file = enc_key_file; + ntls_config_.ca_cert_file = ca_cert_file; + ntls_config_.enable_client_verify = enable_client_verify; + ntls_config_.enable_ntls = true; + use_ntls_ = true; + } + + /*! + * Set NTLS cipher suites + */ + void set_ntls_cipher_suites(const std::string &cipher_suites) { + ntls_config_.cipher_suites = cipher_suites; + } +#endif // OPENSSL_NO_NTLS #endif // only call once, not thread safe. @@ -1065,6 +1120,21 @@ class coro_http_server { std::string key_file_; std::string passwd_; bool use_ssl_ = false; +#ifndef OPENSSL_NO_NTLS + bool use_ntls_ = false; + // NTLS configuration + struct { + std::string base_path; + std::string sign_cert_file; + std::string sign_key_file; + std::string enc_cert_file; + std::string enc_key_file; + std::string ca_cert_file; + std::string cipher_suites = "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"; + bool enable_client_verify = false; + bool enable_ntls = false; + } ntls_config_; +#endif // OPENSSL_NO_NTLS #endif coro_http_router router_; bool need_shrink_every_time_ = false; From 5e7c776db37ecbd03d4f7b85d961afbaaf454055 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 17 Sep 2025 19:43:54 +0800 Subject: [PATCH 03/38] feat: add ntls example --- src/coro_http/examples/CMakeLists.txt | 12 +- src/coro_http/examples/ntls_http_client.cpp | 126 ++++++++++++++ src/coro_http/examples/ntls_http_server.cpp | 126 ++++++++++++++ .../examples/base_examples/CMakeLists.txt | 12 +- .../examples/base_examples/ntls_client.cpp | 154 ++++++++++++++++++ .../examples/base_examples/ntls_server.cpp | 133 +++++++++++++++ 6 files changed, 561 insertions(+), 2 deletions(-) create mode 100644 src/coro_http/examples/ntls_http_client.cpp create mode 100644 src/coro_http/examples/ntls_http_server.cpp create mode 100644 src/coro_rpc/examples/base_examples/ntls_client.cpp create mode 100644 src/coro_rpc/examples/base_examples/ntls_server.cpp diff --git a/src/coro_http/examples/CMakeLists.txt b/src/coro_http/examples/CMakeLists.txt index 8d0db5435..2fa71124d 100644 --- a/src/coro_http/examples/CMakeLists.txt +++ b/src/coro_http/examples/CMakeLists.txt @@ -30,11 +30,21 @@ endif() add_executable(coro_http_example example.cpp) add_executable(coro_http_load_balancer load_balancer.cpp) add_executable(coro_chat_room chat_room.cpp) - +if(YLT_ENABLE_NTLS) + message(STATUS "Building NTLS examples with Tongsuo support") + add_executable(ntls_http_server ntls_http_server.cpp) + add_executable(ntls_http_client ntls_http_client.cpp) + target_compile_definitions(ntls_http_server PRIVATE YLT_ENABLE_NTLS) + target_compile_definitions(ntls_http_client PRIVATE YLT_ENABLE_NTLS) +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 target_link_libraries(coro_http_example wsock32 ws2_32) target_link_libraries(coro_http_load_balancer wsock32 ws2_32) target_link_libraries(coro_chat_room wsock32 ws2_32) + if(YLT_ENABLE_NTLS) + target_link_libraries(ntls_http_server wsock32 ws2_32) + target_link_libraries(ntls_http_client wsock32 ws2_32) + endif() endif() option(ENABLE_pybind11 "Enable pybind11 " OFF) diff --git a/src/coro_http/examples/ntls_http_client.cpp b/src/coro_http/examples/ntls_http_client.cpp new file mode 100644 index 000000000..90179fcc4 --- /dev/null +++ b/src/coro_http/examples/ntls_http_client.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace cinatra; + +// Certificate paths - adjust to your environment +const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate +const std::string CLIENT_SIGN_KEY = CERT_PATH + "client_sign.key"; // SM2 client signing private key +const std::string CLIENT_ENC_CERT = CERT_PATH + "client_enc.crt"; // SM2 client encryption certificate +const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; // SM2 client encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate + +// Test one-way authentication NTLS client (server certificate only) +async_simple::coro::Lazy test_ntls_http_client_one_way() { + std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" << std::endl; + + coro_http_client client{}; + + // Enable NTLS protocol (simple mode, server authentication only) + client.set_ntls_schema(true); + + std::cout << "NTLS HTTP Client initialized (one-way auth mode)" << std::endl; + + // Send GET request to root endpoint + auto response = co_await client.async_get("https://localhost:8801/"); + if (response.net_err) { + std::cout << "GET / request failed: " << response.net_err.message() << std::endl; + } else { + std::cout << "GET / Response status: " << response.status << std::endl; + std::cout << "GET / Response body: " << response.resp_body << std::endl; + } + + // Send POST request to echo endpoint + auto post_response = co_await client.async_post("https://localhost:8801/echo", + "Test message", + req_content_type::text); + if (post_response.net_err) { + std::cout << "POST /echo request failed: " << post_response.net_err.message() << std::endl; + } else { + std::cout << "POST /echo Response status: " << post_response.status << std::endl; + std::cout << "POST /echo Response body: " << post_response.resp_body << std::endl; + } +} + +// Test mutual authentication NTLS client (client certificate required) +async_simple::coro::Lazy test_ntls_http_client_mutual_auth() { + std::cout << "\n=== Mutual Authentication NTLS Client (8802) ===" << std::endl; + + coro_http_client client{}; + + // Initialize NTLS client with mutual authentication + bool init_ok = client.init_ntls_client( + CLIENT_SIGN_CERT, // Client SM2 signing certificate + CLIENT_SIGN_KEY, // Client SM2 signing private key + CLIENT_ENC_CERT, // Client SM2 encryption certificate + CLIENT_ENC_KEY, // Client SM2 encryption private key + CA_CERT, // CA certificate + asio::ssl::verify_peer // Verify server certificate + ); + + if (!init_ok) { + std::cout << "Failed to initialize NTLS client with mutual authentication" << std::endl; + co_return; + } + + std::cout << "NTLS HTTP Client initialized (mutual authentication mode)" << std::endl; + + // Send GET request to root endpoint (requires client certificate) + auto response = co_await client.async_get("https://localhost:8802/"); + if (response.net_err) { + std::cout << "GET / request failed: " << response.net_err.message() << std::endl; + } else { + std::cout << "GET / Response status: " << response.status << std::endl; + std::cout << "GET / Response body: " << response.resp_body << std::endl; + } + + // Send POST request to echo endpoint (requires client certificate) + auto post_response = co_await client.async_post("https://localhost:8802/echo", + "Test message with client certificate", + req_content_type::text); + if (post_response.net_err) { + std::cout << "POST /echo request failed: " << post_response.net_err.message() << std::endl; + } else { + std::cout << "POST /echo Response status: " << post_response.status << std::endl; + std::cout << "POST /echo Response body: " << post_response.resp_body << std::endl; + } +} + +int main() { + std::cout << "Starting NTLS HTTP Client Examples..." << std::endl; + std::cout << "Testing NTLS/TLCP protocol with SM2/SM3/SM4 algorithms" << std::endl; + + try { + // Test one-way authentication NTLS client (port 8801) + //async_simple::coro::syncAwait(test_ntls_http_client_one_way()); + + // Test mutual authentication NTLS client (port 8802) + async_simple::coro::syncAwait(test_ntls_http_client_mutual_auth()); + + } catch (const std::exception& e) { + std::cout << "Exception: " << e.what() << std::endl; + return 1; + } + + std::cout << "\nNTLS HTTP Client Examples completed." << std::endl; + return 0; +} \ No newline at end of file diff --git a/src/coro_http/examples/ntls_http_server.cpp b/src/coro_http/examples/ntls_http_server.cpp new file mode 100644 index 000000000..f1a11c85a --- /dev/null +++ b/src/coro_http/examples/ntls_http_server.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace cinatra; + +// Certificate paths - adjust to your environment +const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 signing certificate +const std::string SERVER_SIGN_KEY = CERT_PATH + "server_sign.key"; // SM2 signing private key +const std::string SERVER_ENC_CERT = CERT_PATH + "server_enc.crt"; // SM2 encryption certificate +const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; // SM2 encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate + +// Start one-way authentication NTLS server (server certificate only) +void start_one_way_server() { + std::cout << "Starting NTLS HTTP Server (one-way auth) on port 8801..." << std::endl; + + coro_http_server server(1, 8801); + + // Initialize NTLS server with dual certificates (SM2 sign/encrypt) + // One-way authentication mode (no client certificate verification) + server.init_ntls(SERVER_SIGN_CERT, + SERVER_SIGN_KEY, + SERVER_ENC_CERT, + SERVER_ENC_KEY, + CA_CERT, + false); // false = don't verify client certificate + + // Set NTLS cipher suites + server.set_ntls_cipher_suites("ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"); + + // Root endpoint + server.set_http_handler("/", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + + // Echo endpoint + server.set_http_handler("/echo", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + + std::cout << "One-way NTLS server ready. Test endpoints:" << std::endl; + std::cout << " GET https://localhost:8801/" << std::endl; + std::cout << " POST https://localhost:8801/echo" << std::endl; + + auto result = server.sync_start(); + if (result) { + std::cerr << "Failed to start one-way server: " << result.message() << std::endl; + } +} + +// Start mutual authentication NTLS server (requires client certificate) +void start_mutual_auth_server() { + std::cout << "Starting NTLS HTTP Server (mutual auth) on port 8802..." << std::endl; + + coro_http_server server(1, 8802); + + // Initialize NTLS server with dual certificates (SM2 sign/encrypt) + // Mutual authentication mode (client certificate verification enabled) + server.init_ntls(SERVER_SIGN_CERT, + SERVER_SIGN_KEY, + SERVER_ENC_CERT, + SERVER_ENC_KEY, + CA_CERT, + true); // true = verify client certificate + + // Set NTLS cipher suites + server.set_ntls_cipher_suites("ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"); + + // Root endpoint + server.set_http_handler("/", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + + // Echo endpoint + server.set_http_handler("/echo", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + + std::cout << "Mutual auth NTLS server ready. Test endpoints:" << std::endl; + std::cout << " GET https://localhost:8802/ (requires client certificate)" << std::endl; + std::cout << " POST https://localhost:8802/echo (requires client certificate)" << std::endl; + + auto result = server.sync_start(); + if (result) { + std::cerr << "Failed to start mutual auth server: " << result.message() << std::endl; + } +} + +int main() { + std::cout << "NTLS HTTP Server Example - Starting two servers:" << std::endl; + std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; + std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + + // Start both servers in separate threads + std::thread one_way_thread(start_one_way_server); + std::thread mutual_auth_thread(start_mutual_auth_server); + + // Wait for both servers + one_way_thread.join(); + mutual_auth_thread.join(); + + return 0; +} \ No newline at end of file diff --git a/src/coro_rpc/examples/base_examples/CMakeLists.txt b/src/coro_rpc/examples/base_examples/CMakeLists.txt index fe09885c2..ddf859ffe 100644 --- a/src/coro_rpc/examples/base_examples/CMakeLists.txt +++ b/src/coro_rpc/examples/base_examples/CMakeLists.txt @@ -70,7 +70,13 @@ add_executable(coro_rpc_example_client_pools client_pools.cpp) add_executable(coro_rpc_example_client client.cpp) add_executable(coro_rpc_example_concurrent_clients concurrent_clients.cpp) add_executable(coro_rpc_example_server server.cpp rpc_service.cpp) - +if(YLT_ENABLE_NTLS) + message(STATUS "Building NTLS examples with Tongsuo support") + add_executable(ntls_server ntls_server.cpp) + add_executable(ntls_client ntls_client.cpp) + target_compile_definitions(ntls_server PRIVATE YLT_ENABLE_NTLS) + target_compile_definitions(ntls_client PRIVATE YLT_ENABLE_NTLS) +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 target_link_libraries(coro_rpc_example_load_balancer wsock32 ws2_32) target_link_libraries(coro_rpc_example_client_pool wsock32 ws2_32) @@ -78,5 +84,9 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows" target_link_libraries(coro_rpc_example_client wsock32 ws2_32) target_link_libraries(coro_rpc_example_concurrent_clients wsock32 ws2_32) target_link_libraries(coro_rpc_example_server wsock32 ws2_32) + if(YLT_ENABLE_NTLS) + target_link_libraries(ntls_server wsock32 ws2_32) + target_link_libraries(ntls_client wsock32 ws2_32) + endif() endif() diff --git a/src/coro_rpc/examples/base_examples/ntls_client.cpp b/src/coro_rpc/examples/base_examples/ntls_client.cpp new file mode 100644 index 000000000..3e47ff246 --- /dev/null +++ b/src/coro_rpc/examples/base_examples/ntls_client.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "async_simple/coro/Lazy.h" +#include "async_simple/coro/SyncAwait.h" + +using namespace coro_rpc; +using namespace async_simple::coro; +using namespace std::string_literals; + +// Certificate paths - adjust to your environment +const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate +const std::string CLIENT_SIGN_KEY = CERT_PATH + "client_sign.key"; // SM2 client signing private key +const std::string CLIENT_ENC_CERT = CERT_PATH + "client_enc.crt"; // SM2 client encryption certificate +const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; // SM2 client encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate + +// RPC function declarations (should match server) +std::string echo(std::string_view data); + +// Test one-way authentication NTLS client (server certificate only) +Lazy test_one_way_auth() { + std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" << std::endl; + + coro_rpc_client client; + + // Initialize NTLS client for one-way authentication + // In one-way mode, we only verify server's certificate + bool ok = client.init_ntls(CERT_PATH, // Certificate base path + "", // No client signing certificate needed + "", // No client signing key needed + "", // No client encryption certificate needed + "", // No client encryption key needed + CA_CERT, // CA certificate + "localhost", // Server domain + false); // Don't verify client certificate + + if (!ok) { + std::cout << "Failed to initialize one-way NTLS client" << std::endl; + co_return; + } + + std::cout << "One-way NTLS client initialized successfully" << std::endl; + + // Connect to one-way NTLS server + auto ec = co_await client.connect("127.0.0.1", "8801"); + if (ec) { + std::cout << "Failed to connect to one-way server: " << ec.message() << std::endl; + co_return; + } + + std::cout << "Connected to one-way NTLS server successfully!" << std::endl; + + try { + // Test echo function + auto result = co_await client.call("Test message to one-way server"); + if (result) { + std::cout << "Echo result from one-way server: " << result.value() << std::endl; + } else { + std::cout << "Echo failed: " << result.error().msg << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "RPC call error (one-way): " << e.what() << std::endl; + } +} + +// Test mutual authentication NTLS client (client certificate required) +Lazy test_mutual_auth() { + std::cout << "\n=== Mutual Authentication NTLS Client (8802) ===" << std::endl; + + coro_rpc_client client; + + // Initialize NTLS client for mutual authentication + // In mutual mode, both client and server certificates are verified + + bool ok = client.init_ntls( + CERT_PATH, // Certificate base path + CLIENT_SIGN_CERT, // client signing certificate needed + CLIENT_SIGN_KEY, // client signing key needed + CLIENT_ENC_CERT, // client encryption certificate needed + CLIENT_ENC_KEY, // client encryption key needed + CA_CERT, // CA certificate + "localhost", // Server domain + true); // Don't verify client certificate + + if (!ok) { + std::cout << "Failed to initialize mutual auth NTLS client" << std::endl; + co_return; + } + + std::cout << "Mutual auth NTLS client initialized successfully" << std::endl; + + // Connect to mutual auth NTLS server + auto ec = co_await client.connect("127.0.0.1", "8802"); + if (ec) { + std::cout << "Failed to connect to mutual auth server: " << ec.message() << std::endl; + co_return; + } + + std::cout << "Connected to mutual auth NTLS server successfully!" << std::endl; + + try { + // Test echo function + auto result = co_await client.call("Test message to mutual auth server"); + if (result) { + std::cout << "Echo result from mutual auth server: " << result.value() << std::endl; + } else { + std::cout << "Echo failed: " << result.error().msg << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "RPC call error (mutual auth): " << e.what() << std::endl; + } +} + +int main() { + try { + std::cout << "NTLS RPC Client Example - Testing two servers:" << std::endl; + std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; + std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + + // Test one-way authentication client + syncAwait(test_one_way_auth()); + + // Test mutual authentication client + syncAwait(test_mutual_auth()); + + std::cout << "\nNTLS RPC Client tests completed." << std::endl; + + } catch (const std::exception& e) { + std::cout << "Client error: " << e.what() << std::endl; + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/src/coro_rpc/examples/base_examples/ntls_server.cpp b/src/coro_rpc/examples/base_examples/ntls_server.cpp new file mode 100644 index 000000000..44cdfd5d8 --- /dev/null +++ b/src/coro_rpc/examples/base_examples/ntls_server.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace coro_rpc; +using namespace std::string_literals; + +// Certificate paths - adjust to your environment +const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 server signing certificate +const std::string SERVER_SIGN_KEY = CERT_PATH + "server_sign.key"; // SM2 server signing private key +const std::string SERVER_ENC_CERT = CERT_PATH + "server_enc.crt"; // SM2 server encryption certificate +const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; // SM2 server encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate + +// Simple RPC service function +std::string echo(std::string_view data) { + std::cout << "Server received: " << data << std::endl; + return "Hello World"; +} + +// Start one-way authentication server (server certificate only) +void start_one_way_server() { + try { + // Create RPC server for one-way authentication + coro_rpc_server server(std::thread::hardware_concurrency(), 8801); + + // Configure NTLS with Tongsuo (one-way authentication) + ssl_ntls_configure ntls_conf; + ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2 signing certificate + ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2 signing private key + ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2 encryption certificate + ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2 encryption private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = false; // Disable client certificate verification + ntls_conf.enable_ntls = true; // Enable NTLS mode + + // Initialize NTLS + server.init_ntls(ntls_conf); + + // Register RPC service function + server.register_handler(); + + std::cout << "NTLS RPC Server (one-way auth) starting on port 8801..." << std::endl; + std::cout << "Using Tongsuo for NTLS support" << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + + // Start server (blocking) + auto result = server.start(); + if (result) { + std::cout << "One-way auth server started successfully!" << std::endl; + } else { + std::cout << "Failed to start one-way auth server: " << result.message() << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "One-way auth server error: " << e.what() << std::endl; + } +} + +// Start mutual authentication server (client certificate required) +void start_mutual_auth_server() { + try { + // Create RPC server for mutual authentication + coro_rpc_server server(std::thread::hardware_concurrency(), 8802); + + // Configure NTLS with Tongsuo (mutual authentication) + ssl_ntls_configure ntls_conf; + ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2 signing certificate + ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2 signing private key + ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2 encryption certificate + ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2 encryption private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = true; // Enable client certificate verification + ntls_conf.enable_ntls = true; // Enable NTLS mode + + // Initialize NTLS + server.init_ntls(ntls_conf); + + // Register RPC service function + server.register_handler(); + + std::cout << "NTLS RPC Server (mutual auth) starting on port 8802..." << std::endl; + std::cout << "Using Tongsuo for NTLS support" << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + + // Start server (blocking) + auto result = server.start(); + if (result) { + std::cout << "Mutual auth server started successfully!" << std::endl; + } else { + std::cout << "Failed to start mutual auth server: " << result.message() << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "Mutual auth server error: " << e.what() << std::endl; + } +} + +int main() { + std::cout << "NTLS RPC Server Example - Starting two servers:" << std::endl; + std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; + std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + + // Start both servers in separate threads + std::thread one_way_thread(start_one_way_server); + std::thread mutual_auth_thread(start_mutual_auth_server); + + // Wait for both servers + one_way_thread.join(); + mutual_auth_thread.join(); + + return 0; +} \ No newline at end of file From e285956a93562ef52cf5b996793d7aaad70cd98e Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 17 Sep 2025 19:53:20 +0800 Subject: [PATCH 04/38] delete --- include/ylt/coro_rpc/impl/common_service.hpp | 80 +------------------- 1 file changed, 1 insertion(+), 79 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index 4926744d4..69648bca8 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -49,7 +49,7 @@ struct ssl_configure { // relative path of SM2 encryption private key file std::string ca_cert_file; // //!< relative path of CA certificate file (optional) bool // enable_client_verify = false; //!< enable client certificate verification -// bool enable_ntls = true; //!< enable NTLS mode (国密SSL) +// bool enable_ntls = true; //!< enable NTLS mode //}; /*! @@ -300,84 +300,6 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, return false; } } - -///*! -// * Initialize SSL Context with extended SSL/NTLS config -// * -// * @param context instance of asio::ssl::context -// * @param conf object of ssl_ntls_configure -// * @return true if init success, otherwise false -// */ -// inline bool init_ssl_ntls_context_helper(asio::ssl::context &context, -// const ssl_ntls_configure &conf) { -// if (conf.enable_ntls) { -// // Convert to ntls_configure and use NTLS initialization -// ntls_configure ntls_conf; -// ntls_conf.base_path = conf.base_path; -// ntls_conf.sign_cert_file = conf.sign_cert_file; -// ntls_conf.sign_key_file = conf.sign_key_file; -// ntls_conf.enc_cert_file = conf.enc_cert_file; -// ntls_conf.enc_key_file = conf.enc_key_file; -// ntls_conf.ca_cert_file = conf.ca_cert_file; -// ntls_conf.enable_client_verify = conf.enable_client_verify; -// ntls_conf.enable_ntls = conf.enable_ntls; -// -// return init_ntls_context_helper(context, ntls_conf); -// } else { -// // Use traditional SSL initialization -// ssl_configure ssl_conf; -// ssl_conf.base_path = conf.base_path; -// ssl_conf.cert_file = conf.cert_file; -// ssl_conf.key_file = conf.key_file; -// ssl_conf.dh_file = conf.dh_file; -// -// return init_ssl_context_helper(context, ssl_conf); -// } -//} -// -///*! -// * Initialize SSL Context with TLCP method for NTLS -// * -// * @param conf object of ntls_configure -// * @return initialized asio::ssl::context for NTLS -// */ -// inline std::unique_ptr create_ntls_context(const -// ntls_configure &conf) { -// try { -// // Create SSL context with TLCP server method -// auto context = -// std::make_unique(asio::ssl::context::tlcp_server); -// -// if (init_ntls_context_helper(*context, conf)) { -// return context; -// } -// } catch (const std::exception &e) { -// ELOG_ERROR << "Failed to create NTLS context: " << e.what(); -// } -// return nullptr; -//} -// -///*! -// * Initialize SSL Context with TLCP client method for NTLS -// * -// * @param conf object of ntls_configure -// * @return initialized asio::ssl::context for NTLS client -// */ -// inline std::unique_ptr create_ntls_client_context(const -// ntls_configure &conf) { -// try { -// // Create SSL context with TLCP client method -// auto context = -// std::make_unique(asio::ssl::context::tlcp_client); -// -// if (init_ntls_context_helper(*context, conf)) { -// return context; -// } -// } catch (const std::exception &e) { -// ELOG_ERROR << "Failed to create NTLS client context: " << e.what(); -// } -// return nullptr; -//} #endif // OPENSSL_NO_NTLS #endif } // namespace coro_rpc \ No newline at end of file From 920640a1ee6f80711c4b591cef4fe7853e73cddc Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 17 Sep 2025 20:02:56 +0800 Subject: [PATCH 05/38] style: Clang Format --- include/ylt/coro_rpc/impl/common_service.hpp | 42 +++--- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 13 +- .../standalone/cinatra/coro_http_client.hpp | 6 +- .../cinatra/coro_http_connection.hpp | 39 +++--- src/coro_http/examples/ntls_http_client.cpp | 120 +++++++++------- src/coro_http/examples/ntls_http_server.cpp | 131 ++++++++++-------- .../examples/base_examples/ntls_client.cpp | 89 +++++++----- .../examples/base_examples/ntls_server.cpp | 78 ++++++----- 8 files changed, 298 insertions(+), 220 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index 69648bca8..ae40e4fe7 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -178,9 +178,9 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, context.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::single_dh_use); - + // Configure NTLS via Tongsuo native APIs - SSL_CTX* ctx = context.native_handle(); + SSL_CTX *ctx = context.native_handle(); if (!ctx) { ELOG_ERROR << "SSL_CTX native_handle is null"; return false; @@ -197,7 +197,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, unsigned long err = ::ERR_get_error(); ELOG_WARN << "Failed to set NTLS cipher suites '" << cipher_suites << "': " << ::ERR_error_string(err, nullptr); - } else { + } + else { ELOG_INFO << "NTLS cipher suites set to: " << cipher_suites; } context.set_password_callback( @@ -206,22 +207,23 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, return "test"; }); auto sign_cert_file = fs::path(conf.base_path).append(conf.sign_cert_file); - auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); - auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); - auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); + auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); + auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); + auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); ELOG_INFO << "current path " << fs::current_path().string(); // Load SM2 signing certificate and key if (file_exists(sign_cert_file)) { - if (SSL_CTX_use_sign_certificate_file(ctx, sign_cert_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_sign_certificate_file( + ctx, sign_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); ELOG_ERROR << "failed to load SM2 signing certificate: " << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { ELOG_ERROR << "no SM2 signing certificate file " << sign_cert_file.string(); return false; @@ -235,9 +237,9 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, << ::ERR_error_string(err, nullptr); return false; } - } else { - ELOG_ERROR << "no SM2 signing key file " - << sign_key_file.string(); + } + else { + ELOG_ERROR << "no SM2 signing key file " << sign_key_file.string(); return false; } @@ -250,7 +252,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { ELOG_ERROR << "no SM2 encryption certificate file " << enc_cert_file.string(); return false; @@ -264,9 +267,9 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, << ::ERR_error_string(err, nullptr); return false; } - } else { - ELOG_ERROR << "no SM2 encryption key file " - << enc_key_file.string(); + } + else { + ELOG_ERROR << "no SM2 encryption key file " << enc_key_file.string(); return false; } @@ -277,8 +280,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, if (file_exists(ca_cert_file)) { context.load_verify_file(ca_cert_file.string(), ec); if (ec) { - ELOG_WARN << "failed to load CA certificate: " - << ec.message() << ", continuing without it"; + ELOG_WARN << "failed to load CA certificate: " << ec.message() + << ", continuing without it"; } } } @@ -286,7 +289,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, // Set verification mode if (conf.enable_client_verify) { context.set_verify_mode(asio::ssl::verify_peer, ec); - } else { + } + else { context.set_verify_mode(asio::ssl::verify_none, ec); } if (ec) { diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index deb666405..370ad46c7 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -383,7 +383,8 @@ class coro_rpc_client { if (!config.ca_cert_path.empty() && file_exists(config.ca_cert_path)) { ELOG_INFO << "load CA cert " << config.ca_cert_path.string(); if (!SSL_CTX_load_verify_locations(ssl_ctx_.native_handle(), - config.ca_cert_path.string().c_str(), nullptr)) { + config.ca_cert_path.string().c_str(), + nullptr)) { ELOG_WARN << "failed to load CA certificate"; } } @@ -391,9 +392,9 @@ class coro_rpc_client { // Set verification mode - use same approach as HTTP client if (config.enable_client_verify) { ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); - // Note: Skip host_name_verification for NTLS as it may not be compatible - // The server certificate will still be verified against CA - //ssl_ctx_.set_verify_callback( + // Note: Skip host_name_verification for NTLS as it may not be + // compatible The server certificate will still be verified against CA + // ssl_ctx_.set_verify_callback( // asio::ssl::host_name_verification(config.ssl_domain)); } else { @@ -533,7 +534,7 @@ class coro_rpc_client { //[[nodiscard]] bool init_ntls(const ssl_ntls_configure &conf) { // return init_ntls(conf.base_path, conf.sign_cert_file, conf.sign_key_file, // conf.enc_cert_file, conf.enc_key_file, conf.ca_cert_file, - // conf..empty() ? "localhost" : conf.server_name, + // conf..empty() ? "localhost" : conf.server_name, // conf.enable_client_verify); //} @@ -555,7 +556,7 @@ class coro_rpc_client { std::string enc_key_path = std::filesystem::path(base_file).append(enc_key_file).string(); std::string ca_cert_path; - + if (!ca_cert_file.empty()) { ca_cert_path = std::filesystem::path(base_file).append(ca_cert_file).string(); diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index 3e4c817d6..ff7d25a28 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -140,7 +140,7 @@ class coro_http_client : public std::enable_shared_from_this { #ifndef OPENSSL_NO_NTLS bool use_ntls = false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. -#endif // OPENSSL_NO_NTLS +#endif // OPENSSL_NO_NTLS #endif }; @@ -1519,7 +1519,7 @@ class coro_http_client : public std::enable_shared_from_this { CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; } - if (!passwd.empty()) { + if (!passwd.empty()) { ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { return pwd; }); @@ -1559,7 +1559,7 @@ class coro_http_client : public std::enable_shared_from_this { return false; } } - + // Load CA certificate if provided if (!ca_cert_file.empty()) { if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), diff --git a/include/ylt/standalone/cinatra/coro_http_connection.hpp b/include/ylt/standalone/cinatra/coro_http_connection.hpp index c0893d42a..92247e1d3 100644 --- a/include/ylt/standalone/cinatra/coro_http_connection.hpp +++ b/include/ylt/standalone/cinatra/coro_http_connection.hpp @@ -102,7 +102,7 @@ class coro_http_connection /*! * Initialize NTLS SSL context with dual certificates (Tongsuo native API) */ - //bool init_ntls(const std::string &sign_cert_file, + // bool init_ntls(const std::string &sign_cert_file, // const std::string &sign_key_file, // const std::string &enc_cert_file, // const std::string &enc_key_file, @@ -114,14 +114,13 @@ class coro_http_connection // //} bool init_ntls(const auto &ntls_config, std::string passwd) { - try { // Create SSL context with TLCP server method ssl_ctx_ = std::make_unique(SSL_CTX_new(NTLS_method())); // Enable NTLS using Tongsuo native API - SSL_CTX* ctx = ssl_ctx_->native_handle(); + SSL_CTX *ctx = ssl_ctx_->native_handle(); if (!ctx) { CINATRA_LOG_ERROR << "SSL_CTX native_handle is null"; return false; @@ -143,7 +142,7 @@ class coro_http_connection << "': " << ::ERR_error_string(err, nullptr); } - // Set context options + // Set context options ssl_ctx_->set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::single_dh_use); @@ -176,30 +175,32 @@ class coro_http_connection // Load SM2 signing certificate and key if (fs::exists(sign_cert_path, file_ec)) { - if (SSL_CTX_use_sign_certificate_file(ctx, sign_cert_path.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_sign_certificate_file( + ctx, sign_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); CINATRA_LOG_ERROR << "failed to load SM2 signing certificate: " << sign_cert_path.string() << ", err: " << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { CINATRA_LOG_ERROR << "SM2 signing certificate file not found: " << sign_cert_path.string(); return false; } if (fs::exists(sign_key_path, file_ec)) { - if (SSL_CTX_use_sign_PrivateKey_file(ctx, sign_key_path.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_sign_PrivateKey_file( + ctx, sign_key_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); CINATRA_LOG_ERROR << "failed to load SM2 signing private key: " << sign_key_path.string() << ", err: " << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { CINATRA_LOG_ERROR << "SM2 signing key file not found: " << sign_key_path.string(); return false; @@ -207,15 +208,16 @@ class coro_http_connection // Load SM2 encryption certificate and key if (fs::exists(enc_cert_path, file_ec)) { - if (SSL_CTX_use_enc_certificate_file(ctx, enc_cert_path.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_enc_certificate_file( + ctx, enc_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); CINATRA_LOG_ERROR << "failed to load SM2 encryption certificate: " << enc_cert_path.string() << ", err: " << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { CINATRA_LOG_ERROR << "SM2 encryption certificate file not found: " << enc_cert_path.string(); return false; @@ -230,7 +232,8 @@ class coro_http_connection << ", err: " << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { CINATRA_LOG_ERROR << "SM2 encryption key file not found: " << enc_key_path.string(); return false; @@ -245,9 +248,9 @@ class coro_http_connection asio::error_code ec; ssl_ctx_->load_verify_file(ca_cert_path.string(), ec); if (ec) { - CINATRA_LOG_WARNING << "failed to load CA certificate: " - << ca_cert_path.string() - << ", error: " << ec.message(); + CINATRA_LOG_WARNING + << "failed to load CA certificate: " << ca_cert_path.string() + << ", error: " << ec.message(); } } } @@ -263,7 +266,7 @@ class coro_http_connection if (ec) { CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); } - + // Create SSL stream socket_wrapper_.ssl_stream() = std::make_unique>( diff --git a/src/coro_http/examples/ntls_http_client.cpp b/src/coro_http/examples/ntls_http_client.cpp index 90179fcc4..54e859e3c 100644 --- a/src/coro_http/examples/ntls_http_client.cpp +++ b/src/coro_http/examples/ntls_http_client.cpp @@ -23,104 +23,124 @@ using namespace cinatra; // Certificate paths - adjust to your environment const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; -const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate -const std::string CLIENT_SIGN_KEY = CERT_PATH + "client_sign.key"; // SM2 client signing private key -const std::string CLIENT_ENC_CERT = CERT_PATH + "client_enc.crt"; // SM2 client encryption certificate -const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; // SM2 client encryption private key -const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +const std::string CLIENT_SIGN_CERT = + CERT_PATH + "client_sign.crt"; // SM2 client signing certificate +const std::string CLIENT_SIGN_KEY = + CERT_PATH + "client_sign.key"; // SM2 client signing private key +const std::string CLIENT_ENC_CERT = + CERT_PATH + "client_enc.crt"; // SM2 client encryption certificate +const std::string CLIENT_ENC_KEY = + CERT_PATH + "client_enc.key"; // SM2 client encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Test one-way authentication NTLS client (server certificate only) async_simple::coro::Lazy test_ntls_http_client_one_way() { - std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" << std::endl; - + std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" + << std::endl; + coro_http_client client{}; - + // Enable NTLS protocol (simple mode, server authentication only) client.set_ntls_schema(true); - + std::cout << "NTLS HTTP Client initialized (one-way auth mode)" << std::endl; - + // Send GET request to root endpoint auto response = co_await client.async_get("https://localhost:8801/"); if (response.net_err) { - std::cout << "GET / request failed: " << response.net_err.message() << std::endl; - } else { + std::cout << "GET / request failed: " << response.net_err.message() + << std::endl; + } + else { std::cout << "GET / Response status: " << response.status << std::endl; std::cout << "GET / Response body: " << response.resp_body << std::endl; } - + // Send POST request to echo endpoint - auto post_response = co_await client.async_post("https://localhost:8801/echo", - "Test message", - req_content_type::text); + auto post_response = co_await client.async_post( + "https://localhost:8801/echo", "Test message", req_content_type::text); if (post_response.net_err) { - std::cout << "POST /echo request failed: " << post_response.net_err.message() << std::endl; - } else { - std::cout << "POST /echo Response status: " << post_response.status << std::endl; - std::cout << "POST /echo Response body: " << post_response.resp_body << std::endl; + std::cout << "POST /echo request failed: " + << post_response.net_err.message() << std::endl; + } + else { + std::cout << "POST /echo Response status: " << post_response.status + << std::endl; + std::cout << "POST /echo Response body: " << post_response.resp_body + << std::endl; } } // Test mutual authentication NTLS client (client certificate required) async_simple::coro::Lazy test_ntls_http_client_mutual_auth() { - std::cout << "\n=== Mutual Authentication NTLS Client (8802) ===" << std::endl; - + std::cout << "\n=== Mutual Authentication NTLS Client (8802) ===" + << std::endl; + coro_http_client client{}; - + // Initialize NTLS client with mutual authentication bool init_ok = client.init_ntls_client( - CLIENT_SIGN_CERT, // Client SM2 signing certificate - CLIENT_SIGN_KEY, // Client SM2 signing private key - CLIENT_ENC_CERT, // Client SM2 encryption certificate - CLIENT_ENC_KEY, // Client SM2 encryption private key - CA_CERT, // CA certificate - asio::ssl::verify_peer // Verify server certificate + CLIENT_SIGN_CERT, // Client SM2 signing certificate + CLIENT_SIGN_KEY, // Client SM2 signing private key + CLIENT_ENC_CERT, // Client SM2 encryption certificate + CLIENT_ENC_KEY, // Client SM2 encryption private key + CA_CERT, // CA certificate + asio::ssl::verify_peer // Verify server certificate ); - + if (!init_ok) { - std::cout << "Failed to initialize NTLS client with mutual authentication" << std::endl; + std::cout << "Failed to initialize NTLS client with mutual authentication" + << std::endl; co_return; } - - std::cout << "NTLS HTTP Client initialized (mutual authentication mode)" << std::endl; - + + std::cout << "NTLS HTTP Client initialized (mutual authentication mode)" + << std::endl; + // Send GET request to root endpoint (requires client certificate) auto response = co_await client.async_get("https://localhost:8802/"); if (response.net_err) { - std::cout << "GET / request failed: " << response.net_err.message() << std::endl; - } else { + std::cout << "GET / request failed: " << response.net_err.message() + << std::endl; + } + else { std::cout << "GET / Response status: " << response.status << std::endl; std::cout << "GET / Response body: " << response.resp_body << std::endl; } - + // Send POST request to echo endpoint (requires client certificate) - auto post_response = co_await client.async_post("https://localhost:8802/echo", - "Test message with client certificate", - req_content_type::text); + auto post_response = co_await client.async_post( + "https://localhost:8802/echo", "Test message with client certificate", + req_content_type::text); if (post_response.net_err) { - std::cout << "POST /echo request failed: " << post_response.net_err.message() << std::endl; - } else { - std::cout << "POST /echo Response status: " << post_response.status << std::endl; - std::cout << "POST /echo Response body: " << post_response.resp_body << std::endl; + std::cout << "POST /echo request failed: " + << post_response.net_err.message() << std::endl; + } + else { + std::cout << "POST /echo Response status: " << post_response.status + << std::endl; + std::cout << "POST /echo Response body: " << post_response.resp_body + << std::endl; } } int main() { std::cout << "Starting NTLS HTTP Client Examples..." << std::endl; - std::cout << "Testing NTLS/TLCP protocol with SM2/SM3/SM4 algorithms" << std::endl; - + std::cout << "Testing NTLS/TLCP protocol with SM2/SM3/SM4 algorithms" + << std::endl; + try { // Test one-way authentication NTLS client (port 8801) - //async_simple::coro::syncAwait(test_ntls_http_client_one_way()); - + // async_simple::coro::syncAwait(test_ntls_http_client_one_way()); + // Test mutual authentication NTLS client (port 8802) async_simple::coro::syncAwait(test_ntls_http_client_mutual_auth()); - + } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; return 1; } - + std::cout << "\nNTLS HTTP Client Examples completed." << std::endl; return 0; } \ No newline at end of file diff --git a/src/coro_http/examples/ntls_http_server.cpp b/src/coro_http/examples/ntls_http_server.cpp index f1a11c85a..6a75f3d1b 100644 --- a/src/coro_http/examples/ntls_http_server.cpp +++ b/src/coro_http/examples/ntls_http_server.cpp @@ -23,104 +23,123 @@ using namespace cinatra; // Certificate paths - adjust to your environment const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; -const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 signing certificate -const std::string SERVER_SIGN_KEY = CERT_PATH + "server_sign.key"; // SM2 signing private key -const std::string SERVER_ENC_CERT = CERT_PATH + "server_enc.crt"; // SM2 encryption certificate -const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; // SM2 encryption private key -const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +const std::string SERVER_SIGN_CERT = + CERT_PATH + "server_sign.crt"; // SM2 signing certificate +const std::string SERVER_SIGN_KEY = + CERT_PATH + "server_sign.key"; // SM2 signing private key +const std::string SERVER_ENC_CERT = + CERT_PATH + "server_enc.crt"; // SM2 encryption certificate +const std::string SERVER_ENC_KEY = + CERT_PATH + "server_enc.key"; // SM2 encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Start one-way authentication NTLS server (server certificate only) void start_one_way_server() { - std::cout << "Starting NTLS HTTP Server (one-way auth) on port 8801..." << std::endl; - + std::cout << "Starting NTLS HTTP Server (one-way auth) on port 8801..." + << std::endl; + coro_http_server server(1, 8801); - + // Initialize NTLS server with dual certificates (SM2 sign/encrypt) // One-way authentication mode (no client certificate verification) - server.init_ntls(SERVER_SIGN_CERT, - SERVER_SIGN_KEY, - SERVER_ENC_CERT, - SERVER_ENC_KEY, - CA_CERT, + server.init_ntls(SERVER_SIGN_CERT, SERVER_SIGN_KEY, SERVER_ENC_CERT, + SERVER_ENC_KEY, CA_CERT, false); // false = don't verify client certificate - + // Set NTLS cipher suites server.set_ntls_cipher_suites("ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"); - + // Root endpoint - server.set_http_handler("/", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "Hello World"); - co_return; - }); - + server.set_http_handler( + "/", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + // Echo endpoint - server.set_http_handler("/echo", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "Hello World"); - co_return; - }); - + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + std::cout << "One-way NTLS server ready. Test endpoints:" << std::endl; std::cout << " GET https://localhost:8801/" << std::endl; std::cout << " POST https://localhost:8801/echo" << std::endl; - + auto result = server.sync_start(); if (result) { - std::cerr << "Failed to start one-way server: " << result.message() << std::endl; + std::cerr << "Failed to start one-way server: " << result.message() + << std::endl; } } // Start mutual authentication NTLS server (requires client certificate) void start_mutual_auth_server() { - std::cout << "Starting NTLS HTTP Server (mutual auth) on port 8802..." << std::endl; - + std::cout << "Starting NTLS HTTP Server (mutual auth) on port 8802..." + << std::endl; + coro_http_server server(1, 8802); - + // Initialize NTLS server with dual certificates (SM2 sign/encrypt) // Mutual authentication mode (client certificate verification enabled) - server.init_ntls(SERVER_SIGN_CERT, - SERVER_SIGN_KEY, - SERVER_ENC_CERT, - SERVER_ENC_KEY, - CA_CERT, + server.init_ntls(SERVER_SIGN_CERT, SERVER_SIGN_KEY, SERVER_ENC_CERT, + SERVER_ENC_KEY, CA_CERT, true); // true = verify client certificate - + // Set NTLS cipher suites server.set_ntls_cipher_suites("ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"); - + // Root endpoint - server.set_http_handler("/", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "Hello World"); - co_return; - }); - + server.set_http_handler( + "/", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + // Echo endpoint - server.set_http_handler("/echo", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "Hello World"); - co_return; - }); - + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + std::cout << "Mutual auth NTLS server ready. Test endpoints:" << std::endl; - std::cout << " GET https://localhost:8802/ (requires client certificate)" << std::endl; - std::cout << " POST https://localhost:8802/echo (requires client certificate)" << std::endl; - + std::cout << " GET https://localhost:8802/ (requires client certificate)" + << std::endl; + std::cout + << " POST https://localhost:8802/echo (requires client certificate)" + << std::endl; + auto result = server.sync_start(); if (result) { - std::cerr << "Failed to start mutual auth server: " << result.message() << std::endl; + std::cerr << "Failed to start mutual auth server: " << result.message() + << std::endl; } } int main() { std::cout << "NTLS HTTP Server Example - Starting two servers:" << std::endl; - std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; - std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; - + std::cout << "1. One-way authentication (8801) - Server certificate only" + << std::endl; + std::cout << "2. Mutual authentication (8802) - Requires client certificate" + << std::endl; + // Start both servers in separate threads std::thread one_way_thread(start_one_way_server); std::thread mutual_auth_thread(start_mutual_auth_server); - + // Wait for both servers one_way_thread.join(); mutual_auth_thread.join(); - + return 0; } \ No newline at end of file diff --git a/src/coro_rpc/examples/base_examples/ntls_client.cpp b/src/coro_rpc/examples/base_examples/ntls_client.cpp index 3e47ff246..dee5c20fb 100644 --- a/src/coro_rpc/examples/base_examples/ntls_client.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_client.cpp @@ -17,6 +17,7 @@ #include #include #include + #include "async_simple/coro/Lazy.h" #include "async_simple/coro/SyncAwait.h" @@ -26,31 +27,36 @@ using namespace std::string_literals; // Certificate paths - adjust to your environment const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; -const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate -const std::string CLIENT_SIGN_KEY = CERT_PATH + "client_sign.key"; // SM2 client signing private key -const std::string CLIENT_ENC_CERT = CERT_PATH + "client_enc.crt"; // SM2 client encryption certificate -const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; // SM2 client encryption private key -const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +const std::string CLIENT_SIGN_CERT = + CERT_PATH + "client_sign.crt"; // SM2 client signing certificate +const std::string CLIENT_SIGN_KEY = + CERT_PATH + "client_sign.key"; // SM2 client signing private key +const std::string CLIENT_ENC_CERT = + CERT_PATH + "client_enc.crt"; // SM2 client encryption certificate +const std::string CLIENT_ENC_KEY = + CERT_PATH + "client_enc.key"; // SM2 client encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // RPC function declarations (should match server) std::string echo(std::string_view data); // Test one-way authentication NTLS client (server certificate only) Lazy test_one_way_auth() { - std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" << std::endl; - + std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" + << std::endl; + coro_rpc_client client; // Initialize NTLS client for one-way authentication // In one-way mode, we only verify server's certificate - bool ok = client.init_ntls(CERT_PATH, // Certificate base path - "", // No client signing certificate needed - "", // No client signing key needed - "", // No client encryption certificate needed - "", // No client encryption key needed - CA_CERT, // CA certificate - "localhost", // Server domain - false); // Don't verify client certificate + bool ok = client.init_ntls(CERT_PATH, // Certificate base path + "", // No client signing certificate needed + "", // No client signing key needed + "", // No client encryption certificate needed + "", // No client encryption key needed + CA_CERT, // CA certificate + "localhost", // Server domain + false); // Don't verify client certificate if (!ok) { std::cout << "Failed to initialize one-way NTLS client" << std::endl; @@ -62,7 +68,8 @@ Lazy test_one_way_auth() { // Connect to one-way NTLS server auto ec = co_await client.connect("127.0.0.1", "8801"); if (ec) { - std::cout << "Failed to connect to one-way server: " << ec.message() << std::endl; + std::cout << "Failed to connect to one-way server: " << ec.message() + << std::endl; co_return; } @@ -72,8 +79,10 @@ Lazy test_one_way_auth() { // Test echo function auto result = co_await client.call("Test message to one-way server"); if (result) { - std::cout << "Echo result from one-way server: " << result.value() << std::endl; - } else { + std::cout << "Echo result from one-way server: " << result.value() + << std::endl; + } + else { std::cout << "Echo failed: " << result.error().msg << std::endl; } @@ -84,22 +93,23 @@ Lazy test_one_way_auth() { // Test mutual authentication NTLS client (client certificate required) Lazy test_mutual_auth() { - std::cout << "\n=== Mutual Authentication NTLS Client (8802) ===" << std::endl; - + std::cout << "\n=== Mutual Authentication NTLS Client (8802) ===" + << std::endl; + coro_rpc_client client; // Initialize NTLS client for mutual authentication // In mutual mode, both client and server certificates are verified - bool ok = client.init_ntls( - CERT_PATH, // Certificate base path - CLIENT_SIGN_CERT, // client signing certificate needed - CLIENT_SIGN_KEY, // client signing key needed - CLIENT_ENC_CERT, // client encryption certificate needed - CLIENT_ENC_KEY, // client encryption key needed - CA_CERT, // CA certificate - "localhost", // Server domain - true); // Don't verify client certificate + bool ok = + client.init_ntls(CERT_PATH, // Certificate base path + CLIENT_SIGN_CERT, // client signing certificate needed + CLIENT_SIGN_KEY, // client signing key needed + CLIENT_ENC_CERT, // client encryption certificate needed + CLIENT_ENC_KEY, // client encryption key needed + CA_CERT, // CA certificate + "localhost", // Server domain + true); // Don't verify client certificate if (!ok) { std::cout << "Failed to initialize mutual auth NTLS client" << std::endl; @@ -111,18 +121,23 @@ Lazy test_mutual_auth() { // Connect to mutual auth NTLS server auto ec = co_await client.connect("127.0.0.1", "8802"); if (ec) { - std::cout << "Failed to connect to mutual auth server: " << ec.message() << std::endl; + std::cout << "Failed to connect to mutual auth server: " << ec.message() + << std::endl; co_return; } - std::cout << "Connected to mutual auth NTLS server successfully!" << std::endl; + std::cout << "Connected to mutual auth NTLS server successfully!" + << std::endl; try { // Test echo function - auto result = co_await client.call("Test message to mutual auth server"); + auto result = + co_await client.call("Test message to mutual auth server"); if (result) { - std::cout << "Echo result from mutual auth server: " << result.value() << std::endl; - } else { + std::cout << "Echo result from mutual auth server: " << result.value() + << std::endl; + } + else { std::cout << "Echo failed: " << result.error().msg << std::endl; } @@ -134,8 +149,10 @@ Lazy test_mutual_auth() { int main() { try { std::cout << "NTLS RPC Client Example - Testing two servers:" << std::endl; - std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; - std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + std::cout << "1. One-way authentication (8801) - Server certificate only" + << std::endl; + std::cout << "2. Mutual authentication (8802) - Requires client certificate" + << std::endl; // Test one-way authentication client syncAwait(test_one_way_auth()); diff --git a/src/coro_rpc/examples/base_examples/ntls_server.cpp b/src/coro_rpc/examples/base_examples/ntls_server.cpp index 44cdfd5d8..0003bfec4 100644 --- a/src/coro_rpc/examples/base_examples/ntls_server.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_server.cpp @@ -24,11 +24,15 @@ using namespace std::string_literals; // Certificate paths - adjust to your environment const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; -const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 server signing certificate -const std::string SERVER_SIGN_KEY = CERT_PATH + "server_sign.key"; // SM2 server signing private key -const std::string SERVER_ENC_CERT = CERT_PATH + "server_enc.crt"; // SM2 server encryption certificate -const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; // SM2 server encryption private key -const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +const std::string SERVER_SIGN_CERT = + CERT_PATH + "server_sign.crt"; // SM2 server signing certificate +const std::string SERVER_SIGN_KEY = + CERT_PATH + "server_sign.key"; // SM2 server signing private key +const std::string SERVER_ENC_CERT = + CERT_PATH + "server_enc.crt"; // SM2 server encryption certificate +const std::string SERVER_ENC_KEY = + CERT_PATH + "server_enc.key"; // SM2 server encryption private key +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Simple RPC service function std::string echo(std::string_view data) { @@ -44,14 +48,15 @@ void start_one_way_server() { // Configure NTLS with Tongsuo (one-way authentication) ssl_ntls_configure ntls_conf; - ntls_conf.base_path = ""; // Using full paths instead - ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2 signing certificate - ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2 signing private key - ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2 encryption certificate - ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2 encryption private key - ntls_conf.ca_cert_file = CA_CERT; // CA certificate - ntls_conf.enable_client_verify = false; // Disable client certificate verification - ntls_conf.enable_ntls = true; // Enable NTLS mode + ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2 signing certificate + ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2 signing private key + ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2 encryption certificate + ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2 encryption private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = + false; // Disable client certificate verification + ntls_conf.enable_ntls = true; // Enable NTLS mode // Initialize NTLS server.init_ntls(ntls_conf); @@ -59,7 +64,8 @@ void start_one_way_server() { // Register RPC service function server.register_handler(); - std::cout << "NTLS RPC Server (one-way auth) starting on port 8801..." << std::endl; + std::cout << "NTLS RPC Server (one-way auth) starting on port 8801..." + << std::endl; std::cout << "Using Tongsuo for NTLS support" << std::endl; std::cout << "Certificate path: " << CERT_PATH << std::endl; @@ -67,8 +73,10 @@ void start_one_way_server() { auto result = server.start(); if (result) { std::cout << "One-way auth server started successfully!" << std::endl; - } else { - std::cout << "Failed to start one-way auth server: " << result.message() << std::endl; + } + else { + std::cout << "Failed to start one-way auth server: " << result.message() + << std::endl; } } catch (const std::exception& e) { @@ -84,14 +92,15 @@ void start_mutual_auth_server() { // Configure NTLS with Tongsuo (mutual authentication) ssl_ntls_configure ntls_conf; - ntls_conf.base_path = ""; // Using full paths instead - ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2 signing certificate - ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2 signing private key - ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2 encryption certificate - ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2 encryption private key - ntls_conf.ca_cert_file = CA_CERT; // CA certificate - ntls_conf.enable_client_verify = true; // Enable client certificate verification - ntls_conf.enable_ntls = true; // Enable NTLS mode + ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2 signing certificate + ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2 signing private key + ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2 encryption certificate + ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2 encryption private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = + true; // Enable client certificate verification + ntls_conf.enable_ntls = true; // Enable NTLS mode // Initialize NTLS server.init_ntls(ntls_conf); @@ -99,7 +108,8 @@ void start_mutual_auth_server() { // Register RPC service function server.register_handler(); - std::cout << "NTLS RPC Server (mutual auth) starting on port 8802..." << std::endl; + std::cout << "NTLS RPC Server (mutual auth) starting on port 8802..." + << std::endl; std::cout << "Using Tongsuo for NTLS support" << std::endl; std::cout << "Certificate path: " << CERT_PATH << std::endl; @@ -107,8 +117,10 @@ void start_mutual_auth_server() { auto result = server.start(); if (result) { std::cout << "Mutual auth server started successfully!" << std::endl; - } else { - std::cout << "Failed to start mutual auth server: " << result.message() << std::endl; + } + else { + std::cout << "Failed to start mutual auth server: " << result.message() + << std::endl; } } catch (const std::exception& e) { @@ -118,16 +130,18 @@ void start_mutual_auth_server() { int main() { std::cout << "NTLS RPC Server Example - Starting two servers:" << std::endl; - std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; - std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; - + std::cout << "1. One-way authentication (8801) - Server certificate only" + << std::endl; + std::cout << "2. Mutual authentication (8802) - Requires client certificate" + << std::endl; + // Start both servers in separate threads std::thread one_way_thread(start_one_way_server); std::thread mutual_auth_thread(start_mutual_auth_server); - + // Wait for both servers one_way_thread.join(); mutual_auth_thread.join(); - + return 0; } \ No newline at end of file From 6037b4e574164c043c4a67a8b9db25dd9266ee06 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 17 Sep 2025 22:23:32 +0800 Subject: [PATCH 06/38] feat: rpc and http support RFC 8998 --- include/ylt/coro_rpc/impl/common_service.hpp | 246 ++++++++++---- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 262 +++++++++++---- .../standalone/cinatra/coro_http_client.hpp | 85 +++++ .../cinatra/coro_http_connection.hpp | 300 ++++++++++++------ .../standalone/cinatra/coro_http_server.hpp | 59 +++- src/coro_http/examples/ntls_http_client.cpp | 127 +++++++- src/coro_http/examples/ntls_http_server.cpp | 119 ++++++- .../examples/base_examples/ntls_client.cpp | 135 +++++++- .../examples/base_examples/ntls_server.cpp | 118 ++++++- 9 files changed, 1203 insertions(+), 248 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index ae40e4fe7..2826d17e9 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -52,6 +52,16 @@ struct ssl_configure { // bool enable_ntls = true; //!< enable NTLS mode //}; +#ifndef OPENSSL_NO_NTLS +/*! + * NTLS mode enumeration + */ +enum class ntls_mode { + tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + encryption) + tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate +}; +#endif + /*! * Extended SSL config that supports both SSL and NTLS */ @@ -64,7 +74,7 @@ struct ssl_ntls_configure { std::string dh_file; //!< relative path of tmp dh file (optional) #ifndef OPENSSL_NO_NTLS - // NTLS configuration for Tongsuo + // TLCP dual certificate configuration (GB/T 38636-2020) std::string sign_cert_file; //!< relative path of SM2 signing certificate file std::string sign_key_file; //!< relative path of SM2 signing private key file @@ -72,11 +82,19 @@ struct ssl_ntls_configure { enc_cert_file; //!< relative path of SM2 encryption certificate file std::string enc_key_file; //!< relative path of SM2 encryption private key file + + // TLS 1.3 + GM single certificate configuration (RFC 8998) + std::string gm_cert_file; //!< relative path of single SM2 certificate file (for TLS 1.3 + GM) + std::string gm_key_file; //!< relative path of single SM2 private key file (for TLS 1.3 + GM) + + // Common NTLS configuration std::string ca_cert_file; //!< relative path of CA certificate file (optional) std::string cipher_suites; //!< NTLS cipher suites (e.g., //!< "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3") + std::string server_name = "localhost"; //!< server name for verification + ntls_mode mode = ntls_mode::tlcp_dual_cert; //!< NTLS mode selection bool enable_ntls = false; //!< enable NTLS mode bool enable_client_verify = false; //!< enable client certificate verification @@ -186,91 +204,187 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, return false; } - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ctx); - ELOG_INFO << "NTLS mode enabled successfully"; - // Set NTLS cipher suites (SM2/SM3/SM4) - std::string cipher_suites = conf.cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : conf.cipher_suites; - if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_WARN << "Failed to set NTLS cipher suites '" << cipher_suites - << "': " << ::ERR_error_string(err, nullptr); - } - else { - ELOG_INFO << "NTLS cipher suites set to: " << cipher_suites; + // Configure based on NTLS mode + if (conf.mode == ntls_mode::tls13_single_cert) { + // Enable strict SM TLS 1.3 (Tongsuo) + SSL_CTX_enable_sm_tls13_strict(ctx); + + // RFC 8998 TLS 1.3 + GM single certificate mode + ELOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + + // Set TLS 1.3 version + if (SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; + return false; + } + if (SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; + return false; + } + + // Set TLS 1.3 GM cipher suites + std::string cipher_suites = conf.cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : conf.cipher_suites; + if (SSL_CTX_set_ciphersuites(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites '" << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; + + // Set signature algorithms for SM2 + if (SSL_CTX_set1_sigalgs_list(ctx, "sm2sig_sm3") != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "Failed to set SM2 signature algorithms: " + << ::ERR_error_string(err, nullptr); + return false; + } + + // Set supported curves for SM2 + if (SSL_CTX_set1_curves_list(ctx, "SM2") != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "Failed to set SM2 curves: " + << ::ERR_error_string(err, nullptr); + return false; + } + } else { + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ctx); + ELOG_INFO << "NTLS mode enabled successfully"; + + // GB/T 38636-2020 TLCP dual certificate mode (default) + ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; + + // Set TLCP cipher suites (SM2/SM3/SM4) + std::string cipher_suites = conf.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : conf.cipher_suites; + if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_WARN << "Failed to set TLCP cipher suites '" << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + } + else { + ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; + } } context.set_password_callback( [](std::size_t size, asio::ssl::context_base::password_purpose purpose) { return "test"; }); - auto sign_cert_file = fs::path(conf.base_path).append(conf.sign_cert_file); - auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); - auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); - auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); ELOG_INFO << "current path " << fs::current_path().string(); - // Load SM2 signing certificate and key - if (file_exists(sign_cert_file)) { - if (SSL_CTX_use_sign_certificate_file( - ctx, sign_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 signing certificate: " - << ::ERR_error_string(err, nullptr); + // Load certificates based on NTLS mode + if (conf.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + auto gm_cert_file = fs::path(conf.base_path).append(conf.gm_cert_file); + auto gm_key_file = fs::path(conf.base_path).append(conf.gm_key_file); + + // Load single GM certificate + if (file_exists(gm_cert_file)) { + if (SSL_CTX_use_certificate_file(ctx, gm_cert_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load GM certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded GM certificate: " << gm_cert_file.string(); + } + else { + ELOG_ERROR << "no GM certificate file " << gm_cert_file.string(); return false; } - } - else { - ELOG_ERROR << "no SM2 signing certificate file " - << sign_cert_file.string(); - return false; - } - if (file_exists(sign_key_file)) { - if (SSL_CTX_use_sign_PrivateKey_file(ctx, sign_key_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 signing private key: " - << ::ERR_error_string(err, nullptr); + // Load single GM private key + if (file_exists(gm_key_file)) { + if (SSL_CTX_use_PrivateKey_file(ctx, gm_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load GM private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded GM private key: " << gm_key_file.string(); + } + else { + ELOG_ERROR << "no GM private key file " << gm_key_file.string(); return false; } - } - else { - ELOG_ERROR << "no SM2 signing key file " << sign_key_file.string(); - return false; - } + } else { + // GB/T 38636-2020 TLCP dual certificate mode + auto sign_cert_file = fs::path(conf.base_path).append(conf.sign_cert_file); + auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); + auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); + auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); - // Load SM2 encryption certificate and key - if (file_exists(enc_cert_file)) { - if (SSL_CTX_use_enc_certificate_file(ctx, enc_cert_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 encryption certificate: " - << ::ERR_error_string(err, nullptr); + // Load SM2 signing certificate and key + if (file_exists(sign_cert_file)) { + if (SSL_CTX_use_sign_certificate_file( + ctx, sign_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 signing certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 signing certificate: " << sign_cert_file.string(); + } + else { + ELOG_ERROR << "no SM2 signing certificate file " + << sign_cert_file.string(); return false; } - } - else { - ELOG_ERROR << "no SM2 encryption certificate file " - << enc_cert_file.string(); - return false; - } - if (file_exists(enc_key_file)) { - if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 encryption private key: " - << ::ERR_error_string(err, nullptr); + if (file_exists(sign_key_file)) { + if (SSL_CTX_use_sign_PrivateKey_file(ctx, sign_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 signing private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 signing private key: " << sign_key_file.string(); + } + else { + ELOG_ERROR << "no SM2 signing key file " << sign_key_file.string(); + return false; + } + + // Load SM2 encryption certificate and key + if (file_exists(enc_cert_file)) { + if (SSL_CTX_use_enc_certificate_file(ctx, enc_cert_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 encryption certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 encryption certificate: " << enc_cert_file.string(); + } + else { + ELOG_ERROR << "no SM2 encryption certificate file " + << enc_cert_file.string(); + return false; + } + + if (file_exists(enc_key_file)) { + if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 encryption private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 encryption private key: " << enc_key_file.string(); + } + else { + ELOG_ERROR << "no SM2 encryption key file " << enc_key_file.string(); return false; } - } - else { - ELOG_ERROR << "no SM2 encryption key file " << enc_key_file.string(); - return false; } // Load CA certificate if provided (ASIO wrapper is fine) diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index 370ad46c7..7c2337acf 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -189,12 +189,22 @@ class coro_rpc_client { struct tcp_with_ntls_config { bool enable_tcp_no_delay = true; std::filesystem::path base_path{}; + + // TLCP dual certificate configuration (GB/T 38636-2020) std::filesystem::path sign_cert_path{}; std::filesystem::path sign_key_path{}; std::filesystem::path enc_cert_path{}; std::filesystem::path enc_key_path{}; + + // TLS 1.3 + GM single certificate configuration (RFC 8998) + std::filesystem::path gm_cert_path{}; + std::filesystem::path gm_key_path{}; + + // Common configuration std::filesystem::path ca_cert_path{}; std::string ssl_domain{}; + std::string cipher_suites{}; + ntls_mode mode = ntls_mode::tlcp_dual_cert; bool enable_client_verify = false; }; #endif // OPENSSL_NO_NTLS @@ -309,73 +319,155 @@ class coro_rpc_client { ssl_init_ret_ = false; ELOG_INFO << "init NTLS: " << config.ssl_domain; - // Create SSL context with TLCP client method for NTLS - ssl_ctx_ = asio::ssl::context(SSL_CTX_new(NTLS_client_method())); - - // Enable NTLS mode - Tongsuo will handle protocol version automatically - SSL_CTX_enable_ntls(ssl_ctx_.native_handle()); - ELOG_INFO << "NTLS mode enabled successfully"; + + + // Configure based on NTLS mode + if (config.mode == ntls_mode::tls13_single_cert) { + // Create SSL context with TLCP server method + ssl_ctx_ = asio::ssl::context(SSL_CTX_new(TLS_method())); + + // Enable strict SM TLS 1.3 (Tongsuo) + SSL_CTX_enable_sm_tls13_strict(ssl_ctx_.native_handle()); + // RFC 8998 TLS 1.3 + GM single certificate mode + ELOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + + // Set TLS 1.3 version + if (SSL_CTX_set_min_proto_version(ssl_ctx_.native_handle(), TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; + return false; + } + if (SSL_CTX_set_max_proto_version(ssl_ctx_.native_handle(), TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; + return false; + } - // Set cipher suites for NTLS (SM2/SM3/SM4) - if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), - "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { - ELOG_WARN << "failed to set NTLS cipher suites, using default"; - } - if (config.enable_client_verify) { - ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; - if (config.sign_cert_path.empty() || - !file_exists(config.sign_cert_path) || - config.sign_key_path.empty() || - !file_exists(config.sign_key_path) || - config.enc_cert_path.empty() || - !file_exists(config.enc_cert_path) || config.enc_key_path.empty() || - !file_exists(config.enc_key_path)) { - ELOG_ERROR - << "client verify enabled, but cert or key file is missing"; + // Set TLS 1.3 GM cipher suites + std::string cipher_suites = config.cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : config.cipher_suites; + if (SSL_CTX_set_ciphersuites(ssl_ctx_.native_handle(), cipher_suites.c_str()) != 1) { + ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites: " << cipher_suites; return false; } + ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; + + //// Set supported curves for SM2 + //if (SSL_CTX_set1_curves_list(ssl_ctx_.native_handle(), "SM2:X25519:prime256v1") != 1) { + // ELOG_ERROR << "Failed to set SM2 curves"; + // return false; + //} + } else { + // Create SSL context with TLCP client method for NTLS + ssl_ctx_ = asio::ssl::context(SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode - Tongsuo will handle protocol version automatically + SSL_CTX_enable_ntls(ssl_ctx_.native_handle()); + ELOG_INFO << "NTLS mode enabled successfully"; + + // GB/T 38636-2020 TLCP dual certificate mode (default) + ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; + + // Set TLCP cipher suites for NTLS (SM2/SM3/SM4) + std::string cipher_suites = config.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : config.cipher_suites; + if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), cipher_suites.c_str())) { + ELOG_WARN << "failed to set TLCP cipher suites: " << cipher_suites << ", using default"; + } else { + ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; + } } - // Load certificates if provided - if (!config.sign_cert_path.empty() && - file_exists(config.sign_cert_path)) { - ELOG_INFO << "load SM2 signing cert " << config.sign_cert_path.string(); - if (!SSL_CTX_use_sign_certificate_file( - ssl_ctx_.native_handle(), - config.sign_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 signing certificate"; - return false; + // Load certificates based on NTLS mode + if (config.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + if (config.enable_client_verify) { + ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; + if (config.gm_cert_path.empty() || !file_exists(config.gm_cert_path) || + config.gm_key_path.empty() || !file_exists(config.gm_key_path)) { + ELOG_ERROR << "client verify enabled, but GM cert or key file is missing"; + return false; + } } - if (!config.sign_key_path.empty() && - file_exists(config.sign_key_path)) { - ELOG_INFO << "load SM2 signing private key " - << config.sign_key_path.string(); - if (!SSL_CTX_use_sign_PrivateKey_file( - ssl_ctx_.native_handle(), - config.sign_key_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 signing private key"; + + // Load single GM certificate and key + if (!config.gm_cert_path.empty() && file_exists(config.gm_cert_path)) { + ELOG_INFO << "load GM certificate " << config.gm_cert_path.string(); + if (!SSL_CTX_use_certificate_file(ssl_ctx_.native_handle(), + config.gm_cert_path.string().c_str(), + SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load GM certificate"; return false; } } - } - if (!config.enc_cert_path.empty() && file_exists(config.enc_cert_path)) { - ELOG_INFO << "load SM2 encryption cert " - << config.enc_cert_path.string(); - if (!SSL_CTX_use_enc_certificate_file( - ssl_ctx_.native_handle(), config.enc_cert_path.string().c_str(), - SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 encryption certificate"; - return false; + if (!config.gm_key_path.empty() && file_exists(config.gm_key_path)) { + ELOG_INFO << "load GM private key " << config.gm_key_path.string(); + if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_.native_handle(), + config.gm_key_path.string().c_str(), + SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load GM private key"; + return false; + } + } + } else { + // GB/T 38636-2020 TLCP dual certificate mode + if (config.enable_client_verify) { + ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; + if (config.sign_cert_path.empty() || + !file_exists(config.sign_cert_path) || + config.sign_key_path.empty() || + !file_exists(config.sign_key_path) || + config.enc_cert_path.empty() || + !file_exists(config.enc_cert_path) || config.enc_key_path.empty() || + !file_exists(config.enc_key_path)) { + ELOG_ERROR + << "client verify enabled, but cert or key file is missing"; + return false; + } } - if (!config.enc_key_path.empty() && file_exists(config.enc_key_path)) { - ELOG_INFO << "load SM2 encryption private key " - << config.enc_key_path.string(); - if (!SSL_CTX_use_enc_PrivateKey_file( + + // Load dual certificates if provided + if (!config.sign_cert_path.empty() && + file_exists(config.sign_cert_path)) { + ELOG_INFO << "load SM2 signing cert " << config.sign_cert_path.string(); + if (!SSL_CTX_use_sign_certificate_file( ssl_ctx_.native_handle(), - config.enc_key_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 encryption private key"; + config.sign_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 signing certificate"; return false; } + if (!config.sign_key_path.empty() && + file_exists(config.sign_key_path)) { + ELOG_INFO << "load SM2 signing private key " + << config.sign_key_path.string(); + if (!SSL_CTX_use_sign_PrivateKey_file( + ssl_ctx_.native_handle(), + config.sign_key_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 signing private key"; + return false; + } + } + } + + if (!config.enc_cert_path.empty() && file_exists(config.enc_cert_path)) { + ELOG_INFO << "load SM2 encryption cert " + << config.enc_cert_path.string(); + if (!SSL_CTX_use_enc_certificate_file( + ssl_ctx_.native_handle(), config.enc_cert_path.string().c_str(), + SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 encryption certificate"; + return false; + } + if (!config.enc_key_path.empty() && file_exists(config.enc_key_path)) { + ELOG_INFO << "load SM2 encryption private key " + << config.enc_key_path.string(); + if (!SSL_CTX_use_enc_PrivateKey_file( + ssl_ctx_.native_handle(), + config.enc_key_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 encryption private key"; + return false; + } + } } } @@ -531,12 +623,62 @@ class coro_rpc_client { std::get(config_.socket_config)); } #ifndef OPENSSL_NO_NTLS - //[[nodiscard]] bool init_ntls(const ssl_ntls_configure &conf) { - // return init_ntls(conf.base_path, conf.sign_cert_file, conf.sign_key_file, - // conf.enc_cert_file, conf.enc_key_file, conf.ca_cert_file, - // conf..empty() ? "localhost" : conf.server_name, - // conf.enable_client_verify); - //} + [[nodiscard]] bool init_ntls(const ssl_ntls_configure &conf) { + if (conf.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + if (config_.socket_config.index() != 2) { + config_.socket_config = + tcp_with_ntls_config{.enable_tcp_no_delay = true, + .base_path = conf.base_path, + .gm_cert_path = std::filesystem::path(conf.base_path).append(conf.gm_cert_file), + .gm_key_path = std::filesystem::path(conf.base_path).append(conf.gm_key_file), + .ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file), + .ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name, + .cipher_suites = conf.cipher_suites, + .mode = ntls_mode::tls13_single_cert, + .enable_client_verify = conf.enable_client_verify}; + } else { + auto &config = std::get(config_.socket_config); + config.base_path = conf.base_path; + config.gm_cert_path = std::filesystem::path(conf.base_path).append(conf.gm_cert_file); + config.gm_key_path = std::filesystem::path(conf.base_path).append(conf.gm_key_file); + config.ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file); + config.ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name; + config.cipher_suites = conf.cipher_suites; + config.mode = ntls_mode::tls13_single_cert; + config.enable_client_verify = conf.enable_client_verify; + } + } else { + // GB/T 38636-2020 TLCP dual certificate mode (default) + if (config_.socket_config.index() != 2) { + config_.socket_config = + tcp_with_ntls_config{.enable_tcp_no_delay = true, + .base_path = conf.base_path, + .sign_cert_path = std::filesystem::path(conf.base_path).append(conf.sign_cert_file), + .sign_key_path = std::filesystem::path(conf.base_path).append(conf.sign_key_file), + .enc_cert_path = std::filesystem::path(conf.base_path).append(conf.enc_cert_file), + .enc_key_path = std::filesystem::path(conf.base_path).append(conf.enc_key_file), + .ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file), + .ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name, + .cipher_suites = conf.cipher_suites, + .mode = ntls_mode::tlcp_dual_cert, + .enable_client_verify = conf.enable_client_verify}; + } else { + auto &config = std::get(config_.socket_config); + config.base_path = conf.base_path; + config.sign_cert_path = std::filesystem::path(conf.base_path).append(conf.sign_cert_file); + config.sign_key_path = std::filesystem::path(conf.base_path).append(conf.sign_key_file); + config.enc_cert_path = std::filesystem::path(conf.base_path).append(conf.enc_cert_file); + config.enc_key_path = std::filesystem::path(conf.base_path).append(conf.enc_key_file); + config.ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file); + config.ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name; + config.cipher_suites = conf.cipher_suites; + config.mode = ntls_mode::tlcp_dual_cert; + config.enable_client_verify = conf.enable_client_verify; + } + } + return init_socket_wrapper(std::get(config_.socket_config)); + } [[nodiscard]] bool init_ntls(std::string_view base_file, std::string_view sign_cert_file, diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index ff7d25a28..db63e6f4a 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -1582,6 +1582,91 @@ class coro_http_client : public std::enable_shared_from_this { return false; } } + /*! + * Initialize NTLS client with TLS 1.3 + GM single certificate mode (RFC 8998) + */ + bool init_ntls_tls13_gm_client(const std::string &gm_cert_file = "", + const std::string &gm_key_file = "", + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none, + const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", + const std::string &passwd = "") { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(TLS_client_method())); + + // Configure TLS 1.3 + GM mode (RFC 8998) + SSL_CTX_set_min_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); + + // Enable strict SM TLS 1.3 mode (Tongsuo specific) + SSL_CTX_enable_sm_tls13_strict(ssl_ctx_->native_handle()); + + // Set TLS 1.3 GM cipher suites + if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), cipher_suites.c_str())) { + CINATRA_LOG_WARNING << "failed to set TLS 1.3 GM cipher suites"; + } + + // Set GM signature algorithms (required for SM TLS 1.3 strict mode) + if (!SSL_CTX_set1_sigalgs_list(ssl_ctx_->native_handle(), "sm2sig_sm3")) { + CINATRA_LOG_WARNING << "failed to set GM signature algorithms"; + } + + // Set GM curves (required for SM TLS 1.3 strict mode) + if (!SSL_CTX_set1_curves_list(ssl_ctx_->native_handle(), "SM2")) { + CINATRA_LOG_WARNING << "failed to set GM curves"; + } + + if (!passwd.empty()) { + ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { + return pwd; + }); + } + + // Load client certificate if provided (for mutual authentication) + if (!gm_cert_file.empty() && !gm_key_file.empty()) { + if (!SSL_CTX_use_certificate_file(ssl_ctx_->native_handle(), + gm_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client GM certificate"; + return false; + } + + if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_->native_handle(), + gm_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client GM private key"; + return false; + } + } + + // Load CA certificate if provided + if (!ca_cert_file.empty()) { + if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), + ca_cert_file.c_str(), nullptr)) { + CINATRA_LOG_WARNING << "failed to load CA certificate"; + } + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + has_init_ssl_ = true; + use_ntls_ = true; + return true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init TLS 1.3 + GM client failed: " << e.what(); + return false; + } + } + #endif // OPENSSL_NO_NTLS #endif diff --git a/include/ylt/standalone/cinatra/coro_http_connection.hpp b/include/ylt/standalone/cinatra/coro_http_connection.hpp index 92247e1d3..6c7fb7f08 100644 --- a/include/ylt/standalone/cinatra/coro_http_connection.hpp +++ b/include/ylt/standalone/cinatra/coro_http_connection.hpp @@ -99,6 +99,14 @@ class coro_http_connection } #ifndef OPENSSL_NO_NTLS + /*! + * NTLS mode enumeration for HTTP connection + */ + enum class ntls_mode { + tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + encryption) + tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate + }; + /*! * Initialize NTLS SSL context with dual certificates (Tongsuo native API) */ @@ -115,31 +123,89 @@ class coro_http_connection //} bool init_ntls(const auto &ntls_config, std::string passwd) { try { - // Create SSL context with TLCP server method - ssl_ctx_ = - std::make_unique(SSL_CTX_new(NTLS_method())); + SSL_CTX *ctx = NULL; + // Configure based on NTLS mode + if (ntls_config.mode == ntls_mode::tls13_single_cert) { + // Create SSL context with TLCP server method + ssl_ctx_ = + std::make_unique(SSL_CTX_new(TLS_method())); + + // Enable NTLS using Tongsuo native API + ctx = ssl_ctx_->native_handle(); + if (!ctx) { + CINATRA_LOG_ERROR << "SSL_CTX native_handle is null"; + return false; + } + // RFC 8998 TLS 1.3 + GM single certificate mode + CINATRA_LOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + + // Enable strict SM TLS 1.3 (Tongsuo) + SSL_CTX_enable_sm_tls13_strict(ctx); - // Enable NTLS using Tongsuo native API - SSL_CTX *ctx = ssl_ctx_->native_handle(); - if (!ctx) { - CINATRA_LOG_ERROR << "SSL_CTX native_handle is null"; - return false; - } + CINATRA_LOG_INFO << "TLS 1.3 strict mode enabled successfully"; - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ctx); - CINATRA_LOG_INFO << "NTLS mode enabled successfully"; - - // Set NTLS cipher suites - std::string cipher_suites = - ntls_config.cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : ntls_config.cipher_suites; - if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { - unsigned long err = ::ERR_get_error(); - CINATRA_LOG_WARNING << "Failed to set NTLS cipher suites '" + // Enforce TLS 1.3 only + if (SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) != 1) { + CINATRA_LOG_ERROR << "Failed to set minimum TLS version to 1.3"; + return false; + } + if (SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) != 1) { + CINATRA_LOG_ERROR << "Failed to set maximum TLS version to 1.3"; + return false; + } + + // Set TLS 1.3 GM cipher suites (align with s_client) + std::string cipher_suites = ntls_config.cipher_suites.empty() + ? "TLS_SM4_GCM_SM3" + : ntls_config.cipher_suites; + if (SSL_CTX_set_ciphersuites(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "Failed to set TLS 1.3 GM cipher suites '" << cipher_suites << "': " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; + + //// Set supported curves for SM2 (strict mode requires only SM2) + //if (SSL_CTX_set1_curves_list(ctx, "SM2:X25519:prime256v1") != 1) { + // unsigned long err = ::ERR_get_error(); + // CINATRA_LOG_ERROR << "Failed to set SM2 curves: " + // << ::ERR_error_string(err, nullptr); + // return false; + //} + CINATRA_LOG_INFO << "SM2 curves configured for strict mode"; + } else { + // Create SSL context with TLCP server method + ssl_ctx_ = + std::make_unique(SSL_CTX_new(NTLS_method())); + + // Enable NTLS using Tongsuo native API + ctx = ssl_ctx_->native_handle(); + if (!ctx) { + CINATRA_LOG_ERROR << "SSL_CTX native_handle is null"; + return false; + } + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ctx); + CINATRA_LOG_INFO << "NTLS mode enabled successfully"; + + // GB/T 38636-2020 TLCP dual certificate mode (default) + CINATRA_LOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; + + // Set TLCP cipher suites + std::string cipher_suites = ntls_config.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : ntls_config.cipher_suites; + if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_WARNING << "Failed to set TLCP cipher suites '" + << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + } else { + CINATRA_LOG_INFO << "TLCP cipher suites set to: " << cipher_suites; + } } // Set context options @@ -152,92 +218,144 @@ class coro_http_connection }); } namespace fs = std::filesystem; + std::error_code file_ec; - // Build full paths - auto sign_cert_path = ntls_config.base_path.empty() - ? ntls_config.sign_cert_file + // Load certificates based on NTLS mode + if (ntls_config.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + auto gm_cert_path = ntls_config.base_path.empty() + ? ntls_config.gm_cert_file : fs::path(ntls_config.base_path) - .append(ntls_config.sign_cert_file); - auto sign_key_path = ntls_config.base_path.empty() - ? ntls_config.sign_key_file + .append(ntls_config.gm_cert_file); + auto gm_key_path = ntls_config.base_path.empty() + ? ntls_config.gm_key_file : fs::path(ntls_config.base_path) - .append(ntls_config.sign_key_file); - auto enc_cert_path = ntls_config.base_path.empty() - ? ntls_config.enc_cert_file - : fs::path(ntls_config.base_path) - .append(ntls_config.enc_cert_file); - auto enc_key_path = ntls_config.base_path.empty() - ? ntls_config.enc_key_file - : fs::path(ntls_config.base_path) - .append(ntls_config.enc_key_file); - - std::error_code file_ec; + .append(ntls_config.gm_key_file); + + // Load single GM certificate + if (fs::exists(gm_cert_path, file_ec)) { + if (SSL_CTX_use_certificate_file(ctx, gm_cert_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load GM certificate: " + << gm_cert_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "loaded GM certificate: " << gm_cert_path.string(); + } + else { + CINATRA_LOG_ERROR << "GM certificate file not found: " + << gm_cert_path.string(); + return false; + } - // Load SM2 signing certificate and key - if (fs::exists(sign_cert_path, file_ec)) { - if (SSL_CTX_use_sign_certificate_file( - ctx, sign_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - CINATRA_LOG_ERROR << "failed to load SM2 signing certificate: " - << sign_cert_path.string() - << ", err: " << ::ERR_error_string(err, nullptr); + // Load single GM private key + if (fs::exists(gm_key_path, file_ec)) { + if (SSL_CTX_use_PrivateKey_file(ctx, gm_key_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load GM private key: " + << gm_key_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "loaded GM private key: " << gm_key_path.string(); + } + else { + CINATRA_LOG_ERROR << "GM private key file not found: " + << gm_key_path.string(); + return false; + } + } else { + // GB/T 38636-2020 TLCP dual certificate mode + auto sign_cert_path = ntls_config.base_path.empty() + ? ntls_config.sign_cert_file + : fs::path(ntls_config.base_path) + .append(ntls_config.sign_cert_file); + auto sign_key_path = ntls_config.base_path.empty() + ? ntls_config.sign_key_file + : fs::path(ntls_config.base_path) + .append(ntls_config.sign_key_file); + auto enc_cert_path = ntls_config.base_path.empty() + ? ntls_config.enc_cert_file + : fs::path(ntls_config.base_path) + .append(ntls_config.enc_cert_file); + auto enc_key_path = ntls_config.base_path.empty() + ? ntls_config.enc_key_file + : fs::path(ntls_config.base_path) + .append(ntls_config.enc_key_file); + + // Load SM2 signing certificate and key + if (fs::exists(sign_cert_path, file_ec)) { + if (SSL_CTX_use_sign_certificate_file( + ctx, sign_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 signing certificate: " + << sign_cert_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "loaded SM2 signing certificate: " << sign_cert_path.string(); + } + else { + CINATRA_LOG_ERROR << "SM2 signing certificate file not found: " + << sign_cert_path.string(); return false; } - } - else { - CINATRA_LOG_ERROR << "SM2 signing certificate file not found: " - << sign_cert_path.string(); - return false; - } - if (fs::exists(sign_key_path, file_ec)) { - if (SSL_CTX_use_sign_PrivateKey_file( - ctx, sign_key_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - CINATRA_LOG_ERROR << "failed to load SM2 signing private key: " - << sign_key_path.string() - << ", err: " << ::ERR_error_string(err, nullptr); + if (fs::exists(sign_key_path, file_ec)) { + if (SSL_CTX_use_sign_PrivateKey_file( + ctx, sign_key_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 signing private key: " + << sign_key_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "loaded SM2 signing private key: " << sign_key_path.string(); + } + else { + CINATRA_LOG_ERROR << "SM2 signing key file not found: " + << sign_key_path.string(); return false; } - } - else { - CINATRA_LOG_ERROR << "SM2 signing key file not found: " - << sign_key_path.string(); - return false; - } - // Load SM2 encryption certificate and key - if (fs::exists(enc_cert_path, file_ec)) { - if (SSL_CTX_use_enc_certificate_file( - ctx, enc_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - CINATRA_LOG_ERROR << "failed to load SM2 encryption certificate: " - << enc_cert_path.string() - << ", err: " << ::ERR_error_string(err, nullptr); + // Load SM2 encryption certificate and key + if (fs::exists(enc_cert_path, file_ec)) { + if (SSL_CTX_use_enc_certificate_file( + ctx, enc_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 encryption certificate: " + << enc_cert_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "loaded SM2 encryption certificate: " << enc_cert_path.string(); + } + else { + CINATRA_LOG_ERROR << "SM2 encryption certificate file not found: " + << enc_cert_path.string(); return false; } - } - else { - CINATRA_LOG_ERROR << "SM2 encryption certificate file not found: " - << enc_cert_path.string(); - return false; - } - if (fs::exists(enc_key_path, file_ec)) { - if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_path.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - CINATRA_LOG_ERROR << "failed to load SM2 encryption private key: " - << enc_key_path.string() - << ", err: " << ::ERR_error_string(err, nullptr); + if (fs::exists(enc_key_path, file_ec)) { + if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + CINATRA_LOG_ERROR << "failed to load SM2 encryption private key: " + << enc_key_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); + return false; + } + CINATRA_LOG_INFO << "loaded SM2 encryption private key: " << enc_key_path.string(); + } + else { + CINATRA_LOG_ERROR << "SM2 encryption key file not found: " + << enc_key_path.string(); return false; } } - else { - CINATRA_LOG_ERROR << "SM2 encryption key file not found: " - << enc_key_path.string(); - return false; - } // Load CA certificate if provided if (!ntls_config.ca_cert_file.empty()) { auto ca_cert_path = ntls_config.base_path.empty() diff --git a/include/ylt/standalone/cinatra/coro_http_server.hpp b/include/ylt/standalone/cinatra/coro_http_server.hpp index 5b2bdaa98..fb51b2400 100644 --- a/include/ylt/standalone/cinatra/coro_http_server.hpp +++ b/include/ylt/standalone/cinatra/coro_http_server.hpp @@ -114,13 +114,53 @@ class coro_http_server { const std::string &enc_cert_file, const std::string &enc_key_file, const std::string &ca_cert_file = "", - bool enable_client_verify = false) { + bool enable_client_verify = false, + const std::string &cipher_suites = "", + coro_http_connection::ntls_mode mode = coro_http_connection::ntls_mode::tlcp_dual_cert) { ntls_config_.base_path = base_path; - ntls_config_.sign_cert_file = sign_cert_file; - ntls_config_.sign_key_file = sign_key_file; - ntls_config_.enc_cert_file = enc_cert_file; - ntls_config_.enc_key_file = enc_key_file; + ntls_config_.mode = mode; + + if (mode == coro_http_connection::ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + ntls_config_.gm_cert_file = sign_cert_file; + ntls_config_.gm_key_file = sign_key_file; + ntls_config_.cipher_suites = cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : cipher_suites; + } else { + // GB/T 38636-2020 TLCP dual certificate mode (default) + ntls_config_.sign_cert_file = sign_cert_file; + ntls_config_.sign_key_file = sign_key_file; + ntls_config_.enc_cert_file = enc_cert_file; + ntls_config_.enc_key_file = enc_key_file; + ntls_config_.cipher_suites = cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : cipher_suites; + } + + ntls_config_.ca_cert_file = ca_cert_file; + ntls_config_.enable_client_verify = enable_client_verify; + ntls_config_.enable_ntls = true; + use_ntls_ = true; + } + + /*! + * Initialize NTLS with RFC 8998 TLS 1.3 + GM single certificate mode + */ + void init_ntls(const std::string &base_path, + const std::string &gm_cert_file, + const std::string &gm_key_file, + const std::string &ca_cert_file = "", + bool enable_client_verify = false, + const std::string &cipher_suites = "") { + ntls_config_.base_path = base_path; + ntls_config_.mode = coro_http_connection::ntls_mode::tls13_single_cert; + ntls_config_.gm_cert_file = gm_cert_file; + ntls_config_.gm_key_file = gm_key_file; ntls_config_.ca_cert_file = ca_cert_file; + ntls_config_.cipher_suites = cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : cipher_suites; ntls_config_.enable_client_verify = enable_client_verify; ntls_config_.enable_ntls = true; use_ntls_ = true; @@ -1132,12 +1172,21 @@ class coro_http_server { // NTLS configuration struct { std::string base_path; + + // TLCP dual certificate configuration (GB/T 38636-2020) std::string sign_cert_file; std::string sign_key_file; std::string enc_cert_file; std::string enc_key_file; + + // TLS 1.3 + GM single certificate configuration (RFC 8998) + std::string gm_cert_file; + std::string gm_key_file; + + // Common configuration std::string ca_cert_file; std::string cipher_suites = "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"; + coro_http_connection::ntls_mode mode = coro_http_connection::ntls_mode::tlcp_dual_cert; bool enable_client_verify = false; bool enable_ntls = false; } ntls_config_; diff --git a/src/coro_http/examples/ntls_http_client.cpp b/src/coro_http/examples/ntls_http_client.cpp index 54e859e3c..05e43842d 100644 --- a/src/coro_http/examples/ntls_http_client.cpp +++ b/src/coro_http/examples/ntls_http_client.cpp @@ -33,6 +33,10 @@ const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; // SM2 client encryption private key const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +// Single certificate paths for RFC 8998 TLS 1.3 + GM mode +const std::string CLIENT_GM_CERT = CERT_PATH + "client_sign.crt"; // GM single certificate +const std::string CLIENT_GM_KEY = CERT_PATH + "client_sign.key"; // GM single private key + // Test one-way authentication NTLS client (server certificate only) async_simple::coro::Lazy test_ntls_http_client_one_way() { std::cout << "\n=== One-way Authentication NTLS Client (8801) ===" @@ -124,23 +128,134 @@ async_simple::coro::Lazy test_ntls_http_client_mutual_auth() { } } +// Test one-way authentication TLS 1.3 + GM client (single certificate) +async_simple::coro::Lazy test_tls13_gm_client_one_way() { + std::cout << "\n=== TLS 1.3 + GM One-way Authentication Client (8803) ===" + << std::endl; + + coro_http_client client{}; + + // Initialize TLS 1.3 + GM client for one-way authentication + // Use empty certificates for one-way mode, but specify TLS 1.3 + GM mode + bool init_ok = client.init_ntls_tls13_gm_client( + "", // No client certificate needed for one-way + "", // No client key needed for one-way + CA_CERT, // CA certificate to verify server + asio::ssl::verify_peer, // Verify server certificate + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" // TLS 1.3 GM cipher suites + ); + + if (!init_ok) { + std::cout << "Failed to initialize TLS 1.3 + GM one-way client" << std::endl; + co_return; + } + + std::cout << "TLS 1.3 + GM HTTP Client initialized (one-way auth mode)" << std::endl; + + // Send GET request to root endpoint + auto response = co_await client.async_get("https://localhost:8803/"); + if (response.net_err) { + std::cout << "GET / request failed: " << response.net_err.message() + << std::endl; + } + else { + std::cout << "GET / Response status: " << response.status << std::endl; + std::cout << "GET / Response body: " << response.resp_body << std::endl; + } + + // Send POST request to echo endpoint + auto post_response = co_await client.async_post( + "https://localhost:8803/echo", "TLS 1.3 + GM test message", req_content_type::text); + if (post_response.net_err) { + std::cout << "POST /echo request failed: " + << post_response.net_err.message() << std::endl; + } + else { + std::cout << "POST /echo Response status: " << post_response.status + << std::endl; + std::cout << "POST /echo Response body: " << post_response.resp_body + << std::endl; + } +} + +// Test mutual authentication TLS 1.3 + GM client (single certificate) +async_simple::coro::Lazy test_tls13_gm_client_mutual_auth() { + std::cout << "\n=== TLS 1.3 + GM Mutual Authentication Client (8804) ===" + << std::endl; + + coro_http_client client{}; + + // Initialize TLS 1.3 + GM client with mutual authentication (single certificate) + bool init_ok = client.init_ntls_tls13_gm_client( + CLIENT_GM_CERT, // Client GM single certificate + CLIENT_GM_KEY, // Client GM single private key + CA_CERT, // CA certificate + asio::ssl::verify_peer, // Verify server certificate + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" // TLS 1.3 GM cipher suites + ); + + if (!init_ok) { + std::cout << "Failed to initialize TLS 1.3 + GM client with mutual authentication" + << std::endl; + co_return; + } + + std::cout << "TLS 1.3 + GM HTTP Client initialized (mutual authentication mode)" + << std::endl; + + // Send GET request to root endpoint (requires client certificate) + auto response = co_await client.async_get("https://localhost:8804/"); + if (response.net_err) { + std::cout << "GET / request failed: " << response.net_err.message() + << std::endl; + } + else { + std::cout << "GET / Response status: " << response.status << std::endl; + std::cout << "GET / Response body: " << response.resp_body << std::endl; + } + + // Send POST request to echo endpoint (requires client certificate) + auto post_response = co_await client.async_post( + "https://localhost:8804/echo", "TLS 1.3 + GM mutual auth test message", + req_content_type::text); + if (post_response.net_err) { + std::cout << "POST /echo request failed: " + << post_response.net_err.message() << std::endl; + } + else { + std::cout << "POST /echo Response status: " << post_response.status + << std::endl; + std::cout << "POST /echo Response body: " << post_response.resp_body + << std::endl; + } +} + int main() { std::cout << "Starting NTLS HTTP Client Examples..." << std::endl; - std::cout << "Testing NTLS/TLCP protocol with SM2/SM3/SM4 algorithms" - << std::endl; + std::cout << "Testing both TLCP and TLS 1.3 + GM protocols" << std::endl; try { - // Test one-way authentication NTLS client (port 8801) - // async_simple::coro::syncAwait(test_ntls_http_client_one_way()); + std::cout << "TLCP Dual Certificate Mode Tests" << std::endl; + + // Test TLCP one-way authentication client (port 8801) + async_simple::coro::syncAwait(test_ntls_http_client_one_way()); - // Test mutual authentication NTLS client (port 8802) + // Test TLCP mutual authentication client (port 8802) async_simple::coro::syncAwait(test_ntls_http_client_mutual_auth()); + std::cout << "TLS 1.3 + GM Single Certificate Mode Tests" << std::endl; + + // Test TLS 1.3 + GM one-way authentication client (port 8803) + async_simple::coro::syncAwait(test_tls13_gm_client_one_way()); + + // Test TLS 1.3 + GM mutual authentication client (port 8804) + async_simple::coro::syncAwait(test_tls13_gm_client_mutual_auth()); + } catch (const std::exception& e) { std::cout << "Exception: " << e.what() << std::endl; return 1; } - std::cout << "\nNTLS HTTP Client Examples completed." << std::endl; + std::cout << "NTLS HTTP Client Examples completed." << std::endl; return 0; } \ No newline at end of file diff --git a/src/coro_http/examples/ntls_http_server.cpp b/src/coro_http/examples/ntls_http_server.cpp index 6a75f3d1b..3273490c1 100644 --- a/src/coro_http/examples/ntls_http_server.cpp +++ b/src/coro_http/examples/ntls_http_server.cpp @@ -33,6 +33,10 @@ const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; // SM2 encryption private key const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +// Single certificate paths for RFC 8998 TLS 1.3 + GM mode +const std::string SERVER_GM_CERT = CERT_PATH + "server_sign.crt"; // GM single certificate +const std::string SERVER_GM_KEY = CERT_PATH + "server_sign.key"; // GM single private key + // Start one-way authentication NTLS server (server certificate only) void start_one_way_server() { std::cout << "Starting NTLS HTTP Server (one-way auth) on port 8801..." @@ -126,20 +130,119 @@ void start_mutual_auth_server() { } } +// Start one-way authentication TLS 1.3 + GM server (single certificate) +void start_tls13_gm_one_way_server() { + std::cout << "Starting TLS 1.3 + GM HTTP Server (one-way auth) on port 8803..." + << std::endl; + + coro_http_server server(1, 8803); + + // Initialize TLS 1.3 + GM server with single certificate (RFC 8998) + // One-way authentication mode (no client certificate verification) + server.init_ntls("", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, + false, // false = don't verify client certificate + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3 GM cipher suites + coro_http_connection::ntls_mode::tls13_single_cert); + + // Root endpoint + server.set_http_handler( + "/", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello from TLS 1.3 + GM Server"); + co_return; + }); + + // Echo endpoint + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "TLS 1.3 + GM Echo: " + std::string(req.get_body())); + co_return; + }); + + std::cout << "One-way TLS 1.3 + GM server ready. Test endpoints:" << std::endl; + std::cout << " GET https://localhost:8803/" << std::endl; + std::cout << " POST https://localhost:8803/echo" << std::endl; + + auto result = server.sync_start(); + if (result) { + std::cerr << "Failed to start TLS 1.3 + GM one-way server: " << result.message() + << std::endl; + } +} + +// Start mutual authentication TLS 1.3 + GM server (single certificate) +void start_tls13_gm_mutual_auth_server() { + std::cout << "Starting TLS 1.3 + GM HTTP Server (mutual auth) on port 8804..." + << std::endl; + + coro_http_server server(1, 8804); + + // Initialize TLS 1.3 + GM server with single certificate (RFC 8998) + // Mutual authentication mode (client certificate verification enabled) + server.init_ntls("", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, + true, // true = verify client certificate + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3 GM cipher suites + coro_http_connection::ntls_mode::tls13_single_cert); + + // Root endpoint + server.set_http_handler( + "/", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello from TLS 1.3 + GM Server (Mutual Auth)"); + co_return; + }); + + // Echo endpoint + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "TLS 1.3 + GM Mutual Echo: " + std::string(req.get_body())); + co_return; + }); + + std::cout << "Mutual auth TLS 1.3 + GM server ready. Test endpoints:" << std::endl; + std::cout << " GET https://localhost:8804/ (requires client certificate)" + << std::endl; + std::cout + << " POST https://localhost:8804/echo (requires client certificate)" + << std::endl; + + auto result = server.sync_start(); + if (result) { + std::cerr << "Failed to start TLS 1.3 + GM mutual auth server: " << result.message() + << std::endl; + } +} + int main() { - std::cout << "NTLS HTTP Server Example - Starting two servers:" << std::endl; + std::cout << "NTLS HTTP Server Example - Starting four servers:" << std::endl; + std::cout << "TLCP Dual Certificate Mode:" << std::endl; std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + std::cout << "TLS 1.3 + GM Single Certificate Mode (RFC 8998):" << std::endl; + std::cout << "3. One-way authentication (8803) - Server certificate only" + << std::endl; + std::cout << "4. Mutual authentication (8804) - Requires client certificate" + << std::endl; - // Start both servers in separate threads - std::thread one_way_thread(start_one_way_server); - std::thread mutual_auth_thread(start_mutual_auth_server); - - // Wait for both servers - one_way_thread.join(); - mutual_auth_thread.join(); + // Start all servers in separate threads + std::thread tlcp_one_way_thread(start_one_way_server); + std::thread tlcp_mutual_auth_thread(start_mutual_auth_server); + std::thread tls13_one_way_thread(start_tls13_gm_one_way_server); + std::thread tls13_mutual_auth_thread(start_tls13_gm_mutual_auth_server); + + // Wait for all servers + tlcp_one_way_thread.join(); + tlcp_mutual_auth_thread.join(); + tls13_one_way_thread.join(); + tls13_mutual_auth_thread.join(); return 0; } \ No newline at end of file diff --git a/src/coro_rpc/examples/base_examples/ntls_client.cpp b/src/coro_rpc/examples/base_examples/ntls_client.cpp index dee5c20fb..d91102ef9 100644 --- a/src/coro_rpc/examples/base_examples/ntls_client.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_client.cpp @@ -37,6 +37,10 @@ const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; // SM2 client encryption private key const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +// Single certificate paths for RFC 8998 TLS 1.3 + GM mode +const std::string CLIENT_GM_CERT = CERT_PATH + "client_sign.crt"; // GM single certificate +const std::string CLIENT_GM_KEY = CERT_PATH + "client_sign.key"; // GM single private key + // RPC function declarations (should match server) std::string echo(std::string_view data); @@ -146,21 +150,144 @@ Lazy test_mutual_auth() { } } +// Test one-way authentication TLS 1.3 + GM client (single certificate) +Lazy test_tls13_gm_one_way_auth() { + std::cout << "\n=== TLS 1.3 + GM One-way Authentication Client (8803) ===" + << std::endl; + + coro_rpc_client client; + + // Initialize TLS 1.3 + GM client for one-way authentication + // In one-way mode, we only verify server's certificate + ssl_ntls_configure config; + config.base_path = CERT_PATH; + config.ca_cert_file = CA_CERT; + config.server_name = "localhost"; + config.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode + config.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + // No client certificate needed for one-way auth + + bool ok = client.init_ntls(config); + + if (!ok) { + std::cout << "Failed to initialize TLS 1.3 + GM one-way client" << std::endl; + co_return; + } + + std::cout << "TLS 1.3 + GM one-way client initialized successfully" << std::endl; + + // Connect to TLS 1.3 + GM one-way server + auto ec = co_await client.connect("127.0.0.1", "8803"); + if (ec) { + std::cout << "Failed to connect to TLS 1.3 + GM one-way server: " << ec.message() + << std::endl; + co_return; + } + + std::cout << "Connected to TLS 1.3 + GM one-way server successfully!" << std::endl; + + try { + // Test echo function + auto result = co_await client.call("TLS 1.3 + GM test message to one-way server"); + if (result) { + std::cout << "Echo result from TLS 1.3 + GM one-way server: " << result.value() + << std::endl; + } + else { + std::cout << "Echo failed: " << result.error().msg << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "RPC call error (TLS 1.3 + GM one-way): " << e.what() << std::endl; + } +} + +// Test mutual authentication TLS 1.3 + GM client (single certificate) +Lazy test_tls13_gm_mutual_auth() { + std::cout << "\n=== TLS 1.3 + GM Mutual Authentication Client (8804) ===" + << std::endl; + + coro_rpc_client client; + + // Initialize TLS 1.3 + GM client for mutual authentication + // In mutual mode, both client and server certificates are verified + ssl_ntls_configure config; + config.base_path = CERT_PATH; + config.gm_cert_file = CLIENT_GM_CERT; // Client GM single certificate + config.gm_key_file = CLIENT_GM_KEY; // Client GM single private key + config.ca_cert_file = CA_CERT; + config.server_name = "localhost"; + config.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode + config.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + + bool ok = client.init_ntls(config); + + if (!ok) { + std::cout << "Failed to initialize TLS 1.3 + GM mutual auth client" << std::endl; + co_return; + } + + std::cout << "TLS 1.3 + GM mutual auth client initialized successfully" << std::endl; + + // Connect to TLS 1.3 + GM mutual auth server + auto ec = co_await client.connect("127.0.0.1", "8804"); + if (ec) { + std::cout << "Failed to connect to TLS 1.3 + GM mutual auth server: " << ec.message() + << std::endl; + co_return; + } + + std::cout << "Connected to TLS 1.3 + GM mutual auth server successfully!" + << std::endl; + + try { + // Test echo function + auto result = + co_await client.call("TLS 1.3 + GM test message to mutual auth server"); + if (result) { + std::cout << "Echo result from TLS 1.3 + GM mutual auth server: " << result.value() + << std::endl; + } + else { + std::cout << "Echo failed: " << result.error().msg << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "RPC call error (TLS 1.3 + GM mutual auth): " << e.what() << std::endl; + } +} + int main() { try { - std::cout << "NTLS RPC Client Example - Testing two servers:" << std::endl; + std::cout << "NTLS RPC Client Example - Testing four servers:" << std::endl; + std::cout << "TLCP Dual Certificate Mode:" << std::endl; std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + std::cout << "TLS 1.3 + GM Single Certificate Mode (RFC 8998):" << std::endl; + std::cout << "3. One-way authentication (8803) - Server certificate only" + << std::endl; + std::cout << "4. Mutual authentication (8804) - Requires client certificate" + << std::endl; - // Test one-way authentication client + std::cout << "\n=== TLCP Dual Certificate Mode Tests ===" << std::endl; + + // Test TLCP one-way authentication client syncAwait(test_one_way_auth()); - // Test mutual authentication client + // Test TLCP mutual authentication client syncAwait(test_mutual_auth()); - std::cout << "\nNTLS RPC Client tests completed." << std::endl; + std::cout << "\n=== TLS 1.3 + GM Single Certificate Mode Tests ===" << std::endl; + + // Test TLS 1.3 + GM one-way authentication client + syncAwait(test_tls13_gm_one_way_auth()); + + // Test TLS 1.3 + GM mutual authentication client + syncAwait(test_tls13_gm_mutual_auth()); + + std::cout << "NTLS RPC Client tests completed." << std::endl; } catch (const std::exception& e) { std::cout << "Client error: " << e.what() << std::endl; diff --git a/src/coro_rpc/examples/base_examples/ntls_server.cpp b/src/coro_rpc/examples/base_examples/ntls_server.cpp index 0003bfec4..84f5e68d0 100644 --- a/src/coro_rpc/examples/base_examples/ntls_server.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_server.cpp @@ -34,6 +34,10 @@ const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; // SM2 server encryption private key const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate +// Single certificate paths for RFC 8998 TLS 1.3 + GM mode +const std::string SERVER_GM_CERT = CERT_PATH + "server_sign.crt"; // GM single certificate +const std::string SERVER_GM_KEY = CERT_PATH + "server_sign.key"; // GM single private key + // Simple RPC service function std::string echo(std::string_view data) { std::cout << "Server received: " << data << std::endl; @@ -128,20 +132,118 @@ void start_mutual_auth_server() { } } + +// Start one-way authentication TLS 1.3 + GM server (single certificate) +void start_tls13_gm_one_way_server() { + try { + // Create RPC server for TLS 1.3 + GM one-way authentication + coro_rpc_server server(std::thread::hardware_concurrency(), 8803); + + // Configure TLS 1.3 + GM with single certificate (RFC 8998) + ssl_ntls_configure ntls_conf; + ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode + ntls_conf.gm_cert_file = SERVER_GM_CERT; // GM single certificate + ntls_conf.gm_key_file = SERVER_GM_KEY; // GM single private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = + false; // Disable client certificate verification + ntls_conf.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + + // Initialize TLS 1.3 + GM + server.init_ntls(ntls_conf); + + // Register RPC service function + server.register_handler(); + + std::cout << "TLS 1.3 + GM RPC Server (one-way auth) starting on port 8803..." + << std::endl; + std::cout << "Using RFC 8998 TLS 1.3 + GM single certificate mode" << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + + // Start server (blocking) + auto result = server.start(); + if (result) { + std::cout << "TLS 1.3 + GM one-way auth server started successfully!" << std::endl; + } + else { + std::cout << "Failed to start TLS 1.3 + GM one-way auth server: " << result.message() + << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "TLS 1.3 + GM one-way auth server error: " << e.what() << std::endl; + } +} + +// Start mutual authentication TLS 1.3 + GM server (single certificate) +void start_tls13_gm_mutual_auth_server() { + try { + // Create RPC server for TLS 1.3 + GM mutual authentication + coro_rpc_server server(std::thread::hardware_concurrency(), 8804); + + // Configure TLS 1.3 + GM with single certificate (RFC 8998) + ssl_ntls_configure ntls_conf; + ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode + ntls_conf.gm_cert_file = SERVER_GM_CERT; // GM single certificate + ntls_conf.gm_key_file = SERVER_GM_KEY; // GM single private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = true; // Enable client certificate verification + ntls_conf.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + + // Initialize TLS 1.3 + GM + server.init_ntls(ntls_conf); + + // Register RPC service function + server.register_handler(); + + std::cout + << "TLS 1.3 + GM RPC Server (mutual auth) starting on port 8804..." + << std::endl; + std::cout << "Using RFC 8998 TLS 1.3 + GM single certificate mode" + << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + + // Start server (blocking) + auto result = server.start(); + if (result) { + std::cout << "TLS 1.3 + GM mutual auth server started successfully!" << std::endl; + } + else { + std::cout << "Failed to start TLS 1.3 + GM mutual auth server: " << result.message() + << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "TLS 1.3 + GM mutual auth server error: " << e.what() << std::endl; + } +} + int main() { - std::cout << "NTLS RPC Server Example - Starting two servers:" << std::endl; + std::cout << "NTLS RPC Server Example - Starting four servers:" << std::endl; + std::cout << "TLCP Dual Certificate Mode:" << std::endl; std::cout << "1. One-way authentication (8801) - Server certificate only" << std::endl; std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; + std::cout << "TLS 1.3 + GM Single Certificate Mode (RFC 8998):" << std::endl; + std::cout << "3. One-way authentication (8803) - Server certificate only" + << std::endl; + std::cout << "4. Mutual authentication (8804) - Requires client certificate" + << std::endl; - // Start both servers in separate threads - std::thread one_way_thread(start_one_way_server); - std::thread mutual_auth_thread(start_mutual_auth_server); - - // Wait for both servers - one_way_thread.join(); - mutual_auth_thread.join(); + // Start all servers in separate threads + std::thread tlcp_one_way_thread(start_one_way_server); + std::thread tlcp_mutual_auth_thread(start_mutual_auth_server); + std::thread tls13_one_way_thread(start_tls13_gm_one_way_server); + std::thread tls13_mutual_auth_thread(start_tls13_gm_mutual_auth_server); + + // Wait for all servers + tlcp_one_way_thread.join(); + tlcp_mutual_auth_thread.join(); + tls13_one_way_thread.join(); + tls13_mutual_auth_thread.join(); return 0; } \ No newline at end of file From 3121bc00d0ea27445510c77914c332ec0006c10c Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 17 Sep 2025 22:24:27 +0800 Subject: [PATCH 07/38] style: Clang Format --- include/ylt/coro_rpc/impl/common_service.hpp | 57 +++--- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 186 +++++++++++------- .../standalone/cinatra/coro_http_client.hpp | 15 +- .../cinatra/coro_http_connection.hpp | 72 ++++--- .../standalone/cinatra/coro_http_server.hpp | 53 +++-- src/coro_http/examples/ntls_http_client.cpp | 50 +++-- src/coro_http/examples/ntls_http_server.cpp | 57 +++--- .../examples/base_examples/ntls_client.cpp | 66 ++++--- .../examples/base_examples/ntls_server.cpp | 58 +++--- 9 files changed, 366 insertions(+), 248 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index 2826d17e9..6d788cf62 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -57,8 +57,9 @@ struct ssl_configure { * NTLS mode enumeration */ enum class ntls_mode { - tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + encryption) - tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate + tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + + //!< encryption) + tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate }; #endif @@ -82,11 +83,13 @@ struct ssl_ntls_configure { enc_cert_file; //!< relative path of SM2 encryption certificate file std::string enc_key_file; //!< relative path of SM2 encryption private key file - + // TLS 1.3 + GM single certificate configuration (RFC 8998) - std::string gm_cert_file; //!< relative path of single SM2 certificate file (for TLS 1.3 + GM) - std::string gm_key_file; //!< relative path of single SM2 private key file (for TLS 1.3 + GM) - + std::string gm_cert_file; //!< relative path of single SM2 certificate file + //!< (for TLS 1.3 + GM) + std::string gm_key_file; //!< relative path of single SM2 private key file + //!< (for TLS 1.3 + GM) + // Common NTLS configuration std::string ca_cert_file; //!< relative path of CA certificate file (optional) @@ -211,7 +214,7 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, // RFC 8998 TLS 1.3 + GM single certificate mode ELOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; - + // Set TLS 1.3 version if (SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) != 1) { ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; @@ -228,7 +231,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, : conf.cipher_suites; if (SSL_CTX_set_ciphersuites(ctx, cipher_suites.c_str()) != 1) { unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites '" << cipher_suites + ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites '" + << cipher_suites << "': " << ::ERR_error_string(err, nullptr); return false; } @@ -249,18 +253,19 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, << ::ERR_error_string(err, nullptr); return false; } - } else { + } + else { // Enable NTLS mode for Tongsuo SSL_CTX_enable_ntls(ctx); ELOG_INFO << "NTLS mode enabled successfully"; // GB/T 38636-2020 TLCP dual certificate mode (default) ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; - + // Set TLCP cipher suites (SM2/SM3/SM4) - std::string cipher_suites = conf.cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : conf.cipher_suites; + std::string cipher_suites = + conf.cipher_suites.empty() ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : conf.cipher_suites; if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { unsigned long err = ::ERR_get_error(); ELOG_WARN << "Failed to set TLCP cipher suites '" << cipher_suites @@ -315,9 +320,11 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, ELOG_ERROR << "no GM private key file " << gm_key_file.string(); return false; } - } else { + } + else { // GB/T 38636-2020 TLCP dual certificate mode - auto sign_cert_file = fs::path(conf.base_path).append(conf.sign_cert_file); + auto sign_cert_file = + fs::path(conf.base_path).append(conf.sign_cert_file); auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); @@ -331,7 +338,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, << ::ERR_error_string(err, nullptr); return false; } - ELOG_INFO << "loaded SM2 signing certificate: " << sign_cert_file.string(); + ELOG_INFO << "loaded SM2 signing certificate: " + << sign_cert_file.string(); } else { ELOG_ERROR << "no SM2 signing certificate file " @@ -340,14 +348,15 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, } if (file_exists(sign_key_file)) { - if (SSL_CTX_use_sign_PrivateKey_file(ctx, sign_key_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_sign_PrivateKey_file( + ctx, sign_key_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); ELOG_ERROR << "failed to load SM2 signing private key: " << ::ERR_error_string(err, nullptr); return false; } - ELOG_INFO << "loaded SM2 signing private key: " << sign_key_file.string(); + ELOG_INFO << "loaded SM2 signing private key: " + << sign_key_file.string(); } else { ELOG_ERROR << "no SM2 signing key file " << sign_key_file.string(); @@ -356,14 +365,15 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, // Load SM2 encryption certificate and key if (file_exists(enc_cert_file)) { - if (SSL_CTX_use_enc_certificate_file(ctx, enc_cert_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_enc_certificate_file( + ctx, enc_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); ELOG_ERROR << "failed to load SM2 encryption certificate: " << ::ERR_error_string(err, nullptr); return false; } - ELOG_INFO << "loaded SM2 encryption certificate: " << enc_cert_file.string(); + ELOG_INFO << "loaded SM2 encryption certificate: " + << enc_cert_file.string(); } else { ELOG_ERROR << "no SM2 encryption certificate file " @@ -379,7 +389,8 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, << ::ERR_error_string(err, nullptr); return false; } - ELOG_INFO << "loaded SM2 encryption private key: " << enc_key_file.string(); + ELOG_INFO << "loaded SM2 encryption private key: " + << enc_key_file.string(); } else { ELOG_ERROR << "no SM2 encryption key file " << enc_key_file.string(); diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index 7c2337acf..b17bd22cb 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -189,17 +189,17 @@ class coro_rpc_client { struct tcp_with_ntls_config { bool enable_tcp_no_delay = true; std::filesystem::path base_path{}; - + // TLCP dual certificate configuration (GB/T 38636-2020) std::filesystem::path sign_cert_path{}; std::filesystem::path sign_key_path{}; std::filesystem::path enc_cert_path{}; std::filesystem::path enc_key_path{}; - + // TLS 1.3 + GM single certificate configuration (RFC 8998) std::filesystem::path gm_cert_path{}; std::filesystem::path gm_key_path{}; - + // Common configuration std::filesystem::path ca_cert_path{}; std::string ssl_domain{}; @@ -319,8 +319,6 @@ class coro_rpc_client { ssl_init_ret_ = false; ELOG_INFO << "init NTLS: " << config.ssl_domain; - - // Configure based on NTLS mode if (config.mode == ntls_mode::tls13_single_cert) { // Create SSL context with TLCP server method @@ -329,14 +327,17 @@ class coro_rpc_client { // Enable strict SM TLS 1.3 (Tongsuo) SSL_CTX_enable_sm_tls13_strict(ssl_ctx_.native_handle()); // RFC 8998 TLS 1.3 + GM single certificate mode - ELOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; - + ELOG_INFO + << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + // Set TLS 1.3 version - if (SSL_CTX_set_min_proto_version(ssl_ctx_.native_handle(), TLS1_3_VERSION) != 1) { + if (SSL_CTX_set_min_proto_version(ssl_ctx_.native_handle(), + TLS1_3_VERSION) != 1) { ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; return false; } - if (SSL_CTX_set_max_proto_version(ssl_ctx_.native_handle(), TLS1_3_VERSION) != 1) { + if (SSL_CTX_set_max_proto_version(ssl_ctx_.native_handle(), + TLS1_3_VERSION) != 1) { ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; return false; } @@ -345,18 +346,22 @@ class coro_rpc_client { std::string cipher_suites = config.cipher_suites.empty() ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" : config.cipher_suites; - if (SSL_CTX_set_ciphersuites(ssl_ctx_.native_handle(), cipher_suites.c_str()) != 1) { - ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites: " << cipher_suites; + if (SSL_CTX_set_ciphersuites(ssl_ctx_.native_handle(), + cipher_suites.c_str()) != 1) { + ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites: " + << cipher_suites; return false; } ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; //// Set supported curves for SM2 - //if (SSL_CTX_set1_curves_list(ssl_ctx_.native_handle(), "SM2:X25519:prime256v1") != 1) { + // if (SSL_CTX_set1_curves_list(ssl_ctx_.native_handle(), + // "SM2:X25519:prime256v1") != 1) { // ELOG_ERROR << "Failed to set SM2 curves"; // return false; //} - } else { + } + else { // Create SSL context with TLCP client method for NTLS ssl_ctx_ = asio::ssl::context(SSL_CTX_new(NTLS_client_method())); @@ -366,14 +371,18 @@ class coro_rpc_client { // GB/T 38636-2020 TLCP dual certificate mode (default) ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; - + // Set TLCP cipher suites for NTLS (SM2/SM3/SM4) - std::string cipher_suites = config.cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : config.cipher_suites; - if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), cipher_suites.c_str())) { - ELOG_WARN << "failed to set TLCP cipher suites: " << cipher_suites << ", using default"; - } else { + std::string cipher_suites = + config.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : config.cipher_suites; + if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), + cipher_suites.c_str())) { + ELOG_WARN << "failed to set TLCP cipher suites: " << cipher_suites + << ", using default"; + } + else { ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; } } @@ -382,9 +391,11 @@ class coro_rpc_client { // RFC 8998 TLS 1.3 + GM single certificate mode if (config.enable_client_verify) { ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; - if (config.gm_cert_path.empty() || !file_exists(config.gm_cert_path) || - config.gm_key_path.empty() || !file_exists(config.gm_key_path)) { - ELOG_ERROR << "client verify enabled, but GM cert or key file is missing"; + if (config.gm_cert_path.empty() || + !file_exists(config.gm_cert_path) || config.gm_key_path.empty() || + !file_exists(config.gm_key_path)) { + ELOG_ERROR + << "client verify enabled, but GM cert or key file is missing"; return false; } } @@ -392,9 +403,9 @@ class coro_rpc_client { // Load single GM certificate and key if (!config.gm_cert_path.empty() && file_exists(config.gm_cert_path)) { ELOG_INFO << "load GM certificate " << config.gm_cert_path.string(); - if (!SSL_CTX_use_certificate_file(ssl_ctx_.native_handle(), - config.gm_cert_path.string().c_str(), - SSL_FILETYPE_PEM)) { + if (!SSL_CTX_use_certificate_file( + ssl_ctx_.native_handle(), + config.gm_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { ELOG_ERROR << "failed to load GM certificate"; return false; } @@ -409,7 +420,8 @@ class coro_rpc_client { return false; } } - } else { + } + else { // GB/T 38636-2020 TLCP dual certificate mode if (config.enable_client_verify) { ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; @@ -418,7 +430,8 @@ class coro_rpc_client { config.sign_key_path.empty() || !file_exists(config.sign_key_path) || config.enc_cert_path.empty() || - !file_exists(config.enc_cert_path) || config.enc_key_path.empty() || + !file_exists(config.enc_cert_path) || + config.enc_key_path.empty() || !file_exists(config.enc_key_path)) { ELOG_ERROR << "client verify enabled, but cert or key file is missing"; @@ -429,7 +442,8 @@ class coro_rpc_client { // Load dual certificates if provided if (!config.sign_cert_path.empty() && file_exists(config.sign_cert_path)) { - ELOG_INFO << "load SM2 signing cert " << config.sign_cert_path.string(); + ELOG_INFO << "load SM2 signing cert " + << config.sign_cert_path.string(); if (!SSL_CTX_use_sign_certificate_file( ssl_ctx_.native_handle(), config.sign_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { @@ -449,16 +463,18 @@ class coro_rpc_client { } } - if (!config.enc_cert_path.empty() && file_exists(config.enc_cert_path)) { + if (!config.enc_cert_path.empty() && + file_exists(config.enc_cert_path)) { ELOG_INFO << "load SM2 encryption cert " << config.enc_cert_path.string(); if (!SSL_CTX_use_enc_certificate_file( - ssl_ctx_.native_handle(), config.enc_cert_path.string().c_str(), - SSL_FILETYPE_PEM)) { + ssl_ctx_.native_handle(), + config.enc_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { ELOG_ERROR << "failed to load SM2 encryption certificate"; return false; } - if (!config.enc_key_path.empty() && file_exists(config.enc_key_path)) { + if (!config.enc_key_path.empty() && + file_exists(config.enc_key_path)) { ELOG_INFO << "load SM2 encryption private key " << config.enc_key_path.string(); if (!SSL_CTX_use_enc_PrivateKey_file( @@ -627,57 +643,89 @@ class coro_rpc_client { if (conf.mode == ntls_mode::tls13_single_cert) { // RFC 8998 TLS 1.3 + GM single certificate mode if (config_.socket_config.index() != 2) { - config_.socket_config = - tcp_with_ntls_config{.enable_tcp_no_delay = true, - .base_path = conf.base_path, - .gm_cert_path = std::filesystem::path(conf.base_path).append(conf.gm_cert_file), - .gm_key_path = std::filesystem::path(conf.base_path).append(conf.gm_key_file), - .ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file), - .ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name, - .cipher_suites = conf.cipher_suites, - .mode = ntls_mode::tls13_single_cert, - .enable_client_verify = conf.enable_client_verify}; - } else { + config_.socket_config = tcp_with_ntls_config{ + .enable_tcp_no_delay = true, + .base_path = conf.base_path, + .gm_cert_path = + std::filesystem::path(conf.base_path).append(conf.gm_cert_file), + .gm_key_path = + std::filesystem::path(conf.base_path).append(conf.gm_key_file), + .ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file), + .ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name, + .cipher_suites = conf.cipher_suites, + .mode = ntls_mode::tls13_single_cert, + .enable_client_verify = conf.enable_client_verify}; + } + else { auto &config = std::get(config_.socket_config); config.base_path = conf.base_path; - config.gm_cert_path = std::filesystem::path(conf.base_path).append(conf.gm_cert_file); - config.gm_key_path = std::filesystem::path(conf.base_path).append(conf.gm_key_file); - config.ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file); - config.ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name; + config.gm_cert_path = + std::filesystem::path(conf.base_path).append(conf.gm_cert_file); + config.gm_key_path = + std::filesystem::path(conf.base_path).append(conf.gm_key_file); + config.ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file); + config.ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name; config.cipher_suites = conf.cipher_suites; config.mode = ntls_mode::tls13_single_cert; config.enable_client_verify = conf.enable_client_verify; } - } else { + } + else { // GB/T 38636-2020 TLCP dual certificate mode (default) if (config_.socket_config.index() != 2) { - config_.socket_config = - tcp_with_ntls_config{.enable_tcp_no_delay = true, - .base_path = conf.base_path, - .sign_cert_path = std::filesystem::path(conf.base_path).append(conf.sign_cert_file), - .sign_key_path = std::filesystem::path(conf.base_path).append(conf.sign_key_file), - .enc_cert_path = std::filesystem::path(conf.base_path).append(conf.enc_cert_file), - .enc_key_path = std::filesystem::path(conf.base_path).append(conf.enc_key_file), - .ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file), - .ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name, - .cipher_suites = conf.cipher_suites, - .mode = ntls_mode::tlcp_dual_cert, - .enable_client_verify = conf.enable_client_verify}; - } else { + config_.socket_config = tcp_with_ntls_config{ + .enable_tcp_no_delay = true, + .base_path = conf.base_path, + .sign_cert_path = std::filesystem::path(conf.base_path) + .append(conf.sign_cert_file), + .sign_key_path = std::filesystem::path(conf.base_path) + .append(conf.sign_key_file), + .enc_cert_path = std::filesystem::path(conf.base_path) + .append(conf.enc_cert_file), + .enc_key_path = + std::filesystem::path(conf.base_path).append(conf.enc_key_file), + .ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file), + .ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name, + .cipher_suites = conf.cipher_suites, + .mode = ntls_mode::tlcp_dual_cert, + .enable_client_verify = conf.enable_client_verify}; + } + else { auto &config = std::get(config_.socket_config); config.base_path = conf.base_path; - config.sign_cert_path = std::filesystem::path(conf.base_path).append(conf.sign_cert_file); - config.sign_key_path = std::filesystem::path(conf.base_path).append(conf.sign_key_file); - config.enc_cert_path = std::filesystem::path(conf.base_path).append(conf.enc_cert_file); - config.enc_key_path = std::filesystem::path(conf.base_path).append(conf.enc_key_file); - config.ca_cert_path = conf.ca_cert_file.empty() ? std::filesystem::path{} : std::filesystem::path(conf.base_path).append(conf.ca_cert_file); - config.ssl_domain = conf.server_name.empty() ? "localhost" : conf.server_name; + config.sign_cert_path = + std::filesystem::path(conf.base_path).append(conf.sign_cert_file); + config.sign_key_path = + std::filesystem::path(conf.base_path).append(conf.sign_key_file); + config.enc_cert_path = + std::filesystem::path(conf.base_path).append(conf.enc_cert_file); + config.enc_key_path = + std::filesystem::path(conf.base_path).append(conf.enc_key_file); + config.ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file); + config.ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name; config.cipher_suites = conf.cipher_suites; config.mode = ntls_mode::tlcp_dual_cert; config.enable_client_verify = conf.enable_client_verify; } } - return init_socket_wrapper(std::get(config_.socket_config)); + return init_socket_wrapper( + std::get(config_.socket_config)); } [[nodiscard]] bool init_ntls(std::string_view base_file, diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index db63e6f4a..71e753ffa 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -1585,12 +1585,12 @@ class coro_http_client : public std::enable_shared_from_this { /*! * Initialize NTLS client with TLS 1.3 + GM single certificate mode (RFC 8998) */ - bool init_ntls_tls13_gm_client(const std::string &gm_cert_file = "", - const std::string &gm_key_file = "", - const std::string &ca_cert_file = "", - int verify_mode = asio::ssl::verify_none, - const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", - const std::string &passwd = "") { + bool init_ntls_tls13_gm_client( + const std::string &gm_cert_file = "", const std::string &gm_key_file = "", + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none, + const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", + const std::string &passwd = "") { if (has_init_ssl_) { return true; } @@ -1607,7 +1607,8 @@ class coro_http_client : public std::enable_shared_from_this { SSL_CTX_enable_sm_tls13_strict(ssl_ctx_->native_handle()); // Set TLS 1.3 GM cipher suites - if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), cipher_suites.c_str())) { + if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), + cipher_suites.c_str())) { CINATRA_LOG_WARNING << "failed to set TLS 1.3 GM cipher suites"; } diff --git a/include/ylt/standalone/cinatra/coro_http_connection.hpp b/include/ylt/standalone/cinatra/coro_http_connection.hpp index 6c7fb7f08..d05a94a0f 100644 --- a/include/ylt/standalone/cinatra/coro_http_connection.hpp +++ b/include/ylt/standalone/cinatra/coro_http_connection.hpp @@ -103,8 +103,9 @@ class coro_http_connection * NTLS mode enumeration for HTTP connection */ enum class ntls_mode { - tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + encryption) - tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate + tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + + //!< encryption) + tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate }; /*! @@ -137,8 +138,9 @@ class coro_http_connection return false; } // RFC 8998 TLS 1.3 + GM single certificate mode - CINATRA_LOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; - + CINATRA_LOG_INFO + << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + // Enable strict SM TLS 1.3 (Tongsuo) SSL_CTX_enable_sm_tls13_strict(ctx); @@ -165,17 +167,19 @@ class coro_http_connection << "': " << ::ERR_error_string(err, nullptr); return false; } - CINATRA_LOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; + CINATRA_LOG_INFO << "TLS 1.3 GM cipher suites set to: " + << cipher_suites; //// Set supported curves for SM2 (strict mode requires only SM2) - //if (SSL_CTX_set1_curves_list(ctx, "SM2:X25519:prime256v1") != 1) { + // if (SSL_CTX_set1_curves_list(ctx, "SM2:X25519:prime256v1") != 1) { // unsigned long err = ::ERR_get_error(); // CINATRA_LOG_ERROR << "Failed to set SM2 curves: " // << ::ERR_error_string(err, nullptr); // return false; //} CINATRA_LOG_INFO << "SM2 curves configured for strict mode"; - } else { + } + else { // Create SSL context with TLCP server method ssl_ctx_ = std::make_unique(SSL_CTX_new(NTLS_method())); @@ -192,18 +196,21 @@ class coro_http_connection CINATRA_LOG_INFO << "NTLS mode enabled successfully"; // GB/T 38636-2020 TLCP dual certificate mode (default) - CINATRA_LOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; - + CINATRA_LOG_INFO + << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; + // Set TLCP cipher suites - std::string cipher_suites = ntls_config.cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : ntls_config.cipher_suites; + std::string cipher_suites = + ntls_config.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : ntls_config.cipher_suites; if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { unsigned long err = ::ERR_get_error(); CINATRA_LOG_WARNING << "Failed to set TLCP cipher suites '" << cipher_suites << "': " << ::ERR_error_string(err, nullptr); - } else { + } + else { CINATRA_LOG_INFO << "TLCP cipher suites set to: " << cipher_suites; } } @@ -237,12 +244,13 @@ class coro_http_connection if (SSL_CTX_use_certificate_file(ctx, gm_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); - CINATRA_LOG_ERROR << "failed to load GM certificate: " - << gm_cert_path.string() - << ", err: " << ::ERR_error_string(err, nullptr); + CINATRA_LOG_ERROR + << "failed to load GM certificate: " << gm_cert_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); return false; } - CINATRA_LOG_INFO << "loaded GM certificate: " << gm_cert_path.string(); + CINATRA_LOG_INFO << "loaded GM certificate: " + << gm_cert_path.string(); } else { CINATRA_LOG_ERROR << "GM certificate file not found: " @@ -255,9 +263,9 @@ class coro_http_connection if (SSL_CTX_use_PrivateKey_file(ctx, gm_key_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); - CINATRA_LOG_ERROR << "failed to load GM private key: " - << gm_key_path.string() - << ", err: " << ::ERR_error_string(err, nullptr); + CINATRA_LOG_ERROR + << "failed to load GM private key: " << gm_key_path.string() + << ", err: " << ::ERR_error_string(err, nullptr); return false; } CINATRA_LOG_INFO << "loaded GM private key: " << gm_key_path.string(); @@ -267,7 +275,8 @@ class coro_http_connection << gm_key_path.string(); return false; } - } else { + } + else { // GB/T 38636-2020 TLCP dual certificate mode auto sign_cert_path = ntls_config.base_path.empty() ? ntls_config.sign_cert_file @@ -288,15 +297,17 @@ class coro_http_connection // Load SM2 signing certificate and key if (fs::exists(sign_cert_path, file_ec)) { - if (SSL_CTX_use_sign_certificate_file( - ctx, sign_cert_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_sign_certificate_file(ctx, + sign_cert_path.string().c_str(), + SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); CINATRA_LOG_ERROR << "failed to load SM2 signing certificate: " << sign_cert_path.string() << ", err: " << ::ERR_error_string(err, nullptr); return false; } - CINATRA_LOG_INFO << "loaded SM2 signing certificate: " << sign_cert_path.string(); + CINATRA_LOG_INFO << "loaded SM2 signing certificate: " + << sign_cert_path.string(); } else { CINATRA_LOG_ERROR << "SM2 signing certificate file not found: " @@ -313,7 +324,8 @@ class coro_http_connection << ", err: " << ::ERR_error_string(err, nullptr); return false; } - CINATRA_LOG_INFO << "loaded SM2 signing private key: " << sign_key_path.string(); + CINATRA_LOG_INFO << "loaded SM2 signing private key: " + << sign_key_path.string(); } else { CINATRA_LOG_ERROR << "SM2 signing key file not found: " @@ -331,7 +343,8 @@ class coro_http_connection << ", err: " << ::ERR_error_string(err, nullptr); return false; } - CINATRA_LOG_INFO << "loaded SM2 encryption certificate: " << enc_cert_path.string(); + CINATRA_LOG_INFO << "loaded SM2 encryption certificate: " + << enc_cert_path.string(); } else { CINATRA_LOG_ERROR << "SM2 encryption certificate file not found: " @@ -340,15 +353,16 @@ class coro_http_connection } if (fs::exists(enc_key_path, file_ec)) { - if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_path.string().c_str(), - SSL_FILETYPE_PEM) != 1) { + if (SSL_CTX_use_enc_PrivateKey_file( + ctx, enc_key_path.string().c_str(), SSL_FILETYPE_PEM) != 1) { unsigned long err = ::ERR_get_error(); CINATRA_LOG_ERROR << "failed to load SM2 encryption private key: " << enc_key_path.string() << ", err: " << ::ERR_error_string(err, nullptr); return false; } - CINATRA_LOG_INFO << "loaded SM2 encryption private key: " << enc_key_path.string(); + CINATRA_LOG_INFO << "loaded SM2 encryption private key: " + << enc_key_path.string(); } else { CINATRA_LOG_ERROR << "SM2 encryption key file not found: " diff --git a/include/ylt/standalone/cinatra/coro_http_server.hpp b/include/ylt/standalone/cinatra/coro_http_server.hpp index fb51b2400..5b0276c67 100644 --- a/include/ylt/standalone/cinatra/coro_http_server.hpp +++ b/include/ylt/standalone/cinatra/coro_http_server.hpp @@ -108,36 +108,35 @@ class coro_http_server { /*! * Initialize NTLS with base path and relative file paths */ - void init_ntls(const std::string &base_path, - const std::string &sign_cert_file, - const std::string &sign_key_file, - const std::string &enc_cert_file, - const std::string &enc_key_file, - const std::string &ca_cert_file = "", - bool enable_client_verify = false, - const std::string &cipher_suites = "", - coro_http_connection::ntls_mode mode = coro_http_connection::ntls_mode::tlcp_dual_cert) { + void init_ntls( + const std::string &base_path, const std::string &sign_cert_file, + const std::string &sign_key_file, const std::string &enc_cert_file, + const std::string &enc_key_file, const std::string &ca_cert_file = "", + bool enable_client_verify = false, const std::string &cipher_suites = "", + coro_http_connection::ntls_mode mode = + coro_http_connection::ntls_mode::tlcp_dual_cert) { ntls_config_.base_path = base_path; ntls_config_.mode = mode; - + if (mode == coro_http_connection::ntls_mode::tls13_single_cert) { // RFC 8998 TLS 1.3 + GM single certificate mode ntls_config_.gm_cert_file = sign_cert_file; ntls_config_.gm_key_file = sign_key_file; - ntls_config_.cipher_suites = cipher_suites.empty() - ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" - : cipher_suites; - } else { + ntls_config_.cipher_suites = cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : cipher_suites; + } + else { // GB/T 38636-2020 TLCP dual certificate mode (default) ntls_config_.sign_cert_file = sign_cert_file; ntls_config_.sign_key_file = sign_key_file; ntls_config_.enc_cert_file = enc_cert_file; ntls_config_.enc_key_file = enc_key_file; - ntls_config_.cipher_suites = cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : cipher_suites; + ntls_config_.cipher_suites = + cipher_suites.empty() ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : cipher_suites; } - + ntls_config_.ca_cert_file = ca_cert_file; ntls_config_.enable_client_verify = enable_client_verify; ntls_config_.enable_ntls = true; @@ -147,8 +146,7 @@ class coro_http_server { /*! * Initialize NTLS with RFC 8998 TLS 1.3 + GM single certificate mode */ - void init_ntls(const std::string &base_path, - const std::string &gm_cert_file, + void init_ntls(const std::string &base_path, const std::string &gm_cert_file, const std::string &gm_key_file, const std::string &ca_cert_file = "", bool enable_client_verify = false, @@ -158,9 +156,9 @@ class coro_http_server { ntls_config_.gm_cert_file = gm_cert_file; ntls_config_.gm_key_file = gm_key_file; ntls_config_.ca_cert_file = ca_cert_file; - ntls_config_.cipher_suites = cipher_suites.empty() - ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" - : cipher_suites; + ntls_config_.cipher_suites = cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : cipher_suites; ntls_config_.enable_client_verify = enable_client_verify; ntls_config_.enable_ntls = true; use_ntls_ = true; @@ -1172,21 +1170,22 @@ class coro_http_server { // NTLS configuration struct { std::string base_path; - + // TLCP dual certificate configuration (GB/T 38636-2020) std::string sign_cert_file; std::string sign_key_file; std::string enc_cert_file; std::string enc_key_file; - + // TLS 1.3 + GM single certificate configuration (RFC 8998) std::string gm_cert_file; std::string gm_key_file; - + // Common configuration std::string ca_cert_file; std::string cipher_suites = "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"; - coro_http_connection::ntls_mode mode = coro_http_connection::ntls_mode::tlcp_dual_cert; + coro_http_connection::ntls_mode mode = + coro_http_connection::ntls_mode::tlcp_dual_cert; bool enable_client_verify = false; bool enable_ntls = false; } ntls_config_; diff --git a/src/coro_http/examples/ntls_http_client.cpp b/src/coro_http/examples/ntls_http_client.cpp index 05e43842d..2b2355e70 100644 --- a/src/coro_http/examples/ntls_http_client.cpp +++ b/src/coro_http/examples/ntls_http_client.cpp @@ -34,8 +34,10 @@ const std::string CLIENT_ENC_KEY = const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Single certificate paths for RFC 8998 TLS 1.3 + GM mode -const std::string CLIENT_GM_CERT = CERT_PATH + "client_sign.crt"; // GM single certificate -const std::string CLIENT_GM_KEY = CERT_PATH + "client_sign.key"; // GM single private key +const std::string CLIENT_GM_CERT = + CERT_PATH + "client_sign.crt"; // GM single certificate +const std::string CLIENT_GM_KEY = + CERT_PATH + "client_sign.key"; // GM single private key // Test one-way authentication NTLS client (server certificate only) async_simple::coro::Lazy test_ntls_http_client_one_way() { @@ -138,19 +140,21 @@ async_simple::coro::Lazy test_tls13_gm_client_one_way() { // Initialize TLS 1.3 + GM client for one-way authentication // Use empty certificates for one-way mode, but specify TLS 1.3 + GM mode bool init_ok = client.init_ntls_tls13_gm_client( - "", // No client certificate needed for one-way - "", // No client key needed for one-way - CA_CERT, // CA certificate to verify server - asio::ssl::verify_peer, // Verify server certificate + "", // No client certificate needed for one-way + "", // No client key needed for one-way + CA_CERT, // CA certificate to verify server + asio::ssl::verify_peer, // Verify server certificate "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" // TLS 1.3 GM cipher suites ); if (!init_ok) { - std::cout << "Failed to initialize TLS 1.3 + GM one-way client" << std::endl; + std::cout << "Failed to initialize TLS 1.3 + GM one-way client" + << std::endl; co_return; } - std::cout << "TLS 1.3 + GM HTTP Client initialized (one-way auth mode)" << std::endl; + std::cout << "TLS 1.3 + GM HTTP Client initialized (one-way auth mode)" + << std::endl; // Send GET request to root endpoint auto response = co_await client.async_get("https://localhost:8803/"); @@ -164,8 +168,9 @@ async_simple::coro::Lazy test_tls13_gm_client_one_way() { } // Send POST request to echo endpoint - auto post_response = co_await client.async_post( - "https://localhost:8803/echo", "TLS 1.3 + GM test message", req_content_type::text); + auto post_response = co_await client.async_post("https://localhost:8803/echo", + "TLS 1.3 + GM test message", + req_content_type::text); if (post_response.net_err) { std::cout << "POST /echo request failed: " << post_response.net_err.message() << std::endl; @@ -185,23 +190,26 @@ async_simple::coro::Lazy test_tls13_gm_client_mutual_auth() { coro_http_client client{}; - // Initialize TLS 1.3 + GM client with mutual authentication (single certificate) + // Initialize TLS 1.3 + GM client with mutual authentication (single + // certificate) bool init_ok = client.init_ntls_tls13_gm_client( - CLIENT_GM_CERT, // Client GM single certificate - CLIENT_GM_KEY, // Client GM single private key - CA_CERT, // CA certificate - asio::ssl::verify_peer, // Verify server certificate + CLIENT_GM_CERT, // Client GM single certificate + CLIENT_GM_KEY, // Client GM single private key + CA_CERT, // CA certificate + asio::ssl::verify_peer, // Verify server certificate "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" // TLS 1.3 GM cipher suites ); if (!init_ok) { - std::cout << "Failed to initialize TLS 1.3 + GM client with mutual authentication" - << std::endl; + std::cout + << "Failed to initialize TLS 1.3 + GM client with mutual authentication" + << std::endl; co_return; } - std::cout << "TLS 1.3 + GM HTTP Client initialized (mutual authentication mode)" - << std::endl; + std::cout + << "TLS 1.3 + GM HTTP Client initialized (mutual authentication mode)" + << std::endl; // Send GET request to root endpoint (requires client certificate) auto response = co_await client.async_get("https://localhost:8804/"); @@ -236,7 +244,7 @@ int main() { try { std::cout << "TLCP Dual Certificate Mode Tests" << std::endl; - + // Test TLCP one-way authentication client (port 8801) async_simple::coro::syncAwait(test_ntls_http_client_one_way()); @@ -244,7 +252,7 @@ int main() { async_simple::coro::syncAwait(test_ntls_http_client_mutual_auth()); std::cout << "TLS 1.3 + GM Single Certificate Mode Tests" << std::endl; - + // Test TLS 1.3 + GM one-way authentication client (port 8803) async_simple::coro::syncAwait(test_tls13_gm_client_one_way()); diff --git a/src/coro_http/examples/ntls_http_server.cpp b/src/coro_http/examples/ntls_http_server.cpp index 3273490c1..9d225aad2 100644 --- a/src/coro_http/examples/ntls_http_server.cpp +++ b/src/coro_http/examples/ntls_http_server.cpp @@ -34,8 +34,10 @@ const std::string SERVER_ENC_KEY = const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Single certificate paths for RFC 8998 TLS 1.3 + GM mode -const std::string SERVER_GM_CERT = CERT_PATH + "server_sign.crt"; // GM single certificate -const std::string SERVER_GM_KEY = CERT_PATH + "server_sign.key"; // GM single private key +const std::string SERVER_GM_CERT = + CERT_PATH + "server_sign.crt"; // GM single certificate +const std::string SERVER_GM_KEY = + CERT_PATH + "server_sign.key"; // GM single private key // Start one-way authentication NTLS server (server certificate only) void start_one_way_server() { @@ -132,24 +134,27 @@ void start_mutual_auth_server() { // Start one-way authentication TLS 1.3 + GM server (single certificate) void start_tls13_gm_one_way_server() { - std::cout << "Starting TLS 1.3 + GM HTTP Server (one-way auth) on port 8803..." - << std::endl; + std::cout + << "Starting TLS 1.3 + GM HTTP Server (one-way auth) on port 8803..." + << std::endl; coro_http_server server(1, 8803); // Initialize TLS 1.3 + GM server with single certificate (RFC 8998) // One-way authentication mode (no client certificate verification) - server.init_ntls("", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, - false, // false = don't verify client certificate - "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3 GM cipher suites - coro_http_connection::ntls_mode::tls13_single_cert); + server.init_ntls( + "", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, + false, // false = don't verify client certificate + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3 GM cipher suites + coro_http_connection::ntls_mode::tls13_single_cert); // Root endpoint server.set_http_handler( "/", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "Hello from TLS 1.3 + GM Server"); + resp.set_status_and_content(status_type::ok, + "Hello from TLS 1.3 + GM Server"); co_return; }); @@ -158,18 +163,21 @@ void start_tls13_gm_one_way_server() { "/echo", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "TLS 1.3 + GM Echo: " + std::string(req.get_body())); + resp.set_status_and_content( + status_type::ok, + "TLS 1.3 + GM Echo: " + std::string(req.get_body())); co_return; }); - std::cout << "One-way TLS 1.3 + GM server ready. Test endpoints:" << std::endl; + std::cout << "One-way TLS 1.3 + GM server ready. Test endpoints:" + << std::endl; std::cout << " GET https://localhost:8803/" << std::endl; std::cout << " POST https://localhost:8803/echo" << std::endl; auto result = server.sync_start(); if (result) { - std::cerr << "Failed to start TLS 1.3 + GM one-way server: " << result.message() - << std::endl; + std::cerr << "Failed to start TLS 1.3 + GM one-way server: " + << result.message() << std::endl; } } @@ -182,17 +190,19 @@ void start_tls13_gm_mutual_auth_server() { // Initialize TLS 1.3 + GM server with single certificate (RFC 8998) // Mutual authentication mode (client certificate verification enabled) - server.init_ntls("", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, - true, // true = verify client certificate - "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3 GM cipher suites - coro_http_connection::ntls_mode::tls13_single_cert); + server.init_ntls( + "", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, + true, // true = verify client certificate + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3 GM cipher suites + coro_http_connection::ntls_mode::tls13_single_cert); // Root endpoint server.set_http_handler( "/", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "Hello from TLS 1.3 + GM Server (Mutual Auth)"); + resp.set_status_and_content( + status_type::ok, "Hello from TLS 1.3 + GM Server (Mutual Auth)"); co_return; }); @@ -201,11 +211,14 @@ void start_tls13_gm_mutual_auth_server() { "/echo", [](coro_http_request& req, coro_http_response& resp) -> async_simple::coro::Lazy { - resp.set_status_and_content(status_type::ok, "TLS 1.3 + GM Mutual Echo: " + std::string(req.get_body())); + resp.set_status_and_content( + status_type::ok, + "TLS 1.3 + GM Mutual Echo: " + std::string(req.get_body())); co_return; }); - std::cout << "Mutual auth TLS 1.3 + GM server ready. Test endpoints:" << std::endl; + std::cout << "Mutual auth TLS 1.3 + GM server ready. Test endpoints:" + << std::endl; std::cout << " GET https://localhost:8804/ (requires client certificate)" << std::endl; std::cout @@ -214,8 +227,8 @@ void start_tls13_gm_mutual_auth_server() { auto result = server.sync_start(); if (result) { - std::cerr << "Failed to start TLS 1.3 + GM mutual auth server: " << result.message() - << std::endl; + std::cerr << "Failed to start TLS 1.3 + GM mutual auth server: " + << result.message() << std::endl; } } diff --git a/src/coro_rpc/examples/base_examples/ntls_client.cpp b/src/coro_rpc/examples/base_examples/ntls_client.cpp index d91102ef9..afc4267ce 100644 --- a/src/coro_rpc/examples/base_examples/ntls_client.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_client.cpp @@ -38,8 +38,10 @@ const std::string CLIENT_ENC_KEY = const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Single certificate paths for RFC 8998 TLS 1.3 + GM mode -const std::string CLIENT_GM_CERT = CERT_PATH + "client_sign.crt"; // GM single certificate -const std::string CLIENT_GM_KEY = CERT_PATH + "client_sign.key"; // GM single private key +const std::string CLIENT_GM_CERT = + CERT_PATH + "client_sign.crt"; // GM single certificate +const std::string CLIENT_GM_KEY = + CERT_PATH + "client_sign.key"; // GM single private key // RPC function declarations (should match server) std::string echo(std::string_view data); @@ -164,41 +166,47 @@ Lazy test_tls13_gm_one_way_auth() { config.ca_cert_file = CA_CERT; config.server_name = "localhost"; config.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode - config.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + config.cipher_suites = + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites // No client certificate needed for one-way auth bool ok = client.init_ntls(config); if (!ok) { - std::cout << "Failed to initialize TLS 1.3 + GM one-way client" << std::endl; + std::cout << "Failed to initialize TLS 1.3 + GM one-way client" + << std::endl; co_return; } - std::cout << "TLS 1.3 + GM one-way client initialized successfully" << std::endl; + std::cout << "TLS 1.3 + GM one-way client initialized successfully" + << std::endl; // Connect to TLS 1.3 + GM one-way server auto ec = co_await client.connect("127.0.0.1", "8803"); if (ec) { - std::cout << "Failed to connect to TLS 1.3 + GM one-way server: " << ec.message() - << std::endl; + std::cout << "Failed to connect to TLS 1.3 + GM one-way server: " + << ec.message() << std::endl; co_return; } - std::cout << "Connected to TLS 1.3 + GM one-way server successfully!" << std::endl; + std::cout << "Connected to TLS 1.3 + GM one-way server successfully!" + << std::endl; try { // Test echo function - auto result = co_await client.call("TLS 1.3 + GM test message to one-way server"); + auto result = co_await client.call( + "TLS 1.3 + GM test message to one-way server"); if (result) { - std::cout << "Echo result from TLS 1.3 + GM one-way server: " << result.value() - << std::endl; + std::cout << "Echo result from TLS 1.3 + GM one-way server: " + << result.value() << std::endl; } else { std::cout << "Echo failed: " << result.error().msg << std::endl; } } catch (const std::exception& e) { - std::cout << "RPC call error (TLS 1.3 + GM one-way): " << e.what() << std::endl; + std::cout << "RPC call error (TLS 1.3 + GM one-way): " << e.what() + << std::endl; } } @@ -218,22 +226,25 @@ Lazy test_tls13_gm_mutual_auth() { config.ca_cert_file = CA_CERT; config.server_name = "localhost"; config.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode - config.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + config.cipher_suites = + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites bool ok = client.init_ntls(config); if (!ok) { - std::cout << "Failed to initialize TLS 1.3 + GM mutual auth client" << std::endl; + std::cout << "Failed to initialize TLS 1.3 + GM mutual auth client" + << std::endl; co_return; } - std::cout << "TLS 1.3 + GM mutual auth client initialized successfully" << std::endl; + std::cout << "TLS 1.3 + GM mutual auth client initialized successfully" + << std::endl; // Connect to TLS 1.3 + GM mutual auth server auto ec = co_await client.connect("127.0.0.1", "8804"); if (ec) { - std::cout << "Failed to connect to TLS 1.3 + GM mutual auth server: " << ec.message() - << std::endl; + std::cout << "Failed to connect to TLS 1.3 + GM mutual auth server: " + << ec.message() << std::endl; co_return; } @@ -242,18 +253,19 @@ Lazy test_tls13_gm_mutual_auth() { try { // Test echo function - auto result = - co_await client.call("TLS 1.3 + GM test message to mutual auth server"); + auto result = co_await client.call( + "TLS 1.3 + GM test message to mutual auth server"); if (result) { - std::cout << "Echo result from TLS 1.3 + GM mutual auth server: " << result.value() - << std::endl; + std::cout << "Echo result from TLS 1.3 + GM mutual auth server: " + << result.value() << std::endl; } else { std::cout << "Echo failed: " << result.error().msg << std::endl; } } catch (const std::exception& e) { - std::cout << "RPC call error (TLS 1.3 + GM mutual auth): " << e.what() << std::endl; + std::cout << "RPC call error (TLS 1.3 + GM mutual auth): " << e.what() + << std::endl; } } @@ -265,22 +277,24 @@ int main() { << std::endl; std::cout << "2. Mutual authentication (8802) - Requires client certificate" << std::endl; - std::cout << "TLS 1.3 + GM Single Certificate Mode (RFC 8998):" << std::endl; + std::cout << "TLS 1.3 + GM Single Certificate Mode (RFC 8998):" + << std::endl; std::cout << "3. One-way authentication (8803) - Server certificate only" << std::endl; std::cout << "4. Mutual authentication (8804) - Requires client certificate" << std::endl; std::cout << "\n=== TLCP Dual Certificate Mode Tests ===" << std::endl; - + // Test TLCP one-way authentication client syncAwait(test_one_way_auth()); // Test TLCP mutual authentication client syncAwait(test_mutual_auth()); - std::cout << "\n=== TLS 1.3 + GM Single Certificate Mode Tests ===" << std::endl; - + std::cout << "\n=== TLS 1.3 + GM Single Certificate Mode Tests ===" + << std::endl; + // Test TLS 1.3 + GM one-way authentication client syncAwait(test_tls13_gm_one_way_auth()); diff --git a/src/coro_rpc/examples/base_examples/ntls_server.cpp b/src/coro_rpc/examples/base_examples/ntls_server.cpp index 84f5e68d0..693d01900 100644 --- a/src/coro_rpc/examples/base_examples/ntls_server.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_server.cpp @@ -35,8 +35,10 @@ const std::string SERVER_ENC_KEY = const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; // CA certificate // Single certificate paths for RFC 8998 TLS 1.3 + GM mode -const std::string SERVER_GM_CERT = CERT_PATH + "server_sign.crt"; // GM single certificate -const std::string SERVER_GM_KEY = CERT_PATH + "server_sign.key"; // GM single private key +const std::string SERVER_GM_CERT = + CERT_PATH + "server_sign.crt"; // GM single certificate +const std::string SERVER_GM_KEY = + CERT_PATH + "server_sign.key"; // GM single private key // Simple RPC service function std::string echo(std::string_view data) { @@ -132,7 +134,6 @@ void start_mutual_auth_server() { } } - // Start one-way authentication TLS 1.3 + GM server (single certificate) void start_tls13_gm_one_way_server() { try { @@ -141,14 +142,15 @@ void start_tls13_gm_one_way_server() { // Configure TLS 1.3 + GM with single certificate (RFC 8998) ssl_ntls_configure ntls_conf; - ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.base_path = ""; // Using full paths instead ntls_conf.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode - ntls_conf.gm_cert_file = SERVER_GM_CERT; // GM single certificate - ntls_conf.gm_key_file = SERVER_GM_KEY; // GM single private key - ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.gm_cert_file = SERVER_GM_CERT; // GM single certificate + ntls_conf.gm_key_file = SERVER_GM_KEY; // GM single private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate ntls_conf.enable_client_verify = false; // Disable client certificate verification - ntls_conf.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + ntls_conf.cipher_suites = + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites // Initialize TLS 1.3 + GM server.init_ntls(ntls_conf); @@ -156,23 +158,27 @@ void start_tls13_gm_one_way_server() { // Register RPC service function server.register_handler(); - std::cout << "TLS 1.3 + GM RPC Server (one-way auth) starting on port 8803..." + std::cout + << "TLS 1.3 + GM RPC Server (one-way auth) starting on port 8803..." + << std::endl; + std::cout << "Using RFC 8998 TLS 1.3 + GM single certificate mode" << std::endl; - std::cout << "Using RFC 8998 TLS 1.3 + GM single certificate mode" << std::endl; std::cout << "Certificate path: " << CERT_PATH << std::endl; // Start server (blocking) auto result = server.start(); if (result) { - std::cout << "TLS 1.3 + GM one-way auth server started successfully!" << std::endl; + std::cout << "TLS 1.3 + GM one-way auth server started successfully!" + << std::endl; } else { - std::cout << "Failed to start TLS 1.3 + GM one-way auth server: " << result.message() - << std::endl; + std::cout << "Failed to start TLS 1.3 + GM one-way auth server: " + << result.message() << std::endl; } } catch (const std::exception& e) { - std::cout << "TLS 1.3 + GM one-way auth server error: " << e.what() << std::endl; + std::cout << "TLS 1.3 + GM one-way auth server error: " << e.what() + << std::endl; } } @@ -184,13 +190,15 @@ void start_tls13_gm_mutual_auth_server() { // Configure TLS 1.3 + GM with single certificate (RFC 8998) ssl_ntls_configure ntls_conf; - ntls_conf.base_path = ""; // Using full paths instead + ntls_conf.base_path = ""; // Using full paths instead ntls_conf.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + GM mode - ntls_conf.gm_cert_file = SERVER_GM_CERT; // GM single certificate - ntls_conf.gm_key_file = SERVER_GM_KEY; // GM single private key - ntls_conf.ca_cert_file = CA_CERT; // CA certificate - ntls_conf.enable_client_verify = true; // Enable client certificate verification - ntls_conf.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites + ntls_conf.gm_cert_file = SERVER_GM_CERT; // GM single certificate + ntls_conf.gm_key_file = SERVER_GM_KEY; // GM single private key + ntls_conf.ca_cert_file = CA_CERT; // CA certificate + ntls_conf.enable_client_verify = + true; // Enable client certificate verification + ntls_conf.cipher_suites = + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3 GM cipher suites // Initialize TLS 1.3 + GM server.init_ntls(ntls_conf); @@ -208,15 +216,17 @@ void start_tls13_gm_mutual_auth_server() { // Start server (blocking) auto result = server.start(); if (result) { - std::cout << "TLS 1.3 + GM mutual auth server started successfully!" << std::endl; + std::cout << "TLS 1.3 + GM mutual auth server started successfully!" + << std::endl; } else { - std::cout << "Failed to start TLS 1.3 + GM mutual auth server: " << result.message() - << std::endl; + std::cout << "Failed to start TLS 1.3 + GM mutual auth server: " + << result.message() << std::endl; } } catch (const std::exception& e) { - std::cout << "TLS 1.3 + GM mutual auth server error: " << e.what() << std::endl; + std::cout << "TLS 1.3 + GM mutual auth server error: " << e.what() + << std::endl; } } From 95912b591658dfb96e36c59556e8a5ea18949871 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Mon, 24 Nov 2025 14:29:50 +0800 Subject: [PATCH 08/38] Add NTLS CI testing workflow and CMake configuration --- .github/workflows/ntls_test.yml | 161 ++++++++++++++++++++++++++++++++ cmake/config.cmake | 36 +++++++ 2 files changed, 197 insertions(+) create mode 100644 .github/workflows/ntls_test.yml diff --git a/.github/workflows/ntls_test.yml b/.github/workflows/ntls_test.yml new file mode 100644 index 000000000..47ff7a4d4 --- /dev/null +++ b/.github/workflows/ntls_test.yml @@ -0,0 +1,161 @@ +name: NTLS Test (Tongsuo) + +on: + push: + branches: [ main, develop ] + paths: + - '**.hpp' + - '**.cpp' + - '**.h' + - 'cmake/**' + - 'src/certs/**' + - '.github/workflows/ntls_test.yml' + pull_request: + branches: [ main, develop ] + paths: + - '**.hpp' + - '**.cpp' + - '**.h' + - 'cmake/**' + - 'src/certs/**' + - '.github/workflows/ntls_test.yml' + workflow_call: + workflow_dispatch: + +env: + TONGSUO_VERSION: "8.4.2" + TONGSUO_INSTALL_PREFIX: /usr/local/tongsuo + +jobs: + ntls_test_ubuntu: + strategy: + matrix: + compiler: [gcc, clang] + mode: [Debug] + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + cmake \ + ninja-build \ + git \ + perl \ + zlib1g-dev \ + libbz2-dev + + - name: Install compiler + if: matrix.compiler == 'clang' + run: | + sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + sudo apt-get update + wget https://apt.llvm.org/llvm.sh + chmod +x ./llvm.sh + sudo ./llvm.sh 17 + + - name: Build and install Tongsuo + run: | + set -e + cd /tmp + git clone --depth 1 --branch ${TONGSUO_VERSION} https://github.com/Tongsuo-Project/Tongsuo.git tongsuo-src || \ + git clone --depth 1 https://github.com/Tongsuo-Project/Tongsuo.git tongsuo-src + cd tongsuo-src + + # Configure and build Tongsuo + ./config --prefix=${TONGSUO_INSTALL_PREFIX} \ + --openssldir=${TONGSUO_INSTALL_PREFIX}/ssl \ + shared \ + -fPIC \ + enable-ntls \ + enable-ec_sm2p_64_gcc_128 + + make -j$(nproc) + sudo make install + + # Update library paths + sudo ldconfig + + # Verify Tongsuo installation + ${TONGSUO_INSTALL_PREFIX}/bin/tongsuo version || \ + ${TONGSUO_INSTALL_PREFIX}/bin/openssl version + + - name: Setup environment for Tongsuo + run: | + echo "${TONGSUO_INSTALL_PREFIX}/bin" >> $GITHUB_PATH + echo "LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV + echo "PKG_CONFIG_PATH=${TONGSUO_INSTALL_PREFIX}/lib/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV + + # Create symlinks for cmake to find Tongsuo as OpenSSL + sudo mkdir -p /usr/local/lib/pkgconfig + sudo ln -sf ${TONGSUO_INSTALL_PREFIX}/lib/pkgconfig/*.pc /usr/local/lib/pkgconfig/ || true + + # Verify NTLS support + ${TONGSUO_INSTALL_PREFIX}/bin/tongsuo s_client -help 2>&1 | grep -i ntls || \ + ${TONGSUO_INSTALL_PREFIX}/bin/openssl s_client -help 2>&1 | grep -i ntls || true + + - name: Install ninja-build tool + uses: seanmiddleditch/gha-setup-ninja@master + + - name: Configure CMake with NTLS + run: | + if [ "${{ matrix.compiler }}" = "clang" ]; then + export CC=clang-17 + export CXX=clang++-17 + else + export CC=gcc + export CXX=g++ + fi + + # Set CMake variables to find Tongsuo + cmake -B ${{github.workspace}}/build -G Ninja \ + -DCMAKE_BUILD_TYPE=${{matrix.mode}} \ + -DYLT_ENABLE_SSL=ON \ + -DYLT_ENABLE_NTLS=ON \ + -DCMAKE_PREFIX_PATH=${TONGSUO_INSTALL_PREFIX} \ + -DOPENSSL_ROOT_DIR=${TONGSUO_INSTALL_PREFIX} \ + -DOPENSSL_CRYPTO_LIBRARY=${TONGSUO_INSTALL_PREFIX}/lib/libcrypto.so \ + -DOPENSSL_SSL_LIBRARY=${TONGSUO_INSTALL_PREFIX}/lib/libssl.so \ + -DOPENSSL_INCLUDE_DIR=${TONGSUO_INSTALL_PREFIX}/include + + - name: Build + run: | + export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH + cmake --build ${{github.workspace}}/build --config ${{matrix.mode}} + + - name: Verify NTLS examples are built + run: | + ls -la ${{github.workspace}}/build/src/coro_rpc/examples/base_examples/ntls_* || true + ls -la ${{github.workspace}}/build/src/coro_http/examples/ntls_* || true + + - name: Test NTLS compilation + run: | + export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH + # Check if NTLS examples were compiled successfully + if [ -f "${{github.workspace}}/build/src/coro_rpc/examples/base_examples/ntls_server" ] || \ + [ -f "${{github.workspace}}/build/src/coro_rpc/examples/base_examples/ntls_client" ]; then + echo "✓ NTLS examples compiled successfully" + else + echo "✗ NTLS examples were not compiled" + exit 1 + fi + + - name: Run basic tests + working-directory: ${{github.workspace}}/build + run: | + export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH + # Run tests that don't require NTLS (to verify basic functionality) + ctest -C ${{matrix.mode}} -j 1 -V --output-on-failure || true + + - name: Check NTLS symbols + run: | + export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH + # Verify that NTLS functions are available + nm -D ${TONGSUO_INSTALL_PREFIX}/lib/libssl.so | grep -i ntls || \ + objdump -T ${TONGSUO_INSTALL_PREFIX}/lib/libssl.so | grep -i ntls || true + diff --git a/cmake/config.cmake b/cmake/config.cmake index 9a99951c2..bd3605288 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -43,6 +43,42 @@ if (YLT_ENABLE_SSL) target_link_libraries(${ylt_target_name} INTERFACE OpenSSL::SSL OpenSSL::Crypto) endif () endif () +option(YLT_ENABLE_NTLS "Enable NTLS (国密SSL) support with Tongsuo" OFF) +message(STATUS "YLT_ENABLE_NTLS: ${YLT_ENABLE_NTLS}") +if (YLT_ENABLE_NTLS) + if (NOT YLT_ENABLE_SSL) + message(FATAL_ERROR "YLT_ENABLE_NTLS requires YLT_ENABLE_SSL to be ON") + endif() + # Check if OpenSSL is actually Tongsuo (which supports NTLS) + # We check for NTLS-specific functions that only exist in Tongsuo + include(CheckCSourceCompiles) + set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) + set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + check_c_source_compiles(" + #include + int main() { + SSL_CTX *ctx = NULL; + SSL_CTX_enable_ntls(ctx); + return 0; + } + " HAVE_NTLS_SUPPORT) + unset(CMAKE_REQUIRED_LIBRARIES) + unset(CMAKE_REQUIRED_INCLUDES) + + if (NOT HAVE_NTLS_SUPPORT) + message(WARNING "OpenSSL library does not support NTLS. Please use Tongsuo library.") + message(WARNING "NTLS code may not compile correctly without Tongsuo.") + else() + message(STATUS "NTLS support detected in OpenSSL/Tongsuo library") + endif() + if(CMAKE_PROJECT_NAME STREQUAL "yaLanTingLibs") + # Don't define OPENSSL_NO_NTLS, which enables NTLS code + # The code uses #ifndef OPENSSL_NO_NTLS, so we don't define it + message(STATUS "NTLS code will be compiled (OPENSSL_NO_NTLS not defined)") + else () + target_compile_definitions(${ylt_target_name} INTERFACE "YLT_ENABLE_NTLS") + endif () +endif () option(YLT_ENABLE_IBV "Enable ibverbs support" OFF) if (YLT_ENABLE_IBV) message(STATUS "Enable ibverbs support") From 4533f66b2ddbc450680d8d0475aed38a588ceceb Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Mon, 24 Nov 2025 14:52:07 +0800 Subject: [PATCH 09/38] Update certificate path references in NTLS examples to point to Tongsuo source tree's test certificates --- .github/workflows/ntls_test.yml | 3 ++- src/coro_http/examples/ntls_http_client.cpp | 5 +++-- src/coro_http/examples/ntls_http_server.cpp | 5 +++-- src/coro_rpc/examples/base_examples/ntls_client.cpp | 5 +++-- src/coro_rpc/examples/base_examples/ntls_server.cpp | 5 +++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ntls_test.yml b/.github/workflows/ntls_test.yml index 47ff7a4d4..147f10ff0 100644 --- a/.github/workflows/ntls_test.yml +++ b/.github/workflows/ntls_test.yml @@ -73,7 +73,8 @@ jobs: shared \ -fPIC \ enable-ntls \ - enable-ec_sm2p_64_gcc_128 + enable-ec_sm2p_64_gcc_128 \ + -Wl,-rpath,${TONGSUO_INSTALL_PREFIX}/lib make -j$(nproc) sudo make install diff --git a/src/coro_http/examples/ntls_http_client.cpp b/src/coro_http/examples/ntls_http_client.cpp index 2b2355e70..cb517aa78 100644 --- a/src/coro_http/examples/ntls_http_client.cpp +++ b/src/coro_http/examples/ntls_http_client.cpp @@ -21,8 +21,9 @@ using namespace cinatra; -// Certificate paths - adjust to your environment -const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +// Certificate paths - point to Tongsuo source tree's test certificates +// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 +const std::string CERT_PATH = "test/certs/sm2/"; const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate const std::string CLIENT_SIGN_KEY = diff --git a/src/coro_http/examples/ntls_http_server.cpp b/src/coro_http/examples/ntls_http_server.cpp index 9d225aad2..ff5c08faf 100644 --- a/src/coro_http/examples/ntls_http_server.cpp +++ b/src/coro_http/examples/ntls_http_server.cpp @@ -21,8 +21,9 @@ using namespace cinatra; -// Certificate paths - adjust to your environment -const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +// Certificate paths - point to Tongsuo source tree's test certificates +// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 +const std::string CERT_PATH = "test/certs/sm2/"; const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 signing certificate const std::string SERVER_SIGN_KEY = diff --git a/src/coro_rpc/examples/base_examples/ntls_client.cpp b/src/coro_rpc/examples/base_examples/ntls_client.cpp index afc4267ce..474e718b7 100644 --- a/src/coro_rpc/examples/base_examples/ntls_client.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_client.cpp @@ -25,8 +25,9 @@ using namespace coro_rpc; using namespace async_simple::coro; using namespace std::string_literals; -// Certificate paths - adjust to your environment -const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +// Certificate paths - point to Tongsuo source tree's test certificates +// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 +const std::string CERT_PATH = "test/certs/sm2/"; const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate const std::string CLIENT_SIGN_KEY = diff --git a/src/coro_rpc/examples/base_examples/ntls_server.cpp b/src/coro_rpc/examples/base_examples/ntls_server.cpp index 693d01900..4958e3fb9 100644 --- a/src/coro_rpc/examples/base_examples/ntls_server.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_server.cpp @@ -22,8 +22,9 @@ using namespace coro_rpc; using namespace std::string_literals; -// Certificate paths - adjust to your environment -const std::string CERT_PATH = "E:/vs2022workspace/yalantinglibs/src/certs/sm2/"; +// Certificate paths - point to Tongsuo source tree's test certificates +// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 +const std::string CERT_PATH = "test/certs/sm2/"; const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 server signing certificate const std::string SERVER_SIGN_KEY = From fd7ca7d84febe810aab18e04c7c9e2e1c53b5b2c Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Mon, 24 Nov 2025 19:45:03 +0800 Subject: [PATCH 10/38] Update CMake configuration and adjust NTLS example certificate paths for CI usage --- .github/workflows/ntls_test.yml | 75 ++++++++------- CMakeLists.txt | 2 + cmake/config.cmake | 8 +- include/ylt/coro_rpc/impl/common_service.hpp | 4 +- .../impl/default_config/coro_rpc_config.hpp | 2 +- .../cinatra/coro_http_connection.hpp | 56 +++++------ scripts/run_ntls_e2e_tests.sh | 95 +++++++++++++++++++ src/coro_http/examples/ntls_http_client.cpp | 5 +- src/coro_http/examples/ntls_http_server.cpp | 5 +- .../examples/base_examples/ntls_client.cpp | 5 +- .../examples/base_examples/ntls_server.cpp | 5 +- 11 files changed, 184 insertions(+), 78 deletions(-) create mode 100644 scripts/run_ntls_e2e_tests.sh diff --git a/.github/workflows/ntls_test.yml b/.github/workflows/ntls_test.yml index 147f10ff0..1b82b7afe 100644 --- a/.github/workflows/ntls_test.yml +++ b/.github/workflows/ntls_test.yml @@ -30,7 +30,6 @@ jobs: ntls_test_ubuntu: strategy: matrix: - compiler: [gcc, clang] mode: [Debug] runs-on: ubuntu-22.04 @@ -50,15 +49,6 @@ jobs: zlib1g-dev \ libbz2-dev - - name: Install compiler - if: matrix.compiler == 'clang' - run: | - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - sudo apt-get update - wget https://apt.llvm.org/llvm.sh - chmod +x ./llvm.sh - sudo ./llvm.sh 17 - - name: Build and install Tongsuo run: | set -e @@ -69,9 +59,6 @@ jobs: # Configure and build Tongsuo ./config --prefix=${TONGSUO_INSTALL_PREFIX} \ - --openssldir=${TONGSUO_INSTALL_PREFIX}/ssl \ - shared \ - -fPIC \ enable-ntls \ enable-ec_sm2p_64_gcc_128 \ -Wl,-rpath,${TONGSUO_INSTALL_PREFIX}/lib @@ -79,10 +66,15 @@ jobs: make -j$(nproc) sudo make install + # Copy SM2 test certificates into installed prefix + sudo mkdir -p ${TONGSUO_INSTALL_PREFIX}/ssl/certs + sudo cp -r test/certs/sm2 ${TONGSUO_INSTALL_PREFIX}/ssl/certs/ + # Update library paths sudo ldconfig - # Verify Tongsuo installation + # Verify Tongsuo installation (ensure it links against newly installed libssl) + export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH ${TONGSUO_INSTALL_PREFIX}/bin/tongsuo version || \ ${TONGSUO_INSTALL_PREFIX}/bin/openssl version @@ -103,25 +95,20 @@ jobs: - name: Install ninja-build tool uses: seanmiddleditch/gha-setup-ninja@master - - name: Configure CMake with NTLS + - name: Configure CMake with NTLS (GCC) run: | - if [ "${{ matrix.compiler }}" = "clang" ]; then - export CC=clang-17 - export CXX=clang++-17 - else - export CC=gcc - export CXX=g++ - fi - - # Set CMake variables to find Tongsuo + export CC=gcc + export CXX=g++ cmake -B ${{github.workspace}}/build -G Ninja \ -DCMAKE_BUILD_TYPE=${{matrix.mode}} \ + -DBUILD_EXAMPLES=ON \ + -DBUILD_BENCHMARK=OFF \ + -DGENERATE_BENCHMARK_DATA=OFF \ -DYLT_ENABLE_SSL=ON \ -DYLT_ENABLE_NTLS=ON \ + -DYLT_HAVE_IBVERBS=0 \ -DCMAKE_PREFIX_PATH=${TONGSUO_INSTALL_PREFIX} \ -DOPENSSL_ROOT_DIR=${TONGSUO_INSTALL_PREFIX} \ - -DOPENSSL_CRYPTO_LIBRARY=${TONGSUO_INSTALL_PREFIX}/lib/libcrypto.so \ - -DOPENSSL_SSL_LIBRARY=${TONGSUO_INSTALL_PREFIX}/lib/libssl.so \ -DOPENSSL_INCLUDE_DIR=${TONGSUO_INSTALL_PREFIX}/include - name: Build @@ -131,15 +118,15 @@ jobs: - name: Verify NTLS examples are built run: | - ls -la ${{github.workspace}}/build/src/coro_rpc/examples/base_examples/ntls_* || true - ls -la ${{github.workspace}}/build/src/coro_http/examples/ntls_* || true + ls -la ${{github.workspace}}/build/output/examples/coro_rpc/ntls_* || true + ls -la ${{github.workspace}}/build/output/examples/coro_http/ntls_* || true - name: Test NTLS compilation run: | export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH # Check if NTLS examples were compiled successfully - if [ -f "${{github.workspace}}/build/src/coro_rpc/examples/base_examples/ntls_server" ] || \ - [ -f "${{github.workspace}}/build/src/coro_rpc/examples/base_examples/ntls_client" ]; then + if [ -f "${{github.workspace}}/build/output/examples/coro_rpc/ntls_server" ] || \ + [ -f "${{github.workspace}}/build/output/examples/coro_rpc/ntls_client" ]; then echo "✓ NTLS examples compiled successfully" else echo "✗ NTLS examples were not compiled" @@ -153,10 +140,32 @@ jobs: # Run tests that don't require NTLS (to verify basic functionality) ctest -C ${{matrix.mode}} -j 1 -V --output-on-failure || true + - name: Run NTLS end-to-end tests + run: | + export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH + bash scripts/run_ntls_e2e_tests.sh ${{github.workspace}}/build/output/examples \ + ${{github.workspace}}/build/ntls-e2e-logs + - name: Check NTLS symbols run: | export LD_LIBRARY_PATH=${TONGSUO_INSTALL_PREFIX}/lib:$LD_LIBRARY_PATH - # Verify that NTLS functions are available - nm -D ${TONGSUO_INSTALL_PREFIX}/lib/libssl.so | grep -i ntls || \ - objdump -T ${TONGSUO_INSTALL_PREFIX}/lib/libssl.so | grep -i ntls || true + # Verify that NTLS functions are available in lib/ or lib64/, static or shared libs + found_symbol=false + for libdir in lib lib64; do + for libname in libssl.so libssl.a; do + candidate=${TONGSUO_INSTALL_PREFIX}/${libdir}/${libname} + if [ -f "$candidate" ]; then + echo "Checking $candidate for NTLS symbols..." + if (nm -a "$candidate" | grep -qi ntls) || (objdump -T "$candidate" 2>/dev/null | grep -qi ntls); then + found_symbol=true + break 2 + fi + fi + done + done + if [ "$found_symbol" = false ]; then + echo "WARNING: NTLS symbols not found in ${TONGSUO_INSTALL_PREFIX}/lib{,64}/libssl.{so,a}" + else + echo "✓ NTLS symbols detected in Tongsuo libssl" + fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 0838608f6..0dbdb68ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,8 @@ project(yaLanTingLibs LANGUAGES CXX ) +enable_language(C) + # load pack finder list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Find/) diff --git a/cmake/config.cmake b/cmake/config.cmake index 57abdc0a0..18c82e189 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -81,12 +81,16 @@ if (YLT_ENABLE_NTLS) message(STATUS "NTLS support detected in OpenSSL/Tongsuo library") endif() if(CMAKE_PROJECT_NAME STREQUAL "yaLanTingLibs") - # Don't define OPENSSL_NO_NTLS, which enables NTLS code - # The code uses #ifndef OPENSSL_NO_NTLS, so we don't define it message(STATUS "NTLS code will be compiled (OPENSSL_NO_NTLS not defined)") else () target_compile_definitions(${ylt_target_name} INTERFACE "YLT_ENABLE_NTLS") endif () +else () + if(CMAKE_PROJECT_NAME STREQUAL "yaLanTingLibs") + add_compile_definitions("OPENSSL_NO_NTLS") + else () + target_compile_definitions(${ylt_target_name} INTERFACE "OPENSSL_NO_NTLS") + endif () endif () option(YLT_ENABLE_IBV "Enable ibverbs support" OFF) if (YLT_ENABLE_IBV) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index 6d788cf62..973c65c38 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -98,13 +98,13 @@ struct ssl_ntls_configure { std::string server_name = "localhost"; //!< server name for verification ntls_mode mode = ntls_mode::tlcp_dual_cert; //!< NTLS mode selection - bool enable_ntls = false; //!< enable NTLS mode + bool enable_ntls = false; //!< enable NTLS mode bool enable_client_verify = false; //!< enable client certificate verification bool enable_dual_mode = false; //!< enable both SSL and NTLS support #endif }; -#endif OPENSSL_NO_NTLS +#endif // OPENSSL_NO_NTLS /*! * Check file (not a folder) exist diff --git a/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp b/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp index 0b51c1688..db45caccb 100644 --- a/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp +++ b/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp @@ -42,7 +42,7 @@ struct config_base { #ifndef OPENSSL_NO_NTLS // std::optional ntls_config = std::nullopt; std::optional ssl_ntls_config = std::nullopt; -#endif OPENSSL_NO_NTLS +#endif // OPENSSL_NO_NTLS #endif #ifdef YLT_ENABLE_IBV std::optional ibv_config = std::nullopt; diff --git a/include/ylt/standalone/cinatra/coro_http_connection.hpp b/include/ylt/standalone/cinatra/coro_http_connection.hpp index d05a94a0f..71df9ef27 100644 --- a/include/ylt/standalone/cinatra/coro_http_connection.hpp +++ b/include/ylt/standalone/cinatra/coro_http_connection.hpp @@ -230,14 +230,14 @@ class coro_http_connection // Load certificates based on NTLS mode if (ntls_config.mode == ntls_mode::tls13_single_cert) { // RFC 8998 TLS 1.3 + GM single certificate mode - auto gm_cert_path = ntls_config.base_path.empty() - ? ntls_config.gm_cert_file - : fs::path(ntls_config.base_path) - .append(ntls_config.gm_cert_file); - auto gm_key_path = ntls_config.base_path.empty() - ? ntls_config.gm_key_file - : fs::path(ntls_config.base_path) - .append(ntls_config.gm_key_file); + auto gm_cert_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.gm_cert_file) + : fs::path(ntls_config.base_path) / ntls_config.gm_cert_file; + auto gm_key_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.gm_key_file) + : fs::path(ntls_config.base_path) / ntls_config.gm_key_file; // Load single GM certificate if (fs::exists(gm_cert_path, file_ec)) { @@ -278,22 +278,22 @@ class coro_http_connection } else { // GB/T 38636-2020 TLCP dual certificate mode - auto sign_cert_path = ntls_config.base_path.empty() - ? ntls_config.sign_cert_file - : fs::path(ntls_config.base_path) - .append(ntls_config.sign_cert_file); - auto sign_key_path = ntls_config.base_path.empty() - ? ntls_config.sign_key_file - : fs::path(ntls_config.base_path) - .append(ntls_config.sign_key_file); - auto enc_cert_path = ntls_config.base_path.empty() - ? ntls_config.enc_cert_file - : fs::path(ntls_config.base_path) - .append(ntls_config.enc_cert_file); - auto enc_key_path = ntls_config.base_path.empty() - ? ntls_config.enc_key_file - : fs::path(ntls_config.base_path) - .append(ntls_config.enc_key_file); + auto sign_cert_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.sign_cert_file) + : fs::path(ntls_config.base_path) / ntls_config.sign_cert_file; + auto sign_key_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.sign_key_file) + : fs::path(ntls_config.base_path) / ntls_config.sign_key_file; + auto enc_cert_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.enc_cert_file) + : fs::path(ntls_config.base_path) / ntls_config.enc_cert_file; + auto enc_key_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.enc_key_file) + : fs::path(ntls_config.base_path) / ntls_config.enc_key_file; // Load SM2 signing certificate and key if (fs::exists(sign_cert_path, file_ec)) { @@ -372,10 +372,10 @@ class coro_http_connection } // Load CA certificate if provided if (!ntls_config.ca_cert_file.empty()) { - auto ca_cert_path = ntls_config.base_path.empty() - ? ntls_config.ca_cert_file - : fs::path(ntls_config.base_path) - .append(ntls_config.ca_cert_file); + auto ca_cert_path = + ntls_config.base_path.empty() + ? fs::path(ntls_config.ca_cert_file) + : fs::path(ntls_config.base_path) / ntls_config.ca_cert_file; if (fs::exists(ca_cert_path, file_ec)) { asio::error_code ec; ssl_ctx_->load_verify_file(ca_cert_path.string(), ec); diff --git a/scripts/run_ntls_e2e_tests.sh b/scripts/run_ntls_e2e_tests.sh new file mode 100644 index 000000000..f394d777f --- /dev/null +++ b/scripts/run_ntls_e2e_tests.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +set -euo pipefail + +BIN_ROOT="${1:-$(pwd)/build/output/examples}" +LOG_DIR="${2:-$(pwd)/build/ntls-e2e-logs}" +RPC_SERVER_PID="" +HTTP_SERVER_PID="" + +mkdir -p "${LOG_DIR}" + +cleanup() { + if [[ -n "${RPC_SERVER_PID}" ]] && kill -0 "${RPC_SERVER_PID}" 2>/dev/null; then + kill "${RPC_SERVER_PID}" 2>/dev/null || true + fi + if [[ -n "${HTTP_SERVER_PID}" ]] && kill -0 "${HTTP_SERVER_PID}" 2>/dev/null; then + kill "${HTTP_SERVER_PID}" 2>/dev/null || true + fi +} +trap cleanup EXIT + +wait_for_port() { + local port="$1" + local retries="${2:-40}" + python - "$port" "$retries" <<'PY' +import socket, sys, time +port = int(sys.argv[1]) +retries = int(sys.argv[2]) +for _ in range(retries): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.settimeout(1) + try: + sock.connect(("127.0.0.1", port)) + except OSError: + time.sleep(1) + else: + sys.exit(0) +print(f"port {port} unreachable after {retries}s", file=sys.stderr) +sys.exit(1) +PY +} + +run_ntls_rpc_tests() { + local server_bin="${BIN_ROOT}/coro_rpc/ntls_server" + local client_bin="${BIN_ROOT}/coro_rpc/ntls_client" + + if [[ ! -x "${server_bin}" || ! -x "${client_bin}" ]]; then + echo "NTLS RPC binaries not found under ${BIN_ROOT}. Skipping RPC tests." + return 1 + fi + + echo "Starting NTLS RPC server..." + timeout 120 "${server_bin}" >"${LOG_DIR}/ntls_rpc_server.log" 2>&1 & + RPC_SERVER_PID=$! + for port in 8801 8802 8803 8804; do + wait_for_port "${port}" 40 + done + + echo "Running NTLS RPC client..." + timeout 120 "${client_bin}" >"${LOG_DIR}/ntls_rpc_client.log" 2>&1 + echo "NTLS RPC client completed successfully." + + kill "${RPC_SERVER_PID}" 2>/dev/null || true + RPC_SERVER_PID="" +} + +run_ntls_http_tests() { + local server_bin="${BIN_ROOT}/coro_http/ntls_http_server" + local client_bin="${BIN_ROOT}/coro_http/ntls_http_client" + + if [[ ! -x "${server_bin}" || ! -x "${client_bin}" ]]; then + echo "NTLS HTTP binaries not found under ${BIN_ROOT}. Skipping HTTP tests." + return 1 + fi + + echo "Starting NTLS HTTP server..." + timeout 120 "${server_bin}" >"${LOG_DIR}/ntls_http_server.log" 2>&1 & + HTTP_SERVER_PID=$! + for port in 8801 8802 8803 8804; do + wait_for_port "${port}" 40 + done + + echo "Running NTLS HTTP client..." + timeout 120 "${client_bin}" >"${LOG_DIR}/ntls_http_client.log" 2>&1 + echo "NTLS HTTP client completed successfully." + + kill "${HTTP_SERVER_PID}" 2>/dev/null || true + HTTP_SERVER_PID="" +} + +echo "===== Running NTLS RPC end-to-end tests =====" +run_ntls_rpc_tests +echo "===== Running NTLS HTTP end-to-end tests =====" +run_ntls_http_tests +echo "NTLS end-to-end tests finished." + diff --git a/src/coro_http/examples/ntls_http_client.cpp b/src/coro_http/examples/ntls_http_client.cpp index cb517aa78..bf7ad3374 100644 --- a/src/coro_http/examples/ntls_http_client.cpp +++ b/src/coro_http/examples/ntls_http_client.cpp @@ -21,9 +21,8 @@ using namespace cinatra; -// Certificate paths - point to Tongsuo source tree's test certificates -// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 -const std::string CERT_PATH = "test/certs/sm2/"; +// Certificate paths - use SM2 certs installed with Tongsuo in CI +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate const std::string CLIENT_SIGN_KEY = diff --git a/src/coro_http/examples/ntls_http_server.cpp b/src/coro_http/examples/ntls_http_server.cpp index ff5c08faf..453cd2649 100644 --- a/src/coro_http/examples/ntls_http_server.cpp +++ b/src/coro_http/examples/ntls_http_server.cpp @@ -21,9 +21,8 @@ using namespace cinatra; -// Certificate paths - point to Tongsuo source tree's test certificates -// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 -const std::string CERT_PATH = "test/certs/sm2/"; +// Certificate paths - use SM2 certs installed with Tongsuo in CI +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 signing certificate const std::string SERVER_SIGN_KEY = diff --git a/src/coro_rpc/examples/base_examples/ntls_client.cpp b/src/coro_rpc/examples/base_examples/ntls_client.cpp index 474e718b7..b006d7c75 100644 --- a/src/coro_rpc/examples/base_examples/ntls_client.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_client.cpp @@ -25,9 +25,8 @@ using namespace coro_rpc; using namespace async_simple::coro; using namespace std::string_literals; -// Certificate paths - point to Tongsuo source tree's test certificates -// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 -const std::string CERT_PATH = "test/certs/sm2/"; +// Certificate paths - use SM2 certs installed with Tongsuo in CI +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; // SM2 client signing certificate const std::string CLIENT_SIGN_KEY = diff --git a/src/coro_rpc/examples/base_examples/ntls_server.cpp b/src/coro_rpc/examples/base_examples/ntls_server.cpp index 4958e3fb9..989a4dc6d 100644 --- a/src/coro_rpc/examples/base_examples/ntls_server.cpp +++ b/src/coro_rpc/examples/base_examples/ntls_server.cpp @@ -22,9 +22,8 @@ using namespace coro_rpc; using namespace std::string_literals; -// Certificate paths - point to Tongsuo source tree's test certificates -// https://github.com/Tongsuo-Project/Tongsuo/tree/master/test/certs/sm2 -const std::string CERT_PATH = "test/certs/sm2/"; +// Certificate paths - use SM2 certs installed with Tongsuo in CI +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; // SM2 server signing certificate const std::string SERVER_SIGN_KEY = From 0ba4f97b47b932640f2fef80c207f010c8972fbf Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 25 Nov 2025 15:42:39 +0800 Subject: [PATCH 11/38] Refactor CMake configuration to remove unnecessary C language support and update Windows library linking to use 'mswsock' alongside 'ws2_32' for various examples and benchmarks. --- CMakeLists.txt | 2 -- cmake/config.cmake | 2 +- src/coro_http/examples/CMakeLists.txt | 11 ++++++----- .../examples/base_examples/CMakeLists.txt | 17 +++++++++-------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dbdb68ed..0838608f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,6 @@ project(yaLanTingLibs LANGUAGES CXX ) -enable_language(C) - # load pack finder list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/Find/) diff --git a/cmake/config.cmake b/cmake/config.cmake index 18c82e189..210f47735 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -52,7 +52,7 @@ if (YLT_ENABLE_SSL) target_link_libraries(${ylt_target_name} INTERFACE OpenSSL::SSL OpenSSL::Crypto) endif () endif () -option(YLT_ENABLE_NTLS "Enable NTLS (国密SSL) support with Tongsuo" OFF) +option(YLT_ENABLE_NTLS "Enable NTLS support with Tongsuo" OFF) message(STATUS "YLT_ENABLE_NTLS: ${YLT_ENABLE_NTLS}") if (YLT_ENABLE_NTLS) if (NOT YLT_ENABLE_SSL) diff --git a/src/coro_http/examples/CMakeLists.txt b/src/coro_http/examples/CMakeLists.txt index dc7e7f78a..ebe1d34d7 100644 --- a/src/coro_http/examples/CMakeLists.txt +++ b/src/coro_http/examples/CMakeLists.txt @@ -30,6 +30,7 @@ endif() add_executable(coro_http_example example.cpp) add_executable(coro_http_load_balancer load_balancer.cpp) add_executable(coro_chat_room chat_room.cpp) + if(YLT_ENABLE_NTLS) message(STATUS "Building NTLS examples with Tongsuo support") add_executable(ntls_http_server ntls_http_server.cpp) @@ -38,12 +39,12 @@ if(YLT_ENABLE_NTLS) target_compile_definitions(ntls_http_client PRIVATE YLT_ENABLE_NTLS) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 - target_link_libraries(coro_http_example wsock32 ws2_32) - target_link_libraries(coro_http_load_balancer wsock32 ws2_32) - target_link_libraries(coro_chat_room wsock32 ws2_32) + target_link_libraries(coro_http_example PRIVATE ws2_32 mswsock) + target_link_libraries(coro_http_load_balancer PRIVATE ws2_32 mswsock) + target_link_libraries(coro_chat_room PRIVATE ws2_32 mswsock) if(YLT_ENABLE_NTLS) - target_link_libraries(ntls_http_server wsock32 ws2_32) - target_link_libraries(ntls_http_client wsock32 ws2_32) + target_link_libraries(ntls_http_server PRIVATE ws2_32 mswsock) + target_link_libraries(ntls_http_client PRIVATE ws2_32 mswsock) endif() endif() diff --git a/src/coro_rpc/examples/base_examples/CMakeLists.txt b/src/coro_rpc/examples/base_examples/CMakeLists.txt index ddf859ffe..8616136cc 100644 --- a/src/coro_rpc/examples/base_examples/CMakeLists.txt +++ b/src/coro_rpc/examples/base_examples/CMakeLists.txt @@ -70,6 +70,7 @@ add_executable(coro_rpc_example_client_pools client_pools.cpp) add_executable(coro_rpc_example_client client.cpp) add_executable(coro_rpc_example_concurrent_clients concurrent_clients.cpp) add_executable(coro_rpc_example_server server.cpp rpc_service.cpp) + if(YLT_ENABLE_NTLS) message(STATUS "Building NTLS examples with Tongsuo support") add_executable(ntls_server ntls_server.cpp) @@ -78,15 +79,15 @@ if(YLT_ENABLE_NTLS) target_compile_definitions(ntls_client PRIVATE YLT_ENABLE_NTLS) endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 - target_link_libraries(coro_rpc_example_load_balancer wsock32 ws2_32) - target_link_libraries(coro_rpc_example_client_pool wsock32 ws2_32) - target_link_libraries(coro_rpc_example_client_pools wsock32 ws2_32) - target_link_libraries(coro_rpc_example_client wsock32 ws2_32) - target_link_libraries(coro_rpc_example_concurrent_clients wsock32 ws2_32) - target_link_libraries(coro_rpc_example_server wsock32 ws2_32) + target_link_libraries(coro_rpc_example_load_balancer PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_example_client_pool PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_example_client_pools PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_example_client PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_example_concurrent_clients PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_example_server PRIVATE ws2_32 mswsock) if(YLT_ENABLE_NTLS) - target_link_libraries(ntls_server wsock32 ws2_32) - target_link_libraries(ntls_client wsock32 ws2_32) + target_link_libraries(ntls_server PRIVATE ws2_32 mswsock) + target_link_libraries(ntls_client PRIVATE ws2_32 mswsock) endif() endif() From ab78886dc080f0c9a651bf5f66d4fa6bebad55bb Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 25 Nov 2025 16:34:54 +0800 Subject: [PATCH 12/38] fix:Remove conditional compilation for NTLS support in common_service.hpp and update coro_http_client.hpp to enable NTLS by default, ensuring proper SSL context initialization. --- include/ylt/coro_rpc/impl/common_service.hpp | 4 ---- include/ylt/standalone/cinatra/coro_http_client.hpp | 9 ++++++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index 973c65c38..d04757344 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -52,7 +52,6 @@ struct ssl_configure { // bool enable_ntls = true; //!< enable NTLS mode //}; -#ifndef OPENSSL_NO_NTLS /*! * NTLS mode enumeration */ @@ -61,7 +60,6 @@ enum class ntls_mode { //!< encryption) tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate }; -#endif /*! * Extended SSL config that supports both SSL and NTLS @@ -74,7 +72,6 @@ struct ssl_ntls_configure { std::string key_file; //!< relative path of private key file std::string dh_file; //!< relative path of tmp dh file (optional) -#ifndef OPENSSL_NO_NTLS // TLCP dual certificate configuration (GB/T 38636-2020) std::string sign_cert_file; //!< relative path of SM2 signing certificate file @@ -102,7 +99,6 @@ struct ssl_ntls_configure { bool enable_client_verify = false; //!< enable client certificate verification bool enable_dual_mode = false; //!< enable both SSL and NTLS support -#endif }; #endif // OPENSSL_NO_NTLS diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index e8f47cc7c..204dc8caa 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -219,6 +219,9 @@ class coro_http_client : public std::enable_shared_from_this { try { #ifndef OPENSSL_NO_NTLS + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); +#else if (use_ntls_) { ssl_ctx_ = std::make_unique( SSL_CTX_new(NTLS_client_method())); @@ -234,8 +237,8 @@ class coro_http_client : public std::enable_shared_from_this { } } else { - ssl_ctx_ = - std::make_unique(asio::ssl::context::sslv23); + CINATRA_LOG_ERROR << "NTLS is not supported in this build."; + return false; } #endif // OPENSSL_NO_NTLS auto full_cert_file = std::filesystem::path(base_path).append(cert_file); @@ -2677,7 +2680,7 @@ class coro_http_client : public std::enable_shared_from_this { bool has_init_ssl_ = false; bool is_ssl_schema_ = false; #ifndef OPENSSL_NO_NTLS - bool use_ntls_ = false; + bool use_ntls_ = true; #endif // OPENSSL_NO_NTLS bool need_set_sni_host_ = true; #endif From fd58e8304b7c76156b192abfc82cf4a7b61bbedb Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 25 Nov 2025 16:44:27 +0800 Subject: [PATCH 13/38] Enhance CMake configuration by enabling C language support for NTLS checks, ensuring proper compilation of OpenSSL dependencies. --- cmake/config.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/config.cmake b/cmake/config.cmake index 210f47735..228dfd51c 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -63,6 +63,7 @@ if (YLT_ENABLE_NTLS) include(CheckCSourceCompiles) set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR}) + enable_language(C) check_c_source_compiles(" #include int main() { From a37fbaa0be4d4380cd40ffa0979bbb284a51007d Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 25 Nov 2025 17:44:01 +0800 Subject: [PATCH 14/38] fix:Refactor SSL context initialization in coro_http_client.hpp to restore conditional compilation for NTLS support, ensuring compatibility with builds that do not support NTLS. --- include/ylt/standalone/cinatra/coro_http_client.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index 204dc8caa..8f6aab16e 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -219,9 +219,6 @@ class coro_http_client : public std::enable_shared_from_this { try { #ifndef OPENSSL_NO_NTLS - ssl_ctx_ = - std::make_unique(asio::ssl::context::sslv23); -#else if (use_ntls_) { ssl_ctx_ = std::make_unique( SSL_CTX_new(NTLS_client_method())); @@ -240,6 +237,9 @@ class coro_http_client : public std::enable_shared_from_this { CINATRA_LOG_ERROR << "NTLS is not supported in this build."; return false; } +#else + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); #endif // OPENSSL_NO_NTLS auto full_cert_file = std::filesystem::path(base_path).append(cert_file); if (std::filesystem::exists(full_cert_file)) { From c12d6c9c129cebc23d2603d9a10890e1d3002707 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Wed, 26 Nov 2025 15:05:35 +0800 Subject: [PATCH 15/38] format --- include/ylt/standalone/cinatra/coro_http_client.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index 8f6aab16e..ec6a6b8b0 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -140,7 +140,7 @@ class coro_http_client : public std::enable_shared_from_this { #ifndef OPENSSL_NO_NTLS bool use_ntls = false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. -#endif // OPENSSL_NO_NTLS +#endif // OPENSSL_NO_NTLS #endif }; From 3cb2d75db36ae8ce4f469d4da8f63b7b78cffeaf Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 27 Nov 2025 11:33:29 +0800 Subject: [PATCH 16/38] Add NTLS support documentation for yaLanTingLibs --- docs/NTLS_Feature_Introduction.md | 665 ++++++++++++++++++++++++++++++ 1 file changed, 665 insertions(+) create mode 100644 docs/NTLS_Feature_Introduction.md diff --git a/docs/NTLS_Feature_Introduction.md b/docs/NTLS_Feature_Introduction.md new file mode 100644 index 000000000..1a8fca569 --- /dev/null +++ b/docs/NTLS_Feature_Introduction.md @@ -0,0 +1,665 @@ +# yaLanTingLibs 国密SSL(NTLS)支持 + +## 概述 + +yaLanTingLibs 现已全面支持国密SSL(NTLS,National TLS),这是中国国家密码管理局制定的密码算法标准。通过集成 Tongsuo(铜锁)密码库,yaLanTingLibs 的 `coro_rpc` 和 `coro_http` 组件现在可以支持两种国密通信协议: + +- **GB/T 38636-2020 TLCP**:双证书国密通信协议(Transport Layer Cryptography Protocol) +- **RFC 8998**:TLS 1.3 + 国密单证书模式 + +这使得 yaLanTingLibs 成为首批支持国密SSL的现代C++20协程网络库之一,为需要符合国密标准的应用提供了高性能、易用的解决方案。 + +## 背景 + +### 什么是国密SSL? + +国密SSL(NTLS)是基于中国国家密码算法标准(SM2/SM3/SM4)的传输层安全协议。与传统的TLS协议使用RSA、AES等国际算法不同,国密SSL使用: + +- **SM2**:椭圆曲线公钥密码算法(用于数字签名和密钥交换) +- **SM3**:密码杂凑算法(用于消息摘要) +- **SM4**:分组密码算法(用于数据加密) + +### 为什么需要国密SSL? + +1. **合规要求**:在中国,涉及国家秘密、商业秘密和个人隐私的信息系统,需要采用国密算法 +2. **自主可控**:国密算法由中国自主研发,符合国家信息安全战略 +3. **标准化**:GB/T 38636-2020 和 RFC 8998 提供了标准化的国密通信协议规范 + +### 应用场景 + +- 政府信息系统 +- 金融行业(银行、证券、保险) +- 关键基础设施 +- 需要符合国密标准的商业应用 + +## 支持的协议模式 + +### 1. GB/T 38636-2020 TLCP(双证书模式) + +TLCP(Transport Layer Cryptography Protocol)是中国的国家标准,采用双证书机制: + +- **签名证书**:用于身份认证和数字签名 +- **加密证书**:用于密钥交换和数据加密 + +**特点:** +- 符合GB/T 38636-2020标准 +- 支持单向认证和双向认证 +- 使用SM2/SM3/SM4国密算法套件 +- 默认密码套件:`ECC-SM2-SM4-GCM-SM3` 和 `ECC-SM2-SM4-CBC-SM3`,支持设置ECDHE_SM4_CBC_SM3和ECDHE_SM4_GCM_SM3 + +### 2. RFC 8998 TLS 1.3 + 国密(单证书模式) + +RFC 8998 是IETF标准,将国密算法集成到TLS 1.3协议中: + +- 使用单一证书(简化证书管理) +- 基于TLS 1.3协议 +- 支持国密算法套件:`TLS_SM4_GCM_SM3` 和 `TLS_SM4_CCM_SM3` + +**特点:** +- 符合国际标准(RFC 8998) +- 证书管理更简单 +- 性能优化(TLS 1.3的1-RTT握手) +- 向后兼容TLS 1.3生态 + +## 技术实现 + +### 依赖库 + +yaLanTingLibs 的NTLS功能基于 **Tongsuo(铜锁)** 密码库实现。Tongsuo(铜锁) 是由开放原子开源基金会(OpenAtom Foundation)孵化及运营的开源项目,基于OpenSSL 3.0.3分支,增加了对国密算法的完整支持。 +铜锁获得了国家密码管理局商用密码检测中心颁发的商用密码产品认证证书,助力用户在国密改造、密评、等保等过程中,更加严谨地满足我国商用密码技术合规的要求。 + +**Tongsuo项目地址:** https://github.com/Tongsuo-Project/Tongsuo + +### 架构设计 + +NTLS功能完全集成到 yaLanTingLibs 的现有架构中: + +- **条件编译**:通过 `YLT_ENABLE_NTLS` 宏控制,默认关闭,不影响现有代码 +- **统一API**:与标准SSL API保持一致的使用方式 +- **协程友好**:完全支持C++20协程,异步非阻塞 +- **零拷贝**:充分利用协程特性,实现高性能网络通信 + +## 编译配置 + +### 前置要求 + +1. **安装Tongsuo库** + ```bash + # 从源码编译安装Tongsuo + git clone https://github.com/Tongsuo-Project/Tongsuo.git + cd Tongsuo + ./config --prefix=/usr/local/tongsuo \ + -Wl,-rpath,/usr/local/tongsuo/lib64 \ + enable-ntls + make -j$(nproc) + sudo make install + sudo ldconfig + ``` + +2. **配置环境变量** + ```bash + export PATH=/usr/local/tongsuo/bin:$PATH + export LD_LIBRARY_PATH=/usr/local/tongsuo/lib64:$LD_LIBRARY_PATH + export PKG_CONFIG_PATH=/usr/local/tongsuo/lib64/pkgconfig:$PKG_CONFIG_PATH + ``` + +### CMake配置 + +在编译 yaLanTingLibs 时,需要启用SSL和NTLS支持: + +```bash +cmake -B build -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DYLT_ENABLE_SSL=ON \ + -DYLT_ENABLE_NTLS=ON \ + -DCMAKE_PREFIX_PATH=/usr/local/tongsuo \ + -DOPENSSL_ROOT_DIR=/usr/local/tongsuo \ + -DOPENSSL_INCLUDE_DIR=/usr/local/tongsuo/include + +cmake --build build +``` + +**关键配置选项:** +- `YLT_ENABLE_SSL=ON`:启用SSL支持(NTLS的前置条件) +- `YLT_ENABLE_NTLS=ON`:启用NTLS支持 +- `CMAKE_PREFIX_PATH`:指定Tongsuo的安装路径 +- `OPENSSL_ROOT_DIR`:CMake查找OpenSSL/Tongsuo的根目录 + +## 使用方法 + +### coro_rpc 使用示例 + +#### TLCP双证书模式(GB/T 38636-2020) + +**服务器端(单向认证):** + +```cpp +#include +#include +#include + +using namespace coro_rpc; + +// 证书路径 - 使用Tongsuo安装的SM2证书 +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; +const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; +const std::string SERVER_SIGN_KEY = CERT_PATH + "server_sign.key"; +const std::string SERVER_ENC_CERT = CERT_PATH + "server_enc.crt"; +const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; + +// RPC服务函数 +std::string echo(std::string_view data) { + std::cout << "Server received: " << data << std::endl; + return "Hello World"; +} + +int main() { + // 创建RPC服务器 + coro_rpc_server server(std::thread::hardware_concurrency(), 8801); + + // 配置NTLS(TLCP双证书模式,单向认证) + ssl_ntls_configure ntls_conf; + ntls_conf.base_path = ""; // 使用完整路径 + ntls_conf.sign_cert_file = SERVER_SIGN_CERT; // SM2签名证书 + ntls_conf.sign_key_file = SERVER_SIGN_KEY; // SM2签名私钥 + ntls_conf.enc_cert_file = SERVER_ENC_CERT; // SM2加密证书 + ntls_conf.enc_key_file = SERVER_ENC_KEY; // SM2加密私钥 + ntls_conf.ca_cert_file = CA_CERT; // CA证书 + ntls_conf.enable_client_verify = false; // 禁用客户端证书验证 + ntls_conf.enable_ntls = true; // 启用NTLS模式 + + // 初始化NTLS + server.init_ntls(ntls_conf); + + // 注册RPC服务 + server.register_handler(); + + std::cout << "NTLS RPC Server (one-way auth) starting on port 8801..." + << std::endl; + + // 启动服务器(阻塞) + auto result = server.start(); + if (result) { + std::cout << "Server start failed: " << result.message() << std::endl; + return 1; + } + + return 0; +} +``` + +**客户端(单向认证):** + +```cpp +#include +#include "async_simple/coro/Lazy.h" +#include "async_simple/coro/SyncAwait.h" + +using namespace coro_rpc; +using namespace async_simple::coro; + +// 证书路径 +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; + +// RPC函数声明 +std::string echo(std::string_view data); + +Lazy test_one_way_auth() { + coro_rpc_client client; + + // 初始化NTLS客户端(单向认证) + // 单向认证模式下,只需要CA证书来验证服务器 + bool ok = client.init_ntls( + CERT_PATH, // 证书基础路径 + "", // 客户端签名证书(单向认证不需要) + "", // 客户端签名私钥 + "", // 客户端加密证书 + "", // 客户端加密私钥 + CA_CERT, // CA证书 + "localhost", // 服务器域名 + false // 不验证客户端证书 + ); + + if (!ok) { + std::cout << "Failed to initialize one-way NTLS client" << std::endl; + co_return; + } + + // 连接到服务器 + auto ec = co_await client.connect("127.0.0.1", "8801"); + if (ec) { + std::cout << "Failed to connect: " << ec.message() << std::endl; + co_return; + } + + // 调用RPC服务 + auto result = co_await client.call("Test message to one-way server"); + if (result) { + std::cout << "Echo result: " << result.value() << std::endl; + } +} + +int main() { + syncAwait(test_one_way_auth()); + return 0; +} +``` + +**双向认证示例:** + +```cpp +// 服务器端:设置 enable_client_verify = true +ntls_conf.enable_client_verify = true; // 启用客户端证书验证 + +// 客户端:提供客户端证书 +const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; +const std::string CLIENT_SIGN_KEY = CERT_PATH + "client_sign.key"; +const std::string CLIENT_ENC_CERT = CERT_PATH + "client_enc.crt"; +const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; + +bool ok = client.init_ntls( + CERT_PATH, + CLIENT_SIGN_CERT, // 客户端签名证书 + CLIENT_SIGN_KEY, // 客户端签名私钥 + CLIENT_ENC_CERT, // 客户端加密证书 + CLIENT_ENC_KEY, // 客户端加密私钥 + CA_CERT, + "localhost", + true // 验证客户端证书 +); +``` + +#### RFC 8998 TLS 1.3 + 国密模式 + +**服务器端:** + +```cpp +// 配置TLS 1.3 + 国密单证书模式(RFC 8998) +ssl_ntls_configure ntls_conf; +ntls_conf.base_path = ""; +ntls_conf.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + 国密模式 +ntls_conf.gm_cert_file = SERVER_GM_CERT; // 国密单证书 +ntls_conf.gm_key_file = SERVER_GM_KEY; // 国密私钥 +ntls_conf.ca_cert_file = CA_CERT; +ntls_conf.enable_client_verify = false; // 单向认证 +ntls_conf.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3国密密码套件 + +server.init_ntls(ntls_conf); +``` + +**客户端:** + +```cpp +// 初始化TLS 1.3 + 国密客户端(单向认证) +ssl_ntls_configure config; +config.base_path = CERT_PATH; +config.mode = ntls_mode::tls13_single_cert; // TLS 1.3 + 国密模式 +config.ca_cert_file = CA_CERT; +config.server_name = "localhost"; +config.cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3"; // TLS 1.3国密密码套件 +// 单向认证不需要客户端证书 + +bool ok = client.init_ntls(config); +``` + +### coro_http 使用示例 + +#### HTTP服务器(TLCP模式) + +```cpp +#include +#include + +using namespace cinatra; + +// 证书路径 +const std::string CERT_PATH = "/usr/local/tongsuo/ssl/certs/sm2/"; +const std::string SERVER_SIGN_CERT = CERT_PATH + "server_sign.crt"; +const std::string SERVER_SIGN_KEY = CERT_PATH + "server_sign.key"; +const std::string SERVER_ENC_CERT = CERT_PATH + "server_enc.crt"; +const std::string SERVER_ENC_KEY = CERT_PATH + "server_enc.key"; +const std::string CA_CERT = CERT_PATH + "chain-ca.crt"; + +void start_one_way_server() { + coro_http_server server(1, 8801); + + // 初始化NTLS(TLCP双证书模式,单向认证) + server.init_ntls( + SERVER_SIGN_CERT, // 签名证书 + SERVER_SIGN_KEY, // 签名私钥 + SERVER_ENC_CERT, // 加密证书 + SERVER_ENC_KEY, // 加密私钥 + CA_CERT, // CA证书 + false // 不验证客户端证书 + ); + + // 设置NTLS密码套件 + server.set_ntls_cipher_suites("ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3"); + + // 注册HTTP处理器 + server.set_http_handler( + "/", + [](coro_http_request& req, coro_http_response& resp) + -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + + server.set_http_handler( + "/echo", + [](coro_http_request& req, coro_http_response& resp) + -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, "Hello World"); + co_return; + }); + + std::cout << "One-way NTLS server ready on port 8801" << std::endl; + server.sync_start(); +} + +int main() { + start_one_way_server(); + return 0; +} +``` + +#### HTTP客户端 + +**TLCP模式(单向认证):** + +```cpp +#include +#include "async_simple/coro/Lazy.h" +#include "async_simple/coro/SyncAwait.h" + +using namespace cinatra; + +async_simple::coro::Lazy test_ntls_http_client_one_way() { + coro_http_client client{}; + + // 启用NTLS协议(简单模式,仅服务器认证) + client.set_ntls_schema(true); + + // 发送GET请求 + auto response = co_await client.async_get("https://localhost:8801/"); + if (response.net_err) { + std::cout << "GET / request failed: " << response.net_err.message() + << std::endl; + } else { + std::cout << "Response status: " << response.status << std::endl; + std::cout << "Response body: " << response.resp_body << std::endl; + } + + // 发送POST请求 + auto post_response = co_await client.async_post( + "https://localhost:8801/echo", "Test message", req_content_type::text); + if (!post_response.net_err) { + std::cout << "POST Response: " << post_response.resp_body << std::endl; + } +} + +int main() { + async_simple::coro::syncAwait(test_ntls_http_client_one_way()); + return 0; +} +``` + +**TLCP模式(双向认证):** + +```cpp +const std::string CLIENT_SIGN_CERT = CERT_PATH + "client_sign.crt"; +const std::string CLIENT_SIGN_KEY = CERT_PATH + "client_sign.key"; +const std::string CLIENT_ENC_CERT = CERT_PATH + "client_enc.crt"; +const std::string CLIENT_ENC_KEY = CERT_PATH + "client_enc.key"; + +async_simple::coro::Lazy test_ntls_http_client_mutual_auth() { + coro_http_client client{}; + + // 初始化NTLS客户端(双向认证) + bool init_ok = client.init_ntls_client( + CLIENT_SIGN_CERT, // 客户端签名证书 + CLIENT_SIGN_KEY, // 客户端签名私钥 + CLIENT_ENC_CERT, // 客户端加密证书 + CLIENT_ENC_KEY, // 客户端加密私钥 + CA_CERT, // CA证书 + asio::ssl::verify_peer // 验证服务器证书 + ); + + if (!init_ok) { + std::cout << "Failed to initialize NTLS client" << std::endl; + co_return; + } + + // 发送请求(需要客户端证书) + auto response = co_await client.async_get("https://localhost:8802/"); + // ... +} +``` + +**TLS 1.3 + 国密模式:** + +```cpp +// 服务器端 +const std::string SERVER_GM_CERT = CERT_PATH + "server_sign.crt"; +const std::string SERVER_GM_KEY = CERT_PATH + "server_sign.key"; + +server.init_ntls( + "", SERVER_GM_CERT, SERVER_GM_KEY, "", "", CA_CERT, + false, // 单向认证 + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", // TLS 1.3国密密码套件 + coro_http_connection::ntls_mode::tls13_single_cert +); + +// 客户端 +bool init_ok = client.init_ntls_tls13_gm_client( + "", // 单向认证不需要客户端证书 + "", // 单向认证不需要客户端私钥 + CA_CERT, // CA证书 + asio::ssl::verify_peer, // 验证服务器证书 + "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" // TLS 1.3国密密码套件 +); +``` + +## 证书管理 + +### 使用Tongsuo测试证书(推荐用于开发测试) + +最简单的方式是直接使用Tongsuo源码中自带的测试证书: + +```bash +# Tongsuo源码目录中的测试证书位置 +TONGSUO_SRC/test/certs/sm2/ + +# 将测试证书复制到安装目录(CI中会自动完成) +cp -r /path/to/Tongsuo/test/certs/sm2 /usr/local/tongsuo/ssl/certs/ +``` + +这些证书已经包含了: +- CA证书(`chain-ca.crt`) +- 服务器签名证书和私钥(`server_sign.crt`, `server_sign.key`) +- 服务器加密证书和私钥(`server_enc.crt`, `server_enc.key`) +- 客户端签名证书和私钥(`client_sign.crt`, `client_sign.key`) +- 客户端加密证书和私钥(`client_enc.crt`, `client_enc.key`) + +### 生成SM2证书 + +如果需要生成自己的SM2证书,可以参考 [Tongsuo官方文档:使用 Tongsuo 签发 SM2 双证书](https://www.yuque.com/tsdoc/ts/sulazb)。 + +以下是基本的生成步骤: + +#### 1. 生成CA私钥和证书 + +```bash +# 生成CA私钥 +tongsuo genpkey -algorithm SM2 -out ca.key + +# 生成CA证书 +tongsuo req -new -x509 -key ca.key -out ca.crt -days 365 \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=Test CA" +``` + +#### 2. 生成服务器签名证书(TLCP模式需要) + +```bash +# 生成服务器签名私钥 +tongsuo genpkey -algorithm SM2 -out server_sign.key + +# 生成服务器签名证书请求 +tongsuo req -new -key server_sign.key -out server_sign.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=localhost" + +# 使用CA签名服务器签名证书 +tongsuo x509 -req -in server_sign.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out server_sign.crt -days 365 +``` + +#### 3. 生成服务器加密证书(TLCP模式需要) + +```bash +# 生成服务器加密私钥 +tongsuo genpkey -algorithm SM2 -out server_enc.key + +# 生成服务器加密证书请求 +tongsuo req -new -key server_enc.key -out server_enc.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=localhost" + +# 使用CA签名服务器加密证书 +tongsuo x509 -req -in server_enc.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out server_enc.crt -days 365 +``` + +#### 4. 生成客户端证书(双向认证需要) + +```bash +# 生成客户端签名私钥 +tongsuo genpkey -algorithm SM2 -out client_sign.key + +# 生成客户端签名证书请求 +tongsuo req -new -key client_sign.key -out client_sign.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=client" + +# 使用CA签名客户端签名证书 +tongsuo x509 -req -in client_sign.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out client_sign.crt -days 365 + +# 生成客户端加密私钥和证书(类似步骤) +tongsuo genpkey -algorithm SM2 -out client_enc.key +tongsuo req -new -key client_enc.key -out client_enc.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=client" +tongsuo x509 -req -in client_enc.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out client_enc.crt -days 365 +``` + +#### 5. 清理临时文件 + +```bash +rm *.csr *.srl +``` + +**注意:** 生产环境请使用正式的CA机构签发的证书,并妥善保管私钥文件。 + +### 证书文件组织 + +建议的证书目录结构: + +``` +/usr/local/tongsuo/ssl/certs/sm2/ +├── ca.crt # CA根证书 +├── server_sign.crt # 服务器签名证书(TLCP) +├── server_sign.key # 服务器签名私钥(TLCP) +├── server_enc.crt # 服务器加密证书(TLCP) +├── server_enc.key # 服务器加密私钥(TLCP) +├── client_sign.crt # 客户端签名证书(双向认证) +├── client_sign.key # 客户端签名私钥(双向认证) +└── chain-ca.crt # CA证书链 +``` + +## 认证模式 + +### 单向认证 + +服务器提供证书,客户端验证服务器身份: + +- **适用场景**:大多数Web应用、API服务 +- **配置**:`enable_client_verify = false` +- **客户端**:只需要CA证书验证服务器 + +### 双向认证(Mutual Authentication) + +服务器和客户端都需要提供证书: + +- **适用场景**:高安全要求的系统、内网服务 +- **配置**:`enable_client_verify = true` +- **客户端**:需要提供客户端证书和私钥 + + +## 示例代码 + +完整的示例代码位于: + +- **RPC示例**:`src/coro_rpc/examples/base_examples/ntls_server.cpp` 和 `ntls_client.cpp` +- **HTTP示例**:`src/coro_http/examples/ntls_http_server.cpp` 和 `ntls_http_client.cpp` + +运行示例: + +```bash +# 编译示例 +cmake --build build --target ntls_server ntls_client + +# 运行服务器(后台) +./build/output/examples/coro_rpc/ntls_server & + +# 运行客户端 +./build/output/examples/coro_rpc/ntls_client +``` + +## 兼容性说明 + +### 默认行为 + +- **NTLS功能默认关闭**:不影响现有代码,需要显式启用 +- **条件编译**:通过 `OPENSSL_NO_NTLS` 宏控制,未启用NTLS时相关代码不会编译 + +### 与标准SSL共存 + +yaLanTingLibs 支持同时使用标准SSL和NTLS: + +- 标准SSL:使用OpenSSL,支持RSA/AES等国际算法 +- NTLS:使用Tongsuo,支持SM2/SM3/SM4国密算法 + +两者可以共存,通过不同的配置选择使用。 + +## 测试 + +yaLanTingLibs 提供了完整的NTLS测试: + +- **CI测试**:`.github/workflows/ntls_test.yml` 自动测试NTLS功能 +- **端到端测试**:`scripts/run_ntls_e2e_tests.sh` 验证完整的通信流程 +- **协议验证**:可以使用Tongsuo自带的命名行与ylt的rpc或者http进行测试,具体参考:https://www.yuque.com/tsdoc/ts/hedgqf 使用方法 + +## 相关资源 + +- **Tongsuo项目**:https://github.com/Tongsuo-Project/Tongsuo +- **GB/T 38636-2020标准**:国家密码管理局 +- **RFC 8998**:https://www.rfc-editor.org/rfc/rfc8998.html +- **yaLanTingLibs文档**:https://github.com/alibaba/yalantinglibs +- **国密TLCP使用手册**:https://www.yuque.com/tsdoc/ts/hedgqf +- **使用 Tongsuo 签发 SM2 双证书**:https://www.yuque.com/tsdoc/ts/sulazb +- - **在 TLS1.3 中使用商用密码算法**:https://www.yuque.com/tsdoc/ts/grur3x +## 总结 + +yaLanTingLibs 的NTLS支持为需要符合国密标准的应用提供了: + +✅ **完整的国密协议支持**:GB/T 38636-2020 TLCP 和 RFC 8998 +✅ **简单易用的API**:与标准SSL API保持一致 +✅ **生产就绪**:完整的测试、文档和示例 + +这使得 yaLanTingLibs 成为构建符合国密标准的高性能网络应用的理想选择。 + +--- + +**作者**:yaLanTingLibs 贡献者 +**最后更新**:2025年11月 + From 07c02ceea2f84efe6b57b0b9a2581f5549a5dc73 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 27 Nov 2025 11:41:26 +0800 Subject: [PATCH 17/38] Remove commented-out SSL NTLS initialization function in coro_rpc_server.hpp to clean up the codebase and improve readability. --- include/ylt/coro_rpc/impl/coro_rpc_server.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp index 718dcfdd6..44686cedd 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp @@ -140,10 +140,6 @@ class coro_rpc_server_base { void init_ntls(const ssl_ntls_configure &conf) { use_ssl_ = init_ntls_context_helper(context_, conf); } - - // void init_ssl_ntls(const ssl_ntls_configure &conf) { - // use_ssl_ = init_ssl_ntls_context_helper(context_, conf); - // } #endif // OPENSSL_NO_NTLS #endif #ifdef YLT_ENABLE_IBV From f76f58d416a6759a09c265ef4036d7b46e202993 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 27 Nov 2025 11:52:03 +0800 Subject: [PATCH 18/38] Code with comments removed --- include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp b/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp index db45caccb..4bd3fce15 100644 --- a/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp +++ b/include/ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp @@ -40,7 +40,6 @@ struct config_base { #ifdef YLT_ENABLE_SSL std::optional ssl_config = std::nullopt; #ifndef OPENSSL_NO_NTLS - // std::optional ntls_config = std::nullopt; std::optional ssl_ntls_config = std::nullopt; #endif // OPENSSL_NO_NTLS #endif From 506a43e8e1f110fc964f953bff4662c8f285ea79 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 27 Nov 2025 12:14:54 +0800 Subject: [PATCH 19/38] Add comprehensive NTLS support documentation for yaLanTingLibs, detailing integration with Tongsuo library, supported protocols, usage examples, and configuration steps. --- website/docs/zh/coro_http/coro_http_introduction.md | 9 +++++++++ .../docs/zh/coro_http/ntls_support.md | 8 +------- 2 files changed, 10 insertions(+), 7 deletions(-) rename docs/NTLS_Feature_Introduction.md => website/docs/zh/coro_http/ntls_support.md (99%) diff --git a/website/docs/zh/coro_http/coro_http_introduction.md b/website/docs/zh/coro_http/coro_http_introduction.md index bfc67c203..91d69df0a 100644 --- a/website/docs/zh/coro_http/coro_http_introduction.md +++ b/website/docs/zh/coro_http/coro_http_introduction.md @@ -212,6 +212,15 @@ void test_coro_http_client() { ``` 根据需要,一般情况下init_ssl()可以不调用。 +## 国密SSL(NTLS)支持 + +yaLanTingLibs 现已全面支持国密SSL(NTLS),通过集成 Tongsuo(铜锁)密码库,`coro_http` 和 `coro_rpc` 组件现在可以支持两种国密通信协议: + +- **GB/T 38636-2020 TLCP**:双证书国密通信协议 +- **RFC 8998**:TLS 1.3 + 国密单证书模式 + +详细使用说明请参考:[国密SSL(NTLS)支持文档](ntls_support.md) + ## http 先连接再请求 前面介绍的get/post 接口传入uri,在函数内部会自动去连接服务器并发请求,一次性完成了连接和请求,如果希望将连接和请求分开程两个阶段,那么就可以先调用connect 接口再调用async_get 接口。 diff --git a/docs/NTLS_Feature_Introduction.md b/website/docs/zh/coro_http/ntls_support.md similarity index 99% rename from docs/NTLS_Feature_Introduction.md rename to website/docs/zh/coro_http/ntls_support.md index 1a8fca569..02c0944dd 100644 --- a/docs/NTLS_Feature_Introduction.md +++ b/website/docs/zh/coro_http/ntls_support.md @@ -647,7 +647,7 @@ yaLanTingLibs 提供了完整的NTLS测试: - **yaLanTingLibs文档**:https://github.com/alibaba/yalantinglibs - **国密TLCP使用手册**:https://www.yuque.com/tsdoc/ts/hedgqf - **使用 Tongsuo 签发 SM2 双证书**:https://www.yuque.com/tsdoc/ts/sulazb -- - **在 TLS1.3 中使用商用密码算法**:https://www.yuque.com/tsdoc/ts/grur3x +- **在 TLS1.3 中使用商用密码算法**:https://www.yuque.com/tsdoc/ts/grur3x ## 总结 yaLanTingLibs 的NTLS支持为需要符合国密标准的应用提供了: @@ -657,9 +657,3 @@ yaLanTingLibs 的NTLS支持为需要符合国密标准的应用提供了: ✅ **生产就绪**:完整的测试、文档和示例 这使得 yaLanTingLibs 成为构建符合国密标准的高性能网络应用的理想选择。 - ---- - -**作者**:yaLanTingLibs 贡献者 -**最后更新**:2025年11月 - From e62f401e4c05834a47ae3cefde389821d9d0bd5f Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 20 Jan 2026 18:01:01 +0800 Subject: [PATCH 20/38] feat: add RSA mutual authentication support for RPC and HTTP --- include/ylt/coro_rpc/impl/common_service.hpp | 75 +++++++- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 84 ++++++++- .../standalone/cinatra/coro_http_client.hpp | 106 +++++++++++ .../cinatra/coro_http_connection.hpp | 71 ++++++- .../standalone/cinatra/coro_http_server.hpp | 11 +- src/coro_http/examples/CMakeLists.txt | 9 + src/coro_http/examples/http_ssl_client.cpp | 140 ++++++++++++++ src/coro_http/examples/http_ssl_server.cpp | 174 ++++++++++++++++++ .../examples/base_examples/CMakeLists.txt | 8 + .../examples/base_examples/rpc_ssl_client.cpp | 132 +++++++++++++ .../examples/base_examples/rpc_ssl_server.cpp | 153 +++++++++++++++ 11 files changed, 945 insertions(+), 18 deletions(-) create mode 100644 src/coro_http/examples/http_ssl_client.cpp create mode 100644 src/coro_http/examples/http_ssl_server.cpp create mode 100644 src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp create mode 100644 src/coro_rpc/examples/base_examples/rpc_ssl_server.cpp diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index fe655f14d..b143a240a 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -35,6 +35,10 @@ struct ssl_configure { std::string cert_file; //!< relative path of certificate chain file std::string key_file; //!< relative path of private key file std::string dh_file; //!< relative path of tmp dh file (optional) + std::string ca_cert_file; //!< relative path of CA certificate file for + //!< client verification (optional) + bool enable_client_verify = + false; //!< enable client certificate verification }; #ifdef YLT_ENABLE_NTLS ///*! @@ -57,7 +61,7 @@ struct ssl_configure { */ enum class ntls_mode { tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + - //!< encryption) + //!< encryption) tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate }; @@ -84,8 +88,8 @@ struct ssl_ntls_configure { // TLS 1.3 + GM single certificate configuration (RFC 8998) std::string gm_cert_file; //!< relative path of single SM2 certificate file //!< (for TLS 1.3 + GM) - std::string gm_key_file; //!< relative path of single SM2 private key file - //!< (for TLS 1.3 + GM) + std::string gm_key_file; //!< relative path of single SM2 private key file + //!< (for TLS 1.3 + GM) // Common NTLS configuration std::string @@ -172,6 +176,46 @@ inline bool init_ssl_context_helper(asio::ssl::context &context, ELOG_INFO << "no temp dh file " << dh_file.string(); } + // Load CA certificate for client verification if provided + asio::error_code ec; + if (!conf.ca_cert_file.empty()) { + auto ca_cert_file = fs::path(conf.base_path).append(conf.ca_cert_file); + if (file_exists(ca_cert_file)) { + context.load_verify_file(ca_cert_file.string(), ec); + if (ec) { + ELOG_ERROR << "failed to load CA certificate: " << ec.message(); + return false; + } + else { + ELOG_INFO << "loaded CA certificate: " << ca_cert_file.string(); + } + } + else { + ELOG_ERROR << "CA certificate file not found: " + << ca_cert_file.string(); + return false; + } + } + + // Set verification mode based on client verification configuration + if (conf.enable_client_verify) { + // Require client certificate and fail if not provided + context.set_verify_mode( + asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + else { + ELOG_INFO << "client certificate verification enabled (mandatory)"; + } + } + else { + context.set_verify_mode(asio::ssl::verify_none, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + } + return true; } catch (std::exception &e) { ELOG_INFO << e.what(); @@ -401,21 +445,34 @@ inline bool init_ntls_context_helper(asio::ssl::context &context, if (file_exists(ca_cert_file)) { context.load_verify_file(ca_cert_file.string(), ec); if (ec) { - ELOG_WARN << "failed to load CA certificate: " << ec.message() - << ", continuing without it"; + ELOG_ERROR << "failed to load CA certificate: " << ec.message(); + return false; } } + else { + ELOG_ERROR << "CA certificate file not found: " + << ca_cert_file.string(); + return false; + } } // Set verification mode if (conf.enable_client_verify) { - context.set_verify_mode(asio::ssl::verify_peer, ec); + // Require client certificate and fail if not provided + context.set_verify_mode( + asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + else { + ELOG_INFO << "client certificate verification enabled (mandatory)"; + } } else { context.set_verify_mode(asio::ssl::verify_none, ec); - } - if (ec) { - ELOG_WARN << "failed to set verify mode: " << ec.message(); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } } ELOG_INFO << "NTLS server context initialized successfully"; diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index 186355385..f87d25f33 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -182,8 +182,13 @@ class coro_rpc_client { #ifdef YLT_ENABLE_SSL struct tcp_with_ssl_config { bool enable_tcp_no_delay = true; - std::filesystem::path ssl_cert_path{}; + std::filesystem::path + ssl_cert_path{}; // CA certificate for server verification std::string ssl_domain{}; + std::filesystem::path + client_cert_file{}; // Client certificate for mutual authentication + std::filesystem::path + client_key_file{}; // Client private key for mutual authentication }; #ifdef YLT_ENABLE_NTLS struct tcp_with_ntls_config { @@ -299,6 +304,41 @@ class coro_rpc_client { ELOG_INFO << "no certificate file " << cert_file.string(); return ssl_init_ret_; } + + // Load client certificate and key for mutual authentication + if (!config.client_cert_file.empty() || !config.client_key_file.empty()) { + // Check if both certificate and key are provided + if (config.client_cert_file.empty() || config.client_key_file.empty()) { + ELOG_ERROR << "Both client certificate and key must be provided for " + "mutual authentication"; + return ssl_init_ret_; + } + + if (file_exists(config.client_cert_file)) { + ELOG_INFO << "load client certificate: " + << config.client_cert_file.string(); + ssl_ctx_.use_certificate_chain_file(config.client_cert_file.string()); + } + else { + ELOG_ERROR << "client certificate file not found: " + << config.client_cert_file.string(); + return ssl_init_ret_; + } + + if (file_exists(config.client_key_file)) { + ELOG_INFO << "load client private key: " + << config.client_key_file.string(); + ssl_ctx_.use_private_key_file(config.client_key_file.string(), + asio::ssl::context::pem); + } + else { + ELOG_ERROR << "client key file not found: " + << config.client_key_file.string(); + return ssl_init_ret_; + } + ELOG_INFO << "client certificate loaded for mutual authentication"; + } + ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); ssl_ctx_.set_verify_callback( asio::ssl::host_name_verification(config.ssl_domain)); @@ -644,6 +684,48 @@ class coro_rpc_client { return init_socket_wrapper( std::get(config_.socket_config)); } + + /*! + * Initialize SSL with client certificate for mutual authentication + * @param cert_base_path Base path for certificate files + * @param cert_file_name CA certificate file name for server verification + * @param client_cert_file Client certificate file name for mutual + * authentication + * @param client_key_file Client private key file name for mutual + * authentication + * @param domain Server domain name + * @return true if initialization successful + */ + [[nodiscard]] bool init_ssl(std::string_view cert_base_path, + std::string_view cert_file_name, + std::string_view client_cert_file, + std::string_view client_key_file, + std::string_view domain = "localhost") { + std::string ssl_domain = std::string{domain}; + std::string ssl_cert_path = + std::filesystem::path(cert_base_path).append(cert_file_name).string(); + std::string ssl_client_cert_path = + std::filesystem::path(cert_base_path).append(client_cert_file).string(); + std::string ssl_client_key_path = + std::filesystem::path(cert_base_path).append(client_key_file).string(); + + if (config_.socket_config.index() != 1) { + config_.socket_config = tcp_with_ssl_config{ + .ssl_cert_path = std::move(ssl_cert_path), + .ssl_domain = std::move(ssl_domain), + .client_cert_file = std::move(ssl_client_cert_path), + .client_key_file = std::move(ssl_client_key_path)}; + } + else { + auto &conf = std::get(config_.socket_config); + conf.ssl_cert_path = std::move(ssl_cert_path); + conf.ssl_domain = std::move(ssl_domain); + conf.client_cert_file = std::move(ssl_client_cert_path); + conf.client_key_file = std::move(ssl_client_key_path); + } + return init_socket_wrapper( + std::get(config_.socket_config)); + } #ifdef YLT_ENABLE_NTLS [[nodiscard]] bool init_ntls(const ssl_ntls_configure &conf) { if (conf.mode == ntls_mode::tls13_single_cert) { diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index e9e50214f..88f5ed728 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -295,6 +295,112 @@ class coro_http_client : public std::enable_shared_from_this { } return init_ssl(verify_mode, base_path, cert_file, sni_hostname); } + + /*! + * Initialize SSL with client certificate for mutual authentication + * @param verify_mode SSL verify mode (e.g., asio::ssl::verify_peer) + * @param base_path Base path for certificate files + * @param cert_file CA certificate file name for server verification + * @param client_cert_file Client certificate file name for mutual + * authentication + * @param client_key_file Client private key file name for mutual + * authentication + * @param sni_hostname SNI hostname + * @return true if initialization successful + */ + [[nodiscard]] bool init_ssl(int verify_mode, const std::string &base_path, + const std::string &cert_file, + const std::string &client_cert_file, + const std::string &client_key_file, + const std::string &sni_hostname) { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique(asio::ssl::context::tls); + + // Set cipher list for OpenSSL 3.0 compatibility + SSL_CTX *native_ctx = ssl_ctx_->native_handle(); + if (native_ctx) { + // const char* ciphers = "HIGH:!aNULL:!MD5:!3DES"; + // SSL_CTX_set_cipher_list(native_ctx, ciphers); + SSL_CTX_set_min_proto_version(native_ctx, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(native_ctx, TLS1_3_VERSION); + } + + auto full_cert_file = std::filesystem::path(base_path).append(cert_file); + if (std::filesystem::exists(full_cert_file)) { + ssl_ctx_->load_verify_file(full_cert_file.string()); + CINATRA_LOG_INFO << "loaded CA certificate: " + << full_cert_file.string(); + } + else { + if (!base_path.empty() || !cert_file.empty()) { + CINATRA_LOG_ERROR << "CA certificate file not found: " + << full_cert_file.string(); + return false; + } + } + + if (base_path.empty() && cert_file.empty()) { + ssl_ctx_->set_default_verify_paths(); + } + + // Load client certificate and key for mutual authentication + auto full_client_cert_file = + std::filesystem::path(base_path).append(client_cert_file); + auto full_client_key_file = + std::filesystem::path(base_path).append(client_key_file); + + if (std::filesystem::exists(full_client_cert_file)) { + ssl_ctx_->use_certificate_chain_file(full_client_cert_file.string()); + CINATRA_LOG_INFO << "loaded client certificate: " + << full_client_cert_file.string(); + } + else { + CINATRA_LOG_ERROR << "client certificate file not found: " + << full_client_cert_file.string(); + return false; + } + + if (std::filesystem::exists(full_client_key_file)) { + ssl_ctx_->use_private_key_file(full_client_key_file.string(), + asio::ssl::context::pem); + CINATRA_LOG_INFO << "loaded client private key: " + << full_client_key_file.string(); + } + else { + CINATRA_LOG_ERROR << "client key file not found: " + << full_client_key_file.string(); + return false; + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + if (!sni_hostname.empty()) { + ssl_ctx_->set_verify_callback( + asio::ssl::host_name_verification(sni_hostname)); + + if (need_set_sni_host_) { + SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), + sni_hostname.c_str()); + } + } + + has_init_ssl_ = true; + CINATRA_LOG_INFO << "SSL initialized with client certificate for mutual " + "authentication"; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); + return false; + } + return true; + } #endif // return body_, the user will own body's lifetime. diff --git a/include/ylt/standalone/cinatra/coro_http_connection.hpp b/include/ylt/standalone/cinatra/coro_http_connection.hpp index bdb376741..96de8014a 100644 --- a/include/ylt/standalone/cinatra/coro_http_connection.hpp +++ b/include/ylt/standalone/cinatra/coro_http_connection.hpp @@ -63,7 +63,8 @@ class coro_http_connection return false; } bool init_ssl(const std::string &cert_file, const std::string &key_file, - std::string passwd) { + std::string passwd, const std::string &ca_cert_file = "", + bool enable_client_verify = false) { unsigned long ssl_options = asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::single_dh_use; @@ -88,6 +89,48 @@ class coro_http_connection asio::ssl::context::pem); } + // Load CA certificate for client verification if provided + if (!ca_cert_file.empty()) { + if (fs::exists(ca_cert_file, ec)) { + ssl_ctx_->load_verify_file(ca_cert_file, ec); + if (ec) { + CINATRA_LOG_ERROR + << "failed to load CA certificate: " << ca_cert_file + << ", error: " << ec.message(); + return false; + } + else { + CINATRA_LOG_INFO << "loaded CA certificate: " << ca_cert_file; + } + } + else { + CINATRA_LOG_ERROR << "CA certificate file not found: " + << ca_cert_file; + return false; + } + } + + // Set verification mode based on client verification configuration + if (enable_client_verify) { + // Require client certificate and fail if not provided + ssl_ctx_->set_verify_mode( + asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, + ec); + if (ec) { + CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); + } + else { + CINATRA_LOG_INFO + << "client certificate verification enabled (mandatory)"; + } + } + else { + ssl_ctx_->set_verify_mode(asio::ssl::verify_none, ec); + if (ec) { + CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); + } + } + socket_wrapper_.ssl_stream() = std::make_unique>( *socket_wrapper_.socket(), *ssl_ctx_); @@ -380,23 +423,39 @@ class coro_http_connection asio::error_code ec; ssl_ctx_->load_verify_file(ca_cert_path.string(), ec); if (ec) { - CINATRA_LOG_WARNING + CINATRA_LOG_ERROR << "failed to load CA certificate: " << ca_cert_path.string() << ", error: " << ec.message(); + return false; } } + else { + CINATRA_LOG_ERROR << "CA certificate file not found: " + << ca_cert_path.string(); + return false; + } } // Set verification mode asio::error_code ec; if (ntls_config.enable_client_verify) { - ssl_ctx_->set_verify_mode(asio::ssl::verify_peer, ec); + // Require client certificate and fail if not provided + ssl_ctx_->set_verify_mode( + asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, + ec); + if (ec) { + CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); + } + else { + CINATRA_LOG_INFO + << "client certificate verification enabled (mandatory)"; + } } else { ssl_ctx_->set_verify_mode(asio::ssl::verify_none, ec); - } - if (ec) { - CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); + if (ec) { + CINATRA_LOG_WARNING << "failed to set verify mode: " << ec.message(); + } } // Create SSL stream diff --git a/include/ylt/standalone/cinatra/coro_http_server.hpp b/include/ylt/standalone/cinatra/coro_http_server.hpp index 8499ce57f..e4186e558 100644 --- a/include/ylt/standalone/cinatra/coro_http_server.hpp +++ b/include/ylt/standalone/cinatra/coro_http_server.hpp @@ -70,10 +70,14 @@ class coro_http_server { #ifdef CINATRA_ENABLE_SSL void init_ssl(const std::string &cert_file, const std::string &key_file, - const std::string &passwd) { + const std::string &passwd, + const std::string &ca_cert_file = "", + bool enable_client_verify = false) { cert_file_ = cert_file; key_file_ = key_file; passwd_ = passwd; + ca_cert_file_ = ca_cert_file; + enable_client_verify_ = enable_client_verify; use_ssl_ = true; } @@ -810,7 +814,8 @@ class coro_http_server { #ifdef CINATRA_ENABLE_SSL if (!is_transfer_connect && use_ssl_) { - conn->init_ssl(cert_file_, key_file_, passwd_); + conn->init_ssl(cert_file_, key_file_, passwd_, ca_cert_file_, + enable_client_verify_); } #ifdef YLT_ENABLE_NTLS else if (!is_transfer_connect && use_ntls_) { @@ -1175,6 +1180,8 @@ class coro_http_server { std::string cert_file_; std::string key_file_; std::string passwd_; + std::string ca_cert_file_; + bool enable_client_verify_ = false; bool use_ssl_ = false; #ifdef YLT_ENABLE_NTLS bool use_ntls_ = false; diff --git a/src/coro_http/examples/CMakeLists.txt b/src/coro_http/examples/CMakeLists.txt index ebe1d34d7..b586c25f3 100644 --- a/src/coro_http/examples/CMakeLists.txt +++ b/src/coro_http/examples/CMakeLists.txt @@ -38,6 +38,15 @@ if(YLT_ENABLE_NTLS) target_compile_definitions(ntls_http_server PRIVATE YLT_ENABLE_NTLS) target_compile_definitions(ntls_http_client PRIVATE YLT_ENABLE_NTLS) endif() + +if(YLT_ENABLE_SSL) + message(STATUS "Building SSL examples with mutual authentication support") + add_executable(http_ssl_server http_ssl_server.cpp) + add_executable(http_ssl_client http_ssl_client.cpp) + target_compile_definitions(http_ssl_server PRIVATE YLT_ENABLE_SSL) + target_compile_definitions(http_ssl_client PRIVATE YLT_ENABLE_SSL) +endif() + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 target_link_libraries(coro_http_example PRIVATE ws2_32 mswsock) target_link_libraries(coro_http_load_balancer PRIVATE ws2_32 mswsock) diff --git a/src/coro_http/examples/http_ssl_client.cpp b/src/coro_http/examples/http_ssl_client.cpp new file mode 100644 index 000000000..85c58ddab --- /dev/null +++ b/src/coro_http/examples/http_ssl_client.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +using namespace cinatra; + +// Certificate paths - adjust these paths to your certificate files +const std::string CERT_PATH = "./certs/rsacerts/"; +const std::string CA_CERT = + "chain-ca.crt"; // Root CA certificate to verify server +const std::string CLIENT_CERT = + "client_sign.crt"; // Client certificate for mutual auth +const std::string CLIENT_KEY = + "client_sign.key"; // Client private key for mutual auth + +// Test one-way authentication (server certificate only) +void test_one_way_auth() { + try { + coro_http_client client; + + // Initialize SSL with CA certificate for server verification + bool init_result = + client.init_ssl(asio::ssl::verify_peer, // Verify server certificate + CERT_PATH, // Base path + CA_CERT, // CA certificate file + "127.0.0.1" // SNI hostname + ); + + if (!init_result) { + std::cout << "Failed to initialize SSL client" << std::endl; + return; + } + + // Test GET request + auto result = client.get("https://127.0.0.1:9001/"); + if (result.status == 200) { + std::cout << "GET / successful: " << result.resp_body << std::endl; + } + else { + std::cout << "GET / failed with status: " << result.status << std::endl; + } + + // Test POST request + auto post_result = + client.post("https://127.0.0.1:9001/echo", "Hello from one-way client", + req_content_type::text); + if (post_result.status == 200) { + std::cout << "POST /echo successful: " << post_result.resp_body + << std::endl; + } + else { + std::cout << "POST /echo failed with status: " << post_result.status + << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "One-way auth client error: " << e.what() << std::endl; + } +} + +// Test mutual authentication (client certificate required) +void test_mutual_auth() { + try { + coro_http_client client; + + // Initialize SSL with CA certificate AND client certificate for mutual auth + bool init_result = + client.init_ssl(asio::ssl::verify_peer, // Verify server certificate + CERT_PATH, // Base path + CA_CERT, // CA certificate for server verification + CLIENT_CERT, // Client certificate + CLIENT_KEY, // Client private key + "127.0.0.1" // SNI hostname + ); + + if (!init_result) { + std::cout << "Failed to initialize SSL client with mutual authentication" + << std::endl; + return; + } + + // Test GET request + auto result = client.get("https://127.0.0.1:9002/"); + if (result.status == 200) { + std::cout << "GET / successful: " << result.resp_body << std::endl; + } + else { + std::cout << "GET / failed with status: " << result.status << std::endl; + } + + // Test POST request + auto post_result = + client.post("https://127.0.0.1:9002/echo", + "Hello from mutual auth client", req_content_type::text); + if (post_result.status == 200) { + std::cout << "POST /echo successful: " << post_result.resp_body + << std::endl; + } + else { + std::cout << "POST /echo failed with status: " << post_result.status + << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "Mutual auth client error: " << e.what() << std::endl; + } +} + +int main() { + std::cout << "SSL HTTP Client Example" << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + std::cout << "\nMake sure the server is running before starting the client" + << std::endl; + std::cout << "\nTesting one-way authentication..." << std::endl; + + // Test one-way authentication + test_one_way_auth(); + + std::cout << "\nTesting mutual authentication..." << std::endl; + + // Test mutual authentication + test_mutual_auth(); + + return 0; +} diff --git a/src/coro_http/examples/http_ssl_server.cpp b/src/coro_http/examples/http_ssl_server.cpp new file mode 100644 index 000000000..89e46d22f --- /dev/null +++ b/src/coro_http/examples/http_ssl_server.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +using namespace cinatra; + +// Certificate paths - adjust these paths to your certificate files +const std::string CERT_PATH = "./certs/rsacerts/"; +const std::string SERVER_CERT = + "server_sign-subca.crt"; // RSA server certificate with full chain +const std::string SERVER_KEY = "server_sign.key"; // RSA server private key +const std::string CA_CERT = + "chain-ca.crt"; // CA certificate for client verification + +// Start one-way authentication SSL server (server certificate only) +void start_one_way_server() { + std::cout << "Starting SSL HTTP Server (one-way auth) on port 9001..." + << std::endl; + + coro_http_server server(1, 9001); + + // Initialize SSL server (one-way authentication) + server.init_ssl((CERT_PATH + SERVER_CERT), // Server certificate + (CERT_PATH + SERVER_KEY), // Server private key + "", // No password + "", // No CA cert for one-way + false // Don't verify client + ); + + // Root endpoint + server.set_http_handler( + "/", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content(status_type::ok, + "Hello World from SSL server (one-way)"); + co_return; + }); + + // Echo endpoint + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + std::string_view body = req.get_body(); + resp.set_status_and_content(status_type::ok, + "Echo: " + std::string(body)); + co_return; + }); + + std::cout << "One-way SSL server ready. Test endpoints:" << std::endl; + std::cout << " GET https://localhost:9001/" << std::endl; + std::cout << " POST https://localhost:9001/echo" << std::endl; + + auto result = server.sync_start(); + if (result) { + std::cerr << "Failed to start one-way server: " << result.message() + << std::endl; + } +} + +// Start mutual authentication SSL server (requires client certificate) +void start_mutual_auth_server() { + std::cout << "Starting SSL HTTP Server (mutual auth) on port 9002..." + << std::endl; + + coro_http_server server(1, 9002); + + // Initialize SSL server (mutual authentication) + server.init_ssl( + (CERT_PATH + SERVER_CERT), // Server certificate + (CERT_PATH + SERVER_KEY), // Server private key + "", // No password + (CERT_PATH + CA_CERT), // CA certificate for verifying clients + true // Verify client certificate (mandatory) + ); + + // Root endpoint + server.set_http_handler( + "/", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + resp.set_status_and_content( + status_type::ok, "Hello World from SSL server (mutual auth)"); + co_return; + }); + + // Echo endpoint + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + std::string_view body = req.get_body(); + resp.set_status_and_content(status_type::ok, + "Echo: " + std::string(body)); + co_return; + }); + + std::cout << "Mutual auth SSL server ready. Test endpoints:" << std::endl; + std::cout << " GET https://localhost:9002/" << std::endl; + std::cout << " POST https://localhost:9002/echo" << std::endl; + std::cout << "Note: Client certificate is REQUIRED for this server" + << std::endl; + + auto result = server.sync_start(); + if (result) { + std::cerr << "Failed to start mutual auth server: " << result.message() + << std::endl; + } +} + +int main() { + std::cout << "SSL HTTP Server Example - Starting two servers:" << std::endl; + std::cout << "1. One-way authentication (9001) - Server certificate only" + << std::endl; + std::cout << "2. Mutual authentication (9002) - Requires client certificate" + << std::endl; + std::cout << "\nMake sure to create the following certificate files in " + << CERT_PATH << ":" << std::endl; + std::cout << " - server.crt: Server certificate" << std::endl; + std::cout << " - server.key: Server private key" << std::endl; + std::cout << " - ca.crt: CA certificate (for mutual authentication)" + << std::endl; + std::cout << "\nYou can generate test certificates using OpenSSL:" + << std::endl; + std::cout << " # Generate CA certificate" << std::endl; + std::cout << " openssl genrsa -out ca.key 2048" << std::endl; + std::cout << " openssl req -new -x509 -days 365 -key ca.key -out ca.crt" + << std::endl; + std::cout << "\n # Generate server certificate" << std::endl; + std::cout << " openssl genrsa -out server.key 2048" << std::endl; + std::cout << " openssl req -new -key server.key -out server.csr" + << std::endl; + std::cout << " openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey " + "ca.key -CAcreateserial -out server.crt" + << std::endl; + std::cout << "\n # Generate client certificate (for mutual authentication)" + << std::endl; + std::cout << " openssl genrsa -out client.key 2048" << std::endl; + std::cout << " openssl req -new -key client.key -out client.csr" + << std::endl; + std::cout << " openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey " + "ca.key -CAcreateserial -out client.crt" + << std::endl; + + // Start servers in separate threads + std::thread one_way_thread(start_one_way_server); + std::thread mutual_auth_thread(start_mutual_auth_server); + + // Wait for all servers + one_way_thread.join(); + mutual_auth_thread.join(); + + return 0; +} diff --git a/src/coro_rpc/examples/base_examples/CMakeLists.txt b/src/coro_rpc/examples/base_examples/CMakeLists.txt index 8616136cc..c612279d1 100644 --- a/src/coro_rpc/examples/base_examples/CMakeLists.txt +++ b/src/coro_rpc/examples/base_examples/CMakeLists.txt @@ -78,6 +78,14 @@ if(YLT_ENABLE_NTLS) target_compile_definitions(ntls_server PRIVATE YLT_ENABLE_NTLS) target_compile_definitions(ntls_client PRIVATE YLT_ENABLE_NTLS) endif() + +if(YLT_ENABLE_SSL) + message(STATUS "Building SSL examples with mutual authentication support") + add_executable(rpc_ssl_server rpc_ssl_server.cpp) + add_executable(rpc_ssl_client rpc_ssl_client.cpp) + target_compile_definitions(rpc_ssl_server PRIVATE YLT_ENABLE_SSL) + target_compile_definitions(rpc_ssl_client PRIVATE YLT_ENABLE_SSL) +endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 target_link_libraries(coro_rpc_example_load_balancer PRIVATE ws2_32 mswsock) target_link_libraries(coro_rpc_example_client_pool PRIVATE ws2_32 mswsock) diff --git a/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp b/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp new file mode 100644 index 000000000..b1d5b48b7 --- /dev/null +++ b/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +using namespace coro_rpc; +using namespace async_simple::coro; + +// Certificate paths - adjust these paths to your certificate files +const std::string CERT_PATH = "./certs/rsacerts"; +const std::string CA_CERT = + "chain-ca.crt"; // Root CA certificate to verify server +const std::string CLIENT_CERT = + "client_sign.crt"; // Client certificate for mutual auth +const std::string CLIENT_KEY = + "client_sign.key"; // Client private key for mutual auth + +// Declare RPC function (must match server side) +std::string echo(std::string_view data); + +// Test one-way authentication (server certificate only) +Lazy test_one_way_auth() { + try { + coro_rpc_client client; + + // Initialize SSL with CA certificate for server verification + bool init_result = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); + if (!init_result) { + std::cout << "Failed to initialize SSL client" << std::endl; + co_return; + } + + // Connect to server + auto ec = co_await client.connect("127.0.0.1", "9001"); + if (ec) { + std::cout << "Failed to connect to server: " << ec.message() << std::endl; + co_return; + } + + std::cout << "Connected to one-way auth server" << std::endl; + + // Call RPC function + auto result = co_await client.call("Hello from one-way client"); + if (result) { + std::cout << "RPC call successful: " << result.value() << std::endl; + } + else { + std::cout << "RPC call failed: " << result.error().msg << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "One-way auth client error: " << e.what() << std::endl; + } +} + +// Test mutual authentication (client certificate required) +Lazy test_mutual_auth() { + try { + coro_rpc_client client; + + // Initialize SSL with CA certificate AND client certificate for mutual auth + bool init_result = + client.init_ssl(CERT_PATH, // Base path + CA_CERT, // CA certificate for server verification + CLIENT_CERT, // Client certificate + CLIENT_KEY, // Client private key + "127.0.0.1" // Server hostname + ); + + if (!init_result) { + std::cout << "Failed to initialize SSL client with mutual authentication" + << std::endl; + co_return; + } + + // Connect to server + auto ec = co_await client.connect("127.0.0.1", "9002"); + if (ec) { + std::cout << "Failed to connect to mutual auth server: " << ec.message() + << std::endl; + co_return; + } + + std::cout << "Connected to mutual auth server" << std::endl; + + // Call RPC function + auto result = co_await client.call("Hello from mutual auth client"); + if (result) { + std::cout << "RPC call successful: " << result.value() << std::endl; + } + else { + std::cout << "RPC call failed: " << result.error().msg << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "Mutual auth client error: " << e.what() << std::endl; + } +} + +int main() { + std::cout << "SSL RPC Client Example" << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + std::cout << "\nMake sure the server is running before starting the client" + << std::endl; + std::cout << "\nTesting one-way authentication..." << std::endl; + + // Test one-way authentication + syncAwait(test_one_way_auth()); + + std::cout << "\nTesting mutual authentication..." << std::endl; + + // Test mutual authentication + syncAwait(test_mutual_auth()); + + return 0; +} diff --git a/src/coro_rpc/examples/base_examples/rpc_ssl_server.cpp b/src/coro_rpc/examples/base_examples/rpc_ssl_server.cpp new file mode 100644 index 000000000..47d497870 --- /dev/null +++ b/src/coro_rpc/examples/base_examples/rpc_ssl_server.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace coro_rpc; +using namespace std::string_literals; + +// Certificate paths - adjust these paths to your certificate files +const std::string CERT_PATH = "./certs/rsacerts"; +const std::string SERVER_CERT = + "server_sign-subca.crt"; // RSA server certificate with full chain +const std::string SERVER_KEY = "server_sign.key"; // RSA server private key +const std::string CA_CERT = + "chain-ca.crt"; // CA certificate for client verification + +// Simple RPC service function +std::string echo(std::string_view data) { + std::cout << "Server received: " << data << std::endl; + return "Hello World from SSL server"; +} + +// Start one-way authentication server (server certificate only) +void start_one_way_server() { + try { + coro_rpc_server server(std::thread::hardware_concurrency(), 9001); + + // Configure SSL (one-way authentication) + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = ""; // No CA cert needed for one-way + ssl_conf.enable_client_verify = false; // Disable client verification + + server.init_ssl(ssl_conf); + server.register_handler(); + + std::cout << "SSL RPC Server (one-way auth) starting on port 9001..." + << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + + auto result = server.start(); + if (result) { + std::cout << "One-way auth server started successfully!" << std::endl; + } + else { + std::cout << "Failed to start one-way auth server: " << result.message() + << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "One-way auth server error: " << e.what() << std::endl; + } +} + +// Start mutual authentication server (client certificate required) +void start_mutual_auth_server() { + try { + coro_rpc_server server(std::thread::hardware_concurrency(), 9002); + + // Configure SSL (mutual authentication) + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; // CA certificate for verifying clients + ssl_conf.enable_client_verify = + true; // Enable client certificate verification + + server.init_ssl(ssl_conf); + server.register_handler(); + + std::cout << "SSL RPC Server (mutual auth) starting on port 9002..." + << std::endl; + std::cout << "Certificate path: " << CERT_PATH << std::endl; + std::cout << "Client certificate verification: ENABLED (mandatory)" + << std::endl; + + auto result = server.start(); + if (result) { + std::cout << "Mutual auth server started successfully!" << std::endl; + } + else { + std::cout << "Failed to start mutual auth server: " << result.message() + << std::endl; + } + + } catch (const std::exception& e) { + std::cout << "Mutual auth server error: " << e.what() << std::endl; + } +} + +int main() { + std::cout << "SSL RPC Server Example - Starting two servers:" << std::endl; + std::cout << "1. One-way authentication (9001) - Server certificate only" + << std::endl; + std::cout << "2. Mutual authentication (9002) - Requires client certificate" + << std::endl; + std::cout << "\nMake sure to create the following certificate files in " + << CERT_PATH << ":" << std::endl; + std::cout << " - server.crt: Server certificate" << std::endl; + std::cout << " - server.key: Server private key" << std::endl; + std::cout << " - ca.crt: CA certificate (for mutual authentication)" + << std::endl; + std::cout << "\nYou can generate test certificates using OpenSSL:" + << std::endl; + std::cout << " # Generate CA certificate" << std::endl; + std::cout << " openssl genrsa -out ca.key 2048" << std::endl; + std::cout << " openssl req -new -x509 -days 365 -key ca.key -out ca.crt" + << std::endl; + std::cout << "\n # Generate server certificate" << std::endl; + std::cout << " openssl genrsa -out server.key 2048" << std::endl; + std::cout << " openssl req -new -key server.key -out server.csr" + << std::endl; + std::cout << " openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey " + "ca.key -CAcreateserial -out server.crt" + << std::endl; + std::cout << "\n # Generate client certificate (for mutual authentication)" + << std::endl; + std::cout << " openssl genrsa -out client.key 2048" << std::endl; + std::cout << " openssl req -new -key client.key -out client.csr" + << std::endl; + std::cout << " openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey " + "ca.key -CAcreateserial -out client.crt" + << std::endl; + + // Start servers in separate threads + std::thread one_way_thread(start_one_way_server); + std::thread mutual_auth_thread(start_mutual_auth_server); + + // Wait for all servers + one_way_thread.join(); + mutual_auth_thread.join(); + + return 0; +} From 582666708d47296144da29882b6f6592d2bc477e Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 3 Feb 2026 17:18:16 +0800 Subject: [PATCH 21/38] feat:add test and doc --- src/coro_http/tests/CMakeLists.txt | 1 + .../tests/test_http_ssl_mutual_auth.cpp | 199 ++++++++++++++++++ .../examples/base_examples/CMakeLists.txt | 19 ++ .../examples/base_examples/rpc_ssl_client.cpp | 8 +- src/coro_rpc/tests/CMakeLists.txt | 1 + .../tests/test_rpc_ssl_mutual_auth.cpp | 186 ++++++++++++++++ website/docs/en/coro_rpc/coro_rpc_client.md | 85 ++++++-- 7 files changed, 475 insertions(+), 24 deletions(-) create mode 100644 src/coro_http/tests/test_http_ssl_mutual_auth.cpp create mode 100644 src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp diff --git a/src/coro_http/tests/CMakeLists.txt b/src/coro_http/tests/CMakeLists.txt index b1061183a..a1a0daade 100644 --- a/src/coro_http/tests/CMakeLists.txt +++ b/src/coro_http/tests/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(coro_http_test test_cinatra_websocket.cpp test_http_parse.cpp test_http_client_filter.cpp + test_http_ssl_mutual_auth.cpp main.cpp ) diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp new file mode 100644 index 000000000..56e35ea42 --- /dev/null +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +using namespace cinatra; + +const std::string CERT_PATH = "../openssl_files/"; +const std::string SERVER_CERT = "server.crt"; +const std::string SERVER_KEY = "server.key"; +const std::string CLIENT_CERT = "client.crt"; +const std::string CLIENT_KEY = "client.key"; +const std::string CA_CERT = "ca.crt"; + +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing HTTP SSL one-way authentication") { + coro_http_server server(1, 8901); + + bool init_ok = server.init_ssl((CERT_PATH + SERVER_CERT), + (CERT_PATH + SERVER_KEY), "", "", false); + REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello World"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("http://127.0.0.1:8901/"); + REQUIRE_MESSAGE(result.status == 200, "GET request failed"); + REQUIRE_MESSAGE(result.resp_body == "Hello World", "response body mismatch"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - success") { + coro_http_server server(1, 8902); + + bool init_ok = + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello Mutual Auth"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("http://127.0.0.1:8902/"); + REQUIRE_MESSAGE(result.status == 200, "GET request failed"); + REQUIRE_MESSAGE(result.resp_body == "Hello Mutual Auth", + "response body mismatch"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { + coro_http_server server(1, 8903); + + bool init_ok = + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Should not reach here"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("http://127.0.0.1:8903/"); + REQUIRE_MESSAGE(result.status != 200, + "request should fail without client cert"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { + coro_http_server server(1, 8904); + + bool init_ok = + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Should not reach here"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + "invalid_client.crt", "invalid_client.key", "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl should succeed"); + + auto result = client.get("http://127.0.0.1:8904/"); + REQUIRE_MESSAGE(result.status != 200, + "request should fail with invalid client cert"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - POST request") { + coro_http_server server(1, 8905); + + bool init_ok = + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + std::string_view body = req.get_body(); + resp.set_status_and_content(status_type::ok, + "Echo: " + std::string(body)); + co_return; + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.post("http://127.0.0.1:8905/echo", "Test Message", + req_content_type::text); + REQUIRE_MESSAGE(result.status == 200, "POST request failed"); + REQUIRE_MESSAGE(result.resp_body == "Echo: Test Message", + "response body mismatch"); + + server.stop(); + thd.join(); +} +#endif diff --git a/src/coro_rpc/examples/base_examples/CMakeLists.txt b/src/coro_rpc/examples/base_examples/CMakeLists.txt index c612279d1..e1ec46777 100644 --- a/src/coro_rpc/examples/base_examples/CMakeLists.txt +++ b/src/coro_rpc/examples/base_examples/CMakeLists.txt @@ -97,5 +97,24 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows" target_link_libraries(ntls_server PRIVATE ws2_32 mswsock) target_link_libraries(ntls_client PRIVATE ws2_32 mswsock) endif() + if(YLT_ENABLE_SSL) + target_link_libraries(rpc_ssl_server PRIVATE ws2_32 mswsock) + target_link_libraries(rpc_ssl_client PRIVATE ws2_32 mswsock) + endif() +endif() + +# Windows (MSVC) system libraries for SSL/TLS +if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + if(YLT_ENABLE_NTLS OR YLT_ENABLE_SSL) + # Tongsuo on Windows requires system crypto libraries + if(YLT_ENABLE_NTLS) + target_link_libraries(ntls_server PRIVATE crypt32) + target_link_libraries(ntls_client PRIVATE crypt32) + endif() + if(YLT_ENABLE_SSL) + target_link_libraries(rpc_ssl_server PRIVATE crypt32) + target_link_libraries(rpc_ssl_client PRIVATE crypt32) + endif() + endif() endif() diff --git a/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp b/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp index b1d5b48b7..83c1aba7b 100644 --- a/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp +++ b/src/coro_rpc/examples/base_examples/rpc_ssl_client.cpp @@ -76,11 +76,11 @@ Lazy test_mutual_auth() { // Initialize SSL with CA certificate AND client certificate for mutual auth bool init_result = - client.init_ssl(CERT_PATH, // Base path - CA_CERT, // CA certificate for server verification + client.init_ssl(CERT_PATH, // Base path + CA_CERT, // CA certificate for server verification CLIENT_CERT, // Client certificate - CLIENT_KEY, // Client private key - "127.0.0.1" // Server hostname + CLIENT_KEY, // Client private key + "127.0.0.1" // Server hostname ); if (!init_result) { diff --git a/src/coro_rpc/tests/CMakeLists.txt b/src/coro_rpc/tests/CMakeLists.txt index c9cf28700..8c64dc427 100644 --- a/src/coro_rpc/tests/CMakeLists.txt +++ b/src/coro_rpc/tests/CMakeLists.txt @@ -12,6 +12,7 @@ set(TEST_SRCS test_parallel.cpp test_client_filter.cpp test_abi_compatible.cpp + test_rpc_ssl_mutual_auth.cpp ) set(TEST_COMMON rpc_api.cpp diff --git a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp new file mode 100644 index 000000000..d3382638b --- /dev/null +++ b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +using namespace coro_rpc; +using namespace async_simple::coro; + +const std::string CERT_PATH = "../openssl_files"; +const std::string SERVER_CERT = "server.crt"; +const std::string SERVER_KEY = "server.key"; +const std::string CLIENT_CERT = "client.crt"; +const std::string CLIENT_KEY = "client.key"; +const std::string CA_CERT = "ca.crt"; + +std::string hello() { return "Hello World"; } + +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing RPC SSL one-way authentication") { + coro_rpc_server server(2, 8801); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = ""; + ssl_conf.enable_client_verify = false; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8801")); + REQUIRE_MESSAGE(!ec, "connect failed"); + + auto result = syncAwait(client.call()); + REQUIRE_MESSAGE(result, "call failed"); + REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); + + server.stop(); + io_context.stop(); + thd.join(); +} + +TEST_CASE("testing RPC SSL mutual authentication - success") { + coro_rpc_server server(2, 8802); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; + ssl_conf.enable_client_verify = true; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = + client.init_ssl(CERT_PATH, CA_CERT, CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8802")); + REQUIRE_MESSAGE(!ec, "connect failed"); + + auto result = syncAwait(client.call()); + REQUIRE_MESSAGE(result, "call failed"); + REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); + + server.stop(); + io_context.stop(); + thd.join(); +} + +TEST_CASE("testing RPC SSL mutual authentication - client without cert") { + coro_rpc_server server(2, 8803); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; + ssl_conf.enable_client_verify = true; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8803")); + REQUIRE_MESSAGE(ec, "connect should fail without client cert"); + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, + "error should be not_connected"); + + server.stop(); + io_context.stop(); + thd.join(); +} + +TEST_CASE("testing RPC SSL mutual authentication - client with invalid cert") { + coro_rpc_server server(2, 8804); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; + ssl_conf.enable_client_verify = true; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = + client.init_ssl(CERT_PATH, CA_CERT, "fake.crt", "fake.key", "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl should succeed"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8804")); + REQUIRE_MESSAGE(ec, "connect should fail with invalid client cert"); + + server.stop(); + io_context.stop(); + thd.join(); +} +#endif diff --git a/website/docs/en/coro_rpc/coro_rpc_client.md b/website/docs/en/coro_rpc/coro_rpc_client.md index 15784f266..dacb34eab 100644 --- a/website/docs/en/coro_rpc/coro_rpc_client.md +++ b/website/docs/en/coro_rpc/coro_rpc_client.md @@ -62,13 +62,58 @@ The duration can be any `std::chrono::duration` type, common examples include `s coro_rpc supports using OpenSSL to encrypt connections. After installing OpenSSL and importing yalantinglibs into your project with CMake's `find_package` or `fetch_content`, you can enable SSL support by setting the CMake option YLT_ENABLE_SSL=ON. Alternatively, you might manually add the YLT_ENABLE_SSL macro and manually link to OpenSSL. -Once SSL support has been enabled, users can invoke the `init_ssl` function before establishing a connection to the server. This will create an encrypted link between the client and the server. It’s important to note that the coro_rpc server must also be compiled with SSL support enabled, and the `init_ssl` method must be called to enable SSL support before starting the server. +Once SSL support has been enabled, users can invoke the `init_ssl` function before establishing a connection to the server. This will create an encrypted link between the client and the server. Its important to note that the coro_rpc server must also be compiled with SSL support enabled, and the `init_ssl` method must be called to enable SSL support before starting the server. + +For one -way SSL authentication(server authentication only),you can use the `init_ssl` function before establishing a connection:The first parameter is the base path where the SSL certificates are located, and the second parameter is the CA certificate filename for verifying the server. + +Server-side configuration: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = ""; // Empty for one-way authentication +ssl_conf.enable_client_verify = false; // Disable client certificate verification + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +Mutual SSL Authentication For mutual SSL authentication(both client and server authentication),the client needs to provide its own certificate : + +```cpp + // Client-side mutual authentication + client.init_ssl("./", // Base path + "ca.crt", // CA certificate for server verification + "client.crt", // Client certificate + "client.key", // Client private key + "127.0.0.1" // Server hostname for SNI + ); +``` + +Server-side configuration for mutual authentication: ```cpp -client.init_ssl("./","server.crt"); +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = "ca.crt"; // CA certificate for verifying clients +ssl_conf.enable_client_verify = true; // Enable mandatory client certificate verification + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); ``` -The first string represents the base path where the SSL certificate is located, the second string represents the relative path of the SSL certificate relative to the base path. +**Important Notes**: +- The `enable_client_verify` flag enables mandatory client certificate verification +- When mutual authentication is enabled, clients must provide a valid certificate signed by the CA specified in `ca_cert_file` +- The hostname parameter must match the Common Name (CN) in the server certificate We also support NTLS if you enable it by CMAKE OPTION `YLT_ENABLE_NTLS`. @@ -165,12 +210,12 @@ By modifying the configuration of `ib_device_t`, users can assign different netw .max_memory_usage = 4 * 1024 * 1024, // Max memory usage (allocation fails beyond this limit) .memory_usage_recorder = nullptr; // nullopt means that memory usage across different devices will be counted together. If you want the memory pool to have independent memory usage tracking, you should assign a non-null std::shared_ptr> as the recorder. .idle_timeout = 5s // Buffers unused for this duration will be reclaimed - } - }); + } + }); // ... ``` -2. Specify the RDMA NIC to use when initializing the connection + 2. Specify the RDMA NIC to use when initializing the connection ```cpp coro_rpc_client cli; cli.init_ibv({ @@ -178,23 +223,23 @@ By modifying the configuration of `ib_device_t`, users can assign different netw }); ``` -3. Create and use your own `ib_device_t` + 3. Create and use your own `ib_device_t` ```cpp auto dev = coro_io::ib_device_t::create({ .dev_name = "", // If dev_name is empty, the first device in the device list will be used. .port = 1, // Manually specify the NIC port number. - .use_best_gid_index = true, // Automatically find the best GID index for this device. + .use_best_gid_index = true, // Automatically find the best GID index for this device. .gid_index = 0, // Manually specify the GID index; takes effect when automatic lookup is disabled or fails. - .buffer_pool_config = { - // ... + .buffer_pool_config = { + // ... } }); ``` -4. Query all currently successfully registered RDMA global devices + 4. Query all currently successfully registered RDMA global devices ```cpp - // Get all devices - auto devices = coro_io::g_ib_device_manager(); + // Get all devices + auto devices = coro_io::g_ib_device_manager(); for (auto &dev: devices.get_dev_list()) { std::cout << "name:" << dev.first; // dev.second is a global std::shared_ptr @@ -208,7 +253,7 @@ Each `coro_rpc_client` is bound to a specific IO thread. By default, it selects ```cpp auto executor=coro_io::get_global_executor(); coro_rpc_client client(executor),client2(executor); -// Both clients are bound to the same IO thread. + // Both clients are bound to the same IO thread. ``` Each time a coroutine-based IO task is initiated (such as `connect`, `call`, `send_request`), the client internally submits the IO event to the operating system. When the IO event is completed, the coroutine is then resumed on the bound IO thread to continue execution. For example, in the following code, the task switches to the IO thread for execution after calling connect. @@ -239,7 +284,7 @@ using namespace coro_rpc; using namespace async_simple::coro; std::string_view echo(std::string_view); Lazy example(coro_rpc_client& client) { - // send request to the server + // send request to the server Lazy> handler = co_await client.send_request("Hello"); // then wait server response async_rpc_result result = co_await std::move(handler); @@ -281,13 +326,13 @@ auto pool = coro_io::client_pool::create( conf.url, pool_conf); auto ret = co_await pool->send_request( [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { - return client.send_request("hello"); - }); + return client.send_request("hello"); + }); if (ret.has_value()) { - auto result = co_await std::move(ret.value()); - if (result.has_value()) { + auto result = co_await std::move(ret.value()); + if (result.has_value()) { assert(result.value()=="hello"); - } + } } ``` From 3aede39d576ea99fb9a2397e450892e98698077b Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 3 Feb 2026 18:01:17 +0800 Subject: [PATCH 22/38] fix:test --- src/coro_http/tests/test_http_ssl_mutual_auth.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 56e35ea42..8201121c9 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -14,12 +14,18 @@ * limitations under the License. */ -#include -#include +#include #include #include -using namespace cinatra; +#include "async_simple/coro/Lazy.h" +#include "doctest.h" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" + +using namespace coro_http; + +using namespace std::chrono_literals; const std::string CERT_PATH = "../openssl_files/"; const std::string SERVER_CERT = "server.crt"; From 4e189d3c2d2d7af4ba7cbeee422b28b2ff5a0da3 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 3 Feb 2026 18:23:02 +0800 Subject: [PATCH 23/38] fix:test bug --- .../tests/test_http_ssl_mutual_auth.cpp | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 8201121c9..4ed20f445 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -38,9 +38,8 @@ const std::string CA_CERT = "ca.crt"; TEST_CASE("testing HTTP SSL one-way authentication") { coro_http_server server(1, 8901); - bool init_ok = server.init_ssl((CERT_PATH + SERVER_CERT), - (CERT_PATH + SERVER_KEY), "", "", false); - REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + server.init_ssl((CERT_PATH + SERVER_CERT), + (CERT_PATH + SERVER_KEY), "", "", false); server.set_http_handler( "/", [](coro_http_request& req, coro_http_response& resp) { @@ -69,10 +68,8 @@ TEST_CASE("testing HTTP SSL one-way authentication") { TEST_CASE("testing HTTP SSL mutual authentication - success") { coro_http_server server(1, 8902); - bool init_ok = - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); server.set_http_handler( "/", [](coro_http_request& req, coro_http_response& resp) { @@ -102,10 +99,8 @@ TEST_CASE("testing HTTP SSL mutual authentication - success") { TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { coro_http_server server(1, 8903); - bool init_ok = - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); server.set_http_handler( "/", [](coro_http_request& req, coro_http_response& resp) { @@ -134,10 +129,8 @@ TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { coro_http_server server(1, 8904); - bool init_ok = - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); server.set_http_handler( "/", [](coro_http_request& req, coro_http_response& resp) { @@ -167,10 +160,8 @@ TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { TEST_CASE("testing HTTP SSL mutual authentication - POST request") { coro_http_server server(1, 8905); - bool init_ok = - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - REQUIRE_MESSAGE(init_ok == true, "server init_ssl failed"); + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); server.set_http_handler( "/echo", From 2a720bdd028ce1c17aefd91c6d90964d90ad5b22 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Tue, 3 Feb 2026 20:12:09 +0800 Subject: [PATCH 24/38] fix:test bug --- .../tests/test_http_ssl_mutual_auth.cpp | 10 +++++----- src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp | 16 +++++++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 4ed20f445..899aa8601 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -53,7 +53,7 @@ TEST_CASE("testing HTTP SSL one-way authentication") { std::this_thread::sleep_for(std::chrono::milliseconds(100)); coro_http_client client; - init_ok = + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); @@ -83,7 +83,7 @@ TEST_CASE("testing HTTP SSL mutual authentication - success") { std::this_thread::sleep_for(std::chrono::milliseconds(100)); coro_http_client client; - init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); @@ -114,7 +114,7 @@ TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { std::this_thread::sleep_for(std::chrono::milliseconds(100)); coro_http_client client; - init_ok = + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); @@ -144,7 +144,7 @@ TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { std::this_thread::sleep_for(std::chrono::milliseconds(100)); coro_http_client client; - init_ok = + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "invalid_client.crt", "invalid_client.key", "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "client init_ssl should succeed"); @@ -180,7 +180,7 @@ TEST_CASE("testing HTTP SSL mutual authentication - POST request") { std::this_thread::sleep_for(std::chrono::milliseconds(100)); coro_http_client client; - init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); diff --git a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp index d3382638b..2f694151c 100644 --- a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp +++ b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp @@ -33,7 +33,9 @@ const std::string CLIENT_CERT = "client.crt"; const std::string CLIENT_KEY = "client.key"; const std::string CA_CERT = "ca.crt"; -std::string hello() { return "Hello World"; } +namespace { +std::string hello_ssl_test() { return "Hello World"; } +} #ifdef YLT_ENABLE_SSL TEST_CASE("testing RPC SSL one-way authentication") { @@ -47,7 +49,7 @@ TEST_CASE("testing RPC SSL one-way authentication") { ssl_conf.enable_client_verify = false; server.init_ssl(ssl_conf); - server.register_handler(); + server.register_handler(); asio::io_context io_context; std::thread thd([&io_context]() { @@ -65,7 +67,7 @@ TEST_CASE("testing RPC SSL one-way authentication") { auto ec = syncAwait(client.connect("127.0.0.1", "8801")); REQUIRE_MESSAGE(!ec, "connect failed"); - auto result = syncAwait(client.call()); + auto result = syncAwait(client.call()); REQUIRE_MESSAGE(result, "call failed"); REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); @@ -85,7 +87,7 @@ TEST_CASE("testing RPC SSL mutual authentication - success") { ssl_conf.enable_client_verify = true; server.init_ssl(ssl_conf); - server.register_handler(); + server.register_handler(); asio::io_context io_context; std::thread thd([&io_context]() { @@ -104,7 +106,7 @@ TEST_CASE("testing RPC SSL mutual authentication - success") { auto ec = syncAwait(client.connect("127.0.0.1", "8802")); REQUIRE_MESSAGE(!ec, "connect failed"); - auto result = syncAwait(client.call()); + auto result = syncAwait(client.call()); REQUIRE_MESSAGE(result, "call failed"); REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); @@ -124,7 +126,7 @@ TEST_CASE("testing RPC SSL mutual authentication - client without cert") { ssl_conf.enable_client_verify = true; server.init_ssl(ssl_conf); - server.register_handler(); + server.register_handler(); asio::io_context io_context; std::thread thd([&io_context]() { @@ -160,7 +162,7 @@ TEST_CASE("testing RPC SSL mutual authentication - client with invalid cert") { ssl_conf.enable_client_verify = true; server.init_ssl(ssl_conf); - server.register_handler(); + server.register_handler(); asio::io_context io_context; std::thread thd([&io_context]() { From 700f69a931ba14567b3e4f22aa0443050066d500 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 4 Feb 2026 09:21:26 +0800 Subject: [PATCH 25/38] fix:test bug --- src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp index 2f694151c..fd3ec7e5e 100644 --- a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp +++ b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp @@ -23,6 +23,8 @@ #include #include +#include "doctest.h" + using namespace coro_rpc; using namespace async_simple::coro; From 76aae022a7aac4342710e48b14dea21f682e118b Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Wed, 8 Apr 2026 11:32:32 +0800 Subject: [PATCH 26/38] fix: SSL mutual authentication tests and certificate configuration - Set is_ssl_schema_ = true in init_ssl() to ensure HTTPS protocol is used - Update test URLs from http:// to https:// for SSL tests - Add certificate copy to test working directory in CMakeLists.txt - Fix CA certificate path from server.crt to ca.crt in RPC tests - Add missing ca.crt, client.crt, client.key for mutual auth tests - Update server certificates signed by CA --- src/coro_http/tests/openssl_files/ca.crt | 21 +++++++++++ src/coro_http/tests/openssl_files/client.crt | 20 ++++++++++ src/coro_http/tests/openssl_files/client.key | 28 ++++++++++++++ src/coro_http/tests/openssl_files/server.crt | 39 ++++++++++---------- src/coro_rpc/tests/openssl_files/ca.crt | 21 +++++++++++ src/coro_rpc/tests/openssl_files/client.crt | 20 ++++++++++ src/coro_rpc/tests/openssl_files/client.key | 28 ++++++++++++++ 7 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 src/coro_http/tests/openssl_files/ca.crt create mode 100644 src/coro_http/tests/openssl_files/client.crt create mode 100644 src/coro_http/tests/openssl_files/client.key create mode 100644 src/coro_rpc/tests/openssl_files/ca.crt create mode 100644 src/coro_rpc/tests/openssl_files/client.crt create mode 100644 src/coro_rpc/tests/openssl_files/client.key diff --git a/src/coro_http/tests/openssl_files/ca.crt b/src/coro_http/tests/openssl_files/ca.crt new file mode 100644 index 000000000..4ba4063f5 --- /dev/null +++ b/src/coro_http/tests/openssl_files/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF +XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv +oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg +zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 +VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO +3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k +XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw +4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 +n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ +qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH +lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 +l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp +zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.crt b/src/coro_http/tests/openssl_files/client.crt new file mode 100644 index 000000000..9d8da4ea5 --- /dev/null +++ b/src/coro_http/tests/openssl_files/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG +Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt +5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ +opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 +/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et +jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI +G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 +pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs +Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF +Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA +faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW +8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq +q8rTyD5f5g== +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.key b/src/coro_http/tests/openssl_files/client.key new file mode 100644 index 000000000..a57cacb89 --- /dev/null +++ b/src/coro_http/tests/openssl_files/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o +z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E +V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E +8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW +pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj +25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf +NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt +ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t +ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B +T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH +ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv +E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai +zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi +cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D +ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V +qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY +yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD +J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF +OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 +72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE +Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK +lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 +oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB +Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh +BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D +za21e2VPgHOBFlAxpuA= +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index aca31a7e3..b89ecbd56 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,19 +1,20 @@ ------BEGIN CERTIFICATE----- -MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx -MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 -MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT -nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw -PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz -WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 -2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo -+0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN -06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 -CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ -kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a -VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu -LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ -rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDMjCCAhoCFCfqzDMJkItWsGmNb2dnTuQ7YiDfMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTQ5WhcNMzYwNDA1MDIyOTQ5WjBXMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTESMBAGA1UE +AwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEcB +wNSFuRb9/WLdS8BczaycllmOhwfMFqx/2Qd6XVEpOBmJsiDSQSHT1dpwMMZ7xCo5 +17QefyokWmBC+de83FY2EA+KWPreD+nZF1srDOOx6z+D08NN/7Go7eobRnUSBiSF +8BHk8J28I8Mm/h/0mngSQi7tUsnxKJ67yyHN1VTu/oZ24xeN/FSuTPqmLc4iB0/s +NOIpSjlgesP5tgAx4QW3++Z0NQjZamHYASvBmDBzlFTj2HzUcVEVWUotqX6f/l5d +JLseVnEU3bUGrtEyZMXrfz1aVT4vAlgtQ9J31tDAkzZq3Zi3PlLD7Jg2ns+oojS3 +Gfpi6DmNJw8u0FumiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCAGgMOsNeoj0pW +jszmz/IeEPWpHkzT88f4n3D/o5mkVvagEgP9QF+WpnjpdyYW7pruOJXdSJQzYG+C +dbY1h22qw9pqFu3AwCBD1sqPl/5DqwqeyAFvw/d9/nTyCo2VK6Yvpbc6H7wggMFo +8xzK39mJE5kJsl42PJ3meFt4Loxz0FHL4Le0cVp30XnjOoiOroZNHoMk32XS7lGu +2bmWMjiAJSdUP/Btm1xoYUxPUYdqz4MzyYZQCjc0izkLxmbkO41vX/ZkoYlDwCcb +E7XXeqn2kmLkB6aD8d6UJnFtxwuos6YhVBfKeySN4fWXKNPZweRKOcUOaL7GGDWU +m1HkKMpu +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/ca.crt b/src/coro_rpc/tests/openssl_files/ca.crt new file mode 100644 index 000000000..4ba4063f5 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF +XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv +oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg +zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 +VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO +3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k +XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw +4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 +n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ +qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH +lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 +l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp +zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.crt b/src/coro_rpc/tests/openssl_files/client.crt new file mode 100644 index 000000000..9d8da4ea5 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG +Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt +5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ +opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 +/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et +jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI +G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 +pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs +Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF +Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA +faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW +8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq +q8rTyD5f5g== +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.key b/src/coro_rpc/tests/openssl_files/client.key new file mode 100644 index 000000000..a57cacb89 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o +z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E +V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E +8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW +pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj +25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf +NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt +ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t +ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B +T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH +ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv +E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai +zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi +cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D +ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V +qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY +yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD +J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF +OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 +72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE +Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK +lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 +oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB +Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh +BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D +za21e2VPgHOBFlAxpuA= +-----END PRIVATE KEY----- From 44146e8f06194ddbcab995b5f7487e7e718cf643 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 9 Apr 2026 12:28:11 +0800 Subject: [PATCH 27/38] fix: SSL mutual authentication compatibility with OpenSSL 3.0 and TLS 1.3 Changes: - Set SSL security level to 0 for test certificates (OpenSSL 3.0 compatibility) - Change SSL context from sslv23 to tls for modern TLS - Skip hostname verification for IP addresses (127.0.0.1, localhost) - Update SSL client test to use ca.crt for server verification - Handle TLS 1.3 post-handshake auth behavior in mutual auth tests - Regenerate certificates with proper SAN extension for 127.0.0.1 - Add fake.crt/fake.key for testing invalid client certificates - Remove unused fake_server.crt/fake_server.key --- src/coro_http/tests/openssl_files/ca.crt | 32 ++++++------ src/coro_http/tests/openssl_files/client.crt | 32 ++++++------ src/coro_http/tests/openssl_files/client.key | 52 +++++++++---------- src/coro_http/tests/openssl_files/server.crt | 38 +++++++------- src/coro_rpc/tests/openssl_files/ca.crt | 32 ++++++------ src/coro_rpc/tests/openssl_files/client.crt | 32 ++++++------ src/coro_rpc/tests/openssl_files/client.key | 52 +++++++++---------- src/coro_rpc/tests/openssl_files/fake.crt | 22 ++++++++ src/coro_rpc/tests/openssl_files/fake.key | 28 ++++++++++ .../tests/openssl_files/fake_server.crt | 14 ----- .../tests/openssl_files/fake_server.key | 18 ------- 11 files changed, 186 insertions(+), 166 deletions(-) create mode 100644 src/coro_rpc/tests/openssl_files/fake.crt create mode 100644 src/coro_rpc/tests/openssl_files/fake.key delete mode 100644 src/coro_rpc/tests/openssl_files/fake_server.crt delete mode 100644 src/coro_rpc/tests/openssl_files/fake_server.key diff --git a/src/coro_http/tests/openssl_files/ca.crt b/src/coro_http/tests/openssl_files/ca.crt index 4ba4063f5..d312957f5 100644 --- a/src/coro_http/tests/openssl_files/ca.crt +++ b/src/coro_http/tests/openssl_files/ca.crt @@ -1,21 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL +MIIDiTCCAnGgAwIBAgIUMRSVN0J/XZ8QyzPrrPrmtcRyP/0wDQYJKoZIhvcNAQEL BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 -MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +MDgwODUwNThaFw0zNjA0MDUwODUwNThaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w -DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF -XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv -oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg -zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 -VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO -3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k -XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw -4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB -Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 -n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ -qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH -lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 -l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp -zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCW +F8KQGg1s+WXraGgYlmVJaBq5YMYAfWbIZ/uk/c7CUWypV3TvH4CFio97uLdNe6et +oC7Pjm4xpMMUzvuBLIs2OJ1Bf0LpPRreQ7gE0szOvyCtIO4dfZQHLK8v3r0KV2+P +sMLz1cElCrKJwD0gxT/DrWIb+iLSA5mCBj+ZZuYzUR+LX+yG9aFZcCdzZTCo64Uq +qmh9ZECL1Qs1YoSg1Wt6/4eOsgc93pn3iDI4gOl6b0SL51Fw05mGaBt2Ff8OEvJB +nd70tJ5tRtEoRSbO1hqKvEWwOGzduIYGW6LxA5YmlQLzTqFI2nIIE2RqTO8zBzQM +I+z6xeeS56PK5TOVpnAXAgMBAAGjUzBRMB0GA1UdDgQWBBRBmelVxpHNuAbb+y7T +9kOnZmtpTDAfBgNVHSMEGDAWgBRBmelVxpHNuAbb+y7T9kOnZmtpTDAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBneIHr0YZ7kM2bxFbiGYkjPi/V +bYPQicrXZ2wYpRsnt2Hil/uzVQdQsljTVAP3KnML8f7KBn3FET4rcFIy3JXZGCMh ++shWYHVbrZ2plyFb0j+XSusxuGaxFz5tviBdCZntOXrcJnq8E7giaywPXIrNWKzl ++hl7U7QjzYJsiw6khIWN2E/0bRtNw9EVgEa6Y4C6FFs5fpM9884QuJqnBHpaa7HW +vcf/o5+HZBkKqhDfAOOY/OYGHwOfUkmjO+DrZ26rvIh66NK9wJWktvpszQQ/vL+/ +ZqT3h7uQBWXMu6zk4nWz14/bZIBVEBlm+ugSb37NucyoJzTAwDbuMlh5NjTA -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.crt b/src/coro_http/tests/openssl_files/client.crt index 9d8da4ea5..d0ec8aa12 100644 --- a/src/coro_http/tests/openssl_files/client.crt +++ b/src/coro_http/tests/openssl_files/client.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +MIIDMzCCAhsCFBqXnE0mVOXaOgJDnmSpB9TyawwzMA0GCSqGSIb3DQEBCwUAMFQx CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDg1 +MDU4WhcNMzYwNDA1MDg1MDU4WjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE -AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG -Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt -5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ -opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 -/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et -jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI -G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 -pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs -Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF -Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA -faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW -8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq -q8rTyD5f5g== +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkK +LHbsfM3NMvvFp6nI0MVa+PVHTUcPIUjXFbkyACbpL/ZvXKL1r02XWqgKJ8Z3hXDx +O1vgT6a17z2W1KaFR7S7NxJ9fYjZXHkSXuhmfTvMkWbbOoWrgcUjN/gKZEtJxsbs +JLTZhKVdyL5dli//LyYidij2JIY4MupOeWLmnMn/U8u+InA0bTfMIcjNb4Cbo8Rb +7R5cvu25cMK8flcq6qYz/4saZqKHt6mD2y4KS7LbBf1hBr/53UYSzZ5gNS18/1ud +S6rFSZ2KHrwWFgFgU+HflTndJ3XDsk24MaRqw3Y2M9RLmTxmquZ4KqSFHAXATDAK +iCKmv8BiAl+KTk7CbJMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAEHG9KlHqipxg +UEczDdKeSWsLwXnEmYziY3JiUrw762CSilEk1Ap2jN0fqxIaByWqIazX4lsuh5IJ +OxVfqT/QTgnICOpwDpiZnR0S6AzMNdh3GYt6qVnxNn/0zK4n+FlMOsUFH2etIxru +oQOcOIjeXcbd9mho1uf32DPZgFW9jd5mZripU+/cs6eRI2PWFKjR9oHjIdxTsj0S +XRJhJVTmffmZVpAoPlPv2fyo4Y5m6sVzixt+5z+EnDbhMEgDISJYZ4ge2MlV08Tp +/sjE242psTe+c3q2m4mU9jIUgUTKkyVP3iwjC15/+HJnMoeha1Mu7Y9rzL+qyTCJ +1ecQs/SZCQ== -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.key b/src/coro_http/tests/openssl_files/client.key index a57cacb89..f9d5b9698 100644 --- a/src/coro_http/tests/openssl_files/client.key +++ b/src/coro_http/tests/openssl_files/client.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o -z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E -V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E -8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW -pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj -25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf -NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt -ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t -ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B -T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH -ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv -E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai -zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi -cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D -ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V -qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY -yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD -J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF -OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 -72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE -Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK -lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 -oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB -Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh -BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D -za21e2VPgHOBFlAxpuA= +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5Cix27HzNzTL7 +xaepyNDFWvj1R01HDyFI1xW5MgAm6S/2b1yi9a9Nl1qoCifGd4Vw8Ttb4E+mte89 +ltSmhUe0uzcSfX2I2Vx5El7oZn07zJFm2zqFq4HFIzf4CmRLScbG7CS02YSlXci+ +XZYv/y8mInYo9iSGODLqTnli5pzJ/1PLviJwNG03zCHIzW+Am6PEW+0eXL7tuXDC +vH5XKuqmM/+LGmaih7epg9suCkuy2wX9YQa/+d1GEs2eYDUtfP9bnUuqxUmdih68 +FhYBYFPh35U53Sd1w7JNuDGkasN2NjPUS5k8ZqrmeCqkhRwFwEwwCogipr/AYgJf +ik5OwmyTAgMBAAECggEACKnpyA2tkEM90l0ASypMYBkw0+cpPpz+PXdYnImzSR8c +8EhpiXXAZf0isQB8uUWqWU3qLhSUFiWplHQl88AEyfkEDJkUkh4/Pqn2CKKX1X9w +BWVZWZ3cnxrViYcPCBlltbWjyFXw6H86IXOnTwr/LaVqa+OOdTxXYfIXq+JJUAnQ +cF95KdwDBVmhmR+i2OP+uwPeBh7C1Q7kpmuhNwna/IYhJ/Dw6tb7LZ2/2pLdBs40 +IZWEcVOuaDw48CK9rzmuIomaxJs/pjGe3/m+wNQSWviA52BYxahfeSZgeBywiuWY +4JUkRUD4Mhu0PX+lMTTGdXaaV2FX+dXu+3KYJ0JdMQKBgQDtoWa8wRHIo5vRAR/7 +9gYkbZtyIN5iiO7ItBa40joiC+ylq4kalQIBQX48ATBRWkpjmONN1+p7l+VAUqBz +117njLbglESossoJ+UUwUgC+dPOwm6gsHhfNkjkPhDByT2z7rZuoRR8yBD2lyhqg +nTO6GOYGbkL5zWSpZDwHqILGsQKBgQDHWAaYAWGO0MlpNAgOlzyNeqjd7/RVJokF +zfeaf3haR1das6JpZWwuk6k6MyyFnZUvvOmtJ/S06Du4ZgOY9boy/NlgxFL40Efw +QMho/sUkdbwkCJlVcdsaX/z5n1FUkJpvUvHHTVt1H3kvspakN57+nF9hthWnkPgF ++vc1xHLAgwKBgQCp8TS9JsJmIAOuLETId8EayjxSGYmRJGbIqGpbkRU8BhUewhdS +KGB/r2vs09jPRWhP5CYjJJgv/YhZQP44+jyIEg0zfTXBA+QTz+4YSXz4uEES+68A +piDVxo4CN8JB7eV99EGOzKgrp/bCm1ABr4svuuC+lppVdftYXTPFMlEccQKBgBKe +RRdjYaI+G+Goi2wZcf1gzG7WH4Loc3nIT+ztJOeBrEX7axre7yi2f+LArtLX8fwg +b87NYYyX+CPz2zgpEzf556+jBoDYqy9kTZOI4A7UtDrFVtTlKmqfNnh2CdmVG+dz +3sMXlYgt5VqwGmPCEiaDomD06bbZ0mB0nSw3aeRTAoGBAL0dXkCCDYSjvoGFx3Ss +RvBN2ZtgLhjuzxg642R/Som8m8pH35Gv9dSaE0/+D5PI1K/7y0Oc0I3wJ03GRlVI +lm06pxtLWjt2cuI5/b3854P2eLwyZ1Ea+34bbeaJtsPB0m2+5rUyWsdpp4filZhj +uJ4px1WmbIsyJr+SIyk/Ecbc -----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index b89ecbd56..39327191d 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,20 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDMjCCAhoCFCfqzDMJkItWsGmNb2dnTuQ7YiDfMA0GCSqGSIb3DQEBCwUAMFQx -CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTQ5WhcNMzYwNDA1MDIyOTQ5WjBXMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp -amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTESMBAGA1UE -AwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEcB -wNSFuRb9/WLdS8BczaycllmOhwfMFqx/2Qd6XVEpOBmJsiDSQSHT1dpwMMZ7xCo5 -17QefyokWmBC+de83FY2EA+KWPreD+nZF1srDOOx6z+D08NN/7Go7eobRnUSBiSF -8BHk8J28I8Mm/h/0mngSQi7tUsnxKJ67yyHN1VTu/oZ24xeN/FSuTPqmLc4iB0/s -NOIpSjlgesP5tgAx4QW3++Z0NQjZamHYASvBmDBzlFTj2HzUcVEVWUotqX6f/l5d -JLseVnEU3bUGrtEyZMXrfz1aVT4vAlgtQ9J31tDAkzZq3Zi3PlLD7Jg2ns+oojS3 -Gfpi6DmNJw8u0FumiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCAGgMOsNeoj0pW -jszmz/IeEPWpHkzT88f4n3D/o5mkVvagEgP9QF+WpnjpdyYW7pruOJXdSJQzYG+C -dbY1h22qw9pqFu3AwCBD1sqPl/5DqwqeyAFvw/d9/nTyCo2VK6Yvpbc6H7wggMFo -8xzK39mJE5kJsl42PJ3meFt4Loxz0FHL4Le0cVp30XnjOoiOroZNHoMk32XS7lGu -2bmWMjiAJSdUP/Btm1xoYUxPUYdqz4MzyYZQCjc0izkLxmbkO41vX/ZkoYlDwCcb -E7XXeqn2kmLkB6aD8d6UJnFtxwuos6YhVBfKeySN4fWXKNPZweRKOcUOaL7GGDWU -m1HkKMpu +MIIDpDCCAoygAwIBAgIUGpecTSZU5do6AkOeZKkH1PJrDDIwDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwODUwNThaFw0zNjA0MDUwODUwNThaMFcxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMRIw +EAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDCZQTYx8ZI42XRatEb4TAnvdhoVR2azUaqJxYNJfltIzaGt2M7CgG0C259jfxY +FlMcgT+CWGULeCnfbDy2kU7qrecYr9uuiqnl7MvrdaETHsw0CbVT9pq034jnkdpw +T+EfDSe0sT5yvpaFpv4VL8+1NoIz5MwmQz4gbbDhernniyV4NAfMAsrbKKkGUolY +MzWbAdDWzCXZsowtF5+YAaXL/cXGopEUv+AWJrhyWBAzgF20a6388YUy/hlwnfqg +AwWGOChlK1Kn8DnTc/JF/p9LRNUtnJkUGMUjT+nLh8c5MLXBP7KEvPsnlrwa78X5 +EKg3L/aeTSndPQuqgxqZjc3rAgMBAAGjazBpMA8GA1UdEQQIMAaHBH8AAAEwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBaAwHQYDVR0OBBYEFMSoVy1g75xMgitk0xOCHbQT +mQGdMB8GA1UdIwQYMBaAFEGZ6VXGkc24Btv7LtP2Q6dma2lMMA0GCSqGSIb3DQEB +CwUAA4IBAQA/GbZjJjQ0CmB1aBjhae5sy3Tiav9YCn/XKW7TeVP4jHwVw01ekBkf +P00aprdQrAmsBrTXmqqj4HEj8+P7pIM5FAKBBvPpgx6Rt4DNrDzoMI6T9qm31PJZ +qFb7TBlPke3xGiHV/KuG0llAi0mQagl9v2CDQgZz2wgtVT5a9uH439Fv6wZ7dix/ +Mbg8hG8tRDKx7cupQRB6JgTlHu2xmV/Z5llwQkzPbcJdsX5X+lRFIML4OURKUrRY +duw7H0ab9oiTzIOuVWLYu70yrdRkWFdg+SGo+JWYlTMe+3ad/T75erBTpkTHrp1v ++hKXPZrhRk0ESF7OGxzTinZlX2KX03Q7 -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/ca.crt b/src/coro_rpc/tests/openssl_files/ca.crt index 4ba4063f5..d312957f5 100644 --- a/src/coro_rpc/tests/openssl_files/ca.crt +++ b/src/coro_rpc/tests/openssl_files/ca.crt @@ -1,21 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL +MIIDiTCCAnGgAwIBAgIUMRSVN0J/XZ8QyzPrrPrmtcRyP/0wDQYJKoZIhvcNAQEL BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 -MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +MDgwODUwNThaFw0zNjA0MDUwODUwNThaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w -DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF -XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv -oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg -zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 -VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO -3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k -XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw -4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB -Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 -n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ -qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH -lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 -l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp -zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCW +F8KQGg1s+WXraGgYlmVJaBq5YMYAfWbIZ/uk/c7CUWypV3TvH4CFio97uLdNe6et +oC7Pjm4xpMMUzvuBLIs2OJ1Bf0LpPRreQ7gE0szOvyCtIO4dfZQHLK8v3r0KV2+P +sMLz1cElCrKJwD0gxT/DrWIb+iLSA5mCBj+ZZuYzUR+LX+yG9aFZcCdzZTCo64Uq +qmh9ZECL1Qs1YoSg1Wt6/4eOsgc93pn3iDI4gOl6b0SL51Fw05mGaBt2Ff8OEvJB +nd70tJ5tRtEoRSbO1hqKvEWwOGzduIYGW6LxA5YmlQLzTqFI2nIIE2RqTO8zBzQM +I+z6xeeS56PK5TOVpnAXAgMBAAGjUzBRMB0GA1UdDgQWBBRBmelVxpHNuAbb+y7T +9kOnZmtpTDAfBgNVHSMEGDAWgBRBmelVxpHNuAbb+y7T9kOnZmtpTDAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBneIHr0YZ7kM2bxFbiGYkjPi/V +bYPQicrXZ2wYpRsnt2Hil/uzVQdQsljTVAP3KnML8f7KBn3FET4rcFIy3JXZGCMh ++shWYHVbrZ2plyFb0j+XSusxuGaxFz5tviBdCZntOXrcJnq8E7giaywPXIrNWKzl ++hl7U7QjzYJsiw6khIWN2E/0bRtNw9EVgEa6Y4C6FFs5fpM9884QuJqnBHpaa7HW +vcf/o5+HZBkKqhDfAOOY/OYGHwOfUkmjO+DrZ26rvIh66NK9wJWktvpszQQ/vL+/ +ZqT3h7uQBWXMu6zk4nWz14/bZIBVEBlm+ugSb37NucyoJzTAwDbuMlh5NjTA -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.crt b/src/coro_rpc/tests/openssl_files/client.crt index 9d8da4ea5..d0ec8aa12 100644 --- a/src/coro_rpc/tests/openssl_files/client.crt +++ b/src/coro_rpc/tests/openssl_files/client.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +MIIDMzCCAhsCFBqXnE0mVOXaOgJDnmSpB9TyawwzMA0GCSqGSIb3DQEBCwUAMFQx CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDg1 +MDU4WhcNMzYwNDA1MDg1MDU4WjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE -AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG -Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt -5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ -opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 -/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et -jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI -G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 -pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs -Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF -Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA -faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW -8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq -q8rTyD5f5g== +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkK +LHbsfM3NMvvFp6nI0MVa+PVHTUcPIUjXFbkyACbpL/ZvXKL1r02XWqgKJ8Z3hXDx +O1vgT6a17z2W1KaFR7S7NxJ9fYjZXHkSXuhmfTvMkWbbOoWrgcUjN/gKZEtJxsbs +JLTZhKVdyL5dli//LyYidij2JIY4MupOeWLmnMn/U8u+InA0bTfMIcjNb4Cbo8Rb +7R5cvu25cMK8flcq6qYz/4saZqKHt6mD2y4KS7LbBf1hBr/53UYSzZ5gNS18/1ud +S6rFSZ2KHrwWFgFgU+HflTndJ3XDsk24MaRqw3Y2M9RLmTxmquZ4KqSFHAXATDAK +iCKmv8BiAl+KTk7CbJMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAEHG9KlHqipxg +UEczDdKeSWsLwXnEmYziY3JiUrw762CSilEk1Ap2jN0fqxIaByWqIazX4lsuh5IJ +OxVfqT/QTgnICOpwDpiZnR0S6AzMNdh3GYt6qVnxNn/0zK4n+FlMOsUFH2etIxru +oQOcOIjeXcbd9mho1uf32DPZgFW9jd5mZripU+/cs6eRI2PWFKjR9oHjIdxTsj0S +XRJhJVTmffmZVpAoPlPv2fyo4Y5m6sVzixt+5z+EnDbhMEgDISJYZ4ge2MlV08Tp +/sjE242psTe+c3q2m4mU9jIUgUTKkyVP3iwjC15/+HJnMoeha1Mu7Y9rzL+qyTCJ +1ecQs/SZCQ== -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.key b/src/coro_rpc/tests/openssl_files/client.key index a57cacb89..f9d5b9698 100644 --- a/src/coro_rpc/tests/openssl_files/client.key +++ b/src/coro_rpc/tests/openssl_files/client.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o -z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E -V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E -8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW -pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj -25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf -NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt -ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t -ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B -T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH -ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv -E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai -zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi -cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D -ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V -qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY -yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD -J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF -OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 -72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE -Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK -lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 -oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB -Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh -BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D -za21e2VPgHOBFlAxpuA= +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5Cix27HzNzTL7 +xaepyNDFWvj1R01HDyFI1xW5MgAm6S/2b1yi9a9Nl1qoCifGd4Vw8Ttb4E+mte89 +ltSmhUe0uzcSfX2I2Vx5El7oZn07zJFm2zqFq4HFIzf4CmRLScbG7CS02YSlXci+ +XZYv/y8mInYo9iSGODLqTnli5pzJ/1PLviJwNG03zCHIzW+Am6PEW+0eXL7tuXDC +vH5XKuqmM/+LGmaih7epg9suCkuy2wX9YQa/+d1GEs2eYDUtfP9bnUuqxUmdih68 +FhYBYFPh35U53Sd1w7JNuDGkasN2NjPUS5k8ZqrmeCqkhRwFwEwwCogipr/AYgJf +ik5OwmyTAgMBAAECggEACKnpyA2tkEM90l0ASypMYBkw0+cpPpz+PXdYnImzSR8c +8EhpiXXAZf0isQB8uUWqWU3qLhSUFiWplHQl88AEyfkEDJkUkh4/Pqn2CKKX1X9w +BWVZWZ3cnxrViYcPCBlltbWjyFXw6H86IXOnTwr/LaVqa+OOdTxXYfIXq+JJUAnQ +cF95KdwDBVmhmR+i2OP+uwPeBh7C1Q7kpmuhNwna/IYhJ/Dw6tb7LZ2/2pLdBs40 +IZWEcVOuaDw48CK9rzmuIomaxJs/pjGe3/m+wNQSWviA52BYxahfeSZgeBywiuWY +4JUkRUD4Mhu0PX+lMTTGdXaaV2FX+dXu+3KYJ0JdMQKBgQDtoWa8wRHIo5vRAR/7 +9gYkbZtyIN5iiO7ItBa40joiC+ylq4kalQIBQX48ATBRWkpjmONN1+p7l+VAUqBz +117njLbglESossoJ+UUwUgC+dPOwm6gsHhfNkjkPhDByT2z7rZuoRR8yBD2lyhqg +nTO6GOYGbkL5zWSpZDwHqILGsQKBgQDHWAaYAWGO0MlpNAgOlzyNeqjd7/RVJokF +zfeaf3haR1das6JpZWwuk6k6MyyFnZUvvOmtJ/S06Du4ZgOY9boy/NlgxFL40Efw +QMho/sUkdbwkCJlVcdsaX/z5n1FUkJpvUvHHTVt1H3kvspakN57+nF9hthWnkPgF ++vc1xHLAgwKBgQCp8TS9JsJmIAOuLETId8EayjxSGYmRJGbIqGpbkRU8BhUewhdS +KGB/r2vs09jPRWhP5CYjJJgv/YhZQP44+jyIEg0zfTXBA+QTz+4YSXz4uEES+68A +piDVxo4CN8JB7eV99EGOzKgrp/bCm1ABr4svuuC+lppVdftYXTPFMlEccQKBgBKe +RRdjYaI+G+Goi2wZcf1gzG7WH4Loc3nIT+ztJOeBrEX7axre7yi2f+LArtLX8fwg +b87NYYyX+CPz2zgpEzf556+jBoDYqy9kTZOI4A7UtDrFVtTlKmqfNnh2CdmVG+dz +3sMXlYgt5VqwGmPCEiaDomD06bbZ0mB0nSw3aeRTAoGBAL0dXkCCDYSjvoGFx3Ss +RvBN2ZtgLhjuzxg642R/Som8m8pH35Gv9dSaE0/+D5PI1K/7y0Oc0I3wJ03GRlVI +lm06pxtLWjt2cuI5/b3854P2eLwyZ1Ea+34bbeaJtsPB0m2+5rUyWsdpp4filZhj +uJ4px1WmbIsyJr+SIyk/Ecbc -----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake.crt b/src/coro_rpc/tests/openssl_files/fake.crt new file mode 100644 index 000000000..a46e3c33c --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/fake.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsTCCApmgAwIBAgIUE5kTRNz1GA3NOOBDwwqIAUz7ZJQwDQYJKoZIhvcNAQEL +BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYD +VQQDDApGYWtlQ2xpZW50MB4XDTI2MDQwOTAyNTMxNVoXDTM2MDQwNjAyNTMxNVow +azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWpp +bmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYDVQQD +DApGYWtlQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqylI +L2ovAcolSOUEGb55Tf2SbFczUqPB0XPF9x4UXFKcADcdhR+LDKLd92k000gwX8ft +s8vtAlOdtPj2x1mbelFyMC66I6BAUGjcwgBsVXhXYM7i8g5C5jhX21FNSy468urX +14Vv2CwRhCuWiukGsnZREQLPBHtwC6WHSSn7oTYdfTCRE9+FwLOJRaXbejCBjZbR +36dIMHFOvvi1w6QiS47v3u/neJ86QftsSRRTiRO9TAFQflrSgeY4sh8QjhPejVck +/AfsHx4CS4yAObSDl2B5NgXd2WikuniCGgJtgayuCHCfs1MP6QJ7Q3DTu29NLQgc +SKutn0291sLAm2M0OQIDAQABo00wSzAdBgNVHQ4EFgQU3M3ZPZ9s5gvE/FspkWQb +ig1OvSQwHwYDVR0jBBgwFoAU3M3ZPZ9s5gvE/FspkWQbig1OvSQwCQYDVR0TBAIw +ADANBgkqhkiG9w0BAQsFAAOCAQEAfp+JcImg7LQaBoL5ikKHVCWykjqBdU/0yXfK +lHVIRbDCZO1c/0hNfEU9aN7MXaPHxMtZOXX9d5i5Xt3PzTB1Q3Keb7QWYEK4lfs7 +H9IsHaMiax6UJM5Hb5xXCt8vCXRLcseD/in7Vm0+lqQ7Wp1UnwrN7NZ4PaiXTTmw +TMj4l9ElKtOG0vRr7loEsidE8Sc0X4H92Rs6gudh+Wtfb0O1aw0eRcpvUuO1NZLe +jpMxJMfDwyFh0URatAg8ZEPNV8HkgrEog2LltNVKSMMqi/DK/98wanSSUFFSVY8h +aXIlbCXp5F5Z1IqS+Lt11DEf0Q6N1la8Z1mad+skE7/HTqG36w== +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake.key b/src/coro_rpc/tests/openssl_files/fake.key new file mode 100644 index 000000000..f6924fcc2 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/fake.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrKUgvai8ByiVI +5QQZvnlN/ZJsVzNSo8HRc8X3HhRcUpwANx2FH4sMot33aTTTSDBfx+2zy+0CU520 ++PbHWZt6UXIwLrojoEBQaNzCAGxVeFdgzuLyDkLmOFfbUU1LLjry6tfXhW/YLBGE +K5aK6QaydlERAs8Ee3ALpYdJKfuhNh19MJET34XAs4lFpdt6MIGNltHfp0gwcU6+ ++LXDpCJLju/e7+d4nzpB+2xJFFOJE71MAVB+WtKB5jiyHxCOE96NVyT8B+wfHgJL +jIA5tIOXYHk2Bd3ZaKS6eIIaAm2BrK4IcJ+zUw/pAntDcNO7b00tCBxIq62fTb3W +wsCbYzQ5AgMBAAECggEAChvFihtUzF/CZPQ1kkmoA12i7KgXvV0zgKm8OtR5clxk +nzSiFy8eOLBTuJ1rg8DjLnzxwmkcRcNncH01od9eadJukn7n+luoALe4tfJtc3zI +eEyvpYkHFW6lbav3CyYfUCJjffSA/vzXSf2DBhAuF5MaRY1raYHaw61SeJU0qbgd +/H9yWYR49zB4csnTxAllJpKbIc1d2IargDgiv7GnzcA0dqROxcaXPU1cruuOag9f +iCd9awvMwy9XDqryEKyBGorN3WgT5TjMeFj8IBMLosOB15/Ruhuk/QNfijPPDS/R +KC7dX6BCHSu7cHHDjA43ikUZVerhOcq/P8ogbtOKiQKBgQDw51QTHz9BBCW5+7j6 +xvhYuosAuz2W+JJmMZlhy8SWHr4PIaQIG9E8CchssWiWSKPgDyHVfg2yTXP8Q8R+ +VT/AWAgeB3wrdMN6JNbNRPtkIGTtVgVDo7padBNbbPAN83+W3CdSF2RmJuAOInhb +Ez4Ctzq10kEyS1eTOT6yc9ujTQKBgQC14x5pdEHrRQpxpgq8qXxnqSlm030xtHJm +2z6oAxcsLVgwWcTRTU22ABJTV3939gJLhtFJ0p3DxfvCy/GOcawmeQsjpYs8j7gL +AIyYEMnxJTjzm+YwUn5uNl1xLeRa7P+YerIIwbC0tZxiNCvUo3IrTvfbIoBddCyZ +GhnqtK1GnQKBgCkIjiaPrPuLFE4AlXqJx6V9aM3gFtaPUoh7rE+fIMYdSGxVY5ZJ +/rLGS9BPy6vFhbxVd4Lg7L5ROQ9gD6khJjHCDOfoiHrycZVtjvT56gQdDHPssgra +aZScrutku+L0deghacUu3NgViRZ/QpboySg3Q5XS0W4arTkTiB1nZKMFAoGAWJBR +U5nHKy6/6hymZ7zDFZp5zVa3RAeQGOMyfA6dLuaZZVmgiyVv7GnWgnw9VgUUkv// +UknaheQWNYCmiuxwnX8c3GuUA5YbUEghLT4nhmLQe1Xy3J6ebz3Le/uTkG6L+gvs +OnVNfIBduDedC/nV8p6N80a2aErUGGxsKCt3n8UCgYEAt1hTr1a6HQheUv7RVC9i +KJu/k8n2olGUzE1pMZX6NlTgFKnmAQ7dLt6xeDBfMu0WBxbZktaSGM1T2Y0Tir+1 ++R/qH2inBE6FnL5rO8rb7ZiaDZy69Ntw4hJZGvryFcp6ZGraeUDqJO+e59xlQzmx +4/mvqmhlMJAYg7KJPfyH5tg= +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake_server.crt b/src/coro_rpc/tests/openssl_files/fake_server.crt deleted file mode 100644 index b56981813..000000000 --- a/src/coro_rpc/tests/openssl_files/fake_server.crt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -AIICIzCCAYwCCQDHoPajMKX7WDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIwODAzMDYwNjM3WhcNMzIw -NzMxMDYwNjM3WjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 -MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv -c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOpVxgJHmtZ88gja1MQm22O -ghYUT7T5X/85sJ0BenfghclyQ8KtxEDPGq4BSbheb+94C6x7YIY3GQ82y9R7nrcx -bcD3sGGRBn13ROyjz9HRUMTei2Uwd2yfIVY3Y0MDb0WMmjGnNTgj1h1YAYSWouC/ -4p/IitnVwmG+ZhwCxafhAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAuqmsA/xLqRW1 -9INE36S1T3xi56bIYdlYS2JRzXNrIqdb1RnNPHyFU0Tizv6dkUDec5VfunR5ZgAq -Hxsr5cowB1cdWi21FjjNGXrd1/nYHTdnxHfMGKFxjHXc+s5+qyFnmQatHJO8hyCR -ljmD56V/y1nMcFna0Gtear44Rh5s6fY= ------END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake_server.key b/src/coro_rpc/tests/openssl_files/fake_server.key deleted file mode 100644 index c55263b04..000000000 --- a/src/coro_rpc/tests/openssl_files/fake_server.key +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,D5B5BF16335844E5 - -X2Qj15pkVsN24Qu3N+sORjm0eDD+I2lrEfEI6QmMg33PV0udkNDEMtchJJ12Uzl/ -Czsj2ydFLHbFCUGGDqx7CfzYNBQhJvw74nrakXDv8uRdygvc0bEym35Nkukx5XNV -FL/jnTChO5ZneHHYYgtMDzsWN7+0oKTHyizN43nGNbBJJEy3hFje4k4ATFrCUXZt -z4OVge2M5JATR2ddFGX62ZrxFmg+PsbvSnfpxZesRXHtu0NPCK/Xv38vGYSaJ0s0 -XlK7A6Ko4O6RfBZXSXCO09GD27HA0zveQfLW2ILhbl4GZrAFd1d7d1Jdltxlvbw6 -cUp8dd3f6hAb5mAk1io4Xey++CEiwkpBgis9qAkFqxPPzwq+cB6LTu2KfzffvAze -FOZ7Yq5JyDLt7frpJ+PTFfv3bNyXdabBt6E9ZdEhIbvf/ArA+JuV4mJ8ilvsurnq -h54Z+R19V8kXUEekKDSYl2WsMrTY7o8ZY0LgiIbTG+LFqGjUpyclx6luz9r2d3yr -/ngKqbG30jct0Y7mDuVz8Vgml9wDqv4JmC/iUxZyGLuZlSRWFqV6RRCzGncMqy+w -wZb5XNGK2CwKCwmTtWuJm3rS88yo2MmJPtkbPeG9JRvUaAsg1uaExA5RRIk2zyIA -mwp0ItT4S63g6iObFadd7l2cDBdIJNMwJ/80FNr+HknBnkC3OYsIimrscoUS5vBc -V+a8C6wJ6A4IDrNXLP5so2PuygE47CGb6ehlzpda9Eyp030JLooeKt7sqWpX7ENA -JmSDScpG6xe5SR0buKVlP4Qf9IqpB6K8CnHsJWiJI40= ------END RSA PRIVATE KEY----- From 490259916d5cecf17ca2f3417938409740f4fb0f Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 9 Apr 2026 16:48:22 +0800 Subject: [PATCH 28/38] style: fix clang-format issues and update docs - Fix code formatting in SSL mutual auth test files - Add Testing to .gitignore - Update coro_rpc_client documentation (Chinese and English) --- .gitignore | 119 +- .../tests/test_cinatra_websocket.cpp | 891 +++++----- .../tests/test_http_ssl_mutual_auth.cpp | 392 ++--- src/coro_rpc/tests/test_coro_rpc_server.cpp | 1467 ++++++++--------- website/docs/en/coro_rpc/coro_rpc_client.md | 914 +++++----- website/docs/zh/coro_rpc/coro_rpc_client.md | 895 +++++----- 6 files changed, 2363 insertions(+), 2315 deletions(-) diff --git a/.gitignore b/.gitignore index 05c4269e5..daa20f28a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,59 +1,60 @@ -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -# -.idea -cmake-* -*build* -.fleet -.vs -out -.vscode -.cache -.coverage -.coverage_lcov -.coverage_llvm_cov -doxygen-awesome-css -html -CMakeUserPresets.json -.DS_Store -node_modules -/CMakeSettings.json - -# Bazel -bazel-* -!BUILD.bazel -cdb.json - -# website -website/docs/zh/*/images/ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# +.idea +cmake-* +*build* +.fleet +.vs +out +.vscode +.cache +.coverage +.coverage_lcov +.coverage_llvm_cov +doxygen-awesome-css +html +CMakeUserPresets.json +.DS_Store +node_modules +/CMakeSettings.json + +# Bazel +bazel-* +!BUILD.bazel +cdb.json + +# website +website/docs/zh/*/images/ +Testing/ diff --git a/src/coro_http/tests/test_cinatra_websocket.cpp b/src/coro_http/tests/test_cinatra_websocket.cpp index dfe58ab72..3aa7a4bea 100644 --- a/src/coro_http/tests/test_cinatra_websocket.cpp +++ b/src/coro_http/tests/test_cinatra_websocket.cpp @@ -1,446 +1,445 @@ -#include -#include -#include -#include -#include - -#include "cinatra/error.hpp" -#include "doctest.h" -#include "ylt/coro_http/coro_http_client.hpp" -#include "ylt/coro_http/coro_http_server.hpp" - -using namespace std::chrono_literals; - -using namespace coro_http; - -#ifdef CINATRA_ENABLE_SSL -TEST_CASE("test wss client") { - cinatra::coro_http_server server(1, 9001); - server.init_ssl("../openssl_files/server.crt", "../openssl_files/server.key", - "test"); - server.set_http_handler( - "/", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - std::this_thread::sleep_for(200ms); - - coro_http_client client{}; - bool ok = - client.init_ssl(asio::ssl::verify_peer, "../openssl_files/server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - - async_simple::coro::syncAwait(client.connect("wss://localhost:9001")); - - async_simple::coro::syncAwait(client.write_websocket("hello")); - auto data = async_simple::coro::syncAwait(client.read_websocket()); - CHECK(data.resp_body == "hello"); - - client.close(); - - server.stop(); -} -#endif - -async_simple::coro::Lazy test_websocket(coro_http_client &client) { - auto r = co_await client.connect("ws://localhost:8090/ws"); - if (r.net_err) { - co_return; - } - - co_await client.write_websocket("hello websocket"); - auto data = co_await client.read_websocket(); - CHECK(data.resp_body == "hello websocket"); - co_await client.write_websocket("test again"); - data = co_await client.read_websocket(); - CHECK(data.resp_body == "test again"); - co_await client.write_websocket_close("ws close"); - data = co_await client.read_websocket(); - CHECK(data.resp_body == "ws close"); - CHECK(data.net_err == asio::error::eof); -} - -#ifdef CINATRA_ENABLE_GZIP -async_simple::coro::Lazy test_gzip_websocket(coro_http_client &client) { - auto r = co_await client.connect("ws://localhost:8090/ws"); - if (r.net_err) { - co_return; - } - - std::string str = "hello websocket"; - co_await client.write_websocket(str.data(), str.size()); - auto data = co_await client.read_websocket(); - CHECK(data.resp_body == "hello websocket"); - - co_await client.write_websocket_close("ws close"); - data = co_await client.read_websocket(); - CHECK(data.resp_body == "ws close"); - CHECK(data.net_err == asio::error::eof); -} -#endif - -TEST_CASE("test websocket") { - cinatra::coro_http_server server(1, 8090); - server.set_http_handler( - "/ws", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.set_http_handler( - "/test_client_timeout", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - std::this_thread::sleep_for(200ms); - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - - auto client_timeout = []() -> async_simple::coro::Lazy { - coro_http_client client{}; - client.set_req_timeout(50ms); - client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); - - auto r = co_await client.connect("ws://localhost:8090/test_client_timeout"); - if (r.net_err) { - co_return; - } - - co_await client.write_websocket("hello websocket"); - auto data = co_await client.read_websocket(); - CINATRA_LOG_DEBUG << data.net_err.message(); - CHECK(data.net_err == http_errc::request_timeout); - }; - - async_simple::coro::syncAwait(client_timeout()); - - coro_http_client client{}; - client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); - - async_simple::coro::syncAwait(test_websocket(client)); - -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - { - auto lazy1 = []() -> async_simple::coro::Lazy { - coro_http_client client{}; - co_await client.connect("ws://localhost:8090/ws"); - std::string send_str = "test"; - websocket ws{}; - // msg too long - auto header = ws.encode_ws_header(9 * 1024 * 1024, opcode::text, true); - co_await client.async_write_raw(header); - co_await client.async_write_raw(send_str); - auto data = co_await client.read_websocket(); - CHECK(data.status != 200); - CINATRA_LOG_DEBUG << data.resp_body; - }; - async_simple::coro::syncAwait(lazy1()); - } - - { - auto lazy1 = []() -> async_simple::coro::Lazy { - coro_http_client client{}; - co_await client.connect("ws://localhost:8090/ws"); - std::string send_str = "test"; - websocket ws{}; - // error frame - auto header = ws.encode_ws_header(send_str.size(), (opcode)15, true); - co_await client.async_write_raw(header); - co_await client.async_write_raw(send_str); - auto data = co_await client.read_websocket(); - CHECK(data.status != 200); - }; - async_simple::coro::syncAwait(lazy1()); - } -#endif - -#ifdef CINATRA_ENABLE_GZIP - coro_http_client client1{}; - client1.set_ws_deflate(true); - async_simple::coro::syncAwait(test_gzip_websocket(client1)); -#endif - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - // client->async_close(); -} - -void test_websocket_content(size_t len) { - cinatra::coro_http_server server(1, 8090); - server.set_http_handler( - "/", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - - auto lazy = [len]() -> async_simple::coro::Lazy { - coro_http_client client{}; - co_await client.connect("ws://localhost:8090"); - std::string send_str(len, 'a'); - co_await client.write_websocket(std::string(send_str)); - auto data = co_await client.read_websocket(); - REQUIRE(data.resp_body.size() == send_str.size()); - CHECK(data.resp_body == send_str); - }; - - async_simple::coro::syncAwait(lazy()); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - server.stop(); -} - -TEST_CASE("test websocket content lt 126") { - test_websocket_content(1); - test_websocket_content(125); -} - -TEST_CASE("test websocket content ge 126") { - test_websocket_content(126); - test_websocket_content(127); -} - -TEST_CASE("test websocket content ge 65535") { - test_websocket_content(65535); - test_websocket_content(65536); -} - -TEST_CASE("test send after server stop") { - cinatra::coro_http_server server(1, 8090); - server.async_start(); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - coro_http_client client{}; - async_simple::coro::syncAwait(client.connect("ws://127.0.0.1:8090")); - - server.stop(); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - async_simple::coro::syncAwait(client.write_websocket("")); - auto data = async_simple::coro::syncAwait(client.read_websocket()); - CHECK(data.net_err); -} - -TEST_CASE("test read write in different threads") { - cinatra::coro_http_server server(1, 8090); - size_t count = 0; - std::promise promise; - server.set_http_handler( - "/", - [&](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - count++; - if (count == 100) { - promise.set_value(); - break; - } - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - - auto client = std::make_shared(); - std::string send_str(100, 'a'); - std::weak_ptr weak = client; - auto another_thread_lazy = [client, - send_str]() -> async_simple::coro::Lazy { - for (int i = 0; i < 100; i++) { - auto data = co_await client->read_websocket(); - if (data.net_err) { - co_return; - } - REQUIRE(data.resp_body.size() == send_str.size()); - CHECK(data.resp_body == send_str); - } - }; - another_thread_lazy().start([](auto &&) { - }); - - auto lazy = [client, weak, &send_str]() -> async_simple::coro::Lazy { - co_await client->connect("ws://localhost:8090"); - for (int i = 0; i < 100; i++) { - auto data = co_await client->write_websocket(std::string(send_str)); - if (data.net_err) { - co_return; - } - } - }; - - async_simple::coro::syncAwait(lazy().via(&client->get_executor())); - - promise.get_future().wait_for(std::chrono::seconds(2)); - - server.stop(); -} - -async_simple::coro::Lazy test_websocket() { - coro_http_client client{}; - auto r = co_await client.connect("ws://127.0.0.1:8089/ws_echo"); - if (r.net_err) { - co_return; - } - - co_await client.write_websocket(std::string_view("test2fdsaf"), - opcode::binary); - auto data = co_await client.read_websocket(); - CHECK(data.resp_body == "test2fdsaf"); - - co_await client.write_websocket_close("ws close"); - data = co_await client.read_websocket(); - CHECK(data.net_err == asio::error::eof); - CHECK(data.resp_body == "ws close"); -} - -TEST_CASE("test client quit after send msg") { - coro_http_server server(1, 8089); - server.set_http_handler( - "/ws_echo", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - websocket_result result{}; - - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - if (result.type == ws_frame_type::WS_CLOSE_FRAME) { - break; - } - - co_await resp.get_conn()->write_websocket(result.data); - } - }); - server.async_start(); - - async_simple::coro::syncAwait(test_websocket()); -} - -#ifdef CINATRA_ENABLE_GZIP -TEST_CASE("test websocket permessage defalte") { - coro_http_server server(1, 8090); - server.set_http_handler( - "/ws_extesion", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - if (result.type == ws_frame_type::WS_CLOSE_FRAME) { - CINATRA_LOG_DEBUG << "close frame\n"; - break; - } - - if (result.type == ws_frame_type::WS_TEXT_FRAME || - result.type == ws_frame_type::WS_BINARY_FRAME) { - CHECK(result.data == "test"); - } - else if (result.type == ws_frame_type::WS_PING_FRAME || - result.type == ws_frame_type::WS_PONG_FRAME) { - // ping pong frame just need to continue, no need echo anything, - // because framework has reply ping/pong msg to client - // automatically. - continue; - } - else { - // error frame - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - - server.async_start(); - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - coro_http_client client{}; - client.set_ws_deflate(true); - async_simple::coro::syncAwait( - client.connect("ws://localhost:8090/ws_extesion")); - - std::string send_str("test"); - - async_simple::coro::syncAwait(client.write_websocket(send_str)); - auto data = async_simple::coro::syncAwait(client.read_websocket()); - CHECK(data.resp_body == "test"); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - server.stop(); - client.close(); -} -#endif +#include +#include +#include +#include +#include + +#include "cinatra/error.hpp" +#include "doctest.h" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" + +using namespace std::chrono_literals; + +using namespace coro_http; + +#ifdef CINATRA_ENABLE_SSL +TEST_CASE("test wss client") { + cinatra::coro_http_server server(1, 9001); + server.init_ssl("../openssl_files/server.crt", "../openssl_files/server.key", + "test"); + server.set_http_handler( + "/", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + std::this_thread::sleep_for(200ms); + + coro_http_client client{}; + bool ok = client.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + + async_simple::coro::syncAwait(client.connect("wss://localhost:9001")); + + async_simple::coro::syncAwait(client.write_websocket("hello")); + auto data = async_simple::coro::syncAwait(client.read_websocket()); + CHECK(data.resp_body == "hello"); + + client.close(); + + server.stop(); +} +#endif + +async_simple::coro::Lazy test_websocket(coro_http_client &client) { + auto r = co_await client.connect("ws://localhost:8090/ws"); + if (r.net_err) { + co_return; + } + + co_await client.write_websocket("hello websocket"); + auto data = co_await client.read_websocket(); + CHECK(data.resp_body == "hello websocket"); + co_await client.write_websocket("test again"); + data = co_await client.read_websocket(); + CHECK(data.resp_body == "test again"); + co_await client.write_websocket_close("ws close"); + data = co_await client.read_websocket(); + CHECK(data.resp_body == "ws close"); + CHECK(data.net_err == asio::error::eof); +} + +#ifdef CINATRA_ENABLE_GZIP +async_simple::coro::Lazy test_gzip_websocket(coro_http_client &client) { + auto r = co_await client.connect("ws://localhost:8090/ws"); + if (r.net_err) { + co_return; + } + + std::string str = "hello websocket"; + co_await client.write_websocket(str.data(), str.size()); + auto data = co_await client.read_websocket(); + CHECK(data.resp_body == "hello websocket"); + + co_await client.write_websocket_close("ws close"); + data = co_await client.read_websocket(); + CHECK(data.resp_body == "ws close"); + CHECK(data.net_err == asio::error::eof); +} +#endif + +TEST_CASE("test websocket") { + cinatra::coro_http_server server(1, 8090); + server.set_http_handler( + "/ws", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.set_http_handler( + "/test_client_timeout", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + std::this_thread::sleep_for(200ms); + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + + auto client_timeout = []() -> async_simple::coro::Lazy { + coro_http_client client{}; + client.set_req_timeout(50ms); + client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); + + auto r = co_await client.connect("ws://localhost:8090/test_client_timeout"); + if (r.net_err) { + co_return; + } + + co_await client.write_websocket("hello websocket"); + auto data = co_await client.read_websocket(); + CINATRA_LOG_DEBUG << data.net_err.message(); + CHECK(data.net_err == http_errc::request_timeout); + }; + + async_simple::coro::syncAwait(client_timeout()); + + coro_http_client client{}; + client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); + + async_simple::coro::syncAwait(test_websocket(client)); + +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + { + auto lazy1 = []() -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await client.connect("ws://localhost:8090/ws"); + std::string send_str = "test"; + websocket ws{}; + // msg too long + auto header = ws.encode_ws_header(9 * 1024 * 1024, opcode::text, true); + co_await client.async_write_raw(header); + co_await client.async_write_raw(send_str); + auto data = co_await client.read_websocket(); + CHECK(data.status != 200); + CINATRA_LOG_DEBUG << data.resp_body; + }; + async_simple::coro::syncAwait(lazy1()); + } + + { + auto lazy1 = []() -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await client.connect("ws://localhost:8090/ws"); + std::string send_str = "test"; + websocket ws{}; + // error frame + auto header = ws.encode_ws_header(send_str.size(), (opcode)15, true); + co_await client.async_write_raw(header); + co_await client.async_write_raw(send_str); + auto data = co_await client.read_websocket(); + CHECK(data.status != 200); + }; + async_simple::coro::syncAwait(lazy1()); + } +#endif + +#ifdef CINATRA_ENABLE_GZIP + coro_http_client client1{}; + client1.set_ws_deflate(true); + async_simple::coro::syncAwait(test_gzip_websocket(client1)); +#endif + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + // client->async_close(); +} + +void test_websocket_content(size_t len) { + cinatra::coro_http_server server(1, 8090); + server.set_http_handler( + "/", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + + auto lazy = [len]() -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await client.connect("ws://localhost:8090"); + std::string send_str(len, 'a'); + co_await client.write_websocket(std::string(send_str)); + auto data = co_await client.read_websocket(); + REQUIRE(data.resp_body.size() == send_str.size()); + CHECK(data.resp_body == send_str); + }; + + async_simple::coro::syncAwait(lazy()); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + server.stop(); +} + +TEST_CASE("test websocket content lt 126") { + test_websocket_content(1); + test_websocket_content(125); +} + +TEST_CASE("test websocket content ge 126") { + test_websocket_content(126); + test_websocket_content(127); +} + +TEST_CASE("test websocket content ge 65535") { + test_websocket_content(65535); + test_websocket_content(65536); +} + +TEST_CASE("test send after server stop") { + cinatra::coro_http_server server(1, 8090); + server.async_start(); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + coro_http_client client{}; + async_simple::coro::syncAwait(client.connect("ws://127.0.0.1:8090")); + + server.stop(); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + async_simple::coro::syncAwait(client.write_websocket("")); + auto data = async_simple::coro::syncAwait(client.read_websocket()); + CHECK(data.net_err); +} + +TEST_CASE("test read write in different threads") { + cinatra::coro_http_server server(1, 8090); + size_t count = 0; + std::promise promise; + server.set_http_handler( + "/", + [&](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + count++; + if (count == 100) { + promise.set_value(); + break; + } + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + + auto client = std::make_shared(); + std::string send_str(100, 'a'); + std::weak_ptr weak = client; + auto another_thread_lazy = [client, + send_str]() -> async_simple::coro::Lazy { + for (int i = 0; i < 100; i++) { + auto data = co_await client->read_websocket(); + if (data.net_err) { + co_return; + } + REQUIRE(data.resp_body.size() == send_str.size()); + CHECK(data.resp_body == send_str); + } + }; + another_thread_lazy().start([](auto &&) { + }); + + auto lazy = [client, weak, &send_str]() -> async_simple::coro::Lazy { + co_await client->connect("ws://localhost:8090"); + for (int i = 0; i < 100; i++) { + auto data = co_await client->write_websocket(std::string(send_str)); + if (data.net_err) { + co_return; + } + } + }; + + async_simple::coro::syncAwait(lazy().via(&client->get_executor())); + + promise.get_future().wait_for(std::chrono::seconds(2)); + + server.stop(); +} + +async_simple::coro::Lazy test_websocket() { + coro_http_client client{}; + auto r = co_await client.connect("ws://127.0.0.1:8089/ws_echo"); + if (r.net_err) { + co_return; + } + + co_await client.write_websocket(std::string_view("test2fdsaf"), + opcode::binary); + auto data = co_await client.read_websocket(); + CHECK(data.resp_body == "test2fdsaf"); + + co_await client.write_websocket_close("ws close"); + data = co_await client.read_websocket(); + CHECK(data.net_err == asio::error::eof); + CHECK(data.resp_body == "ws close"); +} + +TEST_CASE("test client quit after send msg") { + coro_http_server server(1, 8089); + server.set_http_handler( + "/ws_echo", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + websocket_result result{}; + + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + if (result.type == ws_frame_type::WS_CLOSE_FRAME) { + break; + } + + co_await resp.get_conn()->write_websocket(result.data); + } + }); + server.async_start(); + + async_simple::coro::syncAwait(test_websocket()); +} + +#ifdef CINATRA_ENABLE_GZIP +TEST_CASE("test websocket permessage defalte") { + coro_http_server server(1, 8090); + server.set_http_handler( + "/ws_extesion", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + if (result.type == ws_frame_type::WS_CLOSE_FRAME) { + CINATRA_LOG_DEBUG << "close frame\n"; + break; + } + + if (result.type == ws_frame_type::WS_TEXT_FRAME || + result.type == ws_frame_type::WS_BINARY_FRAME) { + CHECK(result.data == "test"); + } + else if (result.type == ws_frame_type::WS_PING_FRAME || + result.type == ws_frame_type::WS_PONG_FRAME) { + // ping pong frame just need to continue, no need echo anything, + // because framework has reply ping/pong msg to client + // automatically. + continue; + } + else { + // error frame + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + + server.async_start(); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + coro_http_client client{}; + client.set_ws_deflate(true); + async_simple::coro::syncAwait( + client.connect("ws://localhost:8090/ws_extesion")); + + std::string send_str("test"); + + async_simple::coro::syncAwait(client.write_websocket(send_str)); + auto data = async_simple::coro::syncAwait(client.read_websocket()); + CHECK(data.resp_body == "test"); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + server.stop(); + client.close(); +} +#endif diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 899aa8601..0258ace19 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -1,196 +1,196 @@ -/* - * Copyright (c) 2025, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "async_simple/coro/Lazy.h" -#include "doctest.h" -#include "ylt/coro_http/coro_http_client.hpp" -#include "ylt/coro_http/coro_http_server.hpp" - -using namespace coro_http; - -using namespace std::chrono_literals; - -const std::string CERT_PATH = "../openssl_files/"; -const std::string SERVER_CERT = "server.crt"; -const std::string SERVER_KEY = "server.key"; -const std::string CLIENT_CERT = "client.crt"; -const std::string CLIENT_KEY = "client.key"; -const std::string CA_CERT = "ca.crt"; - -#ifdef YLT_ENABLE_SSL -TEST_CASE("testing HTTP SSL one-way authentication") { - coro_http_server server(1, 8901); - - server.init_ssl((CERT_PATH + SERVER_CERT), - (CERT_PATH + SERVER_KEY), "", "", false); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Hello World"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.get("http://127.0.0.1:8901/"); - REQUIRE_MESSAGE(result.status == 200, "GET request failed"); - REQUIRE_MESSAGE(result.resp_body == "Hello World", "response body mismatch"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - success") { - coro_http_server server(1, 8902); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Hello Mutual Auth"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, - CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.get("http://127.0.0.1:8902/"); - REQUIRE_MESSAGE(result.status == 200, "GET request failed"); - REQUIRE_MESSAGE(result.resp_body == "Hello Mutual Auth", - "response body mismatch"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { - coro_http_server server(1, 8903); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Should not reach here"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.get("http://127.0.0.1:8903/"); - REQUIRE_MESSAGE(result.status != 200, - "request should fail without client cert"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { - coro_http_server server(1, 8904); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Should not reach here"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, - "invalid_client.crt", "invalid_client.key", "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl should succeed"); - - auto result = client.get("http://127.0.0.1:8904/"); - REQUIRE_MESSAGE(result.status != 200, - "request should fail with invalid client cert"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - POST request") { - coro_http_server server(1, 8905); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/echo", - [](coro_http_request& req, - coro_http_response& resp) -> async_simple::coro::Lazy { - std::string_view body = req.get_body(); - resp.set_status_and_content(status_type::ok, - "Echo: " + std::string(body)); - co_return; - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, - CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.post("http://127.0.0.1:8905/echo", "Test Message", - req_content_type::text); - REQUIRE_MESSAGE(result.status == 200, "POST request failed"); - REQUIRE_MESSAGE(result.resp_body == "Echo: Test Message", - "response body mismatch"); - - server.stop(); - thd.join(); -} -#endif +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "async_simple/coro/Lazy.h" +#include "doctest.h" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" + +using namespace coro_http; + +using namespace std::chrono_literals; + +const std::string CERT_PATH = "../openssl_files/"; +const std::string SERVER_CERT = "server.crt"; +const std::string SERVER_KEY = "server.key"; +const std::string CLIENT_CERT = "client.crt"; +const std::string CLIENT_KEY = "client.key"; +const std::string CA_CERT = "ca.crt"; + +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing HTTP SSL one-way authentication") { + coro_http_server server(1, 8901); + + server.init_ssl((CERT_PATH + SERVER_CERT), + (CERT_PATH + SERVER_KEY), "", "", false); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello World"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("https://127.0.0.1:8901/"); + REQUIRE_MESSAGE(result.status == 200, "GET request failed"); + REQUIRE_MESSAGE(result.resp_body == "Hello World", "response body mismatch"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - success") { + coro_http_server server(1, 8902); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello Mutual Auth"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("https://127.0.0.1:8902/"); + REQUIRE_MESSAGE(result.status == 200, "GET request failed"); + REQUIRE_MESSAGE(result.resp_body == "Hello Mutual Auth", + "response body mismatch"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { + coro_http_server server(1, 8903); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Should not reach here"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("https://127.0.0.1:8903/"); + REQUIRE_MESSAGE(result.status != 200, + "request should fail without client cert"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { + coro_http_server server(1, 8904); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Should not reach here"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + // Use client cert but wrong CA for server verification - this will cause + // handshake failure + bool init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, "wrong_ca.crt", + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + // When CA cert file doesn't exist, init_ssl returns false + REQUIRE_MESSAGE(init_ok == false, + "client init_ssl should fail with wrong CA cert"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - POST request") { + coro_http_server server(1, 8905); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + std::string_view body = req.get_body(); + resp.set_status_and_content(status_type::ok, + "Echo: " + std::string(body)); + co_return; + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.post("https://127.0.0.1:8905/echo", "Test Message", + req_content_type::text); + REQUIRE_MESSAGE(result.status == 200, "POST request failed"); + REQUIRE_MESSAGE(result.resp_body == "Echo: Test Message", + "response body mismatch"); + + server.stop(); + thd.join(); +} +#endif diff --git a/src/coro_rpc/tests/test_coro_rpc_server.cpp b/src/coro_rpc/tests/test_coro_rpc_server.cpp index 582b9be99..be8b9a819 100644 --- a/src/coro_rpc/tests/test_coro_rpc_server.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_server.cpp @@ -1,735 +1,734 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include - -#include "ServerTester.hpp" -#include "async_simple/coro/Lazy.h" -#include "cinatra/response_cv.hpp" -#include "doctest.h" -#include "rpc_api.hpp" -#include "ylt/coro_http/coro_http_client.hpp" -#include "ylt/coro_http/coro_http_server.hpp" -#include "ylt/coro_io/coro_io.hpp" -#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" -#include "ylt/coro_rpc/impl/errno.h" -#include "ylt/struct_pack.hpp" - -async_simple::coro::Lazy get_coro_value(int val) { co_return val; } - -struct CoroServerTester : ServerTester { - CoroServerTester(TesterConfig config) - : ServerTester(config), - server(2, config.port, config.address, config.conn_timeout_duration) { -#ifdef YLT_ENABLE_SSL - if (use_ssl) { - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); - } -#endif -#ifdef YLT_ENABLE_IBV - if (use_rdma) { - server.init_ibv(); - } -#endif - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - } - ~CoroServerTester() { server.stop(); } - - async_simple::coro::Lazy get_value(int val) { co_return val; } - - void test_set_server_address() { - { - coro_rpc_server server(1, 9001); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "0.0.0.0"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "127.0.0.1"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "localhost"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, "0.0.0.0:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, "127.0.0.1:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, "localhost:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "x.x.x.x"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(server.get_errc() == coro_rpc::errc::bad_address); - } - - { - coro_rpc_server server(1, "x.x.x.x:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(server.get_errc() == coro_rpc::errc::bad_address); - } - - { - coro_rpc_server server(1, "127.0.0.1:aaa"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(server.get_errc() == coro_rpc::errc::bad_address); - } - } - - void test_all() override { - g_action = {}; - ELOGV(INFO, "run %s", __func__); - test_set_server_address(); - test_coro_handler(); - ServerTester::test_all(); - test_function_not_registered(); - test_start_new_server_with_same_port(); - test_server_send_bad_rpc_result(); - test_server_send_no_body(); - test_context_func(); - test_return_err_by_throw_exception(); - this->test_call_with_delay_func(); - this->test_call_with_delay_func< - coro_fun_with_user_define_connection_type>(); - this->test_call_with_delay_func(); - this->test_call_with_delay_func_client_read_length_error< - coro_fun_with_delay_return_void>(); - this->test_call_with_delay_func_client_read_body_error< - coro_fun_with_delay_return_void>(); - if (enable_heartbeat) { - this->test_call_with_delay_func_server_timeout< - coro_fun_with_delay_return_void_cost_long_time>(); - } - this->test_call_with_delay_func(); - this->test_call_with_delay_func(); - } - void register_all_function() override { - g_action = {}; - ELOGV(INFO, "run %s", __func__); - server.register_handler(); - server.register_handler(); - server.register_handler<&ns_login::LoginService::login>(&login_service_); - server.register_handler<&HelloService::hello>(&hello_service_); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler<&HelloService::coro_func, - &HelloService::coro_func_return_void>( - &hello_service_); - server.register_handler(); - server.register_handler<&CoroServerTester::get_value>(this); - } - - void test_context_func() { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - client->set_req_attachment("1234567890987654321234567890"); - auto result = syncAwait(client->call()); - CHECK(result); - CHECK(client->get_resp_attachment() == "1234567890987654321234567890"); - - client->set_req_attachment("12345678909876543212345678901"); - result = syncAwait(client->call()); - CHECK(result); - CHECK(client->get_resp_attachment() == "12345678909876543212345678901"); - - client->set_req_attachment("01234567890987654321234567890"); - result = syncAwait(client->call()); - CHECK(result); - CHECK(client->get_resp_attachment() == "01234567890987654321234567890"); - } - void test_return_err_by_throw_exception() { - { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto result = syncAwait(client->call()); - REQUIRE(!result); - CHECK(result.error().code == coro_rpc::errc::address_in_used); - CHECK(result.error().msg == "error with user-defined msg"); - } - { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto result = syncAwait(client->call()); - REQUIRE(!result); - CHECK(result.error().code == coro_rpc::errc::address_in_used); - CHECK(result.error().msg == "error with user-defined msg"); - } - } - - void test_function_not_registered() { - g_action = {}; - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::function_not_registered, - std::to_string(client->get_client_id()).append(ret.error().msg)); - REQUIRE(client->has_closed() == true); - ret = call(client); - CHECK(client->has_closed() == true); - ret = call(client); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - CHECK(client->has_closed() == true); - } - - void test_server_start_again() { - ELOGV(INFO, "run %s", __func__); - - auto ec = server.start(); - REQUIRE_MESSAGE(ec == coro_rpc::errc::io_error, ec.message()); - } - - void test_start_new_server_with_same_port() { - ELOGV(INFO, "run %s", __func__); - { - auto new_server = coro_rpc_server(2, std::stoi(this->port_)); - auto ec = new_server.async_start(); - REQUIRE(ec.hasResult()); - REQUIRE_MESSAGE(ec.value() == coro_rpc::errc::address_in_used, - ec.value().message()); - } - ELOGV(INFO, "OH NO"); - } - void test_server_send_bad_rpc_result() { - auto client = create_client(inject_action::server_send_bad_rpc_result); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = this->call(client); - CHECK_MESSAGE( - ret.error().code == coro_rpc::errc::invalid_rpc_result, - std::to_string(client->get_client_id()).append(ret.error().msg)); - g_action = {}; - } - - void test_server_send_no_body() { - auto client = create_client(inject_action::close_socket_after_send_length); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = this->template call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - g_action = {}; - } - - void test_coro_handler() { - auto client = create_client(inject_action::nothing); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = this->template call(client, 42); - CHECK(ret.value() == 42); - - auto ret1 = this->template call<&HelloService::coro_func>(client, 42); - CHECK(ret1.value() == 42); - - auto ret2 = this->template call<&CoroServerTester::get_value>(client, 42); - CHECK(ret2.value() == 42); - - auto ret3 = this->template call(client, 42); - CHECK(ret3.value() == 42); - - auto ret4 = this->template call(client, 42); - CHECK(ret4.has_value()); - - auto ret5 = - this->template call<&HelloService::coro_func_return_void>(client, 42); - CHECK(ret5.has_value()); - } - coro_rpc_server server; - std::thread thd; - HelloService hello_service_; -}; -TEST_CASE("testing coro rpc server") { - ELOGV(INFO, "run testing coro rpc server"); - unsigned short server_port = 8810; - auto conn_timeout_duration = 500ms; - std::vector switch_list{0 -#ifdef YLT_ENABLE_SSL - , - 1 -#endif -#ifdef YLT_ENABLE_IBV - , - 2 -#endif - }; - for (auto enable_heartbeat : switch_list) { - for (auto type : switch_list) { - TesterConfig config; - config.enable_heartbeat = enable_heartbeat; - if (type == 0) { - config.use_ssl = false; - config.use_rdma = false; - } - else if (type == 1) { - config.use_ssl = true; - config.use_rdma = false; - } - else if (type == 2) { - config.use_ssl = false; - config.use_rdma = true; - } - config.sync_client = false; - config.use_outer_io_context = false; - config.port = server_port; - if (enable_heartbeat) { - config.conn_timeout_duration = conn_timeout_duration; - } - std::stringstream ss; - ss << config; - ELOGV(INFO, "config: %s", ss.str().data()); - CoroServerTester(config).run(); - } - // } - } -} - -TEST_CASE("testing coro rpc server stop") { - ELOGV(INFO, "run testing coro rpc server stop"); - coro_rpc_server server(2, 8810); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - SUBCASE("stop twice") { - server.stop(); - server.stop(); - } - SUBCASE("stop in different thread") { - std::thread thd1([&server]() { - server.stop(); - }); - std::thread thd2([&server]() { - server.stop(); - }); - thd1.join(); - thd2.join(); - } -} - -TEST_CASE("test server accept error") { - ELOGV(INFO, "run test server accept error"); - g_action = inject_action::force_inject_server_accept_error; - coro_rpc_server server(2, 8810); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client(coro_io::get_global_executor()); - ELOGV(INFO, "run test server accept error, client_id %d", - client.get_client_id()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - REQUIRE(client.has_closed() == true); - g_action = {}; -} - -TEST_CASE("test server write queue") { - using coro_rpc_protocol = coro_rpc::protocol::coro_rpc_protocol; - ELOGV(INFO, "run server write queue"); - g_action = {}; - coro_rpc_server server(2, 8810); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - std::string buffer; - buffer.reserve(coro_rpc_protocol::REQ_HEAD_LEN + - struct_pack::get_needed_size(std::monostate{})); - buffer.resize(coro_rpc_protocol::REQ_HEAD_LEN); - auto &header = *(coro_rpc_protocol::req_header *)buffer.data(); - header.magic = coro_rpc_protocol::magic_number; - header.function_id = - func_id(); - header.seq_num = g_client_id++; - header.length = struct_pack::get_needed_size(std::monostate{}); - ELOGV(INFO, "client_id %d begin to connect %d", header.seq_num, 8820); - struct_pack::serialize_to(buffer, std::monostate{}); - asio::io_context io_context; - std::thread thd([&io_context]() { - asio::io_context::work work(io_context); - io_context.run(); - }); - asio::ip::tcp::socket socket(io_context); - auto ret = coro_io::connect(io_context, socket, "127.0.0.1", "8810"); - CHECK(!ret); - ELOGV(INFO, "%s client_id %d call %s", "sync_client", header.seq_num, - "coro_fun_with_delay_return_void_cost_long_time"); - for (int i = 0; i < 1; ++i) { - auto err = - coro_io::write(socket, asio::buffer(buffer.data(), buffer.size())); - CHECK(err.second == buffer.size()); - } - for (int i = 0; i < 1; ++i) { - char buffer2[coro_rpc_protocol::RESP_HEAD_LEN]; - std::monostate r; - auto buf = struct_pack::serialize(r); - std::string buffer_read; - buffer_read.resize(buf.size()); - read(socket, asio::buffer(buffer2, coro_rpc_protocol::RESP_HEAD_LEN)); - [[maybe_unused]] auto resp_head = - *(coro_rpc_protocol::resp_header *)buffer2; - uint32_t body_len = header.length; - CHECK(body_len == buf.size()); - read(socket, asio::buffer(buffer_read, body_len)); - std::monostate r2; - std::size_t sz; - auto ret = - struct_pack::deserialize_to(r2, buffer_read.data(), body_len, sz); - CHECK(!ret); - CHECK(sz == body_len); - CHECK(r2 == r); - } - - ELOGV(INFO, "client_id %d close", header.seq_num); - asio::error_code ignored_ec; - socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); - socket.close(ignored_ec); - io_context.stop(); - thd.join(); - server.stop(); -} - -TEST_CASE("testing coro rpc write error") { - ELOGV(INFO, "run testing coro rpc write error"); - g_action = inject_action::force_inject_connection_close_socket; - coro_rpc_server server(2, 8810); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - ELOGV(INFO, "run testing coro rpc write error, client_id %d", - client.get_client_id()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client.get_client_id()).append(ret.error().msg)); - REQUIRE(client.has_closed() == true); - g_action = inject_action::nothing; -} - -TEST_CASE("testing ipv6") { - using namespace coro_http; - coro_rpc_server server(1, "[::1]:8811"); - server.register_handler(); - auto res = server.async_start(); - if (res.hasResult()) { - return; - } - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client{}; - auto ec = syncAwait(client.connect("[::1]:8811")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - CHECK(ret); - - coro_rpc_client client1{}; - ec = syncAwait(client1.connect("::1:8811")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - ret = syncAwait(client1.call()); - CHECK(ret); - - coro_http::coro_http_server http_server(1, 8812, "::1"); - http_server.set_http_handler( - "/test", [](coro_http_request &req, coro_http_response &resp) { - resp.set_status_and_content(status_type::ok, "ok"); - }); - http_server.async_start(); - coro_http::coro_http_client htttp_client{}; - auto r = syncAwait(htttp_client.connect("http://[::1]:8812")); - CHECK(!r.net_err); - auto result = htttp_client.get("/test"); - CHECK(result.status == 200); - - coro_http::coro_http_client htttp_client1{}; - result = htttp_client1.get("http://[::1]:8812/test"); - CHECK(result.status == 200); -} - -TEST_CASE("testing coro rpc subserver") { - ELOGV(INFO, "run testing coro rpc subserver"); - std::string http_body = R"( - - - - Example - - -

This is an example of a simple HTML page with one paragraph.

- -)"; - coro_rpc_server server(2, 8810); - server.register_handler(); - std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, - std::string_view magic_number, - coro_http::coro_http_server &server) { - CHECK(magic_number == "G"); - server.transfer_connection(std::move(soc), magic_number); - }; - auto http_server = std::make_unique(0, 0); - http_server->set_http_handler( - "/index.html", - [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { - resp.set_status_and_content(coro_http::status_type::ok, http_body); - }); - server.add_subserver(std::move(dispatcher), std::move(http_server)); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); - coro_http::coro_http_client cli; - auto result = syncAwait(cli.connect("localhost:8810")); - CHECK_MESSAGE(!result.net_err, result.net_err.message()); - result = syncAwait(cli.async_get("/index.html")); - CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, - result.status); - CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -} -#ifdef YLT_ENABLE_SSL -TEST_CASE("testing coro rpc ssl subserver") { - ELOGV(INFO, "run testing coro rpc ssl subserver"); - std::string http_body = R"( - - - - Example - - -

This is an example of a simple HTML page with one paragraph.

- -)"; - coro_rpc_server server(2, 8810); - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); - server.register_handler(); - std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, - std::string_view magic_number, - coro_http::coro_http_server &server) { - CHECK(magic_number == "G"); - server.transfer_connection(std::move(soc), magic_number); - }; - auto http_server = std::make_unique(0, 0); - http_server->set_http_handler( - "/index.html", - [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { - resp.set_status_and_content(coro_http::status_type::ok, http_body); - }); - server.add_subserver(std::move(dispatcher), std::move(http_server)); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - CHECK(client.init_ssl("../openssl_files", "server.crt")); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); - coro_http::coro_http_client cli; - CHECK_MESSAGE( - cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/server.crt"), - "init ssl failed"); - auto result = syncAwait(cli.connect("https://localhost:8810")); - CHECK_MESSAGE(!result.net_err, result.net_err.message()); - result = syncAwait(cli.async_get("/index.html")); - CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, - result.net_err.message()); - CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -} -#ifdef YLT_ENABLE_IBV -TEST_CASE("testing coro rpc non-rdma client to rdma server") { - ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); - coro_rpc_server server(2, 8810); - server.init_ibv(); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); -} -TEST_CASE("testing coro rpc subserver with rdma") { - ELOGV(INFO, "run testing coro rpc subserver with rdma"); - std::string http_body = R"( - - - - Example - - -

This is an example of a simple HTML page with one paragraph.

- -)"; - coro_rpc_server server(2, 8810); - server.init_ibv(); - server.register_handler(); - std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, - std::string_view magic_number, - coro_http::coro_http_server &server) { - CHECK(magic_number == "G"); - server.transfer_connection(std::move(soc), magic_number); - }; - auto http_server = std::make_unique(0, 0); - http_server->set_http_handler( - "/index.html", - [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { - resp.set_status_and_content(coro_http::status_type::ok, http_body); - }); - server.add_subserver(std::move(dispatcher), std::move(http_server)); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - bool is_ok = client.init_ibv(); - REQUIRE_MESSAGE(is_ok, "init ibv failed"); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); - coro_http::coro_http_client cli; - auto result = syncAwait(cli.connect("localhost:8810")); - CHECK_MESSAGE(!result.net_err, result.net_err.message()); - result = syncAwait(cli.async_get("/index.html")); - CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, - result.status); - CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -} -#ifdef YLT_ENABLE_SSL - -// TODO: open it when whe support rdma client with ssl-encrypt connection - -// TEST_CASE("testing coro rpc non-rdma ssl client to ssl rdma server") { -// ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); -// coro_rpc_server server(2, 8810); -// server.init_ibv(); -// server.init_ssl( -// ssl_configure{"../openssl_files", "server.crt", "server.key"}); -// server.register_handler(); -// auto res = server.async_start(); -// CHECK_MESSAGE(!res.hasResult(), "server start failed"); -// coro_rpc_client client(coro_io::get_global_executor()); -// CHECK(client.init_ssl("../openssl_files", "server.crt")); -// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); -// REQUIRE_MESSAGE(!ec, -// std::to_string(client.get_client_id()).append(ec.message())); -// auto ret = syncAwait(client.call()); -// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); -// } - -// TEST_CASE("testing ssl coro rpc subserver with rdma") { -// ELOGV(INFO, "run testing ssl coro rpc subserver with rdma"); -// std::string http_body = R"( -// -// -// -// Example -// -// -//

This is an example of a simple HTML page with one paragraph.

-// -// )"; -// coro_rpc_server server(2, 8810); -// server.init_ssl( -// ssl_configure{"../openssl_files", "server.crt", "server.key"}); -// server.init_ibv(); -// server.register_handler(); -// std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, -// std::string_view magic_number, -// coro_http::coro_http_server &server) { -// CHECK(magic_number == "G"); -// server.transfer_connection(std::move(soc), magic_number); -// }; -// auto http_server = std::make_unique(0, 0); -// http_server->set_http_handler( -// "/index.html", -// [&](coro_http::coro_http_request &, coro_http::coro_http_response -// &resp) { -// resp.set_status_and_content(coro_http::status_type::ok, http_body); -// }); -// server.add_subserver(std::move(dispatcher), std::move(http_server)); -// auto res = server.async_start(); -// CHECK_MESSAGE(!res.hasResult(), "server start failed"); -// coro_rpc_client client(coro_io::get_global_executor()); -// bool is_ok = client.init_ibv(); -// CHECK(client.init_ssl("../openssl_files", "server.crt")); -// REQUIRE_MESSAGE(is_ok,"init ibv failed"); -// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); -// REQUIRE_MESSAGE(!ec, -// std::to_string(client.get_client_id()).append(ec.message())); -// auto ret = syncAwait(client.call()); -// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); -// coro_http::coro_http_client cli; -// auto result = syncAwait(cli.connect("localhost:8810")); -// CHECK_MESSAGE(!result.net_err, result.net_err.message()); -// result = syncAwait(cli.async_get("/index.html")); -// CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, -// result.status); -// CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -// } -#endif -#endif +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include +#include + +#include "ServerTester.hpp" +#include "async_simple/coro/Lazy.h" +#include "cinatra/response_cv.hpp" +#include "doctest.h" +#include "rpc_api.hpp" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" +#include "ylt/coro_io/coro_io.hpp" +#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" +#include "ylt/coro_rpc/impl/errno.h" +#include "ylt/struct_pack.hpp" + +async_simple::coro::Lazy get_coro_value(int val) { co_return val; } + +struct CoroServerTester : ServerTester { + CoroServerTester(TesterConfig config) + : ServerTester(config), + server(2, config.port, config.address, config.conn_timeout_duration) { +#ifdef YLT_ENABLE_SSL + if (use_ssl) { + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); + } +#endif +#ifdef YLT_ENABLE_IBV + if (use_rdma) { + server.init_ibv(); + } +#endif + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + } + ~CoroServerTester() { server.stop(); } + + async_simple::coro::Lazy get_value(int val) { co_return val; } + + void test_set_server_address() { + { + coro_rpc_server server(1, 9001); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "0.0.0.0"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "127.0.0.1"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "localhost"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, "0.0.0.0:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, "127.0.0.1:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, "localhost:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "x.x.x.x"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(server.get_errc() == coro_rpc::errc::bad_address); + } + + { + coro_rpc_server server(1, "x.x.x.x:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(server.get_errc() == coro_rpc::errc::bad_address); + } + + { + coro_rpc_server server(1, "127.0.0.1:aaa"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(server.get_errc() == coro_rpc::errc::bad_address); + } + } + + void test_all() override { + g_action = {}; + ELOGV(INFO, "run %s", __func__); + test_set_server_address(); + test_coro_handler(); + ServerTester::test_all(); + test_function_not_registered(); + test_start_new_server_with_same_port(); + test_server_send_bad_rpc_result(); + test_server_send_no_body(); + test_context_func(); + test_return_err_by_throw_exception(); + this->test_call_with_delay_func(); + this->test_call_with_delay_func< + coro_fun_with_user_define_connection_type>(); + this->test_call_with_delay_func(); + this->test_call_with_delay_func_client_read_length_error< + coro_fun_with_delay_return_void>(); + this->test_call_with_delay_func_client_read_body_error< + coro_fun_with_delay_return_void>(); + if (enable_heartbeat) { + this->test_call_with_delay_func_server_timeout< + coro_fun_with_delay_return_void_cost_long_time>(); + } + this->test_call_with_delay_func(); + this->test_call_with_delay_func(); + } + void register_all_function() override { + g_action = {}; + ELOGV(INFO, "run %s", __func__); + server.register_handler(); + server.register_handler(); + server.register_handler<&ns_login::LoginService::login>(&login_service_); + server.register_handler<&HelloService::hello>(&hello_service_); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler<&HelloService::coro_func, + &HelloService::coro_func_return_void>( + &hello_service_); + server.register_handler(); + server.register_handler<&CoroServerTester::get_value>(this); + } + + void test_context_func() { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + client->set_req_attachment("1234567890987654321234567890"); + auto result = syncAwait(client->call()); + CHECK(result); + CHECK(client->get_resp_attachment() == "1234567890987654321234567890"); + + client->set_req_attachment("12345678909876543212345678901"); + result = syncAwait(client->call()); + CHECK(result); + CHECK(client->get_resp_attachment() == "12345678909876543212345678901"); + + client->set_req_attachment("01234567890987654321234567890"); + result = syncAwait(client->call()); + CHECK(result); + CHECK(client->get_resp_attachment() == "01234567890987654321234567890"); + } + void test_return_err_by_throw_exception() { + { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto result = syncAwait(client->call()); + REQUIRE(!result); + CHECK(result.error().code == coro_rpc::errc::address_in_used); + CHECK(result.error().msg == "error with user-defined msg"); + } + { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto result = syncAwait(client->call()); + REQUIRE(!result); + CHECK(result.error().code == coro_rpc::errc::address_in_used); + CHECK(result.error().msg == "error with user-defined msg"); + } + } + + void test_function_not_registered() { + g_action = {}; + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::function_not_registered, + std::to_string(client->get_client_id()).append(ret.error().msg)); + REQUIRE(client->has_closed() == true); + ret = call(client); + CHECK(client->has_closed() == true); + ret = call(client); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + CHECK(client->has_closed() == true); + } + + void test_server_start_again() { + ELOGV(INFO, "run %s", __func__); + + auto ec = server.start(); + REQUIRE_MESSAGE(ec == coro_rpc::errc::io_error, ec.message()); + } + + void test_start_new_server_with_same_port() { + ELOGV(INFO, "run %s", __func__); + { + auto new_server = coro_rpc_server(2, std::stoi(this->port_)); + auto ec = new_server.async_start(); + REQUIRE(ec.hasResult()); + REQUIRE_MESSAGE(ec.value() == coro_rpc::errc::address_in_used, + ec.value().message()); + } + ELOGV(INFO, "OH NO"); + } + void test_server_send_bad_rpc_result() { + auto client = create_client(inject_action::server_send_bad_rpc_result); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = this->call(client); + CHECK_MESSAGE( + ret.error().code == coro_rpc::errc::invalid_rpc_result, + std::to_string(client->get_client_id()).append(ret.error().msg)); + g_action = {}; + } + + void test_server_send_no_body() { + auto client = create_client(inject_action::close_socket_after_send_length); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = this->template call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + g_action = {}; + } + + void test_coro_handler() { + auto client = create_client(inject_action::nothing); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = this->template call(client, 42); + CHECK(ret.value() == 42); + + auto ret1 = this->template call<&HelloService::coro_func>(client, 42); + CHECK(ret1.value() == 42); + + auto ret2 = this->template call<&CoroServerTester::get_value>(client, 42); + CHECK(ret2.value() == 42); + + auto ret3 = this->template call(client, 42); + CHECK(ret3.value() == 42); + + auto ret4 = this->template call(client, 42); + CHECK(ret4.has_value()); + + auto ret5 = + this->template call<&HelloService::coro_func_return_void>(client, 42); + CHECK(ret5.has_value()); + } + coro_rpc_server server; + std::thread thd; + HelloService hello_service_; +}; +TEST_CASE("testing coro rpc server") { + ELOGV(INFO, "run testing coro rpc server"); + unsigned short server_port = 8810; + auto conn_timeout_duration = 500ms; + std::vector switch_list{0 +#ifdef YLT_ENABLE_SSL + , + 1 +#endif +#ifdef YLT_ENABLE_IBV + , + 2 +#endif + }; + for (auto enable_heartbeat : switch_list) { + for (auto type : switch_list) { + TesterConfig config; + config.enable_heartbeat = enable_heartbeat; + if (type == 0) { + config.use_ssl = false; + config.use_rdma = false; + } + else if (type == 1) { + config.use_ssl = true; + config.use_rdma = false; + } + else if (type == 2) { + config.use_ssl = false; + config.use_rdma = true; + } + config.sync_client = false; + config.use_outer_io_context = false; + config.port = server_port; + if (enable_heartbeat) { + config.conn_timeout_duration = conn_timeout_duration; + } + std::stringstream ss; + ss << config; + ELOGV(INFO, "config: %s", ss.str().data()); + CoroServerTester(config).run(); + } + // } + } +} + +TEST_CASE("testing coro rpc server stop") { + ELOGV(INFO, "run testing coro rpc server stop"); + coro_rpc_server server(2, 8810); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + SUBCASE("stop twice") { + server.stop(); + server.stop(); + } + SUBCASE("stop in different thread") { + std::thread thd1([&server]() { + server.stop(); + }); + std::thread thd2([&server]() { + server.stop(); + }); + thd1.join(); + thd2.join(); + } +} + +TEST_CASE("test server accept error") { + ELOGV(INFO, "run test server accept error"); + g_action = inject_action::force_inject_server_accept_error; + coro_rpc_server server(2, 8810); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client(coro_io::get_global_executor()); + ELOGV(INFO, "run test server accept error, client_id %d", + client.get_client_id()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + REQUIRE(client.has_closed() == true); + g_action = {}; +} + +TEST_CASE("test server write queue") { + using coro_rpc_protocol = coro_rpc::protocol::coro_rpc_protocol; + ELOGV(INFO, "run server write queue"); + g_action = {}; + coro_rpc_server server(2, 8810); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + std::string buffer; + buffer.reserve(coro_rpc_protocol::REQ_HEAD_LEN + + struct_pack::get_needed_size(std::monostate{})); + buffer.resize(coro_rpc_protocol::REQ_HEAD_LEN); + auto &header = *(coro_rpc_protocol::req_header *)buffer.data(); + header.magic = coro_rpc_protocol::magic_number; + header.function_id = + func_id(); + header.seq_num = g_client_id++; + header.length = struct_pack::get_needed_size(std::monostate{}); + ELOGV(INFO, "client_id %d begin to connect %d", header.seq_num, 8820); + struct_pack::serialize_to(buffer, std::monostate{}); + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + asio::ip::tcp::socket socket(io_context); + auto ret = coro_io::connect(io_context, socket, "127.0.0.1", "8810"); + CHECK(!ret); + ELOGV(INFO, "%s client_id %d call %s", "sync_client", header.seq_num, + "coro_fun_with_delay_return_void_cost_long_time"); + for (int i = 0; i < 1; ++i) { + auto err = + coro_io::write(socket, asio::buffer(buffer.data(), buffer.size())); + CHECK(err.second == buffer.size()); + } + for (int i = 0; i < 1; ++i) { + char buffer2[coro_rpc_protocol::RESP_HEAD_LEN]; + std::monostate r; + auto buf = struct_pack::serialize(r); + std::string buffer_read; + buffer_read.resize(buf.size()); + read(socket, asio::buffer(buffer2, coro_rpc_protocol::RESP_HEAD_LEN)); + [[maybe_unused]] auto resp_head = + *(coro_rpc_protocol::resp_header *)buffer2; + uint32_t body_len = header.length; + CHECK(body_len == buf.size()); + read(socket, asio::buffer(buffer_read, body_len)); + std::monostate r2; + std::size_t sz; + auto ret = + struct_pack::deserialize_to(r2, buffer_read.data(), body_len, sz); + CHECK(!ret); + CHECK(sz == body_len); + CHECK(r2 == r); + } + + ELOGV(INFO, "client_id %d close", header.seq_num); + asio::error_code ignored_ec; + socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + socket.close(ignored_ec); + io_context.stop(); + thd.join(); + server.stop(); +} + +TEST_CASE("testing coro rpc write error") { + ELOGV(INFO, "run testing coro rpc write error"); + g_action = inject_action::force_inject_connection_close_socket; + coro_rpc_server server(2, 8810); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + ELOGV(INFO, "run testing coro rpc write error, client_id %d", + client.get_client_id()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client.get_client_id()).append(ret.error().msg)); + REQUIRE(client.has_closed() == true); + g_action = inject_action::nothing; +} + +TEST_CASE("testing ipv6") { + using namespace coro_http; + coro_rpc_server server(1, "[::1]:8811"); + server.register_handler(); + auto res = server.async_start(); + if (res.hasResult()) { + return; + } + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client{}; + auto ec = syncAwait(client.connect("[::1]:8811")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + CHECK(ret); + + coro_rpc_client client1{}; + ec = syncAwait(client1.connect("::1:8811")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + ret = syncAwait(client1.call()); + CHECK(ret); + + coro_http::coro_http_server http_server(1, 8812, "::1"); + http_server.set_http_handler( + "/test", [](coro_http_request &req, coro_http_response &resp) { + resp.set_status_and_content(status_type::ok, "ok"); + }); + http_server.async_start(); + coro_http::coro_http_client htttp_client{}; + auto r = syncAwait(htttp_client.connect("http://[::1]:8812")); + CHECK(!r.net_err); + auto result = htttp_client.get("/test"); + CHECK(result.status == 200); + + coro_http::coro_http_client htttp_client1{}; + result = htttp_client1.get("http://[::1]:8812/test"); + CHECK(result.status == 200); +} + +TEST_CASE("testing coro rpc subserver") { + ELOGV(INFO, "run testing coro rpc subserver"); + std::string http_body = R"( + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ +)"; + coro_rpc_server server(2, 8810); + server.register_handler(); + std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, + std::string_view magic_number, + coro_http::coro_http_server &server) { + CHECK(magic_number == "G"); + server.transfer_connection(std::move(soc), magic_number); + }; + auto http_server = std::make_unique(0, 0); + http_server->set_http_handler( + "/index.html", + [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { + resp.set_status_and_content(coro_http::status_type::ok, http_body); + }); + server.add_subserver(std::move(dispatcher), std::move(http_server)); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); + coro_http::coro_http_client cli; + auto result = syncAwait(cli.connect("localhost:8810")); + CHECK_MESSAGE(!result.net_err, result.net_err.message()); + result = syncAwait(cli.async_get("/index.html")); + CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, + result.status); + CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +} +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing coro rpc ssl subserver") { + ELOGV(INFO, "run testing coro rpc ssl subserver"); + std::string http_body = R"( + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ +)"; + coro_rpc_server server(2, 8810); + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); + server.register_handler(); + std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, + std::string_view magic_number, + coro_http::coro_http_server &server) { + CHECK(magic_number == "G"); + server.transfer_connection(std::move(soc), magic_number); + }; + auto http_server = std::make_unique(0, 0); + http_server->set_http_handler( + "/index.html", + [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { + resp.set_status_and_content(coro_http::status_type::ok, http_body); + }); + server.add_subserver(std::move(dispatcher), std::move(http_server)); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + CHECK(client.init_ssl("../openssl_files", "ca.crt", "127.0.0.1")); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); + coro_http::coro_http_client cli; + CHECK_MESSAGE(cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"), + "init ssl failed"); + auto result = syncAwait(cli.connect("https://localhost:8810")); + CHECK_MESSAGE(!result.net_err, result.net_err.message()); + result = syncAwait(cli.async_get("/index.html")); + CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, + result.net_err.message()); + CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +} +#ifdef YLT_ENABLE_IBV +TEST_CASE("testing coro rpc non-rdma client to rdma server") { + ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); + coro_rpc_server server(2, 8810); + server.init_ibv(); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); +} +TEST_CASE("testing coro rpc subserver with rdma") { + ELOGV(INFO, "run testing coro rpc subserver with rdma"); + std::string http_body = R"( + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ +)"; + coro_rpc_server server(2, 8810); + server.init_ibv(); + server.register_handler(); + std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, + std::string_view magic_number, + coro_http::coro_http_server &server) { + CHECK(magic_number == "G"); + server.transfer_connection(std::move(soc), magic_number); + }; + auto http_server = std::make_unique(0, 0); + http_server->set_http_handler( + "/index.html", + [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { + resp.set_status_and_content(coro_http::status_type::ok, http_body); + }); + server.add_subserver(std::move(dispatcher), std::move(http_server)); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + bool is_ok = client.init_ibv(); + REQUIRE_MESSAGE(is_ok, "init ibv failed"); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); + coro_http::coro_http_client cli; + auto result = syncAwait(cli.connect("localhost:8810")); + CHECK_MESSAGE(!result.net_err, result.net_err.message()); + result = syncAwait(cli.async_get("/index.html")); + CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, + result.status); + CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +} +#ifdef YLT_ENABLE_SSL + +// TODO: open it when whe support rdma client with ssl-encrypt connection + +// TEST_CASE("testing coro rpc non-rdma ssl client to ssl rdma server") { +// ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); +// coro_rpc_server server(2, 8810); +// server.init_ibv(); +// server.init_ssl( +// ssl_configure{"../openssl_files", "server.crt", "server.key"}); +// server.register_handler(); +// auto res = server.async_start(); +// CHECK_MESSAGE(!res.hasResult(), "server start failed"); +// coro_rpc_client client(coro_io::get_global_executor()); +// CHECK(client.init_ssl("../openssl_files", "server.crt")); +// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); +// REQUIRE_MESSAGE(!ec, +// std::to_string(client.get_client_id()).append(ec.message())); +// auto ret = syncAwait(client.call()); +// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); +// } + +// TEST_CASE("testing ssl coro rpc subserver with rdma") { +// ELOGV(INFO, "run testing ssl coro rpc subserver with rdma"); +// std::string http_body = R"( +// +// +// +// Example +// +// +//

This is an example of a simple HTML page with one paragraph.

+// +// )"; +// coro_rpc_server server(2, 8810); +// server.init_ssl( +// ssl_configure{"../openssl_files", "server.crt", "server.key"}); +// server.init_ibv(); +// server.register_handler(); +// std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, +// std::string_view magic_number, +// coro_http::coro_http_server &server) { +// CHECK(magic_number == "G"); +// server.transfer_connection(std::move(soc), magic_number); +// }; +// auto http_server = std::make_unique(0, 0); +// http_server->set_http_handler( +// "/index.html", +// [&](coro_http::coro_http_request &, coro_http::coro_http_response +// &resp) { +// resp.set_status_and_content(coro_http::status_type::ok, http_body); +// }); +// server.add_subserver(std::move(dispatcher), std::move(http_server)); +// auto res = server.async_start(); +// CHECK_MESSAGE(!res.hasResult(), "server start failed"); +// coro_rpc_client client(coro_io::get_global_executor()); +// bool is_ok = client.init_ibv(); +// CHECK(client.init_ssl("../openssl_files", "server.crt")); +// REQUIRE_MESSAGE(is_ok,"init ibv failed"); +// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); +// REQUIRE_MESSAGE(!ec, +// std::to_string(client.get_client_id()).append(ec.message())); +// auto ret = syncAwait(client.call()); +// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); +// coro_http::coro_http_client cli; +// auto result = syncAwait(cli.connect("localhost:8810")); +// CHECK_MESSAGE(!result.net_err, result.net_err.message()); +// result = syncAwait(cli.async_get("/index.html")); +// CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, +// result.status); +// CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +// } +#endif +#endif #endif \ No newline at end of file diff --git a/website/docs/en/coro_rpc/coro_rpc_client.md b/website/docs/en/coro_rpc/coro_rpc_client.md index dacb34eab..f330de1d0 100644 --- a/website/docs/en/coro_rpc/coro_rpc_client.md +++ b/website/docs/en/coro_rpc/coro_rpc_client.md @@ -1,458 +1,458 @@ -# Introduction to coro_rpc Client - -## Base Usage - -class `coro_rpc::coro_rpc_client` is the client side of coro_rpc, allowing users to send RPC requests to the server. - -Below, we will demonstrate the basic usage of rpc_client. - -```cpp -using namespace async_simple; -using namespace coro_rpc; -int add(int a,int b); -Lazy example() { - coro_rpc_client client; - coro_rpc::err_code ec = co_await client.connect("localhost:9001"); - if (ec) { /*check if connection error*/ - std::cout<(1,2); - /*rpc_result is type of expected, which T is rpc return type*/ - if (!result.has_value()) { - /*call result.error() to get rpc error message*/ - std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); -} -Lazy example(coro_rpc_client& client, std::string_view attachment) { - client.set_req_attachment(attachment); - rpc_result result = co_await client.call(); - if (result.has_value()) { - assert(result.get_resp_attachment()==attachment); - co_return std::move(result.release_resp_attachment()); - } - co_return ""; -} -``` - -By default, the RPC client will wait for 5 seconds after sending a request/establishing a connection. If no response is received after 5 seconds, it will return a timeout error. Users can also customize the wait duration by calling the `call_for` function. - -```cpp -client.connect("127.0.0.1:9001", std::chrono::seconds{10}); -auto result = co_await client.call_for(std::chrono::seconds{10},1,2); -assert(result.value() == 3); -``` - -The duration can be any `std::chrono::duration` type, common examples include `std::chrono::seconds` and `std::chrono::milliseconds`. Notably, if the duration is set to zero, it indicates that the function call will never time out. - -## SSL support - -coro_rpc supports using OpenSSL to encrypt connections. After installing OpenSSL and importing yalantinglibs into your project with CMake's `find_package` or `fetch_content`, you can enable SSL support by setting the CMake option YLT_ENABLE_SSL=ON. Alternatively, you might manually add the YLT_ENABLE_SSL macro and manually link to OpenSSL. - -Once SSL support has been enabled, users can invoke the `init_ssl` function before establishing a connection to the server. This will create an encrypted link between the client and the server. Its important to note that the coro_rpc server must also be compiled with SSL support enabled, and the `init_ssl` method must be called to enable SSL support before starting the server. - -For one -way SSL authentication(server authentication only),you can use the `init_ssl` function before establishing a connection:The first parameter is the base path where the SSL certificates are located, and the second parameter is the CA certificate filename for verifying the server. - -Server-side configuration: - -```cpp -coro_rpc_server server(2, 9000); -ssl_configure ssl_conf; -ssl_conf.base_path = "./certs"; -ssl_conf.cert_file = "server.crt"; -ssl_conf.key_file = "server.key"; -ssl_conf.ca_cert_file = ""; // Empty for one-way authentication -ssl_conf.enable_client_verify = false; // Disable client certificate verification - -server.init_ssl(ssl_conf); -server.register_handler(); -server.start(); -``` - -Mutual SSL Authentication For mutual SSL authentication(both client and server authentication),the client needs to provide its own certificate : - -```cpp - // Client-side mutual authentication - client.init_ssl("./", // Base path - "ca.crt", // CA certificate for server verification - "client.crt", // Client certificate - "client.key", // Client private key - "127.0.0.1" // Server hostname for SNI - ); -``` - -Server-side configuration for mutual authentication: - -```cpp -coro_rpc_server server(2, 9000); -ssl_configure ssl_conf; -ssl_conf.base_path = "./certs"; -ssl_conf.cert_file = "server.crt"; -ssl_conf.key_file = "server.key"; -ssl_conf.ca_cert_file = "ca.crt"; // CA certificate for verifying clients -ssl_conf.enable_client_verify = true; // Enable mandatory client certificate verification - -server.init_ssl(ssl_conf); -server.register_handler(); -server.start(); -``` - -**Important Notes**: -- The `enable_client_verify` flag enables mandatory client certificate verification -- When mutual authentication is enabled, clients must provide a valid certificate signed by the CA specified in `ca_cert_file` -- The hostname parameter must match the Common Name (CN) in the server certificate - -We also support NTLS if you enable it by CMAKE OPTION `YLT_ENABLE_NTLS`. - -## Conversion and compile-time checking of RPC parameters - -coro_rpc will perform compile-time checks on the validity of arguments during invocation. For example, for the following rpc function: - -```cpp -inline std::string echo(std::string str) { return str; } -``` - -Next, when the current client invokes the rpc function: - -```cpp -client.call(42); // Parameter does not match, compilation error -client.call(); // Missing parameter, compilation error -client.call("", 0); // Extra parameters, compilation error -client.call("hello, coro_rpc"); // The string literal can be converted to std::string, compilation succeeds -``` - -## Connection Options - -The `coro_rpc_client` provides an `init_config` function to configure connection options. The code below lists all configurable options, which are optional with default values. - -```cpp -using namespace coro_rpc; -using namespace std::chrono; -void set_config(coro_rpc_client& client) { - uint64_t client_id; - std::chrono::milliseconds connect_timeout_duration; - std::chrono::milliseconds request_timeout_duration; - std::string host; - std::string port; - std::string local_ip; - client.init_config(config{ - .connect_timeout_duration = 5s, // Connection timeout duration - .request_timeout_duration = 5s, // Request timeout duration - .host = "localhost", // Server hostname - .port = "9001", // Server port - .local_ip = "", // Local IP address used to specify the local communication interface - .socket_config=std::variant{tcp_config{}}; // Specify transport protocol and its configuration. Supported protocols: TCP, SSL over TCP, RDMA - }); -} -``` - -### RDMA Socket Configuration - -The configuration for IBVerbs socket protocol is shown below: - -```cpp -struct ib_socket_t::config_t { - uint32_t cq_size = 128; // Maximum length of event notification queue - uint32_t recv_buffer_cnt = 8; // Number of buffers pre-submitted to receive queue. Each buffer defaults to 256KB, so a RDMA connection occupies 8MB memory immediately after establishment. More pending receive data will result in more buffers in the queue, up to max_recv_wr*buffer_size (where buffer_size is configured in buffer_pool). If upper layer doesn't consume data, sender will receive RNR (Receiver Not Ready) errors and retry continuously. - uint32_t send_buffer_cnt = 4; // default send buffer queue max size, - ibv_qp_type qp_type = IBV_QPT_RC; // Default QP type - ibv_qp_cap cap = {.max_send_wr = 32, // Maximum send queue length - .max_recv_wr = 32, // Maximum receive queue length - .max_send_sge = 3, // Maximum send scatter/gather elements. Only 1 needed without inline data. Use 3 segments when using inline data (default supports 3 scattered addresses) - .max_recv_sge = 1, // Maximum receive scatter/gather elements. 1 suffices with current buffer configuration - .max_inline_data = 256}; // If packet size < inline data threshold and NIC supports it, small packets bypass buffer copy and go directly to NIC - std::shared_ptr device; // Underlying IB device for RPC. Defaults to first device in list -}; -``` - -Simple RDMA activation examples: - -```cpp - coro_rpc_client cli; - cli.init_ibv(); // Use default configuration - cli.init_ibv({.recv_buffer_cnt=8}); // Use custom configuration -``` - -RDMA activation through config: - -```cpp - coro_rpc_client cli; - cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) -``` - -#### RDMA Device Configuration - -The `ib_device_t` manages the connection context and buffers required during the ibverbs transmission process. By default, it uses the global device `coro_io::get_global_ib_device()`, but users can also specify their own device. - -By modifying the configuration of `ib_device_t`, users can assign different network interfaces to RPC connections and use separate buffers. - -1. Modify the default device configuration -```cpp - // The configuration only takes effect on the first invocation - coro_io::get_global_ib_device({ - .buffer_pool_config = { - .buffer_size = 256 * 1024, // Buffer size - .max_memory_usage = 4 * 1024 * 1024, // Max memory usage (allocation fails beyond this limit) - .memory_usage_recorder = nullptr; // nullopt means that memory usage across different devices will be counted together. If you want the memory pool to have independent memory usage tracking, you should assign a non-null std::shared_ptr> as the recorder. - .idle_timeout = 5s // Buffers unused for this duration will be reclaimed - } - }); - // ... -``` - - 2. Specify the RDMA NIC to use when initializing the connection -```cpp - coro_rpc_client cli; - cli.init_ibv({ - .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); - }); -``` - - 3. Create and use your own `ib_device_t` -```cpp - auto dev = coro_io::ib_device_t::create({ - .dev_name = "", // If dev_name is empty, the first device in the device list will be used. - .port = 1, // Manually specify the NIC port number. - .use_best_gid_index = true, // Automatically find the best GID index for this device. - .gid_index = 0, // Manually specify the GID index; takes effect when automatic lookup is disabled or fails. - .buffer_pool_config = { - // ... - } - }); -``` - - 4. Query all currently successfully registered RDMA global devices -```cpp - // Get all devices - auto devices = coro_io::g_ib_device_manager(); - for (auto &dev: devices.get_dev_list()) { - std::cout << "name:" << dev.first; - // dev.second is a global std::shared_ptr - } -``` - -## Calling Model - -Each `coro_rpc_client` is bound to a specific IO thread. By default, it selects a connection via round-robin from the global IO thread pool. Users can also manually bind it to a specific IO thread. - -```cpp -auto executor=coro_io::get_global_executor(); -coro_rpc_client client(executor),client2(executor); - // Both clients are bound to the same IO thread. -``` - -Each time a coroutine-based IO task is initiated (such as `connect`, `call`, `send_request`), the client internally submits the IO event to the operating system. When the IO event is completed, the coroutine is then resumed on the bound IO thread to continue execution. For example, in the following code, the task switches to the IO thread for execution after calling connect. - -```cpp -/*run in thread 1*/ -coro_rpc_client cli; -co_await cli.connect("localhost:9001"); -/*run in thread 2*/ -do_something(); -``` - -## Connection Pool and Load Balancing - -`coro_io` offers a connection pool `client_pool` and a load balancer `channel`. Users can manage `coro_rpc`/`coro_http` connections through the `client_pool`, and can use `channel` to achieve load balancing among multiple hosts. For more details, please refer to the documentation of `coro_io`. - -## Connection Reuse - -The `coro_rpc_client` can achieve connection reuse through the `send_request` function. This function is thread-safe, allowing multiple threads to call the `send_request` method on the same client concurrently. The return value of the function is `Lazy>>`. The first `co_await` waits for the request to be sent, and the second `co_await` waits for the rpc result to return. - - -Connection reuse allows us to reduce the number of connections under high concurrency, eliminating the need to create new connections. It also improves the throughput of each connection. - -Here's a simple example code snippet: - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -std::string_view echo(std::string_view); -Lazy example(coro_rpc_client& client) { - // send request to the server - Lazy> handler = co_await client.send_request("Hello"); - // then wait server response - async_rpc_result result = co_await std::move(handler); - if (result) { - assert(result->result() == "Hello"); - } - else { - // error handle - std::cout< example(coro_rpc_client& client) { - std::vector>> handlers; - // First, send 10 requests consecutively - for (int i=0;i<10;++i) { - handlers.push_back(co_await client.send_request(std::to_string(i))); - } - // Next, wait for all the requests to return - std::vector> results = co_await collectAll(std::move(handlers)); - for (int i=0;i<10;++i) { - assert(results[i]->result() == std::to_string(i)); - } - co_return; -} -``` - -When sending data using the connection pool, we can also reuse a connection. To do this, you need to add the `coro_io::client_reuse_hint` parameter to instruct the connection pool to enable connection reuse. For more details, see the connection pool documentation. (TODO) - -```cpp -auto pool = coro_io::client_pool::create( - conf.url, pool_conf); -auto ret = co_await pool->send_request( - [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { - return client.send_request("hello"); - }); -if (ret.has_value()) { - auto result = co_await std::move(ret.value()); - if (result.has_value()) { - assert(result.value()=="hello"); - } -} -``` - -### Attachment - -When using the `send_request` method, since multiple requests might be sent simultaneously, we should not call the `set_req_attachment` method to send an attachment to the server, nor should we call the `get_resp_attachment` and `release_resp_attachment` methods to get the attachment returned by the server. - -Instead, we can set the attachment when sending a request by calling the `send_request_with_attachment` function. Additionally, we can retrieve the attachment by calling the `->get_attachment()` and `->release_buffer()` methods of `async_rpc_result`. - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -int add(int a, int b); -Lazy example(coro_rpc_client& client) { - auto handler = co_await client.send_request_with_attachment("Hello", 1, 2); - async_rpc_result result = co_await std::move(handler); - assert(result->result() == 3); - assert(result->get_attachment() == "Hello"); - co_return std::move(result->release_buffer().resp_attachment_buf_); -} -``` - - -### Execution order - -When the called rpc function is a coroutine rpc function or a callback rpc function, the rpc requests may not necessarily be executed in order. The server might execute multiple rpc requests simultaneously. -For example, suppose there is the following code: - -```cpp -using namespace async_simple::coro; -Lazy sleep(int seconds) { - co_await coro_io::sleep(1s * seconds); // Yield the coroutine here - co_return; -} -``` - -Server registration and startup: -```cpp -using namespace coro_rpc; -void start() { - coro_rpc_server server(/* thread = */1,/* port = */ 8801); - server.register_handler(); - server.start(); -} -``` - -The client consecutively calls the sleep function twice on the same connection, sleeping for 2 seconds the first time and 1 second the second time. -```cpp -using namespace async_simple::coro; -using namespace coro_rpc; -Lazy call() { - coro_rpc_client cli,cli2; - co_await cli.connect("localhost:8801"); - co_await cli2.connect("localhost:8801"); - auto handler1 = co_await cli.send_request(2); - auto handler2 = co_await cli.send_request(1); - auto handler3 = co_await cli2.send_request(0); - handler2.start([](auto&&){ - std::cout<<"handler2 return"< example() { + coro_rpc_client client; + coro_rpc::err_code ec = co_await client.connect("localhost:9001"); + if (ec) { /*check if connection error*/ + std::cout<(1,2); + /*rpc_result is type of expected, which T is rpc return type*/ + if (!result.has_value()) { + /*call result.error() to get rpc error message*/ + std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); +} +Lazy example(coro_rpc_client& client, std::string_view attachment) { + client.set_req_attachment(attachment); + rpc_result result = co_await client.call(); + if (result.has_value()) { + assert(result.get_resp_attachment()==attachment); + co_return std::move(result.release_resp_attachment()); + } + co_return ""; +} +``` + +By default, the RPC client will wait for 5 seconds after sending a request/establishing a connection. If no response is received after 5 seconds, it will return a timeout error. Users can also customize the wait duration by calling the `call_for` function. + +```cpp +client.connect("127.0.0.1:9001", std::chrono::seconds{10}); +auto result = co_await client.call_for(std::chrono::seconds{10},1,2); +assert(result.value() == 3); +``` + +The duration can be any `std::chrono::duration` type, common examples include `std::chrono::seconds` and `std::chrono::milliseconds`. Notably, if the duration is set to zero, it indicates that the function call will never time out. + +## SSL support + +coro_rpc supports using OpenSSL to encrypt connections. After installing OpenSSL and importing yalantinglibs into your project with CMake's `find_package` or `fetch_content`, you can enable SSL support by setting the CMake option YLT_ENABLE_SSL=ON. Alternatively, you might manually add the YLT_ENABLE_SSL macro and manually link to OpenSSL. + +Once SSL support has been enabled, users can invoke the `init_ssl` function before establishing a connection to the server. This will create an encrypted link between the client and the server. It’s important to note that the coro_rpc server must also be compiled with SSL support enabled, and the `init_ssl` method must be called to enable SSL support before starting the server. + +For one -way SSL authentication(server authentication only),you can use the `init_ssl` function before establishing a connection:The first parameter is the base path where the SSL certificates are located, and the second parameter is the CA certificate filename for verifying the server. + +Server-side configuration: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = ""; // Empty for one-way authentication +ssl_conf.enable_client_verify = false; // Disable client certificate verification + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +Mutual SSL Authentication For mutual SSL authentication(both client and server authentication),the client needs to provide its own certificate : + +```cpp + // Client-side mutual authentication + client.init_ssl("./", // Base path + "ca.crt", // CA certificate for server verification + "client.crt", // Client certificate + "client.key", // Client private key + "127.0.0.1" // Server hostname for SNI + ); +``` + +Server-side configuration for mutual authentication: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = "ca.crt"; // CA certificate for verifying clients +ssl_conf.enable_client_verify = true; // Enable mandatory client certificate verification + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +**Important Notes**: +- The `enable_client_verify` flag enables mandatory client certificate verification +- When mutual authentication is enabled, clients must provide a valid certificate signed by the CA specified in `ca_cert_file` +- The hostname parameter must match the Common Name (CN) in the server certificate + +We also support NTLS if you enable it by CMAKE OPTION `YLT_ENABLE_NTLS`. + +## Conversion and compile-time checking of RPC parameters + +coro_rpc will perform compile-time checks on the validity of arguments during invocation. For example, for the following rpc function: + +```cpp +inline std::string echo(std::string str) { return str; } +``` + +Next, when the current client invokes the rpc function: + +```cpp +client.call(42); // Parameter does not match, compilation error +client.call(); // Missing parameter, compilation error +client.call("", 0); // Extra parameters, compilation error +client.call("hello, coro_rpc"); // The string literal can be converted to std::string, compilation succeeds +``` + +## Connection Options + +The `coro_rpc_client` provides an `init_config` function to configure connection options. The code below lists all configurable options, which are optional with default values. + +```cpp +using namespace coro_rpc; +using namespace std::chrono; +void set_config(coro_rpc_client& client) { + uint64_t client_id; + std::chrono::milliseconds connect_timeout_duration; + std::chrono::milliseconds request_timeout_duration; + std::string host; + std::string port; + std::string local_ip; + client.init_config(config{ + .connect_timeout_duration = 5s, // Connection timeout duration + .request_timeout_duration = 5s, // Request timeout duration + .host = "localhost", // Server hostname + .port = "9001", // Server port + .local_ip = "", // Local IP address used to specify the local communication interface + .socket_config=std::variant{tcp_config{}}; // Specify transport protocol and its configuration. Supported protocols: TCP, SSL over TCP, RDMA + }); +} +``` + +### RDMA Socket Configuration + +The configuration for IBVerbs socket protocol is shown below: + +```cpp +struct ib_socket_t::config_t { + uint32_t cq_size = 128; // Maximum length of event notification queue + uint32_t recv_buffer_cnt = 8; // Number of buffers pre-submitted to receive queue. Each buffer defaults to 256KB, so a RDMA connection occupies 8MB memory immediately after establishment. More pending receive data will result in more buffers in the queue, up to max_recv_wr*buffer_size (where buffer_size is configured in buffer_pool). If upper layer doesn't consume data, sender will receive RNR (Receiver Not Ready) errors and retry continuously. + uint32_t send_buffer_cnt = 4; // default send buffer queue max size, + ibv_qp_type qp_type = IBV_QPT_RC; // Default QP type + ibv_qp_cap cap = {.max_send_wr = 32, // Maximum send queue length + .max_recv_wr = 32, // Maximum receive queue length + .max_send_sge = 3, // Maximum send scatter/gather elements. Only 1 needed without inline data. Use 3 segments when using inline data (default supports 3 scattered addresses) + .max_recv_sge = 1, // Maximum receive scatter/gather elements. 1 suffices with current buffer configuration + .max_inline_data = 256}; // If packet size < inline data threshold and NIC supports it, small packets bypass buffer copy and go directly to NIC + std::shared_ptr device; // Underlying IB device for RPC. Defaults to first device in list +}; +``` + +Simple RDMA activation examples: + +```cpp + coro_rpc_client cli; + cli.init_ibv(); // Use default configuration + cli.init_ibv({.recv_buffer_cnt=8}); // Use custom configuration +``` + +RDMA activation through config: + +```cpp + coro_rpc_client cli; + cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) +``` + +#### RDMA Device Configuration + +The `ib_device_t` manages the connection context and buffers required during the ibverbs transmission process. By default, it uses the global device `coro_io::get_global_ib_device()`, but users can also specify their own device. + +By modifying the configuration of `ib_device_t`, users can assign different network interfaces to RPC connections and use separate buffers. + +1. Modify the default device configuration +```cpp + // The configuration only takes effect on the first invocation + coro_io::get_global_ib_device({ + .buffer_pool_config = { + .buffer_size = 256 * 1024, // Buffer size + .max_memory_usage = 4 * 1024 * 1024, // Max memory usage (allocation fails beyond this limit) + .memory_usage_recorder = nullptr; // nullopt means that memory usage across different devices will be counted together. If you want the memory pool to have independent memory usage tracking, you should assign a non-null std::shared_ptr> as the recorder. + .idle_timeout = 5s // Buffers unused for this duration will be reclaimed + } + }); + // ... +``` + + 2. Specify the RDMA NIC to use when initializing the connection +```cpp + coro_rpc_client cli; + cli.init_ibv({ + .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); + }); +``` + + 3. Create and use your own `ib_device_t` +```cpp + auto dev = coro_io::ib_device_t::create({ + .dev_name = "", // If dev_name is empty, the first device in the device list will be used. + .port = 1, // Manually specify the NIC port number. + .use_best_gid_index = true, // Automatically find the best GID index for this device. + .gid_index = 0, // Manually specify the GID index; takes effect when automatic lookup is disabled or fails. + .buffer_pool_config = { + // ... + } + }); +``` + + 4. Query all currently successfully registered RDMA global devices +```cpp + // Get all devices + auto devices = coro_io::g_ib_device_manager(); + for (auto &dev: devices.get_dev_list()) { + std::cout << "name:" << dev.first; + // dev.second is a global std::shared_ptr + } +``` + +## Calling Model + +Each `coro_rpc_client` is bound to a specific IO thread. By default, it selects a connection via round-robin from the global IO thread pool. Users can also manually bind it to a specific IO thread. + +```cpp +auto executor=coro_io::get_global_executor(); +coro_rpc_client client(executor),client2(executor); + // Both clients are bound to the same IO thread. +``` + +Each time a coroutine-based IO task is initiated (such as `connect`, `call`, `send_request`), the client internally submits the IO event to the operating system. When the IO event is completed, the coroutine is then resumed on the bound IO thread to continue execution. For example, in the following code, the task switches to the IO thread for execution after calling connect. + +```cpp +/*run in thread 1*/ +coro_rpc_client cli; +co_await cli.connect("localhost:9001"); +/*run in thread 2*/ +do_something(); +``` + +## Connection Pool and Load Balancing + +`coro_io` offers a connection pool `client_pool` and a load balancer `channel`. Users can manage `coro_rpc`/`coro_http` connections through the `client_pool`, and can use `channel` to achieve load balancing among multiple hosts. For more details, please refer to the documentation of `coro_io`. + +## Connection Reuse + +The `coro_rpc_client` can achieve connection reuse through the `send_request` function. This function is thread-safe, allowing multiple threads to call the `send_request` method on the same client concurrently. The return value of the function is `Lazy>>`. The first `co_await` waits for the request to be sent, and the second `co_await` waits for the rpc result to return. + + +Connection reuse allows us to reduce the number of connections under high concurrency, eliminating the need to create new connections. It also improves the throughput of each connection. + +Here's a simple example code snippet: + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +std::string_view echo(std::string_view); +Lazy example(coro_rpc_client& client) { + // send request to the server + Lazy> handler = co_await client.send_request("Hello"); + // then wait server response + async_rpc_result result = co_await std::move(handler); + if (result) { + assert(result->result() == "Hello"); + } + else { + // error handle + std::cout< example(coro_rpc_client& client) { + std::vector>> handlers; + // First, send 10 requests consecutively + for (int i=0;i<10;++i) { + handlers.push_back(co_await client.send_request(std::to_string(i))); + } + // Next, wait for all the requests to return + std::vector> results = co_await collectAll(std::move(handlers)); + for (int i=0;i<10;++i) { + assert(results[i]->result() == std::to_string(i)); + } + co_return; +} +``` + +When sending data using the connection pool, we can also reuse a connection. To do this, you need to add the `coro_io::client_reuse_hint` parameter to instruct the connection pool to enable connection reuse. For more details, see the connection pool documentation. (TODO) + +```cpp +auto pool = coro_io::client_pool::create( + conf.url, pool_conf); +auto ret = co_await pool->send_request( + [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { + return client.send_request("hello"); + }); +if (ret.has_value()) { + auto result = co_await std::move(ret.value()); + if (result.has_value()) { + assert(result.value()=="hello"); + } +} +``` + +### Attachment + +When using the `send_request` method, since multiple requests might be sent simultaneously, we should not call the `set_req_attachment` method to send an attachment to the server, nor should we call the `get_resp_attachment` and `release_resp_attachment` methods to get the attachment returned by the server. + +Instead, we can set the attachment when sending a request by calling the `send_request_with_attachment` function. Additionally, we can retrieve the attachment by calling the `->get_attachment()` and `->release_buffer()` methods of `async_rpc_result`. + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +int add(int a, int b); +Lazy example(coro_rpc_client& client) { + auto handler = co_await client.send_request_with_attachment("Hello", 1, 2); + async_rpc_result result = co_await std::move(handler); + assert(result->result() == 3); + assert(result->get_attachment() == "Hello"); + co_return std::move(result->release_buffer().resp_attachment_buf_); +} +``` + + +### Execution order + +When the called rpc function is a coroutine rpc function or a callback rpc function, the rpc requests may not necessarily be executed in order. The server might execute multiple rpc requests simultaneously. +For example, suppose there is the following code: + +```cpp +using namespace async_simple::coro; +Lazy sleep(int seconds) { + co_await coro_io::sleep(1s * seconds); // Yield the coroutine here + co_return; +} +``` + +Server registration and startup: +```cpp +using namespace coro_rpc; +void start() { + coro_rpc_server server(/* thread = */1,/* port = */ 8801); + server.register_handler(); + server.start(); +} +``` + +The client consecutively calls the sleep function twice on the same connection, sleeping for 2 seconds the first time and 1 second the second time. +```cpp +using namespace async_simple::coro; +using namespace coro_rpc; +Lazy call() { + coro_rpc_client cli,cli2; + co_await cli.connect("localhost:8801"); + co_await cli2.connect("localhost:8801"); + auto handler1 = co_await cli.send_request(2); + auto handler2 = co_await cli.send_request(1); + auto handler3 = co_await cli2.send_request(0); + handler2.start([](auto&&){ + std::cout<<"handler2 return"< example() { - coro_rpc_client client; - coro_rpc::err_code ec = co_await client.connect("localhost:9001"); - if (ec) { /*判断连接是否出错*/ - std::cout<(1,2); - /*rpc_result是一个expected类型,其中T为rpc返回值*/ - if (!result.has_value()) { - /*调用result.error()获取rpc错误信息*/ - std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); -} -Lazy example(coro_rpc_client& client, std::string_view attachment) { - client.set_req_attachment(attachment); - rpc_result result = co_await client.call(); - if (result.has_value()) { - assert(result.get_resp_attachment()==attachment); - co_return std::move(result.release_resp_attachment()); - } - co_return ""; -} -``` - -默认情况下,rpc客户端发送请求/建立连接后会等待5秒,如果5秒后仍未收到响应,则会返回超时错误。 -用户也可以通过调用`call_for`函数自定义等待的时长。 - -```cpp -client.connect("127.0.0.1:9001", std::chrono::seconds{10}); -auto result = co_await client.call_for(std::chrono::seconds{10},1,2); -assert(result.value() == 3); -``` - -时长可以是任一的`std::chrono::duration`类型,常见的如`std::chrono::seconds`,`std::chrono::millseconds`。 -特别的,如果时长为0,代表该函数调用永远也不会超时。 - -## SSL支持 - -coro_rpc支持使用openssl对连接进行加密。在安装openssl并使用cmake find_package/fetch_content 将yalantinglibs导入到你的工程后,可以打开cmake选项`YLT_ENABLE_SSL=ON`启用ssl支持。或者,你也可以手动添加宏`YLT_ENABLE_SSL`并手动链接openssl。 - -当启用ssl支持后,用户可以调用`init_ssl`函数,然后再连接到服务器。这会使得客户端与服务器之间建立加密的链接。需要注意的是,coro_rpc服务端在编译时也必须启用ssl支持,并且在启动服务器之前也需要调用`init_ssl`方法来启用SSL支持。 - -```cpp -client.init_ssl("./","server.crt"); -``` - -第一个字符串代表SSL证书所在的基本路径,第二个字符串代表SSL证书相对于基本路径的相对路径。 - -当建立连接时,客户端会使用该证书校验服务端发来的证书,以避免中间人攻击。因此,客户端必须持有服务端使用的证书或其根证书。 - -我们同样支持国密NTLS。你需要开启CMAKE选项`YLT_ENABLE_NTLS`。 - -## RPC参数的转换与编译期检查 - -coro_rpc会在调用的时候对参数的合法性做编译期检查,比如,对于如下rpc函数: - -```cpp -inline std::string echo(std::string str) { return str; } -``` - -接下来,当前client调用rpc函数时: - -```cpp -client.call(42);// The argument does not match, a compilation error occurs. -client.call();// Missing argument, compilation error occurs. -client.call("", 0);// There are too many arguments, a compilation error occurs. -client.call("hello, coro_rpc");// The string literal can be converted to std::string, compilation succeeds. -``` - -## 连接选项 - -coro_rpc_client提供了`init_config`函数,用于配置连接选项。下面这份代码列出了可配置的选项。这些选项默认均可以不填写。 - -```cpp -using namespace coro_rpc; -using namespace std::chrono; -void set_config(coro_rpc_client& client) { - uint64_t client_id; - std::chrono::milliseconds connect_timeout_duration; - std::chrono::milliseconds request_timeout_duration; - std::string host; - std::string port; - std::string local_ip; - client.init_config(config{ - .connect_timeout_duration = 5s, // 连接的超时时间 - .request_timeout_duration = 5s, // 请求超时时间 - .host = "localhost", // 服务器域名 - .port = "9001", // 服务器端口 - .local_ip = "", // 本地ip,用于指定本地通信的ip地址。 - .socket_config=std::variant{tcp_config{}}; // 指定底层的协议及其底层配置,目前支持tcp, ssl over tcp, rdma三种协议。 - }); -} -``` - - -### rdma配置 - -ibverbs协议的配置如下: -```cpp -struct ib_socket_t::config_t { - uint32_t cq_size = 128; // 事件通知队列的最大长度 - uint32_t recv_buffer_cnt = 8; // 默认提交到接受队列的缓冲数目,一个缓冲区默认256KB。积压的接收数据越多,队列中的缓冲区也会越多,最多可以缓冲max_recv_wr*buffer_size这么多的数据(buffer_size为buffer_pool配置的缓冲区大小),此后如果上层仍不消费数据,则发送端会收到rnr错误,不断重试并等待对端消费。 - uint32_t send_buffer_cnt = 4; // 默认的发送缓冲区队列长度上限。代表最多积压的发送缓冲区数目。 - ibv_qp_type qp_type = IBV_QPT_RC; // 默认的qp类型。 - ibv_qp_cap cap = {.max_send_wr = 32, // 发送队列的最大长度。 - .max_recv_wr = 32, // 接受队列的最大长度 - .max_send_sge = 3, // 发送的最大地址分段数,在不启用inline data时只需要1个。使用inline data时数据可能不经过拷贝直接从原始分段地址发送,因此设置为3段(默认支持3段分散地址)。 - .max_recv_sge = 1, // 接受的最大地址分段数,目前的缓冲区配置下只需要1个即可。 - .max_inline_data = 256}; // 如果发送的数据包小于inline data,且底层网卡支持该设置,则小数据包不会被拷贝到缓冲中,而是直接交给网卡发送。 - std::shared_ptr device; // rpc使用的底层ib网卡。默认选择设备列表第一个网卡。 -}; -``` - -可以通过下面的代码简单的启用rdma: - -```cpp - coro_rpc_client cli; - cli.init_ibv(); //使用默认配置 - cli.init_ibv(ib_socket_t::config_t{}); //使用用户指定的配置 -``` - -也可以在配置中启用rdma: - -```cpp - coro_rpc_client cli; - cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) -``` - - -#### ib_device_t - -`ib_device_t`管理了ibverbs传输过程中需要使用到的连接上下文和缓冲区。默认使用全局设备`coro_io::get_global_ib_device()`,用户也可以指定使用自己的设备。 - -通过修改ib_device_t的配置,可以给rpc连接配置不同的网卡,使用独立的缓冲区。 - -1. 修改默认的设备配置 -```cpp - // 配置只有在第一次调用时才会生效 - coro_io::get_global_ib_device({ - .buffer_pool_config = { - .buffer_size = 256 * 1024, // 缓冲区大小 - .max_memory_usage = 4 * 1024 * 1024, // 最大内存使用量(超过此限制将分配失败) - .memory_usage_recorder = nullptr; // nullopt 表示不同设备的内存占用会被一起统计,如果想要让内存池具有独立的内存占用记录,请分配一个非空的std::shared_ptr>作为记录 - .idle_timeout = 5s // 空闲时间超过这个时长的缓冲区将被回收 - } - }); - // ... -``` - -2. 初始化连接时,指定需要使用的 RDMA 网卡 -```cpp - coro_rpc_client cli; - cli.init_ibv({ - .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); - }); -``` - -3. 创建并使用自己的 `ib_device_t` -```cpp - auto dev = coro_io::ib_device_t::create({ - .dev_name = "", // 如果 dev_name 为 空,则会使用设备列表中的第一个设备 - .port = 1, // 手动指定网卡port - .use_best_gid_index = true, // 自动查找该设备最佳的gid_index - .gid_index = 0, // 手动指定gid_index,当关闭自动查找或自动查找失败时生效 - .buffer_pool_config = { - // ... - }, - }); - coro_rpc_client cli; - cli.init_ibv({ - .device = dev - }); -``` - -4. 查询当前所有成功注册的全局 RDMA 设备 -```cpp - // 获取所有设备 - auto devices = coro_io::g_ib_device_manager(); - for (auto &dev: devices.get_dev_list()) { - std::cout<<"name:"< - } -``` - -## 调用模型 - -每一个`coro_rpc_client`都会绑定到某个IO线程上,默认通过轮转法从全局IO线程池中选择一个连接,用户也可以手动绑定到特定的IO线程上。 - -```cpp -auto executor=coro_io::get_global_executor(); -coro_rpc_client client(executor),client2(executor); -// 两个客户端都被绑定到同一个io线程上 -``` - -每次发起一个基于协程的IO任务(如`connect`,`call`,`send_request`),客户端内部会将IO事件提交给操作系统,当IO事件完成后,再将协程恢复到绑定的IO线程上继续执行。 - -例如以下代码,调用connect之后任务将切换到IO线程执行。 - -```cpp -/*run in thread 1*/ -coro_rpc_client cli; -co_await cli.connect("localhost:9001"); -/*run in thread 2*/ -do_something(); -``` - -## 连接池与负载均衡 - -`coro_io`提供了连接池`client_pool`与负载均衡器`channel`。用户可以通过连接池`client_pool`来管理`coro_rpc`/`coro_http`连接,可以使用`channel`实现多个host之间的负载均衡。具体请见`coro_io`的文档。 - -## 连接复用 - -`coro_rpc_client` 可以通过 `send_request`函数实现连接复用。该函数是线程安全的,允许多个线程同时调用同一个client的 `send_request`方法。该函数返回值为`Lazy>>`. - - -连接复用允许我们在高并发下减少连接的个数,无需创建新的连接。同时也能提高每个连接的吞吐量。 - -下面是一段简单的示例代码: - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -std::string_view echo(std::string_view); -Lazy example(coro_rpc_client& client) { - //向服务器发送请求 - Lazy> handler = co_await client.send_request("Hello"); - async_rpc_result result = co_await std::move(handler); - if (result) { - assert(result->result() == "Hello"); - } - else { - // error handle - std::cout< example(coro_rpc_client& client) { - std::vector>> handlers; - // 准备发送10个请求 - for (int i=0;i<10;++i) { - handlers.push_back(co_await client.send_request(std::to_string(i))); - } - //接下来等待所有的请求返回 - std::vector> results = co_await collectAll(std::move(handlers)); - for (int i=0;i<10;++i) { - assert(results[i]->result() == std::to_string(i)); - } - co_return; -} -``` - -在使用连接池发送数据时,我们也可以复用连接,此时需要在添加`coro_io_client_reuse_hint`参数,提示连接池启用连接复用。详见连接池文档。(TODO) - -```cpp -auto pool = coro_io::client_pool::create( - conf.url, pool_conf); -auto ret = co_await pool->send_request( - [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { - return client.send_request("hello"); - }); -if (ret.has_value()) { - auto result = co_await std::move(ret.value()); - if (result.has_value()) { - assert(result.value()=="hello"); - } -} -``` - - -### Attachment - -使用`send_request`方法时,由于可能同时发送多个请求,因此我们不能调用`set_req_attachment`方法向服务器发送attachment,同样也不能调用`get_resp_attachment`和`release_resp_attachment`方法来获取服务器返回的attachment。 - -我们可以通过调用`send_request_with_attachment`函数,在发送请求时设置attachment。我们也可以通过调用async_rpc_result的`->get_attachment()`方法和`->release_buffer()`方法来获取attachment。 - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -int add(int a, int b); -Lazy example(coro_rpc_client& client) { - async_rpc_result result = co_await co_await client.send_request_with_attachment("Hello", 1, 2); - assert(result->result() == 3); - assert(result->get_attachment() == "Hello"); - co_return std::move(result->release_buffer().resp_attachment_buf_); -} -``` - - -### 执行顺序 - -当调用的rpc函数是协程rpc函数或回调rpc函数时,rpc请求不一定会按顺序执行,服务端可能会同时执行多个rpc请求。 - -例如,假如有以下代码: - -```cpp -using namespace async_simple::coro; -Lazy sleep(int seconds) { - co_await coro_io::sleep(1s * seconds); // 在此处让出协程 - co_return; -} -``` - -服务器注册并启动: -```cpp -using namespace coro_rpc; -void start() { - coro_rpc_server server(/* thread = */1,/* port = */ 8801); - server.register_handler(); - server.start(); -} -``` - -客户端连续在同一个连接上调用两次sleep函数,第一次sleep2秒,第二次sleep1秒。 -```cpp -using namespace async_simple::coro; -using namespace coro_rpc; -Lazy call() { - coro_rpc_client cli,cli2; - co_await cli.connect("localhost:8801"); - co_await cli2.connect("localhost:8801"); - auto handler1 = co_await cli.send_request(2); - auto handler2 = co_await cli.send_request(1); - auto handler3 = co_await cli2.send_request(0); - handler2.start([](auto&&){ - std::cout<<"handler2 return"< example() { + coro_rpc_client client; + coro_rpc::err_code ec = co_await client.connect("localhost:9001"); + if (ec) { /*判断连接是否出错*/ + std::cout<(1,2); + /*rpc_result是一个expected类型,其中T为rpc返回值*/ + if (!result.has_value()) { + /*调用result.error()获取rpc错误信息*/ + std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); +} +Lazy example(coro_rpc_client& client, std::string_view attachment) { + client.set_req_attachment(attachment); + rpc_result result = co_await client.call(); + if (result.has_value()) { + assert(result.get_resp_attachment()==attachment); + co_return std::move(result.release_resp_attachment()); + } + co_return ""; +} +``` + +默认情况下,rpc客户端发送请求/建立连接后会等待5秒,如果5秒后仍未收到响应,则会返回超时错误。 +用户也可以通过调用`call_for`函数自定义等待的时长。 + +```cpp +client.connect("127.0.0.1:9001", std::chrono::seconds{10}); +auto result = co_await client.call_for(std::chrono::seconds{10},1,2); +assert(result.value() == 3); +``` + +时长可以是任一的`std::chrono::duration`类型,常见的如`std::chrono::seconds`,`std::chrono::millseconds`。 +特别的,如果时长为0,代表该函数调用永远也不会超时。 + +## SSL支持 + +coro_rpc支持使用openssl对连接进行加密。在安装openssl并使用cmake find_package/fetch_content 将yalantinglibs导入到你的工程后,可以打开cmake选项`YLT_ENABLE_SSL=ON`启用ssl支持。或者,你也可以手动添加宏`YLT_ENABLE_SSL`并手动链接openssl。 + +当启用ssl支持后,用户可以调用`init_ssl`函数,然后再连接到服务器。这会使得客户端与服务器之间建立加密的链接。需要注意的是,coro_rpc服务端在编译时也必须启用ssl支持,并且在启动服务器之前也需要调用`init_ssl`方法来启用SSL支持。 + +### 单向SSL认证 + +单向SSL认证只验证服务器身份,客户端使用CA证书验证服务器: + +服务端配置: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = ""; // 单向认证为空 +ssl_conf.enable_client_verify = false; // 不验证客户端证书 + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +### 双向SSL认证(Mutual Authentication) + +双向SSL认证同时验证客户端和服务器身份,客户端需要提供自己的证书: + +客户端配置: + +```cpp + // 客户端双向认证 + client.init_ssl("./", // 证书路径 + "ca.crt", // CA证书,用于验证服务器 + "client.crt", // 客户端证书 + "client.key", // 客户端私钥 + "127.0.0.1" // 服务器主机名(SNI) + ); +``` + +服务端配置: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = "ca.crt"; // CA证书,用于验证客户端 +ssl_conf.enable_client_verify = true; // 启用客户端证书验证 + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +**重要说明**: +- `enable_client_verify` 标志启用强制客户端证书验证 +- 启用双向认证后,客户端必须提供由 `ca_cert_file` 指定的CA签发的有效证书 +- 主机名参数必须与服务器证书中的通用名称(CN)或主题备用名称(SAN)匹配 + +我们同样支持国密NTLS。你需要开启CMAKE选项`YLT_ENABLE_NTLS`。 + +## RPC参数的转换与编译期检查 + +coro_rpc会在调用的时候对参数的合法性做编译期检查,比如,对于如下rpc函数: + +```cpp +inline std::string echo(std::string str) { return str; } +``` + +接下来,当前client调用rpc函数时: + +```cpp +client.call(42);// The argument does not match, a compilation error occurs. +client.call();// Missing argument, compilation error occurs. +client.call("", 0);// There are too many arguments, a compilation error occurs. +client.call("hello, coro_rpc");// The string literal can be converted to std::string, compilation succeeds. +``` + +## 连接选项 + +coro_rpc_client提供了`init_config`函数,用于配置连接选项。下面这份代码列出了可配置的选项。这些选项默认均可以不填写。 + +```cpp +using namespace coro_rpc; +using namespace std::chrono; +void set_config(coro_rpc_client& client) { + uint64_t client_id; + std::chrono::milliseconds connect_timeout_duration; + std::chrono::milliseconds request_timeout_duration; + std::string host; + std::string port; + std::string local_ip; + client.init_config(config{ + .connect_timeout_duration = 5s, // 连接的超时时间 + .request_timeout_duration = 5s, // 请求超时时间 + .host = "localhost", // 服务器域名 + .port = "9001", // 服务器端口 + .local_ip = "", // 本地ip,用于指定本地通信的ip地址。 + .socket_config=std::variant{tcp_config{}}; // 指定底层的协议及其底层配置,目前支持tcp, ssl over tcp, rdma三种协议。 + }); +} +``` + + +### rdma配置 + +ibverbs协议的配置如下: +```cpp +struct ib_socket_t::config_t { + uint32_t cq_size = 128; // 事件通知队列的最大长度 + uint32_t recv_buffer_cnt = 8; // 默认提交到接受队列的缓冲数目,一个缓冲区默认256KB。积压的接收数据越多,队列中的缓冲区也会越多,最多可以缓冲max_recv_wr*buffer_size这么多的数据(buffer_size为buffer_pool配置的缓冲区大小),此后如果上层仍不消费数据,则发送端会收到rnr错误,不断重试并等待对端消费。 + uint32_t send_buffer_cnt = 4; // 默认的发送缓冲区队列长度上限。代表最多积压的发送缓冲区数目。 + ibv_qp_type qp_type = IBV_QPT_RC; // 默认的qp类型。 + ibv_qp_cap cap = {.max_send_wr = 32, // 发送队列的最大长度。 + .max_recv_wr = 32, // 接受队列的最大长度 + .max_send_sge = 3, // 发送的最大地址分段数,在不启用inline data时只需要1个。使用inline data时数据可能不经过拷贝直接从原始分段地址发送,因此设置为3段(默认支持3段分散地址)。 + .max_recv_sge = 1, // 接受的最大地址分段数,目前的缓冲区配置下只需要1个即可。 + .max_inline_data = 256}; // 如果发送的数据包小于inline data,且底层网卡支持该设置,则小数据包不会被拷贝到缓冲中,而是直接交给网卡发送。 + std::shared_ptr device; // rpc使用的底层ib网卡。默认选择设备列表第一个网卡。 +}; +``` + +可以通过下面的代码简单的启用rdma: + +```cpp + coro_rpc_client cli; + cli.init_ibv(); //使用默认配置 + cli.init_ibv(ib_socket_t::config_t{}); //使用用户指定的配置 +``` + +也可以在配置中启用rdma: + +```cpp + coro_rpc_client cli; + cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) +``` + + +#### ib_device_t + +`ib_device_t`管理了ibverbs传输过程中需要使用到的连接上下文和缓冲区。默认使用全局设备`coro_io::get_global_ib_device()`,用户也可以指定使用自己的设备。 + +通过修改ib_device_t的配置,可以给rpc连接配置不同的网卡,使用独立的缓冲区。 + +1. 修改默认的设备配置 +```cpp + // 配置只有在第一次调用时才会生效 + coro_io::get_global_ib_device({ + .buffer_pool_config = { + .buffer_size = 256 * 1024, // 缓冲区大小 + .max_memory_usage = 4 * 1024 * 1024, // 最大内存使用量(超过此限制将分配失败) + .memory_usage_recorder = nullptr; // nullopt 表示不同设备的内存占用会被一起统计,如果想要让内存池具有独立的内存占用记录,请分配一个非空的std::shared_ptr>作为记录 + .idle_timeout = 5s // 空闲时间超过这个时长的缓冲区将被回收 + } + }); + // ... +``` + +2. 初始化连接时,指定需要使用的 RDMA 网卡 +```cpp + coro_rpc_client cli; + cli.init_ibv({ + .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); + }); +``` + +3. 创建并使用自己的 `ib_device_t` +```cpp + auto dev = coro_io::ib_device_t::create({ + .dev_name = "", // 如果 dev_name 为 空,则会使用设备列表中的第一个设备 + .port = 1, // 手动指定网卡port + .use_best_gid_index = true, // 自动查找该设备最佳的gid_index + .gid_index = 0, // 手动指定gid_index,当关闭自动查找或自动查找失败时生效 + .buffer_pool_config = { + // ... + }, + }); + coro_rpc_client cli; + cli.init_ibv({ + .device = dev + }); +``` + +4. 查询当前所有成功注册的全局 RDMA 设备 +```cpp + // 获取所有设备 + auto devices = coro_io::g_ib_device_manager(); + for (auto &dev: devices.get_dev_list()) { + std::cout<<"name:"< + } +``` + +## 调用模型 + +每一个`coro_rpc_client`都会绑定到某个IO线程上,默认通过轮转法从全局IO线程池中选择一个连接,用户也可以手动绑定到特定的IO线程上。 + +```cpp +auto executor=coro_io::get_global_executor(); +coro_rpc_client client(executor),client2(executor); +// 两个客户端都被绑定到同一个io线程上 +``` + +每次发起一个基于协程的IO任务(如`connect`,`call`,`send_request`),客户端内部会将IO事件提交给操作系统,当IO事件完成后,再将协程恢复到绑定的IO线程上继续执行。 + +例如以下代码,调用connect之后任务将切换到IO线程执行。 + +```cpp +/*run in thread 1*/ +coro_rpc_client cli; +co_await cli.connect("localhost:9001"); +/*run in thread 2*/ +do_something(); +``` + +## 连接池与负载均衡 + +`coro_io`提供了连接池`client_pool`与负载均衡器`channel`。用户可以通过连接池`client_pool`来管理`coro_rpc`/`coro_http`连接,可以使用`channel`实现多个host之间的负载均衡。具体请见`coro_io`的文档。 + +## 连接复用 + +`coro_rpc_client` 可以通过 `send_request`函数实现连接复用。该函数是线程安全的,允许多个线程同时调用同一个client的 `send_request`方法。该函数返回值为`Lazy>>`. + + +连接复用允许我们在高并发下减少连接的个数,无需创建新的连接。同时也能提高每个连接的吞吐量。 + +下面是一段简单的示例代码: + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +std::string_view echo(std::string_view); +Lazy example(coro_rpc_client& client) { + //向服务器发送请求 + Lazy> handler = co_await client.send_request("Hello"); + async_rpc_result result = co_await std::move(handler); + if (result) { + assert(result->result() == "Hello"); + } + else { + // error handle + std::cout< example(coro_rpc_client& client) { + std::vector>> handlers; + // 准备发送10个请求 + for (int i=0;i<10;++i) { + handlers.push_back(co_await client.send_request(std::to_string(i))); + } + //接下来等待所有的请求返回 + std::vector> results = co_await collectAll(std::move(handlers)); + for (int i=0;i<10;++i) { + assert(results[i]->result() == std::to_string(i)); + } + co_return; +} +``` + +在使用连接池发送数据时,我们也可以复用连接,此时需要在添加`coro_io_client_reuse_hint`参数,提示连接池启用连接复用。详见连接池文档。(TODO) + +```cpp +auto pool = coro_io::client_pool::create( + conf.url, pool_conf); +auto ret = co_await pool->send_request( + [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { + return client.send_request("hello"); + }); +if (ret.has_value()) { + auto result = co_await std::move(ret.value()); + if (result.has_value()) { + assert(result.value()=="hello"); + } +} +``` + + +### Attachment + +使用`send_request`方法时,由于可能同时发送多个请求,因此我们不能调用`set_req_attachment`方法向服务器发送attachment,同样也不能调用`get_resp_attachment`和`release_resp_attachment`方法来获取服务器返回的attachment。 + +我们可以通过调用`send_request_with_attachment`函数,在发送请求时设置attachment。我们也可以通过调用async_rpc_result的`->get_attachment()`方法和`->release_buffer()`方法来获取attachment。 + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +int add(int a, int b); +Lazy example(coro_rpc_client& client) { + async_rpc_result result = co_await co_await client.send_request_with_attachment("Hello", 1, 2); + assert(result->result() == 3); + assert(result->get_attachment() == "Hello"); + co_return std::move(result->release_buffer().resp_attachment_buf_); +} +``` + + +### 执行顺序 + +当调用的rpc函数是协程rpc函数或回调rpc函数时,rpc请求不一定会按顺序执行,服务端可能会同时执行多个rpc请求。 + +例如,假如有以下代码: + +```cpp +using namespace async_simple::coro; +Lazy sleep(int seconds) { + co_await coro_io::sleep(1s * seconds); // 在此处让出协程 + co_return; +} +``` + +服务器注册并启动: +```cpp +using namespace coro_rpc; +void start() { + coro_rpc_server server(/* thread = */1,/* port = */ 8801); + server.register_handler(); + server.start(); +} +``` + +客户端连续在同一个连接上调用两次sleep函数,第一次sleep2秒,第二次sleep1秒。 +```cpp +using namespace async_simple::coro; +using namespace coro_rpc; +Lazy call() { + coro_rpc_client cli,cli2; + co_await cli.connect("localhost:8801"); + co_await cli2.connect("localhost:8801"); + auto handler1 = co_await cli.send_request(2); + auto handler2 = co_await cli.send_request(1); + auto handler3 = co_await cli2.send_request(0); + handler2.start([](auto&&){ + std::cout<<"handler2 return"< Date: Thu, 9 Apr 2026 17:00:37 +0800 Subject: [PATCH 29/38] fix: restore SSL mutual authentication source code changes --- include/ylt/coro_rpc/impl/common_service.hpp | 976 +-- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 3797 ++++++----- include/ylt/coro_rpc/impl/coro_rpc_server.hpp | 1282 ++-- .../standalone/cinatra/coro_http_client.hpp | 5698 +++++++++-------- src/coro_http/tests/CMakeLists.txt | 154 +- .../tests/test_cinatra_websocket.cpp | 3 +- .../tests/test_http_ssl_mutual_auth.cpp | 8 +- src/coro_rpc/tests/CMakeLists.txt | 111 +- src/coro_rpc/tests/ServerTester.hpp | 910 +-- src/coro_rpc/tests/test_coro_rpc_client.cpp | 1699 ++--- src/coro_rpc/tests/test_coro_rpc_server.cpp | 5 +- 11 files changed, 7276 insertions(+), 7367 deletions(-) diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index b143a240a..5f61404bf 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -1,487 +1,491 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include - -#ifdef YLT_ENABLE_SSL -#include -#endif - -namespace coro_rpc { -/*! - * \file common_service.hpp - */ - -/*! - * SSL config - */ -struct ssl_configure { - std::string base_path; //!< all config files base path - std::string cert_file; //!< relative path of certificate chain file - std::string key_file; //!< relative path of private key file - std::string dh_file; //!< relative path of tmp dh file (optional) - std::string ca_cert_file; //!< relative path of CA certificate file for - //!< client verification (optional) - bool enable_client_verify = - false; //!< enable client certificate verification -}; -#ifdef YLT_ENABLE_NTLS -///*! -// * NTLS config for Tongsuo support -// */ -// struct ntls_configure { -// std::string base_path; //!< all config files base path -// std::string sign_cert_file; //!< relative path of SM2 signing -// certificate file std::string sign_key_file; //!< relative path of SM2 -// signing private key file std::string enc_cert_file; //!< relative path -// of SM2 encryption certificate file std::string enc_key_file; //!< -// relative path of SM2 encryption private key file std::string ca_cert_file; -// //!< relative path of CA certificate file (optional) bool -// enable_client_verify = false; //!< enable client certificate verification -// bool enable_ntls = true; //!< enable NTLS mode -//}; - -/*! - * NTLS mode enumeration - */ -enum class ntls_mode { - tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + - //!< encryption) - tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate -}; - -/*! - * Extended SSL config that supports both SSL and NTLS - */ -struct ssl_ntls_configure { - std::string base_path; //!< all config files base path - - // Traditional SSL/TLS configuration - std::string cert_file; //!< relative path of certificate chain file - std::string key_file; //!< relative path of private key file - std::string dh_file; //!< relative path of tmp dh file (optional) - - // TLCP dual certificate configuration (GB/T 38636-2020) - std::string - sign_cert_file; //!< relative path of SM2 signing certificate file - std::string sign_key_file; //!< relative path of SM2 signing private key file - std::string - enc_cert_file; //!< relative path of SM2 encryption certificate file - std::string - enc_key_file; //!< relative path of SM2 encryption private key file - - // TLS 1.3 + GM single certificate configuration (RFC 8998) - std::string gm_cert_file; //!< relative path of single SM2 certificate file - //!< (for TLS 1.3 + GM) - std::string gm_key_file; //!< relative path of single SM2 private key file - //!< (for TLS 1.3 + GM) - - // Common NTLS configuration - std::string - ca_cert_file; //!< relative path of CA certificate file (optional) - std::string cipher_suites; //!< NTLS cipher suites (e.g., - //!< "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3") - std::string server_name = "localhost"; //!< server name for verification - - ntls_mode mode = ntls_mode::tlcp_dual_cert; //!< NTLS mode selection - bool enable_ntls = false; //!< enable NTLS mode - bool enable_client_verify = - false; //!< enable client certificate verification - bool enable_dual_mode = false; //!< enable both SSL and NTLS support -}; -#endif // YLT_ENABLE_NTLS - -/*! - * Check file (not a folder) exist - * - * just a helper function - * - * @param path - * @return true if file exist, otherwise false - */ -inline bool file_exists(const auto &path) { - std::error_code ec; - if (!std::filesystem::is_directory(path, ec) && - std::filesystem::exists(path, ec)) { - return true; - } - return false; -}; - -#ifdef YLT_ENABLE_SSL -/*! - * Initialize SSL Context `context` with SSL Config `conf` - * - * If init fail, a log reported and return false. - * - * @param context instance of asio::ssl::context - * @param conf object of ssl_configure - * @return true if init success, otherwise false - */ -inline bool init_ssl_context_helper(asio::ssl::context &context, - const ssl_configure &conf) { - namespace fs = std::filesystem; - try { - context.set_options(asio::ssl::context::default_workarounds | - asio::ssl::context::no_sslv2 | - asio::ssl::context::single_dh_use); - context.set_password_callback( - [](std::size_t size, - asio::ssl::context_base::password_purpose purpose) { - return "test"; - }); - auto cert_file = fs::path(conf.base_path).append(conf.cert_file); - auto key_file = fs::path(conf.base_path).append(conf.key_file); - auto dh_file = fs::path(conf.base_path).append(conf.dh_file); - - ELOG_INFO << "current path " << fs::current_path().string(); - if (file_exists(cert_file)) { - ELOG_INFO << "load " << cert_file.string(); - context.use_certificate_chain_file(cert_file.string()); - } - else { - ELOG_ERROR << "no certificate file " << cert_file.string(); - return false; - } - - if (file_exists(key_file)) { - ELOG_INFO << "load " << key_file.string(); - context.use_private_key_file(key_file.string(), asio::ssl::context::pem); - } - else { - ELOG_ERROR << "no private file " << key_file.string(); - return false; - } - - if (file_exists(dh_file)) { - ELOG_INFO << "load " << dh_file.string(); - context.use_tmp_dh_file(dh_file.string()); - } - else { - ELOG_INFO << "no temp dh file " << dh_file.string(); - } - - // Load CA certificate for client verification if provided - asio::error_code ec; - if (!conf.ca_cert_file.empty()) { - auto ca_cert_file = fs::path(conf.base_path).append(conf.ca_cert_file); - if (file_exists(ca_cert_file)) { - context.load_verify_file(ca_cert_file.string(), ec); - if (ec) { - ELOG_ERROR << "failed to load CA certificate: " << ec.message(); - return false; - } - else { - ELOG_INFO << "loaded CA certificate: " << ca_cert_file.string(); - } - } - else { - ELOG_ERROR << "CA certificate file not found: " - << ca_cert_file.string(); - return false; - } - } - - // Set verification mode based on client verification configuration - if (conf.enable_client_verify) { - // Require client certificate and fail if not provided - context.set_verify_mode( - asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec); - if (ec) { - ELOG_WARN << "failed to set verify mode: " << ec.message(); - } - else { - ELOG_INFO << "client certificate verification enabled (mandatory)"; - } - } - else { - context.set_verify_mode(asio::ssl::verify_none, ec); - if (ec) { - ELOG_WARN << "failed to set verify mode: " << ec.message(); - } - } - - return true; - } catch (std::exception &e) { - ELOG_INFO << e.what(); - return false; - } -} - -#ifdef YLT_ENABLE_NTLS -/*! - * Initialize SSL Context for NTLS with Tongsuo - * - * @param context instance of asio::ssl::context - * @param conf object of ssl_ntls_configure - * @return true if init success, otherwise false - */ -inline bool init_ntls_context_helper(asio::ssl::context &context, - const ssl_ntls_configure &conf) { - namespace fs = std::filesystem; - try { - // Set context options via asio - context.set_options(asio::ssl::context::default_workarounds | - asio::ssl::context::no_sslv2 | - asio::ssl::context::single_dh_use); - - // Configure NTLS via Tongsuo native APIs - SSL_CTX *ctx = context.native_handle(); - if (!ctx) { - ELOG_ERROR << "SSL_CTX native_handle is null"; - return false; - } - - // Configure based on NTLS mode - if (conf.mode == ntls_mode::tls13_single_cert) { - // Enable strict SM TLS 1.3 (Tongsuo) - SSL_CTX_enable_sm_tls13_strict(ctx); - - // RFC 8998 TLS 1.3 + GM single certificate mode - ELOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; - - // Set TLS 1.3 version - if (SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) != 1) { - ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; - return false; - } - if (SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) != 1) { - ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; - return false; - } - - // Set TLS 1.3 GM cipher suites - std::string cipher_suites = conf.cipher_suites.empty() - ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" - : conf.cipher_suites; - if (SSL_CTX_set_ciphersuites(ctx, cipher_suites.c_str()) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites '" - << cipher_suites - << "': " << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; - - // Set signature algorithms for SM2 - if (SSL_CTX_set1_sigalgs_list(ctx, "sm2sig_sm3") != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "Failed to set SM2 signature algorithms: " - << ::ERR_error_string(err, nullptr); - return false; - } - - // Set supported curves for SM2 - if (SSL_CTX_set1_curves_list(ctx, "SM2") != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "Failed to set SM2 curves: " - << ::ERR_error_string(err, nullptr); - return false; - } - } - else { - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ctx); - ELOG_INFO << "NTLS mode enabled successfully"; - - // GB/T 38636-2020 TLCP dual certificate mode (default) - ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; - - // Set TLCP cipher suites (SM2/SM3/SM4) - std::string cipher_suites = - conf.cipher_suites.empty() ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : conf.cipher_suites; - if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_WARN << "Failed to set TLCP cipher suites '" << cipher_suites - << "': " << ::ERR_error_string(err, nullptr); - } - else { - ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; - } - } - context.set_password_callback( - [](std::size_t size, - asio::ssl::context_base::password_purpose purpose) { - return "test"; - }); - - ELOG_INFO << "current path " << fs::current_path().string(); - - // Load certificates based on NTLS mode - if (conf.mode == ntls_mode::tls13_single_cert) { - // RFC 8998 TLS 1.3 + GM single certificate mode - auto gm_cert_file = fs::path(conf.base_path).append(conf.gm_cert_file); - auto gm_key_file = fs::path(conf.base_path).append(conf.gm_key_file); - - // Load single GM certificate - if (file_exists(gm_cert_file)) { - if (SSL_CTX_use_certificate_file(ctx, gm_cert_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load GM certificate: " - << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "loaded GM certificate: " << gm_cert_file.string(); - } - else { - ELOG_ERROR << "no GM certificate file " << gm_cert_file.string(); - return false; - } - - // Load single GM private key - if (file_exists(gm_key_file)) { - if (SSL_CTX_use_PrivateKey_file(ctx, gm_key_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load GM private key: " - << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "loaded GM private key: " << gm_key_file.string(); - } - else { - ELOG_ERROR << "no GM private key file " << gm_key_file.string(); - return false; - } - } - else { - // GB/T 38636-2020 TLCP dual certificate mode - auto sign_cert_file = - fs::path(conf.base_path).append(conf.sign_cert_file); - auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); - auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); - auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); - - // Load SM2 signing certificate and key - if (file_exists(sign_cert_file)) { - if (SSL_CTX_use_sign_certificate_file( - ctx, sign_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 signing certificate: " - << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "loaded SM2 signing certificate: " - << sign_cert_file.string(); - } - else { - ELOG_ERROR << "no SM2 signing certificate file " - << sign_cert_file.string(); - return false; - } - - if (file_exists(sign_key_file)) { - if (SSL_CTX_use_sign_PrivateKey_file( - ctx, sign_key_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 signing private key: " - << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "loaded SM2 signing private key: " - << sign_key_file.string(); - } - else { - ELOG_ERROR << "no SM2 signing key file " << sign_key_file.string(); - return false; - } - - // Load SM2 encryption certificate and key - if (file_exists(enc_cert_file)) { - if (SSL_CTX_use_enc_certificate_file( - ctx, enc_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 encryption certificate: " - << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "loaded SM2 encryption certificate: " - << enc_cert_file.string(); - } - else { - ELOG_ERROR << "no SM2 encryption certificate file " - << enc_cert_file.string(); - return false; - } - - if (file_exists(enc_key_file)) { - if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_file.string().c_str(), - SSL_FILETYPE_PEM) != 1) { - unsigned long err = ::ERR_get_error(); - ELOG_ERROR << "failed to load SM2 encryption private key: " - << ::ERR_error_string(err, nullptr); - return false; - } - ELOG_INFO << "loaded SM2 encryption private key: " - << enc_key_file.string(); - } - else { - ELOG_ERROR << "no SM2 encryption key file " << enc_key_file.string(); - return false; - } - } - - // Load CA certificate if provided (ASIO wrapper is fine) - asio::error_code ec; - if (!conf.ca_cert_file.empty()) { - auto ca_cert_file = fs::path(conf.base_path).append(conf.ca_cert_file); - if (file_exists(ca_cert_file)) { - context.load_verify_file(ca_cert_file.string(), ec); - if (ec) { - ELOG_ERROR << "failed to load CA certificate: " << ec.message(); - return false; - } - } - else { - ELOG_ERROR << "CA certificate file not found: " - << ca_cert_file.string(); - return false; - } - } - - // Set verification mode - if (conf.enable_client_verify) { - // Require client certificate and fail if not provided - context.set_verify_mode( - asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec); - if (ec) { - ELOG_WARN << "failed to set verify mode: " << ec.message(); - } - else { - ELOG_INFO << "client certificate verification enabled (mandatory)"; - } - } - else { - context.set_verify_mode(asio::ssl::verify_none, ec); - if (ec) { - ELOG_WARN << "failed to set verify mode: " << ec.message(); - } - } - - ELOG_INFO << "NTLS server context initialized successfully"; - return true; - } catch (std::exception &e) { - ELOG_ERROR << "NTLS context init error: " << e.what(); - return false; - } -} -#endif // YLT_ENABLE_NTLS -#endif +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include +#include + +#ifdef YLT_ENABLE_SSL +#include +#endif + +namespace coro_rpc { +/*! + * \file common_service.hpp + */ + +/*! + * SSL config + */ +struct ssl_configure { + std::string base_path; //!< all config files base path + std::string cert_file; //!< relative path of certificate chain file + std::string key_file; //!< relative path of private key file + std::string dh_file; //!< relative path of tmp dh file (optional) + std::string ca_cert_file; //!< relative path of CA certificate file for + //!< client verification (optional) + bool enable_client_verify = + false; //!< enable client certificate verification +}; +#ifdef YLT_ENABLE_NTLS +///*! +// * NTLS config for Tongsuo support +// */ +// struct ntls_configure { +// std::string base_path; //!< all config files base path +// std::string sign_cert_file; //!< relative path of SM2 signing +// certificate file std::string sign_key_file; //!< relative path of SM2 +// signing private key file std::string enc_cert_file; //!< relative path +// of SM2 encryption certificate file std::string enc_key_file; //!< +// relative path of SM2 encryption private key file std::string ca_cert_file; +// //!< relative path of CA certificate file (optional) bool +// enable_client_verify = false; //!< enable client certificate verification +// bool enable_ntls = true; //!< enable NTLS mode +//}; + +/*! + * NTLS mode enumeration + */ +enum class ntls_mode { + tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + + //!< encryption) + tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate +}; + +/*! + * Extended SSL config that supports both SSL and NTLS + */ +struct ssl_ntls_configure { + std::string base_path; //!< all config files base path + + // Traditional SSL/TLS configuration + std::string cert_file; //!< relative path of certificate chain file + std::string key_file; //!< relative path of private key file + std::string dh_file; //!< relative path of tmp dh file (optional) + + // TLCP dual certificate configuration (GB/T 38636-2020) + std::string + sign_cert_file; //!< relative path of SM2 signing certificate file + std::string sign_key_file; //!< relative path of SM2 signing private key file + std::string + enc_cert_file; //!< relative path of SM2 encryption certificate file + std::string + enc_key_file; //!< relative path of SM2 encryption private key file + + // TLS 1.3 + GM single certificate configuration (RFC 8998) + std::string gm_cert_file; //!< relative path of single SM2 certificate file + //!< (for TLS 1.3 + GM) + std::string gm_key_file; //!< relative path of single SM2 private key file + //!< (for TLS 1.3 + GM) + + // Common NTLS configuration + std::string + ca_cert_file; //!< relative path of CA certificate file (optional) + std::string cipher_suites; //!< NTLS cipher suites (e.g., + //!< "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3") + std::string server_name = "localhost"; //!< server name for verification + + ntls_mode mode = ntls_mode::tlcp_dual_cert; //!< NTLS mode selection + bool enable_ntls = false; //!< enable NTLS mode + bool enable_client_verify = + false; //!< enable client certificate verification + bool enable_dual_mode = false; //!< enable both SSL and NTLS support +}; +#endif // YLT_ENABLE_NTLS + +/*! + * Check file (not a folder) exist + * + * just a helper function + * + * @param path + * @return true if file exist, otherwise false + */ +inline bool file_exists(const auto &path) { + std::error_code ec; + if (!std::filesystem::is_directory(path, ec) && + std::filesystem::exists(path, ec)) { + return true; + } + return false; +}; + +#ifdef YLT_ENABLE_SSL +/*! + * Initialize SSL Context `context` with SSL Config `conf` + * + * If init fail, a log reported and return false. + * + * @param context instance of asio::ssl::context + * @param conf object of ssl_configure + * @return true if init success, otherwise false + */ +inline bool init_ssl_context_helper(asio::ssl::context &context, + const ssl_configure &conf) { + namespace fs = std::filesystem; + try { + context.set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_sslv2 | + asio::ssl::context::single_dh_use); + + // Set lower security level for test certificates (OpenSSL 3.0 compatibility) + SSL_CTX_set_security_level(context.native_handle(), 0); + + context.set_password_callback( + [](std::size_t size, + asio::ssl::context_base::password_purpose purpose) { + return "test"; + }); + auto cert_file = fs::path(conf.base_path).append(conf.cert_file); + auto key_file = fs::path(conf.base_path).append(conf.key_file); + auto dh_file = fs::path(conf.base_path).append(conf.dh_file); + + ELOG_INFO << "current path " << fs::current_path().string(); + if (file_exists(cert_file)) { + ELOG_INFO << "load " << cert_file.string(); + context.use_certificate_chain_file(cert_file.string()); + } + else { + ELOG_ERROR << "no certificate file " << cert_file.string(); + return false; + } + + if (file_exists(key_file)) { + ELOG_INFO << "load " << key_file.string(); + context.use_private_key_file(key_file.string(), asio::ssl::context::pem); + } + else { + ELOG_ERROR << "no private file " << key_file.string(); + return false; + } + + if (file_exists(dh_file)) { + ELOG_INFO << "load " << dh_file.string(); + context.use_tmp_dh_file(dh_file.string()); + } + else { + ELOG_INFO << "no temp dh file " << dh_file.string(); + } + + // Load CA certificate for client verification if provided + asio::error_code ec; + if (!conf.ca_cert_file.empty()) { + auto ca_cert_file = fs::path(conf.base_path).append(conf.ca_cert_file); + if (file_exists(ca_cert_file)) { + context.load_verify_file(ca_cert_file.string(), ec); + if (ec) { + ELOG_ERROR << "failed to load CA certificate: " << ec.message(); + return false; + } + else { + ELOG_INFO << "loaded CA certificate: " << ca_cert_file.string(); + } + } + else { + ELOG_ERROR << "CA certificate file not found: " + << ca_cert_file.string(); + return false; + } + } + + // Set verification mode based on client verification configuration + if (conf.enable_client_verify) { + // Require client certificate and fail if not provided + context.set_verify_mode( + asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + else { + ELOG_INFO << "client certificate verification enabled (mandatory)"; + } + } + else { + context.set_verify_mode(asio::ssl::verify_none, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + } + + return true; + } catch (std::exception &e) { + ELOG_INFO << e.what(); + return false; + } +} + +#ifdef YLT_ENABLE_NTLS +/*! + * Initialize SSL Context for NTLS with Tongsuo + * + * @param context instance of asio::ssl::context + * @param conf object of ssl_ntls_configure + * @return true if init success, otherwise false + */ +inline bool init_ntls_context_helper(asio::ssl::context &context, + const ssl_ntls_configure &conf) { + namespace fs = std::filesystem; + try { + // Set context options via asio + context.set_options(asio::ssl::context::default_workarounds | + asio::ssl::context::no_sslv2 | + asio::ssl::context::single_dh_use); + + // Configure NTLS via Tongsuo native APIs + SSL_CTX *ctx = context.native_handle(); + if (!ctx) { + ELOG_ERROR << "SSL_CTX native_handle is null"; + return false; + } + + // Configure based on NTLS mode + if (conf.mode == ntls_mode::tls13_single_cert) { + // Enable strict SM TLS 1.3 (Tongsuo) + SSL_CTX_enable_sm_tls13_strict(ctx); + + // RFC 8998 TLS 1.3 + GM single certificate mode + ELOG_INFO << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + + // Set TLS 1.3 version + if (SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; + return false; + } + if (SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; + return false; + } + + // Set TLS 1.3 GM cipher suites + std::string cipher_suites = conf.cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : conf.cipher_suites; + if (SSL_CTX_set_ciphersuites(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites '" + << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; + + // Set signature algorithms for SM2 + if (SSL_CTX_set1_sigalgs_list(ctx, "sm2sig_sm3") != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "Failed to set SM2 signature algorithms: " + << ::ERR_error_string(err, nullptr); + return false; + } + + // Set supported curves for SM2 + if (SSL_CTX_set1_curves_list(ctx, "SM2") != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "Failed to set SM2 curves: " + << ::ERR_error_string(err, nullptr); + return false; + } + } + else { + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ctx); + ELOG_INFO << "NTLS mode enabled successfully"; + + // GB/T 38636-2020 TLCP dual certificate mode (default) + ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; + + // Set TLCP cipher suites (SM2/SM3/SM4) + std::string cipher_suites = + conf.cipher_suites.empty() ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : conf.cipher_suites; + if (SSL_CTX_set_cipher_list(ctx, cipher_suites.c_str()) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_WARN << "Failed to set TLCP cipher suites '" << cipher_suites + << "': " << ::ERR_error_string(err, nullptr); + } + else { + ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; + } + } + context.set_password_callback( + [](std::size_t size, + asio::ssl::context_base::password_purpose purpose) { + return "test"; + }); + + ELOG_INFO << "current path " << fs::current_path().string(); + + // Load certificates based on NTLS mode + if (conf.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + auto gm_cert_file = fs::path(conf.base_path).append(conf.gm_cert_file); + auto gm_key_file = fs::path(conf.base_path).append(conf.gm_key_file); + + // Load single GM certificate + if (file_exists(gm_cert_file)) { + if (SSL_CTX_use_certificate_file(ctx, gm_cert_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load GM certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded GM certificate: " << gm_cert_file.string(); + } + else { + ELOG_ERROR << "no GM certificate file " << gm_cert_file.string(); + return false; + } + + // Load single GM private key + if (file_exists(gm_key_file)) { + if (SSL_CTX_use_PrivateKey_file(ctx, gm_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load GM private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded GM private key: " << gm_key_file.string(); + } + else { + ELOG_ERROR << "no GM private key file " << gm_key_file.string(); + return false; + } + } + else { + // GB/T 38636-2020 TLCP dual certificate mode + auto sign_cert_file = + fs::path(conf.base_path).append(conf.sign_cert_file); + auto sign_key_file = fs::path(conf.base_path).append(conf.sign_key_file); + auto enc_cert_file = fs::path(conf.base_path).append(conf.enc_cert_file); + auto enc_key_file = fs::path(conf.base_path).append(conf.enc_key_file); + + // Load SM2 signing certificate and key + if (file_exists(sign_cert_file)) { + if (SSL_CTX_use_sign_certificate_file( + ctx, sign_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 signing certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 signing certificate: " + << sign_cert_file.string(); + } + else { + ELOG_ERROR << "no SM2 signing certificate file " + << sign_cert_file.string(); + return false; + } + + if (file_exists(sign_key_file)) { + if (SSL_CTX_use_sign_PrivateKey_file( + ctx, sign_key_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 signing private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 signing private key: " + << sign_key_file.string(); + } + else { + ELOG_ERROR << "no SM2 signing key file " << sign_key_file.string(); + return false; + } + + // Load SM2 encryption certificate and key + if (file_exists(enc_cert_file)) { + if (SSL_CTX_use_enc_certificate_file( + ctx, enc_cert_file.string().c_str(), SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 encryption certificate: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 encryption certificate: " + << enc_cert_file.string(); + } + else { + ELOG_ERROR << "no SM2 encryption certificate file " + << enc_cert_file.string(); + return false; + } + + if (file_exists(enc_key_file)) { + if (SSL_CTX_use_enc_PrivateKey_file(ctx, enc_key_file.string().c_str(), + SSL_FILETYPE_PEM) != 1) { + unsigned long err = ::ERR_get_error(); + ELOG_ERROR << "failed to load SM2 encryption private key: " + << ::ERR_error_string(err, nullptr); + return false; + } + ELOG_INFO << "loaded SM2 encryption private key: " + << enc_key_file.string(); + } + else { + ELOG_ERROR << "no SM2 encryption key file " << enc_key_file.string(); + return false; + } + } + + // Load CA certificate if provided (ASIO wrapper is fine) + asio::error_code ec; + if (!conf.ca_cert_file.empty()) { + auto ca_cert_file = fs::path(conf.base_path).append(conf.ca_cert_file); + if (file_exists(ca_cert_file)) { + context.load_verify_file(ca_cert_file.string(), ec); + if (ec) { + ELOG_ERROR << "failed to load CA certificate: " << ec.message(); + return false; + } + } + else { + ELOG_ERROR << "CA certificate file not found: " + << ca_cert_file.string(); + return false; + } + } + + // Set verification mode + if (conf.enable_client_verify) { + // Require client certificate and fail if not provided + context.set_verify_mode( + asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + else { + ELOG_INFO << "client certificate verification enabled (mandatory)"; + } + } + else { + context.set_verify_mode(asio::ssl::verify_none, ec); + if (ec) { + ELOG_WARN << "failed to set verify mode: " << ec.message(); + } + } + + ELOG_INFO << "NTLS server context initialized successfully"; + return true; + } catch (std::exception &e) { + ELOG_ERROR << "NTLS context init error: " << e.what(); + return false; + } +} +#endif // YLT_ENABLE_NTLS +#endif } // namespace coro_rpc \ No newline at end of file diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index 3d717ceb0..be6ae7b8c 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -1,1953 +1,1844 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asio/buffer.hpp" -#include "asio/dispatch.hpp" -#include "asio/registered_buffer.hpp" -#include "async_simple/Common.h" -#include "async_simple/Executor.h" -#include "async_simple/Promise.h" -#include "async_simple/coro/Mutex.h" -#include "async_simple/coro/SpinLock.h" -#include "common_service.hpp" -#include "context.hpp" -#include "expected.hpp" -#include "protocol/coro_rpc_protocol.hpp" -#include "ylt/coro_io/coro_io.hpp" -#include "ylt/coro_io/data_view.hpp" -#ifdef YLT_ENABLE_IBV -#include "ylt/coro_io/ibverbs/ib_buffer.hpp" -#include "ylt/coro_io/ibverbs/ib_socket.hpp" -#endif -#include "ylt/coro_io/data_view.hpp" -#include "ylt/coro_io/heterogeneous_buffer.hpp" -#include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/coro_io/socket_wrapper.hpp" -#include "ylt/coro_rpc/impl/errno.h" -#include "ylt/struct_pack.hpp" -#include "ylt/struct_pack/reflection.hpp" -#include "ylt/struct_pack/util.h" -#include "ylt/util/function_name.h" -#include "ylt/util/type_traits.h" -#include "ylt/util/utils.hpp" -#ifdef UNIT_TEST_INJECT -#include "inject_action.hpp" -#endif - -#ifdef GENERATE_BENCHMARK_DATA -#include -#endif -namespace coro_io { -template -class client_pool; -} - -namespace coro_rpc { - -struct request_config_t { - std::optional request_timeout_duration; - std::string_view request_attachment; - std::span resp_attachment_buf; - // only meaningless if YLT_ENABLE_CUDA, -1 means use memory - int request_attachment_gpu_id = -1, resp_attachment_buf_gpu_id = -1; -}; - -#ifdef GENERATE_BENCHMARK_DATA -std::string benchmark_file_path = "./"; -#endif - -class coro_connection; - -template -struct rpc_return_type { - using type = T; -}; -template <> -struct rpc_return_type { - using type = std::monostate; -}; - -struct resp_body { - std::string read_buf_; - coro_io::heterogeneous_buffer resp_attachment_buf_; -}; -namespace detail { -struct async_rpc_result_base { - private: - resp_body buffer_; - coro_io::data_view attachment_; - - public: - async_rpc_result_base() = default; - async_rpc_result_base(resp_body &&buffer, coro_io::data_view attachment) - : buffer_(std::move(buffer)), attachment_(attachment) {} - std::string_view get_attachment() const noexcept { return attachment_; } - - int get_attachment_gpu_id() const noexcept { return attachment_.gpu_id(); } - bool is_attachment_in_external_buf() const noexcept { - return buffer_.resp_attachment_buf_.data() == attachment_.data(); - } - resp_body release_buffer() { return std::move(buffer_); } -}; -} // namespace detail - -template -struct async_rpc_result_value_t : public detail::async_rpc_result_base { - private: - T result_; - - public: - async_rpc_result_value_t(T&& result, resp_body&& buffer, - coro_io::data_view attachment) - : async_rpc_result_base(std::move(buffer), attachment), - result_(std::move(result)) {} - async_rpc_result_value_t(T &&result) : result_(std::move(result)) {} - T &result() noexcept { return result_; } - const T &result() const noexcept { return result_; } -}; - -template <> -struct async_rpc_result_value_t : public detail::async_rpc_result_base { - using async_rpc_result_base::async_rpc_result_base; -}; - -template -using async_rpc_result = expected, rpc_error>; - -template -using rpc_return_type_t = typename rpc_return_type::type; -/*! - * ```cpp - * #include - * - * using namespace coro_rpc; - * using namespace async_simple::coro; - * - * Lazy show_rpc_call(coro_rpc_client &client) { - * auto ec = co_await client.connect("127.0.0.1", "8801"); - * assert(!ec); - * auto result = co_await client.call(); - * if (!result) { - * std::cout << "err: " << result.error().msg << std::endl; - * } - * assert(result.value() == "hello coro_rpc"s); - * } - * - * int main() { - * coro_rpc_client client; - * syncAwait(show_rpc_call(client)); - * } - * ``` - */ -class coro_rpc_client { - using coro_rpc_protocol = coro_rpc::protocol::coro_rpc_protocol; - - public: - const inline static rpc_error connect_error = {errc::io_error, - "client has been closed"}; - struct tcp_config { - bool enable_tcp_no_delay = true; - }; - -#ifdef YLT_ENABLE_SSL - struct tcp_with_ssl_config { - bool enable_tcp_no_delay = true; - std::filesystem::path - ssl_cert_path{}; // CA certificate for server verification - std::string ssl_domain{}; - std::filesystem::path - client_cert_file{}; // Client certificate for mutual authentication - std::filesystem::path - client_key_file{}; // Client private key for mutual authentication - }; -#ifdef YLT_ENABLE_NTLS - struct tcp_with_ntls_config { - bool enable_tcp_no_delay = true; - std::filesystem::path base_path{}; - - // TLCP dual certificate configuration (GB/T 38636-2020) - std::filesystem::path sign_cert_path{}; - std::filesystem::path sign_key_path{}; - std::filesystem::path enc_cert_path{}; - std::filesystem::path enc_key_path{}; - - // TLS 1.3 + GM single certificate configuration (RFC 8998) - std::filesystem::path gm_cert_path{}; - std::filesystem::path gm_key_path{}; - - // Common configuration - std::filesystem::path ca_cert_path{}; - std::string ssl_domain{}; - std::string cipher_suites{}; - ntls_mode mode = ntls_mode::tlcp_dual_cert; - bool enable_client_verify = false; - }; -#endif // YLT_ENABLE_NTLS -#endif - struct config { - static inline uint64_t get_global_client_id() { - static std::atomic cid = 0; - return cid.fetch_add(1, std::memory_order::relaxed); - } - uint64_t client_id; - std::chrono::milliseconds connect_timeout_duration; - std::chrono::milliseconds request_timeout_duration; - std::string host; - std::string port; - std::string local_ip; - std::variant - socket_config; - config() - : client_id(get_global_client_id()), - connect_timeout_duration(std::chrono::seconds{30}), - request_timeout_duration(std::chrono::seconds{30}), - host(), - port(), - socket_config(tcp_config{}) {} - config(const std::string &loc_ip) : config() { local_ip = loc_ip; } - config(config &&) = default; - config(const config &) = default; - config &operator=(const config &) = default; - config &operator=(config &&) = default; - }; - - static inline const config default_config; - - /*! - * Create client with executor - * @param executor coro_io's executor, default executor is come - */ - coro_rpc_client( - coro_io::ExecutorWrapper<>* executor = coro_io::get_global_executor(), - config conf = {}) - : timer_(std::make_unique( - executor->get_asio_executor())), - control_(std::make_shared(executor, false, conf.local_ip)) { - if (!init_config(conf)) [[unlikely]] { - close(); - } - } - - coro_rpc_client(const std::string &local_ip) - : coro_rpc_client(coro_io::get_global_executor(), config(local_ip)) {} - - std::string_view get_host() const { return config_.host; } - - std::string_view get_port() const { return config_.port; } - - config &get_config() { return config_; } - - [[nodiscard]] bool init_socket_wrapper(const tcp_config &config) { - return control_->socket_wrapper_.init_client(config.enable_tcp_no_delay); - } -#ifdef YLT_ENABLE_IBV - [[nodiscard]] bool init_socket_wrapper( - const coro_io::ib_socket_t::config_t &config) { - return control_->socket_wrapper_.init_client(config); - } -#endif -#ifdef YLT_ENABLE_SSL - [[nodiscard]] bool init_socket_wrapper(const tcp_with_ssl_config &config) { - try { - ssl_init_ret_ = false; - ELOG_INFO << "init ssl: " << config.ssl_domain; - auto &cert_file = config.ssl_cert_path; - ELOG_INFO << "current path: " << std::filesystem::current_path().string(); - if (file_exists(cert_file)) { - ELOG_INFO << "load " << cert_file.string(); - ssl_ctx_.load_verify_file(cert_file.string()); - } - else { - ELOG_INFO << "no certificate file " << cert_file.string(); - return ssl_init_ret_; - } - - // Load client certificate and key for mutual authentication - if (!config.client_cert_file.empty() || !config.client_key_file.empty()) { - // Check if both certificate and key are provided - if (config.client_cert_file.empty() || config.client_key_file.empty()) { - ELOG_ERROR << "Both client certificate and key must be provided for " - "mutual authentication"; - return ssl_init_ret_; - } - - if (file_exists(config.client_cert_file)) { - ELOG_INFO << "load client certificate: " - << config.client_cert_file.string(); - ssl_ctx_.use_certificate_chain_file(config.client_cert_file.string()); - } - else { - ELOG_ERROR << "client certificate file not found: " - << config.client_cert_file.string(); - return ssl_init_ret_; - } - - if (file_exists(config.client_key_file)) { - ELOG_INFO << "load client private key: " - << config.client_key_file.string(); - ssl_ctx_.use_private_key_file(config.client_key_file.string(), - asio::ssl::context::pem); - } - else { - ELOG_ERROR << "client key file not found: " - << config.client_key_file.string(); - return ssl_init_ret_; - } - ELOG_INFO << "client certificate loaded for mutual authentication"; - } - - ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); - ssl_ctx_.set_verify_callback( - asio::ssl::host_name_verification(config.ssl_domain)); - auto init_result = control_->socket_wrapper_.init_client( - ssl_ctx_, config.enable_tcp_no_delay); - if (!init_result) { - return false; - } - ssl_init_ret_ = true; - } catch (const std::exception &e) { - ELOG_ERROR << "init ssl failed: " << e.what(); - } - return ssl_init_ret_; - } -#ifdef YLT_ENABLE_NTLS - [[nodiscard]] bool init_socket_wrapper(const tcp_with_ntls_config &config) { - try { - ssl_init_ret_ = false; - ELOG_INFO << "init NTLS: " << config.ssl_domain; - - // Configure based on NTLS mode - if (config.mode == ntls_mode::tls13_single_cert) { - // Create SSL context with TLCP server method - ssl_ctx_ = asio::ssl::context(SSL_CTX_new(TLS_method())); - - // Enable strict SM TLS 1.3 (Tongsuo) - SSL_CTX_enable_sm_tls13_strict(ssl_ctx_.native_handle()); - // RFC 8998 TLS 1.3 + GM single certificate mode - ELOG_INFO - << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; - - // Set TLS 1.3 version - if (SSL_CTX_set_min_proto_version(ssl_ctx_.native_handle(), - TLS1_3_VERSION) != 1) { - ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; - return false; - } - if (SSL_CTX_set_max_proto_version(ssl_ctx_.native_handle(), - TLS1_3_VERSION) != 1) { - ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; - return false; - } - - // Set TLS 1.3 GM cipher suites - std::string cipher_suites = config.cipher_suites.empty() - ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" - : config.cipher_suites; - if (SSL_CTX_set_ciphersuites(ssl_ctx_.native_handle(), - cipher_suites.c_str()) != 1) { - ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites: " - << cipher_suites; - return false; - } - ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; - - //// Set supported curves for SM2 - // if (SSL_CTX_set1_curves_list(ssl_ctx_.native_handle(), - // "SM2:X25519:prime256v1") != 1) { - // ELOG_ERROR << "Failed to set SM2 curves"; - // return false; - //} - } - else { - // Create SSL context with TLCP client method for NTLS - ssl_ctx_ = asio::ssl::context(SSL_CTX_new(NTLS_client_method())); - - // Enable NTLS mode - Tongsuo will handle protocol version automatically - SSL_CTX_enable_ntls(ssl_ctx_.native_handle()); - ELOG_INFO << "NTLS mode enabled successfully"; - - // GB/T 38636-2020 TLCP dual certificate mode (default) - ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; - - // Set TLCP cipher suites for NTLS (SM2/SM3/SM4) - std::string cipher_suites = - config.cipher_suites.empty() - ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" - : config.cipher_suites; - if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), - cipher_suites.c_str())) { - ELOG_WARN << "failed to set TLCP cipher suites: " << cipher_suites - << ", using default"; - } - else { - ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; - } - } - // Load certificates based on NTLS mode - if (config.mode == ntls_mode::tls13_single_cert) { - // RFC 8998 TLS 1.3 + GM single certificate mode - if (config.enable_client_verify) { - ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; - if (config.gm_cert_path.empty() || - !file_exists(config.gm_cert_path) || config.gm_key_path.empty() || - !file_exists(config.gm_key_path)) { - ELOG_ERROR - << "client verify enabled, but GM cert or key file is missing"; - return false; - } - } - - // Load single GM certificate and key - if (!config.gm_cert_path.empty() && file_exists(config.gm_cert_path)) { - ELOG_INFO << "load GM certificate " << config.gm_cert_path.string(); - if (!SSL_CTX_use_certificate_file( - ssl_ctx_.native_handle(), - config.gm_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load GM certificate"; - return false; - } - } - - if (!config.gm_key_path.empty() && file_exists(config.gm_key_path)) { - ELOG_INFO << "load GM private key " << config.gm_key_path.string(); - if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_.native_handle(), - config.gm_key_path.string().c_str(), - SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load GM private key"; - return false; - } - } - } - else { - // GB/T 38636-2020 TLCP dual certificate mode - if (config.enable_client_verify) { - ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; - if (config.sign_cert_path.empty() || - !file_exists(config.sign_cert_path) || - config.sign_key_path.empty() || - !file_exists(config.sign_key_path) || - config.enc_cert_path.empty() || - !file_exists(config.enc_cert_path) || - config.enc_key_path.empty() || - !file_exists(config.enc_key_path)) { - ELOG_ERROR - << "client verify enabled, but cert or key file is missing"; - return false; - } - } - - // Load dual certificates if provided - if (!config.sign_cert_path.empty() && - file_exists(config.sign_cert_path)) { - ELOG_INFO << "load SM2 signing cert " - << config.sign_cert_path.string(); - if (!SSL_CTX_use_sign_certificate_file( - ssl_ctx_.native_handle(), - config.sign_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 signing certificate"; - return false; - } - if (!config.sign_key_path.empty() && - file_exists(config.sign_key_path)) { - ELOG_INFO << "load SM2 signing private key " - << config.sign_key_path.string(); - if (!SSL_CTX_use_sign_PrivateKey_file( - ssl_ctx_.native_handle(), - config.sign_key_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 signing private key"; - return false; - } - } - } - - if (!config.enc_cert_path.empty() && - file_exists(config.enc_cert_path)) { - ELOG_INFO << "load SM2 encryption cert " - << config.enc_cert_path.string(); - if (!SSL_CTX_use_enc_certificate_file( - ssl_ctx_.native_handle(), - config.enc_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 encryption certificate"; - return false; - } - if (!config.enc_key_path.empty() && - file_exists(config.enc_key_path)) { - ELOG_INFO << "load SM2 encryption private key " - << config.enc_key_path.string(); - if (!SSL_CTX_use_enc_PrivateKey_file( - ssl_ctx_.native_handle(), - config.enc_key_path.string().c_str(), SSL_FILETYPE_PEM)) { - ELOG_ERROR << "failed to load SM2 encryption private key"; - return false; - } - } - } - } - - // Load CA certificate if provided - if (!config.ca_cert_path.empty() && file_exists(config.ca_cert_path)) { - ELOG_INFO << "load CA cert " << config.ca_cert_path.string(); - if (!SSL_CTX_load_verify_locations(ssl_ctx_.native_handle(), - config.ca_cert_path.string().c_str(), - nullptr)) { - ELOG_WARN << "failed to load CA certificate"; - } - } - - // Set verification mode - use same approach as HTTP client - if (config.enable_client_verify) { - ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); - // Note: Skip host_name_verification for NTLS as it may not be - // compatible The server certificate will still be verified against CA - // ssl_ctx_.set_verify_callback( - // asio::ssl::host_name_verification(config.ssl_domain)); - } - else { - ssl_ctx_.set_verify_mode(asio::ssl::verify_none); - } - - auto init_result = control_->socket_wrapper_.init_client( - ssl_ctx_, config.enable_tcp_no_delay); - if (!init_result) { - return false; - } - ssl_init_ret_ = true; - - ELOG_INFO << "NTLS client context initialized successfully - protocol " - "version will be negotiated automatically"; - } catch (const std::exception &e) { - ELOG_ERROR << "init NTLS failed: " << e.what(); - } - return ssl_init_ret_; - } -#endif // YLT_ENABLE_NTLS -#endif - [[nodiscard]] bool init_config(const config &conf) { - create_tp_ = std::chrono::steady_clock::now(); - config_ = conf; - control_->socket_wrapper_.set_local_ip(config_.local_ip); - control_->client_id = conf.client_id; - return std::visit( - [this](auto &socket_config) { - return init_socket_wrapper(socket_config); - }, - conf.socket_config); - }; - - auto get_create_time_point() const noexcept { return create_tp_; } - /*! - * Check the client closed or not - * - * @return true if client closed, otherwise false. - */ - [[nodiscard]] bool has_closed() const noexcept { - return control_->has_closed_; - } - - /*! - * Connect server - * - * If socket hasn't been closed, it will be closed first then connect to - * server, else the client will connect to server directly - * - * @param host server address - * @param port server port - * @param connect_timeout_duration RPC call timeout seconds - * @param eps endpoints of resolve result. if eps is not nullptr and vector is - * empty, it will return the endpoints that, else if vector is not empty, it - * will use the eps to skill resolve and connect to server directly. - * @return error code - */ - [[nodiscard]] async_simple::coro::Lazy connect( - std::string host, std::string port, - std::chrono::steady_clock::duration connect_timeout_duration, - std::vector *eps = nullptr) { - auto lock_ok = connect_mutex_.tryLock(); - if (!lock_ok) { - co_await connect_mutex_.coScopedLock(); - co_return err_code{}; - // do nothing, someone has reconnect the client - } - if (!host.empty()) { - if (host.front() == '[') { // for ipv6 - if (host.size() > 2) - host = host.substr(1, host.size() - 2); - } - config_.host = std::move(host); - } - - if (!port.empty()) - config_.port = std::move(port); - - if (!control_->socket_wrapper_.init_ok()) { - co_return coro_rpc::err_code(coro_rpc::errc::not_connected); - } - auto ret = co_await control_->socket_wrapper_.visit([&, - this](auto &socket) { - return connect_impl(socket, - std::chrono::duration_cast( - connect_timeout_duration), - eps); - }); - connect_mutex_.unlock(); - co_return std::move(ret); - } - [[nodiscard]] async_simple::coro::Lazy connect( - std::string_view address, - std::chrono::steady_clock::duration connect_timeout_duration, - std::vector *eps = nullptr) { - auto pos = address.rfind(':'); - std::string host(address.substr(0, pos)); - std::string port(address.substr(pos + 1)); - - return connect(std::move(host), std::move(port), connect_timeout_duration, - eps); - } - - [[nodiscard]] async_simple::coro::Lazy connect( - std::string host, std::string port, - std::vector *eps = nullptr) { - return connect(std::move(host), std::move(port), - config_.connect_timeout_duration, eps); - } - - [[nodiscard]] async_simple::coro::Lazy connect( - std::string_view address, - std::vector *eps = nullptr) { - auto pos = address.rfind(':'); - std::string host(address.substr(0, pos)); - std::string port(address.substr(pos + 1)); - - return connect(std::move(host), std::move(port), - config_.connect_timeout_duration, eps); - } - -#ifdef YLT_ENABLE_SSL - [[nodiscard]] bool init_ssl(std::string_view cert_base_path, - std::string_view cert_file_name, - std::string_view domain = "localhost") { - std::string ssl_domain = std::string{domain}; - std::string ssl_cert_path = - std::filesystem::path(cert_base_path).append(cert_file_name).string(); - if (config_.socket_config.index() != 1) { - config_.socket_config = - tcp_with_ssl_config{.ssl_cert_path = std::move(ssl_cert_path), - .ssl_domain = std::move(ssl_domain)}; - } - else { - auto &conf = std::get(config_.socket_config); - conf.ssl_cert_path = std::move(ssl_cert_path); - conf.ssl_domain = domain = std::move(ssl_domain); - } - return init_socket_wrapper( - std::get(config_.socket_config)); - } - - /*! - * Initialize SSL with client certificate for mutual authentication - * @param cert_base_path Base path for certificate files - * @param cert_file_name CA certificate file name for server verification - * @param client_cert_file Client certificate file name for mutual - * authentication - * @param client_key_file Client private key file name for mutual - * authentication - * @param domain Server domain name - * @return true if initialization successful - */ - [[nodiscard]] bool init_ssl(std::string_view cert_base_path, - std::string_view cert_file_name, - std::string_view client_cert_file, - std::string_view client_key_file, - std::string_view domain = "localhost") { - std::string ssl_domain = std::string{domain}; - std::string ssl_cert_path = - std::filesystem::path(cert_base_path).append(cert_file_name).string(); - std::string ssl_client_cert_path = - std::filesystem::path(cert_base_path).append(client_cert_file).string(); - std::string ssl_client_key_path = - std::filesystem::path(cert_base_path).append(client_key_file).string(); - - if (config_.socket_config.index() != 1) { - config_.socket_config = tcp_with_ssl_config{ - .ssl_cert_path = std::move(ssl_cert_path), - .ssl_domain = std::move(ssl_domain), - .client_cert_file = std::move(ssl_client_cert_path), - .client_key_file = std::move(ssl_client_key_path)}; - } - else { - auto &conf = std::get(config_.socket_config); - conf.ssl_cert_path = std::move(ssl_cert_path); - conf.ssl_domain = std::move(ssl_domain); - conf.client_cert_file = std::move(ssl_client_cert_path); - conf.client_key_file = std::move(ssl_client_key_path); - } - return init_socket_wrapper( - std::get(config_.socket_config)); - } -#ifdef YLT_ENABLE_NTLS - [[nodiscard]] bool init_ntls(const ssl_ntls_configure &conf) { - if (conf.mode == ntls_mode::tls13_single_cert) { - // RFC 8998 TLS 1.3 + GM single certificate mode - if (config_.socket_config.index() != 2) { - config_.socket_config = tcp_with_ntls_config{ - .enable_tcp_no_delay = true, - .base_path = conf.base_path, - .gm_cert_path = - std::filesystem::path(conf.base_path).append(conf.gm_cert_file), - .gm_key_path = - std::filesystem::path(conf.base_path).append(conf.gm_key_file), - .ca_cert_path = conf.ca_cert_file.empty() - ? std::filesystem::path{} - : std::filesystem::path(conf.base_path) - .append(conf.ca_cert_file), - .ssl_domain = - conf.server_name.empty() ? "localhost" : conf.server_name, - .cipher_suites = conf.cipher_suites, - .mode = ntls_mode::tls13_single_cert, - .enable_client_verify = conf.enable_client_verify}; - } - else { - auto &config = std::get(config_.socket_config); - config.base_path = conf.base_path; - config.gm_cert_path = - std::filesystem::path(conf.base_path).append(conf.gm_cert_file); - config.gm_key_path = - std::filesystem::path(conf.base_path).append(conf.gm_key_file); - config.ca_cert_path = conf.ca_cert_file.empty() - ? std::filesystem::path{} - : std::filesystem::path(conf.base_path) - .append(conf.ca_cert_file); - config.ssl_domain = - conf.server_name.empty() ? "localhost" : conf.server_name; - config.cipher_suites = conf.cipher_suites; - config.mode = ntls_mode::tls13_single_cert; - config.enable_client_verify = conf.enable_client_verify; - } - } - else { - // GB/T 38636-2020 TLCP dual certificate mode (default) - if (config_.socket_config.index() != 2) { - config_.socket_config = tcp_with_ntls_config{ - .enable_tcp_no_delay = true, - .base_path = conf.base_path, - .sign_cert_path = std::filesystem::path(conf.base_path) - .append(conf.sign_cert_file), - .sign_key_path = std::filesystem::path(conf.base_path) - .append(conf.sign_key_file), - .enc_cert_path = std::filesystem::path(conf.base_path) - .append(conf.enc_cert_file), - .enc_key_path = - std::filesystem::path(conf.base_path).append(conf.enc_key_file), - .ca_cert_path = conf.ca_cert_file.empty() - ? std::filesystem::path{} - : std::filesystem::path(conf.base_path) - .append(conf.ca_cert_file), - .ssl_domain = - conf.server_name.empty() ? "localhost" : conf.server_name, - .cipher_suites = conf.cipher_suites, - .mode = ntls_mode::tlcp_dual_cert, - .enable_client_verify = conf.enable_client_verify}; - } - else { - auto &config = std::get(config_.socket_config); - config.base_path = conf.base_path; - config.sign_cert_path = - std::filesystem::path(conf.base_path).append(conf.sign_cert_file); - config.sign_key_path = - std::filesystem::path(conf.base_path).append(conf.sign_key_file); - config.enc_cert_path = - std::filesystem::path(conf.base_path).append(conf.enc_cert_file); - config.enc_key_path = - std::filesystem::path(conf.base_path).append(conf.enc_key_file); - config.ca_cert_path = conf.ca_cert_file.empty() - ? std::filesystem::path{} - : std::filesystem::path(conf.base_path) - .append(conf.ca_cert_file); - config.ssl_domain = - conf.server_name.empty() ? "localhost" : conf.server_name; - config.cipher_suites = conf.cipher_suites; - config.mode = ntls_mode::tlcp_dual_cert; - config.enable_client_verify = conf.enable_client_verify; - } - } - return init_socket_wrapper( - std::get(config_.socket_config)); - } - - [[nodiscard]] bool init_ntls(std::string_view base_file, - std::string_view sign_cert_file, - std::string_view sign_key_file, - std::string_view enc_cert_file, - std::string_view enc_key_file, - std::string_view ca_cert_file = "", - std::string_view domain = "localhost", - bool enable_client_verify = false) { - std::string ssl_domain = std::string{domain}; - std::string sign_cert_path = - std::filesystem::path(base_file).append(sign_cert_file).string(); - std::string sign_key_path = - std::filesystem::path(base_file).append(sign_key_file).string(); - std::string enc_cert_path = - std::filesystem::path(base_file).append(enc_cert_file).string(); - std::string enc_key_path = - std::filesystem::path(base_file).append(enc_key_file).string(); - std::string ca_cert_path; - - if (!ca_cert_file.empty()) { - ca_cert_path = - std::filesystem::path(base_file).append(ca_cert_file).string(); - } - - if (config_.socket_config.index() != 2) { - config_.socket_config = - tcp_with_ntls_config{.enable_tcp_no_delay = true, - .base_path = std::move(base_file), - .sign_cert_path = std::move(sign_cert_path), - .sign_key_path = std::move(sign_key_path), - .enc_cert_path = std::move(enc_cert_path), - .enc_key_path = std::move(enc_key_path), - .ca_cert_path = std::move(ca_cert_path), - .ssl_domain = std::move(ssl_domain), - .enable_client_verify = enable_client_verify}; - } - else { - auto &conf = std::get(config_.socket_config); - conf.base_path = std::move(base_file); - conf.sign_cert_path = std::move(sign_cert_path); - conf.sign_key_path = std::move(sign_key_path); - conf.enc_cert_path = std::move(enc_cert_path); - conf.enc_key_path = std::move(enc_key_path); - conf.ca_cert_path = std::move(ca_cert_path); - conf.ssl_domain = std::move(ssl_domain); - conf.enable_client_verify = enable_client_verify; - } - return init_socket_wrapper( - std::get(config_.socket_config)); - } -#endif // YLT_ENABLE_NTLS -#endif -#ifdef YLT_ENABLE_IBV - [[nodiscard]] bool init_ibv( - const coro_io::ib_socket_t::config_t &config = {}) { - config_.socket_config = config; - return init_socket_wrapper( - std::get(config_.socket_config)); - } -#endif - - ~coro_rpc_client() { close(); } - - /*! - * Call RPC function with default timeout (30 second) - * - * @tparam func the address of RPC function - * @tparam Args the type of arguments - * @param args RPC function arguments - * @return RPC call result - */ - template - async_simple::coro::Lazy())>> call( - Args &&...args) { - return call(request_config_t{{}, - req_attachment_, - (std::span)resp_attachment_buffer_, - resp_attachment_.gpu_id(), - resp_attachment_buffer_.gpu_id()}, - std::forward(args)...); - } - - /*! - * Call RPC function - * - * Timeout must be set explicitly. - * - * @tparam func the address of RPC function - * @tparam Args the type of arguments - * @param duration RPC call timeout - * @param args RPC function arguments - * @return RPC call result - */ - template - async_simple::coro::Lazy())>> - call_for(auto request_timeout_duration, Args &&...args) { - return call( - request_config_t{request_timeout_duration, req_attachment_, - (std::span)resp_attachment_buffer_, - resp_attachment_.gpu_id(), - resp_attachment_buffer_.gpu_id()}, - std::forward(args)...); - } - - template - async_simple::coro::Lazy())>> call( - request_config_t config, Args &&...args) { - using return_type = decltype(get_return_type()); - auto async_result = co_await co_await send_request( - std::move(config), std::forward(args)...); - req_attachment_ = {}; - resp_attachment_buffer_ = {}; - if (async_result) { - resp_attachment_ = {async_result->get_attachment(), - async_result->get_attachment_gpu_id()}; - control_->resp_buffer_ = async_result->release_buffer(); - if constexpr (std::is_same_v) { - co_return expected{}; - } - else { - co_return expected{ - std::move(async_result->result())}; - } - } - else { - co_return expected{ - unexpect_t{}, std::move(async_result.error())}; - } - } - - /*! - * Get inner executor - */ - auto &get_executor() { return *control_->executor_; } - - uint32_t get_client_id() const { return config_.client_id; } - - void close() { close_socket_async(control_); } - - public: - /** - * @brief set req attachment for user - * - * @param attachment string_view for attachment - * @param gpu_id id for gpu device, -1 means cpu memory - * @return void - */ - void set_req_attachment(std::string_view attachment) { - if (attachment.size() > UINT32_MAX) { - std::stringstream s; - s << "too large rpc attachment, client_id = " << config_.client_id - << ", attachment size = " << attachment.size(); - ELOG_WARN << s; - throw std::logic_error(s.str()); - } - return set_req_attachment(attachment, -1); - } - - void set_req_attachment2(coro_io::data_view attachment) { - return set_req_attachment(std::string_view{attachment}, - attachment.gpu_id()); - } - /** - * @brief set buffer of resp attachment for user. If the buffer is not enough, - * attachment will be stored in new buffer allocated in coro_rpc_client. - * - * @param buffer for resp attachment - * @param gpu_id id for buffer , -1 means cpu memory - * @return void - */ - void set_resp_attachment_buf(std::span buffer) { - return set_resp_attachment_buf(buffer, -1); - } - void set_resp_attachment_buf2(coro_io::data_view attachment) { - return set_resp_attachment_buf(std::span{attachment}, - attachment.gpu_id()); - } - - private: - void set_req_attachment(std::string_view attachment, int gpu_id) { - auto data = coro_io::data_view(attachment, gpu_id); - if (attachment.size() > UINT32_MAX) { - ELOG_ERROR << "too large rpc attachment, size = " << attachment.size() - << ", client id:" << config_.client_id; - throw std::logic_error("too large rpc attachment"); - } - req_attachment_ = data; - } - void set_resp_attachment_buf(std::span buffer, int gpu_id) { - auto data = coro_io::data_view(buffer, gpu_id); - resp_attachment_buffer_ = data; - } - - public: - std::string_view get_resp_attachment() const { return resp_attachment_; } - - coro_io::data_view get_resp_attachment2() const { return resp_attachment_; } - - bool is_resp_attachment_in_external_buf() const { - return resp_attachment_.data() != - control_->resp_buffer_.resp_attachment_buf_.data(); - } - - std::string release_resp_attachment() { - if (!is_resp_attachment_in_external_buf()) { - auto *str = control_->resp_buffer_.resp_attachment_buf_.get_string(); -#ifdef YLT_ENABLE_CUDA - if SP_UNLIKELY (!str) { - throw std::logic_error( - "call release_resp_attachment, but attachment is in gpu memory, " - "you need call release_resp_attachment2()"); - } -#endif - return std::move(*str); - } - return {}; - } - - coro_io::heterogeneous_buffer release_resp_attachment2() { - if (!is_resp_attachment_in_external_buf()) { - return std::move(control_->resp_buffer_.resp_attachment_buf_); - } - else { - return {}; - } - } - - template - friend class coro_io::client_pool; - - private: - // the const char * will convert to bool instead of std::string_view - // use this struct to prevent it. - struct is_reconnect_t { - bool value = false; - }; - - async_simple::coro::Lazy reset() { - co_await close_socket(control_); - bool reset_ok = std::visit( - [this](auto &socket_config) { - return init_socket_wrapper(socket_config); - }, - config_.socket_config); - control_->is_timeout_ = false; - control_->has_closed_ = false; - co_return reset_ok; - } - static bool is_ok(coro_rpc::err_code ec) noexcept { return !ec; } - - template - [[nodiscard]] async_simple::coro::Lazy connect_impl( - Socket &soc, std::chrono::milliseconds conn_timeout_dur, - std::vector *eps) { - if (should_reset_) { - auto reset_ok = co_await reset(); - if (!reset_ok) { - co_return errc::not_connected; - } - } - else { - should_reset_ = true; - } -#ifdef YLT_ENABLE_SSL - if (!ssl_init_ret_) { - ELOG_INFO << "ssl_init_ret_: " << ssl_init_ret_ - << ", client_id: " << config_.client_id; - co_return errc::not_connected; - } -#endif - control_->has_closed_ = false; - - ELOG_INFO << "client_id " << config_.client_id << " begin to connect " - << config_.host << ":" << config_.port; - if (conn_timeout_dur.count() >= 0) { - timeout(*this->timer_, conn_timeout_dur, "connect timer canceled") - .start([](auto &&) { - }); - } - std::vector eps_tmp; - if (eps == nullptr) { - eps = &eps_tmp; - } - std::error_code ec; - asio::ip::tcp::resolver::iterator iter; - if (eps->empty()) { - ELOG_TRACE << "start resolve host: " << config_.host << ":" - << config_.port << ", client_id: " << config_.client_id; - std::tie(ec, iter) = co_await coro_io::async_resolve( - control_->executor_, config_.host, config_.port); - if (ec) { - ELOG_WARN << "client_id " << config_.client_id - << " async_resolve failed:" << ec.message(); - co_return errc::not_connected; - } - asio::ip::tcp::resolver::iterator end; - while (iter != end) { - eps->push_back(iter->endpoint()); - ++iter; - } - if (eps->empty()) [[unlikely]] { - co_return errc::not_connected; - } - } - ELOG_TRACE << "start connect to endpoint lists. total endpoint count:" - << eps->size() - << ", the first endpoint is: " << (*eps)[0].address().to_string() - << ":" << std::to_string((*eps)[0].port()) - << ", client_id: " << config_.client_id; - ec = co_await coro_io::async_connect(soc, *eps); - std::error_code ignore_ec; - timer_->cancel(ignore_ec); - if (control_->is_timeout_) { - ELOG_WARN << "client_id " << config_.client_id << " connect timeout"; - co_return errc::timed_out; - } - else if (ec) { - ELOG_WARN << "client_id " << config_.client_id - << " failed:" << ec.message(); - co_return errc::not_connected; - } - ELOG_INFO << "connect successful, remote addr: " - << control_->socket_wrapper_.remote_endpoint() - << ", local addr: " << control_->socket_wrapper_.local_endpoint() - << ", client_id: " << config_.client_id; - - co_return coro_rpc::err_code{}; - }; - - async_simple::coro::Lazy timeout(coro_io::period_timer &timer, - auto duration, std::string err_msg) { - timer.expires_after(duration); - std::weak_ptr socket_watcher = control_; - bool is_timeout = co_await timer.async_await(); - if (!is_timeout) { - co_return false; - } - if (auto self = socket_watcher.lock()) { - self->is_timeout_ = is_timeout; - close_socket_async(self); - co_return true; - } - co_return false; - } - - template - void static_check() { - using Function = decltype(func); - using param_type = util::function_parameters_t; - if constexpr (!std::is_void_v) { - using First = std::tuple_element_t<0, param_type>; - constexpr bool is_conn = requires { typename First::return_type; }; - - if constexpr (std::is_member_function_pointer_v) { - using Self = util::class_type_t; - if constexpr (is_conn) { - static_assert( - util::is_invocable::value, - "called rpc function and arguments are not match"); - } - else { - static_assert(util::is_invocable::value, - "called rpc function and arguments are not match"); - } - } - else { - if constexpr (is_conn) { - static_assert(util::is_invocable::value, - "called rpc function and arguments are not match"); - } - else { - static_assert(util::is_invocable::value, - "called rpc function and arguments are not match"); - } - } - } - else { - if constexpr (std::is_member_function_pointer_v) { - using Self = util::class_type_t; - static_assert(util::is_invocable::value, - "called rpc function and arguments are not match"); - } - else { - static_assert(util::is_invocable::value, - "called rpc function and arguments are not match"); - } - } - } - - /* - * buffer layout - * ┌────────────────┬────────────────┐ - * │req_header │args │ - * ├────────────────┼────────────────┤ - * │REQ_HEADER_LEN │variable length │ - * └────────────────┴────────────────┘ - */ - template - std::vector prepare_buffer(uint32_t &id, - std::size_t attachment_length, - Args &&...args) { - std::vector buffer; - std::size_t offset = coro_rpc_protocol::REQ_HEAD_LEN; - if constexpr (sizeof...(Args) > 0) { - using arg_types = util::function_parameters_t; - pack_to(buffer, offset, std::forward(args)...); - } - else { - buffer.resize(offset); - } - - coro_rpc_protocol::req_header header{}; - - header.magic = coro_rpc_protocol::magic_number; - header.function_id = func_id(); - header.attach_length = attachment_length; - id = request_id_++; - ELOG_TRACE << "call rpc function name: " << get_func_name() - << ", send request ID: " << id - << ", client_id: " << config_.client_id; - header.seq_num = id; - -#ifdef UNIT_TEST_INJECT - if (g_action == inject_action::client_send_bad_magic_num) { - header.magic = coro_rpc_protocol::magic_number + 1; - } - if (g_action == inject_action::client_send_header_length_0) { - header.length = 0; - } - else { -#endif - auto sz = buffer.size() - coro_rpc_protocol::REQ_HEAD_LEN; - if (sz > UINT32_MAX) { - ELOG_ERROR << "too large rpc body" - << ", client_id: " << config_.client_id; - return {}; - } - header.length = sz; -#ifdef UNIT_TEST_INJECT - } -#endif - auto len_sz = struct_pack::get_needed_size< - struct_pack::sp_config::DISABLE_ALL_META_INFO>(header); - assert(len_sz == offset); - struct_pack::serialize_to( - (char *)buffer.data(), len_sz, header); - return buffer; - } - - template - static rpc_result handle_response_buffer(std::string_view buffer, - uint8_t rpc_errc, bool &has_error, - uint64_t client_id) { - rpc_return_type_t ret; - struct_pack::err_code ec; - rpc_error err; - if (rpc_errc == 0) - AS_LIKELY { - ec = struct_pack::deserialize_to(ret, buffer); - if SP_UNLIKELY (ec) { - if constexpr (requires { std::get<0>(ret); }) { - constexpr auto size = std::tuple_size_v; - if constexpr (size > 1) { - if constexpr (struct_pack::get_type_code>>>() == - struct_pack::get_type_code()) { - auto &args_wrapper = std::get<0>(ret); - ec = struct_pack::deserialize_to(args_wrapper, buffer); - } - } - } - if SP_UNLIKELY (ec) { - std::tuple wrapper = ret; - ec = struct_pack::deserialize_to(wrapper, buffer); - } - } - if SP_LIKELY (!ec) { - if constexpr (std::is_same_v) { - return {}; - } - else { - return std::move(ret); - } - } - } - else { - if (rpc_errc != UINT8_MAX) { - err.val() = rpc_errc; - ec = struct_pack::deserialize_to(err.msg, buffer); - if SP_LIKELY (!ec) { - has_error = true; - return rpc_result{unexpect_t{}, std::move(err)}; - } - } - else { - ec = struct_pack::deserialize_to(err, buffer); - if SP_LIKELY (!ec) { - return rpc_result{unexpect_t{}, std::move(err)}; - } - } - } - has_error = true; - // deserialize failed. - ELOG_WARN << "deserilaize rpc result failed" - << ", client_id: " << client_id; - err = {errc::invalid_rpc_result, "failed to deserialize rpc return value"}; - return rpc_result{unexpect_t{}, std::move(err)}; - } - - template - auto get_func_args() { - using First = std::tuple_element_t<0, FuncArgs>; - constexpr bool has_conn_v = requires { typename First::return_type; }; - return util::get_args(); - } - - template - void pack_to_impl(Buffer &buffer, std::size_t offset, Args &&...args) { - struct_pack::serialize_to_with_offset( - buffer, offset, - std::forward((std::forward(args)))...); - } - - template - void pack_to_helper(std::index_sequence, Buffer &buffer, - std::size_t offset, Args &&...args) { - pack_to_impl...>( - buffer, offset, std::forward(args)...); - } - - template - void pack_to(Buffer &buffer, std::size_t offset, Args &&...args) { - using tuple_pack = decltype(get_func_args()); - pack_to_helper( - std::make_index_sequence>{}, buffer, - offset, std::forward(args)...); - } - - struct async_rpc_raw_result_value_type { - resp_body buffer_; - coro_io::data_view attachment; - uint8_t errc_; - }; - - using async_rpc_raw_result = - std::variant; - - struct control_t; - - struct handler_t { - std::unique_ptr timer_; - async_simple::Promise promise_; - coro_io::data_view response_attachment_buffer_; - handler_t(std::unique_ptr &&timer, - async_simple::Promise &&promise, - coro_io::data_view buffer = {}) - : timer_(std::move(timer)), - promise_(std::move(promise)), - response_attachment_buffer_(buffer) {} - coro_io::data_view &get_buffer() { return response_attachment_buffer_; } - void operator()(resp_body &&buffer, uint8_t rpc_errc) { - timer_->cancel(); - promise_.setValue(async_rpc_raw_result{async_rpc_raw_result_value_type{ - std::move(buffer), response_attachment_buffer_, rpc_errc}}); - } - void local_error(std::error_code &ec) { - timer_->cancel(); - promise_.setValue(async_rpc_raw_result{ec}); - } - }; - struct control_t { -#ifdef GENERATE_BENCHMARK_DATA - std::string func_name_; -#endif - bool is_timeout_; - std::atomic has_closed_ = false; - coro_io::ExecutorWrapper<> *executor_; - coro_io::socket_wrapper_t socket_wrapper_; - std::unordered_map response_handler_table_; - resp_body resp_buffer_; - std::atomic recving_cnt_ = 0; - uint64_t client_id = 0; - control_t(coro_io::ExecutorWrapper<> *executor, bool is_timeout, - const std::string &local_ip) - : is_timeout_(is_timeout), - has_closed_(false), - executor_(executor), - socket_wrapper_(executor_, local_ip) {} - }; - - static void close_socket_async( - std::shared_ptr control) { - bool expected = false; - if (!control->has_closed_.compare_exchange_strong(expected, true)) { - return; - } - - ELOG_DEBUG << "client_id " << control->client_id << " close"; - asio::dispatch(control->socket_wrapper_.get_executor()->get_asio_executor(), - [control]() { - - control->socket_wrapper_.close(); - }); - return; - } - - static async_simple::coro::Lazy close_socket( - std::shared_ptr control) { - bool expected = false; - if (!control->has_closed_.compare_exchange_strong(expected, true)) { - co_await coro_io::post( - []() { - }, - control->executor_); // post to control ioc - co_return; - } - co_await coro_io::post( - [control = control.get()]() { - control->socket_wrapper_.close(); - }, - control->executor_); - co_return; - } - -#ifdef UNIT_TEST_INJECT - public: - coro_rpc::err_code sync_connect(const std::string &host, - const std::string &port) { - return async_simple::coro::syncAwait(connect(host, port)); - } - - template - rpc_result())> sync_call(Args &&...args) { - return async_simple::coro::syncAwait( - call(std::forward(args)...)); - } -#endif - private: - template - async_simple::coro::Lazy send_request_for_impl( - Socket& soc, request_config_t& config, uint32_t& id, - coro_io::period_timer& timer, Args&&... args) { - if (control_->has_closed_) - AS_UNLIKELY { - ELOG_ERROR << "client has been closed, please re-connect" - << ", client_id: " << config_.client_id; - co_return rpc_error{errc::io_error, - "client has been closed, please re-connect"}; - } - -#ifdef YLT_ENABLE_SSL - if (!ssl_init_ret_) { - co_return rpc_error{errc::not_connected}; - } -#endif - static_check(); - - if (config.request_timeout_duration->count() >= 0) { - timeout(timer, *config.request_timeout_duration, - "rpc call timer canceled") - .start([](auto &&) { - }); - } - co_return co_await send_impl( - soc, id, - coro_io::data_view{config.request_attachment, - config.request_attachment_gpu_id}, - std::forward(args)...); - } - - static void send_err_response(control_t *controller, std::error_code &errc) { - if (controller->is_timeout_) { - errc = std::make_error_code(std::errc::timed_out); - } - for (auto &e : controller->response_handler_table_) { - e.second.local_error(errc); - } - controller->response_handler_table_.clear(); - } - template - static async_simple::coro::Lazy recv( - std::shared_ptr controller, Socket &socket) { - std::pair ret; - do { - coro_rpc_protocol::resp_header header; - char buffer[coro_rpc_protocol::RESP_HEAD_LEN]; - auto tp = std::chrono::steady_clock::now(); - ret = co_await coro_io::async_read(socket, asio::buffer(buffer)); - [[maybe_unused]] auto ec = struct_pack::deserialize_to< - struct_pack::sp_config::DISABLE_ALL_META_INFO>( - header, std::string_view{buffer, buffer + sizeof(buffer)}); - assert(!ec); - if (ret.first) { - if (ret.first != asio::error::eof) { - ELOG_ERROR << "read rpc head failed, error msg:" - << ret.first.message() - << ". close the socket.value=" << ret.first.value() - << ", cost time = " - << (std::chrono::steady_clock::now() - tp) / - std::chrono::microseconds(1) - << "us" - << ", client_id: " << controller->client_id; - } - break; - } - auto iter = controller->response_handler_table_.find(header.seq_num); - if (iter == controller->response_handler_table_.end()) { - ELOG_ERROR << "unexists request ID: " << header.seq_num - << ". close the socket" - << ", client_id: " << controller->client_id; - break; - } - ELOG_TRACE << "find request ID: " << header.seq_num - << ". start notify response handler" - << ", client_id: " << controller->client_id; - uint32_t body_len = header.length; - struct_pack::detail::resize( - controller->resp_buffer_.read_buf_, - std::max(body_len, sizeof(std::string))); - if (body_len < sizeof(std::string)) { /* this strange code just disable - any SSO optimize so that rpc result - wont point to illegal address*/ - controller->resp_buffer_.read_buf_.resize(body_len); - } - if (header.attach_length == 0) { - ret = co_await coro_io::async_read( - socket, - asio::buffer(controller->resp_buffer_.read_buf_.data(), body_len)); - controller->resp_buffer_.resp_attachment_buf_.clear(); - } - else { - auto &attachment_buffer = iter->second.get_buffer(); - if (attachment_buffer.size() < header.attach_length) { - // allocate attachment buffer - if (attachment_buffer.size()) [[unlikely]] { - ELOG_TRACE << "user's attachment buffer size is too small, instead " - "by inner allocated buffer"; - } - auto &resp_buf = controller->resp_buffer_.resp_attachment_buf_; - int gpu_id = -1; - if constexpr (requires { socket.get_cuda_stream_handler(); }) { - gpu_id = socket.get_gpu_id(); - if (gpu_id >= 0) { - resp_buf = {header.attach_length, gpu_id}; - assert(resp_buf.size() == header.attach_length); - attachment_buffer = {std::span{resp_buf.data(), resp_buf.size()}, - resp_buf.gpu_id()}; - } - } - if (gpu_id < 0) { - auto buffer = resp_buf.get_string(); - assert(buffer != nullptr); - struct_pack::detail::resize( - *buffer, - std::max(header.attach_length, sizeof(std::string))); - attachment_buffer = { - std::span{buffer->data(), header.attach_length}, 0}; - } - } - else if (attachment_buffer.size() > header.attach_length) { - attachment_buffer = { - attachment_buffer.substr(0, header.attach_length), - attachment_buffer.gpu_id()}; - } - [[maybe_unused]] bool is_sended = false; - if constexpr (requires { socket.get_cuda_stream_handler(); }) { - std::array iov{ - coro_io::data_view{ - std::span{controller->resp_buffer_.read_buf_.data(), - body_len}, - 0}, - attachment_buffer}; - ret = co_await coro_io::async_read(socket, iov); - } - else { - std::array iov{ - asio::mutable_buffer{controller->resp_buffer_.read_buf_.data(), - body_len}, - asio::mutable_buffer{attachment_buffer.mutable_data(), - header.attach_length}}; - ret = co_await coro_io::async_read(socket, iov); - } - } - auto cost_time = (std::chrono::steady_clock::now() - tp) / - std::chrono::microseconds(1); - if (ret.first) { - ELOG_ERROR << "read rpc body failed, error msg:" << ret.first.message() - << ". close the socket. cost time = " << cost_time - << "us, request ID: " << header.seq_num - << ", client_id: " << controller->client_id; - break; - } -#ifdef GENERATE_BENCHMARK_DATA - std::ofstream file(benchmark_file_path + controller->func_name_ + ".out", - std::ofstream::binary | std::ofstream::out); - file << std::string_view{(char *)&header, - coro_rpc_protocol::RESP_HEAD_LEN}; - file << controller->resp_buffer_.read_buf_; - file << std::string_view{controller->resp_buffer_.resp_attachment_buf_}; - file.close(); -#endif - ELOG_DEBUG << "recv rpc response, cost time = " << cost_time - << "us, request ID: " << header.seq_num - << ", client_id: " << controller->client_id; - iter->second(std::move(controller->resp_buffer_), header.err_code); - controller->response_handler_table_.erase(iter); - if (controller->response_handler_table_.empty()) { - co_return; - } - } while (true); - close_socket_async(controller); - send_err_response(controller.get(), ret.first); - co_return; - } - - struct recving_guard { - recving_guard(control_t *ctrl) noexcept : ctrl_(ctrl) { - ctrl_->recving_cnt_.fetch_add(1, std::memory_order_release); - } - recving_guard(recving_guard &&o) noexcept : ctrl_(o.ctrl_) { - o.ctrl_ = nullptr; - }; - recving_guard(const recving_guard &) = delete; - void release() noexcept { - if (ctrl_) { - ctrl_->recving_cnt_.fetch_sub(1, std::memory_order_release); - ctrl_ = nullptr; - } - } - - ~recving_guard() noexcept { release(); } - control_t *ctrl_; - }; - - template - static async_simple::coro::Lazy> deserialize_rpc_result( - async_simple::Future future, - std::weak_ptr watcher, recving_guard guard, - uint64_t client_id) { - auto ret_ = co_await std::move(future); - guard.release(); - if (ret_.index() == 1) [[unlikely]] { // local error - auto &ret = std::get<1>(ret_); - if (ret.value() == static_cast(std::errc::operation_canceled) || - ret.value() == static_cast(std::errc::timed_out)) { - co_return coro_rpc::unexpected{ - rpc_error{errc::timed_out, ret.message()}}; - } - else { - co_return coro_rpc::unexpected{ - rpc_error{errc::io_error, ret.message()}}; - } - } - - bool has_error = false; - auto &ret = std::get<0>(ret_); - auto result = handle_response_buffer(ret.buffer_.read_buf_, ret.errc_, - has_error, client_id); - if (has_error) { - if (auto w = watcher.lock(); w) { - close_socket_async(std::move(w)); - } - } - if (result) { - if constexpr (std::is_same_v) { - co_return async_rpc_result{async_rpc_result_value_t{ - std::move(ret.buffer_), ret.attachment}}; - } - else { - co_return async_rpc_result{async_rpc_result_value_t{ - std::move(result.value()), std::move(ret.buffer_), ret.attachment}}; - } - } - else { - co_return coro_rpc::unexpected{result.error()}; - } - } - - public: - template - async_simple::coro::Lazy())>>> - send_request(Args &&...args) { - return send_request(request_config_t{}, std::forward(args)...); - } - - template - async_simple::coro::Lazy())>>> - send_request_with_attachment(std::string_view request_attachment, - Args &&...args) { - return send_request( - request_config_t{.request_attachment = request_attachment}, - std::forward(args)...); - } - - template - async_simple::coro::Lazy())>>> - send_request_for(Duration request_timeout_duration, Args &&...args) { - return send_request(request_config_t{request_timeout_duration}, - std::string_view{}, std::forward(args)...); - } - - private: - template - static async_simple::coro::Lazy> build_failed_rpc_result( - rpc_error err) { - co_return unexpected{err}; - } - - public: - template - async_simple::coro::Lazy())>>> - send_request(request_config_t config, Args &&...args) { - using rpc_return_t = decltype(get_return_type()); - recving_guard guard(control_.get()); - uint32_t id; - if (!config.request_timeout_duration) { - config.request_timeout_duration = config_.request_timeout_duration; - } - assert(config.request_timeout_duration.has_value()); - - auto timer = std::make_unique( - control_->executor_->get_asio_executor()); - auto result = co_await control_->socket_wrapper_.visit([&](auto &socket) { - return send_request_for_impl(socket, config, id, *timer, - std::forward(args)...); - }); - if (!result) { - async_simple::Promise promise; - auto future = promise.getFuture(); - bool is_empty = control_->response_handler_table_.empty(); - auto &&[_, is_ok] = control_->response_handler_table_.try_emplace( - id, std::move(timer), std::move(promise), - coro_io::data_view{config.resp_attachment_buf, - config.resp_attachment_buf_gpu_id}); - if (!is_ok) [[unlikely]] { - close(); - co_return build_failed_rpc_result( - rpc_error{coro_rpc::errc::serial_number_conflict}); - } - else { - if (is_empty) { - control_->socket_wrapper_.visit([control_ = control_](auto &socket) { - recv(control_, socket).start([](auto &&) { - }); - }); - } - co_return deserialize_rpc_result( - std::move(future), std::weak_ptr{control_}, - std::move(guard), config_.client_id); - } - } - else { - co_return build_failed_rpc_result(std::move(result)); - } - } - - uint32_t get_pipeline_size() const noexcept { - return control_->recving_cnt_.load(std::memory_order_acquire); - } - - private: - template - async_simple::coro::Lazy send_impl( - Socket &socket, uint32_t &id, coro_io::data_view req_attachment, - Args &&...args) { - auto buffer = prepare_buffer(id, req_attachment.size(), - std::forward(args)...); - if (buffer.empty()) { - co_return rpc_error{errc::message_too_large}; - } -#ifdef GENERATE_BENCHMARK_DATA - control_->func_name_ = get_func_name(); - std::ofstream file(benchmark_file_path + control_->func_name_ + ".in", - std::ofstream::binary | std::ofstream::out); - file << std::string_view{(char *)buffer.data(), buffer.size()}; - file.close(); -#endif - std::pair ret; - auto tp = std::chrono::steady_clock::now(); - ELOG_TRACE << "rpc request send start, client_id: " << config_.client_id - << ", request ID: " << id; -#ifdef UNIT_TEST_INJECT - if (g_action == inject_action::client_send_bad_header) { - buffer[0] = (std::byte)(uint8_t(buffer[0]) + 1); - } - if (g_action == inject_action::client_close_socket_after_send_header) { - ret = co_await coro_io::async_write( - socket, asio::buffer(buffer.data(), coro_rpc_protocol::REQ_HEAD_LEN)); - ELOG_INFO << "client_id " << config_.client_id << " close socket" - << ", request ID: " << id; - close(); - co_return rpc_error{errc::io_error, ret.first.message()}; - } - else if (g_action == - inject_action::client_close_socket_after_send_partial_header) { - ret = co_await coro_io::async_write( - socket, - asio::buffer(buffer.data(), coro_rpc_protocol::REQ_HEAD_LEN - 1)); - ELOG_INFO << "client_id " << config_.client_id << " close socket" - << ", request ID: " << id; - close(); - co_return rpc_error{errc::io_error, ret.first.message()}; - } - else if (g_action == - inject_action::client_shutdown_socket_after_send_header) { - ret = co_await coro_io::async_write( - socket, asio::buffer(buffer.data(), coro_rpc_protocol::REQ_HEAD_LEN)); - ELOG_INFO << "client_id " << config_.client_id << " shutdown" - << ", request ID: " << id; - if constexpr (std::is_same_v) { - socket.shutdown(asio::ip::tcp::socket::shutdown_send); - } -#ifdef YLT_ENABLE_SSL - else if constexpr (std::is_same_v>) { - socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_send); - } -#endif - else { - socket.close(); - } - co_return rpc_error{errc::io_error, ret.first.message()}; - } - else { -#endif - while (true) { - bool expected = false; - if (write_mutex_.compare_exchange_weak(expected, true)) { - break; - } - // switch to executor thread - co_await coro_io::post( - []() { - }, - control_->executor_); - } - if (req_attachment.empty()) { - ret = co_await coro_io::async_write( - socket, asio::buffer(buffer.data(), buffer.size())); - } - else { - if constexpr (requires { socket.get_cuda_stream_handler(); }) { - std::array iov{ - coro_io::data_view{ - std::string_view{(char *)buffer.data(), buffer.size()}, -1}, - req_attachment}; - ret = co_await coro_io::async_write(socket, iov); - } - else { - std::array iov{ - asio::const_buffer{buffer.data(), buffer.size()}, - asio::const_buffer{req_attachment.data(), req_attachment.size()}}; - ret = co_await coro_io::async_write(socket, iov); - } - } - write_mutex_ = false; -#ifdef UNIT_TEST_INJECT - } -#endif -#ifdef UNIT_TEST_INJECT - if (g_action == inject_action::force_inject_client_write_data_timeout) { - control_->is_timeout_ = true; - } -#endif -#ifdef UNIT_TEST_INJECT - if (g_action == inject_action::client_close_socket_after_send_payload) { - ELOG_INFO << "client_id " << config_.client_id - << " client_close_socket_after_send_payload" - << ", request ID: " << id; - close(); - co_return rpc_error{errc::io_error, ret.first.message()}; - } -#endif - if (ret.first) { - close(); - if (control_->is_timeout_) { - co_return rpc_error{errc::timed_out}; - } - else { - ELOG_ERROR << "write error: " << ret.first.value() << ", " - << ret.first.message() - << ", client_id: " << config_.client_id << ", cost time = " - << (std::chrono::steady_clock::now() - tp) / - std::chrono::microseconds(1) - << "us" - << ", request ID: " << id; - co_return rpc_error{errc::io_error, ret.first.message()}; - } - } - ELOG_TRACE << "rpc request send over, client_id: " << config_.client_id - << ", cost time = " - << (std::chrono::steady_clock::now() - tp) / - std::chrono::microseconds(1) - << "us" - << ", request ID: " << id; - co_return rpc_error{}; - } - - private: - bool should_reset_ = false; - async_simple::coro::Mutex connect_mutex_; - std::atomic write_mutex_ = false; - std::atomic request_id_{0}; - std::unique_ptr timer_; - std::shared_ptr control_; - std::vector endpoints_; - coro_io::data_view req_attachment_, resp_attachment_, resp_attachment_buffer_; - config config_; - constexpr static std::size_t default_read_buf_size_ = 256; -#ifdef YLT_ENABLE_SSL - asio::ssl::context ssl_ctx_{asio::ssl::context::sslv23}; - bool ssl_init_ret_ = true; -#endif - std::chrono::time_point create_tp_; -}; - -} // namespace coro_rpc +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asio/buffer.hpp" +#include "asio/dispatch.hpp" +#include "asio/registered_buffer.hpp" +#include "async_simple/Executor.h" +#include "async_simple/Promise.h" +#include "async_simple/coro/Mutex.h" +#include "async_simple/coro/SpinLock.h" +#include "common_service.hpp" +#include "context.hpp" +#include "expected.hpp" +#include "protocol/coro_rpc_protocol.hpp" +#include "ylt/coro_io/coro_io.hpp" +#ifdef YLT_ENABLE_IBV +#include "ylt/coro_io/ibverbs/ib_socket.hpp" +#endif +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/coro_io/socket_wrapper.hpp" +#include "ylt/coro_rpc/impl/errno.h" +#include "ylt/struct_pack.hpp" +#include "ylt/struct_pack/reflection.hpp" +#include "ylt/struct_pack/util.h" +#include "ylt/util/function_name.h" +#include "ylt/util/type_traits.h" +#include "ylt/util/utils.hpp" +#ifdef UNIT_TEST_INJECT +#include "inject_action.hpp" +#endif + +#ifdef GENERATE_BENCHMARK_DATA +#include +#endif +namespace coro_io { +template +class client_pool; +} + +namespace coro_rpc { + +struct request_config_t { + std::optional request_timeout_duration; + std::string_view request_attachment; + std::span resp_attachment_buf; +}; + +#ifdef GENERATE_BENCHMARK_DATA +std::string benchmark_file_path = "./"; +#endif + +class coro_connection; + +template +struct rpc_return_type { + using type = T; +}; +template <> +struct rpc_return_type { + using type = std::monostate; +}; + +struct resp_body { + std::string read_buf_; + std::string resp_attachment_buf_; +}; +namespace detail { +struct async_rpc_result_base { + private: + resp_body buffer_; + std::string_view attachment_; + + public: + async_rpc_result_base() = default; + async_rpc_result_base(resp_body &&buffer, std::string_view attachment) + : buffer_(std::move(buffer)), attachment_(attachment) {} + std::string_view get_attachment() const noexcept { return attachment_; } + bool is_attachment_in_external_buf() const noexcept { + return buffer_.resp_attachment_buf_.data() == attachment_.data(); + } + resp_body release_buffer() { return std::move(buffer_); } +}; +} // namespace detail + +template +struct async_rpc_result_value_t : public detail::async_rpc_result_base { + private: + T result_; + + public: + async_rpc_result_value_t(T &&result, resp_body &&buffer, + std::string_view attachment) + : result_(std::move(result)), + async_rpc_result_base(std::move(buffer), attachment) {} + async_rpc_result_value_t(T &&result) : result_(std::move(result)) {} + T &result() noexcept { return result_; } + const T &result() const noexcept { return result_; } +}; + +template <> +struct async_rpc_result_value_t : public detail::async_rpc_result_base { + using async_rpc_result_base::async_rpc_result_base; +}; + +template +using async_rpc_result = expected, rpc_error>; + +template +using rpc_return_type_t = typename rpc_return_type::type; +/*! + * ```cpp + * #include + * + * using namespace coro_rpc; + * using namespace async_simple::coro; + * + * Lazy show_rpc_call(coro_rpc_client &client) { + * auto ec = co_await client.connect("127.0.0.1", "8801"); + * assert(!ec); + * auto result = co_await client.call(); + * if (!result) { + * std::cout << "err: " << result.error().msg << std::endl; + * } + * assert(result.value() == "hello coro_rpc"s); + * } + * + * int main() { + * coro_rpc_client client; + * syncAwait(show_rpc_call(client)); + * } + * ``` + */ +class coro_rpc_client { + using coro_rpc_protocol = coro_rpc::protocol::coro_rpc_protocol; + + public: + const inline static rpc_error connect_error = {errc::io_error, + "client has been closed"}; + struct tcp_config { + bool enable_tcp_no_delay = true; + }; + +#ifdef YLT_ENABLE_SSL + struct tcp_with_ssl_config { + bool enable_tcp_no_delay = true; + std::filesystem::path + ssl_cert_path{}; // CA certificate for server verification + std::string ssl_domain{}; + std::filesystem::path + client_cert_file{}; // Client certificate for mutual authentication + std::filesystem::path + client_key_file{}; // Client private key for mutual authentication + }; +#ifdef YLT_ENABLE_NTLS + struct tcp_with_ntls_config { + bool enable_tcp_no_delay = true; + std::filesystem::path base_path{}; + + // TLCP dual certificate configuration (GB/T 38636-2020) + std::filesystem::path sign_cert_path{}; + std::filesystem::path sign_key_path{}; + std::filesystem::path enc_cert_path{}; + std::filesystem::path enc_key_path{}; + + // TLS 1.3 + GM single certificate configuration (RFC 8998) + std::filesystem::path gm_cert_path{}; + std::filesystem::path gm_key_path{}; + + // Common configuration + std::filesystem::path ca_cert_path{}; + std::string ssl_domain{}; + std::string cipher_suites{}; + ntls_mode mode = ntls_mode::tlcp_dual_cert; + bool enable_client_verify = false; + }; +#endif // YLT_ENABLE_NTLS +#endif + struct config { + static inline uint64_t get_global_client_id() { + static std::atomic cid = 0; + return cid.fetch_add(1, std::memory_order::relaxed); + } + uint64_t client_id; + std::chrono::milliseconds connect_timeout_duration; + std::chrono::milliseconds request_timeout_duration; + std::string host; + std::string port; + std::string local_ip; + std::variant + socket_config; + config() + : client_id(get_global_client_id()), + connect_timeout_duration(std::chrono::seconds{30}), + request_timeout_duration(std::chrono::seconds{30}), + host(), + port(), + socket_config(tcp_config{}) {} + config(const std::string &loc_ip) : config() { local_ip = loc_ip; } + config(config &&) = default; + config(const config &) = default; + config &operator=(const config &) = default; + config &operator=(config &&) = default; + }; + + static inline const config default_config; + + /*! + * Create client with executor + * @param executor coro_io's executor, default executor is come + */ + coro_rpc_client( + coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor(), + config conf = {}) + : control_(std::make_shared(executor, false, conf.local_ip)), + timer_(std::make_unique( + executor->get_asio_executor())) { + if (!init_config(conf)) [[unlikely]] { + close(); + } + } + + coro_rpc_client(const std::string &local_ip) + : coro_rpc_client(coro_io::get_global_executor(), config(local_ip)) {} + + std::string_view get_host() const { return config_.host; } + + std::string_view get_port() const { return config_.port; } + + config &get_config() { return config_; } + + [[nodiscard]] bool init_socket_wrapper(const tcp_config &config) { + return control_->socket_wrapper_.init_client(config.enable_tcp_no_delay); + } +#ifdef YLT_ENABLE_IBV + [[nodiscard]] bool init_socket_wrapper( + const coro_io::ib_socket_t::config_t &config) { + return control_->socket_wrapper_.init_client(config); + } +#endif +#ifdef YLT_ENABLE_SSL + [[nodiscard]] bool init_socket_wrapper(const tcp_with_ssl_config &config) { + try { + ssl_init_ret_ = false; + ELOG_INFO << "init ssl: " << config.ssl_domain; + auto &cert_file = config.ssl_cert_path; + ELOG_INFO << "current path: " << std::filesystem::current_path().string(); + + // Set lower security level for test certificates (OpenSSL 3.0 compatibility) + SSL_CTX_set_security_level(ssl_ctx_.native_handle(), 0); + + if (file_exists(cert_file)) { + ELOG_INFO << "load " << cert_file.string(); + ssl_ctx_.load_verify_file(cert_file.string()); + } + else { + ELOG_INFO << "no certificate file " << cert_file.string(); + return ssl_init_ret_; + } + + // Load client certificate and key for mutual authentication + if (!config.client_cert_file.empty() || !config.client_key_file.empty()) { + // Check if both certificate and key are provided + if (config.client_cert_file.empty() || config.client_key_file.empty()) { + ELOG_ERROR << "Both client certificate and key must be provided for " + "mutual authentication"; + return ssl_init_ret_; + } + + if (file_exists(config.client_cert_file)) { + ELOG_INFO << "load client certificate: " + << config.client_cert_file.string(); + ssl_ctx_.use_certificate_chain_file(config.client_cert_file.string()); + } + else { + ELOG_ERROR << "client certificate file not found: " + << config.client_cert_file.string(); + return ssl_init_ret_; + } + + if (file_exists(config.client_key_file)) { + ELOG_INFO << "load client private key: " + << config.client_key_file.string(); + ssl_ctx_.use_private_key_file(config.client_key_file.string(), + asio::ssl::context::pem); + } + else { + ELOG_ERROR << "client key file not found: " + << config.client_key_file.string(); + return ssl_init_ret_; + } + ELOG_INFO << "client certificate loaded for mutual authentication"; + } + + ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); + // Set hostname verification for DNS names (skip for IP addresses) + if (config.ssl_domain != "127.0.0.1" && config.ssl_domain != "localhost") { + ssl_ctx_.set_verify_callback( + asio::ssl::host_name_verification(config.ssl_domain)); + } + auto init_result = control_->socket_wrapper_.init_client( + ssl_ctx_, config.enable_tcp_no_delay); + if (!init_result) { + return false; + } + ssl_init_ret_ = true; + } catch (const std::exception &e) { + ELOG_ERROR << "init ssl failed: " << e.what(); + } + return ssl_init_ret_; + } +#ifdef YLT_ENABLE_NTLS + [[nodiscard]] bool init_socket_wrapper(const tcp_with_ntls_config &config) { + try { + ssl_init_ret_ = false; + ELOG_INFO << "init NTLS: " << config.ssl_domain; + + // Configure based on NTLS mode + if (config.mode == ntls_mode::tls13_single_cert) { + // Create SSL context with TLCP server method + ssl_ctx_ = asio::ssl::context(SSL_CTX_new(TLS_method())); + + // Enable strict SM TLS 1.3 (Tongsuo) + SSL_CTX_enable_sm_tls13_strict(ssl_ctx_.native_handle()); + // RFC 8998 TLS 1.3 + GM single certificate mode + ELOG_INFO + << "Configuring RFC 8998 TLS 1.3 + GM single certificate mode"; + + // Set TLS 1.3 version + if (SSL_CTX_set_min_proto_version(ssl_ctx_.native_handle(), + TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set minimum TLS version to 1.3"; + return false; + } + if (SSL_CTX_set_max_proto_version(ssl_ctx_.native_handle(), + TLS1_3_VERSION) != 1) { + ELOG_ERROR << "Failed to set maximum TLS version to 1.3"; + return false; + } + + // Set TLS 1.3 GM cipher suites + std::string cipher_suites = config.cipher_suites.empty() + ? "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3" + : config.cipher_suites; + if (SSL_CTX_set_ciphersuites(ssl_ctx_.native_handle(), + cipher_suites.c_str()) != 1) { + ELOG_ERROR << "Failed to set TLS 1.3 GM cipher suites: " + << cipher_suites; + return false; + } + ELOG_INFO << "TLS 1.3 GM cipher suites set to: " << cipher_suites; + + //// Set supported curves for SM2 + // if (SSL_CTX_set1_curves_list(ssl_ctx_.native_handle(), + // "SM2:X25519:prime256v1") != 1) { + // ELOG_ERROR << "Failed to set SM2 curves"; + // return false; + //} + } + else { + // Create SSL context with TLCP client method for NTLS + ssl_ctx_ = asio::ssl::context(SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode - Tongsuo will handle protocol version automatically + SSL_CTX_enable_ntls(ssl_ctx_.native_handle()); + ELOG_INFO << "NTLS mode enabled successfully"; + + // GB/T 38636-2020 TLCP dual certificate mode (default) + ELOG_INFO << "Configuring GB/T 38636-2020 TLCP dual certificate mode"; + + // Set TLCP cipher suites for NTLS (SM2/SM3/SM4) + std::string cipher_suites = + config.cipher_suites.empty() + ? "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3" + : config.cipher_suites; + if (!SSL_CTX_set_cipher_list(ssl_ctx_.native_handle(), + cipher_suites.c_str())) { + ELOG_WARN << "failed to set TLCP cipher suites: " << cipher_suites + << ", using default"; + } + else { + ELOG_INFO << "TLCP cipher suites set to: " << cipher_suites; + } + } + // Load certificates based on NTLS mode + if (config.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + if (config.enable_client_verify) { + ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; + if (config.gm_cert_path.empty() || + !file_exists(config.gm_cert_path) || config.gm_key_path.empty() || + !file_exists(config.gm_key_path)) { + ELOG_ERROR + << "client verify enabled, but GM cert or key file is missing"; + return false; + } + } + + // Load single GM certificate and key + if (!config.gm_cert_path.empty() && file_exists(config.gm_cert_path)) { + ELOG_INFO << "load GM certificate " << config.gm_cert_path.string(); + if (!SSL_CTX_use_certificate_file( + ssl_ctx_.native_handle(), + config.gm_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load GM certificate"; + return false; + } + } + + if (!config.gm_key_path.empty() && file_exists(config.gm_key_path)) { + ELOG_INFO << "load GM private key " << config.gm_key_path.string(); + if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_.native_handle(), + config.gm_key_path.string().c_str(), + SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load GM private key"; + return false; + } + } + } + else { + // GB/T 38636-2020 TLCP dual certificate mode + if (config.enable_client_verify) { + ELOG_INFO << "enable_client_verify: " << config.enable_client_verify; + if (config.sign_cert_path.empty() || + !file_exists(config.sign_cert_path) || + config.sign_key_path.empty() || + !file_exists(config.sign_key_path) || + config.enc_cert_path.empty() || + !file_exists(config.enc_cert_path) || + config.enc_key_path.empty() || + !file_exists(config.enc_key_path)) { + ELOG_ERROR + << "client verify enabled, but cert or key file is missing"; + return false; + } + } + + // Load dual certificates if provided + if (!config.sign_cert_path.empty() && + file_exists(config.sign_cert_path)) { + ELOG_INFO << "load SM2 signing cert " + << config.sign_cert_path.string(); + if (!SSL_CTX_use_sign_certificate_file( + ssl_ctx_.native_handle(), + config.sign_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 signing certificate"; + return false; + } + if (!config.sign_key_path.empty() && + file_exists(config.sign_key_path)) { + ELOG_INFO << "load SM2 signing private key " + << config.sign_key_path.string(); + if (!SSL_CTX_use_sign_PrivateKey_file( + ssl_ctx_.native_handle(), + config.sign_key_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 signing private key"; + return false; + } + } + } + + if (!config.enc_cert_path.empty() && + file_exists(config.enc_cert_path)) { + ELOG_INFO << "load SM2 encryption cert " + << config.enc_cert_path.string(); + if (!SSL_CTX_use_enc_certificate_file( + ssl_ctx_.native_handle(), + config.enc_cert_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 encryption certificate"; + return false; + } + if (!config.enc_key_path.empty() && + file_exists(config.enc_key_path)) { + ELOG_INFO << "load SM2 encryption private key " + << config.enc_key_path.string(); + if (!SSL_CTX_use_enc_PrivateKey_file( + ssl_ctx_.native_handle(), + config.enc_key_path.string().c_str(), SSL_FILETYPE_PEM)) { + ELOG_ERROR << "failed to load SM2 encryption private key"; + return false; + } + } + } + } + + // Load CA certificate if provided + if (!config.ca_cert_path.empty() && file_exists(config.ca_cert_path)) { + ELOG_INFO << "load CA cert " << config.ca_cert_path.string(); + if (!SSL_CTX_load_verify_locations(ssl_ctx_.native_handle(), + config.ca_cert_path.string().c_str(), + nullptr)) { + ELOG_WARN << "failed to load CA certificate"; + } + } + + // Set verification mode - use same approach as HTTP client + if (config.enable_client_verify) { + ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); + // Note: Skip host_name_verification for NTLS as it may not be + // compatible The server certificate will still be verified against CA + // ssl_ctx_.set_verify_callback( + // asio::ssl::host_name_verification(config.ssl_domain)); + } + else { + ssl_ctx_.set_verify_mode(asio::ssl::verify_none); + } + + auto init_result = control_->socket_wrapper_.init_client( + ssl_ctx_, config.enable_tcp_no_delay); + if (!init_result) { + return false; + } + ssl_init_ret_ = true; + + ELOG_INFO << "NTLS client context initialized successfully - protocol " + "version will be negotiated automatically"; + } catch (const std::exception &e) { + ELOG_ERROR << "init NTLS failed: " << e.what(); + } + return ssl_init_ret_; + } +#endif // YLT_ENABLE_NTLS +#endif + [[nodiscard]] bool init_config(const config &conf) { + create_tp_ = std::chrono::steady_clock::now(); + config_ = conf; + control_->socket_wrapper_.set_local_ip(config_.local_ip); + control_->client_id = conf.client_id; + return std::visit( + [this](auto &socket_config) { + return init_socket_wrapper(socket_config); + }, + conf.socket_config); + }; + + auto get_create_time_point() const noexcept { return create_tp_; } + /*! + * Check the client closed or not + * + * @return true if client closed, otherwise false. + */ + [[nodiscard]] bool has_closed() const noexcept { + return control_->has_closed_; + } + + /*! + * Connect server + * + * If socket hasn't been closed, it will be closed first then connect to + * server, else the client will connect to server directly + * + * @param host server address + * @param port server port + * @param connect_timeout_duration RPC call timeout seconds + * @param eps endpoints of resolve result. if eps is not nullptr and vector is + * empty, it will return the endpoints that, else if vector is not empty, it + * will use the eps to skill resolve and connect to server directly. + * @return error code + */ + [[nodiscard]] async_simple::coro::Lazy connect( + std::string host, std::string port, + std::chrono::steady_clock::duration connect_timeout_duration, + std::vector *eps = nullptr) { + auto lock_ok = connect_mutex_.tryLock(); + if (!lock_ok) { + co_await connect_mutex_.coScopedLock(); + co_return err_code{}; + // do nothing, someone has reconnect the client + } + if (!host.empty()) { + if (host.front() == '[') { // for ipv6 + if (host.size() > 2) + host = host.substr(1, host.size() - 2); + } + config_.host = std::move(host); + } + + if (!port.empty()) + config_.port = std::move(port); + + if (!control_->socket_wrapper_.init_ok()) { + co_return coro_rpc::err_code(coro_rpc::errc::not_connected); + } + auto ret = co_await control_->socket_wrapper_.visit([&, + this](auto &socket) { + return connect_impl(socket, + std::chrono::duration_cast( + connect_timeout_duration), + eps); + }); + connect_mutex_.unlock(); + co_return std::move(ret); + } + [[nodiscard]] async_simple::coro::Lazy connect( + std::string_view address, + std::chrono::steady_clock::duration connect_timeout_duration, + std::vector *eps = nullptr) { + auto pos = address.rfind(':'); + std::string host(address.substr(0, pos)); + std::string port(address.substr(pos + 1)); + + return connect(std::move(host), std::move(port), connect_timeout_duration, + eps); + } + + [[nodiscard]] async_simple::coro::Lazy connect( + std::string host, std::string port, + std::vector *eps = nullptr) { + return connect(std::move(host), std::move(port), + config_.connect_timeout_duration, eps); + } + + [[nodiscard]] async_simple::coro::Lazy connect( + std::string_view address, + std::vector *eps = nullptr) { + auto pos = address.rfind(':'); + std::string host(address.substr(0, pos)); + std::string port(address.substr(pos + 1)); + + return connect(std::move(host), std::move(port), + config_.connect_timeout_duration, eps); + } + +#ifdef YLT_ENABLE_SSL + [[nodiscard]] bool init_ssl(std::string_view cert_base_path, + std::string_view cert_file_name, + std::string_view domain = "localhost") { + std::string ssl_domain = std::string{domain}; + std::string ssl_cert_path = + std::filesystem::path(cert_base_path).append(cert_file_name).string(); + if (config_.socket_config.index() != 1) { + config_.socket_config = + tcp_with_ssl_config{.ssl_cert_path = std::move(ssl_cert_path), + .ssl_domain = std::move(ssl_domain)}; + } + else { + auto &conf = std::get(config_.socket_config); + conf.ssl_cert_path = std::move(ssl_cert_path); + conf.ssl_domain = domain = std::move(ssl_domain); + } + return init_socket_wrapper( + std::get(config_.socket_config)); + } + + /*! + * Initialize SSL with client certificate for mutual authentication + * @param cert_base_path Base path for certificate files + * @param cert_file_name CA certificate file name for server verification + * @param client_cert_file Client certificate file name for mutual + * authentication + * @param client_key_file Client private key file name for mutual + * authentication + * @param domain Server domain name + * @return true if initialization successful + */ + [[nodiscard]] bool init_ssl(std::string_view cert_base_path, + std::string_view cert_file_name, + std::string_view client_cert_file, + std::string_view client_key_file, + std::string_view domain = "localhost") { + std::string ssl_domain = std::string{domain}; + std::string ssl_cert_path = + std::filesystem::path(cert_base_path).append(cert_file_name).string(); + std::string ssl_client_cert_path = + std::filesystem::path(cert_base_path).append(client_cert_file).string(); + std::string ssl_client_key_path = + std::filesystem::path(cert_base_path).append(client_key_file).string(); + + if (config_.socket_config.index() != 1) { + config_.socket_config = tcp_with_ssl_config{ + .ssl_cert_path = std::move(ssl_cert_path), + .ssl_domain = std::move(ssl_domain), + .client_cert_file = std::move(ssl_client_cert_path), + .client_key_file = std::move(ssl_client_key_path)}; + } + else { + auto &conf = std::get(config_.socket_config); + conf.ssl_cert_path = std::move(ssl_cert_path); + conf.ssl_domain = std::move(ssl_domain); + conf.client_cert_file = std::move(ssl_client_cert_path); + conf.client_key_file = std::move(ssl_client_key_path); + } + return init_socket_wrapper( + std::get(config_.socket_config)); + } +#ifdef YLT_ENABLE_NTLS + [[nodiscard]] bool init_ntls(const ssl_ntls_configure &conf) { + if (conf.mode == ntls_mode::tls13_single_cert) { + // RFC 8998 TLS 1.3 + GM single certificate mode + if (config_.socket_config.index() != 2) { + config_.socket_config = tcp_with_ntls_config{ + .enable_tcp_no_delay = true, + .base_path = conf.base_path, + .gm_cert_path = + std::filesystem::path(conf.base_path).append(conf.gm_cert_file), + .gm_key_path = + std::filesystem::path(conf.base_path).append(conf.gm_key_file), + .ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file), + .ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name, + .cipher_suites = conf.cipher_suites, + .mode = ntls_mode::tls13_single_cert, + .enable_client_verify = conf.enable_client_verify}; + } + else { + auto &config = std::get(config_.socket_config); + config.base_path = conf.base_path; + config.gm_cert_path = + std::filesystem::path(conf.base_path).append(conf.gm_cert_file); + config.gm_key_path = + std::filesystem::path(conf.base_path).append(conf.gm_key_file); + config.ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file); + config.ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name; + config.cipher_suites = conf.cipher_suites; + config.mode = ntls_mode::tls13_single_cert; + config.enable_client_verify = conf.enable_client_verify; + } + } + else { + // GB/T 38636-2020 TLCP dual certificate mode (default) + if (config_.socket_config.index() != 2) { + config_.socket_config = tcp_with_ntls_config{ + .enable_tcp_no_delay = true, + .base_path = conf.base_path, + .sign_cert_path = std::filesystem::path(conf.base_path) + .append(conf.sign_cert_file), + .sign_key_path = std::filesystem::path(conf.base_path) + .append(conf.sign_key_file), + .enc_cert_path = std::filesystem::path(conf.base_path) + .append(conf.enc_cert_file), + .enc_key_path = + std::filesystem::path(conf.base_path).append(conf.enc_key_file), + .ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file), + .ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name, + .cipher_suites = conf.cipher_suites, + .mode = ntls_mode::tlcp_dual_cert, + .enable_client_verify = conf.enable_client_verify}; + } + else { + auto &config = std::get(config_.socket_config); + config.base_path = conf.base_path; + config.sign_cert_path = + std::filesystem::path(conf.base_path).append(conf.sign_cert_file); + config.sign_key_path = + std::filesystem::path(conf.base_path).append(conf.sign_key_file); + config.enc_cert_path = + std::filesystem::path(conf.base_path).append(conf.enc_cert_file); + config.enc_key_path = + std::filesystem::path(conf.base_path).append(conf.enc_key_file); + config.ca_cert_path = conf.ca_cert_file.empty() + ? std::filesystem::path{} + : std::filesystem::path(conf.base_path) + .append(conf.ca_cert_file); + config.ssl_domain = + conf.server_name.empty() ? "localhost" : conf.server_name; + config.cipher_suites = conf.cipher_suites; + config.mode = ntls_mode::tlcp_dual_cert; + config.enable_client_verify = conf.enable_client_verify; + } + } + return init_socket_wrapper( + std::get(config_.socket_config)); + } + + [[nodiscard]] bool init_ntls(std::string_view base_file, + std::string_view sign_cert_file, + std::string_view sign_key_file, + std::string_view enc_cert_file, + std::string_view enc_key_file, + std::string_view ca_cert_file = "", + std::string_view domain = "localhost", + bool enable_client_verify = false) { + std::string ssl_domain = std::string{domain}; + std::string sign_cert_path = + std::filesystem::path(base_file).append(sign_cert_file).string(); + std::string sign_key_path = + std::filesystem::path(base_file).append(sign_key_file).string(); + std::string enc_cert_path = + std::filesystem::path(base_file).append(enc_cert_file).string(); + std::string enc_key_path = + std::filesystem::path(base_file).append(enc_key_file).string(); + std::string ca_cert_path; + + if (!ca_cert_file.empty()) { + ca_cert_path = + std::filesystem::path(base_file).append(ca_cert_file).string(); + } + + if (config_.socket_config.index() != 2) { + config_.socket_config = + tcp_with_ntls_config{.enable_tcp_no_delay = true, + .base_path = std::move(base_file), + .sign_cert_path = std::move(sign_cert_path), + .sign_key_path = std::move(sign_key_path), + .enc_cert_path = std::move(enc_cert_path), + .enc_key_path = std::move(enc_key_path), + .ca_cert_path = std::move(ca_cert_path), + .ssl_domain = std::move(ssl_domain), + .enable_client_verify = enable_client_verify}; + } + else { + auto &conf = std::get(config_.socket_config); + conf.base_path = std::move(base_file); + conf.sign_cert_path = std::move(sign_cert_path); + conf.sign_key_path = std::move(sign_key_path); + conf.enc_cert_path = std::move(enc_cert_path); + conf.enc_key_path = std::move(enc_key_path); + conf.ca_cert_path = std::move(ca_cert_path); + conf.ssl_domain = std::move(ssl_domain); + conf.enable_client_verify = enable_client_verify; + } + return init_socket_wrapper( + std::get(config_.socket_config)); + } +#endif // YLT_ENABLE_NTLS +#endif +#ifdef YLT_ENABLE_IBV + [[nodiscard]] bool init_ibv( + const coro_io::ib_socket_t::config_t &config = {}) { + config_.socket_config = config; + return init_socket_wrapper( + std::get(config_.socket_config)); + } +#endif + + ~coro_rpc_client() { close(); } + + /*! + * Call RPC function with default timeout (30 second) + * + * @tparam func the address of RPC function + * @tparam Args the type of arguments + * @param args RPC function arguments + * @return RPC call result + */ + template + async_simple::coro::Lazy())>> call( + Args &&...args) { + return call( + request_config_t{{}, req_attachment_, resp_attachment_buffer_}, + std::forward(args)...); + } + + /*! + * Call RPC function + * + * Timeout must be set explicitly. + * + * @tparam func the address of RPC function + * @tparam Args the type of arguments + * @param duration RPC call timeout + * @param args RPC function arguments + * @return RPC call result + */ + template + async_simple::coro::Lazy())>> + call_for(auto request_timeout_duration, Args &&...args) { + return call( + request_config_t{request_timeout_duration, req_attachment_, + resp_attachment_buffer_}, + std::forward(args)...); + } + + template + async_simple::coro::Lazy())>> call( + request_config_t config, Args &&...args) { + using return_type = decltype(get_return_type()); + auto async_result = co_await co_await send_request( + std::move(config), std::forward(args)...); + req_attachment_ = {}; + resp_attachment_buffer_ = {}; + if (async_result) { + resp_attachment_ = async_result->get_attachment(); + control_->resp_buffer_ = async_result->release_buffer(); + if constexpr (std::is_same_v) { + co_return expected{}; + } + else { + co_return expected{ + std::move(async_result->result())}; + } + } + else { + co_return expected{ + unexpect_t{}, std::move(async_result.error())}; + } + } + + /*! + * Get inner executor + */ + auto &get_executor() { return *control_->executor_; } + + uint32_t get_client_id() const { return config_.client_id; } + + void close() { close_socket_async(control_); } + + bool set_req_attachment(std::string_view attachment) { + if (attachment.size() > UINT32_MAX) { + ELOG_ERROR << "too large rpc attachment, client_id = " + << config_.client_id; + return false; + } + req_attachment_ = attachment; + return true; + } + void set_resp_attachment_buf(std::span buffer) { + resp_attachment_buffer_ = buffer; + } + + std::string_view get_resp_attachment() const { return resp_attachment_; } + + bool is_resp_attachment_in_external_buf() const { + return resp_attachment_.data() != + control_->resp_buffer_.resp_attachment_buf_.data(); + } + + std::string release_resp_attachment() { + if (!is_resp_attachment_in_external_buf()) { + return std::move(control_->resp_buffer_.resp_attachment_buf_); + } + else { + return {}; + } + } + + template + friend class coro_io::client_pool; + + private: + // the const char * will convert to bool instead of std::string_view + // use this struct to prevent it. + struct is_reconnect_t { + bool value = false; + }; + + async_simple::coro::Lazy reset() { + co_await close_socket(control_); + bool reset_ok = std::visit( + [this](auto &socket_config) { + return init_socket_wrapper(socket_config); + }, + config_.socket_config); + control_->is_timeout_ = false; + control_->has_closed_ = false; + co_return reset_ok; + } + static bool is_ok(coro_rpc::err_code ec) noexcept { return !ec; } + + template + [[nodiscard]] async_simple::coro::Lazy connect_impl( + Socket &soc, std::chrono::milliseconds conn_timeout_dur, + std::vector *eps) { + if (should_reset_) { + auto reset_ok = co_await reset(); + if (!reset_ok) { + co_return errc::not_connected; + } + } + else { + should_reset_ = true; + } +#ifdef YLT_ENABLE_SSL + if (!ssl_init_ret_) { + ELOG_INFO << "ssl_init_ret_: " << ssl_init_ret_ + << ", client_id: " << config_.client_id; + co_return errc::not_connected; + } +#endif + control_->has_closed_ = false; + + ELOG_INFO << "client_id " << config_.client_id << " begin to connect " + << config_.host << ":" << config_.port; + if (conn_timeout_dur.count() >= 0) { + timeout(*this->timer_, conn_timeout_dur, "connect timer canceled") + .start([](auto &&) { + }); + } + std::vector eps_tmp; + if (eps == nullptr) { + eps = &eps_tmp; + } + std::error_code ec; + asio::ip::tcp::resolver::iterator iter; + if (eps->empty()) { + ELOG_TRACE << "start resolve host: " << config_.host << ":" + << config_.port << ", client_id: " << config_.client_id; + std::tie(ec, iter) = co_await coro_io::async_resolve( + control_->executor_, config_.host, config_.port); + if (ec) { + ELOG_WARN << "client_id " << config_.client_id + << " async_resolve failed:" << ec.message(); + co_return errc::not_connected; + } + asio::ip::tcp::resolver::iterator end; + while (iter != end) { + eps->push_back(iter->endpoint()); + ++iter; + } + if (eps->empty()) [[unlikely]] { + co_return errc::not_connected; + } + } + ELOG_TRACE << "start connect to endpoint lists. total endpoint count:" + << eps->size() + << ", the first endpoint is: " << (*eps)[0].address().to_string() + << ":" << std::to_string((*eps)[0].port()) + << ", client_id: " << config_.client_id; + ec = co_await coro_io::async_connect(soc, *eps); + std::error_code ignore_ec; + timer_->cancel(ignore_ec); + if (control_->is_timeout_) { + ELOG_WARN << "client_id " << config_.client_id << " connect timeout"; + co_return errc::timed_out; + } + else if (ec) { + ELOG_WARN << "client_id " << config_.client_id + << " failed:" << ec.message(); + co_return errc::not_connected; + } + ELOG_INFO << "connect successful, remote addr: " + << control_->socket_wrapper_.remote_endpoint() + << ", local addr: " << control_->socket_wrapper_.local_endpoint() + << ", client_id: " << config_.client_id; + + co_return coro_rpc::err_code{}; + }; + + async_simple::coro::Lazy timeout(coro_io::period_timer &timer, + auto duration, std::string err_msg) { + timer.expires_after(duration); + std::weak_ptr socket_watcher = control_; + bool is_timeout = co_await timer.async_await(); + if (!is_timeout) { + co_return false; + } + if (auto self = socket_watcher.lock()) { + self->is_timeout_ = is_timeout; + close_socket_async(self); + co_return true; + } + co_return false; + } + + template + void static_check() { + using Function = decltype(func); + using param_type = util::function_parameters_t; + if constexpr (!std::is_void_v) { + using First = std::tuple_element_t<0, param_type>; + constexpr bool is_conn = requires { typename First::return_type; }; + + if constexpr (std::is_member_function_pointer_v) { + using Self = util::class_type_t; + if constexpr (is_conn) { + static_assert( + util::is_invocable::value, + "called rpc function and arguments are not match"); + } + else { + static_assert(util::is_invocable::value, + "called rpc function and arguments are not match"); + } + } + else { + if constexpr (is_conn) { + static_assert(util::is_invocable::value, + "called rpc function and arguments are not match"); + } + else { + static_assert(util::is_invocable::value, + "called rpc function and arguments are not match"); + } + } + } + else { + if constexpr (std::is_member_function_pointer_v) { + using Self = util::class_type_t; + static_assert(util::is_invocable::value, + "called rpc function and arguments are not match"); + } + else { + static_assert(util::is_invocable::value, + "called rpc function and arguments are not match"); + } + } + } + + /* + * buffer layout + * ┌────────────────┬────────────────┐ + * │req_header │args │ + * ├────────────────┼────────────────┤ + * │REQ_HEADER_LEN │variable length │ + * └────────────────┴────────────────┘ + */ + template + std::vector prepare_buffer(uint32_t &id, + std::size_t attachment_length, + Args &&...args) { + std::vector buffer; + std::size_t offset = coro_rpc_protocol::REQ_HEAD_LEN; + if constexpr (sizeof...(Args) > 0) { + using arg_types = util::function_parameters_t; + pack_to(buffer, offset, std::forward(args)...); + } + else { + buffer.resize(offset); + } + + coro_rpc_protocol::req_header header{}; + + header.magic = coro_rpc_protocol::magic_number; + header.function_id = func_id(); + header.attach_length = attachment_length; + id = request_id_++; + ELOG_TRACE << "call rpc function name: " << get_func_name() + << ", send request ID: " << id + << ", client_id: " << config_.client_id; + header.seq_num = id; + +#ifdef UNIT_TEST_INJECT + if (g_action == inject_action::client_send_bad_magic_num) { + header.magic = coro_rpc_protocol::magic_number + 1; + } + if (g_action == inject_action::client_send_header_length_0) { + header.length = 0; + } + else { +#endif + auto sz = buffer.size() - coro_rpc_protocol::REQ_HEAD_LEN; + if (sz > UINT32_MAX) { + ELOG_ERROR << "too large rpc body" + << ", client_id: " << config_.client_id; + return {}; + } + header.length = sz; +#ifdef UNIT_TEST_INJECT + } +#endif + auto len_sz = struct_pack::get_needed_size< + struct_pack::sp_config::DISABLE_ALL_META_INFO>(header); + assert(len_sz == offset); + struct_pack::serialize_to( + (char *)buffer.data(), len_sz, header); + return buffer; + } + + template + static rpc_result handle_response_buffer(std::string_view buffer, + uint8_t rpc_errc, bool &has_error, + uint64_t client_id) { + rpc_return_type_t ret; + struct_pack::err_code ec; + rpc_error err; + if (rpc_errc == 0) + AS_LIKELY { + ec = struct_pack::deserialize_to(ret, buffer); + if SP_UNLIKELY (ec) { + if constexpr (requires { std::get<0>(ret); }) { + constexpr auto size = std::tuple_size_v; + if constexpr (size > 1) { + if constexpr (struct_pack::get_type_code>>>() == + struct_pack::get_type_code()) { + auto &args_wrapper = std::get<0>(ret); + ec = struct_pack::deserialize_to(args_wrapper, buffer); + } + } + } + if SP_UNLIKELY (ec) { + std::tuple wrapper = ret; + ec = struct_pack::deserialize_to(wrapper, buffer); + } + } + if SP_LIKELY (!ec) { + if constexpr (std::is_same_v) { + return {}; + } + else { + return std::move(ret); + } + } + } + else { + if (rpc_errc != UINT8_MAX) { + err.val() = rpc_errc; + ec = struct_pack::deserialize_to(err.msg, buffer); + if SP_LIKELY (!ec) { + has_error = true; + return rpc_result{unexpect_t{}, std::move(err)}; + } + } + else { + ec = struct_pack::deserialize_to(err, buffer); + if SP_LIKELY (!ec) { + return rpc_result{unexpect_t{}, std::move(err)}; + } + } + } + has_error = true; + // deserialize failed. + ELOG_WARN << "deserilaize rpc result failed" + << ", client_id: " << client_id; + err = {errc::invalid_rpc_result, "failed to deserialize rpc return value"}; + return rpc_result{unexpect_t{}, std::move(err)}; + } + + template + auto get_func_args() { + using First = std::tuple_element_t<0, FuncArgs>; + constexpr bool has_conn_v = requires { typename First::return_type; }; + return util::get_args(); + } + + template + void pack_to_impl(Buffer &buffer, std::size_t offset, Args &&...args) { + struct_pack::serialize_to_with_offset( + buffer, offset, + std::forward((std::forward(args)))...); + } + + template + void pack_to_helper(std::index_sequence, Buffer &buffer, + std::size_t offset, Args &&...args) { + pack_to_impl...>( + buffer, offset, std::forward(args)...); + } + + template + void pack_to(Buffer &buffer, std::size_t offset, Args &&...args) { + using tuple_pack = decltype(get_func_args()); + pack_to_helper( + std::make_index_sequence>{}, buffer, + offset, std::forward(args)...); + } + + struct async_rpc_raw_result_value_type { + resp_body buffer_; + std::string_view attachment; + uint8_t errc_; + }; + + using async_rpc_raw_result = + std::variant; + + struct control_t; + + struct handler_t { + std::unique_ptr timer_; + async_simple::Promise promise_; + std::span response_attachment_buffer_; + handler_t(std::unique_ptr &&timer, + async_simple::Promise &&promise, + std::span buffer = {}) + : timer_(std::move(timer)), + promise_(std::move(promise)), + response_attachment_buffer_(buffer) {} + std::span &get_buffer() { return response_attachment_buffer_; } + void operator()(resp_body &&buffer, uint8_t rpc_errc) { + timer_->cancel(); + promise_.setValue(async_rpc_raw_result{async_rpc_raw_result_value_type{ + std::move(buffer), + std::string_view{response_attachment_buffer_.data(), + response_attachment_buffer_.size()}, + rpc_errc}}); + } + void local_error(std::error_code &ec) { + timer_->cancel(); + promise_.setValue(async_rpc_raw_result{ec}); + } + }; + struct control_t { +#ifdef GENERATE_BENCHMARK_DATA + std::string func_name_; +#endif + bool is_timeout_; + std::atomic has_closed_ = false; + coro_io::ExecutorWrapper<> *executor_; + coro_io::socket_wrapper_t socket_wrapper_; + std::unordered_map response_handler_table_; + resp_body resp_buffer_; + std::atomic recving_cnt_ = 0; + uint64_t client_id = 0; + control_t(coro_io::ExecutorWrapper<> *executor, bool is_timeout, + const std::string &local_ip) + : is_timeout_(is_timeout), + has_closed_(false), + executor_(executor), + socket_wrapper_(executor_, local_ip) {} + }; + + static void close_socket_async( + std::shared_ptr control) { + bool expected = false; + if (!control->has_closed_.compare_exchange_strong(expected, true)) { + return; + } + + ELOG_DEBUG << "client_id " << control->client_id << " close"; + asio::dispatch(control->socket_wrapper_.get_executor()->get_asio_executor(), + [control]() { + + control->socket_wrapper_.close(); + }); + return; + } + + static async_simple::coro::Lazy close_socket( + std::shared_ptr control) { + bool expected = false; + if (!control->has_closed_.compare_exchange_strong(expected, true)) { + co_await coro_io::post( + []() { + }, + control->executor_); // post to control ioc + co_return; + } + co_await coro_io::post( + [control = control.get()]() { + control->socket_wrapper_.close(); + }, + control->executor_); + co_return; + } + +#ifdef UNIT_TEST_INJECT + public: + coro_rpc::err_code sync_connect(const std::string &host, + const std::string &port) { + return async_simple::coro::syncAwait(connect(host, port)); + } + + template + rpc_result())> sync_call(Args &&...args) { + return async_simple::coro::syncAwait( + call(std::forward(args)...)); + } +#endif + private: + template + async_simple::coro::Lazy send_request_for_impl( + Socket &soc, request_config_t &config, uint32_t &id, + coro_io::period_timer &timer, Args &&...args) { + using R = decltype(get_return_type()); + + if (control_->has_closed_) + AS_UNLIKELY { + ELOG_ERROR << "client has been closed, please re-connect" + << ", client_id: " << config_.client_id; + co_return rpc_error{errc::io_error, + "client has been closed, please re-connect"}; + } + +#ifdef YLT_ENABLE_SSL + if (!ssl_init_ret_) { + co_return rpc_error{errc::not_connected}; + } +#endif + static_check(); + + if (config.request_timeout_duration->count() >= 0) { + timeout(timer, *config.request_timeout_duration, + "rpc call timer canceled") + .start([](auto &&) { + }); + } + co_return co_await send_impl(soc, id, config.request_attachment, + std::forward(args)...); + } + + static void send_err_response(control_t *controller, std::error_code &errc) { + if (controller->is_timeout_) { + errc = std::make_error_code(std::errc::timed_out); + } + for (auto &e : controller->response_handler_table_) { + e.second.local_error(errc); + } + controller->response_handler_table_.clear(); + } + template + static async_simple::coro::Lazy recv( + std::shared_ptr controller, Socket &socket) { + std::pair ret; + do { + coro_rpc_protocol::resp_header header; + char buffer[coro_rpc_protocol::RESP_HEAD_LEN]; + auto tp = std::chrono::steady_clock::now(); + ret = co_await coro_io::async_read(socket, asio::buffer(buffer)); + auto ec = struct_pack::deserialize_to< + struct_pack::sp_config::DISABLE_ALL_META_INFO>( + header, std::string_view{buffer, buffer + sizeof(buffer)}); + assert(!ec); + if (ret.first) { + if (ret.first != asio::error::eof) { + ELOG_ERROR << "read rpc head failed, error msg:" + << ret.first.message() + << ". close the socket.value=" << ret.first.value() + << ", cost time = " + << (std::chrono::steady_clock::now() - tp) / + std::chrono::microseconds(1) + << "us" + << ", client_id: " << controller->client_id; + } + break; + } + auto iter = controller->response_handler_table_.find(header.seq_num); + if (iter == controller->response_handler_table_.end()) { + ELOG_ERROR << "unexists request ID: " << header.seq_num + << ". close the socket" + << ", client_id: " << controller->client_id; + break; + } + ELOG_TRACE << "find request ID: " << header.seq_num + << ". start notify response handler" + << ", client_id: " << controller->client_id; + uint32_t body_len = header.length; + struct_pack::detail::resize( + controller->resp_buffer_.read_buf_, + std::max(body_len, sizeof(std::string))); + if (body_len < sizeof(std::string)) { /* this strange code just disable + any SSO optimize so that rpc result + wont point to illegal address*/ + controller->resp_buffer_.read_buf_.resize(body_len); + } + if (header.attach_length == 0) { + ret = co_await coro_io::async_read( + socket, + asio::buffer(controller->resp_buffer_.read_buf_.data(), body_len)); + controller->resp_buffer_.resp_attachment_buf_.clear(); + } + else { + std::span &attachment_buffer = iter->second.get_buffer(); + if (attachment_buffer.size() < header.attach_length) { + // allocate attachment buffer + if (attachment_buffer.size()) [[unlikely]] { + ELOG_TRACE << "user's attachment buffer size is too small, instead " + "by inner allocated buffer"; + } + struct_pack::detail::resize( + controller->resp_buffer_.resp_attachment_buf_, + std::max(header.attach_length, sizeof(std::string))); + attachment_buffer = controller->resp_buffer_.resp_attachment_buf_; + } + attachment_buffer = attachment_buffer.subspan(0, header.attach_length); + std::array iov{ + asio::mutable_buffer{controller->resp_buffer_.read_buf_.data(), + body_len}, + asio::mutable_buffer{attachment_buffer.data(), + attachment_buffer.size()}}; + ret = co_await coro_io::async_read(socket, iov); + } + auto cost_time = (std::chrono::steady_clock::now() - tp) / + std::chrono::microseconds(1); + if (ret.first) { + ELOG_ERROR << "read rpc body failed, error msg:" << ret.first.message() + << ". close the socket. cost time = " << cost_time + << "us, request ID: " << header.seq_num + << ", client_id: " << controller->client_id; + break; + } +#ifdef GENERATE_BENCHMARK_DATA + std::ofstream file(benchmark_file_path + controller->func_name_ + ".out", + std::ofstream::binary | std::ofstream::out); + file << std::string_view{(char *)&header, + coro_rpc_protocol::RESP_HEAD_LEN}; + file << controller->resp_buffer_.read_buf_; + file << controller->resp_buffer_.resp_attachment_buf_; + file.close(); +#endif + ELOG_DEBUG << "recv rpc response, cost time = " << cost_time + << "us, request ID: " << header.seq_num + << ", client_id: " << controller->client_id; + iter->second(std::move(controller->resp_buffer_), header.err_code); + controller->response_handler_table_.erase(iter); + if (controller->response_handler_table_.empty()) { + co_return; + } + } while (true); + close_socket_async(controller); + send_err_response(controller.get(), ret.first); + co_return; + } + + struct recving_guard { + recving_guard(control_t *ctrl) noexcept : ctrl_(ctrl) { + ctrl_->recving_cnt_.fetch_add(1, std::memory_order_release); + } + recving_guard(recving_guard &&o) noexcept : ctrl_(o.ctrl_) { + o.ctrl_ = nullptr; + }; + recving_guard(const recving_guard &) = delete; + void release() noexcept { + if (ctrl_) { + ctrl_->recving_cnt_.fetch_sub(1, std::memory_order_release); + ctrl_ = nullptr; + } + } + + ~recving_guard() noexcept { release(); } + control_t *ctrl_; + }; + + template + static async_simple::coro::Lazy> deserialize_rpc_result( + async_simple::Future future, + std::weak_ptr watcher, recving_guard guard, + uint64_t client_id) { + auto ret_ = co_await std::move(future); + guard.release(); + if (ret_.index() == 1) [[unlikely]] { // local error + auto &ret = std::get<1>(ret_); + if (ret.value() == static_cast(std::errc::operation_canceled) || + ret.value() == static_cast(std::errc::timed_out)) { + co_return coro_rpc::unexpected{ + rpc_error{errc::timed_out, ret.message()}}; + } + else { + co_return coro_rpc::unexpected{ + rpc_error{errc::io_error, ret.message()}}; + } + } + + bool has_error = false; + auto &ret = std::get<0>(ret_); + auto result = handle_response_buffer(ret.buffer_.read_buf_, ret.errc_, + has_error, client_id); + if (has_error) { + if (auto w = watcher.lock(); w) { + close_socket_async(std::move(w)); + } + } + if (result) { + if constexpr (std::is_same_v) { + co_return async_rpc_result{async_rpc_result_value_t{ + std::move(ret.buffer_), ret.attachment}}; + } + else { + co_return async_rpc_result{async_rpc_result_value_t{ + std::move(result.value()), std::move(ret.buffer_), ret.attachment}}; + } + } + else { + co_return coro_rpc::unexpected{result.error()}; + } + } + + public: + template + async_simple::coro::Lazy())>>> + send_request(Args &&...args) { + return send_request(request_config_t{}, std::forward(args)...); + } + + template + async_simple::coro::Lazy())>>> + send_request_with_attachment(std::string_view request_attachment, + Args &&...args) { + return send_request( + request_config_t{.request_attachment = request_attachment}, + std::forward(args)...); + } + + template + async_simple::coro::Lazy())>>> + send_request_for(Duration request_timeout_duration, Args &&...args) { + return send_request(request_config_t{request_timeout_duration}, + std::string_view{}, std::forward(args)...); + } + + private: + template + static async_simple::coro::Lazy> build_failed_rpc_result( + rpc_error err) { + co_return unexpected{err}; + } + + public: + template + async_simple::coro::Lazy())>>> + send_request(request_config_t config, Args &&...args) { + using rpc_return_t = decltype(get_return_type()); + recving_guard guard(control_.get()); + uint32_t id; + if (!config.request_timeout_duration) { + config.request_timeout_duration = config_.request_timeout_duration; + } + assert(config.request_timeout_duration.has_value()); + + auto timer = std::make_unique( + control_->executor_->get_asio_executor()); + auto result = co_await control_->socket_wrapper_.visit([&](auto &socket) { + return send_request_for_impl(socket, config, id, *timer, + std::forward(args)...); + }); + if (!result) { + async_simple::Promise promise; + auto future = promise.getFuture(); + bool is_empty = control_->response_handler_table_.empty(); + auto &&[_, is_ok] = control_->response_handler_table_.try_emplace( + id, std::move(timer), std::move(promise), config.resp_attachment_buf); + if (!is_ok) [[unlikely]] { + close(); + co_return build_failed_rpc_result( + rpc_error{coro_rpc::errc::serial_number_conflict}); + } + else { + if (is_empty) { + control_->socket_wrapper_.visit([control_ = control_](auto &socket) { + recv(control_, socket).start([](auto &&) { + }); + }); + } + co_return deserialize_rpc_result( + std::move(future), std::weak_ptr{control_}, + std::move(guard), config_.client_id); + } + } + else { + co_return build_failed_rpc_result(std::move(result)); + } + } + + uint32_t get_pipeline_size() const noexcept { + return control_->recving_cnt_.load(std::memory_order_acquire); + } + + private: + template + async_simple::coro::Lazy send_impl(Socket &socket, uint32_t &id, + std::string_view req_attachment, + Args &&...args) { + auto buffer = prepare_buffer(id, req_attachment.size(), + std::forward(args)...); + if (buffer.empty()) { + co_return rpc_error{errc::message_too_large}; + } +#ifdef GENERATE_BENCHMARK_DATA + control_->func_name_ = get_func_name(); + std::ofstream file(benchmark_file_path + control_->func_name_ + ".in", + std::ofstream::binary | std::ofstream::out); + file << std::string_view{(char *)buffer.data(), buffer.size()}; + file.close(); +#endif + std::pair ret; + auto tp = std::chrono::steady_clock::now(); + ELOG_TRACE << "rpc request send start, client_id: " << config_.client_id + << ", request ID: " << id; +#ifdef UNIT_TEST_INJECT + if (g_action == inject_action::client_send_bad_header) { + buffer[0] = (std::byte)(uint8_t(buffer[0]) + 1); + } + if (g_action == inject_action::client_close_socket_after_send_header) { + ret = co_await coro_io::async_write( + socket, asio::buffer(buffer.data(), coro_rpc_protocol::REQ_HEAD_LEN)); + ELOG_INFO << "client_id " << config_.client_id << " close socket" + << ", request ID: " << id; + close(); + co_return rpc_error{errc::io_error, ret.first.message()}; + } + else if (g_action == + inject_action::client_close_socket_after_send_partial_header) { + ret = co_await coro_io::async_write( + socket, + asio::buffer(buffer.data(), coro_rpc_protocol::REQ_HEAD_LEN - 1)); + ELOG_INFO << "client_id " << config_.client_id << " close socket" + << ", request ID: " << id; + close(); + co_return rpc_error{errc::io_error, ret.first.message()}; + } + else if (g_action == + inject_action::client_shutdown_socket_after_send_header) { + ret = co_await coro_io::async_write( + socket, asio::buffer(buffer.data(), coro_rpc_protocol::REQ_HEAD_LEN)); + ELOG_INFO << "client_id " << config_.client_id << " shutdown" + << ", request ID: " << id; + if constexpr (std::is_same_v) { + socket.shutdown(asio::ip::tcp::socket::shutdown_send); + } +#ifdef YLT_ENABLE_SSL + else if constexpr (std::is_same_v>) { + socket.lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_send); + } +#endif + else { + socket.close(); + } + co_return rpc_error{errc::io_error, ret.first.message()}; + } + else { +#endif + while (true) { + bool expected = false; + if (write_mutex_.compare_exchange_weak(expected, true)) { + break; + } + // switch to executor thread + co_await coro_io::post( + []() { + }, + control_->executor_); + } + if (req_attachment.empty()) { + ret = co_await coro_io::async_write( + socket, asio::buffer(buffer.data(), buffer.size())); + } + else { + std::array iov{ + asio::const_buffer{buffer.data(), buffer.size()}, + asio::const_buffer{req_attachment.data(), req_attachment.size()}}; + ret = co_await coro_io::async_write(socket, iov); + } + write_mutex_ = false; +#ifdef UNIT_TEST_INJECT + } +#endif +#ifdef UNIT_TEST_INJECT + if (g_action == inject_action::force_inject_client_write_data_timeout) { + control_->is_timeout_ = true; + } +#endif +#ifdef UNIT_TEST_INJECT + if (g_action == inject_action::client_close_socket_after_send_payload) { + ELOG_INFO << "client_id " << config_.client_id + << " client_close_socket_after_send_payload" + << ", request ID: " << id; + close(); + co_return rpc_error{errc::io_error, ret.first.message()}; + } +#endif + if (ret.first) { + close(); + if (control_->is_timeout_) { + co_return rpc_error{errc::timed_out}; + } + else { + ELOG_ERROR << "write error: " << ret.first.value() << ", " + << ret.first.message() + << ", client_id: " << config_.client_id << ", cost time = " + << (std::chrono::steady_clock::now() - tp) / + std::chrono::microseconds(1) + << "us" + << ", request ID: " << id; + co_return rpc_error{errc::io_error, ret.first.message()}; + } + } + ELOG_TRACE << "rpc request send over, client_id: " << config_.client_id + << ", cost time = " + << (std::chrono::steady_clock::now() - tp) / + std::chrono::microseconds(1) + << "us" + << ", request ID: " << id; + co_return rpc_error{}; + } + + private: + bool should_reset_ = false; + async_simple::coro::Mutex connect_mutex_; + std::atomic write_mutex_ = false; + std::atomic request_id_{0}; + std::unique_ptr timer_; + std::shared_ptr control_; + std::vector endpoints_; + std::string_view req_attachment_; + std::span resp_attachment_buffer_; + std::string_view resp_attachment_; + config config_; + constexpr static std::size_t default_read_buf_size_ = 256; +#ifdef YLT_ENABLE_SSL + asio::ssl::context ssl_ctx_{asio::ssl::context::tls}; + bool ssl_init_ret_ = true; +#endif + std::chrono::time_point create_tp_; +}; + +} // namespace coro_rpc diff --git a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp index 2733fe8f8..d422fa550 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp @@ -1,641 +1,641 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asio/ip/tcp.hpp" -#include "async_simple/Common.h" -#include "async_simple/Promise.h" -#include "async_simple/coro/Collect.h" -#include "async_simple/util/move_only_function.h" -#include "common_service.hpp" -#include "coro_connection.hpp" -#include "ylt/coro_io/coro_io.hpp" -#include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/coro_io/server_acceptor.hpp" -#include "ylt/coro_rpc/impl/errno.h" -#include "ylt/coro_rpc/impl/expected.hpp" -namespace coro_rpc { -/*! - * ```cpp - * #include - * inline std::string hello_coro_rpc() { return "hello coro_rpc"; } - * int main(){ - * register_handler(); - * coro_rpc_server server(std::thread::hardware_concurrency(), 9000); - * server.start(); - * return 0; - * } - * ``` - */ - -template -class coro_rpc_server_base { - //!< Server state - enum class stat { - init = 0, // server hasn't started/stopped. - started, // server is started - stop // server is stopping/stopped - }; - - public: - /*! - * TODO: add doc - * @param thread_num the number of io_context. - * @param port the server port to listen. - * @param listen address of server - * @param conn_timeout_duration client connection timeout. 0 for no timeout. - * default no timeout. - * @param is_enable_tcp_no_delay is tcp socket allow - */ - coro_rpc_server_base(size_t thread_num = std::thread::hardware_concurrency(), - unsigned short port = 9001, - std::string address = "0.0.0.0", - std::chrono::steady_clock::duration - conn_timeout_duration = std::chrono::seconds(0), - bool is_enable_tcp_no_delay = true) - : pool_(thread_num), - flag_{stat::init}, - is_enable_tcp_no_delay_(is_enable_tcp_no_delay), - conn_timeout_duration_(conn_timeout_duration) { - acceptors_.push_back( - std::make_unique(address, port)); - } - - coro_rpc_server_base(size_t thread_num, std::string address, - std::chrono::steady_clock::duration - conn_timeout_duration = std::chrono::seconds(0), - bool is_enable_tcp_no_delay = true) - : pool_(thread_num), - flag_{stat::init}, - is_enable_tcp_no_delay_(is_enable_tcp_no_delay), - conn_timeout_duration_(conn_timeout_duration) { - acceptors_.push_back( - std::make_unique(address)); - } - - coro_rpc_server_base( - const server_config& config, - std::vector> acceptors = - {}) - : pool_(config.thread_num), - flag_{stat::init}, - is_enable_tcp_no_delay_(config.is_enable_tcp_no_delay), - conn_timeout_duration_(config.conn_timeout_duration) { -#ifdef YLT_ENABLE_SSL - if (config.ssl_config) { - use_ssl_ = init_ssl_context_helper(context_, config.ssl_config.value()); - } -#ifdef YLT_ENABLE_NTLS - else if (config.ssl_ntls_config) { - use_ssl_ = - init_ntls_context_helper(context_, config.ssl_ntls_config.value()); - } -#endif // YLT_ENABLE_NTLS -#endif -#ifdef YLT_ENABLE_IBV - if (!config.ibv_config.has_value() && !config.ibv_dev_lists.empty()) { - ibv_config_ = coro_io::ib_socket_t::config_t{}; - } - if (config.ibv_config) { - init_ibv(config.ibv_config.value(), std::move(config.ibv_dev_lists)); - } -#endif - if (!acceptors.empty()) { - acceptors_ = std::move(acceptors); - } - else { - acceptors_.push_back(std::make_unique( - config.address, config.port)); - } - } - - ~coro_rpc_server_base() { - ELOG_INFO << "coro_rpc_server will quit"; - stop(); - } - -#ifdef YLT_ENABLE_SSL - void init_ssl(const ssl_configure &conf) { - use_ssl_ = init_ssl_context_helper(context_, conf); - } -#ifdef YLT_ENABLE_NTLS - void init_ntls(const ssl_ntls_configure &conf) { - use_ssl_ = init_ntls_context_helper(context_, conf); - } -#endif // YLT_ENABLE_NTLS -#endif -#ifdef YLT_ENABLE_IBV - void init_ibv( - const coro_io::ib_socket_t::config_t &conf = {}, - std::vector> ibv_dev_lists = {}) { - ibv_config_ = conf; - ibv_dev_lists_ = std::move(ibv_dev_lists); - } -#endif - - /*! - * Start the server in blocking mode - * - * If the port is used, it will return a non-empty error code. - * - * @return error code if start failed, otherwise block until server stop. - */ - [[nodiscard]] coro_rpc::err_code start() noexcept { - return async_start().get(); - } - - private: - async_simple::Future make_error_future( - coro_rpc::err_code &&err) { - async_simple::Promise p; - p.setValue(std::move(err)); - return p.getFuture(); - } - - public: - const std::vector> & - get_acceptors() const noexcept { - return acceptors_; - } - async_simple::Future async_start() noexcept { - { - std::unique_lock lock(start_mtx_); - if (flag_ != stat::init) { - if (flag_ == stat::started) { - ELOG_INFO << "start again"; - } - else if (flag_ == stat::stop) { - ELOG_INFO << "has stoped"; - } - return make_error_future( - coro_rpc::err_code{coro_rpc::errc::server_has_ran}); - } - for (auto &acceptor : acceptors_) { - acceptor->set_io_threads_pool(&pool_); - auto ec = acceptor->listen(); - if (ec != coro_io::listen_errc ::ok) { - switch (ec) { - case coro_io::listen_errc::address_in_used: - errc_ = coro_rpc::err_code{coro_rpc::errc::address_in_used}; - break; - case coro_io::listen_errc::bad_address: - errc_ = coro_rpc::err_code{coro_rpc::errc::bad_address}; - break; - case coro_io::listen_errc::open_error: - errc_ = coro_rpc::err_code{coro_rpc::errc::open_error}; - break; - case coro_io::listen_errc::listen_error: - errc_ = coro_rpc::err_code{coro_rpc::errc::listen_error}; - break; - default: - errc_ = coro_rpc::err_code{coro_rpc::errc::io_error}; - break; - } - } - if (errc_) { - break; - } - } - if (!errc_) { - if constexpr (requires(typename server_config::executor_pool_t &pool) { - pool.run(); - }) { - thd_ = std::thread([this] { - pool_.run(); - }); - } - flag_ = stat::started; - } - else { - flag_ = stat::stop; - } - } - if (!errc_) { - async_simple::Promise promise; - auto future = promise.getFuture(); - accept().start([this, p = std::move(promise)]( - async_simple::Try &&res) mutable { - ELOG_INFO << "server quit!"; - if (res.hasError()) { - try { - std::rethrow_exception(res.getException()); - } catch (const std::exception &e) { - ELOG_ERROR << "server quit with exception: " << e.what(); - } - stop(); - errc_ = coro_rpc::err_code{coro_rpc::errc::io_error}; - p.setValue(errc_); - } - else { - ELOG_ERROR << "server quit with error: " << res.value().message(); - p.setValue(res.value()); - } - }); - return future; - } - else { - return make_error_future(coro_rpc::err_code{errc_}); - } - } - - /*! - * Stop server - * - * Block and wait server stops. - */ - void stop() { - std::unique_lock lock(start_mtx_); - if (flag_ == stat::stop) { - return; - } - - ELOG_INFO << "begin to stop coro_rpc_server"; - - if (flag_ == stat::started) { - for (auto &acceptor : acceptors_) acceptor->close(); - { - std::unique_lock lock(conns_mtx_); - ELOG_INFO << "total connection count: " << conns_.size(); - for (auto &conn_weak : conns_) { - auto conn = conn_weak.second.lock(); - if (conn && !conn->has_closed()) { - conn->async_close(); - } - } - - conns_.clear(); - } - - ELOG_INFO << "wait for server's thread-pool finish all work."; - pool_.stop(); - ELOG_INFO << "server's thread-pool finished."; - } - if (thd_.joinable()) { - thd_.join(); - } - - ELOG_INFO << "stop coro_rpc_server ok."; - flag_ = stat::stop; - } - - /*! - * Get listening port - * @return listening port - */ - uint16_t port() const { return acceptors_[0]->port(); }; - /* - * Get listening address - * @return listening address - */ - std::string_view address() const { return acceptors_[0]->address(); } - coro_rpc::err_code get_errc() const { return errc_; } - - template - void add_subserver( - std::function - dispatcher, - std::unique_ptr... server) { - connection_transfer_ = [dispatcher = std::move(dispatcher), - server = std::make_tuple(std::move(server)...)]( - coro_io::socket_wrapper_t &&socket, - std::string_view magic_number, - int index = -1) mutable { - std::apply( - [&dispatcher, &socket, magic_number](auto &...server) { - dispatcher(std::move(socket), magic_number, *server...); - }, - server); - }; - } - - /*! - * Register RPC service functions (member function) - * - * Before RPC server started, all RPC service functions must be registered. - * All you need to do is fill in the template parameters with the address of - * your own RPC functions. If RPC function is registered twice, the program - * will be terminate with exit code `EXIT_FAILURE`. - * - * Note: All functions must be member functions of the same class. - * - * ```cpp - * class test_class { - * public: - * void plus_one(int val) {} - * std::string get_str(std::string str) { return str; } - * }; - * int main() { - * test_class obj{}; - * // register member functions - * register_handler<&test_class::plus_one, &test_class::get_str>(&obj); - * return 0; - * } - * ``` - * - * @tparam first the address of RPC function. e.g. `&foo::bar` - * @tparam func the address of RPC function. e.g. `&foo::bar` - * @param self the object pointer corresponding to these member functions - */ - - template - void register_handler(util::class_type_t *self) { - router_.template register_handler(self); - } - - template - void register_handler(util::class_type_t *self, - const auto &key) { - router_.template register_handler(self, key); - } - - /*! - * Register RPC service functions (non-member function) - * - * Before RPC server started, all RPC service functions must be registered. - * All you need to do is fill in the template parameters with the address of - * your own RPC functions. If RPC function is registered twice, the program - * will be terminate with exit code `EXIT_FAILURE`. - * - * ```cpp - * // RPC functions (non-member function) - * void hello() {} - * std::string get_str() { return ""; } - * int add(int a, int b) {return a + b; } - * int main() { - * register_handler(); // register one RPC function at once - * register_handler(); // register multiple RPC functions at - * once return 0; - * } - * ``` - * - * @tparam first the address of RPC function. e.g. `foo`, `bar` - * @tparam func the address of RPC function. e.g. `foo`, `bar` - */ - - template - void register_handler() { - router_.template register_handler(); - } - - template - void register_handler(const auto &key) { - router_.template register_handler(key); - } - - auto &get_io_context_pool() noexcept { return pool_; } - - /*! - * Set client filter callback - * @param filter callback function that takes endpoint and returns bool - * true to allow connection, false to reject - */ - void client_filter( - std::function filter) { - client_filter_ = std::move(filter); - } - - private: - async_simple::coro::Lazy accept() { - std::vector> tasks; - acceptors_[0]->address(); - acceptors_[0]->port(); - for (auto &acceptor : acceptors_) { - tasks.emplace_back(accept_impl(*acceptor)); - } - auto results = co_await async_simple::coro::collectAny(std::move(tasks)); - ELOG_INFO << "acceptor:" << acceptors_[results.index()]->address() << ":" - << acceptors_[results.index()]->port() - << " exit by:" << results.value(); - co_return results.value(); - } - - uint64_t get_global_conn_id() { - static std::atomic global_conn_id = 0; - return ++global_conn_id; - } - async_simple::coro::Lazy accept_impl( - coro_io::server_acceptor_base &acceptor) { - ELOG_INFO << "begin to accept looping"; - for (;;) { - auto result = co_await acceptor.accept(); - std::error_code error; -#ifdef UNIT_TEST_INJECT - if (result.has_value()) { - if (g_action == inject_action::force_inject_server_accept_error) { - coro_io::socket_wrapper_t &wrapper = result.value(); - asio::error_code ignored_ec; - wrapper.close(); - g_action = inject_action::nothing; - result = ylt::unexpected{ - make_error_code(std::errc::io_error)}; - // only inject once - } - } -#endif - if (!result.has_value()) { - auto error = result.error(); - if (error == asio::error::operation_aborted) { - ELOG_INFO << "server was canceled:" << error.message(); - } - else { - ELOG_ERROR << "server accept failed:" << error.message(); - } - if (error == asio::error::operation_aborted || - error == asio::error::bad_descriptor) { - co_return coro_rpc::errc::operation_canceled; - } - continue; - } - coro_io::socket_wrapper_t &wrapper = result.value(); - - // Client filter check - if (client_filter_) { - asio::ip::tcp::endpoint remote_endpoint = { - wrapper.remote_endpoint().address, - (uint16_t)wrapper.remote_endpoint().port}; - - if (!client_filter_(remote_endpoint)) { - ELOG_WARN << "Connection from " - << remote_endpoint.address().to_string() - << " rejected by client filter"; - continue; - } - ELOG_INFO << "Connection from " << remote_endpoint.address().to_string() - << " allowed by client filter"; - } - - uint64_t conn_id = get_global_conn_id(); - ELOG_INFO << "new client conn_id " << conn_id << " coming, peer addr[" - << wrapper.remote_endpoint() << "], local addr[" - << wrapper.local_endpoint() << "]"; - - if (auto &socket = wrapper.socket(); socket) { - if (is_enable_tcp_no_delay_) { - std::error_code error; - socket->set_option(asio::ip::tcp::no_delay(true), error); - } -#ifdef YLT_ENABLE_SSL - if (use_ssl_) { - wrapper = coro_io::socket_wrapper_t{std::move(*socket), - wrapper.get_executor(), context_}; - } -#endif - } - auto conn = std::make_shared(std::move(wrapper), - conn_timeout_duration_); - conn->set_quit_callback( - [this](const uint64_t &id) { - std::unique_lock lock(conns_mtx_); - conns_.erase(id); - }, - conn_id); - { - std::unique_lock lock(conns_mtx_); - conns_.emplace(conn_id, conn); - } - ELOG_TRACE << "start new connection, conn_id:" << conn_id; - start_one(std::move(conn)) - .directlyStart( - [id = conn_id, this](async_simple::Try &&res) { - ELOG_INFO << "connection over, conn id:" << id; - }, - wrapper.get_executor()); - } - co_return coro_rpc::err_code{}; - } - -#ifdef YLT_ENABLE_IBV - - std::shared_ptr get_rr_device() { - assert(ibv_dev_lists_.size()); - return ibv_dev_lists_[rr_index_.fetch_add(1, std::memory_order_relaxed) % - ibv_dev_lists_.size()]; - } - - async_simple::coro::Lazy update_to_rdma(coro_connection *conn) { - bool init_ok = true; - auto &wrapper = conn->socket_wrapper(); - try { - if (!ibv_dev_lists_.empty()) { - ibv_config_->device = get_rr_device(); - } - wrapper = {std::move(*wrapper.socket()), wrapper.get_executor(), - *ibv_config_}; - } catch (...) { - ELOG_WARN << "init rdma connection failed"; - init_ok = false; - } - co_return init_ok; - } -#endif - - async_simple::coro::Lazy start_one( - std::shared_ptr conn) noexcept { - [[maybe_unused]] bool init_failed = false; - - ELOG_TRACE << "start handshake for conn:" << conn->get_connection_id() - << ",local endpoint:" << conn->get_local_endpoint() - << ",remote endpoint:" << conn->get_remote_endpoint(); - auto result = - co_await conn->handshake(); - if (result.ec.value() && - result.ec.value() != (int)std::errc::protocol_error) { - ELOG_WARN << "connection error:" << result.ec.message(); - co_return; - } - ELOG_TRACE << "finish handshake for conn:" << conn->get_connection_id(); - if (result.ec == std::errc::protocol_error) { - do { -#ifdef YLT_ENABLE_IBV - if (ibv_config_.has_value() && result.magic_number.size() == 1 && - result.magic_number[0] == - coro_io::ib_socket_t::ib_md5_first_header) { - ELOG_TRACE << "protocol is rdma, try to update"; - auto result = co_await update_to_rdma(conn.get()); - if (!result) { - ELOG_WARN << "rdma init failed"; - co_return; - } - break; - } -#endif - if (connection_transfer_) { - ELOG_TRACE - << "protocol is not coro_rpc. try to transfer to other server"; - connection_transfer_(std::move(conn->socket_wrapper()), - result.magic_number); - } - co_return; - } while (false); - } - co_await conn->template start( - router_, result.magic_number); - } - - typename server_config::executor_pool_t pool_; - - std::thread thd_; - stat flag_; - - std::mutex start_mtx_; - std::unordered_map> conns_; - std::mutex conns_mtx_; - - typename server_config::rpc_protocol::router router_; - - std::vector> acceptors_; - bool is_enable_tcp_no_delay_; - coro_rpc::err_code errc_ = {}; - std::chrono::steady_clock::duration conn_timeout_duration_; - - async_simple::util::move_only_function - connection_transfer_; - -#ifdef YLT_ENABLE_SSL - asio::ssl::context context_{asio::ssl::context::sslv23}; - bool use_ssl_ = false; -#endif -#ifdef YLT_ENABLE_IBV - std::optional ibv_config_; - std::vector> ibv_dev_lists_; - std::atomic rr_index_ = 0; -#endif - - std::function client_filter_; -}; -} // namespace coro_rpc +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asio/ip/tcp.hpp" +#include "async_simple/Common.h" +#include "async_simple/Promise.h" +#include "async_simple/coro/Collect.h" +#include "async_simple/util/move_only_function.h" +#include "common_service.hpp" +#include "coro_connection.hpp" +#include "ylt/coro_io/coro_io.hpp" +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/coro_io/server_acceptor.hpp" +#include "ylt/coro_rpc/impl/errno.h" +#include "ylt/coro_rpc/impl/expected.hpp" +namespace coro_rpc { +/*! + * ```cpp + * #include + * inline std::string hello_coro_rpc() { return "hello coro_rpc"; } + * int main(){ + * register_handler(); + * coro_rpc_server server(std::thread::hardware_concurrency(), 9000); + * server.start(); + * return 0; + * } + * ``` + */ + +template +class coro_rpc_server_base { + //!< Server state + enum class stat { + init = 0, // server hasn't started/stopped. + started, // server is started + stop // server is stopping/stopped + }; + + public: + /*! + * TODO: add doc + * @param thread_num the number of io_context. + * @param port the server port to listen. + * @param listen address of server + * @param conn_timeout_duration client connection timeout. 0 for no timeout. + * default no timeout. + * @param is_enable_tcp_no_delay is tcp socket allow + */ + coro_rpc_server_base(size_t thread_num = std::thread::hardware_concurrency(), + unsigned short port = 9001, + std::string address = "0.0.0.0", + std::chrono::steady_clock::duration + conn_timeout_duration = std::chrono::seconds(0), + bool is_enable_tcp_no_delay = true) + : pool_(thread_num), + conn_timeout_duration_(conn_timeout_duration), + flag_{stat::init}, + is_enable_tcp_no_delay_(is_enable_tcp_no_delay) { + acceptors_.push_back( + std::make_unique(address, port)); + } + + coro_rpc_server_base(size_t thread_num, std::string address, + std::chrono::steady_clock::duration + conn_timeout_duration = std::chrono::seconds(0), + bool is_enable_tcp_no_delay = true) + : pool_(thread_num), + conn_timeout_duration_(conn_timeout_duration), + flag_{stat::init}, + is_enable_tcp_no_delay_(is_enable_tcp_no_delay) { + acceptors_.push_back( + std::make_unique(address)); + } + + coro_rpc_server_base( + const server_config &config, + std::vector> acceptors = + {}) + : pool_(config.thread_num), + conn_timeout_duration_(config.conn_timeout_duration), + flag_{stat::init}, + is_enable_tcp_no_delay_(config.is_enable_tcp_no_delay) { +#ifdef YLT_ENABLE_SSL + if (config.ssl_config) { + use_ssl_ = init_ssl_context_helper(context_, config.ssl_config.value()); + } +#ifdef YLT_ENABLE_NTLS + else if (config.ssl_ntls_config) { + use_ssl_ = + init_ntls_context_helper(context_, config.ssl_ntls_config.value()); + } +#endif // YLT_ENABLE_NTLS +#endif +#ifdef YLT_ENABLE_IBV + if (!config.ibv_config.has_value() && !config.ibv_dev_lists.empty()) { + ibv_config_ = coro_io::ib_socket_t::config_t{}; + } + if (config.ibv_config) { + init_ibv(config.ibv_config.value(), std::move(config.ibv_dev_lists)); + } +#endif + if (!acceptors.empty()) { + acceptors_ = std::move(acceptors); + } + else { + acceptors_.push_back(std::make_unique( + config.address, config.port)); + } + } + + ~coro_rpc_server_base() { + ELOG_INFO << "coro_rpc_server will quit"; + stop(); + } + +#ifdef YLT_ENABLE_SSL + void init_ssl(const ssl_configure &conf) { + use_ssl_ = init_ssl_context_helper(context_, conf); + } +#ifdef YLT_ENABLE_NTLS + void init_ntls(const ssl_ntls_configure &conf) { + use_ssl_ = init_ntls_context_helper(context_, conf); + } +#endif // YLT_ENABLE_NTLS +#endif +#ifdef YLT_ENABLE_IBV + void init_ibv( + const coro_io::ib_socket_t::config_t &conf = {}, + std::vector> ibv_dev_lists = {}) { + ibv_config_ = conf; + ibv_dev_lists_ = std::move(ibv_dev_lists); + } +#endif + + /*! + * Start the server in blocking mode + * + * If the port is used, it will return a non-empty error code. + * + * @return error code if start failed, otherwise block until server stop. + */ + [[nodiscard]] coro_rpc::err_code start() noexcept { + return async_start().get(); + } + + private: + async_simple::Future make_error_future( + coro_rpc::err_code &&err) { + async_simple::Promise p; + p.setValue(std::move(err)); + return p.getFuture(); + } + + public: + const std::vector> & + get_acceptors() const noexcept { + return acceptors_; + } + async_simple::Future async_start() noexcept { + { + std::unique_lock lock(start_mtx_); + if (flag_ != stat::init) { + if (flag_ == stat::started) { + ELOG_INFO << "start again"; + } + else if (flag_ == stat::stop) { + ELOG_INFO << "has stoped"; + } + return make_error_future( + coro_rpc::err_code{coro_rpc::errc::server_has_ran}); + } + for (auto &acceptor : acceptors_) { + acceptor->set_io_threads_pool(&pool_); + auto ec = acceptor->listen(); + if (ec != coro_io::listen_errc ::ok) { + switch (ec) { + case coro_io::listen_errc::address_in_used: + errc_ = coro_rpc::err_code{coro_rpc::errc::address_in_used}; + break; + case coro_io::listen_errc::bad_address: + errc_ = coro_rpc::err_code{coro_rpc::errc::bad_address}; + break; + case coro_io::listen_errc::open_error: + errc_ = coro_rpc::err_code{coro_rpc::errc::open_error}; + break; + case coro_io::listen_errc::listen_error: + errc_ = coro_rpc::err_code{coro_rpc::errc::listen_error}; + break; + default: + errc_ = coro_rpc::err_code{coro_rpc::errc::io_error}; + break; + } + } + if (errc_) { + break; + } + } + if (!errc_) { + if constexpr (requires(typename server_config::executor_pool_t &pool) { + pool.run(); + }) { + thd_ = std::thread([this] { + pool_.run(); + }); + } + flag_ = stat::started; + } + else { + flag_ = stat::stop; + } + } + if (!errc_) { + async_simple::Promise promise; + auto future = promise.getFuture(); + accept().start([this, p = std::move(promise)]( + async_simple::Try &&res) mutable { + ELOG_INFO << "server quit!"; + if (res.hasError()) { + try { + std::rethrow_exception(res.getException()); + } catch (const std::exception &e) { + ELOG_ERROR << "server quit with exception: " << e.what(); + } + stop(); + errc_ = coro_rpc::err_code{coro_rpc::errc::io_error}; + p.setValue(errc_); + } + else { + ELOG_ERROR << "server quit with error: " << res.value().message(); + p.setValue(res.value()); + } + }); + return std::move(future); + } + else { + return make_error_future(coro_rpc::err_code{errc_}); + } + } + + /*! + * Stop server + * + * Block and wait server stops. + */ + void stop() { + std::unique_lock lock(start_mtx_); + if (flag_ == stat::stop) { + return; + } + + ELOG_INFO << "begin to stop coro_rpc_server"; + + if (flag_ == stat::started) { + for (auto &acceptor : acceptors_) acceptor->close(); + { + std::unique_lock lock(conns_mtx_); + ELOG_INFO << "total connection count: " << conns_.size(); + for (auto &conn_weak : conns_) { + auto conn = conn_weak.second.lock(); + if (conn && !conn->has_closed()) { + conn->async_close(); + } + } + + conns_.clear(); + } + + ELOG_INFO << "wait for server's thread-pool finish all work."; + pool_.stop(); + ELOG_INFO << "server's thread-pool finished."; + } + if (thd_.joinable()) { + thd_.join(); + } + + ELOG_INFO << "stop coro_rpc_server ok."; + flag_ = stat::stop; + } + + /*! + * Get listening port + * @return listening port + */ + uint16_t port() const { return acceptors_[0]->port(); }; + /* + * Get listening address + * @return listening address + */ + std::string_view address() const { return acceptors_[0]->address(); } + coro_rpc::err_code get_errc() const { return errc_; } + + template + void add_subserver( + std::function + dispatcher, + std::unique_ptr... server) { + connection_transfer_ = [dispatcher = std::move(dispatcher), + server = std::make_tuple(std::move(server)...)]( + coro_io::socket_wrapper_t &&socket, + std::string_view magic_number, + int index = -1) mutable { + std::apply( + [&dispatcher, &socket, magic_number](auto &...server) { + dispatcher(std::move(socket), magic_number, *server...); + }, + server); + }; + } + + /*! + * Register RPC service functions (member function) + * + * Before RPC server started, all RPC service functions must be registered. + * All you need to do is fill in the template parameters with the address of + * your own RPC functions. If RPC function is registered twice, the program + * will be terminate with exit code `EXIT_FAILURE`. + * + * Note: All functions must be member functions of the same class. + * + * ```cpp + * class test_class { + * public: + * void plus_one(int val) {} + * std::string get_str(std::string str) { return str; } + * }; + * int main() { + * test_class obj{}; + * // register member functions + * register_handler<&test_class::plus_one, &test_class::get_str>(&obj); + * return 0; + * } + * ``` + * + * @tparam first the address of RPC function. e.g. `&foo::bar` + * @tparam func the address of RPC function. e.g. `&foo::bar` + * @param self the object pointer corresponding to these member functions + */ + + template + void register_handler(util::class_type_t *self) { + router_.template register_handler(self); + } + + template + void register_handler(util::class_type_t *self, + const auto &key) { + router_.template register_handler(self, key); + } + + /*! + * Register RPC service functions (non-member function) + * + * Before RPC server started, all RPC service functions must be registered. + * All you need to do is fill in the template parameters with the address of + * your own RPC functions. If RPC function is registered twice, the program + * will be terminate with exit code `EXIT_FAILURE`. + * + * ```cpp + * // RPC functions (non-member function) + * void hello() {} + * std::string get_str() { return ""; } + * int add(int a, int b) {return a + b; } + * int main() { + * register_handler(); // register one RPC function at once + * register_handler(); // register multiple RPC functions at + * once return 0; + * } + * ``` + * + * @tparam first the address of RPC function. e.g. `foo`, `bar` + * @tparam func the address of RPC function. e.g. `foo`, `bar` + */ + + template + void register_handler() { + router_.template register_handler(); + } + + template + void register_handler(const auto &key) { + router_.template register_handler(key); + } + + auto &get_io_context_pool() noexcept { return pool_; } + + /*! + * Set client filter callback + * @param filter callback function that takes endpoint and returns bool + * true to allow connection, false to reject + */ + void client_filter( + std::function filter) { + client_filter_ = std::move(filter); + } + + private: + async_simple::coro::Lazy accept() { + std::vector> tasks; + acceptors_[0]->address(); + acceptors_[0]->port(); + for (auto &acceptor : acceptors_) { + tasks.emplace_back(accept_impl(*acceptor)); + } + auto results = co_await async_simple::coro::collectAny(std::move(tasks)); + ELOG_INFO << "acceptor:" << acceptors_[results.index()]->address() << ":" + << acceptors_[results.index()]->port() + << " exit by:" << results.value(); + co_return results.value(); + } + + uint64_t get_global_conn_id() { + static std::atomic global_conn_id = 0; + return ++global_conn_id; + } + async_simple::coro::Lazy accept_impl( + coro_io::server_acceptor_base &acceptor) { + ELOG_INFO << "begin to accept looping"; + for (;;) { + auto result = co_await acceptor.accept(); + std::error_code error; +#ifdef UNIT_TEST_INJECT + if (result.has_value()) { + if (g_action == inject_action::force_inject_server_accept_error) { + coro_io::socket_wrapper_t &wrapper = result.value(); + asio::error_code ignored_ec; + wrapper.close(); + g_action = inject_action::nothing; + result = ylt::unexpected{ + make_error_code(std::errc::io_error)}; + // only inject once + } + } +#endif + if (!result.has_value()) { + auto error = result.error(); + if (error == asio::error::operation_aborted) { + ELOG_INFO << "server was canceled:" << error.message(); + } + else { + ELOG_ERROR << "server accept failed:" << error.message(); + } + if (error == asio::error::operation_aborted || + error == asio::error::bad_descriptor) { + co_return coro_rpc::errc::operation_canceled; + } + continue; + } + coro_io::socket_wrapper_t &wrapper = result.value(); + + // Client filter check + if (client_filter_) { + asio::ip::tcp::endpoint remote_endpoint = { + wrapper.remote_endpoint().address, + (uint16_t)wrapper.remote_endpoint().port}; + + if (!client_filter_(remote_endpoint)) { + ELOG_WARN << "Connection from " + << remote_endpoint.address().to_string() + << " rejected by client filter"; + continue; + } + ELOG_INFO << "Connection from " << remote_endpoint.address().to_string() + << " allowed by client filter"; + } + + uint64_t conn_id = get_global_conn_id(); + ELOG_INFO << "new client conn_id " << conn_id << " coming, peer addr[" + << wrapper.remote_endpoint() << "], local addr[" + << wrapper.local_endpoint() << "]"; + + if (auto &socket = wrapper.socket(); socket) { + if (is_enable_tcp_no_delay_) { + std::error_code error; + socket->set_option(asio::ip::tcp::no_delay(true), error); + } +#ifdef YLT_ENABLE_SSL + if (use_ssl_) { + wrapper = coro_io::socket_wrapper_t{std::move(*socket), + wrapper.get_executor(), context_}; + } +#endif + } + auto conn = std::make_shared(std::move(wrapper), + conn_timeout_duration_); + conn->set_quit_callback( + [this](const uint64_t &id) { + std::unique_lock lock(conns_mtx_); + conns_.erase(id); + }, + conn_id); + { + std::unique_lock lock(conns_mtx_); + conns_.emplace(conn_id, conn); + } + ELOG_TRACE << "start new connection, conn_id:" << conn_id; + start_one(std::move(conn)) + .directlyStart( + [id = conn_id, this](async_simple::Try &&res) { + ELOG_INFO << "connection over, conn id:" << id; + }, + wrapper.get_executor()); + } + co_return coro_rpc::err_code{}; + } + +#ifdef YLT_ENABLE_IBV + + std::shared_ptr get_rr_device() { + assert(ibv_dev_lists_.size()); + return ibv_dev_lists_[rr_index_.fetch_add(1, std::memory_order_relaxed) % + ibv_dev_lists_.size()]; + } + + async_simple::coro::Lazy update_to_rdma(coro_connection *conn) { + bool init_ok = true; + auto &wrapper = conn->socket_wrapper(); + try { + if (!ibv_dev_lists_.empty()) { + ibv_config_->device = get_rr_device(); + } + wrapper = {std::move(*wrapper.socket()), wrapper.get_executor(), + *ibv_config_}; + } catch (...) { + ELOG_WARN << "init rdma connection failed"; + init_ok = false; + } + co_return init_ok; + } +#endif + + async_simple::coro::Lazy start_one( + std::shared_ptr conn) noexcept { + [[maybe_unused]] bool init_failed = false; + + ELOG_TRACE << "start handshake for conn:" << conn->get_connection_id() + << ",local endpoint:" << conn->get_local_endpoint() + << ",remote endpoint:" << conn->get_remote_endpoint(); + auto result = + co_await conn->handshake(); + if (result.ec.value() && + result.ec.value() != (int)std::errc::protocol_error) { + ELOG_WARN << "connection error:" << result.ec.message(); + co_return; + } + ELOG_TRACE << "finish handshake for conn:" << conn->get_connection_id(); + if (result.ec == std::errc::protocol_error) { + do { +#ifdef YLT_ENABLE_IBV + if (ibv_config_.has_value() && result.magic_number.size() == 1 && + result.magic_number[0] == + coro_io::ib_socket_t::ib_md5_first_header) { + ELOG_TRACE << "protocol is rdma, try to update"; + auto result = co_await update_to_rdma(conn.get()); + if (!result) { + ELOG_WARN << "rdma init failed"; + co_return; + } + break; + } +#endif + if (connection_transfer_) { + ELOG_TRACE + << "protocol is not coro_rpc. try to transfer to other server"; + connection_transfer_(std::move(conn->socket_wrapper()), + result.magic_number); + } + co_return; + } while (false); + } + co_await conn->template start( + router_, result.magic_number); + } + + typename server_config::executor_pool_t pool_; + + std::thread thd_; + stat flag_; + + std::mutex start_mtx_; + std::unordered_map> conns_; + std::mutex conns_mtx_; + + typename server_config::rpc_protocol::router router_; + + std::vector> acceptors_; + bool is_enable_tcp_no_delay_; + coro_rpc::err_code errc_ = {}; + std::chrono::steady_clock::duration conn_timeout_duration_; + + async_simple::util::move_only_function + connection_transfer_; + +#ifdef YLT_ENABLE_SSL + asio::ssl::context context_{asio::ssl::context::tls}; + bool use_ssl_ = false; +#endif +#ifdef YLT_ENABLE_IBV + std::optional ibv_config_; + std::vector> ibv_dev_lists_; + std::atomic rr_index_ = 0; +#endif + + std::function client_filter_; +}; +} // namespace coro_rpc diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index 88f5ed728..123f3c2e1 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -1,2848 +1,2850 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asio/dispatch.hpp" -#include "asio/error.hpp" -#include "asio/ip/tcp.hpp" -#include "asio/streambuf.hpp" -#include "async_simple/Future.h" -#include "async_simple/Unit.h" -#include "async_simple/coro/FutureAwaiter.h" -#include "async_simple/coro/Lazy.h" -#ifdef CINATRA_ENABLE_GZIP -#include "gzip.hpp" -#endif -#ifdef CINATRA_ENABLE_BROTLI -#include "brzip.hpp" -#endif -#include "cinatra_log_wrapper.hpp" -#include "error.hpp" -#include "http_parser.hpp" -#include "multipart.hpp" -#include "picohttpparser.h" -#include "response_cv.hpp" -#include "string_resize.hpp" -#include "uri.hpp" -#include "websocket.hpp" -#include "ylt/coro_io/coro_file.hpp" -#include "ylt/coro_io/coro_io.hpp" -#include "ylt/coro_io/io_context_pool.hpp" - -namespace coro_io { -template -class client_pool; -} -namespace cinatra { -template -struct is_stream : std::false_type {}; - -template -struct is_stream< - T, std::void_t().read(nullptr, 0), - std::declval().async_read(nullptr, 0))>> - : std::true_type {}; - -template -constexpr bool is_stream_v = is_stream::value; - -template -struct is_span : std::false_type {}; - -template -struct is_span().data(), - std::declval().size())>> - : std::true_type {}; - -template -constexpr bool is_span_v = is_span::value; - -template -struct is_smart_ptr : std::false_type {}; - -template -struct is_smart_ptr< - T, std::void_t().get(), *std::declval(), - is_stream_v)>> - : std::true_type {}; - -template -constexpr bool is_stream_ptr_v = is_smart_ptr::value || std::is_pointer_v; - -struct http_header; - -struct resp_data { - std::error_code net_err; - int status = 0; - bool eof = false; - std::string_view resp_body; - std::span resp_headers; -#ifdef BENCHMARK_TEST - uint64_t total = 0; -#endif -}; - -template -struct req_context { - req_content_type content_type = req_content_type::none; - std::string req_header; /*header string*/ - String content; /*body*/ - coro_io::coro_file *resp_body_stream = nullptr; -}; - -struct multipart_t { - std::string filename; - std::string content; - size_t size = 0; -}; - -struct read_result { - std::span buf; - bool eof; - std::error_code err; -}; - -enum class upload_type_t { with_length, chunked, multipart }; - -class coro_http_client : public std::enable_shared_from_this { - public: - struct config { - std::optional conn_timeout_duration; - std::optional req_timeout_duration; - std::string sec_key; - size_t max_single_part_size; - std::string proxy_host; - std::string proxy_port; - std::string proxy_auth_username; - std::string proxy_auth_passwd; - std::string proxy_auth_token; - bool enable_tcp_no_delay; -#ifdef CINATRA_ENABLE_SSL - bool use_ssl = - false; // if set use_ssl true, cinatra will add https automaticlly. -#ifdef YLT_ENABLE_NTLS - bool use_ntls = - false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. -#endif // YLT_ENABLE_NTLS -#endif - }; - - coro_http_client(asio::io_context::executor_type executor) - : executor_wrapper_(executor), - timer_(&executor_wrapper_), - socket_(std::make_shared(executor)), - head_buf_(socket_->head_buf_), - chunked_buf_(socket_->chunked_buf_), - create_tp_(std::chrono::steady_clock::now()) {} - - coro_http_client( - coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor()) - : coro_http_client(executor->get_asio_executor()) {} - - bool init_config(const config &conf) { - config_ = conf; - if (conf.conn_timeout_duration.has_value()) { - set_conn_timeout(*conf.conn_timeout_duration); - } - if (conf.req_timeout_duration.has_value()) { - set_req_timeout(*conf.req_timeout_duration); - } - if (!conf.sec_key.empty()) { - set_ws_sec_key(conf.sec_key); - } - if (conf.max_single_part_size > 0) { - set_max_single_part_size(conf.max_single_part_size); - } - if (!conf.proxy_host.empty()) { - set_proxy_basic_auth(conf.proxy_host, conf.proxy_port); - } - if (!conf.proxy_auth_username.empty()) { - set_proxy_basic_auth(conf.proxy_auth_username, conf.proxy_auth_passwd); - } - if (!conf.proxy_auth_token.empty()) { - set_proxy_bearer_token_auth(conf.proxy_auth_token); - } - if (conf.enable_tcp_no_delay) { - enable_tcp_no_delay_ = conf.enable_tcp_no_delay; - } -#ifdef CINATRA_ENABLE_SSL - set_ssl_schema(conf.use_ssl); -#ifdef YLT_ENABLE_NTLS - set_ntls_schema(conf.use_ntls); -#endif // YLT_ENABLE_NTLS -#endif - return true; - } - - ~coro_http_client() { close(); } - - auto get_create_time_point() const noexcept { - return std::chrono::steady_clock::now(); - } - - void close() { - if (socket_ == nullptr || socket_->has_closed_) - return; - - asio::dispatch(executor_wrapper_.get_asio_executor(), [socket = socket_] { - close_socket(*socket); - }); - } - - coro_io::ExecutorWrapper<> &get_executor() { return executor_wrapper_; } - - const config &get_config() { return config_; } - -#ifdef CINATRA_ENABLE_SSL - bool init_ssl(int verify_mode, const std::string &base_path, - const std::string &cert_file, const std::string &sni_hostname) { - if (has_init_ssl_) { - return true; - } - - try { -#ifdef YLT_ENABLE_NTLS - if (use_ntls_) { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(NTLS_client_method())); - - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); - - // Set NTLS cipher suites - if (!SSL_CTX_set_cipher_list( - ssl_ctx_->native_handle(), - "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { - CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; - } - } - else { - CINATRA_LOG_ERROR << "NTLS is not supported in this build."; - return false; - } -#else - ssl_ctx_ = - std::make_unique(asio::ssl::context::sslv23); -#endif // YLT_ENABLE_NTLS - auto full_cert_file = std::filesystem::path(base_path).append(cert_file); - if (std::filesystem::exists(full_cert_file)) { - ssl_ctx_->load_verify_file(full_cert_file.string()); - } - else { - if (!base_path.empty() || !cert_file.empty()) - return false; - } - - if (base_path.empty() && cert_file.empty()) { - ssl_ctx_->set_default_verify_paths(); - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - // ssl_ctx_.add_certificate_authority(asio::buffer(CA_PEM)); - if (!sni_hostname.empty()) { - ssl_ctx_->set_verify_callback( - asio::ssl::host_name_verification(sni_hostname)); - - if (need_set_sni_host_) { - // Set SNI Hostname (many hosts need this to handshake successfully) - SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), - sni_hostname.c_str()); - } - } - - has_init_ssl_ = true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); - return false; - } - return true; - } - - [[nodiscard]] bool init_ssl(int verify_mode = asio::ssl::verify_none, - std::string full_path = "", - const std::string &sni_hostname = "") { - std::string base_path; - std::string cert_file; - if (full_path.empty()) { - base_path = ""; - cert_file = ""; - } - else { - base_path = full_path.substr(0, full_path.find_last_of('/')); - cert_file = full_path.substr(full_path.find_last_of('/') + 1); - } - return init_ssl(verify_mode, base_path, cert_file, sni_hostname); - } - - /*! - * Initialize SSL with client certificate for mutual authentication - * @param verify_mode SSL verify mode (e.g., asio::ssl::verify_peer) - * @param base_path Base path for certificate files - * @param cert_file CA certificate file name for server verification - * @param client_cert_file Client certificate file name for mutual - * authentication - * @param client_key_file Client private key file name for mutual - * authentication - * @param sni_hostname SNI hostname - * @return true if initialization successful - */ - [[nodiscard]] bool init_ssl(int verify_mode, const std::string &base_path, - const std::string &cert_file, - const std::string &client_cert_file, - const std::string &client_key_file, - const std::string &sni_hostname) { - if (has_init_ssl_) { - return true; - } - - try { - ssl_ctx_ = std::make_unique(asio::ssl::context::tls); - - // Set cipher list for OpenSSL 3.0 compatibility - SSL_CTX *native_ctx = ssl_ctx_->native_handle(); - if (native_ctx) { - // const char* ciphers = "HIGH:!aNULL:!MD5:!3DES"; - // SSL_CTX_set_cipher_list(native_ctx, ciphers); - SSL_CTX_set_min_proto_version(native_ctx, TLS1_2_VERSION); - SSL_CTX_set_max_proto_version(native_ctx, TLS1_3_VERSION); - } - - auto full_cert_file = std::filesystem::path(base_path).append(cert_file); - if (std::filesystem::exists(full_cert_file)) { - ssl_ctx_->load_verify_file(full_cert_file.string()); - CINATRA_LOG_INFO << "loaded CA certificate: " - << full_cert_file.string(); - } - else { - if (!base_path.empty() || !cert_file.empty()) { - CINATRA_LOG_ERROR << "CA certificate file not found: " - << full_cert_file.string(); - return false; - } - } - - if (base_path.empty() && cert_file.empty()) { - ssl_ctx_->set_default_verify_paths(); - } - - // Load client certificate and key for mutual authentication - auto full_client_cert_file = - std::filesystem::path(base_path).append(client_cert_file); - auto full_client_key_file = - std::filesystem::path(base_path).append(client_key_file); - - if (std::filesystem::exists(full_client_cert_file)) { - ssl_ctx_->use_certificate_chain_file(full_client_cert_file.string()); - CINATRA_LOG_INFO << "loaded client certificate: " - << full_client_cert_file.string(); - } - else { - CINATRA_LOG_ERROR << "client certificate file not found: " - << full_client_cert_file.string(); - return false; - } - - if (std::filesystem::exists(full_client_key_file)) { - ssl_ctx_->use_private_key_file(full_client_key_file.string(), - asio::ssl::context::pem); - CINATRA_LOG_INFO << "loaded client private key: " - << full_client_key_file.string(); - } - else { - CINATRA_LOG_ERROR << "client key file not found: " - << full_client_key_file.string(); - return false; - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - if (!sni_hostname.empty()) { - ssl_ctx_->set_verify_callback( - asio::ssl::host_name_verification(sni_hostname)); - - if (need_set_sni_host_) { - SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), - sni_hostname.c_str()); - } - } - - has_init_ssl_ = true; - CINATRA_LOG_INFO << "SSL initialized with client certificate for mutual " - "authentication"; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); - return false; - } - return true; - } -#endif - - // return body_, the user will own body's lifetime. - std::string release_buf() { - if (body_.empty()) { - return std::move(resp_chunk_str_); - } - return std::move(body_); - } - -#ifdef CINATRA_ENABLE_GZIP - void set_ws_deflate(bool enable_ws_deflate) { - enable_ws_deflate_ = enable_ws_deflate; - } -#endif - - /*! - * Connect server - * - * only make socket connet(or handshake) to the host - * - * @param uri server address - * @param eps endpoints of resolve result. if eps is not nullptr and vector is - * empty, it will return the endpoints that, else if vector is not empty, it - * will use the eps to skill resolve and connect to server directly. - * @return resp_data - */ - async_simple::coro::Lazy connect( - std::string uri, std::vector *eps = nullptr) { - resp_data data{}; - bool no_schema = !has_schema(uri); - std::string append_uri; - if (no_schema) { -#ifdef CINATRA_ENABLE_SSL - if (is_ssl_schema_) - append_uri.append("https://").append(uri); - else -#endif - append_uri.append("http://").append(uri); - } - - auto [ok, u] = handle_uri(data, no_schema ? append_uri : uri); - if (!ok) { - co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; - } - { - if (u.is_websocket()) { - // build websocket http header - add_header("Upgrade", "websocket"); - add_header("Connection", "Upgrade"); - if (ws_sec_key_.empty()) { - ws_sec_key_ = "s//GYHa/XO7Hd2F2eOGfyA=="; // provide a random string. - } - add_header("Sec-WebSocket-Key", ws_sec_key_); - add_header("Sec-WebSocket-Version", "13"); -#ifdef CINATRA_ENABLE_GZIP - if (enable_ws_deflate_) - add_header("Sec-WebSocket-Extensions", - "permessage-deflate; client_max_window_bits"); -#endif - req_context<> ctx{}; - data = co_await async_request(std::move(uri), http_method::GET, - std::move(ctx)); - -#ifdef CINATRA_ENABLE_GZIP - if (enable_ws_deflate_) { - for (auto c : data.resp_headers) { - if (c.name == "Sec-WebSocket-Extensions") { - if (c.value.find("permessage-deflate;") != std::string::npos) { - is_server_support_ws_deflate_ = true; - } - else { - is_server_support_ws_deflate_ = false; - } - break; - } - } - } -#endif - co_return data; - } - data = co_await connect(u, eps); - } - if (socket_->is_timeout_) { - co_return resp_data{make_error_code(http_errc::connect_timeout), 404}; - } - if (!data.net_err) { - data.status = 200; - } - co_return data; - } - - bool has_closed() { return socket_->has_closed_; } - - [[nodiscard]] std::size_t get_pipeline_size() const noexcept { return 0; } - - const auto &get_headers() { return req_headers_; } - - void set_headers(std::unordered_map req_headers) { - req_headers_ = std::move(req_headers); - } - - bool add_header(std::string key, std::string val) { - if (key.empty()) - return false; - - req_headers_[key] = std::move(val); - - return true; - } - - void set_ws_sec_key(std::string sec_key) { ws_sec_key_ = std::move(sec_key); } - - /** - * @brief Set the max http body size object, in default it's INT64_MAX - * - * @param max_size - */ - void set_max_http_body_size(int64_t max_size) { - max_http_body_len_ = max_size; - } - - size_t available() { - std::error_code ec{}; - return socket_->impl_.available(ec); - } - - async_simple::coro::Lazy read_websocket() { - auto time_out_guard = - timer_guard(this, req_timeout_duration_, "websocket timer"); - co_return co_await async_read_ws(); - } - - async_simple::coro::Lazy write_websocket( - const char *data, opcode op = opcode::text) { - std::string str(data); - co_return co_await write_websocket(str, op); - } - - async_simple::coro::Lazy write_websocket( - const char *data, size_t size, opcode op = opcode::text) { - std::string str(data, size); - co_return co_await write_websocket(str, op); - } - - async_simple::coro::Lazy write_websocket( - std::string_view data, opcode op = opcode::text) { - std::string str(data); - co_return co_await write_websocket(str, op); - } - - async_simple::coro::Lazy write_websocket( - std::string &data, opcode op = opcode::text) { - co_return co_await write_websocket(std::span(data), op); - } - - async_simple::coro::Lazy write_websocket( - std::string &&data, opcode op = opcode::text) { - co_return co_await write_websocket(std::span(data), op); - } - - async_simple::coro::Lazy write_ws_frame(std::span msg, - websocket ws, opcode op, - resp_data &data, - bool eof = true) { - auto header = ws.encode_frame(msg, op, eof, enable_ws_deflate_); - std::vector buffers{ - asio::buffer(header), asio::buffer(msg.data(), msg.size())}; - - auto [ec, sz] = co_await async_write(buffers); - if (ec) { - data.net_err = ec; - data.status = 404; - } - } - -#ifdef CINATRA_ENABLE_GZIP - void gzip_compress(std::string_view source, std::string &dest_buf, - std::span &span, resp_data &data) { - if (enable_ws_deflate_ && is_server_support_ws_deflate_) { - if (cinatra::gzip_codec::deflate(source, dest_buf)) { - span = dest_buf; - } - else { - CINATRA_LOG_ERROR << "compress data error, data: " << source; - data.net_err = std::make_error_code(std::errc::protocol_error); - data.status = 404; - } - } - } -#endif - - template - async_simple::coro::Lazy write_websocket( - Source source, opcode op = opcode::text) { - resp_data data{}; - - websocket ws{}; - std::string close_str; - if (op == opcode::close) { - if constexpr (is_span_v) { - close_str = ws.format_close_payload(close_code::normal, source.data(), - source.size()); - source = {close_str.data(), close_str.size()}; - } - } - - std::span span{}; - if constexpr (is_span_v) { - span = {source.data(), source.size()}; -#ifdef CINATRA_ENABLE_GZIP - std::string dest_buf; - if (enable_ws_deflate_) { - gzip_compress({source.data(), source.size()}, dest_buf, span, data); - } -#endif - co_await write_ws_frame(span, ws, op, data, true); - } - else { - while (true) { - auto result = co_await source(); - span = {result.buf.data(), result.buf.size()}; -#ifdef CINATRA_ENABLE_GZIP - std::string dest_buf; - if (enable_ws_deflate_) { - gzip_compress({result.buf.data(), result.buf.size()}, dest_buf, span, - data); - } -#endif - co_await write_ws_frame(span, ws, op, data, result.eof); - - if (result.eof || data.status == 404) { - break; - } - } - } - - co_return data; - } - - async_simple::coro::Lazy write_websocket_close( - std::string msg = "") { - co_return co_await write_websocket(std::move(msg), opcode::close); - } - -#ifdef BENCHMARK_TEST - void set_bench_stop() { stop_bench_ = true; } -#endif - - async_simple::coro::Lazy async_patch( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::PATCH, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_options( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::OPTIONS, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_trace( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::TRACE, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_head( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::HEAD, - cinatra::req_context<>{}, std::move(headers)); - } - - // CONNECT example.com HTTP/1.1 - async_simple::coro::Lazy async_http_connect( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::CONNECT, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_get( - std::string uri, - std::unordered_map headers = {}) { - resp_data data{}; - req_context<> ctx{}; - data = co_await async_request(std::move(uri), http_method::GET, - std::move(ctx), std::move(headers)); -#ifdef BENCHMARK_TEST - data.total = total_len_; -#endif - if (redirect_uri_.empty() || !is_redirect(data)) { - co_return data; - } - else { - if (enable_follow_redirect_) - data = co_await async_request(std::move(redirect_uri_), - http_method::GET, std::move(ctx)); - co_return data; - } - } - - resp_data get(std::string uri, - std::unordered_map headers = {}) { - return async_simple::coro::syncAwait( - async_get(std::move(uri), std::move(headers))); - } - - resp_data post(std::string uri, std::string content, - req_content_type content_type, - std::unordered_map headers = {}) { - return async_simple::coro::syncAwait(async_post( - std::move(uri), std::move(content), content_type, std::move(headers))); - } - - async_simple::coro::Lazy async_post( - std::string uri, std::string content, req_content_type content_type, - std::unordered_map headers = {}) { - req_context<> ctx{content_type, "", std::move(content)}; - return async_request(std::move(uri), http_method::POST, std::move(ctx), - std::move(headers)); - } - - async_simple::coro::Lazy async_delete( - std::string uri, std::string content, req_content_type content_type, - std::unordered_map headers = {}) { - req_context<> ctx{content_type, "", std::move(content)}; - return async_request(std::move(uri), http_method::DEL, std::move(ctx), - std::move(headers)); - } - - async_simple::coro::Lazy async_put( - std::string uri, std::string content, req_content_type content_type, - std::unordered_map headers = {}) { - req_context<> ctx{content_type, "", std::move(content)}; - return async_request(std::move(uri), http_method::PUT, std::move(ctx), - std::move(headers)); - } - - bool add_str_part(std::string name, std::string content) { - size_t size = content.size(); - return form_data_ - .emplace(std::move(name), multipart_t{"", std::move(content), size}) - .second; - } - - bool add_file_part(std::string name, std::string filename) { - if (form_data_.find(name) != form_data_.end()) { - CINATRA_LOG_WARNING << "name already exist: " << name; - return false; - } - - std::error_code ec; - bool r = std::filesystem::exists(filename, ec); - if (!r || ec) { - if (ec) { - CINATRA_LOG_WARNING << ec.message(); - } - CINATRA_LOG_WARNING << "file not exists, " - << std::filesystem::current_path().string(); - return false; - } - - size_t file_size = std::filesystem::file_size(filename); - form_data_.emplace(std::move(name), - multipart_t{std::move(filename), "", file_size}); - return true; - } - - void set_max_single_part_size(size_t size) { max_single_part_size_ = size; } - - struct timer_guard { - timer_guard(coro_http_client *self, - std::chrono::steady_clock::duration duration, std::string msg) - : self(self), dur_(duration) { - self->socket_->is_timeout_ = false; - - if (duration.count() >= 0) { - self->timeout(self->timer_, duration, std::move(msg)) - .start([](auto &&) { - }); - } - return; - } - ~timer_guard() { - if (dur_.count() > 0 && self->socket_->is_timeout_ == false) { - std::error_code ignore_ec; - self->timer_.cancel(ignore_ec); - } - } - coro_http_client *self; - std::chrono::steady_clock::duration dur_; - }; - - async_simple::coro::Lazy async_download(std::string uri, - std::string filename, - std::string range = "") { - resp_data data{}; - coro_io::coro_file file; - file.open(filename, std::ios::trunc | std::ios::out); - if (!file.is_open()) { - data.net_err = std::make_error_code(std::errc::no_such_file_or_directory); - data.status = 404; - co_return data; - } - - req_context<> ctx{}; - if (range.empty()) { - add_header("Transfer-Encoding", "chunked"); - ctx = {req_content_type::none, "", "", &file}; - } - else { - std::string req_str = "Range: bytes="; - req_str.append(range).append(CRCF); - ctx = {req_content_type::none, std::move(req_str), {}, &file}; - } - - data = co_await async_request(std::move(uri), http_method::GET, - std::move(ctx)); - - co_return data; - } - - resp_data download(std::string uri, std::string filename, - std::string range = "") { - return async_simple::coro::syncAwait( - async_download(std::move(uri), std::move(filename), std::move(range))); - } - - bool is_body_in_out_buf() const { return !out_buf_.empty(); } - - void reset() { - if (!has_closed()) { - close_socket(*socket_); - } - - socket_->impl_ = asio::ip::tcp::socket{executor_wrapper_.context()}; - if (!socket_->impl_.is_open()) { - std::error_code ec; - socket_->impl_.open(asio::ip::tcp::v4(), ec); - if (ec) { - CINATRA_LOG_WARNING << "client reset socket failed, reason: " - << ec.message(); - return; - } - } - - socket_->has_closed_ = true; -#ifdef CINATRA_ENABLE_SSL - need_set_sni_host_ = true; - if (has_init_ssl_) { - socket_->ssl_stream_ = nullptr; - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - has_init_ssl_ = false; - } -#endif -#ifdef BENCHMARK_TEST - total_len_ = 0; -#endif - - // clear - head_buf_.consume(head_buf_.size()); - chunked_buf_.consume(chunked_buf_.size()); - resp_chunk_str_.clear(); - } - - std::string_view get_host() { return host_; } - - std::string_view get_port() { return port_; } - - private: - async_simple::coro::Lazy send_file_copy_with_chunked( - std::string_view source, std::error_code &ec) { - std::string file_data; - detail::resize(file_data, max_single_part_size_); - coro_io::coro_file file{}; - file.open(source, std::ios::in); - if (!file.is_open()) { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - while (!file.eof()) { - auto [rd_ec, rd_size] = - co_await file.async_read(file_data.data(), file_data.size()); - std::vector bufs; - std::string size_str; - cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, - file.eof()); - std::size_t size; - if (std::tie(ec, size) = co_await async_write(bufs); ec) { - break; - } - } - } - - async_simple::coro::Lazy send_file_copy_with_length( - std::string_view source, std::error_code &ec, std::size_t length, - std::size_t offset) { - if (length <= 0) { - co_return; - } - std::string file_data; - detail::resize(file_data, (std::min)(max_single_part_size_, length)); - coro_io::coro_file file{}; - file.open(source, std::ios::in); - if (!file.is_open()) { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - file.seek(offset, std::ios::cur); - std::size_t size; - while (length > 0) { - if (std::tie(ec, size) = co_await file.async_read( - file_data.data(), (std::min)(file_data.size(), length)); - ec) { - // bad request, file may smaller than content-length - break; - } - length -= size; - if (length > 0 && file.eof()) { - // bad request, file may smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - break; - } - if (std::tie(ec, size) = - co_await async_write(asio::buffer(file_data.data(), size)); - ec) { - break; - } - } - } -#ifdef __linux__ - struct fd_guard { - int fd; - fd_guard(const char *file_path) : fd(::open(file_path, O_RDONLY)) {} - ~fd_guard() { - if (fd >= 0) { - ::close(fd); - } - } - }; - async_simple::coro::Lazy send_file_no_copy_with_length( - const std::filesystem::path &source, std::error_code &ec, - std::size_t length, std::size_t offset) { - fd_guard guard(source.c_str()); - if (guard.fd < 0) [[unlikely]] { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - std::size_t actual_len = 0; - std::tie(ec, actual_len) = co_await coro_io::async_sendfile( - socket_->impl_, guard.fd, offset, length); - if (ec) [[unlikely]] { - co_return; - } - if (actual_len != length) [[unlikely]] { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - co_return; - } - } - async_simple::coro::Lazy send_file_no_copy_with_chunked( - const std::filesystem::path &source, std::error_code &ec) { - fd_guard guard(source.c_str()); - if (guard.fd < 0) [[unlikely]] { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - off_t now_position = 0, - max_position = std::filesystem::file_size(source, ec); - if (ec) { - co_return; - } - size_t len = - std::min(max_single_part_size_, max_position - now_position); - // send chunked - std::array chunked_buffer; - std::size_t sz; - std::tie(ec, sz) = co_await async_write( - asio::buffer(get_chuncked_buffers(len, chunked_buffer))); - if (ec) [[unlikely]] { - co_return; - } - do { - std::size_t actual_len = 0; - std::tie(ec, actual_len) = co_await coro_io::async_sendfile( - socket_->impl_, guard.fd, now_position, len); - if (ec) [[unlikely]] { - co_return; - } - if (actual_len != len) [[unlikely]] { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - co_return; - } - if (now_position += actual_len; now_position < max_position) { - len = std::min(max_single_part_size_, - max_position - now_position); - std::tie(ec, sz) = co_await async_write(asio::buffer( - get_chuncked_buffers(len, chunked_buffer))); - if (ec) { - co_return; - } - } - else [[unlikely]] { - std::tie(ec, sz) = co_await async_write(asio::buffer( - get_chuncked_buffers(len, chunked_buffer))); - if (ec) { - co_return; - } - break; - } - } while (true); - } -#endif - template - static std::size_t getRemainingBytes(stream &file) { - auto current_pos = file.tellg(); - file.seekg(0, std::ios::end); - auto end_pos = file.tellg(); - auto remaining_bytes = end_pos - current_pos; - file.seekg(current_pos); - return remaining_bytes; - } - - template - void check_source(resp_data &data, Source &source) { - if constexpr (is_stream_ptr_v) { - if (!source) { - data = resp_data{ - std::make_error_code(std::errc::no_such_file_or_directory), 404}; - } - } - else if constexpr (std::is_same_v || - std::is_same_v) { - if (!std::filesystem::exists(source)) { - data = resp_data{ - std::make_error_code(std::errc::no_such_file_or_directory), 404}; - } - } - } - - void handle_upload_header_with_multipart() { - size_t content_len = multipart_content_len(); - add_header("Content-Length", std::to_string(content_len)); - } - - void handle_upload_header_with_chunked( - std::unordered_map &headers) { - if (!resp_chunk_str_.empty()) { - resp_chunk_str_.clear(); - } - - if (headers.empty()) { - add_header("Transfer-Encoding", "chunked"); - } - else { - headers.emplace("Transfer-Encoding", "chunked"); - } - } - - template - int64_t handle_upload_header_with_length( - resp_data &data, Source &source, - std::unordered_map &headers, uint64_t offset, - int64_t content_length) { - if (content_length < 0) { - if constexpr (is_stream_ptr_v) { - content_length = getRemainingBytes(*source); - } - else if constexpr (std::is_same_v || - std::is_same_v) { - content_length = std::filesystem::file_size(source); - } - else { - CINATRA_LOG_ERROR - << "user should set content-length before calling async_upload " - "when source is user-defined function."; - data = - resp_data{std::make_error_code(std::errc::invalid_argument), 404}; - return content_length; - } - content_length -= offset; - if (content_length < 0) { - CINATRA_LOG_ERROR << "the offset is larger than the end of file"; - data = - resp_data{std::make_error_code(std::errc::invalid_argument), 404}; - return content_length; - } - } - - assert(content_length >= 0); - char buf[32]; - auto [ptr, _] = std::to_chars(buf, buf + 32, content_length); - if (headers.empty()) { - add_header("Content-Length", std::string(buf, ptr - buf)); - } - else { - headers.emplace("Content-Length", std::string_view(buf, ptr - buf)); - } - return content_length; - } - - async_simple::coro::Lazy send_fstream_with_multipart( - std::error_code &ec) { - resp_data data{}; - for (auto &[key, part] : form_data_) { - data = co_await send_single_part(key, part); - - if (data.net_err) { - ec = data.net_err; - co_return; - } - } - - std::string last_part; - size_t size = 0; - last_part.append("--").append(BOUNDARY).append("--").append(CRCF); - if (std::tie(ec, size) = co_await async_write(asio::buffer(last_part)); - ec) { - co_return; - } - } - - template - async_simple::coro::Lazy send_fstream_with_chunked( - Source &source, std::error_code &ec) { - size_t size = 0; - std::string file_data; - detail::resize(file_data, max_single_part_size_); - while (!source->eof()) { - size_t rd_size = - source->read(file_data.data(), file_data.size()).gcount(); - std::vector bufs; - std::string size_str; - cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, - source->eof()); - if (std::tie(ec, size) = co_await async_write(bufs); ec) { - break; - } - } - } - - template - async_simple::coro::Lazy send_fstream_with_length( - Source &source, std::error_code &ec, uint64_t offset, - int64_t content_length) { - size_t size = 0; - source->seekg(offset, std::ios::cur); - std::string file_data; - detail::resize(file_data, std::min(max_single_part_size_, - content_length)); - while (content_length > 0 && !source->eof()) { - size_t rd_size = - source - ->read(file_data.data(), - std::min(content_length, file_data.size())) - .gcount(); - if (std::tie(ec, size) = - co_await async_write(asio::buffer(file_data.data(), rd_size)); - ec) { - break; - } - content_length -= rd_size; - } - if (!ec && content_length > 0) { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - } - } - - template - async_simple::coro::Lazy send_sink_with_chunked(Source &source, - std::error_code &ec) { - size_t size = 0; - while (true) { - auto result = co_await source(); - std::vector bufs; - std::string size_str; - cinatra::to_chunked_buffers( - bufs, size_str, {result.buf.data(), result.buf.size()}, result.eof); - if (std::tie(ec, size) = co_await async_write(bufs); ec) { - break; - } - if (result.eof) { - break; - } - } - } - - template - async_simple::coro::Lazy send_sink_with_length(Source &source, - std::error_code &ec, - int64_t content_length) { - size_t size = 0; - while (true) { - auto result = co_await source(); - if (std::tie(ec, size) = co_await async_write(asio::buffer( - result.buf.data(), - std::min(content_length, result.buf.size()))); - ec) { - break; - } - content_length -= size; - if (content_length <= 0) { - break; - } - else if (result.eof) [[unlikely]] { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - break; - } - } - } - - async_simple::coro::Lazy reconnect(resp_data &data, uri_t u) { - data = co_await connect(u); - if (socket_->is_timeout_) { - data = resp_data{make_error_code(http_errc::connect_timeout), 404}; - } - if (data.net_err) { - co_return false; - } - - co_return true; - } - - void handle_upload_timeout_error(std::error_code &ec) { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (write_header_timeout_ || write_payload_timeout_ || read_timeout_) { - socket_->is_timeout_ = true; - } -#endif - if (socket_->is_timeout_) { - ec = make_error_code(http_errc::request_timeout); - } - } - - template - async_simple::coro::Lazy async_upload_impl( - S uri, http_method method, Source source /* file */, - req_content_type content_type = req_content_type::text, - std::unordered_map headers = {}, - uint64_t offset = 0 /*file offset*/, - int64_t content_length = -1 /*upload size*/) { - std::error_code ec{}; - size_t size = 0; - bool is_keep_alive = true; - req_context<> ctx{content_type}; - resp_data data{}; - - out_buf_ = {}; - - std::shared_ptr guard(nullptr, [&, this](auto) { - if (!req_headers_.empty()) { - req_headers_.clear(); - } - }); - - auto [ok, u] = handle_uri(data, uri); - if (!ok) { - co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; - } - - if constexpr (upload_type != upload_type_t::multipart) { - check_source(data, source); - if (data.status != 0) { - co_return data; - } - } - - if constexpr (upload_type == upload_type_t::with_length) { - content_length = handle_upload_header_with_length(data, source, headers, - offset, content_length); - if (data.status != 0) { - co_return data; - } - } - else if constexpr (upload_type == upload_type_t::chunked) { - handle_upload_header_with_chunked(headers); - } - else if constexpr (upload_type == upload_type_t::multipart) { - handle_upload_header_with_multipart(); - } - - std::string header_str = - build_request_header(u, method, ctx, true, std::move(headers)); - - if (socket_->has_closed_) { - if (bool r = co_await reconnect(data, u); !r) { - co_return data; - } - } - - auto time_guard = timer_guard(this, req_timeout_duration_, "request timer"); - std::tie(ec, size) = co_await async_write(asio::buffer(header_str)); - if (ec) { - handle_upload_timeout_error(ec); - co_return resp_data{ec, 404}; - } - - constexpr bool is_stream_file = is_stream_ptr_v; - if constexpr (is_stream_file) { - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_fstream_with_length(source, ec, offset, content_length); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_fstream_with_chunked(source, ec); - } - } - else if constexpr (std::is_enum_v) { // only for multipart - co_await send_fstream_with_multipart(ec); - } - else if constexpr (std::is_same_v || - std::is_same_v) { -#ifdef __linux__ -#ifdef CINATRA_ENABLE_SSL - if (!has_init_ssl_) { -#endif - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_file_no_copy_with_length(std::filesystem::path{source}, - ec, content_length, offset); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_file_no_copy_with_chunked(std::filesystem::path{source}, - ec); - } -#ifdef CINATRA_ENABLE_SSL - } - else { - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_file_copy_with_length(source, ec, content_length, - offset); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_file_copy_with_chunked(source, ec); - } - } -#endif -#else - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_file_copy_with_length(source, ec, content_length, offset); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_file_copy_with_chunked(source, ec); - } -#endif - } - else { - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_sink_with_length(source, ec, content_length); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_sink_with_chunked(source, ec); - } - } - if (ec) { - handle_upload_timeout_error(ec); - co_return resp_data{ec, 404}; - } - - data = co_await handle_read(ec, size, is_keep_alive, std::move(ctx), - http_method::POST); - if (ec) { - handle_upload_timeout_error(ec); - } - handle_result(data, ec, is_keep_alive); - co_return data; - } - - public: - // send file with length - template - async_simple::coro::Lazy async_upload( - S uri, http_method method, Source source /* file */, - uint64_t offset = 0 /*file offset*/, - int64_t content_length = -1 /*upload size*/, - req_content_type content_type = req_content_type::text, - std::unordered_map headers = {}) { - return async_upload_impl( - std::move(uri), method, std::move(source), content_type, - std::move(headers), offset, content_length); - } - - // send file with chunked - template - async_simple::coro::Lazy async_upload_chunked( - S uri, http_method method, Source source, - req_content_type content_type = req_content_type::text, - std::unordered_map headers = {}) { - return async_upload_impl( - std::move(uri), method, std::move(source), content_type, - std::move(headers)); - } - - // send multipart data, should call add_file_part or add_str_part firstly. - async_simple::coro::Lazy async_upload_multipart(std::string uri) { - if (form_data_.empty()) { - CINATRA_LOG_WARNING << "no multipart"; - co_return resp_data{std::make_error_code(std::errc::invalid_argument), - 404}; - } - - co_return co_await async_upload_impl( - std::move(uri), http_method::POST, upload_type_t::multipart, - req_content_type::multipart); - } - - async_simple::coro::Lazy async_upload_multipart( - std::string uri, std::string name, std::string filename) { - if (!add_file_part(std::move(name), std::move(filename))) { - CINATRA_LOG_WARNING << "open file failed or duplicate test names"; - co_return resp_data{std::make_error_code(std::errc::invalid_argument), - 404}; - } - co_return co_await async_upload_multipart(std::move(uri)); - } - - template - async_simple::coro::Lazy async_request( - S uri, http_method method, req_context ctx, - std::unordered_map headers = {}, - std::span out_buf = {}) { - if (!resp_chunk_str_.empty()) { - resp_chunk_str_.clear(); - } - if (!body_.empty()) { - body_.clear(); - } - - out_buf_ = out_buf; - - std::shared_ptr guard(nullptr, [this](auto) { - if (!req_headers_.empty()) { - req_headers_.clear(); - } - }); - - resp_data data{}; - - std::error_code ec{}; - size_t size = 0; - bool is_keep_alive = true; - - do { - uri_t u; - std::string append_uri; - - if (socket_->has_closed_ || (!uri.empty() && uri[0] != '/')) { - bool no_schema = !has_schema(uri); - - if (no_schema) { -#ifdef CINATRA_ENABLE_SSL - if (is_ssl_schema_) { - append_uri.append("https://").append(uri); - } - else -#endif - { - append_uri.append("http://").append(uri); - } - } - bool ok = false; - std::tie(ok, u) = handle_uri(data, no_schema ? append_uri : uri); - if (!ok) { - break; - } - } - else { - u.path = uri; - } - if (socket_->has_closed_) { - data = co_await connect(u); - if (data.status != 0) { - co_return data; - } - } - - std::vector vec; - std::string req_head_str = - build_request_header(u, method, ctx, false, std::move(headers)); - - bool has_body = !ctx.content.empty(); - if (has_body) { - vec.push_back(asio::buffer(req_head_str)); - vec.push_back(asio::buffer(ctx.content.data(), ctx.content.size())); - } - -#ifdef CORO_HTTP_PRINT_REQ_HEAD - CINATRA_LOG_DEBUG << req_head_str; -#endif - auto guard = timer_guard(this, req_timeout_duration_, "request timer"); - if (has_body) { - std::tie(ec, size) = co_await async_write(vec); - } - else { - std::tie(ec, size) = co_await async_write(asio::buffer(req_head_str)); - } - if (ec) { - break; - } - data = - co_await handle_read(ec, size, is_keep_alive, std::move(ctx), method); - } while (0); - if (ec && socket_->is_timeout_) { - ec = make_error_code(http_errc::request_timeout); - } - handle_result(data, ec, is_keep_alive); - co_return data; - } - - async_simple::coro::Lazy handle_shake() { -#ifdef CINATRA_ENABLE_SSL - if (!has_init_ssl_) { - bool r = init_ssl(asio::ssl::verify_none, "", host_); - if (!r) { - co_return std::make_error_code(std::errc::invalid_argument); - } - } - - if (socket_->ssl_stream_ == nullptr) { - co_return std::make_error_code(std::errc::not_a_stream); - } - - auto ec = co_await coro_io::async_handshake(socket_->ssl_stream_, - asio::ssl::stream_base::client); - if (ec) { - CINATRA_LOG_ERROR << "handle failed " << ec.message(); - } - co_return ec; -#else - // please open CINATRA_ENABLE_SSL before request https! - co_return std::make_error_code(std::errc::protocol_error); -#endif - } - -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - async_simple::coro::Lazy async_write_raw( - std::string_view data) { - auto [ec, _] = co_await async_write(asio::buffer(data)); - co_return ec; - } - - async_simple::coro::Lazy async_read_raw( - http_method method, bool clear_buffer = false) { - if (clear_buffer) { - body_.clear(); - } - - char buf[1024]; - std::error_code ec{}; - size_t size{}; -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - std::tie(ec, size) = co_await coro_io::async_read_some( - *socket_->ssl_stream_, asio::buffer(buf, 1024)); - } - else { -#endif - std::tie(ec, size) = co_await coro_io::async_read_some( - socket_->impl_, asio::buffer(buf, 1024)); -#ifdef CINATRA_ENABLE_SSL - } -#endif - body_.append(buf, size); - - co_return resp_data{ec, {}, {}, body_}; - } -#endif - - inline void set_proxy(const std::string &host, const std::string &port) { - proxy_host_ = host; - proxy_port_ = port; - } - - inline void set_proxy_basic_auth(const std::string &username, - const std::string &password) { - proxy_basic_auth_username_ = username; - proxy_basic_auth_password_ = password; - } - - inline void set_proxy_bearer_token_auth(const std::string &token) { - proxy_bearer_token_auth_token_ = token; - } - - inline void enable_auto_redirect(bool enable_follow_redirect) { - enable_follow_redirect_ = enable_follow_redirect; - } - -#ifdef CINATRA_ENABLE_SSL - void set_ssl_schema(bool r) { is_ssl_schema_ = r; } -#ifdef YLT_ENABLE_NTLS - void set_ntls_schema(bool r) { use_ntls_ = r; } - - /*! - * Initialize NTLS client with dual certificates - */ - bool init_ntls_client(const std::string &sign_cert_file, - const std::string &sign_key_file, - const std::string &enc_cert_file, - const std::string &enc_key_file, - const std::string &ca_cert_file = "", - int verify_mode = asio::ssl::verify_none, - const std::string &passwd = "") { - if (has_init_ssl_) { - return true; - } - - try { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(NTLS_client_method())); - - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); - - // Set NTLS cipher suites - if (!SSL_CTX_set_cipher_list(ssl_ctx_->native_handle(), - "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { - CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; - } - - if (!passwd.empty()) { - ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { - return pwd; - }); - } - - // Load client certificates if provided (for mutual authentication) - if (!sign_cert_file.empty() && !sign_key_file.empty()) { - if (!SSL_CTX_use_sign_certificate_file(ssl_ctx_->native_handle(), - sign_cert_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client SM2 signing certificate"; - return false; - } - - if (!SSL_CTX_use_sign_PrivateKey_file(ssl_ctx_->native_handle(), - sign_key_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client SM2 signing private key"; - return false; - } - } - - if (!enc_cert_file.empty() && !enc_key_file.empty()) { - if (!SSL_CTX_use_enc_certificate_file(ssl_ctx_->native_handle(), - enc_cert_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR - << "failed to load client SM2 encryption certificate"; - return false; - } - - if (!SSL_CTX_use_enc_PrivateKey_file(ssl_ctx_->native_handle(), - enc_key_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR - << "failed to load client SM2 encryption private key"; - return false; - } - } - - // Load CA certificate if provided - if (!ca_cert_file.empty()) { - if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), - ca_cert_file.c_str(), nullptr)) { - CINATRA_LOG_WARNING << "failed to load CA certificate"; - } - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - has_init_ssl_ = true; - use_ntls_ = true; - return true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init NTLS client failed: " << e.what(); - return false; - } - } - /*! - * Initialize NTLS client with TLS 1.3 + GM single certificate mode (RFC 8998) - */ - bool init_ntls_tls13_gm_client( - const std::string &gm_cert_file = "", const std::string &gm_key_file = "", - const std::string &ca_cert_file = "", - int verify_mode = asio::ssl::verify_none, - const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", - const std::string &passwd = "") { - if (has_init_ssl_) { - return true; - } - - try { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(TLS_client_method())); - - // Configure TLS 1.3 + GM mode (RFC 8998) - SSL_CTX_set_min_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); - - // Enable strict SM TLS 1.3 mode (Tongsuo specific) - SSL_CTX_enable_sm_tls13_strict(ssl_ctx_->native_handle()); - - // Set TLS 1.3 GM cipher suites - if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), - cipher_suites.c_str())) { - CINATRA_LOG_WARNING << "failed to set TLS 1.3 GM cipher suites"; - } - - // Set GM signature algorithms (required for SM TLS 1.3 strict mode) - if (!SSL_CTX_set1_sigalgs_list(ssl_ctx_->native_handle(), "sm2sig_sm3")) { - CINATRA_LOG_WARNING << "failed to set GM signature algorithms"; - } - - // Set GM curves (required for SM TLS 1.3 strict mode) - if (!SSL_CTX_set1_curves_list(ssl_ctx_->native_handle(), "SM2")) { - CINATRA_LOG_WARNING << "failed to set GM curves"; - } - - if (!passwd.empty()) { - ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { - return pwd; - }); - } - - // Load client certificate if provided (for mutual authentication) - if (!gm_cert_file.empty() && !gm_key_file.empty()) { - if (!SSL_CTX_use_certificate_file(ssl_ctx_->native_handle(), - gm_cert_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client GM certificate"; - return false; - } - - if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_->native_handle(), - gm_key_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client GM private key"; - return false; - } - } - - // Load CA certificate if provided - if (!ca_cert_file.empty()) { - if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), - ca_cert_file.c_str(), nullptr)) { - CINATRA_LOG_WARNING << "failed to load CA certificate"; - } - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - has_init_ssl_ = true; - use_ntls_ = true; - return true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init TLS 1.3 + GM client failed: " << e.what(); - return false; - } - } - -#endif // YLT_ENABLE_NTLS -#endif - - std::string get_redirect_uri() { return redirect_uri_; } - - bool is_redirect(resp_data &data) { - if (data.status > 299 && data.status <= 399) - return true; - return false; - } - - void set_conn_timeout(std::chrono::steady_clock::duration timeout_duration) { - conn_timeout_duration_ = timeout_duration; - } - - void set_req_timeout(std::chrono::steady_clock::duration timeout_duration) { - req_timeout_duration_ = timeout_duration; - } - - void set_chunked_callback( - std::function(std::string_view)> cb) { - chunked_cb_ = std::move(cb); - } - - bool has_chunked_callback() const { return chunked_cb_ != nullptr; } - -#ifdef CINATRA_ENABLE_SSL - void enable_sni_hostname(bool r) { need_set_sni_host_ = r; } -#endif - - template - friend class coro_io::client_pool; - - private: - struct socket_t { - asio::ip::tcp::socket impl_; - std::atomic has_closed_ = true; - std::atomic is_timeout_ = false; - asio::streambuf head_buf_; - asio::streambuf chunked_buf_; -#ifdef CINATRA_ENABLE_SSL - std::unique_ptr> ssl_stream_; -#endif - template - socket_t(ioc_t &&ioc) : impl_(std::forward(ioc)) {} - }; - static bool is_ok(const resp_data &data) noexcept { - return data.net_err == std::error_code{}; - } - - template - std::pair handle_uri(resp_data &data, const S &uri) { - uri_t u; - if (!u.parse_from(uri.data())) { - CINATRA_LOG_WARNING - << uri - << ", the url is not right, maybe need to encode the url firstly"; - data.net_err = std::make_error_code(std::errc::protocol_error); - data.status = 404; - return {false, {}}; - } - - if (u.host.front() == '[') { // for ipv6 - if (u.host.size() > 2) - u.host = u.host.substr(1, u.host.size() - 2); - } - - // construct proxy request uri - construct_proxy_uri(u); - - return {true, u}; - } - - void construct_proxy_uri(uri_t &u) { - if (!proxy_host_.empty() && !proxy_port_.empty()) { - if (!proxy_request_uri_.empty()) - proxy_request_uri_.clear(); - if (u.get_port() == "80") { - proxy_request_uri_.append("http://").append(u.get_host()).append(":80"); - } - else if (u.get_port() == "443") { - proxy_request_uri_.append("https://") - .append(u.get_host()) - .append(":443"); - } - else { - // all be http - proxy_request_uri_.append("http://") - .append(u.get_host()) - .append(":") - .append(u.get_port()); - } - proxy_request_uri_.append(u.get_path()); - u.path = std::string_view(proxy_request_uri_); - } - } - - std::string build_request_header( - const uri_t &u, http_method method, const auto &ctx, - bool already_has_len = false, - std::unordered_map headers = {}) { - std::string req_str(method_name(method)); - - req_str.append(" ").append(u.get_path()); - if (!u.query.empty()) { - req_str.append("?").append(u.query); - } - - if (!headers.empty()) { - req_headers_ = std::move(headers); - req_str.append(" HTTP/1.1\r\n"); - } - else { - if (req_headers_.find("Host") == req_headers_.end()) { - req_str.append(" HTTP/1.1\r\nHost:").append(u.host).append("\r\n"); - } - else { - req_str.append(" HTTP/1.1\r\n"); - } - } - - auto type_str = get_content_type_str(ctx.content_type); - if (!type_str.empty()) { - if (ctx.content_type == req_content_type::multipart) { - type_str.append(BOUNDARY); - } - req_headers_["Content-Type"] = std::move(type_str); - } - - bool has_connection = false; - // add user headers - if (!req_headers_.empty()) { - for (auto &pair : req_headers_) { - if (pair.first == "Connection") { - has_connection = true; - } - req_str.append(pair.first) - .append(": ") - .append(pair.second) - .append("\r\n"); - } - } - - if (!has_connection) { - req_str.append("Connection: keep-alive\r\n"); - } - - if (!proxy_basic_auth_username_.empty() && - !proxy_basic_auth_password_.empty()) { - std::string basic_auth_str = "Proxy-Authorization: Basic "; - std::string basic_base64_str = base64_encode( - proxy_basic_auth_username_ + ":" + proxy_basic_auth_password_); - req_str.append(basic_auth_str).append(basic_base64_str).append(CRCF); - } - - if (!proxy_bearer_token_auth_token_.empty()) { - std::string bearer_token_str = "Proxy-Authorization: Bearer "; - req_str.append(bearer_token_str) - .append(proxy_bearer_token_auth_token_) - .append(CRCF); - } - - if (!ctx.req_header.empty()) - req_str.append(ctx.req_header); - size_t content_len = ctx.content.size(); - bool should_add_len = false; - if (content_len > 0) { - should_add_len = true; - } - else { - if ((method == http_method::POST || method == http_method::PUT) && - ctx.content_type != req_content_type::multipart) { - should_add_len = true; - } - } - - if (req_headers_.find("Content-Length") != req_headers_.end()) { - should_add_len = false; - } - - if (already_has_len) { - should_add_len = false; - } - - if (should_add_len) { - char buf[32]; - auto [ptr, ec] = std::to_chars(buf, buf + 32, content_len); - req_str.append("Content-Length: ") - .append(std::string_view(buf, ptr - buf)) - .append("\r\n"); - } - - req_str.append("\r\n"); - return req_str; - } - - std::error_code handle_header(resp_data &data, http_parser &parser, - size_t header_size) { - // parse header - const char *data_ptr = asio::buffer_cast(head_buf_.data()); - - int parse_ret = parser.parse_response(data_ptr, header_size, 0); -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (parse_failed_forever_) { - parse_ret = -1; - } -#endif - if (parse_ret < 0) [[unlikely]] { - head_buf_.consume(head_buf_.size()); - return std::make_error_code(std::errc::protocol_error); - } - - if (parser_.body_len() > max_http_body_len_ || parser_.body_len() < 0) - [[unlikely]] { - CINATRA_LOG_ERROR << "invalid http content length: " - << parser_.body_len(); - head_buf_.consume(head_buf_.size()); - return std::make_error_code(std::errc::invalid_argument); - } - - head_buf_.consume(header_size); // header size - data.resp_headers = parser.get_headers(); - data.status = parser.status(); - return {}; - } - - template - async_simple::coro::Lazy handle_read(std::error_code &ec, - size_t &size, - bool &is_keep_alive, - req_context ctx, - http_method method) { - resp_data data{}; - do { - if (std::tie(ec, size) = co_await async_read_until(head_buf_, TWO_CRCF); - ec) { - break; - } - - ec = handle_header(data, parser_, size); - if (ec) { - break; - } - - is_keep_alive = parser_.keep_alive(); - if (method == http_method::HEAD) { - co_return data; - } - - bool is_out_buf = false; - - bool is_ranges = parser_.is_resp_ranges(); - if (is_ranges) { - is_keep_alive = true; - } - if (parser_.is_chunked()) { - out_buf_ = {}; - is_keep_alive = true; - if (head_buf_.size() > 0) { - const char *data_ptr = - asio::buffer_cast(head_buf_.data()); - chunked_buf_.sputn(data_ptr, head_buf_.size()); - head_buf_.consume(head_buf_.size()); - } - ec = co_await handle_chunked(data, std::move(ctx)); - break; - } - - if (parser_.is_multipart()) { - out_buf_ = {}; - is_keep_alive = true; - if (head_buf_.size() > 0) { - const char *data_ptr = - asio::buffer_cast(head_buf_.data()); - chunked_buf_.sputn(data_ptr, head_buf_.size()); - head_buf_.consume(head_buf_.size()); - } - ec = co_await handle_multipart(data, std::move(ctx)); - break; - } - - redirect_uri_.clear(); - bool is_redirect = parser_.is_location(); - if (is_redirect) - redirect_uri_ = parser_.get_header_value("Location"); - - if (!parser_.get_header_value("Content-Encoding").empty()) { - if (parser_.get_header_value("Content-Encoding").find("gzip") != - std::string_view::npos) - encoding_type_ = content_encoding::gzip; - else if (parser_.get_header_value("Content-Encoding").find("deflate") != - std::string_view::npos) - encoding_type_ = content_encoding::deflate; - else if (parser_.get_header_value("Content-Encoding").find("br") != - std::string_view::npos) - encoding_type_ = content_encoding::br; - } - else { - encoding_type_ = content_encoding::none; - } - - size_t content_len = (size_t)parser_.body_len(); -#ifdef BENCHMARK_TEST - total_len_ = parser_.total_len(); -#endif - - is_out_buf = !out_buf_.empty(); - if (is_out_buf) { - if (content_len > 0 && out_buf_.size() < content_len) { - out_buf_ = {}; - is_out_buf = false; - } - } - - if (content_len <= head_buf_.size()) { - // Now get entire content, additional data will discard. - // copy body. - if (content_len > 0) { - auto data_ptr = asio::buffer_cast(head_buf_.data()); - if (is_out_buf) { - memcpy(out_buf_.data(), data_ptr, content_len); - } - else { - detail::resize(body_, content_len); - memcpy(body_.data(), data_ptr, content_len); - } - head_buf_.consume(head_buf_.size()); - } - co_await handle_entire_content(data, content_len, is_ranges, ctx); - break; - } - - // read left part of content. - size_t part_size = head_buf_.size(); - size_t size_to_read = content_len - part_size; - - auto data_ptr = asio::buffer_cast(head_buf_.data()); - if (is_out_buf) { - memcpy(out_buf_.data(), data_ptr, part_size); - } - else { - detail::resize(body_, content_len); - memcpy(body_.data(), data_ptr, part_size); - } - - head_buf_.consume(part_size); - - if (is_out_buf) { - if (std::tie(ec, size) = co_await async_read( - asio::buffer(out_buf_.data() + part_size, size_to_read), - size_to_read); - ec) { - break; - } - } - else { - if (std::tie(ec, size) = co_await async_read( - asio::buffer(body_.data() + part_size, size_to_read), - size_to_read); - ec) { - break; - } - } - - // Now get entire content, additional data will discard. - co_await handle_entire_content(data, content_len, is_ranges, ctx); - } while (0); - - if (!resp_chunk_str_.empty()) { - data.resp_body = - std::string_view{resp_chunk_str_.data(), resp_chunk_str_.size()}; - } - - co_return data; - } - - async_simple::coro::Lazy handle_entire_content(resp_data &data, - size_t content_len, - bool is_ranges, - auto &ctx) { - if (content_len > 0) { - const char *data_ptr; - if (head_buf_.size() == 0) { - if (out_buf_.empty()) { - data_ptr = body_.data(); - } - else { - data_ptr = out_buf_.data(); - } - } - else { - data_ptr = asio::buffer_cast(head_buf_.data()); - } - - if (is_ranges) { - if (ctx.resp_body_stream) { - auto [ec, size] = co_await ctx.resp_body_stream->async_write( - {data_ptr, content_len}); - if (ec) { - data.net_err = ec; - co_return; - } - } - } - - std::string_view reply(data_ptr, content_len); -#ifdef CINATRA_ENABLE_GZIP - if (encoding_type_ == content_encoding::gzip) { - uncompressed_str_.clear(); - bool r = gzip_codec::uncompress(reply, uncompressed_str_); - if (r) - data.resp_body = uncompressed_str_; - else - data.resp_body = reply; - } - else if (encoding_type_ == content_encoding::deflate) { - uncompressed_str_.clear(); - bool r = gzip_codec::inflate(reply, uncompressed_str_); - if (r) - data.resp_body = uncompressed_str_; - else - data.resp_body = reply; - } -#endif -#if defined(CINATRA_ENABLE_BROTLI) && defined(CINATRA_ENABLE_GZIP) - else if (encoding_type_ == content_encoding::br) -#endif -#if defined(CINATRA_ENABLE_BROTLI) && !defined(CINATRA_ENABLE_GZIP) - if (encoding_type_ == content_encoding::br) -#endif -#ifdef CINATRA_ENABLE_BROTLI - { - uncompressed_str_.clear(); - bool r = br_codec::brotli_decompress(reply, uncompressed_str_); - if (r) - data.resp_body = uncompressed_str_; - else - data.resp_body = reply; - } -#endif -#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) - else -#endif - data.resp_body = reply; - - head_buf_.consume(content_len); - } - data.eof = (head_buf_.size() == 0); - } - - void handle_result(resp_data &data, std::error_code ec, bool is_keep_alive) { - if (ec) { - close_socket(*socket_); - data.net_err = ec; - data.status = 404; -#ifdef BENCHMARK_TEST - if (!stop_bench_) { - CINATRA_LOG_DEBUG << ec.message(); - } -#endif - } - else { - if (!is_keep_alive) { - close_socket(*socket_); - } - } - } - - template - async_simple::coro::Lazy handle_multipart( - resp_data &data, req_context ctx) { - std::error_code ec{}; - std::string boundary = std::string{parser_.get_boundary()}; - multipart_reader_t multipart(this); - while (true) { - auto part_head = co_await multipart.read_part_head(boundary); - if (part_head.ec) { - co_return part_head.ec; - } - - auto part_body = co_await multipart.read_part_body(boundary); - - if (ctx.resp_body_stream) { - size_t size; - std::tie(ec, size) = - co_await ctx.resp_body_stream->async_write(part_body.data); - } - else { - resp_chunk_str_.append(part_body.data.data(), part_body.data.size()); - } - - if (part_body.ec) { - co_return part_body.ec; - } - - if (part_body.eof) { - break; - } - } - co_return ec; - } - - template - async_simple::coro::Lazy handle_chunked( - resp_data &data, req_context ctx) { - std::error_code ec{}; - size_t size = 0; - while (true) { - if (std::tie(ec, size) = co_await async_read_until(chunked_buf_, CRCF); - ec) { - break; - } - - size_t buf_size = chunked_buf_.size(); - size_t additional_size = buf_size - size; - const char *data_ptr = - asio::buffer_cast(chunked_buf_.data()); - std::string_view size_str(data_ptr, size - CRCF.size()); - auto chunk_size = hex_to_int(size_str); - chunked_buf_.consume(size); - if (chunk_size < 0) { - CINATRA_LOG_DEBUG << "bad chunked size"; - ec = asio::error::make_error_code( - asio::error::basic_errors::invalid_argument); - break; - } - - if (additional_size < size_t(chunk_size + 2)) { - // not a complete chunk, read left chunk data. - size_t size_to_read = chunk_size + 2 - additional_size; - if (std::tie(ec, size) = - co_await async_read(chunked_buf_, size_to_read); - ec) { - break; - } - } - - if (chunk_size == 0) { - // all finished, no more data - chunked_buf_.consume(chunked_buf_.size()); - data.eof = true; - break; - } - - data_ptr = asio::buffer_cast(chunked_buf_.data()); - if (chunked_cb_) { - co_await chunked_cb_(std::string_view(data_ptr, chunk_size)); - } - else { - if (ctx.resp_body_stream) { - std::tie(ec, size) = co_await ctx.resp_body_stream->async_write( - {data_ptr, (size_t)chunk_size}); - } - else { - resp_chunk_str_.append(data_ptr, chunk_size); - } - } - - chunked_buf_.consume(chunk_size + CRCF.size()); - } - co_return ec; - } - - async_simple::coro::Lazy connect( - const uri_t &u, std::vector *eps = nullptr) { - std::vector eps_tmp; - if (eps == nullptr) { - eps = &eps_tmp; - } - if (should_reset_) { - reset(); - } - else { - should_reset_ = true; - } - if (socket_->has_closed_) { - auto time_out_guard = - timer_guard(this, conn_timeout_duration_, "connect timer"); - socket_->is_timeout_ = false; - host_ = proxy_host_.empty() ? u.get_host() : proxy_host_; - port_ = proxy_port_.empty() ? u.get_port() : proxy_port_; - if (eps->empty()) { - CINATRA_LOG_TRACE << "start resolve host: " << host_ << ":" << port_; - auto [ec, iter] = co_await coro_io::async_resolve( - &executor_wrapper_, socket_->impl_, host_, port_); - if (ec) { - co_return resp_data{ec, 404}; - } - else { - asio::ip::tcp::resolver::iterator end; - while (iter != end) { - eps->push_back(iter->endpoint()); - ++iter; - } - if (eps->empty()) [[unlikely]] { - co_return resp_data{std::make_error_code(std::errc::not_connected), - 404}; - } - } - } - CINATRA_LOG_TRACE - << "start connect to endpoint lists. total endpoint count:" - << eps->size() - << ", the first endpoint is: " << (*eps)[0].address().to_string() - << ":" << std::to_string((*eps)[0].port()); - std::error_code ec; - if (ec = co_await coro_io::async_connect(socket_->impl_, *eps); ec) { - co_return resp_data{ec, 404}; - } -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (connect_timeout_forever_) { - socket_->is_timeout_ = true; - } -#endif - if (socket_->is_timeout_) { - ec = make_error_code(http_errc::connect_timeout); - co_return resp_data{ec, 404}; - } - - if (enable_tcp_no_delay_) { - socket_->impl_.set_option(asio::ip::tcp::no_delay(true), ec); - if (ec) { - co_return resp_data{ec, 404}; - } - } - - if (u.is_ssl) { -#ifdef CINATRA_ENABLE_SSL - if (!has_init_ssl_) { - size_t pos = u.host.find("www."); - std::string host; - if (pos != std::string_view::npos) { - host = std::string{u.host.substr(pos + 4)}; - } - else { - host = std::string{u.host}; - } - bool r = init_ssl(asio::ssl::verify_none, "", host); - if (!r) { - co_return resp_data{ - std::make_error_code(std::errc::invalid_argument), 404}; - } - } -#endif - if (ec = co_await handle_shake(); ec) { - co_return resp_data{ec, 404}; - } - } - socket_->has_closed_ = false; - CINATRA_LOG_TRACE - << "connect to endpoint: " - << socket_->impl_.remote_endpoint().address().to_string() << ":" - << socket_->impl_.remote_endpoint().port() << " successfully"; - } - co_return resp_data{}; - } - - size_t multipart_content_len() { - size_t content_len = 0; - for (auto &[key, part] : form_data_) { - content_len += 75; - content_len += key.size() + 1; - if (!part.filename.empty()) { - content_len += (12 + part.filename.size() + 1); - auto ext = std::filesystem::path(part.filename).extension().string(); - if (auto it = g_content_type_map.find(ext); - it != g_content_type_map.end()) { - content_len += (14 + it->second.size()); - } - } - - content_len += 4; - - content_len += (part.size + 2); - } - content_len += (6 + BOUNDARY.size()); - return content_len; - } - - async_simple::coro::Lazy send_single_part( - const std::string &key, const multipart_t &part) { - std::string part_content_head; - part_content_head.append("--").append(BOUNDARY).append(CRCF); - part_content_head.append("Content-Disposition: form-data; name=\""); - part_content_head.append(key).append("\""); - bool is_file = !part.filename.empty(); - std::string short_name = - std::filesystem::path(part.filename).filename().string(); - if (is_file) { - part_content_head.append("; filename=\"") - .append(short_name) - .append("\"") - .append(CRCF); - auto ext = std::filesystem::path(short_name).extension().string(); - if (auto it = g_content_type_map.find(ext); - it != g_content_type_map.end()) { - part_content_head.append("Content-Type: ") - .append(it->second) - .append(CRCF); - } - - part_content_head.append(CRCF); - } - else { - part_content_head.append(TWO_CRCF); - } - if (auto [ec, size] = co_await async_write(asio::buffer(part_content_head)); - ec) { - co_return resp_data{ec, 404}; - } - - if (is_file) { - coro_io::coro_file file{}; - file.open(part.filename, std::ios::in); - assert(file.is_open()); - std::string file_data; - detail::resize(file_data, max_single_part_size_); - while (true) { - auto [rd_ec, rd_size] = - co_await file.async_read(file_data.data(), file_data.size()); - if (auto [ec, size] = - co_await async_write(asio::buffer(file_data.data(), rd_size)); - ec) { - co_return resp_data{ec, 404}; - } - if (file.eof()) { - if (auto [ec, size] = co_await async_write(asio::buffer(CRCF)); ec) { - co_return resp_data{ec, 404}; - } - break; - } - } - } - else { - std::array arr{asio::buffer(part.content), - asio::buffer(CRCF)}; - if (auto [ec, size] = co_await async_write(arr); ec) { - co_return resp_data{ec, 404}; - } - } - - co_return resp_data{{}, 200}; - } - - async_simple::coro::Lazy async_read_ws() { - resp_data data{}; - - head_buf_.consume(head_buf_.size()); - std::shared_ptr sock = socket_; - asio::streambuf &read_buf = sock->head_buf_; - bool has_init_ssl = false; -#ifdef CINATRA_ENABLE_SSL - has_init_ssl = has_init_ssl_; -#endif - websocket ws{}; - while (true) { - if (auto [ec, _] = co_await async_read_ws( - sock, read_buf, ws.left_header_len(), has_init_ssl); - ec) { - if (socket_->is_timeout_) { - co_return resp_data{make_error_code(http_errc::request_timeout), 404}; - } - data.net_err = ec; - data.status = 404; - - if (sock->has_closed_) { - co_return data; - } - - close_socket(*sock); - co_return data; - } - - const char *data_ptr = asio::buffer_cast(read_buf.data()); - auto ret = ws.parse_header(data_ptr, read_buf.size(), false); - if (ret == ws_header_status::incomplete) { - continue; - } - else if (ret == ws_header_status::error) { - data.net_err = std::make_error_code(std::errc::protocol_error); - data.status = 404; - close_socket(*sock); - co_return data; - } - - frame_header *header = (frame_header *)data_ptr; - bool is_close_frame = header->opcode == opcode::close; - - read_buf.consume(read_buf.size()); - - size_t payload_len = ws.payload_length(); - - if (auto [ec, size] = - co_await async_read_ws(sock, read_buf, payload_len, has_init_ssl); - ec) { - data.net_err = ec; - data.status = 404; - close_socket(*sock); - co_return data; - } - - data_ptr = asio::buffer_cast(read_buf.data()); -#ifdef CINATRA_ENABLE_GZIP - if (is_server_support_ws_deflate_ && enable_ws_deflate_) { - inflate_str_.clear(); - if (!cinatra::gzip_codec::inflate({data_ptr, payload_len}, - inflate_str_)) { - CINATRA_LOG_ERROR << "uncompuress data error"; - data.status = 404; - data.net_err = std::make_error_code(std::errc::protocol_error); - co_return data; - } - data_ptr = inflate_str_.data(); - payload_len = inflate_str_.length(); - } -#endif - if (is_close_frame) { - if (payload_len >= 2) { - payload_len -= 2; - data_ptr += sizeof(uint16_t); - } - } - data.status = 200; - data.resp_body = {data_ptr, payload_len}; - - read_buf.consume(read_buf.size()); - - if (is_close_frame) { - std::string reason = "close"; - auto close_str = ws.format_close_payload(close_code::normal, - reason.data(), reason.size()); - auto span = std::span(close_str); - auto encode_header = ws.encode_frame(span, opcode::close, true); - std::vector buffers{asio::buffer(encode_header), - asio::buffer(reason)}; - - co_await async_write_ws(sock, buffers, has_init_ssl); - - close_socket(*sock); - - data.net_err = asio::error::eof; - data.status = 404; - co_return data; - } - co_return data; - } - } - - template - async_simple::coro::Lazy> async_read_ws( - auto sock, AsioBuffer &&buffer, size_t size_to_read, - bool has_init_ssl = false) noexcept { -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl) { - return coro_io::async_read(*sock->ssl_stream_, buffer, size_to_read); - } - else { -#endif - return coro_io::async_read(sock->impl_, buffer, size_to_read); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - template - async_simple::coro::Lazy> async_write_ws( - auto sock, AsioBuffer &&buffer, bool has_init_ssl = false) { -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl) { - return coro_io::async_write(*sock->ssl_stream_, buffer); - } - else { -#endif - return coro_io::async_write(sock->impl_, buffer); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - template - async_simple::coro::Lazy> async_read( - AsioBuffer &&buffer, size_t size_to_read) noexcept { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (read_failed_forever_) { - return async_read_failed(); - } -#endif -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - return coro_io::async_read(*socket_->ssl_stream_, buffer, size_to_read); - } - else { -#endif - return coro_io::async_read(socket_->impl_, buffer, size_to_read); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - async_simple::coro::Lazy> - async_write_failed() { - co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); - } - - async_simple::coro::Lazy> - async_read_failed() { - co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); - } -#endif - - template - async_simple::coro::Lazy> async_write( - AsioBuffer &&buffer) { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (write_failed_forever_) { - return async_write_failed(); - } -#endif -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - return coro_io::async_write(*socket_->ssl_stream_, buffer); - } - else { -#endif - return coro_io::async_write(socket_->impl_, buffer); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - template - async_simple::coro::Lazy> async_read_until( - AsioBuffer &buffer, asio::string_view delim) noexcept { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (read_failed_forever_) { - return async_read_failed(); - } -#endif -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - return coro_io::async_read_until(*socket_->ssl_stream_, buffer, delim); - } - else { -#endif - return coro_io::async_read_until(socket_->impl_, buffer, delim); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - static void close_socket(socket_t &socket) { - std::error_code ec; - socket.impl_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); - socket.impl_.close(ec); - socket.has_closed_ = true; - } - - async_simple::coro::Lazy timeout( - auto &timer, std::chrono::steady_clock::duration duration, - std::string msg) { - auto watcher = std::weak_ptr(socket_); - timer.expires_after(duration); - auto is_timeout = co_await timer.async_await(); - if (!is_timeout) { - co_return false; - } - if (auto socket = watcher.lock(); socket) { - socket_->is_timeout_ = true; - CINATRA_LOG_WARNING << msg << " timeout"; - close_socket(*socket_); - } - co_return true; - } - - template - bool has_schema(const S &url) { - size_t pos_http = url.find("http://"); - size_t pos_https = url.find("https://"); - size_t pos_ws = url.find("ws://"); - size_t pos_wss = url.find("wss://"); - bool has_http_scheme = - ((pos_http != std::string::npos) && pos_http == 0) || - ((pos_https != std::string::npos) && pos_https == 0) || - ((pos_ws != std::string::npos) && pos_ws == 0) || - ((pos_wss != std::string::npos) && pos_wss == 0); - return has_http_scheme; - } - - friend class multipart_reader_t; - http_parser parser_; - coro_io::ExecutorWrapper<> executor_wrapper_; - coro_io::period_timer timer_; - std::shared_ptr socket_; - asio::streambuf &head_buf_; - asio::streambuf &chunked_buf_; - std::string body_; - - std::unordered_map req_headers_; - - std::string proxy_request_uri_ = ""; - std::string proxy_host_; - std::string proxy_port_; - - std::string proxy_basic_auth_username_; - std::string proxy_basic_auth_password_; - - std::string proxy_bearer_token_auth_token_; - - std::map form_data_; - size_t max_single_part_size_ = 1024 * 1024; - - std::string ws_sec_key_; - std::string host_; - std::string port_; - -#ifdef CINATRA_ENABLE_SSL - std::unique_ptr ssl_ctx_ = nullptr; - bool has_init_ssl_ = false; - bool is_ssl_schema_ = false; -#ifdef YLT_ENABLE_NTLS - bool use_ntls_ = true; -#endif // YLT_ENABLE_NTLS - bool need_set_sni_host_ = true; -#endif - std::string redirect_uri_; - bool enable_follow_redirect_ = false; - std::chrono::steady_clock::duration conn_timeout_duration_ = - std::chrono::seconds(30); - std::chrono::steady_clock::duration req_timeout_duration_ = - std::chrono::seconds(60); - std::chrono::steady_clock::time_point create_tp_; - bool enable_tcp_no_delay_ = true; - std::string resp_chunk_str_; - std::span out_buf_; - bool should_reset_ = false; - config config_; - - bool enable_ws_deflate_ = false; -#ifdef CINATRA_ENABLE_GZIP - bool is_server_support_ws_deflate_ = false; - std::string inflate_str_; -#endif - content_encoding encoding_type_ = content_encoding::none; - int64_t max_http_body_len_ = - INT64_MAX; // in default we don't limit http body len - - std::function(std::string_view)> chunked_cb_; -#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) - std::string uncompressed_str_; -#endif - -#ifdef BENCHMARK_TEST - bool stop_bench_ = false; - size_t total_len_ = 0; -#endif -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - public: - bool write_failed_forever_ = false; - bool connect_timeout_forever_ = false; - bool parse_failed_forever_ = false; - bool read_failed_forever_ = false; - bool write_header_timeout_ = false; - bool write_payload_timeout_ = false; - bool read_timeout_ = false; -#endif -}; - -} // namespace cinatra +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asio/dispatch.hpp" +#include "asio/error.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/streambuf.hpp" +#include "async_simple/Future.h" +#include "async_simple/Unit.h" +#include "async_simple/coro/FutureAwaiter.h" +#include "async_simple/coro/Lazy.h" +#ifdef CINATRA_ENABLE_GZIP +#include "gzip.hpp" +#endif +#ifdef CINATRA_ENABLE_BROTLI +#include "brzip.hpp" +#endif +#include "cinatra_log_wrapper.hpp" +#include "error.hpp" +#include "http_parser.hpp" +#include "multipart.hpp" +#include "picohttpparser.h" +#include "response_cv.hpp" +#include "string_resize.hpp" +#include "uri.hpp" +#include "websocket.hpp" +#include "ylt/coro_io/coro_file.hpp" +#include "ylt/coro_io/coro_io.hpp" +#include "ylt/coro_io/io_context_pool.hpp" + +namespace coro_io { +template +class client_pool; +} +namespace cinatra { +template +struct is_stream : std::false_type {}; + +template +struct is_stream< + T, std::void_t().read(nullptr, 0), + std::declval().async_read(nullptr, 0))>> + : std::true_type {}; + +template +constexpr bool is_stream_v = is_stream::value; + +template +struct is_span : std::false_type {}; + +template +struct is_span().data(), + std::declval().size())>> + : std::true_type {}; + +template +constexpr bool is_span_v = is_span::value; + +template +struct is_smart_ptr : std::false_type {}; + +template +struct is_smart_ptr< + T, std::void_t().get(), *std::declval(), + is_stream_v)>> + : std::true_type {}; + +template +constexpr bool is_stream_ptr_v = is_smart_ptr::value || std::is_pointer_v; + +struct http_header; + +struct resp_data { + std::error_code net_err; + int status = 0; + bool eof = false; + std::string_view resp_body; + std::span resp_headers; +#ifdef BENCHMARK_TEST + uint64_t total = 0; +#endif +}; + +template +struct req_context { + req_content_type content_type = req_content_type::none; + std::string req_header; /*header string*/ + String content; /*body*/ + coro_io::coro_file *resp_body_stream = nullptr; +}; + +struct multipart_t { + std::string filename; + std::string content; + size_t size = 0; +}; + +struct read_result { + std::span buf; + bool eof; + std::error_code err; +}; + +enum class upload_type_t { with_length, chunked, multipart }; + +class coro_http_client : public std::enable_shared_from_this { + public: + struct config { + std::optional conn_timeout_duration; + std::optional req_timeout_duration; + std::string sec_key; + size_t max_single_part_size; + std::string proxy_host; + std::string proxy_port; + std::string proxy_auth_username; + std::string proxy_auth_passwd; + std::string proxy_auth_token; + bool enable_tcp_no_delay; +#ifdef CINATRA_ENABLE_SSL + bool use_ssl = + false; // if set use_ssl true, cinatra will add https automaticlly. +#ifdef YLT_ENABLE_NTLS + bool use_ntls = + false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. +#endif // YLT_ENABLE_NTLS +#endif + }; + + coro_http_client(asio::io_context::executor_type executor) + : executor_wrapper_(executor), + timer_(&executor_wrapper_), + socket_(std::make_shared(executor)), + head_buf_(socket_->head_buf_), + chunked_buf_(socket_->chunked_buf_), + create_tp_(std::chrono::steady_clock::now()) {} + + coro_http_client( + coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor()) + : coro_http_client(executor->get_asio_executor()) {} + + bool init_config(const config &conf) { + config_ = conf; + if (conf.conn_timeout_duration.has_value()) { + set_conn_timeout(*conf.conn_timeout_duration); + } + if (conf.req_timeout_duration.has_value()) { + set_req_timeout(*conf.req_timeout_duration); + } + if (!conf.sec_key.empty()) { + set_ws_sec_key(conf.sec_key); + } + if (conf.max_single_part_size > 0) { + set_max_single_part_size(conf.max_single_part_size); + } + if (!conf.proxy_host.empty()) { + set_proxy_basic_auth(conf.proxy_host, conf.proxy_port); + } + if (!conf.proxy_auth_username.empty()) { + set_proxy_basic_auth(conf.proxy_auth_username, conf.proxy_auth_passwd); + } + if (!conf.proxy_auth_token.empty()) { + set_proxy_bearer_token_auth(conf.proxy_auth_token); + } + if (conf.enable_tcp_no_delay) { + enable_tcp_no_delay_ = conf.enable_tcp_no_delay; + } +#ifdef CINATRA_ENABLE_SSL + set_ssl_schema(conf.use_ssl); +#ifdef YLT_ENABLE_NTLS + set_ntls_schema(conf.use_ntls); +#endif // YLT_ENABLE_NTLS +#endif + return true; + } + + ~coro_http_client() { close(); } + + auto get_create_time_point() const noexcept { + return std::chrono::steady_clock::now(); + } + + void close() { + if (socket_ == nullptr || socket_->has_closed_) + return; + + asio::dispatch(executor_wrapper_.get_asio_executor(), [socket = socket_] { + close_socket(*socket); + }); + } + + coro_io::ExecutorWrapper<> &get_executor() { return executor_wrapper_; } + + const config &get_config() { return config_; } + +#ifdef CINATRA_ENABLE_SSL + bool init_ssl(int verify_mode, const std::string &base_path, + const std::string &cert_file, const std::string &sni_hostname) { + if (has_init_ssl_) { + return true; + } + + try { +#ifdef YLT_ENABLE_NTLS + if (use_ntls_) { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + + // Set NTLS cipher suites + if (!SSL_CTX_set_cipher_list( + ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + } + else { + CINATRA_LOG_ERROR << "NTLS is not supported in this build."; + return false; + } +#else + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); +#endif // YLT_ENABLE_NTLS + auto full_cert_file = std::filesystem::path(base_path).append(cert_file); + if (std::filesystem::exists(full_cert_file)) { + ssl_ctx_->load_verify_file(full_cert_file.string()); + } + else { + if (!base_path.empty() || !cert_file.empty()) + return false; + } + + if (base_path.empty() && cert_file.empty()) { + ssl_ctx_->set_default_verify_paths(); + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + // ssl_ctx_.add_certificate_authority(asio::buffer(CA_PEM)); + if (!sni_hostname.empty()) { + ssl_ctx_->set_verify_callback( + asio::ssl::host_name_verification(sni_hostname)); + + if (need_set_sni_host_) { + // Set SNI Hostname (many hosts need this to handshake successfully) + SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), + sni_hostname.c_str()); + } + } + + has_init_ssl_ = true; + is_ssl_schema_ = true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); + return false; + } + return true; + } + + [[nodiscard]] bool init_ssl(int verify_mode = asio::ssl::verify_none, + std::string full_path = "", + const std::string &sni_hostname = "") { + std::string base_path; + std::string cert_file; + if (full_path.empty()) { + base_path = ""; + cert_file = ""; + } + else { + base_path = full_path.substr(0, full_path.find_last_of('/')); + cert_file = full_path.substr(full_path.find_last_of('/') + 1); + } + return init_ssl(verify_mode, base_path, cert_file, sni_hostname); + } + + /*! + * Initialize SSL with client certificate for mutual authentication + * @param verify_mode SSL verify mode (e.g., asio::ssl::verify_peer) + * @param base_path Base path for certificate files + * @param cert_file CA certificate file name for server verification + * @param client_cert_file Client certificate file name for mutual + * authentication + * @param client_key_file Client private key file name for mutual + * authentication + * @param sni_hostname SNI hostname + * @return true if initialization successful + */ + [[nodiscard]] bool init_ssl(int verify_mode, const std::string &base_path, + const std::string &cert_file, + const std::string &client_cert_file, + const std::string &client_key_file, + const std::string &sni_hostname) { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique(asio::ssl::context::tls); + + // Set cipher list for OpenSSL 3.0 compatibility + SSL_CTX *native_ctx = ssl_ctx_->native_handle(); + if (native_ctx) { + // const char* ciphers = "HIGH:!aNULL:!MD5:!3DES"; + // SSL_CTX_set_cipher_list(native_ctx, ciphers); + SSL_CTX_set_min_proto_version(native_ctx, TLS1_2_VERSION); + SSL_CTX_set_max_proto_version(native_ctx, TLS1_3_VERSION); + } + + auto full_cert_file = std::filesystem::path(base_path).append(cert_file); + if (std::filesystem::exists(full_cert_file)) { + ssl_ctx_->load_verify_file(full_cert_file.string()); + CINATRA_LOG_INFO << "loaded CA certificate: " + << full_cert_file.string(); + } + else { + if (!base_path.empty() || !cert_file.empty()) { + CINATRA_LOG_ERROR << "CA certificate file not found: " + << full_cert_file.string(); + return false; + } + } + + if (base_path.empty() && cert_file.empty()) { + ssl_ctx_->set_default_verify_paths(); + } + + // Load client certificate and key for mutual authentication + auto full_client_cert_file = + std::filesystem::path(base_path).append(client_cert_file); + auto full_client_key_file = + std::filesystem::path(base_path).append(client_key_file); + + if (std::filesystem::exists(full_client_cert_file)) { + ssl_ctx_->use_certificate_chain_file(full_client_cert_file.string()); + CINATRA_LOG_INFO << "loaded client certificate: " + << full_client_cert_file.string(); + } + else { + CINATRA_LOG_ERROR << "client certificate file not found: " + << full_client_cert_file.string(); + return false; + } + + if (std::filesystem::exists(full_client_key_file)) { + ssl_ctx_->use_private_key_file(full_client_key_file.string(), + asio::ssl::context::pem); + CINATRA_LOG_INFO << "loaded client private key: " + << full_client_key_file.string(); + } + else { + CINATRA_LOG_ERROR << "client key file not found: " + << full_client_key_file.string(); + return false; + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + if (!sni_hostname.empty()) { + ssl_ctx_->set_verify_callback( + asio::ssl::host_name_verification(sni_hostname)); + + if (need_set_sni_host_) { + SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), + sni_hostname.c_str()); + } + } + + has_init_ssl_ = true; + is_ssl_schema_ = true; + CINATRA_LOG_INFO << "SSL initialized with client certificate for mutual " + "authentication"; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); + return false; + } + return true; + } +#endif + + // return body_, the user will own body's lifetime. + std::string release_buf() { + if (body_.empty()) { + return std::move(resp_chunk_str_); + } + return std::move(body_); + } + +#ifdef CINATRA_ENABLE_GZIP + void set_ws_deflate(bool enable_ws_deflate) { + enable_ws_deflate_ = enable_ws_deflate; + } +#endif + + /*! + * Connect server + * + * only make socket connet(or handshake) to the host + * + * @param uri server address + * @param eps endpoints of resolve result. if eps is not nullptr and vector is + * empty, it will return the endpoints that, else if vector is not empty, it + * will use the eps to skill resolve and connect to server directly. + * @return resp_data + */ + async_simple::coro::Lazy connect( + std::string uri, std::vector *eps = nullptr) { + resp_data data{}; + bool no_schema = !has_schema(uri); + std::string append_uri; + if (no_schema) { +#ifdef CINATRA_ENABLE_SSL + if (is_ssl_schema_) + append_uri.append("https://").append(uri); + else +#endif + append_uri.append("http://").append(uri); + } + + auto [ok, u] = handle_uri(data, no_schema ? append_uri : uri); + if (!ok) { + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; + } + { + if (u.is_websocket()) { + // build websocket http header + add_header("Upgrade", "websocket"); + add_header("Connection", "Upgrade"); + if (ws_sec_key_.empty()) { + ws_sec_key_ = "s//GYHa/XO7Hd2F2eOGfyA=="; // provide a random string. + } + add_header("Sec-WebSocket-Key", ws_sec_key_); + add_header("Sec-WebSocket-Version", "13"); +#ifdef CINATRA_ENABLE_GZIP + if (enable_ws_deflate_) + add_header("Sec-WebSocket-Extensions", + "permessage-deflate; client_max_window_bits"); +#endif + req_context<> ctx{}; + data = co_await async_request(std::move(uri), http_method::GET, + std::move(ctx)); + +#ifdef CINATRA_ENABLE_GZIP + if (enable_ws_deflate_) { + for (auto c : data.resp_headers) { + if (c.name == "Sec-WebSocket-Extensions") { + if (c.value.find("permessage-deflate;") != std::string::npos) { + is_server_support_ws_deflate_ = true; + } + else { + is_server_support_ws_deflate_ = false; + } + break; + } + } + } +#endif + co_return data; + } + data = co_await connect(u, eps); + } + if (socket_->is_timeout_) { + co_return resp_data{make_error_code(http_errc::connect_timeout), 404}; + } + if (!data.net_err) { + data.status = 200; + } + co_return data; + } + + bool has_closed() { return socket_->has_closed_; } + + [[nodiscard]] std::size_t get_pipeline_size() const noexcept { return 0; } + + const auto &get_headers() { return req_headers_; } + + void set_headers(std::unordered_map req_headers) { + req_headers_ = std::move(req_headers); + } + + bool add_header(std::string key, std::string val) { + if (key.empty()) + return false; + + req_headers_[key] = std::move(val); + + return true; + } + + void set_ws_sec_key(std::string sec_key) { ws_sec_key_ = std::move(sec_key); } + + /** + * @brief Set the max http body size object, in default it's INT64_MAX + * + * @param max_size + */ + void set_max_http_body_size(int64_t max_size) { + max_http_body_len_ = max_size; + } + + size_t available() { + std::error_code ec{}; + return socket_->impl_.available(ec); + } + + async_simple::coro::Lazy read_websocket() { + auto time_out_guard = + timer_guard(this, req_timeout_duration_, "websocket timer"); + co_return co_await async_read_ws(); + } + + async_simple::coro::Lazy write_websocket( + const char *data, opcode op = opcode::text) { + std::string str(data); + co_return co_await write_websocket(str, op); + } + + async_simple::coro::Lazy write_websocket( + const char *data, size_t size, opcode op = opcode::text) { + std::string str(data, size); + co_return co_await write_websocket(str, op); + } + + async_simple::coro::Lazy write_websocket( + std::string_view data, opcode op = opcode::text) { + std::string str(data); + co_return co_await write_websocket(str, op); + } + + async_simple::coro::Lazy write_websocket( + std::string &data, opcode op = opcode::text) { + co_return co_await write_websocket(std::span(data), op); + } + + async_simple::coro::Lazy write_websocket( + std::string &&data, opcode op = opcode::text) { + co_return co_await write_websocket(std::span(data), op); + } + + async_simple::coro::Lazy write_ws_frame(std::span msg, + websocket ws, opcode op, + resp_data &data, + bool eof = true) { + auto header = ws.encode_frame(msg, op, eof, enable_ws_deflate_); + std::vector buffers{ + asio::buffer(header), asio::buffer(msg.data(), msg.size())}; + + auto [ec, sz] = co_await async_write(buffers); + if (ec) { + data.net_err = ec; + data.status = 404; + } + } + +#ifdef CINATRA_ENABLE_GZIP + void gzip_compress(std::string_view source, std::string &dest_buf, + std::span &span, resp_data &data) { + if (enable_ws_deflate_ && is_server_support_ws_deflate_) { + if (cinatra::gzip_codec::deflate(source, dest_buf)) { + span = dest_buf; + } + else { + CINATRA_LOG_ERROR << "compress data error, data: " << source; + data.net_err = std::make_error_code(std::errc::protocol_error); + data.status = 404; + } + } + } +#endif + + template + async_simple::coro::Lazy write_websocket( + Source source, opcode op = opcode::text) { + resp_data data{}; + + websocket ws{}; + std::string close_str; + if (op == opcode::close) { + if constexpr (is_span_v) { + close_str = ws.format_close_payload(close_code::normal, source.data(), + source.size()); + source = {close_str.data(), close_str.size()}; + } + } + + std::span span{}; + if constexpr (is_span_v) { + span = {source.data(), source.size()}; +#ifdef CINATRA_ENABLE_GZIP + std::string dest_buf; + if (enable_ws_deflate_) { + gzip_compress({source.data(), source.size()}, dest_buf, span, data); + } +#endif + co_await write_ws_frame(span, ws, op, data, true); + } + else { + while (true) { + auto result = co_await source(); + span = {result.buf.data(), result.buf.size()}; +#ifdef CINATRA_ENABLE_GZIP + std::string dest_buf; + if (enable_ws_deflate_) { + gzip_compress({result.buf.data(), result.buf.size()}, dest_buf, span, + data); + } +#endif + co_await write_ws_frame(span, ws, op, data, result.eof); + + if (result.eof || data.status == 404) { + break; + } + } + } + + co_return data; + } + + async_simple::coro::Lazy write_websocket_close( + std::string msg = "") { + co_return co_await write_websocket(std::move(msg), opcode::close); + } + +#ifdef BENCHMARK_TEST + void set_bench_stop() { stop_bench_ = true; } +#endif + + async_simple::coro::Lazy async_patch( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::PATCH, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_options( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::OPTIONS, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_trace( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::TRACE, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_head( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::HEAD, + cinatra::req_context<>{}, std::move(headers)); + } + + // CONNECT example.com HTTP/1.1 + async_simple::coro::Lazy async_http_connect( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::CONNECT, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_get( + std::string uri, + std::unordered_map headers = {}) { + resp_data data{}; + req_context<> ctx{}; + data = co_await async_request(std::move(uri), http_method::GET, + std::move(ctx), std::move(headers)); +#ifdef BENCHMARK_TEST + data.total = total_len_; +#endif + if (redirect_uri_.empty() || !is_redirect(data)) { + co_return data; + } + else { + if (enable_follow_redirect_) + data = co_await async_request(std::move(redirect_uri_), + http_method::GET, std::move(ctx)); + co_return data; + } + } + + resp_data get(std::string uri, + std::unordered_map headers = {}) { + return async_simple::coro::syncAwait( + async_get(std::move(uri), std::move(headers))); + } + + resp_data post(std::string uri, std::string content, + req_content_type content_type, + std::unordered_map headers = {}) { + return async_simple::coro::syncAwait(async_post( + std::move(uri), std::move(content), content_type, std::move(headers))); + } + + async_simple::coro::Lazy async_post( + std::string uri, std::string content, req_content_type content_type, + std::unordered_map headers = {}) { + req_context<> ctx{content_type, "", std::move(content)}; + return async_request(std::move(uri), http_method::POST, std::move(ctx), + std::move(headers)); + } + + async_simple::coro::Lazy async_delete( + std::string uri, std::string content, req_content_type content_type, + std::unordered_map headers = {}) { + req_context<> ctx{content_type, "", std::move(content)}; + return async_request(std::move(uri), http_method::DEL, std::move(ctx), + std::move(headers)); + } + + async_simple::coro::Lazy async_put( + std::string uri, std::string content, req_content_type content_type, + std::unordered_map headers = {}) { + req_context<> ctx{content_type, "", std::move(content)}; + return async_request(std::move(uri), http_method::PUT, std::move(ctx), + std::move(headers)); + } + + bool add_str_part(std::string name, std::string content) { + size_t size = content.size(); + return form_data_ + .emplace(std::move(name), multipart_t{"", std::move(content), size}) + .second; + } + + bool add_file_part(std::string name, std::string filename) { + if (form_data_.find(name) != form_data_.end()) { + CINATRA_LOG_WARNING << "name already exist: " << name; + return false; + } + + std::error_code ec; + bool r = std::filesystem::exists(filename, ec); + if (!r || ec) { + if (ec) { + CINATRA_LOG_WARNING << ec.message(); + } + CINATRA_LOG_WARNING << "file not exists, " + << std::filesystem::current_path().string(); + return false; + } + + size_t file_size = std::filesystem::file_size(filename); + form_data_.emplace(std::move(name), + multipart_t{std::move(filename), "", file_size}); + return true; + } + + void set_max_single_part_size(size_t size) { max_single_part_size_ = size; } + + struct timer_guard { + timer_guard(coro_http_client *self, + std::chrono::steady_clock::duration duration, std::string msg) + : self(self), dur_(duration) { + self->socket_->is_timeout_ = false; + + if (duration.count() >= 0) { + self->timeout(self->timer_, duration, std::move(msg)) + .start([](auto &&) { + }); + } + return; + } + ~timer_guard() { + if (dur_.count() > 0 && self->socket_->is_timeout_ == false) { + std::error_code ignore_ec; + self->timer_.cancel(ignore_ec); + } + } + coro_http_client *self; + std::chrono::steady_clock::duration dur_; + }; + + async_simple::coro::Lazy async_download(std::string uri, + std::string filename, + std::string range = "") { + resp_data data{}; + coro_io::coro_file file; + file.open(filename, std::ios::trunc | std::ios::out); + if (!file.is_open()) { + data.net_err = std::make_error_code(std::errc::no_such_file_or_directory); + data.status = 404; + co_return data; + } + + req_context<> ctx{}; + if (range.empty()) { + add_header("Transfer-Encoding", "chunked"); + ctx = {req_content_type::none, "", "", &file}; + } + else { + std::string req_str = "Range: bytes="; + req_str.append(range).append(CRCF); + ctx = {req_content_type::none, std::move(req_str), {}, &file}; + } + + data = co_await async_request(std::move(uri), http_method::GET, + std::move(ctx)); + + co_return data; + } + + resp_data download(std::string uri, std::string filename, + std::string range = "") { + return async_simple::coro::syncAwait( + async_download(std::move(uri), std::move(filename), std::move(range))); + } + + bool is_body_in_out_buf() const { return !out_buf_.empty(); } + + void reset() { + if (!has_closed()) { + close_socket(*socket_); + } + + socket_->impl_ = asio::ip::tcp::socket{executor_wrapper_.context()}; + if (!socket_->impl_.is_open()) { + std::error_code ec; + socket_->impl_.open(asio::ip::tcp::v4(), ec); + if (ec) { + CINATRA_LOG_WARNING << "client reset socket failed, reason: " + << ec.message(); + return; + } + } + + socket_->has_closed_ = true; +#ifdef CINATRA_ENABLE_SSL + need_set_sni_host_ = true; + if (has_init_ssl_) { + socket_->ssl_stream_ = nullptr; + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + has_init_ssl_ = false; + } +#endif +#ifdef BENCHMARK_TEST + total_len_ = 0; +#endif + + // clear + head_buf_.consume(head_buf_.size()); + chunked_buf_.consume(chunked_buf_.size()); + resp_chunk_str_.clear(); + } + + std::string_view get_host() { return host_; } + + std::string_view get_port() { return port_; } + + private: + async_simple::coro::Lazy send_file_copy_with_chunked( + std::string_view source, std::error_code &ec) { + std::string file_data; + detail::resize(file_data, max_single_part_size_); + coro_io::coro_file file{}; + file.open(source, std::ios::in); + if (!file.is_open()) { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + while (!file.eof()) { + auto [rd_ec, rd_size] = + co_await file.async_read(file_data.data(), file_data.size()); + std::vector bufs; + std::string size_str; + cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, + file.eof()); + std::size_t size; + if (std::tie(ec, size) = co_await async_write(bufs); ec) { + break; + } + } + } + + async_simple::coro::Lazy send_file_copy_with_length( + std::string_view source, std::error_code &ec, std::size_t length, + std::size_t offset) { + if (length <= 0) { + co_return; + } + std::string file_data; + detail::resize(file_data, (std::min)(max_single_part_size_, length)); + coro_io::coro_file file{}; + file.open(source, std::ios::in); + if (!file.is_open()) { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + file.seek(offset, std::ios::cur); + std::size_t size; + while (length > 0) { + if (std::tie(ec, size) = co_await file.async_read( + file_data.data(), (std::min)(file_data.size(), length)); + ec) { + // bad request, file may smaller than content-length + break; + } + length -= size; + if (length > 0 && file.eof()) { + // bad request, file may smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + break; + } + if (std::tie(ec, size) = + co_await async_write(asio::buffer(file_data.data(), size)); + ec) { + break; + } + } + } +#ifdef __linux__ + struct fd_guard { + int fd; + fd_guard(const char *file_path) : fd(::open(file_path, O_RDONLY)) {} + ~fd_guard() { + if (fd >= 0) { + ::close(fd); + } + } + }; + async_simple::coro::Lazy send_file_no_copy_with_length( + const std::filesystem::path &source, std::error_code &ec, + std::size_t length, std::size_t offset) { + fd_guard guard(source.c_str()); + if (guard.fd < 0) [[unlikely]] { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + std::size_t actual_len = 0; + std::tie(ec, actual_len) = co_await coro_io::async_sendfile( + socket_->impl_, guard.fd, offset, length); + if (ec) [[unlikely]] { + co_return; + } + if (actual_len != length) [[unlikely]] { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + co_return; + } + } + async_simple::coro::Lazy send_file_no_copy_with_chunked( + const std::filesystem::path &source, std::error_code &ec) { + fd_guard guard(source.c_str()); + if (guard.fd < 0) [[unlikely]] { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + off_t now_position = 0, + max_position = std::filesystem::file_size(source, ec); + if (ec) { + co_return; + } + size_t len = + std::min(max_single_part_size_, max_position - now_position); + // send chunked + std::array chunked_buffer; + std::size_t sz; + std::tie(ec, sz) = co_await async_write( + asio::buffer(get_chuncked_buffers(len, chunked_buffer))); + if (ec) [[unlikely]] { + co_return; + } + do { + std::size_t actual_len = 0; + std::tie(ec, actual_len) = co_await coro_io::async_sendfile( + socket_->impl_, guard.fd, now_position, len); + if (ec) [[unlikely]] { + co_return; + } + if (actual_len != len) [[unlikely]] { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + co_return; + } + if (now_position += actual_len; now_position < max_position) { + len = std::min(max_single_part_size_, + max_position - now_position); + std::tie(ec, sz) = co_await async_write(asio::buffer( + get_chuncked_buffers(len, chunked_buffer))); + if (ec) { + co_return; + } + } + else [[unlikely]] { + std::tie(ec, sz) = co_await async_write(asio::buffer( + get_chuncked_buffers(len, chunked_buffer))); + if (ec) { + co_return; + } + break; + } + } while (true); + } +#endif + template + static std::size_t getRemainingBytes(stream &file) { + auto current_pos = file.tellg(); + file.seekg(0, std::ios::end); + auto end_pos = file.tellg(); + auto remaining_bytes = end_pos - current_pos; + file.seekg(current_pos); + return remaining_bytes; + } + + template + void check_source(resp_data &data, Source &source) { + if constexpr (is_stream_ptr_v) { + if (!source) { + data = resp_data{ + std::make_error_code(std::errc::no_such_file_or_directory), 404}; + } + } + else if constexpr (std::is_same_v || + std::is_same_v) { + if (!std::filesystem::exists(source)) { + data = resp_data{ + std::make_error_code(std::errc::no_such_file_or_directory), 404}; + } + } + } + + void handle_upload_header_with_multipart() { + size_t content_len = multipart_content_len(); + add_header("Content-Length", std::to_string(content_len)); + } + + void handle_upload_header_with_chunked( + std::unordered_map &headers) { + if (!resp_chunk_str_.empty()) { + resp_chunk_str_.clear(); + } + + if (headers.empty()) { + add_header("Transfer-Encoding", "chunked"); + } + else { + headers.emplace("Transfer-Encoding", "chunked"); + } + } + + template + int64_t handle_upload_header_with_length( + resp_data &data, Source &source, + std::unordered_map &headers, uint64_t offset, + int64_t content_length) { + if (content_length < 0) { + if constexpr (is_stream_ptr_v) { + content_length = getRemainingBytes(*source); + } + else if constexpr (std::is_same_v || + std::is_same_v) { + content_length = std::filesystem::file_size(source); + } + else { + CINATRA_LOG_ERROR + << "user should set content-length before calling async_upload " + "when source is user-defined function."; + data = + resp_data{std::make_error_code(std::errc::invalid_argument), 404}; + return content_length; + } + content_length -= offset; + if (content_length < 0) { + CINATRA_LOG_ERROR << "the offset is larger than the end of file"; + data = + resp_data{std::make_error_code(std::errc::invalid_argument), 404}; + return content_length; + } + } + + assert(content_length >= 0); + char buf[32]; + auto [ptr, _] = std::to_chars(buf, buf + 32, content_length); + if (headers.empty()) { + add_header("Content-Length", std::string(buf, ptr - buf)); + } + else { + headers.emplace("Content-Length", std::string_view(buf, ptr - buf)); + } + return content_length; + } + + async_simple::coro::Lazy send_fstream_with_multipart( + std::error_code &ec) { + resp_data data{}; + for (auto &[key, part] : form_data_) { + data = co_await send_single_part(key, part); + + if (data.net_err) { + ec = data.net_err; + co_return; + } + } + + std::string last_part; + size_t size = 0; + last_part.append("--").append(BOUNDARY).append("--").append(CRCF); + if (std::tie(ec, size) = co_await async_write(asio::buffer(last_part)); + ec) { + co_return; + } + } + + template + async_simple::coro::Lazy send_fstream_with_chunked( + Source &source, std::error_code &ec) { + size_t size = 0; + std::string file_data; + detail::resize(file_data, max_single_part_size_); + while (!source->eof()) { + size_t rd_size = + source->read(file_data.data(), file_data.size()).gcount(); + std::vector bufs; + std::string size_str; + cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, + source->eof()); + if (std::tie(ec, size) = co_await async_write(bufs); ec) { + break; + } + } + } + + template + async_simple::coro::Lazy send_fstream_with_length( + Source &source, std::error_code &ec, uint64_t offset, + int64_t content_length) { + size_t size = 0; + source->seekg(offset, std::ios::cur); + std::string file_data; + detail::resize(file_data, std::min(max_single_part_size_, + content_length)); + while (content_length > 0 && !source->eof()) { + size_t rd_size = + source + ->read(file_data.data(), + std::min(content_length, file_data.size())) + .gcount(); + if (std::tie(ec, size) = + co_await async_write(asio::buffer(file_data.data(), rd_size)); + ec) { + break; + } + content_length -= rd_size; + } + if (!ec && content_length > 0) { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + } + } + + template + async_simple::coro::Lazy send_sink_with_chunked(Source &source, + std::error_code &ec) { + size_t size = 0; + while (true) { + auto result = co_await source(); + std::vector bufs; + std::string size_str; + cinatra::to_chunked_buffers( + bufs, size_str, {result.buf.data(), result.buf.size()}, result.eof); + if (std::tie(ec, size) = co_await async_write(bufs); ec) { + break; + } + if (result.eof) { + break; + } + } + } + + template + async_simple::coro::Lazy send_sink_with_length(Source &source, + std::error_code &ec, + int64_t content_length) { + size_t size = 0; + while (true) { + auto result = co_await source(); + if (std::tie(ec, size) = co_await async_write(asio::buffer( + result.buf.data(), + std::min(content_length, result.buf.size()))); + ec) { + break; + } + content_length -= size; + if (content_length <= 0) { + break; + } + else if (result.eof) [[unlikely]] { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + break; + } + } + } + + async_simple::coro::Lazy reconnect(resp_data &data, uri_t u) { + data = co_await connect(u); + if (socket_->is_timeout_) { + data = resp_data{make_error_code(http_errc::connect_timeout), 404}; + } + if (data.net_err) { + co_return false; + } + + co_return true; + } + + void handle_upload_timeout_error(std::error_code &ec) { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (write_header_timeout_ || write_payload_timeout_ || read_timeout_) { + socket_->is_timeout_ = true; + } +#endif + if (socket_->is_timeout_) { + ec = make_error_code(http_errc::request_timeout); + } + } + + template + async_simple::coro::Lazy async_upload_impl( + S uri, http_method method, Source source /* file */, + req_content_type content_type = req_content_type::text, + std::unordered_map headers = {}, + uint64_t offset = 0 /*file offset*/, + int64_t content_length = -1 /*upload size*/) { + std::error_code ec{}; + size_t size = 0; + bool is_keep_alive = true; + req_context<> ctx{content_type}; + resp_data data{}; + + out_buf_ = {}; + + std::shared_ptr guard(nullptr, [&, this](auto) { + if (!req_headers_.empty()) { + req_headers_.clear(); + } + }); + + auto [ok, u] = handle_uri(data, uri); + if (!ok) { + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; + } + + if constexpr (upload_type != upload_type_t::multipart) { + check_source(data, source); + if (data.status != 0) { + co_return data; + } + } + + if constexpr (upload_type == upload_type_t::with_length) { + content_length = handle_upload_header_with_length(data, source, headers, + offset, content_length); + if (data.status != 0) { + co_return data; + } + } + else if constexpr (upload_type == upload_type_t::chunked) { + handle_upload_header_with_chunked(headers); + } + else if constexpr (upload_type == upload_type_t::multipart) { + handle_upload_header_with_multipart(); + } + + std::string header_str = + build_request_header(u, method, ctx, true, std::move(headers)); + + if (socket_->has_closed_) { + if (bool r = co_await reconnect(data, u); !r) { + co_return data; + } + } + + auto time_guard = timer_guard(this, req_timeout_duration_, "request timer"); + std::tie(ec, size) = co_await async_write(asio::buffer(header_str)); + if (ec) { + handle_upload_timeout_error(ec); + co_return resp_data{ec, 404}; + } + + constexpr bool is_stream_file = is_stream_ptr_v; + if constexpr (is_stream_file) { + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_fstream_with_length(source, ec, offset, content_length); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_fstream_with_chunked(source, ec); + } + } + else if constexpr (std::is_enum_v) { // only for multipart + co_await send_fstream_with_multipart(ec); + } + else if constexpr (std::is_same_v || + std::is_same_v) { +#ifdef __linux__ +#ifdef CINATRA_ENABLE_SSL + if (!has_init_ssl_) { +#endif + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_file_no_copy_with_length(std::filesystem::path{source}, + ec, content_length, offset); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_file_no_copy_with_chunked(std::filesystem::path{source}, + ec); + } +#ifdef CINATRA_ENABLE_SSL + } + else { + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_file_copy_with_length(source, ec, content_length, + offset); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_file_copy_with_chunked(source, ec); + } + } +#endif +#else + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_file_copy_with_length(source, ec, content_length, offset); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_file_copy_with_chunked(source, ec); + } +#endif + } + else { + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_sink_with_length(source, ec, content_length); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_sink_with_chunked(source, ec); + } + } + if (ec) { + handle_upload_timeout_error(ec); + co_return resp_data{ec, 404}; + } + + data = co_await handle_read(ec, size, is_keep_alive, std::move(ctx), + http_method::POST); + if (ec) { + handle_upload_timeout_error(ec); + } + handle_result(data, ec, is_keep_alive); + co_return data; + } + + public: + // send file with length + template + async_simple::coro::Lazy async_upload( + S uri, http_method method, Source source /* file */, + uint64_t offset = 0 /*file offset*/, + int64_t content_length = -1 /*upload size*/, + req_content_type content_type = req_content_type::text, + std::unordered_map headers = {}) { + return async_upload_impl( + std::move(uri), method, std::move(source), content_type, + std::move(headers), offset, content_length); + } + + // send file with chunked + template + async_simple::coro::Lazy async_upload_chunked( + S uri, http_method method, Source source, + req_content_type content_type = req_content_type::text, + std::unordered_map headers = {}) { + return async_upload_impl( + std::move(uri), method, std::move(source), content_type, + std::move(headers)); + } + + // send multipart data, should call add_file_part or add_str_part firstly. + async_simple::coro::Lazy async_upload_multipart(std::string uri) { + if (form_data_.empty()) { + CINATRA_LOG_WARNING << "no multipart"; + co_return resp_data{std::make_error_code(std::errc::invalid_argument), + 404}; + } + + co_return co_await async_upload_impl( + std::move(uri), http_method::POST, upload_type_t::multipart, + req_content_type::multipart); + } + + async_simple::coro::Lazy async_upload_multipart( + std::string uri, std::string name, std::string filename) { + if (!add_file_part(std::move(name), std::move(filename))) { + CINATRA_LOG_WARNING << "open file failed or duplicate test names"; + co_return resp_data{std::make_error_code(std::errc::invalid_argument), + 404}; + } + co_return co_await async_upload_multipart(std::move(uri)); + } + + template + async_simple::coro::Lazy async_request( + S uri, http_method method, req_context ctx, + std::unordered_map headers = {}, + std::span out_buf = {}) { + if (!resp_chunk_str_.empty()) { + resp_chunk_str_.clear(); + } + if (!body_.empty()) { + body_.clear(); + } + + out_buf_ = out_buf; + + std::shared_ptr guard(nullptr, [this](auto) { + if (!req_headers_.empty()) { + req_headers_.clear(); + } + }); + + resp_data data{}; + + std::error_code ec{}; + size_t size = 0; + bool is_keep_alive = true; + + do { + uri_t u; + std::string append_uri; + + if (socket_->has_closed_ || (!uri.empty() && uri[0] != '/')) { + bool no_schema = !has_schema(uri); + + if (no_schema) { +#ifdef CINATRA_ENABLE_SSL + if (is_ssl_schema_) { + append_uri.append("https://").append(uri); + } + else +#endif + { + append_uri.append("http://").append(uri); + } + } + bool ok = false; + std::tie(ok, u) = handle_uri(data, no_schema ? append_uri : uri); + if (!ok) { + break; + } + } + else { + u.path = uri; + } + if (socket_->has_closed_) { + data = co_await connect(u); + if (data.status != 0) { + co_return data; + } + } + + std::vector vec; + std::string req_head_str = + build_request_header(u, method, ctx, false, std::move(headers)); + + bool has_body = !ctx.content.empty(); + if (has_body) { + vec.push_back(asio::buffer(req_head_str)); + vec.push_back(asio::buffer(ctx.content.data(), ctx.content.size())); + } + +#ifdef CORO_HTTP_PRINT_REQ_HEAD + CINATRA_LOG_DEBUG << req_head_str; +#endif + auto guard = timer_guard(this, req_timeout_duration_, "request timer"); + if (has_body) { + std::tie(ec, size) = co_await async_write(vec); + } + else { + std::tie(ec, size) = co_await async_write(asio::buffer(req_head_str)); + } + if (ec) { + break; + } + data = + co_await handle_read(ec, size, is_keep_alive, std::move(ctx), method); + } while (0); + if (ec && socket_->is_timeout_) { + ec = make_error_code(http_errc::request_timeout); + } + handle_result(data, ec, is_keep_alive); + co_return data; + } + + async_simple::coro::Lazy handle_shake() { +#ifdef CINATRA_ENABLE_SSL + if (!has_init_ssl_) { + bool r = init_ssl(asio::ssl::verify_none, "", host_); + if (!r) { + co_return std::make_error_code(std::errc::invalid_argument); + } + } + + if (socket_->ssl_stream_ == nullptr) { + co_return std::make_error_code(std::errc::not_a_stream); + } + + auto ec = co_await coro_io::async_handshake(socket_->ssl_stream_, + asio::ssl::stream_base::client); + if (ec) { + CINATRA_LOG_ERROR << "handle failed " << ec.message(); + } + co_return ec; +#else + // please open CINATRA_ENABLE_SSL before request https! + co_return std::make_error_code(std::errc::protocol_error); +#endif + } + +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + async_simple::coro::Lazy async_write_raw( + std::string_view data) { + auto [ec, _] = co_await async_write(asio::buffer(data)); + co_return ec; + } + + async_simple::coro::Lazy async_read_raw( + http_method method, bool clear_buffer = false) { + if (clear_buffer) { + body_.clear(); + } + + char buf[1024]; + std::error_code ec{}; + size_t size{}; +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + std::tie(ec, size) = co_await coro_io::async_read_some( + *socket_->ssl_stream_, asio::buffer(buf, 1024)); + } + else { +#endif + std::tie(ec, size) = co_await coro_io::async_read_some( + socket_->impl_, asio::buffer(buf, 1024)); +#ifdef CINATRA_ENABLE_SSL + } +#endif + body_.append(buf, size); + + co_return resp_data{ec, {}, {}, body_}; + } +#endif + + inline void set_proxy(const std::string &host, const std::string &port) { + proxy_host_ = host; + proxy_port_ = port; + } + + inline void set_proxy_basic_auth(const std::string &username, + const std::string &password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; + } + + inline void set_proxy_bearer_token_auth(const std::string &token) { + proxy_bearer_token_auth_token_ = token; + } + + inline void enable_auto_redirect(bool enable_follow_redirect) { + enable_follow_redirect_ = enable_follow_redirect; + } + +#ifdef CINATRA_ENABLE_SSL + void set_ssl_schema(bool r) { is_ssl_schema_ = r; } +#ifdef YLT_ENABLE_NTLS + void set_ntls_schema(bool r) { use_ntls_ = r; } + + /*! + * Initialize NTLS client with dual certificates + */ + bool init_ntls_client(const std::string &sign_cert_file, + const std::string &sign_key_file, + const std::string &enc_cert_file, + const std::string &enc_key_file, + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none, + const std::string &passwd = "") { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + + // Set NTLS cipher suites + if (!SSL_CTX_set_cipher_list(ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + + if (!passwd.empty()) { + ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { + return pwd; + }); + } + + // Load client certificates if provided (for mutual authentication) + if (!sign_cert_file.empty() && !sign_key_file.empty()) { + if (!SSL_CTX_use_sign_certificate_file(ssl_ctx_->native_handle(), + sign_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client SM2 signing certificate"; + return false; + } + + if (!SSL_CTX_use_sign_PrivateKey_file(ssl_ctx_->native_handle(), + sign_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client SM2 signing private key"; + return false; + } + } + + if (!enc_cert_file.empty() && !enc_key_file.empty()) { + if (!SSL_CTX_use_enc_certificate_file(ssl_ctx_->native_handle(), + enc_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR + << "failed to load client SM2 encryption certificate"; + return false; + } + + if (!SSL_CTX_use_enc_PrivateKey_file(ssl_ctx_->native_handle(), + enc_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR + << "failed to load client SM2 encryption private key"; + return false; + } + } + + // Load CA certificate if provided + if (!ca_cert_file.empty()) { + if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), + ca_cert_file.c_str(), nullptr)) { + CINATRA_LOG_WARNING << "failed to load CA certificate"; + } + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + has_init_ssl_ = true; + use_ntls_ = true; + return true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init NTLS client failed: " << e.what(); + return false; + } + } + /*! + * Initialize NTLS client with TLS 1.3 + GM single certificate mode (RFC 8998) + */ + bool init_ntls_tls13_gm_client( + const std::string &gm_cert_file = "", const std::string &gm_key_file = "", + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none, + const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", + const std::string &passwd = "") { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(TLS_client_method())); + + // Configure TLS 1.3 + GM mode (RFC 8998) + SSL_CTX_set_min_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); + + // Enable strict SM TLS 1.3 mode (Tongsuo specific) + SSL_CTX_enable_sm_tls13_strict(ssl_ctx_->native_handle()); + + // Set TLS 1.3 GM cipher suites + if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), + cipher_suites.c_str())) { + CINATRA_LOG_WARNING << "failed to set TLS 1.3 GM cipher suites"; + } + + // Set GM signature algorithms (required for SM TLS 1.3 strict mode) + if (!SSL_CTX_set1_sigalgs_list(ssl_ctx_->native_handle(), "sm2sig_sm3")) { + CINATRA_LOG_WARNING << "failed to set GM signature algorithms"; + } + + // Set GM curves (required for SM TLS 1.3 strict mode) + if (!SSL_CTX_set1_curves_list(ssl_ctx_->native_handle(), "SM2")) { + CINATRA_LOG_WARNING << "failed to set GM curves"; + } + + if (!passwd.empty()) { + ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { + return pwd; + }); + } + + // Load client certificate if provided (for mutual authentication) + if (!gm_cert_file.empty() && !gm_key_file.empty()) { + if (!SSL_CTX_use_certificate_file(ssl_ctx_->native_handle(), + gm_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client GM certificate"; + return false; + } + + if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_->native_handle(), + gm_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client GM private key"; + return false; + } + } + + // Load CA certificate if provided + if (!ca_cert_file.empty()) { + if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), + ca_cert_file.c_str(), nullptr)) { + CINATRA_LOG_WARNING << "failed to load CA certificate"; + } + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + has_init_ssl_ = true; + use_ntls_ = true; + return true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init TLS 1.3 + GM client failed: " << e.what(); + return false; + } + } + +#endif // YLT_ENABLE_NTLS +#endif + + std::string get_redirect_uri() { return redirect_uri_; } + + bool is_redirect(resp_data &data) { + if (data.status > 299 && data.status <= 399) + return true; + return false; + } + + void set_conn_timeout(std::chrono::steady_clock::duration timeout_duration) { + conn_timeout_duration_ = timeout_duration; + } + + void set_req_timeout(std::chrono::steady_clock::duration timeout_duration) { + req_timeout_duration_ = timeout_duration; + } + + void set_chunked_callback( + std::function(std::string_view)> cb) { + chunked_cb_ = std::move(cb); + } + + bool has_chunked_callback() const { return chunked_cb_ != nullptr; } + +#ifdef CINATRA_ENABLE_SSL + void enable_sni_hostname(bool r) { need_set_sni_host_ = r; } +#endif + + template + friend class coro_io::client_pool; + + private: + struct socket_t { + asio::ip::tcp::socket impl_; + std::atomic has_closed_ = true; + std::atomic is_timeout_ = false; + asio::streambuf head_buf_; + asio::streambuf chunked_buf_; +#ifdef CINATRA_ENABLE_SSL + std::unique_ptr> ssl_stream_; +#endif + template + socket_t(ioc_t &&ioc) : impl_(std::forward(ioc)) {} + }; + static bool is_ok(const resp_data &data) noexcept { + return data.net_err == std::error_code{}; + } + + template + std::pair handle_uri(resp_data &data, const S &uri) { + uri_t u; + if (!u.parse_from(uri.data())) { + CINATRA_LOG_WARNING + << uri + << ", the url is not right, maybe need to encode the url firstly"; + data.net_err = std::make_error_code(std::errc::protocol_error); + data.status = 404; + return {false, {}}; + } + + if (u.host.front() == '[') { // for ipv6 + if (u.host.size() > 2) + u.host = u.host.substr(1, u.host.size() - 2); + } + + // construct proxy request uri + construct_proxy_uri(u); + + return {true, u}; + } + + void construct_proxy_uri(uri_t &u) { + if (!proxy_host_.empty() && !proxy_port_.empty()) { + if (!proxy_request_uri_.empty()) + proxy_request_uri_.clear(); + if (u.get_port() == "80") { + proxy_request_uri_.append("http://").append(u.get_host()).append(":80"); + } + else if (u.get_port() == "443") { + proxy_request_uri_.append("https://") + .append(u.get_host()) + .append(":443"); + } + else { + // all be http + proxy_request_uri_.append("http://") + .append(u.get_host()) + .append(":") + .append(u.get_port()); + } + proxy_request_uri_.append(u.get_path()); + u.path = std::string_view(proxy_request_uri_); + } + } + + std::string build_request_header( + const uri_t &u, http_method method, const auto &ctx, + bool already_has_len = false, + std::unordered_map headers = {}) { + std::string req_str(method_name(method)); + + req_str.append(" ").append(u.get_path()); + if (!u.query.empty()) { + req_str.append("?").append(u.query); + } + + if (!headers.empty()) { + req_headers_ = std::move(headers); + req_str.append(" HTTP/1.1\r\n"); + } + else { + if (req_headers_.find("Host") == req_headers_.end()) { + req_str.append(" HTTP/1.1\r\nHost:").append(u.host).append("\r\n"); + } + else { + req_str.append(" HTTP/1.1\r\n"); + } + } + + auto type_str = get_content_type_str(ctx.content_type); + if (!type_str.empty()) { + if (ctx.content_type == req_content_type::multipart) { + type_str.append(BOUNDARY); + } + req_headers_["Content-Type"] = std::move(type_str); + } + + bool has_connection = false; + // add user headers + if (!req_headers_.empty()) { + for (auto &pair : req_headers_) { + if (pair.first == "Connection") { + has_connection = true; + } + req_str.append(pair.first) + .append(": ") + .append(pair.second) + .append("\r\n"); + } + } + + if (!has_connection) { + req_str.append("Connection: keep-alive\r\n"); + } + + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + std::string basic_auth_str = "Proxy-Authorization: Basic "; + std::string basic_base64_str = base64_encode( + proxy_basic_auth_username_ + ":" + proxy_basic_auth_password_); + req_str.append(basic_auth_str).append(basic_base64_str).append(CRCF); + } + + if (!proxy_bearer_token_auth_token_.empty()) { + std::string bearer_token_str = "Proxy-Authorization: Bearer "; + req_str.append(bearer_token_str) + .append(proxy_bearer_token_auth_token_) + .append(CRCF); + } + + if (!ctx.req_header.empty()) + req_str.append(ctx.req_header); + size_t content_len = ctx.content.size(); + bool should_add_len = false; + if (content_len > 0) { + should_add_len = true; + } + else { + if ((method == http_method::POST || method == http_method::PUT) && + ctx.content_type != req_content_type::multipart) { + should_add_len = true; + } + } + + if (req_headers_.find("Content-Length") != req_headers_.end()) { + should_add_len = false; + } + + if (already_has_len) { + should_add_len = false; + } + + if (should_add_len) { + char buf[32]; + auto [ptr, ec] = std::to_chars(buf, buf + 32, content_len); + req_str.append("Content-Length: ") + .append(std::string_view(buf, ptr - buf)) + .append("\r\n"); + } + + req_str.append("\r\n"); + return req_str; + } + + std::error_code handle_header(resp_data &data, http_parser &parser, + size_t header_size) { + // parse header + const char *data_ptr = asio::buffer_cast(head_buf_.data()); + + int parse_ret = parser.parse_response(data_ptr, header_size, 0); +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (parse_failed_forever_) { + parse_ret = -1; + } +#endif + if (parse_ret < 0) [[unlikely]] { + head_buf_.consume(head_buf_.size()); + return std::make_error_code(std::errc::protocol_error); + } + + if (parser_.body_len() > max_http_body_len_ || parser_.body_len() < 0) + [[unlikely]] { + CINATRA_LOG_ERROR << "invalid http content length: " + << parser_.body_len(); + head_buf_.consume(head_buf_.size()); + return std::make_error_code(std::errc::invalid_argument); + } + + head_buf_.consume(header_size); // header size + data.resp_headers = parser.get_headers(); + data.status = parser.status(); + return {}; + } + + template + async_simple::coro::Lazy handle_read(std::error_code &ec, + size_t &size, + bool &is_keep_alive, + req_context ctx, + http_method method) { + resp_data data{}; + do { + if (std::tie(ec, size) = co_await async_read_until(head_buf_, TWO_CRCF); + ec) { + break; + } + + ec = handle_header(data, parser_, size); + if (ec) { + break; + } + + is_keep_alive = parser_.keep_alive(); + if (method == http_method::HEAD) { + co_return data; + } + + bool is_out_buf = false; + + bool is_ranges = parser_.is_resp_ranges(); + if (is_ranges) { + is_keep_alive = true; + } + if (parser_.is_chunked()) { + out_buf_ = {}; + is_keep_alive = true; + if (head_buf_.size() > 0) { + const char *data_ptr = + asio::buffer_cast(head_buf_.data()); + chunked_buf_.sputn(data_ptr, head_buf_.size()); + head_buf_.consume(head_buf_.size()); + } + ec = co_await handle_chunked(data, std::move(ctx)); + break; + } + + if (parser_.is_multipart()) { + out_buf_ = {}; + is_keep_alive = true; + if (head_buf_.size() > 0) { + const char *data_ptr = + asio::buffer_cast(head_buf_.data()); + chunked_buf_.sputn(data_ptr, head_buf_.size()); + head_buf_.consume(head_buf_.size()); + } + ec = co_await handle_multipart(data, std::move(ctx)); + break; + } + + redirect_uri_.clear(); + bool is_redirect = parser_.is_location(); + if (is_redirect) + redirect_uri_ = parser_.get_header_value("Location"); + + if (!parser_.get_header_value("Content-Encoding").empty()) { + if (parser_.get_header_value("Content-Encoding").find("gzip") != + std::string_view::npos) + encoding_type_ = content_encoding::gzip; + else if (parser_.get_header_value("Content-Encoding").find("deflate") != + std::string_view::npos) + encoding_type_ = content_encoding::deflate; + else if (parser_.get_header_value("Content-Encoding").find("br") != + std::string_view::npos) + encoding_type_ = content_encoding::br; + } + else { + encoding_type_ = content_encoding::none; + } + + size_t content_len = (size_t)parser_.body_len(); +#ifdef BENCHMARK_TEST + total_len_ = parser_.total_len(); +#endif + + is_out_buf = !out_buf_.empty(); + if (is_out_buf) { + if (content_len > 0 && out_buf_.size() < content_len) { + out_buf_ = {}; + is_out_buf = false; + } + } + + if (content_len <= head_buf_.size()) { + // Now get entire content, additional data will discard. + // copy body. + if (content_len > 0) { + auto data_ptr = asio::buffer_cast(head_buf_.data()); + if (is_out_buf) { + memcpy(out_buf_.data(), data_ptr, content_len); + } + else { + detail::resize(body_, content_len); + memcpy(body_.data(), data_ptr, content_len); + } + head_buf_.consume(head_buf_.size()); + } + co_await handle_entire_content(data, content_len, is_ranges, ctx); + break; + } + + // read left part of content. + size_t part_size = head_buf_.size(); + size_t size_to_read = content_len - part_size; + + auto data_ptr = asio::buffer_cast(head_buf_.data()); + if (is_out_buf) { + memcpy(out_buf_.data(), data_ptr, part_size); + } + else { + detail::resize(body_, content_len); + memcpy(body_.data(), data_ptr, part_size); + } + + head_buf_.consume(part_size); + + if (is_out_buf) { + if (std::tie(ec, size) = co_await async_read( + asio::buffer(out_buf_.data() + part_size, size_to_read), + size_to_read); + ec) { + break; + } + } + else { + if (std::tie(ec, size) = co_await async_read( + asio::buffer(body_.data() + part_size, size_to_read), + size_to_read); + ec) { + break; + } + } + + // Now get entire content, additional data will discard. + co_await handle_entire_content(data, content_len, is_ranges, ctx); + } while (0); + + if (!resp_chunk_str_.empty()) { + data.resp_body = + std::string_view{resp_chunk_str_.data(), resp_chunk_str_.size()}; + } + + co_return data; + } + + async_simple::coro::Lazy handle_entire_content(resp_data &data, + size_t content_len, + bool is_ranges, + auto &ctx) { + if (content_len > 0) { + const char *data_ptr; + if (head_buf_.size() == 0) { + if (out_buf_.empty()) { + data_ptr = body_.data(); + } + else { + data_ptr = out_buf_.data(); + } + } + else { + data_ptr = asio::buffer_cast(head_buf_.data()); + } + + if (is_ranges) { + if (ctx.resp_body_stream) { + auto [ec, size] = co_await ctx.resp_body_stream->async_write( + {data_ptr, content_len}); + if (ec) { + data.net_err = ec; + co_return; + } + } + } + + std::string_view reply(data_ptr, content_len); +#ifdef CINATRA_ENABLE_GZIP + if (encoding_type_ == content_encoding::gzip) { + uncompressed_str_.clear(); + bool r = gzip_codec::uncompress(reply, uncompressed_str_); + if (r) + data.resp_body = uncompressed_str_; + else + data.resp_body = reply; + } + else if (encoding_type_ == content_encoding::deflate) { + uncompressed_str_.clear(); + bool r = gzip_codec::inflate(reply, uncompressed_str_); + if (r) + data.resp_body = uncompressed_str_; + else + data.resp_body = reply; + } +#endif +#if defined(CINATRA_ENABLE_BROTLI) && defined(CINATRA_ENABLE_GZIP) + else if (encoding_type_ == content_encoding::br) +#endif +#if defined(CINATRA_ENABLE_BROTLI) && !defined(CINATRA_ENABLE_GZIP) + if (encoding_type_ == content_encoding::br) +#endif +#ifdef CINATRA_ENABLE_BROTLI + { + uncompressed_str_.clear(); + bool r = br_codec::brotli_decompress(reply, uncompressed_str_); + if (r) + data.resp_body = uncompressed_str_; + else + data.resp_body = reply; + } +#endif +#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) + else +#endif + data.resp_body = reply; + + head_buf_.consume(content_len); + } + data.eof = (head_buf_.size() == 0); + } + + void handle_result(resp_data &data, std::error_code ec, bool is_keep_alive) { + if (ec) { + close_socket(*socket_); + data.net_err = ec; + data.status = 404; +#ifdef BENCHMARK_TEST + if (!stop_bench_) { + CINATRA_LOG_DEBUG << ec.message(); + } +#endif + } + else { + if (!is_keep_alive) { + close_socket(*socket_); + } + } + } + + template + async_simple::coro::Lazy handle_multipart( + resp_data &data, req_context ctx) { + std::error_code ec{}; + std::string boundary = std::string{parser_.get_boundary()}; + multipart_reader_t multipart(this); + while (true) { + auto part_head = co_await multipart.read_part_head(boundary); + if (part_head.ec) { + co_return part_head.ec; + } + + auto part_body = co_await multipart.read_part_body(boundary); + + if (ctx.resp_body_stream) { + size_t size; + std::tie(ec, size) = + co_await ctx.resp_body_stream->async_write(part_body.data); + } + else { + resp_chunk_str_.append(part_body.data.data(), part_body.data.size()); + } + + if (part_body.ec) { + co_return part_body.ec; + } + + if (part_body.eof) { + break; + } + } + co_return ec; + } + + template + async_simple::coro::Lazy handle_chunked( + resp_data &data, req_context ctx) { + std::error_code ec{}; + size_t size = 0; + while (true) { + if (std::tie(ec, size) = co_await async_read_until(chunked_buf_, CRCF); + ec) { + break; + } + + size_t buf_size = chunked_buf_.size(); + size_t additional_size = buf_size - size; + const char *data_ptr = + asio::buffer_cast(chunked_buf_.data()); + std::string_view size_str(data_ptr, size - CRCF.size()); + auto chunk_size = hex_to_int(size_str); + chunked_buf_.consume(size); + if (chunk_size < 0) { + CINATRA_LOG_DEBUG << "bad chunked size"; + ec = asio::error::make_error_code( + asio::error::basic_errors::invalid_argument); + break; + } + + if (additional_size < size_t(chunk_size + 2)) { + // not a complete chunk, read left chunk data. + size_t size_to_read = chunk_size + 2 - additional_size; + if (std::tie(ec, size) = + co_await async_read(chunked_buf_, size_to_read); + ec) { + break; + } + } + + if (chunk_size == 0) { + // all finished, no more data + chunked_buf_.consume(chunked_buf_.size()); + data.eof = true; + break; + } + + data_ptr = asio::buffer_cast(chunked_buf_.data()); + if (chunked_cb_) { + co_await chunked_cb_(std::string_view(data_ptr, chunk_size)); + } + else { + if (ctx.resp_body_stream) { + std::tie(ec, size) = co_await ctx.resp_body_stream->async_write( + {data_ptr, (size_t)chunk_size}); + } + else { + resp_chunk_str_.append(data_ptr, chunk_size); + } + } + + chunked_buf_.consume(chunk_size + CRCF.size()); + } + co_return ec; + } + + async_simple::coro::Lazy connect( + const uri_t &u, std::vector *eps = nullptr) { + std::vector eps_tmp; + if (eps == nullptr) { + eps = &eps_tmp; + } + if (should_reset_) { + reset(); + } + else { + should_reset_ = true; + } + if (socket_->has_closed_) { + auto time_out_guard = + timer_guard(this, conn_timeout_duration_, "connect timer"); + socket_->is_timeout_ = false; + host_ = proxy_host_.empty() ? u.get_host() : proxy_host_; + port_ = proxy_port_.empty() ? u.get_port() : proxy_port_; + if (eps->empty()) { + CINATRA_LOG_TRACE << "start resolve host: " << host_ << ":" << port_; + auto [ec, iter] = co_await coro_io::async_resolve( + &executor_wrapper_, socket_->impl_, host_, port_); + if (ec) { + co_return resp_data{ec, 404}; + } + else { + asio::ip::tcp::resolver::iterator end; + while (iter != end) { + eps->push_back(iter->endpoint()); + ++iter; + } + if (eps->empty()) [[unlikely]] { + co_return resp_data{std::make_error_code(std::errc::not_connected), + 404}; + } + } + } + CINATRA_LOG_TRACE + << "start connect to endpoint lists. total endpoint count:" + << eps->size() + << ", the first endpoint is: " << (*eps)[0].address().to_string() + << ":" << std::to_string((*eps)[0].port()); + std::error_code ec; + if (ec = co_await coro_io::async_connect(socket_->impl_, *eps); ec) { + co_return resp_data{ec, 404}; + } +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (connect_timeout_forever_) { + socket_->is_timeout_ = true; + } +#endif + if (socket_->is_timeout_) { + ec = make_error_code(http_errc::connect_timeout); + co_return resp_data{ec, 404}; + } + + if (enable_tcp_no_delay_) { + socket_->impl_.set_option(asio::ip::tcp::no_delay(true), ec); + if (ec) { + co_return resp_data{ec, 404}; + } + } + + if (u.is_ssl) { +#ifdef CINATRA_ENABLE_SSL + if (!has_init_ssl_) { + size_t pos = u.host.find("www."); + std::string host; + if (pos != std::string_view::npos) { + host = std::string{u.host.substr(pos + 4)}; + } + else { + host = std::string{u.host}; + } + bool r = init_ssl(asio::ssl::verify_none, "", host); + if (!r) { + co_return resp_data{ + std::make_error_code(std::errc::invalid_argument), 404}; + } + } +#endif + if (ec = co_await handle_shake(); ec) { + co_return resp_data{ec, 404}; + } + } + socket_->has_closed_ = false; + CINATRA_LOG_TRACE + << "connect to endpoint: " + << socket_->impl_.remote_endpoint().address().to_string() << ":" + << socket_->impl_.remote_endpoint().port() << " successfully"; + } + co_return resp_data{}; + } + + size_t multipart_content_len() { + size_t content_len = 0; + for (auto &[key, part] : form_data_) { + content_len += 75; + content_len += key.size() + 1; + if (!part.filename.empty()) { + content_len += (12 + part.filename.size() + 1); + auto ext = std::filesystem::path(part.filename).extension().string(); + if (auto it = g_content_type_map.find(ext); + it != g_content_type_map.end()) { + content_len += (14 + it->second.size()); + } + } + + content_len += 4; + + content_len += (part.size + 2); + } + content_len += (6 + BOUNDARY.size()); + return content_len; + } + + async_simple::coro::Lazy send_single_part( + const std::string &key, const multipart_t &part) { + std::string part_content_head; + part_content_head.append("--").append(BOUNDARY).append(CRCF); + part_content_head.append("Content-Disposition: form-data; name=\""); + part_content_head.append(key).append("\""); + bool is_file = !part.filename.empty(); + std::string short_name = + std::filesystem::path(part.filename).filename().string(); + if (is_file) { + part_content_head.append("; filename=\"") + .append(short_name) + .append("\"") + .append(CRCF); + auto ext = std::filesystem::path(short_name).extension().string(); + if (auto it = g_content_type_map.find(ext); + it != g_content_type_map.end()) { + part_content_head.append("Content-Type: ") + .append(it->second) + .append(CRCF); + } + + part_content_head.append(CRCF); + } + else { + part_content_head.append(TWO_CRCF); + } + if (auto [ec, size] = co_await async_write(asio::buffer(part_content_head)); + ec) { + co_return resp_data{ec, 404}; + } + + if (is_file) { + coro_io::coro_file file{}; + file.open(part.filename, std::ios::in); + assert(file.is_open()); + std::string file_data; + detail::resize(file_data, max_single_part_size_); + while (true) { + auto [rd_ec, rd_size] = + co_await file.async_read(file_data.data(), file_data.size()); + if (auto [ec, size] = + co_await async_write(asio::buffer(file_data.data(), rd_size)); + ec) { + co_return resp_data{ec, 404}; + } + if (file.eof()) { + if (auto [ec, size] = co_await async_write(asio::buffer(CRCF)); ec) { + co_return resp_data{ec, 404}; + } + break; + } + } + } + else { + std::array arr{asio::buffer(part.content), + asio::buffer(CRCF)}; + if (auto [ec, size] = co_await async_write(arr); ec) { + co_return resp_data{ec, 404}; + } + } + + co_return resp_data{{}, 200}; + } + + async_simple::coro::Lazy async_read_ws() { + resp_data data{}; + + head_buf_.consume(head_buf_.size()); + std::shared_ptr sock = socket_; + asio::streambuf &read_buf = sock->head_buf_; + bool has_init_ssl = false; +#ifdef CINATRA_ENABLE_SSL + has_init_ssl = has_init_ssl_; +#endif + websocket ws{}; + while (true) { + if (auto [ec, _] = co_await async_read_ws( + sock, read_buf, ws.left_header_len(), has_init_ssl); + ec) { + if (socket_->is_timeout_) { + co_return resp_data{make_error_code(http_errc::request_timeout), 404}; + } + data.net_err = ec; + data.status = 404; + + if (sock->has_closed_) { + co_return data; + } + + close_socket(*sock); + co_return data; + } + + const char *data_ptr = asio::buffer_cast(read_buf.data()); + auto ret = ws.parse_header(data_ptr, read_buf.size(), false); + if (ret == ws_header_status::incomplete) { + continue; + } + else if (ret == ws_header_status::error) { + data.net_err = std::make_error_code(std::errc::protocol_error); + data.status = 404; + close_socket(*sock); + co_return data; + } + + frame_header *header = (frame_header *)data_ptr; + bool is_close_frame = header->opcode == opcode::close; + + read_buf.consume(read_buf.size()); + + size_t payload_len = ws.payload_length(); + + if (auto [ec, size] = + co_await async_read_ws(sock, read_buf, payload_len, has_init_ssl); + ec) { + data.net_err = ec; + data.status = 404; + close_socket(*sock); + co_return data; + } + + data_ptr = asio::buffer_cast(read_buf.data()); +#ifdef CINATRA_ENABLE_GZIP + if (is_server_support_ws_deflate_ && enable_ws_deflate_) { + inflate_str_.clear(); + if (!cinatra::gzip_codec::inflate({data_ptr, payload_len}, + inflate_str_)) { + CINATRA_LOG_ERROR << "uncompuress data error"; + data.status = 404; + data.net_err = std::make_error_code(std::errc::protocol_error); + co_return data; + } + data_ptr = inflate_str_.data(); + payload_len = inflate_str_.length(); + } +#endif + if (is_close_frame) { + if (payload_len >= 2) { + payload_len -= 2; + data_ptr += sizeof(uint16_t); + } + } + data.status = 200; + data.resp_body = {data_ptr, payload_len}; + + read_buf.consume(read_buf.size()); + + if (is_close_frame) { + std::string reason = "close"; + auto close_str = ws.format_close_payload(close_code::normal, + reason.data(), reason.size()); + auto span = std::span(close_str); + auto encode_header = ws.encode_frame(span, opcode::close, true); + std::vector buffers{asio::buffer(encode_header), + asio::buffer(reason)}; + + co_await async_write_ws(sock, buffers, has_init_ssl); + + close_socket(*sock); + + data.net_err = asio::error::eof; + data.status = 404; + co_return data; + } + co_return data; + } + } + + template + async_simple::coro::Lazy> async_read_ws( + auto sock, AsioBuffer &&buffer, size_t size_to_read, + bool has_init_ssl = false) noexcept { +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl) { + return coro_io::async_read(*sock->ssl_stream_, buffer, size_to_read); + } + else { +#endif + return coro_io::async_read(sock->impl_, buffer, size_to_read); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + template + async_simple::coro::Lazy> async_write_ws( + auto sock, AsioBuffer &&buffer, bool has_init_ssl = false) { +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl) { + return coro_io::async_write(*sock->ssl_stream_, buffer); + } + else { +#endif + return coro_io::async_write(sock->impl_, buffer); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + template + async_simple::coro::Lazy> async_read( + AsioBuffer &&buffer, size_t size_to_read) noexcept { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (read_failed_forever_) { + return async_read_failed(); + } +#endif +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + return coro_io::async_read(*socket_->ssl_stream_, buffer, size_to_read); + } + else { +#endif + return coro_io::async_read(socket_->impl_, buffer, size_to_read); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + async_simple::coro::Lazy> + async_write_failed() { + co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); + } + + async_simple::coro::Lazy> + async_read_failed() { + co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); + } +#endif + + template + async_simple::coro::Lazy> async_write( + AsioBuffer &&buffer) { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (write_failed_forever_) { + return async_write_failed(); + } +#endif +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + return coro_io::async_write(*socket_->ssl_stream_, buffer); + } + else { +#endif + return coro_io::async_write(socket_->impl_, buffer); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + template + async_simple::coro::Lazy> async_read_until( + AsioBuffer &buffer, asio::string_view delim) noexcept { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (read_failed_forever_) { + return async_read_failed(); + } +#endif +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + return coro_io::async_read_until(*socket_->ssl_stream_, buffer, delim); + } + else { +#endif + return coro_io::async_read_until(socket_->impl_, buffer, delim); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + static void close_socket(socket_t &socket) { + std::error_code ec; + socket.impl_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket.impl_.close(ec); + socket.has_closed_ = true; + } + + async_simple::coro::Lazy timeout( + auto &timer, std::chrono::steady_clock::duration duration, + std::string msg) { + auto watcher = std::weak_ptr(socket_); + timer.expires_after(duration); + auto is_timeout = co_await timer.async_await(); + if (!is_timeout) { + co_return false; + } + if (auto socket = watcher.lock(); socket) { + socket_->is_timeout_ = true; + CINATRA_LOG_WARNING << msg << " timeout"; + close_socket(*socket_); + } + co_return true; + } + + template + bool has_schema(const S &url) { + size_t pos_http = url.find("http://"); + size_t pos_https = url.find("https://"); + size_t pos_ws = url.find("ws://"); + size_t pos_wss = url.find("wss://"); + bool has_http_scheme = + ((pos_http != std::string::npos) && pos_http == 0) || + ((pos_https != std::string::npos) && pos_https == 0) || + ((pos_ws != std::string::npos) && pos_ws == 0) || + ((pos_wss != std::string::npos) && pos_wss == 0); + return has_http_scheme; + } + + friend class multipart_reader_t; + http_parser parser_; + coro_io::ExecutorWrapper<> executor_wrapper_; + coro_io::period_timer timer_; + std::shared_ptr socket_; + asio::streambuf &head_buf_; + asio::streambuf &chunked_buf_; + std::string body_; + + std::unordered_map req_headers_; + + std::string proxy_request_uri_ = ""; + std::string proxy_host_; + std::string proxy_port_; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + + std::string proxy_bearer_token_auth_token_; + + std::map form_data_; + size_t max_single_part_size_ = 1024 * 1024; + + std::string ws_sec_key_; + std::string host_; + std::string port_; + +#ifdef CINATRA_ENABLE_SSL + std::unique_ptr ssl_ctx_ = nullptr; + bool has_init_ssl_ = false; + bool is_ssl_schema_ = false; +#ifdef YLT_ENABLE_NTLS + bool use_ntls_ = true; +#endif // YLT_ENABLE_NTLS + bool need_set_sni_host_ = true; +#endif + std::string redirect_uri_; + bool enable_follow_redirect_ = false; + std::chrono::steady_clock::duration conn_timeout_duration_ = + std::chrono::seconds(30); + std::chrono::steady_clock::duration req_timeout_duration_ = + std::chrono::seconds(60); + std::chrono::steady_clock::time_point create_tp_; + bool enable_tcp_no_delay_ = true; + std::string resp_chunk_str_; + std::span out_buf_; + bool should_reset_ = false; + config config_; + + bool enable_ws_deflate_ = false; +#ifdef CINATRA_ENABLE_GZIP + bool is_server_support_ws_deflate_ = false; + std::string inflate_str_; +#endif + content_encoding encoding_type_ = content_encoding::none; + int64_t max_http_body_len_ = + INT64_MAX; // in default we don't limit http body len + + std::function(std::string_view)> chunked_cb_; +#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) + std::string uncompressed_str_; +#endif + +#ifdef BENCHMARK_TEST + bool stop_bench_ = false; + size_t total_len_ = 0; +#endif +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + public: + bool write_failed_forever_ = false; + bool connect_timeout_forever_ = false; + bool parse_failed_forever_ = false; + bool read_failed_forever_ = false; + bool write_header_timeout_ = false; + bool write_payload_timeout_ = false; + bool read_timeout_ = false; +#endif +}; + +} // namespace cinatra diff --git a/src/coro_http/tests/CMakeLists.txt b/src/coro_http/tests/CMakeLists.txt index a1a0daade..ff4c12feb 100644 --- a/src/coro_http/tests/CMakeLists.txt +++ b/src/coro_http/tests/CMakeLists.txt @@ -1,74 +1,80 @@ -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release") -endif() -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) -find_package(Threads REQUIRED) -find_package(ZLIB) -if (ZLIB_FOUND) - add_definitions(-DCINATRA_ENABLE_GZIP) -endif () -link_libraries(Threads::Threads) - -include_directories(include) -include_directories(include/ylt/thirdparty) - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") - #-ftree-slp-vectorize with coroutine cause link error. disable it util gcc fix. - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize") -endif() - -add_executable(coro_http_test - test_mixed.cpp - test_coro_http_server.cpp - test_cinatra.cpp - test_cinatra_websocket.cpp - test_http_parse.cpp - test_http_client_filter.cpp - test_http_ssl_mutual_auth.cpp - main.cpp - ) - -add_custom_command( - TARGET coro_http_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/src/coro_http/openssl_files) -add_custom_command( - TARGET coro_http_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/output/openssl_files) - -add_definitions(-DINJECT_FOR_HTTP_CLIENT_TEST) -add_definitions(-DINJECT_FOR_HTTP_SEVER_TEST) - -add_test(NAME coro_http_test COMMAND coro_http_test) -# target_compile_definitions(easylog_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 - if (YLT_ENABLE_SSL) - message(STATUS "Use SSL") - find_package(OpenSSL REQUIRED) - add_definitions(-DCINATRA_ENABLE_SSL) - target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock OpenSSL::SSL OpenSSL::Crypto) - endif () - - if (ZLIB_FOUND) - target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock ZLIB::ZLIB) - endif () -else() - if (YLT_ENABLE_SSL) - message(STATUS "Use SSL") - find_package(OpenSSL REQUIRED) - add_definitions(-DCINATRA_ENABLE_SSL) - target_link_libraries(coro_http_test OpenSSL::SSL OpenSSL::Crypto) - endif () - - if (ZLIB_FOUND) - target_link_libraries(coro_http_test ZLIB::ZLIB) - endif () -endif() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +find_package(Threads REQUIRED) +find_package(ZLIB) +if (ZLIB_FOUND) + add_definitions(-DCINATRA_ENABLE_GZIP) +endif () +link_libraries(Threads::Threads) + +include_directories(include) +include_directories(include/ylt/thirdparty) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") + #-ftree-slp-vectorize with coroutine cause link error. disable it util gcc fix. + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize") +endif() + +add_executable(coro_http_test + test_mixed.cpp + test_coro_http_server.cpp + test_cinatra.cpp + test_cinatra_websocket.cpp + test_http_parse.cpp + test_http_client_filter.cpp + test_http_ssl_mutual_auth.cpp + main.cpp + ) + +add_custom_command( + TARGET coro_http_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_http/openssl_files) +add_custom_command( + TARGET coro_http_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/output/openssl_files) +add_custom_command( + TARGET coro_http_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_http/tests/openssl_files) + +add_definitions(-DINJECT_FOR_HTTP_CLIENT_TEST) +add_definitions(-DINJECT_FOR_HTTP_SEVER_TEST) + +add_test(NAME coro_http_test COMMAND coro_http_test + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/coro_http/tests) +# target_compile_definitions(easylog_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 + if (YLT_ENABLE_SSL) + message(STATUS "Use SSL") + find_package(OpenSSL REQUIRED) + add_definitions(-DCINATRA_ENABLE_SSL) + target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock OpenSSL::SSL OpenSSL::Crypto) + endif () + + if (ZLIB_FOUND) + target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock ZLIB::ZLIB) + endif () +else() + if (YLT_ENABLE_SSL) + message(STATUS "Use SSL") + find_package(OpenSSL REQUIRED) + add_definitions(-DCINATRA_ENABLE_SSL) + target_link_libraries(coro_http_test OpenSSL::SSL OpenSSL::Crypto) + endif () + + if (ZLIB_FOUND) + target_link_libraries(coro_http_test ZLIB::ZLIB) + endif () +endif() diff --git a/src/coro_http/tests/test_cinatra_websocket.cpp b/src/coro_http/tests/test_cinatra_websocket.cpp index 3aa7a4bea..fc4ccd609 100644 --- a/src/coro_http/tests/test_cinatra_websocket.cpp +++ b/src/coro_http/tests/test_cinatra_websocket.cpp @@ -40,7 +40,8 @@ TEST_CASE("test wss client") { std::this_thread::sleep_for(200ms); coro_http_client client{}; - bool ok = client.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"); + bool ok = + client.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"); REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); async_simple::coro::syncAwait(client.connect("wss://localhost:9001")); diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 0258ace19..54287ed46 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -146,12 +146,10 @@ TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { coro_http_client client; // Use client cert but wrong CA for server verification - this will cause // handshake failure - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, "wrong_ca.crt", - CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, + "wrong_ca.crt", CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); // When CA cert file doesn't exist, init_ssl returns false - REQUIRE_MESSAGE(init_ok == false, - "client init_ssl should fail with wrong CA cert"); + REQUIRE_MESSAGE(init_ok == false, "client init_ssl should fail with wrong CA cert"); server.stop(); thd.join(); diff --git a/src/coro_rpc/tests/CMakeLists.txt b/src/coro_rpc/tests/CMakeLists.txt index eea162257..0fcfc916d 100644 --- a/src/coro_rpc/tests/CMakeLists.txt +++ b/src/coro_rpc/tests/CMakeLists.txt @@ -1,54 +1,57 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) -set(TEST_SRCS - test_acceptor.cpp - test_coro_rpc_server.cpp - test_coro_rpc_client.cpp - test_register_handler.cpp - test_router.cpp - test_connection.cpp - test_function_name.cpp - test_variadic.cpp - test_parallel.cpp - test_client_filter.cpp - test_abi_compatible.cpp - test_rpc_ssl_mutual_auth.cpp - ) -if (YLT_ENABLE_IBV AND YLT_ENABLE_CUDA) - set(TEST_SRC ${TEST_SRCS} test_gdr.cpp) -endif() -set(TEST_COMMON - rpc_api.cpp - main.cpp - ) -add_executable(coro_rpc_test - ${TEST_SRCS} - ${TEST_COMMON} - ) -add_custom_command( - TARGET coro_rpc_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/src/coro_rpc/openssl_files) -add_custom_command( - TARGET coro_rpc_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/output/openssl_files) -target_compile_definitions(coro_rpc_test PRIVATE UNIT_TEST_INJECT) -target_compile_definitions(coro_rpc_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) - -add_test(NAME coro_rpc_test COMMAND coro_rpc_test) - -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests/coro_rpc) - -add_executable(coro_rpc_regist_test_1 rpc_api.cpp test_register_duplication_1.cpp) -add_executable(coro_rpc_regist_test_2 rpc_api.cpp test_register_duplication_2.cpp) -add_executable(coro_rpc_regist_test_3 rpc_api.cpp test_register_duplication_3.cpp) - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 - target_link_libraries(coro_rpc_test PRIVATE ws2_32 mswsock) - target_link_libraries(coro_rpc_regist_test_1 PRIVATE ws2_32 mswsock) - target_link_libraries(coro_rpc_regist_test_2 PRIVATE ws2_32 mswsock) - target_link_libraries(coro_rpc_regist_test_3 PRIVATE ws2_32 mswsock) -endif() +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) +set(TEST_SRCS + test_acceptor.cpp + test_coro_rpc_server.cpp + test_coro_rpc_client.cpp + test_register_handler.cpp + test_router.cpp + test_connection.cpp + test_function_name.cpp + test_variadic.cpp + test_parallel.cpp + test_client_filter.cpp + test_abi_compatible.cpp + test_rpc_ssl_mutual_auth.cpp + ) +set(TEST_COMMON + rpc_api.cpp + main.cpp + ) +add_executable(coro_rpc_test + ${TEST_SRCS} + ${TEST_COMMON} + ) +add_custom_command( + TARGET coro_rpc_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_rpc/openssl_files) +add_custom_command( + TARGET coro_rpc_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/output/openssl_files) +add_custom_command( + TARGET coro_rpc_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_rpc/tests/openssl_files) +target_compile_definitions(coro_rpc_test PRIVATE UNIT_TEST_INJECT) +target_compile_definitions(coro_rpc_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) + +add_test(NAME coro_rpc_test COMMAND coro_rpc_test + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/coro_rpc/tests) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests/coro_rpc) + +add_executable(coro_rpc_regist_test_1 rpc_api.cpp test_register_duplication_1.cpp) +add_executable(coro_rpc_regist_test_2 rpc_api.cpp test_register_duplication_2.cpp) +add_executable(coro_rpc_regist_test_3 rpc_api.cpp test_register_duplication_3.cpp) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 + target_link_libraries(coro_rpc_test PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_regist_test_1 PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_regist_test_2 PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_regist_test_3 PRIVATE ws2_32 mswsock) +endif() diff --git a/src/coro_rpc/tests/ServerTester.hpp b/src/coro_rpc/tests/ServerTester.hpp index 8f58589e8..e52dbf086 100644 --- a/src/coro_rpc/tests/ServerTester.hpp +++ b/src/coro_rpc/tests/ServerTester.hpp @@ -1,455 +1,455 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef CORO_RPC_SERVERTESTER_HPP -#define CORO_RPC_SERVERTESTER_HPP -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "inject_action.hpp" -#include "rpc_api.hpp" -#include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/coro_rpc/impl/errno.h" - -#ifdef _MSC_VER -#define CORO_RPC_FUNCTION_SIGNATURE __FUNCSIG__ -#else -#define CORO_RPC_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ -#endif - -using namespace std::string_literals; -using namespace async_simple::coro; -using namespace coro_rpc; -using namespace std::chrono_literals; - -struct TesterConfig { - TesterConfig() = default; - TesterConfig(TesterConfig &c) { - enable_heartbeat = c.enable_heartbeat; - use_ssl = c.use_ssl; - use_rdma = c.use_rdma; - sync_client = c.sync_client; - use_outer_io_context = c.use_outer_io_context; - port = c.port; - conn_timeout_duration = c.conn_timeout_duration; - } - bool enable_heartbeat; - bool use_ssl; - bool use_rdma; - bool sync_client; - bool use_outer_io_context; - unsigned short port; - std::string address = "0.0.0.0"; - std::chrono::steady_clock::duration conn_timeout_duration = - std::chrono::seconds(0); - - friend std::ostream &operator<<(std::ostream &os, - const TesterConfig &config) { - os << std::boolalpha; - os << " enable_heartbeat: " << config.enable_heartbeat << ";" - << " use_ssl: " << config.use_ssl << ";" - << " use_rdma: " << config.use_rdma << ";" - << " sync_client: " << config.sync_client << ";" - << " use_outer_io_context: " << config.use_outer_io_context << ";" - << " port: " << config.port << ";" - << " address: " << config.address << ";"; - os << " conn_timeout_duration: "; - auto val = std::chrono::duration_cast( - config.conn_timeout_duration) - .count(); - os << val << "ms" - << ";"; - return os; - } -}; - -struct ServerTester : TesterConfig { - ServerTester(TesterConfig config) - : TesterConfig(config), - executor_(io_context_.get_executor()), - port_(std::to_string(config.port)) { - if (use_outer_io_context) { - std::promise promise; - auto future = promise.get_future(); - thd_ = std::thread([this, &promise]() { - asio::io_context::work work(io_context_); - promise.set_value(); - io_context_.run(); - }); - future.wait(); - } - - std::stringstream ss; - ss << config; - conf_str_ = ss.str(); - } - ~ServerTester() { - if (use_outer_io_context) { - io_context_.stop(); - thd_.join(); - } - g_action = coro_rpc::inject_action::nothing; - } - void run() { - ELOGV(INFO, "run test: heartbeat=%d, ssl=%d", enable_heartbeat, use_ssl); - register_all_function(); - test_all(); - } - virtual void test_all() { - test_connect_timeout(); - test_function_registered(); - if (sync_client) { - // only sync_call implement inject behavior - test_client_send_bad_header(); - test_client_send_bad_magic_num(); - test_client_send_header_length_is_0(); - test_client_close_socket_after_send_header(); - test_client_close_socket_after_send_partial_header(); - test_client_close_socket_after_send_payload(); - } - test_heartbeat(); - test_call_function_with_long_response_time(); - test_call_with_large_buffer(); - } - std::shared_ptr create_client( - inject_action action = inject_action::nothing) { - std::shared_ptr client; - // sometimes, connect will take more than conn_timeout_duration(500ms), so - // retry 3 times to make sure connect ok. - coro_rpc::errc ec; - int retry = 4; - for (int i = 0; i < retry; i++) { - if (use_outer_io_context) { - client = std::make_shared(&executor_); - } - else { - client = - std::make_shared(coro_io::get_global_executor()); - } -#ifdef YLT_ENABLE_SSL - if (use_ssl) { - bool ok = client->init_ssl("../openssl_files", "server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - } -#endif -#ifdef YLT_ENABLE_IBV - if (use_rdma) { - bool ok = client->init_ibv(); - REQUIRE_MESSAGE(ok == true, - "init ibv fail, please check ibverbs config"); - } -#endif - g_action = action; - - if (sync_client) { - ec = client->sync_connect("127.0.0.1", port_); - } - else { - ec = syncAwait(client->connect("127.0.0.1", port_)); - } - - if (!ec) { - break; - } - - ELOGV(INFO, "retry times %d", i); - } - - REQUIRE_MESSAGE(!ec, std::to_string(client->get_client_id()) - .append(" not connected ") - .append(conf_str_)); - return client; - } - template - decltype(auto) call(std::shared_ptr client, Args &&...args) { - ELOGV(INFO, "%s client_id %d call %s", - sync_client ? "sync_client" : "async_client", client->get_client_id(), - coro_rpc::get_func_name().data()); - if (sync_client) { - return client->sync_call(std::forward(args)...); - } - else { - return syncAwait( - client->template call(std::forward(args)...)); - } - } - - virtual void register_all_function() {} - - virtual void remove_all_rpc_function() {} - - void test_function_registered() { - g_action = {}; - - // because heartbeat timeout is 500ms, sometimes the client closed because - // of timeout, so retry call. - int retry = 4; - for (int i = 0; i < retry; i++) { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - ELOGV(INFO, "retry call times %d", i); - { - auto ret = call(client); - ELOGV(INFO, "client_id %d call async_hi", client->get_client_id()); - if (!ret) { - ELOGV(ERROR, "client_id %d call async_hi error %s", - client->get_client_id(), ret.error().msg.data()); - continue; - } - else { - ELOGV(INFO, "client_id %d call async_hi ok, result %s", - client->get_client_id(), ret.value().data()); - } - CHECK(ret.value() == "async hi"s); - } - { - auto ret = call(client); - ELOGV(INFO, "client_id %d call hello", client->get_client_id()); - if (!ret) { - ELOGV(ERROR, "client_id %d call hello error %s", - client->get_client_id(), ret.error().msg.data()); - continue; - } - else { - ELOGV(INFO, "client_id %d call hello ok, result %s", - client->get_client_id(), ret.value().data()); - } - CHECK(ret.value() == "hello"s); - } - { - auto ret = call<&HelloService::hello>(client); - ELOGV(INFO, "client_id %d call HelloService::hello", - client->get_client_id()); - if (!ret) { - ELOGV(ERROR, "client_id %d call HelloService::hello error %s", - client->get_client_id(), ret.error().msg.data()); - continue; - } - else { - ELOGV(INFO, "client_id %d call HelloService::hello ok, result %s", - client->get_client_id(), ret.value().data()); - } - CHECK(ret.value() == "hello"s); - } - { -#ifdef __GNUC__ - auto ret = call<&ns_login::LoginService::login>(client, "foo"s, "bar"s); - if (!ret) { - ELOGV(WARN, "%d %s", client->get_client_id(), ret.error().msg.data()); - } - CHECK(ret.value() == true); -#endif - } - - break; - } - } - void test_client_send_bad_header() { - g_action = {}; - auto client = create_client(inject_action::client_send_bad_header); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_send_bad_magic_num() { - g_action = {}; - auto client = create_client(inject_action::client_send_bad_magic_num); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_send_header_length_is_0() { - g_action = {}; - auto client = create_client(inject_action::client_send_header_length_0); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_close_socket_after_send_header() { - g_action = {}; - auto client = - create_client(inject_action::client_close_socket_after_send_header); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_close_socket_after_send_partial_header() { - g_action = {}; - auto client = create_client( - inject_action::client_close_socket_after_send_partial_header); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_close_socket_after_send_payload() { - g_action = {}; - auto client = - create_client(inject_action::client_close_socket_after_send_payload); - auto ret = call(client); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - - void test_heartbeat() { - // auto client = create_client(inject_action::nothing); - // ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - // auto ret = call(client); - // CHECK(ret.value() == "async hi"s); - - // std::this_thread::sleep_for(700ms); - - // ret = call(client); - // if (enable_heartbeat) { - // REQUIRE_MESSAGE( - // ret.error().code == coro_rpc::errc::io_error, - // std::to_string(client->get_client_id()).append(ret.error().msg)); - // } - // else { - // CHECK(ret.value() == "async hi"s); - // } - // ELOGV(INFO, "test heartbeat done"); - } - void test_call_function_with_long_response_time() { - auto client = create_client(inject_action::nothing); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client, 1); - CHECK(ret.value() == 1); - } - void test_call_with_large_buffer() { - auto client = create_client(inject_action::nothing); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - std::string arg; - arg.resize(2048); - auto ret = call(client, arg); - CHECK(ret.value() == arg); - } - - void test_connect_timeout() { - g_action = {}; - if (sync_client) { - return; - } - auto init_client = [this]() { - std::shared_ptr client; - if (use_outer_io_context) { - client = std::make_shared(&executor_); - } - else { - client = - std::make_shared(coro_io::get_global_executor()); - } -#ifdef YLT_ENABLE_SSL - if (use_ssl) { - bool ok = client->init_ssl("../openssl_files", "server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - } -#endif -#ifdef YLT_ENABLE_IBV - if (use_rdma) { - bool ok = client->init_ibv(); - REQUIRE_MESSAGE(ok == true, - "init ibv fail, please check ibverbs config"); - } -#endif - return client; - }; - auto client = init_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - coro_rpc::err_code ec; - auto client2 = init_client(); - ec = syncAwait(client2->connect("10.255.255.1", port_, 5ms)); - CHECK_MESSAGE(ec, - std::to_string(client->get_client_id()).append(ec.message())); - } - - template - void test_call_with_delay_func(Args... args) { - g_action = {}; - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client, std::forward(args)...); - CHECK(ret.has_value()); - } - - template - void test_call_with_delay_func_client_read_length_error(Args... args) { - g_action = {}; - auto client = this->create_client(); - ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, - client->get_client_id()); - g_action = inject_action::close_socket_after_read_header; - auto ret = this->template call(client, std::forward(args)...); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - }; - - template - void test_call_with_delay_func_client_read_body_error(Args... args) { - g_action = {}; - auto client = this->create_client(); - ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, - client->get_client_id()); - g_action = inject_action::close_socket_after_send_length; - auto ret = this->template call(client, std::forward(args)...); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - }; - - template - void test_call_with_delay_func_server_timeout(Args... args) { - g_action = {}; - auto client = this->create_client(); - ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, - client->get_client_id()); - auto ret = this->template call(client, std::forward(args)...); - REQUIRE(ret); - std::this_thread::sleep_for(700ms); - ret = this->call(client, std::forward(args)...); - REQUIRE(!ret); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - }; - asio::io_context io_context_; - coro_io::ExecutorWrapper<> executor_; - std::string port_; - std::thread thd_; - ns_login::LoginService login_service_; - HelloService hello_service_; - std::string conf_str_; -}; -#endif // CORO_RPC_SERVERTESTER_HPP +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CORO_RPC_SERVERTESTER_HPP +#define CORO_RPC_SERVERTESTER_HPP +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "doctest.h" +#include "inject_action.hpp" +#include "rpc_api.hpp" +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/coro_rpc/impl/errno.h" + +#ifdef _MSC_VER +#define CORO_RPC_FUNCTION_SIGNATURE __FUNCSIG__ +#else +#define CORO_RPC_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ +#endif + +using namespace std::string_literals; +using namespace async_simple::coro; +using namespace coro_rpc; +using namespace std::chrono_literals; + +struct TesterConfig { + TesterConfig() = default; + TesterConfig(TesterConfig &c) { + enable_heartbeat = c.enable_heartbeat; + use_ssl = c.use_ssl; + use_rdma = c.use_rdma; + sync_client = c.sync_client; + use_outer_io_context = c.use_outer_io_context; + port = c.port; + conn_timeout_duration = c.conn_timeout_duration; + } + bool enable_heartbeat; + bool use_ssl; + bool use_rdma; + bool sync_client; + bool use_outer_io_context; + unsigned short port; + std::string address = "0.0.0.0"; + std::chrono::steady_clock::duration conn_timeout_duration = + std::chrono::seconds(0); + + friend std::ostream &operator<<(std::ostream &os, + const TesterConfig &config) { + os << std::boolalpha; + os << " enable_heartbeat: " << config.enable_heartbeat << ";" + << " use_ssl: " << config.use_ssl << ";" + << " use_rdma: " << config.use_rdma << ";" + << " sync_client: " << config.sync_client << ";" + << " use_outer_io_context: " << config.use_outer_io_context << ";" + << " port: " << config.port << ";" + << " address: " << config.address << ";"; + os << " conn_timeout_duration: "; + auto val = std::chrono::duration_cast( + config.conn_timeout_duration) + .count(); + os << val << "ms" + << ";"; + return os; + } +}; + +struct ServerTester : TesterConfig { + ServerTester(TesterConfig config) + : TesterConfig(config), + executor_(io_context_.get_executor()), + port_(std::to_string(config.port)) { + if (use_outer_io_context) { + std::promise promise; + auto future = promise.get_future(); + thd_ = std::thread([this, &promise]() { + asio::io_context::work work(io_context_); + promise.set_value(); + io_context_.run(); + }); + future.wait(); + } + + std::stringstream ss; + ss << config; + conf_str_ = ss.str(); + } + ~ServerTester() { + if (use_outer_io_context) { + io_context_.stop(); + thd_.join(); + } + g_action = coro_rpc::inject_action::nothing; + } + void run() { + ELOGV(INFO, "run test: heartbeat=%d, ssl=%d", enable_heartbeat, use_ssl); + register_all_function(); + test_all(); + } + virtual void test_all() { + test_connect_timeout(); + test_function_registered(); + if (sync_client) { + // only sync_call implement inject behavior + test_client_send_bad_header(); + test_client_send_bad_magic_num(); + test_client_send_header_length_is_0(); + test_client_close_socket_after_send_header(); + test_client_close_socket_after_send_partial_header(); + test_client_close_socket_after_send_payload(); + } + test_heartbeat(); + test_call_function_with_long_response_time(); + test_call_with_large_buffer(); + } + std::shared_ptr create_client( + inject_action action = inject_action::nothing) { + std::shared_ptr client; + // sometimes, connect will take more than conn_timeout_duration(500ms), so + // retry 3 times to make sure connect ok. + coro_rpc::errc ec; + int retry = 4; + for (int i = 0; i < retry; i++) { + if (use_outer_io_context) { + client = std::make_shared(&executor_); + } + else { + client = + std::make_shared(coro_io::get_global_executor()); + } +#ifdef YLT_ENABLE_SSL + if (use_ssl) { + bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + } +#endif +#ifdef YLT_ENABLE_IBV + if (use_rdma) { + bool ok = client->init_ibv(); + REQUIRE_MESSAGE(ok == true, + "init ibv fail, please check ibverbs config"); + } +#endif + g_action = action; + + if (sync_client) { + ec = client->sync_connect("127.0.0.1", port_); + } + else { + ec = syncAwait(client->connect("127.0.0.1", port_)); + } + + if (!ec) { + break; + } + + ELOGV(INFO, "retry times %d", i); + } + + REQUIRE_MESSAGE(!ec, std::to_string(client->get_client_id()) + .append(" not connected ") + .append(conf_str_)); + return client; + } + template + decltype(auto) call(std::shared_ptr client, Args &&...args) { + ELOGV(INFO, "%s client_id %d call %s", + sync_client ? "sync_client" : "async_client", client->get_client_id(), + coro_rpc::get_func_name().data()); + if (sync_client) { + return client->sync_call(std::forward(args)...); + } + else { + return syncAwait( + client->template call(std::forward(args)...)); + } + } + + virtual void register_all_function() {} + + virtual void remove_all_rpc_function() {} + + void test_function_registered() { + g_action = {}; + + // because heartbeat timeout is 500ms, sometimes the client closed because + // of timeout, so retry call. + int retry = 4; + for (int i = 0; i < retry; i++) { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + ELOGV(INFO, "retry call times %d", i); + { + auto ret = call(client); + ELOGV(INFO, "client_id %d call async_hi", client->get_client_id()); + if (!ret) { + ELOGV(ERROR, "client_id %d call async_hi error %s", + client->get_client_id(), ret.error().msg.data()); + continue; + } + else { + ELOGV(INFO, "client_id %d call async_hi ok, result %s", + client->get_client_id(), ret.value().data()); + } + CHECK(ret.value() == "async hi"s); + } + { + auto ret = call(client); + ELOGV(INFO, "client_id %d call hello", client->get_client_id()); + if (!ret) { + ELOGV(ERROR, "client_id %d call hello error %s", + client->get_client_id(), ret.error().msg.data()); + continue; + } + else { + ELOGV(INFO, "client_id %d call hello ok, result %s", + client->get_client_id(), ret.value().data()); + } + CHECK(ret.value() == "hello"s); + } + { + auto ret = call<&HelloService::hello>(client); + ELOGV(INFO, "client_id %d call HelloService::hello", + client->get_client_id()); + if (!ret) { + ELOGV(ERROR, "client_id %d call HelloService::hello error %s", + client->get_client_id(), ret.error().msg.data()); + continue; + } + else { + ELOGV(INFO, "client_id %d call HelloService::hello ok, result %s", + client->get_client_id(), ret.value().data()); + } + CHECK(ret.value() == "hello"s); + } + { +#ifdef __GNUC__ + auto ret = call<&ns_login::LoginService::login>(client, "foo"s, "bar"s); + if (!ret) { + ELOGV(WARN, "%d %s", client->get_client_id(), ret.error().msg.data()); + } + CHECK(ret.value() == true); +#endif + } + + break; + } + } + void test_client_send_bad_header() { + g_action = {}; + auto client = create_client(inject_action::client_send_bad_header); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_send_bad_magic_num() { + g_action = {}; + auto client = create_client(inject_action::client_send_bad_magic_num); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_send_header_length_is_0() { + g_action = {}; + auto client = create_client(inject_action::client_send_header_length_0); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_close_socket_after_send_header() { + g_action = {}; + auto client = + create_client(inject_action::client_close_socket_after_send_header); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_close_socket_after_send_partial_header() { + g_action = {}; + auto client = create_client( + inject_action::client_close_socket_after_send_partial_header); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_close_socket_after_send_payload() { + g_action = {}; + auto client = + create_client(inject_action::client_close_socket_after_send_payload); + auto ret = call(client); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + + void test_heartbeat() { + // auto client = create_client(inject_action::nothing); + // ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + // auto ret = call(client); + // CHECK(ret.value() == "async hi"s); + + // std::this_thread::sleep_for(700ms); + + // ret = call(client); + // if (enable_heartbeat) { + // REQUIRE_MESSAGE( + // ret.error().code == coro_rpc::errc::io_error, + // std::to_string(client->get_client_id()).append(ret.error().msg)); + // } + // else { + // CHECK(ret.value() == "async hi"s); + // } + // ELOGV(INFO, "test heartbeat done"); + } + void test_call_function_with_long_response_time() { + auto client = create_client(inject_action::nothing); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client, 1); + CHECK(ret.value() == 1); + } + void test_call_with_large_buffer() { + auto client = create_client(inject_action::nothing); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + std::string arg; + arg.resize(2048); + auto ret = call(client, arg); + CHECK(ret.value() == arg); + } + + void test_connect_timeout() { + g_action = {}; + if (sync_client) { + return; + } + auto init_client = [this]() { + std::shared_ptr client; + if (use_outer_io_context) { + client = std::make_shared(&executor_); + } + else { + client = + std::make_shared(coro_io::get_global_executor()); + } +#ifdef YLT_ENABLE_SSL + if (use_ssl) { + bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + } +#endif +#ifdef YLT_ENABLE_IBV + if (use_rdma) { + bool ok = client->init_ibv(); + REQUIRE_MESSAGE(ok == true, + "init ibv fail, please check ibverbs config"); + } +#endif + return client; + }; + auto client = init_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + coro_rpc::err_code ec; + auto client2 = init_client(); + ec = syncAwait(client2->connect("10.255.255.1", port_, 5ms)); + CHECK_MESSAGE(ec, + std::to_string(client->get_client_id()).append(ec.message())); + } + + template + void test_call_with_delay_func(Args... args) { + g_action = {}; + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client, std::forward(args)...); + CHECK(ret.has_value()); + } + + template + void test_call_with_delay_func_client_read_length_error(Args... args) { + g_action = {}; + auto client = this->create_client(); + ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, + client->get_client_id()); + g_action = inject_action::close_socket_after_read_header; + auto ret = this->template call(client, std::forward(args)...); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + }; + + template + void test_call_with_delay_func_client_read_body_error(Args... args) { + g_action = {}; + auto client = this->create_client(); + ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, + client->get_client_id()); + g_action = inject_action::close_socket_after_send_length; + auto ret = this->template call(client, std::forward(args)...); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + }; + + template + void test_call_with_delay_func_server_timeout(Args... args) { + g_action = {}; + auto client = this->create_client(); + ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, + client->get_client_id()); + auto ret = this->template call(client, std::forward(args)...); + REQUIRE(ret); + std::this_thread::sleep_for(700ms); + ret = this->call(client, std::forward(args)...); + REQUIRE(!ret); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + }; + asio::io_context io_context_; + coro_io::ExecutorWrapper<> executor_; + std::string port_; + std::thread thd_; + ns_login::LoginService login_service_; + HelloService hello_service_; + std::string conf_str_; +}; +#endif // CORO_RPC_SERVERTESTER_HPP diff --git a/src/coro_rpc/tests/test_coro_rpc_client.cpp b/src/coro_rpc/tests/test_coro_rpc_client.cpp index 7c0102a79..b87736021 100644 --- a/src/coro_rpc/tests/test_coro_rpc_client.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_client.cpp @@ -1,848 +1,851 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "rpc_api.hpp" -#include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/coro_rpc/impl/coro_rpc_client.hpp" -#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" -#include "ylt/coro_rpc/impl/errno.h" -using namespace coro_rpc; -using namespace std::chrono_literals; -using namespace std::string_literals; -using namespace async_simple::coro; -using asio::ip::tcp; - -template -class ClientTester { - ClientTester(unsigned short port) : port_(std::to_string(port)) {} - std::string port_; -}; - -static unsigned short coro_rpc_server_port = 8803; -Lazy> create_client( - coro_io::ExecutorWrapper<>* executor, std::string port) { - auto client = std::make_shared(executor); -#ifdef YLT_ENABLE_SSL - bool ok = client->init_ssl("../openssl_files", "server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); -#endif - auto ec = co_await client->connect("127.0.0.1", port); - REQUIRE(!ec); - co_return client; -} - -void show(auto& s) { return; } - -TEST_CASE("testing client") { - { - coro_rpc::coro_rpc_client client; - auto lazy_ret = client.connect("", ""); - auto ret = syncAwait(lazy_ret); - CHECK(ret == coro_rpc::errc::not_connected); - } - { - coro_rpc::coro_rpc_client client; - auto ret = client.sync_connect("", ""); - CHECK(ret == coro_rpc::errc::not_connected); - } - g_action = {}; - std::string port = std::to_string(coro_rpc_server_port); - asio::io_context io_context; - coro_io::ExecutorWrapper<> executor = io_context.get_executor(); - auto executor_ptr = &executor; - std::promise promise; - auto worker = std::make_unique(io_context); - auto future = promise.get_future(); - std::thread thd([&io_context, &promise] { - promise.set_value(); - io_context.run(); - }); - - future.wait(); - coro_rpc_server server(2, coro_rpc_server_port); -#ifdef YLT_ENABLE_SSL - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); -#endif - auto res = server.async_start(); - - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - SUBCASE("call rpc, function not registered") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, - ret.error().msg); - co_return; - }; - syncAwait(f()); - } - SUBCASE("call rpc, function not registered 2") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, - ret.error().msg); - co_return; - }; - syncAwait(f()); - } - - server.stop(); - - coro_rpc_server server2(2, coro_rpc_server_port); -#ifdef YLT_ENABLE_SSL - server2.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); -#endif - server2.register_handler(); - server2.register_handler(); - server2.register_handler(); - res = server2.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - SUBCASE("call rpc timeout") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call_for(10ms); - CHECK_MESSAGE(ret.error().code == coro_rpc::errc::timed_out, - ret.error().msg); - co_return; - }; - syncAwait(f()); - } - - SUBCASE("call rpc success") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK(ret.value() == std::string("hello")); - ret = co_await client->call_for(100ms); - CHECK(ret.value() == std::string("hello")); - co_return; - }; - syncAwait(f()); - } - - SUBCASE("call with large buffer") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - std::string arg; - arg.resize(2048); - auto ret = co_await client->template call(arg); - show(ret); - CHECK(ret.value() == arg); - co_return; - }; - syncAwait(f()); - } - - server.stop(); - worker = nullptr; - thd.join(); -} - -std::string get_first_local_ip() { - using asio::ip::tcp; - try { - tcp::resolver resolver(coro_io::get_global_executor()->get_asio_executor()); - tcp::resolver::query query(asio::ip::host_name(), ""); - tcp::resolver::iterator iter = resolver.resolve(query); - tcp::resolver::iterator end; // End marker. - while (iter != end) { - tcp::endpoint ep = *iter++; - auto addr = ep.address(); - if (addr.is_v4()) { - return addr.to_string(); - } - } - } catch (std::exception& e) { - ELOG_WARN << "get_first_local_ip error: " << e.what(); - } - - return "localhost"; -} - -void foo(const size_t& i) {} - -TEST_CASE("testing const & args") { - coro_rpc_server server(1, 8901); - server.register_handler(); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client{}; - syncAwait(client.connect("127.0.0.1:8901")); - const int& i = 0; - auto ret = syncAwait(client.call(i)); - CHECK(ret.has_value()); -} - -TEST_CASE("testing client with local ip") { - coro_rpc_server server(1, 8901); - server.register_handler(); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - std::string local_ip = get_first_local_ip(); - ELOG_INFO << "local ip: " << local_ip; - -// Due to windows strong host mode, we cant connect to localhost from specify -// nic ip address -#ifndef _WIN32 - std::string target_ip = "127.0.0.1"; -#else - std::string target_ip = local_ip; -#endif - coro_rpc_client client(local_ip); - auto ec = client.sync_connect(target_ip, "8901"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - coro_rpc_server server1(1, 8902, local_ip); - server1.register_handler(); - res = server1.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - ec = client.sync_connect(local_ip, "8902"); - REQUIRE_MESSAGE(!ec, ec.message()); - - ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - std::vector eps; - eps.push_back(asio::ip::tcp::endpoint( - asio::ip::address::from_string("192.0.2.1"), 8901)); - eps.push_back(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 8901)); - - auto local_ep = - asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 0); - asio::ip::tcp::socket socket(client.get_executor().get_asio_executor()); - socket.open(local_ep.protocol()); - socket.bind(local_ep); - - auto ec1 = async_simple::coro::syncAwait(coro_io::async_connect(socket, eps)); - CHECK(!ec1); - CHECK(socket.local_endpoint().address().to_string() == "127.0.0.1"); -} - -TEST_CASE("testing client with inject server") { - g_action = {}; - std::string port = std::to_string(coro_rpc_server_port); - ELOGV(INFO, "inject server port: %d", port.data()); - asio::io_context io_context; - coro_io::ExecutorWrapper<> executor = io_context.get_executor(); - auto executor_ptr = &executor; - auto worker = std::make_unique(io_context); - std::thread thd([&io_context] { - io_context.run(); - }); - coro_rpc_server server(2, coro_rpc_server_port); -#ifdef YLT_ENABLE_SSL - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); -#endif - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - SUBCASE("server run ok") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK(ret.value() == std::string("hello")); - co_return; - }; - syncAwait(f()); - } - - SUBCASE("client read length error") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - g_action = inject_action::close_socket_after_read_header; - auto ret = co_await client->template call(); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - }; - syncAwait(f()); - } - SUBCASE("client read body error") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - g_action = inject_action::close_socket_after_send_length; - auto ret = co_await client->template call(); - show(ret); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - }; - syncAwait(f()); - } - - server.stop(); - worker = nullptr; - thd.join(); - g_action = inject_action::nothing; -} -#ifdef YLT_ENABLE_SSL - -enum class ssl_type { _, fake, no }; - -template -class SSLClientTester { - public: - SSLClientTester(std::string base_path, unsigned short port, - ssl_type client_crt, ssl_type server_crt, ssl_type server_key, - ssl_type dh) - : port_(std::to_string(port)), - server(2, port), - base_path(base_path), - client_crt(client_crt), - server_crt(server_crt), - server_key(server_key), - dh(dh), - executor_(io_context.get_executor()) { - inject("client crt", client_crt_path, client_crt); - inject("server crt", server_crt_path, server_crt); - inject("server key", server_key_path, server_key); - inject("dh", dh_path, dh); - ssl_configure config{base_path, server_crt_path, server_key_path, dh_path}; - server.init_ssl(config); - server.template register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - - std::promise promise; - auto future = promise.get_future(); - worker = std::make_unique(io_context); - thd = std::thread([this, &promise] { - promise.set_value(); - io_context.run(); - }); - future.wait(); - } - ~SSLClientTester() { - worker = nullptr; - thd.join(); - } - void inject(std::string msg, std::string& path, ssl_type type) { - switch (type) { - case ssl_type::fake: { - path = "fake_" + path; - break; - } - case ssl_type::no: { - path = "no_" + path; - break; - } - default: { - } - } - ELOGV(INFO, "%s %s", msg.data(), path.data()); - } - void run() { - auto client = std::make_shared(&executor_); - bool ok = client->init_ssl(base_path, client_crt_path); - if (client_crt == ssl_type::fake || client_crt == ssl_type::no) { - REQUIRE(ok == false); - auto ec = syncAwait(client->connect("127.0.0.1", port_)); - REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); - auto ret = syncAwait(client->template call()); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::not_connected, - ret.error().msg); - } - else { - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - auto f = [this, &client]() -> Lazy { - auto ec = co_await client->connect("127.0.0.1", port_); - if (server_crt == ssl_type::_ && server_key == ssl_type::_) { - if (ec) { - ELOGV(INFO, "%s", gen_err().data()); - } - REQUIRE_MESSAGE(!ec, ec.message()); - auto ret = co_await client->template call(); - CHECK(ret.has_value()); - } - else { - REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); - } - }; - syncAwait(f()); - } - } - - std::string gen_err() { - auto to_string = [](ssl_type t) -> std::string { - switch (t) { - case ssl_type::fake: { - return "fake"s; - } - case ssl_type::no: { - return "no"s; - } - default: { - return "_"s; - } - } - }; - std::stringstream buf; - buf << "\n"; - buf << "client crt: " << client_crt_path << " __ " << to_string(client_crt) - << "; "; - buf << "server crt: " << server_crt_path << " __ " << to_string(server_crt) - << "; "; - buf << "server key: " << server_key_path << " __ " << to_string(server_key) - << "; "; - buf << "dh: " << dh_path << " __ " << to_string(dh) << "; "; - return buf.str(); - } - - std::string port_; - Server server; - std::string base_path; - std::string client_crt_path = "server.crt"; - std::string server_crt_path = "server.crt"; - std::string server_key_path = "server.key"; - std::string dh_path = "dhparam.pem"; - ssl_type client_crt; - ssl_type server_crt; - ssl_type server_key; - ssl_type dh; - asio::io_context io_context; - coro_io::ExecutorWrapper<> executor_; - std::thread thd; - std::unique_ptr worker; -}; - -TEST_CASE("testing client with ssl server") { - std::vector type_list{ssl_type::fake, ssl_type::no, ssl_type::_}; - std::string base_path = "../openssl_files"; - unsigned short port = 8809; - for (auto client_crt : type_list) { - for (auto server_crt : type_list) { - for (auto server_key : type_list) { - for (auto dh : type_list) { - SSLClientTester(base_path, port, client_crt, - server_crt, server_key, dh) - .run(); - } - } - } - } -} -#endif -TEST_CASE("testing client with eof") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - ret = client.sync_call(); - CHECK(ret.value() == "client hello"s); - - g_action = inject_action::client_close_socket_after_send_partial_header; - ret = client.sync_call(); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); -} -TEST_CASE("testing client with attachment") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call(); - CHECK(ret.has_value()); - CHECK(client.get_resp_attachment() == ""); - - client.set_req_attachment("hellohi"); - ret = client.sync_call(); - CHECK(ret.has_value()); - CHECK(client.get_resp_attachment() == "hellohi"); - - ret = client.sync_call(); - CHECK(ret.has_value()); - CHECK(client.get_resp_attachment() == ""); - - char buf[100], short_buf[1]; - - client.set_req_attachment("This is attachment."); - client.set_resp_attachment_buf(buf); - ret = client.sync_call(); - assert(client.get_resp_attachment() == "This is attachment."); - assert(client.is_resp_attachment_in_external_buf()); - assert(client.get_resp_attachment().data() == buf); - - client.set_req_attachment("This is attachment."); - ret = client.sync_call(); - assert(client.get_resp_attachment() == "This is attachment."); - assert(!client.is_resp_attachment_in_external_buf()); - assert(client.get_resp_attachment().data() != buf); - - client.set_req_attachment("This is attachment."); - client.set_resp_attachment_buf(short_buf); - ret = client.sync_call(); - assert(client.get_resp_attachment() == "This is attachment."); - assert(!client.is_resp_attachment_in_external_buf()); - assert(client.get_resp_attachment().data() != short_buf); -} - -TEST_CASE("testing std::string_view") { - g_action = {}; - coro_rpc_server server(1, 8801); - server.register_handler(); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call("123"); - CHECK(ret.value() == "123OK"); - - ret = client.sync_call("1231232132123123"); - CHECK(ret.value() == "1231232132123123OK"); - - ret = client.sync_call("ABDD"); - CHECK(ret.value() == "ABDDOK"); -} - -TEST_CASE("testing client with context response user-defined error") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE(!ec); - auto ret = client.sync_call(); - REQUIRE(!ret.has_value()); - CHECK(ret.error().code == coro_rpc::errc{1004}); - CHECK(ret.error().msg == "My Error."); - CHECK(client.has_closed() == false); - auto ret2 = client.sync_call(); - REQUIRE(ret2.has_value()); - CHECK(ret2.value() == "hello"); -} - -TEST_CASE("testing client with shutdown") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - g_action = inject_action::nothing; - auto ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - ret = client.sync_call(); - CHECK(ret.value() == "client hello"s); - - g_action = inject_action::client_shutdown_socket_after_send_header; - ret = client.sync_call(); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - - g_action = {}; -} -TEST_CASE("testing client timeout") { - coro_rpc_server server(2, 8801); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - - SUBCASE("connect, 0ms connect timeout") { - coro_rpc_client client; - coro_rpc_client::config config{}; - config.connect_timeout_duration = 0ms; - bool r = client.init_config(config); - CHECK(r); - auto ret = client.connect("127.0.0.1", "8801"); - auto val = syncAwait(ret); - if (val) { - CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); - } - } - SUBCASE("connect, -1ms never timeout") { - coro_rpc_client client; - coro_rpc_client::config config{}; - config.connect_timeout_duration = -1ms; // less than 0, no timeout - // checking. - bool r = client.init_config(config); - CHECK(r); - auto ret = client.connect( - "127.0.0.1", - "8801"); // this arg won't update config connect timeout duration. - auto val = syncAwait(ret); - - CHECK(!val); - } - - SUBCASE("connect, 0ms request timeout") { - coro_rpc_client client; - coro_rpc_client::config config{}; - config.connect_timeout_duration = 1000ms; - config.request_timeout_duration = 0ms; - bool r = client.init_config(config); - CHECK(r); - auto ret = client.connect("127.0.0.1", "8801"); - auto val = syncAwait(ret); - - CHECK(!val); - - // TODO will remove via later for 0ms timeout - auto result = syncAwait(client.call().via(&client.get_executor())); - - if (result.has_value()) { - ELOG_INFO << result.value(); - } - else { - CHECK_MESSAGE(result.error().code == coro_rpc::errc::timed_out, - result.error().msg); - } - } - SUBCASE("connect, -1ms never request timeout") { - coro_rpc_client client; - client.get_config().request_timeout_duration = - -1ms; // less than 0, never timeout. - auto ret = client.connect("127.0.0.1", "8801"); - auto val = syncAwait(ret); - - CHECK(!val); - auto result = syncAwait(client.call()); - - CHECK(result.has_value()); - } - SUBCASE("connect, ip timeout") { - g_action = {}; - // https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error - coro_rpc_client client(coro_io::get_global_executor()); - auto ret = client.connect("10.255.255.1", "8801", 5ms); - auto val = syncAwait(ret); - CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); - } -} -TEST_CASE("testing client connect err") { - coro_rpc_client client(coro_io::get_global_executor()); - auto val = syncAwait(client.connect("127.0.0.1", "8801")); - CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); -} -#ifdef UNIT_TEST_INJECT -TEST_CASE("testing client sync connect, unit test inject only") { - coro_rpc_client client(coro_io::get_global_executor()); - auto val = client.sync_connect("127.0.0.1", "8801"); - CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); -#ifdef YLT_ENABLE_SSL - SUBCASE("client use ssl but server don't use ssl") { - g_action = {}; - coro_rpc_server server(2, 8801); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client2(coro_io::get_global_executor()); - bool ok = client2.init_ssl("../openssl_files", "server.crt"); - CHECK(ok == true); - val = client2.sync_connect("127.0.0.1", "8801"); - CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); - } -#endif -} -#endif -TEST_CASE("testing client call timeout") { - g_action = {}; - SUBCASE("write timeout") { - g_action = inject_action::force_inject_client_write_data_timeout; - // coro_rpc_server server(2, 8801); - // server.async_start().start([](auto&&) { - // }); - coro_rpc_client client(coro_io::get_global_executor()); - // auto ec_lazy = client.connect("127.0.0.1", "8801", 5ms); - // auto ec = syncAwait(ec_lazy); - // assert(ec == std::errc{}); - auto ret = client.call(); - auto val = syncAwait(ret); - CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, - val.error().msg); - g_action = inject_action::nothing; - } -#ifdef __GNUC__ - SUBCASE("read timeout") { - g_action = {}; - coro_rpc_server server(2, 8801); - - server.register_handler(); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec_lazy = client.connect("127.0.0.1", "8801"); - auto ec = syncAwait(ec_lazy); - REQUIRE(!ec); - auto ret = client.call_for(10ms); - auto val = syncAwait(ret); - CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, - val.error().msg); - } -#endif - g_action = {}; -} -static std::string_view echo(std::string_view data) { return data; } -TEST_CASE("testing client reconnect") { - coro_rpc_server s1(1, 9002), s2(1, 9003); - s1.async_start(); - s2.async_start(); - s2.register_handler(); - SUBCASE("test client reconnect") { - coro_rpc_client cli; - auto result = syncAwait(cli.connect("127.0.0.1", "9002")); - CHECK_MESSAGE(!result, result.message()); - result = syncAwait(cli.connect("127.0.0.1", "9003")); - CHECK_MESSAGE(!result, result.message()); - auto result2 = syncAwait(cli.call("hi")); - REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); - CHECK_MESSAGE(result2.value() == "hi", result2.value()); - } - SUBCASE("test client reconnect if client close") { - coro_rpc_client cli; - auto result = syncAwait(cli.connect("127.0.0.1", "9002")); - CHECK_MESSAGE(!result, result.message()); - cli.close(); - result = syncAwait(cli.connect("127.0.0.1", "9003")); - CHECK_MESSAGE(!result, result.message()); - auto result2 = syncAwait(cli.call("hi")); - REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); - CHECK_MESSAGE(result2.value() == "hi", result2.value()); - } - SUBCASE("test client reconnect if server close") { - coro_rpc_client cli; - auto result = syncAwait(cli.connect("127.0.0.1", "9002")); - CHECK_MESSAGE(!result, result.message()); - s1.stop(); - result = syncAwait(cli.connect("127.0.0.1", "9003")); - CHECK_MESSAGE(!result, result.message()); - auto result2 = syncAwait(cli.call("hi")); - REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); - CHECK_MESSAGE(result2.value() == "hi", result2.value()); - } -} -std::errc init_acceptor(auto& acceptor_, auto port_) { - using asio::ip::tcp; - auto endpoint = tcp::endpoint(tcp::v4(), port_); - acceptor_.open(endpoint.protocol()); - acceptor_.set_option(tcp::acceptor::reuse_address(true)); - - asio::error_code ec; - acceptor_.bind(endpoint, ec); - if (ec) { - ELOGV(ERROR, "bind error : %s", ec.message().data()); - acceptor_.cancel(ec); - acceptor_.close(ec); - return std::errc::address_in_use; - } - acceptor_.listen(); - - ELOGV(INFO, "listen port %d successfully", port_); - return std::errc{}; -} -// TODO: will open after code refactor. -// TEST_CASE("testing read body timeout") { -// register_handler(); -// std::promise server_p; -// std::promise p; -// std::thread thd = std::thread([&server_p, &p]() { -// asio::io_context io_context; -// tcp::acceptor acceptor(io_context); -// tcp::socket socket(io_context); -// auto ec = init_acceptor(acceptor, 8801); -// REQUIRE(ec == std::errc{}); -// easylog::info("server started"); -// server_p.set_value(); -// auto ec2 = accept(acceptor, socket); -// REQUIRE(!ec2); -// std::array head; -// read(socket, asio::buffer(head, RPC_HEAD_LEN)); -// req_header header; -// auto errc = struct_pack::deserialize_to(header, head); -// REQUIRE(errc == std::errc{}); -// std::vector body_; -// body_.resize(header.length); -// auto ret = read(socket, asio::buffer(body_.data(), header.length)); -// REQUIRE(!ret.first); -// auto buf = struct_pack::serialize_with_offset( -// /*offset = */ RESP_HEAD_LEN, std::monostate{}); -// *((uint32_t*)buf.data()) = buf.size() - RESP_HEAD_LEN; -// write(socket, asio::buffer(buf.data(), RESP_HEAD_LEN)); -// std::this_thread::sleep_for(50ms); -// write(socket, asio::buffer(buf.data() + RESP_HEAD_LEN, -// buf.size() - RESP_HEAD_LEN)); -// p.set_value(); -// }); -// easylog::info("wait for server start"); -// server_p.get_future().wait(); -// easylog::info("custom server started"); -// coro_rpc_client client; -// auto ec_lazy = client.connect("127.0.0.1", "8801"s, 5ms); -// auto ec = syncAwait(ec_lazy); -// REQUIRE(ec == std::errc{}); -// auto ret = client.call_for(50ms); -// auto val = syncAwait(ret); -// CHECK_MESSAGE(val.error().code == std::errc::timed_out, val.error().msg); -// thd.join(); -// p.get_future().wait(); -// } +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "doctest.h" +#include "rpc_api.hpp" +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/coro_rpc/impl/coro_rpc_client.hpp" +#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" +#include "ylt/coro_rpc/impl/errno.h" +using namespace coro_rpc; +using namespace std::chrono_literals; +using namespace std::string_literals; +using namespace async_simple::coro; +using asio::ip::tcp; + +template +class ClientTester { + ClientTester(unsigned short port) : port_(std::to_string(port)) {} + std::string port_; +}; + +static unsigned short coro_rpc_server_port = 8803; +Lazy> create_client( + coro_io::ExecutorWrapper<>* executor, std::string port) { + auto client = std::make_shared(executor); +#ifdef YLT_ENABLE_SSL + bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); +#endif + auto ec = co_await client->connect("127.0.0.1", port); + REQUIRE(!ec); + co_return client; +} + +void show(auto& s) { return; } + +TEST_CASE("testing client") { + { + coro_rpc::coro_rpc_client client; + auto lazy_ret = client.connect("", ""); + auto ret = syncAwait(lazy_ret); + CHECK(ret == coro_rpc::errc::not_connected); + } + { + coro_rpc::coro_rpc_client client; + auto ret = client.sync_connect("", ""); + CHECK(ret == coro_rpc::errc::not_connected); + } + g_action = {}; + std::string port = std::to_string(coro_rpc_server_port); + asio::io_context io_context; + coro_io::ExecutorWrapper<> executor = io_context.get_executor(); + auto executor_ptr = &executor; + std::promise promise; + auto worker = std::make_unique(io_context); + auto future = promise.get_future(); + std::thread thd([&io_context, &promise] { + promise.set_value(); + io_context.run(); + }); + + future.wait(); + coro_rpc_server server(2, coro_rpc_server_port); +#ifdef YLT_ENABLE_SSL + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); +#endif + auto res = server.async_start(); + + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + SUBCASE("call rpc, function not registered") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, + ret.error().msg); + co_return; + }; + syncAwait(f()); + } + SUBCASE("call rpc, function not registered 2") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, + ret.error().msg); + co_return; + }; + syncAwait(f()); + } + + server.stop(); + + coro_rpc_server server2(2, coro_rpc_server_port); +#ifdef YLT_ENABLE_SSL + server2.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); +#endif + server2.register_handler(); + server2.register_handler(); + server2.register_handler(); + res = server2.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + SUBCASE("call rpc timeout") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call_for(10ms); + CHECK_MESSAGE(ret.error().code == coro_rpc::errc::timed_out, + ret.error().msg); + co_return; + }; + syncAwait(f()); + } + + SUBCASE("call rpc success") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK(ret.value() == std::string("hello")); + ret = co_await client->call_for(100ms); + CHECK(ret.value() == std::string("hello")); + co_return; + }; + syncAwait(f()); + } + + SUBCASE("call with large buffer") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + std::string arg; + arg.resize(2048); + auto ret = co_await client->template call(arg); + show(ret); + CHECK(ret.value() == arg); + co_return; + }; + syncAwait(f()); + } + + server.stop(); + worker = nullptr; + thd.join(); +} + +std::string get_first_local_ip() { + using asio::ip::tcp; + try { + tcp::resolver resolver(coro_io::get_global_executor()->get_asio_executor()); + tcp::resolver::query query(asio::ip::host_name(), ""); + tcp::resolver::iterator iter = resolver.resolve(query); + tcp::resolver::iterator end; // End marker. + while (iter != end) { + tcp::endpoint ep = *iter++; + auto addr = ep.address(); + if (addr.is_v4()) { + return addr.to_string(); + } + } + } catch (std::exception& e) { + ELOG_WARN << "get_first_local_ip error: " << e.what(); + } + + return "localhost"; +} + +void foo(const size_t& i) {} + +TEST_CASE("testing const & args") { + coro_rpc_server server(1, 8901); + server.register_handler(); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client{}; + syncAwait(client.connect("127.0.0.1:8901")); + const int& i = 0; + auto ret = syncAwait(client.call(i)); + CHECK(ret.has_value()); +} + +TEST_CASE("testing client with local ip") { + coro_rpc_server server(1, 8901); + server.register_handler(); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + std::string local_ip = get_first_local_ip(); + ELOG_INFO << "local ip: " << local_ip; + +// Due to windows strong host mode, we cant connect to localhost from specify +// nic ip address +#ifndef _WIN32 + std::string target_ip = "127.0.0.1"; +#else + std::string target_ip = local_ip; +#endif + coro_rpc_client client(local_ip); + auto ec = client.sync_connect(target_ip, "8901"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + coro_rpc_server server1(1, 8902, local_ip); + server1.register_handler(); + res = server1.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + ec = client.sync_connect(local_ip, "8902"); + REQUIRE_MESSAGE(!ec, ec.message()); + + ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + std::vector eps; + eps.push_back(asio::ip::tcp::endpoint( + asio::ip::address::from_string("192.0.2.1"), 8901)); + eps.push_back(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 8901)); + + auto local_ep = + asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 0); + asio::ip::tcp::socket socket(client.get_executor().get_asio_executor()); + socket.open(local_ep.protocol()); + socket.bind(local_ep); + + auto ec1 = async_simple::coro::syncAwait(coro_io::async_connect(socket, eps)); + CHECK(!ec1); + CHECK(socket.local_endpoint().address().to_string() == "127.0.0.1"); +} + +TEST_CASE("testing client with inject server") { + g_action = {}; + std::string port = std::to_string(coro_rpc_server_port); + ELOGV(INFO, "inject server port: %d", port.data()); + asio::io_context io_context; + coro_io::ExecutorWrapper<> executor = io_context.get_executor(); + auto executor_ptr = &executor; + auto worker = std::make_unique(io_context); + std::thread thd([&io_context] { + io_context.run(); + }); + coro_rpc_server server(2, coro_rpc_server_port); +#ifdef YLT_ENABLE_SSL + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); +#endif + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + SUBCASE("server run ok") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK(ret.value() == std::string("hello")); + co_return; + }; + syncAwait(f()); + } + + SUBCASE("client read length error") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + g_action = inject_action::close_socket_after_read_header; + auto ret = co_await client->template call(); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + }; + syncAwait(f()); + } + SUBCASE("client read body error") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + g_action = inject_action::close_socket_after_send_length; + auto ret = co_await client->template call(); + show(ret); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + }; + syncAwait(f()); + } + + server.stop(); + worker = nullptr; + thd.join(); + g_action = inject_action::nothing; +} +#ifdef YLT_ENABLE_SSL + +enum class ssl_type { _, fake, no }; + +template +class SSLClientTester { + public: + SSLClientTester(std::string base_path, unsigned short port, + ssl_type client_crt, ssl_type server_crt, ssl_type server_key, + ssl_type dh) + : port_(std::to_string(port)), + server(2, port), + base_path(base_path), + client_crt(client_crt), + server_crt(server_crt), + server_key(server_key), + dh(dh), + executor_(io_context.get_executor()) { + inject("client crt", client_crt_path, client_crt); + inject("server crt", server_crt_path, server_crt); + inject("server key", server_key_path, server_key); + inject("dh", dh_path, dh); + ssl_configure config{base_path, server_crt_path, server_key_path, dh_path}; + server.init_ssl(config); + server.template register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + + std::promise promise; + auto future = promise.get_future(); + worker = std::make_unique(io_context); + thd = std::thread([this, &promise] { + promise.set_value(); + io_context.run(); + }); + future.wait(); + } + ~SSLClientTester() { + worker = nullptr; + thd.join(); + } + void inject(std::string msg, std::string& path, ssl_type type) { + switch (type) { + case ssl_type::fake: { + path = "fake_" + path; + break; + } + case ssl_type::no: { + path = "no_" + path; + break; + } + default: { + } + } + ELOGV(INFO, "%s %s", msg.data(), path.data()); + } + void run() { + auto client = std::make_shared(&executor_); + bool ok = client->init_ssl(base_path, client_crt_path); + if (client_crt == ssl_type::fake || client_crt == ssl_type::no) { + REQUIRE(ok == false); + auto ec = syncAwait(client->connect("127.0.0.1", port_)); + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); + auto ret = syncAwait(client->template call()); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::not_connected, + ret.error().msg); + } + else { + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + auto f = [this, &client]() -> Lazy { + auto ec = co_await client->connect("127.0.0.1", port_); + if (server_crt == ssl_type::_ && server_key == ssl_type::_) { + if (ec) { + ELOGV(INFO, "%s", gen_err().data()); + } + REQUIRE_MESSAGE(!ec, ec.message()); + auto ret = co_await client->template call(); + CHECK(ret.has_value()); + } + else { + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); + } + }; + syncAwait(f()); + } + } + + std::string gen_err() { + auto to_string = [](ssl_type t) -> std::string { + switch (t) { + case ssl_type::fake: { + return "fake"s; + } + case ssl_type::no: { + return "no"s; + } + default: { + return "_"s; + } + } + }; + std::stringstream buf; + buf << "\n"; + buf << "client crt: " << client_crt_path << " __ " << to_string(client_crt) + << "; "; + buf << "server crt: " << server_crt_path << " __ " << to_string(server_crt) + << "; "; + buf << "server key: " << server_key_path << " __ " << to_string(server_key) + << "; "; + buf << "dh: " << dh_path << " __ " << to_string(dh) << "; "; + return buf.str(); + } + + std::string port_; + Server server; + std::string base_path; + std::string client_crt_path = "ca.crt"; + std::string server_crt_path = "server.crt"; + std::string server_key_path = "server.key"; + std::string dh_path = "dhparam.pem"; + ssl_type client_crt; + ssl_type server_crt; + ssl_type server_key; + ssl_type dh; + asio::io_context io_context; + coro_io::ExecutorWrapper<> executor_; + std::thread thd; + std::unique_ptr worker; +}; + +TEST_CASE("testing client with ssl server") { + std::string base_path = "../openssl_files"; + unsigned short port = 8809; + + // Test client certificate validation scenarios + // Server always uses valid certificates (ssl_type::_) + // Client uses different CA certificates (valid, fake, missing) + for (auto client_crt : {ssl_type::_, ssl_type::fake, ssl_type::no}) { + for (auto server_crt : {ssl_type::_}) { + for (auto server_key : {ssl_type::_}) { + for (auto dh : {ssl_type::_}) { + SSLClientTester(base_path, port, client_crt, + server_crt, server_key, dh) + .run(); + } + } + } + } +} +#endif +TEST_CASE("testing client with eof") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + ret = client.sync_call(); + CHECK(ret.value() == "client hello"s); + + g_action = inject_action::client_close_socket_after_send_partial_header; + ret = client.sync_call(); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); +} +TEST_CASE("testing client with attachment") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call(); + CHECK(ret.has_value()); + CHECK(client.get_resp_attachment() == ""); + + client.set_req_attachment("hellohi"); + ret = client.sync_call(); + CHECK(ret.has_value()); + CHECK(client.get_resp_attachment() == "hellohi"); + + ret = client.sync_call(); + CHECK(ret.has_value()); + CHECK(client.get_resp_attachment() == ""); + + char buf[100], short_buf[1]; + + client.set_req_attachment("This is attachment."); + client.set_resp_attachment_buf(buf); + ret = client.sync_call(); + assert(client.get_resp_attachment() == "This is attachment."); + assert(client.is_resp_attachment_in_external_buf()); + assert(client.get_resp_attachment().data() == buf); + + client.set_req_attachment("This is attachment."); + ret = client.sync_call(); + assert(client.get_resp_attachment() == "This is attachment."); + assert(!client.is_resp_attachment_in_external_buf()); + assert(client.get_resp_attachment().data() != buf); + + client.set_req_attachment("This is attachment."); + client.set_resp_attachment_buf(short_buf); + ret = client.sync_call(); + assert(client.get_resp_attachment() == "This is attachment."); + assert(!client.is_resp_attachment_in_external_buf()); + assert(client.get_resp_attachment().data() != short_buf); +} + +TEST_CASE("testing std::string_view") { + g_action = {}; + coro_rpc_server server(1, 8801); + server.register_handler(); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call("123"); + CHECK(ret.value() == "123OK"); + + ret = client.sync_call("1231232132123123"); + CHECK(ret.value() == "1231232132123123OK"); + + ret = client.sync_call("ABDD"); + CHECK(ret.value() == "ABDDOK"); +} + +TEST_CASE("testing client with context response user-defined error") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE(!ec); + auto ret = client.sync_call(); + REQUIRE(!ret.has_value()); + CHECK(ret.error().code == coro_rpc::errc{1004}); + CHECK(ret.error().msg == "My Error."); + CHECK(client.has_closed() == false); + auto ret2 = client.sync_call(); + REQUIRE(ret2.has_value()); + CHECK(ret2.value() == "hello"); +} + +TEST_CASE("testing client with shutdown") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + g_action = inject_action::nothing; + auto ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + ret = client.sync_call(); + CHECK(ret.value() == "client hello"s); + + g_action = inject_action::client_shutdown_socket_after_send_header; + ret = client.sync_call(); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + + g_action = {}; +} +TEST_CASE("testing client timeout") { + coro_rpc_server server(2, 8801); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + + SUBCASE("connect, 0ms connect timeout") { + coro_rpc_client client; + coro_rpc_client::config config{}; + config.connect_timeout_duration = 0ms; + bool r = client.init_config(config); + CHECK(r); + auto ret = client.connect("127.0.0.1", "8801"); + auto val = syncAwait(ret); + if (val) { + CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); + } + } + SUBCASE("connect, -1ms never timeout") { + coro_rpc_client client; + coro_rpc_client::config config{}; + config.connect_timeout_duration = -1ms; // less than 0, no timeout + // checking. + bool r = client.init_config(config); + CHECK(r); + auto ret = client.connect( + "127.0.0.1", + "8801"); // this arg won't update config connect timeout duration. + auto val = syncAwait(ret); + + CHECK(!val); + } + + SUBCASE("connect, 0ms request timeout") { + coro_rpc_client client; + coro_rpc_client::config config{}; + config.connect_timeout_duration = 1000ms; + config.request_timeout_duration = 0ms; + bool r = client.init_config(config); + CHECK(r); + auto ret = client.connect("127.0.0.1", "8801"); + auto val = syncAwait(ret); + + CHECK(!val); + + // TODO will remove via later for 0ms timeout + auto result = syncAwait(client.call().via(&client.get_executor())); + + if (result.has_value()) { + ELOG_INFO << result.value(); + } + else { + CHECK_MESSAGE(result.error().code == coro_rpc::errc::timed_out, + result.error().msg); + } + } + SUBCASE("connect, -1ms never request timeout") { + coro_rpc_client client; + client.get_config().request_timeout_duration = + -1ms; // less than 0, never timeout. + auto ret = client.connect("127.0.0.1", "8801"); + auto val = syncAwait(ret); + + CHECK(!val); + auto result = syncAwait(client.call()); + + CHECK(result.has_value()); + } + SUBCASE("connect, ip timeout") { + g_action = {}; + // https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error + coro_rpc_client client(coro_io::get_global_executor()); + auto ret = client.connect("10.255.255.1", "8801", 5ms); + auto val = syncAwait(ret); + CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); + } +} +TEST_CASE("testing client connect err") { + coro_rpc_client client(coro_io::get_global_executor()); + auto val = syncAwait(client.connect("127.0.0.1", "8801")); + CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); +} +#ifdef UNIT_TEST_INJECT +TEST_CASE("testing client sync connect, unit test inject only") { + coro_rpc_client client(coro_io::get_global_executor()); + auto val = client.sync_connect("127.0.0.1", "8801"); + CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); +#ifdef YLT_ENABLE_SSL + SUBCASE("client use ssl but server don't use ssl") { + g_action = {}; + coro_rpc_server server(2, 8801); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client2(coro_io::get_global_executor()); + bool ok = client2.init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + CHECK(ok == true); + val = client2.sync_connect("127.0.0.1", "8801"); + CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); + } +#endif +} +#endif +TEST_CASE("testing client call timeout") { + g_action = {}; + SUBCASE("write timeout") { + g_action = inject_action::force_inject_client_write_data_timeout; + // coro_rpc_server server(2, 8801); + // server.async_start().start([](auto&&) { + // }); + coro_rpc_client client(coro_io::get_global_executor()); + // auto ec_lazy = client.connect("127.0.0.1", "8801", 5ms); + // auto ec = syncAwait(ec_lazy); + // assert(ec == std::errc{}); + auto ret = client.call(); + auto val = syncAwait(ret); + CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, + val.error().msg); + g_action = inject_action::nothing; + } +#ifdef __GNUC__ + SUBCASE("read timeout") { + g_action = {}; + coro_rpc_server server(2, 8801); + + server.register_handler(); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec_lazy = client.connect("127.0.0.1", "8801"); + auto ec = syncAwait(ec_lazy); + REQUIRE(!ec); + auto ret = client.call_for(10ms); + auto val = syncAwait(ret); + CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, + val.error().msg); + } +#endif + g_action = {}; +} +static std::string_view echo(std::string_view data) { return data; } +TEST_CASE("testing client reconnect") { + coro_rpc_server s1(1, 9002), s2(1, 9003); + s1.async_start(); + s2.async_start(); + s2.register_handler(); + SUBCASE("test client reconnect") { + coro_rpc_client cli; + auto result = syncAwait(cli.connect("127.0.0.1", "9002")); + CHECK_MESSAGE(!result, result.message()); + result = syncAwait(cli.connect("127.0.0.1", "9003")); + CHECK_MESSAGE(!result, result.message()); + auto result2 = syncAwait(cli.call("hi")); + REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); + CHECK_MESSAGE(result2.value() == "hi", result2.value()); + } + SUBCASE("test client reconnect if client close") { + coro_rpc_client cli; + auto result = syncAwait(cli.connect("127.0.0.1", "9002")); + CHECK_MESSAGE(!result, result.message()); + cli.close(); + result = syncAwait(cli.connect("127.0.0.1", "9003")); + CHECK_MESSAGE(!result, result.message()); + auto result2 = syncAwait(cli.call("hi")); + REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); + CHECK_MESSAGE(result2.value() == "hi", result2.value()); + } + SUBCASE("test client reconnect if server close") { + coro_rpc_client cli; + auto result = syncAwait(cli.connect("127.0.0.1", "9002")); + CHECK_MESSAGE(!result, result.message()); + s1.stop(); + result = syncAwait(cli.connect("127.0.0.1", "9003")); + CHECK_MESSAGE(!result, result.message()); + auto result2 = syncAwait(cli.call("hi")); + REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); + CHECK_MESSAGE(result2.value() == "hi", result2.value()); + } +} +std::errc init_acceptor(auto& acceptor_, auto port_) { + using asio::ip::tcp; + auto endpoint = tcp::endpoint(tcp::v4(), port_); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(tcp::acceptor::reuse_address(true)); + + asio::error_code ec; + acceptor_.bind(endpoint, ec); + if (ec) { + ELOGV(ERROR, "bind error : %s", ec.message().data()); + acceptor_.cancel(ec); + acceptor_.close(ec); + return std::errc::address_in_use; + } + acceptor_.listen(); + + ELOGV(INFO, "listen port %d successfully", port_); + return std::errc{}; +} +// TODO: will open after code refactor. +// TEST_CASE("testing read body timeout") { +// register_handler(); +// std::promise server_p; +// std::promise p; +// std::thread thd = std::thread([&server_p, &p]() { +// asio::io_context io_context; +// tcp::acceptor acceptor(io_context); +// tcp::socket socket(io_context); +// auto ec = init_acceptor(acceptor, 8801); +// REQUIRE(ec == std::errc{}); +// easylog::info("server started"); +// server_p.set_value(); +// auto ec2 = accept(acceptor, socket); +// REQUIRE(!ec2); +// std::array head; +// read(socket, asio::buffer(head, RPC_HEAD_LEN)); +// req_header header; +// auto errc = struct_pack::deserialize_to(header, head); +// REQUIRE(errc == std::errc{}); +// std::vector body_; +// body_.resize(header.length); +// auto ret = read(socket, asio::buffer(body_.data(), header.length)); +// REQUIRE(!ret.first); +// auto buf = struct_pack::serialize_with_offset( +// /*offset = */ RESP_HEAD_LEN, std::monostate{}); +// *((uint32_t*)buf.data()) = buf.size() - RESP_HEAD_LEN; +// write(socket, asio::buffer(buf.data(), RESP_HEAD_LEN)); +// std::this_thread::sleep_for(50ms); +// write(socket, asio::buffer(buf.data() + RESP_HEAD_LEN, +// buf.size() - RESP_HEAD_LEN)); +// p.set_value(); +// }); +// easylog::info("wait for server start"); +// server_p.get_future().wait(); +// easylog::info("custom server started"); +// coro_rpc_client client; +// auto ec_lazy = client.connect("127.0.0.1", "8801"s, 5ms); +// auto ec = syncAwait(ec_lazy); +// REQUIRE(ec == std::errc{}); +// auto ret = client.call_for(50ms); +// auto val = syncAwait(ret); +// CHECK_MESSAGE(val.error().code == std::errc::timed_out, val.error().msg); +// thd.join(); +// p.get_future().wait(); +// } diff --git a/src/coro_rpc/tests/test_coro_rpc_server.cpp b/src/coro_rpc/tests/test_coro_rpc_server.cpp index be8b9a819..91b7fa065 100644 --- a/src/coro_rpc/tests/test_coro_rpc_server.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_server.cpp @@ -587,8 +587,9 @@ TEST_CASE("testing coro rpc ssl subserver") { auto ret = syncAwait(client.call()); REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); coro_http::coro_http_client cli; - CHECK_MESSAGE(cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"), - "init ssl failed"); + CHECK_MESSAGE( + cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"), + "init ssl failed"); auto result = syncAwait(cli.connect("https://localhost:8810")); CHECK_MESSAGE(!result.net_err, result.net_err.message()); result = syncAwait(cli.async_get("/index.html")); From f723d8a62c3ef24b6a2c9b849f5ab827ea98b81d Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Thu, 9 Apr 2026 22:48:47 +0800 Subject: [PATCH 30/38] feat: add SSL mutual authentication support - Add init_ssl with client certificate for mutual auth (RPC & HTTP) - Set is_ssl_schema_ = true in init_ssl() - Set SSL security level to 0 for test certificates (OpenSSL 3.0) - Change SSL context from sslv23 to tls for modern TLS - Skip hostname verification for IP addresses - Add mutual auth test files and certificates - Add SSL mutual auth documentation --- docs/NTLS_CI_CHECKLIST.md | 145 ++ docs/NTLS_CI_README.md | 384 ++++ generate_test_certs.bat | 107 ++ include/ylt/coro_rpc/impl/common_service.hpp | 19 +- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 33 +- include/ylt/coro_rpc/impl/coro_rpc_server.hpp | 4 +- .../standalone/cinatra/coro_http_client.hpp | 2 + src/certs/rsa/client-rsa-enc.crt | 19 + src/certs/rsa/client-rsa-enc.key | 28 + src/certs/rsa/client-rsa-sign.crt | 19 + src/certs/rsa/client-rsa-sign.key | 28 + src/certs/rsa/root-cert.crt | 19 + src/certs/rsa/root-key.pem | 28 + src/certs/rsa/server-rsa-enc.crt | 19 + src/certs/rsa/server-rsa-enc.key | 28 + src/certs/rsa/server-rsa-sign.crt | 19 + src/certs/rsa/server-rsa-sign.key | 28 + src/certs/sm2/README.md | 71 + src/certs/sm2/chain-ca.crt | 26 + src/certs/sm2/client_enc.crt | 13 + src/certs/sm2/client_enc.key | 5 + src/certs/sm2/client_enc_expire.crt | 13 + src/certs/sm2/client_sign.crt | 13 + src/certs/sm2/client_sign.key | 5 + src/certs/sm2/client_sign_expire.crt | 13 + src/certs/sm2/server_enc.crt | 13 + src/certs/sm2/server_enc.key | 5 + src/certs/sm2/server_enc2.crt | 17 + src/certs/sm2/server_enc2.key | 5 + src/certs/sm2/server_enc_expire.crt | 13 + src/certs/sm2/server_sign.crt | 13 + src/certs/sm2/server_sign.key | 5 + src/certs/sm2/server_sign2.crt | 17 + src/certs/sm2/server_sign2.key | 5 + src/certs/sm2/server_sign_expire.crt | 13 + src/coro_http/tests/CMakeLists.txt | 154 +- src/coro_http/tests/openssl_files/ca.crt | 21 + src/coro_http/tests/openssl_files/client.crt | 20 + src/coro_http/tests/openssl_files/client.key | 28 + src/coro_http/tests/openssl_files/server.crt | 39 +- src/coro_http/tests/openssl_files/server.key | 58 +- .../tests/test_cinatra_websocket.cpp | 891 +++++---- .../tests/test_http_ssl_mutual_auth.cpp | 392 ++-- src/coro_rpc/tests/CMakeLists.txt | 111 +- src/coro_rpc/tests/ServerTester.hpp | 910 ++++----- src/coro_rpc/tests/openssl_files/ca.crt | 21 + src/coro_rpc/tests/openssl_files/client.crt | 20 + src/coro_rpc/tests/openssl_files/client.key | 28 + src/coro_rpc/tests/openssl_files/fake.crt | 22 + src/coro_rpc/tests/openssl_files/fake.key | 28 + .../tests/openssl_files/fake_server.crt | 14 - .../tests/openssl_files/fake_server.key | 18 - .../generate_mutual_auth_certs.bat | 22 + .../generate_mutual_auth_certs.sh | 30 + src/coro_rpc/tests/openssl_files/server.crt | 35 +- src/coro_rpc/tests/openssl_files/server.key | 58 +- src/coro_rpc/tests/test_coro_rpc_client.cpp | 1696 ++++++++--------- src/coro_rpc/tests/test_coro_rpc_server.cpp | 1467 +++++++------- .../tests/test_rpc_ssl_mutual_auth.cpp | 402 ++-- .../en/coro_http/ssl_mutual_authentication.md | 291 +++ website/docs/en/coro_rpc/coro_rpc_client.md | 914 ++++----- website/docs/zh/coro_rpc/coro_rpc_client.md | 895 +++++---- 62 files changed, 5751 insertions(+), 4028 deletions(-) create mode 100644 docs/NTLS_CI_CHECKLIST.md create mode 100644 docs/NTLS_CI_README.md create mode 100644 generate_test_certs.bat create mode 100644 src/certs/rsa/client-rsa-enc.crt create mode 100644 src/certs/rsa/client-rsa-enc.key create mode 100644 src/certs/rsa/client-rsa-sign.crt create mode 100644 src/certs/rsa/client-rsa-sign.key create mode 100644 src/certs/rsa/root-cert.crt create mode 100644 src/certs/rsa/root-key.pem create mode 100644 src/certs/rsa/server-rsa-enc.crt create mode 100644 src/certs/rsa/server-rsa-enc.key create mode 100644 src/certs/rsa/server-rsa-sign.crt create mode 100644 src/certs/rsa/server-rsa-sign.key create mode 100644 src/certs/sm2/README.md create mode 100644 src/certs/sm2/chain-ca.crt create mode 100644 src/certs/sm2/client_enc.crt create mode 100644 src/certs/sm2/client_enc.key create mode 100644 src/certs/sm2/client_enc_expire.crt create mode 100644 src/certs/sm2/client_sign.crt create mode 100644 src/certs/sm2/client_sign.key create mode 100644 src/certs/sm2/client_sign_expire.crt create mode 100644 src/certs/sm2/server_enc.crt create mode 100644 src/certs/sm2/server_enc.key create mode 100644 src/certs/sm2/server_enc2.crt create mode 100644 src/certs/sm2/server_enc2.key create mode 100644 src/certs/sm2/server_enc_expire.crt create mode 100644 src/certs/sm2/server_sign.crt create mode 100644 src/certs/sm2/server_sign.key create mode 100644 src/certs/sm2/server_sign2.crt create mode 100644 src/certs/sm2/server_sign2.key create mode 100644 src/certs/sm2/server_sign_expire.crt create mode 100644 src/coro_http/tests/openssl_files/ca.crt create mode 100644 src/coro_http/tests/openssl_files/client.crt create mode 100644 src/coro_http/tests/openssl_files/client.key create mode 100644 src/coro_rpc/tests/openssl_files/ca.crt create mode 100644 src/coro_rpc/tests/openssl_files/client.crt create mode 100644 src/coro_rpc/tests/openssl_files/client.key create mode 100644 src/coro_rpc/tests/openssl_files/fake.crt create mode 100644 src/coro_rpc/tests/openssl_files/fake.key delete mode 100644 src/coro_rpc/tests/openssl_files/fake_server.crt delete mode 100644 src/coro_rpc/tests/openssl_files/fake_server.key create mode 100644 src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat create mode 100644 src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh create mode 100644 website/docs/en/coro_http/ssl_mutual_authentication.md diff --git a/docs/NTLS_CI_CHECKLIST.md b/docs/NTLS_CI_CHECKLIST.md new file mode 100644 index 000000000..6fae9b36a --- /dev/null +++ b/docs/NTLS_CI_CHECKLIST.md @@ -0,0 +1,145 @@ +# NTLS CI 快速检查清单 + +## ✅ 如何确认CI使用了你的配置 + +### 步骤1:确认文件已提交 +```bash +# 在你的fork仓库中检查 +git log --oneline --all -- .github/workflows/ntls_test.yml +# 应该能看到你提交的记录 +``` + +### 步骤2:在GitHub上检查 + +#### 方法A:在PR页面查看(推荐) +1. 打开你的PR页面 +2. 向下滚动到"Checks"部分 +3. **如果看到 "NTLS Test (Tongsuo)" 工作流** ✅ + - 说明CI正在使用你的配置 + - 点击可以查看详细运行情况 + +#### 方法B:在Actions页面查看 +1. 进入**你的fork仓库**(不是主仓库) +2. 点击顶部的 **"Actions"** 标签 +3. **如果左侧列表中有 "NTLS Test (Tongsuo)"** ✅ + - 说明配置已生效 +4. 点击工作流名称,查看运行历史 + +#### 方法C:查看CI日志确认 +1. 在PR的"Checks"部分,点击 "NTLS Test (Tongsuo)" +2. 点击具体的job(如 "ntls_test_ubuntu") +3. 展开 "Configure CMake with NTLS" 步骤 +4. **在日志中查找 `YLT_ENABLE_NTLS: ON`** ✅ + - 如果看到这个输出,说明使用了你的配置 + +### 步骤3:验证关键特征 + +你的CI配置应该包含以下特征,在日志中可以验证: + +- ✅ **编译Tongsuo库** - 日志中应该有 "Build and install Tongsuo" 步骤 +- ✅ **配置CMake启用NTLS** - 日志中应该有 `-DYLT_ENABLE_NTLS=ON` +- ✅ **编译NTLS示例** - 日志中应该有 "Verify NTLS examples are built" 步骤 +- ✅ **使用Tongsuo路径** - 日志中应该有 `/usr/local/tongsuo` 相关路径 + +## 🔍 如何区分fork的CI和主仓库的CI + +### 关键区别 + +| 特征 | Fork的CI | 主仓库的CI | +|------|---------|-----------| +| 工作流名称 | "NTLS Test (Tongsuo)" | 不存在(除非他们也添加了) | +| 显示位置 | 你的fork仓库的Actions页面 | 主仓库的Actions页面 | +| PR中的显示 | 会显示在你的PR中 | 不会显示(除非主仓库有) | + +### 确认方法 + +1. **查看工作流来源** + - 在PR的"Checks"部分,每个检查旁边会显示来源 + - 如果显示你的用户名,说明是fork的CI + - 如果显示主仓库名,说明是主仓库的CI + +2. **检查工作流文件位置** + - Fork的CI:`.github/workflows/ntls_test.yml` 在你的fork中 + - 主仓库的CI:`.github/workflows/ntls_test.yml` 在主仓库中 + +## 🚨 常见问题 + +### Q: 看不到CI运行怎么办? + +**检查清单:** +- [ ] 确认 `.github/workflows/ntls_test.yml` 文件已提交 +- [ ] 确认文件路径正确(`.github/workflows/` 目录) +- [ ] 检查YAML语法是否正确(可以在线验证) +- [ ] 确认你修改的文件符合触发条件(`paths:` 部分) + +**解决方法:** +```bash +# 重新提交CI文件 +git add .github/workflows/ntls_test.yml +git commit -m "Add NTLS CI workflow" +git push +``` + +### Q: CI运行但失败了? + +**查看日志:** +1. 点击失败的CI运行 +2. 查看红色标记的步骤 +3. 展开查看详细错误信息 + +**常见原因:** +- Tongsuo编译失败(网络问题、依赖缺失) +- CMake找不到Tongsuo(路径配置问题) +- 编译错误(代码问题) + +### Q: 如何手动触发CI? + +1. 进入你的fork仓库 +2. 点击 "Actions" 标签 +3. 选择 "NTLS Test (Tongsuo)" 工作流 +4. 点击 "Run workflow" 按钮 +5. 选择分支,点击绿色按钮 + +### Q: 不确定是否使用了我的配置? + +**验证方法:** +1. 查看CI日志中的CMake配置步骤 +2. 确认看到 `YLT_ENABLE_NTLS: ON` +3. 确认看到Tongsuo相关的路径和配置 +4. 如果看到这些,说明使用了你的配置 ✅ + +## 📝 快速验证命令 + +在你的fork仓库中运行: + +```bash +# 1. 检查文件是否存在 +ls -la .github/workflows/ntls_test.yml + +# 2. 检查文件内容 +head -20 .github/workflows/ntls_test.yml + +# 3. 检查是否已提交 +git ls-files .github/workflows/ntls_test.yml + +# 4. 查看提交历史 +git log --oneline -- .github/workflows/ntls_test.yml +``` + +## 🎯 成功标志 + +如果以下所有条件都满足,说明CI正确使用了你的配置: + +- ✅ PR的"Checks"部分显示 "NTLS Test (Tongsuo)" 工作流 +- ✅ 工作流运行成功(绿色对勾) +- ✅ CI日志中显示 `YLT_ENABLE_NTLS: ON` +- ✅ CI日志中显示Tongsuo编译和安装步骤 +- ✅ CI日志中显示NTLS示例编译成功 + +## 📞 需要帮助? + +如果仍然无法确认,可以: +1. 查看完整的CI日志 +2. 检查GitHub Actions的运行历史 +3. 在主仓库的PR中询问维护者 + diff --git a/docs/NTLS_CI_README.md b/docs/NTLS_CI_README.md new file mode 100644 index 000000000..9daaa52aa --- /dev/null +++ b/docs/NTLS_CI_README.md @@ -0,0 +1,384 @@ +# NTLS CI 测试说明 + +本文档说明如何使用GitHub Actions CI来测试NTLS(国密SSL)功能。 + +## 概述 + +由于NTLS功能依赖于Tongsuo库(而不是标准的OpenSSL),原仓库的CI配置无法测试NTLS相关功能。因此,我们创建了专门的CI工作流来测试NTLS功能。 + +## CI工作流 + +### 文件位置 + +CI配置文件位于:`.github/workflows/ntls_test.yml` + +### 触发条件 + +该工作流会在以下情况触发: +- 推送到 `main` 或 `develop` 分支 +- 创建或更新PR到 `main` 或 `develop` 分支 +- 手动触发(workflow_dispatch) +- 当相关文件发生变化时(包括NTLS相关代码、CMake配置、证书文件等) + +### 测试内容 + +CI工作流会执行以下步骤: + +1. **安装Tongsuo库** + - 从源码编译安装Tongsuo(支持NTLS的OpenSSL分支) + - 启用NTLS、SM2、SM3、SM4等国密算法支持 + +2. **配置和编译** + - 使用CMake配置项目,启用SSL和NTLS支持 + - 编译NTLS相关的示例程序(`ntls_server`, `ntls_client`等) + +3. **验证** + - 检查NTLS示例程序是否成功编译 + - 验证Tongsuo库中的NTLS符号 + - 运行基本测试 + +### 支持的编译器 + +- GCC +- Clang 17 + +### 构建模式 + +- Debug模式 + +## 使用方法 + +### 在Fork的仓库中 + +1. **将CI文件添加到你的fork** + ```bash + git add .github/workflows/ntls_test.yml + git commit -m "Add NTLS CI workflow" + git push + ``` + +2. **创建或更新PR** + - 当你创建PR时,CI会自动运行 + - 你可以在PR页面看到CI运行状态 + +3. **查看CI结果** + - 在PR页面点击"Checks"标签 + - 查看"NTLS Test (Tongsuo)"工作流的运行结果 + +### 如何确认CI使用了你的配置 + +#### 方法1:查看PR中的CI运行状态 + +1. **在PR页面查看** + - 打开你的PR页面 + - 向下滚动,你会看到"Checks"部分 + - 如果看到"NTLS Test (Tongsuo)"工作流,说明CI正在运行 + - 点击工作流名称可以查看详细信息 + +2. **检查工作流名称** + - 如果看到 `NTLS Test (Tongsuo)` 这个工作流名称,说明使用了你的配置 + - 主仓库的CI不会包含这个工作流(除非他们也添加了) + +#### 方法2:在Actions页面查看 + +1. **在你的fork仓库中** + - 进入你的fork仓库(不是主仓库) + - 点击顶部的"Actions"标签 + - 在左侧工作流列表中找到"NTLS Test (Tongsuo)" + - 如果能看到这个工作流,说明配置已生效 + +2. **查看运行历史** + - 点击工作流名称 + - 查看运行历史,确认是否有新的运行记录 + - 每次推送代码或更新PR时,应该会触发新的运行 + +#### 方法3:检查工作流文件是否存在 + +在你的fork仓库中检查: +```bash +# 在你的fork仓库中运行 +ls -la .github/workflows/ntls_test.yml +``` + +如果文件存在,CI应该会使用它。 + +#### 方法4:查看CI日志确认 + +1. **打开CI运行详情** + - 在PR页面点击"Checks"标签 + - 点击"NTLS Test (Tongsuo)"工作流 + - 点击具体的job(如"ntls_test_ubuntu") + +2. **检查关键步骤** + - 查看"Build and install Tongsuo"步骤 + - 查看"Configure CMake with NTLS"步骤 + - 如果这些步骤存在且成功,说明使用了你的CI配置 + +3. **查看CMake配置输出** + - 在"Configure CMake with NTLS"步骤的日志中 + - 应该能看到 `YLT_ENABLE_NTLS: ON` 的输出 + - 这证明CMake使用了你的配置 + +#### 方法5:通过GitHub API检查(高级) + +如果你想通过API检查,可以使用: +```bash +# 获取工作流列表 +curl -H "Authorization: token YOUR_TOKEN" \ + https://api.github.com/repos/YOUR_USERNAME/yalantinglibs/actions/workflows +``` + +#### 常见问题排查 + +**问题1:看不到CI运行** +- 确认 `.github/workflows/ntls_test.yml` 文件已提交到你的fork +- 检查文件路径是否正确(`.github/workflows/` 目录) +- 确认文件格式正确(YAML语法) + +**问题2:CI运行但失败** +- 查看CI日志中的错误信息 +- 检查Tongsuo编译是否成功 +- 确认CMake配置是否正确 + +**问题3:CI没有自动触发** +- 检查工作流的触发条件(`on:` 部分) +- 确认你修改的文件在触发路径中 +- 可以手动触发(workflow_dispatch) + +**问题4:不确定是否使用了fork的CI还是主仓库的CI** +- Fork的CI会在你的fork仓库的Actions页面显示 +- PR中的CI检查会显示来源仓库 +- 如果主仓库没有这个工作流,那么运行的肯定是你的配置 + +### 手动触发 + +如果需要手动触发CI: + +1. **在你的fork仓库中** + - 进入你的fork仓库(不是主仓库) + - 点击"Actions"标签 + - 选择"NTLS Test (Tongsuo)"工作流 + - 点击"Run workflow"按钮 + - 选择分支(通常是你的PR分支) + - 点击绿色的"Run workflow"按钮 + +2. **在PR中触发** + - 如果PR已经创建,可以重新推送代码来触发 + - 或者关闭并重新打开PR + +## 本地测试 + +如果你想在本地测试NTLS功能,可以按照以下步骤: + +### 1. 安装Tongsuo + +```bash +# 克隆Tongsuo仓库 +git clone https://github.com/Tongsuo-Project/Tongsuo.git +cd Tongsuo + +# 配置和编译 +./config --prefix=/usr/local/tongsuo \ + --openssldir=/usr/local/tongsuo/ssl \ + shared \ + -fPIC \ + enable-ntls \ + enable-sm2 \ + enable-sm3 \ + enable-sm4 \ + enable-tls1_3 + +make -j$(nproc) +sudo make install +sudo ldconfig +``` + +### 2. 配置环境变量 + +```bash +export PATH=/usr/local/tongsuo/bin:$PATH +export LD_LIBRARY_PATH=/usr/local/tongsuo/lib:$LD_LIBRARY_PATH +export PKG_CONFIG_PATH=/usr/local/tongsuo/lib/pkgconfig:$PKG_CONFIG_PATH +``` + +### 3. 编译项目 + +```bash +mkdir build && cd build +cmake .. \ + -DYLT_ENABLE_SSL=ON \ + -DYLT_ENABLE_NTLS=ON \ + -DCMAKE_PREFIX_PATH=/usr/local/tongsuo \ + -DOPENSSL_ROOT_DIR=/usr/local/tongsuo + +make -j$(nproc) +``` + +### 4. 运行示例 + +```bash +# 运行NTLS服务器示例 +./src/coro_rpc/examples/base_examples/ntls_server + +# 在另一个终端运行客户端 +./src/coro_rpc/examples/base_examples/ntls_client +``` + +## 关于Fork和PR的说明 + +### 权限问题 + +- **你不能直接修改主仓库的CI配置** +- 但是,**你可以在自己的fork中创建CI配置** +- 当你提交PR时,fork中的CI会运行,结果会显示在PR中 + +### 最佳实践 + +1. **在fork中创建CI配置** + - 将`.github/workflows/ntls_test.yml`添加到你的fork + - 这样每次推送代码时,CI会自动运行 + +2. **在PR中说明** + - 在PR描述中说明你添加了NTLS CI测试 + - 提供CI运行结果的链接 + +3. **与维护者沟通** + - 如果主仓库维护者希望合并你的CI配置,他们可以接受你的PR + - 或者,他们可以参考你的CI配置,在主仓库中创建类似的配置 + +## 故障排除 + +### CI失败常见原因 + +1. **Tongsuo编译失败** + - 检查Tongsuo版本是否可用 + - 确认所有依赖都已安装 + +2. **CMake找不到Tongsuo** + - 检查`CMAKE_PREFIX_PATH`和`OPENSSL_ROOT_DIR`设置 + - 确认库文件路径正确 + +3. **链接错误** + - 确认`LD_LIBRARY_PATH`包含Tongsuo库路径 + - 检查库文件是否存在 + +### 调试建议 + +1. 查看CI日志中的详细错误信息 +2. 在本地复现相同的环境进行测试 +3. 检查Tongsuo版本兼容性 + +## 相关资源 + +- [Tongsuo项目](https://github.com/Tongsuo-Project/Tongsuo) +- [NTLS文档](https://github.com/Tongsuo-Project/Tongsuo/wiki) +- [国密SSL标准](https://www.oscca.gov.cn/) + +## 关于测试环境的选择 + +### 当前CI配置 + +当前的NTLS CI配置测试了以下环境: +- ✅ Ubuntu 22.04 + GCC +- ✅ Ubuntu 22.04 + Clang 17 + +### 是否需要测试所有环境? + +**简短回答:不需要。** 对于PR测试,测试主要环境就足够了。 + +### 为什么不需要所有环境? + +1. **Tongsuo库的平台支持** + - Tongsuo主要在Linux平台上使用和测试 + - Linux是NTLS功能的主要部署环境 + - Mac和Windows上的Tongsuo支持可能有限或需要额外配置 + +2. **PR测试的目的** + - PR中的CI主要用于验证代码能正常编译和基本功能 + - 不需要覆盖所有可能的部署环境 + - 主仓库的CI会处理其他环境的测试 + +3. **资源效率** + - 测试所有环境会消耗大量CI资源 + - 增加CI运行时间,影响开发效率 + - 对于fork的PR,资源可能有限 + +4. **编译器差异** + - 当前配置已经测试了GCC和Clang两个主要编译器 + - 这已经覆盖了大部分编译问题 + - MSVC的测试可以在主仓库合并后进行 + +### 建议的测试策略 + +#### 最小配置(推荐用于PR) +- ✅ Ubuntu + GCC(主要编译器) +- ✅ Ubuntu + Clang(验证C++标准兼容性) + +**理由:** +- 覆盖了主要的Linux部署环境 +- 测试了两个主要编译器 +- 资源消耗合理 +- 足以验证NTLS功能的基本正确性 + +#### 完整配置(如果资源充足) +如果主仓库维护者希望,可以添加: +- Ubuntu + GCC +- Ubuntu + Clang +- ~~Mac + Clang~~(Tongsuo在Mac上支持有限) +- ~~Windows + MSVC~~(Tongsuo在Windows上支持有限) + +**注意:** Mac和Windows上的Tongsuo支持可能需要额外配置,不建议在PR阶段测试。 + +### 什么时候需要添加更多环境? + +1. **主仓库要求** + - 如果维护者明确要求测试特定环境 + - 如果项目有跨平台部署需求 + +2. **功能需要** + - 如果你的NTLS功能有平台特定的代码 + - 如果需要验证特定平台的兼容性 + +3. **资源充足** + - 如果你的fork有充足的CI资源 + - 如果测试时间不是问题 + +### 当前配置是否足够? + +**是的,当前配置已经足够用于PR测试:** + +✅ **优点:** +- 测试了主要部署平台(Linux) +- 覆盖了两个主要编译器(GCC、Clang) +- 资源消耗合理 +- 足以验证NTLS功能的基本正确性 + +✅ **验证内容:** +- NTLS代码能够编译 +- Tongsuo库能够正确链接 +- CMake配置正确 +- 基本功能正常 + +### 与维护者沟通 + +在PR中,你可以说明: +- 当前CI测试了Linux上的GCC和Clang +- 这是NTLS功能的主要部署环境 +- 如果需要,可以在主仓库合并后添加其他环境的测试 +- 或者,如果维护者需要,可以添加更多环境的测试 + +### 总结 + +**对于PR测试:** +- ✅ 测试主要环境(Linux + GCC/Clang)就足够了 +- ❌ 不需要测试所有环境(Mac、Windows等) +- ✅ 当前配置已经合理且足够 + +**如果主仓库合并后:** +- 维护者可以根据需要添加其他环境的测试 +- 或者你可以在后续PR中添加更多环境的支持 + +## 更新日志 + +- 2025-01-XX: 初始版本,支持Ubuntu 22.04上的GCC和Clang测试 + diff --git a/generate_test_certs.bat b/generate_test_certs.bat new file mode 100644 index 000000000..14ea48075 --- /dev/null +++ b/generate_test_certs.bat @@ -0,0 +1,107 @@ +@echo off +REM 生成测试 SSL 证书的批处理脚本 +REM 用于 yalantinglibs 的 SSL 示例程序 + +echo ======================================== +echo 生成测试 SSL 证书 +echo ======================================== + +REM 设置证书目录 +set CERT_DIR=.\certs + +REM 创建证书目录 +if not exist "%CERT_DIR%" ( + mkdir "%CERT_DIR%" +) + +cd /d "%CERT_DIR%" + +echo. +echo [1/8] 创建 OpenSSL 配置文件... +( +echo [req] +echo default_bits = 2048 +echo distinguished_name = req_distinguished_name +echo req_extensions = v3_req +echo x509_extensions = v3_ca +echo. +echo [req_distinguished_name] +echo countryName = CN +echo stateOrProvinceName = Beijing +echo localityName = Beijing +echo organizationName = Test +echo organizationalUnitName = Test +echo commonName = localhost +echo. +echo [v3_req] +echo keyUsage = keyEncipherment, dataEncipherment +echo extendedKeyUsage = serverAuth +echo subjectAltName = @alt_names +echo. +echo [v3_ca] +echo subjectKeyIdentifier = hash +echo authorityKeyIdentifier = keyid:always,issuer +echo basicConstraints = critical,CA:TRUE +echo keyUsage = critical, cRLSign, keyCertSign +echo. +echo [alt_names] +echo DNS.1 = localhost +echo DNS.2 = 127.0.0.1 +echo IP.1 = 127.0.0.1 +) > openssl.cnf + +echo. +echo [2/8] 生成 CA 私钥... +openssl genrsa -out root-ca.key 2048 + +echo. +echo [3/8] 生成 CA 证书... +openssl req -new -x509 -days 3650 -key root-ca.key -out root-cert.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=Test-Root-CA" -config openssl.cnf -extensions v3_ca + +echo. +echo [4/8] 生成服务器私钥... +openssl genrsa -out server-rsa-sign.key 2048 + +echo. +echo [5/8] 生成服务器证书 CSR... +openssl req -new -key server-rsa-sign.key -out server-rsa-sign.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=localhost" -config openssl.cnf + +echo. +echo [6/8] 签发服务器证书... +openssl x509 -req -days 3650 -in server-rsa-sign.csr -CA root-cert.crt -CAkey root-ca.key -CAcreateserial -out server-rsa-sign.crt -extfile openssl.cnf -extensions v3_req + +echo. +echo [7/8] 生成客户端私钥... +openssl genrsa -out client-rsa-sign.key 2048 + +echo. +echo [8/8] 生成并签发客户端证书... +openssl req -new -key client-rsa-sign.key -out client-rsa-sign.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=Test-Client" -config openssl.cnf +openssl x509 -req -days 3650 -in client-rsa-sign.csr -CA root-cert.crt -CAkey root-ca.key -CAcreateserial -out client-rsa-sign.crt + +echo. +echo 清理临时文件... +del *.csr 2>nul +del *.srl 2>nul +del openssl.cnf 2>nul + +echo. +echo ======================================== +echo 证书生成完成! +echo ======================================== +echo. +echo 生成的文件: +echo - root-cert.crt (CA 证书) +echo - root-ca.key (CA 私钥) +echo - server-rsa-sign.crt (服务器证书) +echo - server-rsa-sign.key (服务器私钥) +echo - client-rsa-sign.crt (客户端证书) +echo - client-rsa-sign.key (客户端私钥) +echo. +echo 证书已保存到: %CERT_DIR% +echo. + +cd /d .. +echo. +echo 现在可以运行服务器和客户端程序了! +pause diff --git a/include/ylt/coro_rpc/impl/common_service.hpp b/include/ylt/coro_rpc/impl/common_service.hpp index b143a240a..609084ba6 100644 --- a/include/ylt/coro_rpc/impl/common_service.hpp +++ b/include/ylt/coro_rpc/impl/common_service.hpp @@ -31,10 +31,10 @@ namespace coro_rpc { * SSL config */ struct ssl_configure { - std::string base_path; //!< all config files base path - std::string cert_file; //!< relative path of certificate chain file - std::string key_file; //!< relative path of private key file - std::string dh_file; //!< relative path of tmp dh file (optional) + std::string base_path; //!< all config files base path + std::string cert_file; //!< relative path of certificate chain file + std::string key_file; //!< relative path of private key file + std::string dh_file; //!< relative path of tmp dh file (optional) std::string ca_cert_file; //!< relative path of CA certificate file for //!< client verification (optional) bool enable_client_verify = @@ -61,7 +61,7 @@ struct ssl_configure { */ enum class ntls_mode { tlcp_dual_cert, //!< GB/T 38636-2020 TLCP with dual certificates (signing + - //!< encryption) + //!< encryption) tls13_single_cert //!< RFC 8998 TLS 1.3 + GM with single certificate }; @@ -88,8 +88,8 @@ struct ssl_ntls_configure { // TLS 1.3 + GM single certificate configuration (RFC 8998) std::string gm_cert_file; //!< relative path of single SM2 certificate file //!< (for TLS 1.3 + GM) - std::string gm_key_file; //!< relative path of single SM2 private key file - //!< (for TLS 1.3 + GM) + std::string gm_key_file; //!< relative path of single SM2 private key file + //!< (for TLS 1.3 + GM) // Common NTLS configuration std::string @@ -140,6 +140,11 @@ inline bool init_ssl_context_helper(asio::ssl::context &context, context.set_options(asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 | asio::ssl::context::single_dh_use); + + // Set lower security level for test certificates (OpenSSL 3.0 + // compatibility) + SSL_CTX_set_security_level(context.native_handle(), 0); + context.set_password_callback( [](std::size_t size, asio::ssl::context_base::password_purpose purpose) { diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index 3d717ceb0..0ac6bb2fc 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -139,7 +139,7 @@ struct async_rpc_result_value_t : public detail::async_rpc_result_base { T result_; public: - async_rpc_result_value_t(T&& result, resp_body&& buffer, + async_rpc_result_value_t(T &&result, resp_body &&buffer, coro_io::data_view attachment) : async_rpc_result_base(std::move(buffer), attachment), result_(std::move(result)) {} @@ -273,7 +273,7 @@ class coro_rpc_client { * @param executor coro_io's executor, default executor is come */ coro_rpc_client( - coro_io::ExecutorWrapper<>* executor = coro_io::get_global_executor(), + coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor(), config conf = {}) : timer_(std::make_unique( executor->get_asio_executor())), @@ -308,6 +308,11 @@ class coro_rpc_client { ELOG_INFO << "init ssl: " << config.ssl_domain; auto &cert_file = config.ssl_cert_path; ELOG_INFO << "current path: " << std::filesystem::current_path().string(); + + // Set lower security level for test certificates (OpenSSL 3.0 + // compatibility) + SSL_CTX_set_security_level(ssl_ctx_.native_handle(), 0); + if (file_exists(cert_file)) { ELOG_INFO << "load " << cert_file.string(); ssl_ctx_.load_verify_file(cert_file.string()); @@ -352,8 +357,12 @@ class coro_rpc_client { } ssl_ctx_.set_verify_mode(asio::ssl::verify_peer); - ssl_ctx_.set_verify_callback( - asio::ssl::host_name_verification(config.ssl_domain)); + // Set hostname verification for DNS names (skip for IP addresses) + if (config.ssl_domain != "127.0.0.1" && + config.ssl_domain != "localhost") { + ssl_ctx_.set_verify_callback( + asio::ssl::host_name_verification(config.ssl_domain)); + } auto init_result = control_->socket_wrapper_.init_client( ssl_ctx_, config.enable_tcp_no_delay); if (!init_result) { @@ -1430,7 +1439,6 @@ class coro_rpc_client { ELOG_DEBUG << "client_id " << control->client_id << " close"; asio::dispatch(control->socket_wrapper_.get_executor()->get_asio_executor(), [control]() { - control->socket_wrapper_.close(); }); return; @@ -1470,8 +1478,8 @@ class coro_rpc_client { private: template async_simple::coro::Lazy send_request_for_impl( - Socket& soc, request_config_t& config, uint32_t& id, - coro_io::period_timer& timer, Args&&... args) { + Socket &soc, request_config_t &config, uint32_t &id, + coro_io::period_timer &timer, Args &&...args) { if (control_->has_closed_) AS_UNLIKELY { ELOG_ERROR << "client has been closed, please re-connect" @@ -1530,8 +1538,7 @@ class coro_rpc_client { << ", cost time = " << (std::chrono::steady_clock::now() - tp) / std::chrono::microseconds(1) - << "us" - << ", client_id: " << controller->client_id; + << "us" << ", client_id: " << controller->client_id; } break; } @@ -1918,8 +1925,7 @@ class coro_rpc_client { << ", client_id: " << config_.client_id << ", cost time = " << (std::chrono::steady_clock::now() - tp) / std::chrono::microseconds(1) - << "us" - << ", request ID: " << id; + << "us" << ", request ID: " << id; co_return rpc_error{errc::io_error, ret.first.message()}; } } @@ -1927,8 +1933,7 @@ class coro_rpc_client { << ", cost time = " << (std::chrono::steady_clock::now() - tp) / std::chrono::microseconds(1) - << "us" - << ", request ID: " << id; + << "us" << ", request ID: " << id; co_return rpc_error{}; } @@ -1944,7 +1949,7 @@ class coro_rpc_client { config config_; constexpr static std::size_t default_read_buf_size_ = 256; #ifdef YLT_ENABLE_SSL - asio::ssl::context ssl_ctx_{asio::ssl::context::sslv23}; + asio::ssl::context ssl_ctx_{asio::ssl::context::tls}; bool ssl_init_ret_ = true; #endif std::chrono::time_point create_tp_; diff --git a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp index 2733fe8f8..3e33b66d7 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp @@ -107,7 +107,7 @@ class coro_rpc_server_base { } coro_rpc_server_base( - const server_config& config, + const server_config &config, std::vector> acceptors = {}) : pool_(config.thread_num), @@ -627,7 +627,7 @@ class coro_rpc_server_base { connection_transfer_; #ifdef YLT_ENABLE_SSL - asio::ssl::context context_{asio::ssl::context::sslv23}; + asio::ssl::context context_{asio::ssl::context::tls}; bool use_ssl_ = false; #endif #ifdef YLT_ENABLE_IBV diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index 88f5ed728..96d906662 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -273,6 +273,7 @@ class coro_http_client : public std::enable_shared_from_this { } has_init_ssl_ = true; + is_ssl_schema_ = true; } catch (std::exception &e) { CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); return false; @@ -393,6 +394,7 @@ class coro_http_client : public std::enable_shared_from_this { } has_init_ssl_ = true; + is_ssl_schema_ = true; CINATRA_LOG_INFO << "SSL initialized with client certificate for mutual " "authentication"; } catch (std::exception &e) { diff --git a/src/certs/rsa/client-rsa-enc.crt b/src/certs/rsa/client-rsa-enc.crt new file mode 100644 index 000000000..46ceb4cb8 --- /dev/null +++ b/src/certs/rsa/client-rsa-enc.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIBEzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTIyMDIxNTAzMzMxM1oYDzIxMjIwMTIyMDMzMzEzWjBaMQswCQYDVQQG +EwJBQjELMAkGA1UECAwCQ0QxCzAJBgNVBAcMAkVGMQswCQYDVQQKDAJHSDELMAkG +A1UECwwCSUoxFzAVBgNVBAMMDkNMSUVOVCBSU0EgRU5DMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAveTwgAGlSgdKyU5ObLFQilckoPnyxdR+XC4nTy+o ++9fhdR0kZZAoK9AFiwL7j37KxacqPO0cBr6QkBxmyLfJ2whNpZVaOvn44lXSEcz3 +kBVz2aFmtJNKKukl8+QYXmngd80dIia8g4ydQTfjKqNQngc5Bp4eDdrnFTqNyeW+ +1mhV4Vx8nkZrzpzDRxFnDueZp98wpmhXTT25igktBErXANo5UMWC1POhFQzt6qbu +st3GYprkMGchPl+GrL36hCaS4WlzOChF7jX2MNJGCi/g5g1egR+JecWEZBvPgi0f +V+m3gr+fJv5eCgqlXhmnTAecc/haOYlijRUghtMsry2vGwIDAQABoxowGDAJBgNV +HRMEAjAAMAsGA1UdDwQEAwIDODANBgkqhkiG9w0BAQsFAAOCAQEAOWReYcJU+Mv1 +rbVPhbQ61032sD3ukidzrpko3fymcCX/HGkxcBuvmtV0lpxoNAuEVbue0Z4SWcHU +09A5mthsiq6hVk8kCiOmV8YVaMWOZQ0MvYS51t0tf8DDhR//6i/6pQvUVdf35OK1 +ZRSik+8r+Y2BE8vpq9UfM66pAxRjTqMycF3lz4WZyqcIp9hdPeibRGH6lsyVrJ1l +dVx+HZw6aIsL0Wb2rktge5IDJEAVV9YTPgT9qUnSQKXKCoV80+/y1IzAXJkRzCAv +GA/J47pBt8BDzrzVAKvxEjNLk6PKn5Fw0fFhJYykGBhYLjPBcx6UErdNH/tmhgMz +o2EDMZeDhA== +-----END CERTIFICATE----- diff --git a/src/certs/rsa/client-rsa-enc.key b/src/certs/rsa/client-rsa-enc.key new file mode 100644 index 000000000..df5c28e7a --- /dev/null +++ b/src/certs/rsa/client-rsa-enc.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC95PCAAaVKB0rJ +Tk5ssVCKVySg+fLF1H5cLidPL6j71+F1HSRlkCgr0AWLAvuPfsrFpyo87RwGvpCQ +HGbIt8nbCE2llVo6+fjiVdIRzPeQFXPZoWa0k0oq6SXz5BheaeB3zR0iJryDjJ1B +N+Mqo1CeBzkGnh4N2ucVOo3J5b7WaFXhXHyeRmvOnMNHEWcO55mn3zCmaFdNPbmK +CS0EStcA2jlQxYLU86EVDO3qpu6y3cZimuQwZyE+X4asvfqEJpLhaXM4KEXuNfYw +0kYKL+DmDV6BH4l5xYRkG8+CLR9X6beCv58m/l4KCqVeGadMB5xz+Fo5iWKNFSCG +0yyvLa8bAgMBAAECggEAa+r5FvZYdkrQoLkE7taSXByML4P9CZrquP5tzp3aXk3g +zoriTWnwun19OPRX/MPk/xEyeu+Rtu+D/rJ1Y2q3p+f/ILRRVCUN4as+OGmQ4+yd +KziDwunWA67+p62gf7+SFuOVw/vZtLUsBY16Z5fqz/Rw3ybrE5qtKy5cuDn+C6DD +AwZXcQj8i64zrtuZwEycrhwymHYIrMSEN3VUQZrb/BC+CssjAbJPag/oLrPRogQP +LjH5penusOD/eLZFl/Mc35qckwt4+GZae7onwT8B9cTw9HzxOpxUzpYJuOt/aSII +P4rSrEBD+cxPaGS1TIhd6gW51BOQE1wC1qDnZWOqsQKBgQDoSdx9WrgXkHttswEZ +BH6iX4m1tGWHoOqeCG2Vt9lC5AFsx6pInUdoPteC/dJwXnVkxJiHLyLAxLsYnx7c +Hp6J9dd1DE52LYPDRMos9/Ah9kAUf5mvx5sPvOQZrRR/zgYywOeVi3354k95fQWw ++JvygGHA91ySgtwkCaVMk8wY+QKBgQDRRzye7HQHBbOHdVEmt/YFqeOVRBbAApBe +xscp2pP51S4DxD5w9VsfN43zzEVN6+SRk/5dMrsjZj2mQlCaCnCuI2EGhlwI9D6R +g2S09tiru+UGXJ+pdiL0NBwZAlJZxOvfwBHwB+4PzehSRq4Lo3285FaymGDs/4Um +Lbz3P2lBswKBgEwIwU+W/mNYO86wpU9XFUx5EU6U+hbnKWUJRTWYtFkc4QqGYMrc +EJ+i0lBiXRvDD5Iod/ToDOwkeUmZ7DQJIcPu4NPDb3tncpXcUoFdtxCm1lrfWaMc +JzmDjvaInQkomwbJ9gQRYO8GkRUnQ1GmTwWV2GmdjoO2qfBx59WlG02hAoGBAJa/ +ocM4wDtut+uTbmldijJ6Nh2HdnvJXQVoWn6jnIQSM5sy+WeU1ZafBfnf4uxGR9c1 +q7ymlnbhZvckPbs4WgKWKYXxssogZhGbfCd+yISsTEVQ4xHGr1cJUlQRgBB58Q6z +5Dfq/mkUAY2CrExl8GTodisf6HHPfbIKsFvJqaBRAoGBANt3wITPHb7zklX20B4e +9IV8MR3tdb/pwm1eFIIYTNWYKBVnix0QaXt21rqcRGgHA9ZJFp4RrnlhAr37z13P +WZLXkfkdNv2VR1QD1zp1B2ehFRjZyPPuG/UbHcJAw5lqt9qlOZELgMdek8ESnJN7 +7EMRcWWHA32yAekgfrhBF4t9 +-----END PRIVATE KEY----- diff --git a/src/certs/rsa/client-rsa-sign.crt b/src/certs/rsa/client-rsa-sign.crt new file mode 100644 index 000000000..187a55f1f --- /dev/null +++ b/src/certs/rsa/client-rsa-sign.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBEjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTIyMDIxNTAzMzAzM1oYDzIxMjIwMTIyMDMzMDMzWjBbMQswCQYDVQQG +EwJBQjELMAkGA1UECAwCQ0QxCzAJBgNVBAcMAkVGMQswCQYDVQQKDAJHSDELMAkG +A1UECwwCSUoxGDAWBgNVBAMMD0NMSUVOVCBSU0EgU0lHTjCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAL7VnKBVPx/13o9WAWgLmxP32ZF4iGkPiycwJuJx +WNemJjW0lSMbgmS8xgjfsXydUOhshbK+Ttc4Kj+jo6winOhcvJhV66Ri74V+rq5c +thEK4ZERvTrc8lColIHYWfste5Ntu7J1c5hdvtkiGF5sQlrKcnQf3yv8TAiCMMTX +M1NycGCBeGCsm+Skia+hv06lWPylhv1k/6eviBxcRSAm0d0xN5te1tAof4Blc0wg +eEulTOjXg03KLSLWx7v2J+dPmqUqZLAK5wbOdItBUQCHYUaUYKsMuOjdtzcXElCE +3OrIMeukRvOZn9Exhj4bv3lWzgIbXdtW9YaKp/Zj6rm5P/cCAwEAAaMaMBgwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBsAwDQYJKoZIhvcNAQELBQADggEBAHEp1xgBGL3p +ab0TY1OZhWSzy71CiYY8y6aMtqYnC758KI2sypquHNHc5KGHk7uZDB5kOTR+ZAaB +F1G0IVGFB0Bg2njsk3O447M8/yFUQRleVtqkRKfNQ+/v4Ncrraxxui2+PNbfmiFu +4R+xpUz7XIlDUXjVpN0u5IZMPKkIbVb6MUm8TRSDYTnL6+Dah33u3XcywZhqdIv4 +ymLi0zvRvUEOqyTYx9dORsj9vQY4zPoUpBRtZLrqwE+6XFq56zPWz5JwucrqOFaE +XBw4yhAalcoZjcC+b/52j62myHcbydoq68Sn8nz6HRDQZLENIUziaTkJRE7McW9T +OcBhXe6f2u4= +-----END CERTIFICATE----- diff --git a/src/certs/rsa/client-rsa-sign.key b/src/certs/rsa/client-rsa-sign.key new file mode 100644 index 000000000..b82a28d34 --- /dev/null +++ b/src/certs/rsa/client-rsa-sign.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC+1ZygVT8f9d6P +VgFoC5sT99mReIhpD4snMCbicVjXpiY1tJUjG4JkvMYI37F8nVDobIWyvk7XOCo/ +o6OsIpzoXLyYVeukYu+Ffq6uXLYRCuGREb063PJQqJSB2Fn7LXuTbbuydXOYXb7Z +IhhebEJaynJ0H98r/EwIgjDE1zNTcnBggXhgrJvkpImvob9OpVj8pYb9ZP+nr4gc +XEUgJtHdMTebXtbQKH+AZXNMIHhLpUzo14NNyi0i1se79ifnT5qlKmSwCucGznSL +QVEAh2FGlGCrDLjo3bc3FxJQhNzqyDHrpEbzmZ/RMYY+G795Vs4CG13bVvWGiqf2 +Y+q5uT/3AgMBAAECggEAT+JHoG5O8GgR9FqCWDWTgzZbYyPbpm8VRyBHBr9/+zXY +BN9FFt8rJFRJaemJ+Ko59jGHfh1gD4O5W856c1JB8Jrxb6t36uZncbBoGyFALb4q +UnnkhsF6xfzrvbb9Y8HUWxCyY6/65atIeshk/xG+bRcV9jppR5ZjlaQ6GgwjtigW +QprgrEV++JPKuWdtUk1LGxsxY2idFUwCkszrewSKuv+I5Kaur/kgSik1HLlR+SfW +TmyQhRQRar0lc5Qd0JUUw/T+x/AH4sUGP+8b6jnMu+V6HrXNG01oIA0WYf11qbX2 +9i1dM4lSt7X6ZJ956n3gitH0e0hw+5YGSTmBn34uMQKBgQDpgYp8KFiHeygRyFCF +1IO/b0RvgK3/lXKO/kjMwMfAjdjzYaubYMdSJB3oagZqWME2pJ5eDkp0Lf6PEW9D +JtmtFG5asHM7VIoHiVc3Qm6lUZ+x6VexI7F3ubLZ5nsvmpU2sH9e0CkKae/LBBTp +9fDbQywrdwfrnN5VpK1N3JYjowKBgQDRN8Cp/7jZI0Tb9pnRFDT/BOTnWYj90FkB +kHKOMK0n5XxmJycw/xFMFhyDMdg5QthMCDaxHJf6z36n7ZF/8lHbyd03BgFEdsen +gcfMput8d9/kVhjhA/zUqwLaAcAl69CCXkiT2Rmq8w36W58LUaUJwDLk2rKyTvwp +ty+UBuNXnQKBgQCIVyT/HaejY5uf1nB90/ohOtygpmfyRdaf+4WSWDXKDojQgwTq +MnXvVjtISy/3xF78gt4dWQbME1cBPlLSyMpsrvNIbKK7z77Qa7bLAzULvj8aaH7V +ZVvn5jux00u2HfJl/mRAfhaYiAaFaZq0iO/bdrtBnby4K93976tpPRVFNQKBgQCF +1IYXuI7Dxz3K/unRxn1IDBdoretlgJsO1xmQPy7kcHWs2qrFOsQTyakfewj3R/0Y +VcC2drX5KDW6scr670TRtUUeZ2b0Jo78ZZYrITNNgVjktPZhKa3XVDUUwz3og/nz +CuCeQqeapLNyTJ1mHinWHO3xWrwoN0IyZQ24QNe4OQKBgQCV6ZANWnHmlcHNULF1 ++G+4JydjEGzhzUZ4UKMBLuYMI1TIa+a/ZCP+OUnVzDduFz4hb7D3eBI74E8XhhFj +FyewqxoAzF1KjVJiQh7G9s0GNnL5yeZdY+rFSvV6HlvScD1NSQKuZGGTgqjkf97m +WSVIEdTHBEiLcxkL8H/4RZfo8w== +-----END PRIVATE KEY----- diff --git a/src/certs/rsa/root-cert.crt b/src/certs/rsa/root-cert.crt new file mode 100644 index 000000000..4cd71abf6 --- /dev/null +++ b/src/certs/rsa/root-cert.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDATCCAemgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTIwMTIxMjIwMDk0OVoYDzIxMjAxMjEzMjAwOTQ5WjASMRAwDgYDVQQD +DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eYA9Qa8 +oEY4eQ8/HnEZE20C3yubdmv8rLAh7daRCEI7pWM17FJboKJKxdYAlAOXWj25ZyjS +feMhXKTtxjyNjoTRnVTDPdl0opZ2Z3H5xhpQd7P9eO5b4OOMiSPCmiLsPtQ3ngfN +wCtVERc6NEIcaQ06GLDtFZRexv2eh8Yc55QaksBfBcFzQ+UD3gmRySTO2I6Lfi7g +MUjRhipqVSZ66As2Tpex4KTJ2lxpSwOACFaDox+yKrjBTP7FsU3UwAGq7b7OJb3u +aa32B81uK6GJVPVo65gJ7clgZsszYkoDsGjWDqtfwTVVfv1G7rrr3Laio+2Ff3ff +tWgiQ35mJCOvxQIDAQABo2AwXjAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB +BjAdBgNVHQ4EFgQUjvUlrx6ba4Q9fICayVOcTXL3o1IwHwYDVR0jBBgwFoAUjvUl +rx6ba4Q9fICayVOcTXL3o1IwDQYJKoZIhvcNAQELBQADggEBAL2sqYB5P22c068E +UNoMAfDgGxnuZ48ddWSWK/OWiS5U5VI7R/c8vjOCHU1OI/eQfhOenXxnHNF2QBuu +bjdg5ImPsvgQNFs6ZUgenQh+E4JDkTpn7bKCgtK7qlAPUXZRZI6uAaH5zKu3yFPU +2kow3LFCwYutrSfVg6JYeX+cuYsLHFzNzOhqh88Mu9yJ7pPJ8faeHFglHa51eoaw +vurAVknk7tzUxLZN0PxD9nrduVwtiluFbCPz0EtP5Dt1KylGdPrKvCJNkFkRJX+S +0t9VNIhyqLmslP5uSFtuTt8toXkizaYlxIVHckkvpuKZB8m7l8C/lom9sqagjZ1J +If+teEc= +-----END CERTIFICATE----- diff --git a/src/certs/rsa/root-key.pem b/src/certs/rsa/root-key.pem new file mode 100644 index 000000000..b73511c57 --- /dev/null +++ b/src/certs/rsa/root-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDh5gD1BrygRjh5 +Dz8ecRkTbQLfK5t2a/yssCHt1pEIQjulYzXsUlugokrF1gCUA5daPblnKNJ94yFc +pO3GPI2OhNGdVMM92XSilnZncfnGGlB3s/147lvg44yJI8KaIuw+1DeeB83AK1UR +Fzo0QhxpDToYsO0VlF7G/Z6HxhznlBqSwF8FwXND5QPeCZHJJM7Yjot+LuAxSNGG +KmpVJnroCzZOl7HgpMnaXGlLA4AIVoOjH7IquMFM/sWxTdTAAartvs4lve5prfYH +zW4roYlU9WjrmAntyWBmyzNiSgOwaNYOq1/BNVV+/UbuuuvctqKj7YV/d9+1aCJD +fmYkI6/FAgMBAAECggEBAJWfUmL441cFK8KDZrszDfjf61cF+yKkw+ddghuWUtm1 +hOw2mjC9HBN5ay0+YYOoHUeYOoUpdMIM/rvuQc9brCcJsg0TZ34/2hEWt7Yst9OO ++l1OykSfHBwIm7z6Aps1JmU3Ct+RK6ZtDRsU12hloYuzRxezmFUCwQPsWGR1xA9d +usQHGbhM6F0WXFiTUulwf5x/06egO4IcDQL/qi5B79Byou+YnEQXcvWlF+6shPb6 +dvQbHuNRWd7QJRU/nJ/yhUDQ8Kx8e1WB2QJemBbf3jzkrGxHxWojcr6avJOQuNk4 +DCo+ksUcpiJqKLF26dGyLTypqL3s1L6FXbQ2kvlGqMECgYEA+Bpv+uJAZk7N83+N +9DSsDmzQ1I1pp9tnwHvHko3gaWBr2O0bC7kl2CyLlUZ8Fi1SuiZPkWBXbffZbLxb +H7t8ppvsvnVC4+BWkdmQR9du6mtl16GojZxPE1GL0Gqqw8CyJGSATBm9UJs/roq1 +hQxnvWrbGZp2+PGJTndlb+UpIXMCgYEA6Raj0bHa4Lzb2CLxf5eo6p4iBNacD3as +CokJmDETIAwXFpjnHLDEuq8W26OACCv6vZtHefhtO0ULqJC1pRLF5/3lxhp219pp +5Q1bSIysOKUJtZyqNwvhvROoiFgTDRyfu48eKsAx18pzRJ/ZFHsHqNbNUVWjF2Nf +1yNC6PErO+cCgYEAufImhyPXHFZnqN2qqKJpaY5InWU3EKUyB2M92isXTCMF8VkT +ch/bqz8HhmODUKO7YuSfsddA75xYilRI/2lnLP+j2cLM1Tkn6LvklNNzv6GqfIge +abQC1/nADf5D6d6cJQZl2aNY7fbNLaWsEgGQJrjVaUUJcd6lB/gYQFrCDt0CgYAb +61RQB8e556VFrFnU+KuI/swAXFxxFbvhvyxhCG/MBOa3FDWKoDnQSqyoPltVnz73 +eyQCnhknYkvVMxAu56LA+Q6OvhjCHi1U/FaUyBTQ7CPPlyZkVY4CgdpMCpCtn2Ia +qzfn0wGlYWCRNbxg0aPHO4N4XJOed47BTD31fP26vQKBgDWXNz5he9pLYYDiF4F3 +jdjbG/QCmO41VBdfd81pFajJKm4TaskuMLTaWa51KHcoW+ddQQLy72qtXH53X4SL +lO/RacV9hRoSX5Nq7ivSO8sM1fnU55kBre+V/ZgKkG4Xe669P1Hd4TpmmpoYDhgO +Jq+6oI4L87uoIoyqvltRK3hM +-----END PRIVATE KEY----- diff --git a/src/certs/rsa/server-rsa-enc.crt b/src/certs/rsa/server-rsa-enc.crt new file mode 100644 index 000000000..1346a4267 --- /dev/null +++ b/src/certs/rsa/server-rsa-enc.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDAzCCAeugAwIBAgIBDzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTIyMDIxNTAzMTIyN1oYDzIxMjIwMTIyMDMxMjI3WjBaMQswCQYDVQQG +EwJBQjELMAkGA1UECAwCQ0QxCzAJBgNVBAcMAkVGMQswCQYDVQQKDAJHSDELMAkG +A1UECwwCSUoxFzAVBgNVBAMMDlNFUlZFUiBSU0EgRU5DMIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEA7TIykMa5+8ksjJM7tl2kKSwr2Q67PB0FFqb8JwzF +0Q89iQIxSxRzDRPPdAIKvVP/4baiI6vJqUEA9U0aMBZYaDRGhY49z/97cpObmY9t +zkTcz6mvW9PoOmYx1sfqrSuCW6/Mc1aJc7XH7/xGQzt3lDhNddfyNyxs2tZd8Fdp +GuF3SPKlfQemqKcIIR+kTCtqgvikXNzEj/P6cKGBSRqEzIfOTF0XESgvHjzRSOvm +MoJTrVODRh/6sRVFg0ChJbVEmGmYXflYr3yF+L+yW+xgTVY5VffHrDafKxUeIMS6 +Wk7Klk0HSgG3KH2bq9cF6kA8ecQTWM3g3uLECpQpI3smMwIDAQABoxowGDAJBgNV +HRMEAjAAMAsGA1UdDwQEAwIDODANBgkqhkiG9w0BAQsFAAOCAQEAFVdyVjjQ/hqe +N6TlIGSoPcl6VW5ssG9FXkdqAsIZKIuIqajXC/vu/7iQ2pZogyGBC79CHEZ+Bk35 +S9QQhtDQiTjsOL2xHO381qadQjL4MaaR7bEAtKjrENbghsQB8lFSm8Wt8GcUXPby +7t9HQQ3bshHn6yQbPXvf32b+YHNyhdTTnepv8QHl8Pep+R6Uk638SwArOytM2Fvm +llAaTyZjFAMnfRMBr4c0jjPa2JdLg1ZbASS0CjkVmhU5M1UdJqs+AHxSF0x94OmB +aT+s2i5hN4SJkTd//r72Kes4cQd3GXXjsNv53yqxo2z3Ph5szbIRm98+HSI7VVbh +fKZOps4tZA== +-----END CERTIFICATE----- diff --git a/src/certs/rsa/server-rsa-enc.key b/src/certs/rsa/server-rsa-enc.key new file mode 100644 index 000000000..c3bb1d8e4 --- /dev/null +++ b/src/certs/rsa/server-rsa-enc.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDtMjKQxrn7ySyM +kzu2XaQpLCvZDrs8HQUWpvwnDMXRDz2JAjFLFHMNE890Agq9U//htqIjq8mpQQD1 +TRowFlhoNEaFjj3P/3tyk5uZj23ORNzPqa9b0+g6ZjHWx+qtK4Jbr8xzVolztcfv +/EZDO3eUOE111/I3LGza1l3wV2ka4XdI8qV9B6aopwghH6RMK2qC+KRc3MSP8/pw +oYFJGoTMh85MXRcRKC8ePNFI6+YyglOtU4NGH/qxFUWDQKEltUSYaZhd+VivfIX4 +v7Jb7GBNVjlV98esNp8rFR4gxLpaTsqWTQdKAbcofZur1wXqQDx5xBNYzeDe4sQK +lCkjeyYzAgMBAAECggEAPRB3RB5lMxUPiwE8li51XMQqZa8NNOSjsrk6oTMAmlm6 +FdOirRmOpQc8mTkNWsCNV6aVB2nqeNBrVruKymZ2TB1pKmj8MNgtB8pIfLgYJMpF +rpCYvKNJXf+BRTIikIJrBP+1OtxEdq2N05K25AgjON1l07jMerfNLmmRtZZLUMfL +7CWvWnd3jYXQlR6nlN0ws+cmgtaZTSTNuPGu14w7cv9/L4hDl6wAn3FKQGa4jx3+ +IBzX3TuDXfHF8JzqWkbeGFuwyfYwIz0tCsZ1VdfakyvkUNwymIeyJNk2RDYcGXbi +dj1DZWYlX+jyrzRXOAxfh3QOknWYihqJitjlOcGRwQKBgQD8KHX/tKoTA2WUE9lC +aJ30knKoYkEvtlyW51uTjloQdF6JoTz6hcbh3QFJQTt4/6/8SQlvS/t6QxoLIacY +ATKMfTTwCBPgNYyc0HpHq1czRNeh8hLVNQHzd3jBiHEELfS8vkywcioEAc86eBvy +FnjN0atukaHnNFJmnjyWfSMFJQKBgQDwz2CtED46mE62hqvJ3K7Byc3OrB2kkrtG +cCfUGUOBGfe10EcHTZn73/l48+ODzxDGXbCG5ZPNXYQ2Vt/kzNo7Va+aSmFfU/wL +goc0m79KPa8nKKaf2jiDsneoA5CFQwFiP+khRawUpwKUycbgLBesgTLkhwR60vA0 +Eh3K7pYadwKBgDX4RwE63ugA4x3lfavMDnrJWYcWuJMIuhqGXEBhyH8bGRzmCNIq +Dd5L2jmen6cPOtCJpV5P6fxKHmnhgw0NPI97QPhbEj372CO8L1/lmV1cz7ZWxJcj +qE4+9PafSGk3mNlI9csSNFH1PTBrgre2/ZY32TRqMTf2J+jU12zyTauNAoGBAN7U +xvzeHvdRoOS90ZFFqGCiQNOR6Qw0yx7vAav+hLyeLt4LEO7tHCQ6qTsHU+zQWxFz ++LmbDx3l/0XZNe0esoJdU9VOJrRznrN34W0JAgXM9pshvq4E3G7hYmASjFUEH++u +fCfRtN6XfIf5/xHJO07hbxqQ5dBSCPvmF7OE+1kZAoGBALl6HFeGjB/4slIKj6r/ +n4GLkePiJXjTvXm52vDs7HLOwFZ/Sw6tN0iWTk1IDBmWykc8MYO3Y8GXUE0hbQGL +5qWPgUffe8UH0J6j2X2awfFXOmhQ5nwbmN0W5XkWw8cJGNjrO9RxYon6hFctpHxO +7FaSDafUmHdcu1EYPI4StWr/ +-----END PRIVATE KEY----- diff --git a/src/certs/rsa/server-rsa-sign.crt b/src/certs/rsa/server-rsa-sign.crt new file mode 100644 index 000000000..37eacef2e --- /dev/null +++ b/src/certs/rsa/server-rsa-sign.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBDCCAeygAwIBAgIBDjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290 +IENBMCAXDTIyMDIxNTAzMDk1NVoYDzIxMjIwMTIyMDMwOTU1WjBbMQswCQYDVQQG +EwJBQjELMAkGA1UECAwCQ0QxCzAJBgNVBAcMAkVGMQswCQYDVQQKDAJHSDELMAkG +A1UECwwCSUoxGDAWBgNVBAMMD1NFUlZFUiBSU0EgU0lHTjCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAL9tQ1nkKJ+Xj68qYcakxRORCfM5PO9OMlUDTYLo +/qs5XizIKR5/XOpVatSHvtEIaLSOnDlZitUu6mmi12qwcXHSCXAy0xqVtikQBlmz +TV8fRo3sWPm4QGGe3j1unm1z7EWry+S9Ry8ylBpJwP+qSw9qApYYgwGsvmLhQJNK +jt8l29VhcukOhXc7Lp4qJ6C2Ib4qpc/CLXLMO4Q6HXVR4xL+rVPhOUBsOaqhzaoR +VLvsyldqqFuQHxc3ntKUcBtu7ug0wCBNUzP/IoNOhM+Oui06mk6tj7FjYsacy+4z +QmoZ4B5tdvUs8ObXk5csb2OQeNSZUCd+3vbRfEYA+dUOcjsCAwEAAaMaMBgwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBsAwDQYJKoZIhvcNAQELBQADggEBANOfhZZcPmVW +5CnVgYG9+J69cv44zd5Ylf0QZbl4jVCyp3JdMU9vWZQTJxu4MEDwhro+KsxxdfE1 +o8FLteabWV3N0tMtkksNBzRGjRIK9gsCqkomqvMOFMLppbOkBQA6xfvaxynFoquG +FUK1fxqBPvjTCeFrBcDhrIPym6r+7KpR6A5urVexsbEJttuFXuKxp5dvEqD55H5X +vYyx10nqMJPYWd/m1fpDw2yg365UYrroomwKHt+thHnqM/acV+uyal65xDw8aYDV +dBmIwMu6eQ0q1B2DrBdjfRm4reSTJRzh14pZJEHmIvrvG/b451f733c91pZOCP9E +FLEbPUVYzuI= +-----END CERTIFICATE----- diff --git a/src/certs/rsa/server-rsa-sign.key b/src/certs/rsa/server-rsa-sign.key new file mode 100644 index 000000000..f808284a3 --- /dev/null +++ b/src/certs/rsa/server-rsa-sign.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC/bUNZ5Cifl4+v +KmHGpMUTkQnzOTzvTjJVA02C6P6rOV4syCkef1zqVWrUh77RCGi0jpw5WYrVLupp +otdqsHFx0glwMtMalbYpEAZZs01fH0aN7Fj5uEBhnt49bp5tc+xFq8vkvUcvMpQa +ScD/qksPagKWGIMBrL5i4UCTSo7fJdvVYXLpDoV3Oy6eKiegtiG+KqXPwi1yzDuE +Oh11UeMS/q1T4TlAbDmqoc2qEVS77MpXaqhbkB8XN57SlHAbbu7oNMAgTVMz/yKD +ToTPjrotOppOrY+xY2LGnMvuM0JqGeAebXb1LPDm15OXLG9jkHjUmVAnft720XxG +APnVDnI7AgMBAAECggEBAISxg89rBfHiyLn+ofpluyreDkyob6Hezar6eB93zRj5 +0lsugLpgRRM3FK+wTUhvEM2gUZC7ji5KsDl7pOGS34CrvNkmkglKoBQExLInRUnb +zzeQDvlDjHVe/+ULqgxrH+wHs3Wi8xY3dExotb1D2XYc4SAUa7Ocf/jg9bdkJndt +fQe6k1AK24sAvKO5CFPKakNeQFLmZ7V4xjwh0ne0ofgfGGiU0ihDbjdOLcvD4Aw6 ++vYAYI1aDLTWF0WDJdfSiSfYAtFauOm+CDgK8tKuJ65iICOhayjBGYfxyfOfhi3R +daYPES5P6zZpWKsxnRDHCdC3FoLhWEDSWS+fmTOXL5kCgYEA96eA6wXYxJ7Gub99 +T6b2HqywaFF0Sbt3xBxH7lwNecPb7FKAeY2dm7THZL3pMc35CA2uBElERwK3uilW +s735rlgdfuzwgqI2o2PN9DNfwlc6OaXYDapro/D8JaBUqPUkdVwrMIEylzJKzNiw +HG/EmEAuNHJA48l/RIbZ17gpO6UCgYEAxeCwU83Iu2iLZ6n+bHaWXLa28DY7ZDct +ilRXSWl18kGfTKWKUifuRF5LChPKb5naUKkfFvw+EkZsNA0zdfzV1s43taNV/qJj +7Qw+8yMvbwzttJAZHq2hymc6KGUzuyRYEEn3cckLvNIDVv1zj1S4fp0gEa7l0Xbv +gxseuI87EF8CgYEA8Q9qvVoDld+3MDbkkxPBnjoZvHEM8Hg6K/obSjjwJQYwm+fy +hl8Cev2M2NLCmmgTb3NhX2qxuy06CUsTygGxd7Ltgw6/TWj4JjKP1pPrMhAV7Ocx +Vpv8CqgXx8g4n/1+b343zA72X5XkmWCSjBt/EXPnhcVwxEjuSJW6le/8ssECgYA5 +ryuX8ph+0ZY6DRtnQRbKRlWEJzsfFtrGxZmAhCbnDWxD6sos8wkJkzdq46QS7pXv +B65RhA0QRMlMT0DeN2ubKBijcc8i4PIia3x6Ypp0VB+DkLJR+Cn/GCKjHgV7m7e0 +X+urlwE99TcWB+LFONxjxKhRn1vNuU9PN/u3r8F3HwKBgQCxJUmKYEEqIM8janyh +scki1IBRn2Pb3XCr/9pdObc7rBNIhLh9OwPvVgI1nczTejMrL7QyEHXZwQ/V3Bvg +hNWATPOzCqAFultPPfRWmu6jDKH6xCuqIcH7wzEyKuKm7d8x55CFfmNE/go9007N +4rP/7MaDg/Nepnu8F/ildDHDeg== +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/README.md b/src/certs/sm2/README.md new file mode 100644 index 000000000..9687c232c --- /dev/null +++ b/src/certs/sm2/README.md @@ -0,0 +1,71 @@ +# SM2 Certificates for NTLS Examples + +This directory contains SM2 certificates for NTLS (国密SSL) examples using Tongsuo. + +## Certificate Files + +- `sm2_sign.crt` - SM2 signing certificate +- `sm2_sign.key` - SM2 signing private key +- `sm2_enc.crt` - SM2 encryption certificate +- `sm2_enc.key` - SM2 encryption private key +- `ca.crt` - CA certificate (optional) + +## Generating SM2 Certificates with Tongsuo + +To generate your own SM2 certificates using Tongsuo, follow these steps: + +### 1. Generate CA private key and certificate +```bash +# Generate CA private key +tongsuo genpkey -algorithm SM2 -out ca.key + +# Generate CA certificate +tongsuo req -new -x509 -key ca.key -out ca.crt -days 365 \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=Test CA" +``` + +### 2. Generate server signing certificate +```bash +# Generate server signing private key +tongsuo genpkey -algorithm SM2 -out sm2_sign.key + +# Generate server signing certificate request +tongsuo req -new -key sm2_sign.key -out sm2_sign.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=localhost" + +# Sign the certificate request +tongsuo x509 -req -in sm2_sign.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out sm2_sign.crt -days 365 +``` + +### 3. Generate server encryption certificate +```bash +# Generate server encryption private key +tongsuo genpkey -algorithm SM2 -out sm2_enc.key + +# Generate server encryption certificate request +tongsuo req -new -key sm2_enc.key -out sm2_enc.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Test/OU=Test/CN=localhost" + +# Sign the certificate request +tongsuo x509 -req -in sm2_enc.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out sm2_enc.crt -days 365 +``` + +### 4. Clean up temporary files +```bash +rm *.csr *.srl +``` + +## Usage + +These certificates are used by the NTLS examples: +- `ntls_server.cpp` - NTLS RPC server example +- `ntls_client.cpp` - NTLS RPC client example + +Make sure Tongsuo is properly installed and configured before running the examples. + +## Security Note + +The certificates in this directory are for testing purposes only. +Do not use them in production environments. \ No newline at end of file diff --git a/src/certs/sm2/chain-ca.crt b/src/certs/sm2/chain-ca.crt new file mode 100644 index 000000000..44063943f --- /dev/null +++ b/src/certs/sm2/chain-ca.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIB4DCCAYagAwIBAgIBADAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG +A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v +dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRjELMAkGA1UE +BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEDAO +BgNVBAMMB3Jvb3QgY2EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASN55Ju2pvU +Bi8UrWHc4ZaKnsqiFPWfcM/6H2Gu/VQ7I1oVnyPktvlTrtwhSy6K43JoCnjVPHrq +jOXxnkOtGVDVo2MwYTAdBgNVHQ4EFgQUxu7mMmVaB3vq7JRi8UEFHcxVFY4wHwYD +VR0jBBgwFoAUxu7mMmVaB3vq7JRi8UEFHcxVFY4wDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwCgYIKoEcz1UBg3UDSAAwRQIhAIz7tgrp7LmOQEJGPAU3 +8m9PNzMOTqGWZqux8CxIuEGjAiB4cFVYQ4sTCYb/4fNayKYO1FH+Q2Cc7xGq7WPd +knwWpw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB4zCCAYigAwIBAgIBATAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG +A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v +dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRTELMAkGA1UE +BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxDzAN +BgNVBAMMBnN1YiBjYTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABH0feWwae0S0 +w4QQA5cBGYwaQPaxZFcLzIqph+I6BQQCGXaIAabqpO0zjAyf1twYmoM3ZRLJgbZz +HE/2rRMPBiajZjBkMB0GA1UdDgQWBBSsYesigGJZCD6WyNF/znRcAq88mTAfBgNV +HSMEGDAWgBTG7uYyZVoHe+rslGLxQQUdzFUVjjASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAKBggqgRzPVQGDdQNJADBGAiEApoHDue1bzGukE97O +BqQbboU1d3jqNg4gAgpMe5fFIosCIQDwndSp7Tc3DZ0QCifXKNqgykjepsWTPZ3R +NrMzM0rflg== +-----END CERTIFICATE----- diff --git a/src/certs/sm2/client_enc.crt b/src/certs/sm2/client_enc.crt new file mode 100644 index 000000000..00af663f0 --- /dev/null +++ b/src/certs/sm2/client_enc.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7TCCAZKgAwIBAgIUWy6/ole1R8GwTrzoOtZebFla3aowCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y13MXG1Zsx+NiEZ7Bb +OFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncMo1owWDAJBgNVHRMEAjAAMAsG +A1UdDwQEAwIDODAdBgNVHQ4EFgQUfPE8T3aPRzOi/+LiWTRrkM+0dKgwHwYDVR0j +BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDSQAwRgIhAOIT +GEUHnILUpLCbSZCyG8TigYmbg7ImyZFtXF/uEhOfAiEA59PnEVYaegvpI5Ltn5T2 +PKKqiZ2QOWEfRHJIi/FFZeo= +-----END CERTIFICATE----- diff --git a/src/certs/sm2/client_enc.key b/src/certs/sm2/client_enc.key new file mode 100644 index 000000000..a58fd4d38 --- /dev/null +++ b/src/certs/sm2/client_enc.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQguz0M4/6qUhgHAxuG +WI2NPtNciIwmYAN4AUDoBEka1tehRANCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y1 +3MXG1Zsx+NiEZ7BbOFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncM +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/client_enc_expire.crt b/src/certs/sm2/client_enc_expire.crt new file mode 100644 index 000000000..5df3c163c --- /dev/null +++ b/src/certs/sm2/client_enc_expire.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6zCCAZCgAwIBAgIUMB957+vqASBc5y4ZMBsfYU/FzlAwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAeFw0wMDAxMDEwMDAwMDBaFw0wMTAxMDEw +MDAwMDBaMEkxCzAJBgNVBAYTAkFBMQswCQYDVQQIDAJCQjELMAkGA1UECgwCQ0Mx +CzAJBgNVBAsMAkREMRMwEQYDVQQDDApjbGllbnQgZW5jMFkwEwYHKoZIzj0CAQYI +KoEcz1UBgi0DQgAEKSDd6TSAQd6GfkmlyqGTbTJB+bectdzFxtWbMfjYhGewWzhQ +WxAffb8kK8unPa85TPCHnT8OjsCHOfHbAPp3DKNaMFgwCQYDVR0TBAIwADALBgNV +HQ8EBAMCAzgwHQYDVR0OBBYEFHzxPE92j0czov/i4lk0a5DPtHSoMB8GA1UdIwQY +MBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQDCDf6A +9OP6LX0TKlssMucsUuYPHBu+dybxydFIUol5kgIhAOgFxSFd6AF43SJbHij2yCsf +2QugcC3FMVM68PU9iflK +-----END CERTIFICATE----- diff --git a/src/certs/sm2/client_sign.crt b/src/certs/sm2/client_sign.crt new file mode 100644 index 000000000..ea9ed91d2 --- /dev/null +++ b/src/certs/sm2/client_sign.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7jCCAZOgAwIBAgIUbEstbqUWeJMWK3rlwLXE9YdtJ8QwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0C +AQYIKoEcz1UBgi0DQgAELWF+dNVYbd4j0kkPvUaOMPEdAS1QqPOSzRhJsQsWfpoG +YffjoqAO5+xHGO2Te0qyxQqg00HRXkCVdDs4UK9tPKNaMFgwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFFRpNXBAPyjFwT7w8EBNJLMVaFgdMB8GA1Ud +IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQDH +LBaKDJFhHYRNLhYXFBtZH6BIa6cJfewLyVaH0oDaMgIhAMgZQDmGvHCzJ9vdgL2P +7upTf3I28uj+3pq7ZiwKRBlO +-----END CERTIFICATE----- diff --git a/src/certs/sm2/client_sign.key b/src/certs/sm2/client_sign.key new file mode 100644 index 000000000..4c4cc0ee4 --- /dev/null +++ b/src/certs/sm2/client_sign.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg5zLNmBvXdesfATxu +gMMpvEHfuxhUCr6L8rS2EEBr6SGhRANCAAQtYX501Vht3iPSSQ+9Ro4w8R0BLVCo +85LNGEmxCxZ+mgZh9+OioA7n7EcY7ZN7SrLFCqDTQdFeQJV0OzhQr208 +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/client_sign_expire.crt b/src/certs/sm2/client_sign_expire.crt new file mode 100644 index 000000000..fd31a85b8 --- /dev/null +++ b/src/certs/sm2/client_sign_expire.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6zCCAZGgAwIBAgIUMcx8UyWHnfbVRe5biYw44Kx6bvwwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAeFw0wMDAxMDEwMDAwMDBaFw0wMTAxMDEw +MDAwMDBaMEoxCzAJBgNVBAYTAkFBMQswCQYDVQQIDAJCQjELMAkGA1UECgwCQ0Mx +CzAJBgNVBAsMAkREMRQwEgYDVQQDDAtjbGllbnQgc2lnbjBZMBMGByqGSM49AgEG +CCqBHM9VAYItA0IABC1hfnTVWG3eI9JJD71GjjDxHQEtUKjzks0YSbELFn6aBmH3 +46KgDufsRxjtk3tKssUKoNNB0V5AlXQ7OFCvbTyjWjBYMAkGA1UdEwQCMAAwCwYD +VR0PBAQDAgbAMB0GA1UdDgQWBBRUaTVwQD8oxcE+8PBATSSzFWhYHTAfBgNVHSME +GDAWgBSsYesigGJZCD6WyNF/znRcAq88mTAKBggqgRzPVQGDdQNIADBFAiBiEQiy +OfgcmIBIohgYJtk/5IrjWI8LbHVGl0jVV0OgKgIhAKs9Mjk/3Mlls/A/G0IPR6p2 +ljXD0/dra7WTooE1z42+ +-----END CERTIFICATE----- diff --git a/src/certs/sm2/server_enc.crt b/src/certs/sm2/server_enc.crt new file mode 100644 index 000000000..4f129bf21 --- /dev/null +++ b/src/certs/sm2/server_enc.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6zCCAZKgAwIBAgIUaNiS6WOsoEViDnmdb8Mdk3Qz5XwwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY7JQUmflKUKWMZ11v +mtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNko1owWDAJBgNVHRMEAjAAMAsG +A1UdDwQEAwIDODAdBgNVHQ4EFgQUZ6Wt1ZR24FqcXla4hg/xOyju7FQwHwYDVR0j +BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDRwAwRAIgR1k1 +ecSt7I2335jEquFmHBE5pe8Sk/IqOqQS0Jvs1uYCIG5XMB0XeUaVb9OctaxgOQLN +F8dRftiUHsyYXqfbaVjI +-----END CERTIFICATE----- diff --git a/src/certs/sm2/server_enc.key b/src/certs/sm2/server_enc.key new file mode 100644 index 000000000..250fbcc5f --- /dev/null +++ b/src/certs/sm2/server_enc.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgLrRk3CWTe+WZOFSf +TMYwbOocLs3MSRpOO0/AvSmvH5mhRANCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY +7JQUmflKUKWMZ11vmtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNk +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/server_enc2.crt b/src/certs/sm2/server_enc2.crt new file mode 100644 index 000000000..1bc202c0f --- /dev/null +++ b/src/certs/sm2/server_enc2.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICsjCCAligAwIBAgIBFDAKBggqgRzPVQGDdTBIMQswCQYDVQQGEwJBQjELMAkG +A1UECAwCQ0QxCzAJBgNVBAoMAkdIMQswCQYDVQQLDAJJSjESMBAGA1UEAwwJU1VC +Q0EgU00yMCAXDTIzMDIyMjAzMjQxMloYDzIxMjMwMTI5MDMyNDEyWjBNMQswCQYD +VQQGEwJBQjELMAkGA1UECAwCQ0QxCzAJBgNVBAoMAkdIMQswCQYDVQQLDAJJSjEX +MBUGA1UEAwwOU0VSVkVSIEVuYyBTTTIwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNC +AATOBfM0c7CFYYQTh8qjRNLV193rMqRqQ4hyFuJ9sSpmvlPS/Ou8NzkTvU22drvf +Ki9DWlFOAfCfK7fA15sdBY1So4IBKjCCASYwCQYDVR0TBAIwADARBglghkgBhvhC +AQEEBAMCBkAwMwYJYIZIAYb4QgENBCYWJFRvbmdzdW8gR2VuZXJhdGVkIFNlcnZl +ciBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUGxUxeJPDyURLizts5QM6aV8KMoAwbQYD +VR0jBGYwZIAUd0P/IyfOaMzfEf7xN4PtL2/JnlyhSaRHMEUxCzAJBgNVBAYTAkFC +MQswCQYDVQQIDAJDRDELMAkGA1UECgwCR0gxCzAJBgNVBAsMAklKMQ8wDQYDVQQD +DAZDQSBTTTKCAQUwCwYDVR0PBAQDAgM4MDYGA1UdEQQvMC2CCWxvY2FsaG9zdIIV +bG9jYWxob3N0LmxvY2FsZG9tYWluggkxMjcuMC4wLjEwCgYIKoEcz1UBg3UDSAAw +RQIhAKAQDyIpKbMJQXwgwd9rTRWfue1EsbzkOzN7WfqDyJXyAiB8gBdGpJA+nSOn +cj/nKr6phU3Zi3Hff/42EwwDV7TO3g== +-----END CERTIFICATE----- diff --git a/src/certs/sm2/server_enc2.key b/src/certs/sm2/server_enc2.key new file mode 100644 index 000000000..8fe209298 --- /dev/null +++ b/src/certs/sm2/server_enc2.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgKdgbft7a+phXO8/Q +Tcmf3jnUq1dQtpH2InFijPCqzcOhRANCAATOBfM0c7CFYYQTh8qjRNLV193rMqRq +Q4hyFuJ9sSpmvlPS/Ou8NzkTvU22drvfKi9DWlFOAfCfK7fA15sdBY1S +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/server_enc_expire.crt b/src/certs/sm2/server_enc_expire.crt new file mode 100644 index 000000000..546e83a96 --- /dev/null +++ b/src/certs/sm2/server_enc_expire.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6jCCAZCgAwIBAgIURlC8XDK83pSxF4VdK/sfDeNm42QwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAeFw0wMDAxMDEwMDAwMDBaFw0wMTAxMDEw +MDAwMDBaMEkxCzAJBgNVBAYTAkFBMQswCQYDVQQIDAJCQjELMAkGA1UECgwCQ0Mx +CzAJBgNVBAsMAkREMRMwEQYDVQQDDApzZXJ2ZXIgZW5jMFkwEwYHKoZIzj0CAQYI +KoEcz1UBgi0DQgAEfb6lRUNFgXK9O2iOUJwt9UWGrOALWOyUFJn5SlCljGddb5ra +/wiegTenY0Os0vNcowqKXM+Bt83OUmagFrnjZKNaMFgwCQYDVR0TBAIwADALBgNV +HQ8EBAMCAzgwHQYDVR0OBBYEFGelrdWUduBanF5WuIYP8Tso7uxUMB8GA1UdIwQY +MBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0gAMEUCIQCmSKvp +2ArjHFa/Z9TrwRwiyw734+QS7ju8OEpJKNeE4QIgTxEwWFkeDEKL6eL6ftuF/a/2 +S/5Xwlef6wuGIE855PI= +-----END CERTIFICATE----- diff --git a/src/certs/sm2/server_sign.crt b/src/certs/sm2/server_sign.crt new file mode 100644 index 000000000..084defed0 --- /dev/null +++ b/src/certs/sm2/server_sign.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7jCCAZOgAwIBAgIUcbKTlc6+CNoHglmEk+xm+WIqZcAwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0C +AQYIKoEcz1UBgi0DQgAEBb/67sQGyPP1gKbjnFKEdsDfK2EGXUp09HavD7ZokPiW +rMSyHYsDbRPxe9TTgjSQi+23f44+rocGVPxvqASNDKNaMFgwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFH3uBqkdowIvk//P7n5UtnpV9TR6MB8GA1Ud +IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQCz +W/6Z/d/IJUTrO0o8nCxNle6R0AkRCKUFhW9zbIRlNwIhAJZxg4gs2cV2QF37oHs6 +9TD+MkRbql4Yb47+jLf8f247 +-----END CERTIFICATE----- diff --git a/src/certs/sm2/server_sign.key b/src/certs/sm2/server_sign.key new file mode 100644 index 000000000..e065cafa3 --- /dev/null +++ b/src/certs/sm2/server_sign.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgeQTrKtO8mNXn/yvg +R+pdbCgH5sl+WCFfXcqGl64soU2hRANCAAQFv/ruxAbI8/WApuOcUoR2wN8rYQZd +SnT0dq8PtmiQ+JasxLIdiwNtE/F71NOCNJCL7bd/jj6uhwZU/G+oBI0M +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/server_sign2.crt b/src/certs/sm2/server_sign2.crt new file mode 100644 index 000000000..d1054e6a5 --- /dev/null +++ b/src/certs/sm2/server_sign2.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICxzCCAm6gAwIBAgIBEzAKBggqgRzPVQGDdTBIMQswCQYDVQQGEwJBQjELMAkG +A1UECAwCQ0QxCzAJBgNVBAoMAkdIMQswCQYDVQQLDAJJSjESMBAGA1UEAwwJU1VC +Q0EgU00yMCAXDTIzMDIyMjAzMjQxMVoYDzIxMjMwMTI5MDMyNDExWjBOMQswCQYD +VQQGEwJBQjELMAkGA1UECAwCQ0QxCzAJBgNVBAoMAkdIMQswCQYDVQQLDAJJSjEY +MBYGA1UEAwwPU0VSVkVSIFNpZ24gU00yMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0D +QgAE3ZKRIv+YD/UNbouIQmAanw3y2xlgQ/VjRkffMKbq9a3svI24Md4sERyi/DNm +x/lFBn3UA0IsAKSdHAWmX5Ij3aOCAT8wggE7MAkGA1UdEwQCMAAwEQYJYIZIAYb4 +QgEBBAQDAgZAMDMGCWCGSAGG+EIBDQQmFiRUb25nc3VvIEdlbmVyYXRlZCBTZXJ2 +ZXIgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFJOVTVFCLzwTmrEqSJmpF0Ya2hmnMG0G +A1UdIwRmMGSAFHdD/yMnzmjM3xH+8TeD7S9vyZ5coUmkRzBFMQswCQYDVQQGEwJB +QjELMAkGA1UECAwCQ0QxCzAJBgNVBAoMAkdIMQswCQYDVQQLDAJJSjEPMA0GA1UE +AwwGQ0EgU00yggEFMAsGA1UdDwQEAwIGwDATBgNVHSUEDDAKBggrBgEFBQcDATA2 +BgNVHREELzAtgglsb2NhbGhvc3SCFWxvY2FsaG9zdC5sb2NhbGRvbWFpboIJMTI3 +LjAuMC4xMAoGCCqBHM9VAYN1A0cAMEQCIGkAKq4wx1/kXJ32RLlZ87dW4zW5jbuE +Xfx/7Gzo3/F0AiBaRKWEVUPP2UpQ6VYFEl9JFWH0cecgNJA+TNW+2NhqOw== +-----END CERTIFICATE----- diff --git a/src/certs/sm2/server_sign2.key b/src/certs/sm2/server_sign2.key new file mode 100644 index 000000000..a333c2c2f --- /dev/null +++ b/src/certs/sm2/server_sign2.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgD/DJqNohvOhcq81i +JWB+7ZEDXjeh0J9jM8U2KZViPUGhRANCAATdkpEi/5gP9Q1ui4hCYBqfDfLbGWBD +9WNGR98wpur1rey8jbgx3iwRHKL8M2bH+UUGfdQDQiwApJ0cBaZfkiPd +-----END PRIVATE KEY----- diff --git a/src/certs/sm2/server_sign_expire.crt b/src/certs/sm2/server_sign_expire.crt new file mode 100644 index 000000000..c3048632c --- /dev/null +++ b/src/certs/sm2/server_sign_expire.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6zCCAZGgAwIBAgIUGzfhuDTb9o1gFiWy8bqo3kcPkdQwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAeFw0wMDAxMDEwMDAwMDBaFw0wMTAxMDEw +MDAwMDBaMEoxCzAJBgNVBAYTAkFBMQswCQYDVQQIDAJCQjELMAkGA1UECgwCQ0Mx +CzAJBgNVBAsMAkREMRQwEgYDVQQDDAtzZXJ2ZXIgc2lnbjBZMBMGByqGSM49AgEG +CCqBHM9VAYItA0IABAW/+u7EBsjz9YCm45xShHbA3ythBl1KdPR2rw+2aJD4lqzE +sh2LA20T8XvU04I0kIvtt3+OPq6HBlT8b6gEjQyjWjBYMAkGA1UdEwQCMAAwCwYD +VR0PBAQDAgbAMB0GA1UdDgQWBBR97gapHaMCL5P/z+5+VLZ6VfU0ejAfBgNVHSME +GDAWgBSsYesigGJZCD6WyNF/znRcAq88mTAKBggqgRzPVQGDdQNIADBFAiB+CbW2 +jPan1KPG+AcuiYkaT/XLl0JzZp2pZSgRsL+c5QIhAIfi2T6PJc4ye0vFrRuSIxtq +CqSPMt9fU97ol6FNXpux +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/CMakeLists.txt b/src/coro_http/tests/CMakeLists.txt index a1a0daade..ff4c12feb 100644 --- a/src/coro_http/tests/CMakeLists.txt +++ b/src/coro_http/tests/CMakeLists.txt @@ -1,74 +1,80 @@ -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release") -endif() -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) -find_package(Threads REQUIRED) -find_package(ZLIB) -if (ZLIB_FOUND) - add_definitions(-DCINATRA_ENABLE_GZIP) -endif () -link_libraries(Threads::Threads) - -include_directories(include) -include_directories(include/ylt/thirdparty) - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") - #-ftree-slp-vectorize with coroutine cause link error. disable it util gcc fix. - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize") -endif() - -add_executable(coro_http_test - test_mixed.cpp - test_coro_http_server.cpp - test_cinatra.cpp - test_cinatra_websocket.cpp - test_http_parse.cpp - test_http_client_filter.cpp - test_http_ssl_mutual_auth.cpp - main.cpp - ) - -add_custom_command( - TARGET coro_http_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/src/coro_http/openssl_files) -add_custom_command( - TARGET coro_http_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/output/openssl_files) - -add_definitions(-DINJECT_FOR_HTTP_CLIENT_TEST) -add_definitions(-DINJECT_FOR_HTTP_SEVER_TEST) - -add_test(NAME coro_http_test COMMAND coro_http_test) -# target_compile_definitions(easylog_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 - if (YLT_ENABLE_SSL) - message(STATUS "Use SSL") - find_package(OpenSSL REQUIRED) - add_definitions(-DCINATRA_ENABLE_SSL) - target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock OpenSSL::SSL OpenSSL::Crypto) - endif () - - if (ZLIB_FOUND) - target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock ZLIB::ZLIB) - endif () -else() - if (YLT_ENABLE_SSL) - message(STATUS "Use SSL") - find_package(OpenSSL REQUIRED) - add_definitions(-DCINATRA_ENABLE_SSL) - target_link_libraries(coro_http_test OpenSSL::SSL OpenSSL::Crypto) - endif () - - if (ZLIB_FOUND) - target_link_libraries(coro_http_test ZLIB::ZLIB) - endif () -endif() +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Release") +endif() +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +find_package(Threads REQUIRED) +find_package(ZLIB) +if (ZLIB_FOUND) + add_definitions(-DCINATRA_ENABLE_GZIP) +endif () +link_libraries(Threads::Threads) + +include_directories(include) +include_directories(include/ylt/thirdparty) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines") + #-ftree-slp-vectorize with coroutine cause link error. disable it util gcc fix. + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-tree-slp-vectorize") +endif() + +add_executable(coro_http_test + test_mixed.cpp + test_coro_http_server.cpp + test_cinatra.cpp + test_cinatra_websocket.cpp + test_http_parse.cpp + test_http_client_filter.cpp + test_http_ssl_mutual_auth.cpp + main.cpp + ) + +add_custom_command( + TARGET coro_http_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_http/openssl_files) +add_custom_command( + TARGET coro_http_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/output/openssl_files) +add_custom_command( + TARGET coro_http_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_http/tests/openssl_files) + +add_definitions(-DINJECT_FOR_HTTP_CLIENT_TEST) +add_definitions(-DINJECT_FOR_HTTP_SEVER_TEST) + +add_test(NAME coro_http_test COMMAND coro_http_test + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/coro_http/tests) +# target_compile_definitions(easylog_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 + if (YLT_ENABLE_SSL) + message(STATUS "Use SSL") + find_package(OpenSSL REQUIRED) + add_definitions(-DCINATRA_ENABLE_SSL) + target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock OpenSSL::SSL OpenSSL::Crypto) + endif () + + if (ZLIB_FOUND) + target_link_libraries(coro_http_test PRIVATE ws2_32 mswsock ZLIB::ZLIB) + endif () +else() + if (YLT_ENABLE_SSL) + message(STATUS "Use SSL") + find_package(OpenSSL REQUIRED) + add_definitions(-DCINATRA_ENABLE_SSL) + target_link_libraries(coro_http_test OpenSSL::SSL OpenSSL::Crypto) + endif () + + if (ZLIB_FOUND) + target_link_libraries(coro_http_test ZLIB::ZLIB) + endif () +endif() diff --git a/src/coro_http/tests/openssl_files/ca.crt b/src/coro_http/tests/openssl_files/ca.crt new file mode 100644 index 000000000..4ba4063f5 --- /dev/null +++ b/src/coro_http/tests/openssl_files/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF +XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv +oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg +zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 +VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO +3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k +XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw +4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 +n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ +qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH +lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 +l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp +zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.crt b/src/coro_http/tests/openssl_files/client.crt new file mode 100644 index 000000000..9d8da4ea5 --- /dev/null +++ b/src/coro_http/tests/openssl_files/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG +Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt +5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ +opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 +/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et +jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI +G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 +pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs +Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF +Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA +faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW +8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq +q8rTyD5f5g== +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.key b/src/coro_http/tests/openssl_files/client.key new file mode 100644 index 000000000..a57cacb89 --- /dev/null +++ b/src/coro_http/tests/openssl_files/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o +z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E +V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E +8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW +pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj +25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf +NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt +ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t +ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B +T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH +ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv +E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai +zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi +cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D +ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V +qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY +yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD +J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF +OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 +72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE +Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK +lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 +oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB +Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh +BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D +za21e2VPgHOBFlAxpuA= +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index aca31a7e3..b89ecbd56 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,19 +1,20 @@ ------BEGIN CERTIFICATE----- -MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx -MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 -MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT -nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw -PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz -WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 -2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo -+0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN -06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 -CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ -kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a -VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu -LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ -rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDMjCCAhoCFCfqzDMJkItWsGmNb2dnTuQ7YiDfMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTQ5WhcNMzYwNDA1MDIyOTQ5WjBXMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTESMBAGA1UE +AwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEcB +wNSFuRb9/WLdS8BczaycllmOhwfMFqx/2Qd6XVEpOBmJsiDSQSHT1dpwMMZ7xCo5 +17QefyokWmBC+de83FY2EA+KWPreD+nZF1srDOOx6z+D08NN/7Go7eobRnUSBiSF +8BHk8J28I8Mm/h/0mngSQi7tUsnxKJ67yyHN1VTu/oZ24xeN/FSuTPqmLc4iB0/s +NOIpSjlgesP5tgAx4QW3++Z0NQjZamHYASvBmDBzlFTj2HzUcVEVWUotqX6f/l5d +JLseVnEU3bUGrtEyZMXrfz1aVT4vAlgtQ9J31tDAkzZq3Zi3PlLD7Jg2ns+oojS3 +Gfpi6DmNJw8u0FumiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCAGgMOsNeoj0pW +jszmz/IeEPWpHkzT88f4n3D/o5mkVvagEgP9QF+WpnjpdyYW7pruOJXdSJQzYG+C +dbY1h22qw9pqFu3AwCBD1sqPl/5DqwqeyAFvw/d9/nTyCo2VK6Yvpbc6H7wggMFo +8xzK39mJE5kJsl42PJ3meFt4Loxz0FHL4Le0cVp30XnjOoiOroZNHoMk32XS7lGu +2bmWMjiAJSdUP/Btm1xoYUxPUYdqz4MzyYZQCjc0izkLxmbkO41vX/ZkoYlDwCcb +E7XXeqn2kmLkB6aD8d6UJnFtxwuos6YhVBfKeySN4fWXKNPZweRKOcUOaL7GGDWU +m1HkKMpu +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/server.key b/src/coro_http/tests/openssl_files/server.key index 9aef5f575..42c3a1f23 100644 --- a/src/coro_http/tests/openssl_files/server.key +++ b/src/coro_http/tests/openssl_files/server.key @@ -1,30 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,D920B8941C56ADDC - -I2lW3QsAG/xubjtXpXh3wQ5Ru3VZiMkPNjc+G6/2JjjVr1sD+fzCWvvwdqdxGuNJ -gKdpPBHLuQfTTzGETE4NKDkYzmiPTVbZPJ77DyfL2cK1dcZtAY46RsHf+VMI5N8l -Be1jQSB5xvUa88dSIeowPTc2XSnTIoSFWCa38XuqYF7i0a3lv96eAyXpqB7Tm2r8 -SoYlm0n7/uzRpk6HWST65qnVv/j+37LuvSy6ehyh44+KDS4x9FUOZc5xwJ/37Jnl -SDC10+9zLc+jOTk6XgUuBSmG+xfZdcOrbknQ1Xj1YtseYH0plYAEWi4PsnMQkHzC -GGvK08Lgqxd7cGEKFh2MRZ/TEwriN5ud5HGm4yIHIj45rbedtRSQwl2EyHdWeW0J -rFltDy+SXnnkJaOcnBYXUD1jEwyy2lLamWRiu83VFbCv6yhOYuR6JejM6dctjgZ+ -Qf0PzH6L1bVpHKEl/GLByJ6GWYrQJqw83LAXlR+NNCC3nN7WAAaTuzA9LpgW9Vk0 -khRRs7rJGxwwwE4TfG9FbQxwuOsjKV9pRohB1x1nFMMm5IJ9SON2KjizsVdLbt7t -Gb/5M7RcSnnGvIWWXalXpFGKgciwYd8F1v0TJ+FMooZxgUp7Pmp5YKIHkBjMrnnW -rKuoxmA5oPgSNUtr4ddMJ1sTIQPhqI27+CrySTzWKH1ls45okBvsiCejpcJwfrZW -KLSkz/FsPoWm44uomBSDOikry8axrKQLB9tOVPKCx/z0VP060P9N81mu4h67bixr -xu+odIONqGhRZT/BYHL2NjDfWlFmTJQy8Drn1a7IEhp8FV7l2aY/hisrMN7MQVza -FGB0hMbVHGeFOCD9QNQwRU2wLtwpE7LT/lGNmKadQadXxeAqOWBckXrpwnrxZDEP -a8AYr2J55h/IE4Oi2DyibSEZdB+7334OJHMmr14q53eIpeit19BYVhWyu9AtORJp -As61C7s82AO+E5gOswsq05jwWV/GIIkgZ8/vswEffiihmDEf6AUZsVGW3BlpFlyU -i3g4e8HFTJ+s9Z3sTgZ1EWOP6Wd2OzyQYVA4ggBR/g/IC9s5em1wvAkVwIZaPvj7 -21BIQXyiGrw52T+vTUrAUG0l7yoHGCgVYJ+aEm+f103AiBYuReUbo39GEIY2GHLu -r3oUehtt4of0ootmPCmjrRUyY6LPeD+d+i1jJUSYFKezsVRpaiF5+J8YLGMcOPiI -8qRRNgXDMMvttwyhoxyr5+667OMv+XWr2VQj7i9MWCFwTMwNzdUoZI3PWDhXbXDO -lQJS6v3iAPw+KvLJywODe+C4shUqYdrRdUSKE0FfuB8Ajzh86+FmjJcZM+BSxM4J -hC2yjv114jDlsgjFSxQE2K1iotLUY9mfmW8QWVMO3L4LlNpr4ypNLYX0Ph2wgqzQ -kszXTFN11RFKFLUhF0Mi5m4ffMLPD5YyoqO9grpyC1Nt7vxaPPvcvPD86jK3ksqJ -MwucZGgm9HtUuAjGOSljUr0d+d+4pySJbcpH2YDIBHGVsCScYPVg8XZ1CYko3mq/ -d6jDUgydraEmQvIPiKMpTE18rW+jierv2FlB8AGcwxm2VWxuM25wQ40J2YuZLY7k ------END RSA PRIVATE KEY----- \ No newline at end of file +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIRwHA1IW5Fv39 +Yt1LwFzNrJyWWY6HB8wWrH/ZB3pdUSk4GYmyINJBIdPV2nAwxnvEKjnXtB5/KiRa +YEL517zcVjYQD4pY+t4P6dkXWysM47HrP4PTw03/sajt6htGdRIGJIXwEeTwnbwj +wyb+H/SaeBJCLu1SyfEonrvLIc3VVO7+hnbjF438VK5M+qYtziIHT+w04ilKOWB6 +w/m2ADHhBbf75nQ1CNlqYdgBK8GYMHOUVOPYfNRxURVZSi2pfp/+Xl0kux5WcRTd +tQau0TJkxet/PVpVPi8CWC1D0nfW0MCTNmrdmLc+UsPsmDaez6iiNLcZ+mLoOY0n +Dy7QW6aJAgMBAAECggEAFBYzEdD+3HJ25Ov+f/N6G1K9ncK7rcVVbcy9QdojJqrW +NH8zNT9fdxLaeS9gYzP1A9asWHsDOAPVA492CDLgCUVIRNIaMRRwIy50DIijR7aq +iIqlQR7sesGpzLrXI3joZt9Q46QXzx4y2W9gQMqZsMhsJGEqgkwouMc61IO+bi/a +II1Uf1EGijFd96Qk/bT76puakg6qjRoJ+SdjNJOM/cOd8Jon6qnvXB6jOtW+DqIs +539RvibVK4pPr8OIxzGSTXPqcEcdaKV0NiWbTWonuOZu4Nf49auK3awjpQPzzXPT +BPHMKSSAtFXUhjaHIaHOAyWnQ8NM/kgrOpvSxMxkhQKBgQDkQIFmMzw+LsjnzAeg +IrA6ANzfPQJzhkGQRuv57Cv6CWVQXpatixgvFpd8CHviIwVD90hNQH+PMF1fM1pf +aYEOkRdWhFjSSIEkO2VRkcS8B0unexNeBbTupQfbvVx8etu8N+SEwevhWv0rNs7R +lF5JkZx+g1iT/6hTpES14i/6PwKBgQDgn+VSP7USOAgCwTU1kJcCBNUS5sROcpWV +5BrRjzkRvVo7wFxCBEOzKoukAV+RZJFwvvam0PxSwP3eLtjYOyW+BT2uywJ10K28 +OgSPkUnMg6PKw0ilCz38CvCw0rI9g8aS1akFCNZ53LFIognWwqpnZsJEm1yR0Qlv +Tj/ERNxdNwKBgBLHe938uSgkkUMA9l+mevlKuOFlE56NnTdRnnihhby8qSlDnwII +P6UgJrZ9vDOOzhAZeEli1RvizsvWXckb1RJtvY3Qtb4XWQiyGlPrulP+Batx5NYH +gitgSJU7rzBOq2WA87w4eD/CTLIRgFKd8mP7JvUBuXfzwNWg3kZYpbnhAoGAEzNX +xNoRPkdv19xwEe4UGmYTWJRFP3dn9fIToMofVLbc2bKtsC7xIoWGfjRn2OPB0uNf +7g57Iw/AI5fZjVIw/bcw+Jn90dhOoYJMFYGTz1mJTLG4qfL2D29X96Vq+vsipDaD +RhzlSHFm7hB7ytHFAyWzgW3OUeCOb+c+aCaCt60CgYBZaensYV8gEd/z06DZ5SQC +cPdRpC7TOCJJyi9Lr3VJDUZJaF4gfnOjzNKQqzh3Jy/vrtDkYJArtIKbISK6Kb2q ++pXpItKPuYFNeIRoieZXy9hSJhaw88fcDfMVODG7Fx0h2LcIFmoNXxNFEayUjkLt +sVgJ1M3OhYqqfZ+8d4QoZg== +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/test_cinatra_websocket.cpp b/src/coro_http/tests/test_cinatra_websocket.cpp index dfe58ab72..3aa7a4bea 100644 --- a/src/coro_http/tests/test_cinatra_websocket.cpp +++ b/src/coro_http/tests/test_cinatra_websocket.cpp @@ -1,446 +1,445 @@ -#include -#include -#include -#include -#include - -#include "cinatra/error.hpp" -#include "doctest.h" -#include "ylt/coro_http/coro_http_client.hpp" -#include "ylt/coro_http/coro_http_server.hpp" - -using namespace std::chrono_literals; - -using namespace coro_http; - -#ifdef CINATRA_ENABLE_SSL -TEST_CASE("test wss client") { - cinatra::coro_http_server server(1, 9001); - server.init_ssl("../openssl_files/server.crt", "../openssl_files/server.key", - "test"); - server.set_http_handler( - "/", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - std::this_thread::sleep_for(200ms); - - coro_http_client client{}; - bool ok = - client.init_ssl(asio::ssl::verify_peer, "../openssl_files/server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - - async_simple::coro::syncAwait(client.connect("wss://localhost:9001")); - - async_simple::coro::syncAwait(client.write_websocket("hello")); - auto data = async_simple::coro::syncAwait(client.read_websocket()); - CHECK(data.resp_body == "hello"); - - client.close(); - - server.stop(); -} -#endif - -async_simple::coro::Lazy test_websocket(coro_http_client &client) { - auto r = co_await client.connect("ws://localhost:8090/ws"); - if (r.net_err) { - co_return; - } - - co_await client.write_websocket("hello websocket"); - auto data = co_await client.read_websocket(); - CHECK(data.resp_body == "hello websocket"); - co_await client.write_websocket("test again"); - data = co_await client.read_websocket(); - CHECK(data.resp_body == "test again"); - co_await client.write_websocket_close("ws close"); - data = co_await client.read_websocket(); - CHECK(data.resp_body == "ws close"); - CHECK(data.net_err == asio::error::eof); -} - -#ifdef CINATRA_ENABLE_GZIP -async_simple::coro::Lazy test_gzip_websocket(coro_http_client &client) { - auto r = co_await client.connect("ws://localhost:8090/ws"); - if (r.net_err) { - co_return; - } - - std::string str = "hello websocket"; - co_await client.write_websocket(str.data(), str.size()); - auto data = co_await client.read_websocket(); - CHECK(data.resp_body == "hello websocket"); - - co_await client.write_websocket_close("ws close"); - data = co_await client.read_websocket(); - CHECK(data.resp_body == "ws close"); - CHECK(data.net_err == asio::error::eof); -} -#endif - -TEST_CASE("test websocket") { - cinatra::coro_http_server server(1, 8090); - server.set_http_handler( - "/ws", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.set_http_handler( - "/test_client_timeout", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - std::this_thread::sleep_for(200ms); - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - - auto client_timeout = []() -> async_simple::coro::Lazy { - coro_http_client client{}; - client.set_req_timeout(50ms); - client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); - - auto r = co_await client.connect("ws://localhost:8090/test_client_timeout"); - if (r.net_err) { - co_return; - } - - co_await client.write_websocket("hello websocket"); - auto data = co_await client.read_websocket(); - CINATRA_LOG_DEBUG << data.net_err.message(); - CHECK(data.net_err == http_errc::request_timeout); - }; - - async_simple::coro::syncAwait(client_timeout()); - - coro_http_client client{}; - client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); - - async_simple::coro::syncAwait(test_websocket(client)); - -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - { - auto lazy1 = []() -> async_simple::coro::Lazy { - coro_http_client client{}; - co_await client.connect("ws://localhost:8090/ws"); - std::string send_str = "test"; - websocket ws{}; - // msg too long - auto header = ws.encode_ws_header(9 * 1024 * 1024, opcode::text, true); - co_await client.async_write_raw(header); - co_await client.async_write_raw(send_str); - auto data = co_await client.read_websocket(); - CHECK(data.status != 200); - CINATRA_LOG_DEBUG << data.resp_body; - }; - async_simple::coro::syncAwait(lazy1()); - } - - { - auto lazy1 = []() -> async_simple::coro::Lazy { - coro_http_client client{}; - co_await client.connect("ws://localhost:8090/ws"); - std::string send_str = "test"; - websocket ws{}; - // error frame - auto header = ws.encode_ws_header(send_str.size(), (opcode)15, true); - co_await client.async_write_raw(header); - co_await client.async_write_raw(send_str); - auto data = co_await client.read_websocket(); - CHECK(data.status != 200); - }; - async_simple::coro::syncAwait(lazy1()); - } -#endif - -#ifdef CINATRA_ENABLE_GZIP - coro_http_client client1{}; - client1.set_ws_deflate(true); - async_simple::coro::syncAwait(test_gzip_websocket(client1)); -#endif - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - // client->async_close(); -} - -void test_websocket_content(size_t len) { - cinatra::coro_http_server server(1, 8090); - server.set_http_handler( - "/", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - - auto lazy = [len]() -> async_simple::coro::Lazy { - coro_http_client client{}; - co_await client.connect("ws://localhost:8090"); - std::string send_str(len, 'a'); - co_await client.write_websocket(std::string(send_str)); - auto data = co_await client.read_websocket(); - REQUIRE(data.resp_body.size() == send_str.size()); - CHECK(data.resp_body == send_str); - }; - - async_simple::coro::syncAwait(lazy()); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - server.stop(); -} - -TEST_CASE("test websocket content lt 126") { - test_websocket_content(1); - test_websocket_content(125); -} - -TEST_CASE("test websocket content ge 126") { - test_websocket_content(126); - test_websocket_content(127); -} - -TEST_CASE("test websocket content ge 65535") { - test_websocket_content(65535); - test_websocket_content(65536); -} - -TEST_CASE("test send after server stop") { - cinatra::coro_http_server server(1, 8090); - server.async_start(); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - coro_http_client client{}; - async_simple::coro::syncAwait(client.connect("ws://127.0.0.1:8090")); - - server.stop(); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - async_simple::coro::syncAwait(client.write_websocket("")); - auto data = async_simple::coro::syncAwait(client.read_websocket()); - CHECK(data.net_err); -} - -TEST_CASE("test read write in different threads") { - cinatra::coro_http_server server(1, 8090); - size_t count = 0; - std::promise promise; - server.set_http_handler( - "/", - [&](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - CHECK(req.get_content_type() == content_type::websocket); - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - count++; - if (count == 100) { - promise.set_value(); - break; - } - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - server.async_start(); - - auto client = std::make_shared(); - std::string send_str(100, 'a'); - std::weak_ptr weak = client; - auto another_thread_lazy = [client, - send_str]() -> async_simple::coro::Lazy { - for (int i = 0; i < 100; i++) { - auto data = co_await client->read_websocket(); - if (data.net_err) { - co_return; - } - REQUIRE(data.resp_body.size() == send_str.size()); - CHECK(data.resp_body == send_str); - } - }; - another_thread_lazy().start([](auto &&) { - }); - - auto lazy = [client, weak, &send_str]() -> async_simple::coro::Lazy { - co_await client->connect("ws://localhost:8090"); - for (int i = 0; i < 100; i++) { - auto data = co_await client->write_websocket(std::string(send_str)); - if (data.net_err) { - co_return; - } - } - }; - - async_simple::coro::syncAwait(lazy().via(&client->get_executor())); - - promise.get_future().wait_for(std::chrono::seconds(2)); - - server.stop(); -} - -async_simple::coro::Lazy test_websocket() { - coro_http_client client{}; - auto r = co_await client.connect("ws://127.0.0.1:8089/ws_echo"); - if (r.net_err) { - co_return; - } - - co_await client.write_websocket(std::string_view("test2fdsaf"), - opcode::binary); - auto data = co_await client.read_websocket(); - CHECK(data.resp_body == "test2fdsaf"); - - co_await client.write_websocket_close("ws close"); - data = co_await client.read_websocket(); - CHECK(data.net_err == asio::error::eof); - CHECK(data.resp_body == "ws close"); -} - -TEST_CASE("test client quit after send msg") { - coro_http_server server(1, 8089); - server.set_http_handler( - "/ws_echo", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - websocket_result result{}; - - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - if (result.type == ws_frame_type::WS_CLOSE_FRAME) { - break; - } - - co_await resp.get_conn()->write_websocket(result.data); - } - }); - server.async_start(); - - async_simple::coro::syncAwait(test_websocket()); -} - -#ifdef CINATRA_ENABLE_GZIP -TEST_CASE("test websocket permessage defalte") { - coro_http_server server(1, 8090); - server.set_http_handler( - "/ws_extesion", - [](coro_http_request &req, - coro_http_response &resp) -> async_simple::coro::Lazy { - websocket_result result{}; - while (true) { - result = co_await req.get_conn()->read_websocket(); - if (result.ec) { - break; - } - - if (result.type == ws_frame_type::WS_CLOSE_FRAME) { - CINATRA_LOG_DEBUG << "close frame\n"; - break; - } - - if (result.type == ws_frame_type::WS_TEXT_FRAME || - result.type == ws_frame_type::WS_BINARY_FRAME) { - CHECK(result.data == "test"); - } - else if (result.type == ws_frame_type::WS_PING_FRAME || - result.type == ws_frame_type::WS_PONG_FRAME) { - // ping pong frame just need to continue, no need echo anything, - // because framework has reply ping/pong msg to client - // automatically. - continue; - } - else { - // error frame - break; - } - - auto ec = co_await req.get_conn()->write_websocket(result.data); - if (ec) { - break; - } - } - }); - - server.async_start(); - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - coro_http_client client{}; - client.set_ws_deflate(true); - async_simple::coro::syncAwait( - client.connect("ws://localhost:8090/ws_extesion")); - - std::string send_str("test"); - - async_simple::coro::syncAwait(client.write_websocket(send_str)); - auto data = async_simple::coro::syncAwait(client.read_websocket()); - CHECK(data.resp_body == "test"); - - std::this_thread::sleep_for(std::chrono::milliseconds(300)); - - server.stop(); - client.close(); -} -#endif +#include +#include +#include +#include +#include + +#include "cinatra/error.hpp" +#include "doctest.h" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" + +using namespace std::chrono_literals; + +using namespace coro_http; + +#ifdef CINATRA_ENABLE_SSL +TEST_CASE("test wss client") { + cinatra::coro_http_server server(1, 9001); + server.init_ssl("../openssl_files/server.crt", "../openssl_files/server.key", + "test"); + server.set_http_handler( + "/", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + std::this_thread::sleep_for(200ms); + + coro_http_client client{}; + bool ok = client.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + + async_simple::coro::syncAwait(client.connect("wss://localhost:9001")); + + async_simple::coro::syncAwait(client.write_websocket("hello")); + auto data = async_simple::coro::syncAwait(client.read_websocket()); + CHECK(data.resp_body == "hello"); + + client.close(); + + server.stop(); +} +#endif + +async_simple::coro::Lazy test_websocket(coro_http_client &client) { + auto r = co_await client.connect("ws://localhost:8090/ws"); + if (r.net_err) { + co_return; + } + + co_await client.write_websocket("hello websocket"); + auto data = co_await client.read_websocket(); + CHECK(data.resp_body == "hello websocket"); + co_await client.write_websocket("test again"); + data = co_await client.read_websocket(); + CHECK(data.resp_body == "test again"); + co_await client.write_websocket_close("ws close"); + data = co_await client.read_websocket(); + CHECK(data.resp_body == "ws close"); + CHECK(data.net_err == asio::error::eof); +} + +#ifdef CINATRA_ENABLE_GZIP +async_simple::coro::Lazy test_gzip_websocket(coro_http_client &client) { + auto r = co_await client.connect("ws://localhost:8090/ws"); + if (r.net_err) { + co_return; + } + + std::string str = "hello websocket"; + co_await client.write_websocket(str.data(), str.size()); + auto data = co_await client.read_websocket(); + CHECK(data.resp_body == "hello websocket"); + + co_await client.write_websocket_close("ws close"); + data = co_await client.read_websocket(); + CHECK(data.resp_body == "ws close"); + CHECK(data.net_err == asio::error::eof); +} +#endif + +TEST_CASE("test websocket") { + cinatra::coro_http_server server(1, 8090); + server.set_http_handler( + "/ws", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.set_http_handler( + "/test_client_timeout", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + std::this_thread::sleep_for(200ms); + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + + auto client_timeout = []() -> async_simple::coro::Lazy { + coro_http_client client{}; + client.set_req_timeout(50ms); + client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); + + auto r = co_await client.connect("ws://localhost:8090/test_client_timeout"); + if (r.net_err) { + co_return; + } + + co_await client.write_websocket("hello websocket"); + auto data = co_await client.read_websocket(); + CINATRA_LOG_DEBUG << data.net_err.message(); + CHECK(data.net_err == http_errc::request_timeout); + }; + + async_simple::coro::syncAwait(client_timeout()); + + coro_http_client client{}; + client.set_ws_sec_key("s//GYHa/XO7Hd2F2eOGfyA=="); + + async_simple::coro::syncAwait(test_websocket(client)); + +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + { + auto lazy1 = []() -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await client.connect("ws://localhost:8090/ws"); + std::string send_str = "test"; + websocket ws{}; + // msg too long + auto header = ws.encode_ws_header(9 * 1024 * 1024, opcode::text, true); + co_await client.async_write_raw(header); + co_await client.async_write_raw(send_str); + auto data = co_await client.read_websocket(); + CHECK(data.status != 200); + CINATRA_LOG_DEBUG << data.resp_body; + }; + async_simple::coro::syncAwait(lazy1()); + } + + { + auto lazy1 = []() -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await client.connect("ws://localhost:8090/ws"); + std::string send_str = "test"; + websocket ws{}; + // error frame + auto header = ws.encode_ws_header(send_str.size(), (opcode)15, true); + co_await client.async_write_raw(header); + co_await client.async_write_raw(send_str); + auto data = co_await client.read_websocket(); + CHECK(data.status != 200); + }; + async_simple::coro::syncAwait(lazy1()); + } +#endif + +#ifdef CINATRA_ENABLE_GZIP + coro_http_client client1{}; + client1.set_ws_deflate(true); + async_simple::coro::syncAwait(test_gzip_websocket(client1)); +#endif + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + // client->async_close(); +} + +void test_websocket_content(size_t len) { + cinatra::coro_http_server server(1, 8090); + server.set_http_handler( + "/", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + + auto lazy = [len]() -> async_simple::coro::Lazy { + coro_http_client client{}; + co_await client.connect("ws://localhost:8090"); + std::string send_str(len, 'a'); + co_await client.write_websocket(std::string(send_str)); + auto data = co_await client.read_websocket(); + REQUIRE(data.resp_body.size() == send_str.size()); + CHECK(data.resp_body == send_str); + }; + + async_simple::coro::syncAwait(lazy()); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + server.stop(); +} + +TEST_CASE("test websocket content lt 126") { + test_websocket_content(1); + test_websocket_content(125); +} + +TEST_CASE("test websocket content ge 126") { + test_websocket_content(126); + test_websocket_content(127); +} + +TEST_CASE("test websocket content ge 65535") { + test_websocket_content(65535); + test_websocket_content(65536); +} + +TEST_CASE("test send after server stop") { + cinatra::coro_http_server server(1, 8090); + server.async_start(); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + coro_http_client client{}; + async_simple::coro::syncAwait(client.connect("ws://127.0.0.1:8090")); + + server.stop(); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + async_simple::coro::syncAwait(client.write_websocket("")); + auto data = async_simple::coro::syncAwait(client.read_websocket()); + CHECK(data.net_err); +} + +TEST_CASE("test read write in different threads") { + cinatra::coro_http_server server(1, 8090); + size_t count = 0; + std::promise promise; + server.set_http_handler( + "/", + [&](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + CHECK(req.get_content_type() == content_type::websocket); + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + count++; + if (count == 100) { + promise.set_value(); + break; + } + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + server.async_start(); + + auto client = std::make_shared(); + std::string send_str(100, 'a'); + std::weak_ptr weak = client; + auto another_thread_lazy = [client, + send_str]() -> async_simple::coro::Lazy { + for (int i = 0; i < 100; i++) { + auto data = co_await client->read_websocket(); + if (data.net_err) { + co_return; + } + REQUIRE(data.resp_body.size() == send_str.size()); + CHECK(data.resp_body == send_str); + } + }; + another_thread_lazy().start([](auto &&) { + }); + + auto lazy = [client, weak, &send_str]() -> async_simple::coro::Lazy { + co_await client->connect("ws://localhost:8090"); + for (int i = 0; i < 100; i++) { + auto data = co_await client->write_websocket(std::string(send_str)); + if (data.net_err) { + co_return; + } + } + }; + + async_simple::coro::syncAwait(lazy().via(&client->get_executor())); + + promise.get_future().wait_for(std::chrono::seconds(2)); + + server.stop(); +} + +async_simple::coro::Lazy test_websocket() { + coro_http_client client{}; + auto r = co_await client.connect("ws://127.0.0.1:8089/ws_echo"); + if (r.net_err) { + co_return; + } + + co_await client.write_websocket(std::string_view("test2fdsaf"), + opcode::binary); + auto data = co_await client.read_websocket(); + CHECK(data.resp_body == "test2fdsaf"); + + co_await client.write_websocket_close("ws close"); + data = co_await client.read_websocket(); + CHECK(data.net_err == asio::error::eof); + CHECK(data.resp_body == "ws close"); +} + +TEST_CASE("test client quit after send msg") { + coro_http_server server(1, 8089); + server.set_http_handler( + "/ws_echo", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + websocket_result result{}; + + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + if (result.type == ws_frame_type::WS_CLOSE_FRAME) { + break; + } + + co_await resp.get_conn()->write_websocket(result.data); + } + }); + server.async_start(); + + async_simple::coro::syncAwait(test_websocket()); +} + +#ifdef CINATRA_ENABLE_GZIP +TEST_CASE("test websocket permessage defalte") { + coro_http_server server(1, 8090); + server.set_http_handler( + "/ws_extesion", + [](coro_http_request &req, + coro_http_response &resp) -> async_simple::coro::Lazy { + websocket_result result{}; + while (true) { + result = co_await req.get_conn()->read_websocket(); + if (result.ec) { + break; + } + + if (result.type == ws_frame_type::WS_CLOSE_FRAME) { + CINATRA_LOG_DEBUG << "close frame\n"; + break; + } + + if (result.type == ws_frame_type::WS_TEXT_FRAME || + result.type == ws_frame_type::WS_BINARY_FRAME) { + CHECK(result.data == "test"); + } + else if (result.type == ws_frame_type::WS_PING_FRAME || + result.type == ws_frame_type::WS_PONG_FRAME) { + // ping pong frame just need to continue, no need echo anything, + // because framework has reply ping/pong msg to client + // automatically. + continue; + } + else { + // error frame + break; + } + + auto ec = co_await req.get_conn()->write_websocket(result.data); + if (ec) { + break; + } + } + }); + + server.async_start(); + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + coro_http_client client{}; + client.set_ws_deflate(true); + async_simple::coro::syncAwait( + client.connect("ws://localhost:8090/ws_extesion")); + + std::string send_str("test"); + + async_simple::coro::syncAwait(client.write_websocket(send_str)); + auto data = async_simple::coro::syncAwait(client.read_websocket()); + CHECK(data.resp_body == "test"); + + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + + server.stop(); + client.close(); +} +#endif diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 899aa8601..528a9d792 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -1,196 +1,196 @@ -/* - * Copyright (c) 2025, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include "async_simple/coro/Lazy.h" -#include "doctest.h" -#include "ylt/coro_http/coro_http_client.hpp" -#include "ylt/coro_http/coro_http_server.hpp" - -using namespace coro_http; - -using namespace std::chrono_literals; - -const std::string CERT_PATH = "../openssl_files/"; -const std::string SERVER_CERT = "server.crt"; -const std::string SERVER_KEY = "server.key"; -const std::string CLIENT_CERT = "client.crt"; -const std::string CLIENT_KEY = "client.key"; -const std::string CA_CERT = "ca.crt"; - -#ifdef YLT_ENABLE_SSL -TEST_CASE("testing HTTP SSL one-way authentication") { - coro_http_server server(1, 8901); - - server.init_ssl((CERT_PATH + SERVER_CERT), - (CERT_PATH + SERVER_KEY), "", "", false); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Hello World"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.get("http://127.0.0.1:8901/"); - REQUIRE_MESSAGE(result.status == 200, "GET request failed"); - REQUIRE_MESSAGE(result.resp_body == "Hello World", "response body mismatch"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - success") { - coro_http_server server(1, 8902); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Hello Mutual Auth"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, - CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.get("http://127.0.0.1:8902/"); - REQUIRE_MESSAGE(result.status == 200, "GET request failed"); - REQUIRE_MESSAGE(result.resp_body == "Hello Mutual Auth", - "response body mismatch"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { - coro_http_server server(1, 8903); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Should not reach here"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.get("http://127.0.0.1:8903/"); - REQUIRE_MESSAGE(result.status != 200, - "request should fail without client cert"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { - coro_http_server server(1, 8904); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/", [](coro_http_request& req, coro_http_response& resp) { - resp.set_status_and_content(status_type::ok, "Should not reach here"); - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = - client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, - "invalid_client.crt", "invalid_client.key", "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl should succeed"); - - auto result = client.get("http://127.0.0.1:8904/"); - REQUIRE_MESSAGE(result.status != 200, - "request should fail with invalid client cert"); - - server.stop(); - thd.join(); -} - -TEST_CASE("testing HTTP SSL mutual authentication - POST request") { - coro_http_server server(1, 8905); - - server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", - (CERT_PATH + CA_CERT), true); - - server.set_http_handler( - "/echo", - [](coro_http_request& req, - coro_http_response& resp) -> async_simple::coro::Lazy { - std::string_view body = req.get_body(); - resp.set_status_and_content(status_type::ok, - "Echo: " + std::string(body)); - co_return; - }); - - std::thread thd([&server]() { - server.sync_start(); - }); - - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - - coro_http_client client; - bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, - CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); - - auto result = client.post("http://127.0.0.1:8905/echo", "Test Message", - req_content_type::text); - REQUIRE_MESSAGE(result.status == 200, "POST request failed"); - REQUIRE_MESSAGE(result.resp_body == "Echo: Test Message", - "response body mismatch"); - - server.stop(); - thd.join(); -} -#endif +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "async_simple/coro/Lazy.h" +#include "doctest.h" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" + +using namespace coro_http; + +using namespace std::chrono_literals; + +const std::string CERT_PATH = "../openssl_files/"; +const std::string SERVER_CERT = "server.crt"; +const std::string SERVER_KEY = "server.key"; +const std::string CLIENT_CERT = "client.crt"; +const std::string CLIENT_KEY = "client.key"; +const std::string CA_CERT = "ca.crt"; + +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing HTTP SSL one-way authentication") { + coro_http_server server(1, 8901); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", "", + false); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello World"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("https://127.0.0.1:8901/"); + REQUIRE_MESSAGE(result.status == 200, "GET request failed"); + REQUIRE_MESSAGE(result.resp_body == "Hello World", "response body mismatch"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - success") { + coro_http_server server(1, 8902); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello Mutual Auth"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("https://127.0.0.1:8902/"); + REQUIRE_MESSAGE(result.status == 200, "GET request failed"); + REQUIRE_MESSAGE(result.resp_body == "Hello Mutual Auth", + "response body mismatch"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - client without cert") { + coro_http_server server(1, 8903); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Should not reach here"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.get("https://127.0.0.1:8903/"); + REQUIRE_MESSAGE(result.status != 200, + "request should fail without client cert"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - client with invalid cert") { + coro_http_server server(1, 8904); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/", [](coro_http_request& req, coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Should not reach here"); + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + // Use client cert but wrong CA for server verification - this will cause + // handshake failure + bool init_ok = + client.init_ssl(asio::ssl::verify_peer, CERT_PATH, "wrong_ca.crt", + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + // When CA cert file doesn't exist, init_ssl returns false + REQUIRE_MESSAGE(init_ok == false, + "client init_ssl should fail with wrong CA cert"); + + server.stop(); + thd.join(); +} + +TEST_CASE("testing HTTP SSL mutual authentication - POST request") { + coro_http_server server(1, 8905); + + server.init_ssl((CERT_PATH + SERVER_CERT), (CERT_PATH + SERVER_KEY), "", + (CERT_PATH + CA_CERT), true); + + server.set_http_handler( + "/echo", + [](coro_http_request& req, + coro_http_response& resp) -> async_simple::coro::Lazy { + std::string_view body = req.get_body(); + resp.set_status_and_content(status_type::ok, + "Echo: " + std::string(body)); + co_return; + }); + + std::thread thd([&server]() { + server.sync_start(); + }); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + coro_http_client client; + bool init_ok = client.init_ssl(asio::ssl::verify_peer, CERT_PATH, CA_CERT, + CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "client init_ssl failed"); + + auto result = client.post("https://127.0.0.1:8905/echo", "Test Message", + req_content_type::text); + REQUIRE_MESSAGE(result.status == 200, "POST request failed"); + REQUIRE_MESSAGE(result.resp_body == "Echo: Test Message", + "response body mismatch"); + + server.stop(); + thd.join(); +} +#endif diff --git a/src/coro_rpc/tests/CMakeLists.txt b/src/coro_rpc/tests/CMakeLists.txt index eea162257..0fcfc916d 100644 --- a/src/coro_rpc/tests/CMakeLists.txt +++ b/src/coro_rpc/tests/CMakeLists.txt @@ -1,54 +1,57 @@ -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) -set(TEST_SRCS - test_acceptor.cpp - test_coro_rpc_server.cpp - test_coro_rpc_client.cpp - test_register_handler.cpp - test_router.cpp - test_connection.cpp - test_function_name.cpp - test_variadic.cpp - test_parallel.cpp - test_client_filter.cpp - test_abi_compatible.cpp - test_rpc_ssl_mutual_auth.cpp - ) -if (YLT_ENABLE_IBV AND YLT_ENABLE_CUDA) - set(TEST_SRC ${TEST_SRCS} test_gdr.cpp) -endif() -set(TEST_COMMON - rpc_api.cpp - main.cpp - ) -add_executable(coro_rpc_test - ${TEST_SRCS} - ${TEST_COMMON} - ) -add_custom_command( - TARGET coro_rpc_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/src/coro_rpc/openssl_files) -add_custom_command( - TARGET coro_rpc_test POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files - ${CMAKE_BINARY_DIR}/output/openssl_files) -target_compile_definitions(coro_rpc_test PRIVATE UNIT_TEST_INJECT) -target_compile_definitions(coro_rpc_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) - -add_test(NAME coro_rpc_test COMMAND coro_rpc_test) - -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests/coro_rpc) - -add_executable(coro_rpc_regist_test_1 rpc_api.cpp test_register_duplication_1.cpp) -add_executable(coro_rpc_regist_test_2 rpc_api.cpp test_register_duplication_2.cpp) -add_executable(coro_rpc_regist_test_3 rpc_api.cpp test_register_duplication_3.cpp) - -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 - target_link_libraries(coro_rpc_test PRIVATE ws2_32 mswsock) - target_link_libraries(coro_rpc_regist_test_1 PRIVATE ws2_32 mswsock) - target_link_libraries(coro_rpc_regist_test_2 PRIVATE ws2_32 mswsock) - target_link_libraries(coro_rpc_regist_test_3 PRIVATE ws2_32 mswsock) -endif() +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests) +set(TEST_SRCS + test_acceptor.cpp + test_coro_rpc_server.cpp + test_coro_rpc_client.cpp + test_register_handler.cpp + test_router.cpp + test_connection.cpp + test_function_name.cpp + test_variadic.cpp + test_parallel.cpp + test_client_filter.cpp + test_abi_compatible.cpp + test_rpc_ssl_mutual_auth.cpp + ) +set(TEST_COMMON + rpc_api.cpp + main.cpp + ) +add_executable(coro_rpc_test + ${TEST_SRCS} + ${TEST_COMMON} + ) +add_custom_command( + TARGET coro_rpc_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_rpc/openssl_files) +add_custom_command( + TARGET coro_rpc_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/output/openssl_files) +add_custom_command( + TARGET coro_rpc_test POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/openssl_files + ${CMAKE_BINARY_DIR}/src/coro_rpc/tests/openssl_files) +target_compile_definitions(coro_rpc_test PRIVATE UNIT_TEST_INJECT) +target_compile_definitions(coro_rpc_test PRIVATE STRUCT_PACK_ENABLE_UNPORTABLE_TYPE) + +add_test(NAME coro_rpc_test COMMAND coro_rpc_test + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/coro_rpc/tests) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/output/tests/coro_rpc) + +add_executable(coro_rpc_regist_test_1 rpc_api.cpp test_register_duplication_1.cpp) +add_executable(coro_rpc_regist_test_2 rpc_api.cpp test_register_duplication_2.cpp) +add_executable(coro_rpc_regist_test_3 rpc_api.cpp test_register_duplication_3.cpp) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_SYSTEM_NAME MATCHES "Windows") # mingw-w64 + target_link_libraries(coro_rpc_test PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_regist_test_1 PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_regist_test_2 PRIVATE ws2_32 mswsock) + target_link_libraries(coro_rpc_regist_test_3 PRIVATE ws2_32 mswsock) +endif() diff --git a/src/coro_rpc/tests/ServerTester.hpp b/src/coro_rpc/tests/ServerTester.hpp index 8f58589e8..e52dbf086 100644 --- a/src/coro_rpc/tests/ServerTester.hpp +++ b/src/coro_rpc/tests/ServerTester.hpp @@ -1,455 +1,455 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef CORO_RPC_SERVERTESTER_HPP -#define CORO_RPC_SERVERTESTER_HPP -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "inject_action.hpp" -#include "rpc_api.hpp" -#include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/coro_rpc/impl/errno.h" - -#ifdef _MSC_VER -#define CORO_RPC_FUNCTION_SIGNATURE __FUNCSIG__ -#else -#define CORO_RPC_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ -#endif - -using namespace std::string_literals; -using namespace async_simple::coro; -using namespace coro_rpc; -using namespace std::chrono_literals; - -struct TesterConfig { - TesterConfig() = default; - TesterConfig(TesterConfig &c) { - enable_heartbeat = c.enable_heartbeat; - use_ssl = c.use_ssl; - use_rdma = c.use_rdma; - sync_client = c.sync_client; - use_outer_io_context = c.use_outer_io_context; - port = c.port; - conn_timeout_duration = c.conn_timeout_duration; - } - bool enable_heartbeat; - bool use_ssl; - bool use_rdma; - bool sync_client; - bool use_outer_io_context; - unsigned short port; - std::string address = "0.0.0.0"; - std::chrono::steady_clock::duration conn_timeout_duration = - std::chrono::seconds(0); - - friend std::ostream &operator<<(std::ostream &os, - const TesterConfig &config) { - os << std::boolalpha; - os << " enable_heartbeat: " << config.enable_heartbeat << ";" - << " use_ssl: " << config.use_ssl << ";" - << " use_rdma: " << config.use_rdma << ";" - << " sync_client: " << config.sync_client << ";" - << " use_outer_io_context: " << config.use_outer_io_context << ";" - << " port: " << config.port << ";" - << " address: " << config.address << ";"; - os << " conn_timeout_duration: "; - auto val = std::chrono::duration_cast( - config.conn_timeout_duration) - .count(); - os << val << "ms" - << ";"; - return os; - } -}; - -struct ServerTester : TesterConfig { - ServerTester(TesterConfig config) - : TesterConfig(config), - executor_(io_context_.get_executor()), - port_(std::to_string(config.port)) { - if (use_outer_io_context) { - std::promise promise; - auto future = promise.get_future(); - thd_ = std::thread([this, &promise]() { - asio::io_context::work work(io_context_); - promise.set_value(); - io_context_.run(); - }); - future.wait(); - } - - std::stringstream ss; - ss << config; - conf_str_ = ss.str(); - } - ~ServerTester() { - if (use_outer_io_context) { - io_context_.stop(); - thd_.join(); - } - g_action = coro_rpc::inject_action::nothing; - } - void run() { - ELOGV(INFO, "run test: heartbeat=%d, ssl=%d", enable_heartbeat, use_ssl); - register_all_function(); - test_all(); - } - virtual void test_all() { - test_connect_timeout(); - test_function_registered(); - if (sync_client) { - // only sync_call implement inject behavior - test_client_send_bad_header(); - test_client_send_bad_magic_num(); - test_client_send_header_length_is_0(); - test_client_close_socket_after_send_header(); - test_client_close_socket_after_send_partial_header(); - test_client_close_socket_after_send_payload(); - } - test_heartbeat(); - test_call_function_with_long_response_time(); - test_call_with_large_buffer(); - } - std::shared_ptr create_client( - inject_action action = inject_action::nothing) { - std::shared_ptr client; - // sometimes, connect will take more than conn_timeout_duration(500ms), so - // retry 3 times to make sure connect ok. - coro_rpc::errc ec; - int retry = 4; - for (int i = 0; i < retry; i++) { - if (use_outer_io_context) { - client = std::make_shared(&executor_); - } - else { - client = - std::make_shared(coro_io::get_global_executor()); - } -#ifdef YLT_ENABLE_SSL - if (use_ssl) { - bool ok = client->init_ssl("../openssl_files", "server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - } -#endif -#ifdef YLT_ENABLE_IBV - if (use_rdma) { - bool ok = client->init_ibv(); - REQUIRE_MESSAGE(ok == true, - "init ibv fail, please check ibverbs config"); - } -#endif - g_action = action; - - if (sync_client) { - ec = client->sync_connect("127.0.0.1", port_); - } - else { - ec = syncAwait(client->connect("127.0.0.1", port_)); - } - - if (!ec) { - break; - } - - ELOGV(INFO, "retry times %d", i); - } - - REQUIRE_MESSAGE(!ec, std::to_string(client->get_client_id()) - .append(" not connected ") - .append(conf_str_)); - return client; - } - template - decltype(auto) call(std::shared_ptr client, Args &&...args) { - ELOGV(INFO, "%s client_id %d call %s", - sync_client ? "sync_client" : "async_client", client->get_client_id(), - coro_rpc::get_func_name().data()); - if (sync_client) { - return client->sync_call(std::forward(args)...); - } - else { - return syncAwait( - client->template call(std::forward(args)...)); - } - } - - virtual void register_all_function() {} - - virtual void remove_all_rpc_function() {} - - void test_function_registered() { - g_action = {}; - - // because heartbeat timeout is 500ms, sometimes the client closed because - // of timeout, so retry call. - int retry = 4; - for (int i = 0; i < retry; i++) { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - ELOGV(INFO, "retry call times %d", i); - { - auto ret = call(client); - ELOGV(INFO, "client_id %d call async_hi", client->get_client_id()); - if (!ret) { - ELOGV(ERROR, "client_id %d call async_hi error %s", - client->get_client_id(), ret.error().msg.data()); - continue; - } - else { - ELOGV(INFO, "client_id %d call async_hi ok, result %s", - client->get_client_id(), ret.value().data()); - } - CHECK(ret.value() == "async hi"s); - } - { - auto ret = call(client); - ELOGV(INFO, "client_id %d call hello", client->get_client_id()); - if (!ret) { - ELOGV(ERROR, "client_id %d call hello error %s", - client->get_client_id(), ret.error().msg.data()); - continue; - } - else { - ELOGV(INFO, "client_id %d call hello ok, result %s", - client->get_client_id(), ret.value().data()); - } - CHECK(ret.value() == "hello"s); - } - { - auto ret = call<&HelloService::hello>(client); - ELOGV(INFO, "client_id %d call HelloService::hello", - client->get_client_id()); - if (!ret) { - ELOGV(ERROR, "client_id %d call HelloService::hello error %s", - client->get_client_id(), ret.error().msg.data()); - continue; - } - else { - ELOGV(INFO, "client_id %d call HelloService::hello ok, result %s", - client->get_client_id(), ret.value().data()); - } - CHECK(ret.value() == "hello"s); - } - { -#ifdef __GNUC__ - auto ret = call<&ns_login::LoginService::login>(client, "foo"s, "bar"s); - if (!ret) { - ELOGV(WARN, "%d %s", client->get_client_id(), ret.error().msg.data()); - } - CHECK(ret.value() == true); -#endif - } - - break; - } - } - void test_client_send_bad_header() { - g_action = {}; - auto client = create_client(inject_action::client_send_bad_header); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_send_bad_magic_num() { - g_action = {}; - auto client = create_client(inject_action::client_send_bad_magic_num); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_send_header_length_is_0() { - g_action = {}; - auto client = create_client(inject_action::client_send_header_length_0); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_close_socket_after_send_header() { - g_action = {}; - auto client = - create_client(inject_action::client_close_socket_after_send_header); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_close_socket_after_send_partial_header() { - g_action = {}; - auto client = create_client( - inject_action::client_close_socket_after_send_partial_header); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - void test_client_close_socket_after_send_payload() { - g_action = {}; - auto client = - create_client(inject_action::client_close_socket_after_send_payload); - auto ret = call(client); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - } - - void test_heartbeat() { - // auto client = create_client(inject_action::nothing); - // ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - // auto ret = call(client); - // CHECK(ret.value() == "async hi"s); - - // std::this_thread::sleep_for(700ms); - - // ret = call(client); - // if (enable_heartbeat) { - // REQUIRE_MESSAGE( - // ret.error().code == coro_rpc::errc::io_error, - // std::to_string(client->get_client_id()).append(ret.error().msg)); - // } - // else { - // CHECK(ret.value() == "async hi"s); - // } - // ELOGV(INFO, "test heartbeat done"); - } - void test_call_function_with_long_response_time() { - auto client = create_client(inject_action::nothing); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client, 1); - CHECK(ret.value() == 1); - } - void test_call_with_large_buffer() { - auto client = create_client(inject_action::nothing); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - std::string arg; - arg.resize(2048); - auto ret = call(client, arg); - CHECK(ret.value() == arg); - } - - void test_connect_timeout() { - g_action = {}; - if (sync_client) { - return; - } - auto init_client = [this]() { - std::shared_ptr client; - if (use_outer_io_context) { - client = std::make_shared(&executor_); - } - else { - client = - std::make_shared(coro_io::get_global_executor()); - } -#ifdef YLT_ENABLE_SSL - if (use_ssl) { - bool ok = client->init_ssl("../openssl_files", "server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - } -#endif -#ifdef YLT_ENABLE_IBV - if (use_rdma) { - bool ok = client->init_ibv(); - REQUIRE_MESSAGE(ok == true, - "init ibv fail, please check ibverbs config"); - } -#endif - return client; - }; - auto client = init_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - coro_rpc::err_code ec; - auto client2 = init_client(); - ec = syncAwait(client2->connect("10.255.255.1", port_, 5ms)); - CHECK_MESSAGE(ec, - std::to_string(client->get_client_id()).append(ec.message())); - } - - template - void test_call_with_delay_func(Args... args) { - g_action = {}; - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client, std::forward(args)...); - CHECK(ret.has_value()); - } - - template - void test_call_with_delay_func_client_read_length_error(Args... args) { - g_action = {}; - auto client = this->create_client(); - ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, - client->get_client_id()); - g_action = inject_action::close_socket_after_read_header; - auto ret = this->template call(client, std::forward(args)...); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - }; - - template - void test_call_with_delay_func_client_read_body_error(Args... args) { - g_action = {}; - auto client = this->create_client(); - ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, - client->get_client_id()); - g_action = inject_action::close_socket_after_send_length; - auto ret = this->template call(client, std::forward(args)...); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - }; - - template - void test_call_with_delay_func_server_timeout(Args... args) { - g_action = {}; - auto client = this->create_client(); - ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, - client->get_client_id()); - auto ret = this->template call(client, std::forward(args)...); - REQUIRE(ret); - std::this_thread::sleep_for(700ms); - ret = this->call(client, std::forward(args)...); - REQUIRE(!ret); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - }; - asio::io_context io_context_; - coro_io::ExecutorWrapper<> executor_; - std::string port_; - std::thread thd_; - ns_login::LoginService login_service_; - HelloService hello_service_; - std::string conf_str_; -}; -#endif // CORO_RPC_SERVERTESTER_HPP +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CORO_RPC_SERVERTESTER_HPP +#define CORO_RPC_SERVERTESTER_HPP +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "doctest.h" +#include "inject_action.hpp" +#include "rpc_api.hpp" +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/coro_rpc/impl/errno.h" + +#ifdef _MSC_VER +#define CORO_RPC_FUNCTION_SIGNATURE __FUNCSIG__ +#else +#define CORO_RPC_FUNCTION_SIGNATURE __PRETTY_FUNCTION__ +#endif + +using namespace std::string_literals; +using namespace async_simple::coro; +using namespace coro_rpc; +using namespace std::chrono_literals; + +struct TesterConfig { + TesterConfig() = default; + TesterConfig(TesterConfig &c) { + enable_heartbeat = c.enable_heartbeat; + use_ssl = c.use_ssl; + use_rdma = c.use_rdma; + sync_client = c.sync_client; + use_outer_io_context = c.use_outer_io_context; + port = c.port; + conn_timeout_duration = c.conn_timeout_duration; + } + bool enable_heartbeat; + bool use_ssl; + bool use_rdma; + bool sync_client; + bool use_outer_io_context; + unsigned short port; + std::string address = "0.0.0.0"; + std::chrono::steady_clock::duration conn_timeout_duration = + std::chrono::seconds(0); + + friend std::ostream &operator<<(std::ostream &os, + const TesterConfig &config) { + os << std::boolalpha; + os << " enable_heartbeat: " << config.enable_heartbeat << ";" + << " use_ssl: " << config.use_ssl << ";" + << " use_rdma: " << config.use_rdma << ";" + << " sync_client: " << config.sync_client << ";" + << " use_outer_io_context: " << config.use_outer_io_context << ";" + << " port: " << config.port << ";" + << " address: " << config.address << ";"; + os << " conn_timeout_duration: "; + auto val = std::chrono::duration_cast( + config.conn_timeout_duration) + .count(); + os << val << "ms" + << ";"; + return os; + } +}; + +struct ServerTester : TesterConfig { + ServerTester(TesterConfig config) + : TesterConfig(config), + executor_(io_context_.get_executor()), + port_(std::to_string(config.port)) { + if (use_outer_io_context) { + std::promise promise; + auto future = promise.get_future(); + thd_ = std::thread([this, &promise]() { + asio::io_context::work work(io_context_); + promise.set_value(); + io_context_.run(); + }); + future.wait(); + } + + std::stringstream ss; + ss << config; + conf_str_ = ss.str(); + } + ~ServerTester() { + if (use_outer_io_context) { + io_context_.stop(); + thd_.join(); + } + g_action = coro_rpc::inject_action::nothing; + } + void run() { + ELOGV(INFO, "run test: heartbeat=%d, ssl=%d", enable_heartbeat, use_ssl); + register_all_function(); + test_all(); + } + virtual void test_all() { + test_connect_timeout(); + test_function_registered(); + if (sync_client) { + // only sync_call implement inject behavior + test_client_send_bad_header(); + test_client_send_bad_magic_num(); + test_client_send_header_length_is_0(); + test_client_close_socket_after_send_header(); + test_client_close_socket_after_send_partial_header(); + test_client_close_socket_after_send_payload(); + } + test_heartbeat(); + test_call_function_with_long_response_time(); + test_call_with_large_buffer(); + } + std::shared_ptr create_client( + inject_action action = inject_action::nothing) { + std::shared_ptr client; + // sometimes, connect will take more than conn_timeout_duration(500ms), so + // retry 3 times to make sure connect ok. + coro_rpc::errc ec; + int retry = 4; + for (int i = 0; i < retry; i++) { + if (use_outer_io_context) { + client = std::make_shared(&executor_); + } + else { + client = + std::make_shared(coro_io::get_global_executor()); + } +#ifdef YLT_ENABLE_SSL + if (use_ssl) { + bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + } +#endif +#ifdef YLT_ENABLE_IBV + if (use_rdma) { + bool ok = client->init_ibv(); + REQUIRE_MESSAGE(ok == true, + "init ibv fail, please check ibverbs config"); + } +#endif + g_action = action; + + if (sync_client) { + ec = client->sync_connect("127.0.0.1", port_); + } + else { + ec = syncAwait(client->connect("127.0.0.1", port_)); + } + + if (!ec) { + break; + } + + ELOGV(INFO, "retry times %d", i); + } + + REQUIRE_MESSAGE(!ec, std::to_string(client->get_client_id()) + .append(" not connected ") + .append(conf_str_)); + return client; + } + template + decltype(auto) call(std::shared_ptr client, Args &&...args) { + ELOGV(INFO, "%s client_id %d call %s", + sync_client ? "sync_client" : "async_client", client->get_client_id(), + coro_rpc::get_func_name().data()); + if (sync_client) { + return client->sync_call(std::forward(args)...); + } + else { + return syncAwait( + client->template call(std::forward(args)...)); + } + } + + virtual void register_all_function() {} + + virtual void remove_all_rpc_function() {} + + void test_function_registered() { + g_action = {}; + + // because heartbeat timeout is 500ms, sometimes the client closed because + // of timeout, so retry call. + int retry = 4; + for (int i = 0; i < retry; i++) { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + ELOGV(INFO, "retry call times %d", i); + { + auto ret = call(client); + ELOGV(INFO, "client_id %d call async_hi", client->get_client_id()); + if (!ret) { + ELOGV(ERROR, "client_id %d call async_hi error %s", + client->get_client_id(), ret.error().msg.data()); + continue; + } + else { + ELOGV(INFO, "client_id %d call async_hi ok, result %s", + client->get_client_id(), ret.value().data()); + } + CHECK(ret.value() == "async hi"s); + } + { + auto ret = call(client); + ELOGV(INFO, "client_id %d call hello", client->get_client_id()); + if (!ret) { + ELOGV(ERROR, "client_id %d call hello error %s", + client->get_client_id(), ret.error().msg.data()); + continue; + } + else { + ELOGV(INFO, "client_id %d call hello ok, result %s", + client->get_client_id(), ret.value().data()); + } + CHECK(ret.value() == "hello"s); + } + { + auto ret = call<&HelloService::hello>(client); + ELOGV(INFO, "client_id %d call HelloService::hello", + client->get_client_id()); + if (!ret) { + ELOGV(ERROR, "client_id %d call HelloService::hello error %s", + client->get_client_id(), ret.error().msg.data()); + continue; + } + else { + ELOGV(INFO, "client_id %d call HelloService::hello ok, result %s", + client->get_client_id(), ret.value().data()); + } + CHECK(ret.value() == "hello"s); + } + { +#ifdef __GNUC__ + auto ret = call<&ns_login::LoginService::login>(client, "foo"s, "bar"s); + if (!ret) { + ELOGV(WARN, "%d %s", client->get_client_id(), ret.error().msg.data()); + } + CHECK(ret.value() == true); +#endif + } + + break; + } + } + void test_client_send_bad_header() { + g_action = {}; + auto client = create_client(inject_action::client_send_bad_header); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_send_bad_magic_num() { + g_action = {}; + auto client = create_client(inject_action::client_send_bad_magic_num); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_send_header_length_is_0() { + g_action = {}; + auto client = create_client(inject_action::client_send_header_length_0); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_close_socket_after_send_header() { + g_action = {}; + auto client = + create_client(inject_action::client_close_socket_after_send_header); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_close_socket_after_send_partial_header() { + g_action = {}; + auto client = create_client( + inject_action::client_close_socket_after_send_partial_header); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + void test_client_close_socket_after_send_payload() { + g_action = {}; + auto client = + create_client(inject_action::client_close_socket_after_send_payload); + auto ret = call(client); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + } + + void test_heartbeat() { + // auto client = create_client(inject_action::nothing); + // ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + // auto ret = call(client); + // CHECK(ret.value() == "async hi"s); + + // std::this_thread::sleep_for(700ms); + + // ret = call(client); + // if (enable_heartbeat) { + // REQUIRE_MESSAGE( + // ret.error().code == coro_rpc::errc::io_error, + // std::to_string(client->get_client_id()).append(ret.error().msg)); + // } + // else { + // CHECK(ret.value() == "async hi"s); + // } + // ELOGV(INFO, "test heartbeat done"); + } + void test_call_function_with_long_response_time() { + auto client = create_client(inject_action::nothing); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client, 1); + CHECK(ret.value() == 1); + } + void test_call_with_large_buffer() { + auto client = create_client(inject_action::nothing); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + std::string arg; + arg.resize(2048); + auto ret = call(client, arg); + CHECK(ret.value() == arg); + } + + void test_connect_timeout() { + g_action = {}; + if (sync_client) { + return; + } + auto init_client = [this]() { + std::shared_ptr client; + if (use_outer_io_context) { + client = std::make_shared(&executor_); + } + else { + client = + std::make_shared(coro_io::get_global_executor()); + } +#ifdef YLT_ENABLE_SSL + if (use_ssl) { + bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + } +#endif +#ifdef YLT_ENABLE_IBV + if (use_rdma) { + bool ok = client->init_ibv(); + REQUIRE_MESSAGE(ok == true, + "init ibv fail, please check ibverbs config"); + } +#endif + return client; + }; + auto client = init_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + coro_rpc::err_code ec; + auto client2 = init_client(); + ec = syncAwait(client2->connect("10.255.255.1", port_, 5ms)); + CHECK_MESSAGE(ec, + std::to_string(client->get_client_id()).append(ec.message())); + } + + template + void test_call_with_delay_func(Args... args) { + g_action = {}; + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client, std::forward(args)...); + CHECK(ret.has_value()); + } + + template + void test_call_with_delay_func_client_read_length_error(Args... args) { + g_action = {}; + auto client = this->create_client(); + ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, + client->get_client_id()); + g_action = inject_action::close_socket_after_read_header; + auto ret = this->template call(client, std::forward(args)...); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + }; + + template + void test_call_with_delay_func_client_read_body_error(Args... args) { + g_action = {}; + auto client = this->create_client(); + ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, + client->get_client_id()); + g_action = inject_action::close_socket_after_send_length; + auto ret = this->template call(client, std::forward(args)...); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + }; + + template + void test_call_with_delay_func_server_timeout(Args... args) { + g_action = {}; + auto client = this->create_client(); + ELOGV(INFO, "run %s, client_id %d", CORO_RPC_FUNCTION_SIGNATURE, + client->get_client_id()); + auto ret = this->template call(client, std::forward(args)...); + REQUIRE(ret); + std::this_thread::sleep_for(700ms); + ret = this->call(client, std::forward(args)...); + REQUIRE(!ret); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + }; + asio::io_context io_context_; + coro_io::ExecutorWrapper<> executor_; + std::string port_; + std::thread thd_; + ns_login::LoginService login_service_; + HelloService hello_service_; + std::string conf_str_; +}; +#endif // CORO_RPC_SERVERTESTER_HPP diff --git a/src/coro_rpc/tests/openssl_files/ca.crt b/src/coro_rpc/tests/openssl_files/ca.crt new file mode 100644 index 000000000..4ba4063f5 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/ca.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF +XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv +oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg +zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 +VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO +3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k +XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw +4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 +n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ +qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH +lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 +l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp +zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.crt b/src/coro_rpc/tests/openssl_files/client.crt new file mode 100644 index 000000000..9d8da4ea5 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/client.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG +Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt +5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ +opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 +/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et +jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI +G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 +pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs +Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF +Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA +faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW +8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq +q8rTyD5f5g== +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.key b/src/coro_rpc/tests/openssl_files/client.key new file mode 100644 index 000000000..a57cacb89 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o +z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E +V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E +8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW +pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj +25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf +NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt +ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t +ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B +T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH +ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv +E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai +zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi +cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D +ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V +qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY +yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD +J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF +OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 +72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE +Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK +lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 +oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB +Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh +BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D +za21e2VPgHOBFlAxpuA= +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake.crt b/src/coro_rpc/tests/openssl_files/fake.crt new file mode 100644 index 000000000..b3711a9c8 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/fake.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDsTCCApmgAwIBAgIUE5kTRNz1GA3NOOBDwwqIAUz7ZJQwDQYJKoZIhvcNAQEL +BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYD +VQQDDApGYWtlQ2xpZW50MB4XDTI2MDQwOTAyNTMxNVoXDTM2MDQwNjAyNTMxNVow +azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWpp +bmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYDVQQD +DApGYWtlQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqylI +L2ovAcolSOUEGb55Tf2SbFczUqPB0XPF9x4UXFKcADcdhR+LDKLd92k000gwX8ft +s8vtAlOdtPj2x1mbelFyMC66I6BAUGjcwgBsVXhXYM7i8g5C5jhX21FNSy468urX +14Vv2CwRhCuWiukGsnZREQLPBHtwC6WHSSn7oTYdfTCRE9+FwLOJRaXbejCBjZbR +36dIMHFOvvi1w6QiS47v3u/neJ86QftsSRRTiRO9TAFQflrSgeY4sh8QjhPejVck +/AfsHx4CS4yAObSDl2B5NgXd2WikuniCGgJtgayuCHCfs1MP6QJ7Q3DTu29NLQgc +SKutn0291sLAm2M0OQIDAQABo00wSzAdBgNVHQ4EFgQU3M3ZPZ9s5gvE/FspkWQb +ig1OvSQwHwYDVR0jBBgwFoAU3M3ZPZ9s5gvE/FspkWQbig1OvSQwCQYDVR0TBAIw +ADANBgkqhkiG9w0BAQsFAAOCAQEAfp+JcImg7LQaBoL5ikKHVCWykjqBdU/0yXfK +lHVIRbDCZO1c/0hNfEU9aN7MXaPHxMtZOXX9d5i5Xt3PzTB1Q3Keb7QWYEK4lfs7 +H9IsHaMiax6UJM5Hb5xXCt8vCXRLcseD/in7Vm0+lqQ7Wp1UnwrN7NZ4PaiXTTmw +TMj4l9ElKtOG0vRr7loEsidE8Sc0X4H92Rs6gudh+Wtfb0O1aw0eRcpvUuO1NZLe +jpMxJMfDwyFh0URatAg8ZEPNV8HkgrEog2LltNVKSMMqi/DK/98wanSSUFFSVY8h +aXIlbCXp5F5Z1IqS+Lt11DEf0Q6N1la8Z1mad+skE7/HTqG36w== +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake.key b/src/coro_rpc/tests/openssl_files/fake.key new file mode 100644 index 000000000..d1bdafff5 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/fake.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrKUgvai8ByiVI +5QQZvnlN/ZJsVzNSo8HRc8X3HhRcUpwANx2FH4sMot33aTTTSDBfx+2zy+0CU520 ++PbHWZt6UXIwLrojoEBQaNzCAGxVeFdgzuLyDkLmOFfbUU1LLjry6tfXhW/YLBGE +K5aK6QaydlERAs8Ee3ALpYdJKfuhNh19MJET34XAs4lFpdt6MIGNltHfp0gwcU6+ ++LXDpCJLju/e7+d4nzpB+2xJFFOJE71MAVB+WtKB5jiyHxCOE96NVyT8B+wfHgJL +jIA5tIOXYHk2Bd3ZaKS6eIIaAm2BrK4IcJ+zUw/pAntDcNO7b00tCBxIq62fTb3W +wsCbYzQ5AgMBAAECggEAChvFihtUzF/CZPQ1kkmoA12i7KgXvV0zgKm8OtR5clxk +nzSiFy8eOLBTuJ1rg8DjLnzxwmkcRcNncH01od9eadJukn7n+luoALe4tfJtc3zI +eEyvpYkHFW6lbav3CyYfUCJjffSA/vzXSf2DBhAuF5MaRY1raYHaw61SeJU0qbgd +/H9yWYR49zB4csnTxAllJpKbIc1d2IargDgiv7GnzcA0dqROxcaXPU1cruuOag9f +iCd9awvMwy9XDqryEKyBGorN3WgT5TjMeFj8IBMLosOB15/Ruhuk/QNfijPPDS/R +KC7dX6BCHSu7cHHDjA43ikUZVerhOcq/P8ogbtOKiQKBgQDw51QTHz9BBCW5+7j6 +xvhYuosAuz2W+JJmMZlhy8SWHr4PIaQIG9E8CchssWiWSKPgDyHVfg2yTXP8Q8R+ +VT/AWAgeB3wrdMN6JNbNRPtkIGTtVgVDo7padBNbbPAN83+W3CdSF2RmJuAOInhb +Ez4Ctzq10kEyS1eTOT6yc9ujTQKBgQC14x5pdEHrRQpxpgq8qXxnqSlm030xtHJm +2z6oAxcsLVgwWcTRTU22ABJTV3939gJLhtFJ0p3DxfvCy/GOcawmeQsjpYs8j7gL +AIyYEMnxJTjzm+YwUn5uNl1xLeRa7P+YerIIwbC0tZxiNCvUo3IrTvfbIoBddCyZ +GhnqtK1GnQKBgCkIjiaPrPuLFE4AlXqJx6V9aM3gFtaPUoh7rE+fIMYdSGxVY5ZJ +/rLGS9BPy6vFhbxVd4Lg7L5ROQ9gD6khJjHCDOfoiHrycZVtjvT56gQdDHPssgra +aZScrutku+L0deghacUu3NgViRZ/QpboySg3Q5XS0W4arTkTiB1nZKMFAoGAWJBR +U5nHKy6/6hymZ7zDFZp5zVa3RAeQGOMyfA6dLuaZZVmgiyVv7GnWgnw9VgUUkv// +UknaheQWNYCmiuxwnX8c3GuUA5YbUEghLT4nhmLQe1Xy3J6ebz3Le/uTkG6L+gvs +OnVNfIBduDedC/nV8p6N80a2aErUGGxsKCt3n8UCgYEAt1hTr1a6HQheUv7RVC9i +KJu/k8n2olGUzE1pMZX6NlTgFKnmAQ7dLt6xeDBfMu0WBxbZktaSGM1T2Y0Tir+1 ++R/qH2inBE6FnL5rO8rb7ZiaDZy69Ntw4hJZGvryFcp6ZGraeUDqJO+e59xlQzmx +4/mvqmhlMJAYg7KJPfyH5tg= +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake_server.crt b/src/coro_rpc/tests/openssl_files/fake_server.crt deleted file mode 100644 index b56981813..000000000 --- a/src/coro_rpc/tests/openssl_files/fake_server.crt +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -AIICIzCCAYwCCQDHoPajMKX7WDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIwODAzMDYwNjM3WhcNMzIw -NzMxMDYwNjM3WjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 -MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv -c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOpVxgJHmtZ88gja1MQm22O -ghYUT7T5X/85sJ0BenfghclyQ8KtxEDPGq4BSbheb+94C6x7YIY3GQ82y9R7nrcx -bcD3sGGRBn13ROyjz9HRUMTei2Uwd2yfIVY3Y0MDb0WMmjGnNTgj1h1YAYSWouC/ -4p/IitnVwmG+ZhwCxafhAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAuqmsA/xLqRW1 -9INE36S1T3xi56bIYdlYS2JRzXNrIqdb1RnNPHyFU0Tizv6dkUDec5VfunR5ZgAq -Hxsr5cowB1cdWi21FjjNGXrd1/nYHTdnxHfMGKFxjHXc+s5+qyFnmQatHJO8hyCR -ljmD56V/y1nMcFna0Gtear44Rh5s6fY= ------END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake_server.key b/src/coro_rpc/tests/openssl_files/fake_server.key deleted file mode 100644 index c55263b04..000000000 --- a/src/coro_rpc/tests/openssl_files/fake_server.key +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,D5B5BF16335844E5 - -X2Qj15pkVsN24Qu3N+sORjm0eDD+I2lrEfEI6QmMg33PV0udkNDEMtchJJ12Uzl/ -Czsj2ydFLHbFCUGGDqx7CfzYNBQhJvw74nrakXDv8uRdygvc0bEym35Nkukx5XNV -FL/jnTChO5ZneHHYYgtMDzsWN7+0oKTHyizN43nGNbBJJEy3hFje4k4ATFrCUXZt -z4OVge2M5JATR2ddFGX62ZrxFmg+PsbvSnfpxZesRXHtu0NPCK/Xv38vGYSaJ0s0 -XlK7A6Ko4O6RfBZXSXCO09GD27HA0zveQfLW2ILhbl4GZrAFd1d7d1Jdltxlvbw6 -cUp8dd3f6hAb5mAk1io4Xey++CEiwkpBgis9qAkFqxPPzwq+cB6LTu2KfzffvAze -FOZ7Yq5JyDLt7frpJ+PTFfv3bNyXdabBt6E9ZdEhIbvf/ArA+JuV4mJ8ilvsurnq -h54Z+R19V8kXUEekKDSYl2WsMrTY7o8ZY0LgiIbTG+LFqGjUpyclx6luz9r2d3yr -/ngKqbG30jct0Y7mDuVz8Vgml9wDqv4JmC/iUxZyGLuZlSRWFqV6RRCzGncMqy+w -wZb5XNGK2CwKCwmTtWuJm3rS88yo2MmJPtkbPeG9JRvUaAsg1uaExA5RRIk2zyIA -mwp0ItT4S63g6iObFadd7l2cDBdIJNMwJ/80FNr+HknBnkC3OYsIimrscoUS5vBc -V+a8C6wJ6A4IDrNXLP5so2PuygE47CGb6ehlzpda9Eyp030JLooeKt7sqWpX7ENA -JmSDScpG6xe5SR0buKVlP4Qf9IqpB6K8CnHsJWiJI40= ------END RSA PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat new file mode 100644 index 000000000..c2c837966 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat @@ -0,0 +1,22 @@ +@echo off +REM Generate test certificates for mutual SSL authentication + +REM Generate CA certificate +openssl genrsa -out ca.key 2048 +openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj /C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestCA + +REM Generate server certificate +openssl genrsa -out server.key 2048 +openssl req -new -key server.key -out server.csr -subj /C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=127.0.0.1 +openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt + +REM Generate client certificate +openssl genrsa -out client.key 2048 +openssl req -new -key client.key -out client.csr -subj /C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestClient +openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt + +REM Generate DH parameters +openssl dhparam -out dhparam.pem 1024 +openssl dhparam -out dh512.pem 512 + +echo Certificates generated successfully! diff --git a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh new file mode 100644 index 000000000..bcef1072a --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh @@ -0,0 +1,30 @@ +#!/ bin / bash +#Generate test certificates for mutual SSL authentication + +#Generate CA certificate +openssl genrsa - out ca.key 2048 openssl req - new - x509 - days 3650 - + key ca.key - out ca.crt - + subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestCA" + +#Generate server certificate + openssl genrsa - + out server.key 2048 openssl req - new - key server.key - out server.csr - + subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=127.0.0.1" openssl x509 - + req - days 3650 - in server.csr - CA ca.crt - CAkey ca.key - + CAcreateserial - + out server.crt + +#Generate client certificate + openssl genrsa - + out client.key 2048 openssl req - new - key client.key - out client.csr - + subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestClient" openssl x509 - + req - days 3650 - in client.csr - CA ca.crt - CAkey ca.key - + CAcreateserial - + out client.crt + +#Generate DH parameters + openssl dhparam - + out dhparam.pem 1024 openssl dhparam - + out dh512.pem 512 + + echo "Certificates generated successfully!" diff --git a/src/coro_rpc/tests/openssl_files/server.crt b/src/coro_rpc/tests/openssl_files/server.crt index 12c774ba3..b89ecbd56 100644 --- a/src/coro_rpc/tests/openssl_files/server.crt +++ b/src/coro_rpc/tests/openssl_files/server.crt @@ -1,19 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx -MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 -MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT -nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw -PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz -WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 -2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo -+0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN -06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 -CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ -kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a -VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu -LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ -rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= +MIIDMjCCAhoCFCfqzDMJkItWsGmNb2dnTuQ7YiDfMA0GCSqGSIb3DQEBCwUAMFQx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy +OTQ5WhcNMzYwNDA1MDIyOTQ5WjBXMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTESMBAGA1UE +AwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEcB +wNSFuRb9/WLdS8BczaycllmOhwfMFqx/2Qd6XVEpOBmJsiDSQSHT1dpwMMZ7xCo5 +17QefyokWmBC+de83FY2EA+KWPreD+nZF1srDOOx6z+D08NN/7Go7eobRnUSBiSF +8BHk8J28I8Mm/h/0mngSQi7tUsnxKJ67yyHN1VTu/oZ24xeN/FSuTPqmLc4iB0/s +NOIpSjlgesP5tgAx4QW3++Z0NQjZamHYASvBmDBzlFTj2HzUcVEVWUotqX6f/l5d +JLseVnEU3bUGrtEyZMXrfz1aVT4vAlgtQ9J31tDAkzZq3Zi3PlLD7Jg2ns+oojS3 +Gfpi6DmNJw8u0FumiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCAGgMOsNeoj0pW +jszmz/IeEPWpHkzT88f4n3D/o5mkVvagEgP9QF+WpnjpdyYW7pruOJXdSJQzYG+C +dbY1h22qw9pqFu3AwCBD1sqPl/5DqwqeyAFvw/d9/nTyCo2VK6Yvpbc6H7wggMFo +8xzK39mJE5kJsl42PJ3meFt4Loxz0FHL4Le0cVp30XnjOoiOroZNHoMk32XS7lGu +2bmWMjiAJSdUP/Btm1xoYUxPUYdqz4MzyYZQCjc0izkLxmbkO41vX/ZkoYlDwCcb +E7XXeqn2kmLkB6aD8d6UJnFtxwuos6YhVBfKeySN4fWXKNPZweRKOcUOaL7GGDWU +m1HkKMpu -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/server.key b/src/coro_rpc/tests/openssl_files/server.key index 0edde26de..42c3a1f23 100644 --- a/src/coro_rpc/tests/openssl_files/server.key +++ b/src/coro_rpc/tests/openssl_files/server.key @@ -1,30 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,D920B8941C56ADDC - -I2lW3QsAG/xubjtXpXh3wQ5Ru3VZiMkPNjc+G6/2JjjVr1sD+fzCWvvwdqdxGuNJ -gKdpPBHLuQfTTzGETE4NKDkYzmiPTVbZPJ77DyfL2cK1dcZtAY46RsHf+VMI5N8l -Be1jQSB5xvUa88dSIeowPTc2XSnTIoSFWCa38XuqYF7i0a3lv96eAyXpqB7Tm2r8 -SoYlm0n7/uzRpk6HWST65qnVv/j+37LuvSy6ehyh44+KDS4x9FUOZc5xwJ/37Jnl -SDC10+9zLc+jOTk6XgUuBSmG+xfZdcOrbknQ1Xj1YtseYH0plYAEWi4PsnMQkHzC -GGvK08Lgqxd7cGEKFh2MRZ/TEwriN5ud5HGm4yIHIj45rbedtRSQwl2EyHdWeW0J -rFltDy+SXnnkJaOcnBYXUD1jEwyy2lLamWRiu83VFbCv6yhOYuR6JejM6dctjgZ+ -Qf0PzH6L1bVpHKEl/GLByJ6GWYrQJqw83LAXlR+NNCC3nN7WAAaTuzA9LpgW9Vk0 -khRRs7rJGxwwwE4TfG9FbQxwuOsjKV9pRohB1x1nFMMm5IJ9SON2KjizsVdLbt7t -Gb/5M7RcSnnGvIWWXalXpFGKgciwYd8F1v0TJ+FMooZxgUp7Pmp5YKIHkBjMrnnW -rKuoxmA5oPgSNUtr4ddMJ1sTIQPhqI27+CrySTzWKH1ls45okBvsiCejpcJwfrZW -KLSkz/FsPoWm44uomBSDOikry8axrKQLB9tOVPKCx/z0VP060P9N81mu4h67bixr -xu+odIONqGhRZT/BYHL2NjDfWlFmTJQy8Drn1a7IEhp8FV7l2aY/hisrMN7MQVza -FGB0hMbVHGeFOCD9QNQwRU2wLtwpE7LT/lGNmKadQadXxeAqOWBckXrpwnrxZDEP -a8AYr2J55h/IE4Oi2DyibSEZdB+7334OJHMmr14q53eIpeit19BYVhWyu9AtORJp -As61C7s82AO+E5gOswsq05jwWV/GIIkgZ8/vswEffiihmDEf6AUZsVGW3BlpFlyU -i3g4e8HFTJ+s9Z3sTgZ1EWOP6Wd2OzyQYVA4ggBR/g/IC9s5em1wvAkVwIZaPvj7 -21BIQXyiGrw52T+vTUrAUG0l7yoHGCgVYJ+aEm+f103AiBYuReUbo39GEIY2GHLu -r3oUehtt4of0ootmPCmjrRUyY6LPeD+d+i1jJUSYFKezsVRpaiF5+J8YLGMcOPiI -8qRRNgXDMMvttwyhoxyr5+667OMv+XWr2VQj7i9MWCFwTMwNzdUoZI3PWDhXbXDO -lQJS6v3iAPw+KvLJywODe+C4shUqYdrRdUSKE0FfuB8Ajzh86+FmjJcZM+BSxM4J -hC2yjv114jDlsgjFSxQE2K1iotLUY9mfmW8QWVMO3L4LlNpr4ypNLYX0Ph2wgqzQ -kszXTFN11RFKFLUhF0Mi5m4ffMLPD5YyoqO9grpyC1Nt7vxaPPvcvPD86jK3ksqJ -MwucZGgm9HtUuAjGOSljUr0d+d+4pySJbcpH2YDIBHGVsCScYPVg8XZ1CYko3mq/ -d6jDUgydraEmQvIPiKMpTE18rW+jierv2FlB8AGcwxm2VWxuM25wQ40J2YuZLY7k ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIRwHA1IW5Fv39 +Yt1LwFzNrJyWWY6HB8wWrH/ZB3pdUSk4GYmyINJBIdPV2nAwxnvEKjnXtB5/KiRa +YEL517zcVjYQD4pY+t4P6dkXWysM47HrP4PTw03/sajt6htGdRIGJIXwEeTwnbwj +wyb+H/SaeBJCLu1SyfEonrvLIc3VVO7+hnbjF438VK5M+qYtziIHT+w04ilKOWB6 +w/m2ADHhBbf75nQ1CNlqYdgBK8GYMHOUVOPYfNRxURVZSi2pfp/+Xl0kux5WcRTd +tQau0TJkxet/PVpVPi8CWC1D0nfW0MCTNmrdmLc+UsPsmDaez6iiNLcZ+mLoOY0n +Dy7QW6aJAgMBAAECggEAFBYzEdD+3HJ25Ov+f/N6G1K9ncK7rcVVbcy9QdojJqrW +NH8zNT9fdxLaeS9gYzP1A9asWHsDOAPVA492CDLgCUVIRNIaMRRwIy50DIijR7aq +iIqlQR7sesGpzLrXI3joZt9Q46QXzx4y2W9gQMqZsMhsJGEqgkwouMc61IO+bi/a +II1Uf1EGijFd96Qk/bT76puakg6qjRoJ+SdjNJOM/cOd8Jon6qnvXB6jOtW+DqIs +539RvibVK4pPr8OIxzGSTXPqcEcdaKV0NiWbTWonuOZu4Nf49auK3awjpQPzzXPT +BPHMKSSAtFXUhjaHIaHOAyWnQ8NM/kgrOpvSxMxkhQKBgQDkQIFmMzw+LsjnzAeg +IrA6ANzfPQJzhkGQRuv57Cv6CWVQXpatixgvFpd8CHviIwVD90hNQH+PMF1fM1pf +aYEOkRdWhFjSSIEkO2VRkcS8B0unexNeBbTupQfbvVx8etu8N+SEwevhWv0rNs7R +lF5JkZx+g1iT/6hTpES14i/6PwKBgQDgn+VSP7USOAgCwTU1kJcCBNUS5sROcpWV +5BrRjzkRvVo7wFxCBEOzKoukAV+RZJFwvvam0PxSwP3eLtjYOyW+BT2uywJ10K28 +OgSPkUnMg6PKw0ilCz38CvCw0rI9g8aS1akFCNZ53LFIognWwqpnZsJEm1yR0Qlv +Tj/ERNxdNwKBgBLHe938uSgkkUMA9l+mevlKuOFlE56NnTdRnnihhby8qSlDnwII +P6UgJrZ9vDOOzhAZeEli1RvizsvWXckb1RJtvY3Qtb4XWQiyGlPrulP+Batx5NYH +gitgSJU7rzBOq2WA87w4eD/CTLIRgFKd8mP7JvUBuXfzwNWg3kZYpbnhAoGAEzNX +xNoRPkdv19xwEe4UGmYTWJRFP3dn9fIToMofVLbc2bKtsC7xIoWGfjRn2OPB0uNf +7g57Iw/AI5fZjVIw/bcw+Jn90dhOoYJMFYGTz1mJTLG4qfL2D29X96Vq+vsipDaD +RhzlSHFm7hB7ytHFAyWzgW3OUeCOb+c+aCaCt60CgYBZaensYV8gEd/z06DZ5SQC +cPdRpC7TOCJJyi9Lr3VJDUZJaF4gfnOjzNKQqzh3Jy/vrtDkYJArtIKbISK6Kb2q ++pXpItKPuYFNeIRoieZXy9hSJhaw88fcDfMVODG7Fx0h2LcIFmoNXxNFEayUjkLt +sVgJ1M3OhYqqfZ+8d4QoZg== +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/test_coro_rpc_client.cpp b/src/coro_rpc/tests/test_coro_rpc_client.cpp index 7c0102a79..40ffda0c3 100644 --- a/src/coro_rpc/tests/test_coro_rpc_client.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_client.cpp @@ -1,848 +1,848 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "doctest.h" -#include "rpc_api.hpp" -#include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/coro_rpc/impl/coro_rpc_client.hpp" -#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" -#include "ylt/coro_rpc/impl/errno.h" -using namespace coro_rpc; -using namespace std::chrono_literals; -using namespace std::string_literals; -using namespace async_simple::coro; -using asio::ip::tcp; - -template -class ClientTester { - ClientTester(unsigned short port) : port_(std::to_string(port)) {} - std::string port_; -}; - -static unsigned short coro_rpc_server_port = 8803; -Lazy> create_client( - coro_io::ExecutorWrapper<>* executor, std::string port) { - auto client = std::make_shared(executor); -#ifdef YLT_ENABLE_SSL - bool ok = client->init_ssl("../openssl_files", "server.crt"); - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); -#endif - auto ec = co_await client->connect("127.0.0.1", port); - REQUIRE(!ec); - co_return client; -} - -void show(auto& s) { return; } - -TEST_CASE("testing client") { - { - coro_rpc::coro_rpc_client client; - auto lazy_ret = client.connect("", ""); - auto ret = syncAwait(lazy_ret); - CHECK(ret == coro_rpc::errc::not_connected); - } - { - coro_rpc::coro_rpc_client client; - auto ret = client.sync_connect("", ""); - CHECK(ret == coro_rpc::errc::not_connected); - } - g_action = {}; - std::string port = std::to_string(coro_rpc_server_port); - asio::io_context io_context; - coro_io::ExecutorWrapper<> executor = io_context.get_executor(); - auto executor_ptr = &executor; - std::promise promise; - auto worker = std::make_unique(io_context); - auto future = promise.get_future(); - std::thread thd([&io_context, &promise] { - promise.set_value(); - io_context.run(); - }); - - future.wait(); - coro_rpc_server server(2, coro_rpc_server_port); -#ifdef YLT_ENABLE_SSL - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); -#endif - auto res = server.async_start(); - - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - SUBCASE("call rpc, function not registered") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, - ret.error().msg); - co_return; - }; - syncAwait(f()); - } - SUBCASE("call rpc, function not registered 2") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, - ret.error().msg); - co_return; - }; - syncAwait(f()); - } - - server.stop(); - - coro_rpc_server server2(2, coro_rpc_server_port); -#ifdef YLT_ENABLE_SSL - server2.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); -#endif - server2.register_handler(); - server2.register_handler(); - server2.register_handler(); - res = server2.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - SUBCASE("call rpc timeout") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call_for(10ms); - CHECK_MESSAGE(ret.error().code == coro_rpc::errc::timed_out, - ret.error().msg); - co_return; - }; - syncAwait(f()); - } - - SUBCASE("call rpc success") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK(ret.value() == std::string("hello")); - ret = co_await client->call_for(100ms); - CHECK(ret.value() == std::string("hello")); - co_return; - }; - syncAwait(f()); - } - - SUBCASE("call with large buffer") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - std::string arg; - arg.resize(2048); - auto ret = co_await client->template call(arg); - show(ret); - CHECK(ret.value() == arg); - co_return; - }; - syncAwait(f()); - } - - server.stop(); - worker = nullptr; - thd.join(); -} - -std::string get_first_local_ip() { - using asio::ip::tcp; - try { - tcp::resolver resolver(coro_io::get_global_executor()->get_asio_executor()); - tcp::resolver::query query(asio::ip::host_name(), ""); - tcp::resolver::iterator iter = resolver.resolve(query); - tcp::resolver::iterator end; // End marker. - while (iter != end) { - tcp::endpoint ep = *iter++; - auto addr = ep.address(); - if (addr.is_v4()) { - return addr.to_string(); - } - } - } catch (std::exception& e) { - ELOG_WARN << "get_first_local_ip error: " << e.what(); - } - - return "localhost"; -} - -void foo(const size_t& i) {} - -TEST_CASE("testing const & args") { - coro_rpc_server server(1, 8901); - server.register_handler(); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client{}; - syncAwait(client.connect("127.0.0.1:8901")); - const int& i = 0; - auto ret = syncAwait(client.call(i)); - CHECK(ret.has_value()); -} - -TEST_CASE("testing client with local ip") { - coro_rpc_server server(1, 8901); - server.register_handler(); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - std::string local_ip = get_first_local_ip(); - ELOG_INFO << "local ip: " << local_ip; - -// Due to windows strong host mode, we cant connect to localhost from specify -// nic ip address -#ifndef _WIN32 - std::string target_ip = "127.0.0.1"; -#else - std::string target_ip = local_ip; -#endif - coro_rpc_client client(local_ip); - auto ec = client.sync_connect(target_ip, "8901"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - coro_rpc_server server1(1, 8902, local_ip); - server1.register_handler(); - res = server1.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - ec = client.sync_connect(local_ip, "8902"); - REQUIRE_MESSAGE(!ec, ec.message()); - - ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - std::vector eps; - eps.push_back(asio::ip::tcp::endpoint( - asio::ip::address::from_string("192.0.2.1"), 8901)); - eps.push_back(asio::ip::tcp::endpoint( - asio::ip::address::from_string("127.0.0.1"), 8901)); - - auto local_ep = - asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 0); - asio::ip::tcp::socket socket(client.get_executor().get_asio_executor()); - socket.open(local_ep.protocol()); - socket.bind(local_ep); - - auto ec1 = async_simple::coro::syncAwait(coro_io::async_connect(socket, eps)); - CHECK(!ec1); - CHECK(socket.local_endpoint().address().to_string() == "127.0.0.1"); -} - -TEST_CASE("testing client with inject server") { - g_action = {}; - std::string port = std::to_string(coro_rpc_server_port); - ELOGV(INFO, "inject server port: %d", port.data()); - asio::io_context io_context; - coro_io::ExecutorWrapper<> executor = io_context.get_executor(); - auto executor_ptr = &executor; - auto worker = std::make_unique(io_context); - std::thread thd([&io_context] { - io_context.run(); - }); - coro_rpc_server server(2, coro_rpc_server_port); -#ifdef YLT_ENABLE_SSL - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); -#endif - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - SUBCASE("server run ok") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - auto ret = co_await client->template call(); - CHECK(ret.value() == std::string("hello")); - co_return; - }; - syncAwait(f()); - } - - SUBCASE("client read length error") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - g_action = inject_action::close_socket_after_read_header; - auto ret = co_await client->template call(); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - }; - syncAwait(f()); - } - SUBCASE("client read body error") { - g_action = {}; - auto f = [executor_ptr, &port]() -> Lazy { - auto client = co_await create_client(executor_ptr, port); - g_action = inject_action::close_socket_after_send_length; - auto ret = co_await client->template call(); - show(ret); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - }; - syncAwait(f()); - } - - server.stop(); - worker = nullptr; - thd.join(); - g_action = inject_action::nothing; -} -#ifdef YLT_ENABLE_SSL - -enum class ssl_type { _, fake, no }; - -template -class SSLClientTester { - public: - SSLClientTester(std::string base_path, unsigned short port, - ssl_type client_crt, ssl_type server_crt, ssl_type server_key, - ssl_type dh) - : port_(std::to_string(port)), - server(2, port), - base_path(base_path), - client_crt(client_crt), - server_crt(server_crt), - server_key(server_key), - dh(dh), - executor_(io_context.get_executor()) { - inject("client crt", client_crt_path, client_crt); - inject("server crt", server_crt_path, server_crt); - inject("server key", server_key_path, server_key); - inject("dh", dh_path, dh); - ssl_configure config{base_path, server_crt_path, server_key_path, dh_path}; - server.init_ssl(config); - server.template register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - - std::promise promise; - auto future = promise.get_future(); - worker = std::make_unique(io_context); - thd = std::thread([this, &promise] { - promise.set_value(); - io_context.run(); - }); - future.wait(); - } - ~SSLClientTester() { - worker = nullptr; - thd.join(); - } - void inject(std::string msg, std::string& path, ssl_type type) { - switch (type) { - case ssl_type::fake: { - path = "fake_" + path; - break; - } - case ssl_type::no: { - path = "no_" + path; - break; - } - default: { - } - } - ELOGV(INFO, "%s %s", msg.data(), path.data()); - } - void run() { - auto client = std::make_shared(&executor_); - bool ok = client->init_ssl(base_path, client_crt_path); - if (client_crt == ssl_type::fake || client_crt == ssl_type::no) { - REQUIRE(ok == false); - auto ec = syncAwait(client->connect("127.0.0.1", port_)); - REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); - auto ret = syncAwait(client->template call()); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::not_connected, - ret.error().msg); - } - else { - REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); - auto f = [this, &client]() -> Lazy { - auto ec = co_await client->connect("127.0.0.1", port_); - if (server_crt == ssl_type::_ && server_key == ssl_type::_) { - if (ec) { - ELOGV(INFO, "%s", gen_err().data()); - } - REQUIRE_MESSAGE(!ec, ec.message()); - auto ret = co_await client->template call(); - CHECK(ret.has_value()); - } - else { - REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); - } - }; - syncAwait(f()); - } - } - - std::string gen_err() { - auto to_string = [](ssl_type t) -> std::string { - switch (t) { - case ssl_type::fake: { - return "fake"s; - } - case ssl_type::no: { - return "no"s; - } - default: { - return "_"s; - } - } - }; - std::stringstream buf; - buf << "\n"; - buf << "client crt: " << client_crt_path << " __ " << to_string(client_crt) - << "; "; - buf << "server crt: " << server_crt_path << " __ " << to_string(server_crt) - << "; "; - buf << "server key: " << server_key_path << " __ " << to_string(server_key) - << "; "; - buf << "dh: " << dh_path << " __ " << to_string(dh) << "; "; - return buf.str(); - } - - std::string port_; - Server server; - std::string base_path; - std::string client_crt_path = "server.crt"; - std::string server_crt_path = "server.crt"; - std::string server_key_path = "server.key"; - std::string dh_path = "dhparam.pem"; - ssl_type client_crt; - ssl_type server_crt; - ssl_type server_key; - ssl_type dh; - asio::io_context io_context; - coro_io::ExecutorWrapper<> executor_; - std::thread thd; - std::unique_ptr worker; -}; - -TEST_CASE("testing client with ssl server") { - std::vector type_list{ssl_type::fake, ssl_type::no, ssl_type::_}; - std::string base_path = "../openssl_files"; - unsigned short port = 8809; - for (auto client_crt : type_list) { - for (auto server_crt : type_list) { - for (auto server_key : type_list) { - for (auto dh : type_list) { - SSLClientTester(base_path, port, client_crt, - server_crt, server_key, dh) - .run(); - } - } - } - } -} -#endif -TEST_CASE("testing client with eof") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - ret = client.sync_call(); - CHECK(ret.value() == "client hello"s); - - g_action = inject_action::client_close_socket_after_send_partial_header; - ret = client.sync_call(); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); -} -TEST_CASE("testing client with attachment") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call(); - CHECK(ret.has_value()); - CHECK(client.get_resp_attachment() == ""); - - client.set_req_attachment("hellohi"); - ret = client.sync_call(); - CHECK(ret.has_value()); - CHECK(client.get_resp_attachment() == "hellohi"); - - ret = client.sync_call(); - CHECK(ret.has_value()); - CHECK(client.get_resp_attachment() == ""); - - char buf[100], short_buf[1]; - - client.set_req_attachment("This is attachment."); - client.set_resp_attachment_buf(buf); - ret = client.sync_call(); - assert(client.get_resp_attachment() == "This is attachment."); - assert(client.is_resp_attachment_in_external_buf()); - assert(client.get_resp_attachment().data() == buf); - - client.set_req_attachment("This is attachment."); - ret = client.sync_call(); - assert(client.get_resp_attachment() == "This is attachment."); - assert(!client.is_resp_attachment_in_external_buf()); - assert(client.get_resp_attachment().data() != buf); - - client.set_req_attachment("This is attachment."); - client.set_resp_attachment_buf(short_buf); - ret = client.sync_call(); - assert(client.get_resp_attachment() == "This is attachment."); - assert(!client.is_resp_attachment_in_external_buf()); - assert(client.get_resp_attachment().data() != short_buf); -} - -TEST_CASE("testing std::string_view") { - g_action = {}; - coro_rpc_server server(1, 8801); - server.register_handler(); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - auto ret = client.sync_call("123"); - CHECK(ret.value() == "123OK"); - - ret = client.sync_call("1231232132123123"); - CHECK(ret.value() == "1231232132123123OK"); - - ret = client.sync_call("ABDD"); - CHECK(ret.value() == "ABDDOK"); -} - -TEST_CASE("testing client with context response user-defined error") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE(!ec); - auto ret = client.sync_call(); - REQUIRE(!ret.has_value()); - CHECK(ret.error().code == coro_rpc::errc{1004}); - CHECK(ret.error().msg == "My Error."); - CHECK(client.has_closed() == false); - auto ret2 = client.sync_call(); - REQUIRE(ret2.has_value()); - CHECK(ret2.value() == "hello"); -} - -TEST_CASE("testing client with shutdown") { - g_action = {}; - coro_rpc_server server(2, 8801); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = client.sync_connect("127.0.0.1", "8801"); - REQUIRE_MESSAGE(!ec, ec.message()); - - g_action = inject_action::nothing; - auto ret = client.sync_call(); - CHECK(ret.value() == "hello"s); - - ret = client.sync_call(); - CHECK(ret.value() == "client hello"s); - - g_action = inject_action::client_shutdown_socket_after_send_header; - ret = client.sync_call(); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - - g_action = {}; -} -TEST_CASE("testing client timeout") { - coro_rpc_server server(2, 8801); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - - SUBCASE("connect, 0ms connect timeout") { - coro_rpc_client client; - coro_rpc_client::config config{}; - config.connect_timeout_duration = 0ms; - bool r = client.init_config(config); - CHECK(r); - auto ret = client.connect("127.0.0.1", "8801"); - auto val = syncAwait(ret); - if (val) { - CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); - } - } - SUBCASE("connect, -1ms never timeout") { - coro_rpc_client client; - coro_rpc_client::config config{}; - config.connect_timeout_duration = -1ms; // less than 0, no timeout - // checking. - bool r = client.init_config(config); - CHECK(r); - auto ret = client.connect( - "127.0.0.1", - "8801"); // this arg won't update config connect timeout duration. - auto val = syncAwait(ret); - - CHECK(!val); - } - - SUBCASE("connect, 0ms request timeout") { - coro_rpc_client client; - coro_rpc_client::config config{}; - config.connect_timeout_duration = 1000ms; - config.request_timeout_duration = 0ms; - bool r = client.init_config(config); - CHECK(r); - auto ret = client.connect("127.0.0.1", "8801"); - auto val = syncAwait(ret); - - CHECK(!val); - - // TODO will remove via later for 0ms timeout - auto result = syncAwait(client.call().via(&client.get_executor())); - - if (result.has_value()) { - ELOG_INFO << result.value(); - } - else { - CHECK_MESSAGE(result.error().code == coro_rpc::errc::timed_out, - result.error().msg); - } - } - SUBCASE("connect, -1ms never request timeout") { - coro_rpc_client client; - client.get_config().request_timeout_duration = - -1ms; // less than 0, never timeout. - auto ret = client.connect("127.0.0.1", "8801"); - auto val = syncAwait(ret); - - CHECK(!val); - auto result = syncAwait(client.call()); - - CHECK(result.has_value()); - } - SUBCASE("connect, ip timeout") { - g_action = {}; - // https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error - coro_rpc_client client(coro_io::get_global_executor()); - auto ret = client.connect("10.255.255.1", "8801", 5ms); - auto val = syncAwait(ret); - CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); - } -} -TEST_CASE("testing client connect err") { - coro_rpc_client client(coro_io::get_global_executor()); - auto val = syncAwait(client.connect("127.0.0.1", "8801")); - CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); -} -#ifdef UNIT_TEST_INJECT -TEST_CASE("testing client sync connect, unit test inject only") { - coro_rpc_client client(coro_io::get_global_executor()); - auto val = client.sync_connect("127.0.0.1", "8801"); - CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); -#ifdef YLT_ENABLE_SSL - SUBCASE("client use ssl but server don't use ssl") { - g_action = {}; - coro_rpc_server server(2, 8801); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client2(coro_io::get_global_executor()); - bool ok = client2.init_ssl("../openssl_files", "server.crt"); - CHECK(ok == true); - val = client2.sync_connect("127.0.0.1", "8801"); - CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); - } -#endif -} -#endif -TEST_CASE("testing client call timeout") { - g_action = {}; - SUBCASE("write timeout") { - g_action = inject_action::force_inject_client_write_data_timeout; - // coro_rpc_server server(2, 8801); - // server.async_start().start([](auto&&) { - // }); - coro_rpc_client client(coro_io::get_global_executor()); - // auto ec_lazy = client.connect("127.0.0.1", "8801", 5ms); - // auto ec = syncAwait(ec_lazy); - // assert(ec == std::errc{}); - auto ret = client.call(); - auto val = syncAwait(ret); - CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, - val.error().msg); - g_action = inject_action::nothing; - } -#ifdef __GNUC__ - SUBCASE("read timeout") { - g_action = {}; - coro_rpc_server server(2, 8801); - - server.register_handler(); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec_lazy = client.connect("127.0.0.1", "8801"); - auto ec = syncAwait(ec_lazy); - REQUIRE(!ec); - auto ret = client.call_for(10ms); - auto val = syncAwait(ret); - CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, - val.error().msg); - } -#endif - g_action = {}; -} -static std::string_view echo(std::string_view data) { return data; } -TEST_CASE("testing client reconnect") { - coro_rpc_server s1(1, 9002), s2(1, 9003); - s1.async_start(); - s2.async_start(); - s2.register_handler(); - SUBCASE("test client reconnect") { - coro_rpc_client cli; - auto result = syncAwait(cli.connect("127.0.0.1", "9002")); - CHECK_MESSAGE(!result, result.message()); - result = syncAwait(cli.connect("127.0.0.1", "9003")); - CHECK_MESSAGE(!result, result.message()); - auto result2 = syncAwait(cli.call("hi")); - REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); - CHECK_MESSAGE(result2.value() == "hi", result2.value()); - } - SUBCASE("test client reconnect if client close") { - coro_rpc_client cli; - auto result = syncAwait(cli.connect("127.0.0.1", "9002")); - CHECK_MESSAGE(!result, result.message()); - cli.close(); - result = syncAwait(cli.connect("127.0.0.1", "9003")); - CHECK_MESSAGE(!result, result.message()); - auto result2 = syncAwait(cli.call("hi")); - REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); - CHECK_MESSAGE(result2.value() == "hi", result2.value()); - } - SUBCASE("test client reconnect if server close") { - coro_rpc_client cli; - auto result = syncAwait(cli.connect("127.0.0.1", "9002")); - CHECK_MESSAGE(!result, result.message()); - s1.stop(); - result = syncAwait(cli.connect("127.0.0.1", "9003")); - CHECK_MESSAGE(!result, result.message()); - auto result2 = syncAwait(cli.call("hi")); - REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); - CHECK_MESSAGE(result2.value() == "hi", result2.value()); - } -} -std::errc init_acceptor(auto& acceptor_, auto port_) { - using asio::ip::tcp; - auto endpoint = tcp::endpoint(tcp::v4(), port_); - acceptor_.open(endpoint.protocol()); - acceptor_.set_option(tcp::acceptor::reuse_address(true)); - - asio::error_code ec; - acceptor_.bind(endpoint, ec); - if (ec) { - ELOGV(ERROR, "bind error : %s", ec.message().data()); - acceptor_.cancel(ec); - acceptor_.close(ec); - return std::errc::address_in_use; - } - acceptor_.listen(); - - ELOGV(INFO, "listen port %d successfully", port_); - return std::errc{}; -} -// TODO: will open after code refactor. -// TEST_CASE("testing read body timeout") { -// register_handler(); -// std::promise server_p; -// std::promise p; -// std::thread thd = std::thread([&server_p, &p]() { -// asio::io_context io_context; -// tcp::acceptor acceptor(io_context); -// tcp::socket socket(io_context); -// auto ec = init_acceptor(acceptor, 8801); -// REQUIRE(ec == std::errc{}); -// easylog::info("server started"); -// server_p.set_value(); -// auto ec2 = accept(acceptor, socket); -// REQUIRE(!ec2); -// std::array head; -// read(socket, asio::buffer(head, RPC_HEAD_LEN)); -// req_header header; -// auto errc = struct_pack::deserialize_to(header, head); -// REQUIRE(errc == std::errc{}); -// std::vector body_; -// body_.resize(header.length); -// auto ret = read(socket, asio::buffer(body_.data(), header.length)); -// REQUIRE(!ret.first); -// auto buf = struct_pack::serialize_with_offset( -// /*offset = */ RESP_HEAD_LEN, std::monostate{}); -// *((uint32_t*)buf.data()) = buf.size() - RESP_HEAD_LEN; -// write(socket, asio::buffer(buf.data(), RESP_HEAD_LEN)); -// std::this_thread::sleep_for(50ms); -// write(socket, asio::buffer(buf.data() + RESP_HEAD_LEN, -// buf.size() - RESP_HEAD_LEN)); -// p.set_value(); -// }); -// easylog::info("wait for server start"); -// server_p.get_future().wait(); -// easylog::info("custom server started"); -// coro_rpc_client client; -// auto ec_lazy = client.connect("127.0.0.1", "8801"s, 5ms); -// auto ec = syncAwait(ec_lazy); -// REQUIRE(ec == std::errc{}); -// auto ret = client.call_for(50ms); -// auto val = syncAwait(ret); -// CHECK_MESSAGE(val.error().code == std::errc::timed_out, val.error().msg); -// thd.join(); -// p.get_future().wait(); -// } +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "doctest.h" +#include "rpc_api.hpp" +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/coro_rpc/impl/coro_rpc_client.hpp" +#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" +#include "ylt/coro_rpc/impl/errno.h" +using namespace coro_rpc; +using namespace std::chrono_literals; +using namespace std::string_literals; +using namespace async_simple::coro; +using asio::ip::tcp; + +template +class ClientTester { + ClientTester(unsigned short port) : port_(std::to_string(port)) {} + std::string port_; +}; + +static unsigned short coro_rpc_server_port = 8803; +Lazy> create_client( + coro_io::ExecutorWrapper<>* executor, std::string port) { + auto client = std::make_shared(executor); +#ifdef YLT_ENABLE_SSL + bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); +#endif + auto ec = co_await client->connect("127.0.0.1", port); + REQUIRE(!ec); + co_return client; +} + +void show(auto& s) { return; } + +TEST_CASE("testing client") { + { + coro_rpc::coro_rpc_client client; + auto lazy_ret = client.connect("", ""); + auto ret = syncAwait(lazy_ret); + CHECK(ret == coro_rpc::errc::not_connected); + } + { + coro_rpc::coro_rpc_client client; + auto ret = client.sync_connect("", ""); + CHECK(ret == coro_rpc::errc::not_connected); + } + g_action = {}; + std::string port = std::to_string(coro_rpc_server_port); + asio::io_context io_context; + coro_io::ExecutorWrapper<> executor = io_context.get_executor(); + auto executor_ptr = &executor; + std::promise promise; + auto worker = std::make_unique(io_context); + auto future = promise.get_future(); + std::thread thd([&io_context, &promise] { + promise.set_value(); + io_context.run(); + }); + + future.wait(); + coro_rpc_server server(2, coro_rpc_server_port); +#ifdef YLT_ENABLE_SSL + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); +#endif + auto res = server.async_start(); + + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + SUBCASE("call rpc, function not registered") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, + ret.error().msg); + co_return; + }; + syncAwait(f()); + } + SUBCASE("call rpc, function not registered 2") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK_MESSAGE(ret.error().code == coro_rpc::errc::function_not_registered, + ret.error().msg); + co_return; + }; + syncAwait(f()); + } + + server.stop(); + + coro_rpc_server server2(2, coro_rpc_server_port); +#ifdef YLT_ENABLE_SSL + server2.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); +#endif + server2.register_handler(); + server2.register_handler(); + server2.register_handler(); + res = server2.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + SUBCASE("call rpc timeout") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call_for(10ms); + CHECK_MESSAGE(ret.error().code == coro_rpc::errc::timed_out, + ret.error().msg); + co_return; + }; + syncAwait(f()); + } + + SUBCASE("call rpc success") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK(ret.value() == std::string("hello")); + ret = co_await client->call_for(100ms); + CHECK(ret.value() == std::string("hello")); + co_return; + }; + syncAwait(f()); + } + + SUBCASE("call with large buffer") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + std::string arg; + arg.resize(2048); + auto ret = co_await client->template call(arg); + show(ret); + CHECK(ret.value() == arg); + co_return; + }; + syncAwait(f()); + } + + server.stop(); + worker = nullptr; + thd.join(); +} + +std::string get_first_local_ip() { + using asio::ip::tcp; + try { + tcp::resolver resolver(coro_io::get_global_executor()->get_asio_executor()); + tcp::resolver::query query(asio::ip::host_name(), ""); + tcp::resolver::iterator iter = resolver.resolve(query); + tcp::resolver::iterator end; // End marker. + while (iter != end) { + tcp::endpoint ep = *iter++; + auto addr = ep.address(); + if (addr.is_v4()) { + return addr.to_string(); + } + } + } catch (std::exception& e) { + ELOG_WARN << "get_first_local_ip error: " << e.what(); + } + + return "localhost"; +} + +void foo(const size_t& i) {} + +TEST_CASE("testing const & args") { + coro_rpc_server server(1, 8901); + server.register_handler(); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client{}; + syncAwait(client.connect("127.0.0.1:8901")); + const int& i = 0; + auto ret = syncAwait(client.call(i)); + CHECK(ret.has_value()); +} + +TEST_CASE("testing client with local ip") { + coro_rpc_server server(1, 8901); + server.register_handler(); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + std::string local_ip = get_first_local_ip(); + ELOG_INFO << "local ip: " << local_ip; + +// Due to windows strong host mode, we cant connect to localhost from specify +// nic ip address +#ifndef _WIN32 + std::string target_ip = "127.0.0.1"; +#else + std::string target_ip = local_ip; +#endif + coro_rpc_client client(local_ip); + auto ec = client.sync_connect(target_ip, "8901"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + coro_rpc_server server1(1, 8902, local_ip); + server1.register_handler(); + res = server1.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + ec = client.sync_connect(local_ip, "8902"); + REQUIRE_MESSAGE(!ec, ec.message()); + + ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + std::vector eps; + eps.push_back(asio::ip::tcp::endpoint( + asio::ip::address::from_string("192.0.2.1"), 8901)); + eps.push_back(asio::ip::tcp::endpoint( + asio::ip::address::from_string("127.0.0.1"), 8901)); + + auto local_ep = + asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 0); + asio::ip::tcp::socket socket(client.get_executor().get_asio_executor()); + socket.open(local_ep.protocol()); + socket.bind(local_ep); + + auto ec1 = async_simple::coro::syncAwait(coro_io::async_connect(socket, eps)); + CHECK(!ec1); + CHECK(socket.local_endpoint().address().to_string() == "127.0.0.1"); +} + +TEST_CASE("testing client with inject server") { + g_action = {}; + std::string port = std::to_string(coro_rpc_server_port); + ELOGV(INFO, "inject server port: %d", port.data()); + asio::io_context io_context; + coro_io::ExecutorWrapper<> executor = io_context.get_executor(); + auto executor_ptr = &executor; + auto worker = std::make_unique(io_context); + std::thread thd([&io_context] { + io_context.run(); + }); + coro_rpc_server server(2, coro_rpc_server_port); +#ifdef YLT_ENABLE_SSL + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); +#endif + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + SUBCASE("server run ok") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + auto ret = co_await client->template call(); + CHECK(ret.value() == std::string("hello")); + co_return; + }; + syncAwait(f()); + } + + SUBCASE("client read length error") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + g_action = inject_action::close_socket_after_read_header; + auto ret = co_await client->template call(); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + }; + syncAwait(f()); + } + SUBCASE("client read body error") { + g_action = {}; + auto f = [executor_ptr, &port]() -> Lazy { + auto client = co_await create_client(executor_ptr, port); + g_action = inject_action::close_socket_after_send_length; + auto ret = co_await client->template call(); + show(ret); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + }; + syncAwait(f()); + } + + server.stop(); + worker = nullptr; + thd.join(); + g_action = inject_action::nothing; +} +#ifdef YLT_ENABLE_SSL + +enum class ssl_type { _, fake, no }; + +template +class SSLClientTester { + public: + SSLClientTester(std::string base_path, unsigned short port, + ssl_type client_crt, ssl_type server_crt, ssl_type server_key, + ssl_type dh) + : port_(std::to_string(port)), + server(2, port), + base_path(base_path), + client_crt(client_crt), + server_crt(server_crt), + server_key(server_key), + dh(dh), + executor_(io_context.get_executor()) { + inject("client crt", client_crt_path, client_crt); + inject("server crt", server_crt_path, server_crt); + inject("server key", server_key_path, server_key); + inject("dh", dh_path, dh); + ssl_configure config{base_path, server_crt_path, server_key_path, dh_path}; + server.init_ssl(config); + server.template register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + + std::promise promise; + auto future = promise.get_future(); + worker = std::make_unique(io_context); + thd = std::thread([this, &promise] { + promise.set_value(); + io_context.run(); + }); + future.wait(); + } + ~SSLClientTester() { + worker = nullptr; + thd.join(); + } + void inject(std::string msg, std::string& path, ssl_type type) { + switch (type) { + case ssl_type::fake: { + path = "fake_" + path; + break; + } + case ssl_type::no: { + path = "no_" + path; + break; + } + default: { + } + } + ELOGV(INFO, "%s %s", msg.data(), path.data()); + } + void run() { + auto client = std::make_shared(&executor_); + bool ok = client->init_ssl(base_path, client_crt_path); + if (client_crt == ssl_type::fake || client_crt == ssl_type::no) { + REQUIRE(ok == false); + auto ec = syncAwait(client->connect("127.0.0.1", port_)); + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); + auto ret = syncAwait(client->template call()); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::not_connected, + ret.error().msg); + } + else { + REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); + auto f = [this, &client]() -> Lazy { + auto ec = co_await client->connect("127.0.0.1", port_); + if (server_crt == ssl_type::_ && server_key == ssl_type::_) { + if (ec) { + ELOGV(INFO, "%s", gen_err().data()); + } + REQUIRE_MESSAGE(!ec, ec.message()); + auto ret = co_await client->template call(); + CHECK(ret.has_value()); + } + else { + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, ec.message()); + } + }; + syncAwait(f()); + } + } + + std::string gen_err() { + auto to_string = [](ssl_type t) -> std::string { + switch (t) { + case ssl_type::fake: { + return "fake"s; + } + case ssl_type::no: { + return "no"s; + } + default: { + return "_"s; + } + } + }; + std::stringstream buf; + buf << "\n"; + buf << "client crt: " << client_crt_path << " __ " << to_string(client_crt) + << "; "; + buf << "server crt: " << server_crt_path << " __ " << to_string(server_crt) + << "; "; + buf << "server key: " << server_key_path << " __ " << to_string(server_key) + << "; "; + buf << "dh: " << dh_path << " __ " << to_string(dh) << "; "; + return buf.str(); + } + + std::string port_; + Server server; + std::string base_path; + std::string client_crt_path = "server.crt"; + std::string server_crt_path = "server.crt"; + std::string server_key_path = "server.key"; + std::string dh_path = "dhparam.pem"; + ssl_type client_crt; + ssl_type server_crt; + ssl_type server_key; + ssl_type dh; + asio::io_context io_context; + coro_io::ExecutorWrapper<> executor_; + std::thread thd; + std::unique_ptr worker; +}; + +TEST_CASE("testing client with ssl server") { + std::vector type_list{ssl_type::fake, ssl_type::no, ssl_type::_}; + std::string base_path = "../openssl_files"; + unsigned short port = 8809; + for (auto client_crt : type_list) { + for (auto server_crt : type_list) { + for (auto server_key : type_list) { + for (auto dh : type_list) { + SSLClientTester(base_path, port, client_crt, + server_crt, server_key, dh) + .run(); + } + } + } + } +} +#endif +TEST_CASE("testing client with eof") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + ret = client.sync_call(); + CHECK(ret.value() == "client hello"s); + + g_action = inject_action::client_close_socket_after_send_partial_header; + ret = client.sync_call(); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); +} +TEST_CASE("testing client with attachment") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call(); + CHECK(ret.has_value()); + CHECK(client.get_resp_attachment() == ""); + + client.set_req_attachment("hellohi"); + ret = client.sync_call(); + CHECK(ret.has_value()); + CHECK(client.get_resp_attachment() == "hellohi"); + + ret = client.sync_call(); + CHECK(ret.has_value()); + CHECK(client.get_resp_attachment() == ""); + + char buf[100], short_buf[1]; + + client.set_req_attachment("This is attachment."); + client.set_resp_attachment_buf(buf); + ret = client.sync_call(); + assert(client.get_resp_attachment() == "This is attachment."); + assert(client.is_resp_attachment_in_external_buf()); + assert(client.get_resp_attachment().data() == buf); + + client.set_req_attachment("This is attachment."); + ret = client.sync_call(); + assert(client.get_resp_attachment() == "This is attachment."); + assert(!client.is_resp_attachment_in_external_buf()); + assert(client.get_resp_attachment().data() != buf); + + client.set_req_attachment("This is attachment."); + client.set_resp_attachment_buf(short_buf); + ret = client.sync_call(); + assert(client.get_resp_attachment() == "This is attachment."); + assert(!client.is_resp_attachment_in_external_buf()); + assert(client.get_resp_attachment().data() != short_buf); +} + +TEST_CASE("testing std::string_view") { + g_action = {}; + coro_rpc_server server(1, 8801); + server.register_handler(); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + auto ret = client.sync_call("123"); + CHECK(ret.value() == "123OK"); + + ret = client.sync_call("1231232132123123"); + CHECK(ret.value() == "1231232132123123OK"); + + ret = client.sync_call("ABDD"); + CHECK(ret.value() == "ABDDOK"); +} + +TEST_CASE("testing client with context response user-defined error") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE(!ec); + auto ret = client.sync_call(); + REQUIRE(!ret.has_value()); + CHECK(ret.error().code == coro_rpc::errc{1004}); + CHECK(ret.error().msg == "My Error."); + CHECK(client.has_closed() == false); + auto ret2 = client.sync_call(); + REQUIRE(ret2.has_value()); + CHECK(ret2.value() == "hello"); +} + +TEST_CASE("testing client with shutdown") { + g_action = {}; + coro_rpc_server server(2, 8801); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = client.sync_connect("127.0.0.1", "8801"); + REQUIRE_MESSAGE(!ec, ec.message()); + + g_action = inject_action::nothing; + auto ret = client.sync_call(); + CHECK(ret.value() == "hello"s); + + ret = client.sync_call(); + CHECK(ret.value() == "client hello"s); + + g_action = inject_action::client_shutdown_socket_after_send_header; + ret = client.sync_call(); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + + g_action = {}; +} +TEST_CASE("testing client timeout") { + coro_rpc_server server(2, 8801); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + + SUBCASE("connect, 0ms connect timeout") { + coro_rpc_client client; + coro_rpc_client::config config{}; + config.connect_timeout_duration = 0ms; + bool r = client.init_config(config); + CHECK(r); + auto ret = client.connect("127.0.0.1", "8801"); + auto val = syncAwait(ret); + if (val) { + CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); + } + } + SUBCASE("connect, -1ms never timeout") { + coro_rpc_client client; + coro_rpc_client::config config{}; + config.connect_timeout_duration = -1ms; // less than 0, no timeout + // checking. + bool r = client.init_config(config); + CHECK(r); + auto ret = client.connect( + "127.0.0.1", + "8801"); // this arg won't update config connect timeout duration. + auto val = syncAwait(ret); + + CHECK(!val); + } + + SUBCASE("connect, 0ms request timeout") { + coro_rpc_client client; + coro_rpc_client::config config{}; + config.connect_timeout_duration = 1000ms; + config.request_timeout_duration = 0ms; + bool r = client.init_config(config); + CHECK(r); + auto ret = client.connect("127.0.0.1", "8801"); + auto val = syncAwait(ret); + + CHECK(!val); + + // TODO will remove via later for 0ms timeout + auto result = syncAwait(client.call().via(&client.get_executor())); + + if (result.has_value()) { + ELOG_INFO << result.value(); + } + else { + CHECK_MESSAGE(result.error().code == coro_rpc::errc::timed_out, + result.error().msg); + } + } + SUBCASE("connect, -1ms never request timeout") { + coro_rpc_client client; + client.get_config().request_timeout_duration = + -1ms; // less than 0, never timeout. + auto ret = client.connect("127.0.0.1", "8801"); + auto val = syncAwait(ret); + + CHECK(!val); + auto result = syncAwait(client.call()); + + CHECK(result.has_value()); + } + SUBCASE("connect, ip timeout") { + g_action = {}; + // https://stackoverflow.com/questions/100841/artificially-create-a-connection-timeout-error + coro_rpc_client client(coro_io::get_global_executor()); + auto ret = client.connect("10.255.255.1", "8801", 5ms); + auto val = syncAwait(ret); + CHECK_MESSAGE(val == coro_rpc::errc::timed_out, val.message()); + } +} +TEST_CASE("testing client connect err") { + coro_rpc_client client(coro_io::get_global_executor()); + auto val = syncAwait(client.connect("127.0.0.1", "8801")); + CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); +} +#ifdef UNIT_TEST_INJECT +TEST_CASE("testing client sync connect, unit test inject only") { + coro_rpc_client client(coro_io::get_global_executor()); + auto val = client.sync_connect("127.0.0.1", "8801"); + CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); +#ifdef YLT_ENABLE_SSL + SUBCASE("client use ssl but server don't use ssl") { + g_action = {}; + coro_rpc_server server(2, 8801); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client2(coro_io::get_global_executor()); + bool ok = client2.init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + CHECK(ok == true); + val = client2.sync_connect("127.0.0.1", "8801"); + CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); + } +#endif +} +#endif +TEST_CASE("testing client call timeout") { + g_action = {}; + SUBCASE("write timeout") { + g_action = inject_action::force_inject_client_write_data_timeout; + // coro_rpc_server server(2, 8801); + // server.async_start().start([](auto&&) { + // }); + coro_rpc_client client(coro_io::get_global_executor()); + // auto ec_lazy = client.connect("127.0.0.1", "8801", 5ms); + // auto ec = syncAwait(ec_lazy); + // assert(ec == std::errc{}); + auto ret = client.call(); + auto val = syncAwait(ret); + CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, + val.error().msg); + g_action = inject_action::nothing; + } +#ifdef __GNUC__ + SUBCASE("read timeout") { + g_action = {}; + coro_rpc_server server(2, 8801); + + server.register_handler(); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec_lazy = client.connect("127.0.0.1", "8801"); + auto ec = syncAwait(ec_lazy); + REQUIRE(!ec); + auto ret = client.call_for(10ms); + auto val = syncAwait(ret); + CHECK_MESSAGE(val.error().code == coro_rpc::errc::timed_out, + val.error().msg); + } +#endif + g_action = {}; +} +static std::string_view echo(std::string_view data) { return data; } +TEST_CASE("testing client reconnect") { + coro_rpc_server s1(1, 9002), s2(1, 9003); + s1.async_start(); + s2.async_start(); + s2.register_handler(); + SUBCASE("test client reconnect") { + coro_rpc_client cli; + auto result = syncAwait(cli.connect("127.0.0.1", "9002")); + CHECK_MESSAGE(!result, result.message()); + result = syncAwait(cli.connect("127.0.0.1", "9003")); + CHECK_MESSAGE(!result, result.message()); + auto result2 = syncAwait(cli.call("hi")); + REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); + CHECK_MESSAGE(result2.value() == "hi", result2.value()); + } + SUBCASE("test client reconnect if client close") { + coro_rpc_client cli; + auto result = syncAwait(cli.connect("127.0.0.1", "9002")); + CHECK_MESSAGE(!result, result.message()); + cli.close(); + result = syncAwait(cli.connect("127.0.0.1", "9003")); + CHECK_MESSAGE(!result, result.message()); + auto result2 = syncAwait(cli.call("hi")); + REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); + CHECK_MESSAGE(result2.value() == "hi", result2.value()); + } + SUBCASE("test client reconnect if server close") { + coro_rpc_client cli; + auto result = syncAwait(cli.connect("127.0.0.1", "9002")); + CHECK_MESSAGE(!result, result.message()); + s1.stop(); + result = syncAwait(cli.connect("127.0.0.1", "9003")); + CHECK_MESSAGE(!result, result.message()); + auto result2 = syncAwait(cli.call("hi")); + REQUIRE_MESSAGE(result2.has_value(), result2.error().msg); + CHECK_MESSAGE(result2.value() == "hi", result2.value()); + } +} +std::errc init_acceptor(auto& acceptor_, auto port_) { + using asio::ip::tcp; + auto endpoint = tcp::endpoint(tcp::v4(), port_); + acceptor_.open(endpoint.protocol()); + acceptor_.set_option(tcp::acceptor::reuse_address(true)); + + asio::error_code ec; + acceptor_.bind(endpoint, ec); + if (ec) { + ELOGV(ERROR, "bind error : %s", ec.message().data()); + acceptor_.cancel(ec); + acceptor_.close(ec); + return std::errc::address_in_use; + } + acceptor_.listen(); + + ELOGV(INFO, "listen port %d successfully", port_); + return std::errc{}; +} +// TODO: will open after code refactor. +// TEST_CASE("testing read body timeout") { +// register_handler(); +// std::promise server_p; +// std::promise p; +// std::thread thd = std::thread([&server_p, &p]() { +// asio::io_context io_context; +// tcp::acceptor acceptor(io_context); +// tcp::socket socket(io_context); +// auto ec = init_acceptor(acceptor, 8801); +// REQUIRE(ec == std::errc{}); +// easylog::info("server started"); +// server_p.set_value(); +// auto ec2 = accept(acceptor, socket); +// REQUIRE(!ec2); +// std::array head; +// read(socket, asio::buffer(head, RPC_HEAD_LEN)); +// req_header header; +// auto errc = struct_pack::deserialize_to(header, head); +// REQUIRE(errc == std::errc{}); +// std::vector body_; +// body_.resize(header.length); +// auto ret = read(socket, asio::buffer(body_.data(), header.length)); +// REQUIRE(!ret.first); +// auto buf = struct_pack::serialize_with_offset( +// /*offset = */ RESP_HEAD_LEN, std::monostate{}); +// *((uint32_t*)buf.data()) = buf.size() - RESP_HEAD_LEN; +// write(socket, asio::buffer(buf.data(), RESP_HEAD_LEN)); +// std::this_thread::sleep_for(50ms); +// write(socket, asio::buffer(buf.data() + RESP_HEAD_LEN, +// buf.size() - RESP_HEAD_LEN)); +// p.set_value(); +// }); +// easylog::info("wait for server start"); +// server_p.get_future().wait(); +// easylog::info("custom server started"); +// coro_rpc_client client; +// auto ec_lazy = client.connect("127.0.0.1", "8801"s, 5ms); +// auto ec = syncAwait(ec_lazy); +// REQUIRE(ec == std::errc{}); +// auto ret = client.call_for(50ms); +// auto val = syncAwait(ret); +// CHECK_MESSAGE(val.error().code == std::errc::timed_out, val.error().msg); +// thd.join(); +// p.get_future().wait(); +// } diff --git a/src/coro_rpc/tests/test_coro_rpc_server.cpp b/src/coro_rpc/tests/test_coro_rpc_server.cpp index 582b9be99..be8b9a819 100644 --- a/src/coro_rpc/tests/test_coro_rpc_server.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_server.cpp @@ -1,735 +1,734 @@ -/* - * Copyright (c) 2023, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include - -#include -#include -#include -#include - -#include "ServerTester.hpp" -#include "async_simple/coro/Lazy.h" -#include "cinatra/response_cv.hpp" -#include "doctest.h" -#include "rpc_api.hpp" -#include "ylt/coro_http/coro_http_client.hpp" -#include "ylt/coro_http/coro_http_server.hpp" -#include "ylt/coro_io/coro_io.hpp" -#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" -#include "ylt/coro_rpc/impl/errno.h" -#include "ylt/struct_pack.hpp" - -async_simple::coro::Lazy get_coro_value(int val) { co_return val; } - -struct CoroServerTester : ServerTester { - CoroServerTester(TesterConfig config) - : ServerTester(config), - server(2, config.port, config.address, config.conn_timeout_duration) { -#ifdef YLT_ENABLE_SSL - if (use_ssl) { - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); - } -#endif -#ifdef YLT_ENABLE_IBV - if (use_rdma) { - server.init_ibv(); - } -#endif - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - } - ~CoroServerTester() { server.stop(); } - - async_simple::coro::Lazy get_value(int val) { co_return val; } - - void test_set_server_address() { - { - coro_rpc_server server(1, 9001); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "0.0.0.0"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "127.0.0.1"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "localhost"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, "0.0.0.0:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, "127.0.0.1:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, "localhost:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(!server.get_errc()); - } - - { - coro_rpc_server server(1, 9001, "x.x.x.x"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(server.get_errc() == coro_rpc::errc::bad_address); - } - - { - coro_rpc_server server(1, "x.x.x.x:9001"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(server.get_errc() == coro_rpc::errc::bad_address); - } - - { - coro_rpc_server server(1, "127.0.0.1:aaa"); - [[maybe_unused]] auto r = server.async_start(); - CHECK(server.get_errc() == coro_rpc::errc::bad_address); - } - } - - void test_all() override { - g_action = {}; - ELOGV(INFO, "run %s", __func__); - test_set_server_address(); - test_coro_handler(); - ServerTester::test_all(); - test_function_not_registered(); - test_start_new_server_with_same_port(); - test_server_send_bad_rpc_result(); - test_server_send_no_body(); - test_context_func(); - test_return_err_by_throw_exception(); - this->test_call_with_delay_func(); - this->test_call_with_delay_func< - coro_fun_with_user_define_connection_type>(); - this->test_call_with_delay_func(); - this->test_call_with_delay_func_client_read_length_error< - coro_fun_with_delay_return_void>(); - this->test_call_with_delay_func_client_read_body_error< - coro_fun_with_delay_return_void>(); - if (enable_heartbeat) { - this->test_call_with_delay_func_server_timeout< - coro_fun_with_delay_return_void_cost_long_time>(); - } - this->test_call_with_delay_func(); - this->test_call_with_delay_func(); - } - void register_all_function() override { - g_action = {}; - ELOGV(INFO, "run %s", __func__); - server.register_handler(); - server.register_handler(); - server.register_handler<&ns_login::LoginService::login>(&login_service_); - server.register_handler<&HelloService::hello>(&hello_service_); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler(); - server.register_handler<&HelloService::coro_func, - &HelloService::coro_func_return_void>( - &hello_service_); - server.register_handler(); - server.register_handler<&CoroServerTester::get_value>(this); - } - - void test_context_func() { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - client->set_req_attachment("1234567890987654321234567890"); - auto result = syncAwait(client->call()); - CHECK(result); - CHECK(client->get_resp_attachment() == "1234567890987654321234567890"); - - client->set_req_attachment("12345678909876543212345678901"); - result = syncAwait(client->call()); - CHECK(result); - CHECK(client->get_resp_attachment() == "12345678909876543212345678901"); - - client->set_req_attachment("01234567890987654321234567890"); - result = syncAwait(client->call()); - CHECK(result); - CHECK(client->get_resp_attachment() == "01234567890987654321234567890"); - } - void test_return_err_by_throw_exception() { - { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto result = syncAwait(client->call()); - REQUIRE(!result); - CHECK(result.error().code == coro_rpc::errc::address_in_used); - CHECK(result.error().msg == "error with user-defined msg"); - } - { - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto result = syncAwait(client->call()); - REQUIRE(!result); - CHECK(result.error().code == coro_rpc::errc::address_in_used); - CHECK(result.error().msg == "error with user-defined msg"); - } - } - - void test_function_not_registered() { - g_action = {}; - auto client = create_client(); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::function_not_registered, - std::to_string(client->get_client_id()).append(ret.error().msg)); - REQUIRE(client->has_closed() == true); - ret = call(client); - CHECK(client->has_closed() == true); - ret = call(client); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - CHECK(client->has_closed() == true); - } - - void test_server_start_again() { - ELOGV(INFO, "run %s", __func__); - - auto ec = server.start(); - REQUIRE_MESSAGE(ec == coro_rpc::errc::io_error, ec.message()); - } - - void test_start_new_server_with_same_port() { - ELOGV(INFO, "run %s", __func__); - { - auto new_server = coro_rpc_server(2, std::stoi(this->port_)); - auto ec = new_server.async_start(); - REQUIRE(ec.hasResult()); - REQUIRE_MESSAGE(ec.value() == coro_rpc::errc::address_in_used, - ec.value().message()); - } - ELOGV(INFO, "OH NO"); - } - void test_server_send_bad_rpc_result() { - auto client = create_client(inject_action::server_send_bad_rpc_result); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = this->call(client); - CHECK_MESSAGE( - ret.error().code == coro_rpc::errc::invalid_rpc_result, - std::to_string(client->get_client_id()).append(ret.error().msg)); - g_action = {}; - } - - void test_server_send_no_body() { - auto client = create_client(inject_action::close_socket_after_send_length); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = this->template call(client); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client->get_client_id()).append(ret.error().msg)); - g_action = {}; - } - - void test_coro_handler() { - auto client = create_client(inject_action::nothing); - ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); - auto ret = this->template call(client, 42); - CHECK(ret.value() == 42); - - auto ret1 = this->template call<&HelloService::coro_func>(client, 42); - CHECK(ret1.value() == 42); - - auto ret2 = this->template call<&CoroServerTester::get_value>(client, 42); - CHECK(ret2.value() == 42); - - auto ret3 = this->template call(client, 42); - CHECK(ret3.value() == 42); - - auto ret4 = this->template call(client, 42); - CHECK(ret4.has_value()); - - auto ret5 = - this->template call<&HelloService::coro_func_return_void>(client, 42); - CHECK(ret5.has_value()); - } - coro_rpc_server server; - std::thread thd; - HelloService hello_service_; -}; -TEST_CASE("testing coro rpc server") { - ELOGV(INFO, "run testing coro rpc server"); - unsigned short server_port = 8810; - auto conn_timeout_duration = 500ms; - std::vector switch_list{0 -#ifdef YLT_ENABLE_SSL - , - 1 -#endif -#ifdef YLT_ENABLE_IBV - , - 2 -#endif - }; - for (auto enable_heartbeat : switch_list) { - for (auto type : switch_list) { - TesterConfig config; - config.enable_heartbeat = enable_heartbeat; - if (type == 0) { - config.use_ssl = false; - config.use_rdma = false; - } - else if (type == 1) { - config.use_ssl = true; - config.use_rdma = false; - } - else if (type == 2) { - config.use_ssl = false; - config.use_rdma = true; - } - config.sync_client = false; - config.use_outer_io_context = false; - config.port = server_port; - if (enable_heartbeat) { - config.conn_timeout_duration = conn_timeout_duration; - } - std::stringstream ss; - ss << config; - ELOGV(INFO, "config: %s", ss.str().data()); - CoroServerTester(config).run(); - } - // } - } -} - -TEST_CASE("testing coro rpc server stop") { - ELOGV(INFO, "run testing coro rpc server stop"); - coro_rpc_server server(2, 8810); - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - SUBCASE("stop twice") { - server.stop(); - server.stop(); - } - SUBCASE("stop in different thread") { - std::thread thd1([&server]() { - server.stop(); - }); - std::thread thd2([&server]() { - server.stop(); - }); - thd1.join(); - thd2.join(); - } -} - -TEST_CASE("test server accept error") { - ELOGV(INFO, "run test server accept error"); - g_action = inject_action::force_inject_server_accept_error; - coro_rpc_server server(2, 8810); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - coro_rpc_client client(coro_io::get_global_executor()); - ELOGV(INFO, "run test server accept error, client_id %d", - client.get_client_id()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, - ret.error().msg); - REQUIRE(client.has_closed() == true); - g_action = {}; -} - -TEST_CASE("test server write queue") { - using coro_rpc_protocol = coro_rpc::protocol::coro_rpc_protocol; - ELOGV(INFO, "run server write queue"); - g_action = {}; - coro_rpc_server server(2, 8810); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start timeout"); - std::string buffer; - buffer.reserve(coro_rpc_protocol::REQ_HEAD_LEN + - struct_pack::get_needed_size(std::monostate{})); - buffer.resize(coro_rpc_protocol::REQ_HEAD_LEN); - auto &header = *(coro_rpc_protocol::req_header *)buffer.data(); - header.magic = coro_rpc_protocol::magic_number; - header.function_id = - func_id(); - header.seq_num = g_client_id++; - header.length = struct_pack::get_needed_size(std::monostate{}); - ELOGV(INFO, "client_id %d begin to connect %d", header.seq_num, 8820); - struct_pack::serialize_to(buffer, std::monostate{}); - asio::io_context io_context; - std::thread thd([&io_context]() { - asio::io_context::work work(io_context); - io_context.run(); - }); - asio::ip::tcp::socket socket(io_context); - auto ret = coro_io::connect(io_context, socket, "127.0.0.1", "8810"); - CHECK(!ret); - ELOGV(INFO, "%s client_id %d call %s", "sync_client", header.seq_num, - "coro_fun_with_delay_return_void_cost_long_time"); - for (int i = 0; i < 1; ++i) { - auto err = - coro_io::write(socket, asio::buffer(buffer.data(), buffer.size())); - CHECK(err.second == buffer.size()); - } - for (int i = 0; i < 1; ++i) { - char buffer2[coro_rpc_protocol::RESP_HEAD_LEN]; - std::monostate r; - auto buf = struct_pack::serialize(r); - std::string buffer_read; - buffer_read.resize(buf.size()); - read(socket, asio::buffer(buffer2, coro_rpc_protocol::RESP_HEAD_LEN)); - [[maybe_unused]] auto resp_head = - *(coro_rpc_protocol::resp_header *)buffer2; - uint32_t body_len = header.length; - CHECK(body_len == buf.size()); - read(socket, asio::buffer(buffer_read, body_len)); - std::monostate r2; - std::size_t sz; - auto ret = - struct_pack::deserialize_to(r2, buffer_read.data(), body_len, sz); - CHECK(!ret); - CHECK(sz == body_len); - CHECK(r2 == r); - } - - ELOGV(INFO, "client_id %d close", header.seq_num); - asio::error_code ignored_ec; - socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); - socket.close(ignored_ec); - io_context.stop(); - thd.join(); - server.stop(); -} - -TEST_CASE("testing coro rpc write error") { - ELOGV(INFO, "run testing coro rpc write error"); - g_action = inject_action::force_inject_connection_close_socket; - coro_rpc_server server(2, 8810); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - ELOGV(INFO, "run testing coro rpc write error, client_id %d", - client.get_client_id()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE( - ret.error().code == coro_rpc::errc::io_error, - std::to_string(client.get_client_id()).append(ret.error().msg)); - REQUIRE(client.has_closed() == true); - g_action = inject_action::nothing; -} - -TEST_CASE("testing ipv6") { - using namespace coro_http; - coro_rpc_server server(1, "[::1]:8811"); - server.register_handler(); - auto res = server.async_start(); - if (res.hasResult()) { - return; - } - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client{}; - auto ec = syncAwait(client.connect("[::1]:8811")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - CHECK(ret); - - coro_rpc_client client1{}; - ec = syncAwait(client1.connect("::1:8811")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - ret = syncAwait(client1.call()); - CHECK(ret); - - coro_http::coro_http_server http_server(1, 8812, "::1"); - http_server.set_http_handler( - "/test", [](coro_http_request &req, coro_http_response &resp) { - resp.set_status_and_content(status_type::ok, "ok"); - }); - http_server.async_start(); - coro_http::coro_http_client htttp_client{}; - auto r = syncAwait(htttp_client.connect("http://[::1]:8812")); - CHECK(!r.net_err); - auto result = htttp_client.get("/test"); - CHECK(result.status == 200); - - coro_http::coro_http_client htttp_client1{}; - result = htttp_client1.get("http://[::1]:8812/test"); - CHECK(result.status == 200); -} - -TEST_CASE("testing coro rpc subserver") { - ELOGV(INFO, "run testing coro rpc subserver"); - std::string http_body = R"( - - - - Example - - -

This is an example of a simple HTML page with one paragraph.

- -)"; - coro_rpc_server server(2, 8810); - server.register_handler(); - std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, - std::string_view magic_number, - coro_http::coro_http_server &server) { - CHECK(magic_number == "G"); - server.transfer_connection(std::move(soc), magic_number); - }; - auto http_server = std::make_unique(0, 0); - http_server->set_http_handler( - "/index.html", - [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { - resp.set_status_and_content(coro_http::status_type::ok, http_body); - }); - server.add_subserver(std::move(dispatcher), std::move(http_server)); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); - coro_http::coro_http_client cli; - auto result = syncAwait(cli.connect("localhost:8810")); - CHECK_MESSAGE(!result.net_err, result.net_err.message()); - result = syncAwait(cli.async_get("/index.html")); - CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, - result.status); - CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -} -#ifdef YLT_ENABLE_SSL -TEST_CASE("testing coro rpc ssl subserver") { - ELOGV(INFO, "run testing coro rpc ssl subserver"); - std::string http_body = R"( - - - - Example - - -

This is an example of a simple HTML page with one paragraph.

- -)"; - coro_rpc_server server(2, 8810); - server.init_ssl( - ssl_configure{"../openssl_files", "server.crt", "server.key"}); - server.register_handler(); - std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, - std::string_view magic_number, - coro_http::coro_http_server &server) { - CHECK(magic_number == "G"); - server.transfer_connection(std::move(soc), magic_number); - }; - auto http_server = std::make_unique(0, 0); - http_server->set_http_handler( - "/index.html", - [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { - resp.set_status_and_content(coro_http::status_type::ok, http_body); - }); - server.add_subserver(std::move(dispatcher), std::move(http_server)); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - CHECK(client.init_ssl("../openssl_files", "server.crt")); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); - coro_http::coro_http_client cli; - CHECK_MESSAGE( - cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/server.crt"), - "init ssl failed"); - auto result = syncAwait(cli.connect("https://localhost:8810")); - CHECK_MESSAGE(!result.net_err, result.net_err.message()); - result = syncAwait(cli.async_get("/index.html")); - CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, - result.net_err.message()); - CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -} -#ifdef YLT_ENABLE_IBV -TEST_CASE("testing coro rpc non-rdma client to rdma server") { - ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); - coro_rpc_server server(2, 8810); - server.init_ibv(); - server.register_handler(); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); -} -TEST_CASE("testing coro rpc subserver with rdma") { - ELOGV(INFO, "run testing coro rpc subserver with rdma"); - std::string http_body = R"( - - - - Example - - -

This is an example of a simple HTML page with one paragraph.

- -)"; - coro_rpc_server server(2, 8810); - server.init_ibv(); - server.register_handler(); - std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, - std::string_view magic_number, - coro_http::coro_http_server &server) { - CHECK(magic_number == "G"); - server.transfer_connection(std::move(soc), magic_number); - }; - auto http_server = std::make_unique(0, 0); - http_server->set_http_handler( - "/index.html", - [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { - resp.set_status_and_content(coro_http::status_type::ok, http_body); - }); - server.add_subserver(std::move(dispatcher), std::move(http_server)); - auto res = server.async_start(); - CHECK_MESSAGE(!res.hasResult(), "server start failed"); - coro_rpc_client client(coro_io::get_global_executor()); - bool is_ok = client.init_ibv(); - REQUIRE_MESSAGE(is_ok, "init ibv failed"); - auto ec = syncAwait(client.connect("127.0.0.1", "8810")); - REQUIRE_MESSAGE(!ec, - std::to_string(client.get_client_id()).append(ec.message())); - auto ret = syncAwait(client.call()); - REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); - coro_http::coro_http_client cli; - auto result = syncAwait(cli.connect("localhost:8810")); - CHECK_MESSAGE(!result.net_err, result.net_err.message()); - result = syncAwait(cli.async_get("/index.html")); - CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, - result.status); - CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -} -#ifdef YLT_ENABLE_SSL - -// TODO: open it when whe support rdma client with ssl-encrypt connection - -// TEST_CASE("testing coro rpc non-rdma ssl client to ssl rdma server") { -// ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); -// coro_rpc_server server(2, 8810); -// server.init_ibv(); -// server.init_ssl( -// ssl_configure{"../openssl_files", "server.crt", "server.key"}); -// server.register_handler(); -// auto res = server.async_start(); -// CHECK_MESSAGE(!res.hasResult(), "server start failed"); -// coro_rpc_client client(coro_io::get_global_executor()); -// CHECK(client.init_ssl("../openssl_files", "server.crt")); -// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); -// REQUIRE_MESSAGE(!ec, -// std::to_string(client.get_client_id()).append(ec.message())); -// auto ret = syncAwait(client.call()); -// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); -// } - -// TEST_CASE("testing ssl coro rpc subserver with rdma") { -// ELOGV(INFO, "run testing ssl coro rpc subserver with rdma"); -// std::string http_body = R"( -// -// -// -// Example -// -// -//

This is an example of a simple HTML page with one paragraph.

-// -// )"; -// coro_rpc_server server(2, 8810); -// server.init_ssl( -// ssl_configure{"../openssl_files", "server.crt", "server.key"}); -// server.init_ibv(); -// server.register_handler(); -// std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, -// std::string_view magic_number, -// coro_http::coro_http_server &server) { -// CHECK(magic_number == "G"); -// server.transfer_connection(std::move(soc), magic_number); -// }; -// auto http_server = std::make_unique(0, 0); -// http_server->set_http_handler( -// "/index.html", -// [&](coro_http::coro_http_request &, coro_http::coro_http_response -// &resp) { -// resp.set_status_and_content(coro_http::status_type::ok, http_body); -// }); -// server.add_subserver(std::move(dispatcher), std::move(http_server)); -// auto res = server.async_start(); -// CHECK_MESSAGE(!res.hasResult(), "server start failed"); -// coro_rpc_client client(coro_io::get_global_executor()); -// bool is_ok = client.init_ibv(); -// CHECK(client.init_ssl("../openssl_files", "server.crt")); -// REQUIRE_MESSAGE(is_ok,"init ibv failed"); -// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); -// REQUIRE_MESSAGE(!ec, -// std::to_string(client.get_client_id()).append(ec.message())); -// auto ret = syncAwait(client.call()); -// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); -// coro_http::coro_http_client cli; -// auto result = syncAwait(cli.connect("localhost:8810")); -// CHECK_MESSAGE(!result.net_err, result.net_err.message()); -// result = syncAwait(cli.async_get("/index.html")); -// CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, -// result.status); -// CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); -// } -#endif -#endif +/* + * Copyright (c) 2023, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include +#include + +#include "ServerTester.hpp" +#include "async_simple/coro/Lazy.h" +#include "cinatra/response_cv.hpp" +#include "doctest.h" +#include "rpc_api.hpp" +#include "ylt/coro_http/coro_http_client.hpp" +#include "ylt/coro_http/coro_http_server.hpp" +#include "ylt/coro_io/coro_io.hpp" +#include "ylt/coro_rpc/impl/default_config/coro_rpc_config.hpp" +#include "ylt/coro_rpc/impl/errno.h" +#include "ylt/struct_pack.hpp" + +async_simple::coro::Lazy get_coro_value(int val) { co_return val; } + +struct CoroServerTester : ServerTester { + CoroServerTester(TesterConfig config) + : ServerTester(config), + server(2, config.port, config.address, config.conn_timeout_duration) { +#ifdef YLT_ENABLE_SSL + if (use_ssl) { + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); + } +#endif +#ifdef YLT_ENABLE_IBV + if (use_rdma) { + server.init_ibv(); + } +#endif + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + } + ~CoroServerTester() { server.stop(); } + + async_simple::coro::Lazy get_value(int val) { co_return val; } + + void test_set_server_address() { + { + coro_rpc_server server(1, 9001); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "0.0.0.0"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "127.0.0.1"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "localhost"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, "0.0.0.0:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, "127.0.0.1:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, "localhost:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(!server.get_errc()); + } + + { + coro_rpc_server server(1, 9001, "x.x.x.x"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(server.get_errc() == coro_rpc::errc::bad_address); + } + + { + coro_rpc_server server(1, "x.x.x.x:9001"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(server.get_errc() == coro_rpc::errc::bad_address); + } + + { + coro_rpc_server server(1, "127.0.0.1:aaa"); + [[maybe_unused]] auto r = server.async_start(); + CHECK(server.get_errc() == coro_rpc::errc::bad_address); + } + } + + void test_all() override { + g_action = {}; + ELOGV(INFO, "run %s", __func__); + test_set_server_address(); + test_coro_handler(); + ServerTester::test_all(); + test_function_not_registered(); + test_start_new_server_with_same_port(); + test_server_send_bad_rpc_result(); + test_server_send_no_body(); + test_context_func(); + test_return_err_by_throw_exception(); + this->test_call_with_delay_func(); + this->test_call_with_delay_func< + coro_fun_with_user_define_connection_type>(); + this->test_call_with_delay_func(); + this->test_call_with_delay_func_client_read_length_error< + coro_fun_with_delay_return_void>(); + this->test_call_with_delay_func_client_read_body_error< + coro_fun_with_delay_return_void>(); + if (enable_heartbeat) { + this->test_call_with_delay_func_server_timeout< + coro_fun_with_delay_return_void_cost_long_time>(); + } + this->test_call_with_delay_func(); + this->test_call_with_delay_func(); + } + void register_all_function() override { + g_action = {}; + ELOGV(INFO, "run %s", __func__); + server.register_handler(); + server.register_handler(); + server.register_handler<&ns_login::LoginService::login>(&login_service_); + server.register_handler<&HelloService::hello>(&hello_service_); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler(); + server.register_handler<&HelloService::coro_func, + &HelloService::coro_func_return_void>( + &hello_service_); + server.register_handler(); + server.register_handler<&CoroServerTester::get_value>(this); + } + + void test_context_func() { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + client->set_req_attachment("1234567890987654321234567890"); + auto result = syncAwait(client->call()); + CHECK(result); + CHECK(client->get_resp_attachment() == "1234567890987654321234567890"); + + client->set_req_attachment("12345678909876543212345678901"); + result = syncAwait(client->call()); + CHECK(result); + CHECK(client->get_resp_attachment() == "12345678909876543212345678901"); + + client->set_req_attachment("01234567890987654321234567890"); + result = syncAwait(client->call()); + CHECK(result); + CHECK(client->get_resp_attachment() == "01234567890987654321234567890"); + } + void test_return_err_by_throw_exception() { + { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto result = syncAwait(client->call()); + REQUIRE(!result); + CHECK(result.error().code == coro_rpc::errc::address_in_used); + CHECK(result.error().msg == "error with user-defined msg"); + } + { + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto result = syncAwait(client->call()); + REQUIRE(!result); + CHECK(result.error().code == coro_rpc::errc::address_in_used); + CHECK(result.error().msg == "error with user-defined msg"); + } + } + + void test_function_not_registered() { + g_action = {}; + auto client = create_client(); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::function_not_registered, + std::to_string(client->get_client_id()).append(ret.error().msg)); + REQUIRE(client->has_closed() == true); + ret = call(client); + CHECK(client->has_closed() == true); + ret = call(client); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + CHECK(client->has_closed() == true); + } + + void test_server_start_again() { + ELOGV(INFO, "run %s", __func__); + + auto ec = server.start(); + REQUIRE_MESSAGE(ec == coro_rpc::errc::io_error, ec.message()); + } + + void test_start_new_server_with_same_port() { + ELOGV(INFO, "run %s", __func__); + { + auto new_server = coro_rpc_server(2, std::stoi(this->port_)); + auto ec = new_server.async_start(); + REQUIRE(ec.hasResult()); + REQUIRE_MESSAGE(ec.value() == coro_rpc::errc::address_in_used, + ec.value().message()); + } + ELOGV(INFO, "OH NO"); + } + void test_server_send_bad_rpc_result() { + auto client = create_client(inject_action::server_send_bad_rpc_result); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = this->call(client); + CHECK_MESSAGE( + ret.error().code == coro_rpc::errc::invalid_rpc_result, + std::to_string(client->get_client_id()).append(ret.error().msg)); + g_action = {}; + } + + void test_server_send_no_body() { + auto client = create_client(inject_action::close_socket_after_send_length); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = this->template call(client); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client->get_client_id()).append(ret.error().msg)); + g_action = {}; + } + + void test_coro_handler() { + auto client = create_client(inject_action::nothing); + ELOGV(INFO, "run %s, client_id %d", __func__, client->get_client_id()); + auto ret = this->template call(client, 42); + CHECK(ret.value() == 42); + + auto ret1 = this->template call<&HelloService::coro_func>(client, 42); + CHECK(ret1.value() == 42); + + auto ret2 = this->template call<&CoroServerTester::get_value>(client, 42); + CHECK(ret2.value() == 42); + + auto ret3 = this->template call(client, 42); + CHECK(ret3.value() == 42); + + auto ret4 = this->template call(client, 42); + CHECK(ret4.has_value()); + + auto ret5 = + this->template call<&HelloService::coro_func_return_void>(client, 42); + CHECK(ret5.has_value()); + } + coro_rpc_server server; + std::thread thd; + HelloService hello_service_; +}; +TEST_CASE("testing coro rpc server") { + ELOGV(INFO, "run testing coro rpc server"); + unsigned short server_port = 8810; + auto conn_timeout_duration = 500ms; + std::vector switch_list{0 +#ifdef YLT_ENABLE_SSL + , + 1 +#endif +#ifdef YLT_ENABLE_IBV + , + 2 +#endif + }; + for (auto enable_heartbeat : switch_list) { + for (auto type : switch_list) { + TesterConfig config; + config.enable_heartbeat = enable_heartbeat; + if (type == 0) { + config.use_ssl = false; + config.use_rdma = false; + } + else if (type == 1) { + config.use_ssl = true; + config.use_rdma = false; + } + else if (type == 2) { + config.use_ssl = false; + config.use_rdma = true; + } + config.sync_client = false; + config.use_outer_io_context = false; + config.port = server_port; + if (enable_heartbeat) { + config.conn_timeout_duration = conn_timeout_duration; + } + std::stringstream ss; + ss << config; + ELOGV(INFO, "config: %s", ss.str().data()); + CoroServerTester(config).run(); + } + // } + } +} + +TEST_CASE("testing coro rpc server stop") { + ELOGV(INFO, "run testing coro rpc server stop"); + coro_rpc_server server(2, 8810); + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + SUBCASE("stop twice") { + server.stop(); + server.stop(); + } + SUBCASE("stop in different thread") { + std::thread thd1([&server]() { + server.stop(); + }); + std::thread thd2([&server]() { + server.stop(); + }); + thd1.join(); + thd2.join(); + } +} + +TEST_CASE("test server accept error") { + ELOGV(INFO, "run test server accept error"); + g_action = inject_action::force_inject_server_accept_error; + coro_rpc_server server(2, 8810); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + coro_rpc_client client(coro_io::get_global_executor()); + ELOGV(INFO, "run test server accept error, client_id %d", + client.get_client_id()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.error().code == coro_rpc::errc::io_error, + ret.error().msg); + REQUIRE(client.has_closed() == true); + g_action = {}; +} + +TEST_CASE("test server write queue") { + using coro_rpc_protocol = coro_rpc::protocol::coro_rpc_protocol; + ELOGV(INFO, "run server write queue"); + g_action = {}; + coro_rpc_server server(2, 8810); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start timeout"); + std::string buffer; + buffer.reserve(coro_rpc_protocol::REQ_HEAD_LEN + + struct_pack::get_needed_size(std::monostate{})); + buffer.resize(coro_rpc_protocol::REQ_HEAD_LEN); + auto &header = *(coro_rpc_protocol::req_header *)buffer.data(); + header.magic = coro_rpc_protocol::magic_number; + header.function_id = + func_id(); + header.seq_num = g_client_id++; + header.length = struct_pack::get_needed_size(std::monostate{}); + ELOGV(INFO, "client_id %d begin to connect %d", header.seq_num, 8820); + struct_pack::serialize_to(buffer, std::monostate{}); + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + asio::ip::tcp::socket socket(io_context); + auto ret = coro_io::connect(io_context, socket, "127.0.0.1", "8810"); + CHECK(!ret); + ELOGV(INFO, "%s client_id %d call %s", "sync_client", header.seq_num, + "coro_fun_with_delay_return_void_cost_long_time"); + for (int i = 0; i < 1; ++i) { + auto err = + coro_io::write(socket, asio::buffer(buffer.data(), buffer.size())); + CHECK(err.second == buffer.size()); + } + for (int i = 0; i < 1; ++i) { + char buffer2[coro_rpc_protocol::RESP_HEAD_LEN]; + std::monostate r; + auto buf = struct_pack::serialize(r); + std::string buffer_read; + buffer_read.resize(buf.size()); + read(socket, asio::buffer(buffer2, coro_rpc_protocol::RESP_HEAD_LEN)); + [[maybe_unused]] auto resp_head = + *(coro_rpc_protocol::resp_header *)buffer2; + uint32_t body_len = header.length; + CHECK(body_len == buf.size()); + read(socket, asio::buffer(buffer_read, body_len)); + std::monostate r2; + std::size_t sz; + auto ret = + struct_pack::deserialize_to(r2, buffer_read.data(), body_len, sz); + CHECK(!ret); + CHECK(sz == body_len); + CHECK(r2 == r); + } + + ELOGV(INFO, "client_id %d close", header.seq_num); + asio::error_code ignored_ec; + socket.shutdown(asio::ip::tcp::socket::shutdown_both, ignored_ec); + socket.close(ignored_ec); + io_context.stop(); + thd.join(); + server.stop(); +} + +TEST_CASE("testing coro rpc write error") { + ELOGV(INFO, "run testing coro rpc write error"); + g_action = inject_action::force_inject_connection_close_socket; + coro_rpc_server server(2, 8810); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + ELOGV(INFO, "run testing coro rpc write error, client_id %d", + client.get_client_id()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE( + ret.error().code == coro_rpc::errc::io_error, + std::to_string(client.get_client_id()).append(ret.error().msg)); + REQUIRE(client.has_closed() == true); + g_action = inject_action::nothing; +} + +TEST_CASE("testing ipv6") { + using namespace coro_http; + coro_rpc_server server(1, "[::1]:8811"); + server.register_handler(); + auto res = server.async_start(); + if (res.hasResult()) { + return; + } + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client{}; + auto ec = syncAwait(client.connect("[::1]:8811")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + CHECK(ret); + + coro_rpc_client client1{}; + ec = syncAwait(client1.connect("::1:8811")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + ret = syncAwait(client1.call()); + CHECK(ret); + + coro_http::coro_http_server http_server(1, 8812, "::1"); + http_server.set_http_handler( + "/test", [](coro_http_request &req, coro_http_response &resp) { + resp.set_status_and_content(status_type::ok, "ok"); + }); + http_server.async_start(); + coro_http::coro_http_client htttp_client{}; + auto r = syncAwait(htttp_client.connect("http://[::1]:8812")); + CHECK(!r.net_err); + auto result = htttp_client.get("/test"); + CHECK(result.status == 200); + + coro_http::coro_http_client htttp_client1{}; + result = htttp_client1.get("http://[::1]:8812/test"); + CHECK(result.status == 200); +} + +TEST_CASE("testing coro rpc subserver") { + ELOGV(INFO, "run testing coro rpc subserver"); + std::string http_body = R"( + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ +)"; + coro_rpc_server server(2, 8810); + server.register_handler(); + std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, + std::string_view magic_number, + coro_http::coro_http_server &server) { + CHECK(magic_number == "G"); + server.transfer_connection(std::move(soc), magic_number); + }; + auto http_server = std::make_unique(0, 0); + http_server->set_http_handler( + "/index.html", + [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { + resp.set_status_and_content(coro_http::status_type::ok, http_body); + }); + server.add_subserver(std::move(dispatcher), std::move(http_server)); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); + coro_http::coro_http_client cli; + auto result = syncAwait(cli.connect("localhost:8810")); + CHECK_MESSAGE(!result.net_err, result.net_err.message()); + result = syncAwait(cli.async_get("/index.html")); + CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, + result.status); + CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +} +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing coro rpc ssl subserver") { + ELOGV(INFO, "run testing coro rpc ssl subserver"); + std::string http_body = R"( + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ +)"; + coro_rpc_server server(2, 8810); + server.init_ssl( + ssl_configure{"../openssl_files", "server.crt", "server.key"}); + server.register_handler(); + std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, + std::string_view magic_number, + coro_http::coro_http_server &server) { + CHECK(magic_number == "G"); + server.transfer_connection(std::move(soc), magic_number); + }; + auto http_server = std::make_unique(0, 0); + http_server->set_http_handler( + "/index.html", + [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { + resp.set_status_and_content(coro_http::status_type::ok, http_body); + }); + server.add_subserver(std::move(dispatcher), std::move(http_server)); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + CHECK(client.init_ssl("../openssl_files", "ca.crt", "127.0.0.1")); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); + coro_http::coro_http_client cli; + CHECK_MESSAGE(cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"), + "init ssl failed"); + auto result = syncAwait(cli.connect("https://localhost:8810")); + CHECK_MESSAGE(!result.net_err, result.net_err.message()); + result = syncAwait(cli.async_get("/index.html")); + CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, + result.net_err.message()); + CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +} +#ifdef YLT_ENABLE_IBV +TEST_CASE("testing coro rpc non-rdma client to rdma server") { + ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); + coro_rpc_server server(2, 8810); + server.init_ibv(); + server.register_handler(); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); +} +TEST_CASE("testing coro rpc subserver with rdma") { + ELOGV(INFO, "run testing coro rpc subserver with rdma"); + std::string http_body = R"( + + + + Example + + +

This is an example of a simple HTML page with one paragraph.

+ +)"; + coro_rpc_server server(2, 8810); + server.init_ibv(); + server.register_handler(); + std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, + std::string_view magic_number, + coro_http::coro_http_server &server) { + CHECK(magic_number == "G"); + server.transfer_connection(std::move(soc), magic_number); + }; + auto http_server = std::make_unique(0, 0); + http_server->set_http_handler( + "/index.html", + [&](coro_http::coro_http_request &, coro_http::coro_http_response &resp) { + resp.set_status_and_content(coro_http::status_type::ok, http_body); + }); + server.add_subserver(std::move(dispatcher), std::move(http_server)); + auto res = server.async_start(); + CHECK_MESSAGE(!res.hasResult(), "server start failed"); + coro_rpc_client client(coro_io::get_global_executor()); + bool is_ok = client.init_ibv(); + REQUIRE_MESSAGE(is_ok, "init ibv failed"); + auto ec = syncAwait(client.connect("127.0.0.1", "8810")); + REQUIRE_MESSAGE(!ec, + std::to_string(client.get_client_id()).append(ec.message())); + auto ret = syncAwait(client.call()); + REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); + coro_http::coro_http_client cli; + auto result = syncAwait(cli.connect("localhost:8810")); + CHECK_MESSAGE(!result.net_err, result.net_err.message()); + result = syncAwait(cli.async_get("/index.html")); + CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, + result.status); + CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +} +#ifdef YLT_ENABLE_SSL + +// TODO: open it when whe support rdma client with ssl-encrypt connection + +// TEST_CASE("testing coro rpc non-rdma ssl client to ssl rdma server") { +// ELOGV(INFO, "run testing coro rpc non-rdma client to rdma server"); +// coro_rpc_server server(2, 8810); +// server.init_ibv(); +// server.init_ssl( +// ssl_configure{"../openssl_files", "server.crt", "server.key"}); +// server.register_handler(); +// auto res = server.async_start(); +// CHECK_MESSAGE(!res.hasResult(), "server start failed"); +// coro_rpc_client client(coro_io::get_global_executor()); +// CHECK(client.init_ssl("../openssl_files", "server.crt")); +// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); +// REQUIRE_MESSAGE(!ec, +// std::to_string(client.get_client_id()).append(ec.message())); +// auto ret = syncAwait(client.call()); +// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); +// } + +// TEST_CASE("testing ssl coro rpc subserver with rdma") { +// ELOGV(INFO, "run testing ssl coro rpc subserver with rdma"); +// std::string http_body = R"( +// +// +// +// Example +// +// +//

This is an example of a simple HTML page with one paragraph.

+// +// )"; +// coro_rpc_server server(2, 8810); +// server.init_ssl( +// ssl_configure{"../openssl_files", "server.crt", "server.key"}); +// server.init_ibv(); +// server.register_handler(); +// std::function dispatcher = [](coro_io::socket_wrapper_t &&soc, +// std::string_view magic_number, +// coro_http::coro_http_server &server) { +// CHECK(magic_number == "G"); +// server.transfer_connection(std::move(soc), magic_number); +// }; +// auto http_server = std::make_unique(0, 0); +// http_server->set_http_handler( +// "/index.html", +// [&](coro_http::coro_http_request &, coro_http::coro_http_response +// &resp) { +// resp.set_status_and_content(coro_http::status_type::ok, http_body); +// }); +// server.add_subserver(std::move(dispatcher), std::move(http_server)); +// auto res = server.async_start(); +// CHECK_MESSAGE(!res.hasResult(), "server start failed"); +// coro_rpc_client client(coro_io::get_global_executor()); +// bool is_ok = client.init_ibv(); +// CHECK(client.init_ssl("../openssl_files", "server.crt")); +// REQUIRE_MESSAGE(is_ok,"init ibv failed"); +// auto ec = syncAwait(client.connect("127.0.0.1", "8810")); +// REQUIRE_MESSAGE(!ec, +// std::to_string(client.get_client_id()).append(ec.message())); +// auto ret = syncAwait(client.call()); +// REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); +// coro_http::coro_http_client cli; +// auto result = syncAwait(cli.connect("localhost:8810")); +// CHECK_MESSAGE(!result.net_err, result.net_err.message()); +// result = syncAwait(cli.async_get("/index.html")); +// CHECK_MESSAGE(result.status == (int)coro_http::status_type::ok, +// result.status); +// CHECK_MESSAGE(result.resp_body == http_body, result.resp_body); +// } +#endif +#endif #endif \ No newline at end of file diff --git a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp index fd3ec7e5e..2e82b7d8f 100644 --- a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp +++ b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp @@ -1,190 +1,212 @@ -/* - * Copyright (c) 2025, Alibaba Group Holding Limited; - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include -#include -#include - -#include "doctest.h" - -using namespace coro_rpc; -using namespace async_simple::coro; - -const std::string CERT_PATH = "../openssl_files"; -const std::string SERVER_CERT = "server.crt"; -const std::string SERVER_KEY = "server.key"; -const std::string CLIENT_CERT = "client.crt"; -const std::string CLIENT_KEY = "client.key"; -const std::string CA_CERT = "ca.crt"; - -namespace { -std::string hello_ssl_test() { return "Hello World"; } -} - -#ifdef YLT_ENABLE_SSL -TEST_CASE("testing RPC SSL one-way authentication") { - coro_rpc_server server(2, 8801); - - ssl_configure ssl_conf; - ssl_conf.base_path = CERT_PATH; - ssl_conf.cert_file = SERVER_CERT; - ssl_conf.key_file = SERVER_KEY; - ssl_conf.ca_cert_file = ""; - ssl_conf.enable_client_verify = false; - - server.init_ssl(ssl_conf); - server.register_handler(); - - asio::io_context io_context; - std::thread thd([&io_context]() { - asio::io_context::work work(io_context); - io_context.run(); - }); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client; - bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); - - auto ec = syncAwait(client.connect("127.0.0.1", "8801")); - REQUIRE_MESSAGE(!ec, "connect failed"); - - auto result = syncAwait(client.call()); - REQUIRE_MESSAGE(result, "call failed"); - REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); - - server.stop(); - io_context.stop(); - thd.join(); -} - -TEST_CASE("testing RPC SSL mutual authentication - success") { - coro_rpc_server server(2, 8802); - - ssl_configure ssl_conf; - ssl_conf.base_path = CERT_PATH; - ssl_conf.cert_file = SERVER_CERT; - ssl_conf.key_file = SERVER_KEY; - ssl_conf.ca_cert_file = CA_CERT; - ssl_conf.enable_client_verify = true; - - server.init_ssl(ssl_conf); - server.register_handler(); - - asio::io_context io_context; - std::thread thd([&io_context]() { - asio::io_context::work work(io_context); - io_context.run(); - }); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client; - bool init_ok = - client.init_ssl(CERT_PATH, CA_CERT, CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); - - auto ec = syncAwait(client.connect("127.0.0.1", "8802")); - REQUIRE_MESSAGE(!ec, "connect failed"); - - auto result = syncAwait(client.call()); - REQUIRE_MESSAGE(result, "call failed"); - REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); - - server.stop(); - io_context.stop(); - thd.join(); -} - -TEST_CASE("testing RPC SSL mutual authentication - client without cert") { - coro_rpc_server server(2, 8803); - - ssl_configure ssl_conf; - ssl_conf.base_path = CERT_PATH; - ssl_conf.cert_file = SERVER_CERT; - ssl_conf.key_file = SERVER_KEY; - ssl_conf.ca_cert_file = CA_CERT; - ssl_conf.enable_client_verify = true; - - server.init_ssl(ssl_conf); - server.register_handler(); - - asio::io_context io_context; - std::thread thd([&io_context]() { - asio::io_context::work work(io_context); - io_context.run(); - }); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client; - bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); - - auto ec = syncAwait(client.connect("127.0.0.1", "8803")); - REQUIRE_MESSAGE(ec, "connect should fail without client cert"); - REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, - "error should be not_connected"); - - server.stop(); - io_context.stop(); - thd.join(); -} - -TEST_CASE("testing RPC SSL mutual authentication - client with invalid cert") { - coro_rpc_server server(2, 8804); - - ssl_configure ssl_conf; - ssl_conf.base_path = CERT_PATH; - ssl_conf.cert_file = SERVER_CERT; - ssl_conf.key_file = SERVER_KEY; - ssl_conf.ca_cert_file = CA_CERT; - ssl_conf.enable_client_verify = true; - - server.init_ssl(ssl_conf); - server.register_handler(); - - asio::io_context io_context; - std::thread thd([&io_context]() { - asio::io_context::work work(io_context); - io_context.run(); - }); - - auto res = server.async_start(); - REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); - - coro_rpc_client client; - bool init_ok = - client.init_ssl(CERT_PATH, CA_CERT, "fake.crt", "fake.key", "127.0.0.1"); - REQUIRE_MESSAGE(init_ok == true, "init ssl should succeed"); - - auto ec = syncAwait(client.connect("127.0.0.1", "8804")); - REQUIRE_MESSAGE(ec, "connect should fail with invalid client cert"); - - server.stop(); - io_context.stop(); - thd.join(); -} -#endif +/* + * Copyright (c) 2025, Alibaba Group Holding Limited; + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "doctest.h" + +using namespace coro_rpc; +using namespace async_simple::coro; + +const std::string CERT_PATH = "../openssl_files"; +const std::string SERVER_CERT = "server.crt"; +const std::string SERVER_KEY = "server.key"; +const std::string CLIENT_CERT = "client.crt"; +const std::string CLIENT_KEY = "client.key"; +const std::string CA_CERT = "ca.crt"; + +namespace { +std::string hello_ssl_test() { return "Hello World"; } +} + +#ifdef YLT_ENABLE_SSL +TEST_CASE("testing RPC SSL one-way authentication") { + coro_rpc_server server(2, 8801); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = ""; + ssl_conf.enable_client_verify = false; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8801")); + REQUIRE_MESSAGE(!ec, "connect failed"); + + auto result = syncAwait(client.call()); + REQUIRE_MESSAGE(result, "call failed"); + REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); + + server.stop(); + io_context.stop(); + thd.join(); +} + +TEST_CASE("testing RPC SSL mutual authentication - success") { + coro_rpc_server server(2, 8802); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; + ssl_conf.enable_client_verify = true; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = + client.init_ssl(CERT_PATH, CA_CERT, CLIENT_CERT, CLIENT_KEY, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8802")); + REQUIRE_MESSAGE(!ec, "connect failed"); + + auto result = syncAwait(client.call()); + REQUIRE_MESSAGE(result, "call failed"); + REQUIRE_MESSAGE(result.value() == "Hello World", "result mismatch"); + + server.stop(); + io_context.stop(); + thd.join(); +} + +TEST_CASE("testing RPC SSL mutual authentication - client without cert") { + coro_rpc_server server(2, 8803); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; + ssl_conf.enable_client_verify = true; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl fail"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8803")); + // Note: With TLS 1.3, connect() may succeed even when server requires + // client certificate. The server-side verification failure is logged. + // Check that either connect fails OR the subsequent RPC call fails. + if (ec) { + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, + "error should be not_connected"); + } + else { + // TLS handshake succeeded, but server rejected due to missing client cert + // The RPC call should fail + auto result = syncAwait(client.call()); + REQUIRE_MESSAGE(!result, "RPC call should fail without client cert"); + } + + server.stop(); + io_context.stop(); + thd.join(); +} + +TEST_CASE("testing RPC SSL mutual authentication - client with invalid cert") { + coro_rpc_server server(2, 8804); + + ssl_configure ssl_conf; + ssl_conf.base_path = CERT_PATH; + ssl_conf.cert_file = SERVER_CERT; + ssl_conf.key_file = SERVER_KEY; + ssl_conf.ca_cert_file = CA_CERT; + ssl_conf.enable_client_verify = true; + + server.init_ssl(ssl_conf); + server.register_handler(); + + asio::io_context io_context; + std::thread thd([&io_context]() { + asio::io_context::work work(io_context); + io_context.run(); + }); + + auto res = server.async_start(); + REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); + + coro_rpc_client client; + bool init_ok = + client.init_ssl(CERT_PATH, CA_CERT, "fake.crt", "fake.key", "127.0.0.1"); + REQUIRE_MESSAGE(init_ok == true, "init ssl should succeed"); + + auto ec = syncAwait(client.connect("127.0.0.1", "8804")); + // Note: With TLS 1.3, connect() may succeed even when server requires + // valid client certificate. The server-side verification failure is logged. + // Check that either connect fails OR the subsequent RPC call fails. + if (ec) { + REQUIRE_MESSAGE(ec == coro_rpc::errc::not_connected, + "error should be not_connected"); + } + else { + // TLS handshake succeeded, but server rejected due to invalid client cert + // The RPC call should fail + auto result = syncAwait(client.call()); + REQUIRE_MESSAGE(!result, "RPC call should fail with invalid client cert"); + } + + server.stop(); + io_context.stop(); + thd.join(); +} +#endif diff --git a/website/docs/en/coro_http/ssl_mutual_authentication.md b/website/docs/en/coro_http/ssl_mutual_authentication.md new file mode 100644 index 000000000..24d936d9a --- /dev/null +++ b/website/docs/en/coro_http/ssl_mutual_authentication.md @@ -0,0 +1,291 @@ +#HTTP SSL Mutual Authentication + +##Overview + + coro_http supports SSL / + TLS encrypted connections using OpenSSL.This document describes how + to configure both one - + way(server authentication) and + mutual(client and server authentication) SSL / TLS authentication. + + ##Enabling SSL Support + + After installing OpenSSL, + enable SSL support by setting the CMake option : + +```cmake option(YLT_ENABLE_SSL "Enable SSL support" ON) +``` + + Or manually add the `YLT_ENABLE_SSL` macro and link to OpenSSL. + + ##One + - + way SSL Authentication + + One + - + way SSL authentication(server authentication only) + ensures that the client verifies the server + 's certificate but doesn' t require the client to present a certificate. + + ## #Client + - + side Configuration + +```cpp +#include + + using namespace cinatra; + +coro_http_client client; +bool init_ok = + client.init_ssl(asio::ssl::verify_peer, // Verify server certificate + "./certs/", // Base path to certificates + "ca.crt", // CA certificate for server verification + "127.0.0.1" // SNI hostname + ); + +if (!init_ok) { + std::cerr << "Failed to initialize SSL" << std::endl; + return; +} + +// Make HTTPS request +auto result = client.get("https://127.0.0.1:9001/"); +if (result.status == 200) { + std::cout << "Response: " << result.resp_body << std::endl; +} +``` + + ## #Server - + side Configuration + +```cpp +#include + + using namespace cinatra; + +coro_http_server server(1, 9001); + +bool init_ok = server.init_ssl("./certs/server.crt", // Server certificate + "./certs/server.key", // Server private key + "", // No password + "", // No CA cert for one-way + false // Don't verify client +); + +if (!init_ok) { + std::cerr << "Failed to initialize SSL server" << std::endl; + return; +} + +server.set_http_handler("/", [](coro_http_request& req, + coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello SSL!"); +}); + +server.sync_start(); +``` + +## Mutual SSL Authentication + +Mutual SSL authentication (mTLS) requires both client and server to present valid certificates, providing strong security for sensitive applications. + +### Client-side Configuration + +```cpp +#include + +using namespace cinatra; + +coro_http_client client; +bool init_ok = + client.init_ssl(asio::ssl::verify_peer, // Verify server certificate + "./certs/", // Base path to certificates + "ca.crt", // CA certificate for server verification + "client.crt", // Client certificate + "client.key", // Client private key + "127.0.0.1" // SNI hostname + ); + +if (!init_ok) { + std::cerr << "Failed to initialize SSL client" << std::endl; + return; +} + +// Make HTTPS request with mutual authentication +auto result = client.get("https://127.0.0.1:9002/"); +if (result.status == 200) { + std::cout << "Mutual auth successful: " << result.resp_body << std::endl; +} +``` + + ## #Server - + side Configuration + +```cpp +#include + + using namespace cinatra; + +coro_http_server server(1, 9002); + +bool init_ok = + server.init_ssl("./certs/server.crt", // Server certificate + "./certs/server.key", // Server private key + "", // No password + "./certs/ca.crt", // CA certificate for verifying clients + true // Verify client certificate (mandatory) + ); + +if (!init_ok) { + std::cerr << "Failed to initialize SSL server" << std::endl; + return; +} + +server.set_http_handler("/", [](coro_http_request& req, + coro_http_response& resp) { + resp.set_status_and_content(status_type::ok, "Hello Mutual Auth!"); +}); + +server.sync_start(); +``` + + ##Certificate Management + + ## #Generating Test Certificates + + For testing purposes, + you can generate self - + signed certificates using OpenSSL : + + ####Generate CA Certificate + +```bash +#Generate CA private key + openssl genrsa - + out ca.key 2048 + +#Generate CA certificate + openssl req - + new - x509 - days 3650 - key ca.key - out ca.crt - + subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=TestCA" +``` + + ####Generate Server Certificate + +```bash +#Generate server private key + openssl genrsa - + out server.key 2048 + +#Generate server certificate signing request + openssl req - + new - key server.key - out server.csr - + subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=127.0.0.1" + +#Sign server certificate with CA + openssl x509 - + req - days 3650 - in server.csr - CA ca.crt - CAkey ca.key - + CAcreateserial - + out server.crt +``` + + ####Generate Client Certificate + +```bash +#Generate client private key + openssl genrsa - + out client.key 2048 + +#Generate client certificate signing request + openssl req - + new - key client.key - out client.csr - + subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=TestClient" + +#Sign client certificate with CA + openssl x509 - + req - days 3650 - in client.csr - CA ca.crt - CAkey ca.key - + CAcreateserial - + out client.crt +``` + + ## #Certificate Requirements + + - + **Server Certificate * + * : Must contain the server's IP address or hostname in the Common Name (CN) or Subject Alternative Name (SAN) - + **Client Certificate * + * : Must be signed by the CA certificate specified in the + server's `ca_cert_file` - **CA Certificate * + * : The root CA certificate that signed both + server and client certificates - + **Certificate Chain ** : For production use, + ensure proper certificate chains are configured + + ##Verification Modes + + The `init_ssl` function supports different verification modes : + +```cpp const int verify_none = asio::ssl::verify_none; // No verification +const int verify_peer = asio::ssl::verify_peer; // Verify peer certificate +const int verify_fail_if_no_peer_cert = + asio::ssl::verify_fail_if_no_peer_cert; // Fail if no peer cert +const int verify_client_once = + asio::ssl::verify_client_once; // Verify client once +``` + +**Recommended Settings**: +- **One-way auth**: `asio::ssl::verify_peer` +- **Mutual auth**: `asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert` (automatically set when `enable_client_verify=true` on server) + +## Best Practices + +1. **Use Strong Certificates**: In production, use certificates signed by a trusted CA +2. **Proper Hostname Matching**: Ensure the hostname parameter matches the certificate CN +3. **Secure Private Keys**: Never commit private keys to version control +4. **Certificate Rotation**: Implement procedures for rotating certificates before expiration +5. **Error Handling**: Always check the return value of `init_ssl()` and handle errors appropriately +6. **Testing**: Test both successful and failed authentication scenarios + +## Troubleshooting + +### Connection Failures + +If the client fails to connect: + +1. **Check Certificate Validity**: Ensure certificates are not expired +2. **Verify Hostname**: Ensure the SNI hostname matches the certificate CN +3. **Check CA Chain**: Ensure the CA certificate is correct and includes the full chain +4. **Verify File Paths**: Ensure certificate file paths are correct and accessible + +### Certificate Verification Errors + +Common errors and solutions: + +- **"certificate verify failed"**: CA certificate doesn't match or is missing +- **"hostname mismatch"**: SNI hostname doesn't match certificate CN +- **"unable to get local issuer certificate"**: CA certificate not provided +- **"no shared cipher"**: SSL/TLS version or cipher suite mismatch + +### Debug Mode + +Enable SSL debugging for troubleshooting: + +```cpp +#ifdef YLT_ENABLE_SSL +// Set SSL debug mode (environment variable) +// On Linux/Mac: +putenv("OPENSSL_DEBUG=1"); + +// Or use detailed error messages +SSL_load_error_strings(); +ERR_print_errors_fp(stderr); +#endif +``` + +## Complete Example + +See the complete example files: +- HTTP SSL server: [src/coro_http/examples/http_ssl_server.cpp](https://github.com/alibaba/yalantinglibs/blob/main/src/coro_http/examples/http_ssl_server.cpp) +- HTTP SSL client: [src/coro_http/examples/http_ssl_client.cpp](https://github.com/alibaba/yalantinglibs/blob/main/src/coro_http/examples/http_ssl_client.cpp) diff --git a/website/docs/en/coro_rpc/coro_rpc_client.md b/website/docs/en/coro_rpc/coro_rpc_client.md index dacb34eab..f330de1d0 100644 --- a/website/docs/en/coro_rpc/coro_rpc_client.md +++ b/website/docs/en/coro_rpc/coro_rpc_client.md @@ -1,458 +1,458 @@ -# Introduction to coro_rpc Client - -## Base Usage - -class `coro_rpc::coro_rpc_client` is the client side of coro_rpc, allowing users to send RPC requests to the server. - -Below, we will demonstrate the basic usage of rpc_client. - -```cpp -using namespace async_simple; -using namespace coro_rpc; -int add(int a,int b); -Lazy example() { - coro_rpc_client client; - coro_rpc::err_code ec = co_await client.connect("localhost:9001"); - if (ec) { /*check if connection error*/ - std::cout<(1,2); - /*rpc_result is type of expected, which T is rpc return type*/ - if (!result.has_value()) { - /*call result.error() to get rpc error message*/ - std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); -} -Lazy example(coro_rpc_client& client, std::string_view attachment) { - client.set_req_attachment(attachment); - rpc_result result = co_await client.call(); - if (result.has_value()) { - assert(result.get_resp_attachment()==attachment); - co_return std::move(result.release_resp_attachment()); - } - co_return ""; -} -``` - -By default, the RPC client will wait for 5 seconds after sending a request/establishing a connection. If no response is received after 5 seconds, it will return a timeout error. Users can also customize the wait duration by calling the `call_for` function. - -```cpp -client.connect("127.0.0.1:9001", std::chrono::seconds{10}); -auto result = co_await client.call_for(std::chrono::seconds{10},1,2); -assert(result.value() == 3); -``` - -The duration can be any `std::chrono::duration` type, common examples include `std::chrono::seconds` and `std::chrono::milliseconds`. Notably, if the duration is set to zero, it indicates that the function call will never time out. - -## SSL support - -coro_rpc supports using OpenSSL to encrypt connections. After installing OpenSSL and importing yalantinglibs into your project with CMake's `find_package` or `fetch_content`, you can enable SSL support by setting the CMake option YLT_ENABLE_SSL=ON. Alternatively, you might manually add the YLT_ENABLE_SSL macro and manually link to OpenSSL. - -Once SSL support has been enabled, users can invoke the `init_ssl` function before establishing a connection to the server. This will create an encrypted link between the client and the server. Its important to note that the coro_rpc server must also be compiled with SSL support enabled, and the `init_ssl` method must be called to enable SSL support before starting the server. - -For one -way SSL authentication(server authentication only),you can use the `init_ssl` function before establishing a connection:The first parameter is the base path where the SSL certificates are located, and the second parameter is the CA certificate filename for verifying the server. - -Server-side configuration: - -```cpp -coro_rpc_server server(2, 9000); -ssl_configure ssl_conf; -ssl_conf.base_path = "./certs"; -ssl_conf.cert_file = "server.crt"; -ssl_conf.key_file = "server.key"; -ssl_conf.ca_cert_file = ""; // Empty for one-way authentication -ssl_conf.enable_client_verify = false; // Disable client certificate verification - -server.init_ssl(ssl_conf); -server.register_handler(); -server.start(); -``` - -Mutual SSL Authentication For mutual SSL authentication(both client and server authentication),the client needs to provide its own certificate : - -```cpp - // Client-side mutual authentication - client.init_ssl("./", // Base path - "ca.crt", // CA certificate for server verification - "client.crt", // Client certificate - "client.key", // Client private key - "127.0.0.1" // Server hostname for SNI - ); -``` - -Server-side configuration for mutual authentication: - -```cpp -coro_rpc_server server(2, 9000); -ssl_configure ssl_conf; -ssl_conf.base_path = "./certs"; -ssl_conf.cert_file = "server.crt"; -ssl_conf.key_file = "server.key"; -ssl_conf.ca_cert_file = "ca.crt"; // CA certificate for verifying clients -ssl_conf.enable_client_verify = true; // Enable mandatory client certificate verification - -server.init_ssl(ssl_conf); -server.register_handler(); -server.start(); -``` - -**Important Notes**: -- The `enable_client_verify` flag enables mandatory client certificate verification -- When mutual authentication is enabled, clients must provide a valid certificate signed by the CA specified in `ca_cert_file` -- The hostname parameter must match the Common Name (CN) in the server certificate - -We also support NTLS if you enable it by CMAKE OPTION `YLT_ENABLE_NTLS`. - -## Conversion and compile-time checking of RPC parameters - -coro_rpc will perform compile-time checks on the validity of arguments during invocation. For example, for the following rpc function: - -```cpp -inline std::string echo(std::string str) { return str; } -``` - -Next, when the current client invokes the rpc function: - -```cpp -client.call(42); // Parameter does not match, compilation error -client.call(); // Missing parameter, compilation error -client.call("", 0); // Extra parameters, compilation error -client.call("hello, coro_rpc"); // The string literal can be converted to std::string, compilation succeeds -``` - -## Connection Options - -The `coro_rpc_client` provides an `init_config` function to configure connection options. The code below lists all configurable options, which are optional with default values. - -```cpp -using namespace coro_rpc; -using namespace std::chrono; -void set_config(coro_rpc_client& client) { - uint64_t client_id; - std::chrono::milliseconds connect_timeout_duration; - std::chrono::milliseconds request_timeout_duration; - std::string host; - std::string port; - std::string local_ip; - client.init_config(config{ - .connect_timeout_duration = 5s, // Connection timeout duration - .request_timeout_duration = 5s, // Request timeout duration - .host = "localhost", // Server hostname - .port = "9001", // Server port - .local_ip = "", // Local IP address used to specify the local communication interface - .socket_config=std::variant{tcp_config{}}; // Specify transport protocol and its configuration. Supported protocols: TCP, SSL over TCP, RDMA - }); -} -``` - -### RDMA Socket Configuration - -The configuration for IBVerbs socket protocol is shown below: - -```cpp -struct ib_socket_t::config_t { - uint32_t cq_size = 128; // Maximum length of event notification queue - uint32_t recv_buffer_cnt = 8; // Number of buffers pre-submitted to receive queue. Each buffer defaults to 256KB, so a RDMA connection occupies 8MB memory immediately after establishment. More pending receive data will result in more buffers in the queue, up to max_recv_wr*buffer_size (where buffer_size is configured in buffer_pool). If upper layer doesn't consume data, sender will receive RNR (Receiver Not Ready) errors and retry continuously. - uint32_t send_buffer_cnt = 4; // default send buffer queue max size, - ibv_qp_type qp_type = IBV_QPT_RC; // Default QP type - ibv_qp_cap cap = {.max_send_wr = 32, // Maximum send queue length - .max_recv_wr = 32, // Maximum receive queue length - .max_send_sge = 3, // Maximum send scatter/gather elements. Only 1 needed without inline data. Use 3 segments when using inline data (default supports 3 scattered addresses) - .max_recv_sge = 1, // Maximum receive scatter/gather elements. 1 suffices with current buffer configuration - .max_inline_data = 256}; // If packet size < inline data threshold and NIC supports it, small packets bypass buffer copy and go directly to NIC - std::shared_ptr device; // Underlying IB device for RPC. Defaults to first device in list -}; -``` - -Simple RDMA activation examples: - -```cpp - coro_rpc_client cli; - cli.init_ibv(); // Use default configuration - cli.init_ibv({.recv_buffer_cnt=8}); // Use custom configuration -``` - -RDMA activation through config: - -```cpp - coro_rpc_client cli; - cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) -``` - -#### RDMA Device Configuration - -The `ib_device_t` manages the connection context and buffers required during the ibverbs transmission process. By default, it uses the global device `coro_io::get_global_ib_device()`, but users can also specify their own device. - -By modifying the configuration of `ib_device_t`, users can assign different network interfaces to RPC connections and use separate buffers. - -1. Modify the default device configuration -```cpp - // The configuration only takes effect on the first invocation - coro_io::get_global_ib_device({ - .buffer_pool_config = { - .buffer_size = 256 * 1024, // Buffer size - .max_memory_usage = 4 * 1024 * 1024, // Max memory usage (allocation fails beyond this limit) - .memory_usage_recorder = nullptr; // nullopt means that memory usage across different devices will be counted together. If you want the memory pool to have independent memory usage tracking, you should assign a non-null std::shared_ptr> as the recorder. - .idle_timeout = 5s // Buffers unused for this duration will be reclaimed - } - }); - // ... -``` - - 2. Specify the RDMA NIC to use when initializing the connection -```cpp - coro_rpc_client cli; - cli.init_ibv({ - .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); - }); -``` - - 3. Create and use your own `ib_device_t` -```cpp - auto dev = coro_io::ib_device_t::create({ - .dev_name = "", // If dev_name is empty, the first device in the device list will be used. - .port = 1, // Manually specify the NIC port number. - .use_best_gid_index = true, // Automatically find the best GID index for this device. - .gid_index = 0, // Manually specify the GID index; takes effect when automatic lookup is disabled or fails. - .buffer_pool_config = { - // ... - } - }); -``` - - 4. Query all currently successfully registered RDMA global devices -```cpp - // Get all devices - auto devices = coro_io::g_ib_device_manager(); - for (auto &dev: devices.get_dev_list()) { - std::cout << "name:" << dev.first; - // dev.second is a global std::shared_ptr - } -``` - -## Calling Model - -Each `coro_rpc_client` is bound to a specific IO thread. By default, it selects a connection via round-robin from the global IO thread pool. Users can also manually bind it to a specific IO thread. - -```cpp -auto executor=coro_io::get_global_executor(); -coro_rpc_client client(executor),client2(executor); - // Both clients are bound to the same IO thread. -``` - -Each time a coroutine-based IO task is initiated (such as `connect`, `call`, `send_request`), the client internally submits the IO event to the operating system. When the IO event is completed, the coroutine is then resumed on the bound IO thread to continue execution. For example, in the following code, the task switches to the IO thread for execution after calling connect. - -```cpp -/*run in thread 1*/ -coro_rpc_client cli; -co_await cli.connect("localhost:9001"); -/*run in thread 2*/ -do_something(); -``` - -## Connection Pool and Load Balancing - -`coro_io` offers a connection pool `client_pool` and a load balancer `channel`. Users can manage `coro_rpc`/`coro_http` connections through the `client_pool`, and can use `channel` to achieve load balancing among multiple hosts. For more details, please refer to the documentation of `coro_io`. - -## Connection Reuse - -The `coro_rpc_client` can achieve connection reuse through the `send_request` function. This function is thread-safe, allowing multiple threads to call the `send_request` method on the same client concurrently. The return value of the function is `Lazy>>`. The first `co_await` waits for the request to be sent, and the second `co_await` waits for the rpc result to return. - - -Connection reuse allows us to reduce the number of connections under high concurrency, eliminating the need to create new connections. It also improves the throughput of each connection. - -Here's a simple example code snippet: - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -std::string_view echo(std::string_view); -Lazy example(coro_rpc_client& client) { - // send request to the server - Lazy> handler = co_await client.send_request("Hello"); - // then wait server response - async_rpc_result result = co_await std::move(handler); - if (result) { - assert(result->result() == "Hello"); - } - else { - // error handle - std::cout< example(coro_rpc_client& client) { - std::vector>> handlers; - // First, send 10 requests consecutively - for (int i=0;i<10;++i) { - handlers.push_back(co_await client.send_request(std::to_string(i))); - } - // Next, wait for all the requests to return - std::vector> results = co_await collectAll(std::move(handlers)); - for (int i=0;i<10;++i) { - assert(results[i]->result() == std::to_string(i)); - } - co_return; -} -``` - -When sending data using the connection pool, we can also reuse a connection. To do this, you need to add the `coro_io::client_reuse_hint` parameter to instruct the connection pool to enable connection reuse. For more details, see the connection pool documentation. (TODO) - -```cpp -auto pool = coro_io::client_pool::create( - conf.url, pool_conf); -auto ret = co_await pool->send_request( - [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { - return client.send_request("hello"); - }); -if (ret.has_value()) { - auto result = co_await std::move(ret.value()); - if (result.has_value()) { - assert(result.value()=="hello"); - } -} -``` - -### Attachment - -When using the `send_request` method, since multiple requests might be sent simultaneously, we should not call the `set_req_attachment` method to send an attachment to the server, nor should we call the `get_resp_attachment` and `release_resp_attachment` methods to get the attachment returned by the server. - -Instead, we can set the attachment when sending a request by calling the `send_request_with_attachment` function. Additionally, we can retrieve the attachment by calling the `->get_attachment()` and `->release_buffer()` methods of `async_rpc_result`. - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -int add(int a, int b); -Lazy example(coro_rpc_client& client) { - auto handler = co_await client.send_request_with_attachment("Hello", 1, 2); - async_rpc_result result = co_await std::move(handler); - assert(result->result() == 3); - assert(result->get_attachment() == "Hello"); - co_return std::move(result->release_buffer().resp_attachment_buf_); -} -``` - - -### Execution order - -When the called rpc function is a coroutine rpc function or a callback rpc function, the rpc requests may not necessarily be executed in order. The server might execute multiple rpc requests simultaneously. -For example, suppose there is the following code: - -```cpp -using namespace async_simple::coro; -Lazy sleep(int seconds) { - co_await coro_io::sleep(1s * seconds); // Yield the coroutine here - co_return; -} -``` - -Server registration and startup: -```cpp -using namespace coro_rpc; -void start() { - coro_rpc_server server(/* thread = */1,/* port = */ 8801); - server.register_handler(); - server.start(); -} -``` - -The client consecutively calls the sleep function twice on the same connection, sleeping for 2 seconds the first time and 1 second the second time. -```cpp -using namespace async_simple::coro; -using namespace coro_rpc; -Lazy call() { - coro_rpc_client cli,cli2; - co_await cli.connect("localhost:8801"); - co_await cli2.connect("localhost:8801"); - auto handler1 = co_await cli.send_request(2); - auto handler2 = co_await cli.send_request(1); - auto handler3 = co_await cli2.send_request(0); - handler2.start([](auto&&){ - std::cout<<"handler2 return"< example() { + coro_rpc_client client; + coro_rpc::err_code ec = co_await client.connect("localhost:9001"); + if (ec) { /*check if connection error*/ + std::cout<(1,2); + /*rpc_result is type of expected, which T is rpc return type*/ + if (!result.has_value()) { + /*call result.error() to get rpc error message*/ + std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); +} +Lazy example(coro_rpc_client& client, std::string_view attachment) { + client.set_req_attachment(attachment); + rpc_result result = co_await client.call(); + if (result.has_value()) { + assert(result.get_resp_attachment()==attachment); + co_return std::move(result.release_resp_attachment()); + } + co_return ""; +} +``` + +By default, the RPC client will wait for 5 seconds after sending a request/establishing a connection. If no response is received after 5 seconds, it will return a timeout error. Users can also customize the wait duration by calling the `call_for` function. + +```cpp +client.connect("127.0.0.1:9001", std::chrono::seconds{10}); +auto result = co_await client.call_for(std::chrono::seconds{10},1,2); +assert(result.value() == 3); +``` + +The duration can be any `std::chrono::duration` type, common examples include `std::chrono::seconds` and `std::chrono::milliseconds`. Notably, if the duration is set to zero, it indicates that the function call will never time out. + +## SSL support + +coro_rpc supports using OpenSSL to encrypt connections. After installing OpenSSL and importing yalantinglibs into your project with CMake's `find_package` or `fetch_content`, you can enable SSL support by setting the CMake option YLT_ENABLE_SSL=ON. Alternatively, you might manually add the YLT_ENABLE_SSL macro and manually link to OpenSSL. + +Once SSL support has been enabled, users can invoke the `init_ssl` function before establishing a connection to the server. This will create an encrypted link between the client and the server. It’s important to note that the coro_rpc server must also be compiled with SSL support enabled, and the `init_ssl` method must be called to enable SSL support before starting the server. + +For one -way SSL authentication(server authentication only),you can use the `init_ssl` function before establishing a connection:The first parameter is the base path where the SSL certificates are located, and the second parameter is the CA certificate filename for verifying the server. + +Server-side configuration: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = ""; // Empty for one-way authentication +ssl_conf.enable_client_verify = false; // Disable client certificate verification + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +Mutual SSL Authentication For mutual SSL authentication(both client and server authentication),the client needs to provide its own certificate : + +```cpp + // Client-side mutual authentication + client.init_ssl("./", // Base path + "ca.crt", // CA certificate for server verification + "client.crt", // Client certificate + "client.key", // Client private key + "127.0.0.1" // Server hostname for SNI + ); +``` + +Server-side configuration for mutual authentication: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = "ca.crt"; // CA certificate for verifying clients +ssl_conf.enable_client_verify = true; // Enable mandatory client certificate verification + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +**Important Notes**: +- The `enable_client_verify` flag enables mandatory client certificate verification +- When mutual authentication is enabled, clients must provide a valid certificate signed by the CA specified in `ca_cert_file` +- The hostname parameter must match the Common Name (CN) in the server certificate + +We also support NTLS if you enable it by CMAKE OPTION `YLT_ENABLE_NTLS`. + +## Conversion and compile-time checking of RPC parameters + +coro_rpc will perform compile-time checks on the validity of arguments during invocation. For example, for the following rpc function: + +```cpp +inline std::string echo(std::string str) { return str; } +``` + +Next, when the current client invokes the rpc function: + +```cpp +client.call(42); // Parameter does not match, compilation error +client.call(); // Missing parameter, compilation error +client.call("", 0); // Extra parameters, compilation error +client.call("hello, coro_rpc"); // The string literal can be converted to std::string, compilation succeeds +``` + +## Connection Options + +The `coro_rpc_client` provides an `init_config` function to configure connection options. The code below lists all configurable options, which are optional with default values. + +```cpp +using namespace coro_rpc; +using namespace std::chrono; +void set_config(coro_rpc_client& client) { + uint64_t client_id; + std::chrono::milliseconds connect_timeout_duration; + std::chrono::milliseconds request_timeout_duration; + std::string host; + std::string port; + std::string local_ip; + client.init_config(config{ + .connect_timeout_duration = 5s, // Connection timeout duration + .request_timeout_duration = 5s, // Request timeout duration + .host = "localhost", // Server hostname + .port = "9001", // Server port + .local_ip = "", // Local IP address used to specify the local communication interface + .socket_config=std::variant{tcp_config{}}; // Specify transport protocol and its configuration. Supported protocols: TCP, SSL over TCP, RDMA + }); +} +``` + +### RDMA Socket Configuration + +The configuration for IBVerbs socket protocol is shown below: + +```cpp +struct ib_socket_t::config_t { + uint32_t cq_size = 128; // Maximum length of event notification queue + uint32_t recv_buffer_cnt = 8; // Number of buffers pre-submitted to receive queue. Each buffer defaults to 256KB, so a RDMA connection occupies 8MB memory immediately after establishment. More pending receive data will result in more buffers in the queue, up to max_recv_wr*buffer_size (where buffer_size is configured in buffer_pool). If upper layer doesn't consume data, sender will receive RNR (Receiver Not Ready) errors and retry continuously. + uint32_t send_buffer_cnt = 4; // default send buffer queue max size, + ibv_qp_type qp_type = IBV_QPT_RC; // Default QP type + ibv_qp_cap cap = {.max_send_wr = 32, // Maximum send queue length + .max_recv_wr = 32, // Maximum receive queue length + .max_send_sge = 3, // Maximum send scatter/gather elements. Only 1 needed without inline data. Use 3 segments when using inline data (default supports 3 scattered addresses) + .max_recv_sge = 1, // Maximum receive scatter/gather elements. 1 suffices with current buffer configuration + .max_inline_data = 256}; // If packet size < inline data threshold and NIC supports it, small packets bypass buffer copy and go directly to NIC + std::shared_ptr device; // Underlying IB device for RPC. Defaults to first device in list +}; +``` + +Simple RDMA activation examples: + +```cpp + coro_rpc_client cli; + cli.init_ibv(); // Use default configuration + cli.init_ibv({.recv_buffer_cnt=8}); // Use custom configuration +``` + +RDMA activation through config: + +```cpp + coro_rpc_client cli; + cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) +``` + +#### RDMA Device Configuration + +The `ib_device_t` manages the connection context and buffers required during the ibverbs transmission process. By default, it uses the global device `coro_io::get_global_ib_device()`, but users can also specify their own device. + +By modifying the configuration of `ib_device_t`, users can assign different network interfaces to RPC connections and use separate buffers. + +1. Modify the default device configuration +```cpp + // The configuration only takes effect on the first invocation + coro_io::get_global_ib_device({ + .buffer_pool_config = { + .buffer_size = 256 * 1024, // Buffer size + .max_memory_usage = 4 * 1024 * 1024, // Max memory usage (allocation fails beyond this limit) + .memory_usage_recorder = nullptr; // nullopt means that memory usage across different devices will be counted together. If you want the memory pool to have independent memory usage tracking, you should assign a non-null std::shared_ptr> as the recorder. + .idle_timeout = 5s // Buffers unused for this duration will be reclaimed + } + }); + // ... +``` + + 2. Specify the RDMA NIC to use when initializing the connection +```cpp + coro_rpc_client cli; + cli.init_ibv({ + .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); + }); +``` + + 3. Create and use your own `ib_device_t` +```cpp + auto dev = coro_io::ib_device_t::create({ + .dev_name = "", // If dev_name is empty, the first device in the device list will be used. + .port = 1, // Manually specify the NIC port number. + .use_best_gid_index = true, // Automatically find the best GID index for this device. + .gid_index = 0, // Manually specify the GID index; takes effect when automatic lookup is disabled or fails. + .buffer_pool_config = { + // ... + } + }); +``` + + 4. Query all currently successfully registered RDMA global devices +```cpp + // Get all devices + auto devices = coro_io::g_ib_device_manager(); + for (auto &dev: devices.get_dev_list()) { + std::cout << "name:" << dev.first; + // dev.second is a global std::shared_ptr + } +``` + +## Calling Model + +Each `coro_rpc_client` is bound to a specific IO thread. By default, it selects a connection via round-robin from the global IO thread pool. Users can also manually bind it to a specific IO thread. + +```cpp +auto executor=coro_io::get_global_executor(); +coro_rpc_client client(executor),client2(executor); + // Both clients are bound to the same IO thread. +``` + +Each time a coroutine-based IO task is initiated (such as `connect`, `call`, `send_request`), the client internally submits the IO event to the operating system. When the IO event is completed, the coroutine is then resumed on the bound IO thread to continue execution. For example, in the following code, the task switches to the IO thread for execution after calling connect. + +```cpp +/*run in thread 1*/ +coro_rpc_client cli; +co_await cli.connect("localhost:9001"); +/*run in thread 2*/ +do_something(); +``` + +## Connection Pool and Load Balancing + +`coro_io` offers a connection pool `client_pool` and a load balancer `channel`. Users can manage `coro_rpc`/`coro_http` connections through the `client_pool`, and can use `channel` to achieve load balancing among multiple hosts. For more details, please refer to the documentation of `coro_io`. + +## Connection Reuse + +The `coro_rpc_client` can achieve connection reuse through the `send_request` function. This function is thread-safe, allowing multiple threads to call the `send_request` method on the same client concurrently. The return value of the function is `Lazy>>`. The first `co_await` waits for the request to be sent, and the second `co_await` waits for the rpc result to return. + + +Connection reuse allows us to reduce the number of connections under high concurrency, eliminating the need to create new connections. It also improves the throughput of each connection. + +Here's a simple example code snippet: + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +std::string_view echo(std::string_view); +Lazy example(coro_rpc_client& client) { + // send request to the server + Lazy> handler = co_await client.send_request("Hello"); + // then wait server response + async_rpc_result result = co_await std::move(handler); + if (result) { + assert(result->result() == "Hello"); + } + else { + // error handle + std::cout< example(coro_rpc_client& client) { + std::vector>> handlers; + // First, send 10 requests consecutively + for (int i=0;i<10;++i) { + handlers.push_back(co_await client.send_request(std::to_string(i))); + } + // Next, wait for all the requests to return + std::vector> results = co_await collectAll(std::move(handlers)); + for (int i=0;i<10;++i) { + assert(results[i]->result() == std::to_string(i)); + } + co_return; +} +``` + +When sending data using the connection pool, we can also reuse a connection. To do this, you need to add the `coro_io::client_reuse_hint` parameter to instruct the connection pool to enable connection reuse. For more details, see the connection pool documentation. (TODO) + +```cpp +auto pool = coro_io::client_pool::create( + conf.url, pool_conf); +auto ret = co_await pool->send_request( + [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { + return client.send_request("hello"); + }); +if (ret.has_value()) { + auto result = co_await std::move(ret.value()); + if (result.has_value()) { + assert(result.value()=="hello"); + } +} +``` + +### Attachment + +When using the `send_request` method, since multiple requests might be sent simultaneously, we should not call the `set_req_attachment` method to send an attachment to the server, nor should we call the `get_resp_attachment` and `release_resp_attachment` methods to get the attachment returned by the server. + +Instead, we can set the attachment when sending a request by calling the `send_request_with_attachment` function. Additionally, we can retrieve the attachment by calling the `->get_attachment()` and `->release_buffer()` methods of `async_rpc_result`. + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +int add(int a, int b); +Lazy example(coro_rpc_client& client) { + auto handler = co_await client.send_request_with_attachment("Hello", 1, 2); + async_rpc_result result = co_await std::move(handler); + assert(result->result() == 3); + assert(result->get_attachment() == "Hello"); + co_return std::move(result->release_buffer().resp_attachment_buf_); +} +``` + + +### Execution order + +When the called rpc function is a coroutine rpc function or a callback rpc function, the rpc requests may not necessarily be executed in order. The server might execute multiple rpc requests simultaneously. +For example, suppose there is the following code: + +```cpp +using namespace async_simple::coro; +Lazy sleep(int seconds) { + co_await coro_io::sleep(1s * seconds); // Yield the coroutine here + co_return; +} +``` + +Server registration and startup: +```cpp +using namespace coro_rpc; +void start() { + coro_rpc_server server(/* thread = */1,/* port = */ 8801); + server.register_handler(); + server.start(); +} +``` + +The client consecutively calls the sleep function twice on the same connection, sleeping for 2 seconds the first time and 1 second the second time. +```cpp +using namespace async_simple::coro; +using namespace coro_rpc; +Lazy call() { + coro_rpc_client cli,cli2; + co_await cli.connect("localhost:8801"); + co_await cli2.connect("localhost:8801"); + auto handler1 = co_await cli.send_request(2); + auto handler2 = co_await cli.send_request(1); + auto handler3 = co_await cli2.send_request(0); + handler2.start([](auto&&){ + std::cout<<"handler2 return"< example() { - coro_rpc_client client; - coro_rpc::err_code ec = co_await client.connect("localhost:9001"); - if (ec) { /*判断连接是否出错*/ - std::cout<(1,2); - /*rpc_result是一个expected类型,其中T为rpc返回值*/ - if (!result.has_value()) { - /*调用result.error()获取rpc错误信息*/ - std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); -} -Lazy example(coro_rpc_client& client, std::string_view attachment) { - client.set_req_attachment(attachment); - rpc_result result = co_await client.call(); - if (result.has_value()) { - assert(result.get_resp_attachment()==attachment); - co_return std::move(result.release_resp_attachment()); - } - co_return ""; -} -``` - -默认情况下,rpc客户端发送请求/建立连接后会等待5秒,如果5秒后仍未收到响应,则会返回超时错误。 -用户也可以通过调用`call_for`函数自定义等待的时长。 - -```cpp -client.connect("127.0.0.1:9001", std::chrono::seconds{10}); -auto result = co_await client.call_for(std::chrono::seconds{10},1,2); -assert(result.value() == 3); -``` - -时长可以是任一的`std::chrono::duration`类型,常见的如`std::chrono::seconds`,`std::chrono::millseconds`。 -特别的,如果时长为0,代表该函数调用永远也不会超时。 - -## SSL支持 - -coro_rpc支持使用openssl对连接进行加密。在安装openssl并使用cmake find_package/fetch_content 将yalantinglibs导入到你的工程后,可以打开cmake选项`YLT_ENABLE_SSL=ON`启用ssl支持。或者,你也可以手动添加宏`YLT_ENABLE_SSL`并手动链接openssl。 - -当启用ssl支持后,用户可以调用`init_ssl`函数,然后再连接到服务器。这会使得客户端与服务器之间建立加密的链接。需要注意的是,coro_rpc服务端在编译时也必须启用ssl支持,并且在启动服务器之前也需要调用`init_ssl`方法来启用SSL支持。 - -```cpp -client.init_ssl("./","server.crt"); -``` - -第一个字符串代表SSL证书所在的基本路径,第二个字符串代表SSL证书相对于基本路径的相对路径。 - -当建立连接时,客户端会使用该证书校验服务端发来的证书,以避免中间人攻击。因此,客户端必须持有服务端使用的证书或其根证书。 - -我们同样支持国密NTLS。你需要开启CMAKE选项`YLT_ENABLE_NTLS`。 - -## RPC参数的转换与编译期检查 - -coro_rpc会在调用的时候对参数的合法性做编译期检查,比如,对于如下rpc函数: - -```cpp -inline std::string echo(std::string str) { return str; } -``` - -接下来,当前client调用rpc函数时: - -```cpp -client.call(42);// The argument does not match, a compilation error occurs. -client.call();// Missing argument, compilation error occurs. -client.call("", 0);// There are too many arguments, a compilation error occurs. -client.call("hello, coro_rpc");// The string literal can be converted to std::string, compilation succeeds. -``` - -## 连接选项 - -coro_rpc_client提供了`init_config`函数,用于配置连接选项。下面这份代码列出了可配置的选项。这些选项默认均可以不填写。 - -```cpp -using namespace coro_rpc; -using namespace std::chrono; -void set_config(coro_rpc_client& client) { - uint64_t client_id; - std::chrono::milliseconds connect_timeout_duration; - std::chrono::milliseconds request_timeout_duration; - std::string host; - std::string port; - std::string local_ip; - client.init_config(config{ - .connect_timeout_duration = 5s, // 连接的超时时间 - .request_timeout_duration = 5s, // 请求超时时间 - .host = "localhost", // 服务器域名 - .port = "9001", // 服务器端口 - .local_ip = "", // 本地ip,用于指定本地通信的ip地址。 - .socket_config=std::variant{tcp_config{}}; // 指定底层的协议及其底层配置,目前支持tcp, ssl over tcp, rdma三种协议。 - }); -} -``` - - -### rdma配置 - -ibverbs协议的配置如下: -```cpp -struct ib_socket_t::config_t { - uint32_t cq_size = 128; // 事件通知队列的最大长度 - uint32_t recv_buffer_cnt = 8; // 默认提交到接受队列的缓冲数目,一个缓冲区默认256KB。积压的接收数据越多,队列中的缓冲区也会越多,最多可以缓冲max_recv_wr*buffer_size这么多的数据(buffer_size为buffer_pool配置的缓冲区大小),此后如果上层仍不消费数据,则发送端会收到rnr错误,不断重试并等待对端消费。 - uint32_t send_buffer_cnt = 4; // 默认的发送缓冲区队列长度上限。代表最多积压的发送缓冲区数目。 - ibv_qp_type qp_type = IBV_QPT_RC; // 默认的qp类型。 - ibv_qp_cap cap = {.max_send_wr = 32, // 发送队列的最大长度。 - .max_recv_wr = 32, // 接受队列的最大长度 - .max_send_sge = 3, // 发送的最大地址分段数,在不启用inline data时只需要1个。使用inline data时数据可能不经过拷贝直接从原始分段地址发送,因此设置为3段(默认支持3段分散地址)。 - .max_recv_sge = 1, // 接受的最大地址分段数,目前的缓冲区配置下只需要1个即可。 - .max_inline_data = 256}; // 如果发送的数据包小于inline data,且底层网卡支持该设置,则小数据包不会被拷贝到缓冲中,而是直接交给网卡发送。 - std::shared_ptr device; // rpc使用的底层ib网卡。默认选择设备列表第一个网卡。 -}; -``` - -可以通过下面的代码简单的启用rdma: - -```cpp - coro_rpc_client cli; - cli.init_ibv(); //使用默认配置 - cli.init_ibv(ib_socket_t::config_t{}); //使用用户指定的配置 -``` - -也可以在配置中启用rdma: - -```cpp - coro_rpc_client cli; - cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) -``` - - -#### ib_device_t - -`ib_device_t`管理了ibverbs传输过程中需要使用到的连接上下文和缓冲区。默认使用全局设备`coro_io::get_global_ib_device()`,用户也可以指定使用自己的设备。 - -通过修改ib_device_t的配置,可以给rpc连接配置不同的网卡,使用独立的缓冲区。 - -1. 修改默认的设备配置 -```cpp - // 配置只有在第一次调用时才会生效 - coro_io::get_global_ib_device({ - .buffer_pool_config = { - .buffer_size = 256 * 1024, // 缓冲区大小 - .max_memory_usage = 4 * 1024 * 1024, // 最大内存使用量(超过此限制将分配失败) - .memory_usage_recorder = nullptr; // nullopt 表示不同设备的内存占用会被一起统计,如果想要让内存池具有独立的内存占用记录,请分配一个非空的std::shared_ptr>作为记录 - .idle_timeout = 5s // 空闲时间超过这个时长的缓冲区将被回收 - } - }); - // ... -``` - -2. 初始化连接时,指定需要使用的 RDMA 网卡 -```cpp - coro_rpc_client cli; - cli.init_ibv({ - .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); - }); -``` - -3. 创建并使用自己的 `ib_device_t` -```cpp - auto dev = coro_io::ib_device_t::create({ - .dev_name = "", // 如果 dev_name 为 空,则会使用设备列表中的第一个设备 - .port = 1, // 手动指定网卡port - .use_best_gid_index = true, // 自动查找该设备最佳的gid_index - .gid_index = 0, // 手动指定gid_index,当关闭自动查找或自动查找失败时生效 - .buffer_pool_config = { - // ... - }, - }); - coro_rpc_client cli; - cli.init_ibv({ - .device = dev - }); -``` - -4. 查询当前所有成功注册的全局 RDMA 设备 -```cpp - // 获取所有设备 - auto devices = coro_io::g_ib_device_manager(); - for (auto &dev: devices.get_dev_list()) { - std::cout<<"name:"< - } -``` - -## 调用模型 - -每一个`coro_rpc_client`都会绑定到某个IO线程上,默认通过轮转法从全局IO线程池中选择一个连接,用户也可以手动绑定到特定的IO线程上。 - -```cpp -auto executor=coro_io::get_global_executor(); -coro_rpc_client client(executor),client2(executor); -// 两个客户端都被绑定到同一个io线程上 -``` - -每次发起一个基于协程的IO任务(如`connect`,`call`,`send_request`),客户端内部会将IO事件提交给操作系统,当IO事件完成后,再将协程恢复到绑定的IO线程上继续执行。 - -例如以下代码,调用connect之后任务将切换到IO线程执行。 - -```cpp -/*run in thread 1*/ -coro_rpc_client cli; -co_await cli.connect("localhost:9001"); -/*run in thread 2*/ -do_something(); -``` - -## 连接池与负载均衡 - -`coro_io`提供了连接池`client_pool`与负载均衡器`channel`。用户可以通过连接池`client_pool`来管理`coro_rpc`/`coro_http`连接,可以使用`channel`实现多个host之间的负载均衡。具体请见`coro_io`的文档。 - -## 连接复用 - -`coro_rpc_client` 可以通过 `send_request`函数实现连接复用。该函数是线程安全的,允许多个线程同时调用同一个client的 `send_request`方法。该函数返回值为`Lazy>>`. - - -连接复用允许我们在高并发下减少连接的个数,无需创建新的连接。同时也能提高每个连接的吞吐量。 - -下面是一段简单的示例代码: - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -std::string_view echo(std::string_view); -Lazy example(coro_rpc_client& client) { - //向服务器发送请求 - Lazy> handler = co_await client.send_request("Hello"); - async_rpc_result result = co_await std::move(handler); - if (result) { - assert(result->result() == "Hello"); - } - else { - // error handle - std::cout< example(coro_rpc_client& client) { - std::vector>> handlers; - // 准备发送10个请求 - for (int i=0;i<10;++i) { - handlers.push_back(co_await client.send_request(std::to_string(i))); - } - //接下来等待所有的请求返回 - std::vector> results = co_await collectAll(std::move(handlers)); - for (int i=0;i<10;++i) { - assert(results[i]->result() == std::to_string(i)); - } - co_return; -} -``` - -在使用连接池发送数据时,我们也可以复用连接,此时需要在添加`coro_io_client_reuse_hint`参数,提示连接池启用连接复用。详见连接池文档。(TODO) - -```cpp -auto pool = coro_io::client_pool::create( - conf.url, pool_conf); -auto ret = co_await pool->send_request( - [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { - return client.send_request("hello"); - }); -if (ret.has_value()) { - auto result = co_await std::move(ret.value()); - if (result.has_value()) { - assert(result.value()=="hello"); - } -} -``` - - -### Attachment - -使用`send_request`方法时,由于可能同时发送多个请求,因此我们不能调用`set_req_attachment`方法向服务器发送attachment,同样也不能调用`get_resp_attachment`和`release_resp_attachment`方法来获取服务器返回的attachment。 - -我们可以通过调用`send_request_with_attachment`函数,在发送请求时设置attachment。我们也可以通过调用async_rpc_result的`->get_attachment()`方法和`->release_buffer()`方法来获取attachment。 - -```cpp -using namespace coro_rpc; -using namespace async_simple::coro; -int add(int a, int b); -Lazy example(coro_rpc_client& client) { - async_rpc_result result = co_await co_await client.send_request_with_attachment("Hello", 1, 2); - assert(result->result() == 3); - assert(result->get_attachment() == "Hello"); - co_return std::move(result->release_buffer().resp_attachment_buf_); -} -``` - - -### 执行顺序 - -当调用的rpc函数是协程rpc函数或回调rpc函数时,rpc请求不一定会按顺序执行,服务端可能会同时执行多个rpc请求。 - -例如,假如有以下代码: - -```cpp -using namespace async_simple::coro; -Lazy sleep(int seconds) { - co_await coro_io::sleep(1s * seconds); // 在此处让出协程 - co_return; -} -``` - -服务器注册并启动: -```cpp -using namespace coro_rpc; -void start() { - coro_rpc_server server(/* thread = */1,/* port = */ 8801); - server.register_handler(); - server.start(); -} -``` - -客户端连续在同一个连接上调用两次sleep函数,第一次sleep2秒,第二次sleep1秒。 -```cpp -using namespace async_simple::coro; -using namespace coro_rpc; -Lazy call() { - coro_rpc_client cli,cli2; - co_await cli.connect("localhost:8801"); - co_await cli2.connect("localhost:8801"); - auto handler1 = co_await cli.send_request(2); - auto handler2 = co_await cli.send_request(1); - auto handler3 = co_await cli2.send_request(0); - handler2.start([](auto&&){ - std::cout<<"handler2 return"< example() { + coro_rpc_client client; + coro_rpc::err_code ec = co_await client.connect("localhost:9001"); + if (ec) { /*判断连接是否出错*/ + std::cout<(1,2); + /*rpc_result是一个expected类型,其中T为rpc返回值*/ + if (!result.has_value()) { + /*调用result.error()获取rpc错误信息*/ + std::cout<<"error code:"<set_resp_attachment(ctx->get_req_attachment()); +} +Lazy example(coro_rpc_client& client, std::string_view attachment) { + client.set_req_attachment(attachment); + rpc_result result = co_await client.call(); + if (result.has_value()) { + assert(result.get_resp_attachment()==attachment); + co_return std::move(result.release_resp_attachment()); + } + co_return ""; +} +``` + +默认情况下,rpc客户端发送请求/建立连接后会等待5秒,如果5秒后仍未收到响应,则会返回超时错误。 +用户也可以通过调用`call_for`函数自定义等待的时长。 + +```cpp +client.connect("127.0.0.1:9001", std::chrono::seconds{10}); +auto result = co_await client.call_for(std::chrono::seconds{10},1,2); +assert(result.value() == 3); +``` + +时长可以是任一的`std::chrono::duration`类型,常见的如`std::chrono::seconds`,`std::chrono::millseconds`。 +特别的,如果时长为0,代表该函数调用永远也不会超时。 + +## SSL支持 + +coro_rpc支持使用openssl对连接进行加密。在安装openssl并使用cmake find_package/fetch_content 将yalantinglibs导入到你的工程后,可以打开cmake选项`YLT_ENABLE_SSL=ON`启用ssl支持。或者,你也可以手动添加宏`YLT_ENABLE_SSL`并手动链接openssl。 + +当启用ssl支持后,用户可以调用`init_ssl`函数,然后再连接到服务器。这会使得客户端与服务器之间建立加密的链接。需要注意的是,coro_rpc服务端在编译时也必须启用ssl支持,并且在启动服务器之前也需要调用`init_ssl`方法来启用SSL支持。 + +### 单向SSL认证 + +单向SSL认证只验证服务器身份,客户端使用CA证书验证服务器: + +服务端配置: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = ""; // 单向认证为空 +ssl_conf.enable_client_verify = false; // 不验证客户端证书 + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +### 双向SSL认证(Mutual Authentication) + +双向SSL认证同时验证客户端和服务器身份,客户端需要提供自己的证书: + +客户端配置: + +```cpp + // 客户端双向认证 + client.init_ssl("./", // 证书路径 + "ca.crt", // CA证书,用于验证服务器 + "client.crt", // 客户端证书 + "client.key", // 客户端私钥 + "127.0.0.1" // 服务器主机名(SNI) + ); +``` + +服务端配置: + +```cpp +coro_rpc_server server(2, 9000); +ssl_configure ssl_conf; +ssl_conf.base_path = "./certs"; +ssl_conf.cert_file = "server.crt"; +ssl_conf.key_file = "server.key"; +ssl_conf.ca_cert_file = "ca.crt"; // CA证书,用于验证客户端 +ssl_conf.enable_client_verify = true; // 启用客户端证书验证 + +server.init_ssl(ssl_conf); +server.register_handler(); +server.start(); +``` + +**重要说明**: +- `enable_client_verify` 标志启用强制客户端证书验证 +- 启用双向认证后,客户端必须提供由 `ca_cert_file` 指定的CA签发的有效证书 +- 主机名参数必须与服务器证书中的通用名称(CN)或主题备用名称(SAN)匹配 + +我们同样支持国密NTLS。你需要开启CMAKE选项`YLT_ENABLE_NTLS`。 + +## RPC参数的转换与编译期检查 + +coro_rpc会在调用的时候对参数的合法性做编译期检查,比如,对于如下rpc函数: + +```cpp +inline std::string echo(std::string str) { return str; } +``` + +接下来,当前client调用rpc函数时: + +```cpp +client.call(42);// The argument does not match, a compilation error occurs. +client.call();// Missing argument, compilation error occurs. +client.call("", 0);// There are too many arguments, a compilation error occurs. +client.call("hello, coro_rpc");// The string literal can be converted to std::string, compilation succeeds. +``` + +## 连接选项 + +coro_rpc_client提供了`init_config`函数,用于配置连接选项。下面这份代码列出了可配置的选项。这些选项默认均可以不填写。 + +```cpp +using namespace coro_rpc; +using namespace std::chrono; +void set_config(coro_rpc_client& client) { + uint64_t client_id; + std::chrono::milliseconds connect_timeout_duration; + std::chrono::milliseconds request_timeout_duration; + std::string host; + std::string port; + std::string local_ip; + client.init_config(config{ + .connect_timeout_duration = 5s, // 连接的超时时间 + .request_timeout_duration = 5s, // 请求超时时间 + .host = "localhost", // 服务器域名 + .port = "9001", // 服务器端口 + .local_ip = "", // 本地ip,用于指定本地通信的ip地址。 + .socket_config=std::variant{tcp_config{}}; // 指定底层的协议及其底层配置,目前支持tcp, ssl over tcp, rdma三种协议。 + }); +} +``` + + +### rdma配置 + +ibverbs协议的配置如下: +```cpp +struct ib_socket_t::config_t { + uint32_t cq_size = 128; // 事件通知队列的最大长度 + uint32_t recv_buffer_cnt = 8; // 默认提交到接受队列的缓冲数目,一个缓冲区默认256KB。积压的接收数据越多,队列中的缓冲区也会越多,最多可以缓冲max_recv_wr*buffer_size这么多的数据(buffer_size为buffer_pool配置的缓冲区大小),此后如果上层仍不消费数据,则发送端会收到rnr错误,不断重试并等待对端消费。 + uint32_t send_buffer_cnt = 4; // 默认的发送缓冲区队列长度上限。代表最多积压的发送缓冲区数目。 + ibv_qp_type qp_type = IBV_QPT_RC; // 默认的qp类型。 + ibv_qp_cap cap = {.max_send_wr = 32, // 发送队列的最大长度。 + .max_recv_wr = 32, // 接受队列的最大长度 + .max_send_sge = 3, // 发送的最大地址分段数,在不启用inline data时只需要1个。使用inline data时数据可能不经过拷贝直接从原始分段地址发送,因此设置为3段(默认支持3段分散地址)。 + .max_recv_sge = 1, // 接受的最大地址分段数,目前的缓冲区配置下只需要1个即可。 + .max_inline_data = 256}; // 如果发送的数据包小于inline data,且底层网卡支持该设置,则小数据包不会被拷贝到缓冲中,而是直接交给网卡发送。 + std::shared_ptr device; // rpc使用的底层ib网卡。默认选择设备列表第一个网卡。 +}; +``` + +可以通过下面的代码简单的启用rdma: + +```cpp + coro_rpc_client cli; + cli.init_ibv(); //使用默认配置 + cli.init_ibv(ib_socket_t::config_t{}); //使用用户指定的配置 +``` + +也可以在配置中启用rdma: + +```cpp + coro_rpc_client cli; + cli.init_config(config{.socket_config=ib_socket_t::config_t{}}) +``` + + +#### ib_device_t + +`ib_device_t`管理了ibverbs传输过程中需要使用到的连接上下文和缓冲区。默认使用全局设备`coro_io::get_global_ib_device()`,用户也可以指定使用自己的设备。 + +通过修改ib_device_t的配置,可以给rpc连接配置不同的网卡,使用独立的缓冲区。 + +1. 修改默认的设备配置 +```cpp + // 配置只有在第一次调用时才会生效 + coro_io::get_global_ib_device({ + .buffer_pool_config = { + .buffer_size = 256 * 1024, // 缓冲区大小 + .max_memory_usage = 4 * 1024 * 1024, // 最大内存使用量(超过此限制将分配失败) + .memory_usage_recorder = nullptr; // nullopt 表示不同设备的内存占用会被一起统计,如果想要让内存池具有独立的内存占用记录,请分配一个非空的std::shared_ptr>作为记录 + .idle_timeout = 5s // 空闲时间超过这个时长的缓冲区将被回收 + } + }); + // ... +``` + +2. 初始化连接时,指定需要使用的 RDMA 网卡 +```cpp + coro_rpc_client cli; + cli.init_ibv({ + .device = coro_io::get_global_ib_device({.dev_name = "my_rmda_network_device_name"}); + }); +``` + +3. 创建并使用自己的 `ib_device_t` +```cpp + auto dev = coro_io::ib_device_t::create({ + .dev_name = "", // 如果 dev_name 为 空,则会使用设备列表中的第一个设备 + .port = 1, // 手动指定网卡port + .use_best_gid_index = true, // 自动查找该设备最佳的gid_index + .gid_index = 0, // 手动指定gid_index,当关闭自动查找或自动查找失败时生效 + .buffer_pool_config = { + // ... + }, + }); + coro_rpc_client cli; + cli.init_ibv({ + .device = dev + }); +``` + +4. 查询当前所有成功注册的全局 RDMA 设备 +```cpp + // 获取所有设备 + auto devices = coro_io::g_ib_device_manager(); + for (auto &dev: devices.get_dev_list()) { + std::cout<<"name:"< + } +``` + +## 调用模型 + +每一个`coro_rpc_client`都会绑定到某个IO线程上,默认通过轮转法从全局IO线程池中选择一个连接,用户也可以手动绑定到特定的IO线程上。 + +```cpp +auto executor=coro_io::get_global_executor(); +coro_rpc_client client(executor),client2(executor); +// 两个客户端都被绑定到同一个io线程上 +``` + +每次发起一个基于协程的IO任务(如`connect`,`call`,`send_request`),客户端内部会将IO事件提交给操作系统,当IO事件完成后,再将协程恢复到绑定的IO线程上继续执行。 + +例如以下代码,调用connect之后任务将切换到IO线程执行。 + +```cpp +/*run in thread 1*/ +coro_rpc_client cli; +co_await cli.connect("localhost:9001"); +/*run in thread 2*/ +do_something(); +``` + +## 连接池与负载均衡 + +`coro_io`提供了连接池`client_pool`与负载均衡器`channel`。用户可以通过连接池`client_pool`来管理`coro_rpc`/`coro_http`连接,可以使用`channel`实现多个host之间的负载均衡。具体请见`coro_io`的文档。 + +## 连接复用 + +`coro_rpc_client` 可以通过 `send_request`函数实现连接复用。该函数是线程安全的,允许多个线程同时调用同一个client的 `send_request`方法。该函数返回值为`Lazy>>`. + + +连接复用允许我们在高并发下减少连接的个数,无需创建新的连接。同时也能提高每个连接的吞吐量。 + +下面是一段简单的示例代码: + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +std::string_view echo(std::string_view); +Lazy example(coro_rpc_client& client) { + //向服务器发送请求 + Lazy> handler = co_await client.send_request("Hello"); + async_rpc_result result = co_await std::move(handler); + if (result) { + assert(result->result() == "Hello"); + } + else { + // error handle + std::cout< example(coro_rpc_client& client) { + std::vector>> handlers; + // 准备发送10个请求 + for (int i=0;i<10;++i) { + handlers.push_back(co_await client.send_request(std::to_string(i))); + } + //接下来等待所有的请求返回 + std::vector> results = co_await collectAll(std::move(handlers)); + for (int i=0;i<10;++i) { + assert(results[i]->result() == std::to_string(i)); + } + co_return; +} +``` + +在使用连接池发送数据时,我们也可以复用连接,此时需要在添加`coro_io_client_reuse_hint`参数,提示连接池启用连接复用。详见连接池文档。(TODO) + +```cpp +auto pool = coro_io::client_pool::create( + conf.url, pool_conf); +auto ret = co_await pool->send_request( + [&](coro_io::client_reuse_hint, coro_rpc::coro_rpc_client& client) { + return client.send_request("hello"); + }); +if (ret.has_value()) { + auto result = co_await std::move(ret.value()); + if (result.has_value()) { + assert(result.value()=="hello"); + } +} +``` + + +### Attachment + +使用`send_request`方法时,由于可能同时发送多个请求,因此我们不能调用`set_req_attachment`方法向服务器发送attachment,同样也不能调用`get_resp_attachment`和`release_resp_attachment`方法来获取服务器返回的attachment。 + +我们可以通过调用`send_request_with_attachment`函数,在发送请求时设置attachment。我们也可以通过调用async_rpc_result的`->get_attachment()`方法和`->release_buffer()`方法来获取attachment。 + +```cpp +using namespace coro_rpc; +using namespace async_simple::coro; +int add(int a, int b); +Lazy example(coro_rpc_client& client) { + async_rpc_result result = co_await co_await client.send_request_with_attachment("Hello", 1, 2); + assert(result->result() == 3); + assert(result->get_attachment() == "Hello"); + co_return std::move(result->release_buffer().resp_attachment_buf_); +} +``` + + +### 执行顺序 + +当调用的rpc函数是协程rpc函数或回调rpc函数时,rpc请求不一定会按顺序执行,服务端可能会同时执行多个rpc请求。 + +例如,假如有以下代码: + +```cpp +using namespace async_simple::coro; +Lazy sleep(int seconds) { + co_await coro_io::sleep(1s * seconds); // 在此处让出协程 + co_return; +} +``` + +服务器注册并启动: +```cpp +using namespace coro_rpc; +void start() { + coro_rpc_server server(/* thread = */1,/* port = */ 8801); + server.register_handler(); + server.start(); +} +``` + +客户端连续在同一个连接上调用两次sleep函数,第一次sleep2秒,第二次sleep1秒。 +```cpp +using namespace async_simple::coro; +using namespace coro_rpc; +Lazy call() { + coro_rpc_client cli,cli2; + co_await cli.connect("localhost:8801"); + co_await cli2.connect("localhost:8801"); + auto handler1 = co_await cli.send_request(2); + auto handler2 = co_await cli.send_request(1); + auto handler3 = co_await cli2.send_request(0); + handler2.start([](auto&&){ + std::cout<<"handler2 return"< Date: Fri, 10 Apr 2026 12:40:57 +0800 Subject: [PATCH 31/38] fix: restore original fake_server certs and update mutual auth cert generation script Restore fake_server.crt/key, update generate_mutual_auth_certs script to only generate certs for mutual auth (CA-signed server/client certs). --- src/coro_http/tests/openssl_files/ca.crt | 39 ++--- src/coro_http/tests/openssl_files/client.crt | 34 ++--- src/coro_http/tests/openssl_files/client.key | 52 +++---- src/coro_http/tests/openssl_files/fake.crt | 20 +++ src/coro_http/tests/openssl_files/fake.key | 28 ++++ src/coro_http/tests/openssl_files/server.crt | 39 ++--- src/coro_http/tests/openssl_files/server.key | 52 +++---- src/coro_rpc/tests/openssl_files/ca.crt | 39 ++--- src/coro_rpc/tests/openssl_files/client.crt | 34 ++--- src/coro_rpc/tests/openssl_files/client.key | 52 +++---- src/coro_rpc/tests/openssl_files/fake.crt | 42 +++--- src/coro_rpc/tests/openssl_files/fake.key | 56 +++---- src/coro_rpc/tests/openssl_files/generate.txt | 12 +- .../generate_mutual_auth_certs.bat | 125 +++++++++++++--- .../generate_mutual_auth_certs.sh | 140 ++++++++++++++---- src/coro_rpc/tests/openssl_files/server.crt | 39 ++--- src/coro_rpc/tests/openssl_files/server.key | 52 +++---- 17 files changed, 535 insertions(+), 320 deletions(-) create mode 100644 src/coro_http/tests/openssl_files/fake.crt create mode 100644 src/coro_http/tests/openssl_files/fake.key diff --git a/src/coro_http/tests/openssl_files/ca.crt b/src/coro_http/tests/openssl_files/ca.crt index 4ba4063f5..6cc065352 100644 --- a/src/coro_http/tests/openssl_files/ca.crt +++ b/src/coro_http/tests/openssl_files/ca.crt @@ -1,21 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL -BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 -MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI -DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w -DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF -XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv -oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg -zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 -VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO -3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k -XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw -4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB -Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 -n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ -qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH -lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 -l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp -zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +MIIDtzCCAp+gAwIBAgIUE0DZiUgfbUwiz2nF4zRYH7WC0YgwDQYJKoZIhvcNAQEL +BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM +BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGMxCzAJBgNV +BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD +VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0Q0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrbDIeJL2YCTvjlQCYx77DJmTg +TLorGcNn8HiZaAK5EhR0zh34szV97Ck9qUpMEMUOVBpQ4I4UEFWfYX/hOe9F0e37 +QTayDIKarc/Dz4CZu/i1ERzcweYs+Bkd+HBqSOweSQIdArYpTvQbennXo81Yk/3R +lgNQRLqaWdVB6A/P+qJPo4whytsbkbdFNw/PYX7TnuUKxfAsNHvE1UhUkyTs8den +sl/LjPqoX+NfZPcDDrlq7jtRNDHkh47LtFb5yqR6RQ/18uJenQhFyaqOAuIsYnPh +8n24kj05k+uz3gX++LRcmstX33SmVf6W2mIlWubFqSth7udlMF/yqMfHILhzAgMB +AAGjYzBhMB0GA1UdDgQWBBRnstOwi7/KoizEJkfFsuHXQuKbZTAfBgNVHSMEGDAW +gBRnstOwi7/KoizEJkfFsuHXQuKbZTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAXs08fcYcYZFUgzoVHX6wqME+uPBp +hB+zDq1s3ESqpP9ckuOiAtbAc+TKm4b1tVHW8ikYLmjZx57VarV9I/+VHEDUOM2Y +MROYnCDXytUAXcV6fjow5HbacYcnPGfa4iq6r+fDs3TdpUFikvWhg8kEmxy5bsGn +ADMNOriFEFXkKv4mjt20blHiKhv7uOGFtjZZk4UhZWE+qjHcRcCJDaxNudr7hHph +KlrsCiN4R3bkqukI6kIoaywQz14A3xhsEDAJF4v8qGhv2zC/7Cf2/TeLnC20BJ3s +W+uiVB89Fril42C87ZrxIZKbrKv1bqUXPBLg/QM5FKFwhkIgV2d4eldrYA== -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.crt b/src/coro_http/tests/openssl_files/client.crt index 9d8da4ea5..35a3078ce 100644 --- a/src/coro_http/tests/openssl_files/client.crt +++ b/src/coro_http/tests/openssl_files/client.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +MIIDUTCCAjkCFGK25l/HLuKMlZeUvy+4vikSGy9rMA0GCSqGSIb3DQEBCwUAMGMx CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp -amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE -AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG -Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt -5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ -opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 -/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et -jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI -G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 -pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs -Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF -Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA -faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW -8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq -q8rTyD5f5g== +MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 +Q0EwHhcNMjYwNDEwMDQzMDQ3WhcNMzYwNDA3MDQzMDQ3WjBnMQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwH +QWxpYmFiYTENMAsGA1UECwwEVGVzdDETMBEGA1UEAwwKVGVzdENsaWVudDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOkCl4uA5atdS8A0E1NX4PCEfezT +cvvlTJSsqaFIScU687P2stPGWAbuL+aav/6VNysIfyFXFk1R4YZgifT+YgzzqEoD +zF6F5YA+rVxm35vQ2MCqgLVmtlG2PWM8WxltbPCanQGlWIGwnb4W4hK+5ZWna4CL +NSfUhXZScBH7CfHXL6wvlkukKQvOLI1sqJWWTmk6JVyTwk8RzteSM5gB11/CvdNy +uB2l0ya/2ThltABdXljn6hIX4H69CZdt42gAIOr1N/rDspZ215/DAlcFxYWpxm47 +JUoGIK2BborwOfqCoc/LGrEk4DOK3Ey3za9hMH/O/WzdKK+0M2s9aH41G8ECAwEA +ATANBgkqhkiG9w0BAQsFAAOCAQEAcGJUg2jrFlu6vQpsP7yccz3acKgFNMdLYr1C +A9zDpxWaOa9JFQTquiKKntKC69X39ulxBbDOuUMUMFFb1+Ra2gqUgm4uSq8YmXd5 +cTI4JyGryzzm9RDVIjkh55q7wof7HQHe/FuskvZzc6KEi/ZnVKxugj2DYT67jDzL +4upaAXqWbVUzqjH0W1/Nj/rqjifrt+DxoezMs8bFhZi19lhMZQ3OWD+CirhIl9Ke +FtzUAREDiE066mYkdnK4axbW2nsKRQFGOBJculgIgnnyfghfe1SNVBj3/NhNZuh+ +xUD7DkF6mz3Xz24RNVRhsLXDJM5c1vPnnCpESoqbu1ltBDw3mQ== -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.key b/src/coro_http/tests/openssl_files/client.key index a57cacb89..e6a05dd51 100644 --- a/src/coro_http/tests/openssl_files/client.key +++ b/src/coro_http/tests/openssl_files/client.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o -z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E -V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E -8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW -pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj -25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf -NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt -ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t -ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B -T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH -ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv -E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai -zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi -cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D -ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V -qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY -yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD -J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF -OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 -72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE -Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK -lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 -oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB -Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh -BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D -za21e2VPgHOBFlAxpuA= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDpApeLgOWrXUvA +NBNTV+DwhH3s03L75UyUrKmhSEnFOvOz9rLTxlgG7i/mmr/+lTcrCH8hVxZNUeGG +YIn0/mIM86hKA8xeheWAPq1cZt+b0NjAqoC1ZrZRtj1jPFsZbWzwmp0BpViBsJ2+ +FuISvuWVp2uAizUn1IV2UnAR+wnx1y+sL5ZLpCkLziyNbKiVlk5pOiVck8JPEc7X +kjOYAddfwr3TcrgdpdMmv9k4ZbQAXV5Y5+oSF+B+vQmXbeNoACDq9Tf6w7KWdtef +wwJXBcWFqcZuOyVKBiCtgW6K8Dn6gqHPyxqxJOAzitxMt82vYTB/zv1s3SivtDNr +PWh+NRvBAgMBAAECgf86lhWqJ0Jq16xHDQ0pPgmnYNX9X1pJ5Q5+Jif4/79Qckmu +pxjAiJMZhVkU+PmRBZG/HlD24B+olQC526K2NvSG60RDOcfZ7VCsxLtDC7jLjw3/ +2+4PzGz0cXhBrEHhPS2FJGoC5ZPIj5SDWPnRpjV1fj1Ja1yyI1AvGVaFTPeYy1Za +AVG1GqWb7Gr+7LtWYlh5UTDbuOZTdZFhkMYILiewRZX0n2nL5DJ/RmrU7dizQQ8Y +KR6+y1XoXNl06Zu0mJorRPsXp8hi2KdA0W0OBvUIiS1ZCmj7shjn3gd2o6hb+Dux +ytY0yBEQ4LQ1Bw+ye4VYumF0SuSsK9s2jdewla0CgYEA/39T9pw7D6cocqRvlX6w +3s4SEvpVc53hGe5RdT4f2t1iaYmo3WYdahmYw1e4v5S6jOR2urNF5QTb/9HI5CE2 +vTzrzhfAjQrOgeojAW8SWde3qRgGJX9i2R+MvqyIjh7k7uGlxk5XtHnYWp3orl8I +to5uMCpNg0ywhmumZSoSmeUCgYEA6XfwaNsOFkOsOlGa55W0OMhsBcEpzg+9nZ/n +wPJAOfmSiCPC48DKWdHl3G1sM7dT/7xxvFlA5242Oex9ulF+/241+FOzzsKmIX5o +1sY23iIDSqjNcd1ItpHl+SAAXl4fLHU1hf1DwE4+J4IjvSeyrJIqJdhrSdEum56Q +iJZ07K0CgYEAj/ntk8PYWGrHFUtqgeDhxLx1XPJqovtt9RHiH2KByvEEWxqy6Qh5 +POftuO6+8l7afTjlWzJZCcSiQNe0EDJTSXKCIyIpZJGZa4ZIca9otO4l1gjutcTC +LD5mLrDFRulL8v1/UG+nZtFexTnE/DYbj9xVZZkBEyNtOmKBYvLBhq0CgYEA5Ud1 +oPQntHPHKwrDTtV1RSKG+2vEy2on9Cl6psEBlC3l2q8METFfR7Bbxgrr7SoIYylE +pQ0eMWnJ9T6sBpNMXjt04ygIeHAuSMxk1y+X6LSMeQCnqj//zdQgfnUQB5z1jmqZ +IrojlDMC1Tf4MyZOUS3GGJ6eVsMIu6mQFaN0to0CgYEAihXTENTPZ2KPfOECT5dr +HcyxTigY8R2cl+ekJI73TxgQ2csbRPrf0BBDGnp6jl575cGyOq1Ym4MfLWvnt/fn +3uGdzbk96KcVHqdjXc2FVHV3WlxJ90cF2uRWIoMW/hIaL3kSUkKGkBfNRZ6G5bLu +ALpqM+wAnNgzczPQpfGs61w= -----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/fake.crt b/src/coro_http/tests/openssl_files/fake.crt new file mode 100644 index 000000000..c22e7eaba --- /dev/null +++ b/src/coro_http/tests/openssl_files/fake.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDTjCCAjYCFGK25l/HLuKMlZeUvy+4vikSGy9sMA0GCSqGSIb3DQEBCwUAMGMx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 +Q0EwHhcNMjYwNDEwMDQzMDQ4WhcNMzYwNDA3MDQzMDQ4WjBkMQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzENMAsGA1UECgwE +RmFrZTENMAsGA1UECwwERmFrZTETMBEGA1UEAwwKRmFrZUNsaWVudDCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDa7bfVeA55s30ljVESiSs0m2ur7uSi +81ffQuZKpKlG6AeZW1hdIVPQFiEn6jfWkT9KjUWnJS/2hFrQcTH74vPQabTNcu0/ +8ywzg8uMEtcO0Jg3XlMj/4cOoB/SrGa37EXLe7FvewK66hx1edSwBxwaQw5rLDQZ +Lf1B/INRdFCcsdgelF3vch7l/OkrYsisr/y/j8ylXYji1ZHUvpiaMUDj0SMoRu4y +LA0G6FljjQC2hrMG4JfPItQH/6NDVFymqux2idMuNzYa+NdkQXsDBiTXNmPYel/M +ZCP57JTJg6Hu4mnfSBEgGNcdc6/lzK6lNmjTpYYR8ngL0PWbb54cvyUCAwEAATAN +BgkqhkiG9w0BAQsFAAOCAQEAIZYcaI7wOwYXj3oRsf/0Zyw7JXFvQbVmOdUXCOjl +c0XuJCe7wyiiyawgsGzF4mfKKFDOYBIjuHAMzNBWEjGcFYde5+UuqBJKP7oOrQVU +6kkMMcK56ewkNfsN4hEVco3KMj1VMYYRqInn30lUREgUjfYohEIC+weOrwrjgmIt +kqYYImUAofju2im5/ixJKyDWftj88vM8iRWlSznGj96ENboVzz/iY0ozCEQPwdGx +s5z8ouAa122HqrxX3epWG2t5GZ/aLJxvhmkrX4pveyx3fMUCs52inV1LDpATTzU/ +Q3k85ut/oeQ/3UGoEd+hmtFPtS+3jmTYGw48h9EwAtOhkg== +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/fake.key b/src/coro_http/tests/openssl_files/fake.key new file mode 100644 index 000000000..725eaa8e3 --- /dev/null +++ b/src/coro_http/tests/openssl_files/fake.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg2u231XgOebN9 +JY1REokrNJtrq+7kovNX30LmSqSpRugHmVtYXSFT0BYhJ+o31pE/So1FpyUv9oRa +0HEx++Lz0Gm0zXLtP/MsM4PLjBLXDtCYN15TI/+HDqAf0qxmt+xFy3uxb3sCuuoc +dXnUsAccGkMOayw0GS39QfyDUXRQnLHYHpRd73Ie5fzpK2LIrK/8v4/MpV2I4tWR +1L6YmjFA49EjKEbuMiwNBuhZY40AtoazBuCXzyLUB/+jQ1RcpqrsdonTLjc2GvjX +ZEF7AwYk1zZj2HpfzGQj+eyUyYOh7uJp30gRIBjXHXOv5cyupTZo06WGEfJ4C9D1 +m2+eHL8lAgMBAAECggEAGHVRggteZu0QYq4MD9C+tKgaHcQV3gP89laHSJb+9JJI +g6dI4WW/xIu2YbI9BeKFUVdXpcileeaE934MSCphturwt8IpgHOh2Q24M4IH91VU +WGDK4d9uYi3SISSafD/pGC+5jiBCVwbuxAcE0Fc3rYk8uvuGzCEsAf8/kwQWqI79 +JSwTHkYTFQ4KJlims50GJqOxUypyfnvKTnD6z6D8860YZhHukZ/zyn/h/PyzKaWs +uaorDt2XdyuhLHRPt7plW0kk4foZNYFSU10q5QyC4snAo0P27b8JkbBqbLo2VyN0 +qXk1GKdM0tCnFTUAcCoVUYSVxuuvG7gMD+5SCFcBoQKBgQDTJgMso3MN/ic+Cyl+ +B7g15GWFo3JRabMBe79rD/AkyuwdjB/xIcO+mcuNMN9W940RMdqKyrU5n0HX+E3B +JGgdXdUFW8yiDXBmqiHeJ+KtjgAgkRutqh2tvssBTpTvxG+EnP7wmrMgwdR3NlcG +YHuWVtKH5JeuVw3H9AJ8SlgtbQKBgQDDBgfFSDzm9pxKRsamwdXi5Jcuvzv/BZER +96Im5RN3FSQeEgcCMHRZ6gfIrX7WiZcK6I1Y7zn2pej0uPk399Y5WpH/FMilU/dC +fqhyhw+ZEC7UwAujEuLoAm5/p8+JlIscoHY6SE1VIQnyIBpvIprUDSAxZiXSmERA +V8b83e1dmQKBgFoZ+HTN8sTf1WMWZEhRhvwIUIIscxXmoupZIh/Pl0w8A3HAX8kH +/X2SJ1hCqKt1y46w1W8wfRDvsqs0XAm1PXB6n/I/cB0e2v4UT4t7PbGNzOQYx9Td +qPiBWCNgoxGFo4jVbbzCZvntfHq6h3xDI7nNpt0yYL+Wilzu9TiQiPwpAoGAJ0uO +w0Rz6QGlqh2qpy0FQKtYfvXPS+o+OcWQqY+cpXDgDyMIwHss5nUOTnQOy4F7qpoC +6PmCz1zMnIqsxIcuZe4G3sO8TfumJYLJxBHMpcWp+focHiaPC7p5s0UHvyvr/7+h +MssgsddvXpbzXJM3aSSf6PQxCMbfcat+D7NTwXkCgYEAn43lkTPjym0nqqSPzaUA +CtqXYRcgyaoQnuj6E4G61Y4z5vDKD8SE6xHQc2F4tIDUJN1F5opXhb55zcOYSbrM +1MKtZQNdZfOM7hZtBRzZJrP+t7vieXiEgdA0yQVTQoBI1ycQd7erC0kOyzZsbloH +W4yoJlKUzm4JaQN0rNZOaas= +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index b89ecbd56..9d79d31c6 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,20 +1,23 @@ -----BEGIN CERTIFICATE----- -MIIDMjCCAhoCFCfqzDMJkItWsGmNb2dnTuQ7YiDfMA0GCSqGSIb3DQEBCwUAMFQx -CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTQ5WhcNMzYwNDA1MDIyOTQ5WjBXMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp -amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTESMBAGA1UE -AwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEcB -wNSFuRb9/WLdS8BczaycllmOhwfMFqx/2Qd6XVEpOBmJsiDSQSHT1dpwMMZ7xCo5 -17QefyokWmBC+de83FY2EA+KWPreD+nZF1srDOOx6z+D08NN/7Go7eobRnUSBiSF -8BHk8J28I8Mm/h/0mngSQi7tUsnxKJ67yyHN1VTu/oZ24xeN/FSuTPqmLc4iB0/s -NOIpSjlgesP5tgAx4QW3++Z0NQjZamHYASvBmDBzlFTj2HzUcVEVWUotqX6f/l5d -JLseVnEU3bUGrtEyZMXrfz1aVT4vAlgtQ9J31tDAkzZq3Zi3PlLD7Jg2ns+oojS3 -Gfpi6DmNJw8u0FumiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCAGgMOsNeoj0pW -jszmz/IeEPWpHkzT88f4n3D/o5mkVvagEgP9QF+WpnjpdyYW7pruOJXdSJQzYG+C -dbY1h22qw9pqFu3AwCBD1sqPl/5DqwqeyAFvw/d9/nTyCo2VK6Yvpbc6H7wggMFo -8xzK39mJE5kJsl42PJ3meFt4Loxz0FHL4Le0cVp30XnjOoiOroZNHoMk32XS7lGu -2bmWMjiAJSdUP/Btm1xoYUxPUYdqz4MzyYZQCjc0izkLxmbkO41vX/ZkoYlDwCcb -E7XXeqn2kmLkB6aD8d6UJnFtxwuos6YhVBfKeySN4fWXKNPZweRKOcUOaL7GGDWU -m1HkKMpu +MIID5DCCAsygAwIBAgIUYrbmX8cu4oyVl5S/L7i+KRIbL2owDQYJKoZIhvcNAQEL +BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM +BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGYxCzAJBgNV +BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD +VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MRIwEAYDVQQDDAkxMjcuMC4wLjEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZoBMxTWffKpQOXd7eIVHZ +8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C0Wxth33q +ww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdKj2t1iUcz +1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR01yNvTXe +qTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUlKAiEFUO+ +7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHaG9IJJw2j +AgMBAAGjgYwwgYkwCwYDVR0PBAQDAgQwMBMGA1UdJQQMMAoGCCsGAQUFBwMBMCUG +A1UdEQQeMByCCWxvY2FsaG9zdIIJMTI3LjAuMC4xhwR/AAABMB0GA1UdDgQWBBRi +FJ5dKk3VTdYkJYJJ2H4+oeU8ODAfBgNVHSMEGDAWgBRnstOwi7/KoizEJkfFsuHX +QuKbZTANBgkqhkiG9w0BAQsFAAOCAQEAHRyHOVKYQl5jmDbnRQ2GQXdk4uysaPDQ +HwSbLRwoKnQMTN2dh0Uu8odkaXiGKO2tdnRTfE/Ai3UXo6jSKXI3SHlr//nUNoMw +5RNa6VFldlP2GbEtRh3P0YFPtWo9pAs7WV0FS3g0SMFA5CGjVlrbVoqOrhqjlScp +gr7wqGJsOqsSDLkg6N90aH2OYHdNePGMArR+h0Z+QEaaNBuRr6O0aZnBtCN+kDQw +37ATj9Cj0uJaLjWexyG821gYCWtBvq0x/9c9tIV2HnD9VvKwlcZqlC/QCGz8tafE +vWQ64kYniH9npgAaD+TAUlkSia60R8z+RTd6opvPB+rvMsFOEIYNqQ== -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/server.key b/src/coro_http/tests/openssl_files/server.key index 42c3a1f23..a05b48fb1 100644 --- a/src/coro_http/tests/openssl_files/server.key +++ b/src/coro_http/tests/openssl_files/server.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIRwHA1IW5Fv39 -Yt1LwFzNrJyWWY6HB8wWrH/ZB3pdUSk4GYmyINJBIdPV2nAwxnvEKjnXtB5/KiRa -YEL517zcVjYQD4pY+t4P6dkXWysM47HrP4PTw03/sajt6htGdRIGJIXwEeTwnbwj -wyb+H/SaeBJCLu1SyfEonrvLIc3VVO7+hnbjF438VK5M+qYtziIHT+w04ilKOWB6 -w/m2ADHhBbf75nQ1CNlqYdgBK8GYMHOUVOPYfNRxURVZSi2pfp/+Xl0kux5WcRTd -tQau0TJkxet/PVpVPi8CWC1D0nfW0MCTNmrdmLc+UsPsmDaez6iiNLcZ+mLoOY0n -Dy7QW6aJAgMBAAECggEAFBYzEdD+3HJ25Ov+f/N6G1K9ncK7rcVVbcy9QdojJqrW -NH8zNT9fdxLaeS9gYzP1A9asWHsDOAPVA492CDLgCUVIRNIaMRRwIy50DIijR7aq -iIqlQR7sesGpzLrXI3joZt9Q46QXzx4y2W9gQMqZsMhsJGEqgkwouMc61IO+bi/a -II1Uf1EGijFd96Qk/bT76puakg6qjRoJ+SdjNJOM/cOd8Jon6qnvXB6jOtW+DqIs -539RvibVK4pPr8OIxzGSTXPqcEcdaKV0NiWbTWonuOZu4Nf49auK3awjpQPzzXPT -BPHMKSSAtFXUhjaHIaHOAyWnQ8NM/kgrOpvSxMxkhQKBgQDkQIFmMzw+LsjnzAeg -IrA6ANzfPQJzhkGQRuv57Cv6CWVQXpatixgvFpd8CHviIwVD90hNQH+PMF1fM1pf -aYEOkRdWhFjSSIEkO2VRkcS8B0unexNeBbTupQfbvVx8etu8N+SEwevhWv0rNs7R -lF5JkZx+g1iT/6hTpES14i/6PwKBgQDgn+VSP7USOAgCwTU1kJcCBNUS5sROcpWV -5BrRjzkRvVo7wFxCBEOzKoukAV+RZJFwvvam0PxSwP3eLtjYOyW+BT2uywJ10K28 -OgSPkUnMg6PKw0ilCz38CvCw0rI9g8aS1akFCNZ53LFIognWwqpnZsJEm1yR0Qlv -Tj/ERNxdNwKBgBLHe938uSgkkUMA9l+mevlKuOFlE56NnTdRnnihhby8qSlDnwII -P6UgJrZ9vDOOzhAZeEli1RvizsvWXckb1RJtvY3Qtb4XWQiyGlPrulP+Batx5NYH -gitgSJU7rzBOq2WA87w4eD/CTLIRgFKd8mP7JvUBuXfzwNWg3kZYpbnhAoGAEzNX -xNoRPkdv19xwEe4UGmYTWJRFP3dn9fIToMofVLbc2bKtsC7xIoWGfjRn2OPB0uNf -7g57Iw/AI5fZjVIw/bcw+Jn90dhOoYJMFYGTz1mJTLG4qfL2D29X96Vq+vsipDaD -RhzlSHFm7hB7ytHFAyWzgW3OUeCOb+c+aCaCt60CgYBZaensYV8gEd/z06DZ5SQC -cPdRpC7TOCJJyi9Lr3VJDUZJaF4gfnOjzNKQqzh3Jy/vrtDkYJArtIKbISK6Kb2q -+pXpItKPuYFNeIRoieZXy9hSJhaw88fcDfMVODG7Fx0h2LcIFmoNXxNFEayUjkLt -sVgJ1M3OhYqqfZ+8d4QoZg== +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZoBMxTWffKpQO +Xd7eIVHZ8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C +0Wxth33qww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdK +j2t1iUcz1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR +01yNvTXeqTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUl +KAiEFUO+7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHa +G9IJJw2jAgMBAAECggEAKwfYeGXbI3p0sDWA+K1SlP8tgFnL5RplIBehHR9FDtI4 +Y6clEK/T0bUObZsMoM0XMp54kA2ror1LxK7OdTuPfJOYH4yfT94zx8Z8sxs5GkCo +0YBRfoP8RY6uFV+MAvSXGB/u004ndnykoODdU7XZGN6dDVFrJQ5atCZShHNto63Q +voCoKP+MFtEdGFMY8ymLIKuRH3JAY9xof+fS/6vo2fClUxQPEuH7VN+Pr4A/j2ID +WUOdcClGHPuGDW2nGKyEcj/VBKnoT7rcU9u3FDf8/CuG4hf+720/1T/tgPdgyn02 +LVcb78ELlQQ5iLke72FvVQDCzZMUkA4+NYf4A0EUOQKBgQDUenrK6rbjdamsZ+Ze +hcEZpGIoYIzl2fVk2mAJd5ssE6JcGfHAZsFOhtAGICGVZedpOC7vw0fnBDAqJsP2 +RJ0mhehzScTTnqIsNLL8y76jkwI+19tBWFk/VKAgCxhpOtevJ0Tg6wsz4gU0zNi8 +TSICoUvrgeHc4xFIlJ4ppyCcpwKBgQC5F5JUEnrhMwkqIBmnlSWaKZ5HI9riRZXd +TdQmgmFYGZNU8IpyoqaJhBIw9O+5O3NggK7jPr7zOUEGoN9lQvwEy4s5KmVv3ahE +fJimXTd34WML/N1Klfkv9u4M8ajSwanJRv0CuNLGASnuB28jHUhzi0S7u0KoAMVl +nO1wOIH6pQKBgQC+RfAwRoAAsR2AhoqFmsGRdONxxQRn0QcviV73G2SZ0/tQ/uq+ +oUX6TD99PMjWQKs6TlW53+ZDujxngs95eXhBRykiFK1t1GDltUuTleDiGoOoQyTV +H4jMbbv29fimQ0tiTogf4lvl2kFPRyHPfkM2l7qk62qXo7+Wf4AjTMangQKBgE1F +afFvRZ1+kPTlMdCkk24osctBTL/qDQa21zq8c6c0Bi4Pvbzd6mi+mlmV5/6Msz42 +esykBVPFM0BxKNI9hLj7wMO8z1xETVtKKPBLOjzx+0el6TyaH83GaNs+iBx0fU1q +NXZNcrD3C2oz8FHYh8a0/ZziMJGywLYLLZUMAjeBAoGBANEOpeitU+8sYOSz1rmi +dtNKnMWFSxEWjtlxzXgRuIC8HJis9YHLI5Hny0Iao3to4AEXRZ/r6EL9+/Z10AdZ +iGmiN/cbMn6yTErpehKLLCB3S2nLxOdeLIgOcgPcJDyEyvyXCcBI3A9W4uMxYI2I +kplBOEOtHG/SR3vfiHN8Mdpb -----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/ca.crt b/src/coro_rpc/tests/openssl_files/ca.crt index 4ba4063f5..6cc065352 100644 --- a/src/coro_rpc/tests/openssl_files/ca.crt +++ b/src/coro_rpc/tests/openssl_files/ca.crt @@ -1,21 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDiTCCAnGgAwIBAgIUS9caKeNVud47u2yAWe59RLU8dqswDQYJKoZIhvcNAQEL -BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 -MDgwMjI5NDlaFw0zNjA0MDUwMjI5NDlaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI -DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w -DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDF -XznmxIaWVDtNq4pwzYs8Rlh1oSx5cLB5uRCmAJeGvmq6am/ZQhMT2f/3trK6w6Jv -oP06fFo++MiGjsaxCKvTnQkvWInMZNwpyX1D67fWVFU/8obZEoF8XjzLuDI58Cmg -zhrwvCVJlF6fmO4t1esY5w8N8tv6ce9ZNDZQyBRLpDeViTknyIdhr0SQxELiUpm1 -VUX7Qpbpkzj3rcJLxC5cQWFY2/GIUfDqjojbBn0JOdOkzF+XqkZyncP7cI8saRcO -3BkKvJ6Dks90cV+r6gInRMVpSd2aKCNWx6f0ErI/hzornWUMl2iXCF9i+3Eu/A6k -XJGVhKIJFqLC5Wf9ANqhAgMBAAGjUzBRMB0GA1UdDgQWBBRWCykbeECekxwM/qpw -4cwCGMq3zzAfBgNVHSMEGDAWgBRWCykbeECekxwM/qpw4cwCGMq3zzAPBgNVHRMB -Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCA9kCscU25XfFlBWpgPbCu6af4 -n/jPYvpkuxBMJYBLJWPBOLsjOvGWc46+fRWz2weeEn+/ifBhclZymaipM9AcvMrJ -qWkHB5ugxZpmW3blwAMmsrZ+ngAQEreoXgee5RaF+sOcFE6xkkoE6OAmD6mc6FgH -lCzZ9mmZbfgtADfyHlk8//pzpTwCPbGKgGk12DpwvV7ttb266SFFvfWTdZNLpse3 -l/hY4ctkQpCBxS2Ha0WU8PqV+QpJmQ3cPSUul2UfuhOkWCc2yu/tCizp0nth8OGp -zcxYnbkaYeunXFrWfHChJdz3P0+avLRT1zrfSLse1gyDsEwEcY98adozHb9G +MIIDtzCCAp+gAwIBAgIUE0DZiUgfbUwiz2nF4zRYH7WC0YgwDQYJKoZIhvcNAQEL +BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM +BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGMxCzAJBgNV +BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD +VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0Q0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrbDIeJL2YCTvjlQCYx77DJmTg +TLorGcNn8HiZaAK5EhR0zh34szV97Ck9qUpMEMUOVBpQ4I4UEFWfYX/hOe9F0e37 +QTayDIKarc/Dz4CZu/i1ERzcweYs+Bkd+HBqSOweSQIdArYpTvQbennXo81Yk/3R +lgNQRLqaWdVB6A/P+qJPo4whytsbkbdFNw/PYX7TnuUKxfAsNHvE1UhUkyTs8den +sl/LjPqoX+NfZPcDDrlq7jtRNDHkh47LtFb5yqR6RQ/18uJenQhFyaqOAuIsYnPh +8n24kj05k+uz3gX++LRcmstX33SmVf6W2mIlWubFqSth7udlMF/yqMfHILhzAgMB +AAGjYzBhMB0GA1UdDgQWBBRnstOwi7/KoizEJkfFsuHXQuKbZTAfBgNVHSMEGDAW +gBRnstOwi7/KoizEJkfFsuHXQuKbZTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB +/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAXs08fcYcYZFUgzoVHX6wqME+uPBp +hB+zDq1s3ESqpP9ckuOiAtbAc+TKm4b1tVHW8ikYLmjZx57VarV9I/+VHEDUOM2Y +MROYnCDXytUAXcV6fjow5HbacYcnPGfa4iq6r+fDs3TdpUFikvWhg8kEmxy5bsGn +ADMNOriFEFXkKv4mjt20blHiKhv7uOGFtjZZk4UhZWE+qjHcRcCJDaxNudr7hHph +KlrsCiN4R3bkqukI6kIoaywQz14A3xhsEDAJF4v8qGhv2zC/7Cf2/TeLnC20BJ3s +W+uiVB89Fril42C87ZrxIZKbrKv1bqUXPBLg/QM5FKFwhkIgV2d4eldrYA== -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.crt b/src/coro_rpc/tests/openssl_files/client.crt index 9d8da4ea5..35a3078ce 100644 --- a/src/coro_rpc/tests/openssl_files/client.crt +++ b/src/coro_rpc/tests/openssl_files/client.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDMzCCAhsCFCfqzDMJkItWsGmNb2dnTuQ7YiDgMA0GCSqGSIb3DQEBCwUAMFQx +MIIDUTCCAjkCFGK25l/HLuKMlZeUvy+4vikSGy9rMA0GCSqGSIb3DQEBCwUAMGMx CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTUwWhcNMzYwNDA1MDIyOTUwWjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp -amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE -AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKG -Hx8G9QT5zWjPv9l/GDZ45aYLh/0jyhnzCK9CrNJo2QS1oLhpDMEGmgeZuRJkestt -5/kUnLkCDoRXqCqoVB8p63APoRiEP14dEItiaT/LLQmrqI22YKhyt30jQ4Cap2wQ -opSWsvud/8Tz+ezzJewFTzikGeb1MIhuvD+55kl4iR7KivQIchlz1GaedUQv2a48 -/sZ/KMrJPJakWJB6ux3Mp148qE0mnlJ/eQbvv0PKVfDXrcDKunTIvuW9OLRiA0et -jfIB99wp06PbkCt9nonD8VzLwgu9sdRQm4B0OrzXLkDl228ScDyuQeA4BORhEKtI -G8x7co4e8180DqBgBw0CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEACmnnn0BUSei4 -pcfI93FHhP8Cq+wveh473WBipt1v5uFNDFaiww92GlfQx95RNOBKWS+42Hak99Rs -Fo28RV2i4YixSMIfeR8fa8KZrmIMrdBZTlKVTz16PuSp05RmPXJdsi4zePda4PZF -Jc/o9WJTakgMEu8TTz47VZOJbbd0YDbY1rAjtGIw8baLk8sddZCFXmSGzriNCCFA -faTDl4excrNhGc4+HkxDna0zmenM0sqenRpIw7JYBK36YrBwPbmsBvMXvin2JVyW -8zMUhjrfSNGdOoJG5DxXAoZ82qGit2ALogvAtFAPtm7knhOhPY4Zo88cVJEYZhIq -q8rTyD5f5g== +MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 +Q0EwHhcNMjYwNDEwMDQzMDQ3WhcNMzYwNDA3MDQzMDQ3WjBnMQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwH +QWxpYmFiYTENMAsGA1UECwwEVGVzdDETMBEGA1UEAwwKVGVzdENsaWVudDCCASIw +DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOkCl4uA5atdS8A0E1NX4PCEfezT +cvvlTJSsqaFIScU687P2stPGWAbuL+aav/6VNysIfyFXFk1R4YZgifT+YgzzqEoD +zF6F5YA+rVxm35vQ2MCqgLVmtlG2PWM8WxltbPCanQGlWIGwnb4W4hK+5ZWna4CL +NSfUhXZScBH7CfHXL6wvlkukKQvOLI1sqJWWTmk6JVyTwk8RzteSM5gB11/CvdNy +uB2l0ya/2ThltABdXljn6hIX4H69CZdt42gAIOr1N/rDspZ215/DAlcFxYWpxm47 +JUoGIK2BborwOfqCoc/LGrEk4DOK3Ey3za9hMH/O/WzdKK+0M2s9aH41G8ECAwEA +ATANBgkqhkiG9w0BAQsFAAOCAQEAcGJUg2jrFlu6vQpsP7yccz3acKgFNMdLYr1C +A9zDpxWaOa9JFQTquiKKntKC69X39ulxBbDOuUMUMFFb1+Ra2gqUgm4uSq8YmXd5 +cTI4JyGryzzm9RDVIjkh55q7wof7HQHe/FuskvZzc6KEi/ZnVKxugj2DYT67jDzL +4upaAXqWbVUzqjH0W1/Nj/rqjifrt+DxoezMs8bFhZi19lhMZQ3OWD+CirhIl9Ke +FtzUAREDiE066mYkdnK4axbW2nsKRQFGOBJculgIgnnyfghfe1SNVBj3/NhNZuh+ +xUD7DkF6mz3Xz24RNVRhsLXDJM5c1vPnnCpESoqbu1ltBDw3mQ== -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.key b/src/coro_rpc/tests/openssl_files/client.key index a57cacb89..e6a05dd51 100644 --- a/src/coro_rpc/tests/openssl_files/client.key +++ b/src/coro_rpc/tests/openssl_files/client.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQCShh8fBvUE+c1o -z7/Zfxg2eOWmC4f9I8oZ8wivQqzSaNkEtaC4aQzBBpoHmbkSZHrLbef5FJy5Ag6E -V6gqqFQfKetwD6EYhD9eHRCLYmk/yy0Jq6iNtmCocrd9I0OAmqdsEKKUlrL7nf/E -8/ns8yXsBU84pBnm9TCIbrw/ueZJeIkeyor0CHIZc9RmnnVEL9muPP7GfyjKyTyW -pFiQersdzKdePKhNJp5Sf3kG779DylXw163Ayrp0yL7lvTi0YgNHrY3yAffcKdOj -25ArfZ6Jw/Fcy8ILvbHUUJuAdDq81y5A5dtvEnA8rkHgOATkYRCrSBvMe3KOHvNf -NA6gYAcNAgMBAAECgf8vnpp+riqNDQjoOpyFrMiYHrKEETtUPRo+h/EXavpZZ7nt -ARpuB7YdbAfWcIW1dIwNkUQ5SOAM2jfdl9KpPaVMe4ZvS3HchetFd8ZPIBMUqI0t -ypwwPxWRQupfWrAvG620PhoyMGGUmCtUo/YvcnAT3nKtj3R1NNQqtjjefSX0U+2B -T5W32DoJnLo1n+qve5BnApSU15ZEPLv0178MU8/A+8UAH+rOEy/xXENd4BiYJTCH -ZRr9tPSMYXnUOuS6n0NexoT3pJiH3WyrXov8K6O8VpjYMs2bEF+H5QNnQjjPRVNv -E/xWUd03jb1CK2K1s9UOCcSYKrajUqoiUGd8H4ECgYEAxsEc4dnTJM32qXcwo2ai -zYbQy4J8+WA3KjF5cVjGNwj5MbA0MgeESAE8aHwtqlBbEQK2D3Y064vh0DSeOXsi -cyUDxw6xf6Xh7BLz9Ljqxc+keM4DfqT9XUulVt0RM+Xvp8VbNXSJqxTbOSsryG9D -ldKP8RRIsfLEbGOpF+OF0NMCgYEAvLnd/8hFeLv8C3viWsHcR5Wn8g+hqsYwOS8V -qgPJQV6wAM5bysvP5kNE5HVoqG3E23ij+WlRDOTKjNDLiwq9ICiyu18fWiOh43XY -yb3nsaGTXsO4HdmfKhadSUKMy41janfWR8ch/zI7KyGi3qzYHquUZ4rcREjHwAnD -J1s63J8CgYAtigG8HdSrEiX6Hj0es12KCeG9P2CzIsCBAmT4+4YvBfdS0zSiYeaF -OQNGTW2JIHA9LYnZcRQfBCXxNp0qPnRePZTn/w3cWX2yQYV0BQqF2FWu+EUEt3j1 -72cqx+wxH/YRUr7bOKByeozgRGv7uMKbiWtBqYweealXzF3qA0+d0QKBgELiNDUE -Curg5FBFlVDIx4JvHVgCBi95kXmSoEDimp6aKhH/EDTsyj82s+GrYm3eiRemx6YK -lvjU1JvXG2upYKFXCxCwg3H0ktkD2NKWhNhFBO9euY+KoofN/+wIs9EnyIXg9oX1 -oqzIZoPApfH4m5czA6M2aR2iFXiPfSQjhtbNAoGAebPW4y+EqaZTuSf1PJO087aB -Rdj/ApRY0Bxye8bTwXP+i1jtA1U1HzUdpv8Z70tVkFbII7B223XeDHOGYES5L0Xh -BAczxw2TlmYY8TMzarOEWRk8ca703Qq1Ps4jaBlWLW6aD35m5f3jnaGH3I5Zsm6D -za21e2VPgHOBFlAxpuA= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDpApeLgOWrXUvA +NBNTV+DwhH3s03L75UyUrKmhSEnFOvOz9rLTxlgG7i/mmr/+lTcrCH8hVxZNUeGG +YIn0/mIM86hKA8xeheWAPq1cZt+b0NjAqoC1ZrZRtj1jPFsZbWzwmp0BpViBsJ2+ +FuISvuWVp2uAizUn1IV2UnAR+wnx1y+sL5ZLpCkLziyNbKiVlk5pOiVck8JPEc7X +kjOYAddfwr3TcrgdpdMmv9k4ZbQAXV5Y5+oSF+B+vQmXbeNoACDq9Tf6w7KWdtef +wwJXBcWFqcZuOyVKBiCtgW6K8Dn6gqHPyxqxJOAzitxMt82vYTB/zv1s3SivtDNr +PWh+NRvBAgMBAAECgf86lhWqJ0Jq16xHDQ0pPgmnYNX9X1pJ5Q5+Jif4/79Qckmu +pxjAiJMZhVkU+PmRBZG/HlD24B+olQC526K2NvSG60RDOcfZ7VCsxLtDC7jLjw3/ +2+4PzGz0cXhBrEHhPS2FJGoC5ZPIj5SDWPnRpjV1fj1Ja1yyI1AvGVaFTPeYy1Za +AVG1GqWb7Gr+7LtWYlh5UTDbuOZTdZFhkMYILiewRZX0n2nL5DJ/RmrU7dizQQ8Y +KR6+y1XoXNl06Zu0mJorRPsXp8hi2KdA0W0OBvUIiS1ZCmj7shjn3gd2o6hb+Dux +ytY0yBEQ4LQ1Bw+ye4VYumF0SuSsK9s2jdewla0CgYEA/39T9pw7D6cocqRvlX6w +3s4SEvpVc53hGe5RdT4f2t1iaYmo3WYdahmYw1e4v5S6jOR2urNF5QTb/9HI5CE2 +vTzrzhfAjQrOgeojAW8SWde3qRgGJX9i2R+MvqyIjh7k7uGlxk5XtHnYWp3orl8I +to5uMCpNg0ywhmumZSoSmeUCgYEA6XfwaNsOFkOsOlGa55W0OMhsBcEpzg+9nZ/n +wPJAOfmSiCPC48DKWdHl3G1sM7dT/7xxvFlA5242Oex9ulF+/241+FOzzsKmIX5o +1sY23iIDSqjNcd1ItpHl+SAAXl4fLHU1hf1DwE4+J4IjvSeyrJIqJdhrSdEum56Q +iJZ07K0CgYEAj/ntk8PYWGrHFUtqgeDhxLx1XPJqovtt9RHiH2KByvEEWxqy6Qh5 +POftuO6+8l7afTjlWzJZCcSiQNe0EDJTSXKCIyIpZJGZa4ZIca9otO4l1gjutcTC +LD5mLrDFRulL8v1/UG+nZtFexTnE/DYbj9xVZZkBEyNtOmKBYvLBhq0CgYEA5Ud1 +oPQntHPHKwrDTtV1RSKG+2vEy2on9Cl6psEBlC3l2q8METFfR7Bbxgrr7SoIYylE +pQ0eMWnJ9T6sBpNMXjt04ygIeHAuSMxk1y+X6LSMeQCnqj//zdQgfnUQB5z1jmqZ +IrojlDMC1Tf4MyZOUS3GGJ6eVsMIu6mQFaN0to0CgYEAihXTENTPZ2KPfOECT5dr +HcyxTigY8R2cl+ekJI73TxgQ2csbRPrf0BBDGnp6jl575cGyOq1Ym4MfLWvnt/fn +3uGdzbk96KcVHqdjXc2FVHV3WlxJ90cF2uRWIoMW/hIaL3kSUkKGkBfNRZ6G5bLu +ALpqM+wAnNgzczPQpfGs61w= -----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake.crt b/src/coro_rpc/tests/openssl_files/fake.crt index b3711a9c8..c22e7eaba 100644 --- a/src/coro_rpc/tests/openssl_files/fake.crt +++ b/src/coro_rpc/tests/openssl_files/fake.crt @@ -1,22 +1,20 @@ ------BEGIN CERTIFICATE----- -MIIDsTCCApmgAwIBAgIUE5kTRNz1GA3NOOBDwwqIAUz7ZJQwDQYJKoZIhvcNAQEL -BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYD -VQQDDApGYWtlQ2xpZW50MB4XDTI2MDQwOTAyNTMxNVoXDTM2MDQwNjAyNTMxNVow -azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWpp -bmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYDVQQD -DApGYWtlQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqylI -L2ovAcolSOUEGb55Tf2SbFczUqPB0XPF9x4UXFKcADcdhR+LDKLd92k000gwX8ft -s8vtAlOdtPj2x1mbelFyMC66I6BAUGjcwgBsVXhXYM7i8g5C5jhX21FNSy468urX -14Vv2CwRhCuWiukGsnZREQLPBHtwC6WHSSn7oTYdfTCRE9+FwLOJRaXbejCBjZbR -36dIMHFOvvi1w6QiS47v3u/neJ86QftsSRRTiRO9TAFQflrSgeY4sh8QjhPejVck -/AfsHx4CS4yAObSDl2B5NgXd2WikuniCGgJtgayuCHCfs1MP6QJ7Q3DTu29NLQgc -SKutn0291sLAm2M0OQIDAQABo00wSzAdBgNVHQ4EFgQU3M3ZPZ9s5gvE/FspkWQb -ig1OvSQwHwYDVR0jBBgwFoAU3M3ZPZ9s5gvE/FspkWQbig1OvSQwCQYDVR0TBAIw -ADANBgkqhkiG9w0BAQsFAAOCAQEAfp+JcImg7LQaBoL5ikKHVCWykjqBdU/0yXfK -lHVIRbDCZO1c/0hNfEU9aN7MXaPHxMtZOXX9d5i5Xt3PzTB1Q3Keb7QWYEK4lfs7 -H9IsHaMiax6UJM5Hb5xXCt8vCXRLcseD/in7Vm0+lqQ7Wp1UnwrN7NZ4PaiXTTmw -TMj4l9ElKtOG0vRr7loEsidE8Sc0X4H92Rs6gudh+Wtfb0O1aw0eRcpvUuO1NZLe -jpMxJMfDwyFh0URatAg8ZEPNV8HkgrEog2LltNVKSMMqi/DK/98wanSSUFFSVY8h -aXIlbCXp5F5Z1IqS+Lt11DEf0Q6N1la8Z1mad+skE7/HTqG36w== ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDTjCCAjYCFGK25l/HLuKMlZeUvy+4vikSGy9sMA0GCSqGSIb3DQEBCwUAMGMx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 +Q0EwHhcNMjYwNDEwMDQzMDQ4WhcNMzYwNDA3MDQzMDQ4WjBkMQswCQYDVQQGEwJD +TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzENMAsGA1UECgwE +RmFrZTENMAsGA1UECwwERmFrZTETMBEGA1UEAwwKRmFrZUNsaWVudDCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDa7bfVeA55s30ljVESiSs0m2ur7uSi +81ffQuZKpKlG6AeZW1hdIVPQFiEn6jfWkT9KjUWnJS/2hFrQcTH74vPQabTNcu0/ +8ywzg8uMEtcO0Jg3XlMj/4cOoB/SrGa37EXLe7FvewK66hx1edSwBxwaQw5rLDQZ +Lf1B/INRdFCcsdgelF3vch7l/OkrYsisr/y/j8ylXYji1ZHUvpiaMUDj0SMoRu4y +LA0G6FljjQC2hrMG4JfPItQH/6NDVFymqux2idMuNzYa+NdkQXsDBiTXNmPYel/M +ZCP57JTJg6Hu4mnfSBEgGNcdc6/lzK6lNmjTpYYR8ngL0PWbb54cvyUCAwEAATAN +BgkqhkiG9w0BAQsFAAOCAQEAIZYcaI7wOwYXj3oRsf/0Zyw7JXFvQbVmOdUXCOjl +c0XuJCe7wyiiyawgsGzF4mfKKFDOYBIjuHAMzNBWEjGcFYde5+UuqBJKP7oOrQVU +6kkMMcK56ewkNfsN4hEVco3KMj1VMYYRqInn30lUREgUjfYohEIC+weOrwrjgmIt +kqYYImUAofju2im5/ixJKyDWftj88vM8iRWlSznGj96ENboVzz/iY0ozCEQPwdGx +s5z8ouAa122HqrxX3epWG2t5GZ/aLJxvhmkrX4pveyx3fMUCs52inV1LDpATTzU/ +Q3k85ut/oeQ/3UGoEd+hmtFPtS+3jmTYGw48h9EwAtOhkg== +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake.key b/src/coro_rpc/tests/openssl_files/fake.key index d1bdafff5..725eaa8e3 100644 --- a/src/coro_rpc/tests/openssl_files/fake.key +++ b/src/coro_rpc/tests/openssl_files/fake.key @@ -1,28 +1,28 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrKUgvai8ByiVI -5QQZvnlN/ZJsVzNSo8HRc8X3HhRcUpwANx2FH4sMot33aTTTSDBfx+2zy+0CU520 -+PbHWZt6UXIwLrojoEBQaNzCAGxVeFdgzuLyDkLmOFfbUU1LLjry6tfXhW/YLBGE -K5aK6QaydlERAs8Ee3ALpYdJKfuhNh19MJET34XAs4lFpdt6MIGNltHfp0gwcU6+ -+LXDpCJLju/e7+d4nzpB+2xJFFOJE71MAVB+WtKB5jiyHxCOE96NVyT8B+wfHgJL -jIA5tIOXYHk2Bd3ZaKS6eIIaAm2BrK4IcJ+zUw/pAntDcNO7b00tCBxIq62fTb3W -wsCbYzQ5AgMBAAECggEAChvFihtUzF/CZPQ1kkmoA12i7KgXvV0zgKm8OtR5clxk -nzSiFy8eOLBTuJ1rg8DjLnzxwmkcRcNncH01od9eadJukn7n+luoALe4tfJtc3zI -eEyvpYkHFW6lbav3CyYfUCJjffSA/vzXSf2DBhAuF5MaRY1raYHaw61SeJU0qbgd -/H9yWYR49zB4csnTxAllJpKbIc1d2IargDgiv7GnzcA0dqROxcaXPU1cruuOag9f -iCd9awvMwy9XDqryEKyBGorN3WgT5TjMeFj8IBMLosOB15/Ruhuk/QNfijPPDS/R -KC7dX6BCHSu7cHHDjA43ikUZVerhOcq/P8ogbtOKiQKBgQDw51QTHz9BBCW5+7j6 -xvhYuosAuz2W+JJmMZlhy8SWHr4PIaQIG9E8CchssWiWSKPgDyHVfg2yTXP8Q8R+ -VT/AWAgeB3wrdMN6JNbNRPtkIGTtVgVDo7padBNbbPAN83+W3CdSF2RmJuAOInhb -Ez4Ctzq10kEyS1eTOT6yc9ujTQKBgQC14x5pdEHrRQpxpgq8qXxnqSlm030xtHJm -2z6oAxcsLVgwWcTRTU22ABJTV3939gJLhtFJ0p3DxfvCy/GOcawmeQsjpYs8j7gL -AIyYEMnxJTjzm+YwUn5uNl1xLeRa7P+YerIIwbC0tZxiNCvUo3IrTvfbIoBddCyZ -GhnqtK1GnQKBgCkIjiaPrPuLFE4AlXqJx6V9aM3gFtaPUoh7rE+fIMYdSGxVY5ZJ -/rLGS9BPy6vFhbxVd4Lg7L5ROQ9gD6khJjHCDOfoiHrycZVtjvT56gQdDHPssgra -aZScrutku+L0deghacUu3NgViRZ/QpboySg3Q5XS0W4arTkTiB1nZKMFAoGAWJBR -U5nHKy6/6hymZ7zDFZp5zVa3RAeQGOMyfA6dLuaZZVmgiyVv7GnWgnw9VgUUkv// -UknaheQWNYCmiuxwnX8c3GuUA5YbUEghLT4nhmLQe1Xy3J6ebz3Le/uTkG6L+gvs -OnVNfIBduDedC/nV8p6N80a2aErUGGxsKCt3n8UCgYEAt1hTr1a6HQheUv7RVC9i -KJu/k8n2olGUzE1pMZX6NlTgFKnmAQ7dLt6xeDBfMu0WBxbZktaSGM1T2Y0Tir+1 -+R/qH2inBE6FnL5rO8rb7ZiaDZy69Ntw4hJZGvryFcp6ZGraeUDqJO+e59xlQzmx -4/mvqmhlMJAYg7KJPfyH5tg= ------END PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg2u231XgOebN9 +JY1REokrNJtrq+7kovNX30LmSqSpRugHmVtYXSFT0BYhJ+o31pE/So1FpyUv9oRa +0HEx++Lz0Gm0zXLtP/MsM4PLjBLXDtCYN15TI/+HDqAf0qxmt+xFy3uxb3sCuuoc +dXnUsAccGkMOayw0GS39QfyDUXRQnLHYHpRd73Ie5fzpK2LIrK/8v4/MpV2I4tWR +1L6YmjFA49EjKEbuMiwNBuhZY40AtoazBuCXzyLUB/+jQ1RcpqrsdonTLjc2GvjX +ZEF7AwYk1zZj2HpfzGQj+eyUyYOh7uJp30gRIBjXHXOv5cyupTZo06WGEfJ4C9D1 +m2+eHL8lAgMBAAECggEAGHVRggteZu0QYq4MD9C+tKgaHcQV3gP89laHSJb+9JJI +g6dI4WW/xIu2YbI9BeKFUVdXpcileeaE934MSCphturwt8IpgHOh2Q24M4IH91VU +WGDK4d9uYi3SISSafD/pGC+5jiBCVwbuxAcE0Fc3rYk8uvuGzCEsAf8/kwQWqI79 +JSwTHkYTFQ4KJlims50GJqOxUypyfnvKTnD6z6D8860YZhHukZ/zyn/h/PyzKaWs +uaorDt2XdyuhLHRPt7plW0kk4foZNYFSU10q5QyC4snAo0P27b8JkbBqbLo2VyN0 +qXk1GKdM0tCnFTUAcCoVUYSVxuuvG7gMD+5SCFcBoQKBgQDTJgMso3MN/ic+Cyl+ +B7g15GWFo3JRabMBe79rD/AkyuwdjB/xIcO+mcuNMN9W940RMdqKyrU5n0HX+E3B +JGgdXdUFW8yiDXBmqiHeJ+KtjgAgkRutqh2tvssBTpTvxG+EnP7wmrMgwdR3NlcG +YHuWVtKH5JeuVw3H9AJ8SlgtbQKBgQDDBgfFSDzm9pxKRsamwdXi5Jcuvzv/BZER +96Im5RN3FSQeEgcCMHRZ6gfIrX7WiZcK6I1Y7zn2pej0uPk399Y5WpH/FMilU/dC +fqhyhw+ZEC7UwAujEuLoAm5/p8+JlIscoHY6SE1VIQnyIBpvIprUDSAxZiXSmERA +V8b83e1dmQKBgFoZ+HTN8sTf1WMWZEhRhvwIUIIscxXmoupZIh/Pl0w8A3HAX8kH +/X2SJ1hCqKt1y46w1W8wfRDvsqs0XAm1PXB6n/I/cB0e2v4UT4t7PbGNzOQYx9Td +qPiBWCNgoxGFo4jVbbzCZvntfHq6h3xDI7nNpt0yYL+Wilzu9TiQiPwpAoGAJ0uO +w0Rz6QGlqh2qpy0FQKtYfvXPS+o+OcWQqY+cpXDgDyMIwHss5nUOTnQOy4F7qpoC +6PmCz1zMnIqsxIcuZe4G3sO8TfumJYLJxBHMpcWp+focHiaPC7p5s0UHvyvr/7+h +MssgsddvXpbzXJM3aSSf6PQxCMbfcat+D7NTwXkCgYEAn43lkTPjym0nqqSPzaUA +CtqXYRcgyaoQnuj6E4G61Y4z5vDKD8SE6xHQc2F4tIDUJN1F5opXhb55zcOYSbrM +1MKtZQNdZfOM7hZtBRzZJrP+t7vieXiEgdA0yQVTQoBI1ycQd7erC0kOyzZsbloH +W4yoJlKUzm4JaQN0rNZOaas= +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/generate.txt b/src/coro_rpc/tests/openssl_files/generate.txt index 5c5f01fbb..0d94c72b9 100644 --- a/src/coro_rpc/tests/openssl_files/generate.txt +++ b/src/coro_rpc/tests/openssl_files/generate.txt @@ -1,7 +1,7 @@ -openssl genrsa -des3 -out server.key 1024 - -openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt - -openssl dhparam -out dh512.pem 512 - +openssl genrsa -des3 -out server.key 1024 + +openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt + +openssl dhparam -out dh512.pem 512 + openssl dhparam -out dhparam.pem 1024 \ No newline at end of file diff --git a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat index c2c837966..f1a1859e4 100644 --- a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat +++ b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat @@ -1,22 +1,103 @@ -@echo off -REM Generate test certificates for mutual SSL authentication - -REM Generate CA certificate -openssl genrsa -out ca.key 2048 -openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj /C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestCA - -REM Generate server certificate -openssl genrsa -out server.key 2048 -openssl req -new -key server.key -out server.csr -subj /C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=127.0.0.1 -openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt - -REM Generate client certificate -openssl genrsa -out client.key 2048 -openssl req -new -key client.key -out client.csr -subj /C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestClient -openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt - -REM Generate DH parameters -openssl dhparam -out dhparam.pem 1024 -openssl dhparam -out dh512.pem 512 - -echo Certificates generated successfully! +@echo off +REM Generate test certificates for mutual SSL authentication +REM This script generates CA, server, and client certificates for mutual auth testing only +REM The original fake_server.crt/key and server.crt/key are NOT modified + +echo ======================================== +echo Generating mutual authentication certificates +echo ======================================== + +REM Create OpenSSL config file +( +echo [req] +echo default_bits = 2048 +echo distinguished_name = req_distinguished_name +echo req_extensions = v3_req +echo x509_extensions = v3_ca +echo. +echo [req_distinguished_name] +echo countryName = CN +echo stateOrProvinceName = Beijing +echo localityName = Beijing +echo organizationName = Alibaba +echo organizationalUnitName = Test +echo commonName = TestCA +echo. +echo [v3_req] +echo keyUsage = keyEncipherment, dataEncipherment +echo extendedKeyUsage = serverAuth +echo subjectAltName = @alt_names +echo. +echo [v3_ca] +echo subjectKeyIdentifier = hash +echo authorityKeyIdentifier = keyid:always,issuer +echo basicConstraints = critical,CA:TRUE +echo keyUsage = critical, cRLSign, keyCertSign +echo. +echo [alt_names] +echo DNS.1 = localhost +echo DNS.2 = 127.0.0.1 +echo IP.1 = 127.0.0.1 +) > openssl_mutual_auth.cnf + +echo. +echo [1/6] Generating CA private key... +openssl genrsa -out ca.key 2048 + +echo. +echo [2/6] Generating CA certificate... +openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestCA" -config openssl_mutual_auth.cnf -extensions v3_ca + +echo. +echo [3/6] Generating server private key for mutual auth... +openssl genrsa -out server.key 2048 + +echo. +echo [4/6] Generating and signing server certificate... +openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=127.0.0.1" -config openssl_mutual_auth.cnf +openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile openssl_mutual_auth.cnf -extensions v3_req + +echo. +echo [5/6] Generating client private key... +openssl genrsa -out client.key 2048 + +echo. +echo [6/6] Generating and signing client certificate... +openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestClient" -config openssl_mutual_auth.cnf +openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt + +echo. +echo Generating fake certificates for negative testing... +openssl genrsa -out fake.key 2048 +openssl req -new -key fake.key -out fake.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Fake/OU=Fake/CN=FakeClient" -config openssl_mutual_auth.cnf +openssl x509 -req -days 3650 -in fake.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out fake.crt + +echo. +echo Cleaning up temporary files... +del *.csr 2>nul +del *.srl 2>nul +del ca.key 2>nul +del openssl_mutual_auth.cnf 2>nul + +echo. +echo ======================================== +echo Mutual auth certificates generated! +echo ======================================== +echo. +echo Generated files (for mutual auth only): +echo ca.crt - CA certificate +echo server.crt - Server certificate (CA-signed) +echo server.key - Server private key +echo client.crt - Client certificate +echo client.key - Client private key +echo fake.crt - Fake client certificate (for negative testing) +echo fake.key - Fake client private key (for negative testing) +echo. +echo Original files preserved: +echo fake_server.crt - Original server certificate +echo fake_server.key - Original server private key +echo dh512.pem - DH parameters +echo dhparam.pem - DH parameters +echo generate.txt - Original generation notes +echo. +pause diff --git a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh index bcef1072a..d9ccb9c75 100644 --- a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh +++ b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh @@ -1,30 +1,110 @@ -#!/ bin / bash -#Generate test certificates for mutual SSL authentication - -#Generate CA certificate -openssl genrsa - out ca.key 2048 openssl req - new - x509 - days 3650 - - key ca.key - out ca.crt - - subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestCA" - -#Generate server certificate - openssl genrsa - - out server.key 2048 openssl req - new - key server.key - out server.csr - - subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=127.0.0.1" openssl x509 - - req - days 3650 - in server.csr - CA ca.crt - CAkey ca.key - - CAcreateserial - - out server.crt - -#Generate client certificate - openssl genrsa - - out client.key 2048 openssl req - new - key client.key - out client.csr - - subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/CN=TestClient" openssl x509 - - req - days 3650 - in client.csr - CA ca.crt - CAkey ca.key - - CAcreateserial - - out client.crt - -#Generate DH parameters - openssl dhparam - - out dhparam.pem 1024 openssl dhparam - - out dh512.pem 512 - - echo "Certificates generated successfully!" +#!/bin/bash +# Generate test certificates for mutual SSL authentication +# This script generates CA, server, and client certificates for mutual auth testing only +# The original fake_server.crt/key and server.crt/key are NOT modified + +echo "========================================" +echo "Generating mutual authentication certificates" +echo "========================================" + +# Create OpenSSL config file +cat > openssl_mutual_auth.cnf << 'EOF' +[req] +default_bits = 2048 +distinguished_name = req_distinguished_name +req_extensions = v3_req +x509_extensions = v3_ca + +[req_distinguished_name] +countryName = CN +stateOrProvinceName = Beijing +localityName = Beijing +organizationName = Alibaba +organizationalUnitName = Test +commonName = TestCA + +[v3_req] +keyUsage = keyEncipherment, dataEncipherment +extendedKeyUsage = serverAuth +subjectAltName = @alt_names + +[v3_ca] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical,CA:TRUE +keyUsage = critical, cRLSign, keyCertSign + +[alt_names] +DNS.1 = localhost +DNS.2 = 127.0.0.1 +IP.1 = 127.0.0.1 +EOF + +echo "" +echo "[1/6] Generating CA private key..." +openssl genrsa -out ca.key 2048 + +echo "" +echo "[2/6] Generating CA certificate..." +openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestCA" \ + -config openssl_mutual_auth.cnf -extensions v3_ca + +echo "" +echo "[3/6] Generating server private key for mutual auth..." +openssl genrsa -out server.key 2048 + +echo "" +echo "[4/6] Generating and signing server certificate..." +openssl req -new -key server.key -out server.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=127.0.0.1" \ + -config openssl_mutual_auth.cnf +openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out server.crt \ + -extfile openssl_mutual_auth.cnf -extensions v3_req + +echo "" +echo "[5/6] Generating client private key..." +openssl genrsa -out client.key 2048 + +echo "" +echo "[6/6] Generating and signing client certificate..." +openssl req -new -key client.key -out client.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestClient" \ + -config openssl_mutual_auth.cnf +openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out client.crt + +echo "" +echo "Generating fake certificates for negative testing..." +openssl genrsa -out fake.key 2048 +openssl req -new -key fake.key -out fake.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Fake/OU=Fake/CN=FakeClient" \ + -config openssl_mutual_auth.cnf +openssl x509 -req -days 3650 -in fake.csr -CA ca.crt -CAkey ca.key \ + -CAcreateserial -out fake.crt + +echo "" +echo "Cleaning up temporary files..." +rm -f *.csr *.srl ca.key openssl_mutual_auth.cnf + +echo "" +echo "========================================" +echo "Mutual auth certificates generated!" +echo "========================================" +echo "" +echo "Generated files (for mutual auth only):" +echo " ca.crt - CA certificate" +echo " server.crt - Server certificate (CA-signed)" +echo " server.key - Server private key" +echo " client.crt - Client certificate" +echo " client.key - Client private key" +echo " fake.crt - Fake client certificate (for negative testing)" +echo " fake.key - Fake client private key (for negative testing)" +echo "" +echo "Original files preserved:" +echo " fake_server.crt - Original server certificate" +echo " fake_server.key - Original server private key" +echo " dh512.pem - DH parameters" +echo " dhparam.pem - DH parameters" +echo " generate.txt - Original generation notes" diff --git a/src/coro_rpc/tests/openssl_files/server.crt b/src/coro_rpc/tests/openssl_files/server.crt index b89ecbd56..9d79d31c6 100644 --- a/src/coro_rpc/tests/openssl_files/server.crt +++ b/src/coro_rpc/tests/openssl_files/server.crt @@ -1,20 +1,23 @@ -----BEGIN CERTIFICATE----- -MIIDMjCCAhoCFCfqzDMJkItWsGmNb2dnTuQ7YiDfMA0GCSqGSIb3DQEBCwUAMFQx -CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDIy -OTQ5WhcNMzYwNDA1MDIyOTQ5WjBXMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp -amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTESMBAGA1UE -AwwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyEcB -wNSFuRb9/WLdS8BczaycllmOhwfMFqx/2Qd6XVEpOBmJsiDSQSHT1dpwMMZ7xCo5 -17QefyokWmBC+de83FY2EA+KWPreD+nZF1srDOOx6z+D08NN/7Go7eobRnUSBiSF -8BHk8J28I8Mm/h/0mngSQi7tUsnxKJ67yyHN1VTu/oZ24xeN/FSuTPqmLc4iB0/s -NOIpSjlgesP5tgAx4QW3++Z0NQjZamHYASvBmDBzlFTj2HzUcVEVWUotqX6f/l5d -JLseVnEU3bUGrtEyZMXrfz1aVT4vAlgtQ9J31tDAkzZq3Zi3PlLD7Jg2ns+oojS3 -Gfpi6DmNJw8u0FumiQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCAGgMOsNeoj0pW -jszmz/IeEPWpHkzT88f4n3D/o5mkVvagEgP9QF+WpnjpdyYW7pruOJXdSJQzYG+C -dbY1h22qw9pqFu3AwCBD1sqPl/5DqwqeyAFvw/d9/nTyCo2VK6Yvpbc6H7wggMFo -8xzK39mJE5kJsl42PJ3meFt4Loxz0FHL4Le0cVp30XnjOoiOroZNHoMk32XS7lGu -2bmWMjiAJSdUP/Btm1xoYUxPUYdqz4MzyYZQCjc0izkLxmbkO41vX/ZkoYlDwCcb -E7XXeqn2kmLkB6aD8d6UJnFtxwuos6YhVBfKeySN4fWXKNPZweRKOcUOaL7GGDWU -m1HkKMpu +MIID5DCCAsygAwIBAgIUYrbmX8cu4oyVl5S/L7i+KRIbL2owDQYJKoZIhvcNAQEL +BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM +BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGYxCzAJBgNV +BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD +VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MRIwEAYDVQQDDAkxMjcuMC4wLjEw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZoBMxTWffKpQOXd7eIVHZ +8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C0Wxth33q +ww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdKj2t1iUcz +1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR01yNvTXe +qTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUlKAiEFUO+ +7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHaG9IJJw2j +AgMBAAGjgYwwgYkwCwYDVR0PBAQDAgQwMBMGA1UdJQQMMAoGCCsGAQUFBwMBMCUG +A1UdEQQeMByCCWxvY2FsaG9zdIIJMTI3LjAuMC4xhwR/AAABMB0GA1UdDgQWBBRi +FJ5dKk3VTdYkJYJJ2H4+oeU8ODAfBgNVHSMEGDAWgBRnstOwi7/KoizEJkfFsuHX +QuKbZTANBgkqhkiG9w0BAQsFAAOCAQEAHRyHOVKYQl5jmDbnRQ2GQXdk4uysaPDQ +HwSbLRwoKnQMTN2dh0Uu8odkaXiGKO2tdnRTfE/Ai3UXo6jSKXI3SHlr//nUNoMw +5RNa6VFldlP2GbEtRh3P0YFPtWo9pAs7WV0FS3g0SMFA5CGjVlrbVoqOrhqjlScp +gr7wqGJsOqsSDLkg6N90aH2OYHdNePGMArR+h0Z+QEaaNBuRr6O0aZnBtCN+kDQw +37ATj9Cj0uJaLjWexyG821gYCWtBvq0x/9c9tIV2HnD9VvKwlcZqlC/QCGz8tafE +vWQ64kYniH9npgAaD+TAUlkSia60R8z+RTd6opvPB+rvMsFOEIYNqQ== -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/server.key b/src/coro_rpc/tests/openssl_files/server.key index 42c3a1f23..a05b48fb1 100644 --- a/src/coro_rpc/tests/openssl_files/server.key +++ b/src/coro_rpc/tests/openssl_files/server.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIRwHA1IW5Fv39 -Yt1LwFzNrJyWWY6HB8wWrH/ZB3pdUSk4GYmyINJBIdPV2nAwxnvEKjnXtB5/KiRa -YEL517zcVjYQD4pY+t4P6dkXWysM47HrP4PTw03/sajt6htGdRIGJIXwEeTwnbwj -wyb+H/SaeBJCLu1SyfEonrvLIc3VVO7+hnbjF438VK5M+qYtziIHT+w04ilKOWB6 -w/m2ADHhBbf75nQ1CNlqYdgBK8GYMHOUVOPYfNRxURVZSi2pfp/+Xl0kux5WcRTd -tQau0TJkxet/PVpVPi8CWC1D0nfW0MCTNmrdmLc+UsPsmDaez6iiNLcZ+mLoOY0n -Dy7QW6aJAgMBAAECggEAFBYzEdD+3HJ25Ov+f/N6G1K9ncK7rcVVbcy9QdojJqrW -NH8zNT9fdxLaeS9gYzP1A9asWHsDOAPVA492CDLgCUVIRNIaMRRwIy50DIijR7aq -iIqlQR7sesGpzLrXI3joZt9Q46QXzx4y2W9gQMqZsMhsJGEqgkwouMc61IO+bi/a -II1Uf1EGijFd96Qk/bT76puakg6qjRoJ+SdjNJOM/cOd8Jon6qnvXB6jOtW+DqIs -539RvibVK4pPr8OIxzGSTXPqcEcdaKV0NiWbTWonuOZu4Nf49auK3awjpQPzzXPT -BPHMKSSAtFXUhjaHIaHOAyWnQ8NM/kgrOpvSxMxkhQKBgQDkQIFmMzw+LsjnzAeg -IrA6ANzfPQJzhkGQRuv57Cv6CWVQXpatixgvFpd8CHviIwVD90hNQH+PMF1fM1pf -aYEOkRdWhFjSSIEkO2VRkcS8B0unexNeBbTupQfbvVx8etu8N+SEwevhWv0rNs7R -lF5JkZx+g1iT/6hTpES14i/6PwKBgQDgn+VSP7USOAgCwTU1kJcCBNUS5sROcpWV -5BrRjzkRvVo7wFxCBEOzKoukAV+RZJFwvvam0PxSwP3eLtjYOyW+BT2uywJ10K28 -OgSPkUnMg6PKw0ilCz38CvCw0rI9g8aS1akFCNZ53LFIognWwqpnZsJEm1yR0Qlv -Tj/ERNxdNwKBgBLHe938uSgkkUMA9l+mevlKuOFlE56NnTdRnnihhby8qSlDnwII -P6UgJrZ9vDOOzhAZeEli1RvizsvWXckb1RJtvY3Qtb4XWQiyGlPrulP+Batx5NYH -gitgSJU7rzBOq2WA87w4eD/CTLIRgFKd8mP7JvUBuXfzwNWg3kZYpbnhAoGAEzNX -xNoRPkdv19xwEe4UGmYTWJRFP3dn9fIToMofVLbc2bKtsC7xIoWGfjRn2OPB0uNf -7g57Iw/AI5fZjVIw/bcw+Jn90dhOoYJMFYGTz1mJTLG4qfL2D29X96Vq+vsipDaD -RhzlSHFm7hB7ytHFAyWzgW3OUeCOb+c+aCaCt60CgYBZaensYV8gEd/z06DZ5SQC -cPdRpC7TOCJJyi9Lr3VJDUZJaF4gfnOjzNKQqzh3Jy/vrtDkYJArtIKbISK6Kb2q -+pXpItKPuYFNeIRoieZXy9hSJhaw88fcDfMVODG7Fx0h2LcIFmoNXxNFEayUjkLt -sVgJ1M3OhYqqfZ+8d4QoZg== +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZoBMxTWffKpQO +Xd7eIVHZ8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C +0Wxth33qww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdK +j2t1iUcz1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR +01yNvTXeqTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUl +KAiEFUO+7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHa +G9IJJw2jAgMBAAECggEAKwfYeGXbI3p0sDWA+K1SlP8tgFnL5RplIBehHR9FDtI4 +Y6clEK/T0bUObZsMoM0XMp54kA2ror1LxK7OdTuPfJOYH4yfT94zx8Z8sxs5GkCo +0YBRfoP8RY6uFV+MAvSXGB/u004ndnykoODdU7XZGN6dDVFrJQ5atCZShHNto63Q +voCoKP+MFtEdGFMY8ymLIKuRH3JAY9xof+fS/6vo2fClUxQPEuH7VN+Pr4A/j2ID +WUOdcClGHPuGDW2nGKyEcj/VBKnoT7rcU9u3FDf8/CuG4hf+720/1T/tgPdgyn02 +LVcb78ELlQQ5iLke72FvVQDCzZMUkA4+NYf4A0EUOQKBgQDUenrK6rbjdamsZ+Ze +hcEZpGIoYIzl2fVk2mAJd5ssE6JcGfHAZsFOhtAGICGVZedpOC7vw0fnBDAqJsP2 +RJ0mhehzScTTnqIsNLL8y76jkwI+19tBWFk/VKAgCxhpOtevJ0Tg6wsz4gU0zNi8 +TSICoUvrgeHc4xFIlJ4ppyCcpwKBgQC5F5JUEnrhMwkqIBmnlSWaKZ5HI9riRZXd +TdQmgmFYGZNU8IpyoqaJhBIw9O+5O3NggK7jPr7zOUEGoN9lQvwEy4s5KmVv3ahE +fJimXTd34WML/N1Klfkv9u4M8ajSwanJRv0CuNLGASnuB28jHUhzi0S7u0KoAMVl +nO1wOIH6pQKBgQC+RfAwRoAAsR2AhoqFmsGRdONxxQRn0QcviV73G2SZ0/tQ/uq+ +oUX6TD99PMjWQKs6TlW53+ZDujxngs95eXhBRykiFK1t1GDltUuTleDiGoOoQyTV +H4jMbbv29fimQ0tiTogf4lvl2kFPRyHPfkM2l7qk62qXo7+Wf4AjTMangQKBgE1F +afFvRZ1+kPTlMdCkk24osctBTL/qDQa21zq8c6c0Bi4Pvbzd6mi+mlmV5/6Msz42 +esykBVPFM0BxKNI9hLj7wMO8z1xETVtKKPBLOjzx+0el6TyaH83GaNs+iBx0fU1q +NXZNcrD3C2oz8FHYh8a0/ZziMJGywLYLLZUMAjeBAoGBANEOpeitU+8sYOSz1rmi +dtNKnMWFSxEWjtlxzXgRuIC8HJis9YHLI5Hny0Iao3to4AEXRZ/r6EL9+/Z10AdZ +iGmiN/cbMn6yTErpehKLLCB3S2nLxOdeLIgOcgPcJDyEyvyXCcBI3A9W4uMxYI2I +kplBOEOtHG/SR3vfiHN8Mdpb -----END PRIVATE KEY----- From a3a0b59451e7184c9f599e79d48165a362920da7 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Fri, 10 Apr 2026 14:09:24 +0800 Subject: [PATCH 32/38] fix: use separate mutual_ prefixed certs for mutual auth tests - Restore all original upstream cert/key files unchanged - Add mutual_ prefixed certs for mutual auth tests only - mutual_fake.crt is self-signed (not by mutual CA) for negative testing - Update test files to reference new mutual_ cert names --- src/coro_http/tests/openssl_files/ca.crt | 39 +++--- src/coro_http/tests/openssl_files/client.crt | 34 ++--- src/coro_http/tests/openssl_files/client.key | 52 ++++---- src/coro_http/tests/openssl_files/fake.crt | 38 +++--- src/coro_http/tests/openssl_files/fake.key | 52 ++++---- .../tests/openssl_files/fake_server.crt | 14 ++ .../tests/openssl_files/fake_server.key | 18 +++ .../tests/openssl_files/mutual_ca.crt | 22 ++++ .../tests/openssl_files/mutual_client.crt | 21 +++ .../tests/openssl_files/mutual_client.key | 28 ++++ .../tests/openssl_files/mutual_fake.crt | 22 ++++ .../tests/openssl_files/mutual_fake.key | 28 ++++ .../tests/openssl_files/mutual_server.crt | 20 +++ .../tests/openssl_files/mutual_server.key | 28 ++++ src/coro_http/tests/openssl_files/server.crt | 38 +++--- src/coro_http/tests/openssl_files/server.key | 58 +++++---- .../tests/test_http_ssl_mutual_auth.cpp | 10 +- src/coro_rpc/tests/openssl_files/ca.crt | 39 +++--- src/coro_rpc/tests/openssl_files/client.crt | 34 ++--- src/coro_rpc/tests/openssl_files/client.key | 52 ++++---- src/coro_rpc/tests/openssl_files/fake.crt | 38 +++--- src/coro_rpc/tests/openssl_files/fake.key | 52 ++++---- .../tests/openssl_files/fake_server.crt | 14 ++ .../tests/openssl_files/fake_server.key | 18 +++ src/coro_rpc/tests/openssl_files/generate.txt | 12 +- .../generate_mutual_auth_certs.bat | 104 +++++---------- .../generate_mutual_auth_certs.sh | 121 ++++++------------ .../tests/openssl_files/mutual_ca.crt | 22 ++++ .../tests/openssl_files/mutual_client.crt | 21 +++ .../tests/openssl_files/mutual_client.key | 28 ++++ .../tests/openssl_files/mutual_fake.crt | 22 ++++ .../tests/openssl_files/mutual_fake.key | 28 ++++ .../tests/openssl_files/mutual_server.crt | 20 +++ .../tests/openssl_files/mutual_server.key | 28 ++++ src/coro_rpc/tests/openssl_files/server.crt | 38 +++--- src/coro_rpc/tests/openssl_files/server.csr | 11 -- src/coro_rpc/tests/openssl_files/server.key | 58 +++++---- .../tests/test_rpc_ssl_mutual_auth.cpp | 12 +- 38 files changed, 795 insertions(+), 499 deletions(-) create mode 100644 src/coro_http/tests/openssl_files/fake_server.crt create mode 100644 src/coro_http/tests/openssl_files/fake_server.key create mode 100644 src/coro_http/tests/openssl_files/mutual_ca.crt create mode 100644 src/coro_http/tests/openssl_files/mutual_client.crt create mode 100644 src/coro_http/tests/openssl_files/mutual_client.key create mode 100644 src/coro_http/tests/openssl_files/mutual_fake.crt create mode 100644 src/coro_http/tests/openssl_files/mutual_fake.key create mode 100644 src/coro_http/tests/openssl_files/mutual_server.crt create mode 100644 src/coro_http/tests/openssl_files/mutual_server.key create mode 100644 src/coro_rpc/tests/openssl_files/fake_server.crt create mode 100644 src/coro_rpc/tests/openssl_files/fake_server.key create mode 100644 src/coro_rpc/tests/openssl_files/mutual_ca.crt create mode 100644 src/coro_rpc/tests/openssl_files/mutual_client.crt create mode 100644 src/coro_rpc/tests/openssl_files/mutual_client.key create mode 100644 src/coro_rpc/tests/openssl_files/mutual_fake.crt create mode 100644 src/coro_rpc/tests/openssl_files/mutual_fake.key create mode 100644 src/coro_rpc/tests/openssl_files/mutual_server.crt create mode 100644 src/coro_rpc/tests/openssl_files/mutual_server.key delete mode 100644 src/coro_rpc/tests/openssl_files/server.csr diff --git a/src/coro_http/tests/openssl_files/ca.crt b/src/coro_http/tests/openssl_files/ca.crt index 6cc065352..d312957f5 100644 --- a/src/coro_http/tests/openssl_files/ca.crt +++ b/src/coro_http/tests/openssl_files/ca.crt @@ -1,22 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIUE0DZiUgfbUwiz2nF4zRYH7WC0YgwDQYJKoZIhvcNAQEL -BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM -BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGMxCzAJBgNV -BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD -VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0Q0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrbDIeJL2YCTvjlQCYx77DJmTg -TLorGcNn8HiZaAK5EhR0zh34szV97Ck9qUpMEMUOVBpQ4I4UEFWfYX/hOe9F0e37 -QTayDIKarc/Dz4CZu/i1ERzcweYs+Bkd+HBqSOweSQIdArYpTvQbennXo81Yk/3R -lgNQRLqaWdVB6A/P+qJPo4whytsbkbdFNw/PYX7TnuUKxfAsNHvE1UhUkyTs8den -sl/LjPqoX+NfZPcDDrlq7jtRNDHkh47LtFb5yqR6RQ/18uJenQhFyaqOAuIsYnPh -8n24kj05k+uz3gX++LRcmstX33SmVf6W2mIlWubFqSth7udlMF/yqMfHILhzAgMB -AAGjYzBhMB0GA1UdDgQWBBRnstOwi7/KoizEJkfFsuHXQuKbZTAfBgNVHSMEGDAW -gBRnstOwi7/KoizEJkfFsuHXQuKbZTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB -/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAXs08fcYcYZFUgzoVHX6wqME+uPBp -hB+zDq1s3ESqpP9ckuOiAtbAc+TKm4b1tVHW8ikYLmjZx57VarV9I/+VHEDUOM2Y -MROYnCDXytUAXcV6fjow5HbacYcnPGfa4iq6r+fDs3TdpUFikvWhg8kEmxy5bsGn -ADMNOriFEFXkKv4mjt20blHiKhv7uOGFtjZZk4UhZWE+qjHcRcCJDaxNudr7hHph -KlrsCiN4R3bkqukI6kIoaywQz14A3xhsEDAJF4v8qGhv2zC/7Cf2/TeLnC20BJ3s -W+uiVB89Fril42C87ZrxIZKbrKv1bqUXPBLg/QM5FKFwhkIgV2d4eldrYA== +MIIDiTCCAnGgAwIBAgIUMRSVN0J/XZ8QyzPrrPrmtcRyP/0wDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwODUwNThaFw0zNjA0MDUwODUwNThaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCW +F8KQGg1s+WXraGgYlmVJaBq5YMYAfWbIZ/uk/c7CUWypV3TvH4CFio97uLdNe6et +oC7Pjm4xpMMUzvuBLIs2OJ1Bf0LpPRreQ7gE0szOvyCtIO4dfZQHLK8v3r0KV2+P +sMLz1cElCrKJwD0gxT/DrWIb+iLSA5mCBj+ZZuYzUR+LX+yG9aFZcCdzZTCo64Uq +qmh9ZECL1Qs1YoSg1Wt6/4eOsgc93pn3iDI4gOl6b0SL51Fw05mGaBt2Ff8OEvJB +nd70tJ5tRtEoRSbO1hqKvEWwOGzduIYGW6LxA5YmlQLzTqFI2nIIE2RqTO8zBzQM +I+z6xeeS56PK5TOVpnAXAgMBAAGjUzBRMB0GA1UdDgQWBBRBmelVxpHNuAbb+y7T +9kOnZmtpTDAfBgNVHSMEGDAWgBRBmelVxpHNuAbb+y7T9kOnZmtpTDAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBneIHr0YZ7kM2bxFbiGYkjPi/V +bYPQicrXZ2wYpRsnt2Hil/uzVQdQsljTVAP3KnML8f7KBn3FET4rcFIy3JXZGCMh ++shWYHVbrZ2plyFb0j+XSusxuGaxFz5tviBdCZntOXrcJnq8E7giaywPXIrNWKzl ++hl7U7QjzYJsiw6khIWN2E/0bRtNw9EVgEa6Y4C6FFs5fpM9884QuJqnBHpaa7HW +vcf/o5+HZBkKqhDfAOOY/OYGHwOfUkmjO+DrZ26rvIh66NK9wJWktvpszQQ/vL+/ +ZqT3h7uQBWXMu6zk4nWz14/bZIBVEBlm+ugSb37NucyoJzTAwDbuMlh5NjTA -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.crt b/src/coro_http/tests/openssl_files/client.crt index 35a3078ce..d0ec8aa12 100644 --- a/src/coro_http/tests/openssl_files/client.crt +++ b/src/coro_http/tests/openssl_files/client.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDUTCCAjkCFGK25l/HLuKMlZeUvy+4vikSGy9rMA0GCSqGSIb3DQEBCwUAMGMx +MIIDMzCCAhsCFBqXnE0mVOXaOgJDnmSpB9TyawwzMA0GCSqGSIb3DQEBCwUAMFQx CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 -Q0EwHhcNMjYwNDEwMDQzMDQ3WhcNMzYwNDA3MDQzMDQ3WjBnMQswCQYDVQQGEwJD -TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwH -QWxpYmFiYTENMAsGA1UECwwEVGVzdDETMBEGA1UEAwwKVGVzdENsaWVudDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOkCl4uA5atdS8A0E1NX4PCEfezT -cvvlTJSsqaFIScU687P2stPGWAbuL+aav/6VNysIfyFXFk1R4YZgifT+YgzzqEoD -zF6F5YA+rVxm35vQ2MCqgLVmtlG2PWM8WxltbPCanQGlWIGwnb4W4hK+5ZWna4CL -NSfUhXZScBH7CfHXL6wvlkukKQvOLI1sqJWWTmk6JVyTwk8RzteSM5gB11/CvdNy -uB2l0ya/2ThltABdXljn6hIX4H69CZdt42gAIOr1N/rDspZ215/DAlcFxYWpxm47 -JUoGIK2BborwOfqCoc/LGrEk4DOK3Ey3za9hMH/O/WzdKK+0M2s9aH41G8ECAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAcGJUg2jrFlu6vQpsP7yccz3acKgFNMdLYr1C -A9zDpxWaOa9JFQTquiKKntKC69X39ulxBbDOuUMUMFFb1+Ra2gqUgm4uSq8YmXd5 -cTI4JyGryzzm9RDVIjkh55q7wof7HQHe/FuskvZzc6KEi/ZnVKxugj2DYT67jDzL -4upaAXqWbVUzqjH0W1/Nj/rqjifrt+DxoezMs8bFhZi19lhMZQ3OWD+CirhIl9Ke -FtzUAREDiE066mYkdnK4axbW2nsKRQFGOBJculgIgnnyfghfe1SNVBj3/NhNZuh+ -xUD7DkF6mz3Xz24RNVRhsLXDJM5c1vPnnCpESoqbu1ltBDw3mQ== +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDg1 +MDU4WhcNMzYwNDA1MDg1MDU4WjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkK +LHbsfM3NMvvFp6nI0MVa+PVHTUcPIUjXFbkyACbpL/ZvXKL1r02XWqgKJ8Z3hXDx +O1vgT6a17z2W1KaFR7S7NxJ9fYjZXHkSXuhmfTvMkWbbOoWrgcUjN/gKZEtJxsbs +JLTZhKVdyL5dli//LyYidij2JIY4MupOeWLmnMn/U8u+InA0bTfMIcjNb4Cbo8Rb +7R5cvu25cMK8flcq6qYz/4saZqKHt6mD2y4KS7LbBf1hBr/53UYSzZ5gNS18/1ud +S6rFSZ2KHrwWFgFgU+HflTndJ3XDsk24MaRqw3Y2M9RLmTxmquZ4KqSFHAXATDAK +iCKmv8BiAl+KTk7CbJMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAEHG9KlHqipxg +UEczDdKeSWsLwXnEmYziY3JiUrw762CSilEk1Ap2jN0fqxIaByWqIazX4lsuh5IJ +OxVfqT/QTgnICOpwDpiZnR0S6AzMNdh3GYt6qVnxNn/0zK4n+FlMOsUFH2etIxru +oQOcOIjeXcbd9mho1uf32DPZgFW9jd5mZripU+/cs6eRI2PWFKjR9oHjIdxTsj0S +XRJhJVTmffmZVpAoPlPv2fyo4Y5m6sVzixt+5z+EnDbhMEgDISJYZ4ge2MlV08Tp +/sjE242psTe+c3q2m4mU9jIUgUTKkyVP3iwjC15/+HJnMoeha1Mu7Y9rzL+qyTCJ +1ecQs/SZCQ== -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/client.key b/src/coro_http/tests/openssl_files/client.key index e6a05dd51..f9d5b9698 100644 --- a/src/coro_http/tests/openssl_files/client.key +++ b/src/coro_http/tests/openssl_files/client.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDpApeLgOWrXUvA -NBNTV+DwhH3s03L75UyUrKmhSEnFOvOz9rLTxlgG7i/mmr/+lTcrCH8hVxZNUeGG -YIn0/mIM86hKA8xeheWAPq1cZt+b0NjAqoC1ZrZRtj1jPFsZbWzwmp0BpViBsJ2+ -FuISvuWVp2uAizUn1IV2UnAR+wnx1y+sL5ZLpCkLziyNbKiVlk5pOiVck8JPEc7X -kjOYAddfwr3TcrgdpdMmv9k4ZbQAXV5Y5+oSF+B+vQmXbeNoACDq9Tf6w7KWdtef -wwJXBcWFqcZuOyVKBiCtgW6K8Dn6gqHPyxqxJOAzitxMt82vYTB/zv1s3SivtDNr -PWh+NRvBAgMBAAECgf86lhWqJ0Jq16xHDQ0pPgmnYNX9X1pJ5Q5+Jif4/79Qckmu -pxjAiJMZhVkU+PmRBZG/HlD24B+olQC526K2NvSG60RDOcfZ7VCsxLtDC7jLjw3/ -2+4PzGz0cXhBrEHhPS2FJGoC5ZPIj5SDWPnRpjV1fj1Ja1yyI1AvGVaFTPeYy1Za -AVG1GqWb7Gr+7LtWYlh5UTDbuOZTdZFhkMYILiewRZX0n2nL5DJ/RmrU7dizQQ8Y -KR6+y1XoXNl06Zu0mJorRPsXp8hi2KdA0W0OBvUIiS1ZCmj7shjn3gd2o6hb+Dux -ytY0yBEQ4LQ1Bw+ye4VYumF0SuSsK9s2jdewla0CgYEA/39T9pw7D6cocqRvlX6w -3s4SEvpVc53hGe5RdT4f2t1iaYmo3WYdahmYw1e4v5S6jOR2urNF5QTb/9HI5CE2 -vTzrzhfAjQrOgeojAW8SWde3qRgGJX9i2R+MvqyIjh7k7uGlxk5XtHnYWp3orl8I -to5uMCpNg0ywhmumZSoSmeUCgYEA6XfwaNsOFkOsOlGa55W0OMhsBcEpzg+9nZ/n -wPJAOfmSiCPC48DKWdHl3G1sM7dT/7xxvFlA5242Oex9ulF+/241+FOzzsKmIX5o -1sY23iIDSqjNcd1ItpHl+SAAXl4fLHU1hf1DwE4+J4IjvSeyrJIqJdhrSdEum56Q -iJZ07K0CgYEAj/ntk8PYWGrHFUtqgeDhxLx1XPJqovtt9RHiH2KByvEEWxqy6Qh5 -POftuO6+8l7afTjlWzJZCcSiQNe0EDJTSXKCIyIpZJGZa4ZIca9otO4l1gjutcTC -LD5mLrDFRulL8v1/UG+nZtFexTnE/DYbj9xVZZkBEyNtOmKBYvLBhq0CgYEA5Ud1 -oPQntHPHKwrDTtV1RSKG+2vEy2on9Cl6psEBlC3l2q8METFfR7Bbxgrr7SoIYylE -pQ0eMWnJ9T6sBpNMXjt04ygIeHAuSMxk1y+X6LSMeQCnqj//zdQgfnUQB5z1jmqZ -IrojlDMC1Tf4MyZOUS3GGJ6eVsMIu6mQFaN0to0CgYEAihXTENTPZ2KPfOECT5dr -HcyxTigY8R2cl+ekJI73TxgQ2csbRPrf0BBDGnp6jl575cGyOq1Ym4MfLWvnt/fn -3uGdzbk96KcVHqdjXc2FVHV3WlxJ90cF2uRWIoMW/hIaL3kSUkKGkBfNRZ6G5bLu -ALpqM+wAnNgzczPQpfGs61w= +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5Cix27HzNzTL7 +xaepyNDFWvj1R01HDyFI1xW5MgAm6S/2b1yi9a9Nl1qoCifGd4Vw8Ttb4E+mte89 +ltSmhUe0uzcSfX2I2Vx5El7oZn07zJFm2zqFq4HFIzf4CmRLScbG7CS02YSlXci+ +XZYv/y8mInYo9iSGODLqTnli5pzJ/1PLviJwNG03zCHIzW+Am6PEW+0eXL7tuXDC +vH5XKuqmM/+LGmaih7epg9suCkuy2wX9YQa/+d1GEs2eYDUtfP9bnUuqxUmdih68 +FhYBYFPh35U53Sd1w7JNuDGkasN2NjPUS5k8ZqrmeCqkhRwFwEwwCogipr/AYgJf +ik5OwmyTAgMBAAECggEACKnpyA2tkEM90l0ASypMYBkw0+cpPpz+PXdYnImzSR8c +8EhpiXXAZf0isQB8uUWqWU3qLhSUFiWplHQl88AEyfkEDJkUkh4/Pqn2CKKX1X9w +BWVZWZ3cnxrViYcPCBlltbWjyFXw6H86IXOnTwr/LaVqa+OOdTxXYfIXq+JJUAnQ +cF95KdwDBVmhmR+i2OP+uwPeBh7C1Q7kpmuhNwna/IYhJ/Dw6tb7LZ2/2pLdBs40 +IZWEcVOuaDw48CK9rzmuIomaxJs/pjGe3/m+wNQSWviA52BYxahfeSZgeBywiuWY +4JUkRUD4Mhu0PX+lMTTGdXaaV2FX+dXu+3KYJ0JdMQKBgQDtoWa8wRHIo5vRAR/7 +9gYkbZtyIN5iiO7ItBa40joiC+ylq4kalQIBQX48ATBRWkpjmONN1+p7l+VAUqBz +117njLbglESossoJ+UUwUgC+dPOwm6gsHhfNkjkPhDByT2z7rZuoRR8yBD2lyhqg +nTO6GOYGbkL5zWSpZDwHqILGsQKBgQDHWAaYAWGO0MlpNAgOlzyNeqjd7/RVJokF +zfeaf3haR1das6JpZWwuk6k6MyyFnZUvvOmtJ/S06Du4ZgOY9boy/NlgxFL40Efw +QMho/sUkdbwkCJlVcdsaX/z5n1FUkJpvUvHHTVt1H3kvspakN57+nF9hthWnkPgF ++vc1xHLAgwKBgQCp8TS9JsJmIAOuLETId8EayjxSGYmRJGbIqGpbkRU8BhUewhdS +KGB/r2vs09jPRWhP5CYjJJgv/YhZQP44+jyIEg0zfTXBA+QTz+4YSXz4uEES+68A +piDVxo4CN8JB7eV99EGOzKgrp/bCm1ABr4svuuC+lppVdftYXTPFMlEccQKBgBKe +RRdjYaI+G+Goi2wZcf1gzG7WH4Loc3nIT+ztJOeBrEX7axre7yi2f+LArtLX8fwg +b87NYYyX+CPz2zgpEzf556+jBoDYqy9kTZOI4A7UtDrFVtTlKmqfNnh2CdmVG+dz +3sMXlYgt5VqwGmPCEiaDomD06bbZ0mB0nSw3aeRTAoGBAL0dXkCCDYSjvoGFx3Ss +RvBN2ZtgLhjuzxg642R/Som8m8pH35Gv9dSaE0/+D5PI1K/7y0Oc0I3wJ03GRlVI +lm06pxtLWjt2cuI5/b3854P2eLwyZ1Ea+34bbeaJtsPB0m2+5rUyWsdpp4filZhj +uJ4px1WmbIsyJr+SIyk/Ecbc -----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/fake.crt b/src/coro_http/tests/openssl_files/fake.crt index c22e7eaba..a46e3c33c 100644 --- a/src/coro_http/tests/openssl_files/fake.crt +++ b/src/coro_http/tests/openssl_files/fake.crt @@ -1,20 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDTjCCAjYCFGK25l/HLuKMlZeUvy+4vikSGy9sMA0GCSqGSIb3DQEBCwUAMGMx -CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 -Q0EwHhcNMjYwNDEwMDQzMDQ4WhcNMzYwNDA3MDQzMDQ4WjBkMQswCQYDVQQGEwJD -TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzENMAsGA1UECgwE -RmFrZTENMAsGA1UECwwERmFrZTETMBEGA1UEAwwKRmFrZUNsaWVudDCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDa7bfVeA55s30ljVESiSs0m2ur7uSi -81ffQuZKpKlG6AeZW1hdIVPQFiEn6jfWkT9KjUWnJS/2hFrQcTH74vPQabTNcu0/ -8ywzg8uMEtcO0Jg3XlMj/4cOoB/SrGa37EXLe7FvewK66hx1edSwBxwaQw5rLDQZ -Lf1B/INRdFCcsdgelF3vch7l/OkrYsisr/y/j8ylXYji1ZHUvpiaMUDj0SMoRu4y -LA0G6FljjQC2hrMG4JfPItQH/6NDVFymqux2idMuNzYa+NdkQXsDBiTXNmPYel/M -ZCP57JTJg6Hu4mnfSBEgGNcdc6/lzK6lNmjTpYYR8ngL0PWbb54cvyUCAwEAATAN -BgkqhkiG9w0BAQsFAAOCAQEAIZYcaI7wOwYXj3oRsf/0Zyw7JXFvQbVmOdUXCOjl -c0XuJCe7wyiiyawgsGzF4mfKKFDOYBIjuHAMzNBWEjGcFYde5+UuqBJKP7oOrQVU -6kkMMcK56ewkNfsN4hEVco3KMj1VMYYRqInn30lUREgUjfYohEIC+weOrwrjgmIt -kqYYImUAofju2im5/ixJKyDWftj88vM8iRWlSznGj96ENboVzz/iY0ozCEQPwdGx -s5z8ouAa122HqrxX3epWG2t5GZ/aLJxvhmkrX4pveyx3fMUCs52inV1LDpATTzU/ -Q3k85ut/oeQ/3UGoEd+hmtFPtS+3jmTYGw48h9EwAtOhkg== +MIIDsTCCApmgAwIBAgIUE5kTRNz1GA3NOOBDwwqIAUz7ZJQwDQYJKoZIhvcNAQEL +BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYD +VQQDDApGYWtlQ2xpZW50MB4XDTI2MDQwOTAyNTMxNVoXDTM2MDQwNjAyNTMxNVow +azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWpp +bmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYDVQQD +DApGYWtlQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqylI +L2ovAcolSOUEGb55Tf2SbFczUqPB0XPF9x4UXFKcADcdhR+LDKLd92k000gwX8ft +s8vtAlOdtPj2x1mbelFyMC66I6BAUGjcwgBsVXhXYM7i8g5C5jhX21FNSy468urX +14Vv2CwRhCuWiukGsnZREQLPBHtwC6WHSSn7oTYdfTCRE9+FwLOJRaXbejCBjZbR +36dIMHFOvvi1w6QiS47v3u/neJ86QftsSRRTiRO9TAFQflrSgeY4sh8QjhPejVck +/AfsHx4CS4yAObSDl2B5NgXd2WikuniCGgJtgayuCHCfs1MP6QJ7Q3DTu29NLQgc +SKutn0291sLAm2M0OQIDAQABo00wSzAdBgNVHQ4EFgQU3M3ZPZ9s5gvE/FspkWQb +ig1OvSQwHwYDVR0jBBgwFoAU3M3ZPZ9s5gvE/FspkWQbig1OvSQwCQYDVR0TBAIw +ADANBgkqhkiG9w0BAQsFAAOCAQEAfp+JcImg7LQaBoL5ikKHVCWykjqBdU/0yXfK +lHVIRbDCZO1c/0hNfEU9aN7MXaPHxMtZOXX9d5i5Xt3PzTB1Q3Keb7QWYEK4lfs7 +H9IsHaMiax6UJM5Hb5xXCt8vCXRLcseD/in7Vm0+lqQ7Wp1UnwrN7NZ4PaiXTTmw +TMj4l9ElKtOG0vRr7loEsidE8Sc0X4H92Rs6gudh+Wtfb0O1aw0eRcpvUuO1NZLe +jpMxJMfDwyFh0URatAg8ZEPNV8HkgrEog2LltNVKSMMqi/DK/98wanSSUFFSVY8h +aXIlbCXp5F5Z1IqS+Lt11DEf0Q6N1la8Z1mad+skE7/HTqG36w== -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/fake.key b/src/coro_http/tests/openssl_files/fake.key index 725eaa8e3..f6924fcc2 100644 --- a/src/coro_http/tests/openssl_files/fake.key +++ b/src/coro_http/tests/openssl_files/fake.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg2u231XgOebN9 -JY1REokrNJtrq+7kovNX30LmSqSpRugHmVtYXSFT0BYhJ+o31pE/So1FpyUv9oRa -0HEx++Lz0Gm0zXLtP/MsM4PLjBLXDtCYN15TI/+HDqAf0qxmt+xFy3uxb3sCuuoc -dXnUsAccGkMOayw0GS39QfyDUXRQnLHYHpRd73Ie5fzpK2LIrK/8v4/MpV2I4tWR -1L6YmjFA49EjKEbuMiwNBuhZY40AtoazBuCXzyLUB/+jQ1RcpqrsdonTLjc2GvjX -ZEF7AwYk1zZj2HpfzGQj+eyUyYOh7uJp30gRIBjXHXOv5cyupTZo06WGEfJ4C9D1 -m2+eHL8lAgMBAAECggEAGHVRggteZu0QYq4MD9C+tKgaHcQV3gP89laHSJb+9JJI -g6dI4WW/xIu2YbI9BeKFUVdXpcileeaE934MSCphturwt8IpgHOh2Q24M4IH91VU -WGDK4d9uYi3SISSafD/pGC+5jiBCVwbuxAcE0Fc3rYk8uvuGzCEsAf8/kwQWqI79 -JSwTHkYTFQ4KJlims50GJqOxUypyfnvKTnD6z6D8860YZhHukZ/zyn/h/PyzKaWs -uaorDt2XdyuhLHRPt7plW0kk4foZNYFSU10q5QyC4snAo0P27b8JkbBqbLo2VyN0 -qXk1GKdM0tCnFTUAcCoVUYSVxuuvG7gMD+5SCFcBoQKBgQDTJgMso3MN/ic+Cyl+ -B7g15GWFo3JRabMBe79rD/AkyuwdjB/xIcO+mcuNMN9W940RMdqKyrU5n0HX+E3B -JGgdXdUFW8yiDXBmqiHeJ+KtjgAgkRutqh2tvssBTpTvxG+EnP7wmrMgwdR3NlcG -YHuWVtKH5JeuVw3H9AJ8SlgtbQKBgQDDBgfFSDzm9pxKRsamwdXi5Jcuvzv/BZER -96Im5RN3FSQeEgcCMHRZ6gfIrX7WiZcK6I1Y7zn2pej0uPk399Y5WpH/FMilU/dC -fqhyhw+ZEC7UwAujEuLoAm5/p8+JlIscoHY6SE1VIQnyIBpvIprUDSAxZiXSmERA -V8b83e1dmQKBgFoZ+HTN8sTf1WMWZEhRhvwIUIIscxXmoupZIh/Pl0w8A3HAX8kH -/X2SJ1hCqKt1y46w1W8wfRDvsqs0XAm1PXB6n/I/cB0e2v4UT4t7PbGNzOQYx9Td -qPiBWCNgoxGFo4jVbbzCZvntfHq6h3xDI7nNpt0yYL+Wilzu9TiQiPwpAoGAJ0uO -w0Rz6QGlqh2qpy0FQKtYfvXPS+o+OcWQqY+cpXDgDyMIwHss5nUOTnQOy4F7qpoC -6PmCz1zMnIqsxIcuZe4G3sO8TfumJYLJxBHMpcWp+focHiaPC7p5s0UHvyvr/7+h -MssgsddvXpbzXJM3aSSf6PQxCMbfcat+D7NTwXkCgYEAn43lkTPjym0nqqSPzaUA -CtqXYRcgyaoQnuj6E4G61Y4z5vDKD8SE6xHQc2F4tIDUJN1F5opXhb55zcOYSbrM -1MKtZQNdZfOM7hZtBRzZJrP+t7vieXiEgdA0yQVTQoBI1ycQd7erC0kOyzZsbloH -W4yoJlKUzm4JaQN0rNZOaas= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrKUgvai8ByiVI +5QQZvnlN/ZJsVzNSo8HRc8X3HhRcUpwANx2FH4sMot33aTTTSDBfx+2zy+0CU520 ++PbHWZt6UXIwLrojoEBQaNzCAGxVeFdgzuLyDkLmOFfbUU1LLjry6tfXhW/YLBGE +K5aK6QaydlERAs8Ee3ALpYdJKfuhNh19MJET34XAs4lFpdt6MIGNltHfp0gwcU6+ ++LXDpCJLju/e7+d4nzpB+2xJFFOJE71MAVB+WtKB5jiyHxCOE96NVyT8B+wfHgJL +jIA5tIOXYHk2Bd3ZaKS6eIIaAm2BrK4IcJ+zUw/pAntDcNO7b00tCBxIq62fTb3W +wsCbYzQ5AgMBAAECggEAChvFihtUzF/CZPQ1kkmoA12i7KgXvV0zgKm8OtR5clxk +nzSiFy8eOLBTuJ1rg8DjLnzxwmkcRcNncH01od9eadJukn7n+luoALe4tfJtc3zI +eEyvpYkHFW6lbav3CyYfUCJjffSA/vzXSf2DBhAuF5MaRY1raYHaw61SeJU0qbgd +/H9yWYR49zB4csnTxAllJpKbIc1d2IargDgiv7GnzcA0dqROxcaXPU1cruuOag9f +iCd9awvMwy9XDqryEKyBGorN3WgT5TjMeFj8IBMLosOB15/Ruhuk/QNfijPPDS/R +KC7dX6BCHSu7cHHDjA43ikUZVerhOcq/P8ogbtOKiQKBgQDw51QTHz9BBCW5+7j6 +xvhYuosAuz2W+JJmMZlhy8SWHr4PIaQIG9E8CchssWiWSKPgDyHVfg2yTXP8Q8R+ +VT/AWAgeB3wrdMN6JNbNRPtkIGTtVgVDo7padBNbbPAN83+W3CdSF2RmJuAOInhb +Ez4Ctzq10kEyS1eTOT6yc9ujTQKBgQC14x5pdEHrRQpxpgq8qXxnqSlm030xtHJm +2z6oAxcsLVgwWcTRTU22ABJTV3939gJLhtFJ0p3DxfvCy/GOcawmeQsjpYs8j7gL +AIyYEMnxJTjzm+YwUn5uNl1xLeRa7P+YerIIwbC0tZxiNCvUo3IrTvfbIoBddCyZ +GhnqtK1GnQKBgCkIjiaPrPuLFE4AlXqJx6V9aM3gFtaPUoh7rE+fIMYdSGxVY5ZJ +/rLGS9BPy6vFhbxVd4Lg7L5ROQ9gD6khJjHCDOfoiHrycZVtjvT56gQdDHPssgra +aZScrutku+L0deghacUu3NgViRZ/QpboySg3Q5XS0W4arTkTiB1nZKMFAoGAWJBR +U5nHKy6/6hymZ7zDFZp5zVa3RAeQGOMyfA6dLuaZZVmgiyVv7GnWgnw9VgUUkv// +UknaheQWNYCmiuxwnX8c3GuUA5YbUEghLT4nhmLQe1Xy3J6ebz3Le/uTkG6L+gvs +OnVNfIBduDedC/nV8p6N80a2aErUGGxsKCt3n8UCgYEAt1hTr1a6HQheUv7RVC9i +KJu/k8n2olGUzE1pMZX6NlTgFKnmAQ7dLt6xeDBfMu0WBxbZktaSGM1T2Y0Tir+1 ++R/qH2inBE6FnL5rO8rb7ZiaDZy69Ntw4hJZGvryFcp6ZGraeUDqJO+e59xlQzmx +4/mvqmhlMJAYg7KJPfyH5tg= -----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/fake_server.crt b/src/coro_http/tests/openssl_files/fake_server.crt new file mode 100644 index 000000000..b56981813 --- /dev/null +++ b/src/coro_http/tests/openssl_files/fake_server.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +AIICIzCCAYwCCQDHoPajMKX7WDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh +bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIwODAzMDYwNjM3WhcNMzIw +NzMxMDYwNjM3WjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOpVxgJHmtZ88gja1MQm22O +ghYUT7T5X/85sJ0BenfghclyQ8KtxEDPGq4BSbheb+94C6x7YIY3GQ82y9R7nrcx +bcD3sGGRBn13ROyjz9HRUMTei2Uwd2yfIVY3Y0MDb0WMmjGnNTgj1h1YAYSWouC/ +4p/IitnVwmG+ZhwCxafhAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAuqmsA/xLqRW1 +9INE36S1T3xi56bIYdlYS2JRzXNrIqdb1RnNPHyFU0Tizv6dkUDec5VfunR5ZgAq +Hxsr5cowB1cdWi21FjjNGXrd1/nYHTdnxHfMGKFxjHXc+s5+qyFnmQatHJO8hyCR +ljmD56V/y1nMcFna0Gtear44Rh5s6fY= +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/fake_server.key b/src/coro_http/tests/openssl_files/fake_server.key new file mode 100644 index 000000000..c55263b04 --- /dev/null +++ b/src/coro_http/tests/openssl_files/fake_server.key @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,D5B5BF16335844E5 + +X2Qj15pkVsN24Qu3N+sORjm0eDD+I2lrEfEI6QmMg33PV0udkNDEMtchJJ12Uzl/ +Czsj2ydFLHbFCUGGDqx7CfzYNBQhJvw74nrakXDv8uRdygvc0bEym35Nkukx5XNV +FL/jnTChO5ZneHHYYgtMDzsWN7+0oKTHyizN43nGNbBJJEy3hFje4k4ATFrCUXZt +z4OVge2M5JATR2ddFGX62ZrxFmg+PsbvSnfpxZesRXHtu0NPCK/Xv38vGYSaJ0s0 +XlK7A6Ko4O6RfBZXSXCO09GD27HA0zveQfLW2ILhbl4GZrAFd1d7d1Jdltxlvbw6 +cUp8dd3f6hAb5mAk1io4Xey++CEiwkpBgis9qAkFqxPPzwq+cB6LTu2KfzffvAze +FOZ7Yq5JyDLt7frpJ+PTFfv3bNyXdabBt6E9ZdEhIbvf/ArA+JuV4mJ8ilvsurnq +h54Z+R19V8kXUEekKDSYl2WsMrTY7o8ZY0LgiIbTG+LFqGjUpyclx6luz9r2d3yr +/ngKqbG30jct0Y7mDuVz8Vgml9wDqv4JmC/iUxZyGLuZlSRWFqV6RRCzGncMqy+w +wZb5XNGK2CwKCwmTtWuJm3rS88yo2MmJPtkbPeG9JRvUaAsg1uaExA5RRIk2zyIA +mwp0ItT4S63g6iObFadd7l2cDBdIJNMwJ/80FNr+HknBnkC3OYsIimrscoUS5vBc +V+a8C6wJ6A4IDrNXLP5so2PuygE47CGb6ehlzpda9Eyp030JLooeKt7sqWpX7ENA +JmSDScpG6xe5SR0buKVlP4Qf9IqpB6K8CnHsJWiJI40= +-----END RSA PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/mutual_ca.crt b/src/coro_http/tests/openssl_files/mutual_ca.crt new file mode 100644 index 000000000..e75df18d5 --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDuTCCAqGgAwIBAgIUKCavP59Twp7Vlap3ZuoEgC1QIYswDQYJKoZIhvcNAQEL +BQAwbDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEzARBgNVBAoMCk11dHVhbFRlc3QxDTALBgNVBAsMBFRlc3QxFTATBgNV +BAMMDE11dHVhbFRlc3RDQTAeFw0yNjA0MTAwNTU0MjJaFw0zNjA0MDcwNTU0MjJa +MGwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlq +aW5nMRMwEQYDVQQKDApNdXR1YWxUZXN0MQ0wCwYDVQQLDARUZXN0MRUwEwYDVQQD +DAxNdXR1YWxUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+ +OCxjH+5E7Bn3DBrGgsC7I7gTKqzKXNAD6RsxN3IlzDC5GPrB9tU3yYHL3BirW7JB +aJUFGW5M+aXjHu/y+qdZjFgLQvP0a9pzxMbyFcYKod0i95q7aCm9YCfw1DCvxZfB +sal0bltG0IEg+/4MGXyFUdhhmyvRigqgaBC5MmV2f6SjxQSp7c6/0sH3kBB/qoYF +GmKPPSz6ubr0Fmkg/deU+51OhuUli5kNYsguyVL1yNCNVo4w00BVfflUOPMde0ul +7qURZV/7Gui0OMcCkP2fxS1xFhOdvfj56jrwd7I95O82XaIe+Hu7S5nMm6p8S83u +rjXwIdOaK7g57Nt9FOh/AgMBAAGjUzBRMB0GA1UdDgQWBBQfzBZduML17QrUO9vm +Icky7Vfx5zAfBgNVHSMEGDAWgBQfzBZduML17QrUO9vmIcky7Vfx5zAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCn7tinfFMl1sQTF7I+lE1zkITF +GHEhd7LAjj/MhM8zkf4bIdCSsGLormxxjk+uZzE1voZ+St22y4I4si+1+EosOSp2 +l7grZNHbVQGIU+HcI4Necg85yCToUikPj923Ql0ZPq2lBKaOjYVSWUlNB8EpBMgV +nVDaTmyIT3rvKimEgBbyK93kuRg3qhlH20QQnXdkH1ns4uqxoP3iBPdniSk4iBBW +yHQMPkqRQMH8FO5mcINGT3h9kd9pSP1A4ZFr7CusH8nbHz0tTdzg4IBXMZtYocoP +NqoASGGanNNb7jbK/fgQ0PDWsrSM5g+CfmLYRr/f//Io5bAZCUFW/u2YuB2l +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/mutual_client.crt b/src/coro_http/tests/openssl_files/mutual_client.crt new file mode 100644 index 000000000..7ac580e5d --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_client.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYzCCAksCFBQv5CdHFz7el4unEPzp6yhwBju5MA0GCSqGSIb3DQEBCwUAMGwx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRMwEQYDVQQKDApNdXR1YWxUZXN0MQ0wCwYDVQQLDARUZXN0MRUwEwYDVQQDDAxN +dXR1YWxUZXN0Q0EwHhcNMjYwNDEwMDU1NDIyWhcNMzYwNDA3MDU1NDIyWjBwMQsw +CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzET +MBEGA1UECgwKTXV0dWFsVGVzdDENMAsGA1UECwwEVGVzdDEZMBcGA1UEAwwQTXV0 +dWFsVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOo +gip+F/KreIBBzFDxX0nhsmhaB3JNkdVjNYsUTL9nCUQNIHokhhEp8PFiBz5J+1A8 +x62yRq0DM33psZi7NGUUkYh/ndPKdjdS7iypH6NWkBHX1ggcAGyT01y64kB1sotT +ozfQNvnY8mGxCdto4O2gJfZa/VMQqRXRFgaXOz3tIkuhtxvurqIr/Lv43j/LyGuk ++Q+ombP+Pll7euT1dXrCrhZDFRQVCNkkunR5SDEOZtoGEKeEKb6BDmIq9FSwGym7 +qK0cqPXUd0bW9RumueWKLhmcoV8lgkc0xWxyTngCcm/brzUj2btiUfUxwVXavHmt +BuKx7v2wSoH349/DvssCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAaQJhVvnUUI/b +lcYmc/FrrxntdO01+fYD6lr8Y4iAjiXrQAr9xSKZAhHEHqyB03ipFgSM98MiCcjF +Nf0u5qZxXENHZ0r3uSAN/hpajPS7y+PDquOszeqs3N4irIX2003Ev902/tPuARLJ +UPN2sT1IGsdXfw+S83uAHFH3LPW5y8dc1xF7sAyqo5OT2aTG1/gwMMJGow48N0qp +8OhAebfJSf6e0MZZSr+apNMMNRAHkiJnRfjeglGujW99PzpAyLEWCGgzkqXsswg3 +ktVtpgtPRPNRgs54DiLTXewfvlzyHvZ1nGVo9fc1lz0Fy1XyxliR47V4Qb9HYosF +RmlzfIraCw== +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/mutual_client.key b/src/coro_http/tests/openssl_files/mutual_client.key new file mode 100644 index 000000000..de373d554 --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDqIIqfhfyq3iA +QcxQ8V9J4bJoWgdyTZHVYzWLFEy/ZwlEDSB6JIYRKfDxYgc+SftQPMetskatAzN9 +6bGYuzRlFJGIf53TynY3Uu4sqR+jVpAR19YIHABsk9NcuuJAdbKLU6M30Db52PJh +sQnbaODtoCX2Wv1TEKkV0RYGlzs97SJLobcb7q6iK/y7+N4/y8hrpPkPqJmz/j5Z +e3rk9XV6wq4WQxUUFQjZJLp0eUgxDmbaBhCnhCm+gQ5iKvRUsBspu6itHKj11HdG +1vUbprnlii4ZnKFfJYJHNMVsck54AnJv2681I9m7YlH1McFV2rx5rQbise79sEqB +9+Pfw77LAgMBAAECggEAFDJXxWUgubcDiFHCcnSH/otirCzm6eCh9iH4i/O7fGJ5 +bWHhgVo10J3AtloFH2Ppoj2z6vUlIITdEtlFsNtaLDj0UN/DffI/Q7S2yztl/alY +086w1EN3s72Kqt7LrhW4KXOnvIIsupuvYXAx8UkhNsY6RPTdg26L1amwmVuRDPI4 +QZxqBYcYdPS2TkzhNzRGajnkM4qm6ZPJDUHZcl4iqEkUHU4AB7zvmWQAFsyTJFZR +TKQI5StGdxHrW1QEpijGZ9H5sNv+ssHIQtdSPdbYTXThkXP/UQ3VgtsAhFuIsr1h +wTZyAL/+zVz4sJHO/5B8OmCgiag6KSxaKkDiHkFnMQKBgQDpriZg5tsEW1K0KWCU +VdyN40bJowW8nbgdTVcA9AhESd5qvOYVgnJ5VDNZyHZTGuISLW5Q9UhVKjlu2aY0 +TINAk3q97E1gYZ/iVGS+WADR8ICKfDZF7FU256psNQ+sqyLR8zqu7KR0HA/n5Z0a +uKEh1mVir8Zbz9HHm1cxrOrceQKBgQDWWKjzNx2n7bmP523i57iZdXkmejBpqSZH +wg1KXZ/ETdnECN6OWyTgCMyKFBbdSZU0SH0Mz2c/skYX5hZUGa4P9ZAQEkuI9Q/z +Q14nlWbukW5V1lSUjMR3ZR8g5ONeKqR/zoyFQINZdiiY2cnfcEAolnT6qCARhJVP ++w97oB1cYwKBgEbuUr31NSP0aH1BVgyQp3r2MwV/k302Tq2uTSt/54Z6+aVio2CC +ESdc9J1bKPd+4IJuAd9XJNadE4PfUwDq/Kg8W/SMZsxLtdFolo/kfJM9MndWzs6Y +tyEMXwGrdY+O/unFr9lrAVwxLG7SlsaGpnpz7qBvBIHX6jBxqZzthPjZAoGBAM0Z +rSB9Hr1vNd5C/tzeCb+drr1osiaImn6TapA8IgJ+099G9V6WTCSrhryhGHfKTyDm +M/IsC4nhljyMB9WVdP8EZENcnjaA+DA3yEJsLUAenMs5+VjjkaMFedHJ8t5KQ3mg +NMnUv1q9O3929jn9eQbdYTXv5i+dBBqyC1CqFy4tAoGBANXci7qB5h2EWmPaQOGI +kKAzKUP+STFxrMdY1Zze2T/pkIZGPkfe/0wIp/9CxHglPJeivSWrsI0kBdY/DdOw +EEAGM9wzzJ4Y94d5xMgVI0wEgmPiSTMI3t+sCmWlN/KKxWUH0YUXblM2fxj06nC2 +c7tG4/Vo5vk49OIOL7fIvDbB +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/mutual_fake.crt b/src/coro_http/tests/openssl_files/mutual_fake.crt new file mode 100644 index 000000000..ac6514d51 --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_fake.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqTCCApGgAwIBAgIURtn2QucupuunD9sjD9QqI/r07okwDQYJKoZIhvcNAQEL +BQAwZDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxDTALBgNVBAoMBEZha2UxDTALBgNVBAsMBEZha2UxEzARBgNVBAMMCkZh +a2VDbGllbnQwHhcNMjYwNDEwMDU1NDIzWhcNMzYwNDA3MDU1NDIzWjBkMQswCQYD +VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzENMAsG +A1UECgwERmFrZTENMAsGA1UECwwERmFrZTETMBEGA1UEAwwKRmFrZUNsaWVudDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALgUys06AMvbpyEs6rgFD0SF +dRjV0oiQdmpVlk1bG0/ssSa5tnspzfGVJAK7oOxWuyq3/92jlvsX9kUQ12eEmu8/ +AWM3dHayaCFxxVg9fqD/cIYZD9z/vMQkONLTzZ5XbmhLlTQVcob8jgJChw2//gBz +NLsdjHH0iLEHNqIBFhTMAjwgk1ydQ4nroQEzplYDlsUudyxlv+/I6K6HoPvrf/cx +J5PR5lffa74xJ3hF7NEUNsvFI059QYN9w2ANraszyFNQ1BDt6jBNwfFcR3r7fG1V +LnbpncRwWXIKgamwGbOR2tYx4yuy6OACWQY9sygVzUyjkSe3PAi1e3HxmliOPz0C +AwEAAaNTMFEwHQYDVR0OBBYEFDP5Qo1pAe2dx5UEtEL7UeDl4PhoMB8GA1UdIwQY +MBaAFDP5Qo1pAe2dx5UEtEL7UeDl4PhoMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBAAO6JF7lFdSZe0OcE82qRwJGKpQmLB1XqKaMTkWQNjOs/Mpp +Oeiv8IiV+Pzt++RTrubwn0r54xB3WrgQeNqkAp3pYP7q/Lwl9AsKeWPOuqs0F+VA +RgZeWDI579u3Og+ZdPvuAObVmQxvsOHGHf7E0lIlB7ZcNscH8bD0cXQhdHaXLMj7 +ayxxNpnauHe2gblFEIXOdLApW9ZFtUyZ0mEipdYJRNSG0ujfNRY5mcM0sK9pPIrq +uRZjDbdiBBOM1jSGzoRjCXVonnO0JHLiNej3QxdXEjmHhNi1WtxkqPtiKbPIWBSN +IEYXYwXftVq2U2sH4e97OVvWQspSrGSVlZpjEkQ= +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/mutual_fake.key b/src/coro_http/tests/openssl_files/mutual_fake.key new file mode 100644 index 000000000..dcb331bf0 --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_fake.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4FMrNOgDL26ch +LOq4BQ9EhXUY1dKIkHZqVZZNWxtP7LEmubZ7Kc3xlSQCu6DsVrsqt//do5b7F/ZF +ENdnhJrvPwFjN3R2smghccVYPX6g/3CGGQ/c/7zEJDjS082eV25oS5U0FXKG/I4C +QocNv/4AczS7HYxx9IixBzaiARYUzAI8IJNcnUOJ66EBM6ZWA5bFLncsZb/vyOiu +h6D763/3MSeT0eZX32u+MSd4RezRFDbLxSNOfUGDfcNgDa2rM8hTUNQQ7eowTcHx +XEd6+3xtVS526Z3EcFlyCoGpsBmzkdrWMeMrsujgAlkGPbMoFc1Mo5EntzwItXtx +8ZpYjj89AgMBAAECggEADiNeriOB0yZ0MCqcfnS5Bvi/FSC7Ek0SIzmemSNhtiJG +OuSxnMUJsb/UK7eQdQZ2SqImLzY0zuU4v3Y7LRK0uaJbr2yfb8xlDgiIcS4L7z6f +PFVpb/5eV6w2hw7IcJxjePQxKfZpvO9h0s+cQtSXpB41ExgCZPA0nXAh5JBzyzX8 +YhQfDwlIshaePzrNq8vI3Xy2kcUBVT143ecvm1tYCXYtC65/WdGyey0gnGlilkNh +qliUyyVwc5+jxfVJPgnkFI1UZxuuneRlhGcMXtGLD1wO7nphrUNeR9pm2AoIrbO3 +qUZ/3f/k06WE0Z7hjSI54bfJ9MrJJPGEFjoXVDDk8QKBgQD4Utipzc/GilT+FrX7 +UAUlwxNAbFfaJ/StMgDJWu4H8elOjDGjTo2ZGP7d7Uk4E8bBM6OACyfPd3ApTbVC +nTK8+1b+EHre9hDRl6GrDuAfPfZg5guPXit7zTPDgwfri/JarBifUyJWYiTe+KlA +g91PL+P5/N+fhJUWQDrqU6KmiQKBgQC9xY1UBgzY209QjqLz81SwxZrxqKe91/YZ +ZEOhnTqh6cFGGsw6X82mWnQInn3p+0ay3jpzMIEG4ip4PssYd9xeZlKjc2mSlelB +LRI6RU5BPioSlHzoP2kGROi2JMgIVDTbE2HB85VJGYcYF08rNXxqE5xJCx6DLTMv +rYv9B/xmFQKBgQChIEBtjZmv3bpgVCQouTdd1UH8n1AxwZNFfhh8jn+8r/8OdHEQ +4buHB2z4WysTM+HXIsaIIrTmLT2dz0o5uv5dGUjM+ayAV3F6TcUc1T4fh3kCTsJZ +eGUGo5Ne3Pqan+fVZa1kU/EH1A7QjtBjiCxlYVGvt6DyRHjoQyz3NuVm4QKBgGqF +oI4gk0eK2xuZ5ShQVyKe2Rl7FSVAOzkHidsG+al3H/EtC6RcsIAHWAiaho03afjv +Oxn7iQGHJFW40aHbbgxjHVH4b7NDiNn35bplusZukYk6Zl6rcVV+iq3rOYlLUxwB +5ibLhumwdZ91PhBABqU4esqZfjgdwBSsMt8Gg4v9AoGBAJ8rHAIrfMPJfSHSkEIG +CgOg0biRnIOFAP1dJCmCcQF6gioVozOe7Oc5+2Re7F72KUMSndtmOz6cYLU/16v8 +TxGFboo4fuX7naewSaJUX6VdvUSmNdCh4L+MK7jOyl+tzp7cX2P+jpFtG8YtNcNU +iPjzGOWbeXTrfynobuEvwQiR +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/mutual_server.crt b/src/coro_http/tests/openssl_files/mutual_server.crt new file mode 100644 index 000000000..3babca3f6 --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDXDCCAkQCFBQv5CdHFz7el4unEPzp6yhwBju4MA0GCSqGSIb3DQEBCwUAMGwx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRMwEQYDVQQKDApNdXR1YWxUZXN0MQ0wCwYDVQQLDARUZXN0MRUwEwYDVQQDDAxN +dXR1YWxUZXN0Q0EwHhcNMjYwNDEwMDU1NDIyWhcNMzYwNDA3MDU1NDIyWjBpMQsw +CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzET +MBEGA1UECgwKTXV0dWFsVGVzdDENMAsGA1UECwwEVGVzdDESMBAGA1UEAwwJMTI3 +LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyf37hTNvnra +gOY8V4bl6TFuKtq970qoJdLQfrCgs4BbXWFZaavsB//nk6EUjNiQlVrP5iYtl50l +op6ZKbv+o54f9C+xQ2Ohe3+UQr1mvl9sloRWEYUeYxl+nQKbn0a0pl6fqtBb68N6 +lcTViPAxRM/2/iX3Sa+1ojUe1BvJovIg94KS8wJM8/OYTA4wNKkWhro2S8b8IFl7 +SayUjjPe1gNskkm7xOGh9cOjui/W1fBQWJfPvQt9DVtnzk01NDrdKAaqp0DXkBpg +QHhJYYeasVnwk/dr3YUfrq6Koi/PMEhtV59jNDBcJ83CqrZD2F44RtijLoeJ09Lt +7PC2PhT1awIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBU7nvLvfVjCrvfyKI7fs6v +pKtNldwWmE5Ro6vXeF8QpTfWT5/9uV8cwKt4zK12oxSkJtat6YIahnaB2rRomCcv +UjBVsC7r59L5lES++kOGkbjQ7YbbnOG4hp6u60mCz0ubuFUqeSqobzLdab3nTs8+ +V3z3eXnZiTjSG/A4Op7DclKgdBWbGe9foxUfz82gFeIkl2kSvhe6PFaJDAr9d/0a +TQPfJthSTNZRidMSwfcuhMegyir0L8HdecvcVg6wngxY/CWDDowji2uOSiX/LL2N +UCvZaFb39hY3TvS+8smZ28H4rDSw7RgBMhBw9eSo8yJ8PTf53Y8nmG4rfL2anoKk +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/mutual_server.key b/src/coro_http/tests/openssl_files/mutual_server.key new file mode 100644 index 000000000..1ab90f19f --- /dev/null +++ b/src/coro_http/tests/openssl_files/mutual_server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzJ/fuFM2+etqA +5jxXhuXpMW4q2r3vSqgl0tB+sKCzgFtdYVlpq+wH/+eToRSM2JCVWs/mJi2XnSWi +npkpu/6jnh/0L7FDY6F7f5RCvWa+X2yWhFYRhR5jGX6dApufRrSmXp+q0Fvrw3qV +xNWI8DFEz/b+JfdJr7WiNR7UG8mi8iD3gpLzAkzz85hMDjA0qRaGujZLxvwgWXtJ +rJSOM97WA2ySSbvE4aH1w6O6L9bV8FBYl8+9C30NW2fOTTU0Ot0oBqqnQNeQGmBA +eElhh5qxWfCT92vdhR+uroqiL88wSG1Xn2M0MFwnzcKqtkPYXjhG2KMuh4nT0u3s +8LY+FPVrAgMBAAECggEAFQkZXFQHAFmOdFoUNba6IhJSvCdo68LZUW+aWXXFuK3W +jHVUuUqdcScD+tqL/imjeFXsWTqcWdPyylBS7YqMUIvNdq9u4dm96TFGqDty5+Fu +b5HkRTRbKAmjSy42NZJovawYlUbXtCwEpbcx111Ue57rglXU3ksKSZxxHTiSCVaw +YZcgqZuVx9bQNFAUpZVCin1c3S+eJMPfnjVioDqtd7LGXMoEui0LykDTOOKr6Fxj +QWPeub8R2C6SEpaShGBcH07N5wnKtH5ITR9lVCAm1OPVxu1izyM0oVWovLcR4LbC +PklegWjsa6WSr/w6UiD5S/yK0dqW1TRWM/c6cT5xiQKBgQDeyITcuzqD4puS7baM +BCPLItVNFM84ddMq1eVFwC8a6XDMOtszsIrnvVpe5xqrZZfm9Oaz08FLDUl8JVsj +jo1eCFaI9+Vshq2Pxqg69qblxRjky6TxvIS9lHQ5BD3gXsBvRWYIcGkdXkfsVDIM ++Y7ueq7ThTpy43GmJugDSgWtpQKBgQDN3jtZgf5usmBNqA44LamnFgynqGWa/9uA +dK07H2qLFtlJSvGGcV1ZVTwCEIhrC/+ICXbAorwFHOZdIg3gtgzCs+8rCeP1waHi +XFjXPb/42v+30jKMV7G2FkriUmYnHaDHYGJhoBmwnM9y8mGr7gltXNpOZo0bvIrR +qS0MOtvJzwKBgBZ1oqdaHMEVBFggrOmatT0SauyVb3qirkJARBfvExCkfiGowVaJ +ssdAGK8+nzquSE0ZXXS9oVv+n+zrGzAPfAMB1i+CxldVkIPRJD6lhRfe8e6G9T8F +oWA3aiwhWFeZVc8h8PJi2sYCLkAOEOmr8xPpvFxIrybL9TYp7/P872udAoGBAMnw +Okt8pjWzp5/FP91/fTE3Acbb+n7mh0wkJ2EdWgeBrDam2vBD94uPfkOQMCDBLjEl +B2XCu6hQRvAIXZCVQ0Mh+XNASmphPAitCUBphAv51mlcONVNmDbC+0WyCh5Ig9PP +CfI1d720tBFPDNv3rSunr0TEd5pDgfBTgKrEeaAlAoGAdmkca363Crrk1yMl3e+d +M9wySBLdTccLgKeW8LL/pMWWYvVHS/vQjnYTeAnmSwB3TQ4FrT7QgVoZv6hccrhZ +F3fV6rrKZVOPVddGqrJAD/jPSbjv5gejrzaWMiXgjoF25o4unfYPqi9z82Ef1Ljb +XEngc6Jxr0Xk/YswFby7zzw= +-----END PRIVATE KEY----- diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index 9d79d31c6..12c774ba3 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,23 +1,19 @@ -----BEGIN CERTIFICATE----- -MIID5DCCAsygAwIBAgIUYrbmX8cu4oyVl5S/L7i+KRIbL2owDQYJKoZIhvcNAQEL -BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM -BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGYxCzAJBgNV -BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD -VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MRIwEAYDVQQDDAkxMjcuMC4wLjEw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZoBMxTWffKpQOXd7eIVHZ -8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C0Wxth33q -ww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdKj2t1iUcz -1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR01yNvTXe -qTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUlKAiEFUO+ -7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHaG9IJJw2j -AgMBAAGjgYwwgYkwCwYDVR0PBAQDAgQwMBMGA1UdJQQMMAoGCCsGAQUFBwMBMCUG -A1UdEQQeMByCCWxvY2FsaG9zdIIJMTI3LjAuMC4xhwR/AAABMB0GA1UdDgQWBBRi -FJ5dKk3VTdYkJYJJ2H4+oeU8ODAfBgNVHSMEGDAWgBRnstOwi7/KoizEJkfFsuHX -QuKbZTANBgkqhkiG9w0BAQsFAAOCAQEAHRyHOVKYQl5jmDbnRQ2GQXdk4uysaPDQ -HwSbLRwoKnQMTN2dh0Uu8odkaXiGKO2tdnRTfE/Ai3UXo6jSKXI3SHlr//nUNoMw -5RNa6VFldlP2GbEtRh3P0YFPtWo9pAs7WV0FS3g0SMFA5CGjVlrbVoqOrhqjlScp -gr7wqGJsOqsSDLkg6N90aH2OYHdNePGMArR+h0Z+QEaaNBuRr6O0aZnBtCN+kDQw -37ATj9Cj0uJaLjWexyG821gYCWtBvq0x/9c9tIV2HnD9VvKwlcZqlC/QCGz8tafE -vWQ64kYniH9npgAaD+TAUlkSia60R8z+RTd6opvPB+rvMsFOEIYNqQ== +MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh +bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx +MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT +nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw +PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz +WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 +2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo ++0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN +06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 +CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ +kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a +VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu +LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ +rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= -----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/server.key b/src/coro_http/tests/openssl_files/server.key index a05b48fb1..0edde26de 100644 --- a/src/coro_http/tests/openssl_files/server.key +++ b/src/coro_http/tests/openssl_files/server.key @@ -1,28 +1,30 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZoBMxTWffKpQO -Xd7eIVHZ8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C -0Wxth33qww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdK -j2t1iUcz1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR -01yNvTXeqTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUl -KAiEFUO+7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHa -G9IJJw2jAgMBAAECggEAKwfYeGXbI3p0sDWA+K1SlP8tgFnL5RplIBehHR9FDtI4 -Y6clEK/T0bUObZsMoM0XMp54kA2ror1LxK7OdTuPfJOYH4yfT94zx8Z8sxs5GkCo -0YBRfoP8RY6uFV+MAvSXGB/u004ndnykoODdU7XZGN6dDVFrJQ5atCZShHNto63Q -voCoKP+MFtEdGFMY8ymLIKuRH3JAY9xof+fS/6vo2fClUxQPEuH7VN+Pr4A/j2ID -WUOdcClGHPuGDW2nGKyEcj/VBKnoT7rcU9u3FDf8/CuG4hf+720/1T/tgPdgyn02 -LVcb78ELlQQ5iLke72FvVQDCzZMUkA4+NYf4A0EUOQKBgQDUenrK6rbjdamsZ+Ze -hcEZpGIoYIzl2fVk2mAJd5ssE6JcGfHAZsFOhtAGICGVZedpOC7vw0fnBDAqJsP2 -RJ0mhehzScTTnqIsNLL8y76jkwI+19tBWFk/VKAgCxhpOtevJ0Tg6wsz4gU0zNi8 -TSICoUvrgeHc4xFIlJ4ppyCcpwKBgQC5F5JUEnrhMwkqIBmnlSWaKZ5HI9riRZXd -TdQmgmFYGZNU8IpyoqaJhBIw9O+5O3NggK7jPr7zOUEGoN9lQvwEy4s5KmVv3ahE -fJimXTd34WML/N1Klfkv9u4M8ajSwanJRv0CuNLGASnuB28jHUhzi0S7u0KoAMVl -nO1wOIH6pQKBgQC+RfAwRoAAsR2AhoqFmsGRdONxxQRn0QcviV73G2SZ0/tQ/uq+ -oUX6TD99PMjWQKs6TlW53+ZDujxngs95eXhBRykiFK1t1GDltUuTleDiGoOoQyTV -H4jMbbv29fimQ0tiTogf4lvl2kFPRyHPfkM2l7qk62qXo7+Wf4AjTMangQKBgE1F -afFvRZ1+kPTlMdCkk24osctBTL/qDQa21zq8c6c0Bi4Pvbzd6mi+mlmV5/6Msz42 -esykBVPFM0BxKNI9hLj7wMO8z1xETVtKKPBLOjzx+0el6TyaH83GaNs+iBx0fU1q -NXZNcrD3C2oz8FHYh8a0/ZziMJGywLYLLZUMAjeBAoGBANEOpeitU+8sYOSz1rmi -dtNKnMWFSxEWjtlxzXgRuIC8HJis9YHLI5Hny0Iao3to4AEXRZ/r6EL9+/Z10AdZ -iGmiN/cbMn6yTErpehKLLCB3S2nLxOdeLIgOcgPcJDyEyvyXCcBI3A9W4uMxYI2I -kplBOEOtHG/SR3vfiHN8Mdpb ------END PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,D920B8941C56ADDC + +I2lW3QsAG/xubjtXpXh3wQ5Ru3VZiMkPNjc+G6/2JjjVr1sD+fzCWvvwdqdxGuNJ +gKdpPBHLuQfTTzGETE4NKDkYzmiPTVbZPJ77DyfL2cK1dcZtAY46RsHf+VMI5N8l +Be1jQSB5xvUa88dSIeowPTc2XSnTIoSFWCa38XuqYF7i0a3lv96eAyXpqB7Tm2r8 +SoYlm0n7/uzRpk6HWST65qnVv/j+37LuvSy6ehyh44+KDS4x9FUOZc5xwJ/37Jnl +SDC10+9zLc+jOTk6XgUuBSmG+xfZdcOrbknQ1Xj1YtseYH0plYAEWi4PsnMQkHzC +GGvK08Lgqxd7cGEKFh2MRZ/TEwriN5ud5HGm4yIHIj45rbedtRSQwl2EyHdWeW0J +rFltDy+SXnnkJaOcnBYXUD1jEwyy2lLamWRiu83VFbCv6yhOYuR6JejM6dctjgZ+ +Qf0PzH6L1bVpHKEl/GLByJ6GWYrQJqw83LAXlR+NNCC3nN7WAAaTuzA9LpgW9Vk0 +khRRs7rJGxwwwE4TfG9FbQxwuOsjKV9pRohB1x1nFMMm5IJ9SON2KjizsVdLbt7t +Gb/5M7RcSnnGvIWWXalXpFGKgciwYd8F1v0TJ+FMooZxgUp7Pmp5YKIHkBjMrnnW +rKuoxmA5oPgSNUtr4ddMJ1sTIQPhqI27+CrySTzWKH1ls45okBvsiCejpcJwfrZW +KLSkz/FsPoWm44uomBSDOikry8axrKQLB9tOVPKCx/z0VP060P9N81mu4h67bixr +xu+odIONqGhRZT/BYHL2NjDfWlFmTJQy8Drn1a7IEhp8FV7l2aY/hisrMN7MQVza +FGB0hMbVHGeFOCD9QNQwRU2wLtwpE7LT/lGNmKadQadXxeAqOWBckXrpwnrxZDEP +a8AYr2J55h/IE4Oi2DyibSEZdB+7334OJHMmr14q53eIpeit19BYVhWyu9AtORJp +As61C7s82AO+E5gOswsq05jwWV/GIIkgZ8/vswEffiihmDEf6AUZsVGW3BlpFlyU +i3g4e8HFTJ+s9Z3sTgZ1EWOP6Wd2OzyQYVA4ggBR/g/IC9s5em1wvAkVwIZaPvj7 +21BIQXyiGrw52T+vTUrAUG0l7yoHGCgVYJ+aEm+f103AiBYuReUbo39GEIY2GHLu +r3oUehtt4of0ootmPCmjrRUyY6LPeD+d+i1jJUSYFKezsVRpaiF5+J8YLGMcOPiI +8qRRNgXDMMvttwyhoxyr5+667OMv+XWr2VQj7i9MWCFwTMwNzdUoZI3PWDhXbXDO +lQJS6v3iAPw+KvLJywODe+C4shUqYdrRdUSKE0FfuB8Ajzh86+FmjJcZM+BSxM4J +hC2yjv114jDlsgjFSxQE2K1iotLUY9mfmW8QWVMO3L4LlNpr4ypNLYX0Ph2wgqzQ +kszXTFN11RFKFLUhF0Mi5m4ffMLPD5YyoqO9grpyC1Nt7vxaPPvcvPD86jK3ksqJ +MwucZGgm9HtUuAjGOSljUr0d+d+4pySJbcpH2YDIBHGVsCScYPVg8XZ1CYko3mq/ +d6jDUgydraEmQvIPiKMpTE18rW+jierv2FlB8AGcwxm2VWxuM25wQ40J2YuZLY7k +-----END RSA PRIVATE KEY----- diff --git a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp index 528a9d792..7e57d4f0e 100644 --- a/src/coro_http/tests/test_http_ssl_mutual_auth.cpp +++ b/src/coro_http/tests/test_http_ssl_mutual_auth.cpp @@ -28,11 +28,11 @@ using namespace coro_http; using namespace std::chrono_literals; const std::string CERT_PATH = "../openssl_files/"; -const std::string SERVER_CERT = "server.crt"; -const std::string SERVER_KEY = "server.key"; -const std::string CLIENT_CERT = "client.crt"; -const std::string CLIENT_KEY = "client.key"; -const std::string CA_CERT = "ca.crt"; +const std::string SERVER_CERT = "mutual_server.crt"; +const std::string SERVER_KEY = "mutual_server.key"; +const std::string CLIENT_CERT = "mutual_client.crt"; +const std::string CLIENT_KEY = "mutual_client.key"; +const std::string CA_CERT = "mutual_ca.crt"; #ifdef YLT_ENABLE_SSL TEST_CASE("testing HTTP SSL one-way authentication") { diff --git a/src/coro_rpc/tests/openssl_files/ca.crt b/src/coro_rpc/tests/openssl_files/ca.crt index 6cc065352..d312957f5 100644 --- a/src/coro_rpc/tests/openssl_files/ca.crt +++ b/src/coro_rpc/tests/openssl_files/ca.crt @@ -1,22 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIUE0DZiUgfbUwiz2nF4zRYH7WC0YgwDQYJKoZIhvcNAQEL -BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM -BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGMxCzAJBgNV -BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD -VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0Q0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrbDIeJL2YCTvjlQCYx77DJmTg -TLorGcNn8HiZaAK5EhR0zh34szV97Ck9qUpMEMUOVBpQ4I4UEFWfYX/hOe9F0e37 -QTayDIKarc/Dz4CZu/i1ERzcweYs+Bkd+HBqSOweSQIdArYpTvQbennXo81Yk/3R -lgNQRLqaWdVB6A/P+qJPo4whytsbkbdFNw/PYX7TnuUKxfAsNHvE1UhUkyTs8den -sl/LjPqoX+NfZPcDDrlq7jtRNDHkh47LtFb5yqR6RQ/18uJenQhFyaqOAuIsYnPh -8n24kj05k+uz3gX++LRcmstX33SmVf6W2mIlWubFqSth7udlMF/yqMfHILhzAgMB -AAGjYzBhMB0GA1UdDgQWBBRnstOwi7/KoizEJkfFsuHXQuKbZTAfBgNVHSMEGDAW -gBRnstOwi7/KoizEJkfFsuHXQuKbZTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB -/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAXs08fcYcYZFUgzoVHX6wqME+uPBp -hB+zDq1s3ESqpP9ckuOiAtbAc+TKm4b1tVHW8ikYLmjZx57VarV9I/+VHEDUOM2Y -MROYnCDXytUAXcV6fjow5HbacYcnPGfa4iq6r+fDs3TdpUFikvWhg8kEmxy5bsGn -ADMNOriFEFXkKv4mjt20blHiKhv7uOGFtjZZk4UhZWE+qjHcRcCJDaxNudr7hHph -KlrsCiN4R3bkqukI6kIoaywQz14A3xhsEDAJF4v8qGhv2zC/7Cf2/TeLnC20BJ3s -W+uiVB89Fril42C87ZrxIZKbrKv1bqUXPBLg/QM5FKFwhkIgV2d4eldrYA== +MIIDiTCCAnGgAwIBAgIUMRSVN0J/XZ8QyzPrrPrmtcRyP/0wDQYJKoZIhvcNAQEL +BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 +MDgwODUwNThaFw0zNjA0MDUwODUwNThaMFQxCzAJBgNVBAYTAkNOMRAwDgYDVQQI +DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMQ8w +DQYDVQQDDAZUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCW +F8KQGg1s+WXraGgYlmVJaBq5YMYAfWbIZ/uk/c7CUWypV3TvH4CFio97uLdNe6et +oC7Pjm4xpMMUzvuBLIs2OJ1Bf0LpPRreQ7gE0szOvyCtIO4dfZQHLK8v3r0KV2+P +sMLz1cElCrKJwD0gxT/DrWIb+iLSA5mCBj+ZZuYzUR+LX+yG9aFZcCdzZTCo64Uq +qmh9ZECL1Qs1YoSg1Wt6/4eOsgc93pn3iDI4gOl6b0SL51Fw05mGaBt2Ff8OEvJB +nd70tJ5tRtEoRSbO1hqKvEWwOGzduIYGW6LxA5YmlQLzTqFI2nIIE2RqTO8zBzQM +I+z6xeeS56PK5TOVpnAXAgMBAAGjUzBRMB0GA1UdDgQWBBRBmelVxpHNuAbb+y7T +9kOnZmtpTDAfBgNVHSMEGDAWgBRBmelVxpHNuAbb+y7T9kOnZmtpTDAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBneIHr0YZ7kM2bxFbiGYkjPi/V +bYPQicrXZ2wYpRsnt2Hil/uzVQdQsljTVAP3KnML8f7KBn3FET4rcFIy3JXZGCMh ++shWYHVbrZ2plyFb0j+XSusxuGaxFz5tviBdCZntOXrcJnq8E7giaywPXIrNWKzl ++hl7U7QjzYJsiw6khIWN2E/0bRtNw9EVgEa6Y4C6FFs5fpM9884QuJqnBHpaa7HW +vcf/o5+HZBkKqhDfAOOY/OYGHwOfUkmjO+DrZ26rvIh66NK9wJWktvpszQQ/vL+/ +ZqT3h7uQBWXMu6zk4nWz14/bZIBVEBlm+ugSb37NucyoJzTAwDbuMlh5NjTA -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.crt b/src/coro_rpc/tests/openssl_files/client.crt index 35a3078ce..d0ec8aa12 100644 --- a/src/coro_rpc/tests/openssl_files/client.crt +++ b/src/coro_rpc/tests/openssl_files/client.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDUTCCAjkCFGK25l/HLuKMlZeUvy+4vikSGy9rMA0GCSqGSIb3DQEBCwUAMGMx +MIIDMzCCAhsCFBqXnE0mVOXaOgJDnmSpB9TyawwzMA0GCSqGSIb3DQEBCwUAMFQx CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 -Q0EwHhcNMjYwNDEwMDQzMDQ3WhcNMzYwNDA3MDQzMDQ3WjBnMQswCQYDVQQGEwJD -TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwH -QWxpYmFiYTENMAsGA1UECwwEVGVzdDETMBEGA1UEAwwKVGVzdENsaWVudDCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOkCl4uA5atdS8A0E1NX4PCEfezT -cvvlTJSsqaFIScU687P2stPGWAbuL+aav/6VNysIfyFXFk1R4YZgifT+YgzzqEoD -zF6F5YA+rVxm35vQ2MCqgLVmtlG2PWM8WxltbPCanQGlWIGwnb4W4hK+5ZWna4CL -NSfUhXZScBH7CfHXL6wvlkukKQvOLI1sqJWWTmk6JVyTwk8RzteSM5gB11/CvdNy -uB2l0ya/2ThltABdXljn6hIX4H69CZdt42gAIOr1N/rDspZ215/DAlcFxYWpxm47 -JUoGIK2BborwOfqCoc/LGrEk4DOK3Ey3za9hMH/O/WzdKK+0M2s9aH41G8ECAwEA -ATANBgkqhkiG9w0BAQsFAAOCAQEAcGJUg2jrFlu6vQpsP7yccz3acKgFNMdLYr1C -A9zDpxWaOa9JFQTquiKKntKC69X39ulxBbDOuUMUMFFb1+Ra2gqUgm4uSq8YmXd5 -cTI4JyGryzzm9RDVIjkh55q7wof7HQHe/FuskvZzc6KEi/ZnVKxugj2DYT67jDzL -4upaAXqWbVUzqjH0W1/Nj/rqjifrt+DxoezMs8bFhZi19lhMZQ3OWD+CirhIl9Ke -FtzUAREDiE066mYkdnK4axbW2nsKRQFGOBJculgIgnnyfghfe1SNVBj3/NhNZuh+ -xUD7DkF6mz3Xz24RNVRhsLXDJM5c1vPnnCpESoqbu1ltBDw3mQ== +MRAwDgYDVQQKDAdBbGliYWJhMQ8wDQYDVQQDDAZUZXN0Q0EwHhcNMjYwNDA4MDg1 +MDU4WhcNMzYwNDA1MDg1MDU4WjBYMQswCQYDVQQGEwJDTjEQMA4GA1UECAwHQmVp +amluZzEQMA4GA1UEBwwHQmVpamluZzEQMA4GA1UECgwHQWxpYmFiYTETMBEGA1UE +AwwKVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkK +LHbsfM3NMvvFp6nI0MVa+PVHTUcPIUjXFbkyACbpL/ZvXKL1r02XWqgKJ8Z3hXDx +O1vgT6a17z2W1KaFR7S7NxJ9fYjZXHkSXuhmfTvMkWbbOoWrgcUjN/gKZEtJxsbs +JLTZhKVdyL5dli//LyYidij2JIY4MupOeWLmnMn/U8u+InA0bTfMIcjNb4Cbo8Rb +7R5cvu25cMK8flcq6qYz/4saZqKHt6mD2y4KS7LbBf1hBr/53UYSzZ5gNS18/1ud +S6rFSZ2KHrwWFgFgU+HflTndJ3XDsk24MaRqw3Y2M9RLmTxmquZ4KqSFHAXATDAK +iCKmv8BiAl+KTk7CbJMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAEHG9KlHqipxg +UEczDdKeSWsLwXnEmYziY3JiUrw762CSilEk1Ap2jN0fqxIaByWqIazX4lsuh5IJ +OxVfqT/QTgnICOpwDpiZnR0S6AzMNdh3GYt6qVnxNn/0zK4n+FlMOsUFH2etIxru +oQOcOIjeXcbd9mho1uf32DPZgFW9jd5mZripU+/cs6eRI2PWFKjR9oHjIdxTsj0S +XRJhJVTmffmZVpAoPlPv2fyo4Y5m6sVzixt+5z+EnDbhMEgDISJYZ4ge2MlV08Tp +/sjE242psTe+c3q2m4mU9jIUgUTKkyVP3iwjC15/+HJnMoeha1Mu7Y9rzL+qyTCJ +1ecQs/SZCQ== -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/client.key b/src/coro_rpc/tests/openssl_files/client.key index e6a05dd51..f9d5b9698 100644 --- a/src/coro_rpc/tests/openssl_files/client.key +++ b/src/coro_rpc/tests/openssl_files/client.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDpApeLgOWrXUvA -NBNTV+DwhH3s03L75UyUrKmhSEnFOvOz9rLTxlgG7i/mmr/+lTcrCH8hVxZNUeGG -YIn0/mIM86hKA8xeheWAPq1cZt+b0NjAqoC1ZrZRtj1jPFsZbWzwmp0BpViBsJ2+ -FuISvuWVp2uAizUn1IV2UnAR+wnx1y+sL5ZLpCkLziyNbKiVlk5pOiVck8JPEc7X -kjOYAddfwr3TcrgdpdMmv9k4ZbQAXV5Y5+oSF+B+vQmXbeNoACDq9Tf6w7KWdtef -wwJXBcWFqcZuOyVKBiCtgW6K8Dn6gqHPyxqxJOAzitxMt82vYTB/zv1s3SivtDNr -PWh+NRvBAgMBAAECgf86lhWqJ0Jq16xHDQ0pPgmnYNX9X1pJ5Q5+Jif4/79Qckmu -pxjAiJMZhVkU+PmRBZG/HlD24B+olQC526K2NvSG60RDOcfZ7VCsxLtDC7jLjw3/ -2+4PzGz0cXhBrEHhPS2FJGoC5ZPIj5SDWPnRpjV1fj1Ja1yyI1AvGVaFTPeYy1Za -AVG1GqWb7Gr+7LtWYlh5UTDbuOZTdZFhkMYILiewRZX0n2nL5DJ/RmrU7dizQQ8Y -KR6+y1XoXNl06Zu0mJorRPsXp8hi2KdA0W0OBvUIiS1ZCmj7shjn3gd2o6hb+Dux -ytY0yBEQ4LQ1Bw+ye4VYumF0SuSsK9s2jdewla0CgYEA/39T9pw7D6cocqRvlX6w -3s4SEvpVc53hGe5RdT4f2t1iaYmo3WYdahmYw1e4v5S6jOR2urNF5QTb/9HI5CE2 -vTzrzhfAjQrOgeojAW8SWde3qRgGJX9i2R+MvqyIjh7k7uGlxk5XtHnYWp3orl8I -to5uMCpNg0ywhmumZSoSmeUCgYEA6XfwaNsOFkOsOlGa55W0OMhsBcEpzg+9nZ/n -wPJAOfmSiCPC48DKWdHl3G1sM7dT/7xxvFlA5242Oex9ulF+/241+FOzzsKmIX5o -1sY23iIDSqjNcd1ItpHl+SAAXl4fLHU1hf1DwE4+J4IjvSeyrJIqJdhrSdEum56Q -iJZ07K0CgYEAj/ntk8PYWGrHFUtqgeDhxLx1XPJqovtt9RHiH2KByvEEWxqy6Qh5 -POftuO6+8l7afTjlWzJZCcSiQNe0EDJTSXKCIyIpZJGZa4ZIca9otO4l1gjutcTC -LD5mLrDFRulL8v1/UG+nZtFexTnE/DYbj9xVZZkBEyNtOmKBYvLBhq0CgYEA5Ud1 -oPQntHPHKwrDTtV1RSKG+2vEy2on9Cl6psEBlC3l2q8METFfR7Bbxgrr7SoIYylE -pQ0eMWnJ9T6sBpNMXjt04ygIeHAuSMxk1y+X6LSMeQCnqj//zdQgfnUQB5z1jmqZ -IrojlDMC1Tf4MyZOUS3GGJ6eVsMIu6mQFaN0to0CgYEAihXTENTPZ2KPfOECT5dr -HcyxTigY8R2cl+ekJI73TxgQ2csbRPrf0BBDGnp6jl575cGyOq1Ym4MfLWvnt/fn -3uGdzbk96KcVHqdjXc2FVHV3WlxJ90cF2uRWIoMW/hIaL3kSUkKGkBfNRZ6G5bLu -ALpqM+wAnNgzczPQpfGs61w= +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5Cix27HzNzTL7 +xaepyNDFWvj1R01HDyFI1xW5MgAm6S/2b1yi9a9Nl1qoCifGd4Vw8Ttb4E+mte89 +ltSmhUe0uzcSfX2I2Vx5El7oZn07zJFm2zqFq4HFIzf4CmRLScbG7CS02YSlXci+ +XZYv/y8mInYo9iSGODLqTnli5pzJ/1PLviJwNG03zCHIzW+Am6PEW+0eXL7tuXDC +vH5XKuqmM/+LGmaih7epg9suCkuy2wX9YQa/+d1GEs2eYDUtfP9bnUuqxUmdih68 +FhYBYFPh35U53Sd1w7JNuDGkasN2NjPUS5k8ZqrmeCqkhRwFwEwwCogipr/AYgJf +ik5OwmyTAgMBAAECggEACKnpyA2tkEM90l0ASypMYBkw0+cpPpz+PXdYnImzSR8c +8EhpiXXAZf0isQB8uUWqWU3qLhSUFiWplHQl88AEyfkEDJkUkh4/Pqn2CKKX1X9w +BWVZWZ3cnxrViYcPCBlltbWjyFXw6H86IXOnTwr/LaVqa+OOdTxXYfIXq+JJUAnQ +cF95KdwDBVmhmR+i2OP+uwPeBh7C1Q7kpmuhNwna/IYhJ/Dw6tb7LZ2/2pLdBs40 +IZWEcVOuaDw48CK9rzmuIomaxJs/pjGe3/m+wNQSWviA52BYxahfeSZgeBywiuWY +4JUkRUD4Mhu0PX+lMTTGdXaaV2FX+dXu+3KYJ0JdMQKBgQDtoWa8wRHIo5vRAR/7 +9gYkbZtyIN5iiO7ItBa40joiC+ylq4kalQIBQX48ATBRWkpjmONN1+p7l+VAUqBz +117njLbglESossoJ+UUwUgC+dPOwm6gsHhfNkjkPhDByT2z7rZuoRR8yBD2lyhqg +nTO6GOYGbkL5zWSpZDwHqILGsQKBgQDHWAaYAWGO0MlpNAgOlzyNeqjd7/RVJokF +zfeaf3haR1das6JpZWwuk6k6MyyFnZUvvOmtJ/S06Du4ZgOY9boy/NlgxFL40Efw +QMho/sUkdbwkCJlVcdsaX/z5n1FUkJpvUvHHTVt1H3kvspakN57+nF9hthWnkPgF ++vc1xHLAgwKBgQCp8TS9JsJmIAOuLETId8EayjxSGYmRJGbIqGpbkRU8BhUewhdS +KGB/r2vs09jPRWhP5CYjJJgv/YhZQP44+jyIEg0zfTXBA+QTz+4YSXz4uEES+68A +piDVxo4CN8JB7eV99EGOzKgrp/bCm1ABr4svuuC+lppVdftYXTPFMlEccQKBgBKe +RRdjYaI+G+Goi2wZcf1gzG7WH4Loc3nIT+ztJOeBrEX7axre7yi2f+LArtLX8fwg +b87NYYyX+CPz2zgpEzf556+jBoDYqy9kTZOI4A7UtDrFVtTlKmqfNnh2CdmVG+dz +3sMXlYgt5VqwGmPCEiaDomD06bbZ0mB0nSw3aeRTAoGBAL0dXkCCDYSjvoGFx3Ss +RvBN2ZtgLhjuzxg642R/Som8m8pH35Gv9dSaE0/+D5PI1K/7y0Oc0I3wJ03GRlVI +lm06pxtLWjt2cuI5/b3854P2eLwyZ1Ea+34bbeaJtsPB0m2+5rUyWsdpp4filZhj +uJ4px1WmbIsyJr+SIyk/Ecbc -----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake.crt b/src/coro_rpc/tests/openssl_files/fake.crt index c22e7eaba..a46e3c33c 100644 --- a/src/coro_rpc/tests/openssl_files/fake.crt +++ b/src/coro_rpc/tests/openssl_files/fake.crt @@ -1,20 +1,22 @@ -----BEGIN CERTIFICATE----- -MIIDTjCCAjYCFGK25l/HLuKMlZeUvy+4vikSGy9sMA0GCSqGSIb3DQEBCwUAMGMx -CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n -MRAwDgYDVQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQDDAZUZXN0 -Q0EwHhcNMjYwNDEwMDQzMDQ4WhcNMzYwNDA3MDQzMDQ4WjBkMQswCQYDVQQGEwJD -TjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzENMAsGA1UECgwE -RmFrZTENMAsGA1UECwwERmFrZTETMBEGA1UEAwwKRmFrZUNsaWVudDCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDa7bfVeA55s30ljVESiSs0m2ur7uSi -81ffQuZKpKlG6AeZW1hdIVPQFiEn6jfWkT9KjUWnJS/2hFrQcTH74vPQabTNcu0/ -8ywzg8uMEtcO0Jg3XlMj/4cOoB/SrGa37EXLe7FvewK66hx1edSwBxwaQw5rLDQZ -Lf1B/INRdFCcsdgelF3vch7l/OkrYsisr/y/j8ylXYji1ZHUvpiaMUDj0SMoRu4y -LA0G6FljjQC2hrMG4JfPItQH/6NDVFymqux2idMuNzYa+NdkQXsDBiTXNmPYel/M -ZCP57JTJg6Hu4mnfSBEgGNcdc6/lzK6lNmjTpYYR8ngL0PWbb54cvyUCAwEAATAN -BgkqhkiG9w0BAQsFAAOCAQEAIZYcaI7wOwYXj3oRsf/0Zyw7JXFvQbVmOdUXCOjl -c0XuJCe7wyiiyawgsGzF4mfKKFDOYBIjuHAMzNBWEjGcFYde5+UuqBJKP7oOrQVU -6kkMMcK56ewkNfsN4hEVco3KMj1VMYYRqInn30lUREgUjfYohEIC+weOrwrjgmIt -kqYYImUAofju2im5/ixJKyDWftj88vM8iRWlSznGj96ENboVzz/iY0ozCEQPwdGx -s5z8ouAa122HqrxX3epWG2t5GZ/aLJxvhmkrX4pveyx3fMUCs52inV1LDpATTzU/ -Q3k85ut/oeQ/3UGoEd+hmtFPtS+3jmTYGw48h9EwAtOhkg== +MIIDsTCCApmgAwIBAgIUE5kTRNz1GA3NOOBDwwqIAUz7ZJQwDQYJKoZIhvcNAQEL +BQAwazELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYD +VQQDDApGYWtlQ2xpZW50MB4XDTI2MDQwOTAyNTMxNVoXDTM2MDQwNjAyNTMxNVow +azELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0JlaWpp +bmcxEDAOBgNVBAoMB1Rlc3RPcmcxETAPBgNVBAsMCFRlc3RVbml0MRMwEQYDVQQD +DApGYWtlQ2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqylI +L2ovAcolSOUEGb55Tf2SbFczUqPB0XPF9x4UXFKcADcdhR+LDKLd92k000gwX8ft +s8vtAlOdtPj2x1mbelFyMC66I6BAUGjcwgBsVXhXYM7i8g5C5jhX21FNSy468urX +14Vv2CwRhCuWiukGsnZREQLPBHtwC6WHSSn7oTYdfTCRE9+FwLOJRaXbejCBjZbR +36dIMHFOvvi1w6QiS47v3u/neJ86QftsSRRTiRO9TAFQflrSgeY4sh8QjhPejVck +/AfsHx4CS4yAObSDl2B5NgXd2WikuniCGgJtgayuCHCfs1MP6QJ7Q3DTu29NLQgc +SKutn0291sLAm2M0OQIDAQABo00wSzAdBgNVHQ4EFgQU3M3ZPZ9s5gvE/FspkWQb +ig1OvSQwHwYDVR0jBBgwFoAU3M3ZPZ9s5gvE/FspkWQbig1OvSQwCQYDVR0TBAIw +ADANBgkqhkiG9w0BAQsFAAOCAQEAfp+JcImg7LQaBoL5ikKHVCWykjqBdU/0yXfK +lHVIRbDCZO1c/0hNfEU9aN7MXaPHxMtZOXX9d5i5Xt3PzTB1Q3Keb7QWYEK4lfs7 +H9IsHaMiax6UJM5Hb5xXCt8vCXRLcseD/in7Vm0+lqQ7Wp1UnwrN7NZ4PaiXTTmw +TMj4l9ElKtOG0vRr7loEsidE8Sc0X4H92Rs6gudh+Wtfb0O1aw0eRcpvUuO1NZLe +jpMxJMfDwyFh0URatAg8ZEPNV8HkgrEog2LltNVKSMMqi/DK/98wanSSUFFSVY8h +aXIlbCXp5F5Z1IqS+Lt11DEf0Q6N1la8Z1mad+skE7/HTqG36w== -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake.key b/src/coro_rpc/tests/openssl_files/fake.key index 725eaa8e3..f6924fcc2 100644 --- a/src/coro_rpc/tests/openssl_files/fake.key +++ b/src/coro_rpc/tests/openssl_files/fake.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCg2u231XgOebN9 -JY1REokrNJtrq+7kovNX30LmSqSpRugHmVtYXSFT0BYhJ+o31pE/So1FpyUv9oRa -0HEx++Lz0Gm0zXLtP/MsM4PLjBLXDtCYN15TI/+HDqAf0qxmt+xFy3uxb3sCuuoc -dXnUsAccGkMOayw0GS39QfyDUXRQnLHYHpRd73Ie5fzpK2LIrK/8v4/MpV2I4tWR -1L6YmjFA49EjKEbuMiwNBuhZY40AtoazBuCXzyLUB/+jQ1RcpqrsdonTLjc2GvjX -ZEF7AwYk1zZj2HpfzGQj+eyUyYOh7uJp30gRIBjXHXOv5cyupTZo06WGEfJ4C9D1 -m2+eHL8lAgMBAAECggEAGHVRggteZu0QYq4MD9C+tKgaHcQV3gP89laHSJb+9JJI -g6dI4WW/xIu2YbI9BeKFUVdXpcileeaE934MSCphturwt8IpgHOh2Q24M4IH91VU -WGDK4d9uYi3SISSafD/pGC+5jiBCVwbuxAcE0Fc3rYk8uvuGzCEsAf8/kwQWqI79 -JSwTHkYTFQ4KJlims50GJqOxUypyfnvKTnD6z6D8860YZhHukZ/zyn/h/PyzKaWs -uaorDt2XdyuhLHRPt7plW0kk4foZNYFSU10q5QyC4snAo0P27b8JkbBqbLo2VyN0 -qXk1GKdM0tCnFTUAcCoVUYSVxuuvG7gMD+5SCFcBoQKBgQDTJgMso3MN/ic+Cyl+ -B7g15GWFo3JRabMBe79rD/AkyuwdjB/xIcO+mcuNMN9W940RMdqKyrU5n0HX+E3B -JGgdXdUFW8yiDXBmqiHeJ+KtjgAgkRutqh2tvssBTpTvxG+EnP7wmrMgwdR3NlcG -YHuWVtKH5JeuVw3H9AJ8SlgtbQKBgQDDBgfFSDzm9pxKRsamwdXi5Jcuvzv/BZER -96Im5RN3FSQeEgcCMHRZ6gfIrX7WiZcK6I1Y7zn2pej0uPk399Y5WpH/FMilU/dC -fqhyhw+ZEC7UwAujEuLoAm5/p8+JlIscoHY6SE1VIQnyIBpvIprUDSAxZiXSmERA -V8b83e1dmQKBgFoZ+HTN8sTf1WMWZEhRhvwIUIIscxXmoupZIh/Pl0w8A3HAX8kH -/X2SJ1hCqKt1y46w1W8wfRDvsqs0XAm1PXB6n/I/cB0e2v4UT4t7PbGNzOQYx9Td -qPiBWCNgoxGFo4jVbbzCZvntfHq6h3xDI7nNpt0yYL+Wilzu9TiQiPwpAoGAJ0uO -w0Rz6QGlqh2qpy0FQKtYfvXPS+o+OcWQqY+cpXDgDyMIwHss5nUOTnQOy4F7qpoC -6PmCz1zMnIqsxIcuZe4G3sO8TfumJYLJxBHMpcWp+focHiaPC7p5s0UHvyvr/7+h -MssgsddvXpbzXJM3aSSf6PQxCMbfcat+D7NTwXkCgYEAn43lkTPjym0nqqSPzaUA -CtqXYRcgyaoQnuj6E4G61Y4z5vDKD8SE6xHQc2F4tIDUJN1F5opXhb55zcOYSbrM -1MKtZQNdZfOM7hZtBRzZJrP+t7vieXiEgdA0yQVTQoBI1ycQd7erC0kOyzZsbloH -W4yoJlKUzm4JaQN0rNZOaas= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrKUgvai8ByiVI +5QQZvnlN/ZJsVzNSo8HRc8X3HhRcUpwANx2FH4sMot33aTTTSDBfx+2zy+0CU520 ++PbHWZt6UXIwLrojoEBQaNzCAGxVeFdgzuLyDkLmOFfbUU1LLjry6tfXhW/YLBGE +K5aK6QaydlERAs8Ee3ALpYdJKfuhNh19MJET34XAs4lFpdt6MIGNltHfp0gwcU6+ ++LXDpCJLju/e7+d4nzpB+2xJFFOJE71MAVB+WtKB5jiyHxCOE96NVyT8B+wfHgJL +jIA5tIOXYHk2Bd3ZaKS6eIIaAm2BrK4IcJ+zUw/pAntDcNO7b00tCBxIq62fTb3W +wsCbYzQ5AgMBAAECggEAChvFihtUzF/CZPQ1kkmoA12i7KgXvV0zgKm8OtR5clxk +nzSiFy8eOLBTuJ1rg8DjLnzxwmkcRcNncH01od9eadJukn7n+luoALe4tfJtc3zI +eEyvpYkHFW6lbav3CyYfUCJjffSA/vzXSf2DBhAuF5MaRY1raYHaw61SeJU0qbgd +/H9yWYR49zB4csnTxAllJpKbIc1d2IargDgiv7GnzcA0dqROxcaXPU1cruuOag9f +iCd9awvMwy9XDqryEKyBGorN3WgT5TjMeFj8IBMLosOB15/Ruhuk/QNfijPPDS/R +KC7dX6BCHSu7cHHDjA43ikUZVerhOcq/P8ogbtOKiQKBgQDw51QTHz9BBCW5+7j6 +xvhYuosAuz2W+JJmMZlhy8SWHr4PIaQIG9E8CchssWiWSKPgDyHVfg2yTXP8Q8R+ +VT/AWAgeB3wrdMN6JNbNRPtkIGTtVgVDo7padBNbbPAN83+W3CdSF2RmJuAOInhb +Ez4Ctzq10kEyS1eTOT6yc9ujTQKBgQC14x5pdEHrRQpxpgq8qXxnqSlm030xtHJm +2z6oAxcsLVgwWcTRTU22ABJTV3939gJLhtFJ0p3DxfvCy/GOcawmeQsjpYs8j7gL +AIyYEMnxJTjzm+YwUn5uNl1xLeRa7P+YerIIwbC0tZxiNCvUo3IrTvfbIoBddCyZ +GhnqtK1GnQKBgCkIjiaPrPuLFE4AlXqJx6V9aM3gFtaPUoh7rE+fIMYdSGxVY5ZJ +/rLGS9BPy6vFhbxVd4Lg7L5ROQ9gD6khJjHCDOfoiHrycZVtjvT56gQdDHPssgra +aZScrutku+L0deghacUu3NgViRZ/QpboySg3Q5XS0W4arTkTiB1nZKMFAoGAWJBR +U5nHKy6/6hymZ7zDFZp5zVa3RAeQGOMyfA6dLuaZZVmgiyVv7GnWgnw9VgUUkv// +UknaheQWNYCmiuxwnX8c3GuUA5YbUEghLT4nhmLQe1Xy3J6ebz3Le/uTkG6L+gvs +OnVNfIBduDedC/nV8p6N80a2aErUGGxsKCt3n8UCgYEAt1hTr1a6HQheUv7RVC9i +KJu/k8n2olGUzE1pMZX6NlTgFKnmAQ7dLt6xeDBfMu0WBxbZktaSGM1T2Y0Tir+1 ++R/qH2inBE6FnL5rO8rb7ZiaDZy69Ntw4hJZGvryFcp6ZGraeUDqJO+e59xlQzmx +4/mvqmhlMJAYg7KJPfyH5tg= -----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/fake_server.crt b/src/coro_rpc/tests/openssl_files/fake_server.crt new file mode 100644 index 000000000..b56981813 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/fake_server.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +AIICIzCCAYwCCQDHoPajMKX7WDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh +bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIwODAzMDYwNjM3WhcNMzIw +NzMxMDYwNjM3WjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOpVxgJHmtZ88gja1MQm22O +ghYUT7T5X/85sJ0BenfghclyQ8KtxEDPGq4BSbheb+94C6x7YIY3GQ82y9R7nrcx +bcD3sGGRBn13ROyjz9HRUMTei2Uwd2yfIVY3Y0MDb0WMmjGnNTgj1h1YAYSWouC/ +4p/IitnVwmG+ZhwCxafhAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAuqmsA/xLqRW1 +9INE36S1T3xi56bIYdlYS2JRzXNrIqdb1RnNPHyFU0Tizv6dkUDec5VfunR5ZgAq +Hxsr5cowB1cdWi21FjjNGXrd1/nYHTdnxHfMGKFxjHXc+s5+qyFnmQatHJO8hyCR +ljmD56V/y1nMcFna0Gtear44Rh5s6fY= +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/fake_server.key b/src/coro_rpc/tests/openssl_files/fake_server.key new file mode 100644 index 000000000..c55263b04 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/fake_server.key @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,D5B5BF16335844E5 + +X2Qj15pkVsN24Qu3N+sORjm0eDD+I2lrEfEI6QmMg33PV0udkNDEMtchJJ12Uzl/ +Czsj2ydFLHbFCUGGDqx7CfzYNBQhJvw74nrakXDv8uRdygvc0bEym35Nkukx5XNV +FL/jnTChO5ZneHHYYgtMDzsWN7+0oKTHyizN43nGNbBJJEy3hFje4k4ATFrCUXZt +z4OVge2M5JATR2ddFGX62ZrxFmg+PsbvSnfpxZesRXHtu0NPCK/Xv38vGYSaJ0s0 +XlK7A6Ko4O6RfBZXSXCO09GD27HA0zveQfLW2ILhbl4GZrAFd1d7d1Jdltxlvbw6 +cUp8dd3f6hAb5mAk1io4Xey++CEiwkpBgis9qAkFqxPPzwq+cB6LTu2KfzffvAze +FOZ7Yq5JyDLt7frpJ+PTFfv3bNyXdabBt6E9ZdEhIbvf/ArA+JuV4mJ8ilvsurnq +h54Z+R19V8kXUEekKDSYl2WsMrTY7o8ZY0LgiIbTG+LFqGjUpyclx6luz9r2d3yr +/ngKqbG30jct0Y7mDuVz8Vgml9wDqv4JmC/iUxZyGLuZlSRWFqV6RRCzGncMqy+w +wZb5XNGK2CwKCwmTtWuJm3rS88yo2MmJPtkbPeG9JRvUaAsg1uaExA5RRIk2zyIA +mwp0ItT4S63g6iObFadd7l2cDBdIJNMwJ/80FNr+HknBnkC3OYsIimrscoUS5vBc +V+a8C6wJ6A4IDrNXLP5so2PuygE47CGb6ehlzpda9Eyp030JLooeKt7sqWpX7ENA +JmSDScpG6xe5SR0buKVlP4Qf9IqpB6K8CnHsJWiJI40= +-----END RSA PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/generate.txt b/src/coro_rpc/tests/openssl_files/generate.txt index 0d94c72b9..5c5f01fbb 100644 --- a/src/coro_rpc/tests/openssl_files/generate.txt +++ b/src/coro_rpc/tests/openssl_files/generate.txt @@ -1,7 +1,7 @@ -openssl genrsa -des3 -out server.key 1024 - -openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt - -openssl dhparam -out dh512.pem 512 - +openssl genrsa -des3 -out server.key 1024 + +openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt + +openssl dhparam -out dh512.pem 512 + openssl dhparam -out dhparam.pem 1024 \ No newline at end of file diff --git a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat index f1a1859e4..0aa8c02f0 100644 --- a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat +++ b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.bat @@ -1,103 +1,59 @@ @echo off REM Generate test certificates for mutual SSL authentication -REM This script generates CA, server, and client certificates for mutual auth testing only -REM The original fake_server.crt/key and server.crt/key are NOT modified +REM All generated files use "mutual_" prefix to avoid overwriting original files +REM The original upstream certificate/key files are NOT modified echo ======================================== echo Generating mutual authentication certificates echo ======================================== -REM Create OpenSSL config file -( -echo [req] -echo default_bits = 2048 -echo distinguished_name = req_distinguished_name -echo req_extensions = v3_req -echo x509_extensions = v3_ca echo. -echo [req_distinguished_name] -echo countryName = CN -echo stateOrProvinceName = Beijing -echo localityName = Beijing -echo organizationName = Alibaba -echo organizationalUnitName = Test -echo commonName = TestCA -echo. -echo [v3_req] -echo keyUsage = keyEncipherment, dataEncipherment -echo extendedKeyUsage = serverAuth -echo subjectAltName = @alt_names -echo. -echo [v3_ca] -echo subjectKeyIdentifier = hash -echo authorityKeyIdentifier = keyid:always,issuer -echo basicConstraints = critical,CA:TRUE -echo keyUsage = critical, cRLSign, keyCertSign -echo. -echo [alt_names] -echo DNS.1 = localhost -echo DNS.2 = 127.0.0.1 -echo IP.1 = 127.0.0.1 -) > openssl_mutual_auth.cnf - -echo. -echo [1/6] Generating CA private key... -openssl genrsa -out ca.key 2048 +echo [1/5] Generating CA private key... +openssl genrsa -out mutual_ca.key 2048 echo. -echo [2/6] Generating CA certificate... -openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestCA" -config openssl_mutual_auth.cnf -extensions v3_ca +echo [2/5] Generating CA certificate... +openssl req -new -x509 -days 3650 -key mutual_ca.key -out mutual_ca.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=MutualTest/OU=Test/CN=MutualTestCA" echo. -echo [3/6] Generating server private key for mutual auth... -openssl genrsa -out server.key 2048 +echo [3/5] Generating server certificate (CA-signed)... +openssl genrsa -out mutual_server.key 2048 +openssl req -new -key mutual_server.key -out mutual_server.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=MutualTest/OU=Test/CN=127.0.0.1" +openssl x509 -req -days 3650 -in mutual_server.csr -CA mutual_ca.crt -CAkey mutual_ca.key -CAcreateserial -out mutual_server.crt echo. -echo [4/6] Generating and signing server certificate... -openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=127.0.0.1" -config openssl_mutual_auth.cnf -openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile openssl_mutual_auth.cnf -extensions v3_req +echo [4/5] Generating client certificate (CA-signed)... +openssl genrsa -out mutual_client.key 2048 +openssl req -new -key mutual_client.key -out mutual_client.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=MutualTest/OU=Test/CN=MutualTestClient" +openssl x509 -req -days 3650 -in mutual_client.csr -CA mutual_ca.crt -CAkey mutual_ca.key -CAcreateserial -out mutual_client.crt echo. -echo [5/6] Generating client private key... -openssl genrsa -out client.key 2048 - -echo. -echo [6/6] Generating and signing client certificate... -openssl req -new -key client.key -out client.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestClient" -config openssl_mutual_auth.cnf -openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt - -echo. -echo Generating fake certificates for negative testing... -openssl genrsa -out fake.key 2048 -openssl req -new -key fake.key -out fake.csr -subj "/C=CN/ST=Beijing/L=Beijing/O=Fake/OU=Fake/CN=FakeClient" -config openssl_mutual_auth.cnf -openssl x509 -req -days 3650 -in fake.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out fake.crt +echo [5/5] Generating fake client certificate (self-signed, NOT by CA)... +openssl genrsa -out mutual_fake.key 2048 +openssl req -new -x509 -days 3650 -key mutual_fake.key -out mutual_fake.crt -subj "/C=CN/ST=Beijing/L=Beijing/O=Fake/OU=Fake/CN=FakeClient" echo. echo Cleaning up temporary files... +del mutual_ca.key 2>nul del *.csr 2>nul del *.srl 2>nul -del ca.key 2>nul -del openssl_mutual_auth.cnf 2>nul echo. echo ======================================== echo Mutual auth certificates generated! echo ======================================== echo. -echo Generated files (for mutual auth only): -echo ca.crt - CA certificate -echo server.crt - Server certificate (CA-signed) -echo server.key - Server private key -echo client.crt - Client certificate -echo client.key - Client private key -echo fake.crt - Fake client certificate (for negative testing) -echo fake.key - Fake client private key (for negative testing) -echo. -echo Original files preserved: -echo fake_server.crt - Original server certificate -echo fake_server.key - Original server private key -echo dh512.pem - DH parameters -echo dhparam.pem - DH parameters -echo generate.txt - Original generation notes +echo Generated files (mutual_ prefix): +echo mutual_ca.crt - CA certificate +echo mutual_server.crt - Server certificate (CA-signed) +echo mutual_server.key - Server private key +echo mutual_client.crt - Client certificate (CA-signed) +echo mutual_client.key - Client private key +echo mutual_fake.crt - Fake client certificate (self-signed) +echo mutual_fake.key - Fake client private key +echo. +echo Original upstream files NOT modified: +echo ca.crt, server.crt, server.key, client.crt, client.key +echo fake.crt, fake.key, dh512.pem, dhparam.pem, generate.txt echo. pause diff --git a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh index d9ccb9c75..fc009f182 100644 --- a/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh +++ b/src/coro_rpc/tests/openssl_files/generate_mutual_auth_certs.sh @@ -1,110 +1,61 @@ #!/bin/bash # Generate test certificates for mutual SSL authentication -# This script generates CA, server, and client certificates for mutual auth testing only -# The original fake_server.crt/key and server.crt/key are NOT modified +# All generated files use "mutual_" prefix to avoid overwriting original files +# The original upstream certificate/key files are NOT modified echo "========================================" echo "Generating mutual authentication certificates" echo "========================================" -# Create OpenSSL config file -cat > openssl_mutual_auth.cnf << 'EOF' -[req] -default_bits = 2048 -distinguished_name = req_distinguished_name -req_extensions = v3_req -x509_extensions = v3_ca - -[req_distinguished_name] -countryName = CN -stateOrProvinceName = Beijing -localityName = Beijing -organizationName = Alibaba -organizationalUnitName = Test -commonName = TestCA - -[v3_req] -keyUsage = keyEncipherment, dataEncipherment -extendedKeyUsage = serverAuth -subjectAltName = @alt_names - -[v3_ca] -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer -basicConstraints = critical,CA:TRUE -keyUsage = critical, cRLSign, keyCertSign - -[alt_names] -DNS.1 = localhost -DNS.2 = 127.0.0.1 -IP.1 = 127.0.0.1 -EOF - echo "" -echo "[1/6] Generating CA private key..." -openssl genrsa -out ca.key 2048 +echo "[1/5] Generating CA private key..." +openssl genrsa -out mutual_ca.key 2048 echo "" -echo "[2/6] Generating CA certificate..." -openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \ - -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestCA" \ - -config openssl_mutual_auth.cnf -extensions v3_ca +echo "[2/5] Generating CA certificate..." +openssl req -new -x509 -days 3650 -key mutual_ca.key -out mutual_ca.crt \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=MutualTest/OU=Test/CN=MutualTestCA" echo "" -echo "[3/6] Generating server private key for mutual auth..." -openssl genrsa -out server.key 2048 +echo "[3/5] Generating server certificate (CA-signed)..." +openssl genrsa -out mutual_server.key 2048 +openssl req -new -key mutual_server.key -out mutual_server.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=MutualTest/OU=Test/CN=127.0.0.1" +openssl x509 -req -days 3650 -in mutual_server.csr -CA mutual_ca.crt -CAkey mutual_ca.key \ + -CAcreateserial -out mutual_server.crt echo "" -echo "[4/6] Generating and signing server certificate..." -openssl req -new -key server.key -out server.csr \ - -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=127.0.0.1" \ - -config openssl_mutual_auth.cnf -openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key \ - -CAcreateserial -out server.crt \ - -extfile openssl_mutual_auth.cnf -extensions v3_req +echo "[4/5] Generating client certificate (CA-signed)..." +openssl genrsa -out mutual_client.key 2048 +openssl req -new -key mutual_client.key -out mutual_client.csr \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=MutualTest/OU=Test/CN=MutualTestClient" +openssl x509 -req -days 3650 -in mutual_client.csr -CA mutual_ca.crt -CAkey mutual_ca.key \ + -CAcreateserial -out mutual_client.crt echo "" -echo "[5/6] Generating client private key..." -openssl genrsa -out client.key 2048 - -echo "" -echo "[6/6] Generating and signing client certificate..." -openssl req -new -key client.key -out client.csr \ - -subj "/C=CN/ST=Beijing/L=Beijing/O=Alibaba/OU=Test/CN=TestClient" \ - -config openssl_mutual_auth.cnf -openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key \ - -CAcreateserial -out client.crt - -echo "" -echo "Generating fake certificates for negative testing..." -openssl genrsa -out fake.key 2048 -openssl req -new -key fake.key -out fake.csr \ - -subj "/C=CN/ST=Beijing/L=Beijing/O=Fake/OU=Fake/CN=FakeClient" \ - -config openssl_mutual_auth.cnf -openssl x509 -req -days 3650 -in fake.csr -CA ca.crt -CAkey ca.key \ - -CAcreateserial -out fake.crt +echo "[5/5] Generating fake client certificate (self-signed, NOT by CA)..." +openssl genrsa -out mutual_fake.key 2048 +openssl req -new -x509 -days 3650 -key mutual_fake.key -out mutual_fake.crt \ + -subj "/C=CN/ST=Beijing/L=Beijing/O=Fake/OU=Fake/CN=FakeClient" echo "" echo "Cleaning up temporary files..." -rm -f *.csr *.srl ca.key openssl_mutual_auth.cnf +rm -f mutual_ca.key *.csr *.srl echo "" echo "========================================" echo "Mutual auth certificates generated!" echo "========================================" echo "" -echo "Generated files (for mutual auth only):" -echo " ca.crt - CA certificate" -echo " server.crt - Server certificate (CA-signed)" -echo " server.key - Server private key" -echo " client.crt - Client certificate" -echo " client.key - Client private key" -echo " fake.crt - Fake client certificate (for negative testing)" -echo " fake.key - Fake client private key (for negative testing)" -echo "" -echo "Original files preserved:" -echo " fake_server.crt - Original server certificate" -echo " fake_server.key - Original server private key" -echo " dh512.pem - DH parameters" -echo " dhparam.pem - DH parameters" -echo " generate.txt - Original generation notes" +echo "Generated files (mutual_ prefix):" +echo " mutual_ca.crt - CA certificate" +echo " mutual_server.crt - Server certificate (CA-signed)" +echo " mutual_server.key - Server private key" +echo " mutual_client.crt - Client certificate (CA-signed)" +echo " mutual_client.key - Client private key" +echo " mutual_fake.crt - Fake client certificate (self-signed)" +echo " mutual_fake.key - Fake client private key" +echo "" +echo "Original upstream files NOT modified:" +echo " ca.crt, server.crt, server.key, client.crt, client.key" +echo " fake.crt, fake.key, dh512.pem, dhparam.pem, generate.txt" diff --git a/src/coro_rpc/tests/openssl_files/mutual_ca.crt b/src/coro_rpc/tests/openssl_files/mutual_ca.crt new file mode 100644 index 000000000..e75df18d5 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDuTCCAqGgAwIBAgIUKCavP59Twp7Vlap3ZuoEgC1QIYswDQYJKoZIhvcNAQEL +BQAwbDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxEzARBgNVBAoMCk11dHVhbFRlc3QxDTALBgNVBAsMBFRlc3QxFTATBgNV +BAMMDE11dHVhbFRlc3RDQTAeFw0yNjA0MTAwNTU0MjJaFw0zNjA0MDcwNTU0MjJa +MGwxCzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlq +aW5nMRMwEQYDVQQKDApNdXR1YWxUZXN0MQ0wCwYDVQQLDARUZXN0MRUwEwYDVQQD +DAxNdXR1YWxUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+ +OCxjH+5E7Bn3DBrGgsC7I7gTKqzKXNAD6RsxN3IlzDC5GPrB9tU3yYHL3BirW7JB +aJUFGW5M+aXjHu/y+qdZjFgLQvP0a9pzxMbyFcYKod0i95q7aCm9YCfw1DCvxZfB +sal0bltG0IEg+/4MGXyFUdhhmyvRigqgaBC5MmV2f6SjxQSp7c6/0sH3kBB/qoYF +GmKPPSz6ubr0Fmkg/deU+51OhuUli5kNYsguyVL1yNCNVo4w00BVfflUOPMde0ul +7qURZV/7Gui0OMcCkP2fxS1xFhOdvfj56jrwd7I95O82XaIe+Hu7S5nMm6p8S83u +rjXwIdOaK7g57Nt9FOh/AgMBAAGjUzBRMB0GA1UdDgQWBBQfzBZduML17QrUO9vm +Icky7Vfx5zAfBgNVHSMEGDAWgBQfzBZduML17QrUO9vmIcky7Vfx5zAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCn7tinfFMl1sQTF7I+lE1zkITF +GHEhd7LAjj/MhM8zkf4bIdCSsGLormxxjk+uZzE1voZ+St22y4I4si+1+EosOSp2 +l7grZNHbVQGIU+HcI4Necg85yCToUikPj923Ql0ZPq2lBKaOjYVSWUlNB8EpBMgV +nVDaTmyIT3rvKimEgBbyK93kuRg3qhlH20QQnXdkH1ns4uqxoP3iBPdniSk4iBBW +yHQMPkqRQMH8FO5mcINGT3h9kd9pSP1A4ZFr7CusH8nbHz0tTdzg4IBXMZtYocoP +NqoASGGanNNb7jbK/fgQ0PDWsrSM5g+CfmLYRr/f//Io5bAZCUFW/u2YuB2l +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/mutual_client.crt b/src/coro_rpc/tests/openssl_files/mutual_client.crt new file mode 100644 index 000000000..7ac580e5d --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_client.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDYzCCAksCFBQv5CdHFz7el4unEPzp6yhwBju5MA0GCSqGSIb3DQEBCwUAMGwx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRMwEQYDVQQKDApNdXR1YWxUZXN0MQ0wCwYDVQQLDARUZXN0MRUwEwYDVQQDDAxN +dXR1YWxUZXN0Q0EwHhcNMjYwNDEwMDU1NDIyWhcNMzYwNDA3MDU1NDIyWjBwMQsw +CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzET +MBEGA1UECgwKTXV0dWFsVGVzdDENMAsGA1UECwwEVGVzdDEZMBcGA1UEAwwQTXV0 +dWFsVGVzdENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMOo +gip+F/KreIBBzFDxX0nhsmhaB3JNkdVjNYsUTL9nCUQNIHokhhEp8PFiBz5J+1A8 +x62yRq0DM33psZi7NGUUkYh/ndPKdjdS7iypH6NWkBHX1ggcAGyT01y64kB1sotT +ozfQNvnY8mGxCdto4O2gJfZa/VMQqRXRFgaXOz3tIkuhtxvurqIr/Lv43j/LyGuk ++Q+ombP+Pll7euT1dXrCrhZDFRQVCNkkunR5SDEOZtoGEKeEKb6BDmIq9FSwGym7 +qK0cqPXUd0bW9RumueWKLhmcoV8lgkc0xWxyTngCcm/brzUj2btiUfUxwVXavHmt +BuKx7v2wSoH349/DvssCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAaQJhVvnUUI/b +lcYmc/FrrxntdO01+fYD6lr8Y4iAjiXrQAr9xSKZAhHEHqyB03ipFgSM98MiCcjF +Nf0u5qZxXENHZ0r3uSAN/hpajPS7y+PDquOszeqs3N4irIX2003Ev902/tPuARLJ +UPN2sT1IGsdXfw+S83uAHFH3LPW5y8dc1xF7sAyqo5OT2aTG1/gwMMJGow48N0qp +8OhAebfJSf6e0MZZSr+apNMMNRAHkiJnRfjeglGujW99PzpAyLEWCGgzkqXsswg3 +ktVtpgtPRPNRgs54DiLTXewfvlzyHvZ1nGVo9fc1lz0Fy1XyxliR47V4Qb9HYosF +RmlzfIraCw== +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/mutual_client.key b/src/coro_rpc/tests/openssl_files/mutual_client.key new file mode 100644 index 000000000..de373d554 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDqIIqfhfyq3iA +QcxQ8V9J4bJoWgdyTZHVYzWLFEy/ZwlEDSB6JIYRKfDxYgc+SftQPMetskatAzN9 +6bGYuzRlFJGIf53TynY3Uu4sqR+jVpAR19YIHABsk9NcuuJAdbKLU6M30Db52PJh +sQnbaODtoCX2Wv1TEKkV0RYGlzs97SJLobcb7q6iK/y7+N4/y8hrpPkPqJmz/j5Z +e3rk9XV6wq4WQxUUFQjZJLp0eUgxDmbaBhCnhCm+gQ5iKvRUsBspu6itHKj11HdG +1vUbprnlii4ZnKFfJYJHNMVsck54AnJv2681I9m7YlH1McFV2rx5rQbise79sEqB +9+Pfw77LAgMBAAECggEAFDJXxWUgubcDiFHCcnSH/otirCzm6eCh9iH4i/O7fGJ5 +bWHhgVo10J3AtloFH2Ppoj2z6vUlIITdEtlFsNtaLDj0UN/DffI/Q7S2yztl/alY +086w1EN3s72Kqt7LrhW4KXOnvIIsupuvYXAx8UkhNsY6RPTdg26L1amwmVuRDPI4 +QZxqBYcYdPS2TkzhNzRGajnkM4qm6ZPJDUHZcl4iqEkUHU4AB7zvmWQAFsyTJFZR +TKQI5StGdxHrW1QEpijGZ9H5sNv+ssHIQtdSPdbYTXThkXP/UQ3VgtsAhFuIsr1h +wTZyAL/+zVz4sJHO/5B8OmCgiag6KSxaKkDiHkFnMQKBgQDpriZg5tsEW1K0KWCU +VdyN40bJowW8nbgdTVcA9AhESd5qvOYVgnJ5VDNZyHZTGuISLW5Q9UhVKjlu2aY0 +TINAk3q97E1gYZ/iVGS+WADR8ICKfDZF7FU256psNQ+sqyLR8zqu7KR0HA/n5Z0a +uKEh1mVir8Zbz9HHm1cxrOrceQKBgQDWWKjzNx2n7bmP523i57iZdXkmejBpqSZH +wg1KXZ/ETdnECN6OWyTgCMyKFBbdSZU0SH0Mz2c/skYX5hZUGa4P9ZAQEkuI9Q/z +Q14nlWbukW5V1lSUjMR3ZR8g5ONeKqR/zoyFQINZdiiY2cnfcEAolnT6qCARhJVP ++w97oB1cYwKBgEbuUr31NSP0aH1BVgyQp3r2MwV/k302Tq2uTSt/54Z6+aVio2CC +ESdc9J1bKPd+4IJuAd9XJNadE4PfUwDq/Kg8W/SMZsxLtdFolo/kfJM9MndWzs6Y +tyEMXwGrdY+O/unFr9lrAVwxLG7SlsaGpnpz7qBvBIHX6jBxqZzthPjZAoGBAM0Z +rSB9Hr1vNd5C/tzeCb+drr1osiaImn6TapA8IgJ+099G9V6WTCSrhryhGHfKTyDm +M/IsC4nhljyMB9WVdP8EZENcnjaA+DA3yEJsLUAenMs5+VjjkaMFedHJ8t5KQ3mg +NMnUv1q9O3929jn9eQbdYTXv5i+dBBqyC1CqFy4tAoGBANXci7qB5h2EWmPaQOGI +kKAzKUP+STFxrMdY1Zze2T/pkIZGPkfe/0wIp/9CxHglPJeivSWrsI0kBdY/DdOw +EEAGM9wzzJ4Y94d5xMgVI0wEgmPiSTMI3t+sCmWlN/KKxWUH0YUXblM2fxj06nC2 +c7tG4/Vo5vk49OIOL7fIvDbB +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/mutual_fake.crt b/src/coro_rpc/tests/openssl_files/mutual_fake.crt new file mode 100644 index 000000000..ac6514d51 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_fake.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDqTCCApGgAwIBAgIURtn2QucupuunD9sjD9QqI/r07okwDQYJKoZIhvcNAQEL +BQAwZDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl +aWppbmcxDTALBgNVBAoMBEZha2UxDTALBgNVBAsMBEZha2UxEzARBgNVBAMMCkZh +a2VDbGllbnQwHhcNMjYwNDEwMDU1NDIzWhcNMzYwNDA3MDU1NDIzWjBkMQswCQYD +VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzENMAsG +A1UECgwERmFrZTENMAsGA1UECwwERmFrZTETMBEGA1UEAwwKRmFrZUNsaWVudDCC +ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALgUys06AMvbpyEs6rgFD0SF +dRjV0oiQdmpVlk1bG0/ssSa5tnspzfGVJAK7oOxWuyq3/92jlvsX9kUQ12eEmu8/ +AWM3dHayaCFxxVg9fqD/cIYZD9z/vMQkONLTzZ5XbmhLlTQVcob8jgJChw2//gBz +NLsdjHH0iLEHNqIBFhTMAjwgk1ydQ4nroQEzplYDlsUudyxlv+/I6K6HoPvrf/cx +J5PR5lffa74xJ3hF7NEUNsvFI059QYN9w2ANraszyFNQ1BDt6jBNwfFcR3r7fG1V +LnbpncRwWXIKgamwGbOR2tYx4yuy6OACWQY9sygVzUyjkSe3PAi1e3HxmliOPz0C +AwEAAaNTMFEwHQYDVR0OBBYEFDP5Qo1pAe2dx5UEtEL7UeDl4PhoMB8GA1UdIwQY +MBaAFDP5Qo1pAe2dx5UEtEL7UeDl4PhoMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBAAO6JF7lFdSZe0OcE82qRwJGKpQmLB1XqKaMTkWQNjOs/Mpp +Oeiv8IiV+Pzt++RTrubwn0r54xB3WrgQeNqkAp3pYP7q/Lwl9AsKeWPOuqs0F+VA +RgZeWDI579u3Og+ZdPvuAObVmQxvsOHGHf7E0lIlB7ZcNscH8bD0cXQhdHaXLMj7 +ayxxNpnauHe2gblFEIXOdLApW9ZFtUyZ0mEipdYJRNSG0ujfNRY5mcM0sK9pPIrq +uRZjDbdiBBOM1jSGzoRjCXVonnO0JHLiNej3QxdXEjmHhNi1WtxkqPtiKbPIWBSN +IEYXYwXftVq2U2sH4e97OVvWQspSrGSVlZpjEkQ= +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/mutual_fake.key b/src/coro_rpc/tests/openssl_files/mutual_fake.key new file mode 100644 index 000000000..dcb331bf0 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_fake.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4FMrNOgDL26ch +LOq4BQ9EhXUY1dKIkHZqVZZNWxtP7LEmubZ7Kc3xlSQCu6DsVrsqt//do5b7F/ZF +ENdnhJrvPwFjN3R2smghccVYPX6g/3CGGQ/c/7zEJDjS082eV25oS5U0FXKG/I4C +QocNv/4AczS7HYxx9IixBzaiARYUzAI8IJNcnUOJ66EBM6ZWA5bFLncsZb/vyOiu +h6D763/3MSeT0eZX32u+MSd4RezRFDbLxSNOfUGDfcNgDa2rM8hTUNQQ7eowTcHx +XEd6+3xtVS526Z3EcFlyCoGpsBmzkdrWMeMrsujgAlkGPbMoFc1Mo5EntzwItXtx +8ZpYjj89AgMBAAECggEADiNeriOB0yZ0MCqcfnS5Bvi/FSC7Ek0SIzmemSNhtiJG +OuSxnMUJsb/UK7eQdQZ2SqImLzY0zuU4v3Y7LRK0uaJbr2yfb8xlDgiIcS4L7z6f +PFVpb/5eV6w2hw7IcJxjePQxKfZpvO9h0s+cQtSXpB41ExgCZPA0nXAh5JBzyzX8 +YhQfDwlIshaePzrNq8vI3Xy2kcUBVT143ecvm1tYCXYtC65/WdGyey0gnGlilkNh +qliUyyVwc5+jxfVJPgnkFI1UZxuuneRlhGcMXtGLD1wO7nphrUNeR9pm2AoIrbO3 +qUZ/3f/k06WE0Z7hjSI54bfJ9MrJJPGEFjoXVDDk8QKBgQD4Utipzc/GilT+FrX7 +UAUlwxNAbFfaJ/StMgDJWu4H8elOjDGjTo2ZGP7d7Uk4E8bBM6OACyfPd3ApTbVC +nTK8+1b+EHre9hDRl6GrDuAfPfZg5guPXit7zTPDgwfri/JarBifUyJWYiTe+KlA +g91PL+P5/N+fhJUWQDrqU6KmiQKBgQC9xY1UBgzY209QjqLz81SwxZrxqKe91/YZ +ZEOhnTqh6cFGGsw6X82mWnQInn3p+0ay3jpzMIEG4ip4PssYd9xeZlKjc2mSlelB +LRI6RU5BPioSlHzoP2kGROi2JMgIVDTbE2HB85VJGYcYF08rNXxqE5xJCx6DLTMv +rYv9B/xmFQKBgQChIEBtjZmv3bpgVCQouTdd1UH8n1AxwZNFfhh8jn+8r/8OdHEQ +4buHB2z4WysTM+HXIsaIIrTmLT2dz0o5uv5dGUjM+ayAV3F6TcUc1T4fh3kCTsJZ +eGUGo5Ne3Pqan+fVZa1kU/EH1A7QjtBjiCxlYVGvt6DyRHjoQyz3NuVm4QKBgGqF +oI4gk0eK2xuZ5ShQVyKe2Rl7FSVAOzkHidsG+al3H/EtC6RcsIAHWAiaho03afjv +Oxn7iQGHJFW40aHbbgxjHVH4b7NDiNn35bplusZukYk6Zl6rcVV+iq3rOYlLUxwB +5ibLhumwdZ91PhBABqU4esqZfjgdwBSsMt8Gg4v9AoGBAJ8rHAIrfMPJfSHSkEIG +CgOg0biRnIOFAP1dJCmCcQF6gioVozOe7Oc5+2Re7F72KUMSndtmOz6cYLU/16v8 +TxGFboo4fuX7naewSaJUX6VdvUSmNdCh4L+MK7jOyl+tzp7cX2P+jpFtG8YtNcNU +iPjzGOWbeXTrfynobuEvwQiR +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/mutual_server.crt b/src/coro_rpc/tests/openssl_files/mutual_server.crt new file mode 100644 index 000000000..3babca3f6 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_server.crt @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDXDCCAkQCFBQv5CdHFz7el4unEPzp6yhwBju4MA0GCSqGSIb3DQEBCwUAMGwx +CzAJBgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5n +MRMwEQYDVQQKDApNdXR1YWxUZXN0MQ0wCwYDVQQLDARUZXN0MRUwEwYDVQQDDAxN +dXR1YWxUZXN0Q0EwHhcNMjYwNDEwMDU1NDIyWhcNMzYwNDA3MDU1NDIyWjBpMQsw +CQYDVQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzET +MBEGA1UECgwKTXV0dWFsVGVzdDENMAsGA1UECwwEVGVzdDESMBAGA1UEAwwJMTI3 +LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyf37hTNvnra +gOY8V4bl6TFuKtq970qoJdLQfrCgs4BbXWFZaavsB//nk6EUjNiQlVrP5iYtl50l +op6ZKbv+o54f9C+xQ2Ohe3+UQr1mvl9sloRWEYUeYxl+nQKbn0a0pl6fqtBb68N6 +lcTViPAxRM/2/iX3Sa+1ojUe1BvJovIg94KS8wJM8/OYTA4wNKkWhro2S8b8IFl7 +SayUjjPe1gNskkm7xOGh9cOjui/W1fBQWJfPvQt9DVtnzk01NDrdKAaqp0DXkBpg +QHhJYYeasVnwk/dr3YUfrq6Koi/PMEhtV59jNDBcJ83CqrZD2F44RtijLoeJ09Lt +7PC2PhT1awIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBU7nvLvfVjCrvfyKI7fs6v +pKtNldwWmE5Ro6vXeF8QpTfWT5/9uV8cwKt4zK12oxSkJtat6YIahnaB2rRomCcv +UjBVsC7r59L5lES++kOGkbjQ7YbbnOG4hp6u60mCz0ubuFUqeSqobzLdab3nTs8+ +V3z3eXnZiTjSG/A4Op7DclKgdBWbGe9foxUfz82gFeIkl2kSvhe6PFaJDAr9d/0a +TQPfJthSTNZRidMSwfcuhMegyir0L8HdecvcVg6wngxY/CWDDowji2uOSiX/LL2N +UCvZaFb39hY3TvS+8smZ28H4rDSw7RgBMhBw9eSo8yJ8PTf53Y8nmG4rfL2anoKk +-----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/mutual_server.key b/src/coro_rpc/tests/openssl_files/mutual_server.key new file mode 100644 index 000000000..1ab90f19f --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/mutual_server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzJ/fuFM2+etqA +5jxXhuXpMW4q2r3vSqgl0tB+sKCzgFtdYVlpq+wH/+eToRSM2JCVWs/mJi2XnSWi +npkpu/6jnh/0L7FDY6F7f5RCvWa+X2yWhFYRhR5jGX6dApufRrSmXp+q0Fvrw3qV +xNWI8DFEz/b+JfdJr7WiNR7UG8mi8iD3gpLzAkzz85hMDjA0qRaGujZLxvwgWXtJ +rJSOM97WA2ySSbvE4aH1w6O6L9bV8FBYl8+9C30NW2fOTTU0Ot0oBqqnQNeQGmBA +eElhh5qxWfCT92vdhR+uroqiL88wSG1Xn2M0MFwnzcKqtkPYXjhG2KMuh4nT0u3s +8LY+FPVrAgMBAAECggEAFQkZXFQHAFmOdFoUNba6IhJSvCdo68LZUW+aWXXFuK3W +jHVUuUqdcScD+tqL/imjeFXsWTqcWdPyylBS7YqMUIvNdq9u4dm96TFGqDty5+Fu +b5HkRTRbKAmjSy42NZJovawYlUbXtCwEpbcx111Ue57rglXU3ksKSZxxHTiSCVaw +YZcgqZuVx9bQNFAUpZVCin1c3S+eJMPfnjVioDqtd7LGXMoEui0LykDTOOKr6Fxj +QWPeub8R2C6SEpaShGBcH07N5wnKtH5ITR9lVCAm1OPVxu1izyM0oVWovLcR4LbC +PklegWjsa6WSr/w6UiD5S/yK0dqW1TRWM/c6cT5xiQKBgQDeyITcuzqD4puS7baM +BCPLItVNFM84ddMq1eVFwC8a6XDMOtszsIrnvVpe5xqrZZfm9Oaz08FLDUl8JVsj +jo1eCFaI9+Vshq2Pxqg69qblxRjky6TxvIS9lHQ5BD3gXsBvRWYIcGkdXkfsVDIM ++Y7ueq7ThTpy43GmJugDSgWtpQKBgQDN3jtZgf5usmBNqA44LamnFgynqGWa/9uA +dK07H2qLFtlJSvGGcV1ZVTwCEIhrC/+ICXbAorwFHOZdIg3gtgzCs+8rCeP1waHi +XFjXPb/42v+30jKMV7G2FkriUmYnHaDHYGJhoBmwnM9y8mGr7gltXNpOZo0bvIrR +qS0MOtvJzwKBgBZ1oqdaHMEVBFggrOmatT0SauyVb3qirkJARBfvExCkfiGowVaJ +ssdAGK8+nzquSE0ZXXS9oVv+n+zrGzAPfAMB1i+CxldVkIPRJD6lhRfe8e6G9T8F +oWA3aiwhWFeZVc8h8PJi2sYCLkAOEOmr8xPpvFxIrybL9TYp7/P872udAoGBAMnw +Okt8pjWzp5/FP91/fTE3Acbb+n7mh0wkJ2EdWgeBrDam2vBD94uPfkOQMCDBLjEl +B2XCu6hQRvAIXZCVQ0Mh+XNASmphPAitCUBphAv51mlcONVNmDbC+0WyCh5Ig9PP +CfI1d720tBFPDNv3rSunr0TEd5pDgfBTgKrEeaAlAoGAdmkca363Crrk1yMl3e+d +M9wySBLdTccLgKeW8LL/pMWWYvVHS/vQjnYTeAnmSwB3TQ4FrT7QgVoZv6hccrhZ +F3fV6rrKZVOPVddGqrJAD/jPSbjv5gejrzaWMiXgjoF25o4unfYPqi9z82Ef1Ljb +XEngc6Jxr0Xk/YswFby7zzw= +-----END PRIVATE KEY----- diff --git a/src/coro_rpc/tests/openssl_files/server.crt b/src/coro_rpc/tests/openssl_files/server.crt index 9d79d31c6..12c774ba3 100644 --- a/src/coro_rpc/tests/openssl_files/server.crt +++ b/src/coro_rpc/tests/openssl_files/server.crt @@ -1,23 +1,19 @@ -----BEGIN CERTIFICATE----- -MIID5DCCAsygAwIBAgIUYrbmX8cu4oyVl5S/L7i+KRIbL2owDQYJKoZIhvcNAQEL -BQAwYzELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDTALBgNVBAsMBFRlc3QxDzANBgNVBAMM -BlRlc3RDQTAeFw0yNjA0MTAwNDMwNDdaFw0zNjA0MDcwNDMwNDdaMGYxCzAJBgNV -BAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYD -VQQKDAdBbGliYWJhMQ0wCwYDVQQLDARUZXN0MRIwEAYDVQQDDAkxMjcuMC4wLjEw -ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZoBMxTWffKpQOXd7eIVHZ -8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C0Wxth33q -ww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdKj2t1iUcz -1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR01yNvTXe -qTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUlKAiEFUO+ -7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHaG9IJJw2j -AgMBAAGjgYwwgYkwCwYDVR0PBAQDAgQwMBMGA1UdJQQMMAoGCCsGAQUFBwMBMCUG -A1UdEQQeMByCCWxvY2FsaG9zdIIJMTI3LjAuMC4xhwR/AAABMB0GA1UdDgQWBBRi -FJ5dKk3VTdYkJYJJ2H4+oeU8ODAfBgNVHSMEGDAWgBRnstOwi7/KoizEJkfFsuHX -QuKbZTANBgkqhkiG9w0BAQsFAAOCAQEAHRyHOVKYQl5jmDbnRQ2GQXdk4uysaPDQ -HwSbLRwoKnQMTN2dh0Uu8odkaXiGKO2tdnRTfE/Ai3UXo6jSKXI3SHlr//nUNoMw -5RNa6VFldlP2GbEtRh3P0YFPtWo9pAs7WV0FS3g0SMFA5CGjVlrbVoqOrhqjlScp -gr7wqGJsOqsSDLkg6N90aH2OYHdNePGMArR+h0Z+QEaaNBuRr6O0aZnBtCN+kDQw -37ATj9Cj0uJaLjWexyG821gYCWtBvq0x/9c9tIV2HnD9VvKwlcZqlC/QCGz8tafE -vWQ64kYniH9npgAaD+TAUlkSia60R8z+RTd6opvPB+rvMsFOEIYNqQ== +MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh +bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx +MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT +nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw +PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz +WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 +2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo ++0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN +06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 +CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ +kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a +VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu +LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ +rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= -----END CERTIFICATE----- diff --git a/src/coro_rpc/tests/openssl_files/server.csr b/src/coro_rpc/tests/openssl_files/server.csr deleted file mode 100644 index 91a64dc15..000000000 --- a/src/coro_rpc/tests/openssl_files/server.csr +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE REQUEST----- -MIIBqzCCARQCAQAwVjELMAkGA1UEBhMCQ04xFTATBgNVBAcMDERlZmF1bHQgQ2l0 -eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAGA1UEAwwJbG9jYWxo -b3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTqVcYCR5rWfPII2tTEJtt -joIWFE+0+V//ObCdAXp34IXJckPCrcRAzxquAUm4Xm/veAuse2CGNxkPNsvUe563 -MW3A97BhkQZ9d0Tso8/R0VDE3otlMHdsnyFWN2NDA29FjJoxpzU4I9YdWAGElqLg -v+KfyIrZ1cJhvmYcAsWn4QIDAQABoBUwEwYJKoZIhvcNAQkHMQYMBHRlc3QwDQYJ -KoZIhvcNAQELBQADgYEAakt8QseV1LSmQB5rV77cOzkNXr38zk7xWok/lc2hdMNm -yVrBDqtApRXBMdbQ/wal0uTtQxkzHW4FU6FO8x3MNwllVBoC3Q+9rsKwEcmTP73b -qa4zSQ+Ho7+p8HX7svyhuLq3dqWydyRnPkr3femO0SxBsWXDlB4Oxi4RxwaW3Ns= ------END CERTIFICATE REQUEST----- diff --git a/src/coro_rpc/tests/openssl_files/server.key b/src/coro_rpc/tests/openssl_files/server.key index a05b48fb1..0edde26de 100644 --- a/src/coro_rpc/tests/openssl_files/server.key +++ b/src/coro_rpc/tests/openssl_files/server.key @@ -1,28 +1,30 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZoBMxTWffKpQO -Xd7eIVHZ8/U6NpYt23czO5tEaJ9mXWWXmLNk7984pTrRZn1egXF/3ObAc/exsb6C -0Wxth33qww2rMWvgVi3aWbua3XzKy0lXouKLZK88BXYwoyCgN0DX3WZyrLakFfdK -j2t1iUcz1PZyP8s1XOqZuRwVVggo4xZzizZ3+0w6xJqAXE5RtuMHEB8e7aEwRqMR -01yNvTXeqTFoG4fFBFvXNq1+27cFrG+Yahz48bNnraxZ9y2Y6dUaXte0wp6LoiUl -KAiEFUO+7aO3Eh6sPHlXy9qUwrWMcEx3wiMa0rZ2Do2DOVKZg/NCMi7PHCdUpGHa -G9IJJw2jAgMBAAECggEAKwfYeGXbI3p0sDWA+K1SlP8tgFnL5RplIBehHR9FDtI4 -Y6clEK/T0bUObZsMoM0XMp54kA2ror1LxK7OdTuPfJOYH4yfT94zx8Z8sxs5GkCo -0YBRfoP8RY6uFV+MAvSXGB/u004ndnykoODdU7XZGN6dDVFrJQ5atCZShHNto63Q -voCoKP+MFtEdGFMY8ymLIKuRH3JAY9xof+fS/6vo2fClUxQPEuH7VN+Pr4A/j2ID -WUOdcClGHPuGDW2nGKyEcj/VBKnoT7rcU9u3FDf8/CuG4hf+720/1T/tgPdgyn02 -LVcb78ELlQQ5iLke72FvVQDCzZMUkA4+NYf4A0EUOQKBgQDUenrK6rbjdamsZ+Ze -hcEZpGIoYIzl2fVk2mAJd5ssE6JcGfHAZsFOhtAGICGVZedpOC7vw0fnBDAqJsP2 -RJ0mhehzScTTnqIsNLL8y76jkwI+19tBWFk/VKAgCxhpOtevJ0Tg6wsz4gU0zNi8 -TSICoUvrgeHc4xFIlJ4ppyCcpwKBgQC5F5JUEnrhMwkqIBmnlSWaKZ5HI9riRZXd -TdQmgmFYGZNU8IpyoqaJhBIw9O+5O3NggK7jPr7zOUEGoN9lQvwEy4s5KmVv3ahE -fJimXTd34WML/N1Klfkv9u4M8ajSwanJRv0CuNLGASnuB28jHUhzi0S7u0KoAMVl -nO1wOIH6pQKBgQC+RfAwRoAAsR2AhoqFmsGRdONxxQRn0QcviV73G2SZ0/tQ/uq+ -oUX6TD99PMjWQKs6TlW53+ZDujxngs95eXhBRykiFK1t1GDltUuTleDiGoOoQyTV -H4jMbbv29fimQ0tiTogf4lvl2kFPRyHPfkM2l7qk62qXo7+Wf4AjTMangQKBgE1F -afFvRZ1+kPTlMdCkk24osctBTL/qDQa21zq8c6c0Bi4Pvbzd6mi+mlmV5/6Msz42 -esykBVPFM0BxKNI9hLj7wMO8z1xETVtKKPBLOjzx+0el6TyaH83GaNs+iBx0fU1q -NXZNcrD3C2oz8FHYh8a0/ZziMJGywLYLLZUMAjeBAoGBANEOpeitU+8sYOSz1rmi -dtNKnMWFSxEWjtlxzXgRuIC8HJis9YHLI5Hny0Iao3to4AEXRZ/r6EL9+/Z10AdZ -iGmiN/cbMn6yTErpehKLLCB3S2nLxOdeLIgOcgPcJDyEyvyXCcBI3A9W4uMxYI2I -kplBOEOtHG/SR3vfiHN8Mdpb ------END PRIVATE KEY----- +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,D920B8941C56ADDC + +I2lW3QsAG/xubjtXpXh3wQ5Ru3VZiMkPNjc+G6/2JjjVr1sD+fzCWvvwdqdxGuNJ +gKdpPBHLuQfTTzGETE4NKDkYzmiPTVbZPJ77DyfL2cK1dcZtAY46RsHf+VMI5N8l +Be1jQSB5xvUa88dSIeowPTc2XSnTIoSFWCa38XuqYF7i0a3lv96eAyXpqB7Tm2r8 +SoYlm0n7/uzRpk6HWST65qnVv/j+37LuvSy6ehyh44+KDS4x9FUOZc5xwJ/37Jnl +SDC10+9zLc+jOTk6XgUuBSmG+xfZdcOrbknQ1Xj1YtseYH0plYAEWi4PsnMQkHzC +GGvK08Lgqxd7cGEKFh2MRZ/TEwriN5ud5HGm4yIHIj45rbedtRSQwl2EyHdWeW0J +rFltDy+SXnnkJaOcnBYXUD1jEwyy2lLamWRiu83VFbCv6yhOYuR6JejM6dctjgZ+ +Qf0PzH6L1bVpHKEl/GLByJ6GWYrQJqw83LAXlR+NNCC3nN7WAAaTuzA9LpgW9Vk0 +khRRs7rJGxwwwE4TfG9FbQxwuOsjKV9pRohB1x1nFMMm5IJ9SON2KjizsVdLbt7t +Gb/5M7RcSnnGvIWWXalXpFGKgciwYd8F1v0TJ+FMooZxgUp7Pmp5YKIHkBjMrnnW +rKuoxmA5oPgSNUtr4ddMJ1sTIQPhqI27+CrySTzWKH1ls45okBvsiCejpcJwfrZW +KLSkz/FsPoWm44uomBSDOikry8axrKQLB9tOVPKCx/z0VP060P9N81mu4h67bixr +xu+odIONqGhRZT/BYHL2NjDfWlFmTJQy8Drn1a7IEhp8FV7l2aY/hisrMN7MQVza +FGB0hMbVHGeFOCD9QNQwRU2wLtwpE7LT/lGNmKadQadXxeAqOWBckXrpwnrxZDEP +a8AYr2J55h/IE4Oi2DyibSEZdB+7334OJHMmr14q53eIpeit19BYVhWyu9AtORJp +As61C7s82AO+E5gOswsq05jwWV/GIIkgZ8/vswEffiihmDEf6AUZsVGW3BlpFlyU +i3g4e8HFTJ+s9Z3sTgZ1EWOP6Wd2OzyQYVA4ggBR/g/IC9s5em1wvAkVwIZaPvj7 +21BIQXyiGrw52T+vTUrAUG0l7yoHGCgVYJ+aEm+f103AiBYuReUbo39GEIY2GHLu +r3oUehtt4of0ootmPCmjrRUyY6LPeD+d+i1jJUSYFKezsVRpaiF5+J8YLGMcOPiI +8qRRNgXDMMvttwyhoxyr5+667OMv+XWr2VQj7i9MWCFwTMwNzdUoZI3PWDhXbXDO +lQJS6v3iAPw+KvLJywODe+C4shUqYdrRdUSKE0FfuB8Ajzh86+FmjJcZM+BSxM4J +hC2yjv114jDlsgjFSxQE2K1iotLUY9mfmW8QWVMO3L4LlNpr4ypNLYX0Ph2wgqzQ +kszXTFN11RFKFLUhF0Mi5m4ffMLPD5YyoqO9grpyC1Nt7vxaPPvcvPD86jK3ksqJ +MwucZGgm9HtUuAjGOSljUr0d+d+4pySJbcpH2YDIBHGVsCScYPVg8XZ1CYko3mq/ +d6jDUgydraEmQvIPiKMpTE18rW+jierv2FlB8AGcwxm2VWxuM25wQ40J2YuZLY7k +-----END RSA PRIVATE KEY----- diff --git a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp index 2e82b7d8f..d9153beca 100644 --- a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp +++ b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp @@ -29,11 +29,11 @@ using namespace coro_rpc; using namespace async_simple::coro; const std::string CERT_PATH = "../openssl_files"; -const std::string SERVER_CERT = "server.crt"; -const std::string SERVER_KEY = "server.key"; -const std::string CLIENT_CERT = "client.crt"; -const std::string CLIENT_KEY = "client.key"; -const std::string CA_CERT = "ca.crt"; +const std::string SERVER_CERT = "mutual_server.crt"; +const std::string SERVER_KEY = "mutual_server.key"; +const std::string CLIENT_CERT = "mutual_client.crt"; +const std::string CLIENT_KEY = "mutual_client.key"; +const std::string CA_CERT = "mutual_ca.crt"; namespace { std::string hello_ssl_test() { return "Hello World"; } @@ -187,7 +187,7 @@ TEST_CASE("testing RPC SSL mutual authentication - client with invalid cert") { coro_rpc_client client; bool init_ok = - client.init_ssl(CERT_PATH, CA_CERT, "fake.crt", "fake.key", "127.0.0.1"); + client.init_ssl(CERT_PATH, CA_CERT, "mutual_fake.crt", "mutual_fake.key", "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "init ssl should succeed"); auto ec = syncAwait(client.connect("127.0.0.1", "8804")); From bc61753cc4c4c6ea3e89df45dbb28c745891e6ca Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Fri, 10 Apr 2026 15:05:32 +0800 Subject: [PATCH 33/38] fix: restore main branch SSL certs and use server.crt for one-way auth tests - Restore server.crt/key from main branch (self-signed certificates) - Fix test code to use server.crt directly instead of ca.crt for verification - Update line endings for dh512.pem, dhparam.pem, generate.txt - Add server.csr from main branch SSL one-way authentication now uses self-signed server.crt directly, while mutual authentication tests continue using mutual_* prefixed certs. --- src/coro_http/tests/openssl_files/server.crt | 38 +++++++++---------- src/coro_http/tests/openssl_files/server.key | 2 +- .../tests/test_cinatra_websocket.cpp | 3 +- src/coro_rpc/tests/openssl_files/dh512.pem | 16 ++++---- src/coro_rpc/tests/openssl_files/dhparam.pem | 16 ++++---- src/coro_rpc/tests/openssl_files/generate.txt | 12 +++--- src/coro_rpc/tests/openssl_files/server.csr | 11 ++++++ src/coro_rpc/tests/test_coro_rpc_client.cpp | 4 +- src/coro_rpc/tests/test_coro_rpc_server.cpp | 7 ++-- 9 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 src/coro_rpc/tests/openssl_files/server.csr diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index 12c774ba3..aca31a7e3 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,19 +1,19 @@ ------BEGIN CERTIFICATE----- -MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh -bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx -MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 -MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv -c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT -nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw -PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz -WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 -2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo -+0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN -06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 -CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ -kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a -VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu -LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ -rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= ------END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh +bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx +MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT +nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw +PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz +WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 +2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo ++0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN +06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 +CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ +kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a +VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu +LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ +rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= +-----END CERTIFICATE----- diff --git a/src/coro_http/tests/openssl_files/server.key b/src/coro_http/tests/openssl_files/server.key index 0edde26de..9aef5f575 100644 --- a/src/coro_http/tests/openssl_files/server.key +++ b/src/coro_http/tests/openssl_files/server.key @@ -27,4 +27,4 @@ hC2yjv114jDlsgjFSxQE2K1iotLUY9mfmW8QWVMO3L4LlNpr4ypNLYX0Ph2wgqzQ kszXTFN11RFKFLUhF0Mi5m4ffMLPD5YyoqO9grpyC1Nt7vxaPPvcvPD86jK3ksqJ MwucZGgm9HtUuAjGOSljUr0d+d+4pySJbcpH2YDIBHGVsCScYPVg8XZ1CYko3mq/ d6jDUgydraEmQvIPiKMpTE18rW+jierv2FlB8AGcwxm2VWxuM25wQ40J2YuZLY7k ------END RSA PRIVATE KEY----- +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/src/coro_http/tests/test_cinatra_websocket.cpp b/src/coro_http/tests/test_cinatra_websocket.cpp index 3aa7a4bea..36f299a27 100644 --- a/src/coro_http/tests/test_cinatra_websocket.cpp +++ b/src/coro_http/tests/test_cinatra_websocket.cpp @@ -40,7 +40,8 @@ TEST_CASE("test wss client") { std::this_thread::sleep_for(200ms); coro_http_client client{}; - bool ok = client.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"); + bool ok = + client.init_ssl(asio::ssl::verify_peer, "../openssl_files/server.crt"); REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); async_simple::coro::syncAwait(client.connect("wss://localhost:9001")); diff --git a/src/coro_rpc/tests/openssl_files/dh512.pem b/src/coro_rpc/tests/openssl_files/dh512.pem index 5ccc3bdb4..8bb38bfa9 100644 --- a/src/coro_rpc/tests/openssl_files/dh512.pem +++ b/src/coro_rpc/tests/openssl_files/dh512.pem @@ -1,8 +1,8 @@ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEAxqqz/g13Jf4FzpXOlSYnJzmi8JrIdZFpu9RIPbrVX7uZNopulqZo -67bYNq38ZqHbstgFNxpuCndR6rsdJoiS/PY2xZ+J0e2ye7oI9UYf7Ad3SkAiZUyN -V+ENt4pGnyyppM4zxIJOZzj7cyH+rvThfWJl+Jlx74ZCd90u2u0mYBDvNXdnwRyy -Fi7ba8oN/UwDXH4Y2NUy5L0A4808G9NQsjrLXHsa7yOf9CRlU6X9fGybs0BQCNRb -wnneoAsIoPNSc8pR63AwAYsQWu4Fbfl5QffFOk8K6j5E6Nk+YSUYyAhxqlQ+w22b -ung1bzHU/Bo0AukYSEv1zOQJshMysYat8wIBAg== ------END DH PARAMETERS----- +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAxqqz/g13Jf4FzpXOlSYnJzmi8JrIdZFpu9RIPbrVX7uZNopulqZo +67bYNq38ZqHbstgFNxpuCndR6rsdJoiS/PY2xZ+J0e2ye7oI9UYf7Ad3SkAiZUyN +V+ENt4pGnyyppM4zxIJOZzj7cyH+rvThfWJl+Jlx74ZCd90u2u0mYBDvNXdnwRyy +Fi7ba8oN/UwDXH4Y2NUy5L0A4808G9NQsjrLXHsa7yOf9CRlU6X9fGybs0BQCNRb +wnneoAsIoPNSc8pR63AwAYsQWu4Fbfl5QffFOk8K6j5E6Nk+YSUYyAhxqlQ+w22b +ung1bzHU/Bo0AukYSEv1zOQJshMysYat8wIBAg== +-----END DH PARAMETERS----- diff --git a/src/coro_rpc/tests/openssl_files/dhparam.pem b/src/coro_rpc/tests/openssl_files/dhparam.pem index ba41a4de4..c250596bd 100644 --- a/src/coro_rpc/tests/openssl_files/dhparam.pem +++ b/src/coro_rpc/tests/openssl_files/dhparam.pem @@ -1,8 +1,8 @@ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEAu2Z47eEtvxxaGm5ZVAHsfyZ4l+SVjyQhwXcXwhTVYlp9K873W1jT -W4arg8yrtAq0SwB96VRckm+cdWDXLiMQ8RniI73JboAMTDYMpCzF7ojVxw8ZeAZv -cng9EgUDY+NrECcEIpNfZpzLCSOdVRzM27UiGx2UGxUxS/uDU2l9DI0bIecOYYgZ -6p9OOMTOqq0cyO1ZUr9mX7BLVa15rDH1EXDsdz8uqohcwjoZLzZmDnHmD0U0GL2k -IB0gt9m4mMO0FzMbotfWbhDt28L12dlLWFvwj/fK/+NG7kxUIORArOOK99lD2xBp -JLjdu/XZTp2jMRZNkhrEcOlL/dccz+UIgwIBAg== ------END DH PARAMETERS----- +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEAu2Z47eEtvxxaGm5ZVAHsfyZ4l+SVjyQhwXcXwhTVYlp9K873W1jT +W4arg8yrtAq0SwB96VRckm+cdWDXLiMQ8RniI73JboAMTDYMpCzF7ojVxw8ZeAZv +cng9EgUDY+NrECcEIpNfZpzLCSOdVRzM27UiGx2UGxUxS/uDU2l9DI0bIecOYYgZ +6p9OOMTOqq0cyO1ZUr9mX7BLVa15rDH1EXDsdz8uqohcwjoZLzZmDnHmD0U0GL2k +IB0gt9m4mMO0FzMbotfWbhDt28L12dlLWFvwj/fK/+NG7kxUIORArOOK99lD2xBp +JLjdu/XZTp2jMRZNkhrEcOlL/dccz+UIgwIBAg== +-----END DH PARAMETERS----- diff --git a/src/coro_rpc/tests/openssl_files/generate.txt b/src/coro_rpc/tests/openssl_files/generate.txt index 5c5f01fbb..0d94c72b9 100644 --- a/src/coro_rpc/tests/openssl_files/generate.txt +++ b/src/coro_rpc/tests/openssl_files/generate.txt @@ -1,7 +1,7 @@ -openssl genrsa -des3 -out server.key 1024 - -openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt - -openssl dhparam -out dh512.pem 512 - +openssl genrsa -des3 -out server.key 1024 + +openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt + +openssl dhparam -out dh512.pem 512 + openssl dhparam -out dhparam.pem 1024 \ No newline at end of file diff --git a/src/coro_rpc/tests/openssl_files/server.csr b/src/coro_rpc/tests/openssl_files/server.csr new file mode 100644 index 000000000..91a64dc15 --- /dev/null +++ b/src/coro_rpc/tests/openssl_files/server.csr @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBqzCCARQCAQAwVjELMAkGA1UEBhMCQ04xFTATBgNVBAcMDERlZmF1bHQgQ2l0 +eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDESMBAGA1UEAwwJbG9jYWxo +b3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTqVcYCR5rWfPII2tTEJtt +joIWFE+0+V//ObCdAXp34IXJckPCrcRAzxquAUm4Xm/veAuse2CGNxkPNsvUe563 +MW3A97BhkQZ9d0Tso8/R0VDE3otlMHdsnyFWN2NDA29FjJoxpzU4I9YdWAGElqLg +v+KfyIrZ1cJhvmYcAsWn4QIDAQABoBUwEwYJKoZIhvcNAQkHMQYMBHRlc3QwDQYJ +KoZIhvcNAQELBQADgYEAakt8QseV1LSmQB5rV77cOzkNXr38zk7xWok/lc2hdMNm +yVrBDqtApRXBMdbQ/wal0uTtQxkzHW4FU6FO8x3MNwllVBoC3Q+9rsKwEcmTP73b +qa4zSQ+Ho7+p8HX7svyhuLq3dqWydyRnPkr3femO0SxBsWXDlB4Oxi4RxwaW3Ns= +-----END CERTIFICATE REQUEST----- diff --git a/src/coro_rpc/tests/test_coro_rpc_client.cpp b/src/coro_rpc/tests/test_coro_rpc_client.cpp index 40ffda0c3..d0eace28f 100644 --- a/src/coro_rpc/tests/test_coro_rpc_client.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_client.cpp @@ -51,7 +51,7 @@ Lazy> create_client( coro_io::ExecutorWrapper<>* executor, std::string port) { auto client = std::make_shared(executor); #ifdef YLT_ENABLE_SSL - bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + bool ok = client->init_ssl("../openssl_files", "server.crt"); REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); #endif auto ec = co_await client->connect("127.0.0.1", port); @@ -696,7 +696,7 @@ TEST_CASE("testing client sync connect, unit test inject only") { auto res = server.async_start(); CHECK_MESSAGE(!res.hasResult(), "server start timeout"); coro_rpc_client client2(coro_io::get_global_executor()); - bool ok = client2.init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + bool ok = client2.init_ssl("../openssl_files", "server.crt"); CHECK(ok == true); val = client2.sync_connect("127.0.0.1", "8801"); CHECK_MESSAGE(val == coro_rpc::errc::not_connected, val.message()); diff --git a/src/coro_rpc/tests/test_coro_rpc_server.cpp b/src/coro_rpc/tests/test_coro_rpc_server.cpp index be8b9a819..6da154edd 100644 --- a/src/coro_rpc/tests/test_coro_rpc_server.cpp +++ b/src/coro_rpc/tests/test_coro_rpc_server.cpp @@ -580,15 +580,16 @@ TEST_CASE("testing coro rpc ssl subserver") { auto res = server.async_start(); CHECK_MESSAGE(!res.hasResult(), "server start failed"); coro_rpc_client client(coro_io::get_global_executor()); - CHECK(client.init_ssl("../openssl_files", "ca.crt", "127.0.0.1")); + CHECK(client.init_ssl("../openssl_files", "server.crt")); auto ec = syncAwait(client.connect("127.0.0.1", "8810")); REQUIRE_MESSAGE(!ec, std::to_string(client.get_client_id()).append(ec.message())); auto ret = syncAwait(client.call()); REQUIRE_MESSAGE(ret.has_value(), ret.error().msg); coro_http::coro_http_client cli; - CHECK_MESSAGE(cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/ca.crt"), - "init ssl failed"); + CHECK_MESSAGE( + cli.init_ssl(asio::ssl::verify_peer, "../openssl_files/server.crt"), + "init ssl failed"); auto result = syncAwait(cli.connect("https://localhost:8810")); CHECK_MESSAGE(!result.net_err, result.net_err.message()); result = syncAwait(cli.async_get("/index.html")); From a720bace6dbef32360689e245fc5cb376841e417 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Fri, 10 Apr 2026 15:27:52 +0800 Subject: [PATCH 34/38] fix: use server.crt instead of ca.crt in ServerTester SSL init --- src/coro_rpc/tests/ServerTester.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coro_rpc/tests/ServerTester.hpp b/src/coro_rpc/tests/ServerTester.hpp index e52dbf086..95d9520bc 100644 --- a/src/coro_rpc/tests/ServerTester.hpp +++ b/src/coro_rpc/tests/ServerTester.hpp @@ -148,7 +148,7 @@ struct ServerTester : TesterConfig { } #ifdef YLT_ENABLE_SSL if (use_ssl) { - bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + bool ok = client->init_ssl("../openssl_files", "server.crt"); REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); } #endif @@ -372,7 +372,7 @@ struct ServerTester : TesterConfig { } #ifdef YLT_ENABLE_SSL if (use_ssl) { - bool ok = client->init_ssl("../openssl_files", "ca.crt", "127.0.0.1"); + bool ok = client->init_ssl("../openssl_files", "server.crt"); REQUIRE_MESSAGE(ok == true, "init ssl fail, please check ssl config"); } #endif From 50406b2a8ce7b7c40271e9f521155d91c936d2ad Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Fri, 10 Apr 2026 18:29:29 +0800 Subject: [PATCH 35/38] fix: resolve SSL key mismatch in coro_http tests --- src/coro_http/tests/openssl_files/server.crt | 37 +++++++++----------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/coro_http/tests/openssl_files/server.crt b/src/coro_http/tests/openssl_files/server.crt index 39327191d..12c774ba3 100644 --- a/src/coro_http/tests/openssl_files/server.crt +++ b/src/coro_http/tests/openssl_files/server.crt @@ -1,22 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDpDCCAoygAwIBAgIUGpecTSZU5do6AkOeZKkH1PJrDDIwDQYJKoZIhvcNAQEL -BQAwVDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl -aWppbmcxEDAOBgNVBAoMB0FsaWJhYmExDzANBgNVBAMMBlRlc3RDQTAeFw0yNjA0 -MDgwODUwNThaFw0zNjA0MDUwODUwNThaMFcxCzAJBgNVBAYTAkNOMRAwDgYDVQQI -DAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRAwDgYDVQQKDAdBbGliYWJhMRIw -EAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDCZQTYx8ZI42XRatEb4TAnvdhoVR2azUaqJxYNJfltIzaGt2M7CgG0C259jfxY -FlMcgT+CWGULeCnfbDy2kU7qrecYr9uuiqnl7MvrdaETHsw0CbVT9pq034jnkdpw -T+EfDSe0sT5yvpaFpv4VL8+1NoIz5MwmQz4gbbDhernniyV4NAfMAsrbKKkGUolY -MzWbAdDWzCXZsowtF5+YAaXL/cXGopEUv+AWJrhyWBAzgF20a6388YUy/hlwnfqg -AwWGOChlK1Kn8DnTc/JF/p9LRNUtnJkUGMUjT+nLh8c5MLXBP7KEvPsnlrwa78X5 -EKg3L/aeTSndPQuqgxqZjc3rAgMBAAGjazBpMA8GA1UdEQQIMAaHBH8AAAEwCQYD -VR0TBAIwADALBgNVHQ8EBAMCBaAwHQYDVR0OBBYEFMSoVy1g75xMgitk0xOCHbQT -mQGdMB8GA1UdIwQYMBaAFEGZ6VXGkc24Btv7LtP2Q6dma2lMMA0GCSqGSIb3DQEB -CwUAA4IBAQA/GbZjJjQ0CmB1aBjhae5sy3Tiav9YCn/XKW7TeVP4jHwVw01ekBkf -P00aprdQrAmsBrTXmqqj4HEj8+P7pIM5FAKBBvPpgx6Rt4DNrDzoMI6T9qm31PJZ -qFb7TBlPke3xGiHV/KuG0llAi0mQagl9v2CDQgZz2wgtVT5a9uH439Fv6wZ7dix/ -Mbg8hG8tRDKx7cupQRB6JgTlHu2xmV/Z5llwQkzPbcJdsX5X+lRFIML4OURKUrRY -duw7H0ab9oiTzIOuVWLYu70yrdRkWFdg+SGo+JWYlTMe+3ad/T75erBTpkTHrp1v -+hKXPZrhRk0ESF7OGxzTinZlX2KX03Q7 +MIIDKDCCAhACCQDHu0UVVUEr4DANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD +TjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBh +bnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDI1MDM1NzMwWhcNMzIx +MDIyMDM1NzMwWjBWMQswCQYDVQQGEwJDTjEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5 +MRwwGgYDVQQKDBNEZWZhdWx0IENvbXBhbnkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCr6iWgRRYJ9QfKSUPT +nbw2rKZRlSBqnLeLdPam+s8RUA1p+YPoH2HJqIdxcfYmToz5t6G5OX8TFhAssShw +PalRlQm5QHp4pL7nqPV79auB3PYKv6TgOumwDUpoBxcu0l9di9fjYbC2LmpVJeVz +WQxCo+XO/g5YjXN1nPPeBgmZVkRvXLIYCTKshLlUa0nW7hj7Sl8CAV8OBNMBFkf1 +2vgcTqhs3yW9gnIwIoCFZvsdAsSbwR6zF1z96MeAYDIZWeyzUXkoZa4OCWwAhqzo ++0JWukuNuHhsQhIJDvIZWHEblT0GlentP8HPXjFnJHYGUAjx3Fj1mH8mFG0fEXXN +06qlAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGbKTy1mfSlJF012jKuIue2valI2 +CKz8X619jmxxIzk0k7wcmAlUUrUSFIzdIddZj92wYbBC1YNOWQ4AG5zpFo3NAQaZ +kYGnlt+d2pNHLaT4IV9JM4iwTqPyi+FsOwTjUGHgaOr+tfK8fZmPbDmAE46OlC/a +VVqNPmjaJiM2c/pJOs+HV9PvEOFmV9p5Yjjz4eV3jwqHdOcxZuLJl28/oqz65uCu +LQiivkdVCuwc1IlpRFejkrbkrk28XCCJwokLt03EQj4xs0sjoTKgd92fpjls/tt+ +rw+7ILsAsuoWPIdiuCArCU1LXJDz3FDHafX/dxzdVBzpfVgP0rNpS050Mls= -----END CERTIFICATE----- From f75e9b1d23d90a287427597a76af192eab8bb8e4 Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Fri, 10 Apr 2026 21:07:00 +0800 Subject: [PATCH 36/38] style: apply clang-format to modified files --- include/ylt/coro_rpc/impl/coro_rpc_client.hpp | 9 ++++++--- include/ylt/coro_rpc/impl/coro_rpc_server.hpp | 6 +++--- src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp | 6 +++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp index a68feb552..c78e2f3a9 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_client.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_client.hpp @@ -1538,7 +1538,8 @@ class coro_rpc_client { << ", cost time = " << (std::chrono::steady_clock::now() - tp) / std::chrono::microseconds(1) - << "us" << ", client_id: " << controller->client_id; + << "us" + << ", client_id: " << controller->client_id; } break; } @@ -1925,7 +1926,8 @@ class coro_rpc_client { << ", client_id: " << config_.client_id << ", cost time = " << (std::chrono::steady_clock::now() - tp) / std::chrono::microseconds(1) - << "us" << ", request ID: " << id; + << "us" + << ", request ID: " << id; co_return rpc_error{errc::io_error, ret.first.message()}; } } @@ -1933,7 +1935,8 @@ class coro_rpc_client { << ", cost time = " << (std::chrono::steady_clock::now() - tp) / std::chrono::microseconds(1) - << "us" << ", request ID: " << id; + << "us" + << ", request ID: " << id; co_return rpc_error{}; } diff --git a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp index 5e3b3dcae..259c9069e 100644 --- a/include/ylt/coro_rpc/impl/coro_rpc_server.hpp +++ b/include/ylt/coro_rpc/impl/coro_rpc_server.hpp @@ -186,8 +186,8 @@ class coro_rpc_server_base { } public: - const std::vector> & - get_acceptors() const noexcept { + const std::vector> + &get_acceptors() const noexcept { return acceptors_; } async_simple::Future async_start() noexcept { @@ -230,7 +230,7 @@ class coro_rpc_server_base { } } if (!errc_) { - if constexpr (requires(typename server_config::executor_pool_t &pool) { + if constexpr (requires(typename server_config::executor_pool_t & pool) { pool.run(); }) { thd_ = std::thread([this] { diff --git a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp index d9153beca..94dc46f58 100644 --- a/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp +++ b/src/coro_rpc/tests/test_rpc_ssl_mutual_auth.cpp @@ -37,7 +37,7 @@ const std::string CA_CERT = "mutual_ca.crt"; namespace { std::string hello_ssl_test() { return "Hello World"; } -} +} // namespace #ifdef YLT_ENABLE_SSL TEST_CASE("testing RPC SSL one-way authentication") { @@ -186,8 +186,8 @@ TEST_CASE("testing RPC SSL mutual authentication - client with invalid cert") { REQUIRE_MESSAGE(!res.hasResult(), "server start failed"); coro_rpc_client client; - bool init_ok = - client.init_ssl(CERT_PATH, CA_CERT, "mutual_fake.crt", "mutual_fake.key", "127.0.0.1"); + bool init_ok = client.init_ssl(CERT_PATH, CA_CERT, "mutual_fake.crt", + "mutual_fake.key", "127.0.0.1"); REQUIRE_MESSAGE(init_ok == true, "init ssl should succeed"); auto ec = syncAwait(client.connect("127.0.0.1", "8804")); From 47cb25ba4cfb8a34accaca9f2c31034ee8459f2b Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Mon, 13 Apr 2026 10:50:51 +0800 Subject: [PATCH 37/38] feat: add mutual authentication init_ssl to coro_http_client Add init_ssl overload that supports client certificate and key files for mutual SSL/TLS authentication, based on upstream code. --- .../standalone/cinatra/coro_http_client.hpp | 5789 +++++++++-------- 1 file changed, 2939 insertions(+), 2850 deletions(-) diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index 123f3c2e1..dd6fc1aae 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -1,2850 +1,2939 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asio/dispatch.hpp" -#include "asio/error.hpp" -#include "asio/ip/tcp.hpp" -#include "asio/streambuf.hpp" -#include "async_simple/Future.h" -#include "async_simple/Unit.h" -#include "async_simple/coro/FutureAwaiter.h" -#include "async_simple/coro/Lazy.h" -#ifdef CINATRA_ENABLE_GZIP -#include "gzip.hpp" -#endif -#ifdef CINATRA_ENABLE_BROTLI -#include "brzip.hpp" -#endif -#include "cinatra_log_wrapper.hpp" -#include "error.hpp" -#include "http_parser.hpp" -#include "multipart.hpp" -#include "picohttpparser.h" -#include "response_cv.hpp" -#include "string_resize.hpp" -#include "uri.hpp" -#include "websocket.hpp" -#include "ylt/coro_io/coro_file.hpp" -#include "ylt/coro_io/coro_io.hpp" -#include "ylt/coro_io/io_context_pool.hpp" - -namespace coro_io { -template -class client_pool; -} -namespace cinatra { -template -struct is_stream : std::false_type {}; - -template -struct is_stream< - T, std::void_t().read(nullptr, 0), - std::declval().async_read(nullptr, 0))>> - : std::true_type {}; - -template -constexpr bool is_stream_v = is_stream::value; - -template -struct is_span : std::false_type {}; - -template -struct is_span().data(), - std::declval().size())>> - : std::true_type {}; - -template -constexpr bool is_span_v = is_span::value; - -template -struct is_smart_ptr : std::false_type {}; - -template -struct is_smart_ptr< - T, std::void_t().get(), *std::declval(), - is_stream_v)>> - : std::true_type {}; - -template -constexpr bool is_stream_ptr_v = is_smart_ptr::value || std::is_pointer_v; - -struct http_header; - -struct resp_data { - std::error_code net_err; - int status = 0; - bool eof = false; - std::string_view resp_body; - std::span resp_headers; -#ifdef BENCHMARK_TEST - uint64_t total = 0; -#endif -}; - -template -struct req_context { - req_content_type content_type = req_content_type::none; - std::string req_header; /*header string*/ - String content; /*body*/ - coro_io::coro_file *resp_body_stream = nullptr; -}; - -struct multipart_t { - std::string filename; - std::string content; - size_t size = 0; -}; - -struct read_result { - std::span buf; - bool eof; - std::error_code err; -}; - -enum class upload_type_t { with_length, chunked, multipart }; - -class coro_http_client : public std::enable_shared_from_this { - public: - struct config { - std::optional conn_timeout_duration; - std::optional req_timeout_duration; - std::string sec_key; - size_t max_single_part_size; - std::string proxy_host; - std::string proxy_port; - std::string proxy_auth_username; - std::string proxy_auth_passwd; - std::string proxy_auth_token; - bool enable_tcp_no_delay; -#ifdef CINATRA_ENABLE_SSL - bool use_ssl = - false; // if set use_ssl true, cinatra will add https automaticlly. -#ifdef YLT_ENABLE_NTLS - bool use_ntls = - false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. -#endif // YLT_ENABLE_NTLS -#endif - }; - - coro_http_client(asio::io_context::executor_type executor) - : executor_wrapper_(executor), - timer_(&executor_wrapper_), - socket_(std::make_shared(executor)), - head_buf_(socket_->head_buf_), - chunked_buf_(socket_->chunked_buf_), - create_tp_(std::chrono::steady_clock::now()) {} - - coro_http_client( - coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor()) - : coro_http_client(executor->get_asio_executor()) {} - - bool init_config(const config &conf) { - config_ = conf; - if (conf.conn_timeout_duration.has_value()) { - set_conn_timeout(*conf.conn_timeout_duration); - } - if (conf.req_timeout_duration.has_value()) { - set_req_timeout(*conf.req_timeout_duration); - } - if (!conf.sec_key.empty()) { - set_ws_sec_key(conf.sec_key); - } - if (conf.max_single_part_size > 0) { - set_max_single_part_size(conf.max_single_part_size); - } - if (!conf.proxy_host.empty()) { - set_proxy_basic_auth(conf.proxy_host, conf.proxy_port); - } - if (!conf.proxy_auth_username.empty()) { - set_proxy_basic_auth(conf.proxy_auth_username, conf.proxy_auth_passwd); - } - if (!conf.proxy_auth_token.empty()) { - set_proxy_bearer_token_auth(conf.proxy_auth_token); - } - if (conf.enable_tcp_no_delay) { - enable_tcp_no_delay_ = conf.enable_tcp_no_delay; - } -#ifdef CINATRA_ENABLE_SSL - set_ssl_schema(conf.use_ssl); -#ifdef YLT_ENABLE_NTLS - set_ntls_schema(conf.use_ntls); -#endif // YLT_ENABLE_NTLS -#endif - return true; - } - - ~coro_http_client() { close(); } - - auto get_create_time_point() const noexcept { - return std::chrono::steady_clock::now(); - } - - void close() { - if (socket_ == nullptr || socket_->has_closed_) - return; - - asio::dispatch(executor_wrapper_.get_asio_executor(), [socket = socket_] { - close_socket(*socket); - }); - } - - coro_io::ExecutorWrapper<> &get_executor() { return executor_wrapper_; } - - const config &get_config() { return config_; } - -#ifdef CINATRA_ENABLE_SSL - bool init_ssl(int verify_mode, const std::string &base_path, - const std::string &cert_file, const std::string &sni_hostname) { - if (has_init_ssl_) { - return true; - } - - try { -#ifdef YLT_ENABLE_NTLS - if (use_ntls_) { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(NTLS_client_method())); - - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); - - // Set NTLS cipher suites - if (!SSL_CTX_set_cipher_list( - ssl_ctx_->native_handle(), - "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { - CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; - } - } - else { - CINATRA_LOG_ERROR << "NTLS is not supported in this build."; - return false; - } -#else - ssl_ctx_ = - std::make_unique(asio::ssl::context::sslv23); -#endif // YLT_ENABLE_NTLS - auto full_cert_file = std::filesystem::path(base_path).append(cert_file); - if (std::filesystem::exists(full_cert_file)) { - ssl_ctx_->load_verify_file(full_cert_file.string()); - } - else { - if (!base_path.empty() || !cert_file.empty()) - return false; - } - - if (base_path.empty() && cert_file.empty()) { - ssl_ctx_->set_default_verify_paths(); - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - // ssl_ctx_.add_certificate_authority(asio::buffer(CA_PEM)); - if (!sni_hostname.empty()) { - ssl_ctx_->set_verify_callback( - asio::ssl::host_name_verification(sni_hostname)); - - if (need_set_sni_host_) { - // Set SNI Hostname (many hosts need this to handshake successfully) - SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), - sni_hostname.c_str()); - } - } - - has_init_ssl_ = true; - is_ssl_schema_ = true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); - return false; - } - return true; - } - - [[nodiscard]] bool init_ssl(int verify_mode = asio::ssl::verify_none, - std::string full_path = "", - const std::string &sni_hostname = "") { - std::string base_path; - std::string cert_file; - if (full_path.empty()) { - base_path = ""; - cert_file = ""; - } - else { - base_path = full_path.substr(0, full_path.find_last_of('/')); - cert_file = full_path.substr(full_path.find_last_of('/') + 1); - } - return init_ssl(verify_mode, base_path, cert_file, sni_hostname); - } - - /*! - * Initialize SSL with client certificate for mutual authentication - * @param verify_mode SSL verify mode (e.g., asio::ssl::verify_peer) - * @param base_path Base path for certificate files - * @param cert_file CA certificate file name for server verification - * @param client_cert_file Client certificate file name for mutual - * authentication - * @param client_key_file Client private key file name for mutual - * authentication - * @param sni_hostname SNI hostname - * @return true if initialization successful - */ - [[nodiscard]] bool init_ssl(int verify_mode, const std::string &base_path, - const std::string &cert_file, - const std::string &client_cert_file, - const std::string &client_key_file, - const std::string &sni_hostname) { - if (has_init_ssl_) { - return true; - } - - try { - ssl_ctx_ = std::make_unique(asio::ssl::context::tls); - - // Set cipher list for OpenSSL 3.0 compatibility - SSL_CTX *native_ctx = ssl_ctx_->native_handle(); - if (native_ctx) { - // const char* ciphers = "HIGH:!aNULL:!MD5:!3DES"; - // SSL_CTX_set_cipher_list(native_ctx, ciphers); - SSL_CTX_set_min_proto_version(native_ctx, TLS1_2_VERSION); - SSL_CTX_set_max_proto_version(native_ctx, TLS1_3_VERSION); - } - - auto full_cert_file = std::filesystem::path(base_path).append(cert_file); - if (std::filesystem::exists(full_cert_file)) { - ssl_ctx_->load_verify_file(full_cert_file.string()); - CINATRA_LOG_INFO << "loaded CA certificate: " - << full_cert_file.string(); - } - else { - if (!base_path.empty() || !cert_file.empty()) { - CINATRA_LOG_ERROR << "CA certificate file not found: " - << full_cert_file.string(); - return false; - } - } - - if (base_path.empty() && cert_file.empty()) { - ssl_ctx_->set_default_verify_paths(); - } - - // Load client certificate and key for mutual authentication - auto full_client_cert_file = - std::filesystem::path(base_path).append(client_cert_file); - auto full_client_key_file = - std::filesystem::path(base_path).append(client_key_file); - - if (std::filesystem::exists(full_client_cert_file)) { - ssl_ctx_->use_certificate_chain_file(full_client_cert_file.string()); - CINATRA_LOG_INFO << "loaded client certificate: " - << full_client_cert_file.string(); - } - else { - CINATRA_LOG_ERROR << "client certificate file not found: " - << full_client_cert_file.string(); - return false; - } - - if (std::filesystem::exists(full_client_key_file)) { - ssl_ctx_->use_private_key_file(full_client_key_file.string(), - asio::ssl::context::pem); - CINATRA_LOG_INFO << "loaded client private key: " - << full_client_key_file.string(); - } - else { - CINATRA_LOG_ERROR << "client key file not found: " - << full_client_key_file.string(); - return false; - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - if (!sni_hostname.empty()) { - ssl_ctx_->set_verify_callback( - asio::ssl::host_name_verification(sni_hostname)); - - if (need_set_sni_host_) { - SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), - sni_hostname.c_str()); - } - } - - has_init_ssl_ = true; - is_ssl_schema_ = true; - CINATRA_LOG_INFO << "SSL initialized with client certificate for mutual " - "authentication"; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); - return false; - } - return true; - } -#endif - - // return body_, the user will own body's lifetime. - std::string release_buf() { - if (body_.empty()) { - return std::move(resp_chunk_str_); - } - return std::move(body_); - } - -#ifdef CINATRA_ENABLE_GZIP - void set_ws_deflate(bool enable_ws_deflate) { - enable_ws_deflate_ = enable_ws_deflate; - } -#endif - - /*! - * Connect server - * - * only make socket connet(or handshake) to the host - * - * @param uri server address - * @param eps endpoints of resolve result. if eps is not nullptr and vector is - * empty, it will return the endpoints that, else if vector is not empty, it - * will use the eps to skill resolve and connect to server directly. - * @return resp_data - */ - async_simple::coro::Lazy connect( - std::string uri, std::vector *eps = nullptr) { - resp_data data{}; - bool no_schema = !has_schema(uri); - std::string append_uri; - if (no_schema) { -#ifdef CINATRA_ENABLE_SSL - if (is_ssl_schema_) - append_uri.append("https://").append(uri); - else -#endif - append_uri.append("http://").append(uri); - } - - auto [ok, u] = handle_uri(data, no_schema ? append_uri : uri); - if (!ok) { - co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; - } - { - if (u.is_websocket()) { - // build websocket http header - add_header("Upgrade", "websocket"); - add_header("Connection", "Upgrade"); - if (ws_sec_key_.empty()) { - ws_sec_key_ = "s//GYHa/XO7Hd2F2eOGfyA=="; // provide a random string. - } - add_header("Sec-WebSocket-Key", ws_sec_key_); - add_header("Sec-WebSocket-Version", "13"); -#ifdef CINATRA_ENABLE_GZIP - if (enable_ws_deflate_) - add_header("Sec-WebSocket-Extensions", - "permessage-deflate; client_max_window_bits"); -#endif - req_context<> ctx{}; - data = co_await async_request(std::move(uri), http_method::GET, - std::move(ctx)); - -#ifdef CINATRA_ENABLE_GZIP - if (enable_ws_deflate_) { - for (auto c : data.resp_headers) { - if (c.name == "Sec-WebSocket-Extensions") { - if (c.value.find("permessage-deflate;") != std::string::npos) { - is_server_support_ws_deflate_ = true; - } - else { - is_server_support_ws_deflate_ = false; - } - break; - } - } - } -#endif - co_return data; - } - data = co_await connect(u, eps); - } - if (socket_->is_timeout_) { - co_return resp_data{make_error_code(http_errc::connect_timeout), 404}; - } - if (!data.net_err) { - data.status = 200; - } - co_return data; - } - - bool has_closed() { return socket_->has_closed_; } - - [[nodiscard]] std::size_t get_pipeline_size() const noexcept { return 0; } - - const auto &get_headers() { return req_headers_; } - - void set_headers(std::unordered_map req_headers) { - req_headers_ = std::move(req_headers); - } - - bool add_header(std::string key, std::string val) { - if (key.empty()) - return false; - - req_headers_[key] = std::move(val); - - return true; - } - - void set_ws_sec_key(std::string sec_key) { ws_sec_key_ = std::move(sec_key); } - - /** - * @brief Set the max http body size object, in default it's INT64_MAX - * - * @param max_size - */ - void set_max_http_body_size(int64_t max_size) { - max_http_body_len_ = max_size; - } - - size_t available() { - std::error_code ec{}; - return socket_->impl_.available(ec); - } - - async_simple::coro::Lazy read_websocket() { - auto time_out_guard = - timer_guard(this, req_timeout_duration_, "websocket timer"); - co_return co_await async_read_ws(); - } - - async_simple::coro::Lazy write_websocket( - const char *data, opcode op = opcode::text) { - std::string str(data); - co_return co_await write_websocket(str, op); - } - - async_simple::coro::Lazy write_websocket( - const char *data, size_t size, opcode op = opcode::text) { - std::string str(data, size); - co_return co_await write_websocket(str, op); - } - - async_simple::coro::Lazy write_websocket( - std::string_view data, opcode op = opcode::text) { - std::string str(data); - co_return co_await write_websocket(str, op); - } - - async_simple::coro::Lazy write_websocket( - std::string &data, opcode op = opcode::text) { - co_return co_await write_websocket(std::span(data), op); - } - - async_simple::coro::Lazy write_websocket( - std::string &&data, opcode op = opcode::text) { - co_return co_await write_websocket(std::span(data), op); - } - - async_simple::coro::Lazy write_ws_frame(std::span msg, - websocket ws, opcode op, - resp_data &data, - bool eof = true) { - auto header = ws.encode_frame(msg, op, eof, enable_ws_deflate_); - std::vector buffers{ - asio::buffer(header), asio::buffer(msg.data(), msg.size())}; - - auto [ec, sz] = co_await async_write(buffers); - if (ec) { - data.net_err = ec; - data.status = 404; - } - } - -#ifdef CINATRA_ENABLE_GZIP - void gzip_compress(std::string_view source, std::string &dest_buf, - std::span &span, resp_data &data) { - if (enable_ws_deflate_ && is_server_support_ws_deflate_) { - if (cinatra::gzip_codec::deflate(source, dest_buf)) { - span = dest_buf; - } - else { - CINATRA_LOG_ERROR << "compress data error, data: " << source; - data.net_err = std::make_error_code(std::errc::protocol_error); - data.status = 404; - } - } - } -#endif - - template - async_simple::coro::Lazy write_websocket( - Source source, opcode op = opcode::text) { - resp_data data{}; - - websocket ws{}; - std::string close_str; - if (op == opcode::close) { - if constexpr (is_span_v) { - close_str = ws.format_close_payload(close_code::normal, source.data(), - source.size()); - source = {close_str.data(), close_str.size()}; - } - } - - std::span span{}; - if constexpr (is_span_v) { - span = {source.data(), source.size()}; -#ifdef CINATRA_ENABLE_GZIP - std::string dest_buf; - if (enable_ws_deflate_) { - gzip_compress({source.data(), source.size()}, dest_buf, span, data); - } -#endif - co_await write_ws_frame(span, ws, op, data, true); - } - else { - while (true) { - auto result = co_await source(); - span = {result.buf.data(), result.buf.size()}; -#ifdef CINATRA_ENABLE_GZIP - std::string dest_buf; - if (enable_ws_deflate_) { - gzip_compress({result.buf.data(), result.buf.size()}, dest_buf, span, - data); - } -#endif - co_await write_ws_frame(span, ws, op, data, result.eof); - - if (result.eof || data.status == 404) { - break; - } - } - } - - co_return data; - } - - async_simple::coro::Lazy write_websocket_close( - std::string msg = "") { - co_return co_await write_websocket(std::move(msg), opcode::close); - } - -#ifdef BENCHMARK_TEST - void set_bench_stop() { stop_bench_ = true; } -#endif - - async_simple::coro::Lazy async_patch( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::PATCH, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_options( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::OPTIONS, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_trace( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::TRACE, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_head( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::HEAD, - cinatra::req_context<>{}, std::move(headers)); - } - - // CONNECT example.com HTTP/1.1 - async_simple::coro::Lazy async_http_connect( - std::string uri, - std::unordered_map headers = {}) { - return async_request(std::move(uri), cinatra::http_method::CONNECT, - cinatra::req_context<>{}, std::move(headers)); - } - - async_simple::coro::Lazy async_get( - std::string uri, - std::unordered_map headers = {}) { - resp_data data{}; - req_context<> ctx{}; - data = co_await async_request(std::move(uri), http_method::GET, - std::move(ctx), std::move(headers)); -#ifdef BENCHMARK_TEST - data.total = total_len_; -#endif - if (redirect_uri_.empty() || !is_redirect(data)) { - co_return data; - } - else { - if (enable_follow_redirect_) - data = co_await async_request(std::move(redirect_uri_), - http_method::GET, std::move(ctx)); - co_return data; - } - } - - resp_data get(std::string uri, - std::unordered_map headers = {}) { - return async_simple::coro::syncAwait( - async_get(std::move(uri), std::move(headers))); - } - - resp_data post(std::string uri, std::string content, - req_content_type content_type, - std::unordered_map headers = {}) { - return async_simple::coro::syncAwait(async_post( - std::move(uri), std::move(content), content_type, std::move(headers))); - } - - async_simple::coro::Lazy async_post( - std::string uri, std::string content, req_content_type content_type, - std::unordered_map headers = {}) { - req_context<> ctx{content_type, "", std::move(content)}; - return async_request(std::move(uri), http_method::POST, std::move(ctx), - std::move(headers)); - } - - async_simple::coro::Lazy async_delete( - std::string uri, std::string content, req_content_type content_type, - std::unordered_map headers = {}) { - req_context<> ctx{content_type, "", std::move(content)}; - return async_request(std::move(uri), http_method::DEL, std::move(ctx), - std::move(headers)); - } - - async_simple::coro::Lazy async_put( - std::string uri, std::string content, req_content_type content_type, - std::unordered_map headers = {}) { - req_context<> ctx{content_type, "", std::move(content)}; - return async_request(std::move(uri), http_method::PUT, std::move(ctx), - std::move(headers)); - } - - bool add_str_part(std::string name, std::string content) { - size_t size = content.size(); - return form_data_ - .emplace(std::move(name), multipart_t{"", std::move(content), size}) - .second; - } - - bool add_file_part(std::string name, std::string filename) { - if (form_data_.find(name) != form_data_.end()) { - CINATRA_LOG_WARNING << "name already exist: " << name; - return false; - } - - std::error_code ec; - bool r = std::filesystem::exists(filename, ec); - if (!r || ec) { - if (ec) { - CINATRA_LOG_WARNING << ec.message(); - } - CINATRA_LOG_WARNING << "file not exists, " - << std::filesystem::current_path().string(); - return false; - } - - size_t file_size = std::filesystem::file_size(filename); - form_data_.emplace(std::move(name), - multipart_t{std::move(filename), "", file_size}); - return true; - } - - void set_max_single_part_size(size_t size) { max_single_part_size_ = size; } - - struct timer_guard { - timer_guard(coro_http_client *self, - std::chrono::steady_clock::duration duration, std::string msg) - : self(self), dur_(duration) { - self->socket_->is_timeout_ = false; - - if (duration.count() >= 0) { - self->timeout(self->timer_, duration, std::move(msg)) - .start([](auto &&) { - }); - } - return; - } - ~timer_guard() { - if (dur_.count() > 0 && self->socket_->is_timeout_ == false) { - std::error_code ignore_ec; - self->timer_.cancel(ignore_ec); - } - } - coro_http_client *self; - std::chrono::steady_clock::duration dur_; - }; - - async_simple::coro::Lazy async_download(std::string uri, - std::string filename, - std::string range = "") { - resp_data data{}; - coro_io::coro_file file; - file.open(filename, std::ios::trunc | std::ios::out); - if (!file.is_open()) { - data.net_err = std::make_error_code(std::errc::no_such_file_or_directory); - data.status = 404; - co_return data; - } - - req_context<> ctx{}; - if (range.empty()) { - add_header("Transfer-Encoding", "chunked"); - ctx = {req_content_type::none, "", "", &file}; - } - else { - std::string req_str = "Range: bytes="; - req_str.append(range).append(CRCF); - ctx = {req_content_type::none, std::move(req_str), {}, &file}; - } - - data = co_await async_request(std::move(uri), http_method::GET, - std::move(ctx)); - - co_return data; - } - - resp_data download(std::string uri, std::string filename, - std::string range = "") { - return async_simple::coro::syncAwait( - async_download(std::move(uri), std::move(filename), std::move(range))); - } - - bool is_body_in_out_buf() const { return !out_buf_.empty(); } - - void reset() { - if (!has_closed()) { - close_socket(*socket_); - } - - socket_->impl_ = asio::ip::tcp::socket{executor_wrapper_.context()}; - if (!socket_->impl_.is_open()) { - std::error_code ec; - socket_->impl_.open(asio::ip::tcp::v4(), ec); - if (ec) { - CINATRA_LOG_WARNING << "client reset socket failed, reason: " - << ec.message(); - return; - } - } - - socket_->has_closed_ = true; -#ifdef CINATRA_ENABLE_SSL - need_set_sni_host_ = true; - if (has_init_ssl_) { - socket_->ssl_stream_ = nullptr; - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - has_init_ssl_ = false; - } -#endif -#ifdef BENCHMARK_TEST - total_len_ = 0; -#endif - - // clear - head_buf_.consume(head_buf_.size()); - chunked_buf_.consume(chunked_buf_.size()); - resp_chunk_str_.clear(); - } - - std::string_view get_host() { return host_; } - - std::string_view get_port() { return port_; } - - private: - async_simple::coro::Lazy send_file_copy_with_chunked( - std::string_view source, std::error_code &ec) { - std::string file_data; - detail::resize(file_data, max_single_part_size_); - coro_io::coro_file file{}; - file.open(source, std::ios::in); - if (!file.is_open()) { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - while (!file.eof()) { - auto [rd_ec, rd_size] = - co_await file.async_read(file_data.data(), file_data.size()); - std::vector bufs; - std::string size_str; - cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, - file.eof()); - std::size_t size; - if (std::tie(ec, size) = co_await async_write(bufs); ec) { - break; - } - } - } - - async_simple::coro::Lazy send_file_copy_with_length( - std::string_view source, std::error_code &ec, std::size_t length, - std::size_t offset) { - if (length <= 0) { - co_return; - } - std::string file_data; - detail::resize(file_data, (std::min)(max_single_part_size_, length)); - coro_io::coro_file file{}; - file.open(source, std::ios::in); - if (!file.is_open()) { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - file.seek(offset, std::ios::cur); - std::size_t size; - while (length > 0) { - if (std::tie(ec, size) = co_await file.async_read( - file_data.data(), (std::min)(file_data.size(), length)); - ec) { - // bad request, file may smaller than content-length - break; - } - length -= size; - if (length > 0 && file.eof()) { - // bad request, file may smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - break; - } - if (std::tie(ec, size) = - co_await async_write(asio::buffer(file_data.data(), size)); - ec) { - break; - } - } - } -#ifdef __linux__ - struct fd_guard { - int fd; - fd_guard(const char *file_path) : fd(::open(file_path, O_RDONLY)) {} - ~fd_guard() { - if (fd >= 0) { - ::close(fd); - } - } - }; - async_simple::coro::Lazy send_file_no_copy_with_length( - const std::filesystem::path &source, std::error_code &ec, - std::size_t length, std::size_t offset) { - fd_guard guard(source.c_str()); - if (guard.fd < 0) [[unlikely]] { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - std::size_t actual_len = 0; - std::tie(ec, actual_len) = co_await coro_io::async_sendfile( - socket_->impl_, guard.fd, offset, length); - if (ec) [[unlikely]] { - co_return; - } - if (actual_len != length) [[unlikely]] { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - co_return; - } - } - async_simple::coro::Lazy send_file_no_copy_with_chunked( - const std::filesystem::path &source, std::error_code &ec) { - fd_guard guard(source.c_str()); - if (guard.fd < 0) [[unlikely]] { - ec = std::make_error_code(std::errc::bad_file_descriptor); - co_return; - } - off_t now_position = 0, - max_position = std::filesystem::file_size(source, ec); - if (ec) { - co_return; - } - size_t len = - std::min(max_single_part_size_, max_position - now_position); - // send chunked - std::array chunked_buffer; - std::size_t sz; - std::tie(ec, sz) = co_await async_write( - asio::buffer(get_chuncked_buffers(len, chunked_buffer))); - if (ec) [[unlikely]] { - co_return; - } - do { - std::size_t actual_len = 0; - std::tie(ec, actual_len) = co_await coro_io::async_sendfile( - socket_->impl_, guard.fd, now_position, len); - if (ec) [[unlikely]] { - co_return; - } - if (actual_len != len) [[unlikely]] { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - co_return; - } - if (now_position += actual_len; now_position < max_position) { - len = std::min(max_single_part_size_, - max_position - now_position); - std::tie(ec, sz) = co_await async_write(asio::buffer( - get_chuncked_buffers(len, chunked_buffer))); - if (ec) { - co_return; - } - } - else [[unlikely]] { - std::tie(ec, sz) = co_await async_write(asio::buffer( - get_chuncked_buffers(len, chunked_buffer))); - if (ec) { - co_return; - } - break; - } - } while (true); - } -#endif - template - static std::size_t getRemainingBytes(stream &file) { - auto current_pos = file.tellg(); - file.seekg(0, std::ios::end); - auto end_pos = file.tellg(); - auto remaining_bytes = end_pos - current_pos; - file.seekg(current_pos); - return remaining_bytes; - } - - template - void check_source(resp_data &data, Source &source) { - if constexpr (is_stream_ptr_v) { - if (!source) { - data = resp_data{ - std::make_error_code(std::errc::no_such_file_or_directory), 404}; - } - } - else if constexpr (std::is_same_v || - std::is_same_v) { - if (!std::filesystem::exists(source)) { - data = resp_data{ - std::make_error_code(std::errc::no_such_file_or_directory), 404}; - } - } - } - - void handle_upload_header_with_multipart() { - size_t content_len = multipart_content_len(); - add_header("Content-Length", std::to_string(content_len)); - } - - void handle_upload_header_with_chunked( - std::unordered_map &headers) { - if (!resp_chunk_str_.empty()) { - resp_chunk_str_.clear(); - } - - if (headers.empty()) { - add_header("Transfer-Encoding", "chunked"); - } - else { - headers.emplace("Transfer-Encoding", "chunked"); - } - } - - template - int64_t handle_upload_header_with_length( - resp_data &data, Source &source, - std::unordered_map &headers, uint64_t offset, - int64_t content_length) { - if (content_length < 0) { - if constexpr (is_stream_ptr_v) { - content_length = getRemainingBytes(*source); - } - else if constexpr (std::is_same_v || - std::is_same_v) { - content_length = std::filesystem::file_size(source); - } - else { - CINATRA_LOG_ERROR - << "user should set content-length before calling async_upload " - "when source is user-defined function."; - data = - resp_data{std::make_error_code(std::errc::invalid_argument), 404}; - return content_length; - } - content_length -= offset; - if (content_length < 0) { - CINATRA_LOG_ERROR << "the offset is larger than the end of file"; - data = - resp_data{std::make_error_code(std::errc::invalid_argument), 404}; - return content_length; - } - } - - assert(content_length >= 0); - char buf[32]; - auto [ptr, _] = std::to_chars(buf, buf + 32, content_length); - if (headers.empty()) { - add_header("Content-Length", std::string(buf, ptr - buf)); - } - else { - headers.emplace("Content-Length", std::string_view(buf, ptr - buf)); - } - return content_length; - } - - async_simple::coro::Lazy send_fstream_with_multipart( - std::error_code &ec) { - resp_data data{}; - for (auto &[key, part] : form_data_) { - data = co_await send_single_part(key, part); - - if (data.net_err) { - ec = data.net_err; - co_return; - } - } - - std::string last_part; - size_t size = 0; - last_part.append("--").append(BOUNDARY).append("--").append(CRCF); - if (std::tie(ec, size) = co_await async_write(asio::buffer(last_part)); - ec) { - co_return; - } - } - - template - async_simple::coro::Lazy send_fstream_with_chunked( - Source &source, std::error_code &ec) { - size_t size = 0; - std::string file_data; - detail::resize(file_data, max_single_part_size_); - while (!source->eof()) { - size_t rd_size = - source->read(file_data.data(), file_data.size()).gcount(); - std::vector bufs; - std::string size_str; - cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, - source->eof()); - if (std::tie(ec, size) = co_await async_write(bufs); ec) { - break; - } - } - } - - template - async_simple::coro::Lazy send_fstream_with_length( - Source &source, std::error_code &ec, uint64_t offset, - int64_t content_length) { - size_t size = 0; - source->seekg(offset, std::ios::cur); - std::string file_data; - detail::resize(file_data, std::min(max_single_part_size_, - content_length)); - while (content_length > 0 && !source->eof()) { - size_t rd_size = - source - ->read(file_data.data(), - std::min(content_length, file_data.size())) - .gcount(); - if (std::tie(ec, size) = - co_await async_write(asio::buffer(file_data.data(), rd_size)); - ec) { - break; - } - content_length -= rd_size; - } - if (!ec && content_length > 0) { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - } - } - - template - async_simple::coro::Lazy send_sink_with_chunked(Source &source, - std::error_code &ec) { - size_t size = 0; - while (true) { - auto result = co_await source(); - std::vector bufs; - std::string size_str; - cinatra::to_chunked_buffers( - bufs, size_str, {result.buf.data(), result.buf.size()}, result.eof); - if (std::tie(ec, size) = co_await async_write(bufs); ec) { - break; - } - if (result.eof) { - break; - } - } - } - - template - async_simple::coro::Lazy send_sink_with_length(Source &source, - std::error_code &ec, - int64_t content_length) { - size_t size = 0; - while (true) { - auto result = co_await source(); - if (std::tie(ec, size) = co_await async_write(asio::buffer( - result.buf.data(), - std::min(content_length, result.buf.size()))); - ec) { - break; - } - content_length -= size; - if (content_length <= 0) { - break; - } - else if (result.eof) [[unlikely]] { - // bad request, file is smaller than content-length - ec = std::make_error_code(std::errc::invalid_argument); - break; - } - } - } - - async_simple::coro::Lazy reconnect(resp_data &data, uri_t u) { - data = co_await connect(u); - if (socket_->is_timeout_) { - data = resp_data{make_error_code(http_errc::connect_timeout), 404}; - } - if (data.net_err) { - co_return false; - } - - co_return true; - } - - void handle_upload_timeout_error(std::error_code &ec) { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (write_header_timeout_ || write_payload_timeout_ || read_timeout_) { - socket_->is_timeout_ = true; - } -#endif - if (socket_->is_timeout_) { - ec = make_error_code(http_errc::request_timeout); - } - } - - template - async_simple::coro::Lazy async_upload_impl( - S uri, http_method method, Source source /* file */, - req_content_type content_type = req_content_type::text, - std::unordered_map headers = {}, - uint64_t offset = 0 /*file offset*/, - int64_t content_length = -1 /*upload size*/) { - std::error_code ec{}; - size_t size = 0; - bool is_keep_alive = true; - req_context<> ctx{content_type}; - resp_data data{}; - - out_buf_ = {}; - - std::shared_ptr guard(nullptr, [&, this](auto) { - if (!req_headers_.empty()) { - req_headers_.clear(); - } - }); - - auto [ok, u] = handle_uri(data, uri); - if (!ok) { - co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; - } - - if constexpr (upload_type != upload_type_t::multipart) { - check_source(data, source); - if (data.status != 0) { - co_return data; - } - } - - if constexpr (upload_type == upload_type_t::with_length) { - content_length = handle_upload_header_with_length(data, source, headers, - offset, content_length); - if (data.status != 0) { - co_return data; - } - } - else if constexpr (upload_type == upload_type_t::chunked) { - handle_upload_header_with_chunked(headers); - } - else if constexpr (upload_type == upload_type_t::multipart) { - handle_upload_header_with_multipart(); - } - - std::string header_str = - build_request_header(u, method, ctx, true, std::move(headers)); - - if (socket_->has_closed_) { - if (bool r = co_await reconnect(data, u); !r) { - co_return data; - } - } - - auto time_guard = timer_guard(this, req_timeout_duration_, "request timer"); - std::tie(ec, size) = co_await async_write(asio::buffer(header_str)); - if (ec) { - handle_upload_timeout_error(ec); - co_return resp_data{ec, 404}; - } - - constexpr bool is_stream_file = is_stream_ptr_v; - if constexpr (is_stream_file) { - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_fstream_with_length(source, ec, offset, content_length); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_fstream_with_chunked(source, ec); - } - } - else if constexpr (std::is_enum_v) { // only for multipart - co_await send_fstream_with_multipart(ec); - } - else if constexpr (std::is_same_v || - std::is_same_v) { -#ifdef __linux__ -#ifdef CINATRA_ENABLE_SSL - if (!has_init_ssl_) { -#endif - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_file_no_copy_with_length(std::filesystem::path{source}, - ec, content_length, offset); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_file_no_copy_with_chunked(std::filesystem::path{source}, - ec); - } -#ifdef CINATRA_ENABLE_SSL - } - else { - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_file_copy_with_length(source, ec, content_length, - offset); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_file_copy_with_chunked(source, ec); - } - } -#endif -#else - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_file_copy_with_length(source, ec, content_length, offset); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_file_copy_with_chunked(source, ec); - } -#endif - } - else { - if constexpr (upload_type == upload_type_t::with_length) { - co_await send_sink_with_length(source, ec, content_length); - } - else if constexpr (upload_type == upload_type_t::chunked) { - co_await send_sink_with_chunked(source, ec); - } - } - if (ec) { - handle_upload_timeout_error(ec); - co_return resp_data{ec, 404}; - } - - data = co_await handle_read(ec, size, is_keep_alive, std::move(ctx), - http_method::POST); - if (ec) { - handle_upload_timeout_error(ec); - } - handle_result(data, ec, is_keep_alive); - co_return data; - } - - public: - // send file with length - template - async_simple::coro::Lazy async_upload( - S uri, http_method method, Source source /* file */, - uint64_t offset = 0 /*file offset*/, - int64_t content_length = -1 /*upload size*/, - req_content_type content_type = req_content_type::text, - std::unordered_map headers = {}) { - return async_upload_impl( - std::move(uri), method, std::move(source), content_type, - std::move(headers), offset, content_length); - } - - // send file with chunked - template - async_simple::coro::Lazy async_upload_chunked( - S uri, http_method method, Source source, - req_content_type content_type = req_content_type::text, - std::unordered_map headers = {}) { - return async_upload_impl( - std::move(uri), method, std::move(source), content_type, - std::move(headers)); - } - - // send multipart data, should call add_file_part or add_str_part firstly. - async_simple::coro::Lazy async_upload_multipart(std::string uri) { - if (form_data_.empty()) { - CINATRA_LOG_WARNING << "no multipart"; - co_return resp_data{std::make_error_code(std::errc::invalid_argument), - 404}; - } - - co_return co_await async_upload_impl( - std::move(uri), http_method::POST, upload_type_t::multipart, - req_content_type::multipart); - } - - async_simple::coro::Lazy async_upload_multipart( - std::string uri, std::string name, std::string filename) { - if (!add_file_part(std::move(name), std::move(filename))) { - CINATRA_LOG_WARNING << "open file failed or duplicate test names"; - co_return resp_data{std::make_error_code(std::errc::invalid_argument), - 404}; - } - co_return co_await async_upload_multipart(std::move(uri)); - } - - template - async_simple::coro::Lazy async_request( - S uri, http_method method, req_context ctx, - std::unordered_map headers = {}, - std::span out_buf = {}) { - if (!resp_chunk_str_.empty()) { - resp_chunk_str_.clear(); - } - if (!body_.empty()) { - body_.clear(); - } - - out_buf_ = out_buf; - - std::shared_ptr guard(nullptr, [this](auto) { - if (!req_headers_.empty()) { - req_headers_.clear(); - } - }); - - resp_data data{}; - - std::error_code ec{}; - size_t size = 0; - bool is_keep_alive = true; - - do { - uri_t u; - std::string append_uri; - - if (socket_->has_closed_ || (!uri.empty() && uri[0] != '/')) { - bool no_schema = !has_schema(uri); - - if (no_schema) { -#ifdef CINATRA_ENABLE_SSL - if (is_ssl_schema_) { - append_uri.append("https://").append(uri); - } - else -#endif - { - append_uri.append("http://").append(uri); - } - } - bool ok = false; - std::tie(ok, u) = handle_uri(data, no_schema ? append_uri : uri); - if (!ok) { - break; - } - } - else { - u.path = uri; - } - if (socket_->has_closed_) { - data = co_await connect(u); - if (data.status != 0) { - co_return data; - } - } - - std::vector vec; - std::string req_head_str = - build_request_header(u, method, ctx, false, std::move(headers)); - - bool has_body = !ctx.content.empty(); - if (has_body) { - vec.push_back(asio::buffer(req_head_str)); - vec.push_back(asio::buffer(ctx.content.data(), ctx.content.size())); - } - -#ifdef CORO_HTTP_PRINT_REQ_HEAD - CINATRA_LOG_DEBUG << req_head_str; -#endif - auto guard = timer_guard(this, req_timeout_duration_, "request timer"); - if (has_body) { - std::tie(ec, size) = co_await async_write(vec); - } - else { - std::tie(ec, size) = co_await async_write(asio::buffer(req_head_str)); - } - if (ec) { - break; - } - data = - co_await handle_read(ec, size, is_keep_alive, std::move(ctx), method); - } while (0); - if (ec && socket_->is_timeout_) { - ec = make_error_code(http_errc::request_timeout); - } - handle_result(data, ec, is_keep_alive); - co_return data; - } - - async_simple::coro::Lazy handle_shake() { -#ifdef CINATRA_ENABLE_SSL - if (!has_init_ssl_) { - bool r = init_ssl(asio::ssl::verify_none, "", host_); - if (!r) { - co_return std::make_error_code(std::errc::invalid_argument); - } - } - - if (socket_->ssl_stream_ == nullptr) { - co_return std::make_error_code(std::errc::not_a_stream); - } - - auto ec = co_await coro_io::async_handshake(socket_->ssl_stream_, - asio::ssl::stream_base::client); - if (ec) { - CINATRA_LOG_ERROR << "handle failed " << ec.message(); - } - co_return ec; -#else - // please open CINATRA_ENABLE_SSL before request https! - co_return std::make_error_code(std::errc::protocol_error); -#endif - } - -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - async_simple::coro::Lazy async_write_raw( - std::string_view data) { - auto [ec, _] = co_await async_write(asio::buffer(data)); - co_return ec; - } - - async_simple::coro::Lazy async_read_raw( - http_method method, bool clear_buffer = false) { - if (clear_buffer) { - body_.clear(); - } - - char buf[1024]; - std::error_code ec{}; - size_t size{}; -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - std::tie(ec, size) = co_await coro_io::async_read_some( - *socket_->ssl_stream_, asio::buffer(buf, 1024)); - } - else { -#endif - std::tie(ec, size) = co_await coro_io::async_read_some( - socket_->impl_, asio::buffer(buf, 1024)); -#ifdef CINATRA_ENABLE_SSL - } -#endif - body_.append(buf, size); - - co_return resp_data{ec, {}, {}, body_}; - } -#endif - - inline void set_proxy(const std::string &host, const std::string &port) { - proxy_host_ = host; - proxy_port_ = port; - } - - inline void set_proxy_basic_auth(const std::string &username, - const std::string &password) { - proxy_basic_auth_username_ = username; - proxy_basic_auth_password_ = password; - } - - inline void set_proxy_bearer_token_auth(const std::string &token) { - proxy_bearer_token_auth_token_ = token; - } - - inline void enable_auto_redirect(bool enable_follow_redirect) { - enable_follow_redirect_ = enable_follow_redirect; - } - -#ifdef CINATRA_ENABLE_SSL - void set_ssl_schema(bool r) { is_ssl_schema_ = r; } -#ifdef YLT_ENABLE_NTLS - void set_ntls_schema(bool r) { use_ntls_ = r; } - - /*! - * Initialize NTLS client with dual certificates - */ - bool init_ntls_client(const std::string &sign_cert_file, - const std::string &sign_key_file, - const std::string &enc_cert_file, - const std::string &enc_key_file, - const std::string &ca_cert_file = "", - int verify_mode = asio::ssl::verify_none, - const std::string &passwd = "") { - if (has_init_ssl_) { - return true; - } - - try { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(NTLS_client_method())); - - // Enable NTLS mode for Tongsuo - SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); - - // Set NTLS cipher suites - if (!SSL_CTX_set_cipher_list(ssl_ctx_->native_handle(), - "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { - CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; - } - - if (!passwd.empty()) { - ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { - return pwd; - }); - } - - // Load client certificates if provided (for mutual authentication) - if (!sign_cert_file.empty() && !sign_key_file.empty()) { - if (!SSL_CTX_use_sign_certificate_file(ssl_ctx_->native_handle(), - sign_cert_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client SM2 signing certificate"; - return false; - } - - if (!SSL_CTX_use_sign_PrivateKey_file(ssl_ctx_->native_handle(), - sign_key_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client SM2 signing private key"; - return false; - } - } - - if (!enc_cert_file.empty() && !enc_key_file.empty()) { - if (!SSL_CTX_use_enc_certificate_file(ssl_ctx_->native_handle(), - enc_cert_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR - << "failed to load client SM2 encryption certificate"; - return false; - } - - if (!SSL_CTX_use_enc_PrivateKey_file(ssl_ctx_->native_handle(), - enc_key_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR - << "failed to load client SM2 encryption private key"; - return false; - } - } - - // Load CA certificate if provided - if (!ca_cert_file.empty()) { - if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), - ca_cert_file.c_str(), nullptr)) { - CINATRA_LOG_WARNING << "failed to load CA certificate"; - } - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - has_init_ssl_ = true; - use_ntls_ = true; - return true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init NTLS client failed: " << e.what(); - return false; - } - } - /*! - * Initialize NTLS client with TLS 1.3 + GM single certificate mode (RFC 8998) - */ - bool init_ntls_tls13_gm_client( - const std::string &gm_cert_file = "", const std::string &gm_key_file = "", - const std::string &ca_cert_file = "", - int verify_mode = asio::ssl::verify_none, - const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", - const std::string &passwd = "") { - if (has_init_ssl_) { - return true; - } - - try { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(TLS_client_method())); - - // Configure TLS 1.3 + GM mode (RFC 8998) - SSL_CTX_set_min_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); - - // Enable strict SM TLS 1.3 mode (Tongsuo specific) - SSL_CTX_enable_sm_tls13_strict(ssl_ctx_->native_handle()); - - // Set TLS 1.3 GM cipher suites - if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), - cipher_suites.c_str())) { - CINATRA_LOG_WARNING << "failed to set TLS 1.3 GM cipher suites"; - } - - // Set GM signature algorithms (required for SM TLS 1.3 strict mode) - if (!SSL_CTX_set1_sigalgs_list(ssl_ctx_->native_handle(), "sm2sig_sm3")) { - CINATRA_LOG_WARNING << "failed to set GM signature algorithms"; - } - - // Set GM curves (required for SM TLS 1.3 strict mode) - if (!SSL_CTX_set1_curves_list(ssl_ctx_->native_handle(), "SM2")) { - CINATRA_LOG_WARNING << "failed to set GM curves"; - } - - if (!passwd.empty()) { - ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { - return pwd; - }); - } - - // Load client certificate if provided (for mutual authentication) - if (!gm_cert_file.empty() && !gm_key_file.empty()) { - if (!SSL_CTX_use_certificate_file(ssl_ctx_->native_handle(), - gm_cert_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client GM certificate"; - return false; - } - - if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_->native_handle(), - gm_key_file.c_str(), - SSL_FILETYPE_PEM)) { - CINATRA_LOG_ERROR << "failed to load client GM private key"; - return false; - } - } - - // Load CA certificate if provided - if (!ca_cert_file.empty()) { - if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), - ca_cert_file.c_str(), nullptr)) { - CINATRA_LOG_WARNING << "failed to load CA certificate"; - } - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - has_init_ssl_ = true; - use_ntls_ = true; - return true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init TLS 1.3 + GM client failed: " << e.what(); - return false; - } - } - -#endif // YLT_ENABLE_NTLS -#endif - - std::string get_redirect_uri() { return redirect_uri_; } - - bool is_redirect(resp_data &data) { - if (data.status > 299 && data.status <= 399) - return true; - return false; - } - - void set_conn_timeout(std::chrono::steady_clock::duration timeout_duration) { - conn_timeout_duration_ = timeout_duration; - } - - void set_req_timeout(std::chrono::steady_clock::duration timeout_duration) { - req_timeout_duration_ = timeout_duration; - } - - void set_chunked_callback( - std::function(std::string_view)> cb) { - chunked_cb_ = std::move(cb); - } - - bool has_chunked_callback() const { return chunked_cb_ != nullptr; } - -#ifdef CINATRA_ENABLE_SSL - void enable_sni_hostname(bool r) { need_set_sni_host_ = r; } -#endif - - template - friend class coro_io::client_pool; - - private: - struct socket_t { - asio::ip::tcp::socket impl_; - std::atomic has_closed_ = true; - std::atomic is_timeout_ = false; - asio::streambuf head_buf_; - asio::streambuf chunked_buf_; -#ifdef CINATRA_ENABLE_SSL - std::unique_ptr> ssl_stream_; -#endif - template - socket_t(ioc_t &&ioc) : impl_(std::forward(ioc)) {} - }; - static bool is_ok(const resp_data &data) noexcept { - return data.net_err == std::error_code{}; - } - - template - std::pair handle_uri(resp_data &data, const S &uri) { - uri_t u; - if (!u.parse_from(uri.data())) { - CINATRA_LOG_WARNING - << uri - << ", the url is not right, maybe need to encode the url firstly"; - data.net_err = std::make_error_code(std::errc::protocol_error); - data.status = 404; - return {false, {}}; - } - - if (u.host.front() == '[') { // for ipv6 - if (u.host.size() > 2) - u.host = u.host.substr(1, u.host.size() - 2); - } - - // construct proxy request uri - construct_proxy_uri(u); - - return {true, u}; - } - - void construct_proxy_uri(uri_t &u) { - if (!proxy_host_.empty() && !proxy_port_.empty()) { - if (!proxy_request_uri_.empty()) - proxy_request_uri_.clear(); - if (u.get_port() == "80") { - proxy_request_uri_.append("http://").append(u.get_host()).append(":80"); - } - else if (u.get_port() == "443") { - proxy_request_uri_.append("https://") - .append(u.get_host()) - .append(":443"); - } - else { - // all be http - proxy_request_uri_.append("http://") - .append(u.get_host()) - .append(":") - .append(u.get_port()); - } - proxy_request_uri_.append(u.get_path()); - u.path = std::string_view(proxy_request_uri_); - } - } - - std::string build_request_header( - const uri_t &u, http_method method, const auto &ctx, - bool already_has_len = false, - std::unordered_map headers = {}) { - std::string req_str(method_name(method)); - - req_str.append(" ").append(u.get_path()); - if (!u.query.empty()) { - req_str.append("?").append(u.query); - } - - if (!headers.empty()) { - req_headers_ = std::move(headers); - req_str.append(" HTTP/1.1\r\n"); - } - else { - if (req_headers_.find("Host") == req_headers_.end()) { - req_str.append(" HTTP/1.1\r\nHost:").append(u.host).append("\r\n"); - } - else { - req_str.append(" HTTP/1.1\r\n"); - } - } - - auto type_str = get_content_type_str(ctx.content_type); - if (!type_str.empty()) { - if (ctx.content_type == req_content_type::multipart) { - type_str.append(BOUNDARY); - } - req_headers_["Content-Type"] = std::move(type_str); - } - - bool has_connection = false; - // add user headers - if (!req_headers_.empty()) { - for (auto &pair : req_headers_) { - if (pair.first == "Connection") { - has_connection = true; - } - req_str.append(pair.first) - .append(": ") - .append(pair.second) - .append("\r\n"); - } - } - - if (!has_connection) { - req_str.append("Connection: keep-alive\r\n"); - } - - if (!proxy_basic_auth_username_.empty() && - !proxy_basic_auth_password_.empty()) { - std::string basic_auth_str = "Proxy-Authorization: Basic "; - std::string basic_base64_str = base64_encode( - proxy_basic_auth_username_ + ":" + proxy_basic_auth_password_); - req_str.append(basic_auth_str).append(basic_base64_str).append(CRCF); - } - - if (!proxy_bearer_token_auth_token_.empty()) { - std::string bearer_token_str = "Proxy-Authorization: Bearer "; - req_str.append(bearer_token_str) - .append(proxy_bearer_token_auth_token_) - .append(CRCF); - } - - if (!ctx.req_header.empty()) - req_str.append(ctx.req_header); - size_t content_len = ctx.content.size(); - bool should_add_len = false; - if (content_len > 0) { - should_add_len = true; - } - else { - if ((method == http_method::POST || method == http_method::PUT) && - ctx.content_type != req_content_type::multipart) { - should_add_len = true; - } - } - - if (req_headers_.find("Content-Length") != req_headers_.end()) { - should_add_len = false; - } - - if (already_has_len) { - should_add_len = false; - } - - if (should_add_len) { - char buf[32]; - auto [ptr, ec] = std::to_chars(buf, buf + 32, content_len); - req_str.append("Content-Length: ") - .append(std::string_view(buf, ptr - buf)) - .append("\r\n"); - } - - req_str.append("\r\n"); - return req_str; - } - - std::error_code handle_header(resp_data &data, http_parser &parser, - size_t header_size) { - // parse header - const char *data_ptr = asio::buffer_cast(head_buf_.data()); - - int parse_ret = parser.parse_response(data_ptr, header_size, 0); -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (parse_failed_forever_) { - parse_ret = -1; - } -#endif - if (parse_ret < 0) [[unlikely]] { - head_buf_.consume(head_buf_.size()); - return std::make_error_code(std::errc::protocol_error); - } - - if (parser_.body_len() > max_http_body_len_ || parser_.body_len() < 0) - [[unlikely]] { - CINATRA_LOG_ERROR << "invalid http content length: " - << parser_.body_len(); - head_buf_.consume(head_buf_.size()); - return std::make_error_code(std::errc::invalid_argument); - } - - head_buf_.consume(header_size); // header size - data.resp_headers = parser.get_headers(); - data.status = parser.status(); - return {}; - } - - template - async_simple::coro::Lazy handle_read(std::error_code &ec, - size_t &size, - bool &is_keep_alive, - req_context ctx, - http_method method) { - resp_data data{}; - do { - if (std::tie(ec, size) = co_await async_read_until(head_buf_, TWO_CRCF); - ec) { - break; - } - - ec = handle_header(data, parser_, size); - if (ec) { - break; - } - - is_keep_alive = parser_.keep_alive(); - if (method == http_method::HEAD) { - co_return data; - } - - bool is_out_buf = false; - - bool is_ranges = parser_.is_resp_ranges(); - if (is_ranges) { - is_keep_alive = true; - } - if (parser_.is_chunked()) { - out_buf_ = {}; - is_keep_alive = true; - if (head_buf_.size() > 0) { - const char *data_ptr = - asio::buffer_cast(head_buf_.data()); - chunked_buf_.sputn(data_ptr, head_buf_.size()); - head_buf_.consume(head_buf_.size()); - } - ec = co_await handle_chunked(data, std::move(ctx)); - break; - } - - if (parser_.is_multipart()) { - out_buf_ = {}; - is_keep_alive = true; - if (head_buf_.size() > 0) { - const char *data_ptr = - asio::buffer_cast(head_buf_.data()); - chunked_buf_.sputn(data_ptr, head_buf_.size()); - head_buf_.consume(head_buf_.size()); - } - ec = co_await handle_multipart(data, std::move(ctx)); - break; - } - - redirect_uri_.clear(); - bool is_redirect = parser_.is_location(); - if (is_redirect) - redirect_uri_ = parser_.get_header_value("Location"); - - if (!parser_.get_header_value("Content-Encoding").empty()) { - if (parser_.get_header_value("Content-Encoding").find("gzip") != - std::string_view::npos) - encoding_type_ = content_encoding::gzip; - else if (parser_.get_header_value("Content-Encoding").find("deflate") != - std::string_view::npos) - encoding_type_ = content_encoding::deflate; - else if (parser_.get_header_value("Content-Encoding").find("br") != - std::string_view::npos) - encoding_type_ = content_encoding::br; - } - else { - encoding_type_ = content_encoding::none; - } - - size_t content_len = (size_t)parser_.body_len(); -#ifdef BENCHMARK_TEST - total_len_ = parser_.total_len(); -#endif - - is_out_buf = !out_buf_.empty(); - if (is_out_buf) { - if (content_len > 0 && out_buf_.size() < content_len) { - out_buf_ = {}; - is_out_buf = false; - } - } - - if (content_len <= head_buf_.size()) { - // Now get entire content, additional data will discard. - // copy body. - if (content_len > 0) { - auto data_ptr = asio::buffer_cast(head_buf_.data()); - if (is_out_buf) { - memcpy(out_buf_.data(), data_ptr, content_len); - } - else { - detail::resize(body_, content_len); - memcpy(body_.data(), data_ptr, content_len); - } - head_buf_.consume(head_buf_.size()); - } - co_await handle_entire_content(data, content_len, is_ranges, ctx); - break; - } - - // read left part of content. - size_t part_size = head_buf_.size(); - size_t size_to_read = content_len - part_size; - - auto data_ptr = asio::buffer_cast(head_buf_.data()); - if (is_out_buf) { - memcpy(out_buf_.data(), data_ptr, part_size); - } - else { - detail::resize(body_, content_len); - memcpy(body_.data(), data_ptr, part_size); - } - - head_buf_.consume(part_size); - - if (is_out_buf) { - if (std::tie(ec, size) = co_await async_read( - asio::buffer(out_buf_.data() + part_size, size_to_read), - size_to_read); - ec) { - break; - } - } - else { - if (std::tie(ec, size) = co_await async_read( - asio::buffer(body_.data() + part_size, size_to_read), - size_to_read); - ec) { - break; - } - } - - // Now get entire content, additional data will discard. - co_await handle_entire_content(data, content_len, is_ranges, ctx); - } while (0); - - if (!resp_chunk_str_.empty()) { - data.resp_body = - std::string_view{resp_chunk_str_.data(), resp_chunk_str_.size()}; - } - - co_return data; - } - - async_simple::coro::Lazy handle_entire_content(resp_data &data, - size_t content_len, - bool is_ranges, - auto &ctx) { - if (content_len > 0) { - const char *data_ptr; - if (head_buf_.size() == 0) { - if (out_buf_.empty()) { - data_ptr = body_.data(); - } - else { - data_ptr = out_buf_.data(); - } - } - else { - data_ptr = asio::buffer_cast(head_buf_.data()); - } - - if (is_ranges) { - if (ctx.resp_body_stream) { - auto [ec, size] = co_await ctx.resp_body_stream->async_write( - {data_ptr, content_len}); - if (ec) { - data.net_err = ec; - co_return; - } - } - } - - std::string_view reply(data_ptr, content_len); -#ifdef CINATRA_ENABLE_GZIP - if (encoding_type_ == content_encoding::gzip) { - uncompressed_str_.clear(); - bool r = gzip_codec::uncompress(reply, uncompressed_str_); - if (r) - data.resp_body = uncompressed_str_; - else - data.resp_body = reply; - } - else if (encoding_type_ == content_encoding::deflate) { - uncompressed_str_.clear(); - bool r = gzip_codec::inflate(reply, uncompressed_str_); - if (r) - data.resp_body = uncompressed_str_; - else - data.resp_body = reply; - } -#endif -#if defined(CINATRA_ENABLE_BROTLI) && defined(CINATRA_ENABLE_GZIP) - else if (encoding_type_ == content_encoding::br) -#endif -#if defined(CINATRA_ENABLE_BROTLI) && !defined(CINATRA_ENABLE_GZIP) - if (encoding_type_ == content_encoding::br) -#endif -#ifdef CINATRA_ENABLE_BROTLI - { - uncompressed_str_.clear(); - bool r = br_codec::brotli_decompress(reply, uncompressed_str_); - if (r) - data.resp_body = uncompressed_str_; - else - data.resp_body = reply; - } -#endif -#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) - else -#endif - data.resp_body = reply; - - head_buf_.consume(content_len); - } - data.eof = (head_buf_.size() == 0); - } - - void handle_result(resp_data &data, std::error_code ec, bool is_keep_alive) { - if (ec) { - close_socket(*socket_); - data.net_err = ec; - data.status = 404; -#ifdef BENCHMARK_TEST - if (!stop_bench_) { - CINATRA_LOG_DEBUG << ec.message(); - } -#endif - } - else { - if (!is_keep_alive) { - close_socket(*socket_); - } - } - } - - template - async_simple::coro::Lazy handle_multipart( - resp_data &data, req_context ctx) { - std::error_code ec{}; - std::string boundary = std::string{parser_.get_boundary()}; - multipart_reader_t multipart(this); - while (true) { - auto part_head = co_await multipart.read_part_head(boundary); - if (part_head.ec) { - co_return part_head.ec; - } - - auto part_body = co_await multipart.read_part_body(boundary); - - if (ctx.resp_body_stream) { - size_t size; - std::tie(ec, size) = - co_await ctx.resp_body_stream->async_write(part_body.data); - } - else { - resp_chunk_str_.append(part_body.data.data(), part_body.data.size()); - } - - if (part_body.ec) { - co_return part_body.ec; - } - - if (part_body.eof) { - break; - } - } - co_return ec; - } - - template - async_simple::coro::Lazy handle_chunked( - resp_data &data, req_context ctx) { - std::error_code ec{}; - size_t size = 0; - while (true) { - if (std::tie(ec, size) = co_await async_read_until(chunked_buf_, CRCF); - ec) { - break; - } - - size_t buf_size = chunked_buf_.size(); - size_t additional_size = buf_size - size; - const char *data_ptr = - asio::buffer_cast(chunked_buf_.data()); - std::string_view size_str(data_ptr, size - CRCF.size()); - auto chunk_size = hex_to_int(size_str); - chunked_buf_.consume(size); - if (chunk_size < 0) { - CINATRA_LOG_DEBUG << "bad chunked size"; - ec = asio::error::make_error_code( - asio::error::basic_errors::invalid_argument); - break; - } - - if (additional_size < size_t(chunk_size + 2)) { - // not a complete chunk, read left chunk data. - size_t size_to_read = chunk_size + 2 - additional_size; - if (std::tie(ec, size) = - co_await async_read(chunked_buf_, size_to_read); - ec) { - break; - } - } - - if (chunk_size == 0) { - // all finished, no more data - chunked_buf_.consume(chunked_buf_.size()); - data.eof = true; - break; - } - - data_ptr = asio::buffer_cast(chunked_buf_.data()); - if (chunked_cb_) { - co_await chunked_cb_(std::string_view(data_ptr, chunk_size)); - } - else { - if (ctx.resp_body_stream) { - std::tie(ec, size) = co_await ctx.resp_body_stream->async_write( - {data_ptr, (size_t)chunk_size}); - } - else { - resp_chunk_str_.append(data_ptr, chunk_size); - } - } - - chunked_buf_.consume(chunk_size + CRCF.size()); - } - co_return ec; - } - - async_simple::coro::Lazy connect( - const uri_t &u, std::vector *eps = nullptr) { - std::vector eps_tmp; - if (eps == nullptr) { - eps = &eps_tmp; - } - if (should_reset_) { - reset(); - } - else { - should_reset_ = true; - } - if (socket_->has_closed_) { - auto time_out_guard = - timer_guard(this, conn_timeout_duration_, "connect timer"); - socket_->is_timeout_ = false; - host_ = proxy_host_.empty() ? u.get_host() : proxy_host_; - port_ = proxy_port_.empty() ? u.get_port() : proxy_port_; - if (eps->empty()) { - CINATRA_LOG_TRACE << "start resolve host: " << host_ << ":" << port_; - auto [ec, iter] = co_await coro_io::async_resolve( - &executor_wrapper_, socket_->impl_, host_, port_); - if (ec) { - co_return resp_data{ec, 404}; - } - else { - asio::ip::tcp::resolver::iterator end; - while (iter != end) { - eps->push_back(iter->endpoint()); - ++iter; - } - if (eps->empty()) [[unlikely]] { - co_return resp_data{std::make_error_code(std::errc::not_connected), - 404}; - } - } - } - CINATRA_LOG_TRACE - << "start connect to endpoint lists. total endpoint count:" - << eps->size() - << ", the first endpoint is: " << (*eps)[0].address().to_string() - << ":" << std::to_string((*eps)[0].port()); - std::error_code ec; - if (ec = co_await coro_io::async_connect(socket_->impl_, *eps); ec) { - co_return resp_data{ec, 404}; - } -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (connect_timeout_forever_) { - socket_->is_timeout_ = true; - } -#endif - if (socket_->is_timeout_) { - ec = make_error_code(http_errc::connect_timeout); - co_return resp_data{ec, 404}; - } - - if (enable_tcp_no_delay_) { - socket_->impl_.set_option(asio::ip::tcp::no_delay(true), ec); - if (ec) { - co_return resp_data{ec, 404}; - } - } - - if (u.is_ssl) { -#ifdef CINATRA_ENABLE_SSL - if (!has_init_ssl_) { - size_t pos = u.host.find("www."); - std::string host; - if (pos != std::string_view::npos) { - host = std::string{u.host.substr(pos + 4)}; - } - else { - host = std::string{u.host}; - } - bool r = init_ssl(asio::ssl::verify_none, "", host); - if (!r) { - co_return resp_data{ - std::make_error_code(std::errc::invalid_argument), 404}; - } - } -#endif - if (ec = co_await handle_shake(); ec) { - co_return resp_data{ec, 404}; - } - } - socket_->has_closed_ = false; - CINATRA_LOG_TRACE - << "connect to endpoint: " - << socket_->impl_.remote_endpoint().address().to_string() << ":" - << socket_->impl_.remote_endpoint().port() << " successfully"; - } - co_return resp_data{}; - } - - size_t multipart_content_len() { - size_t content_len = 0; - for (auto &[key, part] : form_data_) { - content_len += 75; - content_len += key.size() + 1; - if (!part.filename.empty()) { - content_len += (12 + part.filename.size() + 1); - auto ext = std::filesystem::path(part.filename).extension().string(); - if (auto it = g_content_type_map.find(ext); - it != g_content_type_map.end()) { - content_len += (14 + it->second.size()); - } - } - - content_len += 4; - - content_len += (part.size + 2); - } - content_len += (6 + BOUNDARY.size()); - return content_len; - } - - async_simple::coro::Lazy send_single_part( - const std::string &key, const multipart_t &part) { - std::string part_content_head; - part_content_head.append("--").append(BOUNDARY).append(CRCF); - part_content_head.append("Content-Disposition: form-data; name=\""); - part_content_head.append(key).append("\""); - bool is_file = !part.filename.empty(); - std::string short_name = - std::filesystem::path(part.filename).filename().string(); - if (is_file) { - part_content_head.append("; filename=\"") - .append(short_name) - .append("\"") - .append(CRCF); - auto ext = std::filesystem::path(short_name).extension().string(); - if (auto it = g_content_type_map.find(ext); - it != g_content_type_map.end()) { - part_content_head.append("Content-Type: ") - .append(it->second) - .append(CRCF); - } - - part_content_head.append(CRCF); - } - else { - part_content_head.append(TWO_CRCF); - } - if (auto [ec, size] = co_await async_write(asio::buffer(part_content_head)); - ec) { - co_return resp_data{ec, 404}; - } - - if (is_file) { - coro_io::coro_file file{}; - file.open(part.filename, std::ios::in); - assert(file.is_open()); - std::string file_data; - detail::resize(file_data, max_single_part_size_); - while (true) { - auto [rd_ec, rd_size] = - co_await file.async_read(file_data.data(), file_data.size()); - if (auto [ec, size] = - co_await async_write(asio::buffer(file_data.data(), rd_size)); - ec) { - co_return resp_data{ec, 404}; - } - if (file.eof()) { - if (auto [ec, size] = co_await async_write(asio::buffer(CRCF)); ec) { - co_return resp_data{ec, 404}; - } - break; - } - } - } - else { - std::array arr{asio::buffer(part.content), - asio::buffer(CRCF)}; - if (auto [ec, size] = co_await async_write(arr); ec) { - co_return resp_data{ec, 404}; - } - } - - co_return resp_data{{}, 200}; - } - - async_simple::coro::Lazy async_read_ws() { - resp_data data{}; - - head_buf_.consume(head_buf_.size()); - std::shared_ptr sock = socket_; - asio::streambuf &read_buf = sock->head_buf_; - bool has_init_ssl = false; -#ifdef CINATRA_ENABLE_SSL - has_init_ssl = has_init_ssl_; -#endif - websocket ws{}; - while (true) { - if (auto [ec, _] = co_await async_read_ws( - sock, read_buf, ws.left_header_len(), has_init_ssl); - ec) { - if (socket_->is_timeout_) { - co_return resp_data{make_error_code(http_errc::request_timeout), 404}; - } - data.net_err = ec; - data.status = 404; - - if (sock->has_closed_) { - co_return data; - } - - close_socket(*sock); - co_return data; - } - - const char *data_ptr = asio::buffer_cast(read_buf.data()); - auto ret = ws.parse_header(data_ptr, read_buf.size(), false); - if (ret == ws_header_status::incomplete) { - continue; - } - else if (ret == ws_header_status::error) { - data.net_err = std::make_error_code(std::errc::protocol_error); - data.status = 404; - close_socket(*sock); - co_return data; - } - - frame_header *header = (frame_header *)data_ptr; - bool is_close_frame = header->opcode == opcode::close; - - read_buf.consume(read_buf.size()); - - size_t payload_len = ws.payload_length(); - - if (auto [ec, size] = - co_await async_read_ws(sock, read_buf, payload_len, has_init_ssl); - ec) { - data.net_err = ec; - data.status = 404; - close_socket(*sock); - co_return data; - } - - data_ptr = asio::buffer_cast(read_buf.data()); -#ifdef CINATRA_ENABLE_GZIP - if (is_server_support_ws_deflate_ && enable_ws_deflate_) { - inflate_str_.clear(); - if (!cinatra::gzip_codec::inflate({data_ptr, payload_len}, - inflate_str_)) { - CINATRA_LOG_ERROR << "uncompuress data error"; - data.status = 404; - data.net_err = std::make_error_code(std::errc::protocol_error); - co_return data; - } - data_ptr = inflate_str_.data(); - payload_len = inflate_str_.length(); - } -#endif - if (is_close_frame) { - if (payload_len >= 2) { - payload_len -= 2; - data_ptr += sizeof(uint16_t); - } - } - data.status = 200; - data.resp_body = {data_ptr, payload_len}; - - read_buf.consume(read_buf.size()); - - if (is_close_frame) { - std::string reason = "close"; - auto close_str = ws.format_close_payload(close_code::normal, - reason.data(), reason.size()); - auto span = std::span(close_str); - auto encode_header = ws.encode_frame(span, opcode::close, true); - std::vector buffers{asio::buffer(encode_header), - asio::buffer(reason)}; - - co_await async_write_ws(sock, buffers, has_init_ssl); - - close_socket(*sock); - - data.net_err = asio::error::eof; - data.status = 404; - co_return data; - } - co_return data; - } - } - - template - async_simple::coro::Lazy> async_read_ws( - auto sock, AsioBuffer &&buffer, size_t size_to_read, - bool has_init_ssl = false) noexcept { -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl) { - return coro_io::async_read(*sock->ssl_stream_, buffer, size_to_read); - } - else { -#endif - return coro_io::async_read(sock->impl_, buffer, size_to_read); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - template - async_simple::coro::Lazy> async_write_ws( - auto sock, AsioBuffer &&buffer, bool has_init_ssl = false) { -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl) { - return coro_io::async_write(*sock->ssl_stream_, buffer); - } - else { -#endif - return coro_io::async_write(sock->impl_, buffer); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - template - async_simple::coro::Lazy> async_read( - AsioBuffer &&buffer, size_t size_to_read) noexcept { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (read_failed_forever_) { - return async_read_failed(); - } -#endif -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - return coro_io::async_read(*socket_->ssl_stream_, buffer, size_to_read); - } - else { -#endif - return coro_io::async_read(socket_->impl_, buffer, size_to_read); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - async_simple::coro::Lazy> - async_write_failed() { - co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); - } - - async_simple::coro::Lazy> - async_read_failed() { - co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); - } -#endif - - template - async_simple::coro::Lazy> async_write( - AsioBuffer &&buffer) { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (write_failed_forever_) { - return async_write_failed(); - } -#endif -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - return coro_io::async_write(*socket_->ssl_stream_, buffer); - } - else { -#endif - return coro_io::async_write(socket_->impl_, buffer); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - template - async_simple::coro::Lazy> async_read_until( - AsioBuffer &buffer, asio::string_view delim) noexcept { -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - if (read_failed_forever_) { - return async_read_failed(); - } -#endif -#ifdef CINATRA_ENABLE_SSL - if (has_init_ssl_) { - return coro_io::async_read_until(*socket_->ssl_stream_, buffer, delim); - } - else { -#endif - return coro_io::async_read_until(socket_->impl_, buffer, delim); -#ifdef CINATRA_ENABLE_SSL - } -#endif - } - - static void close_socket(socket_t &socket) { - std::error_code ec; - socket.impl_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); - socket.impl_.close(ec); - socket.has_closed_ = true; - } - - async_simple::coro::Lazy timeout( - auto &timer, std::chrono::steady_clock::duration duration, - std::string msg) { - auto watcher = std::weak_ptr(socket_); - timer.expires_after(duration); - auto is_timeout = co_await timer.async_await(); - if (!is_timeout) { - co_return false; - } - if (auto socket = watcher.lock(); socket) { - socket_->is_timeout_ = true; - CINATRA_LOG_WARNING << msg << " timeout"; - close_socket(*socket_); - } - co_return true; - } - - template - bool has_schema(const S &url) { - size_t pos_http = url.find("http://"); - size_t pos_https = url.find("https://"); - size_t pos_ws = url.find("ws://"); - size_t pos_wss = url.find("wss://"); - bool has_http_scheme = - ((pos_http != std::string::npos) && pos_http == 0) || - ((pos_https != std::string::npos) && pos_https == 0) || - ((pos_ws != std::string::npos) && pos_ws == 0) || - ((pos_wss != std::string::npos) && pos_wss == 0); - return has_http_scheme; - } - - friend class multipart_reader_t; - http_parser parser_; - coro_io::ExecutorWrapper<> executor_wrapper_; - coro_io::period_timer timer_; - std::shared_ptr socket_; - asio::streambuf &head_buf_; - asio::streambuf &chunked_buf_; - std::string body_; - - std::unordered_map req_headers_; - - std::string proxy_request_uri_ = ""; - std::string proxy_host_; - std::string proxy_port_; - - std::string proxy_basic_auth_username_; - std::string proxy_basic_auth_password_; - - std::string proxy_bearer_token_auth_token_; - - std::map form_data_; - size_t max_single_part_size_ = 1024 * 1024; - - std::string ws_sec_key_; - std::string host_; - std::string port_; - -#ifdef CINATRA_ENABLE_SSL - std::unique_ptr ssl_ctx_ = nullptr; - bool has_init_ssl_ = false; - bool is_ssl_schema_ = false; -#ifdef YLT_ENABLE_NTLS - bool use_ntls_ = true; -#endif // YLT_ENABLE_NTLS - bool need_set_sni_host_ = true; -#endif - std::string redirect_uri_; - bool enable_follow_redirect_ = false; - std::chrono::steady_clock::duration conn_timeout_duration_ = - std::chrono::seconds(30); - std::chrono::steady_clock::duration req_timeout_duration_ = - std::chrono::seconds(60); - std::chrono::steady_clock::time_point create_tp_; - bool enable_tcp_no_delay_ = true; - std::string resp_chunk_str_; - std::span out_buf_; - bool should_reset_ = false; - config config_; - - bool enable_ws_deflate_ = false; -#ifdef CINATRA_ENABLE_GZIP - bool is_server_support_ws_deflate_ = false; - std::string inflate_str_; -#endif - content_encoding encoding_type_ = content_encoding::none; - int64_t max_http_body_len_ = - INT64_MAX; // in default we don't limit http body len - - std::function(std::string_view)> chunked_cb_; -#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) - std::string uncompressed_str_; -#endif - -#ifdef BENCHMARK_TEST - bool stop_bench_ = false; - size_t total_len_ = 0; -#endif -#ifdef INJECT_FOR_HTTP_CLIENT_TEST - public: - bool write_failed_forever_ = false; - bool connect_timeout_forever_ = false; - bool parse_failed_forever_ = false; - bool read_failed_forever_ = false; - bool write_header_timeout_ = false; - bool write_payload_timeout_ = false; - bool read_timeout_ = false; -#endif -}; - -} // namespace cinatra +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "asio/dispatch.hpp" +#include "asio/error.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/streambuf.hpp" +#include "async_simple/Future.h" +#include "async_simple/Unit.h" +#include "async_simple/coro/FutureAwaiter.h" +#include "async_simple/coro/Lazy.h" +#ifdef CINATRA_ENABLE_GZIP +#include "gzip.hpp" +#endif +#ifdef CINATRA_ENABLE_BROTLI +#include "brzip.hpp" +#endif +#include "cinatra_log_wrapper.hpp" +#include "error.hpp" +#include "http_parser.hpp" +#include "multipart.hpp" +#include "picohttpparser.h" +#include "response_cv.hpp" +#include "string_resize.hpp" +#include "uri.hpp" +#include "websocket.hpp" +#include "ylt/coro_io/coro_file.hpp" +#include "ylt/coro_io/coro_io.hpp" +#include "ylt/coro_io/io_context_pool.hpp" + +namespace coro_io { +template +class client_pool; +} +namespace cinatra { +template +struct is_stream : std::false_type {}; + +template +struct is_stream< + T, std::void_t().read(nullptr, 0), + std::declval().async_read(nullptr, 0))>> + : std::true_type {}; + +template +constexpr bool is_stream_v = is_stream::value; + +template +struct is_span : std::false_type {}; + +template +struct is_span().data(), + std::declval().size())>> + : std::true_type {}; + +template +constexpr bool is_span_v = is_span::value; + +template +struct is_smart_ptr : std::false_type {}; + +template +struct is_smart_ptr< + T, std::void_t().get(), *std::declval(), + is_stream_v)>> + : std::true_type {}; + +template +constexpr bool is_stream_ptr_v = is_smart_ptr::value || std::is_pointer_v; + +struct http_header; + +struct resp_data { + std::error_code net_err; + int status = 0; + bool eof = false; + std::string_view resp_body; + std::span resp_headers; +#ifdef BENCHMARK_TEST + uint64_t total = 0; +#endif +}; + +template +struct req_context { + req_content_type content_type = req_content_type::none; + std::string req_header; /*header string*/ + String content; /*body*/ + coro_io::coro_file *resp_body_stream = nullptr; +}; + +struct multipart_t { + std::string filename; + std::string content; + size_t size = 0; +}; + +struct read_result { + std::span buf; + bool eof; + std::error_code err; +}; + +enum class upload_type_t { with_length, chunked, multipart }; + +class coro_http_client : public std::enable_shared_from_this { + public: + struct config { + std::optional conn_timeout_duration; + std::optional req_timeout_duration; + std::string sec_key; + size_t max_single_part_size; + std::string proxy_host; + std::string proxy_port; + std::string proxy_auth_username; + std::string proxy_auth_passwd; + std::string proxy_auth_token; + bool enable_tcp_no_delay; +#ifdef CINATRA_ENABLE_SSL + bool use_ssl = + false; // if set use_ssl true, cinatra will add https automaticlly. +#ifdef YLT_ENABLE_NTLS + bool use_ntls = + false; // if set use_ntls true, cinatra will use NTLS/TLCP protocol. +#endif // YLT_ENABLE_NTLS +#endif + }; + + coro_http_client(asio::io_context::executor_type executor) + : executor_wrapper_(executor), + timer_(&executor_wrapper_), + socket_(std::make_shared(executor)), + head_buf_(socket_->head_buf_), + chunked_buf_(socket_->chunked_buf_), + create_tp_(std::chrono::steady_clock::now()) {} + + coro_http_client( + coro_io::ExecutorWrapper<> *executor = coro_io::get_global_executor()) + : coro_http_client(executor->get_asio_executor()) {} + + bool init_config(const config &conf) { + config_ = conf; + if (conf.conn_timeout_duration.has_value()) { + set_conn_timeout(*conf.conn_timeout_duration); + } + if (conf.req_timeout_duration.has_value()) { + set_req_timeout(*conf.req_timeout_duration); + } + if (!conf.sec_key.empty()) { + set_ws_sec_key(conf.sec_key); + } + if (conf.max_single_part_size > 0) { + set_max_single_part_size(conf.max_single_part_size); + } + if (!conf.proxy_host.empty()) { + set_proxy_basic_auth(conf.proxy_host, conf.proxy_port); + } + if (!conf.proxy_auth_username.empty()) { + set_proxy_basic_auth(conf.proxy_auth_username, conf.proxy_auth_passwd); + } + if (!conf.proxy_auth_token.empty()) { + set_proxy_bearer_token_auth(conf.proxy_auth_token); + } + if (conf.enable_tcp_no_delay) { + enable_tcp_no_delay_ = conf.enable_tcp_no_delay; + } +#ifdef CINATRA_ENABLE_SSL + set_ssl_schema(conf.use_ssl); +#ifdef YLT_ENABLE_NTLS + set_ntls_schema(conf.use_ntls); +#endif // YLT_ENABLE_NTLS +#endif + return true; + } + + ~coro_http_client() { close(); } + + auto get_create_time_point() const noexcept { + return std::chrono::steady_clock::now(); + } + + void close() { + if (socket_ == nullptr || socket_->has_closed_) + return; + + asio::dispatch(executor_wrapper_.get_asio_executor(), [socket = socket_] { + close_socket(*socket); + }); + } + + coro_io::ExecutorWrapper<> &get_executor() { return executor_wrapper_; } + + const config &get_config() { return config_; } + +#ifdef CINATRA_ENABLE_SSL + bool init_ssl(int verify_mode, const std::string &base_path, + const std::string &cert_file, const std::string &sni_hostname) { + if (has_init_ssl_) { + return true; + } + + try { +#ifdef YLT_ENABLE_NTLS + if (use_ntls_) { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + + // Set NTLS cipher suites + if (!SSL_CTX_set_cipher_list( + ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + } + else { + CINATRA_LOG_ERROR << "NTLS is not supported in this build."; + return false; + } +#else + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); +#endif // YLT_ENABLE_NTLS + auto full_cert_file = std::filesystem::path(base_path).append(cert_file); + if (std::filesystem::exists(full_cert_file)) { + ssl_ctx_->load_verify_file(full_cert_file.string()); + } + else { + if (!base_path.empty() || !cert_file.empty()) + return false; + } + + if (base_path.empty() && cert_file.empty()) { + ssl_ctx_->set_default_verify_paths(); + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + // ssl_ctx_.add_certificate_authority(asio::buffer(CA_PEM)); + if (!sni_hostname.empty()) { + ssl_ctx_->set_verify_callback( + asio::ssl::host_name_verification(sni_hostname)); + + if (need_set_sni_host_) { + // Set SNI Hostname (many hosts need this to handshake successfully) + SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), + sni_hostname.c_str()); + } + } + + has_init_ssl_ = true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); + return false; + } + return true; + } + + [[nodiscard]] bool init_ssl(int verify_mode = asio::ssl::verify_none, + std::string full_path = "", + const std::string &sni_hostname = "") { + std::string base_path; + std::string cert_file; + if (full_path.empty()) { + base_path = ""; + cert_file = ""; + } + else { + base_path = full_path.substr(0, full_path.find_last_of('/')); + cert_file = full_path.substr(full_path.find_last_of('/') + 1); + } + return init_ssl(verify_mode, base_path, cert_file, sni_hostname); + } + + /*! + * Initialize SSL with mutual authentication support + * @param verify_mode SSL verify mode + * @param base_path Base path for certificate files + * @param cert_file CA certificate file name for server verification + * @param client_cert_file Client certificate file name for mutual + * authentication + * @param client_key_file Client private key file name for mutual + * authentication + * @param sni_hostname SNI hostname + * @return true if initialization successful + */ + bool init_ssl(int verify_mode, const std::string &base_path, + const std::string &cert_file, + const std::string &client_cert_file, + const std::string &client_key_file, + const std::string &sni_hostname = "") { + if (has_init_ssl_) { + return true; + } + + try { +#ifdef YLT_ENABLE_NTLS + if (use_ntls_) { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + if (!SSL_CTX_set_cipher_list( + ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + } + else { + CINATRA_LOG_ERROR << "NTLS is not supported in this build."; + return false; + } +#else + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); +#endif + auto full_cert_file = std::filesystem::path(base_path).append(cert_file); + if (std::filesystem::exists(full_cert_file)) { + ssl_ctx_->load_verify_file(full_cert_file.string()); + } + else { + if (!base_path.empty() || !cert_file.empty()) + return false; + } + + if (base_path.empty() && cert_file.empty()) { + ssl_ctx_->set_default_verify_paths(); + } + + // Load client certificate and key for mutual authentication + auto full_client_cert_file = + std::filesystem::path(base_path).append(client_cert_file); + auto full_client_key_file = + std::filesystem::path(base_path).append(client_key_file); + + if (std::filesystem::exists(full_client_cert_file)) { + ssl_ctx_->use_certificate_chain_file(full_client_cert_file.string()); + CINATRA_LOG_INFO << "loaded client certificate: " + << full_client_cert_file.string(); + } + else { + CINATRA_LOG_ERROR << "client certificate file not found: " + << full_client_cert_file.string(); + return false; + } + + if (std::filesystem::exists(full_client_key_file)) { + ssl_ctx_->use_private_key_file(full_client_key_file.string(), + asio::ssl::context::pem); + CINATRA_LOG_INFO << "loaded client private key: " + << full_client_key_file.string(); + } + else { + CINATRA_LOG_ERROR << "client key file not found: " + << full_client_key_file.string(); + return false; + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + if (!sni_hostname.empty()) { + ssl_ctx_->set_verify_callback( + asio::ssl::host_name_verification(sni_hostname)); + if (need_set_sni_host_) { + SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), + sni_hostname.c_str()); + } + } + + has_init_ssl_ = true; + is_ssl_schema_ = true; + CINATRA_LOG_INFO << "SSL initialized with client certificate for mutual " + "authentication"; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); + return false; + } + return true; + } + + /*! + * Initialize SSL with CA certificate for server verification + * @param verify_mode SSL verify mode + * @param base_path Base path for certificate files + * @param cert_file CA certificate file name + * @param sni_hostname SNI hostname + * @return true if initialization successful + */ + [[nodiscard]] bool init_ssl(int verify_mode, const std::string &base_path, + const std::string &cert_file, + const std::string &sni_hostname) { + if (has_init_ssl_) { + return true; + } + + try { +#ifdef YLT_ENABLE_NTLS + if (use_ntls_) { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + if (!SSL_CTX_set_cipher_list( + ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + } + else { + CINATRA_LOG_ERROR << "NTLS is not supported in this build."; + return false; + } +#else + ssl_ctx_ = + std::make_unique(asio::ssl::context::sslv23); +#endif + auto full_cert_file = std::filesystem::path(base_path).append(cert_file); + if (std::filesystem::exists(full_cert_file)) { + ssl_ctx_->load_verify_file(full_cert_file.string()); + } + else { + if (!base_path.empty() || !cert_file.empty()) + return false; + } + + if (base_path.empty() && cert_file.empty()) { + ssl_ctx_->set_default_verify_paths(); + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + if (!sni_hostname.empty()) { + ssl_ctx_->set_verify_callback( + asio::ssl::host_name_verification(sni_hostname)); + if (need_set_sni_host_) { + SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), + sni_hostname.c_str()); + } + } + + has_init_ssl_ = true; + is_ssl_schema_ = true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); + return false; + } + return true; + } +#endif + + // return body_, the user will own body's lifetime. + std::string release_buf() { + if (body_.empty()) { + return std::move(resp_chunk_str_); + } + return std::move(body_); + } + +#ifdef CINATRA_ENABLE_GZIP + void set_ws_deflate(bool enable_ws_deflate) { + enable_ws_deflate_ = enable_ws_deflate; + } +#endif + + /*! + * Connect server + * + * only make socket connet(or handshake) to the host + * + * @param uri server address + * @param eps endpoints of resolve result. if eps is not nullptr and vector is + * empty, it will return the endpoints that, else if vector is not empty, it + * will use the eps to skill resolve and connect to server directly. + * @return resp_data + */ + async_simple::coro::Lazy connect( + std::string uri, std::vector *eps = nullptr) { + resp_data data{}; + bool no_schema = !has_schema(uri); + std::string append_uri; + if (no_schema) { +#ifdef CINATRA_ENABLE_SSL + if (is_ssl_schema_) + append_uri.append("https://").append(uri); + else +#endif + append_uri.append("http://").append(uri); + } + + auto [ok, u] = handle_uri(data, no_schema ? append_uri : uri); + if (!ok) { + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; + } + { + if (u.is_websocket()) { + // build websocket http header + add_header("Upgrade", "websocket"); + add_header("Connection", "Upgrade"); + if (ws_sec_key_.empty()) { + ws_sec_key_ = "s//GYHa/XO7Hd2F2eOGfyA=="; // provide a random string. + } + add_header("Sec-WebSocket-Key", ws_sec_key_); + add_header("Sec-WebSocket-Version", "13"); +#ifdef CINATRA_ENABLE_GZIP + if (enable_ws_deflate_) + add_header("Sec-WebSocket-Extensions", + "permessage-deflate; client_max_window_bits"); +#endif + req_context<> ctx{}; + data = co_await async_request(std::move(uri), http_method::GET, + std::move(ctx)); + +#ifdef CINATRA_ENABLE_GZIP + if (enable_ws_deflate_) { + for (auto c : data.resp_headers) { + if (c.name == "Sec-WebSocket-Extensions") { + if (c.value.find("permessage-deflate;") != std::string::npos) { + is_server_support_ws_deflate_ = true; + } + else { + is_server_support_ws_deflate_ = false; + } + break; + } + } + } +#endif + co_return data; + } + data = co_await connect(u, eps); + } + if (socket_->is_timeout_) { + co_return resp_data{make_error_code(http_errc::connect_timeout), 404}; + } + if (!data.net_err) { + data.status = 200; + } + co_return data; + } + + bool has_closed() { return socket_->has_closed_; } + + [[nodiscard]] std::size_t get_pipeline_size() const noexcept { return 0; } + + const auto &get_headers() { return req_headers_; } + + void set_headers(std::unordered_map req_headers) { + req_headers_ = std::move(req_headers); + } + + bool add_header(std::string key, std::string val) { + if (key.empty()) + return false; + + req_headers_[key] = std::move(val); + + return true; + } + + void set_ws_sec_key(std::string sec_key) { ws_sec_key_ = std::move(sec_key); } + + /** + * @brief Set the max http body size object, in default it's INT64_MAX + * + * @param max_size + */ + void set_max_http_body_size(int64_t max_size) { + max_http_body_len_ = max_size; + } + + size_t available() { + std::error_code ec{}; + return socket_->impl_.available(ec); + } + + async_simple::coro::Lazy read_websocket() { + auto time_out_guard = + timer_guard(this, req_timeout_duration_, "websocket timer"); + co_return co_await async_read_ws(); + } + + async_simple::coro::Lazy write_websocket( + const char *data, opcode op = opcode::text) { + std::string str(data); + co_return co_await write_websocket(str, op); + } + + async_simple::coro::Lazy write_websocket( + const char *data, size_t size, opcode op = opcode::text) { + std::string str(data, size); + co_return co_await write_websocket(str, op); + } + + async_simple::coro::Lazy write_websocket( + std::string_view data, opcode op = opcode::text) { + std::string str(data); + co_return co_await write_websocket(str, op); + } + + async_simple::coro::Lazy write_websocket( + std::string &data, opcode op = opcode::text) { + co_return co_await write_websocket(std::span(data), op); + } + + async_simple::coro::Lazy write_websocket( + std::string &&data, opcode op = opcode::text) { + co_return co_await write_websocket(std::span(data), op); + } + + async_simple::coro::Lazy write_ws_frame(std::span msg, + websocket ws, opcode op, + resp_data &data, + bool eof = true) { + auto header = ws.encode_frame(msg, op, eof, enable_ws_deflate_); + std::vector buffers{ + asio::buffer(header), asio::buffer(msg.data(), msg.size())}; + + auto [ec, sz] = co_await async_write(buffers); + if (ec) { + data.net_err = ec; + data.status = 404; + } + } + +#ifdef CINATRA_ENABLE_GZIP + void gzip_compress(std::string_view source, std::string &dest_buf, + std::span &span, resp_data &data) { + if (enable_ws_deflate_ && is_server_support_ws_deflate_) { + if (cinatra::gzip_codec::deflate(source, dest_buf)) { + span = dest_buf; + } + else { + CINATRA_LOG_ERROR << "compress data error, data: " << source; + data.net_err = std::make_error_code(std::errc::protocol_error); + data.status = 404; + } + } + } +#endif + + template + async_simple::coro::Lazy write_websocket( + Source source, opcode op = opcode::text) { + resp_data data{}; + + websocket ws{}; + std::string close_str; + if (op == opcode::close) { + if constexpr (is_span_v) { + close_str = ws.format_close_payload(close_code::normal, source.data(), + source.size()); + source = {close_str.data(), close_str.size()}; + } + } + + std::span span{}; + if constexpr (is_span_v) { + span = {source.data(), source.size()}; +#ifdef CINATRA_ENABLE_GZIP + std::string dest_buf; + if (enable_ws_deflate_) { + gzip_compress({source.data(), source.size()}, dest_buf, span, data); + } +#endif + co_await write_ws_frame(span, ws, op, data, true); + } + else { + while (true) { + auto result = co_await source(); + span = {result.buf.data(), result.buf.size()}; +#ifdef CINATRA_ENABLE_GZIP + std::string dest_buf; + if (enable_ws_deflate_) { + gzip_compress({result.buf.data(), result.buf.size()}, dest_buf, span, + data); + } +#endif + co_await write_ws_frame(span, ws, op, data, result.eof); + + if (result.eof || data.status == 404) { + break; + } + } + } + + co_return data; + } + + async_simple::coro::Lazy write_websocket_close( + std::string msg = "") { + co_return co_await write_websocket(std::move(msg), opcode::close); + } + +#ifdef BENCHMARK_TEST + void set_bench_stop() { stop_bench_ = true; } +#endif + + async_simple::coro::Lazy async_patch( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::PATCH, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_options( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::OPTIONS, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_trace( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::TRACE, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_head( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::HEAD, + cinatra::req_context<>{}, std::move(headers)); + } + + // CONNECT example.com HTTP/1.1 + async_simple::coro::Lazy async_http_connect( + std::string uri, + std::unordered_map headers = {}) { + return async_request(std::move(uri), cinatra::http_method::CONNECT, + cinatra::req_context<>{}, std::move(headers)); + } + + async_simple::coro::Lazy async_get( + std::string uri, + std::unordered_map headers = {}) { + resp_data data{}; + req_context<> ctx{}; + data = co_await async_request(std::move(uri), http_method::GET, + std::move(ctx), std::move(headers)); +#ifdef BENCHMARK_TEST + data.total = total_len_; +#endif + if (redirect_uri_.empty() || !is_redirect(data)) { + co_return data; + } + else { + if (enable_follow_redirect_) + data = co_await async_request(std::move(redirect_uri_), + http_method::GET, std::move(ctx)); + co_return data; + } + } + + resp_data get(std::string uri, + std::unordered_map headers = {}) { + return async_simple::coro::syncAwait( + async_get(std::move(uri), std::move(headers))); + } + + resp_data post(std::string uri, std::string content, + req_content_type content_type, + std::unordered_map headers = {}) { + return async_simple::coro::syncAwait(async_post( + std::move(uri), std::move(content), content_type, std::move(headers))); + } + + async_simple::coro::Lazy async_post( + std::string uri, std::string content, req_content_type content_type, + std::unordered_map headers = {}) { + req_context<> ctx{content_type, "", std::move(content)}; + return async_request(std::move(uri), http_method::POST, std::move(ctx), + std::move(headers)); + } + + async_simple::coro::Lazy async_delete( + std::string uri, std::string content, req_content_type content_type, + std::unordered_map headers = {}) { + req_context<> ctx{content_type, "", std::move(content)}; + return async_request(std::move(uri), http_method::DEL, std::move(ctx), + std::move(headers)); + } + + async_simple::coro::Lazy async_put( + std::string uri, std::string content, req_content_type content_type, + std::unordered_map headers = {}) { + req_context<> ctx{content_type, "", std::move(content)}; + return async_request(std::move(uri), http_method::PUT, std::move(ctx), + std::move(headers)); + } + + bool add_str_part(std::string name, std::string content) { + size_t size = content.size(); + return form_data_ + .emplace(std::move(name), multipart_t{"", std::move(content), size}) + .second; + } + + bool add_file_part(std::string name, std::string filename) { + if (form_data_.find(name) != form_data_.end()) { + CINATRA_LOG_WARNING << "name already exist: " << name; + return false; + } + + std::error_code ec; + bool r = std::filesystem::exists(filename, ec); + if (!r || ec) { + if (ec) { + CINATRA_LOG_WARNING << ec.message(); + } + CINATRA_LOG_WARNING << "file not exists, " + << std::filesystem::current_path().string(); + return false; + } + + size_t file_size = std::filesystem::file_size(filename); + form_data_.emplace(std::move(name), + multipart_t{std::move(filename), "", file_size}); + return true; + } + + void set_max_single_part_size(size_t size) { max_single_part_size_ = size; } + + struct timer_guard { + timer_guard(coro_http_client *self, + std::chrono::steady_clock::duration duration, std::string msg) + : self(self), dur_(duration) { + if (duration.count() == 0) { + // Zero duration means immediate timeout. + self->socket_->is_timeout_ = true; + return; + } + self->socket_->is_timeout_ = false; + if (duration.count() > 0) { + self->timeout(self->timer_, duration, std::move(msg)) + .start([](auto &&) { + }); + } + return; + } + ~timer_guard() { + if (dur_.count() > 0 && self->socket_->is_timeout_ == false) { + std::error_code ignore_ec; + self->timer_.cancel(ignore_ec); + } + } + coro_http_client *self; + std::chrono::steady_clock::duration dur_; + }; + + async_simple::coro::Lazy async_download(std::string uri, + std::string filename, + std::string range = "") { + resp_data data{}; + coro_io::coro_file file; + file.open(filename, std::ios::trunc | std::ios::out); + if (!file.is_open()) { + data.net_err = std::make_error_code(std::errc::no_such_file_or_directory); + data.status = 404; + co_return data; + } + + req_context<> ctx{}; + if (range.empty()) { + add_header("Transfer-Encoding", "chunked"); + ctx = {req_content_type::none, "", "", &file}; + } + else { + std::string req_str = "Range: bytes="; + req_str.append(range).append(CRCF); + ctx = {req_content_type::none, std::move(req_str), {}, &file}; + } + + data = co_await async_request(std::move(uri), http_method::GET, + std::move(ctx)); + + co_return data; + } + + resp_data download(std::string uri, std::string filename, + std::string range = "") { + return async_simple::coro::syncAwait( + async_download(std::move(uri), std::move(filename), std::move(range))); + } + + bool is_body_in_out_buf() const { return !out_buf_.empty(); } + + void reset() { + if (!has_closed()) { + close_socket(*socket_); + } + + socket_->impl_ = asio::ip::tcp::socket{executor_wrapper_.context()}; + if (!socket_->impl_.is_open()) { + std::error_code ec; + socket_->impl_.open(asio::ip::tcp::v4(), ec); + if (ec) { + CINATRA_LOG_WARNING << "client reset socket failed, reason: " + << ec.message(); + return; + } + } + + socket_->has_closed_ = true; +#ifdef CINATRA_ENABLE_SSL + need_set_sni_host_ = true; + if (has_init_ssl_) { + socket_->ssl_stream_ = nullptr; + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + has_init_ssl_ = false; + } +#endif +#ifdef BENCHMARK_TEST + total_len_ = 0; +#endif + + // clear + head_buf_.consume(head_buf_.size()); + chunked_buf_.consume(chunked_buf_.size()); + resp_chunk_str_.clear(); + } + + std::string_view get_host() { return host_; } + + std::string_view get_port() { return port_; } + + private: + async_simple::coro::Lazy send_file_copy_with_chunked( + std::string_view source, std::error_code &ec) { + std::string file_data; + detail::resize(file_data, max_single_part_size_); + coro_io::coro_file file{}; + file.open(source, std::ios::in); + if (!file.is_open()) { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + while (!file.eof()) { + auto [rd_ec, rd_size] = + co_await file.async_read(file_data.data(), file_data.size()); + std::vector bufs; + std::string size_str; + cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, + file.eof()); + std::size_t size; + if (std::tie(ec, size) = co_await async_write(bufs); ec) { + break; + } + } + } + + async_simple::coro::Lazy send_file_copy_with_length( + std::string_view source, std::error_code &ec, std::size_t length, + std::size_t offset) { + if (length <= 0) { + co_return; + } + std::string file_data; + detail::resize(file_data, (std::min)(max_single_part_size_, length)); + coro_io::coro_file file{}; + file.open(source, std::ios::in); + if (!file.is_open()) { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + file.seek(offset, std::ios::cur); + std::size_t size; + while (length > 0) { + if (std::tie(ec, size) = co_await file.async_read( + file_data.data(), (std::min)(file_data.size(), length)); + ec) { + // bad request, file may smaller than content-length + break; + } + length -= size; + if (length > 0 && file.eof()) { + // bad request, file may smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + break; + } + if (std::tie(ec, size) = + co_await async_write(asio::buffer(file_data.data(), size)); + ec) { + break; + } + } + } +#ifdef __linux__ + struct fd_guard { + int fd; + fd_guard(const char *file_path) : fd(::open(file_path, O_RDONLY)) {} + ~fd_guard() { + if (fd >= 0) { + ::close(fd); + } + } + }; + async_simple::coro::Lazy send_file_no_copy_with_length( + const std::filesystem::path &source, std::error_code &ec, + std::size_t length, std::size_t offset) { + fd_guard guard(source.c_str()); + if (guard.fd < 0) [[unlikely]] { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + std::size_t actual_len = 0; + std::tie(ec, actual_len) = co_await coro_io::async_sendfile( + socket_->impl_, guard.fd, offset, length); + if (ec) [[unlikely]] { + co_return; + } + if (actual_len != length) [[unlikely]] { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + co_return; + } + } + async_simple::coro::Lazy send_file_no_copy_with_chunked( + const std::filesystem::path &source, std::error_code &ec) { + fd_guard guard(source.c_str()); + if (guard.fd < 0) [[unlikely]] { + ec = std::make_error_code(std::errc::bad_file_descriptor); + co_return; + } + off_t now_position = 0, + max_position = std::filesystem::file_size(source, ec); + if (ec) { + co_return; + } + size_t len = + std::min(max_single_part_size_, max_position - now_position); + // send chunked + std::array chunked_buffer; + std::size_t sz; + std::tie(ec, sz) = co_await async_write( + asio::buffer(get_chuncked_buffers(len, chunked_buffer))); + if (ec) [[unlikely]] { + co_return; + } + do { + std::size_t actual_len = 0; + std::tie(ec, actual_len) = co_await coro_io::async_sendfile( + socket_->impl_, guard.fd, now_position, len); + if (ec) [[unlikely]] { + co_return; + } + if (actual_len != len) [[unlikely]] { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + co_return; + } + if (now_position += actual_len; now_position < max_position) { + len = std::min(max_single_part_size_, + max_position - now_position); + std::tie(ec, sz) = co_await async_write(asio::buffer( + get_chuncked_buffers(len, chunked_buffer))); + if (ec) { + co_return; + } + } + else [[unlikely]] { + std::tie(ec, sz) = co_await async_write(asio::buffer( + get_chuncked_buffers(len, chunked_buffer))); + if (ec) { + co_return; + } + break; + } + } while (true); + } +#endif + template + static std::size_t getRemainingBytes(stream &file) { + auto current_pos = file.tellg(); + file.seekg(0, std::ios::end); + auto end_pos = file.tellg(); + auto remaining_bytes = end_pos - current_pos; + file.seekg(current_pos); + return remaining_bytes; + } + + template + void check_source(resp_data &data, Source &source) { + if constexpr (is_stream_ptr_v) { + if (!source) { + data = resp_data{ + std::make_error_code(std::errc::no_such_file_or_directory), 404}; + } + } + else if constexpr (std::is_same_v || + std::is_same_v) { + if (!std::filesystem::exists(source)) { + data = resp_data{ + std::make_error_code(std::errc::no_such_file_or_directory), 404}; + } + } + } + + void handle_upload_header_with_multipart() { + size_t content_len = multipart_content_len(); + add_header("Content-Length", std::to_string(content_len)); + } + + void handle_upload_header_with_chunked( + std::unordered_map &headers) { + if (!resp_chunk_str_.empty()) { + resp_chunk_str_.clear(); + } + + if (headers.empty()) { + add_header("Transfer-Encoding", "chunked"); + } + else { + headers.emplace("Transfer-Encoding", "chunked"); + } + } + + template + int64_t handle_upload_header_with_length( + resp_data &data, Source &source, + std::unordered_map &headers, uint64_t offset, + int64_t content_length) { + if (content_length < 0) { + if constexpr (is_stream_ptr_v) { + content_length = getRemainingBytes(*source); + } + else if constexpr (std::is_same_v || + std::is_same_v) { + content_length = std::filesystem::file_size(source); + } + else { + CINATRA_LOG_ERROR + << "user should set content-length before calling async_upload " + "when source is user-defined function."; + data = + resp_data{std::make_error_code(std::errc::invalid_argument), 404}; + return content_length; + } + content_length -= offset; + if (content_length < 0) { + CINATRA_LOG_ERROR << "the offset is larger than the end of file"; + data = + resp_data{std::make_error_code(std::errc::invalid_argument), 404}; + return content_length; + } + } + + assert(content_length >= 0); + char buf[32]; + auto [ptr, _] = std::to_chars(buf, buf + 32, content_length); + if (headers.empty()) { + add_header("Content-Length", std::string(buf, ptr - buf)); + } + else { + headers.emplace("Content-Length", std::string_view(buf, ptr - buf)); + } + return content_length; + } + + async_simple::coro::Lazy send_fstream_with_multipart( + std::error_code &ec) { + resp_data data{}; + for (auto &[key, part] : form_data_) { + data = co_await send_single_part(key, part); + + if (data.net_err) { + ec = data.net_err; + co_return; + } + } + + std::string last_part; + size_t size = 0; + last_part.append("--").append(BOUNDARY).append("--").append(CRCF); + if (std::tie(ec, size) = co_await async_write(asio::buffer(last_part)); + ec) { + co_return; + } + } + + template + async_simple::coro::Lazy send_fstream_with_chunked( + Source &source, std::error_code &ec) { + size_t size = 0; + std::string file_data; + detail::resize(file_data, max_single_part_size_); + while (!source->eof()) { + size_t rd_size = + source->read(file_data.data(), file_data.size()).gcount(); + std::vector bufs; + std::string size_str; + cinatra::to_chunked_buffers(bufs, size_str, {file_data.data(), rd_size}, + source->eof()); + if (std::tie(ec, size) = co_await async_write(bufs); ec) { + break; + } + } + } + + template + async_simple::coro::Lazy send_fstream_with_length( + Source &source, std::error_code &ec, uint64_t offset, + int64_t content_length) { + size_t size = 0; + source->seekg(offset, std::ios::cur); + std::string file_data; + detail::resize(file_data, std::min(max_single_part_size_, + content_length)); + while (content_length > 0 && !source->eof()) { + size_t rd_size = + source + ->read(file_data.data(), + std::min(content_length, file_data.size())) + .gcount(); + if (std::tie(ec, size) = + co_await async_write(asio::buffer(file_data.data(), rd_size)); + ec) { + break; + } + content_length -= rd_size; + } + if (!ec && content_length > 0) { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + } + } + + template + async_simple::coro::Lazy send_sink_with_chunked(Source &source, + std::error_code &ec) { + size_t size = 0; + while (true) { + auto result = co_await source(); + std::vector bufs; + std::string size_str; + cinatra::to_chunked_buffers( + bufs, size_str, {result.buf.data(), result.buf.size()}, result.eof); + if (std::tie(ec, size) = co_await async_write(bufs); ec) { + break; + } + if (result.eof) { + break; + } + } + } + + template + async_simple::coro::Lazy send_sink_with_length(Source &source, + std::error_code &ec, + int64_t content_length) { + size_t size = 0; + while (true) { + auto result = co_await source(); + if (std::tie(ec, size) = co_await async_write(asio::buffer( + result.buf.data(), + std::min(content_length, result.buf.size()))); + ec) { + break; + } + content_length -= size; + if (content_length <= 0) { + break; + } + else if (result.eof) [[unlikely]] { + // bad request, file is smaller than content-length + ec = std::make_error_code(std::errc::invalid_argument); + break; + } + } + } + + async_simple::coro::Lazy reconnect(resp_data &data, uri_t u) { + data = co_await connect(u); + if (socket_->is_timeout_) { + data = resp_data{make_error_code(http_errc::connect_timeout), 404}; + } + if (data.net_err) { + co_return false; + } + + co_return true; + } + + void handle_upload_timeout_error(std::error_code &ec) { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (write_header_timeout_ || write_payload_timeout_ || read_timeout_) { + socket_->is_timeout_ = true; + } +#endif + if (socket_->is_timeout_) { + ec = make_error_code(http_errc::request_timeout); + } + } + + template + async_simple::coro::Lazy async_upload_impl( + S uri, http_method method, Source source /* file */, + req_content_type content_type = req_content_type::text, + std::unordered_map headers = {}, + uint64_t offset = 0 /*file offset*/, + int64_t content_length = -1 /*upload size*/) { + std::error_code ec{}; + size_t size = 0; + bool is_keep_alive = true; + req_context<> ctx{content_type}; + resp_data data{}; + + out_buf_ = {}; + + std::shared_ptr guard(nullptr, [&, this](auto) { + if (!req_headers_.empty()) { + req_headers_.clear(); + } + }); + + auto [ok, u] = handle_uri(data, uri); + if (!ok) { + co_return resp_data{std::make_error_code(std::errc::protocol_error), 404}; + } + + if constexpr (upload_type != upload_type_t::multipart) { + check_source(data, source); + if (data.status != 0) { + co_return data; + } + } + + if constexpr (upload_type == upload_type_t::with_length) { + content_length = handle_upload_header_with_length(data, source, headers, + offset, content_length); + if (data.status != 0) { + co_return data; + } + } + else if constexpr (upload_type == upload_type_t::chunked) { + handle_upload_header_with_chunked(headers); + } + else if constexpr (upload_type == upload_type_t::multipart) { + handle_upload_header_with_multipart(); + } + + std::string header_str = + build_request_header(u, method, ctx, true, std::move(headers)); + + if (socket_->has_closed_) { + if (bool r = co_await reconnect(data, u); !r) { + co_return data; + } + } + + auto time_guard = timer_guard(this, req_timeout_duration_, "request timer"); + if (socket_->is_timeout_) { + co_return resp_data{make_error_code(http_errc::request_timeout), 404}; + } + std::tie(ec, size) = co_await async_write(asio::buffer(header_str)); + if (ec) { + handle_upload_timeout_error(ec); + co_return resp_data{ec, 404}; + } + + constexpr bool is_stream_file = is_stream_ptr_v; + if constexpr (is_stream_file) { + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_fstream_with_length(source, ec, offset, content_length); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_fstream_with_chunked(source, ec); + } + } + else if constexpr (std::is_enum_v) { // only for multipart + co_await send_fstream_with_multipart(ec); + } + else if constexpr (std::is_same_v || + std::is_same_v) { +#ifdef __linux__ +#ifdef CINATRA_ENABLE_SSL + if (!has_init_ssl_) { +#endif + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_file_no_copy_with_length(std::filesystem::path{source}, + ec, content_length, offset); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_file_no_copy_with_chunked(std::filesystem::path{source}, + ec); + } +#ifdef CINATRA_ENABLE_SSL + } + else { + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_file_copy_with_length(source, ec, content_length, + offset); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_file_copy_with_chunked(source, ec); + } + } +#endif +#else + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_file_copy_with_length(source, ec, content_length, offset); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_file_copy_with_chunked(source, ec); + } +#endif + } + else { + if constexpr (upload_type == upload_type_t::with_length) { + co_await send_sink_with_length(source, ec, content_length); + } + else if constexpr (upload_type == upload_type_t::chunked) { + co_await send_sink_with_chunked(source, ec); + } + } + if (ec) { + handle_upload_timeout_error(ec); + co_return resp_data{ec, 404}; + } + + data = co_await handle_read(ec, size, is_keep_alive, std::move(ctx), + http_method::POST); + if (ec) { + handle_upload_timeout_error(ec); + } + handle_result(data, ec, is_keep_alive); + co_return data; + } + + public: + // send file with length + template + async_simple::coro::Lazy async_upload( + S uri, http_method method, Source source /* file */, + uint64_t offset = 0 /*file offset*/, + int64_t content_length = -1 /*upload size*/, + req_content_type content_type = req_content_type::text, + std::unordered_map headers = {}) { + return async_upload_impl( + std::move(uri), method, std::move(source), content_type, + std::move(headers), offset, content_length); + } + + // send file with chunked + template + async_simple::coro::Lazy async_upload_chunked( + S uri, http_method method, Source source, + req_content_type content_type = req_content_type::text, + std::unordered_map headers = {}) { + return async_upload_impl( + std::move(uri), method, std::move(source), content_type, + std::move(headers)); + } + + // send multipart data, should call add_file_part or add_str_part firstly. + async_simple::coro::Lazy async_upload_multipart(std::string uri) { + if (form_data_.empty()) { + CINATRA_LOG_WARNING << "no multipart"; + co_return resp_data{std::make_error_code(std::errc::invalid_argument), + 404}; + } + + co_return co_await async_upload_impl( + std::move(uri), http_method::POST, upload_type_t::multipart, + req_content_type::multipart); + } + + async_simple::coro::Lazy async_upload_multipart( + std::string uri, std::string name, std::string filename) { + if (!add_file_part(std::move(name), std::move(filename))) { + CINATRA_LOG_WARNING << "open file failed or duplicate test names"; + co_return resp_data{std::make_error_code(std::errc::invalid_argument), + 404}; + } + co_return co_await async_upload_multipart(std::move(uri)); + } + + template + async_simple::coro::Lazy async_request( + S uri, http_method method, req_context ctx, + std::unordered_map headers = {}, + std::span out_buf = {}) { + if (!resp_chunk_str_.empty()) { + resp_chunk_str_.clear(); + } + if (!body_.empty()) { + body_.clear(); + } + + out_buf_ = out_buf; + + std::shared_ptr guard(nullptr, [this](auto) { + if (!req_headers_.empty()) { + req_headers_.clear(); + } + }); + + resp_data data{}; + + std::error_code ec{}; + size_t size = 0; + bool is_keep_alive = true; + + do { + uri_t u; + std::string append_uri; + + if (socket_->has_closed_ || (!uri.empty() && uri[0] != '/')) { + bool no_schema = !has_schema(uri); + + if (no_schema) { +#ifdef CINATRA_ENABLE_SSL + if (is_ssl_schema_) { + append_uri.append("https://").append(uri); + } + else +#endif + { + append_uri.append("http://").append(uri); + } + } + bool ok = false; + std::tie(ok, u) = handle_uri(data, no_schema ? append_uri : uri); + if (!ok) { + break; + } + } + else { + u.path = uri; + } + if (socket_->has_closed_) { + data = co_await connect(u); + if (data.status != 0) { + co_return data; + } + } + + std::vector vec; + std::string req_head_str = + build_request_header(u, method, ctx, false, std::move(headers)); + + bool has_body = !ctx.content.empty(); + if (has_body) { + vec.push_back(asio::buffer(req_head_str)); + vec.push_back(asio::buffer(ctx.content.data(), ctx.content.size())); + } + +#ifdef CORO_HTTP_PRINT_REQ_HEAD + CINATRA_LOG_DEBUG << req_head_str; +#endif + auto guard = timer_guard(this, req_timeout_duration_, "request timer"); + if (socket_->is_timeout_) { + ec = make_error_code(http_errc::request_timeout); + break; + } + if (has_body) { + std::tie(ec, size) = co_await async_write(vec); + } + else { + std::tie(ec, size) = co_await async_write(asio::buffer(req_head_str)); + } + if (ec) { + break; + } + data = + co_await handle_read(ec, size, is_keep_alive, std::move(ctx), method); + } while (0); + if (ec && socket_->is_timeout_) { + ec = make_error_code(http_errc::request_timeout); + } + handle_result(data, ec, is_keep_alive); + co_return data; + } + + async_simple::coro::Lazy handle_shake() { +#ifdef CINATRA_ENABLE_SSL + if (!has_init_ssl_) { + bool r = init_ssl(asio::ssl::verify_none, "", host_); + if (!r) { + co_return std::make_error_code(std::errc::invalid_argument); + } + } + + if (socket_->ssl_stream_ == nullptr) { + co_return std::make_error_code(std::errc::not_a_stream); + } + + auto ec = co_await coro_io::async_handshake(socket_->ssl_stream_, + asio::ssl::stream_base::client); + if (ec) { + CINATRA_LOG_ERROR << "handle failed " << ec.message(); + } + co_return ec; +#else + // please open CINATRA_ENABLE_SSL before request https! + co_return std::make_error_code(std::errc::protocol_error); +#endif + } + +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + async_simple::coro::Lazy async_write_raw( + std::string_view data) { + auto [ec, _] = co_await async_write(asio::buffer(data)); + co_return ec; + } + + async_simple::coro::Lazy async_read_raw( + http_method method, bool clear_buffer = false) { + if (clear_buffer) { + body_.clear(); + } + + char buf[1024]; + std::error_code ec{}; + size_t size{}; +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + std::tie(ec, size) = co_await coro_io::async_read_some( + *socket_->ssl_stream_, asio::buffer(buf, 1024)); + } + else { +#endif + std::tie(ec, size) = co_await coro_io::async_read_some( + socket_->impl_, asio::buffer(buf, 1024)); +#ifdef CINATRA_ENABLE_SSL + } +#endif + body_.append(buf, size); + + co_return resp_data{ec, {}, {}, body_}; + } +#endif + + inline void set_proxy(const std::string &host, const std::string &port) { + proxy_host_ = host; + proxy_port_ = port; + } + + inline void set_proxy_basic_auth(const std::string &username, + const std::string &password) { + proxy_basic_auth_username_ = username; + proxy_basic_auth_password_ = password; + } + + inline void set_proxy_bearer_token_auth(const std::string &token) { + proxy_bearer_token_auth_token_ = token; + } + + inline void enable_auto_redirect(bool enable_follow_redirect) { + enable_follow_redirect_ = enable_follow_redirect; + } + +#ifdef CINATRA_ENABLE_SSL + void set_ssl_schema(bool r) { is_ssl_schema_ = r; } +#ifdef YLT_ENABLE_NTLS + void set_ntls_schema(bool r) { use_ntls_ = r; } + + /*! + * Initialize NTLS client with dual certificates + */ + bool init_ntls_client(const std::string &sign_cert_file, + const std::string &sign_key_file, + const std::string &enc_cert_file, + const std::string &enc_key_file, + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none, + const std::string &passwd = "") { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(NTLS_client_method())); + + // Enable NTLS mode for Tongsuo + SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); + + // Set NTLS cipher suites + if (!SSL_CTX_set_cipher_list(ssl_ctx_->native_handle(), + "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { + CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; + } + + if (!passwd.empty()) { + ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { + return pwd; + }); + } + + // Load client certificates if provided (for mutual authentication) + if (!sign_cert_file.empty() && !sign_key_file.empty()) { + if (!SSL_CTX_use_sign_certificate_file(ssl_ctx_->native_handle(), + sign_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client SM2 signing certificate"; + return false; + } + + if (!SSL_CTX_use_sign_PrivateKey_file(ssl_ctx_->native_handle(), + sign_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client SM2 signing private key"; + return false; + } + } + + if (!enc_cert_file.empty() && !enc_key_file.empty()) { + if (!SSL_CTX_use_enc_certificate_file(ssl_ctx_->native_handle(), + enc_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR + << "failed to load client SM2 encryption certificate"; + return false; + } + + if (!SSL_CTX_use_enc_PrivateKey_file(ssl_ctx_->native_handle(), + enc_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR + << "failed to load client SM2 encryption private key"; + return false; + } + } + + // Load CA certificate if provided + if (!ca_cert_file.empty()) { + if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), + ca_cert_file.c_str(), nullptr)) { + CINATRA_LOG_WARNING << "failed to load CA certificate"; + } + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + has_init_ssl_ = true; + use_ntls_ = true; + return true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init NTLS client failed: " << e.what(); + return false; + } + } + /*! + * Initialize NTLS client with TLS 1.3 + GM single certificate mode (RFC 8998) + */ + bool init_ntls_tls13_gm_client( + const std::string &gm_cert_file = "", const std::string &gm_key_file = "", + const std::string &ca_cert_file = "", + int verify_mode = asio::ssl::verify_none, + const std::string &cipher_suites = "TLS_SM4_GCM_SM3:TLS_SM4_CCM_SM3", + const std::string &passwd = "") { + if (has_init_ssl_) { + return true; + } + + try { + ssl_ctx_ = std::make_unique( + SSL_CTX_new(TLS_client_method())); + + // Configure TLS 1.3 + GM mode (RFC 8998) + SSL_CTX_set_min_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx_->native_handle(), TLS1_3_VERSION); + + // Enable strict SM TLS 1.3 mode (Tongsuo specific) + SSL_CTX_enable_sm_tls13_strict(ssl_ctx_->native_handle()); + + // Set TLS 1.3 GM cipher suites + if (!SSL_CTX_set_ciphersuites(ssl_ctx_->native_handle(), + cipher_suites.c_str())) { + CINATRA_LOG_WARNING << "failed to set TLS 1.3 GM cipher suites"; + } + + // Set GM signature algorithms (required for SM TLS 1.3 strict mode) + if (!SSL_CTX_set1_sigalgs_list(ssl_ctx_->native_handle(), "sm2sig_sm3")) { + CINATRA_LOG_WARNING << "failed to set GM signature algorithms"; + } + + // Set GM curves (required for SM TLS 1.3 strict mode) + if (!SSL_CTX_set1_curves_list(ssl_ctx_->native_handle(), "SM2")) { + CINATRA_LOG_WARNING << "failed to set GM curves"; + } + + if (!passwd.empty()) { + ssl_ctx_->set_password_callback([pwd = std::move(passwd)](auto, auto) { + return pwd; + }); + } + + // Load client certificate if provided (for mutual authentication) + if (!gm_cert_file.empty() && !gm_key_file.empty()) { + if (!SSL_CTX_use_certificate_file(ssl_ctx_->native_handle(), + gm_cert_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client GM certificate"; + return false; + } + + if (!SSL_CTX_use_PrivateKey_file(ssl_ctx_->native_handle(), + gm_key_file.c_str(), + SSL_FILETYPE_PEM)) { + CINATRA_LOG_ERROR << "failed to load client GM private key"; + return false; + } + } + + // Load CA certificate if provided + if (!ca_cert_file.empty()) { + if (!SSL_CTX_load_verify_locations(ssl_ctx_->native_handle(), + ca_cert_file.c_str(), nullptr)) { + CINATRA_LOG_WARNING << "failed to load CA certificate"; + } + } + + ssl_ctx_->set_verify_mode(verify_mode); + + socket_->ssl_stream_ = + std::make_unique>( + socket_->impl_, *ssl_ctx_); + + has_init_ssl_ = true; + use_ntls_ = true; + return true; + } catch (std::exception &e) { + CINATRA_LOG_ERROR << "init TLS 1.3 + GM client failed: " << e.what(); + return false; + } + } + +#endif // YLT_ENABLE_NTLS +#endif + + std::string get_redirect_uri() { return redirect_uri_; } + + bool is_redirect(resp_data &data) { + if (data.status > 299 && data.status <= 399) + return true; + return false; + } + + void set_conn_timeout(std::chrono::steady_clock::duration timeout_duration) { + conn_timeout_duration_ = timeout_duration; + } + + void set_req_timeout(std::chrono::steady_clock::duration timeout_duration) { + req_timeout_duration_ = timeout_duration; + } + + void set_chunked_callback( + std::function(std::string_view)> cb) { + chunked_cb_ = std::move(cb); + } + + bool has_chunked_callback() const { return chunked_cb_ != nullptr; } + +#ifdef CINATRA_ENABLE_SSL + void enable_sni_hostname(bool r) { need_set_sni_host_ = r; } +#endif + + template + friend class coro_io::client_pool; + + private: + struct socket_t { + asio::ip::tcp::socket impl_; + std::atomic has_closed_ = true; + std::atomic is_timeout_ = false; + asio::streambuf head_buf_; + asio::streambuf chunked_buf_; +#ifdef CINATRA_ENABLE_SSL + std::unique_ptr> ssl_stream_; +#endif + template + socket_t(ioc_t &&ioc) : impl_(std::forward(ioc)) {} + }; + static bool is_ok(const resp_data &data) noexcept { + return data.net_err == std::error_code{}; + } + + template + std::pair handle_uri(resp_data &data, const S &uri) { + uri_t u; + if (!u.parse_from(uri.data())) { + CINATRA_LOG_WARNING + << uri + << ", the url is not right, maybe need to encode the url firstly"; + data.net_err = std::make_error_code(std::errc::protocol_error); + data.status = 404; + return {false, {}}; + } + + if (u.host.front() == '[') { // for ipv6 + if (u.host.size() > 2) + u.host = u.host.substr(1, u.host.size() - 2); + } + + // construct proxy request uri + construct_proxy_uri(u); + + return {true, u}; + } + + void construct_proxy_uri(uri_t &u) { + if (!proxy_host_.empty() && !proxy_port_.empty()) { + if (!proxy_request_uri_.empty()) + proxy_request_uri_.clear(); + if (u.get_port() == "80") { + proxy_request_uri_.append("http://").append(u.get_host()).append(":80"); + } + else if (u.get_port() == "443") { + proxy_request_uri_.append("https://") + .append(u.get_host()) + .append(":443"); + } + else { + // all be http + proxy_request_uri_.append("http://") + .append(u.get_host()) + .append(":") + .append(u.get_port()); + } + proxy_request_uri_.append(u.get_path()); + u.path = std::string_view(proxy_request_uri_); + } + } + + std::string build_request_header( + const uri_t &u, http_method method, const auto &ctx, + bool already_has_len = false, + std::unordered_map headers = {}) { + std::string req_str(method_name(method)); + + req_str.append(" ").append(u.get_path()); + if (!u.query.empty()) { + req_str.append("?").append(u.query); + } + + if (!headers.empty()) { + req_headers_ = std::move(headers); + req_str.append(" HTTP/1.1\r\n"); + } + else { + if (req_headers_.find("Host") == req_headers_.end()) { + req_str.append(" HTTP/1.1\r\nHost:").append(u.host).append("\r\n"); + } + else { + req_str.append(" HTTP/1.1\r\n"); + } + } + + auto type_str = get_content_type_str(ctx.content_type); + if (!type_str.empty()) { + if (ctx.content_type == req_content_type::multipart) { + type_str.append(BOUNDARY); + } + req_headers_["Content-Type"] = std::move(type_str); + } + + bool has_connection = false; + // add user headers + if (!req_headers_.empty()) { + for (auto &pair : req_headers_) { + if (pair.first == "Connection") { + has_connection = true; + } + req_str.append(pair.first) + .append(": ") + .append(pair.second) + .append("\r\n"); + } + } + + if (!has_connection) { + req_str.append("Connection: keep-alive\r\n"); + } + + if (!proxy_basic_auth_username_.empty() && + !proxy_basic_auth_password_.empty()) { + std::string basic_auth_str = "Proxy-Authorization: Basic "; + std::string basic_base64_str = base64_encode( + proxy_basic_auth_username_ + ":" + proxy_basic_auth_password_); + req_str.append(basic_auth_str).append(basic_base64_str).append(CRCF); + } + + if (!proxy_bearer_token_auth_token_.empty()) { + std::string bearer_token_str = "Proxy-Authorization: Bearer "; + req_str.append(bearer_token_str) + .append(proxy_bearer_token_auth_token_) + .append(CRCF); + } + + if (!ctx.req_header.empty()) + req_str.append(ctx.req_header); + size_t content_len = ctx.content.size(); + bool should_add_len = false; + if (content_len > 0) { + should_add_len = true; + } + else { + if ((method == http_method::POST || method == http_method::PUT) && + ctx.content_type != req_content_type::multipart) { + should_add_len = true; + } + } + + if (req_headers_.find("Content-Length") != req_headers_.end()) { + should_add_len = false; + } + + if (already_has_len) { + should_add_len = false; + } + + if (should_add_len) { + char buf[32]; + auto [ptr, ec] = std::to_chars(buf, buf + 32, content_len); + req_str.append("Content-Length: ") + .append(std::string_view(buf, ptr - buf)) + .append("\r\n"); + } + + req_str.append("\r\n"); + return req_str; + } + + std::error_code handle_header(resp_data &data, http_parser &parser, + size_t header_size) { + // parse header + const char *data_ptr = asio::buffer_cast(head_buf_.data()); + + int parse_ret = parser.parse_response(data_ptr, header_size, 0); +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (parse_failed_forever_) { + parse_ret = -1; + } +#endif + if (parse_ret < 0) [[unlikely]] { + head_buf_.consume(head_buf_.size()); + return std::make_error_code(std::errc::protocol_error); + } + + if (parser_.body_len() > max_http_body_len_ || parser_.body_len() < 0) + [[unlikely]] { + CINATRA_LOG_ERROR << "invalid http content length: " + << parser_.body_len(); + head_buf_.consume(head_buf_.size()); + return std::make_error_code(std::errc::invalid_argument); + } + + head_buf_.consume(header_size); // header size + data.resp_headers = parser.get_headers(); + data.status = parser.status(); + return {}; + } + + template + async_simple::coro::Lazy handle_read(std::error_code &ec, + size_t &size, + bool &is_keep_alive, + req_context ctx, + http_method method) { + resp_data data{}; + do { + if (std::tie(ec, size) = co_await async_read_until(head_buf_, TWO_CRCF); + ec) { + break; + } + + ec = handle_header(data, parser_, size); + if (ec) { + break; + } + + is_keep_alive = parser_.keep_alive(); + if (method == http_method::HEAD) { + co_return data; + } + + bool is_out_buf = false; + + bool is_ranges = parser_.is_resp_ranges(); + if (is_ranges) { + is_keep_alive = true; + } + if (parser_.is_chunked()) { + out_buf_ = {}; + is_keep_alive = true; + if (head_buf_.size() > 0) { + const char *data_ptr = + asio::buffer_cast(head_buf_.data()); + chunked_buf_.sputn(data_ptr, head_buf_.size()); + head_buf_.consume(head_buf_.size()); + } + ec = co_await handle_chunked(data, std::move(ctx)); + break; + } + + if (parser_.is_multipart()) { + out_buf_ = {}; + is_keep_alive = true; + if (head_buf_.size() > 0) { + const char *data_ptr = + asio::buffer_cast(head_buf_.data()); + chunked_buf_.sputn(data_ptr, head_buf_.size()); + head_buf_.consume(head_buf_.size()); + } + ec = co_await handle_multipart(data, std::move(ctx)); + break; + } + + redirect_uri_.clear(); + bool is_redirect = parser_.is_location(); + if (is_redirect) + redirect_uri_ = parser_.get_header_value("Location"); + + if (!parser_.get_header_value("Content-Encoding").empty()) { + if (parser_.get_header_value("Content-Encoding").find("gzip") != + std::string_view::npos) + encoding_type_ = content_encoding::gzip; + else if (parser_.get_header_value("Content-Encoding").find("deflate") != + std::string_view::npos) + encoding_type_ = content_encoding::deflate; + else if (parser_.get_header_value("Content-Encoding").find("br") != + std::string_view::npos) + encoding_type_ = content_encoding::br; + } + else { + encoding_type_ = content_encoding::none; + } + + size_t content_len = (size_t)parser_.body_len(); +#ifdef BENCHMARK_TEST + total_len_ = parser_.total_len(); +#endif + + is_out_buf = !out_buf_.empty(); + if (is_out_buf) { + if (content_len > 0 && out_buf_.size() < content_len) { + out_buf_ = {}; + is_out_buf = false; + } + } + + if (content_len <= head_buf_.size()) { + // Now get entire content, additional data will discard. + // copy body. + if (content_len > 0) { + auto data_ptr = asio::buffer_cast(head_buf_.data()); + if (is_out_buf) { + memcpy(out_buf_.data(), data_ptr, content_len); + } + else { + detail::resize(body_, content_len); + memcpy(body_.data(), data_ptr, content_len); + } + head_buf_.consume(head_buf_.size()); + } + co_await handle_entire_content(data, content_len, is_ranges, ctx); + break; + } + + // read left part of content. + size_t part_size = head_buf_.size(); + size_t size_to_read = content_len - part_size; + + auto data_ptr = asio::buffer_cast(head_buf_.data()); + if (is_out_buf) { + memcpy(out_buf_.data(), data_ptr, part_size); + } + else { + detail::resize(body_, content_len); + memcpy(body_.data(), data_ptr, part_size); + } + + head_buf_.consume(part_size); + + if (is_out_buf) { + if (std::tie(ec, size) = co_await async_read( + asio::buffer(out_buf_.data() + part_size, size_to_read), + size_to_read); + ec) { + break; + } + } + else { + if (std::tie(ec, size) = co_await async_read( + asio::buffer(body_.data() + part_size, size_to_read), + size_to_read); + ec) { + break; + } + } + + // Now get entire content, additional data will discard. + co_await handle_entire_content(data, content_len, is_ranges, ctx); + } while (0); + + if (!resp_chunk_str_.empty()) { + data.resp_body = + std::string_view{resp_chunk_str_.data(), resp_chunk_str_.size()}; + } + + co_return data; + } + + async_simple::coro::Lazy handle_entire_content(resp_data &data, + size_t content_len, + bool is_ranges, + auto &ctx) { + if (content_len > 0) { + const char *data_ptr; + if (head_buf_.size() == 0) { + if (out_buf_.empty()) { + data_ptr = body_.data(); + } + else { + data_ptr = out_buf_.data(); + } + } + else { + data_ptr = asio::buffer_cast(head_buf_.data()); + } + + if (is_ranges) { + if (ctx.resp_body_stream) { + auto [ec, size] = co_await ctx.resp_body_stream->async_write( + {data_ptr, content_len}); + if (ec) { + data.net_err = ec; + co_return; + } + } + } + + std::string_view reply(data_ptr, content_len); +#ifdef CINATRA_ENABLE_GZIP + if (encoding_type_ == content_encoding::gzip) { + uncompressed_str_.clear(); + bool r = gzip_codec::uncompress(reply, uncompressed_str_); + if (r) + data.resp_body = uncompressed_str_; + else + data.resp_body = reply; + } + else if (encoding_type_ == content_encoding::deflate) { + uncompressed_str_.clear(); + bool r = gzip_codec::inflate(reply, uncompressed_str_); + if (r) + data.resp_body = uncompressed_str_; + else + data.resp_body = reply; + } +#endif +#if defined(CINATRA_ENABLE_BROTLI) && defined(CINATRA_ENABLE_GZIP) + else if (encoding_type_ == content_encoding::br) +#endif +#if defined(CINATRA_ENABLE_BROTLI) && !defined(CINATRA_ENABLE_GZIP) + if (encoding_type_ == content_encoding::br) +#endif +#ifdef CINATRA_ENABLE_BROTLI + { + uncompressed_str_.clear(); + bool r = br_codec::brotli_decompress(reply, uncompressed_str_); + if (r) + data.resp_body = uncompressed_str_; + else + data.resp_body = reply; + } +#endif +#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) + else +#endif + data.resp_body = reply; + + head_buf_.consume(content_len); + } + data.eof = (head_buf_.size() == 0); + } + + void handle_result(resp_data &data, std::error_code ec, bool is_keep_alive) { + if (ec) { + close_socket(*socket_); + data.net_err = ec; + data.status = 404; +#ifdef BENCHMARK_TEST + if (!stop_bench_) { + CINATRA_LOG_DEBUG << ec.message(); + } +#endif + } + else { + if (!is_keep_alive) { + close_socket(*socket_); + } + } + } + + template + async_simple::coro::Lazy handle_multipart( + resp_data &data, req_context ctx) { + std::error_code ec{}; + std::string boundary = std::string{parser_.get_boundary()}; + multipart_reader_t multipart(this); + while (true) { + auto part_head = co_await multipart.read_part_head(boundary); + if (part_head.ec) { + co_return part_head.ec; + } + + auto part_body = co_await multipart.read_part_body(boundary); + + if (ctx.resp_body_stream) { + size_t size; + std::tie(ec, size) = + co_await ctx.resp_body_stream->async_write(part_body.data); + } + else { + resp_chunk_str_.append(part_body.data.data(), part_body.data.size()); + } + + if (part_body.ec) { + co_return part_body.ec; + } + + if (part_body.eof) { + break; + } + } + co_return ec; + } + + template + async_simple::coro::Lazy handle_chunked( + resp_data &data, req_context ctx) { + std::error_code ec{}; + size_t size = 0; + while (true) { + if (std::tie(ec, size) = co_await async_read_until(chunked_buf_, CRCF); + ec) { + break; + } + + size_t buf_size = chunked_buf_.size(); + size_t additional_size = buf_size - size; + const char *data_ptr = + asio::buffer_cast(chunked_buf_.data()); + std::string_view size_str(data_ptr, size - CRCF.size()); + auto chunk_size = hex_to_int(size_str); + chunked_buf_.consume(size); + if (chunk_size < 0) { + CINATRA_LOG_DEBUG << "bad chunked size"; + ec = asio::error::make_error_code( + asio::error::basic_errors::invalid_argument); + break; + } + + if (additional_size < size_t(chunk_size + 2)) { + // not a complete chunk, read left chunk data. + size_t size_to_read = chunk_size + 2 - additional_size; + if (std::tie(ec, size) = + co_await async_read(chunked_buf_, size_to_read); + ec) { + break; + } + } + + if (chunk_size == 0) { + // all finished, no more data + chunked_buf_.consume(chunked_buf_.size()); + data.eof = true; + break; + } + + data_ptr = asio::buffer_cast(chunked_buf_.data()); + if (chunked_cb_) { + co_await chunked_cb_(std::string_view(data_ptr, chunk_size)); + } + else { + if (ctx.resp_body_stream) { + std::tie(ec, size) = co_await ctx.resp_body_stream->async_write( + {data_ptr, (size_t)chunk_size}); + } + else { + resp_chunk_str_.append(data_ptr, chunk_size); + } + } + + chunked_buf_.consume(chunk_size + CRCF.size()); + } + co_return ec; + } + + async_simple::coro::Lazy connect( + const uri_t &u, std::vector *eps = nullptr) { + std::vector eps_tmp; + if (eps == nullptr) { + eps = &eps_tmp; + } + if (should_reset_) { + reset(); + } + else { + should_reset_ = true; + } + if (socket_->has_closed_) { + auto time_out_guard = + timer_guard(this, conn_timeout_duration_, "connect timer"); + if (socket_->is_timeout_) { + co_return resp_data{make_error_code(http_errc::connect_timeout), 404}; + } + host_ = proxy_host_.empty() ? u.get_host() : proxy_host_; + port_ = proxy_port_.empty() ? u.get_port() : proxy_port_; + if (eps->empty()) { + CINATRA_LOG_TRACE << "start resolve host: " << host_ << ":" << port_; + auto [ec, iter] = co_await coro_io::async_resolve( + &executor_wrapper_, socket_->impl_, host_, port_); + if (ec) { + co_return resp_data{ec, 404}; + } + else { + asio::ip::tcp::resolver::iterator end; + while (iter != end) { + eps->push_back(iter->endpoint()); + ++iter; + } + if (eps->empty()) [[unlikely]] { + co_return resp_data{std::make_error_code(std::errc::not_connected), + 404}; + } + } + } + CINATRA_LOG_TRACE + << "start connect to endpoint lists. total endpoint count:" + << eps->size() + << ", the first endpoint is: " << (*eps)[0].address().to_string() + << ":" << std::to_string((*eps)[0].port()); + std::error_code ec; + if (ec = co_await coro_io::async_connect(socket_->impl_, *eps); ec) { + if (socket_->is_timeout_) { + ec = make_error_code(http_errc::connect_timeout); + } + co_return resp_data{ec, 404}; + } +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (connect_timeout_forever_) { + socket_->is_timeout_ = true; + } +#endif + if (socket_->is_timeout_) { + ec = make_error_code(http_errc::connect_timeout); + co_return resp_data{ec, 404}; + } + + if (enable_tcp_no_delay_) { + socket_->impl_.set_option(asio::ip::tcp::no_delay(true), ec); + if (ec) { + co_return resp_data{ec, 404}; + } + } + + if (u.is_ssl) { +#ifdef CINATRA_ENABLE_SSL + if (!has_init_ssl_) { + size_t pos = u.host.find("www."); + std::string host; + if (pos != std::string_view::npos) { + host = std::string{u.host.substr(pos + 4)}; + } + else { + host = std::string{u.host}; + } + bool r = init_ssl(asio::ssl::verify_none, "", host); + if (!r) { + co_return resp_data{ + std::make_error_code(std::errc::invalid_argument), 404}; + } + } +#endif + if (ec = co_await handle_shake(); ec) { + co_return resp_data{ec, 404}; + } + } + socket_->has_closed_ = false; + CINATRA_LOG_TRACE + << "connect to endpoint: " + << socket_->impl_.remote_endpoint().address().to_string() << ":" + << socket_->impl_.remote_endpoint().port() << " successfully"; + } + co_return resp_data{}; + } + + size_t multipart_content_len() { + size_t content_len = 0; + for (auto &[key, part] : form_data_) { + content_len += 75; + content_len += key.size() + 1; + if (!part.filename.empty()) { + content_len += (12 + part.filename.size() + 1); + auto ext = std::filesystem::path(part.filename).extension().string(); + if (auto it = g_content_type_map.find(ext); + it != g_content_type_map.end()) { + content_len += (14 + it->second.size()); + } + } + + content_len += 4; + + content_len += (part.size + 2); + } + content_len += (6 + BOUNDARY.size()); + return content_len; + } + + async_simple::coro::Lazy send_single_part( + const std::string &key, const multipart_t &part) { + std::string part_content_head; + part_content_head.append("--").append(BOUNDARY).append(CRCF); + part_content_head.append("Content-Disposition: form-data; name=\""); + part_content_head.append(key).append("\""); + bool is_file = !part.filename.empty(); + std::string short_name = + std::filesystem::path(part.filename).filename().string(); + if (is_file) { + part_content_head.append("; filename=\"") + .append(short_name) + .append("\"") + .append(CRCF); + auto ext = std::filesystem::path(short_name).extension().string(); + if (auto it = g_content_type_map.find(ext); + it != g_content_type_map.end()) { + part_content_head.append("Content-Type: ") + .append(it->second) + .append(CRCF); + } + + part_content_head.append(CRCF); + } + else { + part_content_head.append(TWO_CRCF); + } + if (auto [ec, size] = co_await async_write(asio::buffer(part_content_head)); + ec) { + co_return resp_data{ec, 404}; + } + + if (is_file) { + coro_io::coro_file file{}; + file.open(part.filename, std::ios::in); + assert(file.is_open()); + std::string file_data; + detail::resize(file_data, max_single_part_size_); + while (true) { + auto [rd_ec, rd_size] = + co_await file.async_read(file_data.data(), file_data.size()); + if (auto [ec, size] = + co_await async_write(asio::buffer(file_data.data(), rd_size)); + ec) { + co_return resp_data{ec, 404}; + } + if (file.eof()) { + if (auto [ec, size] = co_await async_write(asio::buffer(CRCF)); ec) { + co_return resp_data{ec, 404}; + } + break; + } + } + } + else { + std::array arr{asio::buffer(part.content), + asio::buffer(CRCF)}; + if (auto [ec, size] = co_await async_write(arr); ec) { + co_return resp_data{ec, 404}; + } + } + + co_return resp_data{{}, 200}; + } + + async_simple::coro::Lazy async_read_ws() { + resp_data data{}; + + head_buf_.consume(head_buf_.size()); + std::shared_ptr sock = socket_; + asio::streambuf &read_buf = sock->head_buf_; + bool has_init_ssl = false; +#ifdef CINATRA_ENABLE_SSL + has_init_ssl = has_init_ssl_; +#endif + websocket ws{}; + while (true) { + if (auto [ec, _] = co_await async_read_ws( + sock, read_buf, ws.left_header_len(), has_init_ssl); + ec) { + if (socket_->is_timeout_) { + co_return resp_data{make_error_code(http_errc::request_timeout), 404}; + } + data.net_err = ec; + data.status = 404; + + if (sock->has_closed_) { + co_return data; + } + + close_socket(*sock); + co_return data; + } + + const char *data_ptr = asio::buffer_cast(read_buf.data()); + auto ret = ws.parse_header(data_ptr, read_buf.size(), false); + if (ret == ws_header_status::incomplete) { + continue; + } + else if (ret == ws_header_status::error) { + data.net_err = std::make_error_code(std::errc::protocol_error); + data.status = 404; + close_socket(*sock); + co_return data; + } + + frame_header *header = (frame_header *)data_ptr; + bool is_close_frame = header->opcode == opcode::close; + + read_buf.consume(read_buf.size()); + + size_t payload_len = ws.payload_length(); + + if (auto [ec, size] = + co_await async_read_ws(sock, read_buf, payload_len, has_init_ssl); + ec) { + data.net_err = ec; + data.status = 404; + close_socket(*sock); + co_return data; + } + + data_ptr = asio::buffer_cast(read_buf.data()); +#ifdef CINATRA_ENABLE_GZIP + if (is_server_support_ws_deflate_ && enable_ws_deflate_) { + inflate_str_.clear(); + if (!cinatra::gzip_codec::inflate({data_ptr, payload_len}, + inflate_str_)) { + CINATRA_LOG_ERROR << "uncompuress data error"; + data.status = 404; + data.net_err = std::make_error_code(std::errc::protocol_error); + co_return data; + } + data_ptr = inflate_str_.data(); + payload_len = inflate_str_.length(); + } +#endif + if (is_close_frame) { + if (payload_len >= 2) { + payload_len -= 2; + data_ptr += sizeof(uint16_t); + } + } + data.status = 200; + data.resp_body = {data_ptr, payload_len}; + + read_buf.consume(read_buf.size()); + + if (is_close_frame) { + std::string reason = "close"; + auto close_str = ws.format_close_payload(close_code::normal, + reason.data(), reason.size()); + auto span = std::span(close_str); + auto encode_header = ws.encode_frame(span, opcode::close, true); + std::vector buffers{asio::buffer(encode_header), + asio::buffer(reason)}; + + co_await async_write_ws(sock, buffers, has_init_ssl); + + close_socket(*sock); + + data.net_err = asio::error::eof; + data.status = 404; + co_return data; + } + co_return data; + } + } + + template + async_simple::coro::Lazy> async_read_ws( + auto sock, AsioBuffer &&buffer, size_t size_to_read, + bool has_init_ssl = false) noexcept { +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl) { + return coro_io::async_read(*sock->ssl_stream_, buffer, size_to_read); + } + else { +#endif + return coro_io::async_read(sock->impl_, buffer, size_to_read); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + template + async_simple::coro::Lazy> async_write_ws( + auto sock, AsioBuffer &&buffer, bool has_init_ssl = false) { +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl) { + return coro_io::async_write(*sock->ssl_stream_, buffer); + } + else { +#endif + return coro_io::async_write(sock->impl_, buffer); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + template + async_simple::coro::Lazy> async_read( + AsioBuffer &&buffer, size_t size_to_read) noexcept { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (read_failed_forever_) { + return async_read_failed(); + } +#endif +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + return coro_io::async_read(*socket_->ssl_stream_, buffer, size_to_read); + } + else { +#endif + return coro_io::async_read(socket_->impl_, buffer, size_to_read); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + async_simple::coro::Lazy> + async_write_failed() { + co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); + } + + async_simple::coro::Lazy> + async_read_failed() { + co_return std::make_pair(std::make_error_code(std::errc::io_error), 0); + } +#endif + + template + async_simple::coro::Lazy> async_write( + AsioBuffer &&buffer) { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (write_failed_forever_) { + return async_write_failed(); + } +#endif +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + return coro_io::async_write(*socket_->ssl_stream_, buffer); + } + else { +#endif + return coro_io::async_write(socket_->impl_, buffer); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + template + async_simple::coro::Lazy> async_read_until( + AsioBuffer &buffer, asio::string_view delim) noexcept { +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + if (read_failed_forever_) { + return async_read_failed(); + } +#endif +#ifdef CINATRA_ENABLE_SSL + if (has_init_ssl_) { + return coro_io::async_read_until(*socket_->ssl_stream_, buffer, delim); + } + else { +#endif + return coro_io::async_read_until(socket_->impl_, buffer, delim); +#ifdef CINATRA_ENABLE_SSL + } +#endif + } + + static void close_socket(socket_t &socket) { + std::error_code ec; + socket.impl_.shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket.impl_.close(ec); + socket.has_closed_ = true; + } + + async_simple::coro::Lazy timeout( + auto &timer, std::chrono::steady_clock::duration duration, + std::string msg) { + auto watcher = std::weak_ptr(socket_); + timer.expires_after(duration); + auto is_timeout = co_await timer.async_await(); + if (!is_timeout) { + co_return false; + } + if (auto socket = watcher.lock(); socket) { + socket_->is_timeout_ = true; + CINATRA_LOG_WARNING << msg << " timeout"; + close_socket(*socket_); + } + co_return true; + } + + template + bool has_schema(const S &url) { + size_t pos_http = url.find("http://"); + size_t pos_https = url.find("https://"); + size_t pos_ws = url.find("ws://"); + size_t pos_wss = url.find("wss://"); + bool has_http_scheme = + ((pos_http != std::string::npos) && pos_http == 0) || + ((pos_https != std::string::npos) && pos_https == 0) || + ((pos_ws != std::string::npos) && pos_ws == 0) || + ((pos_wss != std::string::npos) && pos_wss == 0); + return has_http_scheme; + } + + friend class multipart_reader_t; + http_parser parser_; + coro_io::ExecutorWrapper<> executor_wrapper_; + coro_io::period_timer timer_; + std::shared_ptr socket_; + asio::streambuf &head_buf_; + asio::streambuf &chunked_buf_; + std::string body_; + + std::unordered_map req_headers_; + + std::string proxy_request_uri_ = ""; + std::string proxy_host_; + std::string proxy_port_; + + std::string proxy_basic_auth_username_; + std::string proxy_basic_auth_password_; + + std::string proxy_bearer_token_auth_token_; + + std::map form_data_; + size_t max_single_part_size_ = 1024 * 1024; + + std::string ws_sec_key_; + std::string host_; + std::string port_; + +#ifdef CINATRA_ENABLE_SSL + std::unique_ptr ssl_ctx_ = nullptr; + bool has_init_ssl_ = false; + bool is_ssl_schema_ = false; +#ifdef YLT_ENABLE_NTLS + bool use_ntls_ = true; +#endif // YLT_ENABLE_NTLS + bool need_set_sni_host_ = true; +#endif + std::string redirect_uri_; + bool enable_follow_redirect_ = false; + std::chrono::steady_clock::duration conn_timeout_duration_ = + std::chrono::seconds(30); + std::chrono::steady_clock::duration req_timeout_duration_ = + std::chrono::seconds(60); + std::chrono::steady_clock::time_point create_tp_; + bool enable_tcp_no_delay_ = true; + std::string resp_chunk_str_; + std::span out_buf_; + bool should_reset_ = false; + config config_; + + bool enable_ws_deflate_ = false; +#ifdef CINATRA_ENABLE_GZIP + bool is_server_support_ws_deflate_ = false; + std::string inflate_str_; +#endif + content_encoding encoding_type_ = content_encoding::none; + int64_t max_http_body_len_ = + INT64_MAX; // in default we don't limit http body len + + std::function(std::string_view)> chunked_cb_; +#if defined(CINATRA_ENABLE_BROTLI) || defined(CINATRA_ENABLE_GZIP) + std::string uncompressed_str_; +#endif + +#ifdef BENCHMARK_TEST + bool stop_bench_ = false; + size_t total_len_ = 0; +#endif +#ifdef INJECT_FOR_HTTP_CLIENT_TEST + public: + bool write_failed_forever_ = false; + bool connect_timeout_forever_ = false; + bool parse_failed_forever_ = false; + bool read_failed_forever_ = false; + bool write_header_timeout_ = false; + bool write_payload_timeout_ = false; + bool read_timeout_ = false; +#endif +}; + +} // namespace cinatra From 7849500df661027f63400ce40676c32d4442d67d Mon Sep 17 00:00:00 2001 From: shenxuebing Date: Mon, 13 Apr 2026 15:11:27 +0800 Subject: [PATCH 38/38] fix: remove duplicate init_ssl function in coro_http_client --- .../standalone/cinatra/coro_http_client.hpp | 72 ------------------- 1 file changed, 72 deletions(-) diff --git a/include/ylt/standalone/cinatra/coro_http_client.hpp b/include/ylt/standalone/cinatra/coro_http_client.hpp index dd6fc1aae..cda7bc909 100644 --- a/include/ylt/standalone/cinatra/coro_http_client.hpp +++ b/include/ylt/standalone/cinatra/coro_http_client.hpp @@ -404,78 +404,6 @@ class coro_http_client : public std::enable_shared_from_this { } return true; } - - /*! - * Initialize SSL with CA certificate for server verification - * @param verify_mode SSL verify mode - * @param base_path Base path for certificate files - * @param cert_file CA certificate file name - * @param sni_hostname SNI hostname - * @return true if initialization successful - */ - [[nodiscard]] bool init_ssl(int verify_mode, const std::string &base_path, - const std::string &cert_file, - const std::string &sni_hostname) { - if (has_init_ssl_) { - return true; - } - - try { -#ifdef YLT_ENABLE_NTLS - if (use_ntls_) { - ssl_ctx_ = std::make_unique( - SSL_CTX_new(NTLS_client_method())); - SSL_CTX_enable_ntls(ssl_ctx_->native_handle()); - if (!SSL_CTX_set_cipher_list( - ssl_ctx_->native_handle(), - "ECC-SM2-SM4-GCM-SM3:ECC-SM2-SM4-CBC-SM3")) { - CINATRA_LOG_WARNING << "failed to set NTLS cipher suites"; - } - } - else { - CINATRA_LOG_ERROR << "NTLS is not supported in this build."; - return false; - } -#else - ssl_ctx_ = - std::make_unique(asio::ssl::context::sslv23); -#endif - auto full_cert_file = std::filesystem::path(base_path).append(cert_file); - if (std::filesystem::exists(full_cert_file)) { - ssl_ctx_->load_verify_file(full_cert_file.string()); - } - else { - if (!base_path.empty() || !cert_file.empty()) - return false; - } - - if (base_path.empty() && cert_file.empty()) { - ssl_ctx_->set_default_verify_paths(); - } - - ssl_ctx_->set_verify_mode(verify_mode); - - socket_->ssl_stream_ = - std::make_unique>( - socket_->impl_, *ssl_ctx_); - - if (!sni_hostname.empty()) { - ssl_ctx_->set_verify_callback( - asio::ssl::host_name_verification(sni_hostname)); - if (need_set_sni_host_) { - SSL_set_tlsext_host_name(socket_->ssl_stream_->native_handle(), - sni_hostname.c_str()); - } - } - - has_init_ssl_ = true; - is_ssl_schema_ = true; - } catch (std::exception &e) { - CINATRA_LOG_ERROR << "init ssl failed: " << e.what(); - return false; - } - return true; - } #endif // return body_, the user will own body's lifetime.