diff options
Diffstat (limited to 'decoders/http/http_decoder_decompress.c')
| -rw-r--r-- | decoders/http/http_decoder_decompress.c | 274 |
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 |
