Remove dynamic exceptions
[anna.git] / source / http / Transport.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 #include <anna/core/tracing/Logger.hpp>
10 #include <anna/config/defines.hpp>
11 #include <anna/core/functions.hpp>
12
13 #include <anna/http/Transport.hpp>
14 #include <anna/http/internal/Tokenizer.hpp>
15 #include <anna/http/internal/Token.hpp>
16 #include <anna/http/internal/sccs.hpp>
17 #include <anna/http/internal/defines.hpp>
18 #include <anna/http/MessageFactory.hpp>
19 #include <anna/http/Method.hpp>
20 #include <anna/http/internal/EncodedBlock.hpp>
21
22 #include <anna/http/parser/Abstract.hpp>
23
24 using namespace std;
25 using namespace anna;
26
27 comm::TransportFactoryImpl <http::Transport> http::Transport::st_factory;
28
29 http::Transport::Transport() :
30   comm::Transport(),
31   a_parser(NULL),
32   a_inputMessage(NULL),
33   a_result(false),
34   a_haveToClear(false),
35   a_encodedBlock(NULL),
36   a_lastChunkedByte(0),
37   a_fullMessage(NULL) {
38   http::sccs::activate();
39   clear();
40   setOverQuotaSize(8192);
41   activateTimeout();
42 }
43
44 //----------------------------------------------------------------------------------------------
45 // Inicializa el estado de esta instancia. Libera el ultimo mensaje HTTP recibido,
46 // inicializa el estado del Parser y las variables numericas que indican atributos
47 // importantes del mensaje HTTP recibido.
48 //----------------------------------------------------------------------------------------------
49 void http::Transport::clear()
50 {
51   comm::Transport::clear();
52
53   if(a_inputMessage != NULL) {
54     MessageFactory::instantiate().release(a_inputMessage);
55     a_inputMessage = NULL;
56   }
57
58   parser::Abstract::setState(*this, parser::Abstract::ClassType::WaitMessage);
59   a_contentLength = a_bodyOffset = -1;
60   a_lastChunkedByte = 0;
61   a_haveToClear = false;
62
63   if(a_encodedBlock != NULL)
64     a_encodedBlock->clear();
65
66   a_fullMessage = NULL;
67 }
68
69 http::Message* http::Transport::getInputMessage()
70 noexcept(false) {
71   if(a_inputMessage == NULL)
72     throw RuntimeException("getInputMessage | HTTP message was not extracted", ANNA_FILE_LOCATION);
73
74   return a_inputMessage;
75 }
76
77 void http::Transport::setParserState(const parser::Abstract* parser)
78 noexcept(false) {
79   if(a_parser == parser)
80     return;
81
82   a_parser = parser;
83   LOGDEBUG(
84     string msg("http::Transport::setParserState | Transport: ");
85     msg += functions::asHexString(anna_ptrnumber_cast(this));
86     msg += " | ";
87     msg += a_parser->asString();
88     Logger::debug(msg, ANNA_FILE_LOCATION);
89   );
90 }
91
92 http::Message* http::Transport::allocateInputMessage(const http::Message::Type::_v type)
93 noexcept(false) {
94   if(a_inputMessage != NULL)
95     throw RuntimeException("http::Transport::allocateInputMessage | Former HTTP message was not released", ANNA_FILE_LOCATION);
96
97   return a_inputMessage = MessageFactory::instantiate().create(type);
98 }
99
100 const http::Tokenizer& http::Transport::split(const http::Token& token)
101 noexcept(false) {
102   a_lineScope.apply(token);
103   return a_lineScope;
104 }
105
106 const http::Tokenizer& http::Transport::split(const http::Token& token, const char* separator)
107 noexcept(false) {
108   a_lineScope.apply(token, separator);
109   return a_lineScope;
110 }
111
112 const http::Tokenizer& http::Transport::split(const http::Token& token, const char separator)
113 noexcept(false) {
114   a_lineScope.apply(token, separator);
115   return a_lineScope;
116 }
117
118 /* El primer bloque se crea con el tamaño aportado por el exterior
119  * El segundo bloque se crea con el tamaño calculado al analizar el mensaje
120  */
121 const http::Message* http::Transport::externalDecode(const char* buffer, const int size)
122 noexcept(false) {
123   DataBlock dataBlock(buffer, size, false);
124   clear();
125   int httpSize;
126
127   if((httpSize = calculeSize(dataBlock)) == -1) {
128     DataBlock shortDataBlock(buffer, min(128, size), false);
129     string msg("http::Transport::externalDecode | Buffer: ");
130     msg += anna::functions::asString(shortDataBlock);
131     msg += " | Can not calculate length of message";
132     throw RuntimeException(msg, ANNA_FILE_LOCATION);
133   }
134
135   if(httpSize > size) {
136     DataBlock shortDataBlock(buffer, min(128, size), false);
137     string msg("http::Transport::externalDecode | ");
138     msg += functions::asString("Message is incomplete | Size: %d | HTTP-Size: %d | Buffer:", size, httpSize);
139     msg += anna::functions::asString(shortDataBlock);
140     throw RuntimeException(msg, ANNA_FILE_LOCATION);
141   }
142
143   DataBlock fine(buffer, httpSize, false);
144   return static_cast <const http::Message*>(decode(fine));
145 }
146
147 //----------------------------------------------------------------------------------------------
148 // Según vayan pasando por los estados se iran estableciendo la longitud del mensaje, el punto
149 // de inicio, etc ...
150 //
151 // Este metodo se puede invocar N-veces para cada mensaje.
152 //
153 // Analiza independientemente cada linea.
154 //
155 // (1) Si comenzamos un nuevo mensaje tenemos que iniciar el estado de la instancia.
156 // (2) Si estamos analizando un Transfer-Encoding: chunked.
157 //----------------------------------------------------------------------------------------------
158 int http::Transport::calculeSize(const DataBlock& dataBlock)
159 noexcept(false) {
160   Tokenizer::iterator ii;
161   Tokenizer::iterator maxii;
162   int result = -1;
163
164   if(a_haveToClear == true)                                                    // (1)
165     clear();
166
167   // Hay estados del parser que requieren acceso al mensaje completo.
168   a_fullMessage = &dataBlock;
169
170   /* Recordar que el 'dataBlock' contiene TODO el mensaje que llevamos acumulando y eso
171    * es malo para analizar los distintos chunk's que nos van llegando.
172    * Así que lo que hace en caso de Transfer-Encoding: chunked es "saltar" todos los bytes
173    * que hayan sido procesados en alguna de las llamadas anteriores.
174    */
175   if(a_encodedBlock != NULL && a_encodedBlock->isValid() && a_lastChunkedByte > 0) {                  // (2)
176     const int chunkSize = dataBlock.getSize() - a_lastChunkedByte;
177     DataBlock chunk(dataBlock.getData() + a_lastChunkedByte, chunkSize, false);
178     a_fullScope.apply(chunk, endOfLine);
179     LOGDEBUG(
180       string msg("http::Transport::calculeSize | DataBlockSize: ");
181       msg += anna::functions::asString("%d | LastChunkSize: %d | Chunk: ", dataBlock.getSize(), a_lastChunkedByte);
182       msg += functions::asString(chunk, 24);
183       Logger::debug(msg, ANNA_FILE_LOCATION);
184     );
185     a_fullScope.apply(chunk, endOfLine);
186
187     for(ii = a_fullScope.begin(), maxii = a_fullScope.end(); ii != maxii && result == -1; ii ++) {
188       Token& token = *Tokenizer::token(ii);
189       result = a_parser->processLine(*this, chunk, token);
190     }
191   } else {
192     a_fullScope.apply(dataBlock, endOfLine);
193
194     for(ii = a_fullScope.begin(), maxii = a_fullScope.end(); ii != maxii && result == -1; ii ++) {
195       Token& token = *Tokenizer::token(ii);
196       LOGDEBUG(
197         string msg("http::Transport::calculeSize | Line: ");
198         msg += functions::asString(token, 24);
199         Logger::debug(msg, ANNA_FILE_LOCATION);
200       )
201       result = a_parser->processLine(*this, dataBlock, token);
202     }
203   }
204
205   return result;
206 }
207
208 //----------------------------------------------------------------------------------------------
209 // Una vez que tenemos todo el mensaje HTTP completo, solo queda establecer el cuerpo del
210 // mensaje, si es que existe.
211 //
212 // (0) El estado parser::WaitMessage deberia haber instancia algun tipo de mensaje.
213 // (1) Hemos terminado de procesar un mensaje => la proxima vez que llamemos a calculeSize sera
214 // para comenzar con el analisis de un un nuevo mensaje.
215 //----------------------------------------------------------------------------------------------
216 const comm::Message* http::Transport::decode(const DataBlock& dataBlock)
217 noexcept(false) {
218   const bool isEncoded = (a_encodedBlock == NULL) ? false : a_encodedBlock->isValid();
219
220   if(isEncoded == true) {
221     switch(a_encodedBlock->getType()) {
222     case EncodedBlock::Type::Chunked:
223       a_inputMessage->comm::Message::setBody(*a_encodedBlock);
224       break;
225     default:
226       a_inputMessage->clearBody();
227       break;
228     }
229   } else {
230     int maxSize = dataBlock.getSize();
231
232     if(a_bodyOffset >= maxSize) {
233       string msg("anna::http::Transport::decode | Error detected | BodyOffset: ");
234       msg += functions::asString(a_bodyOffset);
235       msg += " | Data: ";
236       msg += functions::asString(dataBlock);
237       throw RuntimeException(msg, ANNA_FILE_LOCATION);
238     }
239
240     if(a_inputMessage == NULL) {                                                  // (0)
241       string msg("anna::http::Transport::decode | HTTP message was not extracted | Data: ");
242       msg += functions::asString(dataBlock);
243       throw RuntimeException(msg, ANNA_FILE_LOCATION);
244     }
245
246     if(a_bodyOffset == -1)
247       a_inputMessage->clearBody();
248     else {
249       int bodySize = maxSize - a_bodyOffset;
250
251       if(bodySize > 0)
252         a_inputMessage->comm::Message::setBody(dataBlock.getData() + a_bodyOffset, bodySize);
253       else
254         a_inputMessage->clearBody();
255     }
256   }
257
258   a_haveToClear = true;                                                         // (1)
259   return a_inputMessage;
260 }
261
262 //-----------------------------------------------------------------------------------------------------
263 // Toda la complejidad de la codificacion la soporta el http::Message.
264 //-----------------------------------------------------------------------------------------------------
265 const DataBlock& http::Transport::code(comm::Message& message)
266 noexcept(false) {
267   return message.code();
268 }
269
270 http::EncodedBlock* http::Transport::getEncodedBlock()
271 {
272   if(a_encodedBlock == NULL)
273     a_encodedBlock = new EncodedBlock;
274
275   return a_encodedBlock;
276 }