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:
parent
44d3801760
commit
06921f35f3
|
@ -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 = [
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
196
src/shrpx.cc
196
src/shrpx.cc
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {}
|
||||
|
|
21
src/util.cc
21
src/util.cc
|
@ -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;
|
||||
|
|
10
src/util.h
10
src/util.h
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue