PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/boost/cgi/common/request_base.hpp

http://github.com/darrengarvey/cgi
C++ Header | 439 lines | 308 code | 58 blank | 73 comment | 25 complexity | 0a3b796f535f857bff7ef0146b5b82cb MD5 | raw file
  1. // -- common/request_base.hpp --
  2. //
  3. // Copyright (c) Darren Garvey 2007.
  4. // Distributed under the Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. ////////////////////////////////////////////////////////////////
  9. //
  10. // Defines `request_base` - a base class for all CGI requests.
  11. // Contains common data members and functionality.
  12. //
  13. #ifndef CGI_COMMON_REQUEST_BASE_HPP_INCLUDE_
  14. #define CGI_COMMON_REQUEST_BASE_HPP_INCLUDE_
  15. #include <string>
  16. ////////////////////////////////////////////////////////////////
  17. #include <boost/asio/buffer.hpp>
  18. #include <boost/system/error_code.hpp>
  19. #include <boost/algorithm/string/find.hpp>
  20. #include <boost/fusion/include/vector.hpp>
  21. ////////////////////////////////////////////////////////////////
  22. #include "boost/cgi/common/map.hpp"
  23. #include "boost/cgi/common/form_part.hpp"
  24. #include "boost/cgi/common/parse_options.hpp"
  25. #include "boost/cgi/common/protocol_traits.hpp"
  26. #include "boost/cgi/common/request_status.hpp"
  27. #include "boost/cgi/detail/extract_params.hpp"
  28. #include "boost/cgi/detail/save_environment.hpp"
  29. #include "boost/cgi/http/status_code.hpp"
  30. #include "boost/cgi/config.hpp"
  31. #ifdef BOOST_CGI_ENABLE_SESSIONS
  32. # include "boost/cgi/utility/sessions.hpp"
  33. # include <boost/uuid/uuid.hpp>
  34. # include <boost/uuid/uuid_generators.hpp>
  35. # include <boost/uuid/uuid_io.hpp>
  36. #endif // BOOST_CGI_ENABLE_SESSIONS
  37. BOOST_CGI_NAMESPACE_BEGIN
  38. namespace detail {
  39. template<typename ImplType, typename Service>
  40. struct callback_functor
  41. {
  42. callback_functor(ImplType& impl, Service* service)
  43. : impl_(impl)
  44. , service_(service)
  45. {
  46. }
  47. std::size_t operator()(boost::system::error_code& ec)
  48. {
  49. return service_->read_some(impl_, ec);
  50. }
  51. private:
  52. ImplType& impl_;
  53. Service* service_;
  54. };
  55. } // namespace detail
  56. namespace common {
  57. /// ABC that defines the common interface for basic_request<>s
  58. /**
  59. * This class provides generic member functions that can be used by any
  60. * request type.
  61. */
  62. template<typename Protocol>
  63. class request_base
  64. {
  65. public:
  66. typedef common::request_base<Protocol> base_type;
  67. typedef Protocol protocol_type;
  68. typedef protocol_traits<Protocol> traits;
  69. typedef typename traits::buffer_type buffer_type;
  70. typedef typename traits::char_type char_type;
  71. typedef typename traits::client_type client_type;
  72. typedef typename traits::connection_type connection_type;
  73. typedef typename traits::const_buffers_type const_buffers_type;
  74. typedef typename traits::form_parser_type form_parser_type;
  75. typedef typename traits::mutable_buffers_type mutable_buffers_type;
  76. typedef typename traits::protocol_service_type protocol_service_type;
  77. typedef typename traits::request_type request_type;
  78. typedef typename traits::string_type string_type;
  79. typedef typename connection_type::pointer conn_ptr;
  80. #ifdef BOOST_CGI_ENABLE_SESSIONS
  81. typedef typename traits::uuid_generator_type uuid_generator_type;
  82. typedef typename traits::session_manager_type session_manager_type;
  83. #endif // BOOST_CGI_ENABLE_SESSIONS
  84. protected:
  85. // impl_base is the common base class for all request types'
  86. // implementation_type and should be inherited by it.
  87. struct impl_base
  88. {
  89. typedef impl_base base_type;
  90. typedef Protocol protocol_type;
  91. typedef protocol_traits<Protocol> traits;
  92. typedef typename traits::buffer_type buffer_type;
  93. typedef typename traits::char_type char_type;
  94. typedef typename traits::client_type client_type;
  95. typedef typename traits::connection_type connection_type;
  96. typedef typename traits::const_buffers_type const_buffers_type;
  97. typedef typename traits::form_parser_type form_parser_type;
  98. typedef typename traits::mutable_buffers_type mutable_buffers_type;
  99. typedef typename traits::protocol_service_type protocol_service_type;
  100. typedef typename traits::request_type request_type;
  101. typedef typename traits::string_type string_type;
  102. typedef typename connection_type::pointer conn_ptr;
  103. /**
  104. * If you want to add a new data type to a request you need to:
  105. * > Update this file (just below)
  106. * > Update source_enums.hpp
  107. * > Update map.hpp with a new map type
  108. * > Add a member variable to basic_request<>
  109. */
  110. typedef boost::fusion::vector<
  111. common::env_map, common::get_map
  112. , common::post_map, common::cookie_map
  113. , common::upload_map, common::session_map
  114. > var_map_type;
  115. /// Construct.
  116. impl_base()
  117. : service_(NULL)
  118. , vars_(), post_buffer_()
  119. , stdin_parsed_(false)
  120. , all_done_(false)
  121. , bytes_left_(0)
  122. , http_status_(common::http::no_content)
  123. , request_status_(common::unloaded)
  124. , client_()
  125. , fp_(NULL)
  126. {}
  127. bool stdin_parsed() { return stdin_parsed_; }
  128. common::http::status_code& http_status() { return http_status_; }
  129. common::request_status status() const { return request_status_; }
  130. void status(common::request_status& st) { request_status_ = st; }
  131. mutable_buffers_type prepare(std::size_t size)
  132. {
  133. // Make sure we're not trying to make a zero-sized buffer.
  134. BOOST_ASSERT(size && "Attempting to allocate a zero-sized buffer.");
  135. std::size_t bufsz(post_buffer_.size());
  136. post_buffer_.resize(bufsz + size);
  137. return boost::asio::buffer(&post_buffer_[bufsz], size);
  138. }
  139. protocol_service_type* service_;
  140. var_map_type vars_;
  141. buffer_type post_buffer_;
  142. /// Whether the post data has been parsed yet.
  143. bool stdin_parsed_;
  144. bool all_done_;
  145. // The number of bytes left to read (ie. content_length - bytes_read)
  146. std::size_t bytes_left_;
  147. common::http::status_code http_status_;
  148. common::request_status request_status_;
  149. client_type client_;
  150. boost::scoped_ptr<form_parser_type> fp_;
  151. std::vector<common::form_part> form_parts_;
  152. };
  153. public:
  154. /// Get the request ID of a FastCGI request, or 1.
  155. template<typename ImplType>
  156. boost::uint16_t const& request_id(ImplType& impl) const
  157. {
  158. return impl.client_.request_id();
  159. }
  160. /// Load the base_environment into the current environment.
  161. /**
  162. * Parsed the base_environment and add it to the current request's
  163. * environment. This overwrites any environment variables with the
  164. * existing key.
  165. *
  166. * If `is_command_line` is true, then the first argument is skipped as
  167. * this is the name of the program and ignored. Using it actually causes
  168. * a crash on Windows (MSVC 9) anyway: I'm not exactly sure why.
  169. */
  170. template<typename ImplType>
  171. void load_environment(
  172. ImplType& impl,
  173. char** base_environment,
  174. bool is_command_line
  175. )
  176. {
  177. if (is_command_line && base_environment != NULL)
  178. ++base_environment;
  179. detail::save_environment(env_vars(impl.vars_), base_environment);
  180. }
  181. /// Read some data into the internal buffer.
  182. template<typename ImplType>
  183. std::size_t
  184. read_some(ImplType& impl, boost::system::error_code& ec)
  185. {
  186. return impl.client_.read_some(impl.prepare(64), ec);
  187. }
  188. /// Read some data from the client into the supplied buffer.
  189. template<typename ImplType, typename MutableBufferSequence>
  190. std::size_t
  191. read_some(ImplType& impl, const MutableBufferSequence& buf
  192. , boost::system::error_code& ec)
  193. {
  194. return impl.client_.read_some(buf,ec);
  195. }
  196. template<typename ImplType>
  197. void destroy(ImplType& impl)
  198. {
  199. }
  200. /// Return the connection associated with the request
  201. template<typename ImplType>
  202. client_type& client(ImplType& impl)
  203. {
  204. return impl.client_;
  205. }
  206. template<typename ImplType>
  207. void set_service(
  208. ImplType& impl,
  209. typename ImplType::protocol_service_type& ps
  210. )
  211. {
  212. impl.service_ = &ps;
  213. }
  214. /// Get the request status.
  215. template<typename ImplType>
  216. common::request_status status(ImplType& impl) const
  217. {
  218. return impl.status();
  219. }
  220. /// Set the request status.
  221. template<typename ImplType>
  222. void status(ImplType& impl, common::request_status status)
  223. {
  224. impl.status(status);
  225. }
  226. /// Close the request.
  227. template<typename ImplType>
  228. int close(ImplType& impl
  229. , const http::status_code& hsc = http::ok
  230. , int program_status = 0)
  231. {
  232. status(closed);
  233. return program_status;
  234. }
  235. /// Return if the request is still open.
  236. template<typename ImplType>
  237. bool is_open(ImplType& impl)
  238. {
  239. return !impl.all_done_
  240. && impl.status() >= common::accepted
  241. && impl.status() <= common::aborted
  242. && impl.client_.is_open();
  243. }
  244. /// Synchronously read/parse the request meta-data
  245. template<typename ImplType>
  246. boost::system::error_code
  247. load(ImplType& impl, common::parse_options parse_opts
  248. , boost::system::error_code& ec)
  249. {
  250. if (parse_opts & common::parse_env)
  251. {
  252. if (!read_env_vars(impl, ec)) // returns an error_code
  253. return ec;
  254. }
  255. std::string const& cl = env_vars(impl.vars_)["CONTENT_LENGTH"];
  256. // This will throw if the content-length isn't a valid number
  257. // (which shouldn't ever happen).
  258. impl.bytes_left_
  259. = cl.empty() ? 0 : boost::lexical_cast<std::size_t>(cl);
  260. impl.client_.bytes_left() = impl.bytes_left_;
  261. // We could check the request method to determine if the query string
  262. // should be parsed, but it is useful to always parse it. AFAIK this is
  263. // portable.
  264. if (parse_opts & common::parse_get_only)
  265. {
  266. if (!parse_get_vars(impl, ec))
  267. return ec;
  268. }
  269. std::string const& request_method
  270. = env_vars(impl.vars_)["REQUEST_METHOD"];
  271. if ((request_method == "POST" || request_method == "PUT")
  272. && parse_opts & common::parse_post_only)
  273. {
  274. if (!parse_post_vars(impl, ec))
  275. return ec;
  276. }
  277. if (parse_opts & common::parse_cookies_only)
  278. {
  279. if (!parse_cookie_vars(impl, ec)) // returns an error_code
  280. return ec;
  281. }
  282. status(impl, common::loaded);
  283. return ec;
  284. }
  285. /// Read and parse the cgi GET meta variables
  286. template<typename ImplType>
  287. boost::system::error_code
  288. parse_get_vars(ImplType& impl, boost::system::error_code& ec)
  289. {
  290. if (!(status(impl) & common::get_read))
  291. {
  292. std::string const& vars (env_vars(impl.vars_)["QUERY_STRING"]);
  293. if (!vars.empty())
  294. detail::extract_params(vars, get_vars(impl.vars_)
  295. , boost::char_separator<char>
  296. ("", "=&", boost::keep_empty_tokens)
  297. , ec);
  298. status(impl, (common::request_status)(status(impl) | common::get_read));
  299. }
  300. return ec;
  301. }
  302. /// Read and parse the HTTP_COOKIE meta variable.
  303. /**
  304. * Note: Do not URL decode the cookie values.
  305. *
  306. * @param cookie_key The name of the header that the cookie should appear
  307. * as. eg. this is HTTP_COOKIE for CGI-like protocols and Cookie
  308. * for HTTP.
  309. */
  310. template<typename ImplType>
  311. boost::system::error_code
  312. parse_cookie_vars(ImplType& impl, const char* cookie_key, boost::system::error_code& ec)
  313. {
  314. if (!(status(impl) & common::cookies_read))
  315. {
  316. std::string const& vars (env_vars(impl.vars_)[cookie_key]);
  317. if (!vars.empty())
  318. detail::extract_params(vars, cookie_vars(impl.vars_)
  319. , boost::char_separator<char>
  320. ("", "=;", boost::keep_empty_tokens)
  321. , ec, false);
  322. status(impl, (common::request_status)(status(impl) | common::cookies_read));
  323. }
  324. return ec;
  325. }
  326. /// Read and parse the cgi POST meta variables.
  327. template<typename ImplType, typename Callback>
  328. boost::system::error_code&
  329. parse_post_vars(ImplType& impl, Callback callback, boost::system::error_code& ec)
  330. {
  331. if (!(status(impl) & common::post_read))
  332. {
  333. if (!impl.fp_)
  334. // Construct a form_parser instance.
  335. impl.fp_.reset(new typename ImplType::form_parser_type());
  336. // Create a context for this request.
  337. typename ImplType::form_parser_type::context
  338. context
  339. = { env_vars(impl.vars_)["CONTENT_TYPE"]
  340. , env_vars(impl.vars_)["CONTENT_LENGTH"]
  341. , impl.post_buffer_
  342. , impl.client_.bytes_left_
  343. , post_vars(impl.vars_)
  344. , upload_vars(impl.vars_)
  345. , callback
  346. , impl.stdin_parsed_
  347. , env_vars(impl.vars_)["REMOTE_ADDR"]
  348. };
  349. // Parse the current request.
  350. impl.fp_->parse(context, ec);
  351. status(impl, (common::request_status)(status(impl) | common::post_read));
  352. }
  353. return ec;
  354. }
  355. #ifdef BOOST_CGI_ENABLE_SESSIONS
  356. public:
  357. /// Get the session manager.
  358. session_manager_type& session_manager() { return session_mgr_; }
  359. /// Get the session manager.
  360. session_manager_type const& session_manager() const { return session_mgr_; }
  361. /// Get a new UUID as a string, suitable as a session id.
  362. string_type make_session_id()
  363. {
  364. string_type val;
  365. try {
  366. val = boost::lexical_cast<string_type>(make_uuid());
  367. } catch (...) {
  368. std::cerr<< "Caught error." << std::endl;
  369. }
  370. return val;
  371. }
  372. /// Generate a new UUID.
  373. boost::uuids::uuid make_uuid() { return generator_(); }
  374. private:
  375. session_manager_type session_mgr_;
  376. uuid_generator_type generator_;
  377. #endif // BOOST_CGI_ENABLE_SESSIONS
  378. };
  379. } // namespace common
  380. BOOST_CGI_NAMESPACE_END
  381. #endif // CGI_COMMON_REQUEST_BASE_HPP_INCLUDE_