diff options
author | Stefan Eissing <icing@apache.org> | 2016-04-15 15:50:46 +0200 |
---|---|---|
committer | Stefan Eissing <icing@apache.org> | 2016-04-15 15:50:46 +0200 |
commit | 52cdae53be08ed37e5d73eac20a39d76f7617bcb (patch) | |
tree | e383ea1324254aee627932931ee9dd5250f0444f /modules/http2/h2_util.c | |
parent | http: Respond with "408 Request Timeout" when a timeout occurs while (diff) | |
download | apache2-52cdae53be08ed37e5d73eac20a39d76f7617bcb.tar.xz apache2-52cdae53be08ed37e5d73eac20a39d76f7617bcb.zip |
mod_http2: new bucket beams for tranporting buckets across threads without buffer copy. Code cleanup
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1739303 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'modules/http2/h2_util.c')
-rw-r--r-- | modules/http2/h2_util.c | 796 |
1 files changed, 437 insertions, 359 deletions
diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index 06472425f2..9beaeae048 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -332,6 +332,301 @@ void h2_ihash_clear(h2_ihash_t *ih) } /******************************************************************************* + * ilist - sorted list for structs with int identifier + ******************************************************************************/ + +#define h2_ilist_IDX(list, i) ((int**)(list)->elts)[i] + +struct h2_ilist_t { + apr_array_header_t *l; +}; + +h2_ilist_t *h2_ilist_create(apr_pool_t *pool) +{ + h2_ilist_t *list = apr_pcalloc(pool, sizeof(h2_ilist_t)); + if (list) { + list->l = apr_array_make(pool, 100, sizeof(int*)); + if (!list->l) { + return NULL; + } + } + return list; +} + +static int h2_ilist_cmp(const void *s1, const void *s2) +{ + int **pi1 = (int **)s1; + int **pi2 = (int **)s2; + return *(*pi1) - *(*pi2); +} + +void *h2_ilist_get(h2_ilist_t *list, int id) +{ + /* we keep the array sorted by id, so lookup can be done + * by bsearch. + */ + int **pi; + int *pkey = &id; + + pi = bsearch(&pkey, list->l->elts, list->l->nelts, + list->l->elt_size, h2_ilist_cmp); + return pi? *pi : NULL; +} + +static void h2_ilist_sort(h2_ilist_t *list) +{ + qsort(list->l->elts, list->l->nelts, list->l->elt_size, h2_ilist_cmp); +} + +apr_status_t h2_ilist_add(h2_ilist_t *list, void *val) +{ + int *pi = val; + void *existing = h2_ilist_get(list, *pi); + if (!existing) { + int last; + APR_ARRAY_PUSH(list->l, void*) = val; + /* Often, values get added in ascending order of id. We + * keep the array sorted, so we just need to check if the newly + * appended stream has a lower id than the last one. if not, + * sorting is not necessary. + */ + last = list->l->nelts - 1; + if (last > 0 + && *h2_ilist_IDX(list->l, last) < *h2_ilist_IDX(list->l, last-1)) { + h2_ilist_sort(list); + } + } + return APR_SUCCESS; +} + +static void remove_idx(h2_ilist_t *list, int idx) +{ + int n; + --list->l->nelts; + n = list->l->nelts - idx; + if (n > 0) { + /* There are n h2_io* behind idx. Move the rest down */ + int **selts = (int**)list->l->elts; + memmove(selts + idx, selts + idx + 1, n * sizeof(int*)); + } +} + +void *h2_ilist_remove(h2_ilist_t *list, int id) +{ + int i; + for (i = 0; i < list->l->nelts; ++i) { + int *e = h2_ilist_IDX(list->l, i); + if (id == *e) { + remove_idx(list, i); + return e; + } + } + return NULL; +} + +void *h2_ilist_shift(h2_ilist_t *list) +{ + if (list->l->nelts > 0) { + int *pi = h2_ilist_IDX(list->l, 0); + remove_idx(list, 0); + return pi; + } + return NULL; +} + +int h2_ilist_empty(h2_ilist_t *list) +{ + return list->l->nelts == 0; +} + +int h2_ilist_iter(h2_ilist_t *list, h2_ilist_iter_t *iter, void *ctx) +{ + int i; + for (i = 0; i < list->l->nelts; ++i) { + int *pi = h2_ilist_IDX(list->l, i); + if (!iter(ctx, pi)) { + return 0; + } + } + return 1; +} + +apr_size_t h2_ilist_count(h2_ilist_t *list) +{ + return list->l->nelts; +} + +/******************************************************************************* + * iqueue - sorted list of int + ******************************************************************************/ + +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, + h2_iq_cmp *cmp, void *ctx); +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; + } + return q; +} + +int h2_iq_empty(h2_iqueue *q) +{ + return q->nelts == 0; +} + +int h2_iq_count(h2_iqueue *q) +{ + return q->nelts; +} + + +void h2_iq_add(h2_iqueue *q, int sid, h2_iq_cmp *cmp, void *ctx) +{ + int i; + + if (q->nelts >= q->nalloc) { + iq_grow(q, q->nalloc * 2); + } + + 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); + } +} + +int h2_iq_remove(h2_iqueue *q, int sid) +{ + int i; + for (i = 0; i < q->nelts; ++i) { + if (sid == q->elts[(q->head + i) % q->nalloc]) { + break; + } + } + + if (i < q->nelts) { + ++i; + for (; i < q->nelts; ++i) { + q->elts[(q->head+i-1)%q->nalloc] = q->elts[(q->head+i)%q->nalloc]; + } + --q->nelts; + return 1; + } + return 0; +} + +void h2_iq_clear(h2_iqueue *q) +{ + q->nelts = 0; +} + +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 + * 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. */ + iq_bubble_down(q, i, last, cmp, ctx); + } + i = prev; + }; + } +} + + +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; +} + +static void iq_grow(h2_iqueue *q, int nlen) +{ + if (nlen > q->nalloc) { + 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 */ + int remain = q->nelts - l; + memmove(nq + l, q->elts, sizeof(int) * remain); + } + } + q->elts = nq; + q->nalloc = nlen; + q->head = 0; + } +} + +static void iq_swap(h2_iqueue *q, int i, int j) +{ + int x = q->elts[i]; + q->elts[i] = q->elts[j]; + q->elts[j] = x; +} + +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) + && (*cmp)(q->elts[i], q->elts[prev], ctx) < 0) { + iq_swap(q, prev, i); + i = prev; + } + return i; +} + +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) + && (*cmp)(q->elts[i], q->elts[next], ctx) > 0) { + iq_swap(q, next, i); + i = next; + } + return i; +} + +/******************************************************************************* * h2_util for apt_table_t ******************************************************************************/ @@ -368,15 +663,6 @@ apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra) * h2_util for bucket brigades ******************************************************************************/ -/* DEEP_COPY==0 crashes under load. I think the setaside is fine, - * however buckets moved to another thread will still be - * free'd against the old bucket_alloc. *And* if the old - * pool gets destroyed too early, the bucket disappears while - * still needed. - */ -static const int DEEP_COPY = 1; -static const int FILE_MOVE = 1; - static apr_status_t last_not_included(apr_bucket_brigade *bb, apr_off_t maxlen, int same_alloc, @@ -434,200 +720,95 @@ static apr_status_t last_not_included(apr_bucket_brigade *bb, return status; } -#define LOG_BUCKETS 0 -#define LOG_LEVEL APLOG_INFO - -apr_status_t h2_util_move(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_off_t maxlen, apr_size_t *pfile_buckets_allowed, - const char *msg) +apr_status_t h2_brigade_concat_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; - int same_alloc; - AP_DEBUG_ASSERT(to); - AP_DEBUG_ASSERT(from); - same_alloc = (to->bucket_alloc == from->bucket_alloc - || to->p == from->p); - - if (!FILE_MOVE) { - pfile_buckets_allowed = NULL; - } - - if (!APR_BRIGADE_EMPTY(from)) { - apr_bucket *b, *end; + for (b = APR_BRIGADE_FIRST(src); + b != APR_BRIGADE_SENTINEL(src); + b = next) { + next = APR_BUCKET_NEXT(b); - status = last_not_included(from, maxlen, same_alloc, - pfile_buckets_allowed, &end); - if (status != APR_SUCCESS) { - return status; + if (APR_BUCKET_IS_METADATA(b)) { + /* fall through */ } - - while (!APR_BRIGADE_EMPTY(from) && status == APR_SUCCESS) { - b = APR_BRIGADE_FIRST(from); - if (b == end) { - break; + else { + if (remain == b->length) { + /* fall through */ } - - if (same_alloc || (b->list == to->bucket_alloc)) { - /* both brigades use the same bucket_alloc and auto-cleanups - * have the same life time. It's therefore safe to just move - * directly. */ - APR_BUCKET_REMOVE(b); - APR_BRIGADE_INSERT_TAIL(to, b); -#if LOG_BUCKETS - ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, APLOGNO(03205) - "h2_util_move: %s, passed bucket(same bucket_alloc) " - "%ld-%ld, type=%s", - msg, (long)b->start, (long)b->length, - APR_BUCKET_IS_METADATA(b)? - (APR_BUCKET_IS_EOS(b)? "EOS": - (APR_BUCKET_IS_FLUSH(b)? "FLUSH" : "META")) : - (APR_BUCKET_IS_FILE(b)? "FILE" : "DATA")); -#endif + else if (remain <= 0) { + return status; } - else if (DEEP_COPY) { - /* we have not managed the magic of passing buckets from - * one thread to another. Any attempts result in - * cleanup of pools scrambling memory. - */ - if (APR_BUCKET_IS_METADATA(b)) { - if (APR_BUCKET_IS_EOS(b)) { - APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc)); - } - else { - /* ignore */ - } - } - else if (pfile_buckets_allowed - && *pfile_buckets_allowed > 0 - && APR_BUCKET_IS_FILE(b)) { - /* We do not want to read files when passing buckets, if - * we can avoid it. However, what we've come up so far - * is not working corrently, resulting either in crashes or - * too many open file descriptors. - */ - apr_bucket_file *f = (apr_bucket_file *)b->data; - apr_file_t *fd = f->fd; - int setaside = (f->readpool != to->p); -#if LOG_BUCKETS - ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, APLOGNO(03206) - "h2_util_move: %s, moving FILE bucket %ld-%ld " - "from=%lx(p=%lx) to=%lx(p=%lx), setaside=%d", - msg, (long)b->start, (long)b->length, - (long)from, (long)from->p, - (long)to, (long)to->p, setaside); -#endif - if (setaside) { - status = apr_file_setaside(&fd, fd, to->p); - if (status != APR_SUCCESS) { - ap_log_perror(APLOG_MARK, APLOG_ERR, status, to->p, - APLOGNO(02947) "h2_util: %s, setaside FILE", - msg); - 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; } - apr_brigade_insert_file(to, fd, b->start, b->length, - to->p); - --(*pfile_buckets_allowed); } - else { - const char *data; - apr_size_t len; - - status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); - if (status == APR_SUCCESS && len > 0) { - status = apr_brigade_write(to, NULL, NULL, data, len); -#if LOG_BUCKETS - ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, APLOGNO(03207) - "h2_util_move: %s, copied bucket %ld-%ld " - "from=%lx(p=%lx) to=%lx(p=%lx)", - msg, (long)b->start, (long)b->length, - (long)from, (long)from->p, - (long)to, (long)to->p); -#endif - } + + if (remain < b->length) { + apr_bucket_split(b, remain); } - apr_bucket_delete(b); - } - else { - apr_bucket_setaside(b, to->p); - APR_BUCKET_REMOVE(b); - APR_BRIGADE_INSERT_TAIL(to, b); -#if LOG_BUCKETS - ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, APLOGNO(03208) - "h2_util_move: %s, passed setaside bucket %ld-%ld " - "from=%lx(p=%lx) to=%lx(p=%lx)", - msg, (long)b->start, (long)b->length, - (long)from, (long)from->p, - (long)to, (long)to->p); -#endif } } + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(dest, b); + remain -= b->length; } - return status; } -apr_status_t h2_util_copy(apr_bucket_brigade *to, apr_bucket_brigade *from, - apr_off_t maxlen, const char *msg) +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; - int same_alloc; - - (void)msg; - AP_DEBUG_ASSERT(to); - AP_DEBUG_ASSERT(from); - same_alloc = (to->bucket_alloc == from->bucket_alloc); - - if (!APR_BRIGADE_EMPTY(from)) { - apr_bucket *b, *end, *cpy; + + for (b = APR_BRIGADE_FIRST(src); + b != APR_BRIGADE_SENTINEL(src); + b = next) { + next = APR_BUCKET_NEXT(b); - status = last_not_included(from, maxlen, 0, 0, &end); - if (status != APR_SUCCESS) { - return status; + if (APR_BUCKET_IS_METADATA(b)) { + /* fall through */ } - - for (b = APR_BRIGADE_FIRST(from); - b != APR_BRIGADE_SENTINEL(from) && b != end; - b = APR_BUCKET_NEXT(b)) - { - if (same_alloc) { - status = apr_bucket_copy(b, &cpy); - if (status != APR_SUCCESS) { - break; - } - APR_BRIGADE_INSERT_TAIL(to, cpy); + else { + if (remain == b->length) { + /* fall through */ + } + else if (remain <= 0) { + return status; } else { - if (APR_BUCKET_IS_METADATA(b)) { - if (APR_BUCKET_IS_EOS(b)) { - APR_BRIGADE_INSERT_TAIL(to, apr_bucket_eos_create(to->bucket_alloc)); - } - else if (APR_BUCKET_IS_FLUSH(b)) { - APR_BRIGADE_INSERT_TAIL(to, apr_bucket_flush_create(to->bucket_alloc)); - } - else { - /* ignore */ + 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; } } - else { - const char *data; - apr_size_t len; - status = apr_bucket_read(b, &data, &len, APR_BLOCK_READ); - if (status == APR_SUCCESS && len > 0) { - status = apr_brigade_write(to, NULL, NULL, data, len); -#if LOG_BUCKETS - ap_log_perror(APLOG_MARK, LOG_LEVEL, 0, to->p, APLOGNO(03209) - "h2_util_copy: %s, copied bucket %ld-%ld " - "from=%lx(p=%lx) to=%lx(p=%lx)", - msg, (long)b->start, (long)b->length, - (long)from, (long)from->p, - (long)to, (long)to->p); -#endif - } + + if (remain < b->length) { + apr_bucket_split(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; } @@ -666,25 +847,6 @@ int h2_util_bb_has_data(apr_bucket_brigade *bb) return 0; } -int h2_util_bb_has_data_or_eos(apr_bucket_brigade *bb) -{ - apr_bucket *b; - for (b = APR_BRIGADE_FIRST(bb); - b != APR_BRIGADE_SENTINEL(bb); - b = APR_BUCKET_NEXT(b)) - { - if (APR_BUCKET_IS_METADATA(b)) { - if (APR_BUCKET_IS_EOS(b)) { - return 1; - } - } - else { - return 1; - } - } - return 0; -} - apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb, apr_off_t *plen, int *peos) { @@ -789,186 +951,102 @@ apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb, return status; } -void h2_util_bb_log(conn_rec *c, int stream_id, int level, - const char *tag, apr_bucket_brigade *bb) +apr_size_t h2_util_bucket_print(char *buffer, apr_size_t bmax, + apr_bucket *b, const char *sep) { - char buffer[16 * 1024]; - const char *line = "(null)"; - apr_size_t bmax = sizeof(buffer)/sizeof(buffer[0]); - int off = 0; - apr_bucket *b; + apr_size_t off = 0; + if (sep && *sep) { + off += apr_snprintf(buffer+off, bmax-off, "%s", sep); + } - if (bb) { - memset(buffer, 0, bmax--); - for (b = APR_BRIGADE_FIRST(bb); - bmax && (b != APR_BRIGADE_SENTINEL(bb)); - b = APR_BUCKET_NEXT(b)) { - - if (APR_BUCKET_IS_METADATA(b)) { - if (APR_BUCKET_IS_EOS(b)) { - off += apr_snprintf(buffer+off, bmax-off, "eos "); - } - else if (APR_BUCKET_IS_FLUSH(b)) { - off += apr_snprintf(buffer+off, bmax-off, "flush "); - } - else if (AP_BUCKET_IS_EOR(b)) { - off += apr_snprintf(buffer+off, bmax-off, "eor "); - } - else { - off += apr_snprintf(buffer+off, bmax-off, "meta(unknown) "); - } - } - else { - const char *btype = "data"; - if (APR_BUCKET_IS_FILE(b)) { - btype = "file"; - } - else if (APR_BUCKET_IS_PIPE(b)) { - btype = "pipe"; - } - else if (APR_BUCKET_IS_SOCKET(b)) { - btype = "socket"; - } - else if (APR_BUCKET_IS_HEAP(b)) { - btype = "heap"; - } - else if (APR_BUCKET_IS_TRANSIENT(b)) { - btype = "transient"; - } - else if (APR_BUCKET_IS_IMMORTAL(b)) { - btype = "immortal"; - } -#if APR_HAS_MMAP - else if (APR_BUCKET_IS_MMAP(b)) { - btype = "mmap"; - } -#endif - else if (APR_BUCKET_IS_POOL(b)) { - btype = "pool"; - } - - off += apr_snprintf(buffer+off, bmax-off, "%s[%ld] ", - btype, - (long)(b->length == ((apr_size_t)-1)? - -1 : b->length)); - } + if (APR_BUCKET_IS_METADATA(b)) { + if (APR_BUCKET_IS_EOS(b)) { + off += apr_snprintf(buffer+off, bmax-off, "eos"); + } + else if (APR_BUCKET_IS_FLUSH(b)) { + off += apr_snprintf(buffer+off, bmax-off, "flush"); + } + else if (AP_BUCKET_IS_EOR(b)) { + off += apr_snprintf(buffer+off, bmax-off, "eor"); + } + else { + off += apr_snprintf(buffer+off, bmax-off, "meta(unknown)"); } - line = *buffer? buffer : "(empty)"; } - /* Intentional no APLOGNO */ - ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%ld-%d)-%s: %s", - c->id, stream_id, tag, line); - -} - -apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to, - apr_bucket_brigade *from, - apr_pool_t *p, - apr_off_t *plen, - int *peos) -{ - apr_bucket *e; - apr_off_t len = 0, remain = *plen; - apr_status_t rv; - - *peos = 0; - - while (!APR_BRIGADE_EMPTY(from)) { - e = APR_BRIGADE_FIRST(from); - - if (APR_BUCKET_IS_METADATA(e)) { - if (APR_BUCKET_IS_EOS(e)) { - *peos = 1; - } + else { + const char *btype = "data"; + if (APR_BUCKET_IS_FILE(b)) { + btype = "file"; } - else { - if (remain > 0 && e->length == ((apr_size_t)-1)) { - const char *ign; - apr_size_t ilen; - rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ); - if (rv != APR_SUCCESS) { - return rv; - } - } - - if (remain < e->length) { - if (remain <= 0) { - return APR_SUCCESS; - } - apr_bucket_split(e, remain); - } + else if (APR_BUCKET_IS_PIPE(b)) { + btype = "pipe"; } - - rv = apr_bucket_setaside(e, p); - - /* If the bucket type does not implement setaside, then - * (hopefully) morph it into a bucket type which does, and set - * *that* aside... */ - if (rv == APR_ENOTIMPL) { - const char *s; - apr_size_t n; - - rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ); - if (rv == APR_SUCCESS) { - rv = apr_bucket_setaside(e, p); - } + else if (APR_BUCKET_IS_SOCKET(b)) { + btype = "socket"; } - - if (rv != APR_SUCCESS) { - /* Return an error but still save the brigade if - * ->setaside() is really not implemented. */ - if (rv != APR_ENOTIMPL) { - return rv; - } + else if (APR_BUCKET_IS_HEAP(b)) { + btype = "heap"; + } + else if (APR_BUCKET_IS_TRANSIENT(b)) { + btype = "transient"; + } + else if (APR_BUCKET_IS_IMMORTAL(b)) { + btype = "immortal"; + } +#if APR_HAS_MMAP + else if (APR_BUCKET_IS_MMAP(b)) { + btype = "mmap"; + } +#endif + else if (APR_BUCKET_IS_POOL(b)) { + btype = "pool"; } - APR_BUCKET_REMOVE(e); - APR_BRIGADE_INSERT_TAIL(to, e); - len += e->length; - remain -= e->length; + off += apr_snprintf(buffer+off, bmax-off, "%s[%ld]", + btype, + (long)(b->length == ((apr_size_t)-1)? + -1 : b->length)); } - - *plen = len; - return APR_SUCCESS; + return off; } -apr_status_t h2_transfer_brigade(apr_bucket_brigade *to, - apr_bucket_brigade *from, - apr_pool_t *p) +apr_size_t h2_util_bb_print(char *buffer, apr_size_t bmax, + const char *tag, const char *sep, + apr_bucket_brigade *bb) { - apr_bucket *e; - apr_status_t rv; - - while (!APR_BRIGADE_EMPTY(from)) { - e = APR_BRIGADE_FIRST(from); - - rv = apr_bucket_setaside(e, p); - - /* If the bucket type does not implement setaside, then - * (hopefully) morph it into a bucket type which does, and set - * *that* aside... */ - if (rv == APR_ENOTIMPL) { - const char *s; - apr_size_t n; + apr_size_t off = 0; + const char *sp = ""; + apr_bucket *b; + + if (bb) { + memset(buffer, 0, bmax--); + off += apr_snprintf(buffer+off, bmax-off, "%s(", tag); + for (b = APR_BRIGADE_FIRST(bb); + bmax && (b != APR_BRIGADE_SENTINEL(bb)); + b = APR_BUCKET_NEXT(b)) { - rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ); - if (rv == APR_SUCCESS) { - rv = apr_bucket_setaside(e, p); - } - } - - if (rv != APR_SUCCESS) { - /* Return an error but still save the brigade if - * ->setaside() is really not implemented. */ - if (rv != APR_ENOTIMPL) { - return rv; - } + off += h2_util_bucket_print(buffer+off, bmax-off, b, sp); + sp = " "; } - - APR_BUCKET_REMOVE(e); - APR_BRIGADE_INSERT_TAIL(to, e); + off += apr_snprintf(buffer+off, bmax-off, ")%s", sep); } - return APR_SUCCESS; + else { + off += apr_snprintf(buffer+off, bmax-off, "%s(null)%s", tag, sep); + } + return off; +} + +void h2_util_bb_log(conn_rec *c, int stream_id, int level, + const char *tag, apr_bucket_brigade *bb) +{ + char buffer[4 * 1024]; + const char *line = "(null)"; + apr_size_t len, bmax = sizeof(buffer)/sizeof(buffer[0]); + + len = h2_util_bb_print(buffer, bmax, tag, "", bb); + /* Intentional no APLOGNO */ + ap_log_cerror(APLOG_MARK, level, 0, c, "bb_dump(%ld-%d): %s", + c->id, stream_id, len? buffer : line); } apr_status_t h2_append_brigade(apr_bucket_brigade *to, |