6#define _POSIX_C_SOURCE 200809L
11#include <brotli/encode.h>
17#include "../../net/http/async_internal.h"
19#define CWIST_MAX_BACKENDS 8
22static int g_backend_count = 0;
23static size_t g_compress_min_size = 0;
32 g_backends[g_backend_count++] = backend;
50static int zlib_gzip_init(
void **state) {
68static int zlib_deflate_init(
void **state) {
90static int zlib_compress(
void *state,
const char *
in,
size_t in_len,
char *out,
size_t *out_len,
96 zs->avail_out = (
uInt)(*out_len);
105 if (
rc !=
Z_OK ||
zs->avail_in != 0)
return -1;
107 *out_len = (
size_t)(*out_len -
zs->avail_out);
119static int zlib_finish(
void *state,
char *out,
size_t *out_len) {
124 zs->avail_out = (
uInt)(*out_len);
129 *out_len = (
size_t)(*out_len -
zs->avail_out);
137static void zlib_cleanup(
void *state) {
147 .init = zlib_gzip_init,
148 .compress = zlib_compress,
149 .finish = zlib_finish,
150 .cleanup = zlib_cleanup,
155 .init = zlib_deflate_init,
156 .compress = zlib_compress,
157 .finish = zlib_finish,
158 .cleanup = zlib_cleanup,
166 return &cwist_backend_gzip;
174 return &cwist_backend_deflate;
186} cwist_brotli_state_t;
194static int brotli_init(
void **state) {
195 cwist_brotli_state_t *
bs = (cwist_brotli_state_t *)
calloc(1,
sizeof(*
bs));
208static int brotli_ensure_accum(cwist_brotli_state_t *
bs,
size_t need) {
210 size_t required =
bs->accum_len +
need;
211 if (required >
bs->accum_cap) {
212 size_t new_cap =
bs->accum_cap ?
bs->accum_cap * 2 : 4096;
239static int brotli_compress(
void *state,
const char *
in,
size_t in_len,
char *out,
size_t *out_len,
241 cwist_brotli_state_t *
bs = (cwist_brotli_state_t *)state;
242 if (!
bs || !out || !out_len)
return -1;
249 if (brotli_ensure_accum(
bs,
in_len) != 0)
return -1;
260 if (
bs->accum_len > 0) {
314static int brotli_finish(
void *state,
char *out,
size_t *out_len) {
315 cwist_brotli_state_t *
bs = (cwist_brotli_state_t *)state;
316 if (!
bs || !out || !out_len)
return -1;
330static void brotli_cleanup(
void *state) {
331 cwist_brotli_state_t *
bs = (cwist_brotli_state_t *)state;
341 .compress = brotli_compress,
342 .finish = brotli_finish,
343 .cleanup = brotli_cleanup,
351 return &cwist_backend_brotli;
372static int zstd_init(
void **state) {
373 cwist_zstd_state_t *
zs = (cwist_zstd_state_t *)
calloc(1,
sizeof(*
zs));
397static int zstd_ensure_accum(cwist_zstd_state_t *
zs,
size_t need) {
398 size_t required =
zs->accum_len +
need;
399 if (required >
zs->accum_cap) {
400 size_t new_cap =
zs->accum_cap ?
zs->accum_cap * 2 : 4096;
427static int zstd_compress(
void *state,
const char *
in,
size_t in_len,
char *out,
size_t *out_len,
429 cwist_zstd_state_t *
zs = (cwist_zstd_state_t *)state;
430 if (!
zs || !out || !out_len)
return -1;
437 if (zstd_ensure_accum(
zs,
in_len) != 0)
return -1;
451 char *out_buf = (
char *)
malloc(out_cap);
470 zs->out_len =
ob.pos;
473 size_t to_copy = (
ob.pos < *out_len) ?
ob.pos : *out_len;
488static int zstd_finish(
void *state,
char *out,
size_t *out_len) {
489 cwist_zstd_state_t *
zs = (cwist_zstd_state_t *)state;
490 if (!
zs || !out || !out_len)
return -1;
503static void zstd_cleanup(
void *state) {
504 cwist_zstd_state_t *
zs = (cwist_zstd_state_t *)state;
515 .compress = zstd_compress,
516 .finish = zstd_finish,
517 .cleanup = zstd_cleanup,
525 return &cwist_backend_zstd;
536static int str_contains_ci(
const char *
haystack,
const char *
needle) {
540 if (
nlen == 0)
return 1;
547 if (
a >=
'A' &&
a <=
'Z')
a =
a -
'A' +
'a';
548 if (b >=
'A' && b <=
'Z') b = b -
'A' +
'a';
551 if (
j ==
nlen)
return 1;
565 for (
int i = 0;
i < g_backend_count; ++
i) {
567 return g_backends[
i];
585 if ((*curr)->key && (*curr)->key->data &&
strcasecmp((*curr)->key->data, key) == 0) {
595 curr = &(*curr)->next;
624 if (!backend)
return;
628 if (res->
body->
size < g_compress_min_size)
return;
631 if (backend->
init(&state) != 0)
return;
633 size_t out_cap = res->
body->
size + 256;
694 if (
wrote < tail)
break;
703 remove_header(&res->
headers,
"Content-Length");
715 return cwist_mw_compress_handler;
Memory allocation wrappers backed by libttak.
void cwist_free(void *ptr)
Free memory obtained via cwist_alloc/cwist_strdup/etc.
void * cwist_realloc(void *ptr, size_t new_size)
Resize an existing allocation (libttak tracked).
void * cwist_malloc(size_t size)
Allocate zeroed memory via the CWIST owner context.
bool cwist_async_res_deferred(const cwist_http_response *res)
const cwist_compress_backend * cwist_compress_backend_zstd(void)
Get the built-in zstd backend.
const cwist_compress_backend * cwist_compress_backend_deflate(void)
Get the built-in deflate backend.
void cwist_compress_register_backend(const cwist_compress_backend *backend)
Register a compression backend for use by the middleware.
const cwist_compress_backend * cwist_compress_backend_brotli(void)
Get the built-in Brotli backend.
cwist_middleware_func cwist_mw_compress(size_t min_body_size)
Create the response compression middleware.
void cwist_compress_unregister_all(void)
Remove all registered compression backends.
#define CWIST_MAX_BACKENDS
const cwist_compress_backend * cwist_compress_backend_gzip(void)
Get the built-in gzip backend.
Compression middleware with swappable backend interface.
cwist_error_t cwist_sstring_assign_len(cwist_sstring *str, const char *data, size_t len)
Assign raw bytes with length.
void cwist_sstring_destroy(cwist_sstring *str)
Destroy an sstring instance.
char * cwist_http_header_get(cwist_http_header_node *head, const char *key)
Find a header value by key.
cwist_error_t cwist_http_header_add(cwist_http_header_node **head, const char *key, const char *value)
Add a header to the list.
HTTP Protocol Definitions and Helpers.
Compression backend vtable.
const char * encoding_name
int(* init)(void **state)
Initialize compressor state.
int(* compress)(void *state, const char *in, size_t in_len, char *out, size_t *out_len, int flush)
Compress one chunk.
int(* finish)(void *state, char *out, size_t *out_len)
Finish compression and write any trailing bytes.
void(* cleanup)(void *state)
Clean up compressor state.
cwist_http_header_node * headers
HTTP Response Object. Supports standard string body or Zero-Copy pointer body.
bool use_file_stream
Fast File Streaming.
cwist_http_status_t status_code
cwist_http_header_node * headers
bool is_ptr_body
Zero-Copy Pointer Body.
char * data
Access directly only when raw handling is necessary.
void(* cwist_handler_func)(cwist_http_request *req, cwist_http_response *res)
Function pointer type for HTTP route handlers.
void(* cwist_middleware_func)(cwist_http_request *req, cwist_http_response *res, cwist_handler_func next)
Middleware type that receives req/res pair and the next stage in the chain.