2 * Copyright 2016 IoT.bzh
3 * Author: José Bollo <jose.bollo@iot.bzh>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
29 #include "afb-ws-json.h"
31 #include "afb-req-itf.h"
34 static void aws_on_close(struct afb_ws_json *ws, uint16_t code, char *text, size_t size);
35 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size);
37 static struct afb_ws_itf aws_itf = {
38 .on_close = (void*)aws_on_close,
39 .on_text = (void*)aws_on_text,
47 void (*cleanup)(void*);
48 void *cleanup_closure;
49 struct afb_wsreq *requests;
50 struct AFB_clientCtx *context;
51 struct json_tokener *tokener;
55 struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *closure)
57 struct afb_ws_json *result;
60 assert(context != NULL);
62 result = malloc(sizeof * result);
66 result->cleanup = cleanup;
67 result->cleanup_closure = closure;
68 result->requests = NULL;
69 result->context = ctxClientGet(context);
70 if (result->context == NULL)
73 result->tokener = json_tokener_new();
74 if (result->tokener == NULL)
77 result->ws = afb_ws_create(fd, &aws_itf, result);
78 if (result->ws == NULL)
84 json_tokener_free(result->tokener);
86 ctxClientPut(result->context);
94 static void aws_on_close(struct afb_ws_json *ws, uint16_t code, char *text, size_t size)
103 struct afb_ws_json *aws;
104 struct afb_wsreq *next;
105 struct json_object *id;
106 struct json_object *name;
107 struct json_object *token;
108 struct json_object *request;
110 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
111 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure);
112 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
113 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
114 static int wsreq_session_create(struct afb_wsreq *wsreq);
115 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
116 static void wsreq_session_close(struct afb_wsreq *wsreq);
118 static const struct afb_req_itf wsreq_itf = {
119 .get = (void*)wsreq_get,
120 .iterate = (void*)wsreq_iterate,
121 .fail = (void*)wsreq_fail,
122 .success = (void*)wsreq_success,
123 .session_create = (void*)wsreq_session_create,
124 .session_check = (void*)wsreq_session_check,
125 .session_close = (void*)wsreq_session_close
128 static int aws_handle_json(struct afb_ws_json *aws, struct json_object *obj)
132 struct json_object *type, *id, *name, *req, *token;
133 struct afb_wsreq *wsreq;
134 const char *api, *verb;
135 size_t lenapi, lenverb;
137 /* protocol inspired by http://www.gir.fr/ocppjs/ocpp_srpc_spec.shtml */
139 /* the object must be an array of 4 or 5 elements */
140 if (!json_object_is_type(obj, json_type_array))
142 count = json_object_array_length(obj);
143 if (count < 4 || count > 5)
146 /* get the 5 elements: type id name request token */
147 type = json_object_array_get_idx(obj, 0);
148 id = json_object_array_get_idx(obj, 1);
149 name = json_object_array_get_idx(obj, 2);
150 req = json_object_array_get_idx(obj, 3);
151 token = json_object_array_get_idx(obj, 4);
153 /* check the types: int string string object string */
154 if (!json_object_is_type(type, json_type_int))
156 if (!json_object_is_type(id, json_type_string))
158 if (!json_object_is_type(name, json_type_string))
160 if (!json_object_is_type(req, json_type_object))
162 if (token != NULL && !json_object_is_type(token, json_type_string))
165 /* the type is only 2 */
166 num = json_object_get_int(type);
170 /* checks the api/verb structure of name */
171 api = json_object_get_string(name);
172 for (lenapi = 0 ; api[lenapi] && api[lenapi] != '/' ; lenapi++);
173 if (!lenapi || !api[lenapi])
175 verb = &api[lenapi+1];
176 for (lenverb = 0 ; verb[lenverb] && verb[lenverb] != '/' ; lenverb++);
177 if (!lenverb || verb[lenverb])
180 /* allocates the request data */
181 wsreq = malloc(sizeof *wsreq);
185 /* fill and record the request */
187 wsreq->id = json_object_get(id);
188 wsreq->name = json_object_get(name);
189 wsreq->token = json_object_get(token);
190 wsreq->request = json_object_get(req);
191 wsreq->next = aws->requests;
192 aws->requests = wsreq;
193 json_object_put(obj);
197 afb_apis_call(r, aws->context, api, lenapi, verb, lenverb);
201 json_object_put(obj);
205 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size)
207 struct json_object *obj;
208 json_tokener_reset(ws->tokener);
209 obj = json_tokener_parse_ex(ws->tokener, text, (int)size);
211 afb_ws_close(ws->ws, 1008);
212 } else if (!aws_handle_json(ws, obj)) {
213 afb_ws_close(ws->ws, 1008);
217 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
220 struct json_object *value;
222 if (json_object_object_get_ex(wsreq->request, name, &value)) {
224 arg.value = json_object_get_string(value);
225 arg.size = strlen(arg.value);
235 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure)
238 struct json_object_iterator it = json_object_iter_begin(wsreq->request);
239 struct json_object_iterator end = json_object_iter_end(wsreq->request);
243 while(!json_object_iter_equal(&it, &end)) {
244 arg.name = json_object_iter_peek_name(&it);
245 arg.value = json_object_get_string(json_object_iter_peek_value(&it));
246 if (!iterator(closure, arg))
248 json_object_iter_next(&it);
252 static int wsreq_session_create(struct afb_wsreq *wsreq)
254 struct AFB_clientCtx *context = wsreq->aws->context;
255 if (context->created)
257 return wsreq_session_check(wsreq, 1);
260 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
263 struct AFB_clientCtx *context = wsreq->aws->context;
265 if (wsreq->token == NULL)
268 token = json_object_get_string(wsreq->token);
272 if (!ctxTokenCheck (context, token))
276 ctxTokenNew (context);
282 static void wsreq_session_close(struct afb_wsreq *wsreq)
284 struct AFB_clientCtx *context = wsreq->aws->context;
285 ctxClientClose(context);
289 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
291 json_object *root, *request, *reply;
294 /* builds the answering structure */
295 root = json_object_new_object();
296 json_object_object_add(root, "jtype", json_object_new_string("afb-reply"));
297 request = json_object_new_object();
298 json_object_object_add(root, "request", request);
299 json_object_object_add(request, "status", json_object_new_string(status));
301 json_object_object_add(request, "info", json_object_new_string(info));
303 json_object_object_add(root, "response", resp);
306 reply = json_object_new_array();
307 json_object_array_add(reply, json_object_new_int(retcode));
308 json_object_array_add(reply, wsreq->id);
309 json_object_array_add(reply, root);
310 json_object_array_add(reply, json_object_new_string(wsreq->aws->context->token));
312 /* emits the reply */
313 message = json_object_to_json_string(reply);
314 afb_ws_text(wsreq->aws->ws, message, strlen(message));
315 json_object_put(reply);
317 /* TODO eliminates the wsreq */
320 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
322 wsreq_reply(wsreq, 4, status, info, NULL);
325 static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info)
327 wsreq_reply(wsreq, 3, "success", info, obj);