libuv integration
[platform/upstream/libwebsockets.git] / lib / extension.c
1 #include "private-libwebsockets.h"
2
3 #include "extension-permessage-deflate.h"
4
5 LWS_VISIBLE void
6 lws_context_init_extensions(struct lws_context_creation_info *info,
7                             struct lws_context *context)
8 {
9         context->extensions = info->extensions;
10         lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
11 }
12
13 enum lws_ext_option_parser_states {
14         LEAPS_SEEK_NAME,
15         LEAPS_EAT_NAME,
16         LEAPS_SEEK_VAL,
17         LEAPS_EAT_DEC,
18         LEAPS_SEEK_ARG_TERM
19 };
20
21 LWS_VISIBLE int
22 lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi,
23                       void *ext_user, const struct lws_ext_options *opts,
24                       const char *in, int len)
25 {
26         enum lws_ext_option_parser_states leap = LEAPS_SEEK_NAME;
27         unsigned int match_map = 0, n, m, w = 0, count_options = 0,
28                      pending_close_quote = 0;
29         struct lws_ext_option_arg oa;
30
31         while (opts[count_options].name)
32                 count_options++;
33         while (len) {
34                 lwsl_ext("'%c' %d", *in, leap);
35                 switch (leap) {
36                 case LEAPS_SEEK_NAME:
37                         if (*in == ' ')
38                                 break;
39                         if (*in == ',') {
40                                 len = 1;
41                                 break;
42                         }
43                         match_map = (1 << count_options) - 1;
44                         leap = LEAPS_EAT_NAME;
45                         w = 0;
46
47                 /* fallthru */
48
49                 case LEAPS_EAT_NAME:
50                         oa.start = NULL;
51                         oa.len = 0;
52                         m = match_map;
53                         n = 0;
54                         pending_close_quote = 0;
55                         while (m) {
56                                 if (m & 1) {
57                                         lwsl_ext("    m=%d, n=%d, w=%d\n", m, n, w);
58
59                                         if (*in == opts[n].name[w]) {
60                                                 if (!opts[n].name[w + 1]) {
61                                                         oa.option_index = n;
62                                                         lwsl_ext("hit %d\n", oa.option_index);
63                                                         leap = LEAPS_SEEK_VAL;
64                                                         if (len ==1)
65                                                                 goto set_arg;
66                                                         break;
67                                                 }
68                                         } else {
69                                                 match_map &= ~(1 << n);
70                                                 if (!match_map) {
71                                                         lwsl_ext("empty match map\n");
72                                                         return -1;
73                                                 }
74                                         }
75                                 }
76                                 m >>= 1;
77                                 n++;
78                         }
79                         w++;
80                         break;
81                 case LEAPS_SEEK_VAL:
82                         if (*in == ' ')
83                                 break;
84                         if (*in == ',') {
85                                 len = 1;
86                                 break;
87                         }
88                         if (*in == ';' || len == 1) { /* ie,nonoptional */
89                                 if (opts[oa.option_index].type == EXTARG_DEC)
90                                         return -1;
91                                 leap = LEAPS_SEEK_NAME;
92                                 goto set_arg;
93                         }
94                         if (*in == '=') {
95                                 w = 0;
96                                 pending_close_quote = 0;
97                                 if (opts[oa.option_index].type == EXTARG_NONE)
98                                         return -1;
99
100                                 leap = LEAPS_EAT_DEC;
101                                 break;
102                         }
103                         return -1;
104
105                 case LEAPS_EAT_DEC:
106                         if (*in >= '0' && *in <= '9') {
107                                 if (!w)
108                                         oa.start = in;
109                                 w++;
110                                 if (len != 1)
111                                         break;
112                         }
113                         if (!w && *in =='"') {
114                                 pending_close_quote = 1;
115                                 break;
116                         }
117                         if (!w)
118                                 return -1;
119                         if (pending_close_quote && *in != '"' && len != 1)
120                                 return -1;
121                         leap = LEAPS_SEEK_ARG_TERM;
122                         if (oa.start)
123                                 oa.len = in - oa.start;
124                         if (len == 1)
125                                 oa.len++;
126
127 set_arg:
128                         ext->callback(lws_get_context(wsi),
129                                 ext, wsi, LWS_EXT_CB_OPTION_SET,
130                                 ext_user, (char *)&oa, 0);
131                         if (len == 1)
132                                 break;
133                         if (pending_close_quote && *in == '"')
134                                 break;
135
136                         /* fallthru */
137
138                 case LEAPS_SEEK_ARG_TERM:
139                         if (*in == ' ')
140                                 break;
141                         if (*in == ';') {
142                                 leap = LEAPS_SEEK_NAME;
143                                 break;
144                         }
145                         if (*in == ',') {
146                                 len = 1;
147                                 break;
148                         }
149                         return -1;
150                 }
151                 len--;
152                 in++;
153         }
154
155         return 0;
156 }
157
158
159 /* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */
160
161 int lws_ext_cb_active(struct lws *wsi, int reason, void *arg, int len)
162 {
163         int n, m, handled = 0;
164
165         for (n = 0; n < wsi->count_act_ext; n++) {
166                 m = wsi->active_extensions[n]->callback(lws_get_context(wsi),
167                         wsi->active_extensions[n], wsi, reason,
168                         wsi->act_ext_user[n], arg, len);
169                 if (m < 0) {
170                         lwsl_ext("Ext '%s' failed to handle callback %d!\n",
171                                  wsi->active_extensions[n]->name, reason);
172                         return -1;
173                 }
174                 /* valgrind... */
175                 if (reason == LWS_EXT_CB_DESTROY)
176                         wsi->act_ext_user[n] = NULL;
177                 if (m > handled)
178                         handled = m;
179         }
180
181         return handled;
182 }
183
184 int lws_ext_cb_all_exts(struct lws_context *context, struct lws *wsi,
185                         int reason, void *arg, int len)
186 {
187         int n = 0, m, handled = 0;
188         const struct lws_extension *ext = context->extensions;
189
190         while (ext && ext->callback && !handled) {
191                 m = ext->callback(context, ext, wsi, reason,
192                                   (void *)(long)n, arg, len);
193                 if (m < 0) {
194                         lwsl_ext("Ext '%s' failed to handle callback %d!\n",
195                                  wsi->active_extensions[n]->name, reason);
196                         return -1;
197                 }
198                 if (m)
199                         handled = 1;
200
201                 ext++;
202                 n++;
203         }
204
205         return 0;
206 }
207
208 int
209 lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len)
210 {
211         struct lws_tokens eff_buf;
212         int ret, m, n = 0;
213
214         eff_buf.token = (char *)buf;
215         eff_buf.token_len = len;
216
217         /*
218          * while we have original buf to spill ourselves, or extensions report
219          * more in their pipeline
220          */
221
222         ret = 1;
223         while (ret == 1) {
224
225                 /* default to nobody has more to spill */
226
227                 ret = 0;
228
229                 /* show every extension the new incoming data */
230                 m = lws_ext_cb_active(wsi,
231                                LWS_EXT_CB_PACKET_TX_PRESEND, &eff_buf, 0);
232                 if (m < 0)
233                         return -1;
234                 if (m) /* handled */
235                         ret = 1;
236
237                 if ((char *)buf != eff_buf.token)
238                         /*
239                          * extension recreated it:
240                          * need to buffer this if not all sent
241                          */
242                         wsi->u.ws.clean_buffer = 0;
243
244                 /* assuming they left us something to send, send it */
245
246                 if (eff_buf.token_len) {
247                         n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token,
248                                                             eff_buf.token_len);
249                         if (n < 0) {
250                                 lwsl_info("closing from ext access\n");
251                                 return -1;
252                         }
253
254                         /* always either sent it all or privately buffered */
255                         if (wsi->u.ws.clean_buffer)
256                                 len = n;
257                 }
258
259                 lwsl_parser("written %d bytes to client\n", n);
260
261                 /* no extension has more to spill?  Then we can go */
262
263                 if (!ret)
264                         break;
265
266                 /* we used up what we had */
267
268                 eff_buf.token = NULL;
269                 eff_buf.token_len = 0;
270
271                 /*
272                  * Did that leave the pipe choked?
273                  * Or we had to hold on to some of it?
274                  */
275
276                 if (!lws_send_pipe_choked(wsi) && !wsi->trunc_len)
277                         /* no we could add more, lets's do that */
278                         continue;
279
280                 lwsl_debug("choked\n");
281
282                 /*
283                  * Yes, he's choked.  Don't spill the rest now get a callback
284                  * when he is ready to send and take care of it there
285                  */
286                 lws_callback_on_writable(wsi);
287                 wsi->extension_data_pending = 1;
288                 ret = 0;
289         }
290
291         return len;
292 }
293
294 int
295 lws_any_extension_handled(struct lws *wsi, enum lws_extension_callback_reasons r,
296                           void *v, size_t len)
297 {
298         struct lws_context *context = wsi->context;
299         int n, handled = 0;
300
301         /* maybe an extension will take care of it for us */
302
303         for (n = 0; n < wsi->count_act_ext && !handled; n++) {
304                 if (!wsi->active_extensions[n]->callback)
305                         continue;
306
307                 handled |= wsi->active_extensions[n]->callback(context,
308                         wsi->active_extensions[n], wsi,
309                         r, wsi->act_ext_user[n], v, len);
310         }
311
312         return handled;
313 }