Expose use of the event loop
[src/app-framework-binder.git] / src / afb-wsj1.c
1 /*
2  * Copyright (C) 2016 "IoT.bzh"
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <assert.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <stdio.h>
26
27 #include <json-c/json.h>
28
29 #include "afb-ws.h"
30 #include "afb-wsj1.h"
31
32 #define CALL 2
33 #define RETOK 3
34 #define RETERR 4
35 #define EVENT 5
36
37 static void wsj1_on_hangup(struct afb_wsj1 *wsj1);
38 static void wsj1_on_text(struct afb_wsj1 *wsj1, char *text, size_t size);
39
40 static struct afb_ws_itf wsj1_itf = {
41         .on_hangup = (void*)wsj1_on_hangup,
42         .on_text = (void*)wsj1_on_text
43 };
44
45 struct wsj1_call
46 {
47         struct wsj1_call *next;
48         void (*callback)(void *, struct afb_wsj1_msg *);
49         void *closure;
50         char id[16];
51 };
52
53 struct afb_wsj1_msg
54 {
55         int refcount;
56         struct afb_wsj1 *wsj1;
57         struct afb_wsj1_msg *next, *previous;
58         char *text;
59         int code;
60         char *id;
61         char *api;
62         char *verb;
63         char *event;
64         char *object_s;
65         size_t object_s_length;
66         char *token;
67         struct json_object *object_j;
68 };
69
70 struct afb_wsj1
71 {
72         int refcount;
73         int genid;
74         struct afb_wsj1_itf *itf;
75         void *closure;
76         struct json_tokener *tokener;
77         struct afb_ws *ws;
78         struct afb_wsj1_msg *messages;
79         struct wsj1_call *calls;
80 };
81
82 struct afb_wsj1 *afb_wsj1_create(struct sd_event *eloop, int fd, struct afb_wsj1_itf *itf, void *closure)
83 {
84         struct afb_wsj1 *result;
85
86         assert(fd >= 0);
87
88         result = calloc(1, sizeof * result);
89         if (result == NULL)
90                 goto error;
91
92         result->refcount = 1;
93         result->itf = itf;
94         result->closure = closure;
95
96         result->tokener = json_tokener_new();
97         if (result->tokener == NULL)
98                 goto error2;
99
100         result->ws = afb_ws_create(eloop, fd, &wsj1_itf, result);
101         if (result->ws == NULL)
102                 goto error3;
103
104         return result;
105
106 error3:
107         json_tokener_free(result->tokener);
108 error2:
109         free(result);
110 error:
111         close(fd);
112         return NULL;
113 }
114
115 void afb_wsj1_addref(struct afb_wsj1 *wsj1)
116 {
117         if (wsj1 != NULL)
118                 wsj1->refcount++;
119 }
120
121 void afb_wsj1_unref(struct afb_wsj1 *wsj1)
122 {
123         if (wsj1 != NULL && !--wsj1->refcount) {
124                 afb_ws_destroy(wsj1->ws);
125                 json_tokener_free(wsj1->tokener);
126                 free(wsj1);
127         }
128 }
129
130 static void wsj1_on_hangup(struct afb_wsj1 *wsj1)
131 {
132         if (wsj1->itf->on_hangup != NULL)
133                 wsj1->itf->on_hangup(wsj1->closure, wsj1);
134 }
135
136
137 static struct wsj1_call *wsj1_call_search(struct afb_wsj1 *wsj1, const char *id, int remove)
138 {
139         struct wsj1_call *r, **p;
140         p = &wsj1->calls;
141         while((r = *p) != NULL) {
142                 if (strcmp(r->id, id) == 0) {
143                         if (remove)
144                                 *p = r->next;
145                         break;
146                 }
147                 p = &r->next;
148         }
149         return r;
150 }
151
152 static struct wsj1_call *wsj1_call_create(struct afb_wsj1 *wsj1, void (*on_reply)(void*,struct afb_wsj1_msg*), void *closure)
153 {
154         struct wsj1_call *call = malloc(sizeof *call);
155         if (call == NULL)
156                 errno = ENOMEM;
157         else {
158                 do {
159                         if (wsj1->genid == 0)
160                                 wsj1->genid = 999999;
161                         sprintf(call->id, "%d", wsj1->genid--);
162                 } while (wsj1_call_search(wsj1, call->id, 0) != NULL);
163                 call->callback = on_reply;
164                 call->closure = closure;
165                 call->next = wsj1->calls;
166                 wsj1->calls = call;
167         }
168         return call;
169 }
170
171
172 static int wsj1_msg_scan(char *text, size_t items[10][2])
173 {
174         char *pos, *beg, *end, c;
175         int aux, n = 0;
176
177         /* scan */
178         pos = text;
179
180         /* scans: [ */
181         while(*pos == ' ') pos++;
182         if (*pos++ != '[') goto bad_scan;
183
184         /* scans list */
185         while(*pos == ' ') pos++;
186         if (*pos != ']') {
187                 for (;;) {
188                         if (n == 10)
189                                 goto bad_scan;
190                         beg = pos;
191                         aux = 0;
192                         while (aux != 0 || (*pos != ',' && *pos != ']')) {
193                                 switch(*pos++) {
194                                 case '{': case '[': aux++; break;
195                                 case '}': case ']': if (aux--) break;
196                                 case 0: goto bad_scan;
197                                 case '"':
198                                         do {
199                                                 switch(c = *pos++) {
200                                                 case '\\': if (*pos++) break;
201                                                 case 0: goto bad_scan;
202                                                 }
203                                         } while(c != '"');
204                                 }
205                         }
206                         end = pos;
207                         while (end > beg && end[-1] == ' ')
208                                 end--;
209                         items[n][0] = beg - text; /* start offset */
210                         items[n][1] = end - beg;  /* length */
211                         n++;
212                         if (*pos == ']')
213                                 break;
214                         while(*++pos == ' ');
215                 }
216         }
217         while(*++pos == ' ');
218         if (*pos) goto bad_scan;
219         return n;
220
221 bad_scan:
222         return -1;
223 }
224
225 static char *wsj1_msg_parse_extract(char *text, size_t offset, size_t size)
226 {
227         text[offset + size] = 0;
228         return text + offset;
229 }
230
231 static char *wsj1_msg_parse_string(char *text, size_t offset, size_t size)
232 {
233         if (size > 1 && text[offset] == '"') {
234                 offset += 1;
235                 size -= 2;
236         }
237         return wsj1_msg_parse_extract(text, offset, size);
238 }
239
240 static void wsj1_on_text(struct afb_wsj1 *wsj1, char *text, size_t size)
241 {
242         size_t items[10][2];
243         int n;
244         struct afb_wsj1_msg *msg;
245         struct wsj1_call *call = NULL;
246
247         /* allocate */
248         msg = calloc(1, sizeof *msg);
249         if (msg == NULL)
250                 goto alloc_error;
251
252         /* scan */
253         n = wsj1_msg_scan(text, items);
254         if (n < 0)
255                 goto bad_header;
256
257         /* scans code: 2|3|4|5 */
258         if (items[0][1] != 1) goto bad_header;
259         switch (text[items[0][0]]) {
260         case '2': msg->code = CALL; break;
261         case '3': msg->code = RETOK; break;
262         case '4': msg->code = RETERR; break;
263         case '5': msg->code = EVENT; break;
264         default: goto bad_header;
265         }
266
267         /* fills the message */
268         switch (msg->code) {
269         case CALL:
270                 if (n != 4 && n != 5) goto bad_header;
271                 msg->id = wsj1_msg_parse_string(text, items[1][0], items[1][1]);
272                 msg->api = wsj1_msg_parse_string(text, items[2][0], items[2][1]);
273                 msg->verb = strchr(msg->api, '/');
274                 if (msg->verb == NULL) goto bad_header;
275                 *msg->verb++ = 0;
276                 msg->object_s = wsj1_msg_parse_extract(text, items[3][0], items[3][1]);
277                 msg->object_s_length = items[3][1];
278                 msg->token = n == 5 ? wsj1_msg_parse_string(text, items[4][0], items[4][1]) : NULL;
279                 break;
280         case RETOK:
281         case RETERR:
282                 if (n != 3 && n != 4) goto bad_header;
283                 msg->id = wsj1_msg_parse_string(text, items[1][0], items[1][1]);
284                 call = wsj1_call_search(wsj1, msg->id, 1);
285                 if (call == NULL) goto bad_header;
286                 msg->object_s = wsj1_msg_parse_extract(text, items[2][0], items[2][1]);
287                 msg->object_s_length = items[2][1];
288                 msg->token = n == 5 ? wsj1_msg_parse_string(text, items[3][0], items[3][1]) : NULL;
289                 break;
290         case EVENT:
291                 if (n != 3) goto bad_header;
292                 msg->event = wsj1_msg_parse_string(text, items[1][0], items[1][1]);
293                 msg->object_s = wsj1_msg_parse_extract(text, items[2][0], items[2][1]);
294                 msg->object_s_length = items[2][1];
295                 break;
296         }
297         /* done */
298         msg->text = text;
299
300         /* fill and record the request */
301         msg->refcount = 1;
302         afb_wsj1_addref(wsj1);
303         msg->wsj1 = wsj1;
304         msg->next = wsj1->messages;
305         if (msg->next != NULL)
306                 msg->next->previous = msg;
307         wsj1->messages = msg;
308
309         /* incoke the handler */
310         switch (msg->code) {
311         case CALL:
312                 wsj1->itf->on_call(wsj1->closure, msg->api, msg->verb, msg);
313                 break;
314         case RETOK:
315         case RETERR:
316                 call->callback(call->closure, msg);
317                 free(call);
318                 break;
319         case EVENT:
320                 wsj1->itf->on_event(wsj1->closure, msg->event, msg);
321                 break;
322         }
323         afb_wsj1_msg_unref(msg);
324         return;
325
326 bad_header:
327         free(msg);
328 alloc_error:
329         free(text);
330         afb_ws_close(wsj1->ws, 1008, NULL);
331 }
332
333 void afb_wsj1_msg_addref(struct afb_wsj1_msg *msg)
334 {
335         if (msg != NULL)
336                 msg->refcount++;
337 }
338
339 void afb_wsj1_msg_unref(struct afb_wsj1_msg *msg)
340 {
341         if (msg != NULL && --msg->refcount == 0) {
342                 /* unlink the message */
343                 if (msg->next != NULL)
344                         msg->next->previous = msg->previous;
345                 if (msg->previous == NULL)
346                         msg->wsj1->messages = msg->next;
347                 else
348                         msg->previous->next = msg->next;
349                 /* free ressources */
350                 afb_wsj1_unref(msg->wsj1);
351                 json_object_put(msg->object_j);
352                 free(msg->text);
353                 free(msg);
354         }
355 }
356
357 const char *afb_wsj1_msg_object_s(struct afb_wsj1_msg *msg)
358 {
359         return msg->object_s;
360 }
361
362 struct json_object *afb_wsj1_msg_object_j(struct afb_wsj1_msg *msg)
363 {
364         struct json_object *object = msg->object_j;
365         if (object == NULL) {
366                 json_tokener_reset(msg->wsj1->tokener);
367                 object = json_tokener_parse_ex(msg->wsj1->tokener, msg->object_s, (int)msg->object_s_length);
368                 if (object == NULL) {
369                         /* lazy error detection of json request. Is it to improve? */
370                         object = json_object_new_string_len(msg->object_s, (int)msg->object_s_length);
371                 }
372                 msg->object_j = object;
373         }
374         return object;
375 }
376
377 int afb_wsj1_msg_is_call(struct afb_wsj1_msg *msg)
378 {
379         return msg->code == CALL;
380 }
381
382 int afb_wsj1_msg_is_reply(struct afb_wsj1_msg *msg)
383 {
384         return msg->code == RETOK || msg->code == RETERR;
385 }
386
387 int afb_wsj1_msg_is_reply_ok(struct afb_wsj1_msg *msg)
388 {
389         return msg->code == RETOK;
390 }
391
392 int afb_wsj1_msg_is_reply_error(struct afb_wsj1_msg *msg)
393 {
394         return msg->code == RETERR;
395 }
396
397 int afb_wsj1_msg_is_event(struct afb_wsj1_msg *msg)
398 {
399         return msg->code == EVENT;
400 }
401
402 const char *afb_wsj1_msg_api(struct afb_wsj1_msg *msg)
403 {
404         return msg->api;
405 }
406
407 const char *afb_wsj1_msg_verb(struct afb_wsj1_msg *msg)
408 {
409         return msg->verb;
410 }
411
412 const char *afb_wsj1_msg_event(struct afb_wsj1_msg *msg)
413 {
414         return msg->event;
415 }
416
417 const char *afb_wsj1_msg_token(struct afb_wsj1_msg *msg)
418 {
419         return msg->token;
420 }
421
422 struct afb_wsj1 *afb_wsj1_msg_wsj1(struct afb_wsj1_msg *msg)
423 {
424         return msg->wsj1;
425 }
426
427 int afb_wsj1_close(struct afb_wsj1 *wsj1, uint16_t code, const char *text)
428 {
429         return afb_ws_close(wsj1->ws, code, text);
430 }
431
432 static int wsj1_send_isot(struct afb_wsj1 *wsj1, int i1, const char *s1, const char *o1, const char *t1)
433 {
434         char code[2] = { (char)('0' + i1), 0 };
435         return afb_ws_texts(wsj1->ws, "[", code, ",\"", s1, "\",", o1 == NULL ? "null" : o1, t1 != NULL ? ",\"" : "]", t1, "\"]", NULL);
436 }
437
438 static int wsj1_send_issot(struct afb_wsj1 *wsj1, int i1, const char *s1, const char *s2, const char *o1, const char *t1)
439 {
440         char code[2] = { (char)('0' + i1), 0 };
441         return afb_ws_texts(wsj1->ws, "[", code, ",\"", s1, "\",\"", s2, "\",", o1 == NULL ? "null" : o1, t1 != NULL ? ",\"" : "]", t1, "\"]", NULL);
442 }
443
444 int afb_wsj1_send_event_j(struct afb_wsj1 *wsj1, const char *event, struct json_object *object)
445 {
446         const char *objstr = json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN);
447         int rc = afb_wsj1_send_event_s(wsj1, event, objstr);
448         json_object_put(object);
449         return rc;
450 }
451
452 int afb_wsj1_send_event_s(struct afb_wsj1 *wsj1, const char *event, const char *object)
453 {
454         return wsj1_send_isot(wsj1, EVENT, event, object, NULL);
455 }
456
457 int afb_wsj1_call_j(struct afb_wsj1 *wsj1, const char *api, const char *verb, struct json_object *object, void (*on_reply)(void *closure, struct afb_wsj1_msg *msg), void *closure)
458 {
459         const char *objstr = json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN);
460         int rc = afb_wsj1_call_s(wsj1, api, verb, objstr, on_reply, closure);
461         json_object_put(object);
462         return rc;
463 }
464
465 int afb_wsj1_call_s(struct afb_wsj1 *wsj1, const char *api, const char *verb, const char *object, void (*on_reply)(void *closure, struct afb_wsj1_msg *msg), void *closure)
466 {
467         int rc;
468         struct wsj1_call *call;
469         char *tag;
470
471         /* allocates the call */
472         call = wsj1_call_create(wsj1, on_reply, closure);
473         if (call == NULL) {
474                 errno = ENOMEM;
475                 return -1;
476         }
477
478         /* makes the tag */
479         tag = alloca(2 + strlen(api) + strlen(verb));
480         stpcpy(stpcpy(stpcpy(tag, api), "/"), verb);
481
482         /* makes the call */
483         rc = wsj1_send_issot(wsj1, CALL, call->id, tag, object, NULL);
484         if (rc < 0) {
485                 wsj1_call_search(wsj1, call->id, 1);
486                 free(call);
487         }
488         return rc;
489 }
490
491 int afb_wsj1_reply_j(struct afb_wsj1_msg *msg, struct json_object *object, const char *token, int iserror)
492 {
493         const char *objstr = json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN);
494         int rc = afb_wsj1_reply_s(msg, objstr, token, iserror);
495         json_object_put(object);
496         return rc;
497 }
498
499 int afb_wsj1_reply_s(struct afb_wsj1_msg *msg, const char *object, const char *token, int iserror)
500 {
501         return wsj1_send_isot(msg->wsj1, iserror ? RETERR : RETOK, msg->id, object, token);
502 }
503