Improved libfast session management, using a hashtable

This commit is contained in:
Martin Willi 2009-12-05 17:56:44 +01:00
parent 4e90d9de9f
commit a6225e4936
2 changed files with 109 additions and 40 deletions

View File

@ -26,6 +26,10 @@
#include <debug.h> #include <debug.h>
#include <utils/mutex.h> #include <utils/mutex.h>
#include <utils/linked_list.h> #include <utils/linked_list.h>
#include <utils/hashtable.h>
/** Intervall to check for expired sessions, in seconds */
#define CLEANUP_INTERVAL 30
typedef struct private_dispatcher_t private_dispatcher_t; typedef struct private_dispatcher_t private_dispatcher_t;
@ -60,15 +64,20 @@ struct private_dispatcher_t {
mutex_t *mutex; mutex_t *mutex;
/** /**
* List of sessions * Hahstable with active sessions
*/ */
linked_list_t *sessions; hashtable_t *sessions;
/** /**
* session timeout * session timeout
*/ */
time_t timeout; time_t timeout;
/**
* timestamp of last session cleanup round
*/
time_t last_cleanup;
/** /**
* running in debug mode? * running in debug mode?
*/ */
@ -218,6 +227,60 @@ static void add_filter(private_dispatcher_t *this,
this->filters->insert_last(this->filters, entry); this->filters->insert_last(this->filters, entry);
} }
/**
* Hashtable hash function
*/
static u_int session_hash(char *sid)
{
return chunk_hash(chunk_create(sid, strlen(sid)));
}
/**
* Hashtable equals function
*/
static bool session_equals(char *sid1, char *sid2)
{
return streq(sid1, sid2);
}
/**
* Cleanup unused sessions
*/
static void cleanup_sessions(private_dispatcher_t *this, time_t now)
{
if (this->last_cleanup < now - CLEANUP_INTERVAL)
{
char *sid;
session_entry_t *entry;
enumerator_t *enumerator;
linked_list_t *remove;
this->last_cleanup = now;
remove = linked_list_create();
enumerator = this->sessions->create_enumerator(this->sessions);
while (enumerator->enumerate(enumerator, &sid, &entry))
{
/* check all sessions for timeout or close flag */
if (!entry->in_use &&
(entry->used < now - this->timeout || entry->closed))
{
remove->insert_last(remove, sid);
}
}
enumerator->destroy(enumerator);
while (remove->remove_last(remove, (void**)&sid) == SUCCESS)
{
entry = this->sessions->remove(this->sessions, sid);
if (entry)
{
session_entry_destroy(entry);
}
}
remove->destroy(remove);
}
}
/** /**
* Actual dispatching code * Actual dispatching code
*/ */
@ -228,8 +291,7 @@ static void dispatch(private_dispatcher_t *this)
while (TRUE) while (TRUE)
{ {
request_t *request; request_t *request;
session_entry_t *current, *found = NULL; session_entry_t *found = NULL;
enumerator_t *enumerator;
time_t now; time_t now;
char *sid; char *sid;
@ -241,33 +303,18 @@ static void dispatch(private_dispatcher_t *this)
{ {
continue; continue;
} }
sid = request->get_cookie(request, "SID");
now = time_monotonic(NULL); now = time_monotonic(NULL);
sid = request->get_cookie(request, "SID");
/* find session */
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
enumerator = this->sessions->create_enumerator(this->sessions); if (sid)
while (enumerator->enumerate(enumerator, &current))
{ {
/* check all sessions for timeout or close flag found = this->sessions->get(this->sessions, sid);
* TODO: use a seperate cleanup thread */ }
if (!current->in_use && if (found && !streq(found->host, request->get_host(request)))
(current->used < now - this->timeout || current->closed)) {
{ found = NULL;
this->sessions->remove_at(this->sessions, enumerator);
session_entry_destroy(current);
continue;
}
/* find by session ID. Prevent session hijacking by host check */
if (!found && sid && current->session->get_sid(current->session) &&
streq(current->session->get_sid(current->session), sid) &&
streq(current->host, request->get_host(request)))
{
found = current;
}
} }
enumerator->destroy(enumerator);
if (found) if (found)
{ {
/* wait until session is unused */ /* wait until session is unused */
@ -279,7 +326,8 @@ static void dispatch(private_dispatcher_t *this)
else else
{ /* create a new session if not found */ { /* create a new session if not found */
found = session_entry_create(this, request->get_host(request)); found = session_entry_create(this, request->get_host(request));
this->sessions->insert_first(this->sessions, found); sid = found->session->get_sid(found->session);
this->sessions->put(this->sessions, sid, found);
} }
found->in_use = TRUE; found->in_use = TRUE;
this->mutex->unlock(this->mutex); this->mutex->unlock(this->mutex);
@ -292,10 +340,10 @@ static void dispatch(private_dispatcher_t *this)
this->mutex->lock(this->mutex); this->mutex->lock(this->mutex);
found->in_use = FALSE; found->in_use = FALSE;
found->closed = request->session_closed(request); found->closed = request->session_closed(request);
this->mutex->unlock(this->mutex);
found->cond->signal(found->cond); found->cond->signal(found->cond);
cleanup_sessions(this, now);
this->mutex->unlock(this->mutex);
/* cleanup */
request->destroy(request); request->destroy(request);
} }
} }
@ -338,13 +386,23 @@ static void waitsignal(private_dispatcher_t *this)
*/ */
static void destroy(private_dispatcher_t *this) static void destroy(private_dispatcher_t *this)
{ {
char *sid;
session_entry_t *entry;
enumerator_t *enumerator;
FCGX_ShutdownPending(); FCGX_ShutdownPending();
while (this->thread_count--) while (this->thread_count--)
{ {
pthread_cancel(this->threads[this->thread_count]); pthread_cancel(this->threads[this->thread_count]);
pthread_join(this->threads[this->thread_count], NULL); pthread_join(this->threads[this->thread_count], NULL);
} }
this->sessions->destroy_function(this->sessions, (void*)session_entry_destroy); enumerator = this->sessions->create_enumerator(this->sessions);
while (enumerator->enumerate(enumerator, &sid, &entry))
{
session_entry_destroy(entry);
}
enumerator->destroy(enumerator);
this->sessions->destroy(this->sessions);
this->controllers->destroy_function(this->controllers, free); this->controllers->destroy_function(this->controllers, free);
this->filters->destroy_function(this->filters, free); this->filters->destroy_function(this->filters, free);
this->mutex->destroy(this->mutex); this->mutex->destroy(this->mutex);
@ -366,7 +424,8 @@ dispatcher_t *dispatcher_create(char *socket, bool debug, int timeout,
this->public.waitsignal = (void(*)(dispatcher_t*))waitsignal; this->public.waitsignal = (void(*)(dispatcher_t*))waitsignal;
this->public.destroy = (void(*)(dispatcher_t*))destroy; this->public.destroy = (void(*)(dispatcher_t*))destroy;
this->sessions = linked_list_create(); this->sessions = hashtable_create((void*)session_hash,
(void*)session_equals, 4096);
this->controllers = linked_list_create(); this->controllers = linked_list_create();
this->filters = linked_list_create(); this->filters = linked_list_create();
this->context_constructor = constructor; this->context_constructor = constructor;
@ -374,6 +433,7 @@ dispatcher_t *dispatcher_create(char *socket, bool debug, int timeout,
this->param = param; this->param = param;
this->fd = 0; this->fd = 0;
this->timeout = timeout; this->timeout = timeout;
this->last_cleanup = time_monotonic(NULL);
this->debug = debug; this->debug = debug;
this->threads = NULL; this->threads = NULL;

View File

@ -23,6 +23,8 @@
#include <utils/linked_list.h> #include <utils/linked_list.h>
#define COOKIE_LEN 16
typedef struct private_session_t private_session_t; typedef struct private_session_t private_session_t;
/** /**
@ -38,7 +40,12 @@ struct private_session_t {
/** /**
* session ID * session ID
*/ */
char *sid; char sid[COOKIE_LEN * 2 + 1];
/**
* have we sent the session cookie?
*/
bool cookie_sent;
/** /**
* list of controller instances controller_t * list of controller instances controller_t
@ -75,19 +82,20 @@ static void add_filter(private_session_t *this, filter_t *filter)
/** /**
* Create a session ID and a cookie * Create a session ID and a cookie
*/ */
static void create_sid(private_session_t *this, request_t *request) static void create_sid(private_session_t *this)
{ {
char buf[16]; char buf[COOKIE_LEN];
rng_t *rng; rng_t *rng;
memset(buf, 0, sizeof(buf));
memset(this->sid, 0, sizeof(this->sid));
rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
if (rng) if (rng)
{ {
rng->get_bytes(rng, sizeof(buf), buf); rng->get_bytes(rng, sizeof(buf), buf);
this->sid = chunk_to_hex(chunk_create(buf, sizeof(buf)), NULL, FALSE).ptr;
request->add_cookie(request, "SID", this->sid);
rng->destroy(rng); rng->destroy(rng);
} }
chunk_to_hex(chunk_create(buf, sizeof(buf)), this->sid, FALSE);
} }
/** /**
@ -123,9 +131,10 @@ static void process(private_session_t *this, request_t *request)
controller_t *current; controller_t *current;
int i = 0; int i = 0;
if (this->sid == NULL) if (!this->cookie_sent)
{ {
create_sid(this, request); request->add_cookie(request, "SID", this->sid);
this->cookie_sent = TRUE;
} }
start = request->get_path(request); start = request->get_path(request);
@ -189,7 +198,6 @@ static void destroy(private_session_t *this)
this->controllers->destroy_offset(this->controllers, offsetof(controller_t, destroy)); this->controllers->destroy_offset(this->controllers, offsetof(controller_t, destroy));
this->filters->destroy_offset(this->filters, offsetof(filter_t, destroy)); this->filters->destroy_offset(this->filters, offsetof(filter_t, destroy));
DESTROY_IF(this->context); DESTROY_IF(this->context);
free(this->sid);
free(this); free(this);
} }
@ -206,7 +214,8 @@ session_t *session_create(context_t *context)
this->public.get_sid = (char*(*)(session_t*))get_sid; this->public.get_sid = (char*(*)(session_t*))get_sid;
this->public.destroy = (void(*)(session_t*))destroy; this->public.destroy = (void(*)(session_t*))destroy;
this->sid = NULL; create_sid(this);
this->cookie_sent = FALSE;
this->controllers = linked_list_create(); this->controllers = linked_list_create();
this->filters = linked_list_create(); this->filters = linked_list_create();
this->context = context; this->context = context;