From 0b75800c23bc3f414b11dcf90257ef59534f9ca2 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 1 Feb 2012 00:26:26 +0900 Subject: [PATCH] Added SETTINGS frame and its pack/unpack functions. --- lib/includes/spdylay/spdylay.h | 35 +++++++++++++++ lib/spdylay_frame.c | 78 ++++++++++++++++++++++++++++++++++ lib/spdylay_frame.h | 33 ++++++++++++++ tests/main.c | 2 + tests/spdylay_frame_test.c | 48 +++++++++++++++++++++ tests/spdylay_frame_test.h | 1 + 6 files changed, 197 insertions(+) diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index d5de38b..36074e6 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -78,6 +78,27 @@ typedef enum { SPDYLAY_FLAG_UNIDIRECTIONAL = 2 } spdylay_flag; +typedef enum { + SPDYLAY_FLAG_SETTINGS_NONE = 0, + SPDYLAY_FLAG_SETTINGS_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS = 1 +} spdylay_settings_flag; + +typedef enum { + SPDYLAY_ID_FLAG_SETTINGS_NONE = 0, + SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE = 1, + SPDYLAY_ID_FLAG_SETTINGS_PERSISTED = 2 +} spdylay_settings_id_flag; + +typedef enum { + SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH = 1, + SPDYLAY_SETTINGS_DOWNLOAD_BANDWIDTH = 2, + SPDYLAY_SETTINGS_ROUND_TRIP_TIME = 3, + SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS = 4, + SPDYLAY_SETTINGS_CURRENT_CWND = 5, + SPDYLAY_SETTINGS_DOWNLOAD_RETRANS_RATE = 6, + SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE = 7 +} spdylay_settings_id; + typedef enum { SPDYLAY_OK = 0, SPDYLAY_PROTOCOL_ERROR = 1, @@ -123,6 +144,19 @@ typedef struct { uint32_t status_code; } spdylay_rst_stream; +typedef struct { + int32_t settings_id; + uint8_t flags; + uint32_t value; +} spdylay_settings_entry; + +typedef struct { + spdylay_ctrl_hd hd; + /* Number of entries in |iv| */ + size_t niv; + spdylay_settings_entry *iv; +} spdylay_settings; + typedef struct { spdylay_ctrl_hd hd; uint32_t unique_id; @@ -158,6 +192,7 @@ typedef union { spdylay_syn_stream syn_stream; spdylay_syn_reply syn_reply; spdylay_rst_stream rst_stream; + spdylay_settings settings; spdylay_ping ping; spdylay_goaway goaway; spdylay_headers headers; diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c index 8f613df..e4c1572 100644 --- a/lib/spdylay_frame.c +++ b/lib/spdylay_frame.c @@ -444,6 +444,23 @@ void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame, void spdylay_frame_rst_stream_free(spdylay_rst_stream *frame) {} +void spdylay_frame_settings_init(spdylay_settings *frame, uint8_t flags, + spdylay_settings_entry *iv, size_t niv) +{ + memset(frame, 0, sizeof(spdylay_settings)); + frame->hd.version = SPDYLAY_PROTO_VERSION; + frame->hd.type = SPDYLAY_SETTINGS; + frame->hd.flags = flags; + frame->hd.length = 4+niv*8; + frame->niv = niv; + frame->iv = iv; +} + +void spdylay_frame_settings_free(spdylay_settings *frame) +{ + free(frame->iv); +} + void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, spdylay_data_provider *data_prd) { @@ -658,3 +675,64 @@ int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame, frame->status_code = spdylay_get_uint32(payload+4); return 0; } + +ssize_t spdylay_frame_pack_settings(uint8_t **buf_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; + } + memset(framebuf, 0, framelen); + spdylay_frame_pack_ctrl_hd(framebuf, &frame->hd); + spdylay_put_uint32be(&framebuf[8], frame->niv); + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + spdylay_put_uint32be(&framebuf[12+off], frame->iv[i].settings_id << 8); + framebuf[15+off] = frame->iv[i].flags; + spdylay_put_uint32be(&framebuf[16+off], frame->iv[i].value); + } + *buf_ptr = framebuf; + return framelen; +} + +int spdylay_frame_unpack_settings(spdylay_settings *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen) +{ + int i; + if(payloadlen < 4) { + return SPDYLAY_ERR_INVALID_FRAME; + } + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + frame->niv = spdylay_get_uint32(payload); + if(payloadlen != 4+frame->niv*8) { + return SPDYLAY_ERR_INVALID_FRAME; + } + frame->iv = malloc(frame->niv*sizeof(spdylay_settings_entry)); + if(frame->iv == NULL) { + return SPDYLAY_ERR_NOMEM; + } + for(i = 0; i < frame->niv; ++i) { + int off = i*8; + frame->iv[i].settings_id = (spdylay_get_uint32(&payload[4+off]) >> 8); + frame->iv[i].flags = payload[7+off]; + frame->iv[i].value = spdylay_get_uint32(&payload[8+off]); + } + return 0; +} + +spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv, + size_t niv) +{ + spdylay_settings_entry *iv_copy; + size_t len = niv*sizeof(spdylay_settings_entry); + iv_copy = malloc(len); + if(iv_copy == NULL) { + return NULL; + } + memcpy(iv_copy, iv, len); + return iv_copy; +} diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h index 5e4e4eb..93123fa 100644 --- a/lib/spdylay_frame.h +++ b/lib/spdylay_frame.h @@ -155,6 +155,22 @@ int spdylay_frame_unpack_rst_stream(spdylay_rst_stream *frame, const uint8_t *head, size_t headlen, const uint8_t *payload, size_t payloadlen); +/* + * 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. + */ +ssize_t spdylay_frame_pack_settings(uint8_t **buf_ptr, spdylay_settings *frame); + +/* + * Unpacks SETTINGS wire format into |frame|. This function returns 0 + * if it succeeds, or negative error code. + */ +int spdylay_frame_unpack_settings(spdylay_settings *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen); + /* * Returns number of bytes to pack name/value pairs |nv|. This * function expects |nv| is sorted in ascending order of key. This @@ -212,6 +228,15 @@ void spdylay_frame_rst_stream_init(spdylay_rst_stream *frame, void spdylay_frame_rst_stream_free(spdylay_rst_stream *frame); +/* + * Initializes SETTINGS frame |frame| with given values. |frame| takes + * ownership of |iv|, so caller must not free it. + */ +void spdylay_frame_settings_init(spdylay_settings *frame, uint8_t flags, + spdylay_settings_entry *iv, size_t niv); + +void spdylay_frame_settings_free(spdylay_settings *frame); + void spdylay_frame_data_init(spdylay_data *frame, int32_t stream_id, spdylay_data_provider *data_prd); @@ -239,4 +264,12 @@ char** spdylay_frame_nv_copy(const char **nv); */ void spdylay_frame_nv_sort(char **nv); +/* + * Makes copy of |iv| and return the copy. The |niv| is the number of + * entries in |iv|. This function returns the pointer to the copy if + * it succeeds, or NULL. + */ +spdylay_settings_entry* spdylay_frame_iv_copy(const spdylay_settings_entry *iv, + size_t niv); + #endif /* SPDYLAY_FRAME_H */ diff --git a/tests/main.c b/tests/main.c index 6eda7f3..76d199f 100644 --- a/tests/main.c +++ b/tests/main.c @@ -100,6 +100,8 @@ int main() test_spdylay_frame_pack_goaway) || !CU_add_test(pSuite, "frame_pack_headers", test_spdylay_frame_pack_headers) || + !CU_add_test(pSuite, "frame_pack_settings", + test_spdylay_frame_pack_settings) || !CU_add_test(pSuite, "frame_nv_sort", test_spdylay_frame_nv_sort)) { CU_cleanup_registry(); return CU_get_error(); diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c index 936cc46..50bb3c1 100644 --- a/tests/spdylay_frame_test.c +++ b/tests/spdylay_frame_test.c @@ -125,6 +125,54 @@ void test_spdylay_frame_pack_headers() spdylay_zlib_deflate_free(&deflater); } +void test_spdylay_frame_pack_settings() +{ + spdylay_frame frame, oframe; + uint8_t *buf; + ssize_t buflen; + int i; + spdylay_settings_entry iv[3]; + iv[0].settings_id = SPDYLAY_SETTINGS_UPLOAD_BANDWIDTH; + iv[0].flags = SPDYLAY_ID_FLAG_SETTINGS_PERSIST_VALUE; + iv[0].value = 256; + iv[1].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; + iv[1].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv[1].value = 100; + iv[2].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; + iv[2].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; + iv[2].value = 65536; + + spdylay_frame_settings_init + (&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); + + 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)); + + 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(3 == oframe.settings.niv); + for(i = 0; i < 3; ++i) { + CU_ASSERT(iv[i].settings_id == oframe.settings.iv[i].settings_id); + CU_ASSERT(iv[i].flags == oframe.settings.iv[i].flags); + CU_ASSERT(iv[i].value == oframe.settings.iv[i].value); + } + + free(buf); + spdylay_frame_settings_free(&frame.settings); + spdylay_frame_settings_free(&oframe.settings); +} + void test_spdylay_frame_nv_sort() { char *nv[7]; diff --git a/tests/spdylay_frame_test.h b/tests/spdylay_frame_test.h index b8f6aed..104c099 100644 --- a/tests/spdylay_frame_test.h +++ b/tests/spdylay_frame_test.h @@ -30,6 +30,7 @@ void test_spdylay_frame_count_nv_space(); void test_spdylay_frame_pack_ping(); void test_spdylay_frame_pack_goaway(); void test_spdylay_frame_pack_headers(); +void test_spdylay_frame_pack_settings(); void test_spdylay_frame_nv_sort(); #endif /* SPDYLAY_FRAME_TEST_H */