spdylay_data_source_read_callback can return

SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE to signal stream error.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-05-12 18:19:05 +09:00
parent 42c0e3c758
commit 5c187b950f
8 changed files with 110 additions and 7 deletions

View File

@ -402,7 +402,7 @@ ssize_t file_read_callback
ssize_t r;
while((r = read(fd, buf, length)) == -1 && errno == EINTR);
if(r == -1) {
return SPDYLAY_ERR_CALLBACK_FAILURE;
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
} else {
if(r == 0) {
*eof = 1;

View File

@ -164,6 +164,10 @@ typedef enum {
* The gzip error.
*/
SPDYLAY_ERR_GZIP = -520,
/**
* The user callback function failed due to the temporal error.
*/
SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE = -521,
/**
* The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is
* under unexpected condition and cannot process any further data
@ -737,8 +741,11 @@ typedef union {
* reading any data in this invocation. The library removes DATA
* frame from the outgoing queue temporarily. To move back deferred
* DATA frame to outgoing queue, call `spdylay_session_resume_data()`.
* In case of error, return :enum:`SPDYLAY_ERR_CALLBACK_FAILURE`,
* which leads to session failure.
* In case of error, there are 2 choices. Returning
* :enum:`SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE` will close the stream
* by issuing RST_STREAM with :enum:`SPDYLAY_INTERNAL_ERROR`.
* Returning :enum:`SPDYLAY_ERR_CALLBACK_FAILURE` will signal the
* entire session failure.
*/
typedef ssize_t (*spdylay_data_source_read_callback)
(spdylay_session *session, int32_t stream_id,

View File

@ -115,6 +115,8 @@ const char* spdylay_strerror(int error_code)
return "Invalid state";
case SPDYLAY_ERR_GZIP:
return "Gzip error";
case SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE:
return "The user callback function failed due to the temporal error";
case SPDYLAY_ERR_NOMEM:
return "Out of memory";
case SPDYLAY_ERR_CALLBACK_FAILURE:

View File

@ -1123,6 +1123,14 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session,
if(framebuflen == SPDYLAY_ERR_DEFERRED) {
spdylay_stream_defer_data(stream, item, SPDYLAY_DEFERRED_NONE);
return SPDYLAY_ERR_DEFERRED;
} else if(framebuflen == SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE) {
r = spdylay_session_add_rst_stream(session, data_frame->stream_id,
SPDYLAY_INTERNAL_ERROR);
if(r == 0) {
return framebuflen;
} else {
return r;
}
} else if(framebuflen < 0) {
return framebuflen;
}
@ -1395,6 +1403,16 @@ static int spdylay_session_after_frame_sent(spdylay_session *session)
SPDYLAY_DEFERRED_NONE);
session->aob.item = NULL;
spdylay_active_outbound_item_reset(&session->aob);
} else if(r == SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE) {
/* Stop DATA frame chain and issue RST_STREAM to close the
stream. We don't return
SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE intentionally. */
r = spdylay_session_add_rst_stream(session, data_frame->stream_id,
SPDYLAY_INTERNAL_ERROR);
spdylay_active_outbound_item_reset(&session->aob);
if(r != 0) {
return r;
}
} else if(r < 0) {
/* In this context, r is either SPDYLAY_ERR_NOMEM or
SPDYLAY_ERR_CALLBACK_FAILURE */
@ -2606,7 +2624,7 @@ ssize_t spdylay_session_pack_data(spdylay_session *session,
r = frame->data_prd.read_callback
(session, frame->stream_id, (*buf_ptr)+8, datamax,
&eof, &frame->data_prd.source, session->user_data);
if(r == SPDYLAY_ERR_DEFERRED) {
if(r == SPDYLAY_ERR_DEFERRED || r == SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE) {
return r;
} else if(r < 0 || datamax < (size_t)r) {
/* This is the error code when callback is failed. */

View File

@ -484,12 +484,14 @@ spdylay_stream* spdylay_session_get_stream(spdylay_session *session,
* This function returns the size of packed frame if it succeeds, or
* one of the following negative error codes:
*
* SPDYLAY_ERR_NOMEM
* Out of memory.
* SPDYLAY_ERR_DEFERRED
* The DATA frame is postponed.
* SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE
* The read_callback failed (stream error).
* SPDYLAY_ERR_NOMEM
* Out of memory.
* SPDYLAY_ERR_CALLBACK_FAILURE
* The read_callback failed.
* The read_callback failed (session error).
*/
ssize_t spdylay_session_pack_data(spdylay_session *session,
uint8_t **buf_ptr, size_t *buflen_ptr,

View File

@ -156,6 +156,8 @@ int main(int argc, char* argv[])
test_spdylay_session_set_option) ||
!CU_add_test(pSuite, "submit_window_update",
test_spdylay_submit_window_update) ||
!CU_add_test(pSuite, "session_data_read_temporal_failure",
test_spdylay_session_data_read_temporal_failure) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy2",
test_spdylay_frame_unpack_nv_spdy2) ||
!CU_add_test(pSuite, "frame_unpack_nv_spdy3",

View File

@ -176,6 +176,22 @@ static ssize_t fixed_length_data_source_read_callback
return wlen;
}
static ssize_t temporal_failure_data_source_read_callback
(spdylay_session *session, int32_t stream_id,
uint8_t *buf, size_t len, int *eof,
spdylay_data_source *source, void *user_data)
{
return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE;
}
static ssize_t fail_data_source_read_callback
(spdylay_session *session, int32_t stream_id,
uint8_t *buf, size_t len, int *eof,
spdylay_data_source *source, void *user_data)
{
return SPDYLAY_ERR_CALLBACK_FAILURE;
}
static void on_request_recv_callback(spdylay_session *session,
int32_t stream_id,
void *user_data)
@ -2328,3 +2344,58 @@ void test_spdylay_submit_window_update(void)
spdylay_session_del(session);
}
void test_spdylay_session_data_read_temporal_failure(void)
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
const char *nv[] = { NULL };
my_user_data ud;
spdylay_data_provider data_prd;
spdylay_frame frame;
spdylay_data *data_frame;
spdylay_stream *stream;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.send_callback = null_send_callback;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback;
data_prd.read_callback = fixed_length_data_source_read_callback;
ud.data_source_length = 128*1024;
/* Initial window size is 64KiB */
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY3, &callbacks, &ud);
spdylay_submit_request(session, 3, nv, &data_prd, NULL);
/* Sends 64KiB data */
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(64*1024 == ud.data_source_length);
stream = spdylay_session_get_stream(session, 1);
CU_ASSERT(NULL != stream->deferred_data);
CU_ASSERT(SPDYLAY_DATA == stream->deferred_data->frame_cat);
data_frame = (spdylay_data*)stream->deferred_data->frame;
data_frame->data_prd.read_callback =
temporal_failure_data_source_read_callback;
/* Back 64KiB */
spdylay_frame_window_update_init(&frame.window_update, SPDYLAY_PROTO_SPDY3,
1, 64*1024);
spdylay_session_on_window_update_received(session, &frame);
spdylay_frame_window_update_free(&frame.window_update);
/* Sending data will fail */
ud.ctrl_send_cb_called = 0;
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(64*1024 == ud.data_source_length);
CU_ASSERT(1 == ud.ctrl_send_cb_called);
CU_ASSERT(SPDYLAY_RST_STREAM == ud.sent_frame_type);
data_prd.read_callback = fail_data_source_read_callback;
spdylay_submit_request(session, 3, nv, &data_prd, NULL);
/* Sending data will fail */
CU_ASSERT(SPDYLAY_ERR_CALLBACK_FAILURE == spdylay_session_send(session));
spdylay_session_del(session);
}

View File

@ -69,5 +69,6 @@ void test_spdylay_submit_syn_stream_with_credential(void);
void test_spdylay_session_set_initial_client_cert_origin(void);
void test_spdylay_session_set_option(void);
void test_spdylay_submit_window_update(void);
void test_spdylay_session_data_read_temporal_failure(void);
#endif /* SPDYLAY_SESSION_TEST_H */