From abf81b5bb75ebc2ae8fef7a2b3637bfe4dc66f1b Mon Sep 17 00:00:00 2001 From: Wenfeng Liu Date: Mon, 15 Aug 2016 10:28:45 +0000 Subject: [PATCH 1/4] lib: Add nghttp2_hd_deflate_hd_vec() deflate API to support multiple bufs input --- lib/includes/nghttp2/nghttp2.h | 33 +++++++++++++++++ lib/nghttp2_buf.c | 67 +++++++++++++++++++++++++++++++++- lib/nghttp2_buf.h | 25 ++++++++++++- lib/nghttp2_hd.c | 34 +++++++++++++++++ 4 files changed, 155 insertions(+), 4 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 0ed36051..4114415c 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -4516,6 +4516,39 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nva, size_t nvlen); +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |inlen| size of buf vector |bufsin|, each buf has length |buflen|. + * |buflens| is a |inlen| size of array, which will be used to store the writen + * size of each chunk. If one chunk is filled up, next chunk will be used. + * If |bufsin| is not large enough to store the deflated header block, + * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The + * caller should use `nghttp2_hd_deflate_bound()` to know the upper + * bound of buffer size required to deflate given header name/value + * pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t +nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, uint8_t **bufsin, + size_t inlen, size_t buflen, size_t *buflens, + const nghttp2_nv *nva, size_t nvlen); + /** * @function * diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c index 9dc2f950..afe54464 100644 --- a/lib/nghttp2_buf.c +++ b/lib/nghttp2_buf.c @@ -223,13 +223,62 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, return 0; } +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t **bufs_in, size_t in_len, + size_t buf_len, nghttp2_mem *mem) { + size_t i = 0; + nghttp2_buf_chain *cur_chain; + nghttp2_buf_chain *pre_chain = NULL; + nghttp2_buf_chain *head_chain = NULL; + + for (i = 0; i < in_len; ++i) { + uint8_t *begin = bufs_in[i]; + + cur_chain = nghttp2_mem_malloc(mem, sizeof(nghttp2_buf_chain)); + if (cur_chain == NULL) { + while (head_chain) { + nghttp2_buf_chain *tmp_chain = head_chain->next; + nghttp2_mem_free(mem, head_chain); + head_chain = tmp_chain; + } + return NGHTTP2_ERR_NOMEM; + } + + cur_chain->next = NULL; + nghttp2_buf_wrap_init(&cur_chain->buf, begin, buf_len); + + if (pre_chain) { + pre_chain->next = cur_chain; + } else { + head_chain = cur_chain; + } + + pre_chain = cur_chain; + } + + bufs->mem = mem; + bufs->offset = 0; + + bufs->head = head_chain; + bufs->cur = bufs->head; + + bufs->chunk_length = buf_len; + bufs->chunk_used = in_len; + bufs->max_chunk = in_len; + bufs->chunk_keep = in_len; + + return 0; +} + void nghttp2_bufs_wrap_free(nghttp2_bufs *bufs) { if (bufs == NULL) { return; } - nghttp2_mem_free(bufs->mem, bufs->head); - bufs->head = NULL; + while (bufs->head) { + nghttp2_buf_chain *tmp_chain = bufs->head->next; + nghttp2_mem_free(bufs->mem, bufs->head); + bufs->head = tmp_chain; + } } void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs) { @@ -256,6 +305,20 @@ size_t nghttp2_bufs_len(nghttp2_bufs *bufs) { return len; } +size_t nghttp2_bufs_len_vec(nghttp2_bufs *bufs, size_t *buflens) { + nghttp2_buf_chain *ci; + size_t len, total_len = 0; + int i = 0; + + for (ci = bufs->head; ci; ci = ci->next) { + len = nghttp2_buf_len(&ci->buf); + total_len += len; + buflens[i++] = len; + } + + return total_len; +} + static size_t bufs_avail(nghttp2_bufs *bufs) { return nghttp2_buf_avail(&bufs->cur->buf) + (bufs->chunk_length - bufs->offset) * diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h index 6770d6f3..c7259c03 100644 --- a/lib/nghttp2_buf.h +++ b/lib/nghttp2_buf.h @@ -197,7 +197,7 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs); /* * Initializes |bufs| using supplied buffer |begin| of length |len|. * The first buffer bufs->head uses buffer |begin|. The buffer size - * is fixed and no allocate extra chunk buffer is allocated. In other + * is fixed and no extra chunk buffer is allocated. In other * words, max_chunk = chunk_keep = 1. To free the resource allocated * for |bufs|, use nghttp2_bufs_wrap_free(). * @@ -210,6 +210,22 @@ void nghttp2_bufs_free(nghttp2_bufs *bufs); int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, nghttp2_mem *mem); +/* + * Initializes |bufs| using supplied |in_len| size of buf vector |bufs_in|, + * each buf has length |buf_len|. + * The buffer size is fixed and no extra chunk buffer is allocated. + * In other words, max_chunk = chunk_keep = |in_len|. To free the resource allocated + * for |bufs|, use nghttp2_bufs_wrap_free(). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory. + */ +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t **bufs_in, size_t in_len, + size_t buf_len, nghttp2_mem *mem); + /* * Frees any related resource to the |bufs|. This function does not * free supplied buffer provided in nghttp2_bufs_wrap_init(). @@ -381,8 +397,13 @@ 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|. + * Returns the total buffer length of |bufs|. */ size_t nghttp2_bufs_len(nghttp2_bufs *bufs); +/* + * Returns the total buffer length of |bufs|, and each buffer length in |buflens|. + */ +size_t nghttp2_bufs_len_vec(nghttp2_bufs *bufs, size_t *buflens); + #endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index fe5c5f52..33e85299 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -1503,6 +1503,40 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, return (ssize_t)buflen; } +ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, uint8_t **bufsin, + size_t inlen, size_t buflen, size_t *buflens, + const nghttp2_nv *nv, size_t nvlen) { + nghttp2_bufs bufs; + int rv; + nghttp2_mem *mem; + + memset(buflens, 0, sizeof(size_t) * inlen); + + mem = deflater->ctx.mem; + + rv = nghttp2_bufs_wrap_init2(&bufs, bufsin, inlen, buflen, mem); + + if (rv != 0) { + return rv; + } + + rv = nghttp2_hd_deflate_hd_bufs(deflater, &bufs, nv, nvlen); + + buflen = nghttp2_bufs_len_vec(&bufs, buflens); + + nghttp2_bufs_wrap_free(&bufs); + + if (rv == NGHTTP2_ERR_BUFFER_ERROR) { + return NGHTTP2_ERR_INSUFF_BUFSIZE; + } + + if (rv != 0) { + return rv; + } + + return (ssize_t)buflen; +} + size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater _U_, const nghttp2_nv *nva, size_t nvlen) { size_t n = 0; From c4d36aeff7d88b4bfff087e61b120bdb0c64a46b Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 16 Aug 2016 10:55:51 +0900 Subject: [PATCH 2/4] Make parameters const pointer --- lib/includes/nghttp2/nghttp2.h | 6 +++--- lib/nghttp2_buf.c | 6 +++--- lib/nghttp2_buf.h | 6 +++--- lib/nghttp2_hd.c | 7 ++++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 51d003fc..bf814edd 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -4628,9 +4628,9 @@ nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, * The provided |buflen| size is too small to hold the output. */ NGHTTP2_EXTERN ssize_t -nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, uint8_t **bufsin, - size_t inlen, size_t buflen, size_t *buflens, - const nghttp2_nv *nva, size_t nvlen); +nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, uint8_t *const *bufsin, + size_t inlen, size_t buflen, size_t *const buflens, + const nghttp2_nv *nva, size_t nvlen); /** * @function diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c index afe54464..490cae67 100644 --- a/lib/nghttp2_buf.c +++ b/lib/nghttp2_buf.c @@ -223,8 +223,8 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, return 0; } -int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t **bufs_in, size_t in_len, - size_t buf_len, nghttp2_mem *mem) { +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t *const *bufs_in, + size_t in_len, size_t buf_len, nghttp2_mem *mem) { size_t i = 0; nghttp2_buf_chain *cur_chain; nghttp2_buf_chain *pre_chain = NULL; @@ -305,7 +305,7 @@ size_t nghttp2_bufs_len(nghttp2_bufs *bufs) { return len; } -size_t nghttp2_bufs_len_vec(nghttp2_bufs *bufs, size_t *buflens) { +size_t nghttp2_bufs_len_vec(nghttp2_bufs *bufs, size_t *const buflens) { nghttp2_buf_chain *ci; size_t len, total_len = 0; int i = 0; diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h index c7259c03..36e8df95 100644 --- a/lib/nghttp2_buf.h +++ b/lib/nghttp2_buf.h @@ -223,8 +223,8 @@ int nghttp2_bufs_wrap_init(nghttp2_bufs *bufs, uint8_t *begin, size_t len, * NGHTTP2_ERR_NOMEM * Out of memory. */ -int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t **bufs_in, size_t in_len, - size_t buf_len, nghttp2_mem *mem); +int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t *const *bufs_in, + size_t in_len, size_t buf_len, nghttp2_mem *mem); /* * Frees any related resource to the |bufs|. This function does not @@ -404,6 +404,6 @@ size_t nghttp2_bufs_len(nghttp2_bufs *bufs); /* * Returns the total buffer length of |bufs|, and each buffer length in |buflens|. */ -size_t nghttp2_bufs_len_vec(nghttp2_bufs *bufs, size_t *buflens); +size_t nghttp2_bufs_len_vec(nghttp2_bufs *bufs, size_t *const buflens); #endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index 49c23245..acdbc874 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -1503,9 +1503,10 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, return (ssize_t)buflen; } -ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, uint8_t **bufsin, - size_t inlen, size_t buflen, size_t *buflens, - const nghttp2_nv *nv, size_t nvlen) { +ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + uint8_t *const *bufsin, size_t inlen, + size_t buflen, size_t *const buflens, + const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; From c6111b3792d3beb76bd7266965dc40c476276839 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 16 Aug 2016 11:11:06 +0900 Subject: [PATCH 3/4] Add test for nghttp2_hd_deflate_hd_vec --- tests/main.c | 2 ++ tests/nghttp2_hd_test.c | 51 +++++++++++++++++++++++++++++++++++++++++ tests/nghttp2_hd_test.h | 1 + 3 files changed, 54 insertions(+) diff --git a/tests/main.c b/tests/main.c index f4890f08..e4f4900f 100644 --- a/tests/main.c +++ b/tests/main.c @@ -383,6 +383,8 @@ int main(int argc _U_, char *argv[] _U_) { !CU_add_test(pSuite, "hd_no_index", test_nghttp2_hd_no_index) || !CU_add_test(pSuite, "hd_deflate_bound", test_nghttp2_hd_deflate_bound) || !CU_add_test(pSuite, "hd_public_api", test_nghttp2_hd_public_api) || + !CU_add_test(pSuite, "hd_deflate_hd_vec", + test_nghttp2_hd_deflate_hd_vec) || !CU_add_test(pSuite, "hd_decode_length", test_nghttp2_hd_decode_length) || !CU_add_test(pSuite, "hd_huff_encode", test_nghttp2_hd_huff_encode) || !CU_add_test(pSuite, "adjust_local_window_size", diff --git a/tests/nghttp2_hd_test.c b/tests/nghttp2_hd_test.c index 3d8ab83b..0e74d752 100644 --- a/tests/nghttp2_hd_test.c +++ b/tests/nghttp2_hd_test.c @@ -1265,6 +1265,57 @@ void test_nghttp2_hd_public_api(void) { nghttp2_hd_deflate_del(deflater); } +void test_nghttp2_hd_deflate_hd_vec(void) { + nghttp2_hd_deflater *deflater; + nghttp2_hd_inflater *inflater; + nghttp2_nv nva[] = { + MAKE_NV(":method", "PUT"), MAKE_NV(":scheme", "https"), + MAKE_NV(":authority", "localhost:3000"), + MAKE_NV(":path", "/usr/foo/alpha/bravo"), + MAKE_NV("content-type", "image/png"), + MAKE_NV("content-length", "1000000007"), + }; + uint8_t buf[4096]; + ssize_t blocklen; + nghttp2_mem *mem; + uint8_t *bufsin[2]; + size_t buflens[2] = {0}; + size_t buflen; + nghttp2_bufs bufs; + nva_out out; + + mem = nghttp2_mem_default(); + + nva_out_init(&out); + + nghttp2_hd_deflate_new(&deflater, 4096); + nghttp2_hd_inflate_new(&inflater); + + buflen = nghttp2_hd_deflate_bound(deflater, nva, ARRLEN(nva)); + + bufsin[0] = &buf[0]; + bufsin[1] = &buf[buflen / 2]; + + blocklen = nghttp2_hd_deflate_hd_vec(deflater, bufsin, ARRLEN(bufsin), + buflen / 2, buflens, nva, ARRLEN(nva)); + + CU_ASSERT(blocklen > 0); + + nghttp2_bufs_wrap_init(&bufs, buf, (size_t)blocklen, mem); + bufs.head->buf.last += blocklen; + + CU_ASSERT(blocklen == inflate_hd(inflater, &out, &bufs, 0, mem)); + + CU_ASSERT(ARRLEN(nva) == out.nvlen); + assert_nv_equal(nva, out.nva, ARRLEN(nva), mem); + + nghttp2_bufs_wrap_free(&bufs); + + nghttp2_hd_inflate_del(inflater); + nghttp2_hd_deflate_del(deflater); + nva_out_reset(&out, mem); +} + static size_t encode_length(uint8_t *buf, uint64_t n, size_t prefix) { size_t k = (size_t)((1 << prefix) - 1); size_t len = 0; diff --git a/tests/nghttp2_hd_test.h b/tests/nghttp2_hd_test.h index bf28282c..ab5fcae6 100644 --- a/tests/nghttp2_hd_test.h +++ b/tests/nghttp2_hd_test.h @@ -47,6 +47,7 @@ void test_nghttp2_hd_deflate_inflate(void); void test_nghttp2_hd_no_index(void); void test_nghttp2_hd_deflate_bound(void); void test_nghttp2_hd_public_api(void); +void test_nghttp2_hd_deflate_hd_vec(void); void test_nghttp2_hd_decode_length(void); void test_nghttp2_hd_huff_encode(void); From 8acef2711ba2f8e98dfdb395538ebd607fabfe37 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 16 Aug 2016 13:02:24 +0900 Subject: [PATCH 4/4] Use pointer-to-pointer idiom to construct linked list --- lib/nghttp2_buf.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c index 490cae67..b28aaf85 100644 --- a/lib/nghttp2_buf.c +++ b/lib/nghttp2_buf.c @@ -227,8 +227,8 @@ int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t *const *bufs_in, size_t in_len, size_t buf_len, nghttp2_mem *mem) { size_t i = 0; nghttp2_buf_chain *cur_chain; - nghttp2_buf_chain *pre_chain = NULL; nghttp2_buf_chain *head_chain = NULL; + nghttp2_buf_chain **dst_chain = &head_chain; for (i = 0; i < in_len; ++i) { uint8_t *begin = bufs_in[i]; @@ -246,13 +246,8 @@ int nghttp2_bufs_wrap_init2(nghttp2_bufs *bufs, uint8_t *const *bufs_in, cur_chain->next = NULL; nghttp2_buf_wrap_init(&cur_chain->buf, begin, buf_len); - if (pre_chain) { - pre_chain->next = cur_chain; - } else { - head_chain = cur_chain; - } - - pre_chain = cur_chain; + *dst_chain = cur_chain; + dst_chain = &cur_chain->next; } bufs->mem = mem;