summaryrefslogtreecommitdiff
path: root/src/bitz/request_handler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitz/request_handler.cpp')
-rw-r--r--src/bitz/request_handler.cpp401
1 files changed, 401 insertions, 0 deletions
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 */
+