Take into account shut_flags when accepting DATA frame

This commit is contained in:
Tatsuhiro Tsujikawa 2012-06-14 22:39:44 +09:00
parent 14d1a5a547
commit 5236394c1c
4 changed files with 123 additions and 9 deletions

View File

@ -2449,6 +2449,32 @@ static int spdylay_session_update_recv_window_size(spdylay_session *session,
return 0; return 0;
} }
/*
* Returns nonzero if the reception of DATA for stream |stream_id| is
* allowed.
*/
static int spdylay_session_check_data_recv_allowed(spdylay_session *session,
int32_t stream_id)
{
spdylay_stream *stream;
stream = spdylay_session_get_stream(session, stream_id);
if(stream) {
if((stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {
if(spdylay_session_is_my_stream_id(session, stream_id)) {
if(stream->state == SPDYLAY_STREAM_OPENED) {
return 1;
}
} else if(stream->state != SPDYLAY_STREAM_CLOSING) {
/* It is OK if this is remote peer initiated stream and we did
not receive FIN unless stream is in SPDYLAY_STREAM_CLOSING
state. This is a race condition. */
return 1;
}
}
}
return 0;
}
ssize_t spdylay_session_mem_recv(spdylay_session *session, ssize_t spdylay_session_mem_recv(spdylay_session *session,
const uint8_t *in, size_t inlen) const uint8_t *in, size_t inlen)
{ {
@ -2510,6 +2536,14 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session,
assert(r < SPDYLAY_ERR_FATAL); assert(r < SPDYLAY_ERR_FATAL);
return r; return r;
} }
} else {
/* Check stream is open. If it is not open or closing,
ignore payload. */
int32_t stream_id;
stream_id = spdylay_get_uint32(session->iframe.headbuf);
if(!spdylay_session_check_data_recv_allowed(session, stream_id)) {
session->iframe.state = SPDYLAY_RECV_PAYLOAD_IGN;
}
} }
} else { } else {
break; break;
@ -2523,7 +2557,6 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session,
size_t bufavail, readlen; size_t bufavail, readlen;
int32_t data_stream_id = 0; int32_t data_stream_id = 0;
uint8_t data_flags = SPDYLAY_DATA_FLAG_NONE; uint8_t data_flags = SPDYLAY_DATA_FLAG_NONE;
spdylay_stream *data_stream = NULL;
rempayloadlen = session->iframe.payloadlen - session->iframe.off; rempayloadlen = session->iframe.payloadlen - session->iframe.off;
bufavail = inlimit - inmark; bufavail = inlimit - inmark;
@ -2583,8 +2616,7 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session,
data_stream_id = spdylay_get_uint32(session->iframe.headbuf) & data_stream_id = spdylay_get_uint32(session->iframe.headbuf) &
SPDYLAY_STREAM_ID_MASK; SPDYLAY_STREAM_ID_MASK;
data_flags = session->iframe.headbuf[4]; data_flags = session->iframe.headbuf[4];
data_stream = spdylay_session_get_stream(session, data_stream_id); if(session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN) {
if(data_stream && data_stream->state != SPDYLAY_STREAM_CLOSING) {
if(session->callbacks.on_data_chunk_recv_callback) { if(session->callbacks.on_data_chunk_recv_callback) {
session->callbacks.on_data_chunk_recv_callback(session, session->callbacks.on_data_chunk_recv_callback(session,
data_flags, data_flags,
@ -2599,7 +2631,7 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session,
inmark += readlen; inmark += readlen;
if(session->flow_control && if(session->flow_control &&
data_stream && data_stream->state != SPDYLAY_STREAM_CLOSING && session->iframe.state != SPDYLAY_RECV_PAYLOAD_IGN &&
!spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { !spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) {
if(readlen > 0 && if(readlen > 0 &&
(session->iframe.payloadlen != session->iframe.off || (session->iframe.payloadlen != session->iframe.off ||
@ -2618,11 +2650,7 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session,
if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) {
r = spdylay_session_process_ctrl_frame(session); r = spdylay_session_process_ctrl_frame(session);
} else { } else {
if(data_stream && data_stream->state != SPDYLAY_STREAM_CLOSING) { r = spdylay_session_process_data_frame(session);
r = spdylay_session_process_data_frame(session);
} else {
r = 0;
}
} }
if(r < 0) { if(r < 0) {
/* FATAL */ /* FATAL */

View File

@ -162,6 +162,8 @@ int main(int argc, char* argv[])
test_spdylay_session_data_read_temporal_failure) || test_spdylay_session_data_read_temporal_failure) ||
!CU_add_test(pSuite, "session_recv_eof", !CU_add_test(pSuite, "session_recv_eof",
test_spdylay_session_recv_eof) || test_spdylay_session_recv_eof) ||
!CU_add_test(pSuite, "session_recv_data",
test_spdylay_session_recv_data) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy2", !CU_add_test(pSuite, "frame_unpack_nv_spdy2",
test_spdylay_frame_unpack_nv_spdy2) || test_spdylay_frame_unpack_nv_spdy2) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy3", !CU_add_test(pSuite, "frame_unpack_nv_spdy3",

View File

@ -65,6 +65,8 @@ typedef struct {
size_t data_source_length; size_t data_source_length;
int32_t stream_id; int32_t stream_id;
size_t block_count; size_t block_count;
int data_chunk_recv_cb_called;
int data_recv_cb_called;
} my_user_data; } my_user_data;
static void scripted_data_feed_init(scripted_data_feed *df, static void scripted_data_feed_init(scripted_data_feed *df,
@ -166,6 +168,23 @@ static void on_ctrl_not_send_callback(spdylay_session *session,
ud->not_sent_error = error; ud->not_sent_error = error;
} }
static void on_data_chunk_recv_callback(spdylay_session *session,
uint8_t flags, int32_t stream_id,
const uint8_t *data, size_t len,
void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->data_chunk_recv_cb_called;
}
static void on_data_recv_callback(spdylay_session *session,
uint8_t flags, int32_t stream_id,
int32_t length, void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->data_recv_cb_called;
}
static ssize_t fixed_length_data_source_read_callback static ssize_t fixed_length_data_source_read_callback
(spdylay_session *session, int32_t stream_id, (spdylay_session *session, int32_t stream_id,
uint8_t *buf, size_t len, int *eof, uint8_t *buf, size_t len, int *eof,
@ -2546,3 +2565,67 @@ void test_spdylay_session_recv_eof(void)
spdylay_session_del(session); spdylay_session_del(session);
} }
void test_spdylay_session_recv_data(void)
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
my_user_data ud;
uint8_t data[8092];
int rv;
spdylay_outbound_item *item;
spdylay_stream *stream;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.send_callback = null_send_callback;
callbacks.on_data_chunk_recv_callback = on_data_chunk_recv_callback;
callbacks.on_data_recv_callback = on_data_recv_callback;
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);
/* Create DATA frame with length 4KiB */
memset(data, 0, sizeof(data));
spdylay_put_uint32be(data, 1);
spdylay_put_uint32be(data+4, 4096);
/* stream 1 is not opened, so it must be responded with RST_STREAM */
ud.data_chunk_recv_cb_called = 0;
ud.data_recv_cb_called = 0;
rv = spdylay_session_mem_recv(session, data, 8+4096);
CU_ASSERT(8+4096 == rv);
CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
CU_ASSERT(0 == ud.data_recv_cb_called);
item = spdylay_session_get_next_ob_item(session);
CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item));
CU_ASSERT(0 == spdylay_session_send(session));
/* Create stream 1 with CLOSING state. It is ignored. */
stream = spdylay_session_open_stream(session, 1,
SPDYLAY_CTRL_FLAG_NONE, 3,
SPDYLAY_STREAM_CLOSING, NULL);
ud.data_chunk_recv_cb_called = 0;
ud.data_recv_cb_called = 0;
rv = spdylay_session_mem_recv(session, data, 8+4096);
CU_ASSERT(8+4096 == rv);
CU_ASSERT(0 == ud.data_chunk_recv_cb_called);
CU_ASSERT(0 == ud.data_recv_cb_called);
item = spdylay_session_get_next_ob_item(session);
CU_ASSERT(NULL == item);
/* This is normal case. DATA is acceptable. */
stream->state = SPDYLAY_STREAM_OPENED;
ud.data_chunk_recv_cb_called = 0;
ud.data_recv_cb_called = 0;
rv = spdylay_session_mem_recv(session, data, 8+4096);
CU_ASSERT(8+4096 == rv);
CU_ASSERT(1 == ud.data_chunk_recv_cb_called);
CU_ASSERT(1 == ud.data_recv_cb_called);
spdylay_session_del(session);
}

View File

@ -71,5 +71,6 @@ void test_spdylay_session_set_option(void);
void test_spdylay_submit_window_update(void); void test_spdylay_submit_window_update(void);
void test_spdylay_session_data_read_temporal_failure(void); void test_spdylay_session_data_read_temporal_failure(void);
void test_spdylay_session_recv_eof(void); void test_spdylay_session_recv_eof(void);
void test_spdylay_session_recv_data(void);
#endif /* SPDYLAY_SESSION_TEST_H */ #endif /* SPDYLAY_SESSION_TEST_H */