#include "afb-session.h"
#include "verbose.h"
-#define NOW (time(NULL))
+#define COOKEYCOUNT 8
+#define COOKEYMASK (COOKEYCOUNT - 1)
-struct value
-{
- void *value;
- void (*freecb)(void*);
-};
+#define NOW (time(NULL))
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 value *values;
- struct cookie *cookies;
+ struct cookie *cookies[COOKEYCOUNT];
};
// Session UUID are store in a simple array [for 10 sessions this should be enough]
int count; // current number of sessions
int max;
int timeout;
- int apicount;
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])
{
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)
{
int idx;
- struct cookie *cookie;
-
- // If application add a handle let's free it now
- assert (session->values != NULL);
-
- // Free session handle with a standard Free function, with app callback or ignore it
- for (idx=0; idx < sessions.apicount; idx ++)
- afb_session_set_value(session, idx, NULL, NULL);
+ 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;
+ }
}
}
// Create a new store in RAM, not that is too small it will be automatically extended
-void afb_session_init (int max_session_count, int timeout, const char *initok, int context_count)
+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));
+ pthread_mutex_init(&sessions.mutex, NULL);
sessions.max = max_session_count;
sessions.timeout = timeout;
- sessions.apicount = context_count;
if (initok == NULL)
/* without token, a secret is made to forbid creation of sessions */
new_uuid(sessions.initok);
struct afb_session *session;
/* allocates a new one */
- session = calloc(1, sizeof(struct afb_session) + ((unsigned)sessions.apicount * sizeof(*session->values)));
+ session = calloc(1, sizeof *session);
if (session == NULL) {
errno = ENOMEM;
goto error;
}
- session->values = (void*)(session + 1);
+ pthread_mutex_init(&session->mutex, NULL);
/* generate the uuid */
if (uuid == NULL) {
--session->refcount;
if (session->refcount == 0 && session->uuid[0] == 0) {
destroy (session);
+ pthread_mutex_destroy(&session->mutex);
free(session);
}
}
return session->token;
}
-unsigned afb_session_get_LOA (struct afb_session *session)
+static struct cookie *cookie_search(struct afb_session *session, const void *key, int *idx)
{
- assert(session != NULL);
- return session->loa;
-}
+ struct cookie *cookie;
-void afb_session_set_LOA (struct afb_session *session, unsigned loa)
-{
- assert(session != NULL);
- session->loa = loa;
+ cookie = session->cookies[*idx = cookeyidx(key)];
+ while(cookie != NULL && cookie->key != key)
+ cookie = cookie->next;
+ return cookie;
}
-void *afb_session_get_value(struct afb_session *session, int index)
+static struct cookie *cookie_add(struct afb_session *session, int idx, const void *key, void *value, void (*freecb)(void*))
{
- assert(session != NULL);
- assert(index >= 0);
- assert(index < sessions.apicount);
- return session->values[index].value;
+ struct cookie *cookie;
+
+ cookie = malloc(sizeof *cookie);
+ if (!cookie)
+ errno = ENOMEM;
+ else {
+ cookie->key = key;
+ cookie->value = value;
+ cookie->freecb = freecb;
+ cookie->next = session->cookies[idx];
+ session->cookies[idx] = cookie;
+ }
+ return cookie;
}
-void afb_session_set_value(struct afb_session *session, int index, void *value, void (*freecb)(void*))
+void *afb_session_cookie(struct afb_session *session, const void *key, void *(*makecb)(void), void (*freecb)(void*))
{
- struct value prev;
- assert(session != NULL);
- assert(index >= 0);
- assert(index < sessions.apicount);
- prev = session->values[index];
- session->values[index] = (struct value){.value = value, .freecb = freecb};
- if (prev.value != NULL && prev.value != value && prev.freecb != NULL)
- prev.freecb(prev.value);
+ int idx;
+ void *value;
+ struct cookie *cookie;
+
+ lock(session);
+ cookie = cookie_search(session, key, &idx);
+ if (cookie)
+ value = cookie->value;
+ else {
+ value = makecb ? makecb() : NULL;
+ if (makecb || freecb) {
+ cookie = cookie_add(session, idx, key, value, freecb);
+ if (!cookie) {
+ if (makecb && freecb)
+ free(value);
+ value = NULL;
+ }
+ }
+ }
+ unlock(session);
+ return value;
}
void *afb_session_get_cookie(struct afb_session *session, const void *key)
{
+ int idx;
+ void *value;
struct cookie *cookie;
- cookie = session->cookies;
- while(cookie != NULL) {
- if (cookie->key == key)
- return cookie->value;
- cookie = cookie->next;
- }
- return NULL;
+ lock(session);
+ cookie = cookie_search(session, key, &idx);
+ value = cookie ? cookie->value : NULL;
+ unlock(session);
+ return value;
}
int afb_session_set_cookie(struct afb_session *session, const void *key, void *value, void (*freecb)(void*))
{
+ int idx;
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;
+ lock(session);
+ cookie = cookie_search(session, key, &idx);
+ if (!cookie)
+ cookie = cookie_add(session, idx, key, value, freecb);
+ else {
+ if (cookie->value != value && cookie->freecb)
+ cookie->freecb(cookie->value);
+ cookie->value = value;
+ cookie->freecb = freecb;
}
-
- cookie->key = key;
- cookie->value = value;
- cookie->freecb = freecb;
- cookie->next = session->cookies;
- session->cookies = cookie;
- return 0;
+ unlock(session);
+ return -!cookie;
}