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

Language C++ Lines 250
MD5 Hash 9cb7c8215f9f3f04078ca5bab56d041b Estimated Cost $2,534 (why?)
Repository git://github.com/darrengarvey/cgi.git View Raw File
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*<
This example demonstrates using custom session types to carry around
data suited to your app. In this case, we'll make a dummy authenticated
CGI app that users can log into.

Note that the example uses cTemplate to construct the page, for the sake
of clarity. You will need to install cTemplate to compile the example
yourself.
>*/

//[sessions_quickstart

/*<
Session support is an optional component of the library. To use sessions,
just define `BOOST_CGI_ENABLE_SESSIONS` before including the library
headers.

Sessions are supported by using Boost.Serialization, so you'll need to
link to the library. On Windows, the linking should be done automatically.
>*/
#define BOOST_CGI_ENABLE_SESSIONS
#include <boost/cgi/cgi.hpp>
#include <boost/cgi/utility/stencil.hpp>
#include <boost/date_time/posix_time/time_serialize.hpp>

using namespace std;
namespace cgi = boost::cgi;

/// A basic class that hold site user's information.
class user
{
public:
  string id;
  string name;
  string email;
  string location;

  // This class is Serializable.
  template<typename Archive>
  void serialize(Archive& ar, const unsigned /* version */)
  {
    ar & id & name & email & location;
  }
};

/// The user_manager class checks credentials and logs users in.
/*<
For the purposes of this example, this class is just a stub that
knows about one user: guest.
  
In reality this would do more thorough validation on the authentication
information and look to a database to check the user's credentials and
retrieve their user information.
>*/
class user_manager
{
public:
  /// A dummy implementation of a login function.
  /*<
     For this example, we just have one user, "guest" with a password
     of "password".
  >*/
  template<typename RequestData>
  bool login(RequestData& data, user& usr)
  {
    if (data.count("name") && data.count("pass")) {
      if (data["name"] == "guest" && data["pass"] == "password") {
        usr.id = "1";
        usr.name = "guest";
        usr.email = "\"Guest\" <guest@omnisplat.com>";
        usr.location = "Liverpool, England, UK";
        return true;
      }
    }
    return false;
  }
};

/// Our custom session class.
/*< This class must be both DefaultConstructible and Serializable. >*/
struct my_session
{
  /// Your session class must be DefaultConstrutible.
  my_session()
    : last_accessed(boost::posix_time::microsec_clock::universal_time())
  {
  }
  
  /// The last-accessed time for the user. Defaults to now.
  boost::posix_time::ptime last_accessed;
  
  /// User information.
  user info;
  
  /// Your session class must be Serializable.
  template<typename Archive>
  void serialize(Archive& ar, const unsigned /* version */)
  {
    ar & last_accessed & info;
  }
};

/*<
The library uses tag-dispatching to determine which types to use
internally to make things work. You can specialise the `protocol_traits`
class template to customise parts of the library at compile-time.
The `protocol_traits` template is specialised for the protocols
supported by the library (ie. CGI and FastCGI).

To define a custom type to use for sessions, you can define a
"tag" (just an empty struct) and then specialise `protocol_traits`
for this tag. You should put this specialisation into the
`boost::cgi::common` namespace so it can be found via ADL.

In general, you will inherit most of the characteristics of either
CGI or FastCGI and override one or two types. You can do this by
inheriting from either `protocol_traits<tags::cgi>` or
`protocol_traits<tags::fcgi>`.

eg. This is overriding a protocol the long way.

struct mycgi; // The protocol tag

namespace boost { namespace cgi { namespace common {

  template<>
  struct protocol_traits<mycgi>
    : protocol_traits<tags::cgi>
  {
    // some custom traits.
  };

}}} // namespace boost::cgi::common

// Then add typedefs to use the custom tag struct.
struct mycgi {
  typedef boost::cgi::common::basic_protocol_service<mycgi> service;
  typedef boost::cgi::common::basic_request_acceptor<mycgi> acceptor;
  typedef boost::cgi::common::basic_request<mycgi> my_request;
};

   The `BOOST_CGI_OVERRIDE_PROTOCOL` macro is available as a shortcut
   for the above. You pass the protocol as the first argument, the
   name of your "tag" as the second and the third is a brace-enclosed
   list of traits. The code between the braces is actually the body of
   the specialised `protocol_traits<>` class template.
  
   When you use `BOOST_CGI_OVERRIDE_PROTOCOL`, you will end up with a
   struct with nested typedefs for `request`, `service` and `acceptor`.
>*/

/// Declare a custom protocol `mycgi`.
BOOST_CGI_OVERRIDE_PROTOCOL(cgi, mycgi, {
    typedef basic_session<my_session> session_type;
    static const bool auto_start_session = false;
})

int main(int, char**)
{
  try
  {
    // Construct our custom request type, auto-parsing the request.
    mycgi::request req;
    // A basic wrapper over cTemplate. This constructor takes the directory
    // where all "stencils" (eg. HTML templates) are kept. Your web server
    // should be given read access to this directory.
    cgi::stencil resp("../stencils/");
    // The dummy user manager defined earlier in this example.
    user_manager user_mgr;
    
    resp.set("last_accessed",
        boost::posix_time::to_simple_string(req.session.last_accessed));

    if (req.form.count("login"))
    {
      // Allow users to log in to the application.
      if (user_mgr.login(req.form, req.session.info)) {
        // Mark the request so when you call `commit`, the session is saved.
        req.start_session();
        // Redirect them back to where they started.
        resp<< cgi::redirect(req, req.script_name());
      } else {
        resp.set("user_name", req.form["name"]);
        // Show the HAS_ERROR section and set the {{error}} token.
        resp.set("error", "Login failed.", "HAS_ERROR");
        // Show all LOGIN_FORM sections.
        resp.show("LOGIN_FORM");
      }
    }
    else
    if (req.form.count("logout"))
    {
      // Allow users to log out.
      /*<
         We call `stop_session` to close the session. This call deletes the
         session data stored on the server, but leaves the session object
         stored in memory as is. The session cookie itself is only removed
         when you call `commit`, later.

         Redirect the user. This causes a "Location" header to be added to
         the response.
      >*/
      req.stop_session();
      resp<< cgi::redirect(req, req.script_name());
    }
    else
    if (!req.session.info.name.empty())
    {
      // Retrieve the session information for the user.
      resp.set("user_name", req.session.info.name);
      resp.set("user_email", req.session.info.email);
      resp.set("user_location", req.session.info.location);
      resp.show("AUTHENTICATED_PAGE");
    }
    else
    {
      resp.show("DEFAULT_PAGE");
      resp.show("LOGIN_FORM");
    }

    /*<
       This is where the response is actually built up, using the specified
       stencil: In this case we send back an HTML page. This could easily
       be turned into an AJAX-style page by constructing a stencil that looks
       like JSON when expanded and setting the content-type to
       `"application/json"`.
      
       Note that if the template looks like JSON, cTemplate can be very
       clever and auto-escape the content so you get valid JSON. For more
       info, take a look at the ctemplate documentation:
       @ http://code.google.com/p/google-ctemplate/
    >*/
    resp.expand("login.html");
    resp<< cgi::content_type("text/html");
    
    req.session.last_accessed = boost::posix_time::microsec_clock::universal_time();
    
    // Send the response and save session data.
    return cgi::commit(req, resp);
  
  } catch (std::exception& e) {
    cerr<< "Error: " << e.what() << endl;
  } catch (...) {
    cerr<< "Unknown error" << endl;
  }

  cout<< "Content-type: text/html\r\n\r\nAn error occurred.";
}
//]
Back to Top