Commit 92a56d03 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

Fix bug that idle/closed stream may be destroyed while it is referenced

parent 5de2c7a8
This diff is collapsed.
...@@ -73,6 +73,10 @@ typedef struct { ...@@ -73,6 +73,10 @@ typedef struct {
/* The default maximum number of incoming reserved streams */ /* The default maximum number of incoming reserved streams */
#define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200 #define NGHTTP2_MAX_INCOMING_RESERVED_STREAMS 200
/* Even if we have less SETTINGS_MAX_CONCURRENT_STREAMS than this
number, we keep NGHTTP2_MIN_IDLE_STREAMS streams in idle state */
#define NGHTTP2_MIN_IDLE_STREAMS 16
/* The maximum number of items in outbound queue, which is considered /* The maximum number of items in outbound queue, which is considered
as flooding caused by peer. All frames are not considered here. as flooding caused by peer. All frames are not considered here.
We only consider PING + ACK and SETTINGS + ACK. This is because We only consider PING + ACK and SETTINGS + ACK. This is because
...@@ -443,6 +447,11 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags, ...@@ -443,6 +447,11 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
* *
* This function returns a pointer to created new stream object, or * This function returns a pointer to created new stream object, or
* NULL. * NULL.
*
* This function adjusts neither the number of closed streams or idle
* streams. The caller should manually call
* nghttp2_session_adjust_closed_stream() or
* nghttp2_session_adjust_idle_stream() respectively.
*/ */
nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session, nghttp2_stream *nghttp2_session_open_stream(nghttp2_session *session,
int32_t stream_id, uint8_t flags, int32_t stream_id, uint8_t flags,
...@@ -491,29 +500,17 @@ int nghttp2_session_destroy_stream(nghttp2_session *session, ...@@ -491,29 +500,17 @@ int nghttp2_session_destroy_stream(nghttp2_session *session,
* limitation of maximum number of streams in memory, |stream| is not * limitation of maximum number of streams in memory, |stream| is not
* closed and just deleted from memory (see * closed and just deleted from memory (see
* nghttp2_session_destroy_stream). * nghttp2_session_destroy_stream).
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/ */
int nghttp2_session_keep_closed_stream(nghttp2_session *session, void nghttp2_session_keep_closed_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Appends |stream| to linked list |session->idle_stream_head|. We * Appends |stream| to linked list |session->idle_stream_head|. We
* apply fixed limit for list size. To fit into that limit, one or * apply fixed limit for list size. To fit into that limit, one or
* more oldest streams are removed from list as necessary. * more oldest streams are removed from list as necessary.
*
* This function returns 0 if it succeeds, or one the following
* negative error codes:
*
* NGHTTP2_ERR_NOMEM
* Out of memory
*/ */
int nghttp2_session_keep_idle_stream(nghttp2_session *session, void nghttp2_session_keep_idle_stream(nghttp2_session *session,
nghttp2_stream *stream); nghttp2_stream *stream);
/* /*
* Detaches |stream| from idle streams linked list. * Detaches |stream| from idle streams linked list.
...@@ -524,9 +521,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session, ...@@ -524,9 +521,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
/* /*
* Deletes closed stream to ensure that number of incoming streams * Deletes closed stream to ensure that number of incoming streams
* including active and closed is in the maximum number of allowed * including active and closed is in the maximum number of allowed
* stream. If |offset| is nonzero, it is decreased from the maximum * stream.
* number of allowed stream when comparing number of active and closed
* stream and the maximum number.
* *
* This function returns 0 if it succeeds, or one the following * This function returns 0 if it succeeds, or one the following
* negative error codes: * negative error codes:
...@@ -534,8 +529,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session, ...@@ -534,8 +529,7 @@ void nghttp2_session_detach_idle_stream(nghttp2_session *session,
* NGHTTP2_ERR_NOMEM * NGHTTP2_ERR_NOMEM
* Out of memory * Out of memory
*/ */
int nghttp2_session_adjust_closed_stream(nghttp2_session *session, int nghttp2_session_adjust_closed_stream(nghttp2_session *session);
size_t offset);
/* /*
* Deletes idle stream to ensure that number of idle streams is in * Deletes idle stream to ensure that number of idle streams is in
...@@ -807,6 +801,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session, ...@@ -807,6 +801,9 @@ int nghttp2_session_update_local_settings(nghttp2_session *session,
* |pri_spec|. Caller must ensure that stream->hd.stream_id != * |pri_spec|. Caller must ensure that stream->hd.stream_id !=
* pri_spec->stream_id. * pri_spec->stream_id.
* *
* This function does not adjust the number of idle streams. The
* caller should call nghttp2_session_adjust_idle_stream() later.
*
* 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:
* *
......
...@@ -7669,6 +7669,7 @@ void test_nghttp2_session_keep_closed_stream(void) { ...@@ -7669,6 +7669,7 @@ void test_nghttp2_session_keep_closed_stream(void) {
CU_ASSERT(NULL == session->closed_stream_head->closed_prev); CU_ASSERT(NULL == session->closed_stream_head->closed_prev);
open_stream(session, 11); open_stream(session, 11);
nghttp2_session_adjust_closed_stream(session);
CU_ASSERT(1 == session->num_closed_streams); CU_ASSERT(1 == session->num_closed_streams);
CU_ASSERT(5 == session->closed_stream_tail->stream_id); CU_ASSERT(5 == session->closed_stream_tail->stream_id);
...@@ -7677,6 +7678,7 @@ void test_nghttp2_session_keep_closed_stream(void) { ...@@ -7677,6 +7678,7 @@ void test_nghttp2_session_keep_closed_stream(void) {
CU_ASSERT(NULL == session->closed_stream_head->closed_next); CU_ASSERT(NULL == session->closed_stream_head->closed_next);
open_stream(session, 13); open_stream(session, 13);
nghttp2_session_adjust_closed_stream(session);
CU_ASSERT(0 == session->num_closed_streams); CU_ASSERT(0 == session->num_closed_streams);
CU_ASSERT(NULL == session->closed_stream_tail); CU_ASSERT(NULL == session->closed_stream_tail);
...@@ -7689,6 +7691,7 @@ void test_nghttp2_session_keep_closed_stream(void) { ...@@ -7689,6 +7691,7 @@ void test_nghttp2_session_keep_closed_stream(void) {
/* server initiated stream is not counted to max concurrent limit */ /* server initiated stream is not counted to max concurrent limit */
open_stream(session, 2); open_stream(session, 2);
nghttp2_session_adjust_closed_stream(session);
CU_ASSERT(1 == session->num_closed_streams); CU_ASSERT(1 == session->num_closed_streams);
CU_ASSERT(3 == session->closed_stream_head->stream_id); CU_ASSERT(3 == session->closed_stream_head->stream_id);
...@@ -7708,6 +7711,7 @@ void test_nghttp2_session_keep_idle_stream(void) { ...@@ -7708,6 +7711,7 @@ void test_nghttp2_session_keep_idle_stream(void) {
nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS,
max_concurrent_streams}; max_concurrent_streams};
int i; int i;
int32_t stream_id;
memset(&callbacks, 0, sizeof(callbacks)); memset(&callbacks, 0, sizeof(callbacks));
callbacks.send_callback = null_send_callback; callbacks.send_callback = null_send_callback;
...@@ -7716,25 +7720,29 @@ void test_nghttp2_session_keep_idle_stream(void) { ...@@ -7716,25 +7720,29 @@ void test_nghttp2_session_keep_idle_stream(void) {
nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1); nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, &iv, 1);
/* We at least allow 2 idle streams even if max concurrent streams /* We at least allow NGHTTP2_MIN_IDLE_STREAM idle streams even if
is very low. */ max concurrent streams is very low. */
for (i = 0; i < 2; ++i) { for (i = 0; i < NGHTTP2_MIN_IDLE_STREAMS; ++i) {
nghttp2_session_open_stream(session, i * 2 + 1, NGHTTP2_STREAM_FLAG_NONE, nghttp2_session_open_stream(session, i * 2 + 1, NGHTTP2_STREAM_FLAG_NONE,
&pri_spec_default, NGHTTP2_STREAM_IDLE, NULL); &pri_spec_default, NGHTTP2_STREAM_IDLE, NULL);
nghttp2_session_adjust_idle_stream(session);
} }
CU_ASSERT(2 == session->num_idle_streams); CU_ASSERT(NGHTTP2_MIN_IDLE_STREAMS == session->num_idle_streams);
stream_id = (NGHTTP2_MIN_IDLE_STREAMS - 1) * 2 + 1;
CU_ASSERT(1 == session->idle_stream_head->stream_id); CU_ASSERT(1 == session->idle_stream_head->stream_id);
CU_ASSERT(3 == session->idle_stream_tail->stream_id); CU_ASSERT(stream_id == session->idle_stream_tail->stream_id);
nghttp2_session_open_stream(session, 5, NGHTTP2_FLAG_NONE, &pri_spec_default, stream_id += 2;
NGHTTP2_STREAM_IDLE, NULL);
CU_ASSERT(2 == session->num_idle_streams); nghttp2_session_open_stream(session, stream_id, NGHTTP2_FLAG_NONE,
&pri_spec_default, NGHTTP2_STREAM_IDLE, NULL);
nghttp2_session_adjust_idle_stream(session);
CU_ASSERT(NGHTTP2_MIN_IDLE_STREAMS == session->num_idle_streams);
CU_ASSERT(3 == session->idle_stream_head->stream_id); CU_ASSERT(3 == session->idle_stream_head->stream_id);
CU_ASSERT(5 == session->idle_stream_tail->stream_id); CU_ASSERT(stream_id == session->idle_stream_tail->stream_id);
nghttp2_session_del(session); nghttp2_session_del(session);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment