5219 lines
195 KiB
C++
5219 lines
195 KiB
C++
/*
|
|
* nghttp2 - HTTP/2 C Library
|
|
*
|
|
* Copyright (c) 2012 Tatsuhiro Tsujikawa
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
#include "shrpx.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/stat.h>
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
# include <sys/socket.h>
|
|
#endif // HAVE_SYS_SOCKET_H
|
|
#include <sys/un.h>
|
|
#ifdef HAVE_NETDB_H
|
|
# include <netdb.h>
|
|
#endif // HAVE_NETDB_H
|
|
#include <signal.h>
|
|
#ifdef HAVE_NETINET_IN_H
|
|
# include <netinet/in.h>
|
|
#endif // HAVE_NETINET_IN_H
|
|
#include <netinet/tcp.h>
|
|
#ifdef HAVE_ARPA_INET_H
|
|
# include <arpa/inet.h>
|
|
#endif // HAVE_ARPA_INET_H
|
|
#ifdef HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif // HAVE_UNISTD_H
|
|
#include <getopt.h>
|
|
#ifdef HAVE_SYSLOG_H
|
|
# include <syslog.h>
|
|
#endif // HAVE_SYSLOG_H
|
|
#ifdef HAVE_LIMITS_H
|
|
# include <limits.h>
|
|
#endif // HAVE_LIMITS_H
|
|
#ifdef HAVE_SYS_TIME_H
|
|
# include <sys/time.h>
|
|
#endif // HAVE_SYS_TIME_H
|
|
#include <sys/resource.h>
|
|
#ifdef HAVE_LIBSYSTEMD
|
|
# include <systemd/sd-daemon.h>
|
|
#endif // HAVE_LIBSYSTEMD
|
|
#ifdef HAVE_LIBBPF
|
|
# include <bpf/libbpf.h>
|
|
#endif // HAVE_LIBBPF
|
|
|
|
#include <cinttypes>
|
|
#include <limits>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <initializer_list>
|
|
#include <random>
|
|
|
|
#include <openssl/ssl.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/rand.h>
|
|
#include <ev.h>
|
|
|
|
#include <nghttp2/nghttp2.h>
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
# include <ngtcp2/ngtcp2.h>
|
|
# include <nghttp3/nghttp3.h>
|
|
#endif // ENABLE_HTTP3
|
|
|
|
#include "shrpx_config.h"
|
|
#include "shrpx_tls.h"
|
|
#include "shrpx_log_config.h"
|
|
#include "shrpx_worker.h"
|
|
#include "shrpx_http2_upstream.h"
|
|
#include "shrpx_http2_session.h"
|
|
#include "shrpx_worker_process.h"
|
|
#include "shrpx_process.h"
|
|
#include "shrpx_signal.h"
|
|
#include "shrpx_connection.h"
|
|
#include "shrpx_log.h"
|
|
#include "shrpx_http.h"
|
|
#include "util.h"
|
|
#include "app_helper.h"
|
|
#include "tls.h"
|
|
#include "template.h"
|
|
#include "allocator.h"
|
|
#include "ssl_compat.h"
|
|
#include "xsi_strerror.h"
|
|
|
|
extern char **environ;
|
|
|
|
using namespace nghttp2;
|
|
|
|
namespace shrpx {
|
|
|
|
// Deprecated: Environment variables to tell new binary the listening
|
|
// socket's file descriptors. They are not close-on-exec.
|
|
constexpr auto ENV_LISTENER4_FD = StringRef::from_lit("NGHTTPX_LISTENER4_FD");
|
|
constexpr auto ENV_LISTENER6_FD = StringRef::from_lit("NGHTTPX_LISTENER6_FD");
|
|
|
|
// Deprecated: Environment variable to tell new binary the port number
|
|
// the current binary is listening to.
|
|
constexpr auto ENV_PORT = StringRef::from_lit("NGHTTPX_PORT");
|
|
|
|
// Deprecated: Environment variable to tell new binary the listening
|
|
// socket's file descriptor if frontend listens UNIX domain socket.
|
|
constexpr auto ENV_UNIX_FD = StringRef::from_lit("NGHTTP2_UNIX_FD");
|
|
// Deprecated: Environment variable to tell new binary the UNIX domain
|
|
// socket path.
|
|
constexpr auto ENV_UNIX_PATH = StringRef::from_lit("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 auto ENV_ACCEPT_PREFIX = StringRef::from_lit("NGHTTPX_ACCEPT_");
|
|
|
|
// This environment variable contains PID of the original main
|
|
// process, assuming that it created this main process as a result of
|
|
// SIGUSR2. The new main process is expected to send QUIT signal to
|
|
// the original main process to shut it down gracefully.
|
|
constexpr auto ENV_ORIG_PID = StringRef::from_lit("NGHTTPX_ORIG_PID");
|
|
|
|
// Prefix of environment variables to tell new binary the QUIC IPC
|
|
// file descriptor and CID prefix of the lingering worker process.
|
|
// The value must be comma separated parameters:
|
|
// <FD>,<CID_PREFIX_0>,<CID_PREFIX_1>,... <FD> is the file
|
|
// descriptor. <CID_PREFIX_I> is the I-th CID prefix in hex encoded
|
|
// string.
|
|
constexpr auto ENV_QUIC_WORKER_PROCESS_PREFIX =
|
|
StringRef::from_lit("NGHTTPX_QUIC_WORKER_PROCESS_");
|
|
|
|
#ifndef _KERNEL_FASTOPEN
|
|
# define _KERNEL_FASTOPEN
|
|
// conditional define for TCP_FASTOPEN mostly on ubuntu
|
|
# ifndef TCP_FASTOPEN
|
|
# define TCP_FASTOPEN 23
|
|
# endif
|
|
|
|
// conditional define for SOL_TCP mostly on ubuntu
|
|
# ifndef SOL_TCP
|
|
# define SOL_TCP 6
|
|
# endif
|
|
#endif
|
|
|
|
// This configuration is fixed at the first startup of the main
|
|
// process, and does not change after subsequent reloadings.
|
|
struct StartupConfig {
|
|
// This contains all options given in command-line.
|
|
std::vector<std::pair<StringRef, StringRef>> cmdcfgs;
|
|
// The current working directory where this process started.
|
|
char *cwd;
|
|
// The pointer to original argv (not sure why we have this?)
|
|
char **original_argv;
|
|
// The pointer to argv, this is a deep copy of original argv.
|
|
char **argv;
|
|
// The number of elements in argv.
|
|
int argc;
|
|
};
|
|
|
|
namespace {
|
|
StartupConfig suconfig;
|
|
} // namespace
|
|
|
|
struct InheritedAddr {
|
|
// IP address if TCP socket. Otherwise, UNIX domain socket path.
|
|
StringRef host;
|
|
uint16_t port;
|
|
// true if UNIX domain socket path
|
|
bool host_unix;
|
|
int fd;
|
|
bool used;
|
|
};
|
|
|
|
namespace {
|
|
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents);
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents);
|
|
} // namespace
|
|
|
|
struct WorkerProcess {
|
|
WorkerProcess(struct ev_loop *loop, pid_t worker_pid, int ipc_fd
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
int quic_ipc_fd,
|
|
const std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
|
|
&cid_prefixes
|
|
#endif // ENABLE_HTTP3
|
|
)
|
|
: loop(loop),
|
|
worker_pid(worker_pid),
|
|
ipc_fd(ipc_fd),
|
|
termination_deadline(0.)
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
quic_ipc_fd(quic_ipc_fd),
|
|
cid_prefixes(cid_prefixes)
|
|
#endif // ENABLE_HTTP3
|
|
{
|
|
ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
|
|
reopen_log_signalev.data = this;
|
|
ev_signal_start(loop, &reopen_log_signalev);
|
|
|
|
ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
|
|
exec_binary_signalev.data = this;
|
|
ev_signal_start(loop, &exec_binary_signalev);
|
|
|
|
ev_signal_init(&graceful_shutdown_signalev, signal_cb,
|
|
GRACEFUL_SHUTDOWN_SIGNAL);
|
|
graceful_shutdown_signalev.data = this;
|
|
ev_signal_start(loop, &graceful_shutdown_signalev);
|
|
|
|
ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
|
|
reload_signalev.data = this;
|
|
ev_signal_start(loop, &reload_signalev);
|
|
|
|
ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid,
|
|
0);
|
|
worker_process_childev.data = this;
|
|
ev_child_start(loop, &worker_process_childev);
|
|
}
|
|
|
|
~WorkerProcess() {
|
|
shutdown_signal_watchers();
|
|
|
|
ev_child_stop(loop, &worker_process_childev);
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
if (quic_ipc_fd != -1) {
|
|
close(quic_ipc_fd);
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
if (ipc_fd != -1) {
|
|
shutdown(ipc_fd, SHUT_WR);
|
|
close(ipc_fd);
|
|
}
|
|
}
|
|
|
|
void shutdown_signal_watchers() {
|
|
ev_signal_stop(loop, &reopen_log_signalev);
|
|
ev_signal_stop(loop, &exec_binary_signalev);
|
|
ev_signal_stop(loop, &graceful_shutdown_signalev);
|
|
ev_signal_stop(loop, &reload_signalev);
|
|
}
|
|
|
|
ev_signal reopen_log_signalev;
|
|
ev_signal exec_binary_signalev;
|
|
ev_signal graceful_shutdown_signalev;
|
|
ev_signal reload_signalev;
|
|
ev_child worker_process_childev;
|
|
struct ev_loop *loop;
|
|
pid_t worker_pid;
|
|
int ipc_fd;
|
|
ev_tstamp termination_deadline;
|
|
#ifdef ENABLE_HTTP3
|
|
int quic_ipc_fd;
|
|
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
|
#endif // ENABLE_HTTP3
|
|
};
|
|
|
|
namespace {
|
|
void reload_config(WorkerProcess *wp);
|
|
} // namespace
|
|
|
|
namespace {
|
|
std::deque<std::unique_ptr<WorkerProcess>> worker_processes;
|
|
} // namespace
|
|
|
|
namespace {
|
|
ev_timer worker_process_grace_period_timer;
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_grace_period_timercb(struct ev_loop *loop, ev_timer *w,
|
|
int revents) {
|
|
auto now = ev_now(loop);
|
|
ev_tstamp next_repeat = 0.;
|
|
|
|
for (auto it = std::begin(worker_processes);
|
|
it != std::end(worker_processes);) {
|
|
auto &wp = *it;
|
|
if (!(wp->termination_deadline > 0.)) {
|
|
++it;
|
|
|
|
continue;
|
|
}
|
|
|
|
auto d = wp->termination_deadline - now;
|
|
if (d > 0) {
|
|
if (!(next_repeat > 0.) || d < next_repeat) {
|
|
next_repeat = d;
|
|
}
|
|
|
|
++it;
|
|
|
|
continue;
|
|
}
|
|
|
|
LOG(NOTICE) << "Deleting worker process pid=" << wp->worker_pid
|
|
<< " because its grace shutdown period is over";
|
|
|
|
it = worker_processes.erase(it);
|
|
}
|
|
|
|
if (next_repeat > 0.) {
|
|
w->repeat = next_repeat;
|
|
ev_timer_again(loop, w);
|
|
|
|
return;
|
|
}
|
|
|
|
ev_timer_stop(loop, w);
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_set_termination_deadline(WorkerProcess *wp,
|
|
struct ev_loop *loop) {
|
|
auto config = get_config();
|
|
|
|
if (!(config->worker_process_grace_shutdown_period > 0.)) {
|
|
return;
|
|
}
|
|
|
|
wp->termination_deadline =
|
|
ev_now(loop) + config->worker_process_grace_shutdown_period;
|
|
|
|
if (!ev_is_active(&worker_process_grace_period_timer)) {
|
|
worker_process_grace_period_timer.repeat =
|
|
config->worker_process_grace_shutdown_period;
|
|
|
|
ev_timer_again(loop, &worker_process_grace_period_timer);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_add(std::unique_ptr<WorkerProcess> wp) {
|
|
worker_processes.push_back(std::move(wp));
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_remove(const WorkerProcess *wp, struct ev_loop *loop) {
|
|
for (auto it = std::begin(worker_processes); it != std::end(worker_processes);
|
|
++it) {
|
|
auto &s = *it;
|
|
|
|
if (s.get() != wp) {
|
|
continue;
|
|
}
|
|
|
|
worker_processes.erase(it);
|
|
|
|
if (worker_processes.empty()) {
|
|
ev_timer_stop(loop, &worker_process_grace_period_timer);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_adjust_limit() {
|
|
auto config = get_config();
|
|
|
|
if (config->max_worker_processes &&
|
|
worker_processes.size() > config->max_worker_processes) {
|
|
worker_processes.pop_front();
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_remove_all(struct ev_loop *loop) {
|
|
std::deque<std::unique_ptr<WorkerProcess>>().swap(worker_processes);
|
|
|
|
ev_timer_stop(loop, &worker_process_grace_period_timer);
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Send signal |signum| to all worker processes, and clears
|
|
// worker_processes.
|
|
void worker_process_kill(int signum, struct ev_loop *loop) {
|
|
for (auto &s : worker_processes) {
|
|
if (s->worker_pid == -1) {
|
|
continue;
|
|
}
|
|
kill(s->worker_pid, signum);
|
|
}
|
|
worker_process_remove_all(loop);
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Returns the last PID of worker process. Returns -1 if there is no
|
|
// worker process at the moment.
|
|
int worker_process_last_pid() {
|
|
if (worker_processes.empty()) {
|
|
return -1;
|
|
}
|
|
|
|
return worker_processes.back()->worker_pid;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int save_pid() {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
auto config = get_config();
|
|
|
|
constexpr auto SUFFIX = StringRef::from_lit(".XXXXXX");
|
|
auto &pid_file = config->pid_file;
|
|
|
|
auto len = config->pid_file.size() + SUFFIX.size();
|
|
auto buf = std::make_unique<char[]>(len + 1);
|
|
auto p = buf.get();
|
|
|
|
p = std::copy(std::begin(pid_file), std::end(pid_file), p);
|
|
p = std::copy(std::begin(SUFFIX), std::end(SUFFIX), p);
|
|
*p = '\0';
|
|
|
|
auto temp_path = buf.get();
|
|
|
|
auto fd = mkstemp(temp_path);
|
|
if (fd == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
|
|
auto content = util::utos(config->pid) + '\n';
|
|
|
|
if (write(fd, content.c_str(), content.size()) == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
|
|
if (fsync(fd) == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
if (rename(temp_path, pid_file.c_str()) == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
unlink(temp_path);
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (config->uid != 0) {
|
|
if (chown(pid_file.c_str(), config->uid, config->gid) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Changing owner of pid file " << pid_file << " failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void shrpx_sd_notifyf(int unset_environment, const char *format, ...) {
|
|
#ifdef HAVE_LIBSYSTEMD
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
sd_notifyf(unset_environment, format, va_arg(args, char *));
|
|
va_end(args);
|
|
#endif // HAVE_LIBSYSTEMD
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void exec_binary() {
|
|
int rv;
|
|
sigset_t oldset;
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
|
|
LOG(NOTICE) << "Executing new binary";
|
|
|
|
shrpx_sd_notifyf(0, "RELOADING=1");
|
|
|
|
rv = shrpx_signal_block_all(&oldset);
|
|
if (rv != 0) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Blocking all signals failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
return;
|
|
}
|
|
|
|
auto pid = fork();
|
|
|
|
if (pid != 0) {
|
|
if (pid == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "fork() failed errno=" << error;
|
|
} else {
|
|
// update PID tracking information in systemd
|
|
shrpx_sd_notifyf(0, "MAINPID=%d\n", pid);
|
|
}
|
|
|
|
rv = shrpx_signal_set(&oldset);
|
|
|
|
if (rv != 0) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Restoring signal mask failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// child process
|
|
|
|
shrpx_signal_unset_main_proc_ign_handler();
|
|
|
|
rv = shrpx_signal_unblock_all();
|
|
if (rv != 0) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Unblocking all signals failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
nghttp2_Exit(EXIT_FAILURE);
|
|
}
|
|
|
|
auto exec_path =
|
|
util::get_exec_path(suconfig.argc, suconfig.argv, suconfig.cwd);
|
|
|
|
if (!exec_path) {
|
|
LOG(ERROR) << "Could not resolve the executable path";
|
|
nghttp2_Exit(EXIT_FAILURE);
|
|
}
|
|
|
|
auto argv = std::make_unique<char *[]>(suconfig.argc + 1);
|
|
|
|
argv[0] = exec_path;
|
|
for (int i = 1; i < suconfig.argc; ++i) {
|
|
argv[i] = suconfig.argv[i];
|
|
}
|
|
argv[suconfig.argc] = nullptr;
|
|
|
|
size_t envlen = 0;
|
|
for (char **p = environ; *p; ++p, ++envlen)
|
|
;
|
|
|
|
auto config = get_config();
|
|
auto &listenerconf = config->conn.listener;
|
|
|
|
// 2 for ENV_ORIG_PID and terminal nullptr.
|
|
auto envp = std::make_unique<char *[]>(envlen + listenerconf.addrs.size() +
|
|
worker_processes.size() + 2);
|
|
size_t envidx = 0;
|
|
|
|
std::vector<ImmutableString> fd_envs;
|
|
for (size_t i = 0; i < listenerconf.addrs.size(); ++i) {
|
|
auto &addr = listenerconf.addrs[i];
|
|
auto s = ENV_ACCEPT_PREFIX.str();
|
|
s += util::utos(i + 1);
|
|
s += '=';
|
|
if (addr.host_unix) {
|
|
s += "unix,";
|
|
s += util::utos(addr.fd);
|
|
s += ',';
|
|
s += addr.host;
|
|
} else {
|
|
s += "tcp,";
|
|
s += util::utos(addr.fd);
|
|
}
|
|
|
|
fd_envs.emplace_back(s);
|
|
envp[envidx++] = const_cast<char *>(fd_envs.back().c_str());
|
|
}
|
|
|
|
auto ipc_fd_str = ENV_ORIG_PID.str();
|
|
ipc_fd_str += '=';
|
|
ipc_fd_str += util::utos(config->pid);
|
|
envp[envidx++] = const_cast<char *>(ipc_fd_str.c_str());
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
std::vector<ImmutableString> quic_lwps;
|
|
for (size_t i = 0; i < worker_processes.size(); ++i) {
|
|
auto &wp = worker_processes[i];
|
|
auto s = ENV_QUIC_WORKER_PROCESS_PREFIX.str();
|
|
s += util::utos(i + 1);
|
|
s += '=';
|
|
s += util::utos(wp->quic_ipc_fd);
|
|
for (auto &cid_prefix : wp->cid_prefixes) {
|
|
s += ',';
|
|
s += util::format_hex(cid_prefix);
|
|
}
|
|
|
|
quic_lwps.emplace_back(s);
|
|
envp[envidx++] = const_cast<char *>(quic_lwps.back().c_str());
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
for (size_t i = 0; i < envlen; ++i) {
|
|
auto env = StringRef{environ[i]};
|
|
if (util::starts_with(env, ENV_ACCEPT_PREFIX) ||
|
|
util::starts_with(env, ENV_LISTENER4_FD) ||
|
|
util::starts_with(env, ENV_LISTENER6_FD) ||
|
|
util::starts_with(env, ENV_PORT) ||
|
|
util::starts_with(env, ENV_UNIX_FD) ||
|
|
util::starts_with(env, ENV_UNIX_PATH) ||
|
|
util::starts_with(env, ENV_ORIG_PID) ||
|
|
util::starts_with(env, ENV_QUIC_WORKER_PROCESS_PREFIX)) {
|
|
continue;
|
|
}
|
|
|
|
envp[envidx++] = environ[i];
|
|
}
|
|
|
|
envp[envidx++] = nullptr;
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
LOG(INFO) << "cmdline";
|
|
for (int i = 0; argv[i]; ++i) {
|
|
LOG(INFO) << i << ": " << argv[i];
|
|
}
|
|
LOG(INFO) << "environ";
|
|
for (int i = 0; envp[i]; ++i) {
|
|
LOG(INFO) << i << ": " << envp[i];
|
|
}
|
|
}
|
|
|
|
// restores original stderr
|
|
restore_original_fds();
|
|
|
|
// reloading finished
|
|
shrpx_sd_notifyf(0, "READY=1");
|
|
|
|
if (execve(argv[0], argv.get(), envp.get()) == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "execve failed: errno=" << error;
|
|
nghttp2_Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void ipc_send(WorkerProcess *wp, uint8_t ipc_event) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
ssize_t nwrite;
|
|
while ((nwrite = write(wp->ipc_fd, &ipc_event, 1)) == -1 && errno == EINTR)
|
|
;
|
|
|
|
if (nwrite < 0) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Could not send IPC event to worker process: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return;
|
|
}
|
|
|
|
if (nwrite == 0) {
|
|
LOG(ERROR) << "Could not send IPC event due to pipe overflow";
|
|
return;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void reopen_log(WorkerProcess *wp) {
|
|
LOG(NOTICE) << "Reopening log files: main process";
|
|
|
|
auto config = get_config();
|
|
auto &loggingconf = config->logging;
|
|
|
|
(void)reopen_log_files(loggingconf);
|
|
redirect_stderr_to_errorlog(loggingconf);
|
|
ipc_send(wp, SHRPX_IPC_REOPEN_LOG);
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
|
|
auto wp = static_cast<WorkerProcess *>(w->data);
|
|
if (wp->worker_pid == -1) {
|
|
ev_break(loop);
|
|
return;
|
|
}
|
|
|
|
switch (w->signum) {
|
|
case REOPEN_LOG_SIGNAL:
|
|
reopen_log(wp);
|
|
return;
|
|
case EXEC_BINARY_SIGNAL:
|
|
exec_binary();
|
|
return;
|
|
case GRACEFUL_SHUTDOWN_SIGNAL: {
|
|
auto &listenerconf = get_config()->conn.listener;
|
|
for (auto &addr : listenerconf.addrs) {
|
|
close(addr.fd);
|
|
}
|
|
ipc_send(wp, SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
|
worker_process_set_termination_deadline(wp, loop);
|
|
return;
|
|
}
|
|
case RELOAD_SIGNAL:
|
|
reload_config(wp);
|
|
return;
|
|
default:
|
|
worker_process_kill(w->signum, loop);
|
|
ev_break(loop);
|
|
return;
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
|
|
auto wp = static_cast<WorkerProcess *>(w->data);
|
|
|
|
log_chld(w->rpid, w->rstatus, "Worker process");
|
|
|
|
auto pid = wp->worker_pid;
|
|
|
|
worker_process_remove(wp, loop);
|
|
|
|
if (worker_process_last_pid() == pid) {
|
|
ev_break(loop);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int create_unix_domain_server_socket(UpstreamAddr &faddr,
|
|
std::vector<InheritedAddr> &iaddrs) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
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;
|
|
});
|
|
|
|
if (found != std::end(iaddrs)) {
|
|
LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host
|
|
<< (faddr.tls ? ", tls" : "");
|
|
(*found).used = true;
|
|
faddr.fd = (*found).fd;
|
|
faddr.hostport = StringRef::from_lit("localhost");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef SOCK_NONBLOCK
|
|
auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
|
if (fd == -1) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "socket() syscall failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
#else // !SOCK_NONBLOCK
|
|
auto fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (fd == -1) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "socket() syscall failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
util::make_socket_nonblocking(fd);
|
|
#endif // !SOCK_NONBLOCK
|
|
int val = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
|
|
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Failed to set SO_REUSEADDR option to listener socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
sockaddr_union addr;
|
|
addr.un.sun_family = AF_UNIX;
|
|
if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) {
|
|
LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > "
|
|
<< sizeof(addr.un.sun_path);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
// copy path including terminal NULL
|
|
std::copy_n(faddr.host.c_str(), faddr.host.size() + 1, addr.un.sun_path);
|
|
|
|
// unlink (remove) already existing UNIX domain socket path
|
|
unlink(faddr.host.c_str());
|
|
|
|
if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Failed to bind UNIX domain socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
auto &listenerconf = get_config()->conn.listener;
|
|
|
|
if (listen(fd, listenerconf.backlog) != 0) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Failed to listen to UNIX domain socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host
|
|
<< (faddr.tls ? ", tls" : "");
|
|
|
|
faddr.fd = fd;
|
|
faddr.hostport = StringRef::from_lit("localhost");
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int create_tcp_server_socket(UpstreamAddr &faddr,
|
|
std::vector<InheritedAddr> &iaddrs) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
int fd = -1;
|
|
int rv;
|
|
|
|
auto &listenerconf = get_config()->conn.listener;
|
|
|
|
auto service = util::utos(faddr.port);
|
|
addrinfo hints{};
|
|
hints.ai_family = faddr.family;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_PASSIVE;
|
|
#ifdef AI_ADDRCONFIG
|
|
hints.ai_flags |= AI_ADDRCONFIG;
|
|
#endif // AI_ADDRCONFIG
|
|
|
|
auto node =
|
|
faddr.host == StringRef::from_lit("*") ? nullptr : faddr.host.c_str();
|
|
|
|
addrinfo *res, *rp;
|
|
rv = getaddrinfo(node, service.c_str(), &hints, &res);
|
|
#ifdef AI_ADDRCONFIG
|
|
if (rv != 0) {
|
|
// Retry without AI_ADDRCONFIG
|
|
hints.ai_flags &= ~AI_ADDRCONFIG;
|
|
rv = getaddrinfo(node, service.c_str(), &hints, &res);
|
|
}
|
|
#endif // AI_ADDRCONFIG
|
|
if (rv != 0) {
|
|
LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6")
|
|
<< " address for " << faddr.host << ", port " << faddr.port
|
|
<< ": " << gai_strerror(rv);
|
|
return -1;
|
|
}
|
|
|
|
auto res_d = defer(freeaddrinfo, res);
|
|
|
|
std::array<char, NI_MAXHOST> host;
|
|
|
|
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
|
|
fd =
|
|
socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
|
|
if (fd == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "socket() syscall failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
continue;
|
|
}
|
|
#else // !SOCK_NONBLOCK
|
|
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
|
if (fd == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "socket() syscall failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
continue;
|
|
}
|
|
util::make_socket_nonblocking(fd);
|
|
#endif // !SOCK_NONBLOCK
|
|
int val = 1;
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
|
|
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
#ifdef IPV6_V6ONLY
|
|
if (faddr.family == AF_INET6) {
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
|
|
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
continue;
|
|
}
|
|
}
|
|
#endif // IPV6_V6ONLY
|
|
|
|
#ifdef TCP_DEFER_ACCEPT
|
|
val = 3;
|
|
if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val,
|
|
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
#endif // TCP_DEFER_ACCEPT
|
|
|
|
// When we are executing new binary, and the old binary did not
|
|
// bind privileged port (< 1024) for some reason, binding to those
|
|
// ports will fail with permission denied error.
|
|
if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "bind() syscall failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
if (listenerconf.fastopen > 0) {
|
|
val = listenerconf.fastopen;
|
|
if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &val,
|
|
static_cast<socklen_t>(sizeof(val))) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
|
|
if (listen(fd, listenerconf.backlog) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "listen() syscall failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
close(fd);
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (!rp) {
|
|
LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6")
|
|
<< " socket failed";
|
|
|
|
return -1;
|
|
}
|
|
|
|
faddr.fd = fd;
|
|
faddr.hostport = util::make_http_hostport(mod_config()->balloc,
|
|
StringRef{host.data()}, faddr.port);
|
|
|
|
LOG(NOTICE) << "Listening on " << faddr.hostport
|
|
<< (faddr.tls ? ", tls" : "");
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Returns array of InheritedAddr constructed from |config|. This
|
|
// function is intended to be used when reloading configuration, and
|
|
// |config| is usually a current configuration.
|
|
std::vector<InheritedAddr>
|
|
get_inherited_addr_from_config(BlockAllocator &balloc, Config *config) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
int rv;
|
|
|
|
auto &listenerconf = config->conn.listener;
|
|
|
|
std::vector<InheritedAddr> iaddrs(listenerconf.addrs.size());
|
|
|
|
size_t idx = 0;
|
|
for (auto &addr : listenerconf.addrs) {
|
|
auto &iaddr = iaddrs[idx++];
|
|
|
|
if (addr.host_unix) {
|
|
iaddr.host = addr.host;
|
|
iaddr.host_unix = true;
|
|
iaddr.fd = addr.fd;
|
|
|
|
continue;
|
|
}
|
|
|
|
iaddr.port = addr.port;
|
|
iaddr.fd = addr.fd;
|
|
|
|
// We have to getsockname/getnameinfo for fd, since we may have
|
|
// '*' appear in addr.host, which makes comparison against "real"
|
|
// address fail.
|
|
|
|
sockaddr_union su;
|
|
socklen_t salen = sizeof(su);
|
|
|
|
// We already added entry to iaddrs. Even if we got errors, we
|
|
// don't remove it. This is required because we have to close the
|
|
// socket if it is not reused. The empty host name usually does
|
|
// not match anything.
|
|
|
|
if (getsockname(addr.fd, &su.sa, &salen) != 0) {
|
|
auto error = errno;
|
|
LOG(WARN) << "getsockname() syscall failed (fd=" << addr.fd
|
|
<< "): " << xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
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=" << addr.fd
|
|
<< "): " << gai_strerror(rv);
|
|
continue;
|
|
}
|
|
|
|
iaddr.host = make_string_ref(balloc, StringRef{host.data()});
|
|
}
|
|
|
|
return iaddrs;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Returns array of InheritedAddr constructed from environment
|
|
// variables. This function handles the old environment variable
|
|
// names used in 1.7.0 or earlier.
|
|
std::vector<InheritedAddr> get_inherited_addr_from_env(Config *config) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
int rv;
|
|
std::vector<InheritedAddr> iaddrs;
|
|
|
|
{
|
|
// Upgrade from 1.7.0 or earlier
|
|
auto portenv = getenv(ENV_PORT.c_str());
|
|
if (portenv) {
|
|
size_t i = 1;
|
|
for (const auto &env_name : {ENV_LISTENER4_FD, ENV_LISTENER6_FD}) {
|
|
auto fdenv = getenv(env_name.c_str());
|
|
if (fdenv) {
|
|
auto name = ENV_ACCEPT_PREFIX.str();
|
|
name += util::utos(i);
|
|
std::string value = "tcp,";
|
|
value += fdenv;
|
|
setenv(name.c_str(), value.c_str(), 0);
|
|
++i;
|
|
}
|
|
}
|
|
} else {
|
|
// The return value of getenv may be allocated statically.
|
|
if (getenv(ENV_UNIX_PATH.c_str()) && getenv(ENV_UNIX_FD.c_str())) {
|
|
auto name = ENV_ACCEPT_PREFIX.str();
|
|
name += '1';
|
|
std::string value = "unix,";
|
|
value += getenv(ENV_UNIX_FD.c_str());
|
|
value += ',';
|
|
value += getenv(ENV_UNIX_PATH.c_str());
|
|
setenv(name.c_str(), value.c_str(), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 1;; ++i) {
|
|
auto name = ENV_ACCEPT_PREFIX.str();
|
|
name += util::utos(i);
|
|
auto env = getenv(name.c_str());
|
|
if (!env) {
|
|
break;
|
|
}
|
|
|
|
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 == StringRef::from_lit("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 = make_string_ref(config->balloc, StringRef{path});
|
|
addr.host_unix = true;
|
|
addr.fd = static_cast<int>(fd);
|
|
iaddrs.push_back(std::move(addr));
|
|
}
|
|
|
|
if (type == StringRef::from_lit("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
|
|
<< "): " << xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
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 = make_string_ref(config->balloc, StringRef{host.data()});
|
|
addr.port = static_cast<uint16_t>(port);
|
|
addr.fd = static_cast<int>(fd);
|
|
iaddrs.push_back(std::move(addr));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return iaddrs;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Closes all sockets which are not reused.
|
|
void close_unused_inherited_addr(const std::vector<InheritedAddr> &iaddrs) {
|
|
for (auto &ia : iaddrs) {
|
|
if (ia.used) {
|
|
continue;
|
|
}
|
|
|
|
close(ia.fd);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Returns the PID of the original main process from environment
|
|
// variable ENV_ORIG_PID.
|
|
pid_t get_orig_pid_from_env() {
|
|
auto s = getenv(ENV_ORIG_PID.c_str());
|
|
if (s == nullptr) {
|
|
return -1;
|
|
}
|
|
return util::parse_uint(s);
|
|
}
|
|
} // namespace
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
namespace {
|
|
std::vector<QUICLingeringWorkerProcess>
|
|
inherited_quic_lingering_worker_processes;
|
|
} // namespace
|
|
|
|
namespace {
|
|
std::vector<QUICLingeringWorkerProcess>
|
|
get_inherited_quic_lingering_worker_process_from_env() {
|
|
std::vector<QUICLingeringWorkerProcess> iwps;
|
|
|
|
for (size_t i = 1;; ++i) {
|
|
auto name = ENV_QUIC_WORKER_PROCESS_PREFIX.str();
|
|
name += util::utos(i);
|
|
auto env = getenv(name.c_str());
|
|
if (!env) {
|
|
break;
|
|
}
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
LOG(INFO) << "Read env " << name << "=" << env;
|
|
}
|
|
|
|
auto envend = env + strlen(env);
|
|
|
|
auto end_fd = std::find(env, envend, ',');
|
|
if (end_fd == envend) {
|
|
continue;
|
|
}
|
|
|
|
auto fd =
|
|
util::parse_uint(reinterpret_cast<const uint8_t *>(env), end_fd - env);
|
|
if (fd == -1) {
|
|
LOG(WARN) << "Could not parse file descriptor from "
|
|
<< StringRef{env, static_cast<size_t>(end_fd - env)};
|
|
continue;
|
|
}
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
LOG(INFO) << "Inherit worker process QUIC IPC socket fd=" << fd;
|
|
}
|
|
|
|
util::make_socket_closeonexec(fd);
|
|
|
|
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
|
|
|
auto p = end_fd + 1;
|
|
for (;;) {
|
|
auto end = std::find(p, envend, ',');
|
|
|
|
auto hex_cid_prefix = StringRef{p, end};
|
|
if (hex_cid_prefix.size() != SHRPX_QUIC_CID_PREFIXLEN * 2 ||
|
|
!util::is_hex_string(hex_cid_prefix)) {
|
|
LOG(WARN) << "Found invalid CID prefix=" << hex_cid_prefix;
|
|
break;
|
|
}
|
|
|
|
if (LOG_ENABLED(INFO)) {
|
|
LOG(INFO) << "Inherit worker process CID prefix=" << hex_cid_prefix;
|
|
}
|
|
|
|
cid_prefixes.emplace_back();
|
|
|
|
util::decode_hex(std::begin(cid_prefixes.back()), hex_cid_prefix);
|
|
|
|
if (end == envend) {
|
|
break;
|
|
}
|
|
|
|
p = end + 1;
|
|
}
|
|
|
|
iwps.emplace_back(std::move(cid_prefixes), fd);
|
|
}
|
|
|
|
return iwps;
|
|
}
|
|
} // namespace
|
|
#endif // ENABLE_HTTP3
|
|
|
|
namespace {
|
|
int create_acceptor_socket(Config *config, std::vector<InheritedAddr> &iaddrs) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
auto &listenerconf = config->conn.listener;
|
|
|
|
for (auto &addr : listenerconf.addrs) {
|
|
if (addr.host_unix) {
|
|
if (create_unix_domain_server_socket(addr, iaddrs) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (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(addr.host.c_str(), config->uid, config->gid) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Changing owner of UNIX domain socket " << addr.host
|
|
<< " failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (create_tcp_server_socket(addr, iaddrs) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int call_daemon() {
|
|
#ifdef __sgi
|
|
return _daemonize(0, 0, 0, 0);
|
|
#else // !__sgi
|
|
# ifdef HAVE_LIBSYSTEMD
|
|
if (sd_booted() && (getenv("NOTIFY_SOCKET") != nullptr)) {
|
|
LOG(NOTICE) << "Daemonising disabled under systemd";
|
|
chdir("/");
|
|
return 0;
|
|
}
|
|
# endif // HAVE_LIBSYSTEMD
|
|
return util::daemonize(0, 0);
|
|
#endif // !__sgi
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Opens IPC socket used to communicate with worker proess. The
|
|
// communication is unidirectional; that is main process sends
|
|
// messages to the worker process. On success, ipc_fd[0] is for
|
|
// reading, and ipc_fd[1] for writing, just like pipe(2).
|
|
int create_ipc_socket(std::array<int, 2> &ipc_fd) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
int rv;
|
|
|
|
rv = pipe(ipc_fd.data());
|
|
if (rv == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Failed to create pipe to communicate worker process: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
auto fd = ipc_fd[i];
|
|
util::make_socket_nonblocking(fd);
|
|
util::make_socket_closeonexec(fd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
namespace {
|
|
int create_quic_ipc_socket(std::array<int, 2> &quic_ipc_fd) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
int rv;
|
|
|
|
rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, quic_ipc_fd.data());
|
|
if (rv == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Failed to create socket pair to communicate worker process: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
|
|
for (auto fd : quic_ipc_fd) {
|
|
util::make_socket_nonblocking(fd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int generate_cid_prefix(
|
|
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> &cid_prefixes,
|
|
const Config *config) {
|
|
auto &apiconf = config->api;
|
|
auto &quicconf = config->quic;
|
|
|
|
size_t num_cid_prefix;
|
|
if (config->single_thread) {
|
|
num_cid_prefix = 1;
|
|
} else {
|
|
num_cid_prefix = config->num_worker;
|
|
|
|
// API endpoint occupies the one dedicated worker thread.
|
|
// Although such worker never gets QUIC traffic, we create CID
|
|
// prefix for it to make code a bit simpler.
|
|
if (apiconf.enabled) {
|
|
++num_cid_prefix;
|
|
}
|
|
}
|
|
|
|
cid_prefixes.resize(num_cid_prefix);
|
|
|
|
for (auto &cid_prefix : cid_prefixes) {
|
|
if (create_cid_prefix(cid_prefix.data(), quicconf.server_id.data()) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
std::vector<QUICLingeringWorkerProcess>
|
|
collect_quic_lingering_worker_processes() {
|
|
std::vector<QUICLingeringWorkerProcess> quic_lwps{
|
|
std::begin(inherited_quic_lingering_worker_processes),
|
|
std::end(inherited_quic_lingering_worker_processes)};
|
|
|
|
for (auto &wp : worker_processes) {
|
|
quic_lwps.emplace_back(wp->cid_prefixes, wp->quic_ipc_fd);
|
|
}
|
|
|
|
return quic_lwps;
|
|
}
|
|
} // namespace
|
|
#endif // ENABLE_HTTP3
|
|
|
|
namespace {
|
|
// Creates worker process, and returns PID of worker process. On
|
|
// success, file descriptor for IPC (send only) is assigned to
|
|
// |main_ipc_fd|. In child process, we will close file descriptors
|
|
// which are inherited from previous configuration/process, but not
|
|
// used in the current configuration.
|
|
pid_t fork_worker_process(
|
|
int &main_ipc_fd
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
int &wp_quic_ipc_fd
|
|
#endif // ENABLE_HTTP3
|
|
,
|
|
const std::vector<InheritedAddr> &iaddrs
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
const std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>>
|
|
&cid_prefixes,
|
|
const std::vector<QUICLingeringWorkerProcess> &quic_lwps
|
|
#endif // ENABLE_HTTP3
|
|
) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
int rv;
|
|
sigset_t oldset;
|
|
|
|
std::array<int, 2> ipc_fd;
|
|
|
|
rv = create_ipc_socket(ipc_fd);
|
|
if (rv != 0) {
|
|
return -1;
|
|
}
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
std::array<int, 2> quic_ipc_fd;
|
|
|
|
rv = create_quic_ipc_socket(quic_ipc_fd);
|
|
if (rv != 0) {
|
|
return -1;
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
rv = shrpx_signal_block_all(&oldset);
|
|
if (rv != 0) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Blocking all signals failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
close(ipc_fd[0]);
|
|
close(ipc_fd[1]);
|
|
|
|
return -1;
|
|
}
|
|
|
|
auto config = get_config();
|
|
|
|
pid_t pid = 0;
|
|
|
|
if (!config->single_process) {
|
|
pid = fork();
|
|
}
|
|
|
|
if (pid == 0) {
|
|
ev_loop_fork(EV_DEFAULT);
|
|
|
|
for (auto &addr : config->conn.listener.addrs) {
|
|
util::make_socket_closeonexec(addr.fd);
|
|
}
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
util::make_socket_closeonexec(quic_ipc_fd[0]);
|
|
|
|
for (auto &lwp : quic_lwps) {
|
|
util::make_socket_closeonexec(lwp.quic_ipc_fd);
|
|
}
|
|
|
|
for (auto &wp : worker_processes) {
|
|
util::make_socket_closeonexec(wp->quic_ipc_fd);
|
|
// Do not close quic_ipc_fd.
|
|
wp->quic_ipc_fd = -1;
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
// Remove all WorkerProcesses to stop any registered watcher on
|
|
// default loop.
|
|
worker_process_remove_all(EV_DEFAULT);
|
|
|
|
close_unused_inherited_addr(iaddrs);
|
|
|
|
shrpx_signal_set_worker_proc_ign_handler();
|
|
|
|
rv = shrpx_signal_unblock_all();
|
|
if (rv != 0) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Unblocking all signals failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
if (config->single_process) {
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
nghttp2_Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
if (!config->single_process) {
|
|
close(ipc_fd[1]);
|
|
#ifdef ENABLE_HTTP3
|
|
close(quic_ipc_fd[1]);
|
|
#endif // ENABLE_HTTP3
|
|
}
|
|
|
|
WorkerProcessConfig wpconf{
|
|
.ipc_fd = ipc_fd[0],
|
|
#ifdef ENABLE_HTTP3
|
|
.cid_prefixes = cid_prefixes,
|
|
.quic_ipc_fd = quic_ipc_fd[0],
|
|
.quic_lingering_worker_processes = quic_lwps,
|
|
#endif // ENABLE_HTTP3
|
|
};
|
|
rv = worker_process_event_loop(&wpconf);
|
|
if (rv != 0) {
|
|
LOG(FATAL) << "Worker process returned error";
|
|
|
|
if (config->single_process) {
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
nghttp2_Exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
LOG(NOTICE) << "Worker process shutting down momentarily";
|
|
|
|
// call exit(...) instead of nghttp2_Exit to get leak sanitizer report
|
|
if (config->single_process) {
|
|
exit(EXIT_SUCCESS);
|
|
} else {
|
|
nghttp2_Exit(EXIT_SUCCESS);
|
|
}
|
|
}
|
|
|
|
// parent process
|
|
if (pid == -1) {
|
|
auto error = errno;
|
|
LOG(ERROR) << "Could not spawn worker process: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
|
|
rv = shrpx_signal_set(&oldset);
|
|
if (rv != 0) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Restoring signal mask failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (pid == -1) {
|
|
close(ipc_fd[0]);
|
|
close(ipc_fd[1]);
|
|
#ifdef ENABLE_HTTP3
|
|
close(quic_ipc_fd[0]);
|
|
close(quic_ipc_fd[1]);
|
|
#endif // ENABLE_HTTP3
|
|
|
|
return -1;
|
|
}
|
|
|
|
close(ipc_fd[0]);
|
|
#ifdef ENABLE_HTTP3
|
|
close(quic_ipc_fd[0]);
|
|
#endif // ENABLE_HTTP3
|
|
|
|
main_ipc_fd = ipc_fd[1];
|
|
#ifdef ENABLE_HTTP3
|
|
wp_quic_ipc_fd = quic_ipc_fd[1];
|
|
#endif // ENABLE_HTTP3
|
|
|
|
LOG(NOTICE) << "Worker process [" << pid << "] spawned";
|
|
|
|
return pid;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int event_loop() {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
|
|
shrpx_signal_set_main_proc_ign_handler();
|
|
|
|
auto config = mod_config();
|
|
|
|
if (config->daemon) {
|
|
if (call_daemon() == -1) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "Failed to daemonize: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
return -1;
|
|
}
|
|
|
|
// We get new PID after successful daemon().
|
|
mod_config()->pid = getpid();
|
|
|
|
// daemon redirects stderr file descriptor to /dev/null, so we
|
|
// need this.
|
|
redirect_stderr_to_errorlog(config->logging);
|
|
}
|
|
|
|
// update systemd PID tracking
|
|
shrpx_sd_notifyf(0, "MAINPID=%d\n", config->pid);
|
|
|
|
{
|
|
auto iaddrs = get_inherited_addr_from_env(config);
|
|
|
|
if (create_acceptor_socket(config, iaddrs) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
close_unused_inherited_addr(iaddrs);
|
|
}
|
|
|
|
auto orig_pid = get_orig_pid_from_env();
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
inherited_quic_lingering_worker_processes =
|
|
get_inherited_quic_lingering_worker_process_from_env();
|
|
#endif // ENABLE_HTTP3
|
|
|
|
auto loop = ev_default_loop(config->ev_loop_flags);
|
|
|
|
int ipc_fd = 0;
|
|
#ifdef ENABLE_HTTP3
|
|
int quic_ipc_fd = 0;
|
|
|
|
auto quic_lwps = collect_quic_lingering_worker_processes();
|
|
|
|
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
|
|
|
if (generate_cid_prefix(cid_prefixes, config) != 0) {
|
|
return -1;
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
auto pid = fork_worker_process(ipc_fd
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
quic_ipc_fd
|
|
#endif // ENABLE_HTTP3
|
|
,
|
|
{}
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
cid_prefixes, quic_lwps
|
|
#endif // ENABLE_HTTP3
|
|
);
|
|
|
|
if (pid == -1) {
|
|
return -1;
|
|
}
|
|
|
|
ev_timer_init(&worker_process_grace_period_timer,
|
|
worker_process_grace_period_timercb, 0., 0.);
|
|
|
|
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
quic_ipc_fd, cid_prefixes
|
|
#endif // ENABLE_HTTP3
|
|
));
|
|
|
|
// Write PID file when we are ready to accept connection from peer.
|
|
// This makes easier to write restart script for nghttpx. Because
|
|
// when we know that PID file is recreated, it means we can send
|
|
// QUIT signal to the old process to make it shutdown gracefully.
|
|
if (!config->pid_file.empty()) {
|
|
save_pid();
|
|
}
|
|
|
|
// ready to serve requests
|
|
shrpx_sd_notifyf(0, "READY=1");
|
|
|
|
if (orig_pid != -1) {
|
|
LOG(NOTICE) << "Send QUIT signal to the original main process to tell "
|
|
"that we are ready to serve requests.";
|
|
kill(orig_pid, SIGQUIT);
|
|
}
|
|
|
|
ev_run(loop, 0);
|
|
|
|
ev_timer_stop(loop, &worker_process_grace_period_timer);
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Returns true if regular file or symbolic link |path| exists.
|
|
bool conf_exists(const char *path) {
|
|
struct stat buf;
|
|
int rv = stat(path, &buf);
|
|
return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK));
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
constexpr auto DEFAULT_NPN_LIST =
|
|
StringRef::from_lit("h2,h2-16,h2-14,http/1.1");
|
|
} // namespace
|
|
|
|
namespace {
|
|
constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
|
|
#ifdef TLS1_3_VERSION
|
|
constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.3");
|
|
#else // !TLS1_3_VERSION
|
|
constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = StringRef::from_lit("TLSv1.2");
|
|
#endif // !TLS1_3_VERSION
|
|
} // namespace
|
|
|
|
namespace {
|
|
constexpr auto DEFAULT_ACCESSLOG_FORMAT =
|
|
StringRef::from_lit(R"($remote_addr - - [$time_local] )"
|
|
R"("$request" $status $body_bytes_sent )"
|
|
R"("$http_referer" "$http_user_agent")");
|
|
} // namespace
|
|
|
|
namespace {
|
|
void fill_default_config(Config *config) {
|
|
config->num_worker = 1;
|
|
config->conf_path = StringRef::from_lit("/etc/nghttpx/nghttpx.conf");
|
|
config->pid = getpid();
|
|
|
|
#ifdef NOTHREADS
|
|
config->single_thread = true;
|
|
#endif // NOTHREADS
|
|
|
|
if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
|
|
config->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE;
|
|
}
|
|
|
|
auto &tlsconf = config->tls;
|
|
{
|
|
auto &ticketconf = tlsconf.ticket;
|
|
{
|
|
auto &memcachedconf = ticketconf.memcached;
|
|
memcachedconf.max_retry = 3;
|
|
memcachedconf.max_fail = 2;
|
|
memcachedconf.interval = 10_min;
|
|
memcachedconf.family = AF_UNSPEC;
|
|
}
|
|
|
|
auto &session_cacheconf = tlsconf.session_cache;
|
|
{
|
|
auto &memcachedconf = session_cacheconf.memcached;
|
|
memcachedconf.family = AF_UNSPEC;
|
|
}
|
|
|
|
ticketconf.cipher = EVP_aes_128_cbc();
|
|
}
|
|
|
|
{
|
|
auto &ocspconf = tlsconf.ocsp;
|
|
// ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
|
|
ocspconf.update_interval = 4_h;
|
|
ocspconf.fetch_ocsp_response_file =
|
|
StringRef::from_lit(PKGDATADIR "/fetch-ocsp-response");
|
|
}
|
|
|
|
{
|
|
auto &dyn_recconf = tlsconf.dyn_rec;
|
|
dyn_recconf.warmup_threshold = 1_m;
|
|
dyn_recconf.idle_timeout = 1_s;
|
|
}
|
|
|
|
tlsconf.session_timeout = std::chrono::hours(12);
|
|
tlsconf.ciphers = StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
|
|
tlsconf.tls13_ciphers =
|
|
StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
|
|
tlsconf.client.ciphers =
|
|
StringRef::from_lit(nghttp2::tls::DEFAULT_CIPHER_LIST);
|
|
tlsconf.client.tls13_ciphers =
|
|
StringRef::from_lit(nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST);
|
|
tlsconf.min_proto_version =
|
|
tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
|
|
tlsconf.max_proto_version =
|
|
tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
|
|
tlsconf.max_early_data = 16_k;
|
|
#if OPENSSL_1_1_API || defined(OPENSSL_IS_BORINGSSL)
|
|
tlsconf.ecdh_curves = StringRef::from_lit("X25519:P-256:P-384:P-521");
|
|
#else // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
|
tlsconf.ecdh_curves = StringRef::from_lit("P-256:P-384:P-521");
|
|
#endif // !OPENSSL_1_1_API && !defined(OPENSSL_IS_BORINGSSL)
|
|
|
|
auto &httpconf = config->http;
|
|
httpconf.server_name = StringRef::from_lit("nghttpx");
|
|
httpconf.no_host_rewrite = true;
|
|
httpconf.request_header_field_buffer = 64_k;
|
|
httpconf.max_request_header_fields = 100;
|
|
httpconf.response_header_field_buffer = 64_k;
|
|
httpconf.max_response_header_fields = 500;
|
|
httpconf.redirect_https_port = StringRef::from_lit("443");
|
|
httpconf.max_requests = std::numeric_limits<size_t>::max();
|
|
httpconf.xfp.add = true;
|
|
httpconf.xfp.strip_incoming = true;
|
|
httpconf.early_data.strip_incoming = true;
|
|
|
|
auto &http2conf = config->http2;
|
|
{
|
|
auto &upstreamconf = http2conf.upstream;
|
|
|
|
{
|
|
auto &timeoutconf = upstreamconf.timeout;
|
|
timeoutconf.settings = 10_s;
|
|
}
|
|
|
|
// window size for HTTP/2 upstream connection per stream. 2**16-1
|
|
// = 64KiB-1, which is HTTP/2 default.
|
|
upstreamconf.window_size = 64_k - 1;
|
|
// HTTP/2 has connection-level flow control. The default window
|
|
// size for HTTP/2 is 64KiB - 1.
|
|
upstreamconf.connection_window_size = 64_k - 1;
|
|
upstreamconf.max_concurrent_streams = 100;
|
|
|
|
upstreamconf.encoder_dynamic_table_size = 4_k;
|
|
upstreamconf.decoder_dynamic_table_size = 4_k;
|
|
|
|
nghttp2_option_new(&upstreamconf.option);
|
|
nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1);
|
|
nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1);
|
|
nghttp2_option_set_max_deflate_dynamic_table_size(
|
|
upstreamconf.option, upstreamconf.encoder_dynamic_table_size);
|
|
nghttp2_option_set_server_fallback_rfc7540_priorities(upstreamconf.option,
|
|
1);
|
|
nghttp2_option_set_builtin_recv_extension_type(upstreamconf.option,
|
|
NGHTTP2_PRIORITY_UPDATE);
|
|
|
|
// For API endpoint, we enable automatic window update. This is
|
|
// because we are a sink.
|
|
nghttp2_option_new(&upstreamconf.alt_mode_option);
|
|
nghttp2_option_set_no_recv_client_magic(upstreamconf.alt_mode_option, 1);
|
|
nghttp2_option_set_max_deflate_dynamic_table_size(
|
|
upstreamconf.alt_mode_option, upstreamconf.encoder_dynamic_table_size);
|
|
}
|
|
|
|
http2conf.timeout.stream_write = 1_min;
|
|
|
|
{
|
|
auto &downstreamconf = http2conf.downstream;
|
|
|
|
{
|
|
auto &timeoutconf = downstreamconf.timeout;
|
|
timeoutconf.settings = 10_s;
|
|
}
|
|
|
|
downstreamconf.window_size = 64_k - 1;
|
|
downstreamconf.connection_window_size = (1u << 31) - 1;
|
|
downstreamconf.max_concurrent_streams = 100;
|
|
|
|
downstreamconf.encoder_dynamic_table_size = 4_k;
|
|
downstreamconf.decoder_dynamic_table_size = 4_k;
|
|
|
|
nghttp2_option_new(&downstreamconf.option);
|
|
nghttp2_option_set_no_auto_window_update(downstreamconf.option, 1);
|
|
nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100);
|
|
nghttp2_option_set_max_deflate_dynamic_table_size(
|
|
downstreamconf.option, downstreamconf.encoder_dynamic_table_size);
|
|
}
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
auto &quicconf = config->quic;
|
|
{
|
|
auto &upstreamconf = quicconf.upstream;
|
|
|
|
{
|
|
auto &timeoutconf = upstreamconf.timeout;
|
|
timeoutconf.idle = 30_s;
|
|
}
|
|
|
|
auto &bpfconf = quicconf.bpf;
|
|
bpfconf.prog_file = StringRef::from_lit(PKGLIBDIR "/reuseport_kern.o");
|
|
|
|
upstreamconf.congestion_controller = NGTCP2_CC_ALGO_CUBIC;
|
|
|
|
upstreamconf.initial_rtt =
|
|
static_cast<ev_tstamp>(NGTCP2_DEFAULT_INITIAL_RTT) / NGTCP2_SECONDS;
|
|
}
|
|
|
|
if (RAND_bytes(quicconf.server_id.data(), quicconf.server_id.size()) != 1) {
|
|
assert(0);
|
|
abort();
|
|
}
|
|
|
|
auto &http3conf = config->http3;
|
|
{
|
|
auto &upstreamconf = http3conf.upstream;
|
|
|
|
upstreamconf.max_concurrent_streams = 100;
|
|
upstreamconf.window_size = 256_k;
|
|
upstreamconf.connection_window_size = 1_m;
|
|
upstreamconf.max_window_size = 6_m;
|
|
upstreamconf.max_connection_window_size = 8_m;
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
auto &loggingconf = config->logging;
|
|
{
|
|
auto &accessconf = loggingconf.access;
|
|
accessconf.format =
|
|
parse_log_format(config->balloc, DEFAULT_ACCESSLOG_FORMAT);
|
|
|
|
auto &errorconf = loggingconf.error;
|
|
errorconf.file = StringRef::from_lit("/dev/stderr");
|
|
}
|
|
|
|
loggingconf.syslog_facility = LOG_DAEMON;
|
|
loggingconf.severity = NOTICE;
|
|
|
|
auto &connconf = config->conn;
|
|
{
|
|
auto &listenerconf = connconf.listener;
|
|
{
|
|
// Default accept() backlog
|
|
listenerconf.backlog = 65536;
|
|
listenerconf.timeout.sleep = 30_s;
|
|
}
|
|
}
|
|
|
|
{
|
|
auto &upstreamconf = connconf.upstream;
|
|
{
|
|
auto &timeoutconf = upstreamconf.timeout;
|
|
// Read timeout for HTTP2 upstream connection
|
|
timeoutconf.http2_read = 3_min;
|
|
|
|
// Read timeout for HTTP3 upstream connection
|
|
timeoutconf.http3_read = 3_min;
|
|
|
|
// Read timeout for non-HTTP2 upstream connection
|
|
timeoutconf.read = 1_min;
|
|
|
|
// Write timeout for HTTP2/non-HTTP2 upstream connection
|
|
timeoutconf.write = 30_s;
|
|
|
|
// Keep alive timeout for HTTP/1 upstream connection
|
|
timeoutconf.idle_read = 1_min;
|
|
}
|
|
}
|
|
|
|
{
|
|
connconf.downstream = std::make_shared<DownstreamConfig>();
|
|
auto &downstreamconf = *connconf.downstream;
|
|
{
|
|
auto &timeoutconf = downstreamconf.timeout;
|
|
// Read/Write timeouts for downstream connection
|
|
timeoutconf.read = 1_min;
|
|
timeoutconf.write = 30_s;
|
|
// Timeout for pooled (idle) connections
|
|
timeoutconf.idle_read = 2_s;
|
|
timeoutconf.connect = 30_s;
|
|
timeoutconf.max_backoff = 120_s;
|
|
}
|
|
|
|
downstreamconf.connections_per_host = 8;
|
|
downstreamconf.request_buffer_size = 16_k;
|
|
downstreamconf.response_buffer_size = 128_k;
|
|
downstreamconf.family = AF_UNSPEC;
|
|
}
|
|
|
|
auto &apiconf = config->api;
|
|
apiconf.max_request_body = 32_m;
|
|
|
|
auto &dnsconf = config->dns;
|
|
{
|
|
auto &timeoutconf = dnsconf.timeout;
|
|
timeoutconf.cache = 10_s;
|
|
timeoutconf.lookup = 5_s;
|
|
}
|
|
dnsconf.max_try = 2;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace {
|
|
void print_version(std::ostream &out) {
|
|
out << "nghttpx nghttp2/" NGHTTP2_VERSION
|
|
#ifdef ENABLE_HTTP3
|
|
" ngtcp2/" NGTCP2_VERSION " nghttp3/" NGHTTP3_VERSION
|
|
#endif // ENABLE_HTTP3
|
|
<< std::endl;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void print_usage(std::ostream &out) {
|
|
out << R"(Usage: nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
|
|
A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.)"
|
|
<< std::endl;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void print_help(std::ostream &out) {
|
|
auto config = get_config();
|
|
|
|
print_usage(out);
|
|
out << R"(
|
|
<PRIVATE_KEY>
|
|
Set path to server's private key. Required unless
|
|
"no-tls" parameter is used in --frontend option.
|
|
<CERT> Set path to server's certificate. Required unless
|
|
"no-tls" parameter is used in --frontend option. To
|
|
make OCSP stapling work, this must be an absolute path.
|
|
|
|
Options:
|
|
The options are categorized into several groups.
|
|
|
|
Connections:
|
|
-b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][[;<PARAM>]...]
|
|
|
|
Set backend host and port. The multiple backend
|
|
addresses are accepted by repeating this option. UNIX
|
|
domain socket can be specified by prefixing path name
|
|
with "unix:" (e.g., unix:/var/run/backend.sock).
|
|
|
|
Optionally, if <PATTERN>s are given, the backend address
|
|
is only used if request matches the pattern. The
|
|
pattern matching is closely designed to ServeMux in
|
|
net/http package of Go programming language. <PATTERN>
|
|
consists of path, host + path or just host. The path
|
|
must start with "/". If it ends with "/", it matches
|
|
all request path in its subtree. To deal with the
|
|
request to the directory without trailing slash, the
|
|
path which ends with "/" also matches the request path
|
|
which only lacks trailing '/' (e.g., path "/foo/"
|
|
matches request path "/foo"). If it does not end with
|
|
"/", it performs exact match against the request path.
|
|
If host is given, it performs a match against the
|
|
request host. For a request received on the frontend
|
|
listener with "sni-fwd" parameter enabled, SNI host is
|
|
used instead of a 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/"). CONNECT method is treated
|
|
specially. It does not have path, and we don't allow
|
|
empty path. To workaround this, we assume that CONNECT
|
|
method has "/" as path.
|
|
|
|
Patterns with host take precedence over patterns with
|
|
just path. Then, longer patterns take precedence over
|
|
shorter ones.
|
|
|
|
Host can include "*" in the left most position to
|
|
indicate wildcard match (only suffix match is done).
|
|
The "*" must match at least one character. For example,
|
|
host pattern "*.nghttp2.org" matches against
|
|
"www.nghttp2.org" and "git.ngttp2.org", but does not
|
|
match against "nghttp2.org". The exact hosts match
|
|
takes precedence over the wildcard hosts match.
|
|
|
|
If path part ends with "*", it is treated as wildcard
|
|
path. The wildcard path behaves differently from the
|
|
normal path. For normal path, match is made around the
|
|
boundary of path component separator,"/". On the other
|
|
hand, the wildcard path does not take into account the
|
|
path component separator. All paths which include the
|
|
wildcard path without last "*" as prefix, and are
|
|
strictly longer than wildcard path without last "*" are
|
|
matched. "*" must match at least one character. For
|
|
example, the pattern "/foo*" matches "/foo/" and
|
|
"/foobar". But it does not match "/foo", or "/fo".
|
|
|
|
If <PATTERN> is omitted or empty string, "/" is used as
|
|
pattern, which matches all request paths (catch-all
|
|
pattern). The catch-all backend must be given.
|
|
|
|
When doing a match, nghttpx made some normalization to
|
|
pattern, request host and path. For host part, they are
|
|
converted to lower case. For path part, percent-encoded
|
|
unreserved characters defined in RFC 3986 are decoded,
|
|
and any dot-segments (".." and ".") are resolved and
|
|
removed.
|
|
|
|
For example, -b'127.0.0.1,8080;nghttp2.org/httpbin/'
|
|
matches the request host "nghttp2.org" and the request
|
|
path "/httpbin/get", but does not match the request host
|
|
"nghttp2.org" and the request path "/index.html".
|
|
|
|
The multiple <PATTERN>s can be specified, delimiting
|
|
them by ":". Specifying
|
|
-b'127.0.0.1,8080;nghttp2.org:www.nghttp2.org' has the
|
|
same effect to specify -b'127.0.0.1,8080;nghttp2.org'
|
|
and -b'127.0.0.1,8080;www.nghttp2.org'.
|
|
|
|
The backend addresses sharing same <PATTERN> are grouped
|
|
together forming load balancing group.
|
|
|
|
Several parameters <PARAM> are accepted after <PATTERN>.
|
|
The parameters are delimited by ";". The available
|
|
parameters are: "proto=<PROTO>", "tls",
|
|
"sni=<SNI_HOST>", "fall=<N>", "rise=<N>",
|
|
"affinity=<METHOD>", "dns", "redirect-if-not-tls",
|
|
"upgrade-scheme", "mruby=<PATH>",
|
|
"read-timeout=<DURATION>", "write-timeout=<DURATION>",
|
|
"group=<GROUP>", "group-weight=<N>", "weight=<N>", and
|
|
"dnf". The parameter consists of keyword, and
|
|
optionally followed by "=" and value. For example, the
|
|
parameter "proto=h2" consists of the keyword "proto" and
|
|
value "h2". The parameter "tls" consists of the keyword
|
|
"tls" without value. Each parameter is described as
|
|
follows.
|
|
|
|
The backend application protocol can be specified using
|
|
optional "proto" parameter, and in the form of
|
|
"proto=<PROTO>". <PROTO> should be one of the following
|
|
list without quotes: "h2", "http/1.1". The default
|
|
value of <PROTO> is "http/1.1". Note that usually "h2"
|
|
refers to HTTP/2 over TLS. But in this option, it may
|
|
mean HTTP/2 over cleartext TCP unless "tls" keyword is
|
|
used (see below).
|
|
|
|
TLS can be enabled by specifying optional "tls"
|
|
parameter. TLS is not enabled by default.
|
|
|
|
With "sni=<SNI_HOST>" parameter, it can override the TLS
|
|
SNI field value with given <SNI_HOST>. This will
|
|
default to the backend <HOST> name
|
|
|
|
The feature to detect whether backend is online or
|
|
offline can be enabled using optional "fall" and "rise"
|
|
parameters. Using "fall=<N>" parameter, if nghttpx
|
|
cannot connect to a this backend <N> times in a row,
|
|
this backend is assumed to be offline, and it is
|
|
excluded from load balancing. If <N> is 0, this backend
|
|
never be excluded from load balancing whatever times
|
|
nghttpx cannot connect to it, and this is the default.
|
|
There is also "rise=<N>" parameter. After backend was
|
|
excluded from load balancing group, nghttpx periodically
|
|
attempts to make a connection to the failed backend, and
|
|
if the connection is made successfully <N> times in a
|
|
row, the backend is assumed to be online, and it is now
|
|
eligible for load balancing target. If <N> is 0, a
|
|
backend is permanently offline, once it goes in that
|
|
state, and this is the default behaviour.
|
|
|
|
The session affinity is enabled using
|
|
"affinity=<METHOD>" parameter. If "ip" is given in
|
|
<METHOD>, client IP based session affinity is enabled.
|
|
If "cookie" is given in <METHOD>, cookie based session
|
|
affinity is enabled. If "none" is given in <METHOD>,
|
|
session affinity is disabled, and this is the default.
|
|
The session affinity is enabled per <PATTERN>. If at
|
|
least one backend has "affinity" parameter, and its
|
|
<METHOD> is not "none", session affinity is enabled for
|
|
all backend servers sharing the same <PATTERN>. It is
|
|
advised to set "affinity" parameter to all backend
|
|
explicitly if session affinity is desired. The session
|
|
affinity may break if one of the backend gets
|
|
unreachable, or backend settings are reloaded or
|
|
replaced by API.
|
|
|
|
If "affinity=cookie" is used, the additional
|
|
configuration is required.
|
|
"affinity-cookie-name=<NAME>" must be used to specify a
|
|
name of cookie to use. Optionally,
|
|
"affinity-cookie-path=<PATH>" can be used to specify a
|
|
path which cookie is applied. The optional
|
|
"affinity-cookie-secure=<SECURE>" controls the Secure
|
|
attribute of a cookie. The default value is "auto", and
|
|
the Secure attribute is determined by a request scheme.
|
|
If a request scheme is "https", then Secure attribute is
|
|
set. Otherwise, it is not set. If <SECURE> is "yes",
|
|
the Secure attribute is always set. If <SECURE> is
|
|
"no", the Secure attribute is always omitted.
|
|
"affinity-cookie-stickiness=<STICKINESS>" controls
|
|
stickiness of this affinity. If <STICKINESS> is
|
|
"loose", removing or adding a backend server might break
|
|
the affinity and the request might be forwarded to a
|
|
different backend server. If <STICKINESS> is "strict",
|
|
removing the designated backend server breaks affinity,
|
|
but adding new backend server does not cause breakage.
|
|
If the designated backend server becomes unavailable,
|
|
new backend server is chosen as if the request does not
|
|
have an affinity cookie. <STICKINESS> defaults to
|
|
"loose".
|
|
|
|
By default, name resolution of backend host name is done
|
|
at start up, or reloading configuration. If "dns"
|
|
parameter is given, name resolution takes place
|
|
dynamically. This is useful if backend address changes
|
|
frequently. If "dns" is given, name resolution of
|
|
backend host name at start up, or reloading
|
|
configuration is skipped.
|
|
|
|
If "redirect-if-not-tls" parameter is used, the matched
|
|
backend requires that frontend connection is TLS
|
|
encrypted. If it isn't, nghttpx responds to the request
|
|
with 308 status code, and https URI the client should
|
|
use instead is included in Location header field. The
|
|
port number in redirect URI is 443 by default, and can
|
|
be changed using --redirect-https-port option. If at
|
|
least one backend has "redirect-if-not-tls" parameter,
|
|
this feature is enabled for all backend servers sharing
|
|
the same <PATTERN>. It is advised to set
|
|
"redirect-if-no-tls" parameter to all backends
|
|
explicitly if this feature is desired.
|
|
|
|
If "upgrade-scheme" parameter is used along with "tls"
|
|
parameter, HTTP/2 :scheme pseudo header field is changed
|
|
to "https" from "http" when forwarding a request to this
|
|
particular backend. This is a workaround for a backend
|
|
server which requires "https" :scheme pseudo header
|
|
field on TLS encrypted connection.
|
|
|
|
"mruby=<PATH>" parameter specifies a path to mruby
|
|
script file which is invoked when this pattern is
|
|
matched. All backends which share the same pattern must
|
|
have the same mruby path.
|
|
|
|
"read-timeout=<DURATION>" and "write-timeout=<DURATION>"
|
|
parameters specify the read and write timeout of the
|
|
backend connection when this pattern is matched. All
|
|
backends which share the same pattern must have the same
|
|
timeouts. If these timeouts are entirely omitted for a
|
|
pattern, --backend-read-timeout and
|
|
--backend-write-timeout are used.
|
|
|
|
"group=<GROUP>" parameter specifies the name of group
|
|
this backend address belongs to. By default, it belongs
|
|
to the unnamed default group. The name of group is
|
|
unique per pattern. "group-weight=<N>" parameter
|
|
specifies the weight of the group. The higher weight
|
|
gets more frequently selected by the load balancing
|
|
algorithm. <N> must be [1, 256] inclusive. The weight
|
|
8 has 4 times more weight than 2. <N> must be the same
|
|
for all addresses which share the same <GROUP>. If
|
|
"group-weight" is omitted in an address, but the other
|
|
address which belongs to the same group specifies
|
|
"group-weight", its weight is used. If no
|
|
"group-weight" is specified for all addresses, the
|
|
weight of a group becomes 1. "group" and "group-weight"
|
|
are ignored if session affinity is enabled.
|
|
|
|
"weight=<N>" parameter specifies the weight of the
|
|
backend address inside a group which this address
|
|
belongs to. The higher weight gets more frequently
|
|
selected by the load balancing algorithm. <N> must be
|
|
[1, 256] inclusive. The weight 8 has 4 times more
|
|
weight than weight 2. If this parameter is omitted,
|
|
weight becomes 1. "weight" is ignored if session
|
|
affinity is enabled.
|
|
|
|
If "dnf" parameter is specified, an incoming request is
|
|
not forwarded to a backend and just consumed along with
|
|
the request body (actually a backend server never be
|
|
contacted). It is expected that the HTTP response is
|
|
generated by mruby script (see "mruby=<PATH>" parameter
|
|
above). "dnf" is an abbreviation of "do not forward".
|
|
|
|
Since ";" and ":" are used as delimiter, <PATTERN> must
|
|
not contain these characters. In order to include ":"
|
|
in <PATTERN>, one has to specify "%3A" (which is
|
|
percent-encoded from of ":") instead. Since ";" has
|
|
special meaning in shell, the option value must be
|
|
quoted.
|
|
|
|
Default: )"
|
|
<< DEFAULT_DOWNSTREAM_HOST << "," << DEFAULT_DOWNSTREAM_PORT << R"(
|
|
-f, --frontend=(<HOST>,<PORT>|unix:<PATH>)[[;<PARAM>]...]
|
|
Set frontend host and port. If <HOST> is '*', it
|
|
assumes all addresses including both IPv4 and IPv6.
|
|
UNIX domain socket can be specified by prefixing path
|
|
name with "unix:" (e.g., unix:/var/run/nghttpx.sock).
|
|
This option can be used multiple times to listen to
|
|
multiple addresses.
|
|
|
|
This option can take 0 or more parameters, which are
|
|
described below. Note that "api" and "healthmon"
|
|
parameters are mutually exclusive.
|
|
|
|
Optionally, TLS can be disabled by specifying "no-tls"
|
|
parameter. TLS is enabled by default.
|
|
|
|
If "sni-fwd" parameter is used, when performing a match
|
|
to select a backend server, SNI host name received from
|
|
the client is used instead of the request host. See
|
|
--backend option about the pattern match.
|
|
|
|
To make this frontend as API endpoint, specify "api"
|
|
parameter. This is disabled by default. It is
|
|
important to limit the access to the API frontend.
|
|
Otherwise, someone may change the backend server, and
|
|
break your services, or expose confidential information
|
|
to the outside the world.
|
|
|
|
To make this frontend as health monitor endpoint,
|
|
specify "healthmon" parameter. This is disabled by
|
|
default. Any requests which come through this address
|
|
are replied with 200 HTTP status, without no body.
|
|
|
|
To accept PROXY protocol version 1 and 2 on frontend
|
|
connection, specify "proxyproto" parameter. This is
|
|
disabled by default.
|
|
|
|
To receive HTTP/3 (QUIC) traffic, specify "quic"
|
|
parameter. It makes nghttpx listen on UDP port rather
|
|
than TCP port. UNIX domain socket, "api", and
|
|
"healthmon" parameters cannot be used with "quic"
|
|
parameter.
|
|
|
|
Default: *,3000
|
|
--backlog=<N>
|
|
Set listen backlog size.
|
|
Default: )"
|
|
<< config->conn.listener.backlog << R"(
|
|
--backend-address-family=(auto|IPv4|IPv6)
|
|
Specify address family of backend connections. If
|
|
"auto" is given, both IPv4 and IPv6 are considered. If
|
|
"IPv4" is given, only IPv4 address is considered. If
|
|
"IPv6" is given, only IPv6 address is considered.
|
|
Default: auto
|
|
--backend-http-proxy-uri=<URI>
|
|
Specify proxy URI in the form
|
|
http://[<USER>:<PASS>@]<PROXY>:<PORT>. If a proxy
|
|
requires authentication, specify <USER> and <PASS>.
|
|
Note that they must be properly percent-encoded. This
|
|
proxy is used when the backend connection is HTTP/2.
|
|
First, make a CONNECT request to the proxy and it
|
|
connects to the backend on behalf of nghttpx. This
|
|
forms tunnel. After that, nghttpx performs SSL/TLS
|
|
handshake with the downstream through the tunnel. The
|
|
timeouts when connecting and making CONNECT request can
|
|
be specified by --backend-read-timeout and
|
|
--backend-write-timeout options.
|
|
|
|
Performance:
|
|
-n, --workers=<N>
|
|
Set the number of worker threads.
|
|
Default: )"
|
|
<< config->num_worker << R"(
|
|
--single-thread
|
|
Run everything in one thread inside the worker process.
|
|
This feature is provided for better debugging
|
|
experience, or for the platforms which lack thread
|
|
support. If threading is disabled, this option is
|
|
always enabled.
|
|
--read-rate=<SIZE>
|
|
Set maximum average read rate on frontend connection.
|
|
Setting 0 to this option means read rate is unlimited.
|
|
Default: )"
|
|
<< config->conn.upstream.ratelimit.read.rate << R"(
|
|
--read-burst=<SIZE>
|
|
Set maximum read burst size on frontend connection.
|
|
Setting 0 to this option means read burst size is
|
|
unlimited.
|
|
Default: )"
|
|
<< config->conn.upstream.ratelimit.read.burst << R"(
|
|
--write-rate=<SIZE>
|
|
Set maximum average write rate on frontend connection.
|
|
Setting 0 to this option means write rate is unlimited.
|
|
Default: )"
|
|
<< config->conn.upstream.ratelimit.write.rate << R"(
|
|
--write-burst=<SIZE>
|
|
Set maximum write burst size on frontend connection.
|
|
Setting 0 to this option means write burst size is
|
|
unlimited.
|
|
Default: )"
|
|
<< config->conn.upstream.ratelimit.write.burst << R"(
|
|
--worker-read-rate=<SIZE>
|
|
Set maximum average read rate on frontend connection per
|
|
worker. Setting 0 to this option means read rate is
|
|
unlimited. Not implemented yet.
|
|
Default: 0
|
|
--worker-read-burst=<SIZE>
|
|
Set maximum read burst size on frontend connection per
|
|
worker. Setting 0 to this option means read burst size
|
|
is unlimited. Not implemented yet.
|
|
Default: 0
|
|
--worker-write-rate=<SIZE>
|
|
Set maximum average write rate on frontend connection
|
|
per worker. Setting 0 to this option means write rate
|
|
is unlimited. Not implemented yet.
|
|
Default: 0
|
|
--worker-write-burst=<SIZE>
|
|
Set maximum write burst size on frontend connection per
|
|
worker. Setting 0 to this option means write burst size
|
|
is unlimited. Not implemented yet.
|
|
Default: 0
|
|
--worker-frontend-connections=<N>
|
|
Set maximum number of simultaneous connections frontend
|
|
accepts. Setting 0 means unlimited.
|
|
Default: )"
|
|
<< config->conn.upstream.worker_connections << R"(
|
|
--backend-connections-per-host=<N>
|
|
Set maximum number of backend concurrent connections
|
|
(and/or streams in case of HTTP/2) per origin host.
|
|
This option is meaningful 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: )"
|
|
<< config->conn.downstream->connections_per_host << R"(
|
|
--backend-connections-per-frontend=<N>
|
|
Set maximum number of backend concurrent connections
|
|
(and/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
|
|
with --http2-proxy option, use
|
|
--backend-connections-per-host.
|
|
Default: )"
|
|
<< config->conn.downstream->connections_per_frontend << R"(
|
|
--rlimit-nofile=<N>
|
|
Set maximum number of open files (RLIMIT_NOFILE) to <N>.
|
|
If 0 is given, nghttpx does not set the limit.
|
|
Default: )"
|
|
<< config->rlimit_nofile << R"(
|
|
--rlimit-memlock=<N>
|
|
Set maximum number of bytes of memory that may be locked
|
|
into RAM. If 0 is given, nghttpx does not set the
|
|
limit.
|
|
Default: )"
|
|
<< config->rlimit_memlock << R"(
|
|
--backend-request-buffer=<SIZE>
|
|
Set buffer size used to store backend request.
|
|
Default: )"
|
|
<< util::utos_unit(config->conn.downstream->request_buffer_size) << R"(
|
|
--backend-response-buffer=<SIZE>
|
|
Set buffer size used to store backend response.
|
|
Default: )"
|
|
<< util::utos_unit(config->conn.downstream->response_buffer_size) << R"(
|
|
--fastopen=<N>
|
|
Enables "TCP Fast Open" for the listening socket and
|
|
limits the maximum length for the queue of connections
|
|
that have not yet completed the three-way handshake. If
|
|
value is 0 then fast open is disabled.
|
|
Default: )"
|
|
<< config->conn.listener.fastopen << R"(
|
|
--no-kqueue Don't use kqueue. This option is only applicable for
|
|
the platforms which have kqueue. For other platforms,
|
|
this option will be simply ignored.
|
|
|
|
Timeout:
|
|
--frontend-http2-read-timeout=<DURATION>
|
|
Specify read timeout for HTTP/2 frontend connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.upstream.timeout.http2_read) << R"(
|
|
--frontend-http3-read-timeout=<DURATION>
|
|
Specify read timeout for HTTP/3 frontend connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.upstream.timeout.http3_read) << R"(
|
|
--frontend-read-timeout=<DURATION>
|
|
Specify read timeout for HTTP/1.1 frontend connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.upstream.timeout.read) << R"(
|
|
--frontend-write-timeout=<DURATION>
|
|
Specify write timeout for all frontend connections.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.upstream.timeout.write) << R"(
|
|
--frontend-keep-alive-timeout=<DURATION>
|
|
Specify keep-alive timeout for frontend HTTP/1
|
|
connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.upstream.timeout.idle_read) << R"(
|
|
--stream-read-timeout=<DURATION>
|
|
Specify read timeout for HTTP/2 streams. 0 means no
|
|
timeout.
|
|
Default: )"
|
|
<< util::duration_str(config->http2.timeout.stream_read) << R"(
|
|
--stream-write-timeout=<DURATION>
|
|
Specify write timeout for HTTP/2 streams. 0 means no
|
|
timeout.
|
|
Default: )"
|
|
<< util::duration_str(config->http2.timeout.stream_write) << R"(
|
|
--backend-read-timeout=<DURATION>
|
|
Specify read timeout for backend connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.downstream->timeout.read) << R"(
|
|
--backend-write-timeout=<DURATION>
|
|
Specify write timeout for backend connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.downstream->timeout.write) << R"(
|
|
--backend-connect-timeout=<DURATION>
|
|
Specify timeout before establishing TCP connection to
|
|
backend.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.downstream->timeout.connect) << R"(
|
|
--backend-keep-alive-timeout=<DURATION>
|
|
Specify keep-alive timeout for backend HTTP/1
|
|
connection.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.downstream->timeout.idle_read) << R"(
|
|
--listener-disable-timeout=<DURATION>
|
|
After accepting connection failed, connection listener
|
|
is disabled for a given amount of time. Specifying 0
|
|
disables this feature.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.listener.timeout.sleep) << R"(
|
|
--frontend-http2-setting-timeout=<DURATION>
|
|
Specify timeout before SETTINGS ACK is received from
|
|
client.
|
|
Default: )"
|
|
<< util::duration_str(config->http2.upstream.timeout.settings) << R"(
|
|
--backend-http2-settings-timeout=<DURATION>
|
|
Specify timeout before SETTINGS ACK is received from
|
|
backend server.
|
|
Default: )"
|
|
<< util::duration_str(config->http2.downstream.timeout.settings) << R"(
|
|
--backend-max-backoff=<DURATION>
|
|
Specify maximum backoff interval. This is used when
|
|
doing health check against offline backend (see "fail"
|
|
parameter in --backend option). It is also used to
|
|
limit the maximum interval to temporarily disable
|
|
backend when nghttpx failed to connect to it. These
|
|
intervals are calculated using exponential backoff, and
|
|
consecutive failed attempts increase the interval. This
|
|
option caps its maximum value.
|
|
Default: )"
|
|
<< util::duration_str(config->conn.downstream->timeout.max_backoff) << R"(
|
|
|
|
SSL/TLS:
|
|
--ciphers=<SUITE>
|
|
Set allowed cipher list for frontend connection. The
|
|
format of the string is described in OpenSSL ciphers(1).
|
|
This option sets cipher suites for TLSv1.2 or earlier.
|
|
Use --tls13-ciphers for TLSv1.3.
|
|
Default: )"
|
|
<< config->tls.ciphers << R"(
|
|
--tls13-ciphers=<SUITE>
|
|
Set allowed cipher list for frontend connection. The
|
|
format of the string is described in OpenSSL ciphers(1).
|
|
This option sets cipher suites for TLSv1.3. Use
|
|
--ciphers for TLSv1.2 or earlier.
|
|
Default: )"
|
|
<< config->tls.tls13_ciphers << R"(
|
|
--client-ciphers=<SUITE>
|
|
Set allowed cipher list for backend connection. The
|
|
format of the string is described in OpenSSL ciphers(1).
|
|
This option sets cipher suites for TLSv1.2 or earlier.
|
|
Use --tls13-client-ciphers for TLSv1.3.
|
|
Default: )"
|
|
<< config->tls.client.ciphers << R"(
|
|
--tls13-client-ciphers=<SUITE>
|
|
Set allowed cipher list for backend connection. The
|
|
format of the string is described in OpenSSL ciphers(1).
|
|
This option sets cipher suites for TLSv1.3. Use
|
|
--tls13-client-ciphers for TLSv1.2 or earlier.
|
|
Default: )"
|
|
<< config->tls.client.tls13_ciphers << R"(
|
|
--ecdh-curves=<LIST>
|
|
Set supported curve list for frontend connections.
|
|
<LIST> is a colon separated list of curve NID or names
|
|
in the preference order. The supported curves depend on
|
|
the linked OpenSSL library. This function requires
|
|
OpenSSL >= 1.0.2.
|
|
Default: )"
|
|
<< config->tls.ecdh_curves << R"(
|
|
-k, --insecure
|
|
Don't verify backend server's certificate if TLS is
|
|
enabled for backend connections.
|
|
--cacert=<PATH>
|
|
Set path to trusted CA certificate file. It is used in
|
|
backend TLS connections to verify peer's certificate.
|
|
It is also used to verify OCSP response from the script
|
|
set by --fetch-ocsp-response-file. The file must be in
|
|
PEM format. It can contain multiple certificates. If
|
|
the linked OpenSSL is configured to load system wide
|
|
certificates, they are loaded at startup regardless of
|
|
this option.
|
|
--private-key-passwd-file=<PATH>
|
|
Path to file that contains password for the server's
|
|
private key. If none is given and the private key is
|
|
password protected it'll be requested interactively.
|
|
--subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
|
|
Specify additional certificate and private key file.
|
|
nghttpx will choose certificates based on the hostname
|
|
indicated by client using TLS SNI extension. If nghttpx
|
|
is built with OpenSSL >= 1.0.2, the shared elliptic
|
|
curves (e.g., P-256) between client and server are also
|
|
taken into consideration. This allows nghttpx to send
|
|
ECDSA certificate to modern clients, while sending RSA
|
|
based certificate to older clients. This option can be
|
|
used multiple times. To make OCSP stapling work,
|
|
<CERTPATH> must be absolute path.
|
|
|
|
Additional parameter can be specified in <PARAM>. The
|
|
available <PARAM> is "sct-dir=<DIR>".
|
|
|
|
"sct-dir=<DIR>" specifies the path to directory which
|
|
contains *.sct files for TLS
|
|
signed_certificate_timestamp extension (RFC 6962). This
|
|
feature requires OpenSSL >= 1.0.2. See also
|
|
--tls-sct-dir option.
|
|
--dh-param-file=<PATH>
|
|
Path to file that contains DH parameters in PEM format.
|
|
Without this option, DHE cipher suites are not
|
|
available.
|
|
--npn-list=<LIST>
|
|
Comma delimited list of ALPN protocol identifier sorted
|
|
in the order of preference. That means most desirable
|
|
protocol comes first. This is used in both ALPN and
|
|
NPN. The parameter must be delimited by a single comma
|
|
only and any white spaces are treated as a part of
|
|
protocol string.
|
|
Default: )"
|
|
<< DEFAULT_NPN_LIST
|
|
<< R"(
|
|
--verify-client
|
|
Require and verify client certificate.
|
|
--verify-client-cacert=<PATH>
|
|
Path to file that contains CA certificates to verify
|
|
client certificate. The file must be in PEM format. It
|
|
can contain multiple certificates.
|
|
--verify-client-tolerate-expired
|
|
Accept expired client certificate. Operator should
|
|
handle the expired client certificate by some means
|
|
(e.g., mruby script). Otherwise, this option might
|
|
cause a security risk.
|
|
--client-private-key-file=<PATH>
|
|
Path to file that contains client private key used in
|
|
backend client authentication.
|
|
--client-cert-file=<PATH>
|
|
Path to file that contains client certificate used in
|
|
backend client authentication.
|
|
--tls-min-proto-version=<VER>
|
|
Specify minimum SSL/TLS protocol. The name matching is
|
|
done in case-insensitive manner. The versions between
|
|
--tls-min-proto-version and --tls-max-proto-version are
|
|
enabled. If the protocol list advertised by client does
|
|
not overlap this range, you will receive the error
|
|
message "unknown protocol". If a protocol version lower
|
|
than TLSv1.2 is specified, make sure that the compatible
|
|
ciphers are included in --ciphers option. The default
|
|
cipher list only includes ciphers compatible with
|
|
TLSv1.2 or above. The available versions are:
|
|
)"
|
|
#ifdef TLS1_3_VERSION
|
|
"TLSv1.3, "
|
|
#endif // TLS1_3_VERSION
|
|
"TLSv1.2, TLSv1.1, and TLSv1.0"
|
|
R"(
|
|
Default: )"
|
|
<< DEFAULT_TLS_MIN_PROTO_VERSION
|
|
<< R"(
|
|
--tls-max-proto-version=<VER>
|
|
Specify maximum SSL/TLS protocol. The name matching is
|
|
done in case-insensitive manner. The versions between
|
|
--tls-min-proto-version and --tls-max-proto-version are
|
|
enabled. If the protocol list advertised by client does
|
|
not overlap this range, you will receive the error
|
|
message "unknown protocol". The available versions are:
|
|
)"
|
|
#ifdef TLS1_3_VERSION
|
|
"TLSv1.3, "
|
|
#endif // TLS1_3_VERSION
|
|
"TLSv1.2, TLSv1.1, and TLSv1.0"
|
|
R"(
|
|
Default: )"
|
|
<< DEFAULT_TLS_MAX_PROTO_VERSION << R"(
|
|
--tls-ticket-key-file=<PATH>
|
|
Path to file that contains random data to construct TLS
|
|
session ticket parameters. If aes-128-cbc is given in
|
|
--tls-ticket-key-cipher, the file must contain exactly
|
|
48 bytes. If aes-256-cbc is given in
|
|
--tls-ticket-key-cipher, the file must contain exactly
|
|
80 bytes. This options can be used repeatedly to
|
|
specify multiple ticket parameters. If several files
|
|
are given, only the first key is used to encrypt TLS
|
|
session tickets. Other keys are accepted but server
|
|
will issue new session ticket with first key. This
|
|
allows session key rotation. Please note that key
|
|
rotation does not occur automatically. User should
|
|
rearrange files or change options values and restart
|
|
nghttpx gracefully. If opening or reading given file
|
|
fails, all loaded keys are discarded and it is treated
|
|
as if none of this option is given. If this option is
|
|
not given or an error occurred while opening or reading
|
|
a file, key is generated every 1 hour internally and
|
|
they are valid for 12 hours. This is recommended if
|
|
ticket key sharing between nghttpx instances is not
|
|
required.
|
|
--tls-ticket-key-memcached=<HOST>,<PORT>[;tls]
|
|
Specify address of memcached server to get TLS ticket
|
|
keys for session resumption. This enables shared TLS
|
|
ticket key between multiple nghttpx instances. nghttpx
|
|
does not set TLS ticket key to memcached. The external
|
|
ticket key generator is required. nghttpx just gets TLS
|
|
ticket keys from memcached, and use them, possibly
|
|
replacing current set of keys. It is up to extern TLS
|
|
ticket key generator to rotate keys frequently. See
|
|
"TLS SESSION TICKET RESUMPTION" section in manual page
|
|
to know the data format in memcached entry. Optionally,
|
|
memcached connection can be encrypted with TLS by
|
|
specifying "tls" parameter.
|
|
--tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)
|
|
Specify address family of memcached connections to get
|
|
TLS ticket keys. If "auto" is given, both IPv4 and IPv6
|
|
are considered. If "IPv4" is given, only IPv4 address
|
|
is considered. If "IPv6" is given, only IPv6 address is
|
|
considered.
|
|
Default: auto
|
|
--tls-ticket-key-memcached-interval=<DURATION>
|
|
Set interval to get TLS ticket keys from memcached.
|
|
Default: )"
|
|
<< util::duration_str(config->tls.ticket.memcached.interval) << R"(
|
|
--tls-ticket-key-memcached-max-retry=<N>
|
|
Set maximum number of consecutive retries before
|
|
abandoning TLS ticket key retrieval. If this number is
|
|
reached, the attempt is considered as failure, and
|
|
"failure" count is incremented by 1, which contributed
|
|
to the value controlled
|
|
--tls-ticket-key-memcached-max-fail option.
|
|
Default: )"
|
|
<< config->tls.ticket.memcached.max_retry << R"(
|
|
--tls-ticket-key-memcached-max-fail=<N>
|
|
Set maximum number of consecutive failure before
|
|
disabling TLS ticket until next scheduled key retrieval.
|
|
Default: )"
|
|
<< config->tls.ticket.memcached.max_fail << R"(
|
|
--tls-ticket-key-cipher=<CIPHER>
|
|
Specify cipher to encrypt TLS session ticket. Specify
|
|
either aes-128-cbc or aes-256-cbc. By default,
|
|
aes-128-cbc is used.
|
|
--tls-ticket-key-memcached-cert-file=<PATH>
|
|
Path to client certificate for memcached connections to
|
|
get TLS ticket keys.
|
|
--tls-ticket-key-memcached-private-key-file=<PATH>
|
|
Path to client private key for memcached connections to
|
|
get TLS ticket keys.
|
|
--fetch-ocsp-response-file=<PATH>
|
|
Path to fetch-ocsp-response script file. It should be
|
|
absolute path.
|
|
Default: )"
|
|
<< config->tls.ocsp.fetch_ocsp_response_file << R"(
|
|
--ocsp-update-interval=<DURATION>
|
|
Set interval to update OCSP response cache.
|
|
Default: )"
|
|
<< util::duration_str(config->tls.ocsp.update_interval) << R"(
|
|
--ocsp-startup
|
|
Start accepting connections after initial attempts to
|
|
get OCSP responses finish. It does not matter some of
|
|
the attempts fail. This feature is useful if OCSP
|
|
responses must be available before accepting
|
|
connections.
|
|
--no-verify-ocsp
|
|
nghttpx does not verify OCSP response.
|
|
--no-ocsp Disable OCSP stapling.
|
|
--tls-session-cache-memcached=<HOST>,<PORT>[;tls]
|
|
Specify address of memcached server to store session
|
|
cache. This enables shared session cache between
|
|
multiple nghttpx instances. Optionally, memcached
|
|
connection can be encrypted with TLS by specifying "tls"
|
|
parameter.
|
|
--tls-session-cache-memcached-address-family=(auto|IPv4|IPv6)
|
|
Specify address family of memcached connections to store
|
|
session cache. If "auto" is given, both IPv4 and IPv6
|
|
are considered. If "IPv4" is given, only IPv4 address
|
|
is considered. If "IPv6" is given, only IPv6 address is
|
|
considered.
|
|
Default: auto
|
|
--tls-session-cache-memcached-cert-file=<PATH>
|
|
Path to client certificate for memcached connections to
|
|
store session cache.
|
|
--tls-session-cache-memcached-private-key-file=<PATH>
|
|
Path to client private key for memcached connections to
|
|
store session cache.
|
|
--tls-dyn-rec-warmup-threshold=<SIZE>
|
|
Specify the threshold size for TLS dynamic record size
|
|
behaviour. During a TLS session, after the threshold
|
|
number of bytes have been written, the TLS record size
|
|
will be increased to the maximum allowed (16K). The max
|
|
record size will continue to be used on the active TLS
|
|
session. After --tls-dyn-rec-idle-timeout has elapsed,
|
|
the record size is reduced to 1300 bytes. Specify 0 to
|
|
always use the maximum record size, regardless of idle
|
|
period. This behaviour applies to all TLS based
|
|
frontends, and TLS HTTP/2 backends.
|
|
Default: )"
|
|
<< util::utos_unit(config->tls.dyn_rec.warmup_threshold) << R"(
|
|
--tls-dyn-rec-idle-timeout=<DURATION>
|
|
Specify TLS dynamic record size behaviour timeout. See
|
|
--tls-dyn-rec-warmup-threshold for more information.
|
|
This behaviour applies to all TLS based frontends, and
|
|
TLS HTTP/2 backends.
|
|
Default: )"
|
|
<< util::duration_str(config->tls.dyn_rec.idle_timeout) << R"(
|
|
--no-http2-cipher-block-list
|
|
Allow block listed cipher suite on frontend HTTP/2
|
|
connection. See
|
|
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
|
complete HTTP/2 cipher suites block list.
|
|
--client-no-http2-cipher-block-list
|
|
Allow block listed cipher suite on backend HTTP/2
|
|
connection. See
|
|
https://tools.ietf.org/html/rfc7540#appendix-A for the
|
|
complete HTTP/2 cipher suites block list.
|
|
--tls-sct-dir=<DIR>
|
|
Specifies the directory where *.sct files exist. All
|
|
*.sct files in <DIR> are read, and sent as
|
|
extension_data of TLS signed_certificate_timestamp (RFC
|
|
6962) to client. These *.sct files are for the
|
|
certificate specified in positional command-line
|
|
argument <CERT>, or certificate option in configuration
|
|
file. For additional certificates, use --subcert
|
|
option. This option requires OpenSSL >= 1.0.2.
|
|
--psk-secrets=<PATH>
|
|
Read list of PSK identity and secrets from <PATH>. This
|
|
is used for frontend connection. The each line of input
|
|
file is formatted as <identity>:<hex-secret>, where
|
|
<identity> is PSK identity, and <hex-secret> is secret
|
|
in hex. An empty line, and line which starts with '#'
|
|
are skipped. The default enabled cipher list might not
|
|
contain any PSK cipher suite. In that case, desired PSK
|
|
cipher suites must be enabled using --ciphers option.
|
|
The desired PSK cipher suite may be block listed by
|
|
HTTP/2. To use those cipher suites with HTTP/2,
|
|
consider to use --no-http2-cipher-block-list option.
|
|
But be aware its implications.
|
|
--client-psk-secrets=<PATH>
|
|
Read PSK identity and secrets from <PATH>. This is used
|
|
for backend connection. The each line of input file is
|
|
formatted as <identity>:<hex-secret>, where <identity>
|
|
is PSK identity, and <hex-secret> is secret in hex. An
|
|
empty line, and line which starts with '#' are skipped.
|
|
The first identity and secret pair encountered is used.
|
|
The default enabled cipher list might not contain any
|
|
PSK cipher suite. In that case, desired PSK cipher
|
|
suites must be enabled using --client-ciphers option.
|
|
The desired PSK cipher suite may be block listed by
|
|
HTTP/2. To use those cipher suites with HTTP/2,
|
|
consider to use --client-no-http2-cipher-block-list
|
|
option. But be aware its implications.
|
|
--tls-no-postpone-early-data
|
|
By default, except for QUIC connections, nghttpx
|
|
postpones forwarding HTTP requests sent in early data,
|
|
including those sent in partially in it, until TLS
|
|
handshake finishes. If all backend server recognizes
|
|
"Early-Data" header field, using this option makes
|
|
nghttpx not postpone forwarding request and get full
|
|
potential of 0-RTT data.
|
|
--tls-max-early-data=<SIZE>
|
|
Sets the maximum amount of 0-RTT data that server
|
|
accepts.
|
|
Default: )"
|
|
<< util::utos_unit(config->tls.max_early_data) << R"(
|
|
--tls-ktls Enable ktls. For server, ktls is enable if
|
|
--tls-session-cache-memcached is not configured.
|
|
|
|
HTTP/2:
|
|
-c, --frontend-http2-max-concurrent-streams=<N>
|
|
Set the maximum number of the concurrent streams in one
|
|
frontend HTTP/2 session.
|
|
Default: )"
|
|
<< config->http2.upstream.max_concurrent_streams << R"(
|
|
--backend-http2-max-concurrent-streams=<N>
|
|
Set the maximum number of the concurrent streams in one
|
|
backend HTTP/2 session. This sets maximum number of
|
|
concurrent opened pushed streams. The maximum number of
|
|
concurrent requests are set by a remote server.
|
|
Default: )"
|
|
<< config->http2.downstream.max_concurrent_streams << R"(
|
|
--frontend-http2-window-size=<SIZE>
|
|
Sets the per-stream initial window size of HTTP/2
|
|
frontend connection.
|
|
Default: )"
|
|
<< config->http2.upstream.window_size << R"(
|
|
--frontend-http2-connection-window-size=<SIZE>
|
|
Sets the per-connection window size of HTTP/2 frontend
|
|
connection.
|
|
Default: )"
|
|
<< config->http2.upstream.connection_window_size << R"(
|
|
--backend-http2-window-size=<SIZE>
|
|
Sets the initial window size of HTTP/2 backend
|
|
connection.
|
|
Default: )"
|
|
<< config->http2.downstream.window_size << R"(
|
|
--backend-http2-connection-window-size=<SIZE>
|
|
Sets the per-connection window size of HTTP/2 backend
|
|
connection.
|
|
Default: )"
|
|
<< config->http2.downstream.connection_window_size << R"(
|
|
--http2-no-cookie-crumbling
|
|
Don't crumble cookie header field.
|
|
--padding=<N>
|
|
Add at most <N> bytes to a HTTP/2 frame payload as
|
|
padding. Specify 0 to disable padding. This option is
|
|
meant for debugging purpose and not intended to enhance
|
|
protocol security.
|
|
--no-server-push
|
|
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 in default mode. In this case, server push from
|
|
backend session is relayed to frontend, and server push
|
|
via Link header field is also supported.
|
|
--frontend-http2-optimize-write-buffer-size
|
|
(Experimental) Enable write buffer size optimization in
|
|
frontend HTTP/2 TLS connection. This optimization aims
|
|
to reduce write buffer size so that it only contains
|
|
bytes which can send immediately. This makes server
|
|
more responsive to prioritized HTTP/2 stream because the
|
|
buffering of lower priority stream is reduced. This
|
|
option is only effective on recent Linux platform.
|
|
--frontend-http2-optimize-window-size
|
|
(Experimental) Automatically tune connection level
|
|
window size of frontend HTTP/2 TLS connection. If this
|
|
feature is enabled, connection window size starts with
|
|
the default window size, 65535 bytes. nghttpx
|
|
automatically adjusts connection window size based on
|
|
TCP receiving window size. The maximum window size is
|
|
capped by the value specified by
|
|
--frontend-http2-connection-window-size. Since the
|
|
stream is subject to stream level window size, it should
|
|
be adjusted using --frontend-http2-window-size option as
|
|
well. This option is only effective on recent Linux
|
|
platform.
|
|
--frontend-http2-encoder-dynamic-table-size=<SIZE>
|
|
Specify the maximum dynamic table size of HPACK encoder
|
|
in the frontend HTTP/2 connection. The decoder (client)
|
|
specifies the maximum dynamic table size it accepts.
|
|
Then the negotiated dynamic table size is the minimum of
|
|
this option value and the value which client specified.
|
|
Default: )"
|
|
<< util::utos_unit(config->http2.upstream.encoder_dynamic_table_size)
|
|
<< R"(
|
|
--frontend-http2-decoder-dynamic-table-size=<SIZE>
|
|
Specify the maximum dynamic table size of HPACK decoder
|
|
in the frontend HTTP/2 connection.
|
|
Default: )"
|
|
<< util::utos_unit(config->http2.upstream.decoder_dynamic_table_size)
|
|
<< R"(
|
|
--backend-http2-encoder-dynamic-table-size=<SIZE>
|
|
Specify the maximum dynamic table size of HPACK encoder
|
|
in the backend HTTP/2 connection. The decoder (backend)
|
|
specifies the maximum dynamic table size it accepts.
|
|
Then the negotiated dynamic table size is the minimum of
|
|
this option value and the value which backend specified.
|
|
Default: )"
|
|
<< util::utos_unit(config->http2.downstream.encoder_dynamic_table_size)
|
|
<< R"(
|
|
--backend-http2-decoder-dynamic-table-size=<SIZE>
|
|
Specify the maximum dynamic table size of HPACK decoder
|
|
in the backend HTTP/2 connection.
|
|
Default: )"
|
|
<< util::utos_unit(config->http2.downstream.decoder_dynamic_table_size)
|
|
<< R"(
|
|
|
|
Mode:
|
|
(default mode)
|
|
Accept HTTP/2, and HTTP/1.1 over SSL/TLS. "no-tls"
|
|
parameter is used in --frontend option, accept HTTP/2
|
|
and HTTP/1.1 over cleartext TCP. The incoming HTTP/1.1
|
|
connection can be upgraded to HTTP/2 through HTTP
|
|
Upgrade.
|
|
-s, --http2-proxy
|
|
Like default mode, but enable forward proxy. This is so
|
|
called HTTP/2 proxy mode.
|
|
|
|
Logging:
|
|
-L, --log-level=<LEVEL>
|
|
Set the severity level of log output. <LEVEL> must be
|
|
one of INFO, NOTICE, WARN, ERROR and FATAL.
|
|
Default: NOTICE
|
|
--accesslog-file=<PATH>
|
|
Set path to write access log. To reopen file, send USR1
|
|
signal to nghttpx.
|
|
--accesslog-syslog
|
|
Send access log to syslog. If this option is used,
|
|
--accesslog-file option is ignored.
|
|
--accesslog-format=<FORMAT>
|
|
Specify format string for access log. The default
|
|
format is combined format. The following variables are
|
|
available:
|
|
|
|
* $remote_addr: client IP address.
|
|
* $time_local: local time in Common Log format.
|
|
* $time_iso8601: local time in ISO 8601 format.
|
|
* $request: HTTP request line.
|
|
* $status: HTTP response status code.
|
|
* $body_bytes_sent: the number of bytes sent to client
|
|
as response body.
|
|
* $http_<VAR>: value of HTTP request header <VAR> where
|
|
'_' in <VAR> is replaced with '-'.
|
|
* $remote_port: client port.
|
|
* $server_port: server port.
|
|
* $request_time: request processing time in seconds with
|
|
milliseconds resolution.
|
|
* $pid: PID of the running process.
|
|
* $alpn: ALPN identifier of the protocol which generates
|
|
the response. For HTTP/1, ALPN is always http/1.1,
|
|
regardless of minor version.
|
|
* $tls_cipher: cipher used for SSL/TLS connection.
|
|
* $tls_client_fingerprint_sha256: SHA-256 fingerprint of
|
|
client certificate.
|
|
* $tls_client_fingerprint_sha1: SHA-1 fingerprint of
|
|
client certificate.
|
|
* $tls_client_subject_name: subject name in client
|
|
certificate.
|
|
* $tls_client_issuer_name: issuer name in client
|
|
certificate.
|
|
* $tls_client_serial: serial number in client
|
|
certificate.
|
|
* $tls_protocol: protocol for SSL/TLS connection.
|
|
* $tls_session_id: session ID for SSL/TLS connection.
|
|
* $tls_session_reused: "r" if SSL/TLS session was
|
|
reused. Otherwise, "."
|
|
* $tls_sni: SNI server name for SSL/TLS connection.
|
|
* $backend_host: backend host used to fulfill the
|
|
request. "-" if backend host is not available.
|
|
* $backend_port: backend port used to fulfill the
|
|
request. "-" if backend host is not available.
|
|
* $method: HTTP method
|
|
* $path: Request path including query. For CONNECT
|
|
request, authority is recorded.
|
|
* $path_without_query: $path up to the first '?'
|
|
character. For CONNECT request, authority is
|
|
recorded.
|
|
* $protocol_version: HTTP version (e.g., HTTP/1.1,
|
|
HTTP/2)
|
|
|
|
The variable can be enclosed by "{" and "}" for
|
|
disambiguation (e.g., ${remote_addr}).
|
|
|
|
Default: )"
|
|
<< DEFAULT_ACCESSLOG_FORMAT << R"(
|
|
--accesslog-write-early
|
|
Write access log when response header fields are
|
|
received from backend rather than when request
|
|
transaction finishes.
|
|
--errorlog-file=<PATH>
|
|
Set path to write error log. To reopen file, send USR1
|
|
signal to nghttpx. stderr will be redirected to the
|
|
error log file unless --errorlog-syslog is used.
|
|
Default: )"
|
|
<< config->logging.error.file << R"(
|
|
--errorlog-syslog
|
|
Send error log to syslog. If this option is used,
|
|
--errorlog-file option is ignored.
|
|
--syslog-facility=<FACILITY>
|
|
Set syslog facility to <FACILITY>.
|
|
Default: )"
|
|
<< str_syslog_facility(config->logging.syslog_facility) << R"(
|
|
|
|
HTTP:
|
|
--add-x-forwarded-for
|
|
Append X-Forwarded-For header field to the downstream
|
|
request.
|
|
--strip-incoming-x-forwarded-for
|
|
Strip X-Forwarded-For header field from inbound client
|
|
requests.
|
|
--no-add-x-forwarded-proto
|
|
Don't append additional X-Forwarded-Proto header field
|
|
to the backend request. If inbound client sets
|
|
X-Forwarded-Proto, and
|
|
--no-strip-incoming-x-forwarded-proto option is used,
|
|
they are passed to the backend.
|
|
--no-strip-incoming-x-forwarded-proto
|
|
Don't strip X-Forwarded-Proto header field from inbound
|
|
client requests.
|
|
--add-forwarded=<LIST>
|
|
Append RFC 7239 Forwarded header field with parameters
|
|
specified in comma delimited list <LIST>. The supported
|
|
parameters are "by", "for", "host", and "proto". By
|
|
default, the value of "by" and "for" parameters are
|
|
obfuscated string. See --forwarded-by and
|
|
--forwarded-for options respectively. Note that nghttpx
|
|
does not translate non-standard X-Forwarded-* header
|
|
fields into Forwarded header field, and vice versa.
|
|
--strip-incoming-forwarded
|
|
Strip Forwarded header field from inbound client
|
|
requests.
|
|
--forwarded-by=(obfuscated|ip|<VALUE>)
|
|
Specify the parameter value sent out with "by" parameter
|
|
of Forwarded header field. If "obfuscated" is given,
|
|
the string is randomly generated at startup. If "ip" is
|
|
given, the interface address of the connection,
|
|
including port number, is sent with "by" parameter. In
|
|
case of UNIX domain socket, "localhost" is used instead
|
|
of address and port. User can also specify the static
|
|
obfuscated string. The limitation is that it must start
|
|
with "_", and only consists of character set
|
|
[A-Za-z0-9._-], as described in RFC 7239.
|
|
Default: obfuscated
|
|
--forwarded-for=(obfuscated|ip)
|
|
Specify the parameter value sent out with "for"
|
|
parameter of Forwarded header field. If "obfuscated" is
|
|
given, the string is randomly generated for each client
|
|
connection. If "ip" is given, the remote client address
|
|
of the connection, without port number, is sent with
|
|
"for" parameter. In case of UNIX domain socket,
|
|
"localhost" is used instead of address.
|
|
Default: obfuscated
|
|
--no-via Don't append to Via header field. If Via header field
|
|
is received, it is left unaltered.
|
|
--no-strip-incoming-early-data
|
|
Don't strip Early-Data header field from inbound client
|
|
requests.
|
|
--no-location-rewrite
|
|
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 in default
|
|
mode. When --http2-proxy is used, these headers will
|
|
not be altered regardless of this option.
|
|
--altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
|
Specify protocol ID, port, host and origin of
|
|
alternative service. <HOST>, <ORIGIN> and <PARAMS> are
|
|
optional. Empty <HOST> and <ORIGIN> are allowed and
|
|
they are treated as nothing is specified. They are
|
|
advertised in alt-svc header field only in HTTP/1.1
|
|
frontend. This option can be used multiple times to
|
|
specify multiple alternative services.
|
|
Example: --altsvc="h2,443,,,ma=3600; persist=1"
|
|
--http2-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
|
|
Just like --altsvc option, but this altsvc is only sent
|
|
in HTTP/2 frontend.
|
|
--add-request-header=<HEADER>
|
|
Specify additional header field to add to request header
|
|
set. This option just appends header field and won't
|
|
replace anything already set. This option can be used
|
|
several times to specify multiple header fields.
|
|
Example: --add-request-header="foo: bar"
|
|
--add-response-header=<HEADER>
|
|
Specify additional header field to add to response
|
|
header set. This option just appends header field and
|
|
won't replace anything already set. This option can be
|
|
used several times to specify multiple header fields.
|
|
Example: --add-response-header="foo: bar"
|
|
--request-header-field-buffer=<SIZE>
|
|
Set maximum buffer size for incoming HTTP request header
|
|
field list. This is the sum of header name and value in
|
|
bytes. If trailer fields exist, they are counted
|
|
towards this number.
|
|
Default: )"
|
|
<< util::utos_unit(config->http.request_header_field_buffer) << R"(
|
|
--max-request-header-fields=<N>
|
|
Set maximum number of incoming HTTP request header
|
|
fields. If trailer fields exist, they are counted
|
|
towards this number.
|
|
Default: )"
|
|
<< config->http.max_request_header_fields << R"(
|
|
--response-header-field-buffer=<SIZE>
|
|
Set maximum buffer size for incoming HTTP response
|
|
header field list. This is the sum of header name and
|
|
value in bytes. If trailer fields exist, they are
|
|
counted towards this number.
|
|
Default: )"
|
|
<< util::utos_unit(config->http.response_header_field_buffer) << R"(
|
|
--max-response-header-fields=<N>
|
|
Set maximum number of incoming HTTP response header
|
|
fields. If trailer fields exist, they are counted
|
|
towards this number.
|
|
Default: )"
|
|
<< config->http.max_response_header_fields << R"(
|
|
--error-page=(<CODE>|*)=<PATH>
|
|
Set file path to custom error page served when nghttpx
|
|
originally generates HTTP error status code <CODE>.
|
|
<CODE> must be greater than or equal to 400, and at most
|
|
599. If "*" is used instead of <CODE>, it matches all
|
|
HTTP status code. If error status code comes from
|
|
backend server, the custom error pages are not used.
|
|
--server-name=<NAME>
|
|
Change server response header field value to <NAME>.
|
|
Default: )"
|
|
<< config->http.server_name << R"(
|
|
--no-server-rewrite
|
|
Don't rewrite server header field in default mode. When
|
|
--http2-proxy is used, these headers will not be altered
|
|
regardless of this option.
|
|
--redirect-https-port=<PORT>
|
|
Specify the port number which appears in Location header
|
|
field when redirect to HTTPS URI is made due to
|
|
"redirect-if-not-tls" parameter in --backend option.
|
|
Default: )"
|
|
<< config->http.redirect_https_port << R"(
|
|
--require-http-scheme
|
|
Always require http or https scheme in HTTP request. It
|
|
also requires that https scheme must be used for an
|
|
encrypted connection. Otherwise, http scheme must be
|
|
used. This option is recommended for a server
|
|
deployment which directly faces clients and the services
|
|
it provides only require http or https scheme.
|
|
|
|
API:
|
|
--api-max-request-body=<SIZE>
|
|
Set the maximum size of request body for API request.
|
|
Default: )"
|
|
<< util::utos_unit(config->api.max_request_body) << R"(
|
|
|
|
DNS:
|
|
--dns-cache-timeout=<DURATION>
|
|
Set duration that cached DNS results remain valid. Note
|
|
that nghttpx caches the unsuccessful results as well.
|
|
Default: )"
|
|
<< util::duration_str(config->dns.timeout.cache) << R"(
|
|
--dns-lookup-timeout=<DURATION>
|
|
Set timeout that DNS server is given to respond to the
|
|
initial DNS query. For the 2nd and later queries,
|
|
server is given time based on this timeout, and it is
|
|
scaled linearly.
|
|
Default: )"
|
|
<< util::duration_str(config->dns.timeout.lookup) << R"(
|
|
--dns-max-try=<N>
|
|
Set the number of DNS query before nghttpx gives up name
|
|
lookup.
|
|
Default: )"
|
|
<< config->dns.max_try << R"(
|
|
--frontend-max-requests=<N>
|
|
The number of requests that single frontend connection
|
|
can process. For HTTP/2, this is the number of streams
|
|
in one HTTP/2 connection. For HTTP/1, this is the
|
|
number of keep alive requests. This is hint to nghttpx,
|
|
and it may allow additional few requests. The default
|
|
value is unlimited.
|
|
|
|
Debug:
|
|
--frontend-http2-dump-request-header=<PATH>
|
|
Dumps request headers received by HTTP/2 frontend to the
|
|
file denoted in <PATH>. The output is done in HTTP/1
|
|
header field format and each header block is followed by
|
|
an empty line. This option is not thread safe and MUST
|
|
NOT be used with option -n<N>, where <N> >= 2.
|
|
--frontend-http2-dump-response-header=<PATH>
|
|
Dumps response headers sent from HTTP/2 frontend to the
|
|
file denoted in <PATH>. The output is done in HTTP/1
|
|
header field format and each header block is followed by
|
|
an empty line. This option is not thread safe and MUST
|
|
NOT be used with option -n<N>, where <N> >= 2.
|
|
-o, --frontend-frame-debug
|
|
Print HTTP/2 frames in frontend to stderr. This option
|
|
is not thread safe and MUST NOT be used with option
|
|
-n=N, where N >= 2.
|
|
|
|
Process:
|
|
-D, --daemon
|
|
Run in a background. If -D is used, the current working
|
|
directory is changed to '/'.
|
|
--pid-file=<PATH>
|
|
Set path to save PID of this program.
|
|
--user=<USER>
|
|
Run this program as <USER>. This option is intended to
|
|
be used to drop root privileges.
|
|
--single-process
|
|
Run this program in a single process mode for debugging
|
|
purpose. Without this option, nghttpx creates at least
|
|
2 processes: main and worker processes. If this option
|
|
is used, main and worker are unified into a single
|
|
process. nghttpx still spawns additional process if
|
|
neverbleed is used. In the single process mode, the
|
|
signal handling feature is disabled.
|
|
--max-worker-processes=<N>
|
|
The maximum number of worker processes. nghttpx spawns
|
|
new worker process when it reloads its configuration.
|
|
The previous worker process enters graceful termination
|
|
period and will terminate when it finishes handling the
|
|
existing connections. However, if reloading
|
|
configurations happen very frequently, the worker
|
|
processes might be piled up if they take a bit long time
|
|
to finish the existing connections. With this option,
|
|
if the number of worker processes exceeds the given
|
|
value, the oldest worker process is terminated
|
|
immediately. Specifying 0 means no limit and it is the
|
|
default behaviour.
|
|
--worker-process-grace-shutdown-period=<DURATION>
|
|
Maximum period for a worker process to terminate
|
|
gracefully. When a worker process enters in graceful
|
|
shutdown period (e.g., when nghttpx reloads its
|
|
configuration) and it does not finish handling the
|
|
existing connections in the given period of time, it is
|
|
immediately terminated. Specifying 0 means no limit and
|
|
it is the default behaviour.
|
|
|
|
Scripting:
|
|
--mruby-file=<PATH>
|
|
Set mruby script file
|
|
--ignore-per-pattern-mruby-error
|
|
Ignore mruby compile error for per-pattern mruby script
|
|
file. If error occurred, it is treated as if no mruby
|
|
file were specified for the pattern.
|
|
)";
|
|
|
|
#ifdef ENABLE_HTTP3
|
|
out << R"(
|
|
HTTP/3 and QUIC:
|
|
--frontend-quic-idle-timeout=<DURATION>
|
|
Specify an idle timeout for QUIC connection.
|
|
Default: )"
|
|
<< util::duration_str(config->quic.upstream.timeout.idle) << R"(
|
|
--frontend-quic-debug-log
|
|
Output QUIC debug log to /dev/stderr.
|
|
--quic-bpf-program-file=<PATH>
|
|
Specify a path to eBPF program file reuseport_kern.o to
|
|
direct an incoming QUIC UDP datagram to a correct
|
|
socket.
|
|
Default: )"
|
|
<< config->quic.bpf.prog_file << R"(
|
|
--frontend-quic-early-data
|
|
Enable early data on frontend QUIC connections. nghttpx
|
|
sends "Early-Data" header field to a backend server if a
|
|
request is received in early data and handshake has not
|
|
finished. All backend servers should deal with possibly
|
|
replayed requests.
|
|
--frontend-quic-qlog-dir=<DIR>
|
|
Specify a directory where a qlog file is written for
|
|
frontend QUIC connections. A qlog file is created per
|
|
each QUIC connection. The file name is ISO8601 basic
|
|
format, followed by "-", server Source Connection ID and
|
|
".sqlog".
|
|
--frontend-quic-require-token
|
|
Require an address validation token for a frontend QUIC
|
|
connection. Server sends a token in Retry packet or
|
|
NEW_TOKEN frame in the previous connection.
|
|
--frontend-quic-congestion-controller=<CC>
|
|
Specify a congestion controller algorithm for a frontend
|
|
QUIC connection. <CC> should be one of "cubic", "bbr",
|
|
and "bbr2".
|
|
Default: )"
|
|
<< (config->quic.upstream.congestion_controller == NGTCP2_CC_ALGO_CUBIC
|
|
? "cubic"
|
|
: (config->quic.upstream.congestion_controller ==
|
|
NGTCP2_CC_ALGO_BBR
|
|
? "bbr"
|
|
: "bbr2"))
|
|
<< R"(
|
|
--frontend-quic-secret-file=<PATH>
|
|
Path to file that contains secure random data to be used
|
|
as QUIC keying materials. It is used to derive keys for
|
|
encrypting tokens and Connection IDs. It is not used to
|
|
encrypt QUIC packets. Each line of this file must
|
|
contain exactly 136 bytes hex-encoded string (when
|
|
decoded the byte string is 68 bytes long). The first 2
|
|
bits of decoded byte string are used to identify the
|
|
keying material. An empty line or a line which starts
|
|
'#' is ignored. The file can contain more than one
|
|
keying materials. Because the identifier is 2 bits, at
|
|
most 4 keying materials are read and the remaining data
|
|
is discarded. The first keying material in the file is
|
|
primarily used for encryption and decryption for new
|
|
connection. The other ones are used to decrypt data for
|
|
the existing connections. Specifying multiple keying
|
|
materials enables key rotation. Please note that key
|
|
rotation does not occur automatically. User should
|
|
update files or change options values and restart
|
|
nghttpx gracefully. If opening or reading given file
|
|
fails, all loaded keying materials are discarded and it
|
|
is treated as if none of this option is given. If this
|
|
option is not given or an error occurred while opening
|
|
or reading a file, a keying material is generated
|
|
internally on startup and reload.
|
|
--quic-server-id=<HEXSTRING>
|
|
Specify server ID encoded in Connection ID to identify
|
|
this particular server instance. Connection ID is
|
|
encrypted and this part is not visible in public. It
|
|
must be 4 bytes long and must be encoded in hex string
|
|
(which is 8 bytes long). If this option is omitted, a
|
|
random server ID is generated on startup and
|
|
configuration reload.
|
|
--frontend-quic-initial-rtt=<DURATION>
|
|
Specify the initial RTT of the frontend QUIC connection.
|
|
Default: )"
|
|
<< util::duration_str(config->quic.upstream.initial_rtt) << R"(
|
|
--no-quic-bpf
|
|
Disable eBPF.
|
|
--frontend-http3-window-size=<SIZE>
|
|
Sets the per-stream initial window size of HTTP/3
|
|
frontend connection.
|
|
Default: )"
|
|
<< util::utos_unit(config->http3.upstream.window_size) << R"(
|
|
--frontend-http3-connection-window-size=<SIZE>
|
|
Sets the per-connection window size of HTTP/3 frontend
|
|
connection.
|
|
Default: )"
|
|
<< util::utos_unit(config->http3.upstream.connection_window_size) << R"(
|
|
--frontend-http3-max-window-size=<SIZE>
|
|
Sets the maximum per-stream window size of HTTP/3
|
|
frontend connection. The window size is adjusted based
|
|
on the receiving rate of stream data. The initial value
|
|
is the value specified by --frontend-http3-window-size
|
|
and the window size grows up to <SIZE> bytes.
|
|
Default: )"
|
|
<< util::utos_unit(config->http3.upstream.max_window_size) << R"(
|
|
--frontend-http3-max-connection-window-size=<SIZE>
|
|
Sets the maximum per-connection window size of HTTP/3
|
|
frontend connection. The window size is adjusted based
|
|
on the receiving rate of stream data. The initial value
|
|
is the value specified by
|
|
--frontend-http3-connection-window-size and the window
|
|
size grows up to <SIZE> bytes.
|
|
Default: )"
|
|
<< util::utos_unit(config->http3.upstream.max_connection_window_size)
|
|
<< R"(
|
|
--frontend-http3-max-concurrent-streams=<N>
|
|
Set the maximum number of the concurrent streams in one
|
|
frontend HTTP/3 connection.
|
|
Default: )"
|
|
<< config->http3.upstream.max_concurrent_streams << R"(
|
|
)";
|
|
#endif // ENABLE_HTTP3
|
|
|
|
out << R"(
|
|
Misc:
|
|
--conf=<PATH>
|
|
Load configuration from <PATH>. Please note that
|
|
nghttpx always tries to read the default configuration
|
|
file if --conf is not given.
|
|
Default: )"
|
|
<< config->conf_path << R"(
|
|
--include=<PATH>
|
|
Load additional configurations from <PATH>. File <PATH>
|
|
is read when configuration parser encountered this
|
|
option. This option can be used multiple times, or even
|
|
recursively.
|
|
-v, --version
|
|
Print version and exit.
|
|
-h, --help Print this help and exit.
|
|
|
|
--
|
|
|
|
The <SIZE> argument is an integer and an optional unit (e.g., 10K is
|
|
10 * 1024). Units are K, M and G (powers of 1024).
|
|
|
|
The <DURATION> argument is an integer and an optional unit (e.g., 1s
|
|
is 1 second and 500ms is 500 milliseconds). Units are h, m, s or ms
|
|
(hours, minutes, seconds and milliseconds, respectively). If a unit
|
|
is omitted, a second is used as unit.)"
|
|
<< std::endl;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
int process_options(Config *config,
|
|
std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
std::map<StringRef, size_t> pattern_addr_indexer;
|
|
if (conf_exists(config->conf_path.c_str())) {
|
|
LOG(NOTICE) << "Loading configuration from " << config->conf_path;
|
|
std::set<StringRef> include_set;
|
|
if (load_config(config, config->conf_path.c_str(), include_set,
|
|
pattern_addr_indexer) == -1) {
|
|
LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
|
|
return -1;
|
|
}
|
|
assert(include_set.empty());
|
|
}
|
|
|
|
// Reopen log files using configurations in file
|
|
reopen_log_files(config->logging);
|
|
|
|
{
|
|
std::set<StringRef> include_set;
|
|
|
|
for (auto &p : cmdcfgs) {
|
|
if (parse_config(config, p.first, p.second, include_set,
|
|
pattern_addr_indexer) == -1) {
|
|
LOG(FATAL) << "Failed to parse command-line argument.";
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
assert(include_set.empty());
|
|
}
|
|
|
|
Log::set_severity_level(config->logging.severity);
|
|
|
|
auto &loggingconf = config->logging;
|
|
|
|
if (loggingconf.access.syslog || loggingconf.error.syslog) {
|
|
openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
|
|
loggingconf.syslog_facility);
|
|
}
|
|
|
|
if (reopen_log_files(config->logging) != 0) {
|
|
LOG(FATAL) << "Failed to open log file";
|
|
return -1;
|
|
}
|
|
|
|
redirect_stderr_to_errorlog(loggingconf);
|
|
|
|
if (config->uid != 0) {
|
|
if (log_config()->accesslog_fd != -1 &&
|
|
fchown(log_config()->accesslog_fd, config->uid, config->gid) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Changing owner of access log file failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
if (log_config()->errorlog_fd != -1 &&
|
|
fchown(log_config()->errorlog_fd, config->uid, config->gid) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Changing owner of error log file failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
|
|
if (config->single_thread) {
|
|
LOG(WARN) << "single-thread: Set workers to 1";
|
|
config->num_worker = 1;
|
|
}
|
|
|
|
auto &http2conf = config->http2;
|
|
{
|
|
auto &dumpconf = http2conf.upstream.debug.dump;
|
|
|
|
if (!dumpconf.request_header_file.empty()) {
|
|
auto path = dumpconf.request_header_file.c_str();
|
|
auto f = open_file_for_write(path);
|
|
|
|
if (f == nullptr) {
|
|
LOG(FATAL) << "Failed to open http2 upstream request header file: "
|
|
<< path;
|
|
return -1;
|
|
}
|
|
|
|
dumpconf.request_header = f;
|
|
|
|
if (config->uid != 0) {
|
|
if (chown(path, config->uid, config->gid) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Changing owner of http2 upstream request header file "
|
|
<< path << " failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!dumpconf.response_header_file.empty()) {
|
|
auto path = dumpconf.response_header_file.c_str();
|
|
auto f = open_file_for_write(path);
|
|
|
|
if (f == nullptr) {
|
|
LOG(FATAL) << "Failed to open http2 upstream response header file: "
|
|
<< path;
|
|
return -1;
|
|
}
|
|
|
|
dumpconf.response_header = f;
|
|
|
|
if (config->uid != 0) {
|
|
if (chown(path, config->uid, config->gid) == -1) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Changing owner of http2 upstream response header file"
|
|
<< " " << path << " failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
auto &tlsconf = config->tls;
|
|
|
|
if (tlsconf.npn_list.empty()) {
|
|
tlsconf.npn_list = util::split_str(DEFAULT_NPN_LIST, ',');
|
|
}
|
|
|
|
if (!tlsconf.tls_proto_list.empty()) {
|
|
tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list);
|
|
}
|
|
|
|
// TODO We depends on the ordering of protocol version macro in
|
|
// OpenSSL.
|
|
if (tlsconf.min_proto_version > tlsconf.max_proto_version) {
|
|
LOG(ERROR) << "tls-max-proto-version must be equal to or larger than "
|
|
"tls-min-proto-version";
|
|
return -1;
|
|
}
|
|
|
|
if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.npn_list) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
tlsconf.bio_method = create_bio_method();
|
|
|
|
auto &listenerconf = config->conn.listener;
|
|
auto &upstreamconf = config->conn.upstream;
|
|
|
|
if (listenerconf.addrs.empty()) {
|
|
UpstreamAddr addr{};
|
|
addr.host = StringRef::from_lit("*");
|
|
addr.port = 3000;
|
|
addr.tls = true;
|
|
addr.family = AF_INET;
|
|
addr.index = 0;
|
|
listenerconf.addrs.push_back(addr);
|
|
addr.family = AF_INET6;
|
|
addr.index = 1;
|
|
listenerconf.addrs.push_back(std::move(addr));
|
|
}
|
|
|
|
if (upstreamconf.worker_connections == 0) {
|
|
upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
|
|
}
|
|
|
|
if (tls::upstream_tls_enabled(config->conn) &&
|
|
(tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
|
|
LOG(FATAL) << "TLS private key and certificate files are required. "
|
|
"Specify them in command-line, or in configuration file "
|
|
"using private-key-file and certificate-file options.";
|
|
return -1;
|
|
}
|
|
|
|
if (tls::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
|
|
struct stat buf;
|
|
if (stat(tlsconf.ocsp.fetch_ocsp_response_file.c_str(), &buf) != 0) {
|
|
tlsconf.ocsp.disabled = true;
|
|
LOG(WARN) << "--fetch-ocsp-response-file: "
|
|
<< tlsconf.ocsp.fetch_ocsp_response_file
|
|
<< " not found. OCSP stapling has been disabled.";
|
|
}
|
|
}
|
|
|
|
if (configure_downstream_group(config, config->http2_proxy, false, tlsconf) !=
|
|
0) {
|
|
return -1;
|
|
}
|
|
|
|
std::array<char, util::max_hostport> hostport_buf;
|
|
|
|
auto &proxy = config->downstream_http_proxy;
|
|
if (!proxy.host.empty()) {
|
|
auto hostport = util::make_hostport(std::begin(hostport_buf),
|
|
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;
|
|
return -1;
|
|
}
|
|
LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> "
|
|
<< util::to_numeric_addr(&proxy.addr);
|
|
}
|
|
|
|
{
|
|
auto &memcachedconf = tlsconf.session_cache.memcached;
|
|
if (!memcachedconf.host.empty()) {
|
|
auto hostport = util::make_hostport(std::begin(hostport_buf),
|
|
StringRef{memcachedconf.host},
|
|
memcachedconf.port);
|
|
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
|
|
memcachedconf.port, memcachedconf.family) == -1) {
|
|
LOG(FATAL)
|
|
<< "Resolving memcached address for TLS session cache failed: "
|
|
<< hostport;
|
|
return -1;
|
|
}
|
|
LOG(NOTICE) << "Memcached address for TLS session cache: " << hostport
|
|
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
|
if (memcachedconf.tls) {
|
|
LOG(NOTICE) << "Connection to memcached for TLS session cache will be "
|
|
"encrypted by TLS";
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
auto &memcachedconf = tlsconf.ticket.memcached;
|
|
if (!memcachedconf.host.empty()) {
|
|
auto hostport = util::make_hostport(std::begin(hostport_buf),
|
|
StringRef{memcachedconf.host},
|
|
memcachedconf.port);
|
|
if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.c_str(),
|
|
memcachedconf.port, memcachedconf.family) == -1) {
|
|
LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: "
|
|
<< hostport;
|
|
return -1;
|
|
}
|
|
LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport
|
|
<< " -> " << util::to_numeric_addr(&memcachedconf.addr);
|
|
if (memcachedconf.tls) {
|
|
LOG(NOTICE) << "Connection to memcached for TLS ticket key will be "
|
|
"encrypted by TLS";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (config->rlimit_nofile) {
|
|
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
|
|
static_cast<rlim_t>(config->rlimit_nofile)};
|
|
if (setrlimit(RLIMIT_NOFILE, &lim) != 0) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Setting rlimit-nofile failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
|
|
#ifdef RLIMIT_MEMLOCK
|
|
if (config->rlimit_memlock) {
|
|
struct rlimit lim = {static_cast<rlim_t>(config->rlimit_memlock),
|
|
static_cast<rlim_t>(config->rlimit_memlock)};
|
|
if (setrlimit(RLIMIT_MEMLOCK, &lim) != 0) {
|
|
auto error = errno;
|
|
LOG(WARN) << "Setting rlimit-memlock failed: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
}
|
|
}
|
|
#endif // RLIMIT_MEMLOCK
|
|
|
|
auto &fwdconf = config->http.forwarded;
|
|
|
|
if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED &&
|
|
fwdconf.by_obfuscated.empty()) {
|
|
// 2 for '_' and terminal NULL
|
|
auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
|
|
auto p = iov.base;
|
|
*p++ = '_';
|
|
auto gen = util::make_mt19937();
|
|
p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, gen);
|
|
*p = '\0';
|
|
fwdconf.by_obfuscated = StringRef{iov.base, p};
|
|
}
|
|
|
|
if (config->http2.upstream.debug.frame_debug) {
|
|
// To make it sync to logging
|
|
set_output(stderr);
|
|
if (isatty(fileno(stdout))) {
|
|
set_color_output(true);
|
|
}
|
|
reset_timer();
|
|
}
|
|
|
|
config->http2.upstream.callbacks = create_http2_upstream_callbacks();
|
|
config->http2.downstream.callbacks = create_http2_downstream_callbacks();
|
|
|
|
if (!config->http.altsvcs.empty()) {
|
|
config->http.altsvc_header_value =
|
|
http::create_altsvc_header_value(config->balloc, config->http.altsvcs);
|
|
}
|
|
|
|
if (!config->http.http2_altsvcs.empty()) {
|
|
config->http.http2_altsvc_header_value = http::create_altsvc_header_value(
|
|
config->balloc, config->http.http2_altsvcs);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
// Closes file descriptor which are opened for listeners in config,
|
|
// and are not inherited from |iaddrs|.
|
|
void close_not_inherited_fd(Config *config,
|
|
const std::vector<InheritedAddr> &iaddrs) {
|
|
auto &listenerconf = config->conn.listener;
|
|
|
|
for (auto &addr : listenerconf.addrs) {
|
|
auto inherited = std::find_if(
|
|
std::begin(iaddrs), std::end(iaddrs),
|
|
[&addr](const InheritedAddr &iaddr) { return addr.fd == iaddr.fd; });
|
|
|
|
if (inherited != std::end(iaddrs)) {
|
|
continue;
|
|
}
|
|
|
|
close(addr.fd);
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
namespace {
|
|
void reload_config(WorkerProcess *wp) {
|
|
int rv;
|
|
|
|
LOG(NOTICE) << "Reloading configuration";
|
|
|
|
auto cur_config = mod_config();
|
|
auto new_config = std::make_unique<Config>();
|
|
|
|
fill_default_config(new_config.get());
|
|
|
|
new_config->conf_path =
|
|
make_string_ref(new_config->balloc, cur_config->conf_path);
|
|
// daemon option is ignored here.
|
|
new_config->daemon = cur_config->daemon;
|
|
// loop is reused, and ev_loop_flags gets ignored
|
|
new_config->ev_loop_flags = cur_config->ev_loop_flags;
|
|
new_config->config_revision = cur_config->config_revision + 1;
|
|
|
|
rv = process_options(new_config.get(), suconfig.cmdcfgs);
|
|
if (rv != 0) {
|
|
LOG(ERROR) << "Failed to process new configuration";
|
|
return;
|
|
}
|
|
|
|
auto iaddrs = get_inherited_addr_from_config(new_config->balloc, cur_config);
|
|
|
|
if (create_acceptor_socket(new_config.get(), iaddrs) != 0) {
|
|
close_not_inherited_fd(new_config.get(), iaddrs);
|
|
return;
|
|
}
|
|
|
|
// According to libev documentation, flags are ignored since we have
|
|
// already created first default loop.
|
|
auto loop = ev_default_loop(new_config->ev_loop_flags);
|
|
|
|
int ipc_fd = 0;
|
|
#ifdef ENABLE_HTTP3
|
|
int quic_ipc_fd = 0;
|
|
|
|
auto quic_lwps = collect_quic_lingering_worker_processes();
|
|
|
|
std::vector<std::array<uint8_t, SHRPX_QUIC_CID_PREFIXLEN>> cid_prefixes;
|
|
|
|
if (generate_cid_prefix(cid_prefixes, new_config.get()) != 0) {
|
|
close_not_inherited_fd(new_config.get(), iaddrs);
|
|
return;
|
|
}
|
|
#endif // ENABLE_HTTP3
|
|
|
|
// fork_worker_process and forked child process assumes new
|
|
// configuration can be obtained from get_config().
|
|
|
|
auto old_config = replace_config(std::move(new_config));
|
|
|
|
auto pid = fork_worker_process(ipc_fd
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
quic_ipc_fd
|
|
#endif // ENABLE_HTTP3
|
|
|
|
,
|
|
iaddrs
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
cid_prefixes, quic_lwps
|
|
#endif // ENABLE_HTTP3
|
|
);
|
|
|
|
if (pid == -1) {
|
|
LOG(ERROR) << "Failed to process new configuration";
|
|
|
|
new_config = replace_config(std::move(old_config));
|
|
close_not_inherited_fd(new_config.get(), iaddrs);
|
|
|
|
return;
|
|
}
|
|
|
|
close_unused_inherited_addr(iaddrs);
|
|
|
|
// Send last worker process a graceful shutdown notice
|
|
auto &last_wp = worker_processes.back();
|
|
ipc_send(last_wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
|
|
worker_process_set_termination_deadline(last_wp.get(), loop);
|
|
// We no longer use signals for this worker.
|
|
last_wp->shutdown_signal_watchers();
|
|
|
|
worker_process_add(std::make_unique<WorkerProcess>(loop, pid, ipc_fd
|
|
#ifdef ENABLE_HTTP3
|
|
,
|
|
quic_ipc_fd, cid_prefixes
|
|
#endif // ENABLE_HTTP3
|
|
));
|
|
|
|
worker_process_adjust_limit();
|
|
|
|
if (!get_config()->pid_file.empty()) {
|
|
save_pid();
|
|
}
|
|
}
|
|
} // namespace
|
|
|
|
int main(int argc, char **argv) {
|
|
int rv;
|
|
std::array<char, STRERROR_BUFSIZE> errbuf;
|
|
|
|
nghttp2::tls::libssl_init();
|
|
|
|
#ifdef HAVE_LIBBPF
|
|
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
|
|
#endif // HAVE_LIBBPF
|
|
|
|
#ifndef NOTHREADS
|
|
nghttp2::tls::LibsslGlobalLock lock;
|
|
#endif // NOTHREADS
|
|
|
|
Log::set_severity_level(NOTICE);
|
|
create_config();
|
|
fill_default_config(mod_config());
|
|
|
|
// make copy of stderr
|
|
store_original_fds();
|
|
|
|
// First open log files with default configuration, so that we can
|
|
// log errors/warnings while reading configuration files.
|
|
reopen_log_files(get_config()->logging);
|
|
|
|
suconfig.original_argv = argv;
|
|
|
|
// We have to copy argv, since getopt_long may change its content.
|
|
suconfig.argc = argc;
|
|
suconfig.argv = new char *[argc];
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
suconfig.argv[i] = strdup(argv[i]);
|
|
if (suconfig.argv[i] == nullptr) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "failed to copy argv: "
|
|
<< xsi_strerror(error, errbuf.data(), errbuf.size());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
suconfig.cwd = getcwd(nullptr, 0);
|
|
if (suconfig.cwd == nullptr) {
|
|
auto error = errno;
|
|
LOG(FATAL) << "failed to get current working directory: errno=" << error;
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
auto &cmdcfgs = suconfig.cmdcfgs;
|
|
|
|
while (1) {
|
|
static int flag = 0;
|
|
static constexpr option long_options[] = {
|
|
{SHRPX_OPT_DAEMON.c_str(), no_argument, nullptr, 'D'},
|
|
{SHRPX_OPT_LOG_LEVEL.c_str(), required_argument, nullptr, 'L'},
|
|
{SHRPX_OPT_BACKEND.c_str(), required_argument, nullptr, 'b'},
|
|
{SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS.c_str(), required_argument,
|
|
nullptr, 'c'},
|
|
{SHRPX_OPT_FRONTEND.c_str(), required_argument, nullptr, 'f'},
|
|
{"help", no_argument, nullptr, 'h'},
|
|
{SHRPX_OPT_INSECURE.c_str(), no_argument, nullptr, 'k'},
|
|
{SHRPX_OPT_WORKERS.c_str(), required_argument, nullptr, 'n'},
|
|
{SHRPX_OPT_CLIENT_PROXY.c_str(), no_argument, nullptr, 'p'},
|
|
{SHRPX_OPT_HTTP2_PROXY.c_str(), no_argument, nullptr, 's'},
|
|
{"version", no_argument, nullptr, 'v'},
|
|
{SHRPX_OPT_FRONTEND_FRAME_DEBUG.c_str(), no_argument, nullptr, 'o'},
|
|
{SHRPX_OPT_ADD_X_FORWARDED_FOR.c_str(), no_argument, &flag, 1},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT.c_str(), required_argument,
|
|
&flag, 2},
|
|
{SHRPX_OPT_FRONTEND_READ_TIMEOUT.c_str(), required_argument, &flag, 3},
|
|
{SHRPX_OPT_FRONTEND_WRITE_TIMEOUT.c_str(), required_argument, &flag, 4},
|
|
{SHRPX_OPT_BACKEND_READ_TIMEOUT.c_str(), required_argument, &flag, 5},
|
|
{SHRPX_OPT_BACKEND_WRITE_TIMEOUT.c_str(), required_argument, &flag, 6},
|
|
{SHRPX_OPT_ACCESSLOG_FILE.c_str(), required_argument, &flag, 7},
|
|
{SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument, &flag,
|
|
8},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS.c_str(), required_argument, &flag,
|
|
9},
|
|
{SHRPX_OPT_PID_FILE.c_str(), required_argument, &flag, 10},
|
|
{SHRPX_OPT_USER.c_str(), required_argument, &flag, 11},
|
|
{"conf", required_argument, &flag, 12},
|
|
{SHRPX_OPT_SYSLOG_FACILITY.c_str(), required_argument, &flag, 14},
|
|
{SHRPX_OPT_BACKLOG.c_str(), required_argument, &flag, 15},
|
|
{SHRPX_OPT_CIPHERS.c_str(), required_argument, &flag, 16},
|
|
{SHRPX_OPT_CLIENT.c_str(), no_argument, &flag, 17},
|
|
{SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS.c_str(), required_argument, &flag,
|
|
18},
|
|
{SHRPX_OPT_CACERT.c_str(), required_argument, &flag, 19},
|
|
{SHRPX_OPT_BACKEND_IPV4.c_str(), no_argument, &flag, 20},
|
|
{SHRPX_OPT_BACKEND_IPV6.c_str(), no_argument, &flag, 21},
|
|
{SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE.c_str(), required_argument, &flag,
|
|
22},
|
|
{SHRPX_OPT_NO_VIA.c_str(), no_argument, &flag, 23},
|
|
{SHRPX_OPT_SUBCERT.c_str(), required_argument, &flag, 24},
|
|
{SHRPX_OPT_HTTP2_BRIDGE.c_str(), no_argument, &flag, 25},
|
|
{SHRPX_OPT_BACKEND_HTTP_PROXY_URI.c_str(), required_argument, &flag,
|
|
26},
|
|
{SHRPX_OPT_BACKEND_NO_TLS.c_str(), no_argument, &flag, 27},
|
|
{SHRPX_OPT_OCSP_STARTUP.c_str(), no_argument, &flag, 28},
|
|
{SHRPX_OPT_FRONTEND_NO_TLS.c_str(), no_argument, &flag, 29},
|
|
{SHRPX_OPT_NO_VERIFY_OCSP.c_str(), no_argument, &flag, 30},
|
|
{SHRPX_OPT_BACKEND_TLS_SNI_FIELD.c_str(), required_argument, &flag, 31},
|
|
{SHRPX_OPT_DH_PARAM_FILE.c_str(), required_argument, &flag, 33},
|
|
{SHRPX_OPT_READ_RATE.c_str(), required_argument, &flag, 34},
|
|
{SHRPX_OPT_READ_BURST.c_str(), required_argument, &flag, 35},
|
|
{SHRPX_OPT_WRITE_RATE.c_str(), required_argument, &flag, 36},
|
|
{SHRPX_OPT_WRITE_BURST.c_str(), required_argument, &flag, 37},
|
|
{SHRPX_OPT_NPN_LIST.c_str(), required_argument, &flag, 38},
|
|
{SHRPX_OPT_VERIFY_CLIENT.c_str(), no_argument, &flag, 39},
|
|
{SHRPX_OPT_VERIFY_CLIENT_CACERT.c_str(), required_argument, &flag, 40},
|
|
{SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE.c_str(), required_argument, &flag,
|
|
41},
|
|
{SHRPX_OPT_CLIENT_CERT_FILE.c_str(), required_argument, &flag, 42},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER.c_str(),
|
|
required_argument, &flag, 43},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER.c_str(),
|
|
required_argument, &flag, 44},
|
|
{SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING.c_str(), no_argument, &flag, 45},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS.c_str(),
|
|
required_argument, &flag, 46},
|
|
{SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS.c_str(),
|
|
required_argument, &flag, 47},
|
|
{SHRPX_OPT_TLS_PROTO_LIST.c_str(), required_argument, &flag, 48},
|
|
{SHRPX_OPT_PADDING.c_str(), required_argument, &flag, 49},
|
|
{SHRPX_OPT_WORKER_READ_RATE.c_str(), required_argument, &flag, 50},
|
|
{SHRPX_OPT_WORKER_READ_BURST.c_str(), required_argument, &flag, 51},
|
|
{SHRPX_OPT_WORKER_WRITE_RATE.c_str(), required_argument, &flag, 52},
|
|
{SHRPX_OPT_WORKER_WRITE_BURST.c_str(), required_argument, &flag, 53},
|
|
{SHRPX_OPT_ALTSVC.c_str(), required_argument, &flag, 54},
|
|
{SHRPX_OPT_ADD_RESPONSE_HEADER.c_str(), required_argument, &flag, 55},
|
|
{SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS.c_str(), required_argument,
|
|
&flag, 56},
|
|
{SHRPX_OPT_ACCESSLOG_SYSLOG.c_str(), no_argument, &flag, 57},
|
|
{SHRPX_OPT_ERRORLOG_FILE.c_str(), required_argument, &flag, 58},
|
|
{SHRPX_OPT_ERRORLOG_SYSLOG.c_str(), no_argument, &flag, 59},
|
|
{SHRPX_OPT_STREAM_READ_TIMEOUT.c_str(), required_argument, &flag, 60},
|
|
{SHRPX_OPT_STREAM_WRITE_TIMEOUT.c_str(), required_argument, &flag, 61},
|
|
{SHRPX_OPT_NO_LOCATION_REWRITE.c_str(), no_argument, &flag, 62},
|
|
{SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST.c_str(),
|
|
required_argument, &flag, 63},
|
|
{SHRPX_OPT_LISTENER_DISABLE_TIMEOUT.c_str(), required_argument, &flag,
|
|
64},
|
|
{SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR.c_str(), no_argument, &flag,
|
|
65},
|
|
{SHRPX_OPT_ACCESSLOG_FORMAT.c_str(), required_argument, &flag, 66},
|
|
{SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND.c_str(),
|
|
required_argument, &flag, 67},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_FILE.c_str(), required_argument, &flag, 68},
|
|
{SHRPX_OPT_RLIMIT_NOFILE.c_str(), required_argument, &flag, 69},
|
|
{SHRPX_OPT_BACKEND_RESPONSE_BUFFER.c_str(), required_argument, &flag,
|
|
71},
|
|
{SHRPX_OPT_BACKEND_REQUEST_BUFFER.c_str(), required_argument, &flag,
|
|
72},
|
|
{SHRPX_OPT_NO_HOST_REWRITE.c_str(), no_argument, &flag, 73},
|
|
{SHRPX_OPT_NO_SERVER_PUSH.c_str(), no_argument, &flag, 74},
|
|
{SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER.c_str(),
|
|
required_argument, &flag, 76},
|
|
{SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE.c_str(), required_argument, &flag,
|
|
77},
|
|
{SHRPX_OPT_OCSP_UPDATE_INTERVAL.c_str(), required_argument, &flag, 78},
|
|
{SHRPX_OPT_NO_OCSP.c_str(), no_argument, &flag, 79},
|
|
{SHRPX_OPT_HEADER_FIELD_BUFFER.c_str(), required_argument, &flag, 80},
|
|
{SHRPX_OPT_MAX_HEADER_FIELDS.c_str(), required_argument, &flag, 81},
|
|
{SHRPX_OPT_ADD_REQUEST_HEADER.c_str(), required_argument, &flag, 82},
|
|
{SHRPX_OPT_INCLUDE.c_str(), required_argument, &flag, 83},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_CIPHER.c_str(), required_argument, &flag, 84},
|
|
{SHRPX_OPT_HOST_REWRITE.c_str(), no_argument, &flag, 85},
|
|
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED.c_str(), required_argument,
|
|
&flag, 86},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED.c_str(), required_argument, &flag,
|
|
87},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL.c_str(), required_argument,
|
|
&flag, 88},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY.c_str(),
|
|
required_argument, &flag, 89},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL.c_str(), required_argument,
|
|
&flag, 90},
|
|
{SHRPX_OPT_MRUBY_FILE.c_str(), required_argument, &flag, 91},
|
|
{SHRPX_OPT_ACCEPT_PROXY_PROTOCOL.c_str(), no_argument, &flag, 93},
|
|
{SHRPX_OPT_FASTOPEN.c_str(), required_argument, &flag, 94},
|
|
{SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD.c_str(), required_argument,
|
|
&flag, 95},
|
|
{SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT.c_str(), required_argument, &flag,
|
|
96},
|
|
{SHRPX_OPT_ADD_FORWARDED.c_str(), required_argument, &flag, 97},
|
|
{SHRPX_OPT_STRIP_INCOMING_FORWARDED.c_str(), no_argument, &flag, 98},
|
|
{SHRPX_OPT_FORWARDED_BY.c_str(), required_argument, &flag, 99},
|
|
{SHRPX_OPT_FORWARDED_FOR.c_str(), required_argument, &flag, 100},
|
|
{SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER.c_str(), required_argument,
|
|
&flag, 101},
|
|
{SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS.c_str(), required_argument, &flag,
|
|
102},
|
|
{SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument, &flag, 103},
|
|
{SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER.c_str(), required_argument,
|
|
&flag, 104},
|
|
{SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS.c_str(), required_argument, &flag,
|
|
105},
|
|
{SHRPX_OPT_BACKEND_HTTP1_TLS.c_str(), no_argument, &flag, 106},
|
|
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS.c_str(), no_argument, &flag,
|
|
108},
|
|
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE.c_str(),
|
|
required_argument, &flag, 109},
|
|
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
|
|
required_argument, &flag, 110},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS.c_str(), no_argument, &flag,
|
|
111},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE.c_str(),
|
|
required_argument, &flag, 112},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE.c_str(),
|
|
required_argument, &flag, 113},
|
|
{SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY.c_str(),
|
|
required_argument, &flag, 114},
|
|
{SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY.c_str(),
|
|
required_argument, &flag, 115},
|
|
{SHRPX_OPT_BACKEND_ADDRESS_FAMILY.c_str(), required_argument, &flag,
|
|
116},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS.c_str(),
|
|
required_argument, &flag, 117},
|
|
{SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS.c_str(),
|
|
required_argument, &flag, 118},
|
|
{SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND.c_str(), required_argument,
|
|
&flag, 119},
|
|
{SHRPX_OPT_BACKEND_TLS.c_str(), no_argument, &flag, 120},
|
|
{SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST.c_str(), required_argument,
|
|
&flag, 121},
|
|
{SHRPX_OPT_ERROR_PAGE.c_str(), required_argument, &flag, 122},
|
|
{SHRPX_OPT_NO_KQUEUE.c_str(), no_argument, &flag, 123},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT.c_str(), required_argument,
|
|
&flag, 124},
|
|
{SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT.c_str(), required_argument,
|
|
&flag, 125},
|
|
{SHRPX_OPT_API_MAX_REQUEST_BODY.c_str(), required_argument, &flag, 126},
|
|
{SHRPX_OPT_BACKEND_MAX_BACKOFF.c_str(), required_argument, &flag, 127},
|
|
{SHRPX_OPT_SERVER_NAME.c_str(), required_argument, &flag, 128},
|
|
{SHRPX_OPT_NO_SERVER_REWRITE.c_str(), no_argument, &flag, 129},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE.c_str(),
|
|
no_argument, &flag, 130},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE.c_str(), no_argument,
|
|
&flag, 131},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE.c_str(), required_argument, &flag,
|
|
132},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE.c_str(),
|
|
required_argument, &flag, 133},
|
|
{SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE.c_str(), required_argument, &flag,
|
|
134},
|
|
{SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE.c_str(),
|
|
required_argument, &flag, 135},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.c_str(),
|
|
required_argument, &flag, 136},
|
|
{SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.c_str(),
|
|
required_argument, &flag, 137},
|
|
{SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.c_str(),
|
|
required_argument, &flag, 138},
|
|
{SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.c_str(),
|
|
required_argument, &flag, 139},
|
|
{SHRPX_OPT_ECDH_CURVES.c_str(), required_argument, &flag, 140},
|
|
{SHRPX_OPT_TLS_SCT_DIR.c_str(), required_argument, &flag, 141},
|
|
{SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.c_str(), required_argument, &flag,
|
|
142},
|
|
{SHRPX_OPT_DNS_CACHE_TIMEOUT.c_str(), required_argument, &flag, 143},
|
|
{SHRPX_OPT_DNS_LOOKUP_TIMEOUT.c_str(), required_argument, &flag, 144},
|
|
{SHRPX_OPT_DNS_MAX_TRY.c_str(), required_argument, &flag, 145},
|
|
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
|
|
&flag, 146},
|
|
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
|
|
{SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
|
|
{SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.c_str(), no_argument,
|
|
&flag, 149},
|
|
{SHRPX_OPT_CLIENT_CIPHERS.c_str(), required_argument, &flag, 150},
|
|
{SHRPX_OPT_ACCESSLOG_WRITE_EARLY.c_str(), no_argument, &flag, 151},
|
|
{SHRPX_OPT_TLS_MIN_PROTO_VERSION.c_str(), required_argument, &flag,
|
|
152},
|
|
{SHRPX_OPT_TLS_MAX_PROTO_VERSION.c_str(), required_argument, &flag,
|
|
153},
|
|
{SHRPX_OPT_REDIRECT_HTTPS_PORT.c_str(), required_argument, &flag, 154},
|
|
{SHRPX_OPT_FRONTEND_MAX_REQUESTS.c_str(), required_argument, &flag,
|
|
155},
|
|
{SHRPX_OPT_SINGLE_THREAD.c_str(), no_argument, &flag, 156},
|
|
{SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.c_str(), no_argument, &flag, 157},
|
|
{SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.c_str(), no_argument,
|
|
&flag, 158},
|
|
{SHRPX_OPT_SINGLE_PROCESS.c_str(), no_argument, &flag, 159},
|
|
{SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.c_str(), no_argument, &flag,
|
|
160},
|
|
{SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.c_str(), no_argument, &flag,
|
|
161},
|
|
{SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA.c_str(), no_argument, &flag, 162},
|
|
{SHRPX_OPT_TLS_MAX_EARLY_DATA.c_str(), required_argument, &flag, 163},
|
|
{SHRPX_OPT_TLS13_CIPHERS.c_str(), required_argument, &flag, 164},
|
|
{SHRPX_OPT_TLS13_CLIENT_CIPHERS.c_str(), required_argument, &flag, 165},
|
|
{SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA.c_str(), no_argument, &flag,
|
|
166},
|
|
{SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST.c_str(), no_argument, &flag, 167},
|
|
{SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST.c_str(), no_argument,
|
|
&flag, 168},
|
|
{SHRPX_OPT_QUIC_BPF_PROGRAM_FILE.c_str(), required_argument, &flag,
|
|
169},
|
|
{SHRPX_OPT_NO_QUIC_BPF.c_str(), no_argument, &flag, 170},
|
|
{SHRPX_OPT_HTTP2_ALTSVC.c_str(), required_argument, &flag, 171},
|
|
{SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT.c_str(), required_argument,
|
|
&flag, 172},
|
|
{SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT.c_str(), required_argument, &flag,
|
|
173},
|
|
{SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG.c_str(), no_argument, &flag, 174},
|
|
{SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE.c_str(), required_argument, &flag,
|
|
175},
|
|
{SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE.c_str(),
|
|
required_argument, &flag, 176},
|
|
{SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE.c_str(), required_argument,
|
|
&flag, 177},
|
|
{SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE.c_str(),
|
|
required_argument, &flag, 178},
|
|
{SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS.c_str(),
|
|
required_argument, &flag, 179},
|
|
{SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA.c_str(), no_argument, &flag, 180},
|
|
{SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR.c_str(), required_argument, &flag,
|
|
181},
|
|
{SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN.c_str(), no_argument, &flag,
|
|
182},
|
|
{SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER.c_str(),
|
|
required_argument, &flag, 183},
|
|
{SHRPX_OPT_QUIC_SERVER_ID.c_str(), required_argument, &flag, 185},
|
|
{SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE.c_str(), required_argument, &flag,
|
|
186},
|
|
{SHRPX_OPT_RLIMIT_MEMLOCK.c_str(), required_argument, &flag, 187},
|
|
{SHRPX_OPT_MAX_WORKER_PROCESSES.c_str(), required_argument, &flag, 188},
|
|
{SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD.c_str(),
|
|
required_argument, &flag, 189},
|
|
{SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT.c_str(), required_argument, &flag,
|
|
190},
|
|
{SHRPX_OPT_REQUIRE_HTTP_SCHEME.c_str(), no_argument, &flag, 191},
|
|
{SHRPX_OPT_TLS_KTLS.c_str(), no_argument, &flag, 192},
|
|
{nullptr, 0, nullptr, 0}};
|
|
|
|
int option_index = 0;
|
|
int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options,
|
|
&option_index);
|
|
if (c == -1) {
|
|
break;
|
|
}
|
|
switch (c) {
|
|
case 'D':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, StringRef::from_lit("yes"));
|
|
break;
|
|
case 'L':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, StringRef{optarg});
|
|
break;
|
|
case 'b':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, StringRef{optarg});
|
|
break;
|
|
case 'c':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 'f':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, StringRef{optarg});
|
|
break;
|
|
case 'h':
|
|
print_help(std::cout);
|
|
exit(EXIT_SUCCESS);
|
|
case 'k':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, StringRef::from_lit("yes"));
|
|
break;
|
|
case 'n':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, StringRef{optarg});
|
|
break;
|
|
case 'o':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 'p':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, StringRef::from_lit("yes"));
|
|
break;
|
|
case 's':
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, StringRef::from_lit("yes"));
|
|
break;
|
|
case 'v':
|
|
print_version(std::cout);
|
|
exit(EXIT_SUCCESS);
|
|
case '?':
|
|
util::show_candidates(argv[optind - 1], long_options);
|
|
exit(EXIT_FAILURE);
|
|
case 0:
|
|
switch (flag) {
|
|
case 1:
|
|
// --add-x-forwarded-for
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 2:
|
|
// --frontend-http2-read-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 3:
|
|
// --frontend-read-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 4:
|
|
// --frontend-write-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 5:
|
|
// --backend-read-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, StringRef{optarg});
|
|
break;
|
|
case 6:
|
|
// --backend-write-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 7:
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, StringRef{optarg});
|
|
break;
|
|
case 8:
|
|
// --backend-keep-alive-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 9:
|
|
// --frontend-http2-window-bits
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 10:
|
|
cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, StringRef{optarg});
|
|
break;
|
|
case 11:
|
|
cmdcfgs.emplace_back(SHRPX_OPT_USER, StringRef{optarg});
|
|
break;
|
|
case 12:
|
|
// --conf
|
|
mod_config()->conf_path =
|
|
make_string_ref(mod_config()->balloc, StringRef{optarg});
|
|
break;
|
|
case 14:
|
|
// --syslog-facility
|
|
cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, StringRef{optarg});
|
|
break;
|
|
case 15:
|
|
// --backlog
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, StringRef{optarg});
|
|
break;
|
|
case 16:
|
|
// --ciphers
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, StringRef{optarg});
|
|
break;
|
|
case 17:
|
|
// --client
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, StringRef::from_lit("yes"));
|
|
break;
|
|
case 18:
|
|
// --backend-http2-window-bits
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 19:
|
|
// --cacert
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CACERT, StringRef{optarg});
|
|
break;
|
|
case 20:
|
|
// --backend-ipv4
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 21:
|
|
// --backend-ipv6
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 22:
|
|
// --private-key-passwd-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 23:
|
|
// --no-via
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, StringRef::from_lit("yes"));
|
|
break;
|
|
case 24:
|
|
// --subcert
|
|
cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, StringRef{optarg});
|
|
break;
|
|
case 25:
|
|
// --http2-bridge
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 26:
|
|
// --backend-http-proxy-uri
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI,
|
|
StringRef{optarg});
|
|
break;
|
|
case 27:
|
|
// --backend-no-tls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 28:
|
|
// --ocsp-startup
|
|
cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 29:
|
|
// --frontend-no-tls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 30:
|
|
// --no-verify-ocsp
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 31:
|
|
// --backend-tls-sni-field
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,
|
|
StringRef{optarg});
|
|
break;
|
|
case 33:
|
|
// --dh-param-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, StringRef{optarg});
|
|
break;
|
|
case 34:
|
|
// --read-rate
|
|
cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, StringRef{optarg});
|
|
break;
|
|
case 35:
|
|
// --read-burst
|
|
cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, StringRef{optarg});
|
|
break;
|
|
case 36:
|
|
// --write-rate
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, StringRef{optarg});
|
|
break;
|
|
case 37:
|
|
// --write-burst
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, StringRef{optarg});
|
|
break;
|
|
case 38:
|
|
// --npn-list
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, StringRef{optarg});
|
|
break;
|
|
case 39:
|
|
// --verify-client
|
|
cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 40:
|
|
// --verify-client-cacert
|
|
cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, StringRef{optarg});
|
|
break;
|
|
case 41:
|
|
// --client-private-key-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 42:
|
|
// --client-cert-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, StringRef{optarg});
|
|
break;
|
|
case 43:
|
|
// --frontend-http2-dump-request-header
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 44:
|
|
// --frontend-http2-dump-response-header
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 45:
|
|
// --http2-no-cookie-crumbling
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 46:
|
|
// --frontend-http2-connection-window-bits
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 47:
|
|
// --backend-http2-connection-window-bits
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 48:
|
|
// --tls-proto-list
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, StringRef{optarg});
|
|
break;
|
|
case 49:
|
|
// --padding
|
|
cmdcfgs.emplace_back(SHRPX_OPT_PADDING, StringRef{optarg});
|
|
break;
|
|
case 50:
|
|
// --worker-read-rate
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, StringRef{optarg});
|
|
break;
|
|
case 51:
|
|
// --worker-read-burst
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, StringRef{optarg});
|
|
break;
|
|
case 52:
|
|
// --worker-write-rate
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, StringRef{optarg});
|
|
break;
|
|
case 53:
|
|
// --worker-write-burst
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, StringRef{optarg});
|
|
break;
|
|
case 54:
|
|
// --altsvc
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, StringRef{optarg});
|
|
break;
|
|
case 55:
|
|
// --add-response-header
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, StringRef{optarg});
|
|
break;
|
|
case 56:
|
|
// --worker-frontend-connections
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 57:
|
|
// --accesslog-syslog
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 58:
|
|
// --errorlog-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, StringRef{optarg});
|
|
break;
|
|
case 59:
|
|
// --errorlog-syslog
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 60:
|
|
// --stream-read-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, StringRef{optarg});
|
|
break;
|
|
case 61:
|
|
// --stream-write-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, StringRef{optarg});
|
|
break;
|
|
case 62:
|
|
// --no-location-rewrite
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 63:
|
|
// --backend-http1-connections-per-host
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
|
|
StringRef{optarg});
|
|
break;
|
|
case 64:
|
|
// --listener-disable-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 65:
|
|
// --strip-incoming-x-forwarded-for
|
|
cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 66:
|
|
// --accesslog-format
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, StringRef{optarg});
|
|
break;
|
|
case 67:
|
|
// --backend-http1-connections-per-frontend
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
|
|
StringRef{optarg});
|
|
break;
|
|
case 68:
|
|
// --tls-ticket-key-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, StringRef{optarg});
|
|
break;
|
|
case 69:
|
|
// --rlimit-nofile
|
|
cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, StringRef{optarg});
|
|
break;
|
|
case 71:
|
|
// --backend-response-buffer
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 72:
|
|
// --backend-request-buffer
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 73:
|
|
// --no-host-rewrite
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 74:
|
|
// --no-server-push
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 76:
|
|
// --backend-http2-connections-per-worker
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 77:
|
|
// --fetch-ocsp-response-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 78:
|
|
// --ocsp-update-interval
|
|
cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, StringRef{optarg});
|
|
break;
|
|
case 79:
|
|
// --no-ocsp
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, StringRef::from_lit("yes"));
|
|
break;
|
|
case 80:
|
|
// --header-field-buffer
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, StringRef{optarg});
|
|
break;
|
|
case 81:
|
|
// --max-header-fields
|
|
cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, StringRef{optarg});
|
|
break;
|
|
case 82:
|
|
// --add-request-header
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, StringRef{optarg});
|
|
break;
|
|
case 83:
|
|
// --include
|
|
cmdcfgs.emplace_back(SHRPX_OPT_INCLUDE, StringRef{optarg});
|
|
break;
|
|
case 84:
|
|
// --tls-ticket-key-cipher
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_CIPHER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 85:
|
|
// --host-rewrite
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HOST_REWRITE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 86:
|
|
// --tls-session-cache-memcached
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED,
|
|
StringRef{optarg});
|
|
break;
|
|
case 87:
|
|
// --tls-ticket-key-memcached
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED,
|
|
StringRef{optarg});
|
|
break;
|
|
case 88:
|
|
// --tls-ticket-key-memcached-interval
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL,
|
|
StringRef{optarg});
|
|
break;
|
|
case 89:
|
|
// --tls-ticket-key-memcached-max-retry
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
|
|
StringRef{optarg});
|
|
break;
|
|
case 90:
|
|
// --tls-ticket-key-memcached-max-fail
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
|
|
StringRef{optarg});
|
|
break;
|
|
case 91:
|
|
// --mruby-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_MRUBY_FILE, StringRef{optarg});
|
|
break;
|
|
case 93:
|
|
// --accept-proxy-protocol
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ACCEPT_PROXY_PROTOCOL,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 94:
|
|
// --fastopen
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FASTOPEN, StringRef{optarg});
|
|
break;
|
|
case 95:
|
|
// --tls-dyn-rec-warmup-threshold
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD,
|
|
StringRef{optarg});
|
|
break;
|
|
case 96:
|
|
// --tls-dyn-rec-idle-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 97:
|
|
// --add-forwarded
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ADD_FORWARDED, StringRef{optarg});
|
|
break;
|
|
case 98:
|
|
// --strip-incoming-forwarded
|
|
cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_FORWARDED,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 99:
|
|
// --forwarded-by
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_BY, StringRef{optarg});
|
|
break;
|
|
case 100:
|
|
// --forwarded-for
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_FOR, StringRef{optarg});
|
|
break;
|
|
case 101:
|
|
// --response-header-field-buffer
|
|
cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 102:
|
|
// --max-response-header-fields
|
|
cmdcfgs.emplace_back(SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 103:
|
|
// --no-http2-cipher-black-list
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 104:
|
|
// --request-header-field-buffer
|
|
cmdcfgs.emplace_back(SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 105:
|
|
// --max-request-header-fields
|
|
cmdcfgs.emplace_back(SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 106:
|
|
// --backend-http1-tls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_TLS,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 108:
|
|
// --tls-session-cache-memcached-tls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 109:
|
|
// --tls-session-cache-memcached-cert-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 110:
|
|
// --tls-session-cache-memcached-private-key-file
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 111:
|
|
// --tls-ticket-key-memcached-tls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 112:
|
|
// --tls-ticket-key-memcached-cert-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 113:
|
|
// --tls-ticket-key-memcached-private-key-file
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 114:
|
|
// --tls-ticket-key-memcached-address-family
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
|
|
StringRef{optarg});
|
|
break;
|
|
case 115:
|
|
// --tls-session-cache-memcached-address-family
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
|
|
StringRef{optarg});
|
|
break;
|
|
case 116:
|
|
// --backend-address-family
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_ADDRESS_FAMILY,
|
|
StringRef{optarg});
|
|
break;
|
|
case 117:
|
|
// --frontend-http2-max-concurrent-streams
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 118:
|
|
// --backend-http2-max-concurrent-streams
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 119:
|
|
// --backend-connections-per-frontend
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND,
|
|
StringRef{optarg});
|
|
break;
|
|
case 120:
|
|
// --backend-tls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS, StringRef::from_lit("yes"));
|
|
break;
|
|
case 121:
|
|
// --backend-connections-per-host
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST,
|
|
StringRef{optarg});
|
|
break;
|
|
case 122:
|
|
// --error-page
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ERROR_PAGE, StringRef{optarg});
|
|
break;
|
|
case 123:
|
|
// --no-kqueue
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_KQUEUE, StringRef::from_lit("yes"));
|
|
break;
|
|
case 124:
|
|
// --frontend-http2-settings-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 125:
|
|
// --backend-http2-settings-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 126:
|
|
// --api-max-request-body
|
|
cmdcfgs.emplace_back(SHRPX_OPT_API_MAX_REQUEST_BODY, StringRef{optarg});
|
|
break;
|
|
case 127:
|
|
// --backend-max-backoff
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_MAX_BACKOFF, StringRef{optarg});
|
|
break;
|
|
case 128:
|
|
// --server-name
|
|
cmdcfgs.emplace_back(SHRPX_OPT_SERVER_NAME, StringRef{optarg});
|
|
break;
|
|
case 129:
|
|
// --no-server-rewrite
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_REWRITE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 130:
|
|
// --frontend-http2-optimize-write-buffer-size
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 131:
|
|
// --frontend-http2-optimize-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 132:
|
|
// --frontend-http2-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 133:
|
|
// --frontend-http2-connection-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 134:
|
|
// --backend-http2-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 135:
|
|
// --backend-http2-connection-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 136:
|
|
// --frontend-http2-encoder-dynamic-table-size
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 137:
|
|
// --frontend-http2-decoder-dynamic-table-size
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 138:
|
|
// --backend-http2-encoder-dynamic-table-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 139:
|
|
// --backend-http2-decoder-dynamic-table-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 140:
|
|
// --ecdh-curves
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ECDH_CURVES, StringRef{optarg});
|
|
break;
|
|
case 141:
|
|
// --tls-sct-dir
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_SCT_DIR, StringRef{optarg});
|
|
break;
|
|
case 142:
|
|
// --backend-connect-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 143:
|
|
// --dns-cache-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, StringRef{optarg});
|
|
break;
|
|
case 144:
|
|
// --dns-lookup-timeou
|
|
cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, StringRef{optarg});
|
|
break;
|
|
case 145:
|
|
// --dns-max-try
|
|
cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, StringRef{optarg});
|
|
break;
|
|
case 146:
|
|
// --frontend-keep-alive-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 147:
|
|
// --psk-secrets
|
|
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
|
|
break;
|
|
case 148:
|
|
// --client-psk-secrets
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
|
|
break;
|
|
case 149:
|
|
// --client-no-http2-cipher-black-list
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 150:
|
|
// --client-ciphers
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
|
|
break;
|
|
case 151:
|
|
// --accesslog-write-early
|
|
cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 152:
|
|
// --tls-min-proto-version
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MIN_PROTO_VERSION,
|
|
StringRef{optarg});
|
|
break;
|
|
case 153:
|
|
// --tls-max-proto-version
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION,
|
|
StringRef{optarg});
|
|
break;
|
|
case 154:
|
|
// --redirect-https-port
|
|
cmdcfgs.emplace_back(SHRPX_OPT_REDIRECT_HTTPS_PORT, StringRef{optarg});
|
|
break;
|
|
case 155:
|
|
// --frontend-max-requests
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_MAX_REQUESTS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 156:
|
|
// --single-thread
|
|
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 157:
|
|
// --no-add-x-forwarded-proto
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 158:
|
|
// --no-strip-incoming-x-forwarded-proto
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 159:
|
|
// --single-process
|
|
cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 160:
|
|
// --verify-client-tolerate-expired
|
|
cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 161:
|
|
// --ignore-per-pattern-mruby-error
|
|
cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 162:
|
|
// --tls-no-postpone-early-data
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 163:
|
|
// --tls-max-early-data
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_EARLY_DATA, StringRef{optarg});
|
|
break;
|
|
case 164:
|
|
// --tls13-ciphers
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CIPHERS, StringRef{optarg});
|
|
break;
|
|
case 165:
|
|
// --tls13-client-ciphers
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CLIENT_CIPHERS, StringRef{optarg});
|
|
break;
|
|
case 166:
|
|
// --no-strip-incoming-early-data
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 167:
|
|
// --no-http2-cipher-block-list
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 168:
|
|
// --client-no-http2-cipher-block-list
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 169:
|
|
// --quic-bpf-program-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_QUIC_BPF_PROGRAM_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 170:
|
|
// --no-quic-bpf
|
|
cmdcfgs.emplace_back(SHRPX_OPT_NO_QUIC_BPF, StringRef::from_lit("yes"));
|
|
break;
|
|
case 171:
|
|
// --http2-altsvc
|
|
cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_ALTSVC, StringRef{optarg});
|
|
break;
|
|
case 172:
|
|
// --frontend-http3-read-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 173:
|
|
// --frontend-quic-idle-timeout
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 174:
|
|
// --frontend-quic-debug-log
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 175:
|
|
// --frontend-http3-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 176:
|
|
// --frontend-http3-connection-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 177:
|
|
// --frontend-http3-max-window-size
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 178:
|
|
// --frontend-http3-max-connection-window-size
|
|
cmdcfgs.emplace_back(
|
|
SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 179:
|
|
// --frontend-http3-max-concurrent-streams
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS,
|
|
StringRef{optarg});
|
|
break;
|
|
case 180:
|
|
// --frontend-quic-early-data
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 181:
|
|
// --frontend-quic-qlog-dir
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR,
|
|
StringRef{optarg});
|
|
break;
|
|
case 182:
|
|
// --frontend-quic-require-token
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 183:
|
|
// --frontend-quic-congestion-controller
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER,
|
|
StringRef{optarg});
|
|
break;
|
|
case 185:
|
|
// --quic-server-id
|
|
cmdcfgs.emplace_back(SHRPX_OPT_QUIC_SERVER_ID, StringRef{optarg});
|
|
break;
|
|
case 186:
|
|
// --frontend-quic-secret-file
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE,
|
|
StringRef{optarg});
|
|
break;
|
|
case 187:
|
|
// --rlimit-memlock
|
|
cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_MEMLOCK, StringRef{optarg});
|
|
break;
|
|
case 188:
|
|
// --max-worker-processes
|
|
cmdcfgs.emplace_back(SHRPX_OPT_MAX_WORKER_PROCESSES, StringRef{optarg});
|
|
break;
|
|
case 189:
|
|
// --worker-process-grace-shutdown-period
|
|
cmdcfgs.emplace_back(SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD,
|
|
StringRef{optarg});
|
|
break;
|
|
case 190:
|
|
// --frontend-quic-initial-rtt
|
|
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT,
|
|
StringRef{optarg});
|
|
break;
|
|
case 191:
|
|
// --require-http-scheme
|
|
cmdcfgs.emplace_back(SHRPX_OPT_REQUIRE_HTTP_SCHEME,
|
|
StringRef::from_lit("yes"));
|
|
break;
|
|
case 192:
|
|
// --tls-ktls
|
|
cmdcfgs.emplace_back(SHRPX_OPT_TLS_KTLS, StringRef::from_lit("yes"));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (argc - optind >= 2) {
|
|
cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, StringRef{argv[optind++]});
|
|
cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, StringRef{argv[optind++]});
|
|
}
|
|
|
|
rv = process_options(mod_config(), cmdcfgs);
|
|
if (rv != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (event_loop() != 0) {
|
|
return -1;
|
|
}
|
|
|
|
LOG(NOTICE) << "Shutdown momentarily";
|
|
|
|
delete_log_config();
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace shrpx
|
|
|
|
int main(int argc, char **argv) { return run_app(shrpx::main, argc, argv); }
|