From 9c8270436ff64c52c439312d4b7b77e9087cf556 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 24 Jan 2012 22:02:24 +0900 Subject: [PATCH] Added header deflate/inflate using zlib. Added send/recv frame. --- configure.ac | 5 + lib/Makefile.am | 6 +- lib/includes/spdylay/spdylay.h | 45 ++- lib/spdylay_buffer.c | 15 + lib/spdylay_buffer.h | 7 + lib/spdylay_frame.c | 395 +++++++++++++++++++++++++++ lib/spdylay_frame.h | 154 +++++++++++ lib/spdylay_helper.c | 54 ++++ lib/spdylay_helper.h | 58 ++++ lib/spdylay_pq.c | 5 + lib/spdylay_pq.h | 1 + lib/spdylay_session.c | 482 +++++++++++++++++++++++++++++++++ lib/spdylay_session.h | 105 +++++++ lib/spdylay_stream.c | 33 +++ lib/spdylay_stream.h | 42 +++ lib/spdylay_zlib.c | 141 ++++++++++ lib/spdylay_zlib.h | 57 ++++ tests/Makefile.am | 6 +- tests/main.c | 11 +- tests/spdylay_frame_test.c | 50 ++++ tests/spdylay_frame_test.h | 30 ++ tests/spdylay_session_test.c | 175 ++++++++++++ tests/spdylay_session_test.h | 31 +++ tests/spdylay_zlib_test.c | 70 +++++ tests/spdylay_zlib_test.h | 30 ++ 25 files changed, 2000 insertions(+), 8 deletions(-) create mode 100644 lib/spdylay_frame.c create mode 100644 lib/spdylay_frame.h create mode 100644 lib/spdylay_helper.c create mode 100644 lib/spdylay_helper.h create mode 100644 lib/spdylay_session.c create mode 100644 lib/spdylay_session.h create mode 100644 lib/spdylay_stream.c create mode 100644 lib/spdylay_stream.h create mode 100644 lib/spdylay_zlib.c create mode 100644 lib/spdylay_zlib.h create mode 100644 tests/spdylay_frame_test.c create mode 100644 tests/spdylay_frame_test.h create mode 100644 tests/spdylay_session_test.c create mode 100644 tests/spdylay_session_test.h create mode 100644 tests/spdylay_zlib_test.c create mode 100644 tests/spdylay_zlib_test.h diff --git a/configure.ac b/configure.ac index 6028cb3..97ebbc1 100644 --- a/configure.ac +++ b/configure.ac @@ -40,8 +40,13 @@ AM_PROG_LIBTOOL AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_MAKE_SET +PKG_PROG_PKG_CONFIG([0.20]) # Checks for libraries. +PKG_CHECK_MODULES([ZLIB], [zlib >= 1.2.3]) +LIBS=$ZLIB_LIBS $LIBS +CFLAGS=$CFLAGS $ZLIB_CFLAGS + AC_CHECK_LIB([cunit], [CU_initialize_registry], [have_cunit=yes], [have_cunit=no]) AM_CONDITIONAL([HAVE_CUNIT], [ test "x${have_cunit}" = "xyes" ]) diff --git a/lib/Makefile.am b/lib/Makefile.am index 56f4df8..d57c46f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -32,10 +32,12 @@ DISTCLEANFILES = $(pkgconfig_DATA) lib_LTLIBRARIES = libspdylay.la OBJECTS = spdylay_pq.c spdylay_map.c spdylay_queue.c \ - spdylay_buffer.c + spdylay_buffer.c spdylay_frame.c spdylay_zlib.c \ + spdylay_session.c spdylay_helper.c spdylay_stream.c HFILES = spdylay_pq.h spdylay_int.h spdylay_map.h spdylay_queue.h \ - spdylay_buffer.h + spdylay_buffer.h spdylay_frame.h spdylay_zlib.h \ + spdylay_session.h spdylay_helper.h spdylay_stream.h spdylay_int.h libspdylay_la_SOURCES = $(HFILES) $(OBJECTS) libspdylay_la_LDFLAGS = -no-undefined \ diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index cdc7130..ef0434d 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -32,10 +32,49 @@ extern "C" { #include #include -enum spdylay_error { +typedef enum { SPDYLAY_ERR_NOMEM = -500, - SPDYLAY_ERR_INVALID_ARGUMENT = -501 -}; + SPDYLAY_ERR_INVALID_ARGUMENT = -501, + SPDYLAY_ERR_ZLIB = -502, + SPDYLAY_ERR_ZLIB_BUF = -503, + SPDYLAY_ERR_WOULDBLOCK = -504, + SPDYLAY_ERR_PROTO = -505, + SPDYLAY_ERR_CALLBACK_FAILURE = -505, +} spdylay_error; + +typedef enum { + SPDYLAY_MSG_MORE +} spdylay_io_flag; + +typedef ssize_t (*spdylay_send_callback) +(const uint8_t *data, size_t length, int flags, void *user_data); + +typedef ssize_t (*spdylay_recv_callback) +(uint8_t *buf, size_t length, int flags, void *user_data); + +typedef struct { + spdylay_send_callback send_callback; + spdylay_recv_callback recv_callback; +} spdylay_session_callbacks; + +struct spdylay_session; +typedef struct spdylay_session spdylay_session; + +int spdylay_session_client_init(spdylay_session **session_ptr, + const spdylay_session_callbacks *callbacks, + void *user_data); + +void spdylay_session_free(struct spdylay_session *session); + +int spdylay_session_send(spdylay_session *session); + +int spdylay_session_recv(spdylay_session *session); + +int spdylay_session_want_read(spdylay_session *session); + +int spdylay_session_want_write(spdylay_session *session); + +int spdylay_req_submit(spdylay_session *session, const char *path); #ifdef __cplusplus } diff --git a/lib/spdylay_buffer.c b/lib/spdylay_buffer.c index b62c18c..6743d8b 100644 --- a/lib/spdylay_buffer.c +++ b/lib/spdylay_buffer.c @@ -122,3 +122,18 @@ void spdylay_buffer_pop(spdylay_buffer *buffer) spdylay_queue_pop(&buffer->q); } } + +size_t spdylay_buffer_capacity(spdylay_buffer *buffer) +{ + return buffer->capacity; +} + +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); + buf += len; + spdylay_buffer_pop(buffer); + } +} diff --git a/lib/spdylay_buffer.h b/lib/spdylay_buffer.h index 3007679..0d4327a 100644 --- a/lib/spdylay_buffer.h +++ b/lib/spdylay_buffer.h @@ -75,4 +75,11 @@ uint8_t* spdylay_buffer_front_data(spdylay_buffer *buffer); 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 + spdylay_buffer_length(buffer) bytes long. */ +void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf); + #endif /* SPDYLAY_BUFFER_H */ diff --git a/lib/spdylay_frame.c b/lib/spdylay_frame.c new file mode 100644 index 0000000..84c246e --- /dev/null +++ b/lib/spdylay_frame.c @@ -0,0 +1,395 @@ +/* + * 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_frame.h" + +#include +#include +#include +#include + +#include "spdylay_helper.h" + +static uint8_t spdylay_unpack_pri(const uint8_t *data) +{ + return (data[0] >> 6) & 0x3; +} + +static uint8_t* spdylay_pack_str(uint8_t *buf, const char *str, size_t len) +{ + spdylay_put_uint16be(buf, len); + buf += 2; + memcpy(buf, str, len); + return buf+len; +} + +static void spdylay_frame_pack_ctrl_hd(uint8_t* buf, const spdylay_ctrl_hd *hd) +{ + spdylay_put_uint16be(&buf[0], hd->version); + buf[0] |= 1 << 7; + spdylay_put_uint16be(&buf[2], hd->type); + spdylay_put_uint32be(&buf[4], hd->length); + printf("hd->length=%d\n", hd->length); + buf[4] = hd->flags; + printf("hd->flags=%d\n", hd->flags); +} + +static void spdylay_frame_unpack_ctrl_hd(spdylay_ctrl_hd *hd, + const uint8_t* buf) +{ + hd->version = spdylay_get_uint16(buf) & 0x7fff; + hd->type = spdylay_get_uint16(buf+2); + hd->flags = buf[4]; + hd->length = spdylay_get_uint32(buf+5) & 0xffffff; +} + +static ssize_t spdylay_frame_alloc_pack_nv(uint8_t **buf_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); + ssize_t framelen; + spdylay_frame_pack_nv(nvbuf, nv); + framelen = spdylay_zlib_deflate_hd(deflater, framebuf+18, maxframelen-18, + nvbuf, nvbuflen); + free(nvbuf); + if(framelen < 0) { + free(framebuf); + return framelen; + } + framelen += 18; + *buf_ptr = framebuf; + return framelen; +} + +int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen) +{ + int r = 0; + uint16_t n; + size_t off; + size_t nv_max = 126; + int i, j, k; + char *name = NULL, *val = NULL; + if(inlen < 2) { + return SPDYLAY_ERR_PROTO; + } + memcpy(&n, in, sizeof(uint16_t)); + n = ntohs(n); + if(n > nv_max) { + nv_max = n; + } + *nv_ptr = malloc(nv_max*sizeof(char*)+1); + if(*nv_ptr == NULL) { + return SPDYLAY_ERR_NOMEM; + } + off = 2; + for(i = 0, j = 0; i < n; ++i) { + uint16_t nlen, vlen; + size_t last, len; + if(off+2 > inlen) { + r = SPDYLAY_ERR_PROTO; + break; + } + /* For NULL delimited values, they are splitted with each name */ + memcpy(&nlen, &in[off], sizeof(uint16_t)); + nlen = ntohs(nlen); + off += 2; + if(off+nlen > inlen || nlen == 0) { + r = SPDYLAY_ERR_PROTO; + break; + } + name = malloc(nlen+1); + if(name == NULL) { + r = SPDYLAY_ERR_NOMEM; + break; + } + memcpy(name, &in[off], nlen); + name[nlen] = '\0'; + off += nlen; + if(off+2 > inlen) { + r = SPDYLAY_ERR_PROTO; + break; + } + memcpy(&vlen, &in[off], sizeof(uint16_t)); + vlen = ntohs(vlen); + off += 2; + if(off+vlen > inlen) { + r = SPDYLAY_ERR_PROTO; + break; + } + for(k = 0, last = off; k < vlen; ++k) { + size_t len; + if(in[off+k] == '\0') { + len = off+k-last; + if(len == 0) { + r = SPDYLAY_ERR_PROTO; + break; + } + val = malloc(len+1); + if(val == NULL) { + r = SPDYLAY_ERR_NOMEM; + break; + } + memcpy(val, &in[last], len); + val[len] = '\0'; + if(j >= nv_max) { + char **nnv; + nv_max *= 2; + nnv = realloc(*nv_ptr, nv_max+1); + if(nnv == NULL) { + r = SPDYLAY_ERR_NOMEM; + break; + } else { + *nv_ptr = nnv; + } + } + (*nv_ptr)[j] = strdup(name); + (*nv_ptr)[j+1] = val; + val = NULL; + last = off+k+1; + j += 2; + } + } + if(r != 0) { + break; + } + len = vlen-(last-off); + if(len == 0) { + r = SPDYLAY_ERR_PROTO; + break; + } + val = malloc(len+1); + if(val == NULL) { + free(name); + r = SPDYLAY_ERR_NOMEM; + break; + } + memcpy(val, &in[last], len); + val[len] = '\0'; + if(j >= nv_max) { + char **nnv; + nv_max *= 2; + nnv = realloc(*nv_ptr, nv_max+1); + if(nnv == NULL) { + r = SPDYLAY_ERR_NOMEM; + break; + } else { + *nv_ptr = nnv; + } + } + (*nv_ptr)[j] = name; + (*nv_ptr)[j+1] = val; + name = val = NULL; + j += 2; + off += vlen; + } + free(name); + free(val); + if(r == 0) { + (*nv_ptr)[j] = NULL; + } else { + for(i = 0; i < j; ++i) { + free((*nv_ptr)[i]); + } + free(*nv_ptr); + } + return r; +} + +static int spdylay_frame_alloc_unpack_nv(char ***nv_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; + } else { + uint8_t *buf = malloc(r); + if(buf == NULL) { + spdylay_buffer_free(&outbuffer); + return SPDYLAY_ERR_NOMEM; + } + spdylay_buffer_serialize(&outbuffer, buf); + spdylay_buffer_free(&outbuffer); + r = spdylay_frame_unpack_nv(nv_ptr, buf, r); + free(buf); + return r; + } +} + +size_t spdylay_frame_count_nv_space(char **nv) +{ + size_t sum = 2; + int i; + const char *prev = ""; + size_t prevlen = 0; + for(i = 0; nv[i]; i += 2) { + const char *key = nv[i]; + const char *val = nv[i+1]; + size_t keylen = strlen(key); + size_t vallen = strlen(val); + if(prevlen == keylen && memcmp(prev, key, keylen) == 0) { + /* Join previous value, with NULL character */ + sum += vallen+1; + } else { + prev = key; + /* SPDY NV header does not include terminating NULL byte */ + sum += keylen+vallen+4; + } + } + return sum; +} + +ssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv) +{ + int i; + uint8_t *bufp = buf+2; + uint16_t num_nv = 0; + /* TODO Join values with same keys, using '\0' as a delimiter */ + const char *prev = ""; + uint8_t *prev_vallen_buf = NULL; + uint16_t prev_vallen = 0; + for(i = 0; nv[i]; i += 2) { + const char *key = nv[i]; + const char *val = nv[i+1]; + size_t keylen = strlen(key); + size_t vallen = strlen(val); + if(strcmp(prev, key) == 0) { + prev_vallen += vallen+1; + spdylay_put_uint16be(prev_vallen_buf, prev_vallen); + *bufp = '\0'; + ++bufp; + memcpy(bufp, val, vallen); + bufp += vallen; + } else { + ++num_nv; + bufp = spdylay_pack_str(bufp, key, keylen); + prev = key; + prev_vallen_buf = bufp; + prev_vallen = vallen; + bufp = spdylay_pack_str(bufp, val, vallen); + } + } + spdylay_put_uint16be(buf, num_nv); + return bufp-buf; +} + +int spdylay_frame_is_ctrl_frame(uint8_t first_byte) +{ + return first_byte & 0x80; +} + +void spdylay_frame_nv_free(char **nv) +{ + int i; + for(i = 0; nv[i]; i += 2) { + free(nv[i]); + free(nv[i+1]); + } +} + +void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, uint8_t flags, + int32_t stream_id, int32_t assoc_stream_id, + uint8_t pri, char **nv) +{ + memset(frame, 0, sizeof(spdylay_syn_stream)); + frame->hd.version = 2; + frame->hd.type = SPDYLAY_SYN_STREAM; + frame->hd.flags = flags; + frame->stream_id = stream_id; + frame->assoc_stream_id = assoc_stream_id; + frame->pri = pri; + frame->nv = nv; +} + +void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame) +{ + spdylay_frame_nv_free(frame->nv); + free(frame->nv); +} + +void spdylay_frame_syn_reply_free(spdylay_syn_reply *frame) +{ + spdylay_frame_nv_free(frame->nv); + free(frame->nv); +} + +ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, + spdylay_syn_stream *frame, + spdylay_zlib *deflater) +{ + uint8_t *framebuf = NULL; + size_t framelen; + framelen = spdylay_frame_alloc_pack_nv(&framebuf, frame->nv, 18, deflater); + if(framelen < 0) { + return framelen; + } + frame->hd.length = framelen-8; + memset(framebuf, 0, 18); + /* 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; + return framelen; +} + +int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater) +{ + int r; + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + frame->stream_id = spdylay_get_uint32(payload); + frame->assoc_stream_id = spdylay_get_uint32(payload+4); + frame->pri = spdylay_unpack_pri(payload+8); + r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+10, payloadlen-10, + inflater); + return r; +} + +int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater) +{ + int r; + spdylay_frame_unpack_ctrl_hd(&frame->hd, head); + frame->stream_id = spdylay_get_uint32(payload); + r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+6, payloadlen-6, + inflater); + return r; +} + diff --git a/lib/spdylay_frame.h b/lib/spdylay_frame.h new file mode 100644 index 0000000..5b1a9f8 --- /dev/null +++ b/lib/spdylay_frame.h @@ -0,0 +1,154 @@ +/* + * 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_FRAME_H +#define SPDYLAY_FRAME_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include "spdylay_zlib.h" +#include "spdylay_buffer.h" + +typedef enum { + SPDYLAY_SYN_STREAM = 1, + SPDYLAY_SYN_REPLY = 2, + SPDYLAY_RST_STREAM = 3, + SPDYLAY_SETTINGS = 4, + SPDYLAY_NOOP = 5, + SPDYLAY_PING = 6, + SPDYLAY_GOAWAY = 7, +} spdylay_frame_type; + +typedef enum { + SPDYLAY_FLAG_FIN = 1 +} spdylay_flag; + +typedef struct { + uint16_t version; + uint16_t type; + uint8_t flags; + int32_t length; +} spdylay_ctrl_hd; + +typedef struct { + spdylay_ctrl_hd hd; + int32_t stream_id; + int32_t assoc_stream_id; + uint8_t pri; + char **nv; +} spdylay_syn_stream; + +typedef struct { + spdylay_ctrl_hd hd; + int32_t stream_id; + char **nv; +} spdylay_syn_reply; + +typedef union { + spdylay_syn_stream syn_stream; +} spdylay_frame; + +/* + * 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 + * code. frame->hd.length is assigned after length is determined + * during packing process. + */ +ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr, + spdylay_syn_stream *frame, + 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. + * This function returns 0 if it succeeds or negative error code. + */ +int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater); + + + +/* + * Unpacks SYN_REPLY frame byte sequence into |frame|. This function + * returns 0 if it succeeds or negative error code. + */ +int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame, + const uint8_t *head, size_t headlen, + const uint8_t *payload, size_t payloadlen, + spdylay_zlib *inflater); + +/* + * Returns number of bytes to pack name/value pairs |nv|. This + * function expects |nv| is sorted in ascending order of key. This + * function can handles duplicate keys and concatenation of thier + * values with '\0'. + */ +size_t spdylay_frame_count_nv_space(char **nv); + +/* + * Packs name/value pairs in |nv| in |buf|. |buf| must have at least + * spdylay_frame_count_nv_space(nv) bytes. + */ +ssize_t spdylay_frame_pack_nv(uint8_t *buf, char **nv); + +/* + * 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|. This function + * returns 0 if it succeeds, or negative error code. + */ +int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen); + +/* + * Initializes SYN_STREAM frame |frame| with given values. |frame| + * takes ownership of |nv|, so caller must not free it. If stream_id + * is not assigned yet, it must be 0. + */ +void spdylay_frame_syn_stream_init(spdylay_syn_stream *frame, uint8_t flags, + int32_t stream_id, int32_t assoc_stream_id, + uint8_t pri, char **nv); + +void spdylay_frame_syn_stream_free(spdylay_syn_stream *frame); + +void spdylay_frame_syn_reply_free(spdylay_syn_reply *frame); + +/* + * Returns 1 if the first byte of this frame indicates it is a control + * frame. + */ +int spdylay_frame_is_ctrl_frame(uint8_t first_byte); + +/* + * Deallocates memory of key/value pairs in |nv|. + */ +void spdylay_frame_nv_free(char **nv); + +#endif /* SPDYLAY_FRAME_H */ diff --git a/lib/spdylay_helper.c b/lib/spdylay_helper.c new file mode 100644 index 0000000..6418aa3 --- /dev/null +++ b/lib/spdylay_helper.c @@ -0,0 +1,54 @@ +/* + * 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_helper.h" + +#include +#include + +void spdylay_put_uint16be(uint8_t *buf, uint16_t n) +{ + uint16_t x = htons(n); + memcpy(buf, &x, sizeof(uint16_t)); +} + +void spdylay_put_uint32be(uint8_t *buf, uint32_t n) +{ + uint32_t x = htonl(n); + memcpy(buf, &x, sizeof(uint32_t)); +} + +uint16_t spdylay_get_uint16(const uint8_t *data) +{ + uint16_t n; + memcpy(&n, data, sizeof(uint16_t)); + return ntohs(n); +} + +uint32_t spdylay_get_uint32(const uint8_t *data) +{ + uint32_t n; + memcpy(&n, data, sizeof(uint32_t)); + return ntohl(n); +} diff --git a/lib/spdylay_helper.h b/lib/spdylay_helper.h new file mode 100644 index 0000000..6b547f3 --- /dev/null +++ b/lib/spdylay_helper.h @@ -0,0 +1,58 @@ +/* + * 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_HELPER_H +#define SPDYLAY_HELPER_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +/* + * Copies 2 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void spdylay_put_uint16be(uint8_t *buf, uint16_t n); + +/* + * Copies 4 byte unsigned integer |n| in host byte order to |buf| in + * network byte order. + */ +void spdylay_put_uint32be(uint8_t *buf, uint32_t n); + +/* + * Retrieves 2 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint16_t spdylay_get_uint16(const uint8_t *data); + +/* + * Retrieves 4 byte unsigned integer stored in |data| in network byte + * order and returns it in host byte order. + */ +uint32_t spdylay_get_uint32(const uint8_t *data); + +#endif /* SPDYLAY_HELPER_H */ diff --git a/lib/spdylay_pq.c b/lib/spdylay_pq.c index 79f1cbd..193f956 100644 --- a/lib/spdylay_pq.c +++ b/lib/spdylay_pq.c @@ -115,3 +115,8 @@ void spdylay_pq_pop(spdylay_pq *pq) bubble_down(pq, 0); } } + +int spdylay_pq_empty(spdylay_pq *pq) +{ + return pq->length == 0; +} diff --git a/lib/spdylay_pq.h b/lib/spdylay_pq.h index 6b71ce9..7009fa4 100644 --- a/lib/spdylay_pq.h +++ b/lib/spdylay_pq.h @@ -51,5 +51,6 @@ void* spdylay_pq_top(spdylay_pq *pq); void spdylay_pq_pop(spdylay_pq *pq); +int spdylay_pq_empty(spdylay_pq *pq); #endif /* SPDYLAY_PQ_H */ diff --git a/lib/spdylay_session.c b/lib/spdylay_session.c new file mode 100644 index 0000000..5eebe8b --- /dev/null +++ b/lib/spdylay_session.c @@ -0,0 +1,482 @@ +/* + * 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_session.h" + +#include +#include +#include +#include +#include + +#include "spdylay_stream.h" +#include "spdylay_helper.h" + +int spdylay_outbound_item_compar(const void *lhsx, const void *rhsx) +{ + const spdylay_outbound_item *lhs, *rhs; + lhs = (const spdylay_outbound_item*)lhsx; + rhs = (const spdylay_outbound_item*)rhsx; + return lhs->pri-rhs->pri; +} + +int spdylay_session_client_init(spdylay_session **session_ptr, + const spdylay_session_callbacks *callbacks, + void *user_data) +{ + int r; + *session_ptr = malloc(sizeof(spdylay_session)); + if(*session_ptr == NULL) { + return SPDYLAY_ERR_NOMEM; + } + memset(*session_ptr, 0, sizeof(spdylay_session)); + (*session_ptr)->next_stream_id = 1; + (*session_ptr)->last_accepted_stream_id = 0; + + r = spdylay_zlib_deflate_hd_init(&(*session_ptr)->hd_deflater); + if(r != 0) { + free(*session_ptr); + return r; + } + r = spdylay_zlib_inflate_hd_init(&(*session_ptr)->hd_inflater); + if(r != 0) { + spdylay_zlib_deflate_free(&(*session_ptr)->hd_deflater); + free(*session_ptr); + return r; + } + r = spdylay_map_init(&(*session_ptr)->streams); + if(r != 0) { + spdylay_zlib_inflate_free(&(*session_ptr)->hd_inflater); + spdylay_zlib_deflate_free(&(*session_ptr)->hd_deflater); + free(*session_ptr); + return r; + } + r = spdylay_pq_init(&(*session_ptr)->ob_pq, spdylay_outbound_item_compar); + if(r != 0) { + 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)->callbacks = *callbacks; + (*session_ptr)->user_data = user_data; + + (*session_ptr)->ibuf.mark = (*session_ptr)->ibuf.buf; + (*session_ptr)->ibuf.limit = (*session_ptr)->ibuf.buf; + + (*session_ptr)->iframe.state = SPDYLAY_RECV_HEAD; + return 0; +} + +static void spdylay_free_streams(key_type key, void *val) +{ + spdylay_stream_free((spdylay_stream*)val); + free(val); +} + +static void spdylay_outbound_item_free(spdylay_outbound_item *item) +{ + if(item == NULL) { + return; + } + switch(item->frame_type) { + case SPDYLAY_SYN_STREAM: + spdylay_frame_syn_stream_free(&item->frame->syn_stream); + break; + } + free(item->frame); +} + +void spdylay_session_free(spdylay_session *session) +{ + spdylay_map_each(&session->streams, spdylay_free_streams); + spdylay_map_free(&session->streams); + while(!spdylay_pq_empty(&session->ob_pq)) { + spdylay_outbound_item *item = (spdylay_outbound_item*) + spdylay_pq_top(&session->ob_pq); + spdylay_outbound_item_free(item); + free(item); + spdylay_pq_pop(&session->ob_pq); + } + spdylay_pq_free(&session->ob_pq); + spdylay_zlib_deflate_free(&session->hd_deflater); + spdylay_zlib_inflate_free(&session->hd_inflater); + free(session->iframe.buf); + free(session); +} + +int spdylay_session_add_frame(spdylay_session *session, + spdylay_frame_type frame_type, + spdylay_frame *frame) +{ + int r; + spdylay_outbound_item *item; + item = malloc(sizeof(spdylay_outbound_item)); + if(item == NULL) { + return SPDYLAY_ERR_NOMEM; + } + item->frame_type = frame_type; + item->frame = frame; + /* TODO Add pri field to SYN_REPLY, DATA frame which copies + corresponding SYN_STREAM pri. PING frame always pri = 0 + (highest) */ + switch(frame_type) { + case SPDYLAY_SYN_STREAM: + item->pri = 4-frame->syn_stream.pri; + break; + default: + item->pri = 4; + }; + r = spdylay_pq_push(&session->ob_pq, item); + if(r != 0) { + free(item); + return r; + } + return 0; +} + +int spdylay_session_open_stream(spdylay_session *session, int32_t stream_id) +{ + int r; + spdylay_stream *stream = malloc(sizeof(spdylay_stream)); + if(stream == NULL) { + return SPDYLAY_ERR_NOMEM; + } + spdylay_stream_init(stream, stream_id); + r = spdylay_map_insert(&session->streams, stream_id, stream); + if(r != 0) { + free(stream); + } + return r; +} + +ssize_t spdylay_session_prep_frame(spdylay_session *session, + spdylay_outbound_item *item, + uint8_t **framebuf_ptr) +{ + uint8_t *framebuf; + ssize_t framebuflen; + int r; + switch(item->frame_type) { + case SPDYLAY_SYN_STREAM: { + item->frame->syn_stream.stream_id = session->next_stream_id; + session->next_stream_id += 2; + framebuflen = spdylay_frame_pack_syn_stream(&framebuf, + &item->frame->syn_stream, + &session->hd_deflater); + if(framebuflen < 0) { + return framebuflen; + } + printf("packed %d bytes\n", framebuflen); + r = spdylay_session_open_stream(session, item->frame->syn_stream.stream_id); + if(r != 0) { + free(framebuf); + return r; + } + *framebuf_ptr = framebuf; + break; + } + default: + framebuflen = SPDYLAY_ERR_INVALID_ARGUMENT; + } + return framebuflen; +} + +static void spdylay_active_outbound_item_reset +(spdylay_active_outbound_item *aob) +{ + spdylay_outbound_item_free(aob->item); + free(aob->item); + free(aob->framebuf); + memset(aob, 0, sizeof(spdylay_active_outbound_item)); +} + +int spdylay_session_send(spdylay_session *session) +{ + printf("session_send\n"); + while(session->aob.item || !spdylay_pq_empty(&session->ob_pq)) { + const uint8_t *data; + size_t datalen; + ssize_t sentlen; + if(session->aob.item == NULL) { + spdylay_outbound_item *item = spdylay_pq_top(&session->ob_pq); + uint8_t *framebuf; + ssize_t framebuflen; + spdylay_pq_pop(&session->ob_pq); + /* TODO Get or validate stream id here */ + framebuflen = spdylay_session_prep_frame(session, item, &framebuf); + if(framebuflen < 0) { + /* TODO Call error callback? */ + spdylay_outbound_item_free(item); + free(item); + continue;; + } + session->aob.item = item; + session->aob.framebuf = framebuf; + session->aob.framebuflen = framebuflen; + } + data = session->aob.framebuf + session->aob.framebufoff; + datalen = session->aob.framebuflen - session->aob.framebufoff; + sentlen = session->callbacks.send_callback(data, datalen, 0, + session->user_data); + if(sentlen < 0) { + if(sentlen == SPDYLAY_ERR_WOULDBLOCK) { + return 0; + } else { + return sentlen; + } + } else { + printf("sent %d bytes\n", sentlen); + session->aob.framebufoff += sentlen; + if(session->aob.framebufoff == session->aob.framebuflen) { + /* Frame has completely sent */ + spdylay_active_outbound_item_reset(&session->aob); + /* TODO If frame is data frame, we need to sent all chunk of + data.*/ + } else { + /* partial write */ + break; + } + } + } + return 0; +} + +static void spdylay_inbound_buffer_shift(spdylay_inbound_buffer *ibuf) +{ + ptrdiff_t len = ibuf->limit-ibuf->mark; + memmove(ibuf->buf, ibuf->mark, len); + ibuf->limit = ibuf->buf+len; + ibuf->mark = ibuf->buf; +} + +static ssize_t spdylay_recv(spdylay_session *session) +{ + ssize_t r; + size_t recv_max; + if(session->ibuf.mark != session->ibuf.buf) { + spdylay_inbound_buffer_shift(&session->ibuf); + } + recv_max = session->ibuf.buf+sizeof(session->ibuf.buf)-session->ibuf.limit; + r = session->callbacks.recv_callback + (session->ibuf.limit, recv_max, 0, session->user_data); + if(r > 0) { + if(r > recv_max) { + return SPDYLAY_ERR_CALLBACK_FAILURE; + } else { + session->ibuf.limit += r; + } + } else if(r < 0) { + if(r != SPDYLAY_ERR_WOULDBLOCK) { + r = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + return r; +} + +static size_t spdylay_inbound_buffer_avail(spdylay_inbound_buffer *ibuf) +{ + return ibuf->limit-ibuf->mark; +} + +static void spdylay_inbound_frame_reset(spdylay_inbound_frame *iframe) +{ + iframe->state = SPDYLAY_RECV_HEAD; + free(iframe->buf); + iframe->buf = NULL; + iframe->len = iframe->off = 0; + iframe->ign = 0; +} + +static void spdylay_debug_print_nv(char **nv) +{ + int i; + for(i = 0; nv[i]; i += 2) { + printf("%s: %s\n", nv[i], nv[i+1]); + } +} + +int spdylay_session_process_ctrl_frame(spdylay_session *session) +{ + int r; + uint16_t type; + memcpy(&type, &session->iframe.headbuf[2], sizeof(uint16_t)); + type = ntohs(type); + switch(type) { + case SPDYLAY_SYN_STREAM: { + spdylay_syn_stream frame; + printf("SYN_STREAM\n"); + r = spdylay_frame_unpack_syn_stream(&frame, session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len, + &session->hd_inflater); + if(r == 0) { + spdylay_debug_print_nv(frame.nv); + spdylay_frame_syn_stream_free(&frame); + } + break; + } + case SPDYLAY_SYN_REPLY: { + spdylay_syn_reply frame; + printf("SYN_REPLY\n"); + r = spdylay_frame_unpack_syn_reply(&frame, session->iframe.headbuf, + sizeof(session->iframe.headbuf), + session->iframe.buf, + session->iframe.len, + &session->hd_inflater); + if(r == 0) { + spdylay_debug_print_nv(frame.nv); + spdylay_frame_syn_reply_free(&frame); + } + break; + } + default: + /* ignore */ + printf("Received control frame type %x\n", type); + } + return 0; +} + +int spdylay_session_process_data_frame(spdylay_session *session) +{ + printf("Received data frame, stream_id %d, %zu bytes, fin=%d\n", + spdylay_get_uint32(session->iframe.headbuf), + session->iframe.len, + session->iframe.headbuf[4] & SPDYLAY_FLAG_FIN); + return 0; +} + +int spdylay_session_recv(spdylay_session *session) +{ + printf("session_recv\n"); + while(1) { + ssize_t r; + if(session->iframe.state == SPDYLAY_RECV_HEAD) { + uint32_t payloadlen; + if(spdylay_inbound_buffer_avail(&session->ibuf) < SPDYLAY_HEAD_LEN) { + r = spdylay_recv(session); + printf("r=%d\n", r); + /* TODO handle EOF */ + if(r < 0) { + if(r == SPDYLAY_ERR_WOULDBLOCK) { + return 0; + } else { + return r; + } + } + printf("Recved %d bytes\n", r); + if(spdylay_inbound_buffer_avail(&session->ibuf) < SPDYLAY_HEAD_LEN) { + return 0; + } + } + session->iframe.state = SPDYLAY_RECV_PAYLOAD; + payloadlen = spdylay_get_uint32(&session->ibuf.mark[4]) & 0xffffff; + memcpy(session->iframe.headbuf, session->ibuf.mark, SPDYLAY_HEAD_LEN); + session->ibuf.mark += SPDYLAY_HEAD_LEN; + if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + /* control frame */ + session->iframe.len = payloadlen; + session->iframe.buf = malloc(session->iframe.len); + if(session->iframe.buf == NULL) { + return SPDYLAY_ERR_NOMEM; + } + session->iframe.off = 0; + } else { + int32_t stream_id; + /* data frame */ + /* For data frame, We dont' buffer data. Instead, just pass + received data to callback function. */ + stream_id = spdylay_get_uint32(session->iframe.headbuf) & 0x7fffffff; + /* TODO validate stream id here */ + session->iframe.len = payloadlen; + session->iframe.off = 0; + } + } + if(session->iframe.state == SPDYLAY_RECV_PAYLOAD) { + size_t rempayloadlen = session->iframe.len - session->iframe.off; + size_t bufavail, readlen; + if(spdylay_inbound_buffer_avail(&session->ibuf) == 0 && + rempayloadlen > 0) { + r = spdylay_recv(session); + if(r <= 0) { + if(r == SPDYLAY_ERR_WOULDBLOCK) { + return 0; + } else { + return r; + } + } + } + bufavail = spdylay_inbound_buffer_avail(&session->ibuf); + readlen = bufavail < rempayloadlen ? bufavail : rempayloadlen; + if(session->iframe.buf != NULL) { + memcpy(session->iframe.buf, session->ibuf.mark, readlen); + } + session->iframe.off += readlen; + session->ibuf.mark += readlen; + if(session->iframe.len == session->iframe.off) { + if(spdylay_frame_is_ctrl_frame(session->iframe.headbuf[0])) { + spdylay_session_process_ctrl_frame(session); + } else { + spdylay_session_process_data_frame(session); + } + spdylay_inbound_frame_reset(&session->iframe); + } + } + } + return 0; +} + +int spdylay_session_want_read(spdylay_session *session) +{ + return 1; +} + +int spdylay_session_want_write(spdylay_session *session) +{ + return session->aob.item != NULL || !spdylay_pq_empty(&session->ob_pq); +} + +int spdylay_req_submit(spdylay_session *session, const char *path) +{ + int r; + spdylay_frame *frame; + char **nv; + frame = malloc(sizeof(spdylay_frame)); + nv = malloc(9*sizeof(char*)); + nv[0] = strdup("method"); + nv[1] = strdup("GET"); + nv[2] = strdup("scheme"); + nv[3] = strdup("https"); + nv[4] = strdup("url"); + nv[5] = strdup(path); + nv[6] = strdup("version"); + nv[7] = strdup("HTTP/1.1"); + nv[8] = NULL; + spdylay_frame_syn_stream_init(&frame->syn_stream, + SPDYLAY_FLAG_FIN, 0, 0, 0, nv); + r = spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame); + assert(r == 0); +} diff --git a/lib/spdylay_session.h b/lib/spdylay_session.h new file mode 100644 index 0000000..fa024cc --- /dev/null +++ b/lib/spdylay_session.h @@ -0,0 +1,105 @@ +/* + * 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_SESSION_H +#define SPDYLAY_SESSION_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include +#include "spdylay_pq.h" +#include "spdylay_map.h" +#include "spdylay_frame.h" +#include "spdylay_zlib.h" + +typedef struct { + spdylay_frame_type frame_type; + spdylay_frame *frame; + int pri; +} spdylay_outbound_item; + +typedef struct { + spdylay_outbound_item *item; + uint8_t *framebuf; + size_t framebuflen; + size_t framebufoff; +} spdylay_active_outbound_item; + +typedef struct { + uint8_t buf[4096]; + uint8_t *mark; + uint8_t *limit; +} spdylay_inbound_buffer; + +typedef enum { + SPDYLAY_RECV_HEAD, + SPDYLAY_RECV_PAYLOAD +} spdylay_inbound_state; + +#define SPDYLAY_HEAD_LEN 8 + +typedef struct { + spdylay_inbound_state state; + uint8_t headbuf[SPDYLAY_HEAD_LEN]; + /* NULL if inbound frame is data frame */ + uint8_t *buf; + /* length in Length field */ + size_t len; + size_t off; + uint8_t ign; +} spdylay_inbound_frame; + +typedef struct spdylay_session { + uint8_t server; + int32_t next_stream_id; + int32_t last_accepted_stream_id; + + spdylay_map /* */ streams; + spdylay_pq /* */ ob_pq; + + spdylay_active_outbound_item aob; + + spdylay_inbound_buffer ibuf; + spdylay_inbound_frame iframe; + + spdylay_zlib hd_deflater; + spdylay_zlib hd_inflater; + + spdylay_session_callbacks callbacks; + void *user_data; +} spdylay_session; + +/* TODO stream timeout etc */ + +int spdylay_session_add_frame(spdylay_session *session, + spdylay_frame_type frame_type, + spdylay_frame *frame); + +int spdylay_session_open_stream(spdylay_session *session, int32_t stream_id); + +int spdylay_session_close_stream(spdylay_session *session, int32_t stream_id); + +#endif /* SPDYLAY_SESSION_H */ diff --git a/lib/spdylay_stream.c b/lib/spdylay_stream.c new file mode 100644 index 0000000..16e27fd --- /dev/null +++ b/lib/spdylay_stream.c @@ -0,0 +1,33 @@ +/* + * 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_stream.h" + +void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id) +{ + stream->stream_id = stream_id; +} + +void spdylay_stream_free(spdylay_stream *stream) +{} diff --git a/lib/spdylay_stream.h b/lib/spdylay_stream.h new file mode 100644 index 0000000..674cac3 --- /dev/null +++ b/lib/spdylay_stream.h @@ -0,0 +1,42 @@ +/* + * 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_STREAM_H +#define SPDYLAY_STREAM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct { + int32_t stream_id; +} spdylay_stream; + +void spdylay_stream_init(spdylay_stream *stream, int32_t stream_id); + +void spdylay_stream_free(spdylay_stream *stream); + +#endif /* SPDYLAY_STREAM */ diff --git a/lib/spdylay_zlib.c b/lib/spdylay_zlib.c new file mode 100644 index 0000000..006d44d --- /dev/null +++ b/lib/spdylay_zlib.c @@ -0,0 +1,141 @@ +/* + * 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_zlib.h" + +#define COMPRESSION_LEVEL 9 +#define WINDOW_BITS 11 +#define MEM_LEVEL 1 + +static const char hd_dict[] = + "optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" + "languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" + "f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" + "-agent10010120020120220320420520630030130230330430530630740040140240340440" + "5406407408409410411412413414415416417500501502503504505accept-rangesageeta" + "glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" + "ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" + "sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" + "oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" + "ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" + "pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" + "ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" + ".1statusversionurl"; + +int spdylay_zlib_deflate_hd_init(spdylay_zlib *deflater) +{ + deflater->zst.next_in = Z_NULL; + deflater->zst.zalloc = Z_NULL; + deflater->zst.zfree = Z_NULL; + deflater->zst.opaque = Z_NULL; + if(Z_OK != deflateInit2(&deflater->zst, COMPRESSION_LEVEL, Z_DEFLATED, + WINDOW_BITS, MEM_LEVEL, Z_DEFAULT_STRATEGY)) { + return SPDYLAY_ERR_ZLIB; + } + if(Z_OK != deflateSetDictionary(&deflater->zst, (uint8_t*)hd_dict, + sizeof(hd_dict))) { + return SPDYLAY_ERR_ZLIB; + } + return 0; +} + +int spdylay_zlib_inflate_hd_init(spdylay_zlib *inflater) +{ + inflater->zst.next_in = Z_NULL; + inflater->zst.avail_in = 0; + inflater->zst.zalloc = Z_NULL; + inflater->zst.zfree = Z_NULL; + if(Z_OK != inflateInit(&inflater->zst)) { + return SPDYLAY_ERR_ZLIB; + } + return 0; +} + +void spdylay_zlib_deflate_free(spdylay_zlib *zlib) +{ + deflateEnd(&zlib->zst); +} + +void spdylay_zlib_inflate_free(spdylay_zlib *zlib) +{ + inflateEnd(&zlib->zst); +} + +ssize_t spdylay_zlib_deflate_hd(spdylay_zlib *deflater, + uint8_t *out, size_t outlen, + const uint8_t *in, size_t inlen) +{ + int r; + deflater->zst.avail_in = inlen; + deflater->zst.next_in = (uint8_t*)in; + deflater->zst.avail_out = outlen; + deflater->zst.next_out = out; + r = deflate(&deflater->zst, Z_SYNC_FLUSH); + if(r == Z_OK) { + return outlen-deflater->zst.avail_out; + } else { + return SPDYLAY_ERR_ZLIB; + } +} + +size_t spdylay_zlib_deflate_hd_bound(spdylay_zlib *deflater, size_t len) +{ + return deflateBound(&deflater->zst, len); +} + +ssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater, + spdylay_buffer* buf, + const uint8_t *in, size_t inlen) +{ + int r; + inflater->zst.avail_in = inlen; + inflater->zst.next_in = (uint8_t*)in; + while(1) { + if(spdylay_buffer_avail(buf) == 0) { + if((r = spdylay_buffer_alloc(buf)) != 0) { + return r; + } + } + inflater->zst.avail_out = spdylay_buffer_avail(buf); + inflater->zst.next_out = spdylay_buffer_get(buf); + r = inflate(&inflater->zst, Z_NO_FLUSH); + if(r == Z_STREAM_ERROR || r == Z_STREAM_END) { + return SPDYLAY_ERR_ZLIB; + } else if(r == Z_NEED_DICT) { + if(Z_OK != inflateSetDictionary(&inflater->zst, (uint8_t*)hd_dict, + sizeof(hd_dict))) { + return SPDYLAY_ERR_ZLIB; + } + } else { + if(r == Z_OK) { + size_t adv = spdylay_buffer_avail(buf)-inflater->zst.avail_out; + spdylay_buffer_advance(buf, adv); + } + if(inflater->zst.avail_in == 0 && inflater->zst.avail_out > 0) { + break; + } + } + } + return spdylay_buffer_length(buf); +} diff --git a/lib/spdylay_zlib.h b/lib/spdylay_zlib.h new file mode 100644 index 0000000..938b4b9 --- /dev/null +++ b/lib/spdylay_zlib.h @@ -0,0 +1,57 @@ +/* + * 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_ZLIB_H +#define SPDYLAY_ZLIB_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ +#include + +#include "spdylay_buffer.h" + +typedef struct { + z_stream zst; +} spdylay_zlib; + +int spdylay_zlib_deflate_hd_init(spdylay_zlib *deflater); + +int spdylay_zlib_inflate_hd_init(spdylay_zlib *inflater); + +void spdylay_zlib_deflate_free(spdylay_zlib *zlib); + +void spdylay_zlib_inflate_free(spdylay_zlib *zlib); + +size_t spdylay_zlib_deflate_hd_bound(spdylay_zlib *deflater, size_t len); + +ssize_t spdylay_zlib_deflate_hd(spdylay_zlib *deflater, + uint8_t *out, size_t outlen, + const uint8_t *in, size_t inlen); + +ssize_t spdylay_zlib_inflate_hd(spdylay_zlib *inflater, + spdylay_buffer* buf, + const uint8_t *in, size_t inlen); + +#endif /* SPDYLAY_ZLIB_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index b79bb5b..f64fc10 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -26,10 +26,12 @@ if HAVE_CUNIT check_PROGRAMS = main OBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \ - spdylay_buffer_test.c + spdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \ + spdylay_frame_test.c HFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \ - spdylay_buffer_test.h + spdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h + spdylay_frame_test.h main_SOURCES = $(HFILES) $(OBJECTS) diff --git a/tests/main.c b/tests/main.c index 50c12ce..852294d 100644 --- a/tests/main.c +++ b/tests/main.c @@ -30,6 +30,9 @@ #include "spdylay_map_test.h" #include "spdylay_queue_test.h" #include "spdylay_buffer_test.h" +#include "spdylay_zlib_test.h" +#include "spdylay_session_test.h" +#include "spdylay_frame_test.h" int init_suite1(void) { @@ -61,7 +64,13 @@ int main() if(!CU_add_test(pSuite, "pq", test_spdylay_pq) || !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", test_spdylay_buffer) || + !CU_add_test(pSuite, "zlib", test_spdylay_zlib) || + !CU_add_test(pSuite, "session_recv", test_spdylay_session_recv) || + !CU_add_test(pSuite, "session_add_frame", + test_spdylay_session_add_frame) || + !CU_add_test(pSuite, "frame_unpack_nv", + test_spdylay_frame_unpack_nv)) { CU_cleanup_registry(); return CU_get_error(); } diff --git a/tests/spdylay_frame_test.c b/tests/spdylay_frame_test.c new file mode 100644 index 0000000..62fa682 --- /dev/null +++ b/tests/spdylay_frame_test.c @@ -0,0 +1,50 @@ +/* + * 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_frame_test.h" + +#include + +#include "spdylay_frame.h" + +void test_spdylay_frame_unpack_nv() +{ + const char *headers[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "version", "HTTP/1.1", + "x-head", "foo", + "x-head", "bar", + NULL + }; + const char in[1024]; + char **nv; + int i; + size_t inlen = spdylay_frame_pack_nv(in, (char**)headers); + CU_ASSERT(0 == spdylay_frame_unpack_nv(&nv, in, inlen)); + spdylay_frame_nv_free(nv); + free(nv); +} + diff --git a/tests/spdylay_frame_test.h b/tests/spdylay_frame_test.h new file mode 100644 index 0000000..1df83d8 --- /dev/null +++ b/tests/spdylay_frame_test.h @@ -0,0 +1,30 @@ +/* + * 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_FRAME_TEST_H +#define SPDYLAY_FRAME_TEST_H + +void test_spdylay_frame_unpack_nv(); + +#endif /* SPDYLAY_FRAME_TEST_H */ diff --git a/tests/spdylay_session_test.c b/tests/spdylay_session_test.c new file mode 100644 index 0000000..662dd2e --- /dev/null +++ b/tests/spdylay_session_test.c @@ -0,0 +1,175 @@ +/* + * 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_session_test.h" + +#include + +#include +#include +#include + +#include "spdylay_session.h" + +typedef struct { + uint8_t buf[4096]; + size_t length; +} accumulator; + +typedef struct { + uint8_t data[8192]; + uint8_t* datamark; + uint8_t* datalimit; + size_t feedseq[8192]; + size_t seqidx; +} scripted_data_feed; + +typedef struct { + accumulator *acc; + scripted_data_feed *df; +} my_user_data; + +static void scripted_data_feed_init(scripted_data_feed *df, + uint8_t *data, size_t data_length) +{ + memset(df, 0, sizeof(scripted_data_feed)); + memcpy(df->data, data, data_length); + df->datamark = df->data; + df->datalimit = df->data+data_length; + df->feedseq[0] = data_length; +} + +static ssize_t scripted_recv_callback(uint8_t* data, size_t len, int flags, + void *user_data) +{ + scripted_data_feed *df = ((my_user_data*)user_data)->df; + size_t wlen = df->feedseq[df->seqidx] > len ? len : df->feedseq[df->seqidx]; + memcpy(data, df->datamark, wlen); + df->datamark += wlen; + if(wlen <= len) { + ++df->seqidx; + } else { + df->feedseq[df->seqidx] -= wlen; + } + return wlen; +} + +static ssize_t accumulator_send_callback(const uint8_t *buf, size_t len, + int flags, void* user_data) +{ + accumulator *acc = ((my_user_data*)user_data)->acc; + assert(acc->length+len < sizeof(acc->buf)); + memcpy(acc->buf+acc->length, buf, len); + acc->length += len; + return len; +} + +static char** dup_nv(const char **src) +{ + int i; + char **dst; + for(i = 0; src[i]; ++i); + dst = malloc((i+1)*sizeof(char*)); + for(i = 0; src[i]; ++i) { + dst[i] = strdup(src[i]); + } + dst[i] = NULL; + return dst; +} + +void test_spdylay_session_recv() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + NULL, + scripted_recv_callback + }; + scripted_data_feed df; + my_user_data user_data; + const char *nv[] = { + "url", "/", NULL + }; + uint8_t *framedata; + size_t framelen; + spdylay_frame frame; + + user_data.df = &df; + spdylay_session_client_init(&session, &callbacks, &user_data); + spdylay_frame_syn_stream_init(&frame.syn_stream, 0, 0, 0, 3, dup_nv(nv)); + framelen = spdylay_frame_pack_syn_stream(&framedata, &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)); + spdylay_session_free(session); +} + +void test_spdylay_session_add_frame() +{ + spdylay_session *session; + spdylay_session_callbacks callbacks = { + accumulator_send_callback, + NULL, + }; + accumulator acc; + my_user_data user_data; + const char *nv[] = { + "method", "GET", + "scheme", "https", + "url", "/", + "version", "HTTP/1.1", + NULL + }; + spdylay_frame *frame; + const uint8_t hd_ans1[] = { + 0x80, 0x02, 0x00, 0x01 + }; + uint32_t temp32; + acc.length = 0; + user_data.acc = &acc; + CU_ASSERT(0 == spdylay_session_client_init(&session, &callbacks, &user_data)); + + frame = malloc(sizeof(spdylay_frame)); + spdylay_frame_syn_stream_init(&frame->syn_stream, 0, 0, 0, 3, dup_nv(nv)); + + CU_ASSERT(0 == spdylay_session_add_frame(session, SPDYLAY_SYN_STREAM, frame)); + CU_ASSERT(0 == spdylay_pq_empty(&session->ob_pq)); + CU_ASSERT(0 == spdylay_session_send(session)); + CU_ASSERT(memcmp(hd_ans1, acc.buf, 4) == 0); + /* check stream id */ + memcpy(&temp32, &acc.buf[8], 4); + temp32 = ntohl(temp32); + CU_ASSERT(1 == temp32); + /* check assoc stream id */ + memcpy(&temp32, &acc.buf[12], 4); + temp32 = ntohl(temp32); + CU_ASSERT(0 == temp32); + /* check pri */ + temp32 = (acc.buf[16] >> 6) & 0x3; + CU_ASSERT(3 == temp32); + + spdylay_session_free(session); +} diff --git a/tests/spdylay_session_test.h b/tests/spdylay_session_test.h new file mode 100644 index 0000000..76e375b --- /dev/null +++ b/tests/spdylay_session_test.h @@ -0,0 +1,31 @@ +/* + * 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_SESSION_TEST_H +#define SPDYLAY_SESSION_TEST_H + +void test_spdylay_session_recv(); +void test_spdylay_session_add_frame(); + +#endif // SPDYLAY_SESSION_TEST_H diff --git a/tests/spdylay_zlib_test.c b/tests/spdylay_zlib_test.c new file mode 100644 index 0000000..27e4efb --- /dev/null +++ b/tests/spdylay_zlib_test.c @@ -0,0 +1,70 @@ +/* + * 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_zlib_test.h" + +#include + +#include + +#include "spdylay_zlib.h" + +void test_spdylay_zlib() +{ + spdylay_zlib deflater, inflater; + const char msg[] = + "GET /chat HTTP/1.1\r\n" + "Host: server.example.com\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" + "Origin: http://example.com\r\n" + "Sec-WebSocket-Protocol: chat, superchat\r\n" + "Sec-WebSocket-Version: 13\r\n" + "\r\n"; + uint8_t inflatebuf[sizeof(msg)]; + spdylay_buffer buf; + uint8_t *deflatebuf; + size_t deflatebuf_max; + ssize_t deflatebuf_len; + spdylay_buffer_init(&buf, 4096); + + deflatebuf_max = spdylay_zlib_deflate_hd_bound(&deflater, sizeof(msg)); + deflatebuf = malloc(deflatebuf_max); + + CU_ASSERT(0 == spdylay_zlib_deflate_hd_init(&deflater)); + CU_ASSERT(0 == spdylay_zlib_inflate_hd_init(&inflater)); + + CU_ASSERT(0 < (deflatebuf_len = spdylay_zlib_deflate_hd + (&deflater, deflatebuf, deflatebuf_max, + (const uint8_t*)msg, sizeof(msg)))); + CU_ASSERT(sizeof(msg) == spdylay_zlib_inflate_hd + (&inflater, &buf, deflatebuf, deflatebuf_len)); + free(deflatebuf); + spdylay_buffer_serialize(&buf, inflatebuf); + + + spdylay_zlib_deflate_free(&deflater); + spdylay_zlib_inflate_free(&inflater); +} diff --git a/tests/spdylay_zlib_test.h b/tests/spdylay_zlib_test.h new file mode 100644 index 0000000..94d0850 --- /dev/null +++ b/tests/spdylay_zlib_test.h @@ -0,0 +1,30 @@ +/* + * 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_ZLIB_TEST_H +#define SPDYLAY_ZLIB_TEST_H + +void test_spdylay_zlib(); + +#endif // SPDYLAY_ZLIB_TEST_H