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:
parent
b182179aca
commit
03307116a2
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) ||
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue