diff --git a/lib/spdylay_buffer.c b/lib/spdylay_buffer.c index eb5dbf6..e321594 100644 --- a/lib/spdylay_buffer.c +++ b/lib/spdylay_buffer.c @@ -27,6 +27,9 @@ #include #include +#include "spdylay_net.h" +#include "spdylay_helper.h" + void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity) { buffer->root.data = NULL; @@ -99,6 +102,27 @@ void spdylay_buffer_advance(spdylay_buffer *buffer, size_t amount) assert(buffer->last_offset <= buffer->capacity); } +int spdylay_buffer_write(spdylay_buffer *buffer, const uint8_t *data, + size_t len) +{ + int rv; + size_t i; + while(len) { + size_t writelen; + if(spdylay_buffer_avail(buffer) == 0) { + if((rv = spdylay_buffer_alloc(buffer)) != 0) { + return rv; + } + } + writelen = spdylay_min(spdylay_buffer_avail(buffer), len); + memcpy(spdylay_buffer_get(buffer), data, writelen); + data += writelen; + len -= writelen; + spdylay_buffer_advance(buffer, writelen); + } + return 0; +} + size_t spdylay_buffer_length(spdylay_buffer *buffer) { return buffer->len; @@ -130,3 +154,66 @@ void spdylay_buffer_reset(spdylay_buffer *buffer) buffer->len = 0; buffer->last_offset = buffer->capacity; } + +void spdylay_buffer_reader_init(spdylay_buffer_reader *reader, + spdylay_buffer *buffer) +{ + reader->buffer = buffer; + reader->current = buffer->root.next; + reader->offset = 0; +} + +uint8_t spdylay_buffer_reader_uint8(spdylay_buffer_reader *reader) +{ + uint8_t out; + spdylay_buffer_reader_data(reader, &out, sizeof(uint8_t)); + return out; +} + +uint16_t spdylay_buffer_reader_uint16(spdylay_buffer_reader *reader) +{ + uint16_t out; + spdylay_buffer_reader_data(reader, (uint8_t*)&out, sizeof(uint16_t)); + return ntohs(out); +} + +uint32_t spdylay_buffer_reader_uint32(spdylay_buffer_reader *reader) +{ + uint32_t out; + spdylay_buffer_reader_data(reader, (uint8_t*)&out, sizeof(uint32_t)); + return ntohl(out); +} + +void spdylay_buffer_reader_data(spdylay_buffer_reader *reader, + uint8_t *out, size_t len) +{ + while(len) { + size_t remlen, readlen; + remlen = reader->buffer->capacity - reader->offset; + readlen = spdylay_min(remlen, len); + memcpy(out, reader->current->data + reader->offset, readlen); + out += readlen; + len -= readlen; + reader->offset += readlen; + if(reader->buffer->capacity == reader->offset) { + reader->current = reader->current->next; + reader->offset = 0; + } + } +} + +void spdylay_buffer_reader_advance(spdylay_buffer_reader *reader, + size_t amount) +{ + while(amount) { + size_t remlen, skiplen; + remlen = reader->buffer->capacity - reader->offset; + skiplen = spdylay_min(remlen, amount); + amount -= skiplen; + reader->offset += skiplen; + if(reader->buffer->capacity == reader->offset) { + reader->current = reader->current->next; + reader->offset = 0; + } + } +} diff --git a/lib/spdylay_buffer.h b/lib/spdylay_buffer.h index 980e1d5..38c0f31 100644 --- a/lib/spdylay_buffer.h +++ b/lib/spdylay_buffer.h @@ -66,6 +66,23 @@ size_t spdylay_buffer_avail(spdylay_buffer *buffer); /* Advances buffer pointer by amount. This reduces available buffer length. */ void spdylay_buffer_advance(spdylay_buffer *buffer, size_t amount); + +/* + * Writes the |data| with the |len| bytes starting at the current + * position of the |buffer|. The new chunk buffer will be allocated on + * the course of the write and the current position is updated. If + * this function succeeds, the total length of the |buffer| will be + * increased by |len|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * SPDYLAY_ERR_NOMEM + * Out of memory. + */ +int spdylay_buffer_write(spdylay_buffer *buffer, const uint8_t *data, + size_t len); + /* * Allocate new chunk buffer. This will increase total length of * buffer (returned by spdylay_buffer_length) by capacity-last_offset. @@ -94,4 +111,53 @@ void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf); allocated memory space; they are reused. */ void spdylay_buffer_reset(spdylay_buffer *buffer); +/* + * Reader interface to read data from spdylay_buffer sequentially. + */ +typedef struct { + /* The buffer to read */ + spdylay_buffer *buffer; + /* Pointer to the current chunk to read. */ + spdylay_buffer_chunk *current; + /* Offset to the current chunk data to read. */ + size_t offset; +} spdylay_buffer_reader; + +/* + * Initializes the |reader| with the |buffer|. + */ +void spdylay_buffer_reader_init(spdylay_buffer_reader *reader, + spdylay_buffer *buffer); + +/* + * Reads 1 byte and return it. This function will advance the current + * position by 1. + */ +uint8_t spdylay_buffer_reader_uint8(spdylay_buffer_reader *reader); + +/* + * Reads 2 bytes integer in network byte order and returns it in host + * byte order. This function will advance the current position by 2. + */ +uint16_t spdylay_buffer_reader_uint16(spdylay_buffer_reader *reader); + +/* + * Reads 4 bytes integer in network byte order and returns it in host + * byte order. This function will advance the current position by 4. + */ +uint32_t spdylay_buffer_reader_uint32(spdylay_buffer_reader *reader); + +/* + * Reads |len| bytes and store them in the |out|. This function will + * advance the current position by |len|. + */ +void spdylay_buffer_reader_data(spdylay_buffer_reader *reader, + uint8_t *out, size_t len); + +/* + * Advances the current position by |amount|. + */ +void spdylay_buffer_reader_advance(spdylay_buffer_reader *reader, + size_t amount); + #endif /* SPDYLAY_BUFFER_H */ diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index 03cd4dd..efefd39 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -110,20 +110,23 @@ ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, return framelen; } -int spdylay_frame_count_unpack_nv_space -(size_t *nvlen_ptr, size_t *buflen_ptr, const uint8_t *in, size_t inlen, - size_t len_size) +int spdylay_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr, + spdylay_buffer *in, size_t len_size) { uint32_t n; size_t buflen = 0; size_t nvlen = 0; size_t off = 0; + size_t inlen = spdylay_buffer_length(in); size_t i; + spdylay_buffer_reader reader; if(inlen < len_size) { return SPDYLAY_ERR_INVALID_FRAME; } + spdylay_buffer_reader_init(&reader, in); + /* TODO limit n in a reasonable number */ - n = spdylay_frame_get_nv_len(in, len_size); + n = spdylay_frame_get_nv_len(&reader, len_size); off += len_size; for(i = 0; i < n; ++i) { uint32_t len; @@ -132,16 +135,20 @@ int spdylay_frame_count_unpack_nv_space if(inlen-off < len_size) { return SPDYLAY_ERR_INVALID_FRAME; } - len = spdylay_frame_get_nv_len(in+off, len_size); + len = spdylay_frame_get_nv_len(&reader, len_size); off += len_size; if(inlen-off < len) { return SPDYLAY_ERR_INVALID_FRAME; } buflen += len+1; off += len; + if(j == 0) { + spdylay_buffer_reader_advance(&reader, len); + } } for(j = off, off -= len; off != j; ++off) { - if(in[off] == '\0') { + uint8_t b = spdylay_buffer_reader_uint8(&reader); + if(b == '\0') { ++nvlen; } } @@ -149,7 +156,6 @@ int spdylay_frame_count_unpack_nv_space } if(inlen == off) { *nvlen_ptr = nvlen; - *buflen_ptr = buflen+(nvlen*2+1)*sizeof(char*); return 0; } else { @@ -157,89 +163,7 @@ int spdylay_frame_count_unpack_nv_space } } -static int spdylay_length_prefix_str_compar2(const void *lhs, const void *rhs) -{ - ssize_t lhslen, rhslen, complen; - int r; - lhslen = spdylay_get_uint16(*(uint8_t**)lhs); - rhslen = spdylay_get_uint16(*(uint8_t**)rhs); - complen = spdylay_min(lhslen, rhslen); - r = memcmp(*(uint8_t**)lhs+2, *(uint8_t**)rhs+2, complen); - if(r == 0) { - return lhslen-rhslen; - } else { - return r; - } -} - -static int spdylay_length_prefix_str_compar4(const void *lhs, const void *rhs) -{ - ssize_t lhslen, rhslen, complen; - int r; - /* Assuming the returned value does not exceed the maximum value of - ssize_t */ - lhslen = spdylay_get_uint32(*(uint8_t**)lhs); - rhslen = spdylay_get_uint32(*(uint8_t**)rhs); - complen = spdylay_min(lhslen, rhslen); - r = memcmp(*(uint8_t**)lhs+4, *(uint8_t**)rhs+4, complen); - if(r == 0) { - return lhslen-rhslen; - } else { - return r; - } -} - -int spdylay_frame_unpack_nv_check_name(uint8_t *buf, size_t buflen, - const uint8_t *in, size_t inlen, - size_t len_size) -{ - uint32_t n; - size_t i; - const uint8_t **idx; - n = spdylay_frame_get_nv_len(in, len_size); - assert(n*sizeof(char*) <= buflen); - in += len_size; - idx = (const uint8_t**)buf; - for(i = 0; i < n; ++i) { - uint32_t len; - size_t j; - len = spdylay_frame_get_nv_len(in, len_size); - if(len == 0) { - return SPDYLAY_ERR_INVALID_HEADER_BLOCK; - } - *idx++ = in; - in += len_size; - for(j = 0; j < len; ++j) { - unsigned char c = in[j]; - if(c < 0x20 || c > 0x7e || ('A' <= c && c <= 'Z')) { - return SPDYLAY_ERR_INVALID_HEADER_BLOCK; - } - } - in += len; - len = spdylay_frame_get_nv_len(in, len_size); - in += len_size+len; - } - if(n > 0) { - uint32_t len1, len2; - qsort(buf, n, sizeof(uint8_t*), - len_size == 2 ? - spdylay_length_prefix_str_compar2 : - spdylay_length_prefix_str_compar4); - idx = (const uint8_t**)buf; - len1 = spdylay_frame_get_nv_len(*idx, len_size); - for(i = 1; i < n; ++i) { - len2 = spdylay_frame_get_nv_len(*(idx+i), len_size); - if(len1 == len2 && memcmp(*(idx+i-1)+len_size, *(idx+i)+len_size, - len1) == 0) { - return SPDYLAY_ERR_INVALID_HEADER_BLOCK; - } - len1 = len2; - } - } - return 0; -} - -int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, +int spdylay_frame_unpack_nv(char ***nv_ptr, spdylay_buffer *in, size_t len_size) { size_t nvlen, buflen; @@ -248,44 +172,42 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, char *buf, **idx, *data; uint32_t n; int invalid_header_block = 0; - r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, inlen, len_size); + spdylay_buffer_reader reader; + r = spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, in, len_size); if(r != 0) { return r; } + buf = malloc(buflen); if(buf == NULL) { return SPDYLAY_ERR_NOMEM; } - r = spdylay_frame_unpack_nv_check_name((uint8_t*)buf, buflen, in, inlen, - len_size); - if(r == SPDYLAY_ERR_INVALID_HEADER_BLOCK) { - invalid_header_block = 1; - r = 0; - } else if(r != 0) { - free(buf); - return r; - } + spdylay_buffer_reader_init(&reader, in); idx = (char**)buf; data = buf+(nvlen*2+1)*sizeof(char*); - n = spdylay_frame_get_nv_len(in, len_size); - in += len_size; + n = spdylay_frame_get_nv_len(&reader, len_size); for(i = 0; i < n; ++i) { uint32_t len; char *name, *val; char *stop; - len = spdylay_frame_get_nv_len(in, len_size); - in += len_size; + len = spdylay_frame_get_nv_len(&reader, len_size); + if(len == 0) { + invalid_header_block = 1; + } name = data; - memcpy(data, in, len); - data += len; + spdylay_buffer_reader_data(&reader, (uint8_t*)data, len); + for(stop = data+len; data != stop; ++data) { + unsigned char c = *data; + if(c < 0x20 || c > 0x7e || ('A' <= c && c <= 'Z')) { + invalid_header_block = 1; + } + } *data = '\0'; ++data; - in += len; - len = spdylay_frame_get_nv_len(in, len_size); - in += len_size; + len = spdylay_frame_get_nv_len(&reader, len_size); val = data; - memcpy(data, in, len); + spdylay_buffer_reader_data(&reader, (uint8_t*)data, len); for(stop = data+len; data != stop; ++data) { if(*data == '\0') { @@ -299,7 +221,6 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, } *data = '\0'; ++data; - in += len; *idx++ = name; *idx++ = val; @@ -307,31 +228,17 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, *idx = NULL; assert((size_t)((char*)idx - buf) == (nvlen*2)*sizeof(char*)); *nv_ptr = (char**)buf; - return invalid_header_block ? SPDYLAY_ERR_INVALID_HEADER_BLOCK : 0; -} - -int spdylay_frame_alloc_unpack_nv(char ***nv_ptr, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, - const uint8_t *in, size_t inlen, - size_t len_size, - spdylay_zlib *inflater) -{ - ssize_t nvspace; - int r; - nvspace = spdylay_zlib_inflate_hd(inflater, inflatebuf, in, inlen); - if(nvspace < 0) { - return nvspace; - } else { - r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace); - if(r != 0) { - return SPDYLAY_ERR_NOMEM; + if(!invalid_header_block) { + spdylay_frame_nv_sort(*nv_ptr); + for(i = 2; i < nvlen*2; i += 2) { + if(strcmp((*nv_ptr)[i-2], (*nv_ptr)[i]) == 0 && + (*nv_ptr)[i-2] != (*nv_ptr)[i]) { + invalid_header_block = 1; + break; + } } - spdylay_buffer_serialize(inflatebuf, *nvbuf_ptr); - r = spdylay_frame_unpack_nv(nv_ptr, *nvbuf_ptr, nvspace, len_size); - return r; } + return invalid_header_block ? SPDYLAY_ERR_INVALID_HEADER_BLOCK : 0; } size_t spdylay_frame_count_nv_space(char **nv, size_t len_size) @@ -730,8 +637,6 @@ void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, void spdylay_frame_data_free(spdylay_data *frame) {} -#define SPDYLAY_SYN_STREAM_NV_OFFSET 18 - ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, size_t *buflen_ptr, uint8_t **nvbuf_ptr, @@ -767,16 +672,13 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, } int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - spdylay_zlib *inflater) + spdylay_buffer *inflatebuf) { int r; size_t len_size; - if(payloadlen < 12) { + if(headlen + payloadlen != SPDYLAY_SYN_STREAM_NV_OFFSET) { return SPDYLAY_ERR_INVALID_FRAME; } spdylay_frame_unpack_ctrl_hd(&frame->hd, head); @@ -793,17 +695,10 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, } else { frame->slot = 0; } - r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, - nvbuf_ptr, nvbuflen_ptr, - payload+10, payloadlen-10, - len_size, - inflater); + r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); return r; } -#define SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET 14 -#define SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET 12 - ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, size_t *buflen_ptr, uint8_t **nvbuf_ptr, @@ -813,14 +708,13 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, { ssize_t framelen; size_t len_size; - size_t nv_offset; + ssize_t nv_offset; len_size = spdylay_frame_get_len_size(frame->hd.version); if(len_size == 0) { return SPDYLAY_ERR_UNSUPPORTED_VERSION; } - nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? - SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET : SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET; - + nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version); + assert(nv_offset > 0); framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, nvbuf_ptr, nvbuflen_ptr, frame->nv, nv_offset, @@ -836,30 +730,25 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, } int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - spdylay_zlib *inflater) + spdylay_buffer *inflatebuf) { int r; size_t len_size; - size_t nv_offset; - if(payloadlen < 8) { - return SPDYLAY_ERR_INVALID_FRAME; - } + ssize_t nv_offset; spdylay_frame_unpack_ctrl_hd(&frame->hd, head); len_size = spdylay_frame_get_len_size(frame->hd.version); if(len_size == 0) { return SPDYLAY_ERR_UNSUPPORTED_VERSION; } - nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? 6 : 4; + nv_offset = spdylay_frame_nv_offset(SPDYLAY_SYN_REPLY, frame->hd.version); + assert(nv_offset > 0); + if((ssize_t)(headlen + payloadlen) != nv_offset) { + return SPDYLAY_ERR_INVALID_FRAME; + } frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; - r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, - nvbuf_ptr, nvbuflen_ptr, - payload+nv_offset, payloadlen-nv_offset, - len_size, inflater); + r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); return r; } @@ -941,9 +830,6 @@ int spdylay_frame_unpack_goaway(spdylay_goaway *frame, return 0; } -#define SPDYLAY_SPDY2_HEADERS_NV_OFFSET 14 -#define SPDYLAY_SPDY3_HEADERS_NV_OFFSET 12 - ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, uint8_t **nvbuf_ptr, size_t *nvbuflen_ptr, spdylay_headers *frame, @@ -956,8 +842,8 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, if(len_size == 0) { return SPDYLAY_ERR_UNSUPPORTED_VERSION; } - nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? - SPDYLAY_SPDY2_HEADERS_NV_OFFSET : SPDYLAY_SPDY3_HEADERS_NV_OFFSET; + nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version); + assert(nv_offset > 0); framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, nvbuf_ptr, nvbuflen_ptr, frame->nv, nv_offset, @@ -973,30 +859,25 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, } int spdylay_frame_unpack_headers(spdylay_headers *frame, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - spdylay_zlib *inflater) + spdylay_buffer *inflatebuf) { int r; size_t len_size; - size_t nv_offset; - if(payloadlen < 8) { - return SPDYLAY_ERR_INVALID_FRAME; - } + ssize_t nv_offset; spdylay_frame_unpack_ctrl_hd(&frame->hd, head); len_size = spdylay_frame_get_len_size(frame->hd.version); if(len_size == 0) { return SPDYLAY_ERR_UNSUPPORTED_VERSION; } - nv_offset = frame->hd.version == SPDYLAY_PROTO_SPDY2 ? 6 : 4; + nv_offset = spdylay_frame_nv_offset(SPDYLAY_HEADERS, frame->hd.version); + assert(nv_offset > 0); + if((ssize_t)(headlen + payloadlen) != nv_offset) { + return SPDYLAY_ERR_INVALID_FRAME; + } frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; - r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, - nvbuf_ptr, nvbuflen_ptr, - payload+nv_offset, payloadlen-nv_offset, - len_size, inflater); + r = spdylay_frame_unpack_nv(&frame->nv, inflatebuf, len_size); return r; } @@ -1317,3 +1198,30 @@ void spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv) { qsort(iv, niv, sizeof(spdylay_settings_entry), spdylay_settings_entry_compar); } + +ssize_t spdylay_frame_nv_offset(spdylay_frame_type type, uint16_t version) +{ + switch(type) { + case SPDYLAY_SYN_STREAM: + return SPDYLAY_SYN_STREAM_NV_OFFSET; + case SPDYLAY_SYN_REPLY: { + if(version == SPDYLAY_PROTO_SPDY2) { + return SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET; + } else if(version == SPDYLAY_PROTO_SPDY3) { + return SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET; + } + break; + } + case SPDYLAY_HEADERS: { + if(version == SPDYLAY_PROTO_SPDY2) { + return SPDYLAY_SPDY2_HEADERS_NV_OFFSET; + } else if(version == SPDYLAY_PROTO_SPDY3) { + return SPDYLAY_SPDY3_HEADERS_NV_OFFSET; + } + break; + } + default: + break; + } + return -1; +} diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 2d02f2c..da67011 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -46,8 +46,19 @@ /* The number of bytes of frame header. */ #define SPDYLAY_FRAME_HEAD_LENGTH 8 -#define spdylay_frame_get_nv_len(IN, LEN_SIZE) \ - (LEN_SIZE == 2 ? spdylay_get_uint16(IN) : spdylay_get_uint32(IN)) +/* The offset to the name/value header block in the frame (including + frame header) */ +#define SPDYLAY_SYN_STREAM_NV_OFFSET 18 + +#define SPDYLAY_SPDY2_SYN_REPLY_NV_OFFSET 14 +#define SPDYLAY_SPDY3_SYN_REPLY_NV_OFFSET 12 + +#define SPDYLAY_SPDY2_HEADERS_NV_OFFSET 14 +#define SPDYLAY_SPDY3_HEADERS_NV_OFFSET 12 + +#define spdylay_frame_get_nv_len(RED, LEN_SIZE) \ + (LEN_SIZE == 2 ? spdylay_buffer_reader_uint16(RED) : \ + spdylay_buffer_reader_uint32(RED)) #define spdylay_frame_put_nv_len(OUT, VAL, LEN_SIZE) \ (LEN_SIZE == 2 ? \ @@ -124,15 +135,13 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, spdylay_zlib *deflater); /* - * Unpacks SYN_STREAM frame byte sequence into |frame|. Header is - * given in head and headlen. In spdy/2 spec, headlen is 8 - * bytes. |payload| is the data after length field of the header. + * Unpacks SYN_STREAM frame byte sequence into |frame|. The control + * frame header is given in |head| with |headlen| length. In spdy/3 + * spec, headlen is 8 bytes. |payload| is the data after length field + * of the header and just before name/value header block. * - * |inflatebuf| is used to buffer name/value pairs while inflating - * them using |inflater|. The caller must reset |inflatebuf| before - * the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal - * inflated name/value pairs. This function expands |*nvbuf_ptr| as - * necessary and updates these variables. + * The |inflatebuf| contains inflated name/value header block in wire + * foramt. * * This function also validates the name/value pairs. If unpacking * succeeds but validation fails, it is indicated by returning @@ -147,18 +156,13 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, * The input data are invalid. * SPDYLAY_ERR_UNSUPPORTED_VERSION * The version is not supported. - * SPDYLAY_ERR_ZLIB - * The inflate operation failed. * SPDYLAY_ERR_NOMEM * Out of memory. */ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - spdylay_zlib *inflater); + spdylay_buffer *inflatebuf); /* * Packs SYN_REPLY frame |frame| in wire frame format and store it in @@ -194,11 +198,8 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, /* * Unpacks SYN_REPLY frame byte sequence into |frame|. * - * |inflatebuf| is used to buffer name/value pairs while inflating - * them using |inflater|. The caller must reset |inflatebuf| before - * the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal - * inflated name/value pairs. This function expands |*nvbuf_ptr| as - * necessary and updates these variables. + * The |inflatebuf| contains inflated name/value header block in wire + * foramt. * * This function also validates the name/value pairs. If unpacking * succeeds but validation fails, it is indicated by returning @@ -213,18 +214,13 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, * The version is not supported. * SPDYLAY_ERR_INVALID_FRAME * The input data are invalid. - * SPDYLAY_ERR_ZLIB - * The inflate operation failed. * SPDYLAY_ERR_NOMEM * Out of memory. */ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - spdylay_zlib *inflater); + spdylay_buffer *inflatebuf); /* * Packs PING frame |frame| in wire format and store it in @@ -318,11 +314,8 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, /* * Unpacks HEADERS wire format into |frame|. * - * |inflatebuf| is used to buffer name/value pairs while inflating - * them using |inflater|. The caller must reset |inflatebuf| before - * the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal - * inflated name/value pairs. This function expands |*nvbuf_ptr| as - * necessary and updates these variables. + * The |inflatebuf| contains inflated name/value header block in wire + * foramt. * * This function also validates the name/value pairs. If unpacking * succeeds but validation fails, it is indicated by returning @@ -337,18 +330,13 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, * The version is not supported. * SPDYLAY_ERR_INVALID_FRAME * The input data are invalid. - * SPDYLAY_ERR_ZLIB - * The inflate operation failed. * SPDYLAY_ERR_NOMEM * Out of memory. */ int spdylay_frame_unpack_headers(spdylay_headers *frame, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen, - spdylay_zlib *inflater); + spdylay_buffer *inflatebuf); /* * Packs RST_STREAM frame |frame| in wire frame format and store it in @@ -517,11 +505,11 @@ ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, /* * Counts number of name/value pair in |in| and computes length of * buffers to store unpacked name/value pair and store them in - * |*num_nv_ptr| and |*buf_size_ptr| respectively. |len_size| is the + * |*nvlen_ptr| and |*buflen_ptr| respectively. |len_size| is the * number of bytes in length of name/value pair and it must be 2 or - * 4. We use folloing data structure in |*buf_size_ptr|. First part - * of the data is array of pointer to name/value pair. Supporse the - * buf pointer points to the data region and N is the number of + * 4. We use folloing data structure in |*buflen_ptr| size. First + * part of the data is array of pointer to name/value pair. Supporse + * the buf pointer points to the data region and N is the number of * name/value pair. First (N*2+1)*sizeof(char*) bytes contain array * of pointer to name/value pair and terminating NULL. Each pointer * to name/value pair points to the string in remaining data. For @@ -533,7 +521,7 @@ ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, * data, corresponding index is assigned to name/value pointers. In * this case, the name string is reused. * - * With the above stragety, |*buf_size_ptr| is calculated as + * With the above stragety, |*buflen_ptr| is calculated as * (N*2+1)*sizeof(char*)+sum(strlen(name)+1+strlen(value)+1){for each * name/value pair}. * @@ -543,35 +531,14 @@ ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, * SPDYLAY_ERR_INVALID_FRAME * The input data are invalid. */ -int spdylay_frame_count_unpack_nv_space -(size_t *num_nv_ptr, size_t *buf_size_ptr, const uint8_t *in, size_t inlen, - size_t len_size); +int spdylay_frame_count_unpack_nv_space(size_t *nvlen_ptr, size_t *buflen_ptr, + spdylay_buffer *in, size_t len_size); /* - * Validates name of Name/Value header Block. The |buf| is the - * allocated buffer with the length at least |buflen| bytes. The - * |buflen| must be at least the number of Name/Value pairs in the - * packed name/value header block |in|. The length of |in| is given in - * |inlen|. The |buf| is used as a work memory to validate header - * names and the caller must not use its content on return. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * SPDYLAY_ERR_INVALID_HEADER_BLOCK - * There are duplicate header names; or the header names are not - * encoded in US-ASCII character set and not lower cased; or the - * header name is zero-length string. - */ -int spdylay_frame_unpack_nv_check_name(uint8_t *buf, size_t buflen, - const uint8_t *in, size_t inlen, - size_t len_size); - -/* - * Unpacks name/value pairs in wire format |in| with length |inlen| - * and stores them in |*nv_ptr|. Thif function allocates enough - * memory to store name/value pairs in |*nv_ptr|. |len_size| is the - * number of bytes in length of name/value pair and it must be 2 or 4. + * Unpacks name/value header block in wire format |in| and stores them + * in |*nv_ptr|. Thif function allocates enough memory to store + * name/value pairs in |*nv_ptr|. |len_size| is the number of bytes + * in length of name/value pair and it must be 2 or 4. * * This function also validates the name/value pairs. If unpacking * succeeds but validation fails, it is indicated by returning @@ -586,49 +553,16 @@ int spdylay_frame_unpack_nv_check_name(uint8_t *buf, size_t buflen, * negative error codes: * * SPDYLAY_ERR_INVALID_HEADER_BLOCK - * Unpacking succeeds but the header block is invalid. + * Unpacking succeeds but the header block is invalid. The + * possible reasons are: There are duplicate header names; or the + * header names are not encoded in US-ASCII character set and not + * lower cased; or the header name is zero-length string. * SPDYLAY_ERR_NOMEM * Out of memory. */ -int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen, +int spdylay_frame_unpack_nv(char ***nv_ptr, spdylay_buffer *in, size_t len_size); -/* - * Unpacks name/value pairs from buffer |in| with length |inlen|. The - * necessary memory area required for output is allocated and its - * pointer is assigned to |nv_ptr|. |inflatebuf| is used for inflate - * operation. |*nvbuf_ptr| is used for temporarily stored inflated - * name/value pair in wire format. It is expanded as necessary. - * |len_size| is the number of bytes used in name/value length. It - * must be either 2 or 4. - * - * This function also validates the name/value pairs. If unpacking - * succeeds but validation fails, it is indicated by returning - * SPDYLAY_ERR_INVALID_HEADER_BLOCK. - * - * If error other than SPDYLAY_ERR_INVALID_HEADER_BLOCK is returned, - * the |nv_ptr| is not assigned. In other words, - * SPDYLAY_ERR_INVALID_HEADER_BLOCK means unpacking succeeded, but - * header block validation failed. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * SPDYLAY_ERR_INVALID_HEADER_BLOCK - * Unpacking succeeds but the header block is invalid. - * SPDYLAY_ERR_ZLIB - * The inflate operation failed. - * SPDYLAY_ERR_NOMEM - * Out of memory. - */ -int spdylay_frame_alloc_unpack_nv(char ***nv_ptr, - spdylay_buffer *inflatebuf, - uint8_t **nvbuf_ptr, - size_t *nvbuflen_ptr, - const uint8_t *in, size_t inlen, - size_t len_size, - spdylay_zlib *inflater); - /* * Initializes SYN_STREAM frame |frame| with given values. |frame| * takes ownership of |nv|, so caller must not free it. If stream_id @@ -793,4 +727,12 @@ spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv, */ void spdylay_frame_iv_sort(spdylay_settings_entry *iv, size_t niv); +/* + * Returns the offset of the name/header block in the frame, including + * frame header. If |type| is neither SPDYLAY_SYN_STREAM, + * SPDYLAY_SYN_REPLY nor SPDYLAY_HEADERS, this function returns -1. + * If |version| is unknown, this function returns -1. + */ +ssize_t spdylay_frame_nv_offset(spdylay_frame_type type, uint16_t version); + #endif /* SPDYLAY_FRAME_H */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index c033b65..d34ed93 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -111,8 +111,29 @@ static int spdylay_outbound_item_compar(const void *lhsx, const void *rhsx) static void spdylay_inbound_frame_reset(spdylay_inbound_frame *iframe) { iframe->state = SPDYLAY_RECV_HEAD; - iframe->len = iframe->off = 0; + iframe->payloadlen = iframe->buflen = iframe->off = 0; iframe->headbufoff = 0; + spdylay_buffer_reset(&iframe->inflatebuf); + iframe->error_code = 0; +} + +/* + * Returns the number of bytes before name/value header block for the + * incoming frame. If the incoming frame does not have name/value + * block, this function returns -1. + */ +static size_t spdylay_inbound_frame_payload_nv_offset +(spdylay_inbound_frame *iframe) +{ + uint16_t type, version; + ssize_t offset; + type = spdylay_get_uint16(&iframe->headbuf[2]); + version = spdylay_get_uint16(&iframe->headbuf[0]) & SPDYLAY_VERSION_MASK; + offset = spdylay_frame_nv_offset(type, version); + if(offset != -1) { + offset -= SPDYLAY_HEAD_LEN; + } + return offset; } static int spdylay_session_new(spdylay_session **session_ptr, @@ -183,8 +204,6 @@ static int spdylay_session_new(spdylay_session **session_ptr, } (*session_ptr)->nvbuflen = SPDYLAY_INITIAL_NV_BUFFER_LENGTH; - spdylay_buffer_init(&(*session_ptr)->inflatebuf, 4096); - memset((*session_ptr)->remote_settings, 0, sizeof((*session_ptr)->remote_settings)); (*session_ptr)->remote_settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = @@ -208,6 +227,8 @@ static int spdylay_session_new(spdylay_session **session_ptr, goto fail_iframe_buf; } (*session_ptr)->iframe.bufmax = SPDYLAY_INITIAL_INBOUND_FRAMEBUF_LENGTH; + spdylay_buffer_init(&(*session_ptr)->iframe.inflatebuf, 4096); + spdylay_inbound_frame_reset(&(*session_ptr)->iframe); r = spdylay_client_cert_vector_init(&(*session_ptr)->cli_certvec, @@ -315,7 +336,7 @@ void spdylay_session_del(spdylay_session *session) spdylay_active_outbound_item_reset(&session->aob); free(session->aob.framebuf); free(session->nvbuf); - spdylay_buffer_free(&session->inflatebuf); + spdylay_buffer_free(&session->iframe.inflatebuf); free(session->iframe.buf); spdylay_client_cert_vector_free(&session->cli_certvec); free(session); @@ -2068,7 +2089,7 @@ static void spdylay_session_handle_parse_error(spdylay_session *session, session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len, + session->iframe.buflen, error_code, session->user_data); } @@ -2083,16 +2104,16 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) type = spdylay_get_uint16(&session->iframe.headbuf[2]); switch(type) { case SPDYLAY_SYN_STREAM: - spdylay_buffer_reset(&session->inflatebuf); - r = spdylay_frame_unpack_syn_stream(&frame.syn_stream, - &session->inflatebuf, - &session->nvbuf, - &session->nvbuflen, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.len, - &session->hd_inflater); + if(session->iframe.error_code == 0) { + r = spdylay_frame_unpack_syn_stream(&frame.syn_stream, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen, + &session->iframe.inflatebuf); + } else { + r = session->iframe.error_code; + } if(r == 0) { if(session->version == SPDYLAY_PROTO_SPDY2) { spdylay_frame_nv_2to3(frame.syn_stream.nv); @@ -2113,16 +2134,16 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) } break; case SPDYLAY_SYN_REPLY: - spdylay_buffer_reset(&session->inflatebuf); - r = spdylay_frame_unpack_syn_reply(&frame.syn_reply, - &session->inflatebuf, - &session->nvbuf, - &session->nvbuflen, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.len, - &session->hd_inflater); + if(session->iframe.error_code == 0) { + r = spdylay_frame_unpack_syn_reply(&frame.syn_reply, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen, + &session->iframe.inflatebuf); + } else { + r = session->iframe.error_code; + } if(r == 0) { if(session->version == SPDYLAY_PROTO_SPDY2) { spdylay_frame_nv_2to3(frame.syn_reply.nv); @@ -2144,7 +2165,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len); + session->iframe.buflen); if(r == 0) { r = spdylay_session_on_rst_stream_received(session, &frame); spdylay_frame_rst_stream_free(&frame.rst_stream); @@ -2158,7 +2179,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len); + session->iframe.buflen); if(r == 0) { r = spdylay_session_on_settings_received(session, &frame); spdylay_frame_settings_free(&frame.settings); @@ -2174,7 +2195,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len); + session->iframe.buflen); if(r == 0) { r = spdylay_session_on_ping_received(session, &frame); spdylay_frame_ping_free(&frame.ping); @@ -2188,7 +2209,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len); + session->iframe.buflen); if(r == 0) { r = spdylay_session_on_goaway_received(session, &frame); spdylay_frame_goaway_free(&frame.goaway); @@ -2198,16 +2219,16 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) } break; case SPDYLAY_HEADERS: - spdylay_buffer_reset(&session->inflatebuf); - r = spdylay_frame_unpack_headers(&frame.headers, - &session->inflatebuf, - &session->nvbuf, - &session->nvbuflen, - session->iframe.headbuf, - sizeof(session->iframe.headbuf), - session->iframe.buf, - session->iframe.len, - &session->hd_inflater); + if(session->iframe.error_code == 0) { + r = spdylay_frame_unpack_headers(&frame.headers, + session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.buflen, + &session->iframe.inflatebuf); + } else { + r = session->iframe.error_code; + } if(r == 0) { if(session->version == SPDYLAY_PROTO_SPDY2) { spdylay_frame_nv_2to3(frame.headers.nv); @@ -2229,7 +2250,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len); + session->iframe.buflen); if(r == 0) { r = spdylay_session_on_window_update_received(session, &frame); spdylay_frame_window_update_free(&frame.window_update); @@ -2243,7 +2264,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len); + session->iframe.buflen); if(r == 0) { r = spdylay_session_on_credential_received(session, &frame); spdylay_frame_credential_free(&frame.credential); @@ -2260,7 +2281,7 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) session->iframe.headbuf, sizeof(session->iframe.headbuf), session->iframe.buf, - session->iframe.len, + session->iframe.buflen, session->user_data); } } @@ -2414,39 +2435,85 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session, session->iframe.headbufoff += readlen; if(session->iframe.headbufoff == SPDYLAY_HEAD_LEN) { session->iframe.state = SPDYLAY_RECV_PAYLOAD; - payloadlen = spdylay_get_uint32(&session->iframe.headbuf[4]) & + session->iframe.payloadlen = + spdylay_get_uint32(&session->iframe.headbuf[4]) & SPDYLAY_LENGTH_MASK; if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { /* control frame */ - session->iframe.len = payloadlen; + ssize_t buflen; + buflen = spdylay_inbound_frame_payload_nv_offset(&session->iframe); + if(buflen == -1) { + /* TODO Check if payloadlen is small enough for buffering */ + buflen = session->iframe.payloadlen; + } else if(buflen < (ssize_t)session->iframe.payloadlen) { + session->iframe.state = SPDYLAY_RECV_PAYLOAD_PRE_NV; + } + /* buflen >= session->iframe.payloadlen means frame is + malformed. In this case, we just buffer these bytes and + handle error later. */ + session->iframe.buflen = buflen; + + /* TODO On error case, go into SPDYLAY_RECV_PAYLOAD_IGN state and + discard any input bytes. */ r = spdylay_reserve_buffer(&session->iframe.buf, &session->iframe.bufmax, - session->iframe.len); + buflen); if(r != 0) { /* FATAL */ assert(r < SPDYLAY_ERR_FATAL); return r; } - session->iframe.off = 0; - } else { - session->iframe.len = payloadlen; - session->iframe.off = 0; } } else { break; } } - if(session->iframe.state == SPDYLAY_RECV_PAYLOAD) { - size_t rempayloadlen = session->iframe.len - session->iframe.off; + if(session->iframe.state == SPDYLAY_RECV_PAYLOAD || + session->iframe.state == SPDYLAY_RECV_PAYLOAD_PRE_NV || + session->iframe.state == SPDYLAY_RECV_PAYLOAD_NV) { + size_t rempayloadlen; size_t bufavail, readlen; int32_t data_stream_id = 0; uint8_t data_flags = SPDYLAY_DATA_FLAG_NONE; - bufavail = inlimit-inmark; + + rempayloadlen = session->iframe.payloadlen - session->iframe.off; + bufavail = inlimit - inmark; if(rempayloadlen > 0 && bufavail == 0) { break; } readlen = spdylay_min(bufavail, rempayloadlen); - if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + if(session->iframe.state == SPDYLAY_RECV_PAYLOAD_PRE_NV) { + size_t pnvlen, rpnvlen, readpnvlen; + pnvlen = spdylay_inbound_frame_payload_nv_offset(&session->iframe); + rpnvlen = pnvlen - session->iframe.off; + readpnvlen = spdylay_min(rpnvlen, readlen); + + memcpy(session->iframe.buf+session->iframe.off, inmark, readpnvlen); + readlen -= readpnvlen; + session->iframe.off += readpnvlen; + inmark += readpnvlen; + + if(session->iframe.off == pnvlen) { + session->iframe.state = SPDYLAY_RECV_PAYLOAD_NV; + } + } + if(session->iframe.state == SPDYLAY_RECV_PAYLOAD_NV) { + /* For frame with name/value header block, the compressed + portion of the block is incrementally decompressed. The + result is stored in inflatebuf. */ + if(session->iframe.error_code == 0) { + ssize_t decomplen; + decomplen = spdylay_zlib_inflate_hd(&session->hd_inflater, + &session->iframe.inflatebuf, + inmark, readlen); + /* TODO If total length in inflatebuf exceeds certain limit, + set TOO_LARGE_FRAME to error_code and issue RST_STREAM + later. */ + if(decomplen < 0) { + session->iframe.error_code = decomplen; + } + } + } else if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { memcpy(session->iframe.buf+session->iframe.off, inmark, readlen); } else { /* For data frame, We don't buffer data. Instead, just pass @@ -2469,7 +2536,7 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session, if(session->flow_control && !spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { if(readlen > 0 && - (session->iframe.len != session->iframe.off || + (session->iframe.payloadlen != session->iframe.off || (data_flags & SPDYLAY_DATA_FLAG_FIN) == 0)) { r = spdylay_session_update_recv_window_size(session, data_stream_id, @@ -2481,7 +2548,7 @@ ssize_t spdylay_session_mem_recv(spdylay_session *session, } } } - if(session->iframe.len == session->iframe.off) { + if(session->iframe.payloadlen == session->iframe.off) { if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { r = spdylay_session_process_ctrl_frame(session); } else { diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index ef72041..825a309 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -87,9 +87,18 @@ typedef struct { /* Maxmum size of client certificate vector */ #define SPDYLAY_MAX_CLIENT_CERT_VECTOR_LENGTH 255 +/* Internal state when receiving incoming frame */ typedef enum { + /* Receiving frame header */ SPDYLAY_RECV_HEAD, - SPDYLAY_RECV_PAYLOAD + /* Receiving frame payload (comes after length field) */ + SPDYLAY_RECV_PAYLOAD, + /* Receiving frame payload that comes before name/value header + block. Applied only for SYN_STREAM, SYN_REPLY and HEADERS. */ + SPDYLAY_RECV_PAYLOAD_PRE_NV, + /* Receiving name/value header block in frame payload. Applied only + for SYN_STREAM, SYN_REPLY and HEADERS. */ + SPDYLAY_RECV_PAYLOAD_NV } spdylay_inbound_state; #define SPDYLAY_HEAD_LEN 8 @@ -107,10 +116,22 @@ typedef struct { uint8_t *buf; /* Capacity of buf */ size_t bufmax; + /* For frames without name/value header block, this is how many + bytes are going to filled in buf. For frames with the block, buf + only contains bytes that come before ther block, but this value + includes the length of the block. buflen <= bufmax must be + fulfilled. */ + size_t buflen; /* length in Length field */ - size_t len; - /* How many bytes are filled in buf */ + size_t payloadlen; + /* How many bytes are received for this frame. off <= payloadlen + must be fulfilled. */ size_t off; + /* Buffer used to store name/value pairs while inflating them using + zlib on unpack */ + spdylay_buffer inflatebuf; + /* Error code */ + int error_code; } spdylay_inbound_frame; typedef enum { @@ -161,9 +182,6 @@ struct spdylay_session { uint8_t *nvbuf; /* The number of bytes allocated for nvbuf */ size_t nvbuflen; - /* Buffer used to store name/value pairs while inflating them using - zlib on unpack */ - spdylay_buffer inflatebuf; spdylay_zlib hd_deflater; spdylay_zlib hd_inflater; diff --git a/tests/Makefile.am b/tests/Makefile.am index 050c271..f9b775e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -29,12 +29,14 @@ check_PROGRAMS = main failmalloc OBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \ spdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \ spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c \ - spdylay_client_cert_vector_test.c spdylay_gzip_test.c + spdylay_client_cert_vector_test.c spdylay_gzip_test.c \ + spdylay_test_helper.c HFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \ spdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h \ spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h \ - spdylay_client_cert_vector_test.h spdylay_gzip_test.h + spdylay_client_cert_vector_test.h spdylay_gzip_test.h \ + spdylay_test_helper.h main_SOURCES = $(HFILES) $(OBJECTS) @@ -42,7 +44,8 @@ main_LDADD = ${top_builddir}/lib/libspdylay.la main_LDFLAGS = -static @CUNIT_LIBS@ failmalloc_SOURCES = failmalloc.c failmalloc_test.c failmalloc_test.h \ - malloc_wrapper.c malloc_wrapper.h + malloc_wrapper.c malloc_wrapper.h \ + spdylay_test_helper.c spdylay_test_helper.h failmalloc_LDADD = $(main_LDADD) -ldl failmalloc_LDFLAGS = $(main_LDFLAGS) diff --git a/tests/failmalloc_test.c b/tests/failmalloc_test.c index 74f53fa..f52be11 100644 --- a/tests/failmalloc_test.c +++ b/tests/failmalloc_test.c @@ -34,6 +34,7 @@ #include "spdylay_frame.h" #include "spdylay_helper.h" #include "malloc_wrapper.h" +#include "spdylay_test_helper.h" static char* strcopy(const char* s) { @@ -431,13 +432,8 @@ static void run_spdylay_frame_pack_syn_stream(void) if(framelen < 0) { goto fail; } - rv = spdylay_frame_unpack_syn_stream(&oframe.syn_stream, - &inflatebuf, - &nvbuf, &nvbuflen, - &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, - &buf[SPDYLAY_FRAME_HEAD_LENGTH], - framelen-SPDYLAY_FRAME_HEAD_LENGTH, - &inflater); + rv = unpack_frame_with_nv_block(SPDYLAY_SYN_STREAM, SPDYLAY_PROTO_SPDY3, + &oframe, &inflater, buf, framelen); if(rv != 0) { goto fail; } diff --git a/tests/main.c b/tests/main.c index 793f37b..b65815c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -69,6 +69,7 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "map", test_spdylay_map) || !CU_add_test(pSuite, "queue", test_spdylay_queue) || !CU_add_test(pSuite, "buffer", test_spdylay_buffer) || + !CU_add_test(pSuite, "buffer_reader", test_spdylay_buffer_reader) || !CU_add_test(pSuite, "zlib_spdy2", test_spdylay_zlib_spdy2) || !CU_add_test(pSuite, "zlib_spdy3", test_spdylay_zlib_spdy3) || !CU_add_test(pSuite, "npn", test_spdylay_npn) || diff --git a/tests/spdylay_buffer_test.c b/tests/spdylay_buffer_test.c index dc3c616..7906846 100644 --- a/tests/spdylay_buffer_test.c +++ b/tests/spdylay_buffer_test.c @@ -29,6 +29,7 @@ #include #include "spdylay_buffer.h" +#include "spdylay_net.h" void test_spdylay_buffer(void) { @@ -79,3 +80,45 @@ void test_spdylay_buffer(void) spdylay_buffer_free(&buffer); } + +void test_spdylay_buffer_reader(void) +{ + spdylay_buffer buffer; + spdylay_buffer_reader reader; + uint16_t val16; + uint32_t val32; + uint8_t temp[256]; + + spdylay_buffer_init(&buffer, 3); + spdylay_buffer_write(&buffer, (const uint8_t*)"hello", 5); + val16 = htons(678); + spdylay_buffer_write(&buffer, (const uint8_t*)&val16, sizeof(uint16_t)); + val32 = htonl(1000000007); + spdylay_buffer_write(&buffer, (const uint8_t*)&val32, sizeof(uint32_t)); + spdylay_buffer_write(&buffer, (const uint8_t*)"world", 5); + + CU_ASSERT(5+2+4+5 == spdylay_buffer_length(&buffer)); + + spdylay_buffer_reader_init(&reader, &buffer); + + spdylay_buffer_reader_data(&reader, temp, 5); + CU_ASSERT(memcmp(temp, "hello", 5) == 0); + CU_ASSERT(678 == spdylay_buffer_reader_uint16(&reader)); + CU_ASSERT(1000000007 == spdylay_buffer_reader_uint32(&reader)); + CU_ASSERT('w' == spdylay_buffer_reader_uint8(&reader)); + CU_ASSERT('o' == spdylay_buffer_reader_uint8(&reader)); + CU_ASSERT('r' == spdylay_buffer_reader_uint8(&reader)); + CU_ASSERT('l' == spdylay_buffer_reader_uint8(&reader)); + CU_ASSERT('d' == spdylay_buffer_reader_uint8(&reader)); + + spdylay_buffer_reader_init(&reader, &buffer); + spdylay_buffer_reader_advance(&reader, 5); + CU_ASSERT(678 == spdylay_buffer_reader_uint16(&reader)); + spdylay_buffer_reader_advance(&reader, 1); + spdylay_buffer_reader_advance(&reader, 1); + spdylay_buffer_reader_advance(&reader, 1); + spdylay_buffer_reader_advance(&reader, 1); + CU_ASSERT('w' == spdylay_buffer_reader_uint8(&reader)); + + spdylay_buffer_free(&buffer); +} diff --git a/tests/spdylay_buffer_test.h b/tests/spdylay_buffer_test.h index 312b404..6a52041 100644 --- a/tests/spdylay_buffer_test.h +++ b/tests/spdylay_buffer_test.h @@ -26,5 +26,6 @@ #define SPDYLAY_BUFFER_TEST_H void test_spdylay_buffer(void); +void test_spdylay_buffer_reader(void); #endif /* SPDYLAY_BUFFER_TEST_H */ diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index 5c85244..da83297 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -28,6 +28,7 @@ #include "spdylay_frame.h" #include "spdylay_helper.h" +#include "spdylay_test_helper.h" static const char *headers[] = { "method", "GET", @@ -36,7 +37,7 @@ static const char *headers[] = { "x-head", "foo", "x-head", "bar", "version", "HTTP/1.1", - "empty", "", + "x-empty", "", NULL }; @@ -45,30 +46,40 @@ static void test_spdylay_frame_unpack_nv_with(size_t len_size) uint8_t out[1024]; char **nv; size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size); - CU_ASSERT(0 == spdylay_frame_unpack_nv(&nv, out, inlen, len_size)); + spdylay_buffer buffer; + + spdylay_buffer_init(&buffer, 4096); + spdylay_buffer_write(&buffer, out, inlen); + + CU_ASSERT(0 == spdylay_frame_unpack_nv(&nv, &buffer, len_size)); CU_ASSERT(strcmp("method", nv[0]) == 0); CU_ASSERT(strcmp("GET", nv[1]) == 0); CU_ASSERT(strcmp("scheme", nv[2]) == 0); CU_ASSERT(strcmp("https", nv[3]) == 0); CU_ASSERT(strcmp("url", nv[4]) == 0); CU_ASSERT(strcmp("/", nv[5]) == 0); - CU_ASSERT(strcmp("x-head", nv[6]) == 0); - CU_ASSERT(strcmp("foo", nv[7]) == 0); - CU_ASSERT(strcmp("x-head", nv[8]) == 0); - CU_ASSERT(strcmp("bar", nv[9]) == 0); - CU_ASSERT(strcmp("version", nv[10]) == 0); - CU_ASSERT(strcmp("HTTP/1.1", nv[11]) == 0); - CU_ASSERT(strcmp("empty", nv[12]) == 0); - CU_ASSERT(strcmp("", nv[13]) == 0); + CU_ASSERT(strcmp("version", nv[6]) == 0); + CU_ASSERT(strcmp("HTTP/1.1", nv[7]) == 0); + CU_ASSERT(strcmp("x-empty", nv[8]) == 0); + CU_ASSERT(strcmp("", nv[9]) == 0); + CU_ASSERT(strcmp("x-head", nv[10]) == 0); + CU_ASSERT(strcmp("foo", nv[11]) == 0); + CU_ASSERT(strcmp("x-head", nv[12]) == 0); + CU_ASSERT(strcmp("bar", nv[13]) == 0); spdylay_frame_nv_del(nv); /* Create in-sequence NUL bytes */ - memcpy(&out[len_size+len_size+strlen(headers[0])+len_size+ - strlen(headers[1])-2], + /* Assuming first chunk has enough space to store 1st name/value + pair. */ + memcpy(&buffer.root.next->data[len_size + + len_size + strlen(headers[0]) + + len_size + strlen(headers[1])-2], "\0\0", 2); CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK == - spdylay_frame_unpack_nv(&nv, out, inlen, len_size)); + spdylay_frame_unpack_nv(&nv, &buffer, len_size)); + spdylay_frame_nv_del(nv); + spdylay_buffer_free(&buffer); } void test_spdylay_frame_unpack_nv_spdy2(void) @@ -173,9 +184,9 @@ void test_spdylay_frame_pack_nv_duplicate_keys(void) void test_spdylay_frame_count_nv_space(void) { size_t len_size = 2; - CU_ASSERT(83 == spdylay_frame_count_nv_space((char**)headers, len_size)); + CU_ASSERT(85 == spdylay_frame_count_nv_space((char**)headers, len_size)); len_size = 4; - CU_ASSERT(109 == spdylay_frame_count_nv_space((char**)headers, len_size)); + CU_ASSERT(111 == spdylay_frame_count_nv_space((char**)headers, len_size)); } void test_spdylay_frame_count_unpack_nv_space(void) @@ -186,35 +197,45 @@ void test_spdylay_frame_count_unpack_nv_space(void) size_t inlen = spdylay_frame_pack_nv(out, (char**)headers, len_size); uint16_t temp; size_t expected_buflen; + spdylay_buffer buffer; + uint8_t *chunk; + + spdylay_buffer_init(&buffer, 4096); + spdylay_buffer_write(&buffer, out, inlen); + CU_ASSERT(0 == spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, - out, inlen, len_size)); + &buffer, len_size)); CU_ASSERT(7 == nvlen); - expected_buflen = 69+(nvlen*2+1)*sizeof(char*); + expected_buflen = 71+(nvlen*2+1)*sizeof(char*); CU_ASSERT(expected_buflen == buflen); - /* Trailing garbage */ - CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == - spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, - out, inlen+2, len_size)); - + chunk = buffer.root.next->data; /* Change number of nv pair to a bogus value */ - temp = spdylay_get_uint16(out); - spdylay_put_uint16be(out, temp+1); + temp = spdylay_get_uint16(chunk); + spdylay_put_uint16be(chunk, temp+1); CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == - spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, out, inlen, + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, &buffer, len_size)); - spdylay_put_uint16be(out, temp); + spdylay_put_uint16be(chunk, temp); /* Change the length of name to a bogus value */ - temp = spdylay_get_uint16(out+2); - spdylay_put_uint16be(out+2, temp+1); + temp = spdylay_get_uint16(chunk+2); + spdylay_put_uint16be(chunk+2, temp+1); CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == - spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, out, inlen, + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, &buffer, len_size)); - spdylay_put_uint16be(out+2, 65535); + spdylay_put_uint16be(chunk+2, 65535); CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == - spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, out, inlen, + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, &buffer, len_size)); + + /* Trailing garbage */ + spdylay_buffer_advance(&buffer, 2); + CU_ASSERT(SPDYLAY_ERR_INVALID_FRAME == + spdylay_frame_count_unpack_nv_space(&nvlen, &buflen, + &buffer, len_size)); + /* We advanced buffer 2 bytes, so it is not valid any more. */ + spdylay_buffer_free(&buffer); } void test_spdylay_frame_pack_ping(void) @@ -282,9 +303,8 @@ static void test_spdylay_frame_pack_syn_stream_version(uint16_t version) spdylay_frame frame, oframe; uint8_t *buf = NULL, *nvbuf = NULL; size_t buflen = 0, nvbuflen = 0; - spdylay_buffer inflatebuf; ssize_t framelen; - spdylay_buffer_init(&inflatebuf, 4096); + spdylay_zlib_deflate_hd_init(&deflater, version); spdylay_zlib_inflate_hd_init(&inflater, version); spdylay_frame_syn_stream_init(&frame.syn_stream, version, @@ -293,14 +313,12 @@ static void test_spdylay_frame_pack_syn_stream_version(uint16_t version) framelen = spdylay_frame_pack_syn_stream(&buf, &buflen, &nvbuf, &nvbuflen, &frame.syn_stream, &deflater); - CU_ASSERT(0 == spdylay_frame_unpack_syn_stream - (&oframe.syn_stream, - &inflatebuf, - &nvbuf, &nvbuflen, - &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, - &buf[SPDYLAY_FRAME_HEAD_LENGTH], - framelen-SPDYLAY_FRAME_HEAD_LENGTH, - &inflater)); + + CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_SYN_STREAM, + version, + &oframe, + &inflater, + buf, framelen)); CU_ASSERT(65536 == oframe.syn_stream.stream_id); CU_ASSERT(1000000007 == oframe.syn_stream.assoc_stream_id); CU_ASSERT(version == oframe.syn_stream.hd.version); @@ -316,7 +334,6 @@ static void test_spdylay_frame_pack_syn_stream_version(uint16_t version) spdylay_frame_syn_stream_free(&frame.syn_stream); spdylay_zlib_inflate_free(&inflater); spdylay_zlib_deflate_free(&deflater); - spdylay_buffer_free(&inflatebuf); } void test_spdylay_frame_pack_syn_stream_spdy2(void) @@ -335,9 +352,7 @@ static void test_spdylay_frame_pack_syn_reply_version(uint16_t version) spdylay_frame frame, oframe; uint8_t *buf = NULL, *nvbuf = NULL; size_t buflen = 0, nvbuflen = 0; - spdylay_buffer inflatebuf; ssize_t framelen; - spdylay_buffer_init(&inflatebuf, 4096); spdylay_zlib_deflate_hd_init(&deflater, version); spdylay_zlib_inflate_hd_init(&inflater, version); spdylay_frame_syn_reply_init(&frame.syn_reply, version, @@ -346,14 +361,11 @@ static void test_spdylay_frame_pack_syn_reply_version(uint16_t version) framelen = spdylay_frame_pack_syn_reply(&buf, &buflen, &nvbuf, &nvbuflen, &frame.syn_reply, &deflater); - CU_ASSERT(0 == spdylay_frame_unpack_syn_reply - (&oframe.syn_reply, - &inflatebuf, - &nvbuf, &nvbuflen, - &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, - &buf[SPDYLAY_FRAME_HEAD_LENGTH], - framelen-SPDYLAY_FRAME_HEAD_LENGTH, - &inflater)); + CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_SYN_REPLY, + version, + &oframe, + &inflater, + buf, framelen)); CU_ASSERT(3 == oframe.syn_reply.stream_id); CU_ASSERT(version == oframe.syn_reply.hd.version); CU_ASSERT(SPDYLAY_SYN_REPLY == oframe.syn_reply.hd.type); @@ -368,7 +380,6 @@ static void test_spdylay_frame_pack_syn_reply_version(uint16_t version) spdylay_frame_syn_reply_free(&frame.syn_reply); spdylay_zlib_inflate_free(&inflater); spdylay_zlib_deflate_free(&deflater); - spdylay_buffer_free(&inflatebuf); } void test_spdylay_frame_pack_syn_reply_spdy2(void) @@ -398,14 +409,11 @@ static void test_spdylay_frame_pack_headers_version(uint16_t version) framelen = spdylay_frame_pack_headers(&buf, &buflen, &nvbuf, &nvbuflen, &frame.headers, &deflater); - CU_ASSERT(0 == spdylay_frame_unpack_headers - (&oframe.headers, - &inflatebuf, - &nvbuf, &nvbuflen, - &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, - &buf[SPDYLAY_FRAME_HEAD_LENGTH], - framelen-SPDYLAY_FRAME_HEAD_LENGTH, - &inflater)); + CU_ASSERT(0 == unpack_frame_with_nv_block(SPDYLAY_HEADERS, + version, + &oframe, + &inflater, + buf, framelen)); CU_ASSERT(3 == oframe.headers.stream_id); CU_ASSERT(version == oframe.headers.hd.version); CU_ASSERT(SPDYLAY_HEADERS == oframe.headers.hd.type); @@ -699,25 +707,40 @@ static const char *non_ascii_headers[] = { static void test_spdylay_frame_unpack_nv_check_name_with(size_t len_size) { - uint8_t nvbuf[1024], buf[1024]; + uint8_t nvbuf[1024]; size_t nvbuflen; + spdylay_buffer buffer; + char **nv; + + spdylay_buffer_init(&buffer, 32); nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), headers, len_size); + spdylay_buffer_write(&buffer, nvbuf, nvbuflen); + CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK == - spdylay_frame_unpack_nv_check_name(buf, sizeof(buf), - nvbuf, nvbuflen, len_size)); + spdylay_frame_unpack_nv(&nv, &buffer, len_size)); + + spdylay_frame_nv_del(nv); + spdylay_buffer_reset(&buffer); nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), empty_name_headers, len_size); + spdylay_buffer_write(&buffer, nvbuf, nvbuflen); + CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK == - spdylay_frame_unpack_nv_check_name(buf, sizeof(buf), - nvbuf, nvbuflen, len_size)); + spdylay_frame_unpack_nv(&nv, &buffer, len_size)); + + spdylay_frame_nv_del(nv); + spdylay_buffer_reset(&buffer); nvbuflen = spdylay_pack_nv(nvbuf, sizeof(nvbuf), non_ascii_headers, len_size); + spdylay_buffer_write(&buffer, nvbuf, nvbuflen); CU_ASSERT(SPDYLAY_ERR_INVALID_HEADER_BLOCK == - spdylay_frame_unpack_nv_check_name(buf, sizeof(buf), - nvbuf, nvbuflen, len_size)); + spdylay_frame_unpack_nv(&nv, &buffer, len_size)); + + spdylay_frame_nv_del(nv); + spdylay_buffer_free(&buffer); } void test_spdylay_frame_unpack_nv_check_name_spdy2(void) diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index 5b7588b..3264068 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -32,6 +32,8 @@ #include "spdylay_session.h" #include "spdylay_stream.h" #include "spdylay_net.h" +#include "spdylay_helper.h" +#include "spdylay_test_helper.h" #define OB_CTRL(ITEM) spdylay_outbound_item_get_ctrl_frame(ITEM) #define OB_CTRL_TYPE(ITEM) spdylay_outbound_item_get_ctrl_frame_type(ITEM) @@ -290,6 +292,26 @@ void test_spdylay_session_recv(void) item = spdylay_session_get_next_ob_item(session); CU_ASSERT(SPDYLAY_RST_STREAM == OB_CTRL_TYPE(item)); CU_ASSERT(SPDYLAY_PROTOCOL_ERROR == OB_CTRL(item)->rst_stream.status_code); + CU_ASSERT(0 == spdylay_session_send(session)); + + /* Received SYN_STREAM without name/value header block */ + spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_PROTO_SPDY2, + SPDYLAY_CTRL_FLAG_NONE, + 5, 0, 3, dup_nv(upcase_nv)); + framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_stream, + &session->hd_deflater); + spdylay_frame_syn_stream_free(&frame.syn_stream); + /* Use bytes that come before name/value header block */ + spdylay_put_uint32be(&framedata[4], + SPDYLAY_SYN_STREAM_NV_OFFSET - SPDYLAY_HEAD_LEN); + scripted_data_feed_init(&df, framedata, SPDYLAY_SYN_STREAM_NV_OFFSET); + user_data.ctrl_recv_cb_called = 0; + CU_ASSERT(0 == spdylay_session_recv(session)); + CU_ASSERT(0 == user_data.ctrl_recv_cb_called); + item = spdylay_session_get_next_ob_item(session); + CU_ASSERT(SPDYLAY_GOAWAY == OB_CTRL_TYPE(item)); spdylay_session_del(session); @@ -730,14 +752,11 @@ void test_spdylay_submit_response_with_null_data_read_callback(void) CU_ASSERT(OB_CTRL(item)->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 == unpack_frame_with_nv_block(SPDYLAY_SYN_REPLY, + SPDYLAY_PROTO_SPDY2, + &frame, + &session->hd_inflater, + acc.buf, acc.length)); CU_ASSERT(0 == strcmp("version", frame.syn_reply.nv[0])); spdylay_frame_syn_reply_free(&frame.syn_reply); @@ -792,14 +811,11 @@ void test_spdylay_submit_request_with_null_data_read_callback(void) CU_ASSERT(OB_CTRL(item)->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 == unpack_frame_with_nv_block(SPDYLAY_SYN_STREAM, + SPDYLAY_PROTO_SPDY2, + &frame, + &session->hd_inflater, + acc.buf, acc.length)); CU_ASSERT(0 == strcmp("version", frame.syn_stream.nv[0])); spdylay_frame_syn_stream_free(&frame.syn_stream); @@ -922,14 +938,11 @@ void test_spdylay_submit_headers(void) 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 == unpack_frame_with_nv_block(SPDYLAY_HEADERS, + SPDYLAY_PROTO_SPDY2, + &frame, + &session->hd_inflater, + acc.buf, acc.length)); CU_ASSERT(0 == strcmp("version", frame.headers.nv[0])); spdylay_frame_headers_free(&frame.headers); @@ -2349,6 +2362,7 @@ void test_spdylay_submit_window_update(void) CU_ASSERT(0 == stream->recv_window_size); CU_ASSERT(0 == spdylay_submit_window_update(session, stream_id, 4096)); + item = spdylay_session_get_next_ob_item(session); CU_ASSERT(SPDYLAY_WINDOW_UPDATE == OB_CTRL_TYPE(item)); CU_ASSERT(4096 == OB_CTRL(item)->window_update.delta_window_size); CU_ASSERT(0 == spdylay_session_send(session)); diff --git a/tests/spdylay_test_helper.c b/tests/spdylay_test_helper.c new file mode 100644 index 0000000..626c527 --- /dev/null +++ b/tests/spdylay_test_helper.c @@ -0,0 +1,75 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "spdylay_test_helper.h" + +#include + +#include + +#include "spdylay_session.h" + +ssize_t unpack_frame_with_nv_block(spdylay_frame_type type, + uint16_t version, + spdylay_frame *frame, + spdylay_zlib *inflater, + const uint8_t *in, size_t len) +{ + spdylay_buffer buffer; + ssize_t rv; + ssize_t pnvlen; + pnvlen = spdylay_frame_nv_offset(type, version) - SPDYLAY_HEAD_LEN; + assert(pnvlen > 0); + + spdylay_buffer_init(&buffer, 4096); + rv = spdylay_zlib_inflate_hd(inflater, &buffer, + &in[SPDYLAY_HEAD_LEN + pnvlen], + len - SPDYLAY_HEAD_LEN - pnvlen); + CU_ASSERT(rv >= 0); + switch(type) { + case SPDYLAY_SYN_STREAM: + rv = spdylay_frame_unpack_syn_stream(&frame->syn_stream, + &in[0], SPDYLAY_HEAD_LEN, + &in[SPDYLAY_HEAD_LEN], pnvlen, + &buffer); + break; + case SPDYLAY_SYN_REPLY: + rv = spdylay_frame_unpack_syn_reply(&frame->syn_reply, + &in[0], SPDYLAY_HEAD_LEN, + &in[SPDYLAY_HEAD_LEN], pnvlen, + &buffer); + break; + case SPDYLAY_HEADERS: + rv = spdylay_frame_unpack_headers(&frame->headers, + &in[0], SPDYLAY_HEAD_LEN, + &in[SPDYLAY_HEAD_LEN], pnvlen, + &buffer); + break; + default: + /* Must not be reachable */ + assert(0); + } + spdylay_buffer_free(&buffer); + return rv; +} diff --git a/tests/spdylay_test_helper.h b/tests/spdylay_test_helper.h new file mode 100644 index 0000000..9735ed1 --- /dev/null +++ b/tests/spdylay_test_helper.h @@ -0,0 +1,41 @@ +/* + * Spdylay - SPDY Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef SPDYLAY_TEST_HELPER_H +#define SPDYLAY_TEST_HELPER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include "spdylay_frame.h" +#include "spdylay_zlib.h" + +ssize_t unpack_frame_with_nv_block(spdylay_frame_type type, + uint16_t version, + spdylay_frame *frame, + spdylay_zlib *inflater, + const uint8_t *in, size_t len); + +#endif /* SPDYLAY_TEST_HELPER_H */