1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 08:47:17 +01:00
SqMod/vendor/CivetWeb/mod_zlib.inl
2021-09-07 20:55:05 +03:00

282 lines
8.0 KiB
C++

/* Experimental implementation for on-the-fly compression */
#if !defined(USE_ZLIB)
#error "This file must only be included, if USE_ZLIB is set"
#endif
#if !defined(MEM_LEVEL)
#define MEM_LEVEL (8)
#endif
static void *
zalloc(void *opaque, uInt items, uInt size)
{
struct mg_connection *conn = (struct mg_connection *)opaque;
void *ret = mg_calloc_ctx(items, size, conn->phys_ctx);
(void)conn; /* mg_calloc_ctx makro might not need it */
return ret;
}
static void
zfree(void *opaque, void *address)
{
struct mg_connection *conn = (struct mg_connection *)opaque;
(void)conn; /* not required */
mg_free(address);
}
static void
send_compressed_data(struct mg_connection *conn, struct mg_file *filep)
{
int zret;
z_stream zstream;
int do_flush;
unsigned bytes_avail;
unsigned char in_buf[MG_BUF_LEN];
unsigned char out_buf[MG_BUF_LEN];
FILE *in_file = filep->access.fp;
/* Prepare state buffer. User server context memory allocation. */
memset(&zstream, 0, sizeof(zstream));
zstream.zalloc = zalloc;
zstream.zfree = zfree;
zstream.opaque = (void *)conn;
/* Initialize for GZIP compression (MAX_WBITS | 16) */
zret = deflateInit2(&zstream,
Z_BEST_COMPRESSION,
Z_DEFLATED,
MAX_WBITS | 16,
MEM_LEVEL,
Z_DEFAULT_STRATEGY);
if (zret != Z_OK) {
mg_cry_internal(conn,
"GZIP init failed (%i): %s",
zret,
(zstream.msg ? zstream.msg : "<no error message>"));
deflateEnd(&zstream);
return;
}
/* Read until end of file */
do {
zstream.avail_in = fread(in_buf, 1, MG_BUF_LEN, in_file);
if (ferror(in_file)) {
mg_cry_internal(conn, "fread failed: %s", strerror(ERRNO));
(void)deflateEnd(&zstream);
return;
}
do_flush = (feof(in_file) ? Z_FINISH : Z_NO_FLUSH);
zstream.next_in = in_buf;
/* run deflate() on input until output buffer not full, finish
* compression if all of source has been read in */
do {
zstream.avail_out = MG_BUF_LEN;
zstream.next_out = out_buf;
zret = deflate(&zstream, do_flush);
if (zret == Z_STREAM_ERROR) {
/* deflate error */
zret = -97;
break;
}
bytes_avail = MG_BUF_LEN - zstream.avail_out;
if (bytes_avail) {
if (mg_send_chunk(conn, (char *)out_buf, bytes_avail) < 0) {
zret = -98;
break;
}
}
} while (zstream.avail_out == 0);
if (zret < -90) {
/* Forward write error */
break;
}
if (zstream.avail_in != 0) {
/* all input will be used, otherwise GZIP is incomplete */
zret = -99;
break;
}
/* done when last data in file processed */
} while (do_flush != Z_FINISH);
if (zret != Z_STREAM_END) {
/* Error: We did not compress everything. */
mg_cry_internal(conn,
"GZIP incomplete (%i): %s",
zret,
(zstream.msg ? zstream.msg : "<no error message>"));
}
deflateEnd(&zstream);
/* Send "end of chunked data" marker */
mg_write(conn, "0\r\n\r\n", 5);
}
#if defined(USE_WEBSOCKET) && defined(MG_EXPERIMENTAL_INTERFACES)
static int
websocket_deflate_initialize(struct mg_connection *conn, int server)
{
int zret =
deflateInit2(&conn->websocket_deflate_state,
Z_BEST_COMPRESSION,
Z_DEFLATED,
server
? -1 * conn->websocket_deflate_server_max_windows_bits
: -1 * conn->websocket_deflate_client_max_windows_bits,
MEM_LEVEL,
Z_DEFAULT_STRATEGY);
if (zret != Z_OK) {
mg_cry_internal(conn,
"Websocket deflate init failed (%i): %s",
zret,
(conn->websocket_deflate_state.msg
? conn->websocket_deflate_state.msg
: "<no error message>"));
deflateEnd(&conn->websocket_deflate_state);
return zret;
}
zret = inflateInit2(
&conn->websocket_inflate_state,
server ? -1 * conn->websocket_deflate_client_max_windows_bits
: -1 * conn->websocket_deflate_server_max_windows_bits);
if (zret != Z_OK) {
mg_cry_internal(conn,
"Websocket inflate init failed (%i): %s",
zret,
(conn->websocket_inflate_state.msg
? conn->websocket_inflate_state.msg
: "<no error message>"));
inflateEnd(&conn->websocket_inflate_state);
return zret;
}
if ((conn->websocket_deflate_server_no_context_takeover && server)
|| (conn->websocket_deflate_client_no_context_takeover && !server))
conn->websocket_deflate_flush = Z_FULL_FLUSH;
else
conn->websocket_deflate_flush = Z_SYNC_FLUSH;
conn->websocket_deflate_initialized = 1;
return Z_OK;
}
static void
websocket_deflate_negotiate(struct mg_connection *conn)
{
const char *extensions = mg_get_header(conn, "Sec-WebSocket-Extensions");
int val;
if (extensions && !strncmp(extensions, "permessage-deflate", 18)) {
conn->accept_gzip = 1;
conn->websocket_deflate_client_max_windows_bits = 15;
conn->websocket_deflate_server_max_windows_bits = 15;
conn->websocket_deflate_server_no_context_takeover = 0;
conn->websocket_deflate_client_no_context_takeover = 0;
extensions += 18;
while (*extensions != '\0') {
if (*extensions == ';' || *extensions == ' ')
++extensions;
else if (!strncmp(extensions, "server_no_context_takeover", 26)) {
extensions += 26;
conn->websocket_deflate_server_no_context_takeover = 1;
} else if (!strncmp(extensions, "client_no_context_takeover", 26)) {
extensions += 26;
conn->websocket_deflate_client_no_context_takeover = 1;
} else if (!strncmp(extensions, "server_max_window_bits", 22)) {
extensions += 22;
if (*extensions == '=') {
++extensions;
if (*extensions == '"')
++extensions;
val = 0;
while (*extensions >= '0' && *extensions <= '9') {
val = val * 10 + (*extensions - '0');
++extensions;
}
if (val < 9 || val > 15) {
// The permessage-deflate spec specifies that a
// value of 8 is also allowed, but zlib doesn't accept
// that.
mg_cry_internal(conn,
"server-max-window-bits must be "
"between 9 and 15. Got %i",
val);
} else
conn->websocket_deflate_server_max_windows_bits = val;
if (*extensions == '"')
++extensions;
}
} else if (!strncmp(extensions, "client_max_window_bits", 22)) {
extensions += 22;
if (*extensions == '=') {
++extensions;
if (*extensions == '"')
++extensions;
val = 0;
while (*extensions >= '0' && *extensions <= '9') {
val = val * 10 + (*extensions - '0');
++extensions;
}
if (val < 9 || val > 15)
// The permessage-deflate spec specifies that a
// value of 8 is also allowed, but zlib doesn't
// accept that.
mg_cry_internal(conn,
"client-max-window-bits must be "
"between 9 and 15. Got %i",
val);
else
conn->websocket_deflate_client_max_windows_bits = val;
if (*extensions == '"')
++extensions;
}
} else {
mg_cry_internal(conn,
"Unknown parameter %s for permessage-deflate",
extensions);
break;
}
}
} else {
conn->accept_gzip = 0;
}
conn->websocket_deflate_initialized = 0;
}
static void
websocket_deflate_response(struct mg_connection *conn)
{
if (conn->accept_gzip) {
mg_printf(conn,
"Sec-WebSocket-Extensions: permessage-deflate; "
"server_max_window_bits=%i; "
"client_max_window_bits=%i"
"%s%s\r\n",
conn->websocket_deflate_server_max_windows_bits,
conn->websocket_deflate_client_max_windows_bits,
conn->websocket_deflate_client_no_context_takeover
? "; client_no_context_takeover"
: "",
conn->websocket_deflate_server_no_context_takeover
? "; server_no_context_takeover"
: "");
};
}
#endif