1 /* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 #include <apr_strings.h>
18 #include <apr_thread_mutex.h>
19 #include <apr_thread_cond.h>
22 #include <http_core.h>
24 #include <http_request.h>
26 #include <nghttp2/nghttp2.h>
31 /* h2_log2(n) iff n is a power of 2 */
32 unsigned char h2_log2(int n)
38 if (!(n & 0xffff0000u)) {
42 if (!(n & 0xff000000u)) {
46 if (!(n & 0xf0000000u)) {
50 if (!(n & 0xc0000000u)) {
54 if (!(n & 0x80000000u)) {
61 size_t h2_util_hex_dump(char *buffer, size_t maxlen,
62 const char *data, size_t datalen)
65 size_t maxoffset = (maxlen-4);
67 for (i = 0; i < datalen && offset < maxoffset; ++i) {
68 const char *sep = (i && i % 16 == 0)? "\n" : " ";
69 int n = apr_snprintf(buffer+offset, maxoffset-offset,
70 "%2x%s", ((unsigned int)data[i]&0xff), sep);
73 strcpy(buffer+offset, (i<datalen)? "..." : "");
74 return strlen(buffer);
77 size_t h2_util_header_print(char *buffer, size_t maxlen,
78 const char *name, size_t namelen,
79 const char *value, size_t valuelen)
83 for (i = 0; i < namelen && offset < maxlen; ++i, ++offset) {
84 buffer[offset] = name[i];
86 for (i = 0; i < 2 && offset < maxlen; ++i, ++offset) {
87 buffer[offset] = ": "[i];
89 for (i = 0; i < valuelen && offset < maxlen; ++i, ++offset) {
90 buffer[offset] = value[i];
92 buffer[offset] = '\0';
97 void h2_util_camel_case_header(char *s, size_t len)
101 for (i = 0; i < len; ++i) {
103 if (s[i] >= 'a' && s[i] <= 'z') {
109 else if (s[i] == '-') {
115 /* base64 url encoding */
117 static const int BASE64URL_UINT6[] = {
118 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
119 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0 */
120 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 1 */
121 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, /* 2 */
122 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 3 */
123 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 4 */
124 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, /* 5 */
125 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 6 */
126 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 7 */
127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 8 */
128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 9 */
129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a */
130 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b */
131 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c */
132 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d */
133 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e */
134 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* f */
136 static const char BASE64URL_CHARS[] = {
137 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', /* 0 - 9 */
138 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', /* 10 - 19 */
139 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', /* 20 - 29 */
140 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 30 - 39 */
141 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', /* 40 - 49 */
142 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', /* 50 - 59 */
143 '8', '9', '-', '_', ' ', ' ', ' ', ' ', ' ', ' ', /* 60 - 69 */
146 apr_size_t h2_util_base64url_decode(const char **decoded, const char *encoded,
149 const unsigned char *e = (const unsigned char *)encoded;
150 const unsigned char *p = e;
153 apr_size_t len, mlen, remain, i;
155 while (*p && BASE64URL_UINT6[ *p ] != -1) {
160 *decoded = apr_pcalloc(pool, len+1);
163 d = (unsigned char*)*decoded;
164 for (; i < mlen; i += 4) {
165 n = ((BASE64URL_UINT6[ e[i+0] ] << 18) +
166 (BASE64URL_UINT6[ e[i+1] ] << 12) +
167 (BASE64URL_UINT6[ e[i+2] ] << 6) +
168 (BASE64URL_UINT6[ e[i+3] ]));
170 *d++ = n >> 8 & 0xffu;
176 n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
177 (BASE64URL_UINT6[ e[mlen+1] ] << 12));
182 n = ((BASE64URL_UINT6[ e[mlen+0] ] << 18) +
183 (BASE64URL_UINT6[ e[mlen+1] ] << 12) +
184 (BASE64URL_UINT6[ e[mlen+2] ] << 6));
186 *d++ = n >> 8 & 0xffu;
189 default: /* do nothing */
192 return mlen/4*3 + remain;
195 const char *h2_util_base64url_encode(const char *data,
196 apr_size_t dlen, apr_pool_t *pool)
198 long i, len = (int)dlen;
199 apr_size_t slen = ((dlen+2)/3)*4 + 1; /* 0 terminated */
200 const unsigned char *udata = (const unsigned char*)data;
201 char *enc, *p = apr_pcalloc(pool, slen);
204 for (i = 0; i < len-2; i+= 3) {
205 *p++ = BASE64URL_CHARS[ (udata[i] >> 2) & 0x3fu ];
206 *p++ = BASE64URL_CHARS[ ((udata[i] << 4) + (udata[i+1] >> 4)) & 0x3fu ];
207 *p++ = BASE64URL_CHARS[ ((udata[i+1] << 2) + (udata[i+2] >> 6)) & 0x3fu ];
208 *p++ = BASE64URL_CHARS[ udata[i+2] & 0x3fu ];
212 *p++ = BASE64URL_CHARS[ (udata[i] >> 2) & 0x3fu ];
213 if (i == (len - 1)) {
214 *p++ = BASE64URL_CHARS[ (udata[i] << 4) & 0x3fu ];
217 *p++ = BASE64URL_CHARS[ ((udata[i] << 4) + (udata[i+1] >> 4)) & 0x3fu ];
218 *p++ = BASE64URL_CHARS[ (udata[i+1] << 2) & 0x3fu ];
225 /*******************************************************************************
226 * ihash - hash for structs with int identifier
227 ******************************************************************************/
233 static unsigned int ihash(const char *key, apr_ssize_t *klen)
235 return (unsigned int)(*((int*)key));
238 h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int)
240 h2_ihash_t *ih = apr_pcalloc(pool, sizeof(h2_ihash_t));
241 ih->hash = apr_hash_make_custom(pool, ihash);
242 ih->ioff = offset_of_int;
246 size_t h2_ihash_count(h2_ihash_t *ih)
248 return apr_hash_count(ih->hash);
251 int h2_ihash_empty(h2_ihash_t *ih)
253 return apr_hash_count(ih->hash) == 0;
256 void *h2_ihash_get(h2_ihash_t *ih, int id)
258 return apr_hash_get(ih->hash, &id, sizeof(id));
262 h2_ihash_iter_t *iter;
266 static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen,
269 iter_ctx *ictx = ctx;
270 return ictx->iter(ictx->ctx, (void*)val); /* why is this passed const?*/
273 int h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx)
278 return apr_hash_do(ihash_iter, &ictx, ih->hash);
281 void h2_ihash_add(h2_ihash_t *ih, void *val)
283 apr_hash_set(ih->hash, ((char *)val + ih->ioff), sizeof(int), val);
286 void h2_ihash_remove(h2_ihash_t *ih, int id)
288 apr_hash_set(ih->hash, &id, sizeof(id), NULL);
291 void h2_ihash_remove_val(h2_ihash_t *ih, void *val)
293 int id = *((int*)((char *)val + ih->ioff));
294 apr_hash_set(ih->hash, &id, sizeof(id), NULL);
298 void h2_ihash_clear(h2_ihash_t *ih)
300 apr_hash_clear(ih->hash);
310 static int collect_iter(void *x, void *val)
312 collect_ctx *ctx = x;
313 if (ctx->len < ctx->max) {
314 ctx->buffer[ctx->len++] = val;
320 size_t h2_ihash_shift(h2_ihash_t *ih, void **buffer, size_t max)
329 h2_ihash_iter(ih, collect_iter, &ctx);
330 for (i = 0; i < ctx.len; ++i) {
331 h2_ihash_remove_val(ih, buffer[i]);
336 /*******************************************************************************
337 * iqueue - sorted list of int
338 ******************************************************************************/
340 static void iq_grow(h2_iqueue *q, int nlen);
341 static void iq_swap(h2_iqueue *q, int i, int j);
342 static int iq_bubble_up(h2_iqueue *q, int i, int top,
343 h2_iq_cmp *cmp, void *ctx);
344 static int iq_bubble_down(h2_iqueue *q, int i, int bottom,
345 h2_iq_cmp *cmp, void *ctx);
347 h2_iqueue *h2_iq_create(apr_pool_t *pool, int capacity)
349 h2_iqueue *q = apr_pcalloc(pool, sizeof(h2_iqueue));
352 iq_grow(q, capacity);
358 int h2_iq_empty(h2_iqueue *q)
360 return q->nelts == 0;
363 int h2_iq_count(h2_iqueue *q)
369 int h2_iq_add(h2_iqueue *q, int sid, h2_iq_cmp *cmp, void *ctx)
373 if (h2_iq_contains(q, sid)) {
376 if (q->nelts >= q->nalloc) {
377 iq_grow(q, q->nalloc * 2);
379 i = (q->head + q->nelts) % q->nalloc;
384 /* bubble it to the front of the queue */
385 iq_bubble_up(q, i, q->head, cmp, ctx);
390 int h2_iq_append(h2_iqueue *q, int sid)
392 return h2_iq_add(q, sid, NULL, NULL);
395 int h2_iq_remove(h2_iqueue *q, int sid)
398 for (i = 0; i < q->nelts; ++i) {
399 if (sid == q->elts[(q->head + i) % q->nalloc]) {
406 for (; i < q->nelts; ++i) {
407 q->elts[(q->head+i-1)%q->nalloc] = q->elts[(q->head+i)%q->nalloc];
415 void h2_iq_clear(h2_iqueue *q)
420 void h2_iq_sort(h2_iqueue *q, h2_iq_cmp *cmp, void *ctx)
422 /* Assume that changes in ordering are minimal. This needs,
423 * best case, q->nelts - 1 comparisions to check that nothing
427 int i, ni, prev, last;
429 /* Start at the end of the queue and create a tail of sorted
430 * entries. Make that tail one element longer in each iteration.
432 last = i = (q->head + q->nelts - 1) % q->nalloc;
433 while (i != q->head) {
434 prev = (q->nalloc + i - 1) % q->nalloc;
436 ni = iq_bubble_up(q, i, prev, cmp, ctx);
438 /* i bubbled one up, bubble the new i down, which
439 * keeps all tasks below i sorted. */
440 iq_bubble_down(q, i, last, cmp, ctx);
448 int h2_iq_shift(h2_iqueue *q)
456 sid = q->elts[q->head];
457 q->head = (q->head + 1) % q->nalloc;
463 size_t h2_iq_mshift(h2_iqueue *q, int *pint, size_t max)
466 for (i = 0; i < max; ++i) {
467 pint[i] = h2_iq_shift(q);
475 static void iq_grow(h2_iqueue *q, int nlen)
477 if (nlen > q->nalloc) {
478 int *nq = apr_pcalloc(q->pool, sizeof(int) * nlen);
480 int l = ((q->head + q->nelts) % q->nalloc) - q->head;
482 memmove(nq, q->elts + q->head, sizeof(int) * l);
484 /* elts wrapped, append elts in [0, remain] to nq */
485 int remain = q->nelts - l;
486 memmove(nq + l, q->elts, sizeof(int) * remain);
495 static void iq_swap(h2_iqueue *q, int i, int j)
498 q->elts[i] = q->elts[j];
502 static int iq_bubble_up(h2_iqueue *q, int i, int top,
503 h2_iq_cmp *cmp, void *ctx)
506 while (((prev = (q->nalloc + i - 1) % q->nalloc), i != top)
507 && (*cmp)(q->elts[i], q->elts[prev], ctx) < 0) {
514 static int iq_bubble_down(h2_iqueue *q, int i, int bottom,
515 h2_iq_cmp *cmp, void *ctx)
518 while (((next = (q->nalloc + i + 1) % q->nalloc), i != bottom)
519 && (*cmp)(q->elts[i], q->elts[next], ctx) > 0) {
526 int h2_iq_contains(h2_iqueue *q, int sid)
529 for (i = 0; i < q->nelts; ++i) {
530 if (sid == q->elts[(q->head + i) % q->nalloc]) {
537 /*******************************************************************************
539 ******************************************************************************/
548 apr_thread_mutex_t *lock;
549 apr_thread_cond_t *not_empty;
550 apr_thread_cond_t *not_full;
553 static int nth_index(h2_fifo *fifo, int n)
555 return (fifo->head + n) % fifo->nelems;
558 static apr_status_t fifo_destroy(void *data)
560 h2_fifo *fifo = data;
562 apr_thread_cond_destroy(fifo->not_empty);
563 apr_thread_cond_destroy(fifo->not_full);
564 apr_thread_mutex_destroy(fifo->lock);
569 static int index_of(h2_fifo *fifo, void *elem)
573 for (i = 0; i < fifo->count; ++i) {
574 if (elem == fifo->elems[nth_index(fifo, i)]) {
581 static apr_status_t create_int(h2_fifo **pfifo, apr_pool_t *pool,
582 int capacity, int as_set)
587 fifo = apr_pcalloc(pool, sizeof(*fifo));
592 rv = apr_thread_mutex_create(&fifo->lock,
593 APR_THREAD_MUTEX_UNNESTED, pool);
594 if (rv != APR_SUCCESS) {
598 rv = apr_thread_cond_create(&fifo->not_empty, pool);
599 if (rv != APR_SUCCESS) {
603 rv = apr_thread_cond_create(&fifo->not_full, pool);
604 if (rv != APR_SUCCESS) {
608 fifo->elems = apr_pcalloc(pool, capacity * sizeof(void*));
609 if (fifo->elems == NULL) {
612 fifo->nelems = capacity;
616 apr_pool_cleanup_register(pool, fifo, fifo_destroy, apr_pool_cleanup_null);
621 apr_status_t h2_fifo_create(h2_fifo **pfifo, apr_pool_t *pool, int capacity)
623 return create_int(pfifo, pool, capacity, 0);
626 apr_status_t h2_fifo_set_create(h2_fifo **pfifo, apr_pool_t *pool, int capacity)
628 return create_int(pfifo, pool, capacity, 1);
631 apr_status_t h2_fifo_term(h2_fifo *fifo)
634 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
636 apr_thread_mutex_unlock(fifo->lock);
641 apr_status_t h2_fifo_interrupt(h2_fifo *fifo)
644 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
645 apr_thread_cond_broadcast(fifo->not_empty);
646 apr_thread_cond_broadcast(fifo->not_full);
647 apr_thread_mutex_unlock(fifo->lock);
652 int h2_fifo_count(h2_fifo *fifo)
657 static apr_status_t check_not_empty(h2_fifo *fifo, int block)
659 while (fifo->count == 0) {
666 apr_thread_cond_wait(fifo->not_empty, fifo->lock);
671 static apr_status_t fifo_push_int(h2_fifo *fifo, void *elem, int block)
677 if (fifo->set && index_of(fifo, elem) >= 0) {
678 /* set mode, elem already member */
681 else if (fifo->count == fifo->nelems) {
683 while (fifo->count == fifo->nelems) {
687 apr_thread_cond_wait(fifo->not_full, fifo->lock);
695 ap_assert(fifo->count < fifo->nelems);
696 fifo->elems[nth_index(fifo, fifo->count)] = elem;
698 if (fifo->count == 1) {
699 apr_thread_cond_broadcast(fifo->not_empty);
704 static apr_status_t fifo_push(h2_fifo *fifo, void *elem, int block)
712 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
713 rv = fifo_push_int(fifo, elem, block);
714 apr_thread_mutex_unlock(fifo->lock);
719 apr_status_t h2_fifo_push(h2_fifo *fifo, void *elem)
721 return fifo_push(fifo, elem, 1);
724 apr_status_t h2_fifo_try_push(h2_fifo *fifo, void *elem)
726 return fifo_push(fifo, elem, 0);
729 static apr_status_t pull_head(h2_fifo *fifo, void **pelem, int block)
733 if ((rv = check_not_empty(fifo, block)) != APR_SUCCESS) {
737 *pelem = fifo->elems[fifo->head];
739 if (fifo->count > 0) {
740 fifo->head = nth_index(fifo, 1);
741 if (fifo->count+1 == fifo->nelems) {
742 apr_thread_cond_broadcast(fifo->not_full);
748 static apr_status_t fifo_pull(h2_fifo *fifo, void **pelem, int block)
756 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
757 rv = pull_head(fifo, pelem, block);
758 apr_thread_mutex_unlock(fifo->lock);
763 apr_status_t h2_fifo_pull(h2_fifo *fifo, void **pelem)
765 return fifo_pull(fifo, pelem, 1);
768 apr_status_t h2_fifo_try_pull(h2_fifo *fifo, void **pelem)
770 return fifo_pull(fifo, pelem, 0);
773 static apr_status_t fifo_peek(h2_fifo *fifo, h2_fifo_peek_fn *fn, void *ctx, int block)
782 if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) {
783 if (APR_SUCCESS == (rv = pull_head(fifo, &elem, block))) {
784 switch (fn(elem, ctx)) {
785 case H2_FIFO_OP_PULL:
787 case H2_FIFO_OP_REPUSH:
788 rv = fifo_push_int(fifo, elem, block);
792 apr_thread_mutex_unlock(fifo->lock);
797 apr_status_t h2_fifo_peek(h2_fifo *fifo, h2_fifo_peek_fn *fn, void *ctx)
799 return fifo_peek(fifo, fn, ctx, 1);
802 apr_status_t h2_fifo_try_peek(h2_fifo *fifo, h2_fifo_peek_fn *fn, void *ctx)
804 return fifo_peek(fifo, fn, ctx, 0);
807 apr_status_t h2_fifo_remove(h2_fifo *fifo, void *elem)
815 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
820 for (i = 0; i < fifo->count; ++i) {
821 e = fifo->elems[nth_index(fifo, i)];
826 fifo->elems[nth_index(fifo, i-rc)] = e;
831 if (fifo->count + rc == fifo->nelems) {
832 apr_thread_cond_broadcast(fifo->not_full);
840 apr_thread_mutex_unlock(fifo->lock);
845 /*******************************************************************************
847 ******************************************************************************/
856 apr_thread_mutex_t *lock;
857 apr_thread_cond_t *not_empty;
858 apr_thread_cond_t *not_full;
861 static int inth_index(h2_ififo *fifo, int n)
863 return (fifo->head + n) % fifo->nelems;
866 static apr_status_t ififo_destroy(void *data)
868 h2_ififo *fifo = data;
870 apr_thread_cond_destroy(fifo->not_empty);
871 apr_thread_cond_destroy(fifo->not_full);
872 apr_thread_mutex_destroy(fifo->lock);
877 static int iindex_of(h2_ififo *fifo, int id)
881 for (i = 0; i < fifo->count; ++i) {
882 if (id == fifo->elems[inth_index(fifo, i)]) {
889 static apr_status_t icreate_int(h2_ififo **pfifo, apr_pool_t *pool,
890 int capacity, int as_set)
895 fifo = apr_pcalloc(pool, sizeof(*fifo));
900 rv = apr_thread_mutex_create(&fifo->lock,
901 APR_THREAD_MUTEX_UNNESTED, pool);
902 if (rv != APR_SUCCESS) {
906 rv = apr_thread_cond_create(&fifo->not_empty, pool);
907 if (rv != APR_SUCCESS) {
911 rv = apr_thread_cond_create(&fifo->not_full, pool);
912 if (rv != APR_SUCCESS) {
916 fifo->elems = apr_pcalloc(pool, capacity * sizeof(int));
917 if (fifo->elems == NULL) {
920 fifo->nelems = capacity;
924 apr_pool_cleanup_register(pool, fifo, ififo_destroy, apr_pool_cleanup_null);
929 apr_status_t h2_ififo_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity)
931 return icreate_int(pfifo, pool, capacity, 0);
934 apr_status_t h2_ififo_set_create(h2_ififo **pfifo, apr_pool_t *pool, int capacity)
936 return icreate_int(pfifo, pool, capacity, 1);
939 apr_status_t h2_ififo_term(h2_ififo *fifo)
942 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
944 apr_thread_mutex_unlock(fifo->lock);
949 apr_status_t h2_ififo_interrupt(h2_ififo *fifo)
952 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
953 apr_thread_cond_broadcast(fifo->not_empty);
954 apr_thread_cond_broadcast(fifo->not_full);
955 apr_thread_mutex_unlock(fifo->lock);
960 int h2_ififo_count(h2_ififo *fifo)
965 static apr_status_t icheck_not_empty(h2_ififo *fifo, int block)
967 while (fifo->count == 0) {
974 apr_thread_cond_wait(fifo->not_empty, fifo->lock);
979 static apr_status_t ififo_push_int(h2_ififo *fifo, int id, int block)
985 if (fifo->set && iindex_of(fifo, id) >= 0) {
986 /* set mode, elem already member */
989 else if (fifo->count == fifo->nelems) {
991 while (fifo->count == fifo->nelems) {
995 apr_thread_cond_wait(fifo->not_full, fifo->lock);
1003 ap_assert(fifo->count < fifo->nelems);
1004 fifo->elems[inth_index(fifo, fifo->count)] = id;
1006 if (fifo->count == 1) {
1007 apr_thread_cond_broadcast(fifo->not_empty);
1012 static apr_status_t ififo_push(h2_ififo *fifo, int id, int block)
1016 if (fifo->aborted) {
1020 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1021 rv = ififo_push_int(fifo, id, block);
1022 apr_thread_mutex_unlock(fifo->lock);
1027 apr_status_t h2_ififo_push(h2_ififo *fifo, int id)
1029 return ififo_push(fifo, id, 1);
1032 apr_status_t h2_ififo_try_push(h2_ififo *fifo, int id)
1034 return ififo_push(fifo, id, 0);
1037 static apr_status_t ipull_head(h2_ififo *fifo, int *pi, int block)
1041 if ((rv = icheck_not_empty(fifo, block)) != APR_SUCCESS) {
1045 *pi = fifo->elems[fifo->head];
1047 if (fifo->count > 0) {
1048 fifo->head = inth_index(fifo, 1);
1049 if (fifo->count+1 == fifo->nelems) {
1050 apr_thread_cond_broadcast(fifo->not_full);
1056 static apr_status_t ififo_pull(h2_ififo *fifo, int *pi, int block)
1060 if (fifo->aborted) {
1064 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1065 rv = ipull_head(fifo, pi, block);
1066 apr_thread_mutex_unlock(fifo->lock);
1071 apr_status_t h2_ififo_pull(h2_ififo *fifo, int *pi)
1073 return ififo_pull(fifo, pi, 1);
1076 apr_status_t h2_ififo_try_pull(h2_ififo *fifo, int *pi)
1078 return ififo_pull(fifo, pi, 0);
1081 static apr_status_t ififo_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx, int block)
1086 if (fifo->aborted) {
1090 if (APR_SUCCESS == (rv = apr_thread_mutex_lock(fifo->lock))) {
1091 if (APR_SUCCESS == (rv = ipull_head(fifo, &id, block))) {
1092 switch (fn(id, ctx)) {
1093 case H2_FIFO_OP_PULL:
1095 case H2_FIFO_OP_REPUSH:
1096 rv = ififo_push_int(fifo, id, block);
1100 apr_thread_mutex_unlock(fifo->lock);
1105 apr_status_t h2_ififo_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx)
1107 return ififo_peek(fifo, fn, ctx, 1);
1110 apr_status_t h2_ififo_try_peek(h2_ififo *fifo, h2_ififo_peek_fn *fn, void *ctx)
1112 return ififo_peek(fifo, fn, ctx, 0);
1115 apr_status_t h2_ififo_remove(h2_ififo *fifo, int id)
1119 if (fifo->aborted) {
1123 if ((rv = apr_thread_mutex_lock(fifo->lock)) == APR_SUCCESS) {
1128 for (i = 0; i < fifo->count; ++i) {
1129 e = fifo->elems[inth_index(fifo, i)];
1134 fifo->elems[inth_index(fifo, i-rc)] = e;
1139 if (fifo->count + rc == fifo->nelems) {
1140 apr_thread_cond_broadcast(fifo->not_full);
1148 apr_thread_mutex_unlock(fifo->lock);
1153 /*******************************************************************************
1154 * h2_util for apt_table_t
1155 ******************************************************************************/
1159 apr_size_t pair_extra;
1162 static int count_bytes(void *x, const char *key, const char *value)
1164 table_bytes_ctx *ctx = x;
1166 ctx->bytes += strlen(key);
1169 ctx->bytes += strlen(value);
1171 ctx->bytes += ctx->pair_extra;
1175 apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra)
1177 table_bytes_ctx ctx;
1180 ctx.pair_extra = pair_extra;
1181 apr_table_do(count_bytes, &ctx, t, NULL);
1186 /*******************************************************************************
1187 * h2_util for bucket brigades
1188 ******************************************************************************/
1190 static apr_status_t last_not_included(apr_bucket_brigade *bb,
1193 apr_size_t *pfile_buckets_allowed,
1197 apr_status_t status = APR_SUCCESS;
1198 int files_allowed = pfile_buckets_allowed? (int)*pfile_buckets_allowed : 0;
1201 /* Find the bucket, up to which we reach maxlen/mem bytes */
1202 for (b = APR_BRIGADE_FIRST(bb);
1203 (b != APR_BRIGADE_SENTINEL(bb));
1204 b = APR_BUCKET_NEXT(b)) {
1206 if (APR_BUCKET_IS_METADATA(b)) {
1210 if (b->length == ((apr_size_t)-1)) {
1213 status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
1214 if (status != APR_SUCCESS) {
1219 if (maxlen == 0 && b->length > 0) {
1224 if (same_alloc && APR_BUCKET_IS_FILE(b)) {
1225 /* we like it move it, always */
1227 else if (files_allowed > 0 && APR_BUCKET_IS_FILE(b)) {
1228 /* this has no memory footprint really unless
1229 * it is read, disregard it in length count,
1230 * unless we do not move the file buckets */
1233 else if (maxlen < (apr_off_t)b->length) {
1234 apr_bucket_split(b, (apr_size_t)maxlen);
1238 maxlen -= b->length;
1243 *pend = APR_BRIGADE_SENTINEL(bb);
1247 apr_status_t h2_brigade_concat_length(apr_bucket_brigade *dest,
1248 apr_bucket_brigade *src,
1252 apr_off_t remain = length;
1253 apr_status_t status = APR_SUCCESS;
1255 while (!APR_BRIGADE_EMPTY(src)) {
1256 b = APR_BRIGADE_FIRST(src);
1258 if (APR_BUCKET_IS_METADATA(b)) {
1259 APR_BUCKET_REMOVE(b);
1260 APR_BRIGADE_INSERT_TAIL(dest, b);
1263 if (remain == b->length) {
1266 else if (remain <= 0) {
1270 if (b->length == ((apr_size_t)-1)) {
1273 status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
1274 if (status != APR_SUCCESS) {
1279 if (remain < b->length) {
1280 apr_bucket_split(b, remain);
1283 APR_BUCKET_REMOVE(b);
1284 APR_BRIGADE_INSERT_TAIL(dest, b);
1285 remain -= b->length;
1291 apr_status_t h2_brigade_copy_length(apr_bucket_brigade *dest,
1292 apr_bucket_brigade *src,
1295 apr_bucket *b, *next;
1296 apr_off_t remain = length;
1297 apr_status_t status = APR_SUCCESS;
1299 for (b = APR_BRIGADE_FIRST(src);
1300 b != APR_BRIGADE_SENTINEL(src);
1302 next = APR_BUCKET_NEXT(b);
1304 if (APR_BUCKET_IS_METADATA(b)) {
1308 if (remain == b->length) {
1311 else if (remain <= 0) {
1315 if (b->length == ((apr_size_t)-1)) {
1318 status = apr_bucket_read(b, &ign, &ilen, APR_BLOCK_READ);
1319 if (status != APR_SUCCESS) {
1324 if (remain < b->length) {
1325 apr_bucket_split(b, remain);
1329 status = apr_bucket_copy(b, &b);
1330 if (status != APR_SUCCESS) {
1333 APR_BRIGADE_INSERT_TAIL(dest, b);
1334 remain -= b->length;
1339 int h2_util_has_eos(apr_bucket_brigade *bb, apr_off_t len)
1341 apr_bucket *b, *end;
1343 apr_status_t status = last_not_included(bb, len, 0, 0, &end);
1344 if (status != APR_SUCCESS) {
1348 for (b = APR_BRIGADE_FIRST(bb);
1349 b != APR_BRIGADE_SENTINEL(bb) && b != end;
1350 b = APR_BUCKET_NEXT(b))
1352 if (APR_BUCKET_IS_EOS(b)) {
1359 apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb,
1360 apr_off_t *plen, int *peos)
1362 apr_status_t status;
1365 /* test read to determine available length */
1366 status = apr_brigade_length(bb, 1, &blen);
1367 if (status != APR_SUCCESS) {
1370 else if (blen == 0) {
1371 /* brigade without data, does it have an EOS bucket somwhere? */
1373 *peos = h2_util_has_eos(bb, -1);
1376 /* data in the brigade, limit the length returned. Check for EOS
1377 * bucket only if we indicate data. This is required since plen == 0
1378 * means "the whole brigade" for h2_util_hash_eos()
1380 if (blen < *plen || *plen < 0) {
1383 *peos = h2_util_has_eos(bb, *plen);
1388 apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb,
1389 h2_util_pass_cb *cb, void *ctx,
1390 apr_off_t *plen, int *peos)
1392 apr_status_t status = APR_SUCCESS;
1393 int consume = (cb != NULL);
1394 apr_off_t written = 0;
1395 apr_off_t avail = *plen;
1396 apr_bucket *next, *b;
1398 /* Pass data in our brigade through the callback until the length
1399 * is satisfied or we encounter an EOS.
1402 for (b = APR_BRIGADE_FIRST(bb);
1403 (status == APR_SUCCESS) && (b != APR_BRIGADE_SENTINEL(bb));
1406 if (APR_BUCKET_IS_METADATA(b)) {
1407 if (APR_BUCKET_IS_EOS(b)) {
1414 else if (avail <= 0) {
1418 const char *data = NULL;
1419 apr_size_t data_len;
1421 if (b->length == ((apr_size_t)-1)) {
1422 /* read to determine length */
1423 status = apr_bucket_read(b, &data, &data_len, APR_NONBLOCK_READ);
1426 data_len = b->length;
1429 if (data_len > avail) {
1430 apr_bucket_split(b, avail);
1431 data_len = (apr_size_t)avail;
1436 status = apr_bucket_read(b, &data, &data_len,
1439 if (status == APR_SUCCESS) {
1440 status = cb(ctx, data, data_len);
1444 data_len = b->length;
1447 written += data_len;
1450 next = APR_BUCKET_NEXT(b);
1452 apr_bucket_delete(b);
1457 if (status == APR_SUCCESS && !*peos && !*plen) {
1463 apr_size_t h2_util_bucket_print(char *buffer, apr_size_t bmax,
1464 apr_bucket *b, const char *sep)
1468 off += apr_snprintf(buffer+off, bmax-off, "%s", sep);
1474 else if (APR_BUCKET_IS_METADATA(b)) {
1475 off += apr_snprintf(buffer+off, bmax-off, "%s", b->type->name);
1477 else if (bmax > off) {
1478 off += apr_snprintf(buffer+off, bmax-off, "%s[%ld]",
1480 (long)(b->length == ((apr_size_t)-1)?
1486 apr_size_t h2_util_bb_print(char *buffer, apr_size_t bmax,
1487 const char *tag, const char *sep,
1488 apr_bucket_brigade *bb)
1491 const char *sp = "";
1496 memset(buffer, 0, bmax--);
1497 off += apr_snprintf(buffer+off, bmax-off, "%s(", tag);
1498 for (b = APR_BRIGADE_FIRST(bb);
1499 (bmax > off) && (b != APR_BRIGADE_SENTINEL(bb));
1500 b = APR_BUCKET_NEXT(b)) {
1502 off += h2_util_bucket_print(buffer+off, bmax-off, b, sp);
1506 off += apr_snprintf(buffer+off, bmax-off, ")%s", sep);
1510 off += apr_snprintf(buffer+off, bmax-off, "%s(null)%s", tag, sep);
1516 apr_status_t h2_append_brigade(apr_bucket_brigade *to,
1517 apr_bucket_brigade *from,
1520 h2_bucket_gate *should_append)
1523 apr_off_t len = 0, remain = *plen;
1528 while (!APR_BRIGADE_EMPTY(from)) {
1529 e = APR_BRIGADE_FIRST(from);
1531 if (!should_append(e)) {
1534 else if (APR_BUCKET_IS_METADATA(e)) {
1535 if (APR_BUCKET_IS_EOS(e)) {
1537 apr_bucket_delete(e);
1542 if (remain > 0 && e->length == ((apr_size_t)-1)) {
1545 rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ);
1546 if (rv != APR_SUCCESS) {
1551 if (remain < e->length) {
1555 apr_bucket_split(e, (apr_size_t)remain);
1559 APR_BUCKET_REMOVE(e);
1560 APR_BRIGADE_INSERT_TAIL(to, e);
1562 remain -= e->length;
1569 apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb)
1572 apr_off_t total = 0;
1574 for (b = APR_BRIGADE_FIRST(bb);
1575 b != APR_BRIGADE_SENTINEL(bb);
1576 b = APR_BUCKET_NEXT(b))
1578 total += sizeof(*b);
1579 if (b->length > 0) {
1580 if (APR_BUCKET_IS_HEAP(b)
1581 || APR_BUCKET_IS_POOL(b)) {
1590 /*******************************************************************************
1592 ******************************************************************************/
1594 int h2_util_ignore_header(const char *name)
1596 /* never forward, ch. 8.1.2.2 */
1597 return (H2_HD_MATCH_LIT_CS("connection", name)
1598 || H2_HD_MATCH_LIT_CS("proxy-connection", name)
1599 || H2_HD_MATCH_LIT_CS("upgrade", name)
1600 || H2_HD_MATCH_LIT_CS("keep-alive", name)
1601 || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
1604 static int count_header(void *ctx, const char *key, const char *value)
1606 if (!h2_util_ignore_header(key)) {
1607 (*((size_t*)ctx))++;
1612 static const char *inv_field_name_chr(const char *token)
1614 const char *p = ap_scan_http_token(token);
1615 if (p == token && *p == ':') {
1616 p = ap_scan_http_token(++p);
1618 return (p && *p)? p : NULL;
1621 static const char *inv_field_value_chr(const char *token)
1623 const char *p = ap_scan_http_field_content(token);
1624 return (p && *p)? p : NULL;
1627 typedef struct ngh_ctx {
1631 apr_status_t status;
1634 static int add_header(ngh_ctx *ctx, const char *key, const char *value)
1636 nghttp2_nv *nv = &(ctx->ngh)->nv[(ctx->ngh)->nvlen++];
1640 if ((p = inv_field_name_chr(key))) {
1641 ap_log_perror(APLOG_MARK, APLOG_TRACE1, APR_EINVAL, ctx->p,
1642 "h2_request: head field '%s: %s' has invalid char %s",
1644 ctx->status = APR_EINVAL;
1647 if ((p = inv_field_value_chr(value))) {
1648 ap_log_perror(APLOG_MARK, APLOG_TRACE1, APR_EINVAL, ctx->p,
1649 "h2_request: head field '%s: %s' has invalid char %s",
1651 ctx->status = APR_EINVAL;
1655 nv->name = (uint8_t*)key;
1656 nv->namelen = strlen(key);
1657 nv->value = (uint8_t*)value;
1658 nv->valuelen = strlen(value);
1663 static int add_table_header(void *ctx, const char *key, const char *value)
1665 if (!h2_util_ignore_header(key)) {
1666 add_header(ctx, key, value);
1671 static apr_status_t ngheader_create(h2_ngheader **ph, apr_pool_t *p,
1672 int unsafe, size_t key_count,
1673 const char *keys[], const char *values[],
1674 apr_table_t *headers)
1680 ctx.unsafe = unsafe;
1683 apr_table_do(count_header, &n, headers, NULL);
1685 *ph = ctx.ngh = apr_pcalloc(p, sizeof(h2_ngheader));
1690 ctx.ngh->nv = apr_pcalloc(p, n * sizeof(nghttp2_nv));
1695 ctx.status = APR_SUCCESS;
1696 for (i = 0; i < key_count; ++i) {
1697 if (!add_header(&ctx, keys[i], values[i])) {
1702 apr_table_do(add_table_header, &ctx, headers, NULL);
1707 static int is_unsafe(h2_headers *h)
1709 const char *v = apr_table_get(h->notes, H2_HDR_CONFORMANCE);
1710 return (v && !strcmp(v, H2_HDR_CONFORMANCE_UNSAFE));
1713 apr_status_t h2_res_create_ngtrailer(h2_ngheader **ph, apr_pool_t *p,
1714 h2_headers *headers)
1716 return ngheader_create(ph, p, is_unsafe(headers),
1717 0, NULL, NULL, headers->headers);
1720 apr_status_t h2_res_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
1721 h2_headers *headers)
1723 const char *keys[] = {
1726 const char *values[] = {
1727 apr_psprintf(p, "%d", headers->status)
1729 return ngheader_create(ph, p, is_unsafe(headers),
1730 H2_ALEN(keys), keys, values, headers->headers);
1733 apr_status_t h2_req_create_ngheader(h2_ngheader **ph, apr_pool_t *p,
1734 const struct h2_request *req)
1737 const char *keys[] = {
1743 const char *values[] = {
1750 ap_assert(req->scheme);
1751 ap_assert(req->authority);
1752 ap_assert(req->path);
1753 ap_assert(req->method);
1755 return ngheader_create(ph, p, 0, H2_ALEN(keys), keys, values, req->headers);
1758 /*******************************************************************************
1759 * header HTTP/1 <-> HTTP/2 conversions
1760 ******************************************************************************/
1768 #define H2_DEF_LITERAL(n) { (n), (sizeof(n)-1) }
1769 #define H2_LIT_ARGS(a) (a),H2_ALEN(a)
1771 static literal IgnoredRequestHeaders[] = {
1772 H2_DEF_LITERAL("upgrade"),
1773 H2_DEF_LITERAL("connection"),
1774 H2_DEF_LITERAL("keep-alive"),
1775 H2_DEF_LITERAL("http2-settings"),
1776 H2_DEF_LITERAL("proxy-connection"),
1777 H2_DEF_LITERAL("transfer-encoding"),
1779 static literal IgnoredRequestTrailers[] = { /* Ignore, see rfc7230, ch. 4.1.2 */
1780 H2_DEF_LITERAL("te"),
1781 H2_DEF_LITERAL("host"),
1782 H2_DEF_LITERAL("range"),
1783 H2_DEF_LITERAL("cookie"),
1784 H2_DEF_LITERAL("expect"),
1785 H2_DEF_LITERAL("pragma"),
1786 H2_DEF_LITERAL("max-forwards"),
1787 H2_DEF_LITERAL("cache-control"),
1788 H2_DEF_LITERAL("authorization"),
1789 H2_DEF_LITERAL("content-length"),
1790 H2_DEF_LITERAL("proxy-authorization"),
1792 static literal IgnoredResponseTrailers[] = {
1793 H2_DEF_LITERAL("age"),
1794 H2_DEF_LITERAL("date"),
1795 H2_DEF_LITERAL("vary"),
1796 H2_DEF_LITERAL("cookie"),
1797 H2_DEF_LITERAL("expires"),
1798 H2_DEF_LITERAL("warning"),
1799 H2_DEF_LITERAL("location"),
1800 H2_DEF_LITERAL("retry-after"),
1801 H2_DEF_LITERAL("cache-control"),
1802 H2_DEF_LITERAL("www-authenticate"),
1803 H2_DEF_LITERAL("proxy-authenticate"),
1806 static int ignore_header(const literal *lits, size_t llen,
1807 const char *name, size_t nlen)
1812 for (i = 0; i < llen; ++i) {
1814 if (lit->len == nlen && !apr_strnatcasecmp(lit->name, name)) {
1821 int h2_req_ignore_header(const char *name, size_t len)
1823 return ignore_header(H2_LIT_ARGS(IgnoredRequestHeaders), name, len);
1826 int h2_req_ignore_trailer(const char *name, size_t len)
1828 return (h2_req_ignore_header(name, len)
1829 || ignore_header(H2_LIT_ARGS(IgnoredRequestTrailers), name, len));
1832 int h2_res_ignore_trailer(const char *name, size_t len)
1834 return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
1837 apr_status_t h2_req_add_header(apr_table_t *headers, apr_pool_t *pool,
1838 const char *name, size_t nlen,
1839 const char *value, size_t vlen)
1841 char *hname, *hvalue;
1843 if (h2_req_ignore_header(name, nlen)) {
1846 else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
1847 const char *existing = apr_table_get(headers, "cookie");
1851 /* Cookie header come separately in HTTP/2, but need
1852 * to be merged by "; " (instead of default ", ")
1854 hvalue = apr_pstrndup(pool, value, vlen);
1855 nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
1856 apr_table_setn(headers, "Cookie", nval);
1860 else if (H2_HD_MATCH_LIT("host", name, nlen)) {
1861 if (apr_table_get(headers, "Host")) {
1862 return APR_SUCCESS; /* ignore duplicate */
1866 hname = apr_pstrndup(pool, name, nlen);
1867 hvalue = apr_pstrndup(pool, value, vlen);
1868 h2_util_camel_case_header(hname, nlen);
1869 apr_table_mergen(headers, hname, hvalue);
1874 /*******************************************************************************
1875 * h2 request handling
1876 ******************************************************************************/
1878 h2_request *h2_req_create(int id, apr_pool_t *pool, const char *method,
1879 const char *scheme, const char *authority,
1880 const char *path, apr_table_t *header, int serialize)
1882 h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
1884 req->method = method;
1885 req->scheme = scheme;
1886 req->authority = authority;
1888 req->headers = header? header : apr_table_make(pool, 10);
1889 req->request_time = apr_time_now();
1890 req->serialize = serialize;
1895 /*******************************************************************************
1897 ******************************************************************************/
1899 int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
1902 size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
1904 switch (frame->hd.type) {
1905 case NGHTTP2_DATA: {
1906 return apr_snprintf(buffer, maxlen,
1907 "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
1908 (int)frame->hd.length, frame->hd.flags,
1909 frame->hd.stream_id, (int)frame->data.padlen);
1911 case NGHTTP2_HEADERS: {
1912 return apr_snprintf(buffer, maxlen,
1913 "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
1914 (int)frame->hd.length,
1915 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1916 frame->hd.stream_id,
1917 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
1919 case NGHTTP2_PRIORITY: {
1920 return apr_snprintf(buffer, maxlen,
1921 "PRIORITY[length=%d, flags=%d, stream=%d]",
1922 (int)frame->hd.length,
1923 frame->hd.flags, frame->hd.stream_id);
1925 case NGHTTP2_RST_STREAM: {
1926 return apr_snprintf(buffer, maxlen,
1927 "RST_STREAM[length=%d, flags=%d, stream=%d]",
1928 (int)frame->hd.length,
1929 frame->hd.flags, frame->hd.stream_id);
1931 case NGHTTP2_SETTINGS: {
1932 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1933 return apr_snprintf(buffer, maxlen,
1934 "SETTINGS[ack=1, stream=%d]",
1935 frame->hd.stream_id);
1937 return apr_snprintf(buffer, maxlen,
1938 "SETTINGS[length=%d, stream=%d]",
1939 (int)frame->hd.length, frame->hd.stream_id);
1941 case NGHTTP2_PUSH_PROMISE: {
1942 return apr_snprintf(buffer, maxlen,
1943 "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
1944 (int)frame->hd.length,
1945 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1946 frame->hd.stream_id);
1948 case NGHTTP2_PING: {
1949 return apr_snprintf(buffer, maxlen,
1950 "PING[length=%d, ack=%d, stream=%d]",
1951 (int)frame->hd.length,
1952 frame->hd.flags&NGHTTP2_FLAG_ACK,
1953 frame->hd.stream_id);
1955 case NGHTTP2_GOAWAY: {
1956 size_t len = (frame->goaway.opaque_data_len < s_len)?
1957 frame->goaway.opaque_data_len : s_len-1;
1958 memcpy(scratch, frame->goaway.opaque_data, len);
1959 scratch[len] = '\0';
1960 return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s', "
1961 "last_stream=%d]", frame->goaway.error_code,
1962 scratch, frame->goaway.last_stream_id);
1964 case NGHTTP2_WINDOW_UPDATE: {
1965 return apr_snprintf(buffer, maxlen,
1966 "WINDOW_UPDATE[stream=%d, incr=%d]",
1967 frame->hd.stream_id,
1968 frame->window_update.window_size_increment);
1971 return apr_snprintf(buffer, maxlen,
1972 "type=%d[length=%d, flags=%d, stream=%d]",
1973 frame->hd.type, (int)frame->hd.length,
1974 frame->hd.flags, frame->hd.stream_id);
1978 /*******************************************************************************
1980 ******************************************************************************/
1981 int h2_push_policy_determine(apr_table_t *headers, apr_pool_t *p, int push_enabled)
1983 h2_push_policy policy = H2_PUSH_NONE;
1985 const char *val = apr_table_get(headers, "accept-push-policy");
1987 if (ap_find_token(p, val, "fast-load")) {
1988 policy = H2_PUSH_FAST_LOAD;
1990 else if (ap_find_token(p, val, "head")) {
1991 policy = H2_PUSH_HEAD;
1993 else if (ap_find_token(p, val, "default")) {
1994 policy = H2_PUSH_DEFAULT;
1996 else if (ap_find_token(p, val, "none")) {
1997 policy = H2_PUSH_NONE;
2000 /* nothing known found in this header, go by default */
2001 policy = H2_PUSH_DEFAULT;
2005 policy = H2_PUSH_DEFAULT;