First commit
[anna.git] / example / http / client / main.cpp
1 // ANNA - Anna is Not 'N' Anymore
2 //
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
4 //
5 // https://bitbucket.org/testillano/anna
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 //     * Redistributions of source code must retain the above copyright
12 // notice, this list of conditions and the following disclaimer.
13 //     * Redistributions in binary form must reproduce the above
14 // copyright notice, this list of conditions and the following disclaimer
15 // in the documentation and/or other materials provided with the
16 // distribution.
17 //     * Neither the name of Google Inc. nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 //
33 // Authors: eduardo.ramos.testillano@gmail.com
34 //          cisco.tierra@gmail.com
35
36
37 /**
38    Realiza peticiones automaticas sobre el servidor HTTP de operaciones aritmeticas.
39
40    La cadencia de envio de mensajes se establece mediante un temporizador.
41    Los operadores se calculan de forma aleatoria.
42
43    El servidor de este cliente: http_server.p o http_rserver.p
44 */
45
46 #include <iostream>
47
48 #include <string.h>
49 #include <unistd.h>
50
51 #include <anna/core/core.hpp>
52 #include <anna/app/functions.hpp>
53 #include <anna/comm/comm.hpp>
54
55 #include <anna/timex/Engine.hpp>
56 #include <anna/timex/Clock.hpp>
57
58 #include <anna/http/Request.hpp>
59 #include <anna/http/Response.hpp>
60 #include <anna/http/Transport.hpp>
61 #include <anna/http/Handler.hpp>
62
63 #include <anna/test/Response.hpp>
64 #include <anna/test/Request.hpp>
65
66 class Sender : public timex::Clock {
67 public:
68    Sender () : Clock ("Sender", (Millisecond)1000), 
69       a_messageBySecond (0), 
70       a_nquarter (0), 
71       a_errorCounter (0),
72       a_txMessageCounter (0)
73    {
74       a_httpRequest.setMethod (http::Method::Type::Post);
75       a_httpRequest.setURI ("HTTPKClient");
76    }
77
78    void setMessageBySecond (const int messageBySecond) throw () { a_messageBySecond = messageBySecond; }
79
80    int getTxMessageCounter () const throw () { return a_txMessageCounter; }
81
82 private:
83    int a_messageBySecond;
84    int a_nquarter;
85    int a_errorCounter;
86    int a_txMessageCounter;
87    http::Request a_httpRequest;
88    test::Request a_testRequest;
89
90    bool tick () throw (RuntimeException);
91 };
92
93 class MyHandler : public http::Handler {
94 public:
95    MyHandler () : http::Handler ("http_client::MyHandler") {;}
96
97 private:
98    http::Response a_httpResponse;
99    test::Response a_testResponse;
100
101    void evRequest (ClientSocket&, const http::Request&) throw (RuntimeException) {;}
102    void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException);
103    
104    static bool isOk (const test::Response& response) throw ();
105 };
106
107 class MyCommunicator : public Communicator {
108 public:
109    MyCommunicator () : Communicator (), a_avgResponseTime (0), a_rxMessageCounter (0) {;}
110
111    void count (const int delay) throw (RuntimeException);
112
113 private:
114    int a_avgResponseTime;
115    int a_rxMessageCounter;
116    MyHandler a_httpHandler;
117
118    void eventReceiveMessage (ClientSocket&, const Message&) throw (RuntimeException);
119
120    void eventBreakConnection (const ClientSocket&) throw ();
121
122    void eventBreakConnection (Server* server) throw () {
123       comm::Communicator::eventBreakConnection (server);
124    }
125    void eventBreakConnection (const Service* service) throw () { 
126       comm::Communicator::eventBreakConnection (service);
127    }   
128 };
129
130 class HeavyClient : public anna::comm::Application {
131 public:
132    HeavyClient ();
133
134    Server* getServer () const throw () { return a_server; }
135    const Sender* getSender () const throw () { return &a_sender; }
136
137 private:
138    MyCommunicator a_communicator;
139    timex::Engine a_timeController;
140    Sender a_sender;
141    Server* a_server;
142
143    void initialize () throw (RuntimeException);
144    void run () throw (RuntimeException);
145 };
146
147 using namespace std;
148
149 int main (int argc, const char** argv)
150 {
151    CommandLine& commandLine (CommandLine::instantiate ());
152    HeavyClient app;
153
154    srand (time (NULL));
155
156    try {
157       commandLine.initialize (argv, argc);
158       commandLine.verify ();
159
160       Logger::setLevel (Logger::Information);
161       string traceFile ("client.");
162       traceFile += anna::functions::asString ((int) getpid ());
163       traceFile += ".trace";
164       Logger::initialize ("http_client", new TraceWriter (traceFile.c_str (),4096000));
165
166       app.start ();
167    }
168    catch (Exception& ex) {
169       cout << ex.asString () << endl;
170    }
171
172    return 0;
173 }
174
175 HeavyClient::HeavyClient () :
176    Application ("http_client", "Cliente HTTP", "1.0"),
177    a_communicator (),
178    a_timeController ((Millisecond)1000, (Millisecond)250)
179 {
180    CommandLine& commandLine (CommandLine::instantiate ());
181
182    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que el servidor atiende respuestas.");
183    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP Puerto en el que el servidor atiende respuestas.");
184    commandLine.add ("n", CommandLine::Argument::Mandatory, "Numero de mensajes por segundo");
185    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
186 }
187
188 void HeavyClient::initialize ()
189    throw (RuntimeException)
190 {
191    CommandLine& cl (CommandLine::instantiate ());
192
193    Network& network = Network::instantiate ();
194
195    a_server = network.createServer (cl.getValue ("a"), cl.getIntegerValue ("p"), true, &http::Transport::getFactory ());
196    a_sender.setMessageBySecond (cl.getIntegerValue ("n"));
197
198    if (cl.exists ("trace"))
199       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
200 }
201
202 void HeavyClient::run ()
203    throw (RuntimeException)
204 {
205    a_timeController.activate (a_sender);
206
207    a_communicator.accept ();
208 }
209
210 void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
211    throw (RuntimeException)
212 {
213    LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", ANNA_FILE_LOCATION));
214
215    if (clientSocket.support (http::Transport::className ()) == false)
216       return;
217
218    a_httpHandler.apply (clientSocket, message);
219 }
220
221 void MyCommunicator::count (const int delay)
222    throw (RuntimeException)
223 {
224    Guard guard (this, "MyCommunicator::eventReceiveMessage");
225    
226    a_rxMessageCounter ++;
227    a_avgResponseTime += delay;
228 }
229
230 void MyCommunicator::eventBreakConnection (const ClientSocket& clientSocket)
231    throw ()
232 {
233    if (a_rxMessageCounter == 0)
234       return;
235
236    LOGNOTICE (
237       HeavyClient& app = static_cast <HeavyClient&> (anna::app::functions::getApp ());   
238       string msg ("Tiempo medio respuesta: ");
239       msg += anna::functions::asString (a_avgResponseTime / a_rxMessageCounter);
240       msg += " ms";
241       msg += anna::functions::asText (" | Rx: ", a_rxMessageCounter);
242       msg += anna::functions::asText (" | Tx: ", app.getSender ()->getTxMessageCounter ());
243       Logger::notice (msg, ANNA_FILE_LOCATION);
244
245       cout << msg << endl << endl;
246    );
247    requestStop ();
248    comm::Communicator::eventBreakConnection (clientSocket);
249 }
250
251 bool Sender::tick ()
252    throw (RuntimeException)
253 {
254    Server* server = static_cast <HeavyClient&> (anna::app::functions::getApp ()).getServer ();
255    Communicator* communicator = anna::app::functions::component <Communicator> (ANNA_FILE_LOCATION);
256
257    if (a_errorCounter > 100) {
258       communicator->requestStop ();
259       Logger::warning ("Terminado por errores continuos en la conexion", ANNA_FILE_LOCATION);
260       return false;
261    }
262
263    for (int n = 0; n < a_messageBySecond && communicator->hasRequestedStop () == false; n ++) {
264       a_testRequest.op = '+';
265       a_testRequest.x = rand () % 1000;
266       a_testRequest.y = rand () % 1000;
267       a_testRequest.initTime = anna::functions::millisecond ();
268
269       try {
270          a_httpRequest.setBody (a_testRequest.code ());
271          server->send (a_httpRequest);
272          a_txMessageCounter ++;
273       }
274       catch (RuntimeException& ex) {
275          a_errorCounter ++;
276          ex.trace ();
277          break;
278       }
279    }
280
281    return true;
282 }
283
284 void MyHandler::evResponse (ClientSocket& clientSocket, const http::Response& response)
285    throw (RuntimeException)
286 {
287    if (response.getStatusCode () == 200) {
288       a_testResponse.decode (response.getBody ());
289       
290       test::Response& response (a_testResponse);
291       
292       const anna::Millisecond now = anna::functions::millisecond ();
293       const int delay =  now - (Millisecond) response.initTime;
294
295       if (delay > 0 && isOk (response) == true) {
296          app::functions::component <MyCommunicator> (ANNA_FILE_LOCATION)->count (delay);
297
298          LOGINFORMATION (
299             string msg = anna::functions::asString (
300                "%d %c %d = %d", response.x, response.op, response.y, response.result
301             );
302             msg += anna::functions::asText (" | Delay: ", delay);
303             Logger::information (msg, ANNA_FILE_LOCATION);
304          );
305       }
306       else {
307          LOGWARNING (
308             string msg = anna::functions::asString (
309                "Flip: %d %c %d = %d", response.x, response.op, response.y, response.result
310             );
311             msg += anna::functions::asText (" | Message: ", response.getBody ());
312             msg += anna::functions::asText (" | Delay: ", delay);
313             Logger::warning (msg, ANNA_FILE_LOCATION);
314          );
315       }
316    }
317 }
318
319 bool MyHandler::isOk (const test::Response& response) 
320    throw ()
321 {
322    if (response.op != '+' && response.op != '-' && response.op != '*' && response.op != '/') 
323       return false;
324       
325    int result = 0;
326    
327    switch (response.op) {
328       case '+':
329          result = response.x + response.y;
330          break;
331       case '-':
332          result = response.x - response.y;
333          break;
334       case '*':
335          result = response.x * response.y;
336          break;
337       case '/':
338          result = (response.y != 0) ? (response.x / response.y): 0;
339          break;
340    }
341     
342    return result == response.result;   
343 }