Connection error if payload size is strictly greater than (1 << 14) - 1

It would be desired to add option to make this limit configurable.
Fix scripted_recv_callback in tests.
This commit is contained in:
Tatsuhiro Tsujikawa 2013-08-28 02:13:57 +09:00
parent 1f3b96e233
commit b37f99ca03
4 changed files with 97 additions and 14 deletions

View File

@ -83,7 +83,13 @@ static int32_t nghttp2_pushed_stream_pri(nghttp2_stream *stream)
int nghttp2_session_fail_session(nghttp2_session *session,
nghttp2_error_code error_code)
{
if(session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) {
return 0;
}
session->goaway_flags |= NGHTTP2_GOAWAY_FAIL_ON_SEND;
if(session->goaway_flags & NGHTTP2_GOAWAY_SEND) {
return 0;
}
return nghttp2_submit_goaway(session, error_code, NULL, 0);
}
@ -2806,8 +2812,23 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
session->iframe.state = NGHTTP2_RECV_PAYLOAD;
session->iframe.payloadlen =
nghttp2_get_uint16(&session->iframe.headbuf[0]);
if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) {
/* control frame */
/* TODO Make payloadlen configurable up to
NGHTTP2_MAX_FRAME_LENGTH */
if(session->iframe.payloadlen > NGHTTP2_MAX_HTTP_FRAME_LENGTH) {
session->iframe.error_code = NGHTTP2_ERR_FRAME_TOO_LARGE;
session->iframe.state = NGHTTP2_RECV_PAYLOAD_IGN;
/* Make inflater fail forcibly to disallow reception of
further HEADERS or PUSH_PROMISE */
session->hd_inflater.bad = 1;
/* Just tear down session for now */
r = nghttp2_session_fail_session(session, NGHTTP2_FRAME_TOO_LARGE);
if(r != 0) {
/* FATAL */
assert(r < NGHTTP2_ERR_FATAL);
return r;
}
} else if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) {
/* non-DATA frame */
ssize_t buflen = session->iframe.payloadlen;
session->iframe.buflen = buflen;
r = nghttp2_reserve_buffer(&session->iframe.buf,
@ -2901,15 +2922,19 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
}
}
if(session->iframe.payloadlen == session->iframe.off) {
if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) {
r = nghttp2_session_process_ctrl_frame(session);
} else {
r = nghttp2_session_process_data_frame(session);
}
if(r < 0) {
/* FATAL */
assert(r < NGHTTP2_ERR_FATAL);
return r;
if(session->iframe.error_code != NGHTTP2_ERR_FRAME_TOO_LARGE) {
if(!nghttp2_frame_is_data_frame(session->iframe.headbuf)) {
/* TODO Introduce callback which is invoked when payload is
ignored, especially for frame too large */
r = nghttp2_session_process_ctrl_frame(session);
} else {
r = nghttp2_session_process_data_frame(session);
}
if(r < 0) {
/* FATAL */
assert(r < NGHTTP2_ERR_FATAL);
return r;
}
}
nghttp2_inbound_frame_reset(&session->iframe);
}

View File

@ -84,6 +84,8 @@ int main(int argc, char* argv[])
test_nghttp2_session_recv_eof) ||
!CU_add_test(pSuite, "session_recv_data",
test_nghttp2_session_recv_data) ||
!CU_add_test(pSuite, "session_recv_frame_too_large",
test_nghttp2_session_recv_frame_too_large) ||
!CU_add_test(pSuite, "session_add_frame",
test_nghttp2_session_add_frame) ||
!CU_add_test(pSuite, "session_on_request_headers_received",

View File

@ -101,10 +101,9 @@ static ssize_t scripted_recv_callback(nghttp2_session *session,
size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx];
memcpy(data, df->datamark, wlen);
df->datamark += wlen;
if(wlen <= len) {
df->feedseq[df->seqidx] -= wlen;
if(df->feedseq[df->seqidx] == 0) {
++df->seqidx;
} else {
df->feedseq[df->seqidx] -= wlen;
}
return wlen;
}
@ -282,6 +281,62 @@ static const char *null_val_nv[] = { "Version", "HTTP/1.1",
"Foo", NULL,
NULL };
void test_nghttp2_session_recv_frame_too_large(void)
{
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
scripted_data_feed df;
my_user_data user_data;
uint8_t data[NGHTTP2_FRAME_HEAD_LENGTH + NGHTTP2_MAX_HTTP_FRAME_LENGTH + 1];
nghttp2_frame_hd hd;
nghttp2_outbound_item *item;
nghttp2_frame frame;
uint8_t *framebuf = NULL;
size_t framebuflen = 0;
size_t framelen;
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_client_new(&session, &callbacks, &user_data);
nghttp2_session_open_stream(session, 1, NGHTTP2_FLAG_NONE,
NGHTTP2_PRI_DEFAULT, NGHTTP2_STREAM_OPENED,
NULL);
memset(data, 0, sizeof(data));
hd.length = NGHTTP2_MAX_HTTP_FRAME_LENGTH + 1;
hd.type = NGHTTP2_DATA;
hd.flags = NGHTTP2_FLAG_END_STREAM;
hd.stream_id = 1;
nghttp2_frame_pack_frame_hd(data, &hd);
scripted_data_feed_init(&df, data, sizeof(data));
CU_ASSERT(0 == nghttp2_session_recv(session));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(item != NULL);
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
CU_ASSERT(NGHTTP2_FRAME_TOO_LARGE == OB_CTRL(item)->goaway.error_code);
/* Check next frame can be received */
nghttp2_frame_ping_init(&frame.ping, NGHTTP2_FLAG_NONE, NULL);
framelen = nghttp2_frame_pack_ping(&framebuf, &framebuflen, &frame.ping);
nghttp2_frame_ping_free(&frame.ping);
scripted_data_feed_init(&df, framebuf, framelen);
user_data.frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_recv(session));
CU_ASSERT(1 == user_data.frame_recv_cb_called);
free(framebuf);
nghttp2_session_del(session);
}
void test_nghttp2_session_recv(void)
{
nghttp2_session *session;

View File

@ -30,6 +30,7 @@ void test_nghttp2_session_recv_invalid_stream_id(void);
void test_nghttp2_session_recv_invalid_frame(void);
void test_nghttp2_session_recv_eof(void);
void test_nghttp2_session_recv_data(void);
void test_nghttp2_session_recv_frame_too_large(void);
void test_nghttp2_session_add_frame(void);
void test_nghttp2_session_on_request_headers_received(void);
void test_nghttp2_session_on_response_headers_received(void);