/boost/cgi/basic_request.hpp

http://github.com/darrengarvey/cgi · C++ Header · 668 lines · 367 code · 77 blank · 224 comment · 25 complexity · 5ed9e545f482abe2ce2ba4f5b2f2b1e1 MD5 · raw file

  1. // -- basic_request.hpp --
  2. //
  3. // Copyright (c) 2007-2009 Darren Garvey.
  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 the basic_request<> class; the main entry-point to the
  11. // library.
  12. //
  13. ////////////////////////////////////////////////////////////////
  14. #ifndef CGI_BASIC_REQUEST_HPP_INCLUDED__
  15. #define CGI_BASIC_REQUEST_HPP_INCLUDED__
  16. #include "boost/cgi/detail/push_options.hpp"
  17. #include <boost/shared_ptr.hpp>
  18. #include <boost/noncopyable.hpp>
  19. #include <boost/system/error_code.hpp>
  20. ///////////////////////////////////////////////////////////
  21. #include "boost/cgi/common/request_data.hpp"
  22. #include "boost/cgi/common/form_part.hpp"
  23. #include "boost/cgi/common/map.hpp"
  24. #include "boost/cgi/common/parse_options.hpp"
  25. #include "boost/cgi/common/path_info.hpp"
  26. #include "boost/cgi/common/protocol_traits.hpp"
  27. #include "boost/cgi/common/request_service.hpp"
  28. #include "boost/cgi/common/request_status.hpp"
  29. #include "boost/cgi/common/role_type.hpp"
  30. #include "boost/cgi/common/source_enums.hpp"
  31. #include "boost/cgi/detail/basic_io_object.hpp"
  32. #include "boost/cgi/detail/throw_error.hpp"
  33. #include "boost/cgi/fwd/basic_request_fwd.hpp"
  34. #include "boost/cgi/fwd/basic_protocol_service_fwd.hpp"
  35. #include "boost/cgi/http/status_code.hpp"
  36. #ifdef BOOST_CGI_ENABLE_SESSIONS
  37. # include "boost/cgi/utility/sessions.hpp"
  38. #endif // BOOST_CGI_ENABLE_SESSIONS
  39. BOOST_CGI_NAMESPACE_BEGIN
  40. namespace common {
  41. /// The basic_request class, primary entry point to the library
  42. /**
  43. * Note: By default, synchronous protocols (ie. cgi) auto-load AND parse
  44. * STDIN,whereas async protocols don't.
  45. *
  46. * Note: The alternative functions which take a boost::system::error_code
  47. * are the non-throwing versions. Instead of a boost::system::system_error
  48. * being thrown in case of an error, the passed error_code will be set to
  49. * the value of the error, s.t. if (error) evaluates to true.`
  50. *
  51. * Note: This class isn't thread safe: carrying around a mutex-per-request
  52. * seems prohibitively expensive. There could be functions which take a
  53. * mutex as an argument and lock it. (Async calls could get messy if you
  54. * need a protected request object).
  55. **/
  56. template<typename Protocol>
  57. class basic_request
  58. : public detail::basic_io_object<
  59. typename protocol_traits<Protocol>::request_service_type
  60. >
  61. {
  62. public:
  63. typedef basic_request<Protocol> self_type;
  64. typedef Protocol protocol_type;
  65. typedef protocol_traits<protocol_type> traits;
  66. typedef typename traits::protocol_service_type protocol_service_type;
  67. typedef typename traits::request_service_type service_type;
  68. typedef typename service_type::implementation_type implementation_type;
  69. typedef typename traits::char_type char_type;
  70. typedef typename traits::string_type string_type;
  71. typedef typename traits::request_type request_type;
  72. typedef typename traits::client_type client_type;
  73. typedef typename traits::buffer_type buffer_type;
  74. #ifdef BOOST_CGI_ENABLE_SESSIONS
  75. typedef typename traits::session_type session_type;
  76. session_type session;
  77. #endif // BOOST_CGI_ENABLE_SESSIONS
  78. typedef boost::shared_ptr<request_type> pointer;
  79. /// The environment data, exposed as a `request_data<env_map>`.
  80. env_data env;
  81. /// The POST data, exposed as a `request_data<post_map>`.
  82. post_data post;
  83. /// The GET (ie. query string) data, exposed as a `request_data<get_map>`.
  84. get_data get;
  85. /// The form data, which is either the GET or POST data.
  86. form_data form;
  87. /// The cookie data, exposed as a `request_data<cookie_map>`.
  88. cookie_data cookies;
  89. /// The file uploads, exposed as a `request_data<upload_map>`.
  90. upload_data uploads;
  91. basic_request(
  92. int opts
  93. , char** base_env = NULL)
  94. : detail::basic_io_object<service_type>()
  95. {
  96. if ((parse_options)opts > parse_none) load((parse_options)opts, base_env);
  97. }
  98. basic_request(
  99. const parse_options opts = traits::parse_opts
  100. , char** base_env = NULL)
  101. : detail::basic_io_object<service_type>()
  102. {
  103. set_protocol_service();
  104. if (opts > parse_none) load(opts, base_env);
  105. }
  106. // Won't throw
  107. basic_request(boost::system::error_code& ec
  108. , const parse_options opts = traits::parse_opts
  109. , char** base_env = NULL)
  110. : detail::basic_io_object<service_type>()
  111. {
  112. set_protocol_service();
  113. if (opts > parse_none) load(opts, ec);
  114. }
  115. // Throws
  116. basic_request(protocol_service_type& s
  117. , const parse_options opts = traits::parse_opts
  118. , char** base_env = NULL)
  119. : detail::basic_io_object<service_type>(s.get_io_service())
  120. {
  121. set_protocol_service(s);
  122. if (opts > parse_none) load(opts, base_env);
  123. }
  124. // Won't throw
  125. basic_request(protocol_service_type& s
  126. , boost::system::error_code& ec
  127. , const parse_options opts = traits::parse_opts
  128. , char** base_env = NULL)
  129. : detail::basic_io_object<service_type>(s.get_io_service())
  130. {
  131. set_protocol_service(s);
  132. if (opts > parse_none) load(opts, ec, base_env);
  133. }
  134. /// Make a new mutiplexed request from an existing connection.
  135. // Throws.
  136. basic_request(implementation_type& impl)
  137. : detail::basic_io_object<service_type>(impl.service_->get_io_service())
  138. {
  139. set_protocol_service(*impl.service_);
  140. boost::system::error_code ec;
  141. this->service.begin_request_helper(this->implementation
  142. , impl.header_buf_, ec);
  143. detail::throw_error(ec);
  144. }
  145. /// Make a new mutiplexed request from an existing connection.
  146. // Won't throw.
  147. basic_request(implementation_type& impl, boost::system::error_code& ec)
  148. : detail::basic_io_object<service_type>(impl.service_->get_io_service())
  149. {
  150. set_protocol_service(*impl.service_);
  151. this->service.begin_request_helper(this->implementation
  152. , impl.header_buf_, ec);
  153. }
  154. ~basic_request()
  155. {
  156. #ifdef BOOST_CGI_ENABLE_SESSIONS
  157. try {
  158. if (!session.id().empty())
  159. this->service.session_manager().save(session);
  160. } catch(...) {
  161. // pass
  162. }
  163. #endif // BOOST_CGI_ENABLE_SESSIONS
  164. }
  165. #ifdef BOOST_CGI_ENABLE_SESSIONS
  166. /// Start a session, or load the session from a cookie.
  167. /**
  168. * This starts a session.
  169. */
  170. void start_session()
  171. {
  172. if (!session.loaded())
  173. {
  174. // Assume cookies have been loaded. This will throw at runtime (in
  175. // a debug build) if `request.load(parse_cookie)` hasn't been called
  176. // by now.
  177. this->service.session_manager().start(
  178. session, cookies.pick(BOOST_CGI_SESSION_COOKIE_NAME, ""));
  179. }
  180. }
  181. /// Stop the session and delete the stored session data.
  182. void stop_session()
  183. {
  184. this->service.session_manager().stop(session);
  185. }
  186. #endif // BOOST_CGI_ENABLE_SESSIONS
  187. protocol_service_type& get_protocol_service()
  188. {
  189. return *(this->implementation.service_);
  190. }
  191. static pointer create(protocol_service_type& ps)
  192. {
  193. return pointer(new self_type(ps));
  194. }
  195. static pointer create()
  196. {
  197. return pointer(new self_type());
  198. }
  199. void set_protocol_service()
  200. {
  201. //this->service.set_service(this->implementation, ps);
  202. }
  203. void set_protocol_service(protocol_service_type& ps)
  204. {
  205. this->service.set_service(this->implementation, ps);
  206. }
  207. /// The id of this request.
  208. /**
  209. * This is 1 for CGI/aCGI requests, but may be != 1 for FastCGI
  210. * requests.
  211. *
  212. * Note that for FastCGI requests, the id's are assigned on a
  213. * *per-connection* policy, so in one application you may have
  214. * several requests with the same id.
  215. */
  216. int id()
  217. {
  218. return this->service.request_id(this->implementation);
  219. }
  220. /// Check if the request is still open (ie. not aborted or closed)
  221. bool is_open()
  222. {
  223. return this->service.is_open(this->implementation);
  224. }
  225. /// Synchronously read/parse the request meta-data
  226. /**
  227. * Note: 'loading' including reading/parsing STDIN if
  228. * parse_stdin == true
  229. */
  230. void load(parse_options parse_opts = parse_env, char** base_env = NULL)
  231. {
  232. boost::system::error_code ec;
  233. load(parse_opts, ec, base_env);
  234. if (ec != error::eof)
  235. detail::throw_error(ec);
  236. }
  237. // Error-code semantics
  238. boost::system::error_code
  239. load(parse_options parse_opts, boost::system::error_code& ec
  240. , char** base_environment = NULL, bool is_command_line = true)
  241. {
  242. // Parse just the environment first, then check the user
  243. // isn't trying to upload more data than we want to let them.
  244. // Define `BOOST_CGI_POST_MAX` to set the maximum content-length
  245. // allowed.
  246. if (parse_opts & parse_env)
  247. {
  248. //this->service.load(this->implementation, parse_env, ec);
  249. //if (content_length() >= BOOST_CGI_POST_MAX)
  250. // ec = common::error::max_post_exceeded;
  251. this->service.load(this->implementation, parse_opts, ec);
  252. if (ec) return ec;
  253. // Load the environment passed by the user.
  254. if (base_environment)
  255. this->service.load_environment(
  256. this->implementation, base_environment
  257. , is_command_line);
  258. if (parse_opts & parse_env)
  259. env.set(env_vars(this->implementation.vars_));
  260. if (parse_opts & parse_get_only)
  261. get.set(get_vars(this->implementation.vars_));
  262. if (parse_opts & parse_post_only) {
  263. post.set(post_vars(this->implementation.vars_));
  264. uploads.set(upload_vars(this->implementation.vars_));
  265. }
  266. if ((parse_opts & parse_cookies) == parse_cookies) {
  267. cookies.set(cookie_vars(this->implementation.vars_));
  268. }
  269. if ((parse_opts & parse_form_only) == parse_form_only)
  270. {
  271. common::name rm(request_method().c_str());
  272. form.set(rm == "POST" ? post.impl() : get.impl());
  273. }
  274. #ifdef BOOST_CGI_ENABLE_SESSIONS
  275. if (parse_opts & parse_session_only)
  276. {
  277. if (!!cookies && cookies.count(BOOST_CGI_SESSION_COOKIE_NAME))
  278. session.id(cookies[BOOST_CGI_SESSION_COOKIE_NAME]);
  279. else
  280. if (traits::auto_start_session)
  281. session.id(this->service.make_session_id());
  282. if (!session.id().empty())
  283. this->service.session_manager().load(session);
  284. }
  285. #endif // BOOST_CGI_ENABLE_SESSIONS
  286. }
  287. return ec;
  288. }
  289. void load(char** base_environment, bool is_command_line = true)
  290. {
  291. this->service.load_environment(this->implementation
  292. , base_environment
  293. , is_command_line);
  294. }
  295. /// Get the buffer containing the POST data.
  296. /**
  297. * **FIXME**
  298. * This actually returns the whole buffer on FastCGI at the moment,
  299. * which contains the params too.
  300. */
  301. buffer_type& post_buffer()
  302. {
  303. return this->implementation.post_buffer_;
  304. }
  305. // **FIXME**
  306. /// Asynchronously read/parse the request meta-data
  307. /**
  308. * Note: 'loading' including reading/parsing STDIN if
  309. * parse_stdin == true
  310. */
  311. //template<typename Handler>
  312. //void async_load(Handler handler, bool parse_stdin = false)
  313. //{
  314. // this->service.async_load(this->implementation, parse_stdin
  315. // , handler);
  316. //}
  317. /// Notify the server the request has been handled.
  318. /**
  319. * In certain situations (such as a Proactor client using the async
  320. * read functions) it will be necessary to call end, rather than
  321. * just returning from the sub_main function.
  322. *
  323. * @param http_status The HTTP status of the request.
  324. *
  325. * @param program_status This value is returned to the server
  326. * indicating the state of the request after it was finished
  327. * handling. It is implementation defined how the server deals with
  328. * this, and it may have no effect on the http status code returned
  329. * to the client (eg. 200 OK).
  330. *
  331. * @returns The value of program_status
  332. */
  333. int close(common::http::status_code http_status = http::ok
  334. , int program_status = 0)
  335. {
  336. boost::system::error_code ec;
  337. this->service.close(this->implementation, http_status,
  338. program_status, ec);
  339. detail::throw_error(ec);
  340. return program_status;
  341. }
  342. int close(common::http::status_code http_status
  343. , int program_status
  344. , boost::system::error_code& ec)
  345. {
  346. return this->service.close(this->implementation, http_status
  347. , program_status, ec);
  348. }
  349. /// Reject the request with a '500 Internal Server Error' error.
  350. int reject()
  351. {
  352. this->service.status(this->implementation, aborted);
  353. return this->service.close(this->implementation
  354. , http::internal_server_error);
  355. }
  356. /// Abort a request.
  357. void abort()
  358. {
  359. this->service.status(this->implementation, aborted);
  360. }
  361. /// Clear the data for the request, for reusing this object.
  362. void clear()
  363. {
  364. this->service.clear(this->implementation);
  365. }
  366. /// Get the client connection associated with the request
  367. /**
  368. * You use the client for read/write calls.
  369. */
  370. client_type& client()
  371. {
  372. return this->service.client(this->implementation);
  373. }
  374. /// Read some data into the request, parsing if necessary.
  375. void read_some()
  376. {
  377. boost::system::error_code ec;
  378. this->service.read_some(this->implementation, ec);
  379. detail::throw_error(ec);
  380. }
  381. /// Read some data into the request, parsing if necessary.
  382. std::size_t
  383. read_some(boost::system::error_code& ec)
  384. {
  385. return this->service.read_some(this->implementation, ec);
  386. }
  387. /// Read some data into the supplied buffer, parsing if necessary.
  388. // **FIXME** (remove - use req.client().read_some() instead)
  389. template<typename MutableBufferSequence>
  390. void read_some(const MutableBufferSequence& buf)
  391. {
  392. boost::system::error_code ec;
  393. this->service.read_some(this->implementation, buf, ec);
  394. detail::throw_error(ec);
  395. }
  396. /// Read some data into the supplied buffer, parsing if necessary.
  397. // **FIXME** (remove - use req.client().read_some() instead)
  398. template<typename MutableBufferSequence>
  399. std::size_t
  400. read_some(const MutableBufferSequence& buf
  401. , boost::system::error_code& ec)
  402. {
  403. return this->service.read_some(this->implementation, buf, ec);
  404. }
  405. /////////////////////////////////////////////////////////
  406. // Helper-functions for the basic CGI 1.1 meta-variables.
  407. /////////////////////////////////////////////////////////
  408. string_type& auth_type()
  409. { return env["AUTH_TYPE"]; }
  410. /// Get the content length as a long.
  411. /**
  412. * The content length defaults to zero if it isn't explicitly set
  413. * by your HTTP server.
  414. */
  415. long content_length()
  416. {
  417. string_type& cl(env["CONTENT_LENGTH"]);
  418. return boost::lexical_cast<long>(cl.empty() ? "0" : cl);
  419. }
  420. /// The content type of the request.
  421. /**
  422. * Common value: text/html.
  423. */
  424. string_type& content_type()
  425. { return env["CONTENT_TYPE"]; }
  426. /// The protocol used by the server to communicate to the script.
  427. /**
  428. * Common value: CGI/1.1.
  429. */
  430. string_type& gateway_interface()
  431. { return env["GATEWAY_INTERFACE"]; }
  432. /// Additional information, appendended to the script.
  433. common::path_info path_info()
  434. { return env["PATH_INFO"]; }
  435. /// The translated version of the path info.
  436. /**
  437. * Your HTTP server may provide this, depending on configuration.
  438. * The path info can represent a resource on the local file system.
  439. */
  440. string_type& path_translated()
  441. { return env["PATH_TRANSLATED"]; }
  442. /// The query string for the request.
  443. /**
  444. * This is the part of the request URI after a '?'. A GET request
  445. * passes request parameters, URL encoded in the query string.
  446. */
  447. string_type& query_string()
  448. { return env["QUERY_STRING"]; }
  449. void set_query_string(string_type const& val)
  450. { env["QUERY_STRING"] = val; }
  451. /// The host address of the remote user.
  452. string_type& remote_addr()
  453. { return env["REMOTE_ADDR"]; }
  454. /// The host name of the remote user's machine.
  455. string_type& remote_host()
  456. { return env["REMOTE_HOST"]; }
  457. /// The user making the request.
  458. string_type& remote_ident()
  459. { return env["REMOTE_IDENT"]; }
  460. /// The userid of the person accessing the script.
  461. string_type& remote_user()
  462. { return env["REMOTE_USER"]; }
  463. /// The method of the request.
  464. /**
  465. * Common values: `GET`, `POST`, `HEAD`.
  466. */
  467. string_type& method()
  468. { return env["REQUEST_METHOD"]; }
  469. void set_method(string_type const& val)
  470. { env["REQUEST_METHOD"] = val; }
  471. /// The method of the request (long-hand of `method()`).
  472. /**
  473. * Common values: `GET`, `POST`, `HEAD`.
  474. */
  475. string_type& request_method()
  476. { return env["REQUEST_METHOD"]; }
  477. /// Get the URI of the request.
  478. string_type& uri()
  479. { return env["REQUEST_URI"]; }
  480. /// Get the URI of the request (long-hand of `uri()`).
  481. string_type& request_uri()
  482. { return env["REQUEST_URI"]; }
  483. /// The name of the script.
  484. string_type& script_name()
  485. { return env["SCRIPT_NAME"]; }
  486. /// The URL of the script.
  487. string_type& script_url()
  488. { return env["SCRIPT_URL"]; }
  489. /// The full URI of the script.
  490. string_type& script_uri()
  491. { return env["SCRIPT_URI"]; }
  492. /// Get the name of the server.
  493. /**
  494. * Usually set in your HTTP configuration. This could be the name
  495. * of a virtual host.
  496. */
  497. string_type& server_name()
  498. { return env["SERVER_NAME"]; }
  499. /// Get the port the calling server is listening on.
  500. /**
  501. * Common value: 80.
  502. */
  503. string_type& server_port()
  504. { return env["SERVER_PORT"]; }
  505. /// Get the protocol being used by the calling server.
  506. /**
  507. * Common value: HTTP/1.1.
  508. */
  509. string_type& server_protocol()
  510. { return env["SERVER_PROTOCOL"]; }
  511. /// Get a string identifying the calling server.
  512. /**
  513. * CGI scripts are generally called by a web-facing HTTP server.
  514. * When set, this string can be useful for knowing what is calling
  515. * the script, especially in a multi-server or load balanced
  516. * environment.
  517. */
  518. string_type& server_software()
  519. { return env["SERVER_SOFTWARE"]; }
  520. /// Get the web page the user came from.
  521. /**
  522. * HTTP equivalent: `HTTP_REFERER`
  523. *
  524. * The referer is commonly used for tracking user's movements and
  525. * origins. For instance, some sites use this to highlight some
  526. * keywords on the page when users come from a search engine.
  527. *
  528. * Note that you cannot ever guarantee on this value being either set
  529. * or accurate.
  530. */
  531. string_type& referer()
  532. { return env["HTTP_REFERER"]; }
  533. // -- end helper-functions]
  534. /// Get the charset from the CONTENT_TYPE header
  535. string_type charset()
  536. {
  537. string_type ctype(content_type());
  538. std::size_t pos = ctype.find("charset=");
  539. string_type val(ctype.substr(pos+8, ctype.find(";", pos)));
  540. boost::algorithm::trim(val);
  541. return val.empty() ? BOOST_CGI_DEFAULT_CHARSET : val;
  542. }
  543. /// The email of the user making the request.
  544. string_type& http_from ()
  545. { return env["HTTP_FROM"]; }
  546. /// The cookies sent by the user making the request.
  547. string_type& http_cookie ()
  548. { return env["HTTP_COOKIE"]; }
  549. /// The role that the request is playing
  550. /**
  551. * The default role type is responder.
  552. *
  553. * In some cases - for instance with FastCGI - the role type can be
  554. * different
  555. * eg. `authorizer`, or `filter`.
  556. */
  557. role_type role() const
  558. {
  559. return this->service.role(this->implementation);
  560. }
  561. /// Get / Set the status of a request.
  562. /**
  563. * The usual way to set the request status is to set it
  564. * when calling `close`.
  565. */
  566. common::request_status status() const
  567. {
  568. return this->service.status(this->implementation);
  569. }
  570. /// Set the status of a request.
  571. void status(common::request_status const& status)
  572. {
  573. this->service.status(this->implementation, status);
  574. }
  575. /// Sets the maximum number of async requests.
  576. boost::uint16_t& async_requests()
  577. {
  578. return this->implementation.async_requests_;
  579. }
  580. };
  581. } // namespace common
  582. BOOST_CGI_NAMESPACE_END
  583. #include "boost/cgi/detail/pop_options.hpp"
  584. #endif // CGI_BASIC_REQUEST_HPP_INCLUDED__
  585. /*
  586. NOTES::future_plans:
  587. * When a request is aborted (eg. when the client closes the connection)
  588. the library can call an abort_handler() function stored in the request.
  589. - The user should supply an abort_handler-derived function if they want
  590. any special handling of aborted requests
  591. */