diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index 054944d3..144b7db3 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -462,15 +462,13 @@ static int ensure_write_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, static size_t count_encoded_length(size_t n, int prefix) { + size_t k = (1 << prefix) - 1; size_t len = 0; - if(prefix > 0) { - size_t k = (1 << prefix) - 1; - if(n >= k) { - n -= k; - ++len; - } else { - return 1; - } + if(n >= k) { + n -= k; + ++len; + } else { + return 1; } do { ++len; @@ -485,17 +483,15 @@ static size_t count_encoded_length(size_t n, int prefix) static size_t encode_length(uint8_t *buf, size_t n, int prefix) { + size_t k = (1 << prefix) - 1; size_t len = 0; - if(prefix > 0) { - size_t k = (1 << prefix) - 1; - if(n >= k) { - *buf++ = k; - n -= k; - ++len; - } else { - *buf++ = n; - return 1; - } + if(n >= k) { + *buf++ = k; + n -= k; + ++len; + } else { + *buf++ = n; + return 1; } do { ++len; @@ -528,17 +524,13 @@ static uint8_t* decode_length(ssize_t *res, uint8_t *in, uint8_t *last, *res = -1; return in; } - if(prefix > 0) { - if((*in & k) == k) { - *res = k; - } else { - *res = (*in) & k; - return in + 1; - } - ++in; + if((*in & k) == k) { + *res = k; } else { - *res = 0; + *res = (*in) & k; + return in + 1; } + ++in; for(r = 0; in != last; ++in, r += 7) { *res += (*in & 0x7f) << r; if(*res >= (1 << 16)) { @@ -549,7 +541,7 @@ static uint8_t* decode_length(ssize_t *res, uint8_t *in, uint8_t *last, break; } } - if(*in & (1 << 7)) { + if(in == last || *in & (1 << 7)) { *res = -1; return NULL; } else { @@ -584,14 +576,14 @@ static int emit_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr, uint8_t *bufp; size_t encvallen = nghttp2_hd_huff_encode_count(value, valuelen, side); size_t blocklen = count_encoded_length(index + 1, 6) + - count_encoded_length(encvallen, 0) + encvallen; + count_encoded_length(encvallen, 8) + encvallen; rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); if(rv != 0) { return rv; } bufp = *buf_ptr + *offset_ptr; bufp += encode_length(bufp, index + 1, 6); - bufp += encode_length(bufp, encvallen, 0); + bufp += encode_length(bufp, encvallen, 8); nghttp2_hd_huff_encode(bufp, *buflen_ptr - (bufp - *buf_ptr), value, valuelen, side); if(!inc_indexing) { @@ -613,19 +605,19 @@ static int emit_newname_block(uint8_t **buf_ptr, size_t *buflen_ptr, nghttp2_hd_huff_encode_count(nv->name, nv->namelen, side); size_t encvallen = nghttp2_hd_huff_encode_count(nv->value, nv->valuelen, side); - size_t blocklen = 1 + count_encoded_length(encnamelen, 0) + encnamelen + - count_encoded_length(encvallen, 0) + encvallen; + size_t blocklen = 1 + count_encoded_length(encnamelen, 8) + encnamelen + + count_encoded_length(encvallen, 8) + encvallen; rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); if(rv != 0) { return rv; } bufp = *buf_ptr + *offset_ptr; *bufp++ = inc_indexing ? 0 : 0x40u; - bufp += encode_length(bufp, encnamelen, 0); + bufp += encode_length(bufp, encnamelen, 8); nghttp2_hd_huff_encode(bufp, *buflen_ptr - (bufp - *buf_ptr), nv->name, nv->namelen, side); bufp += encnamelen; - bufp += encode_length(bufp, encvallen, 0); + bufp += encode_length(bufp, encvallen, 8); nghttp2_hd_huff_encode(bufp, *buflen_ptr - (bufp - *buf_ptr), nv->value, nv->valuelen, side); *offset_ptr += blocklen; @@ -988,7 +980,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, rv = NGHTTP2_ERR_HEADER_COMP; goto fail; } - in = decode_length(&namelen, in, last, 0); + in = decode_length(&namelen, in, last, 8); if(namelen < 0 || in + namelen > last) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; @@ -1006,7 +998,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, goto fail; } - in = decode_length(&valuelen, in, last, 0); + in = decode_length(&valuelen, in, last, 8); if(valuelen < 0 || in + valuelen > last) { free(nv.name); rv = NGHTTP2_ERR_HEADER_COMP; @@ -1057,7 +1049,7 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater, goto fail; } ent = nghttp2_hd_table_get(inflater, index); - in = decode_length(&valuelen, in , last, 0); + in = decode_length(&valuelen, in , last, 8); if(valuelen < 0 || in + valuelen > last) { rv = NGHTTP2_ERR_HEADER_COMP; goto fail; diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 7eb6b642..048b0a2a 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -1597,11 +1597,16 @@ int nghttp2_session_send(nghttp2_session *session) return NGHTTP2_ERR_CALLBACK_FAILURE; } } else { - session->aob.framebufoff += sentlen; - if(session->aob.item->frame_cat == NGHTTP2_CAT_DATA) { + if(session->aob.item->frame_cat == NGHTTP2_CAT_DATA && + session->aob.framebufoff + sentlen > NGHTTP2_FRAME_HEAD_LENGTH) { nghttp2_data *frame; nghttp2_stream *stream; - uint16_t len = nghttp2_get_uint16(&session->aob.framebuf[0]); + uint16_t len; + if(session->aob.framebufoff < NGHTTP2_FRAME_HEAD_LENGTH) { + len = session->aob.framebufoff + sentlen - NGHTTP2_FRAME_HEAD_LENGTH; + } else { + len = sentlen; + } frame = nghttp2_outbound_item_get_data_frame(session->aob.item); stream = nghttp2_session_get_stream(session, frame->hd.stream_id); if(stream && stream->remote_flow_control) { @@ -1611,6 +1616,7 @@ int nghttp2_session_send(nghttp2_session *session) session->remote_window_size -= len; } } + session->aob.framebufoff += sentlen; if(session->aob.framebufoff == session->aob.framebuflen) { /* Frame has completely sent */ r = nghttp2_session_after_frame_sent(session); diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index a96498fd..458e229d 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -68,6 +68,7 @@ typedef struct { int data_chunk_recv_cb_called; int data_recv_cb_called; const nghttp2_frame *frame; + size_t fixed_sendlen; } my_user_data; static void scripted_data_feed_init(scripted_data_feed *df, @@ -94,6 +95,15 @@ static ssize_t fail_send_callback(nghttp2_session *session, return NGHTTP2_ERR_CALLBACK_FAILURE; } +static ssize_t fixed_bytes_send_callback(nghttp2_session *session, + const uint8_t *data, size_t len, + int flags, void *user_data) +{ + size_t fixed_sendlen = ((my_user_data*)user_data)->fixed_sendlen; + return fixed_sendlen < len ? fixed_sendlen : len; +} + + static ssize_t scripted_recv_callback(nghttp2_session *session, uint8_t* data, size_t len, int flags, void *user_data) @@ -2979,12 +2989,15 @@ void test_nghttp2_session_flow_control(void) nghttp2_frame settings_frame; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); - callbacks.send_callback = null_send_callback; + callbacks.send_callback = fixed_bytes_send_callback; callbacks.on_frame_send_callback = on_frame_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.frame_send_cb_called = 0; ud.data_source_length = 128*1024; + /* Use smaller emission count so that we can check outbound flow + control window calculation is correct. */ + ud.fixed_sendlen = 2*1024; /* Initial window size to 64KiB - 1*/ nghttp2_session_client_new(&session, &callbacks, &ud);