nghttpx: Add --frontend-http2-window-size option, and its family

We added --frontend-http2-window-size,
--frontend-http2-connection-window-size, --backend-http2-window-size,
and --backend-http2-connection-window-size option to replace existing
*-bits options.  The old options are not flexible because they only
specify number of bits.  Now we can specify integer value, with
possible g, m, and k unit.  The old options are still available for
backend compatibility, but are deprecated.
This commit is contained in:
Tatsuhiro Tsujikawa 2016-09-09 21:05:37 +09:00
parent 27b250ac8e
commit f5a2f1da25
8 changed files with 198 additions and 81 deletions

View File

@ -138,6 +138,10 @@ OPTIONS = [
"no-server-rewrite", "no-server-rewrite",
"frontend-http2-optimize-write-buffer-size", "frontend-http2-optimize-write-buffer-size",
"frontend-http2-optimize-window-size", "frontend-http2-optimize-window-size",
"frontend-http2-window-size",
"frontend-http2-connection-window-size",
"backend-http2-window-size",
"backend-http2-connection-window-size",
] ]
LOGVARS = [ LOGVARS = [

View File

@ -1335,13 +1335,14 @@ void fill_default_config(Config *config) {
timeoutconf.settings = 10_s; timeoutconf.settings = 10_s;
} }
// window bits for HTTP/2 and SPDY upstream connection per // window size for HTTP/2 and SPDY upstream connection per stream.
// stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note // 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note that
// that SPDY/3 default is 64KiB. // SPDY/3 default is 64KiB.
upstreamconf.window_bits = 16; upstreamconf.window_size = 64_k - 1;
// HTTP/2 SPDY/3.1 has connection-level flow control. The default // HTTP/2 and SPDY/3.1 has connection-level flow control. The
// window size for HTTP/2 is 64KiB - 1. SPDY/3's default is 64KiB // default window size for HTTP/2 is 64KiB - 1. SPDY/3's default
upstreamconf.connection_window_bits = 16; // is 64KiB
upstreamconf.connection_window_size = 64_k - 1;
upstreamconf.max_concurrent_streams = 100; upstreamconf.max_concurrent_streams = 100;
nghttp2_option_new(&upstreamconf.option); nghttp2_option_new(&upstreamconf.option);
@ -1362,8 +1363,8 @@ void fill_default_config(Config *config) {
timeoutconf.settings = 10_s; timeoutconf.settings = 10_s;
} }
downstreamconf.window_bits = 16; downstreamconf.window_size = 64_k - 1;
downstreamconf.connection_window_bits = 30; downstreamconf.connection_window_size = (1u << 31) - 1;
downstreamconf.max_concurrent_streams = 100; downstreamconf.max_concurrent_streams = 100;
nghttp2_option_new(&downstreamconf.option); nghttp2_option_new(&downstreamconf.option);
@ -1997,26 +1998,25 @@ HTTP/2 and SPDY:
concurrent requests are set by a remote server. concurrent requests are set by a remote server.
Default: )" Default: )"
<< get_config()->http2.downstream.max_concurrent_streams << R"( << get_config()->http2.downstream.max_concurrent_streams << R"(
--frontend-http2-window-bits=<N> --frontend-http2-window-size=<SIZE>
Sets the per-stream initial window size of HTTP/2 SPDY Sets the per-stream initial window size of HTTP/2 and
frontend connection. For HTTP/2, the size is 2**<N>-1. SPDY frontend connection.
For SPDY, the size is 2**<N>. Default: )" << get_config()->http2.upstream.window_size << R"(
Default: )" << get_config()->http2.upstream.window_bits << R"( --frontend-http2-connection-window-size=<SIZE>
--frontend-http2-connection-window-bits=<N>
Sets the per-connection window size of HTTP/2 and SPDY Sets the per-connection window size of HTTP/2 and SPDY
frontend connection. For HTTP/2, the size is frontend connection. For SPDY connection, the value
2**<N>-1. For SPDY, the size is 2**<N>. less than 64KiB is rounded up to 64KiB.
Default: )" << get_config()->http2.upstream.connection_window_bits Default: )" << get_config()->http2.upstream.connection_window_size
<< R"( << R"(
--backend-http2-window-bits=<N> --backend-http2-window-size=<SIZE>
Sets the initial window size of HTTP/2 backend Sets the initial window size of HTTP/2 backend
connection to 2**<N>-1. connection.
Default: )" << get_config()->http2.downstream.window_bits << R"( Default: )" << get_config()->http2.downstream.window_size << R"(
--backend-http2-connection-window-bits=<N> --backend-http2-connection-window-size=<SIZE>
Sets the per-connection window size of HTTP/2 backend Sets the per-connection window size of HTTP/2 backend
connection to 2**<N>-1. connection.
Default: )" Default: )"
<< get_config()->http2.downstream.connection_window_bits << R"( << get_config()->http2.downstream.connection_window_size << R"(
--http2-no-cookie-crumbling --http2-no-cookie-crumbling
Don't crumble cookie header field. Don't crumble cookie header field.
--padding=<N> --padding=<N>
@ -2048,9 +2048,9 @@ HTTP/2 and SPDY:
automatically adjusts connection window size based on automatically adjusts connection window size based on
TCP receiving window size. The maximum window size is TCP receiving window size. The maximum window size is
capped by the value specified by capped by the value specified by
--frontend-http2-connection-window-bits. Since the --frontend-http2-connection-window-size. Since the
stream is subject to stream level window size, it should stream is subject to stream level window size, it should
be adjusted using --frontend-http2-window-bits option as be adjusted using --frontend-http2-window-size option as
well. This option is only effective on recent Linux well. This option is only effective on recent Linux
platform. platform.
@ -2867,6 +2867,14 @@ int main(int argc, char **argv) {
no_argument, &flag, 130}, no_argument, &flag, 130},
{SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE.c_str(), no_argument, {SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE.c_str(), no_argument,
&flag, 131}, &flag, 131},
{SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE.c_str(), required_argument, &flag,
132},
{SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE.c_str(),
required_argument, &flag, 133},
{SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE.c_str(), required_argument, &flag,
134},
{SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE.c_str(),
required_argument, &flag, 135},
{nullptr, 0, nullptr, 0}}; {nullptr, 0, nullptr, 0}};
int option_index = 0; int option_index = 0;
@ -3484,6 +3492,26 @@ int main(int argc, char **argv) {
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE, cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE,
StringRef::from_lit("yes")); StringRef::from_lit("yes"));
break; break;
case 132:
// --frontend-http2-window-size
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE,
StringRef{optarg});
break;
case 133:
// --frontend-http2-connection-window-size
cmdcfgs.emplace_back(SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE,
StringRef{optarg});
break;
case 134:
// --backend-http2-window-size
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE,
StringRef{optarg});
break;
case 135:
// --backend-http2-connection-window-size
cmdcfgs.emplace_back(SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE,
StringRef{optarg});
break;
default: default:
break; break;
} }

View File

@ -320,6 +320,13 @@ int parse_uint_with_unit(T *dest, const StringRef &opt,
return -1; return -1;
} }
if (std::numeric_limits<T>::max() < n) {
LOG(ERROR) << opt
<< ": too large. The value should be less than or equal to "
<< std::numeric_limits<T>::max();
return -1;
}
*dest = n; *dest = n;
return 0; return 0;
@ -1482,6 +1489,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 25: case 25:
switch (name[24]) { switch (name[24]) {
case 'e':
if (util::strieq_l("backend-http2-window-siz", name, 24)) {
return SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE;
}
break;
case 'g': case 'g':
if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) { if (util::strieq_l("http2-no-cookie-crumblin", name, 24)) {
return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING; return SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING;
@ -1499,6 +1511,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 26: case 26:
switch (name[25]) { switch (name[25]) {
case 'e':
if (util::strieq_l("frontend-http2-window-siz", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE;
}
break;
case 's': case 's':
if (util::strieq_l("frontend-http2-window-bit", name, 25)) { if (util::strieq_l("frontend-http2-window-bit", name, 25)) {
return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS; return SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS;
@ -1657,6 +1674,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break; break;
case 36: case 36:
switch (name[35]) { switch (name[35]) {
case 'e':
if (util::strieq_l("backend-http2-connection-window-siz", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE;
}
break;
case 'r': case 'r':
if (util::strieq_l("backend-http2-connections-per-worke", name, 35)) { if (util::strieq_l("backend-http2-connections-per-worke", name, 35)) {
return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER; return SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER;
@ -1675,6 +1697,9 @@ int option_lookup_token(const char *name, size_t namelen) {
case 37: case 37:
switch (name[36]) { switch (name[36]) {
case 'e': case 'e':
if (util::strieq_l("frontend-http2-connection-window-siz", name, 36)) {
return SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE;
}
if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) { if (util::strieq_l("tls-session-cache-memcached-cert-fil", name, 36)) {
return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE; return SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED_CERT_FILE;
} }
@ -1964,12 +1989,16 @@ int parse_config(Config *config, int optid, const StringRef &opt,
optarg); optarg);
case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS: case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS:
case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: { case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: {
size_t *resp; LOG(WARN) << opt << ": deprecated. Use "
<< (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS
? SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE
: SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE);
int32_t *resp;
if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) { if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) {
resp = &config->http2.upstream.window_bits; resp = &config->http2.upstream.window_size;
} else { } else {
resp = &config->http2.downstream.window_bits; resp = &config->http2.downstream.window_size;
} }
errno = 0; errno = 0;
@ -1986,18 +2015,25 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
*resp = n; // Make 16 bits to the HTTP/2 default 64KiB - 1. This is the same
// behaviour of previous code. For SPDY, we adjust this value in
// SpdyUpstream to look like the SPDY default.
*resp = (1 << n) - 1;
return 0; return 0;
} }
case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS: case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS:
case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: { case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS: {
size_t *resp; LOG(WARN) << opt << ": deprecated. Use "
<< (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS
? SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE
: SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE);
int32_t *resp;
if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) { if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) {
resp = &config->http2.upstream.connection_window_bits; resp = &config->http2.upstream.connection_window_size;
} else { } else {
resp = &config->http2.downstream.connection_window_bits; resp = &config->http2.downstream.connection_window_size;
} }
errno = 0; errno = 0;
@ -2014,7 +2050,7 @@ int parse_config(Config *config, int optid, const StringRef &opt,
return -1; return -1;
} }
*resp = n; *resp = (1 << n) - 1;
return 0; return 0;
} }
@ -2707,6 +2743,34 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE: case SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE:
config->http2.upstream.optimize_window_size = util::strieq_l("yes", optarg); config->http2.upstream.optimize_window_size = util::strieq_l("yes", optarg);
return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE:
if (parse_uint_with_unit(&config->http2.upstream.window_size, opt,
optarg) != 0) {
return -1;
}
return 0;
case SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE:
if (parse_uint_with_unit(&config->http2.upstream.connection_window_size,
opt, optarg) != 0) {
return -1;
}
return 0;
case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE:
if (parse_uint_with_unit(&config->http2.downstream.window_size, opt,
optarg) != 0) {
return -1;
}
return 0;
case SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE:
if (parse_uint_with_unit(&config->http2.downstream.connection_window_size,
opt, optarg) != 0) {
return -1;
}
return 0; return 0;
case SHRPX_OPTID_CONF: case SHRPX_OPTID_CONF:
LOG(WARN) << "conf: ignored"; LOG(WARN) << "conf: ignored";

View File

@ -291,6 +291,14 @@ constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE =
StringRef::from_lit("frontend-http2-optimize-write-buffer-size"); StringRef::from_lit("frontend-http2-optimize-write-buffer-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE = constexpr auto SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE =
StringRef::from_lit("frontend-http2-optimize-window-size"); StringRef::from_lit("frontend-http2-optimize-window-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP2_WINDOW_SIZE =
StringRef::from_lit("frontend-http2-window-size");
constexpr auto SHRPX_OPT_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE =
StringRef::from_lit("frontend-http2-connection-window-size");
constexpr auto SHRPX_OPT_BACKEND_HTTP2_WINDOW_SIZE =
StringRef::from_lit("backend-http2-window-size");
constexpr auto SHRPX_OPT_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE =
StringRef::from_lit("backend-http2-connection-window-size");
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8; constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
@ -587,8 +595,8 @@ struct Http2Config {
nghttp2_option *option; nghttp2_option *option;
nghttp2_option *alt_mode_option; nghttp2_option *alt_mode_option;
nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks *callbacks;
size_t window_bits; int32_t window_size;
size_t connection_window_bits; int32_t connection_window_size;
size_t max_concurrent_streams; size_t max_concurrent_streams;
bool optimize_write_buffer_size; bool optimize_write_buffer_size;
bool optimize_window_size; bool optimize_window_size;
@ -599,8 +607,8 @@ struct Http2Config {
} timeout; } timeout;
nghttp2_option *option; nghttp2_option *option;
nghttp2_session_callbacks *callbacks; nghttp2_session_callbacks *callbacks;
size_t window_bits; int32_t window_size;
size_t connection_window_bits; int32_t connection_window_size;
size_t max_concurrent_streams; size_t max_concurrent_streams;
} downstream; } downstream;
struct { struct {
@ -785,10 +793,12 @@ enum {
SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST, SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_HOST,
SHRPX_OPTID_BACKEND_HTTP1_TLS, SHRPX_OPTID_BACKEND_HTTP1_TLS,
SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS, SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_BITS,
SHRPX_OPTID_BACKEND_HTTP2_CONNECTION_WINDOW_SIZE,
SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER, SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER,
SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_BACKEND_HTTP2_MAX_CONCURRENT_STREAMS,
SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT, SHRPX_OPTID_BACKEND_HTTP2_SETTINGS_TIMEOUT,
SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS, SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS,
SHRPX_OPTID_BACKEND_HTTP2_WINDOW_SIZE,
SHRPX_OPTID_BACKEND_IPV4, SHRPX_OPTID_BACKEND_IPV4,
SHRPX_OPTID_BACKEND_IPV6, SHRPX_OPTID_BACKEND_IPV6,
SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT, SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT,
@ -821,6 +831,7 @@ enum {
SHRPX_OPTID_FRONTEND, SHRPX_OPTID_FRONTEND,
SHRPX_OPTID_FRONTEND_FRAME_DEBUG, SHRPX_OPTID_FRONTEND_FRAME_DEBUG,
SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS, SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS,
SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER, SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER,
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER, SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER,
SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS, SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS,
@ -829,6 +840,7 @@ enum {
SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT, SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS, SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_SIZE,
SHRPX_OPTID_FRONTEND_NO_TLS, SHRPX_OPTID_FRONTEND_NO_TLS,
SHRPX_OPTID_FRONTEND_READ_TIMEOUT, SHRPX_OPTID_FRONTEND_READ_TIMEOUT,
SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT, SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT,

View File

@ -1590,7 +1590,7 @@ int Http2Session::connection_made() {
entry[0].value = http2conf.downstream.max_concurrent_streams; entry[0].value = http2conf.downstream.max_concurrent_streams;
entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
entry[1].value = (1 << http2conf.downstream.window_bits) - 1; entry[1].value = http2conf.downstream.window_size;
if (http2conf.no_server_push || get_config()->http2_proxy) { if (http2conf.no_server_push || get_config()->http2_proxy) {
entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
@ -1604,15 +1604,12 @@ int Http2Session::connection_made() {
return -1; return -1;
} }
if (http2conf.downstream.connection_window_bits != 16) { rv = nghttp2_session_set_local_window_size(
int32_t window_size = session_, NGHTTP2_FLAG_NONE, 0,
(1 << http2conf.downstream.connection_window_bits) - 1; http2conf.downstream.connection_window_size);
rv = nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0,
window_size);
if (rv != 0) { if (rv != 0) {
return -1; return -1;
} }
}
auto must_terminate = auto must_terminate =
addr_->tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl); addr_->tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl);

View File

@ -947,7 +947,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
if (faddr->alt_mode) { if (faddr->alt_mode) {
entry[1].value = (1u << 31) - 1; entry[1].value = (1u << 31) - 1;
} else { } else {
entry[1].value = (1 << http2conf.upstream.window_bits) - 1; entry[1].value = http2conf.upstream.window_size;
} }
rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(),
@ -957,21 +957,18 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
<< nghttp2_strerror(rv); << nghttp2_strerror(rv);
} }
int32_t window_bits = auto window_size = faddr->alt_mode
faddr->alt_mode ? 31 : http2conf.upstream.optimize_window_size ? std::numeric_limits<int32_t>::max()
? 16 : http2conf.upstream.connection_window_size;
: http2conf.upstream.connection_window_bits;
if (window_bits != 16) {
int32_t window_size = (1u << window_bits) - 1;
rv = nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0, rv = nghttp2_session_set_local_window_size(session_, NGHTTP2_FLAG_NONE, 0,
window_size); window_size);
if (rv != 0) { if (rv != 0) {
ULOG(ERROR, this) << "nghttp2_submit_window_update() returned error: " ULOG(ERROR, this)
<< "nghttp2_session_set_local_window_size() returned error: "
<< nghttp2_strerror(rv); << nghttp2_strerror(rv);
} }
}
// We wait for SETTINGS ACK at least 10 seconds. // We wait for SETTINGS ACK at least 10 seconds.
ev_timer_init(&settings_timer_, settings_timeout_cb, ev_timer_init(&settings_timer_, settings_timeout_cb,
@ -1073,10 +1070,8 @@ int Http2Upstream::on_write() {
if (http2conf.upstream.optimize_window_size) { if (http2conf.upstream.optimize_window_size) {
auto faddr = handler_->get_upstream_addr(); auto faddr = handler_->get_upstream_addr();
if (!faddr->alt_mode) { if (!faddr->alt_mode) {
int32_t window_size = auto window_size = std::min(http2conf.upstream.connection_window_size,
(1u << http2conf.upstream.connection_window_bits) - 1; static_cast<int32_t>(hint.rwin * 2));
window_size =
std::min(static_cast<uint32_t>(window_size), hint.rwin * 2);
rv = nghttp2_session_set_local_window_size( rv = nghttp2_session_set_local_window_size(
session_, NGHTTP2_FLAG_NONE, 0, window_size); session_, NGHTTP2_FLAG_NONE, 0, window_size);

View File

@ -53,6 +53,28 @@ namespace {
constexpr size_t MAX_BUFFER_SIZE = 32_k; constexpr size_t MAX_BUFFER_SIZE = 32_k;
} // namespace } // namespace
namespace {
int32_t get_connection_window_size() {
return std::max(get_config()->http2.upstream.connection_window_size,
static_cast<int32_t>(64_k));
}
} // namespace
namespace {
int32_t get_window_size() {
auto n = get_config()->http2.upstream.window_size;
// 65535 is the default window size of HTTP/2. OTOH, the default
// window size of SPDY is 65536. The configuration defaults to
// HTTP/2, so if we have 65535, we use 65536 for SPDY.
if (n == 65535) {
return 64_k;
}
return n;
}
} // namespace
namespace { namespace {
ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len, ssize_t send_callback(spdylay_session *session, const uint8_t *data, size_t len,
int flags, void *user_data) { int flags, void *user_data) {
@ -402,33 +424,27 @@ void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags,
return; return;
} }
auto &http2conf = get_config()->http2;
// If connection-level window control is not enabled (e.g, // If connection-level window control is not enabled (e.g,
// spdy/3), spdylay_session_get_recv_data_length() is always // spdy/3), spdylay_session_get_recv_data_length() is always
// returns 0. // returns 0.
if (spdylay_session_get_recv_data_length(session) > if (spdylay_session_get_recv_data_length(session) >
std::max(SPDYLAY_INITIAL_WINDOW_SIZE, std::max(SPDYLAY_INITIAL_WINDOW_SIZE, get_connection_window_size())) {
1 << http2conf.upstream.connection_window_bits)) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Flow control error on connection: " ULOG(INFO, upstream) << "Flow control error on connection: "
<< "recv_window_size=" << "recv_window_size="
<< spdylay_session_get_recv_data_length(session) << spdylay_session_get_recv_data_length(session)
<< ", window_size=" << ", window_size=" << get_connection_window_size();
<< (1 << http2conf.upstream.connection_window_bits);
} }
spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
return; return;
} }
if (spdylay_session_get_stream_recv_data_length(session, stream_id) > if (spdylay_session_get_stream_recv_data_length(session, stream_id) >
std::max(SPDYLAY_INITIAL_WINDOW_SIZE, std::max(SPDYLAY_INITIAL_WINDOW_SIZE, get_window_size())) {
1 << http2conf.upstream.window_bits)) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
ULOG(INFO, upstream) << "Flow control error: recv_window_size=" ULOG(INFO, upstream) << "Flow control error: recv_window_size="
<< spdylay_session_get_stream_recv_data_length( << spdylay_session_get_stream_recv_data_length(
session, stream_id) session, stream_id)
<< ", initial_window_size=" << ", initial_window_size=" << get_window_size();
<< (1 << http2conf.upstream.window_bits);
} }
upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR); upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR);
return; return;
@ -574,16 +590,17 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)
// going to be deprecated in the future, and the default stream // going to be deprecated in the future, and the default stream
// window is large enough for API request body (64KiB), we don't // window is large enough for API request body (64KiB), we don't
// expand window size depending on the options. // expand window size depending on the options.
int32_t initial_window_size;
if (version >= SPDYLAY_PROTO_SPDY3 && !faddr->alt_mode) { if (version >= SPDYLAY_PROTO_SPDY3 && !faddr->alt_mode) {
int val = 1; int val = 1;
flow_control_ = true; flow_control_ = true;
initial_window_size_ = 1 << http2conf.upstream.window_bits; initial_window_size = get_window_size();
rv = spdylay_session_set_option( rv = spdylay_session_set_option(
session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val)); session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val));
assert(rv == 0); assert(rv == 0);
} else { } else {
flow_control_ = false; flow_control_ = false;
initial_window_size_ = 0; initial_window_size = 0;
} }
// TODO Maybe call from outside? // TODO Maybe call from outside?
std::array<spdylay_settings_entry, 2> entry; std::array<spdylay_settings_entry, 2> entry;
@ -595,7 +612,7 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)
if (flow_control_) { if (flow_control_) {
++num_entry; ++num_entry;
entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE;
entry[1].value = initial_window_size_; entry[1].value = initial_window_size;
entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; entry[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE;
} }
@ -603,10 +620,11 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler)
entry.data(), num_entry); entry.data(), num_entry);
assert(rv == 0); assert(rv == 0);
auto connection_window_size = get_connection_window_size();
if (flow_control_ && version >= SPDYLAY_PROTO_SPDY3_1 && if (flow_control_ && version >= SPDYLAY_PROTO_SPDY3_1 &&
http2conf.upstream.connection_window_bits > 16) { connection_window_size > 64_k) {
int32_t delta = (1 << http2conf.upstream.connection_window_bits) - int32_t delta = connection_window_size - SPDYLAY_INITIAL_WINDOW_SIZE;
SPDYLAY_INITIAL_WINDOW_SIZE;
rv = spdylay_submit_window_update(session_, 0, delta); rv = spdylay_submit_window_update(session_, 0, delta);
assert(rv == 0); assert(rv == 0);
} }

View File

@ -103,7 +103,6 @@ private:
DownstreamQueue downstream_queue_; DownstreamQueue downstream_queue_;
ClientHandler *handler_; ClientHandler *handler_;
spdylay_session *session_; spdylay_session *session_;
int32_t initial_window_size_;
bool flow_control_; bool flow_control_;
}; };