nghttpx: Support multiple frontend addresses

This commit allows nghttpx to listen to multiple address and port pair
by specifying -f option multiple times.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-01-31 19:41:56 +09:00
parent 1d99b425ca
commit aa07fe7fa6
18 changed files with 515 additions and 327 deletions

View File

@ -92,22 +92,30 @@ using namespace nghttp2;
namespace shrpx { namespace shrpx {
// Environment variables to tell new binary the listening socket's // Deprecated: Environment variables to tell new binary the listening
// file descriptors. They are not close-on-exec. // socket's file descriptors. They are not close-on-exec.
#define ENV_LISTENER4_FD "NGHTTPX_LISTENER4_FD" #define ENV_LISTENER4_FD "NGHTTPX_LISTENER4_FD"
#define ENV_LISTENER6_FD "NGHTTPX_LISTENER6_FD" #define ENV_LISTENER6_FD "NGHTTPX_LISTENER6_FD"
// Environment variable to tell new binary the port number the current // Deprecated: Environment variable to tell new binary the port number
// binary is listening to. // the current binary is listening to.
#define ENV_PORT "NGHTTPX_PORT" #define ENV_PORT "NGHTTPX_PORT"
// Environment variable to tell new binary the listening socket's file // Deprecated: Environment variable to tell new binary the listening
// descriptor if frontend listens UNIX domain socket. // socket's file descriptor if frontend listens UNIX domain socket.
#define ENV_UNIX_FD "NGHTTP2_UNIX_FD" #define ENV_UNIX_FD "NGHTTP2_UNIX_FD"
// Environment variable to tell new binary the UNIX domain socket // Deprecated: Environment variable to tell new binary the UNIX domain
// path. // socket path.
#define ENV_UNIX_PATH "NGHTTP2_UNIX_PATH" #define ENV_UNIX_PATH "NGHTTP2_UNIX_PATH"
// Prefix of environment variables to tell new binary the listening
// socket's file descriptor. They are not close-on-exec. For TCP
// socket, the value must be comma separated 2 parameters: tcp,<FD>.
// <FD> is file descriptor. For UNIX domain socket, the value must be
// comma separated 3 parameters: unix,<FD>,<PATH>. <FD> is file
// descriptor. <PATH> is a path to UNIX domain socket.
constexpr char ENV_ACCEPT_PREFIX[] = "NGHTTPX_ACCEPT_";
#ifndef _KERNEL_FASTOPEN #ifndef _KERNEL_FASTOPEN
#define _KERNEL_FASTOPEN #define _KERNEL_FASTOPEN
// conditional define for TCP_FASTOPEN mostly on ubuntu // conditional define for TCP_FASTOPEN mostly on ubuntu
@ -122,18 +130,8 @@ namespace shrpx {
#endif #endif
struct SignalServer { struct SignalServer {
SignalServer() SignalServer() : ipc_fd{{-1, -1}}, worker_process_pid(-1) {}
: ipc_fd{{-1, -1}},
server_fd(-1),
server_fd6(-1),
worker_process_pid(-1) {}
~SignalServer() { ~SignalServer() {
if (server_fd6 != -1) {
close(server_fd6);
}
if (server_fd != -1) {
close(server_fd);
}
if (ipc_fd[0] != -1) { if (ipc_fd[0] != -1) {
close(ipc_fd[0]); close(ipc_fd[0]);
} }
@ -144,10 +142,6 @@ struct SignalServer {
} }
std::array<int, 2> ipc_fd; std::array<int, 2> ipc_fd;
// server socket, either IPv4 or UNIX domain
int server_fd;
// server socket IPv6
int server_fd6;
pid_t worker_process_pid; pid_t worker_process_pid;
}; };
@ -295,42 +289,35 @@ void exec_binary(SignalServer *ssv) {
size_t envlen = 0; size_t envlen = 0;
for (char **p = environ; *p; ++p, ++envlen) for (char **p = environ; *p; ++p, ++envlen)
; ;
// 3 for missing (fd4, fd6 and port) or (unix fd and unix path)
auto envp = make_unique<char *[]>(envlen + 3 + 1);
size_t envidx = 0;
std::string fd, fd6, path, port;
auto &listenerconf = get_config()->conn.listener; auto &listenerconf = get_config()->conn.listener;
if (listenerconf.host_unix) { auto envp = make_unique<char *[]>(envlen + listenerconf.addrs.size() + 1);
fd = ENV_UNIX_FD "="; size_t envidx = 0;
fd += util::utos(ssv->server_fd);
envp[envidx++] = &fd[0];
path = ENV_UNIX_PATH "="; std::vector<ImmutableString> fd_envs;
path += listenerconf.host.get(); for (size_t i = 0; i < listenerconf.addrs.size(); ++i) {
envp[envidx++] = &path[0]; auto &addr = listenerconf.addrs[i];
} else { std::string s = ENV_ACCEPT_PREFIX;
if (ssv->server_fd) { s += util::utos(i + 1);
fd = ENV_LISTENER4_FD "="; s += '=';
fd += util::utos(ssv->server_fd); if (addr.host_unix) {
envp[envidx++] = &fd[0]; s += "unix,";
s += util::utos(addr.fd);
s += ',';
s += addr.host;
} else {
s += "tcp,";
s += util::utos(addr.fd);
} }
if (ssv->server_fd6) { fd_envs.emplace_back(s);
fd6 = ENV_LISTENER6_FD "="; envp[envidx++] = const_cast<char *>(fd_envs.back().c_str());
fd6 += util::utos(ssv->server_fd6);
envp[envidx++] = &fd6[0];
}
port = ENV_PORT "=";
port += util::utos(listenerconf.port);
envp[envidx++] = &port[0];
} }
for (size_t i = 0; i < envlen; ++i) { for (size_t i = 0; i < envlen; ++i) {
if (util::starts_with(environ[i], ENV_LISTENER4_FD) || if (util::starts_with(environ[i], ENV_ACCEPT_PREFIX) ||
util::starts_with(environ[i], ENV_LISTENER4_FD) ||
util::starts_with(environ[i], ENV_LISTENER6_FD) || util::starts_with(environ[i], ENV_LISTENER6_FD) ||
util::starts_with(environ[i], ENV_PORT) || util::starts_with(environ[i], ENV_PORT) ||
util::starts_with(environ[i], ENV_UNIX_FD) || util::starts_with(environ[i], ENV_UNIX_FD) ||
@ -432,38 +419,45 @@ void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
} }
} // namespace } // namespace
struct InheritedAddr {
// IP address if TCP socket. Otherwise, UNIX domain socket path.
ImmutableString host;
uint16_t port;
// true if UNIX domain socket path
bool host_unix;
int fd;
bool used;
};
namespace { namespace {
int create_unix_domain_server_socket() { int create_unix_domain_server_socket(FrontendAddr &faddr,
auto &listenerconf = get_config()->conn.listener; std::vector<InheritedAddr> &iaddrs) {
auto found = std::find_if(
std::begin(iaddrs), std::end(iaddrs), [&faddr](const InheritedAddr &ia) {
return !ia.used && ia.host_unix && ia.host == faddr.host;
});
auto path = listenerconf.host.get(); if (found != std::end(iaddrs)) {
auto pathlen = strlen(path); LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host;
{ (*found).used = true;
auto envfd = getenv(ENV_UNIX_FD); faddr.fd = (*found).fd;
auto envpath = getenv(ENV_UNIX_PATH); faddr.hostport = "localhost";
if (envfd && envpath) {
auto fd = strtoul(envfd, nullptr, 10);
if (util::streq(envpath, path)) { return 0;
LOG(NOTICE) << "Listening on UNIX domain socket " << path;
return fd;
}
LOG(WARN) << "UNIX domain socket path was changed between old binary ("
<< envpath << ") and new binary (" << path << ")";
close(fd);
}
} }
#ifdef SOCK_NONBLOCK #ifdef SOCK_NONBLOCK
auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (fd == -1) { if (fd == -1) {
auto error = errno;
LOG(WARN) << "socket() syscall failed: " << strerror(error);
return -1; return -1;
} }
#else // !SOCK_NONBLOCK #else // !SOCK_NONBLOCK
auto fd = socket(AF_UNIX, SOCK_STREAM, 0); auto fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd == -1) { if (fd == -1) {
auto error = errno;
LOG(WARN) << "socket() syscall failed: " << strerror(error);
return -1; return -1;
} }
util::make_socket_nonblocking(fd); util::make_socket_nonblocking(fd);
@ -471,115 +465,122 @@ int create_unix_domain_server_socket() {
int val = 1; int val = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
static_cast<socklen_t>(sizeof(val))) == -1) { static_cast<socklen_t>(sizeof(val))) == -1) {
auto error = errno;
LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: "
<< strerror(error);
close(fd); close(fd);
return -1; return -1;
} }
sockaddr_union addr; sockaddr_union addr;
addr.un.sun_family = AF_UNIX; addr.un.sun_family = AF_UNIX;
if (pathlen + 1 > sizeof(addr.un.sun_path)) { if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) {
LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > "
<< sizeof(addr.un.sun_path); << sizeof(addr.un.sun_path);
close(fd); close(fd);
return -1; return -1;
} }
// copy path including terminal NULL // copy path including terminal NULL
std::copy_n(path, pathlen + 1, addr.un.sun_path); std::copy_n(faddr.host.c_str(), faddr.host.size() + 1, addr.un.sun_path);
// unlink (remove) already existing UNIX domain socket path // unlink (remove) already existing UNIX domain socket path
unlink(path); unlink(faddr.host.c_str());
if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) { if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) {
auto error = errno; auto error = errno;
LOG(FATAL) << "Failed to bind UNIX domain socket, error=" << error; LOG(FATAL) << "Failed to bind UNIX domain socket: " << strerror(error);
close(fd); close(fd);
return -1; return -1;
} }
auto &listenerconf = get_config()->conn.listener;
if (listen(fd, listenerconf.backlog) != 0) { if (listen(fd, listenerconf.backlog) != 0) {
auto error = errno; auto error = errno;
LOG(FATAL) << "Failed to listen to UNIX domain socket, error=" << error; LOG(FATAL) << "Failed to listen to UNIX domain socket: " << strerror(error);
close(fd); close(fd);
return -1; return -1;
} }
LOG(NOTICE) << "Listening on UNIX domain socket " << path; LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host;
return fd; faddr.fd = fd;
faddr.hostport = "localhost";
return 0;
} }
} // namespace } // namespace
namespace { namespace {
int create_tcp_server_socket(int family) { int create_tcp_server_socket(FrontendAddr &faddr,
auto &listenerconf = get_config()->conn.listener; std::vector<InheritedAddr> &iaddrs) {
{
auto envfd =
getenv(family == AF_INET ? ENV_LISTENER4_FD : ENV_LISTENER6_FD);
auto envport = getenv(ENV_PORT);
if (envfd && envport) {
auto fd = strtoul(envfd, nullptr, 10);
auto port = strtoul(envport, nullptr, 10);
// Only do this iff NGHTTPX_PORT == get_config()->port.
// Otherwise, close fd, and create server socket as usual.
if (port == listenerconf.port) {
LOG(NOTICE) << "Listening on port " << listenerconf.port;
return fd;
}
LOG(WARN) << "Port was changed between old binary (" << port
<< ") and new binary (" << listenerconf.port << ")";
close(fd);
}
}
int fd = -1; int fd = -1;
int rv; int rv;
auto service = util::utos(listenerconf.port); auto &listenerconf = get_config()->conn.listener;
auto service = util::utos(faddr.port);
addrinfo hints{}; addrinfo hints{};
hints.ai_family = family; hints.ai_family = faddr.family;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
#ifdef AI_ADDRCONFIG #ifdef AI_ADDRCONFIG
hints.ai_flags |= AI_ADDRCONFIG; hints.ai_flags |= AI_ADDRCONFIG;
#endif // AI_ADDRCONFIG #endif // AI_ADDRCONFIG
auto node = strcmp("*", listenerconf.host.get()) == 0 auto node = faddr.host == "*" ? nullptr : faddr.host.c_str();
? nullptr
: listenerconf.host.get();
addrinfo *res, *rp; addrinfo *res, *rp;
rv = getaddrinfo(node, service.c_str(), &hints, &res); rv = getaddrinfo(node, service.c_str(), &hints, &res);
if (rv != 0) { if (rv != 0) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6") LOG(INFO) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6")
<< " address for " << listenerconf.host.get() << ": " << " address for " << faddr.host << ", port " << faddr.port
<< gai_strerror(rv); << ": " << gai_strerror(rv);
} }
return -1; return -1;
} }
auto res_d = defer(freeaddrinfo, res); auto res_d = defer(freeaddrinfo, res);
std::array<char, NI_MAXHOST> host;
for (rp = res; rp; rp = rp->ai_next) { for (rp = res; rp; rp = rp->ai_next) {
rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host.data(), host.size(),
nullptr, 0, NI_NUMERICHOST);
if (rv != 0) {
LOG(WARN) << "getnameinfo() failed: " << gai_strerror(rv);
continue;
}
auto found = std::find_if(std::begin(iaddrs), std::end(iaddrs),
[&host, &faddr](const InheritedAddr &ia) {
return !ia.used && !ia.host_unix &&
ia.host == host.data() &&
ia.port == faddr.port;
});
if (found != std::end(iaddrs)) {
(*found).used = true;
fd = (*found).fd;
break;
}
#ifdef SOCK_NONBLOCK #ifdef SOCK_NONBLOCK
fd = fd =
socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol); socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
if (fd == -1) { if (fd == -1) {
auto error = errno; auto error = errno;
LOG(WARN) << "socket() syscall failed, error=" << error; LOG(WARN) << "socket() syscall failed: " << strerror(error);
continue; continue;
} }
#else // !SOCK_NONBLOCK #else // !SOCK_NONBLOCK
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (fd == -1) { if (fd == -1) {
auto error = errno; auto error = errno;
LOG(WARN) << "socket() syscall failed, error=" << error; LOG(WARN) << "socket() syscall failed: " << strerror(error);
continue; continue;
} }
util::make_socket_nonblocking(fd); util::make_socket_nonblocking(fd);
@ -588,21 +589,19 @@ int create_tcp_server_socket(int family) {
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
static_cast<socklen_t>(sizeof(val))) == -1) { static_cast<socklen_t>(sizeof(val))) == -1) {
auto error = errno; auto error = errno;
LOG(WARN) LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: "
<< "Failed to set SO_REUSEADDR option to listener socket, error=" << strerror(error);
<< error;
close(fd); close(fd);
continue; continue;
} }
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
if (family == AF_INET6) { if (faddr.family == AF_INET6) {
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
static_cast<socklen_t>(sizeof(val))) == -1) { static_cast<socklen_t>(sizeof(val))) == -1) {
auto error = errno; auto error = errno;
LOG(WARN) LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: "
<< "Failed to set IPV6_V6ONLY option to listener socket, error=" << strerror(error);
<< error;
close(fd); close(fd);
continue; continue;
} }
@ -613,7 +612,9 @@ int create_tcp_server_socket(int family) {
val = 3; val = 3;
if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val, if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val,
static_cast<socklen_t>(sizeof(val))) == -1) { static_cast<socklen_t>(sizeof(val))) == -1) {
LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket"; auto error = errno;
LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket: "
<< strerror(error);
} }
#endif // TCP_DEFER_ACCEPT #endif // TCP_DEFER_ACCEPT
@ -622,7 +623,7 @@ int create_tcp_server_socket(int family) {
// ports will fail with permission denied error. // ports will fail with permission denied error.
if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) { if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
auto error = errno; auto error = errno;
LOG(WARN) << "bind() syscall failed, error=" << error; LOG(WARN) << "bind() syscall failed: " << strerror(error);
close(fd); close(fd);
continue; continue;
} }
@ -631,13 +632,15 @@ int create_tcp_server_socket(int family) {
val = listenerconf.fastopen; val = listenerconf.fastopen;
if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &val, if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &val,
static_cast<socklen_t>(sizeof(val))) == -1) { static_cast<socklen_t>(sizeof(val))) == -1) {
LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket"; auto error = errno;
LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket: "
<< strerror(error);
} }
} }
if (listen(fd, listenerconf.backlog) == -1) { if (listen(fd, listenerconf.backlog) == -1) {
auto error = errno; auto error = errno;
LOG(WARN) << "listen() syscall failed, error=" << error; LOG(WARN) << "listen() syscall failed: " << strerror(error);
close(fd); close(fd);
continue; continue;
} }
@ -646,27 +649,200 @@ int create_tcp_server_socket(int family) {
} }
if (!rp) { if (!rp) {
LOG(WARN) << "Listening " << (family == AF_INET ? "IPv4" : "IPv6") LOG(WARN) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6")
<< " socket failed"; << " socket failed";
return -1; return -1;
} }
char host[NI_MAXHOST]; faddr.fd = fd;
rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), nullptr, 0, faddr.hostport = util::make_hostport(host.data(), faddr.port);
NI_NUMERICHOST);
if (rv != 0) { LOG(NOTICE) << "Listening on " << faddr.hostport;
LOG(WARN) << gai_strerror(rv);
close(fd); return 0;
}
} // namespace
return -1; namespace {
int create_acceptor_socket() {
int rv;
auto &listenerconf = mod_config()->conn.listener;
std::vector<InheritedAddr> iaddrs;
{
// Upgrade from 1.7.0 or earlier
auto portenv = getenv(ENV_PORT);
if (portenv) {
size_t i = 1;
for (auto env_name : {ENV_LISTENER4_FD, ENV_LISTENER6_FD}) {
auto fdenv = getenv(env_name);
if (fdenv) {
std::string name = ENV_ACCEPT_PREFIX;
name += util::utos(i);
std::string value = "tcp,";
value += fdenv;
setenv(name.c_str(), value.c_str(), 0);
++i;
}
}
} else {
auto pathenv = getenv(ENV_UNIX_PATH);
auto fdenv = getenv(ENV_UNIX_FD);
if (pathenv && fdenv) {
std::string name = ENV_ACCEPT_PREFIX;
name += '1';
std::string value = "unix,";
value += fdenv;
value += ',';
value += pathenv;
setenv(name.c_str(), value.c_str(), 0);
}
}
} }
LOG(NOTICE) << "Listening on " << host << ", port " << listenerconf.port; for (size_t i = 1;; ++i) {
std::string name = ENV_ACCEPT_PREFIX;
name += util::utos(i);
auto env = getenv(name.c_str());
if (!env) {
break;
}
return fd; if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Read env " << name << "=" << env;
}
auto end_type = strchr(env, ',');
if (!end_type) {
continue;
}
auto type = StringRef(env, end_type);
auto value = end_type + 1;
if (type == "unix") {
auto endfd = strchr(value, ',');
if (!endfd) {
continue;
}
auto fd = util::parse_uint(reinterpret_cast<const uint8_t *>(value),
endfd - value);
if (fd == -1) {
LOG(WARN) << "Could not parse file descriptor from "
<< std::string(value, endfd - value);
continue;
}
auto path = endfd + 1;
if (strlen(path) == 0) {
LOG(WARN) << "Empty UNIX domain socket path (fd=" << fd << ")";
close(fd);
continue;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Inherit UNIX domain socket fd=" << fd
<< ", path=" << path;
}
InheritedAddr addr{};
addr.host = path;
addr.host_unix = true;
addr.fd = static_cast<int>(fd);
iaddrs.push_back(std::move(addr));
}
if (type == "tcp") {
auto fd = util::parse_uint(value);
if (fd == -1) {
LOG(WARN) << "Could not parse file descriptor from " << value;
continue;
}
sockaddr_union su;
socklen_t salen = sizeof(su);
if (getsockname(fd, &su.sa, &salen) != 0) {
auto error = errno;
LOG(WARN) << "getsockname() syscall failed (fd=" << fd
<< "): " << strerror(error);
close(fd);
continue;
}
uint16_t port;
switch (su.storage.ss_family) {
case AF_INET:
port = ntohs(su.in.sin_port);
break;
case AF_INET6:
port = ntohs(su.in6.sin6_port);
break;
default:
close(fd);
continue;
}
std::array<char, NI_MAXHOST> host;
rv = getnameinfo(&su.sa, salen, host.data(), host.size(), nullptr, 0,
NI_NUMERICHOST);
if (rv != 0) {
LOG(WARN) << "getnameinfo() failed (fd=" << fd
<< "): " << gai_strerror(rv);
close(fd);
continue;
}
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Inherit TCP socket fd=" << fd
<< ", address=" << host.data() << ", port=" << port;
}
InheritedAddr addr{};
addr.host = host.data();
addr.port = static_cast<uint16_t>(port);
addr.fd = static_cast<int>(fd);
iaddrs.push_back(std::move(addr));
continue;
}
}
for (auto &addr : listenerconf.addrs) {
if (addr.host_unix) {
if (create_unix_domain_server_socket(addr, iaddrs) != 0) {
return -1;
}
if (get_config()->uid != 0) {
// fd is not associated to inode, so we cannot use fchown(2)
// here. https://lkml.org/lkml/2004/11/1/84
if (chown_to_running_user(addr.host.c_str()) == -1) {
auto error = errno;
LOG(WARN) << "Changing owner of UNIX domain socket " << addr.host
<< " failed: " << strerror(error);
}
}
continue;
}
if (create_tcp_server_socket(addr, iaddrs) != 0) {
return -1;
}
}
for (auto &ia : iaddrs) {
if (ia.used) {
continue;
}
close(ia.fd);
}
return 0;
} }
} // namespace } // namespace
@ -680,19 +856,6 @@ int call_daemon() {
} }
} // namespace } // namespace
namespace {
void close_env_fd(std::initializer_list<const char *> envnames) {
for (auto envname : envnames) {
auto envfd = getenv(envname);
if (!envfd) {
continue;
}
auto fd = strtol(envfd, nullptr, 10);
close(fd);
}
}
} // namespace
namespace { namespace {
pid_t fork_worker_process(SignalServer *ssv) { pid_t fork_worker_process(SignalServer *ssv) {
int rv; int rv;
@ -722,7 +885,7 @@ pid_t fork_worker_process(SignalServer *ssv) {
} }
close(ssv->ipc_fd[1]); close(ssv->ipc_fd[1]);
WorkerProcessConfig wpconf{ssv->ipc_fd[0], ssv->server_fd, ssv->server_fd6}; WorkerProcessConfig wpconf{ssv->ipc_fd[0]};
rv = worker_process_event_loop(&wpconf); rv = worker_process_event_loop(&wpconf);
if (rv != 0) { if (rv != 0) {
LOG(FATAL) << "Worker process returned error"; LOG(FATAL) << "Worker process returned error";
@ -803,40 +966,8 @@ int event_loop() {
util::make_socket_closeonexec(fd); util::make_socket_closeonexec(fd);
} }
auto &listenerconf = get_config()->conn.listener; if (create_acceptor_socket() != 0) {
return -1;
if (listenerconf.host_unix) {
close_env_fd({ENV_LISTENER4_FD, ENV_LISTENER6_FD});
auto fd = create_unix_domain_server_socket();
if (fd == -1) {
LOG(FATAL) << "Failed to listen on UNIX domain socket "
<< listenerconf.host.get();
return -1;
}
ssv.server_fd = fd;
if (get_config()->uid != 0) {
// fd is not associated to inode, so we cannot use fchown(2)
// here. https://lkml.org/lkml/2004/11/1/84
if (chown_to_running_user(listenerconf.host.get()) == -1) {
auto error = errno;
LOG(WARN) << "Changing owner of UNIX domain socket "
<< listenerconf.host.get() << " failed: " << strerror(error);
}
}
} else {
close_env_fd({ENV_UNIX_FD});
auto fd6 = create_tcp_server_socket(AF_INET6);
auto fd4 = create_tcp_server_socket(AF_INET);
if (fd6 == -1 && fd4 == -1) {
LOG(FATAL) << "Failed to listen on address " << listenerconf.host.get()
<< ", port " << listenerconf.port;
return -1;
}
ssv.server_fd = fd4;
ssv.server_fd6 = fd6;
} }
auto loop = EV_DEFAULT; auto loop = EV_DEFAULT;
@ -986,8 +1117,6 @@ void fill_default_config() {
{ {
auto &listenerconf = connconf.listener; auto &listenerconf = connconf.listener;
{ {
listenerconf.host = strcopy("*");
listenerconf.port = 3000;
// Default accept() backlog // Default accept() backlog
listenerconf.backlog = 512; listenerconf.backlog = 512;
listenerconf.timeout.sleep = 30_s; listenerconf.timeout.sleep = 30_s;
@ -1120,9 +1249,10 @@ Connections:
Set frontend host and port. If <HOST> is '*', it Set frontend host and port. If <HOST> is '*', it
assumes all addresses including both IPv4 and IPv6. assumes all addresses including both IPv4 and IPv6.
UNIX domain socket can be specified by prefixing path UNIX domain socket can be specified by prefixing path
name with "unix:" (e.g., unix:/var/run/nghttpx.sock) name with "unix:" (e.g., unix:/var/run/nghttpx.sock).
Default: )" << get_config()->conn.listener.host.get() << "," This option can be used multiple times to listen to
<< get_config()->conn.listener.port << R"( multiple addresses.
Default: *,3000
--backlog=<N> --backlog=<N>
Set listen backlog size. Set listen backlog size.
Default: )" << get_config()->conn.listener.backlog << R"( Default: )" << get_config()->conn.listener.backlog << R"(
@ -1838,6 +1968,13 @@ void process_options(
auto &upstreamconf = mod_config()->conn.upstream; auto &upstreamconf = mod_config()->conn.upstream;
auto &downstreamconf = mod_config()->conn.downstream; auto &downstreamconf = mod_config()->conn.downstream;
if (listenerconf.addrs.empty()) {
FrontendAddr addr;
addr.host = "*";
addr.port = 3000;
listenerconf.addrs.push_back(std::move(addr));
}
if (downstreamconf.ipv4 && downstreamconf.ipv6) { if (downstreamconf.ipv4 && downstreamconf.ipv6) {
LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the "
<< "same time."; << "same time.";
@ -1949,8 +2086,7 @@ void process_options(
// for AF_UNIX socket, we use "localhost" as host for backend // for AF_UNIX socket, we use "localhost" as host for backend
// hostport. This is used as Host header field to backend and // hostport. This is used as Host header field to backend and
// not going to be passed to any syscalls. // not going to be passed to any syscalls.
addr.hostport = ImmutableString( addr.hostport = "localhost";
util::make_hostport("localhost", listenerconf.port));
auto path = addr.host.c_str(); auto path = addr.host.c_str();
auto pathlen = addr.host.size(); auto pathlen = addr.host.size();

View File

@ -45,16 +45,16 @@ void acceptcb(struct ev_loop *loop, ev_io *w, int revent) {
} }
} // namespace } // namespace
AcceptHandler::AcceptHandler(int fd, ConnectionHandler *h) AcceptHandler::AcceptHandler(const FrontendAddr *faddr, ConnectionHandler *h)
: conn_hnr_(h), fd_(fd) { : conn_hnr_(h), faddr_(faddr) {
ev_io_init(&wev_, acceptcb, fd_, EV_READ); ev_io_init(&wev_, acceptcb, faddr_->fd, EV_READ);
wev_.data = this; wev_.data = this;
ev_io_start(conn_hnr_->get_loop(), &wev_); ev_io_start(conn_hnr_->get_loop(), &wev_);
} }
AcceptHandler::~AcceptHandler() { AcceptHandler::~AcceptHandler() {
ev_io_stop(conn_hnr_->get_loop(), &wev_); ev_io_stop(conn_hnr_->get_loop(), &wev_);
close(fd_); close(faddr_->fd);
} }
void AcceptHandler::accept_connection() { void AcceptHandler::accept_connection() {
@ -63,10 +63,10 @@ void AcceptHandler::accept_connection() {
socklen_t addrlen = sizeof(sockaddr); socklen_t addrlen = sizeof(sockaddr);
#ifdef HAVE_ACCEPT4 #ifdef HAVE_ACCEPT4
auto cfd = auto cfd = accept4(faddr_->fd, &sockaddr.sa, &addrlen,
accept4(fd_, &sockaddr.sa, &addrlen, SOCK_NONBLOCK | SOCK_CLOEXEC); SOCK_NONBLOCK | SOCK_CLOEXEC);
#else // !HAVE_ACCEPT4 #else // !HAVE_ACCEPT4
auto cfd = accept(fd_, &sockaddr.sa, &addrlen); auto cfd = accept(faddr_->fd, &sockaddr.sa, &addrlen);
#endif // !HAVE_ACCEPT4 #endif // !HAVE_ACCEPT4
if (cfd == -1) { if (cfd == -1) {
@ -101,7 +101,7 @@ void AcceptHandler::accept_connection() {
util::make_socket_nodelay(cfd); util::make_socket_nodelay(cfd);
conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen); conn_hnr_->handle_connection(cfd, &sockaddr.sa, addrlen, faddr_);
} }
} }
@ -109,6 +109,6 @@ void AcceptHandler::enable() { ev_io_start(conn_hnr_->get_loop(), &wev_); }
void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); } void AcceptHandler::disable() { ev_io_stop(conn_hnr_->get_loop(), &wev_); }
int AcceptHandler::get_fd() const { return fd_; } int AcceptHandler::get_fd() const { return faddr_->fd; }
} // namespace shrpx } // namespace shrpx

View File

@ -32,10 +32,11 @@
namespace shrpx { namespace shrpx {
class ConnectionHandler; class ConnectionHandler;
struct FrontendAddr;
class AcceptHandler { class AcceptHandler {
public: public:
AcceptHandler(int fd, ConnectionHandler *h); AcceptHandler(const FrontendAddr *faddr, ConnectionHandler *h);
~AcceptHandler(); ~AcceptHandler();
void accept_connection(); void accept_connection();
void enable(); void enable();
@ -45,7 +46,7 @@ public:
private: private:
ev_io wev_; ev_io wev_;
ConnectionHandler *conn_hnr_; ConnectionHandler *conn_hnr_;
int fd_; const FrontendAddr *faddr_;
}; };
} // namespace shrpx } // namespace shrpx

View File

@ -377,7 +377,8 @@ int ClientHandler::upstream_http1_connhd_read() {
} }
ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
const char *ipaddr, const char *port) const char *ipaddr, const char *port,
const FrontendAddr *faddr)
: conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(), : conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
get_config()->conn.upstream.timeout.write, get_config()->conn.upstream.timeout.write,
get_config()->conn.upstream.timeout.read, get_config()->conn.upstream.timeout.read,
@ -392,6 +393,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
: nullptr), : nullptr),
ipaddr_(ipaddr), ipaddr_(ipaddr),
port_(port), port_(port),
faddr_(faddr),
worker_(worker), worker_(worker),
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
should_close_after_write_(false) { should_close_after_write_(false) {
@ -851,8 +853,8 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
std::chrono::high_resolution_clock::now(), // request_end_time std::chrono::high_resolution_clock::now(), // request_end_time
req.http_major, req.http_minor, resp.http_status, req.http_major, req.http_minor, resp.http_status,
downstream->response_sent_body_length, StringRef(port_), downstream->response_sent_body_length, StringRef(port_), faddr_->port,
get_config()->conn.listener.port, get_config()->pid, get_config()->pid,
}); });
} }
@ -875,7 +877,7 @@ void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
highres_now, // request_end_time highres_now, // request_end_time
major, minor, // major, minor major, minor, // major, minor
status, body_bytes_sent, StringRef(port_), status, body_bytes_sent, StringRef(port_),
get_config()->conn.listener.port, get_config()->pid, faddr_->port, get_config()->pid,
}); });
} }
@ -1116,51 +1118,14 @@ int ClientHandler::proxy_protocol_read() {
return on_proxy_protocol_finish(); return on_proxy_protocol_finish();
} }
const std::string &ClientHandler::get_forwarded_by() { StringRef ClientHandler::get_forwarded_by() {
auto &fwdconf = get_config()->http.forwarded; auto &fwdconf = get_config()->http.forwarded;
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) { if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) {
return fwdconf.by_obfuscated; return StringRef(fwdconf.by_obfuscated);
}
if (!local_hostport_.empty()) {
return local_hostport_;
} }
auto &listenerconf = get_config()->conn.listener; return StringRef(faddr_->hostport);
// For UNIX domain socket listener, just return empty string.
if (listenerconf.host_unix) {
return local_hostport_;
}
int rv;
sockaddr_union su;
socklen_t addrlen = sizeof(su);
rv = getsockname(conn_.fd, &su.sa, &addrlen);
if (rv != 0) {
return local_hostport_;
}
char host[NI_MAXHOST];
rv = getnameinfo(&su.sa, addrlen, host, sizeof(host), nullptr, 0,
NI_NUMERICHOST);
if (rv != 0) {
return local_hostport_;
}
if (su.storage.ss_family == AF_INET6) {
local_hostport_ = "[";
local_hostport_ += host;
local_hostport_ += "]:";
} else {
local_hostport_ = host;
local_hostport_ += ':';
}
local_hostport_ += util::utos(listenerconf.port);
return local_hostport_;
} }
const std::string &ClientHandler::get_forwarded_for() const { const std::string &ClientHandler::get_forwarded_for() const {
@ -1168,10 +1133,6 @@ const std::string &ClientHandler::get_forwarded_for() const {
return forwarded_for_obfuscated_; return forwarded_for_obfuscated_;
} }
if (get_config()->conn.listener.host_unix) {
return EMPTY_STRING;
}
return ipaddr_; return ipaddr_;
} }

View File

@ -53,7 +53,7 @@ struct WorkerStat;
class ClientHandler { class ClientHandler {
public: public:
ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr, ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr,
const char *port); const char *port, const FrontendAddr *faddr);
~ClientHandler(); ~ClientHandler();
int noop(); int noop();
@ -136,7 +136,7 @@ public:
// Returns string suitable for use in "by" parameter of Forwarded // Returns string suitable for use in "by" parameter of Forwarded
// header field. // header field.
const std::string &get_forwarded_by(); StringRef get_forwarded_by();
// Returns string suitable for use in "for" parameter of Forwarded // Returns string suitable for use in "for" parameter of Forwarded
// header field. // header field.
const std::string &get_forwarded_for() const; const std::string &get_forwarded_for() const;
@ -152,13 +152,13 @@ private:
std::string port_; std::string port_;
// The ALPN identifier negotiated for this connection. // The ALPN identifier negotiated for this connection.
std::string alpn_; std::string alpn_;
// Host and port of this socket (e.g., "[::1]:8443")
std::string local_hostport_;
// The obfuscated version of client address used in "for" parameter // The obfuscated version of client address used in "for" parameter
// of Forwarded header field. // of Forwarded header field.
std::string forwarded_for_obfuscated_; std::string forwarded_for_obfuscated_;
std::function<int(ClientHandler &)> read_, write_; std::function<int(ClientHandler &)> read_, write_;
std::function<int(ClientHandler &)> on_read_, on_write_; std::function<int(ClientHandler &)> on_read_, on_write_;
// Address of frontend listening socket
const FrontendAddr *faddr_;
Worker *worker_; Worker *worker_;
// The number of bytes of HTTP/2 client connection header to read // The number of bytes of HTTP/2 client connection header to read
size_t left_connhd_len_; size_t left_connhd_len_;

View File

@ -1428,11 +1428,15 @@ int parse_config(const char *opt, const char *optarg,
case SHRPX_OPTID_FRONTEND: { case SHRPX_OPTID_FRONTEND: {
auto &listenerconf = mod_config()->conn.listener; auto &listenerconf = mod_config()->conn.listener;
FrontendAddr addr{};
addr.fd = -1;
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
listenerconf.host = strcopy(path); addr.host = ImmutableString(path);
listenerconf.port = 0; addr.host_unix = true;
listenerconf.host_unix = true;
listenerconf.addrs.push_back(std::move(addr));
return 0; return 0;
} }
@ -1442,9 +1446,26 @@ int parse_config(const char *opt, const char *optarg,
return -1; return -1;
} }
listenerconf.host = strcopy(host); addr.host = ImmutableString(host);
listenerconf.port = port; addr.port = port;
listenerconf.host_unix = false;
if (util::numeric_host(host, AF_INET)) {
addr.family = AF_INET;
listenerconf.addrs.push_back(std::move(addr));
return 0;
}
if (util::numeric_host(host, AF_INET6)) {
addr.family = AF_INET6;
listenerconf.addrs.push_back(std::move(addr));
return 0;
}
addr.family = AF_INET;
listenerconf.addrs.push_back(addr);
addr.family = AF_INET6;
listenerconf.addrs.push_back(std::move(addr));
return 0; return 0;
} }

View File

@ -239,6 +239,24 @@ struct AltSvc {
uint16_t port; uint16_t port;
}; };
struct FrontendAddr {
// The frontend address (e.g., FQDN, hostname, IP address). If
// |host_unix| is true, this is UNIX domain socket path.
ImmutableString host;
// For TCP socket, this is <IP address>:<PORT>. For IPv6 address,
// address is surrounded by square brackets. If socket is UNIX
// domain socket, this is "localhost".
ImmutableString hostport;
// frontend port. 0 if |host_unix| is true.
uint16_t port;
// For TCP socket, this is either AF_INET or AF_INET6. For UNIX
// domain socket, this is 0.
int family;
// true if |host| contains UNIX domain socket path.
bool host_unix;
int fd;
};
struct DownstreamAddr { struct DownstreamAddr {
DownstreamAddr() : addr{}, port(0), host_unix(false) {} DownstreamAddr() : addr{}, port(0), host_unix(false) {}
DownstreamAddr(const DownstreamAddr &other); DownstreamAddr(const DownstreamAddr &other);
@ -464,14 +482,8 @@ struct ConnectionConfig {
struct { struct {
ev_tstamp sleep; ev_tstamp sleep;
} timeout; } timeout;
// address of frontend connection. This could be a path to UNIX // address of frontend acceptors
// domain socket. In this case, |host_unix| must be true. std::vector<FrontendAddr> addrs;
std::unique_ptr<char[]> host;
// frontend listening port. 0 if frontend listens on UNIX domain
// socket, in this case |host_unix| must be true.
uint16_t port;
// true if host contains UNIX domain socket path
bool host_unix;
int backlog; int backlog;
// TCP fastopen. If this is positive, it is passed to // TCP fastopen. If this is positive, it is passed to
// setsockopt() along with TCP_FASTOPEN. // setsockopt() along with TCP_FASTOPEN.

View File

@ -297,7 +297,8 @@ void ConnectionHandler::graceful_shutdown_worker() {
#endif // NOTHREADS #endif // NOTHREADS
} }
int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) { int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen,
const FrontendAddr *faddr) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LLOG(INFO, this) << "Accepted connection. fd=" << fd; LLOG(INFO, this) << "Accepted connection. fd=" << fd;
} }
@ -317,7 +318,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) {
} }
auto client = auto client =
ssl::accept_connection(single_worker_.get(), fd, addr, addrlen); ssl::accept_connection(single_worker_.get(), fd, addr, addrlen, faddr);
if (!client) { if (!client) {
LLOG(ERROR, this) << "ClientHandler creation failed"; LLOG(ERROR, this) << "ClientHandler creation failed";
@ -338,6 +339,7 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) {
wev.client_fd = fd; wev.client_fd = fd;
memcpy(&wev.client_addr, addr, addrlen); memcpy(&wev.client_addr, addr, addrlen);
wev.client_addrlen = addrlen; wev.client_addrlen = addrlen;
wev.faddr = faddr;
workers_[idx]->send(wev); workers_[idx]->send(wev);
@ -352,39 +354,19 @@ Worker *ConnectionHandler::get_single_worker() const {
return single_worker_.get(); return single_worker_.get();
} }
void ConnectionHandler::set_acceptor(std::unique_ptr<AcceptHandler> h) { void ConnectionHandler::add_acceptor(std::unique_ptr<AcceptHandler> h) {
acceptor_ = std::move(h); acceptors_.push_back(std::move(h));
}
AcceptHandler *ConnectionHandler::get_acceptor() const {
return acceptor_.get();
}
void ConnectionHandler::set_acceptor6(std::unique_ptr<AcceptHandler> h) {
acceptor6_ = std::move(h);
}
AcceptHandler *ConnectionHandler::get_acceptor6() const {
return acceptor6_.get();
} }
void ConnectionHandler::enable_acceptor() { void ConnectionHandler::enable_acceptor() {
if (acceptor_) { for (auto &a : acceptors_) {
acceptor_->enable(); a->enable();
}
if (acceptor6_) {
acceptor6_->enable();
} }
} }
void ConnectionHandler::disable_acceptor() { void ConnectionHandler::disable_acceptor() {
if (acceptor_) { for (auto &a : acceptors_) {
acceptor_->disable(); a->disable();
}
if (acceptor6_) {
acceptor6_->disable();
} }
} }
@ -400,11 +382,8 @@ void ConnectionHandler::sleep_acceptor(ev_tstamp t) {
} }
void ConnectionHandler::accept_pending_connection() { void ConnectionHandler::accept_pending_connection() {
if (acceptor_) { for (auto &a : acceptors_) {
acceptor_->accept_connection(); a->accept_connection();
}
if (acceptor6_) {
acceptor6_->accept_connection();
} }
} }

View File

@ -58,6 +58,7 @@ class Worker;
struct WorkerStat; struct WorkerStat;
struct TicketKeys; struct TicketKeys;
class MemcachedDispatcher; class MemcachedDispatcher;
struct FrontendAddr;
struct OCSPUpdateContext { struct OCSPUpdateContext {
// ocsp response buffer // ocsp response buffer
@ -79,7 +80,8 @@ class ConnectionHandler {
public: public:
ConnectionHandler(struct ev_loop *loop); ConnectionHandler(struct ev_loop *loop);
~ConnectionHandler(); ~ConnectionHandler();
int handle_connection(int fd, sockaddr *addr, int addrlen); int handle_connection(int fd, sockaddr *addr, int addrlen,
const FrontendAddr *faddr);
// Creates Worker object for single threaded configuration. // Creates Worker object for single threaded configuration.
int create_single_worker(); int create_single_worker();
// Creates |num| Worker objects for multi threaded configuration. // Creates |num| Worker objects for multi threaded configuration.
@ -92,10 +94,7 @@ public:
const std::shared_ptr<TicketKeys> &get_ticket_keys() const; const std::shared_ptr<TicketKeys> &get_ticket_keys() const;
struct ev_loop *get_loop() const; struct ev_loop *get_loop() const;
Worker *get_single_worker() const; Worker *get_single_worker() const;
void set_acceptor(std::unique_ptr<AcceptHandler> h); void add_acceptor(std::unique_ptr<AcceptHandler> h);
AcceptHandler *get_acceptor() const;
void set_acceptor6(std::unique_ptr<AcceptHandler> h);
AcceptHandler *get_acceptor6() const;
void enable_acceptor(); void enable_acceptor();
void disable_acceptor(); void disable_acceptor();
void sleep_acceptor(ev_tstamp t); void sleep_acceptor(ev_tstamp t);
@ -154,10 +153,7 @@ private:
// Worker object. // Worker object.
std::shared_ptr<TicketKeys> ticket_keys_; std::shared_ptr<TicketKeys> ticket_keys_;
struct ev_loop *loop_; struct ev_loop *loop_;
// acceptor for IPv4 address or UNIX domain socket. std::vector<std::unique_ptr<AcceptHandler>> acceptors_;
std::unique_ptr<AcceptHandler> acceptor_;
// acceptor for IPv6 address
std::unique_ptr<AcceptHandler> acceptor6_;
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
std::unique_ptr<neverbleed_t> nb_; std::unique_ptr<neverbleed_t> nb_;
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED

View File

@ -46,8 +46,6 @@ std::string create_error_html(unsigned int status_code) {
res += "</h1><footer>"; res += "</h1><footer>";
const auto &server_name = get_config()->http.server_name; const auto &server_name = get_config()->http.server_name;
res.append(server_name.c_str(), server_name.size()); res.append(server_name.c_str(), server_name.size());
res += " at port ";
res += util::utos(get_config()->conn.listener.port);
res += "</footer></body></html>"; res += "</footer></body></html>";
return res; return res;
} }
@ -63,7 +61,7 @@ std::string create_via_header_value(int major, int minor) {
return hdrs; return hdrs;
} }
std::string create_forwarded(int params, const std::string &node_by, std::string create_forwarded(int params, const StringRef &node_by,
const std::string &node_for, const std::string &node_for,
const std::string &host, const std::string &host,
const std::string &proto) { const std::string &proto) {

View File

@ -42,7 +42,7 @@ std::string create_via_header_value(int major, int minor);
// Returns generated RFC 7239 Forwarded header field value. The // Returns generated RFC 7239 Forwarded header field value. The
// |params| is bitwise-OR of zero or more of shrpx_forwarded_param // |params| is bitwise-OR of zero or more of shrpx_forwarded_param
// defined in shrpx_config.h. // defined in shrpx_config.h.
std::string create_forwarded(int params, const std::string &node_by, std::string create_forwarded(int params, const StringRef &node_by,
const std::string &node_for, const std::string &node_for,
const std::string &host, const std::string &proto); const std::string &host, const std::string &proto);

View File

@ -747,7 +747,7 @@ SSL *create_ssl(SSL_CTX *ssl_ctx) {
} }
ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
int addrlen) { int addrlen, const FrontendAddr *faddr) {
char host[NI_MAXHOST]; char host[NI_MAXHOST];
char service[NI_MAXSERV]; char service[NI_MAXSERV];
int rv; int rv;
@ -783,7 +783,7 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
} }
} }
return new ClientHandler(worker, fd, ssl, host, service); return new ClientHandler(worker, fd, ssl, host, service, faddr);
} }
bool tls_hostname_match(const char *pattern, size_t plen, const char *hostname, bool tls_hostname_match(const char *pattern, size_t plen, const char *hostname,

View File

@ -45,6 +45,7 @@ class ClientHandler;
class Worker; class Worker;
class DownstreamConnectionPool; class DownstreamConnectionPool;
struct DownstreamAddr; struct DownstreamAddr;
struct FrontendAddr;
namespace ssl { namespace ssl {
@ -76,7 +77,7 @@ SSL_CTX *create_ssl_client_context(
); );
ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
int addrlen); int addrlen, const FrontendAddr *faddr);
// Check peer's certificate against first downstream address in // Check peer's certificate against first downstream address in
// Config::downstream_addrs. We only consider first downstream since // Config::downstream_addrs. We only consider first downstream since

View File

@ -179,8 +179,9 @@ void Worker::process_events() {
break; break;
} }
auto client_handler = ssl::accept_connection( auto client_handler =
this, wev.client_fd, &wev.client_addr.sa, wev.client_addrlen); ssl::accept_connection(this, wev.client_fd, &wev.client_addr.sa,
wev.client_addrlen, wev.faddr);
if (!client_handler) { if (!client_handler) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
WLOG(ERROR, this) << "ClientHandler creation failed"; WLOG(ERROR, this) << "ClientHandler creation failed";

View File

@ -51,6 +51,7 @@ namespace shrpx {
class Http2Session; class Http2Session;
class ConnectBlocker; class ConnectBlocker;
class MemcachedDispatcher; class MemcachedDispatcher;
struct FrontendAddr;
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
namespace mruby { namespace mruby {
@ -93,6 +94,7 @@ struct WorkerEvent {
sockaddr_union client_addr; sockaddr_union client_addr;
size_t client_addrlen; size_t client_addrlen;
int client_fd; int client_fd;
const FrontendAddr *faddr;
}; };
std::shared_ptr<TicketKeys> ticket_keys; std::shared_ptr<TicketKeys> ticket_keys;
}; };

View File

@ -389,13 +389,8 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) {
ConnectionHandler conn_handler(loop); ConnectionHandler conn_handler(loop);
if (wpconf->server_fd6 != -1) { for (auto &addr : get_config()->conn.listener.addrs) {
conn_handler.set_acceptor6( conn_handler.add_acceptor(make_unique<AcceptHandler>(&addr, &conn_handler));
make_unique<AcceptHandler>(wpconf->server_fd6, &conn_handler));
}
if (wpconf->server_fd != -1) {
conn_handler.set_acceptor(
make_unique<AcceptHandler>(wpconf->server_fd, &conn_handler));
} }
auto &upstreamconf = get_config()->conn.upstream; auto &upstreamconf = get_config()->conn.upstream;

View File

@ -35,6 +35,7 @@
#include <functional> #include <functional>
#include <typeinfo> #include <typeinfo>
#include <algorithm> #include <algorithm>
#include <ostream>
namespace nghttp2 { namespace nghttp2 {
@ -317,6 +318,11 @@ private:
const char *base; const char *base;
}; };
inline bool operator==(const ImmutableString &lhs, const ImmutableString &rhs) {
return lhs.size() == rhs.size() &&
std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
}
inline bool operator==(const ImmutableString &lhs, const std::string &rhs) { inline bool operator==(const ImmutableString &lhs, const std::string &rhs) {
return lhs.size() == rhs.size() && return lhs.size() == rhs.size() &&
std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs)); std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
@ -335,6 +341,10 @@ inline bool operator==(const char *lhs, const ImmutableString &rhs) {
return rhs == lhs; return rhs == lhs;
} }
inline bool operator!=(const ImmutableString &lhs, const ImmutableString &rhs) {
return !(lhs == rhs);
}
inline bool operator!=(const ImmutableString &lhs, const std::string &rhs) { inline bool operator!=(const ImmutableString &lhs, const std::string &rhs) {
return !(lhs == rhs); return !(lhs == rhs);
} }
@ -351,6 +361,15 @@ inline bool operator!=(const char *lhs, const ImmutableString &rhs) {
return !(rhs == lhs); return !(rhs == lhs);
} }
inline std::ostream &operator<<(std::ostream &o, const ImmutableString &s) {
return o.write(s.c_str(), s.size());
}
inline std::string &operator+=(std::string &lhs, const ImmutableString &rhs) {
lhs.append(rhs.c_str(), rhs.size());
return lhs;
}
// StringRef is a reference to a string owned by something else. So // StringRef is a reference to a string owned by something else. So
// it behaves like simple string, but it does not own pointer. When // it behaves like simple string, but it does not own pointer. When
// it is default constructed, it has empty string. You can freely // it is default constructed, it has empty string. You can freely
@ -374,7 +393,9 @@ public:
: base(s.c_str()), len(s.size()) {} : base(s.c_str()), len(s.size()) {}
StringRef(const char *s) : base(s), len(strlen(s)) {} StringRef(const char *s) : base(s), len(strlen(s)) {}
StringRef(const char *s, size_t n) : base(s), len(n) {} StringRef(const char *s, size_t n) : base(s), len(n) {}
template <typename InputIt>
StringRef(InputIt first, InputIt last)
: base(first), len(std::distance(first, last)) {}
template <size_t N> static StringRef from_lit(const char(&s)[N]) { template <size_t N> static StringRef from_lit(const char(&s)[N]) {
return StringRef(s, N - 1); return StringRef(s, N - 1);
} }
@ -388,6 +409,7 @@ public:
const char *c_str() const { return base; } const char *c_str() const { return base; }
size_type size() const { return len; } size_type size() const { return len; }
bool empty() const { return len == 0; } bool empty() const { return len == 0; }
const_reference operator[](size_type pos) const { return *(base + pos); }
std::string str() const { return std::string(base, len); } std::string str() const { return std::string(base, len); }
@ -430,6 +452,15 @@ inline bool operator!=(const char *lhs, const StringRef &rhs) {
return !(rhs == lhs); return !(rhs == lhs);
} }
inline std::ostream &operator<<(std::ostream &o, const StringRef &s) {
return o.write(s.c_str(), s.size());
}
inline std::string &operator+=(std::string &lhs, const StringRef &rhs) {
lhs.append(rhs.c_str(), rhs.size());
return lhs;
}
inline int run_app(std::function<int(int, char **)> app, int argc, inline int run_app(std::function<int(int, char **)> app, int argc,
char **argv) { char **argv) {
try { try {

View File

@ -26,6 +26,7 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <sstream>
#include <CUnit/CUnit.h> #include <CUnit/CUnit.h>
@ -101,6 +102,34 @@ void test_template_immutable_string(void) {
CU_ASSERT('o' == br_op[1]); CU_ASSERT('o' == br_op[1]);
CU_ASSERT('t' == br_op[6]); CU_ASSERT('t' == br_op[6]);
CU_ASSERT('\0' == br_op[7]); CU_ASSERT('\0' == br_op[7]);
// operator==(const ImmutableString &, const ImmutableString &)
{
ImmutableString a("foo");
ImmutableString b("foo");
ImmutableString c("fo");
CU_ASSERT(a == b);
CU_ASSERT(a != c);
CU_ASSERT(c != b);
}
// operator<<
{
ImmutableString a("foo");
std::stringstream ss;
ss << a;
CU_ASSERT("foo" == ss.str());
}
// operator +=(std::string &, const ImmutableString &)
{
std::string a = "alpha";
a += ImmutableString("bravo");
CU_ASSERT("alphabravo" == a);
}
} }
void test_template_string_ref(void) { void test_template_string_ref(void) {
@ -145,6 +174,31 @@ void test_template_string_ref(void) {
CU_ASSERT("delta" == cstrnref); CU_ASSERT("delta" == cstrnref);
CU_ASSERT(5 == cstrnref.size()); CU_ASSERT(5 == cstrnref.size());
// operator[]
StringRef br_op("foxtrot");
CU_ASSERT('f' == br_op[0]);
CU_ASSERT('o' == br_op[1]);
CU_ASSERT('t' == br_op[6]);
CU_ASSERT('\0' == br_op[7]);
// operator<<
{
StringRef a("foo");
std::stringstream ss;
ss << a;
CU_ASSERT("foo" == ss.str());
}
// operator +=(std::string &, const StringRef &)
{
std::string a = "alpha";
a += StringRef("bravo");
CU_ASSERT("alphabravo" == a);
}
} }
} // namespace nghttp2 } // namespace nghttp2