nghttpx: Restructure mode settings

It is very hard to support multiple protocols in backend while
retaining multiple mode settings.  Therefore, we dropped modes except
for default and HTTP/2 proxy mode.  The other removed modes can be
emulated using combinations of options.  Now the backend connection is
not encrypted by default.  To enable encryption on backend connection,
use --backend-tls option.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-02-28 21:35:26 +09:00
parent 44d3801760
commit 06921f35f3
18 changed files with 257 additions and 202 deletions

View File

@ -125,7 +125,8 @@ OPTIONS = [
"backend-address-family",
"frontend-http2-max-concurrent-streams",
"backend-http2-max-concurrent-streams",
"backend-connections-per-frontend"
"backend-connections-per-frontend",
"backend-tls"
]
LOGVARS = [

View File

@ -1416,9 +1416,9 @@ int lookup_method_token(const uint8_t *name, size_t namelen) {
return -1;
}
const char *to_method_string(int method_token) {
StringRef to_method_string(int method_token) {
// we happened to use same value for method with http-parser.
return http_method_str(static_cast<http_method>(method_token));
return StringRef{http_method_str(static_cast<http_method>(method_token))};
}
int get_pure_path_component(const char **base, size_t *baselen,

View File

@ -322,7 +322,11 @@ bool expect_response_body(int status_code);
int lookup_method_token(const uint8_t *name, size_t namelen);
int lookup_method_token(const std::string &name);
const char *to_method_string(int method_token);
// Returns string representation of |method_token|. This is wrapper
// function over http_method_str from http-parser. If |method_token|
// is not known to http-parser, "<unknown>" is returned. The returned
// StringRef is guaranteed to be NULL-terminated.
StringRef to_method_string(int method_token);
template <typename InputIt>
std::string normalize_path(InputIt first, InputIt last) {

View File

@ -656,7 +656,7 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
}
faddr.fd = fd;
faddr.hostport = util::make_http_hostport(host.data(), faddr.port);
faddr.hostport = util::make_http_hostport(StringRef{host.data()}, faddr.port);
LOG(NOTICE) << "Listening on " << faddr.hostport;
@ -1079,7 +1079,8 @@ void fill_default_config() {
tlsconf.session_timeout = std::chrono::hours(12);
auto &httpconf = mod_config()->http;
httpconf.server_name = "nghttpx nghttp2/" NGHTTP2_VERSION;
httpconf.server_name =
StringRef::from_lit("nghttpx nghttp2/" NGHTTP2_VERSION);
httpconf.no_host_rewrite = true;
httpconf.request_header_field_buffer = 64_k;
httpconf.max_request_header_fields = 100;
@ -1165,6 +1166,7 @@ void fill_default_config() {
downstreamconf.request_buffer_size = 16_k;
downstreamconf.response_buffer_size = 128_k;
downstreamconf.family = AF_UNSPEC;
downstreamconf.no_tls = true;
}
}
@ -1188,48 +1190,49 @@ void print_help(std::ostream &out) {
print_usage(out);
out << R"(
<PRIVATE_KEY>
Set path to server's private key. Required unless -p,
--client or --frontend-no-tls are given.
<CERT> Set path to server's certificate. Required unless -p,
--client or --frontend-no-tls are given. To make OCSP
stapling work, this must be absolute path.
Set path to server's private key. Required unless
--frontend-no-tls are given.
<CERT> Set path to server's certificate. Required unless
--frontend-no-tls are given. To make OCSP stapling
work, this must be an absolute path.
Options:
The options are categorized into several groups.
Connections:
-b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;<PATTERN>[:...]]
-b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][;proto=<PROTO>]]
Set backend host and port. The multiple backend
addresses are accepted by repeating this option. UNIX
domain socket can be specified by prefixing path name
with "unix:" (e.g., unix:/var/run/backend.sock).
Optionally, if <PATTERN>s are given, the backend address
is only used if request matches the pattern. If -s or
-p is used, <PATTERN>s are ignored. The pattern
matching is closely designed to ServeMux in net/http
package of Go programming language. <PATTERN> consists
of path, host + path or just host. The path must start
with "/". If it ends with "/", it matches all request
path in its subtree. To deal with the request to the
directory without trailing slash, the path which ends
with "/" also matches the request path which only lacks
trailing '/' (e.g., path "/foo/" matches request path
"/foo"). If it does not end with "/", it performs exact
match against the request path. If host is given, it
performs exact match against the request host. If host
alone is given, "/" is appended to it, so that it
matches all request paths under the host (e.g.,
specifying "nghttp2.org" equals to "nghttp2.org/").
is only used if request matches the pattern. If
--http2-proxy is used, <PATTERN>s are ignored. The
pattern matching is closely designed to ServeMux in
net/http package of Go programming language. <PATTERN>
consists of path, host + path or just host. The path
must start with "/". If it ends with "/", it matches
all request path in its subtree. To deal with the
request to the directory without trailing slash, the
path which ends with "/" also matches the request path
which only lacks trailing '/' (e.g., path "/foo/"
matches request path "/foo"). If it does not end with
"/", it performs exact match against the request path.
If host is given, it performs exact match against the
request host. If host alone is given, "/" is appended
to it, so that it matches all request paths under the
host (e.g., specifying "nghttp2.org" equals to
"nghttp2.org/").
Patterns with host take precedence over patterns with
just path. Then, longer patterns take precedence over
shorter ones, breaking a tie by the order of the
appearance in the configuration.
If <PATTERN> is omitted, "/" is used as pattern, which
matches all request paths (catch-all pattern). The
catch-all backend must be given.
If <PATTERN> is omitted or empty string, "/" is used as
pattern, which matches all request paths (catch-all
pattern). The catch-all backend must be given.
When doing a match, nghttpx made some normalization to
pattern, request host and path. For host part, they are
@ -1252,6 +1255,15 @@ Connections:
The backend addresses sharing same <PATTERN> are grouped
together forming load balancing group.
Optionally, backend application protocol can be
specified in <PROTO>. All that share the same <PATTERN>
must have the same <PROTO> value if it is given.
<PROTO> should be one of the following list without
quotes: "h2", "http/1.1". The default value of <PROTO>
is "http/1.1". Note that usually "h2" refers to HTTP/2
over TLS. But in this option, it may mean HTTP/2 over
cleartext TCP unless --backend-tls is used.
Since ";" and ":" are used as delimiter, <PATTERN> must
not contain these characters. Since ";" has special
meaning in shell, the option value must be quoted.
@ -1290,16 +1302,8 @@ Connections:
--backend-write-timeout options.
--accept-proxy-protocol
Accept PROXY protocol version 1 on frontend connection.
--backend-no-tls
Disable SSL/TLS on backend connections. For HTTP/2
backend connections, TLS is enabled by default. For
HTTP/1 backend connections, TLS is disabled by default,
and can be enabled by --backend-http1-tls option. If
both --backend-no-tls and --backend-http1-tls options
are used, --backend-no-tls has the precedence.
--backend-http1-tls
Enable SSL/TLS on backend HTTP/1 connections. See also
--backend-no-tls option.
--backend-tls
Enable SSL/TLS on backend connections.
Performance:
-n, --workers=<N>
@ -1355,19 +1359,19 @@ Performance:
--backend-http1-connections-per-host=<N>
Set maximum number of backend concurrent HTTP/1
connections per origin host. This option is meaningful
when -s option is used. The origin host is determined
by authority portion of request URI (or :authority
header field for HTTP/2). To limit the number of
connections per frontend for default mode, use
--backend-http1-connections-per-frontend.
when --http2-proxy option is used. The origin host is
determined by authority portion of request URI (or
:authority header field for HTTP/2). To limit the
number of connections per frontend for default mode, use
--backend-connections-per-frontend.
Default: )" << get_config()->conn.downstream.connections_per_host
<< R"(
--backend-connections-per-frontend=<N>
Set maximum number of backend concurrent connections (or
streams in case of HTTP/2) per frontend. This option is
only used for default mode. 0 means unlimited. To
limit the number of connections per host for HTTP/2 or
SPDY proxy mode (-s option), use
limit the number of connections per host with
--http2-proxy option, use
--backend-http1-connections-per-host.
Default: )"
<< get_config()->conn.downstream.connections_per_frontend << R"(
@ -1667,37 +1671,20 @@ HTTP/2 and SPDY:
Disable HTTP/2 server push. Server push is supported by
default mode and HTTP/2 frontend via Link header field.
It is also supported if both frontend and backend are
HTTP/2 (which implies --http2-bridge or --client mode).
In this case, server push from backend session is
relayed to frontend, and server push via Link header
field is also supported. HTTP SPDY frontend does not
support server push.
HTTP/2. In this case, server push from backend session
is relayed to frontend, and server push via Link header
field is also supported. SPDY frontend does not support
server push.
Mode:
(default mode)
Accept HTTP/2, SPDY and HTTP/1.1 over SSL/TLS. If
--frontend-no-tls is used, accept HTTP/2 and HTTP/1.1.
The incoming HTTP/1.1 connection can be upgraded to
HTTP/2 through HTTP Upgrade. The protocol to the
backend is HTTP/1.1.
HTTP/2 through HTTP Upgrade.
-s, --http2-proxy
Like default mode, but enable secure proxy mode.
--http2-bridge
Like default mode, but communicate with the backend in
HTTP/2 over SSL/TLS. Thus the incoming all connections
are converted to HTTP/2 connection and relayed to the
backend. See --backend-http-proxy-uri option if you are
behind the proxy and want to connect to the outside
HTTP/2 proxy.
--client Accept HTTP/2 and HTTP/1.1 without SSL/TLS. The
incoming HTTP/1.1 connection can be upgraded to HTTP/2
connection through HTTP Upgrade. The protocol to the
backend is HTTP/2. To use nghttpx as a forward proxy,
use -p option instead.
-p, --client-proxy
Like --client option, but it also requires the request
path from frontend must be an absolute URI, suitable for
use as a forward proxy.
Like default mode, but enable forward proxy. This is so
called HTTP/2 proxy mode.
Logging:
-L, --log-level=<LEVEL>
@ -1798,15 +1785,13 @@ HTTP:
--no-via Don't append to Via header field. If Via header field
is received, it is left unaltered.
--no-location-rewrite
Don't rewrite location header field on --http2-bridge,
--client and default mode. For --http2-proxy and
--client-proxy mode, location header field will not be
altered regardless of this option.
Don't rewrite location header field in default mode.
When --http2-proxy is used, location header field will
not be altered regardless of this option.
--host-rewrite
Rewrite host and :authority header fields on
--http2-bridge, --client and default mode. For
--http2-proxy and --client-proxy mode, these headers
will not be altered regardless of this option.
Rewrite host and :authority header fields in default
mode. When --http2-proxy is used, these headers will
not be altered regardless of this option.
--altsvc=<PROTOID,PORT[,HOST,[ORIGIN]]>
Specify protocol ID, port, host and origin of
alternative service. <HOST> and <ORIGIN> are optional.
@ -2055,31 +2040,6 @@ void process_options(
upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
}
if (get_config()->http2_proxy + get_config()->http2_bridge +
get_config()->client_proxy + get_config()->client >
1) {
LOG(FATAL) << "--http2-proxy, --http2-bridge, --client-proxy and --client "
<< "cannot be used at the same time.";
exit(EXIT_FAILURE);
}
if (get_config()->client || get_config()->client_proxy) {
mod_config()->client_mode = true;
upstreamconf.no_tls = true;
}
shrpx_proto default_proto;
if (get_config()->client_mode || get_config()->http2_bridge) {
default_proto = PROTO_HTTP2;
} else {
default_proto = PROTO_HTTP1;
}
if (!get_config()->client_mode && !get_config()->http2_bridge &&
!downstreamconf.http1_tls) {
downstreamconf.no_tls = true;
}
if (!upstreamconf.no_tls &&
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
print_usage(std::cerr);
@ -2105,31 +2065,34 @@ void process_options(
addr.port = DEFAULT_DOWNSTREAM_PORT;
DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
g.proto = default_proto;
g.proto = PROTO_HTTP1;
g.addrs.push_back(std::move(addr));
mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size());
addr_groups.push_back(std::move(g));
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
} else if (get_config()->http2_proxy) {
// We don't support host mapping in these cases. Move all
// non-catch-all patterns to catch-all pattern.
DownstreamAddrGroupConfig catch_all(StringRef::from_lit("/"));
auto proto = PROTO_NONE;
for (auto &g : addr_groups) {
if (proto == PROTO_NONE) {
proto = g.proto;
} else if (proto != g.proto) {
LOG(ERROR) << SHRPX_OPT_BACKEND << ": <PATTERN> was ignored with "
"--http2-proxy, and protocol must "
"be the same for all backends.";
exit(EXIT_FAILURE);
}
std::move(std::begin(g.addrs), std::end(g.addrs),
std::back_inserter(catch_all.addrs));
}
catch_all.proto = default_proto;
catch_all.proto = proto;
std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
// maybe not necessary?
mod_config()->router = Router();
mod_config()->router.add_route(StringRef{catch_all.pattern},
addr_groups.size());
addr_groups.push_back(std::move(catch_all));
} else {
for (auto &g : addr_groups) {
if (g.proto == PROTO_NONE) {
g.proto = default_proto;
}
}
}
if (LOG_ENABLED(INFO)) {
@ -2144,7 +2107,7 @@ void process_options(
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
<< "'";
<< "', proto=" << strproto(g.proto);
for (auto &addr : g.addrs) {
LOG(INFO) << "group " << i << " -> " << addr.host.c_str()
<< (addr.host_unix ? "" : ":" + util::utos(addr.port));
@ -2153,7 +2116,7 @@ void process_options(
}
if (catch_all_group == -1) {
LOG(FATAL) << "-b: No catch-all backend address is configured";
LOG(FATAL) << "backend: No catch-all backend address is configured";
exit(EXIT_FAILURE);
}
@ -2197,7 +2160,7 @@ void process_options(
addr.hostport = ImmutableString(
util::make_http_hostport(StringRef(addr.host), addr.port));
auto hostport = util::make_hostport(addr.host.c_str(), addr.port);
auto hostport = util::make_hostport(StringRef{addr.host}, addr.port);
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
downstreamconf.family) == -1) {
@ -2211,7 +2174,7 @@ void process_options(
auto &proxy = mod_config()->downstream_http_proxy;
if (!proxy.host.empty()) {
auto hostport = util::make_hostport(proxy.host.c_str(), proxy.port);
auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port);
if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port,
AF_UNSPEC) == -1) {
LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport;
@ -2476,6 +2439,7 @@ int main(int argc, char **argv) {
&flag, 118},
{SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND, required_argument, &flag,
119},
{SHRPX_OPT_BACKEND_TLS, no_argument, &flag, 120},
{nullptr, 0, nullptr, 0}};
int option_index = 0;
@ -2986,6 +2950,10 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND,
optarg);
break;
case 120:
// --backend-tls
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS, "yes");
break;
default:
break;
}

View File

@ -853,11 +853,11 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
upstream_accesslog(
get_config()->logging.access.format,
LogSpec{
downstream, StringRef(ipaddr_), http2::to_method_string(req.method),
downstream, StringRef{ipaddr_}, http2::to_method_string(req.method),
req.method == HTTP_CONNECT
? StringRef(req.authority)
: (get_config()->http2_proxy || get_config()->client_proxy)
: get_config()->http2_proxy
? StringRef(construct_absolute_request_uri(req))
: req.path.empty()
? req.method == HTTP_OPTIONS

View File

@ -571,33 +571,70 @@ int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) {
} // namespace
namespace {
// Parses host-path mapping patterns in |src|, and stores mappings in
// config. We will store each host-path pattern found in |src| with
// |addr|. |addr| will be copied accordingly. Also we make a group
// based on the pattern. The "/" pattern is considered as catch-all.
void parse_mapping(const DownstreamAddrConfig &addr, const char *src) {
// Parses host-path mapping patterns in |src_pattern|, and stores
// mappings in config. We will store each host-path pattern found in
// |src| with |addr|. |addr| will be copied accordingly. Also we
// make a group based on the pattern. The "/" pattern is considered
// as catch-all. We also parse protocol specified in |src_proto|.
//
// This function returns 0 if it succeeds, or -1.
int parse_mapping(const DownstreamAddrConfig &addr,
const StringRef &src_pattern, const StringRef &src_proto) {
// This returns at least 1 element (it could be empty string). We
// will append '/' to all patterns, so it becomes catch-all pattern.
auto mapping = util::split_config_str_list(src, ':');
auto mapping = util::split_str(src_pattern, ':');
assert(!mapping.empty());
auto &addr_groups = mod_config()->conn.downstream.addr_groups;
auto proto = PROTO_HTTP1;
if (!src_proto.empty()) {
if (!util::istarts_with_l(src_proto, "proto=")) {
LOG(ERROR) << "backend: proto keyword not found";
return -1;
}
auto protostr = StringRef{std::begin(src_proto) + str_size("proto="),
std::end(src_proto)};
if (protostr.empty()) {
LOG(ERROR) << "backend: protocol is empty";
return -1;
}
if (util::streq_l("h2", std::begin(protostr), protostr.size())) {
proto = PROTO_HTTP2;
} else if (util::streq_l("http/1.1", std::begin(protostr),
protostr.size())) {
proto = PROTO_HTTP1;
} else {
LOG(ERROR) << "backend: unknown protocol " << protostr;
return -1;
}
}
for (const auto &raw_pattern : mapping) {
auto done = false;
std::string pattern;
auto slash = std::find(raw_pattern.first, raw_pattern.second, '/');
if (slash == raw_pattern.second) {
auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
if (slash == std::end(raw_pattern)) {
// This effectively makes empty pattern to "/".
pattern.assign(raw_pattern.first, raw_pattern.second);
pattern.assign(std::begin(raw_pattern), std::end(raw_pattern));
util::inp_strlower(pattern);
pattern += '/';
} else {
pattern.assign(raw_pattern.first, slash);
pattern.assign(std::begin(raw_pattern), slash);
util::inp_strlower(pattern);
pattern += http2::normalize_path(slash, raw_pattern.second);
pattern += http2::normalize_path(slash, std::end(raw_pattern));
}
for (auto &g : addr_groups) {
if (g.pattern == pattern) {
if (g.proto != proto) {
LOG(ERROR) << "backend: protocol mismatch. We saw protocol "
<< strproto(g.proto) << " for pattern " << g.pattern
<< ", but another protocol " << strproto(proto);
return -1;
}
g.addrs.push_back(addr);
done = true;
break;
@ -608,11 +645,13 @@ void parse_mapping(const DownstreamAddrConfig &addr, const char *src) {
}
DownstreamAddrGroupConfig g(StringRef{pattern});
g.addrs.push_back(addr);
g.proto = proto;
mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size());
addr_groups.push_back(std::move(g));
}
return 0;
}
} // namespace
@ -670,6 +709,7 @@ enum {
SHRPX_OPTID_BACKEND_READ_TIMEOUT,
SHRPX_OPTID_BACKEND_REQUEST_BUFFER,
SHRPX_OPTID_BACKEND_RESPONSE_BUFFER,
SHRPX_OPTID_BACKEND_TLS,
SHRPX_OPTID_BACKEND_TLS_SNI_FIELD,
SHRPX_OPTID_BACKEND_WRITE_TIMEOUT,
SHRPX_OPTID_BACKLOG,
@ -914,6 +954,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break;
case 11:
switch (name[10]) {
case 's':
if (util::strieq_l("backend-tl", name, 10)) {
return SHRPX_OPTID_BACKEND_TLS;
}
break;
case 't':
if (util::strieq_l("write-burs", name, 10)) {
return SHRPX_OPTID_WRITE_BURST;
@ -1495,19 +1540,17 @@ int parse_config(const char *opt, const char *optarg,
switch (optid) {
case SHRPX_OPTID_BACKEND: {
auto optarglen = strlen(optarg);
const char *pat_delim = strchr(optarg, ';');
if (!pat_delim) {
pat_delim = optarg + optarglen;
}
auto src = StringRef{optarg};
auto addr_end = std::find(std::begin(src), std::end(src), ';');
DownstreamAddrConfig addr{};
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
addr.host = ImmutableString(path, pat_delim);
auto path = std::begin(src) + str_size(SHRPX_UNIX_PATH_PREFIX);
addr.host = ImmutableString(path, addr_end);
addr.host_unix = true;
} else {
if (split_host_port(host, sizeof(host), &port, optarg,
pat_delim - optarg) == -1) {
if (split_host_port(host, sizeof(host), &port, &src[0],
addr_end - std::begin(src)) == -1) {
return -1;
}
@ -1515,14 +1558,16 @@ int parse_config(const char *opt, const char *optarg,
addr.port = port;
}
auto mapping = pat_delim < optarg + optarglen ? pat_delim + 1 : pat_delim;
// We may introduce new parameter after additional ';', so don't
// allow extra ';' in pattern for now.
if (strchr(mapping, ';') != nullptr) {
LOG(ERROR) << opt << ": ';' must not be used in pattern";
auto mapping = addr_end == std::end(src) ? addr_end : addr_end + 1;
auto mapping_end = std::find(mapping, std::end(src), ';');
auto proto = mapping_end == std::end(src) ? mapping_end : mapping_end + 1;
auto proto_end = std::find(proto, std::end(src), ';');
if (parse_mapping(addr, StringRef{mapping, mapping_end},
StringRef{proto, proto_end}) != 0) {
return -1;
}
parse_mapping(addr, mapping);
return 0;
}
@ -1607,13 +1652,13 @@ int parse_config(const char *opt, const char *optarg,
return 0;
case SHRPX_OPTID_HTTP2_BRIDGE:
mod_config()->http2_bridge = util::strieq(optarg, "yes");
return 0;
LOG(ERROR) << opt << ": deprecated. Use backend=<addr>,<port>;;proto=h2 "
"and backend-tls";
return -1;
case SHRPX_OPTID_CLIENT_PROXY:
mod_config()->client_proxy = util::strieq(optarg, "yes");
return 0;
LOG(ERROR) << opt << ": deprecated. Use http2-proxy, frontend-no-tls, "
"backend=<addr>,<port>;;proto=h2 and backend-tls";
return -1;
case SHRPX_OPTID_ADD_X_FORWARDED_FOR:
mod_config()->http.xff.add = util::strieq(optarg, "yes");
@ -1746,8 +1791,8 @@ int parse_config(const char *opt, const char *optarg,
return 0;
case SHRPX_OPTID_BACKEND_NO_TLS:
mod_config()->conn.downstream.no_tls = util::strieq(optarg, "yes");
LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by "
"default. See also " << SHRPX_OPT_BACKEND_TLS;
return 0;
case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
mod_config()->tls.backend_sni_name = optarg;
@ -1834,9 +1879,9 @@ int parse_config(const char *opt, const char *optarg,
return 0;
case SHRPX_OPTID_CLIENT:
mod_config()->client = util::strieq(optarg, "yes");
return 0;
LOG(ERROR) << opt << ": deprecated. Use frontend-no-tls, "
"backend=<addr>,<port>;;proto=h2 and backend-tls";
return -1;
case SHRPX_OPTID_INSECURE:
mod_config()->tls.insecure = util::strieq(optarg, "yes");
@ -2312,7 +2357,11 @@ int parse_config(const char *opt, const char *optarg,
return 0;
case SHRPX_OPTID_BACKEND_HTTP1_TLS:
mod_config()->conn.downstream.http1_tls = util::strieq(optarg, "yes");
LOG(WARN) << opt << ": deprecated. Use " << SHRPX_OPT_BACKEND_TLS
<< " instead.";
// fall through
case SHRPX_OPTID_BACKEND_TLS:
mod_config()->conn.downstream.no_tls = !util::strieq(optarg, "yes");
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS:
@ -2533,4 +2582,17 @@ int int_syslog_facility(const char *strfacility) {
return -1;
}
StringRef strproto(shrpx_proto proto) {
switch (proto) {
case PROTO_NONE:
return StringRef::from_lit("none");
case PROTO_HTTP1:
return StringRef::from_lit("http/1.1");
case PROTO_HTTP2:
return StringRef::from_lit("h2");
case PROTO_MEMCACHED:
return StringRef::from_lit("memcached");
}
}
} // namespace shrpx

View File

@ -234,6 +234,7 @@ constexpr char SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS[] =
"backend-http2-max-concurrent-streams";
constexpr char SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND[] =
"backend-connections-per-frontend";
constexpr char SHRPX_OPT_BACKEND_TLS[] = "backend-tls";
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@ -599,11 +600,6 @@ struct Config {
bool verbose;
bool daemon;
bool http2_proxy;
bool http2_bridge;
bool client_proxy;
bool client;
// true if --client or --client-proxy are enabled.
bool client_mode;
};
const Config *get_config();
@ -650,6 +646,9 @@ std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<std::string> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac);
// Returns string representation of |proto|.
StringRef strproto(shrpx_proto proto);
} // namespace shrpx
#endif // SHRPX_CONFIG_H

View File

@ -264,9 +264,9 @@ int Http2DownstreamConnection::push_request_headers() {
auto &httpconf = get_config()->http;
auto &http2conf = get_config()->http2;
auto no_host_rewrite =
httpconf.no_host_rewrite || get_config()->http2_proxy ||
get_config()->client_proxy || req.method == HTTP_CONNECT;
auto no_host_rewrite = httpconf.no_host_rewrite ||
get_config()->http2_proxy ||
req.method == HTTP_CONNECT;
// http2session_ has already in CONNECTED state, so we can get
// addr_idx here.
@ -302,7 +302,7 @@ int Http2DownstreamConnection::push_request_headers() {
httpconf.add_request_headers.size());
nva.push_back(
http2::make_nv_lc_nocopy(":method", http2::to_method_string(req.method)));
http2::make_nv_ls_nocopy(":method", http2::to_method_string(req.method)));
if (req.method != HTTP_CONNECT) {
assert(!req.scheme.empty());
@ -350,8 +350,7 @@ int Http2DownstreamConnection::push_request_headers() {
if (fwdconf.params) {
auto params = fwdconf.params;
if (get_config()->http2_proxy || get_config()->client_proxy ||
req.method == HTTP_CONNECT) {
if (get_config()->http2_proxy || req.method == HTTP_CONNECT) {
params &= ~FORWARDED_PROTO;
}
@ -394,8 +393,7 @@ int Http2DownstreamConnection::push_request_headers() {
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", (*xff).value));
}
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
req.method != HTTP_CONNECT) {
if (!get_config()->http2_proxy && req.method != HTTP_CONNECT) {
// We use same protocol with :scheme header field
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme));
}

View File

@ -1460,8 +1460,7 @@ int Http2Session::connection_made() {
entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
entry[1].value = (1 << http2conf.downstream.window_bits) - 1;
if (http2conf.no_server_push || get_config()->http2_proxy ||
get_config()->client_proxy) {
if (http2conf.no_server_push || get_config()->http2_proxy) {
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
entry[nentry].value = 0;
++nentry;

View File

@ -316,7 +316,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
if (path) {
if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty.
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
} else if (get_config()->http2_proxy) {
req.path = http2::value_to_str(path);
} else {
const auto &value = path->value;
@ -1346,8 +1346,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
auto &httpconf = get_config()->http;
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!httpconf.no_location_rewrite) {
if (!get_config()->http2_proxy && !httpconf.no_location_rewrite) {
downstream->rewrite_location_response_header(req.scheme);
}
@ -1416,7 +1415,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers());
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
if (!get_config()->http2_proxy) {
nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
} else {
auto server = resp.fs.header(http2::HD_SERVER);
@ -1489,9 +1488,8 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
if (!http2conf.no_server_push &&
nghttp2_session_get_remote_settings(session_,
NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 &&
!get_config()->http2_proxy && !get_config()->client_proxy &&
(downstream->get_stream_id() % 2) && resp.fs.header(http2::HD_LINK) &&
resp.http_status == 200 &&
!get_config()->http2_proxy && (downstream->get_stream_id() % 2) &&
resp.fs.header(http2::HD_LINK) && resp.http_status == 200 &&
(req.method == HTTP_GET || req.method == HTTP_POST)) {
if (prepare_push_promise(downstream) != 0) {
@ -1852,7 +1850,7 @@ bool Http2Upstream::push_enabled() const {
return !(get_config()->http2.no_server_push ||
nghttp2_session_get_remote_settings(
session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 0 ||
get_config()->http2_proxy || get_config()->client_proxy);
get_config()->http2_proxy);
}
int Http2Upstream::initiate_push(Downstream *downstream, const char *uri,

View File

@ -279,9 +279,8 @@ int HttpDownstreamConnection::push_request_headers() {
// For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless.
auto authority = StringRef(downstream_hostport);
auto no_host_rewrite = httpconf.no_host_rewrite ||
get_config()->http2_proxy ||
get_config()->client_proxy || connect_method;
auto no_host_rewrite =
httpconf.no_host_rewrite || get_config()->http2_proxy || connect_method;
if (no_host_rewrite && !req.authority.empty()) {
authority = StringRef(req.authority);
@ -293,12 +292,12 @@ int HttpDownstreamConnection::push_request_headers() {
// Assume that method and request path do not contain \r\n.
auto meth = http2::to_method_string(req.method);
buf->append(meth, strlen(meth));
buf->append(meth);
buf->append(" ");
if (connect_method) {
buf->append(authority);
} else if (get_config()->http2_proxy || get_config()->client_proxy) {
} else if (get_config()->http2_proxy) {
// Construct absolute-form request target because we are going to
// send a request to a HTTP/1 proxy.
assert(!req.scheme.empty());
@ -363,8 +362,7 @@ int HttpDownstreamConnection::push_request_headers() {
if (fwdconf.params) {
auto params = fwdconf.params;
if (get_config()->http2_proxy || get_config()->client_proxy ||
connect_method) {
if (get_config()->http2_proxy || connect_method) {
params &= ~FORWARDED_PROTO;
}
@ -407,8 +405,7 @@ int HttpDownstreamConnection::push_request_headers() {
buf->append((*xff).value);
buf->append("\r\n");
}
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!connect_method) {
if (!get_config()->http2_proxy && !connect_method) {
buf->append("X-Forwarded-Proto: ");
assert(!req.scheme.empty());
buf->append(req.scheme);

View File

@ -232,7 +232,7 @@ void rewrite_request_host_path_from_uri(Request &req, const char *uri,
path += '?';
path.append(uri + fdata.off, fdata.len);
}
if (get_config()->http2_proxy || get_config()->client_proxy) {
if (get_config()->http2_proxy) {
req.path = std::move(path);
} else {
req.path = http2::rewrite_clean_path(std::begin(path), std::end(path));
@ -306,7 +306,7 @@ int htp_hdrs_completecb(http_parser *htp) {
}
// checking UF_HOST could be redundant, but just in case ...
if (!(u.field_set & (1 << UF_SCHEMA)) || !(u.field_set & (1 << UF_HOST))) {
if (get_config()->http2_proxy || get_config()->client_proxy) {
if (get_config()->http2_proxy) {
// Request URI should be absolute-form for client proxy mode
return -1;
}
@ -929,8 +929,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
auto &httpconf = get_config()->http;
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!httpconf.no_location_rewrite) {
if (!get_config()->http2_proxy && !httpconf.no_location_rewrite) {
downstream->rewrite_location_response_header(
get_client_handler()->get_upstream_scheme());
}
@ -999,7 +998,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
}
}
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
if (!get_config()->http2_proxy) {
buf->append("Server: ");
buf->append(httpconf.server_name);
buf->append("\r\n");

View File

@ -70,7 +70,7 @@ mrb_value request_get_method(mrb_state *mrb, mrb_value self) {
const auto &req = downstream->request();
auto method = http2::to_method_string(req.method);
return mrb_str_new_cstr(mrb, method);
return mrb_str_new(mrb, method.c_str(), method.size());
}
} // namespace

View File

@ -262,7 +262,7 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type,
} else {
req.scheme = scheme->value;
req.authority = host->value;
if (get_config()->http2_proxy || get_config()->client_proxy) {
if (get_config()->http2_proxy) {
req.path = path->value;
} else if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty.
@ -1009,8 +1009,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
auto &httpconf = get_config()->http;
if (!get_config()->http2_proxy && !get_config()->client_proxy &&
!httpconf.no_location_rewrite) {
if (!get_config()->http2_proxy && !httpconf.no_location_rewrite) {
downstream->rewrite_location_response_header(req.scheme);
}
@ -1044,7 +1043,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {
nv[hdidx++] = hd.value.c_str();
}
if (!get_config()->http2_proxy && !get_config()->client_proxy) {
if (!get_config()->http2_proxy) {
nv[hdidx++] = "server";
nv[hdidx++] = httpconf.server_name.c_str();
} else {

View File

@ -318,7 +318,7 @@ size_t match_downstream_addr_group_host(
return group;
}
group = router.match("", path);
group = router.match(StringRef::from_lit(""), path);
if (group != -1) {
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Found pattern with query " << path

View File

@ -400,7 +400,7 @@ public:
explicit StringRef(const std::string &s) : base(s.c_str()), len(s.size()) {}
explicit StringRef(const ImmutableString &s)
: base(s.c_str()), len(s.size()) {}
StringRef(const char *s) : base(s), len(strlen(s)) {}
explicit StringRef(const char *s) : base(s), len(strlen(s)) {}
template <typename CharT>
constexpr StringRef(const CharT *s, size_t n)
: base(reinterpret_cast<const char *>(s)), len(n) {}

View File

@ -857,6 +857,27 @@ std::vector<unsigned char> get_default_alpn() {
return res;
}
std::vector<StringRef> split_str(const StringRef &s, char delim) {
size_t len = 1;
auto last = std::end(s);
for (auto first = std::begin(s), d = first;
(d = std::find(first, last, delim)) != last; ++len, first = d + 1)
;
auto list = std::vector<StringRef>(len);
len = 0;
for (auto first = std::begin(s);; ++len) {
auto stop = std::find(first, last, delim);
list[len] = StringRef{first, stop};
if (stop == last) {
break;
}
first = stop + 1;
}
return list;
}
std::vector<Range<const char *>> split_config_str_list(const char *s,
char delim) {
size_t len = 1;

View File

@ -225,6 +225,11 @@ bool istarts_with_l(const std::string &a, const CharT(&b)[N]) {
return istarts_with(std::begin(a), std::end(a), b, b + N - 1);
}
template <typename CharT, size_t N>
bool istarts_with_l(const StringRef &a, const CharT(&b)[N]) {
return istarts_with(std::begin(a), std::end(a), b, b + N - 1);
}
template <typename InputIterator1, typename InputIterator2>
bool ends_with(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2) {
@ -543,6 +548,11 @@ std::vector<std::string> parse_config_str_list(const char *s, char delim = ',');
std::vector<Range<const char *>> split_config_str_list(const char *s,
char delim);
// Parses delimited strings in |s| and returns Substrings in |s|
// delimited by |delim|. The any white spaces around substring are
// treated as a part of substring.
std::vector<StringRef> split_str(const StringRef &s, char delim);
// Returns given time |tp| in Common Log format (e.g.,
// 03/Jul/2014:00:19:38 +0900)
// Expected type of |tp| is std::chrono::timepoint