More explicit handling of shutdown status of read and write in each stream.

This commit is contained in:
Tatsuhiro Tsujikawa 2012-01-29 00:08:51 +09:00
parent e7489503b8
commit 971e46f563
5 changed files with 121 additions and 54 deletions

View File

@ -294,6 +294,16 @@ int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id)
} }
} }
int spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session,
spdylay_stream *stream)
{
if((stream->shut_flags & SPDYLAY_SHUT_RDWR) == SPDYLAY_SHUT_RDWR) {
return spdylay_session_close_stream(session, stream->stream_id);
} else {
return 0;
}
}
/* /*
* Returns non-zero value if local peer can send SYN_REPLY with stream * Returns non-zero value if local peer can send SYN_REPLY with stream
* ID |stream_id| at the moment, or 0. * ID |stream_id| at the moment, or 0.
@ -309,7 +319,7 @@ static int spdylay_session_is_reply_allowed(spdylay_session *session,
return 0; return 0;
} else { } else {
return stream->state == SPDYLAY_STREAM_OPENING && return stream->state == SPDYLAY_STREAM_OPENING &&
(stream->flags & SPDYLAY_FLAG_UNIDIRECTIONAL) == 0; (stream->shut_flags & SPDYLAY_SHUT_WR) == 0;
} }
} }
@ -321,9 +331,10 @@ static int spdylay_session_is_data_allowed(spdylay_session *session,
return 0; return 0;
} }
if(spdylay_session_is_my_stream_id(session, stream_id)) { if(spdylay_session_is_my_stream_id(session, stream_id)) {
return (stream->flags & SPDYLAY_FLAG_FIN) == 0; return (stream->shut_flags & SPDYLAY_SHUT_WR) == 0;
} else { } else {
return stream->state == SPDYLAY_STREAM_OPENED; return stream->state == SPDYLAY_STREAM_OPENED &&
(stream->shut_flags & SPDYLAY_SHUT_WR) == 0;
} }
} }
@ -456,6 +467,13 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
spdylay_session_get_stream(session, frame->syn_stream.stream_id); spdylay_session_get_stream(session, frame->syn_stream.stream_id);
if(stream) { if(stream) {
stream->state = SPDYLAY_STREAM_OPENING; stream->state = SPDYLAY_STREAM_OPENING;
if(frame->syn_stream.hd.flags & SPDYLAY_FLAG_FIN) {
spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);
}
if(frame->syn_stream.hd.flags & SPDYLAY_FLAG_UNIDIRECTIONAL) {
spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);
}
spdylay_session_close_stream_if_shut_rdwr(session, stream);
} }
break; break;
} }
@ -464,6 +482,10 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
spdylay_session_get_stream(session, frame->syn_reply.stream_id); spdylay_session_get_stream(session, frame->syn_reply.stream_id);
if(stream) { if(stream) {
stream->state = SPDYLAY_STREAM_OPENED; stream->state = SPDYLAY_STREAM_OPENED;
if(frame->syn_reply.hd.flags & SPDYLAY_FLAG_FIN) {
spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);
}
spdylay_session_close_stream_if_shut_rdwr(session, stream);
} }
break; break;
} }
@ -490,18 +512,11 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
abort(); abort();
case SPDYLAY_DATA: case SPDYLAY_DATA:
if(frame->data.flags & SPDYLAY_FLAG_FIN) { if(frame->data.flags & SPDYLAY_FLAG_FIN) {
if(spdylay_session_is_my_stream_id(session, frame->data.stream_id)) { spdylay_stream *stream =
/* This is the last frame of request DATA (e.g., POST in HTTP spdylay_session_get_stream(session, frame->data.stream_id);
term), so set FIN bit set in stream. This also happens when if(stream) {
request HEADERS frame is sent with FIN bit set. */ spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);
spdylay_stream *stream = spdylay_session_close_stream_if_shut_rdwr(session, stream);
spdylay_session_get_stream(session, frame->data.stream_id);
if(stream) {
stream->flags |= SPDYLAY_FLAG_FIN;
}
} else {
/* We send all data requested by peer, so close the stream. */
spdylay_session_close_stream(session, frame->data.stream_id);
} }
} }
break; break;
@ -740,6 +755,19 @@ int spdylay_session_on_syn_stream_received(spdylay_session *session,
frame->syn_stream.hd.flags, frame->syn_stream.hd.flags,
frame->syn_stream.pri, frame->syn_stream.pri,
SPDYLAY_STREAM_OPENING); SPDYLAY_STREAM_OPENING);
if(r == 0) {
spdylay_stream *stream = spdylay_session_get_stream
(session, frame->syn_stream.stream_id);
if(flags & SPDYLAY_FLAG_FIN) {
spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);
}
if(flags & SPDYLAY_FLAG_UNIDIRECTIONAL) {
spdylay_stream_shutdown(stream, SPDYLAY_SHUT_WR);
}
/* We don't call spdylay_session_close_stream_if_shut_rdwr()
here because either SPDYLAY_FLAG_FIN or
SPDYLAY_FLAG_UNIDIRECTIONAL is not set here. */
}
} }
if(r == 0) { if(r == 0) {
session->last_recv_stream_id = frame->syn_stream.stream_id; session->last_recv_stream_id = frame->syn_stream.stream_id;
@ -758,20 +786,20 @@ int spdylay_session_on_syn_reply_received(spdylay_session *session,
{ {
int r = 0; int r = 0;
int valid = 0; int valid = 0;
if(spdylay_session_is_my_stream_id(session, frame->syn_reply.stream_id)) { spdylay_stream *stream = spdylay_session_get_stream
spdylay_stream *stream = spdylay_session_get_stream (session, frame->syn_reply.stream_id);
(session, frame->syn_reply.stream_id); if(stream && (stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {
if(stream && (stream->flags & SPDYLAY_FLAG_UNIDIRECTIONAL) == 0) { if(spdylay_session_is_my_stream_id(session, frame->syn_reply.stream_id)) {
if(stream->state == SPDYLAY_STREAM_OPENING) { if(stream->state == SPDYLAY_STREAM_OPENING) {
valid = 1; valid = 1;
stream->state = SPDYLAY_STREAM_OPENED; stream->state = SPDYLAY_STREAM_OPENED;
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_REPLY, spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_SYN_REPLY,
frame); frame);
if(frame->syn_reply.hd.flags & SPDYLAY_FLAG_FIN) { if(frame->syn_reply.hd.flags & SPDYLAY_FLAG_FIN) {
/* This is the last frame of this stream, so close the /* This is the last frame of this stream, so disallow
stream. This also happens when DATA and HEADERS frame further receptions. */
with FIN bit set. */ spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);
spdylay_session_close_stream(session, frame->syn_reply.stream_id); spdylay_session_close_stream_if_shut_rdwr(session, stream);
} }
} else if(stream->state == SPDYLAY_STREAM_CLOSING) { } else if(stream->state == SPDYLAY_STREAM_CLOSING) {
/* This is race condition. SPDYLAY_STREAM_CLOSING indicates /* This is race condition. SPDYLAY_STREAM_CLOSING indicates
@ -853,18 +881,18 @@ int spdylay_session_on_headers_received(spdylay_session *session,
{ {
int r = 0; int r = 0;
int valid = 0; int valid = 0;
if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) { spdylay_stream *stream = spdylay_session_get_stream
spdylay_stream *stream = spdylay_session_get_stream (session, frame->headers.stream_id);
(session, frame->headers.stream_id); /* First we check readability from this stream. */
if(stream) { if(stream && (stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {
if(stream->state == SPDYLAY_STREAM_OPENED) { if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) {
if(stream && stream->state == SPDYLAY_STREAM_OPENED) {
valid = 1; valid = 1;
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS,
frame); frame);
if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) { if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) {
/* Close the stream because this is the last frame of the spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);
stream. */ spdylay_session_close_stream_if_shut_rdwr(session, stream);
spdylay_session_close_stream(session, frame->headers.stream_id);
} }
} else if(stream->state == SPDYLAY_STREAM_CLOSING) { } else if(stream->state == SPDYLAY_STREAM_CLOSING) {
/* This is race condition. SPDYLAY_STREAM_CLOSING indicates /* This is race condition. SPDYLAY_STREAM_CLOSING indicates
@ -872,21 +900,15 @@ int spdylay_session_on_headers_received(spdylay_session *session,
eventually sent, so we just ignore this frame. */ eventually sent, so we just ignore this frame. */
valid = 1; valid = 1;
} }
} } else {
} else { /* If this is remote peer initiated stream, it is OK unless it
spdylay_stream *stream = spdylay_session_get_stream have sent FIN frame already. */
(session, frame->headers.stream_id); valid = 1;
if(stream) { spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS,
if((stream->flags & SPDYLAY_FLAG_FIN) == 0) { frame);
valid = 1; if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) {
spdylay_session_call_on_ctrl_frame_received(session, SPDYLAY_HEADERS, spdylay_stream_shutdown(stream, SPDYLAY_SHUT_RD);
frame); spdylay_session_close_stream_if_shut_rdwr(session, stream);
if(frame->headers.hd.flags & SPDYLAY_FLAG_FIN) {
stream->flags |= SPDYLAY_FLAG_FIN;
}
if(stream->flags & SPDYLAY_FLAG_UNIDIRECTIONAL) {
spdylay_session_close_stream(session, frame->headers.stream_id);
}
} }
} }
} }
@ -1005,16 +1027,20 @@ static int spdylay_session_process_data_frame(spdylay_session *session)
SPDYLAY_LENGTH_MASK; SPDYLAY_LENGTH_MASK;
stream = spdylay_session_get_stream(session, stream_id); stream = spdylay_session_get_stream(session, stream_id);
if(stream) { if(stream) {
if(spdylay_session_is_my_stream_id(session, stream_id)) { if((stream->shut_flags & SPDYLAY_SHUT_RD) == 0) {
if(stream->state == SPDYLAY_STREAM_OPENED) { if(spdylay_session_is_my_stream_id(session, stream_id)) {
if(stream->state == SPDYLAY_STREAM_OPENED) {
valid = 1;
} else if(stream->state != SPDYLAY_STREAM_CLOSING) {
status_code = SPDYLAY_PROTOCOL_ERROR;
}
} else {
/* It is OK if this is remote peer initiated stream and we did
not receive FIN. */
valid = 1; valid = 1;
} else if(stream->state != SPDYLAY_STREAM_CLOSING) {
status_code = SPDYLAY_PROTOCOL_ERROR;
} }
} else if(stream->flags & SPDYLAY_FLAG_FIN) {
status_code = SPDYLAY_PROTOCOL_ERROR;
} else { } else {
valid = 1; status_code = SPDYLAY_PROTOCOL_ERROR;
} }
} else { } else {
status_code = SPDYLAY_INVALID_STREAM; status_code = SPDYLAY_INVALID_STREAM;

View File

@ -155,6 +155,16 @@ int spdylay_session_open_stream(spdylay_session *session, int32_t stream_id,
*/ */
int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id); int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id);
/*
* If further receptions and transmissions over this stream are
* disallowed, close this stream. This function returns 0 if it
* succeeds, or negative error code. If either receptions or
* transmissions is allowed, this function returns 0 and the stream
* will not be closed.
*/
int spdylay_session_close_stream_if_shut_rdwr(spdylay_session *session,
spdylay_stream *stream);
/* /*
* Called when SYN_STREAM is received. Received frame is |frame|. * Called when SYN_STREAM is received. Received frame is |frame|.
* This function does first validate received frame and then open * This function does first validate received frame and then open

View File

@ -32,7 +32,13 @@ void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id,
stream->flags = flags; stream->flags = flags;
stream->pri = pri; stream->pri = pri;
stream->state = initial_state; stream->state = initial_state;
stream->shut_flags = SPDYLAY_SHUT_NONE;
} }
void spdylay_stream_free(spdylay_stream *stream) void spdylay_stream_free(spdylay_stream *stream)
{} {}
void spdylay_stream_shutdown(spdylay_stream *stream, spdylay_shut_flag flag)
{
stream->shut_flags |= flag;
}

View File

@ -35,12 +35,15 @@
* If local peer is stream initiator: * If local peer is stream initiator:
* SPDYLAY_STREAM_OPENING : upon sending SYN_STREAM * SPDYLAY_STREAM_OPENING : upon sending SYN_STREAM
* SPDYLAY_STREAM_OPENED : upon receiving SYN_REPLY * SPDYLAY_STREAM_OPENED : upon receiving SYN_REPLY
* SPDYLAY_STREAM_CLOSING : upon queuing RST_STREAM
* *
* If remote peer is stream initiator: * If remote peer is stream initiator:
* SPDYLAY_STREAM_OPENING : upon receiving SYN_STREAM * SPDYLAY_STREAM_OPENING : upon receiving SYN_STREAM
* SPDYLAY_STREAM_OPENED : upon sending SYN_REPLY * SPDYLAY_STREAM_OPENED : upon sending SYN_REPLY
* SPDYLAY_STREAM_CLOSING : upon queuing RST_STREAM
*/ */
typedef enum { typedef enum {
/* Initial state */
SPDYLAY_STREAM_INITIAL, SPDYLAY_STREAM_INITIAL,
/* For stream initiator: SYN_STREAM has been sent, but SYN_REPLY is /* For stream initiator: SYN_STREAM has been sent, but SYN_REPLY is
not received yet. For receiver: SYN_STREAM has been received, not received yet. For receiver: SYN_STREAM has been received,
@ -54,6 +57,17 @@ typedef enum {
SPDYLAY_STREAM_CLOSING SPDYLAY_STREAM_CLOSING
} spdylay_stream_state; } spdylay_stream_state;
typedef enum {
SPDYLAY_SHUT_NONE = 0,
/* Indicates further receptions will be disallowed. */
SPDYLAY_SHUT_RD = 0x01,
/* Indicates further transmissions will be disallowed. */
SPDYLAY_SHUT_WR = 0x02,
/* Indicates both further receptions and transmissions will be
disallowed. */
SPDYLAY_SHUT_RDWR = SPDYLAY_SHUT_RD | SPDYLAY_SHUT_WR
} spdylay_shut_flag;
typedef struct { typedef struct {
int32_t stream_id; int32_t stream_id;
spdylay_stream_state state; spdylay_stream_state state;
@ -61,6 +75,8 @@ typedef struct {
uint8_t flags; uint8_t flags;
/* Use same scheme in SYN_STREAM frame */ /* Use same scheme in SYN_STREAM frame */
uint8_t pri; uint8_t pri;
/* Bitwise OR of zero or more spdylay_shut_flag values */
uint8_t shut_flags;
} spdylay_stream; } spdylay_stream;
void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id, void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id,
@ -69,4 +85,10 @@ void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id,
void spdylay_stream_free(spdylay_stream *stream); void spdylay_stream_free(spdylay_stream *stream);
/*
* Disallow either further receptions or transmissions, or both.
* |flag| is bitwise OR of one or more of spdylay_shut_flag.
*/
void spdylay_stream_shutdown(spdylay_stream *stream, spdylay_shut_flag flag);
#endif /* SPDYLAY_STREAM */ #endif /* SPDYLAY_STREAM */

View File

@ -503,6 +503,8 @@ void test_spdylay_session_on_headers_received()
spdylay_session_client_new(&session, &callbacks, &user_data); spdylay_session_client_new(&session, &callbacks, &user_data);
spdylay_session_open_stream(session, 1, SPDYLAY_FLAG_NONE, 0, spdylay_session_open_stream(session, 1, SPDYLAY_FLAG_NONE, 0,
SPDYLAY_STREAM_OPENED); SPDYLAY_STREAM_OPENED);
spdylay_stream_shutdown(spdylay_session_get_stream(session, 1),
SPDYLAY_SHUT_WR);
spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_NONE, 1, spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_NONE, 1,
dup_nv(nv)); dup_nv(nv));
@ -531,7 +533,8 @@ void test_spdylay_session_on_headers_received()
CU_ASSERT(3 == user_data.valid); CU_ASSERT(3 == user_data.valid);
CU_ASSERT(SPDYLAY_STREAM_OPENING == CU_ASSERT(SPDYLAY_STREAM_OPENING ==
spdylay_session_get_stream(session, 2)->state); spdylay_session_get_stream(session, 2)->state);
CU_ASSERT(spdylay_session_get_stream(session, 2)->flags & SPDYLAY_FLAG_FIN); CU_ASSERT(spdylay_session_get_stream(session, 2)->shut_flags &
SPDYLAY_SHUT_RD);
CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame));
CU_ASSERT(2 == user_data.invalid); CU_ASSERT(2 == user_data.invalid);