Poco::Net::HTTPServer

Poco::Net::HTTPServer を使うと、簡単に HTTP Server を実現することができます。

ここでは、HTTP Server を装備しているハードウェアの自己診断機能を、Web browser からの指示で実行し、実行結果を表示するという想定のサンプルを作ってみました。

HTTPServerTest.cpp

Poco::ActiveDispatcher を継承した ActiveDiagnosticsDispatcher クラスで、テスト番号に応じた
 自己診断処理を実行。
  ActiveDiagnosticsDispatcher::testImpl に実際のハードウェア自己診断処理を実装するという想定。
Poco::Net::HTTPRequestHandler を継承した FormRequestHandler で、HTTP リクエストを処理。
Poco::Net::HTTPRequestHandlerFactory を継承した FormRequestHandlerFactory から、
 FormRequestHandler を生成。
・MyHTTPServer クラスは Poco::Util::ServerApplication を継承。
  内部で Poco::Logger を生成しているので、ScopedLogMessage や ScopedElapsedTime を使う時の
  PrepareConsoleLogger() は不要。
・コマンドライン起動時の help (-h, –help) オプション部実装。
・MyHTTPServer::main() で、ポート番号 9980 で HTTP Server 開始。
  実行ファイル(HTTPServerTest)と同じディレクトリに HTTPServerTest.properties があり、
  例えば MyHTTPServer.port = 9981 と定義されていれば、そのポート番号に置き換わる。
  defineOptions() の記述が一風変わっているが、詳しくは Poco::Util::Option 参照。
・POCO_SERVER_MAIN(MyHTTPServer) が展開され、main() になる。
 (Poco/Util/ServerApplication.h 参照)

#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 Poco::Tuple<std::size_t, std::size_t, bool> FunctionTestResultType;
 
const int kNumFunction = 3;
const std::size_t kFailFunction = 1;
 
struct DiagnosticsResult
{
	std::size_t	testNum;
	int		result;
};
 
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;
 
private:
	// 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;
};
 
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;
};
 
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;
};
 
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;
};
 
POCO_SERVER_MAIN(MyHTTPServer)

Results of execution

screen shot
スレッド動作確認のため、MyHTTPServerTest を実行しているマシン(以下自機)と他のマシン
(以下他機)の 2 台から接続してテスト。
・01 – 02 行: コンソールで、MyHTTPServerTest を起動。
・04 – 06 行: 他機 (192.168.1.7) の Web browser で、http://192.168.1.8:9980/ を開く。
         自機のアドレスは 192.168.1.8。(仮)
         FormRequestHandler は Thread ID = 2 で実行。
・08 – 10 行: 自機の Web browser で、http://localhost:9980/ を開く。
         FormRequestHandler は Thread ID = 3 で実行。
・12 – 29 行(19 – 20 行を除く):
        他機の Web browser で、Test It! ボタンを押す。
         FormRequestHandler は Thread ID = 2 で、
         ActiveDiagnosticsDispatcher は Thread ID = 1 で実行。
・19 – 20 行、31 – 42 行: 
        直後に自機の Web browser で、Test It! ボタンを押す。
         FormRequestHandler は Thread ID = 3 で、
         ActiveDiagnosticsDispatcher は Thread ID = 1 で実行。
         他機発の処理が終了してから自機の処理が始まることに注意。
・44 – 46 行: 他機の Web browser で、Update ボタンを押す。
         テスト結果表示が更新される。
・48 – 50 行: コンソールで、Control – C を入力し、MyHTTPServerTest を終了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[0]  main() start
[0]  ActiveDiagnosticsDispatcher start
 
[2]  FormRequestHandler start
[2]   Request from 192.168.1.7:49259
[2]  FormRequestHandler end
 
[3]  FormRequestHandler start
[3]   Request from 127.0.0.1:49411
[3]  FormRequestHandler end
 
[2]  FormRequestHandler start
[2]   Request from 192.168.1.7:49259
[1]  ActiveDiagnosticsDispatcher::testImpl start
[1]   test #0 result: 0
[1]  Elepsed time = 50.176mSec
[1]  ActiveDiagnosticsDispatcher::testImpl end
[1]  ActiveDiagnosticsDispatcher::testImpl start
[3]  FormRequestHandler start
[3]   Request from 127.0.0.1:49411
[1]   test #1 result: -1
[1]  Elepsed time = 50.114mSec
[1]  ActiveDiagnosticsDispatcher::testImpl end
[1]  ActiveDiagnosticsDispatcher::testImpl start
[1]   test #2 result: 0
[1]  Elepsed time = 50.182mSec
[1]  ActiveDiagnosticsDispatcher::testImpl end
[1]  ActiveDiagnosticsDispatcher::testImpl start
[2]  FormRequestHandler end
 
[1]   test #0 result: 0
[1]  Elepsed time = 50.172mSec
[1]  ActiveDiagnosticsDispatcher::testImpl end
[1]  ActiveDiagnosticsDispatcher::testImpl start
[1]   test #1 result: -1
[1]  Elepsed time = 50.153mSec
[1]  ActiveDiagnosticsDispatcher::testImpl end
[1]  ActiveDiagnosticsDispatcher::testImpl start
[1]   test #2 result: 0
[1]  Elepsed time = 50.570mSec
[1]  ActiveDiagnosticsDispatcher::testImpl end
[3]  FormRequestHandler end
 
[2]  FormRequestHandler start
[2]   Request from 192.168.1.7:49259
[2]  FormRequestHandler end
 
^C
[0]  ActiveDiagnosticsDispatcher end
[0]  main() end

Downloads

ここをクリックすると、makefile や VC++ プロジェクトなど一式がダウンロードできます。
(2010.09.10 5:10 updated)
・2010年5月7日からのダウンロード数:997

Subversion

・フリーの Subversion ホスティングサービス Assemblaで、ソースコードを管理しています。

Reference

http://pocoproject.org にある Applications のプレセンテーション。(PDF)

Powered by POCO Copyright © 2010 Round Square Inc. All rights reserved.


Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>