Suppress to send frames other than GOAWAY if NGHTTP2_GOAWAY_TERM_ON_SEND is set

This change makes sure that GOAWAY which terminates session is
immediately sent without blocked by other frames.
NGHTTP2_ERR_SESSION_CLOSING library error code was added to indicate
this situation to callback.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-01-15 22:32:29 +09:00
parent 50109bb307
commit b0078a2379
3 changed files with 59 additions and 38 deletions

View File

@ -325,6 +325,11 @@ typedef enum {
* not been fully processed yet.
*/
NGHTTP2_ERR_DATA_EXIST = -529,
/**
* The current session is closing due to a connection error or
* `nghttp2_session_terminate_session()` is called.
*/
NGHTTP2_ERR_SESSION_CLOSING = -530,
/**
* The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and processing was terminated (e.g.,

View File

@ -267,6 +267,8 @@ const char *nghttp2_strerror(int error_code) {
return "Invalid stream state";
case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
return "Another DATA frame has already been deferred";
case NGHTTP2_ERR_SESSION_CLOSING:
return "The current session is closing";
case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
return "request HEADERS is not allowed";
case NGHTTP2_ERR_GOAWAY_ALREADY_SENT:

View File

@ -1214,6 +1214,13 @@ int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
return 0;
}
/*
* This function returns nonzero if session is closing.
*/
static int session_is_closing(nghttp2_session *session) {
return (session->goaway_flags & NGHTTP2_GOAWAY_TERM_ON_SEND) != 0;
}
/*
* Check that we can send a frame to the |stream|. This function
* returns 0 if we can send a frame to the |frame|, or one of the
@ -1223,11 +1230,17 @@ int nghttp2_session_close_stream_if_shut_rdwr(nghttp2_session *session,
* The stream is already closed.
* NGHTTP2_ERR_STREAM_SHUT_WR
* The stream is half-closed for transmission.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int stream_predicate_for_send(nghttp2_stream *stream) {
static int session_predicate_for_stream_send(nghttp2_session *session,
nghttp2_stream *stream) {
if (stream == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED;
}
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (stream->shut_flags & NGHTTP2_SHUT_WR) {
return NGHTTP2_ERR_STREAM_SHUT_WR;
}
@ -1275,11 +1288,13 @@ static int session_predicate_request_headers_send(nghttp2_session *session) {
* RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int session_predicate_response_headers_send(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
rv = stream_predicate_for_send(stream);
rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) {
return rv;
}
@ -1312,13 +1327,15 @@ static int session_predicate_response_headers_send(nghttp2_session *session,
* The stream is not reserved state
* NGHTTP2_ERR_STREAM_CLOSED
* RST_STREAM was queued for this stream.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int
session_predicate_push_response_headers_send(nghttp2_session *session _U_,
session_predicate_push_response_headers_send(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
/* TODO Should disallow HEADERS if GOAWAY has already been issued? */
rv = stream_predicate_for_send(stream);
rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) {
return rv;
}
@ -1333,7 +1350,8 @@ session_predicate_push_response_headers_send(nghttp2_session *session _U_,
}
/*
* This function checks frames belongs to the |stream| can be sent.
* This function checks HEADERS, which is neither stream-opening nor
* first response header, with the |stream| can be sent at this time.
* The |stream| can be NULL.
*
* This function returns 0 if it succeeds, or one of the following
@ -1348,11 +1366,13 @@ session_predicate_push_response_headers_send(nghttp2_session *session _U_,
* RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int session_predicate_stream_frame_send(nghttp2_session *session,
nghttp2_stream *stream) {
static int session_predicate_headers_send(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
rv = stream_predicate_for_send(stream);
rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) {
return rv;
}
@ -1372,16 +1392,6 @@ static int session_predicate_stream_frame_send(nghttp2_session *session,
return NGHTTP2_ERR_INVALID_STREAM_STATE;
}
/*
* This function checks HEADERS, which is neither stream-opening nor
* first response header, with the |stream| can be sent at this time.
* The |stream| can be NULL.
*/
static int session_predicate_headers_send(nghttp2_session *session,
nghttp2_stream *stream) {
return session_predicate_stream_frame_send(session, stream);
}
/*
* This function checks PUSH_PROMISE frame |frame| with the |stream|
* can be sent at this time. The |stream| can be NULL.
@ -1404,6 +1414,8 @@ static int session_predicate_headers_send(nghttp2_session *session,
* with END_STREAM flag set has already sent)
* NGHTTP2_ERR_PUSH_DISABLED
* The remote peer disabled reception of PUSH_PROMISE.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int session_predicate_push_promise_send(nghttp2_session *session,
nghttp2_stream *stream) {
@ -1413,7 +1425,7 @@ static int session_predicate_push_promise_send(nghttp2_session *session,
return NGHTTP2_ERR_PROTO;
}
rv = stream_predicate_for_send(stream);
rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) {
return rv;
}
@ -1426,8 +1438,7 @@ static int session_predicate_push_promise_send(nghttp2_session *session,
if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING;
}
if (session->goaway_flags &
(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) {
if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
}
return 0;
@ -1447,6 +1458,8 @@ static int session_predicate_push_promise_send(nghttp2_session *session,
* RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int session_predicate_window_update_send(nghttp2_session *session,
int32_t stream_id) {
@ -1459,6 +1472,9 @@ static int session_predicate_window_update_send(nghttp2_session *session,
if (stream == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED;
}
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING;
}
@ -1468,16 +1484,6 @@ static int session_predicate_window_update_send(nghttp2_session *session,
return 0;
}
/*
* This function checks SETTINGS can be sent at this time.
*
* Currently this function always returns 0.
*/
static int nghttp2_session_predicate_settings_send(nghttp2_session *session _U_,
nghttp2_frame *frame _U_) {
return 0;
}
/* Take into account settings max frame size and both connection-level
flow control here */
static ssize_t
@ -1530,11 +1536,13 @@ static size_t nghttp2_session_next_data_read(nghttp2_session *session,
* RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/
static int nghttp2_session_predicate_data_send(nghttp2_session *session,
nghttp2_stream *stream) {
int rv;
rv = stream_predicate_for_send(stream);
rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) {
return rv;
}
@ -1759,6 +1767,9 @@ static int session_prep_frame(nghttp2_session *session,
return framerv;
}
case NGHTTP2_PRIORITY: {
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
/* PRIORITY frame can be sent at any time and to any stream
ID. */
framerv = nghttp2_frame_pack_priority(&session->aob.framebufs,
@ -1774,6 +1785,9 @@ static int session_prep_frame(nghttp2_session *session,
break;
}
case NGHTTP2_RST_STREAM:
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
framerv = nghttp2_frame_pack_rst_stream(&session->aob.framebufs,
&frame->rst_stream);
if (framerv < 0) {
@ -1781,10 +1795,6 @@ static int session_prep_frame(nghttp2_session *session,
}
break;
case NGHTTP2_SETTINGS: {
rv = nghttp2_session_predicate_settings_send(session, frame);
if (rv != 0) {
return rv;
}
framerv = nghttp2_frame_pack_settings(&session->aob.framebufs,
&frame->settings);
if (framerv < 0) {
@ -1840,6 +1850,9 @@ static int session_prep_frame(nghttp2_session *session,
break;
}
case NGHTTP2_PING:
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
framerv = nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
if (framerv < 0) {
return framerv;
@ -3829,7 +3842,7 @@ int nghttp2_session_on_settings_received(nghttp2_session *session,
}
}
if (!noack) {
if (!noack && !session_is_closing(session)) {
rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
if (rv != 0) {
@ -3997,7 +4010,8 @@ int nghttp2_session_on_ping_received(nghttp2_session *session,
return session_handle_invalid_connection(
session, frame, NGHTTP2_PROTOCOL_ERROR, "PING: stream_id != 0");
}
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
if ((frame->hd.flags & NGHTTP2_FLAG_ACK) == 0 &&
!session_is_closing(session)) {
/* Peer sent ping, so ping it back */
rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
frame->ping.opaque_data);