123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- /* 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 macro 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
|