pion  5.0.6
http_plugin_server.cpp
1 // ---------------------------------------------------------------------
2 // pion: a Boost C++ framework for building lightweight HTTP interfaces
3 // ---------------------------------------------------------------------
4 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
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 <boost/exception/diagnostic_information.hpp>
11 #include <pion/error.hpp>
12 #include <pion/http/plugin_server.hpp>
13 #include <pion/http/request.hpp>
14 #include <pion/http/basic_auth.hpp>
15 #include <pion/http/cookie_auth.hpp>
16 #include <fstream>
17 
18 
19 namespace pion { // begin namespace pion
20 namespace http { // begin namespace http
21 
22 
23 // plugin_server member functions
24 
25 void plugin_server::add_service(const std::string& resource, http::plugin_service *service_ptr)
26 {
28  const std::string clean_resource(strip_trailing_slash(resource));
29  service_ptr->set_resource(clean_resource);
30  m_services.add(clean_resource, service_ptr);
31  http::server::add_resource(clean_resource, boost::ref(*service_ptr));
32  PION_LOG_INFO(m_logger, "Loaded static web service for resource (" << clean_resource << ")");
33 }
34 
35 void plugin_server::load_service(const std::string& resource, const std::string& service_name)
36 {
37  const std::string clean_resource(strip_trailing_slash(resource));
38  http::plugin_service *service_ptr;
39  service_ptr = m_services.load(clean_resource, service_name);
40  http::server::add_resource(clean_resource, boost::ref(*service_ptr));
41  service_ptr->set_resource(clean_resource);
42  PION_LOG_INFO(m_logger, "Loaded web service plug-in for resource (" << clean_resource << "): " << service_name);
43 }
44 
45 void plugin_server::set_service_option(const std::string& resource,
46  const std::string& name, const std::string& value)
47 {
48  const std::string clean_resource(strip_trailing_slash(resource));
49  m_services.run(clean_resource, boost::bind(&http::plugin_service::set_option, _1, name, value));
50  PION_LOG_INFO(m_logger, "Set web service option for resource ("
51  << resource << "): " << name << '=' << value);
52 }
53 
54 void plugin_server::load_service_config(const std::string& config_name)
55 {
56  std::string config_file;
57  if (! plugin::find_config_file(config_file, config_name))
58  BOOST_THROW_EXCEPTION( error::file_not_found() << error::errinfo_file_name(config_name) );
59 
60  // open the file for reading
61  std::ifstream config_stream;
62  config_stream.open(config_file.c_str(), std::ios::in);
63  if (! config_stream.is_open())
64  BOOST_THROW_EXCEPTION( error::open_file() << error::errinfo_file_name(config_name) );
65 
66  // parse the contents of the file
67  http::auth_ptr my_auth_ptr;
68  enum ParseState {
69  PARSE_NEWLINE, PARSE_COMMAND, PARSE_RESOURCE, PARSE_VALUE, PARSE_COMMENT, PARSE_USERNAME
70  } parse_state = PARSE_NEWLINE;
71  std::string command_string;
72  std::string resource_string;
73  std::string username_string;
74  std::string value_string;
75  std::string option_name_string;
76  std::string option_value_string;
77  int c = config_stream.get(); // read the first character
78 
79  while (config_stream) {
80  switch(parse_state) {
81  case PARSE_NEWLINE:
82  // parsing command portion (or beginning of line)
83  if (c == '#') {
84  // line is a comment
85  parse_state = PARSE_COMMENT;
86  } else if (isalpha(c)) {
87  // first char in command
88  parse_state = PARSE_COMMAND;
89  // ignore case for commands
90  command_string += tolower(c);
91  } else if (c != '\r' && c != '\n') { // check for blank lines
92  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
93  }
94  break;
95 
96  case PARSE_COMMAND:
97  // parsing command portion (or beginning of line)
98  if (c == ' ' || c == '\t') {
99  // command finished -> check if valid
100  if (command_string=="path" || command_string=="auth" || command_string=="restrict") {
101  value_string.clear();
102  parse_state = PARSE_VALUE;
103  } else if (command_string=="service" || command_string=="option") {
104  resource_string.clear();
105  parse_state = PARSE_RESOURCE;
106  } else if (command_string=="user") {
107  username_string.clear();
108  parse_state = PARSE_USERNAME;
109  } else {
110  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
111  }
112  } else if (! isalpha(c)) {
113  // commands may only contain alpha chars
114  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
115  } else {
116  // ignore case for commands
117  command_string += tolower(c);
118  }
119  break;
120 
121  case PARSE_RESOURCE:
122  // parsing resource portion (/hello)
123  if (c == ' ' || c == '\t') {
124  // check for leading whitespace
125  if (! resource_string.empty()) {
126  // resource finished
127  value_string.clear();
128  parse_state = PARSE_VALUE;
129  }
130  } else if (c == '\r' || c == '\n') {
131  // line truncated before value
132  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
133  } else {
134  // add char to resource
135  resource_string += c;
136  }
137  break;
138 
139  case PARSE_USERNAME:
140  // parsing username for user command
141  if (c == ' ' || c == '\t') {
142  // check for leading whitespace
143  if (! username_string.empty()) {
144  // username finished
145  value_string.clear();
146  parse_state = PARSE_VALUE;
147  }
148  } else if (c == '\r' || c == '\n') {
149  // line truncated before value (missing username)
150  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
151  } else {
152  // add char to username
153  username_string += c;
154  }
155  break;
156 
157  case PARSE_VALUE:
158  // parsing value portion
159  if (c == '\r' || c == '\n') {
160  // value is finished
161  if (value_string.empty()) {
162  // value must not be empty
163  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
164  } else if (command_string == "path") {
165  // finished path command
166  try { plugin::add_plugin_directory(value_string); }
167  catch (std::exception& e) {
168  PION_LOG_WARN(m_logger, boost::diagnostic_information(e));
169  }
170  } else if (command_string == "auth") {
171  // finished auth command
172  user_manager_ptr user_mgr(new user_manager);
173  if (value_string == "basic"){
174  my_auth_ptr.reset(new http::basic_auth(user_mgr));
175  }
176  else if (value_string == "cookie"){
177  my_auth_ptr.reset(new http::cookie_auth(user_mgr));
178  }
179  else {
180  // only basic and cookie authentications are supported
181  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
182  }
183  } else if (command_string == "restrict") {
184  // finished restrict command
185  if (! my_auth_ptr)
186  // Authentication type must be defined before restrict
187  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
188  else if (value_string.empty())
189  // No service defined for restrict parameter
190  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
191  my_auth_ptr->add_restrict(value_string);
192  } else if (command_string == "user") {
193  // finished user command
194  if (! my_auth_ptr)
195  // Authentication type must be defined before users
196  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
197  else if (value_string.empty())
198  // No password defined for user parameter
199  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
200  my_auth_ptr->add_user(username_string, value_string);
201  } else if (command_string == "service") {
202  // finished service command
203  load_service(resource_string, value_string);
204  } else if (command_string == "option") {
205  // finished option command
206  std::string::size_type pos = value_string.find('=');
207  if (pos == std::string::npos)
208  BOOST_THROW_EXCEPTION( error::bad_config() << error::errinfo_file_name(config_name) );
209  option_name_string = value_string.substr(0, pos);
210  option_value_string = value_string.substr(pos + 1);
211  set_service_option(resource_string, option_name_string,
212  option_value_string);
213  }
214  command_string.clear();
215  parse_state = PARSE_NEWLINE;
216  } else if (c == ' ' || c == '\t') {
217  // only skip leading whitespace (value may contain spaces, etc)
218  if (! value_string.empty())
219  value_string += c;
220  } else {
221  // add char to value
222  value_string += c;
223  }
224  break;
225 
226  case PARSE_COMMENT:
227  // skipping comment line
228  if (c == '\r' || c == '\n')
229  parse_state = PARSE_NEWLINE;
230  break;
231  }
232 
233  // read the next character
234  c = config_stream.get();
235  }
236 
237  // update authentication configuration for the server
238  set_authentication(my_auth_ptr);
239 }
240 
241 } // end namespace http
242 } // end namespace pion
static bool find_config_file(std::string &path_to_file, const std::string &name)
Definition: plugin.hpp:53
void run(PluginRunFunction run_func)
void add_service(const std::string &resource, http::plugin_service *service_ptr)
static void add_plugin_directory(const std::string &dir)
appends a directory to the plug-in search path
Definition: plugin.cpp:59
void set_service_option(const std::string &resource, const std::string &name, const std::string &value)
void set_resource(const std::string &str)
sets the URI stem or resource that is bound to the web service
void add(const std::string &plugin_id, PluginType *plugin_object_ptr)
PluginType * load(const std::string &plugin_id, const std::string &plugin_type)
virtual void set_option(const std::string &name, const std::string &value)
void add_resource(const std::string &resource, request_handler_t request_handler)
static std::string strip_trailing_slash(const std::string &str)
Definition: server.hpp:160
exception thrown if a file is not found
Definition: error.hpp:167
exception thrown if we failed to open a file
Definition: error.hpp:146
logger m_logger
primary logging interface used by this class
Definition: server.hpp:160
exception thrown if there is an error parsing a configuration file
Definition: error.hpp:139
void load_service(const std::string &resource, const std::string &service_name)
void load_service_config(const std::string &config_name)
void set_authentication(http::auth_ptr auth)
Definition: server.hpp:221