29 | | Using an alternate protocol (FastCGI in this case) will alter the above like so: |
30 | | {{{ |
31 | | #!cpp |
32 | | int sub_main(cgi::request req) |
33 | | { |
34 | | cgi::reply rep; // see Design Notes for more about this |
35 | | |
36 | | rep<< "Hello, " << req.param<cgi::POST>("user_name") << "!"; |
37 | | rep.send(req); |
| 39 | |
| 40 | Protocols such as SCGI and FastCGI allow a single process to handle multiple requests. The simplest way to turn the above into an SCGI ''daemon'' is to use the synchronous API provided by the library. For example: |
| 41 | |
| 42 | {{{ |
| 43 | #!cpp |
| 44 | #include <boost/cgi/scgi.hpp> // Include only SCGI functionality |
| 45 | |
| 46 | using namespace boost::scgi; |
| 47 | |
| 48 | int main() |
| 49 | { |
| 50 | service s; // This manages the request queue, amongst other things. More about this later. |
| 51 | request req(s); // Constructed with the service from above. |
| 52 | response resp; |
| 53 | acceptor a(s); // This is responsible for 'accepting' requests from the server. |
| 54 | |
| 55 | // Initialise anything else you want to keep between |
| 56 | |
| 57 | while(a.accept(req) == 0) // no error here |
| 58 | { |
| 59 | std::string name(req.GET("user_name")); |
| 60 | resp<< "Hello there, " |
| 61 | << (name.empty()? "stranger" : name) |
| 62 | << "!"; |
| 63 | resp.send(req.client()); |
| 64 | req.close(http::ok); |
| 65 | resp.clear(); |
| 66 | } |
| 70 | }}} |
| 71 | |
| 72 | Using a multiplexing protocol (SCGI or FastCGI) in a multi-threaded environment can be much more flexible than the example above allows. In many (if not most) cases you will be able to increase the throughput of your daemon by handling more than one request at a time (ie. within each process). To get the most out of your process you should use the asynchronous functionality of the library - kindly provided by Boost.Asio. Making a fully asynchronous program requires a different approach to synchronous ones and can be quite mind-bending until you are used to it. |
| 73 | |
| 74 | That said, you can still benefit from the asynchronous nature of the library without complicating your program by distinguishing between '''accepting''' requests and '''using''' them. For the example below, we can first create a Server, the purpose of which is to accept requests and pre-load the data from the clients (this is likely sub-optimal, but keeps the demonstration more to the point). The class shown is also not generic - it's only useable with FastCGI programs - but this is just to keep it concise. A generic version is provided (or should be soon) in the distribution. |
| 75 | |
| 76 | {{{ |
| 77 | #!cpp |
| 78 | #include <boost/cgi/fcgi.hpp> |
| 79 | #include <boost/function.hpp> // Really cool library! |
| 80 | |
| 81 | using boost::fcgi; |
| 82 | |
| 83 | // Define the Server class |
| 84 | class Server |
| 85 | { |
| 86 | public: |
| 87 | Server() |
| 88 | : service_() |
| 89 | , acceptor_(service_) |
| 90 | { |
| 91 | } |
| 92 | |
| 93 | void run() |
| 94 | { |
| 95 | start_accept(); |
| 96 | } |
| 97 | |
| 98 | void start_accept() |
| 99 | { |
| 100 | fcgi::request::pointer new_request(fcgi::request::create(service_)); |
| 101 | acceptor_.async_accept(new_request, boost::bind(&Server::handle_accept, new_request)); |
| 102 | } |
| 103 | |
| 104 | void handle_accept(fcgi::request::pointer req, boost::system::error_code& ec) |
| 105 | { |
| 106 | if (!ec) { // no errors, so load the request data |
| 107 | req->async_load(fcgi::parse_all, boost::bind(&Server::handle_load, req)); |
| 108 | } |
| 109 | start_accept(); // start another request |
| 110 | } |
| 111 | |
| 112 | void handle_load(fcgi::request::pointer req, boost::system::error_code& ec) |
| 113 | { |
| 114 | if (!ec) { // no errors |
| 115 | handler_(*req); // Call the user-supplied handler |
| 116 | } else { |
| 117 | req.abort(http::bad_request); |
| 118 | } |
| 119 | } |
| 120 | private: |
| 121 | fcgi::service service_; |
| 122 | fcgi::acceptor acceptor_; |
| 123 | boost::function<int (fcgi::request)> handler_; // The user-supplied handler function. |
| 124 | }; |
| 125 | }}} |
| 126 | |
| 127 | You'll notice that this request runs in an infinite loop and will possibly accept requests faster than you can handle them. We'll deal with that later. For now, we can use the above example class. |
| 128 | |
| 129 | {{{ |
| 130 | #!cpp |
| 131 | #include <boost/cgi/fcgi.hpp> |
| 132 | #include "Server.hpp" // The above example |
| 133 | |
| 134 | using namespace boost::fcgi; |
| 135 | |
| 136 | // This is where you deal with the request |
| 137 | int sub_main(request& req) |
| 138 | { |
| 139 | response resp; |
| 140 | |
| 141 | resp<< "Hello, " |
| 142 | << req.form("user_name") // this accepts either GET or POST variables. |
| 143 | << "!"; |
| 144 | resp.send(req.client()); |
| 145 | |
| 146 | return req.close(http::ok); // we have to explicitly close the requests now. |
| 147 | } |
60 | | '''Separation of ```cgi::request``` and ```cgi::reply```''': |
61 | | |
62 | | This separation is only a recent change. The main reasoning is that equivalent meta-data exists for both the request and the reply (ie. same identifier, different value). Using getters/setters is one idea, although in a large program, there could be a situation where you set a response header and then need to check it later. If everything was done with the ```request``` object then there'd be no clean way to achieve this. |
| 170 | '''Separation of ```cgi::request``` and ```cgi::response```''': |
| 171 | |
| 172 | This separation is only a recent change. The main reasoning is that equivalent meta-data exists for both the request and the response (ie. same identifier, different value). Using getters/setters is one idea, although in a large program, there could be a situation where you set a response header and then need to check it later. If everything was done with the ```request``` object then there'd be no clean way to achieve this. |