Added status_code handling to GOAWAY

This commit is contained in:
Tatsuhiro Tsujikawa 2012-02-26 16:26:38 +09:00
parent 88cd97843f
commit d5cc71c636
11 changed files with 83 additions and 34 deletions

View File

@ -121,7 +121,7 @@ void on_stream_close_callback
if(itr != stream2req.end()) { if(itr != stream2req.end()) {
++complete; ++complete;
if(complete == numreq) { if(complete == numreq) {
spdylay_submit_goaway(session); spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK);
} }
} }
} }

View File

@ -116,7 +116,10 @@ typedef enum {
/* Default maximum concurrent streams */ /* Default maximum concurrent streams */
#define SPDYLAY_CONCURRENT_STREAMS_MAX 100 #define SPDYLAY_CONCURRENT_STREAMS_MAX 100
/* Status code for RST_STREAM */
typedef enum { typedef enum {
/* SPDYLAY_OK is not valid status code for RST_STREAM. It is defined
just for spdylay library use. */
SPDYLAY_OK = 0, SPDYLAY_OK = 0,
SPDYLAY_PROTOCOL_ERROR = 1, SPDYLAY_PROTOCOL_ERROR = 1,
SPDYLAY_INVALID_STREAM = 2, SPDYLAY_INVALID_STREAM = 2,
@ -124,9 +127,21 @@ typedef enum {
SPDYLAY_UNSUPPORTED_VERSION = 4, SPDYLAY_UNSUPPORTED_VERSION = 4,
SPDYLAY_CANCEL = 5, SPDYLAY_CANCEL = 5,
SPDYLAY_INTERNAL_ERROR = 6, SPDYLAY_INTERNAL_ERROR = 6,
SPDYLAY_FLOW_CONTROL_ERROR = 7 SPDYLAY_FLOW_CONTROL_ERROR = 7,
/* Following status codes were introduced in SPDY/3 */
SPDYLAY_STREAM_IN_USE = 8,
SPDYLAY_STREAM_ALREADY_CLOSED = 9,
SPDYLAY_INVALID_CREDENTIALS = 10,
FRAME_TOO_LARGE = 11
} spdylay_status_code; } spdylay_status_code;
/* Status code for GOAWAY, introduced in SPDY/3 */
typedef enum {
SPDYLAY_GOAWAY_OK = 0,
SPDYLAY_GOAWAY_PROTOCOL_ERROR = 1,
SPDYLAY_GOAWAY_INTERNAL_ERROR = 11
} spdylay_goaway_status_code;
#define SPDYLAY_SPDY2_PRI_LOWEST 3 #define SPDYLAY_SPDY2_PRI_LOWEST 3
#define SPDYLAY_SPDY3_PRI_LOWEST 7 #define SPDYLAY_SPDY3_PRI_LOWEST 7
@ -643,7 +658,8 @@ int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id,
int spdylay_submit_ping(spdylay_session *session); int spdylay_submit_ping(spdylay_session *session);
/* /*
* Submits GOAWAY frame. * Submits GOAWAY frame. The status code |status_code| is ignored if
* the protocol version is SPDYLAY_PROTO_SPDY2.
* *
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
@ -651,7 +667,7 @@ int spdylay_submit_ping(spdylay_session *session);
* SPDYLAY_ERR_NOMEM * SPDYLAY_ERR_NOMEM
* Out of memory. * Out of memory.
*/ */
int spdylay_submit_goaway(spdylay_session *session); int spdylay_submit_goaway(spdylay_session *session, uint32_t status_code);
/* /*
* A helper function for dealing with NPN in client side. * A helper function for dealing with NPN in client side.

View File

@ -449,7 +449,8 @@ void spdylay_frame_ping_free(spdylay_ping *frame)
{} {}
void spdylay_frame_goaway_init(spdylay_goaway *frame, void spdylay_frame_goaway_init(spdylay_goaway *frame,
uint16_t version, int32_t last_good_stream_id) uint16_t version, int32_t last_good_stream_id,
uint32_t status_code)
{ {
memset(frame, 0, sizeof(spdylay_goaway)); memset(frame, 0, sizeof(spdylay_goaway));
frame->hd.version = version; frame->hd.version = version;
@ -458,11 +459,11 @@ void spdylay_frame_goaway_init(spdylay_goaway *frame,
frame->hd.length = 4; frame->hd.length = 4;
} else if(version == SPDYLAY_PROTO_SPDY3) { } else if(version == SPDYLAY_PROTO_SPDY3) {
frame->hd.length = 8; frame->hd.length = 8;
frame->status_code = status_code;
} else { } else {
frame->hd.length = 0; frame->hd.length = 0;
} }
frame->last_good_stream_id = last_good_stream_id; frame->last_good_stream_id = last_good_stream_id;
frame->status_code = 0; /* TODO Add status_code arg for spdy/3 */
} }
void spdylay_frame_goaway_free(spdylay_goaway *frame) void spdylay_frame_goaway_free(spdylay_goaway *frame)

View File

@ -463,8 +463,13 @@ void spdylay_frame_ping_init(spdylay_ping *frame, uint16_t version,
void spdylay_frame_ping_free(spdylay_ping *frame); void spdylay_frame_ping_free(spdylay_ping *frame);
/*
* Initializes GOAWAY frame |frame| with given values. The
* |status_code| is ignored if |version| == SPDYLAY_PROTO_SPDY2.
*/
void spdylay_frame_goaway_init(spdylay_goaway *frame, uint16_t version, void spdylay_frame_goaway_init(spdylay_goaway *frame, uint16_t version,
int32_t last_good_stream_id); int32_t last_good_stream_id,
uint32_t status_code);
void spdylay_frame_goaway_free(spdylay_goaway *frame); void spdylay_frame_goaway_free(spdylay_goaway *frame);

View File

@ -1489,13 +1489,14 @@ int spdylay_session_on_headers_received(spdylay_session *session,
/* /*
* This function should be called when the session wants to drop * This function should be called when the session wants to drop
* connection after sending GOAWAY. For example, when it receives bad * connection after sending GOAWAY. These cases are called as the
* zlib data. * session error. For example, when it receives bad zlib data.
*/ */
static int spdylay_session_fail_session(spdylay_session *session) static int spdylay_session_fail_session(spdylay_session *session,
uint32_t status_code)
{ {
session->goaway_flags |= SPDYLAY_GOAWAY_FAIL_ON_SEND; session->goaway_flags |= SPDYLAY_GOAWAY_FAIL_ON_SEND;
return spdylay_submit_goaway(session); return spdylay_submit_goaway(session, status_code);
} }
/* /*
@ -1533,7 +1534,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_SYN_REPLY: case SPDYLAY_SYN_REPLY:
@ -1551,7 +1552,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_RST_STREAM: case SPDYLAY_RST_STREAM:
@ -1564,7 +1565,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_SETTINGS: case SPDYLAY_SETTINGS:
@ -1577,7 +1578,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_NOOP: case SPDYLAY_NOOP:
@ -1592,7 +1593,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_GOAWAY: case SPDYLAY_GOAWAY:
@ -1605,7 +1606,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_HEADERS: case SPDYLAY_HEADERS:
@ -1623,7 +1624,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
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)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
case SPDYLAY_WINDOW_UPDATE: case SPDYLAY_WINDOW_UPDATE:
@ -1636,7 +1637,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
r = spdylay_session_on_window_update_received(session, &frame); r = spdylay_session_on_window_update_received(session, &frame);
spdylay_frame_window_update_free(&frame.window_update); spdylay_frame_window_update_free(&frame.window_update);
} else if(spdylay_is_non_fatal(r)) { } else if(spdylay_is_non_fatal(r)) {
r = spdylay_session_fail_session(session); r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR);
} }
break; break;
} }
@ -1923,7 +1924,8 @@ int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id)
} }
int spdylay_session_add_goaway(spdylay_session *session, int spdylay_session_add_goaway(spdylay_session *session,
int32_t last_good_stream_id) int32_t last_good_stream_id,
uint32_t status_code)
{ {
int r; int r;
spdylay_frame *frame; spdylay_frame *frame;
@ -1932,7 +1934,7 @@ int spdylay_session_add_goaway(spdylay_session *session,
return SPDYLAY_ERR_NOMEM; return SPDYLAY_ERR_NOMEM;
} }
spdylay_frame_goaway_init(&frame->goaway, session->version, spdylay_frame_goaway_init(&frame->goaway, session->version,
last_good_stream_id); last_good_stream_id, status_code);
r = spdylay_session_add_frame(session, SPDYLAY_GOAWAY, frame, NULL); r = spdylay_session_add_frame(session, SPDYLAY_GOAWAY, frame, NULL);
if(r != 0) { if(r != 0) {
spdylay_frame_goaway_free(&frame->goaway); spdylay_frame_goaway_free(&frame->goaway);

View File

@ -218,9 +218,11 @@ int spdylay_session_add_rst_stream(spdylay_session *session,
int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id); int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id);
/* /*
* Adds GOAWAY frame with last-good-stream-ID * Adds GOAWAY frame with last-good-stream-ID |last_good_stream_id|
* |last_good_stream_id|. This is a convenient function built on top * and the status code |status_code|. The |status_code| is ignored if
* of spdylay_session_add_frame() to add GOAWAY easily. * the protocol version is SPDYLAY_PROTO_SPDY2. This is a convenient
* function built on top of spdylay_session_add_frame() to add GOAWAY
* easily.
* *
* This function returns 0 if it succeeds, or one of the following * This function returns 0 if it succeeds, or one of the following
* negative error codes: * negative error codes:
@ -229,7 +231,8 @@ int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id);
* Out of memory. * Out of memory.
*/ */
int spdylay_session_add_goaway(spdylay_session *session, int spdylay_session_add_goaway(spdylay_session *session,
int32_t last_good_stream_id); int32_t last_good_stream_id,
uint32_t status_code);
/* /*
* Adds WINDOW_UPDATE frame with stream ID |stream_id| and * Adds WINDOW_UPDATE frame with stream ID |stream_id| and

View File

@ -151,9 +151,10 @@ int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id,
return spdylay_session_add_rst_stream(session, stream_id, status_code); return spdylay_session_add_rst_stream(session, stream_id, status_code);
} }
int spdylay_submit_goaway(spdylay_session *session) int spdylay_submit_goaway(spdylay_session *session, uint32_t status_code)
{ {
return spdylay_session_add_goaway(session, session->last_recv_stream_id); return spdylay_session_add_goaway(session, session->last_recv_stream_id,
status_code);
} }
int spdylay_submit_request(spdylay_session *session, uint8_t pri, int spdylay_submit_request(spdylay_session *session, uint8_t pri,

View File

@ -141,8 +141,10 @@ int main(int argc, char* argv[])
!CU_add_test(pSuite, "frame_count_unpack_nv_space", !CU_add_test(pSuite, "frame_count_unpack_nv_space",
test_spdylay_frame_count_unpack_nv_space) || test_spdylay_frame_count_unpack_nv_space) ||
!CU_add_test(pSuite, "frame_pack_ping", test_spdylay_frame_pack_ping) || !CU_add_test(pSuite, "frame_pack_ping", test_spdylay_frame_pack_ping) ||
!CU_add_test(pSuite, "frame_pack_goaway", !CU_add_test(pSuite, "frame_pack_goaway_spdy2",
test_spdylay_frame_pack_goaway) || test_spdylay_frame_pack_goaway_spdy2) ||
!CU_add_test(pSuite, "frame_pack_goaway_spdy3",
test_spdylay_frame_pack_goaway_spdy3) ||
!CU_add_test(pSuite, "frame_pack_syn_stream", !CU_add_test(pSuite, "frame_pack_syn_stream",
test_spdylay_frame_pack_syn_stream) || test_spdylay_frame_pack_syn_stream) ||
!CU_add_test(pSuite, "frame_pack_syn_reply", !CU_add_test(pSuite, "frame_pack_syn_reply",

View File

@ -216,13 +216,14 @@ void test_spdylay_frame_pack_ping()
spdylay_frame_ping_free(&frame.ping); spdylay_frame_ping_free(&frame.ping);
} }
void test_spdylay_frame_pack_goaway() void test_spdylay_frame_pack_goaway_version(uint16_t version)
{ {
spdylay_frame frame, oframe; spdylay_frame frame, oframe;
uint8_t *buf = NULL; uint8_t *buf = NULL;
size_t buflen = 0; size_t buflen = 0;
ssize_t framelen; ssize_t framelen;
spdylay_frame_goaway_init(&frame.goaway, SPDYLAY_PROTO_SPDY2, 1000000007); spdylay_frame_goaway_init(&frame.goaway, version, 1000000007,
SPDYLAY_GOAWAY_PROTOCOL_ERROR);
framelen = spdylay_frame_pack_goaway(&buf, &buflen, &frame.goaway); framelen = spdylay_frame_pack_goaway(&buf, &buflen, &frame.goaway);
CU_ASSERT(0 == spdylay_frame_unpack_goaway CU_ASSERT(0 == spdylay_frame_unpack_goaway
(&oframe.goaway, (&oframe.goaway,
@ -230,7 +231,13 @@ void test_spdylay_frame_pack_goaway()
&buf[SPDYLAY_FRAME_HEAD_LENGTH], &buf[SPDYLAY_FRAME_HEAD_LENGTH],
framelen-SPDYLAY_FRAME_HEAD_LENGTH)); framelen-SPDYLAY_FRAME_HEAD_LENGTH));
CU_ASSERT(1000000007 == oframe.goaway.last_good_stream_id); CU_ASSERT(1000000007 == oframe.goaway.last_good_stream_id);
CU_ASSERT(SPDYLAY_PROTO_SPDY2 == oframe.headers.hd.version); if(version == SPDYLAY_PROTO_SPDY2) {
/* The status code is ignored in SPDY/2 */
CU_ASSERT(0 == oframe.goaway.status_code);
} else if(version == SPDYLAY_PROTO_SPDY3) {
CU_ASSERT(SPDYLAY_GOAWAY_PROTOCOL_ERROR == oframe.goaway.status_code);
}
CU_ASSERT(version == oframe.headers.hd.version);
CU_ASSERT(SPDYLAY_GOAWAY == oframe.headers.hd.type); CU_ASSERT(SPDYLAY_GOAWAY == oframe.headers.hd.type);
CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.headers.hd.flags); CU_ASSERT(SPDYLAY_CTRL_FLAG_NONE == oframe.headers.hd.flags);
CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length);
@ -239,6 +246,16 @@ void test_spdylay_frame_pack_goaway()
spdylay_frame_goaway_free(&frame.goaway); spdylay_frame_goaway_free(&frame.goaway);
} }
void test_spdylay_frame_pack_goaway_spdy2()
{
test_spdylay_frame_pack_goaway_version(SPDYLAY_PROTO_SPDY2);
}
void test_spdylay_frame_pack_goaway_spdy3()
{
test_spdylay_frame_pack_goaway_version(SPDYLAY_PROTO_SPDY3);
}
void test_spdylay_frame_pack_syn_stream_with(uint16_t version) void test_spdylay_frame_pack_syn_stream_with(uint16_t version)
{ {
spdylay_zlib deflater, inflater; spdylay_zlib deflater, inflater;

View File

@ -30,7 +30,8 @@ void test_spdylay_frame_pack_nv_duplicate_keys();
void test_spdylay_frame_count_nv_space(); void test_spdylay_frame_count_nv_space();
void test_spdylay_frame_count_unpack_nv_space(); void test_spdylay_frame_count_unpack_nv_space();
void test_spdylay_frame_pack_ping(); void test_spdylay_frame_pack_ping();
void test_spdylay_frame_pack_goaway(); void test_spdylay_frame_pack_goaway_spdy2();
void test_spdylay_frame_pack_goaway_spdy3();
void test_spdylay_frame_pack_syn_stream(); void test_spdylay_frame_pack_syn_stream();
void test_spdylay_frame_pack_syn_reply(); void test_spdylay_frame_pack_syn_reply();
void test_spdylay_frame_pack_headers(); void test_spdylay_frame_pack_headers();

View File

@ -913,7 +913,8 @@ void test_spdylay_session_on_goaway_received()
spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks,
&user_data); &user_data);
spdylay_frame_goaway_init(&frame.goaway, SPDYLAY_PROTO_SPDY2, stream_id); spdylay_frame_goaway_init(&frame.goaway, SPDYLAY_PROTO_SPDY2, stream_id,
SPDYLAY_GOAWAY_OK);
CU_ASSERT(0 == spdylay_session_on_goaway_received(session, &frame)); CU_ASSERT(0 == spdylay_session_on_goaway_received(session, &frame));
CU_ASSERT(1 == user_data.ctrl_recv_cb_called); CU_ASSERT(1 == user_data.ctrl_recv_cb_called);