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
#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> class HelloWorld: public Fastcgipp::Request<wchar_t> {
Now we can define our response function. It is this function that is called to generate a response for the client. 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()
{
First thing we need to do is output our HTTP header. Let us start with settting a cookie in the client using a Unicode string. Just to see how it echoes.
wchar_t langString[] = { 0x0440, 0x0443, 0x0441, 0x0441, 0x043a, 0x0438, 0x0439, 0x0000 }; out << "Set-Cookie: lang=" << langString << '\n';
Next of course we need to output our content type part of the header. Note the charset=utf-8. Remember that HTTP headers must be terminated with "\r\n\r\n". Not just "\n\n".
out << "Content-Type: text/html; charset=utf-8\r\n\r\n";
We are already set up to deal with wide characters internally, but in order to make the library (specifically the stream buffer) code convert the wide characters to UTF-8, we need to set up a proper UTF-8 locale. I've picked "en_US.UTF-8" because it is the most common, but for different primary languages and localities you might want to pick something different eg. "ru_RU.UTF-8". You could simply call out.imbue(), but since we have two streams (err and our) and a locale object stored in the class for general reference, the request class implements a member function to change all locales simultaneously. If you don't have any UTF-8 locales installed on your system, edit /etc/locale.gen, add them and then run locale-gen.
setloc(std::locale("en_US.UTF-8"));
Next we'll get some initial HTML stuff out of the way
out << "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' />"; out << "<title>fastcgi++: Echo in UTF-8</title></head><body>";
Now we are ready to start outputting session data. We'll start with the non-post session data. This data is defined and initialized in the session object which is of type Fastcgipp::Http::Session.
out << "<h1>Session Parameters</h1>"; out << "<p><b>Hostname:</b> " << session.host << "<br />"; out << "<b>User Agent:</b> " << session.userAgent << "<br />"; out << "<b>Accepted Content Types:</b> " << session.acceptContentTypes << "<br />"; out << "<b>Accepted Languages:</b> " << session.acceptLanguages << "<br />"; out << "<b>Accepted Characters Sets:</b> " << session.acceptCharsets << "<br />"; out << "<b>Referer:</b> " << session.referer << "<br />"; out << "<b>Content Type:</b> " << session.contentType << "<br />"; out << "<b>Query String:</b> " << session.queryString << "<br />"; out << "<b>Cookies:</b> " << session.cookies << "<br />"; out << "<b>Root:</b> " << session.root << "<br />"; out << "<b>Script Name:</b> " << session.scriptName << "<br />"; out << "<b>Content Length:</b> " << session.contentLength << "<br />"; out << "<b>Keep Alive Time:</b> " << session.keepAlive << "<br />"; out << "<b>Server Address:</b> " << session.serverAddress << "<br />"; out << "<b>Server Port:</b> " << session.serverPort << "<br />"; out << "<b>Client Address:</b> " << session.remoteAddress << "<br />"; out << "<b>Client Port:</b> " << session.remotePort << "<br />"; out << "<b>If Modified Since:</b> " << session.ifModifiedSince << "</p>";
Next, we will make a little loop to output the post data. The post data is stored in the associative container session.posts of type Fastcgipp::Http::Session::Posts linking field names to Fastcgipp::Http::Post objects.
out << "<h1>Post Data</h1>";
If there isn't any POST data, we'll just say so
if(session.posts.size()) for(Fastcgipp::Http::Session<wchar_t>::Posts::iterator it=session.posts.begin(); it!=session.posts.end(); ++it) { out << "<h2>" << it->first << "</h2>"; if(it->second.type==Fastcgipp::Http::Post<wchar_t>::form) { out << "<p><b>Type:</b> form data<br />"; out << "<b>Value:</b> " << it->second.value << "</p>"; } else { out << "<p><b>Type:</b> file<br />";
When the post type is a file, the filename is stored in Post::value
out << "<b>Filename:</b> " << it->second.value << "<br />"; out << "<b>Size:</b> " << it->second.size << "<br />"; out << "<b>Data:</b></p><pre>";
We will use Fastcgipp::Fcgistream::dump to send the file raw data directly to the client
out.dump(it->second.data.get(), it->second.size); out << "</pre>"; } } else out << "<p>No post data</p>";
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 false if you are not finished but want to relinquish control and allow other requests to operate. You would do this if the request needed to wait for a message to be passed back to it through the task manager.
return true; } };
#include <fastcgi++/manager.hpp> int main() { try { Fastcgipp::Manager<Echo> fcgi; fcgi.handler(); } catch(std::exception& e) { error_log(e.what()); } }
#include <fstream> #include "boost/date_time/posix_time/posix_time.hpp" #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 Echo: public Fastcgipp::Request<wchar_t> { bool response() { wchar_t langString[] = { 0x0440, 0x0443, 0x0441, 0x0441, 0x043a, 0x0438, 0x0439, 0x0000 }; out << "Set-Cookie: lang=" << langString << '\n'; out << "Content-Type: text/html; charset=utf-8\r\n\r\n"; setloc(std::locale("en_US.UTF-8")); out << "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' />"; out << "<title>fastcgi++: Echo in UTF-8</title></head><body>"; out << "<h1>Session Parameters</h1>"; out << "<p><b>Hostname:</b> " << session.host << "<br />"; out << "<b>User Agent:</b> " << session.userAgent << "<br />"; out << "<b>Accepted Content Types:</b> " << session.acceptContentTypes << "<br />"; out << "<b>Accepted Languages:</b> " << session.acceptLanguages << "<br />"; out << "<b>Accepted Characters Sets:</b> " << session.acceptCharsets << "<br />"; out << "<b>Referer:</b> " << session.referer << "<br />"; out << "<b>Content Type:</b> " << session.contentType << "<br />"; out << "<b>Query String:</b> " << session.queryString << "<br />"; out << "<b>Cookies:</b> " << session.cookies << "<br />"; out << "<b>Root:</b> " << session.root << "<br />"; out << "<b>Script Name:</b> " << session.scriptName << "<br />"; out << "<b>Content Length:</b> " << session.contentLength << "<br />"; out << "<b>Keep Alive Time:</b> " << session.keepAlive << "<br />"; out << "<b>Server Address:</b> " << session.serverAddress << "<br />"; out << "<b>Server Port:</b> " << session.serverPort << "<br />"; out << "<b>Client Address:</b> " << session.remoteAddress << "<br />"; out << "<b>Client Port:</b> " << session.remotePort << "<br />"; out << "<b>If Modified Since:</b> " << session.ifModifiedSince << "</p>"; out << "<h1>Post Data</h1>"; if(session.posts.size()) for(Fastcgipp::Http::Session<wchar_t>::Posts::iterator it=session.posts.begin(); it!=session.posts.end(); ++it) { out << "<h2>" << it->first << "</h2>"; if(it->second.type==Fastcgipp::Http::Post<wchar_t>::form) { out << "<p><b>Type:</b> form data<br />"; out << "<b>Value:</b> " << it->second.value << "</p>"; } else { out << "<p><b>Type:</b> file<br />"; out << "<b>Filename:</b> " << it->second.value << "<br />"; out << "<b>Size:</b> " << it->second.size << "<br />"; out << "<b>Data:</b></p><pre>"; out.dump(it->second.data.get(), it->second.size); out << "</pre>"; } } else out << "<p>No post data</p>"; out << "</body></html>"; return true; } }; int main() { try { Fastcgipp::Manager<Echo> fcgi; fcgi.handler(); } catch(std::exception& e) { error_log(e.what()); } }