1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
17 // * Neither the name of the copyright holder 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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
37 #include <anna/core/tracing/Logger.hpp>
38 #include <anna/config/defines.hpp>
39 #include <anna/core/functions.hpp>
41 #include <anna/http/Transport.hpp>
42 #include <anna/http/internal/Tokenizer.hpp>
43 #include <anna/http/internal/Token.hpp>
44 #include <anna/http/internal/sccs.hpp>
45 #include <anna/http/internal/defines.hpp>
46 #include <anna/http/MessageFactory.hpp>
47 #include <anna/http/Method.hpp>
48 #include <anna/http/internal/EncodedBlock.hpp>
50 #include <anna/http/parser/Abstract.hpp>
55 comm::TransportFactoryImpl <http::Transport> http::Transport::st_factory;
57 http::Transport::Transport() :
66 http::sccs::activate();
68 setOverQuotaSize(8192);
72 //----------------------------------------------------------------------------------------------
73 // Inicializa el estado de esta instancia. Libera el ultimo mensaje HTTP recibido,
74 // inicializa el estado del Parser y las variables numericas que indican atributos
75 // importantes del mensaje HTTP recibido.
76 //----------------------------------------------------------------------------------------------
77 void http::Transport::clear()
79 comm::Transport::clear();
81 if(a_inputMessage != NULL) {
82 MessageFactory::instantiate().release(a_inputMessage);
83 a_inputMessage = NULL;
86 parser::Abstract::setState(*this, parser::Abstract::ClassType::WaitMessage);
87 a_contentLength = a_bodyOffset = -1;
88 a_lastChunkedByte = 0;
89 a_haveToClear = false;
91 if(a_encodedBlock != NULL)
92 a_encodedBlock->clear();
97 http::Message* http::Transport::getInputMessage()
98 throw(RuntimeException) {
99 if(a_inputMessage == NULL)
100 throw RuntimeException("getInputMessage | HTTP message was not extracted", ANNA_FILE_LOCATION);
102 return a_inputMessage;
105 void http::Transport::setParserState(const parser::Abstract* parser)
106 throw(RuntimeException) {
107 if(a_parser == parser)
112 string msg("http::Transport::setParserState | Transport: ");
113 msg += functions::asHexString(anna_ptrnumber_cast(this));
115 msg += a_parser->asString();
116 Logger::debug(msg, ANNA_FILE_LOCATION);
120 http::Message* http::Transport::allocateInputMessage(const http::Message::Type::_v type)
121 throw(RuntimeException) {
122 if(a_inputMessage != NULL)
123 throw RuntimeException("http::Transport::allocateInputMessage | Former HTTP message was not released", ANNA_FILE_LOCATION);
125 return a_inputMessage = MessageFactory::instantiate().create(type);
128 const http::Tokenizer& http::Transport::split(const http::Token& token)
129 throw(RuntimeException) {
130 a_lineScope.apply(token);
134 const http::Tokenizer& http::Transport::split(const http::Token& token, const char* separator)
135 throw(RuntimeException) {
136 a_lineScope.apply(token, separator);
140 const http::Tokenizer& http::Transport::split(const http::Token& token, const char separator)
141 throw(RuntimeException) {
142 a_lineScope.apply(token, separator);
146 /* El primer bloque se crea con el tamaño aportado por el exterior
147 * El segundo bloque se crea con el tamaño calculado al analizar el mensaje
149 const http::Message* http::Transport::externalDecode(const char* buffer, const int size)
150 throw(RuntimeException) {
151 DataBlock dataBlock(buffer, size, false);
155 if((httpSize = calculeSize(dataBlock)) == -1) {
156 DataBlock shortDataBlock(buffer, min(128, size), false);
157 string msg("http::Transport::externalDecode | Buffer: ");
158 msg += anna::functions::asString(shortDataBlock);
159 msg += " | Can not calculate length of message";
160 throw RuntimeException(msg, ANNA_FILE_LOCATION);
163 if(httpSize > size) {
164 DataBlock shortDataBlock(buffer, min(128, size), false);
165 string msg("http::Transport::externalDecode | ");
166 msg += functions::asString("Message is incomplete | Size: %d | HTTP-Size: %d | Buffer:", size, httpSize);
167 msg += anna::functions::asString(shortDataBlock);
168 throw RuntimeException(msg, ANNA_FILE_LOCATION);
171 DataBlock fine(buffer, httpSize, false);
172 return static_cast <const http::Message*>(decode(fine));
175 //----------------------------------------------------------------------------------------------
176 // Según vayan pasando por los estados se iran estableciendo la longitud del mensaje, el punto
177 // de inicio, etc ...
179 // Este metodo se puede invocar N-veces para cada mensaje.
181 // Analiza independientemente cada linea.
183 // (1) Si comenzamos un nuevo mensaje tenemos que iniciar el estado de la instancia.
184 // (2) Si estamos analizando un Transfer-Encoding: chunked.
185 //----------------------------------------------------------------------------------------------
186 int http::Transport::calculeSize(const DataBlock& dataBlock)
187 throw(RuntimeException) {
188 Tokenizer::iterator ii;
189 Tokenizer::iterator maxii;
192 if(a_haveToClear == true) // (1)
195 // Hay estados del parser que requieren acceso al mensaje completo.
196 a_fullMessage = &dataBlock;
198 /* Recordar que el 'dataBlock' contiene TODO el mensaje que llevamos acumulando y eso
199 * es malo para analizar los distintos chunk's que nos van llegando.
200 * Así que lo que hace en caso de Transfer-Encoding: chunked es "saltar" todos los bytes
201 * que hayan sido procesados en alguna de las llamadas anteriores.
203 if(a_encodedBlock != NULL && a_encodedBlock->isValid() && a_lastChunkedByte > 0) { // (2)
204 const int chunkSize = dataBlock.getSize() - a_lastChunkedByte;
205 DataBlock chunk(dataBlock.getData() + a_lastChunkedByte, chunkSize, false);
206 a_fullScope.apply(chunk, endOfLine);
208 string msg("http::Transport::calculeSize | DataBlockSize: ");
209 msg += anna::functions::asString("%d | LastChunkSize: %d | Chunk: ", dataBlock.getSize(), a_lastChunkedByte);
210 msg += functions::asString(chunk, 24);
211 Logger::debug(msg, ANNA_FILE_LOCATION);
213 a_fullScope.apply(chunk, endOfLine);
215 for(ii = a_fullScope.begin(), maxii = a_fullScope.end(); ii != maxii && result == -1; ii ++) {
216 Token& token = *Tokenizer::token(ii);
217 result = a_parser->processLine(*this, chunk, token);
220 a_fullScope.apply(dataBlock, endOfLine);
222 for(ii = a_fullScope.begin(), maxii = a_fullScope.end(); ii != maxii && result == -1; ii ++) {
223 Token& token = *Tokenizer::token(ii);
225 string msg("http::Transport::calculeSize | Line: ");
226 msg += functions::asString(token, 24);
227 Logger::debug(msg, ANNA_FILE_LOCATION);
229 result = a_parser->processLine(*this, dataBlock, token);
236 //----------------------------------------------------------------------------------------------
237 // Una vez que tenemos todo el mensaje HTTP completo, solo queda establecer el cuerpo del
238 // mensaje, si es que existe.
240 // (0) El estado parser::WaitMessage deberia haber instancia algun tipo de mensaje.
241 // (1) Hemos terminado de procesar un mensaje => la proxima vez que llamemos a calculeSize sera
242 // para comenzar con el analisis de un un nuevo mensaje.
243 //----------------------------------------------------------------------------------------------
244 const comm::Message* http::Transport::decode(const DataBlock& dataBlock)
245 throw(RuntimeException) {
246 const bool isEncoded = (a_encodedBlock == NULL) ? false : a_encodedBlock->isValid();
248 if(isEncoded == true) {
249 switch(a_encodedBlock->getType()) {
250 case EncodedBlock::Type::Chunked:
251 a_inputMessage->comm::Message::setBody(*a_encodedBlock);
254 a_inputMessage->clearBody();
258 int maxSize = dataBlock.getSize();
260 if(a_bodyOffset >= maxSize) {
261 string msg("anna::http::Transport::decode | Error detected | BodyOffset: ");
262 msg += functions::asString(a_bodyOffset);
264 msg += functions::asString(dataBlock);
265 throw RuntimeException(msg, ANNA_FILE_LOCATION);
268 if(a_inputMessage == NULL) { // (0)
269 string msg("anna::http::Transport::decode | HTTP message was not extracted | Data: ");
270 msg += functions::asString(dataBlock);
271 throw RuntimeException(msg, ANNA_FILE_LOCATION);
274 if(a_bodyOffset == -1)
275 a_inputMessage->clearBody();
277 int bodySize = maxSize - a_bodyOffset;
280 a_inputMessage->comm::Message::setBody(dataBlock.getData() + a_bodyOffset, bodySize);
282 a_inputMessage->clearBody();
286 a_haveToClear = true; // (1)
287 return a_inputMessage;
290 //-----------------------------------------------------------------------------------------------------
291 // Toda la complejidad de la codificacion la soporta el http::Message.
292 //-----------------------------------------------------------------------------------------------------
293 const DataBlock& http::Transport::code(comm::Message& message)
294 throw(RuntimeException) {
295 return message.code();
298 http::EncodedBlock* http::Transport::getEncodedBlock()
300 if(a_encodedBlock == NULL)
301 a_encodedBlock = new EncodedBlock;
303 return a_encodedBlock;