Send GOAWAY when spdylay_frame_unpack_* is failed with non-fatal error.

SPDYLAY_ERR_ZLIB is now moved back to non-fatal error and it is subject
to GOAWAY if it occurred.
This commit is contained in:
Tatsuhiro Tsujikawa 2012-02-18 21:55:40 +09:00
parent b182179aca
commit 03307116a2
6 changed files with 109 additions and 3 deletions

View File

@ -43,6 +43,7 @@ typedef struct spdylay_session spdylay_session;
typedef enum { typedef enum {
SPDYLAY_ERR_INVALID_ARGUMENT = -501, SPDYLAY_ERR_INVALID_ARGUMENT = -501,
SPDYLAY_ERR_ZLIB = -502,
SPDYLAY_ERR_WOULDBLOCK = -504, SPDYLAY_ERR_WOULDBLOCK = -504,
SPDYLAY_ERR_PROTO = -505, SPDYLAY_ERR_PROTO = -505,
SPDYLAY_ERR_INVALID_FRAME = -506, SPDYLAY_ERR_INVALID_FRAME = -506,
@ -54,7 +55,6 @@ typedef enum {
SPDYLAY_ERR_FATAL = -900, SPDYLAY_ERR_FATAL = -900,
SPDYLAY_ERR_NOMEM = -901, SPDYLAY_ERR_NOMEM = -901,
SPDYLAY_ERR_CALLBACK_FAILURE = -902, SPDYLAY_ERR_CALLBACK_FAILURE = -902,
SPDYLAY_ERR_ZLIB = -903,
} spdylay_error; } spdylay_error;
typedef enum { typedef enum {
@ -373,12 +373,20 @@ int spdylay_session_recv(spdylay_session *session);
/* /*
* Returns non-zero value if |session| want to receive data from the * Returns non-zero value if |session| want to receive data from the
* remote peer, or 0. * remote peer, or 0.
*
* if both spdylay_session_want_read() and
* spdylay_session_want_write() return 0, the application should drop
* the connection.
*/ */
int spdylay_session_want_read(spdylay_session *session); int spdylay_session_want_read(spdylay_session *session);
/* /*
* Returns non-zero value if |session| want to send data to the remote * Returns non-zero value if |session| want to send data to the remote
* peer, or 0. * peer, or 0.
*
* if both spdylay_session_want_read() and
* spdylay_session_want_write() return 0, the application should drop
* the connection.
*/ */
int spdylay_session_want_write(spdylay_session *session); int spdylay_session_want_write(spdylay_session *session);

View File

@ -1299,6 +1299,25 @@ int spdylay_session_on_headers_received(spdylay_session *session,
return r; return r;
} }
/*
* This function should be called when the session wants to drop
* connection after sending GOAWAY. For example, when it receives bad
* zlib data.
*/
static int spdylay_session_fail_session(spdylay_session *session)
{
session->goaway_flags |= SPDYLAY_GOAWAY_FAIL_ON_SEND;
return spdylay_submit_goaway(session);
}
/*
* Returns non-zero if |error| is non-fatal error.
*/
static int spdylay_is_non_fatal(int error)
{
return error < 0 && error > SPDYLAY_ERR_FATAL;
}
static int spdylay_session_process_ctrl_frame(spdylay_session *session) static int spdylay_session_process_ctrl_frame(spdylay_session *session)
{ {
int r = 0; int r = 0;
@ -1321,10 +1340,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_syn_stream_received(session, &frame); r = spdylay_session_on_syn_stream_received(session, &frame);
spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_frame_syn_stream_free(&frame.syn_stream);
} else {
/* TODO if r indicates mulformed NV pairs (multiple nulls) or /* TODO if r indicates mulformed NV pairs (multiple nulls) or
invalid frame, send RST_STREAM with PROTOCOL_ERROR. Same for invalid frame, send RST_STREAM with PROTOCOL_ERROR. Same for
other control frames. */ other control frames. */
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
case SPDYLAY_SYN_REPLY: case SPDYLAY_SYN_REPLY:
@ -1341,6 +1361,8 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_syn_reply_received(session, &frame); r = spdylay_session_on_syn_reply_received(session, &frame);
spdylay_frame_syn_reply_free(&frame.syn_reply); spdylay_frame_syn_reply_free(&frame.syn_reply);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
case SPDYLAY_RST_STREAM: case SPDYLAY_RST_STREAM:
@ -1352,6 +1374,8 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_rst_stream_received(session, &frame); r = spdylay_session_on_rst_stream_received(session, &frame);
spdylay_frame_rst_stream_free(&frame.rst_stream); spdylay_frame_rst_stream_free(&frame.rst_stream);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
case SPDYLAY_SETTINGS: case SPDYLAY_SETTINGS:
@ -1363,6 +1387,8 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_settings_received(session, &frame); r = spdylay_session_on_settings_received(session, &frame);
spdylay_frame_settings_free(&frame.settings); spdylay_frame_settings_free(&frame.settings);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
case SPDYLAY_NOOP: case SPDYLAY_NOOP:
@ -1376,6 +1402,8 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_ping_received(session, &frame); r = spdylay_session_on_ping_received(session, &frame);
spdylay_frame_ping_free(&frame.ping); spdylay_frame_ping_free(&frame.ping);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
case SPDYLAY_GOAWAY: case SPDYLAY_GOAWAY:
@ -1387,6 +1415,8 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_goaway_received(session, &frame); r = spdylay_session_on_goaway_received(session, &frame);
spdylay_frame_goaway_free(&frame.goaway); spdylay_frame_goaway_free(&frame.goaway);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
case SPDYLAY_HEADERS: case SPDYLAY_HEADERS:
@ -1403,6 +1433,8 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
if(r == 0) { if(r == 0) {
r = spdylay_session_on_headers_received(session, &frame); r = spdylay_session_on_headers_received(session, &frame);
spdylay_frame_headers_free(&frame.headers); spdylay_frame_headers_free(&frame.headers);
} else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session);
} }
break; break;
} }
@ -1577,6 +1609,12 @@ int spdylay_session_recv(spdylay_session *session)
int spdylay_session_want_read(spdylay_session *session) int spdylay_session_want_read(spdylay_session *session)
{ {
/* If these flags are set, we don't want to read. The application
should drop the connection. */
if((session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND) &&
(session->goaway_flags & SPDYLAY_GOAWAY_SEND)) {
return 0;
}
/* Unless GOAWAY is sent or received, we always want to read /* Unless GOAWAY is sent or received, we always want to read
incoming frames. After GOAWAY is sent or received, we are only incoming frames. After GOAWAY is sent or received, we are only
interested in active streams. */ interested in active streams. */
@ -1585,6 +1623,12 @@ int spdylay_session_want_read(spdylay_session *session)
int spdylay_session_want_write(spdylay_session *session) int spdylay_session_want_write(spdylay_session *session)
{ {
/* If these flags are set, we don't want to write any data. The
application should drop the connection. */
if((session->goaway_flags & SPDYLAY_GOAWAY_FAIL_ON_SEND) &&
(session->goaway_flags & SPDYLAY_GOAWAY_SEND)) {
return 0;
}
/* /*
* Unless GOAWAY is sent or received, we want to write frames if * Unless GOAWAY is sent or received, we want to write frames if
* there is pending ones. If pending frame is SYN_STREAM and * there is pending ones. If pending frame is SYN_STREAM and

View File

@ -104,7 +104,9 @@ typedef enum {
/* Flag means GOAWAY frame is sent to the remote peer. */ /* Flag means GOAWAY frame is sent to the remote peer. */
SPDYLAY_GOAWAY_SEND = 0x1, SPDYLAY_GOAWAY_SEND = 0x1,
/* Flag means GOAWAY frame is received from the remote peer. */ /* Flag means GOAWAY frame is received from the remote peer. */
SPDYLAY_GOAWAY_RECV = 0x2 SPDYLAY_GOAWAY_RECV = 0x2,
/* Flag means connection should be dropped after sending GOAWAY. */
SPDYLAY_GOAWAY_FAIL_ON_SEND = 0x4
} spdylay_goaway_flag; } spdylay_goaway_flag;
struct spdylay_session { struct spdylay_session {

View File

@ -119,6 +119,8 @@ int main(int argc, char* argv[])
test_spdylay_session_stop_data_with_rst_stream) || test_spdylay_session_stop_data_with_rst_stream) ||
!CU_add_test(pSuite, "session_stream_close_on_syn_stream", !CU_add_test(pSuite, "session_stream_close_on_syn_stream",
test_spdylay_session_stream_close_on_syn_stream) || test_spdylay_session_stream_close_on_syn_stream) ||
!CU_add_test(pSuite, "session_recv_invalid_frame",
test_spdylay_session_recv_invalid_frame) ||
!CU_add_test(pSuite, "frame_unpack_nv", test_spdylay_frame_unpack_nv) || !CU_add_test(pSuite, "frame_unpack_nv", test_spdylay_frame_unpack_nv) ||
!CU_add_test(pSuite, "frame_count_nv_space", !CU_add_test(pSuite, "frame_count_nv_space",
test_spdylay_frame_count_nv_space) || test_spdylay_frame_count_nv_space) ||

View File

@ -1214,3 +1214,52 @@ void test_spdylay_session_stream_close_on_syn_stream()
spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_frame_syn_stream_free(&frame.syn_stream);
spdylay_session_del(session); spdylay_session_del(session);
} }
void test_spdylay_session_recv_invalid_frame()
{
spdylay_session *session;
spdylay_session_callbacks callbacks;
scripted_data_feed df;
my_user_data user_data;
const char *nv[] = {
"url", "/", NULL
};
uint8_t *framedata = NULL, *nvbuf = NULL;
size_t framedatalen = 0, nvbuflen = 0;
ssize_t framelen;
spdylay_frame frame;
memset(&callbacks, 0, sizeof(spdylay_session_callbacks));
callbacks.recv_callback = scripted_recv_callback;
callbacks.send_callback = null_send_callback;
callbacks.on_ctrl_send_callback = on_ctrl_send_callback;
user_data.df = &df;
user_data.ctrl_send_cb_called = 0;
spdylay_session_server_new(&session, &callbacks, &user_data);
spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, 1, 0, 3,
dup_nv(nv));
framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen,
&nvbuf, &nvbuflen,
&frame.syn_stream,
&session->hd_deflater);
scripted_data_feed_init(&df, framedata, framelen);
CU_ASSERT(0 == spdylay_session_recv(session));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(0 == user_data.ctrl_send_cb_called);
/* Receive exactly same bytes of SYN_STREAM causes error */
scripted_data_feed_init(&df, framedata, framelen);
CU_ASSERT(0 == spdylay_session_recv(session));
CU_ASSERT(0 == spdylay_session_send(session));
CU_ASSERT(1 == user_data.ctrl_send_cb_called);
CU_ASSERT(SPDYLAY_GOAWAY == user_data.sent_frame_type);
free(framedata);
free(nvbuf);
spdylay_frame_syn_stream_free(&frame.syn_stream);
spdylay_session_del(session);
}

View File

@ -51,5 +51,6 @@ void test_spdylay_session_max_concurrent_streams();
void test_spdylay_session_data_backoff_by_high_pri_frame(); void test_spdylay_session_data_backoff_by_high_pri_frame();
void test_spdylay_session_stop_data_with_rst_stream(); void test_spdylay_session_stop_data_with_rst_stream();
void test_spdylay_session_stream_close_on_syn_stream(); void test_spdylay_session_stream_close_on_syn_stream();
void test_spdylay_session_recv_invalid_frame();
#endif // SPDYLAY_SESSION_TEST_H #endif // SPDYLAY_SESSION_TEST_H