diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 974a97f3..078f7b15 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -511,6 +511,36 @@ int spdylay_submit_response(spdylay_session *session, int32_t stream_id, const char **nv, spdylay_data_provider *data_prd); +/* + * Submits SYN_STREAM frame. The |flags| is bitwise OR of the + * following values: + * + * SPDYLAY_FLAG_FIN + * SPDYLAY_FLAG_UNIDIRECTIONAL + * + * The |assoc_stream_id| is used for server-push. If |session| is + * initialized for client use, |assoc_stream_id| is ignored. The |pri| + * is the priority of this frame and it must be between 0 and 3, + * inclusive. 0 is the highest. The |nv| is the name/value pairs in + * this frame. The |stream_user_data| is a pointer to an arbitrary + * data which is associated to the stream this frame will open. + * + * This function is low-level in a sense that the application code can + * specify flags and assoc-stream-ID directly. For usual HTTP request, + * spdylay_submit_request() is useful. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_INVALID_FRAME + * |pri| is invalid. + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags, + uint32_t assoc_stream_id, uint8_t pri, + const char **nv, void *stream_user_data); + /* * 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 diff --git a/lib/spdylay_submit.c b/lib/spdylay_submit.c index 061af3d7..91fa3864 100644 --- a/lib/spdylay_submit.c +++ b/lib/spdylay_submit.c @@ -27,6 +27,85 @@ #include "spdylay_session.h" #include "spdylay_frame.h" +static int spdylay_submit_syn_stream_shared +(spdylay_session *session, + uint8_t flags, + int32_t assoc_stream_id, + uint8_t pri, + const char **nv, + const spdylay_data_provider *data_prd, + void *stream_user_data) +{ + int r; + spdylay_frame *frame; + char **nv_copy; + uint8_t flags_copy; + spdylay_data_provider *data_prd_copy = NULL; + spdylay_syn_stream_aux_data *aux_data; + if(pri > 3) { + return SPDYLAY_ERR_INVALID_ARGUMENT; + } + if(session->server == 0) { + assoc_stream_id = 0; + } + if(data_prd != NULL && data_prd->read_callback != NULL) { + data_prd_copy = malloc(sizeof(spdylay_data_provider)); + if(data_prd_copy == NULL) { + return SPDYLAY_ERR_NOMEM; + } + *data_prd_copy = *data_prd; + } + aux_data = malloc(sizeof(spdylay_syn_stream_aux_data)); + if(aux_data == NULL) { + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + aux_data->data_prd = data_prd_copy; + aux_data->stream_user_data = stream_user_data; + + frame = malloc(sizeof(spdylay_frame)); + if(frame == NULL) { + free(aux_data); + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + nv_copy = spdylay_frame_nv_copy(nv); + if(nv_copy == NULL) { + free(frame); + free(aux_data); + free(data_prd_copy); + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_nv_downcase(nv_copy); + spdylay_frame_nv_sort(nv_copy); + flags_copy = 0; + if(flags & SPDYLAY_FLAG_FIN) { + flags_copy |= SPDYLAY_FLAG_FIN; + } + if(flags & SPDYLAY_FLAG_UNIDIRECTIONAL) { + flags_copy |= SPDYLAY_FLAG_UNIDIRECTIONAL; + } + spdylay_frame_syn_stream_init(&frame->syn_stream, flags_copy, + 0, assoc_stream_id, pri, nv_copy); + r = spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame, + aux_data); + if(r != 0) { + spdylay_frame_syn_stream_free(&frame->syn_stream); + free(frame); + free(aux_data); + free(data_prd_copy); + } + return r; +} + +int spdylay_submit_syn_stream(spdylay_session *session, uint8_t flags, + uint32_t assoc_stream_id, uint8_t pri, + const char **nv, void *stream_user_data) +{ + return spdylay_submit_syn_stream_shared(session, flags, assoc_stream_id, + pri, nv, NULL, stream_user_data); +} + int spdylay_submit_ping(spdylay_session *session) { return spdylay_session_add_ping(session, @@ -44,6 +123,19 @@ int spdylay_submit_goaway(spdylay_session *session) return spdylay_session_add_goaway(session, session->last_recv_stream_id); } +int spdylay_submit_request(spdylay_session *session, uint8_t pri, + const char **nv, spdylay_data_provider *data_prd, + void *stream_user_data) +{ + int flags; + flags = 0; + if(data_prd == NULL || data_prd->read_callback == NULL) { + flags |= SPDYLAY_FLAG_FIN; + } + return spdylay_submit_syn_stream_shared(session, flags, 0, pri, nv, data_prd, + stream_user_data); +} + int spdylay_submit_response(spdylay_session *session, int32_t stream_id, const char **nv, spdylay_data_provider *data_prd) @@ -110,61 +202,3 @@ int spdylay_submit_data(spdylay_session *session, int32_t stream_id, } return r; } - -int spdylay_submit_request(spdylay_session *session, uint8_t pri, - const char **nv, spdylay_data_provider *data_prd, - void *stream_user_data) -{ - int r; - spdylay_frame *frame; - char **nv_copy; - uint8_t flags = 0; - spdylay_data_provider *data_prd_copy = NULL; - spdylay_syn_stream_aux_data *aux_data; - if(pri > 3) { - return SPDYLAY_ERR_INVALID_ARGUMENT; - } - if(data_prd != NULL && data_prd->read_callback != NULL) { - data_prd_copy = malloc(sizeof(spdylay_data_provider)); - if(data_prd_copy == NULL) { - return SPDYLAY_ERR_NOMEM; - } - *data_prd_copy = *data_prd; - } - aux_data = malloc(sizeof(spdylay_syn_stream_aux_data)); - if(aux_data == NULL) { - free(data_prd_copy); - return SPDYLAY_ERR_NOMEM; - } - aux_data->data_prd = data_prd_copy; - aux_data->stream_user_data = stream_user_data; - - frame = malloc(sizeof(spdylay_frame)); - if(frame == NULL) { - free(aux_data); - free(data_prd_copy); - return SPDYLAY_ERR_NOMEM; - } - nv_copy = spdylay_frame_nv_copy(nv); - if(nv_copy == NULL) { - free(frame); - free(aux_data); - free(data_prd_copy); - return SPDYLAY_ERR_NOMEM; - } - spdylay_frame_nv_downcase(nv_copy); - spdylay_frame_nv_sort(nv_copy); - if(data_prd_copy == NULL) { - flags |= SPDYLAY_FLAG_FIN; - } - spdylay_frame_syn_stream_init(&frame->syn_stream, flags, 0, 0, pri, nv_copy); - r = spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame, - aux_data); - if(r != 0) { - spdylay_frame_syn_stream_free(&frame->syn_stream); - free(frame); - free(aux_data); - free(data_prd_copy); - } - return r; -} diff --git a/tests/main.c b/tests/main.c index 0997a6ab..4b46b374 100644 --- a/tests/main.c +++ b/tests/main.c @@ -91,6 +91,8 @@ int main(int argc, char* argv[]) test_spdylay_submit_request_with_data) || !CU_add_test(pSuite, "submit_request_without_data", test_spdylay_submit_request_with_null_data_read_callback) || + !CU_add_test(pSuite, "submit_syn_stream", + test_spdylay_submit_syn_stream) || !CU_add_test(pSuite, "session_reply_fail", test_spdylay_session_reply_fail) || !CU_add_test(pSuite, "session_on_headers_received", diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 615b41e6..31734150 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -633,6 +633,38 @@ void test_spdylay_submit_request_with_null_data_read_callback() spdylay_session_del(session); } +void test_spdylay_submit_syn_stream() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks; + const char *nv[] = { "version", "HTTP/1.1", NULL }; + spdylay_outbound_item *item; + + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + CU_ASSERT(0 == spdylay_session_client_new(&session, &callbacks, NULL)); + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_FLAG_FIN, 1, 3, + nv, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_stream.nv[0])); + CU_ASSERT(SPDYLAY_FLAG_FIN == item->frame->syn_stream.hd.flags); + /* See assoc-stream-ID is ignored */ + CU_ASSERT(0 == item->frame->syn_stream.assoc_stream_id); + CU_ASSERT(3 == item->frame->syn_stream.pri); + + spdylay_session_del(session); + + CU_ASSERT(0 == spdylay_session_server_new(&session, &callbacks, NULL)); + CU_ASSERT(0 == spdylay_submit_syn_stream(session, SPDYLAY_FLAG_FIN, 1, 3, + nv, NULL)); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(0 == strcmp("version", item->frame->syn_stream.nv[0])); + CU_ASSERT(SPDYLAY_FLAG_FIN == item->frame->syn_stream.hd.flags); + CU_ASSERT(1 == item->frame->syn_stream.assoc_stream_id); + CU_ASSERT(3 == item->frame->syn_stream.pri); + + spdylay_session_del(session); +} + void test_spdylay_session_reply_fail() { spdylay_session *session; diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h index 905c0256..6797bd08 100644 --- a/tests/spdylay_session_test.h +++ b/tests/spdylay_session_test.h @@ -37,6 +37,7 @@ void test_spdylay_submit_response(); void test_spdylay_submit_response_with_null_data_read_callback(); void test_spdylay_submit_request_with_data(); void test_spdylay_submit_request_with_null_data_read_callback(); +void test_spdylay_submit_syn_stream(); void test_spdylay_session_reply_fail(); void test_spdylay_session_on_headers_received(); void test_spdylay_session_on_ping_received();