Merge branch 'nghttpx-more-block-allocator'

This commit is contained in:
Tatsuhiro Tsujikawa 2016-10-03 22:12:28 +09:00
commit 35594e09df
26 changed files with 678 additions and 415 deletions

View File

@ -29,6 +29,9 @@
#include <string> #include <string>
#include "template.h"
#include "allocator.h"
namespace nghttp2 { namespace nghttp2 {
namespace base64 { namespace base64 {
@ -87,7 +90,8 @@ InputIt next_decode_input(InputIt first, InputIt last, const int *tbl) {
return first; return first;
} }
template <typename InputIt> std::string decode(InputIt first, InputIt last) { template <typename InputIt, typename OutputIt>
OutputIt decode(InputIt first, InputIt last, OutputIt d_first) {
static constexpr int INDEX_TABLE[] = { static constexpr int INDEX_TABLE[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@ -104,37 +108,29 @@ template <typename InputIt> std::string decode(InputIt first, InputIt last) {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1}; -1, -1, -1, -1};
auto len = last - first; assert(std::distance(first, last) % 4 == 0);
if (len % 4 != 0) { auto p = d_first;
return "";
}
std::string res;
res.resize(len / 4 * 3);
auto p = std::begin(res);
for (; first != last;) { for (; first != last;) {
uint32_t n = 0; uint32_t n = 0;
for (int i = 1; i <= 4; ++i, ++first) { for (int i = 1; i <= 4; ++i, ++first) {
auto idx = INDEX_TABLE[static_cast<size_t>(*first)]; auto idx = INDEX_TABLE[static_cast<size_t>(*first)];
if (idx == -1) { if (idx == -1) {
if (i <= 2) { if (i <= 2) {
return ""; return d_first;
} }
if (i == 3) { if (i == 3) {
if (*first == '=' && *(first + 1) == '=' && first + 2 == last) { if (*first == '=' && *(first + 1) == '=' && first + 2 == last) {
*p++ = n >> 16; *p++ = n >> 16;
res.resize(p - std::begin(res)); return p;
return res;
} }
return ""; return d_first;
} }
if (*first == '=' && first + 1 == last) { if (*first == '=' && first + 1 == last) {
*p++ = n >> 16; *p++ = n >> 16;
*p++ = n >> 8 & 0xffu; *p++ = n >> 8 & 0xffu;
res.resize(p - std::begin(res)); return p;
return res;
} }
return ""; return d_first;
} }
n += idx << (24 - i * 6); n += idx << (24 - i * 6);
@ -145,9 +141,37 @@ template <typename InputIt> std::string decode(InputIt first, InputIt last) {
*p++ = n & 0xffu; *p++ = n & 0xffu;
} }
return p;
}
template <typename InputIt> std::string decode(InputIt first, InputIt last) {
auto len = std::distance(first, last);
if (len % 4 != 0) {
return "";
}
std::string res;
res.resize(len / 4 * 3);
res.erase(decode(first, last, std::begin(res)), std::end(res));
return res; return res;
} }
template <typename InputIt>
StringRef decode(BlockAllocator &balloc, InputIt first, InputIt last) {
auto len = std::distance(first, last);
if (len % 4 != 0) {
return StringRef::from_lit("");
}
auto iov = make_byte_ref(balloc, len / 4 * 3 + 1);
auto p = iov.base;
p = decode(first, last, p);
*p = '\0';
return StringRef{iov.base, p};
}
} // namespace base64 } // namespace base64
} // namespace nghttp2 } // namespace nghttp2

View File

@ -59,31 +59,40 @@ void test_base64_encode(void) {
} }
void test_base64_decode(void) { void test_base64_decode(void) {
BlockAllocator balloc(4096, 4096);
{ {
std::string in = "/w=="; std::string in = "/w==";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("\xff" == out); CU_ASSERT("\xff" == out);
CU_ASSERT("\xff" == base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
std::string in = "//4="; std::string in = "//4=";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("\xff\xfe" == out); CU_ASSERT("\xff\xfe" == out);
CU_ASSERT("\xff\xfe" ==
base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
std::string in = "//79"; std::string in = "//79";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("\xff\xfe\xfd" == out); CU_ASSERT("\xff\xfe\xfd" == out);
CU_ASSERT("\xff\xfe\xfd" ==
base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
std::string in = "//79/A=="; std::string in = "//79/A==";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("\xff\xfe\xfd\xfc" == out); CU_ASSERT("\xff\xfe\xfd\xfc" == out);
CU_ASSERT("\xff\xfe\xfd\xfc" ==
base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
// we check the number of valid input must be multiples of 4 // we check the number of valid input must be multiples of 4
std::string in = "//79="; std::string in = "//79=";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("" == out); CU_ASSERT("" == out);
CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
// ending invalid character at the boundary of multiples of 4 is // ending invalid character at the boundary of multiples of 4 is
@ -91,18 +100,21 @@ void test_base64_decode(void) {
std::string in = "bmdodHRw\n"; std::string in = "bmdodHRw\n";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("" == out); CU_ASSERT("" == out);
CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
// after seeing '=', subsequent input must be also '='. // after seeing '=', subsequent input must be also '='.
std::string in = "//79/A=A"; std::string in = "//79/A=A";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("" == out); CU_ASSERT("" == out);
CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
} }
{ {
// additional '=' at the end is bad // additional '=' at the end is bad
std::string in = "//79/A======"; std::string in = "//79/A======";
auto out = base64::decode(std::begin(in), std::end(in)); auto out = base64::decode(std::begin(in), std::end(in));
CU_ASSERT("" == out); CU_ASSERT("" == out);
CU_ASSERT("" == base64::decode(balloc, std::begin(in), std::end(in)));
} }
} }

View File

@ -176,6 +176,9 @@ int main(int argc, char *argv[]) {
!CU_add_test(pSuite, "util_make_hostport", !CU_add_test(pSuite, "util_make_hostport",
shrpx::test_util_make_hostport) || shrpx::test_util_make_hostport) ||
!CU_add_test(pSuite, "util_strifind", shrpx::test_util_strifind) || !CU_add_test(pSuite, "util_strifind", shrpx::test_util_strifind) ||
!CU_add_test(pSuite, "util_random_alpha_digit",
shrpx::test_util_random_alpha_digit) ||
!CU_add_test(pSuite, "util_format_hex", shrpx::test_util_format_hex) ||
!CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) || !CU_add_test(pSuite, "gzip_inflate", test_nghttp2_gzip_inflate) ||
!CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) || !CU_add_test(pSuite, "buffer_write", nghttp2::test_buffer_write) ||
!CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) || !CU_add_test(pSuite, "pool_recycle", nghttp2::test_pool_recycle) ||

View File

@ -86,6 +86,7 @@
#include "app_helper.h" #include "app_helper.h"
#include "ssl.h" #include "ssl.h"
#include "template.h" #include "template.h"
#include "allocator.h"
extern char **environ; extern char **environ;
@ -151,7 +152,7 @@ StartupConfig suconfig;
struct InheritedAddr { struct InheritedAddr {
// IP address if TCP socket. Otherwise, UNIX domain socket path. // IP address if TCP socket. Otherwise, UNIX domain socket path.
ImmutableString host; StringRef host;
uint16_t port; uint16_t port;
// true if UNIX domain socket path // true if UNIX domain socket path
bool host_unix; bool host_unix;
@ -574,7 +575,7 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr,
<< (faddr.tls ? ", tls" : ""); << (faddr.tls ? ", tls" : "");
(*found).used = true; (*found).used = true;
faddr.fd = (*found).fd; faddr.fd = (*found).fd;
faddr.hostport = ImmutableString::from_lit("localhost"); faddr.hostport = StringRef::from_lit("localhost");
return 0; return 0;
} }
@ -639,7 +640,7 @@ int create_unix_domain_server_socket(UpstreamAddr &faddr,
<< (faddr.tls ? ", tls" : ""); << (faddr.tls ? ", tls" : "");
faddr.fd = fd; faddr.fd = fd;
faddr.hostport = ImmutableString::from_lit("localhost"); faddr.hostport = StringRef::from_lit("localhost");
return 0; return 0;
} }
@ -791,8 +792,8 @@ int create_tcp_server_socket(UpstreamAddr &faddr,
} }
faddr.fd = fd; faddr.fd = fd;
faddr.hostport = ImmutableString{ faddr.hostport = util::make_http_hostport(mod_config()->balloc,
util::make_http_hostport(StringRef{host.data()}, faddr.port)}; StringRef{host.data()}, faddr.port);
LOG(NOTICE) << "Listening on " << faddr.hostport LOG(NOTICE) << "Listening on " << faddr.hostport
<< (faddr.tls ? ", tls" : ""); << (faddr.tls ? ", tls" : "");
@ -806,7 +807,7 @@ namespace {
// function is intended to be used when reloading configuration, and // function is intended to be used when reloading configuration, and
// |config| is usually a current configuration. // |config| is usually a current configuration.
std::vector<InheritedAddr> std::vector<InheritedAddr>
get_inherited_addr_from_config(const Config *config) { get_inherited_addr_from_config(BlockAllocator &balloc, Config *config) {
int rv; int rv;
auto &listenerconf = config->conn.listener; auto &listenerconf = config->conn.listener;
@ -856,7 +857,7 @@ get_inherited_addr_from_config(const Config *config) {
continue; continue;
} }
iaddr.host = ImmutableString{host.data()}; iaddr.host = make_string_ref(balloc, StringRef{host.data()});
} }
return iaddrs; return iaddrs;
@ -867,7 +868,7 @@ namespace {
// Returns array of InheritedAddr constructed from environment // Returns array of InheritedAddr constructed from environment
// variables. This function handles the old environment variable // variables. This function handles the old environment variable
// names used in 1.7.0 or earlier. // names used in 1.7.0 or earlier.
std::vector<InheritedAddr> get_inherited_addr_from_env() { std::vector<InheritedAddr> get_inherited_addr_from_env(Config *config) {
int rv; int rv;
std::vector<InheritedAddr> iaddrs; std::vector<InheritedAddr> iaddrs;
@ -888,15 +889,14 @@ std::vector<InheritedAddr> get_inherited_addr_from_env() {
} }
} }
} else { } else {
auto pathenv = getenv(ENV_UNIX_PATH.c_str()); // The return value of getenv may be allocated statically.
auto fdenv = getenv(ENV_UNIX_FD.c_str()); if (getenv(ENV_UNIX_PATH.c_str()) && getenv(ENV_UNIX_FD.c_str())) {
if (pathenv && fdenv) {
auto name = ENV_ACCEPT_PREFIX.str(); auto name = ENV_ACCEPT_PREFIX.str();
name += '1'; name += '1';
std::string value = "unix,"; std::string value = "unix,";
value += fdenv; value += getenv(ENV_UNIX_FD.c_str());
value += ','; value += ',';
value += pathenv; value += getenv(ENV_UNIX_PATH.c_str());
setenv(name.c_str(), value.c_str(), 0); setenv(name.c_str(), value.c_str(), 0);
} }
} }
@ -948,7 +948,7 @@ std::vector<InheritedAddr> get_inherited_addr_from_env() {
} }
InheritedAddr addr{}; InheritedAddr addr{};
addr.host = ImmutableString{path}; addr.host = make_string_ref(config->balloc, StringRef{path});
addr.host_unix = true; addr.host_unix = true;
addr.fd = static_cast<int>(fd); addr.fd = static_cast<int>(fd);
iaddrs.push_back(std::move(addr)); iaddrs.push_back(std::move(addr));
@ -1002,7 +1002,7 @@ std::vector<InheritedAddr> get_inherited_addr_from_env() {
} }
InheritedAddr addr{}; InheritedAddr addr{};
addr.host = ImmutableString{host.data()}; addr.host = make_string_ref(config->balloc, StringRef{host.data()});
addr.port = static_cast<uint16_t>(port); addr.port = static_cast<uint16_t>(port);
addr.fd = static_cast<int>(fd); addr.fd = static_cast<int>(fd);
iaddrs.push_back(std::move(addr)); iaddrs.push_back(std::move(addr));
@ -1209,7 +1209,7 @@ int event_loop() {
redirect_stderr_to_errorlog(); redirect_stderr_to_errorlog();
} }
auto iaddrs = get_inherited_addr_from_env(); auto iaddrs = get_inherited_addr_from_env(mod_config());
if (create_acceptor_socket(mod_config(), iaddrs) != 0) { if (create_acceptor_socket(mod_config(), iaddrs) != 0) {
return -1; return -1;
@ -1274,7 +1274,7 @@ constexpr auto DEFAULT_ACCESSLOG_FORMAT = StringRef::from_lit(
namespace { namespace {
void fill_default_config(Config *config) { void fill_default_config(Config *config) {
config->num_worker = 1; config->num_worker = 1;
config->conf_path = ImmutableString::from_lit("/etc/nghttpx/nghttpx.conf"); config->conf_path = StringRef::from_lit("/etc/nghttpx/nghttpx.conf");
config->pid = getpid(); config->pid = getpid();
if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) { if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
@ -1306,7 +1306,7 @@ void fill_default_config(Config *config) {
// ocsp update interval = 14400 secs = 4 hours, borrowed from h2o // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o
ocspconf.update_interval = 4_h; ocspconf.update_interval = 4_h;
ocspconf.fetch_ocsp_response_file = ocspconf.fetch_ocsp_response_file =
ImmutableString::from_lit(PKGDATADIR "/fetch-ocsp-response"); StringRef::from_lit(PKGDATADIR "/fetch-ocsp-response");
} }
{ {
@ -1319,7 +1319,7 @@ void fill_default_config(Config *config) {
auto &httpconf = config->http; auto &httpconf = config->http;
httpconf.server_name = httpconf.server_name =
ImmutableString::from_lit("nghttpx nghttp2/" NGHTTP2_VERSION); StringRef::from_lit("nghttpx nghttp2/" NGHTTP2_VERSION);
httpconf.no_host_rewrite = true; httpconf.no_host_rewrite = true;
httpconf.request_header_field_buffer = 64_k; httpconf.request_header_field_buffer = 64_k;
httpconf.max_request_header_fields = 100; httpconf.max_request_header_fields = 100;
@ -1387,10 +1387,11 @@ void fill_default_config(Config *config) {
auto &loggingconf = config->logging; auto &loggingconf = config->logging;
{ {
auto &accessconf = loggingconf.access; auto &accessconf = loggingconf.access;
accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); accessconf.format =
parse_log_format(config->balloc, DEFAULT_ACCESSLOG_FORMAT);
auto &errorconf = loggingconf.error; auto &errorconf = loggingconf.error;
errorconf.file = ImmutableString::from_lit("/dev/stderr"); errorconf.file = StringRef::from_lit("/dev/stderr");
} }
loggingconf.syslog_facility = LOG_DAEMON; loggingconf.syslog_facility = LOG_DAEMON;
@ -2446,11 +2447,10 @@ int process_options(Config *config,
auto &tlsconf = config->tls; auto &tlsconf = config->tls;
if (tlsconf.npn_list.empty()) { if (tlsconf.npn_list.empty()) {
tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST); tlsconf.npn_list = util::split_str(DEFAULT_NPN_LIST, ',');
} }
if (tlsconf.tls_proto_list.empty()) { if (tlsconf.tls_proto_list.empty()) {
tlsconf.tls_proto_list = tlsconf.tls_proto_list = util::split_str(DEFAULT_TLS_PROTO_LIST, ',');
util::parse_config_str_list(DEFAULT_TLS_PROTO_LIST);
} }
tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list); tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list);
@ -2466,7 +2466,7 @@ int process_options(Config *config,
if (listenerconf.addrs.empty()) { if (listenerconf.addrs.empty()) {
UpstreamAddr addr{}; UpstreamAddr addr{};
addr.host = ImmutableString::from_lit("*"); addr.host = StringRef::from_lit("*");
addr.port = 3000; addr.port = 3000;
addr.tls = true; addr.tls = true;
addr.family = AF_INET; addr.family = AF_INET;
@ -2567,10 +2567,14 @@ int process_options(Config *config,
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED && if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED &&
fwdconf.by_obfuscated.empty()) { fwdconf.by_obfuscated.empty()) {
// 2 for '_' and terminal NULL
auto iov = make_byte_ref(config->balloc, SHRPX_OBFUSCATED_NODE_LENGTH + 2);
auto p = iov.base;
*p++ = '_';
std::mt19937 gen(rd()); std::mt19937 gen(rd());
auto &dst = fwdconf.by_obfuscated; p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH, gen);
dst = "_"; *p = '\0';
dst += util::random_alpha_digit(gen, SHRPX_OBFUSCATED_NODE_LENGTH); fwdconf.by_obfuscated = StringRef{iov.base, p};
} }
if (config->http2.upstream.debug.frame_debug) { if (config->http2.upstream.debug.frame_debug) {
@ -2616,12 +2620,13 @@ void reload_config(WorkerProcess *wp) {
LOG(NOTICE) << "Reloading configuration"; LOG(NOTICE) << "Reloading configuration";
auto cur_config = get_config(); auto cur_config = mod_config();
auto new_config = make_unique<Config>(); auto new_config = make_unique<Config>();
fill_default_config(new_config.get()); fill_default_config(new_config.get());
new_config->conf_path = cur_config->conf_path; new_config->conf_path =
make_string_ref(new_config->balloc, cur_config->conf_path);
// daemon option is ignored here. // daemon option is ignored here.
new_config->daemon = cur_config->daemon; new_config->daemon = cur_config->daemon;
// loop is reused, and ev_loop_flags gets ignored // loop is reused, and ev_loop_flags gets ignored
@ -2633,7 +2638,7 @@ void reload_config(WorkerProcess *wp) {
return; return;
} }
auto iaddrs = get_inherited_addr_from_config(cur_config); auto iaddrs = get_inherited_addr_from_config(new_config->balloc, cur_config);
if (create_acceptor_socket(new_config.get(), iaddrs) != 0) { if (create_acceptor_socket(new_config.get(), iaddrs) != 0) {
close_not_inherited_fd(new_config.get(), iaddrs); close_not_inherited_fd(new_config.get(), iaddrs);
@ -3027,7 +3032,8 @@ int main(int argc, char **argv) {
break; break;
case 12: case 12:
// --conf // --conf
mod_config()->conf_path = ImmutableString{optarg}; mod_config()->conf_path =
make_string_ref(mod_config()->balloc, StringRef{optarg});
break; break;
case 14: case 14:
// --syslog-facility // --syslog-facility

View File

@ -376,17 +376,18 @@ int ClientHandler::upstream_http1_connhd_read() {
} }
ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
const char *ipaddr, const char *port, int family, const StringRef &ipaddr, const StringRef &port,
const UpstreamAddr *faddr) int family, const UpstreamAddr *faddr)
: conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(), : balloc_(256, 256),
conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(),
get_config()->conn.upstream.timeout.write, get_config()->conn.upstream.timeout.write,
get_config()->conn.upstream.timeout.read, get_config()->conn.upstream.timeout.read,
get_config()->conn.upstream.ratelimit.write, get_config()->conn.upstream.ratelimit.write,
get_config()->conn.upstream.ratelimit.read, writecb, readcb, get_config()->conn.upstream.ratelimit.read, writecb, readcb,
timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE), get_config()->tls.dyn_rec.idle_timeout, PROTO_NONE),
ipaddr_(ipaddr), ipaddr_(make_string_ref(balloc_, ipaddr)),
port_(port), port_(make_string_ref(balloc_, port)),
faddr_(faddr), faddr_(faddr),
worker_(worker), worker_(worker),
left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN),
@ -416,13 +417,29 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl,
if (fwdconf.params & FORWARDED_FOR) { if (fwdconf.params & FORWARDED_FOR) {
if (fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) { if (fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) {
forwarded_for_ = "_"; // 1 for '_'
forwarded_for_ += util::random_alpha_digit(worker_->get_randgen(), auto len = SHRPX_OBFUSCATED_NODE_LENGTH + 1;
SHRPX_OBFUSCATED_NODE_LENGTH); // 1 for terminating NUL.
auto buf = make_byte_ref(balloc_, len + 1);
auto p = buf.base;
*p++ = '_';
p = util::random_alpha_digit(p, p + SHRPX_OBFUSCATED_NODE_LENGTH,
worker_->get_randgen());
*p = '\0';
forwarded_for_ = StringRef{buf.base, p};
} else if (family == AF_INET6) { } else if (family == AF_INET6) {
forwarded_for_ = "["; // 2 for '[' and ']'
forwarded_for_ += ipaddr_; auto len = 2 + ipaddr_.size();
forwarded_for_ += ']'; // 1 for terminating NUL.
auto buf = make_byte_ref(balloc_, len + 1);
auto p = buf.base;
*p++ = '[';
p = std::copy(std::begin(ipaddr_), std::end(ipaddr_), p);
*p++ = ']';
*p = '\0';
forwarded_for_ = StringRef{buf.base, p};
} else { } else {
// family == AF_INET or family == AF_UNIX // family == AF_INET or family == AF_UNIX
forwarded_for_ = ipaddr_; forwarded_for_ = ipaddr_;
@ -441,7 +458,7 @@ void ClientHandler::setup_upstream_io_callback() {
// upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2 // upgraded to HTTP/2 through HTTP Upgrade or direct HTTP/2
// connection. // connection.
upstream_ = make_unique<HttpsUpstream>(this); upstream_ = make_unique<HttpsUpstream>(this);
alpn_ = "http/1.1"; alpn_ = StringRef::from_lit("http/1.1");
read_ = &ClientHandler::read_clear; read_ = &ClientHandler::read_clear;
write_ = &ClientHandler::write_clear; write_ = &ClientHandler::write_clear;
on_read_ = &ClientHandler::upstream_http1_connhd_read; on_read_ = &ClientHandler::upstream_http1_connhd_read;
@ -524,7 +541,7 @@ int ClientHandler::validate_next_proto() {
} }
upstream_ = make_unique<HttpsUpstream>(this); upstream_ = make_unique<HttpsUpstream>(this);
alpn_ = "http/1.1"; alpn_ = StringRef::from_lit("http/1.1");
// At this point, input buffer is already filled with some bytes. // At this point, input buffer is already filled with some bytes.
// The read callback is not called until new data come. So consume // The read callback is not called until new data come. So consume
@ -555,7 +572,7 @@ int ClientHandler::validate_next_proto() {
auto http2_upstream = make_unique<Http2Upstream>(this); auto http2_upstream = make_unique<Http2Upstream>(this);
upstream_ = std::move(http2_upstream); upstream_ = std::move(http2_upstream);
alpn_.assign(std::begin(proto), std::end(proto)); alpn_ = make_string_ref(balloc_, proto);
// At this point, input buffer is already filled with some bytes. // At this point, input buffer is already filled with some bytes.
// The read callback is not called until new data come. So consume // The read callback is not called until new data come. So consume
@ -574,16 +591,16 @@ int ClientHandler::validate_next_proto() {
switch (spdy_version) { switch (spdy_version) {
case SPDYLAY_PROTO_SPDY2: case SPDYLAY_PROTO_SPDY2:
alpn_ = "spdy/2"; alpn_ = StringRef::from_lit("spdy/2");
break; break;
case SPDYLAY_PROTO_SPDY3: case SPDYLAY_PROTO_SPDY3:
alpn_ = "spdy/3"; alpn_ = StringRef::from_lit("spdy/3");
break; break;
case SPDYLAY_PROTO_SPDY3_1: case SPDYLAY_PROTO_SPDY3_1:
alpn_ = "spdy/3.1"; alpn_ = StringRef::from_lit("spdy/3.1");
break; break;
default: default:
alpn_ = "spdy/unknown"; alpn_ = StringRef::from_lit("spdy/unknown");
} }
// At this point, input buffer is already filled with some bytes. // At this point, input buffer is already filled with some bytes.
@ -599,7 +616,7 @@ int ClientHandler::validate_next_proto() {
if (proto == StringRef::from_lit("http/1.1")) { if (proto == StringRef::from_lit("http/1.1")) {
upstream_ = make_unique<HttpsUpstream>(this); upstream_ = make_unique<HttpsUpstream>(this);
alpn_ = proto.str(); alpn_ = StringRef::from_lit("http/1.1");
// At this point, input buffer is already filled with some bytes. // At this point, input buffer is already filled with some bytes.
// The read callback is not called until new data come. So consume // The read callback is not called until new data come. So consume
@ -629,7 +646,7 @@ int ClientHandler::on_read() {
} }
int ClientHandler::on_write() { return on_write_(*this); } int ClientHandler::on_write() { return on_write_(*this); }
const std::string &ClientHandler::get_ipaddr() const { return ipaddr_; } const StringRef &ClientHandler::get_ipaddr() const { return ipaddr_; }
bool ClientHandler::get_should_close_after_write() const { bool ClientHandler::get_should_close_after_write() const {
return should_close_after_write_; return should_close_after_write_;
@ -974,7 +991,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) {
if (shared_addr->affinity == AFFINITY_IP) { if (shared_addr->affinity == AFFINITY_IP) {
if (!affinity_hash_computed_) { if (!affinity_hash_computed_) {
affinity_hash_ = compute_affinity_from_ip(StringRef{ipaddr_}); affinity_hash_ = compute_affinity_from_ip(ipaddr_);
affinity_hash_computed_ = true; affinity_hash_computed_ = true;
} }
@ -1093,7 +1110,7 @@ SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; }
void ClientHandler::direct_http2_upgrade() { void ClientHandler::direct_http2_upgrade() {
upstream_ = make_unique<Http2Upstream>(this); upstream_ = make_unique<Http2Upstream>(this);
alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; alpn_ = StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID);
on_read_ = &ClientHandler::upstream_read; on_read_ = &ClientHandler::upstream_read;
write_ = &ClientHandler::write_clear; write_ = &ClientHandler::write_clear;
} }
@ -1118,7 +1135,7 @@ int ClientHandler::perform_http2_upgrade(HttpsUpstream *http) {
upstream_.release(); upstream_.release();
// TODO We might get other version id in HTTP2-settings, if we // TODO We might get other version id in HTTP2-settings, if we
// support aliasing for h2, but we just use library default for now. // support aliasing for h2, but we just use library default for now.
alpn_ = NGHTTP2_CLEARTEXT_PROTO_VERSION_ID; alpn_ = StringRef::from_lit(NGHTTP2_CLEARTEXT_PROTO_VERSION_ID);
on_read_ = &ClientHandler::upstream_http2_connhd_read; on_read_ = &ClientHandler::upstream_http2_connhd_read;
write_ = &ClientHandler::write_clear; write_ = &ClientHandler::write_clear;
@ -1199,7 +1216,7 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
upstream_accesslog( upstream_accesslog(
get_config()->logging.access.format, get_config()->logging.access.format,
LogSpec{ LogSpec{
downstream, downstream->get_addr(), StringRef{ipaddr_}, downstream, downstream->get_addr(), ipaddr_,
http2::to_method_string(req.method), http2::to_method_string(req.method),
req.method == HTTP_CONNECT req.method == HTTP_CONNECT
@ -1212,15 +1229,14 @@ void ClientHandler::write_accesslog(Downstream *downstream) {
: StringRef::from_lit("-") : StringRef::from_lit("-")
: StringRef(req.path), : StringRef(req.path),
StringRef(alpn_), alpn_, nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
std::chrono::system_clock::now(), // time_now std::chrono::system_clock::now(), // time_now
downstream->get_request_start_time(), // request_start_time downstream->get_request_start_time(), // request_start_time
std::chrono::high_resolution_clock::now(), // request_end_time std::chrono::high_resolution_clock::now(), // request_end_time
req.http_major, req.http_minor, resp.http_status, req.http_major, req.http_minor, resp.http_status,
downstream->response_sent_body_length, StringRef(port_), faddr_->port, downstream->response_sent_body_length, port_, faddr_->port,
get_config()->pid, get_config()->pid,
}); });
} }
@ -1231,21 +1247,20 @@ void ClientHandler::write_accesslog(int major, int minor, unsigned int status,
auto highres_now = std::chrono::high_resolution_clock::now(); auto highres_now = std::chrono::high_resolution_clock::now();
nghttp2::ssl::TLSSessionInfo tls_info; nghttp2::ssl::TLSSessionInfo tls_info;
upstream_accesslog(get_config()->logging.access.format, upstream_accesslog(
LogSpec{ get_config()->logging.access.format,
nullptr, nullptr, StringRef(ipaddr_), LogSpec{
StringRef::from_lit("-"), // method nullptr, nullptr, ipaddr_,
StringRef::from_lit("-"), // path, StringRef::from_lit("-"), // method
StringRef(alpn_), nghttp2::ssl::get_tls_session_info( StringRef::from_lit("-"), // path,
&tls_info, conn_.tls.ssl), alpn_, nghttp2::ssl::get_tls_session_info(&tls_info, conn_.tls.ssl),
time_now, time_now,
highres_now, // request_start_time TODO is highres_now, // request_start_time TODO is
// there a better value? // there a better value?
highres_now, // request_end_time highres_now, // request_end_time
major, minor, // major, minor major, minor, // major, minor
status, body_bytes_sent, StringRef(port_), status, body_bytes_sent, port_, faddr_->port, get_config()->pid,
faddr_->port, get_config()->pid, });
});
} }
ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; } ClientHandler::ReadBuf *ClientHandler::get_rb() { return &rb_; }
@ -1474,8 +1489,9 @@ int ClientHandler::proxy_protocol_read() {
rb_.drain(end + 2 - rb_.pos); rb_.drain(end + 2 - rb_.pos);
ipaddr_.assign(src_addr, src_addr + src_addrlen); ipaddr_ =
port_.assign(src_port, src_port + src_portlen); make_string_ref(balloc_, StringRef{src_addr, src_addr + src_addrlen});
port_ = make_string_ref(balloc_, StringRef{src_port, src_port + src_portlen});
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first) CLOG(INFO, this) << "PROXY-protocol-v1: Finished, " << (rb_.pos - first)
@ -1489,22 +1505,24 @@ StringRef ClientHandler::get_forwarded_by() const {
auto &fwdconf = get_config()->http.forwarded; auto &fwdconf = get_config()->http.forwarded;
if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) { if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) {
return StringRef(fwdconf.by_obfuscated); return fwdconf.by_obfuscated;
} }
return StringRef{faddr_->hostport}; return faddr_->hostport;
} }
StringRef ClientHandler::get_forwarded_for() const { StringRef ClientHandler::get_forwarded_for() const { return forwarded_for_; }
return StringRef{forwarded_for_};
}
const UpstreamAddr *ClientHandler::get_upstream_addr() const { return faddr_; } const UpstreamAddr *ClientHandler::get_upstream_addr() const { return faddr_; }
Connection *ClientHandler::get_connection() { return &conn_; }; Connection *ClientHandler::get_connection() { return &conn_; };
void ClientHandler::set_tls_sni(const StringRef &sni) { sni_ = sni.str(); } void ClientHandler::set_tls_sni(const StringRef &sni) {
sni_ = make_string_ref(balloc_, sni);
}
StringRef ClientHandler::get_tls_sni() const { return StringRef{sni_}; } StringRef ClientHandler::get_tls_sni() const { return sni_; }
BlockAllocator &ClientHandler::get_block_allocator() { return balloc_; }
} // namespace shrpx } // namespace shrpx

View File

@ -37,6 +37,7 @@
#include "shrpx_connection.h" #include "shrpx_connection.h"
#include "buffer.h" #include "buffer.h"
#include "memchunk.h" #include "memchunk.h"
#include "allocator.h"
using namespace nghttp2; using namespace nghttp2;
@ -54,8 +55,8 @@ struct DownstreamAddr;
class ClientHandler { class ClientHandler {
public: public:
ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr, ClientHandler(Worker *worker, int fd, SSL *ssl, const StringRef &ipaddr,
const char *port, int family, const UpstreamAddr *faddr); const StringRef &port, int family, const UpstreamAddr *faddr);
~ClientHandler(); ~ClientHandler();
int noop(); int noop();
@ -90,8 +91,7 @@ public:
void reset_upstream_write_timeout(ev_tstamp t); void reset_upstream_write_timeout(ev_tstamp t);
int validate_next_proto(); int validate_next_proto();
const std::string &get_ipaddr() const; const StringRef &get_ipaddr() const;
const std::string &get_port() const;
bool get_should_close_after_write() const; bool get_should_close_after_write() const;
void set_should_close_after_write(bool f); void set_should_close_after_write(bool f);
Upstream *get_upstream(); Upstream *get_upstream();
@ -162,21 +162,27 @@ public:
// Returns TLS SNI extension value client sent in this connection. // Returns TLS SNI extension value client sent in this connection.
StringRef get_tls_sni() const; StringRef get_tls_sni() const;
BlockAllocator &get_block_allocator();
private: private:
// Allocator to allocate memory for connection-wide objects. Make
// sure that the allocations must be bounded, and not proportional
// to the number of requests.
BlockAllocator balloc_;
Connection conn_; Connection conn_;
ev_timer reneg_shutdown_timer_; ev_timer reneg_shutdown_timer_;
std::unique_ptr<Upstream> upstream_; std::unique_ptr<Upstream> upstream_;
// IP address of client. If UNIX domain socket is used, this is // IP address of client. If UNIX domain socket is used, this is
// "localhost". // "localhost".
std::string ipaddr_; StringRef ipaddr_;
std::string port_; StringRef port_;
// The ALPN identifier negotiated for this connection. // The ALPN identifier negotiated for this connection.
std::string alpn_; StringRef alpn_;
// The client address used in "for" parameter of Forwarded header // The client address used in "for" parameter of Forwarded header
// field. // field.
std::string forwarded_for_; StringRef forwarded_for_;
// lowercased TLS SNI which client sent. // lowercased TLS SNI which client sent.
std::string sni_; StringRef sni_;
std::function<int(ClientHandler &)> read_, write_; std::function<int(ClientHandler &)> read_, write_;
std::function<int(ClientHandler &)> on_read_, on_write_; std::function<int(ClientHandler &)> on_read_, on_write_;
// Address of frontend listening socket // Address of frontend listening socket

View File

@ -151,7 +151,7 @@ bool is_secure(const StringRef &filename) {
} // namespace } // namespace
std::unique_ptr<TicketKeys> std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<std::string> &files, read_tls_ticket_key_file(const std::vector<StringRef> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac) { const EVP_CIPHER *cipher, const EVP_MD *hmac) {
auto ticket_keys = make_unique<TicketKeys>(); auto ticket_keys = make_unique<TicketKeys>();
auto &keys = ticket_keys->keys; auto &keys = ticket_keys->keys;
@ -272,29 +272,35 @@ std::string read_passwd_from_file(const StringRef &opt,
} }
} // namespace } // namespace
Headers::value_type parse_header(const StringRef &optarg) { HeaderRefs::value_type parse_header(BlockAllocator &balloc,
const StringRef &optarg) {
auto colon = std::find(std::begin(optarg), std::end(optarg), ':'); auto colon = std::find(std::begin(optarg), std::end(optarg), ':');
if (colon == std::end(optarg) || colon == std::begin(optarg)) { if (colon == std::end(optarg) || colon == std::begin(optarg)) {
return {"", ""}; return {};
} }
auto value = colon + 1; auto value = colon + 1;
for (; *value == '\t' || *value == ' '; ++value) for (; *value == '\t' || *value == ' '; ++value)
; ;
auto p = Header(std::string{std::begin(optarg), colon}, auto name_iov =
std::string{value, std::end(optarg)}); make_byte_ref(balloc, std::distance(std::begin(optarg), colon) + 1);
util::inp_strlower(p.name); auto p = name_iov.base;
p = std::copy(std::begin(optarg), colon, p);
util::inp_strlower(name_iov.base, p);
*p = '\0';
if (!nghttp2_check_header_name( auto nv =
reinterpret_cast<const uint8_t *>(p.name.c_str()), p.name.size()) || HeaderRef(StringRef{name_iov.base, p},
!nghttp2_check_header_value( make_string_ref(balloc, StringRef{value, std::end(optarg)}));
reinterpret_cast<const uint8_t *>(p.value.c_str()), p.value.size())) {
return Header{}; if (!nghttp2_check_header_name(nv.name.byte(), nv.name.size()) ||
!nghttp2_check_header_value(nv.value.byte(), nv.value.size())) {
return {};
} }
return p; return nv;
} }
template <typename T> template <typename T>
@ -490,7 +496,8 @@ bool var_token(char c) {
} }
} // namespace } // namespace
std::vector<LogFragment> parse_log_format(const StringRef &optarg) { std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
const StringRef &optarg) {
auto literal_start = std::begin(optarg); auto literal_start = std::begin(optarg);
auto p = literal_start; auto p = literal_start;
auto eop = std::end(optarg); auto eop = std::end(optarg);
@ -553,8 +560,9 @@ std::vector<LogFragment> parse_log_format(const StringRef &optarg) {
} }
if (literal_start < var_start) { if (literal_start < var_start) {
res.emplace_back(SHRPX_LOGF_LITERAL, res.emplace_back(
ImmutableString(literal_start, var_start)); SHRPX_LOGF_LITERAL,
make_string_ref(balloc, StringRef{literal_start, var_start}));
} }
literal_start = p; literal_start = p;
@ -564,18 +572,24 @@ std::vector<LogFragment> parse_log_format(const StringRef &optarg) {
continue; continue;
} }
auto name = std::string(value, var_name + var_namelen); {
for (auto &c : name) { auto iov = make_byte_ref(
if (c == '_') { balloc, std::distance(value, var_name + var_namelen) + 1);
c = '-'; auto p = iov.base;
p = std::copy(value, var_name + var_namelen, p);
for (auto cp = iov.base; cp != p; ++cp) {
if (*cp == '_') {
*cp = '-';
}
} }
*p = '\0';
res.emplace_back(type, StringRef{iov.base, p});
} }
res.emplace_back(type, ImmutableString(name));
} }
if (literal_start != eop) { if (literal_start != eop) {
res.emplace_back(SHRPX_LOGF_LITERAL, ImmutableString(literal_start, eop)); res.emplace_back(SHRPX_LOGF_LITERAL,
make_string_ref(balloc, StringRef{literal_start, eop}));
} }
return res; return res;
@ -804,7 +818,7 @@ namespace {
// as catch-all. We also parse protocol specified in |src_proto|. // as catch-all. We also parse protocol specified in |src_proto|.
// //
// This function returns 0 if it succeeds, or -1. // This function returns 0 if it succeeds, or -1.
int parse_mapping(Config *config, DownstreamAddrConfig addr, int parse_mapping(Config *config, DownstreamAddrConfig &addr,
const StringRef &src_pattern, const StringRef &src_params) { const StringRef &src_pattern, const StringRef &src_params) {
// This returns at least 1 element (it could be empty string). We // This returns at least 1 element (it could be empty string). We
// will append '/' to all patterns, so it becomes catch-all pattern. // will append '/' to all patterns, so it becomes catch-all pattern.
@ -824,7 +838,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
addr.rise = params.rise; addr.rise = params.rise;
addr.proto = params.proto; addr.proto = params.proto;
addr.tls = params.tls; addr.tls = params.tls;
addr.sni = ImmutableString{std::begin(params.sni), std::end(params.sni)}; addr.sni = make_string_ref(downstreamconf.balloc, params.sni);
auto &routerconf = downstreamconf.router; auto &routerconf = downstreamconf.router;
auto &router = routerconf.router; auto &router = routerconf.router;
@ -833,18 +847,31 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
for (const auto &raw_pattern : mapping) { for (const auto &raw_pattern : mapping) {
auto done = false; auto done = false;
std::string pattern; StringRef pattern;
auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/'); auto slash = std::find(std::begin(raw_pattern), std::end(raw_pattern), '/');
if (slash == std::end(raw_pattern)) { if (slash == std::end(raw_pattern)) {
// This effectively makes empty pattern to "/". // This effectively makes empty pattern to "/". 2 for '/' and
pattern.assign(std::begin(raw_pattern), std::end(raw_pattern)); // terminal NULL character.
util::inp_strlower(pattern); auto iov = make_byte_ref(downstreamconf.balloc, raw_pattern.size() + 2);
pattern += '/'; auto p = iov.base;
p = std::copy(std::begin(raw_pattern), std::end(raw_pattern), p);
util::inp_strlower(iov.base, p);
*p++ = '/';
*p = '\0';
pattern = StringRef{iov.base, p};
} else { } else {
pattern.assign(std::begin(raw_pattern), slash); auto path = http2::normalize_path(downstreamconf.balloc,
util::inp_strlower(pattern); StringRef{slash, std::end(raw_pattern)},
pattern += http2::normalize_path(StringRef{slash, std::end(raw_pattern)}, StringRef{});
StringRef{}); auto iov = make_byte_ref(downstreamconf.balloc,
std::distance(std::begin(raw_pattern), slash) +
path.size() + 1);
auto p = iov.base;
p = std::copy(std::begin(raw_pattern), slash, p);
util::inp_strlower(iov.base, p);
p = std::copy(std::begin(path), std::end(path), p);
*p = '\0';
pattern = StringRef{iov.base, p};
} }
for (auto &g : addr_groups) { for (auto &g : addr_groups) {
if (g.pattern == pattern) { if (g.pattern == pattern) {
@ -863,7 +890,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
} }
auto idx = addr_groups.size(); auto idx = addr_groups.size();
addr_groups.emplace_back(StringRef{pattern}); addr_groups.emplace_back(pattern);
auto &g = addr_groups.back(); auto &g = addr_groups.back();
g.addrs.push_back(addr); g.addrs.push_back(addr);
g.affinity = params.affinity; g.affinity = params.affinity;
@ -886,10 +913,13 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
auto &router = wildcard_patterns.back().router; auto &router = wildcard_patterns.back().router;
router.add_route(path, idx); router.add_route(path, idx);
auto rev_host = host.str(); auto iov = make_byte_ref(downstreamconf.balloc, host.size() + 1);
std::reverse(std::begin(rev_host), std::end(rev_host)); auto p = iov.base;
p = std::reverse_copy(std::begin(host), std::end(host), p);
*p = '\0';
auto rev_host = StringRef{iov.base, p};
rw_router.add_route(StringRef{rev_host}, wildcard_patterns.size() - 1); rw_router.add_route(rev_host, wildcard_patterns.size() - 1);
} else { } else {
(*it).router.add_route(path, idx); (*it).router.add_route(path, idx);
} }
@ -897,7 +927,7 @@ int parse_mapping(Config *config, DownstreamAddrConfig addr,
continue; continue;
} }
router.add_route(StringRef{g.pattern}, idx); router.add_route(g.pattern, idx);
} }
return 0; return 0;
} }
@ -1804,12 +1834,14 @@ int parse_config(Config *config, int optid, const StringRef &opt,
switch (optid) { switch (optid) {
case SHRPX_OPTID_BACKEND: { case SHRPX_OPTID_BACKEND: {
auto &downstreamconf = *config->conn.downstream;
auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';'); auto addr_end = std::find(std::begin(optarg), std::end(optarg), ';');
DownstreamAddrConfig addr{}; DownstreamAddrConfig addr{};
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
addr.host = ImmutableString(path, addr_end); addr.host =
make_string_ref(downstreamconf.balloc, StringRef{path, addr_end});
addr.host_unix = true; addr.host_unix = true;
} else { } else {
if (split_host_port(host, sizeof(host), &port, if (split_host_port(host, sizeof(host), &port,
@ -1817,7 +1849,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
addr.host = ImmutableString(host); addr.host = make_string_ref(downstreamconf.balloc, StringRef{host});
addr.port = port; addr.port = port;
} }
@ -1859,7 +1891,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) {
auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size(); auto path = std::begin(optarg) + SHRPX_UNIX_PATH_PREFIX.size();
addr.host = ImmutableString{path, addr_end}; addr.host = make_string_ref(config->balloc, StringRef{path, addr_end});
addr.host_unix = true; addr.host_unix = true;
listenerconf.addrs.push_back(std::move(addr)); listenerconf.addrs.push_back(std::move(addr));
@ -1872,7 +1904,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
addr.host = ImmutableString(host); addr.host = make_string_ref(config->balloc, StringRef{host});
addr.port = port; addr.port = port;
if (util::numeric_host(host, AF_INET)) { if (util::numeric_host(host, AF_INET)) {
@ -1969,8 +2001,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_STREAM_WRITE_TIMEOUT: case SHRPX_OPTID_STREAM_WRITE_TIMEOUT:
return parse_duration(&config->http2.timeout.stream_write, opt, optarg); return parse_duration(&config->http2.timeout.stream_write, opt, optarg);
case SHRPX_OPTID_ACCESSLOG_FILE: case SHRPX_OPTID_ACCESSLOG_FILE:
config->logging.access.file = config->logging.access.file = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_ACCESSLOG_SYSLOG: case SHRPX_OPTID_ACCESSLOG_SYSLOG:
@ -1978,12 +2009,11 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
case SHRPX_OPTID_ACCESSLOG_FORMAT: case SHRPX_OPTID_ACCESSLOG_FORMAT:
config->logging.access.format = parse_log_format(optarg); config->logging.access.format = parse_log_format(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_ERRORLOG_FILE: case SHRPX_OPTID_ERRORLOG_FILE:
config->logging.error.file = config->logging.error.file = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_ERRORLOG_SYSLOG: case SHRPX_OPTID_ERRORLOG_SYSLOG:
@ -2087,11 +2117,11 @@ int parse_config(Config *config, int optid, const StringRef &opt,
LOG(WARN) << opt << ": deprecated. Use sni keyword in --backend option. " LOG(WARN) << opt << ": deprecated. Use sni keyword in --backend option. "
"For now, all sni values of all backends are " "For now, all sni values of all backends are "
"overridden by the given value " << optarg; "overridden by the given value " << optarg;
config->tls.backend_sni_name = optarg.str(); config->tls.backend_sni_name = make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_PID_FILE: case SHRPX_OPTID_PID_FILE:
config->pid_file = ImmutableString{std::begin(optarg), std::end(optarg)}; config->pid_file = make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_USER: { case SHRPX_OPTID_USER: {
@ -2101,15 +2131,14 @@ int parse_config(Config *config, int optid, const StringRef &opt,
<< strerror(errno); << strerror(errno);
return -1; return -1;
} }
config->user = ImmutableString{pwd->pw_name}; config->user = make_string_ref(config->balloc, StringRef{pwd->pw_name});
config->uid = pwd->pw_uid; config->uid = pwd->pw_uid;
config->gid = pwd->pw_gid; config->gid = pwd->pw_gid;
return 0; return 0;
} }
case SHRPX_OPTID_PRIVATE_KEY_FILE: case SHRPX_OPTID_PRIVATE_KEY_FILE:
config->tls.private_key_file = config->tls.private_key_file = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: { case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: {
@ -2118,18 +2147,17 @@ int parse_config(Config *config, int optid, const StringRef &opt,
LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg;
return -1; return -1;
} }
config->tls.private_key_passwd = ImmutableString{passwd}; config->tls.private_key_passwd =
make_string_ref(config->balloc, StringRef{passwd});
return 0; return 0;
} }
case SHRPX_OPTID_CERTIFICATE_FILE: case SHRPX_OPTID_CERTIFICATE_FILE:
config->tls.cert_file = config->tls.cert_file = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_DH_PARAM_FILE: case SHRPX_OPTID_DH_PARAM_FILE:
config->tls.dh_param_file = config->tls.dh_param_file = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_SUBCERT: { case SHRPX_OPTID_SUBCERT: {
@ -2154,7 +2182,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
config->tls.subcerts.emplace_back(private_key_file.str(), cert_file.str()); config->tls.subcerts.emplace_back(
make_string_ref(config->balloc, private_key_file),
make_string_ref(config->balloc, cert_file));
return 0; return 0;
} }
@ -2185,7 +2215,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
} }
case SHRPX_OPTID_CIPHERS: case SHRPX_OPTID_CIPHERS:
config->tls.ciphers = ImmutableString{std::begin(optarg), std::end(optarg)}; config->tls.ciphers = make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_CLIENT: case SHRPX_OPTID_CLIENT:
@ -2197,7 +2227,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
case SHRPX_OPTID_CACERT: case SHRPX_OPTID_CACERT:
config->tls.cacert = ImmutableString{std::begin(optarg), std::end(optarg)}; config->tls.cacert = make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_BACKEND_IPV4: case SHRPX_OPTID_BACKEND_IPV4:
@ -2228,11 +2258,12 @@ int parse_config(Config *config, int optid, const StringRef &opt,
// Surprisingly, u.field_set & UF_USERINFO is nonzero even if // Surprisingly, u.field_set & UF_USERINFO is nonzero even if
// userinfo component is empty string. // userinfo component is empty string.
if (!uf.empty()) { if (!uf.empty()) {
proxy.userinfo = util::percent_decode(std::begin(uf), std::end(uf)); proxy.userinfo = util::percent_decode(config->balloc, uf);
} }
} }
if (u.field_set & UF_HOST) { if (u.field_set & UF_HOST) {
http2::copy_url_component(proxy.host, &u, UF_HOST, optarg.c_str()); proxy.host = make_string_ref(
config->balloc, util::get_uri_field(optarg.c_str(), u, UF_HOST));
} else { } else {
LOG(ERROR) << opt << ": no hostname specified"; LOG(ERROR) << opt << ": no hostname specified";
return -1; return -1;
@ -2275,11 +2306,11 @@ int parse_config(Config *config, int optid, const StringRef &opt,
LOG(WARN) << opt << ": not implemented yet"; LOG(WARN) << opt << ": not implemented yet";
return 0; return 0;
case SHRPX_OPTID_NPN_LIST: case SHRPX_OPTID_NPN_LIST:
config->tls.npn_list = util::parse_config_str_list(optarg); config->tls.npn_list = util::split_str(optarg, ',');
return 0; return 0;
case SHRPX_OPTID_TLS_PROTO_LIST: case SHRPX_OPTID_TLS_PROTO_LIST:
config->tls.tls_proto_list = util::parse_config_str_list(optarg); config->tls.tls_proto_list = util::split_str(optarg, ',');
return 0; return 0;
case SHRPX_OPTID_VERIFY_CLIENT: case SHRPX_OPTID_VERIFY_CLIENT:
@ -2287,28 +2318,26 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
case SHRPX_OPTID_VERIFY_CLIENT_CACERT: case SHRPX_OPTID_VERIFY_CLIENT_CACERT:
config->tls.client_verify.cacert = config->tls.client_verify.cacert = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE: case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE:
config->tls.client.private_key_file = config->tls.client.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_CLIENT_CERT_FILE: case SHRPX_OPTID_CLIENT_CERT_FILE:
config->tls.client.cert_file = config->tls.client.cert_file = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER:
config->http2.upstream.debug.dump.request_header_file = config->http2.upstream.debug.dump.request_header_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER: case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER:
config->http2.upstream.debug.dump.response_header_file = config->http2.upstream.debug.dump.response_header_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING: case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING:
@ -2350,16 +2379,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
AltSvc altsvc{}; AltSvc altsvc{};
altsvc.protocol_id = tokens[0].str(); altsvc.protocol_id = make_string_ref(config->balloc, tokens[0]);
altsvc.port = port; altsvc.port = port;
altsvc.service = tokens[1].str(); altsvc.service = make_string_ref(config->balloc, tokens[1]);
if (tokens.size() > 2) { if (tokens.size() > 2) {
altsvc.host = tokens[2].str(); altsvc.host = make_string_ref(config->balloc, tokens[2]);
if (tokens.size() > 3) { if (tokens.size() > 3) {
altsvc.origin = tokens[3].str(); altsvc.origin = make_string_ref(config->balloc, tokens[3]);
} }
} }
@ -2369,7 +2398,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
} }
case SHRPX_OPTID_ADD_REQUEST_HEADER: case SHRPX_OPTID_ADD_REQUEST_HEADER:
case SHRPX_OPTID_ADD_RESPONSE_HEADER: { case SHRPX_OPTID_ADD_RESPONSE_HEADER: {
auto p = parse_header(optarg); auto p = parse_header(config->balloc, optarg);
if (p.name.empty()) { if (p.name.empty()) {
LOG(ERROR) << opt << ": invalid header field: " << optarg; LOG(ERROR) << opt << ": invalid header field: " << optarg;
return -1; return -1;
@ -2425,7 +2454,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT:
return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg); return parse_duration(&config->conn.listener.timeout.sleep, opt, optarg);
case SHRPX_OPTID_TLS_TICKET_KEY_FILE: case SHRPX_OPTID_TLS_TICKET_KEY_FILE:
config->tls.ticket.files.push_back(optarg.str()); config->tls.ticket.files.emplace_back(
make_string_ref(config->balloc, optarg));
return 0; return 0;
case SHRPX_OPTID_RLIMIT_NOFILE: { case SHRPX_OPTID_RLIMIT_NOFILE: {
int n; int n;
@ -2475,7 +2505,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE:
config->tls.ocsp.fetch_ocsp_response_file = config->tls.ocsp.fetch_ocsp_response_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_OCSP_UPDATE_INTERVAL: case SHRPX_OPTID_OCSP_UPDATE_INTERVAL:
@ -2553,14 +2583,14 @@ int parse_config(Config *config, int optid, const StringRef &opt,
switch (optid) { switch (optid) {
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: { case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: {
auto &memcachedconf = config->tls.session_cache.memcached; auto &memcachedconf = config->tls.session_cache.memcached;
memcachedconf.host = ImmutableString{host}; memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
memcachedconf.port = port; memcachedconf.port = port;
memcachedconf.tls = params.tls; memcachedconf.tls = params.tls;
break; break;
} }
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: { case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED: {
auto &memcachedconf = config->tls.ticket.memcached; auto &memcachedconf = config->tls.ticket.memcached;
memcachedconf.host = ImmutableString{host}; memcachedconf.host = make_string_ref(config->balloc, StringRef{host});
memcachedconf.port = port; memcachedconf.port = port;
memcachedconf.tls = params.tls; memcachedconf.tls = params.tls;
break; break;
@ -2603,7 +2633,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_MRUBY_FILE: case SHRPX_OPTID_MRUBY_FILE:
#ifdef HAVE_MRUBY #ifdef HAVE_MRUBY
config->mruby_file = ImmutableString{std::begin(optarg), std::end(optarg)}; config->mruby_file = make_string_ref(config->balloc, optarg);
#else // !HAVE_MRUBY #else // !HAVE_MRUBY
LOG(WARN) << opt LOG(WARN) << opt
<< ": ignored because mruby support is disabled at build time."; << ": ignored because mruby support is disabled at build time.";
@ -2662,9 +2692,9 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_FORWARDED_BY: case SHRPX_OPTID_FORWARDED_BY:
fwdconf.by_node_type = static_cast<shrpx_forwarded_node_type>(type); fwdconf.by_node_type = static_cast<shrpx_forwarded_node_type>(type);
if (optarg[0] == '_') { if (optarg[0] == '_') {
fwdconf.by_obfuscated = optarg.str(); fwdconf.by_obfuscated = make_string_ref(config->balloc, optarg);
} else { } else {
fwdconf.by_obfuscated = ""; fwdconf.by_obfuscated = StringRef::from_lit("");
} }
break; break;
case SHRPX_OPTID_FORWARDED_FOR: case SHRPX_OPTID_FORWARDED_FOR:
@ -2689,12 +2719,12 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE: case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE:
config->tls.session_cache.memcached.cert_file = config->tls.session_cache.memcached.cert_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE: case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_PRIVATE_KEY_FILE:
config->tls.session_cache.memcached.private_key_file = config->tls.session_cache.memcached.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_TLS:
@ -2703,12 +2733,12 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return 0; return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_CERT_FILE:
config->tls.ticket.memcached.cert_file = config->tls.ticket.memcached.cert_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE:
config->tls.ticket.memcached.private_key_file = config->tls.ticket.memcached.private_key_file =
ImmutableString{std::begin(optarg), std::end(optarg)}; make_string_ref(config->balloc, optarg);
return 0; return 0;
case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY: case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_ADDRESS_FAMILY:
@ -2748,8 +2778,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return parse_duration(&config->conn.downstream->timeout.max_backoff, opt, return parse_duration(&config->conn.downstream->timeout.max_backoff, opt,
optarg); optarg);
case SHRPX_OPTID_SERVER_NAME: case SHRPX_OPTID_SERVER_NAME:
config->http.server_name = config->http.server_name = make_string_ref(config->balloc, optarg);
ImmutableString{std::begin(optarg), std::end(optarg)};
return 0; return 0;
case SHRPX_OPTID_NO_SERVER_REWRITE: case SHRPX_OPTID_NO_SERVER_REWRITE:
@ -3071,13 +3100,13 @@ int configure_downstream_group(Config *config, bool http2_proxy,
if (addr_groups.empty()) { if (addr_groups.empty()) {
DownstreamAddrConfig addr{}; DownstreamAddrConfig addr{};
addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); addr.host = StringRef::from_lit(DEFAULT_DOWNSTREAM_HOST);
addr.port = DEFAULT_DOWNSTREAM_PORT; addr.port = DEFAULT_DOWNSTREAM_PORT;
addr.proto = PROTO_HTTP1; addr.proto = PROTO_HTTP1;
DownstreamAddrGroupConfig g(StringRef::from_lit("/")); DownstreamAddrGroupConfig g(StringRef::from_lit("/"));
g.addrs.push_back(std::move(addr)); g.addrs.push_back(std::move(addr));
router.add_route(StringRef{g.pattern}, addr_groups.size()); router.add_route(g.pattern, addr_groups.size());
addr_groups.push_back(std::move(g)); addr_groups.push_back(std::move(g));
} else if (http2_proxy) { } else if (http2_proxy) {
// We don't support host mapping in these cases. Move all // We don't support host mapping in these cases. Move all
@ -3090,7 +3119,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
std::vector<DownstreamAddrGroupConfig>().swap(addr_groups); std::vector<DownstreamAddrGroupConfig>().swap(addr_groups);
// maybe not necessary? // maybe not necessary?
routerconf = RouterConfig{}; routerconf = RouterConfig{};
router.add_route(StringRef{catch_all.pattern}, addr_groups.size()); router.add_route(catch_all.pattern, addr_groups.size());
addr_groups.push_back(std::move(catch_all)); addr_groups.push_back(std::move(catch_all));
} }
@ -3100,7 +3129,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
auto &sni = tlsconf.backend_sni_name; auto &sni = tlsconf.backend_sni_name;
for (auto &addr_group : addr_groups) { for (auto &addr_group : addr_groups) {
for (auto &addr : addr_group.addrs) { for (auto &addr : addr_group.addrs) {
addr.sni = ImmutableString{sni}; addr.sni = StringRef{sni};
} }
} }
} }
@ -3147,7 +3176,7 @@ int configure_downstream_group(Config *config, bool http2_proxy,
// for AF_UNIX socket, we use "localhost" as host for backend // for AF_UNIX socket, we use "localhost" as host for backend
// hostport. This is used as Host header field to backend and // hostport. This is used as Host header field to backend and
// not going to be passed to any syscalls. // not going to be passed to any syscalls.
addr.hostport = ImmutableString::from_lit("localhost"); addr.hostport = StringRef::from_lit("localhost");
auto path = addr.host.c_str(); auto path = addr.host.c_str();
auto pathlen = addr.host.size(); auto pathlen = addr.host.size();
@ -3171,10 +3200,11 @@ int configure_downstream_group(Config *config, bool http2_proxy,
continue; continue;
} }
addr.hostport = ImmutableString( addr.hostport =
util::make_http_hostport(StringRef(addr.host), addr.port)); util::make_http_hostport(downstreamconf.balloc, addr.host, addr.port);
auto hostport = util::make_hostport(StringRef{addr.host}, addr.port); auto hostport =
util::make_hostport(downstreamconf.balloc, addr.host, addr.port);
if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port, if (resolve_hostname(&addr.addr, addr.host.c_str(), addr.port,
downstreamconf.family, resolve_flags) == -1) { downstreamconf.family, resolve_flags) == -1) {

View File

@ -54,6 +54,7 @@
#include "template.h" #include "template.h"
#include "http2.h" #include "http2.h"
#include "network.h" #include "network.h"
#include "allocator.h"
using namespace nghttp2; using namespace nghttp2;
@ -336,7 +337,7 @@ enum shrpx_forwarded_node_type {
}; };
struct AltSvc { struct AltSvc {
std::string protocol_id, host, origin, service; StringRef protocol_id, host, origin, service;
uint16_t port; uint16_t port;
}; };
@ -352,12 +353,13 @@ enum UpstreamAltMode {
struct UpstreamAddr { struct UpstreamAddr {
// The frontend address (e.g., FQDN, hostname, IP address). If // The frontend address (e.g., FQDN, hostname, IP address). If
// |host_unix| is true, this is UNIX domain socket path. // |host_unix| is true, this is UNIX domain socket path. This must
ImmutableString host; // be NULL terminated string.
StringRef host;
// For TCP socket, this is <IP address>:<PORT>. For IPv6 address, // For TCP socket, this is <IP address>:<PORT>. For IPv6 address,
// address is surrounded by square brackets. If socket is UNIX // address is surrounded by square brackets. If socket is UNIX
// domain socket, this is "localhost". // domain socket, this is "localhost".
ImmutableString hostport; StringRef hostport;
// frontend port. 0 if |host_unix| is true. // frontend port. 0 if |host_unix| is true.
uint16_t port; uint16_t port;
// For TCP socket, this is either AF_INET or AF_INET6. For UNIX // For TCP socket, this is either AF_INET or AF_INET6. For UNIX
@ -375,13 +377,13 @@ struct UpstreamAddr {
struct DownstreamAddrConfig { struct DownstreamAddrConfig {
Address addr; Address addr;
// backend address. If |host_unix| is true, this is UNIX domain // backend address. If |host_unix| is true, this is UNIX domain
// socket path. // socket path. This must be NULL terminated string.
ImmutableString host; StringRef host;
// <HOST>:<PORT>. This does not treat 80 and 443 specially. If // <HOST>:<PORT>. This does not treat 80 and 443 specially. If
// |host_unix| is true, this is "localhost". // |host_unix| is true, this is "localhost".
ImmutableString hostport; StringRef hostport;
// hostname sent as SNI field // hostname sent as SNI field
ImmutableString sni; StringRef sni;
size_t fall; size_t fall;
size_t rise; size_t rise;
// Application protocol used in this group // Application protocol used in this group
@ -404,9 +406,9 @@ struct AffinityHash {
struct DownstreamAddrGroupConfig { struct DownstreamAddrGroupConfig {
DownstreamAddrGroupConfig(const StringRef &pattern) DownstreamAddrGroupConfig(const StringRef &pattern)
: pattern(pattern.c_str(), pattern.size()), affinity(AFFINITY_NONE) {} : pattern(pattern), affinity(AFFINITY_NONE) {}
ImmutableString pattern; StringRef pattern;
std::vector<DownstreamAddrConfig> addrs; std::vector<DownstreamAddrConfig> addrs;
// Bunch of session affinity hash. Only used if affinity == // Bunch of session affinity hash. Only used if affinity ==
// AFFINITY_IP. // AFFINITY_IP.
@ -437,9 +439,9 @@ struct TicketKeys {
struct HttpProxy { struct HttpProxy {
Address addr; Address addr;
// host in http proxy URI // host in http proxy URI
std::string host; StringRef host;
// userinfo in http proxy URI, not percent-encoded form // userinfo in http proxy URI, not percent-encoded form
std::string userinfo; StringRef userinfo;
// port in http proxy URI // port in http proxy URI
uint16_t port; uint16_t port;
}; };
@ -452,10 +454,10 @@ struct TLSConfig {
uint16_t port; uint16_t port;
// Hostname of memcached server. This is also used as SNI field // Hostname of memcached server. This is also used as SNI field
// if TLS is enabled. // if TLS is enabled.
ImmutableString host; StringRef host;
// Client private key and certificate for authentication // Client private key and certificate for authentication
ImmutableString private_key_file; StringRef private_key_file;
ImmutableString cert_file; StringRef cert_file;
ev_tstamp interval; ev_tstamp interval;
// Maximum number of retries when getting TLS ticket key from // Maximum number of retries when getting TLS ticket key from
// mamcached, due to network error. // mamcached, due to network error.
@ -468,7 +470,7 @@ struct TLSConfig {
int family; int family;
bool tls; bool tls;
} memcached; } memcached;
std::vector<std::string> files; std::vector<StringRef> files;
const EVP_CIPHER *cipher; const EVP_CIPHER *cipher;
// true if --tls-ticket-key-cipher is used // true if --tls-ticket-key-cipher is used
bool cipher_given; bool cipher_given;
@ -481,10 +483,10 @@ struct TLSConfig {
uint16_t port; uint16_t port;
// Hostname of memcached server. This is also used as SNI field // Hostname of memcached server. This is also used as SNI field
// if TLS is enabled. // if TLS is enabled.
ImmutableString host; StringRef host;
// Client private key and certificate for authentication // Client private key and certificate for authentication
ImmutableString private_key_file; StringRef private_key_file;
ImmutableString cert_file; StringRef cert_file;
// Address family of memcached connection. One of either // Address family of memcached connection. One of either
// AF_INET, AF_INET6 or AF_UNSPEC. // AF_INET, AF_INET6 or AF_UNSPEC.
int family; int family;
@ -501,7 +503,7 @@ struct TLSConfig {
// OCSP realted configurations // OCSP realted configurations
struct { struct {
ev_tstamp update_interval; ev_tstamp update_interval;
ImmutableString fetch_ocsp_response_file; StringRef fetch_ocsp_response_file;
bool disabled; bool disabled;
} ocsp; } ocsp;
@ -509,36 +511,36 @@ struct TLSConfig {
struct { struct {
// Path to file containing CA certificate solely used for client // Path to file containing CA certificate solely used for client
// certificate validation // certificate validation
ImmutableString cacert; StringRef cacert;
bool enabled; bool enabled;
} client_verify; } client_verify;
// Client private key and certificate used in backend connections. // Client private key and certificate used in backend connections.
struct { struct {
ImmutableString private_key_file; StringRef private_key_file;
ImmutableString cert_file; StringRef cert_file;
} client; } client;
// The list of (private key file, certificate file) pair // The list of (private key file, certificate file) pair
std::vector<std::pair<std::string, std::string>> subcerts; std::vector<std::pair<StringRef, StringRef>> subcerts;
std::vector<unsigned char> alpn_prefs; std::vector<unsigned char> alpn_prefs;
// list of supported NPN/ALPN protocol strings in the order of // list of supported NPN/ALPN protocol strings in the order of
// preference. // preference.
std::vector<std::string> npn_list; std::vector<StringRef> npn_list;
// list of supported SSL/TLS protocol strings. // list of supported SSL/TLS protocol strings.
std::vector<std::string> tls_proto_list; std::vector<StringRef> tls_proto_list;
BIO_METHOD *bio_method; BIO_METHOD *bio_method;
// Bit mask to disable SSL/TLS protocol versions. This will be // Bit mask to disable SSL/TLS protocol versions. This will be
// passed to SSL_CTX_set_options(). // passed to SSL_CTX_set_options().
long int tls_proto_mask; long int tls_proto_mask;
std::string backend_sni_name; StringRef backend_sni_name;
std::chrono::seconds session_timeout; std::chrono::seconds session_timeout;
ImmutableString private_key_file; StringRef private_key_file;
ImmutableString private_key_passwd; StringRef private_key_passwd;
ImmutableString cert_file; StringRef cert_file;
ImmutableString dh_param_file; StringRef dh_param_file;
ImmutableString ciphers; StringRef ciphers;
ImmutableString cacert; StringRef cacert;
bool insecure; bool insecure;
bool no_http2_cipher_black_list; bool no_http2_cipher_black_list;
}; };
@ -556,7 +558,7 @@ struct HttpConfig {
// obfuscated value used in "by" parameter of Forwarded header // obfuscated value used in "by" parameter of Forwarded header
// field. This is only used when user defined static obfuscated // field. This is only used when user defined static obfuscated
// string is provided. // string is provided.
std::string by_obfuscated; StringRef by_obfuscated;
// bitwise-OR of one or more of shrpx_forwarded_param values. // bitwise-OR of one or more of shrpx_forwarded_param values.
uint32_t params; uint32_t params;
// type of value recorded in "by" parameter of Forwarded header // type of value recorded in "by" parameter of Forwarded header
@ -573,9 +575,9 @@ struct HttpConfig {
} xff; } xff;
std::vector<AltSvc> altsvcs; std::vector<AltSvc> altsvcs;
std::vector<ErrorPage> error_pages; std::vector<ErrorPage> error_pages;
Headers add_request_headers; HeaderRefs add_request_headers;
Headers add_response_headers; HeaderRefs add_response_headers;
ImmutableString server_name; StringRef server_name;
size_t request_header_field_buffer; size_t request_header_field_buffer;
size_t max_request_header_fields; size_t max_request_header_fields;
size_t response_header_field_buffer; size_t response_header_field_buffer;
@ -590,8 +592,8 @@ struct Http2Config {
struct { struct {
struct { struct {
struct { struct {
ImmutableString request_header_file; StringRef request_header_file;
ImmutableString response_header_file; StringRef response_header_file;
FILE *request_header; FILE *request_header;
FILE *response_header; FILE *response_header;
} dump; } dump;
@ -634,12 +636,12 @@ struct Http2Config {
struct LoggingConfig { struct LoggingConfig {
struct { struct {
std::vector<LogFragment> format; std::vector<LogFragment> format;
ImmutableString file; StringRef file;
// Send accesslog to syslog, ignoring accesslog_file. // Send accesslog to syslog, ignoring accesslog_file.
bool syslog; bool syslog;
} access; } access;
struct { struct {
ImmutableString file; StringRef file;
// Send errorlog to syslog, ignoring errorlog_file. // Send errorlog to syslog, ignoring errorlog_file.
bool syslog; bool syslog;
} error; } error;
@ -655,10 +657,11 @@ struct RateLimitConfig {
// field. router includes all path patterns sharing the same wildcard // field. router includes all path patterns sharing the same wildcard
// host. // host.
struct WildcardPattern { struct WildcardPattern {
WildcardPattern(const StringRef &host) WildcardPattern(const StringRef &host) : host(host) {}
: host(std::begin(host), std::end(host)) {}
ImmutableString host; // This might not be NULL terminated. Currently it is only used for
// comparison.
StringRef host;
Router router; Router router;
}; };
@ -676,7 +679,8 @@ struct RouterConfig {
struct DownstreamConfig { struct DownstreamConfig {
DownstreamConfig() DownstreamConfig()
: timeout{}, : balloc(1024, 1024),
timeout{},
addr_group_catch_all{0}, addr_group_catch_all{0},
connections_per_host{0}, connections_per_host{0},
connections_per_frontend{0}, connections_per_frontend{0},
@ -684,6 +688,16 @@ struct DownstreamConfig {
response_buffer_size{0}, response_buffer_size{0},
family{0} {} family{0} {}
DownstreamConfig(const DownstreamConfig &) = delete;
DownstreamConfig(DownstreamConfig &&) = delete;
DownstreamConfig &operator=(const DownstreamConfig &) = delete;
DownstreamConfig &operator=(DownstreamConfig &&) = delete;
// Allocator to allocate memory for Downstream configuration. Since
// we may swap around DownstreamConfig in arbitrary times with API
// calls, we should use their own allocator instead of per Config
// allocator.
BlockAllocator balloc;
struct { struct {
ev_tstamp read; ev_tstamp read;
ev_tstamp write; ev_tstamp write;
@ -745,7 +759,25 @@ struct APIConfig {
}; };
struct Config { struct Config {
Config() = default; Config()
: balloc(4096, 4096),
downstream_http_proxy{},
http{},
http2{},
tls{},
logging{},
conn{},
api{},
num_worker{0},
padding{0},
rlimit_nofile{0},
uid{0},
gid{0},
pid{0},
verbose{false},
daemon{false},
http2_proxy{false},
ev_loop_flags{0} {}
~Config(); ~Config();
Config(Config &&) = delete; Config(Config &&) = delete;
@ -753,6 +785,10 @@ struct Config {
Config &operator=(Config &&) = delete; Config &operator=(Config &&) = delete;
Config &operator=(const Config &&) = delete; Config &operator=(const Config &&) = delete;
// Allocator to allocate memory for this object except for
// DownstreamConfig. Currently, it is used to allocate memory for
// strings.
BlockAllocator balloc;
HttpProxy downstream_http_proxy; HttpProxy downstream_http_proxy;
HttpConfig http; HttpConfig http;
Http2Config http2; Http2Config http2;
@ -760,10 +796,10 @@ struct Config {
LoggingConfig logging; LoggingConfig logging;
ConnectionConfig conn; ConnectionConfig conn;
APIConfig api; APIConfig api;
ImmutableString pid_file; StringRef pid_file;
ImmutableString conf_path; StringRef conf_path;
ImmutableString user; StringRef user;
ImmutableString mruby_file; StringRef mruby_file;
size_t num_worker; size_t num_worker;
size_t padding; size_t padding;
size_t rlimit_nofile; size_t rlimit_nofile;
@ -958,9 +994,11 @@ int load_config(Config *config, const char *filename,
// like "NAME: VALUE". We require that NAME is non empty string. ":" // like "NAME: VALUE". We require that NAME is non empty string. ":"
// is allowed at the start of the NAME, but NAME == ":" is not // is allowed at the start of the NAME, but NAME == ":" is not
// allowed. This function returns pair of NAME and VALUE. // allowed. This function returns pair of NAME and VALUE.
Headers::value_type parse_header(const StringRef &optarg); HeaderRefs::value_type parse_header(BlockAllocator &balloc,
const StringRef &optarg);
std::vector<LogFragment> parse_log_format(const StringRef &optarg); std::vector<LogFragment> parse_log_format(BlockAllocator &balloc,
const StringRef &optarg);
// Returns string for syslog |facility|. // Returns string for syslog |facility|.
StringRef str_syslog_facility(int facility); StringRef str_syslog_facility(int facility);
@ -975,7 +1013,7 @@ FILE *open_file_for_write(const char *filename);
// expected file size. This function returns TicketKey if it // expected file size. This function returns TicketKey if it
// succeeds, or nullptr. // succeeds, or nullptr.
std::unique_ptr<TicketKeys> std::unique_ptr<TicketKeys>
read_tls_ticket_key_file(const std::vector<std::string> &files, read_tls_ticket_key_file(const std::vector<StringRef> &files,
const EVP_CIPHER *cipher, const EVP_MD *hmac); const EVP_CIPHER *cipher, const EVP_MD *hmac);
// Returns string representation of |proto|. // Returns string representation of |proto|.

View File

@ -37,40 +37,45 @@
namespace shrpx { namespace shrpx {
void test_shrpx_config_parse_header(void) { void test_shrpx_config_parse_header(void) {
auto p = parse_header(StringRef::from_lit("a: b")); BlockAllocator balloc(4096, 4096);
auto p = parse_header(balloc, StringRef::from_lit("a: b"));
CU_ASSERT("a" == p.name); CU_ASSERT("a" == p.name);
CU_ASSERT("b" == p.value); CU_ASSERT("b" == p.value);
p = parse_header(StringRef::from_lit("a: b")); p = parse_header(balloc, StringRef::from_lit("a: b"));
CU_ASSERT("a" == p.name); CU_ASSERT("a" == p.name);
CU_ASSERT("b" == p.value); CU_ASSERT("b" == p.value);
p = parse_header(StringRef::from_lit(":a: b")); p = parse_header(balloc, StringRef::from_lit(":a: b"));
CU_ASSERT(p.name.empty()); CU_ASSERT(p.name.empty());
p = parse_header(StringRef::from_lit("a: :b")); p = parse_header(balloc, StringRef::from_lit("a: :b"));
CU_ASSERT("a" == p.name); CU_ASSERT("a" == p.name);
CU_ASSERT(":b" == p.value); CU_ASSERT(":b" == p.value);
p = parse_header(StringRef::from_lit(": b")); p = parse_header(balloc, StringRef::from_lit(": b"));
CU_ASSERT(p.name.empty()); CU_ASSERT(p.name.empty());
p = parse_header(StringRef::from_lit("alpha: bravo charlie")); p = parse_header(balloc, StringRef::from_lit("alpha: bravo charlie"));
CU_ASSERT("alpha" == p.name); CU_ASSERT("alpha" == p.name);
CU_ASSERT("bravo charlie" == p.value); CU_ASSERT("bravo charlie" == p.value);
p = parse_header(StringRef::from_lit("a,: b")); p = parse_header(balloc, StringRef::from_lit("a,: b"));
CU_ASSERT(p.name.empty()); CU_ASSERT(p.name.empty());
p = parse_header(StringRef::from_lit("a: b\x0a")); p = parse_header(balloc, StringRef::from_lit("a: b\x0a"));
CU_ASSERT(p.name.empty()); CU_ASSERT(p.name.empty());
} }
void test_shrpx_config_parse_log_format(void) { void test_shrpx_config_parse_log_format(void) {
auto res = parse_log_format(StringRef::from_lit( BlockAllocator balloc(4096, 4096);
R"($remote_addr - $remote_user [$time_local] )"
R"("$request" $status $body_bytes_sent )" auto res = parse_log_format(
R"("${http_referer}" $http_host "$http_user_agent")")); balloc, StringRef::from_lit(
R"($remote_addr - $remote_user [$time_local] )"
R"("$request" $status $body_bytes_sent )"
R"("${http_referer}" $http_host "$http_user_agent")"));
CU_ASSERT(16 == res.size()); CU_ASSERT(16 == res.size());
CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type); CU_ASSERT(SHRPX_LOGF_REMOTE_ADDR == res[0].type);
@ -115,35 +120,35 @@ void test_shrpx_config_parse_log_format(void) {
CU_ASSERT(SHRPX_LOGF_LITERAL == res[15].type); CU_ASSERT(SHRPX_LOGF_LITERAL == res[15].type);
CU_ASSERT("\"" == res[15].value); CU_ASSERT("\"" == res[15].value);
res = parse_log_format(StringRef::from_lit("$")); res = parse_log_format(balloc, StringRef::from_lit("$"));
CU_ASSERT(1 == res.size()); CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type); CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT("$" == res[0].value); CU_ASSERT("$" == res[0].value);
res = parse_log_format(StringRef::from_lit("${")); res = parse_log_format(balloc, StringRef::from_lit("${"));
CU_ASSERT(1 == res.size()); CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type); CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT("${" == res[0].value); CU_ASSERT("${" == res[0].value);
res = parse_log_format(StringRef::from_lit("${a")); res = parse_log_format(balloc, StringRef::from_lit("${a"));
CU_ASSERT(1 == res.size()); CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type); CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT("${a" == res[0].value); CU_ASSERT("${a" == res[0].value);
res = parse_log_format(StringRef::from_lit("${a ")); res = parse_log_format(balloc, StringRef::from_lit("${a "));
CU_ASSERT(1 == res.size()); CU_ASSERT(1 == res.size());
CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type); CU_ASSERT(SHRPX_LOGF_LITERAL == res[0].type);
CU_ASSERT("${a " == res[0].value); CU_ASSERT("${a " == res[0].value);
res = parse_log_format(StringRef::from_lit("$$remote_addr")); res = parse_log_format(balloc, StringRef::from_lit("$$remote_addr"));
CU_ASSERT(2 == res.size()); CU_ASSERT(2 == res.size());
@ -168,8 +173,8 @@ void test_shrpx_config_read_tls_ticket_key_file(void) {
close(fd1); close(fd1);
close(fd2); close(fd2);
auto ticket_keys = auto ticket_keys = read_tls_ticket_key_file(
read_tls_ticket_key_file({file1, file2}, EVP_aes_128_cbc(), EVP_sha256()); {StringRef{file1}, StringRef{file2}}, EVP_aes_128_cbc(), EVP_sha256());
unlink(file1); unlink(file1);
unlink(file2); unlink(file2);
CU_ASSERT(ticket_keys.get() != nullptr); CU_ASSERT(ticket_keys.get() != nullptr);
@ -211,8 +216,8 @@ void test_shrpx_config_read_tls_ticket_key_file_aes_256(void) {
close(fd1); close(fd1);
close(fd2); close(fd2);
auto ticket_keys = auto ticket_keys = read_tls_ticket_key_file(
read_tls_ticket_key_file({file1, file2}, EVP_aes_256_cbc(), EVP_sha256()); {StringRef{file1}, StringRef{file2}}, EVP_aes_256_cbc(), EVP_sha256());
unlink(file1); unlink(file1);
unlink(file2); unlink(file2);
CU_ASSERT(ticket_keys.get() != nullptr); CU_ASSERT(ticket_keys.get() != nullptr);

View File

@ -224,8 +224,8 @@ int ConnectionHandler::create_single_worker() {
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_.get(), nb_.get(),
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{memcachedconf.cert_file}, tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
StringRef{memcachedconf.private_key_file}, nullptr); nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx); all_ssl_ctx_.push_back(session_cache_ssl_ctx);
} }
@ -280,8 +280,8 @@ int ConnectionHandler::create_worker_thread(size_t num) {
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_.get(), nb_.get(),
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{memcachedconf.cert_file}, tlsconf.cacert, memcachedconf.cert_file,
StringRef{memcachedconf.private_key_file}, nullptr); memcachedconf.private_key_file, nullptr);
all_ssl_ctx_.push_back(session_cache_ssl_ctx); all_ssl_ctx_.push_back(session_cache_ssl_ctx);
} }
auto worker = make_unique<Worker>( auto worker = make_unique<Worker>(
@ -835,8 +835,8 @@ SSL_CTX *ConnectionHandler::create_tls_ticket_key_memcached_ssl_ctx() {
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb_.get(), nb_.get(),
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{memcachedconf.cert_file}, tlsconf.cacert, memcachedconf.cert_file, memcachedconf.private_key_file,
StringRef{memcachedconf.private_key_file}, nullptr); nullptr);
all_ssl_ctx_.push_back(ssl_ctx); all_ssl_ctx_.push_back(ssl_ctx);

View File

@ -46,12 +46,11 @@ StringRef create_error_html(BlockAllocator &balloc, unsigned int http_status) {
} }
auto status_string = http2::get_status_string(balloc, http_status); auto status_string = http2::get_status_string(balloc, http_status);
const auto &server_name = httpconf.server_name;
return concat_string_ref( return concat_string_ref(
balloc, StringRef::from_lit(R"(<!DOCTYPE html><html lang="en"><title>)"), balloc, StringRef::from_lit(R"(<!DOCTYPE html><html lang="en"><title>)"),
status_string, StringRef::from_lit("</title><body><h1>"), status_string, status_string, StringRef::from_lit("</title><body><h1>"), status_string,
StringRef::from_lit("</h1><footer>"), StringRef{server_name}, StringRef::from_lit("</h1><footer>"), httpconf.server_name,
StringRef::from_lit("</footer></body></html>")); StringRef::from_lit("</footer></body></html>"));
} }

View File

@ -357,7 +357,7 @@ int Http2DownstreamConnection::push_request_headers() {
if (xffconf.add) { if (xffconf.add) {
StringRef xff_value; StringRef xff_value;
auto addr = StringRef{upstream->get_client_handler()->get_ipaddr()}; const auto &addr = upstream->get_client_handler()->get_ipaddr();
if (xff) { if (xff) {
xff_value = concat_string_ref(balloc, xff->value, xff_value = concat_string_ref(balloc, xff->value,
StringRef::from_lit(", "), addr); StringRef::from_lit(", "), addr);

View File

@ -108,15 +108,16 @@ int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
int Http2Upstream::upgrade_upstream(HttpsUpstream *http) { int Http2Upstream::upgrade_upstream(HttpsUpstream *http) {
int rv; int rv;
auto http2_settings = http->get_downstream()->get_http2_settings().str(); auto &balloc = http->get_downstream()->get_block_allocator();
util::to_base64(http2_settings);
auto settings_payload = auto http2_settings = http->get_downstream()->get_http2_settings();
base64::decode(std::begin(http2_settings), std::end(http2_settings)); http2_settings = util::to_base64(balloc, http2_settings);
auto settings_payload = base64::decode(balloc, std::begin(http2_settings),
std::end(http2_settings));
rv = nghttp2_session_upgrade2( rv = nghttp2_session_upgrade2(
session_, reinterpret_cast<const uint8_t *>(settings_payload.c_str()), session_, settings_payload.byte(), settings_payload.size(),
settings_payload.size(),
http->get_downstream()->request().method == HTTP_HEAD, nullptr); http->get_downstream()->request().method == HTTP_HEAD, nullptr);
if (rv != 0) { if (rv != 0) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -1429,8 +1430,8 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body,
} }
if (!resp.fs.header(http2::HD_SERVER)) { if (!resp.fs.header(http2::HD_SERVER)) {
nva.push_back(http2::make_nv_ls_nocopy( nva.push_back(
"server", StringRef{get_config()->http.server_name})); http2::make_nv_ls_nocopy("server", get_config()->http.server_name));
} }
for (auto &p : httpconf.add_response_headers) { for (auto &p : httpconf.add_response_headers) {
@ -1481,8 +1482,7 @@ int Http2Upstream::error_reply(Downstream *downstream,
auto nva = std::array<nghttp2_nv, 5>{ auto nva = std::array<nghttp2_nv, 5>{
{http2::make_nv_ls_nocopy(":status", response_status), {http2::make_nv_ls_nocopy(":status", response_status),
http2::make_nv_ll("content-type", "text/html; charset=UTF-8"), http2::make_nv_ll("content-type", "text/html; charset=UTF-8"),
http2::make_nv_ls_nocopy("server", http2::make_nv_ls_nocopy("server", get_config()->http.server_name),
StringRef{get_config()->http.server_name}),
http2::make_nv_ls_nocopy("content-length", content_length), http2::make_nv_ls_nocopy("content-length", content_length),
http2::make_nv_ls_nocopy("date", date)}}; http2::make_nv_ls_nocopy("date", date)}};
@ -1629,8 +1629,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {
http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers()); http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers());
if (!get_config()->http2_proxy && !httpconf.no_server_rewrite) { if (!get_config()->http2_proxy && !httpconf.no_server_rewrite) {
nva.push_back( nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name));
http2::make_nv_ls_nocopy("server", StringRef{httpconf.server_name}));
} else { } else {
auto server = resp.fs.header(http2::HD_SERVER); auto server = resp.fs.header(http2::HD_SERVER);
if (server) { if (server) {

View File

@ -960,13 +960,14 @@ std::unique_ptr<Downstream> HttpsUpstream::pop_downstream() {
} }
namespace { namespace {
void write_altsvc(DefaultMemchunks *buf, const AltSvc &altsvc) { void write_altsvc(DefaultMemchunks *buf, BlockAllocator &balloc,
buf->append(util::percent_encode_token(altsvc.protocol_id)); const AltSvc &altsvc) {
buf->append(util::percent_encode_token(balloc, altsvc.protocol_id));
buf->append("=\""); buf->append("=\"");
buf->append(util::quote_string(altsvc.host)); buf->append(util::quote_string(balloc, altsvc.host));
buf->append(":"); buf->append(':');
buf->append(altsvc.service); buf->append(altsvc.service);
buf->append("\""); buf->append('"');
} }
} // namespace } // namespace
@ -1073,10 +1074,10 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) {
buf->append("Alt-Svc: "); buf->append("Alt-Svc: ");
auto &altsvcs = httpconf.altsvcs; auto &altsvcs = httpconf.altsvcs;
write_altsvc(buf, altsvcs[0]); write_altsvc(buf, downstream->get_block_allocator(), altsvcs[0]);
for (size_t i = 1; i < altsvcs.size(); ++i) { for (size_t i = 1; i < altsvcs.size(); ++i) {
buf->append(", "); buf->append(", ");
write_altsvc(buf, altsvcs[i]); write_altsvc(buf, downstream->get_block_allocator(), altsvcs[i]);
} }
buf->append("\r\n"); buf->append("\r\n");
} }

View File

@ -290,7 +290,7 @@ void upstream_accesslog(const std::vector<LogFragment> &lfv,
break; break;
case SHRPX_LOGF_HTTP: case SHRPX_LOGF_HTTP:
if (req) { if (req) {
auto hd = req->fs.header(StringRef(lf.value)); auto hd = req->fs.header(lf.value);
if (hd) { if (hd) {
std::tie(p, avail) = copy((*hd).value, avail, p); std::tie(p, avail) = copy((*hd).value, avail, p);
break; break;

View File

@ -137,10 +137,10 @@ enum LogFragmentType {
}; };
struct LogFragment { struct LogFragment {
LogFragment(LogFragmentType type, ImmutableString value = ImmutableString()) LogFragment(LogFragmentType type, StringRef value = StringRef::from_lit(""))
: type(type), value(std::move(value)) {} : type(type), value(std::move(value)) {}
LogFragmentType type; LogFragmentType type;
ImmutableString value; StringRef value;
}; };
struct LogSpec { struct LogSpec {

View File

@ -100,7 +100,7 @@ MemcachedConnection::MemcachedConnection(const Address *addr,
connectcb, readcb, timeoutcb, this, 0, 0., PROTO_MEMCACHED), connectcb, readcb, timeoutcb, this, 0, 0., PROTO_MEMCACHED),
do_read_(&MemcachedConnection::noop), do_read_(&MemcachedConnection::noop),
do_write_(&MemcachedConnection::noop), do_write_(&MemcachedConnection::noop),
sni_name_(sni_name.str()), sni_name_(sni_name),
connect_blocker_(gen, loop, [] {}, [] {}), connect_blocker_(gen, loop, [] {}, [] {}),
parse_state_{}, parse_state_{},
addr_(addr), addr_(addr),
@ -268,7 +268,7 @@ int MemcachedConnection::tls_handshake() {
auto &tlsconf = get_config()->tls; auto &tlsconf = get_config()->tls;
if (!tlsconf.insecure && if (!tlsconf.insecure &&
ssl::check_cert(conn_.tls.ssl, addr_, StringRef(sni_name_)) != 0) { ssl::check_cert(conn_.tls.ssl, addr_, sni_name_) != 0) {
connect_blocker_.on_failure(); connect_blocker_.on_failure();
return -1; return -1;
} }

View File

@ -135,7 +135,7 @@ private:
std::deque<std::unique_ptr<MemcachedRequest>> sendq_; std::deque<std::unique_ptr<MemcachedRequest>> sendq_;
std::deque<MemcachedSendbuf> sendbufv_; std::deque<MemcachedSendbuf> sendbufv_;
std::function<int(MemcachedConnection &)> do_read_, do_write_; std::function<int(MemcachedConnection &)> do_read_, do_write_;
std::string sni_name_; StringRef sni_name_;
ssl::TLSSessionCache tls_session_cache_; ssl::TLSSessionCache tls_session_cache_;
ConnectBlocker connect_blocker_; ConnectBlocker connect_blocker_;
MemcachedParseState parse_state_; MemcachedParseState parse_state_;

View File

@ -103,7 +103,7 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
} // namespace } // namespace
int set_alpn_prefs(std::vector<unsigned char> &out, int set_alpn_prefs(std::vector<unsigned char> &out,
const std::vector<std::string> &protos) { const std::vector<StringRef> &protos) {
size_t len = 0; size_t len = 0;
for (const auto &proto : protos) { for (const auto &proto : protos) {
@ -125,8 +125,7 @@ int set_alpn_prefs(std::vector<unsigned char> &out,
for (const auto &proto : protos) { for (const auto &proto : protos) {
*ptr++ = proto.size(); *ptr++ = proto.size();
memcpy(ptr, proto.c_str(), proto.size()); ptr = std::copy(std::begin(proto), std::end(proto), ptr);
ptr += proto.size();
} }
return 0; return 0;
@ -243,6 +242,7 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
auto handler = static_cast<ClientHandler *>(conn->data); auto handler = static_cast<ClientHandler *>(conn->data);
auto worker = handler->get_worker(); auto worker = handler->get_worker();
auto dispatcher = worker->get_session_cache_memcached_dispatcher(); auto dispatcher = worker->get_session_cache_memcached_dispatcher();
auto &balloc = handler->get_block_allocator();
const unsigned char *id; const unsigned char *id;
unsigned int idlen; unsigned int idlen;
@ -256,7 +256,8 @@ int tls_session_new_cb(SSL *ssl, SSL_SESSION *session) {
auto req = make_unique<MemcachedRequest>(); auto req = make_unique<MemcachedRequest>();
req->op = MEMCACHED_OP_ADD; req->op = MEMCACHED_OP_ADD;
req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str(); req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str();
req->key += util::format_hex(id, idlen); req->key +=
util::format_hex(balloc, StringRef{id, static_cast<size_t>(idlen)});
auto sessionlen = i2d_SSL_SESSION(session, nullptr); auto sessionlen = i2d_SSL_SESSION(session, nullptr);
req->value.resize(sessionlen); req->value.resize(sessionlen);
@ -295,6 +296,7 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl,
auto handler = static_cast<ClientHandler *>(conn->data); auto handler = static_cast<ClientHandler *>(conn->data);
auto worker = handler->get_worker(); auto worker = handler->get_worker();
auto dispatcher = worker->get_session_cache_memcached_dispatcher(); auto dispatcher = worker->get_session_cache_memcached_dispatcher();
auto &balloc = handler->get_block_allocator();
if (conn->tls.cached_session) { if (conn->tls.cached_session) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -318,7 +320,8 @@ SSL_SESSION *tls_session_get_cb(SSL *ssl,
auto req = make_unique<MemcachedRequest>(); auto req = make_unique<MemcachedRequest>();
req->op = MEMCACHED_OP_GET; req->op = MEMCACHED_OP_GET;
req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str(); req->key = MEMCACHED_SESSION_CACHE_KEY_PREFIX.str();
req->key += util::format_hex(id, idlen); req->key +=
util::format_hex(balloc, StringRef{id, static_cast<size_t>(idlen)});
req->cb = [conn](MemcachedRequest *, MemcachedResult res) { req->cb = [conn](MemcachedRequest *, MemcachedResult res) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
LOG(INFO) << "Memcached: returned status code " << res.status_code; LOG(INFO) << "Memcached: returned status code " << res.status_code;
@ -465,8 +468,7 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
auto proto_len = *p; auto proto_len = *p;
if (proto_id + proto_len <= end && if (proto_id + proto_len <= end &&
util::streq(StringRef{target_proto_id}, util::streq(target_proto_id, StringRef{proto_id, proto_len})) {
StringRef{proto_id, proto_len})) {
*out = reinterpret_cast<const unsigned char *>(proto_id); *out = reinterpret_cast<const unsigned char *>(proto_id);
*outlen = proto_len; *outlen = proto_len;
@ -493,7 +495,7 @@ constexpr TLSProtocol TLS_PROTOS[] = {
TLSProtocol{StringRef::from_lit("TLSv1.1"), SSL_OP_NO_TLSv1_1}, TLSProtocol{StringRef::from_lit("TLSv1.1"), SSL_OP_NO_TLSv1_1},
TLSProtocol{StringRef::from_lit("TLSv1.0"), SSL_OP_NO_TLSv1}}; TLSProtocol{StringRef::from_lit("TLSv1.0"), SSL_OP_NO_TLSv1}};
long int create_tls_proto_mask(const std::vector<std::string> &tls_proto_list) { long int create_tls_proto_mask(const std::vector<StringRef> &tls_proto_list) {
long int res = 0; long int res = 0;
for (auto &supported : TLS_PROTOS) { for (auto &supported : TLS_PROTOS) {
@ -829,16 +831,16 @@ SSL *create_ssl(SSL_CTX *ssl_ctx) {
ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr, ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
int addrlen, const UpstreamAddr *faddr) { int addrlen, const UpstreamAddr *faddr) {
char host[NI_MAXHOST]; std::array<char, NI_MAXHOST> host;
char service[NI_MAXSERV]; std::array<char, NI_MAXSERV> service;
int rv; int rv;
if (addr->sa_family == AF_UNIX) { if (addr->sa_family == AF_UNIX) {
std::copy_n("localhost", sizeof("localhost"), host); std::copy_n("localhost", sizeof("localhost"), std::begin(host));
service[0] = '\0'; service[0] = '\0';
} else { } else {
rv = getnameinfo(addr, addrlen, host, sizeof(host), service, rv = getnameinfo(addr, addrlen, host.data(), host.size(), service.data(),
sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV); service.size(), NI_NUMERICHOST | NI_NUMERICSERV);
if (rv != 0) { if (rv != 0) {
LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv); LOG(ERROR) << "getnameinfo() failed: " << gai_strerror(rv);
@ -867,8 +869,8 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
} }
} }
return new ClientHandler(worker, fd, ssl, host, service, addr->sa_family, return new ClientHandler(worker, fd, ssl, StringRef{host.data()},
faddr); StringRef{service.data()}, addr->sa_family, faddr);
} }
bool tls_hostname_match(const StringRef &pattern, const StringRef &hostname) { bool tls_hostname_match(const StringRef &pattern, const StringRef &hostname) {
@ -1316,10 +1318,10 @@ int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
return 0; return 0;
} }
bool in_proto_list(const std::vector<std::string> &protos, bool in_proto_list(const std::vector<StringRef> &protos,
const StringRef &needle) { const StringRef &needle) {
for (auto &proto : protos) { for (auto &proto : protos) {
if (util::streq(StringRef{proto}, needle)) { if (util::streq(proto, needle)) {
return true; return true;
} }
} }
@ -1443,8 +1445,8 @@ SSL_CTX *setup_downstream_client_ssl_context(
#ifdef HAVE_NEVERBLEED #ifdef HAVE_NEVERBLEED
nb, nb,
#endif // HAVE_NEVERBLEED #endif // HAVE_NEVERBLEED
StringRef{tlsconf.cacert}, StringRef{tlsconf.client.cert_file}, tlsconf.cacert, tlsconf.client.cert_file, tlsconf.client.private_key_file,
StringRef{tlsconf.client.private_key_file}, select_next_proto_cb); select_next_proto_cb);
} }
void setup_downstream_http2_alpn(SSL *ssl) { void setup_downstream_http2_alpn(SSL *ssl) {

View File

@ -101,11 +101,6 @@ ClientHandler *accept_connection(Worker *worker, int fd, sockaddr *addr,
int check_cert(SSL *ssl, const Address *addr, const StringRef &host); int check_cert(SSL *ssl, const Address *addr, const StringRef &host);
int check_cert(SSL *ssl, const DownstreamAddr *addr); int check_cert(SSL *ssl, const DownstreamAddr *addr);
// Retrieves DNS and IP address in subjectAltNames and commonName from
// the |cert|.
void get_altnames(X509 *cert, std::vector<std::string> &dns_names,
std::vector<std::string> &ip_addrs, std::string &common_name);
struct WildcardRevPrefix { struct WildcardRevPrefix {
WildcardRevPrefix(const StringRef &prefix, size_t idx) WildcardRevPrefix(const StringRef &prefix, size_t idx)
: prefix(std::begin(prefix), std::end(prefix)), idx(idx) {} : prefix(std::begin(prefix), std::end(prefix)), idx(idx) {}
@ -172,7 +167,7 @@ int cert_lookup_tree_add_cert_from_x509(CertLookupTree *lt, size_t idx,
// Returns true if |proto| is included in the // Returns true if |proto| is included in the
// protocol list |protos|. // protocol list |protos|.
bool in_proto_list(const std::vector<std::string> &protos, bool in_proto_list(const std::vector<StringRef> &protos,
const StringRef &proto); const StringRef &proto);
// Returns true if security requirement for HTTP/2 is fulfilled. // Returns true if security requirement for HTTP/2 is fulfilled.
@ -181,10 +176,10 @@ bool check_http2_requirement(SSL *ssl);
// Returns SSL/TLS option mask to disable SSL/TLS protocol version not // Returns SSL/TLS option mask to disable SSL/TLS protocol version not
// included in |tls_proto_list|. The returned mask can be directly // included in |tls_proto_list|. The returned mask can be directly
// passed to SSL_CTX_set_options(). // passed to SSL_CTX_set_options().
long int create_tls_proto_mask(const std::vector<std::string> &tls_proto_list); long int create_tls_proto_mask(const std::vector<StringRef> &tls_proto_list);
int set_alpn_prefs(std::vector<unsigned char> &out, int set_alpn_prefs(std::vector<unsigned char> &out,
const std::vector<std::string> &protos); const std::vector<StringRef> &protos);
// Setups server side SSL_CTX. This function inspects get_config() // Setups server side SSL_CTX. This function inspects get_config()
// and if upstream_no_tls is true, returns nullptr. Otherwise // and if upstream_no_tls is true, returns nullptr. Otherwise

View File

@ -182,7 +182,8 @@ void Worker::replace_downstream_config(
auto &dst = downstream_addr_groups_[i]; auto &dst = downstream_addr_groups_[i];
dst = std::make_shared<DownstreamAddrGroup>(); dst = std::make_shared<DownstreamAddrGroup>();
dst->pattern = src.pattern; dst->pattern =
ImmutableString{std::begin(src.pattern), std::end(src.pattern)};
auto shared_addr = std::make_shared<SharedDownstreamAddr>(); auto shared_addr = std::make_shared<SharedDownstreamAddr>();
@ -198,13 +199,14 @@ void Worker::replace_downstream_config(
auto &dst_addr = shared_addr->addrs[j]; auto &dst_addr = shared_addr->addrs[j];
dst_addr.addr = src_addr.addr; dst_addr.addr = src_addr.addr;
dst_addr.host = src_addr.host; dst_addr.host = make_string_ref(shared_addr->balloc, src_addr.host);
dst_addr.hostport = src_addr.hostport; dst_addr.hostport =
make_string_ref(shared_addr->balloc, src_addr.hostport);
dst_addr.port = src_addr.port; dst_addr.port = src_addr.port;
dst_addr.host_unix = src_addr.host_unix; dst_addr.host_unix = src_addr.host_unix;
dst_addr.proto = src_addr.proto; dst_addr.proto = src_addr.proto;
dst_addr.tls = src_addr.tls; dst_addr.tls = src_addr.tls;
dst_addr.sni = src_addr.sni; dst_addr.sni = make_string_ref(shared_addr->balloc, src_addr.sni);
dst_addr.fall = src_addr.fall; dst_addr.fall = src_addr.fall;
dst_addr.rise = src_addr.rise; dst_addr.rise = src_addr.rise;

View File

@ -48,6 +48,7 @@
#include "shrpx_ssl.h" #include "shrpx_ssl.h"
#include "shrpx_live_check.h" #include "shrpx_live_check.h"
#include "shrpx_connect_blocker.h" #include "shrpx_connect_blocker.h"
#include "allocator.h"
using namespace nghttp2; using namespace nghttp2;
@ -75,15 +76,15 @@ struct DownstreamAddr {
Address addr; Address addr;
// backend address. If |host_unix| is true, this is UNIX domain // backend address. If |host_unix| is true, this is UNIX domain
// socket path. // socket path.
ImmutableString host; StringRef host;
ImmutableString hostport; StringRef hostport;
// backend port. 0 if |host_unix| is true. // backend port. 0 if |host_unix| is true.
uint16_t port; uint16_t port;
// true if |host| contains UNIX domain socket path. // true if |host| contains UNIX domain socket path.
bool host_unix; bool host_unix;
// sni field to send remote server if TLS is enabled. // sni field to send remote server if TLS is enabled.
ImmutableString sni; StringRef sni;
std::unique_ptr<ConnectBlocker> connect_blocker; std::unique_ptr<ConnectBlocker> connect_blocker;
std::unique_ptr<LiveCheck> live_check; std::unique_ptr<LiveCheck> live_check;
@ -128,8 +129,18 @@ struct WeightedPri {
struct SharedDownstreamAddr { struct SharedDownstreamAddr {
SharedDownstreamAddr() SharedDownstreamAddr()
: next{0}, http1_pri{}, http2_pri{}, affinity{AFFINITY_NONE} {} : balloc(1024, 1024),
next{0},
http1_pri{},
http2_pri{},
affinity{AFFINITY_NONE} {}
SharedDownstreamAddr(const SharedDownstreamAddr &) = delete;
SharedDownstreamAddr(SharedDownstreamAddr &&) = delete;
SharedDownstreamAddr &operator=(const SharedDownstreamAddr &) = delete;
SharedDownstreamAddr &operator=(SharedDownstreamAddr &&) = delete;
BlockAllocator balloc;
std::vector<DownstreamAddr> addrs; std::vector<DownstreamAddr> addrs;
// Bunch of session affinity hash. Only used if affinity == // Bunch of session affinity hash. Only used if affinity ==
// AFFINITY_IP. // AFFINITY_IP.
@ -162,6 +173,11 @@ struct SharedDownstreamAddr {
struct DownstreamAddrGroup { struct DownstreamAddrGroup {
DownstreamAddrGroup() : retired{false} {}; DownstreamAddrGroup() : retired{false} {};
DownstreamAddrGroup(const DownstreamAddrGroup &) = delete;
DownstreamAddrGroup(DownstreamAddrGroup &&) = delete;
DownstreamAddrGroup &operator=(const DownstreamAddrGroup &) = delete;
DownstreamAddrGroup &operator=(DownstreamAddrGroup &&) = delete;
ImmutableString pattern; ImmutableString pattern;
std::shared_ptr<SharedDownstreamAddr> shared_addr; std::shared_ptr<SharedDownstreamAddr> shared_addr;
// true if this group is no longer used for new request. If this is // true if this group is no longer used for new request. If this is

View File

@ -131,11 +131,10 @@ bool in_attr_char(char c) {
std::find(std::begin(bad), std::end(bad), c) == std::end(bad); std::find(std::begin(bad), std::end(bad), c) == std::end(bad);
} }
std::string percent_encode_token(const std::string &target) { StringRef percent_encode_token(BlockAllocator &balloc,
std::string dest; const StringRef &target) {
auto iov = make_byte_ref(balloc, target.size() * 3 + 1);
dest.resize(target.size() * 3); auto p = iov.base;
auto p = std::begin(dest);
for (auto first = std::begin(target); first != std::end(target); ++first) { for (auto first = std::begin(target); first != std::end(target); ++first) {
uint8_t c = *first; uint8_t c = *first;
@ -149,8 +148,10 @@ std::string percent_encode_token(const std::string &target) {
*p++ = UPPER_XDIGITS[c >> 4]; *p++ = UPPER_XDIGITS[c >> 4];
*p++ = UPPER_XDIGITS[(c & 0x0f)]; *p++ = UPPER_XDIGITS[(c & 0x0f)];
} }
dest.resize(p - std::begin(dest));
return dest; *p = '\0';
return StringRef{iov.base, p};
} }
uint32_t hex_to_uint(char c) { uint32_t hex_to_uint(char c) {
@ -166,25 +167,27 @@ uint32_t hex_to_uint(char c) {
return c; return c;
} }
std::string quote_string(const std::string &target) { StringRef quote_string(BlockAllocator &balloc, const StringRef &target) {
auto cnt = std::count(std::begin(target), std::end(target), '"'); auto cnt = std::count(std::begin(target), std::end(target), '"');
if (cnt == 0) { if (cnt == 0) {
return target; return make_string_ref(balloc, target);
} }
std::string res; auto iov = make_byte_ref(balloc, target.size() + cnt + 1);
res.reserve(target.size() + cnt); auto p = iov.base;
for (auto c : target) { for (auto c : target) {
if (c == '"') { if (c == '"') {
res += "\\\""; *p++ = '\\';
*p++ = '"';
} else { } else {
res += c; *p++ = c;
} }
} }
*p = '\0';
return res; return StringRef{iov.base, p};
} }
namespace { namespace {
@ -376,6 +379,21 @@ std::string format_hex(const unsigned char *s, size_t len) {
return res; return res;
} }
StringRef format_hex(BlockAllocator &balloc, const StringRef &s) {
auto iov = make_byte_ref(balloc, s.size() * 2 + 1);
auto p = iov.base;
for (auto cc : s) {
uint8_t c = cc;
*p++ = LOWER_XDIGITS[c >> 4];
*p++ = LOWER_XDIGITS[c & 0xf];
}
*p = '\0';
return StringRef{iov.base, p};
}
void to_token68(std::string &base64str) { void to_token68(std::string &base64str) {
std::transform(std::begin(base64str), std::end(base64str), std::transform(std::begin(base64str), std::end(base64str),
std::begin(base64str), [](char c) { std::begin(base64str), [](char c) {
@ -392,22 +410,32 @@ void to_token68(std::string &base64str) {
std::end(base64str)); std::end(base64str));
} }
void to_base64(std::string &token68str) { StringRef to_base64(BlockAllocator &balloc, const StringRef &token68str) {
std::transform(std::begin(token68str), std::end(token68str), // At most 3 padding '='
std::begin(token68str), [](char c) { auto len = token68str.size() + 3;
switch (c) { auto iov = make_byte_ref(balloc, len + 1);
case '-': auto p = iov.base;
return '+';
case '_': p = std::transform(std::begin(token68str), std::end(token68str), p,
return '/'; [](char c) {
default: switch (c) {
return c; case '-':
} return '+';
}); case '_':
if (token68str.size() & 0x3) { return '/';
token68str.append(4 - (token68str.size() & 0x3), '='); default:
return c;
}
});
auto rem = token68str.size() & 0x3;
if (rem) {
p = std::fill_n(p, 4 - rem, '=');
} }
return;
*p = '\0';
return StringRef{iov.base, p};
} }
namespace { namespace {
@ -1119,29 +1147,30 @@ std::string dtos(double n) {
return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f; return utos(static_cast<int64_t>(n)) + "." + (f.size() == 1 ? "0" : "") + f;
} }
std::string make_http_hostport(const StringRef &host, uint16_t port) { StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
uint16_t port) {
if (port != 80 && port != 443) { if (port != 80 && port != 443) {
return make_hostport(host, port); return make_hostport(balloc, host, port);
} }
auto ipv6 = ipv6_numeric_addr(host.c_str()); auto ipv6 = ipv6_numeric_addr(host.c_str());
std::string hostport; auto iov = make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1);
hostport.resize(host.size() + (ipv6 ? 2 : 0)); auto p = iov.base;
auto p = &hostport[0];
if (ipv6) { if (ipv6) {
*p++ = '['; *p++ = '[';
} }
p = std::copy_n(host.c_str(), host.size(), p); p = std::copy(std::begin(host), std::end(host), p);
if (ipv6) { if (ipv6) {
*p++ = ']'; *p++ = ']';
} }
return hostport; *p = '\0';
return StringRef{iov.base, p};
} }
std::string make_hostport(const StringRef &host, uint16_t port) { std::string make_hostport(const StringRef &host, uint16_t port) {
@ -1169,6 +1198,34 @@ std::string make_hostport(const StringRef &host, uint16_t port) {
return hostport; return hostport;
} }
StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
uint16_t port) {
auto ipv6 = ipv6_numeric_addr(host.c_str());
auto serv = utos(port);
auto iov =
make_byte_ref(balloc, host.size() + (ipv6 ? 2 : 0) + 1 + serv.size());
auto p = iov.base;
if (ipv6) {
*p++ = '[';
}
p = std::copy(std::begin(host), std::end(host), p);
if (ipv6) {
*p++ = ']';
}
*p++ = ':';
p = std::copy(std::begin(serv), std::end(serv), p);
*p = '\0';
return StringRef{iov.base, p};
}
namespace { namespace {
void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) {
auto stop = std::min(first + 8, last); auto stop = std::min(first + 8, last);

View File

@ -129,11 +129,11 @@ std::string percent_decode(InputIt first, InputIt last) {
StringRef percent_decode(BlockAllocator &balloc, const StringRef &src); StringRef percent_decode(BlockAllocator &balloc, const StringRef &src);
// Percent encode |target| if character is not in token or '%'. // Percent encode |target| if character is not in token or '%'.
std::string percent_encode_token(const std::string &target); StringRef percent_encode_token(BlockAllocator &balloc, const StringRef &target);
// Returns quotedString version of |target|. Currently, this function // Returns quotedString version of |target|. Currently, this function
// just replace '"' with '\"'. // just replace '"' with '\"'.
std::string quote_string(const std::string &target); StringRef quote_string(BlockAllocator &balloc, const StringRef &target);
std::string format_hex(const unsigned char *s, size_t len); std::string format_hex(const unsigned char *s, size_t len);
@ -145,6 +145,8 @@ template <size_t N> std::string format_hex(const std::array<uint8_t, N> &s) {
return format_hex(s.data(), s.size()); return format_hex(s.data(), s.size());
} }
StringRef format_hex(BlockAllocator &balloc, const StringRef &s);
std::string http_date(time_t t); std::string http_date(time_t t);
// Returns given time |t| from epoch in Common Log format (e.g., // Returns given time |t| from epoch in Common Log format (e.g.,
@ -424,7 +426,8 @@ template <typename T> std::string utox(T n) {
} }
void to_token68(std::string &base64str); void to_token68(std::string &base64str);
void to_base64(std::string &token68str);
StringRef to_base64(BlockAllocator &balloc, const StringRef &token68str);
void show_candidates(const char *unkopt, option *options); void show_candidates(const char *unkopt, option *options);
@ -630,12 +633,16 @@ std::string format_duration(double t);
// Creates "host:port" string using given |host| and |port|. If // Creates "host:port" string using given |host| and |port|. If
// |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "[" // |host| is numeric IPv6 address (e.g., ::1), it is enclosed by "["
// and "]". If |port| is 80 or 443, port part is omitted. // and "]". If |port| is 80 or 443, port part is omitted.
std::string make_http_hostport(const StringRef &host, uint16_t port); StringRef make_http_hostport(BlockAllocator &balloc, const StringRef &host,
uint16_t port);
// Just like make_http_hostport(), but doesn't treat 80 and 443 // Just like make_http_hostport(), but doesn't treat 80 and 443
// specially. // specially.
std::string make_hostport(const StringRef &host, uint16_t port); std::string make_hostport(const StringRef &host, uint16_t port);
StringRef make_hostport(BlockAllocator &balloc, const StringRef &host,
uint16_t port);
// Dumps |src| of length |len| in the format similar to `hexdump -C`. // Dumps |src| of length |len| in the format similar to `hexdump -C`.
void hexdump(FILE *out, const uint8_t *src, size_t len); void hexdump(FILE *out, const uint8_t *src, size_t len);
@ -665,16 +672,17 @@ uint64_t get_uint64(const uint8_t *data);
int read_mime_types(std::map<std::string, std::string> &res, int read_mime_types(std::map<std::string, std::string> &res,
const char *filename); const char *filename);
template <typename Generator> // Fills random alpha and digit byte to the range [|first|, |last|).
std::string random_alpha_digit(Generator &gen, size_t len) { // Returns the one beyond the |last|.
std::string res; template <typename OutputIt, typename Generator>
res.reserve(len); OutputIt random_alpha_digit(OutputIt first, OutputIt last, Generator &gen) {
constexpr uint8_t s[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
std::uniform_int_distribution<> dis(0, 26 * 2 + 10 - 1); std::uniform_int_distribution<> dis(0, 26 * 2 + 10 - 1);
for (; len > 0; --len) { for (; first != last; ++first) {
res += "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"[dis( *first = s[dis(gen)];
gen)];
} }
return res; return first;
} }
template <typename OutputIterator, typename CharT, size_t N> template <typename OutputIterator, typename CharT, size_t N>

View File

@ -26,6 +26,7 @@
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <random>
#include <CUnit/CUnit.h> #include <CUnit/CUnit.h>
@ -113,13 +114,12 @@ void test_util_inp_strlower(void) {
} }
void test_util_to_base64(void) { void test_util_to_base64(void) {
std::string x = "AAA--B_"; BlockAllocator balloc(4096, 4096);
util::to_base64(x);
CU_ASSERT("AAA++B/=" == x);
x = "AAA--B_B"; CU_ASSERT("AAA++B/=" ==
util::to_base64(x); util::to_base64(balloc, StringRef::from_lit("AAA--B_")));
CU_ASSERT("AAA++B/B" == x); CU_ASSERT("AAA++B/B" ==
util::to_base64(balloc, StringRef::from_lit("AAA--B_B")));
} }
void test_util_to_token68(void) { void test_util_to_token68(void) {
@ -133,10 +133,15 @@ void test_util_to_token68(void) {
} }
void test_util_percent_encode_token(void) { void test_util_percent_encode_token(void) {
CU_ASSERT("h2" == util::percent_encode_token("h2")); BlockAllocator balloc(4096, 4096);
CU_ASSERT("h3~" == util::percent_encode_token("h3~")); CU_ASSERT("h2" ==
CU_ASSERT("100%25" == util::percent_encode_token("100%")); util::percent_encode_token(balloc, StringRef::from_lit("h2")));
CU_ASSERT("http%202" == util::percent_encode_token("http 2")); CU_ASSERT("h3~" ==
util::percent_encode_token(balloc, StringRef::from_lit("h3~")));
CU_ASSERT("100%25" ==
util::percent_encode_token(balloc, StringRef::from_lit("100%")));
CU_ASSERT("http%202" ==
util::percent_encode_token(balloc, StringRef::from_lit("http 2")));
} }
void test_util_percent_encode_path(void) { void test_util_percent_encode_path(void) {
@ -169,9 +174,12 @@ void test_util_percent_decode(void) {
} }
void test_util_quote_string(void) { void test_util_quote_string(void) {
CU_ASSERT("alpha" == util::quote_string("alpha")); BlockAllocator balloc(4096, 4096);
CU_ASSERT("" == util::quote_string("")); CU_ASSERT("alpha" ==
CU_ASSERT("\\\"alpha\\\"" == util::quote_string("\"alpha\"")); util::quote_string(balloc, StringRef::from_lit("alpha")));
CU_ASSERT("" == util::quote_string(balloc, StringRef::from_lit("")));
CU_ASSERT("\\\"alpha\\\"" ==
util::quote_string(balloc, StringRef::from_lit("\"alpha\"")));
} }
void test_util_utox(void) { void test_util_utox(void) {
@ -494,12 +502,15 @@ void test_util_parse_config_str_list(void) {
} }
void test_util_make_http_hostport(void) { void test_util_make_http_hostport(void) {
CU_ASSERT("localhost" == BlockAllocator balloc(4096, 4096);
util::make_http_hostport(StringRef::from_lit("localhost"), 80));
CU_ASSERT("localhost" == util::make_http_hostport(
balloc, StringRef::from_lit("localhost"), 80));
CU_ASSERT("[::1]" == CU_ASSERT("[::1]" ==
util::make_http_hostport(StringRef::from_lit("::1"), 443)); util::make_http_hostport(balloc, StringRef::from_lit("::1"), 443));
CU_ASSERT("localhost:3000" == CU_ASSERT(
util::make_http_hostport(StringRef::from_lit("localhost"), 3000)); "localhost:3000" ==
util::make_http_hostport(balloc, StringRef::from_lit("localhost"), 3000));
} }
void test_util_make_hostport(void) { void test_util_make_hostport(void) {
@ -507,6 +518,12 @@ void test_util_make_hostport(void) {
util::make_hostport(StringRef::from_lit("localhost"), 80)); util::make_hostport(StringRef::from_lit("localhost"), 80));
CU_ASSERT("[::1]:443" == CU_ASSERT("[::1]:443" ==
util::make_hostport(StringRef::from_lit("::1"), 443)); util::make_hostport(StringRef::from_lit("::1"), 443));
BlockAllocator balloc(4096, 4096);
CU_ASSERT("localhost:80" ==
util::make_hostport(balloc, StringRef::from_lit("localhost"), 80));
CU_ASSERT("[::1]:443" ==
util::make_hostport(balloc, StringRef::from_lit("::1"), 443));
} }
void test_util_strifind(void) { void test_util_strifind(void) {
@ -528,4 +545,27 @@ void test_util_strifind(void) {
StringRef::from_lit("http1"))); StringRef::from_lit("http1")));
} }
void test_util_random_alpha_digit(void) {
std::random_device rd;
std::mt19937 gen(rd());
std::array<uint8_t, 19> data;
auto p = util::random_alpha_digit(std::begin(data), std::end(data), gen);
CU_ASSERT(std::end(data) == p);
for (auto b : data) {
CU_ASSERT(('A' <= b && b <= 'Z') || ('a' <= b && b <= 'z') ||
('0' <= b && b <= '9'));
}
}
void test_util_format_hex(void) {
BlockAllocator balloc(4096, 4096);
CU_ASSERT("0ff0" ==
util::format_hex(balloc, StringRef::from_lit("\x0f\xf0")));
CU_ASSERT("" == util::format_hex(balloc, StringRef::from_lit("")));
}
} // namespace shrpx } // namespace shrpx

View File

@ -62,6 +62,8 @@ void test_util_parse_config_str_list(void);
void test_util_make_http_hostport(void); void test_util_make_http_hostport(void);
void test_util_make_hostport(void); void test_util_make_hostport(void);
void test_util_strifind(void); void test_util_strifind(void);
void test_util_random_alpha_digit(void);
void test_util_format_hex(void);
} // namespace shrpx } // namespace shrpx