用惯了 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);
2 {
3 try
4 {
5 CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
6 ThreadPool::defaultPool().start( * pHttpTask);
7 }
8 catch (Exception & ex)
9 {
10 if (m_pLogger != NULL)
11 {
12 m_pLogger -> error(ex.displayText());
13 }
14 }
15 }
/**
* 线程池中任务 CHttpTask.run 处理Http请求及响应
*/
CHttpTask : public Runnable
2 {
3 CHttpHandler * pHandler = m_pExchange -> GetHandler();
4
5 try
6 {
7 HTTPClientSession httpClientSession;
8 httpClientSession.setHost(m_pExchange -> GetHost());
9 httpClientSession.setTimeout(m_timeout);
10 httpClientSession.sendRequest(m_pExchange -> GetHttpRequest());
11
12 HTTPResponse response;
13 istream & rs = httpClientSession.receiveResponse(response);
14
15 if (pHandler != NULL)
16 {
17 const string & contentType = response.getContentType();
18 if (contentType.find( " text/ " ) >= 0 )
19 {
20 stringstream responseStream;
21 StreamCopier::copyStream(rs, responseStream);
22
23 string responseContent;
24 responseStream >> responseContent;
25
26 pHandler -> OnResponseComplete(response.getStatus(), responseContent);
27 }
28 }
29 }
30 catch (Exception & ex)
31 {
32 if (m_pLogger != NULL)
33 {
34 m_pLogger -> error(ex.displayText());
35 }
36 }
37
38 if (pHandler != NULL)
39 {
40 delete pHandler;
41 }
42
43 delete this ;
44 }
/**
* 为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
2 {
3 CHttpHandler::OnResponseComplete(status, responseContent);
4
5 if (status == HTTPResponse::HTTP_OK)
6 {
7 const string & version = responseContent;
8
9 m_pMainWnd -> ShowVersionMessage(version);
10 }
11 }
(一)CHttpClient CHttpExchange CHttpHandler 类图:
(二)代码:
HttpClient.h
2 // HttpClient.h
3 // Author: Kylin.dai @kylindai
4 // Date: 2011-05-21
5 //////////////////////////////////////////////////////////////////////// //
6
7 #pragma once
8
9 #include " Poco/AutoPtr.h "
10 #include " Poco/Logger.h "
11 #include " Poco/PatternFormatter.h "
12 #include " Poco/FormattingChannel.h "
13 #include " Poco/ConsoleChannel.h "
14 #include " Poco/FileChannel.h "
15 #include " Poco/Message.h "
16 #include " Poco/Exception.h "
17 #include " Poco/StreamCopier.h "
18 #include " Poco/ThreadPool.h "
19 #include " Poco/Thread.h "
20 #include " Poco/Mutex.h "
21 #include " Poco/Runnable.h "
22 #include " Poco/Stopwatch.h "
23 #include " Poco/Net/HTTPClientSession.h "
24 #include " Poco/Net/HTTPRequest.h "
25 #include " Poco/Net/HTTPResponse.h "
26
27 #include < string >
28 #include < iostream >
29 #include < sstream >
30 #include < fstream >
31 #include < map >
32
33 using namespace std;
34 using namespace Poco;
35 using namespace Poco::Net;
36
37 //////////////////////////////////////////////////////////////////////// //
38 // CHttpHandler Class
39 //////////////////////////////////////////////////////////////////////// //
40 class CHttpHandler
41 {
42 public :
43 CHttpHandler( char * pLogFileName = NULL);
44 ~ CHttpHandler();
45
46 private :
47 map < const string , LPVOID > m_attributeMap;
48
49 protected :
50 Logger * m_pLogger;
51
52 public :
53 void SetAttribute( const string & name, LPVOID value);
54 LPVOID GetAttribute( const string & name);
55
56 virtual void OnException();
57 virtual void OnExpire();
58 virtual void OnRequestComplete();
59 virtual void OnResponseComplete(HTTPResponse::HTTPStatus status, const string & responseContent);
60 };
61
62 //////////////////////////////////////////////////////////////////////// //
63 // CHttpExchange Class
64 //////////////////////////////////////////////////////////////////////// //
65 class CHttpExchange
66 {
67 public :
68 CHttpExchange();
69 ~ CHttpExchange();
70
71 private :
72 int m_timeout;
73 HTTPRequest * m_pRequest;
74 CHttpHandler * m_pHandler;
75
76 public :
77 HTTPRequest & GetHttpRequest();
78
79 void SetMethod( const string & strMethod);
80 const string & GetMethod() const ;
81
82 void SetContentType( const string & strContentType);
83 const string & GetContentType() const ;
84
85 void SetHost( const string & strHost);
86 const string & GetHost() const ;
87
88 void SetUri( const string & strUri);
89 const string & GetUri() const ;
90
91 void SetHandler(CHttpHandler * pHandler);
92 CHttpHandler * GetHandler();
93 };
94
95 //////////////////////////////////////////////////////////////////////// //
96 // CHttpTask Class
97 //////////////////////////////////////////////////////////////////////// //
98 class CHttpTask : public Runnable
99 {
100 public :
101 CHttpTask(CHttpExchange * pExchange, int timeout = 15000 , Logger * pLogger = NULL);
102 ~ CHttpTask();
103
104 private :
105 CHttpExchange * m_pExchange;
106 int m_timeout;
107
108 protected :
109 Logger * m_pLogger;
110
111 public :
112 void run();
113 };
114
115 //////////////////////////////////////////////////////////////////////// //
116 // CHttpClient Class
117 //////////////////////////////////////////////////////////////////////// //
118 class CHttpClient
119 {
120 public :
121 CHttpClient();
122 ~ CHttpClient();
123
124 private :
125 int m_timeout;
126 int m_threads;
127 char * m_pLogFileName;
128
129 protected :
130 Logger * m_pLogger;
131
132 public :
133 void Create( int timeout = 15000 , int threads = 20 , char * pLogFileName = NULL);
134 void Start();
135 void Stop();
136 void Send(CHttpExchange * pExchange);
137 };
HttpClient.cpp
2 // HttpClient.cpp
3 // Author: Kylin.dai @kylindai
4 // Date: 2011-05-21
5 //////////////////////////////////////////////////////////////////////// //
6
7 #include " HttpClient.h "
8
9 //////////////////////////////////////////////////////////////////////// //
10 // CHttpHandler Methods
11 //////////////////////////////////////////////////////////////////////// //
12 CHttpHandler::CHttpHandler( char * pLogFileName)
13 {
14 m_pLogger = NULL;
15
16 // create logger
17 if (pLogFileName != NULL)
18 {
19 try
20 {
21 string logFileName(pLogFileName);
22
23 FormattingChannel * pFCFile = new FormattingChannel( new PatternFormatter( " %Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t " ));
24 pFCFile -> setChannel( new FileChannel(logFileName));
25 pFCFile -> open();
26
27 m_pLogger = & Logger::create( " HttpHandler " , pFCFile, Message::PRIO_DEBUG); // Message::PRIO_WARNING);
28 }
29 catch (Exception & ex)
30 {
31 }
32 }
33 }
34
35 CHttpHandler:: ~ CHttpHandler()
36 {
37 if (m_pLogger != NULL)
38 {
39 m_pLogger -> getChannel() -> close();
40 }
41 }
42
43 void CHttpHandler::SetAttribute( const string & name, LPVOID value)
44 {
45 m_attributeMap[name] = value;
46 }
47
48 LPVOID CHttpHandler::GetAttribute( const string & name)
49 {
50 return m_attributeMap[name];
51 }
52
53 void CHttpHandler::OnException()
54 {
55
56 }
57
58 void CHttpHandler::OnExpire()
59 {
60
61 }
62
63 void CHttpHandler::OnRequestComplete()
64 {
65
66 }
67
68 void CHttpHandler::OnResponseComplete(HTTPResponse::HTTPStatus status, const string & responseContent)
69 {
70 if (m_pLogger != NULL)
71 {
72 stringstream messageStream;
73 messageStream << " HTTP STATUS: " << status << " : " << responseContent << endl;
74
75 m_pLogger -> debug(messageStream.str());
76 }
77 }
78
79 //////////////////////////////////////////////////////////////////////// //
80 // CHttpExchange Methods
81 //////////////////////////////////////////////////////////////////////// //
82 CHttpExchange::CHttpExchange()
83 {
84 m_pRequest = new HTTPRequest();
85 m_pRequest -> setVersion(HTTPRequest::HTTP_1_1);
86 }
87
88 CHttpExchange:: ~ CHttpExchange()
89 {
90 if (m_pRequest != NULL)
91 {
92 // delete m_pRequest;
93 }
94 }
95
96 HTTPRequest & CHttpExchange::GetHttpRequest()
97 {
98 return * m_pRequest;
99 }
100
101 void CHttpExchange::SetMethod( const string & strMethod)
102 {
103 m_pRequest -> setMethod(strMethod);
104 }
105
106 const string & CHttpExchange::GetMethod() const
107 {
108 return m_pRequest -> getMethod();
109 }
110
111 void CHttpExchange::SetContentType( const string & strContentType)
112 {
113 m_pRequest -> setContentType(strContentType);
114 }
115
116 const string & CHttpExchange::GetContentType() const
117 {
118 return m_pRequest -> getContentType();
119 }
120
121 void CHttpExchange::SetHost( const string & strHost)
122 {
123 m_pRequest -> setHost(strHost);
124 }
125
126 const string & CHttpExchange::GetHost() const
127 {
128 return m_pRequest -> getHost();
129 }
130
131 void CHttpExchange::SetUri( const string & strUri)
132 {
133 m_pRequest -> setURI(strUri);
134 }
135
136 const string & CHttpExchange::GetUri() const
137 {
138 return m_pRequest -> getURI();
139 }
140
141 void CHttpExchange::SetHandler(CHttpHandler * pHandler)
142 {
143 m_pHandler = pHandler;
144 }
145
146 CHttpHandler * CHttpExchange::GetHandler()
147 {
148 return m_pHandler;
149 }
150
151 //////////////////////////////////////////////////////////////////////// //
152 // CHttpTask Methods
153 //////////////////////////////////////////////////////////////////////// //
154 CHttpTask::CHttpTask(CHttpExchange * pExchange, int timeout, Logger * pLogger):
155 m_pExchange(pExchange),
156 m_timeout(timeout),
157 m_pLogger(pLogger)
158 {
159
160 }
161
162 CHttpTask:: ~ CHttpTask()
163 {
164 if (m_pExchange != NULL)
165 {
166 delete m_pExchange;
167 }
168 }
169
170 void CHttpTask::run()
171 {
172 CHttpHandler * pHandler = m_pExchange -> GetHandler();
173
174 try
175 {
176 HTTPClientSession httpClientSession;
177 httpClientSession.setHost(m_pExchange -> GetHost());
178 httpClientSession.setTimeout(m_timeout);
179 httpClientSession.sendRequest(m_pExchange -> GetHttpRequest());
180
181 HTTPResponse response;
182 istream & rs = httpClientSession.receiveResponse(response);
183
184 if (pHandler != NULL)
185 {
186 const string & contentType = response.getContentType();
187 if (contentType.find( " text/ " ) >= 0 )
188 {
189 stringstream responseStream;
190 StreamCopier::copyStream(rs, responseStream);
191
192 string responseContent;
193 responseStream >> responseContent;
194
195 pHandler -> OnResponseComplete(response.getStatus(), responseContent);
196 }
197 }
198 }
199 catch (Exception & ex)
200 {
201 if (m_pLogger != NULL)
202 {
203 m_pLogger -> error(ex.displayText());
204 }
205 }
206
207 if (pHandler != NULL)
208 {
209 delete pHandler;
210 }
211
212 delete this ;
213 }
214
215 //////////////////////////////////////////////////////////////////////// //
216 // CHttpClient Methods
217 //////////////////////////////////////////////////////////////////////// //
218 CHttpClient::CHttpClient()
219 {
220 m_pLogger = NULL;
221 }
222
223 CHttpClient:: ~ CHttpClient()
224 {
225 if (m_pLogger != NULL)
226 {
227 m_pLogger -> getChannel() -> close();
228 }
229 }
230
231 void CHttpClient::Create( int timeout, int threads, char * pLogFileName)
232 {
233 m_timeout = timeout;
234 m_threads = threads;
235 m_pLogFileName = pLogFileName;
236
237 ThreadPool::defaultPool().addCapacity(threads);
238
239 // create logger
240 if (pLogFileName != NULL)
241 {
242 try
243 {
244 string logFileName(pLogFileName);
245
246 FormattingChannel * pFCFile = new FormattingChannel( new PatternFormatter( " %Y-%m-%d %H:%M:%S.%c %N[%P]:%s:%q:%t " ));
247 pFCFile -> setChannel( new FileChannel(logFileName));
248 pFCFile -> open();
249
250 m_pLogger = & Logger::create( " fileLogger " , pFCFile, Message::PRIO_WARNING);
251 }
252 catch (Exception & ex)
253 {
254 }
255 }
256 /*
257 else
258 {
259 FormattingChannel* pFCConsole = new FormattingChannel(new PatternFormatter("%s: %p: %t"));
260 pFCConsole->setChannel(new ConsoleChannel);
261 pFCConsole->open();
262
263 m_pLogger = & Logger::create("ConsoleLogger", pFCConsole, Message::PRIO_DEBUG);
264 }
265 */
266 }
267
268 void CHttpClient::Start()
269 {
270
271 }
272
273 void CHttpClient::Stop()
274 {
275 // ThreadPool::defaultPool().joinAll();
276 }
277
278 void CHttpClient::Send(CHttpExchange * pExchange)
279 {
280 try
281 {
282 CHttpTask * pHttpTask = new CHttpTask(pExchange, m_timeout, m_pLogger);
283 ThreadPool::defaultPool().start( * pHttpTask);
284 }
285 catch (Exception & ex)
286 {
287 if (m_pLogger != NULL)
288 {
289 m_pLogger -> error(ex.displayText());
290 }
291 }
292 }