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. * not been fully processed yet.
*/ */
NGHTTP2_ERR_DATA_EXIST = -529, 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 * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is
* under unexpected condition and processing was terminated (e.g., * 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"; return "Invalid stream state";
case NGHTTP2_ERR_DEFERRED_DATA_EXIST: case NGHTTP2_ERR_DEFERRED_DATA_EXIST:
return "Another DATA frame has already been deferred"; 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: case NGHTTP2_ERR_START_STREAM_NOT_ALLOWED:
return "request HEADERS is not allowed"; return "request HEADERS is not allowed";
case NGHTTP2_ERR_GOAWAY_ALREADY_SENT: 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; 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 * 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 * 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. * The stream is already closed.
* NGHTTP2_ERR_STREAM_SHUT_WR * NGHTTP2_ERR_STREAM_SHUT_WR
* The stream is half-closed for transmission. * 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) { if (stream == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED; return NGHTTP2_ERR_STREAM_CLOSED;
} }
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (stream->shut_flags & NGHTTP2_SHUT_WR) { if (stream->shut_flags & NGHTTP2_SHUT_WR) {
return NGHTTP2_ERR_STREAM_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. * RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE * NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid. * 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, static int session_predicate_response_headers_send(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv; int rv;
rv = stream_predicate_for_send(stream); rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
} }
@ -1312,13 +1327,15 @@ static int session_predicate_response_headers_send(nghttp2_session *session,
* The stream is not reserved state * The stream is not reserved state
* NGHTTP2_ERR_STREAM_CLOSED * NGHTTP2_ERR_STREAM_CLOSED
* RST_STREAM was queued for this stream. * RST_STREAM was queued for this stream.
* NGHTTP2_ERR_SESSION_CLOSING
* This session is closing.
*/ */
static int static int
session_predicate_push_response_headers_send(nghttp2_session *session _U_, session_predicate_push_response_headers_send(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv; int rv;
/* TODO Should disallow HEADERS if GOAWAY has already been issued? */ /* 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) { if (rv != 0) {
return rv; 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. * The |stream| can be NULL.
* *
* This function returns 0 if it succeeds, or one of the following * 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. * RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE * NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid. * 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, static int session_predicate_headers_send(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv; int rv;
rv = stream_predicate_for_send(stream); rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
} }
@ -1372,16 +1392,6 @@ static int session_predicate_stream_frame_send(nghttp2_session *session,
return NGHTTP2_ERR_INVALID_STREAM_STATE; 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| * This function checks PUSH_PROMISE frame |frame| with the |stream|
* can be sent at this time. The |stream| can be NULL. * 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) * with END_STREAM flag set has already sent)
* NGHTTP2_ERR_PUSH_DISABLED * NGHTTP2_ERR_PUSH_DISABLED
* The remote peer disabled reception of PUSH_PROMISE. * 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, static int session_predicate_push_promise_send(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
@ -1413,7 +1425,7 @@ static int session_predicate_push_promise_send(nghttp2_session *session,
return NGHTTP2_ERR_PROTO; return NGHTTP2_ERR_PROTO;
} }
rv = stream_predicate_for_send(stream); rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
} }
@ -1426,8 +1438,7 @@ static int session_predicate_push_promise_send(nghttp2_session *session,
if (stream->state == NGHTTP2_STREAM_CLOSING) { if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
} }
if (session->goaway_flags & if (session->goaway_flags & NGHTTP2_GOAWAY_RECV) {
(NGHTTP2_GOAWAY_TERM_ON_SEND | NGHTTP2_GOAWAY_RECV)) {
return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED; return NGHTTP2_ERR_START_STREAM_NOT_ALLOWED;
} }
return 0; return 0;
@ -1447,6 +1458,8 @@ static int session_predicate_push_promise_send(nghttp2_session *session,
* RST_STREAM was queued for this stream. * RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE * NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid. * 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, static int session_predicate_window_update_send(nghttp2_session *session,
int32_t stream_id) { int32_t stream_id) {
@ -1459,6 +1472,9 @@ static int session_predicate_window_update_send(nghttp2_session *session,
if (stream == NULL) { if (stream == NULL) {
return NGHTTP2_ERR_STREAM_CLOSED; return NGHTTP2_ERR_STREAM_CLOSED;
} }
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
if (stream->state == NGHTTP2_STREAM_CLOSING) { if (stream->state == NGHTTP2_STREAM_CLOSING) {
return NGHTTP2_ERR_STREAM_CLOSING; return NGHTTP2_ERR_STREAM_CLOSING;
} }
@ -1468,16 +1484,6 @@ static int session_predicate_window_update_send(nghttp2_session *session,
return 0; 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 /* Take into account settings max frame size and both connection-level
flow control here */ flow control here */
static ssize_t 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. * RST_STREAM was queued for this stream.
* NGHTTP2_ERR_INVALID_STREAM_STATE * NGHTTP2_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid. * 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, static int nghttp2_session_predicate_data_send(nghttp2_session *session,
nghttp2_stream *stream) { nghttp2_stream *stream) {
int rv; int rv;
rv = stream_predicate_for_send(stream); rv = session_predicate_for_stream_send(session, stream);
if (rv != 0) { if (rv != 0) {
return rv; return rv;
} }
@ -1759,6 +1767,9 @@ static int session_prep_frame(nghttp2_session *session,
return framerv; return framerv;
} }
case NGHTTP2_PRIORITY: { 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 /* PRIORITY frame can be sent at any time and to any stream
ID. */ ID. */
framerv = nghttp2_frame_pack_priority(&session->aob.framebufs, framerv = nghttp2_frame_pack_priority(&session->aob.framebufs,
@ -1774,6 +1785,9 @@ static int session_prep_frame(nghttp2_session *session,
break; break;
} }
case NGHTTP2_RST_STREAM: case NGHTTP2_RST_STREAM:
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
framerv = nghttp2_frame_pack_rst_stream(&session->aob.framebufs, framerv = nghttp2_frame_pack_rst_stream(&session->aob.framebufs,
&frame->rst_stream); &frame->rst_stream);
if (framerv < 0) { if (framerv < 0) {
@ -1781,10 +1795,6 @@ static int session_prep_frame(nghttp2_session *session,
} }
break; break;
case NGHTTP2_SETTINGS: { case NGHTTP2_SETTINGS: {
rv = nghttp2_session_predicate_settings_send(session, frame);
if (rv != 0) {
return rv;
}
framerv = nghttp2_frame_pack_settings(&session->aob.framebufs, framerv = nghttp2_frame_pack_settings(&session->aob.framebufs,
&frame->settings); &frame->settings);
if (framerv < 0) { if (framerv < 0) {
@ -1840,6 +1850,9 @@ static int session_prep_frame(nghttp2_session *session,
break; break;
} }
case NGHTTP2_PING: case NGHTTP2_PING:
if (session_is_closing(session)) {
return NGHTTP2_ERR_SESSION_CLOSING;
}
framerv = nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping); framerv = nghttp2_frame_pack_ping(&session->aob.framebufs, &frame->ping);
if (framerv < 0) { if (framerv < 0) {
return framerv; 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); rv = nghttp2_session_add_settings(session, NGHTTP2_FLAG_ACK, NULL, 0);
if (rv != 0) { if (rv != 0) {
@ -3997,7 +4010,8 @@ int nghttp2_session_on_ping_received(nghttp2_session *session,
return session_handle_invalid_connection( return session_handle_invalid_connection(
session, frame, NGHTTP2_PROTOCOL_ERROR, "PING: stream_id != 0"); 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 */ /* Peer sent ping, so ping it back */
rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK, rv = nghttp2_session_add_ping(session, NGHTTP2_FLAG_ACK,
frame->ping.opaque_data); frame->ping.opaque_data);