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
37using Poco::Net::HTTPClientSession;
38using Poco::Net::HTTPRequest;
39using Poco::Net::HTTPResponse;
40using Poco::Net::HTTPMessage;
41using Poco::Net::HTTPCookie;
42using Poco::Net::NameValueCollection;
43using Poco::Util::Application;
44using Poco::Util::Option;
45using Poco::Util::OptionSet;
46using Poco::Util::HelpFormatter;
47using Poco::Util::AbstractConfiguration;
48using Poco::AutoPtr;
49using Poco::Thread;
50using Poco::FastMutex;
51using Poco::Runnable;
52using Poco::Stopwatch;
53using Poco::NumberParser;
54using Poco::Path;
55using Poco::URI;
56using Poco::Exception;
57using Poco::StreamCopier;
58using Poco::NullOutputStream;
59
60
61class HTTPClient : public Runnable
62{
63public:
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
147private:
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
162FastMutex HTTPClient::_mutex;
163int HTTPClient::_gRepetitions;
164Poco::UInt64 HTTPClient::_gUsec;
165int HTTPClient::_gSuccess;
166
167int HTTPClient::totalAttempts()
168{
169 return _gRepetitions;
170}
171
172Poco::UInt64 HTTPClient::totalMicroseconds()
173{
174 return _gUsec;
175}
176
177int HTTPClient::totalSuccessCount()
178{
179 return _gSuccess;
180}
181
182void 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
196class 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{
203public:
204 HTTPLoadTest():
205 _helpRequested(false),
206 _verbose(false),
207 _cookies(false),
208 _repetitions(1),
209 _threads(1)
210 {
211 }
212
213protected:
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
349private:
350 bool _helpRequested;
351 bool _verbose;
352 bool _cookies;
353 std::string _uri;
354 int _repetitions;
355 int _threads;
356};
357
358
359int 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