Click here to Skip to main content
15,892,643 members
Articles / Web Development / HTML

A C++ Embedded Web Server

Rate me:
Please Sign up or sign in to vote.
4.76/5 (47 votes)
23 Jun 2014BSD7 min read 451.2K   8.5K   209  
Give a C++ application its own web page
/**

@mainpage WEBEM

Detailed class and method documentation of the WEBEM C++ embedded web server source code.
<p>

Quit source documentation, return to <a href="http://66.199.140.183/cgi-bin/webem.cgi">WEBEM home page </a>


 Copyright (c) 20010 by James Bremner
 * All rights reserved.
 *
 * Use license: Modified from standard BSD license.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation, advertising
 * materials, Web server pages, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by James Bremner. The name "James Bremner" may not be used to
 * endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.


*/

#include "stdafx.h"
#include "cWebem.h"
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include "server/reply.hpp"
#include "server/request.hpp"

namespace http {
	namespace server {

	typedef std::multimap  < std::string, std::string> webem_mmp_name_value;
	typedef std::multimap  < std::string, std::string>::iterator webem_iter_name_value;

/**

Webem constructor

@param[in] address  IP address.  In general, use  "0.0.0.0"
@param[in] port     port to listen on for browser requests e.g. "8080"
@param[in] doc_root path to folder containing html e.g. "./"

*/
cWebem::cWebem(
	   const std::string& address,
	   const std::string& port,
	   const std::string& doc_root ) :
myRequestHandler( doc_root ), myPort( port ),
myServer( address, port, myRequestHandler )
{
	myRequestHandler.setWebem( this );
}

/**

Start the server.

This does not return.

If application needs to continue, start new thread with call to this method.

*/

void cWebem::Run() { myServer.run(); }


/**

Create a link between a string ID and a function to calculate the dynamic content of the string

The function should return a pointer to a character buffer.  This should be contain only ASCII characters
( Unicode code points 1 to 127 )

@param[in] idname  string identifier
@param[in] fun pointer to function which calculates the string to be displayed

*/

void cWebem::RegisterIncludeCode( const char* idname, webem_include_function fun )
{
	myIncludes.insert( std::pair<std::string, webem_include_function >( std::string(idname), fun  ) );
}
/**

Create a link between a string ID and a function to calculate the dynamic content of the string

The function should return a pointer to wide character buffer.  This should contain a wide character UTF-16 encoded unicode string.
WEBEM will convert the string to UTF-8 encoding before sending to the browser.

@param[in] idname  string identifier
@param[in] fun pointer to function which calculates the string to be displayed

*/

void cWebem::RegisterIncludeCodeW( const char* idname, webem_include_function_w fun )
{
	myIncludes_w.insert( std::pair<std::string, webem_include_function_w >( std::string(idname), fun  ) );
}
/**

Specify link between form and application function to run when form submitted

@param[in] idname string identifier
@param[in] fun fpointer to function

*/
void cWebem::RegisterActionCode( const char* idname, webem_action_function fun )
{
	myActions.insert( std::pair<std::string, webem_action_function >( std::string(idname), fun  ) );
}

		/**

		Conversion between UTF-8 and UTF-16 strings.

		UTF-8 is used by web pages.  It is a variable byte length encoding
		of UNICODE characters which is independant of the byte order in a computer word.

		UTF-16 is the native Windows UNICODE encoding.

		The class stores two copies of the string, one in each encoding,
		so should only exist briefly while conversion is done.

		This is a wrapper for the WideCharToMultiByte and MultiByteToWideChar
		*/
		class cUTF
		{
			wchar_t * myString16;		///< string in UTF-16
			char * myString8;			///< string in UTF-6
		public:
			/// Construct from UTF-16
			cUTF( const wchar_t * ws );
			///  Construct from UTF8
			cUTF( const char * s );
			/// get UTF16 version
			const wchar_t * get16() { return myString16; }
			/// get UTF8 version
			const char * get8() { return myString8; }
			/// free buffers
			~cUTF() { free(myString8); free(myString16); }
		};

		/// Construct from UTF-16
		cUTF::cUTF( const wchar_t * ws )
		{
			// store copy of UTF16
			myString16 = (wchar_t * ) malloc( wcslen( ws ) * 2 + 2 );
			wcscpy( myString16, ws );
			// How long will the UTF-8 string be
			int len = WideCharToMultiByte(CP_UTF8, 0,
				ws, wcslen( ws ),
				NULL, NULL, NULL, NULL );
			// allocate a buffer
			myString8 = (char * ) malloc( len + 1 );
			// convert to UTF-8
			WideCharToMultiByte(CP_UTF8, 0,
				ws, wcslen( ws ),
				myString8, len, NULL, NULL);
			// null terminate
			*(myString8+len) = '\0';
		}
		///  Construct from UTF8
		cUTF::cUTF( const char * s )
		{
			myString8 = (char * ) malloc( strlen( s ) + 1 );
			strcpy( myString8, s );
			// How long will the UTF-16 string be
			int len = MultiByteToWideChar(CP_UTF8, 0,
				s, strlen( s ),
				NULL, NULL );
			// allocate a buffer
			myString16 = (wchar_t * ) malloc( len * 2 + 2 );
			// convert to UTF-16
			MultiByteToWideChar(CP_UTF8, 0,
				s, strlen( s ),
				myString16, len);
			// null terminate
			*(myString16+len) = '\0';
		}


/**

  Do not call from application code, used by server to include generated text.

  @param[in/out] reply  text to include generated

  The text is searched for "<!--#cWebemX-->".
  The X can be any string not containing "-->"

  If X has been registered with cWebem then the associated function
  is called to generate text to be inserted.


*/
void cWebem::Include( std::string& reply )
{
	int p = 0;
	while( 1 ) {
		// find next request for generated text
		p = reply.find("<!--#webem",p);
		if( p == -1 ) {
			break;
		}
		int q = reply.find("-->",p);
		if( q == -1 )
			break;;
		q += 3;

		int reply_len = reply.length();

		// code identifying text generator
		std::string code = reply.substr( p+11, q-p-15 );

		// find the function associated with this code
		std::map < std::string, webem_include_function >::iterator pf = myIncludes.find( code );
		if( pf != myIncludes.end() ) {
			// insert generated text
			reply.insert( p, pf->second() );
		} else {
			// no function found, look for a wide character fuction
			std::map < std::string, webem_include_function_w >::iterator pf = myIncludes_w.find( code );
			if( pf != myIncludes_w.end() ) {
				// function found
				// get return string and convert from UTF-16 to UTF-8
				cUTF utf( pf->second() );
				// insert generated text
				reply.insert( p, utf.get8() );
			}
		}

		// adjust pointer into text for insertion
		p = q + reply.length() - reply_len;
	}
}
/**

Do not call from application code,
used by server  to handle form submissions.

*/
void cWebem::CheckForAction( request& req )
{
	std::string uri = req.uri;
	std::string code;
	int q = 0;

	// look for webem click action request
	q = uri.find("/webem_");
	if( q != -1 ) {
		code = uri.substr(q+7);
		req.uri = uri.substr(0,q);

	} else {

		// look for cWebem form action request
		if( req.method != "POST" ) {
			q = uri.find(".webem?");
			if( q == -1 )
				return;
		} else {
			q = uri.find(".webem");
			if( q == -1 )
				return;
		}
		code = uri.substr(1,q-1);
	}

	// find function matching action code

	std::map < std::string, webem_action_function >::iterator
		pfun = myActions.find(  code );
	if( pfun == myActions.end() )
		return;

	// decode the values

	if( req.method == "POST" ) {
		uri = req.content;
		q = 0;
	} else {
		q += 7;
	}


	myNameValues.clear();
	std::string name;
	std::string value;

	int p = q;
	int flag_done = 0;
	while( ! flag_done ) {
		q = uri.find("=",p);
		name = uri.substr(p,q-p);
		p = q + 1;
		q = uri.find("&",p);
		if( q != -1 )
			value = uri.substr(p,q-p);
		else {
			value = uri.substr(p);
			flag_done = 1;
		}
		// the browser sends blanks as +
		while( 1 ) {
			int p = value.find("+");
			if( p == -1 )
				break;
			value.replace( p, 1, " " );
		}

		myNameValues.insert( std::pair< std::string,std::string > ( name, value ) );
		p = q+1;
	}

	// call the function
	req.uri = pfun->second( this );

	return;
}
/**

  Find the value of a name set by a form submit action

*/
std::string& cWebem::FindValue( const char* name )
{
	static std::string ret;
	ret = "";
	webem_iter_name_value iter = myNameValues.find( name );
	if( iter != myNameValues.end() )
		ret = iter->second;

	return ret;
}

/**

  Tell user where to find the cWebem GUI

*/
std::string& cWebem::Splash()
{
	static std::string ret;
	char buf[1000];
	DWORD bufsize = 999;
	GetComputerNameEx( ComputerNameDnsHostname,
		buf,
		&bufsize );
	char buf2[1000];
	sprintf(buf2,"		cWebem: Embedded Web Server by Raven's Point\n\n"
		   "		Point browser at http://%s:%s\n",
		buf,
		myPort.c_str() );
	ret = buf2;
	return ret;
}


void cWebemRequestHandler::handle_request( const request& req, reply& rep)
{
	// check for webem action request
	request req_modified = req;

	myWebem->CheckForAction( req_modified );

	// do normal handling
	request_handler::handle_request( req_modified, rep);

	// Find and include any special cWebem strings
	myWebem->Include( rep.content );

	// adjust content length header
	// ( Firefox ignores this, but apparently some browsers truncate display without it.
	// fix provided by http://www.codeproject.com/Members/jaeheung72 )

	rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());

	// tell browser that we are using UTF-8 encoding
	rep.headers[1].value = "text/html;charset=UTF-8";

}

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Founder Raven's Point Consulting
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions