nghttpx: Add upgrade-scheme parameter to backend option

If "upgrade-scheme" parameter is present in backend option along with
"tls" paramter, HTTP/2 :scheme pseudo header field is changed to
"https" from "http" when forwarding a request to this particular
backend.  This is a workaround for a server which requests "https"
scheme on HTTP/2 connection encrypted by TLS.
This commit is contained in:
Tatsuhiro Tsujikawa 2018-01-08 18:08:01 +09:00
parent 652f57e79d
commit 5cc3d159e1
6 changed files with 33 additions and 5 deletions

View File

@ -1815,6 +1815,13 @@ Connections:
"redirect-if-no-tls" parameter to all backends "redirect-if-no-tls" parameter to all backends
explicitly if this feature is desired. explicitly if this feature is desired.
If "upgrade-scheme" parameter is used along with "tls"
parameter, HTTP/2 :scheme pseudo header field is changed
to "https" from "http" when forwarding a request to this
particular backend. This is a workaround for a backend
server which requires "https" :scheme pseudo header
field on TLS encrypted connection.
Since ";" and ":" are used as delimiter, <PATTERN> must Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted. meaning in shell, the option value must be quoted.

View File

@ -814,6 +814,7 @@ struct DownstreamParams {
bool tls; bool tls;
bool dns; bool dns;
bool redirect_if_not_tls; bool redirect_if_not_tls;
bool upgrade_scheme;
}; };
namespace { namespace {
@ -918,6 +919,8 @@ int parse_downstream_params(DownstreamParams &out,
out.dns = true; out.dns = true;
} else if (util::strieq_l("redirect-if-not-tls", param)) { } else if (util::strieq_l("redirect-if-not-tls", param)) {
out.redirect_if_not_tls = true; out.redirect_if_not_tls = true;
} else if (util::strieq_l("upgrade-scheme", param)) {
out.upgrade_scheme = 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;
@ -977,6 +980,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
addr.tls = params.tls; addr.tls = params.tls;
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;
auto &routerconf = downstreamconf.router; auto &routerconf = downstreamconf.router;
auto &router = routerconf.router; auto &router = routerconf.router;

View File

@ -461,6 +461,10 @@ struct DownstreamAddrConfig {
bool tls; bool tls;
// true if dynamic DNS is enabled // true if dynamic DNS is enabled
bool dns; bool dns;
// true if :scheme pseudo header field should be upgraded to secure
// variant (e.g., "https") when forwarding request to a backend
// connected by TLS connection.
bool upgrade_scheme;
}; };
// Mapping hash to idx which is an index into // Mapping hash to idx which is an index into

View File

@ -291,7 +291,14 @@ int Http2DownstreamConnection::push_request_headers() {
if (req.method != HTTP_CONNECT) { if (req.method != HTTP_CONNECT) {
assert(!req.scheme.empty()); assert(!req.scheme.empty());
auto addr = http2session_->get_addr();
assert(addr);
// We will handle more protocol scheme upgrade in the future.
if (addr->tls && addr->upgrade_scheme && req.scheme == "http") {
nva.push_back(http2::make_nv_ll(":scheme", "https"));
} else {
nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme)); nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme));
}
if (req.method == HTTP_OPTIONS && req.path.empty()) { if (req.method == HTTP_OPTIONS && req.path.empty()) {
nva.push_back(http2::make_nv_ll(":path", "*")); nva.push_back(http2::make_nv_ll(":path", "*"));

View File

@ -70,9 +70,9 @@ void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) {
// DownstreamKey is used to index SharedDownstreamAddr in order to // DownstreamKey is used to index SharedDownstreamAddr in order to
// find the same configuration. // find the same configuration.
using DownstreamKey = using DownstreamKey = std::tuple<
std::tuple<std::vector<std::tuple<StringRef, StringRef, size_t, size_t, std::vector<std::tuple<StringRef, StringRef, size_t, size_t, shrpx_proto,
shrpx_proto, uint16_t, bool, bool, bool>>, uint16_t, bool, bool, bool, bool>>,
bool, int, StringRef, StringRef, int>; bool, int, StringRef, StringRef, int>;
namespace { namespace {
@ -93,6 +93,7 @@ DownstreamKey create_downstream_key(
std::get<6>(*p) = a.host_unix; std::get<6>(*p) = a.host_unix;
std::get<7>(*p) = a.tls; std::get<7>(*p) = a.tls;
std::get<8>(*p) = a.dns; std::get<8>(*p) = a.dns;
std::get<9>(*p) = a.upgrade_scheme;
++p; ++p;
} }
std::sort(std::begin(addrs), std::end(addrs)); std::sort(std::begin(addrs), std::end(addrs));
@ -220,6 +221,7 @@ void Worker::replace_downstream_config(
dst_addr.fall = src_addr.fall; dst_addr.fall = src_addr.fall;
dst_addr.rise = src_addr.rise; dst_addr.rise = src_addr.rise;
dst_addr.dns = src_addr.dns; dst_addr.dns = src_addr.dns;
dst_addr.upgrade_scheme = src_addr.upgrade_scheme;
auto shared_addr_ptr = shared_addr.get(); auto shared_addr_ptr = shared_addr.get();

View File

@ -115,6 +115,10 @@ struct DownstreamAddr {
bool tls; bool tls;
// true if dynamic DNS is enabled // true if dynamic DNS is enabled
bool dns; bool dns;
// true if :scheme pseudo header field should be upgraded to secure
// variant (e.g., "https") when forwarding request to a backend
// connected by TLS connection.
bool upgrade_scheme;
}; };
// Simplified weighted fair queuing. Actually we don't use queue here // Simplified weighted fair queuing. Actually we don't use queue here