/libs/cgi/doc/src/utilities/sessions/sessions_quickstart.cpp

http://github.com/darrengarvey/cgi · C++ · 250 lines · 105 code · 21 blank · 124 comment · 11 complexity · 9cb7c8215f9f3f04078ca5bab56d041b MD5 · raw file

  1. /*<
  2. This example demonstrates using custom session types to carry around
  3. data suited to your app. In this case, we'll make a dummy authenticated
  4. CGI app that users can log into.
  5. Note that the example uses cTemplate to construct the page, for the sake
  6. of clarity. You will need to install cTemplate to compile the example
  7. yourself.
  8. >*/
  9. //[sessions_quickstart
  10. /*<
  11. Session support is an optional component of the library. To use sessions,
  12. just define `BOOST_CGI_ENABLE_SESSIONS` before including the library
  13. headers.
  14. Sessions are supported by using Boost.Serialization, so you'll need to
  15. link to the library. On Windows, the linking should be done automatically.
  16. >*/
  17. #define BOOST_CGI_ENABLE_SESSIONS
  18. #include <boost/cgi/cgi.hpp>
  19. #include <boost/cgi/utility/stencil.hpp>
  20. #include <boost/date_time/posix_time/time_serialize.hpp>
  21. using namespace std;
  22. namespace cgi = boost::cgi;
  23. /// A basic class that hold site user's information.
  24. class user
  25. {
  26. public:
  27. string id;
  28. string name;
  29. string email;
  30. string location;
  31. // This class is Serializable.
  32. template<typename Archive>
  33. void serialize(Archive& ar, const unsigned /* version */)
  34. {
  35. ar & id & name & email & location;
  36. }
  37. };
  38. /// The user_manager class checks credentials and logs users in.
  39. /*<
  40. For the purposes of this example, this class is just a stub that
  41. knows about one user: guest.
  42. In reality this would do more thorough validation on the authentication
  43. information and look to a database to check the user's credentials and
  44. retrieve their user information.
  45. >*/
  46. class user_manager
  47. {
  48. public:
  49. /// A dummy implementation of a login function.
  50. /*<
  51. For this example, we just have one user, "guest" with a password
  52. of "password".
  53. >*/
  54. template<typename RequestData>
  55. bool login(RequestData& data, user& usr)
  56. {
  57. if (data.count("name") && data.count("pass")) {
  58. if (data["name"] == "guest" && data["pass"] == "password") {
  59. usr.id = "1";
  60. usr.name = "guest";
  61. usr.email = "\"Guest\" <guest@omnisplat.com>";
  62. usr.location = "Liverpool, England, UK";
  63. return true;
  64. }
  65. }
  66. return false;
  67. }
  68. };
  69. /// Our custom session class.
  70. /*< This class must be both DefaultConstructible and Serializable. >*/
  71. struct my_session
  72. {
  73. /// Your session class must be DefaultConstrutible.
  74. my_session()
  75. : last_accessed(boost::posix_time::microsec_clock::universal_time())
  76. {
  77. }
  78. /// The last-accessed time for the user. Defaults to now.
  79. boost::posix_time::ptime last_accessed;
  80. /// User information.
  81. user info;
  82. /// Your session class must be Serializable.
  83. template<typename Archive>
  84. void serialize(Archive& ar, const unsigned /* version */)
  85. {
  86. ar & last_accessed & info;
  87. }
  88. };
  89. /*<
  90. The library uses tag-dispatching to determine which types to use
  91. internally to make things work. You can specialise the `protocol_traits`
  92. class template to customise parts of the library at compile-time.
  93. The `protocol_traits` template is specialised for the protocols
  94. supported by the library (ie. CGI and FastCGI).
  95. To define a custom type to use for sessions, you can define a
  96. "tag" (just an empty struct) and then specialise `protocol_traits`
  97. for this tag. You should put this specialisation into the
  98. `boost::cgi::common` namespace so it can be found via ADL.
  99. In general, you will inherit most of the characteristics of either
  100. CGI or FastCGI and override one or two types. You can do this by
  101. inheriting from either `protocol_traits<tags::cgi>` or
  102. `protocol_traits<tags::fcgi>`.
  103. eg. This is overriding a protocol the long way.
  104. struct mycgi; // The protocol tag
  105. namespace boost { namespace cgi { namespace common {
  106. template<>
  107. struct protocol_traits<mycgi>
  108. : protocol_traits<tags::cgi>
  109. {
  110. // some custom traits.
  111. };
  112. }}} // namespace boost::cgi::common
  113. // Then add typedefs to use the custom tag struct.
  114. struct mycgi {
  115. typedef boost::cgi::common::basic_protocol_service<mycgi> service;
  116. typedef boost::cgi::common::basic_request_acceptor<mycgi> acceptor;
  117. typedef boost::cgi::common::basic_request<mycgi> my_request;
  118. };
  119. The `BOOST_CGI_OVERRIDE_PROTOCOL` macro is available as a shortcut
  120. for the above. You pass the protocol as the first argument, the
  121. name of your "tag" as the second and the third is a brace-enclosed
  122. list of traits. The code between the braces is actually the body of
  123. the specialised `protocol_traits<>` class template.
  124. When you use `BOOST_CGI_OVERRIDE_PROTOCOL`, you will end up with a
  125. struct with nested typedefs for `request`, `service` and `acceptor`.
  126. >*/
  127. /// Declare a custom protocol `mycgi`.
  128. BOOST_CGI_OVERRIDE_PROTOCOL(cgi, mycgi, {
  129. typedef basic_session<my_session> session_type;
  130. static const bool auto_start_session = false;
  131. })
  132. int main(int, char**)
  133. {
  134. try
  135. {
  136. // Construct our custom request type, auto-parsing the request.
  137. mycgi::request req;
  138. // A basic wrapper over cTemplate. This constructor takes the directory
  139. // where all "stencils" (eg. HTML templates) are kept. Your web server
  140. // should be given read access to this directory.
  141. cgi::stencil resp("../stencils/");
  142. // The dummy user manager defined earlier in this example.
  143. user_manager user_mgr;
  144. resp.set("last_accessed",
  145. boost::posix_time::to_simple_string(req.session.last_accessed));
  146. if (req.form.count("login"))
  147. {
  148. // Allow users to log in to the application.
  149. if (user_mgr.login(req.form, req.session.info)) {
  150. // Mark the request so when you call `commit`, the session is saved.
  151. req.start_session();
  152. // Redirect them back to where they started.
  153. resp<< cgi::redirect(req, req.script_name());
  154. } else {
  155. resp.set("user_name", req.form["name"]);
  156. // Show the HAS_ERROR section and set the {{error}} token.
  157. resp.set("error", "Login failed.", "HAS_ERROR");
  158. // Show all LOGIN_FORM sections.
  159. resp.show("LOGIN_FORM");
  160. }
  161. }
  162. else
  163. if (req.form.count("logout"))
  164. {
  165. // Allow users to log out.
  166. /*<
  167. We call `stop_session` to close the session. This call deletes the
  168. session data stored on the server, but leaves the session object
  169. stored in memory as is. The session cookie itself is only removed
  170. when you call `commit`, later.
  171. Redirect the user. This causes a "Location" header to be added to
  172. the response.
  173. >*/
  174. req.stop_session();
  175. resp<< cgi::redirect(req, req.script_name());
  176. }
  177. else
  178. if (!req.session.info.name.empty())
  179. {
  180. // Retrieve the session information for the user.
  181. resp.set("user_name", req.session.info.name);
  182. resp.set("user_email", req.session.info.email);
  183. resp.set("user_location", req.session.info.location);
  184. resp.show("AUTHENTICATED_PAGE");
  185. }
  186. else
  187. {
  188. resp.show("DEFAULT_PAGE");
  189. resp.show("LOGIN_FORM");
  190. }
  191. /*<
  192. This is where the response is actually built up, using the specified
  193. stencil: In this case we send back an HTML page. This could easily
  194. be turned into an AJAX-style page by constructing a stencil that looks
  195. like JSON when expanded and setting the content-type to
  196. `"application/json"`.
  197. Note that if the template looks like JSON, cTemplate can be very
  198. clever and auto-escape the content so you get valid JSON. For more
  199. info, take a look at the ctemplate documentation:
  200. @ http://code.google.com/p/google-ctemplate/
  201. >*/
  202. resp.expand("login.html");
  203. resp<< cgi::content_type("text/html");
  204. req.session.last_accessed = boost::posix_time::microsec_clock::universal_time();
  205. // Send the response and save session data.
  206. return cgi::commit(req, resp);
  207. } catch (std::exception& e) {
  208. cerr<< "Error: " << e.what() << endl;
  209. } catch (...) {
  210. cerr<< "Unknown error" << endl;
  211. }
  212. cout<< "Content-type: text/html\r\n\r\nAn error occurred.";
  213. }
  214. //]