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;