|
用惯了 Jetty 的 基于事件的 HttpClient 类,在C++平台上也没找到这样调用方式的类库,只好自己写一个了。
目前版本 1.0,朋友们看了给点建议。(注:Kylindai原创,转载请注明出处)
Feature: 基于C++跨平台的 POCO 库实现,支持线程池 Poco::ThreadPool, 异步 HttpClient, 支持Http事件回调处理。
基本思路,核心方法:
/**
* 创建多线程支持的HttpClient,
* nTimeout 超时
* nMaxThreads 最大线程数
* pLogFileName 日志文件
*/
CHttpClient.Create(int nTimeout, int nMaxThreads, char * pLogFileName = NULL);
/**
* 发送请求,此方法会创建一个 CHttpTask 放到线程池里,然后由线程发起 HTTPClientSession 请求,
* 收到响应后,回调 CHttpExchange.GetHandler().OnResponseComplate() 方法。
* 类似 Java Jetty的 HttpClient.send(HttpExchange exchange)
* pExchange CHttpExchange HTTP 交换类
*/
CHttpClient.Send(CHttpExchange * pExchange);
void CHttpClient::Send(CHttpExchange * pExchange)
{
try
{
CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
ThreadPool::defaultPool().start(* pHttpTask);
}
catch (Exception& ex)
{
if (m_pLogger != NULL)
{
m_pLogger->error(ex.displayText());
}
}
} /**
* 线程池中任务 CHttpTask.run 处理Http请求及响应
*/
CHttpTask : public Runnable
void CHttpTask::run()
{
CHttpHandler * pHandler = m_pExchange->GetHandler();
try
{
HTTPClientSession httpClientSession;
httpClientSession.setHost(m_pExchange->GetHost());
httpClientSession.setTimeout(m_timeout);
httpClientSession.sendRequest(m_pExchange->GetHttpRequest());
HTTPResponse response;
istream& rs = httpClientSession.receiveResponse(response);
if (pHandler != NULL)
{
const string& contentType = response.getContentType();
if (contentType.find("text/") >= 0)
{
stringstream responseStream;
StreamCopier::copyStream(rs, responseStream);
string responseContent;
responseStream >> responseContent;
pHandler->OnResponseComplete(response.getStatus(), responseContent);
}
}
}
catch (Exception& ex)
{
if (m_pLogger != NULL)
{
m_pLogger->error(ex.displayText());
}
}
if (pHandler != NULL)
{
delete pHandler;
}
delete this;
} /**
* 为HttpExchange 设置 Handler 处理回调类,以后具体的逻辑由 CHttpHandler 继承的具体业务处理逻辑类完成。
* pHandler CHttpHandler HTTP 事件处理类
*/
CHttpExchange.SetHandler(CHttpHandler * pHandler);
/**
* 当收到响应完成时的事件回调函数,传入 HttpStatus 和 文本的Response,
* 目前仅支持文本 Response,即:Resonse.getContentType() 为 text 类型
*/
CHttpHandler.OnResponseComplate(HTTPResponse::HTTPStatus status, const string& responseContent);
CCheckVersionHandler : public CHttpHandler
void CCheckVersionHandler::OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent)
{
CHttpHandler::OnResponseComplete(status, responseContent);
if (status == HTTPResponse::HTTP_OK)
{
const string& version = responseContent;
m_pMainWnd->ShowVersionMessage(version);
}
} (一)CHttpClient CHttpExchange CHttpHandler 类图:
(二)代码:
HttpClient.h
//////////////////////////////////////////////////////////////////////////
// HttpClient.h
// Author: Kylin.dai @kylindai
// Date: 2011-05-21
//////////////////////////////////////////////////////////////////////////
#pragma once
#include "Poco/AutoPtr.h"
#include "Poco/Logger.h"
#include "Poco/PatternFormatter.h"
#include "Poco/FormattingChannel.h"
#include "Poco/ConsoleChannel.h"
#include "Poco/FileChannel.h"
#include "Poco/Message.h"
#include "Poco/Exception.h"
#include "Poco/StreamCopier.h"
#include "Poco/ThreadPool.h"
#include "Poco/Thread.h"
#include "Poco/Mutex.h"
#include "Poco/Runnable.h"
#include "Poco/Stopwatch.h"
#include "Poco/Net/HTTPClientSession.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
using namespace std;
using namespace Poco;
using namespace Poco::Net;
//////////////////////////////////////////////////////////////////////////
// CHttpHandler Class
//////////////////////////////////////////////////////////////////////////
class CHttpHandler
{
public:
CHttpHandler(char * pLogFileName = NULL);
~CHttpHandler();
private:
map<const string, LPVOID> m_attributeMap;
protected:
Logger * m_pLogger;
public:
void SetAttribute(const string& name, LPVOID value);
LPVOID GetAttribute(const string& name);
virtual void OnException();
virtual void OnExpire();
virtual void OnRequestComplete();
virtual void OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent);
};
//////////////////////////////////////////////////////////////////////////
// CHttpExchange Class
//////////////////////////////////////////////////////////////////////////
class CHttpExchange
{
public:
CHttpExchange();
~CHttpExchange();
private:
int m_timeout;
HTTPRequest * m_pRequest;
CHttpHandler * m_pHandler;
public:
HTTPRequest& GetHttpRequest();
void SetMethod(const string& strMethod);
const string& GetMethod() const;
void SetContentType(const string& strContentType);
const string& GetContentType() const;
void SetHost(const string& strHost);
const string& GetHost() const;
void SetUri(const string& strUri);
const string& GetUri() const;
void SetHandler(CHttpHandler * pHandler);
CHttpHandler * GetHandler();
};
//////////////////////////////////////////////////////////////////////////
// CHttpTask Class
//////////////////////////////////////////////////////////////////////////
class CHttpTask : public Runnable
{
public:
CHttpTask(CHttpExchange * pExchange, int timeout = 15000, Logger * pLogger = NULL);
~CHttpTask();
private:
CHttpExchange * m_pExchange;
int m_timeout;
protected:
Logger * m_pLogger;
public:
void run();
};
//////////////////////////////////////////////////////////////////////////
// CHttpClient Class
//////////////////////////////////////////////////////////////////////////
class CHttpClient
{
public:
CHttpClient();
~CHttpClient();
private:
int m_timeout;
int m_threads;
char * m_pLogFileName;
protected:
Logger * m_pLogger;
public:
void Create(int timeout = 15000, int threads = 20, char * pLogFileName = NULL);
void Start();
void Stop();
void Send(CHttpExchange * pExchange);
}; HttpClient.cpp
//////////////////////////////////////////////////////////////////////////
// HttpClient.cpp
// Author: Kylin.dai @kylindai
// Date: 2011-05-21
//////////////////////////////////////////////////////////////////////////
#include "HttpClient.h"
//////////////////////////////////////////////////////////////////////////
// CHttpHandler Methods
//////////////////////////////////////////////////////////////////////////
CHttpHandler::CHttpHandler(char * pLogFileName)
{
m_pLogger = NULL;
// create logger
if (pLogFileName != NULL)
{
try
{
string logFileName(pLogFileName);
FormattingChannel* pFCFile = new FormattingChannel(new PatternFormatter("%Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t"));
pFCFile->setChannel(new FileChannel(logFileName));
pFCFile->open();
m_pLogger = & Logger::create("HttpHandler", pFCFile, Message::PRIO_DEBUG); //Message::PRIO_WARNING);
}
catch (Exception& ex)
{
}
}
}
CHttpHandler::~CHttpHandler()
{
if (m_pLogger != NULL)
{
m_pLogger->getChannel()->close();
}
}
void CHttpHandler::SetAttribute(const string& name, LPVOID value)
{
m_attributeMap[name] = value;
}
LPVOID CHttpHandler::GetAttribute(const string& name)
{
return m_attributeMap[name];
}
void CHttpHandler::OnException()
{
}
void CHttpHandler::OnExpire()
{
}
void CHttpHandler::OnRequestComplete()
{
}
void CHttpHandler::OnResponseComplete(HTTPResponse::HTTPStatus status, const string& responseContent)
{
if (m_pLogger != NULL)
{
stringstream messageStream;
messageStream << "HTTP STATUS:" << status << " : " << responseContent << endl;
m_pLogger->debug(messageStream.str());
}
}
//////////////////////////////////////////////////////////////////////////
// CHttpExchange Methods
//////////////////////////////////////////////////////////////////////////
CHttpExchange::CHttpExchange()
{
m_pRequest = new HTTPRequest();
m_pRequest->setVersion(HTTPRequest::HTTP_1_1);
}
CHttpExchange::~CHttpExchange()
{
if (m_pRequest != NULL)
{
// delete m_pRequest;
}
}
HTTPRequest& CHttpExchange::GetHttpRequest()
{
return * m_pRequest;
}
void CHttpExchange::SetMethod(const string& strMethod)
{
m_pRequest->setMethod(strMethod);
}
const string& CHttpExchange::GetMethod() const
{
return m_pRequest->getMethod();
}
void CHttpExchange::SetContentType(const string& strContentType)
{
m_pRequest->setContentType(strContentType);
}
const string& CHttpExchange::GetContentType() const
{
return m_pRequest->getContentType();
}
void CHttpExchange::SetHost(const string& strHost)
{
m_pRequest->setHost(strHost);
}
const string& CHttpExchange::GetHost() const
{
return m_pRequest->getHost();
}
void CHttpExchange::SetUri(const string& strUri)
{
m_pRequest->setURI(strUri);
}
const string& CHttpExchange::GetUri() const
{
return m_pRequest->getURI();
}
void CHttpExchange::SetHandler(CHttpHandler * pHandler)
{
m_pHandler = pHandler;
}
CHttpHandler * CHttpExchange::GetHandler()
{
return m_pHandler;
}
//////////////////////////////////////////////////////////////////////////
// CHttpTask Methods
//////////////////////////////////////////////////////////////////////////
CHttpTask::CHttpTask(CHttpExchange * pExchange, int timeout, Logger * pLogger):
m_pExchange(pExchange),
m_timeout(timeout),
m_pLogger(pLogger)
{
}
CHttpTask::~CHttpTask()
{
if (m_pExchange != NULL)
{
delete m_pExchange;
}
}
void CHttpTask::run()
{
CHttpHandler * pHandler = m_pExchange->GetHandler();
try
{
HTTPClientSession httpClientSession;
httpClientSession.setHost(m_pExchange->GetHost());
httpClientSession.setTimeout(m_timeout);
httpClientSession.sendRequest(m_pExchange->GetHttpRequest());
HTTPResponse response;
istream& rs = httpClientSession.receiveResponse(response);
if (pHandler != NULL)
{
const string& contentType = response.getContentType();
if (contentType.find("text/") >= 0)
{
stringstream responseStream;
StreamCopier::copyStream(rs, responseStream);
string responseContent;
responseStream >> responseContent;
pHandler->OnResponseComplete(response.getStatus(), responseContent);
}
}
}
catch (Exception& ex)
{
if (m_pLogger != NULL)
{
m_pLogger->error(ex.displayText());
}
}
if (pHandler != NULL)
{
delete pHandler;
}
delete this;
}
//////////////////////////////////////////////////////////////////////////
// CHttpClient Methods
//////////////////////////////////////////////////////////////////////////
CHttpClient::CHttpClient()
{
m_pLogger = NULL;
}
CHttpClient::~CHttpClient()
{
if (m_pLogger != NULL)
{
m_pLogger->getChannel()->close();
}
}
void CHttpClient::Create(int timeout, int threads, char * pLogFileName)
{
m_timeout = timeout;
m_threads = threads;
m_pLogFileName = pLogFileName;
ThreadPool::defaultPool().addCapacity(threads);
// create logger
if (pLogFileName != NULL)
{
try
{
string logFileName(pLogFileName);
FormattingChannel* pFCFile = new FormattingChannel(new PatternFormatter("%Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t"));
pFCFile->setChannel(new FileChannel(logFileName));
pFCFile->open();
m_pLogger = & Logger::create("fileLogger", pFCFile, Message::PRIO_WARNING);
}
catch (Exception& ex)
{
}
}
/*
else
{
FormattingChannel* pFCConsole = new FormattingChannel(new PatternFormatter("%s: %p: %t"));
pFCConsole->setChannel(new ConsoleChannel);
pFCConsole->open();
m_pLogger = & Logger::create("ConsoleLogger", pFCConsole, Message::PRIO_DEBUG);
}
*/
}
void CHttpClient::Start()
{
}
void CHttpClient::Stop()
{
// ThreadPool::defaultPool().joinAll();
}
void CHttpClient::Send(CHttpExchange * pExchange)
{
try
{
CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
ThreadPool::defaultPool().start(* pHttpTask);
}
catch (Exception& ex)
{
if (m_pLogger != NULL)
{
m_pLogger->error(ex.displayText());
}
}
} |
|
|