diff --git a/src/client/etcd_state_client.cpp b/src/client/etcd_state_client.cpp index 9938c831..37bd2cdb 100644 --- a/src/client/etcd_state_client.cpp +++ b/src/client/etcd_state_client.cpp @@ -30,6 +30,11 @@ etcd_state_client_t::~etcd_state_client_t() http_destroy(keepalive_client); keepalive_client = NULL; } + if (http_ctx) + { + http_context_destroy(http_ctx); + http_ctx = NULL; + } #endif if (load_pgs_timer_id >= 0) { @@ -72,6 +77,21 @@ std::vector etcd_state_client_t::get_addresses() return addrs; } +http_context_t *etcd_state_client_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_t::etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function callback) { @@ -91,7 +111,7 @@ void etcd_state_client_t::etcd_call_oneshot(std::string etcd_address, std::strin "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; @@ -100,7 +120,7 @@ void etcd_state_client_t::etcd_call_oneshot(std::string etcd_address, std::strin 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_t::etcd_call(std::string api, json11::Json payload, int timeout, @@ -168,10 +188,8 @@ void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int t 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_t::add_etcd_url(std::string addr) @@ -356,10 +374,10 @@ void etcd_state_client_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.h b/src/client/etcd_state_client.h index ef89a468..7f80400a 100644 --- a/src/client/etcd_state_client.h +++ b/src/client/etcd_state_client.h @@ -96,6 +96,7 @@ struct inode_watch_t }; struct http_co_t; +struct http_context_t; struct __attribute__((visibility("default"))) etcd_state_client_t { @@ -131,6 +132,7 @@ public: int log_level = 0; timerfd_manager_t *tfd = NULL; + http_context_t *http_ctx = NULL; http_co_t *etcd_watch_ws = NULL, *keepalive_client = NULL; int etcd_watches_initialised = 0; uint64_t etcd_watch_revision_config = 0; @@ -163,6 +165,7 @@ public: json11::Json::object serialize_inode_cfg(inode_config_t *cfg); etcd_kv_t parse_etcd_kv(const json11::Json & kv_json); std::vector get_addresses(); + http_context_t *get_http_ctx(); void etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function callback); void etcd_call(std::string api, json11::Json payload, int timeout, int retries, int interval, std::function callback); void etcd_txn(json11::Json txn, int timeout, int retries, int interval, std::function callback); 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,