summaryrefslogtreecommitdiff
path: root/decoders/http/http_decoder_decompress.c
diff options
context:
space:
mode:
Diffstat (limited to 'decoders/http/http_decoder_decompress.c')
-rw-r--r--decoders/http/http_decoder_decompress.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/decoders/http/http_decoder_decompress.c b/decoders/http/http_decoder_decompress.c
new file mode 100644
index 0000000..438987e
--- /dev/null
+++ b/decoders/http/http_decoder_decompress.c
@@ -0,0 +1,274 @@
+#include <zlib.h>
+#include <string.h>
+#include <assert.h>
+#include <brotli/decode.h>
+#include "http_decoder_utils.h"
+#include "http_decoder_decompress.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ struct http_content_decompress
+ {
+ enum http_content_encoding encoding;
+ z_stream *z_stream_ptr;
+ BrotliDecoderState *br_state;
+ char *buffer;
+ size_t buffer_size;
+ };
+
+ void http_content_decompress_ownership_borrow(struct http_content_decompress *decompress)
+ {
+ decompress->buffer = NULL; // ownership move to flow_data, will be freed when message has been processed by all modules
+ }
+
+ enum http_content_encoding http_content_encoding_str2int(const char *content_encoding, size_t encoding_str_len)
+ {
+ if (http_strncasecmp_safe("gzip", content_encoding, 4, encoding_str_len) == 0)
+ {
+ return HTTP_CONTENT_ENCODING_GZIP;
+ }
+ if (http_strncasecmp_safe("deflate", content_encoding, 7, encoding_str_len) == 0)
+ {
+ return HTTP_CONTENT_ENCODING_DEFLATE;
+ }
+ if (http_strncasecmp_safe("br", content_encoding, 2, encoding_str_len) == 0)
+ {
+ return HTTP_CONTENT_ENCODING_BR;
+ }
+ return HTTP_CONTENT_ENCODING_NONE;
+ }
+
+ const char *http_content_encoding_int2str(enum http_content_encoding content_encoding)
+ {
+ if (content_encoding == HTTP_CONTENT_ENCODING_GZIP)
+ {
+ return "gzip";
+ }
+ if (content_encoding == HTTP_CONTENT_ENCODING_DEFLATE)
+ {
+ return "deflate";
+ }
+ if (content_encoding == HTTP_CONTENT_ENCODING_BR)
+ {
+ return "br";
+ }
+ return "unknown";
+ }
+
+ struct http_content_decompress *http_content_decompress_create(enum http_content_encoding encoding)
+ {
+ struct http_content_decompress *decompress = CALLOC(struct http_content_decompress, 1);
+ assert(decompress);
+
+ decompress->encoding = encoding;
+ decompress->z_stream_ptr = NULL;
+ decompress->br_state = NULL;
+
+ if (encoding == HTTP_CONTENT_ENCODING_GZIP || encoding == HTTP_CONTENT_ENCODING_DEFLATE)
+ {
+ decompress->z_stream_ptr = CALLOC(z_stream, 1);
+ assert(decompress->z_stream_ptr);
+
+ decompress->z_stream_ptr->zalloc = NULL;
+ decompress->z_stream_ptr->zfree = NULL;
+ decompress->z_stream_ptr->opaque = NULL;
+ decompress->z_stream_ptr->avail_in = 0;
+ decompress->z_stream_ptr->next_in = Z_NULL;
+
+ if (encoding == HTTP_CONTENT_ENCODING_GZIP)
+ {
+ if (inflateInit2(decompress->z_stream_ptr, MAX_WBITS + 16) != Z_OK)
+ {
+ goto error;
+ }
+ }
+ if (encoding == HTTP_CONTENT_ENCODING_DEFLATE)
+ {
+ if (inflateInit2(decompress->z_stream_ptr, -MAX_WBITS) != Z_OK)
+ {
+ goto error;
+ }
+ }
+ }
+
+ if (encoding == HTTP_CONTENT_ENCODING_BR)
+ {
+ decompress->br_state = BrotliDecoderCreateInstance(NULL, NULL, NULL);
+ if (decompress->br_state == NULL)
+ {
+ goto error;
+ }
+ }
+ return decompress;
+
+ error:
+ http_content_decompress_destroy(decompress);
+ return NULL;
+ }
+
+ void http_content_decompress_destroy(struct http_content_decompress *decompress)
+ {
+ if (NULL == decompress)
+ {
+ return;
+ }
+ if (decompress->z_stream_ptr != NULL)
+ {
+ inflateEnd(decompress->z_stream_ptr);
+ FREE(decompress->z_stream_ptr);
+ }
+ if (decompress->br_state)
+ {
+ BrotliDecoderDestroyInstance(decompress->br_state);
+ decompress->br_state = NULL;
+ }
+ FREE(decompress->buffer);
+ FREE(decompress);
+ }
+
+ static int http_content_decompress_write_zlib(struct http_content_decompress *decompress,
+ const char *indata, size_t indata_len,
+ char **outdata, size_t *outdata_len)
+ {
+ z_stream *z_stream_ptr = decompress->z_stream_ptr;
+ z_stream_ptr->avail_in = (unsigned int)indata_len;
+ z_stream_ptr->next_in = (unsigned char *)indata;
+ z_stream_ptr->avail_out = (unsigned int)HTTP_DECOMPRESS_BUFFER_SIZE;
+ z_stream_ptr->next_out = (unsigned char *)decompress->buffer;
+ *outdata = NULL;
+ *outdata_len = 0;
+ size_t total_have = 0;
+ int no_buffer;
+
+ do
+ {
+ int ret = inflate(z_stream_ptr, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
+ {
+ (void)inflateEnd(z_stream_ptr);
+ return -1;
+ }
+ size_t have = HTTP_DECOMPRESS_BUFFER_SIZE - z_stream_ptr->avail_out;
+ if (have > 0)
+ {
+ total_have += have;
+ if (0 == z_stream_ptr->avail_out)
+ {
+ decompress->buffer_size += HTTP_DECOMPRESS_BUFFER_SIZE;
+ decompress->buffer = REALLOC(char, decompress->buffer, decompress->buffer_size);
+ z_stream_ptr->avail_out = HTTP_DECOMPRESS_BUFFER_SIZE;
+ z_stream_ptr->next_out = (unsigned char *)decompress->buffer + total_have;
+ *outdata = decompress->buffer;
+ *outdata_len = total_have;
+ no_buffer = 1;
+ }
+ else
+ {
+ *outdata = decompress->buffer;
+ *outdata_len = total_have;
+ no_buffer = 0;
+ }
+ }
+ else
+ {
+ break;
+ }
+ if (Z_STREAM_END == ret)
+ {
+ break;
+ }
+ } while (no_buffer == 1);
+ return 0;
+ }
+
+ static int http_content_decompress_write_br(struct http_content_decompress *decompress,
+ const char *indata, size_t indata_len,
+ char **outdata, size_t *outdata_len)
+ {
+ size_t available_in = indata_len;
+ const unsigned char *next_in = (const unsigned char *)indata;
+ size_t available_out = HTTP_DECOMPRESS_BUFFER_SIZE;
+ unsigned char *next_out = (unsigned char *)decompress->buffer;
+
+ *outdata = NULL;
+ *outdata_len = 0;
+ size_t total_have = 0;
+ int no_buffer;
+
+ do
+ {
+ int ret = BrotliDecoderDecompressStream(decompress->br_state, &available_in,
+ &next_in, &available_out, &next_out, NULL);
+ if (ret == BROTLI_DECODER_RESULT_ERROR)
+ {
+ // BrotliDecoderErrorCode errcode = BrotliDecoderGetErrorCode(decompress->br_state);
+ *outdata = NULL;
+ *outdata_len = 0;
+ return -1;
+ }
+ size_t have = HTTP_DECOMPRESS_BUFFER_SIZE - available_out;
+ if (have > 0)
+ {
+ total_have += have;
+ if (0 == available_out)
+ {
+ decompress->buffer_size += HTTP_DECOMPRESS_BUFFER_SIZE;
+ decompress->buffer = REALLOC(char, decompress->buffer, decompress->buffer_size);
+ available_out = HTTP_DECOMPRESS_BUFFER_SIZE;
+ next_out = (unsigned char *)decompress->buffer + total_have;
+ *outdata = decompress->buffer;
+ *outdata_len = total_have;
+ no_buffer = 1;
+ }
+ else
+ {
+ *outdata = decompress->buffer;
+ *outdata_len = have;
+ no_buffer = 0;
+ }
+ }
+ else
+ {
+ break;
+ }
+ } while (no_buffer == 1);
+ return 0;
+ }
+
+ int http_content_decompress_write(struct http_content_decompress *decompress,
+ const char *indata, size_t indata_len,
+ char **outdata, size_t *outdata_len)
+ {
+ assert(decompress);
+ assert(indata);
+ assert(indata_len > 0);
+ assert(outdata);
+ assert(outdata_len);
+ *outdata = NULL;
+ *outdata_len = 0;
+
+ if (NULL == decompress->buffer)
+ {
+ decompress->buffer = CALLOC(char, HTTP_DECOMPRESS_BUFFER_SIZE);
+ assert(decompress->buffer);
+ decompress->buffer_size = HTTP_DECOMPRESS_BUFFER_SIZE;
+ }
+
+ if (decompress->encoding == HTTP_CONTENT_ENCODING_GZIP ||
+ decompress->encoding == HTTP_CONTENT_ENCODING_DEFLATE)
+ {
+ return http_content_decompress_write_zlib(decompress, indata, indata_len, outdata, outdata_len);
+ }
+ if (decompress->encoding == HTTP_CONTENT_ENCODING_BR)
+ {
+ return http_content_decompress_write_br(decompress, indata, indata_len, outdata, outdata_len);
+ }
+ assert(0);
+ return -1;
+ }
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file