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/bitz/request_handler.cpp | 401 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 401 insertions(+) create mode 100644 src/bitz/request_handler.cpp (limited to 'src/bitz/request_handler.cpp') 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 */ + -- cgit v1.2.3