So the steps of an standalone witty w/ custom resource server:
- create a server
- register a custom resource on a desired path
- start the server, hence its resources
- wait for server shutdown signal
- stop the server
Here is the snippet of the main.cpp, ignore all log-related plumbing:
#include <Wt/WServer> #include <iostream> #include "MyResource.h" #include "log.h" using namespace std; using namespace Wt; int main(int argc, char **argv) { WLogger logger; configLogger(logger); try { WServer server(argv[0], ""); try { server.setServerConfiguration(argc, argv); MyResource dr; server.addResource(&dr, "/resource"); info(logger, "Starting resource server."); if (server.start()) { WServer::waitForShutdown(); server.stop(); } else { fatal(logger, "Fatal error starting resource server."); return 1; } return 0; } catch (std::exception& e) { fatal(logger, "Fatal error starting resource server.", e.what()); return 1; } } catch (WServer::Exception& e) { fatal(logger, "Fatal error creating WServer.", e.what()); return 1; } catch (exception& e) { fatal(logger, "Fatal error occurred.", e.what()); return 1; } }
You may notice the creation and registration of MyResource, this is place where you provide the service implementation, specifically on the handleRequest() method, take a look at MyResource.h:
#ifndef MYRESOURCE_H_ #define MYRESOURCE_H_ #include <Wt/WResource> using namespace Wt; using namespace Wt::Http; class MyResource: public WResource { public: MyResource(); virtual ~MyResource(); protected: virtual void handleRequest(const Request &request, Response &response); }; #endif /* MYPRESOURCE_H_ */
The implemantation is very trivial, it's just an echo of the request content also including some extra data like content type, content length and request method: GET, POST, etc. Enjoy MyResource.cpp:
#include <iostream> #include <Wt/Http/Response> #include "MyResource.h" using namespace std; using namespace Wt::Http; MyResource::MyResource() { } MyResource::~MyResource() { } void MyResource::handleRequest(const Request& request, Response& response) { string method = request.method(); string contentType = request.contentType(); int contentLength = request.contentLength(); char* buffer = new char[contentLength + 1]; request.in().read(buffer, contentLength); buffer[contentLength] = 0; response.setMimeType("application/xml"); ostream& out = response.out(); out << "<?xml version='1.0' encoding='utf-8' ?>" << endl; out << "<reply>" << endl; out << "<method>" << method << "</method>" << endl; out << "<contentType>" << contentType << "</contentType>" << endl; out << "<contentLenght>" << contentLength << "</contentLenght>" << endl; out << "<body>" << buffer << "</body>" << endl; out << "</reply>"; delete[] buffer; }
The reamining stuff is just plumbing as I said before, here you got log.h:
#ifndef LOG_H_ #define LOG_H_ #include <Wt/WLogger> using namespace std; using namespace Wt; void info(WLogger& logger, const string& message); void fatal(WLogger& logger, const string& message, const char* what); void fatal(WLogger& logger, const string& message); void configLogger(WLogger& logger); #endif /* LOG_H_ */
And log.cpp:
#include "log.h" #include <iostream> void info(WLogger& logger, const string& message) { WLogEntry entry = logger.entry("info"); entry << WLogger::timestamp << WLogger::sep << WLogger::sep << '[' << "info" << ']' << WLogger::sep << message; } void fatal(WLogger& logger, const string& message, const char* what) { WLogEntry entry = logger.entry("fatal"); entry << WLogger::timestamp << WLogger::sep << WLogger::sep << '[' << "fatal" << ']' << WLogger::sep << message << what; } void fatal(WLogger& logger, const string& message) { fatal(logger, message, ""); } void configLogger(WLogger& logger) { logger.addField("datetime", false); logger.addField("type", false); logger.addField("message", true); logger.setFile("/var/log/resource.log"); }
Now compile an run the server, here in this case using standalone server mode instead of fast cgi mode, but also works w/ fast cgi variant:
$ g++ log.cpp MyResource.cpp main.cpp -lwthttp -oresource
Run the server:
$ ./resource --http-address 0.0.0.0 --http-port 80 --docroot=. INFO: Opened log file (/var/log/resource.log). [2013-May-07 19:52:11.046985] 9658 - [info] "config: reading Wt config file: /etc/wt/wt_config.xml (location = './resource')" [2013-May-07 19:52:11.047757] 9658 - [info] "WServer/wthttp: initializing built-in wthttpd" [2013-May-07 19:52:11.048027] 9658 - [info] "wthttp: started server: http://0.0.0.0:80"
Finally test the service using a simple call:
$ curl -X POST -H "Content-Type: application/xml" -d"<payload>PAYLOAD GOES HERE!</payload>" http://localhost/resource
<?xml version='1.0' encoding='utf-8' ?> <reply> <method>POST</method> <contentType>application/xml</contentType> <contentLenght>37</contentLenght> <body><payload>PAYLOAD GOES HERE!</payload></body> </reply>Very simple as you can see!
Very nice. Thanks!
ReplyDelete-Jim
Very clear article. I like Wt, too.
ReplyDelete