return ticket_keys;
}
+#ifdef ENABLE_HTTP3
+std::shared_ptr<QUICKeyingMaterials>
+read_quic_secret_file(const StringRef &path) {
+ constexpr size_t expectedlen =
+ SHRPX_QUIC_SECRET_RESERVEDLEN + SHRPX_QUIC_SECRETLEN + SHRPX_QUIC_SALTLEN;
+
+ auto qkms = std::make_shared<QUICKeyingMaterials>();
+ auto &kms = qkms->keying_materials;
+
+ std::ifstream f(path.c_str());
+ if (!f) {
+ LOG(ERROR) << "frontend-quic-secret-file: could not open file " << path;
+ return nullptr;
+ }
+
+ std::array<char, 4096> buf;
+
+ while (f.getline(buf.data(), buf.size())) {
+ auto len = strlen(buf.data());
+ if (len == 0 || buf[0] == '#') {
+ continue;
+ }
+
+ auto s = StringRef{std::begin(buf), std::begin(buf) + len};
+ if (s.size() != expectedlen * 2 || !util::is_hex_string(s)) {
+ LOG(ERROR) << "frontend-quic-secret-file: each line must be a "
+ << expectedlen * 2 << " bytes hex encoded string";
+ return nullptr;
+ }
+
+ kms.emplace_back();
+ auto &qkm = kms.back();
+
+ auto p = std::begin(s);
+
+ util::decode_hex(std::begin(qkm.reserved),
+ StringRef{p, p + qkm.reserved.size()});
+ p += qkm.reserved.size() * 2;
+ util::decode_hex(std::begin(qkm.secret),
+ StringRef{p, p + qkm.secret.size()});
+ p += qkm.secret.size() * 2;
+ util::decode_hex(std::begin(qkm.salt), StringRef{p, p + qkm.salt.size()});
+ p += qkm.salt.size() * 2;
+
+ assert(static_cast<size_t>(p - std::begin(s)) == expectedlen * 2);
+
+ qkm.id = qkm.reserved[0] & 0xc0;
+
+ if (kms.size() == 4) {
+ break;
+ }
+ }
+
+ if (f.bad() || (!f.eof() && f.fail())) {
+ LOG(ERROR)
+ << "frontend-quic-secret-file: error occurred while reading file "
+ << path;
+ return nullptr;
+ }
+
+ if (kms.empty()) {
+ LOG(WARN)
+ << "frontend-quic-secret-file: no keying materials are present in file "
+ << path;
+ return nullptr;
+ }
+
+ return qkms;
+}
+#endif // ENABLE_HTTP3
+
FILE *open_file_for_write(const char *filename) {
std::array<char, STRERROR_BUFSIZE> errbuf;
-#if defined O_CLOEXEC
+#ifdef O_CLOEXEC
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
#else
}
namespace {
+int parse_altsvc(AltSvc &altsvc, const StringRef &opt,
+ const StringRef &optarg) {
+ // PROTOID, PORT, HOST, ORIGIN, PARAMS.
+ auto tokens = util::split_str(optarg, ',', 5);
+
+ if (tokens.size() < 2) {
+ // Requires at least protocol_id and port
+ LOG(ERROR) << opt << ": too few parameters: " << optarg;
+ return -1;
+ }
+
+ int port;
+
+ if (parse_uint(&port, opt, tokens[1]) != 0) {
+ return -1;
+ }
+
+ if (port < 1 ||
+ port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
+ LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
+ return -1;
+ }
+
+ altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
+
+ altsvc.port = port;
+ altsvc.service = make_string_ref(config->balloc, tokens[1]);
+
+ if (tokens.size() > 2) {
+ if (!tokens[2].empty()) {
+ altsvc.host = make_string_ref(config->balloc, tokens[2]);
+ }
+
+ if (tokens.size() > 3) {
+ if (!tokens[3].empty()) {
+ altsvc.origin = make_string_ref(config->balloc, tokens[3]);
+ }
+
+ if (tokens.size() > 4) {
+ if (!tokens[4].empty()) {
+ altsvc.params = make_string_ref(config->balloc, tokens[4]);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+} // namespace
+
+namespace {
// generated by gennghttpxfun.py
LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (namelen) {
break;
case 4:
switch (name[3]) {
+ case 'h':
+ if (util::strieq_l("pat", name, 3)) {
+ return LogFragmentType::PATH;
+ }
+ break;
case 'n':
if (util::strieq_l("alp", name, 3)) {
return LogFragmentType::ALPN;
break;
case 6:
switch (name[5]) {
+ case 'd':
+ if (util::strieq_l("metho", name, 5)) {
+ return LogFragmentType::METHOD;
+ }
+ break;
case 's':
if (util::strieq_l("statu", name, 5)) {
return LogFragmentType::STATUS;
break;
}
break;
+ case 16:
+ switch (name[15]) {
+ case 'n':
+ if (util::strieq_l("protocol_versio", name, 15)) {
+ return LogFragmentType::PROTOCOL_VERSION;
+ }
+ break;
+ }
+ break;
case 17:
switch (name[16]) {
case 'l':
return LogFragmentType::TLS_SESSION_REUSED;
}
break;
+ case 'y':
+ if (util::strieq_l("path_without_quer", name, 17)) {
+ return LogFragmentType::PATH_WITHOUT_QUERY;
+ }
+ break;
}
break;
case 22:
bool tls;
bool sni_fwd;
bool proxyproto;
+ bool quic;
};
namespace {
out.alt_mode = UpstreamAltMode::HEALTHMON;
} else if (util::strieq_l("proxyproto", param)) {
out.proxyproto = true;
+ } else if (util::strieq_l("quic", param)) {
+#ifdef ENABLE_HTTP3
+ out.quic = true;
+#else // !ENABLE_HTTP3
+ LOG(ERROR) << "quic: QUIC is disabled at compile time";
+ return -1;
+#endif // !ENABLE_HTTP3
} else if (!param.empty()) {
LOG(ERROR) << "frontend: " << param << ": unknown keyword";
return -1;
bool dns;
bool redirect_if_not_tls;
bool upgrade_scheme;
+ bool dnf;
};
namespace {
return -1;
}
out.group_weight = n;
+ } else if (util::strieq_l("dnf", param)) {
+ out.dnf = true;
} else if (!param.empty()) {
LOG(ERROR) << "backend: " << param << ": unknown keyword";
return -1;
addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
addr.dns = params.dns;
addr.upgrade_scheme = params.upgrade_scheme;
+ addr.dnf = params.dnf;
auto &routerconf = downstreamconf.router;
auto &router = routerconf.router;
*p = '\0';
pattern = StringRef{iov.base, p};
} else {
- auto path = http2::normalize_path(downstreamconf.balloc,
- StringRef{slash, std::end(raw_pattern)},
- StringRef{});
+ auto path = http2::normalize_path_colon(
+ downstreamconf.balloc, StringRef{slash, std::end(raw_pattern)},
+ StringRef{});
auto iov = make_byte_ref(downstreamconf.balloc,
std::distance(std::begin(raw_pattern), slash) +
path.size() + 1);
return -1;
}
}
+ // All backends in the same group must have the same dnf
+ // setting. If some backends do not specify dnf, and there is
+ // at least one backend with dnf, it is used for all backends in
+ // the group. In general, multiple backends are not necessary
+ // for dnf because there is no need for load balancing.
+ if (params.dnf) {
+ g.dnf = true;
+ }
g.addrs.push_back(addr);
continue;
g.mruby_file = make_string_ref(downstreamconf.balloc, params.mruby);
g.timeout.read = params.read_timeout;
g.timeout.write = params.write_timeout;
+ g.dnf = params.dnf;
if (pattern[0] == '*') {
// wildcard pattern
return SHRPX_OPTID_SERVER_NAME;
}
break;
+ case 'f':
+ if (util::strieq_l("no-quic-bp", name, 10)) {
+ return SHRPX_OPTID_NO_QUIC_BPF;
+ }
+ break;
case 'r':
if (util::strieq_l("tls-sct-di", name, 10)) {
return SHRPX_OPTID_TLS_SCT_DIR;
return SHRPX_OPTID_BACKEND_IPV6;
}
break;
+ case 'c':
+ if (util::strieq_l("http2-altsv", name, 11)) {
+ return SHRPX_OPTID_HTTP2_ALTSVC;
+ }
+ break;
case 'e':
if (util::strieq_l("host-rewrit", name, 11)) {
return SHRPX_OPTID_HOST_REWRITE;
break;
case 14:
switch (name[13]) {
+ case 'd':
+ if (util::strieq_l("quic-server-i", name, 13)) {
+ return SHRPX_OPTID_QUIC_SERVER_ID;
+ }
+ break;
case 'e':
if (util::strieq_l("accesslog-fil", name, 13)) {
return SHRPX_OPTID_ACCESSLOG_FILE;
return SHRPX_OPTID_NO_SERVER_PUSH;
}
break;
+ case 'k':
+ if (util::strieq_l("rlimit-memloc", name, 13)) {
+ return SHRPX_OPTID_RLIMIT_MEMLOCK;
+ }
+ break;
case 'p':
if (util::strieq_l("no-verify-ocs", name, 13)) {
return SHRPX_OPTID_NO_VERIFY_OCSP;
}
break;
case 's':
+ if (util::strieq_l("max-worker-processe", name, 19)) {
+ return SHRPX_OPTID_MAX_WORKER_PROCESSES;
+ }
if (util::strieq_l("tls13-client-cipher", name, 19)) {
return SHRPX_OPTID_TLS13_CLIENT_CIPHERS;
}
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
}
break;
+ case 'e':
+ if (util::strieq_l("quic-bpf-program-fil", name, 20)) {
+ return SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE;
+ }
+ break;
case 'l':
if (util::strieq_l("accept-proxy-protoco", name, 20)) {
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
if (util::strieq_l("backend-request-buffe", name, 21)) {
return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
}
+ if (util::strieq_l("frontend-quic-qlog-di", name, 21)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR;
+ }
break;
case 't':
if (util::strieq_l("frontend-write-timeou", name, 21)) {
return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
}
break;
+ case 'g':
+ if (util::strieq_l("frontend-quic-debug-lo", name, 22)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG;
+ }
+ break;
case 'r':
if (util::strieq_l("backend-response-buffe", name, 22)) {
return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
break;
case 24:
switch (name[23]) {
+ case 'a':
+ if (util::strieq_l("frontend-quic-early-dat", name, 23)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA;
+ }
+ break;
case 'd':
if (util::strieq_l("strip-incoming-forwarde", name, 23)) {
return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
}
+ if (util::strieq_l("frontend-quic-secret-fil", name, 24)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE;
+ }
break;
case 'g':
if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
}
break;
+ case 't':
+ if (util::strieq_l("frontend-quic-initial-rt", name, 24)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT;
+ }
+ break;
}
break;
case 26:
if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
}
+ if (util::strieq_l("frontend-http3-window-siz", name, 25)) {
+ return SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE;
+ }
break;
case 's':
if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
}
+ if (util::strieq_l("frontend-quic-idle-timeou", name, 25)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT;
+ }
if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
}
+ if (util::strieq_l("no-http2-cipher-block-lis", name, 25)) {
+ return SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST;
+ }
break;
}
break;
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
}
break;
+ case 'n':
+ if (util::strieq_l("frontend-quic-require-toke", name, 26)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN;
+ }
+ break;
case 'r':
if (util::strieq_l("request-header-field-buffe", name, 26)) {
return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
}
+ if (util::strieq_l("frontend-http3-read-timeou", name, 26)) {
+ return SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT;
+ }
if (util::strieq_l("frontend-keep-alive-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_KEEP_ALIVE_TIMEOUT;
}
return SHRPX_OPTID_VERIFY_CLIENT_TOLERATE_EXPIRED;
}
break;
+ case 'e':
+ if (util::strieq_l("frontend-http3-max-window-siz", name, 29)) {
+ return SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE;
+ }
+ break;
case 'r':
if (util::strieq_l("ignore-per-pattern-mruby-erro", name, 29)) {
return SHRPX_OPTID_IGNORE_PER_PATTERN_MRUBY_ERROR;
if (util::strieq_l("client-no-http2-cipher-black-lis", name, 32)) {
return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST;
}
+ if (util::strieq_l("client-no-http2-cipher-block-lis", name, 32)) {
+ return SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST;
+ }
break;
}
break;
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
}
+ if (util::strieq_l("frontend-quic-congestion-controlle", name, 34)) {
+ return SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER;
+ }
break;
}
break;
case 36:
switch (name[35]) {
+ case 'd':
+ if (util::strieq_l("worker-process-grace-shutdown-perio", name, 35)) {
+ return SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD;
+ }
+ break;
case 'e':
if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
}
+ if (util::strieq_l("frontend-http3-connection-window-siz", name, 36)) {
+ return SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE;
+ }
if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
}
if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
}
+ if (util::strieq_l("frontend-http3-max-concurrent-stream", name, 36)) {
+ return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS;
+ }
break;
}
break;
40)) {
return SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE;
}
+ if (util::strieq_l("frontend-http3-max-connection-window-siz", name,
+ 40)) {
+ return SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE;
+ }
if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
40)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
return 0;
}
case SHRPX_OPTID_FRONTEND: {
- auto &listenerconf = config->conn.listener;
auto &apiconf = config->api;
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
return -1;
}
+ if (params.quic) {
+ if (params.alt_mode != UpstreamAltMode::NONE) {
+ LOG(ERROR) << "frontend: api or healthmon cannot be used with quic";
+ return -1;
+ }
+
+ if (!params.tls) {
+ LOG(ERROR) << "frontend: quic requires TLS";
+ return -1;
+ }
+ }
+
UpstreamAddr addr{};
addr.fd = -1;
addr.tls = params.tls;
addr.sni_fwd = params.sni_fwd;
addr.alt_mode = params.alt_mode;
addr.accept_proxy_protocol = params.proxyproto;
+ addr.quic = params.quic;
if (addr.alt_mode == UpstreamAltMode::API) {
apiconf.enabled = true;
}
+#ifdef ENABLE_HTTP3
+ auto &addrs = params.quic ? config->conn.quic_listener.addrs
+ : config->conn.listener.addrs;
+#else // !ENABLE_HTTP3
+ auto &addrs = config->conn.listener.addrs;
+#endif // !ENABLE_HTTP3
+
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
+ if (addr.quic) {
+ LOG(ERROR) << "frontend: quic cannot be used on UNIX domain socket";
+ return -1;
+ }
+
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
addr.host_unix = true;
+ addr.index = addrs.size();
- listenerconf.addrs.push_back(std::move(addr));
+ addrs.push_back(std::move(addr));
return 0;
}
if (util::numeric_host(host, AF_INET)) {
addr.family = AF_INET;
- listenerconf.addrs.push_back(std::move(addr));
+ addr.index = addrs.size();
+ addrs.push_back(std::move(addr));
return 0;
}
if (util::numeric_host(host, AF_INET6)) {
addr.family = AF_INET6;
- listenerconf.addrs.push_back(std::move(addr));
+ addr.index = addrs.size();
+ addrs.push_back(std::move(addr));
return 0;
}
addr.family = AF_INET;
- listenerconf.addrs.push_back(addr);
+ addr.index = addrs.size();
+ addrs.push_back(addr);
addr.family = AF_INET6;
- listenerconf.addrs.push_back(std::move(addr));
+ addr.index = addrs.size();
+ addrs.push_back(std::move(addr));
return 0;
}
case SHRPX_OPTID_PADDING:
return parse_uint(&config->padding, opt, optarg);
case SHRPX_OPTID_ALTSVC: {
- auto tokens = util::split_str(optarg, ',');
-
- if (tokens.size() < 2) {
- // Requires at least protocol_id and port
- LOG(ERROR) << opt << ": too few parameters: " << optarg;
- return -1;
- }
-
- if (tokens.size() > 4) {
- // We only need protocol_id, port, host and origin
- LOG(ERROR) << opt << ": too many parameters: " << optarg;
- return -1;
- }
-
- int port;
-
- if (parse_uint(&port, opt, tokens[1]) != 0) {
- return -1;
- }
-
- if (port < 1 ||
- port > static_cast<int>(std::numeric_limits<uint16_t>::max())) {
- LOG(ERROR) << opt << ": port is invalid: " << tokens[1];
- return -1;
- }
-
AltSvc altsvc{};
- altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
-
- altsvc.port = port;
- altsvc.service = make_string_ref(config->balloc, tokens[1]);
-
- if (tokens.size() > 2) {
- altsvc.host = make_string_ref(config->balloc, tokens[2]);
-
- if (tokens.size() > 3) {
- altsvc.origin = make_string_ref(config->balloc, tokens[3]);
- }
+ if (parse_altsvc(altsvc, opt, optarg) != 0) {
+ return -1;
}
config->http.altsvcs.push_back(std::move(altsvc));
return 0;
}
case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
- config->tls.no_http2_cipher_black_list = util::strieq_l("yes", optarg);
-
+ LOG(WARN) << opt << ": deprecated. Use "
+ << SHRPX_OPT_NO_HTTP2_CIPHER_BLOCK_LIST << " instead.";
+ // fall through
+ case SHRPX_OPTID_NO_HTTP2_CIPHER_BLOCK_LIST:
+ config->tls.no_http2_cipher_block_list = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_BACKEND_HTTP1_TLS:
case SHRPX_OPTID_BACKEND_TLS:
return 0;
#endif // LIBRESSL_LEGACY_API
case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLACK_LIST:
- config->tls.client.no_http2_cipher_black_list =
+ LOG(WARN) << opt << ": deprecated. Use "
+ << SHRPX_OPT_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST << " instead.";
+ // fall through
+ case SHRPX_OPTID_CLIENT_NO_HTTP2_CIPHER_BLOCK_LIST:
+ config->tls.client.no_http2_cipher_block_list =
util::strieq_l("yes", optarg);
return 0;
"65535], inclusive";
return -1;
}
- config->http.redirect_https_port = optarg;
+ config->http.redirect_https_port = make_string_ref(config->balloc, optarg);
return 0;
}
case SHRPX_OPTID_FRONTEND_MAX_REQUESTS:
config->http.early_data.strip_incoming = !util::strieq_l("yes", optarg);
return 0;
+ case SHRPX_OPTID_QUIC_BPF_PROGRAM_FILE:
+#ifdef ENABLE_HTTP3
+ config->quic.bpf.prog_file = make_string_ref(config->balloc, optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_NO_QUIC_BPF:
+#ifdef ENABLE_HTTP3
+ config->quic.bpf.disabled = util::strieq_l("yes", optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_HTTP2_ALTSVC: {
+ AltSvc altsvc{};
+
+ if (parse_altsvc(altsvc, opt, optarg) != 0) {
+ return -1;
+ }
+
+ config->http.http2_altsvcs.push_back(std::move(altsvc));
+
+ return 0;
+ }
+ case SHRPX_OPTID_FRONTEND_HTTP3_READ_TIMEOUT:
+#ifdef ENABLE_HTTP3
+ return parse_duration(&config->conn.upstream.timeout.http3_read, opt,
+ optarg);
+#else // !ENABLE_HTTP3
+ return 0;
+#endif // !ENABLE_HTTP3
+ case SHRPX_OPTID_FRONTEND_QUIC_IDLE_TIMEOUT:
+#ifdef ENABLE_HTTP3
+ return parse_duration(&config->quic.upstream.timeout.idle, opt, optarg);
+#else // !ENABLE_HTTP3
+ return 0;
+#endif // !ENABLE_HTTP3
+ case SHRPX_OPTID_FRONTEND_QUIC_DEBUG_LOG:
+#ifdef ENABLE_HTTP3
+ config->quic.upstream.debug.log = util::strieq_l("yes", optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_HTTP3_WINDOW_SIZE:
+#ifdef ENABLE_HTTP3
+ if (parse_uint_with_unit(&config->http3.upstream.window_size, opt,
+ optarg) != 0) {
+ return -1;
+ }
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_HTTP3_CONNECTION_WINDOW_SIZE:
+#ifdef ENABLE_HTTP3
+ if (parse_uint_with_unit(&config->http3.upstream.connection_window_size,
+ opt, optarg) != 0) {
+ return -1;
+ }
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_HTTP3_MAX_WINDOW_SIZE:
+#ifdef ENABLE_HTTP3
+ if (parse_uint_with_unit(&config->http3.upstream.max_window_size, opt,
+ optarg) != 0) {
+ return -1;
+ }
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONNECTION_WINDOW_SIZE:
+#ifdef ENABLE_HTTP3
+ if (parse_uint_with_unit(&config->http3.upstream.max_connection_window_size,
+ opt, optarg) != 0) {
+ return -1;
+ }
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_HTTP3_MAX_CONCURRENT_STREAMS:
+#ifdef ENABLE_HTTP3
+ return parse_uint(&config->http3.upstream.max_concurrent_streams, opt,
+ optarg);
+#else // !ENABLE_HTTP3
+ return 0;
+#endif // !ENABLE_HTTP3
+ case SHRPX_OPTID_FRONTEND_QUIC_EARLY_DATA:
+#ifdef ENABLE_HTTP3
+ config->quic.upstream.early_data = util::strieq_l("yes", optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_QUIC_QLOG_DIR:
+#ifdef ENABLE_HTTP3
+ config->quic.upstream.qlog.dir = make_string_ref(config->balloc, optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_QUIC_REQUIRE_TOKEN:
+#ifdef ENABLE_HTTP3
+ config->quic.upstream.require_token = util::strieq_l("yes", optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_QUIC_CONGESTION_CONTROLLER:
+#ifdef ENABLE_HTTP3
+ if (util::strieq_l("cubic", optarg)) {
+ config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_CUBIC;
+ } else if (util::strieq_l("bbr", optarg)) {
+ config->quic.upstream.congestion_controller = NGTCP2_CC_ALGO_BBR;
+ } else {
+ LOG(ERROR) << opt << ": must be either cubic or bbr";
+ return -1;
+ }
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_QUIC_SERVER_ID:
+#ifdef ENABLE_HTTP3
+ if (optarg.size() != config->quic.server_id.size() * 2 ||
+ !util::is_hex_string(optarg)) {
+ LOG(ERROR) << opt << ": must be a hex-string";
+ return -1;
+ }
+ util::decode_hex(std::begin(config->quic.server_id), optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_FRONTEND_QUIC_SECRET_FILE:
+#ifdef ENABLE_HTTP3
+ config->quic.upstream.secret_file = make_string_ref(config->balloc, optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ case SHRPX_OPTID_RLIMIT_MEMLOCK: {
+ int n;
+
+ if (parse_uint(&n, opt, optarg) != 0) {
+ return -1;
+ }
+
+ if (n < 0) {
+ LOG(ERROR) << opt << ": specify the integer more than or equal to 0";
+
+ return -1;
+ }
+
+ config->rlimit_memlock = n;
+
+ return 0;
+ }
+ case SHRPX_OPTID_MAX_WORKER_PROCESSES:
+ return parse_uint(&config->max_worker_processes, opt, optarg);
+ case SHRPX_OPTID_WORKER_PROCESS_GRACE_SHUTDOWN_PERIOD:
+ return parse_duration(&config->worker_process_grace_shutdown_period, opt,
+ optarg);
+ case SHRPX_OPTID_FRONTEND_QUIC_INITIAL_RTT: {
+#ifdef ENABLE_HTTP3
+ return parse_duration(&config->quic.upstream.initial_rtt, opt, optarg);
+#endif // ENABLE_HTTP3
+
+ return 0;
+ }
case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored";
return StringRef::from_lit("http/1.1");
case Proto::HTTP2:
return StringRef::from_lit("h2");
+ case Proto::HTTP3:
+ return StringRef::from_lit("h3");
case Proto::MEMCACHED:
return StringRef::from_lit("memcached");
}
auto resolve_flags = numeric_addr_only ? AI_NUMERICHOST | AI_NUMERICSERV : 0;
+ std::array<char, util::max_hostport> hostport_buf;
+
for (auto &g : addr_groups) {
std::unordered_map<StringRef, uint32_t> wgchk;
for (auto &addr : g.addrs) {
util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
auto hostport =
- util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
+ util::make_hostport(std::begin(hostport_buf), addr.host, addr.port);
if (!addr.dns) {
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,