vlc | branch: master | Rémi Denis-Courmont <[email protected]> | Fri Mar 6 23:21:32 2015 +0200| [7b645ddc0c51f3a36e457195f6a991a1635809a4] | committer: Rémi Denis-Courmont
HPACK Header Compression HTTP/2 minimalistic encoder > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=7b645ddc0c51f3a36e457195f6a991a1635809a4 --- modules/access/http/Makefile.am | 6 +- modules/access/http/hpack.h | 5 + modules/access/http/hpackenc.c | 296 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 305 insertions(+), 2 deletions(-) diff --git a/modules/access/http/Makefile.am b/modules/access/http/Makefile.am index 9a0c96a..010010b 100644 --- a/modules/access/http/Makefile.am +++ b/modules/access/http/Makefile.am @@ -1,4 +1,6 @@ hpack_test_SOURCES = access/http/hpack.c access/http/hpack.h hpack_test_CFLAGS = -DDEC_TEST -check_PROGRAMS += hpack_test -TESTS += hpack_test +hpackenc_test_SOURCES = $(hpack_test_SOURCES) access/http/hpackenc.c +hpackenc_test_CFLAGS = -DENC_TEST +check_PROGRAMS += hpack_test hpackenc_test +TESTS += hpack_test hpackenc_test diff --git a/modules/access/http/hpack.h b/modules/access/http/hpack.h index 0fcc234..536bd33 100644 --- a/modules/access/http/hpack.h +++ b/modules/access/http/hpack.h @@ -25,3 +25,8 @@ void hpack_decode_destroy(struct hpack_decoder *); int hpack_decode(struct hpack_decoder *dec, const uint8_t *data, size_t length, char *headers[][2], unsigned max); + +size_t hpack_encode_hdr_neverindex(uint8_t *restrict buf, size_t size, + const char *name, const char *value); +size_t hpack_encode(uint8_t *restrict buf, size_t size, + const char *const headers[][2], unsigned count); diff --git a/modules/access/http/hpackenc.c b/modules/access/http/hpackenc.c new file mode 100644 index 0000000..18855f4 --- /dev/null +++ b/modules/access/http/hpackenc.c @@ -0,0 +1,296 @@ +/***************************************************************************** + * hpack.c: HPACK Header Compression for HTTP/2 encoder + ***************************************************************************** + * Copyright (C) 2015 Rémi Denis-Courmont + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifdef ENC_TEST +# undef NDEBUG +#endif + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +#include "hpack.h" + +/* + * This is curently the simplest possible HPACK compressor: it does not + * compress anything and is stateless. + * TODO: + * - use static Huffman compression when useful, + * - use header names and values from the static HPACK table, + * - become stateful and let the caller specify the indexed flag, + * - let caller specify the never-indexed flag. + */ + +static size_t hpack_encode_int(uint8_t *restrict buf, size_t size, + uintmax_t value, unsigned n) +{ + assert(n >= 1 && n <= 8); + + unsigned mask = (1 << n) - 1; + size_t ret = 1; + + if (value < mask) + { + if (size > 0) + buf[0] |= value; + return 1; + } + + if (size > 0) + *(buf++) |= mask; + value -= mask; + + while (value >= 128) + { + if (ret++ < size) + *(buf++) = 0x80 | (value & 0x7F); + value >>= 7; + } + + if (ret++ < size) + *(buf++) = value; + return ret; +} + +static size_t hpack_encode_str_raw(uint8_t *restrict buf, size_t size, + const char *str) +{ + size_t len = strlen(str); + + if (size > 0) + *buf = 0; + + size_t ret = hpack_encode_int(buf, size, len, 7); + if (ret < size) + { + buf += ret; + size -= ret; + + memcpy(buf, str, (len <= size) ? len : size); + } + ret += len; + return ret; +} + +static size_t hpack_encode_str_raw_lower(uint8_t *restrict buf, size_t size, + const char *str) +{ + size_t len = strlen(str); + + if (size > 0) + *buf = 0; + + size_t ret = hpack_encode_int(buf, size, len, 7); + if (ret < size) + { + buf += ret; + size -= ret; + + for (size_t i = 0; i < len && i < size; i++) + if (str[i] < 'A' || str[i] > 'Z') + buf[i] = str[i]; + else + buf[i] = str[i] - 'A' + 'a'; + } + ret += len; + return ret; +} + +size_t hpack_encode_hdr_neverindex(uint8_t *restrict buf, size_t size, + const char *name, const char *value) +{ + size_t ret = 1, val; + + if (size > 0) + { + *(buf++) = 0x10; + size--; + } + + val = hpack_encode_str_raw_lower(buf, size, name); + if (size >= val) + { + buf += val; + size -= val; + } + else + size = 0; + ret += val; + + val = hpack_encode_str_raw(buf, size, value); + if (size >= val) + { + buf += val; + size -= val; + } + else + size = 0; + ret += val; + + return ret; +} + +size_t hpack_encode(uint8_t *restrict buf, size_t size, + const char *const headers[][2], unsigned count) +{ + size_t ret = 0; + + while (count > 0) + { + size_t val = hpack_encode_hdr_neverindex(buf, size, headers[0][0], + headers[0][1]); + if (size >= val) + { + buf += val; + size -= val; + } + else + size = 0; + + ret += val; + headers++; + count--; + } + return ret; +} + +/*** Test cases ***/ +#ifdef ENC_TEST +# include <stdarg.h> +# include <stdlib.h> +# include <stdio.h> + +static void test_integer(unsigned n, uintmax_t value) +{ + uint8_t buf[3]; + size_t ret; + + for (unsigned i = 0; i < 3; i++) + { + ret = hpack_encode_int(buf, sizeof (buf), value, n); + assert(ret > 0); + } +} + +static void test_integers(void) +{ + test_integer(5, 10); + test_integer(5, 1337); + test_integer(8, 42); +} + +static void test_lowercase(const char *str) +{ + char c; + + while ((c = *(str++)) != '\0') + assert(c < 'A' || c > 'Z'); +} + +static void test_block(const char *name, ...) +{ + const char *headers[16][2]; + unsigned count = 0; + va_list ap; + + va_start(ap, name); + while (name != NULL) + { + headers[count][0] = name; + headers[count][1] = va_arg(ap, const char *); + name = va_arg(ap, const char *); + count++; + } + va_end(ap); + + printf(" %u header(s):\n", count); + + uint8_t buf[1024]; + + size_t length = hpack_encode(NULL, 0, headers, count); + + for (size_t i = 0; i < sizeof (buf); i++) + assert(hpack_encode(buf, i, headers, count) == length); + + memset(buf, 0xAA, sizeof (buf)); + assert(hpack_encode(buf, length, headers, count) == length); + + char *eheaders[16][2]; + + struct hpack_decoder *dec = hpack_decode_init(4096); + assert(dec != NULL); + + int ecount = hpack_decode(dec, buf, length, eheaders, 16); + assert((unsigned)ecount == count); + + hpack_decode_destroy(dec); + + for (unsigned i = 0; i < count; i++) + { + printf(" %s: %s\n", eheaders[i][0], eheaders[i][1]); + test_lowercase(eheaders[i][0]); + assert(!strcasecmp(eheaders[i][0], headers[i][0])); + assert(!strcmp(eheaders[i][1], headers[i][1])); + free(eheaders[i][1]); + free(eheaders[i][0]); + } +} + +static void test_reqs(void) +{ + test_block(NULL); + test_block(":method", "GET", ":scheme", "http", ":path", "/", + ":authority", "www.example.com", NULL); + test_block(":method", "GET", ":scheme", "http", ":path", "/", + ":authority", "www.example.com", "Cache-Control", "no-cache", + NULL); + test_block(":method", "GET", ":scheme", "https", ":path", "/index.html", + ":authority", "www.example.com", "custom-key", "custom-value", + NULL); +} + +static void test_resps(void) +{ + test_block(":status", "302", "Cache-Control", "private", + "Date", "Mon, 21 Oct 2013 20:13:21 GMT", + "Location", "https://www.example.com", NULL); + test_block(":status", "307", "Cache-Control", "private", + "Date", "Mon, 21 Oct 2013 20:13:21 GMT", + "Location", "https://www.example.com", NULL); + test_block(":status", "200", "Cache-Control", "private", + "Date", "Mon, 21 Oct 2013 20:13:22 GMT", + "Location", "https://www.example.com", + "Content-Encoding", "gzip", + "Set-Cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; " + "max-age=3600; version=1", + NULL); +} + +int main(void) +{ + test_integers(); + test_reqs(); + test_resps(); +} +#endif /* TEST */ _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
