blob: c8ba423c3f792017606019ad1966c8264f818931 [file] [log] [blame]
David Galeano85a09212013-01-09 18:21:33 +08001#include "private-libwebsockets.h"
2#include "extension-deflate-frame.h"
3#include <stdio.h>
4#include <string.h>
5#include <assert.h>
6
7#define LWS_ZLIB_WINDOW_BITS 15
8#define LWS_ZLIB_MEMLEVEL 8
9
David Galeano01d02562013-01-10 09:51:15 +080010#define MIN_SIZE_TO_DEFLATE 4
11
12
David Galeano85a09212013-01-09 18:21:33 +080013int lws_extension_callback_deflate_frame(
14 struct libwebsocket_context *context,
15 struct libwebsocket_extension *ext,
16 struct libwebsocket *wsi,
17 enum libwebsocket_extension_callback_reasons reason,
18 void *user, void *in, size_t len)
19{
20 struct lws_ext_deflate_frame_conn *conn =
David Galeano2605ffe2013-01-10 09:41:06 +080021 (struct lws_ext_deflate_frame_conn *)user;
David Galeano85a09212013-01-09 18:21:33 +080022 struct lws_tokens *eff_buf = (struct lws_tokens *)in;
David Galeano2605ffe2013-01-10 09:41:06 +080023 size_t current_payload, remaining_payload, total_payload;
David Galeano85a09212013-01-09 18:21:33 +080024 int n;
25
26 switch (reason) {
27
28 /*
29 * for deflate-frame, both client and server sides act the same
30 */
31
32 case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
33 case LWS_EXT_CALLBACK_CONSTRUCT:
34 conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
35 conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
36 conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
37 n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
38 if (n != Z_OK) {
Andy Green43db0452013-01-10 19:50:35 +080039 lwsl_ext("deflateInit returned %d", n);
David Galeano85a09212013-01-09 18:21:33 +080040 return 1;
41 }
42 n = deflateInit2(&conn->zs_out,
David Galeanoed3bc902013-01-10 10:24:32 +080043 (context->listen_port ?
44 DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER :
45 DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT),
46 Z_DEFLATED,
David Galeano85a09212013-01-09 18:21:33 +080047 -LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
David Galeano2605ffe2013-01-10 09:41:06 +080048 Z_DEFAULT_STRATEGY);
David Galeano85a09212013-01-09 18:21:33 +080049 if (n != Z_OK) {
Andy Green43db0452013-01-10 19:50:35 +080050 lwsl_ext("deflateInit2 returned %d", n);
David Galeano85a09212013-01-09 18:21:33 +080051 return 1;
52 }
David Galeano2605ffe2013-01-10 09:41:06 +080053 conn->buf_pre_used = 0;
54 conn->buf_pre_length = 0;
David Galeano85a09212013-01-09 18:21:33 +080055 conn->buf_in_length = MAX_USER_RX_BUFFER;
56 conn->buf_out_length = MAX_USER_RX_BUFFER;
57 conn->compressed_out = 0;
David Galeano2605ffe2013-01-10 09:41:06 +080058 conn->buf_pre = NULL;
59 conn->buf_in = (unsigned char *)
60 malloc(LWS_SEND_BUFFER_PRE_PADDING +
61 conn->buf_in_length +
62 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +080063 if (!conn->buf_in)
64 goto bail;
David Galeano2605ffe2013-01-10 09:41:06 +080065 conn->buf_out = (unsigned char *)
66 malloc(LWS_SEND_BUFFER_PRE_PADDING +
67 conn->buf_out_length +
68 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +080069 if (!conn->buf_out)
70 goto bail;
Andy Green43db0452013-01-10 19:50:35 +080071 lwsl_ext("zlibs constructed\n");
David Galeano85a09212013-01-09 18:21:33 +080072 break;
Andy Green988bd982013-01-10 12:26:13 +080073bail:
Andy Green43db0452013-01-10 19:50:35 +080074 lwsl_err("Out of mem\n");
Andy Green988bd982013-01-10 12:26:13 +080075 (void)inflateEnd(&conn->zs_in);
76 (void)deflateEnd(&conn->zs_out);
77 return -1;
David Galeano85a09212013-01-09 18:21:33 +080078
79 case LWS_EXT_CALLBACK_DESTROY:
David Galeano2605ffe2013-01-10 09:41:06 +080080 if (conn->buf_pre)
David Galeano2605ffe2013-01-10 09:41:06 +080081 free(conn->buf_pre);
David Galeano85a09212013-01-09 18:21:33 +080082 free(conn->buf_in);
83 free(conn->buf_out);
David Galeano2605ffe2013-01-10 09:41:06 +080084 conn->buf_pre_used = 0;
85 conn->buf_pre_length = 0;
David Galeano85a09212013-01-09 18:21:33 +080086 conn->buf_in_length = 0;
87 conn->buf_out_length = 0;
88 conn->compressed_out = 0;
89 (void)inflateEnd(&conn->zs_in);
90 (void)deflateEnd(&conn->zs_out);
Andy Green43db0452013-01-10 19:50:35 +080091 lwsl_ext("zlibs destructed\n");
David Galeano85a09212013-01-09 18:21:33 +080092 break;
93
94 case LWS_EXT_CALLBACK_PAYLOAD_RX:
David Galeano2605ffe2013-01-10 09:41:06 +080095 if (!(wsi->rsv & 0x40))
David Galeano85a09212013-01-09 18:21:33 +080096 return 0;
97
98 /*
99 * inflate the incoming payload
100 */
David Galeano2605ffe2013-01-10 09:41:06 +0800101 current_payload = eff_buf->token_len;
David Galeano85a09212013-01-09 18:21:33 +0800102
David Galeano2605ffe2013-01-10 09:41:06 +0800103 remaining_payload = wsi->rx_packet_length;
104 if (remaining_payload) {
105 total_payload = conn->buf_pre_used +
106 current_payload +
107 remaining_payload;
108
109 if (conn->buf_pre_length < total_payload) {
110 conn->buf_pre_length = total_payload;
111 if (conn->buf_pre)
112 free(conn->buf_pre);
113 conn->buf_pre =
114 (unsigned char *)malloc(total_payload + 4);
Andy Green988bd982013-01-10 12:26:13 +0800115 if (!conn->buf_pre) {
Andy Green43db0452013-01-10 19:50:35 +0800116 lwsl_err("Out of memory\n");
Andy Green988bd982013-01-10 12:26:13 +0800117 return -1;
118 }
David Galeano2605ffe2013-01-10 09:41:06 +0800119 }
120
121 memcpy(conn->buf_pre + conn->buf_pre_used,
122 eff_buf->token, current_payload);
123 conn->buf_pre_used += current_payload;
124
125 eff_buf->token = NULL;
126 eff_buf->token_len = 0;
127
128 return 0;
129 }
130 if (conn->buf_pre_used) {
131 total_payload = conn->buf_pre_used +
132 current_payload;
133
134 memcpy(conn->buf_pre + conn->buf_pre_used,
135 eff_buf->token, current_payload);
136 conn->buf_pre_used = 0;
137
138 conn->zs_in.next_in = conn->buf_pre;
139 } else {
140 total_payload = current_payload;
141
142 conn->zs_in.next_in = (unsigned char *)eff_buf->token;
143 }
144
145 conn->zs_in.next_in[total_payload + 0] = 0;
146 conn->zs_in.next_in[total_payload + 1] = 0;
147 conn->zs_in.next_in[total_payload + 2] = 0xff;
148 conn->zs_in.next_in[total_payload + 3] = 0xff;
149
150 conn->zs_in.avail_in = total_payload + 4;
David Galeano85a09212013-01-09 18:21:33 +0800151
152 conn->zs_in.next_out = conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
153 conn->zs_in.avail_out = conn->buf_in_length;
154
Andy Green988bd982013-01-10 12:26:13 +0800155 n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
156 switch (n) {
157 case Z_NEED_DICT:
158 case Z_STREAM_ERROR:
159 case Z_DATA_ERROR:
160 case Z_MEM_ERROR:
161 /*
162 * screwed.. close the connection... we will get a
163 * destroy callback to take care of closing nicely
164 */
Andy Green43db0452013-01-10 19:50:35 +0800165 lwsl_ext("zlib error inflate %d: %s",
Andy Green988bd982013-01-10 12:26:13 +0800166 n, conn->zs_in.msg);
167 return -1;
David Galeano85a09212013-01-09 18:21:33 +0800168 }
169
170 /* rewrite the buffer pointers and length */
171 eff_buf->token = (char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
David Galeano2605ffe2013-01-10 09:41:06 +0800172 eff_buf->token_len = (int)(conn->zs_in.next_out -
173 (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
David Galeano85a09212013-01-09 18:21:33 +0800174
175 return 0;
176
177 case LWS_EXT_CALLBACK_PAYLOAD_TX:
178 /*
179 * deflate the outgoing payload
180 */
David Galeano01d02562013-01-10 09:51:15 +0800181 current_payload = eff_buf->token_len;
182
183 if (current_payload < MIN_SIZE_TO_DEFLATE)
184 return 0;
185
David Galeano85a09212013-01-09 18:21:33 +0800186 conn->zs_out.next_in = (unsigned char *)eff_buf->token;
David Galeano01d02562013-01-10 09:51:15 +0800187 conn->zs_out.avail_in = current_payload;
David Galeano85a09212013-01-09 18:21:33 +0800188
189 conn->zs_out.next_out = conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
190 conn->zs_out.avail_out = conn->buf_out_length;
191
David Galeano2605ffe2013-01-10 09:41:06 +0800192 while (1) {
David Galeano85a09212013-01-09 18:21:33 +0800193 n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
194 if (n == Z_STREAM_ERROR) {
195 /*
196 * screwed.. close the connection... we will get a
197 * destroy callback to take care of closing nicely
198 */
Andy Green43db0452013-01-10 19:50:35 +0800199 lwsl_ext("zlib error deflate");
David Galeano85a09212013-01-09 18:21:33 +0800200
201 return -1;
202 }
203
Andy Green988bd982013-01-10 12:26:13 +0800204 /*
205 * AG: uncertain about this log buffer expansion approach...
206 * same approach in Rx led to memory runaway OOM
207 */
David Galeano2605ffe2013-01-10 09:41:06 +0800208 if (!conn->zs_out.avail_out) {
209 size_t len_so_far = (conn->zs_out.next_out -
210 (conn->buf_out +
211 LWS_SEND_BUFFER_PRE_PADDING));
David Galeano85a09212013-01-09 18:21:33 +0800212 unsigned char *new_buf;
213 conn->buf_out_length *= 2;
David Galeano2605ffe2013-01-10 09:41:06 +0800214 new_buf = (unsigned char *)
215 malloc(LWS_SEND_BUFFER_PRE_PADDING +
216 conn->buf_out_length +
David Galeano01d02562013-01-10 09:51:15 +0800217 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +0800218 if (!new_buf) {
Andy Green43db0452013-01-10 19:50:35 +0800219 lwsl_err("Out of memory\n");
Andy Green988bd982013-01-10 12:26:13 +0800220 return -1;
221 }
David Galeano85a09212013-01-09 18:21:33 +0800222 memcpy(new_buf + LWS_SEND_BUFFER_PRE_PADDING,
David Galeano01d02562013-01-10 09:51:15 +0800223 conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING,
224 len_so_far);
David Galeano85a09212013-01-09 18:21:33 +0800225 free(conn->buf_out);
226 conn->buf_out = new_buf;
David Galeano2605ffe2013-01-10 09:41:06 +0800227 conn->zs_out.next_out = (new_buf +
228 LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
229 conn->zs_out.avail_out =
230 (conn->buf_out_length - len_so_far);
231 } else
David Galeano85a09212013-01-09 18:21:33 +0800232 break;
David Galeano85a09212013-01-09 18:21:33 +0800233 }
234
235 conn->compressed_out = 1;
236
237 /* rewrite the buffer pointers and length */
David Galeano2605ffe2013-01-10 09:41:06 +0800238 eff_buf->token = (char *)(conn->buf_out +
239 LWS_SEND_BUFFER_PRE_PADDING);
240 eff_buf->token_len = (int)(conn->zs_out.next_out -
241 (conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4;
David Galeano85a09212013-01-09 18:21:33 +0800242
243 return 0;
244
245 case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
David Galeano2605ffe2013-01-10 09:41:06 +0800246 if (conn->compressed_out) {
David Galeano85a09212013-01-09 18:21:33 +0800247 conn->compressed_out = 0;
248 *((unsigned char *)eff_buf->token) |= 0x40;
249 }
250 break;
251
252 case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
253 /* Avoid x-webkit-deflate-frame extension on client */
David Galeano2605ffe2013-01-10 09:41:06 +0800254 if (!strcmp((char *)in, "x-webkit-deflate-frame"))
David Galeano85a09212013-01-09 18:21:33 +0800255 return 1;
David Galeano85a09212013-01-09 18:21:33 +0800256 break;
257
258 default:
259 break;
260 }
261
262 return 0;
263}
David Galeano2605ffe2013-01-10 09:41:06 +0800264