diff --git a/src/client.c b/src/client.c index 813737e..eceacdb 100644 --- a/src/client.c +++ b/src/client.c @@ -330,21 +330,24 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int if (uri.meta->filename_comp_br[0] != 0 && strstr(accept_encoding, "br") != NULL) { file = fopen(uri.meta->filename_comp_br, "rb"); if (file == NULL) { - printf("asdf\n"); cache_filename_comp_invalid(uri.filename); + } else { + http_add_header_field(&res.hdr, "Content-Encoding", "br"); } - http_add_header_field(&res.hdr, "Content-Encoding", "br"); } else if (uri.meta->filename_comp_gz[0] != 0 && strstr(accept_encoding, "gzip") != NULL) { file = fopen(uri.meta->filename_comp_gz, "rb"); if (file == NULL) { cache_filename_comp_invalid(uri.filename); + } else { + http_add_header_field(&res.hdr, "Content-Encoding", "gzip"); } - http_add_header_field(&res.hdr, "Content-Encoding", "gzip"); } } + if (file == NULL) { file = fopen(uri.filename, "rb"); } + fseek(file, 0, SEEK_END); content_length = ftell(file); fseek(file, 0, SEEK_SET); diff --git a/src/lib/brotli.c b/src/lib/brotli.c deleted file mode 100644 index ae10f6e..0000000 --- a/src/lib/brotli.c +++ /dev/null @@ -1,24 +0,0 @@ -/** - * - */ - -#include "brotli.h" - -int brotli_init(BrotliEncoderState **state) { - *state = BrotliEncoderCreateInstance(NULL, NULL, NULL); - if (*state == NULL) return -1; - BrotliEncoderSetParameter(*state, BROTLI_PARAM_MODE, BROTLI_MODE_GENERIC); - BrotliEncoderSetParameter(*state, BROTLI_PARAM_MODE, BROTLI_MODE_GENERIC); - return 0; -} - -int brotli_compress(BrotliEncoderState *state, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { - int ret = BrotliEncoderCompressStream(state, finish ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, - in_len, (const unsigned char**) &in, out_len, (unsigned char **) &out, NULL); - return (ret == BROTLI_TRUE) ? 0 : -1; -} - -int brotli_free(BrotliEncoderState *state) { - BrotliEncoderDestroyInstance(state); - return 0; -} diff --git a/src/lib/brotli.h b/src/lib/brotli.h deleted file mode 100644 index f5e12ef..0000000 --- a/src/lib/brotli.h +++ /dev/null @@ -1,11 +0,0 @@ -/** - * - */ - -#include - -int brotli_init(BrotliEncoderState **state); - -int brotli_compress(BrotliEncoderState *state, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish); - -int brotli_free(BrotliEncoderState *state); diff --git a/src/lib/cache.c b/src/lib/cache.c index 4058282..46ce2b2 100644 --- a/src/lib/cache.c +++ b/src/lib/cache.c @@ -7,8 +7,7 @@ #include "cache.h" #include "utils.h" -#include "gzip.h" -#include "brotli.h" +#include "compress.h" #include #include #include @@ -85,7 +84,7 @@ int cache_process() { unsigned char hash[SHA_DIGEST_LENGTH]; int cache_changed = 0; int p_len_gz, p_len_br; - int ret_1, ret_2; + int ret; while (cache_continue) { for (int i = 0; i < CACHE_ENTRIES; i++) { if (cache[i].filename[0] != 0 && cache[i].meta.etag[0] == 0 && !cache[i].is_updating) { @@ -95,8 +94,7 @@ int cache_process() { file = fopen(cache[i].filename, "rb"); compress = mime_is_compressible(cache[i].meta.type); - z_stream gz_state; - BrotliEncoderState *br_state = NULL; + compress_ctx comp_ctx; FILE *comp_file_gz = NULL; FILE *comp_file_br = NULL; if (compress) { @@ -136,15 +134,9 @@ int cache_process() { comp_err: compress = 0; } else { - ret_1 = gzip_init(&gz_state); - ret_2 = brotli_init(&br_state); - if (ret_1 != 0) { - fprintf(stderr, ERR_STR "Unable to init gzip: %s" CLR_STR "\n", strerror(errno)); - compress = 0; - fclose(comp_file_gz); - fclose(comp_file_br); - } else if (ret_2 != 0) { - fprintf(stderr, ERR_STR "Unable to init brotli: %s" CLR_STR "\n", strerror(errno)); + ret = compress_init(&comp_ctx, COMPRESS_GZ | COMPRESS_BR); + if (ret != 0) { + fprintf(stderr, ERR_STR "Unable to init compression: %s" CLR_STR "\n", strerror(errno)); compress = 0; fclose(comp_file_gz); fclose(comp_file_br); @@ -159,21 +151,22 @@ int cache_process() { avail_in = read; do { avail_out = sizeof(comp_buf); - gzip_compress(&gz_state, buf + read - avail_in, &avail_in, comp_buf, &avail_out, feof(file)); + compress_compress_mode(&comp_ctx, COMPRESS_GZ,buf + read - avail_in, &avail_in, + comp_buf, &avail_out, feof(file)); fwrite(comp_buf, 1, sizeof(comp_buf) - avail_out, comp_file_gz); } while (avail_in != 0); avail_in = read; do { avail_out = sizeof(comp_buf); - brotli_compress(br_state, buf + read - avail_in, &avail_in, comp_buf, &avail_out, feof(file)); + compress_compress_mode(&comp_ctx, COMPRESS_BR, buf + read - avail_in, &avail_in, + comp_buf, &avail_out, feof(file)); fwrite(comp_buf, 1, sizeof(comp_buf) - avail_out, comp_file_br); } while (avail_in != 0); } } if (compress) { - gzip_free(&gz_state); - brotli_free(br_state); + compress_free(&comp_ctx); fclose(comp_file_gz); fclose(comp_file_br); fprintf(stdout, "[cache] Finished compressing file %s\n", cache[i].filename); diff --git a/src/lib/compress.c b/src/lib/compress.c new file mode 100644 index 0000000..bb32d95 --- /dev/null +++ b/src/lib/compress.c @@ -0,0 +1,76 @@ +/** + * Necronda Web Server + * Compression interface + * src/lib/compress.c + * Lorenz Stechauner, 2021-05-05 + */ + +#include "compress.h" +#include +#include + +int compress_init(compress_ctx *ctx, int mode) { + ctx->gzip = NULL; + ctx->brotli = NULL; + int ret; + if (mode & COMPRESS_GZ) { + ctx->gzip = malloc(sizeof(z_stream)); + ctx->gzip->zalloc = Z_NULL; + ctx->gzip->zfree = Z_NULL; + ctx->gzip->opaque = Z_NULL; + ret = deflateInit2(ctx->gzip, COMPRESS_LEVEL_GZIP, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) return -1; + } + if (mode & COMPRESS_BR) { + ctx->brotli = BrotliEncoderCreateInstance(NULL, NULL, NULL); + if (ctx->brotli == NULL) return -1; + BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_MODE, BROTLI_MODE_GENERIC); + BrotliEncoderSetParameter(ctx->brotli, BROTLI_PARAM_QUALITY, COMPRESS_LEVEL_BROTLI); + } + return 0; +} + +int compress_compress(compress_ctx *ctx, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { + if ((ctx->mode & COMPRESS_GZ) && (ctx->mode & COMPRESS_BR)) { + errno = EINVAL; + return -1; + } + return compress_compress_mode(ctx, ctx->mode, in, in_len, out, out_len, finish); +} + +int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { + if ((mode & COMPRESS_GZ) && (mode & COMPRESS_BR)) { + errno = EINVAL; + return -1; + } else if (mode & COMPRESS_GZ) { + ctx->gzip->next_in = (unsigned char*) in; + ctx->gzip->avail_in = *in_len; + ctx->gzip->next_out = (unsigned char*) out; + ctx->gzip->avail_out = *out_len; + int ret = deflate(ctx->gzip, finish ? Z_FINISH : Z_NO_FLUSH); + *in_len = ctx->gzip->avail_in; + *out_len = ctx->gzip->avail_out; + return ret; + } else if (mode & COMPRESS_BR) { + int ret = BrotliEncoderCompressStream(ctx->brotli, finish ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS, + in_len, (const unsigned char**) &in, out_len, (unsigned char **) &out, NULL); + return (ret == BROTLI_TRUE) ? 0 : -1; + } else { + errno = EINVAL; + return -2; + } +} + +int compress_free(compress_ctx *ctx) { + if (ctx->gzip != NULL) { + deflateEnd(ctx->gzip); + free(ctx->gzip); + ctx->gzip = NULL; + } + if (ctx->brotli != NULL) { + BrotliEncoderDestroyInstance(ctx->brotli); + ctx->brotli = NULL; + } + ctx->mode = 0; + return 0; +} diff --git a/src/lib/compress.h b/src/lib/compress.h new file mode 100644 index 0000000..ebc9abd --- /dev/null +++ b/src/lib/compress.h @@ -0,0 +1,36 @@ +/** + * Necronda Web Server + * Compression interface (header file) + * src/lib/compress.h + * Lorenz Stechauner, 2021-05-05 + */ + +#ifndef NECRONDA_SERVER_COMPRESS_H +#define NECRONDA_SERVER_COMPRESS_H + +#include +#include + +#define COMPRESS_LEVEL_GZIP 9 +#define COMPRESS_LEVEL_BROTLI BROTLI_MAX_QUALITY + +#define COMPRESS_GZ 1 +#define COMPRESS_BR 2 + +typedef struct { + int mode; + z_stream *gzip; + BrotliEncoderState *brotli; +} compress_ctx; + +int compress_init(compress_ctx *ctx, int mode); + +int compress_compress(compress_ctx *ctx, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, + int finish); + +int compress_compress_mode(compress_ctx *ctx, int mode, const char *in, unsigned long *in_len, char *out, + unsigned long *out_len, int finish); + +int compress_free(compress_ctx *ctx); + +#endif //NECRONDA_SERVER_COMPRESS_H diff --git a/src/lib/fastcgi.c b/src/lib/fastcgi.c index 8e36c48..b7c42e3 100644 --- a/src/lib/fastcgi.c +++ b/src/lib/fastcgi.c @@ -7,8 +7,7 @@ #include "fastcgi.h" #include "utils.h" -#include "gzip.h" -#include "brotli.h" +#include "compress.h" #include "../necronda-server.h" #include #include @@ -393,17 +392,16 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) { char comp_out[4096]; int finish_comp = 0; - z_stream gz_state; - BrotliEncoderState *br_state = NULL; + compress_ctx comp_ctx; if (flags & FASTCGI_COMPRESS_BR) { flags &= ~FASTCGI_COMPRESS_GZ; - if (brotli_init(&br_state) != 0) { + if (compress_init(&comp_ctx, COMPRESS_BR) != 0) { print(ERR_STR "Unable to init brotli: %s" CLR_STR, strerror(errno)); flags &= ~FASTCGI_COMPRESS_BR; } } else if (flags & FASTCGI_COMPRESS_GZ) { flags &= ~FASTCGI_COMPRESS_BR; - if (gzip_init(&gz_state) != 0) { + if (compress_init(&comp_ctx, COMPRESS_BR) != 0) { print(ERR_STR "Unable to init gzip: %s" CLR_STR, strerror(errno)); flags &= ~FASTCGI_COMPRESS_GZ; } @@ -459,8 +457,7 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) { content_len = 0; goto out; finish: - if (flags & FASTCGI_COMPRESS_GZ) gzip_free(&gz_state); - if (flags & FASTCGI_COMPRESS_BR) brotli_free(br_state); + if (flags & FASTCGI_COMPRESS) compress_free(&comp_ctx); } if (flags & FASTCGI_CHUNKED) { @@ -479,10 +476,9 @@ int fastcgi_send(fastcgi_conn *conn, sock *client, int flags) { int buf_len = content_len; if (flags & FASTCGI_COMPRESS) { avail_out = sizeof(comp_out); - if (flags & FASTCGI_COMPRESS_GZ) { - gzip_compress(&gz_state, next_in + content_len - avail_in, &avail_in, comp_out, &avail_out, finish_comp); - } else if (flags & FASTCGI_COMPRESS_BR) { - brotli_compress(br_state, next_in + content_len - avail_in, &avail_in, comp_out, &avail_out, finish_comp); + if (flags & FASTCGI_COMPRESS) { + compress_compress(&comp_ctx, next_in + content_len - avail_in, &avail_in, + comp_out, &avail_out, finish_comp); } ptr = comp_out; buf_len = (int) (sizeof(comp_out) - avail_out); diff --git a/src/lib/gzip.c b/src/lib/gzip.c deleted file mode 100644 index 462345e..0000000 --- a/src/lib/gzip.c +++ /dev/null @@ -1,28 +0,0 @@ -/** - * - */ - -#include "gzip.h" - -int gzip_init(z_stream *stream) { - stream->zalloc = Z_NULL; - stream->zfree = Z_NULL; - stream->opaque = Z_NULL; - int ret = deflateInit2(stream, GZIP_LEVEL, Z_DEFLATED, 15 + 16, 9, Z_DEFAULT_STRATEGY); - return (ret == Z_OK) ? 0 : -1; -} - -int gzip_compress(z_stream *stream, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish) { - stream->next_in = (unsigned char*) in; - stream->avail_in = *in_len; - stream->next_out = (unsigned char*) out; - stream->avail_out = *out_len; - int ret = deflate(stream, finish ? Z_FINISH : Z_NO_FLUSH); - *in_len = stream->avail_in; - *out_len = stream->avail_out; - return ret; -} - -int gzip_free(z_stream *stream) { - return deflateEnd(stream); -} diff --git a/src/lib/gzip.h b/src/lib/gzip.h deleted file mode 100644 index 2d573fb..0000000 --- a/src/lib/gzip.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * - */ - -#ifndef NECRONDA_SERVER_GZIP_H -#define NECRONDA_SERVER_GZIP_H - -#include - -#define GZIP_LEVEL 9 - -int gzip_init(z_stream *stream); - -int gzip_compress(z_stream *stream, const char *in, unsigned long *in_len, char *out, unsigned long *out_len, int finish); - -int gzip_free(z_stream *stream); - -#endif //NECRONDA_SERVER_GZIP_H diff --git a/src/lib/rev_proxy.c b/src/lib/rev_proxy.c index 7e3e378..31b61fe 100644 --- a/src/lib/rev_proxy.c +++ b/src/lib/rev_proxy.c @@ -382,6 +382,7 @@ int rev_proxy_send(sock *client, int chunked, unsigned long len_to_send) { char buffer[CHUNK_SIZE]; long len, snd_len; // TODO handle websockets + // TODO compress -> Transfer-Encoding: br/gzip do { if (chunked) { ret = sock_recv(&rev_proxy, buffer, 16, MSG_PEEK);