Support graceful shutdown using multiple GOAWAY

Add last_stream_id parameter to nghttp2_submit_goaway().  To terminate
connection immediately with application chosen last stream ID,
nghttp2_session_terminate_session2() was added.
This commit is contained in:
Tatsuhiro Tsujikawa 2014-06-18 11:32:07 +09:00
parent 975524a125
commit b78a51da0e
5 changed files with 55 additions and 17 deletions

View File

@ -270,10 +270,10 @@ static int on_stream_close_callback(nghttp2_session *session,
req = nghttp2_session_get_stream_user_data(session, stream_id); req = nghttp2_session_get_stream_user_data(session, stream_id);
if(req) { if(req) {
int rv; int rv;
rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, NGHTTP2_NO_ERROR, rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
NULL, 0);
if(rv != 0) { if(rv != 0) {
diec("nghttp2_submit_goaway", rv); diec("nghttp2_session_terminate_session", rv);
} }
} }
return 0; return 0;

View File

@ -2003,13 +2003,13 @@ int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session* session,
* *
* Signals the session so that the connection should be terminated. * Signals the session so that the connection should be terminated.
* *
* GOAWAY frame with the given |error_code| will be submitted if it * The last stream ID is the ID of a stream for which
* has not been transmitted. After the transmission, both * :type:`nghttp2_on_frame_recv_callback` was called most recently.
* `nghttp2_session_want_read()` and `nghttp2_session_want_write()` *
* return 0. If GOAWAY frame has already transmitted at the time when * The |error_code| is the error code of this GOAWAY frame.
* this function is invoked, `nghttp2_session_want_read()` and *
* `nghttp2_session_want_write()` returns 0 immediately after this * After the transmission, both `nghttp2_session_want_read()` and
* function succeeds. * `nghttp2_session_want_write()` return 0.
* *
* This function should be called when the connection should be * This function should be called when the connection should be
* terminated after sending GOAWAY. If the remaining streams should * terminated after sending GOAWAY. If the remaining streams should
@ -2024,6 +2024,25 @@ int32_t nghttp2_session_get_stream_remote_window_size(nghttp2_session* session,
int nghttp2_session_terminate_session(nghttp2_session *session, int nghttp2_session_terminate_session(nghttp2_session *session,
nghttp2_error_code error_code); nghttp2_error_code error_code);
/**
* @function
*
* Signals the session so that the connection should be terminated.
*
* This function behaves like `nghttp2_session_terminate_session()`,
* but the last stream ID can be specified by the application for fine
* grained control of stream.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
*/
int nghttp2_session_terminate_session2(nghttp2_session *session,
int32_t last_stream_id,
nghttp2_error_code error_code);
/** /**
* @function * @function
* *
@ -2529,7 +2548,8 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
/** /**
* @function * @function
* *
* Submits GOAWAY frame with the error code |error_code|. * Submits GOAWAY frame with the last stream ID |last_stream_id| and
* the error code |error_code|.
* *
* The |flags| is currently ignored and should be * The |flags| is currently ignored and should be
* :enum:`NGHTTP2_FLAG_NONE`. * :enum:`NGHTTP2_FLAG_NONE`.
@ -2541,15 +2561,22 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
* keep this memory after the return of this function. If the * keep this memory after the return of this function. If the
* |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``.
* *
* To shutdown gracefully, first send GOAWAY with ``last_stream_id =
* (1u << 31) - 1``. After 1 RTT, call either
* `nghttp2_submit_goaway()`, `nghttp2_session_terminate_session()` or
* `nghttp2_session_terminate_session2()`. The latter 2 will close
* HTTP/2 session immediately after transmission of the frame.
*
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
* *
* :enum:`NGHTTP2_ERR_NOMEM` * :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory. * Out of memory.
* NGHTTP2_ERR_INVALID_ARGUMENT * :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |opaque_data_len| is too large. * The |opaque_data_len| is too large.
*/ */
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
int32_t last_stream_id,
nghttp2_error_code error_code, nghttp2_error_code error_code,
const uint8_t *opaque_data, size_t opaque_data_len); const uint8_t *opaque_data, size_t opaque_data_len);

View File

@ -134,14 +134,23 @@ static int session_detect_idle_stream(nghttp2_session *session,
int nghttp2_session_terminate_session(nghttp2_session *session, int nghttp2_session_terminate_session(nghttp2_session *session,
nghttp2_error_code error_code) nghttp2_error_code error_code)
{
return nghttp2_session_terminate_session2(session,
session->last_proc_stream_id,
error_code);
}
int nghttp2_session_terminate_session2(nghttp2_session *session,
int32_t last_stream_id,
nghttp2_error_code error_code)
{ {
if(session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) { if(session->goaway_flags & NGHTTP2_GOAWAY_FAIL_ON_SEND) {
return 0; return 0;
} }
session->goaway_flags |= NGHTTP2_GOAWAY_FAIL_ON_SEND; session->goaway_flags |= NGHTTP2_GOAWAY_FAIL_ON_SEND;
return nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, error_code, return nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, last_stream_id,
NULL, 0); error_code, NULL, 0);
} }
int nghttp2_session_is_my_stream_id(nghttp2_session *session, int nghttp2_session_is_my_stream_id(nghttp2_session *session,

View File

@ -246,10 +246,11 @@ int nghttp2_submit_rst_stream(nghttp2_session *session, uint8_t flags,
} }
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags, int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
int32_t last_stream_id,
nghttp2_error_code error_code, nghttp2_error_code error_code,
const uint8_t *opaque_data, size_t opaque_data_len) const uint8_t *opaque_data, size_t opaque_data_len)
{ {
return nghttp2_session_add_goaway(session, session->last_proc_stream_id, return nghttp2_session_add_goaway(session, last_stream_id,
error_code, opaque_data, opaque_data_len); error_code, opaque_data, opaque_data_len);
} }

View File

@ -4429,7 +4429,8 @@ void test_nghttp2_session_on_ctrl_not_send(void)
user_data.frame_not_send_cb_called = 0; user_data.frame_not_send_cb_called = 0;
/* Send GOAWAY */ /* Send GOAWAY */
CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE,
NGHTTP2_NO_ERROR, NULL, 0)); (1u << 31) - 1, NGHTTP2_NO_ERROR,
NULL, 0));
session->next_stream_id = 9; session->next_stream_id = 9;
@ -4456,7 +4457,7 @@ void test_nghttp2_session_get_outbound_queue_size(void)
CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session)); CU_ASSERT(1 == nghttp2_session_get_outbound_queue_size(session));
CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, CU_ASSERT(0 == nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE,
NGHTTP2_NO_ERROR, NULL, 0)); 3, NGHTTP2_NO_ERROR, NULL, 0));
CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session)); CU_ASSERT(2 == nghttp2_session_get_outbound_queue_size(session));
nghttp2_session_del(session); nghttp2_session_del(session);