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 <unordered_map>
52 #include <nghttp2/nghttp2.h>
54 #include "url-parser/url_parser.h"
56 #include "shrpx_log.h"
57 #include "shrpx_tls.h"
58 #include "shrpx_http.h"
60 # include "shrpx_mruby.h"
64 #include "ssl_compat.h"
65 #include "xsi_strerror.h"
73 constexpr auto SHRPX_UNIX_PATH_PREFIX = StringRef::from_lit("unix:");
75 const Config *get_config() { return config; }
77 Config *mod_config() { return config; }
79 std::unique_ptr<Config> replace_config(std::unique_ptr<Config> another) {
81 config = another.release();
82 return std::unique_ptr<Config>(p);
85 void create_config() { config = new Config(); }
88 auto &upstreamconf = http2.upstream;
90 nghttp2_option_del(upstreamconf.option);
91 nghttp2_option_del(upstreamconf.alt_mode_option);
92 nghttp2_session_callbacks_del(upstreamconf.callbacks);
94 auto &downstreamconf = http2.downstream;
96 nghttp2_option_del(downstreamconf.option);
97 nghttp2_session_callbacks_del(downstreamconf.callbacks);
99 auto &dumpconf = http2.upstream.debug.dump;
101 if (dumpconf.request_header) {
102 fclose(dumpconf.request_header);
105 if (dumpconf.response_header) {
106 fclose(dumpconf.response_header);
110 TicketKeys::~TicketKeys() {
111 /* Erase keys from memory */
112 for (auto &key : keys) {
113 memset(&key, 0, sizeof(key));
118 int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
119 const StringRef &hostport, const StringRef &opt) {
120 // host and port in |hostport| is separated by single ','.
121 auto sep = std::find(std::begin(hostport), std::end(hostport), ',');
122 if (sep == std::end(hostport)) {
123 LOG(ERROR) << opt << ": Invalid host, port: " << hostport;
126 size_t len = sep - std::begin(hostport);
127 if (hostlen < len + 1) {
128 LOG(ERROR) << opt << ": Hostname too long: " << hostport;
131 std::copy(std::begin(hostport), sep, host);
134 auto portstr = StringRef{sep + 1, std::end(hostport)};
135 auto d = util::parse_uint(portstr);
136 if (1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
141 LOG(ERROR) << opt << ": Port is invalid: " << portstr;
147 bool is_secure(const StringRef &filename) {
149 int rv = stat(filename.c_str(), &buf);
151 if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) &&
152 !(buf.st_mode & S_IRWXO)) {
161 std::unique_ptr<TicketKeys>
162 read_tls_ticket_key_file(const std::vector<StringRef> &files,
163 const EVP_CIPHER *cipher, const EVP_MD *hmac) {
164 auto ticket_keys = std::make_unique<TicketKeys>();
165 auto &keys = ticket_keys->keys;
166 keys.resize(files.size());
167 auto enc_keylen = EVP_CIPHER_key_length(cipher);
168 auto hmac_keylen = EVP_MD_size(hmac);
169 if (cipher == EVP_aes_128_cbc()) {
170 // backward compatibility, as a legacy of using same file format
171 // with nginx and apache.
174 auto expectedlen = keys[0].data.name.size() + enc_keylen + hmac_keylen;
176 assert(sizeof(buf) >= expectedlen);
179 for (auto &file : files) {
182 if (stat(file.c_str(), &fst) == -1) {
184 LOG(ERROR) << "tls-ticket-key-file: could not stat file " << file
185 << ", errno=" << error;
189 if (static_cast<size_t>(fst.st_size) != expectedlen) {
190 LOG(ERROR) << "tls-ticket-key-file: the expected file size is "
191 << expectedlen << ", the actual file size is " << fst.st_size;
195 std::ifstream f(file.c_str());
197 LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
201 f.read(buf, expectedlen);
202 if (static_cast<size_t>(f.gcount()) != expectedlen) {
203 LOG(ERROR) << "tls-ticket-key-file: want to read " << expectedlen
204 << " bytes but only read " << f.gcount() << " bytes from "
209 auto &key = keys[i++];
212 key.hmac_keylen = hmac_keylen;
214 if (LOG_ENABLED(INFO)) {
215 LOG(INFO) << "enc_keylen=" << enc_keylen
216 << ", hmac_keylen=" << key.hmac_keylen;
220 std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
221 p += key.data.name.size();
222 std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
224 std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
226 if (LOG_ENABLED(INFO)) {
227 LOG(INFO) << "session ticket key: " << util::format_hex(key.data.name);
233 FILE *open_file_for_write(const char *filename) {
234 std::array<char, STRERROR_BUFSIZE> errbuf;
236 #if defined O_CLOEXEC
237 auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
240 auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
242 // We get race condition if execve is called at the same time.
244 util::make_socket_closeonexec(fd);
249 LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: "
250 << xsi_strerror(error, errbuf.data(), errbuf.size());
253 auto f = fdopen(fd, "wb");
256 LOG(ERROR) << "Failed to open " << filename << " for writing. Cause: "
257 << xsi_strerror(error, errbuf.data(), errbuf.size());
265 // Read passwd from |filename|
266 std::string read_passwd_from_file(const StringRef &opt,
267 const StringRef &filename) {
270 if (!is_secure(filename)) {
271 LOG(ERROR) << opt << ": Private key passwd file " << filename
272 << " has insecure mode.";
276 std::ifstream in(filename.c_str(), std::ios::binary);
278 LOG(ERROR) << opt << ": Could not open key passwd file " << filename;
282 std::getline(in, line);
287 HeaderRefs::value_type parse_header(BlockAllocator &balloc,
288 const StringRef &optarg) {
289 auto colon = std::find(std::begin(optarg), std::end(optarg), ':');
291 if (colon == std::end(optarg) || colon == std::begin(optarg)) {
295 auto value = colon + 1;
296 for (; *value == '\t' || *value == ' '; ++value)
300 make_byte_ref(balloc, std::distance(std::begin(optarg), colon) + 1);
301 auto p = name_iov.base;
302 p = std::copy(std::begin(optarg), colon, p);
303 util::inp_strlower(name_iov.base, p);
307 HeaderRef(StringRef{name_iov.base, p},
308 make_string_ref(balloc, StringRef{value, std::end(optarg)}));
310 if (!nghttp2_check_header_name(nv.name.byte(), nv.name.size()) ||
311 !nghttp2_check_header_value(nv.value.byte(), nv.value.size())) {
318 template <typename T>
319 int parse_uint(T *dest, const StringRef &opt, const StringRef &optarg) {
320 auto val = util::parse_uint(optarg);
322 LOG(ERROR) << opt << ": bad value. Specify an integer >= 0.";
332 template <typename T>
333 int parse_uint_with_unit(T *dest, const StringRef &opt,
334 const StringRef &optarg) {
335 auto n = util::parse_uint_with_unit(optarg);
337 LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
341 if (static_cast<uint64_t>(std::numeric_limits<T>::max()) <
342 static_cast<uint64_t>(n)) {
344 << ": too large. The value should be less than or equal to "
345 << std::numeric_limits<T>::max();
355 // Parses |optarg| as signed integer. This requires |optarg| to be
356 // NULL-terminated string.
357 template <typename T>
358 int parse_int(T *dest, const StringRef &opt, const char *optarg) {
363 auto val = strtol(optarg, &end, 10);
365 if (!optarg[0] || errno != 0 || *end) {
366 LOG(ERROR) << opt << ": bad value. Specify an integer.";
376 // generated by gennghttpxfun.py
377 LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
382 if (util::strieq_l("pi", name, 2)) {
383 return LogFragmentType::PID;
391 if (util::strieq_l("alp", name, 3)) {
392 return LogFragmentType::ALPN;
400 if (util::strieq_l("statu", name, 5)) {
401 return LogFragmentType::STATUS;
409 if (util::strieq_l("tls_sn", name, 6)) {
410 return LogFragmentType::TLS_SNI;
414 if (util::strieq_l("reques", name, 6)) {
415 return LogFragmentType::REQUEST;
423 if (util::strieq_l("time_loca", name, 9)) {
424 return LogFragmentType::TIME_LOCAL;
428 if (util::strieq_l("ssl_ciphe", name, 9)) {
429 return LogFragmentType::SSL_CIPHER;
431 if (util::strieq_l("tls_ciphe", name, 9)) {
432 return LogFragmentType::TLS_CIPHER;
440 if (util::strieq_l("remote_add", name, 10)) {
441 return LogFragmentType::REMOTE_ADDR;
445 if (util::strieq_l("remote_por", name, 10)) {
446 return LogFragmentType::REMOTE_PORT;
448 if (util::strieq_l("server_por", name, 10)) {
449 return LogFragmentType::SERVER_PORT;
457 if (util::strieq_l("time_iso860", name, 11)) {
458 return LogFragmentType::TIME_ISO8601;
462 if (util::strieq_l("request_tim", name, 11)) {
463 return LogFragmentType::REQUEST_TIME;
467 if (util::strieq_l("ssl_protoco", name, 11)) {
468 return LogFragmentType::SSL_PROTOCOL;
470 if (util::strieq_l("tls_protoco", name, 11)) {
471 return LogFragmentType::TLS_PROTOCOL;
475 if (util::strieq_l("backend_hos", name, 11)) {
476 return LogFragmentType::BACKEND_HOST;
478 if (util::strieq_l("backend_por", name, 11)) {
479 return LogFragmentType::BACKEND_PORT;
487 if (util::strieq_l("ssl_session_i", name, 13)) {
488 return LogFragmentType::SSL_SESSION_ID;
490 if (util::strieq_l("tls_session_i", name, 13)) {
491 return LogFragmentType::TLS_SESSION_ID;
499 if (util::strieq_l("body_bytes_sen", name, 14)) {
500 return LogFragmentType::BODY_BYTES_SENT;
508 if (util::strieq_l("tls_client_seria", name, 16)) {
509 return LogFragmentType::TLS_CLIENT_SERIAL;
517 if (util::strieq_l("ssl_session_reuse", name, 17)) {
518 return LogFragmentType::SSL_SESSION_REUSED;
520 if (util::strieq_l("tls_session_reuse", name, 17)) {
521 return LogFragmentType::TLS_SESSION_REUSED;
529 if (util::strieq_l("tls_client_issuer_nam", name, 21)) {
530 return LogFragmentType::TLS_CLIENT_ISSUER_NAME;
538 if (util::strieq_l("tls_client_subject_nam", name, 22)) {
539 return LogFragmentType::TLS_CLIENT_SUBJECT_NAME;
547 if (util::strieq_l("tls_client_fingerprint_sha", name, 26)) {
548 return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA1;
556 if (util::strieq_l("tls_client_fingerprint_sha25", name, 28)) {
557 return LogFragmentType::TLS_CLIENT_FINGERPRINT_SHA256;
563 return LogFragmentType::NONE;
568 bool var_token(char c) {
569 return util::is_alpha(c) || util::is_digit(c) || c == '_';
573 std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
574 const StringRef &optarg) {
575 auto literal_start = std::begin(optarg);
576 auto p = literal_start;
577 auto eop = std::end(optarg);
579 auto res = std::vector<LogFragment>();
591 const char *var_name;
593 if (p != eop && *p == '{') {
595 for (; p != eop && var_token(*p); ++p)
598 if (p == eop || *p != '}') {
599 LOG(WARN) << "Missing '}' after " << StringRef{var_start, p};
603 var_namelen = p - var_name;
607 for (; p != eop && var_token(*p); ++p)
610 var_namelen = p - var_name;
613 const char *value = nullptr;
615 auto type = log_var_lookup_token(var_name, var_namelen);
617 if (type == LogFragmentType::NONE) {
618 if (util::istarts_with_l(StringRef{var_name, var_namelen}, "http_")) {
619 if (util::streq_l("host", StringRef{var_name + str_size("http_"),
620 var_namelen - str_size("http_")})) {
621 // Special handling of host header field. We will use
622 // :authority header field if host header is missing. This
623 // is a typical case in HTTP/2.
624 type = LogFragmentType::AUTHORITY;
626 type = LogFragmentType::HTTP;
627 value = var_name + str_size("http_");
630 LOG(WARN) << "Unrecognized log format variable: "
631 << StringRef{var_name, var_namelen};
636 if (literal_start < var_start) {
638 LogFragmentType::LITERAL,
639 make_string_ref(balloc, StringRef{literal_start, var_start}));
644 if (value == nullptr) {
645 res.emplace_back(type);
650 auto iov = make_byte_ref(
651 balloc, std::distance(value, var_name + var_namelen) + 1);
653 p = std::copy(value, var_name + var_namelen, p);
654 for (auto cp = iov.base; cp != p; ++cp) {
660 res.emplace_back(type, StringRef{iov.base, p});
664 if (literal_start != eop) {
665 res.emplace_back(LogFragmentType::LITERAL,
666 make_string_ref(balloc, StringRef{literal_start, eop}));
673 int parse_address_family(int *dest, const StringRef &opt,
674 const StringRef &optarg) {
675 if (util::strieq_l("auto", optarg)) {
679 if (util::strieq_l("IPv4", optarg)) {
683 if (util::strieq_l("IPv6", optarg)) {
688 LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
694 int parse_duration(ev_tstamp *dest, const StringRef &opt,
695 const StringRef &optarg) {
696 auto t = util::parse_duration_with_unit(optarg);
697 if (t == std::numeric_limits<double>::infinity()) {
698 LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
709 int parse_tls_proto_version(int &dest, const StringRef &opt,
710 const StringRef &optarg) {
711 auto v = tls::proto_version_from_string(optarg);
713 LOG(ERROR) << opt << ": invalid TLS protocol version: " << optarg;
723 struct MemcachedConnectionParams {
728 // Parses memcached connection configuration parameter |src_params|,
729 // and stores parsed results into |out|. This function returns 0 if
730 // it succeeds, or -1.
731 int parse_memcached_connection_params(MemcachedConnectionParams &out,
732 const StringRef &src_params,
733 const StringRef &opt) {
734 auto last = std::end(src_params);
735 for (auto first = std::begin(src_params); first != last;) {
736 auto end = std::find(first, last, ';');
737 auto param = StringRef{first, end};
739 if (util::strieq_l("tls", param)) {
741 } else if (util::strieq_l("no-tls", param)) {
743 } else if (!param.empty()) {
744 LOG(ERROR) << opt << ": " << param << ": unknown keyword";
759 struct UpstreamParams {
760 UpstreamAltMode alt_mode;
767 // Parses upstream configuration parameter |src_params|, and stores
768 // parsed results into |out|. This function returns 0 if it succeeds,
770 int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
771 auto last = std::end(src_params);
772 for (auto first = std::begin(src_params); first != last;) {
773 auto end = std::find(first, last, ';');
774 auto param = StringRef{first, end};
776 if (util::strieq_l("tls", param)) {
778 } else if (util::strieq_l("sni-fwd", param)) {
780 } else if (util::strieq_l("no-tls", param)) {
782 } else if (util::strieq_l("api", param)) {
783 if (out.alt_mode != UpstreamAltMode::NONE &&
784 out.alt_mode != UpstreamAltMode::API) {
785 LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
788 out.alt_mode = UpstreamAltMode::API;
789 } else if (util::strieq_l("healthmon", param)) {
790 if (out.alt_mode != UpstreamAltMode::NONE &&
791 out.alt_mode != UpstreamAltMode::HEALTHMON) {
792 LOG(ERROR) << "frontend: api and healthmon are mutually exclusive";
795 out.alt_mode = UpstreamAltMode::HEALTHMON;
796 } else if (util::strieq_l("proxyproto", param)) {
797 out.proxyproto = true;
798 } else if (!param.empty()) {
799 LOG(ERROR) << "frontend: " << param << ": unknown keyword";
814 struct DownstreamParams {
818 AffinityConfig affinity;
819 ev_tstamp read_timeout;
820 ev_tstamp write_timeout;
824 uint32_t group_weight;
828 bool redirect_if_not_tls;
833 // Parses |value| of parameter named |name| as duration. This
834 // function returns 0 if it succeeds and the parsed value is assigned
836 int parse_downstream_param_duration(ev_tstamp &dest, const StringRef &name,
837 const StringRef &value) {
838 auto t = util::parse_duration_with_unit(value);
839 if (t == std::numeric_limits<double>::infinity()) {
840 LOG(ERROR) << "backend: " << name << ": bad value: '" << value << "'";
849 // Parses downstream configuration parameter |src_params|, and stores
850 // parsed results into |out|. This function returns 0 if it succeeds,
852 int parse_downstream_params(DownstreamParams &out,
853 const StringRef &src_params) {
854 auto last = std::end(src_params);
855 for (auto first = std::begin(src_params); first != last;) {
856 auto end = std::find(first, last, ';');
857 auto param = StringRef{first, end};
859 if (util::istarts_with_l(param, "proto=")) {
860 auto protostr = StringRef{first + str_size("proto="), end};
861 if (protostr.empty()) {
862 LOG(ERROR) << "backend: proto: protocol is empty";
866 if (util::streq_l("h2", std::begin(protostr), protostr.size())) {
867 out.proto = Proto::HTTP2;
868 } else if (util::streq_l("http/1.1", std::begin(protostr),
870 out.proto = Proto::HTTP1;
872 LOG(ERROR) << "backend: proto: unknown protocol " << protostr;
875 } else if (util::istarts_with_l(param, "fall=")) {
876 auto valstr = StringRef{first + str_size("fall="), end};
877 if (valstr.empty()) {
878 LOG(ERROR) << "backend: fall: non-negative integer is expected";
882 auto n = util::parse_uint(valstr);
884 LOG(ERROR) << "backend: fall: non-negative integer is expected";
889 } else if (util::istarts_with_l(param, "rise=")) {
890 auto valstr = StringRef{first + str_size("rise="), end};
891 if (valstr.empty()) {
892 LOG(ERROR) << "backend: rise: non-negative integer is expected";
896 auto n = util::parse_uint(valstr);
898 LOG(ERROR) << "backend: rise: non-negative integer is expected";
903 } else if (util::strieq_l("tls", param)) {
905 } else if (util::strieq_l("no-tls", param)) {
907 } else if (util::istarts_with_l(param, "sni=")) {
908 out.sni = StringRef{first + str_size("sni="), end};
909 } else if (util::istarts_with_l(param, "affinity=")) {
910 auto valstr = StringRef{first + str_size("affinity="), end};
911 if (util::strieq_l("none", valstr)) {
912 out.affinity.type = SessionAffinity::NONE;
913 } else if (util::strieq_l("ip", valstr)) {
914 out.affinity.type = SessionAffinity::IP;
915 } else if (util::strieq_l("cookie", valstr)) {
916 out.affinity.type = SessionAffinity::COOKIE;
919 << "backend: affinity: value must be one of none, ip, and cookie";
922 } else if (util::istarts_with_l(param, "affinity-cookie-name=")) {
923 auto val = StringRef{first + str_size("affinity-cookie-name="), end};
926 << "backend: affinity-cookie-name: non empty string is expected";
929 out.affinity.cookie.name = val;
930 } else if (util::istarts_with_l(param, "affinity-cookie-path=")) {
931 out.affinity.cookie.path =
932 StringRef{first + str_size("affinity-cookie-path="), end};
933 } else if (util::istarts_with_l(param, "affinity-cookie-secure=")) {
934 auto valstr = StringRef{first + str_size("affinity-cookie-secure="), end};
935 if (util::strieq_l("auto", valstr)) {
936 out.affinity.cookie.secure = SessionAffinityCookieSecure::AUTO;
937 } else if (util::strieq_l("yes", valstr)) {
938 out.affinity.cookie.secure = SessionAffinityCookieSecure::YES;
939 } else if (util::strieq_l("no", valstr)) {
940 out.affinity.cookie.secure = SessionAffinityCookieSecure::NO;
942 LOG(ERROR) << "backend: affinity-cookie-secure: value must be one of "
946 } else if (util::strieq_l("dns", param)) {
948 } else if (util::strieq_l("redirect-if-not-tls", param)) {
949 out.redirect_if_not_tls = true;
950 } else if (util::strieq_l("upgrade-scheme", param)) {
951 out.upgrade_scheme = true;
952 } else if (util::istarts_with_l(param, "mruby=")) {
953 auto valstr = StringRef{first + str_size("mruby="), end};
955 } else if (util::istarts_with_l(param, "read-timeout=")) {
956 if (parse_downstream_param_duration(
957 out.read_timeout, StringRef::from_lit("read-timeout"),
958 StringRef{first + str_size("read-timeout="), end}) == -1) {
961 } else if (util::istarts_with_l(param, "write-timeout=")) {
962 if (parse_downstream_param_duration(
963 out.write_timeout, StringRef::from_lit("write-timeout"),
964 StringRef{first + str_size("write-timeout="), end}) == -1) {
967 } else if (util::istarts_with_l(param, "weight=")) {
968 auto valstr = StringRef{first + str_size("weight="), end};
969 if (valstr.empty()) {
971 << "backend: weight: non-negative integer [1, 256] is expected";
975 auto n = util::parse_uint(valstr);
976 if (n < 1 || n > 256) {
978 << "backend: weight: non-negative integer [1, 256] is expected";
982 } else if (util::istarts_with_l(param, "group=")) {
983 auto valstr = StringRef{first + str_size("group="), end};
984 if (valstr.empty()) {
985 LOG(ERROR) << "backend: group: empty string is not allowed";
989 } else if (util::istarts_with_l(param, "group-weight=")) {
990 auto valstr = StringRef{first + str_size("group-weight="), end};
991 if (valstr.empty()) {
992 LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is "
997 auto n = util::parse_uint(valstr);
998 if (n < 1 || n > 256) {
999 LOG(ERROR) << "backend: group-weight: non-negative integer [1, 256] is "
1003 out.group_weight = n;
1004 } else if (!param.empty()) {
1005 LOG(ERROR) << "backend: " << param << ": unknown keyword";
1021 // Parses host-path mapping patterns in |src_pattern|, and stores
1022 // mappings in config. We will store each host-path pattern found in
1023 // |src| with |addr|. |addr| will be copied accordingly. Also we
1024 // make a group based on the pattern. The "/" pattern is considered
1025 // as catch-all. We also parse protocol specified in |src_proto|.
1027 // This function returns 0 if it succeeds, or -1.
1028 int parse_mapping(Config *config, DownstreamAddrConfig &addr,
1029 std::map<StringRef, size_t> &pattern_addr_indexer,
1030 const StringRef &src_pattern, const StringRef &src_params) {
1031 // This returns at least 1 element (it could be empty string). We
1032 // will append '/' to all patterns, so it becomes catch-all pattern.
1033 auto mapping = util::split_str(src_pattern, ':');
1034 assert(!mapping.empty());
1035 auto &downstreamconf = *config->conn.downstream;
1036 auto &addr_groups = downstreamconf.addr_groups;
1038 DownstreamParams params{};
1039 params.proto = Proto::HTTP1;
1042 if (parse_downstream_params(params, src_params) != 0) {
1046 if (addr.host_unix && params.dns) {
1047 LOG(ERROR) << "backend: dns: cannot be used for UNIX domain socket";
1051 if (params.affinity.type == SessionAffinity::COOKIE &&
1052 params.affinity.cookie.name.empty()) {
1053 LOG(ERROR) << "backend: affinity-cookie-name is mandatory if "
1054 "affinity=cookie is specified";
1058 addr.fall = params.fall;
1059 addr.rise = params.rise;
1060 addr.weight = params.weight;
1061 addr.group = make_string_ref(downstreamconf.balloc, params.group);
1062 addr.group_weight = params.group_weight;
1063 addr.proto = params.proto;
1064 addr.tls = params.tls;
1065 addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
1066 addr.dns = params.dns;
1067 addr.upgrade_scheme = params.upgrade_scheme;
1069 auto &routerconf = downstreamconf.router;
1070 auto &router = routerconf.router;
1071 auto &rw_router = routerconf.rev_wildcard_router;
1072 auto &wildcard_patterns = routerconf.wildcard_patterns;
1074 for (const auto &raw_pattern : mapping) {
1076 auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
1077 if (slash == std::end(raw_pattern)) {
1078 // This effectively makes empty pattern to "/". 2 for '/' and
1079 // terminal NULL character.
1080 auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2);
1082 p = std::copy(std::begin(raw_pattern), std::end(raw_pattern), p);
1083 util::inp_strlower(iov.base, p);
1086 pattern = StringRef{iov.base, p};
1088 auto path = http2::normalize_path(downstreamconf.balloc,
1089 StringRef{slash, std::end(raw_pattern)},
1091 auto iov = make_byte_ref(downstreamconf.balloc,
1092 std::distance(std::begin(raw_pattern), slash) +
1095 p = std::copy(std::begin(raw_pattern), slash, p);
1096 util::inp_strlower(iov.base, p);
1097 p = std::copy(std::begin(path), std::end(path), p);
1099 pattern = StringRef{iov.base, p};
1101 auto it = pattern_addr_indexer.find(pattern);
1102 if (it != std::end(pattern_addr_indexer)) {
1103 auto &g = addr_groups[(*it).second];
1104 // Last value wins if we have multiple different affinity
1105 // value under one group.
1106 if (params.affinity.type != SessionAffinity::NONE) {
1107 if (g.affinity.type == SessionAffinity::NONE) {
1108 g.affinity.type = params.affinity.type;
1109 if (params.affinity.type == SessionAffinity::COOKIE) {
1110 g.affinity.cookie.name = make_string_ref(
1111 downstreamconf.balloc, params.affinity.cookie.name);
1112 if (!params.affinity.cookie.path.empty()) {
1113 g.affinity.cookie.path = make_string_ref(
1114 downstreamconf.balloc, params.affinity.cookie.path);
1116 g.affinity.cookie.secure = params.affinity.cookie.secure;
1118 } else if (g.affinity.type != params.affinity.type ||
1119 g.affinity.cookie.name != params.affinity.cookie.name ||
1120 g.affinity.cookie.path != params.affinity.cookie.path ||
1121 g.affinity.cookie.secure != params.affinity.cookie.secure) {
1122 LOG(ERROR) << "backend: affinity: multiple different affinity "
1123 "configurations found in a single group";
1127 // If at least one backend requires frontend TLS connection,
1128 // enable it for all backends sharing the same pattern.
1129 if (params.redirect_if_not_tls) {
1130 g.redirect_if_not_tls = true;
1132 // All backends in the same group must have the same mruby path.
1133 // If some backends do not specify mruby file, and there is at
1134 // least one backend with mruby file, it is used for all
1135 // backends in the group.
1136 if (!params.mruby.empty()) {
1137 if (g.mruby_file.empty()) {
1138 g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
1139 } else if (g.mruby_file != params.mruby) {
1140 LOG(ERROR) << "backend: mruby: multiple different mruby file found "
1141 "in a single group";
1145 // All backends in the same group must have the same read/write
1146 // timeout. If some backends do not specify read/write timeout,
1147 // and there is at least one backend with read/write timeout, it
1148 // is used for all backends in the group.
1149 if (params.read_timeout > 1e-9) {
1150 if (g.timeout.read < 1e-9) {
1151 g.timeout.read = params.read_timeout;
1152 } else if (fabs(g.timeout.read - params.read_timeout) > 1e-9) {
1154 << "backend: read-timeout: multiple different read-timeout "
1155 "found in a single group";
1159 if (params.write_timeout > 1e-9) {
1160 if (g.timeout.write < 1e-9) {
1161 g.timeout.write = params.write_timeout;
1162 } else if (fabs(g.timeout.write - params.write_timeout) > 1e-9) {
1163 LOG(ERROR) << "backend: write-timeout: multiple different "
1164 "write-timeout found in a single group";
1169 g.addrs.push_back(addr);
1173 auto idx = addr_groups.size();
1174 pattern_addr_indexer.emplace(pattern, idx);
1175 addr_groups.emplace_back(pattern);
1176 auto &g = addr_groups.back();
1177 g.addrs.push_back(addr);
1178 g.affinity.type = params.affinity.type;
1179 if (params.affinity.type == SessionAffinity::COOKIE) {
1180 g.affinity.cookie.name =
1181 make_string_ref(downstreamconf.balloc, params.affinity.cookie.name);
1182 if (!params.affinity.cookie.path.empty()) {
1183 g.affinity.cookie.path =
1184 make_string_ref(downstreamconf.balloc, params.affinity.cookie.path);
1186 g.affinity.cookie.secure = params.affinity.cookie.secure;
1188 g.redirect_if_not_tls = params.redirect_if_not_tls;
1189 g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
1190 g.timeout.read = params.read_timeout;
1191 g.timeout.write = params.write_timeout;
1193 if (pattern[0] == '*') {
1196 std::find(std::begin(g.pattern), std::end(g.pattern), '/');
1198 auto host = StringRef{std::begin(g.pattern) + 1, path_first};
1199 auto path = StringRef{path_first, std::end(g.pattern)};
1201 auto path_is_wildcard = false;
1202 if (path[path.size() - 1] == '*') {
1203 path = StringRef{std::begin(path), std::begin(path) + path.size() - 1};
1204 path_is_wildcard = true;
1207 auto it = std::find_if(
1208 std::begin(wildcard_patterns), std::end(wildcard_patterns),
1209 [&host](const WildcardPattern &wp) { return wp.host == host; });
1211 if (it == std::end(wildcard_patterns)) {
1212 wildcard_patterns.emplace_back(host);
1214 auto &router = wildcard_patterns.back().router;
1215 router.add_route(path, idx, path_is_wildcard);
1217 auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
1219 p = std::reverse_copy(std::begin(host), std::end(host), p);
1221 auto rev_host = StringRef{iov.base, p};
1223 rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
1225 (*it).router.add_route(path, idx, path_is_wildcard);
1231 auto path_is_wildcard = false;
1232 if (pattern[pattern.size() - 1] == '*') {
1233 pattern = StringRef{std::begin(pattern),
1234 std::begin(pattern) + pattern.size() - 1};
1235 path_is_wildcard = true;
1238 router.add_route(pattern, idx, path_is_wildcard);
1245 ForwardedNode parse_forwarded_node_type(const StringRef &optarg) {
1246 if (util::strieq_l("obfuscated", optarg)) {
1247 return ForwardedNode::OBFUSCATED;
1250 if (util::strieq_l("ip", optarg)) {
1251 return ForwardedNode::IP;
1254 if (optarg.size() < 2 || optarg[0] != '_') {
1255 return static_cast<ForwardedNode>(-1);
1258 if (std::find_if_not(std::begin(optarg), std::end(optarg), [](char c) {
1259 return util::is_alpha(c) || util::is_digit(c) || c == '.' || c == '_' ||
1261 }) != std::end(optarg)) {
1262 return static_cast<ForwardedNode>(-1);
1265 return ForwardedNode::OBFUSCATED;
1270 int parse_error_page(std::vector<ErrorPage> &error_pages, const StringRef &opt,
1271 const StringRef &optarg) {
1272 std::array<char, STRERROR_BUFSIZE> errbuf;
1274 auto eq = std::find(std::begin(optarg), std::end(optarg), '=');
1275 if (eq == std::end(optarg) || eq + 1 == std::end(optarg)) {
1276 LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
1280 auto codestr = StringRef{std::begin(optarg), eq};
1283 if (codestr == StringRef::from_lit("*")) {
1286 auto n = util::parse_uint(codestr);
1288 if (n == -1 || n < 400 || n > 599) {
1289 LOG(ERROR) << opt << ": bad code: '" << codestr << "'";
1293 code = static_cast<unsigned int>(n);
1296 auto path = StringRef{eq + 1, std::end(optarg)};
1298 std::vector<uint8_t> content;
1299 auto fd = open(path.c_str(), O_RDONLY);
1302 LOG(ERROR) << opt << ": " << optarg << ": "
1303 << xsi_strerror(error, errbuf.data(), errbuf.size());
1307 auto fd_closer = defer(close, fd);
1309 std::array<uint8_t, 4096> buf;
1311 auto n = read(fd, buf.data(), buf.size());
1314 LOG(ERROR) << opt << ": " << optarg << ": "
1315 << xsi_strerror(error, errbuf.data(), errbuf.size());
1321 content.insert(std::end(content), std::begin(buf), std::begin(buf) + n);
1324 error_pages.push_back(ErrorPage{std::move(content), code});
1331 // Maximum size of SCT extension payload length.
1332 constexpr size_t MAX_SCT_EXT_LEN = 16_k;
1335 struct SubcertParams {
1340 // Parses subcert parameter |src_params|, and stores parsed results
1341 // into |out|. This function returns 0 if it succeeds, or -1.
1342 int parse_subcert_params(SubcertParams &out, const StringRef &src_params) {
1343 auto last = std::end(src_params);
1344 for (auto first = std::begin(src_params); first != last;) {
1345 auto end = std::find(first, last, ';');
1346 auto param = StringRef{first, end};
1348 if (util::istarts_with_l(param, "sct-dir=")) {
1349 #if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
1351 StringRef{std::begin(param) + str_size("sct-dir="), std::end(param)};
1352 if (sct_dir.empty()) {
1353 LOG(ERROR) << "subcert: " << param << ": empty sct-dir";
1356 out.sct_dir = sct_dir;
1357 #else // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
1358 LOG(WARN) << "subcert: sct-dir requires OpenSSL >= 1.0.2";
1359 #endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
1360 } else if (!param.empty()) {
1361 LOG(ERROR) << "subcert: " << param << ": unknown keyword";
1377 // Reads *.sct files from directory denoted by |dir_path|. |dir_path|
1378 // must be NULL-terminated string.
1379 int read_tls_sct_from_dir(std::vector<uint8_t> &dst, const StringRef &opt,
1380 const StringRef &dir_path) {
1381 std::array<char, STRERROR_BUFSIZE> errbuf;
1383 auto dir = opendir(dir_path.c_str());
1384 if (dir == nullptr) {
1386 LOG(ERROR) << opt << ": " << dir_path << ": "
1387 << xsi_strerror(error, errbuf.data(), errbuf.size());
1391 auto closer = defer(closedir, dir);
1393 // 2 bytes total length field
1394 auto len_idx = std::distance(std::begin(dst), std::end(dst));
1395 dst.insert(std::end(dst), 2, 0);
1399 auto ent = readdir(dir);
1400 if (ent == nullptr) {
1403 LOG(ERROR) << opt << ": failed to read directory " << dir_path << ": "
1404 << xsi_strerror(error, errbuf.data(), errbuf.size());
1410 auto name = StringRef{ent->d_name};
1412 if (name[0] == '.' || !util::iends_with_l(name, ".sct")) {
1417 path.resize(dir_path.size() + 1 + name.size());
1419 auto p = std::begin(path);
1420 p = std::copy(std::begin(dir_path), std::end(dir_path), p);
1422 std::copy(std::begin(name), std::end(name), p);
1425 auto fd = open(path.c_str(), O_RDONLY);
1428 LOG(ERROR) << opt << ": failed to read SCT from " << path << ": "
1429 << xsi_strerror(error, errbuf.data(), errbuf.size());
1433 auto closer = defer(close, fd);
1435 // 2 bytes length field for this SCT.
1436 auto len_idx = std::distance(std::begin(dst), std::end(dst));
1437 dst.insert(std::end(dst), 2, 0);
1439 // *.sct file tends to be small; around 110+ bytes.
1440 std::array<char, 256> buf;
1443 while ((nread = read(fd, buf.data(), buf.size())) == -1 && errno == EINTR)
1448 LOG(ERROR) << opt << ": failed to read SCT data from " << path << ": "
1449 << xsi_strerror(error, errbuf.data(), errbuf.size());
1457 dst.insert(std::end(dst), std::begin(buf), std::begin(buf) + nread);
1459 if (dst.size() > MAX_SCT_EXT_LEN) {
1460 LOG(ERROR) << opt << ": the concatenated SCT data from " << dir_path
1461 << " is too large. Max " << MAX_SCT_EXT_LEN;
1466 auto len = dst.size() - len_idx - 2;
1469 dst.resize(dst.size() - 2);
1473 dst[len_idx] = len >> 8;
1474 dst[len_idx + 1] = len;
1477 auto len = dst.size() - len_idx - 2;
1480 dst.resize(dst.size() - 2);
1484 dst[len_idx] = len >> 8;
1485 dst[len_idx + 1] = len;
1491 #if !LIBRESSL_LEGACY_API
1493 // Reads PSK secrets from path, and parses each line. The result is
1494 // directly stored into config->tls.psk_secrets. This function
1495 // returns 0 if it succeeds, or -1.
1496 int parse_psk_secrets(Config *config, const StringRef &path) {
1497 auto &tlsconf = config->tls;
1499 std::ifstream f(path.c_str(), std::ios::binary);
1501 LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": could not open file " << path;
1507 while (std::getline(f, line)) {
1509 if (line.empty() || line[0] == '#') {
1513 auto sep_it = std::find(std::begin(line), std::end(line), ':');
1514 if (sep_it == std::end(line)) {
1515 LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
1516 << ": could not fine separator at line " << lineno;
1520 if (sep_it == std::begin(line)) {
1521 LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty identity at line "
1526 if (sep_it + 1 == std::end(line)) {
1527 LOG(ERROR) << SHRPX_OPT_PSK_SECRETS << ": empty secret at line "
1532 if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
1533 LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
1534 << ": secret must be hex string at line " << lineno;
1539 make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
1542 util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
1544 auto rv = tlsconf.psk_secrets.emplace(identity, secret);
1546 LOG(ERROR) << SHRPX_OPT_PSK_SECRETS
1547 << ": identity has already been registered at line " << lineno;
1555 #endif // !LIBRESSL_LEGACY_API
1557 #if !LIBRESSL_LEGACY_API
1559 // Reads PSK secrets from path, and parses each line. The result is
1560 // directly stored into config->tls.client.psk. This function returns
1561 // 0 if it succeeds, or -1.
1562 int parse_client_psk_secrets(Config *config, const StringRef &path) {
1563 auto &tlsconf = config->tls;
1565 std::ifstream f(path.c_str(), std::ios::binary);
1567 LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
1574 while (std::getline(f, line)) {
1576 if (line.empty() || line[0] == '#') {
1580 auto sep_it = std::find(std::begin(line), std::end(line), ':');
1581 if (sep_it == std::end(line)) {
1582 LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
1583 << ": could not find separator at line " << lineno;
1587 if (sep_it == std::begin(line)) {
1588 LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
1593 if (sep_it + 1 == std::end(line)) {
1594 LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
1599 if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
1600 LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
1601 << ": secret must be hex string at line " << lineno;
1605 tlsconf.client.psk.identity =
1606 make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
1608 tlsconf.client.psk.secret =
1609 util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
1617 #endif // !LIBRESSL_LEGACY_API
1619 // generated by gennghttpxfun.py
1620 int option_lookup_token(const char *name, size_t namelen) {
1625 if (util::strieq_l("con", name, 3)) {
1626 return SHRPX_OPTID_CONF;
1630 if (util::strieq_l("use", name, 3)) {
1631 return SHRPX_OPTID_USER;
1639 if (util::strieq_l("no-vi", name, 5)) {
1640 return SHRPX_OPTID_NO_VIA;
1644 if (util::strieq_l("altsv", name, 5)) {
1645 return SHRPX_OPTID_ALTSVC;
1649 if (util::strieq_l("daemo", name, 5)) {
1650 return SHRPX_OPTID_DAEMON;
1654 if (util::strieq_l("cacer", name, 5)) {
1655 return SHRPX_OPTID_CACERT;
1657 if (util::strieq_l("clien", name, 5)) {
1658 return SHRPX_OPTID_CLIENT;
1666 if (util::strieq_l("backen", name, 6)) {
1667 return SHRPX_OPTID_BACKEND;
1671 if (util::strieq_l("includ", name, 6)) {
1672 return SHRPX_OPTID_INCLUDE;
1676 if (util::strieq_l("backlo", name, 6)) {
1677 return SHRPX_OPTID_BACKLOG;
1679 if (util::strieq_l("paddin", name, 6)) {
1680 return SHRPX_OPTID_PADDING;
1684 if (util::strieq_l("no-ocs", name, 6)) {
1685 return SHRPX_OPTID_NO_OCSP;
1689 if (util::strieq_l("cipher", name, 6)) {
1690 return SHRPX_OPTID_CIPHERS;
1692 if (util::strieq_l("worker", name, 6)) {
1693 return SHRPX_OPTID_WORKERS;
1697 if (util::strieq_l("subcer", name, 6)) {
1698 return SHRPX_OPTID_SUBCERT;
1706 if (util::strieq_l("fronten", name, 7)) {
1707 return SHRPX_OPTID_FRONTEND;
1711 if (util::strieq_l("insecur", name, 7)) {
1712 return SHRPX_OPTID_INSECURE;
1714 if (util::strieq_l("pid-fil", name, 7)) {
1715 return SHRPX_OPTID_PID_FILE;
1719 if (util::strieq_l("fastope", name, 7)) {
1720 return SHRPX_OPTID_FASTOPEN;
1724 if (util::strieq_l("npn-lis", name, 7)) {
1725 return SHRPX_OPTID_NPN_LIST;
1733 if (util::strieq_l("no-kqueu", name, 8)) {
1734 return SHRPX_OPTID_NO_KQUEUE;
1736 if (util::strieq_l("read-rat", name, 8)) {
1737 return SHRPX_OPTID_READ_RATE;
1741 if (util::strieq_l("log-leve", name, 8)) {
1742 return SHRPX_OPTID_LOG_LEVEL;
1750 if (util::strieq_l("error-pag", name, 9)) {
1751 return SHRPX_OPTID_ERROR_PAGE;
1753 if (util::strieq_l("mruby-fil", name, 9)) {
1754 return SHRPX_OPTID_MRUBY_FILE;
1756 if (util::strieq_l("write-rat", name, 9)) {
1757 return SHRPX_OPTID_WRITE_RATE;
1761 if (util::strieq_l("read-burs", name, 9)) {
1762 return SHRPX_OPTID_READ_BURST;
1770 if (util::strieq_l("server-nam", name, 10)) {
1771 return SHRPX_OPTID_SERVER_NAME;
1775 if (util::strieq_l("tls-sct-di", name, 10)) {
1776 return SHRPX_OPTID_TLS_SCT_DIR;
1780 if (util::strieq_l("backend-tl", name, 10)) {
1781 return SHRPX_OPTID_BACKEND_TLS;
1783 if (util::strieq_l("ecdh-curve", name, 10)) {
1784 return SHRPX_OPTID_ECDH_CURVES;
1786 if (util::strieq_l("psk-secret", name, 10)) {
1787 return SHRPX_OPTID_PSK_SECRETS;
1791 if (util::strieq_l("write-burs", name, 10)) {
1792 return SHRPX_OPTID_WRITE_BURST;
1796 if (util::strieq_l("dns-max-tr", name, 10)) {
1797 return SHRPX_OPTID_DNS_MAX_TRY;
1799 if (util::strieq_l("http2-prox", name, 10)) {
1800 return SHRPX_OPTID_HTTP2_PROXY;
1808 if (util::strieq_l("backend-ipv", name, 11)) {
1809 return SHRPX_OPTID_BACKEND_IPV4;
1813 if (util::strieq_l("backend-ipv", name, 11)) {
1814 return SHRPX_OPTID_BACKEND_IPV6;
1818 if (util::strieq_l("host-rewrit", name, 11)) {
1819 return SHRPX_OPTID_HOST_REWRITE;
1821 if (util::strieq_l("http2-bridg", name, 11)) {
1822 return SHRPX_OPTID_HTTP2_BRIDGE;
1826 if (util::strieq_l("ocsp-startu", name, 11)) {
1827 return SHRPX_OPTID_OCSP_STARTUP;
1831 if (util::strieq_l("client-prox", name, 11)) {
1832 return SHRPX_OPTID_CLIENT_PROXY;
1834 if (util::strieq_l("forwarded-b", name, 11)) {
1835 return SHRPX_OPTID_FORWARDED_BY;
1843 if (util::strieq_l("add-forwarde", name, 12)) {
1844 return SHRPX_OPTID_ADD_FORWARDED;
1846 if (util::strieq_l("single-threa", name, 12)) {
1847 return SHRPX_OPTID_SINGLE_THREAD;
1851 if (util::strieq_l("dh-param-fil", name, 12)) {
1852 return SHRPX_OPTID_DH_PARAM_FILE;
1854 if (util::strieq_l("errorlog-fil", name, 12)) {
1855 return SHRPX_OPTID_ERRORLOG_FILE;
1857 if (util::strieq_l("rlimit-nofil", name, 12)) {
1858 return SHRPX_OPTID_RLIMIT_NOFILE;
1862 if (util::strieq_l("forwarded-fo", name, 12)) {
1863 return SHRPX_OPTID_FORWARDED_FOR;
1867 if (util::strieq_l("tls13-cipher", name, 12)) {
1868 return SHRPX_OPTID_TLS13_CIPHERS;
1872 if (util::strieq_l("verify-clien", name, 12)) {
1873 return SHRPX_OPTID_VERIFY_CLIENT;
1881 if (util::strieq_l("accesslog-fil", name, 13)) {
1882 return SHRPX_OPTID_ACCESSLOG_FILE;
1886 if (util::strieq_l("no-server-pus", name, 13)) {
1887 return SHRPX_OPTID_NO_SERVER_PUSH;
1891 if (util::strieq_l("no-verify-ocs", name, 13)) {
1892 return SHRPX_OPTID_NO_VERIFY_OCSP;
1896 if (util::strieq_l("backend-no-tl", name, 13)) {
1897 return SHRPX_OPTID_BACKEND_NO_TLS;
1899 if (util::strieq_l("client-cipher", name, 13)) {
1900 return SHRPX_OPTID_CLIENT_CIPHERS;
1902 if (util::strieq_l("single-proces", name, 13)) {
1903 return SHRPX_OPTID_SINGLE_PROCESS;
1907 if (util::strieq_l("tls-proto-lis", name, 13)) {
1908 return SHRPX_OPTID_TLS_PROTO_LIST;
1916 if (util::strieq_l("no-host-rewrit", name, 14)) {
1917 return SHRPX_OPTID_NO_HOST_REWRITE;
1921 if (util::strieq_l("errorlog-syslo", name, 14)) {
1922 return SHRPX_OPTID_ERRORLOG_SYSLOG;
1926 if (util::strieq_l("frontend-no-tl", name, 14)) {
1927 return SHRPX_OPTID_FRONTEND_NO_TLS;
1931 if (util::strieq_l("syslog-facilit", name, 14)) {
1932 return SHRPX_OPTID_SYSLOG_FACILITY;
1940 if (util::strieq_l("certificate-fil", name, 15)) {
1941 return SHRPX_OPTID_CERTIFICATE_FILE;
1943 if (util::strieq_l("client-cert-fil", name, 15)) {
1944 return SHRPX_OPTID_CLIENT_CERT_FILE;
1946 if (util::strieq_l("private-key-fil", name, 15)) {
1947 return SHRPX_OPTID_PRIVATE_KEY_FILE;
1949 if (util::strieq_l("worker-read-rat", name, 15)) {
1950 return SHRPX_OPTID_WORKER_READ_RATE;
1954 if (util::strieq_l("accesslog-syslo", name, 15)) {
1955 return SHRPX_OPTID_ACCESSLOG_SYSLOG;
1959 if (util::strieq_l("accesslog-forma", name, 15)) {
1960 return SHRPX_OPTID_ACCESSLOG_FORMAT;
1968 if (util::strieq_l("no-server-rewrit", name, 16)) {
1969 return SHRPX_OPTID_NO_SERVER_REWRITE;
1971 if (util::strieq_l("worker-write-rat", name, 16)) {
1972 return SHRPX_OPTID_WORKER_WRITE_RATE;
1976 if (util::strieq_l("backend-http1-tl", name, 16)) {
1977 return SHRPX_OPTID_BACKEND_HTTP1_TLS;
1979 if (util::strieq_l("max-header-field", name, 16)) {
1980 return SHRPX_OPTID_MAX_HEADER_FIELDS;
1984 if (util::strieq_l("dns-cache-timeou", name, 16)) {
1985 return SHRPX_OPTID_DNS_CACHE_TIMEOUT;
1987 if (util::strieq_l("worker-read-burs", name, 16)) {
1988 return SHRPX_OPTID_WORKER_READ_BURST;
1996 if (util::strieq_l("tls-max-early-dat", name, 17)) {
1997 return SHRPX_OPTID_TLS_MAX_EARLY_DATA;
2001 if (util::strieq_l("add-request-heade", name, 17)) {
2002 return SHRPX_OPTID_ADD_REQUEST_HEADER;
2006 if (util::strieq_l("client-psk-secret", name, 17)) {
2007 return SHRPX_OPTID_CLIENT_PSK_SECRETS;
2011 if (util::strieq_l("dns-lookup-timeou", name, 17)) {
2012 return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
2014 if (util::strieq_l("worker-write-burs", name, 17)) {
2015 return SHRPX_OPTID_WORKER_WRITE_BURST;
2023 if (util::strieq_l("no-location-rewrit", name, 18)) {
2024 return SHRPX_OPTID_NO_LOCATION_REWRITE;
2026 if (util::strieq_l("tls-ticket-key-fil", name, 18)) {
2027 return SHRPX_OPTID_TLS_TICKET_KEY_FILE;
2031 if (util::strieq_l("backend-max-backof", name, 18)) {
2032 return SHRPX_OPTID_BACKEND_MAX_BACKOFF;
2036 if (util::strieq_l("add-response-heade", name, 18)) {
2037 return SHRPX_OPTID_ADD_RESPONSE_HEADER;
2039 if (util::strieq_l("add-x-forwarded-fo", name, 18)) {
2040 return SHRPX_OPTID_ADD_X_FORWARDED_FOR;
2042 if (util::strieq_l("header-field-buffe", name, 18)) {
2043 return SHRPX_OPTID_HEADER_FIELD_BUFFER;
2047 if (util::strieq_l("redirect-https-por", name, 18)) {
2048 return SHRPX_OPTID_REDIRECT_HTTPS_PORT;
2050 if (util::strieq_l("stream-read-timeou", name, 18)) {
2051 return SHRPX_OPTID_STREAM_READ_TIMEOUT;
2059 if (util::strieq_l("frontend-frame-debu", name, 19)) {
2060 return SHRPX_OPTID_FRONTEND_FRAME_DEBUG;
2064 if (util::strieq_l("ocsp-update-interva", name, 19)) {
2065 return SHRPX_OPTID_OCSP_UPDATE_INTERVAL;
2069 if (util::strieq_l("tls13-client-cipher", name, 19)) {
2070 return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
2074 if (util::strieq_l("backend-read-timeou", name, 19)) {
2075 return SHRPX_OPTID_BACKEND_READ_TIMEOUT;
2077 if (util::strieq_l("stream-write-timeou", name, 19)) {
2078 return SHRPX_OPTID_STREAM_WRITE_TIMEOUT;
2080 if (util::strieq_l("verify-client-cacer", name, 19)) {
2081 return SHRPX_OPTID_VERIFY_CLIENT_CACERT;
2085 if (util::strieq_l("api-max-request-bod", name, 19)) {
2086 return SHRPX_OPTID_API_MAX_REQUEST_BODY;
2094 if (util::strieq_l("backend-tls-sni-fiel", name, 20)) {
2095 return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
2099 if (util::strieq_l("accept-proxy-protoco", name, 20)) {
2100 return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
2104 if (util::strieq_l("tls-max-proto-versio", name, 20)) {
2105 return SHRPX_OPTID_TLS_MAX_PROTO_VERSION;
2107 if (util::strieq_l("tls-min-proto-versio", name, 20)) {
2108 return SHRPX_OPTID_TLS_MIN_PROTO_VERSION;
2112 if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
2113 return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
2117 if (util::strieq_l("frontend-max-request", name, 20)) {
2118 return SHRPX_OPTID_FRONTEND_MAX_REQUESTS;
2122 if (util::strieq_l("backend-write-timeou", name, 20)) {
2123 return SHRPX_OPTID_BACKEND_WRITE_TIMEOUT;
2125 if (util::strieq_l("frontend-read-timeou", name, 20)) {
2126 return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
2130 if (util::strieq_l("accesslog-write-earl", name, 20)) {
2131 return SHRPX_OPTID_ACCESSLOG_WRITE_EARLY;
2139 if (util::strieq_l("backend-http-proxy-ur", name, 21)) {
2140 return SHRPX_OPTID_BACKEND_HTTP_PROXY_URI;
2144 if (util::strieq_l("backend-request-buffe", name, 21)) {
2145 return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
2149 if (util::strieq_l("frontend-write-timeou", name, 21)) {
2150 return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT;
2154 if (util::strieq_l("backend-address-famil", name, 21)) {
2155 return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY;
2163 if (util::strieq_l("client-private-key-fil", name, 22)) {
2164 return SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE;
2166 if (util::strieq_l("private-key-passwd-fil", name, 22)) {
2167 return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
2171 if (util::strieq_l("backend-response-buffe", name, 22)) {
2172 return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
2176 if (util::strieq_l("backend-connect-timeou", name, 22)) {
2177 return SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT;
2185 if (util::strieq_l("strip-incoming-forwarde", name, 23)) {
2186 return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
2188 if (util::strieq_l("tls-ticket-key-memcache", name, 23)) {
2189 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED;
2193 if (util::strieq_l("fetch-ocsp-response-fil", name, 23)) {
2194 return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
2198 if (util::strieq_l("no-add-x-forwarded-prot", name, 23)) {
2199 return SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO;
2203 if (util::strieq_l("listener-disable-timeou", name, 23)) {
2204 return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
2206 if (util::strieq_l("tls-dyn-rec-idle-timeou", name, 23)) {
2207 return SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT;
2215 if (util::strieq_l("backend-http2-window-siz", name, 24)) {
2216 return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
2220 if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
2221 return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING;
2225 if (util::strieq_l("backend-http2-window-bit", name, 24)) {
2226 return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS;
2228 if (util::strieq_l("max-request-header-field", name, 24)) {
2229 return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
2237 if (util::strieq_l("tls-no-postpone-early-dat", name, 25)) {
2238 return SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA;
2242 if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
2243 return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
2247 if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
2248 return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS;
2250 if (util::strieq_l("max-response-header-field", name, 25)) {
2251 return SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS;
2255 if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
2256 return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
2258 if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
2259 return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
2267 if (util::strieq_l("tls-session-cache-memcache", name, 26)) {
2268 return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
2272 if (util::strieq_l("request-header-field-buffe", name, 26)) {
2273 return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
2277 if (util::strieq_l("worker-frontend-connection", name, 26)) {
2278 return SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS;
2282 if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
2283 return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
2285 if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
2286 return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
2294 if (util::strieq_l("no-strip-incoming-early-dat", name, 27)) {
2295 return SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA;
2299 if (util::strieq_l("tls-dyn-rec-warmup-threshol", name, 27)) {
2300 return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
2304 if (util::strieq_l("response-header-field-buffe", name, 27)) {
2305 return SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER;
2309 if (util::strieq_l("http2-max-concurrent-stream", name, 27)) {
2310 return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS;
2312 if (util::strieq_l("tls-ticket-key-memcached-tl", name, 27)) {
2313 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS;
2317 if (util::strieq_l("backend-connections-per-hos", name, 27)) {
2318 return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST;
2326 if (util::strieq_l("verify-client-tolerate-expire", name, 29)) {
2327 return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
2331 if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
2332 return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
2334 if (util::strieq_l("strip-incoming-x-forwarded-fo", name, 29)) {
2335 return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
2339 if (util::strieq_l("backend-http2-settings-timeou", name, 29)) {
2340 return SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT;
2348 if (util::strieq_l("tls-session-cache-memcached-tl", name, 30)) {
2349 return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS;
2353 if (util::strieq_l("frontend-http2-settings-timeou", name, 30)) {
2354 return SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT;
2362 if (util::strieq_l("backend-connections-per-fronten", name, 31)) {
2363 return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND;
2371 if (util::strieq_l("tls-ticket-key-memcached-interva", name, 32)) {
2372 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL;
2374 if (util::strieq_l("tls-ticket-key-memcached-max-fai", name, 32)) {
2375 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
2379 if (util::strieq_l("client-no-http2-cipher-black-lis", name, 32)) {
2380 return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
2388 if (util::strieq_l("tls-ticket-key-memcached-cert-fil", name, 33)) {
2389 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE;
2393 if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) {
2394 return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER;
2398 if (util::strieq_l("backend-http1-connections-per-hos", name, 33)) {
2399 return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST;
2403 if (util::strieq_l("tls-ticket-key-memcached-max-retr", name, 33)) {
2404 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY;
2412 if (util::strieq_l("frontend-http2-optimize-window-siz", name, 34)) {
2413 return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE;
2417 if (util::strieq_l("no-strip-incoming-x-forwarded-prot", name, 34)) {
2418 return SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO;
2422 if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
2423 return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
2431 if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
2432 return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
2436 if (util::strieq_l("backend-http2-connections-per-worke", name, 35)) {
2437 return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER;
2441 if (util::strieq_l("backend-http2-connection-window-bit", name, 35)) {
2442 return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS;
2444 if (util::strieq_l("backend-http2-max-concurrent-stream", name, 35)) {
2445 return SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS;
2453 if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) {
2454 return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
2456 if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
2457 return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
2461 if (util::strieq_l("frontend-http2-connection-window-bit", name, 36)) {
2462 return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS;
2464 if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) {
2465 return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
2473 if (util::strieq_l("backend-http1-connections-per-fronten", name, 37)) {
2474 return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND;
2482 if (util::strieq_l("tls-ticket-key-memcached-address-famil", name, 38)) {
2483 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY;
2491 if (util::strieq_l("backend-http2-decoder-dynamic-table-siz", name, 39)) {
2492 return SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE;
2494 if (util::strieq_l("backend-http2-encoder-dynamic-table-siz", name, 39)) {
2495 return SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
2503 if (util::strieq_l("frontend-http2-decoder-dynamic-table-siz", name,
2505 return SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE;
2507 if (util::strieq_l("frontend-http2-encoder-dynamic-table-siz", name,
2509 return SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE;
2511 if (util::strieq_l("frontend-http2-optimize-write-buffer-siz", name,
2513 return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE;
2515 if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
2517 return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
2525 if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
2527 return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY;
2535 if (util::strieq_l("tls-session-cache-memcached-private-key-fil", name,
2537 return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE;
2546 int parse_config(Config *config, const StringRef &opt, const StringRef &optarg,
2547 std::set<StringRef> &included_set,
2548 std::map<StringRef, size_t> &pattern_addr_indexer) {
2549 auto optid = option_lookup_token(opt.c_str(), opt.size());
2550 return parse_config(config, optid, opt, optarg, included_set,
2551 pattern_addr_indexer);
2554 int parse_config(Config *config, int optid, const StringRef &opt,
2555 const StringRef &optarg, std::set<StringRef> &included_set,
2556 std::map<StringRef, size_t> &pattern_addr_indexer) {
2557 std::array<char, STRERROR_BUFSIZE> errbuf;
2558 char host[NI_MAXHOST];
2562 case SHRPX_OPTID_BACKEND: {
2563 auto &downstreamconf = *config->conn.downstream;
2564 auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
2566 DownstreamAddrConfig addr{};
2567 if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
2568 auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
2570 make_string_ref(downstreamconf.balloc, StringRef{path, addr_end});
2571 addr.host_unix = true;
2573 if (split_host_port(host, sizeof(host), &port,
2574 StringRef{std::begin(optarg), addr_end}, opt) == -1) {
2578 addr.host = make_string_ref(downstreamconf.balloc, StringRef{host});
2582 auto mapping = addr_end == std::end(optarg) ? addr_end : addr_end + 1;
2583 auto mapping_end = std::find(mapping, std::end(optarg), ';');
2586 mapping_end == std::end(optarg) ? mapping_end : mapping_end + 1;
2588 if (parse_mapping(config, addr, pattern_addr_indexer,
2589 StringRef{mapping, mapping_end},
2590 StringRef{params, std::end(optarg)}) != 0) {
2596 case SHRPX_OPTID_FRONTEND: {
2597 auto &listenerconf = config->conn.listener;
2598 auto &apiconf = config->api;
2600 auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
2601 auto src_params = StringRef{addr_end, std::end(optarg)};
2603 UpstreamParams params{};
2606 if (parse_upstream_params(params, src_params) != 0) {
2610 if (params.sni_fwd && !params.tls) {
2611 LOG(ERROR) << "frontend: sni_fwd requires tls";
2615 UpstreamAddr addr{};
2617 addr.tls = params.tls;
2618 addr.sni_fwd = params.sni_fwd;
2619 addr.alt_mode = params.alt_mode;
2620 addr.accept_proxy_protocol = params.proxyproto;
2622 if (addr.alt_mode == UpstreamAltMode::API) {
2623 apiconf.enabled = true;
2626 if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
2627 auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
2628 addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
2629 addr.host_unix = true;
2631 listenerconf.addrs.push_back(std::move(addr));
2636 if (split_host_port(host, sizeof(host), &port,
2637 StringRef{std::begin(optarg), addr_end}, opt) == -1) {
2641 addr.host = make_string_ref(config->balloc, StringRef{host});
2644 if (util::numeric_host(host, AF_INET)) {
2645 addr.family = AF_INET;
2646 listenerconf.addrs.push_back(std::move(addr));
2650 if (util::numeric_host(host, AF_INET6)) {
2651 addr.family = AF_INET6;
2652 listenerconf.addrs.push_back(std::move(addr));
2656 addr.family = AF_INET;
2657 listenerconf.addrs.push_back(addr);
2659 addr.family = AF_INET6;
2660 listenerconf.addrs.push_back(std::move(addr));
2664 case SHRPX_OPTID_WORKERS:
2666 LOG(WARN) << "Threading disabled at build time, no threads created.";
2669 return parse_uint(&config->num_worker, opt, optarg);
2670 #endif // !NOTHREADS
2671 case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: {
2672 LOG(WARN) << opt << ": deprecated. Use "
2673 << SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS << " and "
2674 << SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS << " instead.";
2676 if (parse_uint(&n, opt, optarg) != 0) {
2679 auto &http2conf = config->http2;
2680 http2conf.upstream.max_concurrent_streams = n;
2681 http2conf.downstream.max_concurrent_streams = n;
2685 case SHRPX_OPTID_LOG_LEVEL: {
2686 auto level = Log::get_severity_level_by_name(optarg);
2688 LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
2691 config->logging.severity = level;
2695 case SHRPX_OPTID_DAEMON:
2696 config->daemon = util::strieq_l("yes", optarg);
2699 case SHRPX_OPTID_HTTP2_PROXY:
2700 config->http2_proxy = util::strieq_l("yes", optarg);
2703 case SHRPX_OPTID_HTTP2_BRIDGE:
2705 << ": deprecated. Use backend=<addr>,<port>;;proto=h2;tls";
2707 case SHRPX_OPTID_CLIENT_PROXY:
2710 << ": deprecated. Use http2-proxy, frontend=<addr>,<port>;no-tls "
2711 "and backend=<addr>,<port>;;proto=h2;tls";
2713 case SHRPX_OPTID_ADD_X_FORWARDED_FOR:
2714 config->http.xff.add = util::strieq_l("yes", optarg);
2717 case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR:
2718 config->http.xff.strip_incoming = util::strieq_l("yes", optarg);
2721 case SHRPX_OPTID_NO_VIA:
2722 config->http.no_via = util::strieq_l("yes", optarg);
2725 case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT:
2726 return parse_duration(&config->conn.upstream.timeout.http2_read, opt,
2728 case SHRPX_OPTID_FRONTEND_READ_TIMEOUT:
2729 return parse_duration(&config->conn.upstream.timeout.read, opt, optarg);
2730 case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT:
2731 return parse_duration(&config->conn.upstream.timeout.write, opt, optarg);
2732 case SHRPX_OPTID_BACKEND_READ_TIMEOUT:
2733 return parse_duration(&config->conn.downstream->timeout.read, opt, optarg);
2734 case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT:
2735 return parse_duration(&config->conn.downstream->timeout.write, opt, optarg);
2736 case SHRPX_OPTID_BACKEND_CONNECT_TIMEOUT:
2737 return parse_duration(&config->conn.downstream->timeout.connect, opt,
2739 case SHRPX_OPTID_STREAM_READ_TIMEOUT:
2740 return parse_duration(&config->http2.timeout.stream_read, opt, optarg);
2741 case SHRPX_OPTID_STREAM_WRITE_TIMEOUT:
2742 return parse_duration(&config->http2.timeout.stream_write, opt, optarg);
2743 case SHRPX_OPTID_ACCESSLOG_FILE:
2744 config->logging.access.file = make_string_ref(config->balloc, optarg);
2747 case SHRPX_OPTID_ACCESSLOG_SYSLOG:
2748 config->logging.access.syslog = util::strieq_l("yes", optarg);
2751 case SHRPX_OPTID_ACCESSLOG_FORMAT:
2752 config->logging.access.format = parse_log_format(config->balloc, optarg);
2755 case SHRPX_OPTID_ERRORLOG_FILE:
2756 config->logging.error.file = make_string_ref(config->balloc, optarg);
2759 case SHRPX_OPTID_ERRORLOG_SYSLOG:
2760 config->logging.error.syslog = util::strieq_l("yes", optarg);
2763 case SHRPX_OPTID_FASTOPEN:
2764 return parse_uint(&config->conn.listener.fastopen, opt, optarg);
2765 case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT:
2766 return parse_duration(&config->conn.downstream->timeout.idle_read, opt,
2768 case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS:
2769 case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: {
2770 LOG(WARN) << opt << ": deprecated. Use "
2771 << (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS
2772 ? SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE
2773 : SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE);
2776 if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) {
2777 resp = &config->http2.upstream.window_size;
2779 resp = &config->http2.downstream.window_size;
2786 if (parse_uint(&n, opt, optarg) != 0) {
2792 << ": specify the integer in the range [0, 30], inclusive";
2796 // Make 16 bits to the HTTP/2 default 64KiB - 1. This is the same
2797 // behaviour of previous code.
2798 *resp = (1 << n) - 1;
2802 case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS:
2803 case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: {
2804 LOG(WARN) << opt << ": deprecated. Use "
2805 << (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS
2806 ? SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE
2807 : SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE);
2810 if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) {
2811 resp = &config->http2.upstream.connection_window_size;
2813 resp = &config->http2.downstream.connection_window_size;
2820 if (parse_uint(&n, opt, optarg) != 0) {
2824 if (n < 16 || n >= 31) {
2826 << ": specify the integer in the range [16, 30], inclusive";
2830 *resp = (1 << n) - 1;
2834 case SHRPX_OPTID_FRONTEND_NO_TLS:
2835 LOG(WARN) << opt << ": deprecated. Use no-tls keyword in "
2836 << SHRPX_OPT_FRONTEND;
2838 case SHRPX_OPTID_BACKEND_NO_TLS:
2840 << ": deprecated. backend connection is not encrypted by "
2841 "default. See also "
2842 << SHRPX_OPT_BACKEND_TLS;
2844 case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
2846 << ": deprecated. Use sni keyword in --backend option. "
2847 "For now, all sni values of all backends are "
2848 "overridden by the given value "
2850 config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
2853 case SHRPX_OPTID_PID_FILE:
2854 config->pid_file = make_string_ref(config->balloc, optarg);
2857 case SHRPX_OPTID_USER: {
2858 auto pwd = getpwnam(optarg.c_str());
2860 LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": "
2861 << xsi_strerror(errno, errbuf.data(), errbuf.size());
2864 config->user = make_string_ref(config->balloc, StringRef{pwd->pw_name});
2865 config->uid = pwd->pw_uid;
2866 config->gid = pwd->pw_gid;
2870 case SHRPX_OPTID_PRIVATE_KEY_FILE:
2871 config->tls.private_key_file = make_string_ref(config->balloc, optarg);
2874 case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
2875 auto passwd = read_passwd_from_file(opt, optarg);
2876 if (passwd.empty()) {
2877 LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
2880 config->tls.private_key_passwd =
2881 make_string_ref(config->balloc, StringRef{passwd});
2885 case SHRPX_OPTID_CERTIFICATE_FILE:
2886 config->tls.cert_file = make_string_ref(config->balloc, optarg);
2889 case SHRPX_OPTID_DH_PARAM_FILE:
2890 config->tls.dh_param_file = make_string_ref(config->balloc, optarg);
2893 case SHRPX_OPTID_SUBCERT: {
2894 auto end_keys = std::find(std::begin(optarg), std::end(optarg), ';');
2895 auto src_params = StringRef{end_keys, std::end(optarg)};
2897 SubcertParams params;
2898 if (parse_subcert_params(params, src_params) != 0) {
2902 std::vector<uint8_t> sct_data;
2904 if (!params.sct_dir.empty()) {
2905 // Make sure that dir_path is NULL terminated string.
2906 if (read_tls_sct_from_dir(sct_data, opt,
2907 StringRef{params.sct_dir.str()}) != 0) {
2912 // Private Key file and certificate file separated by ':'.
2913 auto sp = std::find(std::begin(optarg), end_keys, ':');
2914 if (sp == end_keys) {
2915 LOG(ERROR) << opt << ": missing ':' in "
2916 << StringRef{std::begin(optarg), end_keys};
2920 auto private_key_file = StringRef{std::begin(optarg), sp};
2922 if (private_key_file.empty()) {
2923 LOG(ERROR) << opt << ": missing private key file: "
2924 << StringRef{std::begin(optarg), end_keys};
2928 auto cert_file = StringRef{sp + 1, end_keys};
2930 if (cert_file.empty()) {
2931 LOG(ERROR) << opt << ": missing certificate file: "
2932 << StringRef{std::begin(optarg), end_keys};
2936 config->tls.subcerts.emplace_back(
2937 make_string_ref(config->balloc, private_key_file),
2938 make_string_ref(config->balloc, cert_file), std::move(sct_data));
2942 case SHRPX_OPTID_SYSLOG_FACILITY: {
2943 int facility = int_syslog_facility(optarg);
2944 if (facility == -1) {
2945 LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg;
2948 config->logging.syslog_facility = facility;
2952 case SHRPX_OPTID_BACKLOG:
2953 return parse_uint(&config->conn.listener.backlog, opt, optarg);
2954 case SHRPX_OPTID_CIPHERS:
2955 config->tls.ciphers = make_string_ref(config->balloc, optarg);
2958 case SHRPX_OPTID_TLS13_CIPHERS:
2959 config->tls.tls13_ciphers = make_string_ref(config->balloc, optarg);
2962 case SHRPX_OPTID_CLIENT:
2964 << ": deprecated. Use frontend=<addr>,<port>;no-tls, "
2965 "backend=<addr>,<port>;;proto=h2;tls";
2967 case SHRPX_OPTID_INSECURE:
2968 config->tls.insecure = util::strieq_l("yes", optarg);
2971 case SHRPX_OPTID_CACERT:
2972 config->tls.cacert = make_string_ref(config->balloc, optarg);
2975 case SHRPX_OPTID_BACKEND_IPV4:
2977 << ": deprecated. Use backend-address-family=IPv4 instead.";
2979 config->conn.downstream->family = AF_INET;
2982 case SHRPX_OPTID_BACKEND_IPV6:
2984 << ": deprecated. Use backend-address-family=IPv6 instead.";
2986 config->conn.downstream->family = AF_INET6;
2989 case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: {
2990 auto &proxy = config->downstream_http_proxy;
2991 // Reset here so that multiple option occurrence does not merge
2994 // parse URI and get hostname, port and optionally userinfo.
2995 http_parser_url u{};
2996 int rv = http_parser_parse_url(optarg.c_str(), optarg.size(), 0, &u);
2998 if (u.field_set & UF_USERINFO) {
2999 auto uf = util::get_uri_field(optarg.c_str(), u, UF_USERINFO);
3000 // Surprisingly, u.field_set & UF_USERINFO is nonzero even if
3001 // userinfo component is empty string.
3003 proxy.userinfo = util::percent_decode(config->balloc, uf);
3006 if (u.field_set & UF_HOST) {
3007 proxy.host = make_string_ref(
3008 config->balloc, util::get_uri_field(optarg.c_str(), u, UF_HOST));
3010 LOG(ERROR) << opt << ": no hostname specified";
3013 if (u.field_set & UF_PORT) {
3014 proxy.port = u.port;
3016 LOG(ERROR) << opt << ": no port specified";
3020 LOG(ERROR) << opt << ": parse error";
3026 case SHRPX_OPTID_READ_RATE:
3027 return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.rate, opt,
3029 case SHRPX_OPTID_READ_BURST:
3030 return parse_uint_with_unit(&config->conn.upstream.ratelimit.read.burst,
3032 case SHRPX_OPTID_WRITE_RATE:
3033 return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.rate,
3035 case SHRPX_OPTID_WRITE_BURST:
3036 return parse_uint_with_unit(&config->conn.upstream.ratelimit.write.burst,
3038 case SHRPX_OPTID_WORKER_READ_RATE:
3039 LOG(WARN) << opt << ": not implemented yet";
3041 case SHRPX_OPTID_WORKER_READ_BURST:
3042 LOG(WARN) << opt << ": not implemented yet";
3044 case SHRPX_OPTID_WORKER_WRITE_RATE:
3045 LOG(WARN) << opt << ": not implemented yet";
3047 case SHRPX_OPTID_WORKER_WRITE_BURST:
3048 LOG(WARN) << opt << ": not implemented yet";
3050 case SHRPX_OPTID_NPN_LIST: {
3051 auto list = util::split_str(optarg, ',');
3052 config->tls.npn_list.resize(list.size());
3053 for (size_t i = 0; i < list.size(); ++i) {
3054 config->tls.npn_list[i] = make_string_ref(config->balloc, list[i]);
3059 case SHRPX_OPTID_TLS_PROTO_LIST: {
3061 << ": deprecated. Use tls-min-proto-version and "
3062 "tls-max-proto-version instead.";
3063 auto list = util::split_str(optarg, ',');
3064 config->tls.tls_proto_list.resize(list.size());
3065 for (size_t i = 0; i < list.size(); ++i) {
3066 config->tls.tls_proto_list[i] = make_string_ref(config->balloc, list[i]);
3071 case SHRPX_OPTID_VERIFY_CLIENT:
3072 config->tls.client_verify.enabled = util::strieq_l("yes", optarg);
3075 case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
3076 config->tls.client_verify.cacert = make_string_ref(config->balloc, optarg);
3079 case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
3080 config->tls.client.private_key_file =
3081 make_string_ref(config->balloc, optarg);
3084 case SHRPX_OPTID_CLIENT_CERT_FILE:
3085 config->tls.client.cert_file = make_string_ref(config->balloc, optarg);
3088 case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
3089 config->http2.upstream.debug.dump.request_header_file =
3090 make_string_ref(config->balloc, optarg);
3093 case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
3094 config->http2.upstream.debug.dump.response_header_file =
3095 make_string_ref(config->balloc, optarg);
3098 case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
3099 config->http2.no_cookie_crumbling = util::strieq_l("yes", optarg);
3102 case SHRPX_OPTID_FRONTEND_FRAME_DEBUG:
3103 config->http2.upstream.debug.frame_debug = util::strieq_l("yes", optarg);
3106 case SHRPX_OPTID_PADDING:
3107 return parse_uint(&config->padding, opt, optarg);
3108 case SHRPX_OPTID_ALTSVC: {
3109 auto tokens = util::split_str(optarg, ',');
3111 if (tokens.size() < 2) {
3112 // Requires at least protocol_id and port
3113 LOG(ERROR) << opt << ": too few parameters: " << optarg;
3117 if (tokens.size() > 4) {
3118 // We only need protocol_id, port, host and origin
3119 LOG(ERROR) << opt << ": too many parameters: " << optarg;
3125 if (parse_uint(&port, opt, tokens[1]) != 0) {
3130 port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
3131 LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
3137 altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
3140 altsvc.service = make_string_ref(config->balloc, tokens[1]);
3142 if (tokens.size() > 2) {
3143 altsvc.host = make_string_ref(config->balloc, tokens[2]);
3145 if (tokens.size() > 3) {
3146 altsvc.origin = make_string_ref(config->balloc, tokens[3]);
3150 config->http.altsvcs.push_back(std::move(altsvc));
3154 case SHRPX_OPTID_ADD_REQUEST_HEADER:
3155 case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
3156 auto p = parse_header(config->balloc, optarg);
3157 if (p.name.empty()) {
3158 LOG(ERROR) << opt << ": invalid header field: " << optarg;
3161 if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) {
3162 config->http.add_request_headers.push_back(std::move(p));
3164 config->http.add_response_headers.push_back(std::move(p));
3168 case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS:
3169 return parse_uint(&config->conn.upstream.worker_connections, opt, optarg);
3170 case SHRPX_OPTID_NO_LOCATION_REWRITE:
3171 config->http.no_location_rewrite = util::strieq_l("yes", optarg);
3174 case SHRPX_OPTID_NO_HOST_REWRITE:
3175 LOG(WARN) << SHRPX_OPT_NO_HOST_REWRITE
3176 << ": deprecated. :authority and host header fields are NOT "
3177 "altered by default. To rewrite these headers, use "
3178 "--host-rewrite option.";
3181 case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST:
3183 << ": deprecated. Use backend-connections-per-host instead.";
3185 case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST: {
3188 if (parse_uint(&n, opt, optarg) != 0) {
3193 LOG(ERROR) << opt << ": specify an integer strictly more than 0";
3198 config->conn.downstream->connections_per_host = n;
3202 case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND:
3203 LOG(WARN) << opt << ": deprecated. Use "
3204 << SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead.";
3206 case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND:
3207 return parse_uint(&config->conn.downstream->connections_per_frontend, opt,
3209 case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT:
3210 return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg);
3211 case SHRPX_OPTID_TLS_TICKET_KEY_FILE:
3212 config->tls.ticket.files.emplace_back(
3213 make_string_ref(config->balloc, optarg));
3215 case SHRPX_OPTID_RLIMIT_NOFILE: {
3218 if (parse_uint(&n, opt, optarg) != 0) {
3223 LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
3228 config->rlimit_nofile = n;
3232 case SHRPX_OPTID_BACKEND_REQUEST_BUFFER:
3233 case SHRPX_OPTID_BACKEND_RESPONSE_BUFFER: {
3235 if (parse_uint_with_unit(&n, opt, optarg) != 0) {
3240 LOG(ERROR) << opt << ": specify an integer strictly more than 0";
3245 if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) {
3246 config->conn.downstream->request_buffer_size = n;
3248 config->conn.downstream->response_buffer_size = n;
3254 case SHRPX_OPTID_NO_SERVER_PUSH:
3255 config->http2.no_server_push = util::strieq_l("yes", optarg);
3258 case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER:
3259 LOG(WARN) << opt << ": deprecated.";
3261 case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
3262 config->tls.ocsp.fetch_ocsp_response_file =
3263 make_string_ref(config->balloc, optarg);
3266 case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
3267 return parse_duration(&config->tls.ocsp.update_interval, opt, optarg);
3268 case SHRPX_OPTID_NO_OCSP:
3269 config->tls.ocsp.disabled = util::strieq_l("yes", optarg);
3272 case SHRPX_OPTID_HEADER_FIELD_BUFFER:
3274 << ": deprecated. Use request-header-field-buffer instead.";
3276 case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER:
3277 return parse_uint_with_unit(&config->http.request_header_field_buffer, opt,
3279 case SHRPX_OPTID_MAX_HEADER_FIELDS:
3280 LOG(WARN) << opt << ": deprecated. Use max-request-header-fields instead.";
3282 case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS:
3283 return parse_uint(&config->http.max_request_header_fields, opt, optarg);
3284 case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER:
3285 return parse_uint_with_unit(&config->http.response_header_field_buffer, opt,
3287 case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS:
3288 return parse_uint(&config->http.max_response_header_fields, opt, optarg);
3289 case SHRPX_OPTID_INCLUDE: {
3290 if (included_set.count(optarg)) {
3291 LOG(ERROR) << opt << ": " << optarg << " has already been included";
3295 included_set.insert(optarg);
3297 load_config(config, optarg.c_str(), included_set, pattern_addr_indexer);
3298 included_set.erase(optarg);
3306 case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
3307 if (util::strieq_l("aes-128-cbc", optarg)) {
3308 config->tls.ticket.cipher = EVP_aes_128_cbc();
3309 } else if (util::strieq_l("aes-256-cbc", optarg)) {
3310 config->tls.ticket.cipher = EVP_aes_256_cbc();
3313 << ": unsupported cipher for ticket encryption: " << optarg;
3316 config->tls.ticket.cipher_given = true;
3319 case SHRPX_OPTID_HOST_REWRITE:
3320 config->http.no_host_rewrite = !util::strieq_l("yes", optarg);
3323 case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
3324 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
3325 auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
3326 auto src_params = StringRef{addr_end, std::end(optarg)};
3328 MemcachedConnectionParams params{};
3329 if (parse_memcached_connection_params(params, src_params, StringRef{opt}) !=
3334 if (split_host_port(host, sizeof(host), &port,
3335 StringRef{std::begin(optarg), addr_end}, opt) == -1) {
3340 case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: {
3341 auto &memcachedconf = config->tls.session_cache.memcached;
3342 memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
3343 memcachedconf.port = port;
3344 memcachedconf.tls = params.tls;
3347 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
3348 auto &memcachedconf = config->tls.ticket.memcached;
3349 memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
3350 memcachedconf.port = port;
3351 memcachedconf.tls = params.tls;
3358 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL:
3359 return parse_duration(&config->tls.ticket.memcached.interval, opt, optarg);
3360 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: {
3362 if (parse_uint(&n, opt, optarg) != 0) {
3367 LOG(ERROR) << opt << ": must be smaller than or equal to 30";
3371 config->tls.ticket.memcached.max_retry = n;
3374 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL:
3375 return parse_uint(&config->tls.ticket.memcached.max_fail, opt, optarg);
3376 case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: {
3378 if (parse_uint_with_unit(&n, opt, optarg) != 0) {
3382 config->tls.dyn_rec.warmup_threshold = n;
3387 case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT:
3388 return parse_duration(&config->tls.dyn_rec.idle_timeout, opt, optarg);
3390 case SHRPX_OPTID_MRUBY_FILE:
3392 config->mruby_file = make_string_ref(config->balloc, optarg);
3393 #else // !HAVE_MRUBY
3395 << ": ignored because mruby support is disabled at build time.";
3396 #endif // !HAVE_MRUBY
3398 case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
3399 LOG(WARN) << opt << ": deprecated. Use proxyproto keyword in "
3400 << SHRPX_OPT_FRONTEND << " instead.";
3401 config->conn.upstream.accept_proxy_protocol = util::strieq_l("yes", optarg);
3404 case SHRPX_OPTID_ADD_FORWARDED: {
3405 auto &fwdconf = config->http.forwarded;
3406 fwdconf.params = FORWARDED_NONE;
3407 for (const auto ¶m : util::split_str(optarg, ',')) {
3408 if (util::strieq_l("by", param)) {
3409 fwdconf.params |= FORWARDED_BY;
3412 if (util::strieq_l("for", param)) {
3413 fwdconf.params |= FORWARDED_FOR;
3416 if (util::strieq_l("host", param)) {
3417 fwdconf.params |= FORWARDED_HOST;
3420 if (util::strieq_l("proto", param)) {
3421 fwdconf.params |= FORWARDED_PROTO;
3425 LOG(ERROR) << opt << ": unknown parameter " << optarg;
3432 case SHRPX_OPTID_STRIP_INCOMING_FORWARDED:
3433 config->http.forwarded.strip_incoming = util::strieq_l("yes", optarg);
3436 case SHRPX_OPTID_FORWARDED_BY:
3437 case SHRPX_OPTID_FORWARDED_FOR: {
3438 auto type = parse_forwarded_node_type(optarg);
3440 if (type == static_cast<ForwardedNode>(-1) ||
3441 (optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) {
3442 LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string "
3447 auto &fwdconf = config->http.forwarded;
3450 case SHRPX_OPTID_FORWARDED_BY:
3451 fwdconf.by_node_type = type;
3452 if (optarg[0] == '_') {
3453 fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg);
3455 fwdconf.by_obfuscated = StringRef::from_lit("");
3458 case SHRPX_OPTID_FORWARDED_FOR:
3459 fwdconf.for_node_type = type;
3465 case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
3466 config->tls.no_http2_cipher_black_list = util::strieq_l("yes", optarg);
3469 case SHRPX_OPTID_BACKEND_HTTP1_TLS:
3470 case SHRPX_OPTID_BACKEND_TLS:
3471 LOG(WARN) << opt << ": deprecated. Use tls keyword in "
3472 << SHRPX_OPT_BACKEND << " instead.";
3474 case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS:
3475 LOG(WARN) << opt << ": deprecated. Use tls keyword in "
3476 << SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED;
3478 case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE:
3479 config->tls.session_cache.memcached.cert_file =
3480 make_string_ref(config->balloc, optarg);
3483 case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE:
3484 config->tls.session_cache.memcached.private_key_file =
3485 make_string_ref(config->balloc, optarg);
3488 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS:
3489 LOG(WARN) << opt << ": deprecated. Use tls keyword in "
3490 << SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED;
3492 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE:
3493 config->tls.ticket.memcached.cert_file =
3494 make_string_ref(config->balloc, optarg);
3497 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE:
3498 config->tls.ticket.memcached.private_key_file =
3499 make_string_ref(config->balloc, optarg);
3502 case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
3503 return parse_address_family(&config->tls.ticket.memcached.family, opt,
3505 case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
3506 return parse_address_family(&config->tls.session_cache.memcached.family,
3508 case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
3509 return parse_address_family(&config->conn.downstream->family, opt, optarg);
3510 case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
3511 return parse_uint(&config->http2.upstream.max_concurrent_streams, opt,
3513 case SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS:
3514 return parse_uint(&config->http2.downstream.max_concurrent_streams, opt,
3516 case SHRPX_OPTID_ERROR_PAGE:
3517 return parse_error_page(config->http.error_pages, opt, optarg);
3518 case SHRPX_OPTID_NO_KQUEUE:
3519 if ((ev_supported_backends() & EVBACKEND_KQUEUE) == 0) {
3520 LOG(WARN) << opt << ": kqueue is not supported on this platform";
3524 config->ev_loop_flags = ev_recommended_backends() & ~EVBACKEND_KQUEUE;
3527 case SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT:
3528 return parse_duration(&config->http2.upstream.timeout.settings, opt,
3530 case SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT:
3531 return parse_duration(&config->http2.downstream.timeout.settings, opt,
3533 case SHRPX_OPTID_API_MAX_REQUEST_BODY:
3534 return parse_uint_with_unit(&config->api.max_request_body, opt, optarg);
3535 case SHRPX_OPTID_BACKEND_MAX_BACKOFF:
3536 return parse_duration(&config->conn.downstream->timeout.max_backoff, opt,
3538 case SHRPX_OPTID_SERVER_NAME:
3539 config->http.server_name = make_string_ref(config->balloc, optarg);
3542 case SHRPX_OPTID_NO_SERVER_REWRITE:
3543 config->http.no_server_rewrite = util::strieq_l("yes", optarg);
3546 case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE:
3547 config->http2.upstream.optimize_write_buffer_size =
3548 util::strieq_l("yes", optarg);
3551 case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE:
3552 config->http2.upstream.optimize_window_size = util::strieq_l("yes", optarg);
3555 case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE:
3556 if (parse_uint_with_unit(&config->http2.upstream.window_size, opt,
3562 case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE:
3563 if (parse_uint_with_unit(&config->http2.upstream.connection_window_size,
3564 opt, optarg) != 0) {
3569 case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE:
3570 if (parse_uint_with_unit(&config->http2.downstream.window_size, opt,
3576 case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE:
3577 if (parse_uint_with_unit(&config->http2.downstream.connection_window_size,
3578 opt, optarg) != 0) {
3583 case SHRPX_OPTID_FRONTEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE:
3584 if (parse_uint_with_unit(&config->http2.upstream.encoder_dynamic_table_size,
3585 opt, optarg) != 0) {
3589 nghttp2_option_set_max_deflate_dynamic_table_size(
3590 config->http2.upstream.option,
3591 config->http2.upstream.encoder_dynamic_table_size);
3592 nghttp2_option_set_max_deflate_dynamic_table_size(
3593 config->http2.upstream.alt_mode_option,
3594 config->http2.upstream.encoder_dynamic_table_size);
3597 case SHRPX_OPTID_FRONTEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE:
3598 return parse_uint_with_unit(
3599 &config->http2.upstream.decoder_dynamic_table_size, opt, optarg);
3600 case SHRPX_OPTID_BACKEND_HTTP2_ENCODER_DYNAMIC_TABLE_SIZE:
3601 if (parse_uint_with_unit(
3602 &config->http2.downstream.encoder_dynamic_table_size, opt,
3607 nghttp2_option_set_max_deflate_dynamic_table_size(
3608 config->http2.downstream.option,
3609 config->http2.downstream.encoder_dynamic_table_size);
3612 case SHRPX_OPTID_BACKEND_HTTP2_DECODER_DYNAMIC_TABLE_SIZE:
3613 return parse_uint_with_unit(
3614 &config->http2.downstream.decoder_dynamic_table_size, opt, optarg);
3615 case SHRPX_OPTID_ECDH_CURVES:
3616 #if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
3617 config->tls.ecdh_curves = make_string_ref(config->balloc, optarg);
3618 #else // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
3619 LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
3620 #endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
3622 case SHRPX_OPTID_TLS_SCT_DIR:
3623 #if !LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L
3624 return read_tls_sct_from_dir(config->tls.sct_data, opt, optarg);
3625 #else // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
3626 LOG(WARN) << opt << ": This option requires OpenSSL >= 1.0.2";
3628 #endif // !(!LIBRESSL_LEGACY_API && OPENSSL_VERSION_NUMBER >= 0x10002000L)
3629 case SHRPX_OPTID_DNS_CACHE_TIMEOUT:
3630 return parse_duration(&config->dns.timeout.cache, opt, optarg);
3631 case SHRPX_OPTID_DNS_LOOKUP_TIMEOUT:
3632 return parse_duration(&config->dns.timeout.lookup, opt, optarg);
3633 case SHRPX_OPTID_DNS_MAX_TRY: {
3635 if (parse_uint(&n, opt, optarg) != 0) {
3640 LOG(ERROR) << opt << ": must be smaller than or equal to 5";
3644 config->dns.max_try = n;
3647 case SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT:
3648 return parse_duration(&config->conn.upstream.timeout.idle_read, opt,
3650 case SHRPX_OPTID_PSK_SECRETS:
3651 #if !LIBRESSL_LEGACY_API
3652 return parse_psk_secrets(config, optarg);
3653 #else // LIBRESSL_LEGACY_API
3656 << ": ignored because underlying TLS library does not support PSK";
3658 #endif // LIBRESSL_LEGACY_API
3659 case SHRPX_OPTID_CLIENT_PSK_SECRETS:
3660 #if !LIBRESSL_LEGACY_API
3661 return parse_client_psk_secrets(config, optarg);
3662 #else // LIBRESSL_LEGACY_API
3665 << ": ignored because underlying TLS library does not support PSK";
3667 #endif // LIBRESSL_LEGACY_API
3668 case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
3669 config->tls.client.no_http2_cipher_black_list =
3670 util::strieq_l("yes", optarg);
3673 case SHRPX_OPTID_CLIENT_CIPHERS:
3674 config->tls.client.ciphers = make_string_ref(config->balloc, optarg);
3677 case SHRPX_OPTID_TLS13_CLIENT_CIPHERS:
3678 config->tls.client.tls13_ciphers = make_string_ref(config->balloc, optarg);
3681 case SHRPX_OPTID_ACCESSLOG_WRITE_EARLY:
3682 config->logging.access.write_early = util::strieq_l("yes", optarg);
3685 case SHRPX_OPTID_TLS_MIN_PROTO_VERSION:
3686 return parse_tls_proto_version(config->tls.min_proto_version, opt, optarg);
3687 case SHRPX_OPTID_TLS_MAX_PROTO_VERSION:
3688 return parse_tls_proto_version(config->tls.max_proto_version, opt, optarg);
3689 case SHRPX_OPTID_REDIRECT_HTTPS_PORT: {
3690 auto n = util::parse_uint(optarg);
3691 if (n == -1 || n < 0 || n > 65535) {
3693 << ": bad value. Specify an integer in the range [0, "
3694 "65535], inclusive";
3697 config->http.redirect_https_port = optarg;
3700 case SHRPX_OPTID_FRONTEND_MAX_REQUESTS:
3701 return parse_uint(&config->http.max_requests, opt, optarg);
3702 case SHRPX_OPTID_SINGLE_THREAD:
3703 config->single_thread = util::strieq_l("yes", optarg);
3706 case SHRPX_OPTID_SINGLE_PROCESS:
3707 config->single_process = util::strieq_l("yes", optarg);
3710 case SHRPX_OPTID_NO_ADD_X_FORWARDED_PROTO:
3711 config->http.xfp.add = !util::strieq_l("yes", optarg);
3714 case SHRPX_OPTID_NO_STRIP_INCOMING_X_FORWARDED_PROTO:
3715 config->http.xfp.strip_incoming = !util::strieq_l("yes", optarg);
3718 case SHRPX_OPTID_OCSP_STARTUP:
3719 config->tls.ocsp.startup = util::strieq_l("yes", optarg);
3722 case SHRPX_OPTID_NO_VERIFY_OCSP:
3723 config->tls.ocsp.no_verify = util::strieq_l("yes", optarg);
3726 case SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED:
3727 config->tls.client_verify.tolerate_expired = util::strieq_l("yes", optarg);
3730 case SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR:
3731 config->ignore_per_pattern_mruby_error = util::strieq_l("yes", optarg);
3734 case SHRPX_OPTID_TLS_NO_POSTPONE_EARLY_DATA:
3735 config->tls.no_postpone_early_data = util::strieq_l("yes", optarg);
3738 case SHRPX_OPTID_TLS_MAX_EARLY_DATA: {
3739 return parse_uint_with_unit(&config->tls.max_early_data, opt, optarg);
3741 case SHRPX_OPTID_NO_STRIP_INCOMING_EARLY_DATA:
3742 config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
3745 case SHRPX_OPTID_CONF:
3746 LOG(WARN) << "conf: ignored";
3751 LOG(ERROR) << "Unknown option: " << opt;
3756 int load_config(Config *config, const char *filename,
3757 std::set<StringRef> &include_set,
3758 std::map<StringRef, size_t> &pattern_addr_indexer) {
3759 std::ifstream in(filename, std::ios::binary);
3761 LOG(ERROR) << "Could not open config file " << filename;
3766 while (std::getline(in, line)) {
3768 if (line.empty() || line[0] == '#') {
3771 auto eq = std::find(std::begin(line), std::end(line), '=');
3772 if (eq == std::end(line)) {
3773 LOG(ERROR) << "Bad configuration format in " << filename << " at line "
3779 if (parse_config(config, StringRef{std::begin(line), eq},
3780 StringRef{eq + 1, std::end(line)}, include_set,
3781 pattern_addr_indexer) != 0) {
3788 StringRef str_syslog_facility(int facility) {
3791 return StringRef::from_lit("auth");
3793 case (LOG_AUTHPRIV):
3794 return StringRef::from_lit("authpriv");
3795 #endif // LOG_AUTHPRIV
3797 return StringRef::from_lit("cron");
3799 return StringRef::from_lit("daemon");
3802 return StringRef::from_lit("ftp");
3805 return StringRef::from_lit("kern");
3807 return StringRef::from_lit("local0");
3809 return StringRef::from_lit("local1");
3811 return StringRef::from_lit("local2");
3813 return StringRef::from_lit("local3");
3815 return StringRef::from_lit("local4");
3817 return StringRef::from_lit("local5");
3819 return StringRef::from_lit("local6");
3821 return StringRef::from_lit("local7");
3823 return StringRef::from_lit("lpr");
3825 return StringRef::from_lit("mail");
3827 return StringRef::from_lit("syslog");
3829 return StringRef::from_lit("user");
3831 return StringRef::from_lit("uucp");
3833 return StringRef::from_lit("(unknown)");
3837 int int_syslog_facility(const StringRef &strfacility) {
3838 if (util::strieq_l("auth", strfacility)) {
3843 if (util::strieq_l("authpriv", strfacility)) {
3844 return LOG_AUTHPRIV;
3846 #endif // LOG_AUTHPRIV
3848 if (util::strieq_l("cron", strfacility)) {
3852 if (util::strieq_l("daemon", strfacility)) {
3857 if (util::strieq_l("ftp", strfacility)) {
3862 if (util::strieq_l("kern", strfacility)) {
3866 if (util::strieq_l("local0", strfacility)) {
3870 if (util::strieq_l("local1", strfacility)) {
3874 if (util::strieq_l("local2", strfacility)) {
3878 if (util::strieq_l("local3", strfacility)) {
3882 if (util::strieq_l("local4", strfacility)) {
3886 if (util::strieq_l("local5", strfacility)) {
3890 if (util::strieq_l("local6", strfacility)) {
3894 if (util::strieq_l("local7", strfacility)) {
3898 if (util::strieq_l("lpr", strfacility)) {
3902 if (util::strieq_l("mail", strfacility)) {
3906 if (util::strieq_l("news", strfacility)) {
3910 if (util::strieq_l("syslog", strfacility)) {
3914 if (util::strieq_l("user", strfacility)) {
3918 if (util::strieq_l("uucp", strfacility)) {
3925 StringRef strproto(Proto proto) {
3928 return StringRef::from_lit("none");
3930 return StringRef::from_lit("http/1.1");
3932 return StringRef::from_lit("h2");
3933 case Proto::MEMCACHED:
3934 return StringRef::from_lit("memcached");
3943 // Consistent hashing method described in
3944 // https://github.com/RJ/ketama. Generate 160 32-bit hashes per |s|,
3945 // which is usually backend address. The each hash is associated to
3946 // index of backend address. When all hashes for every backend
3947 // address are calculated, sort it in ascending order of hash. To
3948 // choose the index, compute 32-bit hash based on client IP address,
3949 // and do lower bound search in the array. The returned index is the
3951 int compute_affinity_hash(std::vector<AffinityHash> &res, size_t idx,
3952 const StringRef &s) {
3954 std::array<uint8_t, 32> buf;
3956 for (auto i = 0; i < 20; ++i) {
3960 rv = util::sha256(buf.data(), StringRef{t});
3965 for (int i = 0; i < 8; ++i) {
3966 auto h = (static_cast<uint32_t>(buf[4 * i]) << 24) |
3967 (static_cast<uint32_t>(buf[4 * i + 1]) << 16) |
3968 (static_cast<uint32_t>(buf[4 * i + 2]) << 8) |
3969 static_cast<uint32_t>(buf[4 * i + 3]);
3971 res.emplace_back(idx, h);
3979 // Configures the following member in |config|:
3980 // conn.downstream_router, conn.downstream.addr_groups,
3981 // conn.downstream.addr_group_catch_all.
3982 int configure_downstream_group(Config *config, bool http2_proxy,
3983 bool numeric_addr_only,
3984 const TLSConfig &tlsconf) {
3987 auto &downstreamconf = *config->conn.downstream;
3988 auto &addr_groups = downstreamconf.addr_groups;
3989 auto &routerconf = downstreamconf.router;
3990 auto &router = routerconf.router;
3992 if (addr_groups.empty()) {
3993 DownstreamAddrConfig addr{};
3994 addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST);
3995 addr.port = DEFAULT_DOWNSTREAM_PORT;
3996 addr.proto = Proto::HTTP1;
3998 addr.group_weight = 1;
4000 DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
4001 g.addrs.push_back(std::move(addr));
4002 router.add_route(g.pattern, addr_groups.size());
4003 addr_groups.push_back(std::move(g));
4006 // backward compatibility: override all SNI fields with the option
4007 // value --backend-tls-sni-field
4008 if (!tlsconf.backend_sni_name.empty()) {
4009 auto &sni = tlsconf.backend_sni_name;
4010 for (auto &addr_group : addr_groups) {
4011 for (auto &addr : addr_group.addrs) {
4017 if (LOG_ENABLED(INFO)) {
4018 LOG(INFO) << "Resolving backend address";
4021 ssize_t catch_all_group = -1;
4022 for (size_t i = 0; i < addr_groups.size(); ++i) {
4023 auto &g = addr_groups[i];
4024 if (g.pattern == StringRef::from_lit("/")) {
4025 catch_all_group = i;
4027 if (LOG_ENABLED(INFO)) {
4028 LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern
4030 for (auto &addr : g.addrs) {
4031 LOG(INFO) << "group " << i << " -> " << addr.host.c_str()
4032 << (addr.host_unix ? "" : ":" + util::utos(addr.port))
4033 << ", proto=" << strproto(addr.proto)
4034 << (addr.tls ? ", tls" : "");
4038 // Try compile mruby script and catch compile error early.
4039 if (!g.mruby_file.empty()) {
4040 if (mruby::create_mruby_context(g.mruby_file) == nullptr) {
4041 LOG(config->ignore_per_pattern_mruby_error ? ERROR : FATAL)
4042 << "backend: Could not compile mruby flie for pattern "
4044 if (!config->ignore_per_pattern_mruby_error) {
4047 g.mruby_file = StringRef{};
4050 #endif // HAVE_MRUBY
4054 // Try compile mruby script (--mruby-file) here to catch compile
4056 if (!config->mruby_file.empty()) {
4057 if (mruby::create_mruby_context(config->mruby_file) == nullptr) {
4058 LOG(FATAL) << "mruby-file: Could not compile mruby file";
4062 #endif // HAVE_MRUBY
4064 if (catch_all_group == -1) {
4065 LOG(FATAL) << "backend: No catch-all backend address is configured";
4069 downstreamconf.addr_group_catch_all = catch_all_group;
4071 if (LOG_ENABLED(INFO)) {
4072 LOG(INFO) << "Catch-all pattern is group " << catch_all_group;
4075 auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
4077 for (auto &g : addr_groups) {
4078 std::unordered_map<StringRef, uint32_t> wgchk;
4079 for (auto &addr : g.addrs) {
4080 if (addr.group_weight) {
4081 auto it = wgchk.find(addr.group);
4082 if (it == std::end(wgchk)) {
4083 wgchk.emplace(addr.group, addr.group_weight);
4084 } else if ((*it).second != addr.group_weight) {
4085 LOG(FATAL) << "backend: inconsistent group-weight for a single group";
4090 if (addr.host_unix) {
4091 // for AF_UNIX socket, we use "localhost" as host for backend
4092 // hostport. This is used as Host header field to backend and
4093 // not going to be passed to any syscalls.
4094 addr.hostport = StringRef::from_lit("localhost");
4096 auto path = addr.host.c_str();
4097 auto pathlen = addr.host.size();
4099 if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) {
4100 LOG(FATAL) << "UNIX domain socket path " << path << " is too long > "
4101 << sizeof(addr.addr.su.un.sun_path);
4105 if (LOG_ENABLED(INFO)) {
4106 LOG(INFO) << "Use UNIX domain socket path " << path
4107 << " for backend connection";
4110 addr.addr.su.un.sun_family = AF_UNIX;
4111 // copy path including terminal NULL
4112 std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path);
4113 addr.addr.len = sizeof(addr.addr.su.un);
4119 util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
4122 util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
4125 if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
4126 downstreamconf.family, resolve_flags) == -1) {
4127 LOG(FATAL) << "Resolving backend address failed: " << hostport;
4131 if (LOG_ENABLED(INFO)) {
4132 LOG(INFO) << "Resolved backend address: " << hostport << " -> "
4133 << util::to_numeric_addr(&addr.addr);
4136 LOG(INFO) << "Resolving backend address " << hostport
4137 << " takes place dynamically";
4141 for (auto &addr : g.addrs) {
4142 if (addr.group_weight == 0) {
4143 auto it = wgchk.find(addr.group);
4144 if (it == std::end(wgchk)) {
4145 addr.group_weight = 1;
4147 addr.group_weight = (*it).second;
4152 if (g.affinity.type != SessionAffinity::NONE) {
4154 for (auto &addr : g.addrs) {
4157 if (addr.host_unix) {
4160 key = addr.hostport;
4163 auto p = reinterpret_cast<uint8_t *>(&addr.addr.su);
4164 key = StringRef{p, addr.addr.len};
4166 rv = compute_affinity_hash(g.affinity_hash, idx, key);
4174 std::sort(std::begin(g.affinity_hash), std::end(g.affinity_hash),
4175 [](const AffinityHash &lhs, const AffinityHash &rhs) {
4176 return lhs.hash < rhs.hash;
4180 auto &timeout = g.timeout;
4181 if (timeout.read < 1e-9) {
4182 timeout.read = downstreamconf.timeout.read;
4184 if (timeout.write < 1e-9) {
4185 timeout.write = downstreamconf.timeout.write;
4192 int resolve_hostname(Address *addr, const char *hostname, uint16_t port,
4193 int family, int additional_flags) {
4196 auto service = util::utos(port);
4199 hints.ai_family = family;
4200 hints.ai_socktype = SOCK_STREAM;
4201 hints.ai_flags |= additional_flags;
4202 #ifdef AI_ADDRCONFIG
4203 hints.ai_flags |= AI_ADDRCONFIG;
4204 #endif // AI_ADDRCONFIG
4207 rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
4208 #ifdef AI_ADDRCONFIG
4210 // Retry without AI_ADDRCONFIG
4211 hints.ai_flags &= ~AI_ADDRCONFIG;
4212 rv = getaddrinfo(hostname, service.c_str(), &hints, &res);
4214 #endif // AI_ADDRCONFIG
4216 LOG(FATAL) << "Unable to resolve address for " << hostname << ": "
4217 << gai_strerror(rv);
4221 auto res_d = defer(freeaddrinfo, res);
4223 char host[NI_MAXHOST];
4224 rv = getnameinfo(res->ai_addr, res->ai_addrlen, host, sizeof(host), nullptr,
4227 LOG(FATAL) << "Address resolution for " << hostname
4228 << " failed: " << gai_strerror(rv);
4233 if (LOG_ENABLED(INFO)) {
4234 LOG(INFO) << "Address resolution for " << hostname
4235 << " succeeded: " << host;
4238 memcpy(&addr->su, res->ai_addr, res->ai_addrlen);
4239 addr->len = res->ai_addrlen;
4244 } // namespace shrpx