diff --git a/examples/spdycat.cc b/examples/spdycat.cc index e992099..1286adb 100644 --- a/examples/spdycat.cc +++ b/examples/spdycat.cc @@ -51,7 +51,6 @@ #include #include #include -#include #include "spdylay_ssl.h" #include "uri.h" @@ -72,17 +71,18 @@ struct Config { struct Request { uri::UriStruct us; - z_stream *inflater; + spdylay_gzip *inflater; Request(const uri::UriStruct& us):us(us), inflater(0) {} ~Request() { - spdylay_free_inflate_stream(inflater); + spdylay_gzip_inflate_del(inflater); } void init_inflater() { - inflater = spdylay_new_inflate_stream(); - assert(inflater != NULL); + int rv; + rv = spdylay_gzip_inflate_new(&inflater); + assert(rv == 0); } }; @@ -105,7 +105,7 @@ void on_data_chunk_recv_callback uint8_t out[MAX_OUTLEN]; size_t outlen = MAX_OUTLEN; size_t tlen = len; - int rv = spdylay_inflate_data(req->inflater, out, &outlen, data, &tlen); + int rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); if(rv == -1) { spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); break; diff --git a/examples/spdycli.c b/examples/spdycli.c index fe818d2..b70b6a8 100644 --- a/examples/spdycli.c +++ b/examples/spdycli.c @@ -45,8 +45,6 @@ #include #include -#include - enum { IO_NONE, WANT_READ, @@ -75,8 +73,8 @@ struct Request { char *hostport; /* Stream ID for this request. */ int32_t stream_id; - /* zlib context for gzip response */ - z_stream *inflater; + /* The gzip stream inflater for the compressed response. */ + spdylay_gzip *inflater; }; struct URI { @@ -147,11 +145,12 @@ static void check_gzip(struct Request *req, char **nv) } } if(gzip) { + int rv; if(req->inflater) { return; } - req->inflater = spdylay_new_inflate_stream(); - if (req->inflater == NULL) { + rv = spdylay_gzip_inflate_new(&req->inflater); + if(rv != 0) { die("Can't allocate inflate stream."); } } @@ -340,7 +339,7 @@ static void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, size_t outlen = MAX_OUTLEN; size_t tlen = len; int rv; - rv = spdylay_inflate_data(req->inflater, out, &outlen, data, &tlen); + rv = spdylay_gzip_inflate(req->inflater, out, &outlen, data, &tlen); if(rv == -1) { spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); break; @@ -561,7 +560,7 @@ static void request_free(struct Request *req) free(req->host); free(req->path); free(req->hostport); - spdylay_free_inflate_stream(req->inflater); + spdylay_gzip_inflate_del(req->inflater); } /* diff --git a/lib/includes/spdylay/spdylay.h b/lib/includes/spdylay/spdylay.h index 5bff9f2..020fdaf 100644 --- a/lib/includes/spdylay/spdylay.h +++ b/lib/includes/spdylay/spdylay.h @@ -160,6 +160,10 @@ typedef enum { * requested operation. */ SPDYLAY_ERR_INVALID_STATE = -519, + /** + * The gzip error. + */ + SPDYLAY_ERR_GZIP = -520, /** * The errors < :enum:`SPDYLAY_ERR_FATAL` mean that the library is * under unexpected condition and cannot process any further data @@ -1828,50 +1832,85 @@ int spdylay_select_next_protocol(unsigned char **out, unsigned char *outlen, */ uint16_t spdylay_npn_get_version(const unsigned char *proto, size_t protolen); +struct spdylay_gzip; + +/** + * @struct + * + * The gzip stream to inflate data. The details of this structure are + * intentionally hidden from the public API. + */ +typedef struct spdylay_gzip spdylay_gzip; + /** * @function * - * A helper function to set up a per request zlib stream to inflate data. + * A helper function to set up a per request gzip stream to inflate data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_GZIP` + * The initialization of gzip stream failed. + * :enum:`SPDYLAY_ERR_NOMEM` + * Out of memory. */ -z_stream *spdylay_new_inflate_stream(); +int spdylay_gzip_inflate_new(spdylay_gzip **inflater_ptr); /** * @function - * Inflates data from in to out. Returns 0 on success and -1 on error. - * E.g - * void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, - * int32_t stream_id, - * const uint8_t *data, size_t len, - * void *user_data) - * req = spdylay_session_get_stream_user_data(session, stream_id); - * z_stream *inflater = req->inflater; - * while(len > 0) { - * uint8_t out[MAX_OUTLEN]; - * size_t outlen = MAX_OUTLEN; - * size_t tlen = len; - * int rv; - * rv = spdylay_inflate_data(inflater, out, &outlen, data, &tlen); - * if(rv == -1) { - * spdylay_submit_rst_stream(session, stream_id, SPDYLAY_INTERNAL_ERROR); - * break; - * } - * ... Do stuff ... - * data += tlen; - * len -= tlen; - * } - * .... - * } + * + * Frees the inflate stream. The |inflater| may be ``NULL``. */ -int spdylay_inflate_data -(z_stream *stream, uint8_t *out, size_t *outlen_ptr, - const uint8_t *in, size_t *inlen_ptr); +void spdylay_gzip_inflate_del(spdylay_gzip *inflater); /** * @function - * Frees the inflate stream. inflater may be null. + * + * Inflates data in |in| with the length |*inlen_ptr| and stores the + * inflated data to |out| which has allocated size at least + * |*outlen_ptr|. On return, |*outlen_ptr| is updated to represent + * the number of data written in |out|. Similarly, |*inlen_ptr| is + * updated to represent the number of input bytes processed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`SPDYLAY_ERR_GZIP` + * The inflation of gzip stream failed. + * + * The example follows:: + * + * void on_data_chunk_recv_callback(spdylay_session *session, + * uint8_t flags, + * int32_t stream_id, + * const uint8_t *data, size_t len, + * void *user_data) + * { + * ... + * req = spdylay_session_get_stream_user_data(session, stream_id); + * spdylay_gzip *inflater = req->inflater; + * while(len > 0) { + * uint8_t out[MAX_OUTLEN]; + * size_t outlen = MAX_OUTLEN; + * size_t tlen = len; + * int rv; + * rv = spdylay_gzip_inflate(inflater, out, &outlen, data, &tlen); + * if(rv != 0) { + * spdylay_submit_rst_stream(session, stream_id, + * SPDYLAY_INTERNAL_ERROR); + * break; + * } + * ... Do stuff ... + * data += tlen; + * len -= tlen; + * } + * .... + * } */ -void spdylay_free_inflate_stream(z_stream* inflater); - +int spdylay_gzip_inflate(spdylay_gzip *inflater, + uint8_t *out, size_t *outlen_ptr, + const uint8_t *in, size_t *inlen_ptr); #ifdef __cplusplus } diff --git a/lib/spdylay_gzip.c b/lib/spdylay_gzip.c index 5304af8..ab88f66 100644 --- a/lib/spdylay_gzip.c +++ b/lib/spdylay_gzip.c @@ -22,47 +22,52 @@ * 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 -#include -#include #include "spdylay_gzip.h" -z_stream *spdylay_new_inflate_stream() +#include + +int spdylay_gzip_inflate_new(spdylay_gzip **inflater_ptr) { int rv; - z_stream *inflater = malloc(sizeof(z_stream)); - if (inflater == NULL) { - return NULL; + *inflater_ptr = malloc(sizeof(spdylay_gzip)); + if(*inflater_ptr == NULL) { + return SPDYLAY_ERR_NOMEM; } - - inflater->next_in = Z_NULL; - inflater->zalloc = Z_NULL; - inflater->zfree = Z_NULL; - inflater->opaque = Z_NULL; - rv = inflateInit2(inflater, 47); + (*inflater_ptr)->zst.next_in = Z_NULL; + (*inflater_ptr)->zst.avail_in = 0; + (*inflater_ptr)->zst.zalloc = Z_NULL; + (*inflater_ptr)->zst.zfree = Z_NULL; + (*inflater_ptr)->zst.opaque = Z_NULL; + rv = inflateInit2(&(*inflater_ptr)->zst, 47); if(rv != Z_OK) { - free(inflater); - return NULL; + free(*inflater_ptr); + return SPDYLAY_ERR_GZIP; } - return inflater; + return 0; } +void spdylay_gzip_inflate_del(spdylay_gzip *inflater) +{ + if(inflater != NULL) { + inflateEnd(&inflater->zst); + free(inflater); + } +} -int spdylay_inflate_data -(z_stream *inflater, uint8_t *out, size_t *outlen_ptr, - const uint8_t *in, size_t *inlen_ptr) { +int spdylay_gzip_inflate(spdylay_gzip *inflater, + uint8_t *out, size_t *outlen_ptr, + const uint8_t *in, size_t *inlen_ptr) +{ int rv; - assert(inflater); - inflater->avail_in = *inlen_ptr; - inflater->next_in = (unsigned char*)in; - inflater->avail_out = *outlen_ptr; - inflater->next_out = out; + inflater->zst.avail_in = *inlen_ptr; + inflater->zst.next_in = (unsigned char*)in; + inflater->zst.avail_out = *outlen_ptr; + inflater->zst.next_out = out; - rv = inflate(inflater, Z_NO_FLUSH); + rv = inflate(&inflater->zst, Z_NO_FLUSH); - *inlen_ptr -= inflater->avail_in; - *outlen_ptr -= inflater->avail_out; + *inlen_ptr -= inflater->zst.avail_in; + *outlen_ptr -= inflater->zst.avail_out; switch(rv) { case Z_OK: case Z_STREAM_END: @@ -72,16 +77,8 @@ int spdylay_inflate_data case Z_STREAM_ERROR: case Z_NEED_DICT: case Z_MEM_ERROR: - return -1; + return SPDYLAY_ERR_GZIP; default: - abort(); - } -} - -void spdylay_free_inflate_stream(z_stream* stream) -{ - if (stream != NULL) { - inflateEnd(stream); - free(stream); + assert(0); } } diff --git a/lib/spdylay_gzip.h b/lib/spdylay_gzip.h index bc10de1..4c0daf5 100644 --- a/lib/spdylay_gzip.h +++ b/lib/spdylay_gzip.h @@ -22,13 +22,17 @@ * 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_GZIP_H -#include +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ #include -/* See usage comments in spdylay.h */ -z_stream *spdylay_new_inflate_stream(); -int spdylay_inflate_data -(z_stream *stream, uint8_t *out, size_t *outlen_ptr, - const uint8_t *in, size_t *inlen_ptr); -void spdylay_free_inflate_stream(z_stream* stream); +#include + +struct spdylay_gzip { + z_stream zst; +}; + +#endif /* SPDYLAY_GZIP_H */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 429b1d4..050c271 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -29,12 +29,12 @@ check_PROGRAMS = main failmalloc OBJECTS = main.c spdylay_pq_test.c spdylay_map_test.c spdylay_queue_test.c \ spdylay_buffer_test.c spdylay_zlib_test.c spdylay_session_test.c \ spdylay_frame_test.c spdylay_stream_test.c spdylay_npn_test.c \ - spdylay_client_cert_vector_test.c + spdylay_client_cert_vector_test.c spdylay_gzip_test.c HFILES = spdylay_pq_test.h spdylay_map_test.h spdylay_queue_test.h \ spdylay_buffer_test.h spdylay_zlib_test.h spdylay_session_test.h \ spdylay_frame_test.h spdylay_stream_test.h spdylay_npn_test.h \ - spdylay_client_cert_vector_test.h + spdylay_client_cert_vector_test.h spdylay_gzip_test.h main_SOURCES = $(HFILES) $(OBJECTS) diff --git a/tests/main.c b/tests/main.c index 453e77a..a9514c5 100644 --- a/tests/main.c +++ b/tests/main.c @@ -36,6 +36,7 @@ #include "spdylay_stream_test.h" #include "spdylay_npn_test.h" #include "spdylay_client_cert_vector_test.h" +#include "spdylay_gzip_test.h" static int init_suite1(void) { @@ -204,7 +205,8 @@ int main(int argc, char* argv[]) !CU_add_test(pSuite, "client_cert_vector_resize", test_spdylay_client_cert_vector_resize) || !CU_add_test(pSuite, "client_cert_vector_get_origin", - test_spdylay_client_cert_vector_get_origin)) { + test_spdylay_client_cert_vector_get_origin) || + !CU_add_test(pSuite, "gzip_inflate", test_spdylay_gzip_inflate)) { CU_cleanup_registry(); return CU_get_error(); } diff --git a/tests/spdylay_gzip_test.c b/tests/spdylay_gzip_test.c new file mode 100644 index 0000000..52760ca --- /dev/null +++ b/tests/spdylay_gzip_test.c @@ -0,0 +1,118 @@ +/* + * 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_gzip_test.h" + +#include +#include + +#include + +#include + +#include "spdylay_gzip.h" + +static ssize_t deflate_data(uint8_t *out, size_t outlen, + const +uint8_t *in, size_t inlen) +{ + int rv; + z_stream zst; + zst.next_in = Z_NULL; + zst.zalloc = Z_NULL; + zst.zfree = Z_NULL; + zst.opaque = Z_NULL; + + rv = deflateInit(&zst, Z_DEFAULT_COMPRESSION); + assert(rv == Z_OK); + + zst.avail_in = inlen; + zst.next_in = (uint8_t*)in; + zst.avail_out = outlen; + zst.next_out = out; + rv = deflate(&zst, Z_SYNC_FLUSH); + assert(rv == Z_OK); + + deflateEnd(&zst); + + return outlen-zst.avail_out; +} + +static const char input[] = + "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."; + +void test_spdylay_gzip_inflate(void) +{ + spdylay_gzip *inflater; + uint8_t in[4096], out[4096], *inptr; + size_t inlen = sizeof(in); + size_t inproclen, outproclen; + const char *inputptr = input; + + inlen = deflate_data(in, inlen, (const uint8_t*)input, sizeof(input)-1); + + CU_ASSERT(0 == spdylay_gzip_inflate_new(&inflater)); + /* First 16 bytes */ + inptr = in; + inproclen = inlen; + outproclen = 16; + CU_ASSERT(0 == spdylay_gzip_inflate(inflater, out, &outproclen, + inptr, &inproclen)); + CU_ASSERT(16 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + /* Next 32 bytes */ + inptr += inproclen; + inlen -= inproclen; + inproclen = inlen; + inputptr += outproclen; + outproclen = 32; + CU_ASSERT(0 == spdylay_gzip_inflate(inflater, out, &outproclen, + inptr, &inproclen)); + CU_ASSERT(32 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + /* Rest */ + inptr += inproclen; + inlen -= inproclen; + inproclen = inlen; + inputptr += outproclen; + outproclen = sizeof(out); + CU_ASSERT(0 == spdylay_gzip_inflate(inflater, out, &outproclen, + inptr, &inproclen)); + CU_ASSERT(sizeof(input)-49 == outproclen); + CU_ASSERT(inproclen > 0); + CU_ASSERT(0 == memcmp(inputptr, out, outproclen)); + + inlen -= inproclen; + CU_ASSERT(0 == inlen); + + spdylay_gzip_inflate_del(inflater); +} diff --git a/tests/spdylay_gzip_test.h b/tests/spdylay_gzip_test.h new file mode 100644 index 0000000..b1143a3 --- /dev/null +++ b/tests/spdylay_gzip_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_GZIP_TEST_H +#define SPDYLAY_GZIP_TEST_H + +void test_spdylay_gzip_inflate(void); + +#endif /* SPDYLAY_GZIP_TEST_H */