From 06921f35f35d550e7dd7a94cc14c197f524bcaa4 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 28 Feb 2016 21:35:26 +0900 Subject: [PATCH] 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. --- gennghttpxfun.py | 3 +- src/http2.cc | 4 +- src/http2.h | 6 +- src/shrpx.cc | 196 ++++++++++------------- src/shrpx_client_handler.cc | 4 +- src/shrpx_config.cc | 138 +++++++++++----- src/shrpx_config.h | 9 +- src/shrpx_http2_downstream_connection.cc | 14 +- src/shrpx_http2_session.cc | 3 +- src/shrpx_http2_upstream.cc | 14 +- src/shrpx_http_downstream_connection.cc | 15 +- src/shrpx_https_upstream.cc | 9 +- src/shrpx_mruby_module_request.cc | 2 +- src/shrpx_spdy_upstream.cc | 7 +- src/shrpx_worker.cc | 2 +- src/template.h | 2 +- src/util.cc | 21 +++ src/util.h | 10 ++ 18 files changed, 257 insertions(+), 202 deletions(-) diff --git a/gennghttpxfun.py b/gennghttpxfun.py index 6609a910..5ffb960d 100755 --- a/gennghttpxfun.py +++ b/gennghttpxfun.py @@ -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 = [ diff --git a/src/http2.cc b/src/http2.cc index f563596e..9acf1862 100644 --- a/src/http2.cc +++ b/src/http2.cc @@ -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(method_token)); + return StringRef{http_method_str(static_cast(method_token))}; } int get_pure_path_component(const char **base, size_t *baselen, diff --git a/src/http2.h b/src/http2.h index 785089c6..bb879fdd 100644 --- a/src/http2.h +++ b/src/http2.h @@ -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, "" is returned. The returned +// StringRef is guaranteed to be NULL-terminated. +StringRef to_method_string(int method_token); template std::string normalize_path(InputIt first, InputIt last) { diff --git a/src/shrpx.cc b/src/shrpx.cc index 76e6f3f0..7a9616f7 100644 --- a/src/shrpx.cc +++ b/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"( - Set path to server's private key. Required unless -p, - --client or --frontend-no-tls are given. - 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. + 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=(,|unix:)[;[:...]] + -b, --backend=(,|unix:)[;[[:...]][;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 s are given, the backend address - is only used if request matches the pattern. If -s or - -p is used, s are ignored. The pattern - matching is closely designed to ServeMux in net/http - package of Go programming language. 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, s are ignored. The + pattern matching is closely designed to ServeMux in + net/http package of Go programming language. + 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 is omitted, "/" is used as pattern, which - matches all request paths (catch-all pattern). The - catch-all backend must be given. + If 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 are grouped together forming load balancing group. + Optionally, backend application protocol can be + specified in . All that share the same + must have the same value if it is given. + should be one of the following list without + quotes: "h2", "http/1.1". The default value of + 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, 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= @@ -1355,19 +1359,19 @@ Performance: --backend-http1-connections-per-host= 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= 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= @@ -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= Specify protocol ID, port, host and origin of alternative service. and are optional. @@ -2055,31 +2040,6 @@ void process_options( upstreamconf.worker_connections = std::numeric_limits::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 << ": 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().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; } diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 9dbd6453..7151dbf2 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -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 diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 2c8a0958..8a1d2103 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -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=,;;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=,;;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=,;;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 diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 611e40ab..874dabe1 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -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 read_tls_ticket_key_file(const std::vector &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 diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index aba79df4..6aa97d1f 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -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)); } diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index dd8cd5e4..9a411ca1 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -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; diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index aecff732..5bf1c5c4 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -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, diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index b68bb27e..fcd9b8e6 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -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); diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 9eaa5202..e089428d 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -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"); diff --git a/src/shrpx_mruby_module_request.cc b/src/shrpx_mruby_module_request.cc index f585d730..89bbb660 100644 --- a/src/shrpx_mruby_module_request.cc +++ b/src/shrpx_mruby_module_request.cc @@ -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 diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 97e56f10..b7f07724 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -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 { diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 65d547b6..7c8c6bb3 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -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 diff --git a/src/template.h b/src/template.h index 7c9dc6f1..6adcf0d5 100644 --- a/src/template.h +++ b/src/template.h @@ -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 constexpr StringRef(const CharT *s, size_t n) : base(reinterpret_cast(s)), len(n) {} diff --git a/src/util.cc b/src/util.cc index 12b90594..7805a957 100644 --- a/src/util.cc +++ b/src/util.cc @@ -857,6 +857,27 @@ std::vector get_default_alpn() { return res; } +std::vector 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(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> split_config_str_list(const char *s, char delim) { size_t len = 1; diff --git a/src/util.h b/src/util.h index f4b1c2a0..15894816 100644 --- a/src/util.h +++ b/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 +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 bool ends_with(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) { @@ -543,6 +548,11 @@ std::vector parse_config_str_list(const char *s, char delim = ','); std::vector> 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 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