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 2016/03/29 15:28:19 UTC

svn commit: r1737006 - in /httpd/httpd/trunk: ./ modules/http2/

Author: icing
Date: Tue Mar 29 13:28:18 2016
New Revision: 1737006

URL: http://svn.apache.org/viewvc?rev=1737006&view=rev
Log:
mod_http2: more effient passing of response bodies

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/http2/h2_io.c
    httpd/httpd/trunk/modules/http2/h2_io.h
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_mplx.h
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_util.c
    httpd/httpd/trunk/modules/http2/h2_util.h
    httpd/httpd/trunk/modules/http2/h2_version.h

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Tue Mar 29 13:28:18 2016
@@ -1,11 +1,9 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
-  *) mod_http2: fix for missing score board updates on request count, fix for
-     memory leak on slave connection reuse.
+  *) mod_http2: more efficient passing of response bodies with less contention
+     and file bucket forwarding. [Stefan Eissing] 
      
-  *) mod_http2: disabling PUSH when client sends GOAWAY.
-  
   *) mod_proxy_http2: using HTTP/2 flow control for backend streams by 
      observing data actually send out on the frontend h2 connection. 
      [Stefan Eissing]

Modified: httpd/httpd/trunk/modules/http2/h2_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_io.c?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_io.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_io.c Tue Mar 29 13:28:18 2016
@@ -346,63 +346,36 @@ apr_status_t h2_io_in_close(h2_io *io)
     return APR_SUCCESS;
 }
 
-static int is_out_readable(h2_io *io, apr_off_t *plen, int *peos, 
-                           apr_status_t *ps)
+apr_status_t h2_io_out_get_brigade(h2_io *io, apr_bucket_brigade *bb, 
+                                   apr_off_t len)
 {
     if (io->rst_error) {
-        *ps = APR_ECONNABORTED;
-        return 0;
+        return APR_ECONNABORTED;
     }
     if (io->eos_out_read) {
-        *plen = 0;
-        *peos = 1;
-        *ps = APR_SUCCESS;
-        return 0;
+        return APR_EOF;
     }
     else if (!io->bbout) {
-        *plen = 0;
-        *peos = 0;
-        *ps = APR_EAGAIN;
-        return 0;
-    }
-    return 1;
-}
-
-apr_status_t h2_io_out_readx(h2_io *io,  
-                             h2_io_data_cb *cb, void *ctx, 
-                             apr_off_t *plen, int *peos)
-{
-    apr_status_t status;
-    if (!is_out_readable(io, plen, peos, &status)) {
-        return status;
-    }
-    if (cb == NULL) {
-        /* just checking length available */
-        status = h2_util_bb_avail(io->bbout, plen, peos);
+        return APR_EAGAIN;
     }
     else {
-        status = h2_util_bb_readx(io->bbout, cb, ctx, plen, peos);
-        if (status == APR_SUCCESS) {
-            io->eos_out_read = *peos;
-            io->output_consumed += *plen;
+        apr_status_t status;
+        apr_off_t pre_len, post_len;
+        /* Allow file handles pass through without limits. If they
+         * already have the lifetime of this stream, we might as well
+         * pass them on to the master connection */
+        apr_size_t files = INT_MAX;
+        
+        apr_brigade_length(bb, 0, &pre_len);
+        status = h2_util_move(bb, io->bbout, len, &files, "h2_io_read_to");
+        if (status == APR_SUCCESS && io->eos_out 
+            && APR_BRIGADE_EMPTY(io->bbout)) {
+            io->eos_out_read = 1;
         }
-    }
-    return status;
-}
-
-apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, 
-                               apr_off_t *plen, int *peos)
-{
-    apr_status_t status;
-    if (!is_out_readable(io, plen, peos, &status)) {
+        apr_brigade_length(bb, 0, &post_len);
+        io->output_consumed += (post_len - pre_len);
         return status;
     }
-    status = h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
-    if (status == APR_SUCCESS && io->eos_out && APR_BRIGADE_EMPTY(io->bbout)) {
-        io->eos_out_read = *peos = 1;
-    }
-    io->output_consumed += *plen;
-    return status;
 }
 
 static void process_trailers(h2_io *io, apr_table_t *trailers)

Modified: httpd/httpd/trunk/modules/http2/h2_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_io.h?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_io.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_io.h Tue Mar 29 13:28:18 2016
@@ -151,13 +151,9 @@ apr_status_t h2_io_in_shutdown(h2_io *io
  * @param plen the requested max len, set to amount of data on return
  * @param peos != 0 iff the end of stream has been reached
  */
-apr_status_t h2_io_out_readx(h2_io *io,  
-                             h2_io_data_cb *cb, void *ctx, 
-                             apr_off_t *plen, int *peos);
-
-apr_status_t h2_io_out_read_to(h2_io *io, 
-                               apr_bucket_brigade *bb, 
-                               apr_off_t *plen, int *peos);
+apr_status_t h2_io_out_get_brigade(h2_io *io, 
+                                   apr_bucket_brigade *bb, 
+                                   apr_off_t len);
 
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
                              apr_size_t maxlen, apr_table_t *trailers,

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.c?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.c Tue Mar 29 13:28:18 2016
@@ -613,40 +613,9 @@ apr_status_t h2_mplx_in_update_windows(h
     return status;
 }
 
-apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, 
-                               h2_io_data_cb *cb, void *ctx, 
-                               apr_off_t *plen, int *peos,
-                               apr_table_t **ptrailers)
-{
-    apr_status_t status;
-    int acquired;
-    
-    AP_DEBUG_ASSERT(m);
-    if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) {
-        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        if (io && !io->orphaned) {
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre");
-            
-            status = h2_io_out_readx(io, cb, ctx, plen, peos);
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post");
-            if (status == APR_SUCCESS && cb) {
-                h2_io_signal(io, H2_IO_WRITE);
-            }
-        }
-        else {
-            status = APR_ECONNABORTED;
-        }
-        
-        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
-        leave_mutex(m, acquired);
-    }
-    return status;
-}
-
-apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, 
-                                 apr_bucket_brigade *bb, 
-                                 apr_off_t *plen, int *peos,
-                                 apr_table_t **ptrailers)
+apr_status_t h2_mplx_out_get_brigade(h2_mplx *m, int stream_id, 
+                                     apr_bucket_brigade *bb, 
+                                     apr_off_t len, apr_table_t **ptrailers)
 {
     apr_status_t status;
     int acquired;
@@ -655,11 +624,11 @@ apr_status_t h2_mplx_out_read_to(h2_mplx
     if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
         if (io && !io->orphaned) {
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_pre");
+            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_get_brigade_pre");
             
-            status = h2_io_out_read_to(io, bb, plen, peos);
+            status = h2_io_out_get_brigade(io, bb, len);
             
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_post");
+            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_get_brigade_post");
             if (status == APR_SUCCESS) {
                 h2_io_signal(io, H2_IO_WRITE);
             }
@@ -667,7 +636,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx
         else {
             status = APR_ECONNABORTED;
         }
-        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
+        *ptrailers = io->response? io->response->trailers : NULL;
         leave_mutex(m, acquired);
     }
     return status;

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.h?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.h Tue Mar 29 13:28:18 2016
@@ -266,22 +266,12 @@ struct h2_stream *h2_mplx_next_submit(h2
                                       struct h2_ihash_t *streams);
 
 /**
- * Reads output data from the given stream. Will never block, but
- * return APR_EAGAIN until data arrives or the stream is closed.
- */
-apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, 
-                               h2_io_data_cb *cb, void *ctx, 
-                               apr_off_t *plen, int *peos,
-                               apr_table_t **ptrailers);
-
-/**
  * Reads output data into the given brigade. Will never block, but
  * return APR_EAGAIN until data arrives or the stream is closed.
  */
-apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id, 
-                                 apr_bucket_brigade *bb, 
-                                 apr_off_t *plen, int *peos,
-                                 apr_table_t **ptrailers);
+apr_status_t h2_mplx_out_get_brigade(h2_mplx *mplx, int stream_id, 
+                                     apr_bucket_brigade *bb, 
+                                     apr_off_t len, apr_table_t **ptrailers);
 
 /**
  * Opens the output for the given stream with the specified response.

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Tue Mar 29 13:28:18 2016
@@ -555,6 +555,7 @@ static int on_send_data_cb(nghttp2_sessi
     unsigned char padlen;
     int eos;
     h2_stream *stream;
+    apr_bucket *b;
     
     (void)ngh2;
     (void)source;
@@ -599,16 +600,10 @@ static int on_send_data_cb(nghttp2_sessi
         }
     }
     else {
-        apr_bucket *b;
-        char *header = apr_pcalloc(stream->pool, 10);
-        memcpy(header, (const char *)framehd, 9);
-        if (padlen) {
-            header[9] = (char)padlen;
+        status = h2_conn_io_write(&session->io, (const char *)framehd, 9);
+        if (padlen && status == APR_SUCCESS) {
+            status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
         }
-        b = apr_bucket_pool_create(header, padlen? 10 : 9, 
-                                   stream->pool, session->c->bucket_alloc);
-        status = h2_conn_io_writeb(&session->io, b);
-        
         if (status == APR_SUCCESS) {
             apr_off_t len = length;
             status = h2_stream_read_to(stream, session->io.output, &len, &eos);

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Tue Mar 29 13:28:18 2016
@@ -493,7 +493,9 @@ const h2_priority *h2_stream_get_priorit
 typedef struct h2_sos_mplx {
     h2_mplx *m;
     apr_bucket_brigade *bb;
+    apr_bucket_brigade *tmp;
     apr_table_t *trailers;
+    apr_off_t  buffer_size;
 } h2_sos_mplx;
 
 #define H2_SOS_MPLX_OUT(lvl,msos,msg) \
@@ -503,129 +505,79 @@ typedef struct h2_sos_mplx {
     } while(0)
     
 
-static apr_status_t h2_sos_mplx_read_to(h2_sos *sos, apr_bucket_brigade *bb, 
-                                        apr_off_t *plen, int *peos)
+static apr_status_t mplx_transfer(h2_sos_mplx *msos, int stream_id, 
+                                  apr_pool_t *pool)
 {
-    h2_sos_mplx *msos = sos->ctx;
-    apr_status_t status = APR_SUCCESS;
+    apr_status_t status;
     apr_table_t *trailers = NULL;
-
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx read_to_pre");
     
-    if (APR_BRIGADE_EMPTY(msos->bb)) {
-        apr_off_t tlen = *plen;
-        int eos;
-        status = h2_mplx_out_read_to(msos->m, sos->stream->id, 
-                                     msos->bb, &tlen, &eos, &trailers);
+    if (!msos->tmp) {
+        msos->tmp = apr_brigade_create(msos->bb->p, msos->bb->bucket_alloc);
     }
-    
-    if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(msos->bb)) {
-        status = h2_transfer_brigade(bb, msos->bb, sos->stream->pool, 
-                                     plen, peos);
-    }
-    else {
-        *plen = 0;
-        *peos = 0;
+    status = h2_mplx_out_get_brigade(msos->m, stream_id, msos->tmp, 
+                                     msos->buffer_size-1, &trailers);
+    if (status == APR_SUCCESS) {
+        status = h2_transfer_brigade(msos->bb, msos->tmp, pool);
     }
-
     if (trailers) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                      "h2_stream(%ld-%d): read_to, saving trailers",
-                      msos->m->id, sos->stream->id);
         msos->trailers = trailers;
     }
-    
+    return status;
+}
+ 
+static apr_status_t h2_sos_mplx_read_to(h2_sos *sos, apr_bucket_brigade *bb, 
+                                        apr_off_t *plen, int *peos)
+{
+    h2_sos_mplx *msos = sos->ctx;
+    apr_status_t status;
+
+    status = h2_append_brigade(bb, msos->bb, plen, peos);
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx read_to_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
                   "h2_stream(%ld-%d): read_to, len=%ld eos=%d",
                   msos->m->id, sos->stream->id, (long)*plen, *peos);
     return status;
 }
 
-static apr_status_t h2_sos_mplx_prep_read(h2_sos *sos, apr_off_t *plen, int *peos)
+static apr_status_t h2_sos_mplx_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx,
+                                      apr_off_t *plen, int *peos)
 {
     h2_sos_mplx *msos = sos->ctx;
     apr_status_t status = APR_SUCCESS;
-    const char *src;
-    apr_table_t *trailers = NULL;
-    int test_read = (*plen == 0);
-    
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_pre");
-    if (!APR_BRIGADE_EMPTY(msos->bb)) {
-        src = "stream";
-        status = h2_util_bb_avail(msos->bb, plen, peos);
-        if (!test_read && status == APR_SUCCESS && !*peos && !*plen) {
-            apr_brigade_cleanup(msos->bb);
-            return h2_sos_mplx_prep_read(sos, plen, peos);
-        }
-    }
-    else {
-        src = "mplx";
-        status = h2_mplx_out_readx(msos->m, sos->stream->id, 
-                                   NULL, NULL, plen, peos, &trailers);
-        if (trailers) {
-            msos->trailers = trailers;
-        }    
-    }
     
-    if (!test_read && status == APR_SUCCESS && !*peos && !*plen) {
+    status = h2_util_bb_readx(msos->bb, cb, ctx, plen, peos);
+    if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
-    
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s",
-                  msos->m->id, sos->stream->id, src, (long)*plen, *peos,
-                  msos->trailers? "yes" : "no");
+                  "h2_stream(%ld-%d): readx, len=%ld eos=%d",
+                  msos->m->id, sos->stream->id, (long)*plen, *peos);
     return status;
 }
 
-static apr_status_t h2_sos_mplx_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx,
-                                      apr_off_t *plen, int *peos)
+static apr_status_t h2_sos_mplx_prep_read(h2_sos *sos, apr_off_t *plen, int *peos)
 {
     h2_sos_mplx *msos = sos->ctx;
     apr_status_t status = APR_SUCCESS;
-    apr_table_t *trailers = NULL;
-    const char *src;
     
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx readx_pre");
-    *peos = 0;
-    if (!APR_BRIGADE_EMPTY(msos->bb)) {
-        apr_off_t origlen = *plen;
-        
-        src = "stream";
-        status = h2_util_bb_readx(msos->bb, cb, ctx, plen, peos);
-        if (status == APR_SUCCESS && !*peos && !*plen) {
-            apr_brigade_cleanup(msos->bb);
-            *plen = origlen;
-            return h2_sos_mplx_readx(sos, cb, ctx, plen, peos);
-        }
-    }
-    else {
-        src = "mplx";
-        status = h2_mplx_out_readx(msos->m, sos->stream->id, 
-                                   cb, ctx, plen, peos, &trailers);
-    }
+    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_pre");
     
-    if (trailers) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                      "h2_stream(%ld-%d): readx, saving trailers",
-                      msos->m->id, sos->stream->id);
-        msos->trailers = trailers;
+    if (APR_BRIGADE_EMPTY(msos->bb)) {
+        status = mplx_transfer(msos, sos->stream->id, sos->stream->pool);
     }
+    status = h2_util_bb_avail(msos->bb, plen, peos);
     
+    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_post");
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
+                  "h2_stream(%ld-%d): prep_read, len=%ld eos=%d, trailers=%s",
+                  msos->m->id, sos->stream->id, (long)*plen, *peos,
+                  msos->trailers? "yes" : "no");
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
     
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_stream readx_post");
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                  "h2_stream(%ld-%d): readx %s, len=%ld eos=%d",
-                  msos->m->id, sos->stream->id, src, (long)*plen, *peos);
-    
     return status;
 }
 
@@ -642,13 +594,8 @@ static apr_status_t h2_sos_mplx_buffer(h
     apr_status_t status = APR_SUCCESS;
 
     if (bb && !APR_BRIGADE_EMPTY(bb)) {
-        apr_size_t move_all = INT_MAX;
-        /* we can move file handles from h2_mplx into this h2_stream as many
-         * as we want, since the lifetimes are the same and we are not freeing
-         * the ones in h2_mplx->io before this stream is done. */
         H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx set_response_pre");
-        status = h2_util_move(msos->bb, bb, 16 * 1024, &move_all,  
-                              "h2_stream_set_response");
+        status = mplx_transfer(msos, sos->stream->id, sos->stream->pool);
         H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx set_response_post");
     }
     return status;
@@ -662,7 +609,8 @@ static h2_sos *h2_sos_mplx_create(h2_str
     msos = apr_pcalloc(stream->pool, sizeof(*msos));
     msos->m = stream->session->mplx;
     msos->bb = apr_brigade_create(stream->pool, msos->m->c->bucket_alloc);
-
+    msos->buffer_size = 32 * 1024;
+    
     sos = apr_pcalloc(stream->pool, sizeof(*sos));
     sos->stream = stream;
     sos->response = response;

Modified: httpd/httpd/trunk/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_util.c?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_util.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_util.c Tue Mar 29 13:28:18 2016
@@ -861,7 +861,7 @@ void h2_util_bb_log(conn_rec *c, int str
 
 }
 
-apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
+apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to,
                                  apr_bucket_brigade *from, 
                                  apr_pool_t *p,
                                  apr_off_t *plen,
@@ -922,6 +922,92 @@ apr_status_t h2_transfer_brigade(apr_buc
             }
         }
         
+        APR_BUCKET_REMOVE(e);
+        APR_BRIGADE_INSERT_TAIL(to, e);
+        len += e->length;
+        remain -= e->length;
+    }
+    
+    *plen = len;
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
+                                 apr_bucket_brigade *from, 
+                                 apr_pool_t *p)
+{
+    apr_bucket *e;
+    apr_status_t rv;
+
+    while (!APR_BRIGADE_EMPTY(from)) {
+        e = APR_BRIGADE_FIRST(from);
+        
+        rv = apr_bucket_setaside(e, p);
+        
+        /* If the bucket type does not implement setaside, then
+         * (hopefully) morph it into a bucket type which does, and set
+         * *that* aside... */
+        if (rv == APR_ENOTIMPL) {
+            const char *s;
+            apr_size_t n;
+            
+            rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ);
+            if (rv == APR_SUCCESS) {
+                rv = apr_bucket_setaside(e, p);
+            }
+        }
+        
+        if (rv != APR_SUCCESS) {
+            /* Return an error but still save the brigade if
+             * ->setaside() is really not implemented. */
+            if (rv != APR_ENOTIMPL) {
+                return rv;
+            }
+        }
+        
+        APR_BUCKET_REMOVE(e);
+        APR_BRIGADE_INSERT_TAIL(to, e);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_append_brigade(apr_bucket_brigade *to,
+                               apr_bucket_brigade *from, 
+                               apr_off_t *plen,
+                               int *peos)
+{
+    apr_bucket *e;
+    apr_off_t len = 0, remain = *plen;
+    apr_status_t rv;
+
+    *peos = 0;
+    
+    while (!APR_BRIGADE_EMPTY(from)) {
+        e = APR_BRIGADE_FIRST(from);
+        
+        if (APR_BUCKET_IS_METADATA(e)) {
+            if (APR_BUCKET_IS_EOS(e)) {
+                *peos = 1;
+            }
+        }
+        else {        
+            if (remain > 0 && e->length == ((apr_size_t)-1)) {
+                const char *ign;
+                apr_size_t ilen;
+                rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ);
+                if (rv != APR_SUCCESS) {
+                    return rv;
+                }
+            }
+            
+            if (remain < e->length) {
+                if (remain <= 0) {
+                    return APR_SUCCESS;
+                }
+                apr_bucket_split(e, remain);
+            }
+        }
+        
         APR_BUCKET_REMOVE(e);
         APR_BRIGADE_INSERT_TAIL(to, e);
         len += e->length;

Modified: httpd/httpd/trunk/modules/http2/h2_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_util.h?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_util.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_util.h Tue Mar 29 13:28:18 2016
@@ -243,18 +243,44 @@ void h2_util_bb_log(conn_rec *c, int str
 
 /**
  * Transfer buckets from one brigade to another with a limit on the 
- * maximum amount of bytes transfered.
+ * maximum amount of bytes transfered. Sets aside the buckets to
+ * pool p.
  * @param to   brigade to transfer buckets to
  * @param from brigades to remove buckets from
  * @param p    pool that buckets should be setaside to
  * @param plen maximum bytes to transfer, actual bytes transferred
  * @param peos if an EOS bucket was transferred
  */
+apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to,
+                                  apr_bucket_brigade *from, 
+                                  apr_pool_t *p,
+                                  apr_off_t *plen,
+                                  int *peos);
+
+/**
+ * Transfer all buckets from one brigade to another. Sets aside the buckets to
+ * pool p.
+ * @param to   brigade to transfer buckets to
+ * @param from brigades to remove buckets from
+ * @param p    pool that buckets should be setaside to
+ */
 apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
                                  apr_bucket_brigade *from, 
-                                 apr_pool_t *p,
-                                 apr_off_t *plen,
-                                 int *peos);
+                                 apr_pool_t *p);
+
+/**
+ * Transfer buckets from one brigade to another with a limit on the 
+ * maximum amount of bytes transfered. Does no setaside magic, lifetime
+ * of brigades must fit. 
+ * @param to   brigade to transfer buckets to
+ * @param from brigades to remove buckets from
+ * @param plen maximum bytes to transfer, actual bytes transferred
+ * @param peos if an EOS bucket was transferred
+ */
+apr_status_t h2_append_brigade(apr_bucket_brigade *to,
+                               apr_bucket_brigade *from, 
+                               apr_off_t *plen,
+                               int *peos);
 
 /**
  * Get an approximnation of the memory footprint of the given

Modified: httpd/httpd/trunk/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_version.h?rev=1737006&r1=1737005&r2=1737006&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_version.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_version.h Tue Mar 29 13:28:18 2016
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.4.5-DEV"
+#define MOD_HTTP2_VERSION "1.4.6-DEV"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_HTTP2_VERSION_NUM 0x010405
+#define MOD_HTTP2_VERSION_NUM 0x010406
 
 
 #endif /* mod_h2_h2_version_h */