PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://github.com/darrengarvey/cgi
C++ | 253 lines | 103 code | 18 blank | 132 comment | 7 complexity | 007ff3ef1a91594d1e6b3aa3f76c03d6 MD5 | raw file
  1. // -- fastcgi_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. //[fastcgi_quickstart
  10. #include <iostream> // for std::cerr
  11. #include <string>
  12. #include <boost/config/warning_disable.hpp>
  13. #include <boost/spirit/include/qi.hpp>
  14. #include <boost/spirit/include/phoenix_operator.hpp>
  15. /*<
  16. A catch-all header is available which includes all of the headers you should
  17. need for FastCGI.
  18. For the sake of clarity we alias the `boost::fcgi` namespace rather than
  19. dumping all of the library names with a `using namespace`. This way, you can
  20. see what comes from the library.
  21. >*/
  22. #include <boost/cgi/fcgi.hpp>
  23. namespace fcgi = boost::fcgi;
  24. /*<
  25. The following code is taken straight from the calc3 example in
  26. [@http://boost.org/libs/spirit Boost.Spirit]. The only difference is to
  27. use `float`s rather than `int`s.
  28. >*/
  29. namespace client
  30. {
  31. namespace qi = boost::spirit::qi;
  32. namespace ascii = boost::spirit::ascii;
  33. ///////////////////////////////////////////////////////////////////////////
  34. // Our calculator grammar
  35. ///////////////////////////////////////////////////////////////////////////
  36. template <typename Iterator>
  37. struct calculator : qi::grammar<Iterator, float(), ascii::space_type>
  38. {
  39. calculator() : calculator::base_type(expression)
  40. {
  41. using qi::_val;
  42. using qi::_1;
  43. using qi::float_;
  44. expression =
  45. term [_val = _1]
  46. >> *( ('+' >> term [_val += _1])
  47. | ('-' >> term [_val -= _1])
  48. )
  49. ;
  50. term =
  51. factor [_val = _1]
  52. >> *( ('*' >> factor [_val *= _1])
  53. | ('/' >> factor [_val /= _1])
  54. )
  55. ;
  56. factor =
  57. float_ [_val = _1]
  58. | '(' >> expression [_val = _1] >> ')'
  59. | ('-' >> factor [_val = -_1])
  60. | ('+' >> factor [_val = _1])
  61. ;
  62. }
  63. qi::rule<Iterator, float(), ascii::space_type> expression, term, factor;
  64. };
  65. } // namespace client
  66. /*<
  67. The first thing to do is write a handler function which takes a request and a
  68. response and does all request-specific work. Later, we will look at writing
  69. the code that calls this function.
  70. >*/
  71. int handle_request(fcgi::request& req)
  72. {
  73. /*<
  74. A FastCGI request is not loaded or parsed by default.
  75. >*/
  76. req.load(fcgi::parse_all);
  77. /*<
  78. Now that the request has been loaded, we can access all of the request data.
  79. The request data is available using `std::map<>`-like public members of a
  80. `fcgi::request`.[footnote
  81. The data is stored internally in a single `fusion::vector<>`
  82. ]
  83. A FastCGI request has several types of variables available. These are listed
  84. in the table below, assuming that `req` is an instance of `fcgi::request`:
  85. [table
  86. [[Source] [Variable] [Description]]
  87. [
  88. [Environment] [`req.env`] [The environment of a FastCGI request contains
  89. most of the information you will need to handle a request. There is a basic
  90. set of common environment variables that you can expect to be set by most
  91. HTTP servers around. A list of them is available on the __TODO__ (link)
  92. variables page.]
  93. ]
  94. [
  95. [GET] [`req.get`] [The variables passed in the query string of an HTTP GET
  96. request. The `get_data` map is multivalued and mimics a `std::multimap<>`.]
  97. ]
  98. [
  99. [POST] [`req.post`] [The HTTP POST data that is sent in an HTTP request's
  100. body. For file uploads, only the filename is stored in this map. As with
  101. `get`, the `post_data` map is multivalued and mimics a `std::multimap<>`.]
  102. ]
  103. [
  104. [Cookies] [`req.cookies`] [Cookies are sent in the HTTP_COOKIE environment
  105. variable. These can store limited amounts session information on the
  106. client's machine, such as database session ids or tracking information. As
  107. with `get`, the `cookie_data` map is multivalued and mimics a
  108. `std::multimap<>`.]
  109. ]
  110. [
  111. [File Uploads] [`req.uploads`] [File uploads, sent in an HTTP POST where
  112. the body is MIME-encoded as multipart/form-data. Uploaded files are read
  113. onto the server's file system. The value of an upload variable is the path
  114. of the file. The `upload_data` map is also multivalued.]
  115. ]
  116. [
  117. [Form] [`req.form`] [The form variables are either the GET variables or
  118. the POST variables, depending on the request method of the request.]
  119. ]
  120. ]
  121. >*/
  122. fcgi::response resp;/*<
  123. The `response` class provides a streaming interface for writing replies. You
  124. can write to the request object directly, but for now we're going to use the
  125. `response`, which works well for most situations.
  126. As you can see, the `response` is decoupled from the rest of the library. The
  127. advantages of using it over any other custom container are separate handling
  128. of the response body and headers, and a [funcref
  129. boost::cgi::common::response::send send()] function which wraps the whole
  130. response and writes it out to the client who instigated the request.
  131. Writing to a `response` is buffered. If an error occurs, you can `clear()` the
  132. response and send an error message instead. Buffered writing may not always
  133. suit your use-case (eg. returning large files), but when memory is not at a
  134. real premium, buffering the response is highly preferable.
  135. Not only does buffering help with network latency issues, but being able to
  136. cancel the response and send another at any time is almost essential when
  137. an error can crop up at any time. A `cgi::response` is not tied to a
  138. request, so the same response can be reused across multiple requests.[footnote
  139. Not with plain CGI though, of course.]
  140. When sending a response that is large relative to the amount of memory
  141. available to the system, you may need to write unbuffered.
  142. >*/
  143. if (req.form.count("expression"))
  144. {
  145. resp<< "<fieldset><legend>Result</legend><pre>";
  146. using boost::spirit::ascii::space;
  147. typedef std::string::const_iterator iterator_type;
  148. typedef client::calculator<iterator_type> calculator;
  149. calculator calc; // Our grammar
  150. std::string str ( req.form["expression"] );
  151. float result;
  152. if (!str.empty())
  153. {
  154. std::string::const_iterator iter = str.begin();
  155. std::string::const_iterator end = str.end();
  156. bool r = phrase_parse(iter, end, calc, space, result);
  157. if (r && iter == end)
  158. {
  159. resp << "-------------------------\n";
  160. resp << "Parsing succeeded\n";
  161. resp << "result = " << result << '\n';
  162. resp << "-------------------------\n";
  163. }
  164. else
  165. {
  166. std::string rest(iter, end);
  167. resp << "-------------------------\n";
  168. resp << "Parsing failed\n";
  169. resp << "stopped at: \": " << rest << "\"\n";
  170. resp << "-------------------------\n";
  171. }
  172. }
  173. else
  174. {
  175. resp<< "No expression found.";
  176. }
  177. resp<< "</pre></fieldset>";
  178. }
  179. resp<< "<form method='post' id=''>"
  180. << " Expression: <input type='text' name='expression' value='"
  181. << req.form["expression"] << "'><br />"
  182. << " <input type='submit' value='Calculate!'>"
  183. << "</form>"
  184. << fcgi::content_type("text/html");
  185. /*<
  186. Finally, send the response back and close the request.
  187. >*/
  188. return fcgi::commit(req, resp);
  189. }
  190. /*<
  191. We now have a request handler in all of it's contrived glory.
  192. The program's `main` function needs to parse the request, call the request
  193. handler defined above, and finally send the response.
  194. >*/
  195. int main(int, char**)
  196. {
  197. fcgi::service service; /*<
  198. A Service handles asynchronous operations and some of the protocol-specific
  199. bits.
  200. >*/
  201. fcgi::acceptor acceptor(service); /*<
  202. An `Acceptor` handles accepting requests and little else.
  203. >*/
  204. int status;
  205. /*<
  206. Keep accepting requests until the handler returns an error.
  207. >*/
  208. do {
  209. /*<
  210. The function `boost::fcgi::acceptor::accept` has a few overloads. The one used
  211. here takes a function or function object with the signature:
  212. ``
  213. boost::function<int (boost::fcgi::request&)>
  214. ``
  215. ie. A function that takes a reference to a `request` and returns an `int`.
  216. The returned `int` should be non-zero if the request was handled with
  217. an error.
  218. >*/
  219. status = acceptor.accept(&handle_request);
  220. if (status)
  221. std::cerr
  222. << "Request handled with error. Exit code: "
  223. << status << std::endl;
  224. } while (!status);
  225. return status;
  226. }
  227. //]