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