514c6b78c8300f8b240969c963902199a1ef851e
[profile/ivi/libwebsockets.git] / lib / extension-deflate-frame.c
1 #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
10 #define MIN_SIZE_TO_DEFLATE 4
11
12
13 int 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 =
21                                      (struct lws_ext_deflate_frame_conn *)user;
22         struct lws_tokens *eff_buf = (struct lws_tokens *)in;
23         size_t current_payload, remaining_payload, total_payload;
24         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) {
39                         fprintf(stderr, "deflateInit returned %d", n);
40                         return 1;
41                 }
42                 n = deflateInit2(&conn->zs_out,
43                                  DEFLATE_FRAME_COMPRESSION_LEVEL, Z_DEFLATED,
44                                  -LWS_ZLIB_WINDOW_BITS, LWS_ZLIB_MEMLEVEL,
45                                  Z_DEFAULT_STRATEGY);
46                 if (n != Z_OK) {
47                         fprintf(stderr, "deflateInit returned %d", n);
48                         return 1;
49                 }
50                 conn->buf_pre_used = 0;
51                 conn->buf_pre_length = 0;
52                 conn->buf_in_length = MAX_USER_RX_BUFFER;
53                 conn->buf_out_length = MAX_USER_RX_BUFFER;
54                 conn->compressed_out = 0;
55                 conn->buf_pre = NULL;
56                 conn->buf_in = (unsigned char *)
57                                 malloc(LWS_SEND_BUFFER_PRE_PADDING +
58                                                conn->buf_in_length +
59                                                LWS_SEND_BUFFER_POST_PADDING);
60                 conn->buf_out = (unsigned char *)
61                                 malloc(LWS_SEND_BUFFER_PRE_PADDING +
62                                                 conn->buf_out_length +
63                                                 LWS_SEND_BUFFER_POST_PADDING);
64                 fprintf(stderr, "zlibs constructed");
65                 break;
66
67         case LWS_EXT_CALLBACK_DESTROY:
68                 if (conn->buf_pre)
69                 {
70                         free(conn->buf_pre);
71                 }
72                 free(conn->buf_in);
73                 free(conn->buf_out);
74                 conn->buf_pre_used = 0;
75                 conn->buf_pre_length = 0;
76                 conn->buf_in_length = 0;
77                 conn->buf_out_length = 0;
78                 conn->compressed_out = 0;
79                 (void)inflateEnd(&conn->zs_in);
80                 (void)deflateEnd(&conn->zs_out);
81                 fprintf(stderr, "zlibs destructed");
82                 break;
83
84         case LWS_EXT_CALLBACK_PAYLOAD_RX:
85                 if (!(wsi->rsv & 0x40))
86                         return 0;
87
88                 /*
89                  * inflate the incoming payload
90                  */
91                 current_payload = eff_buf->token_len;
92
93                 remaining_payload = wsi->rx_packet_length;
94                 if (remaining_payload) {
95                         total_payload = conn->buf_pre_used +
96                                         current_payload +
97                                         remaining_payload;
98
99                         if (conn->buf_pre_length < total_payload) {
100                                 conn->buf_pre_length = total_payload;
101                                 if (conn->buf_pre)
102                                         free(conn->buf_pre);
103                                 conn->buf_pre =
104                                     (unsigned char *)malloc(total_payload + 4);
105                         }
106
107                         memcpy(conn->buf_pre + conn->buf_pre_used,
108                                               eff_buf->token, current_payload);
109                         conn->buf_pre_used += current_payload;
110
111                         eff_buf->token = NULL;
112                         eff_buf->token_len = 0;
113
114                         return 0;
115                 }
116                 if (conn->buf_pre_used) {
117                         total_payload = conn->buf_pre_used +
118                                         current_payload;
119
120                         memcpy(conn->buf_pre + conn->buf_pre_used,
121                                               eff_buf->token, current_payload);
122                         conn->buf_pre_used = 0;
123
124                         conn->zs_in.next_in = conn->buf_pre;
125                 } else {
126                         total_payload = current_payload;
127
128                         conn->zs_in.next_in = (unsigned char *)eff_buf->token;
129                 }
130
131                 conn->zs_in.next_in[total_payload + 0] = 0;
132                 conn->zs_in.next_in[total_payload + 1] = 0;
133                 conn->zs_in.next_in[total_payload + 2] = 0xff;
134                 conn->zs_in.next_in[total_payload + 3] = 0xff;
135
136                 conn->zs_in.avail_in = total_payload + 4;
137
138                 conn->zs_in.next_out = conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING;
139                 conn->zs_in.avail_out = conn->buf_in_length;
140
141                 while (1) {
142                         n = inflate(&conn->zs_in, Z_SYNC_FLUSH);
143                         switch (n) {
144                         case Z_NEED_DICT:
145                         case Z_DATA_ERROR:
146                         case Z_MEM_ERROR:
147                                 /*
148                                  * screwed.. close the connection... we will get a
149                                  * destroy callback to take care of closing nicely
150                                  */
151                                 fprintf(stderr, "zlib error inflate %d: %s",
152                                                            n, conn->zs_in.msg);
153                                 return -1;
154                         }
155
156                         if (conn->zs_in.avail_in > 0) {
157                                 size_t len_so_far = (conn->zs_in.next_out -
158                                  (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
159                                 unsigned char *new_buf;
160                                 conn->buf_in_length *= 2;
161                                 new_buf = (unsigned char *)
162                                         malloc(LWS_SEND_BUFFER_PRE_PADDING +
163                                                   conn->buf_in_length +
164                                                   LWS_SEND_BUFFER_POST_PADDING);
165                                 memcpy(new_buf + LWS_SEND_BUFFER_PRE_PADDING,
166                                         conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING,
167                                         len_so_far);
168                                 free(conn->buf_in);
169                                 conn->buf_in = new_buf;
170                                 conn->zs_in.next_out = (new_buf +
171                                      LWS_SEND_BUFFER_PRE_PADDING + len_so_far);
172                                 conn->zs_in.avail_out =
173                                               conn->buf_in_length - len_so_far;
174                         } else
175                                 break;
176                 }
177
178                 /* rewrite the buffer pointers and length */
179                 eff_buf->token = (char *)(conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING);
180                 eff_buf->token_len = (int)(conn->zs_in.next_out -
181                                  (conn->buf_in + LWS_SEND_BUFFER_PRE_PADDING));
182
183                 return 0;
184
185         case LWS_EXT_CALLBACK_PAYLOAD_TX:
186                 /*
187                  * deflate the outgoing payload
188                  */
189                 current_payload = eff_buf->token_len;
190
191                 if (current_payload < MIN_SIZE_TO_DEFLATE)
192                         return 0;
193
194                 conn->zs_out.next_in = (unsigned char *)eff_buf->token;
195                 conn->zs_out.avail_in = current_payload;
196
197                 conn->zs_out.next_out = conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING;
198                 conn->zs_out.avail_out = conn->buf_out_length;
199
200                 while (1) {
201                         n = deflate(&conn->zs_out, Z_SYNC_FLUSH);
202                         if (n == Z_STREAM_ERROR) {
203                                 /*
204                                  * screwed.. close the connection... we will get a
205                                  * destroy callback to take care of closing nicely
206                                  */
207                                 fprintf(stderr, "zlib error deflate");
208
209                                 return -1;
210                         }
211
212                         if (!conn->zs_out.avail_out) {
213                                 size_t len_so_far = (conn->zs_out.next_out -
214                                         (conn->buf_out +
215                                                  LWS_SEND_BUFFER_PRE_PADDING));
216                                 unsigned char *new_buf;
217                                 conn->buf_out_length *= 2;
218                                 new_buf = (unsigned char *)
219                                         malloc(LWS_SEND_BUFFER_PRE_PADDING +
220                                                   conn->buf_out_length +
221                                                   LWS_SEND_BUFFER_POST_PADDING);
222                                 memcpy(new_buf + LWS_SEND_BUFFER_PRE_PADDING,
223                                         conn->buf_out + LWS_SEND_BUFFER_PRE_PADDING,
224                                         len_so_far);
225                                 free(conn->buf_out);
226                                 conn->buf_out = new_buf;
227                                 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
232                                 break;
233                 }
234
235                 conn->compressed_out = 1;
236
237                 /* rewrite the buffer pointers and length */
238                 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;
242
243                 return 0;
244
245         case LWS_EXT_CALLBACK_PACKET_TX_PRESEND:
246                 if (conn->compressed_out) {
247                         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 */
254                 if (!strcmp((char *)in, "x-webkit-deflate-frame"))
255                         return 1;
256                 break;
257
258         default:
259                 break;
260         }
261
262         return 0;
263 }
264