pion-net
4.0.9
|
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