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

http://github.com/darrengarvey/cgi · C++ · 250 lines · 49 code · 6 blank · 195 comment · 5 complexity · 0b080ffc27e85625777da5d2430708ff MD5 · raw file

  1. // -- cgi_quickstart.cpp --
  2. //
  3. // Copyright (c) Darren Garvey 2009.
  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. //[cgi_quickstart
  10. /*<
  11. A catch-all header is available which includes all of the headers you should
  12. need for CGI.
  13. For the sake of clarity we alias the `boost::cgi` namespace rather than
  14. dumping all of the library names with a `using namespace`. This way, you can
  15. see what comes from the library.
  16. >*/
  17. #include <boost/cgi/cgi.hpp>
  18. namespace cgi = boost::cgi;
  19. /*<
  20. The first thing to do is write a handler function which takes a request and a
  21. response and does all request-specific work. Later, we will look at writing
  22. the code that calls this function.
  23. >*/
  24. int handle_request(cgi::request& req, cgi::response& resp)
  25. {
  26. /*<
  27. In our request handler, we will assume that the request has been fully-parsed
  28. and we can access all of the request data. The request data is available using
  29. public members of a `cgi::request`. These member variables are instances of
  30. the [classref boost::cgi::common::data_map_proxy data_map_proxy], which has a
  31. `std::map<>`-like interface along with some additional helper functions to
  32. facilitate common CGI tasks, such as lexical conversion to different types.
  33. [footnote The data is stored internally in a single `fusion::vector<>` which
  34. is not currently publicly accessible.]
  35. A CGI request has several types of variables available. These are listed in
  36. the table below, assuming that `req` is an instance of `cgi::request`:
  37. [table
  38. [[Source] [Variable] [Description]]
  39. [
  40. [Environment] [`req.env`] [The environment of a CGI request contains most
  41. of the information you will need to handle a request. There is a basic set of
  42. common environment variables that you can expect to be set by most HTTP
  43. servers around. A list of them is available on the __TODO__ (link) variables
  44. page.]
  45. ]
  46. [
  47. [GET] [`req.get`] [The variables passed in the query string of an HTTP GET
  48. request.]
  49. ]
  50. [
  51. [POST] [`req.post`] [The HTTP POST data that is sent in an HTTP request's
  52. body. For file uploads, the file's name is the value stored in the map. You
  53. should use `req.uploads` for more information on file uploads.]
  54. ]
  55. [
  56. [Cookies] [`req.cookies`] [Cookies are sent in the HTTP_COOKIE environment
  57. variable. These can store limited amounts session information on the client's
  58. machine, such as database session ids or tracking information.]
  59. ]
  60. [
  61. [File Uploads] [`req.uploads`] [File uploads, sent in an HTTP POST where
  62. the body is MIME-encoded as multipart/form-data. Uploaded files are written
  63. onto the server's file system and meta-data related to the file is stored in
  64. a [classref boost::cgi::common::form_part form_part]. The value of an upload
  65. variable is the `form_part` for the upload and all `form_part`s are implicitly
  66. convertible to a string, which corresponds to the original filename.]
  67. ]
  68. [
  69. [Form] [`req.form`] [The form variables are either the GET variables or
  70. the POST variables, depending on the request method of the request.]
  71. ]
  72. ]
  73. Let's assume you now want to check if the user has a cookie, "user_name",
  74. set. We can check if a user has a cookie set like this:
  75. >*/
  76. if (req.cookies.count("user_name"))
  77. {
  78. /*<
  79. First, we need to be able to clear the cookie we are setting. We will reset
  80. the cookie if the user navigates to `"/path/to/script?reset=1"`.
  81. The `reset` variable in the query string is a GET variable. The request data
  82. is accessed through a proxy class which works just like a `std::map<>` with
  83. some extra features.
  84. One of them is `pick`, which looks up a key in the map and returns the value
  85. if it is found. Otherwise it returns a default value, which is the second
  86. argument.
  87. The default value can be any type that supports
  88. [@http://boost.org/libs/lexical_cast Boost.Lexical_cast]. If the key isn't
  89. found in the map, or the value cannot be cast to the type of the default
  90. value, the default is returned.
  91. >*/
  92. if (req.get.pick<std::string>("reset", "") == "1")
  93. {
  94. resp<< cgi::cookie("user_name") /*<
  95. Set a cookie with no value to delete it.
  96. >*/
  97. << cgi::redirect(req, req.script_name()) /*<
  98. The `cgi::redirect` free function returns a `"Location"` header that will
  99. redirect the user to the specified URL. This URL can be a relative or absolute
  100. but an absolute URL is always returned. To perform an internal redirect, use
  101. `cgi::location` instead.
  102. >*/
  103. << cgi::content_type("text/plain");
  104. }
  105. else
  106. {
  107. std::string user_name( req.cookies["user_name"] );
  108. /*<
  109. Looking up a request cookie in `req.cookies` really returns a `cgi::cookie`.
  110. The line above works though because a `cgi::cookie` is implicitly convertible
  111. to a `std::string`.
  112. The lookup is case-insensitive, so "USER_NAME" and "User_Name" would be
  113. equivalent lookup keys.
  114. If the cookie is set, we'll be polite and say hello before quitting.
  115. >*/
  116. if (!user_name.empty())
  117. {
  118. resp<< cgi::content_type("text/html")
  119. << "<p>Hello there, " << req.cookies["user_name"]
  120. << ". How are you?</p>"
  121. << "<a href=\"" << req.script_name() << "?reset=1\">Reset</a>";
  122. }
  123. }
  124. /*<
  125. That's all we want to say for now, so we can return.
  126. If you are familiar with CGI programming, you will notice the lack of any
  127. HTTP headers in the response. A `cgi::response` handles headers separately
  128. to the body. You can set headers at any point and when you send the response
  129. the headers will all be sent first.
  130. If you don't explicitly set any response headers, a default header
  131. `"Content-type: text/plain"` is sent, followed by the usual HTTP end-of-line
  132. `"\r\n"` and a blank line which indicates the end of the headers and the
  133. start of response body.
  134. >*/
  135. } else
  136. /*<
  137. If the cookie isn't set, we will check if the user has posted a __GET__/
  138. __POST__ form with their name.
  139. >*/
  140. if (req.form.count("user_name"))
  141. {
  142. std::string user_name (req.form["user_name"]);
  143. /*<
  144. If they have told us their name, we should set a cookie so we remember it next
  145. time. Then we can say hello and exit.
  146. There are two ways to set a cookie: either directly using
  147. `req.set_cookie("user_name", user_name)` or the method shown. You can also
  148. send an expiry date and a path for the cookie.[footnote
  149. See [@http://tools.ietf.org/html/rfc822 RFC822] for more.
  150. ]
  151. Note that if you set a cookie with no value, the cookie will be deleted.
  152. Again, the request object isn't buffered, so we are going to keep using the
  153. `response` in case something breaks and we end up not wanting to set the
  154. cookie. The cookie we set below will expire when the client closes their
  155. browser.
  156. This time, we shall send a Date header. If we do this (ie. send a header
  157. ourselves), we must also set the Content-type header, like below.
  158. >*/
  159. resp<< cgi::cookie("user_name", user_name)
  160. << cgi::header("Date", "Tue, 15 Nov 1994 08:12:31 GMT")
  161. << cgi::content_type("text/html")
  162. << "Hello there, " << user_name << ". You're new around here."
  163. << "user_name.length() = " << user_name.length() ;
  164. }
  165. else
  166. {
  167. /*<
  168. Now, if we have no idea who they are, we'll send a form asking them for their
  169. name. As the default `"Content-type"` header is `"text/plain"`, we'll change
  170. this to `"text/html"` so the user's browser will display the HTML form. You
  171. can do this using
  172. `set_header(req, "Content-type", "text/html")`
  173. or
  174. `resp<< header("Content-type", "text/html")`.
  175. Since writing with raw strings is error-prone, the shortcut below is available.
  176. >*/
  177. resp<< cgi::content_type("text/html")
  178. << "Hello there. What's your name?" "<p />"
  179. "<form method='POST'>"
  180. "<input type='text' name='user_name' />"
  181. "<input type='submit' />";
  182. }
  183. /*<
  184. A CGI program will handle one request each time it is invoked. Returning a
  185. non-zero status to the OS indicates an error handling the request. I don't
  186. know that HTTP servers treat non-zero exit codes specially.[footnote I may well
  187. may well be wrong about this.]
  188. To send the response back to the request, use `cgi::commit`. The third
  189. `status` argument is optional and defaults to zero. The return value of
  190. `cgi::commit` is `status`.
  191. >*/
  192. return cgi::commit(req, resp);
  193. }
  194. /*<
  195. We now have a request handler in all of it's contrived glory.
  196. The program's `main` function needs to parse the request, call the request
  197. handler defined above, and finally send the response.
  198. >*/
  199. int main(int, char**)
  200. {
  201. cgi::request req;
  202. /*<
  203. At this point, the environment variables are accessible. This includes cookie
  204. and form variables too, which are all parsed by default-constructing a
  205. `cgi::request` (this is optional).
  206. >*/
  207. cgi::response resp;
  208. /*<
  209. The `response` class provides a streaming interface for writing replies. You
  210. can write to the request object directly, but for now we're going to just
  211. use the `response`, which works well for most situations.
  212. Writing to a `response` is buffered. If an error occurs, you can simply
  213. `clear()` the response and send an error message instead. Buffered writing
  214. may not always suit your use-case (eg. returning large files), but when memory
  215. is not at a real premium, buffering the response is highly preferable.
  216. Not only does buffering avoid network latency issues, but being able to cancel
  217. the response and send another is much cleaner than sending half a response,
  218. followed by "...Ooops". A `cgi::response` is not tied to a request, so the
  219. same response can be reused across multiple requests.
  220. When sending a response that is large relative to the amount of memory
  221. available to the program, you may want to write unbuffered.
  222. >*/
  223. return handle_request(req, resp);
  224. }
  225. //]