1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
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 //
9 #include <anna/core/tracing/Logger.hpp>
10 #include <anna/config/defines.hpp>
11 #include <anna/core/functions.hpp>
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>
22 #include <anna/http/parser/Abstract.hpp>
27 comm::TransportFactoryImpl <http::Transport> http::Transport::st_factory;
29 http::Transport::Transport() :
38 http::sccs::activate();
40 setOverQuotaSize(8192);
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()
51 comm::Transport::clear();
53 if(a_inputMessage != NULL) {
54 MessageFactory::instantiate().release(a_inputMessage);
55 a_inputMessage = NULL;
58 parser::Abstract::setState(*this, parser::Abstract::ClassType::WaitMessage);
59 a_contentLength = a_bodyOffset = -1;
60 a_lastChunkedByte = 0;
61 a_haveToClear = false;
63 if(a_encodedBlock != NULL)
64 a_encodedBlock->clear();
69 http::Message* http::Transport::getInputMessage()
70 throw(RuntimeException) {
71 if(a_inputMessage == NULL)
72 throw RuntimeException("getInputMessage | HTTP message was not extracted", ANNA_FILE_LOCATION);
74 return a_inputMessage;
77 void http::Transport::setParserState(const parser::Abstract* parser)
78 throw(RuntimeException) {
79 if(a_parser == parser)
84 string msg("http::Transport::setParserState | Transport: ");
85 msg += functions::asHexString(anna_ptrnumber_cast(this));
87 msg += a_parser->asString();
88 Logger::debug(msg, ANNA_FILE_LOCATION);
92 http::Message* http::Transport::allocateInputMessage(const http::Message::Type::_v type)
93 throw(RuntimeException) {
94 if(a_inputMessage != NULL)
95 throw RuntimeException("http::Transport::allocateInputMessage | Former HTTP message was not released", ANNA_FILE_LOCATION);
97 return a_inputMessage = MessageFactory::instantiate().create(type);
100 const http::Tokenizer& http::Transport::split(const http::Token& token)
101 throw(RuntimeException) {
102 a_lineScope.apply(token);
106 const http::Tokenizer& http::Transport::split(const http::Token& token, const char* separator)
107 throw(RuntimeException) {
108 a_lineScope.apply(token, separator);
112 const http::Tokenizer& http::Transport::split(const http::Token& token, const char separator)
113 throw(RuntimeException) {
114 a_lineScope.apply(token, separator);
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
121 const http::Message* http::Transport::externalDecode(const char* buffer, const int size)
122 throw(RuntimeException) {
123 DataBlock dataBlock(buffer, size, false);
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);
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);
143 DataBlock fine(buffer, httpSize, false);
144 return static_cast <const http::Message*>(decode(fine));
147 //----------------------------------------------------------------------------------------------
148 // Según vayan pasando por los estados se iran estableciendo la longitud del mensaje, el punto
149 // de inicio, etc ...
151 // Este metodo se puede invocar N-veces para cada mensaje.
153 // Analiza independientemente cada linea.
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 throw(RuntimeException) {
160 Tokenizer::iterator ii;
161 Tokenizer::iterator maxii;
164 if(a_haveToClear == true) // (1)
167 // Hay estados del parser que requieren acceso al mensaje completo.
168 a_fullMessage = &dataBlock;
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.
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);
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);
185 a_fullScope.apply(chunk, endOfLine);
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);
192 a_fullScope.apply(dataBlock, endOfLine);
194 for(ii = a_fullScope.begin(), maxii = a_fullScope.end(); ii != maxii && result == -1; ii ++) {
195 Token& token = *Tokenizer::token(ii);
197 string msg("http::Transport::calculeSize | Line: ");
198 msg += functions::asString(token, 24);
199 Logger::debug(msg, ANNA_FILE_LOCATION);
201 result = a_parser->processLine(*this, dataBlock, token);
208 //----------------------------------------------------------------------------------------------
209 // Una vez que tenemos todo el mensaje HTTP completo, solo queda establecer el cuerpo del
210 // mensaje, si es que existe.
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 throw(RuntimeException) {
218 const bool isEncoded = (a_encodedBlock == NULL) ? false : a_encodedBlock->isValid();
220 if(isEncoded == true) {
221 switch(a_encodedBlock->getType()) {
222 case EncodedBlock::Type::Chunked:
223 a_inputMessage->comm::Message::setBody(*a_encodedBlock);
226 a_inputMessage->clearBody();
230 int maxSize = dataBlock.getSize();
232 if(a_bodyOffset >= maxSize) {
233 string msg("anna::http::Transport::decode | Error detected | BodyOffset: ");
234 msg += functions::asString(a_bodyOffset);
236 msg += functions::asString(dataBlock);
237 throw RuntimeException(msg, ANNA_FILE_LOCATION);
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);
246 if(a_bodyOffset == -1)
247 a_inputMessage->clearBody();
249 int bodySize = maxSize - a_bodyOffset;
252 a_inputMessage->comm::Message::setBody(dataBlock.getData() + a_bodyOffset, bodySize);
254 a_inputMessage->clearBody();
258 a_haveToClear = true; // (1)
259 return a_inputMessage;
262 //-----------------------------------------------------------------------------------------------------
263 // Toda la complejidad de la codificacion la soporta el http::Message.
264 //-----------------------------------------------------------------------------------------------------
265 const DataBlock& http::Transport::code(comm::Message& message)
266 throw(RuntimeException) {
267 return message.code();
270 http::EncodedBlock* http::Transport::getEncodedBlock()
272 if(a_encodedBlock == NULL)
273 a_encodedBlock = new EncodedBlock;
275 return a_encodedBlock;