nghttp2/src/shrpx_config.cc

2902 lines
80 KiB
C++

/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2012 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "shrpx_config.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif // HAVE_PWD_H
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif // HAVE_NETDB_H
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif // HAVE_SYSLOG_H
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif // HAVE_FCNTL_H
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#include <cstring>
#include <cerrno>
#include <limits>
#include <fstream>
#include <nghttp2/nghttp2.h>
#include "http-parser/http_parser.h"
#include "shrpx_log.h"
#include "shrpx_ssl.h"
#include "shrpx_http.h"
#include "util.h"
#include "base64.h"
namespace shrpx {
namespace {
Config *config = nullptr;
} // namespace
constexpr auto SHRPX_UNIX_PATH_PREFIX = StringRef::from_lit("unix:");
const Config *get_config() { return config; }
Config *mod_config() { return config; }
void create_config() { config = new Config(); }
TicketKeys::~TicketKeys() {
/* Erase keys from memory */
for (auto &key : keys) {
memset(&key, 0, sizeof(key));
}
}
namespace {
int split_host_port(char *host, size_t hostlen, uint16_t *port_ptr,
const StringRef &hostport, const StringRef &opt) {
// host and port in |hostport| is separated by single ','.
auto sep = std::find(std::begin(hostport), std::end(hostport), ',');
if (sep == std::end(hostport)) {
LOG(ERROR) << opt << ": Invalid host, port: " << hostport;
return -1;
}
size_t len = sep - std::begin(hostport);
if (hostlen < len + 1) {
LOG(ERROR) << opt << ": Hostname too long: " << hostport;
return -1;
}
std::copy(std::begin(hostport), sep, host);
host[len] = '\0';
auto portstr = StringRef{sep + 1, std::end(hostport)};
auto d = util::parse_uint(portstr);
if (1 <= d && d <= std::numeric_limits<uint16_t>::max()) {
*port_ptr = d;
return 0;
}
LOG(ERROR) << opt << ": Port is invalid: " << portstr;
return -1;
}
} // namespace
namespace {
bool is_secure(const StringRef &filename) {
struct stat buf;
int rv = stat(filename.c_str(), &buf);
if (rv == 0) {
if ((buf.st_mode & S_IRWXU) && !(buf.st_mode & S_IRWXG) &&
!(buf.st_mode & S_IRWXO)) {
return true;
}
}
return false;
}
} // namespace
std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<std::string> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac) {
auto ticket_keys = make_unique<TicketKeys>();
auto &keys = ticket_keys->keys;
keys.resize(files.size());
auto enc_keylen = EVP_CIPHER_key_length(cipher);
auto hmac_keylen = EVP_MD_size(hmac);
if (cipher == EVP_aes_128_cbc()) {
// backward compatibility, as a legacy of using same file format
// with nginx and apache.
hmac_keylen = 16;
}
auto expectedlen = keys[0].data.name.size() + enc_keylen + hmac_keylen;
char buf[256];
assert(sizeof(buf) >= expectedlen);
size_t i = 0;
for (auto &file : files) {
struct stat fst {};
if (stat(file.c_str(), &fst) == -1) {
auto error = errno;
LOG(ERROR) << "tls-ticket-key-file: could not stat file " << file
<< ", errno=" << error;
return nullptr;
}
if (static_cast<size_t>(fst.st_size) != expectedlen) {
LOG(ERROR) << "tls-ticket-key-file: the expected file size is "
<< expectedlen << ", the actual file size is " << fst.st_size;
return nullptr;
}
std::ifstream f(file.c_str());
if (!f) {
LOG(ERROR) << "tls-ticket-key-file: could not open file " << file;
return nullptr;
}
f.read(buf, expectedlen);
if (static_cast<size_t>(f.gcount()) != expectedlen) {
LOG(ERROR) << "tls-ticket-key-file: want to read " << expectedlen
<< " bytes but only read " << f.gcount() << " bytes from "
<< file;
return nullptr;
}
auto &key = keys[i++];
key.cipher = cipher;
key.hmac = hmac;
key.hmac_keylen = hmac_keylen;
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "enc_keylen=" << enc_keylen
<< ", hmac_keylen=" << key.hmac_keylen;
}
auto p = buf;
std::copy_n(p, key.data.name.size(), std::begin(key.data.name));
p += key.data.name.size();
std::copy_n(p, enc_keylen, std::begin(key.data.enc_key));
p += enc_keylen;
std::copy_n(p, hmac_keylen, std::begin(key.data.hmac_key));
if (LOG_ENABLED(INFO)) {
LOG(INFO) << "session ticket key: " << util::format_hex(key.data.name);
}
}
return ticket_keys;
}
FILE *open_file_for_write(const char *filename) {
#if defined O_CLOEXEC
auto fd = open(filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR);
#else
auto fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
// We get race condition if execve is called at the same time.
if (fd != -1) {
util::make_socket_closeonexec(fd);
}
#endif
if (fd == -1) {
LOG(ERROR) << "Failed to open " << filename
<< " for writing. Cause: " << strerror(errno);
return nullptr;
}
auto f = fdopen(fd, "wb");
if (f == nullptr) {
LOG(ERROR) << "Failed to open " << filename
<< " for writing. Cause: " << strerror(errno);
return nullptr;
}
return f;
}
namespace {
// Read passwd from |filename|
std::string read_passwd_from_file(const StringRef &opt,
const StringRef &filename) {
std::string line;
if (!is_secure(filename)) {
LOG(ERROR) << opt << ": Private key passwd file " << filename
<< " has insecure mode.";
return line;
}
std::ifstream in(filename.c_str(), std::ios::binary);
if (!in) {
LOG(ERROR) << opt << ": Could not open key passwd file " << filename;
return line;
}
std::getline(in, line);
return line;
}
} // namespace
Headers::value_type parse_header(const StringRef &optarg) {
auto colon = std::find(std::begin(optarg), std::end(optarg), ':');
if (colon == std::end(optarg) || colon == std::begin(optarg)) {
return {"", ""};
}
auto value = colon + 1;
for (; *value == '\t' || *value == ' '; ++value)
;
auto p = Header(std::string{std::begin(optarg), colon},
std::string{value, std::end(optarg)});
util::inp_strlower(p.name);
if (!nghttp2_check_header_name(
reinterpret_cast<const uint8_t *>(p.name.c_str()), p.name.size()) ||
!nghttp2_check_header_value(
reinterpret_cast<const uint8_t *>(p.value.c_str()), p.value.size())) {
return Header{};
}
return p;
}
template <typename T>
int parse_uint(T *dest, const StringRef &opt, const StringRef &optarg) {
auto val = util::parse_uint(optarg);
if (val == -1) {
LOG(ERROR) << opt << ": bad value. Specify an integer >= 0.";
return -1;
}
*dest = val;
return 0;
}
namespace {
template <typename T>
int parse_uint_with_unit(T *dest, const StringRef &opt,
const StringRef &optarg) {
auto n = util::parse_uint_with_unit(optarg);
if (n == -1) {
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
return -1;
}
*dest = n;
return 0;
}
} // namespace
// Parses |optarg| as signed integer. This requires |optarg| to be
// NULL-terminated string.
template <typename T>
int parse_int(T *dest, const StringRef &opt, const char *optarg) {
char *end = nullptr;
errno = 0;
auto val = strtol(optarg, &end, 10);
if (!optarg[0] || errno != 0 || *end) {
LOG(ERROR) << opt << ": bad value. Specify an integer.";
return -1;
}
*dest = val;
return 0;
}
namespace {
// generated by gennghttpxfun.py
LogFragmentType log_var_lookup_token(const char *name, size_t namelen) {
switch (namelen) {
case 3:
switch (name[2]) {
case 'd':
if (util::strieq_l("pi", name, 2)) {
return SHRPX_LOGF_PID;
}
break;
}
break;
case 4:
switch (name[3]) {
case 'n':
if (util::strieq_l("alp", name, 3)) {
return SHRPX_LOGF_ALPN;
}
break;
}
break;
case 6:
switch (name[5]) {
case 's':
if (util::strieq_l("statu", name, 5)) {
return SHRPX_LOGF_STATUS;
}
break;
}
break;
case 7:
switch (name[6]) {
case 't':
if (util::strieq_l("reques", name, 6)) {
return SHRPX_LOGF_REQUEST;
}
break;
}
break;
case 10:
switch (name[9]) {
case 'l':
if (util::strieq_l("time_loca", name, 9)) {
return SHRPX_LOGF_TIME_LOCAL;
}
break;
case 'r':
if (util::strieq_l("ssl_ciphe", name, 9)) {
return SHRPX_LOGF_SSL_CIPHER;
}
break;
}
break;
case 11:
switch (name[10]) {
case 'r':
if (util::strieq_l("remote_add", name, 10)) {
return SHRPX_LOGF_REMOTE_ADDR;
}
break;
case 't':
if (util::strieq_l("remote_por", name, 10)) {
return SHRPX_LOGF_REMOTE_PORT;
}
if (util::strieq_l("server_por", name, 10)) {
return SHRPX_LOGF_SERVER_PORT;
}
break;
}
break;
case 12:
switch (name[11]) {
case '1':
if (util::strieq_l("time_iso860", name, 11)) {
return SHRPX_LOGF_TIME_ISO8601;
}
break;
case 'e':
if (util::strieq_l("request_tim", name, 11)) {
return SHRPX_LOGF_REQUEST_TIME;
}
break;
case 'l':
if (util::strieq_l("ssl_protoco", name, 11)) {
return SHRPX_LOGF_SSL_PROTOCOL;
}
break;
}
break;
case 14:
switch (name[13]) {
case 'd':
if (util::strieq_l("ssl_session_i", name, 13)) {
return SHRPX_LOGF_SSL_SESSION_ID;
}
break;
}
break;
case 15:
switch (name[14]) {
case 't':
if (util::strieq_l("body_bytes_sen", name, 14)) {
return SHRPX_LOGF_BODY_BYTES_SENT;
}
break;
}
break;
case 18:
switch (name[17]) {
case 'd':
if (util::strieq_l("ssl_session_reuse", name, 17)) {
return SHRPX_LOGF_SSL_SESSION_REUSED;
}
break;
}
break;
}
return SHRPX_LOGF_NONE;
}
} // namespace
namespace {
bool var_token(char c) {
return util::is_alpha(c) || util::is_digit(c) || c == '_';
}
} // namespace
std::vector<LogFragment> parse_log_format(const StringRef &optarg) {
auto literal_start = std::begin(optarg);
auto p = literal_start;
auto eop = std::end(optarg);
auto res = std::vector<LogFragment>();
for (; p != eop;) {
if (*p != '$') {
++p;
continue;
}
auto var_start = p;
++p;
const char *var_name;
size_t var_namelen;
if (p != eop && *p == '{') {
var_name = ++p;
for (; p != eop && var_token(*p); ++p)
;
if (p == eop || *p != '}') {
LOG(WARN) << "Missing '}' after " << StringRef{var_start, p};
continue;
}
var_namelen = p - var_name;
++p;
} else {
var_name = p;
for (; p != eop && var_token(*p); ++p)
;
var_namelen = p - var_name;
}
const char *value = nullptr;
auto type = log_var_lookup_token(var_name, var_namelen);
if (type == SHRPX_LOGF_NONE) {
if (util::istarts_with_l(StringRef{var_name, var_namelen}, "http_")) {
if (util::streq_l("host", StringRef{var_name + str_size("http_"),
var_namelen - str_size("http_")})) {
// Special handling of host header field. We will use
// :authority header field if host header is missing. This
// is a typical case in HTTP/2.
type = SHRPX_LOGF_AUTHORITY;
} else {
type = SHRPX_LOGF_HTTP;
value = var_name + str_size("http_");
}
} else {
LOG(WARN) << "Unrecognized log format variable: "
<< StringRef{var_name, var_namelen};
continue;
}
}
if (literal_start < var_start) {
res.emplace_back(SHRPX_LOGF_LITERAL,
ImmutableString(literal_start, var_start));
}
literal_start = p;
if (value == nullptr) {
res.emplace_back(type);
continue;
}
auto name = std::string(value, var_name + var_namelen);
for (auto &c : name) {
if (c == '_') {
c = '-';
}
}
res.emplace_back(type, ImmutableString(name));
}
if (literal_start != eop) {
res.emplace_back(SHRPX_LOGF_LITERAL, ImmutableString(literal_start, eop));
}
return res;
}
namespace {
int parse_address_family(int *dest, const StringRef &opt,
const StringRef &optarg) {
if (util::strieq_l("auto", optarg)) {
*dest = AF_UNSPEC;
return 0;
}
if (util::strieq_l("IPv4", optarg)) {
*dest = AF_INET;
return 0;
}
if (util::strieq_l("IPv6", optarg)) {
*dest = AF_INET6;
return 0;
}
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
return -1;
}
} // namespace
namespace {
int parse_duration(ev_tstamp *dest, const StringRef &opt,
const StringRef &optarg) {
auto t = util::parse_duration_with_unit(optarg);
if (t == std::numeric_limits<double>::infinity()) {
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
return -1;
}
*dest = t;
return 0;
}
} // namespace
struct MemcachedConnectionParams {
bool tls;
};
namespace {
// Parses memcached connection configuration parameter |src_params|,
// and stores parsed results into |out|. This function returns 0 if
// it succeeds, or -1.
int parse_memcached_connection_params(MemcachedConnectionParams &out,
const StringRef &src_params,
const StringRef &opt) {
auto last = std::end(src_params);
for (auto first = std::begin(src_params); first != last;) {
auto end = std::find(first, last, ';');
auto param = StringRef{first, end};
if (util::strieq_l("tls", param)) {
out.tls = true;
} else if (util::strieq_l("no-tls", param)) {
out.tls = false;
} else if (!param.empty()) {
LOG(ERROR) << opt << ": " << param << ": unknown keyword";
return -1;
}
if (end == last) {
break;
}
first = end + 1;
}
return 0;
}
} // namespace
struct UpstreamParams {
bool tls;
};
namespace {
// Parses upstream configuration parameter |src_params|, and stores
// parsed results into |out|. This function returns 0 if it succeeds,
// or -1.
int parse_upstream_params(UpstreamParams &out, const StringRef &src_params) {
auto last = std::end(src_params);
for (auto first = std::begin(src_params); first != last;) {
auto end = std::find(first, last, ';');
auto param = StringRef{first, end};
if (util::strieq_l("tls", param)) {
out.tls = true;
} else if (util::strieq_l("no-tls", param)) {
out.tls = false;
} else if (!param.empty()) {
LOG(ERROR) << "frontend: " << param << ": unknown keyword";
return -1;
}
if (end == last) {
break;
}
first = end + 1;
}
return 0;
}
} // namespace
struct DownstreamParams {
size_t fall;
size_t rise;
shrpx_proto proto;
bool tls;
};
namespace {
// Parses downstream configuration parameter |src_params|, and stores
// parsed results into |out|. This function returns 0 if it succeeds,
// or -1.
int parse_downstream_params(DownstreamParams &out,
const StringRef &src_params) {
auto last = std::end(src_params);
for (auto first = std::begin(src_params); first != last;) {
auto end = std::find(first, last, ';');
auto param = StringRef{first, end};
if (util::istarts_with_l(param, "proto=")) {
auto protostr = StringRef{first + str_size("proto="), end};
if (protostr.empty()) {
LOG(ERROR) << "backend: proto: protocol is empty";
return -1;
}
if (util::streq_l("h2", std::begin(protostr), protostr.size())) {
out.proto = PROTO_HTTP2;
} else if (util::streq_l("http/1.1", std::begin(protostr),
protostr.size())) {
out.proto = PROTO_HTTP1;
} else {
LOG(ERROR) << "backend: proto: unknown protocol " << protostr;
return -1;
}
} else if (util::istarts_with_l(param, "fall=")) {
auto valstr = StringRef{first + str_size("fall="), end};
if (valstr.empty()) {
LOG(ERROR) << "backend: fall: non-negative integer is expected";
return -1;
}
auto n = util::parse_uint(valstr);
if (n == -1) {
LOG(ERROR) << "backend: fall: non-negative integer is expected";
return -1;
}
out.fall = n;
} else if (util::istarts_with_l(param, "rise=")) {
auto valstr = StringRef{first + str_size("rise="), end};
if (valstr.empty()) {
LOG(ERROR) << "backend: rise: non-negative integer is expected";
return -1;
}
auto n = util::parse_uint(valstr);
if (n == -1) {
LOG(ERROR) << "backend: rise: non-negative integer is expected";
return -1;
}
out.rise = n;
} else if (util::strieq_l("tls", param)) {
out.tls = true;
} else if (util::strieq_l("no-tls", param)) {
out.tls = false;
} else if (!param.empty()) {
LOG(ERROR) << "backend: " << param << ": unknown keyword";
return -1;
}
if (end == last) {
break;
}
first = end + 1;
}
return 0;
}
} // namespace
namespace {
// Parses host-path mapping patterns in |src_pattern|, and stores
// mappings in config. We will store each host-path pattern found in
// |src| with |addr|. |addr| will be copied accordingly. Also we
// make a group based on the pattern. The "/" pattern is considered
// as catch-all. We also parse protocol specified in |src_proto|.
//
// This function returns 0 if it succeeds, or -1.
int parse_mapping(DownstreamAddrConfig addr, const StringRef &src_pattern,
const StringRef &src_params) {
// This returns at least 1 element (it could be empty string). We
// will append '/' to all patterns, so it becomes catch-all pattern.
auto mapping = util::split_str(src_pattern, ':');
assert(!mapping.empty());
auto &addr_groups = mod_config()->conn.downstream.addr_groups;
DownstreamParams params{};
params.proto = PROTO_HTTP1;
if (parse_downstream_params(params, src_params) != 0) {
return -1;
}
addr.fall = params.fall;
addr.rise = params.rise;
for (const auto &raw_pattern : mapping) {
auto done = false;
std::string pattern;
auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
if (slash == std::end(raw_pattern)) {
// This effectively makes empty pattern to "/".
pattern.assign(std::begin(raw_pattern), std::end(raw_pattern));
util::inp_strlower(pattern);
pattern += '/';
} else {
pattern.assign(std::begin(raw_pattern), slash);
util::inp_strlower(pattern);
pattern += http2::normalize_path(StringRef{slash, std::end(raw_pattern)},
StringRef{});
}
for (auto &g : addr_groups) {
if (g.pattern == pattern) {
if (g.proto != params.proto) {
LOG(ERROR) << "backend: protocol mismatch. We saw protocol "
<< strproto(g.proto) << " for pattern " << g.pattern
<< ", but another protocol " << strproto(params.proto);
return -1;
}
if (g.tls != params.tls) {
LOG(ERROR) << "backend: TLS mismatch. We saw TLS was "
<< (g.tls ? "enabled" : "disabled") << " for pattern "
<< g.pattern << ", but we now got TLS was "
<< (params.tls ? "enabled" : "disabled");
return -1;
}
g.addrs.push_back(addr);
done = true;
break;
}
}
if (done) {
continue;
}
DownstreamAddrGroupConfig g(StringRef{pattern});
g.addrs.push_back(addr);
g.proto = params.proto;
g.tls = params.tls;
if (pattern[0] == '*') {
// wildcard pattern
auto path_first =
std::find(std::begin(g.pattern), std::end(g.pattern), '/');
auto host = StringRef{std::begin(g.pattern) + 1, path_first};
auto path = StringRef{path_first, std::end(g.pattern)};
auto &wildcard_patterns = mod_config()->wildcard_patterns;
auto it = std::find_if(
std::begin(wildcard_patterns), std::end(wildcard_patterns),
[&host](const WildcardPattern &wp) { return wp.host == host; });
if (it == std::end(wildcard_patterns)) {
mod_config()->wildcard_patterns.push_back(
{ImmutableString{std::begin(host), std::end(host)}});
auto &router = mod_config()->wildcard_patterns.back().router;
router.add_route(path, addr_groups.size());
} else {
(*it).router.add_route(path, addr_groups.size());
}
} else {
mod_config()->router.add_route(StringRef{g.pattern}, addr_groups.size());
}
addr_groups.push_back(std::move(g));
}
return 0;
}
} // namespace
namespace {
int parse_forwarded_node_type(const StringRef &optarg) {
if (util::strieq_l("obfuscated", optarg)) {
return FORWARDED_NODE_OBFUSCATED;
}
if (util::strieq_l("ip", optarg)) {
return FORWARDED_NODE_IP;
}
if (optarg.size() < 2 || optarg[0] != '_') {
return -1;
}
if (std::find_if_not(std::begin(optarg), std::end(optarg), [](char c) {
return util::is_alpha(c) || util::is_digit(c) || c == '.' || c == '_' ||
c == '-';
}) != std::end(optarg)) {
return -1;
}
return FORWARDED_NODE_OBFUSCATED;
}
} // namespace
namespace {
int parse_error_page(std::vector<ErrorPage> &error_pages, const StringRef &opt,
const StringRef &optarg) {
auto eq = std::find(std::begin(optarg), std::end(optarg), '=');
if (eq == std::end(optarg) || eq + 1 == std::end(optarg)) {
LOG(ERROR) << opt << ": bad value: '" << optarg << "'";
return -1;
}
auto codestr = StringRef{std::begin(optarg), eq};
unsigned int code;
if (codestr == StringRef::from_lit("*")) {
code = 0;
} else {
auto n = util::parse_uint(codestr);
if (n == -1 || n < 400 || n > 599) {
LOG(ERROR) << opt << ": bad code: '" << codestr << "'";
return -1;
}
code = static_cast<unsigned int>(n);
}
auto path = StringRef{eq + 1, std::end(optarg)};
std::vector<uint8_t> content;
auto fd = open(path.c_str(), O_RDONLY);
if (fd == -1) {
auto error = errno;
LOG(ERROR) << opt << ": " << optarg << ": " << strerror(error);
return -1;
}
auto fd_closer = defer(close, fd);
std::array<uint8_t, 4096> buf;
for (;;) {
auto n = read(fd, buf.data(), buf.size());
if (n == -1) {
auto error = errno;
LOG(ERROR) << opt << ": " << optarg << ": " << strerror(error);
return -1;
}
if (n == 0) {
break;
}
content.insert(std::end(content), std::begin(buf), std::begin(buf) + n);
}
error_pages.push_back(ErrorPage{std::move(content), code});
return 0;
}
} // namespace
// generated by gennghttpxfun.py
enum {
SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL,
SHRPX_OPTID_ACCESSLOG_FILE,
SHRPX_OPTID_ACCESSLOG_FORMAT,
SHRPX_OPTID_ACCESSLOG_SYSLOG,
SHRPX_OPTID_ADD_FORWARDED,
SHRPX_OPTID_ADD_REQUEST_HEADER,
SHRPX_OPTID_ADD_RESPONSE_HEADER,
SHRPX_OPTID_ADD_X_FORWARDED_FOR,
SHRPX_OPTID_ALTSVC,
SHRPX_OPTID_BACKEND,
SHRPX_OPTID_BACKEND_ADDRESS_FAMILY,
SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND,
SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST,
SHRPX_OPTID_BACKEND_HTTP_PROXY_URI,
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND,
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
SHRPX_OPTID_BACKEND_HTTP1_TLS,
SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS,
SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS,
SHRPX_OPTID_BACKEND_IPV4,
SHRPX_OPTID_BACKEND_IPV6,
SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT,
SHRPX_OPTID_BACKEND_NO_TLS,
SHRPX_OPTID_BACKEND_READ_TIMEOUT,
SHRPX_OPTID_BACKEND_REQUEST_BUFFER,
SHRPX_OPTID_BACKEND_RESPONSE_BUFFER,
SHRPX_OPTID_BACKEND_TLS,
SHRPX_OPTID_BACKEND_TLS_SNI_FIELD,
SHRPX_OPTID_BACKEND_WRITE_TIMEOUT,
SHRPX_OPTID_BACKLOG,
SHRPX_OPTID_CACERT,
SHRPX_OPTID_CERTIFICATE_FILE,
SHRPX_OPTID_CIPHERS,
SHRPX_OPTID_CLIENT,
SHRPX_OPTID_CLIENT_CERT_FILE,
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
SHRPX_OPTID_CLIENT_PROXY,
SHRPX_OPTID_CONF,
SHRPX_OPTID_DAEMON,
SHRPX_OPTID_DH_PARAM_FILE,
SHRPX_OPTID_ERROR_PAGE,
SHRPX_OPTID_ERRORLOG_FILE,
SHRPX_OPTID_ERRORLOG_SYSLOG,
SHRPX_OPTID_FASTOPEN,
SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE,
SHRPX_OPTID_FORWARDED_BY,
SHRPX_OPTID_FORWARDED_FOR,
SHRPX_OPTID_FRONTEND,
SHRPX_OPTID_FRONTEND_FRAME_DEBUG,
SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS,
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER,
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER,
SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS,
SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
SHRPX_OPTID_FRONTEND_NO_TLS,
SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,
SHRPX_OPTID_HEADER_FIELD_BUFFER,
SHRPX_OPTID_HOST_REWRITE,
SHRPX_OPTID_HTTP2_BRIDGE,
SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS,
SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING,
SHRPX_OPTID_HTTP2_PROXY,
SHRPX_OPTID_INCLUDE,
SHRPX_OPTID_INSECURE,
SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT,
SHRPX_OPTID_LOG_LEVEL,
SHRPX_OPTID_MAX_HEADER_FIELDS,
SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS,
SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS,
SHRPX_OPTID_MRUBY_FILE,
SHRPX_OPTID_NO_HOST_REWRITE,
SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST,
SHRPX_OPTID_NO_LOCATION_REWRITE,
SHRPX_OPTID_NO_OCSP,
SHRPX_OPTID_NO_SERVER_PUSH,
SHRPX_OPTID_NO_VIA,
SHRPX_OPTID_NPN_LIST,
SHRPX_OPTID_OCSP_UPDATE_INTERVAL,
SHRPX_OPTID_PADDING,
SHRPX_OPTID_PID_FILE,
SHRPX_OPTID_PRIVATE_KEY_FILE,
SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE,
SHRPX_OPTID_READ_BURST,
SHRPX_OPTID_READ_RATE,
SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER,
SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER,
SHRPX_OPTID_RLIMIT_NOFILE,
SHRPX_OPTID_STREAM_READ_TIMEOUT,
SHRPX_OPTID_STREAM_WRITE_TIMEOUT,
SHRPX_OPTID_STRIP_INCOMING_FORWARDED,
SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR,
SHRPX_OPTID_SUBCERT,
SHRPX_OPTID_SYSLOG_FACILITY,
SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT,
SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD,
SHRPX_OPTID_TLS_PROTO_LIST,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE,
SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS,
SHRPX_OPTID_TLS_TICKET_KEY_CIPHER,
SHRPX_OPTID_TLS_TICKET_KEY_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE,
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS,
SHRPX_OPTID_USER,
SHRPX_OPTID_VERIFY_CLIENT,
SHRPX_OPTID_VERIFY_CLIENT_CACERT,
SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS,
SHRPX_OPTID_WORKER_READ_BURST,
SHRPX_OPTID_WORKER_READ_RATE,
SHRPX_OPTID_WORKER_WRITE_BURST,
SHRPX_OPTID_WORKER_WRITE_RATE,
SHRPX_OPTID_WORKERS,
SHRPX_OPTID_WRITE_BURST,
SHRPX_OPTID_WRITE_RATE,
SHRPX_OPTID_MAXIDX,
};
namespace {
// generated by gennghttpxfun.py
int option_lookup_token(const char *name, size_t namelen) {
switch (namelen) {
case 4:
switch (name[3]) {
case 'f':
if (util::strieq_l("con", name, 3)) {
return SHRPX_OPTID_CONF;
}
break;
case 'r':
if (util::strieq_l("use", name, 3)) {
return SHRPX_OPTID_USER;
}
break;
}
break;
case 6:
switch (name[5]) {
case 'a':
if (util::strieq_l("no-vi", name, 5)) {
return SHRPX_OPTID_NO_VIA;
}
break;
case 'c':
if (util::strieq_l("altsv", name, 5)) {
return SHRPX_OPTID_ALTSVC;
}
break;
case 'n':
if (util::strieq_l("daemo", name, 5)) {
return SHRPX_OPTID_DAEMON;
}
break;
case 't':
if (util::strieq_l("cacer", name, 5)) {
return SHRPX_OPTID_CACERT;
}
if (util::strieq_l("clien", name, 5)) {
return SHRPX_OPTID_CLIENT;
}
break;
}
break;
case 7:
switch (name[6]) {
case 'd':
if (util::strieq_l("backen", name, 6)) {
return SHRPX_OPTID_BACKEND;
}
break;
case 'e':
if (util::strieq_l("includ", name, 6)) {
return SHRPX_OPTID_INCLUDE;
}
break;
case 'g':
if (util::strieq_l("backlo", name, 6)) {
return SHRPX_OPTID_BACKLOG;
}
if (util::strieq_l("paddin", name, 6)) {
return SHRPX_OPTID_PADDING;
}
break;
case 'p':
if (util::strieq_l("no-ocs", name, 6)) {
return SHRPX_OPTID_NO_OCSP;
}
break;
case 's':
if (util::strieq_l("cipher", name, 6)) {
return SHRPX_OPTID_CIPHERS;
}
if (util::strieq_l("worker", name, 6)) {
return SHRPX_OPTID_WORKERS;
}
break;
case 't':
if (util::strieq_l("subcer", name, 6)) {
return SHRPX_OPTID_SUBCERT;
}
break;
}
break;
case 8:
switch (name[7]) {
case 'd':
if (util::strieq_l("fronten", name, 7)) {
return SHRPX_OPTID_FRONTEND;
}
break;
case 'e':
if (util::strieq_l("insecur", name, 7)) {
return SHRPX_OPTID_INSECURE;
}
if (util::strieq_l("pid-fil", name, 7)) {
return SHRPX_OPTID_PID_FILE;
}
break;
case 'n':
if (util::strieq_l("fastope", name, 7)) {
return SHRPX_OPTID_FASTOPEN;
}
break;
case 't':
if (util::strieq_l("npn-lis", name, 7)) {
return SHRPX_OPTID_NPN_LIST;
}
break;
}
break;
case 9:
switch (name[8]) {
case 'e':
if (util::strieq_l("read-rat", name, 8)) {
return SHRPX_OPTID_READ_RATE;
}
break;
case 'l':
if (util::strieq_l("log-leve", name, 8)) {
return SHRPX_OPTID_LOG_LEVEL;
}
break;
}
break;
case 10:
switch (name[9]) {
case 'e':
if (util::strieq_l("error-pag", name, 9)) {
return SHRPX_OPTID_ERROR_PAGE;
}
if (util::strieq_l("mruby-fil", name, 9)) {
return SHRPX_OPTID_MRUBY_FILE;
}
if (util::strieq_l("write-rat", name, 9)) {
return SHRPX_OPTID_WRITE_RATE;
}
break;
case 't':
if (util::strieq_l("read-burs", name, 9)) {
return SHRPX_OPTID_READ_BURST;
}
break;
}
break;
case 11:
switch (name[10]) {
case 's':
if (util::strieq_l("backend-tl", name, 10)) {
return SHRPX_OPTID_BACKEND_TLS;
}
break;
case 't':
if (util::strieq_l("write-burs", name, 10)) {
return SHRPX_OPTID_WRITE_BURST;
}
break;
case 'y':
if (util::strieq_l("http2-prox", name, 10)) {
return SHRPX_OPTID_HTTP2_PROXY;
}
break;
}
break;
case 12:
switch (name[11]) {
case '4':
if (util::strieq_l("backend-ipv", name, 11)) {
return SHRPX_OPTID_BACKEND_IPV4;
}
break;
case '6':
if (util::strieq_l("backend-ipv", name, 11)) {
return SHRPX_OPTID_BACKEND_IPV6;
}
break;
case 'e':
if (util::strieq_l("host-rewrit", name, 11)) {
return SHRPX_OPTID_HOST_REWRITE;
}
if (util::strieq_l("http2-bridg", name, 11)) {
return SHRPX_OPTID_HTTP2_BRIDGE;
}
break;
case 'y':
if (util::strieq_l("client-prox", name, 11)) {
return SHRPX_OPTID_CLIENT_PROXY;
}
if (util::strieq_l("forwarded-b", name, 11)) {
return SHRPX_OPTID_FORWARDED_BY;
}
break;
}
break;
case 13:
switch (name[12]) {
case 'd':
if (util::strieq_l("add-forwarde", name, 12)) {
return SHRPX_OPTID_ADD_FORWARDED;
}
break;
case 'e':
if (util::strieq_l("dh-param-fil", name, 12)) {
return SHRPX_OPTID_DH_PARAM_FILE;
}
if (util::strieq_l("errorlog-fil", name, 12)) {
return SHRPX_OPTID_ERRORLOG_FILE;
}
if (util::strieq_l("rlimit-nofil", name, 12)) {
return SHRPX_OPTID_RLIMIT_NOFILE;
}
break;
case 'r':
if (util::strieq_l("forwarded-fo", name, 12)) {
return SHRPX_OPTID_FORWARDED_FOR;
}
break;
case 't':
if (util::strieq_l("verify-clien", name, 12)) {
return SHRPX_OPTID_VERIFY_CLIENT;
}
break;
}
break;
case 14:
switch (name[13]) {
case 'e':
if (util::strieq_l("accesslog-fil", name, 13)) {
return SHRPX_OPTID_ACCESSLOG_FILE;
}
break;
case 'h':
if (util::strieq_l("no-server-pus", name, 13)) {
return SHRPX_OPTID_NO_SERVER_PUSH;
}
break;
case 's':
if (util::strieq_l("backend-no-tl", name, 13)) {
return SHRPX_OPTID_BACKEND_NO_TLS;
}
break;
case 't':
if (util::strieq_l("tls-proto-lis", name, 13)) {
return SHRPX_OPTID_TLS_PROTO_LIST;
}
break;
}
break;
case 15:
switch (name[14]) {
case 'e':
if (util::strieq_l("no-host-rewrit", name, 14)) {
return SHRPX_OPTID_NO_HOST_REWRITE;
}
break;
case 'g':
if (util::strieq_l("errorlog-syslo", name, 14)) {
return SHRPX_OPTID_ERRORLOG_SYSLOG;
}
break;
case 's':
if (util::strieq_l("frontend-no-tl", name, 14)) {
return SHRPX_OPTID_FRONTEND_NO_TLS;
}
break;
case 'y':
if (util::strieq_l("syslog-facilit", name, 14)) {
return SHRPX_OPTID_SYSLOG_FACILITY;
}
break;
}
break;
case 16:
switch (name[15]) {
case 'e':
if (util::strieq_l("certificate-fil", name, 15)) {
return SHRPX_OPTID_CERTIFICATE_FILE;
}
if (util::strieq_l("client-cert-fil", name, 15)) {
return SHRPX_OPTID_CLIENT_CERT_FILE;
}
if (util::strieq_l("private-key-fil", name, 15)) {
return SHRPX_OPTID_PRIVATE_KEY_FILE;
}
if (util::strieq_l("worker-read-rat", name, 15)) {
return SHRPX_OPTID_WORKER_READ_RATE;
}
break;
case 'g':
if (util::strieq_l("accesslog-syslo", name, 15)) {
return SHRPX_OPTID_ACCESSLOG_SYSLOG;
}
break;
case 't':
if (util::strieq_l("accesslog-forma", name, 15)) {
return SHRPX_OPTID_ACCESSLOG_FORMAT;
}
break;
}
break;
case 17:
switch (name[16]) {
case 'e':
if (util::strieq_l("worker-write-rat", name, 16)) {
return SHRPX_OPTID_WORKER_WRITE_RATE;
}
break;
case 's':
if (util::strieq_l("backend-http1-tl", name, 16)) {
return SHRPX_OPTID_BACKEND_HTTP1_TLS;
}
if (util::strieq_l("max-header-field", name, 16)) {
return SHRPX_OPTID_MAX_HEADER_FIELDS;
}
break;
case 't':
if (util::strieq_l("worker-read-burs", name, 16)) {
return SHRPX_OPTID_WORKER_READ_BURST;
}
break;
}
break;
case 18:
switch (name[17]) {
case 'r':
if (util::strieq_l("add-request-heade", name, 17)) {
return SHRPX_OPTID_ADD_REQUEST_HEADER;
}
break;
case 't':
if (util::strieq_l("worker-write-burs", name, 17)) {
return SHRPX_OPTID_WORKER_WRITE_BURST;
}
break;
}
break;
case 19:
switch (name[18]) {
case 'e':
if (util::strieq_l("no-location-rewrit", name, 18)) {
return SHRPX_OPTID_NO_LOCATION_REWRITE;
}
if (util::strieq_l("tls-ticket-key-fil", name, 18)) {
return SHRPX_OPTID_TLS_TICKET_KEY_FILE;
}
break;
case 'r':
if (util::strieq_l("add-response-heade", name, 18)) {
return SHRPX_OPTID_ADD_RESPONSE_HEADER;
}
if (util::strieq_l("add-x-forwarded-fo", name, 18)) {
return SHRPX_OPTID_ADD_X_FORWARDED_FOR;
}
if (util::strieq_l("header-field-buffe", name, 18)) {
return SHRPX_OPTID_HEADER_FIELD_BUFFER;
}
break;
case 't':
if (util::strieq_l("stream-read-timeou", name, 18)) {
return SHRPX_OPTID_STREAM_READ_TIMEOUT;
}
break;
}
break;
case 20:
switch (name[19]) {
case 'g':
if (util::strieq_l("frontend-frame-debu", name, 19)) {
return SHRPX_OPTID_FRONTEND_FRAME_DEBUG;
}
break;
case 'l':
if (util::strieq_l("ocsp-update-interva", name, 19)) {
return SHRPX_OPTID_OCSP_UPDATE_INTERVAL;
}
break;
case 't':
if (util::strieq_l("backend-read-timeou", name, 19)) {
return SHRPX_OPTID_BACKEND_READ_TIMEOUT;
}
if (util::strieq_l("stream-write-timeou", name, 19)) {
return SHRPX_OPTID_STREAM_WRITE_TIMEOUT;
}
if (util::strieq_l("verify-client-cacer", name, 19)) {
return SHRPX_OPTID_VERIFY_CLIENT_CACERT;
}
break;
}
break;
case 21:
switch (name[20]) {
case 'd':
if (util::strieq_l("backend-tls-sni-fiel", name, 20)) {
return SHRPX_OPTID_BACKEND_TLS_SNI_FIELD;
}
break;
case 'l':
if (util::strieq_l("accept-proxy-protoco", name, 20)) {
return SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL;
}
break;
case 'r':
if (util::strieq_l("tls-ticket-key-ciphe", name, 20)) {
return SHRPX_OPTID_TLS_TICKET_KEY_CIPHER;
}
break;
case 't':
if (util::strieq_l("backend-write-timeou", name, 20)) {
return SHRPX_OPTID_BACKEND_WRITE_TIMEOUT;
}
if (util::strieq_l("frontend-read-timeou", name, 20)) {
return SHRPX_OPTID_FRONTEND_READ_TIMEOUT;
}
break;
}
break;
case 22:
switch (name[21]) {
case 'i':
if (util::strieq_l("backend-http-proxy-ur", name, 21)) {
return SHRPX_OPTID_BACKEND_HTTP_PROXY_URI;
}
break;
case 'r':
if (util::strieq_l("backend-request-buffe", name, 21)) {
return SHRPX_OPTID_BACKEND_REQUEST_BUFFER;
}
break;
case 't':
if (util::strieq_l("frontend-write-timeou", name, 21)) {
return SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT;
}
break;
case 'y':
if (util::strieq_l("backend-address-famil", name, 21)) {
return SHRPX_OPTID_BACKEND_ADDRESS_FAMILY;
}
break;
}
break;
case 23:
switch (name[22]) {
case 'e':
if (util::strieq_l("client-private-key-fil", name, 22)) {
return SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE;
}
if (util::strieq_l("private-key-passwd-fil", name, 22)) {
return SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE;
}
break;
case 'r':
if (util::strieq_l("backend-response-buffe", name, 22)) {
return SHRPX_OPTID_BACKEND_RESPONSE_BUFFER;
}
break;
}
break;
case 24:
switch (name[23]) {
case 'd':
if (util::strieq_l("strip-incoming-forwarde", name, 23)) {
return SHRPX_OPTID_STRIP_INCOMING_FORWARDED;
}
if (util::strieq_l("tls-ticket-key-memcache", name, 23)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED;
}
break;
case 'e':
if (util::strieq_l("fetch-ocsp-response-fil", name, 23)) {
return SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE;
}
break;
case 't':
if (util::strieq_l("listener-disable-timeou", name, 23)) {
return SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT;
}
if (util::strieq_l("tls-dyn-rec-idle-timeou", name, 23)) {
return SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT;
}
break;
}
break;
case 25:
switch (name[24]) {
case 'g':
if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING;
}
break;
case 's':
if (util::strieq_l("backend-http2-window-bit", name, 24)) {
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS;
}
if (util::strieq_l("max-request-header-field", name, 24)) {
return SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS;
}
break;
}
break;
case 26:
switch (name[25]) {
case 's':
if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS;
}
if (util::strieq_l("max-response-header-field", name, 25)) {
return SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS;
}
break;
case 't':
if (util::strieq_l("backend-keep-alive-timeou", name, 25)) {
return SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT;
}
if (util::strieq_l("no-http2-cipher-black-lis", name, 25)) {
return SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST;
}
break;
}
break;
case 27:
switch (name[26]) {
case 'd':
if (util::strieq_l("tls-session-cache-memcache", name, 26)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED;
}
break;
case 'r':
if (util::strieq_l("request-header-field-buffe", name, 26)) {
return SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER;
}
break;
case 's':
if (util::strieq_l("worker-frontend-connection", name, 26)) {
return SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS;
}
break;
case 't':
if (util::strieq_l("frontend-http2-read-timeou", name, 26)) {
return SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT;
}
break;
}
break;
case 28:
switch (name[27]) {
case 'd':
if (util::strieq_l("tls-dyn-rec-warmup-threshol", name, 27)) {
return SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD;
}
break;
case 'r':
if (util::strieq_l("response-header-field-buffe", name, 27)) {
return SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER;
}
break;
case 's':
if (util::strieq_l("http2-max-concurrent-stream", name, 27)) {
return SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS;
}
if (util::strieq_l("tls-ticket-key-memcached-tl", name, 27)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS;
}
break;
case 't':
if (util::strieq_l("backend-connections-per-hos", name, 27)) {
return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST;
}
break;
}
break;
case 30:
switch (name[29]) {
case 'r':
if (util::strieq_l("strip-incoming-x-forwarded-fo", name, 29)) {
return SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR;
}
break;
}
break;
case 31:
switch (name[30]) {
case 's':
if (util::strieq_l("tls-session-cache-memcached-tl", name, 30)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS;
}
break;
}
break;
case 32:
switch (name[31]) {
case 'd':
if (util::strieq_l("backend-connections-per-fronten", name, 31)) {
return SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND;
}
break;
}
break;
case 33:
switch (name[32]) {
case 'l':
if (util::strieq_l("tls-ticket-key-memcached-interva", name, 32)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL;
}
if (util::strieq_l("tls-ticket-key-memcached-max-fai", name, 32)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL;
}
break;
}
break;
case 34:
switch (name[33]) {
case 'e':
if (util::strieq_l("tls-ticket-key-memcached-cert-fil", name, 33)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE;
}
break;
case 'r':
if (util::strieq_l("frontend-http2-dump-request-heade", name, 33)) {
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER;
}
break;
case 't':
if (util::strieq_l("backend-http1-connections-per-hos", name, 33)) {
return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST;
}
break;
case 'y':
if (util::strieq_l("tls-ticket-key-memcached-max-retr", name, 33)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY;
}
break;
}
break;
case 35:
switch (name[34]) {
case 'r':
if (util::strieq_l("frontend-http2-dump-response-heade", name, 34)) {
return SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER;
}
break;
}
break;
case 36:
switch (name[35]) {
case 'r':
if (util::strieq_l("backend-http2-connections-per-worke", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER;
}
break;
case 's':
if (util::strieq_l("backend-http2-connection-window-bit", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS;
}
if (util::strieq_l("backend-http2-max-concurrent-stream", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS;
}
break;
}
break;
case 37:
switch (name[36]) {
case 'e':
if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
}
break;
case 's':
if (util::strieq_l("frontend-http2-connection-window-bit", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS;
}
if (util::strieq_l("frontend-http2-max-concurrent-stream", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS;
}
break;
}
break;
case 38:
switch (name[37]) {
case 'd':
if (util::strieq_l("backend-http1-connections-per-fronten", name, 37)) {
return SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND;
}
break;
}
break;
case 39:
switch (name[38]) {
case 'y':
if (util::strieq_l("tls-ticket-key-memcached-address-famil", name, 38)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY;
}
break;
}
break;
case 41:
switch (name[40]) {
case 'e':
if (util::strieq_l("tls-ticket-key-memcached-private-key-fil", name,
40)) {
return SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE;
}
break;
}
break;
case 42:
switch (name[41]) {
case 'y':
if (util::strieq_l("tls-session-cache-memcached-address-famil", name,
41)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY;
}
break;
}
break;
case 44:
switch (name[43]) {
case 'e':
if (util::strieq_l("tls-session-cache-memcached-private-key-fil", name,
43)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE;
}
break;
}
break;
}
return -1;
}
} // namespace
int parse_config(const StringRef &opt, const StringRef &optarg,
std::set<StringRef> &included_set) {
char host[NI_MAXHOST];
uint16_t port;
auto optid = option_lookup_token(opt.c_str(), opt.size());
switch (optid) {
case SHRPX_OPTID_BACKEND: {
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
DownstreamAddrConfig addr{};
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
addr.host = ImmutableString(path, addr_end);
addr.host_unix = true;
} else {
if (split_host_port(host, sizeof(host), &port,
StringRef{std::begin(optarg), addr_end}, opt) == -1) {
return -1;
}
addr.host = ImmutableString(host);
addr.port = port;
}
auto mapping = addr_end == std::end(optarg) ? addr_end : addr_end + 1;
auto mapping_end = std::find(mapping, std::end(optarg), ';');
auto params =
mapping_end == std::end(optarg) ? mapping_end : mapping_end + 1;
if (parse_mapping(addr, StringRef{mapping, mapping_end},
StringRef{params, std::end(optarg)}) != 0) {
return -1;
}
return 0;
}
case SHRPX_OPTID_FRONTEND: {
auto &listenerconf = mod_config()->conn.listener;
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
auto src_params = StringRef{addr_end, std::end(optarg)};
UpstreamParams params{};
params.tls = true;
if (parse_upstream_params(params, src_params) != 0) {
return -1;
}
UpstreamAddr addr{};
addr.fd = -1;
addr.tls = params.tls;
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
addr.host = ImmutableString{path, addr_end};
addr.host_unix = true;
listenerconf.addrs.push_back(std::move(addr));
return 0;
}
if (split_host_port(host, sizeof(host), &port,
StringRef{std::begin(optarg), addr_end}, opt) == -1) {
return -1;
}
addr.host = ImmutableString(host);
addr.port = port;
if (util::numeric_host(host, AF_INET)) {
addr.family = AF_INET;
listenerconf.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));
return 0;
}
addr.family = AF_INET;
listenerconf.addrs.push_back(addr);
addr.family = AF_INET6;
listenerconf.addrs.push_back(std::move(addr));
return 0;
}
case SHRPX_OPTID_WORKERS:
#ifdef NOTHREADS
LOG(WARN) << "Threading disabled at build time, no threads created.";
return 0;
#else // !NOTHREADS
return parse_uint(&mod_config()->num_worker, opt, optarg);
#endif // !NOTHREADS
case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: {
LOG(WARN) << opt << ": deprecated. Use "
<< SHRPX_OPT_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS << " and "
<< SHRPX_OPT_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS << " instead.";
size_t n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
auto &http2conf = mod_config()->http2;
http2conf.upstream.max_concurrent_streams = n;
http2conf.downstream.max_concurrent_streams = n;
return 0;
}
case SHRPX_OPTID_LOG_LEVEL:
if (Log::set_severity_level_by_name(optarg) == -1) {
LOG(ERROR) << opt << ": Invalid severity level: " << optarg;
return -1;
}
return 0;
case SHRPX_OPTID_DAEMON:
mod_config()->daemon = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_HTTP2_PROXY:
mod_config()->http2_proxy = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_HTTP2_BRIDGE:
LOG(ERROR) << opt
<< ": deprecated. Use backend=<addr>,<port>;;proto=h2;tls";
return -1;
case SHRPX_OPTID_CLIENT_PROXY:
LOG(ERROR)
<< opt
<< ": deprecated. Use http2-proxy, frontend=<addr>,<port>;no-tls "
"and backend=<addr>,<port>;;proto=h2;tls";
return -1;
case SHRPX_OPTID_ADD_X_FORWARDED_FOR:
mod_config()->http.xff.add = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR:
mod_config()->http.xff.strip_incoming = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_NO_VIA:
mod_config()->http.no_via = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT:
return parse_duration(&mod_config()->conn.upstream.timeout.http2_read, opt,
optarg);
case SHRPX_OPTID_FRONTEND_READ_TIMEOUT:
return parse_duration(&mod_config()->conn.upstream.timeout.read, opt,
optarg);
case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT:
return parse_duration(&mod_config()->conn.upstream.timeout.write, opt,
optarg);
case SHRPX_OPTID_BACKEND_READ_TIMEOUT:
return parse_duration(&mod_config()->conn.downstream.timeout.read, opt,
optarg);
case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT:
return parse_duration(&mod_config()->conn.downstream.timeout.write, opt,
optarg);
case SHRPX_OPTID_STREAM_READ_TIMEOUT:
return parse_duration(&mod_config()->http2.timeout.stream_read, opt,
optarg);
case SHRPX_OPTID_STREAM_WRITE_TIMEOUT:
return parse_duration(&mod_config()->http2.timeout.stream_write, opt,
optarg);
case SHRPX_OPTID_ACCESSLOG_FILE:
mod_config()->logging.access.file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_ACCESSLOG_SYSLOG:
mod_config()->logging.access.syslog = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_ACCESSLOG_FORMAT:
mod_config()->logging.access.format = parse_log_format(optarg);
return 0;
case SHRPX_OPTID_ERRORLOG_FILE:
mod_config()->logging.error.file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_ERRORLOG_SYSLOG:
mod_config()->logging.error.syslog = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_FASTOPEN: {
int n;
if (parse_int(&n, opt, optarg.c_str()) != 0) {
return -1;
}
if (n < 0) {
LOG(ERROR) << opt << ": " << optarg << " is not allowed";
return -1;
}
mod_config()->conn.listener.fastopen = n;
return 0;
}
case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT:
return parse_duration(&mod_config()->conn.downstream.timeout.idle_read, opt,
optarg);
case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS:
case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: {
size_t *resp;
if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) {
resp = &mod_config()->http2.upstream.window_bits;
} else {
resp = &mod_config()->http2.downstream.window_bits;
}
errno = 0;
int n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
if (n >= 31) {
LOG(ERROR) << opt
<< ": specify the integer in the range [0, 30], inclusive";
return -1;
}
*resp = n;
return 0;
}
case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS:
case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: {
size_t *resp;
if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) {
resp = &mod_config()->http2.upstream.connection_window_bits;
} else {
resp = &mod_config()->http2.downstream.connection_window_bits;
}
errno = 0;
int n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
if (n < 16 || n >= 31) {
LOG(ERROR) << opt
<< ": specify the integer in the range [16, 30], inclusive";
return -1;
}
*resp = n;
return 0;
}
case SHRPX_OPTID_FRONTEND_NO_TLS:
LOG(WARN) << opt << ": deprecated. Use no-tls keyword in "
<< SHRPX_OPT_FRONTEND;
return 0;
case SHRPX_OPTID_BACKEND_NO_TLS:
LOG(WARN) << opt << ": deprecated. backend connection is not encrypted by "
"default. See also " << SHRPX_OPT_BACKEND_TLS;
return 0;
case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD:
mod_config()->tls.backend_sni_name = optarg.str();
return 0;
case SHRPX_OPTID_PID_FILE:
mod_config()->pid_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_USER: {
auto pwd = getpwnam(optarg.c_str());
if (!pwd) {
LOG(ERROR) << opt << ": failed to get uid from " << optarg << ": "
<< strerror(errno);
return -1;
}
mod_config()->user = pwd->pw_name;
mod_config()->uid = pwd->pw_uid;
mod_config()->gid = pwd->pw_gid;
return 0;
}
case SHRPX_OPTID_PRIVATE_KEY_FILE:
mod_config()->tls.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
auto passwd = read_passwd_from_file(opt, optarg);
if (passwd.empty()) {
LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
return -1;
}
mod_config()->tls.private_key_passwd = passwd;
return 0;
}
case SHRPX_OPTID_CERTIFICATE_FILE:
mod_config()->tls.cert_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_DH_PARAM_FILE:
mod_config()->tls.dh_param_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_SUBCERT: {
// Private Key file and certificate file separated by ':'.
auto sp = std::find(std::begin(optarg), std::end(optarg), ':');
if (sp == std::end(optarg)) {
LOG(ERROR) << opt << ": missing ':' in " << optarg;
return -1;
}
auto private_key_file = StringRef{std::begin(optarg), sp};
if (private_key_file.empty()) {
LOG(ERROR) << opt << ": missing private key file: " << optarg;
return -1;
}
auto cert_file = StringRef{sp + 1, std::end(optarg)};
if (cert_file.empty()) {
LOG(ERROR) << opt << ": missing certificate file: " << optarg;
return -1;
}
mod_config()->tls.subcerts.emplace_back(private_key_file.str(),
cert_file.str());
return 0;
}
case SHRPX_OPTID_SYSLOG_FACILITY: {
int facility = int_syslog_facility(optarg);
if (facility == -1) {
LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg;
return -1;
}
mod_config()->logging.syslog_facility = facility;
return 0;
}
case SHRPX_OPTID_BACKLOG: {
int n;
if (parse_int(&n, opt, optarg.c_str()) != 0) {
return -1;
}
if (n < -1) {
LOG(ERROR) << opt << ": " << optarg << " is not allowed";
return -1;
}
mod_config()->conn.listener.backlog = n;
return 0;
}
case SHRPX_OPTID_CIPHERS:
mod_config()->tls.ciphers =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_CLIENT:
LOG(ERROR) << opt << ": deprecated. Use frontend=<addr>,<port>;no-tls, "
"backend=<addr>,<port>;;proto=h2;tls";
return -1;
case SHRPX_OPTID_INSECURE:
mod_config()->tls.insecure = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_CACERT:
mod_config()->tls.cacert =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_BACKEND_IPV4:
LOG(WARN) << opt
<< ": deprecated. Use backend-address-family=IPv4 instead.";
mod_config()->conn.downstream.family = AF_INET;
return 0;
case SHRPX_OPTID_BACKEND_IPV6:
LOG(WARN) << opt
<< ": deprecated. Use backend-address-family=IPv6 instead.";
mod_config()->conn.downstream.family = AF_INET6;
return 0;
case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: {
auto &proxy = mod_config()->downstream_http_proxy;
// Reset here so that multiple option occurrence does not merge
// the results.
proxy = {};
// parse URI and get hostname, port and optionally userinfo.
http_parser_url u{};
int rv = http_parser_parse_url(optarg.c_str(), optarg.size(), 0, &u);
if (rv == 0) {
if (u.field_set & UF_USERINFO) {
auto uf = util::get_uri_field(optarg.c_str(), u, UF_USERINFO);
// Surprisingly, u.field_set & UF_USERINFO is nonzero even if
// userinfo component is empty string.
if (!uf.empty()) {
proxy.userinfo = util::percent_decode(std::begin(uf), std::end(uf));
}
}
if (u.field_set & UF_HOST) {
http2::copy_url_component(proxy.host, &u, UF_HOST, optarg.c_str());
} else {
LOG(ERROR) << opt << ": no hostname specified";
return -1;
}
if (u.field_set & UF_PORT) {
proxy.port = u.port;
} else {
LOG(ERROR) << opt << ": no port specified";
return -1;
}
} else {
LOG(ERROR) << opt << ": parse error";
return -1;
}
return 0;
}
case SHRPX_OPTID_READ_RATE:
return parse_uint_with_unit(
&mod_config()->conn.upstream.ratelimit.read.rate, opt, optarg);
case SHRPX_OPTID_READ_BURST:
return parse_uint_with_unit(
&mod_config()->conn.upstream.ratelimit.read.burst, opt, optarg);
case SHRPX_OPTID_WRITE_RATE:
return parse_uint_with_unit(
&mod_config()->conn.upstream.ratelimit.write.rate, opt, optarg);
case SHRPX_OPTID_WRITE_BURST:
return parse_uint_with_unit(
&mod_config()->conn.upstream.ratelimit.write.burst, opt, optarg);
case SHRPX_OPTID_WORKER_READ_RATE:
LOG(WARN) << opt << ": not implemented yet";
return 0;
case SHRPX_OPTID_WORKER_READ_BURST:
LOG(WARN) << opt << ": not implemented yet";
return 0;
case SHRPX_OPTID_WORKER_WRITE_RATE:
LOG(WARN) << opt << ": not implemented yet";
return 0;
case SHRPX_OPTID_WORKER_WRITE_BURST:
LOG(WARN) << opt << ": not implemented yet";
return 0;
case SHRPX_OPTID_NPN_LIST:
mod_config()->tls.npn_list = util::parse_config_str_list(optarg);
return 0;
case SHRPX_OPTID_TLS_PROTO_LIST:
mod_config()->tls.tls_proto_list = util::parse_config_str_list(optarg);
return 0;
case SHRPX_OPTID_VERIFY_CLIENT:
mod_config()->tls.client_verify.enabled = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
mod_config()->tls.client_verify.cacert =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
mod_config()->tls.client.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_CLIENT_CERT_FILE:
mod_config()->tls.client.cert_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
mod_config()->http2.upstream.debug.dump.request_header_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
mod_config()->http2.upstream.debug.dump.response_header_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
mod_config()->http2.no_cookie_crumbling = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_FRONTEND_FRAME_DEBUG:
mod_config()->http2.upstream.debug.frame_debug =
util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_PADDING:
return parse_uint(&mod_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 = tokens[0].str();
altsvc.port = port;
altsvc.service = tokens[1].str();
if (tokens.size() > 2) {
altsvc.host = tokens[2].str();
if (tokens.size() > 3) {
altsvc.origin = tokens[3].str();
}
}
mod_config()->http.altsvcs.push_back(std::move(altsvc));
return 0;
}
case SHRPX_OPTID_ADD_REQUEST_HEADER:
case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
auto p = parse_header(optarg);
if (p.name.empty()) {
LOG(ERROR) << opt << ": invalid header field: " << optarg;
return -1;
}
if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) {
mod_config()->http.add_request_headers.push_back(std::move(p));
} else {
mod_config()->http.add_response_headers.push_back(std::move(p));
}
return 0;
}
case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS:
return parse_uint(&mod_config()->conn.upstream.worker_connections, opt,
optarg);
case SHRPX_OPTID_NO_LOCATION_REWRITE:
mod_config()->http.no_location_rewrite = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_NO_HOST_REWRITE:
LOG(WARN) << SHRPX_OPT_NO_HOST_REWRITE
<< ": deprecated. :authority and host header fields are NOT "
"altered by default. To rewrite these headers, use "
"--host-rewrite option.";
return 0;
case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST:
LOG(WARN) << opt
<< ": deprecated. Use backend-connections-per-host instead.";
// fall through
case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_HOST: {
int n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
if (n == 0) {
LOG(ERROR) << opt << ": specify an integer strictly more than 0";
return -1;
}
mod_config()->conn.downstream.connections_per_host = n;
return 0;
}
case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND:
LOG(WARN) << opt << ": deprecated. Use "
<< SHRPX_OPT_BACKEND_CONNECTIONS_PER_FRONTEND << " instead.";
// fall through
case SHRPX_OPTID_BACKEND_CONNECTIONS_PER_FRONTEND:
return parse_uint(&mod_config()->conn.downstream.connections_per_frontend,
opt, optarg);
case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT:
return parse_duration(&mod_config()->conn.listener.timeout.sleep, opt,
optarg);
case SHRPX_OPTID_TLS_TICKET_KEY_FILE:
mod_config()->tls.ticket.files.push_back(optarg.str());
return 0;
case SHRPX_OPTID_RLIMIT_NOFILE: {
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;
}
mod_config()->rlimit_nofile = n;
return 0;
}
case SHRPX_OPTID_BACKEND_REQUEST_BUFFER:
case SHRPX_OPTID_BACKEND_RESPONSE_BUFFER: {
size_t n;
if (parse_uint_with_unit(&n, opt, optarg) != 0) {
return -1;
}
if (n == 0) {
LOG(ERROR) << opt << ": specify an integer strictly more than 0";
return -1;
}
if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) {
mod_config()->conn.downstream.request_buffer_size = n;
} else {
mod_config()->conn.downstream.response_buffer_size = n;
}
return 0;
}
case SHRPX_OPTID_NO_SERVER_PUSH:
mod_config()->http2.no_server_push = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER:
LOG(WARN) << opt << ": deprecated.";
return 0;
case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
mod_config()->tls.ocsp.fetch_ocsp_response_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
return parse_duration(&mod_config()->tls.ocsp.update_interval, opt, optarg);
case SHRPX_OPTID_NO_OCSP:
mod_config()->tls.ocsp.disabled = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_HEADER_FIELD_BUFFER:
LOG(WARN) << opt
<< ": deprecated. Use request-header-field-buffer instead.";
// fall through
case SHRPX_OPTID_REQUEST_HEADER_FIELD_BUFFER:
return parse_uint_with_unit(&mod_config()->http.request_header_field_buffer,
opt, optarg);
case SHRPX_OPTID_MAX_HEADER_FIELDS:
LOG(WARN) << opt << ": deprecated. Use max-request-header-fields instead.";
// fall through
case SHRPX_OPTID_MAX_REQUEST_HEADER_FIELDS:
return parse_uint(&mod_config()->http.max_request_header_fields, opt,
optarg);
case SHRPX_OPTID_RESPONSE_HEADER_FIELD_BUFFER:
return parse_uint_with_unit(
&mod_config()->http.response_header_field_buffer, opt, optarg);
case SHRPX_OPTID_MAX_RESPONSE_HEADER_FIELDS:
return parse_uint(&mod_config()->http.max_response_header_fields, opt,
optarg);
case SHRPX_OPTID_INCLUDE: {
if (included_set.count(optarg)) {
LOG(ERROR) << opt << ": " << optarg << " has already been included";
return -1;
}
included_set.insert(optarg);
auto rv = load_config(optarg.c_str(), included_set);
included_set.erase(optarg);
if (rv != 0) {
return -1;
}
return 0;
}
case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER:
if (util::strieq_l("aes-128-cbc", optarg)) {
mod_config()->tls.ticket.cipher = EVP_aes_128_cbc();
} else if (util::strieq_l("aes-256-cbc", optarg)) {
mod_config()->tls.ticket.cipher = EVP_aes_256_cbc();
} else {
LOG(ERROR) << opt
<< ": unsupported cipher for ticket encryption: " << optarg;
return -1;
}
mod_config()->tls.ticket.cipher_given = true;
return 0;
case SHRPX_OPTID_HOST_REWRITE:
mod_config()->http.no_host_rewrite = !util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED:
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
auto src_params = StringRef{addr_end, std::end(optarg)};
MemcachedConnectionParams params{};
if (parse_memcached_connection_params(params, src_params, StringRef{opt}) !=
0) {
return -1;
}
if (split_host_port(host, sizeof(host), &port,
StringRef{std::begin(optarg), addr_end}, opt) == -1) {
return -1;
}
switch (optid) {
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: {
auto &memcachedconf = mod_config()->tls.session_cache.memcached;
memcachedconf.host = host;
memcachedconf.port = port;
memcachedconf.tls = params.tls;
break;
}
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
auto &memcachedconf = mod_config()->tls.ticket.memcached;
memcachedconf.host = host;
memcachedconf.port = port;
memcachedconf.tls = params.tls;
break;
}
};
return 0;
}
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL:
return parse_duration(&mod_config()->tls.ticket.memcached.interval, opt,
optarg);
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: {
int n;
if (parse_uint(&n, opt, optarg) != 0) {
return -1;
}
if (n > 30) {
LOG(ERROR) << opt << ": must be smaller than or equal to 30";
return -1;
}
mod_config()->tls.ticket.memcached.max_retry = n;
return 0;
}
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL:
return parse_uint(&mod_config()->tls.ticket.memcached.max_fail, opt,
optarg);
case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: {
size_t n;
if (parse_uint_with_unit(&n, opt, optarg) != 0) {
return -1;
}
mod_config()->tls.dyn_rec.warmup_threshold = n;
return 0;
}
case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT:
return parse_duration(&mod_config()->tls.dyn_rec.idle_timeout, opt, optarg);
case SHRPX_OPTID_MRUBY_FILE:
#ifdef HAVE_MRUBY
mod_config()->mruby_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
#else // !HAVE_MRUBY
LOG(WARN) << opt
<< ": ignored because mruby support is disabled at build time.";
#endif // !HAVE_MRUBY
return 0;
case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL:
mod_config()->conn.upstream.accept_proxy_protocol =
util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_ADD_FORWARDED: {
auto &fwdconf = mod_config()->http.forwarded;
fwdconf.params = FORWARDED_NONE;
for (const auto &param : util::split_str(optarg, ',')) {
if (util::strieq_l("by", param)) {
fwdconf.params |= FORWARDED_BY;
continue;
}
if (util::strieq_l("for", param)) {
fwdconf.params |= FORWARDED_FOR;
continue;
}
if (util::strieq_l("host", param)) {
fwdconf.params |= FORWARDED_HOST;
continue;
}
if (util::strieq_l("proto", param)) {
fwdconf.params |= FORWARDED_PROTO;
continue;
}
LOG(ERROR) << opt << ": unknown parameter " << optarg;
return -1;
}
return 0;
}
case SHRPX_OPTID_STRIP_INCOMING_FORWARDED:
mod_config()->http.forwarded.strip_incoming = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_FORWARDED_BY:
case SHRPX_OPTID_FORWARDED_FOR: {
auto type = parse_forwarded_node_type(optarg);
if (type == -1 ||
(optid == SHRPX_OPTID_FORWARDED_FOR && optarg[0] == '_')) {
LOG(ERROR) << opt << ": unknown node type or illegal obfuscated string "
<< optarg;
return -1;
}
auto &fwdconf = mod_config()->http.forwarded;
switch (optid) {
case SHRPX_OPTID_FORWARDED_BY:
fwdconf.by_node_type = static_cast<shrpx_forwarded_node_type>(type);
if (optarg[0] == '_') {
fwdconf.by_obfuscated = optarg.str();
} else {
fwdconf.by_obfuscated = "";
}
break;
case SHRPX_OPTID_FORWARDED_FOR:
fwdconf.for_node_type = static_cast<shrpx_forwarded_node_type>(type);
break;
}
return 0;
}
case SHRPX_OPTID_NO_HTTP2_CIPHER_BLACK_LIST:
mod_config()->tls.no_http2_cipher_black_list =
util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_BACKEND_HTTP1_TLS:
case SHRPX_OPTID_BACKEND_TLS:
LOG(WARN) << opt << ": deprecated. Use tls keyword in "
<< SHRPX_OPT_BACKEND << " instead.";
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_TLS:
LOG(WARN) << opt << ": deprecated. Use tls keyword in "
<< SHRPX_OPT_TLS_SESSION_CACHE_MEMCACHED;
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE:
mod_config()->tls.session_cache.memcached.cert_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE:
mod_config()->tls.session_cache.memcached.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS:
LOG(WARN) << opt << ": deprecated. Use tls keyword in "
<< SHRPX_OPT_TLS_TICKET_KEY_MEMCACHED;
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE:
mod_config()->tls.ticket.memcached.cert_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE:
mod_config()->tls.ticket.memcached.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(&mod_config()->tls.ticket.memcached.family, opt,
optarg);
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_ADDRESS_FAMILY:
return parse_address_family(
&mod_config()->tls.session_cache.memcached.family, opt, optarg);
case SHRPX_OPTID_BACKEND_ADDRESS_FAMILY:
return parse_address_family(&mod_config()->conn.downstream.family, opt,
optarg);
case SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS:
return parse_uint(&mod_config()->http2.upstream.max_concurrent_streams, opt,
optarg);
case SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS:
return parse_uint(&mod_config()->http2.downstream.max_concurrent_streams,
opt, optarg);
case SHRPX_OPTID_ERROR_PAGE:
return parse_error_page(mod_config()->http.error_pages, opt, optarg);
case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored";
return 0;
}
LOG(ERROR) << "Unknown option: " << opt;
return -1;
}
int load_config(const char *filename, std::set<StringRef> &include_set) {
std::ifstream in(filename, std::ios::binary);
if (!in) {
LOG(ERROR) << "Could not open config file " << filename;
return -1;
}
std::string line;
int linenum = 0;
while (std::getline(in, line)) {
++linenum;
if (line.empty() || line[0] == '#') {
continue;
}
auto eq = std::find(std::begin(line), std::end(line), '=');
if (eq == std::end(line)) {
LOG(ERROR) << "Bad configuration format in " << filename << " at line "
<< linenum;
return -1;
}
*eq = '\0';
if (parse_config(StringRef{std::begin(line), eq},
StringRef{eq + 1, std::end(line)}, include_set) != 0) {
return -1;
}
}
return 0;
}
StringRef str_syslog_facility(int facility) {
switch (facility) {
case (LOG_AUTH):
return StringRef::from_lit("auth");
#ifdef LOG_AUTHPRIV
case (LOG_AUTHPRIV):
return StringRef::from_lit("authpriv");
#endif // LOG_AUTHPRIV
case (LOG_CRON):
return StringRef::from_lit("cron");
case (LOG_DAEMON):
return StringRef::from_lit("daemon");
#ifdef LOG_FTP
case (LOG_FTP):
return StringRef::from_lit("ftp");
#endif // LOG_FTP
case (LOG_KERN):
return StringRef::from_lit("kern");
case (LOG_LOCAL0):
return StringRef::from_lit("local0");
case (LOG_LOCAL1):
return StringRef::from_lit("local1");
case (LOG_LOCAL2):
return StringRef::from_lit("local2");
case (LOG_LOCAL3):
return StringRef::from_lit("local3");
case (LOG_LOCAL4):
return StringRef::from_lit("local4");
case (LOG_LOCAL5):
return StringRef::from_lit("local5");
case (LOG_LOCAL6):
return StringRef::from_lit("local6");
case (LOG_LOCAL7):
return StringRef::from_lit("local7");
case (LOG_LPR):
return StringRef::from_lit("lpr");
case (LOG_MAIL):
return StringRef::from_lit("mail");
case (LOG_SYSLOG):
return StringRef::from_lit("syslog");
case (LOG_USER):
return StringRef::from_lit("user");
case (LOG_UUCP):
return StringRef::from_lit("uucp");
default:
return StringRef::from_lit("(unknown)");
}
}
int int_syslog_facility(const StringRef &strfacility) {
if (util::strieq_l("auth", strfacility)) {
return LOG_AUTH;
}
#ifdef LOG_AUTHPRIV
if (util::strieq_l("authpriv", strfacility)) {
return LOG_AUTHPRIV;
}
#endif // LOG_AUTHPRIV
if (util::strieq_l("cron", strfacility)) {
return LOG_CRON;
}
if (util::strieq_l("daemon", strfacility)) {
return LOG_DAEMON;
}
#ifdef LOG_FTP
if (util::strieq_l("ftp", strfacility)) {
return LOG_FTP;
}
#endif // LOG_FTP
if (util::strieq_l("kern", strfacility)) {
return LOG_KERN;
}
if (util::strieq_l("local0", strfacility)) {
return LOG_LOCAL0;
}
if (util::strieq_l("local1", strfacility)) {
return LOG_LOCAL1;
}
if (util::strieq_l("local2", strfacility)) {
return LOG_LOCAL2;
}
if (util::strieq_l("local3", strfacility)) {
return LOG_LOCAL3;
}
if (util::strieq_l("local4", strfacility)) {
return LOG_LOCAL4;
}
if (util::strieq_l("local5", strfacility)) {
return LOG_LOCAL5;
}
if (util::strieq_l("local6", strfacility)) {
return LOG_LOCAL6;
}
if (util::strieq_l("local7", strfacility)) {
return LOG_LOCAL7;
}
if (util::strieq_l("lpr", strfacility)) {
return LOG_LPR;
}
if (util::strieq_l("mail", strfacility)) {
return LOG_MAIL;
}
if (util::strieq_l("news", strfacility)) {
return LOG_NEWS;
}
if (util::strieq_l("syslog", strfacility)) {
return LOG_SYSLOG;
}
if (util::strieq_l("user", strfacility)) {
return LOG_USER;
}
if (util::strieq_l("uucp", strfacility)) {
return LOG_UUCP;
}
return -1;
}
StringRef strproto(shrpx_proto proto) {
switch (proto) {
case PROTO_NONE:
return StringRef::from_lit("none");
case PROTO_HTTP1:
return StringRef::from_lit("http/1.1");
case PROTO_HTTP2:
return StringRef::from_lit("h2");
case PROTO_MEMCACHED:
return StringRef::from_lit("memcached");
}
// gcc needs this.
assert(0);
}
} // namespace shrpx