CppWeb is cross-platform C++ library for developing sessionless or sessionful web applications with server push support.
The library decodes CGI variables and cookies, supports file uploads, performs automatic cookie detection, provides URL and HTML entity encode/decode functions, supports server-push (long-polling via ajax), has built-in HTML parser etc.
CppWeb compiles on Windows, Linux and MacOSX (tested with GNU C++, MingW, MS Visual C++ and Borland C++ compiler) and can run with almost any web server (Apache, IIS, etc.)
CppWeb provides two main program classes: CWApp (sessionless application) and CWServer (sessionful application).
CWApp is sessionless application which executes in one-pass: receive and decode request, fire OnRequest() handler, send response and close. Session data is stored server side in file and is loaded on next execution. You just have to instantiate CWApp and write your own OnRequest()
handler.
CWServer is sessionful application. When executed it continues running waiting for new requests. When idle it can push data to user's browser via ajax (long-polling). You just have to instantiate CWServer and write your own OnRequest()
and OnCallback()
handlers. OnRequest
fires when application receives request from user and this is where you generate response page/data. OnCallback
fires automaticaly in predefined interval while waiting for request. This is where you can send data back to client (Push).
advantages: when user starts a session, his CWServer instance is running constantly during session (doesn't start/close on every request). This is useful when your program does time consuming operations on startup or work with complex/slow database queries - initialization is performed only once on first request and open datasets stays open during user session which results in great performance.
disadvantages: since every user have it's own instance of program running all the time during session, many users at a time can slow down your server performance. So if your application will be used by many users at a time, CWApp is better choice.
CWApp is sessionless application which has "short life": it read request, generate response and close.
When executed it decodes CGI variables and cookies and stores request data in Request
object (class CWRequest).
Request object has Query, Post, Cookie and Env members:
Query
stores data received via "GET" method.
Post
stores data received via "POST" method.
Cookie
stores cookies.
Env
member stores CGI environment variables.
You can access request data as easy as this:
string user_name = Request->Post->GetValue("name");
string user_ip = Request->Env->GetValue("REMOTE_ADDR");
Decoding is performed by excelent libccgi library which is slightly modified and included in source (cppw_ccgi.cpp).
After CWApp receives and stores request data, it fires OnRequest
handler which you have to instantiate and write your code to proccess request and generate reponse data.
Member Response
(class CWResponse) is for sending response to user. It can send html from string or any local file (image, video etc.), can redirect to specified url, set cookies etc.
Here is minimal example of OnRequest handler:
bool ExampleApp::OnRequest(string* pErrorMessage) {
string user_ip = Request->Env->GetValue("REMOTE_ADDR");
string HTML = "";
HTML.append("<!doctype html><html><body>");
HTML.append("<h1>Hello! Your IP address is: " + user_ip + "</p>");
HTML.append("</body></html>");
Response->Method = rmData;
Response->ContentSource = csText;
Response->Text = HTML;
return true;
}
In this example, we read user's IP address from Reguest->Env
(environment variable), create simple HTML page and set Response member. Handler returns true
which means "no error" and page is sent to client.
Session data
If you set CWApp::UseSessionData
member to true
then you can use CWApp::SessionData
to keep data which will be available on next request (next execution of CWApp). SessionData is string list object which stores data in form name=value
which you can access with SetValue(name, value)
and GetValue(name)
members. If you are using session data it's recommended to set CheckCookies
to true
. In that case CWApp will perform cookie check (redirect) before SessionID is assigned and if client browser doesn't support cookies error message is sent to user.
Full example is included in "/examples/cppw_app/" directory.
CWServer is sessionful application which is executed on first request and stays resident in memory waiting for further request from the same user (until timeout or regulary closed). Every user opens its own instance of CWServer application.
Every instance gets its own SessionID which is sent to user via cookie.
How it works?
In fact, user's request goes through CWBridge which is program between web server and CWServer. When user made request, CWBridge starts, decode request and read SessionID from cookies. If no session ID found it checks if cookies are enabled in user's browser, generates SessionID and starts CWServer with assigned this SessionID. CWBridge is then waiting for response from CWServer. When bridge receives response, It send response to user and closes. CWServer is resident in memory in Idle state. On next user's request, CWBridge starts again, read SessionID cookie and redirect request to corresponding CWServer instance, waits for response, sends it back to user and closes. This circle repeats on every request.
While server is in idle state waiting for request, it periodicaly fires OnCallback()
handler where you can push data to user based on some event. For example, in chat application if OnCallback handler receives incomming message from other user (from other instance of CWServer) it pushes message to user.
Sounds complicated?
Maybe sounds complicated, but this process is hidden from you. You just have to instantiate CWServer class and make your own OnRequest() and OnCallback() handlers.
CWServer works in the same way as CWApp with difference that it doesn't close after response is sent to user. To understand OnRequest handler, Request and Response members please read CWApp text above.
OnCallback handler fires in specified interval while CWServer is idle waiting for request from user. It should check some event/condition to decide wether to push data to user or not (for example, It can check wether user has unread messages etc.). If condition is satisfied it generates response setting up CWRequest member and call CWServer::PushResponse() member function.
Your OnCallback handler can look like this:
bool ExampleServer::OnCallback(string* pErrorMessage)
{
if(/* some condition */)
{
Response->ContentType = "application/json";
Response->ContentSource = csText;
Response->Text = "{ \"some_json_data\": \"blah blah\" }";
Response->Method = rmData;
PushResponse(pErrorMessage);
}
return true;
}
On the other side, client must have active ajax request running (long-polling request). If no active long-polling request from client detected, PushResponse does nothing.
Client side jQuery ajax request looks like this:
function longPollingRequest() {
$.ajax({
url: "/cppw_bridge",
cache: false,
data: { "long_polling": "true" },
}).done(function( response ) {
// do something with response
// ...
// send new long polling request
longPollingRequest();
});
}
longPollingRequest();
Full CWServer example is included in "/examples/cppw_server/" directory.
CWBridge is program between web server and CWServer. In order to run server, you need to compile and setup bridge first.
CWBridge is included in "/examples/cppw_bridge/" directory.
When bridge starts it searches for its configuration file which has same name as executable with extension ".conf". For example if bridge executable is "/usr/lib/cgi-bin/cppw_bridge.cgi" then config file should be "/usr/lib/cgi-bin/cppw_bridge.conf"
Bridge config file contains path to CWServer application, for example:
ServerPath=/usr/lib/cgi-bin/cppw_server.cgi
Class CWNode
is a node of DOM tree. ParseHTML()
method parses HTML string and constructs a DOM tree. This is very simple parser implementation which parses only valid well-formed documents (all tags properly closed etc.). CWNode has many DOM manipulation functions such as AddChild
, InsertChild
, DeleteChild
, FindChildByName
, FindChildByID
etc. Function GetHTML()
creates HTML string from DOM tree.
For example:
string input_string;
// Fill input_string or load it from HTML file...
// Parse input_string
CWNode document;
if(!document.ParseHTML(input_string, &error_message))
{
// error occoured...
}
// find body element
CWNode* body = document.FindChildByName("body", false, true);
if(body == NULL)
{
// body element not found...
}
// add paragraph to body
CWNode* paragraph = body->AddChild("p", "This is example paragraph.");
// construct HTML from DOM tree
string output_string = document.GetHTML();
Full example is included in "/examples/cppw_parser" directory.
GUI widgets enables you to write web application in the same way as you write classic desktop application - using widgets and events.
CWWidget
is a base class for widget objects such as: CWContainer
(HTML "div" element), CWLink
(HTML "a" element), CWNav
(for navigation and menus, HTML "ul" element), CWNavItem
(menu item, HTML "li" element), CWDropdownLink
(link with dropdown menu) etc.
CWContainer, CWPageContainer and CWPage can contain more widgets - childs.
All widgets have two important methods: EventHandler()
and Draw()
. EventHandler fires when application receives request from user. Event is propagated to all child widgets. When all childs are finished processing event, Draw() method fires for all child widgets currently visible creating DOM structure. Generated DOM structure program converts into HTML and inserts it into template. Template is HTML skeleton that you provide - It's HTML file with empty body in which you can include your custom css files for styling, javascript files etc.
There are three examples using widgets:
cppw_widgets - application demonstrates creating buttons, dropdown buttons, navigation bars etc.
cppw_sqlite - simple SQLite interactive query example.
cppw_login - simple application with register, login, change password and reset password.
Examples are using Twitter Bootstrap theme for styling. Bootstrap template is included in "/templates/" directory.
Library contains simple wrapper for SQLite database (Files "cppw_db_sqlite.cpp" and "cppw_db_sqlite.h", classes "CWDatabase" and "CWQuery").
CWDatabase is database connection class. Example usage:
string error_message = "";
CWDatabase db;
// connect to database
if(!db.Open("path_to_database_file.db", 3000, &error_message))
{
// ... error occoured
}
// execute SQL statement which doesn't return data
if(!db.Execute("INSERT INTO USERS (USR_FULL_NAME) VALUES (\"Chuck Norris\");", &error_message)
{
// ... error occoured
}
// close database connection
db.Close();
CWQuery is SQL query class which is used for retrieving data from database. Example usage:
string error_message = "";
CWDatabase db;
// connect to database
if(!db.Open("path_to_database_file.db", 3000, &error_message))
{
// ... error occoured
}
CWQuery query(&db);
// execute SELECT statement
if(!query.Open("SELECT * FROM USERS;", false, &error_message))
{
// ... error occoured
}
// read all returned rows
int row = 0;
while(!query.Eof())
{
// read user name field
string user_name = query.GetValue("USR_FULL_NAME", row, &read_success, &error_message);
if(!read_success)
{
// ... error occoured
query.Close();
}
// ... do something with user_name...
// read next row from database
if(!query.Fetch(&error_message))
{
// ... error occoured
query.Close();
}
row++;
}
// close query
query.Close();
// close database connection
db.Close();
Full example is included in "/examples/cppw_sqlite" directory. You can see live example here.
Examples are included into source package in "/examples/" directory.
There are 8 examples:
cppw_app is example CWApp application demonstrating form input, file upload and dump of CGI variables and cookies. Live example.
cppw_bridge in order to run cppw_server example, you need to compile and setup bridge first and create "cppw_bridge.conf" file.
cppw_server is example CWServer application demonstrating form input (login) and server push via long polling. Live example.
cppw_parser demonstrates HTML parsing and DOM manipulation. This is classic command line program - not a Web application. Just compile it and run from terminal.
cppw_minify this application parses HTML files in given source directory and uses YUI compressor to minify internal and external scripts and styles. Resulting minified files are then stored into destination directory. This is classic command line program - not a Web application. Just compile it and run from terminal. I use it, maybe can be useful for you too.
cppw_widgets demonstrates using CWWidget classes with Twitter Botstrap theme. Live example.
cppw_sqlite demonstrates using CWDatabase, CWQuery and CWWidget classes. This is simple SQLite interactive query application. Live example.
cppw_login is basic application with authentication: register, login, reset password and change password form. It is based on CWApp and uses SQLite database. It uses "sendmail" program to send e-mails (reset password form). Live example.
Since you are reading this text, I guess that you know how to compile code... however, for beginners:
Each example directory contains Code::Blocks project. If you are using some other IDE then just create a "New console application" project in your favourite IDE and add "main.cpp" from example directory and all ".cpp" files from "/src/" directory. Typical executable file extension for CGI applications is ".cgi" (not mandatory).
There are no external dependencies, you can easily compile examples without IDE (from command line).
Put compiled executables into your web server's "cgi-bin" directory:
Apache2 on Ubuntu linux it's typicaly located at "/usr/lib/cgi-bin/".
Apache2 on Windows it's typicaly located at "C:\Program files\Apache Software Foundation\Apache2\cgi-bin\".
Make sure that web user has "execute" access rights to "cgi-bin" directory (in apache/linux this is user named "www-data").
Configure web server to enable CGI program execution.
"cppw_server" example:
Create "cppw_bridge.conf" file as mentioned in CWBridge section of this tutorial. Example "cppw_bridge.conf" file (Linux):
ServerPath=/usr/lib/cgi-bin/cppw_server.cgi
"cppw_widgets" example:
Copy Twitter Bootstrap template from "/templates/" directory to your web server root (apache "htdocs" or "www" directory depending on platform).
Create "cppw_widgets.conf" in same directory where is your "cppw_widgets.cgi" program and enter path and relative URL to template index.html file. It should look something like this (Linux):
TemplatePath=/var/www/templates/bootstrap/index.html
TemplateRootURL=/templates/bootstrap/
"cppw_sqlite" example:
You should install sqlite dev tools and link "libsqlite3" library to your program or you can include SQLite "amalgamation" source file into your program (SQLite amalgamation is complete SQLite source code in single .c file). You can download SQLite here.
Copy Twitter Bootstrap template from "/templates/" directory to your web server root (apache "htdocs" or "www" directory depending on platform).
Create "cppw_sqlite.conf" in same directory where is your "cppw_sqlite.cgi" program and enter path and relative URL to template index.html file. Also, you need to enter full path to database file ("cppw_sqlite.db" which is included in example directory). Make sure that web user has read-write access to directory containing database file (SQLite requires both database file and directory to be read-write). Config file should look something like this (Linux):
TemplatePath=/var/www/templates/bootstrap/index.html
TemplateRootURL=/templates/bootstrap/
DatabaseName=/usr/lib/cgi-bin/db/cppw_sqlite.db
"cppw_login" example:
You should install and link "libsqlite3" library to your program (see "cppw_sqlite" above).
Reset password form uses "sendmail" program for sending e-mails, so you have to install and configure sendmail. You can download fake sendmail for Windows here.
Copy Twitter Bootstrap template from "/templates/" directory to your web server root (apache "htdocs" or "www" directory depending on platform).
Create "cppw_login.conf" in same directory where is your "cppw_login.cgi" program and enter path and relative URL to template index.html file. Also, you need to enter full path to database file ("cppw_login.db" which is included in example directory). Make sure that web user has read-write access to directory containing database file (SQLite requires both database file and directory to be read-write).
Add to config file path to your "sendmail" program and your e-mail address to be used as "from" address.
Config file should look something like this (Linux):
TemplatePath=/var/www/templates/bootstrap/index.html
TemplateRootURL=/templates/bootstrap/
DatabaseName=/usr/lib/cgi-bin/db/cppw_login.db
SendMailPath=/usr/lib/sendmail
NoReplyEMailAddress=noreply@your.e-mail
That's it.
Author: Petar Korponaić