1ddd525d06d6bcde34c9a8ee4081466e6f629758
[anna.git] / example / http / client / main.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 /**
10    Realiza peticiones automaticas sobre el servidor HTTP de operaciones aritmeticas.
11
12    La cadencia de envio de mensajes se establece mediante un temporizador.
13    Los operadores se calculan de forma aleatoria.
14
15    El servidor de este cliente: http_server.p o http_rserver.p
16 */
17
18 #include <iostream>
19
20 #include <string.h>
21 #include <unistd.h>
22
23 #include <anna/core/core.hpp>
24 #include <anna/app/functions.hpp>
25 #include <anna/comm/comm.hpp>
26
27 #include <anna/timex/Engine.hpp>
28 #include <anna/timex/Clock.hpp>
29
30 #include <anna/http/Request.hpp>
31 #include <anna/http/Response.hpp>
32 #include <anna/http/Transport.hpp>
33 #include <anna/http/Handler.hpp>
34
35 #include <anna/test/Response.hpp>
36 #include <anna/test/Request.hpp>
37
38 class Sender : public anna::timex::Clock {
39 public:
40    Sender () : Clock ("Sender", (Millisecond)1000), 
41       a_messageBySecond (0), 
42       a_nquarter (0), 
43       a_errorCounter (0),
44       a_txMessageCounter (0)
45    {
46       a_httpRequest.setMethod (http::Method::Type::Post);
47       a_httpRequest.setURI ("HTTPKClient");
48    }
49
50    void setMessageBySecond (const int messageBySecond) throw () { a_messageBySecond = messageBySecond; }
51
52    int getTxMessageCounter () const throw () { return a_txMessageCounter; }
53
54 private:
55    int a_messageBySecond;
56    int a_nquarter;
57    int a_errorCounter;
58    int a_txMessageCounter;
59    http::Request a_httpRequest;
60    test::Request a_testRequest;
61
62    bool tick () throw (RuntimeException);
63 };
64
65 class MyHandler : public http::Handler {
66 public:
67    MyHandler () : http::Handler ("http_client::MyHandler") {;}
68
69 private:
70    http::Response a_httpResponse;
71    test::Response a_testResponse;
72
73    void evRequest (ClientSocket&, const http::Request&) throw (RuntimeException) {;}
74    void evResponse (ClientSocket&, const http::Response&) throw (RuntimeException);
75    
76    static bool isOk (const test::Response& response) throw ();
77 };
78
79 class MyCommunicator : public Communicator {
80 public:
81    MyCommunicator () : Communicator (), a_avgResponseTime (0), a_rxMessageCounter (0) {;}
82
83    void count (const int delay) throw (RuntimeException);
84
85 private:
86    int a_avgResponseTime;
87    int a_rxMessageCounter;
88    MyHandler a_httpHandler;
89
90    void eventReceiveMessage (ClientSocket&, const Message&) throw (RuntimeException);
91
92    void eventBreakConnection (const ClientSocket&) throw ();
93
94    void eventBreakConnection (Server* server) throw () {
95       comm::Communicator::eventBreakConnection (server);
96    }
97    void eventBreakConnection (const Service* service) throw () { 
98       comm::Communicator::eventBreakConnection (service);
99    }   
100 };
101
102 class HeavyClient : public anna::comm::Application {
103 public:
104    HeavyClient ();
105
106    Server* getServer () const throw () { return a_server; }
107    const Sender* getSender () const throw () { return &a_sender; }
108
109 private:
110    MyCommunicator a_communicator;
111    anna::timex::Engine a_timeController;
112    Sender a_sender;
113    Server* a_server;
114
115    void initialize () throw (RuntimeException);
116    void run () throw (RuntimeException);
117 };
118
119 using namespace std;
120
121 int main (int argc, const char** argv)
122 {
123    CommandLine& commandLine (CommandLine::instantiate ());
124    HeavyClient app;
125
126    srand (time (NULL));
127
128    try {
129       commandLine.initialize (argv, argc);
130       commandLine.verify ();
131
132       Logger::setLevel (Logger::Information);
133       string traceFile ("client.");
134       traceFile += anna::functions::asString ((int) getpid ());
135       traceFile += ".trace";
136       Logger::initialize ("http_client", new TraceWriter (traceFile.c_str (),4096000));
137
138       app.start ();
139    }
140    catch (Exception& ex) {
141       cout << ex.asString () << endl;
142    }
143
144    return 0;
145 }
146
147 HeavyClient::HeavyClient () :
148    Application ("http_client", "Cliente HTTP", "1.0"),
149    a_communicator (),
150    a_timeController ((Millisecond)1000, (Millisecond)250)
151 {
152    CommandLine& commandLine (CommandLine::instantiate ());
153
154    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que el servidor atiende respuestas.");
155    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP Puerto en el que el servidor atiende respuestas.");
156    commandLine.add ("n", CommandLine::Argument::Mandatory, "Numero de mensajes por segundo");
157    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
158 }
159
160 void HeavyClient::initialize ()
161    throw (RuntimeException)
162 {
163    CommandLine& cl (CommandLine::instantiate ());
164
165    Network& network = Network::instantiate ();
166
167    a_server = network.createServer (cl.getValue ("a"), cl.getIntegerValue ("p"), true, &http::Transport::getFactory ());
168    a_sender.setMessageBySecond (cl.getIntegerValue ("n"));
169
170    if (cl.exists ("trace"))
171       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
172 }
173
174 void HeavyClient::run ()
175    throw (RuntimeException)
176 {
177    a_timeController.activate (a_sender);
178
179    a_communicator.accept ();
180 }
181
182 void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
183    throw (RuntimeException)
184 {
185    LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", ANNA_FILE_LOCATION));
186
187    if (clientSocket.support (http::Transport::className ()) == false)
188       return;
189
190    a_httpHandler.apply (clientSocket, message);
191 }
192
193 void MyCommunicator::count (const int delay)
194    throw (RuntimeException)
195 {
196    Guard guard (this, "MyCommunicator::eventReceiveMessage");
197    
198    a_rxMessageCounter ++;
199    a_avgResponseTime += delay;
200 }
201
202 void MyCommunicator::eventBreakConnection (const ClientSocket& clientSocket)
203    throw ()
204 {
205    if (a_rxMessageCounter == 0)
206       return;
207
208    LOGNOTICE (
209       HeavyClient& app = static_cast <HeavyClient&> (anna::app::functions::getApp ());   
210       string msg ("Tiempo medio respuesta: ");
211       msg += anna::functions::asString (a_avgResponseTime / a_rxMessageCounter);
212       msg += " ms";
213       msg += anna::functions::asText (" | Rx: ", a_rxMessageCounter);
214       msg += anna::functions::asText (" | Tx: ", app.getSender ()->getTxMessageCounter ());
215       Logger::notice (msg, ANNA_FILE_LOCATION);
216
217       cout << msg << endl << endl;
218    );
219    requestStop ();
220    comm::Communicator::eventBreakConnection (clientSocket);
221 }
222
223 bool Sender::tick ()
224    throw (RuntimeException)
225 {
226    Server* server = static_cast <HeavyClient&> (anna::app::functions::getApp ()).getServer ();
227    Communicator* communicator = anna::app::functions::component <Communicator> (ANNA_FILE_LOCATION);
228
229    if (a_errorCounter > 100) {
230       communicator->requestStop ();
231       Logger::warning ("Terminado por errores continuos en la conexion", ANNA_FILE_LOCATION);
232       return false;
233    }
234
235    for (int n = 0; n < a_messageBySecond && communicator->hasRequestedStop () == false; n ++) {
236       a_testRequest.op = '+';
237       a_testRequest.x = rand () % 1000;
238       a_testRequest.y = rand () % 1000;
239       a_testRequest.initTime = anna::functions::millisecond ();
240
241       try {
242          a_httpRequest.setBody (a_testRequest.code ());
243          server->send (a_httpRequest);
244          a_txMessageCounter ++;
245       }
246       catch (RuntimeException& ex) {
247          a_errorCounter ++;
248          ex.trace ();
249          break;
250       }
251    }
252
253    return true;
254 }
255
256 void MyHandler::evResponse (ClientSocket& clientSocket, const http::Response& response)
257    throw (RuntimeException)
258 {
259    if (response.getStatusCode () == 200) {
260       a_testResponse.decode (response.getBody ());
261       
262       test::Response& response (a_testResponse);
263       
264       const anna::Millisecond now = anna::functions::millisecond ();
265       const int delay =  now - (Millisecond) response.initTime;
266
267       if (delay > 0 && isOk (response) == true) {
268          app::functions::component <MyCommunicator> (ANNA_FILE_LOCATION)->count (delay);
269
270          LOGINFORMATION (
271             string msg = anna::functions::asString (
272                "%d %c %d = %d", response.x, response.op, response.y, response.result
273             );
274             msg += anna::functions::asText (" | Delay: ", delay);
275             Logger::information (msg, ANNA_FILE_LOCATION);
276          );
277       }
278       else {
279          LOGWARNING (
280             string msg = anna::functions::asString (
281                "Flip: %d %c %d = %d", response.x, response.op, response.y, response.result
282             );
283             msg += anna::functions::asText (" | Message: ", response.getBody ());
284             msg += anna::functions::asText (" | Delay: ", delay);
285             Logger::warning (msg, ANNA_FILE_LOCATION);
286          );
287       }
288    }
289 }
290
291 bool MyHandler::isOk (const test::Response& response) 
292    throw ()
293 {
294    if (response.op != '+' && response.op != '-' && response.op != '*' && response.op != '/') 
295       return false;
296       
297    int result = 0;
298    
299    switch (response.op) {
300       case '+':
301          result = response.x + response.y;
302          break;
303       case '-':
304          result = response.x - response.y;
305          break;
306       case '*':
307          result = response.x * response.y;
308          break;
309       case '/':
310          result = (response.y != 0) ? (response.x / response.y): 0;
311          break;
312    }
313     
314    return result == response.result;   
315 }