From 050f33e8f9d89bd3b2ba28f583d16346942440dc Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 16 Feb 2012 20:54:30 +0900 Subject: [PATCH] Reuse buffers when packing frames. Temporal name/value buffer will be shared by unpacking frame. --- lib/spdylay_frame.c | 166 +++++++++++++++++++---------------- lib/spdylay_frame.h | 96 +++++++++++++------- lib/spdylay_helper.c | 16 ++++ lib/spdylay_helper.h | 14 +++ lib/spdylay_session.c | 87 ++++++++++++------ lib/spdylay_session.h | 30 +++++-- tests/spdylay_frame_test.c | 47 +++++----- tests/spdylay_session_test.c | 27 ++++-- 8 files changed, 313 insertions(+), 170 deletions(-) diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index 567e54e1..27bcf789 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -63,27 +63,35 @@ static void spdylay_frame_unpack_ctrl_hd(spdylay_ctrl_hd *hd, } static ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, char **nv, size_t nv_offset, spdylay_zlib *deflater) { - size_t nvbuflen = spdylay_frame_count_nv_space(nv); - uint8_t *nvbuf = malloc(nvbuflen); - size_t maxframelen = nv_offset+ - spdylay_zlib_deflate_hd_bound(deflater, nvbuflen); - uint8_t *framebuf = malloc(maxframelen); + size_t nvspace; + size_t maxframelen; ssize_t framelen; - spdylay_frame_pack_nv(nvbuf, nv); + int r; + nvspace = spdylay_frame_count_nv_space(nv); + r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace); + if(r != 0) { + return SPDYLAY_ERR_NOMEM; + } + maxframelen = nv_offset+spdylay_zlib_deflate_hd_bound(deflater, nvspace); + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, maxframelen); + if(r != 0) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_frame_pack_nv(*nvbuf_ptr, nv); framelen = spdylay_zlib_deflate_hd(deflater, - framebuf+nv_offset, + (*buf_ptr)+nv_offset, maxframelen-nv_offset, - nvbuf, nvbuflen); - free(nvbuf); + *nvbuf_ptr, nvspace); if(framelen < 0) { - free(framebuf); return framelen; } framelen += nv_offset; - *buf_ptr = framebuf; return framelen; } @@ -451,26 +459,28 @@ 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, + size_t *nvbuflen_ptr, spdylay_syn_stream *frame, spdylay_zlib *deflater) { - uint8_t *framebuf = NULL; ssize_t framelen; - framelen = spdylay_frame_alloc_pack_nv(&framebuf, frame->nv, + framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, + nvbuf_ptr, nvbuflen_ptr, + frame->nv, SPDYLAY_SYN_STREAM_NV_OFFSET, deflater); if(framelen < 0) { return framelen; } frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH; - memset(framebuf, 0, SPDYLAY_SYN_STREAM_NV_OFFSET); + memset(*buf_ptr, 0, SPDYLAY_SYN_STREAM_NV_OFFSET); /* pack ctrl header after length is determined */ - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->stream_id); - spdylay_put_uint32be(&framebuf[12], frame->assoc_stream_id); - framebuf[16] = (frame->pri << 6); - - *buf_ptr = framebuf; + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + spdylay_put_uint32be(&(*buf_ptr)[12], frame->assoc_stream_id); + (*buf_ptr)[16] = (frame->pri << 6); return framelen; } @@ -496,21 +506,24 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, #define SPDYLAY_SYN_REPLY_NV_OFFSET 14 ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, spdylay_syn_reply *frame, spdylay_zlib *deflater) { - uint8_t *framebuf = NULL; ssize_t framelen; - framelen = spdylay_frame_alloc_pack_nv(&framebuf, frame->nv, + framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, + nvbuf_ptr, nvbuflen_ptr, + frame->nv, SPDYLAY_SYN_REPLY_NV_OFFSET, deflater); if(framelen < 0) { return framelen; } frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH; - memset(framebuf, 0, SPDYLAY_SYN_REPLY_NV_OFFSET); - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->stream_id); - *buf_ptr = framebuf; + memset(*buf_ptr, 0, SPDYLAY_SYN_REPLY_NV_OFFSET); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); return framelen; } @@ -530,18 +543,18 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, return r; } -ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, spdylay_ping *frame) +ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_ping *frame) { - uint8_t *framebuf = NULL; ssize_t framelen = 12; - framebuf = malloc(framelen); - if(framebuf == NULL) { - return SPDYLAY_ERR_NOMEM; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; } - memset(framebuf, 0, framelen); - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->unique_id); - *buf_ptr = framebuf; + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->unique_id); return framelen; } @@ -557,18 +570,18 @@ int spdylay_frame_unpack_ping(spdylay_ping *frame, return 0; } -ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, spdylay_goaway *frame) +ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_goaway *frame) { - uint8_t *framebuf = NULL; ssize_t framelen = 12; - framebuf = malloc(framelen); - if(framebuf == NULL) { - return SPDYLAY_ERR_NOMEM; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; } - memset(framebuf, 0, framelen); - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->last_good_stream_id); - *buf_ptr = framebuf; + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->last_good_stream_id); return framelen; } @@ -587,22 +600,23 @@ int spdylay_frame_unpack_goaway(spdylay_goaway *frame, #define SPDYLAY_HEADERS_NV_OFFSET 14 -ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, +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, spdylay_zlib *deflater) { - uint8_t *framebuf = NULL; ssize_t framelen; - framelen = spdylay_frame_alloc_pack_nv(&framebuf, frame->nv, + framelen = spdylay_frame_alloc_pack_nv(buf_ptr, buflen_ptr, + nvbuf_ptr, nvbuflen_ptr, + frame->nv, SPDYLAY_HEADERS_NV_OFFSET, deflater); if(framelen < 0) { return framelen; } frame->hd.length = framelen-SPDYLAY_FRAME_HEAD_LENGTH; - memset(framebuf, 0, SPDYLAY_HEADERS_NV_OFFSET); - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->stream_id); - *buf_ptr = framebuf; + memset(*buf_ptr, 0, SPDYLAY_HEADERS_NV_OFFSET); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); return framelen; } @@ -622,20 +636,19 @@ int spdylay_frame_unpack_headers(spdylay_headers *frame, return r; } -ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, +ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, spdylay_rst_stream *frame) { - uint8_t *framebuf; ssize_t framelen = 16; - framebuf = malloc(framelen); - if(framebuf == NULL) { - return SPDYLAY_ERR_NOMEM; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; } - memset(framebuf, 0, framelen); - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->stream_id); - spdylay_put_uint32be(&framebuf[12], frame->status_code); - *buf_ptr = framebuf; + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->stream_id); + spdylay_put_uint32be(&(*buf_ptr)[12], frame->status_code); return framelen; } @@ -652,34 +665,33 @@ int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame, return 0; } -ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, spdylay_settings *frame) +ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_settings *frame) { - uint8_t *framebuf; ssize_t framelen = SPDYLAY_FRAME_HEAD_LENGTH+frame->hd.length; - int i; - framebuf = malloc(framelen); - if(framebuf == NULL) { - return SPDYLAY_ERR_NOMEM; + int i, r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; } - memset(framebuf, 0, framelen); - spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); - spdylay_put_uint32be(&framebuf[8], frame->niv); + memset(*buf_ptr, 0, framelen); + spdylay_frame_pack_ctrl_hd(*buf_ptr, &frame->hd); + spdylay_put_uint32be(&(*buf_ptr)[8], frame->niv); for(i = 0; i < frame->niv; ++i) { int off = i*8; /* spdy/2 spec says ID is network byte order, but publicly deployed server sends little endian host byte order. */ char *id_ptr = (char*)(&frame->iv[i].settings_id); #ifdef WORDS_BIGENDIAN - framebuf[12+off] = id_ptr[3]; - framebuf[12+off+1] = id_ptr[2]; - framebuf[12+off+2] = id_ptr[1]; + (*buf_ptr)[12+off] = id_ptr[3]; + (*buf_ptr)[12+off+1] = id_ptr[2]; + (*buf_ptr)[12+off+2] = id_ptr[1]; #else /* !WORDS_BIGENDIAN */ - memcpy(&framebuf[12+off], id_ptr, 3); + memcpy(&(*buf_ptr)[12+off], id_ptr, 3); #endif /* !WORDS_BIGENDIAN */ - framebuf[15+off] = frame->iv[i].flags; - spdylay_put_uint32be(&framebuf[16+off], frame->iv[i].value); + (*buf_ptr)[15+off] = frame->iv[i].flags; + spdylay_put_uint32be(&(*buf_ptr)[16+off], frame->iv[i].value); } - *buf_ptr = framebuf; return framelen; } diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index fcdcbe68..41416a70 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -44,14 +44,25 @@ #define SPDYLAY_FRAME_HEAD_LENGTH 8 /* - * Packs SYN_STREAM frame |frame| in wire frame format and store it in - * |*buf_ptr|. This function allocates enough memory to store given - * frame in |*buf_ptr|. This function returns the size of packed - * frame if it succeeds, or returns negative error + * Packs SYN_STREAM frame |frame| in wire format and store it in + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. + * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire + * format temporarily. Its length is |*nvbuflen_ptr| bytes. This + * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store + * frame and name/value pairs. When expansion occurred, memory + * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. + * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are + * updated accordingly. + * + * This function returns the size of + * packed frame if it succeeds, or returns negative error * code. frame->hd.length is assigned after length is determined * during packing process. */ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, spdylay_syn_stream *frame, spdylay_zlib *deflater); @@ -68,12 +79,22 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, /* * Packs SYN_REPLY frame |frame| in wire frame format and store it in - * |*buf_ptr|. This function allocates enough memory to store given - * frame in |*buf_ptr|. This function returns the size of packed frame - * it it succeeds, or returns negative error code. frame->hd.length is - * assigned after length is determined during packing process. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. + * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire + * format temporarily. Its length is |*nvbuflen_ptr| bytes. This + * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store + * frame and name/value pairs. When expansion occurred, memory + * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. + * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are + * updated accordingly. This function returns the size of packed + * frame it it succeeds, or returns negative error + * code. frame->hd.length is assigned after length is determined + * during packing process. */ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr, + size_t *buflen_ptr, + uint8_t **nvbuf_ptr, + size_t *nvbuflen_ptr, spdylay_syn_reply *frame, spdylay_zlib *deflater); @@ -88,11 +109,13 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, /* * Packs PING frame |frame| in wire format and store it in - * |*buf_ptr|. This function allocates enough memory in |*buf_ptr| to - * store given |frame|. This function returns the size of packed frame - * if it succeeds, or negative error code. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. This function returns the size of packed frame if it + * succeeds, or negative error code. */ -ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, spdylay_ping *frame); +ssize_t spdylay_frame_pack_ping(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_ping *frame); /* * Unpacks PING wire format into |frame|. This function returns 0 if @@ -104,11 +127,13 @@ int spdylay_frame_unpack_ping(spdylay_ping *frame, /* * Packs GOAWAY frame |frame | in wire format and store it in - * |*buf_ptr|. This function allocates enough memory in |*buf_ptr| to - * store given |frame|. This function returns the size of packed frame - * if it succeeds, or negative error code. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. This function returns the size of packed frame if it + * succeeds, or negative error code. */ -ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, spdylay_goaway *frame); +ssize_t spdylay_frame_pack_goaway(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_goaway *frame); /* * Unpacks GOAWAY wire format into |frame|. This function returns 0 if @@ -120,12 +145,20 @@ int spdylay_frame_unpack_goaway(spdylay_goaway *frame, /* * Packs HEADERS frame |frame| in wire format and store it in - * |*buf_ptr|. This function allocates enough memory in |*buf_ptr| to - * store given |frame|. This function returns the size of packed frame - * it it succeeds, or returns negative error code. frame->hd.length is - * assigned after length is determined during packing process. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| bytes. + * The |*nvbuf_ptr| is used to store inflated name/value pairs in wire + * format temporarily. Its length is |*nvbuflen_ptr| bytes. This + * function expands |*buf_ptr| and |*nvbuf_ptr| as necessary to store + * frame and name/value pairs. When expansion occurred, memory + * previously pointed by |*buf_ptr| and |*nvbuf_ptr| is freed. + * |*buf_ptr|, |*buflen_ptr|, |*nvbuf_ptr| and |*nvbuflen_ptr| are + * updated accordingly. This function returns the size of packed + * frame it it succeeds, or returns negative error + * code. frame->hd.length is assigned after length is determined + * during packing process. */ -ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, +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, spdylay_zlib *deflater); @@ -140,12 +173,13 @@ int spdylay_frame_unpack_headers(spdylay_headers *frame, /* * Packs RST_STREAM frame |frame| in wire frame format and store it in - * |*buf_ptr|. This function allocates enough memory to store given - * frame in |*buf_ptr|. In spdy/2 spc, RST_STREAM wire format is - * always 16 bytes long. This function returns the size of packed - * frame if it succeeds, or negative error code. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. In spdy/2 spc, RST_STREAM wire format is always 16 + * bytes long. This function returns the size of packed frame if it + * succeeds, or negative error code. */ -ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, +ssize_t spdylay_frame_pack_rst_stream(uint8_t **buf_ptr, size_t *buflen_ptr, spdylay_rst_stream *frame); /* @@ -158,11 +192,13 @@ int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame, /* * Packs SETTINGS frame |frame| in wire format and store it in - * |*buf_ptr|. This function allocates enough memory to store given - * frame in |*buf_ptr|. This function returns the size of packed frame - * if it succeeds, or negative error code. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. This function returns the size of packed frame if it + * succeeds, or negative error code. */ -ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, spdylay_settings *frame); +ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_settings *frame); /* * Unpacks SETTINGS wire format into |frame|. This function returns 0 diff --git a/lib/spdylay_helper.c b/lib/spdylay_helper.c index 6418aa31..d1bc8bc2 100644 --- a/lib/spdylay_helper.c +++ b/lib/spdylay_helper.c @@ -52,3 +52,19 @@ uint32_t spdylay_get_uint32(const uint8_t *data) memcpy(&n, data, sizeof(uint32_t)); return ntohl(n); } + +int spdylay_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, + size_t min_length) +{ + if(min_length > *buflen_ptr) { + uint8_t *temp = malloc(min_length); + if(temp == NULL) { + return SPDYLAY_ERR_NOMEM; + } else { + free(*buf_ptr); + *buf_ptr = temp; + *buflen_ptr = min_length; + } + } + return 0; +} diff --git a/lib/spdylay_helper.h b/lib/spdylay_helper.h index 6b547f31..4ac44768 100644 --- a/lib/spdylay_helper.h +++ b/lib/spdylay_helper.h @@ -55,4 +55,18 @@ uint16_t spdylay_get_uint16(const uint8_t *data); */ uint32_t spdylay_get_uint32(const uint8_t *data); +/* + * Ensures that buffer |*buf_ptr| with |*buflen_ptr| length has at + * least |min_length| bytes. If |min_length| > |*buflen_ptr|, + * allocates new buffer having at least |min_length| bytes and assigns + * its pointer to |*buf_ptr| and allocated number of bytes to + * |*buflen_ptr|. The memory pointed by |*buf_ptr| previously is + * freed. No memory copy is done between old and new buffer. This + * function returns 0 if it succeeds, or negative error code. + * |*buf_ptr| and |*buflen_ptr| are only updated iff this function + * succeeds. + */ +int spdylay_reserve_buffer(uint8_t **buf_ptr, size_t *buflen_ptr, + size_t min_length); + #endif /* SPDYLAY_HELPER_H */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c index 753c9b63..5913ac69 100644 --- a/lib/spdylay_session.c +++ b/lib/spdylay_session.c @@ -130,6 +130,31 @@ static int spdylay_session_new(spdylay_session **session_ptr, return r; } + (*session_ptr)->aob.framebuf = malloc(SPDYLAY_INITIAL_OUTBOUND_BUFFER_LENGTH); + if((*session_ptr)->aob.framebuf == NULL) { + spdylay_pq_free(&(*session_ptr)->ob_ss_pq); + spdylay_pq_free(&(*session_ptr)->ob_pq); + spdylay_map_free(&(*session_ptr)->streams); + spdylay_zlib_inflate_free(&(*session_ptr)->hd_inflater); + spdylay_zlib_deflate_free(&(*session_ptr)->hd_deflater); + free(*session_ptr); + return r; + } + (*session_ptr)->aob.framebufmax = SPDYLAY_INITIAL_OUTBOUND_BUFFER_LENGTH; + + (*session_ptr)->nvbuf = malloc(SPDYLAY_INITIAL_NV_BUFFER_LENGTH); + if((*session_ptr)->nvbuf == NULL) { + free((*session_ptr)->aob.framebuf); + spdylay_pq_free(&(*session_ptr)->ob_ss_pq); + spdylay_pq_free(&(*session_ptr)->ob_pq); + spdylay_map_free(&(*session_ptr)->streams); + spdylay_zlib_inflate_free(&(*session_ptr)->hd_inflater); + spdylay_zlib_deflate_free(&(*session_ptr)->hd_deflater); + free(*session_ptr); + return r; + } + (*session_ptr)->nvbuflen = SPDYLAY_INITIAL_NV_BUFFER_LENGTH; + memset((*session_ptr)->settings, 0, sizeof((*session_ptr)->settings)); (*session_ptr)->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] = SPDYLAY_CONCURRENT_STREAMS_MAX; @@ -244,6 +269,8 @@ void spdylay_session_del(spdylay_session *session) spdylay_zlib_deflate_free(&session->hd_deflater); spdylay_zlib_inflate_free(&session->hd_inflater); free(session->iframe.buf); + free(session->aob.framebuf); + free(session->nvbuf); free(session); } @@ -454,12 +481,10 @@ static int spdylay_session_is_data_allowed(spdylay_session *session, } ssize_t spdylay_session_prep_frame(spdylay_session *session, - spdylay_outbound_item *item, - uint8_t **framebuf_ptr) + spdylay_outbound_item *item) { /* TODO Get or validate stream ID here */ /* TODO Validate assoc_stream_id here */ - uint8_t *framebuf; ssize_t framebuflen; switch(item->frame_type) { case SPDYLAY_SYN_STREAM: { @@ -473,7 +498,10 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, stream_id = session->next_stream_id; item->frame->syn_stream.stream_id = stream_id; session->next_stream_id += 2; - framebuflen = spdylay_frame_pack_syn_stream(&framebuf, + framebuflen = spdylay_frame_pack_syn_stream(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, &item->frame->syn_stream, &session->hd_deflater); if(framebuflen < 0) { @@ -485,7 +513,6 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, item->frame->syn_stream.pri, SPDYLAY_STREAM_INITIAL, aux_data->stream_user_data) == NULL) { - free(framebuf); return SPDYLAY_ERR_NOMEM; } break; @@ -495,7 +522,10 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, item->frame->syn_reply.stream_id)) { return SPDYLAY_ERR_INVALID_FRAME; } - framebuflen = spdylay_frame_pack_syn_reply(&framebuf, + framebuflen = spdylay_frame_pack_syn_reply(&session->aob.framebuf, + &session->aob.framebufmax, + &session->nvbuf, + &session->nvbuflen, &item->frame->syn_reply, &session->hd_deflater); if(framebuflen < 0) { @@ -504,14 +534,16 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, break; } case SPDYLAY_RST_STREAM: - framebuflen = spdylay_frame_pack_rst_stream(&framebuf, + framebuflen = spdylay_frame_pack_rst_stream(&session->aob.framebuf, + &session->aob.framebufmax, &item->frame->rst_stream); if(framebuflen < 0) { return framebuflen; } break; case SPDYLAY_SETTINGS: - framebuflen = spdylay_frame_pack_settings(&framebuf, + framebuflen = spdylay_frame_pack_settings(&session->aob.framebuf, + &session->aob.framebufmax, &item->frame->settings); if(framebuflen < 0) { return framebuflen; @@ -522,7 +554,9 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, unreachable. */ abort(); case SPDYLAY_PING: - framebuflen = spdylay_frame_pack_ping(&framebuf, &item->frame->ping); + framebuflen = spdylay_frame_pack_ping(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->ping); if(framebuflen < 0) { return framebuflen; } @@ -539,7 +573,9 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, last-good-stream-id. */ return SPDYLAY_ERR_INVALID_FRAME; } - framebuflen = spdylay_frame_pack_goaway(&framebuf, &item->frame->goaway); + framebuflen = spdylay_frame_pack_goaway(&session->aob.framebuf, + &session->aob.framebufmax, + &item->frame->goaway); if(framebuflen < 0) { return framebuflen; } @@ -548,7 +584,9 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, if(!spdylay_session_is_data_allowed(session, item->frame->data.stream_id)) { return SPDYLAY_ERR_INVALID_FRAME; } - framebuflen = spdylay_session_pack_data(session, &framebuf, + framebuflen = spdylay_session_pack_data(session, + &session->aob.framebuf, + &session->aob.framebufmax, &item->frame->data); if(framebuflen < 0) { return framebuflen; @@ -558,7 +596,6 @@ ssize_t spdylay_session_prep_frame(spdylay_session *session, default: framebuflen = SPDYLAY_ERR_INVALID_ARGUMENT; } - *framebuf_ptr = framebuf; return framebuflen; } @@ -567,8 +604,8 @@ static void spdylay_active_outbound_item_reset { spdylay_outbound_item_free(aob->item); free(aob->item); - free(aob->framebuf); - memset(aob, 0, sizeof(spdylay_active_outbound_item)); + aob->item = NULL; + aob->framebuflen = aob->framebufoff = 0; } spdylay_outbound_item* spdylay_session_get_ob_pq_top @@ -815,13 +852,12 @@ int spdylay_session_send(spdylay_session *session) ssize_t sentlen; if(session->aob.item == NULL) { spdylay_outbound_item *item; - uint8_t *framebuf; ssize_t framebuflen; item = spdylay_session_pop_next_ob_item(session); if(item == NULL) { break; } - framebuflen = spdylay_session_prep_frame(session, item, &framebuf); + framebuflen = spdylay_session_prep_frame(session, item); if(framebuflen < 0) { /* TODO Call error callback? */ spdylay_outbound_item_free(item); @@ -833,7 +869,6 @@ int spdylay_session_send(spdylay_session *session) } } session->aob.item = item; - session->aob.framebuf = framebuf; session->aob.framebuflen = framebuflen; /* Call before_send callback */ if(item->frame_type != SPDYLAY_DATA && @@ -1582,21 +1617,17 @@ int spdylay_session_add_goaway(spdylay_session *session, } ssize_t spdylay_session_pack_data(spdylay_session *session, - uint8_t **buf_ptr, spdylay_data *frame) + uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_data *frame) { - uint8_t *framebuf; ssize_t framelen = SPDYLAY_DATA_FRAME_LENGTH; - framebuf = malloc(framelen); - if(framebuf == NULL) { - return SPDYLAY_ERR_NOMEM; + int r; + r = spdylay_reserve_buffer(buf_ptr, buflen_ptr, framelen); + if(r != 0) { + return r; } - framelen = spdylay_session_pack_data_overwrite(session, framebuf, framelen, + framelen = spdylay_session_pack_data_overwrite(session, *buf_ptr, framelen, frame); - if(framelen < 0) { - free(framebuf); - } else { - *buf_ptr = framebuf; - } return framelen; } diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h index 077fef0a..819004bc 100644 --- a/lib/spdylay_session.h +++ b/lib/spdylay_session.h @@ -46,8 +46,16 @@ typedef struct { typedef struct { spdylay_outbound_item *item; + /* Buffer for outbound frames. Used to pack one frame. The memory + pointed by framebuf is initially allocated by + spdylay_session_{client,server}_new() and deallocated by + spdylay_session_del() */ uint8_t *framebuf; + /* The capacity of framebuf in bytes */ + size_t framebufmax; + /* The length of the frame stored in framebuf */ size_t framebuflen; + /* The number of bytes has been sent */ size_t framebufoff; } spdylay_active_outbound_item; @@ -55,6 +63,9 @@ typedef struct { message block of SSLv3/TLSv1 */ #define SPDYLAY_INBOUND_BUFFER_LENGTH 16384 +#define SPDYLAY_INITIAL_OUTBOUND_BUFFER_LENGTH SPDYLAY_DATA_FRAME_LENGTH +#define SPDYLAY_INITIAL_NV_BUFFER_LENGTH 4096 + typedef struct { uint8_t buf[SPDYLAY_INBOUND_BUFFER_LENGTH]; uint8_t *mark; @@ -114,6 +125,12 @@ struct spdylay_session { spdylay_inbound_buffer ibuf; spdylay_inbound_frame iframe; + /* Buffer used to store inflated name/value pairs in wire format + temporarily on pack/unpack. */ + uint8_t *nvbuf; + /* The number of bytes allocated for nvbuf */ + size_t nvbuflen; + spdylay_zlib hd_deflater; spdylay_zlib hd_inflater; @@ -274,14 +291,15 @@ spdylay_stream* spdylay_session_get_stream(spdylay_session *session, /* * Packs DATA frame |frame| in wire frame format and store it in - * |*buf_ptr|. This function always allocates - * 8+SPDYLAY_DATA_CHUNK_LENGTH bytes. It packs header in first 8 - * bytes. Remaining bytes are filled using frame->data_prd. This - * function returns the size of packed frame if it succeeds, or - * negative error code. + * |*buf_ptr|. The capacity of |*buf_ptr| is |*buflen_ptr| + * length. This function expands |*buf_ptr| as necessary to store + * given |frame|. It packs header in first 8 bytes. Remaining bytes + * are filled using frame->data_prd. This function returns the size + * of packed frame if it succeeds, or negative error code. */ ssize_t spdylay_session_pack_data(spdylay_session *session, - uint8_t **buf_ptr, spdylay_data *frame); + uint8_t **buf_ptr, size_t *buflen_ptr, + spdylay_data *frame); /* * Packs DATA frame |frame| in wire frame format and store it in diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index 73a22e95..eec263cd 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -95,15 +95,16 @@ void test_spdylay_frame_count_unpack_nv_space() void test_spdylay_frame_pack_ping() { spdylay_frame frame, oframe; - uint8_t *buf; - ssize_t buflen; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; spdylay_frame_ping_init(&frame.ping, 1); - buflen = spdylay_frame_pack_ping(&buf, &frame.ping); + framelen = spdylay_frame_pack_ping(&buf, &buflen, &frame.ping); CU_ASSERT(0 == spdylay_frame_unpack_ping (&oframe.ping, &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, &buf[SPDYLAY_FRAME_HEAD_LENGTH], - buflen-SPDYLAY_FRAME_HEAD_LENGTH)); + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); CU_ASSERT(1 == oframe.ping.unique_id); free(buf); spdylay_frame_ping_free(&oframe.ping); @@ -113,20 +114,21 @@ void test_spdylay_frame_pack_ping() void test_spdylay_frame_pack_goaway() { spdylay_frame frame, oframe; - uint8_t *buf; - ssize_t buflen; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; spdylay_frame_goaway_init(&frame.goaway, 1000000007); - buflen = spdylay_frame_pack_goaway(&buf, &frame.goaway); + framelen = spdylay_frame_pack_goaway(&buf, &buflen, &frame.goaway); CU_ASSERT(0 == spdylay_frame_unpack_goaway (&oframe.goaway, &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, &buf[SPDYLAY_FRAME_HEAD_LENGTH], - buflen-SPDYLAY_FRAME_HEAD_LENGTH)); + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); CU_ASSERT(1000000007 == oframe.goaway.last_good_stream_id); CU_ASSERT(SPDYLAY_PROTO_VERSION == oframe.headers.hd.version); CU_ASSERT(SPDYLAY_GOAWAY == oframe.headers.hd.type); CU_ASSERT(SPDYLAY_FLAG_NONE == oframe.headers.hd.flags); - CU_ASSERT(buflen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); free(buf); spdylay_frame_goaway_free(&oframe.goaway); spdylay_frame_goaway_free(&frame.goaway); @@ -136,28 +138,32 @@ void test_spdylay_frame_pack_headers() { spdylay_zlib deflater, inflater; spdylay_frame frame, oframe; - uint8_t *buf; - ssize_t buflen; + uint8_t *buf = NULL, *nvbuf = NULL; + size_t buflen = 0, nvbuflen = 0; + ssize_t framelen; spdylay_zlib_deflate_hd_init(&deflater); spdylay_zlib_inflate_hd_init(&inflater); spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_FIN, 3, spdylay_frame_nv_copy(headers)); - buflen = spdylay_frame_pack_headers(&buf, &frame.headers, &deflater); + framelen = spdylay_frame_pack_headers(&buf, &buflen, + &nvbuf, &nvbuflen, + &frame.headers, &deflater); CU_ASSERT(0 == spdylay_frame_unpack_headers (&oframe.headers, &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, &buf[SPDYLAY_FRAME_HEAD_LENGTH], - buflen-SPDYLAY_FRAME_HEAD_LENGTH, + framelen-SPDYLAY_FRAME_HEAD_LENGTH, &inflater)); CU_ASSERT(3 == oframe.headers.stream_id); CU_ASSERT(SPDYLAY_PROTO_VERSION == oframe.headers.hd.version); CU_ASSERT(SPDYLAY_HEADERS == oframe.headers.hd.type); CU_ASSERT(SPDYLAY_FLAG_FIN == oframe.headers.hd.flags); - CU_ASSERT(buflen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.ping.hd.length); CU_ASSERT(strcmp("method", oframe.headers.nv[0]) == 0); CU_ASSERT(strcmp("GET", oframe.headers.nv[1]) == 0); CU_ASSERT(NULL == oframe.headers.nv[12]); free(buf); + free(nvbuf); spdylay_frame_headers_free(&oframe.headers); spdylay_frame_headers_free(&frame.headers); spdylay_zlib_inflate_free(&inflater); @@ -167,8 +173,9 @@ void test_spdylay_frame_pack_headers() void test_spdylay_frame_pack_settings() { spdylay_frame frame, oframe; - uint8_t *buf; - ssize_t buflen; + uint8_t *buf = NULL; + size_t buflen = 0; + ssize_t framelen; int i; spdylay_settings_entry iv[3]; iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH; @@ -185,20 +192,20 @@ void test_spdylay_frame_pack_settings() (&frame.settings, SPDYLAY_FLAG_SETTINGS_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS, spdylay_frame_iv_copy(iv, 3), 3); - buflen = spdylay_frame_pack_settings(&buf, &frame.settings); - CU_ASSERT(8+4+3*8 == buflen); + framelen = spdylay_frame_pack_settings(&buf, &buflen, &frame.settings); + CU_ASSERT(8+4+3*8 == framelen); CU_ASSERT(0 == spdylay_frame_unpack_settings (&oframe.settings, &buf[0], SPDYLAY_FRAME_HEAD_LENGTH, &buf[SPDYLAY_FRAME_HEAD_LENGTH], - buflen-SPDYLAY_FRAME_HEAD_LENGTH)); + framelen-SPDYLAY_FRAME_HEAD_LENGTH)); CU_ASSERT(SPDYLAY_PROTO_VERSION == oframe.settings.hd.version); CU_ASSERT(SPDYLAY_SETTINGS == oframe.settings.hd.type); CU_ASSERT(SPDYLAY_FLAG_SETTINGS_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS == oframe.settings.hd.flags); - CU_ASSERT(buflen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.settings.hd.length); + CU_ASSERT(framelen-SPDYLAY_FRAME_HEAD_LENGTH == oframe.settings.hd.length); CU_ASSERT(3 == oframe.settings.niv); for(i = 0; i < 3; ++i) { diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c index f7bc8fe3..3c949d82 100644 --- a/tests/spdylay_session_test.c +++ b/tests/spdylay_session_test.c @@ -190,18 +190,22 @@ void test_spdylay_session_recv() const char *nv[] = { "url", "/", NULL }; - uint8_t *framedata; - size_t framelen; + uint8_t *framedata = NULL, *nvbuf = NULL; + size_t framedatalen = 0, nvbuflen = 0; + ssize_t framelen; spdylay_frame frame; user_data.df = &df; spdylay_session_client_new(&session, &callbacks, &user_data); spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, 0, 0, 3, dup_nv(nv)); - framelen = spdylay_frame_pack_syn_stream(&framedata, &frame.syn_stream, + framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_stream, &session->hd_deflater); scripted_data_feed_init(&df, framedata, framelen); free(framedata); + free(nvbuf); spdylay_frame_syn_stream_free(&frame.syn_stream); CU_ASSERT(0 == spdylay_session_recv(session)); @@ -272,8 +276,9 @@ void test_spdylay_session_recv_invalid_stream_id() scripted_data_feed df; my_user_data user_data; const char *nv[] = { NULL }; - uint8_t *framedata; - size_t framelen; + uint8_t *framedata = NULL, *nvbuf = NULL; + size_t framedatalen = 0, nvbuflen = 0; + ssize_t framelen; spdylay_frame frame; user_data.df = &df; @@ -281,10 +286,11 @@ void test_spdylay_session_recv_invalid_stream_id() spdylay_session_client_new(&session, &callbacks, &user_data); spdylay_frame_syn_stream_init(&frame.syn_stream, SPDYLAY_FLAG_NONE, 1, 0, 3, dup_nv(nv)); - framelen = spdylay_frame_pack_syn_stream(&framedata, &frame.syn_stream, + framelen = spdylay_frame_pack_syn_stream(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_stream, &session->hd_deflater); scripted_data_feed_init(&df, framedata, framelen); - free(framedata); spdylay_frame_syn_stream_free(&frame.syn_stream); CU_ASSERT(0 == spdylay_session_recv(session)); @@ -292,15 +298,18 @@ void test_spdylay_session_recv_invalid_stream_id() spdylay_frame_syn_reply_init(&frame.syn_reply, SPDYLAY_FLAG_NONE, 100, dup_nv(nv)); - framelen = spdylay_frame_pack_syn_reply(&framedata, &frame.syn_reply, + framelen = spdylay_frame_pack_syn_reply(&framedata, &framedatalen, + &nvbuf, &nvbuflen, + &frame.syn_reply, &session->hd_deflater); scripted_data_feed_init(&df, framedata, framelen); - free(framedata); spdylay_frame_syn_reply_free(&frame.syn_reply); CU_ASSERT(0 == spdylay_session_recv(session)); CU_ASSERT(2 == user_data.invalid_ctrl_recv_cb_called); + free(framedata); + free(nvbuf); spdylay_session_del(session); }