diff --git a/lib/Makefile.am b/lib/Makefile.am index 0084d2cb..08a6d7a2 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -33,6 +33,7 @@ lib_LTLIBRARIES = libnghttp2.la OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ nghttp2_buffer.c nghttp2_frame.c \ + nghttp2_buf.c \ nghttp2_stream.c nghttp2_outbound_item.c \ nghttp2_session.c nghttp2_submit.c \ nghttp2_helper.c \ @@ -42,6 +43,7 @@ OBJECTS = nghttp2_pq.c nghttp2_map.c nghttp2_queue.c \ HFILES = nghttp2_pq.h nghttp2_int.h nghttp2_map.h nghttp2_queue.h \ nghttp2_buffer.h nghttp2_frame.h \ + nghttp2_buf.h \ nghttp2_session.h nghttp2_helper.h nghttp2_stream.h nghttp2_int.h \ nghttp2_npn.h nghttp2_gzip.h \ nghttp2_submit.h nghttp2_outbound_item.h \ diff --git a/lib/nghttp2_buf.c b/lib/nghttp2_buf.c new file mode 100644 index 00000000..1cf36ae2 --- /dev/null +++ b/lib/nghttp2_buf.c @@ -0,0 +1,95 @@ +/* + * nghttp2 - HTTP/2.0 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "nghttp2_buf.h" + +#include "nghttp2_helper.h" + +void nghttp2_buf_init(nghttp2_buf *buf) +{ + buf->begin = NULL; + buf->end = NULL; + buf->pos = NULL; + buf->last = NULL; + buf->mark = NULL; +} + +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial) +{ + nghttp2_buf_init(buf); + return nghttp2_buf_reserve(buf, initial); +} + +void nghttp2_buf_free(nghttp2_buf *buf) +{ + free(buf->begin); +} + +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap) +{ + uint8_t *ptr; + size_t cap; + + cap = nghttp2_buf_cap(buf); + + if(cap >= new_cap) { + return 0; + } + + new_cap = nghttp2_max(new_cap, cap * 2); + + ptr = realloc(buf->begin, new_cap); + if(ptr == NULL) { + return NGHTTP2_ERR_NOMEM; + } + + buf->pos = ptr + (buf->pos - buf->begin); + buf->last = ptr + (buf->last - buf->begin); + buf->mark = ptr + (buf->mark - buf->begin); + buf->begin = ptr; + buf->end = ptr + new_cap; + + return 0; +} + +int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap) +{ + return nghttp2_buf_reserve(buf, nghttp2_buf_pos_offset(buf) + new_rel_cap); +} + +int nghttp2_buf_last_reserve(nghttp2_buf *buf, size_t new_rel_cap) +{ + return nghttp2_buf_reserve(buf, nghttp2_buf_last_offset(buf) + new_rel_cap); +} + +void nghttp2_buf_reset(nghttp2_buf *buf) +{ + buf->pos = buf->last = buf->mark = buf->begin; +} + +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) +{ + buf->begin = buf->pos = buf->last = buf->mark = begin; + buf->end = begin + len; +} diff --git a/lib/nghttp2_buf.h b/lib/nghttp2_buf.h new file mode 100644 index 00000000..0bf9971a --- /dev/null +++ b/lib/nghttp2_buf.h @@ -0,0 +1,137 @@ +/* + * nghttp2 - HTTP/2.0 C Library + * + * Copyright (c) 2014 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2_BUF_H +#define NGHTTP2_BUF_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +#include "nghttp2_int.h" + +typedef struct { + /* This points to the beginning of the buffer. The effective range + of buffer is [begin, end). */ + uint8_t *begin; + /* This points to the memory one byte beyond the end of the + buffer. */ + uint8_t *end; + /* The position indicator for effective start of the buffer. pos <= + last must be hold. */ + uint8_t *pos; + /* The position indicator for effective one beyond of the end of the + buffer. last <= end must be hold. */ + uint8_t *last; + /* Mark arbitrary position in buffer [begin, end) */ + uint8_t *mark; +} nghttp2_buf; + +#define nghttp2_buf_len(BUF) ((BUF)->last - (BUF)->pos) +#define nghttp2_buf_avail(BUF) ((BUF)->end - (BUF)->last) +#define nghttp2_buf_cap(BUF) ((BUF)->end - (BUF)->begin) + +#define nghttp2_buf_pos_offset(BUF) ((BUF)->pos - (BUF)->begin) +#define nghttp2_buf_last_offset(BUF) ((BUF)->last - (BUF)->begin) + +#define nghttp2_buf_shift_right(BUF, AMT) \ + do { \ + (BUF)->pos += AMT; \ + (BUF)->last += AMT; \ + } while(0) + +#define nghttp2_buf_shift_left(BUF, AMT) \ + do { \ + (BUF)->pos -= AMT; \ + (BUF)->last -= AMT; \ + } while(0) + +/* + * Initializes the |buf|. No memory is allocated in this function. Use + * nghttp2_buf_reserve() or nghttp2_buf_reserve2() to allocate memory. + */ +void nghttp2_buf_init(nghttp2_buf *buf); + + +/* + * Initializes the |buf| and allocates at least |initial| bytes of + * memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_init2(nghttp2_buf *buf, size_t initial); + +/* + * Frees buffer in |buf|. + */ +void nghttp2_buf_free(nghttp2_buf *buf); + +/* + * Extends buffer so that nghttp2_buf_cap() returns at least + * |new_cap|. If extensions took place, buffer pointers in |buf| will + * change. + * + * This function returns 0 if it succeeds, or one of the followings + * negative error codes: + * + * NGHTTP2_ERR_NOMEM + * Out of memory + */ +int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap); + +/* + * This function behaves like nghttp2_buf_reserve(), but new capacity + * is calculated as nghttp2_buf_pos_offset(buf) + new_rel_cap. In + * other words, this function reserves memory at least |new_rel_cap| + * bytes from buf->pos. + */ +int nghttp2_buf_pos_reserve(nghttp2_buf *buf, size_t new_rel_cap); + +/* + * This function behaves like nghttp2_buf_reserve(), but new capacity + * is calculated as nghttp2_buf_last_offset(buf) + new_rel_cap. In + * other words, this function reserves memory at least |new_rel_cap| + * bytes from buf->last. + */ +int nghttp2_buf_last_reserve(nghttp2_buf *buf, size_t new_rel_cap); + +/* + * Resets pos, last, mark member of |buf| to buf->begin. + */ +void nghttp2_buf_reset(nghttp2_buf *buf); + +/* + * Initializes |buf| using supplied buffer |begin| of length + * |len|. Semantically, the application should not call *_reserve() or + * nghttp2_free() functions for |buf|. + */ +void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len); + +#endif /* NGHTTP2_BUF_H */ diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index b63a16aa..af83419f 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -224,50 +224,64 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame) } } -ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *bufoff_ptr, +ssize_t nghttp2_frame_pack_headers(nghttp2_buf *buf, nghttp2_headers *frame, nghttp2_hd_deflater *deflater) { - size_t payloadoff = NGHTTP2_FRAME_HEAD_LENGTH + 2; - size_t nv_offset = - payloadoff + nghttp2_frame_headers_payload_nv_offset(frame); + size_t nv_offset; ssize_t rv; - size_t payloadlen; - rv = nghttp2_hd_deflate_hd(deflater, buf_ptr, buflen_ptr, nv_offset, - frame->nva, frame->nvlen); - if(rv < 0) { - return rv; - } + assert(nghttp2_buf_len(buf) == 0); - payloadlen = nghttp2_frame_headers_payload_nv_offset(frame) + rv; + /* Account for possible PAD_HIGH and PAD_LOW */ + buf->pos += 2; - *bufoff_ptr = 2; - frame->padlen = 0; - frame->hd.length = payloadlen; - /* If frame->nvlen == 0, *buflen_ptr may be smaller than + 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_reserve_buffer(buf_ptr, buflen_ptr, nv_offset); + rv = nghttp2_buf_pos_reserve(buf, nv_offset); if(rv < 0) { return rv; } - memset(*buf_ptr + *bufoff_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH); + + 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); + buf->pos -= nv_offset; + + 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_HEAD_LENGTH); + /* pack ctrl header after length is determined */ - if(NGHTTP2_MAX_FRAME_LENGTH < payloadlen) { + if(NGHTTP2_MAX_FRAME_LENGTH < frame->hd.length) { /* Needs CONTINUATION */ nghttp2_frame_hd hd = frame->hd; + hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; hd.length = NGHTTP2_MAX_FRAME_LENGTH; - nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &hd); + + nghttp2_frame_pack_frame_hd(buf->pos, &hd); } else { - nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &frame->hd); + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); } + if(frame->hd.flags & NGHTTP2_FLAG_PRIORITY) { - nghttp2_put_uint32be(&(*buf_ptr)[payloadoff], frame->pri); + nghttp2_put_uint32be(buf->pos + NGHTTP2_FRAME_HDLEN, frame->pri); } - return *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length; + + return nghttp2_buf_len(buf); } int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, @@ -284,19 +298,28 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, return 0; } -ssize_t nghttp2_frame_pack_priority(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_priority(nghttp2_buf *buf, nghttp2_priority *frame) { ssize_t framelen= NGHTTP2_FRAME_HEAD_LENGTH + 4; int rv; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + + assert(nghttp2_buf_len(buf) == 0); + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->pri); - return framelen; + + memset(buf->last, 0, framelen); + + nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); + buf->last += NGHTTP2_FRAME_HDLEN; + + nghttp2_put_uint32be(buf->last, frame->pri); + buf->last += 4; + + return nghttp2_buf_len(buf); } void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, @@ -306,19 +329,28 @@ 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(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_rst_stream(nghttp2_buf *buf, nghttp2_rst_stream *frame) { ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + 4; int rv; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + + assert(nghttp2_buf_len(buf) == 0); + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->error_code); - return framelen; + + memset(buf->last, 0, framelen); + + nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); + buf->last += NGHTTP2_FRAME_HDLEN; + + nghttp2_put_uint32be(buf->last, frame->error_code); + buf->last += 4; + + return nghttp2_buf_len(buf); } void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, @@ -328,19 +360,28 @@ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, frame->error_code = nghttp2_get_uint32(payload); } -ssize_t nghttp2_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_settings(nghttp2_buf *buf, nghttp2_settings *frame) { ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length; int rv; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + + assert(nghttp2_buf_len(buf) == 0); + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - nghttp2_frame_pack_settings_payload(*buf_ptr + 8, frame->iv, frame->niv); - return framelen; + + memset(buf->last, 0, framelen); + + nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); + buf->last += NGHTTP2_FRAME_HDLEN; + + buf->last += nghttp2_frame_pack_settings_payload(buf->last, + frame->iv, frame->niv); + + return nghttp2_buf_len(buf); } size_t nghttp2_frame_pack_settings_payload(uint8_t *buf, @@ -395,47 +436,60 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, return 0; } -ssize_t nghttp2_frame_pack_push_promise(uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *bufoff_ptr, +ssize_t nghttp2_frame_pack_push_promise(nghttp2_buf *buf, nghttp2_push_promise *frame, nghttp2_hd_deflater *deflater) { - size_t payloadoff = NGHTTP2_FRAME_HEAD_LENGTH + 2; - size_t nv_offset = payloadoff + 4; + size_t nv_offset = NGHTTP2_FRAME_HDLEN + 4; ssize_t rv; - size_t payloadlen; - rv = nghttp2_hd_deflate_hd(deflater, buf_ptr, buflen_ptr, nv_offset, - frame->nva, frame->nvlen); - if(rv < 0) { - return rv; - } + assert(nghttp2_buf_len(buf) == 0); - payloadlen = 4 + rv; + /* Account for possible PAD_HIGH and PAD_LOW */ + buf->pos += 2; - *bufoff_ptr = 2; - frame->padlen = 0; - frame->hd.length = payloadlen; - /* If frame->nvlen == 0, *buflen_ptr may be smaller than + /* If frame->nvlen == 0, nghttp2_buf_len(buf) may be smaller than nv_offset */ - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, nv_offset); + rv = nghttp2_buf_pos_reserve(buf, nv_offset); if(rv < 0) { return rv; } - memset(*buf_ptr + *bufoff_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH); + + 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); + buf->pos -= nv_offset; + + if(rv < 0) { + return rv; + } + + frame->hd.length = 4 + 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_HEAD_LENGTH); + /* pack ctrl header after length is determined */ - if(NGHTTP2_MAX_FRAME_LENGTH < payloadlen) { + if(NGHTTP2_MAX_FRAME_LENGTH < frame->hd.length) { /* Needs CONTINUATION */ nghttp2_frame_hd hd = frame->hd; + hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; hd.length = NGHTTP2_MAX_FRAME_LENGTH; - nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &hd); + + nghttp2_frame_pack_frame_hd(buf->pos, &hd); } else { - nghttp2_frame_pack_frame_hd(*buf_ptr + *bufoff_ptr, &frame->hd); + nghttp2_frame_pack_frame_hd(buf->pos, &frame->hd); } - nghttp2_put_uint32be(&(*buf_ptr)[payloadoff], frame->promised_stream_id); - return *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length; + + nghttp2_put_uint32be(buf->pos + NGHTTP2_FRAME_HDLEN, + frame->promised_stream_id); + + return nghttp2_buf_len(buf); } int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, @@ -449,19 +503,27 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, return 0; } -ssize_t nghttp2_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_ping *frame) +ssize_t nghttp2_frame_pack_ping(nghttp2_buf *buf, nghttp2_ping *frame) { ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + 8; int rv; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + + assert(nghttp2_buf_len(buf) == 0); + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - memcpy(&(*buf_ptr)[8], frame->opaque_data, sizeof(frame->opaque_data)); - return framelen; + + memset(buf->last, 0, framelen); + + nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); + buf->last += NGHTTP2_FRAME_HDLEN; + + memcpy(buf->last, frame->opaque_data, sizeof(frame->opaque_data)); + buf->last += sizeof(frame->opaque_data); + + return nghttp2_buf_len(buf); } void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, @@ -471,21 +533,33 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, memcpy(frame->opaque_data, payload, sizeof(frame->opaque_data)); } -ssize_t nghttp2_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_goaway *frame) +ssize_t nghttp2_frame_pack_goaway(nghttp2_buf *buf, nghttp2_goaway *frame) { ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + frame->hd.length; int rv; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + + assert(nghttp2_buf_len(buf) == 0); + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->last_stream_id); - nghttp2_put_uint32be(&(*buf_ptr)[12], frame->error_code); - memcpy(&(*buf_ptr)[16], frame->opaque_data, frame->opaque_data_len); - return framelen; + + memset(buf->last, 0, framelen); + + nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); + buf->last += NGHTTP2_FRAME_HDLEN; + + nghttp2_put_uint32be(buf->last, frame->last_stream_id); + buf->last += 4; + + 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; + + return nghttp2_buf_len(buf); } void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, @@ -499,19 +573,28 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, frame->opaque_data_len = 0; } -ssize_t nghttp2_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_window_update(nghttp2_buf *buf, nghttp2_window_update *frame) { ssize_t framelen = NGHTTP2_FRAME_HEAD_LENGTH + 4; int rv; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + + assert(nghttp2_buf_len(buf) == 0); + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } - memset(*buf_ptr, 0, framelen); - nghttp2_frame_pack_frame_hd(*buf_ptr, &frame->hd); - nghttp2_put_uint32be(&(*buf_ptr)[8], frame->window_size_increment); - return framelen; + + memset(buf->last, 0, framelen); + + nghttp2_frame_pack_frame_hd(buf->last, &frame->hd); + buf->last += NGHTTP2_FRAME_HDLEN; + + nghttp2_put_uint32be(buf->last, frame->window_size_increment); + buf->last += 4; + + return nghttp2_buf_len(buf); } void nghttp2_frame_unpack_window_update_payload(nghttp2_window_update *frame, @@ -654,42 +737,34 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv) return 1; } -int nghttp2_frame_add_pad(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *bufoff_ptr, - uint8_t *flags_ptr, - size_t payloadlen, - size_t padlen) +void nghttp2_frame_set_pad(nghttp2_buf *buf, uint8_t *flags_ptr, size_t padlen) { - int rv; size_t trail_padlen = 0; - /* extra 2 bytes for PAD_HIGH and PAD_LOW. */ - size_t trail_padoff = *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH + payloadlen; - if(padlen > 257) { + if(padlen > 256) { uint8_t *p; - *bufoff_ptr -= 2; + trail_padlen = padlen - 2; *flags_ptr |= NGHTTP2_FLAG_PAD_HIGH | NGHTTP2_FLAG_PAD_LOW; - p = *buf_ptr + *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH; + + 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_HEAD_LENGTH; *p++ = trail_padlen >> 8; *p = trail_padlen & 0xff; + } else if(padlen > 0) { - --*bufoff_ptr; + assert(nghttp2_buf_pos_offset(buf) >= 1); + + /* Consume previous 1 byte, shifting 1 bytes to the left */ + nghttp2_buf_shift_left(buf, 1); + trail_padlen = padlen - 1; *flags_ptr |= NGHTTP2_FLAG_PAD_LOW; - (*buf_ptr)[*bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH] = trail_padlen; - } else { - return 0; - } - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, - trail_padoff + trail_padlen); - if(rv != 0) { - return rv; + *(buf->pos + NGHTTP2_FRAME_HEAD_LENGTH) = trail_padlen; } - /* We have to zero out padding bytes so that we won't reveal the - possible internal data to the remote peer */ - memset((*buf_ptr) + trail_padoff, 0, trail_padlen); - - return 0; } diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index 6c62bbe0..e0ef24c6 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -32,6 +32,7 @@ #include #include "nghttp2_hd.h" #include "nghttp2_buffer.h" +#include "nghttp2_buf.h" #define NGHTTP2_FRAME_LENGTH_MASK ((1 << 14) - 1) #define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) @@ -39,7 +40,8 @@ #define NGHTTP2_WINDOW_SIZE_INCREMENT_MASK ((1u << 31) - 1) #define NGHTTP2_SETTINGS_ID_MASK ((1 << 24) - 1) -/* The maximum payload length of a frame */ +/* The maximum payload length of a frame TODO: Must be renamed as + NGHTTP2_MAX_PAYLOAD_LENGTH */ #define NGHTTP2_MAX_FRAME_LENGTH ((1 << 14) - 1) /* The maximum length of DATA frame payload. To fit entire DATA frame @@ -48,7 +50,8 @@ #define NGHTTP2_DATA_PAYLOAD_LENGTH 4086 /* The number of bytes of frame header. */ -#define NGHTTP2_FRAME_HEAD_LENGTH 8 +#define NGHTTP2_FRAME_HDLEN 8 +#define NGHTTP2_FRAME_HEAD_LENGTH NGHTTP2_FRAME_HDLEN /* The number of bytes for each SETTINGS entry */ #define NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH 5 @@ -100,15 +103,12 @@ 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_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. - * This function expands |*buf_ptr| as necessary to store frame. When - * expansion occurred, memory previously pointed by |*buf_ptr| may - * change. |*buf_ptr| and |*buflen_ptr| are updated accordingly. + * 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. * - * The first byte the frame is serialized is returned in the - * |*bufoff_ptr|. Currently, it is always 2 to account for possible - * PAD_HIGH and PAD_LOW. + * The first byte the frame is serialized is returned in the |buf|. * * frame->hd.length is assigned after length is determined during * packing process. If payload length is strictly larger than @@ -116,8 +116,8 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); * but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and * NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags. * - * This function returns the size of packed frame (which includes - * |*bufoff_ptr| bytes) if it succeeds, or returns one of the + * This function returns the size of packed frame (which equals to + * nghttp2_buf_len(buf)) if it succeeds, or returns one of the * following negative error codes: * * NGHTTP2_ERR_HEADER_COMP @@ -127,9 +127,7 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *bufoff_ptr, +ssize_t nghttp2_frame_pack_headers(nghttp2_buf *buf, nghttp2_headers *frame, nghttp2_hd_deflater *deflater); @@ -149,9 +147,8 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, /* * Packs PRIORITY frame |frame| in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. + * |buf|. This function expands |buf| as necessary to store given + * |frame|. * * This function returns 0 if it succeeds or one of the following * negative error codes: @@ -159,7 +156,7 @@ int nghttp2_frame_unpack_headers_payload(nghttp2_headers *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_priority(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_priority(nghttp2_buf *buf, nghttp2_priority *frame); /* @@ -171,10 +168,8 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, /* * Packs RST_STREAM frame |frame| in wire frame format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. In spdy/2 spec, RST_STREAM wire format is always 16 - * bytes long. + * |buf|. This function expands |buf| as necessary to store given + * |frame|. * * This function returns the size of packed frame if it succeeds, or * returns one of the following negative error codes: @@ -182,7 +177,7 @@ void nghttp2_frame_unpack_priority_payload(nghttp2_priority *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_rst_stream(nghttp2_buf *buf, nghttp2_rst_stream *frame); /* @@ -194,9 +189,8 @@ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, /* * Packs SETTINGS frame |frame| in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. + * |buf|. This function expands |buf| as necessary to store given + * |frame|. * * This function returns the size of packed frame if it succeeds, or * returns one of the following negative error codes: @@ -204,7 +198,7 @@ void nghttp2_frame_unpack_rst_stream_payload(nghttp2_rst_stream *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_settings(nghttp2_buf *buf, nghttp2_settings *frame); /* @@ -253,14 +247,11 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, /* * Packs PUSH_PROMISE frame |frame| in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. - * This function expands |*buf_ptr| as necessary to store frame. When - * expansion occurred, memory previously pointed by |*buf_ptr| may - * change. |*buf_ptr| and |*buflen_ptr| are updated accordingly. + * |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. * - * The first byte the frame is serialized is returned in the - * |*bufoff_ptr|. Currently, it is always 2 to account for possible - * PAD_HIGH and PAD_LOW. + * The first byte the frame is serialized is returned in the |buf|. * * frame->hd.length is assigned after length is determined during * packing process. If payload length is strictly larger than @@ -268,13 +259,10 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, * but frame->hd.length is set to NGHTTP2_MAX_FRAME_LENGTH and * NGHTTP2_FLAG_END_HEADERS flag is cleared from frame->hd.flags. * - * This function returns the size of packed frame (which includes - * |*bufoff_ptr| bytes) if it succeeds, or returns one of the + * This function returns the size of packed frame (which equals to + * nghttp2_buf_len(buf)) if it succeeds, or returns one of the * following negative error codes: * - * This function returns the size of packed frame 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 @@ -282,9 +270,7 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_push_promise(uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *bufoff_ptr, +ssize_t nghttp2_frame_pack_push_promise(nghttp2_buf *buf, nghttp2_push_promise *frame, nghttp2_hd_deflater *deflater); @@ -303,10 +289,8 @@ 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_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. + * Packs PING frame |frame| in wire format and store it in |buf|. This + * function expands |buf| as necessary to store given |frame|. * * This function returns 0 if it succeeds or one of the following * negative error codes: @@ -314,8 +298,7 @@ int nghttp2_frame_unpack_push_promise_payload(nghttp2_push_promise *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_ping *frame); +ssize_t nghttp2_frame_pack_ping(nghttp2_buf *buf, nghttp2_ping *frame); /* * Unpacks PING wire format into |frame|. @@ -326,9 +309,8 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, /* * Packs GOAWAY frame |frame | in wire format and store it in - * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. + * |buf|. This function expands |buf| as necessary to store given + * |frame|. * * This function returns 0 if it succeeds or one of the following * negative error codes: @@ -336,8 +318,7 @@ void nghttp2_frame_unpack_ping_payload(nghttp2_ping *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, - nghttp2_goaway *frame); +ssize_t nghttp2_frame_pack_goaway(nghttp2_buf *buf, nghttp2_goaway *frame); /* * Unpacks GOAWAY wire format into |frame|. @@ -348,9 +329,8 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, /* * Packs WINDOW_UPDATE frame |frame| in wire frame format and store it - * in |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| - * length. This function expands |*buf_ptr| as necessary to store - * given |frame|. + * in |buf|. This function expands |buf| as necessary to store given + * |frame|. * * This function returns the size of packed frame if it succeeds, or * returns one of the following negative error codes: @@ -358,7 +338,7 @@ void nghttp2_frame_unpack_goaway_payload(nghttp2_goaway *frame, * NGHTTP2_ERR_NOMEM * Out of memory. */ -ssize_t nghttp2_frame_pack_window_update(uint8_t **buf_ptr, size_t *buflen_ptr, +ssize_t nghttp2_frame_pack_window_update(nghttp2_buf *buf, nghttp2_window_update *frame); /* @@ -508,29 +488,22 @@ void nghttp2_nv_array_del(nghttp2_nv *nva); int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); /* - * Add padding to the payload in the |*buf_ptr| of length - * |*buflen_ptr|. The payload length is given in |payloadlen|. The - * frame header starts at offset |*bufoff_ptr|. Therefore, the payload - * must start at offset *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH from - * |*buf_ptr| to account for PAD_HIGH and PAD_LOW. The padding is + * 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|. * * The |*flags_ptr| is updated to include NGHTTP2_FLAG_PAD_LOW and - * NGHTTP2_FLAG_PAD_HIGH based on the padding length. The - * |*bufoff_ptr| will have the offset starting the frame header in - * |*buf_ptr|. + * NGHTTP2_FLAG_PAD_HIGH based on the padding length. * - * The |*buf_ptr| and |*buflen_ptr| may be extended to include padding - * bytes. + * 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 allocate - * buffer size as if these 2 bytes always exist. Depending of the - * length of the padding, we move the location of frame header and - * adjust |*bufoff_ptr|. If more than or equal to 256 padding is made, - * the |*bufoff_ptr| is 0 and the content of the |*buf_ptr| looks like - * this: + * 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 @@ -543,8 +516,8 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); * +---------------+---------------+-------------------------------+ * * - * If padding is less than 256 but strictly more than 0, the - * |*bufoff_ptr| is 1 and the |*buf_ptr| looks like this: + * 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 @@ -556,8 +529,8 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); * . Frame Header | Pad low | Payload ... * +---------------+---------------+-------------------------------+ * - * If no padding is added, the |*bufoff_ptr| is 2 and the |*buf_ptr| - * looks like this: + * 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 @@ -571,17 +544,10 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv); * * 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_ptr| is large - * enough to account the additional padding, of course). - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGHTTP2_ERR_NOMEM - * Out of memory. + * additional copy operation is required (if the |buf| is large enough + * to account the additional padding, of course). */ -int nghttp2_frame_add_pad(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *bufoff_ptr, uint8_t *flags_ptr, - size_t payloadlen, size_t padlen); +void nghttp2_frame_set_pad(nghttp2_buf *buf, uint8_t *flags_ptr, + size_t padlen); #endif /* NGHTTP2_FRAME_H */ diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index 2024fa25..4e3181f6 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -436,17 +436,21 @@ static int emit_indname_header(nghttp2_nv *nv_out, nghttp2_hd_entry *ent, return 0; } -static int ensure_write_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t offset, size_t need) +static int ensure_write_buffer(nghttp2_buf *buf, size_t need) { int rv; - if(need + offset > NGHTTP2_HD_MAX_BUFFER_LENGTH) { + + need += nghttp2_buf_last_offset(buf); + + if(need > NGHTTP2_HD_MAX_BUFFER_LENGTH) { return NGHTTP2_ERR_HEADER_COMP; } - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, offset + need); + + rv = nghttp2_buf_reserve(buf, need); if(rv != 0) { return NGHTTP2_ERR_NOMEM; } + return 0; } @@ -556,55 +560,68 @@ static uint8_t* decode_length(ssize_t *res, int *final, ssize_t initial, return in + 1; } -static int emit_clear_refset(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr) +static int emit_clear_refset(nghttp2_buf *buf) { int rv; uint8_t *bufp; - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, 2); + + rv = ensure_write_buffer(buf, 2); if(rv != 0) { return rv; } - bufp = *buf_ptr + *offset_ptr; + + bufp = buf->last; *bufp++ = 0x80u; - *bufp = 0x80u; - *offset_ptr += 2; + *bufp++ = 0x80u; + + buf->last = bufp; + return 0; } -static int emit_table_size(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t table_size) +static int emit_table_size(nghttp2_buf *buf, size_t table_size) { int rv; uint8_t *bufp; - size_t blocklen = 1 + count_encoded_length(table_size, 7); - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); + size_t blocklen; + + blocklen = 1 + count_encoded_length(table_size, 7); + + rv = ensure_write_buffer(buf, blocklen); if(rv != 0) { return rv; } + DEBUGF(fprintf(stderr, "emit table_size=%zu\n", table_size)); - bufp = *buf_ptr + *offset_ptr; + + bufp = buf->last; + *bufp++ = 0x80u; *bufp = 0; encode_length(bufp, table_size, 7); - *offset_ptr += blocklen; + + buf->last += blocklen; + return 0; } -static int emit_indexed_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t index) +static int emit_indexed_block(nghttp2_buf *buf, size_t index) { int rv; - uint8_t *bufp; - size_t blocklen = count_encoded_length(index + 1, 7); - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); + size_t blocklen; + + blocklen = count_encoded_length(index + 1, 7); + + rv = ensure_write_buffer(buf, blocklen); if(rv != 0) { return rv; } - bufp = *buf_ptr + *offset_ptr; - *bufp = 0x80u; - encode_length(bufp, index + 1, 7); - *offset_ptr += blocklen; + + *buf->last = 0x80u; + encode_length(buf->last, index + 1, 7); + + buf->last += blocklen; + return 0; } @@ -613,6 +630,7 @@ static size_t emit_string(uint8_t *buf, size_t buflen, const uint8_t *str, size_t len) { size_t rv; + *buf = huffman ? 1 << 7 : 0; rv = encode_length(buf, enclen, 7); buf += rv; @@ -625,66 +643,89 @@ static size_t emit_string(uint8_t *buf, size_t buflen, return rv + enclen; } -static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t index, +static int emit_indname_block(nghttp2_buf *buf, size_t index, const uint8_t *value, size_t valuelen, int inc_indexing) { int rv; uint8_t *bufp; - size_t encvallen = nghttp2_hd_huff_encode_count(value, valuelen); - size_t blocklen = count_encoded_length(index + 1, 6); - int huffman = encvallen < valuelen; + size_t encvallen; + size_t blocklen; + int huffman; + + encvallen = nghttp2_hd_huff_encode_count(value, valuelen); + blocklen = count_encoded_length(index + 1, 6); + huffman = encvallen < valuelen; + if(!huffman) { encvallen = valuelen; } + blocklen += count_encoded_length(encvallen, 7) + encvallen; - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); + + rv = ensure_write_buffer(buf, blocklen); if(rv != 0) { return rv; } - bufp = *buf_ptr + *offset_ptr; + + bufp = buf->last; + *bufp = inc_indexing ? 0 : 0x40u; bufp += encode_length(bufp, index + 1, 6); - bufp += emit_string(bufp, *buflen_ptr - (bufp - *buf_ptr), + bufp += emit_string(bufp, buf->end - bufp, encvallen, huffman, value, valuelen); - assert(bufp - (*buf_ptr + *offset_ptr) == (ssize_t)blocklen); - *offset_ptr += blocklen; + + assert(bufp - buf->last == (ssize_t)blocklen); + + buf->last = bufp; + return 0; } -static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, nghttp2_nv *nv, +static int emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, int inc_indexing) { int rv; uint8_t *bufp; - size_t encnamelen = - nghttp2_hd_huff_encode_count(nv->name, nv->namelen); - size_t encvallen = - nghttp2_hd_huff_encode_count(nv->value, nv->valuelen); - size_t blocklen = 1; - int name_huffman = encnamelen < nv->namelen; - int value_huffman = encvallen < nv->valuelen; + 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; + if(!name_huffman) { encnamelen = nv->namelen; } if(!value_huffman) { encvallen = nv->valuelen; } + blocklen += count_encoded_length(encnamelen, 7) + encnamelen + count_encoded_length(encvallen, 7) + encvallen; - rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); + + rv = ensure_write_buffer(buf, blocklen); if(rv != 0) { return rv; } - bufp = *buf_ptr + *offset_ptr; + + bufp = buf->last; + *bufp++ = inc_indexing ? 0 : 0x40u; - bufp += emit_string(bufp, *buflen_ptr - (bufp - *buf_ptr), + bufp += emit_string(bufp, buf->end - bufp, encnamelen, name_huffman, nv->name, nv->namelen); - bufp += emit_string(bufp, *buflen_ptr - (bufp - *buf_ptr), + bufp += emit_string(bufp, buf->end - bufp, encvallen, value_huffman, nv->value, nv->valuelen); - *offset_ptr += blocklen; + + assert(bufp - buf->last == (ssize_t)blocklen); + + buf->last = bufp; + return 0; } @@ -692,15 +733,12 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, * Emit common header with |index| by toggle off and on (thus 2 * indexed representation emissions). */ -static int emit_implicit(uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *offset_ptr, - size_t index) +static int emit_implicit(nghttp2_buf *buf, size_t index) { - int i; - int rv; + int i, rv; + for(i = 0; i < 2; ++i) { - rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); + rv = emit_indexed_block(buf, index); if(rv != 0) { return rv; } @@ -709,26 +747,29 @@ static int emit_implicit(uint8_t **buf_ptr, } static nghttp2_hd_entry* add_hd_table_incremental(nghttp2_hd_context *context, - uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *offset_ptr, + nghttp2_buf *buf, nghttp2_nv *nv, uint8_t entry_flags) { int rv; nghttp2_hd_entry *new_ent; - size_t room = entry_room(nv->namelen, nv->valuelen); + size_t room; + + room = entry_room(nv->namelen, nv->valuelen); + while(context->hd_table_bufsize + room > context->hd_table_bufsize_max && context->hd_table.len > 0) { + size_t index = context->hd_table.len - 1; nghttp2_hd_entry* ent = nghttp2_hd_ringbuf_get(&context->hd_table, index); + context->hd_table_bufsize -= entry_room(ent->nv.namelen, ent->nv.valuelen); if(context->role == NGHTTP2_HD_ROLE_DEFLATE) { if(ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) { /* 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_ptr, buflen_ptr, offset_ptr, index); + rv = emit_implicit(buf, index); if(rv != 0) { return NULL; } @@ -948,24 +989,25 @@ static int hd_deflate_should_indexing(nghttp2_hd_deflater *deflater, } static int deflate_nv(nghttp2_hd_deflater *deflater, - uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, - nghttp2_nv *nv) + nghttp2_buf *buf, nghttp2_nv *nv) { int rv; nghttp2_hd_entry *ent; search_result res; + res = search_hd_table(&deflater->ctx, nv); + if(res.index != -1 && res.name_value_match) { size_t index = res.index; + ent = nghttp2_hd_table_get(&deflater->ctx, index); if(index >= deflater->ctx.hd_table.len) { nghttp2_hd_entry *new_ent; + /* 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_ptr, buflen_ptr, - offset_ptr, &ent->nv, + new_ent = add_hd_table_incremental(&deflater->ctx, buf, &ent->nv, NGHTTP2_HD_FLAG_NONE); if(!new_ent) { return NGHTTP2_ERR_HEADER_COMP; @@ -979,13 +1021,13 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, set */ new_ent->flags |= NGHTTP2_HD_FLAG_EMIT; } - rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); + rv = emit_indexed_block(buf, 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_ptr, buflen_ptr, offset_ptr, index); + rv = emit_indexed_block(buf, index); if(rv != 0) { return rv; } @@ -1015,7 +1057,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_ptr, buflen_ptr, offset_ptr, index); + rv = emit_indexed_block(buf, index); if(rv != 0) { break; } @@ -1033,12 +1075,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_ptr, buflen_ptr, - offset_ptr, &nv_indname, + new_ent = add_hd_table_incremental(&deflater->ctx, buf, &nv_indname, NGHTTP2_HD_FLAG_VALUE_ALLOC); } else { - new_ent = add_hd_table_incremental(&deflater->ctx, buf_ptr, buflen_ptr, - offset_ptr, nv, + new_ent = add_hd_table_incremental(&deflater->ctx, buf, nv, NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC); } @@ -1056,10 +1096,9 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, incidx = 1; } if(index == -1) { - rv = emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, incidx); + rv = emit_newname_block(buf, nv, incidx); } else { - rv = emit_indname_block(buf_ptr, buflen_ptr, offset_ptr, index, - nv->value, nv->valuelen, incidx); + rv = emit_indname_block(buf, index, nv->value, nv->valuelen, incidx); } if(rv != 0) { return rv; @@ -1070,42 +1109,44 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, static int deflate_post_process_hd_entry(nghttp2_hd_entry *ent, size_t index, - uint8_t **buf_ptr, - size_t *buflen_ptr, - size_t *offset_ptr) + nghttp2_buf *buf) { int rv; + if((ent->flags & NGHTTP2_HD_FLAG_REFSET) && (ent->flags & NGHTTP2_HD_FLAG_IMPLICIT_EMIT) == 0 && (ent->flags & NGHTTP2_HD_FLAG_EMIT) == 0) { /* This entry is not present in the current header set and must be removed. */ ent->flags ^= NGHTTP2_HD_FLAG_REFSET; - rv = emit_indexed_block(buf_ptr, buflen_ptr, offset_ptr, index); + + rv = emit_indexed_block(buf, index); if(rv != 0) { return rv; } } + ent->flags &= ~(NGHTTP2_HD_FLAG_EMIT | NGHTTP2_HD_FLAG_IMPLICIT_EMIT); + return 0; } ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t **buf_ptr, size_t *buflen_ptr, - size_t nv_offset, + nghttp2_buf *buf, nghttp2_nv *nv, size_t nvlen) { - size_t i, offset; + size_t i; int rv = 0; + + assert(nghttp2_buf_len(buf) == 0); + if(deflater->ctx.bad) { return NGHTTP2_ERR_HEADER_COMP; } - offset = nv_offset; if(deflater->ctx.hd_table_bufsize_max > deflater->deflate_hd_table_bufsize_max) { - rv = emit_table_size(buf_ptr, buflen_ptr, &offset, - deflater->deflate_hd_table_bufsize_max); + rv = emit_table_size(buf, deflater->deflate_hd_table_bufsize_max); if(rv != 0) { goto fail; } @@ -1114,26 +1155,28 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, } if(deflater->no_refset) { - rv = emit_clear_refset(buf_ptr, buflen_ptr, &offset); + rv = emit_clear_refset(buf); if(rv != 0) { goto fail; } clear_refset(&deflater->ctx); } for(i = 0; i < nvlen; ++i) { - rv = deflate_nv(deflater, buf_ptr, buflen_ptr, &offset, &nv[i]); + rv = deflate_nv(deflater, buf, &nv[i]); if(rv != 0) { goto fail; } } 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_ptr, buflen_ptr, &offset); + + rv = deflate_post_process_hd_entry(ent, i, buf); if(rv != 0) { goto fail; } } - return offset - nv_offset; + + return nghttp2_buf_len(buf); fail: deflater->ctx.bad = 1; return rv; @@ -1259,8 +1302,8 @@ static int hd_inflate_commit_indexed(nghttp2_hd_inflater *inflater, nghttp2_hd_entry *ent = nghttp2_hd_table_get(&inflater->ctx, inflater->index); if(inflater->index >= inflater->ctx.hd_table.len) { nghttp2_hd_entry *new_ent; - new_ent = add_hd_table_incremental(&inflater->ctx, NULL, NULL, NULL, - &ent->nv, NGHTTP2_HD_FLAG_NONE); + new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &ent->nv, + NGHTTP2_HD_FLAG_NONE); if(!new_ent) { return NGHTTP2_ERR_NOMEM; } @@ -1307,8 +1350,7 @@ static int hd_inflate_commit_newname(nghttp2_hd_inflater *inflater, uint8_t ent_flags = NGHTTP2_HD_FLAG_NAME_ALLOC | NGHTTP2_HD_FLAG_VALUE_ALLOC | NGHTTP2_HD_FLAG_NAME_GIFT | NGHTTP2_HD_FLAG_VALUE_GIFT; - new_ent = add_hd_table_incremental(&inflater->ctx, NULL, NULL, NULL, &nv, - ent_flags); + new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags); if(new_ent) { nghttp2_buffer_release(&inflater->namebuf); nghttp2_buffer_release(&inflater->valuebuf); @@ -1357,10 +1399,8 @@ static int hd_inflate_commit_indname(nghttp2_hd_inflater *inflater, nv.namelen = inflater->ent_name->nv.namelen; nv.value = inflater->valuebuf.buf; nv.valuelen = inflater->valuebuf.len; - new_ent = add_hd_table_incremental(&inflater->ctx, NULL, NULL, NULL, &nv, - ent_flags); + new_ent = add_hd_table_incremental(&inflater->ctx, NULL, &nv, ent_flags); if(!static_name && --inflater->ent_name->ref == 0) { - fprintf(stderr, "index=%zu, len=%zu\n", inflater->index, inflater->ctx.hd_table.len); nghttp2_hd_entry_free(inflater->ent_name); free(inflater->ent_name); } @@ -1693,24 +1733,20 @@ int nghttp2_hd_inflate_end_headers(nghttp2_hd_inflater *inflater) return 0; } -int nghttp2_hd_emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t index, +int nghttp2_hd_emit_indname_block(nghttp2_buf *buf, size_t index, const uint8_t *value, size_t valuelen, int inc_indexing) { - return emit_indname_block(buf_ptr, buflen_ptr, offset_ptr, - index, value, valuelen, inc_indexing); + return emit_indname_block(buf, index, value, valuelen, inc_indexing); } -int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, nghttp2_nv *nv, +int nghttp2_hd_emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, int inc_indexing) { - return emit_newname_block(buf_ptr, buflen_ptr, offset_ptr, nv, inc_indexing); + return emit_newname_block(buf, nv, inc_indexing); } -int nghttp2_hd_emit_table_size(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t table_size) +int nghttp2_hd_emit_table_size(nghttp2_buf *buf, size_t table_size) { - return emit_table_size(buf_ptr, buflen_ptr, offset_ptr, table_size); + return emit_table_size(buf, table_size); } diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h index d8db8ce0..4666f33a 100644 --- a/lib/nghttp2_hd.h +++ b/lib/nghttp2_hd.h @@ -33,6 +33,7 @@ #include "nghttp2_hd_huffman.h" #include "nghttp2_buffer.h" +#include "nghttp2_buf.h" #define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE (1 << 12) #define NGHTTP2_HD_ENTRY_OVERHEAD 32 @@ -295,16 +296,13 @@ 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_ptr| with the length |*buflen_ptr|. - * The output starts after |nv_offset| bytes from |*buf_ptr|. + * the buffer pointed by the |buf|. The caller must ensure that + * nghttp2_buf_len(buf) == 0 holds. Write starts at buf->last. * - * This function expands |*buf_ptr| as necessary to store the - * result. When expansion occurred, memory previously pointed by - * |*buf_ptr| may change. |*buf_ptr| and |*buflen_ptr| are updated - * accordingly. + * This function expands |buf| as necessary to store the result. * - * This function copies necessary data into |*buf_ptr|. After this - * function returns, it is safe to delete the |nva|. + * This function copies necessary data into |buf|. 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 @@ -319,8 +317,7 @@ int nghttp2_hd_inflate_change_table_size(nghttp2_hd_inflater *inflater, * Deflation process has failed. */ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t **buf_ptr, size_t *buflen_ptr, - size_t nv_offset, + nghttp2_buf *buf, nghttp2_nv *nva, size_t nvlen); typedef enum { @@ -373,19 +370,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(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t index, +int nghttp2_hd_emit_indname_block(nghttp2_buf *buf, size_t index, const uint8_t *value, size_t valuelen, int inc_indexing); /* For unittesting purpose */ -int nghttp2_hd_emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, nghttp2_nv *nv, +int nghttp2_hd_emit_newname_block(nghttp2_buf *buf, nghttp2_nv *nv, int inc_indexing); /* For unittesting purpose */ -int nghttp2_hd_emit_table_size(uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *offset_ptr, size_t table_size); +int nghttp2_hd_emit_table_size(nghttp2_buf *buf, size_t table_size); /* For unittesting purpose */ nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, diff --git a/lib/nghttp2_helper.c b/lib/nghttp2_helper.c index b5bed1f7..ce360d53 100644 --- a/lib/nghttp2_helper.c +++ b/lib/nghttp2_helper.c @@ -382,3 +382,10 @@ int nghttp2_check_header_value(const uint8_t *value, size_t len) } return 1; } + +uint8_t* nghttp2_cpymem(uint8_t *dest, uint8_t *src, size_t len) +{ + memcpy(dest, src, len); + + return dest + len; +} diff --git a/lib/nghttp2_helper.h b/lib/nghttp2_helper.h index 716cc211..7f2b48e2 100644 --- a/lib/nghttp2_helper.h +++ b/lib/nghttp2_helper.h @@ -126,4 +126,11 @@ int nghttp2_should_send_window_update(int32_t local_window_size, */ void nghttp2_free(void *ptr); +/* + * Copies the buffer |src| of length |len| to the destination pointed + * by the |dest|, assuming that the |dest| is at lest |len| bytes long + * . Returns dest + len. + */ +uint8_t* nghttp2_cpymem(uint8_t *dest, uint8_t *src, size_t len); + #endif /* NGHTTP2_HELPER_H */ diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index bfe14251..13d4b619 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; - aob->framebuflen = aob->framebufoff = aob->framebufmark = 0; + nghttp2_buf_reset(&aob->framebuf); aob->state = NGHTTP2_OB_POP_ITEM; } @@ -290,13 +290,12 @@ static int nghttp2_session_new(nghttp2_session **session_ptr, (*session_ptr)->server = 1; } - (*session_ptr)->aob.framebuf = malloc - (NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH); - if((*session_ptr)->aob.framebuf == NULL) { - rv = NGHTTP2_ERR_NOMEM; + rv = nghttp2_buf_init2(&(*session_ptr)->aob.framebuf, + NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH); + if(rv != 0) { goto fail_aob_framebuf; } - (*session_ptr)->aob.framebufmax = NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH; + nghttp2_active_outbound_item_reset(&(*session_ptr)->aob); memset((*session_ptr)->remote_settings, 0, @@ -419,7 +418,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); - free(session->aob.framebuf); + nghttp2_buf_free(&session->aob.framebuf); free(session); } @@ -1125,6 +1124,36 @@ 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. */ @@ -1133,6 +1162,9 @@ static ssize_t session_headers_add_pad(nghttp2_session *session, { int rv; ssize_t padded_payloadlen; + nghttp2_active_outbound_item *aob; + + aob = &session->aob; padded_payloadlen = session_call_select_padding(session, frame, frame->hd.length + 1024); @@ -1143,7 +1175,7 @@ static ssize_t session_headers_add_pad(nghttp2_session *session, frame->headers.padlen = padded_payloadlen - frame->hd.length; frame->hd.length = padded_payloadlen; - DEBUGF(fprintf(stderr, "payloadlen=%zu, padlen=%zu\n", + DEBUGF(fprintf(stderr, "padding selected: payloadlen=%zu, padlen=%zu\n", frame->hd.length, frame->headers.padlen)); if(frame->hd.length > NGHTTP2_MAX_FRAME_LENGTH) { @@ -1151,49 +1183,59 @@ static ssize_t session_headers_add_pad(nghttp2_session *session, hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; hd.length = NGHTTP2_MAX_FRAME_LENGTH; - if(NGHTTP2_MAX_FRAME_LENGTH > - frame->hd.length - frame->headers.padlen) { - size_t padlen = NGHTTP2_MAX_FRAME_LENGTH - + if(NGHTTP2_MAX_FRAME_LENGTH > frame->hd.length - frame->headers.padlen) { + size_t padlen; + + padlen = NGHTTP2_MAX_FRAME_LENGTH - (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 = nghttp2_frame_add_pad(&session->aob.framebuf, - &session->aob.framebufmax, - &session->aob.framebufoff, - &hd.flags, - hd.length - padlen, - 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 - (session->aob.framebuf + session->aob.framebufoff, &hd); - /* At this point, framebuflen > session->aob.framebufmax. But - before we access the missing part, we will allocate it in - nghttp2_session_after_frame_sent(). */ + nghttp2_frame_pack_frame_hd(aob->framebuf.pos, &hd); + } else if(frame->headers.padlen > 0) { nghttp2_frame_hd hd = frame->hd; - rv = nghttp2_frame_add_pad(&session->aob.framebuf, - &session->aob.framebufmax, - &session->aob.framebufoff, - &hd.flags, - frame->hd.length - frame->headers.padlen, - frame->headers.padlen); + + rv = session_reserve_pad_trail(session, frame->headers.padlen); if(nghttp2_is_fatal(rv)) { return rv; } - nghttp2_frame_pack_frame_hd - (session->aob.framebuf + session->aob.framebufoff, &hd); + + 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); } - return session->aob.framebufoff + NGHTTP2_FRAME_HEAD_LENGTH - + frame->hd.length; + + return nghttp2_buf_len(&session->aob.framebuf); } static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, @@ -1233,13 +1275,16 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, } } framebuflen = nghttp2_frame_pack_headers(&session->aob.framebuf, - &session->aob.framebufmax, - &session->aob.framebufoff, &frame->headers, &session->hd_deflater); if(framebuflen < 0) { return framebuflen; } + + DEBUGF(fprintf(stderr, + "before padding, HEADERS serialized in %zd bytes\n", + nghttp2_buf_len(&session->aob.framebuf))); + framebuflen = session_headers_add_pad(session, frame); if(framebuflen < 0) { return framebuflen; @@ -1266,6 +1311,10 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, default: break; } + + DEBUGF(fprintf(stderr, "HEADERS serialized in %zd bytes\n", + nghttp2_buf_len(&session->aob.framebuf))); + break; } case NGHTTP2_PRIORITY: { @@ -1275,7 +1324,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return rv; } framebuflen = nghttp2_frame_pack_priority(&session->aob.framebuf, - &session->aob.framebufmax, &frame->priority); if(framebuflen < 0) { return framebuflen; @@ -1284,7 +1332,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, } case NGHTTP2_RST_STREAM: framebuflen = nghttp2_frame_pack_rst_stream(&session->aob.framebuf, - &session->aob.framebufmax, &frame->rst_stream); if(framebuflen < 0) { return framebuflen; @@ -1296,7 +1343,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return rv; } framebuflen = nghttp2_frame_pack_settings(&session->aob.framebuf, - &session->aob.framebufmax, &frame->settings); if(framebuflen < 0) { return framebuflen; @@ -1316,8 +1362,6 @@ static ssize_t 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, - &session->aob.framebufmax, - &session->aob.framebufoff, &frame->push_promise, &session->hd_deflater); if(framebuflen < 0) { @@ -1343,7 +1387,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, } case NGHTTP2_PING: framebuflen = nghttp2_frame_pack_ping(&session->aob.framebuf, - &session->aob.framebufmax, &frame->ping); if(framebuflen < 0) { return framebuflen; @@ -1356,7 +1399,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return rv; } framebuflen = nghttp2_frame_pack_window_update(&session->aob.framebuf, - &session->aob.framebufmax, &frame->window_update); if(framebuflen < 0) { return framebuflen; @@ -1372,7 +1414,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, return NGHTTP2_ERR_GOAWAY_ALREADY_SENT; } framebuflen = nghttp2_frame_pack_goaway(&session->aob.framebuf, - &session->aob.framebufmax, &frame->goaway); if(framebuflen < 0) { return framebuflen; @@ -1403,8 +1444,6 @@ static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, } framebuflen = nghttp2_session_pack_data(session, &session->aob.framebuf, - &session->aob.framebufmax, - &session->aob.framebufoff, next_readmax, data_frame); if(framebuflen == NGHTTP2_ERR_DEFERRED) { @@ -1516,13 +1555,8 @@ static int session_call_before_frame_send(nghttp2_session *session, { int rv; if(session->callbacks.before_frame_send_callback) { - /* Adjust frame length to deal with CONTINUATION frame */ - size_t origlen = frame->hd.length; - frame->hd.length = - session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH; rv = session->callbacks.before_frame_send_callback(session, frame, session->user_data); - frame->hd.length = origlen; if(rv != 0) { return NGHTTP2_ERR_CALLBACK_FAILURE; } @@ -1558,24 +1592,33 @@ static int session_call_on_frame_send(nghttp2_session *session, static int nghttp2_session_after_frame_sent(nghttp2_session *session) { int rv; - nghttp2_outbound_item *item = session->aob.item; + nghttp2_active_outbound_item *aob = &session->aob; + nghttp2_outbound_item *item = aob->item; + nghttp2_buf *framebuf = &aob->framebuf; + if(item->frame_cat == NGHTTP2_CAT_CTRL) { nghttp2_frame *frame; - frame = nghttp2_outbound_item_get_ctrl_frame(session->aob.item); + + frame = nghttp2_outbound_item_get_ctrl_frame(item); + if(frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_PUSH_PROMISE) { - if(session->aob.framebufmark < session->aob.framebuflen) { + + if(framebuf->mark < framebuf->last) { nghttp2_frame_hd cont_hd; - cont_hd.length = nghttp2_min(session->aob.framebuflen - - session->aob.framebufmark, + + cont_hd.length = nghttp2_min(framebuf->last - framebuf->mark, NGHTTP2_MAX_FRAME_LENGTH); 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 */ - session->aob.framebufoff -= NGHTTP2_FRAME_HEAD_LENGTH; - if(cont_hd.length + session->aob.framebufmark == - session->aob.framebuflen) { + framebuf->pos -= NGHTTP2_FRAME_HEAD_LENGTH; + + if(cont_hd.length + framebuf->mark == framebuf->last) { size_t padlen; + if(cont_hd.length < frame->headers.padlen) { padlen = cont_hd.length; } else { @@ -1584,34 +1627,41 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) 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)); - cont_hd.flags = NGHTTP2_FLAG_END_HEADERS; + nghttp2_frame_set_pad(framebuf, &cont_hd.flags, padlen); - rv = nghttp2_frame_add_pad(&session->aob.framebuf, - &session->aob.framebufmax, - &session->aob.framebufoff, - &cont_hd.flags, - cont_hd.length - padlen, - padlen); - if(nghttp2_is_fatal(rv)) { - return rv; - } - /* we reuses previous up to 2 bytes for PAD_HIGH and - PAD_LOW. Because of this, session->aob.framebuflen is 1 - or 2 bytes longer. Re-compute the value here. */ - session->aob.framebuflen = session->aob.framebufmark = - session->aob.framebufoff + NGHTTP2_FRAME_HEAD_LENGTH + - cont_hd.length; + framebuf->mark = framebuf->last; } else { - cont_hd.flags = NGHTTP2_FLAG_NONE; - session->aob.framebufmark += cont_hd.length; + 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(session->aob.framebuf + - session->aob.framebufoff, - &cont_hd); + + nghttp2_frame_pack_frame_hd(framebuf->pos, &cont_hd); + return 0; } } @@ -1754,7 +1804,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) nghttp2_outbound_item* next_item; nghttp2_stream *stream; - data_frame = nghttp2_outbound_item_get_data_frame(session->aob.item); + data_frame = nghttp2_outbound_item_get_data_frame(aob->item); stream = nghttp2_session_get_stream(session, data_frame->hd.stream_id); /* We update flow control window after a frame was completely sent. This is possible because we choose payload length not to @@ -1786,7 +1836,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) if(data_frame->eof || nghttp2_session_predicate_data_send(session, data_frame->hd.stream_id) != 0) { - nghttp2_active_outbound_item_reset(&session->aob); + nghttp2_active_outbound_item_reset(aob); return 0; } /* Assuming stream is not NULL */ @@ -1795,30 +1845,27 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) /* If priority of this stream is higher or equal to other stream waiting at the top of the queue, we continue to send this data. */ - if(next_item == NULL || session->aob.item->pri < next_item->pri) { + if(next_item == NULL || aob->item->pri < next_item->pri) { size_t next_readmax; next_readmax = nghttp2_session_next_data_read(session, stream); if(next_readmax == 0) { - nghttp2_stream_defer_data(stream, session->aob.item, + nghttp2_stream_defer_data(stream, aob->item, NGHTTP2_DEFERRED_FLOW_CONTROL); - session->aob.item = NULL; - nghttp2_active_outbound_item_reset(&session->aob); + aob->item = NULL; + nghttp2_active_outbound_item_reset(aob); + return 0; } - rv = nghttp2_session_pack_data(session, - &session->aob.framebuf, - &session->aob.framebufmax, - &session->aob.framebufoff, - next_readmax, + rv = nghttp2_session_pack_data(session, framebuf, next_readmax, data_frame); if(nghttp2_is_fatal(rv)) { return rv; } if(rv == NGHTTP2_ERR_DEFERRED) { - nghttp2_stream_defer_data(stream, session->aob.item, - NGHTTP2_DEFERRED_NONE); - session->aob.item = NULL; - nghttp2_active_outbound_item_reset(&session->aob); + nghttp2_stream_defer_data(stream, aob->item, NGHTTP2_DEFERRED_NONE); + aob->item = NULL; + nghttp2_active_outbound_item_reset(aob); + return 0; } if(rv == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { @@ -1828,24 +1875,26 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) rv = nghttp2_session_add_rst_stream(session, data_frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); - nghttp2_active_outbound_item_reset(&session->aob); + nghttp2_active_outbound_item_reset(aob); if(nghttp2_is_fatal(rv)) { return rv; } + return 0; } assert(rv >= 0); - session->aob.framebuflen = session->aob.framebufmark = rv; + framebuf->mark = framebuf->last; + return 0; } /* Update seq to interleave other streams with the same priority. */ - session->aob.item->seq = session->next_seq++; - rv = nghttp2_pq_push(&session->ob_pq, session->aob.item); + aob->item->seq = session->next_seq++; + rv = nghttp2_pq_push(&session->ob_pq, aob->item); if(nghttp2_is_fatal(rv)) { return rv; } - session->aob.item = NULL; + aob->item = NULL; nghttp2_active_outbound_item_reset(&session->aob); return 0; } @@ -1857,10 +1906,15 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, const uint8_t **data_ptr) { int rv; + nghttp2_active_outbound_item *aob; + nghttp2_buf *framebuf; + + aob = &session->aob; + framebuf = &aob->framebuf; *data_ptr = NULL; for(;;) { - switch(session->aob.state) { + switch(aob->state) { case NGHTTP2_OB_POP_ITEM: { nghttp2_outbound_item *item; ssize_t framebuflen; @@ -1895,7 +1949,7 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, } nghttp2_outbound_item_free(item); free(item); - nghttp2_active_outbound_item_reset(&session->aob); + nghttp2_active_outbound_item_reset(aob); if(framebuflen == NGHTTP2_ERR_HEADER_COMP) { /* If header compression error occurred, should terminiate @@ -1908,32 +1962,43 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, } break; } - session->aob.item = item; - session->aob.framebuflen = framebuflen; + aob->item = item; if(item->frame_cat == NGHTTP2_CAT_CTRL) { - nghttp2_frame *frame = nghttp2_outbound_item_get_ctrl_frame(item); + 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_FRAME_LENGTH */ - session->aob.framebufmark = - session->aob.framebufoff + NGHTTP2_FRAME_HEAD_LENGTH + - nghttp2_get_uint16(session->aob.framebuf + session->aob.framebufoff); + frame = nghttp2_outbound_item_get_ctrl_frame(item); + + framebuf->mark = framebuf->pos + NGHTTP2_FRAME_HEAD_LENGTH + + nghttp2_get_uint16(framebuf->pos); + rv = session_call_before_frame_send(session, frame); if(nghttp2_is_fatal(rv)) { return rv; } + } else { - session->aob.framebufmark = session->aob.framebuflen; + framebuf->mark = framebuf->last; } - session->aob.state = NGHTTP2_OB_SEND_DATA; + + DEBUGF(fprintf(stderr, "start transmitting type %d frame %zd bytes\n", + framebuf->pos[2], framebuf->mark - framebuf->pos)); + + aob->state = NGHTTP2_OB_SEND_DATA; + break; } case NGHTTP2_OB_SEND_DATA: { size_t datalen; - if(session->aob.framebufoff == session->aob.framebufmark) { + if(framebuf->pos == framebuf->mark) { + DEBUGF(fprintf(stderr, "end transmission of frame, left %zd\n", + framebuf->last - framebuf->mark)); + /* Frame has completely sent */ rv = nghttp2_session_after_frame_sent(session); if(rv < 0) { @@ -1945,11 +2010,11 @@ ssize_t nghttp2_session_mem_send(nghttp2_session *session, break; } - *data_ptr = session->aob.framebuf + session->aob.framebufoff; - datalen = session->aob.framebufmark - session->aob.framebufoff; + *data_ptr = framebuf->pos; + datalen = framebuf->mark - framebuf->pos; /* We increment the offset here. If send_callback does not send everything, we will adjust it. */ - session->aob.framebufoff += datalen; + framebuf->pos += datalen; return datalen; } @@ -1962,6 +2027,9 @@ int nghttp2_session_send(nghttp2_session *session) const uint8_t *data; ssize_t datalen; ssize_t sentlen; + nghttp2_buf *framebuf; + + framebuf = &session->aob.framebuf; for(;;) { datalen = nghttp2_session_mem_send(session, &data); @@ -1973,13 +2041,14 @@ int nghttp2_session_send(nghttp2_session *session) if(sentlen < 0) { if(sentlen == NGHTTP2_ERR_WOULDBLOCK) { /* Transmission canceled. Rewind the offset */ - session->aob.framebufoff -= datalen; + framebuf->pos -= datalen; + return 0; } return NGHTTP2_ERR_CALLBACK_FAILURE; } /* Rewind the offset to the amount of unsent bytes */ - session->aob.framebufoff -= datalen - sentlen; + framebuf->pos -= datalen - sentlen; } return 0; } @@ -2083,82 +2152,6 @@ static int session_detect_idle_stream(nghttp2_session *session, return 0; } -/* - * Inflates header block in the memory pointed by |in| with |inlen| - * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must - * call this function again, until it returns 0 or one of negative - * error code. If |call_header_cb| is zero, the on_header_callback - * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If - * the given |in| is the last chunk of header block, the |final| must - * be nonzero. If header block is successfully processed (which is - * indicated by the return value 0, NGHTTP2_ERR_PAUSE or - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed - * input bytes is assigned to the |*readlen_ptr|. - * - * This function return 0 if it succeeds, or one of the negative error - * codes: - * - * NGHTTP2_ERR_CALLBACK_FAILURE - * The callback function failed. - * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE - * The callback returns this error code, indicating that this - * stream should be RST_STREAMed. - * NGHTTP2_ERR_NOMEM - * Out of memory. - * NGHTTP2_ERR_PAUSE - * The callback function returned NGHTTP2_ERR_PAUSE - * NGHTTP2_ERR_HEADER_COMP - * Header decompression failed - */ -static ssize_t inflate_header_block(nghttp2_session *session, - nghttp2_frame *frame, - size_t *readlen_ptr, - uint8_t *in, size_t inlen, - int final, int call_header_cb) -{ - ssize_t rv; - int inflate_flags; - nghttp2_nv nv; - *readlen_ptr = 0; - - DEBUGF(fprintf(stderr, "processing header block %zu bytes\n", inlen)); - for(;;) { - inflate_flags = 0; - rv = nghttp2_hd_inflate_hd(&session->hd_inflater, &nv, &inflate_flags, - in, inlen, final); - if(nghttp2_is_fatal(rv)) { - return rv; - } - if(rv < 0) { - rv = nghttp2_session_terminate_session(session, - NGHTTP2_COMPRESSION_ERROR); - if(rv != 0) { - return rv; - } - return NGHTTP2_ERR_HEADER_COMP; - } - in += rv; - inlen -= rv; - *readlen_ptr += rv; - if(call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { - rv = session_call_on_header(session, frame, &nv); - /* This handles NGHTTP2_ERR_PAUSE and - NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ - if(rv != 0) { - return rv; - } - } - if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { - nghttp2_hd_inflate_end_headers(&session->hd_inflater); - break; - } - if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { - break; - } - } - return 0; -} - /* * Handles frame size error. * @@ -2238,6 +2231,87 @@ static int nghttp2_session_inflate_handle_invalid_connection return NGHTTP2_ERR_IGN_HEADER_BLOCK; } +/* + * Inflates header block in the memory pointed by |in| with |inlen| + * bytes. If this function returns NGHTTP2_ERR_PAUSE, the caller must + * call this function again, until it returns 0 or one of negative + * error code. If |call_header_cb| is zero, the on_header_callback + * are not invoked and the function never return NGHTTP2_ERR_PAUSE. If + * the given |in| is the last chunk of header block, the |final| must + * be nonzero. If header block is successfully processed (which is + * indicated by the return value 0, NGHTTP2_ERR_PAUSE or + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE), the number of processed + * input bytes is assigned to the |*readlen_ptr|. + * + * This function return 0 if it succeeds, or one of the negative error + * codes: + * + * NGHTTP2_ERR_CALLBACK_FAILURE + * The callback function failed. + * NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE + * The callback returns this error code, indicating that this + * stream should be RST_STREAMed. + * NGHTTP2_ERR_NOMEM + * Out of memory. + * NGHTTP2_ERR_PAUSE + * The callback function returned NGHTTP2_ERR_PAUSE + * NGHTTP2_ERR_HEADER_COMP + * Header decompression failed + */ +static ssize_t inflate_header_block(nghttp2_session *session, + nghttp2_frame *frame, + size_t *readlen_ptr, + uint8_t *in, size_t inlen, + int final, int call_header_cb) +{ + ssize_t rv; + int inflate_flags; + nghttp2_nv nv; + *readlen_ptr = 0; + + DEBUGF(fprintf(stderr, "processing header block %zu bytes\n", inlen)); + for(;;) { + inflate_flags = 0; + rv = nghttp2_hd_inflate_hd(&session->hd_inflater, &nv, &inflate_flags, + in, inlen, final); + if(nghttp2_is_fatal(rv)) { + return rv; + } + if(rv < 0) { + if(session->iframe.state == NGHTTP2_IB_READ_HEADER_BLOCK) { + rv = nghttp2_session_handle_invalid_connection + (session, frame, NGHTTP2_COMPRESSION_ERROR); + } else { + rv = nghttp2_session_terminate_session(session, + NGHTTP2_COMPRESSION_ERROR); + } + if(rv != 0) { + return rv; + } + return NGHTTP2_ERR_HEADER_COMP; + } + in += rv; + inlen -= rv; + *readlen_ptr += rv; + if(call_header_cb && (inflate_flags & NGHTTP2_HD_INFLATE_EMIT)) { + rv = session_call_on_header(session, frame, &nv); + /* This handles NGHTTP2_ERR_PAUSE and + NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE as well */ + if(rv != 0) { + return rv; + } + } + if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + nghttp2_hd_inflate_end_headers(&session->hd_inflater); + break; + } + if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && inlen == 0) { + break; + } + } + return 0; +} + /* * Decompress header blocks of incoming request HEADERS and also call * additional callbacks. This function can be called again if this @@ -4488,48 +4562,53 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, } ssize_t nghttp2_session_pack_data(nghttp2_session *session, - uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *bufoff_ptr, + nghttp2_buf *buf, size_t datamax, nghttp2_private_data *frame) { - size_t payloadoff; 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_hd hd; /* 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|. */ - *bufoff_ptr = 2; - payloadoff = *bufoff_ptr + NGHTTP2_FRAME_HEAD_LENGTH; - framelen = payloadoff + datamax; + buf->pos += 2; - rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, framelen); + framelen = NGHTTP2_FRAME_HDLEN + datamax; + + rv = nghttp2_buf_pos_reserve(buf, framelen); if(rv != 0) { return rv; } + eof_flags = 0; payloadlen = frame->data_prd.read_callback - (session, frame->hd.stream_id, (*buf_ptr) + payloadoff, datamax, + (session, frame->hd.stream_id, buf->pos + NGHTTP2_FRAME_HDLEN, datamax, &eof_flags, &frame->data_prd.source, session->user_data); if(payloadlen == NGHTTP2_ERR_DEFERRED || payloadlen == NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE) { DEBUGF(fprintf(stderr, "DATA postponed due to %s\n", nghttp2_strerror(payloadlen))); + return payloadlen; } + if(payloadlen < 0 || datamax < (size_t)payloadlen) { /* This is the error code when callback is failed. */ return NGHTTP2_ERR_CALLBACK_FAILURE; } + buf->last = buf->pos + NGHTTP2_FRAME_HDLEN + payloadlen; + /* Clear flags, because this may contain previous flags of previous DATA */ frame->hd.flags &= (NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT); @@ -4545,7 +4624,9 @@ 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; @@ -4556,25 +4637,31 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, if(nghttp2_is_fatal(padded_payloadlen)) { return padded_payloadlen; } - rv = nghttp2_frame_add_pad(buf_ptr, buflen_ptr, bufoff_ptr, &flags, - payloadlen, padded_payloadlen - payloadlen); + + padlen = padded_payloadlen - payloadlen; + + rv = session_reserve_pad_trail(session, padlen); if(nghttp2_is_fatal(rv)) { return rv; } - frame->padlen = padded_payloadlen - payloadlen; + + 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_ptr + *bufoff_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH); - nghttp2_put_uint16be(&(*buf_ptr)[*bufoff_ptr], frame->hd.length); + memset(buf->pos, 0, NGHTTP2_FRAME_HEAD_LENGTH); - (*buf_ptr)[*bufoff_ptr + 3] = flags; - nghttp2_put_uint32be(&(*buf_ptr)[*bufoff_ptr + 4], frame->hd.stream_id); + hd = frame->hd; + hd.flags = flags; - return frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH + *bufoff_ptr; + nghttp2_frame_pack_frame_hd(buf->pos, &hd); + + return nghttp2_buf_len(buf); } void* nghttp2_session_get_stream_user_data(nghttp2_session *session, diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 4faf12d4..81f894cf 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -38,6 +38,7 @@ #include "nghttp2_buffer.h" #include "nghttp2_outbound_item.h" #include "nghttp2_int.h" +#include "nghttp2_buf.h" /* * Option flags. @@ -54,27 +55,15 @@ typedef enum { typedef struct { nghttp2_outbound_item *item; - /* Buffer for outbound frames. Used to pack one frame. The memory - pointed by framebuf is initially allocated by - nghttp2_session_{client,server}_new() and deallocated by - nghttp2_session_del() */ - uint8_t *framebuf; - /* The capacity of framebuf in bytes */ - size_t framebufmax; - /* The length of the frame stored in framebuf */ - size_t framebuflen; - /* The number of bytes has been sent */ - size_t framebufoff; - /* Marks the last position to send. This is used to implement - CONTINUATION */ - size_t framebufmark; + + nghttp2_buf framebuf; 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 4096 +#define NGHTTP2_INITIAL_OUTBOUND_FRAMEBUF_LENGTH 16384 #define NGHTTP2_INITIAL_NV_BUFFER_LENGTH 4096 @@ -538,8 +527,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, - uint8_t **buf_ptr, size_t *buflen_ptr, - size_t *bufoff_ptr, + nghttp2_buf *buf, size_t datamax, nghttp2_private_data *frame); diff --git a/python/cnghttp2.pxd b/python/cnghttp2.pxd index 8f9431db..624a5b4c 100644 --- a/python/cnghttp2.pxd +++ b/python/cnghttp2.pxd @@ -291,8 +291,7 @@ cdef extern from 'nghttp2_hd.h': size_t hd_table_bufsize_max) ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t **buf_ptr, size_t *buflen_ptr, - size_t nv_offset, + nghttp2_buf *buf, nghttp2_nv *nva, size_t nvlen) ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, @@ -303,3 +302,14 @@ cdef extern from 'nghttp2_hd.h': nghttp2_hd_entry* nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index) + +cdef extern from 'nghttp2_buf.h': + + ctypedef struct nghttp2_buf: + uint8_t *pos + uint8_t *last + + + void nghttp2_buf_init(nghttp2_buf *buf) + + void nghttp2_buf_free(nghttp2_buf *buf) diff --git a/python/nghttp2.pyx b/python/nghttp2.pyx index cc155ef1..1b8de9ac 100644 --- a/python/nghttp2.pyx +++ b/python/nghttp2.pyx @@ -106,25 +106,36 @@ cdef class HDDeflater: malloc(sizeof(cnghttp2.nghttp2_nv)*\ len(headers)) cdef cnghttp2.nghttp2_nv *nvap = nva + for k, v in headers: nvap[0].name = k nvap[0].namelen = len(k) nvap[0].value = v nvap[0].valuelen = len(v) nvap += 1 - cdef uint8_t *out = NULL + + cdef cnghttp2.nghttp2_buf buf cdef size_t outcap = 0 cdef ssize_t rv - rv = cnghttp2.nghttp2_hd_deflate_hd(&self._deflater, &out, &outcap, - 0, nva, len(headers)) + + cnghttp2.nghttp2_buf_init(&buf) + + rv = cnghttp2.nghttp2_hd_deflate_hd(&self._deflater, &buf, + nva, len(headers)) free(nva) + if rv < 0: + cnghttp2.nghttp2_buf_free(&buf); + raise Exception(_strerror(rv)) + cdef bytes res + try: - res = out[:rv] + res = buf.pos[:rv] finally: - cnghttp2.nghttp2_free(out) + cnghttp2.nghttp2_buf_free(&buf) + return res def set_no_refset(self, no_refset): diff --git a/src/deflatehd.c b/src/deflatehd.c index ca2d6e2c..2b083dc2 100644 --- a/src/deflatehd.c +++ b/src/deflatehd.c @@ -72,12 +72,15 @@ static void to_hex(char *dest, const uint8_t *src, size_t len) } static void output_to_json(nghttp2_hd_deflater *deflater, - const uint8_t *buf, size_t len, size_t inputlen, + nghttp2_buf *buf, size_t inputlen, nghttp2_nv *nva, size_t nvlen, int seq) { json_t *obj; char *hex = NULL; + size_t len; + + len = nghttp2_buf_len(buf); if(len > 0) { hex = malloc(len * 2); @@ -88,7 +91,7 @@ static void output_to_json(nghttp2_hd_deflater *deflater, 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, len); + to_hex(hex, buf->pos, len); if(len == 0) { json_object_set_new(obj, "wire", json_string("")); } else { @@ -114,17 +117,21 @@ static void deflate_hd(nghttp2_hd_deflater *deflater, nghttp2_nv *nva, size_t nvlen, size_t inputlen, int seq) { ssize_t rv; - uint8_t *buf = NULL; - size_t buflen = 0; - rv = nghttp2_hd_deflate_hd(deflater, &buf, &buflen, 0, nva, nvlen); + nghttp2_buf buf; + + nghttp2_buf_init(&buf); + + rv = nghttp2_hd_deflate_hd(deflater, &buf, nva, nvlen); if(rv < 0) { fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq); exit(EXIT_FAILURE); } + input_sum += inputlen; output_sum += rv; - output_to_json(deflater, buf, rv, inputlen, nva, nvlen, seq); - free(buf); + + output_to_json(deflater, &buf, inputlen, nva, nvlen, seq); + nghttp2_buf_free(&buf); } static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq) diff --git a/tests/main.c b/tests/main.c index 87384207..9db8f840 100644 --- a/tests/main.c +++ b/tests/main.c @@ -202,6 +202,12 @@ int main(int argc, char* argv[]) test_nghttp2_session_pack_data_with_padding) || !CU_add_test(pSuite, "session_pack_headers_with_padding", test_nghttp2_session_pack_headers_with_padding) || + !CU_add_test(pSuite, "session_pack_headers_with_padding2", + test_nghttp2_session_pack_headers_with_padding2) || + !CU_add_test(pSuite, "session_pack_headers_with_padding3", + test_nghttp2_session_pack_headers_with_padding3) || + !CU_add_test(pSuite, "session_pack_headers_with_padding4", + test_nghttp2_session_pack_headers_with_padding4) || !CU_add_test(pSuite, "pack_settings_payload", test_nghttp2_pack_settings_payload) || !CU_add_test(pSuite, "frame_pack_headers", diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index b64d217a..6a8817f0 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; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t bufoff; + nghttp2_buf buf; ssize_t framelen; nghttp2_nv *nva; ssize_t nvlen; nva_out out; - ssize_t nv_offset; + ssize_t hdblocklen; + + nghttp2_buf_init(&buf); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater); @@ -91,22 +91,22 @@ 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, &buflen, &bufoff, &frame, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame, &deflater); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf + bufoff, - framelen - bufoff)); - check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH, + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + + check_frame_header(nghttp2_buf_len(&buf) - 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); - nv_offset = bufoff + NGHTTP2_FRAME_HEAD_LENGTH; - CU_ASSERT(framelen - nv_offset == + hdblocklen = nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN; + CU_ASSERT(hdblocklen == inflate_hd(&inflater, &out, - buf + nv_offset, framelen - nv_offset)); + buf.pos + NGHTTP2_FRAME_HDLEN, hdblocklen)); CU_ASSERT(7 == out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); @@ -114,32 +114,33 @@ void test_nghttp2_frame_pack_headers() nghttp2_frame_headers_free(&oframe); nva_out_reset(&out); + nghttp2_buf_reset(&buf); memset(&oframe, 0, sizeof(oframe)); /* Next, include PRIORITY flag */ frame.hd.flags |= NGHTTP2_FLAG_PRIORITY; - framelen = nghttp2_frame_pack_headers(&buf, &buflen, &bufoff, &frame, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame, &deflater); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf + bufoff, - framelen - bufoff)); - check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH, + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + + check_frame_header(nghttp2_buf_len(&buf) - 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); - nv_offset = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 4; - CU_ASSERT(framelen - nv_offset == + hdblocklen = nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN - 4; + CU_ASSERT(hdblocklen == inflate_hd(&inflater, &out, - buf + nv_offset, framelen - nv_offset)); + buf.pos + NGHTTP2_FRAME_HDLEN + 4, hdblocklen)); nghttp2_nv_array_sort(out.nva, out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); nva_out_reset(&out); - free(buf); + nghttp2_buf_free(&buf); nghttp2_frame_headers_free(&oframe); nghttp2_frame_headers_free(&frame); nghttp2_hd_inflate_free(&inflater); @@ -150,9 +151,7 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) { nghttp2_hd_deflater deflater; nghttp2_headers frame; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t bufoff; + nghttp2_buf buf; ssize_t framelen; nghttp2_nv *nva; ssize_t nvlen; @@ -161,6 +160,8 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) size_t big_hdslen = ARRLEN(big_hds); size_t i; + nghttp2_buf_init(&buf); + for(i = 0; i < big_hdslen; ++i) { big_hds[i].name = (uint8_t*)"header"; big_hds[i].value = malloc(big_vallen+1); @@ -176,12 +177,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, &buflen, &bufoff, &frame, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame, &deflater); CU_ASSERT_EQUAL(NGHTTP2_ERR_HEADER_COMP, framelen); nghttp2_frame_headers_free(&frame); - free(buf); + nghttp2_buf_free(&buf); for(i = 0; i < big_hdslen; ++i) { free(big_hds[i].value); } @@ -191,16 +191,21 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) void test_nghttp2_frame_pack_priority(void) { nghttp2_priority frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t framelen; + + nghttp2_buf_init(&buf); + nghttp2_frame_priority_init(&frame, 1000000007, 1 << 30); - framelen = nghttp2_frame_pack_priority(&buf, &buflen, &frame); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); + framelen = nghttp2_frame_pack_priority(&buf, &frame); + + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); check_frame_header(4, NGHTTP2_PRIORITY, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); CU_ASSERT(1 << 30 == oframe.pri); - free(buf); + + nghttp2_buf_free(&buf); nghttp2_frame_priority_free(&oframe); nghttp2_frame_priority_free(&frame); } @@ -208,16 +213,21 @@ void test_nghttp2_frame_pack_priority(void) void test_nghttp2_frame_pack_rst_stream(void) { nghttp2_rst_stream frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t framelen; + + nghttp2_buf_init(&buf); + nghttp2_frame_rst_stream_init(&frame, 1000000007, NGHTTP2_PROTOCOL_ERROR); - framelen = nghttp2_frame_pack_rst_stream(&buf, &buflen, &frame); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); + framelen = nghttp2_frame_pack_rst_stream(&buf, &frame); + + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); check_frame_header(4, NGHTTP2_RST_STREAM, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); CU_ASSERT(NGHTTP2_PROTOCOL_ERROR == oframe.error_code); - free(buf); + + nghttp2_buf_free(&buf); nghttp2_frame_rst_stream_free(&oframe); nghttp2_frame_rst_stream_free(&frame); } @@ -225,24 +235,33 @@ void test_nghttp2_frame_pack_rst_stream(void) void test_nghttp2_frame_pack_settings() { nghttp2_settings frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t framelen; int i; - nghttp2_settings_entry iv[3]; - iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - iv[0].value = 256; - iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - iv[1].value = 16384; - iv[2].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; - iv[2].value = 4096; + nghttp2_settings_entry iv[] = + { + { + NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 256 + }, + { + NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, 16384 + }, + { + NGHTTP2_SETTINGS_HEADER_TABLE_SIZE, 4096 + } + }; + + nghttp2_buf_init(&buf); nghttp2_frame_settings_init(&frame, NGHTTP2_FLAG_NONE, nghttp2_frame_iv_copy(iv, 3), 3); - framelen = nghttp2_frame_pack_settings(&buf, &buflen, &frame); + framelen = nghttp2_frame_pack_settings(&buf, &frame); + CU_ASSERT(NGHTTP2_FRAME_HEAD_LENGTH + 3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH == framelen); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); check_frame_header(3 * NGHTTP2_FRAME_SETTINGS_ENTRY_LENGTH, NGHTTP2_SETTINGS, NGHTTP2_FLAG_NONE, 0, &oframe.hd); CU_ASSERT(3 == oframe.niv); @@ -251,7 +270,7 @@ void test_nghttp2_frame_pack_settings() CU_ASSERT(iv[i].value == oframe.iv[i].value); } - free(buf); + nghttp2_buf_free(&buf); nghttp2_frame_settings_free(&frame); nghttp2_frame_settings_free(&oframe); } @@ -261,14 +280,14 @@ void test_nghttp2_frame_pack_push_promise() nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; nghttp2_push_promise frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t bufoff; + nghttp2_buf buf; ssize_t framelen; nghttp2_nv *nva; ssize_t nvlen; nva_out out; - ssize_t nv_offset; + ssize_t hdblocklen; + + nghttp2_buf_init(&buf); nva_out_init(&out); nghttp2_hd_deflate_init(&deflater); @@ -278,26 +297,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, &buflen, &bufoff, &frame, - &deflater); + framelen = nghttp2_frame_pack_push_promise(&buf, &frame, &deflater); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, - buf + bufoff, framelen - bufoff)); - check_frame_header(framelen - bufoff - NGHTTP2_FRAME_HEAD_LENGTH, + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); + + check_frame_header(nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN, NGHTTP2_PUSH_PROMISE, NGHTTP2_FLAG_END_HEADERS, 1000000007, &oframe.hd); CU_ASSERT((1U << 31) - 1 == oframe.promised_stream_id); - nv_offset = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 4; - CU_ASSERT(framelen - nv_offset == - inflate_hd(&inflater, &out, buf + nv_offset, framelen - nv_offset)); + hdblocklen = nghttp2_buf_len(&buf) - NGHTTP2_FRAME_HDLEN - 4; + CU_ASSERT(hdblocklen == + inflate_hd(&inflater, &out, + buf.pos + NGHTTP2_FRAME_HDLEN + 4, hdblocklen)); CU_ASSERT(7 == out.nvlen); CU_ASSERT(nvnameeq("method", &out.nva[0])); CU_ASSERT(nvvalueeq("GET", &out.nva[0])); nva_out_reset(&out); - free(buf); + nghttp2_buf_free(&buf); nghttp2_frame_push_promise_free(&oframe); nghttp2_frame_push_promise_free(&frame); nghttp2_hd_inflate_free(&inflater); @@ -307,17 +327,22 @@ void test_nghttp2_frame_pack_push_promise() void test_nghttp2_frame_pack_ping(void) { nghttp2_ping frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t framelen; const uint8_t opaque_data[] = "01234567"; + + nghttp2_buf_init(&buf); + nghttp2_frame_ping_init(&frame, NGHTTP2_FLAG_ACK, opaque_data); - framelen = nghttp2_frame_pack_ping(&buf, &buflen, &frame); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); + framelen = nghttp2_frame_pack_ping(&buf, &frame); + + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); 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); - free(buf); + + nghttp2_buf_free(&buf); nghttp2_frame_ping_free(&oframe); nghttp2_frame_ping_free(&frame); } @@ -325,17 +350,20 @@ void test_nghttp2_frame_pack_ping(void) void test_nghttp2_frame_pack_goaway() { nghttp2_goaway frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t framelen; size_t opaque_data_len = 16; uint8_t *opaque_data = malloc(opaque_data_len); + nghttp2_buf_init(&buf); + 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, &buflen, &frame); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); + framelen = nghttp2_frame_pack_goaway(&buf, &frame); + + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); 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); @@ -344,7 +372,8 @@ void test_nghttp2_frame_pack_goaway() CU_ASSERT(NULL == oframe.opaque_data); /* CU_ASSERT(opaque_data_len == oframe.opaque_data_len); */ /* CU_ASSERT(memcmp(opaque_data, oframe.opaque_data, opaque_data_len) == 0); */ - free(buf); + + nghttp2_buf_free(&buf); nghttp2_frame_goaway_free(&oframe); nghttp2_frame_goaway_free(&frame); } @@ -352,19 +381,22 @@ void test_nghttp2_frame_pack_goaway() void test_nghttp2_frame_pack_window_update(void) { nghttp2_window_update frame, oframe; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t framelen; + nghttp2_buf_init(&buf); + nghttp2_frame_window_update_init(&frame, NGHTTP2_FLAG_NONE, 1000000007, 4096); - framelen = nghttp2_frame_pack_window_update(&buf, &buflen, - &frame); - CU_ASSERT(0 == unpack_frame((nghttp2_frame*)&oframe, buf, framelen)); + framelen = nghttp2_frame_pack_window_update(&buf, &frame); + + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + CU_ASSERT(0 == unpack_framebuf((nghttp2_frame*)&oframe, &buf)); check_frame_header(4, NGHTTP2_WINDOW_UPDATE, NGHTTP2_FLAG_NONE, 1000000007, &oframe.hd); CU_ASSERT(4096 == oframe.window_size_increment); - free(buf); + + nghttp2_buf_free(&buf); 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 8a59742d..c715edba 100644 --- a/tests/nghttp2_hd_test.c +++ b/tests/nghttp2_hd_test.c @@ -52,77 +52,83 @@ void test_nghttp2_hd_deflate(void) MAKE_NV("cookie", "k1=v1")}; nghttp2_nv nva5[] = {MAKE_NV(":path", "/style.css"), MAKE_NV("x-nghttp2", "")}; - size_t nv_offset = 12; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t blocklen; nva_out out; + nghttp2_buf_init(&buf); + 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, &buflen, nv_offset, nva1, - sizeof(nva1)/sizeof(nghttp2_nv)); + + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva1, ARRLEN(nva1)); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == - inflate_hd(&inflater, &out, buf + nv_offset, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva1, out.nva, 3); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* Second headers */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva2, - sizeof(nva2)/sizeof(nghttp2_nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva2, ARRLEN(nva2)); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == - inflate_hd(&inflater, &out, buf + nv_offset, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(2 == out.nvlen); assert_nv_equal(nva2, out.nva, 2); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* Third headers, including same header field name, but value is not the same. */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva3, - sizeof(nva3)/sizeof(nghttp2_nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva3, ARRLEN(nva3)); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == - inflate_hd(&inflater, &out, buf + nv_offset, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva3, out.nva, 3); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* Fourth headers, including duplicate header fields. */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva4, - sizeof(nva4)/sizeof(nghttp2_nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva4, ARRLEN(nva4)); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == - inflate_hd(&inflater, &out, buf + nv_offset, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva4, out.nva, 3); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* Fifth headers includes empty value */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva5, - sizeof(nva5)/sizeof(nghttp2_nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva5, ARRLEN(nva5)); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == - inflate_hd(&inflater, &out, buf + nv_offset, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(2 == out.nvlen); assert_nv_equal(nva5, out.nva, 2); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* Cleanup */ - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -136,41 +142,46 @@ void test_nghttp2_hd_deflate_same_indexed_repr(void) nghttp2_nv nva2[] = {MAKE_NV("cookie", "alpha"), MAKE_NV("cookie", "alpha"), MAKE_NV("cookie", "alpha")}; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t blocklen; nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); CU_ASSERT(0 == nghttp2_hd_deflate_init(&deflater)); CU_ASSERT(0 == nghttp2_hd_inflate_init(&inflater)); /* 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, &buflen, 0, nva1, - sizeof(nva1)/sizeof(nghttp2_nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva1, ARRLEN(nva1)); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(2 == out.nvlen); assert_nv_equal(nva1, out.nva, 2); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* 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, &buflen, 0, nva2, - sizeof(nva2)/sizeof(nghttp2_nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva2, ARRLEN(nva2)); + CU_ASSERT(blocklen == 6); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(3 == out.nvlen); assert_nv_equal(nva2, out.nva, 3); nva_out_reset(&out); + nghttp2_buf_reset(&buf); /* Cleanup */ - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -181,8 +192,7 @@ void test_nghttp2_hd_deflate_common_header_eviction(void) nghttp2_hd_inflater inflater; nghttp2_nv nva[] = {MAKE_NV("h1", ""), MAKE_NV("h2", "")}; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; 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 @@ -191,6 +201,8 @@ void test_nghttp2_hd_deflate_common_header_eviction(void) nva_out out; size_t i; + nghttp2_buf_init(&buf); + nva_out_init(&out); memset(value, '0', sizeof(value)); for(i = 0; i < 2; ++i) { @@ -203,35 +215,42 @@ 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, &buflen, 0, nva, 1); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 1); + CU_ASSERT(blocklen > 0); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen)); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); 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); /* Encode with second header */ - blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, 0, nva, 2); + + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); + 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, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); 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); CU_ASSERT(1 == deflater.ctx.hd_table.len); CU_ASSERT(1 == inflater.ctx.hd_table.len); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -240,8 +259,7 @@ void test_nghttp2_hd_deflate_clear_refset(void) { nghttp2_hd_deflater deflater; nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t blocklen; nghttp2_nv nv[] = { MAKE_NV(":path", "/"), @@ -250,6 +268,8 @@ void test_nghttp2_hd_deflate_clear_refset(void) size_t i; nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); nghttp2_hd_deflate_init2(&deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE); @@ -257,18 +277,18 @@ 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, &buflen, 0, - nv, ARRLEN(nv)); + blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, nv, ARRLEN(nv)); CU_ASSERT(blocklen > 1); - CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf, blocklen)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(ARRLEN(nv) == out.nvlen); assert_nv_equal(nv, out.nva, ARRLEN(nv)); nva_out_reset(&out); + nghttp2_buf_reset(&buf); } - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -276,9 +296,8 @@ void test_nghttp2_hd_deflate_clear_refset(void) void test_nghttp2_hd_inflate_indname_noinc(void) { nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; + nghttp2_buf buf; + ssize_t blocklen; nghttp2_nv nv[] = { /* Huffman */ MAKE_NV("user-agent", "nghttp2"), @@ -288,42 +307,53 @@ void test_nghttp2_hd_inflate_indname_noinc(void) size_t i; nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); for(i = 0; i < ARRLEN(nv); ++i) { - offset = 0; - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 56, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 56, nv[i].value, nv[i].valuelen, 0)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); 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); } - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_inc(void) { nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; + nghttp2_buf buf; + ssize_t blocklen; nghttp2_nv nv = MAKE_NV("user-agent", "nghttp2"); nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 56, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 56, nv.value, nv.valuelen, 1)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); @@ -333,33 +363,38 @@ void test_nghttp2_hd_inflate_indname_inc(void) inflater.ctx.hd_table.len-1)->nv, 1); nva_out_reset(&out); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_indname_inc_eviction(void) { nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; + nghttp2_buf buf; + ssize_t blocklen; uint8_t value[1024]; nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); memset(value, '0', sizeof(value)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 13, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 13, value, sizeof(value), 1)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 14, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 14, value, sizeof(value), 1)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 15, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 15, value, sizeof(value), 1)); - CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, &buflen, &offset, 16, + CU_ASSERT(0 == nghttp2_hd_emit_indname_block(&buf, 16, value, sizeof(value), 1)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(4 == out.nvlen); CU_ASSERT(14 == out.nva[0].namelen); @@ -367,20 +402,20 @@ 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); CU_ASSERT(3 == inflater.ctx.hd_table.len); CU_ASSERT(GET_TABLE_ENT(&inflater.ctx, 0)->flags & NGHTTP2_HD_FLAG_REFSET); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_newname_noinc(void) { nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; + nghttp2_buf buf; + ssize_t blocklen; nghttp2_nv nv[] = { /* Expecting huffman for both */ MAKE_NV("my-long-content-length", "nghttp2"), @@ -394,40 +429,49 @@ void test_nghttp2_hd_inflate_newname_noinc(void) size_t i; nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); for(i = 0; i < ARRLEN(nv); ++i) { - offset = 0; - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset, - &nv[i], 0)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv[i], 0)); + + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); 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); } - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_newname_inc(void) { nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; + nghttp2_buf buf; + ssize_t blocklen; nghttp2_nv nv = MAKE_NV("x-rel", "nghttp2"); nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset, - &nv, 1)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv, 1)); + + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); @@ -437,20 +481,21 @@ void test_nghttp2_hd_inflate_newname_inc(void) inflater.ctx.hd_table.len-1)->nv, 1); nva_out_reset(&out); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); } void test_nghttp2_hd_inflate_clearall_inc(void) { nghttp2_hd_inflater inflater; - uint8_t *buf = NULL; - size_t buflen = 0; - size_t offset = 0; + nghttp2_buf buf; + ssize_t blocklen; nghttp2_nv nv; uint8_t value[4060]; nva_out out; + nghttp2_buf_init(&buf); + nva_out_init(&out); /* Total 4097 bytes space required to hold this entry */ nv.name = (uint8_t*)"alpha"; @@ -461,18 +506,22 @@ void test_nghttp2_hd_inflate_clearall_inc(void) nghttp2_hd_inflate_init(&inflater); - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset, - &nv, 1)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv, 1)); + + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); 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((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); CU_ASSERT(1 == out.nvlen); assert_nv_equal(&nv, out.nva, 1); @@ -484,18 +533,21 @@ void test_nghttp2_hd_inflate_clearall_inc(void) header table */ nv.valuelen = sizeof(value) - 1; - offset = 0; - CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &buflen, &offset, - &nv, 1)); - CU_ASSERT((ssize_t)offset == inflate_hd(&inflater, &out, buf, offset)); + CU_ASSERT(0 == nghttp2_hd_emit_newname_block(&buf, &nv, 1)); + + blocklen = nghttp2_buf_len(&buf); + + CU_ASSERT(blocklen > 0); + CU_ASSERT(blocklen == inflate_hd(&inflater, &out, buf.pos, blocklen)); 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); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); } @@ -531,11 +583,11 @@ void test_nghttp2_hd_change_table_size(void) nghttp2_hd_inflater inflater; nghttp2_nv nva[] = { MAKE_NV(":method", "GET"), MAKE_NV(":path", "/") }; - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t rv; nva_out out; - size_t offset; + + nghttp2_buf_init(&buf); nva_out_init(&out); @@ -554,17 +606,19 @@ 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, &buflen, 0, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); CU_ASSERT(rv > 0); + CU_ASSERT(rv == nghttp2_buf_len(&buf)); 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, rv)); + CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); 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); /* inflater changes header table size to 1024 */ CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 1024)); @@ -577,17 +631,19 @@ 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, &buflen, 0, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); CU_ASSERT(rv >= 0); + CU_ASSERT(rv == nghttp2_buf_len(&buf)); 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, rv)); + CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); 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); /* inflater changes header table size to 0 */ CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 0)); @@ -602,25 +658,27 @@ 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, &buflen, 0, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); CU_ASSERT(rv >= 0); + CU_ASSERT(rv == nghttp2_buf_len(&buf)); 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, rv)); + CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); 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); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); /* Check table buffer is expanded */ - buf = NULL; - buflen = 0; + nghttp2_buf_init(&buf); + nghttp2_hd_deflate_init2(&deflater, 8192); nghttp2_hd_inflate_init(&inflater); @@ -635,17 +693,19 @@ 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, &buflen, 0, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); CU_ASSERT(rv > 0); + CU_ASSERT(rv == nghttp2_buf_len(&buf)); 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, rv)); + CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); 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); CU_ASSERT(0 == nghttp2_hd_inflate_change_table_size(&inflater, 16383)); CU_ASSERT(0 == nghttp2_hd_deflate_change_table_size(&deflater, 16383)); @@ -657,26 +717,29 @@ 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, &buflen, 0, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); CU_ASSERT(rv >= 0); + CU_ASSERT(rv == nghttp2_buf_len(&buf)); 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, rv)); + CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); 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); /* Lastly, check the error condition */ - offset = 0; - rv = nghttp2_hd_emit_table_size(&buf, &buflen, &offset, 25600); + + rv = nghttp2_hd_emit_table_size(&buf, 25600); CU_ASSERT(rv == 0); CU_ASSERT(NGHTTP2_ERR_HEADER_COMP == - inflate_hd(&inflater, &out, buf, offset)); + inflate_hd(&inflater, &out, buf.pos, nghttp2_buf_len(&buf))); nva_out_reset(&out); + nghttp2_buf_reset(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); @@ -690,19 +753,21 @@ 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, &buflen, 0, nva, 2); + rv = nghttp2_hd_deflate_hd(&deflater, &buf, nva, 2); CU_ASSERT(rv > 0); + CU_ASSERT(rv == nghttp2_buf_len(&buf)); 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, rv)); + CU_ASSERT(rv == inflate_hd(&inflater, &out, buf.pos, rv)); 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); - free(buf); + nghttp2_buf_free(&buf); nghttp2_hd_inflate_free(&inflater); nghttp2_hd_deflate_free(&deflater); } @@ -711,22 +776,25 @@ static void check_deflate_inflate(nghttp2_hd_deflater *deflater, nghttp2_hd_inflater *inflater, nghttp2_nv *nva, size_t nvlen) { - uint8_t *buf = NULL; - size_t buflen = 0; + nghttp2_buf buf; ssize_t blocklen; nva_out out; - nva_out_init(&out); - blocklen = nghttp2_hd_deflate_hd(deflater, &buf, &buflen, 0, nva, nvlen); - assert(blocklen >= 0); + nghttp2_buf_init(&buf); - CU_ASSERT(blocklen == inflate_hd(inflater, &out, buf, blocklen)); + nva_out_init(&out); + blocklen = nghttp2_hd_deflate_hd(deflater, &buf, nva, nvlen); + + CU_ASSERT(blocklen >= 0); + CU_ASSERT(blocklen == nghttp2_buf_len(&buf)); + + CU_ASSERT(blocklen == inflate_hd(inflater, &out, buf.pos, blocklen)); CU_ASSERT(nvlen == out.nvlen); assert_nv_equal(nva, out.nva, nvlen); nva_out_reset(&out); - free(buf); + nghttp2_buf_free(&buf); } void test_nghttp2_hd_deflate_inflate(void) diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 882a7b3b..d7a68352 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -346,37 +346,39 @@ void test_nghttp2_session_recv(void) const nghttp2_nv nv[] = { MAKE_NV("url", "/") }; - uint8_t *framedata = NULL; - size_t framedatalen = 0; - size_t bufoff; + nghttp2_buf buf; ssize_t framelen; nghttp2_frame frame; - size_t i; + ssize_t i; nghttp2_outbound_item *item; nghttp2_nv *nva; ssize_t nvlen; nghttp2_hd_deflater deflater; + nghttp2_buf_init(&buf); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; callbacks.recv_callback = scripted_recv_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; + user_data.df = &df; + nghttp2_session_server_new(&session, &callbacks, &user_data); nghttp2_hd_deflate_init(&deflater); 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(&framedata, &framedatalen, &bufoff, - &frame.headers, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + + scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); - scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); /* Send 1 byte per each read */ - for(i = 0; i < framelen - bufoff; ++i) { + for(i = 0; i < nghttp2_buf_len(&buf); ++i) { df.feedseq[i] = 1; } + nghttp2_frame_headers_free(&frame.headers); user_data.frame_recv_cb_called = 0; @@ -385,16 +387,16 @@ void test_nghttp2_session_recv(void) } CU_ASSERT(1 == user_data.frame_recv_cb_called); + nghttp2_buf_reset(&buf); + /* 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(&framedata, &framedatalen, &bufoff, - &frame.headers, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); nghttp2_frame_headers_free(&frame.headers); - scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); + scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(1 == user_data.frame_recv_cb_called); @@ -405,15 +407,21 @@ void test_nghttp2_session_recv(void) /* Some tests for frame too large */ nghttp2_session_server_new(&session, &callbacks, &user_data); + nghttp2_buf_reset(&buf); + /* Receive PING with too large payload */ nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL); - nghttp2_reserve_buffer(&framedata, &framedatalen, 77); - framelen = nghttp2_frame_pack_ping(&framedata, &framedatalen, &frame.ping); + + framelen = nghttp2_frame_pack_ping(&buf, &frame.ping); + + /* 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_frame_ping_free(&frame.ping); - nghttp2_put_uint16be(&framedata[0], - framedatalen - NGHTTP2_FRAME_HEAD_LENGTH); - scripted_data_feed_init(&df, framedata, framedatalen); + scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); user_data.frame_recv_cb_called = 0; CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == user_data.frame_recv_cb_called); @@ -422,7 +430,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)); - free(framedata); + nghttp2_buf_free(&buf); nghttp2_session_del(session); } @@ -432,13 +440,13 @@ void test_nghttp2_session_recv_invalid_stream_id(void) nghttp2_session_callbacks callbacks; scripted_data_feed df; my_user_data user_data; - uint8_t *framedata = NULL; - size_t framedatalen = 0; - size_t bufoff; + nghttp2_buf buf; ssize_t framelen; nghttp2_frame frame; nghttp2_hd_deflater deflater; + nghttp2_buf_init(&buf); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback = scripted_recv_callback; callbacks.on_invalid_frame_recv_callback = on_invalid_frame_recv_callback; @@ -450,17 +458,17 @@ 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(&framedata, &framedatalen, &bufoff, - &frame.headers, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); - scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + + scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); nghttp2_frame_headers_free(&frame.headers); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(1 == user_data.invalid_frame_recv_cb_called); - free(framedata); + nghttp2_buf_free(&buf); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -474,15 +482,15 @@ void test_nghttp2_session_recv_invalid_frame(void) const nghttp2_nv nv[] = { MAKE_NV("url", "/") }; - uint8_t *framedata = NULL; - size_t framedatalen = 0; - size_t bufoff; + nghttp2_buf buf; ssize_t framelen; nghttp2_frame frame; nghttp2_nv *nva; ssize_t nvlen; nghttp2_hd_deflater deflater; + nghttp2_buf_init(&buf); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.recv_callback = scripted_recv_callback; callbacks.send_callback = null_send_callback; @@ -495,11 +503,11 @@ 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(&framedata, &framedatalen, &bufoff, - &frame.headers, - &deflater); + framelen = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); - scripted_data_feed_init(&df, framedata + bufoff, framelen - bufoff); + CU_ASSERT(framelen == nghttp2_buf_len(&buf)); + + scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); @@ -507,13 +515,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, framedata + bufoff, framelen - bufoff); + scripted_data_feed_init(&df, buf.pos, nghttp2_buf_len(&buf)); CU_ASSERT(0 == nghttp2_session_recv(session)); CU_ASSERT(0 == nghttp2_session_send(session)); CU_ASSERT(0 == user_data.frame_send_cb_called); - free(framedata); + nghttp2_buf_free(&buf); nghttp2_frame_headers_free(&frame.headers); nghttp2_hd_deflate_free(&deflater); @@ -672,11 +680,7 @@ void test_nghttp2_session_recv_continuation(void) nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; - uint8_t *framedata = NULL; - size_t framedatacap = 0; - size_t framedatalen; - size_t bufoff; - size_t framedataoff; + nghttp2_buf buf; ssize_t rv; my_user_data ud; nghttp2_hd_deflater deflater; @@ -684,6 +688,8 @@ void test_nghttp2_session_recv_continuation(void) size_t datalen; nghttp2_frame_hd cont_hd; + nghttp2_buf_init(&buf); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.on_header_callback = on_header_callback; callbacks.on_begin_headers_callback = on_begin_headers_callback; @@ -696,15 +702,16 @@ 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); - framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, - &bufoff, - &frame.headers, - &deflater); + rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + + CU_ASSERT(rv == nghttp2_buf_len(&buf)); + nghttp2_frame_headers_free(&frame.headers); - memcpy(data, framedata + bufoff, 9); + /* HEADERS's payload is 1 byte */ + memcpy(data, buf.pos, 9); datalen = 9; - framedataoff = bufoff + NGHTTP2_FRAME_HEAD_LENGTH + 1; + buf.pos += 9; nghttp2_put_uint16be(data, 1); @@ -715,25 +722,25 @@ void test_nghttp2_session_recv_continuation(void) cont_hd.stream_id = 1; nghttp2_frame_pack_frame_hd(data + datalen, &cont_hd); - datalen += NGHTTP2_FRAME_HEAD_LENGTH; + datalen += NGHTTP2_FRAME_HDLEN; - memcpy(data + datalen, framedata + framedataoff, cont_hd.length); + memcpy(data + datalen, buf.pos, cont_hd.length); datalen += cont_hd.length; - framedataoff += cont_hd.length; + buf.pos += cont_hd.length; /* Second CONTINUATION, rest of the bytes */ - cont_hd.length = framedatalen - framedataoff; + 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_HEAD_LENGTH; - memcpy(data + datalen, framedata + framedataoff, cont_hd.length); + memcpy(data + datalen, buf.pos, cont_hd.length); datalen += cont_hd.length; - framedataoff += cont_hd.length; + buf.pos += cont_hd.length; - assert(framedataoff == framedatalen); + CU_ASSERT(0 == nghttp2_buf_len(&buf)); ud.header_cb_called = 0; rv = nghttp2_session_mem_recv(session, data, datalen); @@ -752,19 +759,25 @@ 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); - framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, &bufoff, - &frame.headers, - &deflater); + nghttp2_buf_reset(&buf); + rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + + CU_ASSERT(rv == nghttp2_buf_len(&buf)); + nghttp2_frame_headers_free(&frame.headers); - memcpy(data, framedata + bufoff, framedatalen - bufoff); - datalen = framedatalen - bufoff; + memcpy(data, buf.pos, nghttp2_buf_len(&buf)); + datalen = nghttp2_buf_len(&buf); /* Followed by PRIORITY */ nghttp2_frame_priority_init(&frame.priority, 1, 0); - framedatalen = nghttp2_frame_pack_priority(&framedata, &framedatacap, - &frame.priority); - memcpy(data + datalen, framedata, framedatalen); - datalen += framedatalen; + nghttp2_buf_reset(&buf); + + rv = nghttp2_frame_pack_priority(&buf, &frame.priority); + + CU_ASSERT(rv == 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); @@ -774,7 +787,7 @@ void test_nghttp2_session_recv_continuation(void) CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(nghttp2_session_get_next_ob_item(session))); - free(framedata); + nghttp2_buf_free(&buf); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -790,14 +803,13 @@ void test_nghttp2_session_recv_premature_headers(void) nghttp2_nv *nva; size_t nvlen; nghttp2_frame frame; - uint8_t *framedata = NULL; - size_t framedatacap = 0; - size_t framedatalen; + nghttp2_buf buf; ssize_t rv; my_user_data ud; nghttp2_hd_deflater deflater; nghttp2_outbound_item *item; - size_t bufoff = 0; + + nghttp2_buf_init(&buf); memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); @@ -808,24 +820,24 @@ 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); - framedatalen = nghttp2_frame_pack_headers(&framedata, &framedatacap, - &bufoff, - &frame.headers, - &deflater); + rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + + CU_ASSERT(rv == nghttp2_buf_len(&buf)); + nghttp2_frame_headers_free(&frame.headers); /* Intentionally feed payload cutting last 1 byte off */ - nghttp2_put_uint16be(framedata + bufoff, frame.hd.length - 1); - rv = nghttp2_session_mem_recv(session, framedata + bufoff, - framedatalen - bufoff - 1); - CU_ASSERT((ssize_t)(framedatalen - bufoff - 1) == rv); + 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); - free(framedata); + nghttp2_buf_free(&buf); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -843,14 +855,11 @@ void test_nghttp2_session_continue(void) MAKE_NV("user-agent", "nghttp2/1.0.0"), MAKE_NV("alpha", "bravo") }; - uint8_t *framedata = NULL; - size_t framedatalen = 0; - ssize_t framelen1, framelen2; + nghttp2_buf buf; + size_t framelen1, framelen2; ssize_t rv; uint8_t buffer[4096]; - uint8_t *bufp = buffer; - size_t buflen; - size_t bufoff; + nghttp2_buf databuf; nghttp2_frame frame; nghttp2_nv *nva; ssize_t nvlen; @@ -858,6 +867,9 @@ void test_nghttp2_session_continue(void) nghttp2_frame_hd data_hd; nghttp2_hd_deflater deflater; + nghttp2_buf_init(&buf); + nghttp2_buf_wrap_init(&databuf, buffer, sizeof(buffer)); + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; callbacks.on_frame_recv_callback = on_frame_recv_callback; @@ -873,38 +885,40 @@ 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); - framelen1 = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, - &frame.headers, - &deflater); + rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + + CU_ASSERT(rv == nghttp2_buf_len(&buf)); + nghttp2_frame_headers_free(&frame.headers); - memcpy(buffer, framedata + bufoff, framelen1 - bufoff); - framelen1 -= bufoff; + 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); - framelen2 = nghttp2_frame_pack_headers(&framedata, &framedatalen, &bufoff, - &frame.headers, - &deflater); + nghttp2_buf_reset(&buf); + rv = nghttp2_frame_pack_headers(&buf, &frame.headers, &deflater); + + CU_ASSERT(rv == nghttp2_buf_len(&buf)); + nghttp2_frame_headers_free(&frame.headers); - memcpy(buffer + framelen1, framedata + bufoff, framelen2 - bufoff); - framelen2 -= bufoff; - buflen = framelen1 + framelen2; + 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; user_data.header_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufp, buflen); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); - bufp += rv; - buflen -= rv; + CU_ASSERT(rv >= 0); + databuf.pos += rv; recv_frame = user_data.frame; CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); - CU_ASSERT((size_t)framelen1 - NGHTTP2_FRAME_HEAD_LENGTH == - recv_frame->hd.length); + CU_ASSERT(framelen1 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length); CU_ASSERT(1 == user_data.begin_headers_cb_called); CU_ASSERT(1 == user_data.header_cb_called); @@ -914,10 +928,11 @@ void test_nghttp2_session_continue(void) /* get 2nd header field */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufp, buflen); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); - bufp += rv; - buflen -= rv; + CU_ASSERT(rv >= 0); + databuf.pos += rv; CU_ASSERT(0 == user_data.begin_headers_cb_called); CU_ASSERT(1 == user_data.header_cb_called); @@ -927,15 +942,15 @@ void test_nghttp2_session_continue(void) /* will call end_headers_callback and receive 2nd HEADERS and pause */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufp, buflen); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); - bufp += rv; - buflen -= rv; + CU_ASSERT(rv >= 0); + databuf.pos += rv; recv_frame = user_data.frame; CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); - CU_ASSERT((size_t)framelen2 - NGHTTP2_FRAME_HEAD_LENGTH == - recv_frame->hd.length); + CU_ASSERT(framelen2 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length); CU_ASSERT(1 == user_data.begin_headers_cb_called); CU_ASSERT(1 == user_data.header_cb_called); @@ -945,10 +960,11 @@ void test_nghttp2_session_continue(void) /* get 2nd header field */ user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufp, buflen); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); - bufp += rv; - buflen -= rv; + CU_ASSERT(rv >= 0); + databuf.pos += rv; CU_ASSERT(0 == user_data.begin_headers_cb_called); CU_ASSERT(1 == user_data.header_cb_called); @@ -959,10 +975,11 @@ void test_nghttp2_session_continue(void) user_data.begin_headers_cb_called = 0; user_data.header_cb_called = 0; user_data.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, bufp, buflen); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); - bufp += rv; - buflen -= rv; + CU_ASSERT(rv >= 0); + databuf.pos += rv; CU_ASSERT(0 == user_data.begin_headers_cb_called); CU_ASSERT(0 == user_data.header_cb_called); @@ -973,13 +990,18 @@ void test_nghttp2_session_continue(void) data_hd.type = NGHTTP2_DATA; data_hd.flags = NGHTTP2_FLAG_NONE; data_hd.stream_id = 1; - nghttp2_frame_pack_frame_hd(buffer, &data_hd); - bufp = buffer; - buflen = sizeof(buffer); + + nghttp2_buf_reset(&databuf); + nghttp2_frame_pack_frame_hd(databuf.pos, &data_hd); + /* Intentionally specify larger buffer size to see pause is kicked in. */ + databuf.last = databuf.end; + user_data.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, buffer, sizeof(buffer)); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); + CU_ASSERT(16 + NGHTTP2_FRAME_HEAD_LENGTH == rv); CU_ASSERT(0 == user_data.frame_recv_cb_called); @@ -987,7 +1009,8 @@ void test_nghttp2_session_continue(void) pause again in on_data_chunk_recv_callback since we pass same DATA frame. */ user_data.frame_recv_cb_called = 0; - rv = nghttp2_session_mem_recv(session, buffer, sizeof(buffer)); + rv = nghttp2_session_mem_recv(session, + databuf.pos, nghttp2_buf_len(&databuf)); CU_ASSERT(16 + NGHTTP2_FRAME_HEAD_LENGTH == rv); CU_ASSERT(1 == user_data.frame_recv_cb_called); @@ -997,7 +1020,7 @@ void test_nghttp2_session_continue(void) CU_ASSERT(0 == rv); CU_ASSERT(1 == user_data.frame_recv_cb_called); - free(framedata); + nghttp2_buf_free(&buf); nghttp2_hd_deflate_free(&deflater); nghttp2_session_del(session); } @@ -1022,13 +1045,16 @@ void test_nghttp2_session_add_frame(void) memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = accumulator_send_callback; + memset(aux_data, 0, sizeof(nghttp2_headers_aux_data)); acc.length = 0; user_data.acc = &acc; + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &user_data)); frame = malloc(sizeof(nghttp2_frame)); nvlen = nghttp2_nv_array_copy(&nva, nv, ARRLEN(nv)); + nghttp2_frame_headers_init(&frame->headers, NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY, -1, NGHTTP2_PRI_DEFAULT, nva, nvlen); @@ -2239,6 +2265,8 @@ void test_nghttp2_submit_data(void) my_user_data ud; nghttp2_private_data *data_frame; nghttp2_frame_hd hd; + nghttp2_active_outbound_item *aob; + nghttp2_buf *framebuf; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = block_count_send_callback; @@ -2246,6 +2274,8 @@ void test_nghttp2_submit_data(void) data_prd.read_callback = fixed_length_data_source_read_callback; 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; nghttp2_session_open_stream(session, 1, NGHTTP2_STREAM_FLAG_NONE, NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENING, @@ -2253,12 +2283,12 @@ void test_nghttp2_submit_data(void) CU_ASSERT(0 == nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT, 1, &data_prd)); + ud.block_count = 0; CU_ASSERT(0 == nghttp2_session_send(session)); - data_frame = nghttp2_outbound_item_get_data_frame(session->aob.item); - nghttp2_frame_unpack_frame_hd(&hd, - session->aob.framebuf + - session->aob.framebufoff); + data_frame = nghttp2_outbound_item_get_data_frame(aob->item); + nghttp2_frame_unpack_frame_hd(&hd, framebuf->pos); + CU_ASSERT(NGHTTP2_FLAG_NONE == hd.flags); /* frame->hd.flags has these flags */ CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT) == @@ -2266,10 +2296,9 @@ 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(session->aob.item); - nghttp2_frame_unpack_frame_hd(&hd, - session->aob.framebuf + - session->aob.framebufoff); + data_frame = nghttp2_outbound_item_get_data_frame(aob->item); + nghttp2_frame_unpack_frame_hd(&hd, framebuf->pos); + /* This is the last frame, so we must have following flags */ CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_SEGMENT) == hd.flags); /* frame->hd.flags has these flags */ @@ -4100,8 +4129,8 @@ void test_nghttp2_session_pack_data_with_padding(void) /* Check reception of this DATA frame */ check_session_recv_data_with_padding - (session->aob.framebuf + session->aob.framebufoff, - session->aob.framebufmark - session->aob.framebufoff, + (session->aob.framebuf.pos, + session->aob.framebuf.mark - session->aob.framebuf.pos, datalen); nghttp2_session_del(session); @@ -4126,8 +4155,8 @@ void test_nghttp2_session_pack_data_with_padding(void) /* Check reception of this DATA frame */ check_session_recv_data_with_padding - (session->aob.framebuf + session->aob.framebufoff, - session->aob.framebufmark - session->aob.framebufoff, + (session->aob.framebuf.pos, + session->aob.framebuf.mark - session->aob.framebuf.pos, datalen); nghttp2_session_del(session); @@ -4158,6 +4187,8 @@ void test_nghttp2_session_pack_headers_with_padding(void) acc.length = 0; ud.acc = &acc; + /* In this test, padding is laid out across 2 frames: HEADERS and + CONTINUATION frames */ nghttp2_session_client_new(&session, &callbacks, &ud); nghttp2_session_server_new(&sv_session, &callbacks, &ud); @@ -4193,6 +4224,147 @@ void test_nghttp2_session_pack_headers_with_padding(void) nghttp2_session_del(session); } +void test_nghttp2_session_pack_headers_with_padding2(void) +{ + nghttp2_session *session, *sv_session; + accumulator acc; + my_user_data ud; + nghttp2_session_callbacks callbacks; + nghttp2_nv nva[16382]; + size_t i; + + for(i = 0; i < ARRLEN(nva); ++i) { + nva[i].name = (uint8_t*)":path"; + nva[i].namelen = 5; + nva[i].value = (uint8_t*)"/"; + nva[i].valuelen = 1; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + acc.length = 0; + ud.acc = &acc; + + /* In this test, padding is laid out across 2 frames: HEADERS and + CONTINUATION frames */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_server_new(&sv_session, &callbacks, &ud); + + ud.padding_boundary = 16385; + + CU_ASSERT(0 == + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, + nva, ARRLEN(nva), NULL, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(acc.length > NGHTTP2_MAX_FRAME_LENGTH); + + ud.frame_recv_cb_called = 0; + CU_ASSERT((ssize_t)acc.length == + nghttp2_session_mem_recv(sv_session, acc.buf, acc.length)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session)); + + nghttp2_session_del(sv_session); + nghttp2_session_del(session); +} + +void test_nghttp2_session_pack_headers_with_padding3(void) +{ + nghttp2_session *session, *sv_session; + accumulator acc; + my_user_data ud; + nghttp2_session_callbacks callbacks; + nghttp2_nv nva[8192]; + size_t i; + + for(i = 0; i < ARRLEN(nva); ++i) { + nva[i].name = (uint8_t*)":path"; + nva[i].namelen = 5; + nva[i].value = (uint8_t*)"/"; + nva[i].valuelen = 1; + } + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + acc.length = 0; + ud.acc = &acc; + + /* In this test, padding is included in the last CONTINUATION + frame */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_server_new(&sv_session, &callbacks, &ud); + + ud.padding_boundary = 16385; + + CU_ASSERT(0 == + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, + nva, ARRLEN(nva), NULL, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(acc.length > NGHTTP2_MAX_FRAME_LENGTH); + ud.frame_recv_cb_called = 0; + CU_ASSERT((ssize_t)acc.length == + nghttp2_session_mem_recv(sv_session, acc.buf, acc.length)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session)); + + nghttp2_session_del(sv_session); + nghttp2_session_del(session); +} + +void test_nghttp2_session_pack_headers_with_padding4(void) +{ + nghttp2_session *session, *sv_session; + accumulator acc; + my_user_data ud; + nghttp2_session_callbacks callbacks; + nghttp2_nv nva[1]; + + nva[0].name = (uint8_t*)":path"; + nva[0].namelen = 5; + nva[0].value = (uint8_t*)"/"; + nva[0].valuelen = 1; + + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.send_callback = accumulator_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + callbacks.select_padding_callback = select_padding_callback; + callbacks.on_frame_recv_callback = on_frame_recv_callback; + + acc.length = 0; + ud.acc = &acc; + + /* In this test, padding is included in the first HEADERS frame */ + nghttp2_session_client_new(&session, &callbacks, &ud); + nghttp2_session_server_new(&sv_session, &callbacks, &ud); + + ud.padding_boundary = 16385; + + CU_ASSERT(0 == + nghttp2_submit_request(session, NGHTTP2_PRI_DEFAULT, + nva, ARRLEN(nva), NULL, NULL)); + CU_ASSERT(0 == nghttp2_session_send(session)); + + CU_ASSERT(acc.length < NGHTTP2_MAX_FRAME_LENGTH); + ud.frame_recv_cb_called = 0; + CU_ASSERT((ssize_t)acc.length == + nghttp2_session_mem_recv(sv_session, acc.buf, acc.length)); + CU_ASSERT(1 == ud.frame_recv_cb_called); + CU_ASSERT(NULL == nghttp2_session_get_next_ob_item(sv_session)); + + nghttp2_session_del(sv_session); + nghttp2_session_del(session); +} + void test_nghttp2_pack_settings_payload(void) { nghttp2_settings_entry iv[2]; diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index 146cc620..aaa8025b 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -92,6 +92,9 @@ void test_nghttp2_session_set_option(void); void test_nghttp2_session_data_backoff_by_high_pri_frame(void); void test_nghttp2_session_pack_data_with_padding(void); void test_nghttp2_session_pack_headers_with_padding(void); +void test_nghttp2_session_pack_headers_with_padding2(void); +void test_nghttp2_session_pack_headers_with_padding3(void); +void test_nghttp2_session_pack_headers_with_padding4(void); void test_nghttp2_pack_settings_payload(void); #endif /* NGHTTP2_SESSION_TEST_H */ diff --git a/tests/nghttp2_test_helper.c b/tests/nghttp2_test_helper.c index 79e0edc8..ec2f18a7 100644 --- a/tests/nghttp2_test_helper.c +++ b/tests/nghttp2_test_helper.c @@ -28,6 +28,11 @@ #include +int unpack_framebuf(nghttp2_frame *frame, nghttp2_buf *buf) +{ + return unpack_frame(frame, buf->pos, nghttp2_buf_len(buf)); +} + int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len) { ssize_t rv = 0; diff --git a/tests/nghttp2_test_helper.h b/tests/nghttp2_test_helper.h index 51efa85a..0a0be66e 100644 --- a/tests/nghttp2_test_helper.h +++ b/tests/nghttp2_test_helper.h @@ -54,6 +54,8 @@ free(a); \ } while(0); +int unpack_framebuf(nghttp2_frame *frame, nghttp2_buf *buf); + int unpack_frame(nghttp2_frame *frame, const uint8_t *in, size_t len); int strmemeq(const char *a, const uint8_t *b, size_t bn);