From a6c8c61510eea98f31961e8220bce4f07f928fd2 Mon Sep 17 00:00:00 2001
From: Stefan Eissing <icing@apache.org>
Date: Wed, 13 Mar 2019 15:00:57 +0000
Subject: [PATCH 4/5] Merge of
 1849296,1852038,1852101,1852339,1853171,1853967,1854365,1854963,1854964,1855295,1855411
 from trunk:

  *) mod_http2: when SSL renegotiation is inhibited and a 403 ErrorDocument is
     in play, the proper HTTP/2 stream reset did not trigger with H2_ERR_HTTP_1_1_REQUIRED.
     Fixed. [Michael Kaufmann]

  *) mod_http2: new configuration directive: `H2Padding numbits` to control
     padding of HTTP/2 payload frames. 'numbits' is a number from 0-8,
     controlling the range of padding bytes added to a frame. The actual number
     added is chosen randomly per frame. This applies to HEADERS, DATA and PUSH_PROMISE
     frames equally. The default continues to be 0, e.g. no padding. [Stefan Eissing]

  *) mod_http2: ripping out all the h2_req_engine internal features now that mod_proxy_http2
     has no more need for it. Optional functions are still declared but no longer implemented.
     While previous mod_proxy_http2 will work with this, it is recommeneded to run the matching
     versions of both modules. [Stefan Eissing]

  *) mod_proxy_http2: changed mod_proxy_http2 implementation and fixed several bugs which
     resolve PR63170. The proxy module does now a single h2 request on the (reused)
     connection and returns. [Stefan Eissing]

  *) mod_http2/mod_proxy_http2: proxy_http2 checks correct master connection aborted status
     to trigger immediate shutdown of backend connections. This is now always signalled
     by mod_http2 when the the session is being released.
     proxy_http2 now only sends a PING frame to the backend when there is not already one
     in flight. [Stefan Eissing]

  *) mod_proxy_http2: fixed an issue where a proxy_http2 handler entered an infinite
     loop when encountering certain errors on the backend connection.
     See <https://bz.apache.org/bugzilla/show_bug.cgi?id=63170>. [Stefan Eissing]

  *) mod_http2: Configuration directives H2Push and H2Upgrade can now be specified per
     Location/Directory, e.g. disabling PUSH for a specific set of resources. [Stefan Eissing]

  *) mod_http2: HEAD requests to some module such as mod_cgid caused the stream to
     terminate improperly and cause a HTTP/2 PROTOCOL_ERROR.
     Fixes <https://github.com/icing/mod_h2/issues/167>. [Michael Kaufmann]

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1855431 13f79535-47bb-0310-9956-ffa450edef68
---
 modules/http2/config2.m4         |   1 -
 modules/http2/h2.h               |   7 +-
 modules/http2/h2_alt_svc.c       |  12 +-
 modules/http2/h2_config.c        | 662 ++++++++++++++++++++++++++++-----------
 modules/http2/h2_config.h        |  63 ++--
 modules/http2/h2_conn.c          |  30 +-
 modules/http2/h2_conn.h          |   8 +-
 modules/http2/h2_conn_io.c       |  24 +-
 modules/http2/h2_conn_io.h       |   3 +-
 modules/http2/h2_ctx.c           |  33 +-
 modules/http2/h2_ctx.h           |  13 +-
 modules/http2/h2_filter.c        |  43 ++-
 modules/http2/h2_from_h1.c       |  12 +-
 modules/http2/h2_h2.c            |  71 ++---
 modules/http2/h2_h2.h            |  16 +-
 modules/http2/h2_headers.c       |  31 +-
 modules/http2/h2_mplx.c          | 235 ++------------
 modules/http2/h2_mplx.h          |  32 +-
 modules/http2/h2_ngn_shed.c      | 392 -----------------------
 modules/http2/h2_ngn_shed.h      |  79 -----
 modules/http2/h2_proxy_session.c |  57 +---
 modules/http2/h2_proxy_session.h |   3 -
 modules/http2/h2_request.c       |  33 +-
 modules/http2/h2_session.c       | 107 ++++---
 modules/http2/h2_session.h       |  19 +-
 modules/http2/h2_stream.c        |   9 +-
 modules/http2/h2_switch.c        |  13 +-
 modules/http2/h2_task.c          |  84 +----
 modules/http2/h2_task.h          |  14 +-
 modules/http2/mod_http2.c        |  49 +--
 modules/http2/mod_http2.h        |  46 +--
 modules/http2/mod_proxy_http2.c  | 384 ++++++-----------------
 32 files changed, 904 insertions(+), 1681 deletions(-)

diff --git a/modules/http2/config2.m4 b/modules/http2/config2.m4
index e8cefe3..5f49adf 100644
--- a/modules/http2/config2.m4
+++ b/modules/http2/config2.m4
@@ -31,7 +31,6 @@ h2_from_h1.lo dnl
 h2_h2.lo dnl
 h2_headers.lo dnl
 h2_mplx.lo dnl
-h2_ngn_shed.lo dnl
 h2_push.lo dnl
 h2_request.lo dnl
 h2_session.lo dnl
diff --git a/modules/http2/h2.h b/modules/http2/h2.h
index 38b4019..e057d66 100644
--- a/modules/http2/h2.h
+++ b/modules/http2/h2.h
@@ -48,12 +48,12 @@ extern const char *H2_MAGIC_TOKEN;
 #define H2_HEADER_PATH_LEN   5
 #define H2_CRLF             "\r\n"
 
-/* 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
  
+/* Max data size to write so it fits inside a TLS record */
+#define H2_DATA_CHUNK_SIZE          ((16*1024) - 100 - H2_FRAME_HDR_LEN) 
+
 /* Maximum number of padding bytes in a frame, rfc7540 */
 #define H2_MAX_PADLEN               256
 /* Initial default window size, RFC 7540 ch. 6.5.2 */
@@ -162,5 +162,6 @@ typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx);
 #define H2_FILTER_DEBUG_NOTE    "http2-debug"
 #define H2_HDR_CONFORMANCE      "http2-hdr-conformance"
 #define H2_HDR_CONFORMANCE_UNSAFE      "unsafe"
+#define H2_PUSH_MODE_NOTE       "http2-push-mode"
 
 #endif /* defined(__mod_h2__h2__) */
diff --git a/modules/http2/h2_alt_svc.c b/modules/http2/h2_alt_svc.c
index 295a16d..2c3253c 100644
--- a/modules/http2/h2_alt_svc.c
+++ b/modules/http2/h2_alt_svc.c
@@ -75,7 +75,7 @@ h2_alt_svc *h2_alt_svc_parse(const char *s, apr_pool_t *pool)
 
 static int h2_alt_svc_handler(request_rec *r)
 {
-    const h2_config *cfg;
+    apr_array_header_t *alt_svcs;
     int i;
     
     if (r->connection->keepalives > 0) {
@@ -87,8 +87,8 @@ static int h2_alt_svc_handler(request_rec *r)
         return DECLINED;
     }
     
-    cfg = h2_config_sget(r->server);
-    if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) {
+    alt_svcs = h2_config_alt_svcs(r);
+    if (r->hostname && alt_svcs && alt_svcs->nelts > 0) {
         const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used");
         if (!alt_svc_used) {
             /* We have alt-svcs defined and client is not already using
@@ -99,7 +99,7 @@ static int h2_alt_svc_handler(request_rec *r)
             const char *alt_svc = "";
             const char *svc_ma = "";
             int secure = h2_h2_is_tls(r->connection);
-            int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE);
+            int ma = h2_config_rgeti(r, H2_CONF_ALT_SVC_MAX_AGE);
             if (ma >= 0) {
                 svc_ma = apr_psprintf(r->pool, "; ma=%d", ma);
             }
@@ -107,8 +107,8 @@ static int h2_alt_svc_handler(request_rec *r)
                           "h2_alt_svc: announce %s for %s:%d", 
                           (secure? "secure" : "insecure"), 
                           r->hostname, (int)r->server->port);
-            for (i = 0; i < cfg->alt_svcs->nelts; ++i) {
-                h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i);
+            for (i = 0; i < alt_svcs->nelts; ++i) {
+                h2_alt_svc *as = h2_alt_svc_IDX(alt_svcs, i);
                 const char *ahost = as->host;
                 if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) {
                     ahost = NULL;
diff --git a/modules/http2/h2_config.c b/modules/http2/h2_config.c
index 8766355..29a0b55 100644
--- a/modules/http2/h2_config.c
+++ b/modules/http2/h2_config.c
@@ -42,6 +42,55 @@
 #define H2_CONFIG_GET(a, b, n) \
     (((a)->n == DEF_VAL)? (b) : (a))->n
 
+#define H2_CONFIG_SET(a, n, v) \
+    ((a)->n = v)
+
+#define CONFIG_CMD_SET(cmd,dir,var,val) \
+    h2_config_seti(((cmd)->path? (dir) : NULL), h2_config_sget((cmd)->server), var, val)
+
+#define CONFIG_CMD_SET64(cmd,dir,var,val) \
+    h2_config_seti64(((cmd)->path? (dir) : NULL), h2_config_sget((cmd)->server), var, val)
+
+/* Apache httpd module configuration for h2. */
+typedef struct h2_config {
+    const char *name;
+    int h2_max_streams;           /* max concurrent # streams (http2) */
+    int h2_window_size;           /* stream window size (http2) */
+    int min_workers;              /* min # of worker threads/child */
+    int max_workers;              /* max # of worker threads/child */
+    int max_worker_idle_secs;     /* max # of idle seconds for worker */
+    int stream_max_mem_size;      /* max # bytes held in memory/stream */
+    apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
+    int alt_svc_max_age;          /* seconds clients can rely on alt-svc info*/
+    int serialize_headers;        /* Use serialized HTTP/1.1 headers for 
+                                     processing, better compatibility */
+    int h2_direct;                /* if mod_h2 is active directly */
+    int modern_tls_only;          /* Accept only modern TLS in HTTP/2 connections */  
+    int h2_upgrade;               /* Allow HTTP/1 upgrade to h2/h2c */
+    apr_int64_t tls_warmup_size;  /* Amount of TLS data to send before going full write size */
+    int tls_cooldown_secs;        /* Seconds of idle time before going back to small TLS records */
+    int h2_push;                  /* if HTTP/2 server push is enabled */
+    struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
+    
+    int push_diary_size;          /* # of entries in push diary */
+    int copy_files;               /* if files shall be copied vs setaside on output */
+    apr_array_header_t *push_list;/* list of h2_push_res configurations */
+    int early_hints;              /* support status code 103 */
+    int padding_bits;
+    int padding_always;
+} h2_config;
+
+typedef struct h2_dir_config {
+    const char *name;
+    apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
+    int alt_svc_max_age;          /* seconds clients can rely on alt-svc info*/
+    int h2_upgrade;               /* Allow HTTP/1 upgrade to h2/h2c */
+    int h2_push;                  /* if HTTP/2 server push is enabled */
+    apr_array_header_t *push_list;/* list of h2_push_res configurations */
+    int early_hints;              /* support status code 103 */
+} h2_dir_config;
+
+
 static h2_config defconf = {
     "default",
     100,                    /* max_streams */
@@ -64,6 +113,18 @@ static h2_config defconf = {
     0,                      /* copy files across threads */
     NULL,                   /* push list */
     0,                      /* early hints, http status 103 */
+    0,                      /* padding bits */
+    1,                      /* padding always */
+};
+
+static h2_dir_config defdconf = {
+    "default",
+    NULL,                   /* no alt-svcs */
+    -1,                     /* alt-svc max age */
+    -1,                     /* HTTP/1 Upgrade support */
+    -1,                     /* HTTP/2 server push enabled */
+    NULL,                   /* push list */
+    -1,                     /* early hints, http status 103 */
 };
 
 void h2_config_init(apr_pool_t *pool)
@@ -71,12 +132,10 @@ void h2_config_init(apr_pool_t *pool)
     (void)pool;
 }
 
-static void *h2_config_create(apr_pool_t *pool,
-                              const char *prefix, const char *x)
+void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
 {
     h2_config *conf = (h2_config *)apr_pcalloc(pool, sizeof(h2_config));
-    const char *s = x? x : "unknown";
-    char *name = apr_pstrcat(pool, prefix, "[", s, "]", NULL);
+    char *name = apr_pstrcat(pool, "srv[", s->defn_name, "]", NULL);
     
     conf->name                 = name;
     conf->h2_max_streams       = DEF_VAL;
@@ -98,19 +157,11 @@ static void *h2_config_create(apr_pool_t *pool,
     conf->copy_files           = DEF_VAL;
     conf->push_list            = NULL;
     conf->early_hints          = DEF_VAL;
+    conf->padding_bits         = DEF_VAL;
+    conf->padding_always       = DEF_VAL;
     return conf;
 }
 
-void *h2_config_create_svr(apr_pool_t *pool, server_rec *s)
-{
-    return h2_config_create(pool, "srv", s->defn_name);
-}
-
-void *h2_config_create_dir(apr_pool_t *pool, char *x)
-{
-    return h2_config_create(pool, "dir", x);
-}
-
 static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
 {
     h2_config *base = (h2_config *)basev;
@@ -149,25 +200,52 @@ static void *h2_config_merge(apr_pool_t *pool, void *basev, void *addv)
         n->push_list        = add->push_list? add->push_list : base->push_list;
     }
     n->early_hints          = H2_CONFIG_GET(add, base, early_hints);
+    n->padding_bits         = H2_CONFIG_GET(add, base, padding_bits);
+    n->padding_always       = H2_CONFIG_GET(add, base, padding_always);
     return n;
 }
 
-void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv)
+void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv)
 {
     return h2_config_merge(pool, basev, addv);
 }
 
-void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv)
+void *h2_config_create_dir(apr_pool_t *pool, char *x)
 {
-    return h2_config_merge(pool, basev, addv);
+    h2_dir_config *conf = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config));
+    const char *s = x? x : "unknown";
+    char *name = apr_pstrcat(pool, "dir[", s, "]", NULL);
+    
+    conf->name                 = name;
+    conf->alt_svc_max_age      = DEF_VAL;
+    conf->h2_upgrade           = DEF_VAL;
+    conf->h2_push              = DEF_VAL;
+    conf->early_hints          = DEF_VAL;
+    return conf;
 }
 
-int h2_config_geti(const h2_config *conf, h2_config_var_t var)
+void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv)
 {
-    return (int)h2_config_geti64(conf, var);
+    h2_dir_config *base = (h2_dir_config *)basev;
+    h2_dir_config *add = (h2_dir_config *)addv;
+    h2_dir_config *n = (h2_dir_config *)apr_pcalloc(pool, sizeof(h2_dir_config));
+
+    n->name = apr_pstrcat(pool, "merged[", add->name, ", ", base->name, "]", NULL);
+    n->alt_svcs             = add->alt_svcs? add->alt_svcs : base->alt_svcs;
+    n->alt_svc_max_age      = H2_CONFIG_GET(add, base, alt_svc_max_age);
+    n->h2_upgrade           = H2_CONFIG_GET(add, base, h2_upgrade);
+    n->h2_push              = H2_CONFIG_GET(add, base, h2_push);
+    if (add->push_list && base->push_list) {
+        n->push_list        = apr_array_append(pool, base->push_list, add->push_list);
+    }
+    else {
+        n->push_list        = add->push_list? add->push_list : base->push_list;
+    }
+    n->early_hints          = H2_CONFIG_GET(add, base, early_hints);
+    return n;
 }
 
-apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var)
+static apr_int64_t h2_srv_config_geti64(const h2_config *conf, h2_config_var_t var)
 {
     switch(var) {
         case H2_CONF_MAX_STREAMS:
@@ -191,7 +269,8 @@ apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var)
         case H2_CONF_UPGRADE:
             return H2_CONFIG_GET(conf, &defconf, h2_upgrade);
         case H2_CONF_DIRECT:
-            return H2_CONFIG_GET(conf, &defconf, h2_direct);
+            return 1;
+            /*return H2_CONFIG_GET(conf, &defconf, h2_direct);*/
         case H2_CONF_TLS_WARMUP_SIZE:
             return H2_CONFIG_GET(conf, &defconf, tls_warmup_size);
         case H2_CONF_TLS_COOLDOWN_SECS:
@@ -204,12 +283,93 @@ apr_int64_t h2_config_geti64(const h2_config *conf, h2_config_var_t var)
             return H2_CONFIG_GET(conf, &defconf, copy_files);
         case H2_CONF_EARLY_HINTS:
             return H2_CONFIG_GET(conf, &defconf, early_hints);
+        case H2_CONF_PADDING_BITS:
+            return H2_CONFIG_GET(conf, &defconf, padding_bits);
+        case H2_CONF_PADDING_ALWAYS:
+            return H2_CONFIG_GET(conf, &defconf, padding_always);
         default:
             return DEF_VAL;
     }
 }
 
-const h2_config *h2_config_sget(server_rec *s)
+static void h2_srv_config_seti(h2_config *conf, h2_config_var_t var, int val)
+{
+    switch(var) {
+        case H2_CONF_MAX_STREAMS:
+            H2_CONFIG_SET(conf, h2_max_streams, val);
+            break;
+        case H2_CONF_WIN_SIZE:
+            H2_CONFIG_SET(conf, h2_window_size, val);
+            break;
+        case H2_CONF_MIN_WORKERS:
+            H2_CONFIG_SET(conf, min_workers, val);
+            break;
+        case H2_CONF_MAX_WORKERS:
+            H2_CONFIG_SET(conf, max_workers, val);
+            break;
+        case H2_CONF_MAX_WORKER_IDLE_SECS:
+            H2_CONFIG_SET(conf, max_worker_idle_secs, val);
+            break;
+        case H2_CONF_STREAM_MAX_MEM:
+            H2_CONFIG_SET(conf, stream_max_mem_size, val);
+            break;
+        case H2_CONF_ALT_SVC_MAX_AGE:
+            H2_CONFIG_SET(conf, alt_svc_max_age, val);
+            break;
+        case H2_CONF_SER_HEADERS:
+            H2_CONFIG_SET(conf, serialize_headers, val);
+            break;
+        case H2_CONF_MODERN_TLS_ONLY:
+            H2_CONFIG_SET(conf, modern_tls_only, val);
+            break;
+        case H2_CONF_UPGRADE:
+            H2_CONFIG_SET(conf, h2_upgrade, val);
+            break;
+        case H2_CONF_DIRECT:
+            H2_CONFIG_SET(conf, h2_direct, val);
+            break;
+        case H2_CONF_TLS_WARMUP_SIZE:
+            H2_CONFIG_SET(conf, tls_warmup_size, val);
+            break;
+        case H2_CONF_TLS_COOLDOWN_SECS:
+            H2_CONFIG_SET(conf, tls_cooldown_secs, val);
+            break;
+        case H2_CONF_PUSH:
+            H2_CONFIG_SET(conf, h2_push, val);
+            break;
+        case H2_CONF_PUSH_DIARY_SIZE:
+            H2_CONFIG_SET(conf, push_diary_size, val);
+            break;
+        case H2_CONF_COPY_FILES:
+            H2_CONFIG_SET(conf, copy_files, val);
+            break;
+        case H2_CONF_EARLY_HINTS:
+            H2_CONFIG_SET(conf, early_hints, val);
+            break;
+        case H2_CONF_PADDING_BITS:
+            H2_CONFIG_SET(conf, padding_bits, val);
+            break;
+        case H2_CONF_PADDING_ALWAYS:
+            H2_CONFIG_SET(conf, padding_always, val);
+            break;
+        default:
+            break;
+    }
+}
+
+static void h2_srv_config_seti64(h2_config *conf, h2_config_var_t var, apr_int64_t val)
+{
+    switch(var) {
+        case H2_CONF_TLS_WARMUP_SIZE:
+            H2_CONFIG_SET(conf, tls_warmup_size, val);
+            break;
+        default:
+            h2_srv_config_seti(conf, var, (int)val);
+            break;
+    }
+}
+
+static h2_config *h2_config_sget(server_rec *s)
 {
     h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, 
                                                        &http2_module);
@@ -217,9 +377,162 @@ const h2_config *h2_config_sget(server_rec *s)
     return cfg;
 }
 
-const struct h2_priority *h2_config_get_priority(const h2_config *conf, 
-                                                 const char *content_type)
+static const h2_dir_config *h2_config_rget(request_rec *r)
+{
+    h2_dir_config *cfg = (h2_dir_config *)ap_get_module_config(r->per_dir_config, 
+                                                               &http2_module);
+    ap_assert(cfg);
+    return cfg;
+}
+
+static apr_int64_t h2_dir_config_geti64(const h2_dir_config *conf, h2_config_var_t var)
+{
+    switch(var) {
+        case H2_CONF_ALT_SVC_MAX_AGE:
+            return H2_CONFIG_GET(conf, &defdconf, alt_svc_max_age);
+        case H2_CONF_UPGRADE:
+            return H2_CONFIG_GET(conf, &defdconf, h2_upgrade);
+        case H2_CONF_PUSH:
+            return H2_CONFIG_GET(conf, &defdconf, h2_push);
+        case H2_CONF_EARLY_HINTS:
+            return H2_CONFIG_GET(conf, &defdconf, early_hints);
+
+        default:
+            return DEF_VAL;
+    }
+}
+
+static void h2_config_seti(h2_dir_config *dconf, h2_config *conf, h2_config_var_t var, int val)
+{
+    int set_srv = !dconf;
+    if (dconf) {
+        switch(var) {
+            case H2_CONF_ALT_SVC_MAX_AGE:
+                H2_CONFIG_SET(dconf, alt_svc_max_age, val);
+                break;
+            case H2_CONF_UPGRADE:
+                H2_CONFIG_SET(dconf, h2_upgrade, val);
+                break;
+            case H2_CONF_PUSH:
+                H2_CONFIG_SET(dconf, h2_push, val);
+                break;
+            case H2_CONF_EARLY_HINTS:
+                H2_CONFIG_SET(dconf, early_hints, val);
+                break;
+            default:
+                /* not handled in dir_conf */
+                set_srv = 1;
+                break;
+        }
+    }
+
+    if (set_srv) {
+        h2_srv_config_seti(conf, var, val);
+    }
+}
+
+static void h2_config_seti64(h2_dir_config *dconf, h2_config *conf, h2_config_var_t var, apr_int64_t val)
+{
+    int set_srv = !dconf;
+    if (dconf) {
+        switch(var) {
+            default:
+                /* not handled in dir_conf */
+                set_srv = 1;
+                break;
+        }
+    }
+
+    if (set_srv) {
+        h2_srv_config_seti64(conf, var, val);
+    }
+}
+
+static const h2_config *h2_config_get(conn_rec *c)
+{
+    h2_ctx *ctx = h2_ctx_get(c, 0);
+    
+    if (ctx) {
+        if (ctx->config) {
+            return ctx->config;
+        }
+        else if (ctx->server) {
+            ctx->config = h2_config_sget(ctx->server);
+            return ctx->config;
+        }
+    }
+    
+    return h2_config_sget(c->base_server);
+}
+
+int h2_config_cgeti(conn_rec *c, h2_config_var_t var)
+{
+    return (int)h2_srv_config_geti64(h2_config_get(c), var);
+}
+
+apr_int64_t h2_config_cgeti64(conn_rec *c, h2_config_var_t var)
+{
+    return h2_srv_config_geti64(h2_config_get(c), var);
+}
+
+int h2_config_sgeti(server_rec *s, h2_config_var_t var)
+{
+    return (int)h2_srv_config_geti64(h2_config_sget(s), var);
+}
+
+apr_int64_t h2_config_sgeti64(server_rec *s, h2_config_var_t var)
+{
+    return h2_srv_config_geti64(h2_config_sget(s), var);
+}
+
+int h2_config_geti(request_rec *r, server_rec *s, h2_config_var_t var)
+{
+    return (int)h2_config_geti64(r, s, var);
+}
+
+apr_int64_t h2_config_geti64(request_rec *r, server_rec *s, h2_config_var_t var)
+{
+    apr_int64_t mode = r? (int)h2_dir_config_geti64(h2_config_rget(r), var) : DEF_VAL;
+    return (mode != DEF_VAL)? mode : h2_config_sgeti64(s, var);
+}
+
+int h2_config_rgeti(request_rec *r, h2_config_var_t var)
+{
+    return h2_config_geti(r, r->server, var);
+}
+
+apr_int64_t h2_config_rgeti64(request_rec *r, h2_config_var_t var)
+{
+    return h2_config_geti64(r, r->server, var);
+}
+
+apr_array_header_t *h2_config_push_list(request_rec *r)
+{
+    const h2_config *sconf;
+    const h2_dir_config *conf = h2_config_rget(r);
+    
+    if (conf && conf->push_list) {
+        return conf->push_list;
+    }
+    sconf = h2_config_sget(r->server); 
+    return sconf? sconf->push_list : NULL;
+}
+
+apr_array_header_t *h2_config_alt_svcs(request_rec *r)
 {
+    const h2_config *sconf;
+    const h2_dir_config *conf = h2_config_rget(r);
+    
+    if (conf && conf->alt_svcs) {
+        return conf->alt_svcs;
+    }
+    sconf = h2_config_sget(r->server); 
+    return sconf? sconf->alt_svcs : NULL;
+}
+
+const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *content_type)
+{
+    const h2_config *conf = h2_config_get(c);
     if (content_type && conf->priorities) {
         size_t len = strcspn(content_type, "; \t");
         h2_priority *prio = apr_hash_get(conf->priorities, content_type, len);
@@ -228,166 +541,156 @@ const struct h2_priority *h2_config_get_priority(const h2_config *conf,
     return NULL;
 }
 
-static const char *h2_conf_set_max_streams(cmd_parms *parms,
-                                           void *arg, const char *value)
+static const char *h2_conf_set_max_streams(cmd_parms *cmd,
+                                           void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->h2_max_streams = (int)apr_atoi64(value);
-    (void)arg;
-    if (cfg->h2_max_streams < 1) {
+    apr_int64_t ival = (int)apr_atoi64(value);
+    if (ival < 1) {
         return "value must be > 0";
     }
+    CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_MAX_STREAMS, ival);
     return NULL;
 }
 
-static const char *h2_conf_set_window_size(cmd_parms *parms,
-                                           void *arg, const char *value)
+static const char *h2_conf_set_window_size(cmd_parms *cmd,
+                                           void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->h2_window_size = (int)apr_atoi64(value);
-    (void)arg;
-    if (cfg->h2_window_size < 1024) {
+    int val = (int)apr_atoi64(value);
+    if (val < 1024) {
         return "value must be >= 1024";
     }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_WIN_SIZE, val);
     return NULL;
 }
 
-static const char *h2_conf_set_min_workers(cmd_parms *parms,
-                                           void *arg, const char *value)
+static const char *h2_conf_set_min_workers(cmd_parms *cmd,
+                                           void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->min_workers = (int)apr_atoi64(value);
-    (void)arg;
-    if (cfg->min_workers < 1) {
+    int val = (int)apr_atoi64(value);
+    if (val < 1) {
         return "value must be > 0";
     }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MIN_WORKERS, val);
     return NULL;
 }
 
-static const char *h2_conf_set_max_workers(cmd_parms *parms,
-                                           void *arg, const char *value)
+static const char *h2_conf_set_max_workers(cmd_parms *cmd,
+                                           void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->max_workers = (int)apr_atoi64(value);
-    (void)arg;
-    if (cfg->max_workers < 1) {
+    int val = (int)apr_atoi64(value);
+    if (val < 1) {
         return "value must be > 0";
     }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_WORKERS, val);
     return NULL;
 }
 
-static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *parms,
-                                                    void *arg, const char *value)
+static const char *h2_conf_set_max_worker_idle_secs(cmd_parms *cmd,
+                                                    void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->max_worker_idle_secs = (int)apr_atoi64(value);
-    (void)arg;
-    if (cfg->max_worker_idle_secs < 1) {
+    int val = (int)apr_atoi64(value);
+    if (val < 1) {
         return "value must be > 0";
     }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MAX_WORKER_IDLE_SECS, val);
     return NULL;
 }
 
-static const char *h2_conf_set_stream_max_mem_size(cmd_parms *parms,
-                                                   void *arg, const char *value)
+static const char *h2_conf_set_stream_max_mem_size(cmd_parms *cmd,
+                                                   void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    
-    
-    cfg->stream_max_mem_size = (int)apr_atoi64(value);
-    (void)arg;
-    if (cfg->stream_max_mem_size < 1024) {
+    int val = (int)apr_atoi64(value);
+    if (val < 1024) {
         return "value must be >= 1024";
     }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_STREAM_MAX_MEM, val);
     return NULL;
 }
 
-static const char *h2_add_alt_svc(cmd_parms *parms,
-                                  void *arg, const char *value)
+static const char *h2_add_alt_svc(cmd_parms *cmd,
+                                  void *dirconf, const char *value)
 {
     if (value && *value) {
-        h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-        h2_alt_svc *as = h2_alt_svc_parse(value, parms->pool);
+        h2_alt_svc *as = h2_alt_svc_parse(value, cmd->pool);
         if (!as) {
             return "unable to parse alt-svc specifier";
         }
-        if (!cfg->alt_svcs) {
-            cfg->alt_svcs = apr_array_make(parms->pool, 5, sizeof(h2_alt_svc*));
+
+        if (cmd->path) {
+            h2_dir_config *dcfg = (h2_dir_config *)dirconf;
+            if (!dcfg->alt_svcs) {
+                dcfg->alt_svcs = apr_array_make(cmd->pool, 5, sizeof(h2_alt_svc*));
+            }
+            APR_ARRAY_PUSH(dcfg->alt_svcs, h2_alt_svc*) = as;
+        }
+        else {
+            h2_config *cfg = (h2_config *)h2_config_sget(cmd->server);
+            if (!cfg->alt_svcs) {
+                cfg->alt_svcs = apr_array_make(cmd->pool, 5, sizeof(h2_alt_svc*));
+            }
+            APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
         }
-        APR_ARRAY_PUSH(cfg->alt_svcs, h2_alt_svc*) = as;
     }
-    (void)arg;
     return NULL;
 }
 
-static const char *h2_conf_set_alt_svc_max_age(cmd_parms *parms,
-                                               void *arg, const char *value)
+static const char *h2_conf_set_alt_svc_max_age(cmd_parms *cmd,
+                                               void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->alt_svc_max_age = (int)apr_atoi64(value);
-    (void)arg;
+    int val = (int)apr_atoi64(value);
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_ALT_SVC_MAX_AGE, val);
     return NULL;
 }
 
-static const char *h2_conf_set_session_extra_files(cmd_parms *parms,
-                                                   void *arg, const char *value)
+static const char *h2_conf_set_session_extra_files(cmd_parms *cmd,
+                                                   void *dirconf, const char *value)
 {
     /* deprecated, ignore */
-    (void)arg;
+    (void)dirconf;
     (void)value;
-    ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, parms->pool, /* NO LOGNO */
+    ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, cmd->pool, /* NO LOGNO */
                   "H2SessionExtraFiles is obsolete and will be ignored");
     return NULL;
 }
 
-static const char *h2_conf_set_serialize_headers(cmd_parms *parms,
-                                                 void *arg, const char *value)
+static const char *h2_conf_set_serialize_headers(cmd_parms *cmd,
+                                                 void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
     if (!strcasecmp(value, "On")) {
-        cfg->serialize_headers = 1;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_SER_HEADERS, 1);
         return NULL;
     }
     else if (!strcasecmp(value, "Off")) {
-        cfg->serialize_headers = 0;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_SER_HEADERS, 0);
         return NULL;
     }
-    
-    (void)arg;
     return "value must be On or Off";
 }
 
-static const char *h2_conf_set_direct(cmd_parms *parms,
-                                      void *arg, const char *value)
+static const char *h2_conf_set_direct(cmd_parms *cmd,
+                                      void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
     if (!strcasecmp(value, "On")) {
-        cfg->h2_direct = 1;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_DIRECT, 1);
         return NULL;
     }
     else if (!strcasecmp(value, "Off")) {
-        cfg->h2_direct = 0;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_DIRECT, 0);
         return NULL;
     }
-    
-    (void)arg;
     return "value must be On or Off";
 }
 
-static const char *h2_conf_set_push(cmd_parms *parms,
-                                    void *arg, const char *value)
+static const char *h2_conf_set_push(cmd_parms *cmd, void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
     if (!strcasecmp(value, "On")) {
-        cfg->h2_push = 1;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH, 1);
         return NULL;
     }
     else if (!strcasecmp(value, "Off")) {
-        cfg->h2_push = 0;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH, 0);
         return NULL;
     }
-    
-    (void)arg;
     return "value must be On or Off";
 }
 
@@ -447,100 +750,88 @@ static const char *h2_conf_add_push_priority(cmd_parms *cmd, void *_cfg,
     return NULL;
 }
 
-static const char *h2_conf_set_modern_tls_only(cmd_parms *parms,
-                                               void *arg, const char *value)
+static const char *h2_conf_set_modern_tls_only(cmd_parms *cmd,
+                                               void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
     if (!strcasecmp(value, "On")) {
-        cfg->modern_tls_only = 1;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MODERN_TLS_ONLY, 1);
         return NULL;
     }
     else if (!strcasecmp(value, "Off")) {
-        cfg->modern_tls_only = 0;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_MODERN_TLS_ONLY, 0);
         return NULL;
     }
-    
-    (void)arg;
     return "value must be On or Off";
 }
 
-static const char *h2_conf_set_upgrade(cmd_parms *parms,
-                                       void *arg, const char *value)
+static const char *h2_conf_set_upgrade(cmd_parms *cmd,
+                                       void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
     if (!strcasecmp(value, "On")) {
-        cfg->h2_upgrade = 1;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_UPGRADE, 1);
         return NULL;
     }
     else if (!strcasecmp(value, "Off")) {
-        cfg->h2_upgrade = 0;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_UPGRADE, 0);
         return NULL;
     }
-    
-    (void)arg;
     return "value must be On or Off";
 }
 
-static const char *h2_conf_set_tls_warmup_size(cmd_parms *parms,
-                                               void *arg, const char *value)
+static const char *h2_conf_set_tls_warmup_size(cmd_parms *cmd,
+                                               void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->tls_warmup_size = apr_atoi64(value);
-    (void)arg;
+    apr_int64_t val = apr_atoi64(value);
+    CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_TLS_WARMUP_SIZE, val);
     return NULL;
 }
 
-static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *parms,
-                                                 void *arg, const char *value)
+static const char *h2_conf_set_tls_cooldown_secs(cmd_parms *cmd,
+                                                 void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    cfg->tls_cooldown_secs = (int)apr_atoi64(value);
-    (void)arg;
+    apr_int64_t val = (int)apr_atoi64(value);
+    CONFIG_CMD_SET64(cmd, dirconf, H2_CONF_TLS_COOLDOWN_SECS, val);
     return NULL;
 }
 
-static const char *h2_conf_set_push_diary_size(cmd_parms *parms,
-                                               void *arg, const char *value)
+static const char *h2_conf_set_push_diary_size(cmd_parms *cmd,
+                                               void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    (void)arg;
-    cfg->push_diary_size = (int)apr_atoi64(value);
-    if (cfg->push_diary_size < 0) {
+    int val = (int)apr_atoi64(value);
+    if (val < 0) {
         return "value must be >= 0";
     }
-    if (cfg->push_diary_size > 0 && (cfg->push_diary_size & (cfg->push_diary_size-1))) {
+    if (val > 0 && (val & (val-1))) {
         return "value must a power of 2";
     }
-    if (cfg->push_diary_size > (1 << 15)) {
+    if (val > (1 << 15)) {
         return "value must <= 65536";
     }
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PUSH_DIARY_SIZE, val);
     return NULL;
 }
 
-static const char *h2_conf_set_copy_files(cmd_parms *parms,
-                                          void *arg, const char *value)
+static const char *h2_conf_set_copy_files(cmd_parms *cmd,
+                                          void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)arg;
     if (!strcasecmp(value, "On")) {
-        cfg->copy_files = 1;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_COPY_FILES, 1);
         return NULL;
     }
     else if (!strcasecmp(value, "Off")) {
-        cfg->copy_files = 0;
+        CONFIG_CMD_SET(cmd, dirconf, H2_CONF_COPY_FILES, 0);
         return NULL;
     }
-    
-    (void)arg;
     return "value must be On or Off";
 }
 
-static void add_push(apr_pool_t *pool, h2_config *conf, h2_push_res *push)
+static void add_push(apr_array_header_t **plist, apr_pool_t *pool, h2_push_res *push)
 {
     h2_push_res *new;
-    if (!conf->push_list) {
-        conf->push_list = apr_array_make(pool, 10, sizeof(*push));
+    if (!*plist) {
+        *plist = apr_array_make(pool, 10, sizeof(*push));
     }
-    new = apr_array_push(conf->push_list);
+    new = apr_array_push(*plist);
     new->uri_ref = push->uri_ref;
     new->critical = push->critical;
 }
@@ -549,8 +840,6 @@ static const char *h2_conf_add_push_res(cmd_parms *cmd, void *dirconf,
                                         const char *arg1, const char *arg2,
                                         const char *arg3)
 {
-    h2_config *dconf = (h2_config*)dirconf ;
-    h2_config *sconf = (h2_config*)h2_config_sget(cmd->server);
     h2_push_res push;
     const char *last = arg3;
     
@@ -575,42 +864,54 @@ static const char *h2_conf_add_push_res(cmd_parms *cmd, void *dirconf,
         }
     }
 
-    /* server command? set both */
-    if (cmd->path == NULL) {
-        add_push(cmd->pool, sconf, &push);
-        add_push(cmd->pool, dconf, &push);
+    if (cmd->path) {
+        add_push(&(((h2_dir_config*)dirconf)->push_list), cmd->pool, &push);
     }
     else {
-        add_push(cmd->pool, dconf, &push);
+        add_push(&(h2_config_sget(cmd->server)->push_list), cmd->pool, &push);
     }
+    return NULL;
+}
+
+static const char *h2_conf_set_early_hints(cmd_parms *cmd,
+                                           void *dirconf, const char *value)
+{
+    int val;
 
+    if (!strcasecmp(value, "On")) val = 1;
+    else if (!strcasecmp(value, "Off")) val = 0;
+    else return "value must be On or Off";
+    
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_EARLY_HINTS, val);
+    if (cmd->path) {
+        ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, cmd->pool, 
+                            "H2EarlyHints = %d on path %s", val, cmd->path);
+    }
     return NULL;
 }
 
-static const char *h2_conf_set_early_hints(cmd_parms *parms,
-                                           void *arg, const char *value)
+static const char *h2_conf_set_padding(cmd_parms *cmd, void *dirconf, const char *value)
 {
-    h2_config *cfg = (h2_config *)h2_config_sget(parms->server);
-    if (!strcasecmp(value, "On")) {
-        cfg->early_hints = 1;
-        return NULL;
+    int val;
+    
+    val = (int)apr_atoi64(value);
+    if (val < 0) {
+        return "number of bits must be >= 0";
     }
-    else if (!strcasecmp(value, "Off")) {
-        cfg->early_hints = 0;
-        return NULL;
+    if (val > 8) {
+        return "number of bits must be <= 8";
     }
-    
-    (void)arg;
-    return "value must be On or Off";
+    CONFIG_CMD_SET(cmd, dirconf, H2_CONF_PADDING_BITS, val);
+    return NULL;
 }
 
+
 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);    
+    *minw = h2_config_sgeti(s, H2_CONF_MIN_WORKERS);
+    *maxw = h2_config_sgeti(s, H2_CONF_MAX_WORKERS);    
     ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child);
 
     if (*minw <= 0) {
@@ -652,7 +953,7 @@ const command_rec h2_cmds[] = {
     AP_INIT_TAKE1("H2ModernTLSOnly", h2_conf_set_modern_tls_only, NULL,
                   RSRC_CONF, "off to not impose RFC 7540 restrictions on TLS"),
     AP_INIT_TAKE1("H2Upgrade", h2_conf_set_upgrade, NULL,
-                  RSRC_CONF, "on to allow HTTP/1 Upgrades to h2/h2c"),
+                  RSRC_CONF|OR_AUTHCFG, "on to allow HTTP/1 Upgrades to h2/h2c"),
     AP_INIT_TAKE1("H2Direct", h2_conf_set_direct, NULL,
                   RSRC_CONF, "on to enable direct HTTP/2 mode"),
     AP_INIT_TAKE1("H2SessionExtraFiles", h2_conf_set_session_extra_files, NULL,
@@ -662,7 +963,7 @@ const command_rec h2_cmds[] = {
     AP_INIT_TAKE1("H2TLSCoolDownSecs", h2_conf_set_tls_cooldown_secs, NULL,
                   RSRC_CONF, "seconds of idle time on TLS before shrinking writes"),
     AP_INIT_TAKE1("H2Push", h2_conf_set_push, NULL,
-                  RSRC_CONF, "off to disable HTTP/2 server push"),
+                  RSRC_CONF|OR_AUTHCFG, "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("H2PushDiarySize", h2_conf_set_push_diary_size, NULL,
@@ -670,33 +971,12 @@ const command_rec h2_cmds[] = {
     AP_INIT_TAKE1("H2CopyFiles", h2_conf_set_copy_files, NULL,
                   OR_FILEINFO, "on to perform copy of file data"),
     AP_INIT_TAKE123("H2PushResource", h2_conf_add_push_res, NULL,
-                   OR_FILEINFO, "add a resource to be pushed in this location/on this server."),
+                   OR_FILEINFO|OR_AUTHCFG, "add a resource to be pushed in this location/on this server."),
     AP_INIT_TAKE1("H2EarlyHints", h2_conf_set_early_hints, NULL,
                   RSRC_CONF, "on to enable interim status 103 responses"),
+    AP_INIT_TAKE1("H2Padding", h2_conf_set_padding, NULL,
+                  RSRC_CONF, "set payload padding"),
     AP_END_CMD
 };
 
 
-const h2_config *h2_config_rget(request_rec *r)
-{
-    h2_config *cfg = (h2_config *)ap_get_module_config(r->per_dir_config, 
-                                                       &http2_module);
-    return cfg? cfg : h2_config_sget(r->server); 
-}
-
-const h2_config *h2_config_get(conn_rec *c)
-{
-    h2_ctx *ctx = h2_ctx_get(c, 0);
-    
-    if (ctx) {
-        if (ctx->config) {
-            return ctx->config;
-        }
-        else if (ctx->server) {
-            ctx->config = h2_config_sget(ctx->server);
-            return ctx->config;
-        }
-    }
-    
-    return h2_config_sget(c->base_server);
-}
diff --git a/modules/http2/h2_config.h b/modules/http2/h2_config.h
index 17d75d6..e940c8a 100644
--- a/modules/http2/h2_config.h
+++ b/modules/http2/h2_config.h
@@ -42,6 +42,8 @@ typedef enum {
     H2_CONF_PUSH_DIARY_SIZE,
     H2_CONF_COPY_FILES,
     H2_CONF_EARLY_HINTS,
+    H2_CONF_PADDING_BITS,
+    H2_CONF_PADDING_ALWAYS,
 } h2_config_var_t;
 
 struct apr_hash_t;
@@ -53,33 +55,6 @@ typedef struct h2_push_res {
     int critical;
 } h2_push_res;
 
-/* Apache httpd module configuration for h2. */
-typedef struct h2_config {
-    const char *name;
-    int h2_max_streams;           /* max concurrent # streams (http2) */
-    int h2_window_size;           /* stream window size (http2) */
-    int min_workers;              /* min # of worker threads/child */
-    int max_workers;              /* max # of worker threads/child */
-    int max_worker_idle_secs;     /* max # of idle seconds for worker */
-    int stream_max_mem_size;      /* max # bytes held in memory/stream */
-    apr_array_header_t *alt_svcs; /* h2_alt_svc specs for this server */
-    int alt_svc_max_age;          /* seconds clients can rely on alt-svc info*/
-    int serialize_headers;        /* Use serialized HTTP/1.1 headers for 
-                                     processing, better compatibility */
-    int h2_direct;                /* if mod_h2 is active directly */
-    int modern_tls_only;          /* Accept only modern TLS in HTTP/2 connections */  
-    int h2_upgrade;               /* Allow HTTP/1 upgrade to h2/h2c */
-    apr_int64_t tls_warmup_size;  /* Amount of TLS data to send before going full write size */
-    int tls_cooldown_secs;        /* Seconds of idle time before going back to small TLS records */
-    int h2_push;                  /* if HTTP/2 server push is enabled */
-    struct apr_hash_t *priorities;/* map of content-type to h2_priority records */
-    
-    int push_diary_size;          /* # of entries in push diary */
-    int copy_files;               /* if files shall be copied vs setaside on output */
-    apr_array_header_t *push_list;/* list of h2_push_res configurations */
-    int early_hints;              /* support status code 103 */
-} h2_config;
-
 
 void *h2_config_create_dir(apr_pool_t *pool, char *x);
 void *h2_config_merge_dir(apr_pool_t *pool, void *basev, void *addv);
@@ -88,19 +63,37 @@ void *h2_config_merge_svr(apr_pool_t *pool, void *basev, void *addv);
 
 extern const command_rec h2_cmds[];
 
-const h2_config *h2_config_get(conn_rec *c);
-const h2_config *h2_config_sget(server_rec *s);
-const h2_config *h2_config_rget(request_rec *r);
+int h2_config_geti(request_rec *r, server_rec *s, h2_config_var_t var);
+apr_int64_t h2_config_geti64(request_rec *r, server_rec *s, h2_config_var_t var);
 
-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);
+/** 
+ * Get the configured value for variable <var> at the given connection.
+ */
+int h2_config_cgeti(conn_rec *c, h2_config_var_t var);
+apr_int64_t h2_config_cgeti64(conn_rec *c, h2_config_var_t var);
 
-void h2_get_num_workers(server_rec *s, int *minw, int *maxw);
+/** 
+ * Get the configured value for variable <var> at the given server.
+ */
+int h2_config_sgeti(server_rec *s, h2_config_var_t var);
+apr_int64_t h2_config_sgeti64(server_rec *s, h2_config_var_t var);
 
+/** 
+ * Get the configured value for variable <var> at the given request,
+ * if configured for the request location. 
+ * Fallback to request server config otherwise.
+ */
+int h2_config_rgeti(request_rec *r, h2_config_var_t var);
+apr_int64_t h2_config_rgeti64(request_rec *r, h2_config_var_t var);
+
+apr_array_header_t *h2_config_push_list(request_rec *r);
+apr_array_header_t *h2_config_alt_svcs(request_rec *r);
+
+
+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, 
-                                                 const char *content_type);
+const struct h2_priority *h2_cconfig_get_priority(conn_rec *c, const char *content_type);
        
 #endif /* __mod_h2__h2_config_h__ */
 
diff --git a/modules/http2/h2_conn.c b/modules/http2/h2_conn.c
index dc2081e..9ef0ea0 100644
--- a/modules/http2/h2_conn.c
+++ b/modules/http2/h2_conn.c
@@ -18,6 +18,7 @@
 #include <apr_strings.h>
 
 #include <ap_mpm.h>
+#include <ap_mmn.h>
 
 #include <httpd.h>
 #include <http_core.h>
@@ -109,7 +110,6 @@ static void check_modules(int force)
 
 apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
 {
-    const h2_config *config = h2_config_sget(s);
     apr_status_t status = APR_SUCCESS;
     int minw, maxw;
     int max_threads_per_child = 0;
@@ -129,7 +129,7 @@ apr_status_t h2_conn_child_init(apr_pool_t *pool, server_rec *s)
     
     h2_get_num_workers(s, &minw, &maxw);
     
-    idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
+    idle_secs = h2_config_sgeti(s, H2_CONF_MAX_WORKER_IDLE_SECS);
     ap_log_error(APLOG_MARK, APLOG_TRACE3, 0, s,
                  "h2_workers: min=%d max=%d, mthrpchild=%d, idle_secs=%d", 
                  minw, maxw, max_threads_per_child, idle_secs);
@@ -172,9 +172,10 @@ static module *h2_conn_mpm_module(void)
     return mpm_module;
 }
 
-apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
+apr_status_t h2_conn_setup(conn_rec *c, request_rec *r, server_rec *s)
 {
     h2_session *session;
+    h2_ctx *ctx;
     apr_status_t status;
     
     if (!workers) {
@@ -183,24 +184,19 @@ apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
         return APR_EGENERAL;
     }
     
-    if (r) {
-        status = h2_session_rcreate(&session, r, ctx, workers);
-    }
-    else {
-        status = h2_session_create(&session, c, ctx, workers);
-    }
-
-    if (status == APR_SUCCESS) {
+    if (APR_SUCCESS == (status = h2_session_create(&session, c, r, s, workers))) {
+        ctx = h2_ctx_get(c, 1);
         h2_ctx_session_set(ctx, session);
     }
+    
     return status;
 }
 
-apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
+apr_status_t h2_conn_run(conn_rec *c)
 {
     apr_status_t status;
     int mpm_state = 0;
-    h2_session *session = h2_ctx_session_get(ctx);
+    h2_session *session = h2_ctx_get_session(c);
     
     ap_assert(session);
     do {
@@ -249,7 +245,7 @@ apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c)
 
 apr_status_t h2_conn_pre_close(struct h2_ctx *ctx, conn_rec *c)
 {
-    h2_session *session = h2_ctx_session_get(ctx);
+    h2_session *session = h2_ctx_get_session(c);
     if (session) {
         apr_status_t status = h2_session_pre_close(session, async_mpm);
         return (status == APR_SUCCESS)? DONE : status;
@@ -310,8 +306,10 @@ conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
     c->filter_conn_ctx        = NULL;
 #endif
     c->bucket_alloc           = apr_bucket_alloc_create(pool);
-    c->data_in_input_filters  = 0;
-    c->data_in_output_filters = 0;
+#if !AP_MODULE_MAGIC_AT_LEAST(20180720, 1)
+     c->data_in_input_filters  = 0;
+     c->data_in_output_filters = 0;
+#endif
     /* prevent mpm_event from making wrong assumptions about this connection,
      * like e.g. using its socket for an async read check. */
     c->clogging_input_filters = 1;
diff --git a/modules/http2/h2_conn.h b/modules/http2/h2_conn.h
index e45ff31..c560405 100644
--- a/modules/http2/h2_conn.h
+++ b/modules/http2/h2_conn.h
@@ -23,21 +23,21 @@ struct h2_task;
 /**
  * Setup the connection and our context for HTTP/2 processing
  *
- * @param ctx the http2 context to setup
  * @param c the connection HTTP/2 is starting on
  * @param r the upgrade request that still awaits an answer, optional
+ * @param s the server selected for this connection (can be != c->base_server)
  */
-apr_status_t h2_conn_setup(struct h2_ctx *ctx, conn_rec *c, request_rec *r);
+apr_status_t h2_conn_setup(conn_rec *c, request_rec *r, server_rec *s);
 
 /**
  * Run the HTTP/2 connection in synchronous fashion. 
  * Return when the HTTP/2 session is done
  * and the connection will close or a fatal error occurred.
  *
- * @param ctx the http2 context to run
+ * @param c the http2 connection to run
  * @return APR_SUCCESS when session is done.
  */
-apr_status_t h2_conn_run(struct h2_ctx *ctx, conn_rec *c);
+apr_status_t h2_conn_run(conn_rec *c);
 
 /**
  * The connection is about to close. If we have not send a GOAWAY
diff --git a/modules/http2/h2_conn_io.c b/modules/http2/h2_conn_io.c
index eb6ec92..5f17e85 100644
--- a/modules/http2/h2_conn_io.c
+++ b/modules/http2/h2_conn_io.c
@@ -40,12 +40,17 @@
  * ~= 1300 bytes */
 #define WRITE_SIZE_INITIAL    1300
 
-/* Calculated like this: max TLS record size 16*1024
- *   - 40 (IP) - 20 (TCP) - 40 (TCP options) 
- *    - TLS overhead (60-100) 
- * which seems to create less TCP packets overall
+/* The maximum we'd like to write in one chunk is
+ * the max size of a TLS record. When pushing
+ * many frames down the h2 connection, this might
+ * align differently because of headers and other
+ * frames or simply as not sufficient data is
+ * in a response body.
+ * However keeping frames at or below this limit
+ * should make optimizations at the layer that writes
+ * to TLS easier.
  */
-#define WRITE_SIZE_MAX        (TLS_DATA_MAX - 100) 
+#define WRITE_SIZE_MAX        (TLS_DATA_MAX) 
 
 
 static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level, 
@@ -123,21 +128,20 @@ static void h2_conn_io_bb_log(conn_rec *c, int stream_id, int level,
 
 }
 
-apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, 
-                             const h2_config *cfg)
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, server_rec *s)
 {
     io->c              = c;
     io->output         = apr_brigade_create(c->pool, c->bucket_alloc);
     io->is_tls         = h2_h2_is_tls(c);
     io->buffer_output  = io->is_tls;
-    io->flush_threshold = (apr_size_t)h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM);
+    io->flush_threshold = (apr_size_t)h2_config_sgeti64(s, H2_CONF_STREAM_MAX_MEM);
 
     if (io->is_tls) {
         /* This is what we start with, 
          * see https://issues.apache.org/jira/browse/TS-2503 
          */
-        io->warmup_size    = h2_config_geti64(cfg, H2_CONF_TLS_WARMUP_SIZE);
-        io->cooldown_usecs = (h2_config_geti(cfg, H2_CONF_TLS_COOLDOWN_SECS) 
+        io->warmup_size    = h2_config_sgeti64(s, H2_CONF_TLS_WARMUP_SIZE);
+        io->cooldown_usecs = (h2_config_sgeti(s, H2_CONF_TLS_COOLDOWN_SECS) 
                               * APR_USEC_PER_SEC);
         io->write_size     = (io->cooldown_usecs > 0? 
                               WRITE_SIZE_INITIAL : WRITE_SIZE_MAX); 
diff --git a/modules/http2/h2_conn_io.h b/modules/http2/h2_conn_io.h
index 2c3be1c..e96203c 100644
--- a/modules/http2/h2_conn_io.h
+++ b/modules/http2/h2_conn_io.h
@@ -48,8 +48,7 @@ typedef struct {
     apr_size_t slen;
 } h2_conn_io;
 
-apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, 
-                             const struct h2_config *cfg);
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, server_rec *s);
 
 /**
  * Append data to the buffered output.
diff --git a/modules/http2/h2_ctx.c b/modules/http2/h2_ctx.c
index d5ccc24..095f355 100644
--- a/modules/http2/h2_ctx.c
+++ b/modules/http2/h2_ctx.c
@@ -29,8 +29,8 @@ static h2_ctx *h2_ctx_create(const conn_rec *c)
 {
     h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
     ap_assert(ctx);
+    h2_ctx_server_update(ctx, c->base_server);
     ap_set_module_config(c->conn_config, &http2_module, ctx);
-    h2_ctx_server_set(ctx, c->base_server);
     return ctx;
 }
 
@@ -79,8 +79,9 @@ h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto)
     return ctx;
 }
 
-h2_session *h2_ctx_session_get(h2_ctx *ctx)
+h2_session *h2_ctx_get_session(conn_rec *c)
 {
+    h2_ctx *ctx = h2_ctx_get(c, 0);
     return ctx? ctx->session : NULL;
 }
 
@@ -89,33 +90,17 @@ void h2_ctx_session_set(h2_ctx *ctx, struct h2_session *session)
     ctx->session = session;
 }
 
-server_rec *h2_ctx_server_get(h2_ctx *ctx)
+h2_ctx *h2_ctx_server_update(h2_ctx *ctx, server_rec *s)
 {
-    return ctx? ctx->server : NULL;
-}
-
-h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s)
-{
-    ctx->server = s;
+    if (ctx->server != s) {
+        ctx->server = s;
+    }
     return ctx;
 }
 
-int h2_ctx_is_task(h2_ctx *ctx)
-{
-    return ctx && ctx->task;
-}
-
-h2_task *h2_ctx_get_task(h2_ctx *ctx)
+h2_task *h2_ctx_get_task(conn_rec *c)
 {
+    h2_ctx *ctx = h2_ctx_get(c, 0);
     return ctx? ctx->task : NULL;
 }
 
-h2_task *h2_ctx_cget_task(conn_rec *c)
-{
-    return h2_ctx_get_task(h2_ctx_get(c, 0));
-}
-
-h2_task *h2_ctx_rget_task(request_rec *r)
-{
-    return h2_ctx_get_task(h2_ctx_rget(r));
-}
diff --git a/modules/http2/h2_ctx.h b/modules/http2/h2_ctx.h
index cb111c9..417ef36 100644
--- a/modules/http2/h2_ctx.h
+++ b/modules/http2/h2_ctx.h
@@ -56,12 +56,11 @@ h2_ctx *h2_ctx_create_for(const conn_rec *c, struct h2_task *task);
  */
 h2_ctx *h2_ctx_protocol_set(h2_ctx *ctx, const char *proto);
 
-/* Set the server_rec relevant for this context.
+/* Update the server_rec relevant for this context. A server for
+ * a connection may change during SNI handling, for example.
  */
-h2_ctx *h2_ctx_server_set(h2_ctx *ctx, server_rec *s);
-server_rec *h2_ctx_server_get(h2_ctx *ctx);
+h2_ctx *h2_ctx_server_update(h2_ctx *ctx, server_rec *s);
 
-struct h2_session *h2_ctx_session_get(h2_ctx *ctx);
 void h2_ctx_session_set(h2_ctx *ctx, struct h2_session *session);
 
 /**
@@ -69,10 +68,8 @@ void h2_ctx_session_set(h2_ctx *ctx, struct h2_session *session);
  */
 const char *h2_ctx_protocol_get(const conn_rec *c);
 
-int h2_ctx_is_task(h2_ctx *ctx);
+struct h2_session *h2_ctx_get_session(conn_rec *c);
+struct h2_task *h2_ctx_get_task(conn_rec *c);
 
-struct h2_task *h2_ctx_get_task(h2_ctx *ctx);
-struct h2_task *h2_ctx_cget_task(conn_rec *c);
-struct h2_task *h2_ctx_rget_task(request_rec *r);
 
 #endif /* defined(__mod_h2__h2_ctx__) */
diff --git a/modules/http2/h2_filter.c b/modules/http2/h2_filter.c
index 8b254b1..5fd237f 100644
--- a/modules/http2/h2_filter.c
+++ b/modules/http2/h2_filter.c
@@ -54,6 +54,7 @@ static apr_status_t recv_RAW_DATA(conn_rec *c, h2_filter_cin *cin,
     const char *data;
     ssize_t n;
     
+    (void)c;
     status = apr_bucket_read(b, &data, &len, block);
     
     while (status == APR_SUCCESS && len > 0) {
@@ -71,10 +72,10 @@ static apr_status_t recv_RAW_DATA(conn_rec *c, h2_filter_cin *cin,
         }
         else {
             session->io.bytes_read += n;
-            if (len <= n) {
+            if ((apr_ssize_t)len <= n) {
                 break;
             }
-            len -= n;
+            len -= (apr_size_t)n;
             data += n;
         }
     }
@@ -277,6 +278,7 @@ apr_bucket *h2_bucket_observer_beam(struct h2_bucket_beam *beam,
                                     apr_bucket_brigade *dest,
                                     const apr_bucket *src)
 {
+    (void)beam;
     if (H2_BUCKET_IS_OBSERVER(src)) {
         h2_bucket_observer *l = (h2_bucket_observer *)src->data; 
         apr_bucket *b = h2_bucket_observer_create(dest->bucket_alloc, 
@@ -311,8 +313,7 @@ static void add_settings(apr_bucket_brigade *bb, h2_session *s, int last)
     bbout(bb, "  \"settings\": {\n");
     bbout(bb, "    \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", m->max_streams); 
     bbout(bb, "    \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 16*1024); 
-    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n",
-          h2_config_geti(s->config, H2_CONF_WIN_SIZE));
+    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n", h2_config_sgeti(s->s, H2_CONF_WIN_SIZE));
     bbout(bb, "    \"SETTINGS_ENABLE_PUSH\": %d\n", h2_session_push_enabled(s)); 
     bbout(bb, "  }%s\n", last? "" : ",");
 }
@@ -431,41 +432,38 @@ static void add_stats(apr_bucket_brigade *bb, h2_session *s,
 
 static apr_status_t h2_status_insert(h2_task *task, apr_bucket *b)
 {
-    conn_rec *c = task->c->master;
-    h2_ctx *h2ctx = h2_ctx_get(c, 0);
-    h2_session *session;
-    h2_stream *stream;
+    h2_mplx *m = task->mplx;
+    h2_stream *stream = h2_mplx_stream_get(m, task->stream_id);
+    h2_session *s;
+    conn_rec *c;
+    
     apr_bucket_brigade *bb;
     apr_bucket *e;
     int32_t connFlowIn, connFlowOut;
     
-    
-    if (!h2ctx || (session = h2_ctx_session_get(h2ctx)) == NULL) {
-        return APR_SUCCESS;
-    }
-    
-    stream = h2_session_stream_get(session, task->stream_id);
     if (!stream) {
         /* stream already done */
         return APR_SUCCESS;
     }
+    s = stream->session;
+    c = s->c;
     
     bb = apr_brigade_create(stream->pool, c->bucket_alloc);
     
-    connFlowIn = nghttp2_session_get_effective_local_window_size(session->ngh2); 
-    connFlowOut = nghttp2_session_get_remote_window_size(session->ngh2);
+    connFlowIn = nghttp2_session_get_effective_local_window_size(s->ngh2); 
+    connFlowOut = nghttp2_session_get_remote_window_size(s->ngh2);
      
     bbout(bb, "{\n");
     bbout(bb, "  \"version\": \"draft-01\",\n");
-    add_settings(bb, session, 0);
-    add_peer_settings(bb, session, 0);
+    add_settings(bb, s, 0);
+    add_peer_settings(bb, s, 0);
     bbout(bb, "  \"connFlowIn\": %d,\n", connFlowIn);
     bbout(bb, "  \"connFlowOut\": %d,\n", connFlowOut);
-    bbout(bb, "  \"sentGoAway\": %d,\n", session->local.shutdown);
+    bbout(bb, "  \"sentGoAway\": %d,\n", s->local.shutdown);
 
-    add_streams(bb, session, 0);
+    add_streams(bb, s, 0);
     
-    add_stats(bb, session, stream, 1);
+    add_stats(bb, s, stream, 1);
     bbout(bb, "}\n");
     
     while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
@@ -497,7 +495,6 @@ static apr_status_t status_event(void *ctx, h2_bucket_event event,
 
 int h2_filter_h2_status_handler(request_rec *r)
 {
-    h2_ctx *ctx = h2_ctx_rget(r);
     conn_rec *c = r->connection;
     h2_task *task;
     apr_bucket_brigade *bb;
@@ -511,7 +508,7 @@ int h2_filter_h2_status_handler(request_rec *r)
         return DECLINED;
     }
 
-    task = ctx? h2_ctx_get_task(ctx) : NULL;
+    task = h2_ctx_get_task(r->connection);
     if (task) {
 
         if ((status = ap_discard_request_body(r)) != OK) {
diff --git a/modules/http2/h2_from_h1.c b/modules/http2/h2_from_h1.c
index dd6ad90..c3e3352 100644
--- a/modules/http2/h2_from_h1.c
+++ b/modules/http2/h2_from_h1.c
@@ -586,18 +586,20 @@ apr_status_t h2_filter_headers_out(ap_filter_t *f, apr_bucket_brigade *bb)
         }
     }
     
-    if (r->header_only) {
+    if (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status)) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
-                      "h2_task(%s): header_only, cleanup output brigade", 
+                      "h2_task(%s): headers only, cleanup output brigade", 
                       task->id);
         b = body_bucket? body_bucket : APR_BRIGADE_FIRST(bb);
         while (b != APR_BRIGADE_SENTINEL(bb)) {
             next = APR_BUCKET_NEXT(b);
             if (APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b)) {
                 break;
-            } 
-            APR_BUCKET_REMOVE(b);
-            apr_bucket_destroy(b);
+            }
+            if (!H2_BUCKET_IS_HEADERS(b)) {
+                APR_BUCKET_REMOVE(b);
+                apr_bucket_destroy(b);
+            }
             b = next;
         }
     }
diff --git a/modules/http2/h2_h2.c b/modules/http2/h2_h2.c
index 5580cef..4ff1d51 100644
--- a/modules/http2/h2_h2.c
+++ b/modules/http2/h2_h2.c
@@ -463,19 +463,18 @@ int h2_h2_is_tls(conn_rec *c)
     return opt_ssl_is_https && opt_ssl_is_https(c);
 }
 
-int h2_is_acceptable_connection(conn_rec *c, int require_all) 
+int h2_is_acceptable_connection(conn_rec *c, request_rec *r, int require_all) 
 {
     int is_tls = h2_h2_is_tls(c);
-    const h2_config *cfg = h2_config_get(c);
 
-    if (is_tls && h2_config_geti(cfg, H2_CONF_MODERN_TLS_ONLY) > 0) {
+    if (is_tls && h2_config_cgeti(c, H2_CONF_MODERN_TLS_ONLY) > 0) {
         /* Check TLS connection for modern TLS parameters, as defined in
          * RFC 7540 and https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
          */
         apr_pool_t *pool = c->pool;
         server_rec *s = c->base_server;
         char *val;
-        
+
         if (!opt_ssl_var_lookup) {
             /* unable to check */
             return 0;
@@ -521,26 +520,22 @@ int h2_is_acceptable_connection(conn_rec *c, int require_all)
     return 1;
 }
 
-int h2_allows_h2_direct(conn_rec *c)
+static int h2_allows_h2_direct(conn_rec *c)
 {
-    const h2_config *cfg = h2_config_get(c);
     int is_tls = h2_h2_is_tls(c);
     const char *needed_protocol = is_tls? "h2" : "h2c";
-    int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT);
+    int h2_direct = h2_config_cgeti(c, H2_CONF_DIRECT);
     
     if (h2_direct < 0) {
         h2_direct = is_tls? 0 : 1;
     }
-    return (h2_direct 
-            && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol));
+    return (h2_direct && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol));
 }
 
-int h2_allows_h2_upgrade(conn_rec *c)
+int h2_allows_h2_upgrade(request_rec *r)
 {
-    const h2_config *cfg = h2_config_get(c);
-    int h2_upgrade = h2_config_geti(cfg, H2_CONF_UPGRADE);
-    
-    return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(c));
+    int h2_upgrade = h2_config_rgeti(r, H2_CONF_UPGRADE);
+    return h2_upgrade > 0 || (h2_upgrade < 0 && !h2_h2_is_tls(r->connection));
 }
 
 /*******************************************************************************
@@ -581,14 +576,17 @@ int h2_h2_process_conn(conn_rec* c)
 {
     apr_status_t status;
     h2_ctx *ctx;
+    server_rec *s;
     
     if (c->master) {
         return DECLINED;
     }
     
     ctx = h2_ctx_get(c, 0);
+    s = ctx? ctx->server : c->base_server;
+    
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn");
-    if (h2_ctx_is_task(ctx)) {
+    if (ctx && ctx->task) {
         /* our stream pseudo connection */
         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, "h2_h2, task, declined");
         return DECLINED;
@@ -601,19 +599,19 @@ int h2_h2_process_conn(conn_rec* c)
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, "
                           "new connection using protocol '%s', direct=%d, "
                           "tls acceptable=%d", proto, h2_allows_h2_direct(c), 
-                          h2_is_acceptable_connection(c, 1));
+                          h2_is_acceptable_connection(c, NULL, 1));
         }
         
         if (!strcmp(AP_PROTOCOL_HTTP1, proto)
             && h2_allows_h2_direct(c) 
-            && h2_is_acceptable_connection(c, 1)) {
+            && h2_is_acceptable_connection(c, NULL, 1)) {
             /* Fresh connection still is on http/1.1 and H2Direct is enabled. 
              * Otherwise connection is in a fully acceptable state.
              * -> peek at the first 24 incoming bytes
              */
             apr_bucket_brigade *temp;
-            char *s = NULL;
-            apr_size_t slen;
+            char *peek = NULL;
+            apr_size_t peeklen;
             
             temp = apr_brigade_create(c->pool, c->bucket_alloc);
             status = ap_get_brigade(c->input_filters, temp,
@@ -626,8 +624,8 @@ int h2_h2_process_conn(conn_rec* c)
                 return DECLINED;
             }
             
-            apr_brigade_pflatten(temp, &s, &slen, c->pool);
-            if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
+            apr_brigade_pflatten(temp, &peek, &peeklen, c->pool);
+            if ((peeklen >= 24) && !memcmp(H2_MAGIC_TOKEN, peek, 24)) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                               "h2_h2, direct mode detected");
                 if (!ctx) {
@@ -638,7 +636,7 @@ int h2_h2_process_conn(conn_rec* c)
             else if (APLOGctrace2(c)) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
                               "h2_h2, not detected in %d bytes(base64): %s", 
-                              (int)slen, h2_util_base64url_encode(s, slen, c->pool));
+                              (int)peeklen, h2_util_base64url_encode(peek, peeklen, c->pool));
             }
             
             apr_brigade_destroy(temp);
@@ -647,15 +645,16 @@ int h2_h2_process_conn(conn_rec* c)
 
     if (ctx) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "process_conn");
-        if (!h2_ctx_session_get(ctx)) {
-            status = h2_conn_setup(ctx, c, NULL);
+        
+        if (!h2_ctx_get_session(c)) {
+            status = h2_conn_setup(c, NULL, s);
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c, "conn_setup");
             if (status != APR_SUCCESS) {
                 h2_ctx_clear(c);
                 return !OK;
             }
         }
-        h2_conn_run(ctx, c);
+        h2_conn_run(c);
         return OK;
     }
     
@@ -684,16 +683,17 @@ static int h2_h2_pre_close_conn(conn_rec *c)
 
 static void check_push(request_rec *r, const char *tag)
 {
-    const h2_config *conf = h2_config_rget(r);
-    if (!r->expecting_100 
-        && conf && conf->push_list && conf->push_list->nelts > 0) {
+    apr_array_header_t *push_list = h2_config_push_list(r);
+
+    if (!r->expecting_100 && push_list && push_list->nelts > 0) {
         int i, old_status;
         const char *old_line;
+        
         ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 
                       "%s, early announcing %d resources for push",
-                      tag, conf->push_list->nelts);
-        for (i = 0; i < conf->push_list->nelts; ++i) {
-            h2_push_res *push = &APR_ARRAY_IDX(conf->push_list, i, h2_push_res);
+                      tag, push_list->nelts);
+        for (i = 0; i < push_list->nelts; ++i) {
+            h2_push_res *push = &APR_ARRAY_IDX(push_list, i, h2_push_res);
             apr_table_add(r->headers_out, "Link", 
                            apr_psprintf(r->pool, "<%s>; rel=preload%s", 
                                         push->uri_ref, push->critical? "; critical" : ""));
@@ -712,8 +712,7 @@ static int h2_h2_post_read_req(request_rec *r)
 {
     /* slave connection? */
     if (r->connection->master) {
-        h2_ctx *ctx = h2_ctx_rget(r);
-        struct h2_task *task = h2_ctx_get_task(ctx);
+        struct h2_task *task = h2_ctx_get_task(r->connection);
         /* This hook will get called twice on internal redirects. Take care
          * that we manipulate filters only once. */
         if (task && !task->filters_set) {
@@ -746,12 +745,10 @@ static int h2_h2_late_fixups(request_rec *r)
 {
     /* slave connection? */
     if (r->connection->master) {
-        h2_ctx *ctx = h2_ctx_rget(r);
-        struct h2_task *task = h2_ctx_get_task(ctx);
+        struct h2_task *task = h2_ctx_get_task(r->connection);
         if (task) {
             /* check if we copy vs. setaside files in this location */
-            task->output.copy_files = h2_config_geti(h2_config_rget(r), 
-                                                     H2_CONF_COPY_FILES);
+            task->output.copy_files = h2_config_rgeti(r, H2_CONF_COPY_FILES);
             if (task->output.copy_files) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
                               "h2_slave_out(%s): copy_files on", task->id);
diff --git a/modules/http2/h2_h2.h b/modules/http2/h2_h2.h
index 367823d..339e898 100644
--- a/modules/http2/h2_h2.h
+++ b/modules/http2/h2_h2.h
@@ -57,23 +57,15 @@ void h2_h2_register_hooks(void);
  *    the handshake is still ongoing.
  * @return != 0 iff connection requirements are met
  */
-int h2_is_acceptable_connection(conn_rec *c, int require_all);
-
-/**
- * Check if the "direct" HTTP/2 mode of protocol handling is enabled
- * for the given connection.
- * @param c the connection to check
- * @return != 0 iff direct mode is enabled
- */
-int h2_allows_h2_direct(conn_rec *c);
+int h2_is_acceptable_connection(conn_rec *c, request_rec *r, int require_all);
 
 /**
  * Check if the "Upgrade" HTTP/1.1 mode of protocol switching is enabled
- * for the given connection.
- * @param c the connection to check
+ * for the given request.
+ * @param r the request to check
  * @return != 0 iff Upgrade switching is enabled
  */
-int h2_allows_h2_upgrade(conn_rec *c);
+int h2_allows_h2_upgrade(request_rec *r);
 
 
 #endif /* defined(__mod_h2__h2_h2__) */
diff --git a/modules/http2/h2_headers.c b/modules/http2/h2_headers.c
index 49d9c0a..6d43290 100644
--- a/modules/http2/h2_headers.c
+++ b/modules/http2/h2_headers.c
@@ -28,6 +28,7 @@
 
 #include "h2_private.h"
 #include "h2_h2.h"
+#include "h2_config.h"
 #include "h2_util.h"
 #include "h2_request.h"
 #include "h2_headers.h"
@@ -129,21 +130,27 @@ h2_headers *h2_headers_rcreate(request_rec *r, int status,
 {
     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) {
-            /* This request triggered a TLS renegotiation that is now allowed 
-             * in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
-             */
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, headers->status, r,
-                          APLOGNO(03061) 
-                          "h2_headers(%ld): renegotiate forbidden, cause: %s",
-                          (long)r->connection->id, cause);
-            headers->status = H2_ERR_HTTP_1_1_REQUIRED;
+        request_rec *r_prev;
+        for (r_prev = r; r_prev != NULL; r_prev = r_prev->prev) {
+            const char *cause = apr_table_get(r_prev->notes, "ssl-renegotiate-forbidden");
+            if (cause) {
+                /* This request triggered a TLS renegotiation that is not allowed
+                 * in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
+                 */
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, headers->status, r,
+                              APLOGNO(03061)
+                              "h2_headers(%ld): renegotiate forbidden, cause: %s",
+                              (long)r->connection->id, cause);
+                headers->status = H2_ERR_HTTP_1_1_REQUIRED;
+                break;
+            }
         }
     }
     if (is_unsafe(r->server)) {
-        apr_table_setn(headers->notes, H2_HDR_CONFORMANCE, 
-                       H2_HDR_CONFORMANCE_UNSAFE);
+        apr_table_setn(headers->notes, H2_HDR_CONFORMANCE, H2_HDR_CONFORMANCE_UNSAFE);
+    }
+    if (h2_config_rgeti(r, H2_CONF_PUSH) == 0 && h2_config_sgeti(r->server, H2_CONF_PUSH) != 0) {
+        apr_table_setn(headers->notes, H2_PUSH_MODE_NOTE, "0");
     }
     return headers;
 }
diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c
index db3cb63..fae77c7 100644
--- a/modules/http2/h2_mplx.c
+++ b/modules/http2/h2_mplx.c
@@ -40,7 +40,6 @@
 #include "h2_ctx.h"
 #include "h2_h2.h"
 #include "h2_mplx.h"
-#include "h2_ngn_shed.h"
 #include "h2_request.h"
 #include "h2_stream.h"
 #include "h2_session.h"
@@ -83,12 +82,6 @@ static void check_data_for(h2_mplx *m, h2_stream *stream, int lock);
 static void stream_output_consumed(void *ctx, 
                                    h2_bucket_beam *beam, apr_off_t length)
 {
-    h2_stream *stream = ctx;
-    h2_task *task = stream->task;
-    
-    if (length > 0 && task && task->assigned) {
-        h2_req_engine_out_consumed(task->assigned, task->c, length); 
-    }
 }
 
 static void stream_input_ev(void *ctx, h2_bucket_beam *beam)
@@ -136,7 +129,6 @@ static void stream_cleanup(h2_mplx *m, h2_stream *stream)
     }
     else if (stream->task) {
         stream->task->c->aborted = 1;
-        apr_thread_cond_broadcast(m->task_thawed);
     }
 }
 
@@ -151,25 +143,19 @@ static void stream_cleanup(h2_mplx *m, h2_stream *stream)
  *   their HTTP/1 cousins, the separate allocator seems to work better
  *   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, 
+h2_mplx *h2_mplx_create(conn_rec *c, server_rec *s, apr_pool_t *parent, 
                         h2_workers *workers)
 {
     apr_status_t status = APR_SUCCESS;
     apr_allocator_t *allocator;
     apr_thread_mutex_t *mutex;
     h2_mplx *m;
-    h2_ctx *ctx = h2_ctx_get(c, 0);
-    ap_assert(conf);
     
     m = apr_pcalloc(parent, sizeof(h2_mplx));
     if (m) {
         m->id = c->id;
         m->c = c;
-        m->s = (ctx? h2_ctx_server_get(ctx) : NULL);
-        if (!m->s) {
-            m->s = c->base_server;
-        }
+        m->s = s;
         
         /* We create a pool with its own allocator to be used for
          * processing slave connections. This is the only way to have the
@@ -204,14 +190,8 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent,
             return NULL;
         }
         
-        status = apr_thread_cond_create(&m->task_thawed, m->pool);
-        if (status != APR_SUCCESS) {
-            apr_pool_destroy(m->pool);
-            return NULL;
-        }
-    
-        m->max_streams = h2_config_geti(conf, H2_CONF_MAX_STREAMS);
-        m->stream_max_mem = h2_config_geti(conf, H2_CONF_STREAM_MAX_MEM);
+        m->max_streams = h2_config_sgeti(s, H2_CONF_MAX_STREAMS);
+        m->stream_max_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM);
 
         m->streams = h2_ihash_create(m->pool, offsetof(h2_stream,id));
         m->sredo = h2_ihash_create(m->pool, offsetof(h2_stream,id));
@@ -232,10 +212,6 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *parent,
         m->limit_change_interval = apr_time_from_msec(100);
         
         m->spare_slaves = apr_array_make(m->pool, 10, sizeof(conn_rec*));
-        
-        m->ngn_shed = h2_ngn_shed_create(m->pool, m->c, m->max_streams, 
-                                         m->stream_max_mem);
-        h2_ngn_shed_set_ctx(m->ngn_shed , m);
     }
     return m;
 }
@@ -394,10 +370,10 @@ static int report_stream_iter(void *ctx, void *val) {
     if (task) {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */
                       H2_STRM_MSG(stream, "->03198: %s %s %s"
-                      "[started=%d/done=%d/frozen=%d]"), 
+                      "[started=%d/done=%d]"), 
                       task->request->method, task->request->authority, 
                       task->request->path, task->worker_started, 
-                      task->worker_done, task->frozen);
+                      task->worker_done);
     }
     else {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, /* NO APLOGNO */
@@ -436,7 +412,7 @@ static int stream_cancel_iter(void *ctx, void *val) {
 void h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
 {
     apr_status_t status;
-    int i, wait_secs = 60;
+    int i, wait_secs = 60, old_aborted;
 
     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
                   "h2_mplx(%ld): start release", m->id);
@@ -447,15 +423,19 @@ void h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
     
     H2_MPLX_ENTER_ALWAYS(m);
 
+    /* While really terminating any slave connections, treat the master
+     * connection as aborted. It's not as if we could send any more data
+     * at this point. */
+    old_aborted = m->c->aborted;
+    m->c->aborted = 1;
+
     /* How to shut down a h2 connection:
      * 1. cancel all streams still active */
     while (!h2_ihash_iter(m->streams, stream_cancel_iter, m)) {
         /* until empty */
     }
     
-    /* 2. terminate ngn_shed, no more streams
-     * should be scheduled or in the active set */
-    h2_ngn_shed_abort(m->ngn_shed);
+    /* 2. no more streams should be scheduled or in the active set */
     ap_assert(h2_ihash_empty(m->streams));
     ap_assert(h2_iq_empty(m->q));
     
@@ -479,10 +459,6 @@ void h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
     ap_assert(m->tasks_active == 0);
     m->join_wait = NULL;
     
-    /* 4. close the h2_req_enginge shed */
-    h2_ngn_shed_destroy(m->ngn_shed);
-    m->ngn_shed = NULL;
-    
     /* 4. With all workers done, all streams should be in spurge */
     if (!h2_ihash_empty(m->shold)) {
         ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, APLOGNO(03516)
@@ -491,6 +467,7 @@ void h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
         h2_ihash_iter(m->shold, unexpected_stream_iter, m);
     }
     
+    m->c->aborted = old_aborted;
     H2_MPLX_LEAVE(m);
 
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
@@ -787,47 +764,14 @@ apr_status_t h2_mplx_pop_task(h2_mplx *m, h2_task **ptask)
     return rv;
 }
 
-static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
+static void task_done(h2_mplx *m, h2_task *task)
 {
     h2_stream *stream;
     
-    if (task->frozen) {
-        /* this task was handed over to an engine for processing 
-         * and the original worker has finished. That means the 
-         * engine may start processing now. */
-        h2_task_thaw(task);
-        apr_thread_cond_broadcast(m->task_thawed);
-        return;
-    }
-        
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
                   "h2_mplx(%ld): task(%s) done", m->id, task->id);
     out_close(m, task);
     
-    if (ngn) {
-        apr_off_t bytes = 0;
-        h2_beam_send(task->output.beam, NULL, APR_NONBLOCK_READ);
-        bytes += h2_beam_get_buffered(task->output.beam);
-        if (bytes > 0) {
-            /* we need to report consumed and current buffered output
-             * to the engine. The request will be streamed out or cancelled,
-             * no more data is coming from it and the engine should update
-             * its calculations before we destroy this information. */
-            h2_req_engine_out_consumed(ngn, task->c, bytes);
-        }
-    }
-    
-    if (task->engine) {
-        if (!m->aborted && !task->c->aborted 
-            && !h2_req_engine_is_shutdown(task->engine)) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, APLOGNO(10022)
-                          "h2_mplx(%ld): task(%s) has not-shutdown "
-                          "engine(%s)", m->id, task->id, 
-                          h2_req_engine_get_id(task->engine));
-        }
-        h2_ngn_shed_done_ngn(m->ngn_shed, task->engine);
-    }
-    
     task->worker_done = 1;
     task->done_at = apr_time_now();
     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
@@ -849,18 +793,24 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
                           m->id, m->limit_active);
         }
     }
-    
+
+    ap_assert(task->done_done == 0);
+
     stream = h2_ihash_get(m->streams, task->stream_id);
     if (stream) {
         /* stream not done yet. */
         if (!m->aborted && h2_ihash_get(m->sredo, stream->id)) {
             /* reset and schedule again */
+            task->worker_done = 0;
             h2_task_redo(task);
             h2_ihash_remove(m->sredo, stream->id);
             h2_iq_add(m->q, stream->id, NULL, NULL);
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, m->c,
+                          H2_STRM_MSG(stream, "redo, added to q")); 
         }
         else {
             /* stream not cleaned up, stay around */
+            task->done_done = 1;
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
                           H2_STRM_MSG(stream, "task_done, stream open")); 
             if (stream->input) {
@@ -873,6 +823,7 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
     }
     else if ((stream = h2_ihash_get(m->shold, task->stream_id)) != NULL) {
         /* stream is done, was just waiting for this. */
+        task->done_done = 1;
         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
                       H2_STRM_MSG(stream, "task_done, in hold"));
         if (stream->input) {
@@ -897,7 +848,7 @@ void h2_mplx_task_done(h2_mplx *m, h2_task *task, h2_task **ptask)
 {
     H2_MPLX_ENTER_ALWAYS(m);
 
-    task_done(m, task, NULL);
+    task_done(m, task);
     --m->tasks_active;
     
     if (m->join_wait) {
@@ -1091,142 +1042,6 @@ apr_status_t h2_mplx_idle(h2_mplx *m)
 }
 
 /*******************************************************************************
- * HTTP/2 request engines
- ******************************************************************************/
-
-typedef struct {
-    h2_mplx * m;
-    h2_req_engine *ngn;
-    int streams_updated;
-} ngn_update_ctx;
-
-static int ngn_update_window(void *ctx, void *val)
-{
-    ngn_update_ctx *uctx = ctx;
-    h2_stream *stream = val;
-    if (stream->task && stream->task->assigned == uctx->ngn
-        && output_consumed_signal(uctx->m, stream->task)) {
-        ++uctx->streams_updated;
-    }
-    return 1;
-}
-
-static apr_status_t ngn_out_update_windows(h2_mplx *m, h2_req_engine *ngn)
-{
-    ngn_update_ctx ctx;
-        
-    ctx.m = m;
-    ctx.ngn = ngn;
-    ctx.streams_updated = 0;
-    h2_ihash_iter(m->streams, ngn_update_window, &ctx);
-    
-    return ctx.streams_updated? APR_SUCCESS : APR_EAGAIN;
-}
-
-apr_status_t h2_mplx_req_engine_push(const char *ngn_type, 
-                                     request_rec *r,
-                                     http2_req_engine_init *einit)
-{
-    apr_status_t status;
-    h2_mplx *m;
-    h2_task *task;
-    h2_stream *stream;
-    
-    task = h2_ctx_rget_task(r);
-    if (!task) {
-        return APR_ECONNABORTED;
-    }
-    m = task->mplx;
-    
-    H2_MPLX_ENTER(m);
-
-    stream = h2_ihash_get(m->streams, task->stream_id);
-    if (stream) {
-        status = h2_ngn_shed_push_request(m->ngn_shed, ngn_type, r, einit);
-    }
-    else {
-        status = APR_ECONNABORTED;
-    }
-
-    H2_MPLX_LEAVE(m);
-    return status;
-}
-
-apr_status_t h2_mplx_req_engine_pull(h2_req_engine *ngn, 
-                                     apr_read_type_e block, 
-                                     int capacity, 
-                                     request_rec **pr)
-{   
-    h2_ngn_shed *shed = h2_ngn_shed_get_shed(ngn);
-    h2_mplx *m = h2_ngn_shed_get_ctx(shed);
-    apr_status_t status;
-    int want_shutdown;
-    
-    H2_MPLX_ENTER(m);
-
-    want_shutdown = (block == APR_BLOCK_READ);
-
-    /* Take this opportunity to update output consummation 
-     * for this engine */
-    ngn_out_update_windows(m, ngn);
-    
-    if (want_shutdown && !h2_iq_empty(m->q)) {
-        /* For a blocking read, check first if requests are to be
-         * had and, if not, wait a short while before doing the
-         * blocking, and if unsuccessful, terminating read.
-         */
-        status = h2_ngn_shed_pull_request(shed, ngn, capacity, 1, pr);
-        if (APR_STATUS_IS_EAGAIN(status)) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
-                          "h2_mplx(%ld): start block engine pull", m->id);
-            apr_thread_cond_timedwait(m->task_thawed, m->lock, 
-                                      apr_time_from_msec(20));
-            status = h2_ngn_shed_pull_request(shed, ngn, capacity, 1, pr);
-        }
-    }
-    else {
-        status = h2_ngn_shed_pull_request(shed, ngn, capacity,
-                                          want_shutdown, pr);
-    }
-
-    H2_MPLX_LEAVE(m);
-    return status;
-}
- 
-void h2_mplx_req_engine_done(h2_req_engine *ngn, conn_rec *r_conn,
-                             apr_status_t status)
-{
-    h2_task *task = h2_ctx_cget_task(r_conn);
-    
-    if (task) {
-        h2_mplx *m = task->mplx;
-        h2_stream *stream;
-
-        H2_MPLX_ENTER_ALWAYS(m);
-
-        stream = h2_ihash_get(m->streams, task->stream_id);
-        
-        ngn_out_update_windows(m, ngn);
-        h2_ngn_shed_done_task(m->ngn_shed, ngn, task);
-        
-        if (status != APR_SUCCESS && stream 
-            && h2_task_can_redo(task) 
-            && !h2_ihash_get(m->sredo, stream->id)) {
-            h2_ihash_add(m->sredo, stream);
-        }
-
-        if (task->engine) { 
-            /* cannot report that as done until engine returns */
-        }
-        else {
-            task_done(m, task, ngn);
-        }
-
-        H2_MPLX_LEAVE(m);
-    }
-}
-
-/*******************************************************************************
  * mplx master events dispatching
  ******************************************************************************/
 
diff --git a/modules/http2/h2_mplx.h b/modules/http2/h2_mplx.h
index 2890b98..575ccaf 100644
--- a/modules/http2/h2_mplx.h
+++ b/modules/http2/h2_mplx.h
@@ -47,8 +47,6 @@ struct h2_request;
 struct apr_thread_cond_t;
 struct h2_workers;
 struct h2_iqueue;
-struct h2_ngn_shed;
-struct h2_req_engine;
 
 #include <apr_queue.h>
 
@@ -86,7 +84,6 @@ struct h2_mplx {
 
     apr_thread_mutex_t *lock;
     struct apr_thread_cond_t *added_output;
-    struct apr_thread_cond_t *task_thawed;
     struct apr_thread_cond_t *join_wait;
     
     apr_size_t stream_max_mem;
@@ -95,8 +92,6 @@ struct h2_mplx {
     apr_array_header_t *spare_slaves; /* spare slave connections */
     
     struct h2_workers *workers;
-    
-    struct h2_ngn_shed *ngn_shed;
 };
 
 
@@ -111,8 +106,7 @@ apr_status_t h2_mplx_child_init(apr_pool_t *pool, server_rec *s);
  * Create the multiplexer for the given HTTP2 session. 
  * Implicitly has reference count 1.
  */
-h2_mplx *h2_mplx_create(conn_rec *c, apr_pool_t *master, 
-                        const struct h2_config *conf, 
+h2_mplx *h2_mplx_create(conn_rec *c, server_rec *s, apr_pool_t *master, 
                         struct h2_workers *workers);
 
 /**
@@ -303,28 +297,4 @@ APR_RING_INSERT_TAIL((b), ap__b, h2_mplx, link);	\
  */
 apr_status_t h2_mplx_idle(h2_mplx *m);
 
-/*******************************************************************************
- * h2_req_engine handling
- ******************************************************************************/
-
-typedef void h2_output_consumed(void *ctx, conn_rec *c, apr_off_t consumed);
-typedef apr_status_t h2_mplx_req_engine_init(struct h2_req_engine *engine, 
-                                             const char *id, 
-                                             const char *type,
-                                             apr_pool_t *pool, 
-                                             apr_size_t req_buffer_size,
-                                             request_rec *r,
-                                             h2_output_consumed **pconsumed,
-                                             void **pbaton);
-
-apr_status_t h2_mplx_req_engine_push(const char *ngn_type, 
-                                     request_rec *r, 
-                                     h2_mplx_req_engine_init *einit);
-apr_status_t h2_mplx_req_engine_pull(struct h2_req_engine *ngn, 
-                                     apr_read_type_e block, 
-                                     int capacity, 
-                                     request_rec **pr);
-void h2_mplx_req_engine_done(struct h2_req_engine *ngn, conn_rec *r_conn,
-                             apr_status_t status);
-
 #endif /* defined(__mod_h2__h2_mplx__) */
diff --git a/modules/http2/h2_ngn_shed.c b/modules/http2/h2_ngn_shed.c
index fb85776..e69de29 100644
--- a/modules/http2/h2_ngn_shed.c
+++ b/modules/http2/h2_ngn_shed.c
@@ -1,392 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- 
-#include <assert.h>
-#include <stddef.h>
-#include <stdlib.h>
-
-#include <apr_thread_mutex.h>
-#include <apr_thread_cond.h>
-#include <apr_strings.h>
-#include <apr_time.h>
-
-#include <httpd.h>
-#include <http_core.h>
-#include <http_log.h>
-
-#include "mod_http2.h"
-
-#include "h2_private.h"
-#include "h2.h"
-#include "h2_config.h"
-#include "h2_conn.h"
-#include "h2_ctx.h"
-#include "h2_h2.h"
-#include "h2_mplx.h"
-#include "h2_request.h"
-#include "h2_task.h"
-#include "h2_util.h"
-#include "h2_ngn_shed.h"
-
-
-typedef struct h2_ngn_entry h2_ngn_entry;
-struct h2_ngn_entry {
-    APR_RING_ENTRY(h2_ngn_entry) link;
-    h2_task *task;
-    request_rec *r;
-};
-
-#define H2_NGN_ENTRY_NEXT(e)	APR_RING_NEXT((e), link)
-#define H2_NGN_ENTRY_PREV(e)	APR_RING_PREV((e), link)
-#define H2_NGN_ENTRY_REMOVE(e)	APR_RING_REMOVE((e), link)
-
-#define H2_REQ_ENTRIES_SENTINEL(b)	APR_RING_SENTINEL((b), h2_ngn_entry, link)
-#define H2_REQ_ENTRIES_EMPTY(b)	APR_RING_EMPTY((b), h2_ngn_entry, link)
-#define H2_REQ_ENTRIES_FIRST(b)	APR_RING_FIRST(b)
-#define H2_REQ_ENTRIES_LAST(b)	APR_RING_LAST(b)
-
-#define H2_REQ_ENTRIES_INSERT_HEAD(b, e) do {				\
-h2_ngn_entry *ap__b = (e);                                        \
-APR_RING_INSERT_HEAD((b), ap__b, h2_ngn_entry, link);	\
-} while (0)
-
-#define H2_REQ_ENTRIES_INSERT_TAIL(b, e) do {				\
-h2_ngn_entry *ap__b = (e);					\
-APR_RING_INSERT_TAIL((b), ap__b, h2_ngn_entry, link);	\
-} while (0)
-
-struct h2_req_engine {
-    const char *id;        /* identifier */
-    const char *type;      /* name of the engine type */
-    apr_pool_t *pool;      /* pool for engine specific allocations */
-    conn_rec *c;           /* connection this engine is assigned to */
-    h2_task *task;         /* the task this engine is based on, running in */
-    h2_ngn_shed *shed;
-
-    unsigned int shutdown : 1; /* engine is being shut down */
-    unsigned int done : 1;     /* engine has finished */
-
-    APR_RING_HEAD(h2_req_entries, h2_ngn_entry) entries;
-    int capacity;     /* maximum concurrent requests */
-    int no_assigned;  /* # of assigned requests */
-    int no_live;      /* # of live */
-    int no_finished;  /* # of finished */
-    
-    h2_output_consumed *out_consumed;
-    void *out_consumed_ctx;
-};
-
-const char *h2_req_engine_get_id(h2_req_engine *engine)
-{
-    return engine->id;
-}
-
-int h2_req_engine_is_shutdown(h2_req_engine *engine)
-{
-    return engine->shutdown;
-}
-
-void h2_req_engine_out_consumed(h2_req_engine *engine, conn_rec *c, 
-                                apr_off_t bytes)
-{
-    if (engine->out_consumed) {
-        engine->out_consumed(engine->out_consumed_ctx, c, bytes);
-    }
-}
-
-h2_ngn_shed *h2_ngn_shed_create(apr_pool_t *pool, conn_rec *c,
-                                int default_capacity, 
-                                apr_size_t req_buffer_size)
-{
-    h2_ngn_shed *shed;
-    
-    shed = apr_pcalloc(pool, sizeof(*shed));
-    shed->c = c;
-    shed->pool = pool;
-    shed->default_capacity = default_capacity;
-    shed->req_buffer_size = req_buffer_size;
-    shed->ngns = apr_hash_make(pool);
-    
-    return shed;
-}
-
-void h2_ngn_shed_set_ctx(h2_ngn_shed *shed, void *user_ctx)
-{
-    shed->user_ctx = user_ctx;
-}
-
-void *h2_ngn_shed_get_ctx(h2_ngn_shed *shed)
-{
-    return shed->user_ctx;
-}
-
-h2_ngn_shed *h2_ngn_shed_get_shed(h2_req_engine *ngn)
-{
-    return ngn->shed;
-}
-
-void h2_ngn_shed_abort(h2_ngn_shed *shed)
-{
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c, APLOGNO(03394)
-                  "h2_ngn_shed(%ld): abort", shed->c->id);
-    shed->aborted = 1;
-}
-
-static void ngn_add_task(h2_req_engine *ngn, h2_task *task, request_rec *r)
-{
-    h2_ngn_entry *entry = apr_pcalloc(task->pool, sizeof(*entry));
-    APR_RING_ELEM_INIT(entry, link);
-    entry->task = task;
-    entry->r = r;
-    H2_REQ_ENTRIES_INSERT_TAIL(&ngn->entries, entry);
-    ngn->no_assigned++;
-}
-
-
-apr_status_t h2_ngn_shed_push_request(h2_ngn_shed *shed, const char *ngn_type, 
-                                      request_rec *r, 
-                                      http2_req_engine_init *einit) 
-{
-    h2_req_engine *ngn;
-    h2_task *task = h2_ctx_rget_task(r);
-
-    ap_assert(task);
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c,
-                  "h2_ngn_shed(%ld): PUSHing request (task=%s)", shed->c->id, 
-                  task->id);
-    if (task->request->serialize) {
-        /* Max compatibility, deny processing of this */
-        return APR_EOF;
-    }
-    
-    if (task->assigned) {
-        --task->assigned->no_assigned;
-        --task->assigned->no_live;
-        task->assigned = NULL;
-    }
-    
-    if (task->engine) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, 
-                      "h2_ngn_shed(%ld): push task(%s) hosting engine %s " 
-                      "already with %d tasks", 
-                      shed->c->id, task->id, task->engine->id,
-                      task->engine->no_assigned);
-        task->assigned = task->engine;
-        ngn_add_task(task->engine, task, r);
-        return APR_SUCCESS;
-    }
-    
-    ngn = apr_hash_get(shed->ngns, ngn_type, APR_HASH_KEY_STRING);
-    if (ngn && !ngn->shutdown) {
-        /* this task will be processed in another thread,
-         * freeze any I/O for the time being. */
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
-                      "h2_ngn_shed(%ld): pushing request %s to %s", 
-                      shed->c->id, task->id, ngn->id);
-        if (!h2_task_has_thawed(task)) {
-            h2_task_freeze(task);
-        }
-        ngn_add_task(ngn, task, r);
-        return APR_SUCCESS;
-    }
-    
-    /* no existing engine or being shut down, start a new one */
-    if (einit) {
-        apr_status_t status;
-        apr_pool_t *pool = task->pool;
-        h2_req_engine *newngn;
-        
-        newngn = apr_pcalloc(pool, sizeof(*ngn));
-        newngn->pool = pool;
-        newngn->id   = apr_psprintf(pool, "ngn-%s", task->id);
-        newngn->type = apr_pstrdup(pool, ngn_type);
-        newngn->c    = task->c;
-        newngn->shed = shed;
-        newngn->capacity = shed->default_capacity;
-        newngn->no_assigned = 1;
-        newngn->no_live = 1;
-        APR_RING_INIT(&newngn->entries, h2_ngn_entry, link);
-        
-        status = einit(newngn, newngn->id, newngn->type, newngn->pool,
-                       shed->req_buffer_size, r,
-                       &newngn->out_consumed, &newngn->out_consumed_ctx);
-        
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, task->c, APLOGNO(03395)
-                      "h2_ngn_shed(%ld): create engine %s (%s)", 
-                      shed->c->id, newngn->id, newngn->type);
-        if (status == APR_SUCCESS) {
-            newngn->task = task;
-            task->engine = newngn;
-            task->assigned = newngn;
-            apr_hash_set(shed->ngns, newngn->type, APR_HASH_KEY_STRING, newngn);
-        }
-        return status;
-    }
-    return APR_EOF;
-}
-
-static h2_ngn_entry *pop_detached(h2_req_engine *ngn)
-{
-    h2_ngn_entry *entry;
-    for (entry = H2_REQ_ENTRIES_FIRST(&ngn->entries);
-         entry != H2_REQ_ENTRIES_SENTINEL(&ngn->entries);
-         entry = H2_NGN_ENTRY_NEXT(entry)) {
-        if (h2_task_has_thawed(entry->task) 
-            || (entry->task->engine == ngn)) {
-            /* The task hosting this engine can always be pulled by it.
-             * For other task, they need to become detached, e.g. no longer
-             * assigned to another worker. */
-            H2_NGN_ENTRY_REMOVE(entry);
-            return entry;
-        }
-    }
-    return NULL;
-}
-
-apr_status_t h2_ngn_shed_pull_request(h2_ngn_shed *shed, 
-                                      h2_req_engine *ngn, 
-                                      int capacity, 
-                                      int want_shutdown,
-                                      request_rec **pr)
-{   
-    h2_ngn_entry *entry;
-    
-    ap_assert(ngn);
-    *pr = NULL;
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, shed->c, APLOGNO(03396)
-                  "h2_ngn_shed(%ld): pull task for engine %s, shutdown=%d", 
-                  shed->c->id, ngn->id, want_shutdown);
-    if (shed->aborted) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c, APLOGNO(03397)
-                      "h2_ngn_shed(%ld): abort while pulling requests %s", 
-                      shed->c->id, ngn->id);
-        ngn->shutdown = 1;
-        return APR_ECONNABORTED;
-    }
-    
-    ngn->capacity = capacity;
-    if (H2_REQ_ENTRIES_EMPTY(&ngn->entries)) {
-        if (want_shutdown) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c,
-                          "h2_ngn_shed(%ld): emtpy queue, shutdown engine %s", 
-                          shed->c->id, ngn->id);
-            ngn->shutdown = 1;
-        }
-        return ngn->shutdown? APR_EOF : APR_EAGAIN;
-    }
-    
-    if ((entry = pop_detached(ngn))) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, entry->task->c, APLOGNO(03398)
-                      "h2_ngn_shed(%ld): pulled request %s for engine %s", 
-                      shed->c->id, entry->task->id, ngn->id);
-        ngn->no_live++;
-        *pr = entry->r;
-        entry->task->assigned = ngn;
-        /* task will now run in ngn's own thread. Modules like lua
-         * seem to require the correct thread set in the conn_rec.
-         * See PR 59542. */
-        if (entry->task->c && ngn->c) {
-            entry->task->c->current_thread = ngn->c->current_thread;
-        }
-        if (entry->task->engine == ngn) {
-            /* If an engine pushes its own base task, and then pulls
-             * it back to itself again, it needs to be thawed.
-             */
-            h2_task_thaw(entry->task);
-        }
-        return APR_SUCCESS;
-    }
-    
-    if (1) {
-        h2_ngn_entry *entry = H2_REQ_ENTRIES_FIRST(&ngn->entries);
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, shed->c, APLOGNO(03399)
-                      "h2_ngn_shed(%ld): pull task, nothing, first task %s", 
-                      shed->c->id, entry->task->id);
-    }
-    return APR_EAGAIN;
-}
-                                 
-static apr_status_t ngn_done_task(h2_ngn_shed *shed, h2_req_engine *ngn, 
-                                  h2_task *task, int waslive, int aborted)
-{
-    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, shed->c, APLOGNO(03400)
-                  "h2_ngn_shed(%ld): task %s %s by %s", 
-                  shed->c->id, task->id, aborted? "aborted":"done", ngn->id);
-    ngn->no_finished++;
-    if (waslive) ngn->no_live--;
-    ngn->no_assigned--;
-    task->assigned = NULL;
-    
-    return APR_SUCCESS;
-}
-                                
-apr_status_t h2_ngn_shed_done_task(h2_ngn_shed *shed, 
-                                    struct h2_req_engine *ngn, h2_task *task)
-{
-    return ngn_done_task(shed, ngn, task, 1, 0);
-}
-                                
-void h2_ngn_shed_done_ngn(h2_ngn_shed *shed, struct h2_req_engine *ngn)
-{
-    if (ngn->done) {
-        return;
-    }
-    
-    if (!shed->aborted && !H2_REQ_ENTRIES_EMPTY(&ngn->entries)) {
-        h2_ngn_entry *entry;
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c,
-                      "h2_ngn_shed(%ld): exit engine %s (%s), "
-                      "has still requests queued, shutdown=%d,"
-                      "assigned=%ld, live=%ld, finished=%ld", 
-                      shed->c->id, ngn->id, ngn->type,
-                      ngn->shutdown, 
-                      (long)ngn->no_assigned, (long)ngn->no_live,
-                      (long)ngn->no_finished);
-        for (entry = H2_REQ_ENTRIES_FIRST(&ngn->entries);
-             entry != H2_REQ_ENTRIES_SENTINEL(&ngn->entries);
-             entry = H2_NGN_ENTRY_NEXT(entry)) {
-            h2_task *task = entry->task;
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c,
-                          "h2_ngn_shed(%ld): engine %s has queued task %s, "
-                          "frozen=%d, aborting",
-                          shed->c->id, ngn->id, task->id, task->frozen);
-            ngn_done_task(shed, ngn, task, 0, 1);
-            task->engine = task->assigned = NULL;
-        }
-    }
-    if (!shed->aborted && (ngn->no_assigned > 1 || ngn->no_live > 1)) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c,
-                      "h2_ngn_shed(%ld): exit engine %s (%s), "
-                      "assigned=%ld, live=%ld, finished=%ld", 
-                      shed->c->id, ngn->id, ngn->type,
-                      (long)ngn->no_assigned, (long)ngn->no_live,
-                      (long)ngn->no_finished);
-    }
-    else {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, shed->c,
-                      "h2_ngn_shed(%ld): exit engine %s", 
-                      shed->c->id, ngn->id);
-    }
-    
-    apr_hash_set(shed->ngns, ngn->type, APR_HASH_KEY_STRING, NULL);
-    ngn->done = 1;
-}
-
-void h2_ngn_shed_destroy(h2_ngn_shed *shed)
-{
-    ap_assert(apr_hash_count(shed->ngns) == 0);
-}
-
diff --git a/modules/http2/h2_ngn_shed.h b/modules/http2/h2_ngn_shed.h
index 7764c18..e69de29 100644
--- a/modules/http2/h2_ngn_shed.h
+++ b/modules/http2/h2_ngn_shed.h
@@ -1,79 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef h2_req_shed_h
-#define h2_req_shed_h
-
-struct h2_req_engine;
-struct h2_task;
-
-typedef struct h2_ngn_shed h2_ngn_shed;
-struct h2_ngn_shed {
-    conn_rec *c;
-    apr_pool_t *pool;
-    apr_hash_t *ngns;
-    void *user_ctx;
-    
-    unsigned int aborted : 1;
-    
-    int default_capacity;
-    apr_size_t req_buffer_size; /* preferred buffer size for responses */
-};
-
-const char *h2_req_engine_get_id(h2_req_engine *engine);
-int h2_req_engine_is_shutdown(h2_req_engine *engine);
-
-void h2_req_engine_out_consumed(h2_req_engine *engine, conn_rec *c, 
-                                apr_off_t bytes);
-
-typedef apr_status_t h2_shed_ngn_init(h2_req_engine *engine, 
-                                      const char *id, 
-                                      const char *type,
-                                      apr_pool_t *pool, 
-                                      apr_size_t req_buffer_size,
-                                      request_rec *r,
-                                      h2_output_consumed **pconsumed,
-                                      void **pbaton);
-
-h2_ngn_shed *h2_ngn_shed_create(apr_pool_t *pool, conn_rec *c,
-                                int default_capactiy, 
-                                apr_size_t req_buffer_size); 
-
-void h2_ngn_shed_destroy(h2_ngn_shed *shed);
-
-void h2_ngn_shed_set_ctx(h2_ngn_shed *shed, void *user_ctx);
-void *h2_ngn_shed_get_ctx(h2_ngn_shed *shed);
-
-h2_ngn_shed *h2_ngn_shed_get_shed(struct h2_req_engine *ngn);
-
-void h2_ngn_shed_abort(h2_ngn_shed *shed);
-
-apr_status_t h2_ngn_shed_push_request(h2_ngn_shed *shed, const char *ngn_type, 
-                                      request_rec *r, 
-                                      h2_shed_ngn_init *init_cb);
-
-apr_status_t h2_ngn_shed_pull_request(h2_ngn_shed *shed, h2_req_engine *pub_ngn, 
-                                      int capacity, 
-                                      int want_shutdown, request_rec **pr);
-
-apr_status_t h2_ngn_shed_done_task(h2_ngn_shed *shed, 
-                                   struct h2_req_engine *ngn, 
-                                   struct h2_task *task);
-
-void h2_ngn_shed_done_ngn(h2_ngn_shed *shed, struct h2_req_engine *ngn);
-
-
-#endif /* h2_req_shed_h */
diff --git a/modules/http2/h2_proxy_session.c b/modules/http2/h2_proxy_session.c
index 8389c7c..3a2718f 100644
--- a/modules/http2/h2_proxy_session.c
+++ b/modules/http2/h2_proxy_session.c
@@ -429,12 +429,6 @@ static int stream_response_data(nghttp2_session *ngh2, uint8_t flags,
                                   stream_id, NGHTTP2_STREAM_CLOSED);
         return NGHTTP2_ERR_STREAM_CLOSING;
     }
-    if (stream->standalone) {
-        nghttp2_session_consume(ngh2, stream_id, len);
-        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, stream->r,
-                      "h2_proxy_session(%s): stream %d, win_update %d bytes",
-                      session->id, stream_id, (int)len);
-    }
     return 0;
 }
 
@@ -641,7 +635,7 @@ h2_proxy_session *h2_proxy_session_setup(const char *id, proxy_conn_rec *p_conn,
         
         nghttp2_option_new(&option);
         nghttp2_option_set_peer_max_concurrent_streams(option, 100);
-        nghttp2_option_set_no_auto_window_update(option, 1);
+        nghttp2_option_set_no_auto_window_update(option, 0);
         
         nghttp2_session_client_new2(&session->ngh2, cbs, session, option);
         
@@ -653,10 +647,12 @@ h2_proxy_session *h2_proxy_session_setup(const char *id, proxy_conn_rec *p_conn,
     }
     else {
         h2_proxy_session *session = p_conn->data;
-        apr_interval_time_t age = apr_time_now() - session->last_frame_received;
-        if (age > apr_time_from_sec(1)) {
-            session->check_ping = 1;
-            nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup");
+        if (!session->check_ping) {
+            apr_interval_time_t age = apr_time_now() - session->last_frame_received;
+            if (age > apr_time_from_sec(1)) {
+                session->check_ping = 1;
+                nghttp2_submit_ping(session->ngh2, 0, (const uint8_t *)"nevergonnagiveyouup");
+            }
         }
     }
     return p_conn->data;
@@ -1543,42 +1539,3 @@ typedef struct {
     int updated;
 } win_update_ctx;
 
-static int win_update_iter(void *udata, void *val)
-{
-    win_update_ctx *ctx = udata;
-    h2_proxy_stream *stream = val;
-    
-    if (stream->r && stream->r->connection == ctx->c) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, ctx->session->c, 
-                      "h2_proxy_session(%s-%d): win_update %ld bytes",
-                      ctx->session->id, (int)stream->id, (long)ctx->bytes);
-        nghttp2_session_consume(ctx->session->ngh2, stream->id, ctx->bytes);
-        ctx->updated = 1;
-        return 0;
-    }
-    return 1;
-}
-
-
-void h2_proxy_session_update_window(h2_proxy_session *session, 
-                                    conn_rec *c, apr_off_t bytes)
-{
-    if (!h2_proxy_ihash_empty(session->streams)) {
-        win_update_ctx ctx;
-        ctx.session = session;
-        ctx.c = c;
-        ctx.bytes = bytes;
-        ctx.updated = 0;
-        h2_proxy_ihash_iter(session->streams, win_update_iter, &ctx);
-        
-        if (!ctx.updated) {
-            /* could not find the stream any more, possibly closed, update
-             * the connection window at least */
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, 
-                          "h2_proxy_session(%s): win_update conn %ld bytes",
-                          session->id, (long)bytes);
-            nghttp2_session_consume_connection(session->ngh2, (size_t)bytes);
-        }
-    }
-}
-
diff --git a/modules/http2/h2_proxy_session.h b/modules/http2/h2_proxy_session.h
index ecebb61..1d0750b 100644
--- a/modules/http2/h2_proxy_session.h
+++ b/modules/http2/h2_proxy_session.h
@@ -120,9 +120,6 @@ void h2_proxy_session_cancel_all(h2_proxy_session *s);
 
 void h2_proxy_session_cleanup(h2_proxy_session *s, h2_proxy_request_done *done);
 
-void h2_proxy_session_update_window(h2_proxy_session *s, 
-                                    conn_rec *c, apr_off_t bytes);
-
 #define H2_PROXY_REQ_URL_NOTE   "h2-proxy-req-url"
 
 #endif /* h2_proxy_session_h */
diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c
index 5ee88e9..5893c8b 100644
--- a/modules/http2/h2_request.c
+++ b/modules/http2/h2_request.c
@@ -17,6 +17,7 @@
 #include <assert.h>
 
 #include <apr_strings.h>
+#include <ap_mmn.h>
 
 #include <httpd.h>
 #include <http_core.h>
@@ -84,8 +85,7 @@ apr_status_t h2_request_rcreate(h2_request **preq, apr_pool_t *pool,
     req->path      = path;
     req->headers   = apr_table_make(pool, 10);
     if (r->server) {
-        req->serialize = h2_config_geti(h2_config_sget(r->server), 
-                                        H2_CONF_SER_HEADERS);
+        req->serialize = h2_config_rgeti(r, H2_CONF_SER_HEADERS);
     }
 
     x.pool = pool;
@@ -206,13 +206,11 @@ h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
     return dst;
 }
 
-request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
+#if !AP_MODULE_MAGIC_AT_LEAST(20150222, 13)
+static request_rec *my_ap_create_request(conn_rec *c)
 {
-    int access_status = HTTP_OK;    
-    const char *rpath;
     apr_pool_t *p;
     request_rec *r;
-    const char *s;
 
     apr_pool_create(&p, c->pool);
     apr_pool_tag(p, "request");
@@ -226,8 +224,8 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
     r->ap_auth_type    = NULL;
     
     r->allowed_methods = ap_make_method_list(p, 2);
-    
-    r->headers_in      = apr_table_clone(r->pool, req->headers);
+
+    r->headers_in      = apr_table_make(r->pool, 5);
     r->trailers_in     = apr_table_make(r->pool, 5);
     r->subprocess_env  = apr_table_make(r->pool, 25);
     r->headers_out     = apr_table_make(r->pool, 12);
@@ -262,6 +260,24 @@ request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
     r->useragent_addr = c->client_addr;
     r->useragent_ip = c->client_ip;
     
+    return r;
+}
+#endif
+
+request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
+{
+    int access_status = HTTP_OK;    
+    const char *rpath;
+    const char *s;
+
+#if AP_MODULE_MAGIC_AT_LEAST(20150222, 13)
+    request_rec *r = ap_create_request(c);
+#else
+    request_rec *r = my_ap_create_request(c);
+#endif
+
+    r->headers_in = apr_table_clone(r->pool, req->headers);
+
     ap_run_pre_read_request(r, c);
     
     /* Time to populate r with the data we have. */
@@ -337,3 +353,4 @@ traceout:
 }
 
 
+
diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c
index 3f0e9c9..f153422 100644
--- a/modules/http2/h2_session.c
+++ b/modules/http2/h2_session.c
@@ -495,9 +495,7 @@ static int on_send_data_cb(nghttp2_session *ngh2,
         return NGHTTP2_ERR_WOULDBLOCK;
     }
 
-    if (frame->data.padlen > H2_MAX_PADLEN) {
-        return NGHTTP2_ERR_PROTO;
-    }
+    ap_assert(frame->data.padlen <= (H2_MAX_PADLEN+1));
     padlen = (unsigned char)frame->data.padlen;
     
     stream = h2_session_stream_get(session, stream_id);
@@ -513,8 +511,9 @@ static int on_send_data_cb(nghttp2_session *ngh2,
                   H2_STRM_MSG(stream, "send_data_cb for %ld bytes"),
                   (long)length);
                   
-    status = h2_conn_io_write(&session->io, (const char *)framehd, 9);
+    status = h2_conn_io_write(&session->io, (const char *)framehd, H2_FRAME_HDR_LEN);
     if (padlen && status == APR_SUCCESS) {
+        --padlen;
         status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
     }
     
@@ -622,6 +621,39 @@ static int on_invalid_header_cb(nghttp2_session *ngh2,
 }
 #endif
 
+static ssize_t select_padding_cb(nghttp2_session *ngh2, 
+                                 const nghttp2_frame *frame, 
+                                 size_t max_payloadlen, void *user_data)
+{
+    h2_session *session = user_data;
+    ssize_t frame_len = frame->hd.length + H2_FRAME_HDR_LEN; /* the total length without padding */
+    ssize_t padded_len = frame_len;
+
+    /* Determine # of padding bytes to append to frame. Unless session->padding_always
+     * the number my be capped by the ui.write_size that currently applies. 
+     */
+    if (session->padding_max) {
+        int n = ap_random_pick(0, session->padding_max);
+        padded_len = H2MIN(max_payloadlen + H2_FRAME_HDR_LEN, frame_len + n); 
+    }
+
+    if (padded_len != frame_len) {
+        if (!session->padding_always && session->io.write_size 
+            && (padded_len > session->io.write_size)
+            && (frame_len <= session->io.write_size)) {
+            padded_len = session->io.write_size;
+        }
+        if (APLOGctrace2(session->c)) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
+                          "select padding from [%d, %d]: %d (frame length: 0x%04x, write size: %d)", 
+                          (int)frame_len, (int)max_payloadlen+H2_FRAME_HDR_LEN, 
+                          (int)(padded_len - frame_len), (int)padded_len, (int)session->io.write_size);
+        }
+        return padded_len - H2_FRAME_HDR_LEN;
+    }
+    return frame->hd.length;
+}
+
 #define NGH2_SET_CALLBACK(callbacks, name, fn)\
 nghttp2_session_callbacks_set_##name##_callback(callbacks, fn)
 
@@ -647,6 +679,7 @@ static apr_status_t init_callbacks(conn_rec *c, nghttp2_session_callbacks **pcb)
 #ifdef H2_NG2_INVALID_HEADER_CB
     NGH2_SET_CALLBACK(*pcb, on_invalid_header, on_invalid_header_cb);
 #endif
+    NGH2_SET_CALLBACK(*pcb, select_padding, select_padding_cb);
     return APR_SUCCESS;
 }
 
@@ -757,9 +790,8 @@ static apr_status_t session_pool_cleanup(void *data)
 {
     conn_rec *c = data;
     h2_session *session;
-    h2_ctx *ctx = h2_ctx_get(c, 0);
     
-    if (ctx && (session = h2_ctx_session_get(ctx))) {
+    if ((session = h2_ctx_get_session(c))) {
         /* if the session is still there, now is the last chance
          * to perform cleanup. Normally, cleanup should have happened
          * earlier in the connection pre_close. Main reason is that
@@ -775,11 +807,8 @@ static apr_status_t session_pool_cleanup(void *data)
     return APR_SUCCESS;
 }
 
-static apr_status_t h2_session_create_int(h2_session **psession,
-                                          conn_rec *c,
-                                          request_rec *r,
-                                          h2_ctx *ctx, 
-                                          h2_workers *workers)
+apr_status_t h2_session_create(h2_session **psession, conn_rec *c, request_rec *r,
+                               server_rec *s, h2_workers *workers)
 {
     nghttp2_session_callbacks *callbacks = NULL;
     nghttp2_option *options = NULL;
@@ -820,19 +849,16 @@ static apr_status_t h2_session_create_int(h2_session **psession,
     session->id = c->id;
     session->c = c;
     session->r = r;
-    session->s = h2_ctx_server_get(ctx);
+    session->s = s;
     session->pool = pool;
-    session->config = h2_config_sget(session->s);
     session->workers = workers;
     
     session->state = H2_SESSION_ST_INIT;
     session->local.accepting = 1;
     session->remote.accepting = 1;
     
-    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->max_stream_count = h2_config_sgeti(s, H2_CONF_MAX_STREAMS);
+    session->max_stream_mem = h2_config_sgeti(s, H2_CONF_STREAM_MAX_MEM);
     
     status = apr_thread_cond_create(&session->iowait, session->pool);
     if (status != APR_SUCCESS) {
@@ -862,14 +888,18 @@ static apr_status_t h2_session_create_int(h2_session **psession,
     session->monitor->on_state_event = on_stream_state_event;
     session->monitor->on_event = on_stream_event;
     
-    session->mplx = h2_mplx_create(c, session->pool, session->config, 
-                                   workers);
+    session->mplx = h2_mplx_create(c, s, session->pool, workers);
     
     /* connection input filter that feeds the session */
     session->cin = h2_filter_cin_create(session);
     ap_add_input_filter("H2_IN", session->cin, r, c);
     
-    h2_conn_io_init(&session->io, c, session->config);
+    h2_conn_io_init(&session->io, c, s);
+    session->padding_max = h2_config_sgeti(s, H2_CONF_PADDING_BITS);
+    if (session->padding_max) {
+        session->padding_max = (0x01 << session->padding_max) - 1; 
+    }
+    session->padding_always = h2_config_sgeti(s, H2_CONF_PADDING_ALWAYS);
     session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
     
     status = init_callbacks(c, &callbacks);
@@ -888,8 +918,7 @@ static apr_status_t h2_session_create_int(h2_session **psession,
         apr_pool_destroy(pool);
         return status;
     }
-    nghttp2_option_set_peer_max_concurrent_streams(
-                                                   options, (uint32_t)session->max_stream_count);
+    nghttp2_option_set_peer_max_concurrent_streams(options, (uint32_t)session->max_stream_count);
     /* We need to handle window updates ourself, otherwise we
      * get flooded by nghttp2. */
     nghttp2_option_set_no_auto_window_update(options, 1);
@@ -907,7 +936,7 @@ static apr_status_t h2_session_create_int(h2_session **psession,
         return APR_ENOMEM;
     }
     
-    n = h2_config_geti(session->config, H2_CONF_PUSH_DIARY_SIZE);
+    n = h2_config_sgeti(s, H2_CONF_PUSH_DIARY_SIZE);
     session->push_diary = h2_push_diary_create(session->pool, n);
     
     if (APLOGcdebug(c)) {
@@ -924,22 +953,11 @@ static apr_status_t h2_session_create_int(h2_session **psession,
                       (int)session->push_diary->N);
     }
     
-    apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup);    
+    apr_pool_pre_cleanup_register(pool, c, session_pool_cleanup);
+        
     return APR_SUCCESS;
 }
 
-apr_status_t h2_session_create(h2_session **psession, 
-                               conn_rec *c, h2_ctx *ctx, h2_workers *workers)
-{
-    return h2_session_create_int(psession, c, NULL, ctx, workers);
-}
-
-apr_status_t h2_session_rcreate(h2_session **psession, 
-                                request_rec *r, h2_ctx *ctx, h2_workers *workers)
-{
-    return h2_session_create_int(psession, r->connection, r, ctx, workers);
-}
-
 static apr_status_t h2_session_start(h2_session *session, int *rv)
 {
     apr_status_t status = APR_SUCCESS;
@@ -1004,7 +1022,7 @@ static apr_status_t h2_session_start(h2_session *session, int *rv)
     settings[slen].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
     settings[slen].value = (uint32_t)session->max_stream_count;
     ++slen;
-    win_size = h2_config_geti(session->config, H2_CONF_WIN_SIZE);
+    win_size = h2_config_sgeti(session->s, H2_CONF_WIN_SIZE);
     if (win_size != H2_INITIAL_WINDOW_SIZE) {
         settings[slen].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
         settings[slen].value = win_size;
@@ -1276,7 +1294,7 @@ int h2_session_push_enabled(h2_session *session)
 {
     /* iff we can and they can and want */
     return (session->remote.accepting /* remote GOAWAY received */
-            && h2_config_geti(session->config, H2_CONF_PUSH)
+            && h2_config_sgeti(session->s, H2_CONF_PUSH)
             && nghttp2_session_get_remote_settings(session->ngh2, 
                    NGHTTP2_SETTINGS_ENABLE_PUSH));
 }
@@ -1320,6 +1338,7 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
                                       int eos)
 {
     apr_status_t status = APR_SUCCESS;
+    const char *s;
     int rv = 0;
 
     ap_assert(session);
@@ -1387,8 +1406,12 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
             && (headers->status < 400)
             && (headers->status != 304)
             && h2_session_push_enabled(session)) {
-            
-            h2_stream_submit_pushes(stream, headers);
+            /* PUSH is possibe and enabled on server, unless the request
+             * denies it, submit resources to push */
+            s = apr_table_get(headers->notes, H2_PUSH_MODE_NOTE);
+            if (!s || strcmp(s, "0")) {
+                h2_stream_submit_pushes(stream, headers);
+            }
         }
         
         if (!stream->pref_priority) {
@@ -1410,7 +1433,7 @@ static apr_status_t on_stream_headers(h2_session *session, h2_stream *stream,
         }
         
         if (headers->status == 103 
-            && !h2_config_geti(session->config, H2_CONF_EARLY_HINTS)) {
+            && !h2_config_sgeti(session->s, H2_CONF_EARLY_HINTS)) {
             /* suppress sending this to the client, it might have triggered 
              * pushes and served its purpose nevertheless */
             rv = 0;
@@ -2086,7 +2109,7 @@ apr_status_t h2_session_process(h2_session *session, int async)
         switch (session->state) {
             case H2_SESSION_ST_INIT:
                 ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
-                if (!h2_is_acceptable_connection(c, 1)) {
+                if (!h2_is_acceptable_connection(c, session->r, 1)) {
                     update_child_status(session, SERVER_BUSY_READ, 
                                         "inadequate security");
                     h2_session_shutdown(session, 
diff --git a/modules/http2/h2_session.h b/modules/http2/h2_session.h
index df2a862..1bf6f05 100644
--- a/modules/http2/h2_session.h
+++ b/modules/http2/h2_session.h
@@ -80,12 +80,13 @@ typedef struct h2_session {
     request_rec *r;                 /* the request that started this in case
                                      * of 'h2c', NULL otherwise */
     server_rec *s;                  /* server/vhost we're starting on */
-    const struct h2_config *config; /* Relevant config for this session */
     apr_pool_t *pool;               /* pool to use in session */
     struct h2_mplx *mplx;           /* multiplexer for stream data */
     struct h2_workers *workers;     /* for executing stream tasks */
     struct h2_filter_cin *cin;      /* connection input filter context */
     h2_conn_io io;                  /* io on httpd conn filters */
+    int padding_max;                /* max number of padding bytes */
+    int padding_always;             /* padding has precedence over I/O optimizations */
     struct nghttp2_session *ngh2;   /* the nghttp2 session (internal use) */
 
     h2_session_state state;         /* state session is in */
@@ -142,27 +143,15 @@ const char *h2_session_state_str(h2_session_state state);
  * The session will apply the configured parameter.
  * @param psession pointer receiving the created session on success or NULL
  * @param c       the connection to work on
+ * @param r       optional request when protocol was upgraded
  * @param cfg     the module config to apply
  * @param workers the worker pool to use
  * @return the created session
  */
 apr_status_t h2_session_create(h2_session **psession,
-                               conn_rec *c, struct h2_ctx *ctx, 
+                               conn_rec *c, request_rec *r, server_rec *, 
                                struct h2_workers *workers);
 
-/**
- * Create a new h2_session for the given request.
- * The session will apply the configured parameter.
- * @param psession pointer receiving the created session on success or NULL
- * @param r       the request that was upgraded
- * @param cfg     the module config to apply
- * @param workers the worker pool to use
- * @return the created session
- */
-apr_status_t h2_session_rcreate(h2_session **psession,
-                                request_rec *r, struct h2_ctx *ctx,
-                                struct h2_workers *workers);
-
 void h2_session_event(h2_session *session, h2_session_event_t ev, 
                              int err, const char *msg);
 
diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c
index 22c5902..b5763ac 100644
--- a/modules/http2/h2_stream.c
+++ b/modules/http2/h2_stream.c
@@ -365,9 +365,8 @@ void h2_stream_dispatch(h2_stream *stream, h2_stream_event_t ev)
 static void set_policy_for(h2_stream *stream, h2_request *r) 
 {
     int enabled = h2_session_push_enabled(stream->session);
-    stream->push_policy = h2_push_policy_determine(r->headers, stream->pool, 
-                                                   enabled);
-    r->serialize = h2_config_geti(stream->session->config, H2_CONF_SER_HEADERS);
+    stream->push_policy = h2_push_policy_determine(r->headers, stream->pool, enabled);
+    r->serialize = h2_config_sgeti(stream->session->s, H2_CONF_SER_HEADERS);
 }
 
 apr_status_t h2_stream_send_frame(h2_stream *stream, int ftype, int flags, size_t frame_len)
@@ -855,7 +854,7 @@ apr_status_t h2_stream_out_prepare(h2_stream *stream, apr_off_t *plen,
      * is requested. But we can reduce the size in case the master
      * connection operates in smaller chunks. (TSL warmup) */
     if (stream->session->io.write_size > 0) {
-        max_chunk = stream->session->io.write_size - 9; /* header bits */ 
+        max_chunk = stream->session->io.write_size - H2_FRAME_HDR_LEN; 
     }
     requested = (*plen > 0)? H2MIN(*plen, max_chunk) : max_chunk;
     
@@ -987,7 +986,7 @@ const h2_priority *h2_stream_get_priority(h2_stream *stream,
         const char *ctype = apr_table_get(response->headers, "content-type");
         if (ctype) {
             /* FIXME: Not good enough, config needs to come from request->server */
-            return h2_config_get_priority(stream->session->config, ctype);
+            return h2_cconfig_get_priority(stream->session->c, ctype);
         }
     }
     return NULL;
diff --git a/modules/http2/h2_switch.c b/modules/http2/h2_switch.c
index 5e73568..07a30cc 100644
--- a/modules/http2/h2_switch.c
+++ b/modules/http2/h2_switch.c
@@ -55,7 +55,6 @@ static int h2_protocol_propose(conn_rec *c, request_rec *r,
     int is_tls = h2_h2_is_tls(c);
     const char **protos = is_tls? h2_tls_protos : h2_clear_protos;
     
-    (void)s;
     if (!h2_mpm_supported()) {
         return DECLINED;
     }
@@ -68,7 +67,7 @@ static int h2_protocol_propose(conn_rec *c, request_rec *r,
         return DECLINED;
     }
     
-    if (!h2_is_acceptable_connection(c, 0)) {
+    if (!h2_is_acceptable_connection(c, r, 0)) {
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03084)
                       "protocol propose: connection requirements not met");
         return DECLINED;
@@ -81,7 +80,7 @@ static int h2_protocol_propose(conn_rec *c, request_rec *r,
          */
         const char *p;
         
-        if (!h2_allows_h2_upgrade(c)) {
+        if (!h2_allows_h2_upgrade(r)) {
             return DECLINED;
         }
          
@@ -150,7 +149,7 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                       "switching protocol to '%s'", protocol);
         h2_ctx_protocol_set(ctx, protocol);
-        h2_ctx_server_set(ctx, s);
+        h2_ctx_server_update(ctx, s);
         
         if (r != NULL) {
             apr_status_t status;
@@ -164,8 +163,8 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
             ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
             
             /* Ok, start an h2_conn on this one. */
-            h2_ctx_server_set(ctx, r->server);
-            status = h2_conn_setup(ctx, r->connection, r);
+            status = h2_conn_setup(c, r, s);
+            
             if (status != APR_SUCCESS) {
                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(03088)
                               "session setup");
@@ -173,7 +172,7 @@ static int h2_protocol_switch(conn_rec *c, request_rec *r, server_rec *s,
                 return !OK;
             }
             
-            h2_conn_run(ctx, c);
+            h2_conn_run(c);
         }
         return OK;
     }
diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c
index f4c875c..a395807 100644
--- a/modules/http2/h2_task.c
+++ b/modules/http2/h2_task.c
@@ -97,7 +97,7 @@ static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block)
     apr_brigade_length(bb, 0, &written);
     H2_TASK_OUT_LOG(APLOG_TRACE2, task, bb, "h2_task send_out");
     h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "send_out(before)");
-    /* engines send unblocking */
+
     status = h2_beam_send(task->output.beam, bb, 
                           block? APR_BLOCK_READ : APR_NONBLOCK_READ);
     h2_beam_log(task->output.beam, task->c, APLOG_TRACE2, "send_out(after)");
@@ -133,26 +133,9 @@ static apr_status_t slave_out(h2_task *task, ap_filter_t* f,
     apr_status_t rv = APR_SUCCESS;
     int flush = 0, blocking;
     
-    if (task->frozen) {
-        h2_util_bb_log(task->c, task->stream_id, APLOG_TRACE2,
-                       "frozen task output write, ignored", bb);
-        while (!APR_BRIGADE_EMPTY(bb)) {
-            b = APR_BRIGADE_FIRST(bb);
-            if (AP_BUCKET_IS_EOR(b)) {
-                APR_BUCKET_REMOVE(b);
-                task->eor = b;
-            }
-            else {
-                apr_bucket_delete(b);
-            }
-        }
-        return APR_SUCCESS;
-    }
-
 send:
-    /* we send block once we opened the output, so someone is there
-     * reading it *and* the task is not assigned to a h2_req_engine */
-    blocking = (!task->assigned && task->output.opened);
+    /* we send block once we opened the output, so someone is there reading it */
+    blocking = task->output.opened;
     for (b = APR_BRIGADE_FIRST(bb);
          b != APR_BRIGADE_SENTINEL(bb);
          b = APR_BUCKET_NEXT(b)) {
@@ -236,7 +219,7 @@ static apr_status_t h2_filter_slave_in(ap_filter_t* f,
     apr_size_t rmax = ((readbytes <= APR_SIZE_MAX)? 
                        (apr_size_t)readbytes : APR_SIZE_MAX);
     
-    task = h2_ctx_cget_task(f->c);
+    task = h2_ctx_get_task(f->c);
     ap_assert(task);
 
     if (trace1) {
@@ -379,7 +362,7 @@ static apr_status_t h2_filter_slave_in(ap_filter_t* f,
 static apr_status_t h2_filter_slave_output(ap_filter_t* filter,
                                            apr_bucket_brigade* brigade)
 {
-    h2_task *task = h2_ctx_cget_task(filter->c);
+    h2_task *task = h2_ctx_get_task(filter->c);
     apr_status_t status;
     
     ap_assert(task);
@@ -392,7 +375,7 @@ static apr_status_t h2_filter_slave_output(ap_filter_t* filter,
 
 static apr_status_t h2_filter_parse_h1(ap_filter_t* f, apr_bucket_brigade* bb)
 {
-    h2_task *task = h2_ctx_cget_task(f->c);
+    h2_task *task = h2_ctx_get_task(f->c);
     apr_status_t status;
     
     ap_assert(task);
@@ -502,7 +485,7 @@ static int h2_task_pre_conn(conn_rec* c, void *arg)
     
     ctx = h2_ctx_get(c, 0);
     (void)arg;
-    if (h2_ctx_is_task(ctx)) {
+    if (ctx->task) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
                       "h2_slave(%s), pre_connection, adding filters", c->log_id);
         ap_add_input_filter("H2_SLAVE_IN", NULL, NULL, c);
@@ -525,6 +508,7 @@ h2_task *h2_task_create(conn_rec *slave, int stream_id,
     ap_assert(req);
 
     apr_pool_create(&pool, slave->pool);
+    apr_pool_tag(pool, "h2_task");
     task = apr_pcalloc(pool, sizeof(h2_task));
     if (task == NULL) {
         return NULL;
@@ -633,18 +617,9 @@ apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread, int worker_id)
     task->c->current_thread = thread; 
     ap_run_process_connection(c);
     
-    if (task->frozen) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                      "h2_task(%s): process_conn returned frozen task", 
-                      task->id);
-        /* cleanup delayed */
-        return APR_EAGAIN;
-    }
-    else {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                      "h2_task(%s): processing done", task->id);
-        return output_finish(task);
-    }
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                  "h2_task(%s): processing done", task->id);
+    return output_finish(task);
 }
 
 static apr_status_t h2_task_process_request(h2_task *task, conn_rec *c)
@@ -682,14 +657,8 @@ static apr_status_t h2_task_process_request(h2_task *task, conn_rec *c)
         
         ap_process_request(r);
         
-        if (task->frozen) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                          "h2_task(%s): process_request frozen", task->id);
-        }
-        else {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                          "h2_task(%s): process_request done", task->id);
-        }
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "h2_task(%s): process_request done", task->id);
         
         /* After the call to ap_process_request, the
          * request pool may have been deleted.  We set
@@ -724,7 +693,7 @@ static int h2_task_process_conn(conn_rec* c)
     }
     
     ctx = h2_ctx_get(c, 0);
-    if (h2_ctx_is_task(ctx)) {
+    if (ctx->task) {
         if (!ctx->task->request->serialize) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
                           "h2_h2, processing request directly");
@@ -741,28 +710,3 @@ static int h2_task_process_conn(conn_rec* c)
     return DECLINED;
 }
 
-apr_status_t h2_task_freeze(h2_task *task)
-{   
-    if (!task->frozen) {
-        task->frozen = 1;
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, APLOGNO(03406) 
-                      "h2_task(%s), frozen", task->id);
-    }
-    return APR_SUCCESS;
-}
-
-apr_status_t h2_task_thaw(h2_task *task)
-{
-    if (task->frozen) {
-        task->frozen = 0;
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, APLOGNO(03407) 
-                      "h2_task(%s), thawed", task->id);
-    }
-    task->thawed = 1;
-    return APR_SUCCESS;
-}
-
-int h2_task_has_thawed(h2_task *task)
-{
-    return task->thawed;
-}
diff --git a/modules/http2/h2_task.h b/modules/http2/h2_task.h
index ab6a746..20be429 100644
--- a/modules/http2/h2_task.h
+++ b/modules/http2/h2_task.h
@@ -42,7 +42,6 @@ struct h2_bucket_beam;
 struct h2_conn;
 struct h2_mplx;
 struct h2_task;
-struct h2_req_engine;
 struct h2_request;
 struct h2_response_parser;
 struct h2_stream;
@@ -80,17 +79,14 @@ struct h2_task {
     struct h2_mplx *mplx;
     
     unsigned int filters_set    : 1;
-    unsigned int frozen         : 1;
-    unsigned int thawed         : 1;
     unsigned int worker_started : 1; /* h2_worker started processing */
-    unsigned int worker_done    : 1; /* h2_worker finished */
+    
+    int worker_done;                 /* h2_worker finished */
+    int done_done;                   /* task_done has been handled */
     
     apr_time_t started_at;           /* when processing started */
     apr_time_t done_at;              /* when processing was done */
     apr_bucket *eor;
-    
-    struct h2_req_engine *engine;   /* engine hosted by this task */
-    struct h2_req_engine *assigned; /* engine that task has been assigned to */
 };
 
 h2_task *h2_task_create(conn_rec *slave, int stream_id,
@@ -120,8 +116,4 @@ apr_status_t h2_task_init(apr_pool_t *pool, server_rec *s);
 extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in;
 extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out;
 
-apr_status_t h2_task_freeze(h2_task *task);
-apr_status_t h2_task_thaw(h2_task *task);
-int h2_task_has_thawed(h2_task *task);
-
 #endif /* defined(__mod_h2__h2_task__) */
diff --git a/modules/http2/mod_http2.c b/modules/http2/mod_http2.c
index 3d278e9..5664f39 100644
--- a/modules/http2/mod_http2.c
+++ b/modules/http2/mod_http2.c
@@ -172,27 +172,6 @@ static char *http2_var_lookup(apr_pool_t *, server_rec *,
                          conn_rec *, request_rec *, char *name);
 static int http2_is_h2(conn_rec *);
 
-static apr_status_t http2_req_engine_push(const char *ngn_type, 
-                                          request_rec *r, 
-                                          http2_req_engine_init *einit)
-{
-    return h2_mplx_req_engine_push(ngn_type, r, einit);
-}
-
-static apr_status_t http2_req_engine_pull(h2_req_engine *ngn, 
-                                          apr_read_type_e block, 
-                                          int capacity, 
-                                          request_rec **pr)
-{
-    return h2_mplx_req_engine_pull(ngn, block, capacity, pr);
-}
-
-static void http2_req_engine_done(h2_req_engine *ngn, conn_rec *r_conn,
-                                  apr_status_t status)
-{
-    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);
@@ -220,9 +199,6 @@ static void h2_hooks(apr_pool_t *pool)
     
     APR_REGISTER_OPTIONAL_FN(http2_is_h2);
     APR_REGISTER_OPTIONAL_FN(http2_var_lookup);
-    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");
@@ -260,9 +236,8 @@ static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s,
 {
     if (ctx) {
         if (r) {
-            h2_task *task = h2_ctx_get_task(ctx);
-            if (task) {
-                h2_stream *stream = h2_mplx_stream_get(task->mplx, task->stream_id);
+            if (ctx->task) {
+                h2_stream *stream = h2_mplx_stream_get(ctx->task->mplx, ctx->task->stream_id);
                 if (stream && stream->push_policy != H2_PUSH_NONE) {
                     return "on";
                 }
@@ -273,8 +248,7 @@ static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s,
         }
     }
     else if (s) {
-        const h2_config *cfg = h2_config_sget(s);
-        if (cfg && h2_config_geti(cfg, H2_CONF_PUSH)) {
+        if (h2_config_geti(r, s, H2_CONF_PUSH)) {
             return "on";
         }
     }
@@ -285,8 +259,7 @@ static const char *val_H2_PUSHED(apr_pool_t *p, server_rec *s,
                                  conn_rec *c, request_rec *r, h2_ctx *ctx)
 {
     if (ctx) {
-        h2_task *task = h2_ctx_get_task(ctx);
-        if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) {
+        if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) {
             return "PUSHED";
         }
     }
@@ -297,9 +270,8 @@ static const char *val_H2_PUSHED_ON(apr_pool_t *p, server_rec *s,
                                     conn_rec *c, request_rec *r, h2_ctx *ctx)
 {
     if (ctx) {
-        h2_task *task = h2_ctx_get_task(ctx);
-        if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) {
-            h2_stream *stream = h2_mplx_stream_get(task->mplx, task->stream_id);
+        if (ctx->task && !H2_STREAM_CLIENT_INITIATED(ctx->task->stream_id)) {
+            h2_stream *stream = h2_mplx_stream_get(ctx->task->mplx, ctx->task->stream_id);
             if (stream) {
                 return apr_itoa(p, stream->initiated_on);
             }
@@ -312,9 +284,8 @@ static const char *val_H2_STREAM_TAG(apr_pool_t *p, server_rec *s,
                                      conn_rec *c, request_rec *r, h2_ctx *ctx)
 {
     if (ctx) {
-        h2_task *task = h2_ctx_get_task(ctx);
-        if (task) {
-            return task->id;
+        if (ctx->task) {
+            return ctx->task->id;
         }
     }
     return "";
@@ -366,7 +337,7 @@ static char *http2_var_lookup(apr_pool_t *p, server_rec *s,
     for (i = 0; i < H2_ALEN(H2_VARS); ++i) {
         h2_var_def *vdef = &H2_VARS[i];
         if (!strcmp(vdef->name, name)) {
-            h2_ctx *ctx = (r? h2_ctx_rget(r) : 
+            h2_ctx *ctx = (r? h2_ctx_get(c, 0) : 
                            h2_ctx_get(c->master? c->master : c, 0));
             return (char *)vdef->lookup(p, s, c, r, ctx);
         }
@@ -377,7 +348,7 @@ static char *http2_var_lookup(apr_pool_t *p, server_rec *s,
 static int h2_h2_fixups(request_rec *r)
 {
     if (r->connection->master) {
-        h2_ctx *ctx = h2_ctx_rget(r);
+        h2_ctx *ctx = h2_ctx_get(r->connection, 0);
         int i;
         
         for (i = 0; ctx && i < H2_ALEN(H2_VARS); ++i) {
diff --git a/modules/http2/mod_http2.h b/modules/http2/mod_http2.h
index 7a1b49a..ba5e6dd 100644
--- a/modules/http2/mod_http2.h
+++ b/modules/http2/mod_http2.h
@@ -30,22 +30,20 @@ APR_DECLARE_OPTIONAL_FN(int,
 
 
 /*******************************************************************************
- * HTTP/2 request engines
+ * START HTTP/2 request engines (DEPRECATED)
  ******************************************************************************/
+
+/* The following functions were introduced for the experimental mod_proxy_http2
+ * support, but have been abandoned since.
+ * They are still declared here for backward compatibiliy, in case someone
+ * tries to build an old mod_proxy_http2 against it, but will disappear
+ * completely sometime in the future.
+ */ 
  
 struct apr_thread_cond_t;
-
 typedef struct h2_req_engine h2_req_engine;
-
 typedef void http2_output_consumed(void *ctx, conn_rec *c, apr_off_t consumed);
 
-/**
- * Initialize a h2_req_engine. The structure will be passed in but
- * only the name and master are set. The function should initialize
- * all fields.
- * @param engine the allocated, partially filled structure
- * @param r      the first request to process, or NULL
- */
 typedef apr_status_t http2_req_engine_init(h2_req_engine *engine, 
                                            const char *id, 
                                            const char *type,
@@ -55,35 +53,11 @@ typedef apr_status_t http2_req_engine_init(h2_req_engine *engine,
                                            http2_output_consumed **pconsumed,
                                            void **pbaton);
 
-/**
- * Push a request to an engine with the specified name for further processing.
- * If no such engine is available, einit is not NULL, einit is called 
- * with a new engine record and the caller is responsible for running the
- * new engine instance.
- * @param engine_type the type of the engine to add the request to
- * @param r           the request to push to an engine for processing
- * @param einit       an optional initialization callback for a new engine 
- *                    of the requested type, should no instance be available.
- *                    By passing a non-NULL callback, the caller is willing
- *                    to init and run a new engine itself.
- * @return APR_SUCCESS iff slave was successfully added to an engine
- */
 APR_DECLARE_OPTIONAL_FN(apr_status_t, 
                         http2_req_engine_push, (const char *engine_type, 
                                                 request_rec *r,
                                                 http2_req_engine_init *einit));
 
-/**
- * Get a new request for processing in this engine.
- * @param engine      the engine which is done processing the slave
- * @param block       if call should block waiting for request to come
- * @param capacity    how many parallel requests are acceptable
- * @param pr          the request that needs processing or NULL
- * @return APR_SUCCESS if new request was assigned
- *         APR_EAGAIN  if no new request is available
- *         APR_EOF          if engine may shut down, as no more request will be scheduled
- *         APR_ECONNABORTED if the engine needs to shut down immediately
- */
 APR_DECLARE_OPTIONAL_FN(apr_status_t, 
                         http2_req_engine_pull, (h2_req_engine *engine, 
                                                 apr_read_type_e block,
@@ -98,4 +72,8 @@ APR_DECLARE_OPTIONAL_FN(void,
                         http2_get_num_workers, (server_rec *s,
                                                 int *minw, int *max));
 
+/*******************************************************************************
+ * END HTTP/2 request engines (DEPRECATED)
+ ******************************************************************************/
+
 #endif
diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c
index a7e0dcd..95336f7 100644
--- a/modules/http2/mod_proxy_http2.c
+++ b/modules/http2/mod_proxy_http2.c
@@ -16,13 +16,14 @@
  
 #include <nghttp2/nghttp2.h>
 
+#include <ap_mmn.h>
 #include <httpd.h>
 #include <mod_proxy.h>
 #include "mod_http2.h"
 
 
 #include "mod_proxy_http2.h"
-#include "h2_request.h"
+#include "h2.h"
 #include "h2_proxy_util.h"
 #include "h2_version.h"
 #include "h2_proxy_session.h"
@@ -46,19 +47,12 @@ AP_DECLARE_MODULE(proxy_http2) = {
 
 /* Optional functions from mod_http2 */
 static int (*is_h2)(conn_rec *c);
-static apr_status_t (*req_engine_push)(const char *name, request_rec *r, 
-                                       http2_req_engine_init *einit);
-static apr_status_t (*req_engine_pull)(h2_req_engine *engine, 
-                                       apr_read_type_e block, 
-                                       int capacity, 
-                                       request_rec **pr);
-static void (*req_engine_done)(h2_req_engine *engine, conn_rec *r_conn,
-                               apr_status_t status);
-                                       
+
 typedef struct h2_proxy_ctx {
+    const char *id;
+    conn_rec *master;
     conn_rec *owner;
     apr_pool_t *pool;
-    request_rec *rbase;
     server_rec *server;
     const char *proxy_func;
     char server_portstr[32];
@@ -66,19 +60,16 @@ typedef struct h2_proxy_ctx {
     proxy_worker *worker;
     proxy_server_conf *conf;
     
-    h2_req_engine *engine;
-    const char *engine_id;
-    const char *engine_type;
-    apr_pool_t *engine_pool;    
     apr_size_t req_buffer_size;
-    h2_proxy_fifo *requests;
     int capacity;
     
-    unsigned standalone : 1;
     unsigned is_ssl : 1;
     unsigned flushall : 1;
     
-    apr_status_t r_status;     /* status of our first request work */
+    request_rec *r;            /* the request processed in this ctx */
+    apr_status_t r_status;     /* status of request work */
+    int r_done;                /* request was processed, not necessarily successfully */
+    int r_may_retry;           /* request may be retried */
     h2_proxy_session *session; /* current http2 session against backend */
 } h2_proxy_ctx;
 
@@ -104,16 +95,6 @@ static int h2_proxy_post_config(apr_pool_t *p, apr_pool_t *plog,
                  MOD_HTTP2_VERSION, ngh2? ngh2->version_str : "unknown");
     
     is_h2 = APR_RETRIEVE_OPTIONAL_FN(http2_is_h2);
-    req_engine_push = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_push);
-    req_engine_pull = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_pull);
-    req_engine_done = APR_RETRIEVE_OPTIONAL_FN(http2_req_engine_done);
-    
-    /* we need all of them */
-    if (!req_engine_push || !req_engine_pull || !req_engine_done) {
-        req_engine_push = NULL;
-        req_engine_pull = NULL;
-        req_engine_done = NULL;
-    }
     
     return status;
 }
@@ -204,45 +185,6 @@ static int proxy_http2_canon(request_rec *r, char *url)
     return OK;
 }
 
-static void out_consumed(void *baton, conn_rec *c, apr_off_t bytes)
-{
-    h2_proxy_ctx *ctx = baton;
-    
-    if (ctx->session) {
-        h2_proxy_session_update_window(ctx->session, c, bytes);
-    }
-}
-
-static apr_status_t proxy_engine_init(h2_req_engine *engine, 
-                                        const char *id, 
-                                        const char *type,
-                                        apr_pool_t *pool, 
-                                        apr_size_t req_buffer_size,
-                                        request_rec *r,
-                                        http2_output_consumed **pconsumed,
-                                        void **pctx)
-{
-    h2_proxy_ctx *ctx = ap_get_module_config(r->connection->conn_config, 
-                                             &proxy_http2_module);
-    if (!ctx) {
-        ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(03368)
-                      "h2_proxy_session, engine init, no ctx found");
-        return APR_ENOTIMPL;
-    }
-    
-    ctx->pool = pool;
-    ctx->engine = engine;
-    ctx->engine_id = id;
-    ctx->engine_type = type;
-    ctx->engine_pool = pool;
-    ctx->req_buffer_size = req_buffer_size;
-    ctx->capacity = H2MIN(100, h2_proxy_fifo_capacity(ctx->requests));
-    
-    *pconsumed = out_consumed;
-    *pctx = ctx;
-    return APR_SUCCESS;
-}
-
 static apr_status_t add_request(h2_proxy_session *session, request_rec *r)
 {
     h2_proxy_ctx *ctx = session->user_data;
@@ -252,7 +194,7 @@ static apr_status_t add_request(h2_proxy_session *session, request_rec *r)
     url = apr_table_get(r->notes, H2_PROXY_REQ_URL_NOTE);
     apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu",
                    ctx->p_conn->connection->local_addr->port));
-    status = h2_proxy_session_submit(session, url, r, ctx->standalone);
+    status = h2_proxy_session_submit(session, url, r, 1);
     if (status != APR_SUCCESS) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, status, r->connection, APLOGNO(03351)
                       "pass request body failed to %pI (%s) from %s (%s)",
@@ -266,43 +208,15 @@ static apr_status_t add_request(h2_proxy_session *session, request_rec *r)
 static void request_done(h2_proxy_ctx *ctx, request_rec *r,
                          apr_status_t status, int touched)
 {   
-    const char *task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE);
-
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, 
-                  "h2_proxy_session(%s): request done %s, touched=%d",
-                  ctx->engine_id, task_id, touched);
-    if (status != APR_SUCCESS) {
-        if (!touched) {
-            /* untouched request, need rescheduling */
-            status = h2_proxy_fifo_push(ctx->requests, r);
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
-                          APLOGNO(03369)
-                          "h2_proxy_session(%s): rescheduled request %s",
-                          ctx->engine_id, task_id);
-            return;
-        }
-        else {
-            const char *uri;
-            uri = apr_uri_unparse(r->pool, &r->parsed_uri, 0);
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
-                          APLOGNO(03471) "h2_proxy_session(%s): request %s -> %s "
-                          "not complete, cannot repeat", 
-                          ctx->engine_id, task_id, uri);
-        }
-    }
-    
-    if (r == ctx->rbase) {
+    if (r == ctx->r) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, r->connection, 
+                      "h2_proxy_session(%s): request done, touched=%d",
+                      ctx->id, touched);
+        ctx->r_done = 1;
+        if (touched) ctx->r_may_retry = 0;
         ctx->r_status = ((status == APR_SUCCESS)? APR_SUCCESS
                          : HTTP_SERVICE_UNAVAILABLE);
     }
-    
-    if (req_engine_done && ctx->engine) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, r->connection, 
-                      APLOGNO(03370)
-                      "h2_proxy_session(%s): finished request %s",
-                      ctx->engine_id, task_id);
-        req_engine_done(ctx->engine, r->connection, status);
-    }
 }    
 
 static void session_req_done(h2_proxy_session *session, request_rec *r,
@@ -311,43 +225,15 @@ static void session_req_done(h2_proxy_session *session, request_rec *r,
     request_done(session->user_data, r, status, touched);
 }
 
-static apr_status_t next_request(h2_proxy_ctx *ctx, int before_leave)
-{
-    if (h2_proxy_fifo_count(ctx->requests) > 0) {
-        return APR_SUCCESS;
-    }
-    else if (req_engine_pull && ctx->engine) {
-        apr_status_t status;
-        request_rec *r = NULL;
-        
-        status = req_engine_pull(ctx->engine, before_leave? 
-                                 APR_BLOCK_READ: APR_NONBLOCK_READ, 
-                                 ctx->capacity, &r);
-        if (status == APR_SUCCESS && r) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE3, status, ctx->owner, 
-                          "h2_proxy_engine(%s): pulled request (%s) %s", 
-                          ctx->engine_id, 
-                          before_leave? "before leave" : "regular", 
-                          r->the_request);
-            h2_proxy_fifo_push(ctx->requests, r);
-        }
-        return APR_STATUS_IS_EAGAIN(status)? APR_SUCCESS : status;
-    }
-    return APR_EOF;
-}
-
-static apr_status_t proxy_engine_run(h2_proxy_ctx *ctx) {
+static apr_status_t ctx_run(h2_proxy_ctx *ctx) {
     apr_status_t status = OK;
     int h2_front;
-    request_rec *r;
     
     /* Step Four: Send the Request in a new HTTP/2 stream and
      * loop until we got the response or encounter errors.
      */
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, 
-                  "eng(%s): setup session", ctx->engine_id);
     h2_front = is_h2? is_h2(ctx->owner) : 0;
-    ctx->session = h2_proxy_session_setup(ctx->engine_id, ctx->p_conn, ctx->conf,
+    ctx->session = h2_proxy_session_setup(ctx->id, ctx->p_conn, ctx->conf,
                                           h2_front, 30, 
                                           h2_proxy_log2((int)ctx->req_buffer_size), 
                                           session_req_done);
@@ -358,105 +244,45 @@ static apr_status_t proxy_engine_run(h2_proxy_ctx *ctx) {
     }
     
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03373)
-                  "eng(%s): run session %s", ctx->engine_id, ctx->session->id);
+                  "eng(%s): run session %s", ctx->id, ctx->session->id);
     ctx->session->user_data = ctx;
     
-    while (!ctx->owner->aborted) {
-        if (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) {
-            add_request(ctx->session, r);
-        }
-        
+    ctx->r_done = 0;
+    add_request(ctx->session, ctx->r);
+    
+    while (!ctx->master->aborted && !ctx->r_done) {
+    
         status = h2_proxy_session_process(ctx->session);
-        
-        if (status == APR_SUCCESS) {
-            apr_status_t s2;
-            /* ongoing processing, call again */
-            if (ctx->session->remote_max_concurrent > 0
-                && ctx->session->remote_max_concurrent != ctx->capacity) {
-                ctx->capacity = H2MIN((int)ctx->session->remote_max_concurrent, 
-                                      h2_proxy_fifo_capacity(ctx->requests));
-            }
-            s2 = next_request(ctx, 0);
-            if (s2 == APR_ECONNABORTED) {
-                /* master connection gone */
-                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, s2, ctx->owner, 
-                              APLOGNO(03374) "eng(%s): pull request", 
-                              ctx->engine_id);
-                /* give notice that we're leaving and cancel all ongoing
-                 * streams. */
-                next_request(ctx, 1); 
-                h2_proxy_session_cancel_all(ctx->session);
-                h2_proxy_session_process(ctx->session);
-                status = ctx->r_status = APR_SUCCESS;
-                break;
-            }
-            if ((h2_proxy_fifo_count(ctx->requests) == 0) 
-                && h2_proxy_ihash_empty(ctx->session->streams)) {
-                break;
-            }
-        }
-        else {
-            /* end of processing, maybe error */
+        if (status != APR_SUCCESS) {
+            /* Encountered an error during session processing */
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
                           APLOGNO(03375) "eng(%s): end of session %s", 
-                          ctx->engine_id, ctx->session->id);
-            /*
-             * Any open stream of that session needs to
+                          ctx->id, ctx->session->id);
+            /* Any open stream of that session needs to
              * a) be reopened on the new session iff safe to do so
              * b) reported as done (failed) otherwise
              */
             h2_proxy_session_cleanup(ctx->session, session_req_done);
-            break;
+            goto out;
         }
     }
     
-    ctx->session->user_data = NULL;
-    ctx->session = NULL;
-    
-    return status;
-}
-
-static apr_status_t push_request_somewhere(h2_proxy_ctx *ctx, request_rec *r)
-{
-    conn_rec *c = ctx->owner;
-    const char *engine_type, *hostname;
-    
-    hostname = (ctx->p_conn->ssl_hostname? 
-                ctx->p_conn->ssl_hostname : ctx->p_conn->hostname);
-    engine_type = apr_psprintf(ctx->pool, "proxy_http2 %s%s", hostname, 
-                               ctx->server_portstr);
-    
-    if (c->master && req_engine_push && r && is_h2 && is_h2(c)) {
-        /* If we are have req_engine capabilities, push the handling of this
-         * request (e.g. slave connection) to a proxy_http2 engine which 
-         * uses the same backend. We may be called to create an engine 
-         * ourself. */
-        if (req_engine_push(engine_type, r, proxy_engine_init) == APR_SUCCESS) {
-            if (ctx->engine == NULL) {
-                /* request has been assigned to an engine in another thread */
-                return SUSPENDED;
-            }
+out:
+    if (ctx->master->aborted) {
+        /* master connection gone */
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
+                      APLOGNO(03374) "eng(%s): master connection gone", ctx->id);
+        /* cancel all ongoing requests */
+        h2_proxy_session_cancel_all(ctx->session);
+        h2_proxy_session_process(ctx->session);
+        if (!ctx->master->aborted) {
+            status = ctx->r_status = APR_SUCCESS;
         }
     }
     
-    if (!ctx->engine) {
-        /* No engine was available or has been initialized, handle this
-         * request just by ourself. */
-        ctx->engine_id = apr_psprintf(ctx->pool, "eng-proxy-%ld", c->id);
-        ctx->engine_type = engine_type;
-        ctx->engine_pool = ctx->pool;
-        ctx->req_buffer_size = (32*1024);
-        ctx->standalone = 1;
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
-                      "h2_proxy_http2(%ld): setup standalone engine for type %s", 
-                      c->id, engine_type);
-    }
-    else {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, 
-                      "H2: hosting engine %s", ctx->engine_id);
-    }
-
-    return h2_proxy_fifo_push(ctx->requests, r);
+    ctx->session->user_data = NULL;
+    ctx->session = NULL;
+    return status;
 }
 
 static int proxy_http2_handler(request_rec *r, 
@@ -466,7 +292,7 @@ static int proxy_http2_handler(request_rec *r,
                                const char *proxyname,
                                apr_port_t proxyport)
 {
-    const char *proxy_func;
+    const char *proxy_func, *task_id;
     char *locurl = url, *u;
     apr_size_t slen;
     int is_ssl = 0;
@@ -498,29 +324,36 @@ static int proxy_http2_handler(request_rec *r,
         default:
             return DECLINED;
     }
+
+    task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE);
     
     ctx = apr_pcalloc(r->pool, sizeof(*ctx));
-    ctx->owner      = r->connection;
-    ctx->pool       = r->pool;
-    ctx->rbase      = r;
-    ctx->server     = r->server;
+    ctx->master = r->connection->master? r->connection->master : r->connection;
+    ctx->id = task_id? task_id : apr_psprintf(r->pool, "%ld", (long)ctx->master->id);
+    ctx->owner = r->connection;
+    ctx->pool = r->pool;
+    ctx->server = r->server;
     ctx->proxy_func = proxy_func;
-    ctx->is_ssl     = is_ssl;
-    ctx->worker     = worker;
-    ctx->conf       = conf;
-    ctx->flushall   = apr_table_get(r->subprocess_env, "proxy-flushall")? 1 : 0;
-    ctx->r_status   = HTTP_SERVICE_UNAVAILABLE;
-    
-    h2_proxy_fifo_set_create(&ctx->requests, ctx->pool, 100);
+    ctx->is_ssl = is_ssl;
+    ctx->worker = worker;
+    ctx->conf = conf;
+    ctx->flushall = apr_table_get(r->subprocess_env, "proxy-flushall")? 1 : 0;
+    ctx->req_buffer_size = (32*1024);
+    ctx->r = r;
+    ctx->r_status = status = HTTP_SERVICE_UNAVAILABLE;
+    ctx->r_done = 0;
+    ctx->r_may_retry =  1;
     
     ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, ctx);
 
     /* scheme says, this is for us. */
-    apr_table_setn(ctx->rbase->notes, H2_PROXY_REQ_URL_NOTE, url);
-    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->rbase, 
+    apr_table_setn(ctx->r->notes, H2_PROXY_REQ_URL_NOTE, url);
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->r, 
                   "H2: serving URL %s", url);
     
 run_connect:    
+    if (ctx->master->aborted) goto cleanup;
+
     /* Get a proxy_conn_rec from the worker, might be a new one, might
      * be one still open from another request, or it might fail if the
      * worker is stopped or in error. */
@@ -530,25 +363,11 @@ run_connect:
     }
 
     ctx->p_conn->is_ssl = ctx->is_ssl;
-    if (ctx->is_ssl && ctx->p_conn->connection) {
-        /* If there are some metadata on the connection (e.g. TLS alert),
-         * let mod_ssl detect them, and create a new connection below.
-         */ 
-        apr_bucket_brigade *tmp_bb;
-        tmp_bb = apr_brigade_create(ctx->rbase->pool, 
-                                    ctx->rbase->connection->bucket_alloc);
-        status = ap_get_brigade(ctx->p_conn->connection->input_filters, tmp_bb,
-                                AP_MODE_SPECULATIVE, APR_NONBLOCK_READ, 1);
-        if (status != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(status)) {
-            ctx->p_conn->close = 1;
-        }
-        apr_brigade_cleanup(tmp_bb);
-    }   
 
     /* Step One: Determine the URL to connect to (might be a proxy),
      * initialize the backend accordingly and determine the server 
      * port string we can expect in responses. */
-    if ((status = ap_proxy_determine_connection(ctx->pool, ctx->rbase, conf, worker, 
+    if ((status = ap_proxy_determine_connection(ctx->pool, ctx->r, conf, worker, 
                                                 ctx->p_conn, &uri, &locurl, 
                                                 proxyname, proxyport, 
                                                 ctx->server_portstr,
@@ -556,17 +375,6 @@ run_connect:
         goto cleanup;
     }
     
-    /* If we are not already hosting an engine, try to push the request 
-     * to an already existing engine or host a new engine here. */
-    if (r && !ctx->engine) {
-        ctx->r_status = push_request_somewhere(ctx, r);
-        r = NULL;
-        if (ctx->r_status == SUSPENDED) {
-            /* request was pushed to another thread, leave processing here */
-            goto cleanup;
-        }
-    }
-    
     /* Step Two: Make the Connection (or check that an already existing
      * socket is still usable). On success, we have a socket connected to
      * backend->hostname. */
@@ -575,70 +383,58 @@ run_connect:
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03352)
                       "H2: failed to make connection to backend: %s",
                       ctx->p_conn->hostname);
-        goto reconnect;
+        goto cleanup;
     }
     
     /* Step Three: Create conn_rec for the socket we have open now. */
     if (!ctx->p_conn->connection) {
-        status = ap_proxy_connection_create_ex(ctx->proxy_func,
-                                               ctx->p_conn, ctx->rbase);
+        status = ap_proxy_connection_create_ex(ctx->proxy_func, ctx->p_conn, ctx->r);
         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, 
                           locurl, ctx->p_conn->hostname);
-            goto reconnect;
+            ctx->r_status = status;
+            goto cleanup;
         }
         
-        if (!ctx->p_conn->data) {
-            /* New conection: set a note on the connection what CN is
-             * requested and what protocol we want */
+        if (!ctx->p_conn->data && ctx->is_ssl) {
+            /* New SSL connection: set a note on the connection about what
+             * protocol we want.
+             */
+            apr_table_setn(ctx->p_conn->connection->notes,
+                           "proxy-request-alpn-protos", "h2");
             if (ctx->p_conn->ssl_hostname) {
-                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, ctx->owner, 
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, ctx->owner, 
                               "set SNI to %s for (%s)", 
                               ctx->p_conn->ssl_hostname, 
                               ctx->p_conn->hostname);
                 apr_table_setn(ctx->p_conn->connection->notes,
                                "proxy-request-hostname", ctx->p_conn->ssl_hostname);
             }
-            if (ctx->is_ssl) {
-                apr_table_setn(ctx->p_conn->connection->notes,
-                               "proxy-request-alpn-protos", "h2");
-            }
         }
     }
 
-run_session:
-    status = proxy_engine_run(ctx);
-    if (status == APR_SUCCESS) {
-        /* session and connection still ok */
-        if (next_request(ctx, 1) == APR_SUCCESS) {
-            /* more requests, run again */
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(03376)
-                          "run_session, again");
-            goto run_session;
-        }
-        /* done */
-        ctx->engine = NULL;
-    }
+    if (ctx->master->aborted) goto cleanup;
+    status = ctx_run(ctx);
 
-reconnect:
-    if (next_request(ctx, 1) == APR_SUCCESS) {
-        /* Still more to do, tear down old conn and start over */
+    if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->master->aborted) {
+        /* Not successfully processed, but may retry, tear down old conn and start over */
         if (ctx->p_conn) {
             ctx->p_conn->close = 1;
-            /*only in trunk so far */
-            /*proxy_run_detach_backend(r, ctx->p_conn);*/
+#if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
+            proxy_run_detach_backend(r, ctx->p_conn);
+#endif
             ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
             ctx->p_conn = NULL;
         }
         ++reconnects;
-        if (reconnects < 5 && !ctx->owner->aborted) {
+        if (reconnects < 5) {
             goto run_connect;
         } 
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, ctx->owner, APLOGNO(10023)
-                      "giving up after %d reconnects, %d requests todo",
-                      reconnects, h2_proxy_fifo_count(ctx->requests));
+                      "giving up after %d reconnects, request-done=%d",
+                      reconnects, ctx->r_done);
     }
     
 cleanup:
@@ -647,17 +443,13 @@ cleanup:
             /* close socket when errors happened or session shut down (EOF) */
             ctx->p_conn->close = 1;
         }
-        /*only in trunk so far */
-        /*proxy_run_detach_backend(ctx->rbase, ctx->p_conn);*/
+#if AP_MODULE_MAGIC_AT_LEAST(20140207, 2)
+        proxy_run_detach_backend(ctx->r, ctx->p_conn);
+#endif
         ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
         ctx->p_conn = NULL;
     }
 
-    /* Any requests will still have need to fail */
-    while (APR_SUCCESS == h2_proxy_fifo_try_pull(ctx->requests, (void**)&r)) {
-        request_done(ctx, r, HTTP_SERVICE_UNAVAILABLE, 1);
-    }
-    
     ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL);
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, 
                   APLOGNO(03377) "leaving handler");
-- 
1.8.3.1

