]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/commitdiff
apache: Update to 2.4.28
authorMatthias Fischer <matthias.fischer@ipfire.org>
Sun, 8 Oct 2017 14:37:21 +0000 (16:37 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 9 Oct 2017 13:46:00 +0000 (14:46 +0100)
http://apache.mirror.digionline.de//httpd/CHANGES_2.4.28

Signed-off-by: Matthias Fischer <matthias.fischer@ipfire.org>
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
lfs/apache2
src/patches/apache-2.4.27-CVE-2017-9798-fix.patch [deleted file]
src/patches/apache-2.4.27-PR61382-fix.patch [deleted file]

index 138ede8de3121f10ceb36872514a5caa2612d3fd..4276a8880a6dcfc012c43d9d389d9bd38438f015 100644 (file)
@@ -1,7 +1,7 @@
 ###############################################################################
 #                                                                             #
 # IPFire.org - A linux based firewall                                         #
-# Copyright (C) 2007-2014   IPFire Team  <info@ipfire.org>                    #
+# Copyright (C) 2007-2017   IPFire Team  <info@ipfire.org>                    #
 #                                                                             #
 # This program is free software: you can redistribute it and/or modify        #
 # it under the terms of the GNU General Public License as published by        #
@@ -25,7 +25,7 @@
 
 include Config
 
-VER        = 2.4.27
+VER        = 2.4.28
 
 THISAPP    = httpd-$(VER)
 DL_FILE    = $(THISAPP).tar.bz2
@@ -45,7 +45,7 @@ objects = $(DL_FILE)
 
 $(DL_FILE) = $(DL_FROM)/$(DL_FILE)
 
-$(DL_FILE)_MD5 = 97b6bbfa83c866dbe20ef317e3afd108
+$(DL_FILE)_MD5 = 49007ffe8e37a0834255b279810edf24
 
 install : $(TARGET)
 
@@ -75,8 +75,6 @@ $(subst %,%_MD5,$(objects)) :
 $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        @$(PREBUILD)
        @rm -rf $(DIR_APP) && cd $(DIR_SRC) && tar jxf $(DIR_DL)/$(DL_FILE)
-       cd $(DIR_APP) && patch -Np0 -i $(DIR_SRC)/src/patches/apache-2.4.27-PR61382-fix.patch
-       cd $(DIR_APP) && patch -Np0 -i $(DIR_SRC)/src/patches/apache-2.4.27-CVE-2017-9798-fix.patch
        ### Add IPFire's layout, too
        echo "# IPFire layout" >> $(DIR_APP)/config.layout
        echo "<Layout IPFire>" >> $(DIR_APP)/config.layout
diff --git a/src/patches/apache-2.4.27-CVE-2017-9798-fix.patch b/src/patches/apache-2.4.27-CVE-2017-9798-fix.patch
deleted file mode 100644 (file)
index eb82c8b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
---- server/core.c      2017/08/16 16:50:29     1805223
-+++ server/core.c      2017/09/08 13:13:11     1807754
-@@ -2262,6 +2262,12 @@
-             /* method has not been registered yet, but resource restriction
-              * is always checked before method handling, so register it.
-              */
-+            if (cmd->pool == cmd->temp_pool) {
-+                /* In .htaccess, we can't globally register new methods. */
-+                return apr_psprintf(cmd->pool, "Could not register method '%s' "
-+                                   "for %s from .htaccess configuration",
-+                                    method, cmd->cmd->name);
-+            }
-             methnum = ap_method_register(cmd->pool,
-                                          apr_pstrdup(cmd->pool, method));
-         }
diff --git a/src/patches/apache-2.4.27-PR61382-fix.patch b/src/patches/apache-2.4.27-PR61382-fix.patch
deleted file mode 100644 (file)
index 128621a..0000000
+++ /dev/null
@@ -1,783 +0,0 @@
-Index: modules/http2/h2_bucket_beam.c
-===================================================================
---- modules/http2/h2_bucket_beam.c     (revision 1804645)
-+++ modules/http2/h2_bucket_beam.c     (working copy)
-@@ -287,7 +287,7 @@
-             /* do not count */
-         }
-         else if (APR_BUCKET_IS_FILE(b)) {
--            /* if unread, has no real mem footprint. how to test? */
-+            /* if unread, has no real mem footprint. */
-         }
-         else {
-             len += b->length;
-@@ -316,32 +316,80 @@
-     return APR_SIZE_MAX;
- }
--static apr_status_t wait_cond(h2_bucket_beam *beam, apr_thread_mutex_t *lock)
-+static int buffer_is_empty(h2_bucket_beam *beam)
- {
--    if (beam->timeout > 0) {
--        return apr_thread_cond_timedwait(beam->cond, lock, beam->timeout);
-+    return ((!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer))
-+            && H2_BLIST_EMPTY(&beam->send_list));
-+}
-+
-+static apr_status_t wait_empty(h2_bucket_beam *beam, apr_read_type_e block,  
-+                               apr_thread_mutex_t *lock)
-+{
-+    apr_status_t rv = APR_SUCCESS;
-+    
-+    while (!buffer_is_empty(beam) && APR_SUCCESS == rv) {
-+        if (APR_BLOCK_READ != block || !lock) {
-+            rv = APR_EAGAIN;
-+        }
-+        else if (beam->timeout > 0) {
-+            rv = apr_thread_cond_timedwait(beam->change, lock, beam->timeout);
-+        }
-+        else {
-+            rv = apr_thread_cond_wait(beam->change, lock);
-+        }
-     }
--    else {
--        return apr_thread_cond_wait(beam->cond, lock);
-+    return rv;
-+}
-+
-+static apr_status_t wait_not_empty(h2_bucket_beam *beam, apr_read_type_e block,  
-+                                   apr_thread_mutex_t *lock)
-+{
-+    apr_status_t rv = APR_SUCCESS;
-+    
-+    while (buffer_is_empty(beam) && APR_SUCCESS == rv) {
-+        if (beam->aborted) {
-+            rv = APR_ECONNABORTED;
-+        }
-+        else if (beam->closed) {
-+            rv = APR_EOF;
-+        }
-+        else if (APR_BLOCK_READ != block || !lock) {
-+            rv = APR_EAGAIN;
-+        }
-+        else if (beam->timeout > 0) {
-+            rv = apr_thread_cond_timedwait(beam->change, lock, beam->timeout);
-+        }
-+        else {
-+            rv = apr_thread_cond_wait(beam->change, lock);
-+        }
-     }
-+    return rv;
- }
--static apr_status_t r_wait_space(h2_bucket_beam *beam, apr_read_type_e block,
--                                 h2_beam_lock *pbl, apr_size_t *premain) 
-+static apr_status_t wait_not_full(h2_bucket_beam *beam, apr_read_type_e block, 
-+                                  apr_size_t *pspace_left, h2_beam_lock *bl)
- {
--    *premain = calc_space_left(beam);
--    while (!beam->aborted && *premain <= 0 
--           && (block == APR_BLOCK_READ) && pbl->mutex) {
--        apr_status_t status;
--        report_prod_io(beam, 1, pbl);
--        status = wait_cond(beam, pbl->mutex);
--        if (APR_STATUS_IS_TIMEUP(status)) {
--            return status;
-+    apr_status_t rv = APR_SUCCESS;
-+    apr_size_t left;
-+    
-+    while (0 == (left = calc_space_left(beam)) && APR_SUCCESS == rv) {
-+        if (beam->aborted) {
-+            rv = APR_ECONNABORTED;
-         }
--        r_purge_sent(beam);
--        *premain = calc_space_left(beam);
-+        else if (block != APR_BLOCK_READ || !bl->mutex) {
-+            rv = APR_EAGAIN;
-+        }
-+        else {
-+            if (beam->timeout > 0) {
-+                rv = apr_thread_cond_timedwait(beam->change, bl->mutex, beam->timeout);
-+            }
-+            else {
-+                rv = apr_thread_cond_wait(beam->change, bl->mutex);
-+            }
-+        }
-     }
--    return beam->aborted? APR_ECONNABORTED : APR_SUCCESS;
-+    *pspace_left = left;
-+    return rv;
- }
- static void h2_beam_emitted(h2_bucket_beam *beam, h2_beam_proxy *proxy)
-@@ -404,8 +452,8 @@
-         if (!bl.mutex) {
-             r_purge_sent(beam);
-         }
--        else if (beam->cond) {
--            apr_thread_cond_broadcast(beam->cond);
-+        else {
-+            apr_thread_cond_broadcast(beam->change);
-         }
-         leave_yellow(beam, &bl);
-     }
-@@ -425,9 +473,7 @@
- {
-     if (!beam->closed) {
-         beam->closed = 1;
--        if (beam->cond) {
--            apr_thread_cond_broadcast(beam->cond);
--        }
-+        apr_thread_cond_broadcast(beam->change);
-     }
-     return APR_SUCCESS;
- }
-@@ -582,7 +628,7 @@
-                             apr_interval_time_t timeout)
- {
-     h2_bucket_beam *beam;
--    apr_status_t status = APR_SUCCESS;
-+    apr_status_t rv = APR_SUCCESS;
-     
-     beam = apr_pcalloc(pool, sizeof(*beam));
-     if (!beam) {
-@@ -601,16 +647,15 @@
-     beam->max_buf_size = max_buf_size;
-     beam->timeout = timeout;
--    status = apr_thread_mutex_create(&beam->lock, APR_THREAD_MUTEX_DEFAULT, 
--                                     pool);
--    if (status == APR_SUCCESS) {
--        status = apr_thread_cond_create(&beam->cond, pool);
--        if (status == APR_SUCCESS) {
-+    rv = apr_thread_mutex_create(&beam->lock, APR_THREAD_MUTEX_DEFAULT, pool);
-+    if (APR_SUCCESS == rv) {
-+        rv = apr_thread_cond_create(&beam->change, pool);
-+        if (APR_SUCCESS == rv) {
-             apr_pool_pre_cleanup_register(pool, beam, beam_cleanup);
-             *pbeam = beam;
-         }
-     }
--    return status;
-+    return rv;
- }
- void h2_beam_buffer_size_set(h2_bucket_beam *beam, apr_size_t buffer_size)
-@@ -691,9 +736,7 @@
-             h2_blist_cleanup(&beam->send_list);
-             report_consumption(beam, &bl);
-         }
--        if (beam->cond) {
--            apr_thread_cond_broadcast(beam->cond);
--        }
-+        apr_thread_cond_broadcast(beam->change);
-         leave_yellow(beam, &bl);
-     }
- }
-@@ -730,18 +773,7 @@
-     h2_beam_lock bl;
-     
-     if ((status = enter_yellow(beam, &bl)) == APR_SUCCESS) {
--        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;
--            }
--            if (beam->cond) {
--                apr_thread_cond_broadcast(beam->cond);
--            }
--            status = wait_cond(beam, bl.mutex);
--        }
-+        status = wait_empty(beam, block, bl.mutex);
-         leave_yellow(beam, &bl);
-     }
-     return status;
-@@ -761,13 +793,18 @@
- static apr_status_t append_bucket(h2_bucket_beam *beam, 
-                                   apr_bucket *b,
-                                   apr_read_type_e block,
-+                                  apr_size_t *pspace_left,
-                                   h2_beam_lock *pbl)
- {
-     const char *data;
-     apr_size_t len;
--    apr_size_t space_left = 0;
-     apr_status_t status;
-+    int can_beam, check_len;
-     
-+    if (beam->aborted) {
-+        return APR_ECONNABORTED;
-+    }
-+    
-     if (APR_BUCKET_IS_METADATA(b)) {
-         if (APR_BUCKET_IS_EOS(b)) {
-             beam->closed = 1;
-@@ -777,11 +814,31 @@
-         return APR_SUCCESS;
-     }
-     else if (APR_BUCKET_IS_FILE(b)) {
--        /* file bucket lengths do not really count */
-+        /* 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
-+         * file cleanup function from the previous pool, we need to
-+         * call that only from the sender thread.
-+         *
-+         * Currently, we do not handle file bucket with refcount > 1 as
-+         * the beam is then not in complete control of the file's lifetime.
-+         * Which results in the bug that a file get closed by the receiver
-+         * while the sender or the beam still have buckets using it. 
-+         * 
-+         * Additionally, we allow callbacks to prevent beaming file
-+         * 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_bucket_file *bf = b->data;
-+        apr_file_t *fd = bf->fd;
-+        can_beam = (bf->refcount.refcount == 1);
-+        if (can_beam && beam->can_beam_fn) {
-+            can_beam = beam->can_beam_fn(beam->can_beam_ctx, beam, fd);
-+        }
-+        check_len = !can_beam;
-     }
-     else {
--        space_left = calc_space_left(beam);
--        if (space_left > 0 && b->length == ((apr_size_t)-1)) {
-+        if (b->length == ((apr_size_t)-1)) {
-             const char *data;
-             status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
-             if (status != APR_SUCCESS) {
-@@ -788,19 +845,15 @@
-                 return status;
-             }
-         }
--        
--        if (space_left <= 0) {
--            status = r_wait_space(beam, block, pbl, &space_left);
--            if (status != APR_SUCCESS) {
--                return status;
--            }
--            if (space_left <= 0) {
--                return APR_EAGAIN;
--            }
-+        check_len = 1;
-+    }
-+    
-+    if (check_len) {
-+        if (b->length > *pspace_left) {
-+            apr_bucket_split(b, *pspace_left);
-         }
--        /* space available, maybe need bucket split */
-+        *pspace_left -= b->length;
-     }
--    
-     /* The fundamental problem is that reading a sender bucket from
-      * a receiver thread is a total NO GO, because the bucket might use
-@@ -830,32 +883,8 @@
-             apr_bucket_heap_make(b, data, len, NULL);
-         }
-     }
--    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
--         * file cleanup function from the previous pool, we need to
--         * call that only from the sender thread.
--         *
--         * Currently, we do not handle file bucket with refcount > 1 as
--         * the beam is then not in complete control of the file's lifetime.
--         * Which results in the bug that a file get closed by the receiver
--         * while the sender or the beam still have buckets using it. 
--         * 
--         * Additionally, we allow callbacks to prevent beaming file
--         * 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_bucket_file *bf = b->data;
--        apr_file_t *fd = bf->fd;
--        int can_beam = (bf->refcount.refcount == 1);
--        if (can_beam && beam->can_beam_fn) {
--            can_beam = beam->can_beam_fn(beam->can_beam_ctx, beam, fd);
--        }
--        if (can_beam) {
--            status = apr_bucket_setaside(b, beam->send_pool);
--        }
--        /* else: enter ENOTIMPL case below */
-+    else if (APR_BUCKET_IS_FILE(b) && can_beam) {
-+        status = apr_bucket_setaside(b, beam->send_pool);
-     }
-     
-     if (status == APR_ENOTIMPL) {
-@@ -865,12 +894,6 @@
-          * a counter example).
-          * We do the read while in the sender thread, so that the bucket may
-          * use pools/allocators safely. */
--        if (space_left < APR_BUCKET_BUFF_SIZE) {
--            space_left = APR_BUCKET_BUFF_SIZE;
--        }
--        if (space_left < b->length) {
--            apr_bucket_split(b, space_left);
--        }
-         status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
-         if (status == APR_SUCCESS) {
-             status = apr_bucket_setaside(b, beam->send_pool);
-@@ -884,7 +907,7 @@
-     APR_BUCKET_REMOVE(b);
-     H2_BLIST_INSERT_TAIL(&beam->send_list, b);
-     beam->sent_bytes += b->length;
--    
-+
-     return APR_SUCCESS;
- }
-@@ -904,7 +927,8 @@
-                           apr_read_type_e block)
- {
-     apr_bucket *b;
--    apr_status_t status = APR_SUCCESS;
-+    apr_status_t rv = APR_SUCCESS;
-+    apr_size_t space_left = 0;
-     h2_beam_lock bl;
-     /* Called from the sender thread to add buckets to the beam */
-@@ -914,23 +938,31 @@
-         
-         if (beam->aborted) {
-             move_to_hold(beam, sender_bb);
--            status = APR_ECONNABORTED;
-+            rv = APR_ECONNABORTED;
-         }
-         else if (sender_bb) {
--            int force_report = !APR_BRIGADE_EMPTY(sender_bb); 
--            while (!APR_BRIGADE_EMPTY(sender_bb) && status == APR_SUCCESS) {
-+            int force_report = !APR_BRIGADE_EMPTY(sender_bb);
-+            
-+            space_left = calc_space_left(beam);
-+            while (!APR_BRIGADE_EMPTY(sender_bb) && APR_SUCCESS == rv) {
-+                if (space_left <= 0) {
-+                    report_prod_io(beam, force_report, &bl);
-+                    rv = wait_not_full(beam, block, &space_left, &bl);
-+                    if (APR_SUCCESS != rv) {
-+                        break;
-+                    }
-+                }
-                 b = APR_BRIGADE_FIRST(sender_bb);
--                status = append_bucket(beam, b, block, &bl);
-+                rv = append_bucket(beam, b, block, &space_left, &bl);
-             }
-+            
-             report_prod_io(beam, force_report, &bl);
--            if (beam->cond) {
--                apr_thread_cond_broadcast(beam->cond);
--            }
-+            apr_thread_cond_broadcast(beam->change);
-         }
-         report_consumption(beam, &bl);
-         leave_yellow(beam, &bl);
-     }
--    return status;
-+    return rv;
- }
- apr_status_t h2_beam_receive(h2_bucket_beam *beam, 
-@@ -942,11 +974,16 @@
-     apr_bucket *bsender, *brecv, *ng;
-     int transferred = 0;
-     apr_status_t status = APR_SUCCESS;
--    apr_off_t remain = readbytes;
-+    apr_off_t remain;
-     int transferred_buckets = 0;
-     
-     /* Called from the receiver thread to take buckets from the beam */
-     if (enter_yellow(beam, &bl) == APR_SUCCESS) {
-+        if (readbytes <= 0) {
-+            readbytes = APR_SIZE_MAX;
-+        }
-+        remain = readbytes;
-+        
- transfer:
-         if (beam->aborted) {
-             recv_buffer_cleanup(beam, &bl);
-@@ -955,11 +992,12 @@
-         }
-         /* transfer enough buckets from our receiver brigade, if we have one */
--        while (beam->recv_buffer
--               && !APR_BRIGADE_EMPTY(beam->recv_buffer)
--               && (readbytes <= 0 || remain >= 0)) {
-+        while (remain >= 0 
-+               && beam->recv_buffer 
-+               && !APR_BRIGADE_EMPTY(beam->recv_buffer)) {
-+               
-             brecv = APR_BRIGADE_FIRST(beam->recv_buffer);
--            if (readbytes > 0 && brecv->length > 0 && remain <= 0) {
-+            if (brecv->length > 0 && remain <= 0) {
-                 break;
-             }            
-             APR_BUCKET_REMOVE(brecv);
-@@ -970,11 +1008,11 @@
-         /* transfer from our sender brigade, transforming sender buckets to
-          * receiver ones until we have enough */
--        while (!H2_BLIST_EMPTY(&beam->send_list) && (readbytes <= 0 || remain >= 0)) {
--            bsender = H2_BLIST_FIRST(&beam->send_list);
-+        while (remain >= 0 && !H2_BLIST_EMPTY(&beam->send_list)) {
-+               
-             brecv = NULL;
--            
--            if (readbytes > 0 && bsender->length > 0 && remain <= 0) {
-+            bsender = H2_BLIST_FIRST(&beam->send_list);            
-+            if (bsender->length > 0 && remain <= 0) {
-                 break;
-             }
-                         
-@@ -1020,11 +1058,12 @@
-                  * been handed out. See also PR 59348 */
-                 apr_bucket_file_enable_mmap(ng, 0);
- #endif
--                remain -= bsender->length;
--                ++transferred;
-                 APR_BUCKET_REMOVE(bsender);
-                 H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender);
-+
-+                remain -= bsender->length;
-                 ++transferred;
-+                ++transferred_buckets;
-                 continue;
-             }
-             else {
-@@ -1041,6 +1080,7 @@
-              * receiver bucket references it any more. */
-             APR_BUCKET_REMOVE(bsender);
-             H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender);
-+            
-             beam->received_bytes += bsender->length;
-             ++transferred_buckets;
-             
-@@ -1063,8 +1103,8 @@
-             }
-         }
--        if (readbytes > 0 && remain < 0) {
--            /* too much, put some back */
-+        if (remain < 0) {
-+            /* too much, put some back into out recv_buffer */
-             remain = readbytes;
-             for (brecv = APR_BRIGADE_FIRST(bb);
-                  brecv != APR_BRIGADE_SENTINEL(bb);
-@@ -1081,15 +1121,7 @@
-             }
-         }
--        if (transferred_buckets > 0) {
--           if (beam->cons_ev_cb) { 
--               beam->cons_ev_cb(beam->cons_ctx, beam);
--            }
--        }
--        
--        if (beam->closed 
--            && (!beam->recv_buffer || APR_BRIGADE_EMPTY(beam->recv_buffer))
--            && H2_BLIST_EMPTY(&beam->send_list)) {
-+        if (beam->closed && buffer_is_empty(beam)) {
-             /* beam is closed and we have nothing more to receive */ 
-             if (!beam->close_sent) {
-                 apr_bucket *b = apr_bucket_eos_create(bb->bucket_alloc);
-@@ -1100,28 +1132,23 @@
-             }
-         }
-         
-+        if (transferred_buckets > 0) {
-+           if (beam->cons_ev_cb) { 
-+               beam->cons_ev_cb(beam->cons_ctx, beam);
-+            }
-+        }
-+        
-         if (transferred) {
--            if (beam->cond) {
--                apr_thread_cond_broadcast(beam->cond);
--            }
-+            apr_thread_cond_broadcast(beam->change);
-             status = APR_SUCCESS;
-         }
--        else if (beam->closed) {
--            status = APR_EOF;
--        }
--        else if (block == APR_BLOCK_READ && bl.mutex && beam->cond) {
--            status = wait_cond(beam, bl.mutex);
-+        else {
-+            status = wait_not_empty(beam, block, bl.mutex);
-             if (status != APR_SUCCESS) {
-                 goto leave;
-             }
-             goto transfer;
-         }
--        else {
--            if (beam->cond) {
--                apr_thread_cond_broadcast(beam->cond);
--            }
--            status = APR_EAGAIN;
--        }
- leave:        
-         leave_yellow(beam, &bl);
-     }
-Index: modules/http2/h2_bucket_beam.h
-===================================================================
---- modules/http2/h2_bucket_beam.h     (revision 1804645)
-+++ modules/http2/h2_bucket_beam.h     (working copy)
-@@ -190,7 +190,7 @@
-     unsigned int tx_mem_limits : 1; /* only memory size counts on transfers */
-     struct apr_thread_mutex_t *lock;
--    struct apr_thread_cond_t *cond;
-+    struct apr_thread_cond_t *change;
-     void *m_ctx;
-     h2_beam_mutex_enter *m_enter;
-     
-Index: modules/http2/h2_stream.c
-===================================================================
---- modules/http2/h2_stream.c  (revision 1804645)
-+++ modules/http2/h2_stream.c  (working copy)
-@@ -774,20 +774,20 @@
-     return NULL;
- }
--static apr_status_t add_data(h2_stream *stream, apr_off_t requested,
--                             apr_off_t *plen, int *peos, int *complete, 
--                             h2_headers **pheaders)
-+static apr_status_t add_buffered_data(h2_stream *stream, apr_off_t requested,
-+                                      apr_off_t *plen, int *peos, int *is_all, 
-+                                      h2_headers **pheaders)
- {
-     apr_bucket *b, *e;
-     
-     *peos = 0;
-     *plen = 0;
--    *complete = 0;
-+    *is_all = 0;
-     if (pheaders) {
-         *pheaders = NULL;
-     }
--    H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "add_data");
-+    H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "add_buffered_data");
-     b = APR_BRIGADE_FIRST(stream->out_buffer);
-     while (b != APR_BRIGADE_SENTINEL(stream->out_buffer)) {
-         e = APR_BUCKET_NEXT(b);
-@@ -833,7 +833,7 @@
-         }
-         b = e;
-     }
--    *complete = 1;
-+    *is_all = 1;
-     return APR_SUCCESS;
- }
-@@ -865,7 +865,7 @@
-     requested = (*plen > 0)? H2MIN(*plen, max_chunk) : max_chunk;
-     
-     /* count the buffered data until eos or a headers bucket */
--    status = add_data(stream, requested, plen, peos, &complete, pheaders);
-+    status = add_buffered_data(stream, requested, plen, peos, &complete, pheaders);
-     
-     if (status == APR_EAGAIN) {
-         /* TODO: ugly, someone needs to retrieve the response first */
-@@ -882,29 +882,39 @@
-         return APR_SUCCESS;
-     }
-     
-+    /* If there we do not have enough buffered data to satisfy the requested
-+     * length *and* we counted the _complete_ buffer (and did not stop in the middle
-+     * because of meta data there), lets see if we can read more from the
-+     * output beam */
-     missing = H2MIN(requested, stream->max_mem) - *plen;
-     if (complete && !*peos && missing > 0) {
-+        apr_status_t rv = APR_EOF;
-+        
-         if (stream->output) {
-             H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "pre");
--            status = h2_beam_receive(stream->output, stream->out_buffer, 
--                                     APR_NONBLOCK_READ, 
--                                     stream->max_mem - *plen);
-+            rv = h2_beam_receive(stream->output, stream->out_buffer, 
-+                                 APR_NONBLOCK_READ, stream->max_mem - *plen);
-             H2_STREAM_OUT_LOG(APLOG_TRACE2, stream, "post");
-         }
--        else {
--            status = APR_EOF;
-+        
-+        if (rv == APR_SUCCESS) {
-+            /* count the buffer again, now that we have read output */
-+            status = add_buffered_data(stream, requested, plen, peos, &complete, pheaders);
-         }
--        
--        if (APR_STATUS_IS_EOF(status)) {
-+        else if (APR_STATUS_IS_EOF(rv)) {
-             apr_bucket *eos = apr_bucket_eos_create(c->bucket_alloc);
-             APR_BRIGADE_INSERT_TAIL(stream->out_buffer, eos);
-             *peos = 1;
--            status = APR_SUCCESS;
-         }
--        else if (status == APR_SUCCESS) {
--            /* do it again, now that we have gotten more */
--            status = add_data(stream, requested, plen, peos, &complete, pheaders);
-+        else if (APR_STATUS_IS_EAGAIN(rv)) {
-+            /* we set this is the status of this call only if there
-+             * is no buffered data, see check below */
-         }
-+        else {
-+            /* real error reading. Give this back directly, even though
-+             * we may have something buffered. */
-+            status = rv;
-+        }
-     }
-     
-     if (status == APR_SUCCESS) {
-Index: modules/http2/h2_task.c
-===================================================================
---- modules/http2/h2_task.c    (revision 1804645)
-+++ modules/http2/h2_task.c    (working copy)
-@@ -129,7 +129,7 @@
-                               apr_bucket_brigade* bb)
- {
-     apr_bucket *b;
--    apr_status_t status = APR_SUCCESS;
-+    apr_status_t rv = APR_SUCCESS;
-     int flush = 0, blocking;
-     
-     if (task->frozen) {
-@@ -148,17 +148,16 @@
-         return APR_SUCCESS;
-     }
-+send:
-     /* we send block once we opened the output, so someone is there
-      * reading it *and* the task is not assigned to a h2_req_engine */
-     blocking = (!task->assigned && task->output.opened);
--    if (!task->output.opened) {
--        for (b = APR_BRIGADE_FIRST(bb);
--             b != APR_BRIGADE_SENTINEL(bb);
--             b = APR_BUCKET_NEXT(b)) {
--            if (APR_BUCKET_IS_FLUSH(b)) {
--                flush = 1;
--                break;
--            }
-+    for (b = APR_BRIGADE_FIRST(bb);
-+         b != APR_BRIGADE_SENTINEL(bb);
-+         b = APR_BUCKET_NEXT(b)) {
-+        if (APR_BUCKET_IS_FLUSH(b) || APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b)) {
-+            flush = 1;
-+            break;
-         }
-     }
-     
-@@ -166,32 +165,48 @@
-         /* still have data buffered from previous attempt.
-          * setaside and append new data and try to pass the complete data */
-         if (!APR_BRIGADE_EMPTY(bb)) {
--            status = ap_save_brigade(f, &task->output.bb, &bb, task->pool);
-+            if (APR_SUCCESS != (rv = ap_save_brigade(f, &task->output.bb, &bb, task->pool))) {
-+                goto out;
-+            }
-         }
--        if (status == APR_SUCCESS) {
--            status = send_out(task, task->output.bb, blocking);
--        } 
-+        rv = send_out(task, task->output.bb, blocking);
-     }
-     else {
--        /* no data buffered here, try to pass the brigade directly */
--        status = send_out(task, bb, blocking); 
--        if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(bb)) {
--            /* could not write all, buffer the rest */
--            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, task->c, APLOGNO(03405)
--                          "h2_slave_out(%s): saving brigade", 
--                          task->id);
--            status = ap_save_brigade(f, &task->output.bb, &bb, task->pool);
--            flush = 1;
-+        /* no data buffered previously, pass brigade directly */
-+        rv = send_out(task, bb, blocking);
-+
-+        if (APR_SUCCESS == rv && !APR_BRIGADE_EMPTY(bb)) {
-+            /* output refused to buffer it all, time to open? */
-+            if (!task->output.opened && APR_SUCCESS == (rv = open_output(task))) {
-+                /* Make another attempt to send the data. With the output open,
-+                 * the call might be blocking and send all data, so we do not need
-+                 * to save the brigade */
-+                goto send;
-+            }
-+            else if (blocking && flush) {
-+                /* Need to keep on doing this. */
-+                goto send;
-+            }
-+            
-+            if (APR_SUCCESS == rv) {
-+                /* could not write all, buffer the rest */
-+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, task->c, APLOGNO(03405)
-+                              "h2_slave_out(%s): saving brigade", task->id);
-+                ap_assert(NULL);
-+                rv = ap_save_brigade(f, &task->output.bb, &bb, task->pool);
-+                flush = 1;
-+            }
-         }
-     }
-     
--    if (status == APR_SUCCESS && !task->output.opened && flush) {
-+    if (APR_SUCCESS == rv && !task->output.opened && flush) {
-         /* got a flush or could not write all, time to tell someone to read */
--        status = open_output(task);
-+        rv = open_output(task);
-     }
--    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, task->c, 
-+out:
-+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, rv, task->c, 
-                   "h2_slave_out(%s): slave_out leave", task->id);    
--    return status;
-+    return rv;
- }
- static apr_status_t output_finish(h2_task *task)
-Index: modules/http2/h2_version.h
-===================================================================
---- modules/http2/h2_version.h (revision 1804645)
-+++ modules/http2/h2_version.h (working copy)
-@@ -26,7 +26,7 @@
-  * @macro
-  * Version number of the http2 module as c string
-  */
--#define MOD_HTTP2_VERSION "1.10.7"
-+#define MOD_HTTP2_VERSION "1.10.10"
- /**
-  * @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 0x010a06
-+#define MOD_HTTP2_VERSION_NUM 0x010a0a
- #endif /* mod_h2_h2_version_h */
-Index: modules/http2
-===================================================================
---- modules/http2      (revision 1804645)
-+++ modules/http2      (working copy)
-
-Property changes on: modules/http2
-___________________________________________________________________
-Modified: svn:mergeinfo
-## -0,0 +0,1 ##
-   Merged /httpd/httpd/trunk/modules/http2:r1803420,1803454,1804090
-Index: .
-===================================================================
---- .  (revision 1804645)
-+++ .  (working copy)
-
-Property changes on: .
-___________________________________________________________________
-Modified: svn:mergeinfo
-## -0,0 +0,1 ##
-   Merged /httpd/httpd/trunk:r1803420,1803454,1804090