From 450ffaa6f0d33d5926da398a6e4c2f212c66f2c4 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 15 Feb 2017 23:23:48 +0900 Subject: [PATCH] nghttpx: Add configrevision API endpoint This commit adds configuration revision, which is considered opaque string, and changes after reloading configuration with SIGHUP. This revision is returned as a response to configrevision API endpoint. This allows external application to know whether nghttpx has finished reloading new configuration or not. Note that this revision does not change on backendconfig API calls. --- src/shrpx.cc | 1 + src/shrpx_api_downstream_connection.cc | 62 ++++++++++++++++++++++---- src/shrpx_api_downstream_connection.h | 5 ++- src/shrpx_config.h | 8 ++++ 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index fac5ae34..cbde333a 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -2942,6 +2942,7 @@ void reload_config(WorkerProcess *wp) { new_config->daemon = cur_config->daemon; // loop is reused, and ev_loop_flags gets ignored new_config->ev_loop_flags = cur_config->ev_loop_flags; + new_config->config_revision = cur_config->config_revision + 1; rv = process_options(new_config.get(), suconfig.cmdcfgs); if (rv != 0) { diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index 46413652..2be26bea 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -41,6 +41,10 @@ const APIEndpoint apis[] = { (1 << API_METHOD_POST) | (1 << API_METHOD_PUT), &APIDownstreamConnection::handle_backendconfig, }, + APIEndpoint{ + StringRef::from_lit("/api/v1beta1/configrevision"), true, + (1 << API_METHOD_GET), &APIDownstreamConnection::handle_configrevision, + }, }; } // namespace @@ -82,7 +86,7 @@ enum { }; int APIDownstreamConnection::send_reply(unsigned int http_status, - int api_status) { + int api_status, const StringRef &data) { shutdown_read_ = true; auto upstream = downstream_->get_upstream(); @@ -112,7 +116,8 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, // 3 is the number of digits in http_status, assuming it is 3 digits // number. - auto buflen = M1.size() + M2.size() + M3.size() + api_status_str.size() + 3; + auto buflen = M1.size() + M2.size() + M3.size() + data.size() + + api_status_str.size() + 3; auto buf = make_byte_ref(balloc, buflen); auto p = buf.base; @@ -121,6 +126,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, p = std::copy(std::begin(api_status_str), std::end(api_status_str), p); p = std::copy(std::begin(M2), std::end(M2), p); p = util::utos(p, http_status); + p = std::copy(std::begin(data), std::end(data), p); p = std::copy(std::begin(M3), std::end(M3), p); buf.len = p - buf.base; @@ -147,6 +153,32 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, return 0; } +namespace { +const APIEndpoint *lookup_api(const StringRef &path) { + switch (path.size()) { + case 26: + switch (path[25]) { + case 'g': + if (util::streq_l("/api/v1beta1/backendconfi", std::begin(path), 25)) { + return &apis[0]; + } + break; + } + break; + case 27: + switch (path[26]) { + case 'n': + if (util::streq_l("/api/v1beta1/configrevisio", std::begin(path), 26)) { + return &apis[1]; + } + break; + } + break; + } + return nullptr; +} +} // namespace + int APIDownstreamConnection::push_request_headers() { auto &req = downstream_->request(); @@ -154,12 +186,7 @@ int APIDownstreamConnection::push_request_headers() { StringRef{std::begin(req.path), std::find(std::begin(req.path), std::end(req.path), '?')}; - for (auto &p : apis) { - if (p.path == path) { - api_ = &p; - break; - } - } + api_ = lookup_api(path); if (!api_) { send_reply(404, API_FAILURE); @@ -365,6 +392,25 @@ int APIDownstreamConnection::handle_backendconfig() { return 0; } +int APIDownstreamConnection::handle_configrevision() { + auto config = get_config(); + auto &balloc = downstream_->get_block_allocator(); + + // Construct the following string: + // , + // "data":{ + // "configRevision": N + // } + auto data = concat_string_ref( + balloc, StringRef::from_lit(R"(,"data":{"configRevision":)"), + util::make_string_ref_uint(balloc, config->config_revision), + StringRef::from_lit("}")); + + send_reply(200, API_SUCCESS, data); + + return 0; +} + void APIDownstreamConnection::pause_read(IOCtrlReason reason) {} int APIDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { diff --git a/src/shrpx_api_downstream_connection.h b/src/shrpx_api_downstream_connection.h index 2a0c4b1e..356ee53d 100644 --- a/src/shrpx_api_downstream_connection.h +++ b/src/shrpx_api_downstream_connection.h @@ -83,11 +83,14 @@ public: get_downstream_addr_group() const; virtual DownstreamAddr *get_addr() const; - int send_reply(unsigned int http_status, int api_status); + int send_reply(unsigned int http_status, int api_status, + const StringRef &data = StringRef{}); int error_method_not_allowed(); // Handles backendconfig API request. int handle_backendconfig(); + // Handles configrevision API request. + int handle_configrevision(); private: Worker *worker_; diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 8efdb40d..79293441 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -850,6 +850,7 @@ struct Config { conn{}, api{}, dns{}, + config_revision{0}, num_worker{0}, padding{0}, rlimit_nofile{0}, @@ -883,6 +884,13 @@ struct Config { StringRef conf_path; StringRef user; StringRef mruby_file; + // The revision of configuration which is opaque string, and changes + // on each configuration reloading. This does not change on + // backendconfig API call. This value is returned in health check + // as "nghttpx-conf-rev" response header field. The external + // program can check this value to know whether reloading has + // completed or not. + uint64_t config_revision; size_t num_worker; size_t padding; size_t rlimit_nofile;