root/trunk/0010_HTTPServer/HTTPServerTest.cpp

User picture

Author: Setsu

Revision: 348 («Previous)


File Size: 10.3 KB

(September 09, 2010 20:00 UTC) Over 1 year ago

State for checkboxes stays on Update button click.

 
Show/hide line numbers
//
//  HTTPServerTest.cpp
//
//  Created by Setsu on 5/5/10.
//  Copyright 2010 RoundSquare Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
// 
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//

//----------------------------------------
//	include
//----------------------------------------
#include <Poco/Net/HTTPServer.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPRequestHandlerFactory.h>
#include <Poco/Net/HTTPServerParams.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/HTTPServerParams.h>
#include <Poco/Net/HTMLForm.h>
#include <Poco/Net/PartHandler.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/ServerSocket.h>
#include <Poco/CountingStream.h>
#include <Poco/NullStream.h>
#include <Poco/StreamCopier.h>
#include <Poco/Util/ServerApplication.h>
#include <Poco/Util/Option.h>
#include <Poco/Util/OptionSet.h>
#include <Poco/Util/HelpFormatter.h>
#include <Poco/NumberParser.h>
#include <Poco/Format.h>
#include <Poco/ActiveMethod.h>
#include <Poco/ActiveDispatcher.h>
#include <Poco/Tuple.h>

#include <iostream>

#include "ScopedElapsedTime.h"
#include "ScopedLogMessage.h"

//----------------------------------------
//	typedef
//----------------------------------------
typedef Poco::Tuple<std::size_t, std::size_t, bool> FunctionTestResultType;

//----------------------------------------
//	const
//----------------------------------------
const int kNumFunction = 3;
const std::size_t kFailFunction = 1;

//----------------------------------------
//	struct
//----------------------------------------
struct DiagnosticsResult
{
	std::size_t	testNum;
	int			result;
};

//----------------------------------------
//	ActiveDiagnosticsDispatcher
//----------------------------------------
class ActiveDiagnosticsDispatcher : public Poco::ActiveDispatcher
{
public:
	ActiveDiagnosticsDispatcher() :
		test(this, &ActiveDiagnosticsDispatcher::testImpl)
 	,	m_msg(" ActiveDiagnosticsDispatcher ", "start\n", "end")
	{
	}

	Poco::ActiveMethod<DiagnosticsResult, std::size_t,
						ActiveDiagnosticsDispatcher, Poco::ActiveStarter<Poco::ActiveDispatcher> > test;

protected:
	// Single Threaded Execution pattern
	DiagnosticsResult testImpl(const std::size_t& testNum)
	{
		ScopedElapsedTime msg(" ActiveDiagnosticsDispatcher::testImpl ", "start", "end");

		// simulate time consuming job
		Poco::Thread::sleep(50);	// 50mSec

		DiagnosticsResult rtn;
		rtn.testNum = testNum;
		rtn.result = (kFailFunction == testNum) ? -1:0;	// test #(kFailFunction) always fails

		msg.Message(Poco::format("  test #%z result: %d", rtn.testNum, rtn.result));

		return rtn;
	}

	ScopedLogMessage	m_msg;
};

//----------------------------------------
//	FormRequestHandler
//----------------------------------------
class FormRequestHandler : public Poco::Net::HTTPRequestHandler
{
public:
	FormRequestHandler(	std::vector<FunctionTestResultType>& functionTestResults
					,	ActiveDiagnosticsDispatcher& diagnosticsDispatcher) :
		m_functionTestResults(functionTestResults)
	,	m_diagnosticsDispatcher(diagnosticsDispatcher)
 	,	m_msg(" FormRequestHandler ", "start", "end\n")
	{
	}
	
	void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
	{
		m_msg.Message("  Request from " + request.clientAddress().toString());

		Poco::Net::HTMLForm form(request, request.stream());

		response.setChunkedTransferEncoding(true);
		response.setContentType("text/html");

		std::deque< Poco::ActiveResult<DiagnosticsResult> > activeResults;

		if(!form.empty())
		{
			Poco::Net::NameValueCollection::ConstIterator itr = form.begin();
			Poco::Net::NameValueCollection::ConstIterator end = form.end();
			for(; itr != end; ++itr)
			{
				activeResults.push_back(m_diagnosticsDispatcher.test(Poco::NumberParser::parse(itr->first)));
			}
		}

		bool changedFlag = false;
		bool changedItem[kNumFunction] = {false};

		while(0 != activeResults.size())
		{
			Poco::ActiveResult<DiagnosticsResult>& result = activeResults.front();
			result.wait();

			if(0 == result.data().result)
			{
				m_functionTestResults[result.data().testNum].set<0>(1 + m_functionTestResults[result.data().testNum].get<0>());
			}
			else
			{
				m_functionTestResults[result.data().testNum].set<1>(1 + m_functionTestResults[result.data().testNum].get<1>());
			}

			changedFlag = true;
			changedItem[result.data().testNum] = true;

			activeResults.pop_front();
		}

		if(changedFlag)
		{
			for(int i=0; i<kNumFunction; ++i)
			{
				m_functionTestResults[i].set<2>(changedItem[i]);
			}
		}

		std::ostream& ostr = response.send();

		ostr <<
			"<html>\n"
			"<head>\n"
			"<title>Diagnostics test</title>\n"
			"</head>\n"
			"<body>\n"
			"<h2>Diagnostics test</h2>\n"
			"<form method='POST' action='/'>\n"
			"<table border='1'>\n"
			"<tbody>\n";
		for(std::size_t i=0; i<m_functionTestResults.size(); ++i)
		{
			ostr << Poco::format("<tr><td>Function #%z</td><td><input type='checkbox' name='%z' %s></td></tr>\n"
								,	i+1
								,	i
								,	std::string(m_functionTestResults[i].get<2>() ? "checked":"")	);
		}
		ostr <<
			"<tr><td colspan='2' align='center'><input type='submit' value='Test It!'></td></tr>\n"
			"</tbody>\n"
			"</table>\n"
			"</form>\n"
			"<h2>Test results</h2>\n"
			"<form method='POST' action='/'>\n"
			"<table border='1'>\n"
			"<tbody>\n"
			"<tr><th></th><th>Trial</th><th>OK</th><th>NG</th></tr>\n";
		for(std::size_t i=0; i<m_functionTestResults.size(); ++i)
		{
			ostr <<
				Poco::format(	"<tr><td>Function #%z</td><td>%z</td><td>%z</td><td>%z</td></tr>\n"
								,	i+1
								,	m_functionTestResults[i].get<0>() + m_functionTestResults[i].get<1>()
								,	m_functionTestResults[i].get<0>()
								,	m_functionTestResults[i].get<1>()	);
		}
		ostr << 
			"<tr><td colspan='4' align='center'><input type='submit' value='Update'></td></tr>\n"
			"</tbody>\n"
			"</table>\n";
		ostr << 
			Poco::format("<font color=\"red\">(Function #%z %always fails)</font>\n", kFailFunction+1);
		ostr << 
			"</form>\n"
			"</body>\n";
	}

private:
	std::vector<FunctionTestResultType>&	m_functionTestResults;
	ActiveDiagnosticsDispatcher&			m_diagnosticsDispatcher;
	ScopedLogMessage						m_msg;
};

//----------------------------------------
//	FormRequestHandlerFactory
//----------------------------------------
class FormRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory
{
public:
	FormRequestHandlerFactory(	std::vector<FunctionTestResultType>& functionTestResults
							,	ActiveDiagnosticsDispatcher& diagnosticsDispatcher) :
		m_functionTestResults(functionTestResults)
	,	m_diagnosticsDispatcher(diagnosticsDispatcher)
	{
	}

	Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request)
	{
		return new FormRequestHandler(m_functionTestResults, m_diagnosticsDispatcher);
	}

private:
	std::vector<FunctionTestResultType>&	m_functionTestResults;
	ActiveDiagnosticsDispatcher&			m_diagnosticsDispatcher;
};

//----------------------------------------
//	MyHTTPServer
//		To test the MyHTTPServer you can use any web browser (http://localhost:9980/).
//----------------------------------------
class MyHTTPServer : public Poco::Util::ServerApplication
{
public:
	MyHTTPServer() :
		m_helpRequested(false)
	,	m_functionTestResults(kNumFunction, FunctionTestResultType(0, 0, true))
	{
	}
	
	~MyHTTPServer()
	{
	}

protected:
	void initialize(Poco::Util::Application& self)
	{
		loadConfiguration(); // load default configuration files, if present
		Poco::Util::ServerApplication::initialize(self);
	}
		
	void uninitialize()
	{
		Poco::Util::ServerApplication::uninitialize();
	}

	void defineOptions(Poco::Util::OptionSet& options)
	{
		Poco::Util::ServerApplication::defineOptions(options);
		
		options.addOption(
			Poco::Util::Option("help", "h", "display help information on command line arguments")
							.required(false)
							.repeatable(false));
	}

	void handleOption(const std::string& name, const std::string& value)
	{
		Poco::Util::ServerApplication::handleOption(name, value);

		if(name == "help")
		{
			m_helpRequested = true;
		}
	}

	void displayHelp()
	{
		Poco::Util::HelpFormatter helpFormatter(options());
		helpFormatter.setCommand(commandName());
		helpFormatter.setUsage("OPTIONS");
		helpFormatter.setHeader("A web server for diagnostics test.");
		helpFormatter.format(std::cout);
	}

	int main(const std::vector<std::string>& args)
	{
		ScopedLogMessage msg(" main() ", "start", "end");

		if(m_helpRequested)
		{
			displayHelp();
		}
		else
		{
			unsigned short port = (unsigned short) config().getInt("MyHTTPServer.port", 9980);

			ActiveDiagnosticsDispatcher	diagnosticsDispatcher;

			Poco::Net::ServerSocket svs(port);
			Poco::Net::HTTPServer srv(new FormRequestHandlerFactory(m_functionTestResults
																,	diagnosticsDispatcher),
										svs, new Poco::Net::HTTPServerParams);
			srv.start();

			// wait for CTRL-C or kill
			waitForTerminationRequest();

			srv.stop();
		}
		return Poco::Util::Application::EXIT_OK;
	}
	
private:
	bool								m_helpRequested;
	std::vector<FunctionTestResultType>	m_functionTestResults;
};

//----------------------------------------
//	main
//----------------------------------------
POCO_SERVER_MAIN(MyHTTPServer)