diff --git a/examples/spdycat.cc b/examples/spdycat.cc index 9bf8b72..1d3d902 100644 --- a/examples/spdycat.cc +++ b/examples/spdycat.cc @@ -121,7 +121,7 @@ void on_stream_close_callback if(itr != stream2req.end()) { ++complete; if(complete == numreq) { - spdylay_submit_goaway(session); + spdylay_submit_goaway(session, SPDYLAY_GOAWAY_OK); } } } diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 54af84d..dc329cb 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -116,7 +116,10 @@ typedef enum { /* Default maximum concurrent streams */ #define SPDYLAY_CONCURRENT_STREAMS_MAX 100 +/* Status code for RST_STREAM */ typedef enum { + /* SPDYLAY_OK is not valid status code for RST_STREAM. It is defined + just for spdylay library use. */ SPDYLAY_OK = 0, SPDYLAY_PROTOCOL_ERROR = 1, SPDYLAY_INVALID_STREAM = 2, @@ -124,9 +127,21 @@ typedef enum { SPDYLAY_UNSUPPORTED_VERSION = 4, SPDYLAY_CANCEL = 5, 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; +/* 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_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); /* - * 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 * negative error codes: @@ -651,7 +667,7 @@ int spdylay_submit_ping(spdylay_session *session); * SPDYLAY_ERR_NOMEM * 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. diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index 1acd91e..9489e46 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -449,7 +449,8 @@ void spdylay_frame_ping_free(spdylay_ping *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)); frame->hd.version = version; @@ -458,11 +459,11 @@ void spdylay_frame_goaway_init(spdylay_goaway *frame, frame->hd.length = 4; } else if(version == SPDYLAY_PROTO_SPDY3) { frame->hd.length = 8; + frame->status_code = status_code; } else { frame->hd.length = 0; } 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) diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 71856a1..2d10577 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -463,8 +463,13 @@ void spdylay_frame_ping_init(spdylay_ping *frame, uint16_t version, 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, - int32_t last_good_stream_id); + int32_t last_good_stream_id, + uint32_t status_code); void spdylay_frame_goaway_free(spdylay_goaway *frame); diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index f127789..9be2630 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -1489,13 +1489,14 @@ int spdylay_session_on_headers_received(spdylay_session *session, /* * This function should be called when the session wants to drop - * connection after sending GOAWAY. For example, when it receives bad - * zlib data. + * connection after sending GOAWAY. These cases are called as the + * 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; - 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 other control frames. */ } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_syn_reply_free(&frame.syn_reply); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_rst_stream_free(&frame.rst_stream); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_settings_free(&frame.settings); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_ping_free(&frame.ping); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_goaway_free(&frame.goaway); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_headers_free(&frame.headers); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } break; 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); spdylay_frame_window_update_free(&frame.window_update); } else if(spdylay_is_non_fatal(r)) { - r = spdylay_session_fail_session(session); + r = spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); } 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, - int32_t last_good_stream_id) + int32_t last_good_stream_id, + uint32_t status_code) { int r; spdylay_frame *frame; @@ -1932,7 +1934,7 @@ int spdylay_session_add_goaway(spdylay_session *session, return SPDYLAY_ERR_NOMEM; } 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); if(r != 0) { spdylay_frame_goaway_free(&frame->goaway); diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index bfa28c5..30928c8 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -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); /* - * Adds GOAWAY frame with last-good-stream-ID - * |last_good_stream_id|. This is a convenient function built on top - * of spdylay_session_add_frame() to add GOAWAY easily. + * Adds GOAWAY frame with last-good-stream-ID |last_good_stream_id| + * and the status code |status_code|. The |status_code| is ignored if + * 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 * negative error codes: @@ -229,7 +231,8 @@ int spdylay_session_add_ping(spdylay_session *session, uint32_t unique_id); * Out of memory. */ 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 diff --git a/lib/spdylay_submit.c b/lib/spdylay_submit.c index ae3128b..b7ae364 100644 --- a/lib/spdylay_submit.c +++ b/lib/spdylay_submit.c @@ -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); } -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, diff --git a/tests/main.c b/tests/main.c index 1740d09..22fd27d 100644 --- a/tests/main.c +++ b/tests/main.c @@ -141,8 +141,10 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "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_goaway", - test_spdylay_frame_pack_goaway) || + !CU_add_test(pSuite, "frame_pack_goaway_spdy2", + 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", test_spdylay_frame_pack_syn_stream) || !CU_add_test(pSuite, "frame_pack_syn_reply", diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index 62f2d56..6d231ff 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -216,13 +216,14 @@ void test_spdylay_frame_pack_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; uint8_t *buf = NULL; size_t buflen = 0; 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); CU_ASSERT(0 == spdylay_frame_unpack_goaway (&oframe.goaway, @@ -230,7 +231,13 @@ void test_spdylay_frame_pack_goaway() &buf[SPDYLAY_FRAME_HEAD_LENGTH], framelen-SPDYLAY_FRAME_HEAD_LENGTH)); 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_CTRL_FLAG_NONE == oframe.headers.hd.flags); 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); } +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) { spdylay_zlib deflater, inflater; diff --git a/tests/spdylay_frame_test.h b/tests/spdylay_frame_test.h index f567a06..cb3f241 100644 --- a/tests/spdylay_frame_test.h +++ b/tests/spdylay_frame_test.h @@ -30,7 +30,8 @@ void test_spdylay_frame_pack_nv_duplicate_keys(); void test_spdylay_frame_count_nv_space(); void test_spdylay_frame_count_unpack_nv_space(); 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_reply(); void test_spdylay_frame_pack_headers(); diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index a7f6a68..717b79c 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -913,7 +913,8 @@ void test_spdylay_session_on_goaway_received() spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &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(1 == user_data.ctrl_recv_cb_called);