2 * Copyright 2016 IoT.bzh
3 * Author: José Bollo <jose.bollo@iot.bzh>
5 * Inspired by the work of
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
30 #include "utils-upoll.h"
32 static ssize_t aws_writev(struct afb_ws *ws, const struct iovec *iov, int iovcnt);
33 static ssize_t aws_readv(struct afb_ws *ws, const struct iovec *iov, int iovcnt);
34 static void aws_disconnect(struct afb_ws *ws);
35 static void aws_on_close(struct afb_ws *ws, uint16_t code, size_t size);
36 static void aws_on_content(struct afb_ws *ws, int last, size_t size);
37 static void aws_on_readable(struct afb_ws *ws);
39 static struct websock_itf aws_itf = {
40 .writev = (void*)aws_writev,
41 .readv = (void*)aws_readv,
42 .disconnect = (void*)aws_disconnect,
46 .on_close = (void*)aws_on_close,
47 .on_text = (void*)aws_on_content,
48 .on_binary = (void*)aws_on_content,
49 .on_continue = (void*)aws_on_content,
56 struct afb_wsreq *next;
57 struct json_object *id;
58 struct json_object *name;
59 struct json_object *token;
60 struct json_object *request;
68 void (*cleanup)(void*);
69 void *cleanup_closure;
70 struct AFB_clientCtx *context;
71 struct afb_wsreq *requests;
74 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
75 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure);
76 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
77 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
78 static int wsreq_session_create(struct afb_wsreq *wsreq);
79 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
80 static void wsreq_session_close(struct afb_wsreq *wsreq);
82 static const struct afb_req_itf wsreq_itf = {
83 .get = (void*)wsreq_get,
84 .iterate = (void*)wsreq_iterate,
85 .fail = (void*)wsreq_fail,
86 .success = (void*)wsreq_success,
87 .session_create = (void*)wsreq_session_create,
88 .session_check = (void*)wsreq_session_check,
89 .session_close = (void*)wsreq_session_close
92 struct afb_ws *afb_ws_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *closure)
94 struct afb_ws *result;
97 assert(context != NULL);
99 result = malloc(sizeof * result);
104 result->cleanup = cleanup;
105 result->cleanup_closure = closure;
106 result->context = ctxClientGet(context);
107 if (result->context == NULL)
110 result->ws = websock_create_v13(&aws_itf, result);
111 if (result->ws == NULL)
114 result->up = upoll_open(result->fd, result);
115 if (result->up == NULL)
118 upoll_on_readable(result->up, (void*)aws_on_readable);
119 upoll_on_hangup(result->up, (void*)aws_disconnect);
122 websock_destroy(result->ws);
124 ctxClientPut(result->context);
132 static ssize_t aws_writev(struct afb_ws *ws, const struct iovec *iov, int iovcnt)
136 rc = writev(ws->fd, iov, iovcnt);
137 } while(rc == -1 && errno == EINTR);
141 static ssize_t aws_readv(struct afb_ws *ws, const struct iovec *iov, int iovcnt)
145 rc = readv(ws->fd, iov, iovcnt);
146 } while(rc == -1 && errno == EINTR);
150 static void aws_disconnect(struct afb_ws *ws)
153 websock_destroy(ws->ws);
155 MHD_resume_connection (ws->connection);
156 ctxClientPut(ws->context);
157 json_tokener_free(ws->tokener);
161 static void aws_on_close(struct afb_ws *ws, uint16_t code, size_t size)
166 static void aws_on_readable(struct afb_ws *ws)
168 websock_dispatch(ws->ws);
171 static int aws_handle_json(struct afb_ws *aws, struct json_object *obj)
175 struct json_object *type, *id, *name, *req, *token;
176 struct afb_wsreq *wsreq;
177 const char *api, *verb;
178 size_t lenapi, lenverb;
180 /* protocol inspired by http://www.gir.fr/ocppjs/ocpp_srpc_spec.shtml */
182 /* the object must be an array of 4 or 5 elements */
183 if (!json_object_is_type(obj, json_type_array))
185 count = json_object_array_length(obj);
186 if (count < 4 || count > 5)
189 /* get the 5 elements: type id name request token */
190 type = json_object_array_get_idx(obj, 0);
191 id = json_object_array_get_idx(obj, 1);
192 name = json_object_array_get_idx(obj, 2);
193 req = json_object_array_get_idx(obj, 3);
194 token = json_object_array_get_idx(obj, 4);
196 /* check the types: int string string object string */
197 if (!json_object_is_type(type, json_type_int))
199 if (!json_object_is_type(id, json_type_string))
201 if (!json_object_is_type(name, json_type_string))
203 if (!json_object_is_type(req, json_type_object))
205 if (token != NULL && !json_object_is_type(token, json_type_string))
208 /* the type is only 2 */
209 num = json_object_get_int(type);
213 /* checks the api/verb structure of name */
214 api = json_object_get_string(name);
215 for (lenapi = 0 ; api[lenapi] && api[lenapi] != '/' ; lenapi++);
216 if (!lenapi || !api[lenapi])
218 verb = &api[lenapi+1];
219 for (lenverb = 0 ; verb[lenverb] && verb[lenverb] != '/' ; lenverb++);
220 if (!lenverb || verb[lenverb])
223 /* allocates the request data */
224 wsreq = malloc(sizeof *wsreq);
228 /* fill and record the request */
230 wsreq->id = json_object_get(id);
231 wsreq->name = json_object_get(name);
232 wsreq->token = json_object_get(token);
233 wsreq->request = json_object_get(req);
234 wsreq->next = aws->requests;
235 aws->requests = wsreq;
236 json_object_put(obj);
240 afb_apis_call(r, aws->context, api, lenapi, verb, lenverb);
244 json_object_put(obj);
248 static void aws_on_content(struct afb_ws *ws, int last, size_t size)
252 struct json_object *obj;
254 json_tokener_reset(ws->tokener);
256 rrc = websock_read(ws->ws, buffer,
257 size > sizeof buffer ? sizeof buffer : size);
259 websock_close(ws->ws);
263 obj = json_tokener_parse_ex(ws->tokener, buffer, (int)rrc);
265 if (!aws_handle_json(ws, obj)) {
266 websock_close(ws->ws);
269 } else if (json_tokener_get_error(ws->tokener) != json_tokener_continue) {
270 websock_close(ws->ws);
276 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
279 struct json_object *value;
281 if (json_object_object_get_ex(wsreq->request, name, &value)) {
283 arg.value = json_object_get_string(value);
284 arg.size = strlen(arg.value);
294 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure)
297 struct json_object_iterator it = json_object_iter_begin(wsreq->request);
298 struct json_object_iterator end = json_object_iter_end(wsreq->request);
302 while(!json_object_iter_equal(&it, &end)) {
303 arg.name = json_object_iter_peek_name(&it);
304 arg.value = json_object_get_string(json_object_iter_peek_value(&it));
305 if (!iterator(closure, arg))
307 json_object_iter_next(&it);
311 static int wsreq_session_create(struct afb_wsreq *wsreq)
313 struct AFB_clientCtx *context = wsreq->aws->context;
314 if (context->created)
316 return wsreq_session_check(wsreq, 1);
319 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
322 struct AFB_clientCtx *context = wsreq->aws->context;
324 if (wsreq->token == NULL)
327 token = json_object_get_string(wsreq->token);
331 if (!ctxTokenCheck (context, token))
335 ctxTokenNew (context);
341 static void wsreq_session_close(struct afb_wsreq *wsreq)
343 struct AFB_clientCtx *context = wsreq->aws->context;
344 ctxClientClose(context);
348 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
350 json_object *root, *request, *reply;
353 /* builds the answering structure */
354 root = json_object_new_object();
355 json_object_object_add(root, "jtype", json_object_new_string("afb-reply"));
356 request = json_object_new_object();
357 json_object_object_add(root, "request", request);
358 json_object_object_add(request, "status", json_object_new_string(status));
360 json_object_object_add(request, "info", json_object_new_string(info));
362 json_object_object_add(root, "response", resp);
365 reply = json_object_new_array();
366 json_object_array_add(reply, json_object_new_int(retcode));
367 json_object_array_add(reply, wsreq->id);
368 json_object_array_add(reply, root);
369 json_object_array_add(reply, json_object_new_string(wsreq->aws->context->token));
371 /* emits the reply */
372 message = json_object_to_json_string(reply);
373 websock_text(wsreq->aws->ws, message, strlen(message));
374 json_object_put(reply);
376 /* TODO eliminates the wsreq */
379 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
381 wsreq_reply(wsreq, 4, status, info, NULL);
384 static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info)
386 wsreq_reply(wsreq, 3, "success", info, obj);