From b8700259fdcce7c6bf99ece4c0589316a7a17dd0 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 8 Feb 2012 21:20:50 +0900 Subject: [PATCH] Changed behaviour of spdylay_select_next_protocol() We use following algorithm to select protocol: 1. If server's list contains "spdy/2", this function selects "spdy/2" and returns 1. The following steps are not taken. 2. If server's list contains "http/1.1", this function selects "http/1.1" and returns 0. The following step is not taken. 3. This function selects "spdy/2" and returns -1. (So called non-overlap case). --- lib/includes/spdylay/spdylay.h | 38 +++++++++++++++++++++++++++------- lib/spdylay_npn.c | 18 ++++++++++++++-- tests/spdylay_npn_test.c | 24 +++++++++++++++++---- 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 24748f96..6daa788b 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -461,10 +461,35 @@ int spdylay_submit_ping(spdylay_session *session); int spdylay_submit_goaway(spdylay_session *session); /* - * A helper function for dealing with NPN. This function always - * selects "spdy/2" protocol. NPN draft permits that client can - * select any protocol even if server does not advertise it at the - * time of this writing: + * A helper function for dealing with NPN in client side. + * |in| contains server's protocol in preferable order. + * The format of |in| is length-prefixed and not null-terminated. + * For example, "spdy/2" are "http/1.1" stored in |in| like this: + * + * in[0] = 6 + * in[1..6] = "spdy/2" + * in[7] = 8 + * in[8..15] = "http/1.1" + * inlen = 16 + * + * The selection algorithm is as follows: + * + * 1. If server's list contains "spdy/2", this function selects + * "spdy/2" and returns 1. The following steps are not taken. + * + * 2. If server's list contains "http/1.1", this function selects + * "http/1.1" and returns 0. The following step is not taken. + * + * 3. This function selects "spdy/2" and returns -1. (So called + * non-overlap case). + * + * When spdylay supports updated version of SPDY in the future, this + * function may select updated protocol and application code which + * relies on spdylay for SPDY stuff needs not be modified. + * + * For rationale of step 3, NPN draft permits that client can select + * any protocol even if server does not advertise it at the time of + * this writing: * * It's expected that a client will have a list of protocols that it * supports, in preference order, and will only select a protocol if @@ -481,9 +506,6 @@ int spdylay_submit_goaway(spdylay_session *session); * Selecting "spdy/2" means that "spdy/2" is written into |*out| and * length of "spdy/2" (which is 6) is assigned to |*outlen|. * - * This function returns 0 if server advertised "spdy/2" and it is - * selected, or -1. - * * To use this method you should do something like: * * static int select_next_proto_cb(SSL* ssl, @@ -491,7 +513,7 @@ int spdylay_submit_goaway(spdylay_session *session); * const unsigned char *in, unsigned int inlen, * void *arg) * { - * if (spdylay_select_next_protocol(out, outlen, in, inlen) == 0) { + * if (spdylay_select_next_protocol(out, outlen, in, inlen) == 1) { * ((MyType*)arg)->spdy = 1; * } * return SSL_TLSEXT_ERR_OK; diff --git a/lib/spdylay_npn.c b/lib/spdylay_npn.c index bf5c52cd..5171c4cd 100644 --- a/lib/spdylay_npn.c +++ b/lib/spdylay_npn.c @@ -29,13 +29,27 @@ int spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen) { + int http_selected = 0; unsigned int i = 0; + /* For non-overlap case */ *out = (unsigned char*)"spdy/2"; *outlen = 6; for(; i < inlen; i += in[i]+1) { if(in[i] == 6 && memcmp(&in[i+1], "spdy/2", in[i]) == 0) { - return 0; + *out = &in[i+1]; + *outlen = in[i]; + return 1; + } else if(in[i] == 8 && memcmp(&in[i+1], "http/1.1", in[i]) == 0) { + http_selected = 1; + *out = &in[i+1]; + *outlen = in[i]; + /* Go through to the next iteration, because "spdy/2" may be + there */ } } - return -1; + if(http_selected) { + return 0; + } else { + return -1; + } } diff --git a/tests/spdylay_npn_test.c b/tests/spdylay_npn_test.c index 8b2420fe..f49a7194 100644 --- a/tests/spdylay_npn_test.c +++ b/tests/spdylay_npn_test.c @@ -30,19 +30,34 @@ static void spdy2() { const unsigned char spdy[] = { - 8, 'h', 't', 't', 'p', '/', '1', '.', '0', + 8, 'h', 't', 't', 'p', '/', '1', '.', '1', 6, 's', 'p', 'd', 'y', '/', '2', 6, 's', 'p', 'd', 'y', '/', '3' }; unsigned char outlen; unsigned char* out; - CU_ASSERT(0 == spdylay_select_next_protocol(&out, &outlen, + CU_ASSERT(1 == spdylay_select_next_protocol(&out, &outlen, spdy, sizeof(spdy))); CU_ASSERT(6 == outlen); CU_ASSERT(memcmp("spdy/2", out, outlen) == 0); } -static void spdy4() +static void http11() +{ + const unsigned char spdy[] = { + 6, 's', 'p', 'd', 'y', '/', '4', + 8, 's', 'p', 'd', 'y', '/', '2', '.', '1', + 8, 'h', 't', 't', 'p', '/', '1', '.', '1', + }; + unsigned char outlen; + unsigned char* out; + CU_ASSERT(0 == spdylay_select_next_protocol(&out, &outlen, + spdy, sizeof(spdy))); + CU_ASSERT(8 == outlen); + CU_ASSERT(memcmp("http/1.1", out, outlen) == 0); +} + +static void no_overlap() { const unsigned char spdy[] = { 6, 's', 'p', 'd', 'y', '/', '4', @@ -60,5 +75,6 @@ static void spdy4() void test_spdylay_npn() { spdy2(); - spdy4(); + http11(); + no_overlap(); }