afb-session: rewrite of cookie handling
[src/app-framework-binder.git] / src / afb-session.c
index 13b9505..77a20fa 100644 (file)
@@ -31,6 +31,9 @@
 #include "afb-session.h"
 #include "verbose.h"
 
+#define COOKEYCOUNT  8
+#define COOKEYMASK   (COOKEYCOUNT - 1)
+
 #define NOW (time(NULL))
 
 struct cookie
@@ -44,13 +47,13 @@ struct cookie
 struct afb_session
 {
        unsigned refcount;
-       unsigned loa;
        int timeout;
        time_t expiration;    // expiration time of the token
        time_t access;
+       pthread_mutex_t mutex;
        char uuid[37];        // long term authentication of remote client
        char token[37];       // short term authentication of remote client
-       struct cookie *cookies;
+       struct cookie *cookies[COOKEYCOUNT];
 };
 
 // Session UUID are store in a simple array [for 10 sessions this should be enough]
@@ -63,6 +66,18 @@ static struct {
        char initok[37];
 } sessions;
 
+/**
+ * Get the index of the 'key' in the cookies array.
+ * @param key the key to scan
+ * @return the index of the list for key within cookies
+ */
+static int cookeyidx(const void *key)
+{
+       intptr_t x = (intptr_t)key;
+       unsigned r = (unsigned)((x >> 5) ^ (x >> 15));
+       return r & COOKEYMASK;
+}
+
 /* generate a uuid */
 static void new_uuid(char uuid[37])
 {
@@ -71,19 +86,33 @@ static void new_uuid(char uuid[37])
        uuid_unparse_lower(newuuid, uuid);
 }
 
+static inline void lock(struct afb_session *session)
+{
+       pthread_mutex_lock(&session->mutex);
+}
+
+static inline void unlock(struct afb_session *session)
+{
+       pthread_mutex_unlock(&session->mutex);
+}
+
 // Free context [XXXX Should be protected again memory abort XXXX]
 static void free_data (struct afb_session *session)
 {
-       struct cookie *cookie;
+       int idx;
+       struct cookie *cookie, *next;
 
        // free cookies
-       cookie = session->cookies;
-       while (cookie != NULL) {
-               session->cookies = cookie->next;
-               if (cookie->value != NULL && cookie->freecb != NULL)
-                       cookie->freecb(cookie->value);
-               free(cookie);
-               cookie = session->cookies;
+       for (idx = 0 ; idx < COOKEYCOUNT ; idx++) {
+               cookie = session->cookies[idx];
+               session->cookies[idx] = NULL;
+               while (cookie != NULL) {
+                       next = cookie->next;
+                       if (cookie->freecb != NULL)
+                               cookie->freecb(cookie->value);
+                       free(cookie);
+                       cookie = next;
+               }
        }
 }
 
@@ -91,7 +120,8 @@ static void free_data (struct afb_session *session)
 void afb_session_init (int max_session_count, int timeout, const char *initok)
 {
        // let's create as store as hashtable does not have any
-       sessions.store = calloc (1 + (unsigned)max_session_count, sizeof(struct afb_session));
+       sessions.store = calloc (1 + (unsigned)max_session_count, sizeof *sessions.store);
+       pthread_mutex_init(&sessions.mutex, NULL);
        sessions.max = max_session_count;
        sessions.timeout = timeout;
        if (initok == NULL)
@@ -105,6 +135,11 @@ void afb_session_init (int max_session_count, int timeout, const char *initok)
        }
 }
 
+const char *afb_session_initial_token()
+{
+       return sessions.initok;
+}
+
 static struct afb_session *search (const char* uuid)
 {
        int  idx;
@@ -206,11 +241,12 @@ static struct afb_session *make_session (const char *uuid, int timeout, time_t n
        struct afb_session *session;
 
        /* allocates a new one */
-       session = calloc(1, sizeof(struct afb_session));
+       session = calloc(1, sizeof *session);
        if (session == NULL) {
                errno = ENOMEM;
                goto error;
        }
+       pthread_mutex_init(&session->mutex, NULL);
 
        /* generate the uuid */
        if (uuid == NULL) {
@@ -278,6 +314,8 @@ struct afb_session *afb_session_get (const char *uuid, int *created)
        /* search for an existing one not too old */
        if (uuid != NULL) {
                session = search(uuid);
+               if (!created)
+                       return session;
                if (session != NULL) {
                        *created = 0;
                        session->access = now;
@@ -286,14 +324,16 @@ struct afb_session *afb_session_get (const char *uuid, int *created)
                }
        }
 
-       *created = 1;
+       if (created)
+               *created = 1;
+
        return make_session(uuid, sessions.timeout, now);
 }
 
 struct afb_session *afb_session_addref(struct afb_session *session)
 {
        if (session != NULL)
-               session->refcount++;
+               __atomic_add_fetch(&session->refcount, 1, __ATOMIC_RELAXED);
        return session;
 }
 
@@ -301,10 +341,12 @@ void afb_session_unref(struct afb_session *session)
 {
        if (session != NULL) {
                assert(session->refcount != 0);
-               --session->refcount;
-               if (session->refcount == 0 && session->uuid[0] == 0) {
-                       destroy (session);
-                       free(session);
+               if (!__atomic_sub_fetch(&session->refcount, 1, __ATOMIC_RELAXED)) {
+                       if (session->uuid[0] == 0) {
+                               destroy (session);
+                               pthread_mutex_destroy(&session->mutex);
+                               free(session);
+                       }
                }
        }
 }
@@ -364,60 +406,60 @@ const char *afb_session_token (struct afb_session *session)
        return session->token;
 }
 
-unsigned afb_session_get_LOA (struct afb_session *session)
+void *afb_session_cookie(struct afb_session *session, const void *key, void *(*makecb)(void *closure), void (*freecb)(void *item), void *closure, int replace)
 {
-       assert(session != NULL);
-       return session->loa;
-}
+       int idx;
+       void *value;
+       struct cookie *cookie;
 
-void afb_session_set_LOA (struct afb_session *session, unsigned loa)
-{
-       assert(session != NULL);
-       session->loa = loa;
+       idx = cookeyidx(key);
+       lock(session);
+       cookie = session->cookies[idx];
+       for (;;) {
+               if (!cookie) {
+                       value = makecb ? makecb(closure) : closure;
+                       if (replace || makecb || freecb) {
+                               cookie = malloc(sizeof *cookie);
+                               if (!cookie) {
+                                       errno = ENOMEM;
+                                       if (freecb)
+                                               freecb(value);
+                                       value = NULL;
+                               } else {
+                                       cookie->key = key;
+                                       cookie->value = value;
+                                       cookie->freecb = freecb;
+                                       cookie->next = session->cookies[idx];
+                                       session->cookies[idx] = cookie;
+                               }
+                       }
+                       break;
+               } else if (cookie->key == key) {
+                       if (!replace)
+                               value = cookie->value;
+                       else {
+                               value = makecb ? makecb(closure) : closure;
+                               if (cookie->value != value && cookie->freecb)
+                                       cookie->freecb(cookie->value);
+                               cookie->value = value;
+                               cookie->freecb = freecb;
+                       }
+                       break;
+               } else {
+                       cookie = cookie->next;
+               }
+       }
+       unlock(session);
+       return value;
 }
 
 void *afb_session_get_cookie(struct afb_session *session, const void *key)
 {
-       struct cookie *cookie;
-
-       cookie = session->cookies;
-       while(cookie != NULL) {
-               if (cookie->key == key)
-                       return cookie->value;
-               cookie = cookie->next;
-       }
-       return NULL;
+       return afb_session_cookie(session, key, NULL, NULL, NULL, 0);
 }
 
 int afb_session_set_cookie(struct afb_session *session, const void *key, void *value, void (*freecb)(void*))
 {
-       struct cookie *cookie;
-
-       /* search for a replacement */
-       cookie = session->cookies;
-       while(cookie != NULL) {
-               if (cookie->key == key) {
-                       if (cookie->value != NULL && cookie->value != value && cookie->freecb != NULL)
-                               cookie->freecb(cookie->value);
-                       cookie->value = value;
-                       cookie->freecb = freecb;
-                       return 0;
-               }
-               cookie = cookie->next;
-       }
-
-       /* allocates */
-       cookie = malloc(sizeof *cookie);
-       if (cookie == NULL) {
-               errno = ENOMEM;
-               return -1;
-       }
-
-       cookie->key = key;
-       cookie->value = value;
-       cookie->freecb = freecb;
-       cookie->next = session->cookies;
-       session->cookies = cookie;
-       return 0;
+       return -(value != afb_session_cookie(session, key, NULL, freecb, value, 1));
 }