summaryrefslogtreecommitdiff
path: root/lib/content_encoding.c
diff options
context:
space:
mode:
authorPatrick Monnerat <patrick@monnerat.net>2017-11-05 15:28:16 +0100
committerPatrick Monnerat <patrick@monnerat.net>2017-11-05 15:28:16 +0100
commit11bf1796cd015373a996e6eb26212e2e1aadb066 (patch)
tree1b63178f15c501be24d33e81f3b57bb7cd163b92 /lib/content_encoding.c
parentdbcced8e32b50c068ac297106f0502ee200a1ebd (diff)
downloadcurl-11bf1796cd015373a996e6eb26212e2e1aadb066.tar.gz
HTTP: implement Brotli content encoding
This uses the brotli external library (https://github.com/google/brotli). Brotli becomes a feature: additional curl_version_info() bit and structure fields are provided for it and CURLVERSION_NOW bumped. Tests 314 and 315 check Brotli content unencoding with correct and erroneous data. Some tests are updated to accomodate with the now configuration dependent parameters of the Accept-Encoding header.
Diffstat (limited to 'lib/content_encoding.c')
-rw-r--r--lib/content_encoding.c138
1 files changed, 136 insertions, 2 deletions
diff --git a/lib/content_encoding.c b/lib/content_encoding.c
index 76a9e6866..6b3168573 100644
--- a/lib/content_encoding.c
+++ b/lib/content_encoding.c
@@ -37,14 +37,15 @@
#ifndef CURL_DISABLE_HTTP
+#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
+
+
#ifdef HAVE_LIBZ
/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
(doing so will reduce code size slightly). */
#define OLD_ZLIB_SUPPORT 1
-#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
-
#define GZIP_MAGIC_0 0x1f
#define GZIP_MAGIC_1 0x8b
@@ -505,6 +506,136 @@ static const content_encoding gzip_encoding = {
#endif /* HAVE_LIBZ */
+#ifdef HAVE_BROTLI
+
+/* Writer parameters. */
+typedef struct {
+ BrotliDecoderState *br; /* State structure for brotli. */
+} brotli_params;
+
+
+static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
+{
+ switch(be) {
+ case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:
+ case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:
+ case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:
+ case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:
+ case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:
+ case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:
+ case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:
+ case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:
+ case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:
+ case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:
+ case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:
+ case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:
+ case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:
+ case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:
+ case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:
+ case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:
+ case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:
+ return CURLE_BAD_CONTENT_ENCODING;
+ case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:
+ case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:
+ case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:
+ case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:
+ case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:
+ case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:
+ return CURLE_OUT_OF_MEMORY;
+ default:
+ break;
+ }
+ return CURLE_WRITE_ERROR;
+}
+
+static CURLcode brotli_init_writer(struct connectdata *conn,
+ contenc_writer *writer)
+{
+ brotli_params *bp = (brotli_params *) &writer->params;
+
+ (void) conn;
+
+ if(!writer->downstream)
+ return CURLE_WRITE_ERROR;
+
+ bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+ return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
+}
+
+static CURLcode brotli_unencode_write(struct connectdata *conn,
+ contenc_writer *writer,
+ const char *buf, size_t nbytes)
+{
+ brotli_params *bp = (brotli_params *) &writer->params;
+ const uint8_t *src = (const uint8_t *) buf;
+ char *decomp;
+ uint8_t *dst;
+ size_t dstleft;
+ CURLcode result = CURLE_OK;
+
+ if(!nbytes)
+ return CURLE_OK;
+ if(!bp->br)
+ return CURLE_WRITE_ERROR; /* Stream already ended. */
+
+ decomp = malloc(DSIZ);
+ if(!decomp)
+ return CURLE_OUT_OF_MEMORY;
+
+ while(nbytes && result == CURLE_OK) {
+ BrotliDecoderResult r;
+
+ dst = (uint8_t *) decomp;
+ dstleft = DSIZ;
+ r = BrotliDecoderDecompressStream(bp->br,
+ &nbytes, &src, &dstleft, &dst, NULL);
+ result = Curl_unencode_write(conn, writer->downstream,
+ decomp, DSIZ - dstleft);
+ if(result)
+ break;
+ switch(r) {
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ break;
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ BrotliDecoderDestroyInstance(bp->br);
+ bp->br = NULL;
+ if(nbytes)
+ result = CURLE_WRITE_ERROR;
+ break;
+ default:
+ result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));
+ break;
+ }
+ }
+ free(decomp);
+ return result;
+}
+
+static void brotli_close_writer(struct connectdata *conn,
+ contenc_writer *writer)
+{
+ brotli_params *bp = (brotli_params *) &writer->params;
+
+ (void) conn;
+
+ if(bp->br) {
+ BrotliDecoderDestroyInstance(bp->br);
+ bp->br = NULL;
+ }
+}
+
+static const content_encoding brotli_encoding = {
+ "br",
+ NULL,
+ brotli_init_writer,
+ brotli_unencode_write,
+ brotli_close_writer,
+ sizeof(brotli_params)
+};
+#endif
+
+
/* Identity handler. */
static CURLcode identity_init_writer(struct connectdata *conn,
contenc_writer *writer)
@@ -544,6 +675,9 @@ static const content_encoding * const encodings[] = {
&deflate_encoding,
&gzip_encoding,
#endif
+#ifdef HAVE_BROTLI
+ &brotli_encoding,
+#endif
NULL
};