From 2a0d0e798b9a1f06c036c339219020f3c0592c89 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 2 Jun 2016 22:39:04 +0900 Subject: [PATCH 01/24] nghttpx: Add api parameter to --frontend option to mark API endpoint --- src/shrpx_config.cc | 4 ++++ src/shrpx_config.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index b934466b..01ef486d 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -611,6 +611,7 @@ int parse_memcached_connection_params(MemcachedConnectionParams &out, struct UpstreamParams { bool tls; + bool api; }; namespace { @@ -627,6 +628,8 @@ int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) { out.tls = true; } else if (util::strieq_l("no-tls", param)) { out.tls = false; + } else if (util::strieq_l("api", param)) { + out.api = true; } else if (!param.empty()) { LOG(ERROR) << "frontend: " << param << ": unknown keyword"; return -1; @@ -1832,6 +1835,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, UpstreamAddr addr{}; addr.fd = -1; addr.tls = params.tls; + addr.api = params.api; if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); diff --git a/src/shrpx_config.h b/src/shrpx_config.h index ad66fd1f..dfd5997a 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -321,6 +321,8 @@ struct UpstreamAddr { bool host_unix; // true if TLS is enabled. bool tls; + // true if this is an API endpoint. + bool api; int fd; }; From 667c8b0e272e0e6b2bcfa7c8d4ba32f6333bdb3b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 2 Jun 2016 23:47:41 +0900 Subject: [PATCH 02/24] nghttpx: Add APIDownstreamConnection to handle API request For those connections via frontend with api parameter, they use solely APIDownstreamConnection. In this commit, APIDownstreamConnection just consumes all request body, and do nothing. The next few commits implements our first API endpoint: /v1/api/dynamicconfig. --- src/CMakeLists.txt | 1 + src/Makefile.am | 1 + src/shrpx.cc | 5 ++ src/shrpx_api_downstream_connection.cc | 107 +++++++++++++++++++++++++ src/shrpx_api_downstream_connection.h | 65 +++++++++++++++ src/shrpx_client_handler.cc | 5 ++ src/shrpx_config.h | 1 + src/shrpx_downstream.cc | 8 ++ src/shrpx_http2_upstream.cc | 26 ++++-- src/template.h | 4 + 10 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 src/shrpx_api_downstream_connection.cc create mode 100644 src/shrpx_api_downstream_connection.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 78c775d8..e3822289 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -109,6 +109,7 @@ if(ENABLE_APP) shrpx_worker_process.cc shrpx_signal.cc shrpx_router.cc + shrpx_api_downstream_connection.cc ) if(HAVE_SPDYLAY) list(APPEND NGHTTPX_SRCS diff --git a/src/Makefile.am b/src/Makefile.am index 01701838..1d671c48 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -132,6 +132,7 @@ NGHTTPX_SRCS = \ shrpx_process.h \ shrpx_signal.cc shrpx_signal.h \ shrpx_router.cc shrpx_router.h \ + shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \ buffer.h memchunk.h template.h allocator.h if HAVE_SPDYLAY diff --git a/src/shrpx.cc b/src/shrpx.cc index aa0d34a8..26fd846c 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1157,6 +1157,11 @@ void fill_default_config() { nghttp2_option_new(&upstreamconf.option); nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1); nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1); + + // For API endpoint, we enable automatic window update. This is + // because we are a sink. + nghttp2_option_new(&upstreamconf.api_option); + nghttp2_option_set_no_recv_client_magic(upstreamconf.api_option, 1); } { diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc new file mode 100644 index 00000000..eb10ef8f --- /dev/null +++ b/src/shrpx_api_downstream_connection.cc @@ -0,0 +1,107 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "shrpx_api_downstream_connection.h" + +#include "shrpx_client_handler.h" +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_worker.h" + +namespace shrpx { + +APIDownstreamConnection::APIDownstreamConnection(Worker *worker) + : worker_(worker) {} + +APIDownstreamConnection::~APIDownstreamConnection() {} + +int APIDownstreamConnection::attach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; + } + + auto &req = downstream->request(); + + if (req.path != StringRef::from_lit("/api/v1/dynamicconfig")) { + // TODO this will return 503 error, which is not nice. We'd like + // to use 404 in this case. + return -1; + } + + downstream_ = downstream; + + return 0; +} + +void APIDownstreamConnection::detach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; + } + downstream_ = nullptr; +} + +int APIDownstreamConnection::push_request_headers() { return 0; } + +int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, + size_t datalen) { + auto output = downstream_->get_request_buf(); + + // TODO limit the maximum payload size + output->append(data, datalen); + + // We don't have to call Upstream::resume_read() here, because + // request buffer is effectively unlimited. Actually, we cannot + // call it here since it could recursively call this function again. + + return 0; +} + +int APIDownstreamConnection::end_upload_data() { + // TODO process request payload here + (void)worker_; + return 0; +} + +void APIDownstreamConnection::pause_read(IOCtrlReason reason) {} + +int APIDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { + return 0; +} + +void APIDownstreamConnection::force_resume_read() {} + +int APIDownstreamConnection::on_read() { return 0; } + +int APIDownstreamConnection::on_write() { return 0; } + +void APIDownstreamConnection::on_upstream_change(Upstream *uptream) {} + +bool APIDownstreamConnection::poolable() const { return false; } + +DownstreamAddrGroup * +APIDownstreamConnection::get_downstream_addr_group() const { + return nullptr; +} + +} // namespace shrpx diff --git a/src/shrpx_api_downstream_connection.h b/src/shrpx_api_downstream_connection.h new file mode 100644 index 00000000..1bd6c1b6 --- /dev/null +++ b/src/shrpx_api_downstream_connection.h @@ -0,0 +1,65 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2016 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SHRPX_API_DOWNSTREAM_CONNECTION_H +#define SHRPX_API_DOWNSTREAM_CONNECTION_H + +#include "shrpx_downstream_connection.h" + +namespace shrpx { + +class Worker; + +class APIDownstreamConnection : public DownstreamConnection { +public: + APIDownstreamConnection(Worker *worker); + virtual ~APIDownstreamConnection(); + virtual int attach_downstream(Downstream *downstream); + virtual void detach_downstream(Downstream *downstream); + + virtual int push_request_headers(); + virtual int push_upload_data_chunk(const uint8_t *data, size_t datalen); + virtual int end_upload_data(); + + virtual void pause_read(IOCtrlReason reason); + virtual int resume_read(IOCtrlReason reason, size_t consumed); + virtual void force_resume_read(); + + virtual int on_read(); + virtual int on_write(); + + virtual void on_upstream_change(Upstream *uptream); + + // true if this object is poolable. + virtual bool poolable() const; + + virtual DownstreamAddrGroup *get_downstream_addr_group() const; + +private: + Worker *worker_; +}; + +} // namespace shrpx + +#endif // SHRPX_API_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index a2083b38..2c27e700 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -48,6 +48,7 @@ #include "shrpx_downstream.h" #include "shrpx_http2_session.h" #include "shrpx_connect_blocker.h" +#include "shrpx_api_downstream_connection.h" #ifdef HAVE_SPDYLAY #include "shrpx_spdy_upstream.h" #endif // HAVE_SPDYLAY @@ -820,6 +821,10 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { const auto &req = downstream->request(); + if (faddr_->api) { + return make_unique(worker_); + } + // Fast path. If we have one group, it must be catch-all group. // proxy mode falls in this case. if (groups.size() == 1) { diff --git a/src/shrpx_config.h b/src/shrpx_config.h index dfd5997a..8376516e 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -540,6 +540,7 @@ struct Http2Config { ev_tstamp settings; } timeout; nghttp2_option *option; + nghttp2_option *api_option; nghttp2_session_callbacks *callbacks; size_t window_bits; size_t connection_window_bits; diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index b8d16066..3e1d7ba0 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -500,6 +500,14 @@ bool Downstream::get_chunked_request() const { return chunked_request_; } void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } bool Downstream::request_buf_full() { + auto handler = upstream_->get_client_handler(); + auto faddr = handler->get_upstream_addr(); + + // We don't check buffer size here for API endpoint. + if (faddr->api) { + return false; + } + if (dconn_) { return request_buf_.rleft() >= get_config()->conn.downstream.request_buffer_size; diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 6c8cc6f8..b05296e7 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -861,8 +861,11 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) auto &http2conf = get_config()->http2; - rv = nghttp2_session_server_new2(&session_, http2conf.upstream.callbacks, - this, http2conf.upstream.option); + auto faddr = handler_->get_upstream_addr(); + + rv = nghttp2_session_server_new2( + &session_, http2conf.upstream.callbacks, this, + faddr->api ? http2conf.upstream.api_option : http2conf.upstream.option); assert(rv == 0); @@ -874,7 +877,11 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) entry[0].value = http2conf.upstream.max_concurrent_streams; entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - entry[1].value = (1 << http2conf.upstream.window_bits) - 1; + if (faddr->api) { + entry[1].value = (1u << 31) - 1; + } else { + entry[1].value = (1 << http2conf.upstream.window_bits) - 1; + } rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), entry.size()); @@ -883,8 +890,11 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) << nghttp2_strerror(rv); } - if (http2conf.upstream.connection_window_bits != 16) { - int32_t window_size = (1 << http2conf.upstream.connection_window_bits) - 1; + int32_t window_bits = + faddr->api ? 31 : http2conf.upstream.connection_window_bits; + + if (window_bits != 16) { + int32_t window_size = (1u << window_bits) - 1; rv = nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0, window_size); @@ -1675,6 +1685,12 @@ int Http2Upstream::on_downstream_abort_request(Downstream *downstream, int Http2Upstream::consume(int32_t stream_id, size_t len) { int rv; + auto faddr = handler_->get_upstream_addr(); + + if (faddr->api) { + return 0; + } + rv = nghttp2_session_consume(session_, stream_id, len); if (rv != 0) { diff --git a/src/template.h b/src/template.h index 55491dc2..f7e8cd86 100644 --- a/src/template.h +++ b/src/template.h @@ -502,6 +502,10 @@ inline bool operator==(const char *lhs, const StringRef &rhs) { return rhs == lhs; } +inline bool operator!=(const StringRef &lhs, const StringRef &rhs) { + return !(lhs == rhs); +} + inline bool operator!=(const StringRef &lhs, const std::string &rhs) { return !(lhs == rhs); } From 09150a7927769fcbbb67e05eaf23adaecbcd6665 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 2 Jun 2016 23:59:59 +0900 Subject: [PATCH 03/24] nghttpx: Pass pointer to Config object to store parsed configurations --- src/shrpx.cc | 2 +- src/shrpx_config.cc | 279 ++++++++++++++++++++------------------------ src/shrpx_config.h | 10 +- 3 files changed, 135 insertions(+), 156 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 26fd846c..018073bd 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -2046,7 +2046,7 @@ void process_options(int argc, char **argv, std::set include_set; for (auto &p : cmdcfgs) { - if (parse_config(p.first, p.second, include_set) == -1) { + if (parse_config(mod_config(), p.first, p.second, include_set) == -1) { LOG(FATAL) << "Failed to parse command-line argument."; exit(EXIT_FAILURE); } diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 01ef486d..5dba902e 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -739,13 +739,13 @@ namespace { // as catch-all. We also parse protocol specified in |src_proto|. // // This function returns 0 if it succeeds, or -1. -int parse_mapping(DownstreamAddrConfig addr, const StringRef &src_pattern, - const StringRef &src_params) { +int parse_mapping(Config *config, DownstreamAddrConfig addr, + const StringRef &src_pattern, const StringRef &src_params) { // This returns at least 1 element (it could be empty string). We // will append '/' to all patterns, so it becomes catch-all pattern. auto mapping = util::split_str(src_pattern, ':'); assert(!mapping.empty()); - auto &addr_groups = mod_config()->conn.downstream.addr_groups; + auto &addr_groups = config->conn.downstream.addr_groups; DownstreamParams params{}; params.proto = PROTO_HTTP1; @@ -796,23 +796,23 @@ int parse_mapping(DownstreamAddrConfig addr, const StringRef &src_pattern, auto host = StringRef{std::begin(g.pattern) + 1, path_first}; auto path = StringRef{path_first, std::end(g.pattern)}; - auto &wildcard_patterns = mod_config()->wildcard_patterns; + auto &wildcard_patterns = config->wildcard_patterns; auto it = std::find_if( std::begin(wildcard_patterns), std::end(wildcard_patterns), [&host](const WildcardPattern &wp) { return wp.host == host; }); if (it == std::end(wildcard_patterns)) { - mod_config()->wildcard_patterns.push_back( + config->wildcard_patterns.push_back( {ImmutableString{std::begin(host), std::end(host)}}); - auto &router = mod_config()->wildcard_patterns.back().router; + auto &router = config->wildcard_patterns.back().router; router.add_route(path, addr_groups.size()); } else { (*it).router.add_route(path, addr_groups.size()); } } else { - mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size()); + config->router.add_route(StringRef{g.pattern}, addr_groups.size()); } addr_groups.push_back(std::move(g)); @@ -1780,7 +1780,7 @@ int option_lookup_token(const char *name, size_t namelen) { } } // namespace -int parse_config(const StringRef &opt, const StringRef &optarg, +int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, std::set &included_set) { char host[NI_MAXHOST]; uint16_t port; @@ -1812,7 +1812,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, auto params = mapping_end == std::end(optarg) ? mapping_end : mapping_end + 1; - if (parse_mapping(addr, StringRef{mapping, mapping_end}, + if (parse_mapping(config, addr, StringRef{mapping, mapping_end}, StringRef{params, std::end(optarg)}) != 0) { return -1; } @@ -1820,7 +1820,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return 0; } case SHRPX_OPTID_FRONTEND: { - auto &listenerconf = mod_config()->conn.listener; + auto &listenerconf = config->conn.listener; auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';'); auto src_params = StringRef{addr_end, std::end(optarg)}; @@ -1879,8 +1879,8 @@ int parse_config(const StringRef &opt, const StringRef &optarg, #ifdef NOTHREADS LOG(WARN) << "Threading disabled at build time, no threads created."; return 0; -#else // !NOTHREADS - return parse_uint(&mod_config()->num_worker, opt, optarg); +#else // !NOTHREADS + return parse_uint(&config->num_worker, opt, optarg); #endif // !NOTHREADS case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: { LOG(WARN) << opt << ": deprecated. Use " @@ -1890,7 +1890,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, if (parse_uint(&n, opt, optarg) != 0) { return -1; } - auto &http2conf = mod_config()->http2; + auto &http2conf = config->http2; http2conf.upstream.max_concurrent_streams = n; http2conf.downstream.max_concurrent_streams = n; @@ -1904,11 +1904,11 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return 0; case SHRPX_OPTID_DAEMON: - mod_config()->daemon = util::strieq_l("yes", optarg); + config->daemon = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_HTTP2_PROXY: - mod_config()->http2_proxy = util::strieq_l("yes", optarg); + config->http2_proxy = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_HTTP2_BRIDGE: @@ -1922,58 +1922,52 @@ int parse_config(const StringRef &opt, const StringRef &optarg, "and backend=,;;proto=h2;tls"; return -1; case SHRPX_OPTID_ADD_X_FORWARDED_FOR: - mod_config()->http.xff.add = util::strieq_l("yes", optarg); + config->http.xff.add = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR: - mod_config()->http.xff.strip_incoming = util::strieq_l("yes", optarg); + config->http.xff.strip_incoming = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_NO_VIA: - mod_config()->http.no_via = util::strieq_l("yes", optarg); + config->http.no_via = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT: - return parse_duration(&mod_config()->conn.upstream.timeout.http2_read, opt, + return parse_duration(&config->conn.upstream.timeout.http2_read, opt, optarg); case SHRPX_OPTID_FRONTEND_READ_TIMEOUT: - return parse_duration(&mod_config()->conn.upstream.timeout.read, opt, - optarg); + return parse_duration(&config->conn.upstream.timeout.read, opt, optarg); case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT: - return parse_duration(&mod_config()->conn.upstream.timeout.write, opt, - optarg); + return parse_duration(&config->conn.upstream.timeout.write, opt, optarg); case SHRPX_OPTID_BACKEND_READ_TIMEOUT: - return parse_duration(&mod_config()->conn.downstream.timeout.read, opt, - optarg); + return parse_duration(&config->conn.downstream.timeout.read, opt, optarg); case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT: - return parse_duration(&mod_config()->conn.downstream.timeout.write, opt, - optarg); + return parse_duration(&config->conn.downstream.timeout.write, opt, optarg); case SHRPX_OPTID_STREAM_READ_TIMEOUT: - return parse_duration(&mod_config()->http2.timeout.stream_read, opt, - optarg); + return parse_duration(&config->http2.timeout.stream_read, opt, optarg); case SHRPX_OPTID_STREAM_WRITE_TIMEOUT: - return parse_duration(&mod_config()->http2.timeout.stream_write, opt, - optarg); + return parse_duration(&config->http2.timeout.stream_write, opt, optarg); case SHRPX_OPTID_ACCESSLOG_FILE: - mod_config()->logging.access.file = + config->logging.access.file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_ACCESSLOG_SYSLOG: - mod_config()->logging.access.syslog = util::strieq_l("yes", optarg); + config->logging.access.syslog = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_ACCESSLOG_FORMAT: - mod_config()->logging.access.format = parse_log_format(optarg); + config->logging.access.format = parse_log_format(optarg); return 0; case SHRPX_OPTID_ERRORLOG_FILE: - mod_config()->logging.error.file = + config->logging.error.file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_ERRORLOG_SYSLOG: - mod_config()->logging.error.syslog = util::strieq_l("yes", optarg); + config->logging.error.syslog = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_FASTOPEN: { @@ -1987,21 +1981,21 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - mod_config()->conn.listener.fastopen = n; + config->conn.listener.fastopen = n; return 0; } case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT: - return parse_duration(&mod_config()->conn.downstream.timeout.idle_read, opt, + return parse_duration(&config->conn.downstream.timeout.idle_read, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS: case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: { size_t *resp; if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) { - resp = &mod_config()->http2.upstream.window_bits; + resp = &config->http2.upstream.window_bits; } else { - resp = &mod_config()->http2.downstream.window_bits; + resp = &config->http2.downstream.window_bits; } errno = 0; @@ -2027,9 +2021,9 @@ int parse_config(const StringRef &opt, const StringRef &optarg, size_t *resp; if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) { - resp = &mod_config()->http2.upstream.connection_window_bits; + resp = &config->http2.upstream.connection_window_bits; } else { - resp = &mod_config()->http2.downstream.connection_window_bits; + resp = &config->http2.downstream.connection_window_bits; } errno = 0; @@ -2062,12 +2056,11 @@ int parse_config(const StringRef &opt, const StringRef &optarg, LOG(WARN) << opt << ": deprecated. Use sni keyword in --backend option. " "For now, all sni values of all backends are " "overridden by the given value " << optarg; - mod_config()->tls.backend_sni_name = optarg.str(); + config->tls.backend_sni_name = optarg.str(); return 0; case SHRPX_OPTID_PID_FILE: - mod_config()->pid_file = - ImmutableString{std::begin(optarg), std::end(optarg)}; + config->pid_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_USER: { @@ -2077,14 +2070,14 @@ int parse_config(const StringRef &opt, const StringRef &optarg, << strerror(errno); return -1; } - mod_config()->user = pwd->pw_name; - mod_config()->uid = pwd->pw_uid; - mod_config()->gid = pwd->pw_gid; + config->user = pwd->pw_name; + config->uid = pwd->pw_uid; + config->gid = pwd->pw_gid; return 0; } case SHRPX_OPTID_PRIVATE_KEY_FILE: - mod_config()->tls.private_key_file = + config->tls.private_key_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; @@ -2094,17 +2087,17 @@ int parse_config(const StringRef &opt, const StringRef &optarg, LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; return -1; } - mod_config()->tls.private_key_passwd = passwd; + config->tls.private_key_passwd = passwd; return 0; } case SHRPX_OPTID_CERTIFICATE_FILE: - mod_config()->tls.cert_file = + config->tls.cert_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_DH_PARAM_FILE: - mod_config()->tls.dh_param_file = + config->tls.dh_param_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; @@ -2130,8 +2123,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - mod_config()->tls.subcerts.emplace_back(private_key_file.str(), - cert_file.str()); + config->tls.subcerts.emplace_back(private_key_file.str(), cert_file.str()); return 0; } @@ -2141,7 +2133,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg; return -1; } - mod_config()->logging.syslog_facility = facility; + config->logging.syslog_facility = facility; return 0; } @@ -2157,13 +2149,12 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - mod_config()->conn.listener.backlog = n; + config->conn.listener.backlog = n; return 0; } case SHRPX_OPTID_CIPHERS: - mod_config()->tls.ciphers = - ImmutableString{std::begin(optarg), std::end(optarg)}; + config->tls.ciphers = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_CLIENT: @@ -2171,30 +2162,29 @@ int parse_config(const StringRef &opt, const StringRef &optarg, "backend=,;;proto=h2;tls"; return -1; case SHRPX_OPTID_INSECURE: - mod_config()->tls.insecure = util::strieq_l("yes", optarg); + config->tls.insecure = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_CACERT: - mod_config()->tls.cacert = - ImmutableString{std::begin(optarg), std::end(optarg)}; + config->tls.cacert = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_BACKEND_IPV4: LOG(WARN) << opt << ": deprecated. Use backend-address-family=IPv4 instead."; - mod_config()->conn.downstream.family = AF_INET; + config->conn.downstream.family = AF_INET; return 0; case SHRPX_OPTID_BACKEND_IPV6: LOG(WARN) << opt << ": deprecated. Use backend-address-family=IPv6 instead."; - mod_config()->conn.downstream.family = AF_INET6; + config->conn.downstream.family = AF_INET6; return 0; case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: { - auto &proxy = mod_config()->downstream_http_proxy; + auto &proxy = config->downstream_http_proxy; // Reset here so that multiple option occurrence does not merge // the results. proxy = {}; @@ -2230,17 +2220,17 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return 0; } case SHRPX_OPTID_READ_RATE: - return parse_uint_with_unit( - &mod_config()->conn.upstream.ratelimit.read.rate, opt, optarg); + return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.rate, opt, + optarg); case SHRPX_OPTID_READ_BURST: - return parse_uint_with_unit( - &mod_config()->conn.upstream.ratelimit.read.burst, opt, optarg); + return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.burst, + opt, optarg); case SHRPX_OPTID_WRITE_RATE: - return parse_uint_with_unit( - &mod_config()->conn.upstream.ratelimit.write.rate, opt, optarg); + return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.rate, + opt, optarg); case SHRPX_OPTID_WRITE_BURST: - return parse_uint_with_unit( - &mod_config()->conn.upstream.ratelimit.write.burst, opt, optarg); + return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.burst, + opt, optarg); case SHRPX_OPTID_WORKER_READ_RATE: LOG(WARN) << opt << ": not implemented yet"; return 0; @@ -2254,53 +2244,52 @@ int parse_config(const StringRef &opt, const StringRef &optarg, LOG(WARN) << opt << ": not implemented yet"; return 0; case SHRPX_OPTID_NPN_LIST: - mod_config()->tls.npn_list = util::parse_config_str_list(optarg); + config->tls.npn_list = util::parse_config_str_list(optarg); return 0; case SHRPX_OPTID_TLS_PROTO_LIST: - mod_config()->tls.tls_proto_list = util::parse_config_str_list(optarg); + config->tls.tls_proto_list = util::parse_config_str_list(optarg); return 0; case SHRPX_OPTID_VERIFY_CLIENT: - mod_config()->tls.client_verify.enabled = util::strieq_l("yes", optarg); + config->tls.client_verify.enabled = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_VERIFY_CLIENT_CACERT: - mod_config()->tls.client_verify.cacert = + config->tls.client_verify.cacert = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE: - mod_config()->tls.client.private_key_file = + config->tls.client.private_key_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_CLIENT_CERT_FILE: - mod_config()->tls.client.cert_file = + config->tls.client.cert_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: - mod_config()->http2.upstream.debug.dump.request_header_file = + config->http2.upstream.debug.dump.request_header_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER: - mod_config()->http2.upstream.debug.dump.response_header_file = + config->http2.upstream.debug.dump.response_header_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING: - mod_config()->http2.no_cookie_crumbling = util::strieq_l("yes", optarg); + config->http2.no_cookie_crumbling = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_FRONTEND_FRAME_DEBUG: - mod_config()->http2.upstream.debug.frame_debug = - util::strieq_l("yes", optarg); + config->http2.upstream.debug.frame_debug = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_PADDING: - return parse_uint(&mod_config()->padding, opt, optarg); + return parse_uint(&config->padding, opt, optarg); case SHRPX_OPTID_ALTSVC: { auto tokens = util::split_str(optarg, ','); @@ -2343,7 +2332,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, } } - mod_config()->http.altsvcs.push_back(std::move(altsvc)); + config->http.altsvcs.push_back(std::move(altsvc)); return 0; } @@ -2355,17 +2344,16 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) { - mod_config()->http.add_request_headers.push_back(std::move(p)); + config->http.add_request_headers.push_back(std::move(p)); } else { - mod_config()->http.add_response_headers.push_back(std::move(p)); + config->http.add_response_headers.push_back(std::move(p)); } return 0; } case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS: - return parse_uint(&mod_config()->conn.upstream.worker_connections, opt, - optarg); + return parse_uint(&config->conn.upstream.worker_connections, opt, optarg); case SHRPX_OPTID_NO_LOCATION_REWRITE: - mod_config()->http.no_location_rewrite = util::strieq_l("yes", optarg); + config->http.no_location_rewrite = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_NO_HOST_REWRITE: @@ -2392,7 +2380,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - mod_config()->conn.downstream.connections_per_host = n; + config->conn.downstream.connections_per_host = n; return 0; } @@ -2401,13 +2389,12 @@ int parse_config(const StringRef &opt, const StringRef &optarg, << SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead."; // fall through case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND: - return parse_uint(&mod_config()->conn.downstream.connections_per_frontend, - opt, optarg); + return parse_uint(&config->conn.downstream.connections_per_frontend, opt, + optarg); case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: - return parse_duration(&mod_config()->conn.listener.timeout.sleep, opt, - optarg); + return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg); case SHRPX_OPTID_TLS_TICKET_KEY_FILE: - mod_config()->tls.ticket.files.push_back(optarg.str()); + config->tls.ticket.files.push_back(optarg.str()); return 0; case SHRPX_OPTID_RLIMIT_NOFILE: { int n; @@ -2422,7 +2409,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - mod_config()->rlimit_nofile = n; + config->rlimit_nofile = n; return 0; } @@ -2440,30 +2427,30 @@ int parse_config(const StringRef &opt, const StringRef &optarg, } if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) { - mod_config()->conn.downstream.request_buffer_size = n; + config->conn.downstream.request_buffer_size = n; } else { - mod_config()->conn.downstream.response_buffer_size = n; + config->conn.downstream.response_buffer_size = n; } return 0; } case SHRPX_OPTID_NO_SERVER_PUSH: - mod_config()->http2.no_server_push = util::strieq_l("yes", optarg); + config->http2.no_server_push = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER: LOG(WARN) << opt << ": deprecated."; return 0; case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: - mod_config()->tls.ocsp.fetch_ocsp_response_file = + config->tls.ocsp.fetch_ocsp_response_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_OCSP_UPDATE_INTERVAL: - return parse_duration(&mod_config()->tls.ocsp.update_interval, opt, optarg); + return parse_duration(&config->tls.ocsp.update_interval, opt, optarg); case SHRPX_OPTID_NO_OCSP: - mod_config()->tls.ocsp.disabled = util::strieq_l("yes", optarg); + config->tls.ocsp.disabled = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_HEADER_FIELD_BUFFER: @@ -2471,20 +2458,18 @@ int parse_config(const StringRef &opt, const StringRef &optarg, << ": deprecated. Use request-header-field-buffer instead."; // fall through case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER: - return parse_uint_with_unit(&mod_config()->http.request_header_field_buffer, - opt, optarg); + return parse_uint_with_unit(&config->http.request_header_field_buffer, opt, + optarg); case SHRPX_OPTID_MAX_HEADER_FIELDS: LOG(WARN) << opt << ": deprecated. Use max-request-header-fields instead."; // fall through case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS: - return parse_uint(&mod_config()->http.max_request_header_fields, opt, - optarg); + return parse_uint(&config->http.max_request_header_fields, opt, optarg); case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER: - return parse_uint_with_unit( - &mod_config()->http.response_header_field_buffer, opt, optarg); + return parse_uint_with_unit(&config->http.response_header_field_buffer, opt, + optarg); case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS: - return parse_uint(&mod_config()->http.max_response_header_fields, opt, - optarg); + return parse_uint(&config->http.max_response_header_fields, opt, optarg); case SHRPX_OPTID_INCLUDE: { if (included_set.count(optarg)) { LOG(ERROR) << opt << ": " << optarg << " has already been included"; @@ -2503,19 +2488,19 @@ int parse_config(const StringRef &opt, const StringRef &optarg, } case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER: if (util::strieq_l("aes-128-cbc", optarg)) { - mod_config()->tls.ticket.cipher = EVP_aes_128_cbc(); + config->tls.ticket.cipher = EVP_aes_128_cbc(); } else if (util::strieq_l("aes-256-cbc", optarg)) { - mod_config()->tls.ticket.cipher = EVP_aes_256_cbc(); + config->tls.ticket.cipher = EVP_aes_256_cbc(); } else { LOG(ERROR) << opt << ": unsupported cipher for ticket encryption: " << optarg; return -1; } - mod_config()->tls.ticket.cipher_given = true; + config->tls.ticket.cipher_given = true; return 0; case SHRPX_OPTID_HOST_REWRITE: - mod_config()->http.no_host_rewrite = !util::strieq_l("yes", optarg); + config->http.no_host_rewrite = !util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: @@ -2536,14 +2521,14 @@ int parse_config(const StringRef &opt, const StringRef &optarg, switch (optid) { case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: { - auto &memcachedconf = mod_config()->tls.session_cache.memcached; + auto &memcachedconf = config->tls.session_cache.memcached; memcachedconf.host = host; memcachedconf.port = port; memcachedconf.tls = params.tls; break; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: { - auto &memcachedconf = mod_config()->tls.ticket.memcached; + auto &memcachedconf = config->tls.ticket.memcached; memcachedconf.host = host; memcachedconf.port = port; memcachedconf.tls = params.tls; @@ -2554,8 +2539,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL: - return parse_duration(&mod_config()->tls.ticket.memcached.interval, opt, - optarg); + return parse_duration(&config->tls.ticket.memcached.interval, opt, optarg); case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: { int n; if (parse_uint(&n, opt, optarg) != 0) { @@ -2567,42 +2551,39 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - mod_config()->tls.ticket.memcached.max_retry = n; + config->tls.ticket.memcached.max_retry = n; return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL: - return parse_uint(&mod_config()->tls.ticket.memcached.max_fail, opt, - optarg); + return parse_uint(&config->tls.ticket.memcached.max_fail, opt, optarg); case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: { size_t n; if (parse_uint_with_unit(&n, opt, optarg) != 0) { return -1; } - mod_config()->tls.dyn_rec.warmup_threshold = n; + config->tls.dyn_rec.warmup_threshold = n; return 0; } case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT: - return parse_duration(&mod_config()->tls.dyn_rec.idle_timeout, opt, optarg); + return parse_duration(&config->tls.dyn_rec.idle_timeout, opt, optarg); case SHRPX_OPTID_MRUBY_FILE: #ifdef HAVE_MRUBY - mod_config()->mruby_file = - ImmutableString{std::begin(optarg), std::end(optarg)}; + config->mruby_file = ImmutableString{std::begin(optarg), std::end(optarg)}; #else // !HAVE_MRUBY LOG(WARN) << opt << ": ignored because mruby support is disabled at build time."; #endif // !HAVE_MRUBY return 0; case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL: - mod_config()->conn.upstream.accept_proxy_protocol = - util::strieq_l("yes", optarg); + config->conn.upstream.accept_proxy_protocol = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_ADD_FORWARDED: { - auto &fwdconf = mod_config()->http.forwarded; + auto &fwdconf = config->http.forwarded; fwdconf.params = FORWARDED_NONE; for (const auto ¶m : util::split_str(optarg, ',')) { if (util::strieq_l("by", param)) { @@ -2630,7 +2611,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return 0; } case SHRPX_OPTID_STRIP_INCOMING_FORWARDED: - mod_config()->http.forwarded.strip_incoming = util::strieq_l("yes", optarg); + config->http.forwarded.strip_incoming = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_FORWARDED_BY: @@ -2644,7 +2625,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return -1; } - auto &fwdconf = mod_config()->http.forwarded; + auto &fwdconf = config->http.forwarded; switch (optid) { case SHRPX_OPTID_FORWARDED_BY: @@ -2663,8 +2644,7 @@ int parse_config(const StringRef &opt, const StringRef &optarg, return 0; } case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST: - mod_config()->tls.no_http2_cipher_black_list = - util::strieq_l("yes", optarg); + config->tls.no_http2_cipher_black_list = util::strieq_l("yes", optarg); return 0; case SHRPX_OPTID_BACKEND_HTTP1_TLS: @@ -2677,12 +2657,12 @@ int parse_config(const StringRef &opt, const StringRef &optarg, << SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED; return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE: - mod_config()->tls.session_cache.memcached.cert_file = + config->tls.session_cache.memcached.cert_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE: - mod_config()->tls.session_cache.memcached.private_key_file = + config->tls.session_cache.memcached.private_key_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; @@ -2691,46 +2671,45 @@ int parse_config(const StringRef &opt, const StringRef &optarg, << SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED; return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE: - mod_config()->tls.ticket.memcached.cert_file = + config->tls.ticket.memcached.cert_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE: - mod_config()->tls.ticket.memcached.private_key_file = + config->tls.ticket.memcached.private_key_file = ImmutableString{std::begin(optarg), std::end(optarg)}; return 0; case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY: - return parse_address_family(&mod_config()->tls.ticket.memcached.family, opt, + return parse_address_family(&config->tls.ticket.memcached.family, opt, optarg); case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY: - return parse_address_family( - &mod_config()->tls.session_cache.memcached.family, opt, optarg); + return parse_address_family(&config->tls.session_cache.memcached.family, + opt, optarg); case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY: - return parse_address_family(&mod_config()->conn.downstream.family, opt, - optarg); + return parse_address_family(&config->conn.downstream.family, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS: - return parse_uint(&mod_config()->http2.upstream.max_concurrent_streams, opt, + return parse_uint(&config->http2.upstream.max_concurrent_streams, opt, optarg); case SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS: - return parse_uint(&mod_config()->http2.downstream.max_concurrent_streams, - opt, optarg); + return parse_uint(&config->http2.downstream.max_concurrent_streams, opt, + optarg); case SHRPX_OPTID_ERROR_PAGE: - return parse_error_page(mod_config()->http.error_pages, opt, optarg); + return parse_error_page(config->http.error_pages, opt, optarg); case SHRPX_OPTID_NO_KQUEUE: if ((ev_supported_backends() & EVBACKEND_KQUEUE) == 0) { LOG(WARN) << opt << ": kqueue is not supported on this platform"; return 0; } - mod_config()->ev_loop_flags = ev_recommended_backends() & ~EVBACKEND_KQUEUE; + config->ev_loop_flags = ev_recommended_backends() & ~EVBACKEND_KQUEUE; return 0; case SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT: - return parse_duration(&mod_config()->http2.upstream.timeout.settings, opt, + return parse_duration(&config->http2.upstream.timeout.settings, opt, optarg); case SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT: - return parse_duration(&mod_config()->http2.downstream.timeout.settings, opt, + return parse_duration(&config->http2.downstream.timeout.settings, opt, optarg); case SHRPX_OPTID_CONF: LOG(WARN) << "conf: ignored"; @@ -2764,7 +2743,7 @@ int load_config(const char *filename, std::set &include_set) { } *eq = '\0'; - if (parse_config(StringRef{std::begin(line), eq}, + if (parse_config(mod_config(), StringRef{std::begin(line), eq}, StringRef{eq + 1, std::end(line)}, include_set) != 0) { return -1; } diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 8376516e..477fa95e 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -674,11 +674,11 @@ Config *mod_config(); void create_config(); // Parses option name |opt| and value |optarg|. The results are -// stored into statically allocated Config object. This function -// returns 0 if it succeeds, or -1. The |included_set| contains the -// all paths already included while processing this configuration, to -// avoid loop in --include option. -int parse_config(const StringRef &opt, const StringRef &optarg, +// stored into the object pointed by |config|. This function returns 0 +// if it succeeds, or -1. The |included_set| contains the all paths +// already included while processing this configuration, to avoid loop +// in --include option. +int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, std::set &included_set); // Loads configurations from |filename| and stores them in statically From 2fd095d036ff5ed8a5acd8c62cec38fa2844ccb0 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 3 Jun 2016 00:22:55 +0900 Subject: [PATCH 04/24] nghttpx: Share the code to configure backends --- src/shrpx.cc | 185 +------------------------------------------ src/shrpx_config.cc | 189 ++++++++++++++++++++++++++++++++++++++++++++ src/shrpx_config.h | 10 +++ 3 files changed, 201 insertions(+), 183 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 018073bd..454206a4 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -146,52 +146,6 @@ struct SignalServer { pid_t worker_process_pid; }; -namespace { -int resolve_hostname(Address *addr, const char *hostname, uint16_t port, - int family) { - int rv; - - auto service = util::utos(port); - - addrinfo hints{}; - hints.ai_family = family; - hints.ai_socktype = SOCK_STREAM; -#ifdef AI_ADDRCONFIG - hints.ai_flags |= AI_ADDRCONFIG; -#endif // AI_ADDRCONFIG - addrinfo *res; - - rv = getaddrinfo(hostname, service.c_str(), &hints, &res); - if (rv != 0) { - LOG(FATAL) << "Unable to resolve address for " << hostname << ": " - << gai_strerror(rv); - return -1; - } - - auto res_d = defer(freeaddrinfo, res); - - char host[NI_MAXHOST]; - rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr, - 0, NI_NUMERICHOST); - if (rv != 0) { - LOG(FATAL) << "Address resolution for " << hostname - << " failed: " << gai_strerror(rv); - - return -1; - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Address resolution for " << hostname - << " succeeded: " << host; - } - - memcpy(&addr->su, res->ai_addr, res->ai_addrlen); - addr->len = res->ai_addrlen; - - return 0; -} -} // namespace - namespace { int chown_to_running_user(const char *path) { return chown(path, get_config()->uid, get_config()->gid); @@ -1075,11 +1029,6 @@ constexpr auto DEFAULT_ACCESSLOG_FORMAT = StringRef::from_lit( R"("$http_referer" "$http_user_agent")"); } // namespace -namespace { -constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1"; -constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80; -} // namespace; - namespace { void fill_default_config() { *mod_config() = {}; @@ -2151,7 +2100,6 @@ void process_options(int argc, char **argv, auto &listenerconf = mod_config()->conn.listener; auto &upstreamconf = mod_config()->conn.upstream; - auto &downstreamconf = mod_config()->conn.downstream; if (listenerconf.addrs.empty()) { UpstreamAddr addr{}; @@ -2185,140 +2133,11 @@ void process_options(int argc, char **argv, } } - auto &addr_groups = downstreamconf.addr_groups; - - if (addr_groups.empty()) { - DownstreamAddrConfig addr{}; - addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); - addr.port = DEFAULT_DOWNSTREAM_PORT; - addr.proto = PROTO_HTTP1; - - DownstreamAddrGroupConfig g(StringRef::from_lit("/")); - g.addrs.push_back(std::move(addr)); - mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size()); - addr_groups.push_back(std::move(g)); - } else if (get_config()->http2_proxy) { - // We don't support host mapping in these cases. Move all - // non-catch-all patterns to catch-all pattern. - DownstreamAddrGroupConfig catch_all(StringRef::from_lit("/")); - for (auto &g : addr_groups) { - std::move(std::begin(g.addrs), std::end(g.addrs), - std::back_inserter(catch_all.addrs)); - } - std::vector().swap(addr_groups); - std::vector().swap(mod_config()->wildcard_patterns); - // maybe not necessary? - mod_config()->router = Router(); - mod_config()->router.add_route(StringRef{catch_all.pattern}, - addr_groups.size()); - addr_groups.push_back(std::move(catch_all)); - } else { - auto &wildcard_patterns = mod_config()->wildcard_patterns; - std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns), - [](const WildcardPattern &lhs, const WildcardPattern &rhs) { - return std::lexicographical_compare( - rhs.host.rbegin(), rhs.host.rend(), lhs.host.rbegin(), - lhs.host.rend()); - }); - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, " - "and sorted in reverse order):"; - for (auto &wp : mod_config()->wildcard_patterns) { - LOG(INFO) << wp.host; - } - } - } - - // backward compatibility: override all SNI fields with the option - // value --backend-tls-sni-field - if (!tlsconf.backend_sni_name.empty()) { - auto &sni = tlsconf.backend_sni_name; - for (auto &addr_group : addr_groups) { - for (auto &addr : addr_group.addrs) { - addr.sni = sni; - } - } - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Resolving backend address"; - } - - ssize_t catch_all_group = -1; - for (size_t i = 0; i < addr_groups.size(); ++i) { - auto &g = addr_groups[i]; - if (g.pattern == StringRef::from_lit("/")) { - catch_all_group = i; - } - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern - << "'"; - for (auto &addr : g.addrs) { - LOG(INFO) << "group " << i << " -> " << addr.host.c_str() - << (addr.host_unix ? "" : ":" + util::utos(addr.port)) - << ", proto=" << strproto(addr.proto) - << (addr.tls ? ", tls" : ""); - } - } - } - - if (catch_all_group == -1) { - LOG(FATAL) << "backend: No catch-all backend address is configured"; + if (configure_downstream_group(mod_config(), get_config()->http2_proxy, false, + tlsconf) != 0) { exit(EXIT_FAILURE); } - downstreamconf.addr_group_catch_all = catch_all_group; - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Catch-all pattern is group " << catch_all_group; - } - - for (auto &g : addr_groups) { - for (auto &addr : g.addrs) { - - if (addr.host_unix) { - // for AF_UNIX socket, we use "localhost" as host for backend - // hostport. This is used as Host header field to backend and - // not going to be passed to any syscalls. - addr.hostport = "localhost"; - - auto path = addr.host.c_str(); - auto pathlen = addr.host.size(); - - if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { - LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " - << sizeof(addr.addr.su.un.sun_path); - exit(EXIT_FAILURE); - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Use UNIX domain socket path " << path - << " for backend connection"; - } - - addr.addr.su.un.sun_family = AF_UNIX; - // copy path including terminal NULL - std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path); - addr.addr.len = sizeof(addr.addr.su.un); - - continue; - } - - addr.hostport = ImmutableString( - util::make_http_hostport(StringRef(addr.host), addr.port)); - - auto hostport = util::make_hostport(StringRef{addr.host}, addr.port); - - if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, - downstreamconf.family) == -1) { - LOG(FATAL) << "Resolving backend address failed: " << hostport; - exit(EXIT_FAILURE); - } - LOG(NOTICE) << "Resolved backend address: " << hostport << " -> " - << util::to_numeric_addr(&addr.addr); - } - } - auto &proxy = mod_config()->downstream_http_proxy; if (!proxy.host.empty()) { auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port); diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 5dba902e..f7140ab1 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -2904,4 +2904,193 @@ StringRef strproto(shrpx_proto proto) { assert(0); } +// Configures the following member in |config|: router, +// conn.downstream.addr_groups, wildcard_patterns, +int configure_downstream_group(Config *config, bool http2_proxy, + bool numeric_addr_only, + const TLSConfig &tlsconf) { + auto &downstreamconf = config->conn.downstream; + auto &addr_groups = downstreamconf.addr_groups; + + if (addr_groups.empty()) { + DownstreamAddrConfig addr{}; + addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); + addr.port = DEFAULT_DOWNSTREAM_PORT; + addr.proto = PROTO_HTTP1; + + DownstreamAddrGroupConfig g(StringRef::from_lit("/")); + g.addrs.push_back(std::move(addr)); + config->router.add_route(StringRef{g.pattern}, addr_groups.size()); + addr_groups.push_back(std::move(g)); + } else if (http2_proxy) { + // We don't support host mapping in these cases. Move all + // non-catch-all patterns to catch-all pattern. + DownstreamAddrGroupConfig catch_all(StringRef::from_lit("/")); + for (auto &g : addr_groups) { + std::move(std::begin(g.addrs), std::end(g.addrs), + std::back_inserter(catch_all.addrs)); + } + std::vector().swap(addr_groups); + std::vector().swap(config->wildcard_patterns); + // maybe not necessary? + config->router = Router(); + config->router.add_route(StringRef{catch_all.pattern}, addr_groups.size()); + addr_groups.push_back(std::move(catch_all)); + } else { + auto &wildcard_patterns = config->wildcard_patterns; + std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns), + [](const WildcardPattern &lhs, const WildcardPattern &rhs) { + return std::lexicographical_compare( + rhs.host.rbegin(), rhs.host.rend(), lhs.host.rbegin(), + lhs.host.rend()); + }); + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, " + "and sorted in reverse order):"; + for (auto &wp : config->wildcard_patterns) { + LOG(INFO) << wp.host; + } + } + } + + // backward compatibility: override all SNI fields with the option + // value --backend-tls-sni-field + if (!tlsconf.backend_sni_name.empty()) { + auto &sni = tlsconf.backend_sni_name; + for (auto &addr_group : addr_groups) { + for (auto &addr : addr_group.addrs) { + addr.sni = sni; + } + } + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend address"; + } + + ssize_t catch_all_group = -1; + for (size_t i = 0; i < addr_groups.size(); ++i) { + auto &g = addr_groups[i]; + if (g.pattern == StringRef::from_lit("/")) { + catch_all_group = i; + } + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern + << "'"; + for (auto &addr : g.addrs) { + LOG(INFO) << "group " << i << " -> " << addr.host.c_str() + << (addr.host_unix ? "" : ":" + util::utos(addr.port)) + << ", proto=" << strproto(addr.proto) + << (addr.tls ? ", tls" : ""); + } + } + } + + if (catch_all_group == -1) { + LOG(FATAL) << "backend: No catch-all backend address is configured"; + return -1; + } + + downstreamconf.addr_group_catch_all = catch_all_group; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Catch-all pattern is group " << catch_all_group; + } + + auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST : 0; + + for (auto &g : addr_groups) { + for (auto &addr : g.addrs) { + + if (addr.host_unix) { + // for AF_UNIX socket, we use "localhost" as host for backend + // hostport. This is used as Host header field to backend and + // not going to be passed to any syscalls. + addr.hostport = "localhost"; + + auto path = addr.host.c_str(); + auto pathlen = addr.host.size(); + + if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.addr.su.un.sun_path); + return -1; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Use UNIX domain socket path " << path + << " for backend connection"; + } + + addr.addr.su.un.sun_family = AF_UNIX; + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path); + addr.addr.len = sizeof(addr.addr.su.un); + + continue; + } + + addr.hostport = ImmutableString( + util::make_http_hostport(StringRef(addr.host), addr.port)); + + auto hostport = util::make_hostport(StringRef{addr.host}, addr.port); + + if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, + downstreamconf.family, resolve_flags) == -1) { + LOG(FATAL) << "Resolving backend address failed: " << hostport; + return -1; + } + LOG(NOTICE) << "Resolved backend address: " << hostport << " -> " + << util::to_numeric_addr(&addr.addr); + } + } + + return 0; +} + +int resolve_hostname(Address *addr, const char *hostname, uint16_t port, + int family, int additional_flags) { + int rv; + + auto service = util::utos(port); + + addrinfo hints{}; + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= additional_flags; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif // AI_ADDRCONFIG + addrinfo *res; + + rv = getaddrinfo(hostname, service.c_str(), &hints, &res); + if (rv != 0) { + LOG(FATAL) << "Unable to resolve address for " << hostname << ": " + << gai_strerror(rv); + return -1; + } + + auto res_d = defer(freeaddrinfo, res); + + char host[NI_MAXHOST]; + rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr, + 0, NI_NUMERICHOST); + if (rv != 0) { + LOG(FATAL) << "Address resolution for " << hostname + << " failed: " << gai_strerror(rv); + + return -1; + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Address resolution for " << hostname + << " succeeded: " << host; + } + + memcpy(&addr->su, res->ai_addr, res->ai_addrlen); + addr->len = res->ai_addrlen; + + return 0; +} + } // namespace shrpx diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 477fa95e..605949a5 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -283,6 +283,9 @@ constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT = constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; +constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1"; +constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80; + enum shrpx_proto { PROTO_NONE, PROTO_HTTP1, PROTO_HTTP2, PROTO_MEMCACHED }; enum shrpx_forwarded_param { @@ -713,6 +716,13 @@ read_tls_ticket_key_file(const std::vector &files, // Returns string representation of |proto|. StringRef strproto(shrpx_proto proto); +int configure_downstream_group(Config *config, bool http2_proxy, + bool numeric_addr_only, + const TLSConfig &tlsconf); + +int resolve_hostname(Address *addr, const char *hostname, uint16_t port, + int family, int additional_flags = 0); + } // namespace shrpx #endif // SHRPX_CONFIG_H From fe58614b235d8ebf06cb2ea2f0948bd0d90264bc Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 3 Jun 2016 01:20:49 +0900 Subject: [PATCH 05/24] nghttpx: Use std::shared_ptr for downstream addresses so that we can swap them --- src/shrpx.cc | 1 + src/shrpx_client_handler.cc | 19 +++++----- src/shrpx_client_handler.h | 3 +- src/shrpx_config.cc | 30 +++++++++------- src/shrpx_config.h | 6 +++- src/shrpx_http2_session.cc | 9 ++--- src/shrpx_http2_session.h | 5 +-- src/shrpx_http_downstream_connection.cc | 8 ++--- src/shrpx_http_downstream_connection.h | 6 ++-- src/shrpx_worker.cc | 48 ++++++++++++++++--------- src/shrpx_worker.h | 14 ++++++-- 11 files changed, 93 insertions(+), 56 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 454206a4..093080f9 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1036,6 +1036,7 @@ void fill_default_config() { mod_config()->num_worker = 1; mod_config()->conf_path = "/etc/nghttpx/nghttpx.conf"; mod_config()->pid = getpid(); + mod_config()->downstream_router = std::make_shared(); if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) { mod_config()->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE; diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 2c27e700..db16d64d 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -690,8 +690,9 @@ bool load_lighter(const DownstreamAddr *lhs, const DownstreamAddr *rhs) { } } // namespace -Http2Session *ClientHandler::select_http2_session(DownstreamAddrGroup &group) { - auto &shared_addr = group.shared_addr; +Http2Session *ClientHandler::select_http2_session( + const std::shared_ptr &group) { + auto &shared_addr = group->shared_addr; // First count the working backend addresses. size_t min = 0; @@ -779,7 +780,7 @@ Http2Session *ClientHandler::select_http2_session(DownstreamAddrGroup &group) { } auto session = new Http2Session(conn_.loop, worker_->get_cl_ssl_ctx(), - worker_, &group, selected_addr); + worker_, group, selected_addr); if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "Create new Http2Session " << session; @@ -815,9 +816,9 @@ uint32_t next_cycle(const WeightedPri &pri) { std::unique_ptr ClientHandler::get_downstream_connection(Downstream *downstream) { size_t group_idx; - auto &downstreamconf = get_config()->conn.downstream; - auto catch_all = downstreamconf.addr_group_catch_all; + auto catch_all = worker_->get_addr_group_catch_all(); auto &groups = worker_->get_downstream_addr_groups(); + auto downstream_router = worker_->get_downstream_router(); const auto &req = downstream->request(); @@ -835,8 +836,8 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { // have dealt with proxy case already, just use catch-all group. group_idx = catch_all; } else { - auto &router = get_config()->router; - auto &wildcard_patterns = get_config()->wildcard_patterns; + auto &router = downstream_router->router; + auto &wildcard_patterns = downstream_router->wildcard_patterns; if (!req.authority.empty()) { group_idx = match_downstream_addr_group(router, wildcard_patterns, req.authority, @@ -859,7 +860,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { } auto &group = worker_->get_downstream_addr_groups()[group_idx]; - auto &shared_addr = group.shared_addr; + auto &shared_addr = group->shared_addr; auto proto = PROTO_NONE; @@ -925,7 +926,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { << " Create new one"; } - dconn = make_unique(&group, conn_.loop, worker_); + dconn = make_unique(group, conn_.loop, worker_); } dconn->set_client_handler(this); diff --git a/src/shrpx_client_handler.h b/src/shrpx_client_handler.h index f9ae6615..93f7b845 100644 --- a/src/shrpx_client_handler.h +++ b/src/shrpx_client_handler.h @@ -142,7 +142,8 @@ public: // header field. StringRef get_forwarded_for() const; - Http2Session *select_http2_session(DownstreamAddrGroup &group); + Http2Session * + select_http2_session(const std::shared_ptr &group); const UpstreamAddr *get_upstream_addr() const; diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index f7140ab1..732178eb 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -788,6 +788,8 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, DownstreamAddrGroupConfig g(StringRef{pattern}); g.addrs.push_back(addr); + auto &downstream_router = config->downstream_router; + if (pattern[0] == '*') { // wildcard pattern auto path_first = @@ -796,23 +798,24 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, auto host = StringRef{std::begin(g.pattern) + 1, path_first}; auto path = StringRef{path_first, std::end(g.pattern)}; - auto &wildcard_patterns = config->wildcard_patterns; + auto &wildcard_patterns = downstream_router->wildcard_patterns; auto it = std::find_if( std::begin(wildcard_patterns), std::end(wildcard_patterns), [&host](const WildcardPattern &wp) { return wp.host == host; }); if (it == std::end(wildcard_patterns)) { - config->wildcard_patterns.push_back( + wildcard_patterns.push_back( {ImmutableString{std::begin(host), std::end(host)}}); - auto &router = config->wildcard_patterns.back().router; + auto &router = wildcard_patterns.back().router; router.add_route(path, addr_groups.size()); } else { (*it).router.add_route(path, addr_groups.size()); } } else { - config->router.add_route(StringRef{g.pattern}, addr_groups.size()); + downstream_router->router.add_route(StringRef{g.pattern}, + addr_groups.size()); } addr_groups.push_back(std::move(g)); @@ -2904,13 +2907,15 @@ StringRef strproto(shrpx_proto proto) { assert(0); } -// Configures the following member in |config|: router, -// conn.downstream.addr_groups, wildcard_patterns, +// Configures the following member in |config|: +// conn.downstream_router, conn.downstream.addr_groups, +// conn.downstream.addr_group_catch_all. int configure_downstream_group(Config *config, bool http2_proxy, bool numeric_addr_only, const TLSConfig &tlsconf) { auto &downstreamconf = config->conn.downstream; auto &addr_groups = downstreamconf.addr_groups; + auto &downstream_router = config->downstream_router; if (addr_groups.empty()) { DownstreamAddrConfig addr{}; @@ -2920,7 +2925,8 @@ int configure_downstream_group(Config *config, bool http2_proxy, DownstreamAddrGroupConfig g(StringRef::from_lit("/")); g.addrs.push_back(std::move(addr)); - config->router.add_route(StringRef{g.pattern}, addr_groups.size()); + downstream_router->router.add_route(StringRef{g.pattern}, + addr_groups.size()); addr_groups.push_back(std::move(g)); } else if (http2_proxy) { // We don't support host mapping in these cases. Move all @@ -2931,13 +2937,13 @@ int configure_downstream_group(Config *config, bool http2_proxy, std::back_inserter(catch_all.addrs)); } std::vector().swap(addr_groups); - std::vector().swap(config->wildcard_patterns); // maybe not necessary? - config->router = Router(); - config->router.add_route(StringRef{catch_all.pattern}, addr_groups.size()); + downstream_router = std::make_shared(); + downstream_router->router.add_route(StringRef{catch_all.pattern}, + addr_groups.size()); addr_groups.push_back(std::move(catch_all)); } else { - auto &wildcard_patterns = config->wildcard_patterns; + auto &wildcard_patterns = downstream_router->wildcard_patterns; std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns), [](const WildcardPattern &lhs, const WildcardPattern &rhs) { return std::lexicographical_compare( @@ -2947,7 +2953,7 @@ int configure_downstream_group(Config *config, bool http2_proxy, if (LOG_ENABLED(INFO)) { LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, " "and sorted in reverse order):"; - for (auto &wp : config->wildcard_patterns) { + for (auto &wp : wildcard_patterns) { LOG(INFO) << wp.host; } } diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 605949a5..2efbb19c 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -642,9 +642,13 @@ struct WildcardPattern { Router router; }; -struct Config { +struct DownstreamRouter { Router router; std::vector wildcard_patterns; +}; + +struct Config { + std::shared_ptr downstream_router; HttpProxy downstream_http_proxy; HttpConfig http; Http2Config http2; diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index bba56fa3..8a8f9b3b 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -172,7 +172,8 @@ void initiate_connection_cb(struct ev_loop *loop, ev_timer *w, int revents) { } // namespace Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, - Worker *worker, DownstreamAddrGroup *group, + Worker *worker, + const std::shared_ptr &group, DownstreamAddr *addr) : dlnext(nullptr), dlprev(nullptr), @@ -2111,7 +2112,7 @@ bool Http2Session::max_concurrency_reached(size_t extra) const { } DownstreamAddrGroup *Http2Session::get_downstream_addr_group() const { - return group_; + return group_.get(); } void Http2Session::add_to_avail_freelist() { @@ -2120,8 +2121,8 @@ void Http2Session::add_to_avail_freelist() { } if (LOG_ENABLED(INFO)) { - SSLOG(INFO, this) << "Append to http2_avail_freelist, group=" << group_ - << ", freelist.size=" + SSLOG(INFO, this) << "Append to http2_avail_freelist, group=" + << group_.get() << ", freelist.size=" << group_->shared_addr->http2_avail_freelist.size(); } diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h index 22ee392b..318d87cb 100644 --- a/src/shrpx_http2_session.h +++ b/src/shrpx_http2_session.h @@ -73,7 +73,8 @@ enum FreelistZone { class Http2Session { public: Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, - DownstreamAddrGroup *group, DownstreamAddr *addr); + const std::shared_ptr &group, + DownstreamAddr *addr); ~Http2Session(); // If hard is true, all pending requests are abandoned and @@ -250,7 +251,7 @@ private: Worker *worker_; // NULL if no TLS is configured SSL_CTX *ssl_ctx_; - DownstreamAddrGroup *group_; + std::shared_ptr group_; // Address of remote endpoint DownstreamAddr *addr_; nghttp2_session *session_; diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 7f515872..63c99a72 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -147,9 +147,9 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) { } } // namespace -HttpDownstreamConnection::HttpDownstreamConnection(DownstreamAddrGroup *group, - struct ev_loop *loop, - Worker *worker) +HttpDownstreamConnection::HttpDownstreamConnection( + const std::shared_ptr &group, struct ev_loop *loop, + Worker *worker) : conn_(loop, -1, nullptr, worker->get_mcpool(), get_config()->conn.downstream.timeout.write, get_config()->conn.downstream.timeout.read, {}, {}, connectcb, @@ -1164,7 +1164,7 @@ int HttpDownstreamConnection::noop() { return 0; } DownstreamAddrGroup * HttpDownstreamConnection::get_downstream_addr_group() const { - return group_; + return group_.get(); } DownstreamAddr *HttpDownstreamConnection::get_addr() const { return addr_; } diff --git a/src/shrpx_http_downstream_connection.h b/src/shrpx_http_downstream_connection.h index 3cca894d..cebed9f0 100644 --- a/src/shrpx_http_downstream_connection.h +++ b/src/shrpx_http_downstream_connection.h @@ -42,8 +42,8 @@ struct DownstreamAddr; class HttpDownstreamConnection : public DownstreamConnection { public: - HttpDownstreamConnection(DownstreamAddrGroup *group, struct ev_loop *loop, - Worker *worker); + HttpDownstreamConnection(const std::shared_ptr &group, + struct ev_loop *loop, Worker *worker); virtual ~HttpDownstreamConnection(); virtual int attach_downstream(Downstream *downstream); virtual void detach_downstream(Downstream *downstream); @@ -88,7 +88,7 @@ private: Worker *worker_; // nullptr if TLS is not used. SSL_CTX *ssl_ctx_; - DownstreamAddrGroup *group_; + const std::shared_ptr &group_; // Address of remote endpoint DownstreamAddr *addr_; IOControl ioctrl_; diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index cd09a78d..503a8a7e 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -109,6 +109,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, const std::shared_ptr &ticket_keys) : randgen_(rd()), worker_stat_{}, + downstream_router_(get_config()->downstream_router), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), @@ -117,6 +118,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, downstream_addr_groups_(get_config()->conn.downstream.addr_groups.size()), connect_blocker_( make_unique(randgen_, loop_, []() {}, []() {})), + addr_group_catch_all_(get_config()->conn.downstream.addr_group_catch_all), graceful_shutdown_(false) { ev_async_init(&w_, eventcb); w_.data = this; @@ -140,7 +142,8 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, auto &src = downstreamconf.addr_groups[i]; auto &dst = downstream_addr_groups_[i]; - dst.pattern = src.pattern; + dst = std::make_shared(); + dst->pattern = src.pattern; auto shared_addr = std::make_shared(); @@ -210,11 +213,11 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, // share the connection if patterns have the same set of backend // addresses. auto end = std::begin(downstream_addr_groups_) + i; - auto it = std::find_if(std::begin(downstream_addr_groups_), end, - [&shared_addr](const DownstreamAddrGroup &group) { - return match_shared_downstream_addr( - group.shared_addr, shared_addr); - }); + auto it = std::find_if( + std::begin(downstream_addr_groups_), end, + [&shared_addr](const std::shared_ptr &group) { + return match_shared_downstream_addr(group->shared_addr, shared_addr); + }); if (it == end) { if (LOG_ENABLED(INFO)) { @@ -225,13 +228,13 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, shared_addr->http1_pri.weight = num_http1; shared_addr->http2_pri.weight = num_http2; - dst.shared_addr = shared_addr; + dst->shared_addr = shared_addr; } else { if (LOG_ENABLED(INFO)) { - LOG(INFO) << dst.pattern << " shares the same backend group with " - << (*it).pattern; + LOG(INFO) << dst->pattern << " shares the same backend group with " + << (*it)->pattern; } - dst.shared_addr = (*it).shared_addr; + dst->shared_addr = (*it)->shared_addr; } } } @@ -395,7 +398,8 @@ mruby::MRubyContext *Worker::get_mruby_context() const { } #endif // HAVE_MRUBY -std::vector &Worker::get_downstream_addr_groups() { +std::vector> & +Worker::get_downstream_addr_groups() { return downstream_addr_groups_; } @@ -403,17 +407,26 @@ ConnectBlocker *Worker::get_connect_blocker() const { return connect_blocker_.get(); } +const DownstreamRouter *Worker::get_downstream_router() const { + return downstream_router_.get(); +} + +size_t Worker::get_addr_group_catch_all() const { + return addr_group_catch_all_; +} + namespace { size_t match_downstream_addr_group_host( const Router &router, const std::vector &wildcard_patterns, const StringRef &host, const StringRef &path, - const std::vector &groups, size_t catch_all) { + const std::vector> &groups, + size_t catch_all) { if (path.empty() || path[0] != '/') { auto group = router.match(host, StringRef::from_lit("/")); if (group != -1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found pattern with query " << host - << ", matched pattern=" << groups[group].pattern; + << ", matched pattern=" << groups[group]->pattern; } return group; } @@ -429,7 +442,7 @@ size_t match_downstream_addr_group_host( if (group != -1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found pattern with query " << host << path - << ", matched pattern=" << groups[group].pattern; + << ", matched pattern=" << groups[group]->pattern; } return group; } @@ -448,7 +461,7 @@ size_t match_downstream_addr_group_host( // longest host pattern. if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found wildcard pattern with query " << host << path - << ", matched pattern=" << groups[group].pattern; + << ", matched pattern=" << groups[group]->pattern; } return group; } @@ -458,7 +471,7 @@ size_t match_downstream_addr_group_host( if (group != -1) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Found pattern with query " << path - << ", matched pattern=" << groups[group].pattern; + << ", matched pattern=" << groups[group]->pattern; } return group; } @@ -473,7 +486,8 @@ size_t match_downstream_addr_group_host( size_t match_downstream_addr_group( const Router &router, const std::vector &wildcard_patterns, const StringRef &hostport, const StringRef &raw_path, - const std::vector &groups, size_t catch_all) { + const std::vector> &groups, + size_t catch_all) { if (std::find(std::begin(hostport), std::end(hostport), '/') != std::end(hostport)) { // We use '/' specially, and if '/' is included in host, it breaks diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h index c5b23658..1f55e404 100644 --- a/src/shrpx_worker.h +++ b/src/shrpx_worker.h @@ -206,10 +206,14 @@ public: mruby::MRubyContext *get_mruby_context() const; #endif // HAVE_MRUBY - std::vector &get_downstream_addr_groups(); + std::vector> & + get_downstream_addr_groups(); ConnectBlocker *get_connect_blocker() const; + const DownstreamRouter *get_downstream_router() const; + size_t get_addr_group_catch_all() const; + private: #ifndef NOTHREADS std::future fut_; @@ -222,6 +226,7 @@ private: MemchunkPool mcpool_; WorkerStat worker_stat_; + std::shared_ptr downstream_router_; std::unique_ptr session_cache_memcached_dispatcher_; #ifdef HAVE_MRUBY std::unique_ptr mruby_ctx_; @@ -235,11 +240,13 @@ private: ssl::CertLookupTree *cert_tree_; std::shared_ptr ticket_keys_; - std::vector downstream_addr_groups_; + std::vector> downstream_addr_groups_; // Worker level blocker for downstream connection. For example, // this is used when file decriptor is exhausted. std::unique_ptr connect_blocker_; + size_t addr_group_catch_all_; + bool graceful_shutdown_; }; @@ -252,7 +259,8 @@ private: size_t match_downstream_addr_group( const Router &router, const std::vector &wildcard_patterns, const StringRef &hostport, const StringRef &path, - const std::vector &groups, size_t catch_all); + const std::vector> &groups, + size_t catch_all); void downstream_failure(DownstreamAddr *addr); From 845aa7a710dd52f5981a21eada2bb0842dcf699e Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 3 Jun 2016 19:13:02 +0900 Subject: [PATCH 06/24] nghttpx: Share downstream config object This is the unit of sharing configurations to change --- src/shrpx.cc | 18 +++---- src/shrpx_api_downstream_connection.cc | 71 +++++++++++++++++++++++++ src/shrpx_client_handler.cc | 11 ++-- src/shrpx_config.cc | 44 +++++++-------- src/shrpx_config.h | 62 +++++++++++---------- src/shrpx_downstream.cc | 20 ++++--- src/shrpx_http2_session.cc | 6 +-- src/shrpx_http2_upstream.cc | 26 +++++++-- src/shrpx_http_downstream_connection.cc | 14 +++-- src/shrpx_live_check.cc | 6 +-- src/shrpx_spdy_upstream.cc | 19 +++++-- src/shrpx_ssl.cc | 15 ------ src/shrpx_ssl.h | 7 +-- src/shrpx_worker.cc | 25 ++++----- src/shrpx_worker.h | 10 ++-- src/shrpx_worker_process.cc | 2 +- 16 files changed, 218 insertions(+), 138 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 093080f9..fcc45638 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1036,7 +1036,6 @@ void fill_default_config() { mod_config()->num_worker = 1; mod_config()->conf_path = "/etc/nghttpx/nghttpx.conf"; mod_config()->pid = getpid(); - mod_config()->downstream_router = std::make_shared(); if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) { mod_config()->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE; @@ -1168,7 +1167,8 @@ void fill_default_config() { } { - auto &downstreamconf = connconf.downstream; + connconf.downstream = std::make_shared(); + auto &downstreamconf = *connconf.downstream; { auto &timeoutconf = downstreamconf.timeout; // Read/Write timeouts for downstream connection @@ -1424,7 +1424,7 @@ Performance: HTTP/2). To limit the number of connections per frontend for default mode, use --backend-connections-per-frontend. - Default: )" << get_config()->conn.downstream.connections_per_host + Default: )" << get_config()->conn.downstream->connections_per_host << R"( --backend-connections-per-frontend= Set maximum number of backend concurrent connections @@ -1434,7 +1434,7 @@ Performance: with --http2-proxy option, use --backend-connections-per-host. Default: )" - << get_config()->conn.downstream.connections_per_frontend << R"( + << get_config()->conn.downstream->connections_per_frontend << R"( --rlimit-nofile= Set maximum number of open files (RLIMIT_NOFILE) to . If 0 is given, nghttpx does not set the limit. @@ -1442,12 +1442,12 @@ Performance: --backend-request-buffer= Set buffer size used to store backend request. Default: )" - << util::utos_unit(get_config()->conn.downstream.request_buffer_size) + << util::utos_unit(get_config()->conn.downstream->request_buffer_size) << R"( --backend-response-buffer= Set buffer size used to store backend response. Default: )" - << util::utos_unit(get_config()->conn.downstream.response_buffer_size) + << util::utos_unit(get_config()->conn.downstream->response_buffer_size) << R"( --fastopen= Enables "TCP Fast Open" for the listening socket and @@ -1487,15 +1487,15 @@ Timeout: --backend-read-timeout= Specify read timeout for backend connection. Default: )" - << util::duration_str(get_config()->conn.downstream.timeout.read) << R"( + << util::duration_str(get_config()->conn.downstream->timeout.read) << R"( --backend-write-timeout= Specify write timeout for backend connection. Default: )" - << util::duration_str(get_config()->conn.downstream.timeout.write) << R"( + << util::duration_str(get_config()->conn.downstream->timeout.write) << R"( --backend-keep-alive-timeout= Specify keep-alive timeout for backend connection. Default: )" - << util::duration_str(get_config()->conn.downstream.timeout.idle_read) + << util::duration_str(get_config()->conn.downstream->timeout.idle_read) << R"( --listener-disable-timeout= After accepting connection failed, connection listener diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index eb10ef8f..bf083234 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -80,6 +80,77 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, int APIDownstreamConnection::end_upload_data() { // TODO process request payload here (void)worker_; + + auto upstream = downstream_->get_upstream(); + auto output = downstream_->get_request_buf(); + auto &resp = downstream_->response(); + struct iovec iov; + + auto iovcnt = output->riovec(&iov, 1); + + constexpr auto body = StringRef::from_lit("OK"); + + if (iovcnt == 0) { + resp.http_status = 200; + + upstream->send_reply(downstream_, body.byte(), body.size()); + + return 0; + } + + Config config{}; + config.conn.downstream = std::make_shared(); + + std::set include_set; + + constexpr auto error_body = StringRef::from_lit("invalid configuration"); + + for (auto first = reinterpret_cast(iov.iov_base), + last = first + iov.iov_len; + first != last;) { + auto eol = std::find(first, last, '\n'); + if (eol == last) { + break; + } + + if (first == eol || *first == '#') { + first = ++eol; + continue; + } + + auto eq = std::find(first, eol, '='); + if (eq == eol) { + resp.http_status = 500; + + upstream->send_reply(downstream_, error_body.byte(), error_body.size()); + return 0; + } + + if (parse_config(&config, StringRef{first, eq}, StringRef{eq + 1, eol}, + include_set) != 0) { + resp.http_status = 500; + + upstream->send_reply(downstream_, error_body.byte(), error_body.size()); + return 0; + } + + first = ++eol; + } + + auto &tlsconf = get_config()->tls; + if (configure_downstream_group(&config, get_config()->http2_proxy, true, + tlsconf) != 0) { + resp.http_status = 500; + upstream->send_reply(downstream_, error_body.byte(), error_body.size()); + return 0; + } + + worker_->replace_downstream_config(config.conn.downstream); + + resp.http_status = 200; + + upstream->send_reply(downstream_, body.byte(), body.size()); + return 0; } diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index db16d64d..5f4eb6e5 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -816,9 +816,10 @@ uint32_t next_cycle(const WeightedPri &pri) { std::unique_ptr ClientHandler::get_downstream_connection(Downstream *downstream) { size_t group_idx; - auto catch_all = worker_->get_addr_group_catch_all(); + auto &downstreamconf = *worker_->get_downstream_config(); + + auto catch_all = downstreamconf.addr_group_catch_all; auto &groups = worker_->get_downstream_addr_groups(); - auto downstream_router = worker_->get_downstream_router(); const auto &req = downstream->request(); @@ -836,8 +837,8 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { // have dealt with proxy case already, just use catch-all group. group_idx = catch_all; } else { - auto &router = downstream_router->router; - auto &wildcard_patterns = downstream_router->wildcard_patterns; + auto &router = downstreamconf.router; + auto &wildcard_patterns = downstreamconf.wildcard_patterns; if (!req.authority.empty()) { group_idx = match_downstream_addr_group(router, wildcard_patterns, req.authority, @@ -859,7 +860,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { CLOG(INFO, this) << "Downstream address group_idx: " << group_idx; } - auto &group = worker_->get_downstream_addr_groups()[group_idx]; + auto &group = groups[group_idx]; auto &shared_addr = group->shared_addr; auto proto = PROTO_NONE; diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 732178eb..4e46de3e 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -745,7 +745,8 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, // will append '/' to all patterns, so it becomes catch-all pattern. auto mapping = util::split_str(src_pattern, ':'); assert(!mapping.empty()); - auto &addr_groups = config->conn.downstream.addr_groups; + auto &downstreamconf = *config->conn.downstream; + auto &addr_groups = downstreamconf.addr_groups; DownstreamParams params{}; params.proto = PROTO_HTTP1; @@ -788,8 +789,6 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, DownstreamAddrGroupConfig g(StringRef{pattern}); g.addrs.push_back(addr); - auto &downstream_router = config->downstream_router; - if (pattern[0] == '*') { // wildcard pattern auto path_first = @@ -798,7 +797,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, auto host = StringRef{std::begin(g.pattern) + 1, path_first}; auto path = StringRef{path_first, std::end(g.pattern)}; - auto &wildcard_patterns = downstream_router->wildcard_patterns; + auto &wildcard_patterns = downstreamconf.wildcard_patterns; auto it = std::find_if( std::begin(wildcard_patterns), std::end(wildcard_patterns), @@ -814,8 +813,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, (*it).router.add_route(path, addr_groups.size()); } } else { - downstream_router->router.add_route(StringRef{g.pattern}, - addr_groups.size()); + downstreamconf.router.add_route(StringRef{g.pattern}, addr_groups.size()); } addr_groups.push_back(std::move(g)); @@ -1944,9 +1942,9 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT: return parse_duration(&config->conn.upstream.timeout.write, opt, optarg); case SHRPX_OPTID_BACKEND_READ_TIMEOUT: - return parse_duration(&config->conn.downstream.timeout.read, opt, optarg); + return parse_duration(&config->conn.downstream->timeout.read, opt, optarg); case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT: - return parse_duration(&config->conn.downstream.timeout.write, opt, optarg); + return parse_duration(&config->conn.downstream->timeout.write, opt, optarg); case SHRPX_OPTID_STREAM_READ_TIMEOUT: return parse_duration(&config->http2.timeout.stream_read, opt, optarg); case SHRPX_OPTID_STREAM_WRITE_TIMEOUT: @@ -1989,7 +1987,7 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, return 0; } case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT: - return parse_duration(&config->conn.downstream.timeout.idle_read, opt, + return parse_duration(&config->conn.downstream->timeout.idle_read, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS: case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: { @@ -2176,14 +2174,14 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, LOG(WARN) << opt << ": deprecated. Use backend-address-family=IPv4 instead."; - config->conn.downstream.family = AF_INET; + config->conn.downstream->family = AF_INET; return 0; case SHRPX_OPTID_BACKEND_IPV6: LOG(WARN) << opt << ": deprecated. Use backend-address-family=IPv6 instead."; - config->conn.downstream.family = AF_INET6; + config->conn.downstream->family = AF_INET6; return 0; case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: { @@ -2383,7 +2381,7 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, return -1; } - config->conn.downstream.connections_per_host = n; + config->conn.downstream->connections_per_host = n; return 0; } @@ -2392,7 +2390,7 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, << SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead."; // fall through case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND: - return parse_uint(&config->conn.downstream.connections_per_frontend, opt, + return parse_uint(&config->conn.downstream->connections_per_frontend, opt, optarg); case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg); @@ -2430,9 +2428,9 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, } if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) { - config->conn.downstream.request_buffer_size = n; + config->conn.downstream->request_buffer_size = n; } else { - config->conn.downstream.response_buffer_size = n; + config->conn.downstream->response_buffer_size = n; } return 0; @@ -2690,7 +2688,7 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, return parse_address_family(&config->tls.session_cache.memcached.family, opt, optarg); case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY: - return parse_address_family(&config->conn.downstream.family, opt, optarg); + return parse_address_family(&config->conn.downstream->family, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS: return parse_uint(&config->http2.upstream.max_concurrent_streams, opt, optarg); @@ -2913,9 +2911,9 @@ StringRef strproto(shrpx_proto proto) { int configure_downstream_group(Config *config, bool http2_proxy, bool numeric_addr_only, const TLSConfig &tlsconf) { - auto &downstreamconf = config->conn.downstream; + auto &downstreamconf = *config->conn.downstream; auto &addr_groups = downstreamconf.addr_groups; - auto &downstream_router = config->downstream_router; + auto &router = downstreamconf.router; if (addr_groups.empty()) { DownstreamAddrConfig addr{}; @@ -2925,8 +2923,7 @@ int configure_downstream_group(Config *config, bool http2_proxy, DownstreamAddrGroupConfig g(StringRef::from_lit("/")); g.addrs.push_back(std::move(addr)); - downstream_router->router.add_route(StringRef{g.pattern}, - addr_groups.size()); + router.add_route(StringRef{g.pattern}, addr_groups.size()); addr_groups.push_back(std::move(g)); } else if (http2_proxy) { // We don't support host mapping in these cases. Move all @@ -2938,12 +2935,11 @@ int configure_downstream_group(Config *config, bool http2_proxy, } std::vector().swap(addr_groups); // maybe not necessary? - downstream_router = std::make_shared(); - downstream_router->router.add_route(StringRef{catch_all.pattern}, - addr_groups.size()); + router = Router(); + router.add_route(StringRef{catch_all.pattern}, addr_groups.size()); addr_groups.push_back(std::move(catch_all)); } else { - auto &wildcard_patterns = downstream_router->wildcard_patterns; + auto &wildcard_patterns = downstreamconf.wildcard_patterns; std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns), [](const WildcardPattern &lhs, const WildcardPattern &rhs) { return std::lexicographical_compare( diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 2efbb19c..75bcd337 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -587,6 +587,35 @@ struct RateLimitConfig { size_t burst; }; +// Wildcard host pattern routing. We strips left most '*' from host +// field. router includes all path pattern sharing same wildcard +// host. +struct WildcardPattern { + ImmutableString host; + Router router; +}; + +struct DownstreamConfig { + struct { + ev_tstamp read; + ev_tstamp write; + ev_tstamp idle_read; + } timeout; + Router router; + std::vector wildcard_patterns; + std::vector addr_groups; + // The index of catch-all group in downstream_addr_groups. + size_t addr_group_catch_all; + size_t connections_per_host; + size_t connections_per_frontend; + size_t request_buffer_size; + size_t response_buffer_size; + // Address family of backend connection. One of either AF_INET, + // AF_INET6 or AF_UNSPEC. This is ignored if backend connection + // is made via Unix domain socket. + int family; +}; + struct ConnectionConfig { struct { struct { @@ -614,41 +643,10 @@ struct ConnectionConfig { bool accept_proxy_protocol; } upstream; - struct { - struct { - ev_tstamp read; - ev_tstamp write; - ev_tstamp idle_read; - } timeout; - std::vector addr_groups; - // The index of catch-all group in downstream_addr_groups. - size_t addr_group_catch_all; - size_t connections_per_host; - size_t connections_per_frontend; - size_t request_buffer_size; - size_t response_buffer_size; - // Address family of backend connection. One of either AF_INET, - // AF_INET6 or AF_UNSPEC. This is ignored if backend connection - // is made via Unix domain socket. - int family; - } downstream; -}; - -// Wildcard host pattern routing. We strips left most '*' from host -// field. router includes all path pattern sharing same wildcard -// host. -struct WildcardPattern { - ImmutableString host; - Router router; -}; - -struct DownstreamRouter { - Router router; - std::vector wildcard_patterns; + std::shared_ptr downstream; }; struct Config { - std::shared_ptr downstream_router; HttpProxy downstream_http_proxy; HttpConfig http; Http2Config http2; diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index 3e1d7ba0..c0a1c15e 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -502,6 +502,7 @@ void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } bool Downstream::request_buf_full() { auto handler = upstream_->get_client_handler(); auto faddr = handler->get_upstream_addr(); + auto worker = handler->get_worker(); // We don't check buffer size here for API endpoint. if (faddr->api) { @@ -509,11 +510,11 @@ bool Downstream::request_buf_full() { } if (dconn_) { - return request_buf_.rleft() >= - get_config()->conn.downstream.request_buffer_size; - } else { - return false; + auto downstreamconf = worker->get_downstream_config(); + return request_buf_.rleft() >= downstreamconf->request_buffer_size; } + + return false; } DefaultMemchunks *Downstream::get_request_buf() { return &request_buf_; } @@ -601,11 +602,14 @@ DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; } bool Downstream::response_buf_full() { if (dconn_) { - return response_buf_.rleft() >= - get_config()->conn.downstream.response_buffer_size; - } else { - return false; + auto handler = upstream_->get_client_handler(); + auto worker = handler->get_worker(); + auto downstreamconf = worker->get_downstream_config(); + + return response_buf_.rleft() >= downstreamconf->response_buffer_size; } + + return false; } bool Downstream::validate_request_recv_body_length() const { diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 8a8f9b3b..231f81db 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -178,9 +178,9 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, : dlnext(nullptr), dlprev(nullptr), conn_(loop, -1, nullptr, worker->get_mcpool(), - get_config()->conn.downstream.timeout.write, - get_config()->conn.downstream.timeout.read, {}, {}, writecb, readcb, - timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, + worker->get_downstream_config()->timeout.write, + worker->get_downstream_config()->timeout.read, {}, {}, writecb, + readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP2), wb_(worker->get_mcpool()), worker_(worker), diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index b05296e7..8f9e4645 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -413,6 +413,13 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) { downstream_queue_.mark_active(downstream); + auto &req = downstream->request(); + if (!req.http2_expect_body) { + downstream->end_upload_data(); + // TODO is this necessary? + handler_->signal_write(); + } + return; } @@ -846,13 +853,22 @@ nghttp2_session_callbacks *create_http2_upstream_callbacks() { return callbacks; } +namespace { +size_t downstream_queue_size(Worker *worker) { + auto downstreamconf = worker->get_downstream_config(); + + if (get_config()->http2_proxy) { + return downstreamconf->connections_per_host; + } + + return downstreamconf->connections_per_frontend; +} +} // namespace + Http2Upstream::Http2Upstream(ClientHandler *handler) : wb_(handler->get_worker()->get_mcpool()), - downstream_queue_( - get_config()->http2_proxy - ? get_config()->conn.downstream.connections_per_host - : get_config()->conn.downstream.connections_per_frontend, - !get_config()->http2_proxy), + downstream_queue_(downstream_queue_size(handler->get_worker()), + !get_config()->http2_proxy), handler_(handler), session_(nullptr), shutdown_handled_(false) { diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 63c99a72..0bd1aeac 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -151,8 +151,8 @@ HttpDownstreamConnection::HttpDownstreamConnection( const std::shared_ptr &group, struct ev_loop *loop, Worker *worker) : conn_(loop, -1, nullptr, worker->get_mcpool(), - get_config()->conn.downstream.timeout.write, - get_config()->conn.downstream.timeout.read, {}, {}, connectcb, + worker->get_downstream_config()->timeout.write, + worker->get_downstream_config()->timeout.read, {}, {}, connectcb, readcb, connect_timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, PROTO_HTTP1), @@ -182,7 +182,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { return SHRPX_ERR_NETWORK; } - auto &downstreamconf = get_config()->conn.downstream; + auto &downstreamconf = *worker_->get_downstream_config(); if (conn_.fd == -1) { auto &shared_addr = group_->shared_addr; @@ -588,7 +588,9 @@ void HttpDownstreamConnection::detach_downstream(Downstream *downstream) { ev_set_cb(&conn_.rev, idle_readcb); ioctrl_.force_resume_read(); - conn_.rt.repeat = get_config()->conn.downstream.timeout.idle_read; + auto &downstreamconf = *worker_->get_downstream_config(); + + conn_.rt.repeat = downstreamconf.timeout.idle_read; ev_set_cb(&conn_.rt, idle_timeoutcb); ev_timer_again(conn_.loop, &conn_.rt); @@ -602,8 +604,10 @@ void HttpDownstreamConnection::pause_read(IOCtrlReason reason) { int HttpDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { + auto &downstreamconf = *worker_->get_downstream_config(); + if (downstream_->get_response_buf()->rleft() <= - get_config()->conn.downstream.request_buffer_size / 2) { + downstreamconf.request_buffer_size / 2) { ioctrl_.resume_read(reason); } diff --git a/src/shrpx_live_check.cc b/src/shrpx_live_check.cc index e0a9b723..eefa8783 100644 --- a/src/shrpx_live_check.cc +++ b/src/shrpx_live_check.cc @@ -98,9 +98,9 @@ void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) { LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, DownstreamAddr *addr, std::mt19937 &gen) : conn_(loop, -1, nullptr, worker->get_mcpool(), - get_config()->conn.downstream.timeout.write, - get_config()->conn.downstream.timeout.read, {}, {}, writecb, readcb, - timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, + worker->get_downstream_config()->timeout.write, + worker->get_downstream_config()->timeout.read, {}, {}, writecb, + readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE), wb_(worker->get_mcpool()), gen_(gen), diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index bbb96ba6..2334ef6d 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -512,13 +512,22 @@ uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) { } } // namespace +namespace { +size_t downstream_queue_size(Worker *worker) { + auto downstreamconf = worker->get_downstream_config(); + + if (get_config()->http2_proxy) { + return downstreamconf->connections_per_host; + } + + return downstreamconf->connections_per_frontend; +} +} // namespace + SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) : wb_(handler->get_worker()->get_mcpool()), - downstream_queue_( - get_config()->http2_proxy - ? get_config()->conn.downstream.connections_per_host - : get_config()->conn.downstream.connections_per_frontend, - !get_config()->http2_proxy), + downstream_queue_(downstream_queue_size(handler->get_worker()), + !get_config()->http2_proxy), handler_(handler), session_(nullptr) { spdylay_session_callbacks callbacks{}; diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 9b9270e4..7ff70955 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -1390,26 +1390,11 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, return ssl_ctx; } -bool downstream_tls_enabled() { - const auto &groups = get_config()->conn.downstream.addr_groups; - - return std::any_of(std::begin(groups), std::end(groups), - [](const DownstreamAddrGroupConfig &g) { - return std::any_of( - std::begin(g.addrs), std::end(g.addrs), - [](const DownstreamAddrConfig &a) { return a.tls; }); - }); -} - SSL_CTX *setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb #endif // HAVE_NEVERBLEED ) { - if (!downstream_tls_enabled()) { - return nullptr; - } - auto &tlsconf = get_config()->tls; return ssl::create_ssl_client_context( diff --git a/src/shrpx_ssl.h b/src/shrpx_ssl.h index ea3a47cd..643f4ffa 100644 --- a/src/shrpx_ssl.h +++ b/src/shrpx_ssl.h @@ -201,9 +201,7 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, #endif // HAVE_NEVERBLEED ); -// Setups client side SSL_CTX. This function inspects get_config() -// and if TLS is disabled in all downstreams, returns nullptr. -// Otherwise, only construct SSL_CTX. +// Setups client side SSL_CTX. SSL_CTX *setup_downstream_client_ssl_context( #ifdef HAVE_NEVERBLEED neverbleed_t *nb @@ -224,9 +222,6 @@ SSL *create_ssl(SSL_CTX *ssl_ctx); // Returns true if SSL/TLS is enabled on upstream bool upstream_tls_enabled(); -// Returns true if SSL/TLS is enabled on downstream -bool downstream_tls_enabled(); - // Performs TLS hostname match. |pattern| can contain wildcard // character '*', which matches prefix of target hostname. There are // several restrictions to make wildcard work. The matching algorithm diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 503a8a7e..660588c8 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -109,16 +109,13 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, const std::shared_ptr &ticket_keys) : randgen_(rd()), worker_stat_{}, - downstream_router_(get_config()->downstream_router), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys), - downstream_addr_groups_(get_config()->conn.downstream.addr_groups.size()), connect_blocker_( make_unique(randgen_, loop_, []() {}, []() {})), - addr_group_catch_all_(get_config()->conn.downstream.addr_group_catch_all), graceful_shutdown_(false) { ev_async_init(&w_, eventcb); w_.data = this; @@ -136,10 +133,18 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, StringRef{session_cacheconf.memcached.host}, &mcpool_); } - auto &downstreamconf = get_config()->conn.downstream; + replace_downstream_config(get_config()->conn.downstream); +} - for (size_t i = 0; i < downstreamconf.addr_groups.size(); ++i) { - auto &src = downstreamconf.addr_groups[i]; +void Worker::replace_downstream_config( + const std::shared_ptr &downstreamconf) { + downstreamconf_ = downstreamconf; + + downstream_addr_groups_ = std::vector>( + downstreamconf->addr_groups.size()); + + for (size_t i = 0; i < downstreamconf->addr_groups.size(); ++i) { + auto &src = downstreamconf->addr_groups[i]; auto &dst = downstream_addr_groups_[i]; dst = std::make_shared(); @@ -407,12 +412,8 @@ ConnectBlocker *Worker::get_connect_blocker() const { return connect_blocker_.get(); } -const DownstreamRouter *Worker::get_downstream_router() const { - return downstream_router_.get(); -} - -size_t Worker::get_addr_group_catch_all() const { - return addr_group_catch_all_; +const DownstreamConfig *Worker::get_downstream_config() const { + return downstreamconf_.get(); } namespace { diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h index 1f55e404..240d80e3 100644 --- a/src/shrpx_worker.h +++ b/src/shrpx_worker.h @@ -211,8 +211,10 @@ public: ConnectBlocker *get_connect_blocker() const; - const DownstreamRouter *get_downstream_router() const; - size_t get_addr_group_catch_all() const; + const DownstreamConfig *get_downstream_config() const; + + void replace_downstream_config( + const std::shared_ptr &downstreamconf); private: #ifndef NOTHREADS @@ -226,7 +228,7 @@ private: MemchunkPool mcpool_; WorkerStat worker_stat_; - std::shared_ptr downstream_router_; + std::shared_ptr downstreamconf_; std::unique_ptr session_cache_memcached_dispatcher_; #ifdef HAVE_MRUBY std::unique_ptr mruby_ctx_; @@ -245,8 +247,6 @@ private: // this is used when file decriptor is exhausted. std::unique_ptr connect_blocker_; - size_t addr_group_catch_all_; - bool graceful_shutdown_; }; diff --git a/src/shrpx_worker_process.cc b/src/shrpx_worker_process.cc index fd22bf4b..85ac3777 100644 --- a/src/shrpx_worker_process.cc +++ b/src/shrpx_worker_process.cc @@ -394,7 +394,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { } #ifdef HAVE_NEVERBLEED - if (ssl::upstream_tls_enabled() || ssl::downstream_tls_enabled()) { + { std::array errbuf; auto nb = make_unique(); if (neverbleed_init(nb.get(), errbuf.data()) != 0) { From 43913838b4a29e61b5737cd871355eb21a59953d Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 3 Jun 2016 23:52:44 +0900 Subject: [PATCH 07/24] nghttpx: Retain memory in Router --- src/allocator.h | 36 ++++++++-- src/shrpx_api_downstream_connection.cc | 95 +++++++++++++++++--------- src/shrpx_api_downstream_connection.h | 3 + src/shrpx_config.cc | 16 ++--- src/shrpx_config.h | 3 + src/shrpx_router.cc | 7 +- src/shrpx_router.h | 9 +++ src/shrpx_worker_test.cc | 23 ++++--- 8 files changed, 133 insertions(+), 59 deletions(-) diff --git a/src/allocator.h b/src/allocator.h index 701ccf30..5a379ac0 100644 --- a/src/allocator.h +++ b/src/allocator.h @@ -54,7 +54,35 @@ struct BlockAllocator { block_size(block_size), isolation_threshold(std::min(block_size, isolation_threshold)) {} - ~BlockAllocator() { + ~BlockAllocator() { destroy(); } + + BlockAllocator(BlockAllocator &&other) noexcept + : retain(other.retain), + head(other.head), + block_size(other.block_size), + isolation_threshold(other.isolation_threshold) { + other.retain = nullptr; + other.head = nullptr; + } + + BlockAllocator &operator=(BlockAllocator &&other) noexcept { + destroy(); + + retain = other.retain; + head = other.head; + block_size = other.block_size; + isolation_threshold = other.isolation_threshold; + + other.retain = nullptr; + other.head = nullptr; + + return *this; + } + + BlockAllocator(const BlockAllocator &) = delete; + BlockAllocator &operator=(const BlockAllocator &) = delete; + + void destroy() { for (auto mb = retain; mb;) { auto next = mb->next; delete[] reinterpret_cast(mb); @@ -62,12 +90,6 @@ struct BlockAllocator { } } - BlockAllocator(BlockAllocator &&) = default; - BlockAllocator &operator=(BlockAllocator &&) = default; - - BlockAllocator(const BlockAllocator &) = delete; - BlockAllocator &operator=(const BlockAllocator &) = delete; - MemBlock *alloc_mem_block(size_t size) { auto block = new uint8_t[sizeof(MemBlock) + size]; auto mb = reinterpret_cast(block); diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index bf083234..e67a7f49 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -32,7 +32,7 @@ namespace shrpx { APIDownstreamConnection::APIDownstreamConnection(Worker *worker) - : worker_(worker) {} + : worker_(worker), abandoned_(false) {} APIDownstreamConnection::~APIDownstreamConnection() {} @@ -41,14 +41,6 @@ int APIDownstreamConnection::attach_downstream(Downstream *downstream) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } - auto &req = downstream->request(); - - if (req.path != StringRef::from_lit("/api/v1/dynamicconfig")) { - // TODO this will return 503 error, which is not nice. We'd like - // to use 404 in this case. - return -1; - } - downstream_ = downstream; return 0; @@ -61,10 +53,48 @@ void APIDownstreamConnection::detach_downstream(Downstream *downstream) { downstream_ = nullptr; } -int APIDownstreamConnection::push_request_headers() { return 0; } +int APIDownstreamConnection::send_reply(unsigned int http_status, + const StringRef &body) { + abandoned_ = true; + + auto upstream = downstream_->get_upstream(); + + auto &resp = downstream_->response(); + + resp.http_status = http_status; + + auto &balloc = downstream_->get_block_allocator(); + + auto content_length = util::make_string_ref_uint(balloc, body.size()); + + resp.fs.add_header_token(StringRef::from_lit("content-length"), + content_length, false, http2::HD_CONTENT_LENGTH); + + if (upstream->send_reply(downstream_, body.byte(), body.size()) != 0) { + return -1; + } + + return 0; +} + +int APIDownstreamConnection::push_request_headers() { + auto &req = downstream_->request(); + + if (req.path != StringRef::from_lit("/api/v1alpha1/backend/replace")) { + send_reply(404, StringRef::from_lit("404 Not Found")); + + return 0; + } + + return 0; +} int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, size_t datalen) { + if (abandoned_) { + return 0; + } + auto output = downstream_->get_request_buf(); // TODO limit the maximum payload size @@ -78,33 +108,39 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, } int APIDownstreamConnection::end_upload_data() { - // TODO process request payload here - (void)worker_; + if (abandoned_) { + return 0; + } - auto upstream = downstream_->get_upstream(); auto output = downstream_->get_request_buf(); - auto &resp = downstream_->response(); - struct iovec iov; + struct iovec iov; auto iovcnt = output->riovec(&iov, 1); - constexpr auto body = StringRef::from_lit("OK"); + constexpr auto body = StringRef::from_lit("200 OK"); + constexpr auto error_body = StringRef::from_lit("400 Bad Request"); if (iovcnt == 0) { - resp.http_status = 200; - - upstream->send_reply(downstream_, body.byte(), body.size()); + send_reply(200, body); return 0; } Config config{}; config.conn.downstream = std::make_shared(); + const auto &downstreamconf = config.conn.downstream; + + auto src = get_config()->conn.downstream; + + downstreamconf->timeout = src->timeout; + downstreamconf->connections_per_host = src->connections_per_host; + downstreamconf->connections_per_frontend = src->connections_per_frontend; + downstreamconf->request_buffer_size = src->request_buffer_size; + downstreamconf->response_buffer_size = src->response_buffer_size; + downstreamconf->family = src->family; std::set include_set; - constexpr auto error_body = StringRef::from_lit("invalid configuration"); - for (auto first = reinterpret_cast(iov.iov_base), last = first + iov.iov_len; first != last;) { @@ -120,17 +156,13 @@ int APIDownstreamConnection::end_upload_data() { auto eq = std::find(first, eol, '='); if (eq == eol) { - resp.http_status = 500; - - upstream->send_reply(downstream_, error_body.byte(), error_body.size()); + send_reply(400, error_body); return 0; } if (parse_config(&config, StringRef{first, eq}, StringRef{eq + 1, eol}, include_set) != 0) { - resp.http_status = 500; - - upstream->send_reply(downstream_, error_body.byte(), error_body.size()); + send_reply(400, error_body); return 0; } @@ -140,16 +172,13 @@ int APIDownstreamConnection::end_upload_data() { auto &tlsconf = get_config()->tls; if (configure_downstream_group(&config, get_config()->http2_proxy, true, tlsconf) != 0) { - resp.http_status = 500; - upstream->send_reply(downstream_, error_body.byte(), error_body.size()); + send_reply(400, error_body); return 0; } - worker_->replace_downstream_config(config.conn.downstream); + worker_->replace_downstream_config(downstreamconf); - resp.http_status = 200; - - upstream->send_reply(downstream_, body.byte(), body.size()); + send_reply(200, body); return 0; } diff --git a/src/shrpx_api_downstream_connection.h b/src/shrpx_api_downstream_connection.h index 1bd6c1b6..f38b0fea 100644 --- a/src/shrpx_api_downstream_connection.h +++ b/src/shrpx_api_downstream_connection.h @@ -56,8 +56,11 @@ public: virtual DownstreamAddrGroup *get_downstream_addr_group() const; + int send_reply(unsigned int http_status, const StringRef &body); + private: Worker *worker_; + bool abandoned_; }; } // namespace shrpx diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 4e46de3e..5586e934 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -786,7 +786,10 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, if (done) { continue; } - DownstreamAddrGroupConfig g(StringRef{pattern}); + + auto idx = addr_groups.size(); + addr_groups.emplace_back(StringRef{pattern}); + auto &g = addr_groups.back(); g.addrs.push_back(addr); if (pattern[0] == '*') { @@ -804,19 +807,16 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr, [&host](const WildcardPattern &wp) { return wp.host == host; }); if (it == std::end(wildcard_patterns)) { - wildcard_patterns.push_back( - {ImmutableString{std::begin(host), std::end(host)}}); + wildcard_patterns.emplace_back(host); auto &router = wildcard_patterns.back().router; - router.add_route(path, addr_groups.size()); + router.add_route(path, idx); } else { - (*it).router.add_route(path, addr_groups.size()); + (*it).router.add_route(path, idx); } } else { - downstreamconf.router.add_route(StringRef{g.pattern}, addr_groups.size()); + downstreamconf.router.add_route(StringRef{g.pattern}, idx); } - - addr_groups.push_back(std::move(g)); } return 0; } diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 75bcd337..c1d71c8c 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -591,6 +591,9 @@ struct RateLimitConfig { // field. router includes all path pattern sharing same wildcard // host. struct WildcardPattern { + WildcardPattern(const StringRef &host) + : host(std::begin(host), std::end(host)) {} + ImmutableString host; Router router; }; diff --git a/src/shrpx_router.cc b/src/shrpx_router.cc index 4c88283f..86d5f02f 100644 --- a/src/shrpx_router.cc +++ b/src/shrpx_router.cc @@ -35,7 +35,9 @@ RNode::RNode() : s(nullptr), len(0), index(-1) {} RNode::RNode(const char *s, size_t len, size_t index) : s(s), len(len), index(index) {} -Router::Router() : root_{} {} +Router::Router() : balloc_(1024, 1024), root_{} {} + +Router::~Router() {} namespace { RNode *find_next_node(const RNode *node, char c) { @@ -62,7 +64,8 @@ void add_next_node(RNode *node, std::unique_ptr new_node) { void Router::add_node(RNode *node, const char *pattern, size_t patlen, size_t index) { - auto new_node = make_unique(pattern, patlen, index); + auto pat = make_string_ref(balloc_, StringRef{pattern, patlen}); + auto new_node = make_unique(pat.c_str(), pat.size(), index); add_next_node(node, std::move(new_node)); } diff --git a/src/shrpx_router.h b/src/shrpx_router.h index ece0e276..2381a96c 100644 --- a/src/shrpx_router.h +++ b/src/shrpx_router.h @@ -30,6 +30,8 @@ #include #include +#include "allocator.h" + namespace shrpx { struct RNode { @@ -55,6 +57,12 @@ struct RNode { class Router { public: Router(); + ~Router(); + Router(Router &&) = default; + Router(const Router &) = delete; + Router &operator=(Router &&) = default; + Router &operator=(const Router &) = delete; + // Adds route |pattern| with its |index|. bool add_route(const StringRef &pattern, size_t index); // Returns the matched index of pattern. -1 if there is no match. @@ -65,6 +73,7 @@ public: void dump() const; private: + BlockAllocator balloc_; // The root node of Patricia tree. This is special node and its s // field is nulptr, and len field is 0. RNode root_; diff --git a/src/shrpx_worker_test.cc b/src/shrpx_worker_test.cc index cd840f9e..c4f60b4f 100644 --- a/src/shrpx_worker_test.cc +++ b/src/shrpx_worker_test.cc @@ -38,20 +38,22 @@ namespace shrpx { void test_shrpx_worker_match_downstream_addr_group(void) { - auto groups = std::vector(); + auto groups = std::vector>(); for (auto &s : {"nghttp2.org/", "nghttp2.org/alpha/bravo/", "nghttp2.org/alpha/charlie", "nghttp2.org/delta%3A", "www.nghttp2.org/", "[::1]/", "nghttp2.org/alpha/bravo/delta", // Check that match is done in the single node "example.com/alpha/bravo", "192.168.0.1/alpha/", "/golf/"}) { - groups.push_back(DownstreamAddrGroup{ImmutableString(s)}); + auto g = std::make_shared(); + g->pattern = ImmutableString(s); + groups.push_back(std::move(g)); } Router router; for (size_t i = 0; i < groups.size(); ++i) { auto &g = groups[i]; - router.add_route(StringRef{g.pattern}, i); + router.add_route(StringRef{g->pattern}, i); } std::vector wp; @@ -176,15 +178,18 @@ void test_shrpx_worker_match_downstream_addr_group(void) { StringRef::from_lit("/"), groups, 255)); // Test for wildcard hosts - groups.push_back( - DownstreamAddrGroup{ImmutableString::from_lit("git.nghttp2.org")}); - groups.push_back( - DownstreamAddrGroup{ImmutableString::from_lit(".nghttp2.org")}); + auto g1 = std::make_shared(); + g1->pattern = ImmutableString::from_lit("git.nghttp2.org"); + groups.push_back(std::move(g1)); - wp.push_back({ImmutableString("git.nghttp2.org")}); + auto g2 = std::make_shared(); + g2->pattern = ImmutableString::from_lit(".nghttp2.org"); + groups.push_back(std::move(g2)); + + wp.emplace_back(StringRef::from_lit("git.nghttp2.org")); wp.back().router.add_route(StringRef::from_lit("/echo/"), 10); - wp.push_back({ImmutableString(".nghttp2.org")}); + wp.emplace_back(StringRef::from_lit(".nghttp2.org")); wp.back().router.add_route(StringRef::from_lit("/echo/"), 11); wp.back().router.add_route(StringRef::from_lit("/echo/foxtrot"), 12); From 0ca7c4cb387ff41759527b31d97386b71e87cf69 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 01:02:57 +0900 Subject: [PATCH 08/24] nghttpx: Send notice to replace downstream via ConnectionHandler --- src/shrpx_api_downstream_connection.cc | 5 +- src/shrpx_connection_handler.cc | 80 ++++++++++++++++++++++++-- src/shrpx_connection_handler.h | 32 +++++++++++ src/shrpx_worker.cc | 19 +++++- src/shrpx_worker.h | 14 ++++- 5 files changed, 137 insertions(+), 13 deletions(-) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index e67a7f49..b6eaca14 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -28,6 +28,7 @@ #include "shrpx_upstream.h" #include "shrpx_downstream.h" #include "shrpx_worker.h" +#include "shrpx_connection_handler.h" namespace shrpx { @@ -176,7 +177,9 @@ int APIDownstreamConnection::end_upload_data() { return 0; } - worker_->replace_downstream_config(downstreamconf); + auto conn_handler = worker_->get_connection_handler(); + + conn_handler->send_replace_downstream(downstreamconf); send_reply(200, body); diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index dc3223a9..7b862a47 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -102,6 +102,14 @@ void thread_join_async_cb(struct ev_loop *loop, ev_async *w, int revent) { } } // namespace +namespace { +void serial_event_async_cb(struct ev_loop *loop, ev_async *w, int revent) { + auto h = static_cast(w->data); + + h->handle_serial_event(); +} +} // namespace + namespace { std::random_device rd; } // namespace @@ -125,6 +133,11 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop) ev_async_init(&thread_join_asyncev_, thread_join_async_cb); + ev_async_init(&serial_event_asyncev_, serial_event_async_cb); + serial_event_asyncev_.data = this; + + ev_async_start(loop_, &serial_event_asyncev_); + ev_child_init(&ocsp_.chldev, ocsp_chld_cb, 0, 0); ocsp_.chldev.data = this; @@ -136,6 +149,7 @@ ConnectionHandler::ConnectionHandler(struct ev_loop *loop) ConnectionHandler::~ConnectionHandler() { ev_child_stop(loop_, &ocsp_.chldev); + ev_async_stop(loop_, &serial_event_asyncev_); ev_async_stop(loop_, &thread_join_asyncev_); ev_io_stop(loop_, &ocsp_.rev); ev_timer_stop(loop_, &ocsp_timer_); @@ -175,6 +189,18 @@ void ConnectionHandler::worker_reopen_log_files() { } } +void ConnectionHandler::worker_replace_downstream( + std::shared_ptr downstreamconf) { + WorkerEvent wev{}; + + wev.type = REPLACE_DOWNSTREAM; + wev.downstreamconf = std::move(downstreamconf); + + for (auto &worker : workers_) { + worker->send(wev); + } +} + int ConnectionHandler::create_single_worker() { auto cert_tree = ssl::create_cert_lookup_tree(); auto sv_ssl_ctx = ssl::setup_server_ssl_context(all_ssl_ctx_, cert_tree @@ -207,9 +233,9 @@ int ConnectionHandler::create_single_worker() { all_ssl_ctx_.push_back(session_cache_ssl_ctx); } - single_worker_ = - make_unique(loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, - cert_tree, ticket_keys_); + single_worker_ = make_unique( + loop_, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree, + ticket_keys_, this, get_config()->conn.downstream); #ifdef HAVE_MRUBY if (single_worker_->create_mruby_context() != 0) { return -1; @@ -256,9 +282,9 @@ int ConnectionHandler::create_worker_thread(size_t num) { StringRef{memcachedconf.private_key_file}, nullptr); all_ssl_ctx_.push_back(session_cache_ssl_ctx); } - auto worker = - make_unique(loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, - cert_tree, ticket_keys_); + auto worker = make_unique( + loop, sv_ssl_ctx, cl_ssl_ctx, session_cache_ssl_ctx, cert_tree, + ticket_keys_, this, get_config()->conn.downstream); #ifdef HAVE_MRUBY if (worker->create_mruby_context() != 0) { return -1; @@ -782,4 +808,46 @@ neverbleed_t *ConnectionHandler::get_neverbleed() const { return nb_.get(); } #endif // HAVE_NEVERBLEED +void ConnectionHandler::handle_serial_event() { + std::vector q; + { + std::lock_guard g(serial_event_mu_); + q.swap(serial_events_); + } + + for (auto &sev : q) { + switch (sev.type) { + case SEV_REPLACE_DOWNSTREAM: + // TODO make sure that none of worker uses + // get_config()->conn.downstream + mod_config()->conn.downstream = sev.downstreamconf; + + if (single_worker_) { + single_worker_->replace_downstream_config(sev.downstreamconf); + + break; + } + + worker_replace_downstream(sev.downstreamconf); + + break; + } + } +} + +void ConnectionHandler::send_replace_downstream( + const std::shared_ptr &downstreamconf) { + send_serial_event(SerialEvent(SEV_REPLACE_DOWNSTREAM, downstreamconf)); +} + +void ConnectionHandler::send_serial_event(SerialEvent ev) { + { + std::lock_guard g(serial_event_mu_); + + serial_events_.push_back(std::move(ev)); + } + + ev_async_send(loop_, &serial_event_asyncev_); +} + } // namespace shrpx diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h index 8602b359..cd805f57 100644 --- a/src/shrpx_connection_handler.h +++ b/src/shrpx_connection_handler.h @@ -48,6 +48,7 @@ #endif // HAVE_NEVERBLEED #include "shrpx_downstream_connection_pool.h" +#include "shrpx_config.h" namespace shrpx { @@ -76,6 +77,21 @@ struct OCSPUpdateContext { pid_t pid; }; +// SerialEvent is an event sent from Worker thread. +enum SerialEventType { + SEV_NONE, + SEV_REPLACE_DOWNSTREAM, +}; + +struct SerialEvent { + // ctor for event uses DownstreamConfig + SerialEvent(int type, const std::shared_ptr &downstreamconf) + : type(type), downstreamconf(downstreamconf) {} + + int type; + std::shared_ptr downstreamconf; +}; + class ConnectionHandler { public: ConnectionHandler(struct ev_loop *loop); @@ -136,6 +152,17 @@ public: neverbleed_t *get_neverbleed() const; #endif // HAVE_NEVERBLEED + // Send SerialEvent SEV_REPLACE_DOWNSTREAM to this object. + void send_replace_downstream( + const std::shared_ptr &downstreamconf); + // Internal function to send |ev| to this object. + void send_serial_event(SerialEvent ev); + // Handles SerialEvents received. + void handle_serial_event(); + // Sends WorkerEvent to make them replace downstream. + void + worker_replace_downstream(std::shared_ptr downstreamconf); + private: // Stores all SSL_CTX objects. std::vector all_ssl_ctx_; @@ -145,6 +172,10 @@ private: std::vector worker_loops_; // Worker instances when multi threaded mode (-nN, N >= 2) is used. std::vector> workers_; + // mutex for serial event resive buffer handling + std::mutex serial_event_mu_; + // SerialEvent receive buffer + std::vector serial_events_; // Worker instance used when single threaded mode (-n1) is used. // Otherwise, nullptr and workers_ has instances of Worker instead. std::unique_ptr single_worker_; @@ -161,6 +192,7 @@ private: ev_timer disable_acceptor_timer_; ev_timer ocsp_timer_; ev_async thread_join_asyncev_; + ev_async serial_event_asyncev_; #ifndef NOTHREADS std::future thread_join_fut_; #endif // NOTHREADS diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 660588c8..64e9d0e8 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -106,13 +106,16 @@ std::random_device rd; Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, SSL_CTX *tls_session_cache_memcached_ssl_ctx, ssl::CertLookupTree *cert_tree, - const std::shared_ptr &ticket_keys) + const std::shared_ptr &ticket_keys, + ConnectionHandler *conn_handler, + std::shared_ptr downstreamconf) : randgen_(rd()), worker_stat_{}, loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), + conn_handler_(conn_handler), ticket_keys_(ticket_keys), connect_blocker_( make_unique(randgen_, loop_, []() {}, []() {})), @@ -133,11 +136,11 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, StringRef{session_cacheconf.memcached.host}, &mcpool_); } - replace_downstream_config(get_config()->conn.downstream); + replace_downstream_config(std::move(downstreamconf)); } void Worker::replace_downstream_config( - const std::shared_ptr &downstreamconf) { + std::shared_ptr downstreamconf) { downstreamconf_ = downstreamconf; downstream_addr_groups_ = std::vector>( @@ -345,6 +348,12 @@ void Worker::process_events() { return; } + break; + case REPLACE_DOWNSTREAM: + WLOG(NOTICE, this) << "Replace downstream"; + + replace_downstream_config(wev.downstreamconf); + break; default: if (LOG_ENABLED(INFO)) { @@ -416,6 +425,10 @@ const DownstreamConfig *Worker::get_downstream_config() const { return downstreamconf_.get(); } +ConnectionHandler *Worker::get_connection_handler() const { + return conn_handler_; +} + namespace { size_t match_downstream_addr_group_host( const Router &router, const std::vector &wildcard_patterns, diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h index 240d80e3..c3e3e04a 100644 --- a/src/shrpx_worker.h +++ b/src/shrpx_worker.h @@ -56,6 +56,7 @@ class ConnectBlocker; class LiveCheck; class MemcachedDispatcher; struct UpstreamAddr; +class ConnectionHandler; #ifdef HAVE_MRUBY namespace mruby { @@ -153,6 +154,7 @@ enum WorkerEventType { NEW_CONNECTION = 0x01, REOPEN_LOG = 0x02, GRACEFUL_SHUTDOWN = 0x03, + REPLACE_DOWNSTREAM = 0x04, }; struct WorkerEvent { @@ -164,6 +166,7 @@ struct WorkerEvent { const UpstreamAddr *faddr; }; std::shared_ptr ticket_keys; + std::shared_ptr downstreamconf; }; class Worker { @@ -171,7 +174,9 @@ public: Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, SSL_CTX *tls_session_cache_memcached_ssl_ctx, ssl::CertLookupTree *cert_tree, - const std::shared_ptr &ticket_keys); + const std::shared_ptr &ticket_keys, + ConnectionHandler *conn_handler, + std::shared_ptr downstreamconf); ~Worker(); void run_async(); void wait(); @@ -213,8 +218,10 @@ public: const DownstreamConfig *get_downstream_config() const; - void replace_downstream_config( - const std::shared_ptr &downstreamconf); + void + replace_downstream_config(std::shared_ptr downstreamconf); + + ConnectionHandler *get_connection_handler() const; private: #ifndef NOTHREADS @@ -240,6 +247,7 @@ private: SSL_CTX *sv_ssl_ctx_; SSL_CTX *cl_ssl_ctx_; ssl::CertLookupTree *cert_tree_; + ConnectionHandler *conn_handler_; std::shared_ptr ticket_keys_; std::vector> downstream_addr_groups_; From cb7269f3345c1965597cc1ebdf2d053ec3894a9b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 12:16:31 +0900 Subject: [PATCH 09/24] nghttpx: Close and disallow h1 backend connection on backend replacement --- src/shrpx_downstream_connection_pool.cc | 6 +++++- src/shrpx_downstream_connection_pool.h | 1 + src/shrpx_http_downstream_connection.cc | 8 +++++++- src/shrpx_http_downstream_connection.h | 2 +- src/shrpx_worker.cc | 12 +++++++----- src/shrpx_worker.h | 4 ++++ 6 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/shrpx_downstream_connection_pool.cc b/src/shrpx_downstream_connection_pool.cc index c58ed81a..0ee66b60 100644 --- a/src/shrpx_downstream_connection_pool.cc +++ b/src/shrpx_downstream_connection_pool.cc @@ -29,10 +29,14 @@ namespace shrpx { DownstreamConnectionPool::DownstreamConnectionPool() {} -DownstreamConnectionPool::~DownstreamConnectionPool() { +DownstreamConnectionPool::~DownstreamConnectionPool() { remove_all(); } + +void DownstreamConnectionPool::remove_all() { for (auto dconn : pool_) { delete dconn; } + + pool_.clear(); } void DownstreamConnectionPool::add_downstream_connection( diff --git a/src/shrpx_downstream_connection_pool.h b/src/shrpx_downstream_connection_pool.h index c2edce45..34dc30d8 100644 --- a/src/shrpx_downstream_connection_pool.h +++ b/src/shrpx_downstream_connection_pool.h @@ -42,6 +42,7 @@ public: void add_downstream_connection(std::unique_ptr dconn); std::unique_ptr pop_downstream_connection(); void remove_downstream_connection(DownstreamConnection *dconn); + void remove_all(); private: std::set pool_; diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 0bd1aeac..14d1e000 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -166,7 +166,11 @@ HttpDownstreamConnection::HttpDownstreamConnection( ioctrl_(&conn_.rlimit), response_htp_{0} {} -HttpDownstreamConnection::~HttpDownstreamConnection() {} +HttpDownstreamConnection::~HttpDownstreamConnection() { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Deleted"; + } +} int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { if (LOG_ENABLED(INFO)) { @@ -1173,4 +1177,6 @@ HttpDownstreamConnection::get_downstream_addr_group() const { DownstreamAddr *HttpDownstreamConnection::get_addr() const { return addr_; } +bool HttpDownstreamConnection::poolable() const { return !group_->retired; } + } // namespace shrpx diff --git a/src/shrpx_http_downstream_connection.h b/src/shrpx_http_downstream_connection.h index cebed9f0..40b53f44 100644 --- a/src/shrpx_http_downstream_connection.h +++ b/src/shrpx_http_downstream_connection.h @@ -61,7 +61,7 @@ public: virtual void on_upstream_change(Upstream *upstream); - virtual bool poolable() const { return true; } + virtual bool poolable() const; virtual DownstreamAddrGroup *get_downstream_addr_group() const; diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 64e9d0e8..1a7cc90e 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -141,6 +141,11 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, void Worker::replace_downstream_config( std::shared_ptr downstreamconf) { + for (auto &g : downstream_addr_groups_) { + g->retired = true; + g->shared_addr->dconn_pool.remove_all(); + } + downstreamconf_ = downstreamconf; downstream_addr_groups_ = std::vector>( @@ -153,14 +158,11 @@ void Worker::replace_downstream_config( dst = std::make_shared(); dst->pattern = src.pattern; + // TODO for some reason, clang-3.6 which comes with Ubuntu 15.10 + // does not value initialize with std::make_shared. auto shared_addr = std::make_shared(); - // TODO for some reason, clang-3.6 which comes with Ubuntu 15.10 - // does not value initialize SharedDownstreamAddr above. - shared_addr->next = 0; shared_addr->addrs.resize(src.addrs.size()); - shared_addr->http1_pri = {}; - shared_addr->http2_pri = {}; size_t num_http1 = 0; size_t num_http2 = 0; diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h index c3e3e04a..1a334723 100644 --- a/src/shrpx_worker.h +++ b/src/shrpx_worker.h @@ -144,6 +144,10 @@ struct SharedDownstreamAddr { struct DownstreamAddrGroup { ImmutableString pattern; std::shared_ptr shared_addr; + // true if this group is no longer used for new request. If this is + // true, the connection made using one of address in shared_addr + // must not be pooled. + bool retired; }; struct WorkerStat { From ef3fa23b2e5a134ffab86bbf302756913160049c Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 12:36:22 +0900 Subject: [PATCH 10/24] nghttpx: Send GOAWAY for retired h2 backend connection --- src/shrpx_http2_session.cc | 27 +++++++++++++++++++++++++++ src/shrpx_http2_session.h | 6 ++++++ 2 files changed, 33 insertions(+) diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 231f81db..6d5f4d43 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -171,6 +171,13 @@ void initiate_connection_cb(struct ev_loop *loop, ev_timer *w, int revents) { } } // namespace +namespace { +void prepare_cb(struct ev_loop *loop, ev_prepare *w, int revents) { + auto http2session = static_cast(w->data); + http2session->check_retire(); +} +} // namespace + Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker, const std::shared_ptr &group, @@ -211,6 +218,10 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, ev_timer_init(&initiate_connection_timer_, initiate_connection_cb, 0., 0.); initiate_connection_timer_.data = this; + + ev_prepare_init(&prep_, prepare_cb); + prep_.data = this; + ev_prepare_start(loop, &prep_); } Http2Session::~Http2Session() { @@ -230,6 +241,8 @@ int Http2Session::disconnect(bool hard) { conn_.rlimit.stopw(); conn_.wlimit.stopw(); + ev_prepare_stop(conn_.loop, &prep_); + ev_timer_stop(conn_.loop, &initiate_connection_timer_); ev_timer_stop(conn_.loop, &settings_timer_); ev_timer_stop(conn_.loop, &connchk_timer_); @@ -2195,4 +2208,18 @@ void Http2Session::on_timeout() { } } +void Http2Session::check_retire() { + if (!group_->retired) { + return; + } + + ev_prepare_stop(conn_.loop, &prep_); + + auto last_stream_id = nghttp2_session_get_last_proc_stream_id(session_); + nghttp2_submit_goaway(session_, NGHTTP2_FLAG_NONE, last_stream_id, + NGHTTP2_NO_ERROR, nullptr, 0); + + signal_write(); +} + } // namespace shrpx diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h index 318d87cb..a3b0a223 100644 --- a/src/shrpx_http2_session.h +++ b/src/shrpx_http2_session.h @@ -200,6 +200,11 @@ public: void on_timeout(); + // This is called periodically using ev_prepare watcher, and if + // group_ is retired (backend has been replaced), send GOAWAY to + // shutdown the connection. + void check_retire(); + enum { // Disconnected DISCONNECTED, @@ -241,6 +246,7 @@ private: ev_timer connchk_timer_; // timer to initiate connection. usually, this fires immediately. ev_timer initiate_connection_timer_; + ev_prepare prep_; DList dconns_; DList streams_; std::function read_, write_; From 9237d30e34ceeeac6083afa89d09c86f834d692d Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 12:38:39 +0900 Subject: [PATCH 11/24] nghttpx: Remove flow_control_ from Http2Session This is a legacy of SPDY era where it can disable flow control. --- src/shrpx_http2_downstream_connection.cc | 3 +-- src/shrpx_http2_session.cc | 7 +------ src/shrpx_http2_session.h | 3 --- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 18de7e78..bb7e60dc 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -479,8 +479,7 @@ int Http2DownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { int rv; - if (http2session_->get_state() != Http2Session::CONNECTED || - !http2session_->get_flow_control()) { + if (http2session_->get_state() != Http2Session::CONNECTED) { return 0; } diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 6d5f4d43..1ad416fb 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -197,8 +197,7 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, session_(nullptr), state_(DISCONNECTED), connection_check_state_(CONNECTION_CHECK_NONE), - freelist_zone_(FREELIST_ZONE_NONE), - flow_control_(false) { + freelist_zone_(FREELIST_ZONE_NONE) { read_ = write_ = &Http2Session::noop; on_read_ = &Http2Session::read_noop; @@ -675,8 +674,6 @@ int Http2Session::submit_rst_stream(int32_t stream_id, uint32_t error_code) { nghttp2_session *Http2Session::get_session() const { return session_; } -bool Http2Session::get_flow_control() const { return flow_control_; } - int Http2Session::resume_data(Http2DownstreamConnection *dconn) { assert(state_ == CONNECTED); auto downstream = dconn->get_downstream(); @@ -1525,8 +1522,6 @@ int Http2Session::connection_made() { return -1; } - flow_control_ = true; - std::array entry; size_t nentry = 2; entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; diff --git a/src/shrpx_http2_session.h b/src/shrpx_http2_session.h index a3b0a223..615b1020 100644 --- a/src/shrpx_http2_session.h +++ b/src/shrpx_http2_session.h @@ -96,8 +96,6 @@ public: nghttp2_session *get_session() const; - bool get_flow_control() const; - int resume_data(Http2DownstreamConnection *dconn); int connection_made(); @@ -264,7 +262,6 @@ private: int state_; int connection_check_state_; int freelist_zone_; - bool flow_control_; }; nghttp2_session_callbacks *create_http2_downstream_callbacks(); From d0bf247419899d40ccd30f47cb343e364050a9a5 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 12:43:17 +0900 Subject: [PATCH 12/24] nghttpx: Refactor graceful shutdown in Http2Upstream Instead of using bool flag, just stop prepare watcher. --- src/shrpx_http2_upstream.cc | 32 ++++++++++++++++---------------- src/shrpx_http2_upstream.h | 1 - 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 8f9e4645..27b0f718 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -787,23 +787,25 @@ void Http2Upstream::submit_goaway() { void Http2Upstream::check_shutdown() { int rv; - if (shutdown_handled_) { - return; - } auto worker = handler_->get_worker(); - if (worker->get_graceful_shutdown()) { - shutdown_handled_ = true; - rv = nghttp2_submit_shutdown_notice(session_); - if (rv != 0) { - ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: " - << nghttp2_strerror(rv); - return; - } - handler_->signal_write(); - ev_timer_start(handler_->get_loop(), &shutdown_timer_); + if (!worker->get_graceful_shutdown()) { + return; } + + ev_prepare_stop(handler_->get_loop(), &prep_); + + rv = nghttp2_submit_shutdown_notice(session_); + if (rv != 0) { + ULOG(FATAL, this) << "nghttp2_submit_shutdown_notice() failed: " + << nghttp2_strerror(rv); + return; + } + + handler_->signal_write(); + + ev_timer_start(handler_->get_loop(), &shutdown_timer_); } nghttp2_session_callbacks *create_http2_upstream_callbacks() { @@ -870,9 +872,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) downstream_queue_(downstream_queue_size(handler->get_worker()), !get_config()->http2_proxy), handler_(handler), - session_(nullptr), - shutdown_handled_(false) { - + session_(nullptr) { int rv; auto &http2conf = get_config()->http2; diff --git a/src/shrpx_http2_upstream.h b/src/shrpx_http2_upstream.h index 1908a504..80585031 100644 --- a/src/shrpx_http2_upstream.h +++ b/src/shrpx_http2_upstream.h @@ -132,7 +132,6 @@ private: ClientHandler *handler_; nghttp2_session *session_; bool flow_control_; - bool shutdown_handled_; }; nghttp2_session_callbacks *create_http2_upstream_callbacks(); From 2a504224de6d0fdbfb980a1521ccf4a137278595 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 16:23:31 +0900 Subject: [PATCH 13/24] nghttpx: Rename BlockAllocator::destroy as BlockAllocator::reset --- src/allocator.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/allocator.h b/src/allocator.h index 5a379ac0..c3302efc 100644 --- a/src/allocator.h +++ b/src/allocator.h @@ -54,7 +54,7 @@ struct BlockAllocator { block_size(block_size), isolation_threshold(std::min(block_size, isolation_threshold)) {} - ~BlockAllocator() { destroy(); } + ~BlockAllocator() { reset(); } BlockAllocator(BlockAllocator &&other) noexcept : retain(other.retain), @@ -66,7 +66,7 @@ struct BlockAllocator { } BlockAllocator &operator=(BlockAllocator &&other) noexcept { - destroy(); + reset(); retain = other.retain; head = other.head; @@ -82,12 +82,15 @@ struct BlockAllocator { BlockAllocator(const BlockAllocator &) = delete; BlockAllocator &operator=(const BlockAllocator &) = delete; - void destroy() { + void reset() { for (auto mb = retain; mb;) { auto next = mb->next; delete[] reinterpret_cast(mb); mb = next; } + + retain = nullptr; + head = nullptr; } MemBlock *alloc_mem_block(size_t size) { From d837887af6e2f5905cb4309edcd35750c2f3a539 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 16:23:50 +0900 Subject: [PATCH 14/24] nghttpx: Avoid copy --- src/shrpx_api_downstream_connection.cc | 2 +- src/shrpx_downstream.cc | 8 ++++---- src/shrpx_http2_upstream.cc | 6 +++--- src/shrpx_spdy_upstream.cc | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index b6eaca14..2501d9da 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -131,7 +131,7 @@ int APIDownstreamConnection::end_upload_data() { config.conn.downstream = std::make_shared(); const auto &downstreamconf = config.conn.downstream; - auto src = get_config()->conn.downstream; + auto &src = get_config()->conn.downstream; downstreamconf->timeout = src->timeout; downstreamconf->connections_per_host = src->connections_per_host; diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index c0a1c15e..60ac686d 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -510,8 +510,8 @@ bool Downstream::request_buf_full() { } if (dconn_) { - auto downstreamconf = worker->get_downstream_config(); - return request_buf_.rleft() >= downstreamconf->request_buffer_size; + auto &downstreamconf = *worker->get_downstream_config(); + return request_buf_.rleft() >= downstreamconf.request_buffer_size; } return false; @@ -604,9 +604,9 @@ bool Downstream::response_buf_full() { if (dconn_) { auto handler = upstream_->get_client_handler(); auto worker = handler->get_worker(); - auto downstreamconf = worker->get_downstream_config(); + auto &downstreamconf = *worker->get_downstream_config(); - return response_buf_.rleft() >= downstreamconf->response_buffer_size; + return response_buf_.rleft() >= downstreamconf.response_buffer_size; } return false; diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 27b0f718..6f7aa393 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -857,13 +857,13 @@ nghttp2_session_callbacks *create_http2_upstream_callbacks() { namespace { size_t downstream_queue_size(Worker *worker) { - auto downstreamconf = worker->get_downstream_config(); + auto &downstreamconf = *worker->get_downstream_config(); if (get_config()->http2_proxy) { - return downstreamconf->connections_per_host; + return downstreamconf.connections_per_host; } - return downstreamconf->connections_per_frontend; + return downstreamconf.connections_per_frontend; } } // namespace diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 2334ef6d..a618a158 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -514,13 +514,13 @@ uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) { namespace { size_t downstream_queue_size(Worker *worker) { - auto downstreamconf = worker->get_downstream_config(); + auto &downstreamconf = *worker->get_downstream_config(); if (get_config()->http2_proxy) { - return downstreamconf->connections_per_host; + return downstreamconf.connections_per_host; } - return downstreamconf->connections_per_frontend; + return downstreamconf.connections_per_frontend; } } // namespace From 9653ae98a6ab97e37548149afa6f8434c662c851 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 17:23:21 +0900 Subject: [PATCH 15/24] nghttpx: Send 100-continue for API request --- src/shrpx_https_upstream.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 1dff8cd6..84973546 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -411,6 +411,23 @@ int htp_hdrs_completecb(http_parser *htp) { return -1; } + auto faddr = handler->get_upstream_addr(); + + if (faddr->api) { + // Normally, we forward expect: 100-continue to backend server, + // and let them decide whether responds with 100 Continue or not. + // For API endpoint, we have no backend, so just send 100 Continue + // here to make the client happy. + auto expect = req.fs.header(http2::HD_EXPECT); + if (expect && + util::strieq(expect->value, StringRef::from_lit("100-continue"))) { + auto output = downstream->get_response_buf(); + constexpr auto res = StringRef::from_lit("HTTP/1.1 100 Continue\r\n\r\n"); + output->append(res); + handler->signal_write(); + } + } + return 0; } } // namespace From 951ef0c6d502519e672b694a16f7e63f2ec2e521 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 17:23:47 +0900 Subject: [PATCH 16/24] nghttpx: Fix typo --- src/shrpx_https_upstream.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 84973546..24c80c7d 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -467,7 +467,7 @@ int htp_msg_completecb(http_parser *htp) { // reason why end_upload_data() failed is when we sent response // in request phase hook. We only delete and proceed to the // next request handling (if we don't close the connection). We - // first pause parser here jsut as we normally do, and call + // first pause parser here just as we normally do, and call // signal_write() to run on_write(). http_parser_pause(htp, 1); From 8288f5713b78231d5b2a7b0db9b6c3af9a1dccd2 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 17:24:54 +0900 Subject: [PATCH 17/24] nghttpx: Add --api-max-request-body option to set maximum API request body size --- gennghttpxfun.py | 1 + src/shrpx.cc | 14 +++++++++++ src/shrpx_api_downstream_connection.cc | 34 ++++++++++++++++++++++++-- src/shrpx_config.cc | 8 ++++++ src/shrpx_config.h | 8 ++++++ 5 files changed, 63 insertions(+), 2 deletions(-) diff --git a/gennghttpxfun.py b/gennghttpxfun.py index 2f72cb06..ab9e7e44 100755 --- a/gennghttpxfun.py +++ b/gennghttpxfun.py @@ -132,6 +132,7 @@ OPTIONS = [ "no-kqueue", "frontend-http2-settings-timeout", "backend-http2-settings-timeout", + "api-max-request-body", ] LOGVARS = [ diff --git a/src/shrpx.cc b/src/shrpx.cc index fcc45638..044e272b 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1183,6 +1183,9 @@ void fill_default_config() { downstreamconf.response_buffer_size = 128_k; downstreamconf.family = AF_UNSPEC; } + + auto &apiconf = mod_config()->api; + apiconf.max_request_body = 16_k; } } // namespace @@ -1914,6 +1917,12 @@ HTTP: HTTP status code. If error status code comes from backend server, the custom error pages are not used. +API: + --api-max-request-body= + Set the maximum size of request body for API request. + Default: )" << util::utos_unit(get_config()->api.max_request_body) + << R"( + Debug: --frontend-http2-dump-request-header= Dumps request headers received by HTTP/2 frontend to the @@ -2447,6 +2456,7 @@ int main(int argc, char **argv) { &flag, 124}, {SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT.c_str(), required_argument, &flag, 125}, + {SHRPX_OPT_API_MAX_REQUEST_BODY.c_str(), required_argument, &flag, 126}, {nullptr, 0, nullptr, 0}}; int option_index = 0; @@ -3036,6 +3046,10 @@ int main(int argc, char **argv) { cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT, StringRef{optarg}); break; + case 126: + // --api-max-request-body + cmdcfgs.emplace_back(SHRPX_OPT_API_MAX_REQUEST_BODY, StringRef{optarg}); + break; default: break; } diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index 2501d9da..fb4867a8 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -87,6 +87,15 @@ int APIDownstreamConnection::push_request_headers() { return 0; } + // This works with req.fs.content_length == -1 + if (req.fs.content_length > + static_cast(get_config()->api.max_request_body)) { + send_reply( + 413, http2::get_status_string(downstream_->get_block_allocator(), 413)); + + return 0; + } + return 0; } @@ -98,7 +107,15 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, auto output = downstream_->get_request_buf(); - // TODO limit the maximum payload size + auto &apiconf = get_config()->api; + + if (output->rleft() + datalen > apiconf.max_request_body) { + send_reply( + 413, http2::get_status_string(downstream_->get_block_allocator(), 413)); + + return 0; + } + output->append(data, datalen); // We don't have to call Upstream::resume_read() here, because @@ -116,7 +133,7 @@ int APIDownstreamConnection::end_upload_data() { auto output = downstream_->get_request_buf(); struct iovec iov; - auto iovcnt = output->riovec(&iov, 1); + auto iovcnt = output->riovec(&iov, 2); constexpr auto body = StringRef::from_lit("200 OK"); constexpr auto error_body = StringRef::from_lit("400 Bad Request"); @@ -127,6 +144,19 @@ int APIDownstreamConnection::end_upload_data() { return 0; } + std::unique_ptr large_buf; + + // If data spans in multiple chunks, pull them up into one + // contiguous buffer. + if (iovcnt > 1) { + large_buf = make_unique(output->rleft()); + auto len = output->rleft(); + output->remove(large_buf.get(), len); + + iov.iov_base = large_buf.get(); + iov.iov_len = len; + } + Config config{}; config.conn.downstream = std::make_shared(); const auto &downstreamconf = config.conn.downstream; diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 5586e934..92d63e21 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -915,6 +915,7 @@ enum { SHRPX_OPTID_ADD_RESPONSE_HEADER, SHRPX_OPTID_ADD_X_FORWARDED_FOR, SHRPX_OPTID_ALTSVC, + SHRPX_OPTID_API_MAX_REQUEST_BODY, SHRPX_OPTID_BACKEND, SHRPX_OPTID_BACKEND_ADDRESS_FAMILY, SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND, @@ -1429,6 +1430,11 @@ int option_lookup_token(const char *name, size_t namelen) { return SHRPX_OPTID_VERIFY_CLIENT_CACERT; } break; + case 'y': + if (util::strieq_l("api-max-request-bod", name, 19)) { + return SHRPX_OPTID_API_MAX_REQUEST_BODY; + } + break; } break; case 21: @@ -2712,6 +2718,8 @@ int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, case SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT: return parse_duration(&config->http2.downstream.timeout.settings, opt, optarg); + case SHRPX_OPTID_API_MAX_REQUEST_BODY: + return parse_uint_with_unit(&config->api.max_request_body, opt, optarg); case SHRPX_OPTID_CONF: LOG(WARN) << "conf: ignored"; diff --git a/src/shrpx_config.h b/src/shrpx_config.h index c1d71c8c..40b626fd 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -280,6 +280,8 @@ constexpr auto SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT = StringRef::from_lit("frontend-http2-settings-timeout"); constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT = StringRef::from_lit("backend-http2-settings-timeout"); +constexpr auto SHRPX_OPT_API_MAX_REQUEST_BODY = + StringRef::from_lit("api-max-request-body"); constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; @@ -649,6 +651,11 @@ struct ConnectionConfig { std::shared_ptr downstream; }; +struct APIConfig { + // Maximum request body size for one API request + size_t max_request_body; +}; + struct Config { HttpProxy downstream_http_proxy; HttpConfig http; @@ -656,6 +663,7 @@ struct Config { TLSConfig tls; LoggingConfig logging; ConnectionConfig conn; + APIConfig api; ImmutableString pid_file; ImmutableString conf_path; ImmutableString user; From 851cbd49f4477cc622f1ab9346e4aafc72d0dd65 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 17:41:06 +0900 Subject: [PATCH 18/24] nghttpx: Only parse backend option for API request for now --- src/shrpx_api_downstream_connection.cc | 16 ++- src/shrpx_config.cc | 145 +------------------------ src/shrpx_config.h | 143 ++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 141 deletions(-) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index fb4867a8..14b455c1 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -191,8 +191,20 @@ int APIDownstreamConnection::end_upload_data() { return 0; } - if (parse_config(&config, StringRef{first, eq}, StringRef{eq + 1, eol}, - include_set) != 0) { + auto opt = StringRef{first, eq}; + auto optval = StringRef{eq + 1, eol}; + + auto optid = option_lookup_token(opt.c_str(), opt.size()); + + switch (optid) { + case SHRPX_OPTID_BACKEND: + break; + default: + first = ++eol; + continue; + } + + if (parse_config(&config, optid, opt, optval, include_set) != 0) { send_reply(400, error_body); return 0; } diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 92d63e21..0fa7082f 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -904,142 +904,6 @@ int parse_error_page(std::vector &error_pages, const StringRef &opt, } } // namespace -// generated by gennghttpxfun.py -enum { - SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL, - SHRPX_OPTID_ACCESSLOG_FILE, - SHRPX_OPTID_ACCESSLOG_FORMAT, - SHRPX_OPTID_ACCESSLOG_SYSLOG, - SHRPX_OPTID_ADD_FORWARDED, - SHRPX_OPTID_ADD_REQUEST_HEADER, - SHRPX_OPTID_ADD_RESPONSE_HEADER, - SHRPX_OPTID_ADD_X_FORWARDED_FOR, - SHRPX_OPTID_ALTSVC, - SHRPX_OPTID_API_MAX_REQUEST_BODY, - SHRPX_OPTID_BACKEND, - SHRPX_OPTID_BACKEND_ADDRESS_FAMILY, - SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND, - SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST, - SHRPX_OPTID_BACKEND_HTTP_PROXY_URI, - SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, - SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST, - SHRPX_OPTID_BACKEND_HTTP1_TLS, - SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, - SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, - SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS, - SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT, - SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS, - SHRPX_OPTID_BACKEND_IPV4, - SHRPX_OPTID_BACKEND_IPV6, - SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT, - SHRPX_OPTID_BACKEND_NO_TLS, - SHRPX_OPTID_BACKEND_READ_TIMEOUT, - SHRPX_OPTID_BACKEND_REQUEST_BUFFER, - SHRPX_OPTID_BACKEND_RESPONSE_BUFFER, - SHRPX_OPTID_BACKEND_TLS, - SHRPX_OPTID_BACKEND_TLS_SNI_FIELD, - SHRPX_OPTID_BACKEND_WRITE_TIMEOUT, - SHRPX_OPTID_BACKLOG, - SHRPX_OPTID_CACERT, - SHRPX_OPTID_CERTIFICATE_FILE, - SHRPX_OPTID_CIPHERS, - SHRPX_OPTID_CLIENT, - SHRPX_OPTID_CLIENT_CERT_FILE, - SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE, - SHRPX_OPTID_CLIENT_PROXY, - SHRPX_OPTID_CONF, - SHRPX_OPTID_DAEMON, - SHRPX_OPTID_DH_PARAM_FILE, - SHRPX_OPTID_ERROR_PAGE, - SHRPX_OPTID_ERRORLOG_FILE, - SHRPX_OPTID_ERRORLOG_SYSLOG, - SHRPX_OPTID_FASTOPEN, - SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE, - SHRPX_OPTID_FORWARDED_BY, - SHRPX_OPTID_FORWARDED_FOR, - SHRPX_OPTID_FRONTEND, - SHRPX_OPTID_FRONTEND_FRAME_DEBUG, - SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, - SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, - SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, - SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS, - SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT, - SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT, - SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS, - SHRPX_OPTID_FRONTEND_NO_TLS, - SHRPX_OPTID_FRONTEND_READ_TIMEOUT, - SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT, - SHRPX_OPTID_HEADER_FIELD_BUFFER, - SHRPX_OPTID_HOST_REWRITE, - SHRPX_OPTID_HTTP2_BRIDGE, - SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS, - SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING, - SHRPX_OPTID_HTTP2_PROXY, - SHRPX_OPTID_INCLUDE, - SHRPX_OPTID_INSECURE, - SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT, - SHRPX_OPTID_LOG_LEVEL, - SHRPX_OPTID_MAX_HEADER_FIELDS, - SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS, - SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS, - SHRPX_OPTID_MRUBY_FILE, - SHRPX_OPTID_NO_HOST_REWRITE, - SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST, - SHRPX_OPTID_NO_KQUEUE, - SHRPX_OPTID_NO_LOCATION_REWRITE, - SHRPX_OPTID_NO_OCSP, - SHRPX_OPTID_NO_SERVER_PUSH, - SHRPX_OPTID_NO_VIA, - SHRPX_OPTID_NPN_LIST, - SHRPX_OPTID_OCSP_UPDATE_INTERVAL, - SHRPX_OPTID_PADDING, - SHRPX_OPTID_PID_FILE, - SHRPX_OPTID_PRIVATE_KEY_FILE, - SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE, - SHRPX_OPTID_READ_BURST, - SHRPX_OPTID_READ_RATE, - SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER, - SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER, - SHRPX_OPTID_RLIMIT_NOFILE, - SHRPX_OPTID_STREAM_READ_TIMEOUT, - SHRPX_OPTID_STREAM_WRITE_TIMEOUT, - SHRPX_OPTID_STRIP_INCOMING_FORWARDED, - SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR, - SHRPX_OPTID_SUBCERT, - SHRPX_OPTID_SYSLOG_FACILITY, - SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT, - SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD, - SHRPX_OPTID_TLS_PROTO_LIST, - SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED, - SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY, - SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE, - SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, - SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS, - SHRPX_OPTID_TLS_TICKET_KEY_CIPHER, - SHRPX_OPTID_TLS_TICKET_KEY_FILE, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, - SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS, - SHRPX_OPTID_USER, - SHRPX_OPTID_VERIFY_CLIENT, - SHRPX_OPTID_VERIFY_CLIENT_CACERT, - SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS, - SHRPX_OPTID_WORKER_READ_BURST, - SHRPX_OPTID_WORKER_READ_RATE, - SHRPX_OPTID_WORKER_WRITE_BURST, - SHRPX_OPTID_WORKER_WRITE_RATE, - SHRPX_OPTID_WORKERS, - SHRPX_OPTID_WRITE_BURST, - SHRPX_OPTID_WRITE_RATE, - SHRPX_OPTID_MAXIDX, -}; - -namespace { // generated by gennghttpxfun.py int option_lookup_token(const char *name, size_t namelen) { switch (namelen) { @@ -1785,15 +1649,18 @@ int option_lookup_token(const char *name, size_t namelen) { } return -1; } -} // namespace int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, std::set &included_set) { + auto optid = option_lookup_token(opt.c_str(), opt.size()); + return parse_config(config, optid, opt, optarg, included_set); +} + +int parse_config(Config *config, int optid, const StringRef &opt, + const StringRef &optarg, std::set &included_set) { char host[NI_MAXHOST]; uint16_t port; - auto optid = option_lookup_token(opt.c_str(), opt.size()); - switch (optid) { case SHRPX_OPTID_BACKEND: { auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';'); diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 40b626fd..c22835cb 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -689,6 +689,144 @@ const Config *get_config(); Config *mod_config(); void create_config(); +// generated by gennghttpxfun.py +enum { + SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL, + SHRPX_OPTID_ACCESSLOG_FILE, + SHRPX_OPTID_ACCESSLOG_FORMAT, + SHRPX_OPTID_ACCESSLOG_SYSLOG, + SHRPX_OPTID_ADD_FORWARDED, + SHRPX_OPTID_ADD_REQUEST_HEADER, + SHRPX_OPTID_ADD_RESPONSE_HEADER, + SHRPX_OPTID_ADD_X_FORWARDED_FOR, + SHRPX_OPTID_ALTSVC, + SHRPX_OPTID_API_MAX_REQUEST_BODY, + SHRPX_OPTID_BACKEND, + SHRPX_OPTID_BACKEND_ADDRESS_FAMILY, + SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND, + SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST, + SHRPX_OPTID_BACKEND_HTTP_PROXY_URI, + SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND, + SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST, + SHRPX_OPTID_BACKEND_HTTP1_TLS, + SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, + SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, + SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS, + SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT, + SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS, + SHRPX_OPTID_BACKEND_IPV4, + SHRPX_OPTID_BACKEND_IPV6, + SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT, + SHRPX_OPTID_BACKEND_NO_TLS, + SHRPX_OPTID_BACKEND_READ_TIMEOUT, + SHRPX_OPTID_BACKEND_REQUEST_BUFFER, + SHRPX_OPTID_BACKEND_RESPONSE_BUFFER, + SHRPX_OPTID_BACKEND_TLS, + SHRPX_OPTID_BACKEND_TLS_SNI_FIELD, + SHRPX_OPTID_BACKEND_WRITE_TIMEOUT, + SHRPX_OPTID_BACKLOG, + SHRPX_OPTID_CACERT, + SHRPX_OPTID_CERTIFICATE_FILE, + SHRPX_OPTID_CIPHERS, + SHRPX_OPTID_CLIENT, + SHRPX_OPTID_CLIENT_CERT_FILE, + SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE, + SHRPX_OPTID_CLIENT_PROXY, + SHRPX_OPTID_CONF, + SHRPX_OPTID_DAEMON, + SHRPX_OPTID_DH_PARAM_FILE, + SHRPX_OPTID_ERROR_PAGE, + SHRPX_OPTID_ERRORLOG_FILE, + SHRPX_OPTID_ERRORLOG_SYSLOG, + SHRPX_OPTID_FASTOPEN, + SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE, + SHRPX_OPTID_FORWARDED_BY, + SHRPX_OPTID_FORWARDED_FOR, + SHRPX_OPTID_FRONTEND, + SHRPX_OPTID_FRONTEND_FRAME_DEBUG, + SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, + SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, + SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, + SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS, + SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT, + SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT, + SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS, + SHRPX_OPTID_FRONTEND_NO_TLS, + SHRPX_OPTID_FRONTEND_READ_TIMEOUT, + SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT, + SHRPX_OPTID_HEADER_FIELD_BUFFER, + SHRPX_OPTID_HOST_REWRITE, + SHRPX_OPTID_HTTP2_BRIDGE, + SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS, + SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING, + SHRPX_OPTID_HTTP2_PROXY, + SHRPX_OPTID_INCLUDE, + SHRPX_OPTID_INSECURE, + SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT, + SHRPX_OPTID_LOG_LEVEL, + SHRPX_OPTID_MAX_HEADER_FIELDS, + SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS, + SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS, + SHRPX_OPTID_MRUBY_FILE, + SHRPX_OPTID_NO_HOST_REWRITE, + SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST, + SHRPX_OPTID_NO_KQUEUE, + SHRPX_OPTID_NO_LOCATION_REWRITE, + SHRPX_OPTID_NO_OCSP, + SHRPX_OPTID_NO_SERVER_PUSH, + SHRPX_OPTID_NO_VIA, + SHRPX_OPTID_NPN_LIST, + SHRPX_OPTID_OCSP_UPDATE_INTERVAL, + SHRPX_OPTID_PADDING, + SHRPX_OPTID_PID_FILE, + SHRPX_OPTID_PRIVATE_KEY_FILE, + SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE, + SHRPX_OPTID_READ_BURST, + SHRPX_OPTID_READ_RATE, + SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER, + SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER, + SHRPX_OPTID_RLIMIT_NOFILE, + SHRPX_OPTID_STREAM_READ_TIMEOUT, + SHRPX_OPTID_STREAM_WRITE_TIMEOUT, + SHRPX_OPTID_STRIP_INCOMING_FORWARDED, + SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR, + SHRPX_OPTID_SUBCERT, + SHRPX_OPTID_SYSLOG_FACILITY, + SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT, + SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD, + SHRPX_OPTID_TLS_PROTO_LIST, + SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED, + SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY, + SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE, + SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE, + SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS, + SHRPX_OPTID_TLS_TICKET_KEY_CIPHER, + SHRPX_OPTID_TLS_TICKET_KEY_FILE, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE, + SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS, + SHRPX_OPTID_USER, + SHRPX_OPTID_VERIFY_CLIENT, + SHRPX_OPTID_VERIFY_CLIENT_CACERT, + SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS, + SHRPX_OPTID_WORKER_READ_BURST, + SHRPX_OPTID_WORKER_READ_RATE, + SHRPX_OPTID_WORKER_WRITE_BURST, + SHRPX_OPTID_WORKER_WRITE_RATE, + SHRPX_OPTID_WORKERS, + SHRPX_OPTID_WRITE_BURST, + SHRPX_OPTID_WRITE_RATE, + SHRPX_OPTID_MAXIDX, +}; + +// Looks up token for given option name |name| of length |namelen|. +int option_lookup_token(const char *name, size_t namelen); + // Parses option name |opt| and value |optarg|. The results are // stored into the object pointed by |config|. This function returns 0 // if it succeeds, or -1. The |included_set| contains the all paths @@ -697,6 +835,11 @@ void create_config(); int parse_config(Config *config, const StringRef &opt, const StringRef &optarg, std::set &included_set); +// Similar to parse_config() above, but additional |optid| which +// should be the return value of option_lookup_token(opt). +int parse_config(Config *config, int optid, const StringRef &opt, + const StringRef &optarg, std::set &included_set); + // Loads configurations from |filename| and stores them in statically // allocated Config object. This function returns 0 if it succeeds, or // -1. See parse_config() for |include_set|. From 92db6820d86ba9e6fdf5aa997e4fc872400af09e Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 17:43:48 +0900 Subject: [PATCH 19/24] nghttpx: Close API request connection for 400 and 413 response --- src/shrpx_api_downstream_connection.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index 14b455c1..ee1eb985 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -71,6 +71,15 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, resp.fs.add_header_token(StringRef::from_lit("content-length"), content_length, false, http2::HD_CONTENT_LENGTH); + switch (http_status) { + case 400: + case 413: + resp.fs.add_header_token(StringRef::from_lit("connection"), + StringRef::from_lit("close"), false, + http2::HD_CONNECTION); + break; + } + if (upstream->send_reply(downstream_, body.byte(), body.size()) != 0) { return -1; } From 27fa9c3c124d63f56bad252b5702191e5d7f7049 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 17:55:48 +0900 Subject: [PATCH 20/24] nghttpx: Only allow POST and PUT for API request --- src/shrpx_api_downstream_connection.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index ee1eb985..12cee7c7 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -73,6 +73,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, switch (http_status) { case 400: + case 405: case 413: resp.fs.add_header_token(StringRef::from_lit("connection"), StringRef::from_lit("close"), false, @@ -89,6 +90,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, int APIDownstreamConnection::push_request_headers() { auto &req = downstream_->request(); + auto &resp = downstream_->response(); if (req.path != StringRef::from_lit("/api/v1alpha1/backend/replace")) { send_reply(404, StringRef::from_lit("404 Not Found")); @@ -96,6 +98,15 @@ int APIDownstreamConnection::push_request_headers() { return 0; } + if (req.method != HTTP_POST && req.method != HTTP_PUT) { + resp.fs.add_header_token(StringRef::from_lit("allow"), + StringRef::from_lit("POST, PUT"), false, -1); + send_reply( + 405, http2::get_status_string(downstream_->get_block_allocator(), 405)); + + return 0; + } + // This works with req.fs.content_length == -1 if (req.fs.content_length > static_cast(get_config()->api.max_request_body)) { From aad2a24a22f3f38d279869858708c275654ecb0a Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 18:18:07 +0900 Subject: [PATCH 21/24] nghttpx: Use JSON for API resposne body --- src/shrpx_api_downstream_connection.cc | 69 +++++++++++++++++++------- src/shrpx_api_downstream_connection.h | 2 +- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index 12cee7c7..2c33372f 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -54,8 +54,15 @@ void APIDownstreamConnection::detach_downstream(Downstream *downstream) { downstream_ = nullptr; } +// API status, which is independent from HTTP status code. But +// generally, 2xx code for API_SUCCESS, and otherwise API_FAILURE. +enum { + API_SUCCESS, + API_FAILURE, +}; + int APIDownstreamConnection::send_reply(unsigned int http_status, - const StringRef &body) { + int api_status) { abandoned_ = true; auto upstream = downstream_->get_upstream(); @@ -66,7 +73,39 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, auto &balloc = downstream_->get_block_allocator(); - auto content_length = util::make_string_ref_uint(balloc, body.size()); + StringRef api_status_str; + + switch (api_status) { + case API_SUCCESS: + api_status_str = StringRef::from_lit("Success"); + break; + case API_FAILURE: + api_status_str = StringRef::from_lit("Failure"); + break; + default: + assert(0); + } + + constexpr auto M1 = StringRef::from_lit("{\"status\":\""); + constexpr auto M2 = StringRef::from_lit("\",\"code\":"); + constexpr auto M3 = StringRef::from_lit("}"); + + // 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 buf = make_byte_ref(balloc, buflen); + auto p = buf.base; + + p = std::copy(std::begin(M1), std::end(M1), p); + 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(M3), std::end(M3), p); + + buf.len = p - buf.base; + + auto content_length = util::make_string_ref_uint(balloc, buf.len); resp.fs.add_header_token(StringRef::from_lit("content-length"), content_length, false, http2::HD_CONTENT_LENGTH); @@ -81,7 +120,7 @@ int APIDownstreamConnection::send_reply(unsigned int http_status, break; } - if (upstream->send_reply(downstream_, body.byte(), body.size()) != 0) { + if (upstream->send_reply(downstream_, buf.base, buf.len) != 0) { return -1; } @@ -93,7 +132,7 @@ int APIDownstreamConnection::push_request_headers() { auto &resp = downstream_->response(); if (req.path != StringRef::from_lit("/api/v1alpha1/backend/replace")) { - send_reply(404, StringRef::from_lit("404 Not Found")); + send_reply(404, API_FAILURE); return 0; } @@ -101,8 +140,7 @@ int APIDownstreamConnection::push_request_headers() { if (req.method != HTTP_POST && req.method != HTTP_PUT) { resp.fs.add_header_token(StringRef::from_lit("allow"), StringRef::from_lit("POST, PUT"), false, -1); - send_reply( - 405, http2::get_status_string(downstream_->get_block_allocator(), 405)); + send_reply(405, API_FAILURE); return 0; } @@ -110,8 +148,7 @@ int APIDownstreamConnection::push_request_headers() { // This works with req.fs.content_length == -1 if (req.fs.content_length > static_cast(get_config()->api.max_request_body)) { - send_reply( - 413, http2::get_status_string(downstream_->get_block_allocator(), 413)); + send_reply(413, API_FAILURE); return 0; } @@ -130,8 +167,7 @@ int APIDownstreamConnection::push_upload_data_chunk(const uint8_t *data, auto &apiconf = get_config()->api; if (output->rleft() + datalen > apiconf.max_request_body) { - send_reply( - 413, http2::get_status_string(downstream_->get_block_allocator(), 413)); + send_reply(413, API_FAILURE); return 0; } @@ -155,11 +191,8 @@ int APIDownstreamConnection::end_upload_data() { struct iovec iov; auto iovcnt = output->riovec(&iov, 2); - constexpr auto body = StringRef::from_lit("200 OK"); - constexpr auto error_body = StringRef::from_lit("400 Bad Request"); - if (iovcnt == 0) { - send_reply(200, body); + send_reply(200, API_SUCCESS); return 0; } @@ -207,7 +240,7 @@ int APIDownstreamConnection::end_upload_data() { auto eq = std::find(first, eol, '='); if (eq == eol) { - send_reply(400, error_body); + send_reply(400, API_FAILURE); return 0; } @@ -225,7 +258,7 @@ int APIDownstreamConnection::end_upload_data() { } if (parse_config(&config, optid, opt, optval, include_set) != 0) { - send_reply(400, error_body); + send_reply(400, API_FAILURE); return 0; } @@ -235,7 +268,7 @@ int APIDownstreamConnection::end_upload_data() { auto &tlsconf = get_config()->tls; if (configure_downstream_group(&config, get_config()->http2_proxy, true, tlsconf) != 0) { - send_reply(400, error_body); + send_reply(400, API_FAILURE); return 0; } @@ -243,7 +276,7 @@ int APIDownstreamConnection::end_upload_data() { conn_handler->send_replace_downstream(downstreamconf); - send_reply(200, body); + send_reply(200, API_SUCCESS); return 0; } diff --git a/src/shrpx_api_downstream_connection.h b/src/shrpx_api_downstream_connection.h index f38b0fea..fdf49526 100644 --- a/src/shrpx_api_downstream_connection.h +++ b/src/shrpx_api_downstream_connection.h @@ -56,7 +56,7 @@ public: virtual DownstreamAddrGroup *get_downstream_addr_group() const; - int send_reply(unsigned int http_status, const StringRef &body); + int send_reply(unsigned int http_status, int api_status); private: Worker *worker_; From d3495405d9bd9acbbfc9f09b2dd6eb6a26d60191 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 18:37:37 +0900 Subject: [PATCH 22/24] nghttpx: Change API endpoint URI --- src/shrpx_api_downstream_connection.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shrpx_api_downstream_connection.cc b/src/shrpx_api_downstream_connection.cc index 2c33372f..5e13971d 100644 --- a/src/shrpx_api_downstream_connection.cc +++ b/src/shrpx_api_downstream_connection.cc @@ -131,7 +131,7 @@ int APIDownstreamConnection::push_request_headers() { auto &req = downstream_->request(); auto &resp = downstream_->response(); - if (req.path != StringRef::from_lit("/api/v1alpha1/backend/replace")) { + if (req.path != StringRef::from_lit("/api/v1beta1/backend/replace")) { send_reply(404, API_FAILURE); return 0; From fbdfecc1431f256c5a361c5df0af1d5c0655980b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 18:42:30 +0900 Subject: [PATCH 23/24] Add nghttpx API section --- doc/nghttpx.h2r | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/doc/nghttpx.h2r b/doc/nghttpx.h2r index a8f50b7d..8fd9430d 100644 --- a/doc/nghttpx.h2r +++ b/doc/nghttpx.h2r @@ -462,6 +462,42 @@ addresses: App.new +API ENDPOINTS +------------- + +nghttpx exposes API endpoints to manipulate it via HTTP based API. By +default, API endpoint is disabled. To enable it, add a dedicated +frontend for API using :option:`--frontend` option with "api" +parameter. All requests which come from this frontend address, will +be treated as API request. + +The following section describes available API endpoints. + +PUT /api/v1beta/backend/replace +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This API replaces the current set of backend servers with the +requested ones. The request must carry request body with method PUT +or POST. The request body must be nghttpx configuration file format. +For configuration file format, see `FILES`_ section. The line +separator inside the request body must be single LF (0x0A). +Currently, only :option:`backend <--backend>` option is parsed, the +others are simply ignored. The semantics of this API is replace the +current backend with the backend options in request body. Describe +the desired set of backend severs, and nghttpx makes it happen. If +there is no :option:`backend <--backend>` option is found in request +body, the current set of backend is replaced with the :option:`backend +<--backend>` option's default value, which is ``127.0.0.1,80``. + +The replacement is done instantly without breaking existing +connections or requests. It also avoids any process creation as is +the case with hot swapping with signals. + +The one limitation is that only numeric IP address is allowd in +:option:`backend <--backend>` in request body while non numeric +hostname is allowed in command-line or configuration file is read +using :option:`--conf`. + SEE ALSO -------- From 708c99c0527e77e66eac14290277854a8c6a60d0 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sat, 4 Jun 2016 18:48:16 +0900 Subject: [PATCH 24/24] nghttpx: Describe api parameter in --frontend option --- src/shrpx.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/shrpx.cc b/src/shrpx.cc index 044e272b..65d41635 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1341,6 +1341,13 @@ Connections: Optionally, TLS can be disabled by specifying "no-tls" parameter. TLS is enabled by default. + To make this frontend as API endpoint, specify "api" + parameter. This is disabled by default. It is + important to limit the access to the API frontend. + Otherwise, someone may change the backend server, and + break your services, or expose confidential information + to the outside the world. + Default: *,3000 --backlog= Set listen backlog size.