diff --git a/examples/SpdyServer.cc b/examples/SpdyServer.cc index 82f6ad4b..d9b82000 100644 --- a/examples/SpdyServer.cc +++ b/examples/SpdyServer.cc @@ -922,10 +922,14 @@ int SpdyServer::run() sessions.mod_poll(hd); } } + if(hd->mark_del()) { + del_list.push_back(hd); + } } for(std::vector::iterator i = del_list.begin(), eoi = del_list.end(); i != eoi; ++i) { on_close(sessions, *i); + sessions.remove_handler(*i); } del_list.clear(); } diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 01f2026d..aa5c86fd 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -394,9 +394,10 @@ void* spdylay_session_get_stream_user_data(spdylay_session *session, int32_t stream_id); /* - * Submits SYN_STREAM frame. |pri| is priority of this request and it - * must be in the range of [0, 3]. 0 means the higest priority. |nv| - * must include following name/value pairs: + * Submits SYN_STREAM frame and optionally one or more DATA + * frames. |pri| is priority of this request and it must be in the + * range of [0, 3]. 0 means the higest priority. |nv| must include + * following name/value pairs: * * "method": HTTP method (e.g., "GET" or "POST") * "scheme": URI scheme (e.g., "https") @@ -405,7 +406,8 @@ void* spdylay_session_get_stream_user_data(spdylay_session *session, * * "host" name/value pair is also required by some hosts. * - * This function creates copies of all name/value pairs in |nv|. + * This function creates copies of all name/value pairs in |nv|. It + * also lower-cases all names in |nv|. * * If |data_prd| is not NULL, it provides data which will be sent in * subsequent DATA frames. In this case, "POST" must be specified with @@ -426,22 +428,17 @@ int spdylay_submit_request(spdylay_session *session, uint8_t pri, void *stream_user_data); /* - * Submits DATA frame to stream |stream_id|. - * - * This function returns 0 if it succeeds, or negative error code. - */ -int spdylay_submit_data(spdylay_session *session, int32_t stream_id, - spdylay_data_provider *data_prd); - -/* - * Submits SYN_REPLY frame against stream |stream_id|. |nv| must - * include following name/value pairs: + * Submits SYN_REPLY frame and optionally one or more DATA frames + * against stream |stream_id|. |nv| must include following name/value + * pairs: * * "status": HTTP status code (e.g., "200" or "200 OK") * "version": HTTP response version (e.g., "HTTP/1.1") * - * This function creates copies of all name/value pairs in |nv|. If - * |data_prd| is not NULL, it provides data which will be sent in + * This function creates copies of all name/value pairs in |nv|. It + * also lower-cases all names in |nv|. + * + * If |data_prd| is not NULL, it provides data which will be sent in * subsequent DATA frames. If |data_prd| is NULL, SYN_REPLY will have * FLAG_FIN. * @@ -452,12 +449,23 @@ int spdylay_submit_response(spdylay_session *session, spdylay_data_provider *data_prd); /* - * Submits RST_STREAM frame to cancel/reset stream |stream_id| with + * Submits 1 or more DATA frames to the stream |stream_id|. The data + * to be sent are provided by |data_prd|. Depending on the length of + * data, 1 or more DATA frames will be sent. If |flags| contains + * SPDYLAY_FLAG_FIN, the last DATA frame has FLAG_FIN set. + * + * This function returns 0 if it succeeds, or negative error code. + */ +int spdylay_submit_data(spdylay_session *session, int32_t stream_id, + uint8_t flags, spdylay_data_provider *data_prd); + +/* + * Submits RST_STREAM frame to cancel/reject stream |stream_id| with * status code |status_code|. This function returns 0 if it succeeds, * or negative error code. */ -int spdylay_submit_cancel(spdylay_session *session, int32_t stream_id, - uint32_t status_code); +int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id, + uint32_t status_code); /* * Submits PING frame. This function returns 0 if it succeeds, or diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index ca21601a..567e54e1 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -315,6 +315,18 @@ void spdylay_frame_nv_sort(char **nv) qsort(nv, n/2, 2*sizeof(char*), spdylay_string_compar); } +void spdylay_frame_nv_downcase(char **nv) +{ + int i, j; + for(i = 0; nv[i]; i += 2) { + for(j = 0; nv[i][j] != '\0'; ++j) { + if('A' <= nv[i][j] && nv[i][j] <= 'Z') { + nv[i][j] += 'a'-'A'; + } + } + } +} + void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, uint8_t flags, int32_t stream_id, int32_t assoc_stream_id, uint8_t pri, char **nv) @@ -425,10 +437,11 @@ void spdylay_frame_settings_free(spdylay_settings *frame) } void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, - spdylay_data_provider *data_prd) + uint8_t flags, spdylay_data_provider *data_prd) { memset(frame, 0, sizeof(spdylay_data)); frame->stream_id = stream_id; + frame->flags = flags; frame->data_prd = *data_prd; } diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 3991ebce..fcdcbe68 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -266,7 +266,7 @@ void spdylay_frame_settings_init(spdylay_settings *frame, uint8_t flags, void spdylay_frame_settings_free(spdylay_settings *frame); void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, - spdylay_data_provider *data_prd); + uint8_t flags, spdylay_data_provider *data_prd); void spdylay_frame_data_free(spdylay_data *frame); @@ -293,6 +293,11 @@ char** spdylay_frame_nv_copy(const char **nv); */ void spdylay_frame_nv_sort(char **nv); +/* + * Makes names in |nv| lower cased. + */ +void spdylay_frame_nv_downcase(char **nv); + /* * Makes copy of |iv| and return the copy. The |niv| is the number of * entries in |iv|. This function returns the pointer to the copy if diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 1983fb73..753c9b63 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -691,7 +691,7 @@ static int spdylay_session_after_frame_sent(spdylay_session *session) int r; /* spdylay_submit_data() makes a copy of aux_data->data_prd */ r = spdylay_submit_data(session, frame->syn_stream.stream_id, - aux_data->data_prd); + SPDYLAY_FLAG_FIN, aux_data->data_prd); if(r != 0) { /* TODO If r is not FATAL, we should send RST_STREAM. */ return r; @@ -714,7 +714,8 @@ static int spdylay_session_after_frame_sent(spdylay_session *session) spdylay_data_provider *data_prd = (spdylay_data_provider*)item->aux_data; int r; - r = spdylay_submit_data(session, frame->syn_reply.stream_id, data_prd); + r = spdylay_submit_data(session, frame->syn_reply.stream_id, + SPDYLAY_FLAG_FIN, data_prd); if(r != 0) { /* TODO If r is not FATAL, we should send RST_STREAM. */ return r; @@ -949,6 +950,33 @@ static int spdylay_session_is_new_peer_stream_id(spdylay_session *session, } } +/* + * Returns non-zero iff version == SPDYLAY_PROTO_VERSION + */ +static int spdylay_session_check_version(uint16_t version) +{ + return version == SPDYLAY_PROTO_VERSION; +} + +/* + * Returns non-zero iff name/value pairs |nv| are good shape. + * Currently, we only checks whether names are lower cased. The spdy/2 + * spec requires that names must be lower cased. + */ +static int spdylay_session_check_nv(char **nv) +{ + int i; + for(i = 0; nv[i]; i += 2) { + int j; + for(j = 0; nv[i][j] != '\0'; ++j) { + if('A' <= nv[i][j] && nv[i][j] <= 'Z') { + return 0; + } + } + } + return 1; +} + /* * Validates SYN_STREAM frame |frame|. This function returns 0 if it * succeeds, or non-zero spdylay_status_code. @@ -959,7 +987,7 @@ static int spdylay_session_validate_syn_stream(spdylay_session *session, if(!spdylay_session_is_new_peer_stream_id(session, frame->stream_id)) { return SPDYLAY_PROTOCOL_ERROR; } - if(frame->hd.version != SPDYLAY_PROTO_VERSION) { + if(!spdylay_session_check_version(frame->hd.version)) { return SPDYLAY_UNSUPPORTED_VERSION; } if(session->server) { @@ -988,9 +1016,13 @@ static int spdylay_session_validate_syn_stream(spdylay_session *session, follow it. */ return SPDYLAY_REFUSED_STREAM; } + if(!spdylay_session_check_nv(frame->nv)) { + return SPDYLAY_PROTOCOL_ERROR; + } return 0; } + static int spdylay_session_handle_invalid_stream (spdylay_session *session, int32_t stream_id, @@ -1070,14 +1102,6 @@ int spdylay_session_on_syn_stream_received(spdylay_session *session, return r; } -/* - * Returns non-zero iff version == SPDYLAY_PROTOCOL_ERROR. - */ -static int spdylay_session_check_version(uint16_t version) -{ - return version == SPDYLAY_PROTO_VERSION; -} - int spdylay_session_on_syn_reply_received(spdylay_session *session, spdylay_frame *frame) { @@ -1087,8 +1111,10 @@ int spdylay_session_on_syn_reply_received(spdylay_session *session, if(!spdylay_session_check_version(frame->syn_reply.hd.version)) { return 0; } - stream = spdylay_session_get_stream(session, frame->syn_reply.stream_id); - if(stream && (stream->shut_flags & SPDYLAY_SHUT_RD) == 0) { + if((stream = spdylay_session_get_stream(session, + frame->syn_reply.stream_id)) && + (stream->shut_flags & SPDYLAY_SHUT_RD) == 0 && + spdylay_session_check_nv(frame->syn_reply.nv)) { if(spdylay_session_is_my_stream_id(session, frame->syn_reply.stream_id)) { if(stream->state == SPDYLAY_STREAM_OPENING) { valid = 1; @@ -1190,9 +1216,10 @@ int spdylay_session_on_headers_received(spdylay_session *session, if(!spdylay_session_check_version(frame->headers.hd.version)) { return 0; } - stream = spdylay_session_get_stream(session, frame->headers.stream_id); - /* First we check readability from this stream. */ - if(stream && (stream->shut_flags & SPDYLAY_SHUT_RD) == 0) { + if((stream = spdylay_session_get_stream(session, + frame->headers.stream_id)) && + (stream->shut_flags & SPDYLAY_SHUT_RD) == 0 && + spdylay_session_check_nv(frame->headers.nv)) { if(spdylay_session_is_my_stream_id(session, frame->headers.stream_id)) { if(stream->state == SPDYLAY_STREAM_OPENED) { valid = 1; diff --git a/lib/spdylay_submit.c b/lib/spdylay_submit.c index 1daaba20..86245a09 100644 --- a/lib/spdylay_submit.c +++ b/lib/spdylay_submit.c @@ -33,8 +33,8 @@ int spdylay_submit_ping(spdylay_session *session) spdylay_session_get_next_unique_id(session)); } -int spdylay_submit_cancel(spdylay_session *session, int32_t stream_id, - uint32_t status_code) +int spdylay_submit_rst_stream(spdylay_session *session, int32_t stream_id, + uint32_t status_code) { return spdylay_session_add_rst_stream(session, stream_id, status_code); } @@ -71,6 +71,7 @@ int spdylay_submit_response(spdylay_session *session, free(data_prd_copy); return SPDYLAY_ERR_NOMEM; } + spdylay_frame_nv_downcase(nv_copy); spdylay_frame_nv_sort(nv_copy); if(data_prd == NULL) { flags |= SPDYLAY_FLAG_FIN; @@ -88,15 +89,20 @@ int spdylay_submit_response(spdylay_session *session, } int spdylay_submit_data(spdylay_session *session, int32_t stream_id, + uint8_t flags, spdylay_data_provider *data_prd) { int r; spdylay_frame *frame; + uint8_t nflags = 0; frame = malloc(sizeof(spdylay_frame)); if(frame == NULL) { return SPDYLAY_ERR_NOMEM; } - spdylay_frame_data_init(&frame->data, stream_id, data_prd); + if(flags & SPDYLAY_FLAG_FIN) { + nflags |= SPDYLAY_FLAG_FIN; + } + spdylay_frame_data_init(&frame->data, stream_id, nflags, data_prd); r = spdylay_session_add_frame(session, SPDYLAY_DATA, frame, NULL); if(r != 0) { spdylay_frame_data_free(&frame->data); @@ -146,6 +152,7 @@ int spdylay_submit_request(spdylay_session *session, uint8_t pri, free(data_prd_copy); return SPDYLAY_ERR_NOMEM; } + spdylay_frame_nv_downcase(nv_copy); spdylay_frame_nv_sort(nv_copy); if(data_prd == NULL) { flags |= SPDYLAY_FLAG_FIN; diff --git a/tests/main.c b/tests/main.c index 1a1892d7..f144a84f 100644 --- a/tests/main.c +++ b/tests/main.c @@ -132,6 +132,8 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "frame_pack_settings", test_spdylay_frame_pack_settings) || !CU_add_test(pSuite, "frame_nv_sort", test_spdylay_frame_nv_sort) || + !CU_add_test(pSuite, "frame_nv_downcase", + test_spdylay_frame_nv_downcase) || !CU_add_test(pSuite, "stream_add_pushed_stream", test_spdylay_stream_add_pushed_stream)) { CU_cleanup_registry(); diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index 9f32268d..73a22e95 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -230,3 +230,20 @@ void test_spdylay_frame_nv_sort() CU_ASSERT(strcmp("version", nv[4]) == 0); CU_ASSERT(strcmp("HTTP/1.1", nv[5]) == 0); } + +void test_spdylay_frame_nv_downcase() +{ + const char *nv_src[] = { + "VERSION", "HTTP/1.1", + "Content-Length", "1000000007", + NULL + }; + char **nv; + nv = spdylay_frame_nv_copy(nv_src); + spdylay_frame_nv_downcase(nv); + CU_ASSERT(0 == strcmp("version", nv[0])); + CU_ASSERT(0 == strcmp("HTTP/1.1", nv[1])); + CU_ASSERT(0 == strcmp("content-length", nv[2])); + CU_ASSERT(0 == strcmp("1000000007", nv[3])); + spdylay_frame_nv_del(nv); +} diff --git a/tests/spdylay_frame_test.h b/tests/spdylay_frame_test.h index 26c35daa..6484e565 100644 --- a/tests/spdylay_frame_test.h +++ b/tests/spdylay_frame_test.h @@ -33,5 +33,6 @@ void test_spdylay_frame_pack_goaway(); void test_spdylay_frame_pack_headers(); void test_spdylay_frame_pack_settings(); void test_spdylay_frame_nv_sort(); +void test_spdylay_frame_nv_downcase(); #endif /* SPDYLAY_FRAME_TEST_H */ diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index b725ae73..f7bc8fe3 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -310,6 +310,7 @@ void test_spdylay_session_on_syn_stream_received() spdylay_session_callbacks callbacks; my_user_data user_data; const char *nv[] = { NULL }; + const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL }; spdylay_frame frame; spdylay_stream *stream; int32_t stream_id = 1; @@ -341,6 +342,14 @@ void test_spdylay_session_on_syn_stream_received() CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + spdylay_frame_syn_stream_free(&frame.syn_stream); + + /* Upper cased name/value pairs */ + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, 3, 0, 3, + dup_nv(upcase_nv)); + CU_ASSERT(0 == spdylay_session_on_syn_stream_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); + spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_session_del(session); } @@ -409,6 +418,7 @@ void test_spdylay_session_on_syn_reply_received() }; my_user_data user_data; const char *nv[] = { NULL }; + const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL }; spdylay_frame frame; spdylay_stream *stream; user_data.ctrl_recv_cb_called = 0; @@ -441,6 +451,17 @@ void test_spdylay_session_on_syn_reply_received() CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); spdylay_frame_syn_reply_free(&frame.syn_reply); + + /* Upper cased name/value pairs */ + spdylay_session_open_stream(session, 5, SPDYLAY_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENING, NULL); + spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_FLAG_NONE, 5, + dup_nv(upcase_nv)); + CU_ASSERT(0 == spdylay_session_on_syn_reply_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_syn_reply_free(&frame.syn_reply); + spdylay_session_del(session); } @@ -506,10 +527,11 @@ void test_spdylay_submit_response() NULL, NULL }; - const char *nv[] = { NULL }; + const char *nv[] = { "Content-Length", "1024", NULL }; int32_t stream_id = 2; spdylay_data_provider data_prd; my_user_data ud; + spdylay_outbound_item *item; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 64*1024; @@ -517,6 +539,8 @@ void test_spdylay_submit_response() spdylay_session_open_stream(session, stream_id, SPDYLAY_FLAG_NONE, 3, SPDYLAY_STREAM_OPENING, NULL); CU_ASSERT(0 == spdylay_submit_response(session, stream_id, nv, &data_prd)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("content-length", item->frame->syn_reply.nv[0])); CU_ASSERT(0 == spdylay_session_send(session)); spdylay_session_del(session); } @@ -530,14 +554,17 @@ void test_spdylay_submit_request_with_data() NULL, NULL }; - const char *nv[] = { NULL }; + const char *nv[] = { "Version", "HTTP/1.1", NULL }; spdylay_data_provider data_prd; my_user_data ud; + spdylay_outbound_item *item; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 64*1024; CU_ASSERT(0 == spdylay_session_client_new(&session, &callbacks, &ud)); CU_ASSERT(0 == spdylay_submit_request(session, 3, nv, &data_prd, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_stream.nv[0])); CU_ASSERT(0 == spdylay_session_send(session)); CU_ASSERT(0 == ud.data_source_length); @@ -572,6 +599,7 @@ void test_spdylay_session_on_headers_received() spdylay_session_callbacks callbacks; my_user_data user_data; const char *nv[] = { NULL }; + const char *upcase_nv[] = { "version", "http/1.1", "methoD", "get", NULL }; spdylay_frame frame; memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); callbacks.on_ctrl_recv_callback = on_ctrl_recv_callback; @@ -629,6 +657,18 @@ void test_spdylay_session_on_headers_received() CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); spdylay_frame_headers_free(&frame.headers); + + /* Upper cased name/value pairs */ + spdylay_session_open_stream(session, 5, SPDYLAY_FLAG_NONE, 0, + SPDYLAY_STREAM_OPENED, NULL); + spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_NONE, 5, + dup_nv(upcase_nv)); + + CU_ASSERT(0 == spdylay_session_on_headers_received(session, &frame)); + CU_ASSERT(3 == user_data.invalid_ctrl_recv_cb_called); + + spdylay_frame_headers_free(&frame.headers); + spdylay_session_del(session); }