From 138419d2322308734a094c54d97821cb7dd893e1 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Fri, 13 Aug 2021 21:20:08 +0900 Subject: [PATCH] Add "dnf" (= "do not forward") parameter to backend option --- src/CMakeLists.txt | 1 + src/Makefile.am | 1 + src/shrpx.cc | 20 ++++-- src/shrpx_client_handler.cc | 8 +++ src/shrpx_config.cc | 13 ++++ src/shrpx_config.h | 4 ++ src/shrpx_https_upstream.cc | 7 ++ src/shrpx_null_downstream_connection.cc | 88 +++++++++++++++++++++++++ src/shrpx_null_downstream_connection.h | 68 +++++++++++++++++++ src/shrpx_worker.cc | 4 +- src/shrpx_worker.h | 2 + 11 files changed, 209 insertions(+), 7 deletions(-) create mode 100644 src/shrpx_null_downstream_connection.cc create mode 100644 src/shrpx_null_downstream_connection.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 12e9e782..8ec64d0e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -104,6 +104,7 @@ if(ENABLE_APP) shrpx_router.cc shrpx_api_downstream_connection.cc shrpx_health_monitor_downstream_connection.cc + shrpx_null_downstream_connection.cc shrpx_exec.cc shrpx_dns_resolver.cc shrpx_dual_dns_resolver.cc diff --git a/src/Makefile.am b/src/Makefile.am index 7301d3a7..033b7f8f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -139,6 +139,7 @@ NGHTTPX_SRCS = \ shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \ shrpx_health_monitor_downstream_connection.cc \ shrpx_health_monitor_downstream_connection.h \ + shrpx_null_downstream_connection.cc shrpx_null_downstream_connection.h \ shrpx_exec.cc shrpx_exec.h \ shrpx_dns_resolver.cc shrpx_dns_resolver.h \ shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \ diff --git a/src/shrpx.cc b/src/shrpx.cc index 24f64262..d33291f0 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1740,12 +1740,13 @@ Connections: "affinity=", "dns", "redirect-if-not-tls", "upgrade-scheme", "mruby=", "read-timeout=", "write-timeout=", - "group=", "group-weight=", and "weight=". - The parameter consists of keyword, and optionally - followed by "=" and value. For example, the parameter - "proto=h2" consists of the keyword "proto" and value - "h2". The parameter "tls" consists of the keyword "tls" - without value. Each parameter is described as follows. + "group=", "group-weight=", "weight=", and + "dnf". The parameter consists of keyword, and + optionally followed by "=" and value. For example, the + parameter "proto=h2" consists of the keyword "proto" and + value "h2". The parameter "tls" consists of the keyword + "tls" without value. Each parameter is described as + follows. The backend application protocol can be specified using optional "proto" parameter, and in the form of @@ -1876,6 +1877,13 @@ Connections: weight becomes 1. "weight" is ignored if session affinity is enabled. + If "dnf" parameter is specified, an incoming request is + not forwarded to a backend and just consumed along with + the request body (actually a backend server never be + contacted). It is expected that the HTTP response is + generated by mruby script (see "mruby=" parameter + above). "dnf" is an abbreviation of "do not forward". + Since ";" and ":" are used as delimiter, must not contain these characters. In order to include ":" in , one has to specify "%3A" (which is diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 3668c17a..9bbec8b0 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -50,6 +50,7 @@ #include "shrpx_connect_blocker.h" #include "shrpx_api_downstream_connection.h" #include "shrpx_health_monitor_downstream_connection.h" +#include "shrpx_null_downstream_connection.h" #include "shrpx_log.h" #include "util.h" #include "template.h" @@ -973,6 +974,13 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) { } auto &group = groups[group_idx]; + + if (group->shared_addr->dnf) { + auto dconn = std::make_unique(group); + dconn->set_client_handler(this); + return dconn; + } + auto addr = get_downstream_addr(err, group.get(), downstream); if (addr == nullptr) { return nullptr; diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 41043843..038c5f21 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -851,6 +851,7 @@ struct DownstreamParams { bool dns; bool redirect_if_not_tls; bool upgrade_scheme; + bool dnf; }; namespace { @@ -1025,6 +1026,8 @@ int parse_downstream_params(DownstreamParams &out, return -1; } out.group_weight = n; + } else if (util::strieq_l("dnf", param)) { + out.dnf = true; } else if (!param.empty()) { LOG(ERROR) << "backend: " << param << ": unknown keyword"; return -1; @@ -1089,6 +1092,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr, addr.sni = make_string_ref(downstreamconf.balloc, params.sni); addr.dns = params.dns; addr.upgrade_scheme = params.upgrade_scheme; + addr.dnf = params.dnf; auto &routerconf = downstreamconf.router; auto &router = routerconf.router; @@ -1189,6 +1193,14 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr, return -1; } } + // All backends in the same group must have the same dnf + // setting. If some backends do not specify dnf, and there is + // at least one backend with dnf, it is used for all backends in + // the group. In general, multiple backends are not necessary + // for dnf because there is no need for load balancing. + if (params.dnf) { + g.dnf = true; + } g.addrs.push_back(addr); continue; @@ -1213,6 +1225,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr, g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby); g.timeout.read = params.read_timeout; g.timeout.write = params.write_timeout; + g.dnf = params.dnf; if (pattern[0] == '*') { // wildcard pattern diff --git a/src/shrpx_config.h b/src/shrpx_config.h index c9de44d5..e5a0fc6a 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -494,6 +494,8 @@ struct DownstreamAddrConfig { // variant (e.g., "https") when forwarding request to a backend // connected by TLS connection. bool upgrade_scheme; + // true if a request should not be forwarded to a backend. + bool dnf; }; // Mapping hash to idx which is an index into @@ -523,6 +525,8 @@ struct DownstreamAddrGroupConfig { // true if this group requires that client connection must be TLS, // and the request must be redirected to https URI. bool redirect_if_not_tls; + // true if a request should not be forwarded to a backend. + bool dnf; // Timeouts for backend connection. struct { ev_tstamp read; diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index e7090d55..2b51af38 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -747,6 +747,13 @@ int HttpsUpstream::on_write() { handler_->repeat_read_timer(); return resume_read(SHRPX_NO_BUFFER, nullptr, 0); + } else { + // If the request is not complete, close the connection. + delete_downstream(); + + handler_->set_should_close_after_write(true); + + return 0; } } diff --git a/src/shrpx_null_downstream_connection.cc b/src/shrpx_null_downstream_connection.cc new file mode 100644 index 00000000..3a80b304 --- /dev/null +++ b/src/shrpx_null_downstream_connection.cc @@ -0,0 +1,88 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2021 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_null_downstream_connection.h" +#include "shrpx_upstream.h" +#include "shrpx_downstream.h" +#include "shrpx_log.h" + +namespace shrpx { + +NullDownstreamConnection::NullDownstreamConnection( + const std::shared_ptr &group) + : group_(group) {} + +NullDownstreamConnection::~NullDownstreamConnection() {} + +int NullDownstreamConnection::attach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; + } + + downstream_ = downstream; + + return 0; +} + +void NullDownstreamConnection::detach_downstream(Downstream *downstream) { + if (LOG_ENABLED(INFO)) { + DCLOG(INFO, this) << "Detaching from DOWNSTREAM:" << downstream; + } + downstream_ = nullptr; +} + +int NullDownstreamConnection::push_request_headers() { return 0; } + +int NullDownstreamConnection::push_upload_data_chunk(const uint8_t *data, + size_t datalen) { + return 0; +} + +int NullDownstreamConnection::end_upload_data() { return 0; } + +void NullDownstreamConnection::pause_read(IOCtrlReason reason) {} + +int NullDownstreamConnection::resume_read(IOCtrlReason reason, + size_t consumed) { + return 0; +} + +void NullDownstreamConnection::force_resume_read() {} + +int NullDownstreamConnection::on_read() { return 0; } + +int NullDownstreamConnection::on_write() { return 0; } + +void NullDownstreamConnection::on_upstream_change(Upstream *uptream) {} + +bool NullDownstreamConnection::poolable() const { return false; } + +const std::shared_ptr & +NullDownstreamConnection::get_downstream_addr_group() const { + return group_; +} + +DownstreamAddr *NullDownstreamConnection::get_addr() const { return nullptr; } + +} // namespace shrpx diff --git a/src/shrpx_null_downstream_connection.h b/src/shrpx_null_downstream_connection.h new file mode 100644 index 00000000..829d659e --- /dev/null +++ b/src/shrpx_null_downstream_connection.h @@ -0,0 +1,68 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2021 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_NULL_DOWNSTREAM_CONNECTION_H +#define SHRPX_NULL_DOWNSTREAM_CONNECTION_H + +#include "shrpx_downstream_connection.h" +#include "template.h" + +using namespace nghttp2; + +namespace shrpx { + +class NullDownstreamConnection : public DownstreamConnection { +public: + NullDownstreamConnection(const std::shared_ptr &group); + virtual ~NullDownstreamConnection(); + 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 const std::shared_ptr & + get_downstream_addr_group() const; + virtual DownstreamAddr *get_addr() const; + +private: + std::shared_ptr group_; +}; + +} // namespace shrpx + +#endif // SHRPX_NULL_DOWNSTREAM_CONNECTION_H diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 8273b58f..cf8854b2 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -82,7 +82,7 @@ using DownstreamKey = size_t, Proto, uint32_t, uint32_t, uint32_t, bool, bool, bool, bool>>, bool, SessionAffinity, StringRef, StringRef, - SessionAffinityCookieSecure, int64_t, int64_t, StringRef>; + SessionAffinityCookieSecure, int64_t, int64_t, StringRef, bool>; namespace { DownstreamKey @@ -122,6 +122,7 @@ create_downstream_key(const std::shared_ptr &shared_addr, std::get<6>(dkey) = timeout.read; std::get<7>(dkey) = timeout.write; std::get<8>(dkey) = mruby_file; + std::get<9>(dkey) = shared_addr->dnf; return dkey; } @@ -252,6 +253,7 @@ void Worker::replace_downstream_config( } shared_addr->affinity_hash = src.affinity_hash; shared_addr->redirect_if_not_tls = src.redirect_if_not_tls; + shared_addr->dnf = src.dnf; shared_addr->timeout.read = src.timeout.read; shared_addr->timeout.write = src.timeout.write; diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h index 9e256a00..e736aad8 100644 --- a/src/shrpx_worker.h +++ b/src/shrpx_worker.h @@ -216,6 +216,8 @@ struct SharedDownstreamAddr { // true if this group requires that client connection must be TLS, // and the request must be redirected to https URI. bool redirect_if_not_tls; + // true if a request should not be forwarded to a backend. + bool dnf; // Timeouts for backend connection. struct { ev_tstamp read;