From 9314e30987d13250b1c3041522ad80e56625cd74 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Sun, 26 Jan 2014 22:01:27 +0900 Subject: [PATCH] Support transmission of CONTINUATION, change nghttp2_frame_hd The maximum frame size including header block is still limited to NGHTTP2_HD_MAX_BUFFER_LENGTH, which is 32KB. --- lib/includes/nghttp2/nghttp2.h | 10 ++-- lib/nghttp2_frame.c | 18 ++++-- lib/nghttp2_frame.h | 14 +++-- lib/nghttp2_hd.c | 4 +- lib/nghttp2_hd.h | 3 + lib/nghttp2_session.c | 101 ++++++++++++++++++++------------- lib/nghttp2_session.h | 3 + src/app_helper.cc | 2 +- tests/main.c | 2 + tests/nghttp2_frame_test.c | 4 +- tests/nghttp2_session_test.c | 61 +++++++++++++++++--- tests/nghttp2_session_test.h | 1 + 12 files changed, 157 insertions(+), 66 deletions(-) diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h index 46a3fbf9..038e2b3b 100644 --- a/lib/includes/nghttp2/nghttp2.h +++ b/lib/includes/nghttp2/nghttp2.h @@ -504,7 +504,11 @@ typedef struct { /** * The length field of this frame, excluding frame header. */ - uint16_t length; + size_t length; + /** + * The stream identifier (aka, stream ID) + */ + int32_t stream_id; /** * The type of this frame. See `nghttp2_frame`. */ @@ -513,10 +517,6 @@ typedef struct { * The flags. */ uint8_t flags; - /** - * The stream identifier (aka, stream ID) - */ - int32_t stream_id; } nghttp2_frame_hd; diff --git a/lib/nghttp2_frame.c b/lib/nghttp2_frame.c index 12b558d8..734528ae 100644 --- a/lib/nghttp2_frame.c +++ b/lib/nghttp2_frame.c @@ -230,7 +230,12 @@ ssize_t nghttp2_frame_pack_headers(uint8_t **buf_ptr, return rv; } framelen = rv + nv_offset; - frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH; + if(NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_FRAME_LENGTH < rv + nv_offset) { + frame->hd.length = NGHTTP2_MAX_FRAME_LENGTH; + frame->hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; + } else { + frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH; + } /* If frame->nvlen == 0, *buflen_ptr may be smaller than nv_offset */ rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, nv_offset); @@ -387,7 +392,12 @@ ssize_t nghttp2_frame_pack_push_promise(uint8_t **buf_ptr, return rv; } framelen = rv + nv_offset; - frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH; + if(NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_FRAME_LENGTH < rv + nv_offset) { + frame->hd.length = NGHTTP2_MAX_FRAME_LENGTH; + frame->hd.flags &= ~NGHTTP2_FLAG_END_HEADERS; + } else { + frame->hd.length = framelen - NGHTTP2_FRAME_HEAD_LENGTH; + } /* If frame->nvlen == 0, *buflen_ptr may be smaller than nv_offset */ rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, nv_offset); @@ -562,8 +572,8 @@ ssize_t nghttp2_nv_array_copy(nghttp2_nv **nva_ptr, size_t buflen = 0; nghttp2_nv *p; for(i = 0; i < nvlen; ++i) { - if(nva[i].namelen > NGHTTP2_MAX_HD_VALUE_LENGTH || - nva[i].valuelen > NGHTTP2_MAX_HD_VALUE_LENGTH) { + if(nva[i].namelen > NGHTTP2_HD_MAX_NAME || + nva[i].valuelen > NGHTTP2_HD_MAX_VALUE) { return NGHTTP2_ERR_INVALID_ARGUMENT; } buflen += nva[i].namelen + nva[i].valuelen; diff --git a/lib/nghttp2_frame.h b/lib/nghttp2_frame.h index b3bea76d..8b4dfa5f 100644 --- a/lib/nghttp2_frame.h +++ b/lib/nghttp2_frame.h @@ -33,10 +33,6 @@ #include "nghttp2_hd.h" #include "nghttp2_buffer.h" -/* The maximum value length of name/value pair. This is not specified - by the spec. We just chose the arbitrary size */ -#define NGHTTP2_MAX_HD_VALUE_LENGTH ((1 << 13) - 1) - #define NGHTTP2_FRAME_LENGTH_MASK ((1 << 14) - 1) #define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) #define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) @@ -99,7 +95,10 @@ size_t nghttp2_frame_headers_payload_nv_offset(nghttp2_headers *frame); * change. |*buf_ptr| and |*buflen_ptr| are updated accordingly. * * frame->hd.length is assigned after length is determined during - * packing process. + * packing process. If payload length is strictly larger than + * NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is, + * 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 if it succeeds, or * returns one of the following negative error codes: @@ -242,7 +241,10 @@ int nghttp2_frame_unpack_settings_payload2(nghttp2_settings_entry **iv_ptr, * change. |*buf_ptr| and |*buflen_ptr| are updated accordingly. * * frame->hd.length is assigned after length is determined during - * packing process. + * packing process. If payload length is strictly larger than + * NGHTTP2_MAX_FRAME_LENGTH, payload data is still serialized as is, + * 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 if it succeeds, or * returns one of the following negative error codes: diff --git a/lib/nghttp2_hd.c b/lib/nghttp2_hd.c index 98e4398f..f04af0ea 100644 --- a/lib/nghttp2_hd.c +++ b/lib/nghttp2_hd.c @@ -443,9 +443,7 @@ static int ensure_write_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, size_t offset, size_t need) { int rv; - /* TODO Remove this limitation when header continuation is - implemented. */ - if(need + offset > NGHTTP2_MAX_FRAME_LENGTH) { + if(need + offset > NGHTTP2_HD_MAX_BUFFER_LENGTH) { return NGHTTP2_ERR_HEADER_COMP; } rv = nghttp2_reserve_buffer(buf_ptr, buflen_ptr, offset + need); diff --git a/lib/nghttp2_hd.h b/lib/nghttp2_hd.h index 9863bddf..c77101e2 100644 --- a/lib/nghttp2_hd.h +++ b/lib/nghttp2_hd.h @@ -37,8 +37,11 @@ #define NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE (1 << 12) #define NGHTTP2_HD_ENTRY_OVERHEAD 32 +/* The maximum value length of name/value pair. This is not specified + by the spec. We just chose the arbitrary size */ #define NGHTTP2_HD_MAX_NAME 256 #define NGHTTP2_HD_MAX_VALUE 4096 +#define NGHTTP2_HD_MAX_BUFFER_LENGTH (1 << 15) /* Default size of maximum table buffer size for encoder. Even if remote decoder notifies larger buffer size for its decoding, diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index 385b319a..14b59dd7 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -384,7 +384,7 @@ static void nghttp2_active_outbound_item_reset nghttp2_outbound_item_free(aob->item); free(aob->item); aob->item = NULL; - aob->framebuflen = aob->framebufoff = 0; + aob->framebuflen = aob->framebufoff = aob->framebufmark = 0; } void nghttp2_session_del(nghttp2_session *session) @@ -1405,6 +1405,32 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) int r; nghttp2_frame *frame; frame = nghttp2_outbound_item_get_ctrl_frame(session->aob.item); + if(frame->hd.type == NGHTTP2_HEADERS || + frame->hd.type == NGHTTP2_PUSH_PROMISE) { + if(session->aob.framebufmark < session->aob.framebuflen) { + nghttp2_frame_hd cont_hd; + cont_hd.length = nghttp2_min(session->aob.framebuflen - + session->aob.framebufmark, + NGHTTP2_MAX_FRAME_LENGTH); + cont_hd.type = NGHTTP2_CONTINUATION; + if(cont_hd.length < NGHTTP2_MAX_FRAME_LENGTH) { + cont_hd.flags = NGHTTP2_FLAG_END_HEADERS; + } else { + cont_hd.flags = NGHTTP2_FLAG_NONE; + } + cont_hd.stream_id = frame->hd.stream_id; + nghttp2_frame_pack_frame_hd(session->aob.framebuf + + session->aob.framebufmark - + NGHTTP2_FRAME_HEAD_LENGTH, + &cont_hd); + session->aob.framebufmark += cont_hd.length; + session->aob.framebufoff -= NGHTTP2_FRAME_HEAD_LENGTH; + return 0; + } + /* Update frame payload length to include data sent in + CONTINUATION frame. */ + frame->hd.length = session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH; + } if(session->callbacks.on_frame_send_callback) { if(session->callbacks.on_frame_send_callback(session, frame, session->user_data) != 0) { @@ -1623,7 +1649,7 @@ static int nghttp2_session_after_frame_sent(nghttp2_session *session) nghttp2_active_outbound_item_reset(&session->aob); return r; } else { - session->aob.framebuflen = r; + session->aob.framebuflen = session->aob.framebufmark = r; session->aob.framebufoff = 0; } } else { @@ -1666,7 +1692,8 @@ int nghttp2_session_send(nghttp2_session *session) framebuflen = nghttp2_session_prep_frame(session, item); if(framebuflen == NGHTTP2_ERR_DEFERRED) { continue; - } else if(framebuflen < 0) { + } + if(framebuflen < 0) { /* TODO If the error comes from compressor, the connection must be closed. */ if(item->frame_cat == NGHTTP2_CAT_CTRL && @@ -1700,19 +1727,31 @@ int nghttp2_session_send(nghttp2_session *session) } session->aob.item = item; session->aob.framebuflen = framebuflen; - /* Call before_send callback */ - if(item->frame_cat == NGHTTP2_CAT_CTRL && - session->callbacks.before_frame_send_callback) { - if(session->callbacks.before_frame_send_callback - (session, - nghttp2_outbound_item_get_ctrl_frame(item), - session->user_data) != 0) { - return NGHTTP2_ERR_CALLBACK_FAILURE; + + if(item->frame_cat == NGHTTP2_CAT_CTRL) { + nghttp2_frame *frame = nghttp2_outbound_item_get_ctrl_frame(item); + session->aob.framebufmark = + frame->hd.length + NGHTTP2_FRAME_HEAD_LENGTH; + /* Call before_send callback */ + if(session->callbacks.before_frame_send_callback) { + size_t origlen = frame->hd.length; + frame->hd.length = + session->aob.framebuflen - NGHTTP2_FRAME_HEAD_LENGTH; + r = session->callbacks.before_frame_send_callback + (session, frame, session->user_data); + frame->hd.length = origlen; + if(r != 0) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } } + } else { + session->aob.framebufmark = + nghttp2_outbound_item_get_data_frame + (session->aob.item)->hd.length + NGHTTP2_FRAME_HEAD_LENGTH; } } data = session->aob.framebuf + session->aob.framebufoff; - datalen = session->aob.framebuflen - session->aob.framebufoff; + datalen = session->aob.framebufmark - session->aob.framebufoff; sentlen = session->callbacks.send_callback(session, data, datalen, 0, session->user_data); if(sentlen < 0) { @@ -1742,7 +1781,7 @@ int nghttp2_session_send(nghttp2_session *session) } } session->aob.framebufoff += sentlen; - if(session->aob.framebufoff == session->aob.framebuflen) { + if(session->aob.framebufoff == session->aob.framebufmark) { /* Frame has completely sent */ r = nghttp2_session_after_frame_sent(session); if(r < 0) { @@ -1823,6 +1862,8 @@ static int session_call_on_end_headers (nghttp2_session *session, nghttp2_frame *frame, nghttp2_error_code error_code) { int rv; + DEBUGF(fprintf(stderr, "Call on_end_headers callback stream_id=%d\n", + frame->hd.stream_id)); if(session->callbacks.on_end_headers_callback) { rv = session->callbacks.on_end_headers_callback(session, frame, error_code, session->user_data); @@ -2167,11 +2208,6 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session, return nghttp2_session_inflate_handle_invalid_connection (session, frame, NGHTTP2_PROTOCOL_ERROR); } - /* Connection error if header continuation is employed for now */ - if((frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { - return nghttp2_session_inflate_handle_invalid_connection - (session, frame, NGHTTP2_INTERNAL_ERROR); - } if(session->goaway_flags) { /* We don't accept new stream after GOAWAY is sent or received. */ return NGHTTP2_ERR_IGN_HEADER_BLOCK; @@ -2222,11 +2258,6 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session, return nghttp2_session_inflate_handle_invalid_connection (session, frame, NGHTTP2_PROTOCOL_ERROR); } - /* Connection error if header continuation is employed for now */ - if((frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { - return nghttp2_session_inflate_handle_invalid_connection - (session, frame, NGHTTP2_INTERNAL_ERROR); - } if(stream->shut_flags & NGHTTP2_SHUT_RD) { /* half closed (remote): from the spec: @@ -2255,11 +2286,6 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session, return nghttp2_session_inflate_handle_invalid_connection (session, frame, NGHTTP2_PROTOCOL_ERROR); } - /* Connection error if header continuation is employed for now */ - if((frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { - return nghttp2_session_inflate_handle_invalid_connection - (session, frame, NGHTTP2_INTERNAL_ERROR); - } if(session->goaway_flags) { /* We don't accept new stream after GOAWAY is sent or received. */ return NGHTTP2_ERR_IGN_HEADER_BLOCK; @@ -2286,11 +2312,6 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, return nghttp2_session_inflate_handle_invalid_connection (session, frame, NGHTTP2_PROTOCOL_ERROR); } - /* Connection error if header continuation is employed for now */ - if((frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) == 0) { - return nghttp2_session_inflate_handle_invalid_connection - (session, frame, NGHTTP2_INTERNAL_ERROR); - } if(stream->state == NGHTTP2_STREAM_RESERVED) { /* reserved. The valid push response HEADERS is processed by nghttp2_session_on_push_response_headers_received(). This @@ -2848,11 +2869,6 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, return nghttp2_session_inflate_handle_invalid_connection (session, frame, NGHTTP2_PROTOCOL_ERROR); } - /* Connection error if header continuation is employed for now */ - if((frame->hd.flags & NGHTTP2_FLAG_END_PUSH_PROMISE) == 0) { - return nghttp2_session_inflate_handle_invalid_connection - (session, frame, NGHTTP2_INTERNAL_ERROR); - } if(session->local_settings[NGHTTP2_SETTINGS_ENABLE_PUSH] == 0) { return nghttp2_session_inflate_handle_invalid_connection (session, frame, NGHTTP2_PROTOCOL_ERROR); @@ -3601,6 +3617,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, readlen = inbound_frame_payload_readlen(iframe, in, last); DEBUGF(fprintf(stderr, "readlen=%zu, payloadleft=%zu\n", readlen, iframe->payloadleft)); + DEBUGF(fprintf(stderr, "block final=%d\n", + (iframe->frame.hd.flags & + NGHTTP2_FLAG_END_HEADERS) && + iframe->payloadleft == readlen)); rv = inflate_header_block(session, &iframe->frame, &readlen, (uint8_t*)in, readlen, (iframe->frame.hd.flags & @@ -3730,6 +3750,10 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session, iframe->payloadleft = cont_hd.length; if(cont_hd.type != NGHTTP2_CONTINUATION || cont_hd.stream_id != iframe->frame.hd.stream_id) { + DEBUGF(fprintf(stderr, "expected stream_id=%d, type=%d, but " + "got stream_id=%d, type=%d\n", + iframe->frame.hd.stream_id, NGHTTP2_CONTINUATION, + cont_hd.stream_id, cont_hd.type)); rv = nghttp2_session_terminate_session(session, NGHTTP2_PROTOCOL_ERROR); if(nghttp2_is_fatal(rv)) { @@ -4024,6 +4048,7 @@ ssize_t nghttp2_session_pack_data(nghttp2_session *session, /* This is the error code when callback is failed. */ return NGHTTP2_ERR_CALLBACK_FAILURE; } + frame->hd.length = r; memset(*buf_ptr, 0, NGHTTP2_FRAME_HEAD_LENGTH); nghttp2_put_uint16be(&(*buf_ptr)[0], r); flags = 0; diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h index 675bb5c4..c5236a13 100644 --- a/lib/nghttp2_session.h +++ b/lib/nghttp2_session.h @@ -60,6 +60,9 @@ typedef struct { 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_active_outbound_item; /* Buffer length for inbound raw byte stream. */ diff --git a/src/app_helper.cc b/src/app_helper.cc index e383b15a..0c88b1fa 100644 --- a/src/app_helper.cc +++ b/src/app_helper.cc @@ -438,7 +438,7 @@ void print_data_frame(print_type ptype, uint16_t length, uint8_t flags, { printf("%sDATA%s frame ", frame_name_ansi_esc(ptype), ansi_escend()); - nghttp2_frame_hd hd = {length, NGHTTP2_DATA, flags, stream_id}; + nghttp2_frame_hd hd = {length, stream_id, NGHTTP2_DATA, flags}; print_frame_hd(hd); if(flags) { print_frame_attr_indent(); diff --git a/tests/main.c b/tests/main.c index e7f97334..e41b4ac0 100644 --- a/tests/main.c +++ b/tests/main.c @@ -144,6 +144,8 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "submit_headers_push_reply", test_nghttp2_submit_headers_push_reply) || !CU_add_test(pSuite, "submit_headers", test_nghttp2_submit_headers) || + !CU_add_test(pSuite, "submit_headers_continuation", + test_nghttp2_submit_headers_continuation) || !CU_add_test(pSuite, "submit_priority", test_nghttp2_submit_priority) || !CU_add_test(pSuite, "session_submit_settings", test_nghttp2_submit_settings) || diff --git a/tests/nghttp2_frame_test.c b/tests/nghttp2_frame_test.c index 6aba757f..3640f2a4 100644 --- a/tests/nghttp2_frame_test.c +++ b/tests/nghttp2_frame_test.c @@ -143,8 +143,8 @@ void test_nghttp2_frame_pack_headers_frame_too_large(void) ssize_t framelen; nghttp2_nv *nva; ssize_t nvlen; - size_t big_vallen = NGHTTP2_MAX_HD_VALUE_LENGTH; - nghttp2_nv big_hds[8]; + size_t big_vallen = NGHTTP2_HD_MAX_VALUE; + nghttp2_nv big_hds[16]; size_t big_hdslen = ARRLEN(big_hds); size_t i; diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c index 99fa5f93..8cd5cc4d 100644 --- a/tests/nghttp2_session_test.c +++ b/tests/nghttp2_session_test.c @@ -734,7 +734,7 @@ void test_nghttp2_session_recv_continuation(void) ud.header_cb_called = 0; rv = nghttp2_session_mem_recv(session, data, datalen); - CU_ASSERT(rv == datalen); + CU_ASSERT((ssize_t)datalen == rv); CU_ASSERT(2 == ud.header_cb_called); nghttp2_hd_deflate_free(&deflater); @@ -765,7 +765,7 @@ void test_nghttp2_session_recv_continuation(void) ud.end_headers_cb_called = 0; rv = nghttp2_session_mem_recv(session, data, datalen); - CU_ASSERT(datalen == rv); + CU_ASSERT((ssize_t)datalen == rv); CU_ASSERT(1 == ud.end_headers_cb_called); CU_ASSERT(NGHTTP2_GOAWAY == @@ -847,7 +847,8 @@ void test_nghttp2_session_continue(void) recv_frame = user_data.frame; CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); - CU_ASSERT(framelen1 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length); + CU_ASSERT((size_t)framelen1 - NGHTTP2_FRAME_HEAD_LENGTH == + recv_frame->hd.length); CU_ASSERT(1 == user_data.header_cb_called); CU_ASSERT(0 == user_data.end_headers_cb_called); @@ -877,7 +878,8 @@ void test_nghttp2_session_continue(void) recv_frame = user_data.frame; CU_ASSERT(NGHTTP2_HEADERS == recv_frame->hd.type); - CU_ASSERT(framelen2 - NGHTTP2_FRAME_HEAD_LENGTH == recv_frame->hd.length); + CU_ASSERT((size_t)framelen2 - NGHTTP2_FRAME_HEAD_LENGTH == + recv_frame->hd.length); CU_ASSERT(1 == user_data.header_cb_called); CU_ASSERT(1 == user_data.end_headers_cb_called); @@ -1801,7 +1803,6 @@ void test_nghttp2_session_send_headers_reply(void) nghttp2_session_del(session); } -/* TODO Rewrite this test when header continuation is supported */ void test_nghttp2_session_send_headers_header_comp_error(void) { nghttp2_session *session; @@ -1809,8 +1810,8 @@ void test_nghttp2_session_send_headers_header_comp_error(void) nghttp2_frame *frame = malloc(sizeof(nghttp2_frame)); nghttp2_nv *nva; ssize_t nvlen; - size_t vallen = NGHTTP2_MAX_HD_VALUE_LENGTH; - nghttp2_nv nv[8]; + size_t vallen = NGHTTP2_HD_MAX_VALUE; + nghttp2_nv nv[16]; size_t nnv = ARRLEN(nv); size_t i; @@ -2455,6 +2456,52 @@ void test_nghttp2_submit_headers(void) nghttp2_session_del(session); } +void test_nghttp2_submit_headers_continuation(void) +{ + nghttp2_session *session; + nghttp2_session_callbacks callbacks; + nghttp2_nv nv[] = { + MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + MAKE_NV("h1", ""), + }; + nghttp2_outbound_item *item; + uint8_t data[4096]; + size_t i; + my_user_data ud; + + memset(data, '0', sizeof(data)); + for(i = 0; i < ARRLEN(nv); ++i) { + nv[i].valuelen = sizeof(data); + nv[i].value = data; + } + + memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); + callbacks.send_callback = null_send_callback; + callbacks.on_frame_send_callback = on_frame_send_callback; + + CU_ASSERT(0 == nghttp2_session_client_new(&session, &callbacks, &ud)); + CU_ASSERT(0 == nghttp2_submit_headers(session, + NGHTTP2_FLAG_END_STREAM, + -1, NGHTTP2_PRI_DEFAULT, + nv, ARRLEN(nv), NULL)); + item = nghttp2_session_get_next_ob_item(session); + CU_ASSERT(NGHTTP2_HEADERS == OB_CTRL_TYPE(item)); + CU_ASSERT((NGHTTP2_FLAG_END_STREAM | NGHTTP2_FLAG_END_HEADERS) == + OB_CTRL(item)->hd.flags); + CU_ASSERT(NGHTTP2_PRI_DEFAULT == OB_CTRL(item)->headers.pri); + + ud.frame_send_cb_called = 0; + CU_ASSERT(0 == nghttp2_session_send(session)); + CU_ASSERT(1 == ud.frame_send_cb_called); + + nghttp2_session_del(session); +} + void test_nghttp2_submit_priority(void) { nghttp2_session *session; diff --git a/tests/nghttp2_session_test.h b/tests/nghttp2_session_test.h index 49f14328..d71cc238 100644 --- a/tests/nghttp2_session_test.h +++ b/tests/nghttp2_session_test.h @@ -62,6 +62,7 @@ void test_nghttp2_submit_headers_start_stream(void); void test_nghttp2_submit_headers_reply(void); void test_nghttp2_submit_headers_push_reply(void); void test_nghttp2_submit_headers(void); +void test_nghttp2_submit_headers_continuation(void); void test_nghttp2_submit_priority(void); void test_nghttp2_submit_settings(void); void test_nghttp2_submit_settings_update_local_window_size(void);