![]() |
General Programming »
Internet / Network »
HTTP
Intermediate
License: The BSD License
A C++ Embedded Web ServerBy ravenspointGive a C++ application its own web page |
C++ (VC6, VC7, VC7.1, VC8.0), Windows (WinXP), Win32, Dev
|
||||||||||||
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Webem is a web server that you can embed in your C++ application. It makes it easy to implement a browser GUI accessible from anywhere.
Webem is based on a minimally modified version of the boost::asio web server, and permits html code to execute C++ methods.
The available embedded C++ web servers are a challenge to use, and tend not to be Windows friendly. They are not the kind of thing you want to get into just to add the ability to monitor your lab application from your cell phone.
I have tried Wt ( http://www.webtoolkit.eu/wt ) but was defeated by the installation and learning curve.
I recently began using Webio by John Bartas . I liked the concept and it worked well.
However, I still found it overly complicated to use and the server code hard to understand. I wanted something easier to use, based on a well known web server that had only been slightly modified.
A lot of the complexity of Webio is caused by using an “HML compiler” to hide the HTML pages that control the appearance of the GUI inside a file system embedded inside the application code. I prefer to have the HTML pages outside in plain view where I can adjust the GUI without recompiling the application.
I learned a lot trying to adjust Webio to my taste, and eventually was ready to build my own exactly to my requirements.
Now you need to make the HTML invoke your C++ methods. There are two things you can do:
The Webem Embedded Web server says: <!--#webem hello -->The text in angle brackets tells webem where you want to include text from the application, and "hello" is the code for the particular application method that must be invoked to provide the included text
Step2:Create the class which says hello
/// An application class which says hello class cHello { public: char * DisplayHTML() { return "Hello World"; } };Step3:Initialise Webem, telling it the address and port to listen for browser requests and where to find the index.html that begins the web page.
// Initialise web server. http::server::cWebem theServer( "0.0.0.0", // address "1570", // port ".\\"); // document rootStep4: Register the application method with webem
cHello hello; // register application method // Whenever server sees <!--#webem hello --> // call cHello::DisplayHTML() and include the HTML returned theServer.RegisterIncludeCode( "hello", boost::bind( &cHello::DisplayHTML, // member function &hello ) ); // instance of classStep4: Finally, you are ready to start the server running.
// run the server theServer.Run();
Step1: Create the website
What is your name, please? <form action=name.webem> <input name=yourname /><input value="Enter" type=submit /> </form> The Webem Embedded Web server says: <!--#webem hello -->The form provide a field to enter the user's name, and a button to submit the entered name to the server. The form attribute "action=name.webem" ensures that the webem server will call the application method registered with "name" to process the input.
Step2: Create the application class
/// An application class which says hello to the identified user class cHelloForm { string UserName; http::server::cWebem& myWebem; public: cHelloForm( http::server::cWebem& webem ) : myWebem( webem ) { myWebem.RegisterIncludeCode( "hello", boost::bind( &cHelloForm::DisplayHTML, // member function this ) ); // instance of class myWebem.RegisterActionCode( "name", boost::bind( &cHelloForm::Action, // member function this ) ); // instance of class } char * DisplayHTML() { static char buf[1000]; if( UserName.length() ) sprintf_s( buf, 999, "Hello, %s", UserName.c_str() ); else buf[0] = '\0'; return buf; } char * Action() { UserName = myWebem.FindValue("yourname"); return "/index.html"; } };The application class stores a reference to the webem server. This allows it to look after registering its own methods with the server when it is constructed, and to call the cWebem class FindValue() to extract the value of the field entered into the form.
The application class must register two methods, one to save the entered user name when the submit button is clicked, one to display the stored user name when the web page is assembled and sent to the browser.
The action methods must return the webpage which should be displayed in response to the click on the submit button.
Note that all action methods are invoked by Webem before the include methods, so that the web page always displays updated data.
Step3: Construct webem, construct the application class and run the server
// Initialise web server. http::server::cWebem theServer( "0.0.0.0", // address "1570", // port ".\\"); // document root // Initialize application code cHelloForm hello( theServer ); // run the server theServer.Run();You may need to run the server in another thread, perhaps so your application can continue logging data from a lab device. To do this, modify the call to server::run
boost::thread* pThread = new boost::thread( boost::bind( &http::server::server::run, // member function &theServer ) ); // instance of class
The demo application uses a webem control to list the contents of a SQLITE database table with the ability to add or delete records. There is a screenshot at the top of this article.
More webem controls, detailed documentation and the latest code are available here.
The boost::asio http server invokes the method request_handler::handle_request( const request& req, reply& rep). This is where the browser requests are parsed and the new page assembled to be sent back to the browser. We need to override this method in a specialization of the request handler so that, in turn, the registered application methods can be invoked.
void cWebemRequestHandler::handle_request( const request& req, reply& rep) { // check for webem action request request req_modified = req; myWebem.CheckForAction( req_modified.uri ); // call base method to do normal handling request_handler::handle_request( req_modified, rep); // Find and include any special cWebem strings myWebem.Include( rep.content ); }Unfortunately, the boost::asio server, although otherwise very elegantly designed and implemented, has not been designed with inheritance in mind. In order to allow the server to invoke the webem request handler I have made the minimal changes to the boost code
>hg diff -U1 -r 0 -r 1 --nodates diff -r 32dc1a479036 -r 86a6e2a3ff58 src/webem/server/request_handler.hpp --- a/src/webem/server/request_handler.hpp +++ b/src/webem/server/request_handler.hpp @@ -31,3 +31,3 @@ /// Handle a request and produce a reply. - void handle_request(const request& req, reply& rep); + virtual void handle_request(const request& req, reply& rep); diff -r 32dc1a479036 -r 86a6e2a3ff58 src/webem/server/server.cpp --- a/src/webem/server/server.cpp +++ b/src/webem/server/server.cpp @@ -17,3 +17,3 @@ server::server(const std::string& address, const std::string& port, - const std::string& doc_root) + request_handler& user_request_handler ) : io_service_(), @@ -23,3 +23,3 @@ connection_manager_, request_handler_)), - request_handler_(doc_root) + request_handler_( user_request_handler) { diff -r 32dc1a479036 -r 86a6e2a3ff58 src/webem/server/server.hpp --- a/src/webem/server/server.hpp +++ b/src/webem/server/server.hpp @@ -31,3 +31,3 @@ explicit server(const std::string& address, const std::string& port, - const std::string& doc_root); + request_handler& user_request_handler ); @@ -55,2 +55,5 @@ + /// The handler for all incoming requests. + request_handler& request_handler_; + /// The next connection to be accepted. @@ -58,4 +61,2 @@ - /// The handler for all incoming requests. - request_handler request_handler_; };
2008 Sep 12 - First release
2008 Sep 15 - Moved server construction into cWebem constructor, which simplifies application code
- Provided two simpler examples, "Hello World" and "Formal Hello"
2008 Sep 29 - Fixed quotes in code fragments ( I hope! )
2009 Mar 9 - Fixed include of modified server code in cWebem.h; fixed build errors in release confuguration.
2009 May 5 - Adjusted value of HTTP header Content-Length, apparently some browsers tuncate display to this length. Change provided by http://www.codeproject.com/Members/jaeheung72
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 5 May 2009 Editor: Sean Ewington |
Copyright 2008 by ravenspoint Everything else Copyright © CodeProject, 1999-2009 Web19 | Advertise on the Code Project |