inflate_header_block: Terminate session on compression error

Code cleanup is done as well
This commit is contained in:
Tatsuhiro Tsujikawa 2014-01-17 10:13:33 +09:00
parent 1daf6de102
commit 5d535766bf
2 changed files with 57 additions and 28 deletions

View File

@ -1873,6 +1873,25 @@ static int nghttp2_session_validate_request_headers(nghttp2_session *session,
return 0; return 0;
} }
/*
* Inflates header block in session->iframe.buf. The offset inside the
* buffer must be initialized before this call. 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 and
* on_end_headers_callback are not invoked and the function never
* return NGHTTP2_ERR_PAUSE.
*
* This function return 0 if it succeeds, or one of the negative error
* codes:
*
* NGHTTP2_ERR_CALLBACK_FAILURE
* The callback function failed.
* NGHTTP2_ERR_NOMEM
* Out of memory.
* NGHTTP2_ERR_PAUSE
* The callback function returned NGHTTP2_ERR_PAUSE
*/
static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame, static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
int call_header_cb) int call_header_cb)
{ {
@ -1889,8 +1908,15 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
return rv; return rv;
} }
if(rv < 0) { if(rv < 0) {
return session_call_on_end_headers(session, frame, if(call_header_cb) {
rv = session_call_on_end_headers(session, frame,
NGHTTP2_COMPRESSION_ERROR); NGHTTP2_COMPRESSION_ERROR);
if(rv != 0) {
return rv;
}
}
return nghttp2_session_terminate_session(session,
NGHTTP2_COMPRESSION_ERROR);
} }
session->iframe.inflate_offset += rv; session->iframe.inflate_offset += rv;
if(final) { if(final) {
@ -1905,7 +1931,10 @@ static int inflate_header_block(nghttp2_session *session, nghttp2_frame *frame,
} }
} }
nghttp2_hd_inflate_end_headers(&session->hd_inflater); nghttp2_hd_inflate_end_headers(&session->hd_inflater);
return session_call_on_end_headers(session, frame, NGHTTP2_NO_ERROR); if(call_header_cb) {
return session_call_on_end_headers(session, frame, NGHTTP2_NO_ERROR);
}
return 0;
} }
static int nghttp2_session_handle_parse_error(nghttp2_session *session, static int nghttp2_session_handle_parse_error(nghttp2_session *session,
@ -1962,14 +1991,25 @@ static size_t get_payload_nv_offset(nghttp2_frame *frame)
return 0; return 0;
} }
/*
* Inflates header block without invoking on_header_callback and
* on_end_headers_callback. The initial offset inside
* session->iframe.buf is calculated based on |frame|.
*/
static int session_skip_inflate_header_block(nghttp2_session *session,
nghttp2_frame *frame)
{
session->iframe.inflate_offset = get_payload_nv_offset(frame);
return inflate_header_block(session, frame, 0);
}
static int nghttp2_session_inflate_handle_invalid_stream static int nghttp2_session_inflate_handle_invalid_stream
(nghttp2_session *session, (nghttp2_session *session,
nghttp2_frame *frame, nghttp2_frame *frame,
nghttp2_error_code error_code) nghttp2_error_code error_code)
{ {
int rv; int rv;
session->iframe.inflate_offset = get_payload_nv_offset(frame); rv = session_skip_inflate_header_block(session, frame);
rv = inflate_header_block(session, frame, 0);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
@ -1999,8 +2039,7 @@ static int nghttp2_session_inflate_handle_invalid_connection
nghttp2_error_code error_code) nghttp2_error_code error_code)
{ {
int rv; int rv;
session->iframe.inflate_offset = get_payload_nv_offset(frame); rv = session_skip_inflate_header_block(session, frame);
rv = inflate_header_block(session, frame, 0);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
@ -2094,9 +2133,7 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
} }
if(session->goaway_flags) { if(session->goaway_flags) {
/* We don't accept new stream after GOAWAY is sent or received. */ /* We don't accept new stream after GOAWAY is sent or received. */
session->iframe.inflate_offset = return session_skip_inflate_header_block(session, frame);
nghttp2_frame_headers_payload_nv_offset(&frame->headers);
return inflate_header_block(session, frame, 0);
} }
if(!nghttp2_session_is_new_peer_stream_id(session, frame->hd.stream_id)) { if(!nghttp2_session_is_new_peer_stream_id(session, frame->hd.stream_id)) {
/* The spec says if an endpoint receives a HEADERS with invalid /* The spec says if an endpoint receives a HEADERS with invalid
@ -2189,9 +2226,7 @@ int nghttp2_session_on_push_response_headers_received(nghttp2_session *session,
} }
if(session->goaway_flags) { if(session->goaway_flags) {
/* We don't accept new stream after GOAWAY is sent or received. */ /* We don't accept new stream after GOAWAY is sent or received. */
session->iframe.inflate_offset = return session_skip_inflate_header_block(session, frame);
nghttp2_frame_headers_payload_nv_offset(&frame->headers);
return inflate_header_block(session, frame, 0);
} }
rv = nghttp2_session_validate_request_headers(session, &frame->headers); rv = nghttp2_session_validate_request_headers(session, &frame->headers);
if(rv != 0) { if(rv != 0) {
@ -2253,9 +2288,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
/* This is race condition. NGHTTP2_STREAM_CLOSING indicates /* This is race condition. NGHTTP2_STREAM_CLOSING indicates
that we queued RST_STREAM but it has not been sent. It will that we queued RST_STREAM but it has not been sent. It will
eventually sent, so we just ignore this frame. */ eventually sent, so we just ignore this frame. */
session->iframe.inflate_offset = return session_skip_inflate_header_block(session, frame);
nghttp2_frame_headers_payload_nv_offset(&frame->headers);
return inflate_header_block(session, frame, 0);
} else { } else {
return nghttp2_session_inflate_handle_invalid_stream return nghttp2_session_inflate_handle_invalid_stream
(session, frame, NGHTTP2_PROTOCOL_ERROR); (session, frame, NGHTTP2_PROTOCOL_ERROR);
@ -2274,9 +2307,7 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
nghttp2_frame_headers_payload_nv_offset(&frame->headers); nghttp2_frame_headers_payload_nv_offset(&frame->headers);
return session_end_headers_received(session, frame); return session_end_headers_received(session, frame);
} }
session->iframe.inflate_offset = return session_skip_inflate_header_block(session, frame);
nghttp2_frame_headers_payload_nv_offset(&frame->headers);
return inflate_header_block(session, frame, 0);
} }
} }
@ -2728,8 +2759,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
if(session->goaway_flags) { if(session->goaway_flags) {
/* We just dicard PUSH_PROMISE after GOAWAY is sent or /* We just dicard PUSH_PROMISE after GOAWAY is sent or
received. */ received. */
session->iframe.inflate_offset = 4; return session_skip_inflate_header_block(session, frame);
return inflate_header_block(session, frame, 0);
} }
if(!nghttp2_session_is_new_peer_stream_id if(!nghttp2_session_is_new_peer_stream_id
(session, frame->push_promise.promised_stream_id)) { (session, frame->push_promise.promised_stream_id)) {
@ -2742,8 +2772,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
session->last_recv_stream_id = frame->push_promise.promised_stream_id; session->last_recv_stream_id = frame->push_promise.promised_stream_id;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(!stream) { if(!stream) {
session->iframe.inflate_offset = 4; rv = session_skip_inflate_header_block(session, frame);
rv = inflate_header_block(session, frame, 0);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
@ -2756,8 +2785,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
(session, frame, NGHTTP2_PROTOCOL_ERROR); (session, frame, NGHTTP2_PROTOCOL_ERROR);
} }
if(stream->shut_flags & NGHTTP2_SHUT_RD) { if(stream->shut_flags & NGHTTP2_SHUT_RD) {
session->iframe.inflate_offset = 4; rv = session_skip_inflate_header_block(session, frame);
rv = inflate_header_block(session, frame, 0);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }
@ -2772,8 +2800,7 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
} }
if(stream->state == NGHTTP2_STREAM_CLOSING) { if(stream->state == NGHTTP2_STREAM_CLOSING) {
session->iframe.inflate_offset = 4; rv = session_skip_inflate_header_block(session, frame);
rv = inflate_header_block(session, frame, 0);
if(rv != 0) { if(rv != 0) {
return rv; return rv;
} }

View File

@ -818,7 +818,8 @@ void test_nghttp2_session_on_request_headers_received(void)
/* More than max concurrent streams leads REFUSED_STREAM */ /* More than max concurrent streams leads REFUSED_STREAM */
session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1; session->local_settings[NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS] = 1;
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, nghttp2_frame_headers_init(&frame.headers,
NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
3, NGHTTP2_PRI_DEFAULT, NULL, 0); 3, NGHTTP2_PRI_DEFAULT, NULL, 0);
user_data.invalid_frame_recv_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));
@ -831,7 +832,8 @@ void test_nghttp2_session_on_request_headers_received(void)
/* Stream ID less than or equal to the previouly received request /* Stream ID less than or equal to the previouly received request
HEADERS leads to connection error */ HEADERS leads to connection error */
nghttp2_frame_headers_init(&frame.headers, NGHTTP2_FLAG_END_HEADERS, nghttp2_frame_headers_init(&frame.headers,
NGHTTP2_FLAG_END_HEADERS | NGHTTP2_FLAG_PRIORITY,
3, NGHTTP2_PRI_DEFAULT, NULL, 0); 3, NGHTTP2_PRI_DEFAULT, NULL, 0);
user_data.invalid_frame_recv_cb_called = 0; user_data.invalid_frame_recv_cb_called = 0;
CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame)); CU_ASSERT(0 == nghttp2_session_on_request_headers_received(session, &frame));