diff --git a/lib/spdylay_buffer.c b/lib/spdylay_buffer.c index 6743d8bc..eb5dbf68 100644 --- a/lib/spdylay_buffer.c +++ b/lib/spdylay_buffer.c @@ -29,7 +29,9 @@ void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity) { - spdylay_queue_init(&buffer->q); + buffer->root.data = NULL; + buffer->root.next = NULL; + buffer->current = &buffer->root; buffer->capacity = chunk_capacity; buffer->len = 0; /* @@ -41,22 +43,35 @@ void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity) void spdylay_buffer_free(spdylay_buffer *buffer) { - while(!spdylay_queue_empty(&buffer->q)) { - free(spdylay_queue_front(&buffer->q)); - spdylay_queue_pop(&buffer->q); + spdylay_buffer_chunk *p = buffer->root.next; + while(p) { + spdylay_buffer_chunk *next = p->next; + free(p->data); + free(p); + p = next; } } int spdylay_buffer_alloc(spdylay_buffer *buffer) { - int r; - uint8_t *buf = (uint8_t*)malloc(buffer->capacity); - if(buf == NULL) { - return SPDYLAY_ERR_NOMEM; - } - if((r = spdylay_queue_push(&buffer->q, buf)) != 0) { - free(buf); - return r; + if(buffer->current->next == NULL) { + spdylay_buffer_chunk *chunk; + uint8_t *buf; + chunk = malloc(sizeof(spdylay_buffer_chunk)); + if(chunk == NULL) { + return SPDYLAY_ERR_NOMEM; + } + buf = malloc(buffer->capacity); + if(buf == NULL) { + free(chunk); + return SPDYLAY_ERR_NOMEM; + } + chunk->data = buf; + chunk->next = NULL; + buffer->current->next = chunk; + buffer->current = chunk; + } else { + buffer->current = buffer->current->next; } buffer->len += buffer->capacity-buffer->last_offset; buffer->last_offset = 0; @@ -65,10 +80,10 @@ int spdylay_buffer_alloc(spdylay_buffer *buffer) uint8_t* spdylay_buffer_get(spdylay_buffer *buffer) { - if(spdylay_queue_empty(&buffer->q)) { + if(buffer->current->data == NULL) { return NULL; } else { - return spdylay_queue_back(&buffer->q)+buffer->last_offset; + return buffer->current->data+buffer->last_offset; } } @@ -89,40 +104,6 @@ size_t spdylay_buffer_length(spdylay_buffer *buffer) return buffer->len; } -size_t spdylay_buffer_front_length(spdylay_buffer *buffer) -{ - if(spdylay_queue_empty(&buffer->q)) { - return 0; - } else if(buffer->len >= buffer->capacity) { - return buffer->capacity; - } else { - return buffer->len; - } -} - -uint8_t* spdylay_buffer_front_data(spdylay_buffer *buffer) -{ - if(spdylay_queue_empty(&buffer->q)) { - return NULL; - } else { - return spdylay_queue_front(&buffer->q); - } -} - -void spdylay_buffer_pop(spdylay_buffer *buffer) -{ - if(!spdylay_queue_empty(&buffer->q)) { - if(buffer->len >= buffer->capacity) { - buffer->len -= buffer->capacity; - } else { - buffer->len = 0; - buffer->last_offset = buffer->capacity; - } - free(spdylay_queue_front(&buffer->q)); - spdylay_queue_pop(&buffer->q); - } -} - size_t spdylay_buffer_capacity(spdylay_buffer *buffer) { return buffer->capacity; @@ -130,10 +111,22 @@ size_t spdylay_buffer_capacity(spdylay_buffer *buffer) void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf) { - while(spdylay_buffer_length(buffer)) { - size_t len = spdylay_buffer_front_length(buffer); - memcpy(buf, spdylay_buffer_front_data(buffer), len); + spdylay_buffer_chunk *p = buffer->root.next; + for(; p; p = p->next) { + size_t len; + if(p == buffer->current) { + len = buffer->last_offset; + } else { + len = buffer->capacity; + } + memcpy(buf, p->data, len); buf += len; - spdylay_buffer_pop(buffer); } } + +void spdylay_buffer_reset(spdylay_buffer *buffer) +{ + buffer->current = &buffer->root; + buffer->len = 0; + buffer->last_offset = buffer->capacity; +} diff --git a/lib/spdylay_buffer.h b/lib/spdylay_buffer.h index 0d4327a0..f46126b6 100644 --- a/lib/spdylay_buffer.h +++ b/lib/spdylay_buffer.h @@ -32,14 +32,22 @@ #include #include "spdylay_queue.h" +typedef struct spdylay_buffer_chunk { + uint8_t *data; + struct spdylay_buffer_chunk *next; +} spdylay_buffer_chunk; + /* - * Fixed sized buffers backed by queue. + * List of fixed sized chunks */ typedef struct { /* Capacity of each chunk buffer */ size_t capacity; - /* Queue of chunk buffers */ - spdylay_queue q; + /* Root of list of chunk buffers. The root is dummy and its data + member is always NULL. */ + spdylay_buffer_chunk root; + /* Points to the current chunk to write */ + spdylay_buffer_chunk *current; /* Total length of this buffer */ size_t len; /* Offset of last chunk buffer */ @@ -66,20 +74,17 @@ int spdylay_buffer_alloc(spdylay_buffer *buffer); /* Returns total length of buffer */ size_t spdylay_buffer_length(spdylay_buffer *buffer); -/* Returns first chunk buffer length. If the chunk buffer is the last - one, last_offset will be returned. */ -size_t spdylay_buffer_front_length(spdylay_buffer *buffer); -/* Returns first chunk buffer pointer */ -uint8_t* spdylay_buffer_front_data(spdylay_buffer *buffer); -/* Pops first chunk buffer and decreases total length of buffer by the - size of poped chunk buffer. */ -void spdylay_buffer_pop(spdylay_buffer *buffer); /* Returns capacity of each fixed chunk buffer */ size_t spdylay_buffer_capacity(spdylay_buffer *buffer); -/* Stores the contents of buffer into buf. buf must be at least +/* Stores the contents of buffer into |buf|. |buf| must be at least spdylay_buffer_length(buffer) bytes long. */ void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf); +/* Reset |buffer| for reuse. Set the total length of buffer to 0. + Next spdylay_buffer_avail() returns 0. This function does not free + allocated memory space; they are reused. */ +void spdylay_buffer_reset(spdylay_buffer *buffer); + #endif /* SPDYLAY_BUFFER_H */ diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index 27bcf789..f543467d 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -193,26 +193,24 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen) } static 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, spdylay_zlib *inflater) { - ssize_t r; - spdylay_buffer outbuffer; - spdylay_buffer_init(&outbuffer, 4096); - r = spdylay_zlib_inflate_hd(inflater, &outbuffer, in, inlen); - if(r < 0) { - spdylay_buffer_free(&outbuffer); - return r; + ssize_t nvspace; + int r; + nvspace = spdylay_zlib_inflate_hd(inflater, inflatebuf, in, inlen); + if(nvspace < 0) { + return nvspace; } else { - uint8_t *buf = malloc(r); - if(buf == NULL) { - spdylay_buffer_free(&outbuffer); + r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace); + if(r != 0) { return SPDYLAY_ERR_NOMEM; } - spdylay_buffer_serialize(&outbuffer, buf); - spdylay_buffer_free(&outbuffer); - r = spdylay_frame_unpack_nv(nv_ptr, buf, r); - free(buf); + spdylay_buffer_serialize(inflatebuf, *nvbuf_ptr); + r = spdylay_frame_unpack_nv(nv_ptr, *nvbuf_ptr, nvspace); return r; } } @@ -485,6 +483,9 @@ 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) @@ -498,7 +499,9 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, frame->assoc_stream_id = spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK; frame->pri = spdylay_unpack_pri(payload+8); - r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+10, payloadlen-10, + r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, + nvbuf_ptr, nvbuflen_ptr, + payload+10, payloadlen-10, inflater); return r; } @@ -528,6 +531,9 @@ 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) @@ -538,7 +544,9 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, } spdylay_frame_unpack_ctrl_hd(&frame->hd, head); frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; - r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+6, payloadlen-6, + r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, + nvbuf_ptr, nvbuflen_ptr, + payload+6, payloadlen-6, inflater); return r; } @@ -621,6 +629,9 @@ 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) @@ -631,7 +642,9 @@ int spdylay_frame_unpack_headers(spdylay_headers *frame, } spdylay_frame_unpack_ctrl_hd(&frame->hd, head); frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK; - r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+6, payloadlen-6, + r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf, + nvbuf_ptr, nvbuflen_ptr, + payload+6, payloadlen-6, inflater); return r; } diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 41416a70..b072f430 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -70,9 +70,19 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, * 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. + * + * |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. + * * This function returns 0 if it succeeds or negative error code. */ 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); @@ -99,10 +109,20 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, spdylay_zlib *deflater); /* - * Unpacks SYN_REPLY frame byte sequence into |frame|. This function - * returns 0 if it succeeds or negative error code. + * 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. + * + * This function returns 0 if it succeeds or negative error code. */ 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); @@ -163,10 +183,20 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr, spdylay_zlib *deflater); /* - * Unpacks HEADERS wire format into |frame|. This function returns 0 - * if it succeeds or negative error code. + * 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. + * + * This function returns 0 if it succeeds or negative error code. */ 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); diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 5913ac69..8c7dc27f 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -155,6 +155,8 @@ 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)->settings, 0, sizeof((*session_ptr)->settings)); (*session_ptr)->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = SPDYLAY_CONCURRENT_STREAMS_MAX; @@ -271,6 +273,7 @@ void spdylay_session_del(spdylay_session *session) free(session->iframe.buf); free(session->aob.framebuf); free(session->nvbuf); + spdylay_buffer_free(&session->inflatebuf); free(session); } @@ -1305,7 +1308,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session) type = ntohs(type); 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, @@ -1321,7 +1328,11 @@ 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, @@ -1379,7 +1390,11 @@ 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, diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index 819004bc..6b7da76d 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -35,6 +35,7 @@ #include "spdylay_frame.h" #include "spdylay_zlib.h" #include "spdylay_stream.h" +#include "spdylay_buffer.h" typedef struct { spdylay_frame_type frame_type; @@ -130,6 +131,9 @@ 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/spdylay_buffer_test.c b/tests/spdylay_buffer_test.c index 75c7c816..8d992b0f 100644 --- a/tests/spdylay_buffer_test.c +++ b/tests/spdylay_buffer_test.c @@ -33,6 +33,7 @@ void test_spdylay_buffer() { spdylay_buffer buffer; + uint8_t out[1024]; spdylay_buffer_init(&buffer, 8); CU_ASSERT(0 == spdylay_buffer_length(&buffer)); CU_ASSERT(0 == spdylay_buffer_avail(&buffer)); @@ -58,26 +59,23 @@ void test_spdylay_buffer() CU_ASSERT(1 == spdylay_buffer_avail(&buffer)); - CU_ASSERT(8 == spdylay_buffer_front_length(&buffer)); - CU_ASSERT(memcmp("01234567", spdylay_buffer_front_data(&buffer), 8) == 0); - spdylay_buffer_pop(&buffer); + spdylay_buffer_serialize(&buffer, out); + CU_ASSERT(0 == memcmp("0123456789ABCDE", out, 15)); - CU_ASSERT(7 == spdylay_buffer_length(&buffer)); - CU_ASSERT(memcmp("89ABCDE", spdylay_buffer_front_data(&buffer), 7) == 0); - spdylay_buffer_pop(&buffer); + spdylay_buffer_reset(&buffer); CU_ASSERT(0 == spdylay_buffer_length(&buffer)); - CU_ASSERT(0 == spdylay_buffer_avail(&buffer)); CU_ASSERT(NULL == spdylay_buffer_get(&buffer)); - CU_ASSERT(0 == spdylay_buffer_alloc(&buffer)); CU_ASSERT(8 == spdylay_buffer_avail(&buffer)); - memcpy(spdylay_buffer_get(&buffer), "34567", 5); + memcpy(spdylay_buffer_get(&buffer), "Hello", 5); spdylay_buffer_advance(&buffer, 5); CU_ASSERT(5 == spdylay_buffer_length(&buffer)); - CU_ASSERT(memcmp("34567", spdylay_buffer_front_data(&buffer), 5) == 0); + + spdylay_buffer_serialize(&buffer, out); + CU_ASSERT(0 == memcmp("Hello", out, 5)); spdylay_buffer_free(&buffer); } diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index eec263cd..c57f66d5 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -140,7 +140,9 @@ void test_spdylay_frame_pack_headers() 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); spdylay_zlib_inflate_hd_init(&inflater); spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_FIN, 3, @@ -150,6 +152,8 @@ void test_spdylay_frame_pack_headers() &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, @@ -168,6 +172,7 @@ void test_spdylay_frame_pack_headers() spdylay_frame_headers_free(&frame.headers); spdylay_zlib_inflate_free(&inflater); spdylay_zlib_deflate_free(&deflater); + spdylay_buffer_free(&inflatebuf); } void test_spdylay_frame_pack_settings() diff --git a/tests/spdylay_zlib_test.c b/tests/spdylay_zlib_test.c index 27e4efba..9fced8c1 100644 --- a/tests/spdylay_zlib_test.c +++ b/tests/spdylay_zlib_test.c @@ -64,7 +64,8 @@ void test_spdylay_zlib() free(deflatebuf); spdylay_buffer_serialize(&buf, inflatebuf); - spdylay_zlib_deflate_free(&deflater); spdylay_zlib_inflate_free(&inflater); + + spdylay_buffer_free(&buf); }