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).
This commit is contained in:
Tatsuhiro Tsujikawa 2012-02-08 21:20:50 +09:00
parent c07f780e52
commit b8700259fd
3 changed files with 66 additions and 14 deletions

View File

@ -461,10 +461,35 @@ int spdylay_submit_ping(spdylay_session *session);
int spdylay_submit_goaway(spdylay_session *session); int spdylay_submit_goaway(spdylay_session *session);
/* /*
* A helper function for dealing with NPN. This function always * A helper function for dealing with NPN in client side.
* selects "spdy/2" protocol. NPN draft permits that client can * |in| contains server's protocol in preferable order.
* select any protocol even if server does not advertise it at the * The format of |in| is length-prefixed and not null-terminated.
* time of this writing: * 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 * 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 * 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 * Selecting "spdy/2" means that "spdy/2" is written into |*out| and
* length of "spdy/2" (which is 6) is assigned to |*outlen|. * 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: * To use this method you should do something like:
* *
* static int select_next_proto_cb(SSL* ssl, * 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, * const unsigned char *in, unsigned int inlen,
* void *arg) * 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; * ((MyType*)arg)->spdy = 1;
* } * }
* return SSL_TLSEXT_ERR_OK; * return SSL_TLSEXT_ERR_OK;

View File

@ -29,13 +29,27 @@
int spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen, int spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen,
const unsigned char *in, unsigned int inlen) const unsigned char *in, unsigned int inlen)
{ {
int http_selected = 0;
unsigned int i = 0; unsigned int i = 0;
/* For non-overlap case */
*out = (unsigned char*)"spdy/2"; *out = (unsigned char*)"spdy/2";
*outlen = 6; *outlen = 6;
for(; i < inlen; i += in[i]+1) { for(; i < inlen; i += in[i]+1) {
if(in[i] == 6 && memcmp(&in[i+1], "spdy/2", in[i]) == 0) { if(in[i] == 6 && memcmp(&in[i+1], "spdy/2", in[i]) == 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 */
}
}
if(http_selected) {
return 0; return 0;
} } else {
}
return -1; return -1;
}
} }

View File

@ -30,19 +30,34 @@
static void spdy2() static void spdy2()
{ {
const unsigned char spdy[] = { 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', '/', '2',
6, 's', 'p', 'd', 'y', '/', '3' 6, 's', 'p', 'd', 'y', '/', '3'
}; };
unsigned char outlen; unsigned char outlen;
unsigned char* out; unsigned char* out;
CU_ASSERT(0 == spdylay_select_next_protocol(&out, &outlen, CU_ASSERT(1 == spdylay_select_next_protocol(&out, &outlen,
spdy, sizeof(spdy))); spdy, sizeof(spdy)));
CU_ASSERT(6 == outlen); CU_ASSERT(6 == outlen);
CU_ASSERT(memcmp("spdy/2", out, outlen) == 0); 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[] = { const unsigned char spdy[] = {
6, 's', 'p', 'd', 'y', '/', '4', 6, 's', 'p', 'd', 'y', '/', '4',
@ -60,5 +75,6 @@ static void spdy4()
void test_spdylay_npn() void test_spdylay_npn()
{ {
spdy2(); spdy2();
spdy4(); http11();
no_overlap();
} }