All code and data is located in the examples directory of the tarball. Make sure to link this with the following library options: -lfastcgipp -lboost_thread -lboost_system
#include <fstream> #include <boost/date_time/posix_time/posix_time.hpp> void error_log(const char* msg) { using namespace std; using namespace boost; static ofstream error; if(!error.is_open()) { error.open("/tmp/errlog", ios_base::out | ios_base::app); error.imbue(locale(error.getloc(), new posix_time::time_facet())); } error << '[' << posix_time::second_clock::local_time() << "] " << msg << endl; }
#include <fastcgi++/request.hpp> #include <cstring> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/thread.hpp> #include <boost/scoped_ptr.hpp> boost::asio::io_service io; class Timer: public Fastcgipp::Request<char> { private:
We'll start with our data to keep track of the execution state and a pointer for our timer object.
enum State { START, FINISH } state;
boost::scoped_ptr<boost::asio::deadline_timer> t;
Now we can define our response function. It is this function that is called to generate a response for the client. We'll start it off with a switch statement that tests our execution state. It isn't a good idea to define the response() function inline as it is called from numerous spots, but for the examples readability we will make an exception.
bool response() { switch(state) { case START: {
First thing we'll do is output our HTTP header. Note the charset=ISO-8859-1. Remember that HTTP headers must be terminated with "\r\n\r\n". NOT just "\n\n".
out << "Content-Type: text/html; charset=ISO-8859-1\r\n\r\n";
Some standard HTML header output
out << "<html><head><meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1' />"; out << "<title>fastcgi++: Threaded Timer</title></head><body>";
Output a message saying we are starting the timer
out << "Starting Timer...<br />";
If we want to the client to see everything that we just outputted now instead of when the request is complete, we will have to flush the stream buffer as it's just sitting in there now.
out.flush();
Now let's make a five second timer.
t.reset(new boost::asio::deadline_timer(io, boost::posix_time::seconds(5)));
Now we work with our Fastcgipp::Request::callback. It is a boost::function that takes a Fastcgipp::Message as a single argument. This callback function will pass the message on to this request thereby having Fastcgipp::Request::response() called function again. The callback function is thread safe. That means you can pass messages back to requests from other threads.
First we'll build the message we want sent back here. Normally the message would be built by whatever is calling the callback, but this is just a simple example. A type of 0 means a FastCGI record and is used internally. All other values we can use ourselves to define different message types (sql queries, file grabs, etc...). In this example we will use type=1 for timer stuff.
Fastcgipp::Message msg; msg.type=1; { char cString[] = "I was passed between two threads!!"; msg.size=sizeof(cString); msg.data.reset(new char[sizeof(cString)]); std::strncpy(msg.data.get(), cString, sizeof(cString)); }
Now we can give our callback function to our timer.
t->async_wait(boost::bind(callback, msg));
We need to set our state to FINISH so that when this response is called a second time, we don't repeat this.
state=FINISH;
Now we will return and allow the task manager to do other things (or sleep if there is nothing to do). We must return false if the request is not yet complete.
return false; }
Next step, we define what's done when Fastcgipp::Request::responce() is called a second time.
case FINISH:
{
Whenever Fastcgipp::Request::response() is called, the Fastcgipp::Message that lead to it's calling is stored in Fastcgipp::Request::message.
out << "Timer Finished! Our message data was \"" << message.data.get() << "\""; out << "</body></html>";
And we're basically done defining our response! All we need to do is return a boolean value. Always return true if you are done. This will let apache and the manager know we are done so they can destroy the request and free it's resources.
return true; } } }
Now we can define our default constructor.
public:
Timer(): state(START) {}
};
#include <fastcgi++/manager.hpp> int main() { try { boost::asio::io_service::work w(io); boost::thread t(boost::bind(&boost::asio::io_service::run, &io)); Fastcgipp::Manager<Timer> fcgi; fcgi.handler(); } catch(std::exception& e) { error_log(e.what()); } }
#include <fstream> #include <boost/date_time/posix_time/posix_time.hpp> #include <cstring> #include <boost/asio.hpp> #include <boost/bind.hpp> #include <boost/thread.hpp> #include <boost/scoped_ptr.hpp> boost::asio::io_service io; #include <fastcgi++/request.hpp> #include <fastcgi++/manager.hpp> void error_log(const char* msg) { using namespace std; using namespace boost; static ofstream error; if(!error.is_open()) { error.open("/tmp/errlog", ios_base::out | ios_base::app); error.imbue(locale(error.getloc(), new posix_time::time_facet())); } error << '[' << posix_time::second_clock::local_time() << "] " << msg << endl; } class Timer: public Fastcgipp::Request<char> { private: enum State { START, FINISH } state; boost::scoped_ptr<boost::asio::deadline_timer> t; bool response() { switch(state) { case START: { out << "Content-Type: text/html; charset=ISO-8859-1\r\n\r\n"; out << "<html><head><meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1' />"; out << "<title>fastcgi++: Threaded Timer</title></head><body>"; out << "Starting Timer...<br />"; out.flush(); t.reset(new boost::asio::deadline_timer(io, boost::posix_time::seconds(5))); Fastcgipp::Message msg; msg.type=1; { char cString[] = "I was passed between two threads!!"; msg.size=sizeof(cString); msg.data.reset(new char[sizeof(cString)]); std::strncpy(msg.data.get(), cString, sizeof(cString)); } t->async_wait(boost::bind(callback, msg)); state=FINISH; return false; } case FINISH: { out << "Timer Finished! Our message data was \"" << message.data.get() << "\""; out << "</body></html>"; return true; } } } public: Timer(): state(START) {} }; int main() { try { boost::asio::io_service::work w(io); boost::thread t(boost::bind(&boost::asio::io_service::run, &io)); Fastcgipp::Manager<Timer> fcgi; fcgi.handler(); } catch(std::exception& e) { error_log(e.what()); } }