Add "dnf" (= "do not forward") parameter to backend option

This commit is contained in:
Tatsuhiro Tsujikawa 2021-08-13 21:20:08 +09:00
parent 29cbf8b83f
commit 138419d232
11 changed files with 209 additions and 7 deletions

View File

@ -104,6 +104,7 @@ if(ENABLE_APP)
shrpx_router.cc shrpx_router.cc
shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.cc
shrpx_health_monitor_downstream_connection.cc shrpx_health_monitor_downstream_connection.cc
shrpx_null_downstream_connection.cc
shrpx_exec.cc shrpx_exec.cc
shrpx_dns_resolver.cc shrpx_dns_resolver.cc
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.cc

View File

@ -139,6 +139,7 @@ NGHTTPX_SRCS = \
shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \ shrpx_api_downstream_connection.cc shrpx_api_downstream_connection.h \
shrpx_health_monitor_downstream_connection.cc \ shrpx_health_monitor_downstream_connection.cc \
shrpx_health_monitor_downstream_connection.h \ shrpx_health_monitor_downstream_connection.h \
shrpx_null_downstream_connection.cc shrpx_null_downstream_connection.h \
shrpx_exec.cc shrpx_exec.h \ shrpx_exec.cc shrpx_exec.h \
shrpx_dns_resolver.cc shrpx_dns_resolver.h \ shrpx_dns_resolver.cc shrpx_dns_resolver.h \
shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \ shrpx_dual_dns_resolver.cc shrpx_dual_dns_resolver.h \

View File

@ -1740,12 +1740,13 @@ Connections:
"affinity=<METHOD>", "dns", "redirect-if-not-tls", "affinity=<METHOD>", "dns", "redirect-if-not-tls",
"upgrade-scheme", "mruby=<PATH>", "upgrade-scheme", "mruby=<PATH>",
"read-timeout=<DURATION>", "write-timeout=<DURATION>", "read-timeout=<DURATION>", "write-timeout=<DURATION>",
"group=<GROUP>", "group-weight=<N>", and "weight=<N>". "group=<GROUP>", "group-weight=<N>", "weight=<N>", and
The parameter consists of keyword, and optionally "dnf". The parameter consists of keyword, and
followed by "=" and value. For example, the parameter optionally followed by "=" and value. For example, the
"proto=h2" consists of the keyword "proto" and value parameter "proto=h2" consists of the keyword "proto" and
"h2". The parameter "tls" consists of the keyword "tls" value "h2". The parameter "tls" consists of the keyword
without value. Each parameter is described as follows. "tls" without value. Each parameter is described as
follows.
The backend application protocol can be specified using The backend application protocol can be specified using
optional "proto" parameter, and in the form of optional "proto" parameter, and in the form of
@ -1876,6 +1877,13 @@ Connections:
weight becomes 1. "weight" is ignored if session weight becomes 1. "weight" is ignored if session
affinity is enabled. 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=<PATH>" parameter
above). "dnf" is an abbreviation of "do not forward".
Since ";" and ":" are used as delimiter, <PATTERN> must Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. In order to include ":" not contain these characters. In order to include ":"
in <PATTERN>, one has to specify "%3A" (which is in <PATTERN>, one has to specify "%3A" (which is

View File

@ -50,6 +50,7 @@
#include "shrpx_connect_blocker.h" #include "shrpx_connect_blocker.h"
#include "shrpx_api_downstream_connection.h" #include "shrpx_api_downstream_connection.h"
#include "shrpx_health_monitor_downstream_connection.h" #include "shrpx_health_monitor_downstream_connection.h"
#include "shrpx_null_downstream_connection.h"
#include "shrpx_log.h" #include "shrpx_log.h"
#include "util.h" #include "util.h"
#include "template.h" #include "template.h"
@ -973,6 +974,13 @@ ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
} }
auto &group = groups[group_idx]; auto &group = groups[group_idx];
if (group->shared_addr->dnf) {
auto dconn = std::make_unique<NullDownstreamConnection>(group);
dconn->set_client_handler(this);
return dconn;
}
auto addr = get_downstream_addr(err, group.get(), downstream); auto addr = get_downstream_addr(err, group.get(), downstream);
if (addr == nullptr) { if (addr == nullptr) {
return nullptr; return nullptr;

View File

@ -851,6 +851,7 @@ struct DownstreamParams {
bool dns; bool dns;
bool redirect_if_not_tls; bool redirect_if_not_tls;
bool upgrade_scheme; bool upgrade_scheme;
bool dnf;
}; };
namespace { namespace {
@ -1025,6 +1026,8 @@ int parse_downstream_params(DownstreamParams &out,
return -1; return -1;
} }
out.group_weight = n; out.group_weight = n;
} else if (util::strieq_l("dnf", param)) {
out.dnf = true;
} else if (!param.empty()) { } else if (!param.empty()) {
LOG(ERROR) << "backend: " << param << ": unknown keyword"; LOG(ERROR) << "backend: " << param << ": unknown keyword";
return -1; return -1;
@ -1089,6 +1092,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
addr.sni = make_string_ref(downstreamconf.balloc, params.sni); addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
addr.dns = params.dns; addr.dns = params.dns;
addr.upgrade_scheme = params.upgrade_scheme; addr.upgrade_scheme = params.upgrade_scheme;
addr.dnf = params.dnf;
auto &routerconf = downstreamconf.router; auto &routerconf = downstreamconf.router;
auto &router = routerconf.router; auto &router = routerconf.router;
@ -1189,6 +1193,14 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
return -1; 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); g.addrs.push_back(addr);
continue; continue;
@ -1213,6 +1225,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby); g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
g.timeout.read = params.read_timeout; g.timeout.read = params.read_timeout;
g.timeout.write = params.write_timeout; g.timeout.write = params.write_timeout;
g.dnf = params.dnf;
if (pattern[0] == '*') { if (pattern[0] == '*') {
// wildcard pattern // wildcard pattern

View File

@ -494,6 +494,8 @@ struct DownstreamAddrConfig {
// variant (e.g., "https") when forwarding request to a backend // variant (e.g., "https") when forwarding request to a backend
// connected by TLS connection. // connected by TLS connection.
bool upgrade_scheme; 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 // 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, // true if this group requires that client connection must be TLS,
// and the request must be redirected to https URI. // and the request must be redirected to https URI.
bool redirect_if_not_tls; bool redirect_if_not_tls;
// true if a request should not be forwarded to a backend.
bool dnf;
// Timeouts for backend connection. // Timeouts for backend connection.
struct { struct {
ev_tstamp read; ev_tstamp read;

View File

@ -747,6 +747,13 @@ int HttpsUpstream::on_write() {
handler_->repeat_read_timer(); handler_->repeat_read_timer();
return resume_read(SHRPX_NO_BUFFER, nullptr, 0); 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;
} }
} }

View File

@ -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<DownstreamAddrGroup> &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<DownstreamAddrGroup> &
NullDownstreamConnection::get_downstream_addr_group() const {
return group_;
}
DownstreamAddr *NullDownstreamConnection::get_addr() const { return nullptr; }
} // namespace shrpx

View File

@ -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<DownstreamAddrGroup> &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<DownstreamAddrGroup> &
get_downstream_addr_group() const;
virtual DownstreamAddr *get_addr() const;
private:
std::shared_ptr<DownstreamAddrGroup> group_;
};
} // namespace shrpx
#endif // SHRPX_NULL_DOWNSTREAM_CONNECTION_H

View File

@ -82,7 +82,7 @@ using DownstreamKey =
size_t, Proto, uint32_t, uint32_t, size_t, Proto, uint32_t, uint32_t,
uint32_t, bool, bool, bool, bool>>, uint32_t, bool, bool, bool, bool>>,
bool, SessionAffinity, StringRef, StringRef, bool, SessionAffinity, StringRef, StringRef,
SessionAffinityCookieSecure, int64_t, int64_t, StringRef>; SessionAffinityCookieSecure, int64_t, int64_t, StringRef, bool>;
namespace { namespace {
DownstreamKey DownstreamKey
@ -122,6 +122,7 @@ create_downstream_key(const std::shared_ptr<SharedDownstreamAddr> &shared_addr,
std::get<6>(dkey) = timeout.read; std::get<6>(dkey) = timeout.read;
std::get<7>(dkey) = timeout.write; std::get<7>(dkey) = timeout.write;
std::get<8>(dkey) = mruby_file; std::get<8>(dkey) = mruby_file;
std::get<9>(dkey) = shared_addr->dnf;
return dkey; return dkey;
} }
@ -252,6 +253,7 @@ void Worker::replace_downstream_config(
} }
shared_addr->affinity_hash = src.affinity_hash; shared_addr->affinity_hash = src.affinity_hash;
shared_addr->redirect_if_not_tls = src.redirect_if_not_tls; 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.read = src.timeout.read;
shared_addr->timeout.write = src.timeout.write; shared_addr->timeout.write = src.timeout.write;

View File

@ -216,6 +216,8 @@ struct SharedDownstreamAddr {
// true if this group requires that client connection must be TLS, // true if this group requires that client connection must be TLS,
// and the request must be redirected to https URI. // and the request must be redirected to https URI.
bool redirect_if_not_tls; bool redirect_if_not_tls;
// true if a request should not be forwarded to a backend.
bool dnf;
// Timeouts for backend connection. // Timeouts for backend connection.
struct { struct {
ev_tstamp read; ev_tstamp read;