http_codec.cc 10.8 KB
Newer Older
garciay's avatar
garciay committed
#include <stdexcept>
#include <regex>
#include <string>
//#include <typeinfo>

garciay's avatar
garciay committed
#include "http_codec.hh"
garciay's avatar
garciay committed
#include "loggers.hh"

#include "LibItsHttp_TypesAndValues.hh"

garciay's avatar
garciay committed
int http_codec::encode (const LibItsHttp__TypesAndValues::HttpMessage& msg, OCTETSTRING& data)
garciay's avatar
garciay committed
{
garciay's avatar
garciay committed
  loggers::get_instance().log_msg(">>> http_codec::encode: ", (const Base_Type&)msg);
garciay's avatar
garciay committed

  TTCN_EncDec::clear_error();
  TTCN_Buffer encoding_buffer;

  int result;
  if (msg.ischosen(LibItsHttp__TypesAndValues::HttpMessage::ALT_request)) {
    result = encode_request(msg.request(), encoding_buffer);
  } else if (msg.ischosen(LibItsHttp__TypesAndValues::HttpMessage::ALT_response)) {
garciay's avatar
garciay committed
    //result = encode_response(msg.response(), encoding_buffer);
garciay's avatar
garciay committed
  } else {
garciay's avatar
garciay committed
    loggers::get_instance().warning("http_codec::encode: Unbound HttpMessage");
garciay's avatar
garciay committed
    return -1;
  }

  data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data());

garciay's avatar
garciay committed
  loggers::get_instance().log_msg("<<< http_codec::encode: data=", data);
garciay's avatar
garciay committed
  return result;
}

garciay's avatar
garciay committed
int http_codec::decode (const OCTETSTRING& data, LibItsHttp__TypesAndValues::HttpMessage& msg, params* params)
garciay's avatar
garciay committed
{
garciay's avatar
garciay committed
  loggers::get_instance().log_msg(">>> http_codec::decode: data=", data);
garciay's avatar
garciay committed
  TTCN_EncDec::clear_error();
  TTCN_Buffer decoding_buffer(data);
garciay's avatar
garciay committed
  loggers::get_instance().log_to_hexa("http_codec::decode: decoding_buffer=", decoding_buffer);
garciay's avatar
garciay committed
  _params = params;

  // Get the first line (e.g. HTTP/1.1 302 Found or POST / HTTP/1.1)
  CHARSTRING message_id;
  if (get_line(decoding_buffer, message_id) == -1) {
    return -1;
  }
garciay's avatar
garciay committed
  loggers::get_instance().log_msg("http_codec::decode: message_id: ", message_id);
garciay's avatar
garciay committed
  // Extract parameters
  try {
    std::string str(static_cast<const char*>(message_id));
    std::regex rgx ("\\s*(\\w+)/");
    std::sregex_iterator begin(str.cbegin(), str.cend(), rgx);
    std::smatch m = *begin;
garciay's avatar
garciay committed
    loggers::get_instance().log("http_codec::decode: %d - %s", m.size(), m[0].str().c_str());
garciay's avatar
garciay committed
    if (m[0].str().compare("HTTP/") == 0) { // HTTP response
garciay's avatar
garciay committed
          LibItsHttp__TypesAndValues::Response response;
      std::regex rgx ("\\s*HTTP/(\\d)\\.(\\d)\\s+(\\d+)\\s+(\\w+)");
garciay's avatar
garciay committed
      std::sregex_iterator begin(str.cbegin(), str.cend(), rgx);
      std::smatch m = *begin;
      if (m.size() != 5) {
garciay's avatar
garciay committed
            loggers::get_instance().error("http_codec::decode: Unsupported tag");
            return -1;
garciay's avatar
garciay committed
      }
      response.version__major() = std::stoi(m[1].str().c_str());
      response.version__minor() = std::stoi(m[2].str().c_str());
      response.statuscode() = std::stoi(m[3].str().c_str());
      response.statustext() = CHARSTRING(m[4].str().c_str());
      LibItsHttp__TypesAndValues::HeaderLines headers;
      decode_headers(decoding_buffer, headers);
      response.header() = headers;
      loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer);
garciay's avatar
garciay committed
          CHARSTRING body("");
        if (decode_body(decoding_buffer, body) == -1) {
              response.body().set_to_omit();
        } else if (body.lengthof() == 0) {
              response.body().set_to_omit();
        } else {
              // TODO
        }
          msg.response() = response;
garciay's avatar
garciay committed
    } else { // HTTP request
garciay's avatar
garciay committed
          LibItsHttp__TypesAndValues::Request request;
garciay's avatar
garciay committed
      std::regex rgx ("\\s*(\\w+)\\s+(.+)\\s+HTTP/(\\d)\\.(\\d)");
      std::sregex_iterator begin(str.cbegin(), str.cend(), rgx);
      std::smatch m = *begin;
      if (m.size() != 5) {
garciay's avatar
garciay committed
            loggers::get_instance().error("http_codec::decode: Unsupported tag");
            return -1;
garciay's avatar
garciay committed
      }
      request.method() = CHARSTRING(m[1].str().c_str());
      request.uri() = CHARSTRING(m[2].str().c_str());
      request.version__major() = std::stoi(m[3].str().c_str());
      request.version__minor() = std::stoi(m[4].str().c_str());
      LibItsHttp__TypesAndValues::HeaderLines headers;
      decode_headers(decoding_buffer, headers);
      request.header() = headers;
garciay's avatar
garciay committed
      loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer);
      CHARSTRING body("");
        if (decode_body(decoding_buffer, body) == -1) {
              request.body().set_to_omit();
        } else if (body.lengthof() == 0) {
              request.body().set_to_omit();
        } else {
              // TODO
        }
garciay's avatar
garciay committed
      msg.request() = request;
garciay's avatar
garciay committed
    }

garciay's avatar
garciay committed
    loggers::get_instance().log_msg("<<< http_codec::decode: ", (const Base_Type&)msg);
    return 0;
garciay's avatar
garciay committed
  }
  catch(const std::logic_error& e) {
    return -1;
  }
}

garciay's avatar
garciay committed
int http_codec::encode_request(const LibItsHttp__TypesAndValues::Request& p_request, TTCN_Buffer& p_encoding_buffer)
garciay's avatar
garciay committed
{
garciay's avatar
garciay committed
  loggers::get_instance().log_msg(">>> http_codec::encode_request: ", (const Base_Type&)p_request);
garciay's avatar
garciay committed

garciay's avatar
garciay committed
  const OPTIONAL<LibItsHttp__MessageBodyTypes::HttpMessageBody>& v = p_request.body();
garciay's avatar
garciay committed
  CHARSTRING body("");
garciay's avatar
garciay committed
  if (v.ispresent()) {
garciay's avatar
garciay committed
    // FIXME body = static_cast<const CHARSTRING&>(*v.get_opt_value());
garciay's avatar
garciay committed
    loggers::get_instance().log_msg("http_codec::encode_request: body: ", body);
garciay's avatar
garciay committed
  }

  // Encode generic part
  p_encoding_buffer.put_cs(p_request.method());
  p_encoding_buffer.put_c(' ');
  p_encoding_buffer.put_cs(p_request.uri());
  p_encoding_buffer.put_cs(" HTTP/");
  p_encoding_buffer.put_cs(int2str(p_request.version__major()));
  p_encoding_buffer.put_c('.');
  p_encoding_buffer.put_cs(int2str(p_request.version__minor()));
  p_encoding_buffer.put_cs("\r\n");
  // Encode headers
  const LibItsHttp__TypesAndValues::HeaderLines& headers = p_request.header();
  for (int i = 0; i < headers.size_of(); i++) {
    const LibItsHttp__TypesAndValues::HeaderLine& header = headers[i];
garciay's avatar
garciay committed
    loggers::get_instance().log_msg("http_codec::encode_request: Processing header ", header.header__name());
garciay's avatar
garciay committed
    p_encoding_buffer.put_cs(header.header__name());
    p_encoding_buffer.put_cs(": ");
    if (std::string(static_cast<const char*>(header.header__name())).compare("Content-Length") == 0) {
garciay's avatar
garciay committed
      loggers::get_instance().log("http_codec::encode_request: body length: %d", body.lengthof());
      p_encoding_buffer.put_cs(int2str(body.lengthof()));
garciay's avatar
garciay committed
    } else {
garciay's avatar
garciay committed
      const OPTIONAL<LibItsHttp__TypesAndValues::charstring__list>& o = header.header__value();
      if (o.ispresent()) {
garciay's avatar
garciay committed
		const LibItsHttp__TypesAndValues::charstring__list& v = dynamic_cast<const OPTIONAL<LibItsHttp__TypesAndValues::charstring__list> &>(o);
		if (v.size_of() > 0) {
		  loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[0]);
		  p_encoding_buffer.put_cs(v[0]);
		  int j = 1;
		  while (j < v.size_of()) {
				p_encoding_buffer.put_cs("; ");
				loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[j]);
				p_encoding_buffer.put_cs(v[j]);
				j += 1;
		  } // End of 'while' statement
		}
garciay's avatar
garciay committed
      } // else, do not include it
garciay's avatar
garciay committed
    }
    p_encoding_buffer.put_cs("\r\n");
  } // End of 'for' statement

garciay's avatar
garciay committed
  if (body.lengthof() != 0) {
    p_encoding_buffer.put_cs(body);
garciay's avatar
garciay committed
  }

  p_encoding_buffer.put_cs("\r\n");
garciay's avatar
garciay committed

  return 0;
}

garciay's avatar
garciay committed

garciay's avatar
garciay committed
int http_codec::decode_headers(TTCN_Buffer& decoding_buffer, LibItsHttp__TypesAndValues::HeaderLines& headers) {
  loggers::get_instance().log(">>> http_codec::decode_headers");
  loggers::get_instance().log_to_hexa("http_codec::decode_headers", decoding_buffer);
garciay's avatar
garciay committed

  CHARSTRING cstr;
  int i = 0;
  while (true) {
garciay's avatar
garciay committed
        switch(get_line(decoding_buffer, cstr, true)) {
        case 0: {
        loggers::get_instance().log_msg("http_codec::decode_headers: ", cstr);
        LibItsHttp__TypesAndValues::HeaderLine header;
        if (decode_header(cstr, header) == -1) {
              loggers::get_instance().warning("http_codec::decode_headers: Failed to decode header %s", static_cast<const char*>(cstr));
              return -1;
garciay's avatar
garciay committed
        }
garciay's avatar
garciay committed
        headers[i++] = header;
          }
          break;
        case 1:
garciay's avatar
garciay committed
        loggers::get_instance().log_msg("<<< http_codec::decode_headers: ", headers);
        return 0;
garciay's avatar
garciay committed
        case -1:
garciay's avatar
garciay committed
        loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers");
        return -1;
garciay's avatar
garciay committed

        } // End of 'switch' statement
garciay's avatar
garciay committed
  } // End of 'while' statement
}

garciay's avatar
garciay committed
int http_codec::decode_header(CHARSTRING& header_line, LibItsHttp__TypesAndValues::HeaderLine& header) {
  loggers::get_instance().log_msg(">>> http_codec::decode_header", header_line);
garciay's avatar
garciay committed

  try {
    std::string str(static_cast<const char*>(header_line));
garciay's avatar
garciay committed
    std::regex rgx ("([0-9a-zA-Z-]+)\\:\\s+(.+)(;(.+))*");
garciay's avatar
garciay committed
    std::sregex_iterator begin(str.cbegin(), str.cend(), rgx);
    std::smatch m = *begin;
    if (m.size() < 5) {
      return -1;
    }
    header.header__name() = CHARSTRING(m[1].str().c_str());
    LibItsHttp__TypesAndValues::charstring__list v;
    for (unsigned int j = 0; j < m.size(); j++) {
      if (m[j + 2].str().length() == 0) {
        break;
      }
      v[j] = CHARSTRING(m[j + 2].str().c_str());
    } // End of 'for' statement

garciay's avatar
garciay committed
    header.header__value() = OPTIONAL<LibItsHttp__TypesAndValues::charstring__list>(v);
garciay's avatar
garciay committed
    return 0;
garciay's avatar
garciay committed
  }
  catch(const std::logic_error& e) {
garciay's avatar
garciay committed
      return -1;
garciay's avatar
garciay committed
int http_codec::decode_body(TTCN_Buffer& decoding_buffer, CHARSTRING& body) {
garciay's avatar
garciay committed
  loggers::get_instance().log(">>> http_codec::decode_body");
  loggers::get_instance().log_to_hexa("http_codec::decode_body", decoding_buffer);
garciay's avatar
garciay committed

garciay's avatar
garciay committed
  loggers::get_instance().log_msg("<<< http_codec::decode_body: ", body);
  return -1;
garciay's avatar
garciay committed
}
garciay's avatar
garciay committed

garciay's avatar
garciay committed
int http_codec::get_line(TTCN_Buffer& buffer, CHARSTRING& to, const bool concatenate_header_lines) {
garciay's avatar
garciay committed
    unsigned int i = 0;
    const unsigned char *cc_to = buffer.get_read_data();

    // Sanity checks
    if(buffer.get_read_len() == 0) {
        return -1;
    }

    while (true) {
garciay's avatar
garciay committed
      // Skip spaces, and emplty lines
garciay's avatar
garciay committed
        for( ; i < buffer.get_read_len() && cc_to[i] != '\0' && cc_to[i] != '\r' && cc_to[i] != '\n'; i++);
        if(i >= buffer.get_read_len()) { // No more characters to process
            to = CHARSTRING("");
            return -1;
        } else if(cc_to[i] == '\n') { // New line found, we don't care is '\r' is missing
garciay's avatar
garciay committed
        if ((i > 0) && ((i + 1) < buffer.get_read_len()) && concatenate_header_lines && ((cc_to[i + 1] == ' ') || (cc_to[i + 1] == '\t'))) {
          i += 1; // Skip it
        } else {
            to = CHARSTRING(i, (const char*)cc_to);
            buffer.set_pos(buffer.get_pos() + i + 1);
            return i == 0 ? 1 : 0;
        }
      } else {
        if ((i + 1) < buffer.get_read_len() && cc_to[i + 1] != '\n') {
             return -1;
        } else if(i > 0 && (i + 2) < buffer.get_read_len() && concatenate_header_lines && (cc_to[i+2] == ' ' || cc_to[i+2] == '\t')) {
             i += 2;
        } else {
            to = CHARSTRING(i, (const char*)cc_to);
            buffer.set_pos(buffer.get_pos() + i + 2);
            return i == 0 ? 1 : 0;
        }
      }
garciay's avatar
garciay committed
    } // End of 'while' statement
}