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", "backend-address-family",
"frontend-http2-max-concurrent-streams", "frontend-http2-max-concurrent-streams",
"backend-http2-max-concurrent-streams", "backend-http2-max-concurrent-streams",
"backend-connections-per-frontend" "backend-connections-per-frontend",
"backend-tls"
] ]
LOGVARS = [ LOGVARS = [

View File

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

View File

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

View File

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

View File

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

View File

@ -264,9 +264,9 @@ int Http2DownstreamConnection::push_request_headers() {
auto &httpconf = get_config()->http; auto &httpconf = get_config()->http;
auto &http2conf = get_config()->http2; auto &http2conf = get_config()->http2;
auto no_host_rewrite = auto no_host_rewrite = httpconf.no_host_rewrite ||
httpconf.no_host_rewrite || get_config()->http2_proxy || get_config()->http2_proxy ||
get_config()->client_proxy || req.method == HTTP_CONNECT; req.method == HTTP_CONNECT;
// http2session_ has already in CONNECTED state, so we can get // http2session_ has already in CONNECTED state, so we can get
// addr_idx here. // addr_idx here.
@ -302,7 +302,7 @@ int Http2DownstreamConnection::push_request_headers() {
httpconf.add_request_headers.size()); httpconf.add_request_headers.size());
nva.push_back( 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) { if (req.method != HTTP_CONNECT) {
assert(!req.scheme.empty()); assert(!req.scheme.empty());
@ -350,8 +350,7 @@ int Http2DownstreamConnection::push_request_headers() {
if (fwdconf.params) { if (fwdconf.params) {
auto params = fwdconf.params; auto params = fwdconf.params;
if (get_config()->http2_proxy || get_config()->client_proxy || if (get_config()->http2_proxy || req.method == HTTP_CONNECT) {
req.method == HTTP_CONNECT) {
params &= ~FORWARDED_PROTO; 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)); nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-for", (*xff).value));
} }
if (!get_config()->http2_proxy && !get_config()->client_proxy && if (!get_config()->http2_proxy && req.method != HTTP_CONNECT) {
req.method != HTTP_CONNECT) {
// We use same protocol with :scheme header field // We use same protocol with :scheme header field
nva.push_back(http2::make_nv_ls_nocopy("x-forwarded-proto", req.scheme)); 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].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
entry[1].value = (1 << http2conf.downstream.window_bits) - 1; entry[1].value = (1 << http2conf.downstream.window_bits) - 1;
if (http2conf.no_server_push || get_config()->http2_proxy || if (http2conf.no_server_push || get_config()->http2_proxy) {
get_config()->client_proxy) {
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
entry[nentry].value = 0; entry[nentry].value = 0;
++nentry; ++nentry;

View File

@ -316,7 +316,7 @@ int Http2Upstream::on_request_headers(Downstream *downstream,
if (path) { if (path) {
if (method_token == HTTP_OPTIONS && path->value == "*") { if (method_token == HTTP_OPTIONS && path->value == "*") {
// Server-wide OPTIONS request. Path is empty. // 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); req.path = http2::value_to_str(path);
} else { } else {
const auto &value = path->value; const auto &value = path->value;
@ -1346,8 +1346,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
auto &httpconf = get_config()->http; auto &httpconf = get_config()->http;
if (!get_config()->http2_proxy && !get_config()->client_proxy && if (!get_config()->http2_proxy && !httpconf.no_location_rewrite) {
!httpconf.no_location_rewrite) {
downstream->rewrite_location_response_header(req.scheme); 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()); 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)); nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
} else { } else {
auto server = resp.fs.header(http2::HD_SERVER); 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 && if (!http2conf.no_server_push &&
nghttp2_session_get_remote_settings(session_, nghttp2_session_get_remote_settings(session_,
NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 && NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 &&
!get_config()->http2_proxy && !get_config()->client_proxy && !get_config()->http2_proxy && (downstream->get_stream_id() % 2) &&
(downstream->get_stream_id() % 2) && resp.fs.header(http2::HD_LINK) && resp.fs.header(http2::HD_LINK) && resp.http_status == 200 &&
resp.http_status == 200 &&
(req.method == HTTP_GET || req.method == HTTP_POST)) { (req.method == HTTP_GET || req.method == HTTP_POST)) {
if (prepare_push_promise(downstream) != 0) { if (prepare_push_promise(downstream) != 0) {
@ -1852,7 +1850,7 @@ bool Http2Upstream::push_enabled() const {
return !(get_config()->http2.no_server_push || return !(get_config()->http2.no_server_push ||
nghttp2_session_get_remote_settings( nghttp2_session_get_remote_settings(
session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 0 || 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, 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 // For HTTP/1.0 request, there is no authority in request. In that
// case, we use backend server's host nonetheless. // case, we use backend server's host nonetheless.
auto authority = StringRef(downstream_hostport); auto authority = StringRef(downstream_hostport);
auto no_host_rewrite = httpconf.no_host_rewrite || auto no_host_rewrite =
get_config()->http2_proxy || httpconf.no_host_rewrite || get_config()->http2_proxy || connect_method;
get_config()->client_proxy || connect_method;
if (no_host_rewrite && !req.authority.empty()) { if (no_host_rewrite && !req.authority.empty()) {
authority = StringRef(req.authority); authority = StringRef(req.authority);
@ -293,12 +292,12 @@ int HttpDownstreamConnection::push_request_headers() {
// Assume that method and request path do not contain \r\n. // Assume that method and request path do not contain \r\n.
auto meth = http2::to_method_string(req.method); auto meth = http2::to_method_string(req.method);
buf->append(meth, strlen(meth)); buf->append(meth);
buf->append(" "); buf->append(" ");
if (connect_method) { if (connect_method) {
buf->append(authority); 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 // Construct absolute-form request target because we are going to
// send a request to a HTTP/1 proxy. // send a request to a HTTP/1 proxy.
assert(!req.scheme.empty()); assert(!req.scheme.empty());
@ -363,8 +362,7 @@ int HttpDownstreamConnection::push_request_headers() {
if (fwdconf.params) { if (fwdconf.params) {
auto params = fwdconf.params; auto params = fwdconf.params;
if (get_config()->http2_proxy || get_config()->client_proxy || if (get_config()->http2_proxy || connect_method) {
connect_method) {
params &= ~FORWARDED_PROTO; params &= ~FORWARDED_PROTO;
} }
@ -407,8 +405,7 @@ int HttpDownstreamConnection::push_request_headers() {
buf->append((*xff).value); buf->append((*xff).value);
buf->append("\r\n"); buf->append("\r\n");
} }
if (!get_config()->http2_proxy && !get_config()->client_proxy && if (!get_config()->http2_proxy && !connect_method) {
!connect_method) {
buf->append("X-Forwarded-Proto: "); buf->append("X-Forwarded-Proto: ");
assert(!req.scheme.empty()); assert(!req.scheme.empty());
buf->append(req.scheme); buf->append(req.scheme);

View File

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

View File

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

View File

@ -318,7 +318,7 @@ size_t match_downstream_addr_group_host(
return group; return group;
} }
group = router.match("", path); group = router.match(StringRef::from_lit(""), path);
if (group != -1) { if (group != -1) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Found pattern with query " << path 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 std::string &s) : base(s.c_str()), len(s.size()) {}
explicit StringRef(const ImmutableString &s) explicit StringRef(const ImmutableString &s)
: base(s.c_str()), len(s.size()) {} : 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> template <typename CharT>
constexpr StringRef(const CharT *s, size_t n) constexpr StringRef(const CharT *s, size_t n)
: base(reinterpret_cast<const char *>(s)), len(n) {} : base(reinterpret_cast<const char *>(s)), len(n) {}

View File

@ -857,6 +857,27 @@ std::vector<unsigned char> get_default_alpn() {
return res; 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, std::vector<Range<const char *>> split_config_str_list(const char *s,
char delim) { char delim) {
size_t len = 1; 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); 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> template <typename InputIterator1, typename InputIterator2>
bool ends_with(InputIterator1 first1, InputIterator1 last1, bool ends_with(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2) { 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, std::vector<Range<const char *>> split_config_str_list(const char *s,
char delim); 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., // Returns given time |tp| in Common Log format (e.g.,
// 03/Jul/2014:00:19:38 +0900) // 03/Jul/2014:00:19:38 +0900)
// Expected type of |tp| is std::chrono::timepoint // Expected type of |tp| is std::chrono::timepoint