You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2019/03/07 10:35:57 UTC

svn commit: r1854965 [2/2] - /httpd/httpd/patches/2.4.x/h2-mega-v1.14.1-backport-bugs-and-whistles.patch

Added: httpd/httpd/patches/2.4.x/h2-mega-v1.14.1-backport-bugs-and-whistles.patch
URL: http://svn.apache.org/viewvc/httpd/httpd/patches/2.4.x/h2-mega-v1.14.1-backport-bugs-and-whistles.patch?rev=1854965&view=auto
==============================================================================
--- httpd/httpd/patches/2.4.x/h2-mega-v1.14.1-backport-bugs-and-whistles.patch (added)
+++ httpd/httpd/patches/2.4.x/h2-mega-v1.14.1-backport-bugs-and-whistles.patch Thu Mar  7 10:35:57 2019
@@ -0,0 +1,4581 @@
+Index: CHANGES
+===================================================================
+--- CHANGES	(revision 1854963)
++++ CHANGES	(working copy)
+@@ -1,6 +1,44 @@
+                                                          -*- coding: utf-8 -*-
+ Changes with Apache 2.4.39
+ 
++  *) 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 directoves H2Push and H2Upgrade can now be specified per 
++     Location/Directory, e.g. disabling PUSH for a specific set of resources. [Stefan Eissing]
++
++  *) http: Fix possible empty response with mod_ratelimit for HEAD requests.
++     PR 63192. [Yann Ylavic]
++
++  *) 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]
++
++  *) mod_http2: enable re-use of slave connections again. Fixed slave connection
++     keepalives counter. [Stefan Eissing]
++
+   *) mod_proxy_wstunnel: Fix websocket proxy over UDS.
+      PR 62932 <pavel dcmsys.com>
+ 
+Index: docs/manual/mod/mod_http2.xml
+===================================================================
+--- docs/manual/mod/mod_http2.xml	(revision 1854963)
++++ docs/manual/mod/mod_http2.xml	(working copy)
+@@ -244,6 +244,8 @@
+         <contextlist>
+             <context>server config</context>
+             <context>virtual host</context>
++            <context>directory</context>
++            <context>.htaccess</context>
+         </contextlist>
+         <compatibility>Available in version 2.4.18 and later.</compatibility>
+         
+@@ -269,7 +271,8 @@
+             </p>
+             <p> 
+                 Link headers in responses are either set by the application or
+-                can be configured via <module>mod_headers</module> as:
++                can be configured via <directive>H2PushResource</directive> or
++                using <module>mod_headers</module> as:
+             </p>
+             <example><title>mod_headers example</title>
+                 <highlight language="config">
+@@ -286,9 +289,10 @@
+                 twice or more to one client. Use with care.
+             </p>
+             <p> 
+-                HTTP/2 server pushes are enabled by default. This directive 
+-                allows it to be switch off on all resources of this server/virtual
+-                host.
++                HTTP/2 server pushes are enabled by default. On a server or virtual host,
++                you may enable/disable this feature for any connection to the host. In addition,
++                you may disable PUSH for a set of resources in a Directory/Location. This controls
++                which resources may cause a PUSH, not which resources may be sent via PUSH.
+             </p>
+             <example><title>Example</title>
+                 <highlight language="config">
+@@ -480,6 +484,8 @@
+         <contextlist>
+             <context>server config</context>
+             <context>virtual host</context>
++            <context>directory</context>
++            <context>.htaccess</context>
+         </contextlist>
+         
+         <usage>
+@@ -938,4 +944,41 @@
+         </usage>
+     </directivesynopsis>
+     
++    <directivesynopsis>
++        <name>H2Padding</name>
++        <description>Determine the range of padding bytes added to payload frames</description>
++        <syntax>H2Padding numbits</syntax>
++        <default>H2Padding 0</default>
++        <contextlist>
++            <context>server config</context>
++            <context>virtual host</context>
++        </contextlist>
++        <compatibility>Available in version 2.4.39 and later.</compatibility>
++        
++        <usage>
++            <p>
++                With the default 0, no padding bytes are added to any payload
++                frames, e.g. HEADERS, DATA and PUSH_PROMISE. This is the behaviour
++                of previous versions. It means that under certain conditions, an
++                observer of network traffic can see the length of those frames 
++                in the TLS stream.
++            </p>
++            <p>
++                When configuring numbits of 1-8, a random number in range
++                [0, 2^numbits[ are added to each frame. The random value is chosen
++                independantly for each frame that the module sends back to the client.
++            </p>
++            <p>
++                While more padding bytes give better message length obfuscation, they
++                are also additional traffic. The optimal number therefore depends on
++                the kind of web traffic the server carries.
++            </p>
++            <p>
++                The default of 0, e.g. no padding, was chosen for maximum backward
++                compatibility. There might be deployments where padding bytes are
++                unwanted or do harm. The most likely cause would be a client that
++                has a faults implementation.
++            </p>
++        </usage>
++    </directivesynopsis>
+ </modulesynopsis>
+Index: modules/http2/config2.m4
+===================================================================
+--- modules/http2/config2.m4	(revision 1854963)
++++ modules/http2/config2.m4	(working copy)
+@@ -31,7 +31,6 @@
+ 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
+Index: modules/http2/h2.h
+===================================================================
+--- modules/http2/h2.h	(revision 1854963)
++++ modules/http2/h2.h	(working copy)
+@@ -48,12 +48,12 @@
+ #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 @@
+ #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__) */
+Index: modules/http2/h2_alt_svc.c
+===================================================================
+--- modules/http2/h2_alt_svc.c	(revision 1854963)
++++ modules/http2/h2_alt_svc.c	(working copy)
+@@ -75,7 +75,7 @@
+ 
+ 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 @@
+         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 @@
+             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 @@
+                           "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;
+Index: modules/http2/h2_config.c
+===================================================================
+--- modules/http2/h2_config.c	(revision 1854963)
++++ modules/http2/h2_config.c	(working copy)
+@@ -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,19 +113,29 @@
+     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)
+ {
+     (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 @@
+     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 @@
+         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 @@
+         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,13 +283,94 @@
+             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);
+     ap_assert(cfg);
+@@ -217,9 +377,162 @@
+     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 @@
+     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;
+         }
+-        APR_ARRAY_PUSH(cfg->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;
++        }
+     }
+-    (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 @@
+     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 @@
+                                         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 @@
+         }
+     }
+ 
+-    /* 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 @@
+     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 @@
+     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 @@
+     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);
+-}
+Index: modules/http2/h2_config.h
+===================================================================
+--- modules/http2/h2_config.h	(revision 1854963)
++++ modules/http2/h2_config.h	(working copy)
+@@ -42,6 +42,8 @@
+     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,34 +55,7 @@
+     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);
+ void *h2_config_create_svr(apr_pool_t *pool, server_rec *s);
+@@ -88,19 +63,37 @@
+ 
+ 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);
+ 
++/** 
++ * 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__ */
+ 
+Index: modules/http2/h2_conn.c
+===================================================================
+--- modules/http2/h2_conn.c	(revision 1854963)
++++ modules/http2/h2_conn.c	(working copy)
+@@ -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 @@
+ 
+ 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 @@
+     
+     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 @@
+     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 @@
+         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_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;
+@@ -305,9 +301,15 @@
+     c->notes                  = apr_table_make(pool, 5);
+     c->input_filters          = NULL;
+     c->output_filters         = NULL;
++    c->keepalives             = 0;
++#if AP_MODULE_MAGIC_AT_LEAST(20180903, 1)
++    c->filter_conn_ctx        = NULL;
++#endif
+     c->bucket_alloc           = apr_bucket_alloc_create(pool);
++#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;
+@@ -332,16 +334,15 @@
+         ap_set_module_config(c->conn_config, mpm, cfg);
+     }
+ 
+-    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+-                  "h2_stream(%ld-%d): created slave", master->id, slave_id);
++    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, c, 
++                  "h2_slave(%s): created", c->log_id);
+     return c;
+ }
+ 
+ void h2_slave_destroy(conn_rec *slave)
+ {
+-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
+-                  "h2_stream(%s): destroy slave", 
+-                  apr_table_get(slave->notes, H2_TASK_ID_NOTE));
++    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, slave,
++                  "h2_slave(%s): destroy", slave->log_id);
+     slave->sbh = NULL;
+     apr_pool_destroy(slave->pool);
+ }
+@@ -365,6 +366,7 @@
+         slave->keepalive = AP_CONN_CLOSE;
+         return ap_run_pre_connection(slave, csd);
+     }
++    ap_assert(slave->output_filters);
+     return APR_SUCCESS;
+ }
+ 
+Index: modules/http2/h2_conn.h
+===================================================================
+--- modules/http2/h2_conn.h	(revision 1854963)
++++ modules/http2/h2_conn.h	(working copy)
+@@ -23,11 +23,11 @@
+ /**
+  * 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. 
+@@ -34,10 +34,10 @@
+  * 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
+Index: modules/http2/h2_conn_io.c
+===================================================================
+--- modules/http2/h2_conn_io.c	(revision 1854963)
++++ modules/http2/h2_conn_io.c	(working copy)
+@@ -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 @@
+ 
+ }
+ 
+-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); 
+Index: modules/http2/h2_conn_io.h
+===================================================================
+--- modules/http2/h2_conn_io.h	(revision 1854963)
++++ modules/http2/h2_conn_io.h	(working copy)
+@@ -48,8 +48,7 @@
+     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.
+Index: modules/http2/h2_ctx.c
+===================================================================
+--- modules/http2/h2_ctx.c	(revision 1854963)
++++ modules/http2/h2_ctx.c	(working copy)
+@@ -29,8 +29,8 @@
+ {
+     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 @@
+     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 @@
+     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)
++h2_task *h2_ctx_get_task(conn_rec *c)
+ {
+-    return ctx && ctx->task;
+-}
+-
+-h2_task *h2_ctx_get_task(h2_ctx *ctx)
+-{
++    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));
+-}
+Index: modules/http2/h2_ctx.h
+===================================================================
+--- modules/http2/h2_ctx.h	(revision 1854963)
++++ modules/http2/h2_ctx.h	(working copy)
+@@ -56,12 +56,11 @@
+  */
+ 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 @@
+  */
+ 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__) */
+Index: modules/http2/h2_filter.c
+===================================================================
+--- modules/http2/h2_filter.c	(revision 1854963)
++++ modules/http2/h2_filter.c	(working copy)
+@@ -54,6 +54,7 @@
+     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 @@
+         }
+         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_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 @@
+     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 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 @@
+ 
+ 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 @@
+         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) {
+Index: modules/http2/h2_from_h1.c
+===================================================================
+--- modules/http2/h2_from_h1.c	(revision 1854963)
++++ modules/http2/h2_from_h1.c	(working copy)
+@@ -594,9 +594,9 @@
+         }
+     }
+     
+-    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)) {
+@@ -603,9 +603,11 @@
+             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;
+         }
+     }
+Index: modules/http2/h2_h2.c
+===================================================================
+--- modules/http2/h2_h2.c	(revision 1854963)
++++ modules/http2/h2_h2.c	(working copy)
+@@ -463,12 +463,11 @@
+     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
+          */
+@@ -475,7 +474,7 @@
+         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 @@
+     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,6 +576,7 @@
+ {
+     apr_status_t status;
+     h2_ctx *ctx;
++    server_rec *s;
+     
+     if (c->master) {
+         return DECLINED;
+@@ -587,8 +583,10 @@
+     }
+     
+     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 @@
+             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 @@
+                 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 @@
+             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,8 +645,9 @@
+ 
+     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);
+@@ -655,7 +654,7 @@
+                 return !OK;
+             }
+         }
+-        h2_conn_run(ctx, c);
++        h2_conn_run(c);
+         return OK;
+     }
+     
+@@ -684,16 +683,17 @@
+ 
+ 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 @@
+ {
+     /* 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 @@
+ {
+     /* 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);
+Index: modules/http2/h2_h2.h
+===================================================================
+--- modules/http2/h2_h2.h	(revision 1854963)
++++ modules/http2/h2_h2.h	(working copy)
+@@ -57,23 +57,15 @@
+  *    the handshake is still ongoing.
+  * @return != 0 iff connection requirements are met
+  */
+-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);
+ 
+ /**
+- * 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);
+-
+-/**
+  * 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__) */
+Index: modules/http2/h2_headers.c
+===================================================================
+--- modules/http2/h2_headers.c	(revision 1854963)
++++ modules/http2/h2_headers.c	(working copy)
+@@ -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"
+@@ -141,9 +142,11 @@
+         }
+     }
+     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;
+ }
+ 
+Index: modules/http2/h2_mplx.c
+===================================================================
+--- modules/http2/h2_mplx.c	(revision 1854963)
++++ modules/http2/h2_mplx.c	(working copy)
+@@ -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 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 @@
+     }
+     else if (stream->task) {
+         stream->task->c->aborted = 1;
+-        apr_thread_cond_broadcast(m->task_thawed);
+     }
+ }
+ 
+@@ -151,8 +143,7 @@
+  *   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;
+@@ -159,17 +150,12 @@
+     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 @@
+             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 @@
+         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;
+ }
+@@ -327,7 +303,7 @@
+                                && !task->rst_error);
+             }
+             
+-            if (reuse_slave && slave->keepalive == AP_CONN_KEEPALIVE) {
++            if (reuse_slave) {
+                 h2_beam_log(task->output.beam, m->c, APLOG_DEBUG, 
+                             APLOGNO(03385) "h2_task_destroy, reuse slave");    
+                 h2_task_destroy(task);
+@@ -393,10 +369,10 @@
+     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 */
+@@ -435,8 +411,10 @@
+ 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);
+     /* How to shut down a h2 connection:
+      * 0. abort and tell the workers that no more tasks will come from us */
+     m->aborted = 1;
+@@ -444,6 +422,12 @@
+     
+     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)) {
+@@ -450,9 +434,7 @@
+         /* 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));
+     
+@@ -476,10 +458,6 @@
+     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)
+@@ -488,6 +466,7 @@
+         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,
+@@ -784,47 +763,14 @@
+     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,
+@@ -846,18 +792,24 @@
+                           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) {
+@@ -870,6 +822,7 @@
+     }
+     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) {
+@@ -894,7 +847,7 @@
+ {
+     H2_MPLX_ENTER_ALWAYS(m);
+ 
+-    task_done(m, task, NULL);
++    task_done(m, task);
+     --m->tasks_active;
+     
+     if (m->join_wait) {
+@@ -977,6 +930,9 @@
+      */
+     n = (m->tasks_active - m->limit_active - (int)h2_ihash_count(m->sredo));
+     while (n > 0 && (stream = get_latest_repeatable_unsubmitted_stream(m))) {
++        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, 
++                      "h2_mplx(%s): unschedule, resetting task for redo later",
++                      stream->task->id);
+         h2_task_rst(stream->task, H2_ERR_CANCEL);
+         h2_ihash_add(m->sredo, stream);
+         --n;
+@@ -1085,142 +1041,6 @@
+ }
+ 
+ /*******************************************************************************
+- * 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);
+-        }
+-

[... 2193 lines stripped ...]