• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012 Tatsuhiro Tsujikawa
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 #include "shrpx.h"
26 
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #ifdef HAVE_SYS_SOCKET_H
31 #  include <sys/socket.h>
32 #endif // HAVE_SYS_SOCKET_H
33 #include <sys/un.h>
34 #ifdef HAVE_NETDB_H
35 #  include <netdb.h>
36 #endif // HAVE_NETDB_H
37 #include <signal.h>
38 #ifdef HAVE_NETINET_IN_H
39 #  include <netinet/in.h>
40 #endif // HAVE_NETINET_IN_H
41 #include <netinet/tcp.h>
42 #ifdef HAVE_ARPA_INET_H
43 #  include <arpa/inet.h>
44 #endif // HAVE_ARPA_INET_H
45 #ifdef HAVE_UNISTD_H
46 #  include <unistd.h>
47 #endif // HAVE_UNISTD_H
48 #include <getopt.h>
49 #ifdef HAVE_SYSLOG_H
50 #  include <syslog.h>
51 #endif // HAVE_SYSLOG_H
52 #ifdef HAVE_LIMITS_H
53 #  include <limits.h>
54 #endif // HAVE_LIMITS_H
55 #ifdef HAVE_SYS_TIME_H
56 #  include <sys/time.h>
57 #endif // HAVE_SYS_TIME_H
58 #include <sys/resource.h>
59 #ifdef HAVE_LIBSYSTEMD
60 #  include <systemd/sd-daemon.h>
61 #endif // HAVE_LIBSYSTEMD
62 #ifdef HAVE_LIBBPF
63 #  include <bpf/libbpf.h>
64 #endif // HAVE_LIBBPF
65 
66 #include <cinttypes>
67 #include <limits>
68 #include <cstdlib>
69 #include <iostream>
70 #include <fstream>
71 #include <vector>
72 #include <initializer_list>
73 #include <random>
74 #include <span>
75 
76 #include <openssl/ssl.h>
77 #include <openssl/err.h>
78 #include <openssl/rand.h>
79 #include <ev.h>
80 
81 #include <nghttp2/nghttp2.h>
82 
83 #ifdef ENABLE_HTTP3
84 #  include <ngtcp2/ngtcp2.h>
85 #  include <nghttp3/nghttp3.h>
86 #endif // ENABLE_HTTP3
87 
88 #include "shrpx_config.h"
89 #include "shrpx_tls.h"
90 #include "shrpx_log_config.h"
91 #include "shrpx_worker.h"
92 #include "shrpx_http2_upstream.h"
93 #include "shrpx_http2_session.h"
94 #include "shrpx_worker_process.h"
95 #include "shrpx_process.h"
96 #include "shrpx_signal.h"
97 #include "shrpx_connection.h"
98 #include "shrpx_log.h"
99 #include "shrpx_http.h"
100 #include "util.h"
101 #include "app_helper.h"
102 #include "tls.h"
103 #include "template.h"
104 #include "allocator.h"
105 #include "ssl_compat.h"
106 #include "xsi_strerror.h"
107 
108 extern char **environ;
109 
110 using namespace nghttp2;
111 
112 namespace shrpx {
113 
114 // Deprecated: Environment variables to tell new binary the listening
115 // socket's file descriptors.  They are not close-on-exec.
116 constexpr auto ENV_LISTENER4_FD = "NGHTTPX_LISTENER4_FD"_sr;
117 constexpr auto ENV_LISTENER6_FD = "NGHTTPX_LISTENER6_FD"_sr;
118 
119 // Deprecated: Environment variable to tell new binary the port number
120 // the current binary is listening to.
121 constexpr auto ENV_PORT = "NGHTTPX_PORT"_sr;
122 
123 // Deprecated: Environment variable to tell new binary the listening
124 // socket's file descriptor if frontend listens UNIX domain socket.
125 constexpr auto ENV_UNIX_FD = "NGHTTP2_UNIX_FD"_sr;
126 // Deprecated: Environment variable to tell new binary the UNIX domain
127 // socket path.
128 constexpr auto ENV_UNIX_PATH = "NGHTTP2_UNIX_PATH"_sr;
129 
130 // Prefix of environment variables to tell new binary the listening
131 // socket's file descriptor.  They are not close-on-exec.  For TCP
132 // socket, the value must be comma separated 2 parameters: tcp,<FD>.
133 // <FD> is file descriptor.  For UNIX domain socket, the value must be
134 // comma separated 3 parameters: unix,<FD>,<PATH>.  <FD> is file
135 // descriptor.  <PATH> is a path to UNIX domain socket.
136 constexpr auto ENV_ACCEPT_PREFIX = "NGHTTPX_ACCEPT_"_sr;
137 
138 // This environment variable contains PID of the original main
139 // process, assuming that it created this main process as a result of
140 // SIGUSR2.  The new main process is expected to send QUIT signal to
141 // the original main process to shut it down gracefully.
142 constexpr auto ENV_ORIG_PID = "NGHTTPX_ORIG_PID"_sr;
143 
144 // Prefix of environment variables to tell new binary the QUIC IPC
145 // file descriptor and Worker ID of the lingering worker process.  The
146 // value must be comma separated parameters:
147 //
148 // <FD>,<WORKER_ID_0>,<WORKER_ID_1>,...,<WORKER_ID_I>
149 //
150 // <FD> is the file descriptor.  <WORKER_ID_I> is the I-th Worker ID
151 // in hex encoded string.
152 constexpr auto ENV_QUIC_WORKER_PROCESS_PREFIX =
153     "NGHTTPX_QUIC_WORKER_PROCESS_"_sr;
154 
155 #ifndef _KERNEL_FASTOPEN
156 #  define _KERNEL_FASTOPEN
157 // conditional define for TCP_FASTOPEN mostly on ubuntu
158 #  ifndef TCP_FASTOPEN
159 #    define TCP_FASTOPEN 23
160 #  endif
161 
162 // conditional define for SOL_TCP mostly on ubuntu
163 #  ifndef SOL_TCP
164 #    define SOL_TCP 6
165 #  endif
166 #endif
167 
168 // This configuration is fixed at the first startup of the main
169 // process, and does not change after subsequent reloadings.
170 struct StartupConfig {
171   // This contains all options given in command-line.
172   std::vector<std::pair<StringRef, StringRef>> cmdcfgs;
173   // The current working directory where this process started.
174   char *cwd;
175   // The pointer to original argv (not sure why we have this?)
176   char **original_argv;
177   // The pointer to argv, this is a deep copy of original argv.
178   char **argv;
179   // The number of elements in argv.
180   int argc;
181 };
182 
183 namespace {
184 StartupConfig suconfig;
185 } // namespace
186 
187 struct InheritedAddr {
188   // IP address if TCP socket.  Otherwise, UNIX domain socket path.
189   StringRef host;
190   uint16_t port;
191   // true if UNIX domain socket path
192   bool host_unix;
193   int fd;
194   bool used;
195 };
196 
197 namespace {
198 void signal_cb(struct ev_loop *loop, ev_signal *w, int revents);
199 } // namespace
200 
201 namespace {
202 void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents);
203 } // namespace
204 
205 struct WorkerProcess {
WorkerProcessshrpx::WorkerProcess206   WorkerProcess(struct ev_loop *loop, pid_t worker_pid, int ipc_fd
207 #ifdef ENABLE_HTTP3
208                 ,
209                 int quic_ipc_fd, std::vector<WorkerID> worker_ids, uint16_t seq
210 #endif // ENABLE_HTTP3
211                 )
212       : loop(loop),
213         worker_pid(worker_pid),
214         ipc_fd(ipc_fd)
215 #ifdef ENABLE_HTTP3
216         ,
217         quic_ipc_fd(quic_ipc_fd),
218         worker_ids(std::move(worker_ids)),
219         seq(seq)
220 #endif // ENABLE_HTTP3
221   {
222     ev_child_init(&worker_process_childev, worker_process_child_cb, worker_pid,
223                   0);
224     worker_process_childev.data = this;
225     ev_child_start(loop, &worker_process_childev);
226   }
227 
~WorkerProcessshrpx::WorkerProcess228   ~WorkerProcess() {
229     ev_child_stop(loop, &worker_process_childev);
230 
231 #ifdef ENABLE_HTTP3
232     if (quic_ipc_fd != -1) {
233       close(quic_ipc_fd);
234     }
235 #endif // ENABLE_HTTP3
236 
237     if (ipc_fd != -1) {
238       shutdown(ipc_fd, SHUT_WR);
239       close(ipc_fd);
240     }
241   }
242 
243   ev_child worker_process_childev;
244   struct ev_loop *loop;
245   pid_t worker_pid;
246   int ipc_fd;
247   std::chrono::steady_clock::time_point termination_deadline;
248 #ifdef ENABLE_HTTP3
249   int quic_ipc_fd;
250   std::vector<WorkerID> worker_ids;
251   uint16_t seq;
252 #endif // ENABLE_HTTP3
253 };
254 
255 namespace {
256 void reload_config();
257 } // namespace
258 
259 namespace {
260 std::deque<std::unique_ptr<WorkerProcess>> worker_processes;
261 
262 #ifdef ENABLE_HTTP3
263 uint16_t worker_process_seq;
264 #endif // ENABLE_HTTP3
265 } // namespace
266 
267 namespace {
268 ev_timer worker_process_grace_period_timer;
269 } // namespace
270 
271 namespace {
worker_process_grace_period_timercb(struct ev_loop * loop,ev_timer * w,int revents)272 void worker_process_grace_period_timercb(struct ev_loop *loop, ev_timer *w,
273                                          int revents) {
274   auto now = std::chrono::steady_clock::now();
275   auto next_repeat = std::chrono::steady_clock::duration::zero();
276 
277   for (auto it = std::begin(worker_processes);
278        it != std::end(worker_processes);) {
279     auto &wp = *it;
280     if (wp->termination_deadline.time_since_epoch().count() == 0) {
281       ++it;
282 
283       continue;
284     }
285 
286     auto d = wp->termination_deadline - now;
287     if (d.count() > 0) {
288       if (next_repeat == std::chrono::steady_clock::duration::zero() ||
289           d < next_repeat) {
290         next_repeat = d;
291       }
292 
293       ++it;
294 
295       continue;
296     }
297 
298     LOG(NOTICE) << "Deleting worker process pid=" << wp->worker_pid
299                 << " because its grace shutdown period is over";
300 
301     it = worker_processes.erase(it);
302   }
303 
304   if (next_repeat.count() > 0) {
305     w->repeat = util::ev_tstamp_from(next_repeat);
306     ev_timer_again(loop, w);
307 
308     return;
309   }
310 
311   ev_timer_stop(loop, w);
312 }
313 } // namespace
314 
315 namespace {
worker_process_set_termination_deadline(WorkerProcess * wp,struct ev_loop * loop)316 void worker_process_set_termination_deadline(WorkerProcess *wp,
317                                              struct ev_loop *loop) {
318   auto config = get_config();
319 
320   if (!(config->worker_process_grace_shutdown_period > 0.)) {
321     return;
322   }
323 
324   wp->termination_deadline =
325       std::chrono::steady_clock::now() +
326       util::duration_from(config->worker_process_grace_shutdown_period);
327 
328   if (!ev_is_active(&worker_process_grace_period_timer)) {
329     worker_process_grace_period_timer.repeat =
330         config->worker_process_grace_shutdown_period;
331 
332     ev_timer_again(loop, &worker_process_grace_period_timer);
333   }
334 }
335 } // namespace
336 
337 namespace {
worker_process_add(std::unique_ptr<WorkerProcess> wp)338 void worker_process_add(std::unique_ptr<WorkerProcess> wp) {
339   worker_processes.push_back(std::move(wp));
340 }
341 } // namespace
342 
343 namespace {
worker_process_remove(const WorkerProcess * wp,struct ev_loop * loop)344 void worker_process_remove(const WorkerProcess *wp, struct ev_loop *loop) {
345   for (auto it = std::begin(worker_processes); it != std::end(worker_processes);
346        ++it) {
347     auto &s = *it;
348 
349     if (s.get() != wp) {
350       continue;
351     }
352 
353     worker_processes.erase(it);
354 
355     if (worker_processes.empty()) {
356       ev_timer_stop(loop, &worker_process_grace_period_timer);
357     }
358 
359     break;
360   }
361 }
362 } // namespace
363 
364 namespace {
worker_process_adjust_limit()365 void worker_process_adjust_limit() {
366   auto config = get_config();
367 
368   if (config->max_worker_processes &&
369       worker_processes.size() > config->max_worker_processes) {
370     worker_processes.pop_front();
371   }
372 }
373 } // namespace
374 
375 namespace {
worker_process_remove_all(struct ev_loop * loop)376 void worker_process_remove_all(struct ev_loop *loop) {
377   std::deque<std::unique_ptr<WorkerProcess>>().swap(worker_processes);
378 
379   ev_timer_stop(loop, &worker_process_grace_period_timer);
380 }
381 } // namespace
382 
383 namespace {
384 // Send signal |signum| to all worker processes, and clears
385 // worker_processes.
worker_process_kill(int signum,struct ev_loop * loop)386 void worker_process_kill(int signum, struct ev_loop *loop) {
387   for (auto &s : worker_processes) {
388     if (s->worker_pid == -1) {
389       continue;
390     }
391     kill(s->worker_pid, signum);
392   }
393   worker_process_remove_all(loop);
394 }
395 } // namespace
396 
397 namespace {
save_pid()398 int save_pid() {
399   std::array<char, STRERROR_BUFSIZE> errbuf;
400   auto config = get_config();
401 
402   constexpr auto SUFFIX = ".XXXXXX"_sr;
403   auto &pid_file = config->pid_file;
404 
405   auto len = config->pid_file.size() + SUFFIX.size();
406   auto buf = std::make_unique<char[]>(len + 1);
407   auto p = buf.get();
408 
409   p = std::copy(std::begin(pid_file), std::end(pid_file), p);
410   p = std::copy(std::begin(SUFFIX), std::end(SUFFIX), p);
411   *p = '\0';
412 
413   auto temp_path = buf.get();
414 
415   auto fd = mkstemp(temp_path);
416   if (fd == -1) {
417     auto error = errno;
418     LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
419                << xsi_strerror(error, errbuf.data(), errbuf.size());
420     return -1;
421   }
422 
423   auto content = util::utos(config->pid) + '\n';
424 
425   if (write(fd, content.c_str(), content.size()) == -1) {
426     auto error = errno;
427     LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
428                << xsi_strerror(error, errbuf.data(), errbuf.size());
429     return -1;
430   }
431 
432   if (fsync(fd) == -1) {
433     auto error = errno;
434     LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
435                << xsi_strerror(error, errbuf.data(), errbuf.size());
436     return -1;
437   }
438 
439   close(fd);
440 
441   if (rename(temp_path, pid_file.data()) == -1) {
442     auto error = errno;
443     LOG(ERROR) << "Could not save PID to file " << pid_file << ": "
444                << xsi_strerror(error, errbuf.data(), errbuf.size());
445 
446     unlink(temp_path);
447 
448     return -1;
449   }
450 
451   if (config->uid != 0) {
452     if (chown(pid_file.data(), config->uid, config->gid) == -1) {
453       auto error = errno;
454       LOG(WARN) << "Changing owner of pid file " << pid_file << " failed: "
455                 << xsi_strerror(error, errbuf.data(), errbuf.size());
456     }
457   }
458 
459   return 0;
460 }
461 } // namespace
462 
463 namespace {
shrpx_sd_notifyf(int unset_environment,const char * format,...)464 void shrpx_sd_notifyf(int unset_environment, const char *format, ...) {
465 #ifdef HAVE_LIBSYSTEMD
466   va_list args;
467 
468   va_start(args, format);
469   sd_notifyf(unset_environment, format, va_arg(args, char *));
470   va_end(args);
471 #endif // HAVE_LIBSYSTEMD
472 }
473 } // namespace
474 
475 namespace {
exec_binary()476 void exec_binary() {
477   int rv;
478   sigset_t oldset;
479   std::array<char, STRERROR_BUFSIZE> errbuf;
480 
481   LOG(NOTICE) << "Executing new binary";
482 
483   shrpx_sd_notifyf(0, "RELOADING=1");
484 
485   rv = shrpx_signal_block_all(&oldset);
486   if (rv != 0) {
487     auto error = errno;
488     LOG(ERROR) << "Blocking all signals failed: "
489                << xsi_strerror(error, errbuf.data(), errbuf.size());
490 
491     return;
492   }
493 
494   auto pid = fork();
495 
496   if (pid != 0) {
497     if (pid == -1) {
498       auto error = errno;
499       LOG(ERROR) << "fork() failed errno=" << error;
500     } else {
501       // update PID tracking information in systemd
502       shrpx_sd_notifyf(0, "MAINPID=%d\n", pid);
503     }
504 
505     rv = shrpx_signal_set(&oldset);
506 
507     if (rv != 0) {
508       auto error = errno;
509       LOG(FATAL) << "Restoring signal mask failed: "
510                  << xsi_strerror(error, errbuf.data(), errbuf.size());
511 
512       exit(EXIT_FAILURE);
513     }
514 
515     return;
516   }
517 
518   // child process
519 
520   shrpx_signal_unset_main_proc_ign_handler();
521 
522   rv = shrpx_signal_unblock_all();
523   if (rv != 0) {
524     auto error = errno;
525     LOG(ERROR) << "Unblocking all signals failed: "
526                << xsi_strerror(error, errbuf.data(), errbuf.size());
527 
528     nghttp2_Exit(EXIT_FAILURE);
529   }
530 
531   auto exec_path =
532       util::get_exec_path(suconfig.argc, suconfig.argv, suconfig.cwd);
533 
534   if (!exec_path) {
535     LOG(ERROR) << "Could not resolve the executable path";
536     nghttp2_Exit(EXIT_FAILURE);
537   }
538 
539   auto argv = std::make_unique<char *[]>(suconfig.argc + 1);
540 
541   argv[0] = exec_path;
542   for (int i = 1; i < suconfig.argc; ++i) {
543     argv[i] = suconfig.argv[i];
544   }
545   argv[suconfig.argc] = nullptr;
546 
547   size_t envlen = 0;
548   for (char **p = environ; *p; ++p, ++envlen)
549     ;
550 
551   auto config = get_config();
552   auto &listenerconf = config->conn.listener;
553 
554   // 2 for ENV_ORIG_PID and terminal nullptr.
555   auto envp = std::make_unique<char *[]>(envlen + listenerconf.addrs.size() +
556                                          worker_processes.size() + 2);
557   size_t envidx = 0;
558 
559   std::vector<ImmutableString> fd_envs;
560   for (size_t i = 0; i < listenerconf.addrs.size(); ++i) {
561     auto &addr = listenerconf.addrs[i];
562     auto s = std::string{ENV_ACCEPT_PREFIX};
563     s += util::utos(i + 1);
564     s += '=';
565     if (addr.host_unix) {
566       s += "unix,";
567       s += util::utos(addr.fd);
568       s += ',';
569       s += addr.host;
570     } else {
571       s += "tcp,";
572       s += util::utos(addr.fd);
573     }
574 
575     fd_envs.emplace_back(s);
576     envp[envidx++] = const_cast<char *>(fd_envs.back().c_str());
577   }
578 
579   auto ipc_fd_str = std::string{ENV_ORIG_PID};
580   ipc_fd_str += '=';
581   ipc_fd_str += util::utos(config->pid);
582   envp[envidx++] = const_cast<char *>(ipc_fd_str.c_str());
583 
584 #ifdef ENABLE_HTTP3
585   std::vector<ImmutableString> quic_lwps;
586   for (size_t i = 0; i < worker_processes.size(); ++i) {
587     auto &wp = worker_processes[i];
588     auto s = std::string{ENV_QUIC_WORKER_PROCESS_PREFIX};
589     s += util::utos(i + 1);
590     s += '=';
591     s += util::utos(wp->quic_ipc_fd);
592     for (auto &wid : wp->worker_ids) {
593       s += ',';
594       s += util::format_hex(std::span{&wid, 1});
595     }
596 
597     quic_lwps.emplace_back(s);
598     envp[envidx++] = const_cast<char *>(quic_lwps.back().c_str());
599   }
600 #endif // ENABLE_HTTP3
601 
602   for (size_t i = 0; i < envlen; ++i) {
603     auto env = StringRef{environ[i]};
604     if (util::starts_with(env, ENV_ACCEPT_PREFIX) ||
605         util::starts_with(env, ENV_LISTENER4_FD) ||
606         util::starts_with(env, ENV_LISTENER6_FD) ||
607         util::starts_with(env, ENV_PORT) ||
608         util::starts_with(env, ENV_UNIX_FD) ||
609         util::starts_with(env, ENV_UNIX_PATH) ||
610         util::starts_with(env, ENV_ORIG_PID) ||
611         util::starts_with(env, ENV_QUIC_WORKER_PROCESS_PREFIX)) {
612       continue;
613     }
614 
615     envp[envidx++] = environ[i];
616   }
617 
618   envp[envidx++] = nullptr;
619 
620   if (LOG_ENABLED(INFO)) {
621     LOG(INFO) << "cmdline";
622     for (int i = 0; argv[i]; ++i) {
623       LOG(INFO) << i << ": " << argv[i];
624     }
625     LOG(INFO) << "environ";
626     for (int i = 0; envp[i]; ++i) {
627       LOG(INFO) << i << ": " << envp[i];
628     }
629   }
630 
631   // restores original stderr
632   restore_original_fds();
633 
634   // reloading finished
635   shrpx_sd_notifyf(0, "READY=1");
636 
637   if (execve(argv[0], argv.get(), envp.get()) == -1) {
638     auto error = errno;
639     LOG(ERROR) << "execve failed: errno=" << error;
640     nghttp2_Exit(EXIT_FAILURE);
641   }
642 }
643 } // namespace
644 
645 namespace {
ipc_send(WorkerProcess * wp,uint8_t ipc_event)646 void ipc_send(WorkerProcess *wp, uint8_t ipc_event) {
647   std::array<char, STRERROR_BUFSIZE> errbuf;
648   ssize_t nwrite;
649   while ((nwrite = write(wp->ipc_fd, &ipc_event, 1)) == -1 && errno == EINTR)
650     ;
651 
652   if (nwrite < 0) {
653     auto error = errno;
654     LOG(ERROR) << "Could not send IPC event to worker process: "
655                << xsi_strerror(error, errbuf.data(), errbuf.size());
656     return;
657   }
658 
659   if (nwrite == 0) {
660     LOG(ERROR) << "Could not send IPC event due to pipe overflow";
661     return;
662   }
663 }
664 } // namespace
665 
666 namespace {
reopen_log(WorkerProcess * wp)667 void reopen_log(WorkerProcess *wp) {
668   LOG(NOTICE) << "Reopening log files: main process";
669 
670   auto config = get_config();
671   auto &loggingconf = config->logging;
672 
673   (void)reopen_log_files(loggingconf);
674   redirect_stderr_to_errorlog(loggingconf);
675   ipc_send(wp, SHRPX_IPC_REOPEN_LOG);
676 }
677 } // namespace
678 
679 namespace {
signal_cb(struct ev_loop * loop,ev_signal * w,int revents)680 void signal_cb(struct ev_loop *loop, ev_signal *w, int revents) {
681   switch (w->signum) {
682   case REOPEN_LOG_SIGNAL:
683     for (auto &wp : worker_processes) {
684       reopen_log(wp.get());
685     }
686 
687     return;
688   case EXEC_BINARY_SIGNAL:
689     exec_binary();
690     return;
691   case GRACEFUL_SHUTDOWN_SIGNAL: {
692     auto &listenerconf = get_config()->conn.listener;
693     for (auto &addr : listenerconf.addrs) {
694       close(addr.fd);
695     }
696 
697     for (auto &wp : worker_processes) {
698       ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
699       worker_process_set_termination_deadline(wp.get(), loop);
700     }
701 
702     return;
703   }
704   case RELOAD_SIGNAL:
705     reload_config();
706 
707     return;
708   default:
709     worker_process_kill(w->signum, loop);
710     ev_break(loop);
711     return;
712   }
713 }
714 } // namespace
715 
716 namespace {
worker_process_child_cb(struct ev_loop * loop,ev_child * w,int revents)717 void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
718   auto wp = static_cast<WorkerProcess *>(w->data);
719 
720   log_chld(w->rpid, w->rstatus, "Worker process");
721 
722   worker_process_remove(wp, loop);
723 
724   if (worker_processes.empty()) {
725     ev_break(loop);
726   }
727 }
728 } // namespace
729 
730 namespace {
create_unix_domain_server_socket(UpstreamAddr & faddr,std::vector<InheritedAddr> & iaddrs)731 int create_unix_domain_server_socket(UpstreamAddr &faddr,
732                                      std::vector<InheritedAddr> &iaddrs) {
733   std::array<char, STRERROR_BUFSIZE> errbuf;
734   auto found = std::find_if(
735       std::begin(iaddrs), std::end(iaddrs), [&faddr](const InheritedAddr &ia) {
736         return !ia.used && ia.host_unix && ia.host == faddr.host;
737       });
738 
739   if (found != std::end(iaddrs)) {
740     LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host
741                 << (faddr.tls ? ", tls" : "");
742     (*found).used = true;
743     faddr.fd = (*found).fd;
744     faddr.hostport = "localhost"_sr;
745 
746     return 0;
747   }
748 
749 #ifdef SOCK_NONBLOCK
750   auto fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
751   if (fd == -1) {
752     auto error = errno;
753     LOG(FATAL) << "socket() syscall failed: "
754                << xsi_strerror(error, errbuf.data(), errbuf.size());
755     return -1;
756   }
757 #else  // !SOCK_NONBLOCK
758   auto fd = socket(AF_UNIX, SOCK_STREAM, 0);
759   if (fd == -1) {
760     auto error = errno;
761     LOG(FATAL) << "socket() syscall failed: "
762                << xsi_strerror(error, errbuf.data(), errbuf.size());
763     return -1;
764   }
765   util::make_socket_nonblocking(fd);
766 #endif // !SOCK_NONBLOCK
767   int val = 1;
768   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
769                  static_cast<socklen_t>(sizeof(val))) == -1) {
770     auto error = errno;
771     LOG(FATAL) << "Failed to set SO_REUSEADDR option to listener socket: "
772                << xsi_strerror(error, errbuf.data(), errbuf.size());
773     close(fd);
774     return -1;
775   }
776 
777   sockaddr_union addr;
778   addr.un.sun_family = AF_UNIX;
779   if (faddr.host.size() + 1 > sizeof(addr.un.sun_path)) {
780     LOG(FATAL) << "UNIX domain socket path " << faddr.host << " is too long > "
781                << sizeof(addr.un.sun_path);
782     close(fd);
783     return -1;
784   }
785   // copy path including terminal NULL
786   std::copy_n(faddr.host.data(), faddr.host.size() + 1, addr.un.sun_path);
787 
788   // unlink (remove) already existing UNIX domain socket path
789   unlink(faddr.host.data());
790 
791   if (bind(fd, &addr.sa, sizeof(addr.un)) != 0) {
792     auto error = errno;
793     LOG(FATAL) << "Failed to bind UNIX domain socket: "
794                << xsi_strerror(error, errbuf.data(), errbuf.size());
795     close(fd);
796     return -1;
797   }
798 
799   auto &listenerconf = get_config()->conn.listener;
800 
801   if (listen(fd, listenerconf.backlog) != 0) {
802     auto error = errno;
803     LOG(FATAL) << "Failed to listen to UNIX domain socket: "
804                << xsi_strerror(error, errbuf.data(), errbuf.size());
805     close(fd);
806     return -1;
807   }
808 
809   LOG(NOTICE) << "Listening on UNIX domain socket " << faddr.host
810               << (faddr.tls ? ", tls" : "");
811 
812   faddr.fd = fd;
813   faddr.hostport = "localhost"_sr;
814 
815   return 0;
816 }
817 } // namespace
818 
819 namespace {
create_tcp_server_socket(UpstreamAddr & faddr,std::vector<InheritedAddr> & iaddrs)820 int create_tcp_server_socket(UpstreamAddr &faddr,
821                              std::vector<InheritedAddr> &iaddrs) {
822   std::array<char, STRERROR_BUFSIZE> errbuf;
823   int fd = -1;
824   int rv;
825 
826   auto &listenerconf = get_config()->conn.listener;
827 
828   auto service = util::utos(faddr.port);
829   addrinfo hints{};
830   hints.ai_family = faddr.family;
831   hints.ai_socktype = SOCK_STREAM;
832   hints.ai_flags = AI_PASSIVE;
833 #ifdef AI_ADDRCONFIG
834   hints.ai_flags |= AI_ADDRCONFIG;
835 #endif // AI_ADDRCONFIG
836 
837   auto node = faddr.host == "*"_sr ? nullptr : faddr.host.data();
838 
839   addrinfo *res, *rp;
840   rv = getaddrinfo(node, service.c_str(), &hints, &res);
841 #ifdef AI_ADDRCONFIG
842   if (rv != 0) {
843     // Retry without AI_ADDRCONFIG
844     hints.ai_flags &= ~AI_ADDRCONFIG;
845     rv = getaddrinfo(node, service.c_str(), &hints, &res);
846   }
847 #endif // AI_ADDRCONFIG
848   if (rv != 0) {
849     LOG(FATAL) << "Unable to get IPv" << (faddr.family == AF_INET ? "4" : "6")
850                << " address for " << faddr.host << ", port " << faddr.port
851                << ": " << gai_strerror(rv);
852     return -1;
853   }
854 
855   auto res_d = defer(freeaddrinfo, res);
856 
857   std::array<char, NI_MAXHOST> host;
858 
859   for (rp = res; rp; rp = rp->ai_next) {
860 
861     rv = getnameinfo(rp->ai_addr, rp->ai_addrlen, host.data(), host.size(),
862                      nullptr, 0, NI_NUMERICHOST);
863 
864     if (rv != 0) {
865       LOG(WARN) << "getnameinfo() failed: " << gai_strerror(rv);
866       continue;
867     }
868 
869     auto host_sr = StringRef{host.data()};
870 
871     auto found = std::find_if(std::begin(iaddrs), std::end(iaddrs),
872                               [&host_sr, &faddr](const InheritedAddr &ia) {
873                                 return !ia.used && !ia.host_unix &&
874                                        ia.host == host_sr &&
875                                        ia.port == faddr.port;
876                               });
877 
878     if (found != std::end(iaddrs)) {
879       (*found).used = true;
880       fd = (*found).fd;
881       break;
882     }
883 
884 #ifdef SOCK_NONBLOCK
885     fd =
886         socket(rp->ai_family, rp->ai_socktype | SOCK_NONBLOCK, rp->ai_protocol);
887     if (fd == -1) {
888       auto error = errno;
889       LOG(WARN) << "socket() syscall failed: "
890                 << xsi_strerror(error, errbuf.data(), errbuf.size());
891       continue;
892     }
893 #else  // !SOCK_NONBLOCK
894     fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
895     if (fd == -1) {
896       auto error = errno;
897       LOG(WARN) << "socket() syscall failed: "
898                 << xsi_strerror(error, errbuf.data(), errbuf.size());
899       continue;
900     }
901     util::make_socket_nonblocking(fd);
902 #endif // !SOCK_NONBLOCK
903     int val = 1;
904     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
905                    static_cast<socklen_t>(sizeof(val))) == -1) {
906       auto error = errno;
907       LOG(WARN) << "Failed to set SO_REUSEADDR option to listener socket: "
908                 << xsi_strerror(error, errbuf.data(), errbuf.size());
909       close(fd);
910       continue;
911     }
912 
913 #ifdef IPV6_V6ONLY
914     if (faddr.family == AF_INET6) {
915       if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
916                      static_cast<socklen_t>(sizeof(val))) == -1) {
917         auto error = errno;
918         LOG(WARN) << "Failed to set IPV6_V6ONLY option to listener socket: "
919                   << xsi_strerror(error, errbuf.data(), errbuf.size());
920         close(fd);
921         continue;
922       }
923     }
924 #endif // IPV6_V6ONLY
925 
926 #ifdef TCP_DEFER_ACCEPT
927     val = 3;
928     if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &val,
929                    static_cast<socklen_t>(sizeof(val))) == -1) {
930       auto error = errno;
931       LOG(WARN) << "Failed to set TCP_DEFER_ACCEPT option to listener socket: "
932                 << xsi_strerror(error, errbuf.data(), errbuf.size());
933     }
934 #endif // TCP_DEFER_ACCEPT
935 
936     // When we are executing new binary, and the old binary did not
937     // bind privileged port (< 1024) for some reason, binding to those
938     // ports will fail with permission denied error.
939     if (bind(fd, rp->ai_addr, rp->ai_addrlen) == -1) {
940       auto error = errno;
941       LOG(WARN) << "bind() syscall failed: "
942                 << xsi_strerror(error, errbuf.data(), errbuf.size());
943       close(fd);
944       continue;
945     }
946 
947     if (listenerconf.fastopen > 0) {
948       val = listenerconf.fastopen;
949       if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &val,
950                      static_cast<socklen_t>(sizeof(val))) == -1) {
951         auto error = errno;
952         LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket: "
953                   << xsi_strerror(error, errbuf.data(), errbuf.size());
954       }
955     }
956 
957     if (listen(fd, listenerconf.backlog) == -1) {
958       auto error = errno;
959       LOG(WARN) << "listen() syscall failed: "
960                 << xsi_strerror(error, errbuf.data(), errbuf.size());
961       close(fd);
962       continue;
963     }
964 
965     break;
966   }
967 
968   if (!rp) {
969     LOG(FATAL) << "Listening " << (faddr.family == AF_INET ? "IPv4" : "IPv6")
970                << " socket failed";
971 
972     return -1;
973   }
974 
975   faddr.fd = fd;
976   faddr.hostport = util::make_http_hostport(mod_config()->balloc,
977                                             StringRef{host.data()}, faddr.port);
978 
979   LOG(NOTICE) << "Listening on " << faddr.hostport
980               << (faddr.tls ? ", tls" : "");
981 
982   return 0;
983 }
984 } // namespace
985 
986 namespace {
987 // Returns array of InheritedAddr constructed from |config|.  This
988 // function is intended to be used when reloading configuration, and
989 // |config| is usually a current configuration.
990 std::vector<InheritedAddr>
get_inherited_addr_from_config(BlockAllocator & balloc,Config * config)991 get_inherited_addr_from_config(BlockAllocator &balloc, Config *config) {
992   std::array<char, STRERROR_BUFSIZE> errbuf;
993   int rv;
994 
995   auto &listenerconf = config->conn.listener;
996 
997   std::vector<InheritedAddr> iaddrs(listenerconf.addrs.size());
998 
999   size_t idx = 0;
1000   for (auto &addr : listenerconf.addrs) {
1001     auto &iaddr = iaddrs[idx++];
1002 
1003     if (addr.host_unix) {
1004       iaddr.host = addr.host;
1005       iaddr.host_unix = true;
1006       iaddr.fd = addr.fd;
1007 
1008       continue;
1009     }
1010 
1011     iaddr.port = addr.port;
1012     iaddr.fd = addr.fd;
1013 
1014     // We have to getsockname/getnameinfo for fd, since we may have
1015     // '*' appear in addr.host, which makes comparison against "real"
1016     // address fail.
1017 
1018     sockaddr_union su;
1019     socklen_t salen = sizeof(su);
1020 
1021     // We already added entry to iaddrs.  Even if we got errors, we
1022     // don't remove it.  This is required because we have to close the
1023     // socket if it is not reused.  The empty host name usually does
1024     // not match anything.
1025 
1026     if (getsockname(addr.fd, &su.sa, &salen) != 0) {
1027       auto error = errno;
1028       LOG(WARN) << "getsockname() syscall failed (fd=" << addr.fd
1029                 << "): " << xsi_strerror(error, errbuf.data(), errbuf.size());
1030       continue;
1031     }
1032 
1033     std::array<char, NI_MAXHOST> host;
1034     rv = getnameinfo(&su.sa, salen, host.data(), host.size(), nullptr, 0,
1035                      NI_NUMERICHOST);
1036     if (rv != 0) {
1037       LOG(WARN) << "getnameinfo() failed (fd=" << addr.fd
1038                 << "): " << gai_strerror(rv);
1039       continue;
1040     }
1041 
1042     iaddr.host = make_string_ref(balloc, StringRef{host.data()});
1043   }
1044 
1045   return iaddrs;
1046 }
1047 } // namespace
1048 
1049 namespace {
1050 // Returns array of InheritedAddr constructed from environment
1051 // variables.  This function handles the old environment variable
1052 // names used in 1.7.0 or earlier.
get_inherited_addr_from_env(Config * config)1053 std::vector<InheritedAddr> get_inherited_addr_from_env(Config *config) {
1054   std::array<char, STRERROR_BUFSIZE> errbuf;
1055   int rv;
1056   std::vector<InheritedAddr> iaddrs;
1057 
1058   {
1059     // Upgrade from 1.7.0 or earlier
1060     auto portenv = getenv(ENV_PORT.data());
1061     if (portenv) {
1062       size_t i = 1;
1063       for (const auto &env_name : {ENV_LISTENER4_FD, ENV_LISTENER6_FD}) {
1064         auto fdenv = getenv(env_name.data());
1065         if (fdenv) {
1066           auto name = std::string{ENV_ACCEPT_PREFIX};
1067           name += util::utos(i);
1068           std::string value = "tcp,";
1069           value += fdenv;
1070           setenv(name.c_str(), value.c_str(), 0);
1071           ++i;
1072         }
1073       }
1074     } else {
1075       // The return value of getenv may be allocated statically.
1076       if (getenv(ENV_UNIX_PATH.data()) && getenv(ENV_UNIX_FD.data())) {
1077         auto name = std::string{ENV_ACCEPT_PREFIX};
1078         name += '1';
1079         std::string value = "unix,";
1080         value += getenv(ENV_UNIX_FD.data());
1081         value += ',';
1082         value += getenv(ENV_UNIX_PATH.data());
1083         setenv(name.c_str(), value.c_str(), 0);
1084       }
1085     }
1086   }
1087 
1088   for (size_t i = 1;; ++i) {
1089     auto name = std::string{ENV_ACCEPT_PREFIX};
1090     name += util::utos(i);
1091     auto env = getenv(name.c_str());
1092     if (!env) {
1093       break;
1094     }
1095 
1096     if (LOG_ENABLED(INFO)) {
1097       LOG(INFO) << "Read env " << name << "=" << env;
1098     }
1099 
1100     auto end_type = strchr(env, ',');
1101     if (!end_type) {
1102       continue;
1103     }
1104 
1105     auto type = StringRef(env, end_type);
1106     auto value = end_type + 1;
1107 
1108     if (type == "unix"_sr) {
1109       auto endfd = strchr(value, ',');
1110       if (!endfd) {
1111         continue;
1112       }
1113       auto fd = util::parse_uint(StringRef{value, endfd});
1114       if (!fd) {
1115         LOG(WARN) << "Could not parse file descriptor from "
1116                   << std::string(value, endfd - value);
1117         continue;
1118       }
1119 
1120       auto path = endfd + 1;
1121       if (strlen(path) == 0) {
1122         LOG(WARN) << "Empty UNIX domain socket path (fd=" << *fd << ")";
1123         close(*fd);
1124         continue;
1125       }
1126 
1127       if (LOG_ENABLED(INFO)) {
1128         LOG(INFO) << "Inherit UNIX domain socket fd=" << *fd
1129                   << ", path=" << path;
1130       }
1131 
1132       InheritedAddr addr{};
1133       addr.host = make_string_ref(config->balloc, StringRef{path});
1134       addr.host_unix = true;
1135       addr.fd = static_cast<int>(*fd);
1136       iaddrs.push_back(std::move(addr));
1137     }
1138 
1139     if (type == "tcp"_sr) {
1140       auto fd = util::parse_uint(value);
1141       if (!fd) {
1142         LOG(WARN) << "Could not parse file descriptor from " << value;
1143         continue;
1144       }
1145 
1146       sockaddr_union su;
1147       socklen_t salen = sizeof(su);
1148 
1149       if (getsockname(*fd, &su.sa, &salen) != 0) {
1150         auto error = errno;
1151         LOG(WARN) << "getsockname() syscall failed (fd=" << *fd
1152                   << "): " << xsi_strerror(error, errbuf.data(), errbuf.size());
1153         close(*fd);
1154         continue;
1155       }
1156 
1157       uint16_t port;
1158 
1159       switch (su.storage.ss_family) {
1160       case AF_INET:
1161         port = ntohs(su.in.sin_port);
1162         break;
1163       case AF_INET6:
1164         port = ntohs(su.in6.sin6_port);
1165         break;
1166       default:
1167         close(*fd);
1168         continue;
1169       }
1170 
1171       std::array<char, NI_MAXHOST> host;
1172       rv = getnameinfo(&su.sa, salen, host.data(), host.size(), nullptr, 0,
1173                        NI_NUMERICHOST);
1174       if (rv != 0) {
1175         LOG(WARN) << "getnameinfo() failed (fd=" << *fd
1176                   << "): " << gai_strerror(rv);
1177         close(*fd);
1178         continue;
1179       }
1180 
1181       if (LOG_ENABLED(INFO)) {
1182         LOG(INFO) << "Inherit TCP socket fd=" << *fd
1183                   << ", address=" << host.data() << ", port=" << port;
1184       }
1185 
1186       InheritedAddr addr{};
1187       addr.host = make_string_ref(config->balloc, StringRef{host.data()});
1188       addr.port = static_cast<uint16_t>(port);
1189       addr.fd = static_cast<int>(*fd);
1190       iaddrs.push_back(std::move(addr));
1191       continue;
1192     }
1193   }
1194 
1195   return iaddrs;
1196 }
1197 } // namespace
1198 
1199 namespace {
1200 // Closes all sockets which are not reused.
close_unused_inherited_addr(const std::vector<InheritedAddr> & iaddrs)1201 void close_unused_inherited_addr(const std::vector<InheritedAddr> &iaddrs) {
1202   for (auto &ia : iaddrs) {
1203     if (ia.used) {
1204       continue;
1205     }
1206 
1207     close(ia.fd);
1208   }
1209 }
1210 } // namespace
1211 
1212 namespace {
1213 // Returns the PID of the original main process from environment
1214 // variable ENV_ORIG_PID.
get_orig_pid_from_env()1215 pid_t get_orig_pid_from_env() {
1216   auto s = getenv(ENV_ORIG_PID.data());
1217   if (s == nullptr) {
1218     return -1;
1219   }
1220   return util::parse_uint(s).value_or(-1);
1221 }
1222 } // namespace
1223 
1224 #ifdef ENABLE_HTTP3
1225 namespace {
1226 std::vector<QUICLingeringWorkerProcess>
1227     inherited_quic_lingering_worker_processes;
1228 } // namespace
1229 
1230 namespace {
1231 std::vector<QUICLingeringWorkerProcess>
get_inherited_quic_lingering_worker_process_from_env()1232 get_inherited_quic_lingering_worker_process_from_env() {
1233   std::vector<QUICLingeringWorkerProcess> lwps;
1234 
1235   for (size_t i = 1;; ++i) {
1236     auto name = std::string{ENV_QUIC_WORKER_PROCESS_PREFIX};
1237     name += util::utos(i);
1238     auto env = getenv(name.c_str());
1239     if (!env) {
1240       break;
1241     }
1242 
1243     if (LOG_ENABLED(INFO)) {
1244       LOG(INFO) << "Read env " << name << "=" << env;
1245     }
1246 
1247     auto envend = env + strlen(env);
1248 
1249     auto end_fd = std::find(env, envend, ',');
1250     if (end_fd == envend) {
1251       continue;
1252     }
1253 
1254     auto fd = util::parse_uint(StringRef{env, end_fd});
1255     if (!fd) {
1256       LOG(WARN) << "Could not parse file descriptor from "
1257                 << StringRef{env, static_cast<size_t>(end_fd - env)};
1258       continue;
1259     }
1260 
1261     if (LOG_ENABLED(INFO)) {
1262       LOG(INFO) << "Inherit worker process QUIC IPC socket fd=" << *fd;
1263     }
1264 
1265     util::make_socket_closeonexec(*fd);
1266 
1267     std::vector<WorkerID> worker_ids;
1268 
1269     auto p = end_fd + 1;
1270     for (;;) {
1271       auto end = std::find(p, envend, ',');
1272 
1273       auto hex_wid = StringRef{p, end};
1274       if (hex_wid.size() != SHRPX_QUIC_WORKER_IDLEN * 2 ||
1275           !util::is_hex_string(hex_wid)) {
1276         LOG(WARN) << "Found invalid WorkerID=" << hex_wid;
1277         break;
1278       }
1279 
1280       if (LOG_ENABLED(INFO)) {
1281         LOG(INFO) << "Inherit worker process WorkerID=" << hex_wid;
1282       }
1283 
1284       worker_ids.emplace_back();
1285 
1286       util::decode_hex(reinterpret_cast<uint8_t *>(&worker_ids.back()),
1287                        hex_wid);
1288 
1289       if (end == envend) {
1290         break;
1291       }
1292 
1293       p = end + 1;
1294     }
1295 
1296     lwps.emplace_back(std::move(worker_ids), *fd);
1297   }
1298 
1299   if (!lwps.empty()) {
1300     const auto &lwp = lwps.back();
1301 
1302     if (!lwp.worker_ids.empty() &&
1303         worker_process_seq <= lwp.worker_ids[0].worker_process) {
1304       worker_process_seq = lwp.worker_ids[0].worker_process;
1305       ++worker_process_seq;
1306     }
1307   }
1308 
1309   return lwps;
1310 }
1311 } // namespace
1312 #endif // ENABLE_HTTP3
1313 
1314 namespace {
create_acceptor_socket(Config * config,std::vector<InheritedAddr> & iaddrs)1315 int create_acceptor_socket(Config *config, std::vector<InheritedAddr> &iaddrs) {
1316   std::array<char, STRERROR_BUFSIZE> errbuf;
1317   auto &listenerconf = config->conn.listener;
1318 
1319   for (auto &addr : listenerconf.addrs) {
1320     if (addr.host_unix) {
1321       if (create_unix_domain_server_socket(addr, iaddrs) != 0) {
1322         return -1;
1323       }
1324 
1325       if (config->uid != 0) {
1326         // fd is not associated to inode, so we cannot use fchown(2)
1327         // here.  https://lkml.org/lkml/2004/11/1/84
1328         if (chown(addr.host.data(), config->uid, config->gid) == -1) {
1329           auto error = errno;
1330           LOG(WARN) << "Changing owner of UNIX domain socket " << addr.host
1331                     << " failed: "
1332                     << xsi_strerror(error, errbuf.data(), errbuf.size());
1333         }
1334       }
1335       continue;
1336     }
1337 
1338     if (create_tcp_server_socket(addr, iaddrs) != 0) {
1339       return -1;
1340     }
1341   }
1342 
1343   return 0;
1344 }
1345 } // namespace
1346 
1347 namespace {
call_daemon()1348 int call_daemon() {
1349 #ifdef __sgi
1350   return _daemonize(0, 0, 0, 0);
1351 #else // !__sgi
1352 #  ifdef HAVE_LIBSYSTEMD
1353   if (sd_booted() && (getenv("NOTIFY_SOCKET") != nullptr)) {
1354     LOG(NOTICE) << "Daemonising disabled under systemd";
1355     chdir("/");
1356     return 0;
1357   }
1358 #  endif // HAVE_LIBSYSTEMD
1359   return util::daemonize(0, 0);
1360 #endif   // !__sgi
1361 }
1362 } // namespace
1363 
1364 namespace {
1365 // Opens IPC socket used to communicate with worker proess.  The
1366 // communication is unidirectional; that is main process sends
1367 // messages to the worker process.  On success, ipc_fd[0] is for
1368 // reading, and ipc_fd[1] for writing, just like pipe(2).
create_ipc_socket(std::span<int,2> ipc_fd)1369 int create_ipc_socket(std::span<int, 2> ipc_fd) {
1370   std::array<char, STRERROR_BUFSIZE> errbuf;
1371   int rv;
1372 
1373   rv = pipe(ipc_fd.data());
1374   if (rv == -1) {
1375     auto error = errno;
1376     LOG(WARN) << "Failed to create pipe to communicate worker process: "
1377               << xsi_strerror(error, errbuf.data(), errbuf.size());
1378     return -1;
1379   }
1380 
1381   for (auto fd : ipc_fd) {
1382     util::make_socket_nonblocking(fd);
1383     util::make_socket_closeonexec(fd);
1384   }
1385 
1386   return 0;
1387 }
1388 } // namespace
1389 
1390 namespace {
create_worker_process_ready_ipc_socket(std::span<int,2> ipc_fd)1391 int create_worker_process_ready_ipc_socket(std::span<int, 2> ipc_fd) {
1392   std::array<char, STRERROR_BUFSIZE> errbuf;
1393   int rv;
1394 
1395   rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, ipc_fd.data());
1396   if (rv == -1) {
1397     auto error = errno;
1398     LOG(WARN) << "Failed to create socket pair to communicate worker process "
1399                  "readiness: "
1400               << xsi_strerror(error, errbuf.data(), errbuf.size());
1401     return -1;
1402   }
1403 
1404   for (auto fd : ipc_fd) {
1405     util::make_socket_closeonexec(fd);
1406   }
1407 
1408   util::make_socket_nonblocking(ipc_fd[0]);
1409 
1410   return 0;
1411 }
1412 } // namespace
1413 
1414 #ifdef ENABLE_HTTP3
1415 namespace {
create_quic_ipc_socket(std::span<int,2> quic_ipc_fd)1416 int create_quic_ipc_socket(std::span<int, 2> quic_ipc_fd) {
1417   std::array<char, STRERROR_BUFSIZE> errbuf;
1418   int rv;
1419 
1420   rv = socketpair(AF_UNIX, SOCK_DGRAM, 0, quic_ipc_fd.data());
1421   if (rv == -1) {
1422     auto error = errno;
1423     LOG(WARN) << "Failed to create socket pair to communicate worker process: "
1424               << xsi_strerror(error, errbuf.data(), errbuf.size());
1425     return -1;
1426   }
1427 
1428   for (auto fd : quic_ipc_fd) {
1429     util::make_socket_nonblocking(fd);
1430   }
1431 
1432   return 0;
1433 }
1434 } // namespace
1435 
1436 namespace {
generate_worker_id(std::vector<WorkerID> & worker_ids,uint16_t wp_seq,const Config * config)1437 int generate_worker_id(std::vector<WorkerID> &worker_ids, uint16_t wp_seq,
1438                        const Config *config) {
1439   auto &apiconf = config->api;
1440   auto &quicconf = config->quic;
1441 
1442   size_t num_wid;
1443   if (config->single_thread) {
1444     num_wid = 1;
1445   } else {
1446     num_wid = config->num_worker;
1447 
1448     // API endpoint occupies the one dedicated worker thread.
1449     // Although such worker never gets QUIC traffic, we create Worker
1450     // ID for it to make code a bit simpler.
1451     if (apiconf.enabled) {
1452       ++num_wid;
1453     }
1454   }
1455 
1456   worker_ids.resize(num_wid);
1457 
1458   uint16_t idx = 0;
1459 
1460   for (auto &wid : worker_ids) {
1461     wid.server = quicconf.server_id;
1462     wid.worker_process = wp_seq;
1463     wid.thread = idx++;
1464   }
1465 
1466   return 0;
1467 }
1468 } // namespace
1469 
1470 namespace {
1471 std::vector<QUICLingeringWorkerProcess>
collect_quic_lingering_worker_processes()1472 collect_quic_lingering_worker_processes() {
1473   std::vector<QUICLingeringWorkerProcess> quic_lwps{
1474       std::begin(inherited_quic_lingering_worker_processes),
1475       std::end(inherited_quic_lingering_worker_processes)};
1476 
1477   for (auto &wp : worker_processes) {
1478     quic_lwps.emplace_back(wp->worker_ids, wp->quic_ipc_fd);
1479   }
1480 
1481   return quic_lwps;
1482 }
1483 } // namespace
1484 #endif // ENABLE_HTTP3
1485 
1486 namespace {
1487 ev_signal reopen_log_signalev;
1488 ev_signal exec_binary_signalev;
1489 ev_signal graceful_shutdown_signalev;
1490 ev_signal reload_signalev;
1491 } // namespace
1492 
1493 namespace {
start_signal_watchers(struct ev_loop * loop)1494 void start_signal_watchers(struct ev_loop *loop) {
1495   ev_signal_init(&reopen_log_signalev, signal_cb, REOPEN_LOG_SIGNAL);
1496   ev_signal_start(loop, &reopen_log_signalev);
1497 
1498   ev_signal_init(&exec_binary_signalev, signal_cb, EXEC_BINARY_SIGNAL);
1499   ev_signal_start(loop, &exec_binary_signalev);
1500 
1501   ev_signal_init(&graceful_shutdown_signalev, signal_cb,
1502                  GRACEFUL_SHUTDOWN_SIGNAL);
1503   ev_signal_start(loop, &graceful_shutdown_signalev);
1504 
1505   ev_signal_init(&reload_signalev, signal_cb, RELOAD_SIGNAL);
1506   ev_signal_start(loop, &reload_signalev);
1507 }
1508 } // namespace
1509 
1510 namespace {
shutdown_signal_watchers(struct ev_loop * loop)1511 void shutdown_signal_watchers(struct ev_loop *loop) {
1512   ev_signal_stop(loop, &reload_signalev);
1513   ev_signal_stop(loop, &graceful_shutdown_signalev);
1514   ev_signal_stop(loop, &exec_binary_signalev);
1515   ev_signal_stop(loop, &reopen_log_signalev);
1516 }
1517 } // namespace
1518 
1519 namespace {
1520 // A pair of connected socket with which a worker process tells main
1521 // process that it is ready for service.  A worker process writes its
1522 // PID to worker_process_ready_ipc_fd[1] and main process reads it
1523 // from worker_process_ready_ipc_fd[0].
1524 std::array<int, 2> worker_process_ready_ipc_fd;
1525 } // namespace
1526 
1527 namespace {
1528 ev_io worker_process_ready_ipcev;
1529 } // namespace
1530 
1531 namespace {
1532 // PID received via NGHTTPX_ORIG_PID environment variable.
1533 pid_t orig_pid = -1;
1534 } // namespace
1535 
1536 namespace {
worker_process_ready_ipc_readcb(struct ev_loop * loop,ev_io * w,int revents)1537 void worker_process_ready_ipc_readcb(struct ev_loop *loop, ev_io *w,
1538                                      int revents) {
1539   std::array<uint8_t, 8> buf;
1540   ssize_t nread;
1541 
1542   while ((nread = read(w->fd, buf.data(), buf.size())) == -1 && errno == EINTR)
1543     ;
1544 
1545   if (nread == -1) {
1546     std::array<char, STRERROR_BUFSIZE> errbuf;
1547     auto error = errno;
1548 
1549     LOG(ERROR) << "Failed to read data from worker process ready IPC channel: "
1550                << xsi_strerror(error, errbuf.data(), errbuf.size());
1551 
1552     return;
1553   }
1554 
1555   if (nread == 0) {
1556     return;
1557   }
1558 
1559   if (nread != sizeof(pid_t)) {
1560     LOG(ERROR) << "Read " << nread
1561                << " bytes from worker process ready IPC channel";
1562 
1563     return;
1564   }
1565 
1566   pid_t pid;
1567 
1568   memcpy(&pid, buf.data(), sizeof(pid));
1569 
1570   LOG(NOTICE) << "Worker process pid=" << pid << " is ready";
1571 
1572   for (auto &wp : worker_processes) {
1573     // Send graceful shutdown signal to all worker processes prior to
1574     // pid.
1575     if (wp->worker_pid == pid) {
1576       break;
1577     }
1578 
1579     LOG(INFO) << "Sending graceful shutdown event to worker process pid="
1580               << wp->worker_pid;
1581 
1582     ipc_send(wp.get(), SHRPX_IPC_GRACEFUL_SHUTDOWN);
1583     worker_process_set_termination_deadline(wp.get(), loop);
1584   }
1585 
1586   if (orig_pid != -1) {
1587     LOG(NOTICE) << "Send QUIT signal to the original main process to tell "
1588                    "that we are ready to serve requests.";
1589     kill(orig_pid, SIGQUIT);
1590 
1591     orig_pid = -1;
1592   }
1593 }
1594 } // namespace
1595 
1596 namespace {
start_worker_process_ready_ipc_watcher(struct ev_loop * loop)1597 void start_worker_process_ready_ipc_watcher(struct ev_loop *loop) {
1598   ev_io_init(&worker_process_ready_ipcev, worker_process_ready_ipc_readcb,
1599              worker_process_ready_ipc_fd[0], EV_READ);
1600   ev_io_start(loop, &worker_process_ready_ipcev);
1601 }
1602 } // namespace
1603 
1604 namespace {
shutdown_worker_process_ready_ipc_watcher(struct ev_loop * loop)1605 void shutdown_worker_process_ready_ipc_watcher(struct ev_loop *loop) {
1606   ev_io_stop(loop, &worker_process_ready_ipcev);
1607 }
1608 } // namespace
1609 
1610 namespace {
1611 // Creates worker process, and returns PID of worker process.  On
1612 // success, file descriptor for IPC (send only) is assigned to
1613 // |main_ipc_fd|.  In child process, we will close file descriptors
1614 // which are inherited from previous configuration/process, but not
1615 // used in the current configuration.
fork_worker_process(int & main_ipc_fd,int & wp_quic_ipc_fd,const std::vector<InheritedAddr> & iaddrs,std::vector<WorkerID> worker_ids,std::vector<QUICLingeringWorkerProcess> quic_lwps)1616 pid_t fork_worker_process(int &main_ipc_fd
1617 #ifdef ENABLE_HTTP3
1618                           ,
1619                           int &wp_quic_ipc_fd
1620 #endif // ENABLE_HTTP3
1621                           ,
1622                           const std::vector<InheritedAddr> &iaddrs
1623 #ifdef ENABLE_HTTP3
1624                           ,
1625                           std::vector<WorkerID> worker_ids,
1626                           std::vector<QUICLingeringWorkerProcess> quic_lwps
1627 #endif // ENABLE_HTTP3
1628 ) {
1629   std::array<char, STRERROR_BUFSIZE> errbuf;
1630   int rv;
1631   sigset_t oldset;
1632 
1633   std::array<int, 2> ipc_fd;
1634 
1635   rv = create_ipc_socket(ipc_fd);
1636   if (rv != 0) {
1637     return -1;
1638   }
1639 
1640 #ifdef ENABLE_HTTP3
1641   std::array<int, 2> quic_ipc_fd;
1642 
1643   rv = create_quic_ipc_socket(quic_ipc_fd);
1644   if (rv != 0) {
1645     return -1;
1646   }
1647 #endif // ENABLE_HTTP3
1648 
1649   rv = shrpx_signal_block_all(&oldset);
1650   if (rv != 0) {
1651     auto error = errno;
1652     LOG(ERROR) << "Blocking all signals failed: "
1653                << xsi_strerror(error, errbuf.data(), errbuf.size());
1654 
1655     close(ipc_fd[0]);
1656     close(ipc_fd[1]);
1657 
1658     return -1;
1659   }
1660 
1661   auto config = get_config();
1662 
1663   pid_t pid = 0;
1664 
1665   if (!config->single_process) {
1666     pid = fork();
1667   }
1668 
1669   if (pid == 0) {
1670     // We are in new process now, update pid for logger.
1671     log_config()->pid = getpid();
1672 
1673     ev_loop_fork(EV_DEFAULT);
1674 
1675     for (auto &addr : config->conn.listener.addrs) {
1676       util::make_socket_closeonexec(addr.fd);
1677     }
1678 
1679 #ifdef ENABLE_HTTP3
1680     util::make_socket_closeonexec(quic_ipc_fd[0]);
1681 
1682     for (auto &lwp : quic_lwps) {
1683       util::make_socket_closeonexec(lwp.quic_ipc_fd);
1684     }
1685 
1686     for (auto &wp : worker_processes) {
1687       util::make_socket_closeonexec(wp->quic_ipc_fd);
1688       // Do not close quic_ipc_fd.
1689       wp->quic_ipc_fd = -1;
1690     }
1691 #endif // ENABLE_HTTP3
1692 
1693     if (!config->single_process) {
1694       close(worker_process_ready_ipc_fd[0]);
1695       shutdown_worker_process_ready_ipc_watcher(EV_DEFAULT);
1696 
1697       shutdown_signal_watchers(EV_DEFAULT);
1698     }
1699 
1700     // Remove all WorkerProcesses to stop any registered watcher on
1701     // default loop.
1702     worker_process_remove_all(EV_DEFAULT);
1703 
1704     close_unused_inherited_addr(iaddrs);
1705 
1706     shrpx_signal_set_worker_proc_ign_handler();
1707 
1708     rv = shrpx_signal_unblock_all();
1709     if (rv != 0) {
1710       auto error = errno;
1711       LOG(FATAL) << "Unblocking all signals failed: "
1712                  << xsi_strerror(error, errbuf.data(), errbuf.size());
1713 
1714       if (config->single_process) {
1715         exit(EXIT_FAILURE);
1716       } else {
1717         nghttp2_Exit(EXIT_FAILURE);
1718       }
1719     }
1720 
1721     if (!config->single_process) {
1722       close(ipc_fd[1]);
1723 #ifdef ENABLE_HTTP3
1724       close(quic_ipc_fd[1]);
1725 #endif // ENABLE_HTTP3
1726     }
1727 
1728     WorkerProcessConfig wpconf{
1729         .ipc_fd = ipc_fd[0],
1730         .ready_ipc_fd = worker_process_ready_ipc_fd[1],
1731 #ifdef ENABLE_HTTP3
1732         .worker_ids = std::move(worker_ids),
1733         .quic_ipc_fd = quic_ipc_fd[0],
1734         .quic_lingering_worker_processes = std::move(quic_lwps),
1735 #endif // ENABLE_HTTP3
1736     };
1737     rv = worker_process_event_loop(&wpconf);
1738     if (rv != 0) {
1739       LOG(FATAL) << "Worker process returned error";
1740 
1741       if (config->single_process) {
1742         exit(EXIT_FAILURE);
1743       } else {
1744         nghttp2_Exit(EXIT_FAILURE);
1745       }
1746     }
1747 
1748     LOG(NOTICE) << "Worker process shutting down momentarily";
1749 
1750     // call exit(...) instead of nghttp2_Exit to get leak sanitizer report
1751     if (config->single_process) {
1752       exit(EXIT_SUCCESS);
1753     } else {
1754       nghttp2_Exit(EXIT_SUCCESS);
1755     }
1756   }
1757 
1758   // parent process
1759   if (pid == -1) {
1760     auto error = errno;
1761     LOG(ERROR) << "Could not spawn worker process: "
1762                << xsi_strerror(error, errbuf.data(), errbuf.size());
1763   }
1764 
1765   rv = shrpx_signal_set(&oldset);
1766   if (rv != 0) {
1767     auto error = errno;
1768     LOG(FATAL) << "Restoring signal mask failed: "
1769                << xsi_strerror(error, errbuf.data(), errbuf.size());
1770 
1771     exit(EXIT_FAILURE);
1772   }
1773 
1774   if (pid == -1) {
1775     close(ipc_fd[0]);
1776     close(ipc_fd[1]);
1777 #ifdef ENABLE_HTTP3
1778     close(quic_ipc_fd[0]);
1779     close(quic_ipc_fd[1]);
1780 #endif // ENABLE_HTTP3
1781 
1782     return -1;
1783   }
1784 
1785   close(ipc_fd[0]);
1786 #ifdef ENABLE_HTTP3
1787   close(quic_ipc_fd[0]);
1788 #endif // ENABLE_HTTP3
1789 
1790   main_ipc_fd = ipc_fd[1];
1791 #ifdef ENABLE_HTTP3
1792   wp_quic_ipc_fd = quic_ipc_fd[1];
1793 #endif // ENABLE_HTTP3
1794 
1795   LOG(NOTICE) << "Worker process [" << pid << "] spawned";
1796 
1797   return pid;
1798 }
1799 } // namespace
1800 
1801 namespace {
event_loop()1802 int event_loop() {
1803   std::array<char, STRERROR_BUFSIZE> errbuf;
1804 
1805   shrpx_signal_set_main_proc_ign_handler();
1806 
1807   auto config = mod_config();
1808 
1809   if (config->daemon) {
1810     if (call_daemon() == -1) {
1811       auto error = errno;
1812       LOG(FATAL) << "Failed to daemonize: "
1813                  << xsi_strerror(error, errbuf.data(), errbuf.size());
1814       return -1;
1815     }
1816 
1817     // We get new PID after successful daemon().
1818     mod_config()->pid = getpid();
1819 
1820     // daemon redirects stderr file descriptor to /dev/null, so we
1821     // need this.
1822     redirect_stderr_to_errorlog(config->logging);
1823   }
1824 
1825   // update systemd PID tracking
1826   shrpx_sd_notifyf(0, "MAINPID=%d\n", config->pid);
1827 
1828   {
1829     auto iaddrs = get_inherited_addr_from_env(config);
1830 
1831     if (create_acceptor_socket(config, iaddrs) != 0) {
1832       return -1;
1833     }
1834 
1835     close_unused_inherited_addr(iaddrs);
1836   }
1837 
1838   orig_pid = get_orig_pid_from_env();
1839 
1840 #ifdef ENABLE_HTTP3
1841   inherited_quic_lingering_worker_processes =
1842       get_inherited_quic_lingering_worker_process_from_env();
1843 #endif // ENABLE_HTTP3
1844 
1845   auto loop = ev_default_loop(config->ev_loop_flags);
1846 
1847   int ipc_fd = 0;
1848 #ifdef ENABLE_HTTP3
1849   int quic_ipc_fd = 0;
1850 
1851   auto quic_lwps = collect_quic_lingering_worker_processes();
1852 
1853   std::vector<WorkerID> worker_ids;
1854 
1855   if (generate_worker_id(worker_ids, worker_process_seq, config) != 0) {
1856     return -1;
1857   }
1858 #endif // ENABLE_HTTP3
1859 
1860   if (!config->single_process) {
1861     start_signal_watchers(loop);
1862   }
1863 
1864   create_worker_process_ready_ipc_socket(worker_process_ready_ipc_fd);
1865   start_worker_process_ready_ipc_watcher(loop);
1866 
1867   auto pid = fork_worker_process(ipc_fd
1868 #ifdef ENABLE_HTTP3
1869                                  ,
1870                                  quic_ipc_fd
1871 #endif // ENABLE_HTTP3
1872                                  ,
1873                                  {}
1874 #ifdef ENABLE_HTTP3
1875                                  ,
1876                                  worker_ids, std::move(quic_lwps)
1877 #endif // ENABLE_HTTP3
1878   );
1879 
1880   if (pid == -1) {
1881     return -1;
1882   }
1883 
1884   ev_timer_init(&worker_process_grace_period_timer,
1885                 worker_process_grace_period_timercb, 0., 0.);
1886 
1887   worker_process_add(std::make_unique<WorkerProcess>(
1888       loop, pid, ipc_fd
1889 #ifdef ENABLE_HTTP3
1890       ,
1891       quic_ipc_fd, std::move(worker_ids), worker_process_seq++
1892 #endif // ENABLE_HTTP3
1893       ));
1894 
1895   // Write PID file when we are ready to accept connection from peer.
1896   // This makes easier to write restart script for nghttpx.  Because
1897   // when we know that PID file is recreated, it means we can send
1898   // QUIT signal to the old process to make it shutdown gracefully.
1899   if (!config->pid_file.empty()) {
1900     save_pid();
1901   }
1902 
1903   shrpx_sd_notifyf(0, "READY=1");
1904 
1905   ev_run(loop, 0);
1906 
1907   ev_timer_stop(loop, &worker_process_grace_period_timer);
1908 
1909   shutdown_worker_process_ready_ipc_watcher(loop);
1910 
1911   // config is now stale if reload has happened.
1912   if (!get_config()->single_process) {
1913     shutdown_signal_watchers(loop);
1914   }
1915 
1916   return 0;
1917 }
1918 } // namespace
1919 
1920 namespace {
1921 // Returns true if regular file or symbolic link |path| exists.
conf_exists(const char * path)1922 bool conf_exists(const char *path) {
1923   struct stat buf;
1924   int rv = stat(path, &buf);
1925   return rv == 0 && (buf.st_mode & (S_IFREG | S_IFLNK));
1926 }
1927 } // namespace
1928 
1929 namespace {
1930 constexpr auto DEFAULT_ALPN_LIST = "h2,h2-16,h2-14,http/1.1"_sr;
1931 } // namespace
1932 
1933 namespace {
1934 constexpr auto DEFAULT_TLS_MIN_PROTO_VERSION = "TLSv1.2"_sr;
1935 #ifdef TLS1_3_VERSION
1936 constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = "TLSv1.3"_sr;
1937 #else  // !TLS1_3_VERSION
1938 constexpr auto DEFAULT_TLS_MAX_PROTO_VERSION = "TLSv1.2"_sr;
1939 #endif // !TLS1_3_VERSION
1940 } // namespace
1941 
1942 namespace {
1943 constexpr auto DEFAULT_ACCESSLOG_FORMAT =
1944     R"($remote_addr - - [$time_local] )"
1945     R"("$request" $status $body_bytes_sent )"
1946     R"("$http_referer" "$http_user_agent")"_sr;
1947 } // namespace
1948 
1949 namespace {
fill_default_config(Config * config)1950 void fill_default_config(Config *config) {
1951   config->num_worker = 1;
1952   config->conf_path = "/etc/nghttpx/nghttpx.conf"_sr;
1953   config->pid = getpid();
1954 
1955 #ifdef NOTHREADS
1956   config->single_thread = true;
1957 #endif // NOTHREADS
1958 
1959   if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
1960     config->ev_loop_flags = ev_recommended_backends() | EVBACKEND_KQUEUE;
1961   }
1962 
1963   auto &tlsconf = config->tls;
1964   {
1965     auto &ticketconf = tlsconf.ticket;
1966     {
1967       auto &memcachedconf = ticketconf.memcached;
1968       memcachedconf.max_retry = 3;
1969       memcachedconf.max_fail = 2;
1970       memcachedconf.interval = 10_min;
1971       memcachedconf.family = AF_UNSPEC;
1972     }
1973 
1974     auto &session_cacheconf = tlsconf.session_cache;
1975     {
1976       auto &memcachedconf = session_cacheconf.memcached;
1977       memcachedconf.family = AF_UNSPEC;
1978     }
1979 
1980     ticketconf.cipher = EVP_aes_128_cbc();
1981   }
1982 
1983   {
1984     auto &ocspconf = tlsconf.ocsp;
1985     // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
1986     ocspconf.update_interval = 4_h;
1987     ocspconf.fetch_ocsp_response_file = PKGDATADIR "/fetch-ocsp-response"_sr;
1988   }
1989 
1990   {
1991     auto &dyn_recconf = tlsconf.dyn_rec;
1992     dyn_recconf.warmup_threshold = 1_m;
1993     dyn_recconf.idle_timeout = 1_s;
1994   }
1995 
1996   tlsconf.session_timeout = std::chrono::hours(12);
1997   tlsconf.ciphers = StringRef{nghttp2::tls::DEFAULT_CIPHER_LIST};
1998   tlsconf.tls13_ciphers = StringRef{nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST};
1999   tlsconf.client.ciphers = StringRef{nghttp2::tls::DEFAULT_CIPHER_LIST};
2000   tlsconf.client.tls13_ciphers =
2001       StringRef{nghttp2::tls::DEFAULT_TLS13_CIPHER_LIST};
2002   tlsconf.min_proto_version =
2003       tls::proto_version_from_string(DEFAULT_TLS_MIN_PROTO_VERSION);
2004   tlsconf.max_proto_version =
2005       tls::proto_version_from_string(DEFAULT_TLS_MAX_PROTO_VERSION);
2006   tlsconf.max_early_data = 16_k;
2007   tlsconf.ecdh_curves = "X25519:P-256:P-384:P-521"_sr;
2008 
2009   auto &httpconf = config->http;
2010   httpconf.server_name = "nghttpx"_sr;
2011   httpconf.no_host_rewrite = true;
2012   httpconf.request_header_field_buffer = 64_k;
2013   httpconf.max_request_header_fields = 100;
2014   httpconf.response_header_field_buffer = 64_k;
2015   httpconf.max_response_header_fields = 500;
2016   httpconf.redirect_https_port = "443"_sr;
2017   httpconf.max_requests = std::numeric_limits<size_t>::max();
2018   httpconf.xfp.add = true;
2019   httpconf.xfp.strip_incoming = true;
2020   httpconf.early_data.strip_incoming = true;
2021   httpconf.timeout.header = 1_min;
2022 
2023   auto &http2conf = config->http2;
2024   {
2025     auto &upstreamconf = http2conf.upstream;
2026 
2027     {
2028       auto &timeoutconf = upstreamconf.timeout;
2029       timeoutconf.settings = 10_s;
2030     }
2031 
2032     // window size for HTTP/2 upstream connection per stream.  2**16-1
2033     // = 64KiB-1, which is HTTP/2 default.
2034     upstreamconf.window_size = 64_k - 1;
2035     // HTTP/2 has connection-level flow control. The default window
2036     // size for HTTP/2 is 64KiB - 1.
2037     upstreamconf.connection_window_size = 64_k - 1;
2038     upstreamconf.max_concurrent_streams = 100;
2039 
2040     upstreamconf.encoder_dynamic_table_size = 4_k;
2041     upstreamconf.decoder_dynamic_table_size = 4_k;
2042 
2043     nghttp2_option_new(&upstreamconf.option);
2044     nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1);
2045     nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1);
2046     nghttp2_option_set_max_deflate_dynamic_table_size(
2047         upstreamconf.option, upstreamconf.encoder_dynamic_table_size);
2048     nghttp2_option_set_server_fallback_rfc7540_priorities(upstreamconf.option,
2049                                                           1);
2050     nghttp2_option_set_builtin_recv_extension_type(upstreamconf.option,
2051                                                    NGHTTP2_PRIORITY_UPDATE);
2052 
2053     // For API endpoint, we enable automatic window update.  This is
2054     // because we are a sink.
2055     nghttp2_option_new(&upstreamconf.alt_mode_option);
2056     nghttp2_option_set_no_recv_client_magic(upstreamconf.alt_mode_option, 1);
2057     nghttp2_option_set_max_deflate_dynamic_table_size(
2058         upstreamconf.alt_mode_option, upstreamconf.encoder_dynamic_table_size);
2059   }
2060 
2061   http2conf.timeout.stream_write = 1_min;
2062 
2063   {
2064     auto &downstreamconf = http2conf.downstream;
2065 
2066     {
2067       auto &timeoutconf = downstreamconf.timeout;
2068       timeoutconf.settings = 10_s;
2069     }
2070 
2071     downstreamconf.window_size = 64_k - 1;
2072     downstreamconf.connection_window_size = (1u << 31) - 1;
2073     downstreamconf.max_concurrent_streams = 100;
2074 
2075     downstreamconf.encoder_dynamic_table_size = 4_k;
2076     downstreamconf.decoder_dynamic_table_size = 4_k;
2077 
2078     nghttp2_option_new(&downstreamconf.option);
2079     nghttp2_option_set_no_auto_window_update(downstreamconf.option, 1);
2080     nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100);
2081     nghttp2_option_set_max_deflate_dynamic_table_size(
2082         downstreamconf.option, downstreamconf.encoder_dynamic_table_size);
2083   }
2084 
2085 #ifdef ENABLE_HTTP3
2086   auto &quicconf = config->quic;
2087   {
2088     auto &upstreamconf = quicconf.upstream;
2089 
2090     {
2091       auto &timeoutconf = upstreamconf.timeout;
2092       timeoutconf.idle = 30_s;
2093     }
2094 
2095     auto &bpfconf = quicconf.bpf;
2096     bpfconf.prog_file = PKGLIBDIR "/reuseport_kern.o"_sr;
2097 
2098     upstreamconf.congestion_controller = NGTCP2_CC_ALGO_CUBIC;
2099 
2100     upstreamconf.initial_rtt =
2101         static_cast<ev_tstamp>(NGTCP2_DEFAULT_INITIAL_RTT) / NGTCP2_SECONDS;
2102   }
2103 
2104   if (RAND_bytes(reinterpret_cast<unsigned char *>(&quicconf.server_id),
2105                  sizeof(quicconf.server_id)) != 1) {
2106     assert(0);
2107     abort();
2108   }
2109 
2110   auto &http3conf = config->http3;
2111   {
2112     auto &upstreamconf = http3conf.upstream;
2113 
2114     upstreamconf.max_concurrent_streams = 100;
2115     upstreamconf.window_size = 256_k;
2116     upstreamconf.connection_window_size = 1_m;
2117     upstreamconf.max_window_size = 6_m;
2118     upstreamconf.max_connection_window_size = 8_m;
2119   }
2120 #endif // ENABLE_HTTP3
2121 
2122   auto &loggingconf = config->logging;
2123   {
2124     auto &accessconf = loggingconf.access;
2125     accessconf.format =
2126         parse_log_format(config->balloc, DEFAULT_ACCESSLOG_FORMAT);
2127 
2128     auto &errorconf = loggingconf.error;
2129     errorconf.file = "/dev/stderr"_sr;
2130   }
2131 
2132   loggingconf.syslog_facility = LOG_DAEMON;
2133   loggingconf.severity = NOTICE;
2134 
2135   auto &connconf = config->conn;
2136   {
2137     auto &listenerconf = connconf.listener;
2138     {
2139       // Default accept() backlog
2140       listenerconf.backlog = 65536;
2141       listenerconf.timeout.sleep = 30_s;
2142     }
2143   }
2144 
2145   {
2146     auto &upstreamconf = connconf.upstream;
2147     {
2148       auto &timeoutconf = upstreamconf.timeout;
2149       // Idle timeout for HTTP2 upstream connection
2150       timeoutconf.http2_idle = 3_min;
2151 
2152       // Idle timeout for HTTP3 upstream connection
2153       timeoutconf.http3_idle = 3_min;
2154 
2155       // Write timeout for HTTP2/non-HTTP2 upstream connection
2156       timeoutconf.write = 30_s;
2157 
2158       // Keep alive (idle) timeout for HTTP/1 upstream connection
2159       timeoutconf.idle = 1_min;
2160     }
2161   }
2162 
2163   {
2164     connconf.downstream = std::make_shared<DownstreamConfig>();
2165     auto &downstreamconf = *connconf.downstream;
2166     {
2167       auto &timeoutconf = downstreamconf.timeout;
2168       // Read/Write timeouts for downstream connection
2169       timeoutconf.read = 1_min;
2170       timeoutconf.write = 30_s;
2171       // Timeout for pooled (idle) connections
2172       timeoutconf.idle_read = 2_s;
2173       timeoutconf.connect = 30_s;
2174       timeoutconf.max_backoff = 120_s;
2175     }
2176 
2177     downstreamconf.connections_per_host = 8;
2178     downstreamconf.request_buffer_size = 16_k;
2179     downstreamconf.response_buffer_size = 128_k;
2180     downstreamconf.family = AF_UNSPEC;
2181   }
2182 
2183   auto &apiconf = config->api;
2184   apiconf.max_request_body = 32_m;
2185 
2186   auto &dnsconf = config->dns;
2187   {
2188     auto &timeoutconf = dnsconf.timeout;
2189     timeoutconf.cache = 10_s;
2190     timeoutconf.lookup = 5_s;
2191   }
2192   dnsconf.max_try = 2;
2193 }
2194 
2195 } // namespace
2196 
2197 namespace {
print_version(std::ostream & out)2198 void print_version(std::ostream &out) {
2199   out << "nghttpx nghttp2/" NGHTTP2_VERSION
2200 #ifdef ENABLE_HTTP3
2201          " ngtcp2/" NGTCP2_VERSION " nghttp3/" NGHTTP3_VERSION
2202 #endif // ENABLE_HTTP3
2203       << std::endl;
2204 }
2205 } // namespace
2206 
2207 namespace {
print_usage(std::ostream & out)2208 void print_usage(std::ostream &out) {
2209   out << R"(Usage: nghttpx [OPTIONS]... [<PRIVATE_KEY> <CERT>]
2210 A reverse proxy for HTTP/3, HTTP/2, and HTTP/1.)"
2211       << std::endl;
2212 }
2213 } // namespace
2214 
2215 namespace {
print_help(std::ostream & out)2216 void print_help(std::ostream &out) {
2217   auto config = get_config();
2218 
2219   print_usage(out);
2220   out << R"(
2221   <PRIVATE_KEY>
2222               Set  path  to  server's private  key.   Required  unless
2223               "no-tls" parameter is used in --frontend option.
2224   <CERT>      Set  path  to  server's  certificate.   Required  unless
2225               "no-tls"  parameter is  used in  --frontend option.   To
2226               make OCSP stapling work, this must be an absolute path.
2227 
2228 Options:
2229   The options are categorized into several groups.
2230 
2231 Connections:
2232   -b, --backend=(<HOST>,<PORT>|unix:<PATH>)[;[<PATTERN>[:...]][[;<PARAM>]...]
2233 
2234               Set  backend  host  and   port.   The  multiple  backend
2235               addresses are  accepted by repeating this  option.  UNIX
2236               domain socket  can be  specified by prefixing  path name
2237               with "unix:" (e.g., unix:/var/run/backend.sock).
2238 
2239               Optionally, if <PATTERN>s are given, the backend address
2240               is  only  used  if  request matches  the  pattern.   The
2241               pattern  matching is  closely  designed  to ServeMux  in
2242               net/http package of  Go programming language.  <PATTERN>
2243               consists of  path, host +  path or just host.   The path
2244               must start  with "/".  If  it ends with "/",  it matches
2245               all  request path  in  its subtree.   To  deal with  the
2246               request  to the  directory without  trailing slash,  the
2247               path which ends  with "/" also matches  the request path
2248               which  only  lacks  trailing  '/'  (e.g.,  path  "/foo/"
2249               matches request path  "/foo").  If it does  not end with
2250               "/", it  performs exact match against  the request path.
2251               If  host  is given,  it  performs  a match  against  the
2252               request host.   For a  request received on  the frontend
2253               listener with  "sni-fwd" parameter enabled, SNI  host is
2254               used instead of a request host.  If host alone is given,
2255               "/" is  appended to it,  so that it matches  all request
2256               paths  under the  host  (e.g., specifying  "nghttp2.org"
2257               equals  to "nghttp2.org/").   CONNECT method  is treated
2258               specially.  It  does not have  path, and we  don't allow
2259               empty path.  To workaround  this, we assume that CONNECT
2260               method has "/" as path.
2261 
2262               Patterns with  host take  precedence over  patterns with
2263               just path.   Then, longer patterns take  precedence over
2264               shorter ones.
2265 
2266               Host  can  include "*"  in  the  left most  position  to
2267               indicate  wildcard match  (only suffix  match is  done).
2268               The "*" must match at least one character.  For example,
2269               host    pattern    "*.nghttp2.org"    matches    against
2270               "www.nghttp2.org"  and  "git.ngttp2.org", but  does  not
2271               match  against  "nghttp2.org".   The exact  hosts  match
2272               takes precedence over the wildcard hosts match.
2273 
2274               If path  part ends with  "*", it is treated  as wildcard
2275               path.  The  wildcard path  behaves differently  from the
2276               normal path.  For normal path,  match is made around the
2277               boundary of path component  separator,"/".  On the other
2278               hand, the wildcard  path does not take  into account the
2279               path component  separator.  All paths which  include the
2280               wildcard  path  without  last  "*" as  prefix,  and  are
2281               strictly longer than wildcard  path without last "*" are
2282               matched.  "*"  must match  at least one  character.  For
2283               example,  the   pattern  "/foo*"  matches   "/foo/"  and
2284               "/foobar".  But it does not match "/foo", or "/fo".
2285 
2286               If <PATTERN> is omitted or  empty string, "/" is used as
2287               pattern,  which  matches  all request  paths  (catch-all
2288               pattern).  The catch-all backend must be given.
2289 
2290               When doing  a match, nghttpx made  some normalization to
2291               pattern, request host and path.  For host part, they are
2292               converted to lower case.  For path part, percent-encoded
2293               unreserved characters  defined in RFC 3986  are decoded,
2294               and any  dot-segments (".."  and ".")   are resolved and
2295               removed.
2296 
2297               For   example,   -b'127.0.0.1,8080;nghttp2.org/httpbin/'
2298               matches the  request host "nghttp2.org" and  the request
2299               path "/httpbin/get", but does not match the request host
2300               "nghttp2.org" and the request path "/index.html".
2301 
2302               The  multiple <PATTERN>s  can  be specified,  delimiting
2303               them            by           ":".             Specifying
2304               -b'127.0.0.1,8080;nghttp2.org:www.nghttp2.org'  has  the
2305               same  effect  to specify  -b'127.0.0.1,8080;nghttp2.org'
2306               and -b'127.0.0.1,8080;www.nghttp2.org'.
2307 
2308               The backend addresses sharing same <PATTERN> are grouped
2309               together forming  load balancing  group.
2310 
2311               Several parameters <PARAM> are accepted after <PATTERN>.
2312               The  parameters are  delimited  by  ";".  The  available
2313               parameters       are:      "proto=<PROTO>",       "tls",
2314               "sni=<SNI_HOST>",         "fall=<N>",        "rise=<N>",
2315               "affinity=<METHOD>",    "dns",    "redirect-if-not-tls",
2316               "upgrade-scheme",                        "mruby=<PATH>",
2317               "read-timeout=<DURATION>",   "write-timeout=<DURATION>",
2318               "group=<GROUP>",  "group-weight=<N>", "weight=<N>",  and
2319               "dnf".    The  parameter   consists   of  keyword,   and
2320               optionally followed by "="  and value.  For example, the
2321               parameter "proto=h2" consists of the keyword "proto" and
2322               value "h2".  The parameter "tls" consists of the keyword
2323               "tls"  without value.   Each parameter  is described  as
2324               follows.
2325 
2326               The backend application protocol  can be specified using
2327               optional  "proto"   parameter,  and   in  the   form  of
2328               "proto=<PROTO>".  <PROTO> should be one of the following
2329               list  without  quotes:  "h2", "http/1.1".   The  default
2330               value of <PROTO> is  "http/1.1".  Note that usually "h2"
2331               refers to HTTP/2  over TLS.  But in this  option, it may
2332               mean HTTP/2  over cleartext TCP unless  "tls" keyword is
2333               used (see below).
2334 
2335               TLS  can   be  enabled  by  specifying   optional  "tls"
2336               parameter.  TLS is not enabled by default.
2337 
2338               With "sni=<SNI_HOST>" parameter, it can override the TLS
2339               SNI  field  value  with  given  <SNI_HOST>.   This  will
2340               default to the backend <HOST> name
2341 
2342               The  feature  to detect  whether  backend  is online  or
2343               offline can be enabled  using optional "fall" and "rise"
2344               parameters.   Using  "fall=<N>"  parameter,  if  nghttpx
2345               cannot connect  to a  this backend <N>  times in  a row,
2346               this  backend  is  assumed  to be  offline,  and  it  is
2347               excluded from load balancing.  If <N> is 0, this backend
2348               never  be excluded  from load  balancing whatever  times
2349               nghttpx cannot connect  to it, and this  is the default.
2350               There is  also "rise=<N>" parameter.  After  backend was
2351               excluded from load balancing group, nghttpx periodically
2352               attempts to make a connection to the failed backend, and
2353               if the  connection is made  successfully <N> times  in a
2354               row, the backend is assumed to  be online, and it is now
2355               eligible  for load  balancing target.   If <N>  is 0,  a
2356               backend  is permanently  offline, once  it goes  in that
2357               state, and this is the default behaviour.
2358 
2359               The     session     affinity    is     enabled     using
2360               "affinity=<METHOD>"  parameter.   If  "ip" is  given  in
2361               <METHOD>, client  IP based session affinity  is enabled.
2362               If "cookie"  is given in <METHOD>,  cookie based session
2363               affinity is  enabled.  If  "none" is given  in <METHOD>,
2364               session affinity  is disabled, and this  is the default.
2365               The session  affinity is  enabled per <PATTERN>.   If at
2366               least  one backend  has  "affinity"  parameter, and  its
2367               <METHOD> is not "none",  session affinity is enabled for
2368               all backend  servers sharing the same  <PATTERN>.  It is
2369               advised  to  set  "affinity" parameter  to  all  backend
2370               explicitly if session affinity  is desired.  The session
2371               affinity  may   break  if   one  of  the   backend  gets
2372               unreachable,  or   backend  settings  are   reloaded  or
2373               replaced by API.
2374 
2375               If   "affinity=cookie"    is   used,    the   additional
2376               configuration                is                required.
2377               "affinity-cookie-name=<NAME>" must be  used to specify a
2378               name     of     cookie      to     use.      Optionally,
2379               "affinity-cookie-path=<PATH>" can  be used to  specify a
2380               path   which   cookie    is   applied.    The   optional
2381               "affinity-cookie-secure=<SECURE>"  controls  the  Secure
2382               attribute of a cookie.  The default value is "auto", and
2383               the Secure attribute is  determined by a request scheme.
2384               If a request scheme is "https", then Secure attribute is
2385               set.  Otherwise, it  is not set.  If  <SECURE> is "yes",
2386               the  Secure attribute  is  always set.   If <SECURE>  is
2387               "no",   the   Secure   attribute  is   always   omitted.
2388               "affinity-cookie-stickiness=<STICKINESS>"       controls
2389               stickiness  of   this  affinity.   If   <STICKINESS>  is
2390               "loose", removing or adding a backend server might break
2391               the affinity  and the  request might  be forwarded  to a
2392               different backend server.   If <STICKINESS> is "strict",
2393               removing the designated  backend server breaks affinity,
2394               but adding  new backend server does  not cause breakage.
2395               If  the designated  backend server  becomes unavailable,
2396               new backend server is chosen  as if the request does not
2397               have  an  affinity  cookie.   <STICKINESS>  defaults  to
2398               "loose".
2399 
2400               By default, name resolution of backend host name is done
2401               at  start  up,  or reloading  configuration.   If  "dns"
2402               parameter   is  given,   name  resolution   takes  place
2403               dynamically.  This is useful  if backend address changes
2404               frequently.   If  "dns"  is given,  name  resolution  of
2405               backend   host   name   at  start   up,   or   reloading
2406               configuration is skipped.
2407 
2408               If "redirect-if-not-tls" parameter  is used, the matched
2409               backend  requires   that  frontend  connection   is  TLS
2410               encrypted.  If it isn't, nghttpx responds to the request
2411               with 308  status code, and  https URI the  client should
2412               use instead  is included in Location  header field.  The
2413               port number in  redirect URI is 443 by  default, and can
2414               be  changed using  --redirect-https-port option.   If at
2415               least one  backend has  "redirect-if-not-tls" parameter,
2416               this feature is enabled  for all backend servers sharing
2417               the   same   <PATTERN>.    It    is   advised   to   set
2418               "redirect-if-no-tls"    parameter   to    all   backends
2419               explicitly if this feature is desired.
2420 
2421               If "upgrade-scheme"  parameter is used along  with "tls"
2422               parameter, HTTP/2 :scheme pseudo header field is changed
2423               to "https" from "http" when forwarding a request to this
2424               particular backend.  This is  a workaround for a backend
2425               server  which  requires  "https" :scheme  pseudo  header
2426               field on TLS encrypted connection.
2427 
2428               "mruby=<PATH>"  parameter  specifies  a  path  to  mruby
2429               script  file  which  is  invoked when  this  pattern  is
2430               matched.  All backends which share the same pattern must
2431               have the same mruby path.
2432 
2433               "read-timeout=<DURATION>" and "write-timeout=<DURATION>"
2434               parameters  specify the  read and  write timeout  of the
2435               backend connection  when this  pattern is  matched.  All
2436               backends which share the same pattern must have the same
2437               timeouts.  If these timeouts  are entirely omitted for a
2438               pattern,            --backend-read-timeout           and
2439               --backend-write-timeout are used.
2440 
2441               "group=<GROUP>"  parameter specifies  the name  of group
2442               this backend address belongs to.  By default, it belongs
2443               to  the unnamed  default group.   The name  of group  is
2444               unique   per   pattern.   "group-weight=<N>"   parameter
2445               specifies the  weight of  the group.  The  higher weight
2446               gets  more frequently  selected  by  the load  balancing
2447               algorithm.  <N> must be  [1, 256] inclusive.  The weight
2448               8 has 4 times more weight  than 2.  <N> must be the same
2449               for  all addresses  which  share the  same <GROUP>.   If
2450               "group-weight" is  omitted in an address,  but the other
2451               address  which  belongs  to  the  same  group  specifies
2452               "group-weight",   its    weight   is   used.     If   no
2453               "group-weight"  is  specified  for  all  addresses,  the
2454               weight of a group becomes 1.  "group" and "group-weight"
2455               are ignored if session affinity is enabled.
2456 
2457               "weight=<N>"  parameter  specifies  the  weight  of  the
2458               backend  address  inside  a  group  which  this  address
2459               belongs  to.  The  higher  weight  gets more  frequently
2460               selected by  the load balancing algorithm.   <N> must be
2461               [1,  256] inclusive.   The  weight 8  has  4 times  more
2462               weight  than weight  2.  If  this parameter  is omitted,
2463               weight  becomes  1.   "weight"  is  ignored  if  session
2464               affinity is enabled.
2465 
2466               If "dnf" parameter is  specified, an incoming request is
2467               not forwarded to a backend  and just consumed along with
2468               the  request body  (actually a  backend server  never be
2469               contacted).  It  is expected  that the HTTP  response is
2470               generated by mruby  script (see "mruby=<PATH>" parameter
2471               above).  "dnf" is an abbreviation of "do not forward".
2472 
2473               Since ";" and ":" are  used as delimiter, <PATTERN> must
2474               not contain  these characters.  In order  to include ":"
2475               in  <PATTERN>,  one  has  to  specify  "%3A"  (which  is
2476               percent-encoded  from of  ":") instead.   Since ";"  has
2477               special  meaning  in shell,  the  option  value must  be
2478               quoted.
2479 
2480               Default: )"
2481       << DEFAULT_DOWNSTREAM_HOST << "," << DEFAULT_DOWNSTREAM_PORT << R"(
2482   -f, --frontend=(<HOST>,<PORT>|unix:<PATH>)[[;<PARAM>]...]
2483               Set  frontend  host and  port.   If  <HOST> is  '*',  it
2484               assumes  all addresses  including  both  IPv4 and  IPv6.
2485               UNIX domain  socket can  be specified by  prefixing path
2486               name  with  "unix:" (e.g.,  unix:/var/run/nghttpx.sock).
2487               This  option can  be used  multiple times  to listen  to
2488               multiple addresses.
2489 
2490               This option  can take  0 or  more parameters,  which are
2491               described  below.   Note   that  "api"  and  "healthmon"
2492               parameters are mutually exclusive.
2493 
2494               Optionally, TLS  can be disabled by  specifying "no-tls"
2495               parameter.  TLS is enabled by default.
2496 
2497               If "sni-fwd" parameter is  used, when performing a match
2498               to select a backend server,  SNI host name received from
2499               the client  is used  instead of  the request  host.  See
2500               --backend option about the pattern match.
2501 
2502               To  make this  frontend as  API endpoint,  specify "api"
2503               parameter.   This   is  disabled  by  default.    It  is
2504               important  to  limit the  access  to  the API  frontend.
2505               Otherwise, someone  may change  the backend  server, and
2506               break your services,  or expose confidential information
2507               to the outside the world.
2508 
2509               To  make  this  frontend  as  health  monitor  endpoint,
2510               specify  "healthmon"  parameter.   This is  disabled  by
2511               default.  Any  requests which come through  this address
2512               are replied with 200 HTTP status, without no body.
2513 
2514               To accept  PROXY protocol  version 1  and 2  on frontend
2515               connection,  specify  "proxyproto" parameter.   This  is
2516               disabled by default.
2517 
2518               To  receive   HTTP/3  (QUIC)  traffic,   specify  "quic"
2519               parameter.  It  makes nghttpx listen on  UDP port rather
2520               than  TCP   port.   UNIX   domain  socket,   "api",  and
2521               "healthmon"  parameters  cannot   be  used  with  "quic"
2522               parameter.
2523 
2524               Default: *,3000
2525   --backlog=<N>
2526               Set listen backlog size.
2527               Default: )"
2528       << config->conn.listener.backlog << R"(
2529   --backend-address-family=(auto|IPv4|IPv6)
2530               Specify  address  family  of  backend  connections.   If
2531               "auto" is given, both IPv4  and IPv6 are considered.  If
2532               "IPv4" is  given, only  IPv4 address is  considered.  If
2533               "IPv6" is given, only IPv6 address is considered.
2534               Default: auto
2535   --backend-http-proxy-uri=<URI>
2536               Specify      proxy       URI      in       the      form
2537               http://[<USER>:<PASS>@]<PROXY>:<PORT>.    If   a   proxy
2538               requires  authentication,  specify  <USER>  and  <PASS>.
2539               Note that  they must be properly  percent-encoded.  This
2540               proxy  is used  when the  backend connection  is HTTP/2.
2541               First,  make  a CONNECT  request  to  the proxy  and  it
2542               connects  to the  backend  on behalf  of nghttpx.   This
2543               forms  tunnel.   After  that, nghttpx  performs  SSL/TLS
2544               handshake with  the downstream through the  tunnel.  The
2545               timeouts when connecting and  making CONNECT request can
2546               be     specified    by     --backend-read-timeout    and
2547               --backend-write-timeout options.
2548 
2549 Performance:
2550   -n, --workers=<N>
2551               Set the number of worker threads.
2552               Default: )"
2553       << config->num_worker << R"(
2554   --single-thread
2555               Run everything in one  thread inside the worker process.
2556               This   feature   is   provided  for   better   debugging
2557               experience,  or  for  the platforms  which  lack  thread
2558               support.   If  threading  is disabled,  this  option  is
2559               always enabled.
2560   --read-rate=<SIZE>
2561               Set maximum  average read  rate on  frontend connection.
2562               Setting 0 to this option means read rate is unlimited.
2563               Default: )"
2564       << config->conn.upstream.ratelimit.read.rate << R"(
2565   --read-burst=<SIZE>
2566               Set  maximum read  burst  size  on frontend  connection.
2567               Setting  0  to this  option  means  read burst  size  is
2568               unlimited.
2569               Default: )"
2570       << config->conn.upstream.ratelimit.read.burst << R"(
2571   --write-rate=<SIZE>
2572               Set maximum  average write rate on  frontend connection.
2573               Setting 0 to this option means write rate is unlimited.
2574               Default: )"
2575       << config->conn.upstream.ratelimit.write.rate << R"(
2576   --write-burst=<SIZE>
2577               Set  maximum write  burst size  on frontend  connection.
2578               Setting  0 to  this  option means  write  burst size  is
2579               unlimited.
2580               Default: )"
2581       << config->conn.upstream.ratelimit.write.burst << R"(
2582   --worker-read-rate=<SIZE>
2583               Set maximum average read rate on frontend connection per
2584               worker.  Setting  0 to  this option  means read  rate is
2585               unlimited.  Not implemented yet.
2586               Default: 0
2587   --worker-read-burst=<SIZE>
2588               Set maximum  read burst size on  frontend connection per
2589               worker.  Setting 0 to this  option means read burst size
2590               is unlimited.  Not implemented yet.
2591               Default: 0
2592   --worker-write-rate=<SIZE>
2593               Set maximum  average write  rate on  frontend connection
2594               per worker.  Setting  0 to this option  means write rate
2595               is unlimited.  Not implemented yet.
2596               Default: 0
2597   --worker-write-burst=<SIZE>
2598               Set maximum write burst  size on frontend connection per
2599               worker.  Setting 0 to this option means write burst size
2600               is unlimited.  Not implemented yet.
2601               Default: 0
2602   --worker-frontend-connections=<N>
2603               Set maximum number  of simultaneous connections frontend
2604               accepts.  Setting 0 means unlimited.
2605               Default: )"
2606       << config->conn.upstream.worker_connections << R"(
2607   --backend-connections-per-host=<N>
2608               Set  maximum number  of  backend concurrent  connections
2609               (and/or  streams in  case  of HTTP/2)  per origin  host.
2610               This option  is meaningful when --http2-proxy  option is
2611               used.   The  origin  host  is  determined  by  authority
2612               portion of  request URI (or :authority  header field for
2613               HTTP/2).   To  limit  the   number  of  connections  per
2614               frontend        for       default        mode,       use
2615               --backend-connections-per-frontend.
2616               Default: )"
2617       << config->conn.downstream->connections_per_host << R"(
2618   --backend-connections-per-frontend=<N>
2619               Set  maximum number  of  backend concurrent  connections
2620               (and/or streams  in case of HTTP/2)  per frontend.  This
2621               option  is   only  used  for  default   mode.   0  means
2622               unlimited.  To limit the  number of connections per host
2623               with          --http2-proxy         option,          use
2624               --backend-connections-per-host.
2625               Default: )"
2626       << config->conn.downstream->connections_per_frontend << R"(
2627   --rlimit-nofile=<N>
2628               Set maximum number of open files (RLIMIT_NOFILE) to <N>.
2629               If 0 is given, nghttpx does not set the limit.
2630               Default: )"
2631       << config->rlimit_nofile << R"(
2632   --rlimit-memlock=<N>
2633               Set maximum number of bytes of memory that may be locked
2634               into  RAM.  If  0 is  given,  nghttpx does  not set  the
2635               limit.
2636               Default: )"
2637       << config->rlimit_memlock << R"(
2638   --backend-request-buffer=<SIZE>
2639               Set buffer size used to store backend request.
2640               Default: )"
2641       << util::utos_unit(config->conn.downstream->request_buffer_size) << R"(
2642   --backend-response-buffer=<SIZE>
2643               Set buffer size used to store backend response.
2644               Default: )"
2645       << util::utos_unit(config->conn.downstream->response_buffer_size) << R"(
2646   --fastopen=<N>
2647               Enables  "TCP Fast  Open" for  the listening  socket and
2648               limits the  maximum length for the  queue of connections
2649               that have not yet completed the three-way handshake.  If
2650               value is 0 then fast open is disabled.
2651               Default: )"
2652       << config->conn.listener.fastopen << R"(
2653   --no-kqueue Don't use  kqueue.  This  option is only  applicable for
2654               the platforms  which have kqueue.  For  other platforms,
2655               this option will be simply ignored.
2656 
2657 Timeout:
2658   --frontend-http2-idle-timeout=<DURATION>
2659               Specify idle timeout for HTTP/2 frontend connection.  If
2660               no active streams exist for this duration, connection is
2661               closed.
2662               Default: )"
2663       << util::duration_str(config->conn.upstream.timeout.http2_idle) << R"(
2664   --frontend-http3-idle-timeout=<DURATION>
2665               Specify idle timeout for HTTP/3 frontend connection.  If
2666               no active streams exist for this duration, connection is
2667               closed.
2668               Default: )"
2669       << util::duration_str(config->conn.upstream.timeout.http3_idle) << R"(
2670   --frontend-write-timeout=<DURATION>
2671               Specify write timeout for all frontend connections.
2672               Default: )"
2673       << util::duration_str(config->conn.upstream.timeout.write) << R"(
2674   --frontend-keep-alive-timeout=<DURATION>
2675               Specify   keep-alive   timeout   for   frontend   HTTP/1
2676               connection.
2677               Default: )"
2678       << util::duration_str(config->conn.upstream.timeout.idle) << R"(
2679   --frontend-header-timeout=<DURATION>
2680               Specify  duration  that the  server  waits  for an  HTTP
2681               request  header fields  to be  received completely.   On
2682               timeout, HTTP/1 and HTTP/2  connections are closed.  For
2683               HTTP/3,  the  stream  is shutdown,  and  the  connection
2684               itself is left intact.
2685               Default: )"
2686       << util::duration_str(config->http.timeout.header) << R"(
2687   --stream-read-timeout=<DURATION>
2688               Specify  read timeout  for HTTP/2  streams.  0  means no
2689               timeout.
2690               Default: )"
2691       << util::duration_str(config->http2.timeout.stream_read) << R"(
2692   --stream-write-timeout=<DURATION>
2693               Specify write  timeout for  HTTP/2 streams.  0  means no
2694               timeout.
2695               Default: )"
2696       << util::duration_str(config->http2.timeout.stream_write) << R"(
2697   --backend-read-timeout=<DURATION>
2698               Specify read timeout for backend connection.
2699               Default: )"
2700       << util::duration_str(config->conn.downstream->timeout.read) << R"(
2701   --backend-write-timeout=<DURATION>
2702               Specify write timeout for backend connection.
2703               Default: )"
2704       << util::duration_str(config->conn.downstream->timeout.write) << R"(
2705   --backend-connect-timeout=<DURATION>
2706               Specify  timeout before  establishing TCP  connection to
2707               backend.
2708               Default: )"
2709       << util::duration_str(config->conn.downstream->timeout.connect) << R"(
2710   --backend-keep-alive-timeout=<DURATION>
2711               Specify   keep-alive   timeout    for   backend   HTTP/1
2712               connection.
2713               Default: )"
2714       << util::duration_str(config->conn.downstream->timeout.idle_read) << R"(
2715   --listener-disable-timeout=<DURATION>
2716               After accepting  connection failed,  connection listener
2717               is disabled  for a given  amount of time.   Specifying 0
2718               disables this feature.
2719               Default: )"
2720       << util::duration_str(config->conn.listener.timeout.sleep) << R"(
2721   --frontend-http2-setting-timeout=<DURATION>
2722               Specify  timeout before  SETTINGS ACK  is received  from
2723               client.
2724               Default: )"
2725       << util::duration_str(config->http2.upstream.timeout.settings) << R"(
2726   --backend-http2-settings-timeout=<DURATION>
2727               Specify  timeout before  SETTINGS ACK  is received  from
2728               backend server.
2729               Default: )"
2730       << util::duration_str(config->http2.downstream.timeout.settings) << R"(
2731   --backend-max-backoff=<DURATION>
2732               Specify  maximum backoff  interval.  This  is used  when
2733               doing health  check against offline backend  (see "fail"
2734               parameter  in --backend  option).   It is  also used  to
2735               limit  the  maximum   interval  to  temporarily  disable
2736               backend  when nghttpx  failed to  connect to  it.  These
2737               intervals are calculated  using exponential backoff, and
2738               consecutive failed attempts increase the interval.  This
2739               option caps its maximum value.
2740               Default: )"
2741       << util::duration_str(config->conn.downstream->timeout.max_backoff) << R"(
2742 
2743 SSL/TLS:
2744   --ciphers=<SUITE>
2745               Set allowed  cipher list  for frontend  connection.  The
2746               format of the string is described in OpenSSL ciphers(1).
2747               This option  sets cipher suites for  TLSv1.2 or earlier.
2748               Use --tls13-ciphers for TLSv1.3.
2749               Default: )"
2750       << config->tls.ciphers << R"(
2751   --tls13-ciphers=<SUITE>
2752               Set allowed  cipher list  for frontend  connection.  The
2753               format of the string is described in OpenSSL ciphers(1).
2754               This  option  sets  cipher   suites  for  TLSv1.3.   Use
2755               --ciphers for TLSv1.2 or earlier.
2756               Default: )"
2757       << config->tls.tls13_ciphers << R"(
2758   --client-ciphers=<SUITE>
2759               Set  allowed cipher  list for  backend connection.   The
2760               format of the string is described in OpenSSL ciphers(1).
2761               This option  sets cipher suites for  TLSv1.2 or earlier.
2762               Use --tls13-client-ciphers for TLSv1.3.
2763               Default: )"
2764       << config->tls.client.ciphers << R"(
2765   --tls13-client-ciphers=<SUITE>
2766               Set  allowed cipher  list for  backend connection.   The
2767               format of the string is described in OpenSSL ciphers(1).
2768               This  option  sets  cipher   suites  for  TLSv1.3.   Use
2769               --tls13-client-ciphers for TLSv1.2 or earlier.
2770               Default: )"
2771       << config->tls.client.tls13_ciphers << R"(
2772   --ecdh-curves=<LIST>
2773               Set  supported  curve  list  for  frontend  connections.
2774               <LIST> is a  colon separated list of curve  NID or names
2775               in the preference order.  The supported curves depend on
2776               the  linked  OpenSSL  library.  This  function  requires
2777               OpenSSL >= 1.0.2.
2778               Default: )"
2779       << config->tls.ecdh_curves << R"(
2780   -k, --insecure
2781               Don't  verify backend  server's  certificate  if TLS  is
2782               enabled for backend connections.
2783   --cacert=<PATH>
2784               Set path to trusted CA  certificate file.  It is used in
2785               backend  TLS connections  to verify  peer's certificate.
2786               It is also used to  verify OCSP response from the script
2787               set by --fetch-ocsp-response-file.  The  file must be in
2788               PEM format.   It can contain multiple  certificates.  If
2789               the  linked OpenSSL  is configured  to load  system wide
2790               certificates, they  are loaded at startup  regardless of
2791               this option.
2792   --private-key-passwd-file=<PATH>
2793               Path  to file  that contains  password for  the server's
2794               private key.   If none is  given and the private  key is
2795               password protected it'll be requested interactively.
2796   --subcert=<KEYPATH>:<CERTPATH>[[;<PARAM>]...]
2797               Specify  additional certificate  and  private key  file.
2798               nghttpx will  choose certificates based on  the hostname
2799               indicated by client using TLS SNI extension.  If nghttpx
2800               is  built with  OpenSSL  >= 1.0.2,  the shared  elliptic
2801               curves (e.g., P-256) between  client and server are also
2802               taken into  consideration.  This allows nghttpx  to send
2803               ECDSA certificate  to modern clients, while  sending RSA
2804               based certificate to older  clients.  This option can be
2805               used  multiple  times.   To  make  OCSP  stapling  work,
2806               <CERTPATH> must be absolute path.
2807 
2808               Additional parameter  can be specified in  <PARAM>.  The
2809               available <PARAM> is "sct-dir=<DIR>".
2810 
2811               "sct-dir=<DIR>"  specifies the  path to  directory which
2812               contains        *.sct        files        for        TLS
2813               signed_certificate_timestamp extension (RFC 6962).  This
2814               feature   requires   OpenSSL   >=   1.0.2.    See   also
2815               --tls-sct-dir option.
2816   --dh-param-file=<PATH>
2817               Path to file that contains  DH parameters in PEM format.
2818               Without  this   option,  DHE   cipher  suites   are  not
2819               available.
2820   --alpn-list=<LIST>
2821               Comma delimited list of  ALPN protocol identifier sorted
2822               in the  order of preference.  That  means most desirable
2823               protocol comes  first.  The parameter must  be delimited
2824               by a single comma only  and any white spaces are treated
2825               as a part of protocol string.
2826               Default: )"
2827       << DEFAULT_ALPN_LIST
2828       << R"(
2829   --verify-client
2830               Require and verify client certificate.
2831   --verify-client-cacert=<PATH>
2832               Path  to file  that contains  CA certificates  to verify
2833               client certificate.  The file must be in PEM format.  It
2834               can contain multiple certificates.
2835   --verify-client-tolerate-expired
2836               Accept  expired  client  certificate.   Operator  should
2837               handle  the expired  client  certificate  by some  means
2838               (e.g.,  mruby  script).   Otherwise, this  option  might
2839               cause a security risk.
2840   --client-private-key-file=<PATH>
2841               Path to  file that contains  client private key  used in
2842               backend client authentication.
2843   --client-cert-file=<PATH>
2844               Path to  file that  contains client certificate  used in
2845               backend client authentication.
2846   --tls-min-proto-version=<VER>
2847               Specify minimum SSL/TLS protocol.   The name matching is
2848               done in  case-insensitive manner.  The  versions between
2849               --tls-min-proto-version and  --tls-max-proto-version are
2850               enabled.  If the protocol list advertised by client does
2851               not  overlap  this range,  you  will  receive the  error
2852               message "unknown protocol".  If a protocol version lower
2853               than TLSv1.2 is specified, make sure that the compatible
2854               ciphers are  included in --ciphers option.   The default
2855               cipher  list  only   includes  ciphers  compatible  with
2856               TLSv1.2 or above.  The available versions are:
2857               )"
2858 #ifdef TLS1_3_VERSION
2859          "TLSv1.3, "
2860 #endif // TLS1_3_VERSION
2861          "TLSv1.2, TLSv1.1, and TLSv1.0"
2862          R"(
2863               Default: )"
2864       << DEFAULT_TLS_MIN_PROTO_VERSION
2865       << R"(
2866   --tls-max-proto-version=<VER>
2867               Specify maximum SSL/TLS protocol.   The name matching is
2868               done in  case-insensitive manner.  The  versions between
2869               --tls-min-proto-version and  --tls-max-proto-version are
2870               enabled.  If the protocol list advertised by client does
2871               not  overlap  this range,  you  will  receive the  error
2872               message "unknown protocol".  The available versions are:
2873               )"
2874 #ifdef TLS1_3_VERSION
2875          "TLSv1.3, "
2876 #endif // TLS1_3_VERSION
2877          "TLSv1.2, TLSv1.1, and TLSv1.0"
2878          R"(
2879               Default: )"
2880       << DEFAULT_TLS_MAX_PROTO_VERSION << R"(
2881   --tls-ticket-key-file=<PATH>
2882               Path to file that contains  random data to construct TLS
2883               session ticket  parameters.  If aes-128-cbc is  given in
2884               --tls-ticket-key-cipher, the  file must  contain exactly
2885               48    bytes.     If     aes-256-cbc    is    given    in
2886               --tls-ticket-key-cipher, the  file must  contain exactly
2887               80  bytes.   This  options  can be  used  repeatedly  to
2888               specify  multiple ticket  parameters.  If  several files
2889               are given,  only the  first key is  used to  encrypt TLS
2890               session  tickets.  Other  keys are  accepted but  server
2891               will  issue new  session  ticket with  first key.   This
2892               allows  session  key  rotation.  Please  note  that  key
2893               rotation  does  not  occur automatically.   User  should
2894               rearrange  files or  change options  values and  restart
2895               nghttpx gracefully.   If opening  or reading  given file
2896               fails, all loaded  keys are discarded and  it is treated
2897               as if none  of this option is given.  If  this option is
2898               not given or an error  occurred while opening or reading
2899               a file,  key is  generated every  1 hour  internally and
2900               they are  valid for  12 hours.   This is  recommended if
2901               ticket  key sharing  between  nghttpx  instances is  not
2902               required.
2903   --tls-ticket-key-memcached=<HOST>,<PORT>[;tls]
2904               Specify address  of memcached  server to get  TLS ticket
2905               keys for  session resumption.   This enables  shared TLS
2906               ticket key between  multiple nghttpx instances.  nghttpx
2907               does not set TLS ticket  key to memcached.  The external
2908               ticket key generator is required.  nghttpx just gets TLS
2909               ticket  keys  from  memcached, and  use  them,  possibly
2910               replacing current set  of keys.  It is up  to extern TLS
2911               ticket  key generator  to rotate  keys frequently.   See
2912               "TLS SESSION  TICKET RESUMPTION" section in  manual page
2913               to know the data format in memcached entry.  Optionally,
2914               memcached  connection  can  be  encrypted  with  TLS  by
2915               specifying "tls" parameter.
2916   --tls-ticket-key-memcached-address-family=(auto|IPv4|IPv6)
2917               Specify address  family of memcached connections  to get
2918               TLS ticket keys.  If "auto" is given, both IPv4 and IPv6
2919               are considered.   If "IPv4" is given,  only IPv4 address
2920               is considered.  If "IPv6" is given, only IPv6 address is
2921               considered.
2922               Default: auto
2923   --tls-ticket-key-memcached-interval=<DURATION>
2924               Set interval to get TLS ticket keys from memcached.
2925               Default: )"
2926       << util::duration_str(config->tls.ticket.memcached.interval) << R"(
2927   --tls-ticket-key-memcached-max-retry=<N>
2928               Set  maximum   number  of  consecutive   retries  before
2929               abandoning TLS ticket key  retrieval.  If this number is
2930               reached,  the  attempt  is considered  as  failure,  and
2931               "failure" count  is incremented by 1,  which contributed
2932               to            the            value            controlled
2933               --tls-ticket-key-memcached-max-fail option.
2934               Default: )"
2935       << config->tls.ticket.memcached.max_retry << R"(
2936   --tls-ticket-key-memcached-max-fail=<N>
2937               Set  maximum   number  of  consecutive   failure  before
2938               disabling TLS ticket until next scheduled key retrieval.
2939               Default: )"
2940       << config->tls.ticket.memcached.max_fail << R"(
2941   --tls-ticket-key-cipher=<CIPHER>
2942               Specify cipher  to encrypt TLS session  ticket.  Specify
2943               either   aes-128-cbc   or  aes-256-cbc.    By   default,
2944               aes-128-cbc is used.
2945   --tls-ticket-key-memcached-cert-file=<PATH>
2946               Path to client certificate  for memcached connections to
2947               get TLS ticket keys.
2948   --tls-ticket-key-memcached-private-key-file=<PATH>
2949               Path to client private  key for memcached connections to
2950               get TLS ticket keys.
2951   --fetch-ocsp-response-file=<PATH>
2952               Path to  fetch-ocsp-response script file.  It  should be
2953               absolute path.
2954               Default: )"
2955       << config->tls.ocsp.fetch_ocsp_response_file << R"(
2956   --ocsp-update-interval=<DURATION>
2957               Set interval to update OCSP response cache.
2958               Default: )"
2959       << util::duration_str(config->tls.ocsp.update_interval) << R"(
2960   --ocsp-startup
2961               Start  accepting connections  after initial  attempts to
2962               get OCSP responses  finish.  It does not  matter some of
2963               the  attempts  fail.  This  feature  is  useful if  OCSP
2964               responses   must    be   available    before   accepting
2965               connections.
2966   --no-verify-ocsp
2967               nghttpx does not verify OCSP response.
2968   --no-ocsp   Disable OCSP stapling.
2969   --tls-session-cache-memcached=<HOST>,<PORT>[;tls]
2970               Specify  address of  memcached server  to store  session
2971               cache.   This  enables   shared  session  cache  between
2972               multiple   nghttpx  instances.    Optionally,  memcached
2973               connection can be encrypted with TLS by specifying "tls"
2974               parameter.
2975   --tls-session-cache-memcached-address-family=(auto|IPv4|IPv6)
2976               Specify address family of memcached connections to store
2977               session cache.  If  "auto" is given, both  IPv4 and IPv6
2978               are considered.   If "IPv4" is given,  only IPv4 address
2979               is considered.  If "IPv6" is given, only IPv6 address is
2980               considered.
2981               Default: auto
2982   --tls-session-cache-memcached-cert-file=<PATH>
2983               Path to client certificate  for memcached connections to
2984               store session cache.
2985   --tls-session-cache-memcached-private-key-file=<PATH>
2986               Path to client private  key for memcached connections to
2987               store session cache.
2988   --tls-dyn-rec-warmup-threshold=<SIZE>
2989               Specify the  threshold size for TLS  dynamic record size
2990               behaviour.  During  a TLS  session, after  the threshold
2991               number of bytes  have been written, the  TLS record size
2992               will be increased to the maximum allowed (16K).  The max
2993               record size will  continue to be used on  the active TLS
2994               session.  After  --tls-dyn-rec-idle-timeout has elapsed,
2995               the record size is reduced  to 1300 bytes.  Specify 0 to
2996               always use  the maximum record size,  regardless of idle
2997               period.   This  behaviour  applies   to  all  TLS  based
2998               frontends, and TLS HTTP/2 backends.
2999               Default: )"
3000       << util::utos_unit(config->tls.dyn_rec.warmup_threshold) << R"(
3001   --tls-dyn-rec-idle-timeout=<DURATION>
3002               Specify TLS dynamic record  size behaviour timeout.  See
3003               --tls-dyn-rec-warmup-threshold  for   more  information.
3004               This behaviour  applies to all TLS  based frontends, and
3005               TLS HTTP/2 backends.
3006               Default: )"
3007       << util::duration_str(config->tls.dyn_rec.idle_timeout) << R"(
3008   --no-http2-cipher-block-list
3009               Allow  block  listed  cipher suite  on  frontend  HTTP/2
3010               connection.                                          See
3011               https://tools.ietf.org/html/rfc7540#appendix-A  for  the
3012               complete HTTP/2 cipher suites block list.
3013   --client-no-http2-cipher-block-list
3014               Allow  block  listed  cipher  suite  on  backend  HTTP/2
3015               connection.                                          See
3016               https://tools.ietf.org/html/rfc7540#appendix-A  for  the
3017               complete HTTP/2 cipher suites block list.
3018   --tls-sct-dir=<DIR>
3019               Specifies the  directory where  *.sct files  exist.  All
3020               *.sct   files   in  <DIR>   are   read,   and  sent   as
3021               extension_data of  TLS signed_certificate_timestamp (RFC
3022               6962)  to  client.   These   *.sct  files  are  for  the
3023               certificate   specified   in   positional   command-line
3024               argument <CERT>, or  certificate option in configuration
3025               file.   For   additional  certificates,   use  --subcert
3026               option.  This option requires OpenSSL >= 1.0.2.
3027   --psk-secrets=<PATH>
3028               Read list of PSK identity and secrets from <PATH>.  This
3029               is used for frontend connection.  The each line of input
3030               file  is  formatted  as  <identity>:<hex-secret>,  where
3031               <identity> is  PSK identity, and <hex-secret>  is secret
3032               in hex.  An  empty line, and line which  starts with '#'
3033               are skipped.  The default  enabled cipher list might not
3034               contain any PSK cipher suite.  In that case, desired PSK
3035               cipher suites  must be  enabled using  --ciphers option.
3036               The  desired PSK  cipher suite  may be  block listed  by
3037               HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
3038               consider  to  use  --no-http2-cipher-block-list  option.
3039               But be aware its implications.
3040   --client-psk-secrets=<PATH>
3041               Read PSK identity and secrets from <PATH>.  This is used
3042               for backend connection.  The each  line of input file is
3043               formatted  as <identity>:<hex-secret>,  where <identity>
3044               is PSK identity, and <hex-secret>  is secret in hex.  An
3045               empty line, and line which  starts with '#' are skipped.
3046               The first identity and  secret pair encountered is used.
3047               The default  enabled cipher  list might not  contain any
3048               PSK  cipher suite.   In  that case,  desired PSK  cipher
3049               suites  must be  enabled using  --client-ciphers option.
3050               The  desired PSK  cipher suite  may be  block listed  by
3051               HTTP/2.   To  use  those   cipher  suites  with  HTTP/2,
3052               consider   to  use   --client-no-http2-cipher-block-list
3053               option.  But be aware its implications.
3054   --tls-no-postpone-early-data
3055               By  default,   except  for  QUIC   connections,  nghttpx
3056               postpones forwarding  HTTP requests sent in  early data,
3057               including  those  sent in  partially  in  it, until  TLS
3058               handshake  finishes.  If  all backend  server recognizes
3059               "Early-Data"  header  field,  using  this  option  makes
3060               nghttpx  not postpone  forwarding request  and get  full
3061               potential of 0-RTT data.
3062   --tls-max-early-data=<SIZE>
3063               Sets  the  maximum  amount  of 0-RTT  data  that  server
3064               accepts.
3065               Default: )"
3066       << util::utos_unit(config->tls.max_early_data) << R"(
3067   --tls-ktls  Enable   ktls.    For   server,  ktls   is   enable   if
3068               --tls-session-cache-memcached is not configured.
3069 
3070 HTTP/2:
3071   -c, --frontend-http2-max-concurrent-streams=<N>
3072               Set the maximum number of  the concurrent streams in one
3073               frontend HTTP/2 session.
3074               Default: )"
3075       << config->http2.upstream.max_concurrent_streams << R"(
3076   --backend-http2-max-concurrent-streams=<N>
3077               Set the maximum number of  the concurrent streams in one
3078               backend  HTTP/2 session.   This sets  maximum number  of
3079               concurrent opened pushed streams.  The maximum number of
3080               concurrent requests are set by a remote server.
3081               Default: )"
3082       << config->http2.downstream.max_concurrent_streams << R"(
3083   --frontend-http2-window-size=<SIZE>
3084               Sets  the  per-stream  initial  window  size  of  HTTP/2
3085               frontend connection.
3086               Default: )"
3087       << config->http2.upstream.window_size << R"(
3088   --frontend-http2-connection-window-size=<SIZE>
3089               Sets the  per-connection window size of  HTTP/2 frontend
3090               connection.
3091               Default: )"
3092       << config->http2.upstream.connection_window_size << R"(
3093   --backend-http2-window-size=<SIZE>
3094               Sets  the   initial  window   size  of   HTTP/2  backend
3095               connection.
3096               Default: )"
3097       << config->http2.downstream.window_size << R"(
3098   --backend-http2-connection-window-size=<SIZE>
3099               Sets the  per-connection window  size of  HTTP/2 backend
3100               connection.
3101               Default: )"
3102       << config->http2.downstream.connection_window_size << R"(
3103   --http2-no-cookie-crumbling
3104               Don't crumble cookie header field.
3105   --padding=<N>
3106               Add  at most  <N> bytes  to  a HTTP/2  frame payload  as
3107               padding.  Specify 0 to  disable padding.  This option is
3108               meant for debugging purpose  and not intended to enhance
3109               protocol security.
3110   --no-server-push
3111               Disable HTTP/2 server push.  Server push is supported by
3112               default mode and HTTP/2  frontend via Link header field.
3113               It is  also supported if  both frontend and  backend are
3114               HTTP/2 in default mode.  In  this case, server push from
3115               backend session is relayed  to frontend, and server push
3116               via Link header field is also supported.
3117   --frontend-http2-optimize-write-buffer-size
3118               (Experimental) Enable write  buffer size optimization in
3119               frontend HTTP/2 TLS  connection.  This optimization aims
3120               to reduce  write buffer  size so  that it  only contains
3121               bytes  which can  send immediately.   This makes  server
3122               more responsive to prioritized HTTP/2 stream because the
3123               buffering  of lower  priority stream  is reduced.   This
3124               option is only effective on recent Linux platform.
3125   --frontend-http2-optimize-window-size
3126               (Experimental)   Automatically  tune   connection  level
3127               window size of frontend  HTTP/2 TLS connection.  If this
3128               feature is  enabled, connection window size  starts with
3129               the   default  window   size,   65535  bytes.    nghttpx
3130               automatically  adjusts connection  window size  based on
3131               TCP receiving  window size.  The maximum  window size is
3132               capped      by      the     value      specified      by
3133               --frontend-http2-connection-window-size.     Since   the
3134               stream is subject to stream level window size, it should
3135               be adjusted using --frontend-http2-window-size option as
3136               well.   This option  is only  effective on  recent Linux
3137               platform.
3138   --frontend-http2-encoder-dynamic-table-size=<SIZE>
3139               Specify the maximum dynamic  table size of HPACK encoder
3140               in the frontend HTTP/2 connection.  The decoder (client)
3141               specifies  the maximum  dynamic table  size it  accepts.
3142               Then the negotiated dynamic table size is the minimum of
3143               this option value and the value which client specified.
3144               Default: )"
3145       << util::utos_unit(config->http2.upstream.encoder_dynamic_table_size)
3146       << R"(
3147   --frontend-http2-decoder-dynamic-table-size=<SIZE>
3148               Specify the maximum dynamic  table size of HPACK decoder
3149               in the frontend HTTP/2 connection.
3150               Default: )"
3151       << util::utos_unit(config->http2.upstream.decoder_dynamic_table_size)
3152       << R"(
3153   --backend-http2-encoder-dynamic-table-size=<SIZE>
3154               Specify the maximum dynamic  table size of HPACK encoder
3155               in the backend HTTP/2 connection.  The decoder (backend)
3156               specifies  the maximum  dynamic table  size it  accepts.
3157               Then the negotiated dynamic table size is the minimum of
3158               this option value and the value which backend specified.
3159               Default: )"
3160       << util::utos_unit(config->http2.downstream.encoder_dynamic_table_size)
3161       << R"(
3162   --backend-http2-decoder-dynamic-table-size=<SIZE>
3163               Specify the maximum dynamic  table size of HPACK decoder
3164               in the backend HTTP/2 connection.
3165               Default: )"
3166       << util::utos_unit(config->http2.downstream.decoder_dynamic_table_size)
3167       << R"(
3168 
3169 Mode:
3170   (default mode)
3171               Accept  HTTP/2,  and  HTTP/1.1 over  SSL/TLS.   "no-tls"
3172               parameter is  used in  --frontend option,  accept HTTP/2
3173               and HTTP/1.1 over cleartext  TCP.  The incoming HTTP/1.1
3174               connection  can  be  upgraded  to  HTTP/2  through  HTTP
3175               Upgrade.
3176   -s, --http2-proxy
3177               Like default mode, but enable forward proxy.  This is so
3178               called HTTP/2 proxy mode.
3179 
3180 Logging:
3181   -L, --log-level=<LEVEL>
3182               Set the severity  level of log output.   <LEVEL> must be
3183               one of INFO, NOTICE, WARN, ERROR and FATAL.
3184               Default: NOTICE
3185   --accesslog-file=<PATH>
3186               Set path to write access log.  To reopen file, send USR1
3187               signal to nghttpx.
3188   --accesslog-syslog
3189               Send  access log  to syslog.   If this  option is  used,
3190               --accesslog-file option is ignored.
3191   --accesslog-format=<FORMAT>
3192               Specify  format  string  for access  log.   The  default
3193               format is combined format.   The following variables are
3194               available:
3195 
3196               * $remote_addr: client IP address.
3197               * $time_local: local time in Common Log format.
3198               * $time_iso8601: local time in ISO 8601 format.
3199               * $request: HTTP request line.
3200               * $status: HTTP response status code.
3201               * $body_bytes_sent: the  number of bytes sent  to client
3202                 as response body.
3203               * $http_<VAR>: value of HTTP  request header <VAR> where
3204                 '_' in <VAR> is replaced with '-'.
3205               * $remote_port: client  port.
3206               * $server_port: server port.
3207               * $request_time: request processing time in seconds with
3208                 milliseconds resolution.
3209               * $pid: PID of the running process.
3210               * $alpn: ALPN identifier of the protocol which generates
3211                 the response.   For HTTP/1,  ALPN is  always http/1.1,
3212                 regardless of minor version.
3213               * $tls_cipher: cipher used for SSL/TLS connection.
3214               * $tls_client_fingerprint_sha256: SHA-256 fingerprint of
3215                 client certificate.
3216               * $tls_client_fingerprint_sha1:  SHA-1   fingerprint  of
3217                 client certificate.
3218               * $tls_client_subject_name:   subject  name   in  client
3219                 certificate.
3220               * $tls_client_issuer_name:   issuer   name   in   client
3221                 certificate.
3222               * $tls_client_serial:    serial    number   in    client
3223                 certificate.
3224               * $tls_protocol: protocol for SSL/TLS connection.
3225               * $tls_session_id: session ID for SSL/TLS connection.
3226               * $tls_session_reused:  "r"   if  SSL/TLS   session  was
3227                 reused.  Otherwise, "."
3228               * $tls_sni: SNI server name for SSL/TLS connection.
3229               * $backend_host:  backend  host   used  to  fulfill  the
3230                 request.  "-" if backend host is not available.
3231               * $backend_port:  backend  port   used  to  fulfill  the
3232                 request.  "-" if backend host is not available.
3233               * $method: HTTP method
3234               * $path:  Request  path  including query.   For  CONNECT
3235                 request, authority is recorded.
3236               * $path_without_query:  $path   up  to  the   first  '?'
3237                 character.    For   CONNECT  request,   authority   is
3238                 recorded.
3239               * $protocol_version:   HTTP  version   (e.g.,  HTTP/1.1,
3240                 HTTP/2)
3241 
3242               The  variable  can  be  enclosed  by  "{"  and  "}"  for
3243               disambiguation (e.g., ${remote_addr}).
3244 
3245               Default: )"
3246       << DEFAULT_ACCESSLOG_FORMAT << R"(
3247   --accesslog-write-early
3248               Write  access  log  when   response  header  fields  are
3249               received   from  backend   rather   than  when   request
3250               transaction finishes.
3251   --errorlog-file=<PATH>
3252               Set path to write error  log.  To reopen file, send USR1
3253               signal  to nghttpx.   stderr will  be redirected  to the
3254               error log file unless --errorlog-syslog is used.
3255               Default: )"
3256       << config->logging.error.file << R"(
3257   --errorlog-syslog
3258               Send  error log  to  syslog.  If  this  option is  used,
3259               --errorlog-file option is ignored.
3260   --syslog-facility=<FACILITY>
3261               Set syslog facility to <FACILITY>.
3262               Default: )"
3263       << str_syslog_facility(config->logging.syslog_facility) << R"(
3264 
3265 HTTP:
3266   --add-x-forwarded-for
3267               Append  X-Forwarded-For header  field to  the downstream
3268               request.
3269   --strip-incoming-x-forwarded-for
3270               Strip X-Forwarded-For  header field from  inbound client
3271               requests.
3272   --no-add-x-forwarded-proto
3273               Don't append  additional X-Forwarded-Proto  header field
3274               to  the   backend  request.   If  inbound   client  sets
3275               X-Forwarded-Proto,                                   and
3276               --no-strip-incoming-x-forwarded-proto  option  is  used,
3277               they are passed to the backend.
3278   --no-strip-incoming-x-forwarded-proto
3279               Don't strip X-Forwarded-Proto  header field from inbound
3280               client requests.
3281   --add-forwarded=<LIST>
3282               Append RFC  7239 Forwarded header field  with parameters
3283               specified in comma delimited list <LIST>.  The supported
3284               parameters  are "by",  "for", "host",  and "proto".   By
3285               default,  the value  of  "by" and  "for" parameters  are
3286               obfuscated     string.     See     --forwarded-by    and
3287               --forwarded-for options respectively.  Note that nghttpx
3288               does  not  translate non-standard  X-Forwarded-*  header
3289               fields into Forwarded header field, and vice versa.
3290   --strip-incoming-forwarded
3291               Strip  Forwarded   header  field  from   inbound  client
3292               requests.
3293   --forwarded-by=(obfuscated|ip|<VALUE>)
3294               Specify the parameter value sent out with "by" parameter
3295               of Forwarded  header field.   If "obfuscated"  is given,
3296               the string is randomly generated at startup.  If "ip" is
3297               given,   the  interface   address  of   the  connection,
3298               including port number, is  sent with "by" parameter.  In
3299               case of UNIX domain  socket, "localhost" is used instead
3300               of address and  port.  User can also  specify the static
3301               obfuscated string.  The limitation is that it must start
3302               with   "_",  and   only   consists   of  character   set
3303               [A-Za-z0-9._-], as described in RFC 7239.
3304               Default: obfuscated
3305   --forwarded-for=(obfuscated|ip)
3306               Specify  the   parameter  value  sent  out   with  "for"
3307               parameter of Forwarded header field.  If "obfuscated" is
3308               given, the string is  randomly generated for each client
3309               connection.  If "ip" is given, the remote client address
3310               of  the connection,  without port  number, is  sent with
3311               "for"  parameter.   In  case   of  UNIX  domain  socket,
3312               "localhost" is used instead of address.
3313               Default: obfuscated
3314   --no-via    Don't append to  Via header field.  If  Via header field
3315               is received, it is left unaltered.
3316   --no-strip-incoming-early-data
3317               Don't strip Early-Data header  field from inbound client
3318               requests.
3319   --no-location-rewrite
3320               Don't  rewrite location  header field  in default  mode.
3321               When --http2-proxy  is used, location header  field will
3322               not be altered regardless of this option.
3323   --host-rewrite
3324               Rewrite  host and  :authority header  fields in  default
3325               mode.  When  --http2-proxy is  used, these  headers will
3326               not be altered regardless of this option.
3327   --altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
3328               Specify   protocol  ID,   port,  host   and  origin   of
3329               alternative service.  <HOST>,  <ORIGIN> and <PARAMS> are
3330               optional.   Empty <HOST>  and <ORIGIN>  are allowed  and
3331               they  are treated  as  nothing is  specified.  They  are
3332               advertised  in alt-svc  header  field  only in  HTTP/1.1
3333               frontend.   This option  can be  used multiple  times to
3334               specify multiple alternative services.
3335               Example: --altsvc="h2,443,,,ma=3600; persist=1"
3336   --http2-altsvc=<PROTOID,PORT[,HOST,[ORIGIN[,PARAMS]]]>
3337               Just like --altsvc option, but  this altsvc is only sent
3338               in HTTP/2 frontend.
3339   --add-request-header=<HEADER>
3340               Specify additional header field to add to request header
3341               set.   The field  name must  be lowercase.   This option
3342               just  appends header  field and  won't replace  anything
3343               already set.  This  option can be used  several times to
3344               specify multiple header fields.
3345               Example: --add-request-header="foo: bar"
3346   --add-response-header=<HEADER>
3347               Specify  additional  header  field to  add  to  response
3348               header  set.  The  field name  must be  lowercase.  This
3349               option  just  appends  header field  and  won't  replace
3350               anything already  set.  This option can  be used several
3351               times to specify multiple header fields.
3352               Example: --add-response-header="foo: bar"
3353   --request-header-field-buffer=<SIZE>
3354               Set maximum buffer size for incoming HTTP request header
3355               field list.  This is the sum of header name and value in
3356               bytes.   If  trailer  fields  exist,  they  are  counted
3357               towards this number.
3358               Default: )"
3359       << util::utos_unit(config->http.request_header_field_buffer) << R"(
3360   --max-request-header-fields=<N>
3361               Set  maximum  number  of incoming  HTTP  request  header
3362               fields.   If  trailer  fields exist,  they  are  counted
3363               towards this number.
3364               Default: )"
3365       << config->http.max_request_header_fields << R"(
3366   --response-header-field-buffer=<SIZE>
3367               Set  maximum  buffer  size for  incoming  HTTP  response
3368               header field list.   This is the sum of  header name and
3369               value  in  bytes.  If  trailer  fields  exist, they  are
3370               counted towards this number.
3371               Default: )"
3372       << util::utos_unit(config->http.response_header_field_buffer) << R"(
3373   --max-response-header-fields=<N>
3374               Set  maximum number  of  incoming  HTTP response  header
3375               fields.   If  trailer  fields exist,  they  are  counted
3376               towards this number.
3377               Default: )"
3378       << config->http.max_response_header_fields << R"(
3379   --error-page=(<CODE>|*)=<PATH>
3380               Set file path  to custom error page  served when nghttpx
3381               originally  generates  HTTP  error status  code  <CODE>.
3382               <CODE> must be greater than or equal to 400, and at most
3383               599.  If "*"  is used instead of <CODE>,  it matches all
3384               HTTP  status  code.  If  error  status  code comes  from
3385               backend server, the custom error pages are not used.
3386   --server-name=<NAME>
3387               Change server response header field value to <NAME>.
3388               Default: )"
3389       << config->http.server_name << R"(
3390   --no-server-rewrite
3391               Don't rewrite server header field in default mode.  When
3392               --http2-proxy is used, these headers will not be altered
3393               regardless of this option.
3394   --redirect-https-port=<PORT>
3395               Specify the port number which appears in Location header
3396               field  when  redirect  to  HTTPS  URI  is  made  due  to
3397               "redirect-if-not-tls" parameter in --backend option.
3398               Default: )"
3399       << config->http.redirect_https_port << R"(
3400   --require-http-scheme
3401               Always require http or https scheme in HTTP request.  It
3402               also  requires that  https scheme  must be  used for  an
3403               encrypted  connection.  Otherwise,  http scheme  must be
3404               used.   This   option  is   recommended  for   a  server
3405               deployment which directly faces clients and the services
3406               it provides only require http or https scheme.
3407 
3408 API:
3409   --api-max-request-body=<SIZE>
3410               Set the maximum size of request body for API request.
3411               Default: )"
3412       << util::utos_unit(config->api.max_request_body) << R"(
3413 
3414 DNS:
3415   --dns-cache-timeout=<DURATION>
3416               Set duration that cached DNS results remain valid.  Note
3417               that nghttpx caches the unsuccessful results as well.
3418               Default: )"
3419       << util::duration_str(config->dns.timeout.cache) << R"(
3420   --dns-lookup-timeout=<DURATION>
3421               Set timeout that  DNS server is given to  respond to the
3422               initial  DNS  query.  For  the  2nd  and later  queries,
3423               server is  given time based  on this timeout, and  it is
3424               scaled linearly.
3425               Default: )"
3426       << util::duration_str(config->dns.timeout.lookup) << R"(
3427   --dns-max-try=<N>
3428               Set the number of DNS query before nghttpx gives up name
3429               lookup.
3430               Default: )"
3431       << config->dns.max_try << R"(
3432   --frontend-max-requests=<N>
3433               The number  of requests that single  frontend connection
3434               can process.  For HTTP/2, this  is the number of streams
3435               in  one  HTTP/2 connection.   For  HTTP/1,  this is  the
3436               number of keep alive requests.  This is hint to nghttpx,
3437               and it  may allow additional few  requests.  The default
3438               value is unlimited.
3439 
3440 Debug:
3441   --frontend-http2-dump-request-header=<PATH>
3442               Dumps request headers received by HTTP/2 frontend to the
3443               file denoted  in <PATH>.  The  output is done  in HTTP/1
3444               header field format and each header block is followed by
3445               an empty line.  This option  is not thread safe and MUST
3446               NOT be used with option -n<N>, where <N> >= 2.
3447   --frontend-http2-dump-response-header=<PATH>
3448               Dumps response headers sent  from HTTP/2 frontend to the
3449               file denoted  in <PATH>.  The  output is done  in HTTP/1
3450               header field format and each header block is followed by
3451               an empty line.  This option  is not thread safe and MUST
3452               NOT be used with option -n<N>, where <N> >= 2.
3453   -o, --frontend-frame-debug
3454               Print HTTP/2 frames in  frontend to stderr.  This option
3455               is  not thread  safe and  MUST NOT  be used  with option
3456               -n=N, where N >= 2.
3457 
3458 Process:
3459   -D, --daemon
3460               Run in a background.  If -D is used, the current working
3461               directory is changed to '/'.
3462   --pid-file=<PATH>
3463               Set path to save PID of this program.
3464   --user=<USER>
3465               Run this program as <USER>.   This option is intended to
3466               be used to drop root privileges.
3467   --single-process
3468               Run this program in a  single process mode for debugging
3469               purpose.  Without this option,  nghttpx creates at least
3470               2 processes: main and  worker processes.  If this option
3471               is  used, main  and  worker are  unified  into a  single
3472               process.   nghttpx still  spawns  additional process  if
3473               neverbleed  is used.   In the  single process  mode, the
3474               signal handling feature is disabled.
3475   --max-worker-processes=<N>
3476               The maximum number of  worker processes.  nghttpx spawns
3477               new worker  process when  it reloads  its configuration.
3478               The previous worker  process enters graceful termination
3479               period and will terminate  when it finishes handling the
3480               existing    connections.     However,    if    reloading
3481               configurations  happen   very  frequently,   the  worker
3482               processes might be piled up if they take a bit long time
3483               to finish  the existing connections.  With  this option,
3484               if  the number  of  worker processes  exceeds the  given
3485               value,   the  oldest   worker   process  is   terminated
3486               immediately.  Specifying 0 means no  limit and it is the
3487               default behaviour.
3488   --worker-process-grace-shutdown-period=<DURATION>
3489               Maximum  period  for  a   worker  process  to  terminate
3490               gracefully.  When  a worker  process enters  in graceful
3491               shutdown   period  (e.g.,   when  nghttpx   reloads  its
3492               configuration)  and  it  does not  finish  handling  the
3493               existing connections in the given  period of time, it is
3494               immediately terminated.  Specifying 0 means no limit and
3495               it is the default behaviour.
3496 
3497 Scripting:
3498   --mruby-file=<PATH>
3499               Set mruby script file
3500   --ignore-per-pattern-mruby-error
3501               Ignore mruby compile error  for per-pattern mruby script
3502               file.  If error  occurred, it is treated as  if no mruby
3503               file were specified for the pattern.
3504 )";
3505 
3506 #ifdef ENABLE_HTTP3
3507   out << R"(
3508 HTTP/3 and QUIC:
3509   --frontend-quic-idle-timeout=<DURATION>
3510               Specify an idle timeout for QUIC connection.
3511               Default: )"
3512       << util::duration_str(config->quic.upstream.timeout.idle) << R"(
3513   --frontend-quic-debug-log
3514               Output QUIC debug log to /dev/stderr.
3515   --quic-bpf-program-file=<PATH>
3516               Specify a path to  eBPF program file reuseport_kern.o to
3517               direct  an  incoming  QUIC  UDP datagram  to  a  correct
3518               socket.
3519               Default: )"
3520       << config->quic.bpf.prog_file << R"(
3521   --frontend-quic-early-data
3522               Enable early data on frontend QUIC connections.  nghttpx
3523               sends "Early-Data" header field to a backend server if a
3524               request is received in early  data and handshake has not
3525               finished.  All backend servers should deal with possibly
3526               replayed requests.
3527   --frontend-quic-qlog-dir=<DIR>
3528               Specify a  directory where  a qlog  file is  written for
3529               frontend QUIC  connections.  A qlog file  is created per
3530               each QUIC  connection.  The  file name is  ISO8601 basic
3531               format, followed by "-", server Source Connection ID and
3532               ".sqlog".
3533   --frontend-quic-require-token
3534               Require an address validation  token for a frontend QUIC
3535               connection.   Server sends  a token  in Retry  packet or
3536               NEW_TOKEN frame in the previous connection.
3537   --frontend-quic-congestion-controller=<CC>
3538               Specify a congestion controller algorithm for a frontend
3539               QUIC  connection.   <CC>  should be  either  "cubic"  or
3540               "bbr".
3541               Default: )"
3542       << (config->quic.upstream.congestion_controller == NGTCP2_CC_ALGO_CUBIC
3543               ? "cubic"
3544               : "bbr")
3545       << R"(
3546   --frontend-quic-secret-file=<PATH>
3547               Path to file that contains secure random data to be used
3548               as QUIC keying materials.  It is used to derive keys for
3549               encrypting tokens and Connection IDs.  It is not used to
3550               encrypt  QUIC  packets.  Each  line  of  this file  must
3551               contain  exactly  136  bytes  hex-encoded  string  (when
3552               decoded the byte string is  68 bytes long).  The first 3
3553               bits of  decoded byte  string are  used to  identify the
3554               keying material.  An  empty line or a  line which starts
3555               '#'  is ignored.   The file  can contain  more than  one
3556               keying materials.  Because the  identifier is 3 bits, at
3557               most 8 keying materials are  read and the remaining data
3558               is discarded.  The first keying  material in the file is
3559               primarily  used for  encryption and  decryption for  new
3560               connection.  The other ones are used to decrypt data for
3561               the  existing connections.   Specifying multiple  keying
3562               materials enables  key rotation.   Please note  that key
3563               rotation  does  not  occur automatically.   User  should
3564               update  files  or  change  options  values  and  restart
3565               nghttpx gracefully.   If opening  or reading  given file
3566               fails, all loaded keying  materials are discarded and it
3567               is treated as if none of  this option is given.  If this
3568               option is not  given or an error  occurred while opening
3569               or  reading  a  file,  a keying  material  is  generated
3570               internally on startup and reload.
3571   --quic-server-id=<HEXSTRING>
3572               Specify server  ID encoded in Connection  ID to identify
3573               this  particular  server  instance.   Connection  ID  is
3574               encrypted and  this part is  not visible in  public.  It
3575               must be 4  bytes long and must be encoded  in hex string
3576               (which is 8  bytes long).  If this option  is omitted, a
3577               random   server  ID   is   generated   on  startup   and
3578               configuration reload.
3579   --frontend-quic-initial-rtt=<DURATION>
3580               Specify the initial RTT of the frontend QUIC connection.
3581               Default: )"
3582       << util::duration_str(config->quic.upstream.initial_rtt) << R"(
3583   --no-quic-bpf
3584               Disable eBPF.
3585   --frontend-http3-window-size=<SIZE>
3586               Sets  the  per-stream  initial  window  size  of  HTTP/3
3587               frontend connection.
3588               Default: )"
3589       << util::utos_unit(config->http3.upstream.window_size) << R"(
3590   --frontend-http3-connection-window-size=<SIZE>
3591               Sets the  per-connection window size of  HTTP/3 frontend
3592               connection.
3593               Default: )"
3594       << util::utos_unit(config->http3.upstream.connection_window_size) << R"(
3595   --frontend-http3-max-window-size=<SIZE>
3596               Sets  the  maximum  per-stream  window  size  of  HTTP/3
3597               frontend connection.  The window  size is adjusted based
3598               on the receiving rate of stream data.  The initial value
3599               is the  value specified  by --frontend-http3-window-size
3600               and the window size grows up to <SIZE> bytes.
3601               Default: )"
3602       << util::utos_unit(config->http3.upstream.max_window_size) << R"(
3603   --frontend-http3-max-connection-window-size=<SIZE>
3604               Sets the  maximum per-connection  window size  of HTTP/3
3605               frontend connection.  The window  size is adjusted based
3606               on the receiving rate of stream data.  The initial value
3607               is         the         value        specified         by
3608               --frontend-http3-connection-window-size  and the  window
3609               size grows up to <SIZE> bytes.
3610               Default: )"
3611       << util::utos_unit(config->http3.upstream.max_connection_window_size)
3612       << R"(
3613   --frontend-http3-max-concurrent-streams=<N>
3614               Set the maximum number of  the concurrent streams in one
3615               frontend HTTP/3 connection.
3616               Default: )"
3617       << config->http3.upstream.max_concurrent_streams << R"(
3618 )";
3619 #endif // ENABLE_HTTP3
3620 
3621   out << R"(
3622 Misc:
3623   --conf=<PATH>
3624               Load  configuration  from   <PATH>.   Please  note  that
3625               nghttpx always  tries to read the  default configuration
3626               file if --conf is not given.
3627               Default: )"
3628       << config->conf_path << R"(
3629   --include=<PATH>
3630               Load additional configurations from <PATH>.  File <PATH>
3631               is  read  when  configuration  parser  encountered  this
3632               option.  This option can be used multiple times, or even
3633               recursively.
3634   -v, --version
3635               Print version and exit.
3636   -h, --help  Print this help and exit.
3637 
3638 --
3639 
3640   The <SIZE> argument is an integer and an optional unit (e.g., 10K is
3641   10 * 1024).  Units are K, M and G (powers of 1024).
3642 
3643   The <DURATION> argument is an integer and an optional unit (e.g., 1s
3644   is 1 second and 500ms is 500 milliseconds).  Units are h, m, s or ms
3645   (hours, minutes, seconds and milliseconds, respectively).  If a unit
3646   is omitted, a second is used as unit.)"
3647       << std::endl;
3648 }
3649 } // namespace
3650 
3651 namespace {
process_options(Config * config,std::vector<std::pair<StringRef,StringRef>> & cmdcfgs)3652 int process_options(Config *config,
3653                     std::vector<std::pair<StringRef, StringRef>> &cmdcfgs) {
3654   std::array<char, STRERROR_BUFSIZE> errbuf;
3655   std::map<StringRef, size_t> pattern_addr_indexer;
3656   if (conf_exists(config->conf_path.data())) {
3657     LOG(NOTICE) << "Loading configuration from " << config->conf_path;
3658     std::set<StringRef> include_set;
3659     if (load_config(config, config->conf_path.data(), include_set,
3660                     pattern_addr_indexer) == -1) {
3661       LOG(FATAL) << "Failed to load configuration from " << config->conf_path;
3662       return -1;
3663     }
3664     assert(include_set.empty());
3665   }
3666 
3667   // Reopen log files using configurations in file
3668   reopen_log_files(config->logging);
3669 
3670   {
3671     std::set<StringRef> include_set;
3672 
3673     for (auto &p : cmdcfgs) {
3674       if (parse_config(config, p.first, p.second, include_set,
3675                        pattern_addr_indexer) == -1) {
3676         LOG(FATAL) << "Failed to parse command-line argument.";
3677         return -1;
3678       }
3679     }
3680 
3681     assert(include_set.empty());
3682   }
3683 
3684   Log::set_severity_level(config->logging.severity);
3685 
3686   auto &loggingconf = config->logging;
3687 
3688   if (loggingconf.access.syslog || loggingconf.error.syslog) {
3689     openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID,
3690             loggingconf.syslog_facility);
3691   }
3692 
3693   if (reopen_log_files(config->logging) != 0) {
3694     LOG(FATAL) << "Failed to open log file";
3695     return -1;
3696   }
3697 
3698   redirect_stderr_to_errorlog(loggingconf);
3699 
3700   if (config->uid != 0) {
3701     if (log_config()->accesslog_fd != -1 &&
3702         fchown(log_config()->accesslog_fd, config->uid, config->gid) == -1) {
3703       auto error = errno;
3704       LOG(WARN) << "Changing owner of access log file failed: "
3705                 << xsi_strerror(error, errbuf.data(), errbuf.size());
3706     }
3707     if (log_config()->errorlog_fd != -1 &&
3708         fchown(log_config()->errorlog_fd, config->uid, config->gid) == -1) {
3709       auto error = errno;
3710       LOG(WARN) << "Changing owner of error log file failed: "
3711                 << xsi_strerror(error, errbuf.data(), errbuf.size());
3712     }
3713   }
3714 
3715   if (config->single_thread) {
3716     LOG(WARN) << "single-thread: Set workers to 1";
3717     config->num_worker = 1;
3718   }
3719 
3720   auto &http2conf = config->http2;
3721   {
3722     auto &dumpconf = http2conf.upstream.debug.dump;
3723 
3724     if (!dumpconf.request_header_file.empty()) {
3725       auto path = dumpconf.request_header_file.data();
3726       auto f = open_file_for_write(path);
3727 
3728       if (f == nullptr) {
3729         LOG(FATAL) << "Failed to open http2 upstream request header file: "
3730                    << path;
3731         return -1;
3732       }
3733 
3734       dumpconf.request_header = f;
3735 
3736       if (config->uid != 0) {
3737         if (chown(path, config->uid, config->gid) == -1) {
3738           auto error = errno;
3739           LOG(WARN) << "Changing owner of http2 upstream request header file "
3740                     << path << " failed: "
3741                     << xsi_strerror(error, errbuf.data(), errbuf.size());
3742         }
3743       }
3744     }
3745 
3746     if (!dumpconf.response_header_file.empty()) {
3747       auto path = dumpconf.response_header_file.data();
3748       auto f = open_file_for_write(path);
3749 
3750       if (f == nullptr) {
3751         LOG(FATAL) << "Failed to open http2 upstream response header file: "
3752                    << path;
3753         return -1;
3754       }
3755 
3756       dumpconf.response_header = f;
3757 
3758       if (config->uid != 0) {
3759         if (chown(path, config->uid, config->gid) == -1) {
3760           auto error = errno;
3761           LOG(WARN) << "Changing owner of http2 upstream response header file"
3762                     << " " << path << " failed: "
3763                     << xsi_strerror(error, errbuf.data(), errbuf.size());
3764         }
3765       }
3766     }
3767   }
3768 
3769   auto &tlsconf = config->tls;
3770 
3771   if (tlsconf.alpn_list.empty()) {
3772     tlsconf.alpn_list = util::split_str(DEFAULT_ALPN_LIST, ',');
3773   }
3774 
3775   if (!tlsconf.tls_proto_list.empty()) {
3776     tlsconf.tls_proto_mask = tls::create_tls_proto_mask(tlsconf.tls_proto_list);
3777   }
3778 
3779   // TODO We depends on the ordering of protocol version macro in
3780   // OpenSSL.
3781   if (tlsconf.min_proto_version > tlsconf.max_proto_version) {
3782     LOG(ERROR) << "tls-max-proto-version must be equal to or larger than "
3783                   "tls-min-proto-version";
3784     return -1;
3785   }
3786 
3787   if (tls::set_alpn_prefs(tlsconf.alpn_prefs, tlsconf.alpn_list) != 0) {
3788     return -1;
3789   }
3790 
3791   tlsconf.bio_method = create_bio_method();
3792 
3793   auto &listenerconf = config->conn.listener;
3794   auto &upstreamconf = config->conn.upstream;
3795 
3796   if (listenerconf.addrs.empty()) {
3797     UpstreamAddr addr{};
3798     addr.host = "*"_sr;
3799     addr.port = 3000;
3800     addr.tls = true;
3801     addr.family = AF_INET;
3802     addr.index = 0;
3803     listenerconf.addrs.push_back(addr);
3804     addr.family = AF_INET6;
3805     addr.index = 1;
3806     listenerconf.addrs.push_back(std::move(addr));
3807   }
3808 
3809   if (upstreamconf.worker_connections == 0) {
3810     upstreamconf.worker_connections = std::numeric_limits<size_t>::max();
3811   }
3812 
3813   if (tls::upstream_tls_enabled(config->conn) &&
3814       (tlsconf.private_key_file.empty() || tlsconf.cert_file.empty())) {
3815     LOG(FATAL) << "TLS private key and certificate files are required.  "
3816                   "Specify them in command-line, or in configuration file "
3817                   "using private-key-file and certificate-file options.";
3818     return -1;
3819   }
3820 
3821   if (tls::upstream_tls_enabled(config->conn) && !tlsconf.ocsp.disabled) {
3822     struct stat buf;
3823     if (stat(tlsconf.ocsp.fetch_ocsp_response_file.data(), &buf) != 0) {
3824       tlsconf.ocsp.disabled = true;
3825       LOG(WARN) << "--fetch-ocsp-response-file: "
3826                 << tlsconf.ocsp.fetch_ocsp_response_file
3827                 << " not found.  OCSP stapling has been disabled.";
3828     }
3829   }
3830 
3831   if (configure_downstream_group(config, config->http2_proxy, false, tlsconf) !=
3832       0) {
3833     return -1;
3834   }
3835 
3836   std::array<char, util::max_hostport> hostport_buf;
3837 
3838   auto &proxy = config->downstream_http_proxy;
3839   if (!proxy.host.empty()) {
3840     auto hostport = util::make_hostport(std::begin(hostport_buf),
3841                                         StringRef{proxy.host}, proxy.port);
3842     if (resolve_hostname(&proxy.addr, proxy.host.data(), proxy.port,
3843                          AF_UNSPEC) == -1) {
3844       LOG(FATAL) << "Resolving backend HTTP proxy address failed: " << hostport;
3845       return -1;
3846     }
3847     LOG(NOTICE) << "Backend HTTP proxy address: " << hostport << " -> "
3848                 << util::to_numeric_addr(&proxy.addr);
3849   }
3850 
3851   {
3852     auto &memcachedconf = tlsconf.session_cache.memcached;
3853     if (!memcachedconf.host.empty()) {
3854       auto hostport = util::make_hostport(std::begin(hostport_buf),
3855                                           StringRef{memcachedconf.host},
3856                                           memcachedconf.port);
3857       if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.data(),
3858                            memcachedconf.port, memcachedconf.family) == -1) {
3859         LOG(FATAL)
3860             << "Resolving memcached address for TLS session cache failed: "
3861             << hostport;
3862         return -1;
3863       }
3864       LOG(NOTICE) << "Memcached address for TLS session cache: " << hostport
3865                   << " -> " << util::to_numeric_addr(&memcachedconf.addr);
3866       if (memcachedconf.tls) {
3867         LOG(NOTICE) << "Connection to memcached for TLS session cache will be "
3868                        "encrypted by TLS";
3869       }
3870     }
3871   }
3872 
3873   {
3874     auto &memcachedconf = tlsconf.ticket.memcached;
3875     if (!memcachedconf.host.empty()) {
3876       auto hostport = util::make_hostport(std::begin(hostport_buf),
3877                                           StringRef{memcachedconf.host},
3878                                           memcachedconf.port);
3879       if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.data(),
3880                            memcachedconf.port, memcachedconf.family) == -1) {
3881         LOG(FATAL) << "Resolving memcached address for TLS ticket key failed: "
3882                    << hostport;
3883         return -1;
3884       }
3885       LOG(NOTICE) << "Memcached address for TLS ticket key: " << hostport
3886                   << " -> " << util::to_numeric_addr(&memcachedconf.addr);
3887       if (memcachedconf.tls) {
3888         LOG(NOTICE) << "Connection to memcached for TLS ticket key will be "
3889                        "encrypted by TLS";
3890       }
3891     }
3892   }
3893 
3894   if (config->rlimit_nofile) {
3895     struct rlimit lim = {static_cast<rlim_t>(config->rlimit_nofile),
3896                          static_cast<rlim_t>(config->rlimit_nofile)};
3897     if (setrlimit(RLIMIT_NOFILE, &lim) != 0) {
3898       auto error = errno;
3899       LOG(WARN) << "Setting rlimit-nofile failed: "
3900                 << xsi_strerror(error, errbuf.data(), errbuf.size());
3901     }
3902   }
3903 
3904 #ifdef RLIMIT_MEMLOCK
3905   if (config->rlimit_memlock) {
3906     struct rlimit lim = {static_cast<rlim_t>(config->rlimit_memlock),
3907                          static_cast<rlim_t>(config->rlimit_memlock)};
3908     if (setrlimit(RLIMIT_MEMLOCK, &lim) != 0) {
3909       auto error = errno;
3910       LOG(WARN) << "Setting rlimit-memlock failed: "
3911                 << xsi_strerror(error, errbuf.data(), errbuf.size());
3912     }
3913   }
3914 #endif // RLIMIT_MEMLOCK
3915 
3916   auto &fwdconf = config->http.forwarded;
3917 
3918   if (fwdconf.by_node_type == ForwardedNode::OBFUSCATED &&
3919       fwdconf.by_obfuscated.empty()) {
3920     // 2 for '_' and terminal NULL
3921     auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
3922     auto p = std::begin(iov);
3923     *p++ = '_';
3924     auto gen = util::make_mt19937();
3925     p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, gen);
3926     *p = '\0';
3927     fwdconf.by_obfuscated = StringRef{std::span{std::begin(iov), p}};
3928   }
3929 
3930   if (config->http2.upstream.debug.frame_debug) {
3931     // To make it sync to logging
3932     set_output(stderr);
3933     if (isatty(fileno(stdout))) {
3934       set_color_output(true);
3935     }
3936     reset_timer();
3937   }
3938 
3939   config->http2.upstream.callbacks = create_http2_upstream_callbacks();
3940   config->http2.downstream.callbacks = create_http2_downstream_callbacks();
3941 
3942   if (!config->http.altsvcs.empty()) {
3943     config->http.altsvc_header_value =
3944         http::create_altsvc_header_value(config->balloc, config->http.altsvcs);
3945   }
3946 
3947   if (!config->http.http2_altsvcs.empty()) {
3948     config->http.http2_altsvc_header_value = http::create_altsvc_header_value(
3949         config->balloc, config->http.http2_altsvcs);
3950   }
3951 
3952   return 0;
3953 }
3954 } // namespace
3955 
3956 namespace {
3957 // Closes file descriptor which are opened for listeners in config,
3958 // and are not inherited from |iaddrs|.
close_not_inherited_fd(Config * config,const std::vector<InheritedAddr> & iaddrs)3959 void close_not_inherited_fd(Config *config,
3960                             const std::vector<InheritedAddr> &iaddrs) {
3961   auto &listenerconf = config->conn.listener;
3962 
3963   for (auto &addr : listenerconf.addrs) {
3964     auto inherited = std::find_if(
3965         std::begin(iaddrs), std::end(iaddrs),
3966         [&addr](const InheritedAddr &iaddr) { return addr.fd == iaddr.fd; });
3967 
3968     if (inherited != std::end(iaddrs)) {
3969       continue;
3970     }
3971 
3972     close(addr.fd);
3973   }
3974 }
3975 } // namespace
3976 
3977 namespace {
reload_config()3978 void reload_config() {
3979   int rv;
3980 
3981   LOG(NOTICE) << "Reloading configuration";
3982 
3983   auto cur_config = mod_config();
3984   auto new_config = std::make_unique<Config>();
3985 
3986   fill_default_config(new_config.get());
3987 
3988   new_config->conf_path =
3989       make_string_ref(new_config->balloc, cur_config->conf_path);
3990   // daemon option is ignored here.
3991   new_config->daemon = cur_config->daemon;
3992   // loop is reused, and ev_loop_flags gets ignored
3993   new_config->ev_loop_flags = cur_config->ev_loop_flags;
3994   new_config->config_revision = cur_config->config_revision + 1;
3995 
3996   rv = process_options(new_config.get(), suconfig.cmdcfgs);
3997   if (rv != 0) {
3998     LOG(ERROR) << "Failed to process new configuration";
3999     return;
4000   }
4001 
4002   auto iaddrs = get_inherited_addr_from_config(new_config->balloc, cur_config);
4003 
4004   if (create_acceptor_socket(new_config.get(), iaddrs) != 0) {
4005     close_not_inherited_fd(new_config.get(), iaddrs);
4006     return;
4007   }
4008 
4009   // According to libev documentation, flags are ignored since we have
4010   // already created first default loop.
4011   auto loop = ev_default_loop(new_config->ev_loop_flags);
4012 
4013   int ipc_fd = 0;
4014 #ifdef ENABLE_HTTP3
4015   int quic_ipc_fd = 0;
4016 
4017   auto quic_lwps = collect_quic_lingering_worker_processes();
4018 
4019   std::vector<WorkerID> worker_ids;
4020 
4021   if (generate_worker_id(worker_ids, worker_process_seq, new_config.get()) !=
4022       0) {
4023     close_not_inherited_fd(new_config.get(), iaddrs);
4024     return;
4025   }
4026 #endif // ENABLE_HTTP3
4027 
4028   // fork_worker_process and forked child process assumes new
4029   // configuration can be obtained from get_config().
4030 
4031   auto old_config = replace_config(std::move(new_config));
4032 
4033   auto pid = fork_worker_process(ipc_fd
4034 #ifdef ENABLE_HTTP3
4035                                  ,
4036                                  quic_ipc_fd
4037 #endif // ENABLE_HTTP3
4038 
4039                                  ,
4040                                  iaddrs
4041 #ifdef ENABLE_HTTP3
4042                                  ,
4043                                  worker_ids, std::move(quic_lwps)
4044 #endif // ENABLE_HTTP3
4045   );
4046 
4047   if (pid == -1) {
4048     LOG(ERROR) << "Failed to process new configuration";
4049 
4050     new_config = replace_config(std::move(old_config));
4051     close_not_inherited_fd(new_config.get(), iaddrs);
4052 
4053     return;
4054   }
4055 
4056   close_unused_inherited_addr(iaddrs);
4057 
4058   worker_process_add(std::make_unique<WorkerProcess>(
4059       loop, pid, ipc_fd
4060 #ifdef ENABLE_HTTP3
4061       ,
4062       quic_ipc_fd, std::move(worker_ids), worker_process_seq++
4063 #endif // ENABLE_HTTP3
4064       ));
4065 
4066   worker_process_adjust_limit();
4067 
4068   if (!get_config()->pid_file.empty()) {
4069     save_pid();
4070   }
4071 }
4072 } // namespace
4073 
main(int argc,char ** argv)4074 int main(int argc, char **argv) {
4075   int rv;
4076   std::array<char, STRERROR_BUFSIZE> errbuf;
4077 
4078 #ifdef HAVE_LIBBPF
4079   libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
4080 #endif // HAVE_LIBBPF
4081 
4082   Log::set_severity_level(NOTICE);
4083   create_config();
4084   fill_default_config(mod_config());
4085 
4086   // make copy of stderr
4087   store_original_fds();
4088 
4089   // First open log files with default configuration, so that we can
4090   // log errors/warnings while reading configuration files.
4091   reopen_log_files(get_config()->logging);
4092 
4093   suconfig.original_argv = argv;
4094 
4095   // We have to copy argv, since getopt_long may change its content.
4096   suconfig.argc = argc;
4097   suconfig.argv = new char *[argc];
4098 
4099   for (int i = 0; i < argc; ++i) {
4100     suconfig.argv[i] = strdup(argv[i]);
4101     if (suconfig.argv[i] == nullptr) {
4102       auto error = errno;
4103       LOG(FATAL) << "failed to copy argv: "
4104                  << xsi_strerror(error, errbuf.data(), errbuf.size());
4105       exit(EXIT_FAILURE);
4106     }
4107   }
4108 
4109   suconfig.cwd = getcwd(nullptr, 0);
4110   if (suconfig.cwd == nullptr) {
4111     auto error = errno;
4112     LOG(FATAL) << "failed to get current working directory: errno=" << error;
4113     exit(EXIT_FAILURE);
4114   }
4115 
4116   auto &cmdcfgs = suconfig.cmdcfgs;
4117 
4118   while (1) {
4119     static int flag = 0;
4120     static constexpr option long_options[] = {
4121         {SHRPX_OPT_DAEMON.data(), no_argument, nullptr, 'D'},
4122         {SHRPX_OPT_LOG_LEVEL.data(), required_argument, nullptr, 'L'},
4123         {SHRPX_OPT_BACKEND.data(), required_argument, nullptr, 'b'},
4124         {SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS.data(), required_argument,
4125          nullptr, 'c'},
4126         {SHRPX_OPT_FRONTEND.data(), required_argument, nullptr, 'f'},
4127         {"help", no_argument, nullptr, 'h'},
4128         {SHRPX_OPT_INSECURE.data(), no_argument, nullptr, 'k'},
4129         {SHRPX_OPT_WORKERS.data(), required_argument, nullptr, 'n'},
4130         {SHRPX_OPT_CLIENT_PROXY.data(), no_argument, nullptr, 'p'},
4131         {SHRPX_OPT_HTTP2_PROXY.data(), no_argument, nullptr, 's'},
4132         {"version", no_argument, nullptr, 'v'},
4133         {SHRPX_OPT_FRONTEND_FRAME_DEBUG.data(), no_argument, nullptr, 'o'},
4134         {SHRPX_OPT_ADD_X_FORWARDED_FOR.data(), no_argument, &flag, 1},
4135         {SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT.data(), required_argument, &flag,
4136          2},
4137         {SHRPX_OPT_FRONTEND_READ_TIMEOUT.data(), required_argument, &flag, 3},
4138         {SHRPX_OPT_FRONTEND_WRITE_TIMEOUT.data(), required_argument, &flag, 4},
4139         {SHRPX_OPT_BACKEND_READ_TIMEOUT.data(), required_argument, &flag, 5},
4140         {SHRPX_OPT_BACKEND_WRITE_TIMEOUT.data(), required_argument, &flag, 6},
4141         {SHRPX_OPT_ACCESSLOG_FILE.data(), required_argument, &flag, 7},
4142         {SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT.data(), required_argument, &flag,
4143          8},
4144         {SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS.data(), required_argument, &flag,
4145          9},
4146         {SHRPX_OPT_PID_FILE.data(), required_argument, &flag, 10},
4147         {SHRPX_OPT_USER.data(), required_argument, &flag, 11},
4148         {"conf", required_argument, &flag, 12},
4149         {SHRPX_OPT_SYSLOG_FACILITY.data(), required_argument, &flag, 14},
4150         {SHRPX_OPT_BACKLOG.data(), required_argument, &flag, 15},
4151         {SHRPX_OPT_CIPHERS.data(), required_argument, &flag, 16},
4152         {SHRPX_OPT_CLIENT.data(), no_argument, &flag, 17},
4153         {SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS.data(), required_argument, &flag,
4154          18},
4155         {SHRPX_OPT_CACERT.data(), required_argument, &flag, 19},
4156         {SHRPX_OPT_BACKEND_IPV4.data(), no_argument, &flag, 20},
4157         {SHRPX_OPT_BACKEND_IPV6.data(), no_argument, &flag, 21},
4158         {SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE.data(), required_argument, &flag,
4159          22},
4160         {SHRPX_OPT_NO_VIA.data(), no_argument, &flag, 23},
4161         {SHRPX_OPT_SUBCERT.data(), required_argument, &flag, 24},
4162         {SHRPX_OPT_HTTP2_BRIDGE.data(), no_argument, &flag, 25},
4163         {SHRPX_OPT_BACKEND_HTTP_PROXY_URI.data(), required_argument, &flag, 26},
4164         {SHRPX_OPT_BACKEND_NO_TLS.data(), no_argument, &flag, 27},
4165         {SHRPX_OPT_OCSP_STARTUP.data(), no_argument, &flag, 28},
4166         {SHRPX_OPT_FRONTEND_NO_TLS.data(), no_argument, &flag, 29},
4167         {SHRPX_OPT_NO_VERIFY_OCSP.data(), no_argument, &flag, 30},
4168         {SHRPX_OPT_BACKEND_TLS_SNI_FIELD.data(), required_argument, &flag, 31},
4169         {SHRPX_OPT_DH_PARAM_FILE.data(), required_argument, &flag, 33},
4170         {SHRPX_OPT_READ_RATE.data(), required_argument, &flag, 34},
4171         {SHRPX_OPT_READ_BURST.data(), required_argument, &flag, 35},
4172         {SHRPX_OPT_WRITE_RATE.data(), required_argument, &flag, 36},
4173         {SHRPX_OPT_WRITE_BURST.data(), required_argument, &flag, 37},
4174         {SHRPX_OPT_NPN_LIST.data(), required_argument, &flag, 38},
4175         {SHRPX_OPT_VERIFY_CLIENT.data(), no_argument, &flag, 39},
4176         {SHRPX_OPT_VERIFY_CLIENT_CACERT.data(), required_argument, &flag, 40},
4177         {SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE.data(), required_argument, &flag,
4178          41},
4179         {SHRPX_OPT_CLIENT_CERT_FILE.data(), required_argument, &flag, 42},
4180         {SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER.data(), required_argument,
4181          &flag, 43},
4182         {SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER.data(),
4183          required_argument, &flag, 44},
4184         {SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING.data(), no_argument, &flag, 45},
4185         {SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS.data(),
4186          required_argument, &flag, 46},
4187         {SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS.data(),
4188          required_argument, &flag, 47},
4189         {SHRPX_OPT_TLS_PROTO_LIST.data(), required_argument, &flag, 48},
4190         {SHRPX_OPT_PADDING.data(), required_argument, &flag, 49},
4191         {SHRPX_OPT_WORKER_READ_RATE.data(), required_argument, &flag, 50},
4192         {SHRPX_OPT_WORKER_READ_BURST.data(), required_argument, &flag, 51},
4193         {SHRPX_OPT_WORKER_WRITE_RATE.data(), required_argument, &flag, 52},
4194         {SHRPX_OPT_WORKER_WRITE_BURST.data(), required_argument, &flag, 53},
4195         {SHRPX_OPT_ALTSVC.data(), required_argument, &flag, 54},
4196         {SHRPX_OPT_ADD_RESPONSE_HEADER.data(), required_argument, &flag, 55},
4197         {SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS.data(), required_argument, &flag,
4198          56},
4199         {SHRPX_OPT_ACCESSLOG_SYSLOG.data(), no_argument, &flag, 57},
4200         {SHRPX_OPT_ERRORLOG_FILE.data(), required_argument, &flag, 58},
4201         {SHRPX_OPT_ERRORLOG_SYSLOG.data(), no_argument, &flag, 59},
4202         {SHRPX_OPT_STREAM_READ_TIMEOUT.data(), required_argument, &flag, 60},
4203         {SHRPX_OPT_STREAM_WRITE_TIMEOUT.data(), required_argument, &flag, 61},
4204         {SHRPX_OPT_NO_LOCATION_REWRITE.data(), no_argument, &flag, 62},
4205         {SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST.data(), required_argument,
4206          &flag, 63},
4207         {SHRPX_OPT_LISTENER_DISABLE_TIMEOUT.data(), required_argument, &flag,
4208          64},
4209         {SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR.data(), no_argument, &flag,
4210          65},
4211         {SHRPX_OPT_ACCESSLOG_FORMAT.data(), required_argument, &flag, 66},
4212         {SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND.data(),
4213          required_argument, &flag, 67},
4214         {SHRPX_OPT_TLS_TICKET_KEY_FILE.data(), required_argument, &flag, 68},
4215         {SHRPX_OPT_RLIMIT_NOFILE.data(), required_argument, &flag, 69},
4216         {SHRPX_OPT_BACKEND_RESPONSE_BUFFER.data(), required_argument, &flag,
4217          71},
4218         {SHRPX_OPT_BACKEND_REQUEST_BUFFER.data(), required_argument, &flag, 72},
4219         {SHRPX_OPT_NO_HOST_REWRITE.data(), no_argument, &flag, 73},
4220         {SHRPX_OPT_NO_SERVER_PUSH.data(), no_argument, &flag, 74},
4221         {SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER.data(),
4222          required_argument, &flag, 76},
4223         {SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE.data(), required_argument, &flag,
4224          77},
4225         {SHRPX_OPT_OCSP_UPDATE_INTERVAL.data(), required_argument, &flag, 78},
4226         {SHRPX_OPT_NO_OCSP.data(), no_argument, &flag, 79},
4227         {SHRPX_OPT_HEADER_FIELD_BUFFER.data(), required_argument, &flag, 80},
4228         {SHRPX_OPT_MAX_HEADER_FIELDS.data(), required_argument, &flag, 81},
4229         {SHRPX_OPT_ADD_REQUEST_HEADER.data(), required_argument, &flag, 82},
4230         {SHRPX_OPT_INCLUDE.data(), required_argument, &flag, 83},
4231         {SHRPX_OPT_TLS_TICKET_KEY_CIPHER.data(), required_argument, &flag, 84},
4232         {SHRPX_OPT_HOST_REWRITE.data(), no_argument, &flag, 85},
4233         {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED.data(), required_argument, &flag,
4234          86},
4235         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED.data(), required_argument, &flag,
4236          87},
4237         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL.data(), required_argument,
4238          &flag, 88},
4239         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY.data(), required_argument,
4240          &flag, 89},
4241         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL.data(), required_argument,
4242          &flag, 90},
4243         {SHRPX_OPT_MRUBY_FILE.data(), required_argument, &flag, 91},
4244         {SHRPX_OPT_ACCEPT_PROXY_PROTOCOL.data(), no_argument, &flag, 93},
4245         {SHRPX_OPT_FASTOPEN.data(), required_argument, &flag, 94},
4246         {SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD.data(), required_argument,
4247          &flag, 95},
4248         {SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT.data(), required_argument, &flag,
4249          96},
4250         {SHRPX_OPT_ADD_FORWARDED.data(), required_argument, &flag, 97},
4251         {SHRPX_OPT_STRIP_INCOMING_FORWARDED.data(), no_argument, &flag, 98},
4252         {SHRPX_OPT_FORWARDED_BY.data(), required_argument, &flag, 99},
4253         {SHRPX_OPT_FORWARDED_FOR.data(), required_argument, &flag, 100},
4254         {SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER.data(), required_argument,
4255          &flag, 101},
4256         {SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS.data(), required_argument, &flag,
4257          102},
4258         {SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST.data(), no_argument, &flag, 103},
4259         {SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER.data(), required_argument, &flag,
4260          104},
4261         {SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS.data(), required_argument, &flag,
4262          105},
4263         {SHRPX_OPT_BACKEND_HTTP1_TLS.data(), no_argument, &flag, 106},
4264         {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS.data(), no_argument, &flag,
4265          108},
4266         {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE.data(),
4267          required_argument, &flag, 109},
4268         {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE.data(),
4269          required_argument, &flag, 110},
4270         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS.data(), no_argument, &flag,
4271          111},
4272         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE.data(), required_argument,
4273          &flag, 112},
4274         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE.data(),
4275          required_argument, &flag, 113},
4276         {SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY.data(),
4277          required_argument, &flag, 114},
4278         {SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY.data(),
4279          required_argument, &flag, 115},
4280         {SHRPX_OPT_BACKEND_ADDRESS_FAMILY.data(), required_argument, &flag,
4281          116},
4282         {SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS.data(),
4283          required_argument, &flag, 117},
4284         {SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS.data(),
4285          required_argument, &flag, 118},
4286         {SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND.data(), required_argument,
4287          &flag, 119},
4288         {SHRPX_OPT_BACKEND_TLS.data(), no_argument, &flag, 120},
4289         {SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST.data(), required_argument,
4290          &flag, 121},
4291         {SHRPX_OPT_ERROR_PAGE.data(), required_argument, &flag, 122},
4292         {SHRPX_OPT_NO_KQUEUE.data(), no_argument, &flag, 123},
4293         {SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT.data(), required_argument,
4294          &flag, 124},
4295         {SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT.data(), required_argument,
4296          &flag, 125},
4297         {SHRPX_OPT_API_MAX_REQUEST_BODY.data(), required_argument, &flag, 126},
4298         {SHRPX_OPT_BACKEND_MAX_BACKOFF.data(), required_argument, &flag, 127},
4299         {SHRPX_OPT_SERVER_NAME.data(), required_argument, &flag, 128},
4300         {SHRPX_OPT_NO_SERVER_REWRITE.data(), no_argument, &flag, 129},
4301         {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE.data(),
4302          no_argument, &flag, 130},
4303         {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE.data(), no_argument,
4304          &flag, 131},
4305         {SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE.data(), required_argument, &flag,
4306          132},
4307         {SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE.data(),
4308          required_argument, &flag, 133},
4309         {SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE.data(), required_argument, &flag,
4310          134},
4311         {SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE.data(),
4312          required_argument, &flag, 135},
4313         {SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.data(),
4314          required_argument, &flag, 136},
4315         {SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.data(),
4316          required_argument, &flag, 137},
4317         {SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE.data(),
4318          required_argument, &flag, 138},
4319         {SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE.data(),
4320          required_argument, &flag, 139},
4321         {SHRPX_OPT_ECDH_CURVES.data(), required_argument, &flag, 140},
4322         {SHRPX_OPT_TLS_SCT_DIR.data(), required_argument, &flag, 141},
4323         {SHRPX_OPT_BACKEND_CONNECT_TIMEOUT.data(), required_argument, &flag,
4324          142},
4325         {SHRPX_OPT_DNS_CACHE_TIMEOUT.data(), required_argument, &flag, 143},
4326         {SHRPX_OPT_DNS_LOOKUP_TIMEOUT.data(), required_argument, &flag, 144},
4327         {SHRPX_OPT_DNS_MAX_TRY.data(), required_argument, &flag, 145},
4328         {SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.data(), required_argument, &flag,
4329          146},
4330         {SHRPX_OPT_PSK_SECRETS.data(), required_argument, &flag, 147},
4331         {SHRPX_OPT_CLIENT_PSK_SECRETS.data(), required_argument, &flag, 148},
4332         {SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST.data(), no_argument, &flag,
4333          149},
4334         {SHRPX_OPT_CLIENT_CIPHERS.data(), required_argument, &flag, 150},
4335         {SHRPX_OPT_ACCESSLOG_WRITE_EARLY.data(), no_argument, &flag, 151},
4336         {SHRPX_OPT_TLS_MIN_PROTO_VERSION.data(), required_argument, &flag, 152},
4337         {SHRPX_OPT_TLS_MAX_PROTO_VERSION.data(), required_argument, &flag, 153},
4338         {SHRPX_OPT_REDIRECT_HTTPS_PORT.data(), required_argument, &flag, 154},
4339         {SHRPX_OPT_FRONTEND_MAX_REQUESTS.data(), required_argument, &flag, 155},
4340         {SHRPX_OPT_SINGLE_THREAD.data(), no_argument, &flag, 156},
4341         {SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO.data(), no_argument, &flag, 157},
4342         {SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO.data(), no_argument,
4343          &flag, 158},
4344         {SHRPX_OPT_SINGLE_PROCESS.data(), no_argument, &flag, 159},
4345         {SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED.data(), no_argument, &flag,
4346          160},
4347         {SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR.data(), no_argument, &flag,
4348          161},
4349         {SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA.data(), no_argument, &flag, 162},
4350         {SHRPX_OPT_TLS_MAX_EARLY_DATA.data(), required_argument, &flag, 163},
4351         {SHRPX_OPT_TLS13_CIPHERS.data(), required_argument, &flag, 164},
4352         {SHRPX_OPT_TLS13_CLIENT_CIPHERS.data(), required_argument, &flag, 165},
4353         {SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA.data(), no_argument, &flag,
4354          166},
4355         {SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST.data(), no_argument, &flag, 167},
4356         {SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST.data(), no_argument, &flag,
4357          168},
4358         {SHRPX_OPT_QUIC_BPF_PROGRAM_FILE.data(), required_argument, &flag, 169},
4359         {SHRPX_OPT_NO_QUIC_BPF.data(), no_argument, &flag, 170},
4360         {SHRPX_OPT_HTTP2_ALTSVC.data(), required_argument, &flag, 171},
4361         {SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT.data(), required_argument, &flag,
4362          172},
4363         {SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT.data(), required_argument, &flag,
4364          173},
4365         {SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG.data(), no_argument, &flag, 174},
4366         {SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE.data(), required_argument, &flag,
4367          175},
4368         {SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE.data(),
4369          required_argument, &flag, 176},
4370         {SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE.data(), required_argument,
4371          &flag, 177},
4372         {SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE.data(),
4373          required_argument, &flag, 178},
4374         {SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS.data(),
4375          required_argument, &flag, 179},
4376         {SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA.data(), no_argument, &flag, 180},
4377         {SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR.data(), required_argument, &flag,
4378          181},
4379         {SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN.data(), no_argument, &flag, 182},
4380         {SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER.data(),
4381          required_argument, &flag, 183},
4382         {SHRPX_OPT_QUIC_SERVER_ID.data(), required_argument, &flag, 185},
4383         {SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE.data(), required_argument, &flag,
4384          186},
4385         {SHRPX_OPT_RLIMIT_MEMLOCK.data(), required_argument, &flag, 187},
4386         {SHRPX_OPT_MAX_WORKER_PROCESSES.data(), required_argument, &flag, 188},
4387         {SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD.data(),
4388          required_argument, &flag, 189},
4389         {SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT.data(), required_argument, &flag,
4390          190},
4391         {SHRPX_OPT_REQUIRE_HTTP_SCHEME.data(), no_argument, &flag, 191},
4392         {SHRPX_OPT_TLS_KTLS.data(), no_argument, &flag, 192},
4393         {SHRPX_OPT_ALPN_LIST.data(), required_argument, &flag, 193},
4394         {SHRPX_OPT_FRONTEND_HEADER_TIMEOUT.data(), required_argument, &flag,
4395          194},
4396         {SHRPX_OPT_FRONTEND_HTTP2_IDLE_TIMEOUT.data(), required_argument, &flag,
4397          195},
4398         {SHRPX_OPT_FRONTEND_HTTP3_IDLE_TIMEOUT.data(), required_argument, &flag,
4399          196},
4400         {nullptr, 0, nullptr, 0}};
4401 
4402     int option_index = 0;
4403     int c = getopt_long(argc, argv, "DL:b:c:f:hkn:opsv", long_options,
4404                         &option_index);
4405     if (c == -1) {
4406       break;
4407     }
4408     switch (c) {
4409     case 'D':
4410       cmdcfgs.emplace_back(SHRPX_OPT_DAEMON, "yes"_sr);
4411       break;
4412     case 'L':
4413       cmdcfgs.emplace_back(SHRPX_OPT_LOG_LEVEL, StringRef{optarg});
4414       break;
4415     case 'b':
4416       cmdcfgs.emplace_back(SHRPX_OPT_BACKEND, StringRef{optarg});
4417       break;
4418     case 'c':
4419       cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS,
4420                            StringRef{optarg});
4421       break;
4422     case 'f':
4423       cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND, StringRef{optarg});
4424       break;
4425     case 'h':
4426       print_help(std::cout);
4427       exit(EXIT_SUCCESS);
4428     case 'k':
4429       cmdcfgs.emplace_back(SHRPX_OPT_INSECURE, "yes"_sr);
4430       break;
4431     case 'n':
4432       cmdcfgs.emplace_back(SHRPX_OPT_WORKERS, StringRef{optarg});
4433       break;
4434     case 'o':
4435       cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_FRAME_DEBUG, "yes"_sr);
4436       break;
4437     case 'p':
4438       cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PROXY, "yes"_sr);
4439       break;
4440     case 's':
4441       cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_PROXY, "yes"_sr);
4442       break;
4443     case 'v':
4444       print_version(std::cout);
4445       exit(EXIT_SUCCESS);
4446     case '?':
4447       util::show_candidates(argv[optind - 1], long_options);
4448       exit(EXIT_FAILURE);
4449     case 0:
4450       switch (flag) {
4451       case 1:
4452         // --add-x-forwarded-for
4453         cmdcfgs.emplace_back(SHRPX_OPT_ADD_X_FORWARDED_FOR, "yes"_sr);
4454         break;
4455       case 2:
4456         // --frontend-http2-read-timeout
4457         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT,
4458                              StringRef{optarg});
4459         break;
4460       case 3:
4461         // --frontend-read-timeout
4462         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_READ_TIMEOUT,
4463                              StringRef{optarg});
4464         break;
4465       case 4:
4466         // --frontend-write-timeout
4467         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_WRITE_TIMEOUT,
4468                              StringRef{optarg});
4469         break;
4470       case 5:
4471         // --backend-read-timeout
4472         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_READ_TIMEOUT, StringRef{optarg});
4473         break;
4474       case 6:
4475         // --backend-write-timeout
4476         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_WRITE_TIMEOUT,
4477                              StringRef{optarg});
4478         break;
4479       case 7:
4480         cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FILE, StringRef{optarg});
4481         break;
4482       case 8:
4483         // --backend-keep-alive-timeout
4484         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT,
4485                              StringRef{optarg});
4486         break;
4487       case 9:
4488         // --frontend-http2-window-bits
4489         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS,
4490                              StringRef{optarg});
4491         break;
4492       case 10:
4493         cmdcfgs.emplace_back(SHRPX_OPT_PID_FILE, StringRef{optarg});
4494         break;
4495       case 11:
4496         cmdcfgs.emplace_back(SHRPX_OPT_USER, StringRef{optarg});
4497         break;
4498       case 12:
4499         // --conf
4500         mod_config()->conf_path =
4501             make_string_ref(mod_config()->balloc, StringRef{optarg});
4502         break;
4503       case 14:
4504         // --syslog-facility
4505         cmdcfgs.emplace_back(SHRPX_OPT_SYSLOG_FACILITY, StringRef{optarg});
4506         break;
4507       case 15:
4508         // --backlog
4509         cmdcfgs.emplace_back(SHRPX_OPT_BACKLOG, StringRef{optarg});
4510         break;
4511       case 16:
4512         // --ciphers
4513         cmdcfgs.emplace_back(SHRPX_OPT_CIPHERS, StringRef{optarg});
4514         break;
4515       case 17:
4516         // --client
4517         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT, "yes"_sr);
4518         break;
4519       case 18:
4520         // --backend-http2-window-bits
4521         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS,
4522                              StringRef{optarg});
4523         break;
4524       case 19:
4525         // --cacert
4526         cmdcfgs.emplace_back(SHRPX_OPT_CACERT, StringRef{optarg});
4527         break;
4528       case 20:
4529         // --backend-ipv4
4530         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV4, "yes"_sr);
4531         break;
4532       case 21:
4533         // --backend-ipv6
4534         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_IPV6, "yes"_sr);
4535         break;
4536       case 22:
4537         // --private-key-passwd-file
4538         cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE,
4539                              StringRef{optarg});
4540         break;
4541       case 23:
4542         // --no-via
4543         cmdcfgs.emplace_back(SHRPX_OPT_NO_VIA, "yes"_sr);
4544         break;
4545       case 24:
4546         // --subcert
4547         cmdcfgs.emplace_back(SHRPX_OPT_SUBCERT, StringRef{optarg});
4548         break;
4549       case 25:
4550         // --http2-bridge
4551         cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_BRIDGE, "yes"_sr);
4552         break;
4553       case 26:
4554         // --backend-http-proxy-uri
4555         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP_PROXY_URI,
4556                              StringRef{optarg});
4557         break;
4558       case 27:
4559         // --backend-no-tls
4560         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_NO_TLS, "yes"_sr);
4561         break;
4562       case 28:
4563         // --ocsp-startup
4564         cmdcfgs.emplace_back(SHRPX_OPT_OCSP_STARTUP, "yes"_sr);
4565         break;
4566       case 29:
4567         // --frontend-no-tls
4568         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_NO_TLS, "yes"_sr);
4569         break;
4570       case 30:
4571         // --no-verify-ocsp
4572         cmdcfgs.emplace_back(SHRPX_OPT_NO_VERIFY_OCSP, "yes"_sr);
4573         break;
4574       case 31:
4575         // --backend-tls-sni-field
4576         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS_SNI_FIELD,
4577                              StringRef{optarg});
4578         break;
4579       case 33:
4580         // --dh-param-file
4581         cmdcfgs.emplace_back(SHRPX_OPT_DH_PARAM_FILE, StringRef{optarg});
4582         break;
4583       case 34:
4584         // --read-rate
4585         cmdcfgs.emplace_back(SHRPX_OPT_READ_RATE, StringRef{optarg});
4586         break;
4587       case 35:
4588         // --read-burst
4589         cmdcfgs.emplace_back(SHRPX_OPT_READ_BURST, StringRef{optarg});
4590         break;
4591       case 36:
4592         // --write-rate
4593         cmdcfgs.emplace_back(SHRPX_OPT_WRITE_RATE, StringRef{optarg});
4594         break;
4595       case 37:
4596         // --write-burst
4597         cmdcfgs.emplace_back(SHRPX_OPT_WRITE_BURST, StringRef{optarg});
4598         break;
4599       case 38:
4600         // --npn-list
4601         cmdcfgs.emplace_back(SHRPX_OPT_NPN_LIST, StringRef{optarg});
4602         break;
4603       case 39:
4604         // --verify-client
4605         cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT, "yes"_sr);
4606         break;
4607       case 40:
4608         // --verify-client-cacert
4609         cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_CACERT, StringRef{optarg});
4610         break;
4611       case 41:
4612         // --client-private-key-file
4613         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE,
4614                              StringRef{optarg});
4615         break;
4616       case 42:
4617         // --client-cert-file
4618         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CERT_FILE, StringRef{optarg});
4619         break;
4620       case 43:
4621         // --frontend-http2-dump-request-header
4622         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER,
4623                              StringRef{optarg});
4624         break;
4625       case 44:
4626         // --frontend-http2-dump-response-header
4627         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER,
4628                              StringRef{optarg});
4629         break;
4630       case 45:
4631         // --http2-no-cookie-crumbling
4632         cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING, "yes"_sr);
4633         break;
4634       case 46:
4635         // --frontend-http2-connection-window-bits
4636         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS,
4637                              StringRef{optarg});
4638         break;
4639       case 47:
4640         // --backend-http2-connection-window-bits
4641         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
4642                              StringRef{optarg});
4643         break;
4644       case 48:
4645         // --tls-proto-list
4646         cmdcfgs.emplace_back(SHRPX_OPT_TLS_PROTO_LIST, StringRef{optarg});
4647         break;
4648       case 49:
4649         // --padding
4650         cmdcfgs.emplace_back(SHRPX_OPT_PADDING, StringRef{optarg});
4651         break;
4652       case 50:
4653         // --worker-read-rate
4654         cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_RATE, StringRef{optarg});
4655         break;
4656       case 51:
4657         // --worker-read-burst
4658         cmdcfgs.emplace_back(SHRPX_OPT_WORKER_READ_BURST, StringRef{optarg});
4659         break;
4660       case 52:
4661         // --worker-write-rate
4662         cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_RATE, StringRef{optarg});
4663         break;
4664       case 53:
4665         // --worker-write-burst
4666         cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, StringRef{optarg});
4667         break;
4668       case 54:
4669         // --altsvc
4670         cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, StringRef{optarg});
4671         break;
4672       case 55:
4673         // --add-response-header
4674         cmdcfgs.emplace_back(SHRPX_OPT_ADD_RESPONSE_HEADER, StringRef{optarg});
4675         break;
4676       case 56:
4677         // --worker-frontend-connections
4678         cmdcfgs.emplace_back(SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS,
4679                              StringRef{optarg});
4680         break;
4681       case 57:
4682         // --accesslog-syslog
4683         cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_SYSLOG, "yes"_sr);
4684         break;
4685       case 58:
4686         // --errorlog-file
4687         cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_FILE, StringRef{optarg});
4688         break;
4689       case 59:
4690         // --errorlog-syslog
4691         cmdcfgs.emplace_back(SHRPX_OPT_ERRORLOG_SYSLOG, "yes"_sr);
4692         break;
4693       case 60:
4694         // --stream-read-timeout
4695         cmdcfgs.emplace_back(SHRPX_OPT_STREAM_READ_TIMEOUT, StringRef{optarg});
4696         break;
4697       case 61:
4698         // --stream-write-timeout
4699         cmdcfgs.emplace_back(SHRPX_OPT_STREAM_WRITE_TIMEOUT, StringRef{optarg});
4700         break;
4701       case 62:
4702         // --no-location-rewrite
4703         cmdcfgs.emplace_back(SHRPX_OPT_NO_LOCATION_REWRITE, "yes"_sr);
4704         break;
4705       case 63:
4706         // --backend-http1-connections-per-host
4707         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
4708                              StringRef{optarg});
4709         break;
4710       case 64:
4711         // --listener-disable-timeout
4712         cmdcfgs.emplace_back(SHRPX_OPT_LISTENER_DISABLE_TIMEOUT,
4713                              StringRef{optarg});
4714         break;
4715       case 65:
4716         // --strip-incoming-x-forwarded-for
4717         cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR,
4718                              "yes"_sr);
4719         break;
4720       case 66:
4721         // --accesslog-format
4722         cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_FORMAT, StringRef{optarg});
4723         break;
4724       case 67:
4725         // --backend-http1-connections-per-frontend
4726         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
4727                              StringRef{optarg});
4728         break;
4729       case 68:
4730         // --tls-ticket-key-file
4731         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_FILE, StringRef{optarg});
4732         break;
4733       case 69:
4734         // --rlimit-nofile
4735         cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_NOFILE, StringRef{optarg});
4736         break;
4737       case 71:
4738         // --backend-response-buffer
4739         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_RESPONSE_BUFFER,
4740                              StringRef{optarg});
4741         break;
4742       case 72:
4743         // --backend-request-buffer
4744         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_REQUEST_BUFFER,
4745                              StringRef{optarg});
4746         break;
4747       case 73:
4748         // --no-host-rewrite
4749         cmdcfgs.emplace_back(SHRPX_OPT_NO_HOST_REWRITE, "yes"_sr);
4750         break;
4751       case 74:
4752         // --no-server-push
4753         cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_PUSH, "yes"_sr);
4754         break;
4755       case 76:
4756         // --backend-http2-connections-per-worker
4757         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
4758                              StringRef{optarg});
4759         break;
4760       case 77:
4761         // --fetch-ocsp-response-file
4762         cmdcfgs.emplace_back(SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE,
4763                              StringRef{optarg});
4764         break;
4765       case 78:
4766         // --ocsp-update-interval
4767         cmdcfgs.emplace_back(SHRPX_OPT_OCSP_UPDATE_INTERVAL, StringRef{optarg});
4768         break;
4769       case 79:
4770         // --no-ocsp
4771         cmdcfgs.emplace_back(SHRPX_OPT_NO_OCSP, "yes"_sr);
4772         break;
4773       case 80:
4774         // --header-field-buffer
4775         cmdcfgs.emplace_back(SHRPX_OPT_HEADER_FIELD_BUFFER, StringRef{optarg});
4776         break;
4777       case 81:
4778         // --max-header-fields
4779         cmdcfgs.emplace_back(SHRPX_OPT_MAX_HEADER_FIELDS, StringRef{optarg});
4780         break;
4781       case 82:
4782         // --add-request-header
4783         cmdcfgs.emplace_back(SHRPX_OPT_ADD_REQUEST_HEADER, StringRef{optarg});
4784         break;
4785       case 83:
4786         // --include
4787         cmdcfgs.emplace_back(SHRPX_OPT_INCLUDE, StringRef{optarg});
4788         break;
4789       case 84:
4790         // --tls-ticket-key-cipher
4791         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_CIPHER,
4792                              StringRef{optarg});
4793         break;
4794       case 85:
4795         // --host-rewrite
4796         cmdcfgs.emplace_back(SHRPX_OPT_HOST_REWRITE, "yes"_sr);
4797         break;
4798       case 86:
4799         // --tls-session-cache-memcached
4800         cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED,
4801                              StringRef{optarg});
4802         break;
4803       case 87:
4804         // --tls-ticket-key-memcached
4805         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED,
4806                              StringRef{optarg});
4807         break;
4808       case 88:
4809         // --tls-ticket-key-memcached-interval
4810         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_INTERVAL,
4811                              StringRef{optarg});
4812         break;
4813       case 89:
4814         // --tls-ticket-key-memcached-max-retry
4815         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
4816                              StringRef{optarg});
4817         break;
4818       case 90:
4819         // --tls-ticket-key-memcached-max-fail
4820         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
4821                              StringRef{optarg});
4822         break;
4823       case 91:
4824         // --mruby-file
4825         cmdcfgs.emplace_back(SHRPX_OPT_MRUBY_FILE, StringRef{optarg});
4826         break;
4827       case 93:
4828         // --accept-proxy-protocol
4829         cmdcfgs.emplace_back(SHRPX_OPT_ACCEPT_PROXY_PROTOCOL, "yes"_sr);
4830         break;
4831       case 94:
4832         // --fastopen
4833         cmdcfgs.emplace_back(SHRPX_OPT_FASTOPEN, StringRef{optarg});
4834         break;
4835       case 95:
4836         // --tls-dyn-rec-warmup-threshold
4837         cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_WARMUP_THRESHOLD,
4838                              StringRef{optarg});
4839         break;
4840       case 96:
4841         // --tls-dyn-rec-idle-timeout
4842         cmdcfgs.emplace_back(SHRPX_OPT_TLS_DYN_REC_IDLE_TIMEOUT,
4843                              StringRef{optarg});
4844         break;
4845       case 97:
4846         // --add-forwarded
4847         cmdcfgs.emplace_back(SHRPX_OPT_ADD_FORWARDED, StringRef{optarg});
4848         break;
4849       case 98:
4850         // --strip-incoming-forwarded
4851         cmdcfgs.emplace_back(SHRPX_OPT_STRIP_INCOMING_FORWARDED, "yes"_sr);
4852         break;
4853       case 99:
4854         // --forwarded-by
4855         cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_BY, StringRef{optarg});
4856         break;
4857       case 100:
4858         // --forwarded-for
4859         cmdcfgs.emplace_back(SHRPX_OPT_FORWARDED_FOR, StringRef{optarg});
4860         break;
4861       case 101:
4862         // --response-header-field-buffer
4863         cmdcfgs.emplace_back(SHRPX_OPT_RESPONSE_HEADER_FIELD_BUFFER,
4864                              StringRef{optarg});
4865         break;
4866       case 102:
4867         // --max-response-header-fields
4868         cmdcfgs.emplace_back(SHRPX_OPT_MAX_RESPONSE_HEADER_FIELDS,
4869                              StringRef{optarg});
4870         break;
4871       case 103:
4872         // --no-http2-cipher-black-list
4873         cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLACK_LIST, "yes"_sr);
4874         break;
4875       case 104:
4876         // --request-header-field-buffer
4877         cmdcfgs.emplace_back(SHRPX_OPT_REQUEST_HEADER_FIELD_BUFFER,
4878                              StringRef{optarg});
4879         break;
4880       case 105:
4881         // --max-request-header-fields
4882         cmdcfgs.emplace_back(SHRPX_OPT_MAX_REQUEST_HEADER_FIELDS,
4883                              StringRef{optarg});
4884         break;
4885       case 106:
4886         // --backend-http1-tls
4887         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP1_TLS, "yes"_sr);
4888         break;
4889       case 108:
4890         // --tls-session-cache-memcached-tls
4891         cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_TLS,
4892                              "yes"_sr);
4893         break;
4894       case 109:
4895         // --tls-session-cache-memcached-cert-file
4896         cmdcfgs.emplace_back(SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
4897                              StringRef{optarg});
4898         break;
4899       case 110:
4900         // --tls-session-cache-memcached-private-key-file
4901         cmdcfgs.emplace_back(
4902             SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
4903             StringRef{optarg});
4904         break;
4905       case 111:
4906         // --tls-ticket-key-memcached-tls
4907         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_TLS, "yes"_sr);
4908         break;
4909       case 112:
4910         // --tls-ticket-key-memcached-cert-file
4911         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
4912                              StringRef{optarg});
4913         break;
4914       case 113:
4915         // --tls-ticket-key-memcached-private-key-file
4916         cmdcfgs.emplace_back(
4917             SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
4918             StringRef{optarg});
4919         break;
4920       case 114:
4921         // --tls-ticket-key-memcached-address-family
4922         cmdcfgs.emplace_back(SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
4923                              StringRef{optarg});
4924         break;
4925       case 115:
4926         // --tls-session-cache-memcached-address-family
4927         cmdcfgs.emplace_back(
4928             SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
4929             StringRef{optarg});
4930         break;
4931       case 116:
4932         // --backend-address-family
4933         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_ADDRESS_FAMILY,
4934                              StringRef{optarg});
4935         break;
4936       case 117:
4937         // --frontend-http2-max-concurrent-streams
4938         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS,
4939                              StringRef{optarg});
4940         break;
4941       case 118:
4942         // --backend-http2-max-concurrent-streams
4943         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS,
4944                              StringRef{optarg});
4945         break;
4946       case 119:
4947         // --backend-connections-per-frontend
4948         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND,
4949                              StringRef{optarg});
4950         break;
4951       case 120:
4952         // --backend-tls
4953         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_TLS, "yes"_sr);
4954         break;
4955       case 121:
4956         // --backend-connections-per-host
4957         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECTIONS_PER_HOST,
4958                              StringRef{optarg});
4959         break;
4960       case 122:
4961         // --error-page
4962         cmdcfgs.emplace_back(SHRPX_OPT_ERROR_PAGE, StringRef{optarg});
4963         break;
4964       case 123:
4965         // --no-kqueue
4966         cmdcfgs.emplace_back(SHRPX_OPT_NO_KQUEUE, "yes"_sr);
4967         break;
4968       case 124:
4969         // --frontend-http2-settings-timeout
4970         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
4971                              StringRef{optarg});
4972         break;
4973       case 125:
4974         // --backend-http2-settings-timeout
4975         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_SETTINGS_TIMEOUT,
4976                              StringRef{optarg});
4977         break;
4978       case 126:
4979         // --api-max-request-body
4980         cmdcfgs.emplace_back(SHRPX_OPT_API_MAX_REQUEST_BODY, StringRef{optarg});
4981         break;
4982       case 127:
4983         // --backend-max-backoff
4984         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_MAX_BACKOFF, StringRef{optarg});
4985         break;
4986       case 128:
4987         // --server-name
4988         cmdcfgs.emplace_back(SHRPX_OPT_SERVER_NAME, StringRef{optarg});
4989         break;
4990       case 129:
4991         // --no-server-rewrite
4992         cmdcfgs.emplace_back(SHRPX_OPT_NO_SERVER_REWRITE, "yes"_sr);
4993         break;
4994       case 130:
4995         // --frontend-http2-optimize-write-buffer-size
4996         cmdcfgs.emplace_back(
4997             SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE, "yes"_sr);
4998         break;
4999       case 131:
5000         // --frontend-http2-optimize-window-size
5001         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE,
5002                              "yes"_sr);
5003         break;
5004       case 132:
5005         // --frontend-http2-window-size
5006         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE,
5007                              StringRef{optarg});
5008         break;
5009       case 133:
5010         // --frontend-http2-connection-window-size
5011         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE,
5012                              StringRef{optarg});
5013         break;
5014       case 134:
5015         // --backend-http2-window-size
5016         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE,
5017                              StringRef{optarg});
5018         break;
5019       case 135:
5020         // --backend-http2-connection-window-size
5021         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE,
5022                              StringRef{optarg});
5023         break;
5024       case 136:
5025         // --frontend-http2-encoder-dynamic-table-size
5026         cmdcfgs.emplace_back(
5027             SHRPX_OPT_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
5028             StringRef{optarg});
5029         break;
5030       case 137:
5031         // --frontend-http2-decoder-dynamic-table-size
5032         cmdcfgs.emplace_back(
5033             SHRPX_OPT_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
5034             StringRef{optarg});
5035         break;
5036       case 138:
5037         // --backend-http2-encoder-dynamic-table-size
5038         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE,
5039                              StringRef{optarg});
5040         break;
5041       case 139:
5042         // --backend-http2-decoder-dynamic-table-size
5043         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE,
5044                              StringRef{optarg});
5045         break;
5046       case 140:
5047         // --ecdh-curves
5048         cmdcfgs.emplace_back(SHRPX_OPT_ECDH_CURVES, StringRef{optarg});
5049         break;
5050       case 141:
5051         // --tls-sct-dir
5052         cmdcfgs.emplace_back(SHRPX_OPT_TLS_SCT_DIR, StringRef{optarg});
5053         break;
5054       case 142:
5055         // --backend-connect-timeout
5056         cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_CONNECT_TIMEOUT,
5057                              StringRef{optarg});
5058         break;
5059       case 143:
5060         // --dns-cache-timeout
5061         cmdcfgs.emplace_back(SHRPX_OPT_DNS_CACHE_TIMEOUT, StringRef{optarg});
5062         break;
5063       case 144:
5064         // --dns-lookup-timeou
5065         cmdcfgs.emplace_back(SHRPX_OPT_DNS_LOOKUP_TIMEOUT, StringRef{optarg});
5066         break;
5067       case 145:
5068         // --dns-max-try
5069         cmdcfgs.emplace_back(SHRPX_OPT_DNS_MAX_TRY, StringRef{optarg});
5070         break;
5071       case 146:
5072         // --frontend-keep-alive-timeout
5073         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT,
5074                              StringRef{optarg});
5075         break;
5076       case 147:
5077         // --psk-secrets
5078         cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
5079         break;
5080       case 148:
5081         // --client-psk-secrets
5082         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
5083         break;
5084       case 149:
5085         // --client-no-http2-cipher-black-list
5086         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST,
5087                              "yes"_sr);
5088         break;
5089       case 150:
5090         // --client-ciphers
5091         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_CIPHERS, StringRef{optarg});
5092         break;
5093       case 151:
5094         // --accesslog-write-early
5095         cmdcfgs.emplace_back(SHRPX_OPT_ACCESSLOG_WRITE_EARLY, "yes"_sr);
5096         break;
5097       case 152:
5098         // --tls-min-proto-version
5099         cmdcfgs.emplace_back(SHRPX_OPT_TLS_MIN_PROTO_VERSION,
5100                              StringRef{optarg});
5101         break;
5102       case 153:
5103         // --tls-max-proto-version
5104         cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_PROTO_VERSION,
5105                              StringRef{optarg});
5106         break;
5107       case 154:
5108         // --redirect-https-port
5109         cmdcfgs.emplace_back(SHRPX_OPT_REDIRECT_HTTPS_PORT, StringRef{optarg});
5110         break;
5111       case 155:
5112         // --frontend-max-requests
5113         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_MAX_REQUESTS,
5114                              StringRef{optarg});
5115         break;
5116       case 156:
5117         // --single-thread
5118         cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_THREAD, "yes"_sr);
5119         break;
5120       case 157:
5121         // --no-add-x-forwarded-proto
5122         cmdcfgs.emplace_back(SHRPX_OPT_NO_ADD_X_FORWARDED_PROTO, "yes"_sr);
5123         break;
5124       case 158:
5125         // --no-strip-incoming-x-forwarded-proto
5126         cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_X_FORWARDED_PROTO,
5127                              "yes"_sr);
5128         break;
5129       case 159:
5130         // --single-process
5131         cmdcfgs.emplace_back(SHRPX_OPT_SINGLE_PROCESS, "yes"_sr);
5132         break;
5133       case 160:
5134         // --verify-client-tolerate-expired
5135         cmdcfgs.emplace_back(SHRPX_OPT_VERIFY_CLIENT_TOLERATE_EXPIRED,
5136                              "yes"_sr);
5137         break;
5138       case 161:
5139         // --ignore-per-pattern-mruby-error
5140         cmdcfgs.emplace_back(SHRPX_OPT_IGNORE_PER_PATTERN_MRUBY_ERROR,
5141                              "yes"_sr);
5142         break;
5143       case 162:
5144         // --tls-no-postpone-early-data
5145         cmdcfgs.emplace_back(SHRPX_OPT_TLS_NO_POSTPONE_EARLY_DATA, "yes"_sr);
5146         break;
5147       case 163:
5148         // --tls-max-early-data
5149         cmdcfgs.emplace_back(SHRPX_OPT_TLS_MAX_EARLY_DATA, StringRef{optarg});
5150         break;
5151       case 164:
5152         // --tls13-ciphers
5153         cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CIPHERS, StringRef{optarg});
5154         break;
5155       case 165:
5156         // --tls13-client-ciphers
5157         cmdcfgs.emplace_back(SHRPX_OPT_TLS13_CLIENT_CIPHERS, StringRef{optarg});
5158         break;
5159       case 166:
5160         // --no-strip-incoming-early-data
5161         cmdcfgs.emplace_back(SHRPX_OPT_NO_STRIP_INCOMING_EARLY_DATA, "yes"_sr);
5162         break;
5163       case 167:
5164         // --no-http2-cipher-block-list
5165         cmdcfgs.emplace_back(SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST, "yes"_sr);
5166         break;
5167       case 168:
5168         // --client-no-http2-cipher-block-list
5169         cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST,
5170                              "yes"_sr);
5171         break;
5172       case 169:
5173         // --quic-bpf-program-file
5174         cmdcfgs.emplace_back(SHRPX_OPT_QUIC_BPF_PROGRAM_FILE,
5175                              StringRef{optarg});
5176         break;
5177       case 170:
5178         // --no-quic-bpf
5179         cmdcfgs.emplace_back(SHRPX_OPT_NO_QUIC_BPF, "yes"_sr);
5180         break;
5181       case 171:
5182         // --http2-altsvc
5183         cmdcfgs.emplace_back(SHRPX_OPT_HTTP2_ALTSVC, StringRef{optarg});
5184         break;
5185       case 172:
5186         // --frontend-http3-read-timeout
5187         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_READ_TIMEOUT,
5188                              StringRef{optarg});
5189         break;
5190       case 173:
5191         // --frontend-quic-idle-timeout
5192         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_IDLE_TIMEOUT,
5193                              StringRef{optarg});
5194         break;
5195       case 174:
5196         // --frontend-quic-debug-log
5197         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_DEBUG_LOG, "yes"_sr);
5198         break;
5199       case 175:
5200         // --frontend-http3-window-size
5201         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_WINDOW_SIZE,
5202                              StringRef{optarg});
5203         break;
5204       case 176:
5205         // --frontend-http3-connection-window-size
5206         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE,
5207                              StringRef{optarg});
5208         break;
5209       case 177:
5210         // --frontend-http3-max-window-size
5211         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_MAX_WINDOW_SIZE,
5212                              StringRef{optarg});
5213         break;
5214       case 178:
5215         // --frontend-http3-max-connection-window-size
5216         cmdcfgs.emplace_back(
5217             SHRPX_OPT_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE,
5218             StringRef{optarg});
5219         break;
5220       case 179:
5221         // --frontend-http3-max-concurrent-streams
5222         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS,
5223                              StringRef{optarg});
5224         break;
5225       case 180:
5226         // --frontend-quic-early-data
5227         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_EARLY_DATA, "yes"_sr);
5228         break;
5229       case 181:
5230         // --frontend-quic-qlog-dir
5231         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_QLOG_DIR,
5232                              StringRef{optarg});
5233         break;
5234       case 182:
5235         // --frontend-quic-require-token
5236         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_REQUIRE_TOKEN, "yes"_sr);
5237         break;
5238       case 183:
5239         // --frontend-quic-congestion-controller
5240         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_CONGESTION_CONTROLLER,
5241                              StringRef{optarg});
5242         break;
5243       case 185:
5244         // --quic-server-id
5245         cmdcfgs.emplace_back(SHRPX_OPT_QUIC_SERVER_ID, StringRef{optarg});
5246         break;
5247       case 186:
5248         // --frontend-quic-secret-file
5249         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_SECRET_FILE,
5250                              StringRef{optarg});
5251         break;
5252       case 187:
5253         // --rlimit-memlock
5254         cmdcfgs.emplace_back(SHRPX_OPT_RLIMIT_MEMLOCK, StringRef{optarg});
5255         break;
5256       case 188:
5257         // --max-worker-processes
5258         cmdcfgs.emplace_back(SHRPX_OPT_MAX_WORKER_PROCESSES, StringRef{optarg});
5259         break;
5260       case 189:
5261         // --worker-process-grace-shutdown-period
5262         cmdcfgs.emplace_back(SHRPX_OPT_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD,
5263                              StringRef{optarg});
5264         break;
5265       case 190:
5266         // --frontend-quic-initial-rtt
5267         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_QUIC_INITIAL_RTT,
5268                              StringRef{optarg});
5269         break;
5270       case 191:
5271         // --require-http-scheme
5272         cmdcfgs.emplace_back(SHRPX_OPT_REQUIRE_HTTP_SCHEME, "yes"_sr);
5273         break;
5274       case 192:
5275         // --tls-ktls
5276         cmdcfgs.emplace_back(SHRPX_OPT_TLS_KTLS, "yes"_sr);
5277         break;
5278       case 193:
5279         // --alpn-list
5280         cmdcfgs.emplace_back(SHRPX_OPT_ALPN_LIST, StringRef{optarg});
5281         break;
5282       case 194:
5283         // --frontend-header-timeout
5284         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HEADER_TIMEOUT,
5285                              StringRef{optarg});
5286         break;
5287       case 195:
5288         // --frontend-http2-idle-timeout
5289         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_IDLE_TIMEOUT,
5290                              StringRef{optarg});
5291         break;
5292       case 196:
5293         // --frontend-http3-idle-timeout
5294         cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP3_IDLE_TIMEOUT,
5295                              StringRef{optarg});
5296         break;
5297       default:
5298         break;
5299       }
5300       break;
5301     default:
5302       break;
5303     }
5304   }
5305 
5306   if (argc - optind >= 2) {
5307     cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, StringRef{argv[optind++]});
5308     cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, StringRef{argv[optind++]});
5309   }
5310 
5311   rv = process_options(mod_config(), cmdcfgs);
5312   if (rv != 0) {
5313     return -1;
5314   }
5315 
5316   if (event_loop() != 0) {
5317     return -1;
5318   }
5319 
5320   LOG(NOTICE) << "Shutdown momentarily";
5321 
5322   delete_log_config();
5323 
5324   return 0;
5325 }
5326 
5327 } // namespace shrpx
5328 
main(int argc,char ** argv)5329 int main(int argc, char **argv) { return run_app(shrpx::main, argc, argv); }
5330