summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Griffis <pgriffis@igalia.com>2019-02-27 13:26:36 -0500
committerPatrick Griffis <pgriffis@igalia.com>2019-05-22 08:46:16 +0200
commit7c33db74361d27d3e96e8524c4e88c488014ca8a (patch)
tree4e3e27873274cb7286549f300e09cc9d2c90421b
parenta88f883d151268ec89078079ffb240c88c486c08 (diff)
downloadlibsoup-7c33db74361d27d3e96e8524c4e88c488014ca8a.tar.gz
Implement Brotli decompression support
-rw-r--r--.gitlab-ci/Dockerfile1
-rw-r--r--libsoup/meson.build9
-rw-r--r--libsoup/soup-brotli-decompressor.c194
-rw-r--r--libsoup/soup-brotli-decompressor.h37
-rw-r--r--libsoup/soup-content-decoder.c21
-rw-r--r--meson.build7
-rw-r--r--meson_options.txt6
-rw-r--r--tests/brotli-data/compressed.brbin0 -> 1218 bytes
-rw-r--r--tests/brotli-data/corrupt.brbin0 -> 1218 bytes
-rw-r--r--tests/brotli-data/uncompressed.txt9
-rw-r--r--tests/brotli-decompressor-test.c158
-rw-r--r--tests/meson.build6
12 files changed, 446 insertions, 2 deletions
diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile
index b8ce5e8c..37257a01 100644
--- a/.gitlab-ci/Dockerfile
+++ b/.gitlab-ci/Dockerfile
@@ -13,6 +13,7 @@ RUN dnf update -y \
mod_ssl \
redhat-rpm-config \
meson \
+ brotli-devel \
&& dnf clean all
ARG HOST_USER_ID=5555
diff --git a/libsoup/meson.build b/libsoup/meson.build
index ed6efdbd..24b41ef9 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -183,6 +183,12 @@ soup_gnome_installed_headers = soup_gnome_introspection_headers + [
'soup-gnome.h'
]
+if brotlidec_dep.found()
+ soup_sources += 'soup-brotli-decompressor.c'
+ soup_headers += 'soup-brotli-decompressor.h'
+endif
+
+
includedir = join_paths(libsoup_api_name, meson.project_name())
install_headers(soup_installed_headers, subdir : includedir)
@@ -220,7 +226,8 @@ deps = [
libxml_dep,
sqlite_dep,
libpsl_dep,
- platform_deps
+ brotlidec_dep,
+ platform_deps,
]
libsoup = library('soup-@0@'.format(apiversion),
diff --git a/libsoup/soup-brotli-decompressor.c b/libsoup/soup-brotli-decompressor.c
new file mode 100644
index 00000000..4c089477
--- /dev/null
+++ b/libsoup/soup-brotli-decompressor.c
@@ -0,0 +1,194 @@
+/* soup-brotli-decompressor.c
+ *
+ * Copyright 2019 Igalia S.L.
+ *
+ * This file 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <brotli/decode.h>
+#include <gio/gio.h>
+
+#include "soup-brotli-decompressor.h"
+
+struct _SoupBrotliDecompressor
+{
+ GObject parent_instance;
+ BrotliDecoderState *state;
+ GError *last_error;
+};
+
+static void soup_brotli_decompressor_iface_init (GConverterIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (SoupBrotliDecompressor, soup_brotli_decompressor, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, soup_brotli_decompressor_iface_init))
+
+SoupBrotliDecompressor *
+soup_brotli_decompressor_new (void)
+{
+ return g_object_new (SOUP_TYPE_BROTLI_DECOMPRESSOR, NULL);
+}
+
+static GError *
+soup_brotli_decompressor_create_error (SoupBrotliDecompressor *self)
+{
+ BrotliDecoderErrorCode code;
+ const char *error_string;
+
+ g_assert (self->state != NULL);
+ code = BrotliDecoderGetErrorCode (self->state);
+ error_string = BrotliDecoderErrorString (code);
+ return g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "SoupBrotliDecompressorError: %s", error_string);
+}
+
+static void
+soup_brotli_decompressor_set_error (SoupBrotliDecompressor *self,
+ GError **error)
+{
+ BrotliDecoderErrorCode code;
+ const char *error_string;
+
+ if (error == NULL)
+ return;
+
+ g_assert (self->state != NULL);
+ code = BrotliDecoderGetErrorCode (self->state);
+ error_string = BrotliDecoderErrorString (code);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "SoupBrotliDecompressorError: %s", error_string);
+}
+
+static GConverterResult
+soup_brotli_decompressor_convert (GConverter *converter,
+ const void *inbuf,
+ gsize inbuf_size,
+ void *outbuf,
+ gsize outbuf_size,
+ GConverterFlags flags,
+ gsize *bytes_read,
+ gsize *bytes_written,
+ GError **error)
+{
+ SoupBrotliDecompressor *self = SOUP_BROTLI_DECOMPRESSOR (converter);
+ BrotliDecoderResult result;
+ gsize available_in = inbuf_size;
+ const guint8 *next_in = inbuf;
+ gsize available_out = outbuf_size;
+ guchar *next_out = outbuf;
+
+ g_return_val_if_fail (inbuf, G_CONVERTER_ERROR);
+
+ if (self->last_error) {
+ if (error)
+ *error = g_steal_pointer (&self->last_error);
+ g_clear_error (&self->last_error);
+ return G_CONVERTER_ERROR;
+ }
+
+ /* NOTE: all error domains/codes must match GZlibDecompressor */
+
+ if (self->state == NULL) {
+ self->state = BrotliDecoderCreateInstance (NULL, NULL, NULL);
+ if (self->state == NULL) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "SoupBrotliDecompressorError: Failed to initialize state");
+ return G_CONVERTER_ERROR;
+ }
+ }
+
+ result = BrotliDecoderDecompressStream (self->state, &available_in, &next_in, &available_out, &next_out, NULL);
+
+ /* available_in is now set to *unread* input size */
+ *bytes_read = inbuf_size - available_in;
+ /* available_out is now set to *unwritten* output size */
+ *bytes_written = outbuf_size - available_out;
+
+ /* As per API docs: If any data was either produced or consumed, and then an error happens, then only
+ * the successful conversion is reported and the error is returned on the next call. */
+ if (*bytes_read || *bytes_written) {
+ switch (result) {
+ case BROTLI_DECODER_RESULT_ERROR:
+ self->last_error = soup_brotli_decompressor_create_error (self);
+ break;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ self->last_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "SoupBrotliDecompressorError: More input required (corrupt input)");
+ break;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ /* Just continue with more output then */
+ break;
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ /* Just continue returning finished next time */
+ break;
+ }
+
+ return G_CONVERTER_CONVERTED;
+ }
+
+ switch (result) {
+ case BROTLI_DECODER_RESULT_SUCCESS:
+ return G_CONVERTER_FINISHED;
+ case BROTLI_DECODER_RESULT_ERROR:
+ soup_brotli_decompressor_set_error (self, error);
+ return G_CONVERTER_ERROR;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, "SoupBrotliDecompressorError: More input required (corrupt input)");
+ return G_CONVERTER_ERROR;
+ case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NO_SPACE, "SoupBrotliDecompressorError: Larger output buffer required");
+ return G_CONVERTER_ERROR;
+ }
+
+ g_assert_not_reached ();
+ return G_CONVERTER_ERROR;
+}
+
+static void
+soup_brotli_decompressor_reset (GConverter *converter)
+{
+ SoupBrotliDecompressor *self = SOUP_BROTLI_DECOMPRESSOR (converter);
+
+ if (self->state && BrotliDecoderIsUsed (self->state))
+ g_clear_pointer (&self->state, BrotliDecoderDestroyInstance);
+ g_clear_error (&self->last_error);
+}
+
+static void
+soup_brotli_decompressor_finalize (GObject *object)
+{
+ SoupBrotliDecompressor *self = (SoupBrotliDecompressor *)object;
+ g_clear_pointer (&self->state, BrotliDecoderDestroyInstance);
+ g_clear_error (&self->last_error);
+ G_OBJECT_CLASS (soup_brotli_decompressor_parent_class)->finalize (object);
+}
+
+static void soup_brotli_decompressor_iface_init (GConverterIface *iface)
+{
+ iface->convert = soup_brotli_decompressor_convert;
+ iface->reset = soup_brotli_decompressor_reset;
+}
+
+static void
+soup_brotli_decompressor_class_init (SoupBrotliDecompressorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = soup_brotli_decompressor_finalize;
+}
+
+static void
+soup_brotli_decompressor_init (SoupBrotliDecompressor *self)
+{
+}
diff --git a/libsoup/soup-brotli-decompressor.h b/libsoup/soup-brotli-decompressor.h
new file mode 100644
index 00000000..a730109f
--- /dev/null
+++ b/libsoup/soup-brotli-decompressor.h
@@ -0,0 +1,37 @@
+/* soup-brotli-decompressor.h
+ *
+ * Copyright 2019 Igalia S.L.
+ *
+ * This file 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include "soup-version.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_BROTLI_DECOMPRESSOR (soup_brotli_decompressor_get_type())
+G_DECLARE_FINAL_TYPE (SoupBrotliDecompressor, soup_brotli_decompressor, SOUP, BROTLI_DECOMPRESSOR, GObject)
+
+SOUP_AVAILABLE_IN_2_68
+SoupBrotliDecompressor *soup_brotli_decompressor_new (void);
+
+SOUP_AVAILABLE_IN_2_68
+GType soup_brotli_decompressor_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
diff --git a/libsoup/soup-content-decoder.c b/libsoup/soup-content-decoder.c
index 080904aa..c485c6e6 100644
--- a/libsoup/soup-content-decoder.c
+++ b/libsoup/soup-content-decoder.c
@@ -13,6 +13,9 @@
#include "soup-converter-wrapper.h"
#include "soup.h"
#include "soup-message-private.h"
+#ifdef WITH_BROTLI
+#include "soup-brotli-decompressor.h"
+#endif
/**
* SECTION:soup-content-decoder
@@ -20,7 +23,7 @@
*
* #SoupContentDecoder handles adding the "Accept-Encoding" header on
* outgoing messages, and processing the "Content-Encoding" header on
- * incoming ones. Currently it supports the "gzip" and "deflate"
+ * incoming ones. Currently it supports the "gzip", "deflate", and "br"
* content codings.
*
* If you are using a plain #SoupSession (ie, not #SoupSessionAsync or
@@ -169,7 +172,11 @@ soup_content_decoder_content_processor_init (SoupContentProcessorInterface *proc
}
/* This is constant for now */
+#ifdef WITH_BROTLI
+#define ACCEPT_ENCODING_HEADER "gzip, deflate, br"
+#else
#define ACCEPT_ENCODING_HEADER "gzip, deflate"
+#endif
static GConverter *
gzip_decoder_creator (void)
@@ -183,6 +190,14 @@ zlib_decoder_creator (void)
return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
}
+#ifdef WITH_BROTLI
+static GConverter *
+brotli_decoder_creator (void)
+{
+ return (GConverter *)soup_brotli_decompressor_new ();
+}
+#endif
+
static void
soup_content_decoder_init (SoupContentDecoder *decoder)
{
@@ -196,6 +211,10 @@ soup_content_decoder_init (SoupContentDecoder *decoder)
gzip_decoder_creator);
g_hash_table_insert (decoder->priv->decoders, "deflate",
zlib_decoder_creator);
+#ifdef WITH_BROTLI
+ g_hash_table_insert (decoder->priv->decoders, "br",
+ brotli_decoder_creator);
+#endif
}
static void
diff --git a/meson.build b/meson.build
index 8395a4cc..63e7f5f0 100644
--- a/meson.build
+++ b/meson.build
@@ -101,6 +101,13 @@ endif
cdata = configuration_data()
+if get_option('brotli')
+ brotlidec_dep = dependency('libbrotlidec')
+ cdata.set('WITH_BROTLI', true)
+else
+ brotlidec_dep = dependency('', required: false)
+endif
+
platform_deps = []
hidden_visibility_flag = []
is_static_library = get_option('default_library') == 'static'
diff --git a/meson_options.txt b/meson_options.txt
index ab565c90..fa033bf2 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -22,6 +22,12 @@ option('ntlm_auth',
description : 'Where to look for ntlm_auth, path points to ntlm_auth installation (defaultly looking in PATH)'
)
+option('brotli',
+ type : 'boolean',
+ value : true,
+ description : 'Build with Brotli decompression support'
+)
+
option('tls_check',
type : 'boolean',
value : true,
diff --git a/tests/brotli-data/compressed.br b/tests/brotli-data/compressed.br
new file mode 100644
index 00000000..affb5ff7
--- /dev/null
+++ b/tests/brotli-data/compressed.br
Binary files differ
diff --git a/tests/brotli-data/corrupt.br b/tests/brotli-data/corrupt.br
new file mode 100644
index 00000000..2bbbeba5
--- /dev/null
+++ b/tests/brotli-data/corrupt.br
Binary files differ
diff --git a/tests/brotli-data/uncompressed.txt b/tests/brotli-data/uncompressed.txt
new file mode 100644
index 00000000..cd141c03
--- /dev/null
+++ b/tests/brotli-data/uncompressed.txt
@@ -0,0 +1,9 @@
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam facilisis imperdiet arcu, cursus feugiat velit ultricies vel. Sed consequat velit id purus finibus, ut semper felis tincidunt. Phasellus non lobortis justo. Duis et fermentum dui, id pharetra tellus. Aenean egestas est diam. Etiam lacinia eu diam et fringilla. Integer fringilla, neque non rhoncus venenatis, mauris leo lobortis dolor, id porta dui mi a nibh. Quisque libero orci, eleifend id ornare ut, tristique quis tellus. Nullam urna sem, sollicitudin sit amet urna id, elementum ornare lectus. Curabitur at luctus arcu, nec viverra odio. Donec luctus, ante ac imperdiet dictum, purus diam fringilla tortor, in posuere nisl nibh et ante. Mauris dapibus, est sed condimentum eleifend, sapien ante rhoncus dui, id porta ante libero at leo.
+
+Vivamus in ligula a mi mollis pellentesque eget sed nisl. Cras viverra semper diam. Donec consectetur placerat dignissim. Donec et porttitor urna. Pellentesque placerat at mi a blandit. Sed nec tellus ac sem semper mattis. Mauris mollis libero quam, vitae tincidunt nisi ullamcorper vel. Proin sapien purus, commodo at urna nec, viverra volutpat neque. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+Duis consectetur, justo a consequat condimentum, lectus tortor ultricies justo, nec tincidunt turpis erat vitae nisi. Praesent at volutpat lacus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras sodales libero vitae ultricies dictum. Integer sollicitudin eu arcu non hendrerit. Etiam vel maximus odio. Quisque ex diam, porta sit amet scelerisque vel, tempor nec purus. In fermentum lectus at risus ullamcorper rhoncus. Vestibulum nulla arcu, commodo a vestibulum vel, porttitor et metus. Phasellus facilisis justo vitae quam maximus, interdum fermentum risus dignissim.
+
+Phasellus tristique sollicitudin orci ac scelerisque. Integer vulputate laoreet rutrum. Nullam mauris elit, lobortis et elementum at, vestibulum at magna. Curabitur accumsan leo ut nisi scelerisque maximus. Cras risus metus, suscipit non volutpat eget, lobortis eu erat. Vestibulum sed dolor egestas, ornare est nec, molestie nisi. Integer laoreet, ipsum non finibus rhoncus, erat nisi lacinia dui, vulputate imperdiet odio nulla eget ipsum. Nam sed cursus metus. Quisque lacinia consectetur erat, et dictum mi interdum sit amet. Praesent eleifend luctus odio in faucibus.
+
+Donec in diam rhoncus, vehicula tortor at, molestie erat. Nulla tempor in justo ut gravida. Praesent ornare laoreet ante non faucibus. Donec cursus mi sit amet fringilla bibendum. Ut tincidunt, libero nec scelerisque lobortis, nisi nunc laoreet velit, vitae sodales est purus vehicula urna. Phasellus fringilla mi tellus, in convallis diam maximus et. Praesent iaculis id sem sit amet posuere. Integer ullamcorper, eros ultrices placerat finibus, turpis sem commodo augue, ac ornare massa ante nec nulla.
diff --git a/tests/brotli-decompressor-test.c b/tests/brotli-decompressor-test.c
new file mode 100644
index 00000000..df236677
--- /dev/null
+++ b/tests/brotli-decompressor-test.c
@@ -0,0 +1,158 @@
+/* brotli-decompressor-test.c
+ *
+ * Copyright 2019 Igalia S.L.
+ *
+ * This file 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 of the
+ * License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.0-or-later
+ */
+
+#include "test-utils.h"
+#include "libsoup/soup-brotli-decompressor.h"
+
+static void
+test_brotli (void)
+{
+ SoupBrotliDecompressor *dec = soup_brotli_decompressor_new ();
+ char *compressed_filename = g_build_filename (g_test_get_dir (G_TEST_DIST), "brotli-data", "compressed.br", NULL);
+ char *uncompressed_filename = g_build_filename (g_test_get_dir (G_TEST_DIST), "brotli-data", "uncompressed.txt", NULL);
+ char *contents;
+ gsize length;
+ GByteArray *out_bytes = g_byte_array_new ();
+ char *in_buf;
+ GConverterResult result;
+
+ g_assert_true (g_file_get_contents (compressed_filename, &contents, &length, NULL));
+ in_buf = contents;
+
+ do {
+ GError *error = NULL;
+ guint8 out_buf[16]; /* This is stupidly small just to simulate common usage of converting in chunks */
+ gsize bytes_read, bytes_written;
+ result = g_converter_convert (G_CONVERTER (dec), in_buf, length, out_buf, sizeof out_buf, 0,
+ &bytes_read, &bytes_written, &error);
+
+ g_assert_no_error (error);
+ g_assert_cmpint (result, !=, G_CONVERTER_ERROR);
+
+ g_byte_array_append (out_bytes, out_buf, bytes_written);
+ in_buf += bytes_read;
+ length -= bytes_read;
+
+ } while (result == G_CONVERTER_CONVERTED);
+
+ g_assert_cmpint (result, ==, G_CONVERTER_FINISHED);
+
+ g_free (contents);
+ g_assert_true (g_file_get_contents (uncompressed_filename, &contents, &length, NULL));
+ g_assert_cmpstr ((char*)out_bytes->data, ==, contents);
+
+ g_byte_array_free (out_bytes, TRUE);
+ g_object_unref (dec);
+ g_free (compressed_filename);
+ g_free (contents);
+}
+
+static void
+test_brotli_corrupt (void)
+{
+ SoupBrotliDecompressor *dec = soup_brotli_decompressor_new ();
+ char *compressed_filename = g_build_filename (g_test_get_dir (G_TEST_DIST), "brotli-data", "corrupt.br", NULL);
+ GError *error = NULL;
+ char *contents;
+ gsize length;
+ char *in_buf;
+ GConverterResult result;
+
+ g_assert_true (g_file_get_contents (compressed_filename, &contents, &length, NULL));
+ in_buf = contents;
+
+ do {
+ guint8 out_buf[4096];
+ gsize bytes_read, bytes_written;
+ result = g_converter_convert (G_CONVERTER (dec), in_buf, length, out_buf, sizeof out_buf, 0,
+ &bytes_read, &bytes_written, &error);
+
+ in_buf += bytes_read;
+ length -= bytes_read;
+ } while (result == G_CONVERTER_CONVERTED);
+
+ g_assert_cmpint (result, ==, G_CONVERTER_ERROR);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+
+ g_object_unref (dec);
+ g_free (compressed_filename);
+ g_free (contents);
+ g_error_free (error);
+}
+
+static void
+test_brotli_reset (void)
+{
+ SoupBrotliDecompressor *dec = soup_brotli_decompressor_new ();
+ char *compressed_filename = g_build_filename (g_test_get_dir (G_TEST_DIST), "brotli-data", "compressed.br", NULL);
+ char *contents;
+ gsize length, in_len;
+ char *in_buf;
+ GConverterResult result;
+ int iterations = 0;
+
+ g_assert_true (g_file_get_contents (compressed_filename, &contents, &length, NULL));
+ in_buf = contents;
+ in_len = length;
+
+ do {
+ GError *error = NULL;
+ guint8 out_buf[16];
+ gsize bytes_read, bytes_written;
+ result = g_converter_convert (G_CONVERTER (dec), in_buf, in_len, out_buf, sizeof out_buf, 0,
+ &bytes_read, &bytes_written, &error);
+
+ /* Just randomly reset in the middle and ensure everything keeps working */
+ if (iterations == 6) {
+ g_converter_reset (G_CONVERTER (dec));
+ in_buf = contents;
+ in_len = length;
+ }
+
+ g_assert_no_error (error);
+ g_assert_cmpint (result, !=, G_CONVERTER_ERROR);
+ in_buf += bytes_read;
+ in_len -= bytes_read;
+ ++iterations;
+ } while (result == G_CONVERTER_CONVERTED);
+
+ g_assert_cmpint (result, ==, G_CONVERTER_FINISHED);
+
+ g_object_unref (dec);
+ g_free (compressed_filename);
+ g_free (contents);
+}
+
+int
+main (int argc, char **argv)
+{
+
+ int ret;
+
+ test_init (argc, argv, NULL);
+
+ g_test_add_func ("/brotli/basic", test_brotli);
+ g_test_add_func ("/brotli/corrupt", test_brotli_corrupt);
+ g_test_add_func ("/brotli/reset", test_brotli_reset);
+
+ ret = g_test_run ();
+ test_cleanup ();
+ return ret;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 90c0bed7..127fb3c8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -44,6 +44,12 @@ tests = [
['websocket', true]
]
+if brotlidec_dep.found()
+ tests += [
+ ['brotli-decompressor', true],
+ ]
+endif
+
if have_apache
tests += [
['auth', false],