</usage>
</directivesynopsis>
- <directivesynopsis>
- <name>H2Timeout</name>
- <description>Timeout (in seconds) for HTTP/2 connections</description>
- <syntax>H2Timeout seconds</syntax>
- <default>H2Timeout 5</default>
- <contextlist>
- <context>server config</context>
- <context>virtual host</context>
- </contextlist>
- <compatibility>Available in version 2.4.19 and later.</compatibility>
-
- <usage>
- <p>
- This directive sets the timeout for read/write operations on
- connections where HTTP/2 is negotiated. This can be used server wide or for specific
- <directive module="core" type="section">VirtualHost</directive>s.
- </p>
- <p>
- This directive is similar to the
- <directive module="core" type="section">Timeout</directive>, but
- applies only to HTTP/2 connections.
- </p>
- <p>
- A value of 0 enforces no timeout.
- </p>
- </usage>
- </directivesynopsis>
-
- <directivesynopsis>
- <name>H2KeepAliveTimeout</name>
- <description>Timeout (in seconds) for idle HTTP/2 connections</description>
- <syntax>H2KeepAliveTimeout seconds</syntax>
- <contextlist>
- <context>server config</context>
- <context>virtual host</context>
- </contextlist>
- <compatibility>Available in version 2.4.19 and later.</compatibility>
-
- <usage>
- <p>
- This directive sets the timeout for read/write operations on
- idle connections where HTTP/2 is negotiated. This can be used server wide or for specific
- <directive module="core" type="section">VirtualHost</directive>s.
- </p>
- <p>
- This directive is similar to the
- <directive module="core" type="section">KeepAliveTimeout</directive>, but
- applies only to HTTP/2 connections. A HTTP/2 connection is considered
- idle when no streams are open, e.g. no requests are ongoing.
- </p>
- <p>
- By default, for non-async MPMs (prefork, worker) the keepalive timeout
- will be the same as H2Timeout. For async MPMs, the keepalive handling for
- HTTP/1 connections applies as no special action is taken.
- </p>
- </usage>
- </directivesynopsis>
-
- <directivesynopsis>
- <name>H2StreamTimeout</name>
- <description>Timeout (in seconds) for idle HTTP/2 connections</description>
- <syntax>H2StreamTimeout seconds</syntax>
- <default>H2StreamTimeout 0</default>
- <contextlist>
- <context>server config</context>
- <context>virtual host</context>
- </contextlist>
- <compatibility>Available in version 2.4.19 and later.</compatibility>
-
- <usage>
- <p>
- This directive sets the timeout for read/write operations on
- HTTP/2 streams, e.g. individual requests. This can be used server wide or for specific
- <directive module="core" type="section">VirtualHost</directive>s.
- </p>
- <p>
- Due to the nature of HTTP/2, which sends multiple requests over a single
- connection and has priority scheduling, individual streams might not
- see input for much longer times than HTTP/1.1 requests would.
- </p>
- <p>
- A value of 0 enforces no timeout, so could wait on chances to receive
- input or write data indefinitely. This expose a server to
- risks of thread exhaustion.
- </p>
- <p>
- Depending on your handling of pushed streams,
- priorities and general responsiveness, a site might need to increase
- this value. For example, if you PUSH a large resource <em>before</em>
- the requested one, the initial stream will not write until the
- pushed resource is fully sent.
- </p>
- </usage>
- </directivesynopsis>
-
</modulesynopsis>
1, /* TLS cooldown secs */
1, /* HTTP/2 server push enabled */
NULL, /* map of content-type to priorities */
- -1, /* connection timeout */
- -1, /* keepalive timeout */
- 0, /* stream timeout */
256, /* push diary size */
};
conf->tls_cooldown_secs = DEF_VAL;
conf->h2_push = DEF_VAL;
conf->priorities = NULL;
- conf->h2_timeout = DEF_VAL;
- conf->h2_keepalive = DEF_VAL;
- conf->h2_stream_timeout = DEF_VAL;
conf->push_diary_size = DEF_VAL;
return conf;
else {
n->priorities = add->priorities? add->priorities : base->priorities;
}
- n->h2_timeout = H2_CONFIG_GET(add, base, h2_timeout);
- n->h2_keepalive = H2_CONFIG_GET(add, base, h2_keepalive);
- n->h2_stream_timeout = H2_CONFIG_GET(add, base, h2_stream_timeout);
n->push_diary_size = H2_CONFIG_GET(add, base, push_diary_size);
return n;
return H2_CONFIG_GET(conf, &defconf, tls_cooldown_secs);
case H2_CONF_PUSH:
return H2_CONFIG_GET(conf, &defconf, h2_push);
- case H2_CONF_TIMEOUT_SECS:
- return H2_CONFIG_GET(conf, &defconf, h2_timeout);
- case H2_CONF_KEEPALIVE_SECS:
- return H2_CONFIG_GET(conf, &defconf, h2_keepalive);
- case H2_CONF_STREAM_TIMEOUT_SECS:
- return H2_CONFIG_GET(conf, &defconf, h2_stream_timeout);
case H2_CONF_PUSH_DIARY_SIZE:
return H2_CONFIG_GET(conf, &defconf, push_diary_size);
default:
return NULL;
}
-static const char *h2_conf_set_timeout(cmd_parms *parms,
- void *arg, const char *value)
-{
- h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
- (void)arg;
- cfg->h2_timeout = (int)apr_atoi64(value);
- if (cfg->h2_timeout < 0) {
- return "value must be >= 0";
- }
- return NULL;
-}
-
-static const char *h2_conf_set_keepalive(cmd_parms *parms,
- void *arg, const char *value)
-{
- h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
- (void)arg;
- cfg->h2_keepalive = (int)apr_atoi64(value);
- if (cfg->h2_keepalive < 0) {
- return "value must be >= 0";
- }
- return NULL;
-}
-
-static const char *h2_conf_set_stream_timeout(cmd_parms *parms,
- void *arg, const char *value)
-{
- h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
- (void)arg;
- cfg->h2_stream_timeout = (int)apr_atoi64(value);
- if (cfg->h2_stream_timeout < 0) {
- return "value must be >= 0";
- }
- return NULL;
-}
-
static const char *h2_conf_set_push_diary_size(cmd_parms *parms,
void *arg, const char *value)
{
RSRC_CONF, "off to disable HTTP/2 server push"),
AP_INIT_TAKE23("H2PushPriority", h2_conf_add_push_priority, NULL,
RSRC_CONF, "define priority of PUSHed resources per content type"),
- AP_INIT_TAKE1("H2Timeout", h2_conf_set_timeout, NULL,
- RSRC_CONF, "read/write timeout (seconds) for HTTP/2 connections"),
- AP_INIT_TAKE1("H2KeepAliveTimeout", h2_conf_set_keepalive, NULL,
- RSRC_CONF, "timeout (seconds) for idle HTTP/2 connections, no streams open"),
- AP_INIT_TAKE1("H2StreamTimeout", h2_conf_set_stream_timeout, NULL,
- RSRC_CONF, "read/write timeout (seconds) for HTTP/2 streams"),
AP_INIT_TAKE1("H2PushDiarySize", h2_conf_set_push_diary_size, NULL,
RSRC_CONF, "size of push diary"),
AP_END_CMD
H2_CONF_TLS_WARMUP_SIZE,
H2_CONF_TLS_COOLDOWN_SECS,
H2_CONF_PUSH,
- H2_CONF_TIMEOUT_SECS,
- H2_CONF_KEEPALIVE_SECS,
- H2_CONF_STREAM_TIMEOUT_SECS,
H2_CONF_PUSH_DIARY_SIZE,
} h2_config_var_t;
int h2_push; /* if HTTP/2 server push is enabled */
struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
- int h2_timeout; /* timeout for http/2 connections */
- int h2_keepalive; /* timeout for idle connections, no streams */
- int h2_stream_timeout; /* timeout for http/2 streams, slave connections */
int push_diary_size; /* # of entries in push diary */
} h2_config;
}
-void h2_io_signal_init(h2_io *io, h2_io_op op, int timeout_secs, apr_thread_cond_t *cond)
+void h2_io_signal_init(h2_io *io, h2_io_op op, apr_interval_time_t timeout,
+ apr_thread_cond_t *cond)
{
io->timed_op = op;
io->timed_cond = cond;
- if (timeout_secs > 0) {
- io->timeout_at = apr_time_now() + apr_time_from_sec(timeout_secs);
+ if (timeout > 0) {
+ io->timeout_at = apr_time_now() + timeout;
}
else {
io->timeout_at = 0;
int h2_io_out_has_data(h2_io *io);
void h2_io_signal(h2_io *io, h2_io_op op);
-void h2_io_signal_init(h2_io *io, h2_io_op op, int timeout_secs,
+void h2_io_signal_init(h2_io *io, h2_io_op op, apr_interval_time_t timeout,
struct apr_thread_cond_t *cond);
void h2_io_signal_exit(h2_io *io);
apr_status_t h2_io_signal_wait(struct h2_mplx *m, h2_io *io);
* than protecting a shared h2_session one with an own lock.
*/
h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent,
- const h2_config *conf,
+ const h2_config *conf,
+ apr_interval_time_t stream_timeout,
h2_workers *workers)
{
apr_status_t status = APR_SUCCESS;
m->stream_ios = h2_io_set_create(m->pool);
m->ready_ios = h2_io_set_create(m->pool);
m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM);
+ m->stream_timeout = stream_timeout;
m->workers = workers;
m->tx_handles_reserved = 0;
m->tx_chunk_size = 4;
-
- m->stream_timeout_secs = h2_config_geti(conf, H2_CONF_STREAM_TIMEOUT_SECS);
}
return m;
}
if (io && !io->orphaned) {
H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_pre");
- h2_io_signal_init(io, H2_IO_READ, m->stream_timeout_secs, iowait);
+ h2_io_signal_init(io, H2_IO_READ, m->stream_timeout, iowait);
status = h2_io_in_read(io, bb, -1, trailers);
while (APR_STATUS_IS_EAGAIN(status)
&& !is_aborted(m, &status)
&m->tx_handles_reserved);
/* Wait for data to drain until there is room again or
* stream timeout expires */
- h2_io_signal_init(io, H2_IO_WRITE, m->stream_timeout_secs, iowait);
+ h2_io_signal_init(io, H2_IO_WRITE, m->stream_timeout, iowait);
while (status == APR_SUCCESS
&& !APR_BRIGADE_EMPTY(bb)
&& iowait
struct apr_thread_cond_t *join_wait;
apr_size_t stream_max_mem;
- int stream_timeout_secs;
+ apr_interval_time_t stream_timeout;
apr_pool_t *spare_pool; /* spare pool, ready for next io */
struct h2_workers *workers;
*/
h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *master,
const struct h2_config *conf,
+ apr_interval_time_t stream_timeout,
struct h2_workers *workers);
/**
session->max_stream_count = h2_config_geti(session->config, H2_CONF_MAX_STREAMS);
session->max_stream_mem = h2_config_geti(session->config, H2_CONF_STREAM_MAX_MEM);
- session->timeout_secs = h2_config_geti(session->config, H2_CONF_TIMEOUT_SECS);
- if (session->timeout_secs <= 0) {
- session->timeout_secs = apr_time_sec(session->s->timeout);
- }
- session->keepalive_secs = h2_config_geti(session->config, H2_CONF_KEEPALIVE_SECS);
- if (session->keepalive_secs <= 0) {
- session->keepalive_secs = apr_time_sec(session->s->keep_alive_timeout);
- }
-
+
status = apr_thread_cond_create(&session->iowait, session->pool);
if (status != APR_SUCCESS) {
return NULL;
session->streams = h2_stream_set_create(session->pool, session->max_stream_count);
session->workers = workers;
- session->mplx = h2_mplx_create(c, session->pool, session->config, workers);
+ session->mplx = h2_mplx_create(c, session->pool, session->config,
+ session->s->timeout, workers);
h2_mplx_set_consumed_cb(session->mplx, update_window, session);
if (APLOGcdebug(c)) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03200)
- "session(%ld) created, timeout=%d, keepalive_timeout=%d, "
- "max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
- session->id, session->timeout_secs, session->keepalive_secs,
- (int)session->max_stream_count, (int)session->max_stream_mem,
- session->push_diary->dtype,
- (int)session->push_diary->N);
+ "session(%ld) created, max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
+ session->id, (int)session->max_stream_count, (int)session->max_stream_mem,
+ session->push_diary->dtype, (int)session->push_diary->N);
}
}
return session;
/* When we have no streams, no task event are possible,
* switch to blocking reads */
transit(session, "no io", H2_SESSION_ST_IDLE);
- session->keepalive_remain = session->keepalive_secs;
+ session->keepalive_until = apr_time_now() + session->s->keep_alive_timeout;
}
}
else if (!h2_stream_set_has_unsubmitted(session->streams)
* new output data from task processing,
* switch to blocking reads. */
transit(session, "no io", H2_SESSION_ST_IDLE);
- session->keepalive_remain = session->keepalive_secs;
+ session->keepalive_until = apr_time_now() + session->s->keep_alive_timeout;
}
else {
/* Unable to do blocking reads, as we wait on events from
dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
}
else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
- if (--session->keepalive_remain <= 0) {
+ if (apr_time_now() > session->keepalive_until) {
dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
}
else {
status = APR_EAGAIN;
- ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, c,
- "h2_session(%ld): idle, conn->timeout=%d/%d"
- ", conn->keepalive=%d/%d",
- session->id,
- (int)session->timeout_secs,
- (int)apr_time_sec(session->s->timeout),
- (int)session->keepalive_secs,
- (int)apr_time_sec(session->s->keep_alive_timeout));
goto out;
}
}
/* nothing to read */
}
else if (APR_STATUS_IS_TIMEUP(status)) {
- if (--session->keepalive_remain <= 0) {
+ if (apr_time_now() > session->keepalive_until) {
dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
}
/* continue keepalive handling */
case H2_SESSION_ST_LOCAL_SHUTDOWN:
case H2_SESSION_ST_REMOTE_SHUTDOWN:
if (nghttp2_session_want_read(session->ngh2)) {
- h2_filter_cin_timeout_set(session->cin, session->timeout_secs);
+ h2_filter_cin_timeout_set(session->cin, session->s->timeout);
status = h2_session_read(session, 0, 10);
if (status == APR_SUCCESS) {
have_read = 1;
session->start_wait = apr_time_now();
update_child_status(session, SERVER_BUSY_READ, "wait");
}
- else if (apr_time_sec(apr_time_now() - session->start_wait)
- >= session->timeout_secs) {
+ else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
/* waited long enough */
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_TIMEUP, c,
"h2_session: wait for data");
apr_size_t max_stream_count; /* max number of open streams */
apr_size_t max_stream_mem; /* max buffer memory for a single stream */
- int timeout_secs; /* connection timeout (seconds) */
- int keepalive_secs; /* connection idle timeout (seconds) */
- int keepalive_remain; /* remaining seconds of keepalive */
apr_time_t start_wait; /* Time we started waiting for sth. to happen */
+ apr_time_t keepalive_until; /* Time when we stop keeing an idle conn alive */
apr_pool_t *pool; /* pool to use in session handling */
apr_bucket_brigade *bbtmp; /* brigade for keeping temporary data */