6fc6e3b33bca117095546f58f0d36e9b1bc4f15b
[src/app-framework-binder.git] / src / afb-ws-json.c
1 /*
2  * Copyright 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
26 #include <json.h>
27
28 #include "afb-ws.h"
29 #include "afb-ws-json.h"
30 #include "afb-msg-json.h"
31 #include "session.h"
32 #include "afb-req-itf.h"
33 #include "afb-apis.h"
34
35 static void aws_on_close(struct afb_ws_json *ws, uint16_t code, char *text, size_t size);
36 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size);
37
38 static struct afb_ws_itf aws_itf = {
39         .on_close = (void*)aws_on_close,
40         .on_text = (void*)aws_on_text,
41         .on_binary = NULL,
42 };
43
44 struct afb_wsreq;
45
46 struct afb_ws_json
47 {
48         void (*cleanup)(void*);
49         void *cleanup_closure;
50         struct afb_wsreq *requests;
51         struct AFB_clientCtx *context;
52         struct json_tokener *tokener;
53         struct afb_ws *ws;
54 };
55
56
57 static void aws_send_event(struct afb_ws_json *ws, const char *event, struct json_object *object);
58
59 static const struct afb_event_sender_itf event_sender_itf = {
60         .send = (void*)aws_send_event
61 };
62
63 struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *cleanup_closure)
64 {
65         struct afb_ws_json *result;
66
67         assert(fd >= 0);
68         assert(context != NULL);
69
70         result = malloc(sizeof * result);
71         if (result == NULL)
72                 goto error;
73
74         result->cleanup = cleanup;
75         result->cleanup_closure = cleanup_closure;
76         result->requests = NULL;
77         result->context = ctxClientGet(context);
78         if (result->context == NULL)
79                 goto error2;
80
81         result->tokener = json_tokener_new();
82         if (result->tokener == NULL)
83                 goto error3;
84
85         result->ws = afb_ws_create(fd, &aws_itf, result);
86         if (result->ws == NULL)
87                 goto error4;
88
89         if (0 > ctxClientEventSenderAdd(result->context, (struct afb_event_sender){ .itf = &event_sender_itf, .closure = result }))
90                 goto error5;
91
92         return result;
93
94 error5:
95         /* TODO */
96 error4:
97         json_tokener_free(result->tokener);
98 error3:
99         ctxClientPut(result->context);
100 error2:
101         free(result);
102 error:
103         close(fd);
104         return NULL;
105 }
106
107 static void aws_on_close(struct afb_ws_json *ws, uint16_t code, char *text, size_t size)
108 {
109         /* do nothing but free the text */
110         free(text);
111 }
112
113 #define CALL 2
114 #define RETOK 3
115 #define RETERR 4
116 #define EVENT 5
117
118 struct afb_wsreq
119 {
120         struct afb_ws_json *aws;
121         struct afb_wsreq *next;
122         char *text;
123         size_t size;
124         int code;
125         char *id;
126         size_t idlen;
127         char *api;
128         size_t apilen;
129         char *verb;
130         size_t verblen;
131         char *obj;
132         size_t objlen;
133         char *tok;
134         size_t toklen;
135         struct json_object *root;
136 };
137
138 static struct json_object *wsreq_json(struct afb_wsreq *wsreq);
139 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
140 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
141 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
142 static const char *wsreq_raw(struct afb_wsreq *wsreq, size_t *size);
143 static void wsreq_send(struct afb_wsreq *wsreq, char *buffer, size_t size);
144 static int wsreq_session_create(struct afb_wsreq *wsreq);
145 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
146 static void wsreq_session_close(struct afb_wsreq *wsreq);
147
148
149 static const struct afb_req_itf wsreq_itf = {
150         .json = (void*)wsreq_json,
151         .get = (void*)wsreq_get,
152         .success = (void*)wsreq_success,
153         .fail = (void*)wsreq_fail,
154         .raw = (void*)wsreq_raw,
155         .send = (void*)wsreq_send,
156         .session_create = (void*)wsreq_session_create,
157         .session_check = (void*)wsreq_session_check,
158         .session_close = (void*)wsreq_session_close,
159         .context_get = (void*)afb_context_get,
160         .context_set = (void*)afb_context_set
161
162 };
163
164 static int aws_wsreq_parse(struct afb_wsreq *r, char *text, size_t size)
165 {
166         char *pos, *end, c;
167         int aux;
168
169         /* scan */
170         pos = text;
171         end = text + size;
172
173         /* scans: [ */
174         while(pos < end && *pos == ' ') pos++;
175         if (pos == end) goto bad_header;
176         if (*pos++ != '[') goto bad_header;
177
178         /* scans code: 2|3|4 */
179         while(pos < end && *pos == ' ') pos++;
180         if (pos == end) goto bad_header;
181         switch (*pos++) {
182         case '2': r->code = CALL; break;
183         case '3': r->code = RETOK; break;
184         case '4': r->code = RETERR; break;
185         default: goto bad_header;
186         }
187
188         /* scans: , */
189         while(pos < end && *pos == ' ') pos++;
190         if (pos == end) goto bad_header;
191         if (*pos++ != ',') goto bad_header;
192
193         /* scans id: "id" */
194         while(pos < end && *pos == ' ') pos++;
195         if (pos == end) goto bad_header;
196         if (*pos++ != '"') goto bad_header;
197         r->id = pos;
198         while(pos < end && *pos != '"') pos++;
199         if (pos == end) goto bad_header;
200         r->idlen = (size_t)(pos++ - r->id);
201
202         /* scans: , */
203         while(pos < end && *pos == ' ') pos++;
204         if (pos == end) goto bad_header;
205         if (*pos++ != ',') goto bad_header;
206
207         /* scans the method if needed */
208         if (r->code == CALL) {
209                 /* scans: " */
210                 while(pos < end && *pos == ' ') pos++;
211                 if (pos == end) goto bad_header;
212                 if (*pos++ != '"') goto bad_header;
213
214                 /* scans: api/ */
215                 r->api = pos;
216                 while(pos < end && *pos != '"' && *pos != '/') pos++;
217                 if (pos == end) goto bad_header;
218                 if (*pos != '/') goto bad_header;
219                 r->apilen = (size_t)(pos++ - r->api);
220                 if (r->apilen && r->api[r->apilen - 1] == '\\')
221                         r->apilen--;
222
223                 /* scans: verb" */
224                 r->verb = pos;
225                 while(pos < end && *pos != '"') pos++;
226                 if (pos == end) goto bad_header;
227                 r->verblen = (size_t)(pos++ - r->verb);
228
229                 /* scans: , */
230                 while(pos < end && *pos == ' ') pos++;
231                 if (pos == end) goto bad_header;
232                 if (*pos++ != ',') goto bad_header;
233         }
234
235         /* scan obj */
236         while(pos < end && *pos == ' ') pos++;
237         if (pos == end) goto bad_header;
238         aux = 0;
239         r->obj = pos;
240         while (pos < end && (aux != 0 || (*pos != ',' && *pos != ']'))) {
241                 if (pos == end) goto bad_header;
242                 switch(*pos) {
243                 case '{': case '[': aux++; break;
244                 case '}': case ']': if (!aux--) goto bad_header; break;
245                 case '"':
246                         do {
247                                 pos += 1 + (*pos == '\\');
248                         } while(pos < end && *pos != '"');
249                 default:
250                         break;
251                 }
252                 pos++;
253         }
254         if (pos > end) goto bad_header;
255         if (pos == end && aux != 0) goto bad_header;
256         c = *pos;
257         r->objlen = (size_t)(pos++ - r->obj);
258         while (r->objlen && r->obj[r->objlen - 1] == ' ')
259                 r->objlen--;
260
261         /* scan the token (if any) */
262         if (c == ',') {
263                 /* scans token: "token" */
264                 while(pos < end && *pos == ' ') pos++;
265                 if (pos == end) goto bad_header;
266                 if (*pos++ != '"') goto bad_header;
267                 r->tok = pos;
268                 while(pos < end && *pos != '"') pos++;
269                 if (pos == end) goto bad_header;
270                 r->toklen = (size_t)(pos++ - r->tok);
271                 while(pos < end && *pos == ' ') pos++;
272                 if (pos == end) goto bad_header;
273                 c = *pos++;
274         }
275
276         /* scan: ] */
277         if (c != ']') goto bad_header;
278         while(pos < end && *pos == ' ') pos++;
279         if (pos != end) goto bad_header;
280
281         /* done */
282         r->text = text;
283         r->size = size;
284 fprintf(stderr, "\n\nONTEXT([%d, %.*s, %.*s/%.*s, %.*s, %.*s])\n\n",
285         r->code,
286         (int)r->idlen, r->id,
287         (int)r->apilen, r->api,
288         (int)r->verblen, r->verb,
289         (int)r->objlen, r->obj,
290         (int)r->toklen, r->tok
291 );
292         return 1;
293
294 bad_header:
295         return 0;
296 }
297
298 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size)
299 {
300         struct afb_req r;
301         struct afb_wsreq *wsreq;
302
303         /* allocate */
304         wsreq = calloc(1, sizeof *wsreq);
305         if (wsreq == NULL)
306                 goto alloc_error;
307
308         /* init */
309         if (!aws_wsreq_parse(wsreq, text, size))
310                 goto bad_header;
311
312         /* fill and record the request */
313         wsreq->aws = ws;
314         wsreq->next = ws->requests;
315         ws->requests = wsreq;
316
317         r.req_closure = wsreq;
318         r.itf = &wsreq_itf;
319         afb_apis_call(r, ws->context, wsreq->api, wsreq->apilen, wsreq->verb, wsreq->verblen);
320         return;
321
322 bad_header:
323         free(wsreq);
324 alloc_error:
325         free(text);
326         afb_ws_close(ws->ws, 1008);
327         return;
328 }
329
330 static struct json_object *wsreq_json(struct afb_wsreq *wsreq)
331 {
332         struct json_object *root = wsreq->root;
333         if (root == NULL) {
334                 json_tokener_reset(wsreq->aws->tokener);
335                 root = json_tokener_parse_ex(wsreq->aws->tokener, wsreq->obj, (int)wsreq->objlen);
336                 if (root == NULL) {
337                         /* lazy error detection of json request. Is it to improve? */
338                         root = json_object_new_string_len(wsreq->obj, (int)wsreq->objlen);
339                 }
340                 wsreq->root = root;
341         }
342         return root;
343 }
344
345 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
346 {
347         struct afb_arg arg;
348         struct json_object *value, *root;
349
350         root = wsreq_json(wsreq);
351         if (json_object_object_get_ex(root, name, &value)) {
352                 arg.name = name;
353                 arg.value = json_object_get_string(value);
354         } else {
355                 arg.name = NULL;
356                 arg.value = NULL;
357         }
358         arg.path = NULL;
359         return arg;
360 }
361
362 static int wsreq_session_create(struct afb_wsreq *wsreq)
363 {
364         struct AFB_clientCtx *context = wsreq->aws->context;
365         if (context->created)
366                 return 0;
367         return wsreq_session_check(wsreq, 1);
368 }
369
370 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
371 {
372         struct AFB_clientCtx *context = wsreq->aws->context;
373
374         if (wsreq->tok == NULL)
375                 return 0;
376
377         if (!ctxTokenCheckLen (context, wsreq->tok, wsreq->toklen))
378                 return 0;
379
380         if (refresh) {
381                 ctxTokenNew (context);
382         }
383
384         return 1;
385 }
386
387 static void wsreq_session_close(struct afb_wsreq *wsreq)
388 {
389         struct AFB_clientCtx *context = wsreq->aws->context;
390         ctxClientClose(context);
391 }
392
393 static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t idlen, struct json_object *data)
394 {
395         json_object *msg;
396         const char *token;
397         const char *txt;
398
399         /* pack the message */
400         msg = json_object_new_array();
401         json_object_array_add(msg, json_object_new_int(code));
402         json_object_array_add(msg, json_object_new_string_len(id, (int)idlen));
403         json_object_array_add(msg, data);
404         token = aws->context->token;
405         if (token)
406                 json_object_array_add(msg, json_object_new_string(token));
407
408         /* emits the reply */
409         txt = json_object_to_json_string(msg);
410         afb_ws_text(aws->ws, txt, strlen(txt));
411         json_object_put(msg);
412 }
413
414 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
415 {
416         aws_emit(wsreq->aws, retcode, wsreq->id, wsreq->idlen, afb_msg_json_reply(status, info, resp, NULL, NULL));
417         /* TODO eliminates the wsreq */
418 }
419
420 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
421 {
422         wsreq_reply(wsreq, RETERR, status, info, NULL);
423 }
424
425 static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info)
426 {
427         wsreq_reply(wsreq, RETOK, "success", info, obj);
428 }
429
430 static const char *wsreq_raw(struct afb_wsreq *wsreq, size_t *size)
431 {
432         *size = wsreq->objlen;
433         return wsreq->obj;
434 }
435
436 static void wsreq_send(struct afb_wsreq *wsreq, char *buffer, size_t size)
437 {
438         afb_ws_text(wsreq->aws->ws, buffer, size);
439 }
440
441 static void aws_send_event(struct afb_ws_json *aws, const char *event, struct json_object *object)
442 {
443         aws_emit(aws, EVENT, event, strlen(event), afb_msg_json_event(event, object));
444 }
445