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