Remove dynamic exceptions
[anna.git] / example / http / xmlRServer / 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 #include <anna/xml/DocumentMemory.hpp>
26
27 #include <anna/comm/comm.hpp>
28
29 #include <anna/http/Request.hpp>
30 #include <anna/http/Response.hpp>
31 #include <anna/http/Handler.hpp>
32 #include <anna/http/Transport.hpp>
33 #include <anna/http/functions.hpp>
34
35
36 #include <anna/test/Communicator.hpp>
37
38
39 using namespace std;
40
41 class MyHandler : public http::Handler {
42 public:
43    MyHandler () : http::Handler ("http_rserver::MyHandler")
44    {
45       anna_memset (a_xmlAttributes, 0, sizeof (a_xmlAttributes));      
46    }
47    ~MyHandler () {
48    }
49
50    static const char* className () { return "http_rserver::ReceiverFactory"; }
51
52 private:
53    struct Attribute { enum _v { ValueOne, ValueTwo, Operator, Result, Time, Max }; };
54    
55    test::Communicator* a_communicator;
56    xml::DocumentMemory a_xmlRequest;
57    xml::Node* a_xmlResponse;
58    xml::Attribute* a_xmlAttributes [Attribute::Max];
59    
60    void initialize () noexcept(false);
61    void evRequest (ClientSocket&, const http::Request& request) noexcept(false);
62    void evResponse (ClientSocket&, const http::Response&) noexcept(false) {;}
63 };
64
65 class WIMS20ArithmeticServer : public comm::Application {
66 public:
67    WIMS20ArithmeticServer ();
68       
69 private:
70    test::Communicator a_communicator;
71    ReceiverFactoryImpl <MyHandler> a_receiverFactory;
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    WIMS20ArithmeticServer 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 WIMS20ArithmeticServer::WIMS20ArithmeticServer () : 
108    Application ("http_rserver", "Servidor WIMS20 de operaciones aritmeticas (iRS)", "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 direccion", 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, "Timeout (ms) del cliente sin enviar peticiones");
120    commandLine.add ("quota", CommandLine::Argument::Optional, "Numero de bytes aceptados antes de cerrar el socket");
121 }
122
123 void WIMS20ArithmeticServer::initialize () 
124    noexcept(false)
125 {
126    CommandLine& cl (CommandLine::instantiate ());
127
128    int port = cl.getIntegerValue ("p");
129    const comm::Device* device = Network::instantiate ().find (Device::asAddress (cl.getValue ("a")));
130
131    comm::TransportFactory* ttff = &http::Transport::getFactory ();
132
133    if (cl.exists ("quota") == true) 
134       ttff->setOverQuotaSize (cl.getIntegerValue ("quota"));
135
136    a_serverSocket = new ServerSocket (INetAddress (device, port), cl.exists ("r"), ttff);
137    a_serverSocket->setReceiverFactory (a_receiverFactory);
138 }
139
140 void WIMS20ArithmeticServer::run ()
141    noexcept(false)
142 {
143    CommandLine& cl (CommandLine::instantiate ());
144
145    a_communicator.attach (a_serverSocket);
146    a_communicator.setDelay ((Millisecond)cl.getIntegerValue ("d"));
147
148    if (cl.exists ("n") == true)
149       a_communicator.setMaxMessage (cl.getIntegerValue ("n"));
150
151    if (cl.exists ("trace"))
152       Logger::setLevel (Logger::asLevel (cl.getValue ("trace")));
153
154    CongestionController::instantiate ().setLimit (cl.getIntegerValue ("limit"));
155
156    if (cl.exists ("timeout") == true)
157       a_communicator.setTimeout ((Millisecond)cl.getIntegerValue ("timeout"));
158
159    a_communicator.accept ();
160 }
161
162 xml::Node* WIMS20ArithmeticServer::asXML (xml::Node* app) const 
163    
164 {
165    xml::Node* node = app::Application::asXML (app);
166    
167    node->createAttribute ("MaxMessage", a_communicator.getMaxMessage ());
168    node->createAttribute ("Message", a_communicator.getMessage ());
169    
170    return node;
171 }
172
173 void MyHandler::initialize ()
174    noexcept(false)
175 {
176    a_communicator = app::functions::component <test::Communicator> (ANNA_FILE_LOCATION);
177    
178    /**
179     * Crea el documento XML que usaremos para codificar la respuesta y 
180     * pre-localiza los objetos sobre los que tendrá que actuar
181     */
182    a_xmlResponse = new xml::Node ("Response");
183    a_xmlAttributes [Attribute::ValueOne] = a_xmlResponse->createAttribute ("ValueOne", 0);
184    a_xmlAttributes [Attribute::ValueTwo] = a_xmlResponse->createAttribute ("ValueTwo", 0);
185    a_xmlAttributes [Attribute::Operator] = a_xmlResponse->createAttribute ("Operator", 0);
186    a_xmlAttributes [Attribute::Result] = a_xmlResponse->createAttribute ("Result", 0);
187    a_xmlAttributes [Attribute::Time] = a_xmlResponse->createAttribute ("Time", 0);
188 }
189
190 /*
191  * Recibe una peticion
192  */
193 void MyHandler::evRequest (ClientSocket& clientSocket, const http::Request& httpRequest)
194    noexcept(false)
195 {
196    LOGMETHOD (TraceMethod tm ("MyReceiver", "apply", ANNA_FILE_LOCATION));
197
198    if (a_communicator->canContinue (clientSocket) == false)
199       return;      
200       
201    // Extrace el documento XML de la petición.
202    a_xmlRequest.initialize (httpRequest.getBody ());
203    
204    const xml::Node* request = a_xmlRequest.parse (); 
205    
206    http::Response* response = allocateResponse ();
207
208    // Antes de contestar espera el tiempo indicado en la configuración
209    a_communicator->delay ();
210    
211    /**
212     * Obtiene los valores de los atributos del documento XML
213     */
214    const char* op = request->getAttribute ("Operator")->getCStringValue ();
215    int v1 = request->getAttribute ("ValueOne")->getIntegerValue ();
216    int v2 = request->getAttribute ("ValueTwo")->getIntegerValue ();
217       
218    /**
219     * Establece el valor de los objetos XML pre-localizados que irán en la respuesta
220     */
221    a_xmlAttributes [Attribute::ValueOne]->setValue (v1);
222    a_xmlAttributes [Attribute::ValueTwo]->setValue (v2);
223    a_xmlAttributes [Attribute::Operator]->setValue (op);
224    
225    // Este dato sirve para calcular el tiempo de respuesta del servidor 
226    a_xmlAttributes [Attribute::Time]->setValue (request->getAttribute ("Millisecond")->getIntegerValue ());
227    
228    response->setStatusCode (200);
229
230    switch (*op) {
231       case '+': a_xmlAttributes [Attribute::Result]->setValue (v1 + v2); break;
232       case '-': a_xmlAttributes [Attribute::Result]->setValue (v1 - v2); break;
233       case '*': a_xmlAttributes [Attribute::Result]->setValue (v1 * v2); break;
234       case '/':
235          if (v2 == 0) {
236             response->setStatusCode (400);
237             response->setReasonPhrase ("Division por cero");
238          }
239          else
240             a_xmlAttributes [Attribute::Result]->setValue (v1 / v2);
241          break;
242       default:
243          response->setStatusCode (501);
244          break;
245    }
246
247    /* Observar que establece directamente el documento XML como cuerpo
248     * de la respuesta HTTP.
249     */    
250    if (response->getStatusCode () == 200)
251       response->setBody (a_xmlResponse);
252    else
253       response->clearBody ();      
254    
255    clientSocket.send (*response);
256 }