Added on_ctrl_not_send_callback.

This callback function is invoked after the control frame
is not sent because of the error. The error is indicated by
the error argument, which is one of the values defined in spdylay_error.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-03-08 00:18:18 +09:00
parent 185d929d86
commit 11020146f5
6 changed files with 353 additions and 69 deletions

View File

@ -52,6 +52,15 @@ typedef enum {
SPDYLAY_ERR_INVALID_FRAME = -506, SPDYLAY_ERR_INVALID_FRAME = -506,
SPDYLAY_ERR_EOF = -507, SPDYLAY_ERR_EOF = -507,
SPDYLAY_ERR_DEFERRED = -508, SPDYLAY_ERR_DEFERRED = -508,
SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE = -509,
SPDYLAY_ERR_STREAM_ALREADY_CLOSED = -510,
SPDYLAY_ERR_STREAM_CLOSING = -511,
SPDYLAY_ERR_STREAM_SHUT_WR = -512,
SPDYLAY_ERR_INVALID_STREAM_ID = -513,
SPDYLAY_ERR_INVALID_STREAM_STATE = -514,
SPDYLAY_ERR_DEFERRED_DATA_EXIST = -515,
SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED = -516,
SPDYLAY_ERR_GOAWAY_ALREADY_SENT = -517,
/* The errors < SPDYLAY_ERR_FATAL mean that the library is under /* The errors < SPDYLAY_ERR_FATAL mean that the library is under
unexpected condition that it cannot process any further data unexpected condition that it cannot process any further data
reliably (e.g., out of memory). */ reliably (e.g., out of memory). */
@ -337,6 +346,15 @@ typedef void (*spdylay_on_ctrl_send_callback)
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, (spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
void *user_data); void *user_data);
/*
* Callback function invoked after the control frame |frame| of type
* |type| is not sent because of the error. The error is indicated by
* the |error|, which is one of the values defined in spdylay_error.
*/
typedef void (*spdylay_on_ctrl_not_send_callback)
(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame,
int error, void *user_data);
/* /*
* Callback function invoked after DATA frame is sent. * Callback function invoked after DATA frame is sent.
*/ */
@ -381,6 +399,7 @@ typedef struct {
spdylay_on_data_recv_callback on_data_recv_callback; spdylay_on_data_recv_callback on_data_recv_callback;
spdylay_before_ctrl_send_callback before_ctrl_send_callback; spdylay_before_ctrl_send_callback before_ctrl_send_callback;
spdylay_on_ctrl_send_callback on_ctrl_send_callback; spdylay_on_ctrl_send_callback on_ctrl_send_callback;
spdylay_on_ctrl_not_send_callback on_ctrl_not_send_callback;
spdylay_on_data_send_callback on_data_send_callback; spdylay_on_data_send_callback on_data_send_callback;
spdylay_on_stream_close_callback on_stream_close_callback; spdylay_on_stream_close_callback on_stream_close_callback;
spdylay_on_request_recv_callback on_request_recv_callback; spdylay_on_request_recv_callback on_request_recv_callback;

View File

@ -43,6 +43,14 @@ static int spdylay_session_get_max_concurrent_streams_reached
<= spdylay_map_size(&session->streams); <= spdylay_map_size(&session->streams);
} }
/*
* Returns non-zero if |error| is non-fatal error.
*/
static int spdylay_is_non_fatal(int error)
{
return error < 0 && error > SPDYLAY_ERR_FATAL;
}
int spdylay_session_is_my_stream_id(spdylay_session *session, int spdylay_session_is_my_stream_id(spdylay_session *session,
int32_t stream_id) int32_t stream_id)
{ {
@ -438,54 +446,129 @@ void spdylay_session_close_pushed_streams(spdylay_session *session,
} }
} }
/* static int spdylay_predicate_stream_for_send(spdylay_stream *stream)
* Returns non-zero value if local peer can send SYN_REPLY with stream
* ID |stream_id| at the moment, or 0.
*/
static int spdylay_session_is_reply_allowed(spdylay_session *session,
int32_t stream_id)
{ {
spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); if(stream == NULL) {
if(stream == NULL || (stream->shut_flags & SPDYLAY_SHUT_WR)) { return SPDYLAY_ERR_STREAM_ALREADY_CLOSED;
return 0; } else if(stream->shut_flags & SPDYLAY_SHUT_WR) {
} return SPDYLAY_ERR_STREAM_SHUT_WR;
if(spdylay_session_is_my_stream_id(session, stream_id)) {
return 0;
} else { } else {
return stream->state == SPDYLAY_STREAM_OPENING; return 0;
} }
} }
/* /*
* Returns nonzero value if local endpoint can send HEADERS with * This function checks SYN_REPLY with the stream ID |stream_id| can
* stream ID |stream_id| at the moment. * be sent at this time.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_STREAM_ALREADY_CLOSED
* The stream is already closed or does not exist.
* SPDYLAY_ERR_STREAM_SHUT_WR
* The transmission is not allowed for this stream (e.g., a frame
* with FIN flag set has already sent)
* SPDYLAY_ERR_INVALID_STREAM_ID
* The stream ID is invalid.
* SPDYLAY_ERR_STREAM_CLOSING
* RST_STREAM was queued for this stream.
* SPDYLAY_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid (e.g., SYN_REPLY has
* already sent).
*/ */
static int spdylay_session_is_headers_allowed(spdylay_session *session, static int spdylay_session_predicate_syn_reply_send(spdylay_session *session,
int32_t stream_id) int32_t stream_id)
{ {
spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);
if(stream == NULL || (stream->shut_flags & SPDYLAY_SHUT_WR)) { int r;
return 0; r = spdylay_predicate_stream_for_send(stream);
if(r != 0) {
return r;
} }
if(spdylay_session_is_my_stream_id(session, stream_id)) { if(spdylay_session_is_my_stream_id(session, stream_id)) {
return stream->state != SPDYLAY_STREAM_CLOSING; return SPDYLAY_ERR_INVALID_STREAM_ID;
} else { } else {
return stream->state == SPDYLAY_STREAM_OPENED; if(stream->state == SPDYLAY_STREAM_OPENING) {
return 0;
} else if(stream->state == SPDYLAY_STREAM_CLOSING) {
return SPDYLAY_ERR_STREAM_CLOSING;
} else {
return SPDYLAY_ERR_INVALID_STREAM_STATE;
}
} }
} }
/* /*
* Returns nonzero value if local endpoint can send WINDOW_UPDATE with * This function checks HEADERS with the stream ID |stream_id| can
* stream ID |stream_id| at the moment. * be sent at this time.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_STREAM_ALREADY_CLOSED
* The stream is already closed or does not exist.
* SPDYLAY_ERR_STREAM_SHUT_WR
* The transmission is not allowed for this stream (e.g., a frame
* with FIN flag set has already sent)
* SPDYLAY_ERR_STREAM_CLOSING
* RST_STREAM was queued for this stream.
* SPDYLAY_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid (e.g., if the local peer
* is receiving side and SYN_REPLY has not been sent).
*/ */
static int spdylay_session_is_window_update_allowed(spdylay_session *session, static int spdylay_session_predicate_headers_send(spdylay_session *session,
int32_t stream_id)
{
spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);
int r;
r = spdylay_predicate_stream_for_send(stream);
if(r != 0) {
return r;
}
if(spdylay_session_is_my_stream_id(session, stream_id)) {
if(stream->state != SPDYLAY_STREAM_CLOSING) {
return 0;
} else {
return SPDYLAY_ERR_STREAM_CLOSING;
}
} else {
if(stream->state == SPDYLAY_STREAM_OPENED) {
return 0;
} else if(stream->state == SPDYLAY_STREAM_CLOSING) {
return SPDYLAY_ERR_STREAM_CLOSING;
} else {
return SPDYLAY_ERR_INVALID_STREAM_STATE;
}
}
}
/*
* This function checks WINDOW_UPDATE with the stream ID |stream_id|
* can be sent at this time. Note that FIN flag of the previous frame
* does not affect the transmission of the WINDOW_UPDATE frame.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_STREAM_ALREADY_CLOSED
* The stream is already closed or does not exist.
* SPDYLAY_ERR_STREAM_CLOSING
* RST_STREAM was queued for this stream.
*/
static int spdylay_session_predicate_window_update_send
(spdylay_session *session,
int32_t stream_id) int32_t stream_id)
{ {
spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);
if(stream == NULL) { if(stream == NULL) {
return 0; return SPDYLAY_ERR_STREAM_ALREADY_CLOSED;
}
if(stream->state != SPDYLAY_STREAM_CLOSING) {
return 0;
} else {
return SPDYLAY_ERR_STREAM_CLOSING;
} }
return stream->state != SPDYLAY_STREAM_CLOSING;
} }
/* /*
@ -506,18 +589,40 @@ static size_t spdylay_session_next_data_read(spdylay_session *session,
} }
} }
static int spdylay_session_is_data_allowed(spdylay_session *session, /*
* This function checks DATA with the stream ID |stream_id| can be
* sent at this time.
*
* This function returns 0 if it succeeds, or one of the following
* negative error codes:
*
* SPDYLAY_ERR_STREAM_ALREADY_CLOSED
* The stream is already closed or does not exist.
* SPDYLAY_ERR_STREAM_SHUT_WR
* The transmission is not allowed for this stream (e.g., a frame
* with FIN flag set has already sent)
* SPDYLAY_ERR_DEFERRED_DATA_EXIST
* Another DATA frame has already been deferred.
* SPDYLAY_ERR_STREAM_CLOSING
* RST_STREAM was queued for this stream.
* SPDYLAY_ERR_INVALID_STREAM_STATE
* The state of the stream is not valid (e.g., if the local peer
* is receiving side and SYN_REPLY has not been sent).
*/
static int spdylay_session_predicate_data_send(spdylay_session *session,
int32_t stream_id) int32_t stream_id)
{ {
spdylay_stream *stream = spdylay_session_get_stream(session, stream_id); spdylay_stream *stream = spdylay_session_get_stream(session, stream_id);
if(stream == NULL || (stream->shut_flags & SPDYLAY_SHUT_WR)) { int r;
return 0; r = spdylay_predicate_stream_for_send(stream);
if(r != 0) {
return r;
} }
if(stream->deferred_data != NULL) { if(stream->deferred_data != NULL) {
/* stream->deferred_data != NULL means previously queued DATA /* stream->deferred_data != NULL means previously queued DATA
frame has not been sent. We don't allow new DATA frame is sent frame has not been sent. We don't allow new DATA frame is sent
in this case. */ in this case. */
return 0; return SPDYLAY_ERR_DEFERRED_DATA_EXIST;
} }
if(spdylay_session_is_my_stream_id(session, stream_id)) { if(spdylay_session_is_my_stream_id(session, stream_id)) {
/* If stream->state is SPDYLAY_STREAM_CLOSING, RST_STREAM was /* If stream->state is SPDYLAY_STREAM_CLOSING, RST_STREAM was
@ -528,9 +633,19 @@ static int spdylay_session_is_data_allowed(spdylay_session *session,
frames are sent. This is not desirable situation; we want to frames are sent. This is not desirable situation; we want to
close stream as soon as possible. To achieve this, we remove close stream as soon as possible. To achieve this, we remove
DATA frame before RST_STREAM. */ DATA frame before RST_STREAM. */
return stream->state != SPDYLAY_STREAM_CLOSING; if(stream->state != SPDYLAY_STREAM_CLOSING) {
return 0;
} else { } else {
return stream->state == SPDYLAY_STREAM_OPENED; return SPDYLAY_ERR_STREAM_CLOSING;
}
} else {
if(stream->state == SPDYLAY_STREAM_OPENED) {
return 0;
} else if(stream->state == SPDYLAY_STREAM_CLOSING) {
return SPDYLAY_ERR_STREAM_CLOSING;
} else {
return SPDYLAY_ERR_INVALID_STREAM_STATE;
}
} }
} }
@ -542,14 +657,19 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
ssize_t framebuflen; ssize_t framebuflen;
switch(item->frame_type) { switch(item->frame_type) {
case SPDYLAY_SYN_STREAM: { case SPDYLAY_SYN_STREAM: {
uint32_t stream_id; int32_t stream_id;
spdylay_syn_stream_aux_data *aux_data; spdylay_syn_stream_aux_data *aux_data;
if(session->goaway_flags) { if(session->goaway_flags) {
/* When GOAWAY is sent or received, peer must not send new /* When GOAWAY is sent or received, peer must not send new
SYN_STREAM. */ SYN_STREAM. */
return SPDYLAY_ERR_INVALID_FRAME; return SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED;
}
/* All 32bit signed stream IDs are spent. */
if(session->next_stream_id > INT32_MAX) {
return SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE;
} }
stream_id = session->next_stream_id; stream_id = session->next_stream_id;
item->frame->syn_stream.stream_id = stream_id; item->frame->syn_stream.stream_id = stream_id;
session->next_stream_id += 2; session->next_stream_id += 2;
if(session->version == SPDYLAY_PROTO_SPDY2) { if(session->version == SPDYLAY_PROTO_SPDY2) {
@ -578,9 +698,11 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
break; break;
} }
case SPDYLAY_SYN_REPLY: { case SPDYLAY_SYN_REPLY: {
if(!spdylay_session_is_reply_allowed(session, int r;
item->frame->syn_reply.stream_id)) { r = spdylay_session_predicate_syn_reply_send
return SPDYLAY_ERR_INVALID_FRAME; (session, item->frame->syn_reply.stream_id);
if(r != 0) {
return r;
} }
if(session->version == SPDYLAY_PROTO_SPDY2) { if(session->version == SPDYLAY_PROTO_SPDY2) {
spdylay_frame_nv_3to2(item->frame->syn_reply.nv); spdylay_frame_nv_3to2(item->frame->syn_reply.nv);
@ -628,9 +750,11 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
} }
break; break;
case SPDYLAY_HEADERS: { case SPDYLAY_HEADERS: {
if(!spdylay_session_is_headers_allowed(session, int r;
item->frame->headers.stream_id)) { r = spdylay_session_predicate_headers_send(session,
return SPDYLAY_ERR_INVALID_FRAME; item->frame->headers.stream_id);
if(r != 0) {
return r;
} }
if(session->version == SPDYLAY_PROTO_SPDY2) { if(session->version == SPDYLAY_PROTO_SPDY2) {
spdylay_frame_nv_3to2(item->frame->headers.nv); spdylay_frame_nv_3to2(item->frame->headers.nv);
@ -650,9 +774,11 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
break; break;
} }
case SPDYLAY_WINDOW_UPDATE: { case SPDYLAY_WINDOW_UPDATE: {
if(!spdylay_session_is_window_update_allowed int r;
(session, item->frame->window_update.stream_id)) { r = spdylay_session_predicate_window_update_send
return SPDYLAY_ERR_INVALID_FRAME; (session, item->frame->window_update.stream_id);
if(r != 0) {
return r;
} }
framebuflen = spdylay_frame_pack_window_update(&session->aob.framebuf, framebuflen = spdylay_frame_pack_window_update(&session->aob.framebuf,
&session->aob.framebufmax, &session->aob.framebufmax,
@ -668,7 +794,7 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
exchange GOAWAY. This implementation allows receiver of first exchange GOAWAY. This implementation allows receiver of first
GOAWAY can sent its own GOAWAY to tell the remote peer that GOAWAY can sent its own GOAWAY to tell the remote peer that
last-good-stream-id. */ last-good-stream-id. */
return SPDYLAY_ERR_INVALID_FRAME; return SPDYLAY_ERR_GOAWAY_ALREADY_SENT;
} }
framebuflen = spdylay_frame_pack_goaway(&session->aob.framebuf, framebuflen = spdylay_frame_pack_goaway(&session->aob.framebuf,
&session->aob.framebufmax, &session->aob.framebufmax,
@ -680,8 +806,11 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
case SPDYLAY_DATA: { case SPDYLAY_DATA: {
size_t next_readmax; size_t next_readmax;
spdylay_stream *stream; spdylay_stream *stream;
if(!spdylay_session_is_data_allowed(session, item->frame->data.stream_id)) { int r;
return SPDYLAY_ERR_INVALID_FRAME; r = spdylay_session_predicate_data_send(session,
item->frame->data.stream_id);
if(r != 0) {
return r;
} }
stream = spdylay_session_get_stream(session, item->frame->data.stream_id); stream = spdylay_session_get_stream(session, item->frame->data.stream_id);
/* Assuming stream is not NULL */ /* Assuming stream is not NULL */
@ -935,7 +1064,8 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
/* If session is closed or RST_STREAM was queued, we won't send /* If session is closed or RST_STREAM was queued, we won't send
further data. */ further data. */
if(frame->data.eof || if(frame->data.eof ||
!spdylay_session_is_data_allowed(session, frame->data.stream_id)) { spdylay_session_predicate_data_send(session,
frame->data.stream_id) != 0) {
spdylay_active_outbound_item_reset(&session->aob); spdylay_active_outbound_item_reset(&session->aob);
} else { } else {
spdylay_outbound_item* item = spdylay_session_get_next_ob_item(session); spdylay_outbound_item* item = spdylay_session_get_next_ob_item(session);
@ -1012,7 +1142,18 @@ int spdylay_session_send(spdylay_session *session)
if(framebuflen == SPDYLAY_ERR_DEFERRED) { if(framebuflen == SPDYLAY_ERR_DEFERRED) {
continue; continue;
} else if(framebuflen < 0) { } else if(framebuflen < 0) {
/* TODO Call error callback? */ /* The library is responsible for the transmission of
WINDOW_UPDATE frame, so we don't call error callback for
it. */
if(session->callbacks.on_ctrl_not_send_callback &&
spdylay_is_non_fatal(framebuflen) &&
item->frame_type != SPDYLAY_WINDOW_UPDATE) {
session->callbacks.on_ctrl_not_send_callback(session,
item->frame_type,
item->frame,
framebuflen,
session->user_data);
}
spdylay_outbound_item_free(item); spdylay_outbound_item_free(item);
free(item); free(item);
if(framebuflen < SPDYLAY_ERR_FATAL) { if(framebuflen < SPDYLAY_ERR_FATAL) {
@ -1510,14 +1651,6 @@ static int spdylay_session_fail_session(spdylay_session *session,
return spdylay_submit_goaway(session, status_code); return spdylay_submit_goaway(session, status_code);
} }
/*
* Returns non-zero if |error| is non-fatal error.
*/
static int spdylay_is_non_fatal(int error)
{
return error < 0 && error > SPDYLAY_ERR_FATAL;
}
/* For errors, this function only returns FATAL error. */ /* For errors, this function only returns FATAL error. */
static int spdylay_session_process_ctrl_frame(spdylay_session *session) static int spdylay_session_process_ctrl_frame(spdylay_session *session)
{ {

View File

@ -109,7 +109,8 @@ struct spdylay_session {
SPDYLAY_PROTO_SPDY3 */ SPDYLAY_PROTO_SPDY3 */
uint16_t version; uint16_t version;
uint8_t server; uint8_t server;
int32_t next_stream_id; /* Next Stream ID. Made unsigned int to detect >= (1 << 31). */
uint32_t next_stream_id;
int32_t last_recv_stream_id; int32_t last_recv_stream_id;
/* Counter of unique ID of PING. Wraps when it exceeds /* Counter of unique ID of PING. Wraps when it exceeds
SPDYLAY_MAX_UNIQUE_ID */ SPDYLAY_MAX_UNIQUE_ID */

View File

@ -136,6 +136,8 @@ int main(int argc, char* argv[])
test_spdylay_session_defer_data) || test_spdylay_session_defer_data) ||
!CU_add_test(pSuite, "session_flow_control", !CU_add_test(pSuite, "session_flow_control",
test_spdylay_session_flow_control) || test_spdylay_session_flow_control) ||
!CU_add_test(pSuite, "session_on_ctrl_not_send",
test_spdylay_session_on_ctrl_not_send) ||
!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

@ -52,6 +52,9 @@ typedef struct {
int ctrl_recv_cb_called, invalid_ctrl_recv_cb_called; int ctrl_recv_cb_called, invalid_ctrl_recv_cb_called;
int ctrl_send_cb_called; int ctrl_send_cb_called;
spdylay_frame_type sent_frame_type; spdylay_frame_type sent_frame_type;
int ctrl_not_send_cb_called;
spdylay_frame_type not_sent_frame_type;
int not_sent_error;
int stream_close_cb_called; int stream_close_cb_called;
size_t data_source_length; size_t data_source_length;
int32_t stream_id; int32_t stream_id;
@ -137,6 +140,18 @@ static void on_ctrl_send_callback(spdylay_session *session,
ud->sent_frame_type = type; ud->sent_frame_type = type;
} }
static void on_ctrl_not_send_callback(spdylay_session *session,
spdylay_frame_type type,
spdylay_frame *frame,
int error,
void *user_data)
{
my_user_data *ud = (my_user_data*)user_data;
++ud->ctrl_not_send_cb_called;
ud->not_sent_frame_type = type;
ud->not_sent_error = error;
}
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,
@ -1691,3 +1706,116 @@ void test_spdylay_session_flow_control()
spdylay_frame_window_update_free(&frame.window_update); spdylay_frame_window_update_free(&frame.window_update);
spdylay_session_del(session); spdylay_session_del(session);
} }
void test_spdylay_session_on_ctrl_not_send()
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
my_user_data user_data;
spdylay_stream *stream;
const char *nv[] = { NULL };
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.on_ctrl_not_send_callback = on_ctrl_not_send_callback;
callbacks.send_callback = null_send_callback;
user_data.ctrl_not_send_cb_called = 0;
user_data.not_sent_frame_type = 0;
user_data.not_sent_error = 0;
CU_ASSERT(spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2,
&callbacks, &user_data) == 0);
stream = spdylay_session_open_stream(session, 1,
SPDYLAY_CTRL_FLAG_NONE,
3, SPDYLAY_STREAM_OPENED, &user_data);
/* Check SYN_REPLY */
CU_ASSERT(0 ==
spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_INVALID_STREAM_STATE == user_data.not_sent_error);
stream->state = SPDYLAY_STREAM_OPENING;
user_data.ctrl_not_send_cb_called = 0;
/* Send bogus stream ID */
CU_ASSERT(0 ==
spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_STREAM_ALREADY_CLOSED == user_data.not_sent_error);
user_data.ctrl_not_send_cb_called = 0;
/* Shutdown transmission */
stream->shut_flags |= SPDYLAY_SHUT_WR;
CU_ASSERT(0 ==
spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_STREAM_SHUT_WR == user_data.not_sent_error);
stream->shut_flags = SPDYLAY_SHUT_NONE;
user_data.ctrl_not_send_cb_called = 0;
/* Queue RST_STREAM */
CU_ASSERT(0 ==
spdylay_submit_syn_reply(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv));
CU_ASSERT(0 == spdylay_submit_rst_stream(session, 1, SPDYLAY_INTERNAL_ERROR));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_REPLY == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSING == user_data.not_sent_error);
stream = spdylay_session_open_stream(session, 3,
SPDYLAY_CTRL_FLAG_NONE,
3, SPDYLAY_STREAM_OPENED, &user_data);
/* Check HEADERS */
user_data.ctrl_not_send_cb_called = 0;
stream->state = SPDYLAY_STREAM_OPENING;
CU_ASSERT(0 ==
spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_HEADERS == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_INVALID_STREAM_STATE == user_data.not_sent_error);
stream->state = SPDYLAY_STREAM_OPENED;
user_data.ctrl_not_send_cb_called = 0;
/* Queue RST_STREAM */
CU_ASSERT(0 ==
spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 3, nv));
CU_ASSERT(0 == spdylay_submit_rst_stream(session, 3, SPDYLAY_INTERNAL_ERROR));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_HEADERS == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_STREAM_CLOSING == user_data.not_sent_error);
spdylay_session_del(session);
/* Check SYN_STREAM */
user_data.ctrl_not_send_cb_called = 0;
CU_ASSERT(spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2,
&callbacks, &user_data) == 0);
/* Maximum Stream ID is reached */
session->next_stream_id = (1u << 31)+1;
CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 0,
3, nv, NULL));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_STREAM_ID_NOT_AVAILABLE == user_data.not_sent_error);
session->next_stream_id = 1;
user_data.ctrl_not_send_cb_called = 0;
/* Send GOAWAY */
CU_ASSERT(0 == spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK));
CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_CTRL_FLAG_FIN, 0,
3, nv, NULL));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_not_send_cb_called);
CU_ASSERT(SPDYLAY_SYN_STREAM == user_data.not_sent_frame_type);
CU_ASSERT(SPDYLAY_ERR_SYN_STREAM_NOT_ALLOWED == user_data.not_sent_error);
spdylay_session_del(session);
}

View File

@ -60,5 +60,6 @@ void test_spdylay_session_stream_close_on_syn_stream();
void test_spdylay_session_recv_invalid_frame(); void test_spdylay_session_recv_invalid_frame();
void test_spdylay_session_defer_data(); void test_spdylay_session_defer_data();
void test_spdylay_session_flow_control(); void test_spdylay_session_flow_control();
void test_spdylay_session_on_ctrl_not_send();
#endif // SPDYLAY_SESSION_TEST_H #endif // SPDYLAY_SESSION_TEST_H