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