From 00abfc8dd39b642caa66a0595806d9db9b07d479 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 6 Mar 2012 23:43:45 +0900 Subject: [PATCH] Always accept SPDY/3 style name/value pairs from spdylay_submit_* and translate them into SPDY/2 as needed. --- lib/includes/spdylay/spdylay.h | 38 +++++------------ lib/spdylay_frame.c | 37 +++++++++++++++++ lib/spdylay_frame.h | 10 +++++ lib/spdylay_session.c | 18 +++++++++ tests/main.c | 2 + tests/spdylay_frame_test.c | 50 +++++++++++++++++++++++ tests/spdylay_frame_test.h | 2 + tests/spdylay_session_test.c | 74 ++++++++++++++++++++++++++-------- 8 files changed, 187 insertions(+), 44 deletions(-) diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 79300db..486e3bc 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -563,24 +563,7 @@ void* spdylay_session_get_stream_user_data(spdylay_session *session, * the lowest priority is 3. If the |session| is initialized with the * version SPDYLAY_PROTO_SPDY3, the lowest priority is 7. * - * If |session| is initialized with the version SPDYLAY_PROTO_SPDY2, - * |nv| must include following name/value pairs: - * - * "method" - * HTTP method (e.g., "GET", "POST", "HEAD", etc) - * "scheme" - * URI scheme (e.g., "https") - * "url" - * Absolute path and parameters of this request (e.g., "/foo", - * "/foo;bar;haz?h=j&y=123") - * "version" - * HTTP version (e.g., "HTTP/1.1") - * - * The "host" name/value pair (this is the same as the HTTP "Host" - * header field) is also required by some hosts. - * - * If |session| is initialized with the version SPDYLAY_PROTO_SPDY3, - * |nv| must include following name/value pairs: + * The |nv| must include following name/value pairs: * * ":method" * HTTP method (e.g., "GET", "POST", "HEAD", etc) @@ -596,6 +579,10 @@ void* spdylay_session_get_stream_user_data(spdylay_session *session, * "example.org:443"). This is the same as the HTTP "Host" header * field. * + * If the |session| is initialized with the version + * SPDYLAY_PROTO_SPDY2, the above names are translated to "method", + * "scheme", "url", "version" and "host" respectively. + * * This function creates copies of all name/value pairs in |nv|. It * also lower-cases all names in |nv|. * @@ -637,22 +624,17 @@ int spdylay_submit_request(spdylay_session *session, uint8_t pri, * Submits SYN_REPLY frame and optionally one or more DATA frames * against stream |stream_id|. * - * If |session| is initialized with the version SPDYLAY_PROTO_SPDY2, - * |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") - * - * If |session| is initialized with the version SPDYLAY_PROTO_SPDY3, - * |nv| must include following name/value pairs: + * The |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") * + * If the |session| is initialized with the version + * SPDYLAY_PROTO_SPDY2, the above names are translated to "status" and + * "version" respectively. + * * This function creates copies of all name/value pairs in |nv|. It * also lower-cases all names in |nv|. * diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index bb162c2..3a94e79 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -376,6 +376,43 @@ char** spdylay_frame_nv_norm_copy(const char **nv) return nv_copy; } +/* Table to translate SPDY/3 header names to SPDY/2. */ +static char *spdylay_nv_3to2[] = { + ":host", "host", + ":method", "method", + ":path", "url", + ":scheme", "scheme", + ":status", "status", + ":version", "version", + NULL +}; + +void spdylay_frame_nv_3to2(char **nv) +{ + int i, j; + for(i = 0; nv[i]; i += 2) { + for(j = 0; spdylay_nv_3to2[j]; j += 2) { + if(strcmp(nv[i], spdylay_nv_3to2[j]) == 0) { + nv[i] = spdylay_nv_3to2[j+1]; + break; + } + } + } +} + +void spdylay_frame_nv_2to3(char **nv) +{ + int i, j; + for(i = 0; nv[i]; i += 2) { + for(j = 0; spdylay_nv_3to2[j]; j += 2) { + if(strcmp(nv[i], spdylay_nv_3to2[j+1]) == 0) { + nv[i] = spdylay_nv_3to2[j]; + break; + } + } + } +} + void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, uint16_t version, uint8_t flags, int32_t stream_id, int32_t assoc_stream_id, diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 05564a2..1d0bde3 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -595,6 +595,16 @@ void spdylay_frame_nv_downcase(char **nv); */ char** spdylay_frame_nv_norm_copy(const char **nv); +/* + * Translates the |nv| in SPDY/3 header names into SPDY/2. + */ +void spdylay_frame_nv_3to2(char **nv); + +/* + * Translates the |nv| in SPDY/2 header names into SPDY/3. + */ +void spdylay_frame_nv_2to3(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 cd4489c..df28520 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -552,12 +552,18 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session, stream_id = session->next_stream_id; item->frame->syn_stream.stream_id = stream_id; session->next_stream_id += 2; + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_3to2(item->frame->syn_stream.nv); + } framebuflen = spdylay_frame_pack_syn_stream(&session->aob.framebuf, &session->aob.framebufmax, &session->nvbuf, &session->nvbuflen, &item->frame->syn_stream, &session->hd_deflater); + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(item->frame->syn_stream.nv); + } if(framebuflen < 0) { return framebuflen; } @@ -576,12 +582,18 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session, item->frame->syn_reply.stream_id)) { return SPDYLAY_ERR_INVALID_FRAME; } + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_3to2(item->frame->syn_reply.nv); + } framebuflen = spdylay_frame_pack_syn_reply(&session->aob.framebuf, &session->aob.framebufmax, &session->nvbuf, &session->nvbuflen, &item->frame->syn_reply, &session->hd_deflater); + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(item->frame->syn_reply.nv); + } if(framebuflen < 0) { return framebuflen; } @@ -620,12 +632,18 @@ static ssize_t spdylay_session_prep_frame(spdylay_session *session, item->frame->headers.stream_id)) { return SPDYLAY_ERR_INVALID_FRAME; } + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_3to2(item->frame->headers.nv); + } framebuflen = spdylay_frame_pack_headers(&session->aob.framebuf, &session->aob.framebufmax, &session->nvbuf, &session->nvbuflen, &item->frame->headers, &session->hd_deflater); + if(session->version == SPDYLAY_PROTO_SPDY2) { + spdylay_frame_nv_2to3(item->frame->headers.nv); + } if(framebuflen < 0) { return framebuflen; } diff --git a/tests/main.c b/tests/main.c index 58632d6..e15654e 100644 --- a/tests/main.c +++ b/tests/main.c @@ -170,6 +170,8 @@ int main(int argc, char* argv[]) test_spdylay_frame_nv_downcase) || !CU_add_test(pSuite, "frame_pack_nv_duplicate_keys", test_spdylay_frame_pack_nv_duplicate_keys) || + !CU_add_test(pSuite, "frame_nv_2to3", test_spdylay_frame_nv_2to3) || + !CU_add_test(pSuite, "frame_nv_3to2", test_spdylay_frame_nv_3to2) || !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 26b98ab..94ea25c 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -526,3 +526,53 @@ void test_spdylay_frame_nv_downcase() CU_ASSERT(0 == strcmp("1000000007", nv[3])); spdylay_frame_nv_del(nv); } + +void test_spdylay_frame_nv_2to3() +{ + const char *nv_src[] = { + "host", "localhost", + "method", "GET", + "url", "/", + "accept", "*/*", + "scheme", "https", + "status", "200 OK", + "version", "HTTP/1.1", + NULL + }; + char **nv; + nv = spdylay_frame_nv_copy(nv_src); + spdylay_frame_nv_2to3(nv); + CU_ASSERT(0 == strcmp(":host", nv[0])); + CU_ASSERT(0 == strcmp(":method", nv[2])); + CU_ASSERT(0 == strcmp(":path", nv[4])); + CU_ASSERT(0 == strcmp("accept", nv[6])); + CU_ASSERT(0 == strcmp(":scheme", nv[8])); + CU_ASSERT(0 == strcmp(":status", nv[10])); + CU_ASSERT(0 == strcmp(":version", nv[12])); + spdylay_frame_nv_del(nv); +} + +void test_spdylay_frame_nv_3to2() +{ + const char *nv_src[] = { + ":host", "localhost", + ":method", "GET", + ":path", "/", + "accept", "*/*", + ":scheme", "https", + ":status", "200 OK", + ":version", "HTTP/1.1", + NULL + }; + char **nv; + nv = spdylay_frame_nv_copy(nv_src); + spdylay_frame_nv_3to2(nv); + CU_ASSERT(0 == strcmp("host", nv[0])); + CU_ASSERT(0 == strcmp("method", nv[2])); + CU_ASSERT(0 == strcmp("url", nv[4])); + CU_ASSERT(0 == strcmp("accept", nv[6])); + CU_ASSERT(0 == strcmp("scheme", nv[8])); + CU_ASSERT(0 == strcmp("status", nv[10])); + CU_ASSERT(0 == strcmp("version", nv[12])); + spdylay_frame_nv_del(nv); +} diff --git a/tests/spdylay_frame_test.h b/tests/spdylay_frame_test.h index fddc224..87673c3 100644 --- a/tests/spdylay_frame_test.h +++ b/tests/spdylay_frame_test.h @@ -43,5 +43,7 @@ void test_spdylay_frame_pack_window_update(); void test_spdylay_frame_pack_settings(); void test_spdylay_frame_nv_sort(); void test_spdylay_frame_nv_downcase(); +void test_spdylay_frame_nv_2to3(); +void test_spdylay_frame_nv_3to2(); #endif /* SPDYLAY_FRAME_TEST_H */ diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 56b70eb..bf93337 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -579,21 +579,36 @@ void test_spdylay_submit_response_with_null_data_read_callback() { spdylay_session *session; spdylay_session_callbacks callbacks; - const char *nv[] = { "Version", "HTTP/1.1", NULL }; + accumulator acc; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; spdylay_data_provider data_prd = {{-1}, NULL}; spdylay_outbound_item *item; + my_user_data ud; + spdylay_frame frame; + acc.length = 0; + ud.acc = &acc; memset(&callbacks, 0, sizeof(callbacks)); - callbacks.send_callback = null_send_callback; + callbacks.send_callback = accumulator_send_callback; CU_ASSERT(0 == spdylay_session_server_new(&session, SPDYLAY_PROTO_SPDY2, - &callbacks, NULL)); + &callbacks, &ud)); spdylay_session_open_stream(session, 1, SPDYLAY_CTRL_FLAG_FIN, 3, SPDYLAY_STREAM_OPENING, NULL); CU_ASSERT(0 == spdylay_submit_response(session, 1, nv, &data_prd)); item = spdylay_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("version", item->frame->syn_reply.nv[0])); + CU_ASSERT(0 == strcmp(":version", item->frame->syn_reply.nv[0])); CU_ASSERT(item->frame->syn_reply.hd.flags & SPDYLAY_CTRL_FLAG_FIN); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == spdylay_frame_unpack_syn_reply(&frame.syn_reply, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + &acc.buf[0], SPDYLAY_HEAD_LEN, + &acc.buf[SPDYLAY_HEAD_LEN], + acc.length-SPDYLAY_HEAD_LEN, + &session->hd_inflater)); + CU_ASSERT(0 == strcmp("version", frame.syn_reply.nv[0])); spdylay_session_del(session); } @@ -627,23 +642,36 @@ void test_spdylay_submit_request_with_data() void test_spdylay_submit_request_with_null_data_read_callback() { spdylay_session *session; - spdylay_session_callbacks callbacks = { - null_send_callback, - NULL, - NULL, - NULL - }; - const char *nv[] = { "Version", "HTTP/1.1", NULL }; + spdylay_session_callbacks callbacks; + accumulator acc; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; spdylay_data_provider data_prd = {{-1}, NULL}; spdylay_outbound_item *item; + my_user_data ud; + spdylay_frame frame; + acc.length = 0; + ud.acc = &acc; + memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks.send_callback = accumulator_send_callback; CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, - &callbacks, NULL)); + &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 == strcmp(":version", item->frame->syn_stream.nv[0])); CU_ASSERT(item->frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(0 == spdylay_frame_unpack_syn_stream(&frame.syn_stream, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + &acc.buf[0], SPDYLAY_HEAD_LEN, + &acc.buf[SPDYLAY_HEAD_LEN], + acc.length-SPDYLAY_HEAD_LEN, + &session->hd_inflater)); + CU_ASSERT(0 == strcmp("version", frame.syn_stream.nv[0])); + spdylay_session_del(session); } @@ -724,20 +752,24 @@ void test_spdylay_submit_headers() { spdylay_session *session; spdylay_session_callbacks callbacks; - const char *nv[] = { "version", "HTTP/1.1", NULL }; + const char *nv[] = { ":Version", "HTTP/1.1", NULL }; my_user_data ud; spdylay_outbound_item *item; spdylay_stream *stream; + accumulator acc; + spdylay_frame frame; + acc.length = 0; + ud.acc = &acc; memset(&callbacks, 0, sizeof(spdylay_session_callbacks)); - callbacks.send_callback = null_send_callback; + callbacks.send_callback = accumulator_send_callback; callbacks.on_ctrl_send_callback = on_ctrl_send_callback; CU_ASSERT(0 == spdylay_session_client_new(&session, SPDYLAY_PROTO_SPDY2, &callbacks, &ud)); CU_ASSERT(0 == spdylay_submit_headers(session, SPDYLAY_CTRL_FLAG_FIN, 1, nv)); item = spdylay_session_get_next_ob_item(session); - CU_ASSERT(0 == strcmp("version", item->frame->headers.nv[0])); + CU_ASSERT(0 == strcmp(":version", item->frame->headers.nv[0])); CU_ASSERT(SPDYLAY_CTRL_FLAG_FIN == item->frame->headers.hd.flags); ud.ctrl_send_cb_called = 0; @@ -754,6 +786,16 @@ void test_spdylay_submit_headers() CU_ASSERT(SPDYLAY_HEADERS == ud.sent_frame_type); CU_ASSERT(stream->shut_flags & SPDYLAY_SHUT_WR); + CU_ASSERT(0 == spdylay_frame_unpack_headers(&frame.headers, + &session->inflatebuf, + &session->nvbuf, + &session->nvbuflen, + &acc.buf[0], SPDYLAY_HEAD_LEN, + &acc.buf[SPDYLAY_HEAD_LEN], + acc.length-SPDYLAY_HEAD_LEN, + &session->hd_inflater)); + CU_ASSERT(0 == strcmp("version", frame.headers.nv[0])); + spdylay_session_del(session); }