Refactoring requests and context handling
authorJosé Bollo <jose.bollo@iot.bzh>
Tue, 10 May 2016 11:47:58 +0000 (13:47 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Tue, 10 May 2016 11:47:58 +0000 (13:47 +0200)
Also adds a first (untested) implmentation of
the afb services over dbus.

Change-Id: Id1bdeccf75f3a70d3658bdaf0510d6e7b97f6c32
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
20 files changed:
include/afb-plugin.h
include/afb-req-itf.h
plugins/afm-main-plugin/afm-main-plugin.c
src/CMakeLists.txt
src/afb-api-dbus.c [new file with mode: 0644]
src/afb-api-dbus.h [new file with mode: 0644]
src/afb-api-so.c
src/afb-apis.c
src/afb-apis.h
src/afb-context.c [new file with mode: 0644]
src/afb-context.h [new file with mode: 0644]
src/afb-hreq.c
src/afb-hreq.h
src/afb-hsrv.c
src/afb-hswitch.c
src/afb-websock.c
src/afb-ws-json.c
src/main.c
src/session.c
src/session.h

index ce78e84..63617cb 100644 (file)
@@ -31,11 +31,12 @@ enum  AFB_pluginE
 /* Enum for Session/Token/Authentication middleware */
 enum AFB_sessionE
 {
-       AFB_SESSION_NONE,
-       AFB_SESSION_CREATE,
-       AFB_SESSION_CLOSE,
-       AFB_SESSION_RENEW,
-       AFB_SESSION_CHECK
+       AFB_SESSION_NONE = 0,
+       AFB_SESSION_CREATE = 1,
+       AFB_SESSION_CLOSE = 2,
+       AFB_SESSION_RENEW = 4,
+       AFB_SESSION_CHECK = 8,
+       AFB_SESSION_MASK = 15
 };
 
 /* API definition */
index 061c1f0..a975503 100644 (file)
@@ -26,28 +26,30 @@ struct afb_arg {
 };
 
 struct afb_req_itf {
-       struct json_object *(*json)(void *req_closure);
-       struct afb_arg (*get)(void *req_closure, const char *name);
-       void (*success)(void *req_closure, struct json_object *obj, const char *info);
-       void (*fail)(void *req_closure, const char *status, const char *info);
-       const char *(*raw)(void *req_closure, size_t *size);
-       void (*send)(void *req_closure, const char *buffer, size_t size);
-       void *(*context_get)(void *ctx_closure);
-       void (*context_set)(void *ctx_closure, void *value, void (*free_value)(void*));
-       int (*session_create)(void *req_closure);
-       int (*session_check)(void *req_closure, int refresh);
-       void (*session_close)(void *req_closure);
+       struct json_object *(*json)(void *closure);
+       struct afb_arg (*get)(void *closure, const char *name);
+
+       void (*success)(void *closure, struct json_object *obj, const char *info);
+       void (*fail)(void *closure, const char *status, const char *info);
+
+       const char *(*raw)(void *closure, size_t *size);
+       void (*send)(void *closure, const char *buffer, size_t size);
+
+       void *(*context_get)(void *closure);
+       void (*context_set)(void *closure, void *value, void (*free_value)(void*));
+
+       void (*addref)(void *closure);
+       void (*unref)(void *closure);
 };
 
 struct afb_req {
        const struct afb_req_itf *itf;
-       void *req_closure;
-       void *ctx_closure;
+       void *closure;
 };
 
 static inline struct afb_arg afb_req_get(struct afb_req req, const char *name)
 {
-       return req.itf->get(req.req_closure, name);
+       return req.itf->get(req.closure, name);
 }
 
 static inline const char *afb_req_value(struct afb_req req, const char *name)
@@ -62,37 +64,37 @@ static inline const char *afb_req_path(struct afb_req req, const char *name)
 
 static inline struct json_object *afb_req_json(struct afb_req req)
 {
-       return req.itf->json(req.req_closure);
+       return req.itf->json(req.closure);
 }
 
 static inline void afb_req_success(struct afb_req req, struct json_object *obj, const char *info)
 {
-       req.itf->success(req.req_closure, obj, info);
+       req.itf->success(req.closure, obj, info);
 }
 
 static inline void afb_req_fail(struct afb_req req, const char *status, const char *info)
 {
-       req.itf->fail(req.req_closure, status, info);
+       req.itf->fail(req.closure, status, info);
 }
 
 static inline const char *afb_req_raw(struct afb_req req, size_t *size)
 {
-       return req.itf->raw(req.req_closure, size);
+       return req.itf->raw(req.closure, size);
 }
 
 static inline void afb_req_send(struct afb_req req, const char *buffer, size_t size)
 {
-       req.itf->send(req.req_closure, buffer, size);
+       req.itf->send(req.closure, buffer, size);
 }
 
 static inline void *afb_req_context_get(struct afb_req req)
 {
-       return req.itf->context_get(req.ctx_closure);
+       return req.itf->context_get(req.closure);
 }
 
 static inline void afb_req_context_set(struct afb_req req, void *value, void (*free_value)(void*))
 {
-       return req.itf->context_set(req.ctx_closure, value, free_value);
+       return req.itf->context_set(req.closure, value, free_value);
 }
 
 static inline void afb_req_context_clear(struct afb_req req)
@@ -100,25 +102,14 @@ static inline void afb_req_context_clear(struct afb_req req)
        afb_req_context_set(req, NULL, NULL);
 }
 
-static inline int afb_req_session_create(struct afb_req req)
+static inline void afb_req_addref(struct afb_req req)
 {
-       int result = req.itf->session_create(req.req_closure);
-       if (!result)
-               afb_req_fail(req, "fail", "Can't create the session");
-       return result;
-}
-
-static inline int afb_req_session_check(struct afb_req req, int refresh)
-{
-       int result = req.itf->session_check(req.req_closure, refresh);
-       if (!result)
-               afb_req_fail(req, "fail", "Token chek failed for the session");
-       return result;
+       return req.itf->addref(req.closure);
 }
 
-static inline void afb_req_session_close(struct afb_req req)
+static inline void afb_req_unref(struct afb_req req)
 {
-       req.itf->session_close(req.req_closure);
+       return req.itf->unref(req.closure);
 }
 
 #include <stdlib.h>
@@ -126,8 +117,10 @@ static inline void afb_req_session_close(struct afb_req req)
 static inline struct afb_req *afb_req_store(struct afb_req req)
 {
        struct afb_req *result = malloc(sizeof *result);
-       if (result != NULL)
+       if (result != NULL) {
                *result = req;
+               afb_req_addref(req);
+       }
        return result;
 }
 
@@ -135,6 +128,7 @@ static inline struct afb_req afb_req_unstore(struct afb_req *req)
 {
        struct afb_req result = *req;
        free(req);
+       afb_req_unref(result);
        return result;
 }
 
index c6408a5..8b26fe7 100644 (file)
@@ -62,6 +62,7 @@ static struct memo *make_memo(struct afb_req request, const char *method)
        if (memo != NULL) {
                memo->request = request;
                memo->method = method;
+               afb_req_addref(request);
        }
        return memo;
 }
@@ -109,6 +110,7 @@ static void embed_call_void_callback(int status, struct json_object *obj, struct
                        afb_req_success(memo->request, obj, NULL);
                }
        }
+       afb_req_unref(memo->request);
        free(memo);
 }
 
@@ -134,6 +136,7 @@ static void call_appid_callback(int status, struct json_object *obj, struct memo
                obj = json_object_get(obj);
                afb_req_success(memo->request, obj, NULL);
        }
+       afb_req_unref(memo->request);
        free(memo);
 }
 
index bc5fd4f..707b2e0 100644 (file)
@@ -1,20 +1,22 @@
 
 ADD_LIBRARY(src OBJECT
-       main.c
-       session.c
+       afb-api-dbus.c
+       afb-api-so.c
+       afb-apis.c
+       afb-common.c
+       afb-context.c
+       afb-hreq.c
        afb-hsrv.c
        afb-hswitch.c
-       afb-apis.c
-       afb-api-so.c
        afb-method.c
-       afb-hreq.c
+       afb-msg-json.c
        afb-websock.c
-       afb-ws.c
        afb-ws-json.c
-       afb-msg-json.c
-       afb-common.c
-       websock.c
+       afb-ws.c
+       main.c
+       session.c
        verbose.c
+       websock.c
 )
 
 INCLUDE_DIRECTORIES(${include_dirs})
diff --git a/src/afb-api-dbus.c b/src/afb-api-dbus.c
new file mode 100644 (file)
index 0000000..818f8d1
--- /dev/null
@@ -0,0 +1,511 @@
+/* 
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <systemd/sd-bus.h>
+#include <json.h>
+
+#include "afb-plugin.h"
+#include "afb-req-itf.h"
+
+#include "afb-common.h"
+
+#include "session.h"
+#include "afb-apis.h"
+#include "afb-api-so.h"
+#include "afb-context.h"
+#include "verbose.h"
+
+static const char DEFAULT_PATH_PREFIX[] = "/org/agl/afb/api/";
+
+/*
+ * The path given are of the form
+ *     system:/org/agl/afb/api/...
+ * or
+ *     user:/org/agl/afb/api/...
+ */
+struct api_dbus
+{
+       struct sd_bus *sdbus;   /* the bus */
+       char *path;             /* path of the object for the API */
+       char *name;             /* name/interface of the object */
+       char *api;              /* api name of the interface */
+};
+
+#define RETOK   1
+#define RETERR  2
+#define RETRAW  3
+
+/******************* common part **********************************/
+
+/*
+ * create a structure api_dbus connected on either the system
+ * bus if 'system' is not null or on the user bus. The connection
+ * is established for either emiting/receiving on 'path' being of length
+ * 'pathlen'.
+ */
+static struct api_dbus *make_api_dbus_3(int system, const char *path, size_t pathlen)
+{
+       struct api_dbus *api;
+       struct sd_bus *sdbus;
+       char *ptr;
+
+       /* allocates the structure */
+       api = calloc(1, sizeof *api + 1 + pathlen + pathlen);
+       if (api == NULL) {
+               errno = ENOMEM;
+               goto error;
+       }
+
+       /* init the structure's strings */
+
+       /* path is copied after the struct */
+       api->path = (void*)(api+1);
+       strcpy(api->path, path);
+
+       /* api name is at the end of the path */
+       api->api = strrchr(api->path, '/');
+       if (api->api == NULL) {
+               errno = EINVAL;
+               goto error2;
+       }
+       api->api++;
+
+       /* the name/interface is copied after the path */
+       api->name = &api->path[pathlen + 1];
+       strcpy(api->name, &path[1]);
+       ptr = strchr(api->name, '/');
+       while(ptr != NULL) {
+               *ptr = '.';
+               ptr = strchr(ptr, '/');
+       }
+
+       /* choose the bus */
+       sdbus = (system ? afb_common_get_system_bus : afb_common_get_user_bus)();
+       if (sdbus == NULL)
+               goto error2;
+
+       api->sdbus = sdbus;
+       return api;
+
+error2:
+       free(api);
+error:
+       return NULL;
+}
+
+/*
+ * create a structure api_dbus connected on either the system
+ * bus if 'system' is not null or on the user bus. The connection
+ * is established for either emiting/receiving on 'path'.
+ * If 'path' is not absolute, it is prefixed with DEFAULT_PATH_PREFIX.
+ */
+static struct api_dbus *make_api_dbus_2(int system, const char *path)
+{
+       size_t len;
+       char *ptr;
+
+       /* check the length of the path */
+       len = strlen(path);
+       if (len == 0) {
+               errno = EINVAL;
+               return NULL;
+       }
+
+       /* if the path is absolute, creation now */
+       if (path[0] == '/')
+               return make_api_dbus_3(system, path, len);
+
+       /* compute the path prefixed with DEFAULT_PATH_PREFIX */
+       assert(strlen(DEFAULT_PATH_PREFIX) > 0);
+       assert(DEFAULT_PATH_PREFIX[strlen(DEFAULT_PATH_PREFIX) - 1] == '/');
+       len += strlen(DEFAULT_PATH_PREFIX);
+       ptr = alloca(len + 1);
+       strcpy(stpcpy(ptr, DEFAULT_PATH_PREFIX), path);
+
+       /* creation for prefixed path */
+       return make_api_dbus_3(system, ptr, len);
+}
+
+/*
+ * create a structure api_dbus connected either emiting/receiving
+ * on 'path'.
+ * The path can be prefixed with "system:" or "user:" to select
+ * either the user or the system D-Bus. If none is set then user's
+ * bus is selected.
+ * If remaining 'path' is not absolute, it is prefixed with
+ * DEFAULT_PATH_PREFIX.
+ */
+static struct api_dbus *make_api_dbus(const char *path)
+{
+       const char *ptr;
+       size_t preflen;
+
+       /* retrieves the prefix "scheme-like" part */
+       ptr = strchr(path, ':');
+       if (ptr == NULL)
+               return make_api_dbus_2(0, path);
+
+       /* check the prefix part */
+       preflen = (size_t)(ptr - path);
+       if (strncmp(path, "system", preflen) == 0)
+               return make_api_dbus_2(1, ptr + 1);
+
+       if (strncmp(path, "user", preflen) == 0)
+               return make_api_dbus_2(0, ptr + 1);
+
+       /* TODO: connect to a foreign D-Bus? */
+       errno = EINVAL;
+       return NULL;
+}
+
+static void destroy_api_dbus(struct api_dbus *api)
+{
+       free(api);
+}
+
+/******************* client part **********************************/
+
+/*
+ * structure for recording query data
+ */
+struct dbus_memo {
+       struct afb_req req;             /* the request handle */
+       struct afb_context *context;    /* the context of the query */
+};
+
+/* allocates and init the memorizing data */
+static struct dbus_memo *api_dbus_client_make_memo(struct afb_req req, struct afb_context *context)
+{
+       struct dbus_memo *memo;
+
+       memo = malloc(sizeof *memo);
+       if (memo != NULL) {
+               afb_req_addref(req);
+               memo->req = req;
+               memo->context = context;
+       }
+       return memo;
+}
+
+/* free and release the memorizing data */
+static void api_dbus_client_free_memo(struct dbus_memo *memo)
+{
+       afb_req_unref(memo->req);
+       free(memo);
+}
+
+/* callback when received answer */
+static int api_dbus_client_on_reply(sd_bus_message *message, void *userdata, sd_bus_error *ret_error)
+{
+       int rc;
+       struct dbus_memo *memo;
+       const char *first, *second;
+       uint8_t type;
+       uint32_t flags;
+
+       /* retrieve the recorded data */
+       memo = userdata;
+
+       /* get the answer */
+       rc = sd_bus_message_read(message, "yssu", &type, &first, &second, &flags);
+       if (rc < 0) {
+               /* failing to have the answer */
+               afb_req_fail(memo->req, "error", "dbus error");
+       } else {
+               /* report the answer */
+               memo->context->flags = (unsigned)flags;
+               switch(type) {
+               case RETOK:
+                       afb_req_success(memo->req, json_tokener_parse(first), second);
+                       break;
+               case RETERR:
+                       afb_req_fail(memo->req, first, second);
+                       break;
+               case RETRAW:
+                       afb_req_send(memo->req, first, strlen(first));
+                       break;
+               default:
+                       afb_req_fail(memo->req, "error", "dbus link broken");
+                       break;
+               }
+       }
+       api_dbus_client_free_memo(memo);
+       return 1;
+}
+
+/* on call, propagate it to the dbus service */
+static void api_dbus_client_call(struct api_dbus *api, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb)
+{
+       size_t size;
+       int rc;
+       char *method = strndupa(verb, lenverb);
+       struct dbus_memo *memo;
+
+       /* create the recording data */
+       memo = api_dbus_client_make_memo(req, context);
+       if (memo == NULL) {
+               afb_req_fail(req, "error", "out of memory");
+               return;
+       }
+
+       /* makes the call */
+       rc = sd_bus_call_method_async(api->sdbus, NULL,
+               api->name, api->path, api->name, method,
+               api_dbus_client_on_reply, memo,
+               "ssu",
+                       afb_req_raw(req, &size),
+                       ctxClientGetUuid(context->session),
+                       (uint32_t)context->flags);
+
+       /* if there was an error report it directly */
+       if (rc < 0) {
+               errno = -rc;
+               afb_req_fail(req, "error", "dbus error");
+               api_dbus_client_free_memo(memo);
+       }
+}
+
+/* adds a afb-dbus-service client api */
+int afb_api_dbus_add_client(const char *path)
+{
+       struct api_dbus *api;
+       struct afb_api afb_api;
+
+       /* create the dbus client api */
+       api = make_api_dbus(path);
+       if (api == NULL)
+               goto error;
+
+       /* record it as an API */
+       afb_api.closure = api;
+       afb_api.call = (void*)api_dbus_client_call;
+       if (afb_apis_add(api->api, afb_api) < 0)
+               goto error2;
+
+       return 0;
+
+error2:
+       destroy_api_dbus(api);
+error:
+       return -1;
+}
+
+/******************* dbus request part for server *****************/
+
+/*
+ * structure for a dbus request
+ */
+struct dbus_req {
+       struct afb_context context;     /* the context, should be THE FIRST */
+       sd_bus_message *message;        /* the incoming request message */
+       const char *request;            /* the readen request as string */
+       struct json_object *json;       /* the readen request as object */
+       int refcount;                   /* reference count of the request */
+};
+
+/* increment the reference count of the request */
+static void dbus_req_addref(struct dbus_req *dreq)
+{
+       dreq->refcount++;
+}
+
+/* decrement the reference count of the request and free/release it on falling to null */
+static void dbus_req_unref(struct dbus_req *dreq)
+{
+       if (dreq == NULL || --dreq->refcount)
+               return;
+
+       afb_context_disconnect(&dreq->context);
+       json_object_put(dreq->json);
+       sd_bus_message_unref(dreq->message);
+       free(dreq);
+}
+
+/* get the object of the request */
+static struct json_object *dbus_req_json(struct dbus_req *dreq)
+{
+       if (dreq->json == NULL) {
+               dreq->json = json_tokener_parse(dreq->request);
+               if (dreq->json == NULL) {
+                       /* lazy error detection of json request. Is it to improve? */
+                       dreq->json = json_object_new_string(dreq->request);
+               }
+       }
+       return dreq->json;
+}
+
+/* get the argument of the request of 'name' */
+static struct afb_arg dbus_req_get(struct dbus_req *dreq, const char *name)
+{
+       struct afb_arg arg;
+       struct json_object *value, *root;
+
+       root = dbus_req_json(dreq);
+       if (root != NULL && json_object_object_get_ex(root, name, &value)) {
+               arg.name = name;
+               arg.value = json_object_get_string(value);
+       } else {
+               arg.name = NULL;
+               arg.value = NULL;
+       }
+       arg.path = NULL;
+       return arg;
+}
+
+static void dbus_req_reply(struct dbus_req *dreq, uint8_t type, const char *first, const char *second)
+{
+       int rc;
+       rc = sd_bus_reply_method_return(dreq->message,
+                       "yssu", type, first, second, (uint32_t)dreq->context.flags);
+}
+
+static void dbus_req_success(struct dbus_req *dreq, struct json_object *obj, const char *info)
+{
+       dbus_req_reply(dreq, RETOK, json_object_to_json_string(obj), info);
+}
+
+static void dbus_req_fail(struct dbus_req *dreq, const char *status, const char *info)
+{
+       dbus_req_reply(dreq, RETERR, status, info);
+}
+
+static const char *dbus_req_raw(struct dbus_req *dreq, size_t *size)
+{
+       if (size != NULL)
+               *size = strlen(dreq->request);
+       return dreq->request;
+}
+
+static void dbus_req_send(struct dbus_req *dreq, const char *buffer, size_t size)
+{
+       /* TODO: how to put sized buffer as strings? things aren't clear here!!! */
+       dbus_req_reply(dreq, RETRAW, buffer, "");
+}
+
+struct afb_req_itf dbus_req_itf = {
+       .json = (void*)dbus_req_json,
+       .get = (void*)dbus_req_get,
+       .success = (void*)dbus_req_success,
+       .fail = (void*)dbus_req_fail,
+       .raw = (void*)dbus_req_raw,
+       .send = (void*)dbus_req_send,
+       .context_get = (void*)afb_context_get,
+       .context_set = (void*)afb_context_set,
+       .addref = (void*)dbus_req_addref,
+       .unref = (void*)dbus_req_unref
+};
+
+/******************* server part **********************************/
+
+/* called when the object for the service is called */
+static int api_dbus_server_on_object_called(sd_bus_message *message, void *userdata, sd_bus_error *ret_error)
+{
+       int rc;
+       const char *method;
+       const char *uuid;
+       struct dbus_req *dreq;
+       struct api_dbus *api = userdata;
+       struct afb_req areq;
+       uint32_t flags;
+
+       /* check the interface */
+       if (strcmp(sd_bus_message_get_interface(message), api->name) != 0)
+               return 0;
+
+       /* get the method */
+       method = sd_bus_message_get_member(message);
+
+       /* create the request */
+       dreq = calloc(1 , sizeof *dreq);
+       if (dreq == NULL) {
+               sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
+               return 1;
+       }
+
+       /* get the data */
+       rc = sd_bus_message_read(message, "ssu", &dreq->request, &uuid, &flags);
+       if (rc < 0) {
+               sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_SIGNATURE, "invalid signature");
+               free(dreq);
+               return 1;
+       }
+
+       /* connect to the context */
+       if (afb_context_connect(&dreq->context, uuid, NULL) < 0) {
+               sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
+               free(dreq);
+               return 1;
+       }
+
+       /* fulfill the request and emit it */
+       dreq->context.flags = flags;
+       dreq->message = sd_bus_message_ref(message);
+       dreq->json = NULL;
+       dreq->refcount = 1;
+       areq.itf = &dbus_req_itf;
+       areq.closure = dreq;
+       afb_apis_call_(areq, &dreq->context, api->api, method);
+       dbus_req_unref(dreq);
+       return 1;
+}
+
+/* create the service */
+int afb_api_dbus_add_server(const char *path)
+{
+       int rc;
+       struct api_dbus *api;
+       sd_bus_slot *slot;
+
+       /* get the dbus api object connected */
+       api = make_api_dbus(path);
+       if (api == NULL)
+               goto error;
+
+       /* request the service object name */
+       rc = sd_bus_request_name(api->sdbus, api->name, 0);
+       if (rc < 0) {
+               errno = -rc;
+               ERROR("can't register name %s", api->name);
+               goto error2;
+       }
+
+       /* connect the service to the dbus object */
+       rc = sd_bus_add_object(api->sdbus, &slot, api->path, api_dbus_server_on_object_called, api);
+       if (rc < 0) {
+               errno = -rc;
+               ERROR("can't add dbus object %s for %s", api->path, api->name);
+               goto error3;
+       }
+       INFO("afb service over dbus installed, name %s, path %s", api->name, api->path);
+
+       return 0;
+error3:
+       sd_bus_release_name(api->sdbus, api->name);
+error2:
+       destroy_api_dbus(api);
+error:
+       return -1;
+}
+
+
diff --git a/src/afb-api-dbus.h b/src/afb-api-dbus.h
new file mode 100644 (file)
index 0000000..8ea1909
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 IoT.bzh
+ * Author: José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#pragma once
+
+extern int afb_api_dbus_add_client(const char *path);
+
+extern int afb_api_dbus_add_server(const char *path);
+
+
index 89418fb..38621ec 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "session.h"
 #include "afb-common.h"
+#include "afb-context.h"
 #include "afb-apis.h"
 #include "afb-api-so.h"
 #include "verbose.h"
@@ -126,32 +127,36 @@ static void trapping_call(struct afb_req req, void(*cb)(struct afb_req))
        error_handler = older;
 }
 
-static void call_check(struct afb_req req, const struct AFB_restapi *verb)
+static void call_check(struct afb_req req, struct afb_context *context, const struct AFB_restapi *verb)
 {
-       switch(verb->session) {
-       case AFB_SESSION_CREATE:
-               if (!afb_req_session_create(req))
-                       return;
-               break;
-       case AFB_SESSION_RENEW:
-               if (!afb_req_session_check(req, 1))
+       int stag = (int)(verb->session & AFB_SESSION_MASK);
+
+       if (stag != AFB_SESSION_NONE) {
+               if (!afb_context_check(context)) {
+                       afb_context_close(context);
+                       afb_req_fail(req, "failed", "invalid token's identity");
                        return;
-               break;
-       case AFB_SESSION_CLOSE:
-       case AFB_SESSION_CHECK:
-               if (!afb_req_session_check(req, 0))
+               }       
+       }
+
+       if ((stag & AFB_SESSION_CREATE) != 0) {
+               if (!afb_context_create(context)) {
+                       afb_context_close(context);
+                       afb_req_fail(req, "failed", "invalid creation state");
                        return;
-               break;
-       case AFB_SESSION_NONE:
-       default:
-               break;
+               }
        }
+       
+       if ((stag & (AFB_SESSION_CREATE | AFB_SESSION_RENEW)) != 0)
+               afb_context_refresh(context);
+
+       if ((stag & AFB_SESSION_CLOSE) != 0)
+               afb_context_close(context);
+
        trapping_call(req, verb->callback);
-       if (verb->session == AFB_SESSION_CLOSE)
-               afb_req_session_close(req);
 }
 
-static void call(struct api_so_desc *desc, struct afb_req req, const char *verb, size_t lenverb)
+static void call(struct api_so_desc *desc, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb)
 {
        const struct AFB_restapi *v;
 
@@ -159,7 +164,7 @@ static void call(struct api_so_desc *desc, struct afb_req req, const char *verb,
        while (v->name && (strncasecmp(v->name, verb, lenverb) || v->name[lenverb]))
                v++;
        if (v->name)
-               call_check(req, v);
+               call_check(req, context, v);
        else
                afb_req_fail_f(req, "unknown-verb", "verb %.*s unknown within api %s", (int)lenverb, verb, desc->plugin->prefix);
 }
index bcb4242..da0b98f 100644 (file)
@@ -25,6 +25,7 @@
 #include "session.h"
 #include "verbose.h"
 #include "afb-apis.h"
+#include "afb-context.h"
 #include "afb-req-itf.h"
 
 struct api_desc {
@@ -83,12 +84,12 @@ error:
        return -1;
 }
 
-void afb_apis_call_(struct afb_req req, struct AFB_clientCtx *context, const char *api, const char *verb)
+void afb_apis_call_(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
 {
        afb_apis_call(req, context, api, strlen(api), verb, strlen(verb));
 }
 
-void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb)
+void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb)
 {
        int i;
        const struct api_desc *a;
@@ -96,8 +97,8 @@ void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char
        a = apis_array;
        for (i = 0 ; i < apis_count ; i++, a++) {
                if (a->namelen == lenapi && !strncasecmp(a->name, api, lenapi)) {
-                       req.ctx_closure = &context->contexts[i];
-                       a->api.call(a->api.closure, req, verb, lenverb);
+                       context->api_index = i;
+                       a->api.call(a->api.closure, req, context, verb, lenverb);
                        return;
                }
        }
index 795054a..3e75130 100644 (file)
 #pragma once
 
 struct afb_req;
-struct AFB_clientCtx;
+struct afb_context;
 
 struct afb_api
 {
        void *closure;
-       void (*call)(void *closure, struct afb_req req, const char *verb, size_t lenverb);
+       void (*call)(void *closure, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb);
 };
 
 
 extern int afb_apis_count();
 extern int afb_apis_add(const char *name, struct afb_api api);
-extern void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb);
-extern void afb_apis_call_(struct afb_req req, struct AFB_clientCtx *context, const char *api, const char *verb);
+extern void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb);
+extern void afb_apis_call_(struct afb_req req, struct afb_context *context, const char *api, const char *verb);
 
 
diff --git a/src/afb-context.c b/src/afb-context.c
new file mode 100644 (file)
index 0000000..2f391de
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 "IoT.bzh"
+ * Author "Fulup Ar Foll"
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "session.h"
+#include "afb-context.h"
+
+
+void afb_context_init(struct afb_context *context, struct AFB_clientCtx *session, const char *token)
+{
+       assert(session != NULL);
+
+       /* reset the context for the session */
+       context->session = session;
+       context->flags = 0;
+       context->api_index = -1;
+
+       /* check the token */
+       if (token != NULL) {
+               if (ctxTokenCheck(session, token))
+                       context->validated = 1;
+               else
+                       context->invalidated = 1;
+       }
+}
+
+int afb_context_connect(struct afb_context *context, const char *uuid, const char *token)
+{
+       int created;
+       struct AFB_clientCtx *session;
+
+       session = ctxClientGetSession (uuid, &created);
+       if (session == NULL)
+               return -1;
+       afb_context_init(context, session, token);
+       if (created)
+               context->created = 1;
+       return 0;
+}
+
+void afb_context_disconnect(struct afb_context *context)
+{
+       if (context->session != NULL) {
+               if (context->closing && !context->closed) {
+                       context->closed = 1;
+                       ctxClientClose(context->session);
+               }
+               ctxClientUnref(context->session);
+       }
+}
+
+const char *afb_context_sent_token(struct afb_context *context)
+{
+       if (context->session == NULL || context->closing)
+               return NULL;
+       if (!(context->created || context->refreshing))
+               return NULL;
+       if (!context->refreshed) {
+               ctxTokenNew (context->session);
+               context->refreshed = 1;
+       }
+       return ctxClientGetToken(context->session);
+}
+
+const char *afb_context_sent_uuid(struct afb_context *context)
+{
+       if (context->session == NULL || context->closing)
+               return NULL;
+       if (!context->created)
+               return NULL;
+       return ctxClientGetUuid(context->session);
+}
+
+void *afb_context_get(struct afb_context *context)
+{
+       assert(context->session != NULL);
+       return ctxClientValueGet(context->session, context->api_index);
+}
+
+void afb_context_set(struct afb_context *context, void *value, void (*free_value)(void*))
+{
+       assert(context->session != NULL);
+       return ctxClientValueSet(context->session, context->api_index, value, free_value);
+}
+
+void afb_context_close(struct afb_context *context)
+{
+       context->closing = 1;
+}
+
+void afb_context_refresh(struct afb_context *context)
+{
+       context->refreshing = 1;
+}
+
+int afb_context_check(struct afb_context *context)
+{
+       return context->validated;
+}
+
+int afb_context_create(struct afb_context *context)
+{
+       return context->created;
+}
diff --git a/src/afb-context.h b/src/afb-context.h
new file mode 100644 (file)
index 0000000..eeb4def
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2016 IoT.bzh
+ * Author: José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+struct AFB_clientCtx;
+
+struct afb_context
+{
+       struct AFB_clientCtx *session;
+       union {
+               unsigned flags;
+               struct {
+                       unsigned created: 1;
+                       unsigned validated: 1;
+                       unsigned invalidated: 1;
+                       unsigned refreshing: 1;
+                       unsigned refreshed: 1;
+                       unsigned closing: 1;
+                       unsigned closed: 1;
+               };
+       };
+       int api_index;
+};
+
+extern void afb_context_init(struct afb_context *context, struct AFB_clientCtx *session, const char *token);
+extern int afb_context_connect(struct afb_context *context, const char *uuid, const char *token);
+extern void afb_context_disconnect(struct afb_context *context);
+extern const char *afb_context_sent_token(struct afb_context *context);
+extern const char *afb_context_sent_uuid(struct afb_context *context);
+
+extern void *afb_context_get(struct afb_context *context);
+extern void afb_context_set(struct afb_context *context, void *value, void (*free_value)(void*));
+
+extern void afb_context_close(struct afb_context *context);
+extern void afb_context_refresh(struct afb_context *context);
+extern int afb_context_check(struct afb_context *context);
+extern int afb_context_create(struct afb_context *context);
+
index d0adda6..518bc5c 100644 (file)
@@ -35,6 +35,7 @@
 #include "afb-method.h"
 #include "afb-req-itf.h"
 #include "afb-msg-json.h"
+#include "afb-context.h"
 #include "afb-hreq.h"
 #include "session.h"
 #include "verbose.h"
@@ -55,12 +56,15 @@ static char *cookie_name = NULL;
 static char *cookie_setter = NULL;
 static char *tmp_pattern = NULL;
 
+/*
+ * Structure for storing key/values read from POST requests
+ */
 struct hreq_data {
-       struct hreq_data *next;
-       char *key;
-       size_t length;
-       char *value;
-       char *path;
+       struct hreq_data *next; /* chain to next data */
+       char *key;              /* key name */
+       size_t length;          /* length of the value (used for appending) */
+       char *value;            /* the value (or original filename) */
+       char *path;             /* path of the file saved */
 };
 
 static struct json_object *req_json(struct afb_hreq *hreq);
@@ -69,9 +73,6 @@ static void req_fail(struct afb_hreq *hreq, const char *status, const char *info
 static void req_success(struct afb_hreq *hreq, json_object *obj, const char *info);
 static const char *req_raw(struct afb_hreq *hreq, size_t *size);
 static void req_send(struct afb_hreq *hreq, const char *buffer, size_t size);
-static int req_session_create(struct afb_hreq *hreq);
-static int req_session_check(struct afb_hreq *hreq, int refresh);
-static void req_session_close(struct afb_hreq *hreq);
 
 static const struct afb_req_itf afb_hreq_itf = {
        .json = (void*)req_json,
@@ -80,11 +81,10 @@ static const struct afb_req_itf afb_hreq_itf = {
        .fail = (void*)req_fail,
        .raw = (void*)req_raw,
        .send = (void*)req_send,
-       .session_create = (void*)req_session_create,
-       .session_check = (void*)req_session_check,
-       .session_close = (void*)req_session_close,
        .context_get = (void*)afb_context_get,
-       .context_set = (void*)afb_context_set
+       .context_set = (void*)afb_context_set,
+       .addref = (void*)afb_hreq_addref,
+       .unref = (void*)afb_hreq_unref
 };
 
 static struct hreq_data *get_data(struct afb_hreq *hreq, const char *key, int create)
@@ -162,7 +162,8 @@ static void afb_hreq_reply_v(struct afb_hreq *hreq, unsigned status, struct MHD_
                MHD_add_response_header(response, k, v);
                k = va_arg(args, const char *);
        }
-       if (hreq->context != NULL && asprintf(&cookie, cookie_setter, hreq->context->uuid)) {
+       v = afb_context_sent_uuid(&hreq->context);
+       if (v != NULL && asprintf(&cookie, cookie_setter, v) > 0) {
                MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cookie);
                free(cookie);
        }
@@ -291,26 +292,33 @@ static const char *mimetype_fd_name(int fd, const char *filename)
        return result;
 }
 
-void afb_hreq_free(struct afb_hreq *hreq)
+void afb_hreq_addref(struct afb_hreq *hreq)
+{
+       hreq->refcount++;
+}
+
+void afb_hreq_unref(struct afb_hreq *hreq)
 {
        struct hreq_data *data;
-       if (hreq != NULL) {
-               if (hreq->postform != NULL)
-                       MHD_destroy_post_processor(hreq->postform);
-               for (data = hreq->data; data; data = hreq->data) {
-                       hreq->data = data->next;
-                       if (data->path) {
-                               unlink(data->path);
-                               free(data->path);
-                       }
-                       free(data->key);
-                       free(data->value);
-                       free(data);
+
+       if (hreq == NULL || --hreq->refcount)
+               return;
+
+       if (hreq->postform != NULL)
+               MHD_destroy_post_processor(hreq->postform);
+       for (data = hreq->data; data; data = hreq->data) {
+               hreq->data = data->next;
+               if (data->path) {
+                       unlink(data->path);
+                       free(data->path);
                }
-               ctxClientPut(hreq->context);
-               json_object_put(hreq->json);
-               free(hreq);
+               free(data->key);
+               free(data->value);
+               free(data);
        }
+       afb_context_disconnect(&hreq->context);
+       json_object_put(hreq->json);
+       free(hreq);
 }
 
 /*
@@ -599,7 +607,7 @@ int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *key, const char *f
 
 struct afb_req afb_hreq_to_req(struct afb_hreq *hreq)
 {
-       return (struct afb_req){ .itf = &afb_hreq_itf, .req_closure = hreq };
+       return (struct afb_req){ .itf = &afb_hreq_itf, .closure = hreq };
 }
 
 static struct afb_arg req_get(struct afb_hreq *hreq, const char *name)
@@ -678,12 +686,9 @@ static void req_reply(struct afb_hreq *hreq, unsigned retcode, const char *statu
        const char *token, *uuid;
        struct MHD_Response *response;
 
-       if (hreq->context == NULL) {
-               token = uuid = NULL;
-       } else {
-               token = hreq->context->token;
-               uuid = hreq->context->uuid;
-       }
+       token = afb_context_sent_token(&hreq->context);
+       uuid = afb_context_sent_uuid(&hreq->context);
+
        reply = afb_msg_json_reply(status, info, resp, token, uuid);
        response = MHD_create_response_from_callback((uint64_t)strlen(json_object_to_json_string(reply)), SIZE_RESPONSE_BUFFER, (void*)send_json_cb, reply, (void*)json_object_put);
        afb_hreq_reply(hreq, retcode, response, NULL);
@@ -699,63 +704,27 @@ static void req_success(struct afb_hreq *hreq, json_object *obj, const char *inf
        req_reply(hreq, MHD_HTTP_OK, "success", info, obj);
 }
 
-struct AFB_clientCtx *afb_hreq_context(struct afb_hreq *hreq)
+int afb_hreq_init_context(struct afb_hreq *hreq)
 {
        const char *uuid;
-
-       if (hreq->context == NULL) {
-               uuid = afb_hreq_get_header(hreq, uuid_header);
-               if (uuid == NULL)
-                       uuid = afb_hreq_get_argument(hreq, uuid_arg);
-               if (uuid == NULL)
-                       uuid = afb_hreq_get_cookie(hreq, cookie_name);
-               hreq->context = ctxClientGetForUuid(uuid);
-       }
-       return hreq->context;
-}
-
-static int req_session_create(struct afb_hreq *hreq)
-{
-       struct AFB_clientCtx *context = afb_hreq_context(hreq);
-       if (context == NULL)
-               return 0;
-       if (context->created)
-               return 0;
-       return req_session_check(hreq, 1);
-}
-
-static int req_session_check(struct afb_hreq *hreq, int refresh)
-{
        const char *token;
 
-       struct AFB_clientCtx *context = afb_hreq_context(hreq);
-
-       if (context == NULL)
+       if (hreq->context.session != NULL)
                return 0;
 
+       uuid = afb_hreq_get_header(hreq, uuid_header);
+       if (uuid == NULL)
+               uuid = afb_hreq_get_argument(hreq, uuid_arg);
+       if (uuid == NULL)
+               uuid = afb_hreq_get_cookie(hreq, cookie_name);
+
        token = afb_hreq_get_header(hreq, token_header);
        if (token == NULL)
                token = afb_hreq_get_argument(hreq, token_arg);
        if (token == NULL)
                token = afb_hreq_get_cookie(hreq, token_cookie);
-       if (token == NULL)
-               return 0;
 
-       if (!ctxTokenCheck (context, token))
-               return 0;
-
-       if (refresh) {
-               ctxTokenNew (context);
-       }
-
-       return 1;
-}
-
-static void req_session_close(struct afb_hreq *hreq)
-{
-       struct AFB_clientCtx *context = afb_hreq_context(hreq);
-       if (context != NULL)
-               ctxClientClose(context);
+       return afb_context_connect(&hreq->context, uuid, token);
 }
 
 int afb_hreq_init_cookie(int port, const char *path, int maxage)
index 82e6b65..3d19778 100644 (file)
@@ -23,6 +23,8 @@ struct hreq_data;
 struct afb_hsrv;
 
 struct afb_hreq {
+       struct afb_context context;
+       int refcount;
        struct afb_hsrv *hsrv;
        const char *cacheTimeout;
        struct MHD_Connection *connection;
@@ -37,14 +39,11 @@ struct afb_hreq {
        const char *tail;
        size_t lentail;
        struct MHD_PostProcessor *postform;
-       struct AFB_clientCtx *context;
        struct hreq_data *data;
        struct json_object *json;
        int upgrade;
 };
 
-extern void afb_hreq_free(struct afb_hreq *request);
-
 extern int afb_hreq_unprefix(struct afb_hreq *request, const char *prefix, size_t length);
 
 extern int afb_hreq_valid_tail(struct afb_hreq *request);
@@ -69,7 +68,7 @@ extern int afb_hreq_post_add(struct afb_hreq *hreq, const char *name, const char
 
 extern struct afb_req afb_hreq_to_req(struct afb_hreq *hreq);
 
-extern struct AFB_clientCtx *afb_hreq_context(struct afb_hreq *hreq);
+extern int afb_hreq_init_context(struct afb_hreq *hreq);
 
 extern int afb_hreq_init_cookie(int port, const char *path, int maxage);
 
@@ -82,3 +81,8 @@ extern void afb_hreq_reply_free(struct afb_hreq *hreq, unsigned status, size_t s
 extern void afb_hreq_reply_empty(struct afb_hreq *hreq, unsigned status, ...);
 
 extern int afb_hreq_init_download_path(const char *directory);
+
+extern void afb_hreq_addref(struct afb_hreq *hreq);
+
+extern void afb_hreq_unref(struct afb_hreq *hreq);
+
index f514c97..e64c5c9 100644 (file)
@@ -30,6 +30,7 @@
 #include <systemd/sd-event.h>
 
 #include "afb-method.h"
+#include "afb-context.h"
 #include "afb-hreq.h"
 #include "afb-hsrv.h"
 #include "afb-req-itf.h"
@@ -130,6 +131,7 @@ static int access_handler(
                }
 
                /* init the request */
+               hreq->refcount = 1;
                hreq->hsrv = hsrv;
                hreq->cacheTimeout = hsrv->cache_to;
                hreq->reqid = ++global_reqids;
@@ -228,7 +230,7 @@ static void end_handler(void *cls, struct MHD_Connection *connection, void **rec
        hreq = *recordreq;
        if (hreq->upgrade)
                MHD_suspend_connection (connection);
-       afb_hreq_free(hreq);
+       afb_hreq_unref(hreq);
 }
 
 void run_micro_httpd(struct afb_hsrv *hsrv)
index 606d266..f030845 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#include <microhttpd.h>
+
 #include "afb-req-itf.h"
+#include "afb-context.h"
 #include "afb-hreq.h"
 #include "afb-apis.h"
 #include "session.h"
@@ -31,7 +34,6 @@ int afb_hswitch_apis(struct afb_hreq *hreq, void *data)
 {
        const char *api, *verb;
        size_t lenapi, lenverb;
-       struct AFB_clientCtx *context;
 
        api = &hreq->tail[strspn(hreq->tail, "/")];
        lenapi = strcspn(api, "/");
@@ -42,8 +44,10 @@ int afb_hswitch_apis(struct afb_hreq *hreq, void *data)
        if (!(*api && *verb && lenapi && lenverb))
                return 0;
 
-       context = afb_hreq_context(hreq);
-       afb_apis_call(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb);
+       if (afb_hreq_init_context(hreq) < 0)
+               afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR);
+       else
+               afb_apis_call(afb_hreq_to_req(hreq), &hreq->context, api, lenapi, verb, lenverb);
        return 1;
 }
 
@@ -77,7 +81,12 @@ int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data)
        if (hreq->lentail != 0)
                return 0;
 
-       return afb_websock_check_upgrade(hreq /* TODO: protocols here */);
+       if (afb_hreq_init_context(hreq) < 0) {
+               afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR);
+               return 1;
+       }
+
+       return afb_websock_check_upgrade(hreq);
 }
 
 
index 9f5619f..0247aae 100644 (file)
@@ -28,6 +28,7 @@
 #include "afb-ws-json.h"
 
 #include "afb-method.h"
+#include "afb-context.h"
 #include "afb-hreq.h"
 #include "afb-websock.h"
 
@@ -203,7 +204,7 @@ int afb_websock_check_upgrade(struct afb_hreq *hreq)
                return 0;
 
        ws = NULL;
-       rc = check_websocket_upgrade(hreq->connection, protodefs, afb_hreq_context(hreq), &ws);
+       rc = check_websocket_upgrade(hreq->connection, protodefs, hreq->context.session, &ws);
        if (rc == 1) {
                hreq->replied = 1;
                if (ws != NULL)
index 4bdda7e..781722f 100644 (file)
@@ -31,6 +31,7 @@
 #include "session.h"
 #include "afb-req-itf.h"
 #include "afb-apis.h"
+#include "afb-context.h"
 
 static void aws_on_hangup(struct afb_ws_json *ws);
 static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size);
@@ -47,7 +48,7 @@ struct afb_ws_json
        void (*cleanup)(void*);
        void *cleanup_closure;
        struct afb_wsreq *requests;
-       struct AFB_clientCtx *context;
+       struct AFB_clientCtx *session;
        struct json_tokener *tokener;
        struct afb_ws *ws;
 };
@@ -58,12 +59,12 @@ static const struct afb_event_listener_itf event_listener_itf = {
        .send = (void*)aws_send_event
 };
 
-struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *cleanup_closure)
+struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *session, void (*cleanup)(void*), void *cleanup_closure)
 {
        struct afb_ws_json *result;
 
        assert(fd >= 0);
-       assert(context != NULL);
+       assert(session != NULL);
 
        result = malloc(sizeof * result);
        if (result == NULL)
@@ -72,8 +73,8 @@ struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, vo
        result->cleanup = cleanup;
        result->cleanup_closure = cleanup_closure;
        result->requests = NULL;
-       result->context = ctxClientGet(context);
-       if (result->context == NULL)
+       result->session = ctxClientAddRef(session);
+       if (result->session == NULL)
                goto error2;
 
        result->tokener = json_tokener_new();
@@ -84,7 +85,7 @@ struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, vo
        if (result->ws == NULL)
                goto error4;
 
-       if (0 > ctxClientEventListenerAdd(result->context, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = result }))
+       if (0 > ctxClientEventListenerAdd(result->session, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = result }))
                goto error5;
 
        return result;
@@ -94,7 +95,7 @@ error5:
 error4:
        json_tokener_free(result->tokener);
 error3:
-       ctxClientPut(result->context);
+       ctxClientUnref(result->session);
 error2:
        free(result);
 error:
@@ -104,11 +105,12 @@ error:
 
 static void aws_on_hangup(struct afb_ws_json *ws)
 {
-       ctxClientEventListenerRemove(ws->context, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = ws });
+       ctxClientEventListenerRemove(ws->session, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = ws });
        afb_ws_destroy(ws->ws);
        json_tokener_free(ws->tokener);
        if (ws->cleanup != NULL)
                ws->cleanup(ws->cleanup_closure);
+       ctxClientUnref(ws->session);
        free(ws);
 }
 
@@ -119,6 +121,8 @@ static void aws_on_hangup(struct afb_ws_json *ws)
 
 struct afb_wsreq
 {
+       struct afb_context context;
+       int refcount;
        struct afb_ws_json *aws;
        struct afb_wsreq *next;
        char *text;
@@ -137,15 +141,14 @@ struct afb_wsreq
        struct json_object *root;
 };
 
+static void wsreq_addref(struct afb_wsreq *wsreq);
+static void wsreq_unref(struct afb_wsreq *wsreq);
 static struct json_object *wsreq_json(struct afb_wsreq *wsreq);
 static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name);
 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info);
 static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info);
 static const char *wsreq_raw(struct afb_wsreq *wsreq, size_t *size);
 static void wsreq_send(struct afb_wsreq *wsreq, const char *buffer, size_t size);
-static int wsreq_session_create(struct afb_wsreq *wsreq);
-static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh);
-static void wsreq_session_close(struct afb_wsreq *wsreq);
 
 
 static const struct afb_req_itf wsreq_itf = {
@@ -155,11 +158,10 @@ static const struct afb_req_itf wsreq_itf = {
        .fail = (void*)wsreq_fail,
        .raw = (void*)wsreq_raw,
        .send = (void*)wsreq_send,
-       .session_create = (void*)wsreq_session_create,
-       .session_check = (void*)wsreq_session_check,
-       .session_close = (void*)wsreq_session_close,
        .context_get = (void*)afb_context_get,
-       .context_set = (void*)afb_context_set
+       .context_set = (void*)afb_context_set,
+       .addref = (void*)wsreq_addref,
+       .unref = (void*)wsreq_unref
 };
 
 static int aws_wsreq_parse(struct afb_wsreq *r, char *text, size_t size)
@@ -303,13 +305,20 @@ static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size)
                goto bad_header;
 
        /* fill and record the request */
+       if (wsreq->tok != NULL)
+               wsreq->tok[wsreq->toklen] = 0;
+       afb_context_init(&wsreq->context, ws->session, wsreq->tok);
+       if (!wsreq->context.invalidated)
+               wsreq->context.validated = 1;
+       wsreq->refcount = 1;
        wsreq->aws = ws;
        wsreq->next = ws->requests;
        ws->requests = wsreq;
 
-       r.req_closure = wsreq;
+       r.closure = wsreq;
        r.itf = &wsreq_itf;
-       afb_apis_call(r, ws->context, wsreq->api, wsreq->apilen, wsreq->verb, wsreq->verblen);
+       afb_apis_call(r, &wsreq->context, wsreq->api, wsreq->apilen, wsreq->verb, wsreq->verblen);
+       wsreq_unref(wsreq);
        return;
 
 bad_header:
@@ -320,6 +329,20 @@ alloc_error:
        return;
 }
 
+static void wsreq_addref(struct afb_wsreq *wsreq)
+{
+       wsreq->refcount++;
+}
+
+static void wsreq_unref(struct afb_wsreq *wsreq)
+{
+       if (--wsreq->refcount == 0) {
+               afb_context_disconnect(&wsreq->context);
+               free(wsreq->text);
+               free(wsreq);
+       }
+}
+
 static struct json_object *wsreq_json(struct afb_wsreq *wsreq)
 {
        struct json_object *root = wsreq->root;
@@ -352,41 +375,9 @@ static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name)
        return arg;
 }
 
-static int wsreq_session_create(struct afb_wsreq *wsreq)
-{
-       struct AFB_clientCtx *context = wsreq->aws->context;
-       if (context->created)
-               return 0;
-       return wsreq_session_check(wsreq, 1);
-}
-
-static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh)
-{
-       struct AFB_clientCtx *context = wsreq->aws->context;
-
-       if (wsreq->tok == NULL)
-               return 0;
-
-       if (!ctxTokenCheckLen (context, wsreq->tok, wsreq->toklen))
-               return 0;
-
-       if (refresh) {
-               ctxTokenNew (context);
-       }
-
-       return 1;
-}
-
-static void wsreq_session_close(struct afb_wsreq *wsreq)
-{
-       struct AFB_clientCtx *context = wsreq->aws->context;
-       ctxClientClose(context);
-}
-
-static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t idlen, struct json_object *data)
+static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t idlen, struct json_object *data, const char *token)
 {
        json_object *msg;
-       const char *token;
        const char *txt;
 
        /* pack the message */
@@ -394,7 +385,6 @@ static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t i
        json_object_array_add(msg, json_object_new_int(code));
        json_object_array_add(msg, json_object_new_string_len(id, (int)idlen));
        json_object_array_add(msg, data);
-       token = aws->context->token;
        if (token)
                json_object_array_add(msg, json_object_new_string(token));
 
@@ -406,8 +396,8 @@ static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t i
 
 static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp)
 {
-       aws_emit(wsreq->aws, retcode, wsreq->id, wsreq->idlen, afb_msg_json_reply(status, info, resp, NULL, NULL));
-       /* TODO eliminates the wsreq */
+       const char *token = afb_context_sent_token(&wsreq->context);
+       aws_emit(wsreq->aws, retcode, wsreq->id, wsreq->idlen, afb_msg_json_reply(status, info, resp, token, NULL), token);
 }
 
 static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info)
@@ -433,6 +423,6 @@ static void wsreq_send(struct afb_wsreq *wsreq, const char *buffer, size_t size)
 
 static void aws_send_event(struct afb_ws_json *aws, const char *event, struct json_object *object)
 {
-       aws_emit(aws, EVENT, event, strlen(event), afb_msg_json_event(event, object));
+       aws_emit(aws, EVENT, event, strlen(event), afb_msg_json_event(event, object), NULL);
 }
 
index fcedd2a..be1927e 100644 (file)
@@ -39,6 +39,7 @@
 #include "afb-apis.h"
 #include "afb-api-so.h"
 #include "afb-hsrv.h"
+#include "afb-context.h"
 #include "afb-hreq.h"
 #include "session.h"
 #include "verbose.h"
index ae44f79..d586e51 100644 (file)
 
 #define NOW (time(NULL))
 
+struct client_value
+{
+       void *value;
+       void (*free_value)(void*);
+};
+
+struct afb_event_listener_list
+{
+       struct afb_event_listener_list *next;
+       struct afb_event_listener listener;
+       int refcount;
+};
+
+struct AFB_clientCtx
+{
+       unsigned refcount;
+       time_t expiration;    // expiration time of the token
+       time_t access;
+       char uuid[37];        // long term authentication of remote client
+       char token[37];       // short term authentication of remote client
+       struct client_value *values;
+       struct afb_event_listener_list *listeners;
+};
+
 // Session UUID are store in a simple array [for 10 sessions this should be enough]
 static struct {
   pthread_mutex_t mutex;          // declare a mutex to protect hash table
@@ -42,31 +66,17 @@ static struct {
   const char *initok;
 } sessions;
 
-void *afb_context_get(struct afb_context *actx)
-{
-       return actx->context;
-}
-
-void afb_context_set(struct afb_context *actx, void *context, void (*free_context)(void*))
-{
-fprintf(stderr, "afb_context_set(%p,%p) was (%p,%p)\n",context, free_context, actx->context, actx->free_context);
-       if (actx->context != NULL && actx->free_context != NULL)
-               actx->free_context(actx->context);
-       actx->context = context;
-       actx->free_context = free_context;
-}
-
 // Free context [XXXX Should be protected again memory abort XXXX]
 static void ctxUuidFreeCB (struct AFB_clientCtx *client)
 {
        int idx;
 
        // If application add a handle let's free it now
-       assert (client->contexts != NULL);
+       assert (client->values != NULL);
 
        // Free client handle with a standard Free function, with app callback or ignore it
        for (idx=0; idx < sessions.apicount; idx ++)
-               afb_context_set(&client->contexts[idx], NULL, NULL);
+               ctxClientValueSet(client, idx, NULL, NULL);
 }
 
 // Create a new store in RAM, not that is too small it will be automatically extended
@@ -77,7 +87,7 @@ void ctxStoreInit (int max_session_count, int timeout, const char *initok, int c
        sessions.max = max_session_count;
        sessions.timeout = timeout;
        sessions.apicount = context_count;
-       if (strlen(initok) >= 37) {
+       if (strlen(initok) >= sizeof(sessions.store[0]->token)) {
                fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok);
                exit(1);
        }
@@ -175,67 +185,80 @@ static void ctxStoreCleanUp (time_t now)
 }
 
 // This function will return exiting client context or newly created client context
-struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid)
+struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created)
 {
        uuid_t newuuid;
        struct AFB_clientCtx *clientCtx;
        time_t now;
 
-       /* search for an existing one not too old */
+       /* cleaning */
        now = NOW;
        ctxStoreCleanUp (now);
-       clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL;
-       if (clientCtx) {
-               clientCtx->refcount++;
-               return clientCtx;
-        }
 
-       /* mimic old behaviour */
-/*
-TODO remove? not remove?
-       if (sessions.initok == NULL)
-               return NULL;
-*/
-       /* check the uuid if given */
-       if (uuid != NULL && strlen(uuid) >= sizeof clientCtx->uuid)
-               return NULL;
+       /* search for an existing one not too old */
+       if (uuid != NULL) {
+               if (strlen(uuid) >= sizeof clientCtx->uuid) {
+                       errno = EINVAL;
+                       goto error;
+               }
+               clientCtx = ctxStoreSearch(uuid);
+               if (clientCtx != NULL) {
+                       *created = 0;
+                       goto found;
+               }
+       }
 
        /* returns a new one */
-        clientCtx = calloc(1, sizeof(struct AFB_clientCtx)); // init NULL clientContext
-       if (clientCtx != NULL) {
-               clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof(*clientCtx->contexts));
-               if (clientCtx->contexts != NULL) {
-                       /* generate the uuid */
-                       if (uuid == NULL) {
-                               uuid_generate(newuuid);
-                               uuid_unparse_lower(newuuid, clientCtx->uuid);
-                       } else {
-                               strcpy(clientCtx->uuid, uuid);
-                       }
-                       strcpy(clientCtx->token, sessions.initok);
-                       clientCtx->expiration = now + sessions.timeout;
-                       clientCtx->refcount = 1;
-                       if (ctxStoreAdd (clientCtx))
-                               return clientCtx;
-                       free(clientCtx->contexts);
-               }
-               free(clientCtx);
+        clientCtx = calloc(1, sizeof(struct AFB_clientCtx) + ((unsigned)sessions.apicount * sizeof(*clientCtx->values)));
+       if (clientCtx == NULL) {
+               errno = ENOMEM;
+               goto error;
+       }
+        clientCtx->values = (void*)(clientCtx + 1);
+
+       /* generate the uuid */
+       if (uuid == NULL) {
+               uuid_generate(newuuid);
+               uuid_unparse_lower(newuuid, clientCtx->uuid);
+       } else {
+               strcpy(clientCtx->uuid, uuid);
+       }
+
+       /* init the token */
+       strcpy(clientCtx->token, sessions.initok);
+       clientCtx->expiration = now + sessions.timeout;
+       if (!ctxStoreAdd (clientCtx)) {
+               errno = ENOMEM;
+               goto error2;
        }
+       *created = 1;
+
+found:
+       clientCtx->access = now;
+       clientCtx->refcount++;
+       return clientCtx;
+
+error2:
+       free(clientCtx);
+error:
        return NULL;
 }
 
-struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx)
+struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx)
 {
        if (clientCtx != NULL)
                clientCtx->refcount++;
        return clientCtx;
 }
 
-void ctxClientPut(struct AFB_clientCtx *clientCtx)
+void ctxClientUnref(struct AFB_clientCtx *clientCtx)
 {
        if (clientCtx != NULL) {
                assert(clientCtx->refcount != 0);
                --clientCtx->refcount;
+                       if (clientCtx->refcount == 0 && clientCtx->uuid[0] == 0) {
+                       ctxStoreDel (clientCtx);
+               }
        }
 }
 
@@ -243,16 +266,12 @@ void ctxClientPut(struct AFB_clientCtx *clientCtx)
 void ctxClientClose (struct AFB_clientCtx *clientCtx)
 {
        assert(clientCtx != NULL);
-       if (clientCtx->created) {
-               clientCtx->created = 0;
-               ctxUuidFreeCB (clientCtx);
-       }
-               if (clientCtx->refcount == 0)
-               ctxStoreDel (clientCtx);
+        ctxUuidFreeCB (clientCtx);
+       clientCtx->uuid[0] = 0;
 }
 
 // Sample Generic Ping Debug API
-int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length)
+int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
 {
        assert(clientCtx != NULL);
        assert(token != NULL);
@@ -261,22 +280,12 @@ int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t
        if (ctxStoreTooOld (clientCtx, NOW))
                return 0;
 
-       if (clientCtx->token[0] && (length >= sizeof(clientCtx->token) || strncmp (token, clientCtx->token, length) || clientCtx->token[length]))
+       if (clientCtx->token[0] && strcmp (token, clientCtx->token) != 0)
                return 0;
 
-       clientCtx->created = 1; /* creates by default */
        return 1;
 }
 
-// Sample Generic Ping Debug API
-int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token)
-{
-       assert(clientCtx != NULL);
-       assert(token != NULL);
-
-       return ctxTokenCheckLen(clientCtx, token, strlen(token));
-}
-
 // generate a new token and update client context
 void ctxTokenNew (struct AFB_clientCtx *clientCtx)
 {
@@ -292,13 +301,6 @@ void ctxTokenNew (struct AFB_clientCtx *clientCtx)
        clientCtx->expiration = NOW + sessions.timeout;
 }
 
-struct afb_event_listener_list
-{
-       struct afb_event_listener_list *next;
-       struct afb_event_listener listener;
-       int refcount;
-};
-
 int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener)
 {
        struct afb_event_listener_list *iter, **prv;
@@ -375,12 +377,43 @@ int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struc
                for (idx=0; idx < sessions.max; idx++) {
                        clientCtx = sessions.store[idx];
                        if (clientCtx != NULL && !ctxStoreTooOld(clientCtx, now)) {
-                               clientCtx = ctxClientGet(clientCtx);
+                               clientCtx = ctxClientAddRef(clientCtx);
                                result += send(clientCtx, event, object);
-                               ctxClientPut(clientCtx);
+                               ctxClientUnref(clientCtx);
                        }
                }
        }
        return result;
 }
 
+const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx)
+{
+       assert(clientCtx != NULL);
+       return clientCtx->uuid;
+}
+
+const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx)
+{
+       assert(clientCtx != NULL);
+       return clientCtx->token;
+}
+
+void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index)
+{
+       assert(clientCtx != NULL);
+       assert(index >= 0);
+       assert(index < sessions.apicount);
+       return clientCtx->values[index].value;
+}
+
+void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*))
+{
+       struct client_value prev;
+       assert(clientCtx != NULL);
+       assert(index >= 0);
+       assert(index < sessions.apicount);
+       prev = clientCtx->values[index];
+       clientCtx->values[index] = (struct client_value){.value = value, .free_value = free_value};
+       if (prev.value !=  NULL && prev.free_value != NULL)
+               prev.free_value(prev.value);
+}
index f6d67fe..c272245 100644 (file)
 #pragma once
 
 struct json_object;
-
-struct afb_context
-{
-       void *context;
-       void (*free_context)(void*);
-};
-
-extern void *afb_context_get(struct afb_context *actx);
-extern void afb_context_set(struct afb_context *actx, void *context, void (*free_context)(void*));
+struct AFB_clientCtx;
 
 struct afb_event_listener_itf
 {
@@ -39,24 +31,11 @@ struct afb_event_listener
        void *closure;
 };
 
-struct afb_event_listener_list;
-
-struct AFB_clientCtx
-{
-       int created;
-       unsigned refcount;
-       time_t expiration;    // expiration time of the token
-       struct afb_context *contexts;
-       char uuid[37];        // long term authentication of remote client
-       char token[37];       // short term authentication of remote client
-       struct afb_event_listener_list *listeners;
-};
-
 extern void ctxStoreInit (int max_session_count, int timeout, const char *initok, int context_count);
 
-extern struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid);
-extern struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx);
-extern void ctxClientPut(struct AFB_clientCtx *clientCtx);
+extern struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created);
+extern struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx);
+extern void ctxClientUnref(struct AFB_clientCtx *clientCtx);
 extern void ctxClientClose (struct AFB_clientCtx *clientCtx);
 
 extern int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener);
@@ -64,6 +43,11 @@ extern void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct
 extern int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object);
 
 extern int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token);
-extern int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length);
 extern void ctxTokenNew (struct AFB_clientCtx *clientCtx);
 
+extern const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx);
+extern const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx);
+
+extern void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index);
+extern void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*));
+