merged the modularization branch (credentials) back to trunk
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
lib_LTLIBRARIES = libfast.la
|
||||
|
||||
libfast_la_SOURCES = context.h dispatcher.c request.h session.h \
|
||||
controller.h dispatcher.h request.c session.c filter.h
|
||||
libfast_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \
|
||||
-lfcgi -lpthread -lneo_cgi -lneo_cs -lneo_utl -lz
|
||||
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I/usr/include/ClearSilver
|
||||
AM_CFLAGS = -rdynamic
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup context context
|
||||
* @{ @ingroup libfast
|
||||
*/
|
||||
|
||||
#ifndef CONTEXT_H_
|
||||
#define CONTEXT_H_
|
||||
|
||||
typedef struct context_t context_t;
|
||||
|
||||
/**
|
||||
* Constructor function for a user specific context.
|
||||
*/
|
||||
typedef context_t *(*context_constructor_t)(void *param);
|
||||
|
||||
/**
|
||||
* User specific session context, to extend.
|
||||
*/
|
||||
struct context_t {
|
||||
|
||||
/**
|
||||
* Destroy the context_t.
|
||||
*/
|
||||
void (*destroy) (context_t *this);
|
||||
};
|
||||
|
||||
#endif /* CONTEXT_H_ @}*/
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup controller_i controller
|
||||
* @{ @ingroup libfast
|
||||
*/
|
||||
|
||||
#ifndef CONTROLLER_H_
|
||||
#define CONTROLLER_H_
|
||||
|
||||
#include "request.h"
|
||||
#include "context.h"
|
||||
|
||||
typedef struct controller_t controller_t;
|
||||
|
||||
/**
|
||||
* Constructor function for a controller.
|
||||
*
|
||||
* @param context session specific context, implements context_t
|
||||
* @param param user supplied param, as registered to the dispatcher
|
||||
*/
|
||||
typedef controller_t *(*controller_constructor_t)(context_t* context, void *param);
|
||||
|
||||
/**
|
||||
* Controller interface, to be implemented by users controllers.
|
||||
*
|
||||
* Controller instances get created per session, so each session has an
|
||||
* associated set of private controller instances.
|
||||
* The controller handle function is called for each incoming request.
|
||||
*/
|
||||
struct controller_t {
|
||||
|
||||
/**
|
||||
* Get the name of the controller.
|
||||
*
|
||||
* @return name of the controller
|
||||
*/
|
||||
char* (*get_name)(controller_t *this);
|
||||
|
||||
/**
|
||||
* Handle a HTTP request for that controller.
|
||||
*
|
||||
* Request URLs are parsed in the form
|
||||
* controller_name/p1/p2/p3/p4/p5 with a maximum of 5 parameters. Each
|
||||
* parameter not found in the request URL is set to NULL.
|
||||
*
|
||||
* @param request HTTP request
|
||||
* @param p1 first parameter
|
||||
* @param p2 second parameter
|
||||
* @param p3 third parameter
|
||||
* @param p4 forth parameter
|
||||
* @param p5 fifth parameter
|
||||
* @return
|
||||
*/
|
||||
void (*handle)(controller_t *this, request_t *request,
|
||||
char *p1, char *p2, char *p3, char *p4, char *p5);
|
||||
|
||||
/**
|
||||
* Destroy the controller instance.
|
||||
*/
|
||||
void (*destroy) (controller_t *this);
|
||||
};
|
||||
|
||||
#endif /* CONTROLLER_H_ @} */
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include "dispatcher.h"
|
||||
|
||||
#include "request.h"
|
||||
#include "session.h"
|
||||
|
||||
#include <fcgiapp.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <utils/linked_list.h>
|
||||
|
||||
typedef struct private_dispatcher_t private_dispatcher_t;
|
||||
|
||||
/**
|
||||
* private data of the task manager
|
||||
*/
|
||||
struct private_dispatcher_t {
|
||||
|
||||
/**
|
||||
* public functions
|
||||
*/
|
||||
dispatcher_t public;
|
||||
|
||||
/**
|
||||
* fcgi socket fd
|
||||
*/
|
||||
int fd;
|
||||
|
||||
/**
|
||||
* thread list
|
||||
*/
|
||||
pthread_t *threads;
|
||||
|
||||
/**
|
||||
* number of threads in "threads"
|
||||
*/
|
||||
int thread_count;
|
||||
|
||||
/**
|
||||
* session locking mutex
|
||||
*/
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
/**
|
||||
* List of sessions
|
||||
*/
|
||||
linked_list_t *sessions;
|
||||
|
||||
/**
|
||||
* session timeout
|
||||
*/
|
||||
time_t timeout;
|
||||
|
||||
/**
|
||||
* running in debug mode?
|
||||
*/
|
||||
bool debug;
|
||||
|
||||
/**
|
||||
* List of controllers controller_constructor_t
|
||||
*/
|
||||
linked_list_t *controllers;
|
||||
|
||||
/**
|
||||
* List of filters filter_constructor_t
|
||||
*/
|
||||
linked_list_t *filters;
|
||||
|
||||
/**
|
||||
* constructor function to create session context (in controller_entry_t)
|
||||
*/
|
||||
context_constructor_t context_constructor;
|
||||
|
||||
/**
|
||||
* user param to context constructor
|
||||
*/
|
||||
void *param;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
/** constructor function */
|
||||
controller_constructor_t constructor;
|
||||
/** parameter to constructor */
|
||||
void *param;
|
||||
} controller_entry_t;
|
||||
|
||||
typedef struct {
|
||||
/** constructor function */
|
||||
filter_constructor_t constructor;
|
||||
/** parameter to constructor */
|
||||
void *param;
|
||||
} filter_entry_t;
|
||||
|
||||
typedef struct {
|
||||
/** session instance */
|
||||
session_t *session;
|
||||
/** condvar to wait for session */
|
||||
pthread_cond_t cond;
|
||||
/** client host address, to prevent session hijacking */
|
||||
char *host;
|
||||
/** TRUE if session is in use */
|
||||
bool in_use;
|
||||
/** last use of the session */
|
||||
time_t used;
|
||||
/** has the session been closed by the handler? */
|
||||
bool closed;
|
||||
} session_entry_t;
|
||||
|
||||
/**
|
||||
* create a session and instanciate controllers
|
||||
*/
|
||||
static session_t* load_session(private_dispatcher_t *this)
|
||||
{
|
||||
iterator_t *iterator;
|
||||
controller_entry_t *centry;
|
||||
filter_entry_t *fentry;
|
||||
session_t *session;
|
||||
context_t *context = NULL;
|
||||
controller_t *controller;
|
||||
filter_t *filter;
|
||||
|
||||
if (this->context_constructor)
|
||||
{
|
||||
context = this->context_constructor(this->param);
|
||||
}
|
||||
session = session_create(context);
|
||||
|
||||
iterator = this->controllers->create_iterator(this->controllers, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)¢ry))
|
||||
{
|
||||
controller = centry->constructor(context, centry->param);
|
||||
session->add_controller(session, controller);
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
|
||||
iterator = this->filters->create_iterator(this->filters, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)&fentry))
|
||||
{
|
||||
filter = fentry->constructor(context, fentry->param);
|
||||
session->add_filter(session, filter);
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new session entry
|
||||
*/
|
||||
static session_entry_t *session_entry_create(private_dispatcher_t *this,
|
||||
char *host)
|
||||
{
|
||||
session_entry_t *entry;
|
||||
|
||||
entry = malloc_thing(session_entry_t);
|
||||
entry->in_use = FALSE;
|
||||
entry->closed = FALSE;
|
||||
pthread_cond_init(&entry->cond, NULL);
|
||||
entry->session = load_session(this);
|
||||
entry->used = time(NULL);
|
||||
entry->host = strdup(host);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void session_entry_destroy(session_entry_t *entry)
|
||||
{
|
||||
entry->session->destroy(entry->session);
|
||||
free(entry->host);
|
||||
free(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dispatcher_t.add_controller.
|
||||
*/
|
||||
static void add_controller(private_dispatcher_t *this,
|
||||
controller_constructor_t constructor, void *param)
|
||||
{
|
||||
controller_entry_t *entry = malloc_thing(controller_entry_t);
|
||||
|
||||
entry->constructor = constructor;
|
||||
entry->param = param;
|
||||
this->controllers->insert_last(this->controllers, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dispatcher_t.add_filter.
|
||||
*/
|
||||
static void add_filter(private_dispatcher_t *this,
|
||||
filter_constructor_t constructor, void *param)
|
||||
{
|
||||
filter_entry_t *entry = malloc_thing(filter_entry_t);
|
||||
|
||||
entry->constructor = constructor;
|
||||
entry->param = param;
|
||||
this->filters->insert_last(this->filters, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual dispatching code
|
||||
*/
|
||||
static void dispatch(private_dispatcher_t *this)
|
||||
{
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
request_t *request;
|
||||
session_entry_t *current, *found = NULL;
|
||||
iterator_t *iterator;
|
||||
time_t now;
|
||||
char *sid;
|
||||
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
request = request_create(this->fd, this->debug);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
|
||||
if (request == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
sid = request->get_cookie(request, "SID");
|
||||
now = time(NULL);
|
||||
|
||||
/* find session */
|
||||
pthread_mutex_lock(&this->mutex);
|
||||
iterator = this->sessions->create_iterator(this->sessions, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)¤t))
|
||||
{
|
||||
/* check all sessions for timeout or close flag
|
||||
* TODO: use a seperate cleanup thread */
|
||||
if (!current->in_use &&
|
||||
(current->used < now - this->timeout || current->closed))
|
||||
{
|
||||
iterator->remove(iterator);
|
||||
session_entry_destroy(current);
|
||||
continue;
|
||||
}
|
||||
/* find by session ID. Prevent session hijacking by host check */
|
||||
if (!found && sid &&
|
||||
streq(current->session->get_sid(current->session), sid) &&
|
||||
streq(current->host, request->get_host(request)))
|
||||
{
|
||||
found = current;
|
||||
}
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
|
||||
if (found)
|
||||
{
|
||||
/* wait until session is unused */
|
||||
while (found->in_use)
|
||||
{
|
||||
pthread_cond_wait(&found->cond, &this->mutex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* create a new session if not found */
|
||||
found = session_entry_create(this, request->get_host(request));
|
||||
this->sessions->insert_first(this->sessions, found);
|
||||
}
|
||||
found->in_use = TRUE;
|
||||
pthread_mutex_unlock(&this->mutex);
|
||||
|
||||
/* start processing */
|
||||
found->session->process(found->session, request);
|
||||
found->used = time(NULL);
|
||||
|
||||
/* release session */
|
||||
pthread_mutex_lock(&this->mutex);
|
||||
found->in_use = FALSE;
|
||||
found->closed = request->session_closed(request);
|
||||
pthread_cond_signal(&found->cond);
|
||||
pthread_mutex_unlock(&this->mutex);
|
||||
|
||||
/* cleanup */
|
||||
request->destroy(request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dispatcher_t.run.
|
||||
*/
|
||||
static void run(private_dispatcher_t *this, int threads)
|
||||
{
|
||||
this->thread_count = threads;
|
||||
this->threads = malloc(sizeof(pthread_t) * threads);
|
||||
while (threads)
|
||||
{
|
||||
if (pthread_create(&this->threads[threads - 1],
|
||||
NULL, (void*)dispatch, this) == 0)
|
||||
{
|
||||
threads--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dispatcher_t.waitsignal.
|
||||
*/
|
||||
static void waitsignal(private_dispatcher_t *this)
|
||||
{
|
||||
sigset_t set;
|
||||
int sig;
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGINT);
|
||||
sigaddset(&set, SIGTERM);
|
||||
sigaddset(&set, SIGHUP);
|
||||
sigprocmask(SIG_BLOCK, &set, NULL);
|
||||
sigwait(&set, &sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of dispatcher_t.destroy
|
||||
*/
|
||||
static void destroy(private_dispatcher_t *this)
|
||||
{
|
||||
FCGX_ShutdownPending();
|
||||
while (this->thread_count--)
|
||||
{
|
||||
pthread_cancel(this->threads[this->thread_count]);
|
||||
pthread_join(this->threads[this->thread_count], NULL);
|
||||
}
|
||||
this->sessions->destroy_function(this->sessions, (void*)session_entry_destroy);
|
||||
this->controllers->destroy_function(this->controllers, free);
|
||||
this->filters->destroy_function(this->filters, free);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* see header file
|
||||
*/
|
||||
dispatcher_t *dispatcher_create(char *socket, bool debug, int timeout,
|
||||
context_constructor_t constructor, void *param)
|
||||
{
|
||||
private_dispatcher_t *this = malloc_thing(private_dispatcher_t);
|
||||
|
||||
this->public.add_controller = (void(*)(dispatcher_t*, controller_constructor_t, void*))add_controller;
|
||||
this->public.add_filter = (void(*)(dispatcher_t*,filter_constructor_t constructor, void *param))add_filter;
|
||||
this->public.run = (void(*)(dispatcher_t*, int threads))run;
|
||||
this->public.waitsignal = (void(*)(dispatcher_t*))waitsignal;
|
||||
this->public.destroy = (void(*)(dispatcher_t*))destroy;
|
||||
|
||||
this->sessions = linked_list_create();
|
||||
this->controllers = linked_list_create();
|
||||
this->filters = linked_list_create();
|
||||
this->context_constructor = constructor;
|
||||
pthread_mutex_init(&this->mutex, NULL);
|
||||
this->param = param;
|
||||
this->fd = 0;
|
||||
this->timeout = timeout;
|
||||
this->debug = debug;
|
||||
|
||||
FCGX_Init();
|
||||
|
||||
if (socket)
|
||||
{
|
||||
unlink(socket);
|
||||
this->fd = FCGX_OpenSocket(socket, 10);
|
||||
}
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup libfast libfast
|
||||
* @{
|
||||
* FastCGI Application Server w/ templates.
|
||||
*
|
||||
* Libfast is a framework to write web applications in an MVC fashion. It uses
|
||||
* the ClearSilver template engine and communicates through FastCGI with
|
||||
* the webserver. It is multithreaded and really fast.
|
||||
*
|
||||
* The application has a global context and a session context. The global
|
||||
* context is accessed from all sessions simultaneously and therefore
|
||||
* needs to be threadsave. Often a database wrapper is the global context.
|
||||
* The session context is instanciated per session. Sessions are managed
|
||||
* automatically through session cookies. The session context is kept alive
|
||||
* until the session times out. It must implement the context_t interface and
|
||||
* a #context_constructor_t is needed to create instances. To each session,
|
||||
* a set of controllers gets instanciated. The controller instances are per
|
||||
* session, so you can hold private data for each user.
|
||||
* Controllers need to implement the controller_t interface and need a
|
||||
* #controller_constructor_t function to create instances.
|
||||
*
|
||||
* A small example shows how to set up libfast:
|
||||
* @code
|
||||
dispatcher_t *dispatcher;
|
||||
your_global_context_implementation_t *global;
|
||||
|
||||
global = initialize_your_global_context();
|
||||
|
||||
dispatcher = dispatcher_create(NULL, FALSE, 180,
|
||||
(context_constructor_t)your_session_context_create, global);
|
||||
dispatcher->add_controller(dispatcher, your_controller1_create, param1);
|
||||
dispatcher->add_controller(dispatcher, your_controller2_create, param2);
|
||||
|
||||
dispatcher->run(dispatcher, 20);
|
||||
|
||||
dispatcher->waitsignal(dispatcher);
|
||||
|
||||
dispatcher->destroy(dispatcher);
|
||||
global->destroy();
|
||||
@endcode
|
||||
* @}
|
||||
*
|
||||
* @defgroup dispatcher dispatcher
|
||||
* @{ @ingroup libfast
|
||||
*/
|
||||
|
||||
#ifndef DISPATCHER_H_
|
||||
#define DISPATCHER_H_
|
||||
|
||||
#include "controller.h"
|
||||
#include "filter.h"
|
||||
|
||||
typedef struct dispatcher_t dispatcher_t;
|
||||
|
||||
/**
|
||||
* Dispatcher, accepts connections using multiple threads.
|
||||
*
|
||||
* The dispatcher creates a session for each client (using SID cookies). In
|
||||
* each session, a session context is created using the context constructor.
|
||||
* Each controller is instanciated in the session using the controller
|
||||
* constructor added with add_controller.
|
||||
*/
|
||||
struct dispatcher_t {
|
||||
|
||||
/**
|
||||
* Register a controller to the dispatcher.
|
||||
*
|
||||
* The first controller added serves as default controller. Client's
|
||||
* get redirected to it if no other controller matches.
|
||||
*
|
||||
* @param constructor constructor function to the conntroller
|
||||
* @param param param to pass to constructor
|
||||
*/
|
||||
void (*add_controller)(dispatcher_t *this,
|
||||
controller_constructor_t constructor, void *param);
|
||||
|
||||
/**
|
||||
* @brief Add a filter to the dispatcher.
|
||||
*
|
||||
* @param constructor constructor to create filter in session
|
||||
* @param param param to pass to constructor
|
||||
*/
|
||||
void (*add_filter)(dispatcher_t *this,
|
||||
filter_constructor_t constructor, void *param);
|
||||
|
||||
/**
|
||||
* Start with dispatching.
|
||||
*
|
||||
* Instanciate a constant thread pool and start dispatching requests.
|
||||
*
|
||||
* @param threads number of dispatching threads
|
||||
*/
|
||||
void (*run)(dispatcher_t *this, int threads);
|
||||
|
||||
/**
|
||||
* Wait for a relevant signal action.
|
||||
*
|
||||
*/
|
||||
void (*waitsignal)(dispatcher_t *this);
|
||||
|
||||
/**
|
||||
* Destroy the dispatcher_t.
|
||||
*/
|
||||
void (*destroy) (dispatcher_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a dispatcher.
|
||||
*
|
||||
* The context constructor is invoked to create a session context for
|
||||
* each session.
|
||||
*
|
||||
* @param socket FastCGI socket path, NULL for dynamic
|
||||
* @param debug no stripping, no compression, timing information
|
||||
* @param timeout session timeout
|
||||
* @param constructor construction function for session context
|
||||
* @param param parameter to supply to context constructor
|
||||
*/
|
||||
dispatcher_t *dispatcher_create(char *socket, bool debug, int timeout,
|
||||
context_constructor_t constructor, void *param);
|
||||
|
||||
#endif /* DISPATCHER_H_ @} */
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/*
|
||||
* @defgroup filter filter
|
||||
* @{ @ingroup libfast
|
||||
*/
|
||||
|
||||
#ifndef FILTER_H_
|
||||
#define FILTER_H_
|
||||
|
||||
#include "request.h"
|
||||
#include "context.h"
|
||||
#include "controller.h"
|
||||
|
||||
typedef struct filter_t filter_t;
|
||||
|
||||
/**
|
||||
* Constructor function for a filter
|
||||
*
|
||||
* @param context session specific context
|
||||
* @param param user supplied param
|
||||
*/
|
||||
typedef filter_t *(*filter_constructor_t)(context_t* context, void *param);
|
||||
|
||||
/**
|
||||
* Filter interface, to be implemented by users filters.
|
||||
*/
|
||||
struct filter_t {
|
||||
|
||||
/**
|
||||
* Called before the controller handles the request
|
||||
*
|
||||
* @param request HTTP request
|
||||
* @param controller selected controller, before execution
|
||||
* @return TRUE to continue request handling
|
||||
*/
|
||||
bool (*run)(filter_t *this, request_t *request, controller_t *controller);
|
||||
|
||||
/**
|
||||
* Destroy the filter instance.
|
||||
*/
|
||||
void (*destroy) (filter_t *this);
|
||||
};
|
||||
|
||||
#endif /* FILTER_H_ @} */
|
||||
@@ -0,0 +1,447 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "request.h"
|
||||
|
||||
#include <library.h>
|
||||
#include <debug.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <ClearSilver/ClearSilver.h>
|
||||
|
||||
typedef struct private_request_t private_request_t;
|
||||
|
||||
/**
|
||||
* private data of the task manager
|
||||
*/
|
||||
struct private_request_t {
|
||||
|
||||
/**
|
||||
* public functions
|
||||
*/
|
||||
request_t public;
|
||||
|
||||
/**
|
||||
* FastCGI request object
|
||||
*/
|
||||
FCGX_Request req;
|
||||
|
||||
/**
|
||||
* length of the req.envp array
|
||||
*/
|
||||
int req_env_len;
|
||||
|
||||
/**
|
||||
* ClearSilver CGI Kit context
|
||||
*/
|
||||
CGI *cgi;
|
||||
|
||||
/**
|
||||
* ClearSilver HDF dataset for this request
|
||||
*/
|
||||
HDF *hdf;
|
||||
|
||||
/**
|
||||
* close the session?
|
||||
*/
|
||||
bool closed;
|
||||
|
||||
/**
|
||||
* reference count
|
||||
*/
|
||||
refcount_t ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* key to a the threads "this" request, used for ClearSilver cgiwrap callbacks.
|
||||
* ClearSilver cgiwrap is not threadsave, so we use a private
|
||||
* context for each thread.
|
||||
*/
|
||||
static pthread_key_t this_key;
|
||||
|
||||
/**
|
||||
* control variable for pthread_once
|
||||
*/
|
||||
pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
|
||||
/**
|
||||
* fcgiwrap read callback
|
||||
*/
|
||||
static int read_cb(void *null, char *buf, int size)
|
||||
{
|
||||
private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
|
||||
|
||||
return FCGX_GetStr(buf, size, this->req.in);
|
||||
}
|
||||
|
||||
/**
|
||||
* fcgiwrap writef callback
|
||||
*/
|
||||
static int writef_cb(void *null, const char *format, va_list args)
|
||||
{
|
||||
private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
|
||||
|
||||
FCGX_VFPrintF(this->req.out, format, args);
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* fcgiwrap write callback
|
||||
*/
|
||||
static int write_cb(void *null, const char *buf, int size)
|
||||
{
|
||||
private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
|
||||
|
||||
return FCGX_PutStr(buf, size, this->req.out);
|
||||
}
|
||||
|
||||
/**
|
||||
* fcgiwrap getenv callback
|
||||
*/
|
||||
static char *getenv_cb(void *null, const char *key)
|
||||
{
|
||||
char *value;
|
||||
private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
|
||||
|
||||
value = FCGX_GetParam(key, this->req.envp);
|
||||
return value ? strdup(value) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fcgiwrap getenv callback
|
||||
*/
|
||||
static int putenv_cb(void *null, const char *key, const char *value)
|
||||
{
|
||||
/* not supported */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* fcgiwrap iterenv callback
|
||||
*/
|
||||
static int iterenv_cb(void *null, int num, char **key, char **value)
|
||||
{
|
||||
*key = NULL;
|
||||
*value = NULL;
|
||||
private_request_t *this = (private_request_t*)pthread_getspecific(this_key);
|
||||
if (num < this->req_env_len)
|
||||
{
|
||||
char *eq;
|
||||
|
||||
eq = strchr(this->req.envp[num], '=');
|
||||
if (eq)
|
||||
{
|
||||
*key = strndup(this->req.envp[num], eq - this->req.envp[num]);
|
||||
*value = strdup(eq + 1);
|
||||
}
|
||||
if (*key == NULL || *value == NULL)
|
||||
{
|
||||
free(*key);
|
||||
free(*value);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_cookie.
|
||||
*/
|
||||
static char* get_cookie(private_request_t *this, char *name)
|
||||
{
|
||||
return hdf_get_valuef(this->hdf, "Cookie.%s", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_path.
|
||||
*/
|
||||
static char* get_path(private_request_t *this)
|
||||
{
|
||||
char * path = FCGX_GetParam("PATH_INFO", this->req.envp);
|
||||
return path ? path : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_host.
|
||||
*/
|
||||
static char* get_host(private_request_t *this)
|
||||
{
|
||||
char *addr = FCGX_GetParam("REMOTE_ADDR", this->req.envp);
|
||||
return addr ? addr : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_user_agent.
|
||||
*/
|
||||
static char* get_user_agent(private_request_t *this)
|
||||
{
|
||||
char *agent = FCGX_GetParam("HTTP_USER_AGENT", this->req.envp);
|
||||
return agent ? agent : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_post_data.
|
||||
*/
|
||||
static char* get_query_data(private_request_t *this, char *name)
|
||||
{
|
||||
return hdf_get_valuef(this->hdf, "Query.%s", name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.add_cookie.
|
||||
*/
|
||||
static void add_cookie(private_request_t *this, char *name, char *value)
|
||||
{
|
||||
pthread_setspecific(this_key, this);
|
||||
cgi_cookie_set (this->cgi, name, value,
|
||||
FCGX_GetParam("SCRIPT_NAME", this->req.envp),
|
||||
NULL, NULL, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.redirect.
|
||||
*/
|
||||
static void redirect(private_request_t *this, char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
FCGX_FPrintF(this->req.out, "Status: 303 See Other\n");
|
||||
FCGX_FPrintF(this->req.out, "Location: %s%s",
|
||||
FCGX_GetParam("SCRIPT_NAME", this->req.envp),
|
||||
*fmt == '/' ? "" : "/");
|
||||
va_start(args, fmt);
|
||||
FCGX_VFPrintF(this->req.out, fmt, args);
|
||||
va_end(args);
|
||||
FCGX_FPrintF(this->req.out, "\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.to_referer.
|
||||
*/
|
||||
static void to_referer(private_request_t *this)
|
||||
{
|
||||
FCGX_FPrintF(this->req.out, "Status: 303 See Other\n");
|
||||
FCGX_FPrintF(this->req.out, "Location: %s\n\n",
|
||||
FCGX_GetParam("HTTP_REFERER", this->req.envp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_base.
|
||||
*/
|
||||
static char* get_base(private_request_t *this)
|
||||
{
|
||||
return FCGX_GetParam("SCRIPT_NAME", this->req.envp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.session_closed.
|
||||
*/
|
||||
static bool session_closed(private_request_t *this)
|
||||
{
|
||||
return this->closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.close_session.
|
||||
*/
|
||||
static void close_session(private_request_t *this)
|
||||
{
|
||||
this->closed = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.serve.
|
||||
*/
|
||||
static void serve(private_request_t *this, char *headers, chunk_t chunk)
|
||||
{
|
||||
FCGX_FPrintF(this->req.out, "%s\n\n", headers);
|
||||
|
||||
FCGX_PutStr(chunk.ptr, chunk.len, this->req.out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.render.
|
||||
*/
|
||||
static void render(private_request_t *this, char *template)
|
||||
{
|
||||
NEOERR* err;
|
||||
|
||||
pthread_setspecific(this_key, this);
|
||||
err = cgi_display(this->cgi, template);
|
||||
if (err)
|
||||
{
|
||||
cgi_neo_error(this->cgi, err);
|
||||
nerr_log_error(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.streamf.
|
||||
*/
|
||||
static int streamf(private_request_t *this, char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
int written;
|
||||
|
||||
va_start(args, format);
|
||||
written = FCGX_VFPrintF(this->req.out, format, args);
|
||||
va_end(args);
|
||||
if (written >= 0 &&
|
||||
FCGX_FFlush(this->req.out) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.set.
|
||||
*/
|
||||
static void set(private_request_t *this, char *key, char *value)
|
||||
{
|
||||
hdf_set_value(this->hdf, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.setf.
|
||||
*/
|
||||
static void setf(private_request_t *this, char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
hdf_set_valuevf(this->hdf, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.get_ref.
|
||||
*/
|
||||
static request_t* get_ref(private_request_t *this)
|
||||
{
|
||||
ref_get(&this->ref);
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of request_t.destroy
|
||||
*/
|
||||
static void destroy(private_request_t *this)
|
||||
{
|
||||
if (ref_put(&this->ref))
|
||||
{
|
||||
pthread_setspecific(this_key, this);
|
||||
cgi_destroy(&this->cgi);
|
||||
FCGX_Finish_r(&this->req);
|
||||
free(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This initialization method is guaranteed to run only once
|
||||
* for all threads.
|
||||
*/
|
||||
static void init(void)
|
||||
{
|
||||
cgiwrap_init_emu(NULL, read_cb, writef_cb, write_cb,
|
||||
getenv_cb, putenv_cb, iterenv_cb);
|
||||
pthread_key_create(&this_key, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* see header file
|
||||
*/
|
||||
request_t *request_create(int fd, bool debug)
|
||||
{
|
||||
NEOERR* err;
|
||||
private_request_t *this = malloc_thing(private_request_t);
|
||||
bool failed = FALSE;
|
||||
|
||||
pthread_cleanup_push(free, this);
|
||||
if (FCGX_InitRequest(&this->req, fd, 0) != 0 ||
|
||||
FCGX_Accept_r(&this->req) != 0)
|
||||
{
|
||||
failed = TRUE;
|
||||
}
|
||||
pthread_cleanup_pop(failed);
|
||||
if (failed)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
this->public.get_path = (char*(*)(request_t*))get_path;
|
||||
this->public.get_base = (char*(*)(request_t*))get_base;
|
||||
this->public.get_host = (char*(*)(request_t*))get_host;
|
||||
this->public.get_user_agent = (char*(*)(request_t*))get_user_agent;
|
||||
this->public.add_cookie = (void(*)(request_t*, char *name, char *value))add_cookie;
|
||||
this->public.get_cookie = (char*(*)(request_t*,char*))get_cookie;
|
||||
this->public.get_query_data = (char*(*)(request_t*, char *name))get_query_data;
|
||||
this->public.session_closed = (bool(*)(request_t*))session_closed;
|
||||
this->public.close_session = (void(*)(request_t*))close_session;
|
||||
this->public.redirect = (void(*)(request_t*, char *fmt,...))redirect;
|
||||
this->public.to_referer = (void(*)(request_t*))to_referer;
|
||||
this->public.render = (void(*)(request_t*,char*))render;
|
||||
this->public.streamf = (int(*)(request_t*, char *format, ...))streamf;
|
||||
this->public.serve = (void(*)(request_t*,char*,chunk_t))serve;
|
||||
this->public.set = (void(*)(request_t*, char *, char*))set;
|
||||
this->public.setf = (void(*)(request_t*, char *format, ...))setf;
|
||||
this->public.get_ref = (request_t*(*)(request_t*))get_ref;
|
||||
this->public.destroy = (void(*)(request_t*))destroy;
|
||||
|
||||
pthread_once(&once, init);
|
||||
pthread_setspecific(this_key, this);
|
||||
|
||||
this->ref = 1;
|
||||
this->closed = FALSE;
|
||||
this->req_env_len = 0;
|
||||
while (this->req.envp[this->req_env_len] != NULL)
|
||||
{
|
||||
this->req_env_len++;
|
||||
}
|
||||
|
||||
err = hdf_init(&this->hdf);
|
||||
if (!err)
|
||||
{
|
||||
hdf_set_value(this->hdf, "base", get_base(this));
|
||||
hdf_set_value(this->hdf, "Config.NoCache", "true");
|
||||
if (!debug)
|
||||
{
|
||||
hdf_set_value(this->hdf, "Config.TimeFooter", "0");
|
||||
hdf_set_value(this->hdf, "Config.CompressionEnabled", "1");
|
||||
hdf_set_value(this->hdf, "Config.WhiteSpaceStrip", "2");
|
||||
}
|
||||
|
||||
err = cgi_init(&this->cgi, this->hdf);
|
||||
if (!err)
|
||||
{
|
||||
err = cgi_parse(this->cgi);
|
||||
if (!err)
|
||||
{
|
||||
return &this->public;
|
||||
}
|
||||
cgi_destroy(&this->cgi);
|
||||
}
|
||||
}
|
||||
nerr_log_error(err);
|
||||
FCGX_Finish_r(&this->req);
|
||||
free(this);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup request request
|
||||
* @{ @ingroup libfast
|
||||
*/
|
||||
|
||||
#ifndef REQUEST_H_
|
||||
#define REQUEST_H_
|
||||
|
||||
#include <fcgiapp.h>
|
||||
#include <library.h>
|
||||
|
||||
typedef struct request_t request_t;
|
||||
|
||||
/**
|
||||
* A HTTP request, encapsulates FCGX_Request.
|
||||
*
|
||||
* The response is also handled through the request object.
|
||||
*/
|
||||
struct request_t {
|
||||
|
||||
/**
|
||||
* Add a cookie to the reply (Set-Cookie header).
|
||||
*
|
||||
* @param name name of the cookie to set
|
||||
* @param value value of the cookie
|
||||
*/
|
||||
void (*add_cookie)(request_t *this, char *name, char *value);
|
||||
|
||||
/**
|
||||
* Get a cookie the client sent in the request.
|
||||
*
|
||||
* @param name name of the cookie
|
||||
* @return cookie value, NULL if no such cookie found
|
||||
*/
|
||||
char* (*get_cookie)(request_t *this, char *name);
|
||||
|
||||
/**
|
||||
* Get the request path relative to the application.
|
||||
*
|
||||
* @return path
|
||||
*/
|
||||
char* (*get_path)(request_t *this);
|
||||
|
||||
/**
|
||||
* Get the base path of the application.
|
||||
*
|
||||
* @return base path
|
||||
*/
|
||||
char* (*get_base)(request_t *this);
|
||||
|
||||
/**
|
||||
* Get the remote host address of this request.
|
||||
*
|
||||
* @return host address as string
|
||||
*/
|
||||
char* (*get_host)(request_t *this);
|
||||
|
||||
/**
|
||||
* Get the user agent string.
|
||||
*
|
||||
* @return user agent string
|
||||
*/
|
||||
char* (*get_user_agent)(request_t *this);
|
||||
|
||||
/**
|
||||
* Get a post/get variable included in the request.
|
||||
*
|
||||
* @param name name of the POST/GET variable
|
||||
* @return value, NULL if not found
|
||||
*/
|
||||
char* (*get_query_data)(request_t *this, char *name);
|
||||
|
||||
/**
|
||||
* Close the session and it's context after handling.
|
||||
*/
|
||||
void (*close_session)(request_t *this);
|
||||
|
||||
/**
|
||||
* Has the session been closed by close_session()?
|
||||
*
|
||||
* @return TRUE if session has been closed
|
||||
*/
|
||||
bool (*session_closed)(request_t *this);
|
||||
|
||||
/**
|
||||
* Redirect the client to another location.
|
||||
*
|
||||
* @param fmt location format string
|
||||
* @param ... variable argument for fmt
|
||||
*/
|
||||
void (*redirect)(request_t *this, char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Redirect the client to the referer.
|
||||
*/
|
||||
void (*to_referer)(request_t *this);
|
||||
|
||||
/**
|
||||
* Set a template value.
|
||||
*
|
||||
* @param key key to set
|
||||
* @param value value to set key to
|
||||
*/
|
||||
void (*set)(request_t *this, char *key, char *value);
|
||||
|
||||
/**
|
||||
* Set a template value using format strings.
|
||||
*
|
||||
* Format string is in the form "key=value", where printf like format
|
||||
* substitution occurs over the whole string.
|
||||
*
|
||||
* @param format printf like format string
|
||||
* @param ... variable argument list
|
||||
*/
|
||||
void (*setf)(request_t *this, char *format, ...);
|
||||
|
||||
/**
|
||||
* Render a template.
|
||||
*
|
||||
* The render() function additionally sets a HDF variable "base"
|
||||
* which points to the root of the web application and allows to point to
|
||||
* other targets without to worry about path location.
|
||||
*
|
||||
* @param template clearsilver template file location
|
||||
*/
|
||||
void (*render)(request_t *this, char *template);
|
||||
|
||||
/**
|
||||
* Stream a format string to the client.
|
||||
*
|
||||
* Stream is not closed and may be called multiple times to allow
|
||||
* server-push functionality.
|
||||
*
|
||||
* @param format printf like format string
|
||||
* @param ... argmuent list to format string
|
||||
* @return number of streamed bytes, < 0 if stream closed
|
||||
*/
|
||||
int (*streamf)(request_t *this, char *format, ...);
|
||||
|
||||
/**
|
||||
* Serve a request with headers and a body.
|
||||
*
|
||||
* @param headers HTTP headers, \n separated
|
||||
* @param chunk body to write to output
|
||||
*/
|
||||
void (*serve)(request_t *this, char *headers, chunk_t chunk);
|
||||
|
||||
/**
|
||||
* Increase the reference count to the stream.
|
||||
*
|
||||
* @return this with increased refcount
|
||||
*/
|
||||
request_t* (*get_ref)(request_t *this);
|
||||
|
||||
/**
|
||||
* Destroy the request_t.
|
||||
*/
|
||||
void (*destroy) (request_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a request from the fastcgi struct.
|
||||
*
|
||||
* @param fd file descripter opened with FCGX_OpenSocket
|
||||
* @param debug no stripping, no compression, timing information
|
||||
*/
|
||||
request_t *request_create(int fd, bool debug);
|
||||
|
||||
#endif /* REQUEST_H_ @} */
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "session.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <fcgiapp.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <utils/linked_list.h>
|
||||
#include <utils/randomizer.h>
|
||||
|
||||
typedef struct private_session_t private_session_t;
|
||||
|
||||
/**
|
||||
* private data of the task manager
|
||||
*/
|
||||
struct private_session_t {
|
||||
|
||||
/**
|
||||
* public functions
|
||||
*/
|
||||
session_t public;
|
||||
|
||||
/**
|
||||
* session ID
|
||||
*/
|
||||
char *sid;
|
||||
|
||||
/**
|
||||
* list of controller instances controller_t
|
||||
*/
|
||||
linked_list_t *controllers;
|
||||
|
||||
/**
|
||||
* list of filter instances filter_t
|
||||
*/
|
||||
linked_list_t *filters;
|
||||
|
||||
/**
|
||||
* user defined session context
|
||||
*/
|
||||
context_t *context;
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of session_t.add_controller.
|
||||
*/
|
||||
static void add_controller(private_session_t *this, controller_t *controller)
|
||||
{
|
||||
this->controllers->insert_last(this->controllers, controller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of session_t.add_filter.
|
||||
*/
|
||||
static void add_filter(private_session_t *this, filter_t *filter)
|
||||
{
|
||||
this->filters->insert_last(this->filters, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a session ID and a cookie
|
||||
*/
|
||||
static void create_sid(private_session_t *this, request_t *request)
|
||||
{
|
||||
char buf[16];
|
||||
chunk_t chunk = chunk_from_buf(buf);
|
||||
randomizer_t *randomizer = randomizer_create();
|
||||
|
||||
randomizer->get_pseudo_random_bytes(randomizer, sizeof(buf), buf);
|
||||
this->sid = chunk_to_hex(chunk, FALSE);
|
||||
request->add_cookie(request, "SID", this->sid);
|
||||
randomizer->destroy(randomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* run all registered filters
|
||||
*/
|
||||
static bool run_filter(private_session_t *this, request_t *request,
|
||||
controller_t *controller)
|
||||
{
|
||||
iterator_t *iterator;
|
||||
filter_t *filter;
|
||||
|
||||
iterator = this->filters->create_iterator(this->filters, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)&filter))
|
||||
{
|
||||
if (!filter->run(filter, request, controller))
|
||||
{
|
||||
iterator->destroy(iterator);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of session_t.process.
|
||||
*/
|
||||
static void process(private_session_t *this, request_t *request)
|
||||
{
|
||||
char *pos, *start, *param[6] = {NULL, NULL, NULL, NULL, NULL, NULL};
|
||||
iterator_t *iterator;
|
||||
bool handled = FALSE;
|
||||
controller_t *current;
|
||||
int i = 0;
|
||||
|
||||
if (this->sid == NULL)
|
||||
{
|
||||
create_sid(this, request);
|
||||
}
|
||||
|
||||
start = request->get_path(request);
|
||||
if (start)
|
||||
{
|
||||
if (*start == '/') start++;
|
||||
while ((pos = strchr(start, '/')) != NULL && i < 5)
|
||||
{
|
||||
param[i++] = strndup(start, pos - start);
|
||||
start = pos + 1;
|
||||
}
|
||||
param[i] = strdup(start);
|
||||
iterator = this->controllers->create_iterator(this->controllers, TRUE);
|
||||
while (iterator->iterate(iterator, (void**)¤t))
|
||||
{
|
||||
if (streq(current->get_name(current), param[0]))
|
||||
{
|
||||
if (run_filter(this, request, current))
|
||||
{
|
||||
current->handle(current, request, param[1], param[2],
|
||||
param[3], param[4], param[5]);
|
||||
handled = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
free(param[i]);
|
||||
}
|
||||
}
|
||||
if (!handled)
|
||||
{
|
||||
if (this->controllers->get_first(this->controllers,
|
||||
(void**)¤t) == SUCCESS)
|
||||
{
|
||||
request->redirect(request, current->get_name(current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of session_t.get_sid.
|
||||
*/
|
||||
static char* get_sid(private_session_t *this)
|
||||
{
|
||||
return this->sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of session_t.destroy
|
||||
*/
|
||||
static void destroy(private_session_t *this)
|
||||
{
|
||||
this->controllers->destroy_offset(this->controllers, offsetof(controller_t, destroy));
|
||||
this->filters->destroy_offset(this->filters, offsetof(filter_t, destroy));
|
||||
if (this->context) this->context->destroy(this->context);
|
||||
free(this->sid);
|
||||
free(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* see header file
|
||||
*/
|
||||
session_t *session_create(context_t *context)
|
||||
{
|
||||
private_session_t *this = malloc_thing(private_session_t);
|
||||
|
||||
this->public.add_controller = (void(*)(session_t*, controller_t*))add_controller;
|
||||
this->public.add_filter = (void(*)(session_t*, filter_t*))add_filter;
|
||||
this->public.process = (void(*)(session_t*,request_t*))process;
|
||||
this->public.get_sid = (char*(*)(session_t*))get_sid;
|
||||
this->public.destroy = (void(*)(session_t*))destroy;
|
||||
|
||||
this->sid = NULL;
|
||||
this->controllers = linked_list_create();
|
||||
this->filters = linked_list_create();
|
||||
this->context = context;
|
||||
|
||||
return &this->public;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Martin Willi
|
||||
* Hochschule fuer Technik Rapperswil
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup session session
|
||||
* @{ @ingroup libfast
|
||||
*/
|
||||
|
||||
#ifndef SESSION_H_
|
||||
#define SESSION_H_
|
||||
|
||||
#include "request.h"
|
||||
#include "controller.h"
|
||||
#include "filter.h"
|
||||
|
||||
typedef struct session_t session_t;
|
||||
|
||||
/**
|
||||
* Session handling class, instanciated for each user session.
|
||||
*/
|
||||
struct session_t {
|
||||
|
||||
/**
|
||||
* Get the session ID of the session.
|
||||
*
|
||||
* @return session ID
|
||||
*/
|
||||
char* (*get_sid)(session_t *this);
|
||||
|
||||
/**
|
||||
* Add a controller instance to the session.
|
||||
*
|
||||
* @param controller controller to add
|
||||
*/
|
||||
void (*add_controller)(session_t *this, controller_t *controller);
|
||||
|
||||
/**
|
||||
* @brief Add a filter instance to the session.
|
||||
*
|
||||
* @param filter filter to add
|
||||
*/
|
||||
void (*add_filter)(session_t *this, filter_t *filter);
|
||||
|
||||
/**
|
||||
* Process a request in this session.
|
||||
*
|
||||
* @param request request to process
|
||||
*/
|
||||
void (*process)(session_t *this, request_t *request);
|
||||
|
||||
/**
|
||||
* Destroy the session_t.
|
||||
*
|
||||
* @param this calling object
|
||||
*/
|
||||
void (*destroy) (session_t *this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a session new session.
|
||||
*
|
||||
* @param context user defined session context instance
|
||||
*/
|
||||
session_t *session_create(context_t *context);
|
||||
|
||||
#endif /* SESSION_H_ @} */
|
||||
Reference in New Issue
Block a user