From 5cc3d159e1a5bdb8f54121571a7dc30174723535 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 8 Jan 2018 18:08:01 +0900 Subject: [PATCH] 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. --- src/shrpx.cc | 7 +++++++ src/shrpx_config.cc | 4 ++++ src/shrpx_config.h | 4 ++++ src/shrpx_http2_downstream_connection.cc | 9 ++++++++- src/shrpx_worker.cc | 10 ++++++---- src/shrpx_worker.h | 4 ++++ 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index e07c3e1d..95e1dda2 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1815,6 +1815,13 @@ Connections: "redirect-if-no-tls" parameter to all backends 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, must not contain these characters. Since ";" has special meaning in shell, the option value must be quoted. diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index fb1e75e6..21a7e09d 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -814,6 +814,7 @@ struct DownstreamParams { bool tls; bool dns; bool redirect_if_not_tls; + bool upgrade_scheme; }; namespace { @@ -918,6 +919,8 @@ int parse_downstream_params(DownstreamParams &out, out.dns = true; } else if (util::strieq_l("redirect-if-not-tls", param)) { out.redirect_if_not_tls = true; + } else if (util::strieq_l("upgrade-scheme", param)) { + out.upgrade_scheme = true; } else if (!param.empty()) { LOG(ERROR) << "backend: " << param << ": unknown keyword"; return -1; @@ -977,6 +980,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr, addr.tls = params.tls; addr.sni = make_string_ref(downstreamconf.balloc, params.sni); addr.dns = params.dns; + addr.upgrade_scheme = params.upgrade_scheme; auto &routerconf = downstreamconf.router; auto &router = routerconf.router; diff --git a/src/shrpx_config.h b/src/shrpx_config.h index c6527e48..96bf8c8e 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -461,6 +461,10 @@ struct DownstreamAddrConfig { bool tls; // true if dynamic DNS is enabled 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 diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index 9c10f5aa..70a4a055 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -291,7 +291,14 @@ int Http2DownstreamConnection::push_request_headers() { if (req.method != HTTP_CONNECT) { assert(!req.scheme.empty()); - nva.push_back(http2::make_nv_ls_nocopy(":scheme", req.scheme)); + 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)); + } if (req.method == HTTP_OPTIONS && req.path.empty()) { nva.push_back(http2::make_nv_ll(":path", "*")); diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 1865e441..58019431 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -70,10 +70,10 @@ void proc_wev_cb(struct ev_loop *loop, ev_timer *w, int revents) { // DownstreamKey is used to index SharedDownstreamAddr in order to // find the same configuration. -using DownstreamKey = - std::tuple>, - bool, int, StringRef, StringRef, int>; +using DownstreamKey = std::tuple< + std::vector>, + bool, int, StringRef, StringRef, int>; namespace { DownstreamKey create_downstream_key( @@ -93,6 +93,7 @@ DownstreamKey create_downstream_key( std::get<6>(*p) = a.host_unix; std::get<7>(*p) = a.tls; std::get<8>(*p) = a.dns; + std::get<9>(*p) = a.upgrade_scheme; ++p; } 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.rise = src_addr.rise; dst_addr.dns = src_addr.dns; + dst_addr.upgrade_scheme = src_addr.upgrade_scheme; auto shared_addr_ptr = shared_addr.get(); diff --git a/src/shrpx_worker.h b/src/shrpx_worker.h index c53376db..a7a7773e 100644 --- a/src/shrpx_worker.h +++ b/src/shrpx_worker.h @@ -115,6 +115,10 @@ struct DownstreamAddr { bool tls; // true if dynamic DNS is enabled 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