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;
|
||||
};
|
||||
|
||||
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 {
|
||||
int chown_to_running_user(const char *path) {
|
||||
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")");
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
constexpr char DEFAULT_DOWNSTREAM_HOST[] = "127.0.0.1";
|
||||
constexpr int16_t DEFAULT_DOWNSTREAM_PORT = 80;
|
||||
} // namespace;
|
||||
|
||||
namespace {
|
||||
void fill_default_config() {
|
||||
*mod_config() = {};
|
||||
|
@ -2151,7 +2100,6 @@ void process_options(int argc, char **argv,
|
|||
|
||||
auto &listenerconf = mod_config()->conn.listener;
|
||||
auto &upstreamconf = mod_config()->conn.upstream;
|
||||
auto &downstreamconf = mod_config()->conn.downstream;
|
||||
|
||||
if (listenerconf.addrs.empty()) {
|
||||
UpstreamAddr addr{};
|
||||
|
@ -2185,140 +2133,11 @@ void process_options(int argc, char **argv,
|
|||
}
|
||||
}
|
||||
|
||||
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));
|
||||
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";
|
||||
if (configure_downstream_group(mod_config(), get_config()->http2_proxy, false,
|
||||
tlsconf) != 0) {
|
||||
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;
|
||||
if (!proxy.host.empty()) {
|
||||
auto hostport = util::make_hostport(StringRef{proxy.host}, proxy.port);
|
||||
|
|
|
@ -2904,4 +2904,193 @@ StringRef strproto(shrpx_proto proto) {
|
|||
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
|
||||
|
|
|
@ -283,6 +283,9 @@ constexpr auto SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT =
|
|||
|
||||
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_forwarded_param {
|
||||
|
@ -713,6 +716,13 @@ read_tls_ticket_key_file(const std::vector<std::string> &files,
|
|||
// Returns string representation of |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
|
||||
|
||||
#endif // SHRPX_CONFIG_H
|
||||
|
|
Loading…
Reference in New Issue