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);
if(req) {
int rv;
rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, NGHTTP2_NO_ERROR,
NULL, 0);
rv = nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR);
if(rv != 0) {
diec("nghttp2_submit_goaway", rv);
diec("nghttp2_session_terminate_session", rv);
}
}
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.
*
* GOAWAY frame with the given |error_code| will be submitted if it
* has not been transmitted. After the transmission, both
* `nghttp2_session_want_read()` and `nghttp2_session_want_write()`
* return 0. If GOAWAY frame has already transmitted at the time when
* this function is invoked, `nghttp2_session_want_read()` and
* `nghttp2_session_want_write()` returns 0 immediately after this
* function succeeds.
* The last stream ID is the ID of a stream for which
* :type:`nghttp2_on_frame_recv_callback` was called most recently.
*
* The |error_code| is the error code of this GOAWAY frame.
*
* After the transmission, both `nghttp2_session_want_read()` and
* `nghttp2_session_want_write()` return 0.
*
* This function should be called when the connection should be
* 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,
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
*
@ -2529,7 +2548,8 @@ int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags,
/**
* @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
* :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
* |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
* negative error codes:
*
* :enum:`NGHTTP2_ERR_NOMEM`
* Out of memory.
* NGHTTP2_ERR_INVALID_ARGUMENT
* :enum:`NGHTTP2_ERR_INVALID_ARGUMENT`
* The |opaque_data_len| is too large.
*/
int nghttp2_submit_goaway(nghttp2_session *session, uint8_t flags,
int32_t last_stream_id,
nghttp2_error_code error_code,
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,
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) {
return 0;
}
session->goaway_flags |= NGHTTP2_GOAWAY_FAIL_ON_SEND;
return nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, error_code,
NULL, 0);
return nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, last_stream_id,
error_code, NULL, 0);
}
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,
int32_t last_stream_id,
nghttp2_error_code error_code,
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);
}

View File

@ -4429,7 +4429,8 @@ void test_nghttp2_session_on_ctrl_not_send(void)
user_data.frame_not_send_cb_called = 0;
/* Send GOAWAY */
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;
@ -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(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));
nghttp2_session_del(session);