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 2023/01/09 07:35:19 UTC

svn commit: r1906475 [9/11] - in /httpd/httpd/branches/2.4.x: ./ changes-entries/ modules/http2/ test/ test/modules/http2/ test/modules/http2/htdocs/cgi/ test/modules/http2/mod_h2test/ test/pyhttpd/ test/pyhttpd/mod_aptest/

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.c?rev=1906475&r1=1906474&r2=1906475&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.c Mon Jan  9 07:35:18 2023
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
- 
+
 #include <assert.h>
 #include <apr_strings.h>
 #include <apr_thread_mutex.h>
@@ -22,11 +22,13 @@
 #include <httpd.h>
 #include <http_core.h>
 #include <http_log.h>
+#include <http_protocol.h>
 #include <http_request.h>
 
 #include <nghttp2/nghttp2.h>
 
 #include "h2.h"
+#include "h2_headers.h"
 #include "h2_util.h"
 
 /* h2_log2(n) iff n is a power of 2 */
@@ -55,7 +57,7 @@ unsigned char h2_log2(int n)
     if (!(n & 0x80000000u)) {
         lz += 1;
     }
-    
+
     return 31 - lz;
 }
 
@@ -84,7 +86,7 @@ void h2_util_camel_case_header(char *s,
             if (s[i] >= 'a' && s[i] <= 'z') {
                 s[i] -= 'a' - 'A';
             }
-            
+
             start = 0;
         }
         else if (s[i] == '-') {
@@ -100,9 +102,9 @@ void h2_util_camel_case_header(char *s,
 static const unsigned int BASE64URL_UINT6[] = {
 /*   0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f        */
     N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /*  0 */
-    N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /*  1 */ 
+    N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, /*  1 */
     N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, N6, 62, N6, N6, /*  2 */
-    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, N6, N6, N6, N6, N6, N6, /*  3 */ 
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, N6, N6, N6, N6, N6, N6, /*  3 */
     N6, 0,  1,  2,  3,  4,  5,  6,   7,  8,  9, 10, 11, 12, 13, 14, /*  4 */
     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, N6, N6, N6, N6, 63, /*  5 */
     N6, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /*  6 */
@@ -128,7 +130,7 @@ static const unsigned char BASE64URL_CHA
 
 #define BASE64URL_CHAR(x)    BASE64URL_CHARS[ (unsigned int)(x) & 0x3fu ]
 
-apr_size_t h2_util_base64url_decode(const char **decoded, const char *encoded, 
+apr_size_t h2_util_base64url_decode(const char **decoded, const char *encoded,
                                     apr_pool_t *pool)
 {
     const unsigned char *e = (const unsigned char *)encoded;
@@ -136,14 +138,14 @@ apr_size_t h2_util_base64url_decode(cons
     unsigned char *d;
     unsigned int n;
     long len, mlen, remain, i;
-    
+
     while (*p && BASE64URL_UINT6[ *p ] != N6) {
         ++p;
     }
     len = (int)(p - e);
     mlen = (len/4)*4;
     *decoded = apr_pcalloc(pool, (apr_size_t)len + 1);
-    
+
     i = 0;
     d = (unsigned char*)*decoded;
     for (; i < mlen; i += 4) {
@@ -177,14 +179,14 @@ apr_size_t h2_util_base64url_decode(cons
     return (apr_size_t)(mlen/4*3 + remain);
 }
 
-const char *h2_util_base64url_encode(const char *data, 
+const char *h2_util_base64url_encode(const char *data,
                                      apr_size_t dlen, apr_pool_t *pool)
 {
     int i, len = (int)dlen;
     apr_size_t slen = ((dlen+2)/3)*4 + 1; /* 0 terminated */
     const unsigned char *udata = (const unsigned char*)data;
     unsigned char *enc, *p = apr_pcalloc(pool, slen);
-    
+
     enc = p;
     for (i = 0; i < len-2; i+= 3) {
         *p++ = BASE64URL_CHAR( (udata[i]   >> 2) );
@@ -192,7 +194,7 @@ const char *h2_util_base64url_encode(con
         *p++ = BASE64URL_CHAR( (udata[i+1] << 2) + (udata[i+2] >> 6) );
         *p++ = BASE64URL_CHAR( (udata[i+2]) );
     }
-    
+
     if (i < len) {
         *p++ = BASE64URL_CHAR( (udata[i] >> 2) );
         if (i == (len - 1)) {
@@ -228,7 +230,7 @@ h2_ihash_t *h2_ihash_create(apr_pool_t *
     return ih;
 }
 
-size_t h2_ihash_count(h2_ihash_t *ih)
+unsigned int h2_ihash_count(h2_ihash_t *ih)
 {
     return apr_hash_count(ih->hash);
 }
@@ -248,7 +250,7 @@ typedef struct {
     void *ctx;
 } iter_ctx;
 
-static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen, 
+static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen,
                      const void *val)
 {
     iter_ctx *ictx = ctx;
@@ -306,7 +308,7 @@ size_t h2_ihash_shift(h2_ihash_t *ih, vo
 {
     collect_ctx ctx;
     size_t i;
-    
+
     ctx.ih = ih;
     ctx.buffer = buffer;
     ctx.max = max;
@@ -324,19 +326,17 @@ size_t h2_ihash_shift(h2_ihash_t *ih, vo
 
 static void iq_grow(h2_iqueue *q, int nlen);
 static void iq_swap(h2_iqueue *q, int i, int j);
-static int iq_bubble_up(h2_iqueue *q, int i, int top, 
+static int iq_bubble_up(h2_iqueue *q, int i, int top,
                         h2_iq_cmp *cmp, void *ctx);
-static int iq_bubble_down(h2_iqueue *q, int i, int bottom, 
+static int iq_bubble_down(h2_iqueue *q, int i, int bottom,
                           h2_iq_cmp *cmp, void *ctx);
 
 h2_iqueue *h2_iq_create(apr_pool_t *pool, int capacity)
 {
     h2_iqueue *q = apr_pcalloc(pool, sizeof(h2_iqueue));
-    if (q) {
-        q->pool = pool;
-        iq_grow(q, capacity);
-        q->nelts = 0;
-    }
+    q->pool = pool;
+    iq_grow(q, capacity);
+    q->nelts = 0;
     return q;
 }
 
@@ -354,7 +354,7 @@ int h2_iq_count(h2_iqueue *q)
 int h2_iq_add(h2_iqueue *q, int sid, h2_iq_cmp *cmp, void *ctx)
 {
     int i;
-    
+
     if (h2_iq_contains(q, sid)) {
         return 0;
     }
@@ -364,7 +364,7 @@ int h2_iq_add(h2_iqueue *q, int sid, h2_
     i = (q->head + q->nelts) % q->nalloc;
     q->elts[i] = sid;
     ++q->nelts;
-    
+
     if (cmp) {
         /* bubble it to the front of the queue */
         iq_bubble_up(q, i, q->head, cmp, ctx);
@@ -385,7 +385,7 @@ int h2_iq_remove(h2_iqueue *q, int sid)
             break;
         }
     }
-    
+
     if (i < q->nelts) {
         ++i;
         for (; i < q->nelts; ++i) {
@@ -410,18 +410,18 @@ void h2_iq_sort(h2_iqueue *q, h2_iq_cmp
      */
     if (q->nelts > 0) {
         int i, ni, prev, last;
-        
+
         /* Start at the end of the queue and create a tail of sorted
          * entries. Make that tail one element longer in each iteration.
          */
         last = i = (q->head + q->nelts - 1) % q->nalloc;
         while (i != q->head) {
             prev = (q->nalloc + i - 1) % q->nalloc;
-            
+
             ni = iq_bubble_up(q, i, prev, cmp, ctx);
             if (ni == prev) {
                 /* i bubbled one up, bubble the new i down, which
-                 * keeps all tasks below i sorted. */
+                 * keeps all ints below i sorted. */
                 iq_bubble_down(q, i, last, cmp, ctx);
             }
             i = prev;
@@ -433,21 +433,21 @@ void h2_iq_sort(h2_iqueue *q, h2_iq_cmp
 int h2_iq_shift(h2_iqueue *q)
 {
     int sid;
-    
+
     if (q->nelts <= 0) {
         return 0;
     }
-    
+
     sid = q->elts[q->head];
     q->head = (q->head + 1) % q->nalloc;
     q->nelts--;
-    
+
     return sid;
 }
 
 size_t h2_iq_mshift(h2_iqueue *q, int *pint, size_t max)
 {
-    int i;
+    size_t i;
     for (i = 0; i < max; ++i) {
         pint[i] = h2_iq_shift(q);
         if (pint[i] == 0) {
@@ -463,7 +463,7 @@ static void iq_grow(h2_iqueue *q, int nl
         int *nq = apr_pcalloc(q->pool, sizeof(int) * nlen);
         if (q->nelts > 0) {
             int l = ((q->head + q->nelts) % q->nalloc) - q->head;
-            
+
             memmove(nq, q->elts + q->head, sizeof(int) * l);
             if (l < q->nelts) {
                 /* elts wrapped, append elts in [0, remain] to nq */
@@ -484,11 +484,11 @@ static void iq_swap(h2_iqueue *q, int i,
     q->elts[j] = x;
 }
 
-static int iq_bubble_up(h2_iqueue *q, int i, int top, 
-                        h2_iq_cmp *cmp, void *ctx) 
+static int iq_bubble_up(h2_iqueue *q, int i, int top,
+                        h2_iq_cmp *cmp, void *ctx)
 {
     int prev;
-    while (((prev = (q->nalloc + i - 1) % q->nalloc), i != top) 
+    while (((prev = (q->nalloc + i - 1) % q->nalloc), i != top)
            && (*cmp)(q->elts[i], q->elts[prev], ctx) < 0) {
         iq_swap(q, prev, i);
         i = prev;
@@ -496,11 +496,11 @@ static int iq_bubble_up(h2_iqueue *q, in
     return i;
 }
 
-static int iq_bubble_down(h2_iqueue *q, int i, int bottom, 
+static int iq_bubble_down(h2_iqueue *q, int i, int bottom,
                           h2_iq_cmp *cmp, void *ctx)
 {
     int next;
-    while (((next = (q->nalloc + i + 1) % q->nalloc), i != bottom) 
+    while (((next = (q->nalloc + i + 1) % q->nalloc), i != bottom)
            && (*cmp)(q->elts[i], q->elts[next], ctx) > 0) {
         iq_swap(q, next, i);
         i = next;
@@ -525,9 +525,10 @@ int h2_iq_contains(h2_iqueue *q, int sid
 
 struct h2_fifo {
     void **elems;
-    int nelems;
+    int capacity;
     int set;
-    int head;
+    int in;
+    int out;
     int count;
     int aborted;
     apr_thread_mutex_t *lock;
@@ -535,12 +536,7 @@ struct h2_fifo {
     apr_thread_cond_t  *not_full;
 };
 
-static int nth_index(h2_fifo *fifo, int n) 
-{
-    return (fifo->head + n) % fifo->nelems;
-}
-
-static apr_status_t fifo_destroy(void *data) 
+static apr_status_t fifo_destroy(void *data)
 {
     h2_fifo *fifo = data;
 
@@ -554,21 +550,21 @@ static apr_status_t fifo_destroy(void *d
 static int index_of(h2_fifo *fifo, void *elem)
 {
     int i;
-    
-    for (i = 0; i < fifo->count; ++i) {
-        if (elem == fifo->elems[nth_index(fifo, i)]) {
+
+    for (i = fifo->out; i != fifo->in; i = (i + 1) % fifo->capacity) {
+        if (elem == fifo->elems[i]) {
             return i;
         }
     }
     return -1;
 }
 
-static apr_status_t create_int(h2_fifo **pfifo, apr_pool_t *pool, 
+static apr_status_t create_int(h2_fifo **pfifo, apr_pool_t *pool,
                                int capacity, int as_set)
 {
     apr_status_t rv;
     h2_fifo *fifo;
-    
+
     fifo = apr_pcalloc(pool, sizeof(*fifo));
     if (fifo == NULL) {
         return APR_ENOMEM;
@@ -594,9 +590,9 @@ static apr_status_t create_int(h2_fifo *
     if (fifo->elems == NULL) {
         return APR_ENOMEM;
     }
-    fifo->nelems = capacity;
+    fifo->capacity = capacity;
     fifo->set = as_set;
-    
+
     *pfifo = fifo;
     apr_pool_cleanup_register(pool, fifo, fifo_destroy, apr_pool_cleanup_null);
 
@@ -627,7 +623,12 @@ apr_status_t h2_fifo_term(h2_fifo *fifo)
 
 int h2_fifo_count(h2_fifo *fifo)
 {
-    return fifo->count;
+    int n;
+
+    apr_thread_mutex_lock(fifo->lock);
+    n = fifo->count;
+    apr_thread_mutex_unlock(fifo->lock);
+    return n;
 }
 
 static apr_status_t check_not_empty(h2_fifo *fifo, int block)
@@ -654,9 +655,9 @@ static apr_status_t fifo_push_int(h2_fif
         /* set mode, elem already member */
         return APR_EEXIST;
     }
-    else if (fifo->count == fifo->nelems) {
+    else if (fifo->count == fifo->capacity) {
         if (block) {
-            while (fifo->count == fifo->nelems) {
+            while (fifo->count == fifo->capacity) {
                 if (fifo->aborted) {
                     return APR_EOF;
                 }
@@ -667,12 +668,14 @@ static apr_status_t fifo_push_int(h2_fif
             return APR_EAGAIN;
         }
     }
-    
-    ap_assert(fifo->count < fifo->nelems);
-    fifo->elems[nth_index(fifo, fifo->count)] = elem;
+
+    fifo->elems[fifo->in++] = elem;
+    if (fifo->in >= fifo->capacity) {
+        fifo->in -= fifo->capacity;
+    }
     ++fifo->count;
     if (fifo->count == 1) {
-        apr_thread_cond_broadcast(fifo->not_empty);
+        apr_thread_cond_signal(fifo->not_empty);
     }
     return APR_SUCCESS;
 }
@@ -680,7 +683,7 @@ static apr_status_t fifo_push_int(h2_fif
 static apr_status_t fifo_push(h2_fifo *fifo, void *elem, int block)
 {
     apr_status_t rv;
-    
+
     if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
         rv = fifo_push_int(fifo, elem, block);
         apr_thread_mutex_unlock(fifo->lock);
@@ -701,18 +704,20 @@ apr_status_t h2_fifo_try_push(h2_fifo *f
 static apr_status_t pull_head(h2_fifo *fifo, void **pelem, int block)
 {
     apr_status_t rv;
-    
+    int was_full;
+
     if ((rv = check_not_empty(fifo, block)) != APR_SUCCESS) {
         *pelem = NULL;
         return rv;
     }
-    *pelem = fifo->elems[fifo->head];
+    *pelem = fifo->elems[fifo->out++];
+    if (fifo->out >= fifo->capacity) {
+        fifo->out -= fifo->capacity;
+    }
+    was_full = (fifo->count == fifo->capacity);
     --fifo->count;
-    if (fifo->count > 0) {
-        fifo->head = nth_index(fifo, 1);
-        if (fifo->count+1 == fifo->nelems) {
-            apr_thread_cond_broadcast(fifo->not_full);
-        }
+    if (was_full) {
+        apr_thread_cond_broadcast(fifo->not_full);
     }
     return APR_SUCCESS;
 }
@@ -720,7 +725,7 @@ static apr_status_t pull_head(h2_fifo *f
 static apr_status_t fifo_pull(h2_fifo *fifo, void **pelem, int block)
 {
     apr_status_t rv;
-    
+
     if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
         rv = pull_head(fifo, pelem, block);
         apr_thread_mutex_unlock(fifo->lock);
@@ -742,11 +747,11 @@ static apr_status_t fifo_peek(h2_fifo *f
 {
     apr_status_t rv;
     void *elem;
-    
+
     if (fifo->aborted) {
         return APR_EOF;
     }
-    
+
     if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) {
         if (APR_SUCCESS == (rv = pull_head(fifo, &elem, block))) {
             switch (fn(elem, ctx)) {
@@ -775,28 +780,58 @@ apr_status_t h2_fifo_try_peek(h2_fifo *f
 apr_status_t h2_fifo_remove(h2_fifo *fifo, void *elem)
 {
     apr_status_t rv;
-    
+
     if (fifo->aborted) {
         return APR_EOF;
     }
 
     if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
-        int i, rc;
-        void *e;
-        
-        rc = 0;
-        for (i = 0; i < fifo->count; ++i) {
-            e = fifo->elems[nth_index(fifo, i)];
-            if (e == elem) {
-                ++rc;
-            }
-            else if (rc) {
-                fifo->elems[nth_index(fifo, i-rc)] = e;
-            }
-        }
-        if (rc) {
-            fifo->count -= rc;
-            if (fifo->count + rc == fifo->nelems) {
+        int i, last_count = fifo->count;
+
+        for (i = fifo->out; i != fifo->in; i = (i + 1) % fifo->capacity) {
+            if (fifo->elems[i] == elem) {
+                --fifo->count;
+                if (fifo->count == 0) {
+                    fifo->out = fifo->in = 0;
+                }
+                else if (i == fifo->out) {
+                    /* first element */
+                    ++fifo->out;
+                    if (fifo->out >= fifo->capacity) {
+                        fifo->out -= fifo->capacity;
+                    }
+                }
+                else if (((i + 1) % fifo->capacity) == fifo->in) {
+                    /* last element */
+                    --fifo->in;
+                    if (fifo->in < 0) {
+                        fifo->in += fifo->capacity;
+                    }
+                }
+                else if (i > fifo->out) {
+                    /* between out and in/capacity, move elements below up */
+                    memmove(&fifo->elems[fifo->out+1], &fifo->elems[fifo->out],
+                            (i - fifo->out) * sizeof(void*));
+                    ++fifo->out;
+                    if (fifo->out >= fifo->capacity) {
+                        fifo->out -= fifo->capacity;
+                    }
+                }
+                else {
+                    /* we wrapped around, move elements above down */
+                    AP_DEBUG_ASSERT((fifo->in - i - 1) > 0);
+                    AP_DEBUG_ASSERT((fifo->in - i - 1) < fifo->capacity);
+                    memmove(&fifo->elems[i], &fifo->elems[i + 1],
+                            (fifo->in - i - 1) * sizeof(void*));
+                    --fifo->in;
+                    if (fifo->in < 0) {
+                        fifo->in += fifo->capacity;
+                    }
+                }
+            }
+        }
+        if (fifo->count != last_count) {
+            if (last_count == fifo->capacity) {
                 apr_thread_cond_broadcast(fifo->not_full);
             }
             rv = APR_SUCCESS;
@@ -804,7 +839,7 @@ apr_status_t h2_fifo_remove(h2_fifo *fif
         else {
             rv = APR_EAGAIN;
         }
-        
+
         apr_thread_mutex_unlock(fifo->lock);
     }
     return rv;
@@ -816,7 +851,7 @@ apr_status_t h2_fifo_remove(h2_fifo *fif
 
 struct h2_ififo {
     int *elems;
-    int nelems;
+    int capacity;
     int set;
     int head;
     int count;
@@ -826,12 +861,12 @@ struct h2_ififo {
     apr_thread_cond_t  *not_full;
 };
 
-static int inth_index(h2_ififo *fifo, int n) 
+static int inth_index(h2_ififo *fifo, int n)
 {
-    return (fifo->head + n) % fifo->nelems;
+    return (fifo->head + n) % fifo->capacity;
 }
 
-static apr_status_t ififo_destroy(void *data) 
+static apr_status_t ififo_destroy(void *data)
 {
     h2_ififo *fifo = data;
 
@@ -845,7 +880,7 @@ static apr_status_t ififo_destroy(void *
 static int iindex_of(h2_ififo *fifo, int id)
 {
     int i;
-    
+
     for (i = 0; i < fifo->count; ++i) {
         if (id == fifo->elems[inth_index(fifo, i)]) {
             return i;
@@ -854,12 +889,12 @@ static int iindex_of(h2_ififo *fifo, int
     return -1;
 }
 
-static apr_status_t icreate_int(h2_ififo **pfifo, apr_pool_t *pool, 
+static apr_status_t icreate_int(h2_ififo **pfifo, apr_pool_t *pool,
                                 int capacity, int as_set)
 {
     apr_status_t rv;
     h2_ififo *fifo;
-    
+
     fifo = apr_pcalloc(pool, sizeof(*fifo));
     if (fifo == NULL) {
         return APR_ENOMEM;
@@ -885,9 +920,9 @@ static apr_status_t icreate_int(h2_ififo
     if (fifo->elems == NULL) {
         return APR_ENOMEM;
     }
-    fifo->nelems = capacity;
+    fifo->capacity = capacity;
     fifo->set = as_set;
-    
+
     *pfifo = fifo;
     apr_pool_cleanup_register(pool, fifo, ififo_destroy, apr_pool_cleanup_null);
 
@@ -945,9 +980,9 @@ static apr_status_t ififo_push_int(h2_if
         /* set mode, elem already member */
         return APR_EEXIST;
     }
-    else if (fifo->count == fifo->nelems) {
+    else if (fifo->count == fifo->capacity) {
         if (block) {
-            while (fifo->count == fifo->nelems) {
+            while (fifo->count == fifo->capacity) {
                 if (fifo->aborted) {
                     return APR_EOF;
                 }
@@ -958,8 +993,8 @@ static apr_status_t ififo_push_int(h2_if
             return APR_EAGAIN;
         }
     }
-    
-    ap_assert(fifo->count < fifo->nelems);
+
+    ap_assert(fifo->count < fifo->capacity);
     fifo->elems[inth_index(fifo, fifo->count)] = id;
     ++fifo->count;
     if (fifo->count == 1) {
@@ -971,7 +1006,7 @@ static apr_status_t ififo_push_int(h2_if
 static apr_status_t ififo_push(h2_ififo *fifo, int id, int block)
 {
     apr_status_t rv;
-    
+
     if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
         rv = ififo_push_int(fifo, id, block);
         apr_thread_mutex_unlock(fifo->lock);
@@ -992,7 +1027,7 @@ apr_status_t h2_ififo_try_push(h2_ififo
 static apr_status_t ipull_head(h2_ififo *fifo, int *pi, int block)
 {
     apr_status_t rv;
-    
+
     if ((rv = icheck_not_empty(fifo, block)) != APR_SUCCESS) {
         *pi = 0;
         return rv;
@@ -1001,7 +1036,7 @@ static apr_status_t ipull_head(h2_ififo
     --fifo->count;
     if (fifo->count > 0) {
         fifo->head = inth_index(fifo, 1);
-        if (fifo->count+1 == fifo->nelems) {
+        if (fifo->count+1 == fifo->capacity) {
             apr_thread_cond_broadcast(fifo->not_full);
         }
     }
@@ -1011,7 +1046,7 @@ static apr_status_t ipull_head(h2_ififo
 static apr_status_t ififo_pull(h2_ififo *fifo, int *pi, int block)
 {
     apr_status_t rv;
-    
+
     if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
         rv = ipull_head(fifo, pi, block);
         apr_thread_mutex_unlock(fifo->lock);
@@ -1033,7 +1068,7 @@ static apr_status_t ififo_peek(h2_ififo
 {
     apr_status_t rv;
     int id;
-    
+
     if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) {
         if (APR_SUCCESS == (rv = ipull_head(fifo, &id, block))) {
             switch (fn(id, ctx)) {
@@ -1062,7 +1097,7 @@ apr_status_t h2_ififo_try_peek(h2_ififo
 static apr_status_t ififo_remove(h2_ififo *fifo, int id)
 {
     int rc, i;
-    
+
     if (fifo->aborted) {
         return APR_EOF;
     }
@@ -1081,7 +1116,7 @@ static apr_status_t ififo_remove(h2_ifif
         return APR_EAGAIN;
     }
     fifo->count -= rc;
-    if (fifo->count + rc == fifo->nelems) {
+    if (fifo->count + rc == fifo->capacity) {
         apr_thread_cond_broadcast(fifo->not_full);
     }
     return APR_SUCCESS;
@@ -1090,7 +1125,7 @@ static apr_status_t ififo_remove(h2_ifif
 apr_status_t h2_ififo_remove(h2_ififo *fifo, int id)
 {
     apr_status_t rv;
-    
+
     if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
         rv = ififo_remove(fifo, id);
         apr_thread_mutex_unlock(fifo->lock);
@@ -1101,7 +1136,7 @@ apr_status_t h2_ififo_remove(h2_ififo *f
 /*******************************************************************************
  * h2_util for apt_table_t
  ******************************************************************************/
- 
+
 typedef struct {
     apr_size_t bytes;
     apr_size_t pair_extra;
@@ -1123,7 +1158,7 @@ static int count_bytes(void *x, const ch
 apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra)
 {
     table_bytes_ctx ctx;
-    
+
     ctx.bytes = 0;
     ctx.pair_extra = pair_extra;
     apr_table_do(count_bytes, &ctx, t, NULL);
@@ -1135,287 +1170,108 @@ apr_size_t h2_util_table_bytes(apr_table
  * h2_util for bucket brigades
  ******************************************************************************/
 
-static apr_status_t last_not_included(apr_bucket_brigade *bb, 
-                                      apr_off_t maxlen, 
-                                      int same_alloc,
-                                      apr_size_t *pfile_buckets_allowed,
-                                      apr_bucket **pend)
+static void fit_bucket_into(apr_bucket *b, apr_off_t *plen)
 {
-    apr_bucket *b;
-    apr_status_t status = APR_SUCCESS;
-    int files_allowed = pfile_buckets_allowed? (int)*pfile_buckets_allowed : 0;
-    
-    if (maxlen >= 0) {
-        /* Find the bucket, up to which we reach maxlen/mem bytes */
-        for (b = APR_BRIGADE_FIRST(bb); 
-             (b != APR_BRIGADE_SENTINEL(bb));
-             b = APR_BUCKET_NEXT(b)) {
-            
-            if (APR_BUCKET_IS_METADATA(b)) {
-                /* included */
-            }
-            else {
-                if (b->length == ((apr_size_t)-1)) {
-                    const char *ign;
-                    apr_size_t ilen;
-                    status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
-                    if (status != APR_SUCCESS) {
-                        return status;
-                    }
-                }
-                
-                if (maxlen == 0 && b->length > 0) {
-                    *pend = b;
-                    return status;
-                }
-                
-                if (same_alloc && APR_BUCKET_IS_FILE(b)) {
-                    /* we like it move it, always */
-                }
-                else if (files_allowed > 0 && APR_BUCKET_IS_FILE(b)) {
-                    /* this has no memory footprint really unless
-                     * it is read, disregard it in length count,
-                     * unless we do not move the file buckets */
-                    --files_allowed;
-                }
-                else if (maxlen < (apr_off_t)b->length) {
-                    apr_bucket_split(b, (apr_size_t)maxlen);
-                    maxlen = 0;
-                }
-                else {
-                    maxlen -= b->length;
-                }
-            }
-        }
+    /* signed apr_off_t is at least as large as unsigned apr_size_t.
+     * Problems may arise when they are both the same size. Then
+     * the bucket length *may* be larger than a value we can hold
+     * in apr_off_t. Before casting b->length to apr_off_t we must
+     * check the limitations.
+     * After we resized the bucket, it is safe to cast and substract.
+     */
+    if ((sizeof(apr_off_t) == sizeof(apr_int64_t)
+         && b->length > APR_INT64_MAX)
+       || (sizeof(apr_off_t) == sizeof(apr_int32_t)
+           && b->length > APR_INT32_MAX)
+       || *plen < (apr_off_t)b->length) {
+        /* bucket is longer the *plen */
+        apr_bucket_split(b, *plen);
     }
-    *pend = APR_BRIGADE_SENTINEL(bb);
-    return status;
+    *plen -= (apr_off_t)b->length;
 }
 
-apr_status_t h2_brigade_concat_length(apr_bucket_brigade *dest, 
+apr_status_t h2_brigade_concat_length(apr_bucket_brigade *dest,
                                       apr_bucket_brigade *src,
                                       apr_off_t length)
 {
     apr_bucket *b;
     apr_off_t remain = length;
     apr_status_t status = APR_SUCCESS;
-    
+
     while (!APR_BRIGADE_EMPTY(src)) {
-        b = APR_BRIGADE_FIRST(src); 
-        
+        b = APR_BRIGADE_FIRST(src);
+
         if (APR_BUCKET_IS_METADATA(b)) {
             APR_BUCKET_REMOVE(b);
             APR_BRIGADE_INSERT_TAIL(dest, b);
         }
         else {
-            if (remain == b->length) {
-                /* fall through */
-            }
-            else if (remain <= 0) {
+            if (remain <= 0) {
                 return status;
             }
-            else {
-                if (b->length == ((apr_size_t)-1)) {
-                    const char *ign;
-                    apr_size_t ilen;
-                    status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
-                    if (status != APR_SUCCESS) {
-                        return status;
-                    }
-                }
-            
-                if (remain < b->length) {
-                    apr_bucket_split(b, remain);
+            if (b->length == ((apr_size_t)-1)) {
+                const char *ign;
+                apr_size_t ilen;
+                status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
+                if (status != APR_SUCCESS) {
+                    return status;
                 }
             }
+            fit_bucket_into(b, &remain);
             APR_BUCKET_REMOVE(b);
             APR_BRIGADE_INSERT_TAIL(dest, b);
-            remain -= b->length;
         }
     }
     return status;
 }
 
-apr_status_t h2_brigade_copy_length(apr_bucket_brigade *dest, 
+apr_status_t h2_brigade_copy_length(apr_bucket_brigade *dest,
                                     apr_bucket_brigade *src,
                                     apr_off_t length)
 {
     apr_bucket *b, *next;
     apr_off_t remain = length;
     apr_status_t status = APR_SUCCESS;
-    
-    for (b = APR_BRIGADE_FIRST(src); 
+
+    for (b = APR_BRIGADE_FIRST(src);
          b != APR_BRIGADE_SENTINEL(src);
          b = next) {
         next = APR_BUCKET_NEXT(b);
-        
+
         if (APR_BUCKET_IS_METADATA(b)) {
             /* fall through */
         }
         else {
-            if (remain == b->length) {
-                /* fall through */
-            }
-            else if (remain <= 0) {
+            if (remain <= 0) {
                 return status;
             }
-            else {
-                if (b->length == ((apr_size_t)-1)) {
-                    const char *ign;
-                    apr_size_t ilen;
-                    status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
-                    if (status != APR_SUCCESS) {
-                        return status;
-                    }
-                }
-            
-                if (remain < b->length) {
-                    apr_bucket_split(b, remain);
+            if (b->length == ((apr_size_t)-1)) {
+                const char *ign;
+                apr_size_t ilen;
+                status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
+                if (status != APR_SUCCESS) {
+                    return status;
                 }
             }
+            fit_bucket_into(b, &remain);
         }
         status = apr_bucket_copy(b, &b);
         if (status != APR_SUCCESS) {
             return status;
         }
         APR_BRIGADE_INSERT_TAIL(dest, b);
-        remain -= b->length;
     }
     return status;
 }
 
-int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len)
-{
-    apr_bucket *b, *end;
-    
-    apr_status_t status = last_not_included(bb, len, 0, 0, &end);
-    if (status != APR_SUCCESS) {
-        return status;
-    }
-    
-    for (b = APR_BRIGADE_FIRST(bb);
-         b != APR_BRIGADE_SENTINEL(bb) && b != end;
-         b = APR_BUCKET_NEXT(b))
-    {
-        if (APR_BUCKET_IS_EOS(b)) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, 
-                              apr_off_t *plen, int *peos)
-{
-    apr_status_t status;
-    apr_off_t blen = 0;
-
-    /* test read to determine available length */
-    status = apr_brigade_length(bb, 1, &blen);
-    if (status != APR_SUCCESS) {
-        return status;
-    }
-    else if (blen == 0) {
-        /* brigade without data, does it have an EOS bucket somewhere? */
-        *plen = 0;
-        *peos = h2_util_has_eos(bb, -1);
-    }
-    else {
-        /* data in the brigade, limit the length returned. Check for EOS
-         * bucket only if we indicate data. This is required since plen == 0
-         * means "the whole brigade" for h2_util_hash_eos()
-         */
-        if (blen < *plen || *plen < 0) {
-            *plen = blen;
-        }
-        *peos = h2_util_has_eos(bb, *plen);
-    }
-    return APR_SUCCESS;
-}
-
-apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, 
-                              h2_util_pass_cb *cb, void *ctx, 
-                              apr_off_t *plen, int *peos)
-{
-    apr_status_t status = APR_SUCCESS;
-    int consume = (cb != NULL);
-    apr_off_t written = 0;
-    apr_off_t avail = *plen;
-    apr_bucket *next, *b;
-    
-    /* Pass data in our brigade through the callback until the length
-     * is satisfied or we encounter an EOS.
-     */
-    *peos = 0;
-    for (b = APR_BRIGADE_FIRST(bb);
-         (status == APR_SUCCESS) && (b != APR_BRIGADE_SENTINEL(bb));
-         b = next) {
-        
-        if (APR_BUCKET_IS_METADATA(b)) {
-            if (APR_BUCKET_IS_EOS(b)) {
-                *peos = 1;
-            }
-            else {
-                /* ignore */
-            }
-        }
-        else if (avail <= 0) {
-            break;
-        } 
-        else {
-            const char *data = NULL;
-            apr_size_t data_len;
-            
-            if (b->length == ((apr_size_t)-1)) {
-                /* read to determine length */
-                status = apr_bucket_read(b, &data, &data_len, APR_NONBLOCK_READ);
-            }
-            else {
-                data_len = b->length;
-            }
-            
-            if (data_len > avail) {
-                apr_bucket_split(b, avail);
-                data_len = (apr_size_t)avail;
-            }
-            
-            if (consume) {
-                if (!data) {
-                    status = apr_bucket_read(b, &data, &data_len, 
-                                             APR_NONBLOCK_READ);
-                }
-                if (status == APR_SUCCESS) {
-                    status = cb(ctx, data, data_len);
-                }
-            }
-            else {
-                data_len = b->length;
-            }
-            avail -= data_len;
-            written += data_len;
-        }
-        
-        next = APR_BUCKET_NEXT(b);
-        if (consume) {
-            apr_bucket_delete(b);
-        }
-    }
-    
-    *plen = written;
-    if (status == APR_SUCCESS && !*peos && !*plen) {
-        return APR_EAGAIN;
-    }
-    return status;
-}
-
-apr_size_t h2_util_bucket_print(char *buffer, apr_size_t bmax, 
+apr_size_t h2_util_bucket_print(char *buffer, apr_size_t bmax,
                                 apr_bucket *b, const char *sep)
 {
     apr_size_t off = 0;
     if (sep && *sep) {
         off += apr_snprintf(buffer+off, bmax-off, "%s", sep);
     }
-    
+
     if (bmax <= off) {
         return off;
     }
@@ -1423,30 +1279,30 @@ apr_size_t h2_util_bucket_print(char *bu
         off += apr_snprintf(buffer+off, bmax-off, "%s", b->type->name);
     }
     else if (bmax > off) {
-        off += apr_snprintf(buffer+off, bmax-off, "%s[%ld]", 
-                            b->type->name, 
-                            (long)(b->length == ((apr_size_t)-1)? 
+        off += apr_snprintf(buffer+off, bmax-off, "%s[%ld]",
+                            b->type->name,
+                            (long)(b->length == ((apr_size_t)-1)?
                                    -1 : b->length));
     }
     return off;
 }
 
-apr_size_t h2_util_bb_print(char *buffer, apr_size_t bmax, 
-                            const char *tag, const char *sep, 
+apr_size_t h2_util_bb_print(char *buffer, apr_size_t bmax,
+                            const char *tag, const char *sep,
                             apr_bucket_brigade *bb)
 {
     apr_size_t off = 0;
     const char *sp = "";
     apr_bucket *b;
-    
+
     if (bmax > 1) {
         if (bb) {
             memset(buffer, 0, bmax--);
             off += apr_snprintf(buffer+off, bmax-off, "%s(", tag);
-            for (b = APR_BRIGADE_FIRST(bb); 
+            for (b = APR_BRIGADE_FIRST(bb);
                  (bmax > off) && (b != APR_BRIGADE_SENTINEL(bb));
                  b = APR_BUCKET_NEXT(b)) {
-                
+
                 off += h2_util_bucket_print(buffer+off, bmax-off, b, sp);
                 sp = " ";
             }
@@ -1462,20 +1318,21 @@ apr_size_t h2_util_bb_print(char *buffer
 }
 
 apr_status_t h2_append_brigade(apr_bucket_brigade *to,
-                               apr_bucket_brigade *from, 
+                               apr_bucket_brigade *from,
                                apr_off_t *plen,
                                int *peos,
                                h2_bucket_gate *should_append)
 {
     apr_bucket *e;
-    apr_off_t len = 0, remain = *plen;
+    apr_off_t start, remain;
     apr_status_t rv;
 
     *peos = 0;
-    
+    start = remain = *plen;
+
     while (!APR_BRIGADE_EMPTY(from)) {
         e = APR_BRIGADE_FIRST(from);
-        
+
         if (!should_append(e)) {
             goto leave;
         }
@@ -1486,8 +1343,11 @@ apr_status_t h2_append_brigade(apr_bucke
                 continue;
             }
         }
-        else {        
-            if (remain > 0 && e->length == ((apr_size_t)-1)) {
+        else {
+            if (remain <= 0) {
+                goto leave;
+            }
+            if (e->length == ((apr_size_t)-1)) {
                 const char *ign;
                 apr_size_t ilen;
                 rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ);
@@ -1495,22 +1355,13 @@ apr_status_t h2_append_brigade(apr_bucke
                     return rv;
                 }
             }
-            
-            if (remain < e->length) {
-                if (remain <= 0) {
-                    goto leave;
-                }
-                apr_bucket_split(e, (apr_size_t)remain);
-            }
+            fit_bucket_into(e, &remain);
         }
-        
         APR_BUCKET_REMOVE(e);
         APR_BRIGADE_INSERT_TAIL(to, e);
-        len += e->length;
-        remain -= e->length;
     }
 leave:
-    *plen = len;
+    *plen = start - remain;
     return APR_SUCCESS;
 }
 
@@ -1538,20 +1389,10 @@ apr_off_t h2_brigade_mem_size(apr_bucket
 /*******************************************************************************
  * h2_ngheader
  ******************************************************************************/
- 
-int h2_util_ignore_header(const char *name) 
-{
-    /* never forward, ch. 8.1.2.2 */
-    return (H2_HD_MATCH_LIT_CS("connection", name)
-            || H2_HD_MATCH_LIT_CS("proxy-connection", name)
-            || H2_HD_MATCH_LIT_CS("upgrade", name)
-            || H2_HD_MATCH_LIT_CS("keep-alive", name)
-            || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
-}
 
 static int count_header(void *ctx, const char *key, const char *value)
 {
-    if (!h2_util_ignore_header(key)) {
+    if (!h2_util_ignore_resp_header(key)) {
         (*((size_t*)ctx))++;
     }
     return 1;
@@ -1572,6 +1413,17 @@ static const char *inv_field_value_chr(c
     return (p && *p)? p : NULL;
 }
 
+static void strip_field_value_ws(nghttp2_nv *nv)
+{
+    while(nv->valuelen && (nv->value[0] == ' ' || nv->value[0] == '\t')) {
+        nv->value++; nv->valuelen--;
+    }
+    while(nv->valuelen && (nv->value[nv->valuelen-1] == ' '
+                           || nv->value[nv->valuelen-1] == '\t')) {
+        nv->valuelen--;
+    }
+}
+
 typedef struct ngh_ctx {
     apr_pool_t *p;
     int unsafe;
@@ -1587,14 +1439,14 @@ static int add_header(ngh_ctx *ctx, cons
     if (!ctx->unsafe) {
         if ((p = inv_field_name_chr(key))) {
             ap_log_perror(APLOG_MARK, APLOG_TRACE1, APR_EINVAL, ctx->p,
-                          "h2_request: head field '%s: %s' has invalid char %s", 
+                          "h2_request: head field '%s: %s' has invalid char %s",
                           key, value, p);
             ctx->status = APR_EINVAL;
             return 0;
         }
         if ((p = inv_field_value_chr(value))) {
             ap_log_perror(APLOG_MARK, APLOG_TRACE1, APR_EINVAL, ctx->p,
-                          "h2_request: head field '%s: %s' has invalid char %s", 
+                          "h2_request: head field '%s: %s' has invalid char %s",
                           key, value, p);
             ctx->status = APR_EINVAL;
             return 0;
@@ -1604,69 +1456,100 @@ static int add_header(ngh_ctx *ctx, cons
     nv->namelen = strlen(key);
     nv->value = (uint8_t*)value;
     nv->valuelen = strlen(value);
-    
+    strip_field_value_ws(nv);
+
     return 1;
 }
 
 static int add_table_header(void *ctx, const char *key, const char *value)
 {
-    if (!h2_util_ignore_header(key)) {
+    if (!h2_util_ignore_resp_header(key)) {
         add_header(ctx, key, value);
     }
     return 1;
 }
 
-static apr_status_t ngheader_create(h2_ngheader **ph, apr_pool_t *p, 
-                                    int unsafe, size_t key_count, 
+static apr_status_t ngheader_create(h2_ngheader **ph, apr_pool_t *p,
+                                    int unsafe, size_t key_count,
                                     const char *keys[], const char *values[],
                                     apr_table_t *headers)
 {
     ngh_ctx ctx;
     size_t n, i;
-    
+
     ctx.p = p;
     ctx.unsafe = unsafe;
-    
+
     n = key_count;
     apr_table_do(count_header, &n, headers, NULL);
-    
+
     *ph = ctx.ngh = apr_pcalloc(p, sizeof(h2_ngheader));
     if (!ctx.ngh) {
         return APR_ENOMEM;
     }
-    
-    ctx.ngh->nv =  apr_pcalloc(p, n * sizeof(nghttp2_nv));
+
+    ctx.ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
     if (!ctx.ngh->nv) {
         return APR_ENOMEM;
     }
-    
+
     ctx.status = APR_SUCCESS;
     for (i = 0; i < key_count; ++i) {
         if (!add_header(&ctx, keys[i], values[i])) {
             return ctx.status;
         }
     }
-    
+
     apr_table_do(add_table_header, &ctx, headers, NULL);
 
     return ctx.status;
 }
 
+#if AP_HAS_RESPONSE_BUCKETS
+
+static int is_unsafe(ap_bucket_response *h)
+{
+    const char *v = h->notes? apr_table_get(h->notes, H2_HDR_CONFORMANCE) : NULL;
+    return (v && !strcmp(v, H2_HDR_CONFORMANCE_UNSAFE));
+}
+
+apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p,
+                                    ap_bucket_headers *headers)
+{
+    return ngheader_create(ph, p, 0,
+                           0, NULL, NULL, headers->headers);
+}
+
+apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
+                                    ap_bucket_response *response)
+{
+    const char *keys[] = {
+        ":status"
+    };
+    const char *values[] = {
+        apr_psprintf(p, "%d", response->status)
+    };
+    return ngheader_create(ph, p, is_unsafe(response),
+                           H2_ALEN(keys), keys, values, response->headers);
+}
+
+#else /* AP_HAS_RESPONSE_BUCKETS */
+
 static int is_unsafe(h2_headers *h)
 {
-    const char *v = apr_table_get(h->notes, H2_HDR_CONFORMANCE);
+    const char *v = h->notes? apr_table_get(h->notes, H2_HDR_CONFORMANCE) : NULL;
     return (v && !strcmp(v, H2_HDR_CONFORMANCE_UNSAFE));
 }
 
-apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p, 
+apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p,
                                     h2_headers *headers)
 {
-    return ngheader_create(ph, p, is_unsafe(headers), 
+    return ngheader_create(ph, p, is_unsafe(headers),
                            0, NULL, NULL, headers->headers);
 }
-                                     
+
 apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
-                                    h2_headers *headers) 
+                                    h2_headers *headers)
 {
     const char *keys[] = {
         ":status"
@@ -1674,27 +1557,29 @@ apr_status_t h2_res_create_ngheader(h2_n
     const char *values[] = {
         apr_psprintf(p, "%d", headers->status)
     };
-    return ngheader_create(ph, p, is_unsafe(headers),  
+    return ngheader_create(ph, p, is_unsafe(headers),
                            H2_ALEN(keys), keys, values, headers->headers);
 }
 
-apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p, 
+#endif /* else AP_HAS_RESPONSE_BUCKETS */
+
+apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
                                     const struct h2_request *req)
 {
-    
+
     const char *keys[] = {
-        ":scheme", 
-        ":authority", 
-        ":path", 
-        ":method", 
+        ":scheme",
+        ":authority",
+        ":path",
+        ":method",
     };
     const char *values[] = {
         req->scheme,
-        req->authority, 
-        req->path, 
-        req->method, 
+        req->authority,
+        req->path,
+        req->method,
     };
-    
+
     ap_assert(req->scheme);
     ap_assert(req->authority);
     ap_assert(req->path);
@@ -1706,7 +1591,7 @@ apr_status_t h2_req_create_ngheader(h2_n
 /*******************************************************************************
  * header HTTP/1 <-> HTTP/2 conversions
  ******************************************************************************/
- 
+
 
 typedef struct {
     const char *name;
@@ -1734,9 +1619,15 @@ static literal IgnoredRequestTrailers[]
     H2_DEF_LITERAL("max-forwards"),
     H2_DEF_LITERAL("cache-control"),
     H2_DEF_LITERAL("authorization"),
-    H2_DEF_LITERAL("content-length"),       
+    H2_DEF_LITERAL("content-length"),
     H2_DEF_LITERAL("proxy-authorization"),
-};    
+};
+static literal IgnoredResponseHeaders[] = {
+    H2_DEF_LITERAL("upgrade"),
+    H2_DEF_LITERAL("connection"),
+    H2_DEF_LITERAL("keep-alive"),
+    H2_DEF_LITERAL("transfer-encoding"),
+};
 static literal IgnoredResponseTrailers[] = {
     H2_DEF_LITERAL("age"),
     H2_DEF_LITERAL("date"),
@@ -1751,108 +1642,124 @@ static literal IgnoredResponseTrailers[]
     H2_DEF_LITERAL("proxy-authenticate"),
 };
 
-static int ignore_header(const literal *lits, size_t llen,
-                         const char *name, size_t nlen)
+static int contains_name(const literal *lits, size_t llen, nghttp2_nv *nv)
 {
     const literal *lit;
     size_t i;
-    
+
     for (i = 0; i < llen; ++i) {
         lit = &lits[i];
-        if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
+        if (lit->len == nv->namelen
+            && !apr_strnatcasecmp(lit->name, (const char *)nv->name)) {
             return 1;
         }
     }
     return 0;
 }
 
-int h2_req_ignore_header(const char *name, size_t len)
+int h2_util_ignore_resp_header(const char *name)
 {
-    return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len);
+    nghttp2_nv nv;
+
+    nv.name = (uint8_t*)name;
+    nv.namelen = strlen(name);
+    return contains_name(H2_LIT_ARGS(IgnoredResponseHeaders), &nv);
 }
 
-int h2_req_ignore_trailer(const char *name, size_t len)
+
+static int h2_req_ignore_header(nghttp2_nv *nv)
 {
-    return (h2_req_ignore_header(name, len) 
-            || ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len));
+    return contains_name(H2_LIT_ARGS(IgnoredRequestHeaders), nv);
 }
 
-int h2_res_ignore_trailer(const char *name, size_t len)
+int h2_ignore_req_trailer(const char *name, size_t len)
 {
-    return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
+    nghttp2_nv nv;
+
+    nv.name = (uint8_t*)name;
+    nv.namelen = strlen(name);
+    return (h2_req_ignore_header(&nv)
+            || contains_name(H2_LIT_ARGS(IgnoredRequestTrailers), &nv));
 }
 
-apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool, 
-                              const char *name, size_t nlen,
-                              const char *value, size_t vlen,
-                              size_t max_field_len, int *pwas_added)
+int h2_ignore_resp_trailer(const char *name, size_t len)
+{
+    nghttp2_nv nv;
+
+    nv.name = (uint8_t*)name;
+    nv.namelen = strlen(name);
+    return (contains_name(H2_LIT_ARGS(IgnoredResponseHeaders), &nv)
+            || contains_name(H2_LIT_ARGS(IgnoredResponseTrailers), &nv));
+}
+
+static apr_status_t req_add_header(apr_table_t *headers, apr_pool_t *pool,
+                                   nghttp2_nv *nv, size_t max_field_len,
+                                   int *pwas_added)
 {
     char *hname, *hvalue;
     const char *existing;
-    
+
     *pwas_added = 0;
-    if (h2_req_ignore_header(name, nlen)) {
+    strip_field_value_ws(nv);
+
+    if (h2_req_ignore_header(nv)) {
         return APR_SUCCESS;
     }
-    else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
+    else if (nv->namelen == sizeof("cookie")-1
+             && !apr_strnatcasecmp("cookie", (const char *)nv->name)) {
         existing = apr_table_get(headers, "cookie");
         if (existing) {
-            char *nval;
-            
             /* Cookie header come separately in HTTP/2, but need
              * to be merged by "; " (instead of default ", ")
              */
-            if (max_field_len && strlen(existing) + vlen + nlen + 4 > max_field_len) {
+            if (max_field_len
+                && strlen(existing) + nv->valuelen + nv->namelen + 4
+                   > max_field_len) {
                 /* "key: oldval, nval" is too long */
                 return APR_EINVAL;
             }
-            hvalue = apr_pstrndup(pool, value, vlen);
-            nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
-            apr_table_setn(headers, "Cookie", nval);
+            hvalue = apr_pstrndup(pool, (const char*)nv->value, nv->valuelen);
+            apr_table_setn(headers, "Cookie",
+                           apr_psprintf(pool, "%s; %s", existing, hvalue));
             return APR_SUCCESS;
         }
     }
-    else if (H2_HD_MATCH_LIT("host", name, nlen)) {
+    else if (nv->namelen == sizeof("host")-1
+             && !apr_strnatcasecmp("host", (const char *)nv->name)) {
         if (apr_table_get(headers, "Host")) {
             return APR_SUCCESS; /* ignore duplicate */
         }
     }
-    
-    hname = apr_pstrndup(pool, name, nlen);
-    h2_util_camel_case_header(hname, nlen);
+
+    hname = apr_pstrndup(pool, (const char*)nv->name, nv->namelen);
+    h2_util_camel_case_header(hname, nv->namelen);
     existing = apr_table_get(headers, hname);
     if (max_field_len) {
-        if ((existing? strlen(existing)+2 : 0) + vlen + nlen + 2 > max_field_len) {
+        if ((existing? strlen(existing)+2 : 0) + nv->valuelen + nv->namelen + 2
+            > max_field_len) {
             /* "key: (oldval, )?nval" is too long */
             return APR_EINVAL;
         }
     }
     if (!existing) *pwas_added = 1;
-    hvalue = apr_pstrndup(pool, value, vlen);
+    hvalue = apr_pstrndup(pool, (const char*)nv->value, nv->valuelen);
     apr_table_mergen(headers, hname, hvalue);
-    
+
     return APR_SUCCESS;
 }
 
-/*******************************************************************************
- * h2 request handling
- ******************************************************************************/
+apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool,
+                              const char *name, size_t nlen,
+                              const char *value, size_t vlen,
+                              size_t max_field_len, int *pwas_added)
+{
+    nghttp2_nv nv;
 
-h2_request *h2_req_create(int id, apr_pool_t *pool, const char *method, 
-                          const char *scheme, const char *authority, 
-                          const char *path, apr_table_t *header, int serialize)
-{
-    h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
-    
-    req->method         = method;
-    req->scheme         = scheme;
-    req->authority      = authority;
-    req->path           = path;
-    req->headers        = header? header : apr_table_make(pool, 10);
-    req->request_time   = apr_time_now();
-    req->serialize      = serialize;
-    
-    return req;
+    nv.name = (uint8_t*)name;
+    nv.namelen = nlen;
+    nv.value = (uint8_t*)value;
+    nv.valuelen = vlen;
+    return req_add_header(headers, pool, &nv, max_field_len, pwas_added);
 }
 
 /*******************************************************************************
@@ -1863,7 +1770,7 @@ int h2_util_frame_print(const nghttp2_fr
 {
     char scratch[128];
     size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
-    
+
     switch (frame->hd.type) {
         case NGHTTP2_DATA: {
             return apr_snprintf(buffer, maxlen,
@@ -1922,13 +1829,13 @@ int h2_util_frame_print(const nghttp2_fr
                 memcpy(scratch, frame->goaway.opaque_data, len);
             scratch[len] = '\0';
             return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s', "
-                                "last_stream=%d]", frame->goaway.error_code, 
+                                "last_stream=%d]", frame->goaway.error_code,
                                 scratch, frame->goaway.last_stream_id);
         }
         case NGHTTP2_WINDOW_UPDATE: {
             return apr_snprintf(buffer, maxlen,
                                 "WINDOW_UPDATE[stream=%d, incr=%d]",
-                                frame->hd.stream_id, 
+                                frame->hd.stream_id,
                                 frame->window_update.window_size_increment);
         }
         default:
@@ -1972,3 +1879,51 @@ int h2_push_policy_determine(apr_table_t
     return policy;
 }
 
+void h2_util_drain_pipe(apr_file_t *pipe)
+{
+    char rb[512];
+    apr_size_t nr = sizeof(rb);
+
+    while (apr_file_read(pipe, rb, &nr) == APR_SUCCESS) {
+        /* Although we write just one byte to the other end of the pipe
+         * during wakeup, multiple threads could call the wakeup.
+         * So simply drain out from the input side of the pipe all
+         * the data.
+         */
+        if (nr != sizeof(rb))
+            break;
+    }
+}
+
+apr_status_t h2_util_wait_on_pipe(apr_file_t *pipe)
+{
+    char rb[512];
+    apr_size_t nr = sizeof(rb);
+
+    return apr_file_read(pipe, rb, &nr);
+}
+
+#if AP_HAS_RESPONSE_BUCKETS
+
+static int add_header_lengths(void *ctx, const char *name, const char *value)
+{
+    apr_size_t *plen = ctx;
+    *plen += strlen(name) + strlen(value);
+    return 1;
+}
+
+apr_size_t headers_length_estimate(ap_bucket_headers *hdrs)
+{
+    apr_size_t len = 0;
+    apr_table_do(add_header_lengths, &len, hdrs->headers, NULL);
+    return len;
+}
+
+apr_size_t response_length_estimate(ap_bucket_response *resp)
+{
+    apr_size_t len = 3 + 1 + 8 + (resp->reason? strlen(resp->reason) : 10);
+    apr_table_do(add_header_lengths, &len, resp->headers, NULL);
+    return len;
+}
+
+#endif /* AP_HAS_RESPONSE_BUCKETS */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.h?rev=1906475&r1=1906474&r2=1906475&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.h Mon Jan  9 07:35:18 2023
@@ -18,6 +18,10 @@
 #define __mod_h2__h2_util__
 
 #include <nghttp2/nghttp2.h>
+#include <http_protocol.h>
+
+#include "h2.h"
+#include "h2_headers.h"
 
 /*******************************************************************************
  * some debugging/format helpers
@@ -45,7 +49,7 @@ typedef int h2_ihash_iter_t(void *ctx, v
  */
 h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int);
 
-size_t h2_ihash_count(h2_ihash_t *ih);
+unsigned int h2_ihash_count(h2_ihash_t *ih);
 int h2_ihash_empty(h2_ihash_t *ih);
 void *h2_ihash_get(h2_ihash_t *ih, int id);
 
@@ -98,7 +102,7 @@ typedef int h2_iq_cmp(int i1, int i2, vo
 h2_iqueue *h2_iq_create(apr_pool_t *pool, int capacity);
 
 /**
- * Return != 0 iff there are no tasks in the queue.
+ * Return != 0 iff there are no ints in the queue.
  * @param q the queue to check
  */
 int h2_iq_empty(h2_iqueue *q);
@@ -130,11 +134,10 @@ int h2_iq_add(h2_iqueue *q, int sid, h2_
 int h2_iq_append(h2_iqueue *q, int sid);
 
 /**
- * Remove the stream id from the queue. Return != 0 iff task
- * was found in queue.
- * @param q the task queue
+ * Remove the int from the queue. Return != 0 iff it was found.
+ * @param q the queue
  * @param sid the stream id to remove
- * @return != 0 iff task was found in queue
+ * @return != 0 iff int was found in queue
  */
 int h2_iq_remove(h2_iqueue *q, int sid);
 
@@ -144,7 +147,7 @@ int h2_iq_remove(h2_iqueue *q, int sid);
 void h2_iq_clear(h2_iqueue *q);
 
 /**
- * Sort the stream idqueue again. Call if the task ordering
+ * Sort the stream idqueue again. Call if the int ordering
  * has changed.
  *
  * @param q the queue to sort
@@ -165,7 +168,7 @@ int h2_iq_shift(h2_iqueue *q);
 /**
  * Get the first max ids from the queue. All these ids will be removed.
  *
- * @param q the queue to get the first task from
+ * @param q the queue to get the first ids from
  * @param pint the int array to receive the values
  * @param max the maximum number of ids to shift
  * @return the actual number of ids shifted
@@ -339,9 +342,8 @@ apr_size_t h2_util_table_bytes(apr_table
 /*******************************************************************************
  * HTTP/2 header helpers
  ******************************************************************************/
-int h2_req_ignore_header(const char *name, size_t len);
-int h2_req_ignore_trailer(const char *name, size_t len);
-int h2_res_ignore_trailer(const char *name, size_t len);
+int h2_ignore_req_trailer(const char *name, size_t len);
+int h2_ignore_resp_trailer(const char *name, size_t len);
 
 /**
  * Set the push policy for the given request. Takes request headers into 
@@ -372,39 +374,28 @@ const char *h2_util_base64url_encode(con
  * nghttp2 helpers
  ******************************************************************************/
 
-#define H2_HD_MATCH_LIT_CS(l, name)  \
-    ((strlen(name) == sizeof(l) - 1) && !apr_strnatcasecmp(l, name))
-
-#define H2_CREATE_NV_LIT_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME;      \
-                                             nv->namelen = sizeof(NAME) - 1;  \
-                                             nv->value = (uint8_t *)VALUE;    \
-                                             nv->valuelen = strlen(VALUE)
-
-#define H2_CREATE_NV_CS_LIT(nv, NAME, VALUE) nv->name = (uint8_t *)NAME;      \
-                                             nv->namelen = strlen(NAME);      \
-                                             nv->value = (uint8_t *)VALUE;    \
-                                             nv->valuelen = sizeof(VALUE) - 1
-
-#define H2_CREATE_NV_CS_CS(nv, NAME, VALUE) nv->name = (uint8_t *)NAME;       \
-                                            nv->namelen = strlen(NAME);       \
-                                            nv->value = (uint8_t *)VALUE;     \
-                                            nv->valuelen = strlen(VALUE)
-
-int h2_util_ignore_header(const char *name);
-
-struct h2_headers;
+int h2_util_ignore_resp_header(const char *name);
 
 typedef struct h2_ngheader {
     nghttp2_nv *nv;
     apr_size_t nvlen;
 } h2_ngheader;
 
+#if AP_HAS_RESPONSE_BUCKETS
+apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p,
+                                     ap_bucket_headers *headers);
+apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
+                                    ap_bucket_response *response);
+apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
+                                    const struct h2_request *req);
+#else
 apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p, 
                                      struct h2_headers *headers); 
 apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p, 
                                     struct h2_headers *headers); 
 apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p, 
                                     const struct h2_request *req);
+#endif
 
 /**
  * Add a HTTP/2 header and return the table key if it really was added
@@ -416,15 +407,6 @@ apr_status_t h2_req_add_header(apr_table
                                size_t max_field_len, int *pwas_added);
 
 /*******************************************************************************
- * h2_request helpers
- ******************************************************************************/
-
-struct h2_request *h2_req_create(int id, apr_pool_t *pool, const char *method, 
-                                 const char *scheme, const char *authority, 
-                                 const char *path, apr_table_t *header,
-                                 int serialize);
-
-/*******************************************************************************
  * apr brigade helpers
  ******************************************************************************/
 
@@ -444,43 +426,10 @@ apr_status_t h2_brigade_copy_length(apr_
                                     apr_bucket_brigade *src,
                                     apr_off_t length);
                                 
-/**
- * Return != 0 iff there is a FLUSH or EOS bucket in the brigade.
- * @param bb the brigade to check on
- * @return != 0 iff brigade holds FLUSH or EOS bucket (or both)
- */
-int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len);
-
-/**
- * Check how many bytes of the desired amount are available and if the
- * end of stream is reached by that amount.
- * @param bb the brigade to check
- * @param plen the desired length and, on return, the available length
- * @param on return, if eos has been reached
- */
-apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, 
-                              apr_off_t *plen, int *peos);
-
-typedef apr_status_t h2_util_pass_cb(void *ctx, 
+typedef apr_status_t h2_util_pass_cb(void *ctx,
                                      const char *data, apr_off_t len);
 
 /**
- * Read at most *plen bytes from the brigade and pass them into the
- * given callback. If cb is NULL, just return the amount of data that
- * could have been read.
- * If an EOS was/would be encountered, set *peos != 0.
- * @param bb the brigade to read from
- * @param cb the callback to invoke for the read data
- * @param ctx optional data passed to callback
- * @param plen inout, as input gives the maximum number of bytes to read,
- *    on return specifies the actual/would be number of bytes
- * @param peos != 0 iff an EOS bucket was/would be encountered.
- */
-apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, 
-                              h2_util_pass_cb *cb, void *ctx, 
-                              apr_off_t *plen, int *peos);
-
-/**
  * Print a bucket's meta data (type and length) to the buffer.
  * @return number of characters printed
  */
@@ -505,14 +454,16 @@ apr_size_t h2_util_bb_print(char *buffer
  * @param bb the brigade to log
  */
 #define h2_util_bb_log(c, sid, level, tag, bb) \
-do { \
-    char buffer[4 * 1024]; \
-    const char *line = "(null)"; \
-    apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
-    len = h2_util_bb_print(buffer, bmax, (tag), "", (bb)); \
-    ap_log_cerror(APLOG_MARK, level, 0, (c), "bb_dump(%ld): %s", \
-        ((c)->master? (c)->master->id : (c)->id), (len? buffer : line)); \
-} while(0)
+if (APLOG_C_IS_LEVEL(c, level)) { \
+    do { \
+        char buffer[4 * 1024]; \
+        const char *line = "(null)"; \
+        apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); \
+        len = h2_util_bb_print(buffer, bmax, (tag), "", (bb)); \
+        ap_log_cerror(APLOG_MARK, level, 0, (c), "bb_dump(%ld): %s", \
+            ((c)->master? (c)->master->id : (c)->id), (len? buffer : line)); \
+    } while(0); \
+}
 
 
 typedef int h2_bucket_gate(apr_bucket *b);
@@ -540,4 +491,29 @@ apr_status_t h2_append_brigade(apr_bucke
  */
 apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb);
 
+/**
+ * Drain a pipe used for notification.
+ */
+void h2_util_drain_pipe(apr_file_t *pipe);
+
+/**
+ * Wait on data arriving on a pipe.
+ */
+apr_status_t h2_util_wait_on_pipe(apr_file_t *pipe);
+
+
+#if AP_HAS_RESPONSE_BUCKETS
+/**
+ * Give an estimate of the length of the header fields,
+ * without compression or other formatting decorations.
+ */
+apr_size_t headers_length_estimate(ap_bucket_headers *hdrs);
+
+/**
+ * Give an estimate of the length of the response meta data size,
+ * without compression or other formatting decorations.
+ */
+apr_size_t response_length_estimate(ap_bucket_response *resp);
+#endif /* AP_HAS_RESPONSE_BUCKETS */
+
 #endif /* defined(__mod_h2__h2_util__) */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_version.h?rev=1906475&r1=1906474&r2=1906475&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_version.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_version.h Mon Jan  9 07:35:18 2023
@@ -27,7 +27,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.15.28"
+#define MOD_HTTP2_VERSION "2.0.11"
 
 /**
  * @macro
@@ -35,7 +35,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 0x010f1a
+#define MOD_HTTP2_VERSION_NUM 0x02000b
 
 
 #endif /* mod_h2_h2_version_h */