pion  5.0.6
src/http_plugin_server.cpp
00001 // ---------------------------------------------------------------------
00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
00003 // ---------------------------------------------------------------------
00004 // Copyright (C) 2007-2014 Splunk Inc.  (https://github.com/splunk/pion)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #include <boost/exception/diagnostic_information.hpp>
00011 #include <pion/error.hpp>
00012 #include <pion/http/plugin_server.hpp>
00013 #include <pion/http/request.hpp>
00014 #include <pion/http/basic_auth.hpp>
00015 #include <pion/http/cookie_auth.hpp>
00016 #include <fstream>
00017 
00018 
00019 namespace pion {    // begin namespace pion
00020 namespace http {    // begin namespace http
00021 
00022 
00023 // plugin_server member functions
00024 
00025 void plugin_server::add_service(const std::string& resource, http::plugin_service *service_ptr)
00026 {
00027     plugin_ptr<http::plugin_service> plugin_ptr;
00028     const std::string clean_resource(strip_trailing_slash(resource));
00029     service_ptr->set_resource(clean_resource);
00030     m_services.add(clean_resource, service_ptr);
00031     http::server::add_resource(clean_resource, boost::ref(*service_ptr));
00032     PION_LOG_INFO(m_logger, "Loaded static web service for resource (" << clean_resource << ")");
00033 }
00034 
00035 void plugin_server::load_service(const std::string& resource, const std::string& service_name)
00036 {
00037     const std::string clean_resource(strip_trailing_slash(resource));
00038     http::plugin_service *service_ptr;
00039     service_ptr = m_services.load(clean_resource, service_name);
00040     http::server::add_resource(clean_resource, boost::ref(*service_ptr));
00041     service_ptr->set_resource(clean_resource);
00042     PION_LOG_INFO(m_logger, "Loaded web service plug-in for resource (" << clean_resource << "): " << service_name);
00043 }
00044 
00045 void plugin_server::set_service_option(const std::string& resource,
00046                                  const std::string& name, const std::string& value)
00047 {
00048     const std::string clean_resource(strip_trailing_slash(resource));
00049     m_services.run(clean_resource, boost::bind(&http::plugin_service::set_option, _1, name, value));
00050     PION_LOG_INFO(m_logger, "Set web service option for resource ("
00051                   << resource << "): " << name << '=' << value);
00052 }
00053 
00054 void plugin_server::load_service_config(const std::string& config_name)
00055 {
00056     std::string config_file;
00057     if (! plugin::find_config_file(config_file, config_name))
00058         BOOST_THROW_EXCEPTION( error::file_not_found() << error::errinfo_file_name(config_name) );
00059     
00060     // open the file for reading
00061     std::ifstream config_stream;
00062     config_stream.open(config_file.c_str(), std::ios::in);
00063     if (! config_stream.is_open())
00064         BOOST_THROW_EXCEPTION( error::open_file() << error::errinfo_file_name(config_name) );
00065     
00066     // parse the contents of the file
00067     http::auth_ptr my_auth_ptr;
00068     enum ParseState {
00069         PARSE_NEWLINE, PARSE_COMMAND, PARSE_RESOURCE, PARSE_VALUE, PARSE_COMMENT, PARSE_USERNAME
00070     } parse_state = PARSE_NEWLINE;
00071     std::string command_string;
00072     std::string resource_string;
00073     std::string username_string;
00074     std::string value_string;
00075     std::string option_name_string;
00076     std::string option_value_string;
00077     int c = config_stream.get();    // read the first character
00078     
00079     while (config_stream) {
00080         switch(parse_state) {
00081         case PARSE_NEWLINE:
00082             // parsing command portion (or beginning of line)
00083             if (c == '#') {
00084                 // line is a comment
00085                 parse_state = PARSE_COMMENT;
00086             } else if (isalpha(c)) {
00087                 // first char in command
00088                 parse_state = PARSE_COMMAND;
00089                 // ignore case for commands
00090                 command_string += tolower(c);
00091             } else if (c != '\r' && c != '\n') {    // check for blank lines
00092                 BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00093             }
00094             break;
00095             
00096         case PARSE_COMMAND:
00097             // parsing command portion (or beginning of line)
00098             if (c == ' ' || c == '\t') {
00099                 // command finished -> check if valid
00100                 if (command_string=="path" || command_string=="auth" || command_string=="restrict") {
00101                     value_string.clear();
00102                     parse_state = PARSE_VALUE;
00103                 } else if (command_string=="service" || command_string=="option") {
00104                     resource_string.clear();
00105                     parse_state = PARSE_RESOURCE;
00106                 } else if (command_string=="user") {
00107                     username_string.clear();
00108                     parse_state = PARSE_USERNAME;
00109                 } else {
00110                     BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00111                 }
00112             } else if (! isalpha(c)) {
00113                 // commands may only contain alpha chars
00114                 BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00115             } else {
00116                 // ignore case for commands
00117                 command_string += tolower(c);
00118             }
00119             break;
00120 
00121         case PARSE_RESOURCE:
00122             // parsing resource portion (/hello)
00123             if (c == ' ' || c == '\t') {
00124                 // check for leading whitespace
00125                 if (! resource_string.empty()) {
00126                     // resource finished
00127                     value_string.clear();
00128                     parse_state = PARSE_VALUE;
00129                 }
00130             } else if (c == '\r' || c == '\n') {
00131                 // line truncated before value
00132                 BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00133             } else {
00134                 // add char to resource
00135                 resource_string += c;
00136             }
00137             break;
00138         
00139         case PARSE_USERNAME:
00140             // parsing username for user command
00141             if (c == ' ' || c == '\t') {
00142                 // check for leading whitespace
00143                 if (! username_string.empty()) {
00144                     // username finished
00145                     value_string.clear();
00146                     parse_state = PARSE_VALUE;
00147                 }
00148             } else if (c == '\r' || c == '\n') {
00149                 // line truncated before value (missing username)
00150                 BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00151             } else {
00152                 // add char to username
00153                 username_string += c;
00154             }
00155             break;
00156         
00157         case PARSE_VALUE:
00158             // parsing value portion
00159             if (c == '\r' || c == '\n') {
00160                 // value is finished
00161                 if (value_string.empty()) {
00162                     // value must not be empty
00163                     BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00164                 } else if (command_string == "path") {
00165                     // finished path command
00166                     try { plugin::add_plugin_directory(value_string); }
00167                     catch (std::exception& e) {
00168                         PION_LOG_WARN(m_logger, boost::diagnostic_information(e));
00169                     }
00170                 } else if (command_string == "auth") {
00171                     // finished auth command
00172                     user_manager_ptr user_mgr(new user_manager);
00173                     if (value_string == "basic"){
00174                         my_auth_ptr.reset(new http::basic_auth(user_mgr));
00175                     }
00176                     else if (value_string == "cookie"){
00177                         my_auth_ptr.reset(new http::cookie_auth(user_mgr));
00178                     }
00179                     else {
00180                         // only basic and cookie authentications are supported
00181                         BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00182                     }
00183                 } else if (command_string == "restrict") {
00184                     // finished restrict command
00185                     if (! my_auth_ptr)
00186                         // Authentication type must be defined before restrict
00187                         BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00188                     else if (value_string.empty())
00189                         // No service defined for restrict parameter
00190                         BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00191                     my_auth_ptr->add_restrict(value_string);
00192                 } else if (command_string == "user") {
00193                     // finished user command
00194                     if (! my_auth_ptr)
00195                         // Authentication type must be defined before users
00196                         BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00197                     else if (value_string.empty())
00198                         // No password defined for user parameter
00199                         BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00200                     my_auth_ptr->add_user(username_string, value_string);
00201                 } else if (command_string == "service") {
00202                     // finished service command
00203                     load_service(resource_string, value_string);
00204                 } else if (command_string == "option") {
00205                     // finished option command
00206                     std::string::size_type pos = value_string.find('=');
00207                     if (pos == std::string::npos)
00208                         BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
00209                     option_name_string = value_string.substr(0, pos);
00210                     option_value_string = value_string.substr(pos + 1);
00211                     set_service_option(resource_string, option_name_string,
00212                                      option_value_string);
00213                 }
00214                 command_string.clear();
00215                 parse_state = PARSE_NEWLINE;
00216             } else if (c == ' ' || c == '\t') {
00217                 // only skip leading whitespace (value may contain spaces, etc)
00218                 if (! value_string.empty())
00219                     value_string += c;
00220             } else {
00221                 // add char to value
00222                 value_string += c;
00223             }
00224             break;
00225         
00226         case PARSE_COMMENT:
00227             // skipping comment line
00228             if (c == '\r' || c == '\n')
00229                 parse_state = PARSE_NEWLINE;
00230             break;
00231         }
00232 
00233         // read the next character
00234         c = config_stream.get();
00235     }
00236     
00237     // update authentication configuration for the server
00238     set_authentication(my_auth_ptr);
00239 }
00240 
00241 }   // end namespace http
00242 }   // end namespace pion