nghttpx: Share the code to configure backends
This commit is contained in:
parent
09150a7927
commit
2fd095d036
185
src/shrpx.cc
185
src/shrpx.cc
|
@ -146,52 +146,6 @@ struct SignalServer {
|
||||||
pid_t worker_process_pid;
|
pid_t worker_process_pid;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
|
||||||
int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
|
|
||||||
int family) {
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
auto service = util::utos(port);
|
|
||||||
|
|
||||||
addrinfo hints{};
|
|
||||||
hints.ai_family = family;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
#ifdef AI_ADDRCONFIG
|
|
||||||
hints.ai_flags |= AI_ADDRCONFIG;
|
|
||||||
#endif // AI_ADDRCONFIG
|
|
||||||
addrinfo *res;
|
|
||||||
|
|
||||||
rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
|
|
||||||
if (rv != 0) {
|
|
||||||
LOG(FATAL) << "Unable to resolve address for " << hostname << ": "
|
|
||||||
<< gai_strerror(rv);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res_d = defer(freeaddrinfo, res);
|
|
||||||
|
|
||||||
char host[NI_MAXHOST];
|
|
||||||
rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr,
|
|
||||||
0, NI_NUMERICHOST);
|
|
||||||
if (rv != 0) {
|
|
||||||
LOG(FATAL) << "Address resolution for " << hostname
|
|
||||||
<< " failed: " << gai_strerror(rv);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
|
||||||
LOG(INFO) << "Address resolution for " << hostname
|
|
||||||
<< " succeeded: " << host;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
|
|
||||||
addr->len = res->ai_addrlen;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int chown_to_running_user(const char *path) {
|
int chown_to_running_user(const char *path) {
|
||||||
return chown(path, get_config()->uid, get_config()->gid);
|
return chown(path, get_config()->uid, get_config()->gid);
|
||||||
|
@ -1075,11 +1029,6 @@ constexpr auto DEFAULT_ACCESSLOG_FORMAT = StringRef::from_lit(
|
||||||
R"("$http_referer" "$http_user_agent")");
|
R"("$http_referer" "$http_user_agent")");
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1";
|
|
||||||
constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80;
|
|
||||||
} // namespace;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void fill_default_config() {
|
void fill_default_config() {
|
||||||
*mod_config() = {};
|
*mod_config() = {};
|
||||||
|
@ -2151,7 +2100,6 @@ void process_options(int argc, char **argv,
|
||||||
|
|
||||||
auto &listenerconf = mod_config()->conn.listener;
|
auto &listenerconf = mod_config()->conn.listener;
|
||||||
auto &upstreamconf = mod_config()->conn.upstream;
|
auto &upstreamconf = mod_config()->conn.upstream;
|
||||||
auto &downstreamconf = mod_config()->conn.downstream;
|
|
||||||
|
|
||||||
if (listenerconf.addrs.empty()) {
|
if (listenerconf.addrs.empty()) {
|
||||||
UpstreamAddr addr{};
|
UpstreamAddr addr{};
|
||||||
|
@ -2185,140 +2133,11 @@ void process_options(int argc, char **argv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &addr_groups = downstreamconf.addr_groups;
|
if (configure_downstream_group(mod_config(), get_config()->http2_proxy, false,
|
||||||
|
tlsconf) != 0) {
|
||||||
if (addr_groups.empty()) {
|
|
||||||
DownstreamAddrConfig addr{};
|
|
||||||
addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST);
|
|
||||||
addr.port = DEFAULT_DOWNSTREAM_PORT;
|
|
||||||
addr.proto = PROTO_HTTP1;
|
|
||||||
|
|
||||||
DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
|
|
||||||
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) {
|
|
||||||
// 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("/"));
|
|
||||||
for (auto &g : addr_groups) {
|
|
||||||
std::move(std::begin(g.addrs), std::end(g.addrs),
|
|
||||||
std::back_inserter(catch_all.addrs));
|
|
||||||
}
|
|
||||||
std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
|
|
||||||
std::vector<WildcardPattern>().swap(mod_config()->wildcard_patterns);
|
|
||||||
// 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 {
|
|
||||||
auto &wildcard_patterns = mod_config()->wildcard_patterns;
|
|
||||||
std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns),
|
|
||||||
[](const WildcardPattern &lhs, const WildcardPattern &rhs) {
|
|
||||||
return std::lexicographical_compare(
|
|
||||||
rhs.host.rbegin(), rhs.host.rend(), lhs.host.rbegin(),
|
|
||||||
lhs.host.rend());
|
|
||||||
});
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
|
||||||
LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, "
|
|
||||||
"and sorted in reverse order):";
|
|
||||||
for (auto &wp : mod_config()->wildcard_patterns) {
|
|
||||||
LOG(INFO) << wp.host;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// backward compatibility: override all SNI fields with the option
|
|
||||||
// value --backend-tls-sni-field
|
|
||||||
if (!tlsconf.backend_sni_name.empty()) {
|
|
||||||
auto &sni = tlsconf.backend_sni_name;
|
|
||||||
for (auto &addr_group : addr_groups) {
|
|
||||||
for (auto &addr : addr_group.addrs) {
|
|
||||||
addr.sni = sni;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
|
||||||
LOG(INFO) << "Resolving backend address";
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t catch_all_group = -1;
|
|
||||||
for (size_t i = 0; i < addr_groups.size(); ++i) {
|
|
||||||
auto &g = addr_groups[i];
|
|
||||||
if (g.pattern == StringRef::from_lit("/")) {
|
|
||||||
catch_all_group = i;
|
|
||||||
}
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
|
||||||
LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
|
|
||||||
<< "'";
|
|
||||||
for (auto &addr : g.addrs) {
|
|
||||||
LOG(INFO) << "group " << i << " -> " << addr.host.c_str()
|
|
||||||
<< (addr.host_unix ? "" : ":" + util::utos(addr.port))
|
|
||||||
<< ", proto=" << strproto(addr.proto)
|
|
||||||
<< (addr.tls ? ", tls" : "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (catch_all_group == -1) {
|
|
||||||
LOG(FATAL) << "backend: No catch-all backend address is configured";
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
downstreamconf.addr_group_catch_all = catch_all_group;
|
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
|
||||||
LOG(INFO) << "Catch-all pattern is group " << catch_all_group;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &g : addr_groups) {
|
|
||||||
for (auto &addr : g.addrs) {
|
|
||||||
|
|
||||||
if (addr.host_unix) {
|
|
||||||
// for AF_UNIX socket, we use "localhost" as host for backend
|
|
||||||
// hostport. This is used as Host header field to backend and
|
|
||||||
// not going to be passed to any syscalls.
|
|
||||||
addr.hostport = "localhost";
|
|
||||||
|
|
||||||
auto path = addr.host.c_str();
|
|
||||||
auto pathlen = addr.host.size();
|
|
||||||
|
|
||||||
if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
|
|
||||||
LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
|
|
||||||
<< sizeof(addr.addr.su.un.sun_path);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LOG_ENABLED(INFO)) {
|
|
||||||
LOG(INFO) << "Use UNIX domain socket path " << path
|
|
||||||
<< " for backend connection";
|
|
||||||
}
|
|
||||||
|
|
||||||
addr.addr.su.un.sun_family = AF_UNIX;
|
|
||||||
// copy path including terminal NULL
|
|
||||||
std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path);
|
|
||||||
addr.addr.len = sizeof(addr.addr.su.un);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr.hostport = ImmutableString(
|
|
||||||
util::make_http_hostport(StringRef(addr.host), 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) {
|
|
||||||
LOG(FATAL) << "Resolving backend address failed: " << hostport;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
LOG(NOTICE) << "Resolved backend address: " << hostport << " -> "
|
|
||||||
<< util::to_numeric_addr(&addr.addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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(StringRef{proxy.host}, proxy.port);
|
auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port);
|
||||||
|
|
|
@ -2904,4 +2904,193 @@ StringRef strproto(shrpx_proto proto) {
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configures the following member in |config|: router,
|
||||||
|
// conn.downstream.addr_groups, wildcard_patterns,
|
||||||
|
int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
|
bool numeric_addr_only,
|
||||||
|
const TLSConfig &tlsconf) {
|
||||||
|
auto &downstreamconf = config->conn.downstream;
|
||||||
|
auto &addr_groups = downstreamconf.addr_groups;
|
||||||
|
|
||||||
|
if (addr_groups.empty()) {
|
||||||
|
DownstreamAddrConfig addr{};
|
||||||
|
addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST);
|
||||||
|
addr.port = DEFAULT_DOWNSTREAM_PORT;
|
||||||
|
addr.proto = PROTO_HTTP1;
|
||||||
|
|
||||||
|
DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
|
||||||
|
g.addrs.push_back(std::move(addr));
|
||||||
|
config->router.add_route(StringRef{g.pattern}, addr_groups.size());
|
||||||
|
addr_groups.push_back(std::move(g));
|
||||||
|
} else if (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("/"));
|
||||||
|
for (auto &g : addr_groups) {
|
||||||
|
std::move(std::begin(g.addrs), std::end(g.addrs),
|
||||||
|
std::back_inserter(catch_all.addrs));
|
||||||
|
}
|
||||||
|
std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
|
||||||
|
std::vector<WildcardPattern>().swap(config->wildcard_patterns);
|
||||||
|
// maybe not necessary?
|
||||||
|
config->router = Router();
|
||||||
|
config->router.add_route(StringRef{catch_all.pattern}, addr_groups.size());
|
||||||
|
addr_groups.push_back(std::move(catch_all));
|
||||||
|
} else {
|
||||||
|
auto &wildcard_patterns = config->wildcard_patterns;
|
||||||
|
std::sort(std::begin(wildcard_patterns), std::end(wildcard_patterns),
|
||||||
|
[](const WildcardPattern &lhs, const WildcardPattern &rhs) {
|
||||||
|
return std::lexicographical_compare(
|
||||||
|
rhs.host.rbegin(), rhs.host.rend(), lhs.host.rbegin(),
|
||||||
|
lhs.host.rend());
|
||||||
|
});
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Reverse sorted wildcard hosts (compared from tail to head, "
|
||||||
|
"and sorted in reverse order):";
|
||||||
|
for (auto &wp : config->wildcard_patterns) {
|
||||||
|
LOG(INFO) << wp.host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// backward compatibility: override all SNI fields with the option
|
||||||
|
// value --backend-tls-sni-field
|
||||||
|
if (!tlsconf.backend_sni_name.empty()) {
|
||||||
|
auto &sni = tlsconf.backend_sni_name;
|
||||||
|
for (auto &addr_group : addr_groups) {
|
||||||
|
for (auto &addr : addr_group.addrs) {
|
||||||
|
addr.sni = sni;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Resolving backend address";
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t catch_all_group = -1;
|
||||||
|
for (size_t i = 0; i < addr_groups.size(); ++i) {
|
||||||
|
auto &g = addr_groups[i];
|
||||||
|
if (g.pattern == StringRef::from_lit("/")) {
|
||||||
|
catch_all_group = i;
|
||||||
|
}
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
|
||||||
|
<< "'";
|
||||||
|
for (auto &addr : g.addrs) {
|
||||||
|
LOG(INFO) << "group " << i << " -> " << addr.host.c_str()
|
||||||
|
<< (addr.host_unix ? "" : ":" + util::utos(addr.port))
|
||||||
|
<< ", proto=" << strproto(addr.proto)
|
||||||
|
<< (addr.tls ? ", tls" : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (catch_all_group == -1) {
|
||||||
|
LOG(FATAL) << "backend: No catch-all backend address is configured";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
downstreamconf.addr_group_catch_all = catch_all_group;
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Catch-all pattern is group " << catch_all_group;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST : 0;
|
||||||
|
|
||||||
|
for (auto &g : addr_groups) {
|
||||||
|
for (auto &addr : g.addrs) {
|
||||||
|
|
||||||
|
if (addr.host_unix) {
|
||||||
|
// for AF_UNIX socket, we use "localhost" as host for backend
|
||||||
|
// hostport. This is used as Host header field to backend and
|
||||||
|
// not going to be passed to any syscalls.
|
||||||
|
addr.hostport = "localhost";
|
||||||
|
|
||||||
|
auto path = addr.host.c_str();
|
||||||
|
auto pathlen = addr.host.size();
|
||||||
|
|
||||||
|
if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
|
||||||
|
LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
|
||||||
|
<< sizeof(addr.addr.su.un.sun_path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Use UNIX domain socket path " << path
|
||||||
|
<< " for backend connection";
|
||||||
|
}
|
||||||
|
|
||||||
|
addr.addr.su.un.sun_family = AF_UNIX;
|
||||||
|
// copy path including terminal NULL
|
||||||
|
std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path);
|
||||||
|
addr.addr.len = sizeof(addr.addr.su.un);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr.hostport = ImmutableString(
|
||||||
|
util::make_http_hostport(StringRef(addr.host), 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, resolve_flags) == -1) {
|
||||||
|
LOG(FATAL) << "Resolving backend address failed: " << hostport;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
LOG(NOTICE) << "Resolved backend address: " << hostport << " -> "
|
||||||
|
<< util::to_numeric_addr(&addr.addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
|
||||||
|
int family, int additional_flags) {
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
auto service = util::utos(port);
|
||||||
|
|
||||||
|
addrinfo hints{};
|
||||||
|
hints.ai_family = family;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_flags |= additional_flags;
|
||||||
|
#ifdef AI_ADDRCONFIG
|
||||||
|
hints.ai_flags |= AI_ADDRCONFIG;
|
||||||
|
#endif // AI_ADDRCONFIG
|
||||||
|
addrinfo *res;
|
||||||
|
|
||||||
|
rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
|
||||||
|
if (rv != 0) {
|
||||||
|
LOG(FATAL) << "Unable to resolve address for " << hostname << ": "
|
||||||
|
<< gai_strerror(rv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res_d = defer(freeaddrinfo, res);
|
||||||
|
|
||||||
|
char host[NI_MAXHOST];
|
||||||
|
rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr,
|
||||||
|
0, NI_NUMERICHOST);
|
||||||
|
if (rv != 0) {
|
||||||
|
LOG(FATAL) << "Address resolution for " << hostname
|
||||||
|
<< " failed: " << gai_strerror(rv);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LOG_ENABLED(INFO)) {
|
||||||
|
LOG(INFO) << "Address resolution for " << hostname
|
||||||
|
<< " succeeded: " << host;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
|
||||||
|
addr->len = res->ai_addrlen;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
|
@ -283,6 +283,9 @@ constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT =
|
||||||
|
|
||||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||||
|
|
||||||
|
constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1";
|
||||||
|
constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80;
|
||||||
|
|
||||||
enum shrpx_proto { PROTO_NONE, PROTO_HTTP1, PROTO_HTTP2, PROTO_MEMCACHED };
|
enum shrpx_proto { PROTO_NONE, PROTO_HTTP1, PROTO_HTTP2, PROTO_MEMCACHED };
|
||||||
|
|
||||||
enum shrpx_forwarded_param {
|
enum shrpx_forwarded_param {
|
||||||
|
@ -713,6 +716,13 @@ read_tls_ticket_key_file(const std::vector<std::string> &files,
|
||||||
// Returns string representation of |proto|.
|
// Returns string representation of |proto|.
|
||||||
StringRef strproto(shrpx_proto proto);
|
StringRef strproto(shrpx_proto proto);
|
||||||
|
|
||||||
|
int configure_downstream_group(Config *config, bool http2_proxy,
|
||||||
|
bool numeric_addr_only,
|
||||||
|
const TLSConfig &tlsconf);
|
||||||
|
|
||||||
|
int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
|
||||||
|
int family, int additional_flags = 0);
|
||||||
|
|
||||||
} // namespace shrpx
|
} // namespace shrpx
|
||||||
|
|
||||||
#endif // SHRPX_CONFIG_H
|
#endif // SHRPX_CONFIG_H
|
||||||
|
|
Loading…
Reference in New Issue