summaryrefslogtreecommitdiffstats
path: root/modules/http2/h2_util.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--modules/http2/h2_util.c983
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 */