Index: httpd-2.4.23/modules/http2/h2_bucket_beam.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_bucket_beam.c	2018-10-02 09:36:09.215205659 +0200
+++ httpd-2.4.23/modules/http2/h2_bucket_beam.c	2018-10-02 09:36:09.239205784 +0200
@@ -673,7 +673,7 @@ apr_size_t h2_beam_buffer_size_get(h2_bu
     h2_beam_lock bl;
     apr_size_t buffer_size = 0;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         buffer_size = beam->max_buf_size;
         leave_yellow(beam, &bl);
     }
@@ -706,7 +706,7 @@ void h2_beam_abort(h2_bucket_beam *beam)
 {
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         beam->aborted = 1;
         r_purge_sent(beam);
         h2_blist_cleanup(&beam->send_list);
@@ -720,7 +720,7 @@ apr_status_t h2_beam_close(h2_bucket_bea
 {
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         r_purge_sent(beam);
         beam_close(beam);
         report_consumption(beam, &bl);
@@ -733,7 +733,7 @@ apr_status_t h2_beam_leave(h2_bucket_bea
 {
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         recv_buffer_cleanup(beam, &bl);
         beam->aborted = 1;
         beam_close(beam);
@@ -922,6 +922,7 @@ apr_status_t h2_beam_send(h2_bucket_beam
             while (!APR_BRIGADE_EMPTY(sender_bb) && APR_SUCCESS == rv) {
                 if (space_left <= 0) {
                     report_prod_io(beam, force_report, &bl);
+                    r_purge_sent(beam);
                     rv = wait_not_full(beam, block, &space_left, &bl);
                     if (APR_SUCCESS != rv) {
                         break;
@@ -1173,7 +1174,7 @@ apr_off_t h2_beam_get_buffered(h2_bucket
     apr_off_t l = 0;
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         for (b = H2_BLIST_FIRST(&beam->send_list); 
             b != H2_BLIST_SENTINEL(&beam->send_list);
             b = APR_BUCKET_NEXT(b)) {
@@ -1191,7 +1192,7 @@ apr_off_t h2_beam_get_mem_used(h2_bucket
     apr_off_t l = 0;
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         for (b = H2_BLIST_FIRST(&beam->send_list); 
             b != H2_BLIST_SENTINEL(&beam->send_list);
             b = APR_BUCKET_NEXT(b)) {
@@ -1207,7 +1208,7 @@ int h2_beam_empty(h2_bucket_beam *beam)
     int empty = 1;
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         empty = (H2_BLIST_EMPTY(&beam->send_list) 
                  && (!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer)));
         leave_yellow(beam, &bl);
@@ -1220,7 +1221,7 @@ int h2_beam_holds_proxies(h2_bucket_beam
     int has_proxies = 1;
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         has_proxies = !H2_BPROXY_LIST_EMPTY(&beam->proxies);
         leave_yellow(beam, &bl);
     }
@@ -1232,7 +1233,7 @@ int h2_beam_was_received(h2_bucket_beam
     int happend = 0;
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         happend = (beam->received_bytes > 0);
         leave_yellow(beam, &bl);
     }
@@ -1244,7 +1245,7 @@ apr_size_t h2_beam_get_files_beamed(h2_b
     apr_size_t n = 0;
     h2_beam_lock bl;
     
-    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+    if (beam && enter_yellow(beam, &bl) == APR_SUCCESS) {
         n = beam->files_beamed;
         leave_yellow(beam, &bl);
     }
Index: httpd-2.4.23/modules/http2/h2_config.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_config.c	2018-10-02 09:36:09.115205137 +0200
+++ httpd-2.4.23/modules/http2/h2_config.c	2018-10-02 09:36:09.239205784 +0200
@@ -603,6 +603,30 @@ static const char *h2_conf_set_early_hin
     return "value must be On or Off";
 }
 
+void h2_get_num_workers(server_rec *s, int *minw, int *maxw)
+{
+    int threads_per_child = 0;
+    const h2_config *config = h2_config_sget(s);
+
+    *minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
+    *maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
+    ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child);
+
+    if (*minw <= 0) {
+        *minw = threads_per_child;
+    }
+    if (*maxw <= 0) {
+        /* As a default, this seems to work quite well under mpm_event. 
+         * For people enabling http2 under mpm_prefork, start 4 threads unless 
+         * configured otherwise. People get unhappy if their http2 requests are 
+         * blocking each other. */
+        *maxw = 3 * (*minw) / 2;
+        if (*maxw < 4) {
+            *maxw = 4;
+        }
+    }
+}
+
 #define AP_END_CMD     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
 
 const command_rec h2_cmds[] = {
Index: httpd-2.4.23/modules/http2/h2_config.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_config.h	2018-10-02 09:36:09.115205137 +0200
+++ httpd-2.4.23/modules/http2/h2_config.h	2018-10-02 09:36:09.239205784 +0200
@@ -94,6 +94,8 @@ const h2_config *h2_config_rget(request_
 int h2_config_geti(const h2_config *conf, h2_config_var_t var);
 apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var);
 
+void h2_get_num_workers(server_rec *s, int *minw, int *maxw);
+
 void h2_config_init(apr_pool_t *pool);
 
 const struct h2_priority *h2_config_get_priority(const h2_config *conf, 
Index: httpd-2.4.23/modules/http2/h2_conn.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_conn.c	2018-10-02 09:36:09.115205137 +0200
+++ httpd-2.4.23/modules/http2/h2_conn.c	2018-10-02 09:36:09.239205784 +0200
@@ -126,18 +126,7 @@ apr_status_t h2_conn_child_init(apr_pool
 
     h2_config_init(pool);
     
-    minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
-    maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
-    if (minw <= 0) {
-        minw = max_threads_per_child;
-    }
-    if (maxw <= 0) {
-        /* As a default, this seems to work quite well under mpm_event. 
-         * For people enabling http2 under mpm_prefork, start 4 threads unless 
-         * configured otherwise. People get unhappy if their http2 requests are 
-         * blocking each other. */
-        maxw = H2MAX(3 * minw / 2, 4);
-    }
+    h2_get_num_workers(s, &minw, &maxw);
     
     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
@@ -237,25 +226,24 @@ apr_status_t h2_conn_run(struct h2_ctx *
     } while (!async_mpm
              && c->keepalive == AP_CONN_KEEPALIVE 
              && mpm_state != AP_MPMQ_STOPPING);
-    
+
     if (c->cs) {
         switch (session->state) {
             case H2_SESSION_ST_INIT:
-            case H2_SESSION_ST_CLEANUP:
-            case H2_SESSION_ST_DONE:
             case H2_SESSION_ST_IDLE:
-                c->cs->state = CONN_STATE_WRITE_COMPLETION;
-                break;
             case H2_SESSION_ST_BUSY:
             case H2_SESSION_ST_WAIT:
-            default:
-                c->cs->state = CONN_STATE_HANDLER;
+                c->cs->state = CONN_STATE_WRITE_COMPLETION;
                 break;
-                
+            case H2_SESSION_ST_CLEANUP:
+            case H2_SESSION_ST_DONE:
+            default:
+                c->cs->state = CONN_STATE_LINGER;
+            break;
         }
     }
-    
-    return DONE;
+
+    return APR_SUCCESS;
 }
 
 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
@@ -325,8 +313,6 @@ conn_rec *h2_slave_create(conn_rec *mast
     c->log                    = NULL;
     c->log_id                 = apr_psprintf(pool, "%ld-%d", 
                                              master->id, slave_id);
-    /* Simulate that we had already a request on this connection. */
-    c->keepalives             = 1;
     c->aborted                = 0;
     /* We cannot install the master connection socket on the slaves, as
      * modules mess with timeouts/blocking of the socket, with
@@ -361,6 +347,14 @@ void h2_slave_destroy(conn_rec *slave)
 
 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd)
 {
-    return ap_run_pre_connection(slave, csd);
+    if (slave->keepalives == 0) {
+        /* Simulate that we had already a request on this connection. Some
+         * hooks trigger special behaviour when keepalives is 0. 
+         * (Not necessarily in pre_connection, but later. Set it here, so it
+         * is in place.) */
+        slave->keepalives = 1;
+        return ap_run_pre_connection(slave, csd);
+    }
+    return APR_SUCCESS;
 }
 
Index: httpd-2.4.23/modules/http2/h2_filter.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_filter.c	2018-10-02 09:36:09.115205137 +0200
+++ httpd-2.4.23/modules/http2/h2_filter.c	2018-10-02 09:36:09.239205784 +0200
@@ -290,6 +290,8 @@ apr_bucket *h2_bucket_observer_beam(stru
 }
 
 static apr_status_t bbout(apr_bucket_brigade *bb, const char *fmt, ...)
+                             __attribute__((format(printf,2,3)));
+static apr_status_t bbout(apr_bucket_brigade *bb, const char *fmt, ...)
 {
     va_list args;
     apr_status_t rv;
@@ -350,8 +352,8 @@ static int add_stream(h2_stream *stream,
     bbout(x->bb, "    \"created\": %f,\n", ((double)stream->created)/APR_USEC_PER_SEC);
     bbout(x->bb, "    \"flowIn\": %d,\n", flowIn);
     bbout(x->bb, "    \"flowOut\": %d,\n", flowOut);
-    bbout(x->bb, "    \"dataIn\": %"APR_UINT64_T_FMT",\n", stream->in_data_octets);  
-    bbout(x->bb, "    \"dataOut\": %"APR_UINT64_T_FMT"\n", stream->out_data_octets);  
+    bbout(x->bb, "    \"dataIn\": %"APR_OFF_T_FMT",\n", stream->in_data_octets);  
+    bbout(x->bb, "    \"dataOut\": %"APR_OFF_T_FMT"\n", stream->out_data_octets);  
     bbout(x->bb, "    }");
     
     ++x->idx;
@@ -521,6 +523,11 @@ int h2_filter_h2_status_handler(request_
         r->clength = -1;
         r->chunked = 1;
         apr_table_unset(r->headers_out, "Content-Length");
+        /* Discourage content-encodings */
+        apr_table_unset(r->headers_out, "Content-Encoding");
+        apr_table_setn(r->subprocess_env, "no-brotli", "1");
+        apr_table_setn(r->subprocess_env, "no-gzip", "1");
+
         ap_set_content_type(r, "application/json");
         apr_table_setn(r->notes, H2_FILTER_DEBUG_NOTE, "on");
 
Index: httpd-2.4.23/modules/http2/h2_from_h1.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_from_h1.c	2018-10-02 09:36:09.115205137 +0200
+++ httpd-2.4.23/modules/http2/h2_from_h1.c	2018-10-02 09:36:09.239205784 +0200
@@ -208,10 +208,18 @@ static h2_headers *create_response(h2_ta
     /* determine the protocol and whether we should use keepalives. */
     ap_set_keepalive(r);
     
-    if (r->chunked) {
+    if (AP_STATUS_IS_HEADER_ONLY(r->status)) {
+        apr_table_unset(r->headers_out, "Transfer-Encoding");
         apr_table_unset(r->headers_out, "Content-Length");
+        r->content_type = r->content_encoding = NULL;
+        r->content_languages = NULL;
+        r->clength = r->chunked = 0;
     }
-    
+    else if (r->chunked) {
+        apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
+        apr_table_unset(r->headers_out, "Content-Length");
+    }
+
     ctype = ap_make_content_type(r, r->content_type);
     if (ctype) {
         apr_table_setn(r->headers_out, "Content-Type", ctype);
@@ -412,7 +420,7 @@ static apr_status_t pass_response(h2_tas
     
     h2_headers *response = h2_headers_create(parser->http_status, 
                                              make_table(parser),
-                                             NULL, task->pool);
+                                             NULL, 0, task->pool);
     apr_brigade_cleanup(parser->tmp);
     b = h2_bucket_headers_create(task->c->bucket_alloc, response);
     APR_BRIGADE_INSERT_TAIL(parser->tmp, b);
@@ -771,6 +779,10 @@ apr_status_t h2_filter_request_in(ap_fil
                 APR_BUCKET_REMOVE(b);
                 apr_bucket_destroy(b);
                 ap_remove_input_filter(f);
+                
+                if (headers->raw_bytes && h2_task_logio_add_bytes_in) {
+                    h2_task_logio_add_bytes_in(task->c, headers->raw_bytes);
+                }
                 break;
             }
         }
Index: httpd-2.4.23/modules/http2/h2.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2.h	2018-10-02 09:36:09.115205137 +0200
+++ httpd-2.4.23/modules/http2/h2.h	2018-10-02 09:36:09.239205784 +0200
@@ -50,6 +50,9 @@ extern const char *H2_MAGIC_TOKEN;
 /* Max data size to write so it fits inside a TLS record */
 #define H2_DATA_CHUNK_SIZE          ((16*1024) - 100 - 9) 
 
+/* Size of the frame header itself in HTTP/2 */
+#define H2_FRAME_HDR_LEN            9
+ 
 /* Maximum number of padding bytes in a frame, rfc7540 */
 #define H2_MAX_PADLEN               256
 /* Initial default window size, RFC 7540 ch. 6.5.2 */
@@ -136,6 +139,7 @@ struct h2_request {
     apr_time_t request_time;
     unsigned int chunked : 1;   /* iff requst body needs to be forwarded as chunked */
     unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */
+    apr_off_t raw_bytes;        /* RAW network bytes that generated this request - if known. */
 };
 
 typedef struct h2_headers h2_headers;
@@ -144,6 +148,7 @@ struct h2_headers {
     int         status;
     apr_table_t *headers;
     apr_table_t *notes;
+    apr_off_t   raw_bytes;      /* RAW network bytes that generated this request - if known. */
 };
 
 typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_off_t len);
Index: httpd-2.4.23/modules/http2/h2_h2.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_h2.c	2018-10-02 09:36:09.119205158 +0200
+++ httpd-2.4.23/modules/http2/h2_h2.c	2018-10-02 09:36:09.239205784 +0200
@@ -59,7 +59,6 @@ const char *H2_MAGIC_TOKEN = "PRI * HTTP
 /*******************************************************************************
  * The optional mod_ssl functions we need. 
  */
-static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *opt_ssl_engine_disable;
 static APR_OPTIONAL_FN_TYPE(ssl_is_https) *opt_ssl_is_https;
 static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *opt_ssl_var_lookup;
 
@@ -445,7 +444,6 @@ apr_status_t h2_h2_init(apr_pool_t *pool
 {
     (void)pool;
     ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, "h2_h2, child_init");
-    opt_ssl_engine_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
     opt_ssl_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
     opt_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
     
@@ -636,10 +634,10 @@ int h2_h2_process_conn(conn_rec* c)
                 }
                 h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c");
             }
-            else {
+            else if (APLOGctrace2(c)) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
-                              "h2_h2, not detected in %d bytes: %s", 
-                              (int)slen, s);
+                              "h2_h2, not detected in %d bytes(base64): %s", 
+                              (int)slen, h2_util_base64url_encode(s, slen, c->pool));
             }
             
             apr_brigade_destroy(temp);
@@ -653,10 +651,11 @@ int h2_h2_process_conn(conn_rec* c)
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c, "conn_setup");
             if (status != APR_SUCCESS) {
                 h2_ctx_clear(c);
-                return status;
+                return !OK;
             }
         }
-        return h2_conn_run(ctx, c);
+        h2_conn_run(ctx, c);
+        return OK;
     }
     
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, declined");
Index: httpd-2.4.23/modules/http2/h2_headers.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_headers.c	2018-10-02 09:36:09.119205158 +0200
+++ httpd-2.4.23/modules/http2/h2_headers.c	2018-10-02 09:36:09.239205784 +0200
@@ -110,7 +110,8 @@ apr_bucket *h2_bucket_headers_beam(struc
 
 
 h2_headers *h2_headers_create(int status, apr_table_t *headers_in, 
-                                apr_table_t *notes, apr_pool_t *pool)
+                                apr_table_t *notes, apr_off_t raw_bytes,
+                                apr_pool_t *pool)
 {
     h2_headers *headers = apr_pcalloc(pool, sizeof(h2_headers));
     headers->status    = status;
@@ -124,7 +125,7 @@ h2_headers *h2_headers_create(int status
 h2_headers *h2_headers_rcreate(request_rec *r, int status,
                                  apr_table_t *header, apr_pool_t *pool)
 {
-    h2_headers *headers = h2_headers_create(status, header, r->notes, pool);
+    h2_headers *headers = h2_headers_create(status, header, r->notes, 0, pool);
     if (headers->status == HTTP_FORBIDDEN) {
         const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
         if (cause) {
@@ -148,7 +149,7 @@ h2_headers *h2_headers_rcreate(request_r
 h2_headers *h2_headers_copy(apr_pool_t *pool, h2_headers *h)
 {
     return h2_headers_create(h->status, apr_table_copy(pool, h->headers), 
-                             apr_table_copy(pool, h->notes), pool);
+                             apr_table_copy(pool, h->notes), h->raw_bytes, pool);
 }
 
 h2_headers *h2_headers_die(apr_status_t type,
Index: httpd-2.4.23/modules/http2/h2_headers.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_headers.h	2018-10-02 09:36:09.119205158 +0200
+++ httpd-2.4.23/modules/http2/h2_headers.h	2018-10-02 09:36:09.239205784 +0200
@@ -40,10 +40,12 @@ apr_bucket *h2_bucket_headers_beam(struc
  * @param status the headers status
  * @param header the headers of the headers
  * @param notes  the notes carried by the headers
+ * @param raw_bytes the raw network bytes (if known) used to transmit these
  * @param pool the memory pool to use
  */
 h2_headers *h2_headers_create(int status, apr_table_t *header, 
-                              apr_table_t *notes, apr_pool_t *pool);
+                              apr_table_t *notes, apr_off_t raw_bytes, 
+                              apr_pool_t *pool);
 
 /**
  * Create the headers from the given request_rec.
Index: httpd-2.4.23/modules/http2/h2_mplx.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_mplx.c	2018-10-02 09:36:09.119205158 +0200
+++ httpd-2.4.23/modules/http2/h2_mplx.c	2018-10-02 09:36:09.239205784 +0200
@@ -285,34 +285,6 @@ static int output_consumed_signal(h2_mpl
     return 0;
 }
 
-static void task_destroy(h2_mplx *m, h2_task *task)
-{
-    conn_rec *slave = NULL;
-    int reuse_slave = 0;
-    
-    slave = task->c;
-
-    if (m->s->keep_alive_max == 0 || slave->keepalives < m->s->keep_alive_max) {
-        reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2))
-                       && !task->rst_error);
-    }
-    
-    if (slave) {
-        if (reuse_slave && slave->keepalive == AP_CONN_KEEPALIVE) {
-            h2_beam_log(task->output.beam, m->c, APLOG_DEBUG, 
-                        APLOGNO(03385) "h2_task_destroy, reuse slave");    
-            h2_task_destroy(task);
-            APR_ARRAY_PUSH(m->spare_slaves, conn_rec*) = slave;
-        }
-        else {
-            h2_beam_log(task->output.beam, m->c, APLOG_TRACE1, 
-                        "h2_task_destroy, destroy slave");    
-            slave->sbh = NULL;
-            h2_slave_destroy(slave);
-        }
-    }
-}
-
 static int stream_destroy_iter(void *ctx, void *val) 
 {   
     h2_mplx *m = ctx;
@@ -330,8 +302,42 @@ static int stream_destroy_iter(void *ctx
     }
 
     if (stream->task) {
-        task_destroy(m, stream->task);
+        h2_task *task = stream->task;
+        conn_rec *slave;
+        int reuse_slave = 0;
+        
         stream->task = NULL;
+        slave = task->c;
+        if (slave) {
+            /* On non-serialized requests, the IO logging has not accounted for any
+             * meta data send over the network: response headers and h2 frame headers. we
+             * counted this on the stream and need to add this now.
+             * This is supposed to happen before the EOR bucket triggers the
+             * logging of the transaction. *fingers crossed* */
+            if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) {
+                apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets;
+                if (unaccounted > 0) {
+                    h2_task_logio_add_bytes_out(slave, unaccounted);
+                }
+            }
+        
+            if (m->s->keep_alive_max == 0 || slave->keepalives < m->s->keep_alive_max) {
+                reuse_slave = ((m->spare_slaves->nelts < (m->limit_active * 3 / 2))
+                               && !task->rst_error);
+            }
+            
+            if (reuse_slave && slave->keepalive == AP_CONN_KEEPALIVE) {
+                h2_beam_log(task->output.beam, m->c, APLOG_DEBUG, 
+                            APLOGNO(03385) "h2_task_destroy, reuse slave");    
+                h2_task_destroy(task);
+                APR_ARRAY_PUSH(m->spare_slaves, conn_rec*) = slave;
+            }
+            else {
+                h2_beam_log(task->output.beam, m->c, APLOG_TRACE1, 
+                            "h2_task_destroy, destroy slave");    
+                h2_slave_destroy(slave);
+            }
+        }
     }
     h2_stream_destroy(stream);
     return 0;
@@ -377,11 +383,12 @@ static int report_stream_iter(void *ctx,
     h2_mplx *m = ctx;
     h2_stream *stream = val;
     h2_task *task = stream->task;
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
-                  H2_STRM_MSG(stream, "started=%d, scheduled=%d, ready=%d, "
-                              "out_buffer=%ld"), 
-                  !!stream->task, stream->scheduled, h2_stream_is_ready(stream),
-                  (long)h2_beam_get_buffered(stream->output));
+    if (APLOGctrace1(m->c)) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
+                      H2_STRM_MSG(stream, "started=%d, scheduled=%d, ready=%d, out_buffer=%ld"), 
+                      !!stream->task, stream->scheduled, h2_stream_is_ready(stream),
+                      (long)h2_beam_get_buffered(stream->output));
+    }
     if (task) {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */
                       H2_STRM_MSG(stream, "->03198: %s %s %s"
Index: httpd-2.4.23/modules/http2/h2_proxy_util.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_proxy_util.c	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_proxy_util.c	2018-10-02 09:36:09.243205805 +0200
@@ -915,8 +915,8 @@ static size_t subst_str(link_ctx *ctx, i
     delta = nlen - olen;
     plen = ctx->slen + delta + 1;
     p = apr_pcalloc(ctx->pool, plen);
-    strncpy(p, ctx->s, start);
-    strncpy(p + start, ns, nlen);
+    memcpy(p, ctx->s, start);
+    memcpy(p + start, ns, nlen);
     strcpy(p + start + nlen, ctx->s + end);
     ctx->s = p;
     ctx->slen = (int)strlen(p);
@@ -942,7 +942,7 @@ static void map_link(link_ctx *ctx)
             /* common to use relative uris in link header, for mappings
              * to work need to prefix the backend server uri */
             need_len += ctx->psu_len;
-            strncpy(buffer, ctx->p_server_uri, sizeof(buffer));
+            apr_cpystrn(buffer, ctx->p_server_uri, sizeof(buffer));
             buffer_len = ctx->psu_len;
         }
         if (need_len > sizeof(buffer)) {
@@ -950,9 +950,7 @@ static void map_link(link_ctx *ctx)
                           "link_reverse_map uri too long, skipped: %s", ctx->s);
             return;
         }
-        strncpy(buffer + buffer_len, ctx->s + ctx->link_start, link_len);
-        buffer_len += link_len;
-        buffer[buffer_len] = '\0';
+        apr_cpystrn(buffer + buffer_len, ctx->s + ctx->link_start, link_len + 1);
         if (!prepend_p_server
             && strcmp(ctx->real_backend_uri, ctx->p_server_uri)
             && !strncmp(buffer, ctx->real_backend_uri, ctx->rbu_len)) {
@@ -960,8 +958,8 @@ static void map_link(link_ctx *ctx)
              * to work, we need to use the proxy uri */
             int path_start = ctx->link_start + ctx->rbu_len;
             link_len -= ctx->rbu_len;
-            strcpy(buffer, ctx->p_server_uri);
-            strncpy(buffer + ctx->psu_len, ctx->s + path_start, link_len);
+            memcpy(buffer, ctx->p_server_uri, ctx->psu_len);
+            memcpy(buffer + ctx->psu_len, ctx->s + path_start, link_len);
             buffer_len = ctx->psu_len + link_len;
             buffer[buffer_len] = '\0';            
         }
Index: httpd-2.4.23/modules/http2/h2_push.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_push.c	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_push.c	2018-10-02 09:36:09.243205805 +0200
@@ -351,7 +351,7 @@ static int add_push(link_ctx *ctx)
                                     ctx->req->authority, path, headers,
                                     ctx->req->serialize);
                 /* atm, we do not push on pushes */
-                h2_request_end_headers(req, ctx->pool, 1);
+                h2_request_end_headers(req, ctx->pool, 1, 0);
                 push->req = req;
                 if (has_param(ctx, "critical")) {
                     h2_priority *prio = apr_pcalloc(ctx->pool, sizeof(*prio));
Index: httpd-2.4.23/modules/http2/h2_request.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_request.c	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_request.c	2018-10-02 09:36:09.243205805 +0200
@@ -149,7 +149,7 @@ apr_status_t h2_request_add_header(h2_re
     return status;
 }
 
-apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos, size_t raw_bytes)
 {
     const char *s;
     
@@ -189,7 +189,8 @@ apr_status_t h2_request_end_headers(h2_r
             apr_table_setn(req->headers, "Content-Length", "0");
         }
     }
-
+    req->raw_bytes += raw_bytes;
+    
     return APR_SUCCESS;
 }
 
Index: httpd-2.4.23/modules/http2/h2_request.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_request.h	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_request.h	2018-10-02 09:36:09.243205805 +0200
@@ -29,7 +29,7 @@ apr_status_t h2_request_add_trailer(h2_r
                                     const char *name, size_t nlen,
                                     const char *value, size_t vlen);
 
-apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos);
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos, size_t raw_bytes);
 
 h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src);
 
Index: httpd-2.4.23/modules/http2/h2_session.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_session.c	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_session.c	2018-10-02 09:36:09.243205805 +0200
@@ -234,6 +234,7 @@ static int on_data_chunk_recv_cb(nghttp2
     stream = h2_session_stream_get(session, stream_id);
     if (stream) {
         status = h2_stream_recv_DATA(stream, flags, data, len);
+        dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream data rcvd");
     }
     else {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03064)
@@ -316,9 +317,9 @@ static int on_header_cb(nghttp2_session
 }
 
 /**
- * nghttp2 session has received a complete frame. Most, it uses
- * for processing of internal state. HEADER and DATA frames however
- * we need to handle ourself.
+ * nghttp2 session has received a complete frame. Most are used by nghttp2
+ * for processing of internal state. Some, like HEADER and DATA frames,
+ * we need to act on.
  */
 static int on_frame_recv_cb(nghttp2_session *ng2s,
                             const nghttp2_frame *frame,
@@ -347,7 +348,8 @@ static int on_frame_recv_cb(nghttp2_sess
              * trailers */
             stream = h2_session_stream_get(session, frame->hd.stream_id);
             if (stream) {
-                rv = h2_stream_recv_frame(stream, NGHTTP2_HEADERS, frame->hd.flags);
+                rv = h2_stream_recv_frame(stream, NGHTTP2_HEADERS, frame->hd.flags, 
+                    frame->hd.length + H2_FRAME_HDR_LEN);
             }
             break;
         case NGHTTP2_DATA:
@@ -357,7 +359,8 @@ static int on_frame_recv_cb(nghttp2_sess
                               H2_STRM_LOG(APLOGNO(02923), stream, 
                               "DATA, len=%ld, flags=%d"), 
                               (long)frame->hd.length, frame->hd.flags);
-                rv = h2_stream_recv_frame(stream, NGHTTP2_DATA, frame->hd.flags);
+                rv = h2_stream_recv_frame(stream, NGHTTP2_DATA, frame->hd.flags, 
+                    frame->hd.length + H2_FRAME_HDR_LEN);
             }
             break;
         case NGHTTP2_PRIORITY:
@@ -375,6 +378,9 @@ static int on_frame_recv_cb(nghttp2_sess
                           "h2_stream(%ld-%d): WINDOW_UPDATE incr=%d", 
                           session->id, (int)frame->hd.stream_id,
                           frame->window_update.window_size_increment);
+            if (nghttp2_session_want_write(session->ngh2)) {
+                dispatch_event(session, H2_SESSION_EV_FRAME_RCVD, 0, "window update");
+            }
             break;
         case NGHTTP2_RST_STREAM:
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03067)
@@ -401,6 +407,12 @@ static int on_frame_recv_cb(nghttp2_sess
                                frame->goaway.error_code, NULL);
             }
             break;
+        case NGHTTP2_SETTINGS:
+            if (APLOGctrace2(session->c)) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                              H2_SSSN_MSG(session, "SETTINGS, len=%ld"), (long)frame->hd.length);
+            }
+            break;
         default:
             if (APLOGctrace2(session->c)) {
                 char buffer[256];
@@ -412,7 +424,40 @@ static int on_frame_recv_cb(nghttp2_sess
             }
             break;
     }
-    return (APR_SUCCESS == rv)? 0 : NGHTTP2_ERR_PROTO;
+    
+    if (session->state == H2_SESSION_ST_IDLE) {
+        /* We received a frame, but session is in state IDLE. That means the frame
+         * did not really progress any of the (possibly) open streams. It was a meta
+         * frame, e.g. SETTINGS/WINDOW_UPDATE/unknown/etc.
+         * Remember: IDLE means we cannot send because either there are no streams open or
+         * all open streams are blocked on exhausted WINDOWs for outgoing data.
+         * The more frames we receive that do not change this, the less interested we
+         * become in serving this connection. This is expressed in increasing "idle_delays".
+         * Eventually, the connection will timeout and we'll close it. */
+        session->idle_frames = H2MIN(session->idle_frames + 1, session->frames_received);
+            ap_log_cerror( APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                          H2_SSSN_MSG(session, "session has %ld idle frames"), 
+                          (long)session->idle_frames);
+        if (session->idle_frames > 10) {
+            apr_size_t busy_frames = H2MAX(session->frames_received - session->idle_frames, 1);
+            int idle_ratio = (int)(session->idle_frames / busy_frames); 
+            if (idle_ratio > 100) {
+                session->idle_delay = apr_time_from_msec(H2MIN(1000, idle_ratio));
+            }
+            else if (idle_ratio > 10) {
+                session->idle_delay = apr_time_from_msec(10);
+            }
+            else if (idle_ratio > 1) {
+                session->idle_delay = apr_time_from_msec(1);
+            }
+            else {
+                session->idle_delay = 0;
+            }
+        }
+    }
+    
+    if (APR_SUCCESS != rv) return NGHTTP2_ERR_PROTO;
+    return 0;
 }
 
 static int h2_session_continue_data(h2_session *session) {
@@ -545,7 +590,8 @@ static int on_frame_send_cb(nghttp2_sess
     
     stream = h2_session_stream_get(session, stream_id);
     if (stream) {
-        h2_stream_send_frame(stream, frame->hd.type, frame->hd.flags);
+        h2_stream_send_frame(stream, frame->hd.type, frame->hd.flags, 
+            frame->hd.length + H2_FRAME_HDR_LEN);
     }
     return 0;
 }
@@ -1599,23 +1645,57 @@ static void update_child_status(h2_sessi
 
 static void transit(h2_session *session, const char *action, h2_session_state nstate)
 {
+    apr_time_t timeout;
+    int ostate, loglvl;
+    const char *s;
+    
     if (session->state != nstate) {
-        int loglvl = APLOG_DEBUG;
-        if ((session->state == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
-            || (session->state == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
+        ostate = session->state;
+        session->state = nstate;
+        
+        loglvl = APLOG_DEBUG;
+        if ((ostate == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
+            || (ostate == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
             loglvl = APLOG_TRACE1;
         }
         ap_log_cerror(APLOG_MARK, loglvl, 0, session->c, 
                       H2_SSSN_LOG(APLOGNO(03078), session, 
                       "transit [%s] -- %s --> [%s]"), 
-                      h2_session_state_str(session->state), action, 
+                      h2_session_state_str(ostate), action, 
                       h2_session_state_str(nstate));
-        session->state = nstate;
+        
         switch (session->state) {
             case H2_SESSION_ST_IDLE:
-                update_child_status(session, (session->open_streams == 0? 
-                                              SERVER_BUSY_KEEPALIVE
-                                              : SERVER_BUSY_READ), "idle");
+                if (!session->remote.emitted_count) {
+                    /* on fresh connections, with async mpm, do not return
+                     * to mpm for a second. This gives the first request a better
+                     * chance to arrive (und connection leaving IDLE state).
+                     * If we return to mpm right away, this connection has the
+                     * same chance of being cleaned up by the mpm as connections
+                     * that already served requests - not fair. */
+                    session->idle_sync_until = apr_time_now() + apr_time_from_sec(1);
+                    s = "timeout";
+                    timeout = H2MAX(session->s->timeout, session->s->keep_alive_timeout);
+                    update_child_status(session, SERVER_BUSY_READ, "idle");
+                    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, 
+                                  H2_SSSN_LOG("", session, "enter idle, timeout = %d sec"), 
+                                  (int)apr_time_sec(H2MAX(session->s->timeout, session->s->keep_alive_timeout)));
+                }
+                else if (session->open_streams) {
+                    s = "timeout";
+                    timeout = session->s->keep_alive_timeout;
+                    update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle");
+                }
+                else {
+                    /* normal keepalive setup */
+                    s = "keepalive";
+                    timeout = session->s->keep_alive_timeout;
+                    update_child_status(session, SERVER_BUSY_KEEPALIVE, "idle");
+                }
+                session->idle_until = apr_time_now() + timeout; 
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, 
+                              H2_SSSN_LOG("", session, "enter idle, %s = %d sec"), 
+                              s, (int)apr_time_sec(timeout));
                 break;
             case H2_SESSION_ST_DONE:
                 update_child_status(session, SERVER_CLOSING, "done");
@@ -1722,8 +1802,6 @@ static void h2_session_ev_no_io(h2_sessi
                      * This means we only wait for WINDOW_UPDATE from the 
                      * client and can block on READ. */
                     transit(session, "no io (flow wait)", H2_SESSION_ST_IDLE);
-                    session->idle_until = apr_time_now() + session->s->timeout;
-                    session->keep_sync_until = session->idle_until;
                     /* Make sure we have flushed all previously written output
                      * so that the client will react. */
                     if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
@@ -1734,12 +1812,7 @@ static void h2_session_ev_no_io(h2_sessi
             }
             else if (session->local.accepting) {
                 /* When we have no streams, but accept new, switch to idle */
-                apr_time_t now = apr_time_now();
                 transit(session, "no io (keepalive)", H2_SESSION_ST_IDLE);
-                session->idle_until = (session->remote.emitted_count? 
-                                       session->s->keep_alive_timeout : 
-                                       session->s->timeout) + now;
-                session->keep_sync_until = now + apr_time_from_sec(1);
             }
             else {
                 /* We are no longer accepting new streams and there are
@@ -1754,12 +1827,25 @@ static void h2_session_ev_no_io(h2_sessi
     }
 }
 
-static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_frame_rcvd(h2_session *session, int arg, const char *msg)
+{
+    switch (session->state) {
+        case H2_SESSION_ST_IDLE:
+        case H2_SESSION_ST_WAIT:
+            transit(session, "frame received", H2_SESSION_ST_BUSY);
+            break;
+        default:
+            /* nop */
+            break;
+    }
+}
+
+static void h2_session_ev_stream_change(h2_session *session, int arg, const char *msg)
 {
     switch (session->state) {
         case H2_SESSION_ST_IDLE:
         case H2_SESSION_ST_WAIT:
-            transit(session, "data read", H2_SESSION_ST_BUSY);
+            transit(session, "stream change", H2_SESSION_ST_BUSY);
             break;
         default:
             /* nop */
@@ -1799,16 +1885,6 @@ static void h2_session_ev_pre_close(h2_s
 static void ev_stream_open(h2_session *session, h2_stream *stream)
 {
     h2_iq_append(session->in_process, stream->id);
-    switch (session->state) {
-        case H2_SESSION_ST_IDLE:
-            if (session->open_streams == 1) {
-                /* enter timeout, since we have a stream again */
-                session->idle_until = (session->s->timeout + apr_time_now());
-            }
-            break;
-        default:
-            break;
-    }
 }
 
 static void ev_stream_closed(h2_session *session, h2_stream *stream)
@@ -1821,11 +1897,6 @@ static void ev_stream_closed(h2_session
     }
     switch (session->state) {
         case H2_SESSION_ST_IDLE:
-            if (session->open_streams == 0) {
-                /* enter keepalive timeout, since we no longer have streams */
-                session->idle_until = (session->s->keep_alive_timeout
-                                       + apr_time_now());
-            }
             break;
         default:
             break;
@@ -1883,6 +1954,7 @@ static void on_stream_state_enter(void *
         default:
             break;
     }
+    dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, "stream state change");
 }
 
 static void on_stream_event(void *ctx, h2_stream *stream, 
@@ -1941,8 +2013,8 @@ static void dispatch_event(h2_session *s
         case H2_SESSION_EV_NO_IO:
             h2_session_ev_no_io(session, arg, msg);
             break;
-        case H2_SESSION_EV_DATA_READ:
-            h2_session_ev_data_read(session, arg, msg);
+        case H2_SESSION_EV_FRAME_RCVD:
+            h2_session_ev_frame_rcvd(session, arg, msg);
             break;
         case H2_SESSION_EV_NGH2_DONE:
             h2_session_ev_ngh2_done(session, arg, msg);
@@ -1953,6 +2025,9 @@ static void dispatch_event(h2_session *s
         case H2_SESSION_EV_PRE_CLOSE:
             h2_session_ev_pre_close(session, arg, msg);
             break;
+        case H2_SESSION_EV_STREAM_CHANGE:
+            h2_session_ev_stream_change(session, arg, msg);
+            break;
         default:
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
                           H2_SSSN_MSG(session, "unknown event %d"), ev);
@@ -1986,13 +2061,15 @@ apr_status_t h2_session_process(h2_sessi
     apr_status_t status = APR_SUCCESS;
     conn_rec *c = session->c;
     int rv, mpm_state, trace = APLOGctrace3(c);
-
+    apr_time_t now;
+    
     if (trace) {
         ap_log_cerror( APLOG_MARK, APLOG_TRACE3, status, c,
                       H2_SSSN_MSG(session, "process start, async=%d"), async);
     }
                   
     while (session->state != H2_SESSION_ST_DONE) {
+        now = apr_time_now();
         session->have_read = session->have_written = 0;
 
         if (session->local.accepting 
@@ -2030,39 +2107,42 @@ apr_status_t h2_session_process(h2_sessi
                 break;
                 
             case H2_SESSION_ST_IDLE:
-                /* We trust our connection into the default timeout/keepalive
-                 * handling of the core filters/mpm iff:
-                 * - keep_sync_until is not set
-                 * - we have an async mpm
-                 * - we have no open streams to process
-                 * - we are not sitting on a Upgrade: request
-                 * - we already have seen at least one request
-                 */
-                if (!session->keep_sync_until && async && !session->open_streams
-                    && !session->r && session->remote.emitted_count) {
+                if (session->idle_until && (apr_time_now() + session->idle_delay) > session->idle_until) {
+                    ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
+                                  H2_SSSN_MSG(session, "idle, timeout reached, closing"));
+                    if (session->idle_delay) {
+                        apr_table_setn(session->c->notes, "short-lingering-close", "1"); 
+                    }
+                    dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
+                    goto out;
+                }
+                
+                if (session->idle_delay) {
+                    /* we are less interested in spending time on this connection */
+                    ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, c,
+                                  H2_SSSN_MSG(session, "session is idle (%ld ms), idle wait %ld sec left"), 
+                                  (long)apr_time_as_msec(session->idle_delay),
+                                  (long)apr_time_sec(session->idle_until - now));
+                    apr_sleep(session->idle_delay);
+                    session->idle_delay = 0;
+                }
+
+                h2_conn_io_flush(&session->io);
+                if (async && !session->r && (now > session->idle_sync_until)) {
                     if (trace) {
                         ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
                                       H2_SSSN_MSG(session, 
                                       "nonblock read, %d streams open"), 
                                       session->open_streams);
                     }
-                    h2_conn_io_flush(&session->io);
                     status = h2_session_read(session, 0);
                     
                     if (status == APR_SUCCESS) {
                         session->have_read = 1;
-                        dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
                     }
-                    else if (APR_STATUS_IS_EAGAIN(status) 
-                        || APR_STATUS_IS_TIMEUP(status)) {
-                        if (apr_time_now() > session->idle_until) {
-                            dispatch_event(session, 
-                                           H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
-                        }
-                        else {
-                            status = APR_EAGAIN;
-                            goto out;
-                        }
+                    else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
+                        status = APR_EAGAIN;
+                        goto out;
                     }
                     else {
                         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
@@ -2074,7 +2154,6 @@ apr_status_t h2_session_process(h2_sessi
                 }
                 else {
                     /* make certain, we send everything before we idle */
-                    h2_conn_io_flush(&session->io);
                     if (trace) {
                         ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
                                       H2_SSSN_MSG(session, 
@@ -2086,7 +2165,6 @@ apr_status_t h2_session_process(h2_sessi
                      */
                     status = h2_mplx_idle(session->mplx);
                     if (status == APR_EAGAIN) {
-                        dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
                         break;
                     }
                     else if (status != APR_SUCCESS) {
@@ -2097,33 +2175,11 @@ apr_status_t h2_session_process(h2_sessi
                     status = h2_session_read(session, 1);
                     if (status == APR_SUCCESS) {
                         session->have_read = 1;
-                        dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
                     }
                     else if (status == APR_EAGAIN) {
                         /* nothing to read */
                     }
                     else if (APR_STATUS_IS_TIMEUP(status)) {
-                        apr_time_t now = apr_time_now();
-                        if (now > session->keep_sync_until) {
-                            /* if we are on an async mpm, now is the time that
-                             * we may dare to pass control to it. */
-                            session->keep_sync_until = 0;
-                        }
-                        if (now > session->idle_until) {
-                            if (trace) {
-                                ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
-                                              H2_SSSN_MSG(session, 
-                                              "keepalive timeout"));
-                            }
-                            dispatch_event(session, 
-                                           H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
-                        }
-                        else if (trace) {                        
-                            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, c,
-                                          H2_SSSN_MSG(session, 
-                                          "keepalive, %f sec left"),
-                                          (session->idle_until - now) / 1000000.0f);
-                        }
                         /* continue reading handling */
                     }
                     else if (APR_STATUS_IS_ECONNABORTED(status)
@@ -2141,6 +2197,18 @@ apr_status_t h2_session_process(h2_sessi
                         dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "error");
                     }
                 }
+                if (nghttp2_session_want_write(session->ngh2)) {
+                    ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
+                    status = h2_session_send(session);
+                    if (status == APR_SUCCESS) {
+                        status = h2_conn_io_flush(&session->io);
+                    }
+                    if (status != APR_SUCCESS) {
+                        dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
+                                       H2_ERR_INTERNAL_ERROR, "writing");
+                        break;
+                    }
+                }
                 break;
                 
             case H2_SESSION_ST_BUSY:
@@ -2150,7 +2218,6 @@ apr_status_t h2_session_process(h2_sessi
                     status = h2_session_read(session, 0);
                     if (status == APR_SUCCESS) {
                         session->have_read = 1;
-                        dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
                     }
                     else if (status == APR_EAGAIN) {
                         /* nothing to read */
@@ -2214,7 +2281,7 @@ apr_status_t h2_session_process(h2_sessi
                                              session->iowait);
                 if (status == APR_SUCCESS) {
                     session->wait_us = 0;
-                    dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+                        dispatch_event(session, H2_SESSION_EV_STREAM_CHANGE, 0, NULL);
                 }
                 else if (APR_STATUS_IS_TIMEUP(status)) {
                     /* go back to checking all inputs again */
Index: httpd-2.4.23/modules/http2/h2_session.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_session.h	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_session.h	2018-10-02 09:36:09.243205805 +0200
@@ -65,10 +65,11 @@ typedef enum {
     H2_SESSION_EV_PROTO_ERROR,      /* protocol error */
     H2_SESSION_EV_CONN_TIMEOUT,     /* connection timeout */
     H2_SESSION_EV_NO_IO,            /* nothing has been read or written */
-    H2_SESSION_EV_DATA_READ,        /* connection data has been read */
+    H2_SESSION_EV_FRAME_RCVD,       /* a frame has been received */
     H2_SESSION_EV_NGH2_DONE,        /* nghttp2 wants neither read nor write anything */
     H2_SESSION_EV_MPM_STOPPING,     /* the process is stopping */
     H2_SESSION_EV_PRE_CLOSE,        /* connection will close after this */
+    H2_SESSION_EV_STREAM_CHANGE,    /* a stream (state/input/output) changed */
 } h2_session_event_t;
 
 typedef struct h2_session {
@@ -117,7 +118,9 @@ typedef struct h2_session {
     apr_size_t max_stream_mem;      /* max buffer memory for a single stream */
     
     apr_time_t idle_until;          /* Time we shut down due to sheer boredom */
-    apr_time_t keep_sync_until;     /* Time we sync wait until passing to async mpm */
+    apr_time_t idle_sync_until;     /* Time we sync wait until keepalive handling kicks in */
+    apr_size_t idle_frames;         /* number of rcvd frames that kept session in idle state */
+    apr_interval_time_t idle_delay; /* Time we delay processing rcvd frames in idle state */
     
     apr_bucket_brigade *bbtmp;      /* brigade for keeping temporary data */
     struct apr_thread_cond_t *iowait; /* our cond when trywaiting for data */
Index: httpd-2.4.23/modules/http2/h2_stream.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_stream.c	2018-10-02 09:36:09.203205597 +0200
+++ httpd-2.4.23/modules/http2/h2_stream.c	2018-10-02 09:36:09.243205805 +0200
@@ -220,7 +220,8 @@ static apr_status_t close_input(h2_strea
             stream->in_buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
         }
         
-        r = h2_headers_create(HTTP_OK, stream->trailers, NULL, stream->pool);
+        r = h2_headers_create(HTTP_OK, stream->trailers, NULL, 
+            stream->in_trailer_octets, stream->pool);
         stream->trailers = NULL;        
         b = h2_bucket_headers_create(c->bucket_alloc, r);
         APR_BRIGADE_INSERT_TAIL(stream->in_buffer, b);
@@ -368,7 +369,7 @@ static void set_policy_for(h2_stream *st
     r->serialize = h2_config_geti(stream->session->config, H2_CONF_SER_HEADERS);
 }
 
-apr_status_t h2_stream_send_frame(h2_stream *stream, int ftype, int flags)
+apr_status_t h2_stream_send_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
 {
     apr_status_t status = APR_SUCCESS;
     int new_state, eos = 0;
@@ -380,7 +381,9 @@ apr_status_t h2_stream_send_frame(h2_str
         AP_DEBUG_ASSERT(new_state > S_XXX);
         return transit(stream, new_state);
     }
-    
+
+    ++stream->out_frames;
+    stream->out_frame_octets += frame_len;
     switch (ftype) {
         case NGHTTP2_DATA:
             eos = (flags & NGHTTP2_FLAG_END_STREAM);
@@ -394,7 +397,7 @@ apr_status_t h2_stream_send_frame(h2_str
                 /* start pushed stream */
                 ap_assert(stream->request == NULL);
                 ap_assert(stream->rtmp != NULL);
-                status = h2_request_end_headers(stream->rtmp, stream->pool, 1);
+                status = h2_request_end_headers(stream->rtmp, stream->pool, 1, 0);
                 if (status != APR_SUCCESS) {
                     return status;
                 }
@@ -415,7 +418,7 @@ apr_status_t h2_stream_send_frame(h2_str
     return status;
 }
 
-apr_status_t h2_stream_recv_frame(h2_stream *stream, int ftype, int flags)
+apr_status_t h2_stream_recv_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
 {
     apr_status_t status = APR_SUCCESS;
     int new_state, eos = 0;
@@ -440,6 +443,7 @@ apr_status_t h2_stream_recv_frame(h2_str
                 if (!eos) {
                     h2_stream_rst(stream, H2_ERR_PROTOCOL_ERROR);
                 }
+                stream->in_trailer_octets += frame_len;
             }
             else {
                 /* request HEADER */
@@ -451,7 +455,7 @@ apr_status_t h2_stream_recv_frame(h2_str
                      * to abort the connection here, since this is clearly a protocol error */
                     return APR_EINVAL;
                 }
-                status = h2_request_end_headers(stream->rtmp, stream->pool, eos);
+                status = h2_request_end_headers(stream->rtmp, stream->pool, eos, frame_len);
                 if (status != APR_SUCCESS) {
                     return status;
                 }
@@ -628,7 +632,7 @@ apr_status_t h2_stream_set_request_rec(h
         stream->rtmp = req;
         /* simulate the frames that led to this */
         return h2_stream_recv_frame(stream, NGHTTP2_HEADERS, 
-                                    NGHTTP2_FLAG_END_STREAM);
+                                    NGHTTP2_FLAG_END_STREAM, 0);
     }
     return status;
 }
Index: httpd-2.4.23/modules/http2/h2_stream.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_stream.h	2018-10-02 09:36:09.203205597 +0200
+++ httpd-2.4.23/modules/http2/h2_stream.h	2018-10-02 09:36:09.243205805 +0200
@@ -95,10 +95,13 @@ struct h2_stream {
     struct h2_task *task;       /* assigned task to fullfill request */
     
     const h2_priority *pref_priority; /* preferred priority for this stream */
+    apr_off_t out_frames;       /* # of frames sent out */
+    apr_off_t out_frame_octets; /* # of RAW frame octets sent out */
     apr_off_t out_data_frames;  /* # of DATA frames sent */
     apr_off_t out_data_octets;  /* # of DATA octets (payload) sent */
     apr_off_t in_data_frames;   /* # of DATA frames received */
     apr_off_t in_data_octets;   /* # of DATA octets (payload) received */
+    apr_off_t in_trailer_octets; /* # of HEADER octets (payload) received in trailers */
     
     h2_stream_monitor *monitor; /* optional monitor for stream states */
 };
@@ -195,8 +198,8 @@ apr_status_t h2_stream_add_header(h2_str
                                   const char *name, size_t nlen,
                                   const char *value, size_t vlen);
 
-apr_status_t h2_stream_send_frame(h2_stream *stream, int frame_type, int flags);
-apr_status_t h2_stream_recv_frame(h2_stream *stream, int frame_type, int flags);
+apr_status_t h2_stream_send_frame(h2_stream *stream, int frame_type, int flags, size_t frame_len);
+apr_status_t h2_stream_recv_frame(h2_stream *stream, int frame_type, int flags, size_t frame_len);
 
 /*
  * Process a frame of received DATA.
Index: httpd-2.4.23/modules/http2/h2_switch.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_switch.c	2018-10-02 09:36:09.123205178 +0200
+++ httpd-2.4.23/modules/http2/h2_switch.c	2018-10-02 09:36:09.243205805 +0200
@@ -169,13 +169,12 @@ static int h2_protocol_switch(conn_rec *
                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088)
                               "session setup");
                 h2_ctx_clear(c);
-                return status;
+                return !OK;
             }
             
             h2_conn_run(ctx, c);
-            return DONE;
         }
-        return DONE;
+        return OK;
     }
     
     return DECLINED;
Index: httpd-2.4.23/modules/http2/h2_task.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_task.c	2018-10-02 09:36:09.127205199 +0200
+++ httpd-2.4.23/modules/http2/h2_task.c	2018-10-02 09:36:09.243205805 +0200
@@ -674,7 +674,14 @@ static apr_status_t h2_task_process_requ
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                       "h2_task(%s): start process_request", task->id);
     
+        /* Add the raw bytes of the request (e.g. header frame lengths to
+         * the logio for this request. */
+        if (req->raw_bytes && h2_task_logio_add_bytes_in) {
+            h2_task_logio_add_bytes_in(c, req->raw_bytes);
+        }
+        
         ap_process_request(r);
+        
         if (task->frozen) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                           "h2_task(%s): process_request frozen", task->id);
Index: httpd-2.4.23/modules/http2/h2_version.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_version.h	2018-10-02 09:36:09.127205199 +0200
+++ httpd-2.4.23/modules/http2/h2_version.h	2018-10-02 09:36:09.243205805 +0200
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.10.12"
+#define MOD_HTTP2_VERSION "1.11.0"
 
 /**
  * @macro
@@ -34,7 +34,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 0x010a0b
+#define MOD_HTTP2_VERSION_NUM 0x010b00
 
 
 #endif /* mod_h2_h2_version_h */
Index: httpd-2.4.23/modules/http2/h2_workers.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/h2_workers.c	2018-10-02 09:36:09.127205199 +0200
+++ httpd-2.4.23/modules/http2/h2_workers.c	2018-10-02 09:36:09.243205805 +0200
@@ -305,7 +305,18 @@ h2_workers *h2_workers_create(server_rec
     workers->max_workers = max_workers;
     workers->max_idle_secs = (idle_secs > 0)? idle_secs : 10;
 
-    status = h2_fifo_create(&workers->mplxs, pool, 2 * workers->max_workers);
+    /* FIXME: the fifo set we use here has limited capacity. Once the
+     * set is full, connections with new requests do a wait. Unfortunately,
+     * we have optimizations in place there that makes such waiting "unfair"
+     * in the sense that it may take connections a looong time to get scheduled.
+     *
+     * Need to rewrite this to use one of our double-linked lists and a mutex
+     * to have unlimited capacity and fair scheduling.
+     *
+     * For now, we just make enough room to have many connections inside one
+     * process.
+     */
+    status = h2_fifo_set_create(&workers->mplxs, pool, 8 * 1024);
     if (status != APR_SUCCESS) {
         return NULL;
     }
Index: httpd-2.4.23/modules/http2/mod_http2.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/mod_http2.c	2018-10-02 09:36:09.127205199 +0200
+++ httpd-2.4.23/modules/http2/mod_http2.c	2018-10-02 09:36:09.243205805 +0200
@@ -52,7 +52,10 @@ AP_DECLARE_MODULE(http2) = {
     h2_config_create_svr, /* func to create per server config */
     h2_config_merge_svr,  /* func to merge per server config */
     h2_cmds,              /* command handlers */
-    h2_hooks
+    h2_hooks,
+#if defined(AP_MODULE_FLAG_NONE)
+    AP_MODULE_FLAG_ALWAYS_MERGE
+#endif
 };
 
 static int h2_h2_fixups(request_rec *r);
@@ -189,6 +192,11 @@ static void http2_req_engine_done(h2_req
     h2_mplx_req_engine_done(ngn, r_conn, status);
 }
 
+static void http2_get_num_workers(server_rec *s, int *minw, int *maxw)
+{
+    h2_get_num_workers(s, minw, maxw);
+}
+
 /* Runs once per created child process. Perform any process 
  * related initionalization here.
  */
@@ -214,6 +222,7 @@ static void h2_hooks(apr_pool_t *pool)
     APR_REGISTER_OPTIONAL_FN(http2_req_engine_push);
     APR_REGISTER_OPTIONAL_FN(http2_req_engine_pull);
     APR_REGISTER_OPTIONAL_FN(http2_req_engine_done);
+    APR_REGISTER_OPTIONAL_FN(http2_get_num_workers);
 
     ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool, "installing hooks");
     
Index: httpd-2.4.23/modules/http2/mod_http2.h
===================================================================
--- httpd-2.4.23.orig/modules/http2/mod_http2.h	2018-10-02 09:36:09.127205199 +0200
+++ httpd-2.4.23/modules/http2/mod_http2.h	2018-10-02 09:36:09.243205805 +0200
@@ -92,4 +92,9 @@ APR_DECLARE_OPTIONAL_FN(void,
                         http2_req_engine_done, (h2_req_engine *engine, 
                                                 conn_rec *rconn,
                                                 apr_status_t status));
+
+APR_DECLARE_OPTIONAL_FN(void,
+                        http2_get_num_workers, (server_rec *s,
+                                                int *minw, int *max));
+
 #endif
Index: httpd-2.4.23/modules/http2/mod_proxy_http2.c
===================================================================
--- httpd-2.4.23.orig/modules/http2/mod_proxy_http2.c	2018-10-02 09:36:09.127205199 +0200
+++ httpd-2.4.23/modules/http2/mod_proxy_http2.c	2018-10-02 09:36:09.243205805 +0200
@@ -37,7 +37,10 @@ AP_DECLARE_MODULE(proxy_http2) = {
     NULL,              /* create per-server config structure */
     NULL,              /* merge per-server config structures */
     NULL,              /* command apr_table_t */
-    register_hook      /* register hooks */
+    register_hook,     /* register hooks */
+#if defined(AP_MODULE_FLAG_NONE)
+    AP_MODULE_FLAG_ALWAYS_MERGE
+#endif
 };
 
 /* Optional functions from mod_http2 */
@@ -576,9 +579,9 @@ run_connect:
     
     /* Step Three: Create conn_rec for the socket we have open now. */
     if (!ctx->p_conn->connection) {
-        if ((status = ap_proxy_connection_create(ctx->proxy_func, ctx->p_conn,
-                                                 ctx->owner, 
-                                                 ctx->server)) != OK) {
+        status = ap_proxy_connection_create_ex(ctx->proxy_func,
+                                               ctx->p_conn, ctx->rbase);
+        if (status != OK) {
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03353)
                           "setup new connection: is_ssl=%d %s %s %s", 
                           ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, 
Index: httpd-2.4.23/include/httpd.h
===================================================================
--- httpd-2.4.23.orig/include/httpd.h	2018-10-02 09:36:08.931204176 +0200
+++ httpd-2.4.23/include/httpd.h	2018-10-02 09:36:09.235205763 +0200
@@ -568,6 +568,10 @@ AP_DECLARE(const char *) ap_get_server_b
                                     ((x) == HTTP_INTERNAL_SERVER_ERROR) || \
                                     ((x) == HTTP_SERVICE_UNAVAILABLE) || \
                                     ((x) == HTTP_NOT_IMPLEMENTED))
+
+/** does the status imply header only response (i.e. never w/ a body)? */
+#define AP_STATUS_IS_HEADER_ONLY(x) ((x) == HTTP_NO_CONTENT || \
+                                     (x) == HTTP_NOT_MODIFIED)
 /** @} */
 
 /**
