2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
25 #include "shrpx_config.h"
32 #endif // HAVE_NETDB_H
35 #endif // HAVE_SYSLOG_H
36 #include <sys/types.h>
40 #endif // HAVE_FCNTL_H
43 #endif // HAVE_UNISTD_H
50 #include <nghttp2/nghttp2.h>
52 #include "http-parser/http_parser.h"
54 #include "shrpx_log.h"
55 #include "shrpx_ssl.h"
56 #include "shrpx_http.h"
61 using namespace nghttp2;
65 const char SHRPX_OPT_PRIVATE_KEY_FILE[] = "private-key-file";
66 const char SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE[] = "private-key-passwd-file";
67 const char SHRPX_OPT_CERTIFICATE_FILE[] = "certificate-file";
68 const char SHRPX_OPT_DH_PARAM_FILE[] = "dh-param-file";
69 const char SHRPX_OPT_SUBCERT[] = "subcert";
71 const char SHRPX_OPT_BACKEND[] = "backend";
72 const char SHRPX_OPT_FRONTEND[] = "frontend";
73 const char SHRPX_OPT_WORKERS[] = "workers";
74 const char SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS[] =
75 "http2-max-concurrent-streams";
76 const char SHRPX_OPT_LOG_LEVEL[] = "log-level";
77 const char SHRPX_OPT_DAEMON[] = "daemon";
78 const char SHRPX_OPT_HTTP2_PROXY[] = "http2-proxy";
79 const char SHRPX_OPT_HTTP2_BRIDGE[] = "http2-bridge";
80 const char SHRPX_OPT_CLIENT_PROXY[] = "client-proxy";
81 const char SHRPX_OPT_ADD_X_FORWARDED_FOR[] = "add-x-forwarded-for";
82 const char SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR[] =
83 "strip-incoming-x-forwarded-for";
84 const char SHRPX_OPT_NO_VIA[] = "no-via";
85 const char SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT[] =
86 "frontend-http2-read-timeout";
87 const char SHRPX_OPT_FRONTEND_READ_TIMEOUT[] = "frontend-read-timeout";
88 const char SHRPX_OPT_FRONTEND_WRITE_TIMEOUT[] = "frontend-write-timeout";
89 const char SHRPX_OPT_BACKEND_READ_TIMEOUT[] = "backend-read-timeout";
90 const char SHRPX_OPT_BACKEND_WRITE_TIMEOUT[] = "backend-write-timeout";
91 const char SHRPX_OPT_STREAM_READ_TIMEOUT[] = "stream-read-timeout";
92 const char SHRPX_OPT_STREAM_WRITE_TIMEOUT[] = "stream-write-timeout";
93 const char SHRPX_OPT_ACCESSLOG_FILE[] = "accesslog-file";
94 const char SHRPX_OPT_ACCESSLOG_SYSLOG[] = "accesslog-syslog";
95 const char SHRPX_OPT_ACCESSLOG_FORMAT[] = "accesslog-format";
96 const char SHRPX_OPT_ERRORLOG_FILE[] = "errorlog-file";
97 const char SHRPX_OPT_ERRORLOG_SYSLOG[] = "errorlog-syslog";
98 const char SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT[] =
99 "backend-keep-alive-timeout";
100 const char SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS[] =
101 "frontend-http2-window-bits";
102 const char SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS[] = "backend-http2-window-bits";
103 const char SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS[] =
104 "frontend-http2-connection-window-bits";
105 const char SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS[] =
106 "backend-http2-connection-window-bits";
107 const char SHRPX_OPT_FRONTEND_NO_TLS[] = "frontend-no-tls";
108 const char SHRPX_OPT_BACKEND_NO_TLS[] = "backend-no-tls";
109 const char SHRPX_OPT_BACKEND_TLS_SNI_FIELD[] = "backend-tls-sni-field";
110 const char SHRPX_OPT_PID_FILE[] = "pid-file";
111 const char SHRPX_OPT_USER[] = "user";
112 const char SHRPX_OPT_SYSLOG_FACILITY[] = "syslog-facility";
113 const char SHRPX_OPT_BACKLOG[] = "backlog";
114 const char SHRPX_OPT_CIPHERS[] = "ciphers";
115 const char SHRPX_OPT_CLIENT[] = "client";
116 const char SHRPX_OPT_INSECURE[] = "insecure";
117 const char SHRPX_OPT_CACERT[] = "cacert";
118 const char SHRPX_OPT_BACKEND_IPV4[] = "backend-ipv4";
119 const char SHRPX_OPT_BACKEND_IPV6[] = "backend-ipv6";
120 const char SHRPX_OPT_BACKEND_HTTP_PROXY_URI[] = "backend-http-proxy-uri";
121 const char SHRPX_OPT_READ_RATE[] = "read-rate";
122 const char SHRPX_OPT_READ_BURST[] = "read-burst";
123 const char SHRPX_OPT_WRITE_RATE[] = "write-rate";
124 const char SHRPX_OPT_WRITE_BURST[] = "write-burst";
125 const char SHRPX_OPT_WORKER_READ_RATE[] = "worker-read-rate";
126 const char SHRPX_OPT_WORKER_READ_BURST[] = "worker-read-burst";
127 const char SHRPX_OPT_WORKER_WRITE_RATE[] = "worker-write-rate";
128 const char SHRPX_OPT_WORKER_WRITE_BURST[] = "worker-write-burst";
129 const char SHRPX_OPT_NPN_LIST[] = "npn-list";
130 const char SHRPX_OPT_TLS_PROTO_LIST[] = "tls-proto-list";
131 const char SHRPX_OPT_VERIFY_CLIENT[] = "verify-client";
132 const char SHRPX_OPT_VERIFY_CLIENT_CACERT[] = "verify-client-cacert";
133 const char SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE[] = "client-private-key-file";
134 const char SHRPX_OPT_CLIENT_CERT_FILE[] = "client-cert-file";
135 const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER[] =
136 "frontend-http2-dump-request-header";
137 const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] =
138 "frontend-http2-dump-response-header";
139 const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling";
140 const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug";
141 const char SHRPX_OPT_PADDING[] = "padding";
142 const char SHRPX_OPT_ALTSVC[] = "altsvc";
143 const char SHRPX_OPT_ADD_RESPONSE_HEADER[] = "add-response-header";
144 const char SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS[] =
145 "worker-frontend-connections";
146 const char SHRPX_OPT_NO_LOCATION_REWRITE[] = "no-location-rewrite";
147 const char SHRPX_OPT_NO_HOST_REWRITE[] = "no-host-rewrite";
148 const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST[] =
149 "backend-http1-connections-per-host";
150 const char SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND[] =
151 "backend-http1-connections-per-frontend";
152 const char SHRPX_OPT_LISTENER_DISABLE_TIMEOUT[] = "listener-disable-timeout";
153 const char SHRPX_OPT_TLS_TICKET_KEY_FILE[] = "tls-ticket-key-file";
154 const char SHRPX_OPT_RLIMIT_NOFILE[] = "rlimit-nofile";
155 const char SHRPX_OPT_BACKEND_REQUEST_BUFFER[] = "backend-request-buffer";
156 const char SHRPX_OPT_BACKEND_RESPONSE_BUFFER[] = "backend-response-buffer";
157 const char SHRPX_OPT_NO_SERVER_PUSH[] = "no-server-push";
158 const char SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER[] =
159 "backend-http2-connections-per-worker";
160 const char SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE[] = "fetch-ocsp-response-file";
161 const char SHRPX_OPT_OCSP_UPDATE_INTERVAL[] = "ocsp-update-interval";
162 const char SHRPX_OPT_NO_OCSP[] = "no-ocsp";
163 const char SHRPX_OPT_HEADER_FIELD_BUFFER[] = "header-field-buffer";
164 const char SHRPX_OPT_MAX_HEADER_FIELDS[] = "max-header-fields";
167 Config *config = nullptr;
170 const Config *get_config() { return config; }
172 Config *mod_config() { return config; }
174 void create_config() { config = new Config(); }
176 TicketKeys::~TicketKeys() {
177 /* Erase keys from memory */
178 for (auto &key : keys) {
179 memset(&key, 0, sizeof(key));
184 int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
185 const char *hostport) {
186 // host and port in |hostport| is separated by single ','.
187 const char *p = strchr(hostport, ',');
189 LOG(ERROR) << "Invalid host, port: " << hostport;
192 size_t len = p - hostport;
193 if (hostlen < len + 1) {
194 LOG(ERROR) << "Hostname too long: " << hostport;
197 memcpy(host, hostport, len);
201 unsigned long d = strtoul(p + 1, nullptr, 10);
202 if (errno == 0 && 1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
206 LOG(ERROR) << "Port is invalid: " << p + 1;
213 bool is_secure(const char *filename) {
215 int rv = stat(filename, &buf);
217 if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) &&
218 !(buf.st_mode & S_IRWXO)) {
227 std::unique_ptr<TicketKeys>
228 read_tls_ticket_key_file(const std::vector<std::string> &files) {
229 auto ticket_keys = make_unique<TicketKeys>();
230 auto &keys = ticket_keys->keys;
231 keys.resize(files.size());
233 for (auto &file : files) {
234 std::ifstream f(file.c_str());
236 LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
240 f.read(buf, sizeof(buf));
241 if (f.gcount() != sizeof(buf)) {
242 LOG(ERROR) << "tls-ticket-key-file: want to read 48 bytes but read "
243 << f.gcount() << " bytes from " << file;
247 auto &key = keys[i++];
249 memcpy(key.name, p, sizeof(key.name));
250 p += sizeof(key.name);
251 memcpy(key.aes_key, p, sizeof(key.aes_key));
252 p += sizeof(key.aes_key);
253 memcpy(key.hmac_key, p, sizeof(key.hmac_key));
255 if (LOG_ENABLED(INFO)) {
256 LOG(INFO) << "session ticket key: " << util::format_hex(key.name,
263 FILE *open_file_for_write(const char *filename) {
264 #if defined O_CLOEXEC
265 auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
268 auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
270 // We get race condition if execve is called at the same time.
272 util::make_socket_closeonexec(fd);
276 LOG(ERROR) << "Failed to open " << filename
277 << " for writing. Cause: " << strerror(errno);
280 auto f = fdopen(fd, "wb");
282 LOG(ERROR) << "Failed to open " << filename
283 << " for writing. Cause: " << strerror(errno);
290 std::string read_passwd_from_file(const char *filename) {
293 if (!is_secure(filename)) {
294 LOG(ERROR) << "Private key passwd file " << filename
295 << " has insecure mode.";
299 std::ifstream in(filename, std::ios::binary);
301 LOG(ERROR) << "Could not open key passwd file " << filename;
305 std::getline(in, line);
309 std::unique_ptr<char[]> strcopy(const char *val) {
310 return strcopy(val, strlen(val));
313 std::unique_ptr<char[]> strcopy(const char *val, size_t len) {
314 auto res = make_unique<char[]>(len + 1);
315 memcpy(res.get(), val, len);
320 std::unique_ptr<char[]> strcopy(const std::string &val) {
321 return strcopy(val.c_str(), val.size());
324 std::vector<char *> parse_config_str_list(const char *s) {
326 for (const char *first = s, *p = nullptr; (p = strchr(first, ','));
327 ++len, first = p + 1)
329 auto list = std::vector<char *>(len);
330 auto first = strdup(s);
333 auto p = strchr(first, ',');
346 void clear_config_str_list(std::vector<char *> &list) {
355 std::pair<std::string, std::string> parse_header(const char *optarg) {
356 // We skip possible ":" at the start of optarg.
357 const auto *colon = strchr(optarg + 1, ':');
359 // name = ":" is not allowed
360 if (colon == nullptr || (optarg[0] == ':' && colon == optarg + 1)) {
364 auto value = colon + 1;
365 for (; *value == '\t' || *value == ' '; ++value)
368 return {std::string(optarg, colon), std::string(value, strlen(value))};
371 template <typename T>
372 int parse_uint(T *dest, const char *opt, const char *optarg) {
377 auto val = strtol(optarg, &end, 10);
379 if (!optarg[0] || errno != 0 || *end || val < 0) {
380 LOG(ERROR) << opt << ": bad value. Specify an integer >= 0.";
390 template <typename T>
391 int parse_uint_with_unit(T *dest, const char *opt, const char *optarg) {
392 auto n = util::parse_uint_with_unit(optarg);
394 LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
404 template <typename T>
405 int parse_int(T *dest, const char *opt, const char *optarg) {
410 auto val = strtol(optarg, &end, 10);
412 if (!optarg[0] || errno != 0 || *end) {
413 LOG(ERROR) << opt << ": bad value. Specify an integer.";
423 LogFragment make_log_fragment(LogFragmentType type,
424 std::unique_ptr<char[]> value = nullptr) {
425 return LogFragment{type, std::move(value)};
430 bool var_token(char c) {
431 return util::isAlpha(c) || util::isDigit(c) || c == '_';
435 std::vector<LogFragment> parse_log_format(const char *optarg) {
436 auto literal_start = optarg;
438 auto eop = p + strlen(optarg);
440 auto res = std::vector<LogFragment>();
452 for (; p != eop && var_token(*p); ++p)
455 auto varlen = p - var_start;
457 auto type = SHRPX_LOGF_NONE;
458 const char *value = nullptr;
461 if (util::strieq_l("$remote_addr", var_start, varlen)) {
462 type = SHRPX_LOGF_REMOTE_ADDR;
463 } else if (util::strieq_l("$time_local", var_start, varlen)) {
464 type = SHRPX_LOGF_TIME_LOCAL;
465 } else if (util::strieq_l("$time_iso8601", var_start, varlen)) {
466 type = SHRPX_LOGF_TIME_ISO8601;
467 } else if (util::strieq_l("$request", var_start, varlen)) {
468 type = SHRPX_LOGF_REQUEST;
469 } else if (util::strieq_l("$status", var_start, varlen)) {
470 type = SHRPX_LOGF_STATUS;
471 } else if (util::strieq_l("$body_bytes_sent", var_start, varlen)) {
472 type = SHRPX_LOGF_BODY_BYTES_SENT;
473 } else if (util::istartsWith(var_start, varlen, "$http_")) {
474 type = SHRPX_LOGF_HTTP;
475 value = var_start + sizeof("$http_") - 1;
476 valuelen = varlen - (sizeof("$http_") - 1);
477 } else if (util::strieq_l("$remote_port", var_start, varlen)) {
478 type = SHRPX_LOGF_REMOTE_PORT;
479 } else if (util::strieq_l("$server_port", var_start, varlen)) {
480 type = SHRPX_LOGF_SERVER_PORT;
481 } else if (util::strieq_l("$request_time", var_start, varlen)) {
482 type = SHRPX_LOGF_REQUEST_TIME;
483 } else if (util::strieq_l("$pid", var_start, varlen)) {
484 type = SHRPX_LOGF_PID;
485 } else if (util::strieq_l("$alpn", var_start, varlen)) {
486 type = SHRPX_LOGF_ALPN;
488 LOG(WARN) << "Unrecognized log format variable: "
489 << std::string(var_start, varlen);
493 if (literal_start < var_start) {
495 make_log_fragment(SHRPX_LOGF_LITERAL,
496 strcopy(literal_start, var_start - literal_start)));
499 if (value == nullptr) {
500 res.push_back(make_log_fragment(type));
502 res.push_back(make_log_fragment(type, strcopy(value, valuelen)));
503 auto &v = res.back().value;
504 for (size_t i = 0; v[i]; ++i) {
511 literal_start = var_start + varlen;
514 if (literal_start != eop) {
515 res.push_back(make_log_fragment(
516 SHRPX_LOGF_LITERAL, strcopy(literal_start, eop - literal_start)));
523 int parse_duration(ev_tstamp *dest, const char *opt, const char *optarg) {
524 auto t = util::parse_duration_with_unit(optarg);
525 if (t == std::numeric_limits<double>::infinity()) {
526 LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
536 int parse_config(const char *opt, const char *optarg) {
537 char host[NI_MAXHOST];
539 if (util::strieq(opt, SHRPX_OPT_BACKEND)) {
540 if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) {
542 auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
543 addr.host = strcopy(path);
544 addr.host_unix = true;
546 mod_config()->downstream_addrs.push_back(std::move(addr));
551 if (split_host_port(host, sizeof(host), &port, optarg) == -1) {
556 addr.host = strcopy(host);
559 mod_config()->downstream_addrs.push_back(std::move(addr));
564 if (util::strieq(opt, SHRPX_OPT_FRONTEND)) {
565 if (util::istartsWith(optarg, SHRPX_UNIX_PATH_PREFIX)) {
566 auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX);
567 mod_config()->host = strcopy(path);
568 mod_config()->port = 0;
569 mod_config()->host_unix = true;
574 if (split_host_port(host, sizeof(host), &port, optarg) == -1) {
578 mod_config()->host = strcopy(host);
579 mod_config()->port = port;
580 mod_config()->host_unix = false;
585 if (util::strieq(opt, SHRPX_OPT_WORKERS)) {
586 return parse_uint(&mod_config()->num_worker, opt, optarg);
589 if (util::strieq(opt, SHRPX_OPT_HTTP2_MAX_CONCURRENT_STREAMS)) {
590 return parse_uint(&mod_config()->http2_max_concurrent_streams, opt, optarg);
593 if (util::strieq(opt, SHRPX_OPT_LOG_LEVEL)) {
594 if (Log::set_severity_level_by_name(optarg) == -1) {
595 LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
602 if (util::strieq(opt, SHRPX_OPT_DAEMON)) {
603 mod_config()->daemon = util::strieq(optarg, "yes");
608 if (util::strieq(opt, SHRPX_OPT_HTTP2_PROXY)) {
609 mod_config()->http2_proxy = util::strieq(optarg, "yes");
614 if (util::strieq(opt, SHRPX_OPT_HTTP2_BRIDGE)) {
615 mod_config()->http2_bridge = util::strieq(optarg, "yes");
620 if (util::strieq(opt, SHRPX_OPT_CLIENT_PROXY)) {
621 mod_config()->client_proxy = util::strieq(optarg, "yes");
626 if (util::strieq(opt, SHRPX_OPT_ADD_X_FORWARDED_FOR)) {
627 mod_config()->add_x_forwarded_for = util::strieq(optarg, "yes");
632 if (util::strieq(opt, SHRPX_OPT_STRIP_INCOMING_X_FORWARDED_FOR)) {
633 mod_config()->strip_incoming_x_forwarded_for = util::strieq(optarg, "yes");
638 if (util::strieq(opt, SHRPX_OPT_NO_VIA)) {
639 mod_config()->no_via = util::strieq(optarg, "yes");
644 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_READ_TIMEOUT)) {
645 return parse_duration(&mod_config()->http2_upstream_read_timeout, opt,
649 if (util::strieq(opt, SHRPX_OPT_FRONTEND_READ_TIMEOUT)) {
650 return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg);
653 if (util::strieq(opt, SHRPX_OPT_FRONTEND_WRITE_TIMEOUT)) {
654 return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg);
657 if (util::strieq(opt, SHRPX_OPT_BACKEND_READ_TIMEOUT)) {
658 return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg);
661 if (util::strieq(opt, SHRPX_OPT_BACKEND_WRITE_TIMEOUT)) {
662 return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg);
665 if (util::strieq(opt, SHRPX_OPT_STREAM_READ_TIMEOUT)) {
666 return parse_duration(&mod_config()->stream_read_timeout, opt, optarg);
669 if (util::strieq(opt, SHRPX_OPT_STREAM_WRITE_TIMEOUT)) {
670 return parse_duration(&mod_config()->stream_write_timeout, opt, optarg);
673 if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FILE)) {
674 mod_config()->accesslog_file = strcopy(optarg);
679 if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_SYSLOG)) {
680 mod_config()->accesslog_syslog = util::strieq(optarg, "yes");
685 if (util::strieq(opt, SHRPX_OPT_ACCESSLOG_FORMAT)) {
686 mod_config()->accesslog_format = parse_log_format(optarg);
691 if (util::strieq(opt, SHRPX_OPT_ERRORLOG_FILE)) {
692 mod_config()->errorlog_file = strcopy(optarg);
697 if (util::strieq(opt, SHRPX_OPT_ERRORLOG_SYSLOG)) {
698 mod_config()->errorlog_syslog = util::strieq(optarg, "yes");
703 if (util::strieq(opt, SHRPX_OPT_BACKEND_KEEP_ALIVE_TIMEOUT)) {
704 return parse_duration(&mod_config()->downstream_idle_read_timeout, opt,
708 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS) ||
709 util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_WINDOW_BITS)) {
713 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_WINDOW_BITS)) {
714 resp = &mod_config()->http2_upstream_window_bits;
716 resp = &mod_config()->http2_downstream_window_bits;
723 if (parse_uint(&n, opt, optarg) != 0) {
729 << ": specify the integer in the range [0, 30], inclusive";
738 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) ||
739 util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_BITS)) {
743 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS)) {
744 resp = &mod_config()->http2_upstream_connection_window_bits;
746 resp = &mod_config()->http2_downstream_connection_window_bits;
753 if (parse_uint(&n, opt, optarg) != 0) {
757 if (n < 16 || n >= 31) {
759 << ": specify the integer in the range [16, 30], inclusive";
768 if (util::strieq(opt, SHRPX_OPT_FRONTEND_NO_TLS)) {
769 mod_config()->upstream_no_tls = util::strieq(optarg, "yes");
774 if (util::strieq(opt, SHRPX_OPT_BACKEND_NO_TLS)) {
775 mod_config()->downstream_no_tls = util::strieq(optarg, "yes");
780 if (util::strieq(opt, SHRPX_OPT_BACKEND_TLS_SNI_FIELD)) {
781 mod_config()->backend_tls_sni_name = strcopy(optarg);
786 if (util::strieq(opt, SHRPX_OPT_PID_FILE)) {
787 mod_config()->pid_file = strcopy(optarg);
792 if (util::strieq(opt, SHRPX_OPT_USER)) {
793 auto pwd = getpwnam(optarg);
795 LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": "
799 mod_config()->user = strcopy(pwd->pw_name);
800 mod_config()->uid = pwd->pw_uid;
801 mod_config()->gid = pwd->pw_gid;
806 if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_FILE)) {
807 mod_config()->private_key_file = strcopy(optarg);
812 if (util::strieq(opt, SHRPX_OPT_PRIVATE_KEY_PASSWD_FILE)) {
813 auto passwd = read_passwd_from_file(optarg);
814 if (passwd.empty()) {
815 LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
818 mod_config()->private_key_passwd = strcopy(passwd);
823 if (util::strieq(opt, SHRPX_OPT_CERTIFICATE_FILE)) {
824 mod_config()->cert_file = strcopy(optarg);
829 if (util::strieq(opt, SHRPX_OPT_DH_PARAM_FILE)) {
830 mod_config()->dh_param_file = strcopy(optarg);
835 if (util::strieq(opt, SHRPX_OPT_SUBCERT)) {
836 // Private Key file and certificate file separated by ':'.
837 const char *sp = strchr(optarg, ':');
839 std::string keyfile(optarg, sp);
840 // TODO Do we need private key for subcert?
841 mod_config()->subcerts.emplace_back(keyfile, sp + 1);
847 if (util::strieq(opt, SHRPX_OPT_SYSLOG_FACILITY)) {
848 int facility = int_syslog_facility(optarg);
849 if (facility == -1) {
850 LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg;
853 mod_config()->syslog_facility = facility;
858 if (util::strieq(opt, SHRPX_OPT_BACKLOG)) {
860 if (parse_int(&n, opt, optarg) != 0) {
865 LOG(ERROR) << opt << ": " << optarg << " is not allowed";
870 mod_config()->backlog = n;
875 if (util::strieq(opt, SHRPX_OPT_CIPHERS)) {
876 mod_config()->ciphers = strcopy(optarg);
881 if (util::strieq(opt, SHRPX_OPT_CLIENT)) {
882 mod_config()->client = util::strieq(optarg, "yes");
887 if (util::strieq(opt, SHRPX_OPT_INSECURE)) {
888 mod_config()->insecure = util::strieq(optarg, "yes");
893 if (util::strieq(opt, SHRPX_OPT_CACERT)) {
894 mod_config()->cacert = strcopy(optarg);
899 if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV4)) {
900 mod_config()->backend_ipv4 = util::strieq(optarg, "yes");
905 if (util::strieq(opt, SHRPX_OPT_BACKEND_IPV6)) {
906 mod_config()->backend_ipv6 = util::strieq(optarg, "yes");
911 if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP_PROXY_URI)) {
912 // parse URI and get hostname, port and optionally userinfo.
914 memset(&u, 0, sizeof(u));
915 int rv = http_parser_parse_url(optarg, strlen(optarg), 0, &u);
918 if (u.field_set & UF_USERINFO) {
919 http2::copy_url_component(val, &u, UF_USERINFO, optarg);
920 // Surprisingly, u.field_set & UF_USERINFO is nonzero even if
921 // userinfo component is empty string.
923 val = util::percentDecode(val.begin(), val.end());
924 mod_config()->downstream_http_proxy_userinfo = strcopy(val);
927 if (u.field_set & UF_HOST) {
928 http2::copy_url_component(val, &u, UF_HOST, optarg);
929 mod_config()->downstream_http_proxy_host = strcopy(val);
931 LOG(ERROR) << opt << ": no hostname specified";
934 if (u.field_set & UF_PORT) {
935 mod_config()->downstream_http_proxy_port = u.port;
937 LOG(ERROR) << opt << ": no port specified";
941 LOG(ERROR) << opt << ": parse error";
948 if (util::strieq(opt, SHRPX_OPT_READ_RATE)) {
949 return parse_uint_with_unit(&mod_config()->read_rate, opt, optarg);
952 if (util::strieq(opt, SHRPX_OPT_READ_BURST)) {
953 return parse_uint_with_unit(&mod_config()->read_burst, opt, optarg);
956 if (util::strieq(opt, SHRPX_OPT_WRITE_RATE)) {
957 return parse_uint_with_unit(&mod_config()->write_rate, opt, optarg);
960 if (util::strieq(opt, SHRPX_OPT_WRITE_BURST)) {
961 return parse_uint_with_unit(&mod_config()->write_burst, opt, optarg);
964 if (util::strieq(opt, SHRPX_OPT_WORKER_READ_RATE)) {
965 LOG(WARN) << opt << ": not implemented yet";
966 return parse_uint_with_unit(&mod_config()->worker_read_rate, opt, optarg);
969 if (util::strieq(opt, SHRPX_OPT_WORKER_READ_BURST)) {
970 LOG(WARN) << opt << ": not implemented yet";
971 return parse_uint_with_unit(&mod_config()->worker_read_burst, opt, optarg);
974 if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_RATE)) {
975 LOG(WARN) << opt << ": not implemented yet";
976 return parse_uint_with_unit(&mod_config()->worker_write_rate, opt, optarg);
979 if (util::strieq(opt, SHRPX_OPT_WORKER_WRITE_BURST)) {
980 LOG(WARN) << opt << ": not implemented yet";
981 return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg);
984 if (util::strieq(opt, SHRPX_OPT_NPN_LIST)) {
985 clear_config_str_list(mod_config()->npn_list);
987 mod_config()->npn_list = parse_config_str_list(optarg);
992 if (util::strieq(opt, SHRPX_OPT_TLS_PROTO_LIST)) {
993 clear_config_str_list(mod_config()->tls_proto_list);
995 mod_config()->tls_proto_list = parse_config_str_list(optarg);
1000 if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT)) {
1001 mod_config()->verify_client = util::strieq(optarg, "yes");
1006 if (util::strieq(opt, SHRPX_OPT_VERIFY_CLIENT_CACERT)) {
1007 mod_config()->verify_client_cacert = strcopy(optarg);
1012 if (util::strieq(opt, SHRPX_OPT_CLIENT_PRIVATE_KEY_FILE)) {
1013 mod_config()->client_private_key_file = strcopy(optarg);
1018 if (util::strieq(opt, SHRPX_OPT_CLIENT_CERT_FILE)) {
1019 mod_config()->client_cert_file = strcopy(optarg);
1024 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_REQUEST_HEADER)) {
1025 mod_config()->http2_upstream_dump_request_header_file = strcopy(optarg);
1030 if (util::strieq(opt, SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER)) {
1031 mod_config()->http2_upstream_dump_response_header_file = strcopy(optarg);
1036 if (util::strieq(opt, SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING)) {
1037 mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes");
1042 if (util::strieq(opt, SHRPX_OPT_FRONTEND_FRAME_DEBUG)) {
1043 mod_config()->upstream_frame_debug = util::strieq(optarg, "yes");
1048 if (util::strieq(opt, SHRPX_OPT_PADDING)) {
1049 return parse_uint(&mod_config()->padding, opt, optarg);
1052 if (util::strieq(opt, SHRPX_OPT_ALTSVC)) {
1053 auto tokens = parse_config_str_list(optarg);
1055 if (tokens.size() < 2) {
1056 // Requires at least protocol_id and port
1057 LOG(ERROR) << opt << ": too few parameters: " << optarg;
1061 if (tokens.size() > 4) {
1062 // We only need protocol_id, port, host and origin
1063 LOG(ERROR) << opt << ": too many parameters: " << optarg;
1069 if (parse_uint(&port, opt, tokens[1]) != 0) {
1074 port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
1075 LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
1083 altsvc.protocol_id = tokens[0];
1084 altsvc.protocol_id_len = strlen(altsvc.protocol_id);
1086 if (tokens.size() > 2) {
1087 altsvc.host = tokens[2];
1088 altsvc.host_len = strlen(altsvc.host);
1090 if (tokens.size() > 3) {
1091 altsvc.origin = tokens[3];
1092 altsvc.origin_len = strlen(altsvc.origin);
1096 mod_config()->altsvcs.push_back(std::move(altsvc));
1101 if (util::strieq(opt, SHRPX_OPT_ADD_RESPONSE_HEADER)) {
1102 auto p = parse_header(optarg);
1103 if (p.first.empty()) {
1104 LOG(ERROR) << opt << ": header field name is empty: " << optarg;
1107 mod_config()->add_response_headers.push_back(std::move(p));
1112 if (util::strieq(opt, SHRPX_OPT_WORKER_FRONTEND_CONNECTIONS)) {
1113 return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg);
1116 if (util::strieq(opt, SHRPX_OPT_NO_LOCATION_REWRITE)) {
1117 mod_config()->no_location_rewrite = util::strieq(optarg, "yes");
1122 if (util::strieq(opt, SHRPX_OPT_NO_HOST_REWRITE)) {
1123 mod_config()->no_host_rewrite = util::strieq(optarg, "yes");
1128 if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_HOST)) {
1131 if (parse_uint(&n, opt, optarg) != 0) {
1136 LOG(ERROR) << opt << ": specify an integer strictly more than 0";
1141 mod_config()->downstream_connections_per_host = n;
1146 if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND)) {
1147 return parse_uint(&mod_config()->downstream_connections_per_frontend, opt,
1151 if (util::strieq(opt, SHRPX_OPT_LISTENER_DISABLE_TIMEOUT)) {
1152 return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg);
1155 if (util::strieq(opt, SHRPX_OPT_TLS_TICKET_KEY_FILE)) {
1156 mod_config()->tls_ticket_key_files.push_back(optarg);
1160 if (util::strieq(opt, SHRPX_OPT_RLIMIT_NOFILE)) {
1163 if (parse_uint(&n, opt, optarg) != 0) {
1168 LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
1173 mod_config()->rlimit_nofile = n;
1178 if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER) ||
1179 util::strieq(opt, SHRPX_OPT_BACKEND_RESPONSE_BUFFER)) {
1181 if (parse_uint_with_unit(&n, opt, optarg) != 0) {
1186 LOG(ERROR) << opt << ": specify an integer strictly more than 0";
1191 if (util::strieq(opt, SHRPX_OPT_BACKEND_REQUEST_BUFFER)) {
1192 mod_config()->downstream_request_buffer_size = n;
1194 mod_config()->downstream_response_buffer_size = n;
1200 if (util::strieq(opt, SHRPX_OPT_NO_SERVER_PUSH)) {
1201 mod_config()->no_server_push = util::strieq(optarg, "yes");
1206 if (util::strieq(opt, SHRPX_OPT_BACKEND_HTTP2_CONNECTIONS_PER_WORKER)) {
1207 return parse_uint(&mod_config()->http2_downstream_connections_per_worker,
1211 if (util::strieq(opt, SHRPX_OPT_FETCH_OCSP_RESPONSE_FILE)) {
1212 mod_config()->fetch_ocsp_response_file = strcopy(optarg);
1217 if (util::strieq(opt, SHRPX_OPT_OCSP_UPDATE_INTERVAL)) {
1218 return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg);
1221 if (util::strieq(opt, SHRPX_OPT_NO_OCSP)) {
1222 mod_config()->no_ocsp = util::strieq(optarg, "yes");
1227 if (util::strieq(opt, SHRPX_OPT_HEADER_FIELD_BUFFER)) {
1228 return parse_uint_with_unit(&mod_config()->header_field_buffer, opt,
1232 if (util::strieq(opt, SHRPX_OPT_MAX_HEADER_FIELDS)) {
1233 return parse_uint(&mod_config()->max_header_fields, opt, optarg);
1236 if (util::strieq(opt, "conf")) {
1237 LOG(WARN) << "conf: ignored";
1242 LOG(ERROR) << "Unknown option: " << opt;
1247 int load_config(const char *filename) {
1248 std::ifstream in(filename, std::ios::binary);
1250 LOG(ERROR) << "Could not open config file " << filename;
1255 while (std::getline(in, line)) {
1257 if (line.empty() || line[0] == '#') {
1261 size_t size = line.size();
1262 for (i = 0; i < size && line[i] != '='; ++i)
1265 LOG(ERROR) << "Bad configuration format at line " << linenum;
1269 auto s = line.c_str();
1270 if (parse_config(s, s + i + 1) == -1) {
1277 const char *str_syslog_facility(int facility) {
1281 case (LOG_AUTHPRIV):
1322 int int_syslog_facility(const char *strfacility) {
1323 if (util::strieq(strfacility, "auth")) {
1327 if (util::strieq(strfacility, "authpriv")) {
1328 return LOG_AUTHPRIV;
1331 if (util::strieq(strfacility, "cron")) {
1335 if (util::strieq(strfacility, "daemon")) {
1339 if (util::strieq(strfacility, "ftp")) {
1343 if (util::strieq(strfacility, "kern")) {
1347 if (util::strieq(strfacility, "local0")) {
1351 if (util::strieq(strfacility, "local1")) {
1355 if (util::strieq(strfacility, "local2")) {
1359 if (util::strieq(strfacility, "local3")) {
1363 if (util::strieq(strfacility, "local4")) {
1367 if (util::strieq(strfacility, "local5")) {
1371 if (util::strieq(strfacility, "local6")) {
1375 if (util::strieq(strfacility, "local7")) {
1379 if (util::strieq(strfacility, "lpr")) {
1383 if (util::strieq(strfacility, "mail")) {
1387 if (util::strieq(strfacility, "news")) {
1391 if (util::strieq(strfacility, "syslog")) {
1395 if (util::strieq(strfacility, "user")) {
1399 if (util::strieq(strfacility, "uucp")) {
1406 } // namespace shrpx