1 | // |
2 | // HTTPLoadTest.cpp |
3 | // |
4 | // This sample demonstrates the HTTPClientSession class. |
5 | // |
6 | // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. |
7 | // and Contributors. |
8 | // |
9 | // SPDX-License-Identifier: BSL-1.0 |
10 | // |
11 | |
12 | |
13 | #include "Poco/Net/HTTPClientSession.h" |
14 | #include "Poco/Net/HTTPRequest.h" |
15 | #include "Poco/Net/HTTPResponse.h" |
16 | #include "Poco/Net/HTTPCookie.h" |
17 | #include "Poco/Net/NameValueCollection.h" |
18 | #include "Poco/Path.h" |
19 | #include "Poco/URI.h" |
20 | #include "Poco/AutoPtr.h" |
21 | #include "Poco/Thread.h" |
22 | #include "Poco/Mutex.h" |
23 | #include "Poco/Runnable.h" |
24 | #include "Poco/Stopwatch.h" |
25 | #include "Poco/NumberParser.h" |
26 | #include "Poco/StreamCopier.h" |
27 | #include "Poco/NullStream.h" |
28 | #include "Poco/Exception.h" |
29 | #include "Poco/Util/Application.h" |
30 | #include "Poco/Util/Option.h" |
31 | #include "Poco/Util/OptionSet.h" |
32 | #include "Poco/Util/HelpFormatter.h" |
33 | #include "Poco/Util/AbstractConfiguration.h" |
34 | #include <iostream> |
35 | |
36 | |
37 | using Poco::Net::HTTPClientSession; |
38 | using Poco::Net::HTTPRequest; |
39 | using Poco::Net::HTTPResponse; |
40 | using Poco::Net::HTTPMessage; |
41 | using Poco::Net::HTTPCookie; |
42 | using Poco::Net::NameValueCollection; |
43 | using Poco::Util::Application; |
44 | using Poco::Util::Option; |
45 | using Poco::Util::OptionSet; |
46 | using Poco::Util::HelpFormatter; |
47 | using Poco::Util::AbstractConfiguration; |
48 | using Poco::AutoPtr; |
49 | using Poco::Thread; |
50 | using Poco::FastMutex; |
51 | using Poco::Runnable; |
52 | using Poco::Stopwatch; |
53 | using Poco::NumberParser; |
54 | using Poco::Path; |
55 | using Poco::URI; |
56 | using Poco::Exception; |
57 | using Poco::StreamCopier; |
58 | using Poco::NullOutputStream; |
59 | |
60 | |
61 | class HTTPClient : public Runnable |
62 | { |
63 | public: |
64 | HTTPClient(const URI& uri, int repetitions, bool cookies=false, bool verbose=false): |
65 | _uri(uri), |
66 | _verbose(verbose), |
67 | _cookies(cookies), |
68 | _repetitions(repetitions), |
69 | _usec(0), |
70 | _success(0) |
71 | { |
72 | _gRepetitions += _repetitions; |
73 | } |
74 | |
75 | ~HTTPClient() |
76 | { |
77 | } |
78 | |
79 | void run() |
80 | { |
81 | Stopwatch sw; |
82 | std::vector<HTTPCookie> cookies; |
83 | |
84 | for (int i = 0; i < _repetitions; ++i) |
85 | { |
86 | try |
87 | { |
88 | int usec = 0; |
89 | std::string path(_uri.getPathAndQuery()); |
90 | if (path.empty()) path = "/" ; |
91 | |
92 | HTTPClientSession session(_uri.getHost(), _uri.getPort()); |
93 | HTTPRequest req(HTTPRequest::HTTP_GET, path, HTTPMessage::HTTP_1_1); |
94 | |
95 | if (_cookies) |
96 | { |
97 | NameValueCollection nvc; |
98 | std::vector<HTTPCookie>::iterator it = cookies.begin(); |
99 | for(; it != cookies.end(); ++it) |
100 | nvc.add((*it).getName(), (*it).getValue()); |
101 | |
102 | req.setCookies(nvc); |
103 | } |
104 | |
105 | HTTPResponse res; |
106 | sw.restart(); |
107 | session.sendRequest(req); |
108 | std::istream& rs = session.receiveResponse(res); |
109 | NullOutputStream nos; |
110 | StreamCopier::copyStream(rs, nos); |
111 | sw.stop(); |
112 | _success += HTTPResponse::HTTP_OK == res.getStatus() ? 1 : 0; |
113 | if (_cookies) res.getCookies(cookies); |
114 | usec = int(sw.elapsed()); |
115 | |
116 | if (_verbose) |
117 | { |
118 | FastMutex::ScopedLock lock(_mutex); |
119 | std::cout |
120 | << _uri.toString() << ' ' << res.getStatus() << ' ' << res.getReason() |
121 | << ' ' << usec/1000.0 << "ms" << std::endl; |
122 | } |
123 | |
124 | _usec += usec; |
125 | } |
126 | catch (Exception& exc) |
127 | { |
128 | FastMutex::ScopedLock lock(_mutex); |
129 | std::cerr << exc.displayText() << std::endl; |
130 | } |
131 | } |
132 | |
133 | { |
134 | FastMutex::ScopedLock lock(_mutex); |
135 | _gSuccess += _success; |
136 | _gUsec += _usec; |
137 | } |
138 | if (_verbose) |
139 | printStats(_uri.toString(), _repetitions, _success, _usec); |
140 | } |
141 | |
142 | static void printStats(std::string uri, int repetitions, int success, Poco::UInt64 usec); |
143 | static int totalAttempts(); |
144 | static Poco::UInt64 totalMicroseconds(); |
145 | static int totalSuccessCount(); |
146 | |
147 | private: |
148 | HTTPClient(); |
149 | |
150 | URI _uri; |
151 | bool _verbose; |
152 | bool _cookies; |
153 | int _repetitions; |
154 | Poco::UInt64 _usec; |
155 | int _success; |
156 | static int _gRepetitions; |
157 | static Poco::UInt64 _gUsec; |
158 | static int _gSuccess; |
159 | static FastMutex _mutex; |
160 | }; |
161 | |
162 | FastMutex HTTPClient::_mutex; |
163 | int HTTPClient::_gRepetitions; |
164 | Poco::UInt64 HTTPClient::_gUsec; |
165 | int HTTPClient::_gSuccess; |
166 | |
167 | int HTTPClient::totalAttempts() |
168 | { |
169 | return _gRepetitions; |
170 | } |
171 | |
172 | Poco::UInt64 HTTPClient::totalMicroseconds() |
173 | { |
174 | return _gUsec; |
175 | } |
176 | |
177 | int HTTPClient::totalSuccessCount() |
178 | { |
179 | return _gSuccess; |
180 | } |
181 | |
182 | void HTTPClient::printStats(std::string uri, int repetitions, int success, Poco::UInt64 usec) |
183 | { |
184 | FastMutex::ScopedLock lock(_mutex); |
185 | |
186 | std::cout << std::endl << "--------------" << std::endl |
187 | << "Statistics for " << uri << std::endl << "--------------" |
188 | << std::endl |
189 | << repetitions << " attempts, " << success << " successful (" |
190 | << ((float) success / (float) repetitions) * 100.0 << "%)" << std::endl |
191 | << "Avg response time: " << ((float) usec / (float) repetitions) / 1000.0 << "ms, " << std::endl |
192 | << "Avg requests/second handled: " << ((float) success /((float) usec / 1000000.0)) << std::endl |
193 | << "Total time: " << (float) usec / 1000000.0 << std::endl; |
194 | } |
195 | |
196 | class HTTPLoadTest: public Application |
197 | /// This sample demonstrates some of the features of the Poco::Util::Application class, |
198 | /// such as configuration file handling and command line arguments processing. |
199 | /// |
200 | /// Try HTTPLoadTest --help (on Unix platforms) or HTTPLoadTest /help (elsewhere) for |
201 | /// more information. |
202 | { |
203 | public: |
204 | HTTPLoadTest(): |
205 | _helpRequested(false), |
206 | _verbose(false), |
207 | _cookies(false), |
208 | _repetitions(1), |
209 | _threads(1) |
210 | { |
211 | } |
212 | |
213 | protected: |
214 | void initialize(Application& self) |
215 | { |
216 | loadConfiguration(); // load default configuration files, if present |
217 | Application::initialize(self); |
218 | // add your own initialization code here |
219 | } |
220 | |
221 | void uninitialize() |
222 | { |
223 | // add your own uninitialization code here |
224 | Application::uninitialize(); |
225 | } |
226 | |
227 | void reinitialize(Application& self) |
228 | { |
229 | Application::reinitialize(self); |
230 | // add your own reinitialization code here |
231 | } |
232 | |
233 | void defineOptions(OptionSet& options) |
234 | { |
235 | Application::defineOptions(options); |
236 | |
237 | options.addOption( |
238 | Option("help" , "h" , "display help information on command line arguments" ) |
239 | .required(false) |
240 | .repeatable(false)); |
241 | |
242 | options.addOption( |
243 | Option("verbose" , "v" , "display messages on stdout" ) |
244 | .required(false) |
245 | .repeatable(false)); |
246 | |
247 | options.addOption( |
248 | Option("cookies" , "c" , "resend cookies" ) |
249 | .required(false) |
250 | .repeatable(false)); |
251 | |
252 | options.addOption( |
253 | Option("uri" , "u" , "HTTP URI" ) |
254 | .required(true) |
255 | .repeatable(false) |
256 | .argument("uri" )); |
257 | |
258 | options.addOption( |
259 | Option("repetitions" , "r" , "fetch repetitions" ) |
260 | .required(false) |
261 | .repeatable(false) |
262 | .argument("repetitions" )); |
263 | |
264 | options.addOption( |
265 | Option("threads" , "t" , "thread count" ) |
266 | .required(false) |
267 | .repeatable(false) |
268 | .argument("threads" )); |
269 | } |
270 | |
271 | void handleOption(const std::string& name, const std::string& value) |
272 | { |
273 | Application::handleOption(name, value); |
274 | |
275 | if (name == "help" ) |
276 | _helpRequested = true; |
277 | else if (name == "verbose" ) |
278 | _verbose = true; |
279 | else if (name == "cookies" ) |
280 | _cookies = true; |
281 | else if (name == "uri" ) |
282 | _uri = value; |
283 | else if (name == "repetitions" ) |
284 | _repetitions = NumberParser::parse(value); |
285 | else if (name == "threads" ) |
286 | _threads = NumberParser::parse(value); |
287 | } |
288 | |
289 | void displayHelp() |
290 | { |
291 | HelpFormatter helpFormatter(options()); |
292 | helpFormatter.setCommand(commandName()); |
293 | helpFormatter.setUsage("OPTIONS" ); |
294 | helpFormatter.setHeader("A sample application that demonstrates some of the features of the Poco::Util::Application class." ); |
295 | helpFormatter.format(std::cout); |
296 | } |
297 | |
298 | void defineProperty(const std::string& def) |
299 | { |
300 | std::string name; |
301 | std::string value; |
302 | std::string::size_type pos = def.find('='); |
303 | if (pos != std::string::npos) |
304 | { |
305 | name.assign(def, 0, pos); |
306 | value.assign(def, pos + 1, def.length() - pos); |
307 | } |
308 | else name = def; |
309 | config().setString(name, value); |
310 | } |
311 | |
312 | int main(const std::vector<std::string>& args) |
313 | { |
314 | if (_helpRequested) |
315 | { |
316 | displayHelp(); |
317 | } |
318 | else |
319 | { |
320 | URI uri(_uri); |
321 | std::vector<Thread*> threads; |
322 | |
323 | Stopwatch sw; |
324 | sw.start(); |
325 | for (int i = 0; i < _threads; ++i) |
326 | { |
327 | Thread* pt = new Thread(_uri); |
328 | poco_check_ptr(pt); |
329 | threads.push_back(pt); |
330 | HTTPClient* pHTTPClient = new HTTPClient(uri, _repetitions, _cookies, _verbose); |
331 | poco_check_ptr(pHTTPClient); |
332 | threads.back()->start(*pHTTPClient); |
333 | } |
334 | |
335 | std::vector<Thread*>::iterator it = threads.begin(); |
336 | for(; it != threads.end(); ++it) |
337 | { |
338 | (*it)->join(); |
339 | delete *it; |
340 | } |
341 | sw.stop(); |
342 | |
343 | HTTPClient::printStats(_uri, HTTPClient::totalAttempts(), HTTPClient::totalSuccessCount(), sw.elapsed()); |
344 | } |
345 | |
346 | return Application::EXIT_OK; |
347 | } |
348 | |
349 | private: |
350 | bool _helpRequested; |
351 | bool _verbose; |
352 | bool _cookies; |
353 | std::string _uri; |
354 | int _repetitions; |
355 | int _threads; |
356 | }; |
357 | |
358 | |
359 | int main(int argc, char** argv) |
360 | { |
361 | AutoPtr<HTTPLoadTest> pApp = new HTTPLoadTest; |
362 | try |
363 | { |
364 | pApp->init(argc, argv); |
365 | } |
366 | catch (Poco::Exception& exc) |
367 | { |
368 | pApp->logger().log(exc); |
369 | return Application::EXIT_CONFIG; |
370 | } |
371 | return pApp->run(); |
372 | } |
373 | |
374 | |