From 105d3ff9d1c88d99dca026ec7a6a583d8b3614d5 Mon Sep 17 00:00:00 2001
From: Stefan Eissing
Date: Wed, 30 Dec 2015 16:12:35 +0000
Subject: [PATCH] fixes after fuzzing tests, changed H2KeepAliveTimeout default
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1722369 13f79535-47bb-0310-9956-ffa450edef68
---
CHANGES | 5 +++
docs/manual/mod/mod_http2.xml | 7 +--
modules/http2/h2_config.c | 2 +-
modules/http2/h2_conn.c | 38 +++++++++++++---
modules/http2/h2_conn_io.c | 51 ++++++++++++++-------
modules/http2/h2_conn_io.h | 1 +
modules/http2/h2_mplx.c | 15 +++++--
modules/http2/h2_session.c | 83 ++++++++++++++++-------------------
modules/http2/h2_session.h | 12 ++---
modules/http2/h2_switch.c | 3 +-
modules/http2/h2_version.h | 4 +-
modules/http2/h2_workers.c | 2 +-
12 files changed, 140 insertions(+), 83 deletions(-)
diff --git a/CHANGES b/CHANGES
index c1b2937335..af8a8bd164 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.0
+ *) mod_http2: Fixed several errors when connections are closed in the middle
+ of requests, changed H2KeepAliveTimeout defaults to be the same as H2Timeout
+ for synchronous MPMs and leave keepalive timeout to async MPMs default.
+ [Stefan Eissing]
+
*) mod_cache_socache: Fix a possible cached entity body corruption when it
is received from an origin server in multiple batches and forwarded by
mod_proxy. [Yann Ylavic]
diff --git a/docs/manual/mod/mod_http2.xml b/docs/manual/mod/mod_http2.xml
index 331521c565..43c64b9762 100644
--- a/docs/manual/mod/mod_http2.xml
+++ b/docs/manual/mod/mod_http2.xml
@@ -761,7 +761,6 @@ H2PushPriority text/css interleaved # weight 256 default
H2KeepAliveTimeout
Timeout (in seconds) for idle HTTP/2 connections
H2KeepAliveTimeout seconds
- H2KeepAliveTimeout 300
server config
virtual host
@@ -781,7 +780,9 @@ H2PushPriority text/css interleaved # weight 256 default
idle when no streams are open, e.g. no requests are ongoing.
- A value of 0 enforces no timeout.
+ 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.
@@ -790,7 +791,7 @@ H2PushPriority text/css interleaved # weight 256 default
H2StreamTimeout
Timeout (in seconds) for idle HTTP/2 connections
H2StreamTimeout seconds
- H2StreamTimeout 120
+ H2StreamTimeout 0
server config
virtual host
diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c
index a7b63fd66f..1ddb26570e 100644
--- a/modules/http2/h2_config.c
+++ b/modules/http2/h2_config.c
@@ -60,7 +60,7 @@ static h2_config defconf = {
1, /* HTTP/2 server push enabled */
NULL, /* map of content-type to priorities */
5, /* normal connection timeout */
- 5, /* keepalive timeout */
+ -1, /* keepalive timeout */
0, /* stream timeout */
};
diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c
index 262748fc1b..29baa6d90e 100644
--- a/modules/http2/h2_conn.c
+++ b/modules/http2/h2_conn.c
@@ -158,47 +158,71 @@ apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
static apr_status_t h2_conn_process(h2_ctx *ctx)
{
h2_session *session;
+ apr_status_t status;
session = h2_ctx_session_get(ctx);
if (session->c->cs) {
session->c->cs->sense = CONN_SENSE_DEFAULT;
}
- h2_session_process(session, async_mpm);
+ status = h2_session_process(session, async_mpm);
session->c->keepalive = AP_CONN_KEEPALIVE;
if (session->c->cs) {
session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
}
+ if (APR_STATUS_IS_EOF(status)
+ || APR_STATUS_IS_ECONNRESET(status)
+ || APR_STATUS_IS_ECONNABORTED(status)) {
+ /* fatal, probably client just closed connection. emergency shutdown */
+ /* Make sure this connection gets closed properly. */
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
+ "h2_session(%ld): aborted", session->id);
+ session->c->keepalive = AP_CONN_CLOSE;
+
+ h2_ctx_clear(session->c);
+ h2_session_abort(session, status);
+ h2_session_eoc_callback(session);
+ /* hereafter session might be gone */
+ return APR_ECONNABORTED;
+ }
+
if (session->state == H2_SESSION_ST_CLOSING) {
ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
- "h2_session(%ld): done", session->id);
+ "h2_session(%ld): closing", session->id);
/* Make sure this connection gets closed properly. */
- ap_update_child_status_from_conn(session->c->sbh, SERVER_CLOSING, session->c);
session->c->keepalive = AP_CONN_CLOSE;
+ h2_ctx_clear(session->c);
h2_session_close(session);
/* hereafter session may be gone */
}
+ else if (session->state == H2_SESSION_ST_ABORTED) {
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
+ "h2_session(%ld): already aborted", session->id);
+ return APR_ECONNABORTED;
+ }
- return DONE;
+ return APR_SUCCESS;
}
apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
{
int mpm_state = 0;
+ apr_status_t status;
do {
- h2_conn_process(ctx);
+ status = h2_conn_process(ctx);
if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
break;
}
- } while (!async_mpm
+ } while (!async_mpm
+ && status == APR_SUCCESS
&& c->keepalive == AP_CONN_KEEPALIVE
&& mpm_state != AP_MPMQ_STOPPING);
- return DONE;
+ return status;
}
diff --git a/modules/http2/h2_conn_io.c b/modules/http2/h2_conn_io.c
index 2c68db59a8..773d7bbb4b 100644
--- a/modules/http2/h2_conn_io.c
+++ b/modules/http2/h2_conn_io.c
@@ -93,9 +93,15 @@ int h2_conn_io_is_buffered(h2_conn_io *io)
return io->bufsize > 0;
}
+typedef struct {
+ conn_rec *c;
+ h2_conn_io *io;
+} pass_out_ctx;
+
static apr_status_t pass_out(apr_bucket_brigade *bb, void *ctx)
{
- h2_conn_io *io = (h2_conn_io*)ctx;
+ pass_out_ctx *pctx = ctx;
+ conn_rec *c = pctx->c;
apr_status_t status;
apr_off_t bblen;
@@ -103,19 +109,19 @@ static apr_status_t pass_out(apr_bucket_brigade *bb, void *ctx)
return APR_SUCCESS;
}
- ap_update_child_status(io->connection->sbh, SERVER_BUSY_WRITE, NULL);
+ ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
status = apr_brigade_length(bb, 0, &bblen);
if (status == APR_SUCCESS) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
"h2_conn_io(%ld): pass_out brigade %ld bytes",
- io->connection->id, (long)bblen);
- status = ap_pass_brigade(io->connection->output_filters, bb);
- if (status == APR_SUCCESS) {
- io->bytes_written += (apr_size_t)bblen;
- io->last_write = apr_time_now();
+ c->id, (long)bblen);
+ status = ap_pass_brigade(c->output_filters, bb);
+ if (status == APR_SUCCESS && pctx->io) {
+ pctx->io->bytes_written += (apr_size_t)bblen;
+ pctx->io->last_write = apr_time_now();
}
- apr_brigade_cleanup(bb);
}
+ apr_brigade_cleanup(bb);
return status;
}
@@ -169,7 +175,10 @@ apr_status_t h2_conn_io_write(h2_conn_io *io,
const char *buf, size_t length)
{
apr_status_t status = APR_SUCCESS;
+ pass_out_ctx ctx;
+ ctx.c = io->connection;
+ ctx.io = io;
io->unflushed = 1;
if (io->bufsize > 0) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
@@ -183,8 +192,9 @@ apr_status_t h2_conn_io_write(h2_conn_io *io,
while (length > 0 && (status == APR_SUCCESS)) {
apr_size_t avail = io->bufsize - io->buflen;
if (avail <= 0) {
+
bucketeer_buffer(io);
- status = pass_out(io->output, io);
+ status = pass_out(io->output, &ctx);
io->buflen = 0;
}
else if (length > avail) {
@@ -205,7 +215,7 @@ apr_status_t h2_conn_io_write(h2_conn_io *io,
else {
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, status, io->connection,
"h2_conn_io: writing %ld bytes to brigade", (long)length);
- status = apr_brigade_write(io->output, pass_out, io, buf, length);
+ status = apr_brigade_write(io->output, pass_out, &ctx, buf, length);
}
return status;
@@ -242,9 +252,11 @@ apr_status_t h2_conn_io_consider_flush(h2_conn_io *io)
return status;
}
-static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int force)
+static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int force, int eoc)
{
if (io->unflushed || force) {
+ pass_out_ctx ctx;
+
if (io->buflen > 0) {
/* something in the buffer, put it in the output brigade */
ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, io->connection,
@@ -262,20 +274,29 @@ static apr_status_t h2_conn_io_flush_int(h2_conn_io *io, int force)
"h2_conn_io: flush");
/* Send it out */
io->unflushed = 0;
- return pass_out(io->output, io);
+
+ ctx.c = io->connection;
+ ctx.io = eoc? NULL : io;
+ return pass_out(io->output, &ctx);
/* no more access after this, as we might have flushed an EOC bucket
* that de-allocated us all. */
}
return APR_SUCCESS;
}
+apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, apr_bucket *b)
+{
+ APR_BRIGADE_INSERT_TAIL(io->output, b);
+ return h2_conn_io_flush_int(io, 1, 1);
+}
+
apr_status_t h2_conn_io_flush(h2_conn_io *io)
{
- return h2_conn_io_flush_int(io, 1);
+ return h2_conn_io_flush_int(io, 1, 0);
}
apr_status_t h2_conn_io_pass(h2_conn_io *io)
{
- return h2_conn_io_flush_int(io, 0);
+ return h2_conn_io_flush_int(io, 0, 0);
}
diff --git a/modules/http2/h2_conn_io.h b/modules/http2/h2_conn_io.h
index e55f08aa5d..b11480ba7c 100644
--- a/modules/http2/h2_conn_io.h
+++ b/modules/http2/h2_conn_io.h
@@ -60,5 +60,6 @@ apr_status_t h2_conn_io_consider_flush(h2_conn_io *io);
apr_status_t h2_conn_io_pass(h2_conn_io *io);
apr_status_t h2_conn_io_flush(h2_conn_io *io);
+apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, apr_bucket *b);
#endif /* defined(__mod_h2__h2_conn_io__) */
diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c
index 54c83fdf4d..eba7cc900a 100644
--- a/modules/http2/h2_mplx.c
+++ b/modules/http2/h2_mplx.c
@@ -264,6 +264,8 @@ apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
workers_unregister(m);
status = apr_thread_mutex_lock(m->lock);
if (APR_SUCCESS == status) {
+ int i;
+
/* disable WINDOW_UPDATE callbacks */
h2_mplx_set_consumed_cb(m, NULL, NULL);
while (!h2_io_set_iter(m->stream_ios, stream_done_iter, m)) {
@@ -271,14 +273,21 @@ apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
}
release(m, 0);
- while (m->refs > 0) {
+ for (i = 0; m->refs > 0; ++i) {
+
m->join_wait = wait;
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
"h2_mplx(%ld): release_join, refs=%d, waiting...",
m->id, m->refs);
- apr_thread_cond_wait(wait, m->lock);
+
+ status = apr_thread_cond_timedwait(wait, m->lock, apr_time_from_sec(2));
+ if (APR_STATUS_IS_TIMEUP(status)) {
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, m->c,
+ "h2_mplx(%ld): release timeup %d, refs=%d, waiting...",
+ m->id, i, m->refs);
+ }
}
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
"h2_mplx(%ld): release_join -> destroy, (#ios=%ld)",
m->id, (long)h2_io_set_size(m->stream_ios));
apr_thread_mutex_unlock(m->lock);
diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index d867e38cd4..51b2743c31 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -788,6 +788,9 @@ static h2_session *h2_session_create_int(conn_rec *c,
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);
session->keepalive_secs = h2_config_geti(session->config, H2_CONF_KEEPALIVE_SECS);
+ if (session->keepalive_secs <= 0) {
+ session->keepalive_secs = session->timeout_secs;
+ }
status = apr_thread_cond_create(&session->iowait, session->pool);
if (status != APR_SUCCESS) {
@@ -878,17 +881,17 @@ void h2_session_eoc_callback(h2_session *session)
h2_session_destroy(session);
}
-static apr_status_t h2_session_abort_int(h2_session *session, int reason)
+static apr_status_t h2_session_shutdown(h2_session *session, int reason)
{
AP_DEBUG_ASSERT(session);
session->aborted = 1;
- if (session->state != H2_SESSION_ST_CLOSING) {
- session->state = H2_SESSION_ST_CLOSING;
+ if (session->state != H2_SESSION_ST_CLOSING
+ && session->state != H2_SESSION_ST_ABORTED) {
if (session->client_goaway) {
/* client sent us a GOAWAY, just terminate */
nghttp2_session_terminate_session(session->ngh2, NGHTTP2_ERR_EOF);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
- "session(%ld): closed, GOAWAY from client", session->id);
+ "session(%ld): shutdown, GOAWAY from client", session->id);
}
else if (!reason) {
nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
@@ -896,7 +899,7 @@ static apr_status_t h2_session_abort_int(h2_session *session, int reason)
reason, NULL, 0);
nghttp2_session_send(session->ngh2);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
- "session(%ld): closed, no err", session->id);
+ "session(%ld): shutdown, no err", session->id);
}
else {
const char *err = nghttp2_strerror(reason);
@@ -906,39 +909,22 @@ static apr_status_t h2_session_abort_int(h2_session *session, int reason)
strlen(err));
nghttp2_session_send(session->ngh2);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
- "session(%ld): closed, err=%d '%s'",
+ "session(%ld): shutdown, err=%d '%s'",
session->id, reason, err);
}
+ session->state = H2_SESSION_ST_CLOSING;
h2_mplx_abort(session->mplx);
}
return APR_SUCCESS;
}
-apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv)
+void h2_session_abort(h2_session *session, apr_status_t status)
{
AP_DEBUG_ASSERT(session);
- if (rv == 0) {
- rv = NGHTTP2_ERR_PROTO;
- switch (reason) {
- case APR_ENOMEM:
- rv = NGHTTP2_ERR_NOMEM;
- break;
- case APR_SUCCESS: /* all fine, just... */
- case APR_EOF: /* client closed its end... */
- case APR_TIMEUP: /* got bored waiting... */
- rv = 0; /* ...gracefully shut down */
- break;
- case APR_EBADF: /* connection unusable, terminate silently */
- default:
- if (APR_STATUS_IS_ECONNABORTED(reason)
- || APR_STATUS_IS_ECONNRESET(reason)
- || APR_STATUS_IS_EBADF(reason)) {
- rv = NGHTTP2_ERR_EOF;
- }
- break;
- }
- }
- return h2_session_abort_int(session, rv);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): aborting", session->id);
+ session->state = H2_SESSION_ST_ABORTED;
+ session->aborted = 1;
}
static apr_status_t h2_session_start(h2_session *session, int *rv)
@@ -1104,16 +1090,23 @@ h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
void h2_session_close(h2_session *session)
{
apr_bucket *b;
+ conn_rec *c = session->c;
+ apr_status_t status;
AP_DEBUG_ASSERT(session);
if (!session->aborted) {
- h2_session_abort_int(session, 0);
+ h2_session_shutdown(session, 0);
}
h2_session_cleanup(session);
- b = h2_bucket_eoc_create(session->c->bucket_alloc, session);
- h2_conn_io_writeb(&session->io, b);
- h2_conn_io_flush(&session->io);
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
+ "h2_session(%ld): writing eoc", c->id);
+ b = h2_bucket_eoc_create(c->bucket_alloc, session);
+ status = h2_conn_io_write_eoc(&session->io, b);
+ if (status != APR_SUCCESS) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, status, c,
+ "h2_session(%ld): flushed eoc bucket", c->id);
+ }
/* and all is or will be destroyed */
}
@@ -1300,7 +1293,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
if (nghttp2_is_fatal(rv)) {
status = APR_EGENERAL;
- h2_session_abort_int(session, rv);
+ h2_session_shutdown(session, rv);
ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
APLOGNO(02940) "submit_response: %s",
nghttp2_strerror(rv));
@@ -1584,7 +1577,7 @@ static apr_status_t h2_session_send(h2_session *session)
if (nghttp2_is_fatal(rv)) {
ap_log_cerror( APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_session: send gave error=%s", nghttp2_strerror(rv));
- h2_session_abort_int(session, rv);
+ h2_session_shutdown(session, rv);
return APR_EGENERAL;
}
}
@@ -1608,7 +1601,7 @@ static apr_status_t h2_session_receive(void *ctx, const char *data,
"h2_session: nghttp2_session_mem_recv error=%d",
(int)n);
if (nghttp2_is_fatal((int)n)) {
- h2_session_abort_int(session, (int)n);
+ h2_session_shutdown(session, (int)n);
return APR_EGENERAL;
}
}
@@ -1672,7 +1665,6 @@ static apr_status_t h2_session_read(h2_session *session, int block, int loops)
"h2_session(%ld): error reading, terminating",
session->id);
}
- h2_session_abort(session, status, 0);
return status;
}
/* subsequent failure after success(es), return initial
@@ -1730,7 +1722,7 @@ apr_status_t h2_session_process(h2_session *session, int async)
if (session->aborted) {
reason = "aborted";
- status = APR_EOF;
+ status = APR_ECONNABORTED;
goto out;
}
@@ -1747,8 +1739,6 @@ apr_status_t h2_session_process(h2_session *session, int async)
session->s->server_hostname,
c->local_addr->port);
if (status != APR_SUCCESS) {
- h2_session_abort(session, status, rv);
- h2_session_eoc_callback(session);
reason = "start failed";
goto out;
}
@@ -1911,10 +1901,10 @@ apr_status_t h2_session_process(h2_session *session, int async)
* extend that with the H2KeepAliveTimeout. This works different
* for async MPMs. */
remain_secs = session->keepalive_secs - session->timeout_secs;
- if (remain_secs <= 0) {
- /* keepalive is smaller than normal timeout, close the session */
+ if (!async && remain_secs <= 0) {
+ /* not async, keepalive is smaller than normal timeout, close the session */
reason = "keepalive expired";
- h2_session_abort_int(session, 0);
+ h2_session_shutdown(session, 0);
goto out;
}
ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_KEEPALIVE, c);
@@ -1940,7 +1930,7 @@ apr_status_t h2_session_process(h2_session *session, int async)
status = h2_session_read(session, 1, 1);
if (APR_STATUS_IS_TIMEUP(status)) {
reason = "keepalive expired";
- h2_session_abort_int(session, 0);
+ h2_session_shutdown(session, 0);
goto out;
}
else if (status != APR_SUCCESS) {
@@ -1964,6 +1954,11 @@ apr_status_t h2_session_process(h2_session *session, int async)
reason = "closing";
goto out;
+ case H2_SESSION_ST_ABORTED:
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
+ "h2_session(%ld): processing ABORTED", session->id);
+ return APR_ECONNABORTED;
+
default:
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, c,
"h2_session(%ld): state %d", session->id, session->state);
diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h
index 39f27afbe6..7bf316c1d4 100644
--- a/modules/http2/h2_session.h
+++ b/modules/http2/h2_session.h
@@ -60,6 +60,7 @@ typedef enum {
H2_SESSION_ST_BUSY_WAIT, /* waiting for tasks reporting back */
H2_SESSION_ST_KEEPALIVE, /* nothing to write, normal timeout passed */
H2_SESSION_ST_CLOSING, /* shuting down */
+ H2_SESSION_ST_ABORTED, /* client closed connection or sky fall */
} h2_session_state;
typedef struct h2_session {
@@ -153,13 +154,12 @@ apr_status_t h2_session_process(h2_session *session, int async);
void h2_session_eoc_callback(h2_session *session);
/**
- * Called when an error occured and the session needs to shut down.
- * @param session the session to shut down
- * @param reason the apache status that caused the shutdown
- * @param rv the nghttp2 reason for shutdown, set to 0 if you have none.
- *
+ * Called when a serious error occured and the session needs to terminate
+ * without further connection io.
+ * @param session the session to abort
+ * @param reason the apache status that caused the abort
*/
-apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv);
+void h2_session_abort(h2_session *session, apr_status_t reason);
/**
* Close and deallocate the given session.
diff --git a/modules/http2/h2_switch.c b/modules/http2/h2_switch.c
index 62be23494f..c08dd9e5fd 100644
--- a/modules/http2/h2_switch.c
+++ b/modules/http2/h2_switch.c
@@ -163,7 +163,8 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
return status;
}
- return h2_conn_run(ctx, c);
+ h2_conn_run(ctx, c);
+ return DONE;
}
return DONE;
}
diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h
index 2637ff2fc5..9c674b9480 100644
--- a/modules/http2/h2_version.h
+++ b/modules/http2/h2_version.h
@@ -20,7 +20,7 @@
* @macro
* Version number of the h2 module as c string
*/
-#define MOD_HTTP2_VERSION "1.0.14-DEVa"
+#define MOD_HTTP2_VERSION "1.0.15-DEVa"
/**
* @macro
@@ -28,7 +28,7 @@
* release. This is a 24 bit number with 8 bits for major number, 8 bits
* for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
*/
-#define MOD_HTTP2_VERSION_NUM 0x01000e
+#define MOD_HTTP2_VERSION_NUM 0x01000f
#endif /* mod_h2_h2_version_h */
diff --git a/modules/http2/h2_workers.c b/modules/http2/h2_workers.c
index 89aa4efdf1..8c16269949 100644
--- a/modules/http2/h2_workers.c
+++ b/modules/http2/h2_workers.c
@@ -272,7 +272,7 @@ h2_workers *h2_workers_create(server_rec *s, apr_pool_t *server_pool,
apr_atomic_set32(&workers->max_idle_secs, 10);
apr_threadattr_create(&workers->thread_attr, workers->pool);
- apr_threadattr_detach_set(workers->thread_attr, 0);
+ apr_threadattr_detach_set(workers->thread_attr, 1);
if (ap_thread_stacksize != 0) {
apr_threadattr_stacksize_set(workers->thread_attr,
ap_thread_stacksize);
--
2.50.0