2 * Copyright (C) 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.
24 #include <json-c/json.h>
26 #include <afb/afb-req-itf.h>
27 #include <afb/afb-event-itf.h>
29 #include "afb-context.h"
38 struct afb_hook *next; /* next hook */
39 unsigned refcount; /* reference count */
42 struct AFB_clientCtx *session;
43 unsigned flags; /* hook flags */
44 struct afb_hook_req_itf *reqitf;
48 struct hook_req_observer {
49 struct afb_hook *hook;
50 struct hook_req_observer *next;
54 * Structure recording a request to hook
57 struct hook_req_observer *observers; /* observers */
58 struct afb_context *context; /* context of the request */
59 struct afb_req req; /* the request hookd */
60 unsigned refcount; /* reference count proxy for request */
61 char name[1]; /* hook info for the request */
65 * Structure for handling subcalls callbacks
68 struct afb_hook_req *tr; /* hookd request */
69 void (*callback)(void*, int, struct json_object*); /* client callback */
70 void *cb_closure; /* cient closure */
73 static unsigned hook_count = 0;
75 static struct afb_hook *list_of_hooks = NULL;
77 /******************************************************************************
78 * section: default callbacks for tracing requests
79 *****************************************************************************/
81 static void _hook_(const struct afb_hook_req *tr, const char *format, ...)
88 len = vasprintf(&buffer, format, ap);
92 NOTICE("tracing %s allocation error", tr->name);
94 NOTICE("hook %s %s", tr->name, buffer);
99 static void hook_req_begin_default_cb(void * closure, const struct afb_hook_req *tr)
104 static void hook_req_end_default_cb(void * closure, const struct afb_hook_req *tr)
109 static void hook_req_json_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj)
111 _hook_(tr, "json() -> %s", json_object_to_json_string(obj));
114 static void hook_req_get_default_cb(void * closure, const struct afb_hook_req *tr, const char *name, struct afb_arg arg)
116 _hook_(tr, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
119 static void hook_req_success_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj, const char *info)
121 _hook_(tr, "success(%s, %s)", json_object_to_json_string(obj), info);
124 static void hook_req_fail_default_cb(void * closure, const struct afb_hook_req *tr, const char *status, const char *info)
126 _hook_(tr, "fail(%s, %s)", status, info);
129 static void hook_req_raw_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size)
131 _hook_(tr, "raw() -> %.*s", (int)size, buffer);
134 static void hook_req_send_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size)
136 _hook_(tr, "send(%.*s)", (int)size, buffer);
139 static void hook_req_context_get_default_cb(void * closure, const struct afb_hook_req *tr, void *value)
141 _hook_(tr, "context_get() -> %p", value);
144 static void hook_req_context_set_default_cb(void * closure, const struct afb_hook_req *tr, void *value, void (*free_value)(void*))
146 _hook_(tr, "context_set(%p, %p)", value, free_value);
149 static void hook_req_addref_default_cb(void * closure, const struct afb_hook_req *tr)
151 _hook_(tr, "addref()");
154 static void hook_req_unref_default_cb(void * closure, const struct afb_hook_req *tr)
156 _hook_(tr, "unref()");
159 static void hook_req_session_close_default_cb(void * closure, const struct afb_hook_req *tr)
161 _hook_(tr, "session_close()");
164 static void hook_req_session_set_LOA_default_cb(void * closure, const struct afb_hook_req *tr, unsigned level, int result)
166 _hook_(tr, "session_set_LOA(%u) -> %d", level, result);
169 static void hook_req_subscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result)
171 _hook_(tr, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
174 static void hook_req_unsubscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result)
176 _hook_(tr, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
179 static void hook_req_subcall_default_cb(void * closure, const struct afb_hook_req *tr, const char *api, const char *verb, struct json_object *args)
181 _hook_(tr, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
184 static void hook_req_subcall_result_default_cb(void * closure, const struct afb_hook_req *tr, int status, struct json_object *result)
186 _hook_(tr, " ...subcall... -> %d: %s", status, json_object_to_json_string(result));
189 static struct afb_hook_req_itf hook_req_default_itf = {
190 .hook_req_begin = hook_req_begin_default_cb,
191 .hook_req_end = hook_req_end_default_cb,
192 .hook_req_json = hook_req_json_default_cb,
193 .hook_req_get = hook_req_get_default_cb,
194 .hook_req_success = hook_req_success_default_cb,
195 .hook_req_fail = hook_req_fail_default_cb,
196 .hook_req_raw = hook_req_raw_default_cb,
197 .hook_req_send = hook_req_send_default_cb,
198 .hook_req_context_get = hook_req_context_get_default_cb,
199 .hook_req_context_set = hook_req_context_set_default_cb,
200 .hook_req_addref = hook_req_addref_default_cb,
201 .hook_req_unref = hook_req_unref_default_cb,
202 .hook_req_session_close = hook_req_session_close_default_cb,
203 .hook_req_session_set_LOA = hook_req_session_set_LOA_default_cb,
204 .hook_req_subscribe = hook_req_subscribe_default_cb,
205 .hook_req_unsubscribe = hook_req_unsubscribe_default_cb,
206 .hook_req_subcall = hook_req_subcall_default_cb,
207 .hook_req_subcall_result = hook_req_subcall_result_default_cb,
210 /******************************************************************************
211 * section: macro for tracing requests
212 *****************************************************************************/
214 #define TRACE_REQX(what,tr) do{\
215 struct hook_req_observer *observer = tr->observers;\
216 while (observer != NULL) {\
217 struct afb_hook *hook = observer->hook;\
218 observer = observer->next;\
219 if (hook->reqitf && hook->reqitf->hook_req_##what)\
220 hook->reqitf->hook_req_##what(hook->closure, tr);\
224 #define TRACE_REQ_(what,tr) do{\
225 struct hook_req_observer *observer = tr->observers;\
226 while (observer != NULL) {\
227 struct afb_hook *hook = observer->hook;\
228 observer = observer->next;\
229 if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\
230 hook->reqitf->hook_req_##what(hook->closure, tr);\
234 #define TRACE_REQ(what,tr,...) do{\
235 struct hook_req_observer *observer = tr->observers;\
236 while (observer != NULL) {\
237 struct afb_hook *hook = observer->hook;\
238 observer = observer->next;\
239 if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\
240 hook->reqitf->hook_req_##what(hook->closure, tr, __VA_ARGS__);\
244 /******************************************************************************
245 * section: afb_hook_req handling
246 *****************************************************************************/
248 static void hook_req_addref(struct afb_hook_req *tr)
253 static void hook_req_unref(struct afb_hook_req *tr)
255 struct hook_req_observer *o1, *o2;
256 if (!--tr->refcount) {
258 afb_req_unref(tr->req);
261 afb_hook_unref(o1->hook);
270 static struct afb_hook_req *hook_req_create(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb)
273 struct afb_hook_req *tr;
275 tr = malloc(sizeof *tr + 8 + lenapi + lenverb);
277 /* get the call id */
283 tr->observers = NULL;
285 tr->context = context;
288 snprintf(tr->name, 9 + lenapi + lenverb, "%06d:%.*s/%.*s", id, (int)lenapi, api, (int)lenverb, verb);
293 static void hook_req_add_observer(struct afb_hook_req *tr, struct afb_hook *hook)
295 struct hook_req_observer *observer;
297 observer = malloc(sizeof *observer);
299 observer->hook = afb_hook_addref(hook);
300 observer->next = tr->observers;
301 tr->observers = observer;
305 /******************************************************************************
306 * section: hooks for tracing requests
307 *****************************************************************************/
309 static struct json_object *req_hook_json(void *closure)
311 struct afb_hook_req *tr = closure;
312 struct json_object *r;
314 r = afb_req_json(tr->req);
315 TRACE_REQ(json, tr, r);
319 static struct afb_arg req_hook_get(void *closure, const char *name)
321 struct afb_hook_req *tr = closure;
324 a = afb_req_get(tr->req, name);
325 TRACE_REQ(get, tr, name, a);
329 static void req_hook_success(void *closure, struct json_object *obj, const char *info)
331 struct afb_hook_req *tr = closure;
333 TRACE_REQ(success, tr, obj, info);
334 afb_req_success(tr->req, obj, info);
338 static void req_hook_fail(void *closure, const char *status, const char *info)
340 struct afb_hook_req *tr = closure;
342 TRACE_REQ(fail, tr, status, info);
343 afb_req_fail(tr->req, status, info);
347 static const char *req_hook_raw(void *closure, size_t *size)
349 struct afb_hook_req *tr = closure;
353 r = afb_req_raw(tr->req, &s);
354 TRACE_REQ(raw, tr, r, s);
360 static void req_hook_send(void *closure, const char *buffer, size_t size)
362 struct afb_hook_req *tr = closure;
364 TRACE_REQ(send, tr, buffer, size);
365 afb_req_send(tr->req, buffer, size);
368 static void *req_hook_context_get(void *closure)
370 struct afb_hook_req *tr = closure;
373 r = afb_req_context_get(tr->req);
374 TRACE_REQ(context_get, tr, r);
379 static void req_hook_context_set(void *closure, void *value, void (*free_value)(void*))
381 struct afb_hook_req *tr = closure;
383 TRACE_REQ(context_set, tr, value, free_value);
384 afb_req_context_set(tr->req, value, free_value);
387 static void req_hook_addref(void *closure)
389 struct afb_hook_req *tr = closure;
391 TRACE_REQ_(addref, tr);
395 static void req_hook_unref(void *closure)
397 struct afb_hook_req *tr = closure;
399 TRACE_REQ_(unref, tr);
403 static void req_hook_session_close(void *closure)
405 struct afb_hook_req *tr = closure;
407 TRACE_REQ_(session_close, tr);
408 afb_req_session_close(tr->req);
411 static int req_hook_session_set_LOA(void *closure, unsigned level)
413 struct afb_hook_req *tr = closure;
416 r = afb_req_session_set_LOA(tr->req, level);
417 TRACE_REQ(session_set_LOA, tr, level, r);
421 static int req_hook_subscribe(void *closure, struct afb_event event)
423 struct afb_hook_req *tr = closure;
426 r = afb_req_subscribe(tr->req, event);
427 TRACE_REQ(subscribe, tr, event, r);
431 static int req_hook_unsubscribe(void *closure, struct afb_event event)
433 struct afb_hook_req *tr = closure;
436 r = afb_req_unsubscribe(tr->req, event);
437 TRACE_REQ(unsubscribe, tr, event, r);
441 static void req_hook_subcall_result(void *closure, int status, struct json_object *result)
443 struct hook_subcall *sc = closure;
444 struct hook_subcall s = *sc;
447 TRACE_REQ(subcall_result, s.tr, status, result);
448 hook_req_unref(s.tr);
449 s.callback(s.cb_closure, status, result);
452 static void req_hook_subcall(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
454 struct afb_hook_req *tr = closure;
455 struct hook_subcall *sc;
457 TRACE_REQ(subcall, tr, api, verb, args);
458 sc = malloc(sizeof *sc);
461 sc->callback = callback;
462 sc->cb_closure = cb_closure;
465 callback = req_hook_subcall_result;
467 afb_req_subcall(tr->req, api, verb, args, callback, cb_closure);
470 static struct afb_req_itf req_hook_itf = {
471 .json = req_hook_json,
473 .success = req_hook_success,
474 .fail = req_hook_fail,
476 .send = req_hook_send,
477 .context_get = req_hook_context_get,
478 .context_set = req_hook_context_set,
479 .addref = req_hook_addref,
480 .unref = req_hook_unref,
481 .session_close = req_hook_session_close,
482 .session_set_LOA = req_hook_session_set_LOA,
483 .subscribe = req_hook_subscribe,
484 .unsubscribe = req_hook_unsubscribe,
485 .subcall = req_hook_subcall
488 /******************************************************************************
490 *****************************************************************************/
492 struct afb_req afb_hook_req_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb)
495 struct afb_hook_req *tr;
496 struct afb_hook *hook;
498 hook = list_of_hooks;
502 add = (hook->flags & afb_hook_flags_req_all) != 0
503 && (!hook->session || hook->session == context->session)
504 && (!hook->api || !(memcmp(hook->api, api, lenapi) || hook->api[lenapi]))
505 && (!hook->verb || !(memcmp(hook->verb, verb, lenverb) || hook->verb[lenverb]));
508 tr = hook_req_create(req, context, api, lenapi, verb, lenverb);
510 hook_req_add_observer(tr, hook);
516 req.itf = &req_hook_itf;
517 TRACE_REQX(begin, tr);
524 struct afb_hook *afb_hook_req_create(const char *api, const char *verb, struct AFB_clientCtx *session, unsigned flags, struct afb_hook_req_itf *itf, void *closure)
526 struct afb_hook *hook;
528 hook = malloc(sizeof *hook);
532 hook->api = api ? strdup(api) : NULL;
533 hook->verb = verb ? strdup(verb) : NULL;
534 hook->session = session ? ctxClientAddRef(session) : NULL;
536 if ((api && !hook->api) || (verb && !hook->verb)) {
540 ctxClientUnref(hook->session);
547 hook->reqitf = itf ? itf : &hook_req_default_itf;
548 hook->closure = closure;
550 hook->next = list_of_hooks;
551 list_of_hooks = hook;
555 struct afb_hook *afb_hook_addref(struct afb_hook *hook)
561 void afb_hook_unref(struct afb_hook *hook)
563 if (!--hook->refcount) {
565 struct afb_hook **prv = &list_of_hooks;
566 while (*prv && *prv != hook)
575 ctxClientUnref(hook->session);