Added SETTINGS frame and its pack/unpack functions.

This commit is contained in:
Tatsuhiro Tsujikawa 2012-02-01 00:26:26 +09:00
parent 49096387c3
commit 0b75800c23
6 changed files with 197 additions and 0 deletions

View File

@ -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;

View File

@ -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;
}

View File

@ -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 */

View File

@ -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();

View File

@ -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];

View File

@ -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 */