c2849a014ec339990a1cf94e6dd4c956b0256936
[anna.git] / 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   Ejemplo de programa servidor. Atiende peticiones aritmeticas, el protocolo de transporte sera HTTP
11   ver http::Transport y el contenido del mensaje sera el resutaldo de un comm::Codec con los
12   (x, y, op) -> El resultado sera estos tres componente mas result.
13
14   Para poder probar el sistema de congestion se puede indicar un numero de milisegundos de
15   retardo aplicados a cada contestacion.
16
17   Los clientes pueden ser: http_kclient.p http_client.p
18 */
19 #include <iostream>
20
21 #include <anna/core/core.hpp>
22
23 #include <anna/xml/Node.hpp>
24 #include <anna/xml/Attribute.hpp>
25
26 #include <anna/comm/comm.hpp>
27
28 #include <anna/http/Request.hpp>
29 #include <anna/http/Response.hpp>
30 #include <anna/http/Handler.hpp>
31 #include <anna/http/Transport.hpp>
32 #include <anna/http/functions.hpp>
33
34 #include <anna/test/Request.hpp>
35 #include <anna/test/Response.hpp>
36 #include <anna/test/Communicator.hpp>
37
38 using namespace std;
39
40 class MyHandler : public http::Handler {
41 public:
42    MyHandler () : http::Handler ("http_server::MyHandler") {    
43       allocateResponse ()->createHeader (http::Header::Type::Date); 
44    }
45
46 private:
47    test::Request a_testRequest;
48    test::Response a_testResponse;
49
50    void evRequest (ClientSocket&, const http::Request& request) noexcept(false);
51    void evResponse (ClientSocket&, const http::Response&) noexcept(false) {;}
52 };
53
54 class MyCommunicator : public test::Communicator {
55 public:
56    MyCommunicator () : 
57       a_contexts ("Contexts")
58    {;}
59
60 private:
61    ThreadData <MyHandler> a_contexts;
62    
63    void eventReceiveMessage (comm::ClientSocket&, const Message&) noexcept(false);
64 };
65
66 class HTTPArithmeticServer : public comm::Application {
67 public:
68    HTTPArithmeticServer ();
69       
70 private:
71    MyCommunicator a_communicator;
72    comm::ServerSocket* a_serverSocket;
73
74    void initialize () noexcept(false);
75    void run () noexcept(false);
76    xml::Node* asXML (xml::Node* app) const ;
77 };
78
79 using namespace std;
80 using namespace anna::comm;
81
82 int main (int argc, const char** argv)
83 {
84    CommandLine& commandLine (CommandLine::instantiate ());
85    HTTPArithmeticServer app;
86
87    http::functions::initialize ();
88
89    srand (time (NULL));
90
91    try {
92       commandLine.initialize (argv, argc);
93       commandLine.verify ();
94
95       Logger::setLevel (Logger::Debug); 
96       Logger::initialize ("http_server", new anna::TraceWriter ("file.trace", 4048000));
97  
98       app.start ();
99    }
100    catch (Exception& ex) {
101       cout << ex.asString () << endl;
102    }
103    
104    return 0;
105 }
106
107 HTTPArithmeticServer::HTTPArithmeticServer () : 
108    Application ("http_server", "Servidor HTTP de operaciones aritmeticas", "1.0") 
109 {
110    CommandLine& commandLine (CommandLine::instantiate ());
111       
112    commandLine.add ("p", CommandLine::Argument::Mandatory, "Puerto en el que atender peticiones");
113    commandLine.add ("a", CommandLine::Argument::Mandatory, "Direccin IP en la que atender");
114    commandLine.add ("d", CommandLine::Argument::Mandatory, "Retardo aplicado a la contestacion");
115    commandLine.add ("r", CommandLine::Argument::Optional, "Indicador de reuso de direccin", false);
116    commandLine.add ("limit", CommandLine::Argument::Mandatory, "% de ocupacion que permitimos");
117    commandLine.add ("n", CommandLine::Argument::Optional, "Numero de mensajes a servir", true);
118    commandLine.add ("trace", CommandLine::Argument::Optional, "Nivel de trazas (debug,warning, error,...)");
119    commandLine.add ("timeout", CommandLine::Argument::Optional, "Milisegundos transcurridos sin actividad para cerrar el socket");
120 }
121
122 void HTTPArithmeticServer::initialize () 
123    noexcept(false)
124 {
125    CommandLine& cl (CommandLine::instantiate ());
126
127    int port = cl.getIntegerValue ("p");
128    const comm::Device* device = Network::instantiate ().find (Device::asAddress (cl.getValue ("a")));
129
130    a_serverSocket = new ServerSocket (INetAddress (device, port), cl.exists ("r"), &http::Transport::getFactory ());
131 }
132
133 void HTTPArithmeticServer::run ()
134    noexcept(false)
135 {
136    CommandLine& cl (CommandLine::instantiate ());
137
138    a_communicator.attach (a_serverSocket);
139    a_communicator.setDelay ((Millisecond)cl.getIntegerValue ("d"));
140
141    if (cl.exists ("n") == true)
142       a_communicator.setMaxMessage (cl.getIntegerValue ("n"));
143
144    if (cl.exists ("trace"))
145       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
146
147    CongestionController::instantiate ().setLimit (cl.getIntegerValue ("limit"));
148
149    if (cl.exists ("timeout"))
150       a_communicator.setTimeout ((Millisecond)cl.getIntegerValue ("timeout"));
151
152    a_communicator.accept ();
153 }
154
155 xml::Node* HTTPArithmeticServer::asXML (xml::Node* app) const 
156    
157 {
158    xml::Node* node = app::Application::asXML (app);
159    
160    node->createAttribute ("MaxMessage", a_communicator.getMaxMessage ());
161    node->createAttribute ("Message", a_communicator.getMessage ());
162    
163    return node;
164 }
165
166 void MyCommunicator::eventReceiveMessage (ClientSocket& clientSocket, const Message& message)
167    noexcept(false)
168 {
169    LOGMETHOD (TraceMethod tm ("MyCommunicator", "eventReceiveMessage", ANNA_FILE_LOCATION));
170
171    if (clientSocket.support (http::Transport::className ()) == false)
172       return;
173
174    if (canContinue (clientSocket) == false)
175       return;   
176
177    delay ();   
178
179    MyHandler& httpHandler = a_contexts.get ();
180
181    httpHandler.apply (clientSocket, message);
182 }
183
184 void MyHandler::evRequest (ClientSocket& clientSocket, const http::Request& request)
185    noexcept(false)
186 {
187    const DataBlock& body = request.getBody ();
188
189    if (body.getSize () == 0)
190       throw RuntimeException ("La peticion no incorpora los parametros de operacion", ANNA_FILE_LOCATION);
191
192    LOGINFORMATION (
193       string msg ("Body recibido: ");
194       msg += anna::functions::asString (body);
195       Logger::information (msg, ANNA_FILE_LOCATION);
196    );
197
198    if (Codec::getType (body) != test::Request::Id) 
199       throw RuntimeException ("El mensaje recibido no es una peticion aritmetica", ANNA_FILE_LOCATION);
200
201    a_testRequest.decode (body);
202
203    a_testResponse.x = a_testRequest.x;
204    a_testResponse.y = a_testRequest.y;
205    a_testResponse.initTime = a_testRequest.initTime;
206    
207    http::Response* response = allocateResponse ();
208
209    response->setStatusCode (200);
210
211    switch (a_testResponse.op = a_testRequest.op) {
212       case '+':
213          a_testResponse.result = a_testRequest.x + a_testRequest.y;
214          break;
215       case '-':
216          a_testResponse.result = a_testRequest.x - a_testRequest.y;
217          break;
218       case '*':
219          a_testResponse.result = a_testRequest.x * a_testRequest.y;
220          break;
221       case '/':
222          if (a_testRequest.y == 0) {
223             response->setStatusCode (400);
224             response->setReasonPhrase ("Division por cero");
225             a_testResponse.result = 0;
226          }
227          else
228             a_testResponse.result = a_testRequest.x / a_testRequest.y;
229          break;
230    }
231
232    response->setBody (a_testResponse.code ());      
233
234    response->find (http::Header::Type::Date)->setValue ("Mon, 30 Jan 2006 14:36:18 GMT");
235    
236    http::Header* keepAlive = response->find ("Keep-Alive");
237    
238    if (keepAlive == NULL)
239       keepAlive = response->createHeader ("Keep-Alive");   
240    
241    keepAlive->setValue ("Verificacion del cambio 1.0.7");
242       
243
244    LOGINFORMATION (
245       string msg = anna::functions::asString ("%d %c %d = %d", a_testRequest.x, a_testRequest.op, a_testRequest.y, a_testResponse.result);
246       Logger::information (msg, ANNA_FILE_LOCATION);
247    );
248
249    try {
250       clientSocket.send (*response);
251    }
252    catch (Exception& ex) {
253       ex.trace ();
254    }
255 }
256