# net/src/WebServer.cpp

    00001 // ------------------------------------------------------------------
    00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
    00003 // ------------------------------------------------------------------
    00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
    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 <pion/net/WebServer.hpp>
    00011 #include <pion/net/HTTPRequest.hpp>
    00012 #include <pion/net/HTTPRequestReader.hpp>
    00013 #include <pion/net/HTTPResponseWriter.hpp>
    00014 #include <pion/net/HTTPBasicAuth.hpp>
    00015 #include <pion/net/HTTPCookieAuth.hpp>
    00016 #include <fstream>
    00017
    00018
    00019 namespace pion {    // begin namespace pion
    00020 namespace net {     // begin namespace net (Pion Network Library)
    00021
    00022
    00023 // WebServer member functions
    00024
    00025 void WebServer::addService(const std::string& resource, WebService *service_ptr)
    00026 {
    00027     PionPluginPtr<WebService> plugin_ptr;
    00028     const std::string clean_resource(stripTrailingSlash(resource));
    00029     service_ptr->setResource(clean_resource);
    00030     // catch exceptions thrown by services since their exceptions may be free'd
    00031     // from memory before they are caught
    00032     try {
    00033         m_services.add(clean_resource, service_ptr);
    00034         HTTPServer::addResource(clean_resource, boost::ref(*service_ptr));
    00035     } catch (std::exception& e) {
    00036         throw WebServiceException(resource, e.what());
    00037     }
    00038     PION_LOG_INFO(m_logger, "Loaded static web service for resource (" << clean_resource << ")");
    00039 }
    00040
    00041 void WebServer::loadService(const std::string& resource, const std::string& service_name)
    00042 {
    00043     const std::string clean_resource(stripTrailingSlash(resource));
    00044     WebService *service_ptr;
    00045     // catch exceptions thrown by services since their exceptions may be free'd
    00046     // from memory before they are caught
    00047     try {
    00048         service_ptr = m_services.load(clean_resource, service_name);
    00049         HTTPServer::addResource(clean_resource, boost::ref(*service_ptr));
    00050         service_ptr->setResource(clean_resource);
    00051     } catch (std::exception& e) {
    00052         throw WebServiceException(resource, e.what());
    00053     }
    00054     PION_LOG_INFO(m_logger, "Loaded web service plug-in for resource (" << clean_resource << "): " << service_name);
    00055 }
    00056
    00057 void WebServer::setServiceOption(const std::string& resource,
    00058                                  const std::string& name, const std::string& value)
    00059 {
    00060     // catch exceptions thrown by services since their exceptions may be free'd
    00061     // from memory before they are caught
    00062     const std::string clean_resource(stripTrailingSlash(resource));
    00063     try {
    00064         m_services.run(clean_resource, boost::bind(&WebService::setOption, _1, name, value));
    00065     } catch (PluginManager<WebService>::PluginNotFoundException&) {
    00066         throw ServiceNotFoundException(resource);
    00067     } catch (std::exception& e) {
    00068         throw WebServiceException(resource, e.what());
    00069     }
    00070     PION_LOG_INFO(m_logger, "Set web service option for resource ("
    00071                   << resource << "): " << name << '=' << value);
    00072 }
    00073
    00074 void WebServer::loadServiceConfig(const std::string& config_name)
    00075 {
    00076     std::string config_file;
    00077     if (! PionPlugin::findConfigFile(config_file, config_name))
    00078         throw ConfigNotFoundException(config_name);
    00079
    00080     // open the file for reading
    00081     std::ifstream config_stream;
    00082     config_stream.open(config_file.c_str(), std::ios::in);
    00083     if (! config_stream.is_open())
    00084         throw ConfigParsingException(config_name);
    00085
    00086     // parse the contents of the file
    00087     HTTPAuthPtr auth_ptr;
    00088     enum ParseState {
    00089         PARSE_NEWLINE, PARSE_COMMAND, PARSE_RESOURCE, PARSE_VALUE, PARSE_COMMENT, PARSE_USERNAME
    00090     } parse_state = PARSE_NEWLINE;
    00091     std::string command_string;
    00092     std::string resource_string;
    00093     std::string username_string;
    00094     std::string value_string;
    00095     std::string option_name_string;
    00096     std::string option_value_string;
    00097     int c = config_stream.get();    // read the first character
    00098
    00099     while (config_stream) {
    00100         switch(parse_state) {
    00101         case PARSE_NEWLINE:
    00102             // parsing command portion (or beginning of line)
    00103             if (c == '#') {
    00104                 // line is a comment
    00105                 parse_state = PARSE_COMMENT;
    00106             } else if (isalpha(c)) {
    00107                 // first char in command
    00108                 parse_state = PARSE_COMMAND;
    00109                 // ignore case for commands
    00110                 command_string += tolower(c);
    00111             } else if (c != '\r' && c != '\n') {    // check for blank lines
    00112                 throw ConfigParsingException(config_name);
    00113             }
    00114             break;
    00115
    00116         case PARSE_COMMAND:
    00117             // parsing command portion (or beginning of line)
    00118             if (c == ' ' || c == '\t') {
    00119                 // command finished -> check if valid
    00120                 if (command_string=="path" || command_string=="auth" || command_string=="restrict") {
    00121                     value_string.clear();
    00122                     parse_state = PARSE_VALUE;
    00123                 } else if (command_string=="service" || command_string=="option") {
    00124                     resource_string.clear();
    00125                     parse_state = PARSE_RESOURCE;
    00126                 } else if (command_string=="user") {
    00127                     username_string.clear();
    00128                     parse_state = PARSE_USERNAME;
    00129                 } else {
    00130                     throw ConfigParsingException(config_name);
    00131                 }
    00132             } else if (! isalpha(c)) {
    00133                 // commands may only contain alpha chars
    00134                 throw ConfigParsingException(config_name);
    00135             } else {
    00136                 // ignore case for commands
    00137                 command_string += tolower(c);
    00138             }
    00139             break;
    00140
    00141         case PARSE_RESOURCE:
    00142             // parsing resource portion (/hello)
    00143             if (c == ' ' || c == '\t') {
    00144                 // check for leading whitespace
    00145                 if (! resource_string.empty()) {
    00146                     // resource finished
    00147                     value_string.clear();
    00148                     parse_state = PARSE_VALUE;
    00149                 }
    00150             } else if (c == '\r' || c == '\n') {
    00151                 // line truncated before value
    00152                 throw ConfigParsingException(config_name);
    00153             } else {
    00154                 // add char to resource
    00155                 resource_string += c;
    00156             }
    00157             break;
    00158
    00159         case PARSE_USERNAME:
    00160             // parsing username for user command
    00161             if (c == ' ' || c == '\t') {
    00162                 // check for leading whitespace
    00163                 if (! username_string.empty()) {
    00164                     // username finished
    00165                     value_string.clear();
    00166                     parse_state = PARSE_VALUE;
    00167                 }
    00168             } else if (c == '\r' || c == '\n') {
    00169                 // line truncated before value
    00170                 throw AuthConfigException("No username defined for user parameter");
    00171             } else {
    00172                 // add char to username
    00173                 username_string += c;
    00174             }
    00175             break;
    00176
    00177         case PARSE_VALUE:
    00178             // parsing value portion
    00179             if (c == '\r' || c == '\n') {
    00180                 // value is finished
    00181                 if (value_string.empty()) {
    00182                     // value must not be empty
    00183                     throw ConfigParsingException(config_name);
    00184                 } else if (command_string == "path") {
    00185                     // finished path command
    00186                     try { PionPlugin::addPluginDirectory(value_string); }
    00187                     catch (std::exception& e) {
    00188                         PION_LOG_WARN(m_logger, e.what());
    00189                     }
    00190                 } else if (command_string == "auth") {
    00191                     // finished auth command
    00192                     PionUserManagerPtr user_manager(new PionUserManager);
    00193                     if (value_string == "basic"){
    00194                         auth_ptr.reset(new HTTPBasicAuth(user_manager));
    00195                     }
    00196                     else if (value_string == "cookie"){
    00197                         auth_ptr.reset(new HTTPCookieAuth(user_manager));
    00198                     }
    00199                     else{
    00200                         throw AuthConfigException("Only basic and cookie authentications are supported");
    00201                     }
    00202                 } else if (command_string == "restrict") {
    00203                     // finished restrict command
    00204                     if (! auth_ptr)
    00205                         throw AuthConfigException("Authentication type must be defined before restrict");
    00206                     else if (value_string.empty())
    00207                         throw AuthConfigException("No service defined for restrict parameter");
    00208                     auth_ptr->addRestrict(value_string);
    00209                 } else if (command_string == "user") {
    00210                     // finished user command
    00211                     if (! auth_ptr)
    00212                         throw AuthConfigException("Authentication type must be defined before users");
    00213                     else if (value_string.empty())
    00214                         throw AuthConfigException("No password defined for user parameter");
    00215                     auth_ptr->addUser(username_string, value_string);
    00216                 } else if (command_string == "service") {
    00217                     // finished service command
    00218                     loadService(resource_string, value_string);
    00219                 } else if (command_string == "option") {
    00220                     // finished option command
    00221                     std::string::size_type pos = value_string.find('=');
    00222                     if (pos == std::string::npos)
    00223                         throw ConfigParsingException(config_name);
    00224                     option_name_string = value_string.substr(0, pos);
    00225                     option_value_string = value_string.substr(pos + 1);
    00226                     setServiceOption(resource_string, option_name_string,
    00227                                      option_value_string);
    00228                 }
    00229                 command_string.clear();
    00230                 parse_state = PARSE_NEWLINE;
    00231             } else if (c == ' ' || c == '\t') {
    00232                 // only skip leading whitespace (value may contain spaces, etc)
    00233                 if (! value_string.empty())
    00234                     value_string += c;
    00235             } else {
    00236                 // add char to value
    00237                 value_string += c;
    00238             }
    00239             break;
    00240
    00241         case PARSE_COMMENT:
    00242             // skipping comment line
    00243             if (c == '\r' || c == '\n')
    00244                 parse_state = PARSE_NEWLINE;
    00245             break;
    00246         }
    00247
    00248         // read the next character
    00249         c = config_stream.get();
    00250     }
    00251
    00252     // update authentication configuration for the server
    00253     setAuthentication(auth_ptr);
    00254 }
    00255
    00256 }   // end namespace net
    00257 }   // end namespace pion
    00258