268 lines
8.2 KiB
C
268 lines
8.2 KiB
C
#include <zlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <brotli/decode.h>
|
|
#include "http_decoder_private.h"
|
|
|
|
#define HTTP_DECOMPRESS_BUFFER_SIZE (4096)
|
|
|
|
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 data->decompress_buffer_list, will be freed when message has been processed by all plugins
|
|
}
|
|
|
|
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;
|
|
} |