blob: b206476bcd311e97a8eedc28e897275d75a450b7 [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
10int lws_extension_callback_deflate_frame(
11 struct libwebsocket_context *context,
12 struct libwebsocket_extension *ext,
13 struct libwebsocket *wsi,
14 enum libwebsocket_extension_callback_reasons reason,
15 void *user, void *in, size_t len)
16{
17 struct lws_ext_deflate_frame_conn *conn =
David Galeano2605ffe2013-01-10 09:41:06 +080018 (struct lws_ext_deflate_frame_conn *)user;
David Galeano85a09212013-01-09 18:21:33 +080019 struct lws_tokens *eff_buf = (struct lws_tokens *)in;
David Galeano2605ffe2013-01-10 09:41:06 +080020 size_t current_payload, remaining_payload, total_payload;
David Galeano85a09212013-01-09 18:21:33 +080021 int n;
Andy Greend678ea32013-01-12 23:09:36 +080022 size_t len_so_far;
David Galeano85a09212013-01-09 18:21:33 +080023
24 switch (reason) {
25
26 /*
27 * for deflate-frame, both client and server sides act the same
28 */
29
30 case LWS_EXT_CALLBACK_CLIENT_CONSTRUCT:
31 case LWS_EXT_CALLBACK_CONSTRUCT:
32 conn->zs_in.zalloc = conn->zs_out.zalloc = Z_NULL;
33 conn->zs_in.zfree = conn->zs_out.zfree = Z_NULL;
34 conn->zs_in.opaque = conn->zs_out.opaque = Z_NULL;
35 n = inflateInit2(&conn->zs_in, -LWS_ZLIB_WINDOW_BITS);
36 if (n != Z_OK) {
Andy Greenf7609e92013-01-14 13:10:55 +080037 lwsl_ext("deflateInit returned %d\n", n);
David Galeano85a09212013-01-09 18:21:33 +080038 return 1;
39 }
40 n = deflateInit2(&conn->zs_out,
David Galeanoed3bc902013-01-10 10:24:32 +080041 (context->listen_port ?
42 DEFLATE_FRAME_COMPRESSION_LEVEL_SERVER :
43 DEFLATE_FRAME_COMPRESSION_LEVEL_CLIENT),
44 Z_DEFLATED,
David Galeano85a09212013-01-09 18:21:33 +080045 -LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
David Galeano2605ffe2013-01-10 09:41:06 +080046 Z_DEFAULT_STRATEGY);
David Galeano85a09212013-01-09 18:21:33 +080047 if (n != Z_OK) {
Andy Greenf7609e92013-01-14 13:10:55 +080048 lwsl_ext("deflateInit2 returned %d\n", n);
David Galeano85a09212013-01-09 18:21:33 +080049 return 1;
50 }
David Galeano2605ffe2013-01-10 09:41:06 +080051 conn->buf_pre_used = 0;
52 conn->buf_pre_length = 0;
Andy Greenb5b23192013-02-11 17:13:32 +080053 conn->buf_in_length = sizeof(conn->buf_in);
54 conn->buf_out_length = sizeof(conn->buf_out);
David Galeano85a09212013-01-09 18:21:33 +080055 conn->compressed_out = 0;
David Galeano2605ffe2013-01-10 09:41:06 +080056 conn->buf_pre = NULL;
57 conn->buf_in = (unsigned char *)
58 malloc(LWS_SEND_BUFFER_PRE_PADDING +
59 conn->buf_in_length +
60 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +080061 if (!conn->buf_in)
62 goto bail;
David Galeano2605ffe2013-01-10 09:41:06 +080063 conn->buf_out = (unsigned char *)
64 malloc(LWS_SEND_BUFFER_PRE_PADDING +
65 conn->buf_out_length +
66 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +080067 if (!conn->buf_out)
68 goto bail;
Andy Green43db0452013-01-10 19:50:35 +080069 lwsl_ext("zlibs constructed\n");
David Galeano85a09212013-01-09 18:21:33 +080070 break;
Andy Green988bd982013-01-10 12:26:13 +080071bail:
Andy Green43db0452013-01-10 19:50:35 +080072 lwsl_err("Out of mem\n");
Andy Green988bd982013-01-10 12:26:13 +080073 (void)inflateEnd(&conn->zs_in);
74 (void)deflateEnd(&conn->zs_out);
75 return -1;
David Galeano85a09212013-01-09 18:21:33 +080076
77 case LWS_EXT_CALLBACK_DESTROY:
David Galeano2605ffe2013-01-10 09:41:06 +080078 if (conn->buf_pre)
David Galeano2605ffe2013-01-10 09:41:06 +080079 free(conn->buf_pre);
David Galeano85a09212013-01-09 18:21:33 +080080 free(conn->buf_in);
81 free(conn->buf_out);
David Galeano2605ffe2013-01-10 09:41:06 +080082 conn->buf_pre_used = 0;
83 conn->buf_pre_length = 0;
David Galeano85a09212013-01-09 18:21:33 +080084 conn->buf_in_length = 0;
85 conn->buf_out_length = 0;
86 conn->compressed_out = 0;
87 (void)inflateEnd(&conn->zs_in);
88 (void)deflateEnd(&conn->zs_out);
Andy Green43db0452013-01-10 19:50:35 +080089 lwsl_ext("zlibs destructed\n");
David Galeano85a09212013-01-09 18:21:33 +080090 break;
91
92 case LWS_EXT_CALLBACK_PAYLOAD_RX:
Andy Green623a98d2013-01-21 11:04:23 +080093 if (!(wsi->u.ws.rsv & 0x40))
David Galeano85a09212013-01-09 18:21:33 +080094 return 0;
95
96 /*
97 * inflate the incoming payload
98 */
David Galeano2605ffe2013-01-10 09:41:06 +080099 current_payload = eff_buf->token_len;
David Galeano85a09212013-01-09 18:21:33 +0800100
Andy Green623a98d2013-01-21 11:04:23 +0800101 remaining_payload = wsi->u.ws.rx_packet_length;
David Galeano2605ffe2013-01-10 09:41:06 +0800102 if (remaining_payload) {
103 total_payload = conn->buf_pre_used +
104 current_payload +
105 remaining_payload;
106
107 if (conn->buf_pre_length < total_payload) {
108 conn->buf_pre_length = total_payload;
109 if (conn->buf_pre)
110 free(conn->buf_pre);
111 conn->buf_pre =
112 (unsigned char *)malloc(total_payload + 4);
Andy Green988bd982013-01-10 12:26:13 +0800113 if (!conn->buf_pre) {
Andy Green43db0452013-01-10 19:50:35 +0800114 lwsl_err("Out of memory\n");
Andy Green988bd982013-01-10 12:26:13 +0800115 return -1;
116 }
David Galeano2605ffe2013-01-10 09:41:06 +0800117 }
118
119 memcpy(conn->buf_pre + conn->buf_pre_used,
120 eff_buf->token, current_payload);
121 conn->buf_pre_used += current_payload;
122
123 eff_buf->token = NULL;
124 eff_buf->token_len = 0;
125
126 return 0;
127 }
128 if (conn->buf_pre_used) {
129 total_payload = conn->buf_pre_used +
130 current_payload;
131
132 memcpy(conn->buf_pre + conn->buf_pre_used,
133 eff_buf->token, current_payload);
134 conn->buf_pre_used = 0;
135
136 conn->zs_in.next_in = conn->buf_pre;
137 } else {
138 total_payload = current_payload;
139
140 conn->zs_in.next_in = (unsigned char *)eff_buf->token;
141 }
142
143 conn->zs_in.next_in[total_payload + 0] = 0;
144 conn->zs_in.next_in[total_payload + 1] = 0;
145 conn->zs_in.next_in[total_payload + 2] = 0xff;
146 conn->zs_in.next_in[total_payload + 3] = 0xff;
147
148 conn->zs_in.avail_in = total_payload + 4;
David Galeano85a09212013-01-09 18:21:33 +0800149
Andy Greenb5b23192013-02-11 17:13:32 +0800150 conn->zs_in.next_out =
151 conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
152 conn->zs_in.avail_out = conn->buf_in_length;
David Galeano85a09212013-01-09 18:21:33 +0800153
Andy Greenb5b23192013-02-11 17:13:32 +0800154 while (1) {
155 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:
Andy Greend678ea32013-01-12 23:09:36 +0800161 /*
Andy Greenb5b23192013-02-11 17:13:32 +0800162 * screwed.. close the connection...
163 * we will get a destroy callback to take care
164 * of closing nicely
Andy Greend678ea32013-01-12 23:09:36 +0800165 */
Andy Green706961d2013-01-17 16:50:35 +0800166 lwsl_err("zlib error inflate %d: %s\n",
Andy Greend678ea32013-01-12 23:09:36 +0800167 n, conn->zs_in.msg);
168 return -1;
169 }
170
171 if (conn->zs_in.avail_out)
172 break;
173
174 len_so_far = conn->zs_in.next_out -
175 (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
176
177 conn->buf_in_length *= 2;
178 if (conn->buf_in_length > LWS_MAX_ZLIB_CONN_BUFFER) {
Andy Greenb5b23192013-02-11 17:13:32 +0800179 lwsl_ext("zlib in buffer hit limit %u\n",
Andy Greend678ea32013-01-12 23:09:36 +0800180 LWS_MAX_ZLIB_CONN_BUFFER);
181 return -1;
182 }
183 conn->buf_in = (unsigned char *)realloc(conn->buf_in,
184 LWS_SEND_BUFFER_PRE_PADDING +
185 conn->buf_in_length +
186 LWS_SEND_BUFFER_POST_PADDING);
187 if (!conn->buf_in) {
188 lwsl_err("Out of memory\n");
189 return -1;
190 }
191 lwsl_debug(
192 "deflate-frame ext RX did realloc to %ld\n",
193 conn->buf_in_length);
194 conn->zs_in.next_out = conn->buf_in +
Andy Greenb5b23192013-02-11 17:13:32 +0800195 LWS_SEND_BUFFER_PRE_PADDING + len_so_far;
Andy Greend678ea32013-01-12 23:09:36 +0800196 conn->zs_in.avail_out =
Andy Greenb5b23192013-02-11 17:13:32 +0800197 conn->buf_in_length - len_so_far;
198 }
David Galeano85a09212013-01-09 18:21:33 +0800199
200 /* rewrite the buffer pointers and length */
Andy Greenb5b23192013-02-11 17:13:32 +0800201 eff_buf->token =
202 (char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
David Galeano2605ffe2013-01-10 09:41:06 +0800203 eff_buf->token_len = (int)(conn->zs_in.next_out -
204 (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
David Galeano85a09212013-01-09 18:21:33 +0800205
206 return 0;
207
208 case LWS_EXT_CALLBACK_PAYLOAD_TX:
209 /*
210 * deflate the outgoing payload
211 */
David Galeano01d02562013-01-10 09:51:15 +0800212 current_payload = eff_buf->token_len;
213
David Galeano85a09212013-01-09 18:21:33 +0800214 conn->zs_out.next_in = (unsigned char *)eff_buf->token;
David Galeano01d02562013-01-10 09:51:15 +0800215 conn->zs_out.avail_in = current_payload;
David Galeano85a09212013-01-09 18:21:33 +0800216
Andy Greenb5b23192013-02-11 17:13:32 +0800217 conn->zs_out.next_out =
218 conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
David Galeano85a09212013-01-09 18:21:33 +0800219 conn->zs_out.avail_out = conn->buf_out_length;
220
David Galeano2605ffe2013-01-10 09:41:06 +0800221 while (1) {
David Galeano85a09212013-01-09 18:21:33 +0800222 n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
223 if (n == Z_STREAM_ERROR) {
224 /*
Andy Greenb5b23192013-02-11 17:13:32 +0800225 * screwed.. close the connection... we will
226 * get a destroy callback to take care of
227 * closing nicely
David Galeano85a09212013-01-09 18:21:33 +0800228 */
Andy Greenf7609e92013-01-14 13:10:55 +0800229 lwsl_ext("zlib error deflate\n");
David Galeano85a09212013-01-09 18:21:33 +0800230
231 return -1;
232 }
233
Andy Greend678ea32013-01-12 23:09:36 +0800234 if (conn->zs_out.avail_out)
235 break;
236
237 len_so_far = (conn->zs_out.next_out -
David Galeano2605ffe2013-01-10 09:41:06 +0800238 (conn->buf_out +
239 LWS_SEND_BUFFER_PRE_PADDING));
Andy Greend678ea32013-01-12 23:09:36 +0800240 conn->buf_out_length *= 2;
241 if (conn->buf_out_length > LWS_MAX_ZLIB_CONN_BUFFER) {
Andy Greenb5b23192013-02-11 17:13:32 +0800242 lwsl_ext("zlib out hit limit %u\n",
243 LWS_MAX_ZLIB_CONN_BUFFER);
Andy Greend678ea32013-01-12 23:09:36 +0800244 return -1;
245 }
246 conn->buf_out = (unsigned char *)realloc(
247 conn->buf_out,
248 LWS_SEND_BUFFER_PRE_PADDING +
David Galeano2605ffe2013-01-10 09:41:06 +0800249 conn->buf_out_length +
David Galeano01d02562013-01-10 09:51:15 +0800250 LWS_SEND_BUFFER_POST_PADDING);
Andy Greend678ea32013-01-12 23:09:36 +0800251 if (!conn->buf_out) {
252 lwsl_err("Out of memory\n");
253 return -1;
254 }
255 lwsl_debug(
256 "deflate-frame ext TX did realloc to %ld\n",
257 conn->buf_in_length);
258
259 conn->zs_out.next_out = (conn->buf_out +
David Galeano2605ffe2013-01-10 09:41:06 +0800260 LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
Andy Greend678ea32013-01-12 23:09:36 +0800261 conn->zs_out.avail_out =
David Galeano2605ffe2013-01-10 09:41:06 +0800262 (conn->buf_out_length - len_so_far);
David Galeano85a09212013-01-09 18:21:33 +0800263 }
264
265 conn->compressed_out = 1;
266
267 /* rewrite the buffer pointers and length */
David Galeano2605ffe2013-01-10 09:41:06 +0800268 eff_buf->token = (char *)(conn->buf_out +
269 LWS_SEND_BUFFER_PRE_PADDING);
270 eff_buf->token_len = (int)(conn->zs_out.next_out -
271 (conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4;
David Galeano85a09212013-01-09 18:21:33 +0800272
273 return 0;
274
275 case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
David Galeano2605ffe2013-01-10 09:41:06 +0800276 if (conn->compressed_out) {
David Galeano85a09212013-01-09 18:21:33 +0800277 conn->compressed_out = 0;
278 *((unsigned char *)eff_buf->token) |= 0x40;
279 }
280 break;
281
282 case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
283 /* Avoid x-webkit-deflate-frame extension on client */
David Galeano2605ffe2013-01-10 09:41:06 +0800284 if (!strcmp((char *)in, "x-webkit-deflate-frame"))
David Galeano85a09212013-01-09 18:21:33 +0800285 return 1;
David Galeano85a09212013-01-09 18:21:33 +0800286 break;
287
288 default:
289 break;
290 }
291
292 return 0;
293}
David Galeano2605ffe2013-01-10 09:41:06 +0800294