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.
28 #include "utils-upoll.h"
30 static ssize_t aws_writev(struct afb_ws *ws, const struct iovec *iov, int iovcnt);
31 static ssize_t aws_readv(struct afb_ws *ws, const struct iovec *iov, int iovcnt);
32 static void aws_disconnect(struct afb_ws *ws);
33 static void aws_on_close(struct afb_ws *ws, uint16_t code, size_t size);
34 static void aws_on_content(struct afb_ws *ws, int last, size_t size);
35 static void aws_on_readable(struct afb_ws *ws);
37 static struct websock_itf aws_itf = {
38 .writev = (void*)aws_writev,
39 .readv = (void*)aws_readv,
40 .disconnect = (void*)aws_disconnect,
44 .on_close = (void*)aws_on_close,
45 .on_text = (void*)aws_on_content,
46 .on_binary = (void*)aws_on_content,
47 .on_continue = (void*)aws_on_content,
54 struct afb_wsreq *next;
55 struct json_object *id;
56 struct json_object *name;
57 struct json_object *token;
58 struct json_object *request;
66 void (*cleanup)(void*);
67 void *cleanup_closure;
68 struct AFB_clientCtx *context;
69 struct afb_wsreq *requests;
72 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
73 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure);
74 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
75 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
76 static int wsreq_session_create(struct afb_wsreq *wsreq);
77 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
78 static void wsreq_session_close(struct afb_wsreq *wsreq);
80 static const struct afb_req_itf wsreq_itf = {
81 .get = (void*)wsreq_get,
82 .iterate = (void*)wsreq_iterate,
83 .fail = (void*)wsreq_fail,
84 .success = (void*)wsreq_success,
85 .session_create = (void*)wsreq_session_create,
86 .session_check = (void*)wsreq_session_check,
87 .session_close = (void*)wsreq_session_close
90 struct afb_ws *afb_ws_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *closure)
92 struct afb_ws *result;
95 assert(context != NULL);
97 result = malloc(sizeof * result);
102 result->cleanup = cleanup;
103 result->cleanup_closure = closure;
104 result->context = ctxClientGet(context);
105 if (result->context == NULL)
108 result->ws = websock_create_v13(&aws_itf, result);
109 if (result->ws == NULL)
112 result->up = upoll_open(result->fd, result);
113 if (result->up == NULL)
116 upoll_on_readable(result->up, (void*)aws_on_readable);
117 upoll_on_hangup(result->up, (void*)aws_disconnect);
120 websock_destroy(result->ws);
122 ctxClientPut(result->context);
130 static ssize_t aws_writev(struct afb_ws *ws, const struct iovec *iov, int iovcnt)
134 rc = writev(ws->fd, iov, iovcnt);
135 } while(rc == -1 && errno == EINTR);
139 static ssize_t aws_readv(struct afb_ws *ws, const struct iovec *iov, int iovcnt)
143 rc = readv(ws->fd, iov, iovcnt);
144 } while(rc == -1 && errno == EINTR);
148 static void aws_disconnect(struct afb_ws *ws)
151 websock_destroy(ws->ws);
153 MHD_resume_connection (ws->connection);
154 ctxClientPut(ws->context);
155 json_tokener_free(ws->tokener);
159 static void aws_on_close(struct afb_ws *ws, uint16_t code, size_t size)
164 static void aws_on_readable(struct afb_ws *ws)
166 websock_dispatch(ws->ws);
169 static int aws_handle_json(struct afb_ws *aws, struct json_object *obj)
173 struct json_object *type, *id, *name, *req, *token;
174 struct afb_wsreq *wsreq;
175 const char *api, *verb;
176 size_t lenapi, lenverb;
178 /* protocol inspired by http://www.gir.fr/ocppjs/ocpp_srpc_spec.shtml */
180 /* the object must be an array of 4 or 5 elements */
181 if (!json_object_is_type(obj, json_type_array))
183 count = json_object_array_length(obj);
184 if (count < 4 || count > 5)
187 /* get the 5 elements: type id name request token */
188 type = json_object_array_get_idx(obj, 0);
189 id = json_object_array_get_idx(obj, 1);
190 name = json_object_array_get_idx(obj, 2);
191 req = json_object_array_get_idx(obj, 3);
192 token = json_object_array_get_idx(obj, 4);
194 /* check the types: int string string object string */
195 if (!json_object_is_type(type, json_type_int))
197 if (!json_object_is_type(id, json_type_string))
199 if (!json_object_is_type(name, json_type_string))
201 if (!json_object_is_type(req, json_type_object))
203 if (token != NULL && !json_object_is_type(token, json_type_string))
206 /* the type is only 2 */
207 num = json_object_get_int(type);
211 /* checks the api/verb structure of name */
212 api = json_object_get_string(name);
213 for (lenapi = 0 ; api[lenapi] && api[lenapi] != '/' ; lenapi++);
214 if (!lenapi || !api[lenapi])
216 verb = &api[lenapi+1];
217 for (lenverb = 0 ; verb[lenverb] && verb[lenverb] != '/' ; lenverb++);
218 if (!lenverb || verb[lenverb])
221 /* allocates the request data */
222 wsreq = malloc(sizeof *wsreq);
226 /* fill and record the request */
228 wsreq->id = json_object_get(id);
229 wsreq->name = json_object_get(name);
230 wsreq->token = json_object_get(token);
231 wsreq->request = json_object_get(req);
232 wsreq->next = aws->requests;
233 aws->requests = wsreq;
234 json_object_put(obj);
238 afb_apis_call(r, aws->context, api, lenapi, verb, lenverb);
242 json_object_put(obj);
246 static void aws_on_content(struct afb_ws *ws, int last, size_t size)
250 struct json_object *obj;
252 json_tokener_reset(ws->tokener);
254 rrc = websock_read(ws->ws, buffer,
255 size > sizeof buffer ? sizeof buffer : size);
257 websock_close(ws->ws);
261 obj = json_tokener_parse_ex(ws->tokener, buffer, (int)rrc);
263 if (!aws_handle_json(ws, obj)) {
264 websock_close(ws->ws);
267 } else if (json_tokener_get_error(ws->tokener) != json_tokener_continue) {
268 websock_close(ws->ws);
274 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
277 struct json_object *value;
279 if (json_object_object_get_ex(wsreq->request, name, &value)) {
281 arg.value = json_object_get_string(value);
282 arg.size = strlen(arg.value);
292 static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure)
295 struct json_object_iterator it = json_object_iter_begin(wsreq->request);
296 struct json_object_iterator end = json_object_iter_end(wsreq->request);
300 while(!json_object_iter_equal(&it, &end)) {
301 arg.name = json_object_iter_peek_name(&it);
302 arg.value = json_object_get_string(json_object_iter_peek_value(&it));
303 if (!iterator(closure, arg))
305 json_object_iter_next(&it);
309 static int wsreq_session_create(struct afb_wsreq *wsreq)
311 struct AFB_clientCtx *context = wsreq->aws->context;
312 if (context->created)
314 return wsreq_session_check(wsreq, 1);
317 static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
320 struct AFB_clientCtx *context = wsreq->aws->context;
322 if (wsreq->token == NULL)
325 token = json_object_get_string(wsreq->token);
329 if (!ctxTokenCheck (context, token))
333 ctxTokenNew (context);
339 static void wsreq_session_close(struct afb_wsreq *wsreq)
341 struct AFB_clientCtx *context = wsreq->aws->context;
342 ctxClientClose(context);
346 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
348 json_object *root, *request, *reply;
351 /* builds the answering structure */
352 root = json_object_new_object();
353 json_object_object_add(root, "jtype", json_object_new_string("afb-reply"));
354 request = json_object_new_object();
355 json_object_object_add(root, "request", request);
356 json_object_object_add(request, "status", json_object_new_string(status));
358 json_object_object_add(request, "info", json_object_new_string(info));
360 json_object_object_add(root, "response", resp);
363 reply = json_object_new_array();
364 json_object_array_add(reply, json_object_new_int(retcode));
365 json_object_array_add(reply, wsreq->id);
366 json_object_array_add(reply, root);
367 json_object_array_add(reply, json_object_new_string(wsreq->aws->context->token));
369 /* emits the reply */
370 message = json_object_to_json_string(reply);
371 websock_text(wsreq->aws->ws, message, strlen(message));
372 json_object_put(reply);
374 /* TODO eliminates the wsreq */
377 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
379 wsreq_reply(wsreq, 4, status, info, NULL);
382 static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info)
384 wsreq_reply(wsreq, 3, "success", info, obj);