More explicit handling of shutdown status of read and write in each stream.
This commit is contained in:
parent
e7489503b8
commit
971e46f563
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue