From d07bb1ddffae2f7ab72eafede3738090f7322a06 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 13 Mar 2014 22:11:02 +0900 Subject: [PATCH] Rework outbound frame buffers --- lib/nghttp2_buf.c | 123 ++++++++- lib/nghttp2_buf.h | 171 ++++++++++++- lib/nghttp2_frame.c | 470 ++++++++++++++++++++++------------- lib/nghttp2_frame.h | 214 +++++++--------- lib/nghttp2_hd.c | 226 ++++++++--------- lib/nghttp2_hd.h | 48 ++-- lib/nghttp2_hd_huffman.c | 103 ++++++-- lib/nghttp2_session.c | 303 ++++++---------------- lib/nghttp2_session.h | 6 +- python/cnghttp2.pxd | 17 +- python/nghttp2.pyx | 21 +- src/app_helper.cc | 2 +- src/deflatehd.c | 28 ++- tests/nghttp2_frame_test.c | 167 +++++++------ tests/nghttp2_hd_test.c | 340 ++++++++++++++----------- tests/nghttp2_session_test.c | 270 ++++++++++++-------- tests/nghttp2_test_helper.c | 77 ++++-- tests/nghttp2_test_helper.h | 8 +- 18 files changed, 1532 insertions(+), 1062 deletions(-) diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c index cc6daedd..d729e1fc 100644 --- a/lib/nghttp2_buf.c +++ b/lib/nghttp2_buf.c @@ -123,11 +123,17 @@ static void nghttp2_buf_chain_del(nghttp2_buf_chain *chain) int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk) +{ + return nghttp2_bufs_init2(bufs, chunk_length, max_chunk, 0); +} + +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset) { int rv; nghttp2_buf_chain *chain; - if(max_chunk == 0) { + if(max_chunk == 0 || chunk_length < offset) { return NGHTTP2_ERR_INVALID_ARGUMENT; } @@ -136,9 +142,13 @@ int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, return rv; } + bufs->offset = offset; + bufs->head = chain; bufs->cur = bufs->head; + nghttp2_buf_shift_right(&bufs->cur->buf, offset); + bufs->chunk_length = chunk_length; bufs->chunk_left = max_chunk - 1; @@ -158,10 +168,36 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs) } } +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) +{ + nghttp2_buf_chain *ci; + + for(ci = bufs->cur; ci->next; ci = ci->next) { + if(nghttp2_buf_len(&ci->buf) == 0) { + return; + } else { + bufs->cur = ci; + } + } +} + +ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs) +{ + nghttp2_buf_chain *ci; + ssize_t len; + + len = 0; + for(ci = bufs->head; ci; ci = ci->next) { + len += nghttp2_buf_len(&ci->buf); + } + + return len; +} + static int nghttp2_bufs_avail(nghttp2_bufs *bufs) { return nghttp2_buf_avail(&bufs->cur->buf) + - bufs->chunk_left * bufs->chunk_left; + (bufs->chunk_left - bufs->offset) * bufs->chunk_left; } static int nghttp2_bufs_alloc_chain(nghttp2_bufs *bufs) @@ -189,6 +225,8 @@ static int nghttp2_bufs_alloc_chain(nghttp2_bufs *bufs) bufs->cur->next = chain; bufs->cur = chain; + nghttp2_buf_shift_right(&bufs->cur->buf, bufs->offset); + return 0; } @@ -199,7 +237,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) nghttp2_buf *buf; const uint8_t *p; - if((size_t)nghttp2_bufs_avail(bufs) < len) { + if(nghttp2_bufs_avail(bufs) < (ssize_t)len) { return NGHTTP2_ERR_BUFFER_ERROR; } @@ -224,7 +262,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) return 0; } -int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) +static int nghttp2_bufs_ensure_addb(nghttp2_bufs *bufs) { int rv; nghttp2_buf *buf; @@ -232,7 +270,6 @@ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) buf = &bufs->cur->buf; if(nghttp2_buf_avail(buf) > 0) { - *buf->last++ = b; return 0; } @@ -241,9 +278,61 @@ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) return rv; } - buf = &bufs->cur->buf; + return 0; +} - *buf->last++ = b; +int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b) +{ + int rv; + + rv = nghttp2_bufs_ensure_addb(bufs); + if(rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ = b; + + return 0; +} + +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b) +{ + int rv; + + rv = nghttp2_bufs_ensure_addb(bufs); + if(rv != 0) { + return rv; + } + + *bufs->cur->buf.last = b; + + return 0; +} + +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b) +{ + int rv; + + rv = nghttp2_bufs_ensure_addb(bufs); + if(rv != 0) { + return rv; + } + + *bufs->cur->buf.last++ |= b; + + return 0; +} + +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) +{ + int rv; + + rv = nghttp2_bufs_ensure_addb(bufs); + if(rv != 0) { + return rv; + } + + *bufs->cur->buf.last |= b; return 0; } @@ -272,7 +361,9 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) for(chain = bufs->head; chain; chain = chain->next) { buf = &chain->buf; resbuf.last = nghttp2_cpymem(resbuf.last, buf->pos, nghttp2_buf_len(buf)); + nghttp2_buf_reset(buf); + nghttp2_buf_shift_right(&chain->buf, bufs->offset); } bufs->cur = bufs->head; @@ -288,5 +379,23 @@ void nghttp2_bufs_reset(nghttp2_bufs *bufs) for(chain = bufs->head; chain; chain = chain->next) { nghttp2_buf_reset(&chain->buf); + nghttp2_buf_shift_right(&chain->buf, bufs->offset); } + + bufs->cur = bufs->head; } + +int nghttp2_bufs_advance(nghttp2_bufs *bufs) +{ + return nghttp2_bufs_alloc_chain(bufs); +} + +int nghttp2_bufs_next_present(nghttp2_bufs *bufs) +{ + nghttp2_buf_chain *chain; + + chain = bufs->cur->next; + + return chain && nghttp2_buf_len(&chain->buf); +} + diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h index ae609fb6..7a6ceed9 100644 --- a/lib/nghttp2_buf.h +++ b/lib/nghttp2_buf.h @@ -134,39 +134,202 @@ void nghttp2_buf_reset(nghttp2_buf *buf); */ void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); -/* - * List of nghttp2_buf - */ - struct nghttp2_buf_chain; typedef struct nghttp2_buf_chain nghttp2_buf_chain; +/* Chains 2 buffers */ struct nghttp2_buf_chain { + /* Points to the subsequent buffer. NULL if there is no such + buffer. */ nghttp2_buf_chain *next; nghttp2_buf buf; }; typedef struct { + /* Points to the first buffer */ nghttp2_buf_chain *head; + /* Buffer pointer where write occurs. */ nghttp2_buf_chain *cur; /* The buffer capacity of each buf */ size_t chunk_length; /* The maximum number of nghttp2_buf_chain */ size_t chunk_left; + /* pos offset from begin in each buffers. On initialization and + reset, buf->pos and buf->last are positioned at buf->begin + + offset. */ + size_t offset; } nghttp2_bufs; +/* + * This is the same as calling nghttp2_bufs_init2 with the given + * arguments and offset = 0. + */ int nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_length, size_t max_chunk); +/* + * Initializes |bufs|. Each buffer size is given in the + * |chunk_length|. The maximum number of buffers is given in the + * |max_chunk|. Each buffer will have bufs->pos and bufs->last shifted + * to left by |offset| bytes on creation and reset. + * + * This function allocates first buffer. bufs->head and bufs->cur + * will point to the first buffer after this call. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_INVALID_ARGUMENT + * max_chunk is 0 + */ +int nghttp2_bufs_init2(nghttp2_bufs *bufs, size_t chunk_length, + size_t max_chunk, size_t offset); + +/* + * Frees any related resources to the |bufs|. + */ void nghttp2_bufs_free(nghttp2_bufs *bufs); +/* + * Appends the |data| of length |len| to the |bufs|. The write starts + * at bufs->cur->buf.last. A new buffers will be allocated to store + * all data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len); +/* + * Appends a single byte |b| to the |bufs|. The write starts at + * bufs->cur->buf.last. A new buffers will be allocated to store all + * data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ int nghttp2_bufs_addb(nghttp2_bufs *bufs, uint8_t b); +/* + * Behaves like nghttp2_bufs_addb(), but this does not update + * buf->last pointer. + */ +int nghttp2_bufs_addb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_addb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ = B; \ + } while(0) + +#define nghttp2_bufs_fast_addb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last = B; \ + } while(0) + +/* + * Performs bitwise-OR of |b| at bufs->cur->buf.last. A new buffers + * will be allocated if necessary. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_orb(nghttp2_bufs *bufs, uint8_t b); + +/* + * Behaves like nghttp2_bufs_orb(), but does not update buf->last + * pointer. + */ +int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); + +#define nghttp2_bufs_fast_orb(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last++ |= B; \ + } while(0) + +#define nghttp2_bufs_fast_orb_hold(BUFS, B) \ + do { \ + *(BUFS)->cur->buf.last |= B; \ + } while(0) + +/* + * Copies all data stored in |bufs| to the contagious buffer. This + * function allocates the contagious memory to store all data in + * |bufs| and assigns it to |*out|. + * + * On successful return, nghttp2_bufs_len(bufs) returns 0, just like + * after calling nghttp2_bufs_reset(). + + * This function returns the length of copied data and assigns the + * pointer to copied data to |*out| if it succeeds, or one of the + * following negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); +/* + * Resets |bufs| and makes the buffers empty. + */ void nghttp2_bufs_reset(nghttp2_bufs *bufs); +/* + * Moves bufs->cur to bufs->cur->next. If resulting bufs->cur is + * NULL, this function allocates new buffers and bufs->cur points to + * it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. + */ +int nghttp2_bufs_advance(nghttp2_bufs *bufs); + +/* Sets bufs->cur to bufs->head */ +#define nghttp2_bufs_rewind(BUFS) \ + do { \ + (BUFS)->cur = (BUFS)->head; \ + } while(0) + +/* + * Move bufs->cur, from the current position, using next member, to + * the last buf which has nghttp2_buf_len(buf) > 0 without seeing buf + * which satisfies nghttp2_buf_len(buf) == 0. If bufs->cur->next is + * NULL, bufs->cur is unchanged. + */ +void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); + +/* + * Returns nonzero if bufs->cur->next is not emtpy. + */ +int nghttp2_bufs_next_present(nghttp2_bufs *bufs); + +#define nghttp2_bufs_cur_avail(BUFS) nghttp2_buf_avail(&(BUFS)->cur->buf) + +/* + * Returns the buffer length of |bufs|. + */ +ssize_t nghttp2_bufs_len(nghttp2_bufs *bufs); + #endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index 6a08be97..2b299621 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -224,64 +224,109 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) } } -ssize_t nghttp2_frame_pack_headers(nghttp2_buf *buf, - nghttp2_headers *frame, - nghttp2_hd_deflater *deflater) +/* + * Call this function after payload was serialized, but not before + * changing buf->pos and serializing frame header. + * + * This function assumes bufs->cur points to the last buf chain of the + * frame(s). + * + * This function serializes frame header for HEADERS/PUSH_PROMISE and + * handles their successive CONTINUATION frames. + * + * We don't process any padding here. + */ +static int frame_pack_headers_shared(nghttp2_bufs *bufs, + nghttp2_frame_hd *frame_hd) +{ + nghttp2_buf *buf; + nghttp2_buf_chain *ci, *ce; + nghttp2_frame_hd hd; + + buf = &bufs->head->buf; + + hd = *frame_hd; + hd.length = nghttp2_buf_len(buf); + + DEBUGF(fprintf(stderr, "HEADERS/PUSH_PROMISE, payloadlen=%zu\n", hd.length)); + + /* We have multiple frame buffers, which means one or more + CONTINUATION frame is involved. Remove END_HEADERS flag from the + first frame. */ + if(bufs->head != bufs->cur) { + hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + + if(bufs->head != bufs->cur) { + /* 2nd and later frames are CONTINUATION frames. */ + hd.type = NGHTTP2_CONTINUATION; + /* We don't have no flags except for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_NONE; + + ce = bufs->cur; + + for(ci = bufs->head->next; ci != ce; ci = ci->next) { + buf = &ci->buf; + + hd.length = nghttp2_buf_len(buf); + + DEBUGF(fprintf(stderr, "int CONTINUATION, payloadlen=%zu\n", hd.length)); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + buf = &ci->buf; + hd.length = nghttp2_buf_len(buf); + /* Set END_HEADERS flag for last CONTINUATION */ + hd.flags = NGHTTP2_FLAG_END_HEADERS; + + DEBUGF(fprintf(stderr, "last CONTINUATION, payloadlen=%zu\n", hd.length)); + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + } + + return 0; +} + +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, + nghttp2_headers *frame, + nghttp2_hd_deflater *deflater) { size_t nv_offset; ssize_t rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - /* Account for possible PAD_HIGH and PAD_LOW */ - buf->pos += 2; + nv_offset = nghttp2_frame_headers_payload_nv_offset(frame); - nv_offset = - NGHTTP2_FRAME_HDLEN + nghttp2_frame_headers_payload_nv_offset(frame); - - /* If frame->nvlen == 0, nghttp2_buf_len(buf) may be smaller than - nv_offset */ - rv = nghttp2_buf_pos_reserve(buf, nv_offset); - if(rv < 0) { - return rv; - } + buf = &bufs->cur->buf; buf->pos += nv_offset; buf->last = buf->pos; /* This call will adjust buf->last to the correct position */ - rv = nghttp2_hd_deflate_hd(deflater, buf, frame->nva, frame->nvlen); + rv = nghttp2_hd_deflate_hd(deflater, bufs, frame->nva, frame->nvlen); + buf->pos -= nv_offset; - if(rv < 0) { + if(rv != 0) { return rv; } - frame->hd.length = nghttp2_frame_headers_payload_nv_offset(frame) + rv; - frame->padlen = 0; - - /* Don't use buf->last, since it already points to the end of the - frame */ - memset(buf->pos, 0, NGHTTP2_FRAME_HDLEN); - - /* pack frame header after length is determined */ - if(NGHTTP2_MAX_PAYLOADLEN < frame->hd.length) { - /* Needs CONTINUATION */ - nghttp2_frame_hd hd = frame->hd; - - hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; - hd.length = NGHTTP2_MAX_PAYLOADLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - } else { - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - } - if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_put_uint32be(buf->pos + NGHTTP2_FRAME_HDLEN, frame->pri); + nghttp2_put_uint32be(buf->pos, frame->pri); } - return nghttp2_buf_len(buf); + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); + + return frame_pack_headers_shared(bufs, &frame->hd); } int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, @@ -298,28 +343,21 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, return 0; } -ssize_t nghttp2_frame_pack_priority(nghttp2_buf *buf, - nghttp2_priority *frame) +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, nghttp2_priority *frame) { - ssize_t framelen= NGHTTP2_FRAME_HDLEN + 4; - int rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; - } + buf = &bufs->head->buf; + buf->pos -= NGHTTP2_FRAME_HDLEN; - memset(buf->last, 0, framelen); - - nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); - buf->last += NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, frame->pri); buf->last += 4; - return nghttp2_buf_len(buf); + return 0; } void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, @@ -329,28 +367,22 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, frame->pri = nghttp2_get_uint32(payload) & NGHTTP2_PRIORITY_MASK; } -ssize_t nghttp2_frame_pack_rst_stream(nghttp2_buf *buf, - nghttp2_rst_stream *frame) +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame) { - ssize_t framelen = NGHTTP2_FRAME_HDLEN + 4; - int rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; - } + buf = &bufs->head->buf; + buf->pos -= NGHTTP2_FRAME_HDLEN; - memset(buf->last, 0, framelen); - - nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); - buf->last += NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, frame->error_code); buf->last += 4; - return nghttp2_buf_len(buf); + return 0; } void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, @@ -360,28 +392,26 @@ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, frame->error_code = nghttp2_get_uint32(payload); } -ssize_t nghttp2_frame_pack_settings(nghttp2_buf *buf, - nghttp2_settings *frame) +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame) { - ssize_t framelen = NGHTTP2_FRAME_HDLEN + frame->hd.length; - int rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; + buf = &bufs->head->buf; + + if(nghttp2_buf_avail(buf) < (ssize_t)frame->hd.length) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; } - memset(buf->last, 0, framelen); + buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); - buf->last += NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); buf->last += nghttp2_frame_pack_settings_payload(buf->last, frame->iv, frame->niv); - return nghttp2_buf_len(buf); + return 0; } size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, @@ -436,60 +466,36 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, return 0; } -ssize_t nghttp2_frame_pack_push_promise(nghttp2_buf *buf, - nghttp2_push_promise *frame, - nghttp2_hd_deflater *deflater) +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater) { - size_t nv_offset = NGHTTP2_FRAME_HDLEN + 4; + size_t nv_offset = 4; ssize_t rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - /* Account for possible PAD_HIGH and PAD_LOW */ - buf->pos += 2; - - /* If frame->nvlen == 0, nghttp2_buf_len(buf) may be smaller than - nv_offset */ - rv = nghttp2_buf_pos_reserve(buf, nv_offset); - if(rv < 0) { - return rv; - } + buf = &bufs->cur->buf; buf->pos += nv_offset; buf->last = buf->pos; /* This call will adjust buf->last to the correct position */ - rv = nghttp2_hd_deflate_hd(deflater, buf, frame->nva, frame->nvlen); + rv = nghttp2_hd_deflate_hd(deflater, bufs, frame->nva, frame->nvlen); + buf->pos -= nv_offset; - if(rv < 0) { + if(rv != 0) { return rv; } - frame->hd.length = 4 + rv; + nghttp2_put_uint32be(buf->pos, frame->promised_stream_id); + frame->padlen = 0; + frame->hd.length = nghttp2_bufs_len(bufs); - /* Don't use buf->last, since it already points to the end of the - frame */ - memset(buf->pos, 0, NGHTTP2_FRAME_HDLEN); - - /* pack frame header after length is determined */ - if(NGHTTP2_MAX_PAYLOADLEN < frame->hd.length) { - /* Needs CONTINUATION */ - nghttp2_frame_hd hd = frame->hd; - - hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; - hd.length = NGHTTP2_MAX_PAYLOADLEN; - - nghttp2_frame_pack_frame_hd(buf->pos, &hd); - } else { - nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - } - - nghttp2_put_uint32be(buf->pos + NGHTTP2_FRAME_HDLEN, - frame->promised_stream_id); - - return nghttp2_buf_len(buf); + return frame_pack_headers_shared(bufs, &frame->hd); } int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, @@ -503,27 +509,21 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, return 0; } -ssize_t nghttp2_frame_pack_ping(nghttp2_buf *buf, nghttp2_ping *frame) +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame) { - ssize_t framelen = NGHTTP2_FRAME_HDLEN + 8; - int rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; - } + buf = &bufs->head->buf; + buf->pos -= NGHTTP2_FRAME_HDLEN; - memset(buf->last, 0, framelen); + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); - nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); - buf->last += NGHTTP2_FRAME_HDLEN; + buf->last = nghttp2_cpymem(buf->last, frame->opaque_data, + sizeof(frame->opaque_data)); - memcpy(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); - buf->last += sizeof(frame->opaque_data); - - return nghttp2_buf_len(buf); + return 0; } void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, @@ -533,22 +533,18 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); } -ssize_t nghttp2_frame_pack_goaway(nghttp2_buf *buf, nghttp2_goaway *frame) +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame) { - ssize_t framelen = NGHTTP2_FRAME_HDLEN + frame->hd.length; int rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; - } + buf = &bufs->head->buf; - memset(buf->last, 0, framelen); + buf->pos -= NGHTTP2_FRAME_HDLEN; - nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); - buf->last += NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, frame->last_stream_id); buf->last += 4; @@ -556,10 +552,12 @@ ssize_t nghttp2_frame_pack_goaway(nghttp2_buf *buf, nghttp2_goaway *frame) nghttp2_put_uint32be(buf->last, frame->error_code); buf->last += 4; - memcpy(buf->last, frame->opaque_data, frame->opaque_data_len); - buf->last += frame->opaque_data_len; + rv = nghttp2_bufs_add(bufs, frame->opaque_data, frame->opaque_data_len); + if(rv != 0) { + return rv; + } - return nghttp2_buf_len(buf); + return 0; } void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, @@ -573,28 +571,22 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, frame->opaque_data_len = 0; } -ssize_t nghttp2_frame_pack_window_update(nghttp2_buf *buf, - nghttp2_window_update *frame) +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame) { - ssize_t framelen = NGHTTP2_FRAME_HDLEN + 4; - int rv; + nghttp2_buf *buf; - assert(nghttp2_buf_len(buf) == 0); + assert(bufs->head == bufs->cur); - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; - } + buf = &bufs->head->buf; + buf->pos -= NGHTTP2_FRAME_HDLEN; - memset(buf->last, 0, framelen); - - nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); - buf->last += NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); nghttp2_put_uint32be(buf->last, frame->window_size_increment); buf->last += 4; - return nghttp2_buf_len(buf); + return 0; } void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, @@ -737,34 +729,158 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) return 1; } -void nghttp2_frame_set_pad(nghttp2_buf *buf, uint8_t *flags_ptr, size_t padlen) +static void frame_set_pad(nghttp2_buf *buf, size_t padlen) { - size_t trail_padlen = 0; + size_t trail_padlen; if(padlen > 256) { - uint8_t *p; + DEBUGF(fprintf(stderr, "padlen=%zu, shift left 2 bytes\n", padlen)); + + memmove(buf->pos - 2, buf->pos, NGHTTP2_FRAME_HDLEN); + + buf->pos -= 2; + + buf->pos[3] |= NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW; + + nghttp2_put_uint16be(buf->pos, nghttp2_get_uint16(buf->pos) + padlen); trail_padlen = padlen - 2; - *flags_ptr |= NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW; - - assert(nghttp2_buf_pos_offset(buf) >= 2); - - /* Consume previous 2 bytes, shifting 2 bytes to the left */ - nghttp2_buf_shift_left(buf, 2); - - p = buf->pos + NGHTTP2_FRAME_HDLEN; - *p++ = trail_padlen >> 8; - *p = trail_padlen & 0xff; + buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen >> 8; + buf->pos[NGHTTP2_FRAME_HDLEN + 1] = trail_padlen & 0xff; } else if(padlen > 0) { - assert(nghttp2_buf_pos_offset(buf) >= 1); + DEBUGF(fprintf(stderr, "padlen=%zu, shift left 1 bytes\n", padlen)); - /* Consume previous 1 byte, shifting 1 bytes to the left */ - nghttp2_buf_shift_left(buf, 1); + memmove(buf->pos - 1, buf->pos, NGHTTP2_FRAME_HDLEN); + + --buf->pos; + + buf->pos[3] |= NGHTTP2_FLAG_PAD_LOW; + + nghttp2_put_uint16be(buf->pos, nghttp2_get_uint16(buf->pos) + padlen); trail_padlen = padlen - 1; - *flags_ptr |= NGHTTP2_FLAG_PAD_LOW; + buf->pos[NGHTTP2_FRAME_HDLEN] = trail_padlen; - *(buf->pos + NGHTTP2_FRAME_HDLEN) = trail_padlen; + } else { + DEBUGF(fprintf(stderr, "padlen=0, no shift left was made\n")); + + return; } + + /* zero out padding */ + memset(buf->last, 0, trail_padlen); + /* extend buffers trail_padlen bytes, since we ate previous padlen - + trail_padlen byte(s) */ + buf->last += trail_padlen; + + return; +} + +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, nghttp2_frame_type type) +{ + int rv; + size_t trail_padlen; + size_t last_avail; + nghttp2_buf *buf; + nghttp2_frame_hd last_hd; + + if(padlen == 0) { + DEBUGF(fprintf(stderr, "padlen = 0, nothing to do\n")); + + return 0; + } + + /* + * We have arranged bufs like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | |Frame header | Frame payload... | + * +-+-+---------------+-------------------------------------------+ + * | | |Frame header | Frame payload... | + * +-+-+---------------+-------------------------------------------+ + * | | |Frame header | Frame payload... | + * +-+-+---------------+-------------------------------------------+ + * + * Since we limit the length of the padding bytes, they are + * completely included in one frame payload or across 2 frames. We + * are going to adjust buf->pos of frame which includes part of + * padding. And serialize (memmove) frame header in the correct + * position. Also extends buf->last to include padding. + */ + + nghttp2_bufs_seek_last_present(bufs); + + buf = &bufs->cur->buf; + + last_avail = nghttp2_buf_avail(buf); + + if(last_avail >= padlen) { + /* Last frame can include all paddings bytes */ + DEBUGF(fprintf(stderr, "last frame includes all paddings\n")); + + frame_set_pad(buf, padlen); + + } else { + /* padding across 2 frames */ + + /* type = DATA must not be here */ + assert(type == NGHTTP2_CONTINUATION); + + /* This will seek to the last chain */ + rv = nghttp2_bufs_advance(bufs); + if(rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_FRAME_SIZE_ERROR; + } + + if(rv != 0) { + return rv; + } + + if(type == NGHTTP2_CONTINUATION) { + /* former last frame has END_HEADERS flag set. Clear it. */ + buf->pos[3] &= ~NGHTTP2_FLAG_END_HEADERS; + } + + trail_padlen = nghttp2_buf_avail(buf); + + /* former last frame may have zero buffer available */ + if(trail_padlen == 0) { + DEBUGF(fprintf(stderr, "last frame has no space to include padding\n")); + } else { + DEBUGF(fprintf(stderr, "padding across 2 frames\n")); + + frame_set_pad(buf, trail_padlen); + } + + /* This buffer does not have frame header serialized */ + buf = &bufs->cur->buf; + + trail_padlen = padlen - trail_padlen; + + last_hd.length = 0; + last_hd.type = type; + last_hd.stream_id = hd->stream_id; + + if(type == NGHTTP2_CONTINUATION) { + last_hd.flags = NGHTTP2_FLAG_END_HEADERS; + } else { + last_hd.flags = NGHTTP2_FLAG_NONE; + } + + buf->pos -= NGHTTP2_FRAME_HDLEN; + nghttp2_frame_pack_frame_hd(buf->pos, &last_hd); + + frame_set_pad(buf, trail_padlen); + } + + hd->length += padlen; + + DEBUGF(fprintf(stderr, "final payloadlen=%zu, padlen=%zu\n", + hd->length, padlen)); + + return 0; } diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index 18dd001b..983cad8f 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -39,17 +39,21 @@ #define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) #define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) -/* The maximum payload length of a frame TODO: Must be renamed as - NGHTTP2_MAX_PAYLOAD_LENGTH */ -#define NGHTTP2_MAX_PAYLOADLEN ((1 << 14) - 1) +/* The number of bytes of frame header. */ +#define NGHTTP2_FRAME_HDLEN 8 + +/* The maximum frame and payload length. The spec allows maximum + payload length up to 16383 bytes. Due to efficient buffer + allocation, we choose smaller buffer. Actual payload limit offsets + frame header and possible PAD_HIGH and PAD_LOW to ease + serialization and save memcopying. */ +#define NGHTTP2_MAX_FRAMELEN 8192 +#define NGHTTP2_MAX_PAYLOADLEN (NGHTTP2_MAX_FRAMELEN - NGHTTP2_FRAME_HDLEN - 2) /* The maximum length of DATA frame payload. To fit entire DATA frame into 4096K buffer, we use subtract header size (8 bytes) + 2 bytes padding. See nghttp2_session_pack_data(). */ -#define NGHTTP2_DATA_PAYLOAD_LENGTH 4086 - -/* The number of bytes of frame header. */ -#define NGHTTP2_FRAME_HDLEN 8 +#define NGHTTP2_DATA_PAYLOAD_LENGTH (4096 - NGHTTP2_FRAME_HDLEN - 2) /* The number of bytes for each SETTINGS entry */ #define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 5 @@ -101,33 +105,29 @@ void nghttp2_frame_unpack_frame_hd(nghttp2_frame_hd *hd, const uint8_t* buf); size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); /* - * Packs HEADERS frame |frame| in wire format and store it in |buf|. - * This function expands |buf| as necessary to store frame. The caller - * must make sure that nghttp2_buf_len(buf) == 0 holds when calling - * this function. + * Packs HEADERS frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. * - * The first byte the frame is serialized is returned in the |buf|. + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. * * frame->hd.length is assigned after length is determined during - * packing process. If payload length is strictly larger than - * NGHTTP2_MAX_PAYLOADLEN, payload data is still serialized as is, but - * serialized header's payload length is set to NGHTTP2_MAX_PAYLOADLEN - * and NGHTTP2_FLAG_END_HEADERS flag is cleared. + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. * - * This function returns the size of packed frame (which equals to - * nghttp2_buf_len(buf)) if it succeeds, or returns one of the + * This function returns 0 if it succeeds, or returns one of the * following negative error codes: * * NGHTTP2_ERR_HEADER_COMP * The deflate operation failed. - * NGHTTP2_ERR_FRAME_TOO_LARGE + * NGHTTP2_ERR_FRAME_SIZE_ERROR * The length of the frame is too large. * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_headers(nghttp2_buf *buf, - nghttp2_headers *frame, - nghttp2_hd_deflater *deflater); +int nghttp2_frame_pack_headers(nghttp2_bufs *bufs, + nghttp2_headers *frame, + nghttp2_hd_deflater *deflater); /* * Unpacks HEADERS frame byte sequence into |frame|. This function @@ -145,8 +145,10 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, /* * Packs PRIORITY frame |frame| in wire format and store it in - * |buf|. This function expands |buf| as necessary to store given - * |frame|. + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. * * This function returns 0 if it succeeds or one of the following * negative error codes: @@ -154,8 +156,8 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_priority(nghttp2_buf *buf, - nghttp2_priority *frame); +int nghttp2_frame_pack_priority(nghttp2_bufs *bufs, + nghttp2_priority *frame); /* * Unpacks PRIORITY wire format into |frame|. @@ -166,17 +168,19 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, /* * Packs RST_STREAM frame |frame| in wire frame format and store it in - * |buf|. This function expands |buf| as necessary to store given - * |frame|. + * |bufs|. * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_rst_stream(nghttp2_buf *buf, - nghttp2_rst_stream *frame); +int nghttp2_frame_pack_rst_stream(nghttp2_bufs *bufs, + nghttp2_rst_stream *frame); /* * Unpacks RST_STREAM frame byte sequence into |frame|. @@ -187,17 +191,20 @@ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, /* * Packs SETTINGS frame |frame| in wire format and store it in - * |buf|. This function expands |buf| as necessary to store given - * |frame|. + * |bufs|. * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. */ -ssize_t nghttp2_frame_pack_settings(nghttp2_buf *buf, - nghttp2_settings *frame); +int nghttp2_frame_pack_settings(nghttp2_bufs *bufs, nghttp2_settings *frame); /* * Packs the |iv|, which includes |niv| entries, in the |buf|, @@ -245,32 +252,29 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, /* * Packs PUSH_PROMISE frame |frame| in wire format and store it in - * |buf|. This function expands |buf| as necessary to store - * frame. The caller must make sure that nghttp2_buf_len(buf) == 0 - * holds when calling this function. + * |bufs|. This function expands |bufs| as necessary to store + * frame. * - * The first byte the frame is serialized is returned in the |buf|. + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. * * frame->hd.length is assigned after length is determined during - * packing process. If payload length is strictly larger than - * NGHTTP2_MAX_PAYLOADLEN, payload data is still serialized as is, but - * serialized header's payload length is set to NGHTTP2_MAX_PAYLOADLEN - * and NGHTTP2_FLAG_END_HEADERS flag is cleared. + * packing process. CONTINUATION frames are also serialized in this + * function. This function does not handle padding. * - * This function returns the size of packed frame (which equals to - * nghttp2_buf_len(buf)) if it succeeds, or returns one of the + * This function returns 0 if it succeeds, or returns one of the * following negative error codes: * * NGHTTP2_ERR_HEADER_COMP * The deflate operation failed. - * NGHTTP2_ERR_FRAME_TOO_LARGE + * NGHTTP2_ERR_FRAME_SIZE_ERROR * The length of the frame is too large. * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_push_promise(nghttp2_buf *buf, - nghttp2_push_promise *frame, - nghttp2_hd_deflater *deflater); +int nghttp2_frame_pack_push_promise(nghttp2_bufs *bufs, + nghttp2_push_promise *frame, + nghttp2_hd_deflater *deflater); /* * Unpacks PUSH_PROMISE frame byte sequence into |frame|. This function @@ -287,8 +291,11 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, size_t payloadlen); /* - * Packs PING frame |frame| in wire format and store it in |buf|. This - * function expands |buf| as necessary to store given |frame|. + * Packs PING frame |frame| in wire format and store it in + * |bufs|. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. * * This function returns 0 if it succeeds or one of the following * negative error codes: @@ -296,7 +303,7 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_ping(nghttp2_buf *buf, nghttp2_ping *frame); +int nghttp2_frame_pack_ping(nghttp2_bufs *bufs, nghttp2_ping *frame); /* * Unpacks PING wire format into |frame|. @@ -306,17 +313,21 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, size_t payloadlen); /* - * Packs GOAWAY frame |frame | in wire format and store it in - * |buf|. This function expands |buf| as necessary to store given - * |frame|. + * Packs GOAWAY frame |frame| in wire format and store it in |bufs|. + * This function expands |bufs| as necessary to store frame. + * + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. * * This function returns 0 if it succeeds or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. + * NGHTTP2_ERR_FRAME_SIZE_ERROR + * The length of the frame is too large. */ -ssize_t nghttp2_frame_pack_goaway(nghttp2_buf *buf, nghttp2_goaway *frame); +int nghttp2_frame_pack_goaway(nghttp2_bufs *bufs, nghttp2_goaway *frame); /* * Unpacks GOAWAY wire format into |frame|. @@ -327,17 +338,19 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, /* * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it - * in |buf|. This function expands |buf| as necessary to store given - * |frame|. + * in |bufs|. * - * This function returns the size of packed frame if it succeeds, or - * returns one of the following negative error codes: + * The caller must make sure that nghttp2_bufs_reset(bufs) is called + * before calling this function. + * + * This function returns 0 if it succeeds, or returns one of the + * following negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_window_update(nghttp2_buf *buf, - nghttp2_window_update *frame); +int nghttp2_frame_pack_window_update(nghttp2_bufs *bufs, + nghttp2_window_update *frame); /* * Unpacks WINDOW_UPDATE frame byte sequence into |frame|. @@ -486,66 +499,21 @@ void nghttp2_nv_array_del(nghttp2_nv *nva); int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); /* - * Sets PAD_HIGH and PAD_LOW fields, flags and adjust buf->pos and - * buf->last accordingly based on given padding length. The padding is - * given in the |padlen|. + * Sets PAD_HIGH and PAD_LOW fields, flags and adjust frame header + * position of each buffers in |bufs|. The padding is given in the + * |padlen|. The |hd| is the frame header for the serialized data. + * The |type| is used as a frame type when padding requires additional + * buffers. * - * The |*flags_ptr| is updated to include NGHTTP2_FLAG_PAD_LOW and - * NGHTTP2_FLAG_PAD_HIGH based on the padding length. + * This function returns 0 if it succeeds, or one of the following + * negative error codes: * - * This function does not allocate memory at all. - * - * The padding specifier PAD_HIGH and PAD_LOW are located right after - * the frame header. But they may not be there depending of the length - * of the padding. To save the additional buffer copy, we shift - * buf->pos to 2 bytes right before this call. Depending of the length - * of the padding, we shift left buf->pos and buf->last. If more than - * or equal to 256 padding is made, 2 left shift is done |buf| looks - * like this: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Frame header ... - * +---------------------------------------------------------------+ - * . Frame header | - * +---------------+---------------+-------------------------------+ - * | Pad high | Pad low | Payload ... - * +---------------+---------------+-------------------------------+ - * - * - * If padding is less than 256 but strictly more than 0, the |buf| is - * 1 left shift and the |buf| looks like this: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Unused | Frame header ... - * +---------------+-----------------------------------------------+ - * . Frame header ... - * +---------------+---------------+-------------------------------+ - * . Frame Header | Pad low | Payload ... - * +---------------+---------------+-------------------------------+ - * - * If no padding is added, no shift is done and the |buf| looks like - * this: - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | Unused | Frame header ... - * +-------------------------------+-------------------------------+ - * . Frame header ... - * +-------------------------------+-------------------------------+ - * . Frame Header | Payload ... - * +-------------------------------+-------------------------------+ - * - * Notice that the position of payload does not change. This way, we - * can set PAD_HIGH and PAD_LOW after payload was serialized and no - * additional copy operation is required (if the |buf| is large enough - * to account the additional padding, of course). + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. This is not a fatal. */ -void nghttp2_frame_set_pad(nghttp2_buf *buf, uint8_t *flags_ptr, - size_t padlen); +int nghttp2_frame_add_pad(nghttp2_bufs *bufs, nghttp2_frame_hd *hd, + size_t padlen, nghttp2_frame_type type); #endif /* NGHTTP2_FRAME_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index db1c92b1..96df4ad7 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -421,24 +421,6 @@ static int emit_literal_header(nghttp2_nv *nv_out, nghttp2_nv *nv) return 0; } -static int ensure_write_buffer(nghttp2_buf *buf, size_t need) -{ - int rv; - - need += nghttp2_buf_last_offset(buf); - - if(need > NGHTTP2_HD_MAX_BUFFER_LENGTH) { - return NGHTTP2_ERR_HEADER_COMP; - } - - rv = nghttp2_buf_reserve(buf, need); - if(rv != 0) { - return NGHTTP2_ERR_NOMEM; - } - - return 0; -} - static size_t count_encoded_length(size_t n, int prefix) { size_t k = (1 << prefix) - 1; @@ -545,90 +527,116 @@ static uint8_t* decode_length(ssize_t *res, int *final, ssize_t initial, return in + 1; } -static int emit_clear_refset(nghttp2_buf *buf) +static int nghttp2_hd_handle_buffer_error(int rv) +{ + if(rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_HEADER_COMP; + } + return rv; +} + +static int emit_clear_refset(nghttp2_bufs *bufs) { int rv; - uint8_t *bufp; + uint8_t sb[] = {0x80u, 0x80u}; - rv = ensure_write_buffer(buf, 2); + rv = nghttp2_bufs_add(bufs, sb, sizeof(sb)); if(rv != 0) { - return rv; + return nghttp2_hd_handle_buffer_error(rv); } - bufp = buf->last; - *bufp++ = 0x80u; - *bufp++ = 0x80u; - - buf->last = bufp; - return 0; } -static int emit_table_size(nghttp2_buf *buf, size_t table_size) +static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { int rv; uint8_t *bufp; size_t blocklen; + uint8_t sb[16]; blocklen = 1 + count_encoded_length(table_size, 7); - rv = ensure_write_buffer(buf, blocklen); - if(rv != 0) { - return rv; + if(sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; } DEBUGF(fprintf(stderr, "emit table_size=%zu\n", table_size)); - bufp = buf->last; + bufp = sb; *bufp++ = 0x80u; *bufp = 0; encode_length(bufp, table_size, 7); - buf->last += blocklen; + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if(rv != 0) { + return nghttp2_hd_handle_buffer_error(rv); + } return 0; } -static int emit_indexed_block(nghttp2_buf *buf, size_t index) +static int emit_indexed_block(nghttp2_bufs *bufs, size_t index) { int rv; size_t blocklen; + uint8_t sb[16]; + uint8_t *bufp; blocklen = count_encoded_length(index + 1, 7); - rv = ensure_write_buffer(buf, blocklen); - if(rv != 0) { - return rv; + if(sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; } - *buf->last = 0x80u; - encode_length(buf->last, index + 1, 7); + bufp = sb; + *bufp = 0x80u; + encode_length(bufp, index + 1, 7); - buf->last += blocklen; + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if(rv != 0) { + return nghttp2_hd_handle_buffer_error(rv); + } return 0; } -static size_t emit_string(uint8_t *buf, size_t buflen, - size_t enclen, int huffman, - const uint8_t *str, size_t len) +static int emit_string(nghttp2_bufs *bufs, + size_t enclen, int huffman, + const uint8_t *str, size_t len) { size_t rv; + uint8_t sb[16]; + uint8_t *bufp; + size_t blocklen; + + blocklen = count_encoded_length(enclen, 7); + + if(sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } + + bufp = sb; + *bufp = huffman ? 1 << 7 : 0; + rv = encode_length(bufp, enclen, 7); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if(rv != 0) { + return nghttp2_hd_handle_buffer_error(rv); + } - *buf = huffman ? 1 << 7 : 0; - rv = encode_length(buf, enclen, 7); - buf += rv; if(huffman) { - nghttp2_hd_huff_encode(buf, buflen - rv, str, len); + rv = nghttp2_hd_huff_encode(bufs, str, len); } else { assert(enclen == len); - memcpy(buf, str, len); + rv = nghttp2_bufs_add(bufs, str, len); } - return rv + enclen; + + return nghttp2_hd_handle_buffer_error(rv); } -static int emit_indname_block(nghttp2_buf *buf, size_t index, +static int emit_indname_block(nghttp2_bufs *bufs, size_t index, const uint8_t *value, size_t valuelen, int inc_indexing) { @@ -637,6 +645,7 @@ static int emit_indname_block(nghttp2_buf *buf, size_t index, size_t encvallen; size_t blocklen; int huffman; + uint8_t sb[16]; encvallen = nghttp2_hd_huff_encode_count(value, valuelen); blocklen = count_encoded_length(index + 1, 6); @@ -646,41 +655,39 @@ static int emit_indname_block(nghttp2_buf *buf, size_t index, encvallen = valuelen; } - blocklen += count_encoded_length(encvallen, 7) + encvallen; + if(sizeof(sb) < blocklen) { + return NGHTTP2_ERR_HEADER_COMP; + } - rv = ensure_write_buffer(buf, blocklen); + bufp = sb; + + *bufp = inc_indexing ? 0 : 0x40u; + bufp += encode_length(bufp, index + 1, 6); + + rv = nghttp2_bufs_add(bufs, sb, blocklen); + if(rv != 0) { + return nghttp2_hd_handle_buffer_error(rv); + } + + rv = emit_string(bufs, encvallen, huffman, value, valuelen); if(rv != 0) { return rv; } - bufp = buf->last; - - *bufp = inc_indexing ? 0 : 0x40u; - bufp += encode_length(bufp, index + 1, 6); - bufp += emit_string(bufp, buf->end - bufp, - encvallen, huffman, value, valuelen); - - assert(bufp - buf->last == (ssize_t)blocklen); - - buf->last = bufp; - return 0; } -static int emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, +static int emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, int inc_indexing) { int rv; - uint8_t *bufp; size_t encnamelen; size_t encvallen; - size_t blocklen; int name_huffman; int value_huffman; encnamelen = nghttp2_hd_huff_encode_count(nv->name, nv->namelen); encvallen = nghttp2_hd_huff_encode_count(nv->value, nv->valuelen); - blocklen = 1; name_huffman = encnamelen < nv->namelen; value_huffman = encvallen < nv->valuelen; @@ -691,25 +698,20 @@ static int emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, encvallen = nv->valuelen; } - blocklen += count_encoded_length(encnamelen, 7) + encnamelen + - count_encoded_length(encvallen, 7) + encvallen; + rv = nghttp2_bufs_addb(bufs, inc_indexing ? 0 : 0x40u); + if(rv != 0) { + return nghttp2_hd_handle_buffer_error(rv); + } - rv = ensure_write_buffer(buf, blocklen); + rv = emit_string(bufs, encnamelen, name_huffman, nv->name, nv->namelen); if(rv != 0) { return rv; } - bufp = buf->last; - - *bufp++ = inc_indexing ? 0 : 0x40u; - bufp += emit_string(bufp, buf->end - bufp, - encnamelen, name_huffman, nv->name, nv->namelen); - bufp += emit_string(bufp, buf->end - bufp, - encvallen, value_huffman, nv->value, nv->valuelen); - - assert(bufp - buf->last == (ssize_t)blocklen); - - buf->last = bufp; + rv = emit_string(bufs, encvallen, value_huffman, nv->value, nv->valuelen); + if(rv != 0) { + return rv; + } return 0; } @@ -718,12 +720,12 @@ static int emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, * Emit common header with |index| by toggle off and on (thus 2 * indexed representation emissions). */ -static int emit_implicit(nghttp2_buf *buf, size_t index) +static int emit_implicit(nghttp2_bufs *bufs, size_t index) { int i, rv; for(i = 0; i < 2; ++i) { - rv = emit_indexed_block(buf, index); + rv = emit_indexed_block(bufs, index); if(rv != 0) { return rv; } @@ -732,7 +734,7 @@ static int emit_implicit(nghttp2_buf *buf, size_t index) } static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, - nghttp2_buf *buf, + nghttp2_bufs *bufs, nghttp2_nv *nv, uint8_t entry_flags) { @@ -754,7 +756,7 @@ static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, /* Emit common header just before it slips away from the table. If we don't do this, we have to emit it in literal representation which hurts compression. */ - rv = emit_implicit(buf, index); + rv = emit_implicit(bufs, index); if(rv != 0) { return NULL; } @@ -974,7 +976,7 @@ static int hd_deflate_should_indexing(nghttp2_hd_deflater *deflater, } static int deflate_nv(nghttp2_hd_deflater *deflater, - nghttp2_buf *buf, nghttp2_nv *nv) + nghttp2_bufs *bufs, nghttp2_nv *nv) { int rv; nghttp2_hd_entry *ent; @@ -992,7 +994,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, /* It is important to first add entry to the header table and let eviction go. If NGHTTP2_HD_FLAG_IMPLICIT_EMIT entry is evicted, it must be emitted before the |nv|. */ - new_ent = add_hd_table_incremental(&deflater->ctx, buf, &ent->nv, + new_ent = add_hd_table_incremental(&deflater->ctx, bufs, &ent->nv, NGHTTP2_HD_FLAG_NONE); if(!new_ent) { return NGHTTP2_ERR_HEADER_COMP; @@ -1006,13 +1008,13 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, set */ new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; } - rv = emit_indexed_block(buf, index); + rv = emit_indexed_block(bufs, index); if(rv != 0) { return rv; } } else if((ent->flags & NGHTTP2_HD_FLAG_REFSET) == 0) { ent->flags |= NGHTTP2_HD_FLAG_REFSET | NGHTTP2_HD_FLAG_EMIT; - rv = emit_indexed_block(buf, index); + rv = emit_indexed_block(bufs, index); if(rv != 0) { return rv; } @@ -1042,7 +1044,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, ent->flags |= NGHTTP2_HD_FLAG_IMPLICIT_EMIT; } for(; num_emits > 0; --num_emits) { - rv = emit_indexed_block(buf, index); + rv = emit_indexed_block(bufs, index); if(rv != 0) { break; } @@ -1060,10 +1062,10 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_nv nv_indname; nv_indname = *nv; nv_indname.name = nghttp2_hd_table_get(&deflater->ctx, index)->nv.name; - new_ent = add_hd_table_incremental(&deflater->ctx, buf, &nv_indname, + new_ent = add_hd_table_incremental(&deflater->ctx, bufs, &nv_indname, NGHTTP2_HD_FLAG_VALUE_ALLOC); } else { - new_ent = add_hd_table_incremental(&deflater->ctx, buf, nv, + new_ent = add_hd_table_incremental(&deflater->ctx, bufs, nv, NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC); } @@ -1081,9 +1083,9 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, incidx = 1; } if(index == -1) { - rv = emit_newname_block(buf, nv, incidx); + rv = emit_newname_block(bufs, nv, incidx); } else { - rv = emit_indname_block(buf, index, nv->value, nv->valuelen, incidx); + rv = emit_indname_block(bufs, index, nv->value, nv->valuelen, incidx); } if(rv != 0) { return rv; @@ -1094,7 +1096,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent, size_t index, - nghttp2_buf *buf) + nghttp2_bufs *bufs) { int rv; @@ -1105,7 +1107,7 @@ static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent, be removed. */ ent->flags ^= NGHTTP2_HD_FLAG_REFSET; - rv = emit_indexed_block(buf, index); + rv = emit_indexed_block(bufs, index); if(rv != 0) { return rv; } @@ -1116,22 +1118,20 @@ static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent, return 0; } -ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - nghttp2_buf *buf, - nghttp2_nv *nv, size_t nvlen) +int nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, + nghttp2_nv *nv, size_t nvlen) { size_t i; int rv = 0; - assert(nghttp2_buf_len(buf) == 0); - if(deflater->ctx.bad) { return NGHTTP2_ERR_HEADER_COMP; } if(deflater->ctx.hd_table_bufsize_max > deflater->deflate_hd_table_bufsize_max) { - rv = emit_table_size(buf, deflater->deflate_hd_table_bufsize_max); + rv = emit_table_size(bufs, deflater->deflate_hd_table_bufsize_max); if(rv != 0) { goto fail; } @@ -1140,14 +1140,14 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, } if(deflater->no_refset) { - rv = emit_clear_refset(buf); + rv = emit_clear_refset(bufs); if(rv != 0) { goto fail; } clear_refset(&deflater->ctx); } for(i = 0; i < nvlen; ++i) { - rv = deflate_nv(deflater, buf, &nv[i]); + rv = deflate_nv(deflater, bufs, &nv[i]); if(rv != 0) { goto fail; } @@ -1155,13 +1155,13 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, for(i = 0; i < deflater->ctx.hd_table.len; ++i) { nghttp2_hd_entry *ent = nghttp2_hd_ringbuf_get(&deflater->ctx.hd_table, i); - rv = deflate_post_process_hd_entry(ent, i, buf); + rv = deflate_post_process_hd_entry(ent, i, bufs); if(rv != 0) { goto fail; } } - return nghttp2_buf_len(buf); + return 0; fail: deflater->ctx.bad = 1; return rv; @@ -1788,20 +1788,20 @@ int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) return 0; } -int nghttp2_hd_emit_indname_block(nghttp2_buf *buf, size_t index, +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, const uint8_t *value, size_t valuelen, int inc_indexing) { - return emit_indname_block(buf, index, value, valuelen, inc_indexing); + return emit_indname_block(bufs, index, value, valuelen, inc_indexing); } -int nghttp2_hd_emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, int inc_indexing) { - return emit_newname_block(buf, nv, inc_indexing); + return emit_newname_block(bufs, nv, inc_indexing); } -int nghttp2_hd_emit_table_size(nghttp2_buf *buf, size_t table_size) +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { - return emit_table_size(buf, table_size); + return emit_table_size(bufs, table_size); } diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h index 446b9cf3..61fc6088 100644 --- a/lib/nghttp2_hd.h +++ b/lib/nghttp2_hd.h @@ -295,29 +295,29 @@ int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, /* * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the buffer pointed by the |buf|. The caller must ensure that - * nghttp2_buf_len(buf) == 0 holds. Write starts at buf->last. + * the |bufs|. * - * This function expands |buf| as necessary to store the result. + * This function expands |bufs| as necessary to store the result. If + * buffers is full and the process still requires more space, this + * funtion fails and returns NGHTTP2_ERR_HEADER_COMP. * - * This function copies necessary data into |buf|. After this function - * returns, it is safe to delete the |nva|. + * After this function returns, it is safe to delete the |nva|. * * TODO: The rest of the code call nghttp2_hd_end_headers() after this * call, but it is just a regacy of the first implementation. Now it * is not required to be called as of now. * - * This function returns the number of bytes outputted if it succeeds, - * or one of the following negative error codes: + * This function returns 0 if it succeeds, or one of the following + * negative error codes: * * NGHTTP2_ERR_NOMEM * Out of memory. * NGHTTP2_ERR_HEADER_COMP * Deflation process has failed. */ -ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - nghttp2_buf *buf, - nghttp2_nv *nva, size_t nvlen); +int nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, + nghttp2_nv *nva, size_t nvlen); typedef enum { NGHTTP2_HD_INFLATE_NONE = 0, @@ -369,16 +369,16 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater); /* For unittesting purpose */ -int nghttp2_hd_emit_indname_block(nghttp2_buf *buf, size_t index, +int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, const uint8_t *value, size_t valuelen, int inc_indexing); /* For unittesting purpose */ -int nghttp2_hd_emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, +int nghttp2_hd_emit_newname_block(nghttp2_bufs *bufs, nghttp2_nv *nv, int inc_indexing); /* For unittesting purpose */ -int nghttp2_hd_emit_table_size(nghttp2_buf *buf, size_t table_size); +int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); /* For unittesting purpose */ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, @@ -396,19 +396,19 @@ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len); /* - * Encodes the given data |src| with length |srclen| to the given - * memory location pointed by |dest|, allocated at lest |destlen| - * bytes. The caller is responsible to specify |destlen| at least the - * length that nghttp2_hd_huff_encode_count() returns. + * Encodes the given data |src| with length |srclen| to the |bufs|. + * This function expands extra buffers in |bufs| if necessary. * - * This function returns the number of written bytes, including - * padding of prefix of terminal symbol code. This return value is - * exactly the same with the return value of - * nghttp2_hd_huff_encode_count() if it is given with the same |src| - * and |srclen|. This function always succeeds. + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_BUFFER_ERROR + * Out of buffer space. */ -ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen, - const uint8_t *src, size_t srclen); +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, + const uint8_t *src, size_t srclen); void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); diff --git a/lib/nghttp2_hd_huffman.c b/lib/nghttp2_hd_huffman.c index 6b9d1db7..5dfbba7d 100644 --- a/lib/nghttp2_hd_huffman.c +++ b/lib/nghttp2_hd_huffman.c @@ -40,24 +40,60 @@ extern const nghttp2_huff_decode huff_decode_table[][16]; * and points where next output should be placed. The number of * unfilled bits in the pointed location is returned. */ -static size_t huff_encode_sym(uint8_t **dest_ptr, size_t rembits, - const nghttp2_huff_sym *sym) +static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr, + size_t rembits, + const nghttp2_huff_sym *sym) { + int rv; size_t nbits = sym->nbits; + for(;;) { if(rembits > nbits) { - **dest_ptr |= sym->code << (rembits - nbits); + if(*avail_ptr) { + nghttp2_bufs_fast_orb_hold(bufs, sym->code << (rembits - nbits)); + } else { + rv = nghttp2_bufs_orb_hold(bufs, sym->code << (rembits - nbits)); + if(rv != 0) { + return rv; + } + + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + } + rembits -= nbits; + break; } - **dest_ptr |= sym->code >> (nbits - rembits); - ++*dest_ptr; + + if(*avail_ptr) { + nghttp2_bufs_fast_orb(bufs, sym->code >> (nbits - rembits)); + --*avail_ptr; + } else { + rv = nghttp2_bufs_orb(bufs, sym->code >> (nbits - rembits)); + if(rv != 0) { + return rv; + } + + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + } + nbits -= rembits; rembits = 8; + if(nbits == 0) { break; } - **dest_ptr = 0; + + if(*avail_ptr) { + nghttp2_bufs_fast_addb_hold(bufs, 0); + } else { + rv = nghttp2_bufs_addb_hold(bufs, 0); + if(rv != 0) { + return rv; + } + + *avail_ptr = nghttp2_bufs_cur_avail(bufs); + } } return rembits; } @@ -74,27 +110,50 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) return (nbits + 7) / 8; } -ssize_t nghttp2_hd_huff_encode(uint8_t *dest, size_t destlen, - const uint8_t *src, size_t srclen) +int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, + const uint8_t *src, size_t srclen) { + int rv; int rembits = 8; - uint8_t *dest_first = dest; size_t i; + size_t avail; + + avail = nghttp2_bufs_cur_avail(bufs); for(i = 0; i < srclen; ++i) { const nghttp2_huff_sym *sym = &huff_sym_table[src[i]]; if(rembits == 8) { - *dest = 0; + if(avail) { + nghttp2_bufs_fast_addb_hold(bufs, 0); + } else { + rv = nghttp2_bufs_addb_hold(bufs, 0); + if(rv != 0) { + return rv; + } + avail = nghttp2_bufs_cur_avail(bufs); + } + } + rembits = huff_encode_sym(bufs, &avail, rembits, sym); + if(rembits < 0) { + return rembits; } - rembits = huff_encode_sym(&dest, rembits, sym); } /* 256 is special terminal symbol, pad with its prefix */ if(rembits < 8) { const nghttp2_huff_sym *sym = &huff_sym_table[256]; - *dest |= sym->code >> (sym->nbits - rembits); - ++dest; + + /* Caution we no longer adjust avail here */ + if(avail) { + nghttp2_bufs_fast_orb(bufs, sym->code >> (sym->nbits - rembits)); + } else { + rv = nghttp2_bufs_orb(bufs, sym->code >> (sym->nbits - rembits)); + if(rv != 0) { + return rv; + } + } } - return dest - dest_first; + + return 0; } void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) @@ -109,6 +168,10 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, { size_t i, j; int rv; + size_t avail; + + avail = nghttp2_bufs_cur_avail(bufs); + /* We use the decoding algorithm described in http://graphics.ics.uci.edu/pub/Prefix.pdf */ for(i = 0; i < srclen; ++i) { @@ -119,9 +182,15 @@ ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, return NGHTTP2_ERR_HEADER_COMP; } if(t->flags & NGHTTP2_HUFF_SYM) { - rv = nghttp2_bufs_addb(bufs, t->sym); - if(rv != 0) { - return rv; + if(avail) { + nghttp2_bufs_fast_addb(bufs, t->sym); + --avail; + } else { + rv = nghttp2_bufs_addb(bufs, t->sym); + if(rv != 0) { + return rv; + } + avail = nghttp2_bufs_cur_avail(bufs); } } ctx->state = t->state; diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 3e961d75..a80d3597 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -211,7 +211,7 @@ static void nghttp2_active_outbound_item_reset nghttp2_outbound_item_free(aob->item); free(aob->item); aob->item = NULL; - nghttp2_buf_reset(&aob->framebuf); + nghttp2_bufs_reset(&aob->framebufs); aob->state = NGHTTP2_OB_POP_ITEM; } @@ -290,8 +290,12 @@ static int nghttp2_session_new(nghttp2_session **session_ptr, (*session_ptr)->server = 1; } - rv = nghttp2_buf_init2(&(*session_ptr)->aob.framebuf, - NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH); + /* 2 for PAD_HIGH and PAD_LOW. We have maximum 64KB of frame + serialization buffer for transmission */ + rv = nghttp2_bufs_init2(&(*session_ptr)->aob.framebufs, + NGHTTP2_MAX_FRAMELEN, + 65536 / NGHTTP2_MAX_FRAMELEN, + NGHTTP2_FRAME_HDLEN + 2); if(rv != 0) { goto fail_aob_framebuf; } @@ -418,7 +422,7 @@ void nghttp2_session_del(nghttp2_session *session) nghttp2_hd_deflate_free(&session->hd_deflater); nghttp2_hd_inflate_free(&session->hd_inflater); nghttp2_active_outbound_item_reset(&session->aob); - nghttp2_buf_free(&session->aob.framebuf); + nghttp2_bufs_free(&session->aob.framebufs); free(session); } @@ -1124,47 +1128,20 @@ static ssize_t session_call_select_padding(nghttp2_session *session, return frame->hd.length; } -static int session_reserve_pad_trail(nghttp2_session *session, size_t padlen) -{ - int rv; - nghttp2_active_outbound_item *aob; - - if(padlen == 0) { - return 0; - } - - aob = &session->aob; - - DEBUGF(fprintf(stderr, - "reserving extra %zu padding bytes, including garbage, " - "but adjusted later\n", - padlen)); - - rv = nghttp2_buf_last_reserve(&aob->framebuf, padlen); - if(nghttp2_is_fatal(rv)) { - return rv; - } - - /* We have to zero out padding bytes so that we won't reveal the - possible internal data to the remote peer */ - memset(aob->framebuf.last, 0, padlen); - - aob->framebuf.last += padlen; - - return 0; -} - /* Add padding to HEADERS or PUSH_PROMISE. We use frame->headers.padlen in this function to use the fact that frame->push_promise has also padlen in the same position. */ -static ssize_t session_headers_add_pad(nghttp2_session *session, - nghttp2_frame *frame) +static int session_headers_add_pad(nghttp2_session *session, + nghttp2_frame *frame) { int rv; ssize_t padded_payloadlen; nghttp2_active_outbound_item *aob; + nghttp2_bufs *framebufs; + size_t padlen; aob = &session->aob; + framebufs = &aob->framebufs; padded_payloadlen = session_call_select_padding(session, frame, frame->hd.length + 1024); @@ -1172,70 +1149,20 @@ static ssize_t session_headers_add_pad(nghttp2_session *session, return padded_payloadlen; } - frame->headers.padlen = padded_payloadlen - frame->hd.length; - frame->hd.length = padded_payloadlen; + padlen = padded_payloadlen - frame->hd.length; DEBUGF(fprintf(stderr, "padding selected: payloadlen=%zu, padlen=%zu\n", - frame->hd.length, frame->headers.padlen)); + padded_payloadlen, padlen)); - if(frame->hd.length > NGHTTP2_MAX_PAYLOADLEN) { - nghttp2_frame_hd hd = frame->hd; - hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; - hd.length = NGHTTP2_MAX_PAYLOADLEN; - - if(NGHTTP2_MAX_PAYLOADLEN > frame->hd.length - frame->headers.padlen) { - size_t padlen; - - padlen = NGHTTP2_MAX_PAYLOADLEN - - (frame->hd.length - frame->headers.padlen); - - DEBUGF(fprintf(stderr, "padding across 2 frames\n")); - DEBUGF(fprintf(stderr, "first HEADERS/PUSH_PROMISE " - "payloadlen=%zu, padlen=%zu\n", hd.length, padlen)); - - rv = session_reserve_pad_trail(session, frame->headers.padlen); - if(nghttp2_is_fatal(rv)) { - return rv; - } - - nghttp2_frame_set_pad(&aob->framebuf, &hd.flags, padlen); - } else { - /* PAD_HIGH and PAD_LOW will be added in - nghttp2_session_after_frame_sent(). */ - DEBUGF(fprintf(stderr, - "first HEADERS/PUSH_PROMISE does not have " - "padding payloadlen=%zu", hd.length)); - - /* Ensure that we have allocated buffer */ - rv = session_reserve_pad_trail(session, frame->headers.padlen); - if(nghttp2_is_fatal(rv)) { - return rv; - } - } - nghttp2_frame_pack_frame_hd(aob->framebuf.pos, &hd); - - } else if(frame->headers.padlen > 0) { - nghttp2_frame_hd hd = frame->hd; - - rv = session_reserve_pad_trail(session, frame->headers.padlen); - if(nghttp2_is_fatal(rv)) { - return rv; - } - - DEBUGF(fprintf(stderr, - "first HEADERS/PUSH_PROMISE payloadlen=%zu, padlen=%zu\n", - frame->hd.length, frame->headers.padlen)); - - nghttp2_frame_set_pad(&aob->framebuf, &hd.flags, frame->headers.padlen); - - if(nghttp2_is_fatal(rv)) { - return rv; - } - - nghttp2_frame_pack_frame_hd(aob->framebuf.pos, &hd); + rv = nghttp2_frame_add_pad(framebufs, &frame->hd, padlen, + NGHTTP2_CONTINUATION); + if(rv != 0) { + return rv; } - return nghttp2_buf_len(&session->aob.framebuf); + frame->headers.padlen = padlen; + + return 0; } /* @@ -1281,7 +1208,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, return rv; } } - framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_headers(&session->aob.framebufs, &frame->headers, &session->hd_deflater); if(framebuflen < 0) { @@ -1290,7 +1217,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, DEBUGF(fprintf(stderr, "before padding, HEADERS serialized in %zd bytes\n", - nghttp2_buf_len(&session->aob.framebuf))); + nghttp2_bufs_len(&session->aob.framebufs))); framebuflen = session_headers_add_pad(session, frame); if(framebuflen < 0) { @@ -1320,7 +1247,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, } DEBUGF(fprintf(stderr, "HEADERS serialized in %zd bytes\n", - nghttp2_buf_len(&session->aob.framebuf))); + nghttp2_bufs_len(&session->aob.framebufs))); break; } @@ -1330,7 +1257,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, if(rv != 0) { return rv; } - framebuflen = nghttp2_frame_pack_priority(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_priority(&session->aob.framebufs, &frame->priority); if(framebuflen < 0) { return framebuflen; @@ -1338,7 +1265,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, break; } case NGHTTP2_RST_STREAM: - framebuflen = nghttp2_frame_pack_rst_stream(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_rst_stream(&session->aob.framebufs, &frame->rst_stream); if(framebuflen < 0) { return framebuflen; @@ -1349,7 +1276,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, if(rv != 0) { return rv; } - framebuflen = nghttp2_frame_pack_settings(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_settings(&session->aob.framebufs, &frame->settings); if(framebuflen < 0) { return framebuflen; @@ -1368,7 +1295,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, } frame->push_promise.promised_stream_id = session->next_stream_id; session->next_stream_id += 2; - framebuflen = nghttp2_frame_pack_push_promise(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_push_promise(&session->aob.framebufs, &frame->push_promise, &session->hd_deflater); if(framebuflen < 0) { @@ -1393,7 +1320,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, break; } case NGHTTP2_PING: - framebuflen = nghttp2_frame_pack_ping(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); if(framebuflen < 0) { return framebuflen; @@ -1405,7 +1332,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, if(rv != 0) { return rv; } - framebuflen = nghttp2_frame_pack_window_update(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_window_update(&session->aob.framebufs, &frame->window_update); if(framebuflen < 0) { return framebuflen; @@ -1420,7 +1347,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, peer that last-stream-id. */ return NGHTTP2_ERR_GOAWAY_ALREADY_SENT; } - framebuflen = nghttp2_frame_pack_goaway(&session->aob.framebuf, + framebuflen = nghttp2_frame_pack_goaway(&session->aob.framebufs, &frame->goaway); if(framebuflen < 0) { return framebuflen; @@ -1450,7 +1377,7 @@ static int nghttp2_session_prep_frame(nghttp2_session *session, return NGHTTP2_ERR_DEFERRED; } framebuflen = nghttp2_session_pack_data(session, - &session->aob.framebuf, + &session->aob.framebufs, next_readmax, data_frame); if(framebuflen == NGHTTP2_ERR_DEFERRED) { @@ -1603,7 +1530,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) int rv; nghttp2_active_outbound_item *aob = &session->aob; nghttp2_outbound_item *item = aob->item; - nghttp2_buf *framebuf = &aob->framebuf; + nghttp2_bufs *framebufs = &aob->framebufs; if(item->frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame; @@ -1613,63 +1540,11 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) if(frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_PUSH_PROMISE) { - if(framebuf->mark < framebuf->last) { - nghttp2_frame_hd cont_hd; + if(nghttp2_bufs_next_present(framebufs)) { + framebufs->cur = framebufs->cur->next; - cont_hd.length = nghttp2_min(framebuf->last - framebuf->mark, - NGHTTP2_MAX_PAYLOADLEN); - cont_hd.type = NGHTTP2_CONTINUATION; - cont_hd.stream_id = frame->hd.stream_id; - cont_hd.flags = NGHTTP2_FLAG_NONE; - - /* Reuse previous buffers for frame header */ - framebuf->pos -= NGHTTP2_FRAME_HDLEN; - - if(cont_hd.length + framebuf->mark == framebuf->last) { - size_t padlen; - - if(cont_hd.length < frame->headers.padlen) { - padlen = cont_hd.length; - } else { - /* We use frame->headers.padlen for PUSH_PROMISE too. This - is possible because padlen is located in the same - position. */ - padlen = frame->headers.padlen; - } - - cont_hd.flags = NGHTTP2_FLAG_END_HEADERS; - - DEBUGF(fprintf(stderr, - "last CONTINUATION payloadlen=%zu, padlen=%zu\n", - cont_hd.length, padlen)); - - nghttp2_frame_set_pad(framebuf, &cont_hd.flags, padlen); - - framebuf->mark = framebuf->last; - } else { - ssize_t padlen; - - framebuf->mark += cont_hd.length; - - padlen = frame->headers.padlen - (framebuf->last - framebuf->mark); - - if(padlen > 0) { - /* frame payload includes a part of padding */ - DEBUGF(fprintf(stderr, - "padding across 2 CONTINUATION frames. " - "payloadlen=%zu, padlen=%zd\n", - cont_hd.length, padlen)); - - nghttp2_frame_set_pad(framebuf, &cont_hd.flags, padlen); - - framebuf->mark = framebuf->pos + NGHTTP2_FRAME_HDLEN + - cont_hd.length; - } else { - /* If no padding, nothing to be done here */ - } - } - - nghttp2_frame_pack_frame_hd(framebuf->pos, &cont_hd); + DEBUGF(fprintf(stderr, "send CONTINUATION frame, %zu bytes\n", + nghttp2_buf_len(&framebufs->cur->buf))); return 0; } @@ -1865,7 +1740,10 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) return 0; } - rv = nghttp2_session_pack_data(session, framebuf, next_readmax, + + nghttp2_bufs_reset(framebufs); + + rv = nghttp2_session_pack_data(session, framebufs, next_readmax, data_frame); if(nghttp2_is_fatal(rv)) { return rv; @@ -1892,7 +1770,6 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) return 0; } assert(rv >= 0); - framebuf->mark = framebuf->last; return 0; } @@ -1916,10 +1793,10 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, { int rv; nghttp2_active_outbound_item *aob; - nghttp2_buf *framebuf; + nghttp2_bufs *framebufs; aob = &session->aob; - framebuf = &aob->framebuf; + framebufs = &aob->framebufs; *data_ptr = NULL; for(;;) { @@ -1974,29 +1851,22 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, } aob->item = item; + nghttp2_bufs_rewind(framebufs); + if(item->frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame; - /* We have to get frame size from headers, because - frame->hd.length does not always shows the actual frame - size, especially for HEADERS size > - NGHTTP2_MAX_PAYLOADLEN */ frame = nghttp2_outbound_item_get_ctrl_frame(item); - framebuf->mark = framebuf->pos + NGHTTP2_FRAME_HDLEN + - nghttp2_get_uint16(framebuf->pos); - rv = session_call_before_frame_send(session, frame); if(nghttp2_is_fatal(rv)) { return rv; } - - } else { - framebuf->mark = framebuf->last; } DEBUGF(fprintf(stderr, "start transmitting type %d frame %zd bytes\n", - framebuf->pos[2], framebuf->mark - framebuf->pos)); + framebufs->cur->buf.pos[2], + framebufs->cur->buf.last - framebufs->cur->buf.pos)); aob->state = NGHTTP2_OB_SEND_DATA; @@ -2004,10 +1874,12 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, } case NGHTTP2_OB_SEND_DATA: { size_t datalen; + nghttp2_buf *buf; - if(framebuf->pos == framebuf->mark) { - DEBUGF(fprintf(stderr, "end transmission of frame, left %zd\n", - framebuf->last - framebuf->mark)); + buf = &framebufs->cur->buf; + + if(buf->pos == buf->last) { + DEBUGF(fprintf(stderr, "end transmission of frame\n")); /* Frame has completely sent */ rv = nghttp2_session_after_frame_sent(session); @@ -2020,11 +1892,12 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, break; } - *data_ptr = framebuf->pos; - datalen = framebuf->mark - framebuf->pos; + *data_ptr = buf->pos; + datalen = nghttp2_buf_len(buf); + /* We increment the offset here. If send_callback does not send everything, we will adjust it. */ - framebuf->pos += datalen; + buf->pos += datalen; return datalen; } @@ -2037,9 +1910,9 @@ int nghttp2_session_send(nghttp2_session *session) const uint8_t *data; ssize_t datalen; ssize_t sentlen; - nghttp2_buf *framebuf; + nghttp2_bufs *framebufs; - framebuf = &session->aob.framebuf; + framebufs = &session->aob.framebufs; for(;;) { datalen = nghttp2_session_mem_send(session, &data); @@ -2051,15 +1924,16 @@ int nghttp2_session_send(nghttp2_session *session) if(sentlen < 0) { if(sentlen == NGHTTP2_ERR_WOULDBLOCK) { /* Transmission canceled. Rewind the offset */ - framebuf->pos -= datalen; + framebufs->cur->buf.pos -= datalen; return 0; } return NGHTTP2_ERR_CALLBACK_FAILURE; } /* Rewind the offset to the amount of unsent bytes */ - framebuf->pos -= datalen - sentlen; + framebufs->cur->buf.pos -= datalen - sentlen; } + return 0; } @@ -4572,36 +4446,30 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, } ssize_t nghttp2_session_pack_data(nghttp2_session *session, - nghttp2_buf *buf, + nghttp2_bufs *bufs, size_t datamax, nghttp2_private_data *frame) { - ssize_t framelen; ssize_t rv; int eof_flags; uint8_t flags; ssize_t payloadlen; ssize_t padded_payloadlen; size_t padlen; - nghttp2_frame data_frame; + nghttp2_frame data_frame = {{0}}; /* FIXME padlen is also initialized? */ nghttp2_frame_hd hd; + nghttp2_buf *buf; - /* extra 2 bytes for PAD_HIGH and PAD_LOW. We allocate extra 2 bytes - for padding. Based on the padding length, we adjust the starting - offset of frame data. The starting offset is assigned into - |*bufoff_ptr|. */ - buf->pos += 2; + assert(bufs->head == bufs->cur); - framelen = NGHTTP2_FRAME_HDLEN + datamax; + buf = &bufs->cur->buf; - rv = nghttp2_buf_pos_reserve(buf, framelen); - if(rv != 0) { - return rv; - } + /* Current max DATA length is less then buffer chunk size */ + assert(nghttp2_buf_avail(buf) >= (ssize_t)datamax); eof_flags = 0; payloadlen = frame->data_prd.read_callback - (session, frame->hd.stream_id, buf->pos + NGHTTP2_FRAME_HDLEN, datamax, + (session, frame->hd.stream_id, buf->pos, datamax, &eof_flags, &frame->data_prd.source, session->user_data); if(payloadlen == NGHTTP2_ERR_DEFERRED || @@ -4617,7 +4485,8 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, return NGHTTP2_ERR_CALLBACK_FAILURE; } - buf->last = buf->pos + NGHTTP2_FRAME_HDLEN + payloadlen; + buf->last = buf->pos + payloadlen; + buf->pos -= NGHTTP2_FRAME_HDLEN; /* Clear flags, because this may contain previous flags of previous DATA */ @@ -4635,8 +4504,6 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, } /* The primary reason of data_frame is pass to the user callback */ - memset(&data_frame, 0, sizeof(data_frame)); - data_frame.hd.length = payloadlen; data_frame.hd.stream_id = frame->hd.stream_id; data_frame.hd.type = NGHTTP2_DATA; @@ -4650,27 +4517,21 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, padlen = padded_payloadlen - payloadlen; - rv = session_reserve_pad_trail(session, padlen); - if(nghttp2_is_fatal(rv)) { - return rv; - } - - nghttp2_frame_set_pad(buf, &flags, padlen); - - frame->padlen = padlen; - frame->hd.length = padded_payloadlen; - - /* Set PAD flags so that we can supply frame to the callback with - the correct flags */ - frame->hd.flags |= flags; - - memset(buf->pos, 0, NGHTTP2_FRAME_HDLEN); - hd = frame->hd; + hd.length = payloadlen; hd.flags = flags; nghttp2_frame_pack_frame_hd(buf->pos, &hd); + rv = nghttp2_frame_add_pad(bufs, &hd, padlen, NGHTTP2_DATA); + if(rv != 0) { + return rv; + } + + frame->hd.length = hd.length; + frame->hd.flags |= hd.flags; + frame->padlen = padlen; + return nghttp2_buf_len(buf); } diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 42d7049a..8b402430 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -55,15 +55,13 @@ typedef enum { typedef struct { nghttp2_outbound_item *item; - nghttp2_buf framebuf; + nghttp2_bufs framebufs; nghttp2_outbound_state state; } nghttp2_active_outbound_item; /* Buffer length for inbound raw byte stream. */ #define NGHTTP2_INBOUND_BUFFER_LENGTH 16384 -#define NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH 16384 - #define NGHTTP2_INITIAL_NV_BUFFER_LENGTH 4096 /* Internal state when receiving incoming frame */ @@ -526,7 +524,7 @@ nghttp2_stream* nghttp2_session_get_stream(nghttp2_session *session, * The read_callback failed (session error). */ ssize_t nghttp2_session_pack_data(nghttp2_session *session, - nghttp2_buf *buf, + nghttp2_bufs *bufs, size_t datamax, nghttp2_private_data *frame); diff --git a/python/cnghttp2.pxd b/python/cnghttp2.pxd index 624a5b4c..2754878f 100644 --- a/python/cnghttp2.pxd +++ b/python/cnghttp2.pxd @@ -290,9 +290,9 @@ cdef extern from 'nghttp2_hd.h': int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, size_t hd_table_bufsize_max) - ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - nghttp2_buf *buf, - nghttp2_nv *nva, size_t nvlen) + int nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + nghttp2_bufs *bufs, + nghttp2_nv *nva, size_t nvlen) ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, @@ -305,11 +305,12 @@ cdef extern from 'nghttp2_hd.h': cdef extern from 'nghttp2_buf.h': - ctypedef struct nghttp2_buf: - uint8_t *pos - uint8_t *last + ctypedef struct nghttp2_bufs: + pass + void nghttp2_bufs_init(nghttp2_bufs *bufs, size_t chunk_size, + size_t max_chunk) - void nghttp2_buf_init(nghttp2_buf *buf) + void nghttp2_bufs_free(nghttp2_bufs *bufs) - void nghttp2_buf_free(nghttp2_buf *buf) + ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx index 1b8de9ac..f74a7c11 100644 --- a/python/nghttp2.pyx +++ b/python/nghttp2.pyx @@ -114,27 +114,36 @@ cdef class HDDeflater: nvap[0].valuelen = len(v) nvap += 1 - cdef cnghttp2.nghttp2_buf buf + cdef cnghttp2.nghttp2_bufs bufs cdef size_t outcap = 0 cdef ssize_t rv + cdef uint8_t *out - cnghttp2.nghttp2_buf_init(&buf) + cnghttp2.nghttp2_bufs_init(&bufs, 4096, 16) - rv = cnghttp2.nghttp2_hd_deflate_hd(&self._deflater, &buf, + rv = cnghttp2.nghttp2_hd_deflate_hd(&self._deflater, &bufs, nva, len(headers)) free(nva) if rv < 0: - cnghttp2.nghttp2_buf_free(&buf); + cnghttp2.nghttp2_bufs_free(&bufs); + + raise Exception(_strerror(rv)) + + rv = cnghttp2.nghttp2_bufs_remove(&bufs, &out) + + if rv < 0: + cnghttp2.nghttp2_bufs_free(&bufs); raise Exception(_strerror(rv)) cdef bytes res try: - res = buf.pos[:rv] + res = out[:rv] finally: - cnghttp2.nghttp2_buf_free(&buf) + cnghttp2.nghttp2_free(out) + cnghttp2.nghttp2_bufs_free(&bufs) return res diff --git a/src/app_helper.cc b/src/app_helper.cc index e0a9d3bc..2516c465 100644 --- a/src/app_helper.cc +++ b/src/app_helper.cc @@ -324,7 +324,7 @@ void print_frame(print_type ptype, const nghttp2_frame *frame) } switch(frame->hd.type) { case NGHTTP2_DATA: - if(frame->hd.flags & (NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW)) { + if(frame->data.padlen > 0) { print_frame_attr_indent(); fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen); } diff --git a/src/deflatehd.c b/src/deflatehd.c index 2b083dc2..540694c0 100644 --- a/src/deflatehd.c +++ b/src/deflatehd.c @@ -72,26 +72,36 @@ static void to_hex(char *dest, const uint8_t *src, size_t len) } static void output_to_json(nghttp2_hd_deflater *deflater, - nghttp2_buf *buf, size_t inputlen, + nghttp2_bufs *bufs, size_t inputlen, nghttp2_nv *nva, size_t nvlen, int seq) { json_t *obj; - char *hex = NULL; + char *hex = NULL, *hexp; size_t len; + nghttp2_buf_chain *ci; + nghttp2_buf *buf; - len = nghttp2_buf_len(buf); + len = nghttp2_bufs_len(bufs); if(len > 0) { hex = malloc(len * 2); } + obj = json_object(); json_object_set_new(obj, "seq", json_integer(seq)); json_object_set_new(obj, "input_length", json_integer(inputlen)); json_object_set_new(obj, "output_length", json_integer(len)); json_object_set_new(obj, "percentage_of_original_size", json_real((double)len / inputlen * 100)); - to_hex(hex, buf->pos, len); + + hexp = hex; + for(ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + to_hex(hexp, buf->pos, nghttp2_buf_len(buf)); + hexp += nghttp2_buf_len(buf); + } + if(len == 0) { json_object_set_new(obj, "wire", json_string("")); } else { @@ -117,11 +127,11 @@ static void deflate_hd(nghttp2_hd_deflater *deflater, nghttp2_nv *nva, size_t nvlen, size_t inputlen, int seq) { ssize_t rv; - nghttp2_buf buf; + nghttp2_bufs bufs; - nghttp2_buf_init(&buf); + nghttp2_bufs_init2(&bufs, 4096, 16, 0); - rv = nghttp2_hd_deflate_hd(deflater, &buf, nva, nvlen); + rv = nghttp2_hd_deflate_hd(deflater, &bufs, nva, nvlen); if(rv < 0) { fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq); exit(EXIT_FAILURE); @@ -130,8 +140,8 @@ static void deflate_hd(nghttp2_hd_deflater *deflater, input_sum += inputlen; output_sum += rv; - output_to_json(deflater, &buf, inputlen, nva, nvlen, seq); - nghttp2_buf_free(&buf); + output_to_json(deflater, &bufs, inputlen, nva, nvlen, seq); + nghttp2_bufs_free(&bufs); } static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq) diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index c36933a6..bf294b5e 100644 --- a/tests/nghttp2_frame_test.c +++ b/tests/nghttp2_frame_test.c @@ -72,14 +72,14 @@ void test_nghttp2_frame_pack_headers() nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_headers frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; nghttp2_nv *nva; ssize_t nvlen; nva_out out; ssize_t hdblocklen; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater); @@ -91,22 +91,24 @@ void test_nghttp2_frame_pack_headers() NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS, 1000000007, 1 << 20, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&buf, &frame, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + nghttp2_bufs_rewind(&bufs); - check_frame_header(nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN, + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); + + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, NGHTTP2_HEADERS, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS, 1000000007, &oframe.hd); /* We didn't include PRIORITY flag so priority is not packed */ CU_ASSERT(1 << 30 == oframe.pri); - hdblocklen = nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN; + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN; CU_ASSERT(hdblocklen == - inflate_hd(&inflater, &out, - buf.pos + NGHTTP2_FRAME_HDLEN, hdblocklen)); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN)); CU_ASSERT(7 == out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); @@ -114,33 +116,33 @@ void test_nghttp2_frame_pack_headers() nghttp2_frame_headers_free(&oframe); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); memset(&oframe, 0, sizeof(oframe)); /* Next, include PRIORITY flag */ frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; - framelen = nghttp2_frame_pack_headers(&buf, &frame, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); - check_frame_header(nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN, + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, NGHTTP2_HEADERS, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, 1000000007, &oframe.hd); CU_ASSERT(1 << 20 == oframe.pri); - hdblocklen = nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN - 4; + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4; CU_ASSERT(hdblocklen == - inflate_hd(&inflater, &out, - buf.pos + NGHTTP2_FRAME_HDLEN + 4, hdblocklen)); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4)); nghttp2_nv_array_sort(out.nva, out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); nva_out_reset(&out); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&oframe); nghttp2_frame_headers_free(&frame); nghttp2_hd_inflate_free(&inflater); @@ -151,16 +153,16 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) { nghttp2_hd_deflater deflater; nghttp2_headers frame; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; nghttp2_nv *nva; ssize_t nvlen; size_t big_vallen = NGHTTP2_HD_MAX_VALUE; nghttp2_nv big_hds[16]; size_t big_hdslen = ARRLEN(big_hds); size_t i; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); for(i = 0; i < big_hdslen; ++i) { big_hds[i].name = (uint8_t*)"header"; @@ -177,11 +179,11 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) NGHTTP2_FLAG_END_STREAM|NGHTTP2_FLAG_END_HEADERS, 1000000007, 0, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&buf, &frame, &deflater); - CU_ASSERT_EQUAL(NGHTTP2_ERR_HEADER_COMP, framelen); + rv = nghttp2_frame_pack_headers(&bufs, &frame, &deflater); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == rv); nghttp2_frame_headers_free(&frame); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); for(i = 0; i < big_hdslen; ++i) { free(big_hds[i].value); } @@ -191,21 +193,22 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) void test_nghttp2_frame_pack_priority(void) { nghttp2_priority frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_frame_priority_init(&frame, 1000000007, 1 << 30); - framelen = nghttp2_frame_pack_priority(&buf, &frame); + rv = nghttp2_frame_pack_priority(&bufs, &frame); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(12 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); check_frame_header(4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); CU_ASSERT(1 << 30 == oframe.pri); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_priority_free(&oframe); nghttp2_frame_priority_free(&frame); } @@ -213,21 +216,22 @@ void test_nghttp2_frame_pack_priority(void) void test_nghttp2_frame_pack_rst_stream(void) { nghttp2_rst_stream frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); - framelen = nghttp2_frame_pack_rst_stream(&buf, &frame); + rv = nghttp2_frame_pack_rst_stream(&bufs, &frame); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(12 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_rst_stream_free(&oframe); nghttp2_frame_rst_stream_free(&frame); } @@ -235,9 +239,9 @@ void test_nghttp2_frame_pack_rst_stream(void) void test_nghttp2_frame_pack_settings() { nghttp2_settings frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; int i; + int rv; nghttp2_settings_entry iv[] = { { @@ -251,17 +255,17 @@ void test_nghttp2_frame_pack_settings() } }; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE, nghttp2_frame_iv_copy(iv, 3), 3); - framelen = nghttp2_frame_pack_settings(&buf, &frame); + rv = nghttp2_frame_pack_settings(&bufs, &frame); - CU_ASSERT(NGHTTP2_FRAME_HDLEN + - 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == framelen); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(NGHTTP2_FRAME_HDLEN + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == + nghttp2_bufs_len(&bufs)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0, &oframe.hd); CU_ASSERT(3 == oframe.niv); @@ -270,7 +274,7 @@ void test_nghttp2_frame_pack_settings() CU_ASSERT(iv[i].value == oframe.iv[i].value); } - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_settings_free(&frame); nghttp2_frame_settings_free(&oframe); } @@ -280,14 +284,14 @@ void test_nghttp2_frame_pack_push_promise() nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_push_promise frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; nghttp2_nv *nva; ssize_t nvlen; nva_out out; ssize_t hdblocklen; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater); @@ -297,27 +301,27 @@ void test_nghttp2_frame_pack_push_promise() nvlen = HEADERS_LENGTH; nghttp2_frame_push_promise_init(&frame, NGHTTP2_FLAG_END_HEADERS, 1000000007, (1U << 31) - 1, nva, nvlen); - framelen = nghttp2_frame_pack_push_promise(&buf, &frame, &deflater); + rv = nghttp2_frame_pack_push_promise(&bufs, &frame, &deflater); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); - check_frame_header(nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN, + check_frame_header(nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN, NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007, &oframe.hd); CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id); - hdblocklen = nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN - 4; + hdblocklen = nghttp2_bufs_len(&bufs) - NGHTTP2_FRAME_HDLEN - 4; CU_ASSERT(hdblocklen == - inflate_hd(&inflater, &out, - buf.pos + NGHTTP2_FRAME_HDLEN + 4, hdblocklen)); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN + 4)); CU_ASSERT(7 == out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); CU_ASSERT(nvvalueeq("GET", &out.nva[0])); nva_out_reset(&out); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_push_promise_free(&oframe); nghttp2_frame_push_promise_free(&frame); nghttp2_hd_inflate_free(&inflater); @@ -327,22 +331,23 @@ void test_nghttp2_frame_pack_push_promise() void test_nghttp2_frame_pack_ping(void) { nghttp2_ping frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; const uint8_t opaque_data[] = "01234567"; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data); - framelen = nghttp2_frame_pack_ping(&buf, &frame); + rv = nghttp2_frame_pack_ping(&bufs, &frame); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(16 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); check_frame_header(8, NGHTTP2_PING, NGHTTP2_FLAG_ACK, 0, &oframe.hd); CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, sizeof(opaque_data) - 1) == 0); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_ping_free(&oframe); nghttp2_frame_ping_free(&frame); } @@ -350,20 +355,21 @@ void test_nghttp2_frame_pack_ping(void) void test_nghttp2_frame_pack_goaway() { nghttp2_goaway frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; size_t opaque_data_len = 16; uint8_t *opaque_data = malloc(opaque_data_len); + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); memcpy(opaque_data, "0123456789abcdef", opaque_data_len); nghttp2_frame_goaway_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR, opaque_data, opaque_data_len); - framelen = nghttp2_frame_pack_goaway(&buf, &frame); + rv = nghttp2_frame_pack_goaway(&bufs, &frame); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT((ssize_t)(16 + opaque_data_len) == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); check_frame_header(24, NGHTTP2_GOAWAY, NGHTTP2_FLAG_NONE, 0, &oframe.hd); CU_ASSERT(1000000007 == oframe.last_stream_id); CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); @@ -373,7 +379,7 @@ void test_nghttp2_frame_pack_goaway() /* CU_ASSERT(opaque_data_len == oframe.opaque_data_len); */ /* CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); */ - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_goaway_free(&oframe); nghttp2_frame_goaway_free(&frame); } @@ -381,22 +387,23 @@ void test_nghttp2_frame_pack_goaway() void test_nghttp2_frame_pack_window_update(void) { nghttp2_window_update frame, oframe; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096); - framelen = nghttp2_frame_pack_window_update(&buf, &frame); + rv = nghttp2_frame_pack_window_update(&bufs, &frame); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); - CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(12 == nghttp2_bufs_len(&bufs)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &bufs)); check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); CU_ASSERT(4096 == oframe.window_size_increment); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_window_update_free(&oframe); nghttp2_frame_window_update_free(&frame); } diff --git a/tests/nghttp2_hd_test.c b/tests/nghttp2_hd_test.c index c715edba..6ec4d85a 100644 --- a/tests/nghttp2_hd_test.c +++ b/tests/nghttp2_hd_test.c @@ -52,83 +52,89 @@ void test_nghttp2_hd_deflate(void) MAKE_NV("cookie", "k1=v1")}; nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"), MAKE_NV("x-nghttp2", "")}; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nva_out out; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater)); CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater)); - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva1, ARRLEN(nva1)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva1, ARRLEN(nva1)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva1, out.nva, 3); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Second headers */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva2, ARRLEN(nva2)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva2, ARRLEN(nva2)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == out.nvlen); assert_nv_equal(nva2, out.nva, 2); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Third headers, including same header field name, but value is not the same. */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva3, ARRLEN(nva3)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva3, ARRLEN(nva3)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva3, out.nva, 3); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Fourth headers, including duplicate header fields. */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva4, ARRLEN(nva4)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva4, ARRLEN(nva4)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva4, out.nva, 3); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Fifth headers includes empty value */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva5, ARRLEN(nva5)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva5, ARRLEN(nva5)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == out.nvlen); assert_nv_equal(nva5, out.nva, 2); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Cleanup */ - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -142,11 +148,12 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void) nghttp2_nv nva2[] = {MAKE_NV("cookie", "alpha"), MAKE_NV("cookie", "alpha"), MAKE_NV("cookie", "alpha")}; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nva_out out; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater)); @@ -154,34 +161,36 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void) /* Encode 2 same headers. cookie:alpha is not in the reference set, so first emit literal repr and then 2 emits of indexed repr. */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva1, ARRLEN(nva1)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva1, ARRLEN(nva1)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == out.nvlen); assert_nv_equal(nva1, out.nva, 2); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Encode 3 same headers. This time, cookie:alpha is in the reference set, so the encoder emits indexed repr 6 times */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva2, ARRLEN(nva2)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva2, ARRLEN(nva2)); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen == 6); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva2, out.nva, 3); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Cleanup */ - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -192,7 +201,7 @@ void test_nghttp2_hd_deflate_common_header_eviction(void) nghttp2_hd_inflater inflater; nghttp2_nv nva[] = {MAKE_NV("h1", ""), MAKE_NV("h2", "")}; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; /* Default header table capacity is 4096. Adding 2 byte header name and 4060 byte value, which is 4094 bytes including overhead, to @@ -200,8 +209,9 @@ void test_nghttp2_hd_deflate_common_header_eviction(void) uint8_t value[3038]; nva_out out; size_t i; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); memset(value, '0', sizeof(value)); @@ -215,42 +225,44 @@ void test_nghttp2_hd_deflate_common_header_eviction(void) /* First emit "h1: ..." to put it in the reference set (index = 0). */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 1); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 1); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); nghttp2_nv_array_sort(nva, 1); assert_nv_equal(nva, out.nva, 1); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Encode with second header */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); /* Check common header "h1: ...:, which is removed from the header table because of eviction, is still emitted by the inflater */ - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == out.nvlen); nghttp2_nv_array_sort(nva, 2); assert_nv_equal(nva, out.nva, 2); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); CU_ASSERT(1 == deflater.ctx.hd_table.len); CU_ASSERT(1 == inflater.ctx.hd_table.len); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -259,7 +271,7 @@ void test_nghttp2_hd_deflate_clear_refset(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nghttp2_nv nv[] = { MAKE_NV(":path", "/"), @@ -267,8 +279,9 @@ void test_nghttp2_hd_deflate_clear_refset(void) }; size_t i; nva_out out; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_deflate_init2(&deflater, @@ -277,18 +290,21 @@ void test_nghttp2_hd_deflate_clear_refset(void) nghttp2_hd_inflate_init(&inflater); for(i = 0; i < 2; ++i) { - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nv, ARRLEN(nv)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nv, ARRLEN(nv)); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); CU_ASSERT(blocklen > 1); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(ARRLEN(nv) == out.nvlen); assert_nv_equal(nv, out.nva, ARRLEN(nv)); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); } - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -296,7 +312,7 @@ void test_nghttp2_hd_deflate_clear_refset(void) void test_nghttp2_hd_inflate_indname_noinc(void) { nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nghttp2_nv nv[] = { /* Huffman */ @@ -307,53 +323,53 @@ void test_nghttp2_hd_inflate_indname_noinc(void) size_t i; nva_out out; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); for(i = 0; i < ARRLEN(nv); ++i) { - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 56, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 56, nv[i].value, nv[i].valuelen, 0)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv[i], out.nva, 1); CU_ASSERT(0 == inflater.ctx.hd_table.len); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); } - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_inc(void) { nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); nva_out out; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 56, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 56, nv.value, nv.valuelen, 1)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); @@ -363,38 +379,38 @@ void test_nghttp2_hd_inflate_indname_inc(void) inflater.ctx.hd_table.len-1)->nv, 1); nva_out_reset(&out); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_inc_eviction(void) { nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; uint8_t value[1024]; nva_out out; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); memset(value, '0', sizeof(value)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 13, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 13, value, sizeof(value), 1)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 14, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 14, value, sizeof(value), 1)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 15, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 15, value, sizeof(value), 1)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 16, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&bufs, 16, value, sizeof(value), 1)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(4 == out.nvlen); CU_ASSERT(14 == out.nva[0].namelen); @@ -402,19 +418,19 @@ void test_nghttp2_hd_inflate_indname_inc_eviction(void) CU_ASSERT(sizeof(value) == out.nva[0].valuelen); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); CU_ASSERT(3 == inflater.ctx.hd_table.len); CU_ASSERT(GET_TABLE_ENT(&inflater.ctx, 0)->flags & NGHTTP2_HD_FLAG_REFSET); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_newname_noinc(void) { nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nghttp2_nv nv[] = { /* Expecting huffman for both */ @@ -429,49 +445,49 @@ void test_nghttp2_hd_inflate_newname_noinc(void) size_t i; nva_out out; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); for(i = 0; i < ARRLEN(nv); ++i) { - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv[i], 0)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv[i], 0)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv[i], out.nva, 1); CU_ASSERT(0 == inflater.ctx.hd_table.len); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); } - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_newname_inc(void) { nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); nva_out out; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv, 1)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv, 1)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); @@ -481,20 +497,20 @@ void test_nghttp2_hd_inflate_newname_inc(void) inflater.ctx.hd_table.len-1)->nv, 1); nva_out_reset(&out); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_clearall_inc(void) { nghttp2_hd_inflater inflater; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nghttp2_nv nv; uint8_t value[4060]; nva_out out; - nghttp2_buf_init(&buf); + bufs_large_init(&bufs, 8192); nva_out_init(&out); /* Total 4097 bytes space required to hold this entry */ @@ -506,66 +522,73 @@ void test_nghttp2_hd_inflate_clearall_inc(void) nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv, 1)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv, 1)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); CU_ASSERT(0 == inflater.ctx.hd_table.len); nva_out_reset(&out); - nghttp2_buf_reset(&buf); /* Do it again */ - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); CU_ASSERT(0 == inflater.ctx.hd_table.len); nva_out_reset(&out); + nghttp2_bufs_reset(&bufs); /* This time, 4096 bytes space required, which is just fits in the header table */ nv.valuelen = sizeof(value) - 1; - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv, 1)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&bufs, &nv, 1)); - blocklen = nghttp2_buf_len(&buf); + blocklen = nghttp2_bufs_len(&bufs); CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); CU_ASSERT(1 == inflater.ctx.hd_table.len); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_zero_length_huffman(void) { nghttp2_hd_inflater inflater; - uint8_t buf[4]; + nghttp2_bufs bufs; + /* Literal header without indexing - new name */ + uint8_t data[] = { 0x40, 0x01, 0x78 /* 'x' */, 0x80 }; nva_out out; + frame_pack_bufs_init(&bufs); + nva_out_init(&out); - /* Literal header without indexing - new name */ - buf[0] = 0x40; - buf[1] = 1; - buf[2] = 'x'; - buf[3] = 0x80; + + nghttp2_bufs_add(&bufs, data, sizeof(data)); + + /* /\* Literal header without indexing - new name *\/ */ + /* ptr[0] = 0x40; */ + /* ptr[1] = 1; */ + /* ptr[2] = 'x'; */ + /* ptr[3] = 0x80; */ nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(4 == inflate_hd(&inflater, &out, buf, 4)); + CU_ASSERT(4 == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(1 == out.nvlen); CU_ASSERT(1 == out.nva[0].namelen); @@ -574,6 +597,7 @@ void test_nghttp2_hd_inflate_zero_length_huffman(void) CU_ASSERT(0 == out.nva[0].valuelen); nva_out_reset(&out); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); } @@ -583,11 +607,12 @@ void test_nghttp2_hd_change_table_size(void) nghttp2_hd_inflater inflater; nghttp2_nv nva[] = { MAKE_NV(":method", "GET"), MAKE_NV(":path", "/") }; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t rv; nva_out out; + ssize_t blocklen; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); @@ -606,19 +631,21 @@ void test_nghttp2_hd_change_table_size(void) CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); /* This will emit encoding context update with header table size 4096 */ - rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); - CU_ASSERT(rv > 0); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); CU_ASSERT(2 == deflater.ctx.hd_table.len); CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == inflater.ctx.hd_table.len); CU_ASSERT(4096 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* inflater changes header table size to 1024 */ CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024)); @@ -631,19 +658,21 @@ void test_nghttp2_hd_change_table_size(void) CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); - rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); - CU_ASSERT(rv >= 0); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == blocklen); CU_ASSERT(2 == deflater.ctx.hd_table.len); CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == inflater.ctx.hd_table.len); CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(1024 == inflater.settings_hd_table_bufsize_max); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* inflater changes header table size to 0 */ CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); @@ -658,26 +687,28 @@ void test_nghttp2_hd_change_table_size(void) CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); - rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); - CU_ASSERT(rv >= 0); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); CU_ASSERT(0 == deflater.ctx.hd_table.len); CU_ASSERT(0 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(0 == inflater.ctx.hd_table.len); CU_ASSERT(0 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(0 == inflater.settings_hd_table_bufsize_max); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); /* Check table buffer is expanded */ - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_hd_deflate_init2(&deflater, 8192); nghttp2_hd_inflate_init(&inflater); @@ -693,19 +724,21 @@ void test_nghttp2_hd_change_table_size(void) CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); - rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); - CU_ASSERT(rv > 0); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); CU_ASSERT(2 == deflater.ctx.hd_table.len); CU_ASSERT(8000 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == inflater.ctx.hd_table.len); CU_ASSERT(8000 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(8000 == inflater.settings_hd_table_bufsize_max); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383)); CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383)); @@ -717,29 +750,30 @@ void test_nghttp2_hd_change_table_size(void) CU_ASSERT(16383 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); - rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); - CU_ASSERT(rv >= 0); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); CU_ASSERT(2 == deflater.ctx.hd_table.len); CU_ASSERT(8192 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == inflater.ctx.hd_table.len); CU_ASSERT(8192 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(16383 == inflater.settings_hd_table_bufsize_max); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Lastly, check the error condition */ - rv = nghttp2_hd_emit_table_size(&buf, 25600); + rv = nghttp2_hd_emit_table_size(&bufs, 25600); CU_ASSERT(rv == 0); - CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == - inflate_hd(&inflater, &out, buf.pos, nghttp2_buf_len(&buf))); + CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == inflate_hd(&inflater, &out, &bufs, 0)); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); @@ -753,21 +787,23 @@ void test_nghttp2_hd_change_table_size(void) CU_ASSERT(4096 == deflater.ctx.hd_table_bufsize_max); /* This emits context update with buffer size 1024 */ - rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); - CU_ASSERT(rv > 0); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + rv = nghttp2_hd_deflate_hd(&deflater, &bufs, nva, 2); + blocklen = nghttp2_bufs_len(&bufs); + + CU_ASSERT(0 == rv); + CU_ASSERT(blocklen > 0); CU_ASSERT(2 == deflater.ctx.hd_table.len); CU_ASSERT(1024 == deflater.ctx.hd_table_bufsize_max); - CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, &bufs, 0)); CU_ASSERT(2 == inflater.ctx.hd_table.len); CU_ASSERT(1024 == inflater.ctx.hd_table_bufsize_max); CU_ASSERT(4096 == inflater.settings_hd_table_bufsize_max); nva_out_reset(&out); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -776,25 +812,27 @@ static void check_deflate_inflate(nghttp2_hd_deflater *deflater, nghttp2_hd_inflater *inflater, nghttp2_nv *nva, size_t nvlen) { - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t blocklen; nva_out out; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nva_out_init(&out); - blocklen = nghttp2_hd_deflate_hd(deflater, &buf, nva, nvlen); + rv = nghttp2_hd_deflate_hd(deflater, &bufs, nva, nvlen); + blocklen = nghttp2_bufs_len(&bufs); + CU_ASSERT(0 == rv); CU_ASSERT(blocklen >= 0); - CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); - CU_ASSERT(blocklen == inflate_hd(inflater, &out, buf.pos, blocklen)); + CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0)); CU_ASSERT(nvlen == out.nvlen); assert_nv_equal(nva, out.nva, nvlen); nva_out_reset(&out); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); } void test_nghttp2_hd_deflate_inflate(void) diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 02c3444c..14ed7bd7 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -75,14 +75,27 @@ typedef struct { size_t padding_boundary; } my_user_data; -static void scripted_data_feed_init(scripted_data_feed *df, - uint8_t *data, size_t data_length) +static void scripted_data_feed_init2(scripted_data_feed *df, + nghttp2_bufs *bufs) { + nghttp2_buf_chain *ci; + nghttp2_buf *buf; + uint8_t *ptr; + size_t len; + memset(df, 0, sizeof(scripted_data_feed)); - memcpy(df->data, data, data_length); + ptr = df->data; + len = 0; + + for(ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + ptr = nghttp2_cpymem(ptr, buf->pos, nghttp2_buf_len(buf)); + len += nghttp2_buf_len(buf); + } + df->datamark = df->data; - df->datalimit = df->data+data_length; - df->feedseq[0] = data_length; + df->datalimit = df->data + len; + df->feedseq[0] = len; } static ssize_t null_send_callback(nghttp2_session *session, @@ -346,7 +359,7 @@ void test_nghttp2_session_recv(void) const nghttp2_nv nv[] = { MAKE_NV("url", "/") }; - nghttp2_buf buf; + nghttp2_bufs bufs; ssize_t framelen; nghttp2_frame frame; ssize_t i; @@ -354,8 +367,9 @@ void test_nghttp2_session_recv(void) nghttp2_nv *nva; ssize_t nvlen; nghttp2_hd_deflater deflater; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; @@ -370,12 +384,16 @@ void test_nghttp2_session_recv(void) nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + + scripted_data_feed_init2(&df, &bufs); + + framelen = nghttp2_bufs_len(&bufs); /* Send 1 byte per each read */ - for(i = 0; i < nghttp2_buf_len(&buf); ++i) { + for(i = 0; i < framelen; ++i) { df.feedseq[i] = 1; } @@ -387,17 +405,20 @@ void test_nghttp2_session_recv(void) } CU_ASSERT(1 == user_data.frame_recv_cb_called); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Received HEADERS without header block, which is valid */ nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 5, NGHTTP2_PRI_DEFAULT, NULL, 0); - framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); + + CU_ASSERT(0 == rv); nghttp2_frame_headers_free(&frame.headers); - scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); + scripted_data_feed_init2(&df, &bufs); user_data.frame_recv_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(1 == user_data.frame_recv_cb_called); @@ -407,21 +428,25 @@ void test_nghttp2_session_recv(void) /* Some tests for frame too large */ nghttp2_session_server_new(&session, &callbacks, &user_data); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); /* Receive PING with too large payload */ nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); - framelen = nghttp2_frame_pack_ping(&buf, &frame.ping); + rv = nghttp2_frame_pack_ping(&bufs, &frame.ping); + + CU_ASSERT(0 == rv); /* Add extra 16 bytes */ - nghttp2_buf_pos_reserve(&buf, nghttp2_buf_len(&buf) + 16); - buf.last += 16; - nghttp2_put_uint16be(buf.pos, frame.hd.length + 16); + nghttp2_bufs_seek_last_present(&bufs); + assert(nghttp2_buf_len(&bufs.cur->buf) >= 16); + + bufs.cur->buf.last += 16; + nghttp2_put_uint16be(bufs.cur->buf.pos, frame.hd.length + 16); nghttp2_frame_ping_free(&frame.ping); - scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); + scripted_data_feed_init2(&df, &bufs); user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == user_data.frame_recv_cb_called); @@ -430,7 +455,7 @@ void test_nghttp2_session_recv(void) CU_ASSERT(NGHTTP2_FRAME_SIZE_ERROR == OB_CTRL(item)->goaway.error_code); CU_ASSERT(0 == nghttp2_session_send(session)); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_session_del(session); } @@ -440,12 +465,12 @@ void test_nghttp2_session_recv_invalid_stream_id(void) nghttp2_session_callbacks callbacks; scripted_data_feed df; my_user_data user_data; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; nghttp2_frame frame; nghttp2_hd_deflater deflater; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback = scripted_recv_callback; @@ -458,17 +483,18 @@ void test_nghttp2_session_recv_invalid_stream_id(void) nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 2, NGHTTP2_PRI_DEFAULT, NULL, 0); - framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); + scripted_data_feed_init2(&df, &bufs); nghttp2_frame_headers_free(&frame.headers); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -482,14 +508,14 @@ void test_nghttp2_session_recv_invalid_frame(void) const nghttp2_nv nv[] = { MAKE_NV("url", "/") }; - nghttp2_buf buf; - ssize_t framelen; + nghttp2_bufs bufs; nghttp2_frame frame; nghttp2_nv *nva; ssize_t nvlen; nghttp2_hd_deflater deflater; + int rv; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback = scripted_recv_callback; @@ -503,11 +529,12 @@ void test_nghttp2_session_recv_invalid_frame(void) nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); + scripted_data_feed_init2(&df, &bufs); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); @@ -515,13 +542,13 @@ void test_nghttp2_session_recv_invalid_frame(void) /* Receive exactly same bytes of HEADERS is treated as subsequent HEADERS (e.g., trailers */ - scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); + scripted_data_feed_init2(&df, &bufs); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(0 == user_data.frame_send_cb_called); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers); nghttp2_hd_deflate_free(&deflater); @@ -680,7 +707,8 @@ void test_nghttp2_session_recv_continuation(void) nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; - nghttp2_buf buf; + nghttp2_bufs bufs; + nghttp2_buf *buf; ssize_t rv; my_user_data ud; nghttp2_hd_deflater deflater; @@ -688,7 +716,7 @@ void test_nghttp2_session_recv_continuation(void) size_t datalen; nghttp2_frame_hd cont_hd; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_header_callback = on_header_callback; @@ -702,16 +730,21 @@ void test_nghttp2_session_recv_continuation(void) nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); + + /* make sure that all data is in the first buf */ + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); nghttp2_frame_headers_free(&frame.headers); /* HEADERS's payload is 1 byte */ - memcpy(data, buf.pos, 9); + memcpy(data, buf->pos, 9); datalen = 9; - buf.pos += 9; + buf->pos += 9; nghttp2_put_uint16be(data, 1); @@ -724,23 +757,23 @@ void test_nghttp2_session_recv_continuation(void) nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); datalen += NGHTTP2_FRAME_HDLEN; - memcpy(data + datalen, buf.pos, cont_hd.length); + memcpy(data + datalen, buf->pos, cont_hd.length); datalen += cont_hd.length; - buf.pos += cont_hd.length; + buf->pos += cont_hd.length; /* Second CONTINUATION, rest of the bytes */ - cont_hd.length = nghttp2_buf_len(&buf); + cont_hd.length = nghttp2_buf_len(buf); cont_hd.flags = NGHTTP2_FLAG_END_HEADERS; cont_hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); datalen += NGHTTP2_FRAME_HDLEN; - memcpy(data + datalen, buf.pos, cont_hd.length); + memcpy(data + datalen, buf->pos, cont_hd.length); datalen += cont_hd.length; - buf.pos += cont_hd.length; + buf->pos += cont_hd.length; - CU_ASSERT(0 == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == nghttp2_buf_len(buf)); ud.header_cb_called = 0; rv = nghttp2_session_mem_recv(session, data, datalen); @@ -759,25 +792,32 @@ void test_nghttp2_session_recv_continuation(void) nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_NONE, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - nghttp2_buf_reset(&buf); - rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + nghttp2_bufs_reset(&bufs); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); nghttp2_frame_headers_free(&frame.headers); - memcpy(data, buf.pos, nghttp2_buf_len(&buf)); - datalen = nghttp2_buf_len(&buf); + + /* make sure that all data is in the first buf */ + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + memcpy(data, buf->pos, nghttp2_buf_len(buf)); + datalen = nghttp2_buf_len(buf); /* Followed by PRIORITY */ nghttp2_frame_priority_init(&frame.priority, 1, 0); - nghttp2_buf_reset(&buf); + nghttp2_bufs_reset(&bufs); - rv = nghttp2_frame_pack_priority(&buf, &frame.priority); + rv = nghttp2_frame_pack_priority(&bufs, &frame.priority); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); - memcpy(data + datalen, buf.pos, nghttp2_buf_len(&buf)); - datalen += nghttp2_buf_len(&buf); + memcpy(data + datalen, buf->pos, nghttp2_buf_len(buf)); + datalen += nghttp2_buf_len(buf); ud.begin_headers_cb_called = 0; rv = nghttp2_session_mem_recv(session, data, datalen); @@ -787,7 +827,7 @@ void test_nghttp2_session_recv_continuation(void) CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -803,13 +843,14 @@ void test_nghttp2_session_recv_premature_headers(void) nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; - nghttp2_buf buf; + nghttp2_bufs bufs; + nghttp2_buf *buf; ssize_t rv; my_user_data ud; nghttp2_hd_deflater deflater; nghttp2_outbound_item *item; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); @@ -820,24 +861,28 @@ void test_nghttp2_session_recv_premature_headers(void) nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); nghttp2_frame_headers_free(&frame.headers); - /* Intentionally feed payload cutting last 1 byte off */ - nghttp2_put_uint16be(buf.pos, frame.hd.length - 1); - rv = nghttp2_session_mem_recv(session, buf.pos, nghttp2_buf_len(&buf) - 1); + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); - CU_ASSERT((ssize_t)(nghttp2_buf_len(&buf) - 1) == rv); + /* Intentionally feed payload cutting last 1 byte off */ + nghttp2_put_uint16be(buf->pos, frame.hd.length - 1); + rv = nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf) - 1); + + CU_ASSERT((ssize_t)(nghttp2_buf_len(buf) - 1) == rv); item = nghttp2_session_get_next_ob_item(session); CU_ASSERT(NULL != item); CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item)); CU_ASSERT(NGHTTP2_COMPRESSION_ERROR == OB_CTRL(item)->goaway.error_code); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -855,7 +900,8 @@ void test_nghttp2_session_continue(void) MAKE_NV("user-agent", "nghttp2/1.0.0"), MAKE_NV("alpha", "bravo") }; - nghttp2_buf buf; + nghttp2_bufs bufs; + nghttp2_buf *buf; size_t framelen1, framelen2; ssize_t rv; uint8_t buffer[4096]; @@ -867,7 +913,7 @@ void test_nghttp2_session_continue(void) nghttp2_frame_hd data_hd; nghttp2_hd_deflater deflater; - nghttp2_buf_init(&buf); + frame_pack_bufs_init(&bufs); nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer)); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); @@ -885,27 +931,34 @@ void test_nghttp2_session_continue(void) nvlen = nghttp2_nv_array_copy(&nva, nv1, ARRLEN(nv1)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 1, NGHTTP2_PRI_DEFAULT, nva, nvlen); - rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); nghttp2_frame_headers_free(&frame.headers); - framelen1 = nghttp2_buf_len(&buf); - databuf.last = nghttp2_cpymem(databuf.last, buf.pos, nghttp2_buf_len(&buf)); + buf = &bufs.head->buf; + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + framelen1 = nghttp2_buf_len(buf); + databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); nvlen = nghttp2_nv_array_copy(&nva, nv2, ARRLEN(nv2)); nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, 3, NGHTTP2_PRI_DEFAULT, nva, nvlen); - nghttp2_buf_reset(&buf); - rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + nghttp2_bufs_reset(&bufs); + rv = nghttp2_frame_pack_headers(&bufs, &frame.headers, &deflater); - CU_ASSERT(rv == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == rv); + CU_ASSERT(nghttp2_bufs_len(&bufs) > 0); nghttp2_frame_headers_free(&frame.headers); - framelen2 = nghttp2_buf_len(&buf); - databuf.last = nghttp2_cpymem(databuf.last, buf.pos, nghttp2_buf_len(&buf)); + assert(nghttp2_bufs_len(&bufs) == nghttp2_buf_len(buf)); + + framelen2 = nghttp2_buf_len(buf); + databuf.last = nghttp2_cpymem(databuf.last, buf->pos, nghttp2_buf_len(buf)); /* Receive 1st HEADERS and pause */ user_data.begin_headers_cb_called = 0; @@ -1020,7 +1073,7 @@ void test_nghttp2_session_continue(void) CU_ASSERT(0 == rv); CU_ASSERT(1 == user_data.frame_recv_cb_called); - nghttp2_buf_free(&buf); + nghttp2_bufs_free(&bufs); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -2266,7 +2319,8 @@ void test_nghttp2_submit_data(void) nghttp2_private_data *data_frame; nghttp2_frame_hd hd; nghttp2_active_outbound_item *aob; - nghttp2_buf *framebuf; + nghttp2_bufs *framebufs; + nghttp2_buf *buf; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = block_count_send_callback; @@ -2275,7 +2329,7 @@ void test_nghttp2_submit_data(void) ud.data_source_length = NGHTTP2_DATA_PAYLOAD_LENGTH * 2; CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); aob = &session->aob; - framebuf = &aob->framebuf; + framebufs = &aob->framebufs; nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING, @@ -2287,7 +2341,9 @@ void test_nghttp2_submit_data(void) ud.block_count = 0; CU_ASSERT(0 == nghttp2_session_send(session)); data_frame = nghttp2_outbound_item_get_data_frame(aob->item); - nghttp2_frame_unpack_frame_hd(&hd, framebuf->pos); + + buf = &framebufs->head->buf; + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); /* frame->hd.flags has these flags */ @@ -2297,7 +2353,7 @@ void test_nghttp2_submit_data(void) ud.block_count = 1; CU_ASSERT(0 == nghttp2_session_send(session)); data_frame = nghttp2_outbound_item_get_data_frame(aob->item); - nghttp2_frame_unpack_frame_hd(&hd, framebuf->pos); + nghttp2_frame_unpack_frame_hd(&hd, buf->pos); /* This is the last frame, so we must have following flags */ CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT) == hd.flags); @@ -2349,6 +2405,9 @@ void test_nghttp2_submit_request_without_data(void) nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; + nghttp2_bufs bufs; + + frame_pack_bufs_init(&bufs); nva_out_init(&out); acc.length = 0; @@ -2367,12 +2426,14 @@ void test_nghttp2_submit_request_without_data(void) CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - inflate_hd(&inflater, &out, acc.buf + 8, acc.length - 8); + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN); CU_ASSERT(nvnameeq(":version", &out.nva[0])); nghttp2_frame_headers_free(&frame.headers); nva_out_reset(&out); + nghttp2_bufs_free(&bufs); nghttp2_hd_inflate_free(&inflater); nghttp2_session_del(session); } @@ -2421,6 +2482,9 @@ void test_nghttp2_submit_response_without_data(void) nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; + nghttp2_bufs bufs; + + frame_pack_bufs_init(&bufs); nva_out_init(&out); acc.length = 0; @@ -2442,11 +2506,13 @@ void test_nghttp2_submit_response_without_data(void) CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - inflate_hd(&inflater, &out, acc.buf + 8, acc.length - 8); + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN); CU_ASSERT(nvnameeq(":version", &out.nva[0])); nva_out_reset(&out); + nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers); nghttp2_hd_inflate_free(&inflater); nghttp2_session_del(session); @@ -2591,6 +2657,9 @@ void test_nghttp2_submit_headers(void) nghttp2_frame frame; nghttp2_hd_inflater inflater; nva_out out; + nghttp2_bufs bufs; + + frame_pack_bufs_init(&bufs); nva_out_init(&out); acc.length = 0; @@ -2633,11 +2702,13 @@ void test_nghttp2_submit_headers(void) CU_ASSERT(0 == unpack_frame(&frame, acc.buf, acc.length)); - inflate_hd(&inflater, &out, acc.buf + 8, acc.length - 8); + nghttp2_bufs_add(&bufs, acc.buf, acc.length); + inflate_hd(&inflater, &out, &bufs, NGHTTP2_FRAME_HDLEN); CU_ASSERT(nvnameeq(":version", &out.nva[0])); nva_out_reset(&out); + nghttp2_bufs_free(&bufs); nghttp2_frame_headers_free(&frame.headers); nghttp2_hd_inflate_free(&inflater); @@ -4066,13 +4137,14 @@ void test_nghttp2_session_data_backoff_by_high_pri_frame(void) nghttp2_session_del(session); } -static void check_session_recv_data_with_padding(const uint8_t *in, - size_t inlen, +static void check_session_recv_data_with_padding(nghttp2_bufs *bufs, size_t datalen) { nghttp2_session *session; my_user_data ud; nghttp2_session_callbacks callbacks; + uint8_t *in; + size_t inlen; memset(&callbacks, 0, sizeof(callbacks)); callbacks.on_frame_recv_callback = on_frame_recv_callback; @@ -4083,13 +4155,17 @@ static void check_session_recv_data_with_padding(const uint8_t *in, NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING, NULL); + inlen = nghttp2_bufs_remove(bufs, &in); + ud.frame_recv_cb_called = 0; ud.data_chunk_len = 0; + CU_ASSERT((ssize_t)inlen == nghttp2_session_mem_recv(session, in, inlen)); CU_ASSERT(1 == ud.frame_recv_cb_called); CU_ASSERT(datalen == ud.data_chunk_len); + free(in); nghttp2_session_del(session); } @@ -4124,14 +4200,12 @@ void test_nghttp2_session_pack_data_with_padding(void) frame = OB_DATA(session->aob.item); CU_ASSERT(ud.padding_boundary - datalen == frame->padlen); - CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW); - CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH); + /* We no longer set PAD_HIGH and PAD_LOW flags in frame->hd */ + /* CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW); */ + /* CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH); */ /* Check reception of this DATA frame */ - check_session_recv_data_with_padding - (session->aob.framebuf.pos, - session->aob.framebuf.mark - session->aob.framebuf.pos, - datalen); + check_session_recv_data_with_padding(&session->aob.framebufs, datalen); nghttp2_session_del(session); @@ -4150,14 +4224,12 @@ void test_nghttp2_session_pack_data_with_padding(void) frame = OB_DATA(session->aob.item); CU_ASSERT((frame->padlen + datalen) % ud.padding_boundary == 0); - CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW); + /* We no longer set PAD_HIGH and PAD_LOW flags in frame->hd */ + /* CU_ASSERT(frame->hd.flags & NGHTTP2_FLAG_PAD_LOW); */ CU_ASSERT(0 == (frame->hd.flags & NGHTTP2_FLAG_PAD_HIGH)); /* Check reception of this DATA frame */ - check_session_recv_data_with_padding - (session->aob.framebuf.pos, - session->aob.framebuf.mark - session->aob.framebuf.pos, - datalen); + check_session_recv_data_with_padding(&session->aob.framebufs, datalen); nghttp2_session_del(session); } diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c index a2a7c56a..006b210e 100644 --- a/tests/nghttp2_test_helper.c +++ b/tests/nghttp2_test_helper.c @@ -28,8 +28,15 @@ #include -int unpack_framebuf(nghttp2_frame *frame, nghttp2_buf *buf) +#include "nghttp2_helper.h" + +int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs) { + nghttp2_buf *buf; + + /* Assuming we have required data in first buffer. We don't decode + header block so, we don't mind its space */ + buf = &bufs->head->buf; return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf)); } @@ -143,28 +150,66 @@ void add_out(nva_out *out, nghttp2_nv *nv) } ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, - uint8_t *buf, size_t buflen) + nghttp2_bufs *bufs, size_t offset) { ssize_t rv; nghttp2_nv nv; int inflate_flags; - size_t initial = buflen; + nghttp2_buf_chain *ci; + nghttp2_buf *buf; + nghttp2_buf bp; + int final; + size_t processed; - for(;;) { - inflate_flags = 0; - rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, buf, buflen, 1); - if(rv < 0) { - return rv; + processed = 0; + + for(ci = bufs->head; ci; ci = ci->next) { + buf = &ci->buf; + final = nghttp2_buf_len(buf) == 0 || ci->next == NULL; + bp = *buf; + + if(offset) { + int n; + + n = nghttp2_min((ssize_t)offset, nghttp2_buf_len(&bp)); + bp.pos += n; + offset -= n; } - buf += rv; - buflen -= rv; - if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { - add_out(out, &nv); - } - if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - break; + + for(;;) { + inflate_flags = 0; + rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, + bp.pos, nghttp2_buf_len(&bp), final); + + if(rv < 0) { + return rv; + } + + bp.pos += rv; + processed += rv; + + if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + add_out(out, &nv); + } + if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + break; + } } } + nghttp2_hd_inflate_end_headers(inflater); - return initial - buflen; + + return processed; +} + +void frame_pack_bufs_init(nghttp2_bufs *bufs) +{ + /* 2 for PAD_HIGH and PAD_LOW */ + nghttp2_bufs_init2(bufs, 4096, 16, NGHTTP2_FRAME_HDLEN + 2); +} + +void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size) +{ + /* 2 for PAD_HIGH and PAD_LOW */ + nghttp2_bufs_init2(bufs, chunk_size, 16, NGHTTP2_FRAME_HDLEN + 2); } diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h index 0a0be66e..6ed49bca 100644 --- a/tests/nghttp2_test_helper.h +++ b/tests/nghttp2_test_helper.h @@ -54,7 +54,7 @@ free(a); \ } while(0); -int unpack_framebuf(nghttp2_frame *frame, nghttp2_buf *buf); +int unpack_framebuf(nghttp2_frame *frame, nghttp2_bufs *bufs); int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len); @@ -75,6 +75,10 @@ void nva_out_reset(nva_out *out); void add_out(nva_out *out, nghttp2_nv *nv); ssize_t inflate_hd(nghttp2_hd_inflater *inflater, nva_out *out, - uint8_t *buf, size_t buflen); + nghttp2_bufs *bufs, size_t offset); + +void frame_pack_bufs_init(nghttp2_bufs *bufs); + +void bufs_large_init(nghttp2_bufs *bufs, size_t chunk_size); #endif /* NGHTTP2_TEST_HELPER_H */