You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by jo...@apache.org on 2016/11/14 10:26:34 UTC

svn commit: r1769588 [11/17] - in /httpd/httpd/branches/2.4.x-openssl-1.1.0-compat: ./ docs/conf/ docs/manual/ docs/manual/howto/ docs/manual/mod/ docs/manual/platform/ docs/manual/programs/ docs/manual/rewrite/ include/ modules/ modules/aaa/ modules/a...

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.c Mon Nov 14 10:26:31 2016
@@ -21,6 +21,7 @@
 #include <apr_thread_cond.h>
 
 #include <httpd.h>
+#include <http_protocol.h>
 #include <http_log.h>
 
 #include "h2_private.h"
@@ -138,25 +139,6 @@ static apr_bucket *h2_beam_bucket_create
     return h2_beam_bucket_make(b, beam, bred, n);
 }
 
-/*static apr_status_t beam_bucket_setaside(apr_bucket *b, apr_pool_t *pool)
-{
-    apr_status_t status = APR_SUCCESS;
-    h2_beam_proxy *d = b->data;
-    if (d->bred) {
-        const char *data;
-        apr_size_t len;
-        
-        status = apr_bucket_read(d->bred, &data, &len, APR_BLOCK_READ);
-        if (status == APR_SUCCESS) {
-            b = apr_bucket_heap_make(b, (char *)data + b->start, b->length, NULL);
-            if (b == NULL) {
-                return APR_ENOMEM;
-            }
-        }
-    }
-    return status;
-}*/
-
 const apr_bucket_type_t h2_bucket_type_beam = {
     "BEAM", 5, APR_BUCKET_DATA,
     beam_bucket_destroy,
@@ -169,7 +151,36 @@ const apr_bucket_type_t h2_bucket_type_b
 /*******************************************************************************
  * h2_blist, a brigade without allocations
  ******************************************************************************/
+
+static apr_array_header_t *beamers;
  
+void h2_register_bucket_beamer(h2_bucket_beamer *beamer)
+{
+    if (!beamers) {
+        beamers = apr_array_make(apr_hook_global_pool, 10, 
+                                 sizeof(h2_bucket_beamer*));
+    }
+    APR_ARRAY_PUSH(beamers, h2_bucket_beamer*) = beamer;
+}
+
+static apr_bucket *h2_beam_bucket(h2_bucket_beam *beam, 
+                                  apr_bucket_brigade *dest,
+                                  const apr_bucket *src)
+{
+    apr_bucket *b = NULL;
+    int i;
+    if (beamers) {
+        for (i = 0; i < beamers->nelts && b == NULL; ++i) {
+            h2_bucket_beamer *beamer;
+            
+            beamer = APR_ARRAY_IDX(beamers, i, h2_bucket_beamer*);
+            b = beamer(beam, dest, src);
+        }
+    }
+    return b;
+}
+
+
 apr_size_t h2_util_bl_print(char *buffer, apr_size_t bmax, 
                             const char *tag, const char *sep, 
                             h2_blist *bl)
@@ -223,12 +234,34 @@ static void leave_yellow(h2_bucket_beam
     }
 }
 
-static apr_off_t calc_buffered(h2_bucket_beam *beam)
+static void report_consumption(h2_bucket_beam *beam, int force)
 {
-    apr_off_t len = 0;
+    if (force || beam->received_bytes != beam->reported_consumed_bytes) {
+        if (beam->consumed_fn) { 
+            beam->consumed_fn(beam->consumed_ctx, beam, beam->received_bytes
+                              - beam->reported_consumed_bytes);
+        }
+        beam->reported_consumed_bytes = beam->received_bytes;
+    }
+}
+
+static void report_production(h2_bucket_beam *beam, int force)
+{
+    if (force || beam->sent_bytes != beam->reported_produced_bytes) {
+        if (beam->produced_fn) { 
+            beam->produced_fn(beam->produced_ctx, beam, beam->sent_bytes
+                              - beam->reported_produced_bytes);
+        }
+        beam->reported_produced_bytes = beam->sent_bytes;
+    }
+}
+
+static apr_size_t calc_buffered(h2_bucket_beam *beam)
+{
+    apr_size_t len = 0;
     apr_bucket *b;
-    for (b = H2_BLIST_FIRST(&beam->red); 
-         b != H2_BLIST_SENTINEL(&beam->red);
+    for (b = H2_BLIST_FIRST(&beam->send_list); 
+         b != H2_BLIST_SENTINEL(&beam->send_list);
          b = APR_BUCKET_NEXT(b)) {
         if (b->length == ((apr_size_t)-1)) {
             /* do not count */
@@ -243,14 +276,14 @@ static apr_off_t calc_buffered(h2_bucket
     return len;
 }
 
-static void r_purge_reds(h2_bucket_beam *beam)
+static void r_purge_sent(h2_bucket_beam *beam)
 {
-    apr_bucket *bred;
-    /* delete all red buckets in purge brigade, needs to be called
-     * from red thread only */
-    while (!H2_BLIST_EMPTY(&beam->purge)) {
-        bred = H2_BLIST_FIRST(&beam->purge);
-        apr_bucket_delete(bred);
+    apr_bucket *b;
+    /* delete all sender buckets in purge brigade, needs to be called
+     * from sender thread only */
+    while (!H2_BLIST_EMPTY(&beam->purge_list)) {
+        b = H2_BLIST_FIRST(&beam->purge_list);
+        apr_bucket_delete(b);
     }
 }
 
@@ -274,16 +307,18 @@ static apr_status_t wait_cond(h2_bucket_
 }
 
 static apr_status_t r_wait_space(h2_bucket_beam *beam, apr_read_type_e block,
-                                 h2_beam_lock *pbl, apr_off_t *premain) 
+                                 h2_beam_lock *pbl, apr_size_t *premain) 
 {
     *premain = calc_space_left(beam);
     while (!beam->aborted && *premain <= 0 
            && (block == APR_BLOCK_READ) && pbl->mutex) {
-        apr_status_t status = wait_cond(beam, pbl->mutex);
+        apr_status_t status;
+        report_production(beam, 1);
+        status = wait_cond(beam, pbl->mutex);
         if (APR_STATUS_IS_TIMEUP(status)) {
             return status;
         }
-        r_purge_reds(beam);
+        r_purge_sent(beam);
         *premain = calc_space_left(beam);
     }
     return beam->aborted? APR_ECONNABORTED : APR_SUCCESS;
@@ -298,34 +333,34 @@ static void h2_beam_emitted(h2_bucket_be
         /* even when beam buckets are split, only the one where
          * refcount drops to 0 will call us */
         H2_BPROXY_REMOVE(proxy);
-        /* invoked from green thread, the last beam bucket for the red
-         * bucket bred is about to be destroyed.
+        /* invoked from receiver thread, the last beam bucket for the send
+         * bucket is about to be destroyed.
          * remove it from the hold, where it should be now */
         if (proxy->bred) {
-            for (b = H2_BLIST_FIRST(&beam->hold); 
-                 b != H2_BLIST_SENTINEL(&beam->hold);
+            for (b = H2_BLIST_FIRST(&beam->hold_list); 
+                 b != H2_BLIST_SENTINEL(&beam->hold_list);
                  b = APR_BUCKET_NEXT(b)) {
                  if (b == proxy->bred) {
                     break;
                  }
             }
-            if (b != H2_BLIST_SENTINEL(&beam->hold)) {
+            if (b != H2_BLIST_SENTINEL(&beam->hold_list)) {
                 /* bucket is in hold as it should be, mark this one
                  * and all before it for purging. We might have placed meta
                  * buckets without a green proxy into the hold before it 
                  * and schedule them for purging now */
-                for (b = H2_BLIST_FIRST(&beam->hold); 
-                     b != H2_BLIST_SENTINEL(&beam->hold);
+                for (b = H2_BLIST_FIRST(&beam->hold_list); 
+                     b != H2_BLIST_SENTINEL(&beam->hold_list);
                      b = next) {
                     next = APR_BUCKET_NEXT(b);
                     if (b == proxy->bred) {
                         APR_BUCKET_REMOVE(b);
-                        H2_BLIST_INSERT_TAIL(&beam->purge, b);
+                        H2_BLIST_INSERT_TAIL(&beam->purge_list, b);
                         break;
                     }
                     else if (APR_BUCKET_IS_METADATA(b)) {
                         APR_BUCKET_REMOVE(b);
-                        H2_BLIST_INSERT_TAIL(&beam->purge, b);
+                        H2_BLIST_INSERT_TAIL(&beam->purge_list, b);
                     }
                     else {
                         /* another data bucket before this one in hold. this
@@ -338,16 +373,16 @@ static void h2_beam_emitted(h2_bucket_be
             }
             else {
                 /* it should be there unless we screwed up */
-                ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, beam->red_pool, 
+                ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, beam->send_pool, 
                               APLOGNO(03384) "h2_beam(%d-%s): emitted bucket not "
                               "in hold, n=%d", beam->id, beam->tag, 
                               (int)proxy->n);
-                AP_DEBUG_ASSERT(!proxy->bred);
+                ap_assert(!proxy->bred);
             }
         }
         /* notify anyone waiting on space to become available */
         if (!bl.mutex) {
-            r_purge_reds(beam);
+            r_purge_sent(beam);
         }
         else if (beam->m_cond) {
             apr_thread_cond_broadcast(beam->m_cond);
@@ -356,28 +391,6 @@ static void h2_beam_emitted(h2_bucket_be
     }
 }
 
-static void report_consumption(h2_bucket_beam *beam, int force)
-{
-    if (force || beam->received_bytes != beam->reported_consumed_bytes) {
-        if (beam->consumed_fn) { 
-            beam->consumed_fn(beam->consumed_ctx, beam, beam->received_bytes
-                              - beam->reported_consumed_bytes);
-        }
-        beam->reported_consumed_bytes = beam->received_bytes;
-    }
-}
-
-static void report_production(h2_bucket_beam *beam, int force)
-{
-    if (force || beam->sent_bytes != beam->reported_produced_bytes) {
-        if (beam->produced_fn) { 
-            beam->produced_fn(beam->produced_ctx, beam, beam->sent_bytes
-                              - beam->reported_produced_bytes);
-        }
-        beam->reported_produced_bytes = beam->sent_bytes;
-    }
-}
-
 static void h2_blist_cleanup(h2_blist *bl)
 {
     apr_bucket *e;
@@ -399,13 +412,38 @@ static apr_status_t beam_close(h2_bucket
     return APR_SUCCESS;
 }
 
-static apr_status_t beam_cleanup(void *data)
+static void beam_set_send_pool(h2_bucket_beam *beam, apr_pool_t *pool);
+static void beam_set_recv_pool(h2_bucket_beam *beam, apr_pool_t *pool);
+
+static apr_status_t beam_recv_cleanup(void *data)
 {
     h2_bucket_beam *beam = data;
-    
-    beam_close(beam);
-    r_purge_reds(beam);
-    h2_blist_cleanup(&beam->red);
+    /* receiver pool has gone away, clear references */
+    beam->recv_buffer = NULL;
+    beam->recv_pool = NULL;
+    return APR_SUCCESS;
+}
+
+static void beam_set_recv_pool(h2_bucket_beam *beam, apr_pool_t *pool) 
+{
+    /* if the beam owner is the sender, monitor receiver pool lifetime */ 
+    if (beam->owner == H2_BEAM_OWNER_SEND && beam->recv_pool != pool) {
+        if (beam->recv_pool) {
+            apr_pool_cleanup_kill(beam->recv_pool, beam, beam_recv_cleanup);
+        }
+        beam->recv_pool = pool;
+        if (beam->recv_pool) {
+            apr_pool_pre_cleanup_register(beam->recv_pool, beam, beam_recv_cleanup);
+        }
+    }
+}
+
+static apr_status_t beam_send_cleanup(void *data)
+{
+    h2_bucket_beam *beam = data;
+    /* sender has gone away, clear up all references to its memory */
+    r_purge_sent(beam);
+    h2_blist_cleanup(&beam->send_list);
     report_consumption(beam, 0);
     while (!H2_BPROXY_LIST_EMPTY(&beam->proxies)) {
         h2_beam_proxy *proxy = H2_BPROXY_LIST_FIRST(&beam->proxies);
@@ -413,40 +451,101 @@ static apr_status_t beam_cleanup(void *d
         proxy->beam = NULL;
         proxy->bred = NULL;
     }
-    h2_blist_cleanup(&beam->purge);
-    h2_blist_cleanup(&beam->hold);
-    
+    h2_blist_cleanup(&beam->purge_list);
+    h2_blist_cleanup(&beam->hold_list);
+    beam->send_pool = NULL;
     return APR_SUCCESS;
 }
 
+static void beam_set_send_pool(h2_bucket_beam *beam, apr_pool_t *pool) 
+{
+    /* if the beam owner is the receiver, monitor sender pool lifetime */
+    if (beam->owner == H2_BEAM_OWNER_RECV && beam->send_pool != pool) {
+        if (beam->send_pool) {
+            apr_pool_cleanup_kill(beam->send_pool, beam, beam_send_cleanup);
+        }
+        beam->send_pool = pool;
+        if (beam->send_pool) {
+            apr_pool_pre_cleanup_register(beam->send_pool, beam, beam_send_cleanup);
+        }
+    }
+}
+
+static apr_status_t beam_cleanup(void *data)
+{
+    h2_bucket_beam *beam = data;
+    apr_status_t status = APR_SUCCESS;
+    /* owner of the beam is going away, depending on its role, cleanup
+     * strategies differ. */
+    beam_close(beam);
+    switch (beam->owner) {
+        case H2_BEAM_OWNER_SEND:
+            status = beam_send_cleanup(beam);
+            beam->recv_buffer = NULL;
+            beam->recv_pool = NULL;
+            break;
+        case H2_BEAM_OWNER_RECV:
+            if (beam->recv_buffer) {
+                apr_brigade_destroy(beam->recv_buffer);
+            }
+            beam->recv_buffer = NULL;
+            beam->recv_pool = NULL;
+            if (!H2_BLIST_EMPTY(&beam->send_list)) {
+                ap_assert(beam->send_pool);
+            }
+            if (beam->send_pool) {
+                /* sender has not cleaned up, its pool still lives.
+                 * this is normal if the sender uses cleanup via a bucket
+                 * such as the BUCKET_EOR for requests. In that case, the
+                 * beam should have lost its mutex protection, meaning
+                 * it is no longer used multi-threaded and we can safely
+                 * purge all remaining sender buckets. */
+                apr_pool_cleanup_kill(beam->send_pool, beam, beam_send_cleanup);
+                ap_assert(!beam->m_enter);
+                beam_send_cleanup(beam);
+            }
+            ap_assert(H2_BPROXY_LIST_EMPTY(&beam->proxies));
+            ap_assert(H2_BLIST_EMPTY(&beam->send_list));
+            ap_assert(H2_BLIST_EMPTY(&beam->hold_list));
+            ap_assert(H2_BLIST_EMPTY(&beam->purge_list));
+            break;
+        default:
+            ap_assert(NULL);
+            break;
+    }
+    return status;
+}
+
 apr_status_t h2_beam_destroy(h2_bucket_beam *beam)
 {
-    apr_pool_cleanup_kill(beam->red_pool, beam, beam_cleanup);
+    apr_pool_cleanup_kill(beam->pool, beam, beam_cleanup);
     return beam_cleanup(beam);
 }
 
-apr_status_t h2_beam_create(h2_bucket_beam **pbeam, apr_pool_t *red_pool, 
+apr_status_t h2_beam_create(h2_bucket_beam **pbeam, apr_pool_t *pool, 
                             int id, const char *tag, 
+                            h2_beam_owner_t owner,
                             apr_size_t max_buf_size)
 {
     h2_bucket_beam *beam;
     apr_status_t status = APR_SUCCESS;
     
-    beam = apr_pcalloc(red_pool, sizeof(*beam));
+    beam = apr_pcalloc(pool, sizeof(*beam));
     if (!beam) {
         return APR_ENOMEM;
     }
 
     beam->id = id;
     beam->tag = tag;
-    H2_BLIST_INIT(&beam->red);
-    H2_BLIST_INIT(&beam->hold);
-    H2_BLIST_INIT(&beam->purge);
+    beam->pool = pool;
+    beam->owner = owner;
+    H2_BLIST_INIT(&beam->send_list);
+    H2_BLIST_INIT(&beam->hold_list);
+    H2_BLIST_INIT(&beam->purge_list);
     H2_BPROXY_LIST_INIT(&beam->proxies);
-    beam->red_pool = red_pool;
     beam->max_buf_size = max_buf_size;
+    apr_pool_pre_cleanup_register(pool, beam, beam_cleanup);
 
-    apr_pool_pre_cleanup_register(red_pool, beam, beam_cleanup);
     *pbeam = beam;
     
     return status;
@@ -516,10 +615,12 @@ void h2_beam_abort(h2_bucket_beam *beam)
     h2_beam_lock bl;
     
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        r_purge_reds(beam);
-        h2_blist_cleanup(&beam->red);
-        beam->aborted = 1;
-        report_consumption(beam, 0);
+        if (!beam->aborted) {
+            beam->aborted = 1;
+            r_purge_sent(beam);
+            h2_blist_cleanup(&beam->send_list);
+            report_consumption(beam, 0);
+        }
         if (beam->m_cond) {
             apr_thread_cond_broadcast(beam->m_cond);
         }
@@ -532,7 +633,7 @@ apr_status_t h2_beam_close(h2_bucket_bea
     h2_beam_lock bl;
     
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        r_purge_reds(beam);
+        r_purge_sent(beam);
         beam_close(beam);
         report_consumption(beam, 0);
         leave_yellow(beam, &bl);
@@ -540,22 +641,15 @@ apr_status_t h2_beam_close(h2_bucket_bea
     return beam->aborted? APR_ECONNABORTED : APR_SUCCESS;
 }
 
-apr_status_t h2_beam_shutdown(h2_bucket_beam *beam, apr_read_type_e block,
-                              int clear_buffers)
+apr_status_t h2_beam_wait_empty(h2_bucket_beam *beam, apr_read_type_e block)
 {
     apr_status_t status;
     h2_beam_lock bl;
     
     if ((status = enter_yellow(beam, &bl)) == APR_SUCCESS) {
-        if (clear_buffers) {
-            r_purge_reds(beam);
-            h2_blist_cleanup(&beam->red);
-        }
-        beam_close(beam);
-        
-        while (status == APR_SUCCESS 
-               && (!H2_BPROXY_LIST_EMPTY(&beam->proxies)
-                   || (beam->green && !APR_BRIGADE_EMPTY(beam->green)))) {
+        while (status == APR_SUCCESS
+               && !H2_BLIST_EMPTY(&beam->send_list)
+               && !H2_BPROXY_LIST_EMPTY(&beam->proxies)) {
             if (block == APR_NONBLOCK_READ || !bl.mutex) {
                 status = APR_EAGAIN;
                 break;
@@ -570,39 +664,49 @@ apr_status_t h2_beam_shutdown(h2_bucket_
     return status;
 }
 
+static void move_to_hold(h2_bucket_beam *beam, 
+                         apr_bucket_brigade *red_brigade)
+{
+    apr_bucket *b;
+    while (red_brigade && !APR_BRIGADE_EMPTY(red_brigade)) {
+        b = APR_BRIGADE_FIRST(red_brigade);
+        APR_BUCKET_REMOVE(b);
+        H2_BLIST_INSERT_TAIL(&beam->send_list, b);
+    }
+}
+
 static apr_status_t append_bucket(h2_bucket_beam *beam, 
-                                  apr_bucket *bred,
+                                  apr_bucket *b,
                                   apr_read_type_e block,
-                                  apr_pool_t *pool,
                                   h2_beam_lock *pbl)
 {
     const char *data;
     apr_size_t len;
-    apr_off_t space_left = 0;
+    apr_size_t space_left = 0;
     apr_status_t status;
     
-    if (APR_BUCKET_IS_METADATA(bred)) {
-        if (APR_BUCKET_IS_EOS(bred)) {
+    if (APR_BUCKET_IS_METADATA(b)) {
+        if (APR_BUCKET_IS_EOS(b)) {
             beam->closed = 1;
         }
-        APR_BUCKET_REMOVE(bred);
-        H2_BLIST_INSERT_TAIL(&beam->red, bred);
+        APR_BUCKET_REMOVE(b);
+        H2_BLIST_INSERT_TAIL(&beam->send_list, b);
         return APR_SUCCESS;
     }
-    else if (APR_BUCKET_IS_FILE(bred)) {
+    else if (APR_BUCKET_IS_FILE(b)) {
         /* file bucket lengths do not really count */
     }
     else {
         space_left = calc_space_left(beam);
-        if (space_left > 0 && bred->length == ((apr_size_t)-1)) {
+        if (space_left > 0 && b->length == ((apr_size_t)-1)) {
             const char *data;
-            status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
+            status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
             if (status != APR_SUCCESS) {
                 return status;
             }
         }
         
-        if (space_left < bred->length) {
+        if (space_left < b->length) {
             status = r_wait_space(beam, block, pbl, &space_left);
             if (status != APR_SUCCESS) {
                 return status;
@@ -620,33 +724,30 @@ static apr_status_t append_bucket(h2_buc
      * its pool/bucket_alloc from a foreign thread and that will
      * corrupt. */
     status = APR_ENOTIMPL;
-    if (beam->closed && bred->length > 0) {
-        status = APR_EOF;
-    }
-    else if (APR_BUCKET_IS_TRANSIENT(bred)) {
+    if (APR_BUCKET_IS_TRANSIENT(b)) {
         /* this takes care of transient buckets and converts them
          * into heap ones. Other bucket types might or might not be
          * affected by this. */
-        status = apr_bucket_setaside(bred, pool);
+        status = apr_bucket_setaside(b, beam->send_pool);
     }
-    else if (APR_BUCKET_IS_HEAP(bred)) {
+    else if (APR_BUCKET_IS_HEAP(b)) {
         /* For heap buckets read from a green thread is fine. The
          * data will be there and live until the bucket itself is
          * destroyed. */
         status = APR_SUCCESS;
     }
-    else if (APR_BUCKET_IS_POOL(bred)) {
+    else if (APR_BUCKET_IS_POOL(b)) {
         /* pool buckets are bastards that register at pool cleanup
          * to morph themselves into heap buckets. That may happen anytime,
          * even after the bucket data pointer has been read. So at
          * any time inside the green thread, the pool bucket memory
          * may disappear. yikes. */
-        status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
+        status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
         if (status == APR_SUCCESS) {
-            apr_bucket_heap_make(bred, data, len, NULL);
+            apr_bucket_heap_make(b, data, len, NULL);
         }
     }
-    else if (APR_BUCKET_IS_FILE(bred)) {
+    else if (APR_BUCKET_IS_FILE(b)) {
         /* For file buckets the problem is their internal readpool that
          * is used on the first read to allocate buffer/mmap.
          * Since setting aside a file bucket will de-register the
@@ -656,14 +757,14 @@ static apr_status_t append_bucket(h2_buc
          * handles across. The use case for this is to limit the number 
          * of open file handles and rather use a less efficient beam
          * transport. */
-        apr_file_t *fd = ((apr_bucket_file *)bred->data)->fd;
+        apr_file_t *fd = ((apr_bucket_file *)b->data)->fd;
         int can_beam = 1;
         if (beam->last_beamed != fd && beam->can_beam_fn) {
             can_beam = beam->can_beam_fn(beam->can_beam_ctx, beam, fd);
         }
         if (can_beam) {
             beam->last_beamed = fd;
-            status = apr_bucket_setaside(bred, pool);
+            status = apr_bucket_setaside(b, beam->send_pool);
         }
         /* else: enter ENOTIMPL case below */
     }
@@ -678,12 +779,12 @@ static apr_status_t append_bucket(h2_buc
         if (space_left < APR_BUCKET_BUFF_SIZE) {
             space_left = APR_BUCKET_BUFF_SIZE;
         }
-        if (space_left < bred->length) {
-            apr_bucket_split(bred, space_left);
+        if (space_left < b->length) {
+            apr_bucket_split(b, space_left);
         }
-        status = apr_bucket_read(bred, &data, &len, APR_BLOCK_READ);
+        status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
         if (status == APR_SUCCESS) {
-            status = apr_bucket_setaside(bred, pool);
+            status = apr_bucket_setaside(b, beam->send_pool);
         }
     }
     
@@ -691,9 +792,9 @@ static apr_status_t append_bucket(h2_buc
         return status;
     }
     
-    APR_BUCKET_REMOVE(bred);
-    H2_BLIST_INSERT_TAIL(&beam->red, bred);
-    beam->sent_bytes += bred->length;
+    APR_BUCKET_REMOVE(b);
+    H2_BLIST_INSERT_TAIL(&beam->send_list, b);
+    beam->sent_bytes += b->length;
     
     return APR_SUCCESS;
 }
@@ -702,23 +803,27 @@ apr_status_t h2_beam_send(h2_bucket_beam
                           apr_bucket_brigade *red_brigade, 
                           apr_read_type_e block)
 {
-    apr_bucket *bred;
+    apr_bucket *b;
     apr_status_t status = APR_SUCCESS;
     h2_beam_lock bl;
 
     /* Called from the red thread to add buckets to the beam */
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        r_purge_reds(beam);
+        r_purge_sent(beam);
+        if (red_brigade) {
+            beam_set_send_pool(beam, red_brigade->p);
+        }
         
         if (beam->aborted) {
+            move_to_hold(beam, red_brigade);
             status = APR_ECONNABORTED;
         }
         else if (red_brigade) {
             int force_report = !APR_BRIGADE_EMPTY(red_brigade); 
             while (!APR_BRIGADE_EMPTY(red_brigade)
                    && status == APR_SUCCESS) {
-                bred = APR_BRIGADE_FIRST(red_brigade);
-                status = append_bucket(beam, bred, block, beam->red_pool, &bl);
+                b = APR_BRIGADE_FIRST(red_brigade);
+                status = append_bucket(beam, b, block, &bl);
             }
             report_production(beam, force_report);
             if (beam->m_cond) {
@@ -746,18 +851,19 @@ apr_status_t h2_beam_receive(h2_bucket_b
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
 transfer:
         if (beam->aborted) {
-            if (beam->green && !APR_BRIGADE_EMPTY(beam->green)) {
-                apr_brigade_cleanup(beam->green);
+            if (beam->recv_buffer && !APR_BRIGADE_EMPTY(beam->recv_buffer)) {
+                apr_brigade_cleanup(beam->recv_buffer);
             }
             status = APR_ECONNABORTED;
             goto leave;
         }
 
         /* transfer enough buckets from our green brigade, if we have one */
-        while (beam->green
-               && !APR_BRIGADE_EMPTY(beam->green)
+        beam_set_recv_pool(beam, bb->p);
+        while (beam->recv_buffer
+               && !APR_BRIGADE_EMPTY(beam->recv_buffer)
                && (readbytes <= 0 || remain >= 0)) {
-            bgreen = APR_BRIGADE_FIRST(beam->green);
+            bgreen = APR_BRIGADE_FIRST(beam->recv_buffer);
             if (readbytes > 0 && bgreen->length > 0 && remain <= 0) {
                 break;
             }            
@@ -769,8 +875,8 @@ transfer:
 
         /* transfer from our red brigade, transforming red buckets to
          * green ones until we have enough */
-        while (!H2_BLIST_EMPTY(&beam->red) && (readbytes <= 0 || remain >= 0)) {
-            bred = H2_BLIST_FIRST(&beam->red);
+        while (!H2_BLIST_EMPTY(&beam->send_list) && (readbytes <= 0 || remain >= 0)) {
+            bred = H2_BLIST_FIRST(&beam->send_list);
             bgreen = NULL;
             
             if (readbytes > 0 && bred->length > 0 && remain <= 0) {
@@ -785,8 +891,10 @@ transfer:
                 else if (APR_BUCKET_IS_FLUSH(bred)) {
                     bgreen = apr_bucket_flush_create(bb->bucket_alloc);
                 }
-                else {
-                    /* put red into hold, no green sent out */
+                else if (AP_BUCKET_IS_ERROR(bred)) {
+                    ap_bucket_error *eb = (ap_bucket_error *)bred;
+                    bgreen = ap_bucket_error_create(eb->status, eb->data,
+                                                    bb->p, bb->bucket_alloc);
                 }
             }
             else if (APR_BUCKET_IS_FILE(bred)) {
@@ -815,7 +923,7 @@ transfer:
                 remain -= bred->length;
                 ++transferred;
                 APR_BUCKET_REMOVE(bred);
-                H2_BLIST_INSERT_TAIL(&beam->hold, bred);
+                H2_BLIST_INSERT_TAIL(&beam->hold_list, bred);
                 ++transferred;
                 continue;
             }
@@ -832,13 +940,21 @@ transfer:
             /* Place the red bucket into our hold, to be destroyed when no
              * green bucket references it any more. */
             APR_BUCKET_REMOVE(bred);
-            H2_BLIST_INSERT_TAIL(&beam->hold, bred);
+            H2_BLIST_INSERT_TAIL(&beam->hold_list, bred);
             beam->received_bytes += bred->length;
             if (bgreen) {
                 APR_BRIGADE_INSERT_TAIL(bb, bgreen);
                 remain -= bgreen->length;
                 ++transferred;
             }
+            else {
+                bgreen = h2_beam_bucket(beam, bb, bred);
+                while (bgreen && bgreen != APR_BRIGADE_SENTINEL(bb)) {
+                    ++transferred;
+                    remain -= bgreen->length;
+                    bgreen = APR_BUCKET_NEXT(bgreen);
+                }
+            }
         }
 
         if (readbytes > 0 && remain < 0) {
@@ -850,17 +966,17 @@ transfer:
                  remain -= bgreen->length;
                  if (remain < 0) {
                      apr_bucket_split(bgreen, bgreen->length+remain);
-                     beam->green = apr_brigade_split_ex(bb, 
+                     beam->recv_buffer = apr_brigade_split_ex(bb, 
                                                         APR_BUCKET_NEXT(bgreen), 
-                                                        beam->green);
+                                                        beam->recv_buffer);
                      break;
                  }
             }
         }
 
         if (beam->closed 
-            && (!beam->green || APR_BRIGADE_EMPTY(beam->green))
-            && H2_BLIST_EMPTY(&beam->red)) {
+            && (!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer))
+            && H2_BLIST_EMPTY(&beam->send_list)) {
             /* beam is closed and we have nothing more to receive */ 
             if (!beam->close_sent) {
                 apr_bucket *b = apr_bucket_eos_create(bb->bucket_alloc);
@@ -872,6 +988,9 @@ transfer:
         }
         
         if (transferred) {
+            if (beam->m_cond) {
+                apr_thread_cond_broadcast(beam->m_cond);
+            }
             status = APR_SUCCESS;
         }
         else if (beam->closed) {
@@ -885,6 +1004,9 @@ transfer:
             goto transfer;
         }
         else {
+            if (beam->m_cond) {
+                apr_thread_cond_broadcast(beam->m_cond);
+            }
             status = APR_EAGAIN;
         }
 leave:        
@@ -937,8 +1059,8 @@ apr_off_t h2_beam_get_buffered(h2_bucket
     h2_beam_lock bl;
     
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        for (b = H2_BLIST_FIRST(&beam->red); 
-            b != H2_BLIST_SENTINEL(&beam->red);
+        for (b = H2_BLIST_FIRST(&beam->send_list); 
+            b != H2_BLIST_SENTINEL(&beam->send_list);
             b = APR_BUCKET_NEXT(b)) {
             /* should all have determinate length */
             l += b->length;
@@ -955,8 +1077,8 @@ apr_off_t h2_beam_get_mem_used(h2_bucket
     h2_beam_lock bl;
     
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        for (b = H2_BLIST_FIRST(&beam->red); 
-            b != H2_BLIST_SENTINEL(&beam->red);
+        for (b = H2_BLIST_FIRST(&beam->send_list); 
+            b != H2_BLIST_SENTINEL(&beam->send_list);
             b = APR_BUCKET_NEXT(b)) {
             if (APR_BUCKET_IS_FILE(b)) {
                 /* do not count */
@@ -977,16 +1099,23 @@ int h2_beam_empty(h2_bucket_beam *beam)
     h2_beam_lock bl;
     
     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-        empty = (H2_BLIST_EMPTY(&beam->red) 
-                 && (!beam->green || APR_BRIGADE_EMPTY(beam->green)));
+        empty = (H2_BLIST_EMPTY(&beam->send_list) 
+                 && (!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer)));
         leave_yellow(beam, &bl);
     }
     return empty;
 }
 
-int h2_beam_closed(h2_bucket_beam *beam)
+int h2_beam_holds_proxies(h2_bucket_beam *beam)
 {
-    return beam->closed;
+    int has_proxies = 1;
+    h2_beam_lock bl;
+    
+    if (enter_yellow(beam, &bl) == APR_SUCCESS) {
+        has_proxies = !H2_BPROXY_LIST_EMPTY(&beam->proxies);
+        leave_yellow(beam, &bl);
+    }
+    return has_proxies;
 }
 
 int h2_beam_was_received(h2_bucket_beam *beam)

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.h?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.h (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_bucket_beam.h Mon Nov 14 10:26:31 2016
@@ -88,7 +88,7 @@ apr_size_t h2_util_bl_print(char *buffer
  * Care needs to be taken when terminating the beam. The beam registers at
  * the pool it was created with and will cleanup after itself. However, if
  * received buckets do still exist, already freed memory might be accessed.
- * The beam does a AP_DEBUG_ASSERT on this condition.
+ * The beam does a assertion on this condition.
  * 
  * The proper way of shutting down a beam is to first make sure there are no
  * more green buckets out there, then cleanup the beam to purge eventually
@@ -163,6 +163,11 @@ typedef struct {
 typedef int h2_beam_can_beam_callback(void *ctx, h2_bucket_beam *beam,
                                       apr_file_t *file);
 
+typedef enum {
+    H2_BEAM_OWNER_SEND,
+    H2_BEAM_OWNER_RECV
+} h2_beam_owner_t;
+
 /**
  * Will deny all transfer of apr_file_t across the beam and force
  * a data copy instead.
@@ -172,12 +177,15 @@ int h2_beam_no_files(void *ctx, h2_bucke
 struct h2_bucket_beam {
     int id;
     const char *tag;
-    h2_blist red;
-    h2_blist hold;
-    h2_blist purge;
-    apr_bucket_brigade *green;
+    apr_pool_t *pool;
+    h2_beam_owner_t owner;
+    h2_blist send_list;
+    h2_blist hold_list;
+    h2_blist purge_list;
+    apr_bucket_brigade *recv_buffer;
     h2_bproxy_list proxies;
-    apr_pool_t *red_pool;
+    apr_pool_t *send_pool;
+    apr_pool_t *recv_pool;
     
     apr_size_t max_buf_size;
     apr_interval_time_t timeout;
@@ -214,22 +222,23 @@ struct h2_bucket_beam {
  * mutex and will be used in multiple threads. It needs a pool allocator
  * that is only used inside that same mutex.
  *
- * @param pbeam will hold the created beam on return
- * @param red_pool      pool usable on red side, beam lifeline
+ * @param pbeam         will hold the created beam on return
+ * @param pool          pool owning the beam, beam will cleanup when pool released
+ * @param id            identifier of the beam
+ * @param tag           tag identifying beam for logging
+ * @param owner         if the beam is owned by the sender or receiver, e.g. if
+ *                      the pool owner is using this beam for sending or receiving
  * @param buffer_size   maximum memory footprint of buckets buffered in beam, or
  *                      0 for no limitation
- *
- * Call from the red side only.
  */
 apr_status_t h2_beam_create(h2_bucket_beam **pbeam,
-                            apr_pool_t *red_pool, 
-                            int id, const char *tag, 
+                            apr_pool_t *pool, 
+                            int id, const char *tag,
+                            h2_beam_owner_t owner,  
                             apr_size_t buffer_size);
 
 /**
  * Destroys the beam immediately without cleanup.
- *
- * Call from the red side only.
  */ 
 apr_status_t h2_beam_destroy(h2_bucket_beam *beam);
 
@@ -239,10 +248,10 @@ apr_status_t h2_beam_destroy(h2_bucket_b
  * All accepted buckets are removed from the given brigade. Will return with
  * APR_EAGAIN on non-blocking sends when not all buckets could be accepted.
  * 
- * Call from the red side only.
+ * Call from the sender side only.
  */
 apr_status_t h2_beam_send(h2_bucket_beam *beam,  
-                          apr_bucket_brigade *red_buckets, 
+                          apr_bucket_brigade *bb, 
                           apr_read_type_e block);
 
 /**
@@ -251,7 +260,7 @@ apr_status_t h2_beam_send(h2_bucket_beam
  * available or the beam has been closed. Non-blocking calls return APR_EAGAIN
  * if no data is available.
  *
- * Call from the green side only.
+ * Call from the receiver side only.
  */
 apr_status_t h2_beam_receive(h2_bucket_beam *beam, 
                              apr_bucket_brigade *green_buckets, 
@@ -259,31 +268,27 @@ apr_status_t h2_beam_receive(h2_bucket_b
                              apr_off_t readbytes);
 
 /**
- * Determine if beam is closed. May still contain buffered data. 
- * 
- * Call from red or green side.
+ * Determine if beam is empty. 
  */
-int h2_beam_closed(h2_bucket_beam *beam);
+int h2_beam_empty(h2_bucket_beam *beam);
 
 /**
- * Determine if beam is empty. 
- * 
- * Call from red or green side.
+ * Determine if beam has handed out proxy buckets that are not destroyed. 
  */
-int h2_beam_empty(h2_bucket_beam *beam);
+int h2_beam_holds_proxies(h2_bucket_beam *beam);
 
 /**
  * Abort the beam. Will cleanup any buffered buckets and answer all send
  * and receives with APR_ECONNABORTED.
  * 
- * Call from the red side only.
+ * Call from the sender side only.
  */
 void h2_beam_abort(h2_bucket_beam *beam);
 
 /**
  * Close the beam. Sending an EOS bucket serves the same purpose. 
  * 
- * Call from the red side only.
+ * Call from the sender side only.
  */
 apr_status_t h2_beam_close(h2_bucket_beam *beam);
 
@@ -295,10 +300,9 @@ apr_status_t h2_beam_close(h2_bucket_bea
  * If a timeout is set on the beam, waiting might also time out and
  * return APR_ETIMEUP.
  *
- * Call from the red side only.
+ * Call from the sender side only.
  */
-apr_status_t h2_beam_shutdown(h2_bucket_beam *beam, apr_read_type_e block,
-                              int clear_buffers);
+apr_status_t h2_beam_wait_empty(h2_bucket_beam *beam, apr_read_type_e block);
 
 void h2_beam_mutex_set(h2_bucket_beam *beam, 
                        h2_beam_mutex_enter m_enter,
@@ -321,27 +325,27 @@ void h2_beam_buffer_size_set(h2_bucket_b
 apr_size_t h2_beam_buffer_size_get(h2_bucket_beam *beam);
 
 /**
- * Register a callback to be invoked on the red side with the
- * amount of bytes that have been consumed by the red side, since the
+ * Register a callback to be invoked on the sender side with the
+ * amount of bytes that have been consumed by the receiver, since the
  * last callback invocation or reset.
  * @param beam the beam to set the callback on
  * @param cb   the callback or NULL
  * @param ctx  the context to use in callback invocation
  * 
- * Call from the red side, callbacks invoked on red side.
+ * Call from the sender side, callbacks invoked on sender side.
  */
 void h2_beam_on_consumed(h2_bucket_beam *beam, 
                          h2_beam_io_callback *cb, void *ctx);
 
 /**
- * Register a callback to be invoked on the red side with the
- * amount of bytes that have been consumed by the red side, since the
+ * Register a callback to be invoked on the receiver side with the
+ * amount of bytes that have been produces by the sender, since the
  * last callback invocation or reset.
  * @param beam the beam to set the callback on
  * @param cb   the callback or NULL
  * @param ctx  the context to use in callback invocation
  * 
- * Call from the red side, callbacks invoked on red side.
+ * Call from the receiver side, callbacks invoked on receiver side.
  */
 void h2_beam_on_produced(h2_bucket_beam *beam, 
                          h2_beam_io_callback *cb, void *ctx);
@@ -366,4 +370,10 @@ int h2_beam_was_received(h2_bucket_beam
 
 apr_size_t h2_beam_get_files_beamed(h2_bucket_beam *beam);
 
+typedef apr_bucket *h2_bucket_beamer(h2_bucket_beam *beam, 
+                                     apr_bucket_brigade *dest,
+                                     const apr_bucket *src);
+
+void h2_register_bucket_beamer(h2_bucket_beamer *beamer);
+
 #endif /* h2_bucket_beam_h */

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_config.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_config.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_config.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_config.c Mon Nov 14 10:26:31 2016
@@ -198,7 +198,7 @@ const h2_config *h2_config_sget(server_r
 {
     h2_config *cfg = (h2_config *)ap_get_module_config(s->module_config, 
                                                        &http2_module);
-    AP_DEBUG_ASSERT(cfg);
+    ap_assert(cfg);
     return cfg;
 }
 

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.c Mon Nov 14 10:26:31 2016
@@ -14,6 +14,7 @@
  */
 
 #include <assert.h>
+#include <apr_strings.h>
 
 #include <ap_mpm.h>
 
@@ -240,14 +241,14 @@ apr_status_t h2_conn_pre_close(struct h2
     return status;
 }
 
-conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
-                          apr_allocator_t *allocator)
+conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent)
 {
+    apr_allocator_t *allocator;
     apr_pool_t *pool;
     conn_rec *c;
     void *cfg;
     
-    AP_DEBUG_ASSERT(master);
+    ap_assert(master);
     ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, master,
                   "h2_conn(%ld): create slave", master->id);
     
@@ -256,9 +257,7 @@ conn_rec *h2_slave_create(conn_rec *mast
      * independant of its parent pool in the sense that it can work in
      * another thread.
      */
-    if (!allocator) {
-        apr_allocator_create(&allocator);
-    }
+    apr_allocator_create(&allocator);
     apr_pool_create_ex(&pool, parent, NULL, allocator);
     apr_pool_tag(pool, "h2_slave_conn");
     apr_allocator_owner_set(allocator, pool);
@@ -271,8 +270,7 @@ conn_rec *h2_slave_create(conn_rec *mast
     }
     
     memcpy(c, master, sizeof(conn_rec));
-           
-    /* Replace these */
+        
     c->master                 = master;
     c->pool                   = pool;   
     c->conn_config            = ap_create_conn_config(pool);
@@ -284,7 +282,8 @@ conn_rec *h2_slave_create(conn_rec *mast
     c->data_in_output_filters = 0;
     c->clogging_input_filters = 1;
     c->log                    = NULL;
-    c->log_id                 = NULL;
+    c->log_id                 = apr_psprintf(pool, "%ld-%d", 
+                                             master->id, slave_id);
     /* Simulate that we had already a request on this connection. */
     c->keepalives             = 1;
     /* We cannot install the master connection socket on the slaves, as
@@ -304,24 +303,17 @@ conn_rec *h2_slave_create(conn_rec *mast
         ap_set_module_config(c->conn_config, h2_conn_mpm_module(), cfg);
     }
 
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+                  "h2_task: creating conn, master=%ld, sid=%ld, logid=%s", 
+                  master->id, c->id, c->log_id);
     return c;
 }
 
-void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator)
+void h2_slave_destroy(conn_rec *slave)
 {
-    apr_pool_t *parent;
-    apr_allocator_t *allocator = apr_pool_allocator_get(slave->pool);
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, slave,
                   "h2_slave_conn(%ld): destroy (task=%s)", slave->id,
                   apr_table_get(slave->notes, H2_TASK_ID_NOTE));
-    /* Attache the allocator to the parent pool and return it for
-     * reuse, otherwise the own is still the slave pool and it will
-     * get destroyed with it. */
-    parent = apr_pool_parent_get(slave->pool);
-    if (pallocator && parent) {
-        apr_allocator_owner_set(allocator, parent);
-        *pallocator = allocator;
-    }
     apr_pool_destroy(slave->pool);
 }
 

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.h?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.h (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn.h Mon Nov 14 10:26:31 2016
@@ -31,7 +31,7 @@ apr_status_t h2_conn_setup(struct h2_ctx
 /**
  * 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 occured.
+ * and the connection will close or a fatal error occurred.
  *
  * @param ctx the http2 context to run
  * @return APR_SUCCESS when session is done.
@@ -66,9 +66,8 @@ typedef enum {
 h2_mpm_type_t h2_conn_mpm_type(void);
 
 
-conn_rec *h2_slave_create(conn_rec *master, apr_pool_t *parent,
-                          apr_allocator_t *allocator);
-void h2_slave_destroy(conn_rec *slave, apr_allocator_t **pallocator);
+conn_rec *h2_slave_create(conn_rec *master, int slave_id, apr_pool_t *parent);
+void h2_slave_destroy(conn_rec *slave);
 
 apr_status_t h2_slave_run_pre_connection(conn_rec *slave, apr_socket_t *csd);
 void h2_slave_run_connection(conn_rec *slave);

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn_io.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn_io.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_conn_io.c Mon Nov 14 10:26:31 2016
@@ -120,8 +120,8 @@ static void h2_conn_io_bb_log(conn_rec *
         line = *buffer? buffer : "(empty)";
     }
     /* Intentional no APLOGNO */
-    ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%ld-%d)-%s: %s", 
-                  c->id, stream_id, tag, line);
+    ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%s)-%s: %s", 
+                  c->log_id, tag, line);
 
 }
 
@@ -132,7 +132,7 @@ apr_status_t h2_conn_io_init(h2_conn_io
     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->pass_threshold = h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM) / 2;
+    io->pass_threshold = (apr_size_t)h2_config_geti64(cfg, H2_CONF_STREAM_MAX_MEM) / 2;
     
     if (io->is_tls) {
         /* This is what we start with, 
@@ -206,7 +206,7 @@ static apr_status_t read_to_scratch(h2_c
         return APR_SUCCESS;
     }
     
-    AP_DEBUG_ASSERT(b->length <= (io->ssize - io->slen));
+    ap_assert(b->length <= (io->ssize - io->slen));
     if (APR_BUCKET_IS_FILE(b)) {
         apr_bucket_file *f = (apr_bucket_file *)b->data;
         apr_file_t *fd = f->fd;
@@ -269,9 +269,11 @@ static void check_write_size(h2_conn_io
     }
 }
 
-static apr_status_t pass_output(h2_conn_io *io, int flush, int eoc)
+static apr_status_t pass_output(h2_conn_io *io, int flush,
+                                h2_session *session_eoc)
 {
     conn_rec *c = io->c;
+    apr_bucket_brigade *bb = io->output;
     apr_bucket *b;
     apr_off_t bblen;
     apr_status_t status;
@@ -279,28 +281,37 @@ static apr_status_t pass_output(h2_conn_
     append_scratch(io);
     if (flush) {
         b = apr_bucket_flush_create(c->bucket_alloc);
-        APR_BRIGADE_INSERT_TAIL(io->output, b);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
     }
     
-    if (APR_BRIGADE_EMPTY(io->output)) {
+    if (APR_BRIGADE_EMPTY(bb)) {
         return APR_SUCCESS;
     }
     
     ap_log_cerror(APLOG_MARK, APLOG_TRACE4, 0, c, "h2_conn_io: pass_output");
     ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
-    apr_brigade_length(io->output, 0, &bblen);
+    apr_brigade_length(bb, 0, &bblen);
     
-    h2_conn_io_bb_log(c, 0, APLOG_TRACE2, "master conn pass", io->output);
-    status = ap_pass_brigade(c->output_filters, io->output);
+    h2_conn_io_bb_log(c, 0, APLOG_TRACE2, "master conn pass", bb);
+    status = ap_pass_brigade(c->output_filters, bb);
+    if (status == APR_SUCCESS) {
+        io->bytes_written += (apr_size_t)bblen;
+        io->last_write = apr_time_now();
+    }
+    apr_brigade_cleanup(bb);
 
-    /* careful with access after this, as we might have flushed an EOC bucket
-     * that de-allocated us all. */
-    if (!eoc) {
-        apr_brigade_cleanup(io->output);
+    if (session_eoc) {
+        apr_status_t tmp;
+        b = h2_bucket_eoc_create(c->bucket_alloc, session_eoc);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
+        h2_conn_io_bb_log(c, 0, APLOG_TRACE2, "master conn pass", bb);
+        tmp = ap_pass_brigade(c->output_filters, bb);
         if (status == APR_SUCCESS) {
-            io->bytes_written += (apr_size_t)bblen;
-            io->last_write = apr_time_now();
+            status = tmp;
         }
+        /* careful with access to io after this, we have flushed an EOC bucket
+         * that de-allocated us all. */
+        apr_brigade_cleanup(bb);
     }
     
     if (status != APR_SUCCESS) {
@@ -313,14 +324,12 @@ static apr_status_t pass_output(h2_conn_
 
 apr_status_t h2_conn_io_flush(h2_conn_io *io)
 {
-    return pass_output(io, 1, 0);
+    return pass_output(io, 1, NULL);
 }
 
 apr_status_t h2_conn_io_write_eoc(h2_conn_io *io, h2_session *session)
 {
-    apr_bucket *b = h2_bucket_eoc_create(io->c->bucket_alloc, session);
-    APR_BRIGADE_INSERT_TAIL(io->output, b);
-    return pass_output(io, 1, 1);
+    return pass_output(io, 1, session);
 }
 
 apr_status_t h2_conn_io_write(h2_conn_io *io, const char *data, size_t length)
@@ -413,7 +422,7 @@ apr_status_t h2_conn_io_pass(h2_conn_io
         if (!APR_BRIGADE_EMPTY(io->output)) {
             apr_off_t len = h2_brigade_mem_size(io->output);
             if (len >= io->pass_threshold) {
-                return pass_output(io, 0, 0);
+                return pass_output(io, 0, NULL);
             }
         }
     }

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_ctx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_ctx.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_ctx.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_ctx.c Mon Nov 14 10:26:31 2016
@@ -27,7 +27,7 @@
 static h2_ctx *h2_ctx_create(const conn_rec *c)
 {
     h2_ctx *ctx = apr_pcalloc(c->pool, sizeof(h2_ctx));
-    AP_DEBUG_ASSERT(ctx);
+    ap_assert(ctx);
     ap_set_module_config(c->conn_config, &http2_module, ctx);
     h2_ctx_server_set(ctx, c->base_server);
     return ctx;
@@ -35,7 +35,7 @@ static h2_ctx *h2_ctx_create(const conn_
 
 void h2_ctx_clear(const conn_rec *c)
 {
-    AP_DEBUG_ASSERT(c);
+    ap_assert(c);
     ap_set_module_config(c->conn_config, &http2_module, NULL);
 }
 

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.c?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.c (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.c Mon Nov 14 10:26:31 2016
@@ -15,14 +15,17 @@
 
 #include <assert.h>
 
+#include <apr_strings.h>
 #include <httpd.h>
 #include <http_core.h>
+#include <http_protocol.h>
 #include <http_log.h>
 #include <http_connection.h>
 #include <scoreboard.h>
 
 #include "h2_private.h"
 #include "h2.h"
+#include "h2_config.h"
 #include "h2_conn_io.h"
 #include "h2_ctx.h"
 #include "h2_mplx.h"
@@ -30,7 +33,8 @@
 #include "h2_task.h"
 #include "h2_stream.h"
 #include "h2_request.h"
-#include "h2_response.h"
+#include "h2_headers.h"
+#include "h2_stream.h"
 #include "h2_session.h"
 #include "h2_util.h"
 #include "h2_version.h"
@@ -171,30 +175,92 @@ apr_status_t h2_filter_core_input(ap_fil
  * http2 connection status handler + stream out source
  ******************************************************************************/
 
-static const char *H2_SOS_H2_STATUS = "http2-status";
+typedef struct {
+    apr_bucket_refcount refcount;
+    h2_bucket_event_cb *cb;
+    void *ctx;
+} h2_bucket_observer;
+ 
+static apr_status_t bucket_read(apr_bucket *b, const char **str,
+                                apr_size_t *len, apr_read_type_e block)
+{
+    (void)b;
+    (void)block;
+    *str = NULL;
+    *len = 0;
+    return APR_SUCCESS;
+}
 
-int h2_filter_h2_status_handler(request_rec *r)
+static void bucket_destroy(void *data)
 {
-    h2_ctx *ctx = h2_ctx_rget(r);
-    h2_task *task;
-    
-    if (strcmp(r->handler, "http2-status")) {
-        return DECLINED;
+    h2_bucket_observer *h = data;
+    if (apr_bucket_shared_destroy(h)) {
+        if (h->cb) {
+            h->cb(h->ctx, H2_BUCKET_EV_BEFORE_DESTROY, NULL);
+        }
+        apr_bucket_free(h);
     }
-    if (r->method_number != M_GET) {
-        return DECLINED;
+}
+
+apr_bucket * h2_bucket_observer_make(apr_bucket *b, h2_bucket_event_cb *cb,
+                                 void *ctx)
+{
+    h2_bucket_observer *br;
+
+    br = apr_bucket_alloc(sizeof(*br), b->list);
+    br->cb = cb;
+    br->ctx = ctx;
+
+    b = apr_bucket_shared_make(b, br, 0, 0);
+    b->type = &h2_bucket_type_observer;
+    return b;
+} 
+
+apr_bucket * h2_bucket_observer_create(apr_bucket_alloc_t *list, 
+                                       h2_bucket_event_cb *cb, void *ctx)
+{
+    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+    APR_BUCKET_INIT(b);
+    b->free = apr_bucket_free;
+    b->list = list;
+    b = h2_bucket_observer_make(b, cb, ctx);
+    return b;
+}
+                                       
+apr_status_t h2_bucket_observer_fire(apr_bucket *b, h2_bucket_event event)
+{
+    if (H2_BUCKET_IS_OBSERVER(b)) {
+        h2_bucket_observer *l = (h2_bucket_observer *)b->data; 
+        return l->cb(l->ctx, event, b);
     }
+    return APR_EINVAL;
+}
 
-    task = ctx? h2_ctx_get_task(ctx) : NULL;
-    if (task) {
-        /* We need to handle the actual output on the main thread, as
-         * we need to access h2_session information. */
-        apr_table_setn(r->notes, H2_RESP_SOS_NOTE, H2_SOS_H2_STATUS);
-        apr_table_setn(r->headers_out, "Content-Type", "application/json");
-        r->status = 200;
-        return DONE;
+const apr_bucket_type_t h2_bucket_type_observer = {
+    "H2OBS", 5, APR_BUCKET_METADATA,
+    bucket_destroy,
+    bucket_read,
+    apr_bucket_setaside_noop,
+    apr_bucket_split_notimpl,
+    apr_bucket_shared_copy
+};
+
+apr_bucket *h2_bucket_observer_beam(struct h2_bucket_beam *beam,
+                                    apr_bucket_brigade *dest,
+                                    const apr_bucket *src)
+{
+    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, 
+                                                  l->cb, l->ctx);
+        APR_BRIGADE_INSERT_TAIL(dest, b);
+        l->cb = NULL;
+        l->ctx = NULL;
+        h2_bucket_observer_fire(b, H2_BUCKET_EV_BEFORE_MASTER_SEND);
+        return b;
     }
-    return DECLINED;
+    return NULL;
 }
 
 static apr_status_t bbout(apr_bucket_brigade *bb, const char *fmt, ...)
@@ -209,82 +275,257 @@ static apr_status_t bbout(apr_bucket_bri
     return rv;
 }
 
-static apr_status_t h2_status_stream_filter(h2_stream *stream)
+static void add_settings(apr_bucket_brigade *bb, h2_session *s, int last) 
 {
-    h2_session *session = stream->session;
-    h2_mplx *mplx = session->mplx;
-    conn_rec *c = session->c;
-    h2_push_diary *diary;
-    apr_bucket_brigade *bb;
-    apr_status_t status;
+    h2_mplx *m = s->mplx;
     
-    if (!stream->response) {
-        return APR_EINVAL;
-    }
-    
-    if (!stream->buffer) {
-        stream->buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
-    }
-    bb = stream->buffer;
+    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_ENABLE_PUSH\": %d\n", h2_session_push_enabled(s)); 
+    bbout(bb, "  }%s\n", last? "" : ",");
+}
+
+static void add_peer_settings(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    bbout(bb, "  \"peerSettings\": {\n");
+    bbout(bb, "    \"SETTINGS_MAX_CONCURRENT_STREAMS\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS)); 
+    bbout(bb, "    \"SETTINGS_MAX_FRAME_SIZE\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_FRAME_SIZE)); 
+    bbout(bb, "    \"SETTINGS_INITIAL_WINDOW_SIZE\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE)); 
+    bbout(bb, "    \"SETTINGS_ENABLE_PUSH\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_ENABLE_PUSH)); 
+    bbout(bb, "    \"SETTINGS_HEADER_TABLE_SIZE\": %d,\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_HEADER_TABLE_SIZE)); 
+    bbout(bb, "    \"SETTINGS_MAX_HEADER_LIST_SIZE\": %d\n", 
+        nghttp2_session_get_remote_settings(s->ngh2, NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE)); 
+    bbout(bb, "  }%s\n", last? "" : ",");
+}
+
+typedef struct {
+    apr_bucket_brigade *bb;
+    h2_session *s;
+    int idx;
+} stream_ctx_t;
+
+static int add_stream(h2_stream *stream, void *ctx)
+{
+    stream_ctx_t *x = ctx;
+    int32_t flowIn, flowOut;
     
-    apr_table_unset(stream->response->headers, "Content-Length");
-    stream->response->content_length = -1;
+    flowIn = nghttp2_session_get_stream_effective_local_window_size(x->s->ngh2, stream->id); 
+    flowOut = nghttp2_session_get_stream_remote_window_size(x->s->ngh2, stream->id);
+    bbout(x->bb, "%s\n    \"%d\": {\n", (x->idx? "," : ""), stream->id);
+    bbout(x->bb, "    \"state\": \"%s\",\n", h2_stream_state_str(stream));
+    bbout(x->bb, "    \"created\": %f,\n", ((double)stream->created)/APR_USEC_PER_SEC);
+    bbout(x->bb, "    \"flowIn\": %d,\n", flowIn);
+    bbout(x->bb, "    \"flowOut\": %d,\n", flowOut);
+    bbout(x->bb, "    \"dataIn\": %"APR_UINT64_T_FMT",\n", stream->in_data_octets);  
+    bbout(x->bb, "    \"dataOut\": %"APR_UINT64_T_FMT"\n", stream->out_data_octets);  
+    bbout(x->bb, "    }");
+    
+    ++x->idx;
+    return 1;
+} 
+
+static void add_streams(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    stream_ctx_t x;
     
-    bbout(bb, "{\n");
-    bbout(bb, "  \"HTTP2\": \"on\",\n");
-    bbout(bb, "  \"H2PUSH\": \"%s\",\n", h2_session_push_enabled(session)? "on" : "off");
-    bbout(bb, "  \"mod_http2_version\": \"%s\",\n", MOD_HTTP2_VERSION);
-    bbout(bb, "  \"session_id\": %ld,\n", (long)session->id);
-    bbout(bb, "  \"streams_max\": %d,\n", (int)session->max_stream_count);
-    bbout(bb, "  \"this_stream\": %d,\n", stream->id);
-    bbout(bb, "  \"streams_open\": %d,\n", (int)h2_ihash_count(session->streams));
-    bbout(bb, "  \"max_stream_started\": %d,\n", mplx->max_stream_started);
-    bbout(bb, "  \"requests_received\": %d,\n", session->remote.emitted_count);
-    bbout(bb, "  \"responses_submitted\": %d,\n", session->responses_submitted);
-    bbout(bb, "  \"streams_reset\": %d, \n", session->streams_reset);
-    bbout(bb, "  \"pushes_promised\": %d,\n", session->pushes_promised);
-    bbout(bb, "  \"pushes_submitted\": %d,\n", session->pushes_submitted);
-    bbout(bb, "  \"pushes_reset\": %d,\n", session->pushes_reset);
+    x.bb = bb;
+    x.s = s;
+    x.idx = 0;
+    bbout(bb, "  \"streams\": {");
+    h2_mplx_stream_do(s->mplx, add_stream, &x);
+    bbout(bb, "\n  }%s\n", last? "" : ",");
+}
+
+static void add_push(apr_bucket_brigade *bb, h2_session *s, 
+                     h2_stream *stream, int last) 
+{
+    h2_push_diary *diary;
+    apr_status_t status;
     
-    diary = session->push_diary;
+    bbout(bb, "    \"push\": {\n");
+    diary = s->push_diary;
     if (diary) {
         const char *data;
         const char *base64_digest;
         apr_size_t len;
         
-        status = h2_push_diary_digest_get(diary, stream->pool, 256, 
-                                          stream->request->authority, &data, &len);
+        status = h2_push_diary_digest_get(diary, bb->p, 256, 
+                                          stream->request->authority, 
+                                          &data, &len);
         if (status == APR_SUCCESS) {
-            base64_digest = h2_util_base64url_encode(data, len, stream->pool);
-            bbout(bb, "  \"cache_digest\": \"%s\",\n", base64_digest);
-        }
-        
-        /* try the reverse for testing purposes */
-        status = h2_push_diary_digest_set(diary, stream->request->authority, data, len);
-        if (status == APR_SUCCESS) {
-            status = h2_push_diary_digest_get(diary, stream->pool, 256, 
-                                              stream->request->authority, &data, &len);
-            if (status == APR_SUCCESS) {
-                base64_digest = h2_util_base64url_encode(data, len, stream->pool);
-                bbout(bb, "  \"cache_digest^2\": \"%s\",\n", base64_digest);
-            }
+            base64_digest = h2_util_base64url_encode(data, len, bb->p);
+            bbout(bb, "      \"cacheDigest\": \"%s\",\n", base64_digest);
         }
     }
-    bbout(bb, "  \"frames_received\": %ld,\n", (long)session->frames_received);
-    bbout(bb, "  \"frames_sent\": %ld,\n", (long)session->frames_sent);
-    bbout(bb, "  \"bytes_received\": %"APR_UINT64_T_FMT",\n", session->io.bytes_read);
-    bbout(bb, "  \"bytes_sent\": %"APR_UINT64_T_FMT"\n", session->io.bytes_written);
+    bbout(bb, "      \"promises\": %d,\n", s->pushes_promised);
+    bbout(bb, "      \"submits\": %d,\n", s->pushes_submitted);
+    bbout(bb, "      \"resets\": %d\n", s->pushes_reset);
+    bbout(bb, "    }%s\n", last? "" : ",");
+}
+
+static void add_in(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    bbout(bb, "    \"in\": {\n");
+    bbout(bb, "      \"requests\": %d,\n", s->remote.emitted_count);
+    bbout(bb, "      \"resets\": %d, \n", s->streams_reset);
+    bbout(bb, "      \"frames\": %ld,\n", (long)s->frames_received);
+    bbout(bb, "      \"octets\": %"APR_UINT64_T_FMT"\n", s->io.bytes_read);
+    bbout(bb, "    }%s\n", last? "" : ",");
+}
+
+static void add_out(apr_bucket_brigade *bb, h2_session *s, int last) 
+{
+    bbout(bb, "    \"out\": {\n");
+    bbout(bb, "      \"responses\": %d,\n", s->responses_submitted);
+    bbout(bb, "      \"frames\": %ld,\n", (long)s->frames_sent);
+    bbout(bb, "      \"octets\": %"APR_UINT64_T_FMT"\n", s->io.bytes_written);
+    bbout(bb, "    }%s\n", last? "" : ",");
+}
+
+static void add_stats(apr_bucket_brigade *bb, h2_session *s, 
+                     h2_stream *stream, int last) 
+{
+    bbout(bb, "  \"stats\": {\n");
+    add_in(bb, s, 0);
+    add_out(bb, s, 0);
+    add_push(bb, s, stream, 1);
+    bbout(bb, "  }%s\n", last? "" : ",");
+}
+
+static apr_status_t h2_status_insert(h2_task *task, apr_bucket *b)
+{
+    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 (!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(s->ngh2); 
+    connFlowOut = nghttp2_session_get_remote_window_size(s->ngh2);
+     
+    bbout(bb, "{\n");
+    bbout(bb, "  \"version\": \"draft-01\",\n");
+    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", s->local.shutdown);
+
+    add_streams(bb, s, 0);
+    
+    add_stats(bb, s, stream, 1);
     bbout(bb, "}\n");
     
+    while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
+        APR_BUCKET_REMOVE(e);
+        APR_BUCKET_INSERT_AFTER(b, e);
+        b = e;
+    }
+    apr_brigade_destroy(bb);
+    
     return APR_SUCCESS;
 }
 
-apr_status_t h2_stream_filter(h2_stream *stream)
+static apr_status_t status_event(void *ctx, h2_bucket_event event, 
+                                 apr_bucket *b)
 {
-    const char *fname = stream->response? stream->response->sos_filter : NULL; 
-    if (fname && !strcmp(H2_SOS_H2_STATUS, fname)) {
-        return h2_status_stream_filter(stream);
+    h2_task *task = ctx;
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, task->c->master, 
+                  "status_event(%s): %d", task->id, event);
+    switch (event) {
+        case H2_BUCKET_EV_BEFORE_MASTER_SEND:
+            h2_status_insert(task, b);
+            break;
+        default:
+            break;
     }
     return APR_SUCCESS;
 }
 
+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;
+    apr_bucket *b;
+    apr_status_t status;
+    
+    if (strcmp(r->handler, "http2-status")) {
+        return DECLINED;
+    }
+    if (r->method_number != M_GET && r->method_number != M_POST) {
+        return DECLINED;
+    }
+
+    task = ctx? h2_ctx_get_task(ctx) : NULL;
+    if (task) {
+
+        if ((status = ap_discard_request_body(r)) != OK) {
+            return status;
+        }
+        
+        /* We need to handle the actual output on the main thread, as
+         * we need to access h2_session information. */
+        r->status = 200;
+        r->clength = -1;
+        r->chunked = 1;
+        apr_table_unset(r->headers_out, "Content-Length");
+        ap_set_content_type(r, "application/json");
+        apr_table_setn(r->notes, H2_FILTER_DEBUG_NOTE, "on");
+
+        bb = apr_brigade_create(r->pool, c->bucket_alloc);
+        b = h2_bucket_observer_create(c->bucket_alloc, status_event, task);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
+        b = apr_bucket_eos_create(c->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(bb, b);
+
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+                      "status_handler(%s): checking for incoming trailers", 
+                      task->id);
+        if (r->trailers_in && !apr_is_empty_table(r->trailers_in)) {
+            ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
+                          "status_handler(%s): seeing incoming trailers", 
+                          task->id);
+            apr_table_setn(r->trailers_out, "h2-trailers-in", 
+                           apr_itoa(r->pool, 1));
+        }
+        
+        status = ap_pass_brigade(r->output_filters, bb);
+        if (status == APR_SUCCESS
+            || r->status != HTTP_OK
+            || c->aborted) {
+            return OK;
+        }
+        else {
+            /* no way to know what type of error occurred */
+            ap_log_rerror(APLOG_MARK, APLOG_TRACE1, status, r,
+                          "status_handler(%s): ap_pass_brigade failed", 
+                          task->id);
+            return AP_FILTER_ERROR;
+        }
+    }
+    return DECLINED;
+}
+

Modified: httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.h?rev=1769588&r1=1769587&r2=1769588&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.h (original)
+++ httpd/httpd/branches/2.4.x-openssl-1.1.0-compat/modules/http2/h2_filter.h Mon Nov 14 10:26:31 2016
@@ -16,6 +16,8 @@
 #ifndef __mod_h2__h2_filter__
 #define __mod_h2__h2_filter__
 
+struct h2_bucket_beam;
+struct h2_headers;
 struct h2_stream;
 struct h2_session;
 
@@ -43,9 +45,33 @@ apr_status_t h2_filter_core_input(ap_fil
                                   apr_read_type_e block,
                                   apr_off_t readbytes);
 
-#define H2_RESP_SOS_NOTE     "h2-sos-filter"
+/******* observer bucket ******************************************************/
+
+typedef enum {
+    H2_BUCKET_EV_BEFORE_DESTROY,
+    H2_BUCKET_EV_BEFORE_MASTER_SEND
+} h2_bucket_event;
+
+extern const apr_bucket_type_t h2_bucket_type_observer;
+
+typedef apr_status_t h2_bucket_event_cb(void *ctx, h2_bucket_event event, apr_bucket *b);
+
+#define H2_BUCKET_IS_OBSERVER(e)     (e->type == &h2_bucket_type_observer)
+
+apr_bucket * h2_bucket_observer_make(apr_bucket *b, h2_bucket_event_cb *cb, 
+                                     void *ctx); 
+
+apr_bucket * h2_bucket_observer_create(apr_bucket_alloc_t *list, 
+                                       h2_bucket_event_cb *cb, void *ctx); 
+                                       
+apr_status_t h2_bucket_observer_fire(apr_bucket *b, h2_bucket_event event);
+
+apr_bucket *h2_bucket_observer_beam(struct h2_bucket_beam *beam,
+                                    apr_bucket_brigade *dest,
+                                    const apr_bucket *src);
+
+/******* /.well-known/h2/state handler ****************************************/
 
-apr_status_t h2_stream_filter(struct h2_stream *stream);
 int h2_filter_h2_status_handler(request_rec *r);
 
 #endif /* __mod_h2__h2_filter__ */