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