From bb9bc9051629c3319c56785c2f4ae0e605d76329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Sat, 21 Nov 2015 14:51:17 +0100 Subject: Initial import of bitz-server version 0.1.6-1 --- src/Makefile.am | 32 +++ src/bitz-server.cpp | 472 +++++++++++++++++++++++++++++++++++ src/bitz-server.h | 113 +++++++++ src/bitz/common.h | 40 +++ src/bitz/config.cpp | 190 ++++++++++++++ src/bitz/config.h | 95 +++++++ src/bitz/exception.cpp | 40 +++ src/bitz/exception.h | 43 ++++ src/bitz/logger.cpp | 98 ++++++++ src/bitz/logger.h | 62 +++++ src/bitz/manager.cpp | 223 +++++++++++++++++ src/bitz/manager.h | 87 +++++++ src/bitz/manager_exception.cpp | 32 +++ src/bitz/manager_exception.h | 36 +++ src/bitz/modifier.cpp | 29 +++ src/bitz/modifier.h | 70 ++++++ src/bitz/options_request_handler.cpp | 57 +++++ src/bitz/options_request_handler.h | 51 ++++ src/bitz/reqmod_request_handler.cpp | 30 +++ src/bitz/reqmod_request_handler.h | 41 +++ src/bitz/request_handler.cpp | 401 +++++++++++++++++++++++++++++ src/bitz/request_handler.h | 133 ++++++++++ src/bitz/respmod_request_handler.cpp | 30 +++ src/bitz/respmod_request_handler.h | 41 +++ src/bitz/util.cpp | 122 +++++++++ src/bitz/util.h | 92 +++++++ src/bitz/worker.cpp | 138 ++++++++++ src/bitz/worker.h | 47 ++++ src/main.cpp | 82 ++++++ 29 files changed, 2927 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/bitz-server.cpp create mode 100644 src/bitz-server.h create mode 100644 src/bitz/common.h create mode 100644 src/bitz/config.cpp create mode 100644 src/bitz/config.h create mode 100644 src/bitz/exception.cpp create mode 100644 src/bitz/exception.h create mode 100644 src/bitz/logger.cpp create mode 100644 src/bitz/logger.h create mode 100644 src/bitz/manager.cpp create mode 100644 src/bitz/manager.h create mode 100644 src/bitz/manager_exception.cpp create mode 100644 src/bitz/manager_exception.h create mode 100644 src/bitz/modifier.cpp create mode 100644 src/bitz/modifier.h create mode 100644 src/bitz/options_request_handler.cpp create mode 100644 src/bitz/options_request_handler.h create mode 100644 src/bitz/reqmod_request_handler.cpp create mode 100644 src/bitz/reqmod_request_handler.h create mode 100644 src/bitz/request_handler.cpp create mode 100644 src/bitz/request_handler.h create mode 100644 src/bitz/respmod_request_handler.cpp create mode 100644 src/bitz/respmod_request_handler.h create mode 100644 src/bitz/util.cpp create mode 100644 src/bitz/util.h create mode 100644 src/bitz/worker.cpp create mode 100644 src/bitz/worker.h create mode 100644 src/main.cpp (limited to 'src') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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=]" + << 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 + + +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 +#include + +#include "request_handler.h" + + +namespace bitz { + + /* request handlers type */ + typedef std::map 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 +#include + + +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 +#include + + +#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 © ); + Config &operator=( const Config © ); + + 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 // For errno +#include // 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 // for exception class +#include // 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 +#include + +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 +#include + +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 © ); + Logger &operator=( const Logger © ); + }; + +} /* 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 +#include +#include + + +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 // pid_t, fork() etc. +#include // 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 +#include + + +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 _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 +#include + + +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 +#include +#include + + +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 +#include + + +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 +#include + + +namespace bitz { + + namespace util { + + /** + * Convert a number into a string + * + * @param number number to be converted + * @return converted string + */ + template 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 +#include + + +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 + +#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 + +#include +#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 ); + +} + -- cgit v1.2.3