Refactor deeply nested if blocks

This commit is contained in:
Tatsuhiro Tsujikawa 2013-09-05 23:17:16 +09:00
parent 50dd5074a5
commit 464f141593
1 changed files with 285 additions and 329 deletions

View File

@ -540,17 +540,19 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
nghttp2_error_code error_code) nghttp2_error_code error_code)
{ {
nghttp2_stream *stream = nghttp2_session_get_stream(session, stream_id); nghttp2_stream *stream = nghttp2_session_get_stream(session, stream_id);
if(stream) { if(!stream) {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
/* We call on_stream_close_callback even if stream->state is /* We call on_stream_close_callback even if stream->state is
NGHTTP2_STREAM_INITIAL. This will happen while sending request NGHTTP2_STREAM_INITIAL. This will happen while sending request
HEADERS, a local endpoint receives RST_STREAM for that HEADERS, a local endpoint receives RST_STREAM for that stream. It
stream. It may be PROTOCOL_ERROR, but without notifying stream may be PROTOCOL_ERROR, but without notifying stream closure will
closure will hang the stream in a local endpoint. hang the stream in a local endpoint.
*/ */
if(stream->state != NGHTTP2_STREAM_RESERVED &&
/* TODO Should on_stream_close_callback be called against /* TODO Should on_stream_close_callback be called against
NGHTTP2_STREAM_RESERVED? It is actually not opened yet. */ NGHTTP2_STREAM_RESERVED? It is actually not opened yet. */
session->callbacks.on_stream_close_callback) { if(stream->state != NGHTTP2_STREAM_RESERVED) {
if(session->callbacks.on_stream_close_callback) {
if(session->callbacks.on_stream_close_callback if(session->callbacks.on_stream_close_callback
(session, stream_id, (session, stream_id,
error_code, error_code,
@ -558,7 +560,6 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} }
if(stream->state != NGHTTP2_STREAM_RESERVED) {
if(nghttp2_session_is_my_stream_id(session, stream_id)) { if(nghttp2_session_is_my_stream_id(session, stream_id)) {
--session->num_outgoing_streams; --session->num_outgoing_streams;
} else { } else {
@ -569,9 +570,6 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id,
nghttp2_stream_free(stream); nghttp2_stream_free(stream);
free(stream); free(stream);
return 0; return 0;
} else {
return NGHTTP2_ERR_INVALID_ARGUMENT;
}
} }
/* /*
@ -678,8 +676,7 @@ static int nghttp2_session_predicate_response_headers_send
} }
if(nghttp2_session_is_my_stream_id(session, stream_id)) { if(nghttp2_session_is_my_stream_id(session, stream_id)) {
return NGHTTP2_ERR_INVALID_STREAM_ID; return NGHTTP2_ERR_INVALID_STREAM_ID;
} else { } else if(stream->state == NGHTTP2_STREAM_OPENING) {
if(stream->state == NGHTTP2_STREAM_OPENING) {
return 0; return 0;
} else if(stream->state == NGHTTP2_STREAM_CLOSING) { } else if(stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
@ -687,7 +684,6 @@ static int nghttp2_session_predicate_response_headers_send
return NGHTTP2_ERR_INVALID_STREAM_STATE; return NGHTTP2_ERR_INVALID_STREAM_STATE;
} }
} }
}
/* /*
* This function checks HEADERS for reserved stream can be sent. The * This function checks HEADERS for reserved stream can be sent. The
@ -758,8 +754,7 @@ static int nghttp2_session_predicate_stream_frame_send
} else { } else {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
} }
} else { } else if(stream->state == NGHTTP2_STREAM_OPENED) {
if(stream->state == NGHTTP2_STREAM_OPENED) {
return 0; return 0;
} else if(stream->state == NGHTTP2_STREAM_CLOSING) { } else if(stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
@ -767,7 +762,6 @@ static int nghttp2_session_predicate_stream_frame_send
return NGHTTP2_ERR_INVALID_STREAM_STATE; return NGHTTP2_ERR_INVALID_STREAM_STATE;
} }
} }
}
/* /*
* This function checks HEADERS, which is neither stream-opening nor * This function checks HEADERS, which is neither stream-opening nor
@ -895,24 +889,24 @@ static int nghttp2_session_predicate_window_update_send
static size_t nghttp2_session_next_data_read(nghttp2_session *session, static size_t nghttp2_session_next_data_read(nghttp2_session *session,
nghttp2_stream *stream) nghttp2_stream *stream)
{ {
/* TODO implement connection-level flow control here */ int32_t session_window_size;
int32_t stream_window_size;
int32_t window_size;
/* Take into account both connection-level flow control here */
if(session->remote_flow_control == 0 && stream->remote_flow_control == 0) { if(session->remote_flow_control == 0 && stream->remote_flow_control == 0) {
return NGHTTP2_DATA_PAYLOAD_LENGTH; return NGHTTP2_DATA_PAYLOAD_LENGTH;
} else { }
int32_t session_window_size = session_window_size =
session->remote_flow_control ? session->remote_window_size : INT32_MAX; session->remote_flow_control ? session->remote_window_size : INT32_MAX;
int32_t stream_window_size = stream_window_size =
stream->remote_flow_control ? stream->remote_window_size : INT32_MAX; stream->remote_flow_control ? stream->remote_window_size : INT32_MAX;
int32_t window_size = nghttp2_min(session_window_size, window_size = nghttp2_min(session_window_size, stream_window_size);
stream_window_size);
if(window_size > 0) { if(window_size > 0) {
return window_size < NGHTTP2_DATA_PAYLOAD_LENGTH ? return nghttp2_min(window_size, NGHTTP2_DATA_PAYLOAD_LENGTH);
window_size : NGHTTP2_DATA_PAYLOAD_LENGTH;
} else { } else {
return 0; return 0;
} }
} }
}
/* /*
* This function checks DATA with the stream ID |stream_id| can be * This function checks DATA with the stream ID |stream_id| can be
@ -963,8 +957,7 @@ static int nghttp2_session_predicate_data_send(nghttp2_session *session,
} else { } else {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
} }
} else { } else if(stream->state == NGHTTP2_STREAM_OPENED) {
if(stream->state == NGHTTP2_STREAM_OPENED) {
return 0; return 0;
} else if(stream->state == NGHTTP2_STREAM_CLOSING) { } else if(stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
@ -972,7 +965,6 @@ static int nghttp2_session_predicate_data_send(nghttp2_session *session,
return NGHTTP2_ERR_INVALID_STREAM_STATE; return NGHTTP2_ERR_INVALID_STREAM_STATE;
} }
} }
}
static ssize_t nghttp2_session_prep_frame(nghttp2_session *session, static ssize_t nghttp2_session_prep_frame(nghttp2_session *session,
nghttp2_outbound_item *item) nghttp2_outbound_item *item)
@ -1649,13 +1641,13 @@ static int nghttp2_session_call_on_frame_received
static int nghttp2_session_is_new_peer_stream_id(nghttp2_session *session, static int nghttp2_session_is_new_peer_stream_id(nghttp2_session *session,
int32_t stream_id) int32_t stream_id)
{ {
if(stream_id == 0) { if(stream_id == 0 || session->last_recv_stream_id >= stream_id) {
return 0; return 0;
} }
if(session->server) { if(session->server) {
return stream_id % 2 == 1 && session->last_recv_stream_id < stream_id; return stream_id % 2 == 1;
} else { } else {
return stream_id % 2 == 0 && session->last_recv_stream_id < stream_id; return stream_id % 2 == 0;
} }
} }
@ -1740,8 +1732,9 @@ static int nghttp2_session_handle_invalid_connection
int nghttp2_session_on_request_headers_received(nghttp2_session *session, int nghttp2_session_on_request_headers_received(nghttp2_session *session,
nghttp2_frame *frame) nghttp2_frame *frame)
{ {
int r = 0; int rv = 0;
nghttp2_error_code error_code = NGHTTP2_NO_ERROR; nghttp2_error_code error_code;
nghttp2_stream *stream;
if(frame->hd.stream_id == 0) { if(frame->hd.stream_id == 0) {
return nghttp2_session_handle_invalid_connection(session, frame, return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
@ -1759,16 +1752,16 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
/* The spec says if an endpoint receives a HEADERS with invalid /* The spec says if an endpoint receives a HEADERS with invalid
stream ID, it MUST issue connection error with error code stream ID, it MUST issue connection error with error code
PROTOCOL_ERROR */ PROTOCOL_ERROR */
return nghttp2_session_handle_invalid_connection return nghttp2_session_handle_invalid_connection(session, frame,
(session, frame, NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
} else {
session->last_recv_stream_id = frame->hd.stream_id;
error_code = nghttp2_session_validate_request_headers
(session, &frame->headers);
} }
if(error_code == 0) { session->last_recv_stream_id = frame->hd.stream_id;
uint8_t flags = frame->hd.flags; error_code = nghttp2_session_validate_request_headers(session,
nghttp2_stream *stream; &frame->headers);
if(error_code != NGHTTP2_NO_ERROR) {
return nghttp2_session_handle_invalid_stream(session, frame, error_code);
}
stream = nghttp2_session_open_stream(session, stream = nghttp2_session_open_stream(session,
frame->hd.stream_id, frame->hd.stream_id,
frame->hd.flags, frame->hd.flags,
@ -1778,23 +1771,18 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session,
if(!stream) { if(!stream) {
return NGHTTP2_ERR_NOMEM; return NGHTTP2_ERR_NOMEM;
} }
if(flags & NGHTTP2_FLAG_END_STREAM) { if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
} }
r = nghttp2_session_call_on_frame_received(session, frame); rv = nghttp2_session_call_on_frame_received(session, frame);
if(r != 0) { if(rv != 0) {
return r; return rv;
} }
if(flags & NGHTTP2_FLAG_END_STREAM) { if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
r = nghttp2_session_call_on_request_recv(session, frame->hd.stream_id); rv = nghttp2_session_call_on_request_recv(session, frame->hd.stream_id);
if(r != 0) {
return r;
} }
} /* Here we assume that stream is not shutdown in NGHTTP2_SHUT_WR */
} else { return rv;
r = nghttp2_session_handle_invalid_stream(session, frame, error_code);
}
return r;
} }
int nghttp2_session_on_response_headers_received(nghttp2_session *session, int nghttp2_session_on_response_headers_received(nghttp2_session *session,
@ -1815,7 +1803,16 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
return nghttp2_session_handle_invalid_connection(session, frame, return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_INTERNAL_ERROR); NGHTTP2_INTERNAL_ERROR);
} }
if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { if(stream->shut_flags & NGHTTP2_SHUT_RD) {
/* half closed (remote): from the spec:
If an endpoint receives additional frames for a stream that is
in this state it MUST respond with a stream error (Section
5.4.2) of type STREAM_CLOSED.
*/
return nghttp2_session_handle_invalid_stream(session, frame,
NGHTTP2_STREAM_CLOSED);
}
stream->state = NGHTTP2_STREAM_OPENED; stream->state = NGHTTP2_STREAM_OPENED;
rv = nghttp2_session_call_on_frame_received(session, frame); rv = nghttp2_session_call_on_frame_received(session, frame);
if(rv != 0) { if(rv != 0) {
@ -1829,17 +1826,6 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session,
if(rv != 0 && nghttp2_is_fatal(rv)) { if(rv != 0 && nghttp2_is_fatal(rv)) {
return rv; return rv;
} }
rv = 0;
}
} else {
/* half closed (remote): from the spec:
If an endpoint receives additional frames for a stream that is
in this state it MUST respond with a stream error (Section
5.4.2) of type STREAM_CLOSED.
*/
return nghttp2_session_handle_invalid_stream(session, frame,
NGHTTP2_STREAM_CLOSED);
} }
return 0; return 0;
} }
@ -1877,8 +1863,6 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
nghttp2_stream *stream) nghttp2_stream *stream)
{ {
int r = 0; int r = 0;
int valid = 0;
nghttp2_error_code error_code = NGHTTP2_PROTOCOL_ERROR;
if(frame->hd.stream_id == 0) { if(frame->hd.stream_id == 0) {
return nghttp2_session_handle_invalid_connection(session, frame, return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
@ -1888,10 +1872,26 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
return nghttp2_session_handle_invalid_connection(session, frame, return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_INTERNAL_ERROR); NGHTTP2_INTERNAL_ERROR);
} }
if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { if(stream->state == NGHTTP2_STREAM_RESERVED) {
/* reserved. The valid push response HEADERS is processed by
nghttp2_session_on_push_response_headers_received(). This
generic HEADERS is called invalid cases for HEADERS against
reserved state. */
return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR);
}
if((stream->shut_flags & NGHTTP2_SHUT_RD)) {
/* half closed (remote): from the spec:
If an endpoint receives additional frames for a stream that is
in this state it MUST respond with a stream error (Section
5.4.2) of type STREAM_CLOSED.
*/
return nghttp2_session_handle_invalid_stream(session, frame,
NGHTTP2_STREAM_CLOSED);
}
if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
if(stream->state == NGHTTP2_STREAM_OPENED) { if(stream->state == NGHTTP2_STREAM_OPENED) {
valid = 1;
r = nghttp2_session_call_on_frame_received(session, frame); r = nghttp2_session_call_on_frame_received(session, frame);
if(r != 0) { if(r != 0) {
return r; return r;
@ -1902,20 +1902,22 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
if(r != 0 && nghttp2_is_fatal(r)) { if(r != 0 && nghttp2_is_fatal(r)) {
return r; return r;
} }
r = 0;
} }
return 0;
} else if(stream->state == NGHTTP2_STREAM_CLOSING) { } else if(stream->state == NGHTTP2_STREAM_CLOSING) {
/* 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. */
valid = 1; return 0;
} else {
return nghttp2_session_handle_invalid_stream(session, frame,
NGHTTP2_PROTOCOL_ERROR);
} }
} else { } else {
/* If this is remote peer initiated stream, it is OK unless it /* If this is remote peer initiated stream, it is OK unless it
has sent END_STREAM frame already. But if stream is in has sent END_STREAM frame already. But if stream is in
NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race
condition. */ condition. */
valid = 1;
if(stream->state != NGHTTP2_STREAM_CLOSING) { if(stream->state != NGHTTP2_STREAM_CLOSING) {
r = nghttp2_session_call_on_frame_received(session, frame); r = nghttp2_session_call_on_frame_received(session, frame);
if(r != 0) { if(r != 0) {
@ -1932,54 +1934,30 @@ int nghttp2_session_on_headers_received(nghttp2_session *session,
if(r != 0 && nghttp2_is_fatal(r)) { if(r != 0 && nghttp2_is_fatal(r)) {
return r; return r;
} }
r = 0;
} }
} }
return 0;
} }
} else if(stream->state == NGHTTP2_STREAM_RESERVED) {
/* reserved (local). The valid push response HEADERS is processed
by nghttp2_session_on_push_response_headers_received(). This
generic HEADERS is called invalid cases for HEADERS against
reserved state. */
return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR);
} else {
/* half closed (remote): from the spec:
If an endpoint receives additional frames for a stream that is
in this state it MUST respond with a stream error (Section
5.4.2) of type STREAM_CLOSED.
*/
error_code = NGHTTP2_STREAM_CLOSED;
}
if(!valid) {
r = nghttp2_session_handle_invalid_stream(session, frame, error_code);
}
return r;
} }
int nghttp2_session_on_priority_received(nghttp2_session *session, int nghttp2_session_on_priority_received(nghttp2_session *session,
nghttp2_frame *frame) nghttp2_frame *frame)
{ {
int rv;
nghttp2_stream *stream; nghttp2_stream *stream;
if(frame->hd.stream_id == 0) { if(frame->hd.stream_id == 0) {
return nghttp2_session_handle_invalid_connection(session, frame, return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
} }
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(stream) { if(!stream) {
return 0;
}
/* Only update priority on server side for now */ /* Only update priority on server side for now */
if(session->server) { if(session->server) {
nghttp2_session_reprioritize_stream(session, stream, nghttp2_session_reprioritize_stream(session, stream,
frame->priority.pri); frame->priority.pri);
} }
rv = nghttp2_session_call_on_frame_received(session, frame); return nghttp2_session_call_on_frame_received(session, frame);
if(rv != 0) {
return rv;
}
}
return 0;
} }
int nghttp2_session_on_rst_stream_received(nghttp2_session *session, int nghttp2_session_on_rst_stream_received(nghttp2_session *session,
@ -2026,13 +2004,12 @@ static int nghttp2_update_remote_initial_window_size_func
(arg->session->remote_flow_control == 0 || (arg->session->remote_flow_control == 0 ||
arg->session->remote_window_size > 0)) { arg->session->remote_window_size > 0)) {
rv = nghttp2_pq_push(&arg->session->ob_pq, stream->deferred_data); rv = nghttp2_pq_push(&arg->session->ob_pq, stream->deferred_data);
if(rv == 0) { if(rv != 0) {
nghttp2_stream_detach_deferred_data(stream);
} else {
/* FATAL */ /* FATAL */
assert(rv < NGHTTP2_ERR_FATAL); assert(rv < NGHTTP2_ERR_FATAL);
return rv; return rv;
} }
nghttp2_stream_detach_deferred_data(stream);
} }
return 0; return 0;
} }
@ -2287,8 +2264,8 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
int nghttp2_session_on_push_promise_received(nghttp2_session *session, int nghttp2_session_on_push_promise_received(nghttp2_session *session,
nghttp2_frame *frame) nghttp2_frame *frame)
{ {
int rv;
nghttp2_stream *stream; nghttp2_stream *stream;
nghttp2_stream *promised_stream;
if(session->server || frame->hd.stream_id == 0) { if(session->server || frame->hd.stream_id == 0) {
return nghttp2_session_handle_invalid_connection(session, frame, return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
@ -2313,35 +2290,18 @@ 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) {
if(!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR);
}
if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) {
if(stream->state == NGHTTP2_STREAM_CLOSING) {
return nghttp2_session_add_rst_stream return nghttp2_session_add_rst_stream
(session, (session,
frame->push_promise.promised_stream_id, frame->push_promise.promised_stream_id,
NGHTTP2_REFUSED_STREAM); NGHTTP2_REFUSED_STREAM);
} else {
nghttp2_stream *promised_stream;
promised_stream = nghttp2_session_open_stream
(session,
frame->push_promise.promised_stream_id,
frame->hd.flags,
nghttp2_pushed_stream_pri(stream),
NGHTTP2_STREAM_RESERVED,
NULL);
if(!promised_stream) {
return NGHTTP2_ERR_NOMEM;
} }
rv = nghttp2_session_call_on_frame_received(session, frame);
if(rv != 0) { if(!nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) {
return rv; return nghttp2_session_handle_invalid_connection(session, frame,
NGHTTP2_PROTOCOL_ERROR);
} }
} if(stream->shut_flags & NGHTTP2_SHUT_RD) {
} else {
if(session->callbacks.on_invalid_frame_recv_callback) { if(session->callbacks.on_invalid_frame_recv_callback) {
if(session->callbacks.on_invalid_frame_recv_callback if(session->callbacks.on_invalid_frame_recv_callback
(session, frame, NGHTTP2_PROTOCOL_ERROR, session->user_data) != 0) { (session, frame, NGHTTP2_PROTOCOL_ERROR, session->user_data) != 0) {
@ -2353,13 +2313,23 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session,
frame->push_promise.promised_stream_id, frame->push_promise.promised_stream_id,
NGHTTP2_PROTOCOL_ERROR); NGHTTP2_PROTOCOL_ERROR);
} }
} else { if(stream->state == NGHTTP2_STREAM_CLOSING) {
return nghttp2_session_add_rst_stream return nghttp2_session_add_rst_stream
(session, (session,
frame->push_promise.promised_stream_id, frame->push_promise.promised_stream_id,
NGHTTP2_REFUSED_STREAM); NGHTTP2_REFUSED_STREAM);
} }
return 0; promised_stream = nghttp2_session_open_stream
(session,
frame->push_promise.promised_stream_id,
frame->hd.flags,
nghttp2_pushed_stream_pri(stream),
NGHTTP2_STREAM_RESERVED,
NULL);
if(!promised_stream) {
return NGHTTP2_ERR_NOMEM;
}
return nghttp2_session_call_on_frame_received(session, frame);
} }
int nghttp2_session_on_ping_received(nghttp2_session *session, int nghttp2_session_on_ping_received(nghttp2_session *session,
@ -2457,7 +2427,9 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
} else { } else {
nghttp2_stream *stream; nghttp2_stream *stream;
stream = nghttp2_session_get_stream(session, frame->hd.stream_id); stream = nghttp2_session_get_stream(session, frame->hd.stream_id);
if(stream) { if(!stream) {
return 0;
}
if(stream->state == NGHTTP2_STREAM_RESERVED) { if(stream->state == NGHTTP2_STREAM_RESERVED) {
return nghttp2_session_handle_invalid_connection return nghttp2_session_handle_invalid_connection
(session, frame, NGHTTP2_PROTOCOL_ERROR); (session, frame, NGHTTP2_PROTOCOL_ERROR);
@ -2468,34 +2440,26 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session,
} }
if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment <
stream->remote_window_size) { stream->remote_window_size) {
int r; return nghttp2_session_handle_invalid_stream(session, frame,
r = nghttp2_session_handle_invalid_stream NGHTTP2_FLOW_CONTROL_ERROR);
(session, frame, NGHTTP2_FLOW_CONTROL_ERROR); }
return r; stream->remote_window_size += frame->window_update.window_size_increment;
} else {
stream->remote_window_size +=
frame->window_update.window_size_increment;
if(stream->remote_window_size > 0 && if(stream->remote_window_size > 0 &&
(session->remote_flow_control == 0 || (session->remote_flow_control == 0 ||
session->remote_window_size > 0) && session->remote_window_size > 0) &&
stream->deferred_data != NULL && stream->deferred_data != NULL &&
(stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL)) { (stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL)) {
int r; rv = nghttp2_pq_push(&session->ob_pq, stream->deferred_data);
r = nghttp2_pq_push(&session->ob_pq, stream->deferred_data); if(rv != 0) {
if(r == 0) {
nghttp2_stream_detach_deferred_data(stream);
} else if(r < 0) {
/* FATAL */ /* FATAL */
assert(r < NGHTTP2_ERR_FATAL); assert(rv < NGHTTP2_ERR_FATAL);
return r; return rv;
} }
nghttp2_stream_detach_deferred_data(stream);
} }
return nghttp2_session_call_on_frame_received(session, frame); return nghttp2_session_call_on_frame_received(session, frame);
} }
} }
}
return 0;
}
static int get_error_code_from_lib_error_code(int lib_error_code) static int get_error_code_from_lib_error_code(int lib_error_code)
{ {
@ -2696,8 +2660,7 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
uint16_t length, uint8_t flags, uint16_t length, uint8_t flags,
int32_t stream_id) int32_t stream_id)
{ {
int r = 0; int rv = 0;
nghttp2_error_code error_code = 0;
nghttp2_stream *stream; nghttp2_stream *stream;
if(stream_id == 0) { if(stream_id == 0) {
/* The spec says that if a DATA frame is received whose stream ID /* The spec says that if a DATA frame is received whose stream ID
@ -2706,53 +2669,17 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR);
} }
stream = nghttp2_session_get_stream(session, stream_id); stream = nghttp2_session_get_stream(session, stream_id);
if(stream) { if(!stream) {
if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { /* This should be treated as stream error, but it results in lots
int valid = 0; of RST_STREAM. So just ignore frame against nonexistent stream
if(nghttp2_session_is_my_stream_id(session, stream_id)) { for now. */
if(stream->state == NGHTTP2_STREAM_OPENED) { return 0;
valid = 1;
if(session->callbacks.on_data_recv_callback) {
if(session->callbacks.on_data_recv_callback
(session, length, flags, stream_id, session->user_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} if(stream->state == NGHTTP2_STREAM_RESERVED) {
} else if(stream->state != NGHTTP2_STREAM_CLOSING) { /* reserved and receiving DATA is connection error */
error_code = NGHTTP2_PROTOCOL_ERROR;
}
} else if(stream->state == NGHTTP2_STREAM_RESERVED) {
/* reserved (remote) and receiving DATA is connection error */
return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR);
} else if(stream->state != NGHTTP2_STREAM_CLOSING) {
/* It is OK if this is remote peer initiated stream and we did
not receive END_STREAM unless stream is in
NGHTTP2_STREAM_CLOSING state. This is a race condition. */
valid = 1;
if(session->callbacks.on_data_recv_callback) {
if(session->callbacks.on_data_recv_callback
(session, length, flags, stream_id, session->user_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} if(stream->shut_flags & NGHTTP2_SHUT_RD) {
if(flags & NGHTTP2_FLAG_END_STREAM) {
r = nghttp2_session_call_on_request_recv(session, stream_id);
if(r != 0) {
return r;
}
}
}
if(valid) {
if(flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
r = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
if(r != 0 && nghttp2_is_fatal(r)) {
return r;
}
r = 0;
}
}
} else {
/* half closed (remote): from the spec: /* half closed (remote): from the spec:
If an endpoint receives additional frames for a stream that is If an endpoint receives additional frames for a stream that is
@ -2760,19 +2687,48 @@ int nghttp2_session_on_data_received(nghttp2_session *session,
5.4.2) of type STREAM_CLOSED. 5.4.2) of type STREAM_CLOSED.
*/ */
if(stream->state != NGHTTP2_STREAM_CLOSING) { if(stream->state != NGHTTP2_STREAM_CLOSING) {
error_code = NGHTTP2_STREAM_CLOSED; return nghttp2_session_add_rst_stream(session, stream_id,
NGHTTP2_STREAM_CLOSED);
}
return 0;
}
if(nghttp2_session_is_my_stream_id(session, stream_id)) {
if(stream->state == NGHTTP2_STREAM_OPENED) {
if(session->callbacks.on_data_recv_callback) {
if(session->callbacks.on_data_recv_callback
(session, length, flags, stream_id, session->user_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
} }
} else { } else if(stream->state != NGHTTP2_STREAM_CLOSING) {
/* This should be treated as stream error, but it results in lots return nghttp2_session_add_rst_stream(session, stream_id,
of RST_STREAM. So just ignore frame against nonexistent stream NGHTTP2_PROTOCOL_ERROR);
for now. */
/* error_code = NGHTTP2_PROTOCOL_ERROR; */
} }
if(error_code != 0) { } else if(stream->state != NGHTTP2_STREAM_CLOSING) {
r = nghttp2_session_add_rst_stream(session, stream_id, error_code); /* It is OK if this is remote peer initiated stream and we did
not receive END_STREAM unless stream is in
NGHTTP2_STREAM_CLOSING state. This is a race condition. */
if(session->callbacks.on_data_recv_callback) {
if(session->callbacks.on_data_recv_callback
(session, length, flags, stream_id, session->user_data) != 0) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
} }
return r; }
if(flags & NGHTTP2_FLAG_END_STREAM) {
rv = nghttp2_session_call_on_request_recv(session, stream_id);
if(rv != 0) {
return rv;
}
}
}
if(flags & NGHTTP2_FLAG_END_STREAM) {
nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD);
rv = nghttp2_session_close_stream_if_shut_rdwr(session, stream);
if(rv != 0 && nghttp2_is_fatal(rv)) {
return rv;
}
}
return 0;
} }
/* For errors, this function only returns FATAL error. */ /* For errors, this function only returns FATAL error. */