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:
parent
50109bb307
commit
b0078a2379
|
@ -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.,
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue