From ba0d9ad9f22f04da59868def56be5e5703c71de4 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Wed, 28 Jan 2026 02:38:07 +0300 Subject: [PATCH] Extract common HTTP context --- src/client/etcd_state_client_http.cpp | 34 ++++++--- src/client/etcd_state_client_http.h | 6 +- src/client/http_client.cpp | 100 ++++++++++++++++---------- src/client/http_client.h | 9 +-- 4 files changed, 99 insertions(+), 50 deletions(-) diff --git a/src/client/etcd_state_client_http.cpp b/src/client/etcd_state_client_http.cpp index b4c21349..d9852815 100644 --- a/src/client/etcd_state_client_http.cpp +++ b/src/client/etcd_state_client_http.cpp @@ -29,6 +29,11 @@ etcd_state_client_http_t::~etcd_state_client_http_t() tfd->clear_timer(load_pgs_timer_id); load_pgs_timer_id = -1; } + if (http_ctx) + { + http_context_destroy(http_ctx); + http_ctx = NULL; + } etcd_watches_initialised = -1; } @@ -40,6 +45,21 @@ void etcd_state_client_http_t::etcd_add_watch(json11::Json watch) } } +http_context_t *etcd_state_client_http_t::get_http_ctx() +{ + if (!http_ctx) + { + std::string error; + http_ctx = http_context_init(etcd_client_cert, etcd_client_key, etcd_ca, error); + if (!http_ctx) + { + fprintf(stderr, "Failed to initialize HTTP context: %s\n", error.c_str()); + exit(1); + } + } + return http_ctx; +} + void etcd_state_client_http_t::etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function callback) { @@ -59,7 +79,7 @@ void etcd_state_client_http_t::etcd_call_oneshot(std::string etcd_address, std:: "Content-Length: "+std::to_string(req.size())+"\r\n" "Connection: close\r\n" "\r\n"+req; - auto http_cli = http_init(tfd); + auto http_cli = http_init(tfd, get_http_ctx()); auto cb = [http_cli, callback](const http_response_t *response) { std::string err; @@ -68,7 +88,7 @@ void etcd_state_client_http_t::etcd_call_oneshot(std::string etcd_address, std:: callback(err, data); http_destroy(http_cli); }; - http_request(http_cli, etcd_address, req, { .timeout = timeout, .ssl = ssl, .ssl_ca = etcd_ca }, cb); + http_request(http_cli, etcd_address, req, { .timeout = timeout, .ssl = ssl }, cb); } void etcd_state_client_http_t::etcd_call(std::string api, json11::Json payload, int timeout, @@ -136,10 +156,8 @@ void etcd_state_client_http_t::etcd_call(std::string api, json11::Json payload, callback(err, data); }; if (!keepalive_client) - { - keepalive_client = http_init(tfd); - } - http_request(keepalive_client, etcd_address, req, { .timeout = timeout, .keepalive = true, .ssl = ssl, .ssl_ca = etcd_ca }, cb); + keepalive_client = http_init(tfd, get_http_ctx()); + http_request(keepalive_client, etcd_address, req, { .timeout = timeout, .keepalive = true, .ssl = ssl }, cb); } void etcd_state_client_http_t::parse_config(const json11::Json & config) @@ -209,10 +227,10 @@ void etcd_state_client_http_t::start_etcd_watcher() etcd_watch_revision_config, etcd_watch_revision_osd, etcd_watch_revision_pg); } if (!etcd_watch_ws) - etcd_watch_ws = http_init(tfd); + etcd_watch_ws = http_init(tfd, get_http_ctx()); else http_close(etcd_watch_ws); - open_websocket(etcd_watch_ws, etcd_address, etcd_api_path+"/watch", { .timeout = etcd_slow_timeout, .ssl = ssl, .ssl_ca = etcd_ca }, + open_websocket(etcd_watch_ws, etcd_address, etcd_api_path+"/watch", { .timeout = etcd_slow_timeout, .ssl = ssl }, [this, cur_addr = selected_etcd_address](const http_response_t *msg) { if (msg->body.length()) diff --git a/src/client/etcd_state_client_http.h b/src/client/etcd_state_client_http.h index 33019499..6ea7efea 100644 --- a/src/client/etcd_state_client_http.h +++ b/src/client/etcd_state_client_http.h @@ -5,6 +5,8 @@ #include "etcd_state_client.h" +struct http_context_t; + struct __attribute__((visibility("default"))) etcd_state_client_http_t: public etcd_state_client_t { protected: @@ -18,13 +20,15 @@ protected: timespec etcd_last_reload = {}; int load_pgs_timer_id = -1; http_co_t *keepalive_client = NULL; + http_co_t *etcd_watch_ws = NULL; + http_context_t *http_ctx = NULL; void pick_next_etcd(); void start_etcd_watcher(); void stop_ws_keepalive(); void start_ws_keepalive(); + http_context_t *get_http_ctx(); public: - http_co_t *etcd_watch_ws = NULL; etcd_state_client_http_t(timerfd_manager_t *tfd); void etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function callback) override; void etcd_call(std::string api, json11::Json payload, int timeout, int retries, int interval, std::function callback) override; diff --git a/src/client/http_client.cpp b/src/client/http_client.cpp index 819123da..f9acd994 100644 --- a/src/client/http_client.cpp +++ b/src/client/http_client.cpp @@ -34,10 +34,32 @@ static std::string ws_format_frame(int type, uint64_t size); static bool ws_parse_frame(std::string & buf, uint8_t & type, std::string & res); static void parse_http_headers(std::string & res, http_response_t *parsed); -struct http_co_t +struct http_context_t { + std::string ssl_cert; + std::string ssl_key; + std::string ssl_ca; + #ifdef WITH_OPENSSL SSL_CTX *ssl_ctx = NULL; +#endif + + ~http_context_t() + { +#ifdef WITH_OPENSSL + if (ssl_ctx) + { + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + } +#endif + } +}; + +struct http_co_t +{ + http_context_t *ctx = NULL; +#ifdef WITH_OPENSSL SSL *ssl_cli = NULL; BIO *ssl_bio = NULL; #endif @@ -47,9 +69,6 @@ struct http_co_t int request_timeout = 0; bool ssl = false; - std::string ssl_cert; - std::string ssl_key; - std::string ssl_ca; std::string host; std::string request; std::string ws_outbox; @@ -108,11 +127,47 @@ struct http_co_t #define DEFAULT_TIMEOUT 5000 -http_co_t *http_init(timerfd_manager_t *tfd) +http_context_t* http_context_init(const std::string & ssl_cert, const std::string & ssl_key, const std::string & ssl_ca, std::string & error) +{ + http_context_t *ctx = new http_context_t; +#ifdef WITH_OPENSSL + SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); + ctx->ssl_cert = ssl_cert; + ctx->ssl_key = ssl_key; + ctx->ssl_ca = ssl_ca; + ctx->ssl_ctx = ssl_ctx; + if (!ssl_ctx) + goto init_err; + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); + if (!SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION)) + goto init_err; + if ((ssl_ca != "") + ? !SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca.c_str(), NULL) + : !SSL_CTX_set_default_verify_paths(ssl_ctx)) + goto init_err; + if (ssl_cert != "" && ssl_key != "" && + (!SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert.c_str(), SSL_FILETYPE_PEM) || + !SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key.c_str(), SSL_FILETYPE_PEM))) + goto init_err; +#endif + return ctx; +init_err: + error = std::string("openssl initialization failed: ")+ERR_error_string(ERR_get_error(), NULL); + delete ctx; + return NULL; +} + +void http_context_destroy(http_context_t *ctx) +{ + delete ctx; +} + +http_co_t *http_init(timerfd_manager_t *tfd, http_context_t *ctx) { http_co_t *handler = new http_co_t(); handler->tfd = tfd; handler->state = HTTP_CO_CLOSED; + handler->ctx = ctx; return handler; } @@ -135,7 +190,6 @@ void open_websocket(http_co_t *handler, const std::string & host, const std::str handler->want_streaming = false; handler->keepalive = false; handler->ssl = options.ssl; - handler->ssl_ca = options.ssl_ca; handler->request = request; handler->response_callback = response_callback; handler->ws_outbox = ""; @@ -188,7 +242,6 @@ void http_co_t::send_request(const std::string & host, const std::string & reque this->want_streaming = options.want_streaming; this->keepalive = options.keepalive; this->ssl = options.ssl; - this->ssl_ca = options.ssl_ca; this->host = host; this->request = request; this->response = ""; @@ -297,13 +350,6 @@ void http_response_t::parse_json_response(std::string & error, json11::Json & r) http_co_t::~http_co_t() { close_connection(); -#ifdef WITH_OPENSSL - if (ssl_ctx) - { - SSL_CTX_free(ssl_ctx); - ssl_ctx = NULL; - } -#endif } void http_co_t::close_connection() @@ -382,29 +428,14 @@ void http_co_t::start_connection() // https://wiki.openssl.org/index.php/Hostname_validation if (ssl) { - if (!ssl_ctx) - { - ssl_ctx = SSL_CTX_new(TLS_method()); - if (!ssl_ctx) - goto init_err; - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); - if (!SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION)) - goto init_err; - if ((ssl_ca != "") - ? !SSL_CTX_load_verify_locations(ssl_ctx, ssl_ca.c_str(), NULL) - : !SSL_CTX_set_default_verify_paths(ssl_ctx)) - goto init_err; - if (ssl_cert != "" && ssl_key != "" && - (!SSL_CTX_use_certificate_file(ssl_ctx, ssl_cert.c_str(), SSL_FILETYPE_PEM) || - !SSL_CTX_use_PrivateKey_file(ssl_ctx, ssl_key.c_str(), SSL_FILETYPE_PEM))) - goto init_err; - } + if (!ctx) + goto init_err; ssl_bio = BIO_new(BIO_s_socket()); if (!ssl_bio) goto init_err; if (!BIO_set_fd(ssl_bio, peer_fd, BIO_NOCLOSE)) goto init_err; - ssl_cli = SSL_new(ssl_ctx); + ssl_cli = SSL_new(ctx->ssl_ctx); if (!ssl_cli) goto init_err; SSL_set_bio(ssl_cli, ssl_bio, ssl_bio); @@ -421,11 +452,6 @@ init_err: BIO_free(ssl_bio); ssl_bio = NULL; } - if (ssl_ctx) - { - SSL_CTX_free(ssl_ctx); - ssl_ctx = NULL; - } parsed = { .error = std::string("openssl initialization failed: ")+ERR_error_string(ERR_get_error(), NULL) }; response_callback(&parsed); response_callback = NULL; diff --git a/src/client/http_client.h b/src/client/http_client.h index fa1f724b..02eac974 100644 --- a/src/client/http_client.h +++ b/src/client/http_client.h @@ -23,11 +23,10 @@ struct http_options_t bool want_streaming; bool keepalive; bool ssl; - std::string ssl_cert; - std::string ssl_key; - std::string ssl_ca; }; +struct http_context_t; + struct http_response_t { std::string error; @@ -45,7 +44,9 @@ struct http_response_t // Opened websocket or keepalive HTTP connection struct http_co_t; -http_co_t* http_init(timerfd_manager_t *tfd); +http_context_t* http_context_init(const std::string & ssl_cert, const std::string & ssl_key, const std::string & ssl_ca, std::string & error); +void http_context_destroy(http_context_t *ctx); +http_co_t* http_init(timerfd_manager_t *tfd, http_context_t *ctx = NULL); void open_websocket(http_co_t *handler, const std::string & host, const std::string & path, const http_options_t & options, std::function on_message); void http_request(http_co_t *handler, const std::string & host, const std::string & request,