Don't pack multiple empty header values in one header field

SPDY spec does not allow multiple empty header values in one header
field. This change makes out-going framer ignore such empty header
value if there is non-empty header value with the same name.
This commit is contained in:
Tatsuhiro Tsujikawa 2013-03-23 19:31:22 +09:00
parent 2ca7b51eb6
commit 10c54e44ba
4 changed files with 150 additions and 14 deletions

View File

@ -250,17 +250,28 @@ size_t spdylay_frame_count_nv_space(char **nv, size_t len_size)
int i; int i;
const char *prev = ""; const char *prev = "";
size_t prevlen = 0; size_t prevlen = 0;
size_t prevvallen = 0;
for(i = 0; nv[i]; i += 2) { for(i = 0; nv[i]; i += 2) {
const char *key = nv[i]; const char *key = nv[i];
const char *val = nv[i+1]; const char *val = nv[i+1];
size_t keylen = strlen(key); size_t keylen = strlen(key);
size_t vallen = strlen(val); size_t vallen = strlen(val);
if(prevlen == keylen && memcmp(prev, key, keylen) == 0) { if(prevlen == keylen && memcmp(prev, key, keylen) == 0) {
/* Join previous value, with NULL character */ if(vallen) {
sum += vallen+1; if(prevvallen) {
/* Join previous value, with NULL character */
sum += vallen+1;
prevvallen = vallen;
} else {
/* Previous value is empty. In this case, drop the
previous. */
sum += vallen;
}
}
} else { } else {
prev = key; prev = key;
prevlen = keylen; prevlen = keylen;
prevvallen = vallen;
/* SPDY NV header does not include terminating NULL byte */ /* SPDY NV header does not include terminating NULL byte */
sum += keylen+vallen+len_size*2; sum += keylen+vallen+len_size*2;
} }
@ -273,28 +284,43 @@ ssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv, size_t len_size)
int i; int i;
uint8_t *bufp = buf+len_size; uint8_t *bufp = buf+len_size;
uint32_t num_nv = 0; uint32_t num_nv = 0;
/* TODO Join values with same keys, using '\0' as a delimiter */
const char *prev = ""; const char *prev = "";
uint8_t *prev_vallen_buf = NULL; uint8_t *cur_vallen_buf = NULL;
uint32_t prev_vallen = 0; uint32_t cur_vallen = 0;
size_t prevkeylen = 0;
size_t prevvallen = 0;
for(i = 0; nv[i]; i += 2) { for(i = 0; nv[i]; i += 2) {
const char *key = nv[i]; const char *key = nv[i];
const char *val = nv[i+1]; const char *val = nv[i+1];
size_t keylen = strlen(key); size_t keylen = strlen(key);
size_t vallen = strlen(val); size_t vallen = strlen(val);
if(strcmp(prev, key) == 0) { if(prevkeylen == keylen && memcmp(prev, key, keylen) == 0) {
prev_vallen += vallen+1; if(vallen) {
spdylay_frame_put_nv_len(prev_vallen_buf, prev_vallen, len_size); if(prevvallen) {
*bufp = '\0'; /* Join previous value, with NULL character */
++bufp; cur_vallen += vallen+1;
memcpy(bufp, val, vallen); spdylay_frame_put_nv_len(cur_vallen_buf, cur_vallen, len_size);
bufp += vallen; *bufp = '\0';
++bufp;
memcpy(bufp, val, vallen);
bufp += vallen;
} else {
/* Previous value is empty. In this case, drop the
previous. */
cur_vallen += vallen;
spdylay_frame_put_nv_len(cur_vallen_buf, cur_vallen, len_size);
memcpy(bufp, val, vallen);
bufp += vallen;
}
}
} else { } else {
++num_nv; ++num_nv;
bufp = spdylay_pack_str(bufp, key, keylen, len_size); bufp = spdylay_pack_str(bufp, key, keylen, len_size);
prev = key; prev = key;
prev_vallen_buf = bufp; cur_vallen_buf = bufp;
prev_vallen = vallen; cur_vallen = vallen;
prevkeylen = keylen;
prevvallen = vallen;
bufp = spdylay_pack_str(bufp, val, vallen, len_size); bufp = spdylay_pack_str(bufp, val, vallen, len_size);
} }
} }

View File

@ -210,6 +210,10 @@ int main(int argc, char* argv[])
test_spdylay_frame_nv_downcase) || test_spdylay_frame_nv_downcase) ||
!CU_add_test(pSuite, "frame_pack_nv_duplicate_keys", !CU_add_test(pSuite, "frame_pack_nv_duplicate_keys",
test_spdylay_frame_pack_nv_duplicate_keys) || test_spdylay_frame_pack_nv_duplicate_keys) ||
!CU_add_test(pSuite, "frame_pack_nv_empty_value_spdy2",
test_spdylay_frame_pack_nv_empty_value_spdy2) ||
!CU_add_test(pSuite, "frame_pack_nv_empty_value_spdy3",
test_spdylay_frame_pack_nv_empty_value_spdy3) ||
!CU_add_test(pSuite, "frame_nv_2to3", test_spdylay_frame_nv_2to3) || !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, "frame_nv_3to2", test_spdylay_frame_nv_3to2) ||
!CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy2", !CU_add_test(pSuite, "frame_unpack_nv_check_name_spdy2",

View File

@ -24,12 +24,29 @@
*/ */
#include "spdylay_frame_test.h" #include "spdylay_frame_test.h"
#include <assert.h>
#include <CUnit/CUnit.h> #include <CUnit/CUnit.h>
#include "spdylay_frame.h" #include "spdylay_frame.h"
#include "spdylay_helper.h" #include "spdylay_helper.h"
#include "spdylay_test_helper.h" #include "spdylay_test_helper.h"
/* Reads |len_size| byte from |data| as |len_size| byte network byte
order integer, and returns it in host byte order. Currently, we
only support len_size == 2 or 4 */
static int get_packed_hd_len(uint8_t *data, size_t len_size)
{
if(len_size == 2) {
return spdylay_get_uint16(data);
} else if(len_size == 4) {
return spdylay_get_uint32(data);
} else {
/* Not supported */
assert(0);
}
}
static const char *headers[] = { static const char *headers[] = {
"method", "GET", "method", "GET",
"scheme", "https", "scheme", "https",
@ -181,12 +198,99 @@ void test_spdylay_frame_pack_nv_duplicate_keys(void)
spdylay_frame_nv_del(nv); spdylay_frame_nv_del(nv);
} }
static const char *multi_empty_headers1[] = {
"a", "",
"a", "",
NULL
};
static const char *multi_empty_headers2[] = {
"a", "/",
"a", "",
NULL
};
static const char *multi_empty_headers3[] = {
"a", "",
"a", "/",
NULL
};
void test_spdylay_frame_count_nv_space(void) void test_spdylay_frame_count_nv_space(void)
{ {
size_t len_size = 2; size_t len_size = 2;
CU_ASSERT(85 == spdylay_frame_count_nv_space((char**)headers, len_size)); CU_ASSERT(85 == spdylay_frame_count_nv_space((char**)headers, len_size));
len_size = 4; len_size = 4;
CU_ASSERT(111 == spdylay_frame_count_nv_space((char**)headers, len_size)); CU_ASSERT(111 == spdylay_frame_count_nv_space((char**)headers, len_size));
/* only ("a", "") is counted */
CU_ASSERT(13 == spdylay_frame_count_nv_space((char**)multi_empty_headers1,
len_size));
/* only ("a", "/") is counted */
CU_ASSERT(14 == spdylay_frame_count_nv_space((char**)multi_empty_headers2,
len_size));
/* only ("a", "/") is counted */
CU_ASSERT(14 == spdylay_frame_count_nv_space((char**)multi_empty_headers3,
len_size));
}
static void frame_pack_nv_empty_value_check(uint8_t *outptr,
int vallen,
const char *val,
size_t len_size)
{
int len;
len = get_packed_hd_len(outptr, len_size);
CU_ASSERT(1 == len);
outptr += len_size;
len = get_packed_hd_len(outptr, len_size);
CU_ASSERT(1 == len);
outptr += len_size;
CU_ASSERT(0 == memcmp("a", outptr, len));
outptr += len;
len = get_packed_hd_len(outptr, len_size);
CU_ASSERT(vallen == len);
len += len_size;
if(vallen == len) {
CU_ASSERT(0 == memcmp(val, outptr, vallen));
}
}
static void test_spdylay_frame_pack_nv_empty_value_with(size_t len_size)
{
uint8_t out[256];
char **nv;
ssize_t rv;
int off = (len_size == 2 ? -6 : 0);
nv = spdylay_frame_nv_copy(multi_empty_headers1);
rv = spdylay_frame_pack_nv(out, nv, len_size);
CU_ASSERT(13+off == rv);
frame_pack_nv_empty_value_check(out, 0, NULL, len_size);
spdylay_frame_nv_del(nv);
nv = spdylay_frame_nv_copy(multi_empty_headers2);
rv = spdylay_frame_pack_nv(out, nv, len_size);
CU_ASSERT(14+off == rv);
frame_pack_nv_empty_value_check(out, 1, "/", len_size);
spdylay_frame_nv_del(nv);
nv = spdylay_frame_nv_copy(multi_empty_headers3);
rv = spdylay_frame_pack_nv(out, nv, len_size);
CU_ASSERT(14+off == rv);
frame_pack_nv_empty_value_check(out, 1, "/", len_size);
spdylay_frame_nv_del(nv);
}
void test_spdylay_frame_pack_nv_empty_value_spdy2(void)
{
test_spdylay_frame_pack_nv_empty_value_with
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY2));
}
void test_spdylay_frame_pack_nv_empty_value_spdy3(void)
{
test_spdylay_frame_pack_nv_empty_value_with
(spdylay_frame_get_len_size(SPDYLAY_PROTO_SPDY3));
} }
void test_spdylay_frame_count_unpack_nv_space(void) void test_spdylay_frame_count_unpack_nv_space(void)

View File

@ -28,6 +28,8 @@
void test_spdylay_frame_unpack_nv_spdy2(void); void test_spdylay_frame_unpack_nv_spdy2(void);
void test_spdylay_frame_unpack_nv_spdy3(void); void test_spdylay_frame_unpack_nv_spdy3(void);
void test_spdylay_frame_pack_nv_duplicate_keys(void); void test_spdylay_frame_pack_nv_duplicate_keys(void);
void test_spdylay_frame_pack_nv_empty_value_spdy2(void);
void test_spdylay_frame_pack_nv_empty_value_spdy3(void);
void test_spdylay_frame_count_nv_space(void); void test_spdylay_frame_count_nv_space(void);
void test_spdylay_frame_count_unpack_nv_space(void); void test_spdylay_frame_count_unpack_nv_space(void);
void test_spdylay_frame_pack_ping(void); void test_spdylay_frame_pack_ping(void);