diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c index a2f08735..b52345cc 100644 --- a/lib/nghttp2_session.c +++ b/lib/nghttp2_session.c @@ -540,17 +540,19 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, nghttp2_error_code error_code) { nghttp2_stream *stream = nghttp2_session_get_stream(session, stream_id); - if(stream) { - /* We call on_stream_close_callback even if stream->state is - NGHTTP2_STREAM_INITIAL. This will happen while sending request - HEADERS, a local endpoint receives RST_STREAM for that - stream. It may be PROTOCOL_ERROR, but without notifying stream - closure will hang the stream in a local endpoint. - */ - if(stream->state != NGHTTP2_STREAM_RESERVED && - /* TODO Should on_stream_close_callback be called against - NGHTTP2_STREAM_RESERVED? It is actually not opened yet. */ - session->callbacks.on_stream_close_callback) { + if(!stream) { + return NGHTTP2_ERR_INVALID_ARGUMENT; + } + /* We call on_stream_close_callback even if stream->state is + NGHTTP2_STREAM_INITIAL. This will happen while sending request + HEADERS, a local endpoint receives RST_STREAM for that stream. It + may be PROTOCOL_ERROR, but without notifying stream closure will + hang the stream in a local endpoint. + */ + /* TODO Should on_stream_close_callback be called against + NGHTTP2_STREAM_RESERVED? It is actually not opened yet. */ + if(stream->state != NGHTTP2_STREAM_RESERVED) { + if(session->callbacks.on_stream_close_callback) { if(session->callbacks.on_stream_close_callback (session, stream_id, error_code, @@ -558,20 +560,16 @@ int nghttp2_session_close_stream(nghttp2_session *session, int32_t stream_id, return NGHTTP2_ERR_CALLBACK_FAILURE; } } - if(stream->state != NGHTTP2_STREAM_RESERVED) { - if(nghttp2_session_is_my_stream_id(session, stream_id)) { - --session->num_outgoing_streams; - } else { - --session->num_incoming_streams; - } + if(nghttp2_session_is_my_stream_id(session, stream_id)) { + --session->num_outgoing_streams; + } else { + --session->num_incoming_streams; } - nghttp2_map_remove(&session->streams, stream_id); - nghttp2_stream_free(stream); - free(stream); - return 0; - } else { - return NGHTTP2_ERR_INVALID_ARGUMENT; } + nghttp2_map_remove(&session->streams, stream_id); + nghttp2_stream_free(stream); + free(stream); + return 0; } /* @@ -678,14 +676,12 @@ static int nghttp2_session_predicate_response_headers_send } if(nghttp2_session_is_my_stream_id(session, stream_id)) { return NGHTTP2_ERR_INVALID_STREAM_ID; + } else if(stream->state == NGHTTP2_STREAM_OPENING) { + return 0; + } else if(stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; } else { - if(stream->state == NGHTTP2_STREAM_OPENING) { - return 0; - } else if(stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } else { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } + return NGHTTP2_ERR_INVALID_STREAM_STATE; } } @@ -758,14 +754,12 @@ static int nghttp2_session_predicate_stream_frame_send } else { return NGHTTP2_ERR_STREAM_CLOSING; } + } else if(stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } else if(stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; } else { - if(stream->state == NGHTTP2_STREAM_OPENED) { - return 0; - } else if(stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } else { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } + return NGHTTP2_ERR_INVALID_STREAM_STATE; } } @@ -895,22 +889,22 @@ static int nghttp2_session_predicate_window_update_send static size_t nghttp2_session_next_data_read(nghttp2_session *session, 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) { return NGHTTP2_DATA_PAYLOAD_LENGTH; + } + session_window_size = + session->remote_flow_control ? session->remote_window_size : INT32_MAX; + stream_window_size = + stream->remote_flow_control ? stream->remote_window_size : INT32_MAX; + window_size = nghttp2_min(session_window_size, stream_window_size); + if(window_size > 0) { + return nghttp2_min(window_size, NGHTTP2_DATA_PAYLOAD_LENGTH); } else { - int32_t session_window_size = - session->remote_flow_control ? session->remote_window_size : INT32_MAX; - int32_t stream_window_size = - stream->remote_flow_control ? stream->remote_window_size : INT32_MAX; - int32_t window_size = nghttp2_min(session_window_size, - stream_window_size); - if(window_size > 0) { - return window_size < NGHTTP2_DATA_PAYLOAD_LENGTH ? - window_size : NGHTTP2_DATA_PAYLOAD_LENGTH; - } else { - return 0; - } + return 0; } } @@ -963,14 +957,12 @@ static int nghttp2_session_predicate_data_send(nghttp2_session *session, } else { return NGHTTP2_ERR_STREAM_CLOSING; } + } else if(stream->state == NGHTTP2_STREAM_OPENED) { + return 0; + } else if(stream->state == NGHTTP2_STREAM_CLOSING) { + return NGHTTP2_ERR_STREAM_CLOSING; } else { - if(stream->state == NGHTTP2_STREAM_OPENED) { - return 0; - } else if(stream->state == NGHTTP2_STREAM_CLOSING) { - return NGHTTP2_ERR_STREAM_CLOSING; - } else { - return NGHTTP2_ERR_INVALID_STREAM_STATE; - } + return NGHTTP2_ERR_INVALID_STREAM_STATE; } } @@ -1649,13 +1641,13 @@ static int nghttp2_session_call_on_frame_received static int nghttp2_session_is_new_peer_stream_id(nghttp2_session *session, int32_t stream_id) { - if(stream_id == 0) { + if(stream_id == 0 || session->last_recv_stream_id >= stream_id) { return 0; } if(session->server) { - return stream_id % 2 == 1 && session->last_recv_stream_id < stream_id; + return stream_id % 2 == 1; } 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, nghttp2_frame *frame) { - int r = 0; - nghttp2_error_code error_code = NGHTTP2_NO_ERROR; + int rv = 0; + nghttp2_error_code error_code; + nghttp2_stream *stream; if(frame->hd.stream_id == 0) { return nghttp2_session_handle_invalid_connection(session, frame, NGHTTP2_PROTOCOL_ERROR); @@ -1759,42 +1752,37 @@ int nghttp2_session_on_request_headers_received(nghttp2_session *session, /* The spec says if an endpoint receives a HEADERS with invalid stream ID, it MUST issue connection error with error code PROTOCOL_ERROR */ - return nghttp2_session_handle_invalid_connection - (session, frame, NGHTTP2_PROTOCOL_ERROR); - } else { - session->last_recv_stream_id = frame->hd.stream_id; - error_code = nghttp2_session_validate_request_headers - (session, &frame->headers); + return nghttp2_session_handle_invalid_connection(session, frame, + NGHTTP2_PROTOCOL_ERROR); } - if(error_code == 0) { - uint8_t flags = frame->hd.flags; - nghttp2_stream *stream; - stream = nghttp2_session_open_stream(session, - frame->hd.stream_id, - frame->hd.flags, - frame->headers.pri, - NGHTTP2_STREAM_OPENING, - NULL); - if(!stream) { - return NGHTTP2_ERR_NOMEM; - } - if(flags & NGHTTP2_FLAG_END_STREAM) { - nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); - } - r = nghttp2_session_call_on_frame_received(session, frame); - if(r != 0) { - return r; - } - if(flags & NGHTTP2_FLAG_END_STREAM) { - r = nghttp2_session_call_on_request_recv(session, frame->hd.stream_id); - if(r != 0) { - return r; - } - } - } else { - r = nghttp2_session_handle_invalid_stream(session, frame, error_code); + session->last_recv_stream_id = frame->hd.stream_id; + error_code = nghttp2_session_validate_request_headers(session, + &frame->headers); + if(error_code != NGHTTP2_NO_ERROR) { + return nghttp2_session_handle_invalid_stream(session, frame, error_code); } - return r; + + stream = nghttp2_session_open_stream(session, + frame->hd.stream_id, + frame->hd.flags, + frame->headers.pri, + NGHTTP2_STREAM_OPENING, + NULL); + if(!stream) { + return NGHTTP2_ERR_NOMEM; + } + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + nghttp2_stream_shutdown(stream, NGHTTP2_SHUT_RD); + } + rv = nghttp2_session_call_on_frame_received(session, frame); + if(rv != 0) { + return rv; + } + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + rv = nghttp2_session_call_on_request_recv(session, frame->hd.stream_id); + } + /* Here we assume that stream is not shutdown in NGHTTP2_SHUT_WR */ + return rv; } int nghttp2_session_on_response_headers_received(nghttp2_session *session, @@ -1815,23 +1803,7 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session, return nghttp2_session_handle_invalid_connection(session, frame, NGHTTP2_INTERNAL_ERROR); } - if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { - stream->state = NGHTTP2_STREAM_OPENED; - rv = nghttp2_session_call_on_frame_received(session, frame); - if(rv != 0) { - return rv; - } - if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - /* This is the last frame of this stream, so disallow - further receptions. */ - 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; - } - rv = 0; - } - } else { + if(stream->shut_flags & NGHTTP2_SHUT_RD) { /* half closed (remote): from the spec: If an endpoint receives additional frames for a stream that is @@ -1841,6 +1813,20 @@ int nghttp2_session_on_response_headers_received(nghttp2_session *session, return nghttp2_session_handle_invalid_stream(session, frame, NGHTTP2_STREAM_CLOSED); } + stream->state = NGHTTP2_STREAM_OPENED; + rv = nghttp2_session_call_on_frame_received(session, frame); + if(rv != 0) { + return rv; + } + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + /* This is the last frame of this stream, so disallow + further receptions. */ + 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; } @@ -1877,8 +1863,6 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, nghttp2_stream *stream) { int r = 0; - int valid = 0; - nghttp2_error_code error_code = NGHTTP2_PROTOCOL_ERROR; if(frame->hd.stream_id == 0) { return nghttp2_session_handle_invalid_connection(session, frame, NGHTTP2_PROTOCOL_ERROR); @@ -1888,98 +1872,92 @@ int nghttp2_session_on_headers_received(nghttp2_session *session, return nghttp2_session_handle_invalid_connection(session, frame, NGHTTP2_INTERNAL_ERROR); } - if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { - if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { - if(stream->state == NGHTTP2_STREAM_OPENED) { - valid = 1; - r = nghttp2_session_call_on_frame_received(session, frame); - if(r != 0) { - return r; - } - if(frame->hd.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 if(stream->state == NGHTTP2_STREAM_CLOSING) { - /* This is race condition. NGHTTP2_STREAM_CLOSING indicates - that we queued RST_STREAM but it has not been sent. It will - eventually sent, so we just ignore this frame. */ - valid = 1; - } - } else { - /* If this is remote peer initiated stream, it is OK unless it - has sent END_STREAM frame already. But if stream is in - NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race - condition. */ - valid = 1; - if(stream->state != NGHTTP2_STREAM_CLOSING) { - r = nghttp2_session_call_on_frame_received(session, frame); - if(r != 0) { - return r; - } - if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { - r = nghttp2_session_call_on_request_recv(session, - frame->hd.stream_id); - if(r != 0) { - return r; - } - 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 if(stream->state == NGHTTP2_STREAM_RESERVED) { - /* reserved (local). The valid push response HEADERS is processed - by nghttp2_session_on_push_response_headers_received(). This + 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); - } else { + } + 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. */ - error_code = NGHTTP2_STREAM_CLOSED; + return nghttp2_session_handle_invalid_stream(session, frame, + NGHTTP2_STREAM_CLOSED); } - if(!valid) { - r = nghttp2_session_handle_invalid_stream(session, frame, error_code); + if(nghttp2_session_is_my_stream_id(session, frame->hd.stream_id)) { + if(stream->state == NGHTTP2_STREAM_OPENED) { + r = nghttp2_session_call_on_frame_received(session, frame); + if(r != 0) { + return r; + } + if(frame->hd.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; + } + } + return 0; + } else if(stream->state == NGHTTP2_STREAM_CLOSING) { + /* This is race condition. NGHTTP2_STREAM_CLOSING indicates + that we queued RST_STREAM but it has not been sent. It will + eventually sent, so we just ignore this frame. */ + return 0; + } else { + return nghttp2_session_handle_invalid_stream(session, frame, + NGHTTP2_PROTOCOL_ERROR); + } + } else { + /* If this is remote peer initiated stream, it is OK unless it + has sent END_STREAM frame already. But if stream is in + NGHTTP2_STREAM_CLOSING, we discard the frame. This is a race + condition. */ + if(stream->state != NGHTTP2_STREAM_CLOSING) { + r = nghttp2_session_call_on_frame_received(session, frame); + if(r != 0) { + return r; + } + if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { + r = nghttp2_session_call_on_request_recv(session, + frame->hd.stream_id); + if(r != 0) { + return r; + } + 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; + } + } + } + return 0; } - return r; } int nghttp2_session_on_priority_received(nghttp2_session *session, nghttp2_frame *frame) { - int rv; nghttp2_stream *stream; if(frame->hd.stream_id == 0) { return nghttp2_session_handle_invalid_connection(session, frame, NGHTTP2_PROTOCOL_ERROR); } stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if(stream) { - /* Only update priority on server side for now */ - if(session->server) { - nghttp2_session_reprioritize_stream(session, stream, - frame->priority.pri); - } - rv = nghttp2_session_call_on_frame_received(session, frame); - if(rv != 0) { - return rv; - } + if(!stream) { + return 0; } - return 0; + /* Only update priority on server side for now */ + if(session->server) { + nghttp2_session_reprioritize_stream(session, stream, + frame->priority.pri); + } + return nghttp2_session_call_on_frame_received(session, frame); } 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_window_size > 0)) { rv = nghttp2_pq_push(&arg->session->ob_pq, stream->deferred_data); - if(rv == 0) { - nghttp2_stream_detach_deferred_data(stream); - } else { + if(rv != 0) { /* FATAL */ assert(rv < NGHTTP2_ERR_FATAL); return rv; } + nghttp2_stream_detach_deferred_data(stream); } 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, nghttp2_frame *frame) { - int rv; nghttp2_stream *stream; + nghttp2_stream *promised_stream; if(session->server || frame->hd.stream_id == 0) { return nghttp2_session_handle_invalid_connection(session, frame, NGHTTP2_PROTOCOL_ERROR); @@ -2313,53 +2290,46 @@ int nghttp2_session_on_push_promise_received(nghttp2_session *session, } session->last_recv_stream_id = frame->push_promise.promised_stream_id; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - 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 - (session, - frame->push_promise.promised_stream_id, - 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) { - return rv; - } - } - } else { - 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) { - return NGHTTP2_ERR_CALLBACK_FAILURE; - } - } - return nghttp2_session_add_rst_stream - (session, - frame->push_promise.promised_stream_id, - NGHTTP2_PROTOCOL_ERROR); - } - } else { + if(!stream) { return nghttp2_session_add_rst_stream (session, frame->push_promise.promised_stream_id, NGHTTP2_REFUSED_STREAM); } - return 0; + + 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) { + 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) { + return NGHTTP2_ERR_CALLBACK_FAILURE; + } + } + return nghttp2_session_add_rst_stream + (session, + frame->push_promise.promised_stream_id, + NGHTTP2_PROTOCOL_ERROR); + } + if(stream->state == NGHTTP2_STREAM_CLOSING) { + return nghttp2_session_add_rst_stream + (session, + frame->push_promise.promised_stream_id, + NGHTTP2_REFUSED_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; + } + return nghttp2_session_call_on_frame_received(session, frame); } int nghttp2_session_on_ping_received(nghttp2_session *session, @@ -2457,44 +2427,38 @@ int nghttp2_session_on_window_update_received(nghttp2_session *session, } else { nghttp2_stream *stream; stream = nghttp2_session_get_stream(session, frame->hd.stream_id); - if(stream) { - if(stream->state == NGHTTP2_STREAM_RESERVED) { - return nghttp2_session_handle_invalid_connection - (session, frame, NGHTTP2_PROTOCOL_ERROR); - } - if(stream->remote_flow_control == 0) { - /* Same reason with connection-level flow control */ - return nghttp2_session_call_on_frame_received(session, frame); - } - if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < - stream->remote_window_size) { - int r; - r = nghttp2_session_handle_invalid_stream - (session, frame, NGHTTP2_FLOW_CONTROL_ERROR); - return r; - } else { - stream->remote_window_size += - frame->window_update.window_size_increment; - if(stream->remote_window_size > 0 && - (session->remote_flow_control == 0 || - session->remote_window_size > 0) && - stream->deferred_data != NULL && - (stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL)) { - int r; - r = nghttp2_pq_push(&session->ob_pq, stream->deferred_data); - if(r == 0) { - nghttp2_stream_detach_deferred_data(stream); - } else if(r < 0) { - /* FATAL */ - assert(r < NGHTTP2_ERR_FATAL); - return r; - } - } - return nghttp2_session_call_on_frame_received(session, frame); - } + if(!stream) { + return 0; } + if(stream->state == NGHTTP2_STREAM_RESERVED) { + return nghttp2_session_handle_invalid_connection + (session, frame, NGHTTP2_PROTOCOL_ERROR); + } + if(stream->remote_flow_control == 0) { + /* Same reason with connection-level flow control */ + return nghttp2_session_call_on_frame_received(session, frame); + } + if(NGHTTP2_MAX_WINDOW_SIZE - frame->window_update.window_size_increment < + stream->remote_window_size) { + return nghttp2_session_handle_invalid_stream(session, frame, + NGHTTP2_FLOW_CONTROL_ERROR); + } + stream->remote_window_size += frame->window_update.window_size_increment; + if(stream->remote_window_size > 0 && + (session->remote_flow_control == 0 || + session->remote_window_size > 0) && + stream->deferred_data != NULL && + (stream->deferred_flags & NGHTTP2_DEFERRED_FLOW_CONTROL)) { + rv = nghttp2_pq_push(&session->ob_pq, stream->deferred_data); + if(rv != 0) { + /* FATAL */ + assert(rv < NGHTTP2_ERR_FATAL); + return rv; + } + nghttp2_stream_detach_deferred_data(stream); + } + return nghttp2_session_call_on_frame_received(session, frame); } - return 0; } 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, int32_t stream_id) { - int r = 0; - nghttp2_error_code error_code = 0; + int rv = 0; nghttp2_stream *stream; if(stream_id == 0) { /* The spec says that if a DATA frame is received whose stream ID @@ -2706,73 +2669,66 @@ int nghttp2_session_on_data_received(nghttp2_session *session, return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); } stream = nghttp2_session_get_stream(session, stream_id); - if(stream) { - if((stream->shut_flags & NGHTTP2_SHUT_RD) == 0) { - int valid = 0; - if(nghttp2_session_is_my_stream_id(session, stream_id)) { - if(stream->state == NGHTTP2_STREAM_OPENED) { - 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; - } - } - } else if(stream->state != NGHTTP2_STREAM_CLOSING) { - 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); - } 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(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: - - 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. - */ - if(stream->state != NGHTTP2_STREAM_CLOSING) { - error_code = NGHTTP2_STREAM_CLOSED; - } - } - } else { + if(!stream) { /* This should be treated as stream error, but it results in lots of RST_STREAM. So just ignore frame against nonexistent stream for now. */ - /* error_code = NGHTTP2_PROTOCOL_ERROR; */ + return 0; } - if(error_code != 0) { - r = nghttp2_session_add_rst_stream(session, stream_id, error_code); + if(stream->state == NGHTTP2_STREAM_RESERVED) { + /* reserved and receiving DATA is connection error */ + return nghttp2_session_fail_session(session, NGHTTP2_PROTOCOL_ERROR); } - return r; + 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. + */ + if(stream->state != NGHTTP2_STREAM_CLOSING) { + 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 if(stream->state != NGHTTP2_STREAM_CLOSING) { + return nghttp2_session_add_rst_stream(session, stream_id, + 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. */ + 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(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. */