pion-net  4.0.9
WebServer.cpp
1 // ------------------------------------------------------------------
2 // pion-net: a C++ framework for building lightweight HTTP interfaces
3 // ------------------------------------------------------------------
4 // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #include <pion/net/WebServer.hpp>
11 #include <pion/net/HTTPRequest.hpp>
12 #include <pion/net/HTTPRequestReader.hpp>
13 #include <pion/net/HTTPResponseWriter.hpp>
14 #include <pion/net/HTTPBasicAuth.hpp>
15 #include <pion/net/HTTPCookieAuth.hpp>
16 #include <fstream>
17 
18 
19 namespace pion { // begin namespace pion
20 namespace net { // begin namespace net (Pion Network Library)
21 
22 
23 // WebServer member functions
24 
25 void WebServer::addService(const std::string& resource, WebService *service_ptr)
26 {
27  PionPluginPtr<WebService> plugin_ptr;
28  const std::string clean_resource(stripTrailingSlash(resource));
29  service_ptr->setResource(clean_resource);
30  // catch exceptions thrown by services since their exceptions may be free'd
31  // from memory before they are caught
32  try {
33  m_services.add(clean_resource, service_ptr);
34  HTTPServer::addResource(clean_resource, boost::ref(*service_ptr));
35  } catch (std::exception& e) {
36  throw WebServiceException(resource, e.what());
37  }
38  PION_LOG_INFO(m_logger, "Loaded static web service for resource (" << clean_resource << ")");
39 }
40 
41 void WebServer::loadService(const std::string& resource, const std::string& service_name)
42 {
43  const std::string clean_resource(stripTrailingSlash(resource));
44  WebService *service_ptr;
45  // catch exceptions thrown by services since their exceptions may be free'd
46  // from memory before they are caught
47  try {
48  service_ptr = m_services.load(clean_resource, service_name);
49  HTTPServer::addResource(clean_resource, boost::ref(*service_ptr));
50  service_ptr->setResource(clean_resource);
51  } catch (std::exception& e) {
52  throw WebServiceException(resource, e.what());
53  }
54  PION_LOG_INFO(m_logger, "Loaded web service plug-in for resource (" << clean_resource << "): " << service_name);
55 }
56 
57 void WebServer::setServiceOption(const std::string& resource,
58  const std::string& name, const std::string& value)
59 {
60  // catch exceptions thrown by services since their exceptions may be free'd
61  // from memory before they are caught
62  const std::string clean_resource(stripTrailingSlash(resource));
63  try {
64  m_services.run(clean_resource, boost::bind(&WebService::setOption, _1, name, value));
66  throw ServiceNotFoundException(resource);
67  } catch (std::exception& e) {
68  throw WebServiceException(resource, e.what());
69  }
70  PION_LOG_INFO(m_logger, "Set web service option for resource ("
71  << resource << "): " << name << '=' << value);
72 }
73 
74 void WebServer::loadServiceConfig(const std::string& config_name)
75 {
76  std::string config_file;
77  if (! PionPlugin::findConfigFile(config_file, config_name))
78  throw ConfigNotFoundException(config_name);
79 
80  // open the file for reading
81  std::ifstream config_stream;
82  config_stream.open(config_file.c_str(), std::ios::in);
83  if (! config_stream.is_open())
84  throw ConfigParsingException(config_name);
85 
86  // parse the contents of the file
87  HTTPAuthPtr auth_ptr;
88  enum ParseState {
89  PARSE_NEWLINE, PARSE_COMMAND, PARSE_RESOURCE, PARSE_VALUE, PARSE_COMMENT, PARSE_USERNAME
90  } parse_state = PARSE_NEWLINE;
91  std::string command_string;
92  std::string resource_string;
93  std::string username_string;
94  std::string value_string;
95  std::string option_name_string;
96  std::string option_value_string;
97  int c = config_stream.get(); // read the first character
98 
99  while (config_stream) {
100  switch(parse_state) {
101  case PARSE_NEWLINE:
102  // parsing command portion (or beginning of line)
103  if (c == '#') {
104  // line is a comment
105  parse_state = PARSE_COMMENT;
106  } else if (isalpha(c)) {
107  // first char in command
108  parse_state = PARSE_COMMAND;
109  // ignore case for commands
110  command_string += tolower(c);
111  } else if (c != '\r' && c != '\n') { // check for blank lines
112  throw ConfigParsingException(config_name);
113  }
114  break;
115 
116  case PARSE_COMMAND:
117  // parsing command portion (or beginning of line)
118  if (c == ' ' || c == '\t') {
119  // command finished -> check if valid
120  if (command_string=="path" || command_string=="auth" || command_string=="restrict") {
121  value_string.clear();
122  parse_state = PARSE_VALUE;
123  } else if (command_string=="service" || command_string=="option") {
124  resource_string.clear();
125  parse_state = PARSE_RESOURCE;
126  } else if (command_string=="user") {
127  username_string.clear();
128  parse_state = PARSE_USERNAME;
129  } else {
130  throw ConfigParsingException(config_name);
131  }
132  } else if (! isalpha(c)) {
133  // commands may only contain alpha chars
134  throw ConfigParsingException(config_name);
135  } else {
136  // ignore case for commands
137  command_string += tolower(c);
138  }
139  break;
140 
141  case PARSE_RESOURCE:
142  // parsing resource portion (/hello)
143  if (c == ' ' || c == '\t') {
144  // check for leading whitespace
145  if (! resource_string.empty()) {
146  // resource finished
147  value_string.clear();
148  parse_state = PARSE_VALUE;
149  }
150  } else if (c == '\r' || c == '\n') {
151  // line truncated before value
152  throw ConfigParsingException(config_name);
153  } else {
154  // add char to resource
155  resource_string += c;
156  }
157  break;
158 
159  case PARSE_USERNAME:
160  // parsing username for user command
161  if (c == ' ' || c == '\t') {
162  // check for leading whitespace
163  if (! username_string.empty()) {
164  // username finished
165  value_string.clear();
166  parse_state = PARSE_VALUE;
167  }
168  } else if (c == '\r' || c == '\n') {
169  // line truncated before value
170  throw AuthConfigException("No username defined for user parameter");
171  } else {
172  // add char to username
173  username_string += c;
174  }
175  break;
176 
177  case PARSE_VALUE:
178  // parsing value portion
179  if (c == '\r' || c == '\n') {
180  // value is finished
181  if (value_string.empty()) {
182  // value must not be empty
183  throw ConfigParsingException(config_name);
184  } else if (command_string == "path") {
185  // finished path command
186  try { PionPlugin::addPluginDirectory(value_string); }
187  catch (std::exception& e) {
188  PION_LOG_WARN(m_logger, e.what());
189  }
190  } else if (command_string == "auth") {
191  // finished auth command
192  PionUserManagerPtr user_manager(new PionUserManager);
193  if (value_string == "basic"){
194  auth_ptr.reset(new HTTPBasicAuth(user_manager));
195  }
196  else if (value_string == "cookie"){
197  auth_ptr.reset(new HTTPCookieAuth(user_manager));
198  }
199  else{
200  throw AuthConfigException("Only basic and cookie authentications are supported");
201  }
202  } else if (command_string == "restrict") {
203  // finished restrict command
204  if (! auth_ptr)
205  throw AuthConfigException("Authentication type must be defined before restrict");
206  else if (value_string.empty())
207  throw AuthConfigException("No service defined for restrict parameter");
208  auth_ptr->addRestrict(value_string);
209  } else if (command_string == "user") {
210  // finished user command
211  if (! auth_ptr)
212  throw AuthConfigException("Authentication type must be defined before users");
213  else if (value_string.empty())
214  throw AuthConfigException("No password defined for user parameter");
215  auth_ptr->addUser(username_string, value_string);
216  } else if (command_string == "service") {
217  // finished service command
218  loadService(resource_string, value_string);
219  } else if (command_string == "option") {
220  // finished option command
221  std::string::size_type pos = value_string.find('=');
222  if (pos == std::string::npos)
223  throw ConfigParsingException(config_name);
224  option_name_string = value_string.substr(0, pos);
225  option_value_string = value_string.substr(pos + 1);
226  setServiceOption(resource_string, option_name_string,
227  option_value_string);
228  }
229  command_string.clear();
230  parse_state = PARSE_NEWLINE;
231  } else if (c == ' ' || c == '\t') {
232  // only skip leading whitespace (value may contain spaces, etc)
233  if (! value_string.empty())
234  value_string += c;
235  } else {
236  // add char to value
237  value_string += c;
238  }
239  break;
240 
241  case PARSE_COMMENT:
242  // skipping comment line
243  if (c == '\r' || c == '\n')
244  parse_state = PARSE_NEWLINE;
245  break;
246  }
247 
248  // read the next character
249  c = config_stream.get();
250  }
251 
252  // update authentication configuration for the server
253  setAuthentication(auth_ptr);
254 }
255 
256 } // end namespace net
257 } // end namespace pion
258