2 * Copyright (C) 2016, 2017 "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"
31 #include "afb-session.h"
35 * Definition of a hook
38 struct afb_hook *next; /* next hook */
39 unsigned refcount; /* reference count */
40 char *api; /* api hooked or NULL for any */
41 char *verb; /* verb hooked or NULL for any */
42 struct afb_session *session; /* session hooked or NULL if any */
43 unsigned flags; /* hook flags */
44 struct afb_hook_req_itf *reqitf; /* interface of hook */
45 void *closure; /* closure for callbacks */
49 * Link hooks to a hooked request
51 struct hook_req_observer {
52 struct afb_hook *hook; /* the hook */
53 struct hook_req_observer *next; /* the next observer */
57 * Structure recording a request to hook
60 struct hook_req_observer *observers; /* observers */
61 struct afb_context *context; /* context of the request */
62 struct afb_req req; /* the request hookd */
63 unsigned refcount; /* reference count proxy for request */
64 char name[1]; /* hook info for the request */
68 * Structure for handling subcalls callbacks
71 struct afb_hook_req *tr; /* hookd request */
72 void (*callback)(void*, int, struct json_object*); /* client callback */
73 void *cb_closure; /* cient closure */
76 /* counter of hooking */
77 static unsigned hook_count = 0;
80 static struct afb_hook *list_of_hooks = NULL;
82 /******************************************************************************
83 * section: default callbacks for tracing requests
84 *****************************************************************************/
86 static void _hook_(const struct afb_hook_req *tr, const char *format, ...)
93 len = vasprintf(&buffer, format, ap);
97 NOTICE("tracing %s allocation error", tr->name);
99 NOTICE("hook %s %s", tr->name, buffer);
104 static void hook_req_begin_default_cb(void * closure, const struct afb_hook_req *tr)
109 static void hook_req_end_default_cb(void * closure, const struct afb_hook_req *tr)
114 static void hook_req_json_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj)
116 _hook_(tr, "json() -> %s", json_object_to_json_string(obj));
119 static void hook_req_get_default_cb(void * closure, const struct afb_hook_req *tr, const char *name, struct afb_arg arg)
121 _hook_(tr, "get(%s) -> { name: %s, value: %s, path: %s }", name, arg.name, arg.value, arg.path);
124 static void hook_req_success_default_cb(void * closure, const struct afb_hook_req *tr, struct json_object *obj, const char *info)
126 _hook_(tr, "success(%s, %s)", json_object_to_json_string(obj), info);
129 static void hook_req_fail_default_cb(void * closure, const struct afb_hook_req *tr, const char *status, const char *info)
131 _hook_(tr, "fail(%s, %s)", status, info);
134 static void hook_req_raw_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size)
136 _hook_(tr, "raw() -> %.*s", (int)size, buffer);
139 static void hook_req_send_default_cb(void * closure, const struct afb_hook_req *tr, const char *buffer, size_t size)
141 _hook_(tr, "send(%.*s)", (int)size, buffer);
144 static void hook_req_context_get_default_cb(void * closure, const struct afb_hook_req *tr, void *value)
146 _hook_(tr, "context_get() -> %p", value);
149 static void hook_req_context_set_default_cb(void * closure, const struct afb_hook_req *tr, void *value, void (*free_value)(void*))
151 _hook_(tr, "context_set(%p, %p)", value, free_value);
154 static void hook_req_addref_default_cb(void * closure, const struct afb_hook_req *tr)
156 _hook_(tr, "addref()");
159 static void hook_req_unref_default_cb(void * closure, const struct afb_hook_req *tr)
161 _hook_(tr, "unref()");
164 static void hook_req_session_close_default_cb(void * closure, const struct afb_hook_req *tr)
166 _hook_(tr, "session_close()");
169 static void hook_req_session_set_LOA_default_cb(void * closure, const struct afb_hook_req *tr, unsigned level, int result)
171 _hook_(tr, "session_set_LOA(%u) -> %d", level, result);
174 static void hook_req_subscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result)
176 _hook_(tr, "subscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
179 static void hook_req_unsubscribe_default_cb(void * closure, const struct afb_hook_req *tr, struct afb_event event, int result)
181 _hook_(tr, "unsubscribe(%s:%p) -> %d", afb_event_name(event), event.closure, result);
184 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)
186 _hook_(tr, "subcall(%s/%s, %s) ...", api, verb, json_object_to_json_string(args));
189 static void hook_req_subcall_result_default_cb(void * closure, const struct afb_hook_req *tr, int status, struct json_object *result)
191 _hook_(tr, " ...subcall... -> %d: %s", status, json_object_to_json_string(result));
194 static struct afb_hook_req_itf hook_req_default_itf = {
195 .hook_req_begin = hook_req_begin_default_cb,
196 .hook_req_end = hook_req_end_default_cb,
197 .hook_req_json = hook_req_json_default_cb,
198 .hook_req_get = hook_req_get_default_cb,
199 .hook_req_success = hook_req_success_default_cb,
200 .hook_req_fail = hook_req_fail_default_cb,
201 .hook_req_raw = hook_req_raw_default_cb,
202 .hook_req_send = hook_req_send_default_cb,
203 .hook_req_context_get = hook_req_context_get_default_cb,
204 .hook_req_context_set = hook_req_context_set_default_cb,
205 .hook_req_addref = hook_req_addref_default_cb,
206 .hook_req_unref = hook_req_unref_default_cb,
207 .hook_req_session_close = hook_req_session_close_default_cb,
208 .hook_req_session_set_LOA = hook_req_session_set_LOA_default_cb,
209 .hook_req_subscribe = hook_req_subscribe_default_cb,
210 .hook_req_unsubscribe = hook_req_unsubscribe_default_cb,
211 .hook_req_subcall = hook_req_subcall_default_cb,
212 .hook_req_subcall_result = hook_req_subcall_result_default_cb,
215 /******************************************************************************
216 * section: macro for tracing requests
217 *****************************************************************************/
219 #define TRACE_REQX(what,tr) do{\
220 struct hook_req_observer *observer = tr->observers;\
221 while (observer != NULL) {\
222 struct afb_hook *hook = observer->hook;\
223 observer = observer->next;\
224 if (hook->reqitf && hook->reqitf->hook_req_##what)\
225 hook->reqitf->hook_req_##what(hook->closure, tr);\
229 #define TRACE_REQ_(what,tr) do{\
230 struct hook_req_observer *observer = tr->observers;\
231 while (observer != NULL) {\
232 struct afb_hook *hook = observer->hook;\
233 observer = observer->next;\
234 if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\
235 hook->reqitf->hook_req_##what(hook->closure, tr);\
239 #define TRACE_REQ(what,tr,...) do{\
240 struct hook_req_observer *observer = tr->observers;\
241 while (observer != NULL) {\
242 struct afb_hook *hook = observer->hook;\
243 observer = observer->next;\
244 if ((hook->flags & afb_hook_flag_req_##what) && hook->reqitf && hook->reqitf->hook_req_##what)\
245 hook->reqitf->hook_req_##what(hook->closure, tr, __VA_ARGS__);\
249 /******************************************************************************
250 * section: afb_hook_req handling
251 *****************************************************************************/
253 static void hook_req_addref(struct afb_hook_req *tr)
258 static void hook_req_unref(struct afb_hook_req *tr)
260 struct hook_req_observer *o1, *o2;
261 if (!--tr->refcount) {
263 afb_req_unref(tr->req);
266 afb_hook_unref(o1->hook);
275 static struct afb_hook_req *hook_req_create(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
280 struct afb_hook_req *tr;
282 /* get the call id */
287 /* creates the name */
288 len = snprintf(name, sizeof name, "%06d:%s/%s", id, api, verb);
289 if (len < 0 || (size_t)len >= sizeof name) {
292 tr = malloc(sizeof *tr + (size_t)len);
295 tr->observers = NULL;
297 tr->context = context;
300 memcpy(tr->name, name, (size_t)(len + 1));
306 static void hook_req_add_observer(struct afb_hook_req *tr, struct afb_hook *hook)
308 struct hook_req_observer *observer;
310 observer = malloc(sizeof *observer);
312 observer->hook = afb_hook_addref(hook);
313 observer->next = tr->observers;
314 tr->observers = observer;
318 /******************************************************************************
319 * section: hooks for tracing requests
320 *****************************************************************************/
322 static struct json_object *req_hook_json(void *closure)
324 struct afb_hook_req *tr = closure;
325 struct json_object *r;
327 r = afb_req_json(tr->req);
328 TRACE_REQ(json, tr, r);
332 static struct afb_arg req_hook_get(void *closure, const char *name)
334 struct afb_hook_req *tr = closure;
337 a = afb_req_get(tr->req, name);
338 TRACE_REQ(get, tr, name, a);
342 static void req_hook_success(void *closure, struct json_object *obj, const char *info)
344 struct afb_hook_req *tr = closure;
346 TRACE_REQ(success, tr, obj, info);
347 afb_req_success(tr->req, obj, info);
351 static void req_hook_fail(void *closure, const char *status, const char *info)
353 struct afb_hook_req *tr = closure;
355 TRACE_REQ(fail, tr, status, info);
356 afb_req_fail(tr->req, status, info);
360 static const char *req_hook_raw(void *closure, size_t *size)
362 struct afb_hook_req *tr = closure;
366 r = afb_req_raw(tr->req, &s);
367 TRACE_REQ(raw, tr, r, s);
373 static void req_hook_send(void *closure, const char *buffer, size_t size)
375 struct afb_hook_req *tr = closure;
377 TRACE_REQ(send, tr, buffer, size);
378 afb_req_send(tr->req, buffer, size);
381 static void *req_hook_context_get(void *closure)
383 struct afb_hook_req *tr = closure;
386 r = afb_req_context_get(tr->req);
387 TRACE_REQ(context_get, tr, r);
392 static void req_hook_context_set(void *closure, void *value, void (*free_value)(void*))
394 struct afb_hook_req *tr = closure;
396 TRACE_REQ(context_set, tr, value, free_value);
397 afb_req_context_set(tr->req, value, free_value);
400 static void req_hook_addref(void *closure)
402 struct afb_hook_req *tr = closure;
404 TRACE_REQ_(addref, tr);
408 static void req_hook_unref(void *closure)
410 struct afb_hook_req *tr = closure;
412 TRACE_REQ_(unref, tr);
416 static void req_hook_session_close(void *closure)
418 struct afb_hook_req *tr = closure;
420 TRACE_REQ_(session_close, tr);
421 afb_req_session_close(tr->req);
424 static int req_hook_session_set_LOA(void *closure, unsigned level)
426 struct afb_hook_req *tr = closure;
429 r = afb_req_session_set_LOA(tr->req, level);
430 TRACE_REQ(session_set_LOA, tr, level, r);
434 static int req_hook_subscribe(void *closure, struct afb_event event)
436 struct afb_hook_req *tr = closure;
439 r = afb_req_subscribe(tr->req, event);
440 TRACE_REQ(subscribe, tr, event, r);
444 static int req_hook_unsubscribe(void *closure, struct afb_event event)
446 struct afb_hook_req *tr = closure;
449 r = afb_req_unsubscribe(tr->req, event);
450 TRACE_REQ(unsubscribe, tr, event, r);
454 static void req_hook_subcall_result(void *closure, int status, struct json_object *result)
456 struct hook_subcall *sc = closure;
457 struct hook_subcall s = *sc;
460 TRACE_REQ(subcall_result, s.tr, status, result);
461 hook_req_unref(s.tr);
462 s.callback(s.cb_closure, status, result);
465 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)
467 struct afb_hook_req *tr = closure;
468 struct hook_subcall *sc;
470 TRACE_REQ(subcall, tr, api, verb, args);
471 sc = malloc(sizeof *sc);
474 sc->callback = callback;
475 sc->cb_closure = cb_closure;
478 callback = req_hook_subcall_result;
480 afb_req_subcall(tr->req, api, verb, args, callback, cb_closure);
483 static struct afb_req_itf req_hook_itf = {
484 .json = req_hook_json,
486 .success = req_hook_success,
487 .fail = req_hook_fail,
489 .send = req_hook_send,
490 .context_get = req_hook_context_get,
491 .context_set = req_hook_context_set,
492 .addref = req_hook_addref,
493 .unref = req_hook_unref,
494 .session_close = req_hook_session_close,
495 .session_set_LOA = req_hook_session_set_LOA,
496 .subscribe = req_hook_subscribe,
497 .unsubscribe = req_hook_unsubscribe,
498 .subcall = req_hook_subcall
501 /******************************************************************************
503 *****************************************************************************/
505 struct afb_req afb_hook_req_call(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
508 struct afb_hook_req *tr;
509 struct afb_hook *hook;
511 hook = list_of_hooks;
515 add = (hook->flags & afb_hook_flags_req_all) != 0
516 && (!hook->session || hook->session == context->session)
517 && (!hook->api || !strcasecmp(hook->api, api))
518 && (!hook->verb || !strcasecmp(hook->verb, verb));
521 tr = hook_req_create(req, context, api, verb);
523 hook_req_add_observer(tr, hook);
529 req.itf = &req_hook_itf;
530 TRACE_REQX(begin, tr);
537 struct afb_hook *afb_hook_req_create(const char *api, const char *verb, struct afb_session *session, unsigned flags, struct afb_hook_req_itf *itf, void *closure)
539 struct afb_hook *hook;
541 hook = malloc(sizeof *hook);
545 hook->api = api ? strdup(api) : NULL;
546 hook->verb = verb ? strdup(verb) : NULL;
547 hook->session = session ? afb_session_addref(session) : NULL;
549 if ((api && !hook->api) || (verb && !hook->verb)) {
553 afb_session_unref(hook->session);
560 hook->reqitf = itf ? itf : &hook_req_default_itf;
561 hook->closure = closure;
563 hook->next = list_of_hooks;
564 list_of_hooks = hook;
568 struct afb_hook *afb_hook_addref(struct afb_hook *hook)
574 void afb_hook_unref(struct afb_hook *hook)
576 if (!--hook->refcount) {
578 struct afb_hook **prv = &list_of_hooks;
579 while (*prv && *prv != hook)
588 afb_session_unref(hook->session);