nghttpx: Add --require-http-scheme option
This commit is contained in:
parent
a67822b382
commit
79524471b4
|
@ -198,6 +198,7 @@ OPTIONS = [
|
|||
"max-worker-processes",
|
||||
"worker-process-grace-shutdown-period",
|
||||
"frontend-quic-initial-rtt",
|
||||
"require-http-scheme",
|
||||
]
|
||||
|
||||
LOGVARS = [
|
||||
|
|
|
@ -2870,3 +2870,88 @@ func TestH2H1ChunkedEndsPrematurely(t *testing.T) {
|
|||
t.Errorf("res.errCode = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequireHTTPSchemeHTTPSWithoutEncryption verifies that https
|
||||
// scheme in non-encrypted connection is treated as error.
|
||||
func TestH2H1RequireHTTPSchemeHTTPSWithoutEncryption(t *testing.T) {
|
||||
st := newServerTester([]string{"--require-http-scheme"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequireHTTPSchemeHTTPSWithoutEncryption",
|
||||
scheme: "https",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 400; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequireHTTPSchemeHTTPWithEncryption verifies that http
|
||||
// scheme in encrypted connection is treated as error.
|
||||
func TestH2H1RequireHTTPSchemeHTTPWithEncryption(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--require-http-scheme"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequireHTTPSchemeHTTPWithEncryption",
|
||||
scheme: "http",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 400; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequireHTTPSchemeUnknownSchemeWithoutEncryption verifies
|
||||
// that unknown scheme in non-encrypted connection is treated as
|
||||
// error.
|
||||
func TestH2H1RequireHTTPSchemeUnknownSchemeWithoutEncryption(t *testing.T) {
|
||||
st := newServerTester([]string{"--require-http-scheme"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequireHTTPSchemeUnknownSchemeWithoutEncryption",
|
||||
scheme: "unknown",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 400; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestH2H1RequireHTTPSchemeUnknownSchemeWithEncryption verifies that
|
||||
// unknown scheme in encrypted connection is treated as error.
|
||||
func TestH2H1RequireHTTPSchemeUnknownSchemeWithEncryption(t *testing.T) {
|
||||
st := newServerTesterTLS([]string{"--require-http-scheme"}, t, func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Errorf("server should not forward this request")
|
||||
})
|
||||
defer st.Close()
|
||||
|
||||
res, err := st.http2(requestParam{
|
||||
name: "TestH2H1RequireHTTPSchemeUnknownSchemeWithEncryption",
|
||||
scheme: "unknown",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Error st.http2() = %v", err)
|
||||
}
|
||||
|
||||
if got, want := res.status, 400; got != want {
|
||||
t.Errorf("status = %v; want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,8 @@ int main(int argc, char *argv[]) {
|
|||
shrpx::test_shrpx_http_create_affinity_cookie) ||
|
||||
!CU_add_test(pSuite, "http_create_atlsvc_header_field_value",
|
||||
shrpx::test_shrpx_http_create_altsvc_header_value) ||
|
||||
!CU_add_test(pSuite, "http_check_http_scheme",
|
||||
shrpx::test_shrpx_http_check_http_scheme) ||
|
||||
!CU_add_test(pSuite, "router_match", shrpx::test_shrpx_router_match) ||
|
||||
!CU_add_test(pSuite, "router_match_wildcard",
|
||||
shrpx::test_shrpx_router_match_wildcard) ||
|
||||
|
|
13
src/shrpx.cc
13
src/shrpx.cc
|
@ -3237,6 +3237,13 @@ HTTP:
|
|||
"redirect-if-not-tls" parameter in --backend option.
|
||||
Default: )"
|
||||
<< config->http.redirect_https_port << R"(
|
||||
--require-http-scheme
|
||||
Always require http or https scheme in HTTP request. It
|
||||
also requires that https scheme must be used for an
|
||||
encrypted connection. Otherwise, http scheme must be
|
||||
used. This option is recommended for a server
|
||||
deployment which directly faces clients and the services
|
||||
it provides only require http or https scheme.
|
||||
|
||||
API:
|
||||
--api-max-request-body=<SIZE>
|
||||
|
@ -4238,6 +4245,7 @@ int main(int argc, char **argv) {
|
|||
required_argument, &flag, 189},
|
||||
{SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT.c_str(), required_argument, &flag,
|
||||
190},
|
||||
{SHRPX_OPT_REQUIRE_HTTP_SCHEME.c_str(), no_argument, &flag, 191},
|
||||
{nullptr, 0, nullptr, 0}};
|
||||
|
||||
int option_index = 0;
|
||||
|
@ -5142,6 +5150,11 @@ int main(int argc, char **argv) {
|
|||
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT,
|
||||
StringRef{optarg});
|
||||
break;
|
||||
case 191:
|
||||
// --require-http-scheme
|
||||
cmdcfgs.emplace_back(SHRPX_OPT_REQUIRE_HTTP_SCHEME,
|
||||
StringRef::from_lit("yes"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2210,6 +2210,9 @@ int option_lookup_token(const char *name, size_t namelen) {
|
|||
if (util::strieq_l("no-location-rewrit", name, 18)) {
|
||||
return SHRPX_OPTID_NO_LOCATION_REWRITE;
|
||||
}
|
||||
if (util::strieq_l("require-http-schem", name, 18)) {
|
||||
return SHRPX_OPTID_REQUIRE_HTTP_SCHEME;
|
||||
}
|
||||
if (util::strieq_l("tls-ticket-key-fil", name, 18)) {
|
||||
return SHRPX_OPTID_TLS_TICKET_KEY_FILE;
|
||||
}
|
||||
|
@ -4166,6 +4169,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
|||
|
||||
return 0;
|
||||
}
|
||||
case SHRPX_OPTID_REQUIRE_HTTP_SCHEME:
|
||||
config->http.require_http_scheme = util::strieq_l("yes", optarg);
|
||||
return 0;
|
||||
case SHRPX_OPTID_CONF:
|
||||
LOG(WARN) << "conf: ignored";
|
||||
|
||||
|
|
|
@ -401,6 +401,8 @@ constexpr auto SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD =
|
|||
StringRef::from_lit("worker-process-grace-shutdown-period");
|
||||
constexpr auto SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT =
|
||||
StringRef::from_lit("frontend-quic-initial-rtt");
|
||||
constexpr auto SHRPX_OPT_REQUIRE_HTTP_SCHEME =
|
||||
StringRef::from_lit("require-http-scheme");
|
||||
|
||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||
|
||||
|
@ -857,6 +859,7 @@ struct HttpConfig {
|
|||
bool no_location_rewrite;
|
||||
bool no_host_rewrite;
|
||||
bool no_server_rewrite;
|
||||
bool require_http_scheme;
|
||||
};
|
||||
|
||||
struct Http2Config {
|
||||
|
@ -1295,6 +1298,7 @@ enum {
|
|||
SHRPX_OPTID_READ_RATE,
|
||||
SHRPX_OPTID_REDIRECT_HTTPS_PORT,
|
||||
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_REQUIRE_HTTP_SCHEME,
|
||||
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
|
||||
SHRPX_OPTID_RLIMIT_MEMLOCK,
|
||||
SHRPX_OPTID_RLIMIT_NOFILE,
|
||||
|
|
|
@ -271,6 +271,10 @@ StringRef create_altsvc_header_value(BlockAllocator &balloc,
|
|||
return StringRef{iov.base, p};
|
||||
}
|
||||
|
||||
bool check_http_scheme(const StringRef &scheme, bool encrypted) {
|
||||
return encrypted ? scheme == "https" : scheme == "http";
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -83,6 +83,12 @@ bool require_cookie_secure_attribute(SessionAffinityCookieSecure secure,
|
|||
StringRef create_altsvc_header_value(BlockAllocator &balloc,
|
||||
const std::vector<AltSvc> &altsvcs);
|
||||
|
||||
// Returns true if either of the following conditions holds:
|
||||
// - scheme is https and encrypted is true
|
||||
// - scheme is http and encrypted is false
|
||||
// Otherwise returns false.
|
||||
bool check_http_scheme(const StringRef &scheme, bool encrypted);
|
||||
|
||||
} // namespace http
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -419,10 +419,16 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
|
|||
|
||||
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
|
||||
|
||||
if (config->http.require_http_scheme &&
|
||||
!http::check_http_scheme(req.scheme, handler_->get_ssl() != nullptr)) {
|
||||
if (error_reply(downstream, 400) != 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
auto upstream = downstream->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
auto worker = handler->get_worker();
|
||||
auto worker = handler_->get_worker();
|
||||
auto mruby_ctx = worker->get_mruby_context();
|
||||
|
||||
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
||||
|
|
|
@ -2325,10 +2325,15 @@ int Http3Upstream::http_end_request_headers(Downstream *downstream, int fin) {
|
|||
|
||||
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
|
||||
|
||||
if (config->http.require_http_scheme &&
|
||||
!http::check_http_scheme(req.scheme, /* encrypted = */true) {
|
||||
if (error_reply(downstream, 400) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
auto upstream = downstream->get_upstream();
|
||||
auto handler = upstream->get_client_handler();
|
||||
auto worker = handler->get_worker();
|
||||
auto worker = handler_->get_worker();
|
||||
auto mruby_ctx = worker->get_mruby_context();
|
||||
|
||||
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
||||
|
|
|
@ -154,4 +154,15 @@ void test_shrpx_http_create_altsvc_header_value(void) {
|
|||
}
|
||||
}
|
||||
|
||||
void test_shrpx_http_check_http_scheme(void) {
|
||||
CU_ASSERT(http::check_http_scheme(StringRef::from_lit("https"), true));
|
||||
CU_ASSERT(!http::check_http_scheme(StringRef::from_lit("https"), false));
|
||||
CU_ASSERT(!http::check_http_scheme(StringRef::from_lit("http"), true));
|
||||
CU_ASSERT(http::check_http_scheme(StringRef::from_lit("http"), false));
|
||||
CU_ASSERT(!http::check_http_scheme(StringRef::from_lit("foo"), true));
|
||||
CU_ASSERT(!http::check_http_scheme(StringRef::from_lit("foo"), false));
|
||||
CU_ASSERT(!http::check_http_scheme(StringRef{}, true));
|
||||
CU_ASSERT(!http::check_http_scheme(StringRef{}, false));
|
||||
}
|
||||
|
||||
} // namespace shrpx
|
||||
|
|
|
@ -35,6 +35,7 @@ void test_shrpx_http_create_forwarded(void);
|
|||
void test_shrpx_http_create_via_header_value(void);
|
||||
void test_shrpx_http_create_affinity_cookie(void);
|
||||
void test_shrpx_http_create_altsvc_header_value(void);
|
||||
void test_shrpx_http_check_http_scheme(void);
|
||||
|
||||
} // namespace shrpx
|
||||
|
||||
|
|
|
@ -433,12 +433,18 @@ int htp_hdrs_completecb(llhttp_t *htp) {
|
|||
|
||||
downstream->set_request_state(DownstreamState::HEADER_COMPLETE);
|
||||
|
||||
auto &resp = downstream->response();
|
||||
|
||||
if (config->http.require_http_scheme &&
|
||||
!http::check_http_scheme(req.scheme, handler->get_ssl() != nullptr)) {
|
||||
resp.http_status = 400;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MRUBY
|
||||
auto worker = handler->get_worker();
|
||||
auto mruby_ctx = worker->get_mruby_context();
|
||||
|
||||
auto &resp = downstream->response();
|
||||
|
||||
if (mruby_ctx->run_on_request_proc(downstream) != 0) {
|
||||
resp.http_status = 500;
|
||||
return -1;
|
||||
|
|
Loading…
Reference in New Issue