nghttpx: Redirect to HTTPS URI with redirect-if-not-tls param
This commit removes frontend-tls parameter, and adds redirect-if-not-tls parameter parameter to --backend option. nghttpx now responds to the request with 308 status code to redirect the request to https URI if frontend connection is not TLS encrypted, and redirect-if-no-tls parameter is used in --backend option. The port number in Location header field is 443 by default (thus omitted), but it can be configurable using --redirect-https-port option.
This commit is contained in:
parent
1bd6893084
commit
a7c780a732
|
@ -160,6 +160,7 @@ OPTIONS = [
|
||||||
"accesslog-write-early",
|
"accesslog-write-early",
|
||||||
"tls-min-proto-version",
|
"tls-min-proto-version",
|
||||||
"tls-max-proto-version",
|
"tls-max-proto-version",
|
||||||
|
"redirect-https-port",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGVARS = [
|
LOGVARS = [
|
||||||
|
|
|
@ -185,6 +185,8 @@ int main(int argc, char *argv[]) {
|
||||||
!CU_add_test(pSuite, "util_is_hex_string",
|
!CU_add_test(pSuite, "util_is_hex_string",
|
||||||
shrpx::test_util_is_hex_string) ||
|
shrpx::test_util_is_hex_string) ||
|
||||||
!CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) ||
|
!CU_add_test(pSuite, "util_decode_hex", shrpx::test_util_decode_hex) ||
|
||||||
|
!CU_add_test(pSuite, "util_extract_host",
|
||||||
|
shrpx::test_util_extract_host) ||
|
||||||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
|
||||||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
|
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
|
||||||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||
|
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||
|
||||||
|
|
46
src/shrpx.cc
46
src/shrpx.cc
|
@ -1447,6 +1447,7 @@ void fill_default_config(Config *config) {
|
||||||
httpconf.max_request_header_fields = 100;
|
httpconf.max_request_header_fields = 100;
|
||||||
httpconf.response_header_field_buffer = 64_k;
|
httpconf.response_header_field_buffer = 64_k;
|
||||||
httpconf.max_response_header_fields = 500;
|
httpconf.max_response_header_fields = 500;
|
||||||
|
httpconf.redirect_https_port = StringRef::from_lit("443");
|
||||||
|
|
||||||
auto &http2conf = config->http2;
|
auto &http2conf = config->http2;
|
||||||
{
|
{
|
||||||
|
@ -1678,12 +1679,12 @@ Connections:
|
||||||
The parameters are delimited by ";". The available
|
The parameters are delimited by ";". The available
|
||||||
parameters are: "proto=<PROTO>", "tls",
|
parameters are: "proto=<PROTO>", "tls",
|
||||||
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
||||||
"affinity=<METHOD>", "dns", and "frontend-tls". The
|
"affinity=<METHOD>", "dns", and "redirect-if-not-tls".
|
||||||
parameter consists of keyword, and optionally followed
|
The parameter consists of keyword, and optionally
|
||||||
by "=" and value. For example, the parameter "proto=h2"
|
followed by "=" and value. For example, the parameter
|
||||||
consists of the keyword "proto" and value "h2". The
|
"proto=h2" consists of the keyword "proto" and value
|
||||||
parameter "tls" consists of the keyword "tls" without
|
"h2". The parameter "tls" consists of the keyword "tls"
|
||||||
value. Each parameter is described as follows.
|
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
|
||||||
|
@ -1740,16 +1741,18 @@ Connections:
|
||||||
backend host name at start up, or reloading
|
backend host name at start up, or reloading
|
||||||
configuration is skipped.
|
configuration is skipped.
|
||||||
|
|
||||||
If "frontend-tls" parameter is used, the matched backend
|
If "redirect-if-not-tls" parameter is used, the matched
|
||||||
requires frontend TLS connection. In other words, even
|
backend requires that frontend connection is TLS
|
||||||
if pattern is matched, frontend connection is not TLS
|
encrypted. If it isn't, nghttpx responds to the request
|
||||||
protected, the request is forwarded to one of catch-all
|
with 308 status code, and https URI the client should
|
||||||
backends. For this reason, catch-all backend cannot
|
use instead is included in Location header field. The
|
||||||
have "frontend-tls" parameter. If at least one backend
|
port number in redirect URI is 443 by default, and can
|
||||||
has "frontend-tls" parameter, this feature is enabled
|
be changed using --redirect-https-port option. If at
|
||||||
for all backend servers sharing the same <PATTERN>. It
|
least one backend has "redirect-if-not-tls" parameter,
|
||||||
is advised to set "frontend-tls" parameter to all
|
this feature is enabled for all backend servers sharing
|
||||||
backends explicitly if this feature is desired.
|
the same <PATTERN>. It is advised to set
|
||||||
|
"redirect-if-no-tls" parameter to all backends
|
||||||
|
explicitly if this feature is desired.
|
||||||
|
|
||||||
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
|
||||||
|
@ -2545,6 +2548,12 @@ HTTP:
|
||||||
Don't rewrite server header field in default mode. When
|
Don't rewrite server header field in default mode. When
|
||||||
--http2-proxy is used, these headers will not be altered
|
--http2-proxy is used, these headers will not be altered
|
||||||
regardless of this option.
|
regardless of this option.
|
||||||
|
--redirect-https-port=<PORT>
|
||||||
|
Specify the port number which appears in Location header
|
||||||
|
field when redirect to HTTPS URI is made due to
|
||||||
|
"redirect-if-not-tls" parameter in --backend option.
|
||||||
|
Default: )"
|
||||||
|
<< config->http.redirect_https_port << R"(
|
||||||
|
|
||||||
API:
|
API:
|
||||||
--api-max-request-body=<SIZE>
|
--api-max-request-body=<SIZE>
|
||||||
|
@ -3253,6 +3262,7 @@ int main(int argc, char **argv) {
|
||||||
152},
|
152},
|
||||||
{SHRPX_OPT_TLS_MAX_PROTO_VERSION.c_str(), required_argument, &flag,
|
{SHRPX_OPT_TLS_MAX_PROTO_VERSION.c_str(), required_argument, &flag,
|
||||||
153},
|
153},
|
||||||
|
{SHRPX_OPT_REDIRECT_HTTPS_PORT.c_str(), required_argument, &flag, 154},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -3975,6 +3985,10 @@ int main(int argc, char **argv) {
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION,
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION,
|
||||||
StringRef{optarg});
|
StringRef{optarg});
|
||||||
break;
|
break;
|
||||||
|
case 154:
|
||||||
|
// --redirect-https-port
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_REDIRECT_HTTPS_PORT, StringRef{optarg});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -961,7 +961,7 @@ uint32_t next_cycle(const WeightedPri &pri) {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::unique_ptr<DownstreamConnection>
|
std::unique_ptr<DownstreamConnection>
|
||||||
ClientHandler::get_downstream_connection(Downstream *downstream) {
|
ClientHandler::get_downstream_connection(int &err, Downstream *downstream) {
|
||||||
size_t group_idx;
|
size_t group_idx;
|
||||||
auto &downstreamconf = *worker_->get_downstream_config();
|
auto &downstreamconf = *worker_->get_downstream_config();
|
||||||
auto &routerconf = downstreamconf.router;
|
auto &routerconf = downstreamconf.router;
|
||||||
|
@ -971,6 +971,8 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||||
|
|
||||||
const auto &req = downstream->request();
|
const auto &req = downstream->request();
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
switch (faddr_->alt_mode) {
|
switch (faddr_->alt_mode) {
|
||||||
case ALTMODE_API:
|
case ALTMODE_API:
|
||||||
return make_unique<APIDownstreamConnection>(worker_);
|
return make_unique<APIDownstreamConnection>(worker_);
|
||||||
|
@ -1009,11 +1011,13 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||||
CLOG(INFO, this) << "Downstream address group_idx: " << group_idx;
|
CLOG(INFO, this) << "Downstream address group_idx: " << group_idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groups[group_idx]->shared_addr->require_upstream_tls && !conn_.tls.ssl) {
|
if (groups[group_idx]->shared_addr->redirect_if_not_tls && !conn_.tls.ssl) {
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
CLOG(INFO, this) << "Downstream address group " << group_idx
|
CLOG(INFO, this) << "Downstream address group " << group_idx
|
||||||
<< " requires frontend TLS connection. Send request to "
|
<< " requires frontend TLS connection.";
|
||||||
"catch-all group.";
|
}
|
||||||
group_idx = catch_all;
|
err = SHRPX_ERR_TLS_REQUIRED;
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &group = groups[group_idx];
|
auto &group = groups[group_idx];
|
||||||
|
@ -1087,6 +1091,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||||
CLOG(INFO, this) << "No working downstream address found";
|
CLOG(INFO, this) << "No working downstream address found";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = -1;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,6 +1104,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
|
||||||
auto http2session = select_http2_session(group);
|
auto http2session = select_http2_session(group);
|
||||||
|
|
||||||
if (http2session == nullptr) {
|
if (http2session == nullptr) {
|
||||||
|
err = -1;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,8 +99,12 @@ public:
|
||||||
|
|
||||||
void pool_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
void pool_downstream_connection(std::unique_ptr<DownstreamConnection> dconn);
|
||||||
void remove_downstream_connection(DownstreamConnection *dconn);
|
void remove_downstream_connection(DownstreamConnection *dconn);
|
||||||
|
// Returns DownstreamConnection object based on request path. This
|
||||||
|
// function returns non-null DownstreamConnection, and assigns 0 to
|
||||||
|
// |err| if it succeeds, or returns nullptr, and assigns negative
|
||||||
|
// error code to |err|.
|
||||||
std::unique_ptr<DownstreamConnection>
|
std::unique_ptr<DownstreamConnection>
|
||||||
get_downstream_connection(Downstream *downstream);
|
get_downstream_connection(int &err, Downstream *downstream);
|
||||||
MemchunkPool *get_mcpool();
|
MemchunkPool *get_mcpool();
|
||||||
SSL *get_ssl() const;
|
SSL *get_ssl() const;
|
||||||
// Call this function when HTTP/2 connection header is received at
|
// Call this function when HTTP/2 connection header is received at
|
||||||
|
|
|
@ -747,7 +747,7 @@ struct DownstreamParams {
|
||||||
shrpx_session_affinity affinity;
|
shrpx_session_affinity affinity;
|
||||||
bool tls;
|
bool tls;
|
||||||
bool dns;
|
bool dns;
|
||||||
bool frontend_tls;
|
bool redirect_if_not_tls;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -823,8 +823,8 @@ int parse_downstream_params(DownstreamParams &out,
|
||||||
}
|
}
|
||||||
} else if (util::strieq_l("dns", param)) {
|
} else if (util::strieq_l("dns", param)) {
|
||||||
out.dns = true;
|
out.dns = true;
|
||||||
} else if (util::strieq_l("frontend-tls", param)) {
|
} else if (util::strieq_l("redirect-if-not-tls", param)) {
|
||||||
out.frontend_tls = true;
|
out.redirect_if_not_tls = 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;
|
||||||
|
@ -919,8 +919,8 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||||
}
|
}
|
||||||
// If at least one backend requires frontend TLS connection,
|
// If at least one backend requires frontend TLS connection,
|
||||||
// enable it for all backends sharing the same pattern.
|
// enable it for all backends sharing the same pattern.
|
||||||
if (params.frontend_tls) {
|
if (params.redirect_if_not_tls) {
|
||||||
g.require_upstream_tls = true;
|
g.redirect_if_not_tls = true;
|
||||||
}
|
}
|
||||||
g.addrs.push_back(addr);
|
g.addrs.push_back(addr);
|
||||||
done = true;
|
done = true;
|
||||||
|
@ -936,7 +936,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig &addr,
|
||||||
auto &g = addr_groups.back();
|
auto &g = addr_groups.back();
|
||||||
g.addrs.push_back(addr);
|
g.addrs.push_back(addr);
|
||||||
g.affinity = params.affinity;
|
g.affinity = params.affinity;
|
||||||
g.require_upstream_tls = params.frontend_tls;
|
g.redirect_if_not_tls = params.redirect_if_not_tls;
|
||||||
|
|
||||||
if (pattern[0] == '*') {
|
if (pattern[0] == '*') {
|
||||||
// wildcard pattern
|
// wildcard pattern
|
||||||
|
@ -1753,6 +1753,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
|
if (util::strieq_l("redirect-https-por", name, 18)) {
|
||||||
|
return SHRPX_OPTID_REDIRECT_HTTPS_PORT;
|
||||||
|
}
|
||||||
if (util::strieq_l("stream-read-timeou", name, 18)) {
|
if (util::strieq_l("stream-read-timeou", name, 18)) {
|
||||||
return SHRPX_OPTID_STREAM_READ_TIMEOUT;
|
return SHRPX_OPTID_STREAM_READ_TIMEOUT;
|
||||||
}
|
}
|
||||||
|
@ -3356,6 +3359,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg);
|
return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg);
|
||||||
case SHRPX_OPTID_TLS_MAX_PROTO_VERSION:
|
case SHRPX_OPTID_TLS_MAX_PROTO_VERSION:
|
||||||
return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg);
|
return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg);
|
||||||
|
case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
|
||||||
|
auto n = util::parse_uint(optarg);
|
||||||
|
if (n == -1 || n < 0 || n > 65535) {
|
||||||
|
LOG(ERROR) << opt << ": bad value. Specify an integer in the range [0, "
|
||||||
|
"65535], inclusive";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
config->http.redirect_https_port = optarg;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
case SHRPX_OPTID_CONF:
|
case SHRPX_OPTID_CONF:
|
||||||
LOG(WARN) << "conf: ignored";
|
LOG(WARN) << "conf: ignored";
|
||||||
|
|
||||||
|
@ -3663,12 +3676,6 @@ int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr_groups[catch_all_group].require_upstream_tls) {
|
|
||||||
LOG(FATAL)
|
|
||||||
<< "backend: Catch-all backend cannot have frontend-tls parameter";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
downstreamconf.addr_group_catch_all = catch_all_group;
|
downstreamconf.addr_group_catch_all = catch_all_group;
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
|
|
@ -331,6 +331,8 @@ constexpr auto SHRPX_OPT_TLS_MIN_PROTO_VERSION =
|
||||||
StringRef::from_lit("tls-min-proto-version");
|
StringRef::from_lit("tls-min-proto-version");
|
||||||
constexpr auto SHRPX_OPT_TLS_MAX_PROTO_VERSION =
|
constexpr auto SHRPX_OPT_TLS_MAX_PROTO_VERSION =
|
||||||
StringRef::from_lit("tls-max-proto-version");
|
StringRef::from_lit("tls-max-proto-version");
|
||||||
|
constexpr auto SHRPX_OPT_REDIRECT_HTTPS_PORT =
|
||||||
|
StringRef::from_lit("redirect-https-port");
|
||||||
|
|
||||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||||
|
|
||||||
|
@ -434,9 +436,7 @@ struct AffinityHash {
|
||||||
|
|
||||||
struct DownstreamAddrGroupConfig {
|
struct DownstreamAddrGroupConfig {
|
||||||
DownstreamAddrGroupConfig(const StringRef &pattern)
|
DownstreamAddrGroupConfig(const StringRef &pattern)
|
||||||
: pattern(pattern),
|
: pattern(pattern), affinity(AFFINITY_NONE), redirect_if_not_tls(false) {}
|
||||||
affinity(AFFINITY_NONE),
|
|
||||||
require_upstream_tls(false) {}
|
|
||||||
|
|
||||||
StringRef pattern;
|
StringRef pattern;
|
||||||
std::vector<DownstreamAddrConfig> addrs;
|
std::vector<DownstreamAddrConfig> addrs;
|
||||||
|
@ -445,8 +445,9 @@ struct DownstreamAddrGroupConfig {
|
||||||
std::vector<AffinityHash> affinity_hash;
|
std::vector<AffinityHash> affinity_hash;
|
||||||
// Session affinity
|
// Session affinity
|
||||||
shrpx_session_affinity affinity;
|
shrpx_session_affinity affinity;
|
||||||
// true if this group requires that client connection must be TLS.
|
// true if this group requires that client connection must be TLS,
|
||||||
bool require_upstream_tls;
|
// and the request must be redirected to https URI.
|
||||||
|
bool redirect_if_not_tls;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TicketKey {
|
struct TicketKey {
|
||||||
|
@ -639,6 +640,9 @@ struct HttpConfig {
|
||||||
HeaderRefs add_request_headers;
|
HeaderRefs add_request_headers;
|
||||||
HeaderRefs add_response_headers;
|
HeaderRefs add_response_headers;
|
||||||
StringRef server_name;
|
StringRef server_name;
|
||||||
|
// Port number which appears in Location header field when https
|
||||||
|
// redirect is made.
|
||||||
|
StringRef redirect_https_port;
|
||||||
size_t request_header_field_buffer;
|
size_t request_header_field_buffer;
|
||||||
size_t max_request_header_fields;
|
size_t max_request_header_fields;
|
||||||
size_t response_header_field_buffer;
|
size_t response_header_field_buffer;
|
||||||
|
@ -1016,6 +1020,7 @@ enum {
|
||||||
SHRPX_OPTID_PSK_SECRETS,
|
SHRPX_OPTID_PSK_SECRETS,
|
||||||
SHRPX_OPTID_READ_BURST,
|
SHRPX_OPTID_READ_BURST,
|
||||||
SHRPX_OPTID_READ_RATE,
|
SHRPX_OPTID_READ_RATE,
|
||||||
|
SHRPX_OPTID_REDIRECT_HTTPS_PORT,
|
||||||
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
|
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
|
||||||
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
|
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
|
||||||
SHRPX_OPTID_RLIMIT_NOFILE,
|
SHRPX_OPTID_RLIMIT_NOFILE,
|
||||||
|
|
|
@ -38,6 +38,7 @@ enum ErrorCode {
|
||||||
SHRPX_ERR_INPROGRESS = -102,
|
SHRPX_ERR_INPROGRESS = -102,
|
||||||
SHRPX_ERR_DCONN_CANCELED = -103,
|
SHRPX_ERR_DCONN_CANCELED = -103,
|
||||||
SHRPX_ERR_RETRY = -104,
|
SHRPX_ERR_RETRY = -104,
|
||||||
|
SHRPX_ERR_TLS_REQUIRED = -105,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -434,9 +434,25 @@ void Http2Upstream::start_downstream(Downstream *downstream) {
|
||||||
void Http2Upstream::initiate_downstream(Downstream *downstream) {
|
void Http2Upstream::initiate_downstream(Downstream *downstream) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
auto dconn = handler_->get_downstream_connection(downstream);
|
auto dconn = handler_->get_downstream_connection(rv, downstream);
|
||||||
if (!dconn ||
|
if (!dconn) {
|
||||||
(rv = downstream->attach_downstream_connection(std::move(dconn))) != 0) {
|
if (rv == SHRPX_ERR_TLS_REQUIRED) {
|
||||||
|
rv = redirect_to_https(downstream);
|
||||||
|
} else {
|
||||||
|
rv = error_reply(downstream, 503);
|
||||||
|
}
|
||||||
|
if (rv != 0) {
|
||||||
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
downstream_queue_.mark_failure(downstream);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = downstream->attach_downstream_connection(std::move(dconn));
|
||||||
|
if (rv != 0) {
|
||||||
// downstream connection fails, send error page
|
// downstream connection fails, send error page
|
||||||
if (error_reply(downstream, 503) != 0) {
|
if (error_reply(downstream, 503) != 0) {
|
||||||
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
|
@ -1792,6 +1808,52 @@ int Http2Upstream::on_downstream_abort_request(Downstream *downstream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Http2Upstream::on_downstream_abort_request_with_https_redirect(
|
||||||
|
Downstream *downstream) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = redirect_to_https(downstream);
|
||||||
|
if (rv != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler_->signal_write();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Http2Upstream::redirect_to_https(Downstream *downstream) {
|
||||||
|
auto &req = downstream->request();
|
||||||
|
if (req.method == HTTP_CONNECT || req.scheme != "http") {
|
||||||
|
return error_reply(downstream, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto authority = util::extract_host(req.authority);
|
||||||
|
if (authority.empty()) {
|
||||||
|
return error_reply(downstream, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &balloc = downstream->get_block_allocator();
|
||||||
|
auto config = get_config();
|
||||||
|
auto &httpconf = config->http;
|
||||||
|
|
||||||
|
StringRef loc;
|
||||||
|
if (httpconf.redirect_https_port == StringRef::from_lit("443")) {
|
||||||
|
loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
|
||||||
|
req.path);
|
||||||
|
} else {
|
||||||
|
loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
|
||||||
|
StringRef::from_lit(":"),
|
||||||
|
httpconf.redirect_https_port, req.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &resp = downstream->response();
|
||||||
|
resp.http_status = 308;
|
||||||
|
resp.fs.add_header_token(StringRef::from_lit("location"), loc, false,
|
||||||
|
http2::HD_LOCATION);
|
||||||
|
|
||||||
|
return send_reply(downstream, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
int Http2Upstream::consume(int32_t stream_id, size_t len) {
|
int Http2Upstream::consume(int32_t stream_id, size_t len) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
|
@ -1879,6 +1941,8 @@ int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
|
||||||
|
|
||||||
std::unique_ptr<DownstreamConnection> dconn;
|
std::unique_ptr<DownstreamConnection> dconn;
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
if (no_retry || downstream->no_more_retry()) {
|
if (no_retry || downstream->no_more_retry()) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -1886,7 +1950,7 @@ int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
|
||||||
// downstream connection is clean; we can retry with new
|
// downstream connection is clean; we can retry with new
|
||||||
// downstream connection.
|
// downstream connection.
|
||||||
|
|
||||||
dconn = handler_->get_downstream_connection(downstream);
|
dconn = handler_->get_downstream_connection(rv, downstream);
|
||||||
if (!dconn) {
|
if (!dconn) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -1904,7 +1968,12 @@ int Http2Upstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (on_downstream_abort_request(downstream, 503) != 0) {
|
if (rv == SHRPX_ERR_TLS_REQUIRED) {
|
||||||
|
rv = on_downstream_abort_request_with_https_redirect(downstream);
|
||||||
|
} else {
|
||||||
|
rv = on_downstream_abort_request(downstream, 503);
|
||||||
|
}
|
||||||
|
if (rv != 0) {
|
||||||
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
rst_stream(downstream, NGHTTP2_INTERNAL_ERROR);
|
||||||
}
|
}
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
|
|
@ -54,6 +54,8 @@ public:
|
||||||
virtual int on_timeout(Downstream *downstream);
|
virtual int on_timeout(Downstream *downstream);
|
||||||
virtual int on_downstream_abort_request(Downstream *downstream,
|
virtual int on_downstream_abort_request(Downstream *downstream,
|
||||||
unsigned int status_code);
|
unsigned int status_code);
|
||||||
|
virtual int
|
||||||
|
on_downstream_abort_request_with_https_redirect(Downstream *downstream);
|
||||||
virtual ClientHandler *get_client_handler() const;
|
virtual ClientHandler *get_client_handler() const;
|
||||||
|
|
||||||
virtual int downstream_read(DownstreamConnection *dconn);
|
virtual int downstream_read(DownstreamConnection *dconn);
|
||||||
|
@ -120,6 +122,8 @@ public:
|
||||||
|
|
||||||
size_t get_max_buffer_size() const;
|
size_t get_max_buffer_size() const;
|
||||||
|
|
||||||
|
int redirect_to_https(Downstream *downstream);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DefaultMemchunks wb_;
|
DefaultMemchunks wb_;
|
||||||
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
std::unique_ptr<HttpsUpstream> pre_upstream_;
|
||||||
|
|
|
@ -89,7 +89,8 @@ void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
|
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
|
||||||
auto ndconn = handler->get_downstream_connection(downstream);
|
int rv;
|
||||||
|
auto ndconn = handler->get_downstream_connection(rv, downstream);
|
||||||
if (ndconn) {
|
if (ndconn) {
|
||||||
if (downstream->attach_downstream_connection(std::move(ndconn)) == 0) {
|
if (downstream->attach_downstream_connection(std::move(ndconn)) == 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -98,7 +99,13 @@ void connect_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
if (upstream->on_downstream_abort_request(downstream, 504) != 0) {
|
if (rv == SHRPX_ERR_TLS_REQUIRED) {
|
||||||
|
rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
|
||||||
|
} else {
|
||||||
|
rv = upstream->on_downstream_abort_request(downstream, 504);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
delete handler;
|
delete handler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +139,8 @@ void backend_retry(Downstream *downstream) {
|
||||||
|
|
||||||
downstream->pop_downstream_connection();
|
downstream->pop_downstream_connection();
|
||||||
|
|
||||||
auto ndconn = handler->get_downstream_connection(downstream);
|
int rv;
|
||||||
|
auto ndconn = handler->get_downstream_connection(rv, downstream);
|
||||||
if (ndconn) {
|
if (ndconn) {
|
||||||
if (downstream->attach_downstream_connection(std::move(ndconn)) == 0) {
|
if (downstream->attach_downstream_connection(std::move(ndconn)) == 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -141,7 +149,13 @@ void backend_retry(Downstream *downstream) {
|
||||||
|
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
if (upstream->on_downstream_abort_request(downstream, 503) != 0) {
|
if (rv == SHRPX_ERR_TLS_REQUIRED) {
|
||||||
|
rv = upstream->on_downstream_abort_request_with_https_redirect(downstream);
|
||||||
|
} else {
|
||||||
|
rv = upstream->on_downstream_abort_request(downstream, 503);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rv != 0) {
|
||||||
delete handler;
|
delete handler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -421,10 +421,18 @@ int htp_hdrs_completecb(http_parser *htp) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto dconn = handler->get_downstream_connection(downstream);
|
auto dconn = handler->get_downstream_connection(rv, downstream);
|
||||||
|
|
||||||
if (!dconn ||
|
if (!dconn) {
|
||||||
(rv = downstream->attach_downstream_connection(std::move(dconn))) != 0) {
|
if (rv == SHRPX_ERR_TLS_REQUIRED) {
|
||||||
|
upstream->redirect_to_https(downstream);
|
||||||
|
}
|
||||||
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downstream->attach_downstream_connection(std::move(dconn)) != 0) {
|
||||||
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
downstream->set_request_state(Downstream::CONNECT_FAIL);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1225,6 +1233,52 @@ int HttpsUpstream::on_downstream_abort_request(Downstream *downstream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int HttpsUpstream::on_downstream_abort_request_with_https_redirect(
|
||||||
|
Downstream *downstream) {
|
||||||
|
redirect_to_https(downstream);
|
||||||
|
handler_->signal_write_no_wait();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpsUpstream::redirect_to_https(Downstream *downstream) {
|
||||||
|
auto &req = downstream->request();
|
||||||
|
if (req.method == HTTP_CONNECT || req.scheme != "http" ||
|
||||||
|
req.authority.empty()) {
|
||||||
|
error_reply(400);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto authority = util::extract_host(req.authority);
|
||||||
|
if (authority.empty()) {
|
||||||
|
error_reply(400);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &balloc = downstream->get_block_allocator();
|
||||||
|
auto config = get_config();
|
||||||
|
auto &httpconf = config->http;
|
||||||
|
|
||||||
|
StringRef loc;
|
||||||
|
if (httpconf.redirect_https_port == StringRef::from_lit("443")) {
|
||||||
|
loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
|
||||||
|
req.path);
|
||||||
|
} else {
|
||||||
|
loc = concat_string_ref(balloc, StringRef::from_lit("https://"), authority,
|
||||||
|
StringRef::from_lit(":"),
|
||||||
|
httpconf.redirect_https_port, req.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &resp = downstream->response();
|
||||||
|
resp.http_status = 308;
|
||||||
|
resp.fs.add_header_token(StringRef::from_lit("location"), loc, false,
|
||||||
|
http2::HD_LOCATION);
|
||||||
|
resp.fs.add_header_token(StringRef::from_lit("connection"),
|
||||||
|
StringRef::from_lit("close"), false,
|
||||||
|
http2::HD_CONNECTION);
|
||||||
|
|
||||||
|
return send_reply(downstream, nullptr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void HttpsUpstream::log_response_headers(DefaultMemchunks *buf) const {
|
void HttpsUpstream::log_response_headers(DefaultMemchunks *buf) const {
|
||||||
std::string nhdrs;
|
std::string nhdrs;
|
||||||
for (auto chunk = buf->head; chunk; chunk = chunk->next) {
|
for (auto chunk = buf->head; chunk; chunk = chunk->next) {
|
||||||
|
@ -1267,11 +1321,13 @@ int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
|
||||||
|
|
||||||
downstream_->add_retry();
|
downstream_->add_retry();
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
if (no_retry || downstream_->no_more_retry()) {
|
if (no_retry || downstream_->no_more_retry()) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
dconn = handler_->get_downstream_connection(downstream_.get());
|
dconn = handler_->get_downstream_connection(rv, downstream_.get());
|
||||||
if (!dconn) {
|
if (!dconn) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -1289,7 +1345,12 @@ int HttpsUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if (on_downstream_abort_request(downstream_.get(), 503) != 0) {
|
if (rv == SHRPX_ERR_TLS_REQUIRED) {
|
||||||
|
rv = on_downstream_abort_request_with_https_redirect(downstream);
|
||||||
|
} else {
|
||||||
|
rv = on_downstream_abort_request(downstream_.get(), 503);
|
||||||
|
}
|
||||||
|
if (rv != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
downstream_->pop_downstream_connection();
|
downstream_->pop_downstream_connection();
|
||||||
|
|
|
@ -50,6 +50,8 @@ public:
|
||||||
virtual int on_event();
|
virtual int on_event();
|
||||||
virtual int on_downstream_abort_request(Downstream *downstream,
|
virtual int on_downstream_abort_request(Downstream *downstream,
|
||||||
unsigned int status_code);
|
unsigned int status_code);
|
||||||
|
virtual int
|
||||||
|
on_downstream_abort_request_with_https_redirect(Downstream *downstream);
|
||||||
virtual ClientHandler *get_client_handler() const;
|
virtual ClientHandler *get_client_handler() const;
|
||||||
|
|
||||||
virtual int downstream_read(DownstreamConnection *dconn);
|
virtual int downstream_read(DownstreamConnection *dconn);
|
||||||
|
@ -91,6 +93,7 @@ public:
|
||||||
|
|
||||||
void reset_current_header_length();
|
void reset_current_header_length();
|
||||||
void log_response_headers(DefaultMemchunks *buf) const;
|
void log_response_headers(DefaultMemchunks *buf) const;
|
||||||
|
int redirect_to_https(Downstream *downstream);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ClientHandler *handler_;
|
ClientHandler *handler_;
|
||||||
|
|
|
@ -370,7 +370,7 @@ void SpdyUpstream::start_downstream(Downstream *downstream) {
|
||||||
void SpdyUpstream::initiate_downstream(Downstream *downstream) {
|
void SpdyUpstream::initiate_downstream(Downstream *downstream) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
auto dconn = handler_->get_downstream_connection(downstream);
|
auto dconn = handler_->get_downstream_connection(rv, downstream);
|
||||||
|
|
||||||
if (!dconn ||
|
if (!dconn ||
|
||||||
(rv = downstream->attach_downstream_connection(std::move(dconn))) != 0) {
|
(rv = downstream->attach_downstream_connection(std::move(dconn))) != 0) {
|
||||||
|
@ -1265,6 +1265,13 @@ int SpdyUpstream::on_downstream_abort_request(Downstream *downstream,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SpdyUpstream::on_downstream_abort_request_with_https_redirect(
|
||||||
|
Downstream *downstream) {
|
||||||
|
// This should not be called since SPDY is only available with TLS.
|
||||||
|
assert(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int SpdyUpstream::consume(int32_t stream_id, size_t len) {
|
int SpdyUpstream::consume(int32_t stream_id, size_t len) {
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
|
@ -1345,7 +1352,7 @@ int SpdyUpstream::on_downstream_reset(Downstream *downstream, bool no_retry) {
|
||||||
// downstream connection is clean; we can retry with new
|
// downstream connection is clean; we can retry with new
|
||||||
// downstream connection.
|
// downstream connection.
|
||||||
|
|
||||||
dconn = handler_->get_downstream_connection(downstream);
|
dconn = handler_->get_downstream_connection(rv, downstream);
|
||||||
if (!dconn) {
|
if (!dconn) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ public:
|
||||||
virtual int on_timeout(Downstream *downstream);
|
virtual int on_timeout(Downstream *downstream);
|
||||||
virtual int on_downstream_abort_request(Downstream *downstream,
|
virtual int on_downstream_abort_request(Downstream *downstream,
|
||||||
unsigned int status_code);
|
unsigned int status_code);
|
||||||
|
virtual int
|
||||||
|
on_downstream_abort_request_with_https_redirect(Downstream *downstream);
|
||||||
virtual ClientHandler *get_client_handler() const;
|
virtual ClientHandler *get_client_handler() const;
|
||||||
virtual int downstream_read(DownstreamConnection *dconn);
|
virtual int downstream_read(DownstreamConnection *dconn);
|
||||||
virtual int downstream_write(DownstreamConnection *dconn);
|
virtual int downstream_write(DownstreamConnection *dconn);
|
||||||
|
|
|
@ -45,6 +45,10 @@ public:
|
||||||
virtual int on_timeout(Downstream *downstream) { return 0; };
|
virtual int on_timeout(Downstream *downstream) { return 0; };
|
||||||
virtual int on_downstream_abort_request(Downstream *downstream,
|
virtual int on_downstream_abort_request(Downstream *downstream,
|
||||||
unsigned int status_code) = 0;
|
unsigned int status_code) = 0;
|
||||||
|
// Called when the current request is aborted without forwarding it
|
||||||
|
// to backend, and it should be redirected to https URI.
|
||||||
|
virtual int
|
||||||
|
on_downstream_abort_request_with_https_redirect(Downstream *downstream) = 0;
|
||||||
virtual int downstream_read(DownstreamConnection *dconn) = 0;
|
virtual int downstream_read(DownstreamConnection *dconn) = 0;
|
||||||
virtual int downstream_write(DownstreamConnection *dconn) = 0;
|
virtual int downstream_write(DownstreamConnection *dconn) = 0;
|
||||||
virtual int downstream_eof(DownstreamConnection *dconn) = 0;
|
virtual int downstream_eof(DownstreamConnection *dconn) = 0;
|
||||||
|
|
|
@ -77,7 +77,7 @@ bool match_shared_downstream_addr(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lhs->affinity != rhs->affinity ||
|
if (lhs->affinity != rhs->affinity ||
|
||||||
lhs->require_upstream_tls != rhs->require_upstream_tls) {
|
lhs->redirect_if_not_tls != rhs->redirect_if_not_tls) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@ void Worker::replace_downstream_config(
|
||||||
shared_addr->addrs.resize(src.addrs.size());
|
shared_addr->addrs.resize(src.addrs.size());
|
||||||
shared_addr->affinity = src.affinity;
|
shared_addr->affinity = src.affinity;
|
||||||
shared_addr->affinity_hash = src.affinity_hash;
|
shared_addr->affinity_hash = src.affinity_hash;
|
||||||
shared_addr->require_upstream_tls = src.require_upstream_tls;
|
shared_addr->redirect_if_not_tls = src.redirect_if_not_tls;
|
||||||
|
|
||||||
size_t num_http1 = 0;
|
size_t num_http1 = 0;
|
||||||
size_t num_http2 = 0;
|
size_t num_http2 = 0;
|
||||||
|
|
|
@ -137,7 +137,7 @@ struct SharedDownstreamAddr {
|
||||||
http1_pri{},
|
http1_pri{},
|
||||||
http2_pri{},
|
http2_pri{},
|
||||||
affinity{AFFINITY_NONE},
|
affinity{AFFINITY_NONE},
|
||||||
require_upstream_tls{false} {}
|
redirect_if_not_tls{false} {}
|
||||||
|
|
||||||
SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
|
SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
|
||||||
SharedDownstreamAddr(SharedDownstreamAddr &&) = delete;
|
SharedDownstreamAddr(SharedDownstreamAddr &&) = delete;
|
||||||
|
@ -172,8 +172,9 @@ struct SharedDownstreamAddr {
|
||||||
WeightedPri http2_pri;
|
WeightedPri http2_pri;
|
||||||
// Session affinity
|
// Session affinity
|
||||||
shrpx_session_affinity affinity;
|
shrpx_session_affinity affinity;
|
||||||
// true if this group requires that client connection must be TLS.
|
// true if this group requires that client connection must be TLS,
|
||||||
bool require_upstream_tls;
|
// and the request must be redirected to https URI.
|
||||||
|
bool redirect_if_not_tls;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DownstreamAddrGroup {
|
struct DownstreamAddrGroup {
|
||||||
|
|
20
src/util.cc
20
src/util.cc
|
@ -1432,6 +1432,26 @@ StringRef decode_hex(BlockAllocator &balloc, const StringRef &s) {
|
||||||
return StringRef{iov.base, p};
|
return StringRef{iov.base, p};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StringRef extract_host(const StringRef &hostport) {
|
||||||
|
if (hostport[0] == '[') {
|
||||||
|
// assume this is IPv6 numeric address
|
||||||
|
auto p = std::find(std::begin(hostport), std::end(hostport), ']');
|
||||||
|
if (p == std::end(hostport)) {
|
||||||
|
return StringRef{};
|
||||||
|
}
|
||||||
|
if (p + 1 < std::end(hostport) && *(p + 1) != ':') {
|
||||||
|
return StringRef{};
|
||||||
|
}
|
||||||
|
return StringRef{std::begin(hostport), p + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto p = std::find(std::begin(hostport), std::end(hostport), ':');
|
||||||
|
if (p == std::begin(hostport)) {
|
||||||
|
return StringRef{};
|
||||||
|
}
|
||||||
|
return StringRef{std::begin(hostport), p};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -739,6 +739,11 @@ uint32_t hash32(const StringRef &s);
|
||||||
// returns 0 if it succeeds, or -1.
|
// returns 0 if it succeeds, or -1.
|
||||||
int sha256(uint8_t *buf, const StringRef &s);
|
int sha256(uint8_t *buf, const StringRef &s);
|
||||||
|
|
||||||
|
// Returns host from |hostport|. If host cannot be found in
|
||||||
|
// |hostport|, returns empty string. The returned string might not be
|
||||||
|
// NULL-terminated.
|
||||||
|
StringRef extract_host(const StringRef &hostport);
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -609,4 +609,22 @@ void test_util_decode_hex(void) {
|
||||||
CU_ASSERT("" == util::decode_hex(balloc, StringRef{}));
|
CU_ASSERT("" == util::decode_hex(balloc, StringRef{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_util_extract_host(void) {
|
||||||
|
CU_ASSERT(StringRef::from_lit("foo") ==
|
||||||
|
util::extract_host(StringRef::from_lit("foo")));
|
||||||
|
CU_ASSERT(StringRef::from_lit("foo") ==
|
||||||
|
util::extract_host(StringRef::from_lit("foo:")));
|
||||||
|
CU_ASSERT(StringRef::from_lit("foo") ==
|
||||||
|
util::extract_host(StringRef::from_lit("foo:0")));
|
||||||
|
CU_ASSERT(StringRef::from_lit("[::1]") ==
|
||||||
|
util::extract_host(StringRef::from_lit("[::1]")));
|
||||||
|
CU_ASSERT(StringRef::from_lit("[::1]") ==
|
||||||
|
util::extract_host(StringRef::from_lit("[::1]:")));
|
||||||
|
|
||||||
|
CU_ASSERT(util::extract_host(StringRef::from_lit(":foo")).empty());
|
||||||
|
CU_ASSERT(util::extract_host(StringRef::from_lit("[::1")).empty());
|
||||||
|
CU_ASSERT(util::extract_host(StringRef::from_lit("[::1]0")).empty());
|
||||||
|
CU_ASSERT(util::extract_host(StringRef{}).empty());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -66,6 +66,7 @@ void test_util_random_alpha_digit(void);
|
||||||
void test_util_format_hex(void);
|
void test_util_format_hex(void);
|
||||||
void test_util_is_hex_string(void);
|
void test_util_is_hex_string(void);
|
||||||
void test_util_decode_hex(void);
|
void test_util_decode_hex(void);
|
||||||
|
void test_util_extract_host(void);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue