diff options
Diffstat (limited to '')
-rw-r--r-- | modules/http2/h2_util.c | 983 |
1 files changed, 453 insertions, 530 deletions
diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index 9dacd8b..8e53ceb 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -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; } @@ -75,26 +77,6 @@ size_t h2_util_hex_dump(char *buffer, size_t maxlen, return strlen(buffer); } -size_t h2_util_header_print(char *buffer, size_t maxlen, - const char *name, size_t namelen, - const char *value, size_t valuelen) -{ - size_t offset = 0; - size_t i; - for (i = 0; i < namelen && offset < maxlen; ++i, ++offset) { - buffer[offset] = name[i]; - } - for (i = 0; i < 2 && offset < maxlen; ++i, ++offset) { - buffer[offset] = ": "[i]; - } - for (i = 0; i < valuelen && offset < maxlen; ++i, ++offset) { - buffer[offset] = value[i]; - } - buffer[offset] = '\0'; - return offset; -} - - void h2_util_camel_case_header(char *s, size_t len) { size_t start = 1; @@ -104,7 +86,7 @@ void h2_util_camel_case_header(char *s, size_t len) if (s[i] >= 'a' && s[i] <= 'z') { s[i] -= 'a' - 'A'; } - + start = 0; } else if (s[i] == '-') { @@ -113,16 +95,16 @@ void h2_util_camel_case_header(char *s, size_t len) } } -/* base64 url encoding ****************************************************************************/ +/* base64 url encoding */ #define N6 (unsigned int)-1 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 */ @@ -148,7 +130,7 @@ static const unsigned char BASE64URL_CHARS[] = { #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; @@ -156,14 +138,14 @@ apr_size_t h2_util_base64url_decode(const char **decoded, const char *encoded, 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) { @@ -197,14 +179,14 @@ apr_size_t h2_util_base64url_decode(const char **decoded, const char *encoded, 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) ); @@ -212,7 +194,7 @@ const char *h2_util_base64url_encode(const char *data, *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)) { @@ -248,7 +230,7 @@ h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int) 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); } @@ -268,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; @@ -326,7 +308,7 @@ size_t h2_ihash_shift(h2_ihash_t *ih, void **buffer, size_t max) { collect_ctx ctx; size_t i; - + ctx.ih = ih; ctx.buffer = buffer; ctx.max = max; @@ -344,19 +326,17 @@ size_t h2_ihash_shift(h2_ihash_t *ih, void **buffer, size_t max) 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; } @@ -374,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; } @@ -384,7 +364,7 @@ int h2_iq_add(h2_iqueue *q, int sid, h2_iq_cmp *cmp, void *ctx) 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); @@ -405,7 +385,7 @@ int h2_iq_remove(h2_iqueue *q, int sid) break; } } - + if (i < q->nelts) { ++i; for (; i < q->nelts; ++i) { @@ -425,23 +405,23 @@ void h2_iq_clear(h2_iqueue *q) void h2_iq_sort(h2_iqueue *q, h2_iq_cmp *cmp, void *ctx) { /* Assume that changes in ordering are minimal. This needs, - * best case, q->nelts - 1 comparisions to check that nothing + * best case, q->nelts - 1 comparisons to check that nothing * changed. */ 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; @@ -453,21 +433,21 @@ void h2_iq_sort(h2_iqueue *q, h2_iq_cmp *cmp, void *ctx) 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) { @@ -483,7 +463,7 @@ static void iq_grow(h2_iqueue *q, int nlen) 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 */ @@ -504,11 +484,11 @@ static void iq_swap(h2_iqueue *q, int i, int j) 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; @@ -516,11 +496,11 @@ static int iq_bubble_up(h2_iqueue *q, int i, int top, 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; @@ -545,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; @@ -555,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; @@ -574,21 +550,21 @@ static apr_status_t fifo_destroy(void *data) 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; @@ -614,9 +590,9 @@ static apr_status_t create_int(h2_fifo **pfifo, apr_pool_t *pool, 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); @@ -638,15 +614,6 @@ apr_status_t h2_fifo_term(h2_fifo *fifo) apr_status_t rv; if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { fifo->aborted = 1; - apr_thread_mutex_unlock(fifo->lock); - } - return rv; -} - -apr_status_t h2_fifo_interrupt(h2_fifo *fifo) -{ - apr_status_t rv; - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { apr_thread_cond_broadcast(fifo->not_empty); apr_thread_cond_broadcast(fifo->not_full); apr_thread_mutex_unlock(fifo->lock); @@ -656,7 +623,12 @@ apr_status_t h2_fifo_interrupt(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) @@ -683,9 +655,9 @@ static apr_status_t fifo_push_int(h2_fifo *fifo, void *elem, int block) /* 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; } @@ -696,12 +668,14 @@ static apr_status_t fifo_push_int(h2_fifo *fifo, void *elem, int block) 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; } @@ -709,10 +683,6 @@ static apr_status_t fifo_push_int(h2_fifo *fifo, void *elem, int block) static apr_status_t fifo_push(h2_fifo *fifo, void *elem, int block) { apr_status_t rv; - - if (fifo->aborted) { - return APR_EOF; - } if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { rv = fifo_push_int(fifo, elem, block); @@ -734,18 +704,20 @@ apr_status_t h2_fifo_try_push(h2_fifo *fifo, void *elem) 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; } @@ -753,11 +725,7 @@ static apr_status_t pull_head(h2_fifo *fifo, void **pelem, int block) static apr_status_t fifo_pull(h2_fifo *fifo, void **pelem, int block) { apr_status_t rv; - - if (fifo->aborted) { - return APR_EOF; - } - + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { rv = pull_head(fifo, pelem, block); apr_thread_mutex_unlock(fifo->lock); @@ -779,11 +747,11 @@ static apr_status_t fifo_peek(h2_fifo *fifo, h2_fifo_peek_fn *fn, void *ctx, int { 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)) { @@ -812,28 +780,58 @@ apr_status_t h2_fifo_try_peek(h2_fifo *fifo, h2_fifo_peek_fn *fn, void *ctx) 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; + 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 (rc) { - fifo->count -= rc; - if (fifo->count + rc == fifo->nelems) { + if (fifo->count != last_count) { + if (last_count == fifo->capacity) { apr_thread_cond_broadcast(fifo->not_full); } rv = APR_SUCCESS; @@ -841,7 +839,7 @@ apr_status_t h2_fifo_remove(h2_fifo *fifo, void *elem) else { rv = APR_EAGAIN; } - + apr_thread_mutex_unlock(fifo->lock); } return rv; @@ -853,7 +851,7 @@ apr_status_t h2_fifo_remove(h2_fifo *fifo, void *elem) struct h2_ififo { int *elems; - int nelems; + int capacity; int set; int head; int count; @@ -863,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; @@ -882,7 +880,7 @@ static apr_status_t ififo_destroy(void *data) 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; @@ -891,12 +889,12 @@ static int iindex_of(h2_ififo *fifo, int id) 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; @@ -922,9 +920,9 @@ static apr_status_t icreate_int(h2_ififo **pfifo, apr_pool_t *pool, 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); @@ -946,15 +944,6 @@ apr_status_t h2_ififo_term(h2_ififo *fifo) apr_status_t rv; if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { fifo->aborted = 1; - apr_thread_mutex_unlock(fifo->lock); - } - return rv; -} - -apr_status_t h2_ififo_interrupt(h2_ififo *fifo) -{ - apr_status_t rv; - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { apr_thread_cond_broadcast(fifo->not_empty); apr_thread_cond_broadcast(fifo->not_full); apr_thread_mutex_unlock(fifo->lock); @@ -991,9 +980,9 @@ static apr_status_t ififo_push_int(h2_ififo *fifo, int id, int block) /* 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; } @@ -1004,8 +993,8 @@ static apr_status_t ififo_push_int(h2_ififo *fifo, int id, int block) 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) { @@ -1017,10 +1006,6 @@ static apr_status_t ififo_push_int(h2_ififo *fifo, int id, int block) static apr_status_t ififo_push(h2_ififo *fifo, int id, int block) { apr_status_t rv; - - if (fifo->aborted) { - return APR_EOF; - } if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { rv = ififo_push_int(fifo, id, block); @@ -1042,7 +1027,7 @@ apr_status_t h2_ififo_try_push(h2_ififo *fifo, int id) 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; @@ -1051,7 +1036,7 @@ static apr_status_t ipull_head(h2_ififo *fifo, int *pi, int block) --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); } } @@ -1061,11 +1046,7 @@ static apr_status_t ipull_head(h2_ififo *fifo, int *pi, int block) static apr_status_t ififo_pull(h2_ififo *fifo, int *pi, int block) { apr_status_t rv; - - if (fifo->aborted) { - return APR_EOF; - } - + if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { rv = ipull_head(fifo, pi, block); apr_thread_mutex_unlock(fifo->lock); @@ -1087,11 +1068,7 @@ static apr_status_t ififo_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx, { apr_status_t rv; int id; - - if (fifo->aborted) { - return APR_EOF; - } - + if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) { if (APR_SUCCESS == (rv = ipull_head(fifo, &id, block))) { switch (fn(id, ctx)) { @@ -1117,39 +1094,40 @@ apr_status_t h2_ififo_try_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx) return ififo_peek(fifo, fn, ctx, 0); } -apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) +static apr_status_t ififo_remove(h2_ififo *fifo, int id) { - apr_status_t rv; - + int rc, i; + if (fifo->aborted) { return APR_EOF; } - if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) { - int i, rc; - int e; - - rc = 0; - for (i = 0; i < fifo->count; ++i) { - e = fifo->elems[inth_index(fifo, i)]; - if (e == id) { - ++rc; - } - else if (rc) { - fifo->elems[inth_index(fifo, i-rc)] = e; - } - } - if (rc) { - fifo->count -= rc; - if (fifo->count + rc == fifo->nelems) { - apr_thread_cond_broadcast(fifo->not_full); - } - rv = APR_SUCCESS; + rc = 0; + for (i = 0; i < fifo->count; ++i) { + int e = fifo->elems[inth_index(fifo, i)]; + if (e == id) { + ++rc; } - else { - rv = APR_EAGAIN; + else if (rc) { + fifo->elems[inth_index(fifo, i-rc)] = e; } - + } + if (!rc) { + return APR_EAGAIN; + } + fifo->count -= rc; + if (fifo->count + rc == fifo->capacity) { + apr_thread_cond_broadcast(fifo->not_full); + } + return APR_SUCCESS; +} + +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); } return rv; @@ -1158,7 +1136,7 @@ apr_status_t h2_ififo_remove(h2_ififo *fifo, int id) /******************************************************************************* * h2_util for apt_table_t ******************************************************************************/ - + typedef struct { apr_size_t bytes; apr_size_t pair_extra; @@ -1180,7 +1158,7 @@ static int count_bytes(void *x, const char *key, const char *value) 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); @@ -1192,287 +1170,108 @@ apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra) * 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 somwhere? */ - *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; } @@ -1480,30 +1279,30 @@ apr_size_t h2_util_bucket_print(char *buffer, apr_size_t bmax, 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)? - -1 : b->length)); + off += apr_snprintf(buffer+off, bmax-off, "%s[%ld]", + b->type->name, + (b->length == ((apr_size_t)-1)? + -1 : (long)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 = " "; } @@ -1519,20 +1318,21 @@ apr_size_t h2_util_bb_print(char *buffer, apr_size_t bmax, } 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; } @@ -1543,8 +1343,11 @@ apr_status_t h2_append_brigade(apr_bucket_brigade *to, 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); @@ -1552,22 +1355,13 @@ apr_status_t h2_append_brigade(apr_bucket_brigade *to, 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; } @@ -1595,20 +1389,10 @@ apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb) /******************************************************************************* * 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; @@ -1629,6 +1413,17 @@ static const char *inv_field_value_chr(const char *token) 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; @@ -1644,14 +1439,14 @@ static int add_header(ngh_ctx *ctx, const char *key, const char *value) 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; @@ -1661,69 +1456,100 @@ static int add_header(ngh_ctx *ctx, const char *key, const char *value) 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" @@ -1731,27 +1557,29 @@ apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p, 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); @@ -1763,7 +1591,7 @@ apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p, /******************************************************************************* * header HTTP/1 <-> HTTP/2 conversions ******************************************************************************/ - + typedef struct { const char *name; @@ -1791,9 +1619,15 @@ static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */ 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"), @@ -1808,93 +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 + && !ap_cstr_casecmp(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) +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; - - if (h2_req_ignore_header(name, nlen)) { + const char *existing; + + *pwas_added = 0; + strip_field_value_ws(nv); + + if (h2_req_ignore_header(nv)) { return APR_SUCCESS; } - else if (H2_HD_MATCH_LIT("cookie", name, nlen)) { - const char *existing = apr_table_get(headers, "cookie"); + else if (nv->namelen == sizeof("cookie")-1 + && !ap_cstr_casecmp("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 ", ") */ - hvalue = apr_pstrndup(pool, value, vlen); - nval = apr_psprintf(pool, "%s; %s", existing, hvalue); - apr_table_setn(headers, "Cookie", nval); + 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, (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 + && !ap_cstr_casecmp("host", (const char *)nv->name)) { if (apr_table_get(headers, "Host")) { return APR_SUCCESS; /* ignore duplicate */ } } - - hname = apr_pstrndup(pool, name, nlen); - hvalue = apr_pstrndup(pool, value, vlen); - 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) + 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, (const char*)nv->value, nv->valuelen); apr_table_mergen(headers, hname, hvalue); - + return APR_SUCCESS; } -/******************************************************************************* - * h2 request handling - ******************************************************************************/ - -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_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) { - 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; + nghttp2_nv nv; + + 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); } /******************************************************************************* @@ -1905,7 +1770,7 @@ int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen) { char scratch[128]; size_t s_len = sizeof(scratch)/sizeof(scratch[0]); - + switch (frame->hd.type) { case NGHTTP2_DATA: { return apr_snprintf(buffer, maxlen, @@ -1960,16 +1825,17 @@ int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen) case NGHTTP2_GOAWAY: { size_t len = (frame->goaway.opaque_data_len < s_len)? frame->goaway.opaque_data_len : s_len-1; - memcpy(scratch, frame->goaway.opaque_data, len); + if (len) + 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: @@ -2013,3 +1879,60 @@ int h2_push_policy_determine(apr_table_t *headers, apr_pool_t *p, int push_enabl return policy; } +void h2_util_drain_pipe(apr_file_t *pipe) +{ + char rb[512]; + apr_size_t nr = sizeof(rb); + apr_interval_time_t timeout; + apr_status_t trv; + + /* Make the pipe non-blocking if we can */ + trv = apr_file_pipe_timeout_get(pipe, &timeout); + if (trv == APR_SUCCESS) + apr_file_pipe_timeout_set(pipe, 0); + + 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; + } + if (trv == APR_SUCCESS) + apr_file_pipe_timeout_set(pipe, timeout); +} + +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 */ |