blob: 13b2b8deddba9072dd7c565075fb0d0207cfb656 [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;
Alejandro Mery6ff28242014-12-04 23:59:35 +010057 conn->buf_in = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING +
58 conn->buf_in_length +
59 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +080060 if (!conn->buf_in)
61 goto bail;
Alejandro Mery6ff28242014-12-04 23:59:35 +010062 conn->buf_out = lws_malloc(LWS_SEND_BUFFER_PRE_PADDING +
63 conn->buf_out_length +
64 LWS_SEND_BUFFER_POST_PADDING);
Andy Green988bd982013-01-10 12:26:13 +080065 if (!conn->buf_out)
66 goto bail;
Andy Green43db0452013-01-10 19:50:35 +080067 lwsl_ext("zlibs constructed\n");
David Galeano85a09212013-01-09 18:21:33 +080068 break;
Andy Green988bd982013-01-10 12:26:13 +080069bail:
Andy Green43db0452013-01-10 19:50:35 +080070 lwsl_err("Out of mem\n");
Andy Green988bd982013-01-10 12:26:13 +080071 (void)inflateEnd(&conn->zs_in);
72 (void)deflateEnd(&conn->zs_out);
73 return -1;
David Galeano85a09212013-01-09 18:21:33 +080074
75 case LWS_EXT_CALLBACK_DESTROY:
Alejandro Mery6ff28242014-12-04 23:59:35 +010076 lws_free(conn->buf_pre);
77 lws_free(conn->buf_in);
78 lws_free(conn->buf_out);
David Galeano2605ffe2013-01-10 09:41:06 +080079 conn->buf_pre_used = 0;
80 conn->buf_pre_length = 0;
David Galeano85a09212013-01-09 18:21:33 +080081 conn->buf_in_length = 0;
82 conn->buf_out_length = 0;
83 conn->compressed_out = 0;
84 (void)inflateEnd(&conn->zs_in);
85 (void)deflateEnd(&conn->zs_out);
Andy Green43db0452013-01-10 19:50:35 +080086 lwsl_ext("zlibs destructed\n");
David Galeano85a09212013-01-09 18:21:33 +080087 break;
88
89 case LWS_EXT_CALLBACK_PAYLOAD_RX:
Andy Green623a98d2013-01-21 11:04:23 +080090 if (!(wsi->u.ws.rsv & 0x40))
David Galeano85a09212013-01-09 18:21:33 +080091 return 0;
92
93 /*
94 * inflate the incoming payload
95 */
David Galeano2605ffe2013-01-10 09:41:06 +080096 current_payload = eff_buf->token_len;
David Galeano85a09212013-01-09 18:21:33 +080097
Andy Green623a98d2013-01-21 11:04:23 +080098 remaining_payload = wsi->u.ws.rx_packet_length;
David Galeano2605ffe2013-01-10 09:41:06 +080099 if (remaining_payload) {
100 total_payload = conn->buf_pre_used +
101 current_payload +
102 remaining_payload;
103
104 if (conn->buf_pre_length < total_payload) {
105 conn->buf_pre_length = total_payload;
Alejandro Mery6ff28242014-12-04 23:59:35 +0100106 lws_free(conn->buf_pre);
107 conn->buf_pre = lws_malloc(total_payload + 4);
Andy Green988bd982013-01-10 12:26:13 +0800108 if (!conn->buf_pre) {
Andy Green43db0452013-01-10 19:50:35 +0800109 lwsl_err("Out of memory\n");
Andy Green988bd982013-01-10 12:26:13 +0800110 return -1;
111 }
David Galeano2605ffe2013-01-10 09:41:06 +0800112 }
113
114 memcpy(conn->buf_pre + conn->buf_pre_used,
115 eff_buf->token, current_payload);
116 conn->buf_pre_used += current_payload;
117
118 eff_buf->token = NULL;
119 eff_buf->token_len = 0;
120
121 return 0;
122 }
123 if (conn->buf_pre_used) {
124 total_payload = conn->buf_pre_used +
125 current_payload;
126
127 memcpy(conn->buf_pre + conn->buf_pre_used,
128 eff_buf->token, current_payload);
129 conn->buf_pre_used = 0;
130
131 conn->zs_in.next_in = conn->buf_pre;
132 } else {
133 total_payload = current_payload;
134
135 conn->zs_in.next_in = (unsigned char *)eff_buf->token;
136 }
137
138 conn->zs_in.next_in[total_payload + 0] = 0;
139 conn->zs_in.next_in[total_payload + 1] = 0;
140 conn->zs_in.next_in[total_payload + 2] = 0xff;
141 conn->zs_in.next_in[total_payload + 3] = 0xff;
142
143 conn->zs_in.avail_in = total_payload + 4;
David Galeano85a09212013-01-09 18:21:33 +0800144
Andy Greenb5b23192013-02-11 17:13:32 +0800145 conn->zs_in.next_out =
146 conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
147 conn->zs_in.avail_out = conn->buf_in_length;
David Galeano85a09212013-01-09 18:21:33 +0800148
Andy Greenb5b23192013-02-11 17:13:32 +0800149 while (1) {
150 n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
151 switch (n) {
152 case Z_NEED_DICT:
153 case Z_STREAM_ERROR:
154 case Z_DATA_ERROR:
155 case Z_MEM_ERROR:
Andy Greend678ea32013-01-12 23:09:36 +0800156 /*
Andy Greenb5b23192013-02-11 17:13:32 +0800157 * screwed.. close the connection...
158 * we will get a destroy callback to take care
159 * of closing nicely
Andy Greend678ea32013-01-12 23:09:36 +0800160 */
Andy Green5ac7e7a2014-03-15 09:32:40 +0800161 lwsl_info("zlib error inflate %d: %s\n",
Andy Greend678ea32013-01-12 23:09:36 +0800162 n, conn->zs_in.msg);
163 return -1;
164 }
165
166 if (conn->zs_in.avail_out)
167 break;
168
169 len_so_far = conn->zs_in.next_out -
170 (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
171
172 conn->buf_in_length *= 2;
173 if (conn->buf_in_length > LWS_MAX_ZLIB_CONN_BUFFER) {
Andy Greenb5b23192013-02-11 17:13:32 +0800174 lwsl_ext("zlib in buffer hit limit %u\n",
Andy Greend678ea32013-01-12 23:09:36 +0800175 LWS_MAX_ZLIB_CONN_BUFFER);
176 return -1;
177 }
Alejandro Mery6ff28242014-12-04 23:59:35 +0100178 conn->buf_in = lws_realloc(conn->buf_in,
179 LWS_SEND_BUFFER_PRE_PADDING +
180 conn->buf_in_length +
181 LWS_SEND_BUFFER_POST_PADDING);
Andy Greend678ea32013-01-12 23:09:36 +0800182 if (!conn->buf_in) {
183 lwsl_err("Out of memory\n");
184 return -1;
185 }
186 lwsl_debug(
187 "deflate-frame ext RX did realloc to %ld\n",
188 conn->buf_in_length);
189 conn->zs_in.next_out = conn->buf_in +
Andy Greenb5b23192013-02-11 17:13:32 +0800190 LWS_SEND_BUFFER_PRE_PADDING + len_so_far;
Andy Greend678ea32013-01-12 23:09:36 +0800191 conn->zs_in.avail_out =
Andy Greenb5b23192013-02-11 17:13:32 +0800192 conn->buf_in_length - len_so_far;
193 }
David Galeano85a09212013-01-09 18:21:33 +0800194
195 /* rewrite the buffer pointers and length */
Andy Greenb5b23192013-02-11 17:13:32 +0800196 eff_buf->token =
197 (char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
David Galeano2605ffe2013-01-10 09:41:06 +0800198 eff_buf->token_len = (int)(conn->zs_in.next_out -
199 (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
David Galeano85a09212013-01-09 18:21:33 +0800200
201 return 0;
202
203 case LWS_EXT_CALLBACK_PAYLOAD_TX:
204 /*
205 * deflate the outgoing payload
206 */
David Galeano01d02562013-01-10 09:51:15 +0800207 current_payload = eff_buf->token_len;
208
David Galeano85a09212013-01-09 18:21:33 +0800209 conn->zs_out.next_in = (unsigned char *)eff_buf->token;
David Galeano01d02562013-01-10 09:51:15 +0800210 conn->zs_out.avail_in = current_payload;
David Galeano85a09212013-01-09 18:21:33 +0800211
Andy Greenb5b23192013-02-11 17:13:32 +0800212 conn->zs_out.next_out =
213 conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
David Galeano85a09212013-01-09 18:21:33 +0800214 conn->zs_out.avail_out = conn->buf_out_length;
215
David Galeano2605ffe2013-01-10 09:41:06 +0800216 while (1) {
David Galeano85a09212013-01-09 18:21:33 +0800217 n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
218 if (n == Z_STREAM_ERROR) {
219 /*
Andy Greenb5b23192013-02-11 17:13:32 +0800220 * screwed.. close the connection... we will
221 * get a destroy callback to take care of
222 * closing nicely
David Galeano85a09212013-01-09 18:21:33 +0800223 */
Andy Greenf7609e92013-01-14 13:10:55 +0800224 lwsl_ext("zlib error deflate\n");
David Galeano85a09212013-01-09 18:21:33 +0800225
226 return -1;
227 }
228
Andy Greend678ea32013-01-12 23:09:36 +0800229 if (conn->zs_out.avail_out)
230 break;
231
232 len_so_far = (conn->zs_out.next_out -
David Galeano2605ffe2013-01-10 09:41:06 +0800233 (conn->buf_out +
234 LWS_SEND_BUFFER_PRE_PADDING));
Andy Greend678ea32013-01-12 23:09:36 +0800235 conn->buf_out_length *= 2;
236 if (conn->buf_out_length > LWS_MAX_ZLIB_CONN_BUFFER) {
Andy Greenb5b23192013-02-11 17:13:32 +0800237 lwsl_ext("zlib out hit limit %u\n",
238 LWS_MAX_ZLIB_CONN_BUFFER);
Andy Greend678ea32013-01-12 23:09:36 +0800239 return -1;
240 }
Alejandro Mery6ff28242014-12-04 23:59:35 +0100241 conn->buf_out = lws_realloc(conn->buf_out,
242 LWS_SEND_BUFFER_PRE_PADDING +
243 conn->buf_out_length +
244 LWS_SEND_BUFFER_POST_PADDING);
Andy Greend678ea32013-01-12 23:09:36 +0800245 if (!conn->buf_out) {
246 lwsl_err("Out of memory\n");
247 return -1;
248 }
249 lwsl_debug(
250 "deflate-frame ext TX did realloc to %ld\n",
251 conn->buf_in_length);
252
253 conn->zs_out.next_out = (conn->buf_out +
David Galeano2605ffe2013-01-10 09:41:06 +0800254 LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
Andy Greend678ea32013-01-12 23:09:36 +0800255 conn->zs_out.avail_out =
David Galeano2605ffe2013-01-10 09:41:06 +0800256 (conn->buf_out_length - len_so_far);
David Galeano85a09212013-01-09 18:21:33 +0800257 }
258
259 conn->compressed_out = 1;
260
261 /* rewrite the buffer pointers and length */
David Galeano2605ffe2013-01-10 09:41:06 +0800262 eff_buf->token = (char *)(conn->buf_out +
263 LWS_SEND_BUFFER_PRE_PADDING);
264 eff_buf->token_len = (int)(conn->zs_out.next_out -
265 (conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING)) - 4;
David Galeano85a09212013-01-09 18:21:33 +0800266
267 return 0;
268
269 case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
David Galeano2605ffe2013-01-10 09:41:06 +0800270 if (conn->compressed_out) {
David Galeano85a09212013-01-09 18:21:33 +0800271 conn->compressed_out = 0;
272 *((unsigned char *)eff_buf->token) |= 0x40;
273 }
274 break;
275
276 case LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION:
277 /* Avoid x-webkit-deflate-frame extension on client */
David Galeano2605ffe2013-01-10 09:41:06 +0800278 if (!strcmp((char *)in, "x-webkit-deflate-frame"))
David Galeano85a09212013-01-09 18:21:33 +0800279 return 1;
David Galeano85a09212013-01-09 18:21:33 +0800280 break;
281
282 default:
283 break;
284 }
285
286 return 0;
287}
David Galeano2605ffe2013-01-10 09:41:06 +0800288