Implement SETTINGS_MAX_FRAME_SIZE and SETTINGS_MAX_HEADER_LIST_SIZE

This commit is contained in:
Tatsuhiro Tsujikawa 2014-07-27 16:58:04 +09:00
parent c4be7d48a0
commit 77374ac6e2
9 changed files with 143 additions and 3 deletions

View File

@ -510,7 +510,15 @@ typedef enum {
/** /**
* SETTINGS_INITIAL_WINDOW_SIZE * SETTINGS_INITIAL_WINDOW_SIZE
*/ */
NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04 NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x04,
/**
* SETTINGS_MAX_FRAME_SIZE
*/
NGHTTP2_SETTINGS_MAX_FRAME_SIZE = 0x05,
/**
* SETTINGS_MAX_HEADER_SET_SIZE
*/
NGHTTP2_SETTINGS_MAX_HEADER_SET_SIZE = 0x06
} nghttp2_settings_id; } nghttp2_settings_id;
/* Note: If we add SETTINGS, update the capacity of /* Note: If we add SETTINGS, update the capacity of
NGHTTP2_INBOUND_NUM_IV as well */ NGHTTP2_INBOUND_NUM_IV as well */

View File

@ -1037,6 +1037,14 @@ int nghttp2_iv_check(const nghttp2_settings_entry *iv, size_t niv)
return 0; return 0;
} }
break; break;
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
if(iv[i].value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
iv[i].value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
return 0;
}
break;
case NGHTTP2_SETTINGS_MAX_HEADER_SET_SIZE:
break;
default: default:
return 0; return 0;
} }

View File

@ -33,7 +33,6 @@
#include "nghttp2_hd.h" #include "nghttp2_hd.h"
#include "nghttp2_buf.h" #include "nghttp2_buf.h"
#define NGHTTP2_FRAME_LENGTH_MASK ((1 << 24) - 1)
#define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1) #define NGHTTP2_STREAM_ID_MASK ((1u << 31) - 1)
#define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1) #define NGHTTP2_PRI_GROUP_ID_MASK ((1u << 31) - 1)
#define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1) #define NGHTTP2_PRIORITY_MASK ((1u << 31) - 1)
@ -43,6 +42,9 @@
/* The number of bytes of frame header. */ /* The number of bytes of frame header. */
#define NGHTTP2_FRAME_HDLEN 9 #define NGHTTP2_FRAME_HDLEN 9
#define NGHTTP2_MAX_FRAME_SIZE_MAX ((1 << 24) - 1)
#define NGHTTP2_MAX_FRAME_SIZE_MIN (1 << 14)
#define NGHTTP2_MAX_PAYLOADLEN 16384 #define NGHTTP2_MAX_PAYLOADLEN 16384
/* The one frame buffer length for tranmission. We may use several of /* The one frame buffer length for tranmission. We may use several of
them to support CONTINUATION. To account for Pad Length field, we them to support CONTINUATION. To account for Pad Length field, we

View File

@ -292,6 +292,8 @@ static void init_settings(nghttp2_settings_storage *settings)
settings->enable_push = 1; settings->enable_push = 1;
settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS; settings->max_concurrent_streams = NGHTTP2_INITIAL_MAX_CONCURRENT_STREAMS;
settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE; settings->initial_window_size = NGHTTP2_INITIAL_WINDOW_SIZE;
settings->max_frame_size = NGHTTP2_MAX_FRAME_SIZE_MIN;
settings->max_header_set_size = UINT32_MAX;
} }
static void active_outbound_item_reset(nghttp2_active_outbound_item *aob) static void active_outbound_item_reset(nghttp2_active_outbound_item *aob)
@ -3273,6 +3275,12 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
session->local_settings.initial_window_size = iv[i].value; session->local_settings.initial_window_size = iv[i].value;
break; break;
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
session->local_settings.max_frame_size = iv[i].value;
break;
case NGHTTP2_SETTINGS_MAX_HEADER_SET_SIZE:
session->local_settings.max_header_set_size = iv[i].value;
break;
} }
} }
@ -3393,6 +3401,23 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
session->remote_settings.initial_window_size = entry->value; session->remote_settings.initial_window_size = entry->value;
break;
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
if(entry->value < NGHTTP2_MAX_FRAME_SIZE_MIN ||
entry->value > NGHTTP2_MAX_FRAME_SIZE_MAX) {
return session_handle_invalid_connection
(session, frame, NGHTTP2_PROTOCOL_ERROR,
"SETTINGS: invalid SETTINGS_MAX_FRAME_SIZE");
}
session->remote_settings.max_frame_size = entry->value;
break;
case NGHTTP2_SETTINGS_MAX_HEADER_SET_SIZE:
session->remote_settings.max_header_set_size = entry->value;
break; break;
} }
} }
@ -4141,6 +4166,8 @@ static void inbound_frame_set_settings_entry(nghttp2_inbound_frame *iframe)
case NGHTTP2_SETTINGS_ENABLE_PUSH: case NGHTTP2_SETTINGS_ENABLE_PUSH:
case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS: case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
case NGHTTP2_SETTINGS_MAX_HEADER_SET_SIZE:
break; break;
default: default:
DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n", DEBUGF(fprintf(stderr, "recv: ignore unknown settings id=0x%02x\n",
@ -4273,6 +4300,26 @@ ssize_t nghttp2_session_mem_recv(nghttp2_session *session,
iframe->frame.hd.flags, iframe->frame.hd.flags,
iframe->frame.hd.stream_id)); iframe->frame.hd.stream_id));
if(iframe->frame.hd.length > session->local_settings.max_frame_size) {
DEBUGF(fprintf(stderr,
"recv: legnth is too large %u > %u\n",
iframe->frame.hd.length,
session->local_settings.max_frame_size));
busy = 1;
iframe->state = NGHTTP2_IB_IGN_PAYLOAD;
rv = nghttp2_session_terminate_session_with_reason
(session, NGHTTP2_PROTOCOL_ERROR, "too large frame size");
if(nghttp2_is_fatal(rv)) {
return rv;
}
break;
}
switch(iframe->frame.hd.type) { switch(iframe->frame.hd.type) {
case NGHTTP2_DATA: { case NGHTTP2_DATA: {
DEBUGF(fprintf(stderr, "recv: DATA\n")); DEBUGF(fprintf(stderr, "recv: DATA\n"));
@ -5688,6 +5735,10 @@ uint32_t nghttp2_session_get_remote_settings(nghttp2_session *session,
return session->remote_settings.max_concurrent_streams; return session->remote_settings.max_concurrent_streams;
case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE: case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
return session->remote_settings.initial_window_size; return session->remote_settings.initial_window_size;
case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
return session->remote_settings.max_frame_size;
case NGHTTP2_SETTINGS_MAX_HEADER_SET_SIZE:
return session->remote_settings.max_header_set_size;
} }
assert(0); assert(0);

View File

@ -80,7 +80,7 @@ typedef enum {
NGHTTP2_IB_IGN_DATA NGHTTP2_IB_IGN_DATA
} nghttp2_inbound_state; } nghttp2_inbound_state;
#define NGHTTP2_INBOUND_NUM_IV 5 #define NGHTTP2_INBOUND_NUM_IV 7
typedef struct { typedef struct {
nghttp2_frame frame; nghttp2_frame frame;
@ -113,6 +113,8 @@ typedef struct {
uint32_t enable_push; uint32_t enable_push;
uint32_t max_concurrent_streams; uint32_t max_concurrent_streams;
uint32_t initial_window_size; uint32_t initial_window_size;
uint32_t max_frame_size;
uint32_t max_header_set_size;
} nghttp2_settings_storage; } nghttp2_settings_storage;
typedef enum { typedef enum {

View File

@ -95,6 +95,8 @@ int main(int argc, char* argv[])
test_nghttp2_session_recv_unexpected_continuation) || test_nghttp2_session_recv_unexpected_continuation) ||
!CU_add_test(pSuite, "session_recv_settings_header_table_size", !CU_add_test(pSuite, "session_recv_settings_header_table_size",
test_nghttp2_session_recv_settings_header_table_size) || test_nghttp2_session_recv_settings_header_table_size) ||
!CU_add_test(pSuite, "session_recv_too_large_frame_length",
test_nghttp2_session_recv_too_large_frame_length) ||
!CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) || !CU_add_test(pSuite, "session_continue", test_nghttp2_session_continue) ||
!CU_add_test(pSuite, "session_add_frame", !CU_add_test(pSuite, "session_add_frame",
test_nghttp2_session_add_frame) || test_nghttp2_session_add_frame) ||

View File

@ -709,4 +709,21 @@ void test_nghttp2_iv_check(void)
iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[1].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
iv[1].value = UINT32_MAX; iv[1].value = UINT32_MAX;
CU_ASSERT(!nghttp2_iv_check(iv, 2)); CU_ASSERT(!nghttp2_iv_check(iv, 2));
/* Too small SETTINGS_MAX_FRAME_SIZE */
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN - 1;
CU_ASSERT(!nghttp2_iv_check(iv, 1));
/* Too large SETTINGS_MAX_FRAME_SIZE */
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
CU_ASSERT(!nghttp2_iv_check(iv, 1));
/* Max and min SETTINGS_MAX_FRAME_SIZE */
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MIN;
iv[1].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
iv[1].value = NGHTTP2_MAX_FRAME_SIZE_MAX;
CU_ASSERT(nghttp2_iv_check(iv, 2));
} }

View File

@ -1410,6 +1410,36 @@ void test_nghttp2_session_recv_settings_header_table_size(void)
nghttp2_session_del(session); nghttp2_session_del(session);
} }
void test_nghttp2_session_recv_too_large_frame_length(void)
{
nghttp2_session *session;
nghttp2_session_callbacks callbacks;
uint8_t buf[NGHTTP2_FRAME_HDLEN];
nghttp2_outbound_item *item;
nghttp2_frame_hd hd = {
/* Initial max frame size is NGHTTP2_MAX_FRAME_SIZE_MIN */
NGHTTP2_MAX_FRAME_SIZE_MIN + 1,
1,
NGHTTP2_HEADERS,
NGHTTP2_FLAG_NONE
};
memset(&callbacks, 0, sizeof(nghttp2_session_callbacks));
nghttp2_session_server_new(&session, &callbacks, NULL);
nghttp2_frame_pack_frame_hd(buf, &hd);
CU_ASSERT(sizeof(buf) ==
nghttp2_session_mem_recv(session, buf, sizeof(buf)));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(item != NULL);
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
nghttp2_session_del(session);
}
void test_nghttp2_session_continue(void) void test_nghttp2_session_continue(void)
{ {
nghttp2_session *session; nghttp2_session *session;
@ -2162,6 +2192,25 @@ void test_nghttp2_session_on_settings_received(void)
nghttp2_frame_settings_free(&frame.settings); nghttp2_frame_settings_free(&frame.settings);
nghttp2_session_del(session); nghttp2_session_del(session);
/* Check too large SETTINGS_MAX_FRAME_SIZE */
nghttp2_session_server_new(&session, &callbacks, NULL);
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_FRAME_SIZE;
iv[0].value = NGHTTP2_MAX_FRAME_SIZE_MAX + 1;
nghttp2_frame_settings_init(&frame.settings, NGHTTP2_FLAG_NONE,
dup_iv(iv, 1), 1);
CU_ASSERT(0 == nghttp2_session_on_settings_received(session, &frame, 0));
item = nghttp2_session_get_next_ob_item(session);
CU_ASSERT(item != NULL);
CU_ASSERT(NGHTTP2_GOAWAY == OB_CTRL_TYPE(item));
nghttp2_frame_settings_free(&frame.settings);
nghttp2_session_del(session);
} }
void test_nghttp2_session_on_push_promise_received(void) void test_nghttp2_session_on_push_promise_received(void)

View File

@ -37,6 +37,7 @@ void test_nghttp2_session_recv_altsvc(void);
void test_nghttp2_session_recv_unknown_frame(void); void test_nghttp2_session_recv_unknown_frame(void);
void test_nghttp2_session_recv_unexpected_continuation(void); void test_nghttp2_session_recv_unexpected_continuation(void);
void test_nghttp2_session_recv_settings_header_table_size(void); void test_nghttp2_session_recv_settings_header_table_size(void);
void test_nghttp2_session_recv_too_large_frame_length(void);
void test_nghttp2_session_continue(void); void test_nghttp2_session_continue(void);
void test_nghttp2_session_add_frame(void); void test_nghttp2_session_add_frame(void);
void test_nghttp2_session_on_request_headers_received(void); void test_nghttp2_session_on_request_headers_received(void);