first revision of new manager webapp

This commit is contained in:
Martin Willi
2007-09-11 15:22:02 +00:00
parent f0c156fbc9
commit 965e99b5bf
36 changed files with 3517 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
/**
* @file context.h
*
* @brief Interface of context_t.
*
*/
/*
* 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.
*/
#ifndef CONTEXT_H_
#define CONTEXT_H_
typedef struct context_t context_t;
/**
* @brief Constructor function for a context
*/
typedef context_t *(*context_constructor_t)(void *param);
/**
* @brief Custom session context
*
*/
struct context_t {
/**
* @brief Destroy the context_t.
*
* @param this calling object
*/
void (*destroy) (context_t *this);
};
#endif /* CONTEXT_H_ */
+75
View File
@@ -0,0 +1,75 @@
/**
* @file controller.h
*
* @brief Interface controller_t.
*
*/
/*
* 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.
*/
#ifndef CONTROLLER_H_
#define CONTROLLER_H_
#include "request.h"
#include "response.h"
#include "context.h"
typedef struct controller_t controller_t;
/**
* @brief Controller action handle function
*
* @param request http request
* @param response http response
*/
typedef void *(*controller_handler_t)(controller_t *this, request_t *request, response_t *response);
/**
* @brief Constructor function for a controller
*
* @param context session specific context
* @param param user supplied param
*/
typedef controller_t *(*controller_constructor_t)(context_t* context, void *param);
/**
* @brief Controller interface, to be implemented by users controllers.
*
*/
struct controller_t {
/**
* @brief Get the name of the controller.
*
* @return name of the controller
*/
char* (*get_name)(controller_t *this);
/**
* @brief Get the controllers handler function for an action name.
*
* @param name name of the action
* @return controllers handler
*/
controller_handler_t (*get_handler)(controller_t *this, char *name);
/**
* @brief Destroy the controller instance.
*/
void (*destroy) (controller_t *this);
};
#endif /* CONTROLLER_H_ */
+348
View File
@@ -0,0 +1,348 @@
/**
* @file dispatcher.c
*
* @brief Implementation of dispatcher_t.
*
*/
/*
* 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.
*/
#include "dispatcher.h"
#include "request.h"
#include "session.h"
#include <fcgiapp.h>
#include <pthread.h>
#include <signal.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;
/**
* List of controllers controller_constructor_t
*/
linked_list_t *controllers;
/**
* constructor function to create session context (in constructor_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;
} constructor_entry_t;
typedef struct {
/** session instance */
session_t *session;
/** condvar to wait for session */
pthread_cond_t cond;
/** number of threads waiting for session */
int waiting;
/** last use of the session */
time_t used;
} session_entry_t;
/**
* create a session and instanciate controllers
*/
static session_t* load_session(private_dispatcher_t *this)
{
iterator_t *iterator;
constructor_entry_t *entry;
session_t *session;
context_t *context = NULL;
controller_t *controller;
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**)&entry))
{
controller = entry->constructor(context, entry->param);
session->add_controller(session, controller);
}
iterator->destroy(iterator);
return session;
}
/**
* create a new session entry
*/
static session_entry_t *session_entry_create(private_dispatcher_t *this)
{
session_entry_t *entry;
entry = malloc_thing(session_entry_t);
entry->waiting = 1;
pthread_cond_init(&entry->cond, NULL);
entry->session = load_session(this);
return entry;
}
static void session_entry_destroy(session_entry_t *entry)
{
entry->session->destroy(entry->session);
free(entry);
}
/**
* Implementation of dispatcher_t.add_controller.
*/
static void add_controller(private_dispatcher_t *this,
controller_constructor_t constructor, void *param)
{
constructor_entry_t *entry = malloc_thing(constructor_entry_t);
entry->constructor = constructor;
entry->param = param;
this->controllers->insert_last(this->controllers, entry);
}
/**
* Dispatch
*/
static void dispatch(private_dispatcher_t *this)
{
FCGX_Request fcgi_req;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (FCGX_InitRequest(&fcgi_req, this->fd, 0) == 0)
{
while (TRUE)
{
request_t *request;
response_t *response;
session_entry_t *current, *found = NULL;
iterator_t *iterator;
time_t now;
char *sid;
int accepted;
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
accepted = FCGX_Accept_r(&fcgi_req);
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
if (accepted != 0)
{
break;
}
/* prepare */
response = response_create(&fcgi_req);
request = request_create(&fcgi_req);
sid = request->get_cookie(request, "SID");
now = time(NULL);
/* find session */
iterator = this->sessions->create_iterator_locked(this->sessions, &this->mutex);
while (iterator->iterate(iterator, (void**)&current))
{
if (sid && streq(current->session->get_sid(current->session), sid))
{
found = current;
found->waiting++;
}
else if (current->waiting == 0 &&
current->used + this->timeout > now)
{
iterator->remove(iterator);
session_entry_destroy(current);
}
}
iterator->destroy(iterator);
if (found)
{ /* wait until session is unused */
pthread_mutex_lock(&this->mutex);
while (found->waiting > 1)
{
pthread_cond_wait(&found->cond, &this->mutex);
}
pthread_mutex_unlock(&this->mutex);
}
else
{ /* create a new session if not found */
found = session_entry_create(this);
pthread_mutex_lock(&this->mutex);
this->sessions->insert_first(this->sessions, found);
pthread_mutex_unlock(&this->mutex);
}
/* start processing */
found->session->process(found->session, request, response);
found->used = time(NULL);
/* release session */
pthread_mutex_lock(&this->mutex);
found->waiting--;
pthread_cond_signal(&found->cond);
pthread_mutex_unlock(&this->mutex);
/* cleanup */
request->destroy(request);
response->destroy(response);
/*
FCGX_FPrintF(fcgi_req.out, "<ul>");
char **env = fcgi_req.envp;
while (*env)
{
FCGX_FPrintF(fcgi_req.out, "<li>%s</li>", *env);
env++;
}
FCGX_FPrintF(fcgi_req.out, "</ul>");
*/
}
}
}
/**
* 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);
free(this);
}
/*
* see header file
*/
dispatcher_t *dispatcher_create(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.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->context_constructor = constructor;
pthread_mutex_init(&this->mutex, NULL);
this->param = param;
this->fd = 0;
this->timeout = 180;
FCGX_Init();
#ifdef FCGI_SOCKET
unlink(FCGI_SOCKET);
this->fd = FCGX_OpenSocket(FCGI_SOCKET, 10);
#endif /* FCGI_SOCKET */
return &this->public;
}
+78
View File
@@ -0,0 +1,78 @@
/**
* @file dispatcher.h
*
* @brief Interface of dispatcher_t.
*
*/
/*
* 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.
*/
#ifndef DISPATCHER_H_
#define DISPATCHER_H_
#include "controller.h"
typedef struct dispatcher_t dispatcher_t;
/**
* @brief 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 {
/**
* @brief Register a controller to the dispatcher.
*
* @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 Start with dispatching.
*
* @param thread number of dispatching threads
*/
void (*run)(dispatcher_t *this, int threads);
/**
* @brief Wait for a relevant signal action.
*/
void (*waitsignal)(dispatcher_t *this);
/**
* @brief Destroy the dispatcher_t.
*/
void (*destroy) (dispatcher_t *this);
};
/**
* @brief Create a dispatcher.
*
* The context constructor is invoked to create a session context for
* each session.
*
* @param constructor construction function for session context
* @param param parameter to supply to context constructor
*/
dispatcher_t *dispatcher_create(context_constructor_t constructor, void *param);
#endif /* DISPATCHER_H_ */
+49
View File
@@ -0,0 +1,49 @@
/**
* @file enumerator.h
*
* @brief Interface of enumerator_t.
*
*/
/*
* 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.
*/
#ifndef ENUMERATOR_H_
#define ENUMERATOR_H_
#include <library.h>
typedef struct enumerator_t enumerator_t;
/**
* @brief Enumerate is simpler, but more flexible than iterator.
*/
struct enumerator_t {
/**
* @brief Enumerate collection.
*
* @param ... variable argument list of pointers, NULL terminated
* @return TRUE if pointers returned
*/
bool (*enumerate)(enumerator_t *this, ...);
/**
* @brief Destroy a enumerator instance.
*/
void (*destroy)(enumerator_t *this);
};
#endif /* ENUMERATOR_H_ */
+306
View File
@@ -0,0 +1,306 @@
/**
* @file request.c
*
* @brief Implementation of request_t.
*
*/
/*
* 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.
*/
#define _GNU_SOURCE
#include "request.h"
#include <stdlib.h>
#include <utils/linked_list.h>
typedef struct {
char *name;
char *value;
} name_value_t;
/**
* destroy a name value pair
*/
static void name_value_destroy(name_value_t *this)
{
free(this->name);
free(this->value);
free(this);
}
typedef struct private_request_t private_request_t;
/**
* private data of the task manager
*/
struct private_request_t {
/**
* public functions
*/
request_t public;
/**
* the associated fcgi request
*/
FCGX_Request *req;
/**
* list of cookies (name_value_t)
*/
linked_list_t *cookies;
/**
* list of post data (name_value_t)
*/
linked_list_t *posts;
};
/**
* Implementation of request_t.get_cookie.
*/
static char* get_cookie(private_request_t *this, char *name)
{
char *value = NULL;
name_value_t *cookie;
iterator_t *iterator;
iterator = this->cookies->create_iterator(this->cookies, TRUE);
while (iterator->iterate(iterator, (void**)&cookie))
{
if (streq(cookie->name, name))
{
value = cookie->value;
break;
}
}
iterator->destroy(iterator);
return value;
}
/**
* 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_post_data.
*/
static char* get_post_data(private_request_t *this, char *name)
{
char *value = NULL;
name_value_t *data;
iterator_t *iterator;
iterator = this->posts->create_iterator(this->posts, TRUE);
while (iterator->iterate(iterator, (void**)&data))
{
if (streq(data->name, name))
{
value = data->value;
break;
}
}
iterator->destroy(iterator);
return value;
}
/**
* convert 2 digit hex string to a integer
*/
static char hex2char(char *hex)
{
static char hexdig[] = "00112233445566778899AaBbCcDdEeFf";
return (strchr(hexdig, hex[1]) - hexdig)/2 +
((strchr(hexdig, hex[0]) - hexdig)/2 * 16);
}
/**
* unescape a string up to the delimiter, and return a clone
*/
static char *unescape(char **pos, char delimiter)
{
char *ptr, *res, *end, code[3] = {'\0','\0','\0'};
if (**pos == '\0')
{
return NULL;
}
ptr = strchr(*pos, delimiter);
if (ptr)
{
res = strndup(*pos, ptr - *pos);
*pos = ptr + 1;
}
else
{
res = strdup(*pos);
*pos = "";
}
end = res + strlen(res) + 1;
/* replace '+' with ' ' */
ptr = res;
while ((ptr = strchr(ptr, '+')))
{
*ptr = ' ';
}
/* replace %HH with its ascii value */
ptr = res;
while ((ptr = strchr(ptr, '%')))
{
if (ptr > end - 2)
{
break;
}
strncpy(code, ptr + 1, 2);
*ptr = hex2char(code);
memmove(ptr + 1, ptr + 3, end - (ptr + 3));
}
return res;
}
/**
* parse the http POST data
*/
static void parse_post(private_request_t *this)
{
char buf[4096], *pos, *name, *value;
name_value_t *data;
int len;
if (!streq(FCGX_GetParam("REQUEST_METHOD", this->req->envp), "POST") ||
!streq(FCGX_GetParam("CONTENT_TYPE", this->req->envp),
"application/x-www-form-urlencoded"))
{
return;
}
len = FCGX_GetStr(buf, sizeof(buf) - 1, this->req->in);
if (len != atoi(FCGX_GetParam("CONTENT_LENGTH", this->req->envp)))
{
return;
}
buf[len] = 0;
pos = buf;
while (TRUE)
{
name = unescape(&pos, '=');
if (name)
{
value = unescape(&pos, '&');
if (value)
{
data = malloc_thing(name_value_t);
data->name = name;
data->value = value;
this->posts->insert_last(this->posts, data);
continue;
}
else
{
free(name);
}
}
break;
}
}
/**
* parse the requests cookies
*/
static void parse_cookies(private_request_t *this)
{
char *str, *pos;
name_value_t *cookie;
str = FCGX_GetParam("HTTP_COOKIE", this->req->envp);
while (str)
{
if (*str == ' ')
{
str++;
continue;
}
pos = strchr(str, '=');
if (pos == NULL)
{
break;
}
cookie = malloc_thing(name_value_t);
cookie->name = strndup(str, pos - str);
cookie->value = NULL;
str = pos + 1;
if (str)
{
pos = strchr(str, ';');
if (pos)
{
cookie->value = strndup(str, pos - str);
}
else
{
cookie->value = strdup(str);
}
}
this->cookies->insert_last(this->cookies, cookie);
if (pos == NULL)
{
break;
}
str = pos + 1;
}
}
/**
* Implementation of request_t.destroy
*/
static void destroy(private_request_t *this)
{
this->cookies->destroy_function(this->cookies, (void*)name_value_destroy);
this->posts->destroy_function(this->posts, (void*)name_value_destroy);
free(this);
}
/*
* see header file
*/
request_t *request_create(FCGX_Request *request)
{
private_request_t *this = malloc_thing(private_request_t);
this->public.get_path = (char*(*)(request_t*))get_path;
this->public.get_cookie = (char*(*)(request_t*,char*))get_cookie;
this->public.get_post_data = (char*(*)(request_t*, char *name))get_post_data;
this->public.destroy = (void(*)(request_t*))destroy;
this->req = request;
this->cookies = linked_list_create();
this->posts = linked_list_create();
parse_cookies(this);
parse_post(this);
return &this->public;
}
+73
View File
@@ -0,0 +1,73 @@
/**
* @file request.h
*
* @brief Interface of request_t.
*
*/
/*
* 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.
*/
#ifndef REQUEST_H_
#define REQUEST_H_
#include <fcgiapp.h>
typedef struct request_t request_t;
/**
* @brief A HTTP request, encapsulates FCGX_Request.
*
*/
struct request_t {
/**
* @brief 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);
/**
* @brief Get the request path relative to the application.
*
* @return path
*/
char* (*get_path)(request_t *this);
/**
* @brief Get a post variable included in the request.
*
* @param name name of the POST variable
* @return value, NULL if not found
*/
char* (*get_post_data)(request_t *this, char *name);
/**
* @brief Destroy the request_t.
*/
void (*destroy) (request_t *this);
};
/**
* @brief Create a request from the fastcgi struct.
*
* @param request the FCGI request
*/
request_t *request_create(FCGX_Request *request);
#endif /* REQUEST_H_ */
+223
View File
@@ -0,0 +1,223 @@
/**
* @file response.c
*
* @brief Implementation of response_t.
*
*/
/*
* 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.
*/
#include "response.h"
#include <stdlib.h>
#include <stdarg.h>
#include <utils/linked_list.h>
typedef struct {
char *name;
char *value;
} name_value_t;
/**
* create name value pair
*/
static name_value_t *name_value_create(char *name, char *value)
{
name_value_t *this = malloc_thing(name_value_t);
this->name = strdup(name);
this->value = strdup(value);
return this;
}
/**
* destroy a name value pair
*/
static void name_value_destroy(name_value_t *this)
{
free(this->name);
free(this->value);
free(this);
}
typedef struct private_response_t private_response_t;
/**
* private data of the task manager
*/
struct private_response_t {
/**
* public functions
*/
response_t public;
/**
* the associated fcgi request
*/
FCGX_Request *req;
/**
* Content type
*/
char *content_type;
/**
* list of cookies (name_value_t)
*/
linked_list_t *cookies;
/**
* list of custom headers (name_value_t)
*/
linked_list_t *headers;
/**
* headers already written?
*/
bool started;
};
/**
* write the headers, if not already written
*/
static void write_headers(private_response_t *this)
{
iterator_t *iterator;
name_value_t *current;
FCGX_FPrintF(this->req->out, "Content-type: %s\n", this->content_type);
iterator = this->cookies->create_iterator(this->cookies, TRUE);
while (iterator->iterate(iterator, (void**)&current))
{
FCGX_FPrintF(this->req->out, "Set-Cookie: %s=%s; path=%s\n",
current->name, current->value,
FCGX_GetParam("SCRIPT_NAME", this->req->envp));
}
iterator->destroy(iterator);
iterator = this->cookies->create_iterator(this->headers, TRUE);
while (iterator->iterate(iterator, (void**)&current))
{
FCGX_FPrintF(this->req->out, "%s: %s\n",
current->name, current->value);
}
iterator->destroy(iterator);
FCGX_PutChar('\n', this->req->out);
this->started = TRUE;
}
/**
* Implementation of response_t.print.
*/
static void print_(private_response_t *this, char *str)
{
if (!this->started)
{
write_headers(this);
}
FCGX_PutS(str, this->req->out);
}
/**
* Implementation of response_t.printf.
*/
static void printf_(private_response_t *this, char *format, ...)
{
va_list args;
if (!this->started)
{
write_headers(this);
}
va_start(args, format);
FCGX_VFPrintF(this->req->out, format, args);
va_end(args);
}
/**
* Implementation of response_t.add_header.
*/
static void add_header(private_response_t *this, char *name, char *value)
{
this->headers->insert_last(this->headers, name_value_create(name, value));
}
/**
* Implementation of response_t.set_content_type.
*/
static void set_content_type(private_response_t *this, char *type)
{
free(this->content_type);
this->content_type = strdup(type);
}
/**
* Implementation of response_t.add_cookie.
*/
static void add_cookie(private_response_t *this, char *name, char *value)
{
this->cookies->insert_last(this->cookies, name_value_create(name, value));
}
/**
* Implementation of response_t.redirect.
*/
static void redirect(private_response_t *this, char *location)
{
FCGX_FPrintF(this->req->out, "Status: 303 See Other\n");
FCGX_FPrintF(this->req->out, "Location: %s%s%s\n\n",
FCGX_GetParam("SCRIPT_NAME", this->req->envp),
*location == '/' ? "" : "/", location);
}
/**
* Implementation of response_t.destroy
*/
static void destroy(private_response_t *this)
{
this->headers->destroy_function(this->headers, (void*)name_value_destroy);
this->cookies->destroy_function(this->cookies, (void*)name_value_destroy);
free(this->content_type);
free(this);
}
/*
* see header file
*/
response_t *response_create(FCGX_Request *request)
{
private_response_t *this = malloc_thing(private_response_t);
this->public.print = (void(*)(response_t*, char *str))print_;
this->public.printf = (void(*)(response_t*, char *format, ...))printf_;
this->public.add_header = (void(*)(response_t*, char *name, char *value))add_header;
this->public.set_content_type = (void(*)(response_t*, char *type))set_content_type;
this->public.add_cookie = (void(*)(response_t*, char *name, char *value))add_cookie;
this->public.redirect = (void(*)(response_t*, char *location))redirect;
this->public.destroy = (void(*)(response_t*))destroy;
this->req = request;
this->headers = linked_list_create();
this->cookies = linked_list_create();
this->content_type = strdup("text/html");
this->started = FALSE;
return &this->public;
}
+95
View File
@@ -0,0 +1,95 @@
/**
* @file response.h
*
* @brief Interface of response_t.
*
*/
/*
* 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.
*/
#ifndef RESPONSE_H_
#define RESPONSE_H_
#include <fcgiapp.h>
typedef struct response_t response_t;
/**
* @brief A HTTP response, wraps response functionality around FCGX_Request.
*
*/
struct response_t {
/**
* @brief Write a string to the client.
*
* @param str string to write
*/
void (*print)(response_t *this, char *str);
/**
* @brief Write a printf like format string to client.
*
* @param format printf like format string
* @param ... variable argument list
*/
void (*printf)(response_t *this, char *format, ...);
/**
* @brief Add a custom header to the response.
*
* @param name name of the header
* @param value value of the header
*/
void (*add_header)(response_t *this, char *name, char *value);
/**
* @brief Set the content type (Content-Type header).
*
* @param type content type (e.g. text/html)
*/
void (*set_content_type)(response_t *this, char *type);
/**
* @brief Add a cookie to the response (Set-Cookie header).
*
* @param name name of the cookie to set
* @param value value of the cookie
*/
void (*add_cookie)(response_t *this, char *name, char *value);
/**
* @brief Redirect the client to another location.
*
* @param location location to redirect to
*/
void (*redirect)(response_t *this, char *location);
/**
* @brief Destroy a response_t.
*/
void (*destroy) (response_t *this);
};
/**
* @brief Create a response.
*
* @param request the FCGI request structure
*/
response_t *response_create(FCGX_Request *request);
#endif /* RESPONSE_H_ */
+185
View File
@@ -0,0 +1,185 @@
/**
* @file session.c
*
* @brief Implementation of session_t.
*
*/
/*
* 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.
*/
#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;
/**
* user defined session context
*/
context_t *context;
};
/**
* Implementation of session_t.load_controller.
*/
static void add_controller(private_session_t *this, controller_t *controller)
{
this->controllers->insert_last(this->controllers, controller);
}
/**
* Create a session ID and a cookie
*/
static void create_sid(private_session_t *this, response_t *response)
{
char buf[16];
chunk_t chunk = chunk_from_buf(buf);
randomizer_t *randomizer = randomizer_create();
randomizer->get_pseudo_random_bytes(randomizer, sizeof(buf), buf);
asprintf(&this->sid, "%#B", &chunk);
response->add_cookie(response, "SID", this->sid);
randomizer->destroy(randomizer);
}
/**
* Implementation of session_t.process.
*/
static void process(private_session_t *this,
request_t *request, response_t *response)
{
char *pos, *path, *controller, *action;
iterator_t *iterator;
bool handled = FALSE;
controller_handler_t handler;
controller_t *current;
if (this->sid == NULL)
{
create_sid(this, response);
}
path = request->get_path(request);
if (*path == '/') path++;
pos = strchr(path, '/');
if (pos == NULL)
{
controller = strdup(path);
action = strdup("");
}
else
{
controller = strndup(path, pos - path);
path = pos + 1;
pos = strchr(path, '/');
if (pos == NULL)
{
action = strdup(path);
}
else
{
action = strndup(path, pos - path);
}
}
iterator = this->controllers->create_iterator(this->controllers, TRUE);
while (iterator->iterate(iterator, (void**)&current))
{
if (streq(current->get_name(current), controller))
{
handler = current->get_handler(current, action);
if (handler)
{
handler(current, request, response);
handled = TRUE;
}
break;
}
}
iterator->destroy(iterator);
free(controller);
free(action);
if (!handled)
{
response->add_header(response, "Status", "400 Not Found");
response->printf(response, "<html><body><h1>Not Found</h1></body></html>\n");
}
}
/**
* 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));
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.process = (void(*)(session_t*, request_t*,response_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->context = context;
return &this->public;
}
+75
View File
@@ -0,0 +1,75 @@
/**
* @file session.h
*
* @brief Interface of session_t.
*
*/
/*
* 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.
*/
#ifndef SESSION_H_
#define SESSION_H_
#include "request.h"
#include "response.h"
#include "controller.h"
typedef struct session_t session_t;
/**
* @brief A session, identified by a session ID.
*
*/
struct session_t {
/**
* @brief Get the session ID of the session.
*
* @return session ID
*/
char* (*get_sid)(session_t *this);
/**
* @brief Add a controller instance to the session.
*
* @param controller controller to add
*/
void (*add_controller)(session_t *this, controller_t *controller);
/**
* @brief Process a request in this session.
*
* @param request request to process
* @param response response to send
*/
void (*process)(session_t *this, request_t *request, response_t *response);
/**
* @brief Destroy the session_t.
*
* @param this calling object
*/
void (*destroy) (session_t *this);
};
/**
* @brief Create a session.
*
* @param context user defined session context instance
*/
session_t *session_create(context_t *context);
#endif /* SESSION_H_ */
+138
View File
@@ -0,0 +1,138 @@
/**
* @file template.c
*
* @brief Implementation of template_t.
*
*/
/*
* 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.
*/
#include "template.h"
#include <ClearSilver/ClearSilver.h>
#include <library.h>
typedef struct private_template_t private_template_t;
/**
* private data of the task manager
*/
struct private_template_t {
/**
* public functions
*/
template_t public;
/**
* template file
*/
char *file;
/**
* clearsilver HDF dataset
*/
HDF *hdf;
};
/**
* clearsilver cs_render callback function
*/
static NEOERR* render_cb(response_t *response, char *str)
{
response->print(response, str);
return NULL;
}
/**
* Implementation of template_t.render.
*/
static void render(private_template_t *this, response_t *response)
{
NEOERR* err;
CSPARSE *parse;
hdf_remove_tree(this->hdf, "");
err = cs_init(&parse, this->hdf);
if (!err)
{
err = cs_parse_file(parse, this->file);
if (!err)
{
err = cs_render(parse, response, (CSOUTFUNC)render_cb);
if (!err)
{
cs_destroy(&parse);
return;
}
}
cs_destroy(&parse);
}
nerr_log_error(err);
return;
}
/**
* Implementation of template_t.set.
*/
static void set(private_template_t *this, char *key, char *value)
{
hdf_set_value(this->hdf, key, value);
}
/**
* Implementation of template_t.setf.
*/
static void setf(private_template_t *this, char *format, ...)
{
va_list args;
va_start(args, format);
hdf_set_valuevf(this->hdf, format, args);
va_end(args);
}
/**
* Implementation of template_t.destroy
*/
static void destroy(private_template_t *this)
{
hdf_destroy(&this->hdf);
free(this->file);
free(this);
}
/*
* see header file
*/
template_t *template_create(char *file)
{
private_template_t *this = malloc_thing(private_template_t);
this->public.render = (void(*)(template_t*,response_t*))render;
this->public.set = (void(*)(template_t*, char *, char*))set;
this->public.setf = (void(*)(template_t*, char *format, ...))setf;
this->public.destroy = (void(*)(template_t*))destroy;
this->file = strdup(file);
this->hdf = NULL;
hdf_init(&this->hdf);
return &this->public;
}
+76
View File
@@ -0,0 +1,76 @@
/**
* @file template.h
*
* @brief Interface of template_t.
*
*/
/*
* 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.
*/
#ifndef TEMPLATE_H_
#define TEMPLATE_H_
#include "response.h"
typedef struct template_t template_t;
/**
* @brief Template engine based on ClearSilver.
*
*/
struct template_t {
/**
* @brief Set a template value.
*
* @param key key to set
* @param value value to set key to
*/
void (*set)(template_t *this, char *key, char *value);
/**
* @brief 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)(template_t *this, char *format, ...);
/**
* @brief Render a template to a response object.
*
* @param response response to render to
* @return rendered template string
*/
void (*render)(template_t *this, response_t *response);
/**
* @brief Destroy the template_t.
*/
void (*destroy) (template_t *this);
};
/**
* @brief Create a template from a file.
*
* @param file template file
*/
template_t *template_create(char *file);
#endif /* TEMPLATE_H_ */