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