merged the modularization branch (credentials) back to trunk

This commit is contained in:
Martin Willi
2008-03-13 14:14:44 +00:00
parent 2df655134c
commit 552cc11b1f
495 changed files with 30378 additions and 23843 deletions
+8
View File
@@ -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
+44
View File
@@ -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_ @}*/
+79
View File
@@ -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_ @} */
+383
View File
@@ -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**)&centry))
{
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**)&current))
{
/* 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;
}
+139
View File
@@ -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_ @} */
+60
View File
@@ -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_ @} */
+447
View File
@@ -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;
}
+186
View File
@@ -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_ @} */
+211
View File
@@ -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**)&current))
{
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**)&current) == 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;
}
+80
View File
@@ -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_ @} */