| #include "private-libwebsockets.h" |
| #include "extension-deflate-frame.h" |
| #include <stdio.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #define LWS_ZLIB_WINDOW_BITS 15 |
| #define LWS_ZLIB_MEMLEVEL 8 |
| |
| int lws_extension_callback_deflate_frame( |
| struct lws_context *context, |
| struct lws_extension *ext, |
| struct lws *wsi, |
| enum lws_extension_callback_reasons reason, |
| void *user, void *in, size_t len) |
| { |
| struct lws_ext_deflate_frame_conn *conn = |
| (struct lws_ext_deflate_frame_conn *)user; |
| struct lws_tokens *eff_buf = (struct lws_tokens *)in; |
| size_t current_payload, remaining_payload, total_payload; |
| int n; |
| size_t len_so_far; |
| |
| switch (reason) { |
| |
| /* |
| * for deflate-frame, both client and server sides act the same |
| */ |
| |
| case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT: |
| case LWS_EXT_CALLBACK_CONSTRUCT: |
| conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL; |
| conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL; |
| conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL; |
| n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS); |
| if (n != Z_OK) { |
| lwsl_ext("deflateInit returned %d\n", n); |
| return 1; |
| } |
| n = deflateInit2(&conn->zs_out, |
| (context->listen_port ? |
| DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER : |
| DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT), |
| Z_DEFLATED, |
| -LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL, |
| Z_DEFAULT_STRATEGY); |
| if (n != Z_OK) { |
| lwsl_ext("deflateInit2 returned %d\n", n); |
| return 1; |
| } |
| conn->buf_pre_used = 0; |
| conn->buf_pre_length = 0; |
| conn->buf_in_length = sizeof(conn->buf_in); |
| conn->buf_out_length = sizeof(conn->buf_out); |
| conn->compressed_out = 0; |
| conn->buf_pre = NULL; |
| conn->buf_in = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING + |
| conn->buf_in_length + |
| LWS_SEND_BUFFER_POST_PADDING); |
| if (!conn->buf_in) |
| goto bail; |
| conn->buf_out = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING + |
| conn->buf_out_length + |
| LWS_SEND_BUFFER_POST_PADDING); |
| if (!conn->buf_out) |
| goto bail; |
| lwsl_ext("zlibs constructed\n"); |
| break; |
| bail: |
| lwsl_err("Out of mem\n"); |
| (void)inflateEnd(&conn->zs_in); |
| (void)deflateEnd(&conn->zs_out); |
| return -1; |
| |
| case LWS_EXT_CALLBACK_DESTROY: |
| lws_free(conn->buf_pre); |
| lws_free(conn->buf_in); |
| lws_free(conn->buf_out); |
| conn->buf_pre_used = 0; |
| conn->buf_pre_length = 0; |
| conn->buf_in_length = 0; |
| conn->buf_out_length = 0; |
| conn->compressed_out = 0; |
| (void)inflateEnd(&conn->zs_in); |
| (void)deflateEnd(&conn->zs_out); |
| lwsl_ext("zlibs destructed\n"); |
| break; |
| |
| case LWS_EXT_CALLBACK_PAYLOAD_RX: |
| if (!(wsi->u.ws.rsv & 0x40)) |
| return 0; |
| |
| /* |
| * inflate the incoming payload |
| */ |
| current_payload = eff_buf->token_len; |
| |
| remaining_payload = wsi->u.ws.rx_packet_length; |
| if (remaining_payload) { |
| total_payload = conn->buf_pre_used + |
| current_payload + |
| remaining_payload; |
| |
| if (conn->buf_pre_length < total_payload) { |
| conn->buf_pre_length = total_payload; |
| lws_free(conn->buf_pre); |
| conn->buf_pre = lws_malloc(total_payload + 4); |
| if (!conn->buf_pre) { |
| lwsl_err("Out of memory\n"); |
| return -1; |
| } |
| } |
| |
| memcpy(conn->buf_pre + conn->buf_pre_used, |
| eff_buf->token, current_payload); |
| conn->buf_pre_used += current_payload; |
| |
| eff_buf->token = NULL; |
| eff_buf->token_len = 0; |
| |
| return 0; |
| } |
| if (conn->buf_pre_used) { |
| total_payload = conn->buf_pre_used + |
| current_payload; |
| |
| memcpy(conn->buf_pre + conn->buf_pre_used, |
| eff_buf->token, current_payload); |
| conn->buf_pre_used = 0; |
| |
| conn->zs_in.next_in = conn->buf_pre; |
| } else { |
| total_payload = current_payload; |
| |
| conn->zs_in.next_in = (unsigned char *)eff_buf->token; |
| } |
| |
| conn->zs_in.next_in[total_payload + 0] = 0; |
| conn->zs_in.next_in[total_payload + 1] = 0; |
| conn->zs_in.next_in[total_payload + 2] = 0xff; |
| conn->zs_in.next_in[total_payload + 3] = 0xff; |
| |
| conn->zs_in.avail_in = total_payload + 4; |
| |
| conn->zs_in.next_out = |
| conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING; |
| conn->zs_in.avail_out = conn->buf_in_length; |
| |
| while (1) { |
| n = inflate(&conn->zs_in, Z_SYNC_FLUSH); |
| switch (n) { |
| case Z_NEED_DICT: |
| case Z_STREAM_ERROR: |
| case Z_DATA_ERROR: |
| case Z_MEM_ERROR: |
| /* |
| * screwed.. close the connection... |
| * we will get a destroy callback to take care |
| * of closing nicely |
| */ |
| lwsl_info("zlib error inflate %d: %s\n", |
| n, conn->zs_in.msg); |
| return -1; |
| } |
| |
| if (conn->zs_in.avail_out) |
| break; |
| |
| len_so_far = conn->zs_in.next_out - |
| (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING); |
| |
| conn->buf_in_length *= 2; |
| if (conn->buf_in_length > LWS_MAX_ZLIB_CONN_BUFFER) { |
| lwsl_ext("zlib in buffer hit limit %u\n", |
| LWS_MAX_ZLIB_CONN_BUFFER); |
| return -1; |
| } |
| conn->buf_in = lws_realloc(conn->buf_in, |
| LWS_SEND_BUFFER_PRE_PADDING + |
| conn->buf_in_length + |
| LWS_SEND_BUFFER_POST_PADDING); |
| if (!conn->buf_in) { |
| lwsl_err("Out of memory\n"); |
| return -1; |
| } |
| lwsl_debug( |
| "deflate-frame ext RX did realloc to %ld\n", |
| conn->buf_in_length); |
| conn->zs_in.next_out = conn->buf_in + |
| LWS_SEND_BUFFER_PRE_PADDING + len_so_far; |
| conn->zs_in.avail_out = |
| conn->buf_in_length - len_so_far; |
| } |
| |
| /* rewrite the buffer pointers and length */ |
| eff_buf->token = |
| (char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING); |
| eff_buf->token_len = (int)(conn->zs_in.next_out - |
| (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING)); |
| |
| return 0; |
| |
| case LWS_EXT_CALLBACK_PAYLOAD_TX: |
| /* |
| * deflate the outgoing payload |
| */ |
| current_payload = eff_buf->token_len; |
| |
| conn->zs_out.next_in = (unsigned char *)eff_buf->token; |
| conn->zs_out.avail_in = current_payload; |
| |
| conn->zs_out.next_out = |
| conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING; |
| conn->zs_out.avail_out = conn->buf_out_length; |
| |
| while (1) { |
| n = deflate(&conn->zs_out, Z_SYNC_FLUSH); |
| if (n == Z_STREAM_ERROR) { |
| /* |
| * screwed.. close the connection... we will |
| * get a destroy callback to take care of |
| * closing nicely |
| */ |
| lwsl_ext("zlib error deflate\n"); |
| |
| return -1; |
| } |
| |
| if (conn->zs_out.avail_out) |
| break; |
| |
| len_so_far = (conn->zs_out.next_out - |
| (conn->buf_out + |
| LWS_SEND_BUFFER_PRE_PADDING)); |
| conn->buf_out_length *= 2; |
| if (conn->buf_out_length > LWS_MAX_ZLIB_CONN_BUFFER) { |
| lwsl_ext("zlib out hit limit %u\n", |
| LWS_MAX_ZLIB_CONN_BUFFER); |
| return -1; |
| } |
| conn->buf_out = lws_realloc(conn->buf_out, |
| LWS_SEND_BUFFER_PRE_PADDING + |
| conn->buf_out_length + |
| LWS_SEND_BUFFER_POST_PADDING); |
| if (!conn->buf_out) { |
| lwsl_err("Out of memory\n"); |
| return -1; |
| } |
| lwsl_debug( |
| "deflate-frame ext TX did realloc to %ld\n", |
| conn->buf_out_length); |
| |
| conn->zs_out.next_out = (conn->buf_out + |
| LWS_SEND_BUFFER_PRE_PADDING + len_so_far); |
| conn->zs_out.avail_out = |
| (conn->buf_out_length - len_so_far); |
| } |
| |
| conn->compressed_out = 1; |
| |
| /* rewrite the buffer pointers and length */ |
| eff_buf->token = (char *)(conn->buf_out + |
| LWS_SEND_BUFFER_PRE_PADDING); |
| eff_buf->token_len = (int)(conn->zs_out.next_out - |
| (conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4; |
| |
| return 0; |
| |
| case LWS_EXT_CALLBACK_PACKET_TX_PRESEND: |
| if (conn->compressed_out) { |
| conn->compressed_out = 0; |
| *((unsigned char *)eff_buf->token) |= 0x40; |
| } |
| break; |
| |
| case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION: |
| /* Avoid x-webkit-deflate-frame extension on client */ |
| if (!strcmp((char *)in, "x-webkit-deflate-frame")) |
| return 1; |
| break; |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |