summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2015-11-21 14:51:17 +0100
committerJörg Frings-Fürst <debian@jff-webhosting.net>2015-11-21 14:51:17 +0100
commitbb9bc9051629c3319c56785c2f4ae0e605d76329 (patch)
treeec393eb145e5a7d43909bdfc43cdeaa28c4e434a /src
Initial import of bitz-server version 0.1.6-1
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am32
-rw-r--r--src/bitz-server.cpp472
-rw-r--r--src/bitz-server.h113
-rw-r--r--src/bitz/common.h40
-rw-r--r--src/bitz/config.cpp190
-rw-r--r--src/bitz/config.h95
-rw-r--r--src/bitz/exception.cpp40
-rw-r--r--src/bitz/exception.h43
-rw-r--r--src/bitz/logger.cpp98
-rw-r--r--src/bitz/logger.h62
-rw-r--r--src/bitz/manager.cpp223
-rw-r--r--src/bitz/manager.h87
-rw-r--r--src/bitz/manager_exception.cpp32
-rw-r--r--src/bitz/manager_exception.h36
-rw-r--r--src/bitz/modifier.cpp29
-rw-r--r--src/bitz/modifier.h70
-rw-r--r--src/bitz/options_request_handler.cpp57
-rw-r--r--src/bitz/options_request_handler.h51
-rw-r--r--src/bitz/reqmod_request_handler.cpp30
-rw-r--r--src/bitz/reqmod_request_handler.h41
-rw-r--r--src/bitz/request_handler.cpp401
-rw-r--r--src/bitz/request_handler.h133
-rw-r--r--src/bitz/respmod_request_handler.cpp30
-rw-r--r--src/bitz/respmod_request_handler.h41
-rw-r--r--src/bitz/util.cpp122
-rw-r--r--src/bitz/util.h92
-rw-r--r--src/bitz/worker.cpp138
-rw-r--r--src/bitz/worker.h47
-rw-r--r--src/main.cpp82
29 files changed, 2927 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..201a051
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,32 @@
+## src/
+AUTOMAKE_OPTIONS = subdir-objects
+AM_CPPFLAGS = -I$(top_srcdir)/lib ${libconfig_CFLAGS} ${log4cpp_CFLAGS}
+
+bitzincludedir = $(pkgincludedir)
+sbin_PROGRAMS = bitz-server
+bitz_server_LDFLAGS = -ldl -export-dynamic
+bitz_server_LDADD = $(top_builddir)/lib/socket/libsocket.la \
+ $(top_builddir)/lib/icap/libicap.la \
+ ${libconfig_LIBS} ${log4cpp_LIBS}
+
+bitz_server_SOURCES = main.cpp \
+ bitz-server.h bitz-server.cpp \
+ bitz/exception.h bitz/exception.cpp \
+ bitz/manager_exception.h bitz/manager_exception.cpp \
+ bitz/config.h bitz/config.cpp \
+ bitz/logger.h bitz/logger.cpp \
+ bitz/common.h \
+ bitz/util.h bitz/util.cpp \
+ bitz/manager.h bitz/manager.cpp \
+ bitz/worker.h bitz/worker.cpp \
+ bitz/request_handler.h bitz/request_handler.cpp \
+ bitz/options_request_handler.h bitz/options_request_handler.cpp \
+ bitz/reqmod_request_handler.h bitz/reqmod_request_handler.cpp \
+ bitz/respmod_request_handler.h bitz/respmod_request_handler.cpp \
+ bitz/modifier.cpp
+
+bitzinclude_HEADERS = \
+ bitz/config.h \
+ bitz/logger.h \
+ bitz/modifier.h
+
diff --git a/src/bitz-server.cpp b/src/bitz-server.cpp
new file mode 100644
index 0000000..c444762
--- /dev/null
+++ b/src/bitz-server.cpp
@@ -0,0 +1,472 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <iostream>
+#include <string>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <fcntl.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <config.h>
+#include "bitz-server.h"
+
+namespace bitz {
+
+ namespace server {
+
+ static server_t globals;
+
+ void init() {
+
+ // initialise defaults
+ globals.pid_handle = -1;
+ globals.manager = NULL;
+ globals.terminating = 0;
+ globals.daemon = false;
+
+ // logger (syslog)
+ setlogmask( LOG_UPTO( LOG_INFO ) );
+ openlog( PACKAGE_NAME, LOG_CONS, LOG_USER );
+
+ // signal handlers
+ init_signal_handlers();
+
+ }
+
+
+ void init_signal_handlers() {
+
+ sigset_t ignore_mask;
+
+ // set signal mask - signals we want to block
+ sigemptyset(&ignore_mask);
+ sigaddset(&ignore_mask, SIGTSTP); // ignore Tty stop signals
+ sigaddset(&ignore_mask, SIGTTOU); // ignore Tty background writes
+ sigaddset(&ignore_mask, SIGTTIN); // ignore Tty background reads
+ sigprocmask(SIG_BLOCK, &ignore_mask, NULL); // block the above specified signals
+
+ init_sigchld_handler();
+ init_sigterm_handler();
+ init_sigquit_handler();
+ init_sigint_handler();
+
+ }
+
+
+ void init_sigchld_handler() {
+
+ struct sigaction sa;
+
+ // block all other signals
+ sigfillset( &sa.sa_mask );
+
+ // signal handler proc
+ sa.sa_sigaction = &sigchld_handler;
+
+ // flags
+ sa.sa_flags = SA_SIGINFO;
+
+ if ( sigaction( SIGCHLD, &sa, NULL ) < 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ }
+
+
+ void sigchld_handler( int sig, siginfo_t *siginfo, void *context ) {
+
+ pid_t worker_pid;
+ int status;
+
+ std::cout << "[" << getpid() << "] inside zombie deleter: ";
+ while ( ( worker_pid = waitpid( WAIT_ANY, &status, WNOHANG ) ) > 0 ) {
+ std::cout << "child " << worker_pid << " terminated with status " << status << std::endl;
+
+ if ( globals.manager != NULL ) {
+ globals.manager->reap_worker( worker_pid );
+ }
+ }
+
+ }
+
+
+ void init_sigterm_handler() {
+
+ struct sigaction sa;
+
+ // block all other signals
+ sigfillset( &sa.sa_mask );
+
+ // signal handler proc
+ sa.sa_sigaction = &sigterm_handler;
+
+ // flags
+ sa.sa_flags = SA_SIGINFO;
+
+ if ( sigaction( SIGTERM, &sa, NULL ) < 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ }
+
+
+ void sigterm_handler( int sig, siginfo_t *siginfo, void *context ) {
+
+ std::cout << "[" << getpid() << "] inside SIGTERM handler" << std::endl;
+ termination_handler( sig, siginfo, context );
+
+ }
+
+
+ void init_sigquit_handler() {
+
+ struct sigaction sa;
+
+ // block all other signals
+ sigfillset( &sa.sa_mask );
+
+ // signal handler proc
+ sa.sa_sigaction = &sigquit_handler;
+
+ // flags
+ sa.sa_flags = SA_SIGINFO;
+
+ if ( sigaction( SIGQUIT, &sa, NULL ) < 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ }
+
+
+ void sigquit_handler( int sig, siginfo_t *siginfo, void *context ) {
+
+ std::cout << "[" << getpid() << "] inside SIGQUIT handler" << std::endl;
+ termination_handler( sig, siginfo, context );
+
+ }
+
+
+ void init_sigint_handler() {
+
+ struct sigaction sa;
+
+ // block all other signals
+ sigfillset( &sa.sa_mask );
+
+ // signal handler proc
+ sa.sa_sigaction = &sigint_handler;
+
+ // flags
+ sa.sa_flags = SA_SIGINFO;
+
+ if ( sigaction( SIGINT, &sa, NULL ) < 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ }
+
+
+ void sigint_handler( int sig, siginfo_t *siginfo, void *context ) {
+
+ std::cout << "[" << getpid() << "] inside SIGQINT handler" << std::endl;
+ termination_handler( sig, siginfo, context );
+
+ }
+
+
+ void daemonize( const char *rundir, const char *pidfile ) {
+
+ pid_t pid, sid;
+ long i;
+ char str[10];
+
+ // notify
+ syslog( LOG_NOTICE, "starting daemon (version %s)", PACKAGE_VERSION );
+
+ // check parent process id value
+ if ( getppid() == 1 ) {
+ // we are already a daemon
+ return;
+ }
+
+ /* fork daemon */
+ pid = fork();
+ if ( pid < 0 ) {
+ exit( EXIT_FAILURE );
+ }
+
+ // exit the parent
+ if ( pid > 0 ) {
+ exit( EXIT_SUCCESS );
+ }
+
+
+ /* child (a.k.a daemon) continues */
+
+ // set file permissions (750)
+ umask( 027 );
+
+ // get a new process group
+ sid = setsid();
+ if ( sid < 0 ) {
+ exit(EXIT_FAILURE);
+ }
+
+ // route I/O connections
+ close( STDIN_FILENO );
+ close( STDOUT_FILENO );
+ close( STDERR_FILENO );
+
+ // change running directory
+ chdir( rundir );
+
+
+ /* lock pid file to ensure we have only one copy */
+
+ globals.pid_handle = open( pidfile, O_RDWR | O_CREAT, 0600 );
+ if ( globals.pid_handle == -1 ) {
+ syslog( LOG_ERR, "could not open pid lock file: %s", pidfile );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( lockf( globals.pid_handle, F_TLOCK, 0 ) == -1 ) {
+ syslog( LOG_ERR, "could not lock pid lock file: %s", pidfile);
+ exit( EXIT_FAILURE );
+ }
+
+ // get and format pid
+ sprintf( str, "%d\n", getpid() );
+
+ // write pid to lockfile
+ write( globals.pid_handle, str, strlen( str ) );
+
+ // update status
+ globals.daemon = true;
+
+
+ }
+
+
+ void shutdown() {
+
+ // notify
+ if ( globals.daemon && ( getppid() == 1 ) ) {
+ syslog( LOG_NOTICE, "shutting down daemon (version %s)", PACKAGE_VERSION );
+ }
+
+ // close pid file
+ if ( globals.pid_handle != -1 ) {
+ close( globals.pid_handle );
+ }
+
+ // close logger (syslog)
+ closelog();
+
+ }
+
+
+ void termination_handler( int sig, siginfo_t * sig_info, void * context ) {
+
+ std::cout << "[" << getpid() << "] inside termination handler" << std::endl;
+
+ // exit by re-raising the signal if termination
+ // already in progress
+ if ( globals.terminating ) {
+ std::cout << "[" << getpid() << "] already terminating" << std::endl;
+ raise( sig );
+ }
+
+ // update termination status
+ globals.terminating = 1;
+
+
+ if ( globals.manager != NULL ) {
+
+ // shutdown the manager
+ globals.manager->shutdown();
+
+ // clean-up
+ delete globals.manager;
+
+ }
+
+ // cleanup
+ shutdown();
+
+ // re-raise the signal after reactivating the signal's default action
+ signal( sig, SIG_DFL );
+ raise( sig );
+
+ }
+
+
+ options_t read_options( int argc, char **argv ) {
+
+ int optidx, optchar;
+ options_t options;
+
+ options.config_file = "";
+
+ struct option lopts[] = {
+ { "config", required_argument, 0, 'c' },
+ { "debug", no_argument, &options.debug_flag, 1 },
+ { "help", no_argument, 0, 'h' },
+ { "usage", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+
+ while ( true ) {
+
+ optidx = 0;
+ optchar = getopt_long( argc, argv, "c:hv", lopts, &optidx );
+
+ // sanity check
+ if ( optchar == -1 ) {
+ break;
+ }
+
+ switch ( optchar ) {
+ case 0:
+ // TODO:
+ break;
+
+ case 'c':
+ options.config_file = optarg;
+ break;
+
+ case 'h':
+ print_usage();
+ exit( EXIT_SUCCESS );
+ break;
+
+ case 'v':
+ print_version();
+ exit( EXIT_SUCCESS );
+
+ case '?':
+ print_usage();
+ exit( EXIT_FAILURE );
+ break;
+
+ default:
+ // TODO:
+ break;
+ }
+
+ }
+
+ return options;
+
+ }
+
+
+ void print_version() {
+
+ std::cout << PACKAGE_STRING << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "Copyright (C) 2012-2013 Uditha Atukorala" << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "This program is free software; you can redistribute it and/or modify" << std::endl;
+ std::cout << "it under the terms of the GNU General Public License as published by" << std::endl;
+ std::cout << "the Free Software Foundation; either version 3 of the License, or" << std::endl;
+ std::cout << "(at your option) any later version." << std::endl;
+ std::cout << "" << std::endl;
+ std::cout << "This program is distributed in the hope that it will be useful," << std::endl;
+ std::cout << "but WITHOUT ANY WARRANTY; without even the implied warranty of" << std::endl;
+ std::cout << "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" << std::endl;
+ std::cout << "GNU General Public License (http://gnu.org/licenses/gpl.html)" << std::endl;
+ std::cout << "for more details." << std::endl;
+ std::cout << "" << std::endl;
+
+ }
+
+
+ void print_usage() {
+
+ std::cout << "usage: " << PACKAGE_NAME << " "
+ << "[--version] [--help] [--usage] [--debug] [--config=<config file>]"
+ << std::endl;
+
+ std::cout << "" << std::endl;
+ std::cout << "See the man pages for more information" << std::endl;
+
+ }
+
+
+ void start( int port, unsigned int children, int max_requests ) {
+
+ try {
+ globals.manager = new bitz::Manager( port );
+ globals.manager->spawn( children, max_requests );
+ } catch ( bitz::ManagerException &mex ) {
+ syslog( LOG_ERR, "failed to start, exception: %s", mex.what() );
+ exit( EXIT_FAILURE );
+ }
+
+ }
+
+
+ void run() {
+
+ sigset_t mask, oldmask;
+
+ // sanity check
+ if ( globals.manager == NULL ) {
+ return;
+ }
+
+ // block termination signals until we are ready
+ sigemptyset( &mask );
+ sigaddset( &mask, SIGTERM );
+ sigaddset( &mask, SIGQUIT );
+ sigaddset( &mask, SIGINT );
+ sigprocmask ( SIG_BLOCK, &mask, &oldmask );
+
+ // loop until a termination signal is received
+ while (! globals.terminating ) {
+
+ // capture any signals
+ sigsuspend( &oldmask );
+
+ // unblock termination signals
+ sigprocmask( SIG_UNBLOCK, &mask, NULL );
+
+ // manage workers
+ globals.manager->manager_workers();
+
+ // block termination signals
+ sigprocmask( SIG_BLOCK, &mask, &oldmask );
+
+ }
+
+ }
+
+ } /* end of namespace server */
+
+} /* end of namespace bitz */
+
+
+
diff --git a/src/bitz-server.h b/src/bitz-server.h
new file mode 100644
index 0000000..87129ad
--- /dev/null
+++ b/src/bitz-server.h
@@ -0,0 +1,113 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_SERVER_H
+#define BITZ_SERVER_H
+
+#include "bitz/manager.h"
+
+#include <csignal>
+
+
+namespace bitz {
+
+ namespace server {
+
+ /**
+ * Structure to hold server globals initialised
+ * by the init() method.
+ */
+ struct server_t {
+ int pid_handle;
+ volatile sig_atomic_t terminating;
+ bool daemon;
+
+ bitz::Manager * manager;
+ };
+
+ /**
+ * Structure to hold command line options.
+ */
+ struct options_t {
+ int debug_flag;
+ std::string config_file;
+ };
+
+ void init();
+ void init_signal_handlers();
+ void init_sigchld_handler();
+ void init_sigterm_handler();
+ void init_sigquit_handler();
+ void init_sigint_handler();
+ void sigchld_handler( int sig, siginfo_t * sig_info, void * context );
+ void sigterm_handler( int sig, siginfo_t * sig_info, void * context );
+ void sigquit_handler( int sig, siginfo_t * sig_info, void * context );
+ void sigint_handler( int sig, siginfo_t * sig_info, void * context );
+
+ void daemonize( const char * run_dir, const char * pid_file );
+ void shutdown();
+ void termination_handler( int sig, siginfo_t * sig_info, void * context );
+
+ /**
+ * Read command line options and return a options_t structure.
+ * This method will terminate the program with an exit status
+ * of EXIT_FAILURE if any errors are found while reading options.
+ * If --version or --help options are found this will print the
+ * appropriate message to the standard output and terminate with
+ * EXIT_SUCCESS.
+ *
+ * @param argc same as main() argc
+ * @param argv same as main() argv
+ * @return read options
+ */
+ options_t read_options( int argc, char **argv );
+
+ /**
+ * Print version information to the standard output
+ */
+ void print_version();
+
+ /**
+ * Print usage message to the standard output
+ */
+ void print_usage();
+
+ /**
+ * Start the server by creating a bitz::Manager instance and spawn
+ * workers.
+ *
+ * @param port port number to listen to
+ * @param children number of children to spawn
+ * @param max_request maximum number of requests that a child will serve
+ */
+ void start( int port, unsigned int children, int max_requests );
+
+ /**
+ * Run the server managing workers until a termination signal is received.
+ * This process will never return, instead the termination signal will
+ * end the process.
+ */
+ void run();
+
+ } /* end of namespace server */
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_SERVER_H */
+
diff --git a/src/bitz/common.h b/src/bitz/common.h
new file mode 100644
index 0000000..5b03948
--- /dev/null
+++ b/src/bitz/common.h
@@ -0,0 +1,40 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_COMMON_H
+#define BITZ_COMMON_H
+
+#include <string>
+#include <map>
+
+#include "request_handler.h"
+
+
+namespace bitz {
+
+ /* request handlers type */
+ typedef std::map<std::string, RequestHandler *> req_handlers_t;
+
+ /* iterator type */
+ typedef req_handlers_t::iterator req_handlers_index_t;
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_COMMIN_H */
+
diff --git a/src/bitz/config.cpp b/src/bitz/config.cpp
new file mode 100644
index 0000000..a9ca856
--- /dev/null
+++ b/src/bitz/config.cpp
@@ -0,0 +1,190 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+#include "logger.h"
+
+#include <iostream>
+#include <cstdlib>
+
+
+namespace bitz {
+
+ Config::Config() {
+
+ // initialise config_t values
+ _config.port = 1344;
+ _config.pid_file = "/dev/null";
+ _config.log_file = "/dev/null";
+ _config.log_category = "bitz";
+ _config.req_handlers_count = 0;
+ _config.req_handlers = NULL;
+
+ _config.max_workers = 0;
+ _config.max_worker_requests = 0;
+
+
+ // defaults
+ _lconfig = NULL;
+
+ }
+
+ Config::~Config() {
+
+ // cleanup
+ delete _lconfig;
+
+ for (int i = 0; i < _config.req_handlers_count ; i++ ) {
+ if ( _config.req_handlers[i].modules_count > 0 ) {
+ delete [] _config.req_handlers[i].modules;
+ }
+ }
+
+ if ( _config.req_handlers != NULL ) {
+ delete [] _config.req_handlers;
+ }
+
+ }
+
+
+ const config_t &Config::initialise( const std::string &config_file ) {
+
+ libconfig::Config * config;
+ config = new libconfig::Config;
+
+ try {
+ config->readFile( config_file.c_str() );
+ } catch( const libconfig::FileIOException &ex ) {
+ std::cerr << "[config] failed to read config file: " << config_file
+ << ", exception: " << ex.what() << std::endl;
+ exit( EXIT_FAILURE );
+ } catch( const libconfig::ParseException &pex ) {
+ std::cerr << "[config] parse error at " << pex.getFile()
+ << ":" << pex.getLine() << " - " << pex.getError() << std::endl;
+ exit( EXIT_FAILURE );
+ }
+
+ try {
+
+ // read core configs
+ config->lookupValue( "port", _config.port );
+ config->lookupValue( "pid_file", _config.pid_file );
+ config->lookupValue( "log_file", _config.log_file );
+ config->lookupValue( "log_category", _config.log_category );
+
+ config->lookupValue( "max_workers", _config.max_workers );
+ config->lookupValue( "max_worker_requests", _config.max_worker_requests );
+
+ } catch( const libconfig::SettingNotFoundException &e ) {
+ std::cerr << "[config] failed to load core configs, "
+ << e.getPath() << " : " << e.what() << std::endl;
+ }
+
+ // cache configs
+ _lconfig = config;
+
+ // read other configs
+ read_req_handler_configs();
+
+ return _config;
+
+ }
+
+
+ const config_t &Config::configs() {
+ return _config;
+ }
+
+
+ const std::string Config::module_config( const std::string &module, const std::string &config ) throw() {
+
+ std::string config_value = "";
+
+ if ( _lconfig->exists( "modules" ) ) {
+
+ try {
+ libconfig::Setting &setting = _lconfig->lookup( std::string( "modules." ).append( module ) );
+ setting.lookupValue( config, config_value );
+ } catch( const libconfig::SettingNotFoundException &e ) {
+ // TODO: log errors ??
+ std::cerr << "[config] " << e.getPath() << " : " << e.what() << std::endl;
+ }
+
+ } else {
+ std::cout << "[config] 'modules' configs not found" << std::endl;
+ }
+
+ return config_value;
+
+ }
+
+
+ void Config::read_req_handler_configs() throw() {
+
+ int i, j;
+ std::string s;
+
+ std::cout << "[config] looking for req_handlers... ";
+ if ( _lconfig->exists( "req_handlers" ) ) {
+
+ std::cout << "found ";
+
+ libconfig::Setting &req_handlers = _lconfig->lookup( "req_handlers" );
+ _config.req_handlers_count = req_handlers.getLength();
+ _config.req_handlers = new req_handlers_config_t[_config.req_handlers_count];
+
+ std::cout << "(" << _config.req_handlers_count << ")" << std::endl;
+
+ try {
+
+ // read request handler configs
+ for ( i = 0; i < _config.req_handlers_count ; i++ ) {
+ _config.req_handlers[i].name = (const char *) req_handlers[i]["handler"];
+ _config.req_handlers[i].class_name = (const char *) req_handlers[i]["class"];
+
+ // read request handler modules config
+ std::cout << "[config] looking for " << _config.req_handlers[i].name << " modules... ";
+ if ( req_handlers[i].exists( "modules" ) ) {
+ std::cout << "found ";
+
+ _config.req_handlers[i].modules_count = req_handlers[i]["modules"].getLength();
+ _config.req_handlers[i].modules = new modules_config_t[_config.req_handlers[i].modules_count];
+
+ std::cout << "(" << _config.req_handlers[i].modules_count << ")" << std::endl;
+ for ( j = 0; j < _config.req_handlers[i].modules_count; j++ ) {
+ _config.req_handlers[i].modules[j].name = (const char *) req_handlers[i]["modules"][j]["name"];
+ _config.req_handlers[i].modules[j].module = (const char *) req_handlers[i]["modules"][j]["module"];
+ }
+ } else {
+ std::cout << "not found" << std::endl;
+ }
+ }
+
+ } catch ( const libconfig::SettingNotFoundException &ex ) {
+ std::cerr << "[config] Error: " << ex.getPath() << ex.what() << std::endl;
+ }
+
+ } else {
+ std::cout << "not found" << std::endl;
+ }
+
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/config.h b/src/bitz/config.h
new file mode 100644
index 0000000..453d29d
--- /dev/null
+++ b/src/bitz/config.h
@@ -0,0 +1,95 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_CONFIG_H
+#define BITZ_CONFIG_H
+
+#include <string>
+#include <libconfig.h++>
+
+
+#ifndef BITZ_SERVER_CONFIG_FILE
+#define BITZ_SERVER_CONFIG_FILE "/etc/bitz/bitz-server.conf"
+#endif
+
+namespace bitz {
+
+ struct modules_config_t {
+ std::string name;
+ std::string module;
+ };
+
+ struct req_handlers_config_t {
+ std::string name;
+ std::string class_name;
+ unsigned int modules_count;
+
+ modules_config_t * modules;
+ };
+
+ struct config_t {
+ int port;
+ int max_workers;
+ int max_worker_requests;
+ std::string pid_file;
+ std::string log_file;
+ std::string log_category;
+ unsigned int req_handlers_count;
+
+ req_handlers_config_t * req_handlers;
+ };
+
+ class Config {
+ public:
+ static Config &instance() {
+ static Config config;
+ return config;
+ }
+
+ const config_t &initialise( const std::string &config_file = BITZ_SERVER_CONFIG_FILE );
+ const config_t &configs();
+
+ /**
+ * Returns module specific config value (or NULL string if not found)
+ * Note: This method should be only used my the pluggable modules and not
+ * by the core code.
+ *
+ * @param module module name
+ * @param config config name
+ * @return module config value
+ */
+ const std::string module_config( const std::string &module, const std::string &config ) throw();
+
+ private:
+ config_t _config;
+ libconfig::Config * _lconfig;
+
+ Config();
+ ~Config();
+ Config( Config const &copy );
+ Config &operator=( const Config &copy );
+
+ void read_req_handler_configs() throw();
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_CONFIG_H */
+
diff --git a/src/bitz/exception.cpp b/src/bitz/exception.cpp
new file mode 100644
index 0000000..bf8be9e
--- /dev/null
+++ b/src/bitz/exception.cpp
@@ -0,0 +1,40 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "exception.h"
+#include <cerrno> // For errno
+#include <cstring> // For strerror
+
+namespace bitz {
+
+ Exception::Exception( const std::string &message, bool inclSystemMessage ) throw() : _message( message ) {
+ if ( inclSystemMessage ) {
+ _message.append( " : " );
+ _message.append( strerror( errno ) );
+ }
+ }
+
+ Exception::~Exception() throw() { }
+
+ const char * Exception::what() const throw() {
+ return _message.c_str();
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/exception.h b/src/bitz/exception.h
new file mode 100644
index 0000000..54abdcd
--- /dev/null
+++ b/src/bitz/exception.h
@@ -0,0 +1,43 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+#ifndef BITZ_EXCEPTION_H
+#define BITZ_EXCEPTION_H
+
+#include <exception> // for exception class
+#include <string> // for std::string
+
+namespace bitz {
+
+ class Exception : public std::exception {
+ public:
+ Exception( const std::string &message, bool inclSystemMessage = false ) throw();
+ virtual ~Exception() throw();
+
+ virtual const char * what() const throw();
+
+ private:
+ std::string _message;
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_EXCEPTION_H */
+
diff --git a/src/bitz/logger.cpp b/src/bitz/logger.cpp
new file mode 100644
index 0000000..1d7d833
--- /dev/null
+++ b/src/bitz/logger.cpp
@@ -0,0 +1,98 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "logger.h"
+
+#include <log4cpp/FileAppender.hh>
+#include <log4cpp/PatternLayout.hh>
+
+namespace bitz {
+
+ Logger::Logger( std::string log_file, std::string category ) {
+ this->LOGGER = NULL;
+ this->initialise( log_file, category );
+ }
+
+ Logger::~Logger() {
+ this->LOGGER->debug( "closing down logger" );
+ }
+
+ void Logger::initialise( std::string log_file, std::string category ) {
+
+ this->LOGGER = &log4cpp::Category::getInstance( category );
+
+ // setting up appender, layout and category
+ log4cpp::Appender * log_appender = new log4cpp::FileAppender( "FileAppender", log_file );
+ log4cpp::PatternLayout * log_layout = new log4cpp::PatternLayout();
+ log_layout->setConversionPattern( "%d %p %c %x: %m%n" );
+
+ log_appender->setLayout( log_layout );
+ this->LOGGER->setAppender( log_appender );
+ this->LOGGER->setPriority( this->getPriorityValue( "DEBUG" ) );
+
+ this->LOGGER->debug( "logger initialised, log_file: " + log_file );
+
+ }
+
+ int Logger::getPriorityValue( const std::string &priority ) {
+ return ( log4cpp::Priority::getPriorityValue( priority ) );
+ }
+
+ void Logger::log( log4cpp::Priority::Value priority, const std::string &message ) {
+ this->LOGGER->log( priority, message );
+ }
+
+ void Logger::fatal( const std::string& message ) {
+ this->LOGGER->fatal( message );
+ }
+
+ void Logger::emerg( const std::string& message ) {
+ this->LOGGER->emerg( message );
+ }
+
+ void Logger::alert( const std::string& message ) {
+ this->LOGGER->alert( message );
+ }
+
+ void Logger::crit( const std::string& message ) {
+ this->LOGGER->crit( message );
+ }
+
+ void Logger::error( const std::string& message ) {
+ this->LOGGER->error( message );
+ }
+
+ void Logger::warn( const std::string& message ) {
+ this->LOGGER->warn( message );
+ }
+
+ void Logger::notice( const std::string& message ) {
+ this->LOGGER->notice( message );
+ }
+
+ void Logger::info( const std::string& message ) {
+ this->LOGGER->info( message );
+ }
+
+ void Logger::debug( const std::string& message ) {
+ this->LOGGER->debug( message );
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/logger.h b/src/bitz/logger.h
new file mode 100644
index 0000000..f1c9c6c
--- /dev/null
+++ b/src/bitz/logger.h
@@ -0,0 +1,62 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_LOGGER_H
+#define BITZ_LOGGER_H
+
+#include <string>
+#include <log4cpp/Category.hh>
+
+namespace bitz {
+
+ class Logger {
+ public:
+ static Logger &instance( std::string log_file = "/dev/null" , std::string category = "logger" ) {
+ static Logger logger( log_file, category );
+ return logger;
+ }
+
+ void initialise( std::string log_file, std::string category );
+
+ static int getPriorityValue( const std::string& priority );
+ void log( int priority, const std::string &message );
+
+ void fatal( const std::string& message );
+ void emerg( const std::string& message );
+ void alert( const std::string& message );
+ void crit( const std::string& message );
+ void error( const std::string& message );
+ void warn( const std::string& message );
+ void notice( const std::string& message );
+ void info( const std::string& message );
+ void debug( const std::string& message );
+
+ private:
+ log4cpp::Category * LOGGER;
+
+ Logger( std::string log_file, std::string category );
+ ~Logger();
+ Logger( Logger const &copy );
+ Logger &operator=( const Logger &copy );
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_LOGGER_H */
+
diff --git a/src/bitz/manager.cpp b/src/bitz/manager.cpp
new file mode 100644
index 0000000..ffa2dae
--- /dev/null
+++ b/src/bitz/manager.cpp
@@ -0,0 +1,223 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "manager.h"
+#include "logger.h"
+#include "util.h"
+
+#include <cstdlib>
+#include <sstream>
+#include <csignal>
+
+
+namespace bitz {
+
+ Manager::Manager( unsigned short port, const std::string &address, int backlog ) throw( ManagerException ) {
+
+ // initialise manager
+ _manager.worker = false;
+ _manager.max_workers = 0;
+ _manager.workers_count = 0;
+ _manager.worker_id = 0;
+ _manager.socket = NULL;
+ _manager.worker_pool = NULL;
+
+ // initialise listening socket
+ try {
+ if ( address.empty() ) {
+ _manager.socket = new socketlibrary::TCPServerSocket( port, backlog );
+ } else {
+ _manager.socket = new socketlibrary::TCPServerSocket( address, port, backlog );
+ }
+ } catch ( socketlibrary::SocketException &sex ) {
+ throw ManagerException( "failed to initialise socket" );
+ }
+
+ Logger &logger = Logger::instance();
+ logger.debug( "manager initialised" );
+
+ }
+
+
+ Manager::~Manager() {
+
+ Logger &logger = Logger::instance();
+
+ if ( _manager.worker ) {
+ logger.debug( "[worker] cleaning up manager" );
+ } else {
+ logger.debug( "[manager] shutting down manager" );
+ }
+
+ delete [] _manager.worker_pool;
+ delete _manager.socket;
+
+ }
+
+
+ void Manager::spawn( unsigned int max_workers, unsigned int max_worker_requests ) throw( ManagerException ) {
+
+ _manager.max_workers = max_workers;
+ _manager.max_worker_requests = max_worker_requests;
+ _manager.worker_pool = new worker_pool_t[max_workers];
+
+ // pre-fork workers
+ if (! _manager.worker ) {
+ for ( unsigned int i = 0; i < max_workers; i++ ) {
+
+ try {
+ spawn_worker( i );
+ } catch ( ManagerException &mex ) {
+ throw mex;
+ }
+
+ }
+ }
+
+ }
+
+
+ void Manager::spawn_worker( unsigned int worker_id ) throw( ManagerException ) {
+
+ Logger &logger = Logger::instance();
+ pid_t worker_pid;
+
+ // create a worker child
+ worker_pid = fork();
+ if ( worker_pid == -1 ) {
+ throw ManagerException( "failed to create worker", true );
+ }
+
+ if ( worker_pid == 0 ) {
+
+ /* worker */
+
+ _manager.worker = true;
+ _manager.worker_id = worker_id;
+ _manager.worker_pool[worker_id].worker = new Worker();
+ _manager.worker_pool[worker_id].worker_id = worker_id;
+ _manager.worker_pool[worker_id].worker_pid = worker_pid;
+
+ _manager.worker_pool[worker_id].worker->run( _manager.socket, _manager.max_worker_requests );
+ logger.info( std::string( "end of cycle, worker[" ).append( util::itoa( worker_id ) ).append( "]" ) );
+
+ delete _manager.worker_pool[worker_id].worker;
+ _exit( EXIT_SUCCESS );
+
+ } else {
+
+ /* manager */
+ logger.info( std::string( "[manager] worker spawned with pid: " ).append( util::itoa( worker_pid) ) );
+
+ _manager.workers_count++;
+
+ _manager.worker = false;
+ _manager.worker_pool[worker_id].worker = NULL;
+ _manager.worker_pool[worker_id].worker_id = worker_id;
+ _manager.worker_pool[worker_id].worker_pid = worker_pid;
+
+ }
+
+ }
+
+
+ void Manager::shutdown( bool graceful ) throw() {
+
+ // logger
+ Logger &logger = Logger::instance();
+
+ if ( _manager.worker ) {
+
+ logger.info( "[worker] manager shutdown request received" );
+
+ /* worker: cleanup */
+ delete _manager.worker_pool[_manager.worker_id].worker;
+
+ } else {
+
+ /* manager: stop all child processes */
+ logger.info( "[manager] shutdown request received" );
+
+ for (unsigned int i = 0; i < _manager.max_workers; i++ ) {
+ if ( _manager.worker_pool[i].worker_pid != 0 ) {
+ if ( graceful ) {
+ kill( _manager.worker_pool[i].worker_pid, SIGTERM );
+ logger.debug( std::string( "[manager] sending SIGTERM to worker[" ).append( util::itoa( i ) )
+ .append( "], pid: " ).append( util::itoa( _manager.worker_pool[i].worker_pid ) ) );
+ } else {
+ kill( _manager.worker_pool[i].worker_pid, SIGKILL );
+ logger.debug( std::string( "[manager] sending SIGKILL to worker[" ).append( util::itoa( i ) )
+ .append( "], pid: " ).append( util::itoa( _manager.worker_pool[i].worker_pid ) ) );
+ }
+ } else {
+ logger.debug( std::string( "[manager] worker[" ).append( util::itoa( i ) ).append( "] already closed" ) );
+ }
+ }
+ }
+
+ }
+
+
+ void Manager::reap_worker( pid_t worker_pid ) throw() {
+
+ // logger
+ Logger &logger = Logger::instance();
+ logger.debug( std::string( "reaping worker, pid: " ).append( util::itoa( worker_pid ) ) );
+
+ if (! _manager.worker ) {
+ for (unsigned int i = 0; i < _manager.max_workers; i++ ) {
+ if ( _manager.worker_pool[i].worker_pid == worker_pid ) {
+
+ // reap the dead worker
+ _manager.worker_pool[i].worker_pid = 0;
+ _manager.workers_count--;
+
+ // break out the loop
+ break;
+
+ }
+ }
+ }
+
+ }
+
+
+ void Manager::manager_workers() throw() {
+
+ if (! _manager.worker ) {
+
+ // check the worker count
+ while ( _manager.workers_count != _manager.max_workers ) {
+
+ // we are missing workers, find out who
+ for (unsigned int i = 0; i < _manager.max_workers; i++ ) {
+ if ( _manager.worker_pool[i].worker_pid == 0 ) {
+ // spawn a worker for the missing
+ spawn_worker( i );
+ }
+ }
+
+ }
+
+ }
+
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/manager.h b/src/bitz/manager.h
new file mode 100644
index 0000000..cc15f9c
--- /dev/null
+++ b/src/bitz/manager.h
@@ -0,0 +1,87 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_MANAGER_H
+#define BITZ_MANAGER_H
+
+#include <unistd.h> // pid_t, fork() etc.
+#include <socket/socket.h> // socket-library
+
+#include "manager_exception.h"
+#include "worker.h"
+
+#ifndef BITZ_MAX_WORKERS
+#define BITZ_MAX_WORKERS 2
+#endif
+
+#ifndef BITZ_MAX_WORKER_REQUESTS
+#define BITZ_MAX_WORKER_REQUESTS 100
+#endif
+
+
+namespace bitz {
+
+ class Manager {
+ public:
+
+ struct worker_pool_t {
+ pid_t worker_pid;
+ unsigned int worker_id;
+ Worker * worker;
+ };
+
+ struct manager_t {
+ bool worker;
+ unsigned int max_workers;
+ unsigned int max_worker_requests;
+ unsigned int workers_count;
+ unsigned int worker_id;
+
+ socketlibrary::TCPServerSocket * socket;
+ worker_pool_t * worker_pool;
+ };
+
+
+ /**
+ * Note: backlog = SOMAXCONN (from sys/socket.h)
+ */
+ Manager( unsigned short port, const std::string &address = "", int backlog = 128 ) throw( ManagerException );
+
+ /**
+ * deconstructor
+ */
+ virtual ~Manager();
+
+ virtual void spawn( unsigned int max_workers = BITZ_MAX_WORKERS, unsigned int max_worker_requests = BITZ_MAX_WORKER_REQUESTS ) throw( ManagerException );
+ virtual void shutdown( bool graceful = true ) throw();
+ virtual void reap_worker( pid_t worker_pid ) throw();
+ virtual void manager_workers() throw();
+
+
+ private:
+ manager_t _manager;
+
+ virtual void spawn_worker( unsigned int worker_id ) throw( ManagerException );
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_MANAGER_H */
+
diff --git a/src/bitz/manager_exception.cpp b/src/bitz/manager_exception.cpp
new file mode 100644
index 0000000..d49f58a
--- /dev/null
+++ b/src/bitz/manager_exception.cpp
@@ -0,0 +1,32 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "manager_exception.h"
+
+namespace bitz {
+
+ ManagerException::ManagerException( const std::string &message, bool inclSystemMessage ) throw() :
+ Exception( message, inclSystemMessage ) {
+ // constructor
+ }
+
+ ManagerException::~ManagerException() throw() { }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/manager_exception.h b/src/bitz/manager_exception.h
new file mode 100644
index 0000000..0729ef0
--- /dev/null
+++ b/src/bitz/manager_exception.h
@@ -0,0 +1,36 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_MANAGER_EXCEPTION_H
+#define BITZ_MANAGER_EXCEPTION_H
+
+#include "exception.h"
+
+namespace bitz {
+
+ class ManagerException: public Exception {
+ public:
+ ManagerException( const std::string &message, bool inclSystemMessage = false ) throw();
+ virtual ~ManagerException() throw();
+ };
+
+} /* end of namespace bitz */
+
+#endif /* BITZ_MANAGER_EXCEPTION_H */
+
diff --git a/src/bitz/modifier.cpp b/src/bitz/modifier.cpp
new file mode 100644
index 0000000..a0a416f
--- /dev/null
+++ b/src/bitz/modifier.cpp
@@ -0,0 +1,29 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "modifier.h"
+
+
+namespace bitz {
+
+ Modifier::Modifier() { }
+ Modifier::~Modifier() { }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/modifier.h b/src/bitz/modifier.h
new file mode 100644
index 0000000..7f189aa
--- /dev/null
+++ b/src/bitz/modifier.h
@@ -0,0 +1,70 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_MODIFIER_H
+#define BITZ_MODIFIER_H
+
+#include <icap/request.h>
+#include <icap/response.h>
+
+
+namespace bitz {
+
+ class Modifier {
+ public:
+
+ /* types of the class factories */
+ typedef bitz::Modifier * create_t();
+ typedef void destroy_t( bitz::Modifier * );
+
+ struct symbols_t {
+ void * modifier;
+ create_t * create;
+ destroy_t * destroy;
+ };
+
+ Modifier();
+ virtual ~Modifier();
+
+ /**
+ * Modify the request as needed and return a response object.
+ *
+ * @param request request object
+ * @return response object
+ */
+ virtual icap::Response * modify( icap::Request * request ) throw() =0;
+
+ /**
+ * Preview the request passed in and return a response object. The
+ * response status of 100 (continue) should be handled by the caller
+ * and pass the complete request to modify() method.
+ *
+ * @param request request object
+ * @return response object
+ */
+ virtual icap::Response * preview( icap::Request * request ) throw() =0;
+
+ private:
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_MODIFIER_H */
+
diff --git a/src/bitz/options_request_handler.cpp b/src/bitz/options_request_handler.cpp
new file mode 100644
index 0000000..6872b5c
--- /dev/null
+++ b/src/bitz/options_request_handler.cpp
@@ -0,0 +1,57 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "options_request_handler.h"
+
+
+namespace bitz {
+
+ OptionsRequestHandler::OptionsRequestHandler() : RequestHandler( "OPTIONS" ) { }
+ OptionsRequestHandler::~OptionsRequestHandler() { }
+
+
+ icap::Response * OptionsRequestHandler::process( icap::RequestHeader * req_header, socketlibrary::TCPSocket * socket ) throw() {
+
+ icap::ResponseHeader * header;
+ icap::Response * response;
+
+ // header
+ header = new icap::ResponseHeader( icap::ResponseHeader::OK );
+
+ // FIXME: Methods are tied to the URIs
+ header->attach( "Methods", _methods.at( 0 ) );
+ header->attach( "Options-TTL", "3600" );
+ header->attach( "Allow", "204" );
+
+ response = new icap::Response( header );
+ return response;
+
+ }
+
+
+ void OptionsRequestHandler::register_handler( RequestHandler * req_handler ) throw() {
+
+ // FIXME: Methods are tied to the URIs
+ _methods.push_back( req_handler->method() );
+ return;
+
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/options_request_handler.h b/src/bitz/options_request_handler.h
new file mode 100644
index 0000000..a81377f
--- /dev/null
+++ b/src/bitz/options_request_handler.h
@@ -0,0 +1,51 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_OPTIONS_REQUEST_HANDLER_H
+#define BITZ_OPTIONS_REQUEST_HANDLER_H
+
+#include "request_handler.h"
+
+
+namespace bitz {
+
+ class OptionsRequestHandler : public RequestHandler {
+ public:
+ OptionsRequestHandler();
+ virtual ~OptionsRequestHandler();
+
+ icap::Response * process( icap::RequestHeader * req_header, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * Register a request handler so it is known to the OPTIONS
+ * handler
+ *
+ * @param req_handler request handler
+ */
+ void register_handler( RequestHandler * req_handler ) throw();
+
+ private:
+ std::vector<std::string> _methods;
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_OPTIONS_REQUEST_HANDLER_H */
+
diff --git a/src/bitz/reqmod_request_handler.cpp b/src/bitz/reqmod_request_handler.cpp
new file mode 100644
index 0000000..7b1f0a2
--- /dev/null
+++ b/src/bitz/reqmod_request_handler.cpp
@@ -0,0 +1,30 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "reqmod_request_handler.h"
+
+
+namespace bitz {
+
+ ReqmodRequestHandler::ReqmodRequestHandler() : RequestHandler( "REQMOD" ) { }
+
+ ReqmodRequestHandler::~ReqmodRequestHandler() { }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/reqmod_request_handler.h b/src/bitz/reqmod_request_handler.h
new file mode 100644
index 0000000..b267c07
--- /dev/null
+++ b/src/bitz/reqmod_request_handler.h
@@ -0,0 +1,41 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_REQMOD_REQUEST_HANDLER_H
+#define BITZ_REQMOD_REQUEST_HANDLER_H
+
+#include "request_handler.h"
+
+
+namespace bitz {
+
+ class ReqmodRequestHandler : public RequestHandler {
+ public:
+
+ ReqmodRequestHandler();
+ virtual ~ReqmodRequestHandler();
+
+ private:
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_REQMOD_REQUEST_HANDLER_H */
+
diff --git a/src/bitz/request_handler.cpp b/src/bitz/request_handler.cpp
new file mode 100644
index 0000000..3665cba
--- /dev/null
+++ b/src/bitz/request_handler.cpp
@@ -0,0 +1,401 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "request_handler.h"
+#include "config.h"
+#include "logger.h"
+#include "util.h"
+
+#include <dlfcn.h>
+#include <icap/util.h>
+
+
+namespace bitz {
+
+ RequestHandler::RequestHandler( const std::string &method ) {
+
+ // initialise defaults
+ _handlers_count = 0;
+ _handlers = NULL;
+
+ // update variables
+ _req_handler.method = method;
+
+ // load modifier modules
+ load_modules();
+
+ }
+
+
+ RequestHandler::~RequestHandler() {
+
+ // cleanup modifier modules
+ cleanup_modules();
+
+ if ( _handlers != NULL ) {
+ delete [] _handlers;
+ }
+
+ Logger &logger = Logger::instance();
+ logger.debug( std::string( "[req] exiting request handler [" ).append( _req_handler.method ).append( "]" ) );
+
+ }
+
+
+ const std::string &RequestHandler::method() const throw() {
+ return _req_handler.method;
+ }
+
+
+ icap::Response * RequestHandler::process( icap::RequestHeader * req_header, socketlibrary::TCPSocket * socket ) throw() {
+
+
+ icap::Request * request;
+ icap::Response * response = NULL;
+
+ // logger
+ Logger &logger = Logger::instance();
+
+
+ // request
+ request = new icap::Request( req_header );
+
+ // read request data
+ if (! icap::util::read_req_data( request, socket ) ) {
+
+ logger.warn( "[req] failed to read request data" );
+ response = new icap::Response( icap::ResponseHeader::SERVER_ERROR );
+
+ } else {
+
+ logger.debug( std::string( "[req] payload.req-hdr:\r\n").append( request->payload().req_header ) );
+ logger.debug( std::string( "[req] payload.req-body:\r\n").append( request->payload().req_body ) );
+ logger.debug( std::string( "[req] payload.res-hdr:\r\n").append( request->payload().res_header ) );
+ logger.debug( std::string( "[req] payload.res-body:\r\n").append( request->payload().res_body ) );
+
+
+ // check for message preview
+ if ( request->preview_size() >= 0 ) {
+
+ // process preview
+ logger.debug( std::string( "[req] message preview request, preview: " ).append( util::itoa( request->preview_size() ) ) );
+ response = process_preview( request, socket );
+
+ }
+
+ /*
+ * When we get here, if the response is NULL then either this is a
+ * pure REQMOD request without message preview or the preview was
+ * inconclusive (i.e. 100 Continue) and we have requested for the full
+ * request.
+ */
+ if ( response == NULL ) {
+
+ // process modify
+ logger.debug( "[req] modify request" );
+ response = process_modify( request );
+
+ }
+
+ }
+
+ // cleanup
+ delete request;
+
+ // sanity check
+ if ( response == NULL ) {
+ logger.warn( "[req] no valid response from modifiers, creating a server error (500) response" );
+ response = new icap::Response( icap::ResponseHeader::SERVER_ERROR );
+ }
+
+ return response;
+
+ }
+
+
+ bool RequestHandler::load_modifier( const std::string &file, Modifier::symbols_t &symbols ) throw() {
+
+ // logger
+ Logger &logger = Logger::instance();
+
+ // vars
+ const char* dlsym_error;
+
+ // load the modifier module
+ logger.debug( "[req] loading modifier: " + file );
+ symbols.modifier = dlopen( file.c_str(), RTLD_LAZY | RTLD_LOCAL );
+
+ if (! symbols.modifier ) {
+ logger.warn( std::string( "[req] failed to load modifier: " ).append( file ).append( dlerror() ) );
+ return false;
+ }
+
+ // reset errors
+ dlerror();
+
+ // load the symbols
+ symbols.create = ( Modifier::create_t * ) dlsym( symbols.modifier, "create" );
+ dlsym_error = dlerror();
+
+ if ( dlsym_error ) {
+ logger.warn( std::string( "[req] failed to load create symbol: " ).append( dlsym_error ) );
+ return false;
+ }
+
+ symbols.destroy = ( Modifier::destroy_t * ) dlsym( symbols.modifier, "destroy" );
+ dlsym_error = dlerror();
+
+ if ( dlsym_error ) {
+ logger.warn( std::string( "[req] failed to load destroy symbol: " ).append( dlsym_error ) );
+ return false;
+ }
+
+ return true;
+
+ }
+
+
+ void RequestHandler::unload_modifier( void * modifier ) throw() {
+ // unload the modifier module
+ dlclose( modifier );
+ }
+
+
+ void RequestHandler::load_modules() throw() {
+
+ int i = 0;
+ int j = 0;
+ const bitz::config_t &config = Config::instance().configs();
+
+ // search for request handlers
+ for ( i = 0; i < config.req_handlers_count; i++ ) {
+
+ // we are only interested in handlers for the current method (e.g. REQMOD, RESPMOD)
+ if ( config.req_handlers[i].name == method() ) {
+
+ if ( config.req_handlers[i].modules_count > 0 ) {
+
+ _handlers_count = config.req_handlers[i].modules_count;
+ _handlers = new handler_t[_handlers_count];
+
+ // search for request handler modules
+ for (j = 0; j < _handlers_count; j++ ) {
+
+ // load module
+ if ( load_modifier( config.req_handlers[i].modules[j].module, _handlers[j].symbols ) ) {
+ _handlers[j].name = config.req_handlers[i].modules[j].name;
+ } else {
+ _handlers[j].name = "";
+ // FIXME: error handling
+ }
+
+ }
+
+ }
+
+ // not interested in duplicate config entries
+ break;
+ }
+
+ }
+
+
+ }
+
+
+ void RequestHandler::cleanup_modules() throw() {
+
+ int i = 0;
+
+ // logger
+ Logger &logger = Logger::instance();
+
+ for ( i = 0; i < _handlers_count; i++ ) {
+
+ logger.debug( std::string( "[req] unloading module: " ).append( _handlers[i].name ) );
+
+ // unload
+ unload_modifier( _handlers[i].symbols.modifier );
+
+ }
+
+ }
+
+
+ icap::Response * RequestHandler::process_preview( icap::Request * request, socketlibrary::TCPSocket * socket ) throw() {
+
+ icap::Response * response = NULL;
+ Modifier * modifier;
+
+ int i = 0;
+ bool continue_status = false;
+
+ // logger
+ Logger &logger = Logger::instance();
+
+
+ /*
+ * Loop through loaded modifier modules and grab responses
+ *
+ * We will only get a chance to get a response from the first
+ * module. But if the first module returns a '100 Continue' response
+ * then we read the rest of the request here before returning a NULL
+ * response.
+ */
+ for ( i = 0 ; i < _handlers_count; i++ ) {
+
+ // sanity check
+ if ( _handlers[i].name == "" ) {
+ logger.info( "[req] modifier not loaded, not trying to get a response" );
+ continue;
+ }
+
+ // grab the response from modifier
+ logger.debug( std::string( "[req] getting preview response from modifier: " ).append( _handlers[i].name ) );
+ modifier = _handlers[i].symbols.create();
+ response = modifier->preview( request );
+
+ // cleanup
+ logger.debug( std::string( "[req] cleaning up modifier: " ).append( _handlers[i].name ) );
+ _handlers[i].symbols.destroy( modifier );
+
+ // check response status
+ if ( ( response->header()->status() == icap::ResponseHeader::NO_CONTENT )
+ || ( response->header()->status() == icap::ResponseHeader::OK ) ) {
+ // no further action needed, break out of the loop
+ break;
+ }
+
+ if ( response->header()->status() == icap::ResponseHeader::CONTINUE ) {
+
+ // read the full response
+ continue_status = preview_continue( response, request, socket );
+
+ // cleanup
+ delete response;
+
+ // sanity check
+ if ( continue_status ) {
+
+ // success - set the response to NULL
+ response = NULL;
+
+ } else {
+
+ // something went wrong, server error
+ response = new icap::Response( icap::ResponseHeader::SERVER_ERROR );
+
+ }
+
+ // exit the loop
+ break;
+
+ }
+
+ // we shouldn't have got this far
+ logger.info( std::string( "[req] unrecognised preview response from modifier: " ).append( _handlers[i].name ) );
+
+ }
+
+ return response;
+
+ }
+
+
+ icap::Response * RequestHandler::process_modify( icap::Request * request ) throw() {
+
+ icap::Response * response = NULL;
+ Modifier * modifier;
+
+ int i = 0;
+
+ // logger
+ Logger &logger = Logger::instance();
+
+
+ /*
+ * Loop through loaded modifier modules and grab responses
+ *
+ * We will only return the response from the last module
+ * unless a icap::ResponseHeader::OK is received
+ */
+ for ( i = 0 ; i < _handlers_count; i++ ) {
+
+ // sanity check
+ if ( _handlers[i].name == "" ) {
+ logger.info( "[req] modifier not loaded, not trying to get a response" );
+ continue;
+ }
+
+ // grab the response from modifier
+ logger.debug( std::string( "[req] getting modify response from modifier: " ).append( _handlers[i].name ) );
+ modifier = _handlers[i].symbols.create();
+ response = modifier->modify( request );
+
+ // cleanup
+ logger.debug( std::string( "[req] cleaning up modifier: " ).append( _handlers[i].name ) );
+ _handlers[i].symbols.destroy( modifier );
+
+ // status 200 OK means content modified
+ if ( response->header()->status() == icap::ResponseHeader::OK ) {
+ logger.debug( "[req] OK response received, not getting responses from other modifiers" );
+ break;
+ }
+
+ }
+
+ return response;
+
+ }
+
+
+ bool RequestHandler::preview_continue( icap::Response * response, icap::Request * request, socketlibrary::TCPSocket * socket ) throw() {
+
+ bool status = false;
+
+ // logger
+ Logger &logger = Logger::instance();
+
+
+ // sanity check
+ if ( request->payload().ieof ) {
+
+ // we can process a '100 Continue' only if an 'ieof' is not received
+ logger.warn( "[req] illegal '100 Continue' response" );
+
+ } else {
+
+ /* read the full request */
+
+ // send back the response first
+ if ( icap::util::send_response( response, socket ) ) {
+
+ // read the rest of the request
+ status = icap::util::read_req_continue_data( request, socket );
+
+ }
+
+ }
+
+ return status;
+
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/request_handler.h b/src/bitz/request_handler.h
new file mode 100644
index 0000000..3fe99f1
--- /dev/null
+++ b/src/bitz/request_handler.h
@@ -0,0 +1,133 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_REQUEST_HANDLER_H
+#define BITZ_REQUEST_HANDLER_H
+
+#include "modifier.h"
+
+#include <icap/response.h>
+#include <icap/request.h>
+#include <socket/socket.h>
+
+
+namespace bitz {
+
+ class RequestHandler {
+ public:
+
+ struct req_handler_t {
+ std::string method;
+ };
+
+ struct handler_t {
+ std::string name;
+ Modifier::symbols_t symbols;
+ };
+
+ RequestHandler( const std::string &method );
+ virtual ~RequestHandler();
+
+ /**
+ * Returns the request method handled by this handler
+ * @return method
+ */
+ const std::string &method() const throw();
+
+ /**
+ * Process the request and return a new response object. This will also
+ * read from the passed in socket if more data needs to be read.
+ *
+ * @param req_header request header object
+ * @param socket socket object to read the data from
+ * @return response object
+ */
+ virtual icap::Response * process( icap::RequestHeader * req_header, socketlibrary::TCPSocket * socket ) throw();
+
+
+ protected:
+
+ unsigned int _handlers_count;
+ handler_t * _handlers;
+
+ /**
+ * Load a modifier module
+ *
+ * @param file file name / path of the module
+ * @param symbols structure to return the symbols
+ * @return boolean to denote success or failure
+ */
+ bool load_modifier( const std::string &file, Modifier::symbols_t &symbols ) throw();
+
+ /**
+ * Unload a modifier module
+ *
+ * @param modifier pointer to the modifier to unload
+ */
+ void unload_modifier( void * modifier ) throw();
+
+ /**
+ * Load all the configured modifier modules for this request handler
+ */
+ void load_modules() throw();
+
+ /**
+ * Cleanup all the loaded modifier modules
+ */
+ void cleanup_modules() throw();
+
+ /**
+ * Given a request instance and a socket instance to communicate, this method will use the
+ * loaded handler modules to grab a preview response. This will return a icap::Response
+ * object or NULL after processing a '100 Continue' response.
+ *
+ * @param request request object
+ * @param socket socket object to read data from
+ * @return preview response (response object)
+ */
+ icap::Response * process_preview( icap::Request * request, socketlibrary::TCPSocket * socket ) throw();
+
+ /**
+ * This method will use the loaded handler modules to get a response to the request.
+ *
+ * @param request request object
+ * @return response object
+ */
+ icap::Response * process_modify( icap::Request * request ) throw();
+
+ /**
+ * Helper method to set a '100 Continue' response back to the client and read the full request.
+ *
+ * @param response response object with status 100
+ * @param request request object
+ * @param socket socket object to read / write data
+ * @return
+ */
+ bool preview_continue( icap::Response * response, icap::Request * request, socketlibrary::TCPSocket * socket ) throw();
+
+ private:
+
+ req_handler_t _req_handler;
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_REQUEST_HANDLER_H */
+
diff --git a/src/bitz/respmod_request_handler.cpp b/src/bitz/respmod_request_handler.cpp
new file mode 100644
index 0000000..8f99695
--- /dev/null
+++ b/src/bitz/respmod_request_handler.cpp
@@ -0,0 +1,30 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "respmod_request_handler.h"
+
+
+namespace bitz {
+
+ RespmodRequestHandler::RespmodRequestHandler() : RequestHandler( "RESPMOD" ) { }
+
+ RespmodRequestHandler::~RespmodRequestHandler() { }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/respmod_request_handler.h b/src/bitz/respmod_request_handler.h
new file mode 100644
index 0000000..dc1a18a
--- /dev/null
+++ b/src/bitz/respmod_request_handler.h
@@ -0,0 +1,41 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_RESPMOD_REQUEST_HANDLER_H
+#define BITZ_RESPMOD_REQUEST_HANDLER_H
+
+#include "request_handler.h"
+
+
+namespace bitz {
+
+ class RespmodRequestHandler : public RequestHandler {
+ public:
+
+ RespmodRequestHandler();
+ virtual ~RespmodRequestHandler();
+
+ private:
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_RESPMOD_REQUEST_HANDLER_H */
+
diff --git a/src/bitz/util.cpp b/src/bitz/util.cpp
new file mode 100644
index 0000000..8fedc85
--- /dev/null
+++ b/src/bitz/util.cpp
@@ -0,0 +1,122 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "util.h"
+
+#include <cerrno>
+#include <sys/stat.h>
+
+
+namespace bitz {
+
+ namespace util {
+
+ RequestHandler * find_req_handler( req_handlers_t req_handlers, const std::string &req_method ) throw() {
+
+ RequestHandler * req_handler;
+ req_handlers_index_t rh_i;
+ rh_i = req_handlers.find( req_method );
+
+ if ( rh_i != req_handlers.end() ) {
+ req_handler = rh_i->second;
+ } else {
+ req_handler = NULL;
+ }
+
+ return req_handler;
+
+ }
+
+
+ void delete_req_handlers( req_handlers_t req_handlers ) throw() {
+
+ req_handlers_index_t rh_i;
+
+ for ( rh_i = req_handlers.begin(); rh_i != req_handlers.end(); rh_i++ ) {
+ if ( rh_i->first != "OPTIONS" ) {
+ delete rh_i->second;
+ }
+ }
+
+ return;
+
+ }
+
+
+ std::string dirpath( const std::string &path ) throw() {
+ return path.substr( 0, path.find_last_of( '/' ) );
+ }
+
+
+ std::string filename( const std::string &path ) throw() {
+ return path.substr( path.find_last_of( '/' ) );
+ }
+
+
+ bool mkdirp( const std::string &path ) throw() {
+
+ bool r_success = false;
+
+
+ if ( ::mkdir( path.c_str(), 0755 ) == -1 ) {
+
+ switch( errno ) {
+
+ case ENOENT:
+
+ // parent didn't exist, try to create it
+ if ( mkdirp( path.substr( 0, path.find_last_of( '/' ) ) ) ) {
+
+ // try creating the dir again
+ r_success = ( 0 == ::mkdir( path.c_str(), 0755 ) );
+
+ } else {
+
+ // failed to create parent
+ r_success = false;
+ }
+
+ break;
+
+ case EEXIST:
+
+ // already exists
+ r_success = true;
+ break;
+
+ default:
+ r_success = false;
+ break;
+
+ }
+
+ } else {
+ r_success = true;
+ }
+
+
+ return r_success;
+
+ }
+
+
+ } /* end of namespace util */
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/util.h b/src/bitz/util.h
new file mode 100644
index 0000000..da1b544
--- /dev/null
+++ b/src/bitz/util.h
@@ -0,0 +1,92 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_UTIL_H
+#define BITZ_UTIL_H
+
+#include "common.h"
+
+#include <string>
+#include <sstream>
+
+
+namespace bitz {
+
+ namespace util {
+
+ /**
+ * Convert a number into a string
+ *
+ * @param number number to be converted
+ * @return converted string
+ */
+ template <typename T> std::string itoa( T number ) {
+ std::ostringstream ss;
+ ss << number;
+
+ return ss.str();
+ }
+
+ /**
+ * Find a matching request handler for the given method
+ *
+ * @param req_handlers request handlers
+ * @param req_method request method
+ * @return request handler (or null pointer if not found)
+ */
+ RequestHandler * find_req_handler( req_handlers_t req_handlers, const std::string &req_method ) throw();
+
+ /**
+ * Helper method to delete any loaded request handlers apart
+ * from the OPTIONS handler
+ *
+ * @param req_handlers request handlers
+ */
+ void delete_req_handlers( req_handlers_t req_handlers ) throw();
+
+ /**
+ * Extract directory path from a path
+ *
+ * @param path path to extract the directory from
+ * @return directory path
+ */
+ std::string dirpath( const std::string &path ) throw();
+
+ /**
+ * Extract the file name from a path
+ *
+ * @param path path to extract the file name from
+ * @return file name
+ */
+ std::string filename( const std::string &path ) throw();
+
+ /**
+ * Create a directory tree recursively
+ *
+ * @param path full directory path
+ * @return boolean to denote success or failure
+ */
+ bool mkdirp( const std::string &path ) throw();
+
+ } /* end of namespace util */
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_UTIL_H */
+
diff --git a/src/bitz/worker.cpp b/src/bitz/worker.cpp
new file mode 100644
index 0000000..efbeef3
--- /dev/null
+++ b/src/bitz/worker.cpp
@@ -0,0 +1,138 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "worker.h"
+#include "logger.h"
+#include "util.h"
+#include "options_request_handler.h"
+#include "reqmod_request_handler.h"
+#include "respmod_request_handler.h"
+
+#include <icap/util.h>
+#include <icap/request_header.h>
+
+
+namespace bitz {
+
+ Worker::Worker() {
+
+ // load request handlers
+ load_req_handlers();
+
+ }
+
+
+ Worker::~Worker() {
+
+ // logger
+ Logger &logger = Logger::instance();
+ logger.debug( "[worker] exiting" );
+
+ // cleanup request handlers
+ util::delete_req_handlers( _req_handlers );
+ delete _req_handlers["OPTIONS"];
+
+ }
+
+
+ void Worker::run( socketlibrary::TCPServerSocket * server_sock, unsigned int max_requests ) throw() {
+
+ Logger &logger = Logger::instance();
+
+ socketlibrary::TCPSocket * client_sock;
+ icap::RequestHeader * req_header;
+ icap::Response * response;
+ RequestHandler * req_handler;
+
+
+ try {
+
+ while ( max_requests > 0 ) {
+
+ logger.debug( std::string( "[worker] waiting for a connection" ) );
+
+ client_sock = server_sock->accept();
+ logger.debug( std::string( "[worker] new connection accepted on " ).append( client_sock->getForeignAddress() )
+ .append( ":" ).append( util::itoa( client_sock->getForeignPort() ) ) );
+
+ // request header
+ req_header = icap::util::read_req_header( client_sock );
+ logger.debug( std::string( "[worker] request header:\r\n" ).append( req_header->raw_data() ) );
+
+ // try to find a handler for the request
+ req_handler = util::find_req_handler( _req_handlers, req_header->method() );
+
+ if ( req_handler != NULL ) {
+
+ logger.debug( std::string( "[worker] handling request: " ).append( req_header->method() ) );
+
+ // process the request and grab the response
+ response = req_handler->process( req_header, client_sock );
+
+ } else {
+
+ // unsupported request
+ logger.info( std::string( "[worker] unsupported request: " ).append( req_header->method() ) );
+ response = new icap::Response( new icap::ResponseHeader( icap::ResponseHeader::NOT_ALLOWED ) );
+
+ }
+
+ // send the response back to the client
+ icap::util::send_response( response, client_sock );
+
+ // cleanup
+ delete response;
+ delete req_header;
+
+ // destroy / close connection
+ delete client_sock;
+
+ max_requests--;
+
+ }
+
+ } catch( socketlibrary::SocketException &sex ) {
+ logger.error( std::string( "[worker] ERROR: " ).append( sex.what() ) );
+ }
+
+ }
+
+
+ void Worker::load_req_handlers() throw() {
+
+ OptionsRequestHandler * options_handler;
+
+ // OPTIONS handler
+ options_handler = new OptionsRequestHandler();
+ _req_handlers["OPTIONS"] = options_handler;
+
+ /* request handlers */
+
+ // REQMOD
+ _req_handlers["REQMOD"] = new ReqmodRequestHandler();
+ options_handler->register_handler( _req_handlers["REQMOD"] );
+
+ // RESPMOD
+ _req_handlers["RESPMOD"] = new RespmodRequestHandler();
+ options_handler->register_handler( _req_handlers["RESPMOD"] );
+
+ }
+
+} /* end of namespace bitz */
+
diff --git a/src/bitz/worker.h b/src/bitz/worker.h
new file mode 100644
index 0000000..69edbca
--- /dev/null
+++ b/src/bitz/worker.h
@@ -0,0 +1,47 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BITZ_WORKER_H
+#define BITZ_WORKER_H
+
+#include <socket/socket.h>
+
+#include "common.h"
+
+
+namespace bitz {
+
+ class Worker {
+ public:
+ Worker();
+ virtual ~Worker();
+
+ virtual void run( socketlibrary::TCPServerSocket * server_sock, unsigned int max_requests ) throw();
+
+ private:
+ req_handlers_t _req_handlers;
+
+ virtual void load_req_handlers() throw();
+
+ };
+
+} /* end of namespace bitz */
+
+#endif /* !BITZ_WORKER_H */
+
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..e00c1c6
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,82 @@
+/*
+ * bitz-server, An ICAP server implementation in C++
+ * Copyright (C) 2012 Uditha Atukorala
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <cstdlib>
+
+#include <config.h>
+#include "bitz-server.h"
+#include "bitz/config.h"
+#include "bitz/logger.h"
+#include "bitz/util.h"
+
+
+int main( int argc, char **argv ) {
+
+ // initialise the server
+ bitz::server::init();
+
+ // read command line options
+ bitz::server::options_t opt = bitz::server::read_options( argc, argv );
+
+ // initialise configurations
+ bitz::Config &server_config = bitz::Config::instance();
+
+ if ( opt.config_file != "" ) {
+ server_config.initialise( opt.config_file );
+ } else {
+ server_config.initialise();
+ }
+
+ // get a copy of the configs
+ const bitz::config_t &config = server_config.configs();
+
+ // create directories
+ if ( ( opt.debug_flag != 1 ) && (! bitz::util::mkdirp( bitz::util::dirpath( config.pid_file ) ) ) ) {
+ std::cerr << "[core] failed to create run dir" << std::endl;
+ exit( EXIT_FAILURE );
+ }
+
+ if (! bitz::util::mkdirp( bitz::util::dirpath( config.log_file ) ) ) {
+ std::cerr << "[core] failed to create log dir" << std::endl;
+ exit( EXIT_FAILURE );
+ }
+
+
+ // daemonize
+ if ( opt.debug_flag != 1 ) {
+ bitz::server::daemonize( bitz::util::dirpath( config.pid_file ).c_str(), config.pid_file.c_str() );
+ }
+
+ // initialise the logger
+ bitz::Logger &logger = bitz::Logger::instance( config.log_file, config.log_category );
+ logger.info( std::string( PACKAGE_STRING ) + " initialised" );
+
+ // start the server
+ bitz::server::start( config.port, config.max_workers, config.max_worker_requests );
+
+ // run the server
+ bitz::server::run();
+
+ // we should never get here
+ bitz::server::shutdown();
+
+ return( EXIT_SUCCESS );
+
+}
+