cgi /libs/cgi/doc/src/tutorial/cgi_quickstart.cpp

Language C++ Lines 251
MD5 Hash 0b080ffc27e85625777da5d2430708ff Estimated Cost $1,139 (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
250
//                     -- cgi_quickstart.cpp --
//
//            Copyright (c) Darren Garvey 2009.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)
//
////////////////////////////////////////////////////////////////

//[cgi_quickstart

/*<
A catch-all header is available which includes all of the headers you should
need for CGI.

For the sake of clarity we alias the `boost::cgi` namespace rather than
dumping all of the library names with a `using namespace`. This way, you can
see what comes from the library.
>*/
#include <boost/cgi/cgi.hpp>
namespace cgi = boost::cgi;

/*<
The first thing to do is write a handler function which takes a request and a
response and does all request-specific work. Later, we will look at writing
the code that calls this function.
>*/
int handle_request(cgi::request& req, cgi::response& resp)
{
/*<
In our request handler, we will assume that the request has been fully-parsed
and we can access all of the request data. The request data is available using
public members of a `cgi::request`. These member variables are instances of
the [classref boost::cgi::common::data_map_proxy data_map_proxy], which has a
`std::map<>`-like interface along with some additional helper functions to
facilitate common CGI tasks, such as lexical conversion to different types.
[footnote The data is stored internally in a single `fusion::vector<>` which
is not currently publicly accessible.]

A CGI request has several types of variables available. These are listed in
the table below, assuming that `req` is an instance of `cgi::request`:

[table
  [[Source] [Variable] [Description]]
  [
    [Environment] [`req.env`] [The environment of a CGI request contains most
    of the information you will need to handle a request. There is a basic set of
    common environment variables that you can expect to be set by most HTTP
    servers around. A list of them is available on the __TODO__ (link) variables
    page.]
  ]
  [
    [GET] [`req.get`] [The variables passed in the query string of an HTTP GET
    request.]
  ]
  [
    [POST] [`req.post`] [The HTTP POST data that is sent in an HTTP request's
    body. For file uploads, the file's name is the value stored in the map. You
    should use `req.uploads` for more information on file uploads.]
  ]
  [
    [Cookies] [`req.cookies`] [Cookies are sent in the HTTP_COOKIE environment
    variable. These can store limited amounts session information on the client's
    machine, such as database session ids or tracking information.]
  ]
  [
    [File Uploads] [`req.uploads`] [File uploads, sent in an HTTP POST where
    the body is MIME-encoded as multipart/form-data. Uploaded files are written
    onto the server's file system and meta-data related to the file is stored in
    a [classref boost::cgi::common::form_part form_part]. The value of an upload
    variable is the `form_part` for the upload and all `form_part`s are implicitly
    convertible to a string, which corresponds to the original filename.]
  ]
  [
    [Form] [`req.form`] [The form variables are either the GET variables or
    the POST variables, depending on the request method of the request.]
  ]
]

Let's assume you now want to check if the user has a cookie, "user_name",
set. We can check if a user has a cookie set like this:
>*/
  if (req.cookies.count("user_name"))
  {
/*<
First, we need to be able to clear the cookie we are setting. We will reset
the cookie if the user navigates to `"/path/to/script?reset=1"`.

The `reset` variable in the query string is a GET variable. The request data
is accessed through a proxy class which works just like a `std::map<>` with
some extra features.

One of them is `pick`, which looks up a key in the map and returns the value
if it is found. Otherwise it returns a default value, which is the second
argument. 

The default value can be any type that supports
[@http://boost.org/libs/lexical_cast Boost.Lexical_cast]. If the key isn't
found in the map, or the value cannot be cast to the type of the default
value, the default is returned.
>*/
    if (req.get.pick<std::string>("reset", "") == "1")
    {
      resp<< cgi::cookie("user_name") /*<
Set a cookie with no value to delete it.
>*/
          << cgi::redirect(req, req.script_name()) /*<
The `cgi::redirect` free function returns a `"Location"` header that will
redirect the user to the specified URL. This URL can be a relative or absolute
but an absolute URL is always returned. To perform an internal redirect, use
`cgi::location` instead.
>*/
          << cgi::content_type("text/plain");
    }
    else
    {
      std::string user_name( req.cookies["user_name"] );
/*<
Looking up a request cookie in `req.cookies` really returns a `cgi::cookie`.
The line above works though because a `cgi::cookie` is implicitly convertible
to a `std::string`.

The lookup is case-insensitive, so "USER_NAME" and "User_Name" would be
equivalent lookup keys.

If the cookie is set, we'll be polite and say hello before quitting.
>*/
      if (!user_name.empty())
      {
        resp<< cgi::content_type("text/html")
            << "<p>Hello there, " << req.cookies["user_name"]
            << ". How are you?</p>"
            << "<a href=\"" << req.script_name() << "?reset=1\">Reset</a>";
      }
    }
/*<
That's all we want to say for now, so we can return.

If you are familiar with CGI programming, you will notice the lack of any
HTTP headers in the response. A `cgi::response` handles headers separately
to the body. You can set headers at any point and when you send the response
the headers will all be sent first.

If you don't explicitly set any response headers, a default header
`"Content-type: text/plain"` is sent, followed by the usual HTTP end-of-line
`"\r\n"` and a blank line which indicates the end of the headers and the
start of response body.
>*/
  } else
/*<
If the cookie isn't set, we will check if the user has posted a __GET__/
__POST__ form with their name.
>*/
  if (req.form.count("user_name"))
  {
    std::string user_name (req.form["user_name"]);
/*<
If they have told us their name, we should set a cookie so we remember it next
time. Then we can say hello and exit.

There are two ways to set a cookie: either directly using
`req.set_cookie("user_name", user_name)` or the method shown. You can also
send an expiry date and a path for the cookie.[footnote 
See [@http://tools.ietf.org/html/rfc822 RFC822] for more.
]
Note that if you set a cookie with no value, the cookie will be deleted.

Again, the request object isn't buffered, so we are going to keep using the
`response` in case something breaks and we end up not wanting to set the
cookie. The cookie we set below will expire when the client closes their
browser.

This time, we shall send a Date header. If we do this (ie. send a header
ourselves), we must also set the Content-type header, like below.
>*/
    resp<< cgi::cookie("user_name", user_name)
        << cgi::header("Date", "Tue, 15 Nov 1994 08:12:31 GMT")
        << cgi::content_type("text/html")
        << "Hello there, " << user_name << ". You're new around here."
        << "user_name.length() = " << user_name.length() ;
  }
  else
  {
/*<
Now, if we have no idea who they are, we'll send a form asking them for their
name. As the default `"Content-type"` header is `"text/plain"`, we'll change
this to `"text/html"` so the user's browser will display the HTML form. You
can do this using
    `set_header(req,  "Content-type", "text/html")`
or
    `resp<< header("Content-type", "text/html")`.
Since writing with raw strings is error-prone, the shortcut below is available.
>*/
  resp<< cgi::content_type("text/html")
      << "Hello there. What's your name?" "<p />"
         "<form method='POST'>"
         "<input type='text' name='user_name' />"
         "<input type='submit' />";
  }
/*<
A CGI program will handle one request each time it is invoked. Returning a
non-zero status to the OS indicates an error handling the request. I don't
know that HTTP servers treat non-zero exit codes specially.[footnote I may well
may well be wrong about this.]

To send the response back to the request, use `cgi::commit`. The third
`status` argument is optional and defaults to zero. The return value of
`cgi::commit` is `status`.
>*/
  return cgi::commit(req, resp);
}

/*<
We now have a request handler in all of it's contrived glory.

The program's `main` function needs to parse the request, call the request
handler defined above, and finally send the response.
>*/

int main(int, char**)
{
  cgi::request req;
/*<
At this point, the environment variables are accessible. This includes cookie
and form variables too, which are all parsed by default-constructing a
`cgi::request` (this is optional).
>*/
  cgi::response resp;
/*<
The `response` class provides a streaming interface for writing replies. You
can write to the request object directly, but for now we're going to just
use the `response`, which works well for most situations.

Writing to a `response` is buffered. If an error occurs, you can simply
`clear()` the response and send an error message instead. Buffered writing
may not always suit your use-case (eg. returning large files), but when memory
is not at a real premium, buffering the response is highly preferable.

Not only does buffering avoid network latency issues, but being able to cancel
the response and send another is much cleaner than sending half a response,
followed by "...Ooops". A `cgi::response` is not tied to a request, so the
same response can be reused across multiple requests.

When sending a response that is large relative to the amount of memory
available to the program, you may want to write unbuffered.
>*/
  return handle_request(req, resp);
}

//]
Back to Top