diff --git a/ccsrc/Framework/include/base_time.hh b/ccsrc/Framework/include/base_time.hh index 03fa8f6cd453f1ed6bcde16fff8b47b86b7a9ecf..ae67b57073b531569978ab9125b60c5bd4251e63 100644 --- a/ccsrc/Framework/include/base_time.hh +++ b/ccsrc/Framework/include/base_time.hh @@ -1,6 +1,6 @@ /*! * \file base_time.hh - * \brief Header file for the control port base_time functionality. + * \brief Header file for base_time functionality. * \author ETSI STF525 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. diff --git a/ccsrc/Framework/src/base_time.cc b/ccsrc/Framework/src/base_time.cc index 38331193d666b1354b31cdc7b0ebc07ed135734c..28c2cc60bc5ce25ef94b3ac81889439019a6c0a4 100644 --- a/ccsrc/Framework/src/base_time.cc +++ b/ccsrc/Framework/src/base_time.cc @@ -1,6 +1,6 @@ /*! * \file base_time.cc - * \brief Source file for the control port base_time functionality. + * \brief Source file base_time functionality. * \author ETSI STF525 * \copyright ETSI Copyright Notification * No part may be reproduced except as authorized by written permission. diff --git a/ccsrc/Helpers/helpers_externals.cc b/ccsrc/Helpers/helpers_externals.cc index b38729221d12fcfc29c346202d2b6ad21cfec7d0..fd7582801945d2cbe4537d286340b98e6730fb72 100644 --- a/ccsrc/Helpers/helpers_externals.cc +++ b/ccsrc/Helpers/helpers_externals.cc @@ -2,6 +2,7 @@ #include #include "base_time.hh" +#include "converter.hh" #include "loggers.hh" #ifndef M_PI @@ -29,7 +30,7 @@ namespace LibHelpers__Functions { * @see function f_getMinuteOfTheYear() return MinuteOfTheYear */ INTEGER fx__getMinuteOfTheYear() { - // TODO: this is just a sceleton. fill in the function + // TODO: this is just a skeleton. fill in the function return 0; } @@ -39,8 +40,38 @@ namespace LibHelpers__Functions { * @see function f_getDSecond() return DSecond */ INTEGER fx__getDSecond() { - // TODO: this is just a sceleton. fill in the function + // TODO: this is just a skeleton. fill in the function return 0; } + /** + * @desc Encode into Base64 + * @return p_to_encode - The buffer to be encoded + * @see function f_enc_base64(in octetstring p_to_encode) return octetstring + */ + OCTETSTRING fx__enc__base64(const OCTETSTRING& p_to_encode) { + loggers::get_instance().log_msg(">>> fx__enc__base64: ", p_to_encode); + + const std::vector to_encode(static_cast(p_to_encode), static_cast(p_to_encode) + p_to_encode.lengthof()); + std::vector b64 = converter::get_instance().buffer_to_base64(to_encode); + OCTETSTRING os(b64.size(), b64.data()); + loggers::get_instance().log_msg("<<< fx__enc__base64: ", os); + return os; + } + + /** + * @desc Decode from Base64 + * @return p_to_decode - The buffer to be decoded + * @see function f_dec_base64(in octetstring p_to_decode) return octetstring + */ + OCTETSTRING fx__dec__base64(const OCTETSTRING& p_to_decode) { + loggers::get_instance().log_msg(">>> fx__dec__base64: ", p_to_decode); + + const std::vector to_decode(static_cast(p_to_decode), static_cast(p_to_decode) + p_to_decode.lengthof()); + std::vector b64 = converter::get_instance().base64_to_buffer(to_decode); + OCTETSTRING os(b64.size(), b64.data()); + loggers::get_instance().log_msg("<<< fx__dec__base64: ", os); + return os; + } + } // namespace LibHelpers__Functions diff --git a/ccsrc/Protocols/Http/http_codec.cc b/ccsrc/Protocols/Http/http_codec.cc index 832276918a793546307919e3d573e4131ea15145..a5818f1c910503dab07f22053b28b2e7d078f105 100644 --- a/ccsrc/Protocols/Http/http_codec.cc +++ b/ccsrc/Protocols/Http/http_codec.cc @@ -1,684 +1,684 @@ -#include -#include -#include - -#include "codec_stack_builder.hh" - -#include "http_codec.hh" - -#include "loggers.hh" - -#include "LibHttp_TypesAndValues.hh" -#include "LibHttp_MessageBodyTypes.hh" -#include "LibHttp_XmlMessageBodyTypes.hh" -#include "LibHttp_JsonMessageBodyTypes.hh" - -int http_codec::encode(const LibHttp__TypesAndValues::HttpMessage &msg, OCTETSTRING &data) { - loggers::get_instance().log_msg(">>> http_codec::encode: ", (const Base_Type &)msg); - loggers::get_instance().log(">>> http_codec::encode: %p", this); - - TTCN_EncDec::clear_error(); - TTCN_Buffer encoding_buffer; - - _ec.reset(); - - int result; - if (msg.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_request)) { - result = encode_request(msg.request(), encoding_buffer); - } else if (msg.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_response)) { - result = encode_response(msg.response(), encoding_buffer); - } else { - loggers::get_instance().warning("http_codec::encode: Unbound HttpMessage"); - return -1; - } - - data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data()); - - loggers::get_instance().log_msg("<<< http_codec::encode: data=", data); - return result; -} - -int http_codec::decode(const OCTETSTRING &data, LibHttp__TypesAndValues::HttpMessage &msg, params *params) { - loggers::get_instance().log_msg(">>> http_codec::decode: data=", data); - - // Sanity checks - if ((data[0].get_octet() & 0x80) == 0x80) { - loggers::get_instance().warning("http_codec::decode: Unicode format not supported"); - return -1; - } - - TTCN_EncDec::clear_error(); - TTCN_Buffer decoding_buffer(data); - loggers::get_instance().log_to_hexa("http_codec::decode: decoding_buffer=", decoding_buffer); - - _dc.reset(); - - _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) { - loggers::get_instance().warning("http_codec::decode: get_line failed, errno: %d", errno); - return -1; - } - loggers::get_instance().log_msg("http_codec::decode: message_id: ", message_id); - // Extract parameters - try { - std::string str(static_cast(message_id)); - std::regex rgx("\\s*(\\w+)/"); - std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); - std::smatch m = *begin; - loggers::get_instance().log("http_codec::decode: %d - %s", m.size(), m[0].str().c_str()); - if (m[0].str().compare("HTTP/") == 0) { // HTTP response - LibHttp__TypesAndValues::Response response; - std::regex rgx("\\s*HTTP/(\\d+)\\.(\\d+)\\s+(\\d+)\\s+([\\w\\s\\t\\v\\f]+)*"); - std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); - std::smatch m = *begin; - loggers::get_instance().log("http_codec::decode: Process response: %d", m.size()); - if (m.size() != 5) { - loggers::get_instance().warning("http_codec::decode: Unsupported tag"); - return -1; - } - 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()); - LibHttp__TypesAndValues::Headers headers; - std::string content_type; - decode_headers(decoding_buffer, headers, content_type); - response.header() = headers; - loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer); - LibHttp__MessageBodyTypes::HttpMessageBody body; - if (decode_body(decoding_buffer, body, content_type) == -1) { - response.body().set_to_omit(); - } else { - response.body() = OPTIONAL(body); - } - // Add lower layers parameters - loggers::get_instance().log("http_codec::decode: Add lower layers parameters"); - params->log(); - params::const_iterator it = params->find(params::use_ssl); - if (it != params->cend()) { - loggers::get_instance().log("http_codec::receive_data: tls=%s", it->second.c_str()); - response.tls() = (it->second.compare("1") == 0) ? true : false; - } else { - response.tls().set_to_omit(); - } - it = params->find(params::mutual_auth); - if (it != params->cend()) { - loggers::get_instance().log("http_codec::receive_data: mutual_tls=%s", it->second.c_str()); - response.mutual__tls() = (it->second.compare("1") == 0) ? true : false; - } else { - response.mutual__tls().set_to_omit(); - } - msg.response() = response; - } else { // HTTP request - LibHttp__TypesAndValues::Request request; - 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) { - loggers::get_instance().error("http_codec::decode: Unsupported tag"); - return -1; - } - 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()); - LibHttp__TypesAndValues::Headers headers; - std::string content_type; - decode_headers(decoding_buffer, headers, content_type); - request.header() = headers; - OPTIONAL body; - body.set_to_omit(); - if (decode_body(decoding_buffer, body, content_type) == -1) { - request.body().set_to_omit(); - } else { - request.body() = body; - } - msg.request() = request; - } - - loggers::get_instance().log_msg("<<< http_codec::decode: ", (const Base_Type &)msg); - return 0; - } catch (const std::logic_error &e) { - return -1; - } -} - -int http_codec::encode_request(const LibHttp__TypesAndValues::Request &p_request, TTCN_Buffer &p_encoding_buffer) { - loggers::get_instance().log_msg(">>> http_codec::encode_request: ", (const Base_Type &)p_request); - - // 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 excepeted the Content-Length - const LibHttp__TypesAndValues::Headers &headers = p_request.header(); - std::string content_type; - for (int i = 0; i < headers.size_of(); i++) { - const LibHttp__TypesAndValues::Header &header = headers[i]; - loggers::get_instance().log_msg("http_codec::encode_request: Processing header ", header.header__name()); - if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { // Skip it, processed later - loggers::get_instance().log("http_codec::encode_request: Skip it"); - continue; - } else { - p_encoding_buffer.put_cs(header.header__name()); - p_encoding_buffer.put_cs(": "); - const OPTIONAL &o = header.header__value(); - if (o.ispresent()) { - const LibHttp__TypesAndValues::charstring__list &v = dynamic_cast &>(o); - if (v.size_of() > 0) { - loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[0]); - if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding - loggers::get_instance().log("http_codec::encode_request: Storing Content-Type"); - int j = 0; - while (j < v.size_of()) { - content_type += v[j++]; - } // End of 'while' statement - } - 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++]); - } // End of 'while' statement - } - } // else, do not include it - } - p_encoding_buffer.put_cs("\r\n"); - } // End of 'for' statement - - // Encode message body - const OPTIONAL &v = p_request.body(); - OCTETSTRING os; - if (v.ispresent()) { - const LibHttp__MessageBodyTypes::HttpMessageBody &body = static_cast(*v.get_opt_value()); - loggers::get_instance().log_msg("http_codec::encode_request: body: ", body); - if (encode_body(body, os, content_type) == -1) { - loggers::get_instance().warning("http_codec::encode_request: Failed to encode HTTP body"); - _ec.length = 0; - _ec.is_content_length_present = 0x00; - } else { - _ec.length = os.lengthof(); - _ec.is_content_length_present = 0x01; - } - loggers::get_instance().log("http_codec::encode_request: length=%d", _ec.length); - } else { - loggers::get_instance().log("http_codec::encode_request: HTTP body field not present"); - _ec.length = 0; - _ec.is_content_length_present = 0x00; - } - - // Encode Content-Length header - p_encoding_buffer.put_cs("Content-Length: "); - if (_ec.length != 0) { - loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", - static_cast(int2str(_ec.length + 2 /*Stand for the last CRLF*/))); - p_encoding_buffer.put_cs(static_cast(int2str(_ec.length))); - _ec.is_content_length_present = 0x01; - } else { - p_encoding_buffer.put_cs("0"); - _ec.is_content_length_present = 0x00; - } - loggers::get_instance().log("http_codec::encode_request: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); - p_encoding_buffer.put_cs("\r\n"); - - // Add message body - p_encoding_buffer.put_cs("\r\n"); - if (_ec.is_content_length_present == 0x01) { - loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); - p_encoding_buffer.put_os(os); - // p_encoding_buffer.put_cs("\r\n"); - } - - loggers::get_instance().log_to_hexa("<<< http_codec::encode_request: ", p_encoding_buffer); - return 0; -} - -int http_codec::encode_response(const LibHttp__TypesAndValues::Response &p_response, TTCN_Buffer &p_encoding_buffer) { - loggers::get_instance().log_msg(">>> http_codec::encode_response: ", (const Base_Type &)p_response); - - // Encode generic part - p_encoding_buffer.put_cs("HTTP/"); - p_encoding_buffer.put_cs(int2str(p_response.version__major())); - p_encoding_buffer.put_c('.'); - p_encoding_buffer.put_cs(int2str(p_response.version__minor())); - p_encoding_buffer.put_cs(" "); - p_encoding_buffer.put_cs(int2str(p_response.statuscode())); - p_encoding_buffer.put_cs(" "); - if (p_response.statustext().lengthof() != 0) { - p_encoding_buffer.put_cs(p_response.statustext()); - } - p_encoding_buffer.put_cs("\r\n"); - - // Encode headers excepeted the Content-Length - const LibHttp__TypesAndValues::Headers &headers = p_response.header(); - std::string content_type; - for (int i = 0; i < headers.size_of(); i++) { - const LibHttp__TypesAndValues::Header &header = headers[i]; - loggers::get_instance().log_msg("http_codec::encode_response: Processing header ", header.header__name()); - if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { - continue; - } else { - p_encoding_buffer.put_cs(header.header__name()); - p_encoding_buffer.put_cs(": "); - const OPTIONAL &o = header.header__value(); - if (o.ispresent()) { - const LibHttp__TypesAndValues::charstring__list &v = dynamic_cast &>(o); - if (v.size_of() > 0) { - loggers::get_instance().log_msg("http_codec::encode_response: Processing value ", v[0]); - if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding - loggers::get_instance().log("http_codec::encode_response: Storing Content-Type"); - int j = 0; - while (j < v.size_of()) { - content_type += v[j++]; - } // End of 'while' statement - } - 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_response: Processing value ", v[j]); - p_encoding_buffer.put_cs(v[j++]); - j += 1; - } // End of 'while' statement - } - } // else, do not include it - } - p_encoding_buffer.put_cs("\r\n"); - } // End of 'for' statement - - // Encode message body - const OPTIONAL &v = p_response.body(); - OCTETSTRING os; - if (v.ispresent()) { - const LibHttp__MessageBodyTypes::HttpMessageBody &body = static_cast(*v.get_opt_value()); - loggers::get_instance().log_msg("http_codec::encode_response: body: ", body); - if (encode_body(body, os, content_type) == -1) { - _ec.length = 0; - _ec.is_content_length_present = 0x00; - } else { - _ec.length = os.lengthof(); - _ec.is_content_length_present = 0x01; - } - loggers::get_instance().log("http_codec::encode_response: length=%d", _ec.length); - } else { - loggers::get_instance().log("http_codec::encode_response: HTTP body field not present"); - _ec.length = 0; - _ec.is_content_length_present = 0x00; - } - - // Encode Content-Length header - p_encoding_buffer.put_cs("Content-Length: "); - if (_ec.length != 0) { - loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", - static_cast(int2str(_ec.length + 2 /*Stand for the last CRLF*/))); - p_encoding_buffer.put_cs(static_cast(int2str(_ec.length))); - _ec.is_content_length_present = 0x01; - } else { - p_encoding_buffer.put_cs("0"); - _ec.is_content_length_present = 0x00; - } - loggers::get_instance().log("http_codec::encode_request: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); - p_encoding_buffer.put_cs("\r\n"); - - // Add message body - p_encoding_buffer.put_cs("\r\n"); - if (_ec.is_content_length_present == 0x01) { - loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); - p_encoding_buffer.put_os(os); - // p_encoding_buffer.put_cs("\r\n"); - } - - loggers::get_instance().log_to_hexa("<<< http_codec::encode_response: ", p_encoding_buffer); - return 0; -} - -int http_codec::decode_headers(TTCN_Buffer &decoding_buffer, LibHttp__TypesAndValues::Headers &headers, std::string &p_content_type) { - loggers::get_instance().log(">>> http_codec::decode_headers"); - loggers::get_instance().log_to_hexa("http_codec::decode_headers: ", decoding_buffer); - - CHARSTRING cstr; - int i = 0; - while (true) { - switch (get_line(decoding_buffer, cstr, true)) { - case 0: { - loggers::get_instance().log_msg("http_codec::decode_headers: ", cstr); - LibHttp__TypesAndValues::Header header; - if (decode_header(cstr, header) == -1) { - loggers::get_instance().warning("http_codec::decode_headers: Failed to decode header %s", static_cast(cstr)); - return -1; - } - headers[i++] = header; - if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { - if (header.header__value().is_present() != 0) { - const PreGenRecordOf::PREGEN__RECORD__OF__CHARSTRING &l = - static_cast(*header.header__value().get_opt_value()); - p_content_type = static_cast(l[0]); - } else { - p_content_type = ""; - } - } - } break; - case 1: - if (headers.is_bound()) { - loggers::get_instance().log_msg("<<< http_codec::decode_headers: ", headers); - return 0; - } else { - loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); - return -1; - } - case -1: - loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); - return -1; - } // End of 'switch' statement - } // End of 'while' statement -} - -int http_codec::decode_header(CHARSTRING &header_line, LibHttp__TypesAndValues::Header &header) { - loggers::get_instance().log_msg(">>> http_codec::decode_header", header_line); - - try { - std::string str(static_cast(header_line)); - std::regex rgx("([0-9a-zA-Z-]+)\\:\\s+(.+)(,(.+))*"); - std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); - std::smatch m = *begin; - if (m.size() < 5) { - loggers::get_instance().warning("http_codec::decode_header: Failed to decode header %s", str.c_str()); - return -1; - } - loggers::get_instance().log("http_codec::decode_header: %d", m.size()); - header.header__name() = CHARSTRING(m[1].str().c_str()); - LibHttp__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 - header.header__value() = OPTIONAL(v); - - if (m[1].str().compare("Content-Length") == 0) { - // Save the the body length - loggers::get_instance().log("http_codec::decode_header: decoded Content-Length %s", m[2].str().c_str()); - _dc.length = std::stoi(m[2].str()); - } else if (m[1].str().compare("Transfer-Encoding") == 0) { - if (m[2].str().find("chunked") != std::string::npos) { - _dc.chunked = true; - loggers::get_instance().log("http_codec::decode_header: decoded Transfer-Encoding %x", _dc.chunked); - } - } - - return 0; - } catch (const std::logic_error &e) { - return -1; - } -} - -int http_codec::encode_body(const LibHttp__MessageBodyTypes::HttpMessageBody &p_message_body, OCTETSTRING &p_encoding_buffer, - const std::string &p_content_type) { - loggers::get_instance().log_msg(">>> http_codec::encode_body: ", (const Base_Type &)p_message_body); - - // Sanity check - if (p_content_type.empty()) { - loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); - return -1; - } - - if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_binary__body)) { - const LibHttp__BinaryMessageBodyTypes::BinaryBody &binary_body = p_message_body.binary__body(); - if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { - p_encoding_buffer = OCTETSTRING(binary_body.raw().lengthof(), (unsigned char *)static_cast(binary_body.raw())); - } else { - encode_body_binary(binary_body, p_encoding_buffer, p_content_type); - } - } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_json__body)) { - const LibHttp__JsonMessageBodyTypes::JsonBody& json_body = p_message_body.json__body(); - if (json_body.ischosen(LibHttp__JsonMessageBodyTypes::JsonBody::ALT_raw)) { - p_encoding_buffer = unichar2oct(json_body.raw()); - } else { - encode_body_json(json_body, p_encoding_buffer, p_content_type); - } - } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)) { - const LibHttp__XmlMessageBodyTypes::XmlBody &xml_body = p_message_body.xml__body(); - if (xml_body.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_raw)) { - p_encoding_buffer = OCTETSTRING(xml_body.raw().lengthof(), (unsigned char *)static_cast(xml_body.raw())); - } else { - encode_body_xml(xml_body, p_encoding_buffer, p_content_type); - } - } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { - p_encoding_buffer = OCTETSTRING(p_message_body.html__body().lengthof(), (unsigned char *)static_cast(p_message_body.html__body())); - } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { - p_encoding_buffer = OCTETSTRING(p_message_body.text__body().lengthof(), (unsigned char *)static_cast(p_message_body.text__body())); - } else { - loggers::get_instance().warning("http_codec::encode_body: Failed to encode HTTP message body"); - return -1; - } - loggers::get_instance().log_msg("http_codec::encode_body: HTTP message ", p_encoding_buffer); - _ec.length = p_encoding_buffer.lengthof(); - loggers::get_instance().log("http_codec::encode_body: HTTP message length: %d", _ec.length); - - return 0; -} - -int http_codec::decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTypes::HttpMessageBody &message_body, const std::string &p_content_type) { - loggers::get_instance().log(">>> http_codec::decode_body"); - loggers::get_instance().log_to_hexa("http_codec::decode_body", decoding_buffer); - loggers::get_instance().log("http_codec::decode_body: # of codecs=%d - %p", _codecs.size(), this); - loggers::get_instance().log("http_codec::decode_body: Content-Type=%s", p_content_type.c_str()); - - // Sanity checks - if (decoding_buffer.get_len() - decoding_buffer.get_pos() <= 0) { - return -1; - } - /* TODO Uncomment if (p_content_type.empty()) { - loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); - return -1; - }*/ - - OCTETSTRING s(decoding_buffer.get_len() - decoding_buffer.get_pos(), decoding_buffer.get_data() + decoding_buffer.get_pos()); - loggers::get_instance().log_msg("http_codec::decode_body: raw body=", s); - - // Align the payload length with the specified Content-Lenght value - loggers::get_instance().log("http_codec::decode_body: _dc.length=%d - body length=%d", _dc.length, s.lengthof()); - OCTETSTRING body; - if (_dc.length != 0) { - const unsigned char *p = static_cast(s); - if ((unsigned int)s.lengthof() <= _dc.length) { - body = OCTETSTRING(s.lengthof(), p); - } else { - body = OCTETSTRING(_dc.length, p); - } - } else { - loggers::get_instance().warning("http_codec::decode_body: No Conten-Length header, process all remaining bytes"); - body = s; - } - loggers::get_instance().log_msg("http_codec::decode_body: Aligned body=", body); - loggers::get_instance().log("http_codec::decode_body: body length=%d", body.lengthof()); - /* TODO To be removed - // Remove CRLF if any - int counter = 0; - if ((body[body.lengthof() - 1].get_octet() == 0x0d) || (body[body.lengthof() - 1].get_octet() == 0x0a)) { - counter += 1; - if ((body[body.lengthof() - 2].get_octet() == 0x0d) || (body[body.lengthof() - 2].get_octet() == 0x0a)) { - counter += 1; - } - } - loggers::get_instance().log("http_codec::decode_body: counter=%d", counter); - body = OCTETSTRING(body.lengthof() - counter, static_cast(body)); - */ - if (_dc.chunked) { - int counter = 0; - int prev = 0; - OCTETSTRING os(0, nullptr); - do { - while (counter < body.lengthof()) { // Extract the size of the chunk \r[\n] - if ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n')) { - break; - } - counter += 1; - } // End of 'while' statement - loggers::get_instance().log("http_codec::decode_body: Chunked(0): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); - if (counter < body.lengthof()) { - int idx = counter - prev; - OCTETSTRING trunk(idx, static_cast(body)); - loggers::get_instance().log_msg("http_codec::decode_body: trunk: ", trunk); - std::string str((const char *)static_cast(trunk), idx); - loggers::get_instance().log("http_codec::decode_body: str: '%s'", str.c_str()); - int len = std::stoi(str, nullptr, 16); // converter::get_instance().string_to_int(str); - loggers::get_instance().log("http_codec::decode_body: Chunk len: %d", len); - while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n - counter += 1; - } // End of 'while' statement - if (counter < body.lengthof()) { - loggers::get_instance().log("http_codec::decode_body: Chunked (1): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); - os += OCTETSTRING(len, counter + static_cast(body)); - loggers::get_instance().log_msg("http_codec::decode_body: os=", os); - counter += len; - loggers::get_instance().log("http_codec::decode_body: Chunked: %02x %02x %02x", body[counter].get_octet(), body[counter + 1].get_octet(), - body[counter + 2].get_octet()); - loggers::get_instance().log("http_codec::decode_body: Chunked (2): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); - while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n - counter += 1; - } // End of 'while' statement - prev = counter; - loggers::get_instance().log("http_codec::decode_body: Chunked (3): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); - } - } - } while (counter < body.lengthof()); // Process next chunk if any - body = os; - loggers::get_instance().log_msg("http_codec::decode_body: Finalised body=", body); - } - // Check if HTTP message body contains binary characters - for (int i = 0; i < body.lengthof(); i++) { - unsigned char c = body[i].get_octet(); - if (!std::isprint(c) && !std::isspace(c) && !std::ispunct(c)) { - loggers::get_instance().log("http_codec::decode_body: Byte #%d is not printable: 0x%02x", i, body[i].get_octet()); - _dc.is_binary = 0x01; - break; - } - } // End of 'for' statement - loggers::get_instance().log("http_codec::decode_body: Binary mode: %x", _dc.is_binary); - LibHttp__MessageBodyTypes::HttpMessageBody v; - if (_dc.is_binary == 0x01) { - LibHttp__BinaryMessageBodyTypes::BinaryBody binary_body; - decode_body_binary(body, binary_body, p_content_type); - message_body.binary__body() = binary_body; - } else { - // Convert into string - params p; - p["decode_str"] = std::string(static_cast(body), body.lengthof() + static_cast(body)); - loggers::get_instance().log("http_codec::decode_body: decode_str: %s", p["decode_str"].c_str()); - // Try to identify xml - if (p["decode_str"].find("") != std::string::npos) { // Try to identify HTML - loggers::get_instance().log("http_codec::decode_body: Find html message"); - LibHttp__MessageBodyTypes::HtmlBody html_body; - decode_body_html(body, html_body, p_content_type, &p); - message_body.html__body() = html_body; - } else if (p_content_type.find("json") != std::string::npos) { // Try to identify JSON - loggers::get_instance().log("http_codec::decode_body: Find json message"); - LibHttp__JsonMessageBodyTypes::JsonBody json_body; - decode_body_json(body, json_body, p_content_type, &p); - message_body.json__body() = json_body; - } else { - loggers::get_instance().log("http_codec::decode_body: Use textBody as default"); - LibHttp__MessageBodyTypes::TextBody text_body; - message_body.text__body() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); - } - } - - return 0; -} - -int http_codec::get_line(TTCN_Buffer &buffer, CHARSTRING &to, const bool concatenate_header_lines) { - 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) { - // Skip spaces, and empty lines - 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 - 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; - } - } - } // End of 'while' statement -} - -void http_codec::set_payload_codecs(const std::string &p_codecs) { - loggers::get_instance().log(">>> http_codec::set_payload_codecs: %s", p_codecs.c_str()); - - // Sanity check - if (p_codecs.length() == 0) { - return; - } - - // Extract codecs - try { - std::regex rgx("(\\w+):(\\w+)(;(\\w+):(\\w+))*"); - std::sregex_iterator begin(p_codecs.cbegin(), p_codecs.cend(), rgx); - std::sregex_iterator end = std::sregex_iterator(); - // E.g. 9 - xml - :held_codec - held_codec - ;html:html_codec - html:html_codec - html - :html_codec - html_codec - for (std::sregex_iterator it = begin; it != end; ++it) { - std::smatch m = *it; - loggers::get_instance().log("http_codec::set_payload_codecs: %d - %s - %s - %s - %s - %s - %s - %s - %s", m.size(), m[1].str().c_str(), - m[2].str().c_str(), m[3].str().c_str(), m[4].str().c_str(), m[5].str().c_str(), m[6].str().c_str(), m[7].str().c_str(), - m[8].str().c_str()); - for (unsigned int j = 1; j < m.size() - 1; j += 3) { // Exclude m[0] - loggers::get_instance().log("http_codec::set_payload_codecs: insert (%s, %s), j = %d", m[j].str().c_str(), m[j + 1].str().c_str(), j); - if (m[j].str().empty()) { - break; - } - std::string key(m[j].str()); - loggers::get_instance().log("http_codec::set_payload_codecs: Add codec %s", key.c_str()); - _codecs.insert( - std::make_pair(key, std::unique_ptr>(codec_stack_builder::get_instance()->get_codec(m[j + 1].str().c_str())))); - } // End of 'for' statement - } // End of 'for' statement - loggers::get_instance().log("http_codec::set_payload_codecs: _codecs length=%d - %p", _codecs.size(), this); - } catch (const std::logic_error &e) { - loggers::get_instance().warning("http_codec::set_payload_codecs: std::logic_error: %s", e.what()); - _codecs.clear(); - } -} - -bool http_codec::decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string &p_content_type, params* p_params) { - p_html_body = CHARSTRING(p_data.lengthof(), (char*)static_cast(p_data)); - return true; -} +#include +#include +#include + +#include "codec_stack_builder.hh" + +#include "http_codec.hh" + +#include "loggers.hh" + +#include "LibHttp_TypesAndValues.hh" +#include "LibHttp_MessageBodyTypes.hh" +#include "LibHttp_XmlMessageBodyTypes.hh" +#include "LibHttp_JsonMessageBodyTypes.hh" + +int http_codec::encode(const LibHttp__TypesAndValues::HttpMessage &msg, OCTETSTRING &data) { + loggers::get_instance().log_msg(">>> http_codec::encode: ", (const Base_Type &)msg); + loggers::get_instance().log(">>> http_codec::encode: %p", this); + + TTCN_EncDec::clear_error(); + TTCN_Buffer encoding_buffer; + + _ec.reset(); + + int result; + if (msg.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_request)) { + result = encode_request(msg.request(), encoding_buffer); + } else if (msg.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_response)) { + result = encode_response(msg.response(), encoding_buffer); + } else { + loggers::get_instance().warning("http_codec::encode: Unbound HttpMessage"); + return -1; + } + + data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data()); + + loggers::get_instance().log_msg("<<< http_codec::encode: data=", data); + return result; +} + +int http_codec::decode(const OCTETSTRING &data, LibHttp__TypesAndValues::HttpMessage &msg, params *params) { + loggers::get_instance().log_msg(">>> http_codec::decode: data=", data); + + // Sanity checks + if ((data[0].get_octet() & 0x80) == 0x80) { + loggers::get_instance().warning("http_codec::decode: Unicode format not supported"); + return -1; + } + + TTCN_EncDec::clear_error(); + TTCN_Buffer decoding_buffer(data); + loggers::get_instance().log_to_hexa("http_codec::decode: decoding_buffer=", decoding_buffer); + + _dc.reset(); + + _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) { + loggers::get_instance().warning("http_codec::decode: get_line failed, errno: %d", errno); + return -1; + } + loggers::get_instance().log_msg("http_codec::decode: message_id: ", message_id); + // Extract parameters + try { + std::string str(static_cast(message_id)); + std::regex rgx("\\s*(\\w+)/"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + loggers::get_instance().log("http_codec::decode: %d - %s", m.size(), m[0].str().c_str()); + if (m[0].str().compare("HTTP/") == 0) { // HTTP response + LibHttp__TypesAndValues::Response response; + std::regex rgx("\\s*HTTP/(\\d+)\\.(\\d+)\\s+(\\d+)\\s+([\\w\\s\\t\\v\\f]+)*"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + loggers::get_instance().log("http_codec::decode: Process response: %d", m.size()); + if (m.size() != 5) { + loggers::get_instance().warning("http_codec::decode: Unsupported tag"); + return -1; + } + 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()); + LibHttp__TypesAndValues::Headers headers; + std::string content_type; + decode_headers(decoding_buffer, headers, content_type); + response.header() = headers; + loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer); + LibHttp__MessageBodyTypes::HttpMessageBody body; + if (decode_body(decoding_buffer, body, content_type) == -1) { + response.body().set_to_omit(); + } else { + response.body() = OPTIONAL(body); + } + // Add lower layers parameters + loggers::get_instance().log("http_codec::decode: Add lower layers parameters"); + params->log(); + params::const_iterator it = params->find(params::use_ssl); + if (it != params->cend()) { + loggers::get_instance().log("http_codec::receive_data: tls=%s", it->second.c_str()); + response.tls() = (it->second.compare("1") == 0) ? true : false; + } else { + response.tls().set_to_omit(); + } + it = params->find(params::mutual_auth); + if (it != params->cend()) { + loggers::get_instance().log("http_codec::receive_data: mutual_tls=%s", it->second.c_str()); + response.mutual__tls() = (it->second.compare("1") == 0) ? true : false; + } else { + response.mutual__tls().set_to_omit(); + } + msg.response() = response; + } else { // HTTP request + LibHttp__TypesAndValues::Request request; + 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) { + loggers::get_instance().error("http_codec::decode: Unsupported tag"); + return -1; + } + 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()); + LibHttp__TypesAndValues::Headers headers; + std::string content_type; + decode_headers(decoding_buffer, headers, content_type); + request.header() = headers; + OPTIONAL body; + body.set_to_omit(); + if (decode_body(decoding_buffer, body, content_type) == -1) { + request.body().set_to_omit(); + } else { + request.body() = body; + } + msg.request() = request; + } + + loggers::get_instance().log_msg("<<< http_codec::decode: ", (const Base_Type &)msg); + return 0; + } catch (const std::logic_error &e) { + return -1; + } +} + +int http_codec::encode_request(const LibHttp__TypesAndValues::Request &p_request, TTCN_Buffer &p_encoding_buffer) { + loggers::get_instance().log_msg(">>> http_codec::encode_request: ", (const Base_Type &)p_request); + + // 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 excepeted the Content-Length + const LibHttp__TypesAndValues::Headers &headers = p_request.header(); + std::string content_type; + for (int i = 0; i < headers.size_of(); i++) { + const LibHttp__TypesAndValues::Header &header = headers[i]; + loggers::get_instance().log_msg("http_codec::encode_request: Processing header ", header.header__name()); + if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { // Skip it, processed later + loggers::get_instance().log("http_codec::encode_request: Skip it"); + continue; + } else { + p_encoding_buffer.put_cs(header.header__name()); + p_encoding_buffer.put_cs(": "); + const OPTIONAL &o = header.header__value(); + if (o.ispresent()) { + const LibHttp__TypesAndValues::charstring__list &v = dynamic_cast &>(o); + if (v.size_of() > 0) { + loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[0]); + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding + loggers::get_instance().log("http_codec::encode_request: Storing Content-Type"); + int j = 0; + while (j < v.size_of()) { + content_type += v[j++]; + } // End of 'while' statement + } + 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++]); + } // End of 'while' statement + } + } // else, do not include it + } + p_encoding_buffer.put_cs("\r\n"); + } // End of 'for' statement + + // Encode message body + const OPTIONAL &v = p_request.body(); + OCTETSTRING os; + if (v.ispresent()) { + const LibHttp__MessageBodyTypes::HttpMessageBody &body = static_cast(*v.get_opt_value()); + loggers::get_instance().log_msg("http_codec::encode_request: body: ", body); + if (encode_body(body, os, content_type) == -1) { + loggers::get_instance().warning("http_codec::encode_request: Failed to encode HTTP body"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } else { + _ec.length = os.lengthof(); + _ec.is_content_length_present = 0x01; + } + loggers::get_instance().log("http_codec::encode_request: length=%d", _ec.length); + } else { + loggers::get_instance().log("http_codec::encode_request: HTTP body field not present"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } + + // Encode Content-Length header + p_encoding_buffer.put_cs("Content-Length: "); + if (_ec.length != 0) { + loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", + static_cast(int2str(_ec.length + 2 /*Stand for the last CRLF*/))); + p_encoding_buffer.put_cs(static_cast(int2str(_ec.length))); + _ec.is_content_length_present = 0x01; + } else { + p_encoding_buffer.put_cs("0"); + _ec.is_content_length_present = 0x00; + } + loggers::get_instance().log("http_codec::encode_request: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); + p_encoding_buffer.put_cs("\r\n"); + + // Add message body + p_encoding_buffer.put_cs("\r\n"); + if (_ec.is_content_length_present == 0x01) { + loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); + p_encoding_buffer.put_os(os); + // p_encoding_buffer.put_cs("\r\n"); + } + + loggers::get_instance().log_to_hexa("<<< http_codec::encode_request: ", p_encoding_buffer); + return 0; +} + +int http_codec::encode_response(const LibHttp__TypesAndValues::Response &p_response, TTCN_Buffer &p_encoding_buffer) { + loggers::get_instance().log_msg(">>> http_codec::encode_response: ", (const Base_Type &)p_response); + + // Encode generic part + p_encoding_buffer.put_cs("HTTP/"); + p_encoding_buffer.put_cs(int2str(p_response.version__major())); + p_encoding_buffer.put_c('.'); + p_encoding_buffer.put_cs(int2str(p_response.version__minor())); + p_encoding_buffer.put_cs(" "); + p_encoding_buffer.put_cs(int2str(p_response.statuscode())); + p_encoding_buffer.put_cs(" "); + if (p_response.statustext().lengthof() != 0) { + p_encoding_buffer.put_cs(p_response.statustext()); + } + p_encoding_buffer.put_cs("\r\n"); + + // Encode headers excepeted the Content-Length + const LibHttp__TypesAndValues::Headers &headers = p_response.header(); + std::string content_type; + for (int i = 0; i < headers.size_of(); i++) { + const LibHttp__TypesAndValues::Header &header = headers[i]; + loggers::get_instance().log_msg("http_codec::encode_response: Processing header ", header.header__name()); + if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { + continue; + } else { + p_encoding_buffer.put_cs(header.header__name()); + p_encoding_buffer.put_cs(": "); + const OPTIONAL &o = header.header__value(); + if (o.ispresent()) { + const LibHttp__TypesAndValues::charstring__list &v = dynamic_cast &>(o); + if (v.size_of() > 0) { + loggers::get_instance().log_msg("http_codec::encode_response: Processing value ", v[0]); + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding + loggers::get_instance().log("http_codec::encode_response: Storing Content-Type"); + int j = 0; + while (j < v.size_of()) { + content_type += v[j++]; + } // End of 'while' statement + } + 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_response: Processing value ", v[j]); + p_encoding_buffer.put_cs(v[j++]); + j += 1; + } // End of 'while' statement + } + } // else, do not include it + } + p_encoding_buffer.put_cs("\r\n"); + } // End of 'for' statement + + // Encode message body + const OPTIONAL &v = p_response.body(); + OCTETSTRING os; + if (v.ispresent()) { + const LibHttp__MessageBodyTypes::HttpMessageBody &body = static_cast(*v.get_opt_value()); + loggers::get_instance().log_msg("http_codec::encode_response: body: ", body); + if (encode_body(body, os, content_type) == -1) { + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } else { + _ec.length = os.lengthof(); + _ec.is_content_length_present = 0x01; + } + loggers::get_instance().log("http_codec::encode_response: length=%d", _ec.length); + } else { + loggers::get_instance().log("http_codec::encode_response: HTTP body field not present"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } + + // Encode Content-Length header + p_encoding_buffer.put_cs("Content-Length: "); + if (_ec.length != 0) { + loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", + static_cast(int2str(_ec.length + 2 /*Stand for the last CRLF*/))); + p_encoding_buffer.put_cs(static_cast(int2str(_ec.length))); + _ec.is_content_length_present = 0x01; + } else { + p_encoding_buffer.put_cs("0"); + _ec.is_content_length_present = 0x00; + } + loggers::get_instance().log("http_codec::encode_request: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); + p_encoding_buffer.put_cs("\r\n"); + + // Add message body + p_encoding_buffer.put_cs("\r\n"); + if (_ec.is_content_length_present == 0x01) { + loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); + p_encoding_buffer.put_os(os); + // p_encoding_buffer.put_cs("\r\n"); + } + + loggers::get_instance().log_to_hexa("<<< http_codec::encode_response: ", p_encoding_buffer); + return 0; +} + +int http_codec::decode_headers(TTCN_Buffer &decoding_buffer, LibHttp__TypesAndValues::Headers &headers, std::string &p_content_type) { + loggers::get_instance().log(">>> http_codec::decode_headers"); + loggers::get_instance().log_to_hexa("http_codec::decode_headers: ", decoding_buffer); + + CHARSTRING cstr; + int i = 0; + while (true) { + switch (get_line(decoding_buffer, cstr, true)) { + case 0: { + loggers::get_instance().log_msg("http_codec::decode_headers: ", cstr); + LibHttp__TypesAndValues::Header header; + if (decode_header(cstr, header) == -1) { + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode header %s", static_cast(cstr)); + return -1; + } + headers[i++] = header; + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { + if (header.header__value().is_present() != 0) { + const PreGenRecordOf::PREGEN__RECORD__OF__CHARSTRING &l = + static_cast(*header.header__value().get_opt_value()); + p_content_type = static_cast(l[0]); + } else { + p_content_type = ""; + } + } + } break; + case 1: + if (headers.is_bound()) { + loggers::get_instance().log_msg("<<< http_codec::decode_headers: ", headers); + return 0; + } else { + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); + return -1; + } + case -1: + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); + return -1; + } // End of 'switch' statement + } // End of 'while' statement +} + +int http_codec::decode_header(CHARSTRING &header_line, LibHttp__TypesAndValues::Header &header) { + loggers::get_instance().log_msg(">>> http_codec::decode_header", header_line); + + try { + std::string str(static_cast(header_line)); + std::regex rgx("([0-9a-zA-Z-]+)\\:\\s+(.+)(,(.+))*"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + if (m.size() < 5) { + loggers::get_instance().warning("http_codec::decode_header: Failed to decode header %s", str.c_str()); + return -1; + } + loggers::get_instance().log("http_codec::decode_header: %d", m.size()); + header.header__name() = CHARSTRING(m[1].str().c_str()); + LibHttp__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 + header.header__value() = OPTIONAL(v); + + if (m[1].str().compare("Content-Length") == 0) { + // Save the the body length + loggers::get_instance().log("http_codec::decode_header: decoded Content-Length %s", m[2].str().c_str()); + _dc.length = std::stoi(m[2].str()); + } else if (m[1].str().compare("Transfer-Encoding") == 0) { + if (m[2].str().find("chunked") != std::string::npos) { + _dc.chunked = true; + loggers::get_instance().log("http_codec::decode_header: decoded Transfer-Encoding %x", _dc.chunked); + } + } + + return 0; + } catch (const std::logic_error &e) { + return -1; + } +} + +int http_codec::encode_body(const LibHttp__MessageBodyTypes::HttpMessageBody &p_message_body, OCTETSTRING &p_encoding_buffer, + const std::string &p_content_type) { + loggers::get_instance().log_msg(">>> http_codec::encode_body: ", (const Base_Type &)p_message_body); + + // Sanity check + if (p_content_type.empty()) { + loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); + return -1; + } + + if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_binary__body)) { + const LibHttp__BinaryMessageBodyTypes::BinaryBody &binary_body = p_message_body.binary__body(); + if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { + p_encoding_buffer = OCTETSTRING(binary_body.raw().lengthof(), (unsigned char *)static_cast(binary_body.raw())); + } else { + encode_body_binary(binary_body, p_encoding_buffer, p_content_type); + } + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_json__body)) { + const LibHttp__JsonMessageBodyTypes::JsonBody& json_body = p_message_body.json__body(); + if (json_body.ischosen(LibHttp__JsonMessageBodyTypes::JsonBody::ALT_raw)) { + p_encoding_buffer = unichar2oct(json_body.raw()); + } else { + encode_body_json(json_body, p_encoding_buffer, p_content_type); + } + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)) { + const LibHttp__XmlMessageBodyTypes::XmlBody &xml_body = p_message_body.xml__body(); + if (xml_body.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_raw)) { + p_encoding_buffer = OCTETSTRING(xml_body.raw().lengthof(), (unsigned char *)static_cast(xml_body.raw())); + } else { + encode_body_xml(xml_body, p_encoding_buffer, p_content_type); + } + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.html__body().lengthof(), (unsigned char *)static_cast(p_message_body.html__body())); + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.text__body().lengthof(), (unsigned char *)static_cast(p_message_body.text__body())); + } else { + loggers::get_instance().warning("http_codec::encode_body: Failed to encode HTTP message body"); + return -1; + } + loggers::get_instance().log_msg("http_codec::encode_body: HTTP message ", p_encoding_buffer); + _ec.length = p_encoding_buffer.lengthof(); + loggers::get_instance().log("http_codec::encode_body: HTTP message length: %d", _ec.length); + + return 0; +} + +int http_codec::decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTypes::HttpMessageBody &message_body, const std::string &p_content_type) { + loggers::get_instance().log(">>> http_codec::decode_body"); + loggers::get_instance().log_to_hexa("http_codec::decode_body", decoding_buffer); + loggers::get_instance().log("http_codec::decode_body: # of codecs=%d - %p", _codecs.size(), this); + loggers::get_instance().log("http_codec::decode_body: Content-Type=%s", p_content_type.c_str()); + + // Sanity checks + if (decoding_buffer.get_len() - decoding_buffer.get_pos() <= 0) { + return -1; + } + /* TODO Uncomment if (p_content_type.empty()) { + loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); + return -1; + }*/ + + OCTETSTRING s(decoding_buffer.get_len() - decoding_buffer.get_pos(), decoding_buffer.get_data() + decoding_buffer.get_pos()); + loggers::get_instance().log_msg("http_codec::decode_body: raw body=", s); + + // Align the payload length with the specified Content-Lenght value + loggers::get_instance().log("http_codec::decode_body: _dc.length=%d - body length=%d", _dc.length, s.lengthof()); + OCTETSTRING body; + if (_dc.length != 0) { + const unsigned char *p = static_cast(s); + if ((unsigned int)s.lengthof() <= _dc.length) { + body = OCTETSTRING(s.lengthof(), p); + } else { + body = OCTETSTRING(_dc.length, p); + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No Conten-Length header, process all remaining bytes"); + body = s; + } + loggers::get_instance().log_msg("http_codec::decode_body: Aligned body=", body); + loggers::get_instance().log("http_codec::decode_body: body length=%d", body.lengthof()); + /* TODO To be removed + // Remove CRLF if any + int counter = 0; + if ((body[body.lengthof() - 1].get_octet() == 0x0d) || (body[body.lengthof() - 1].get_octet() == 0x0a)) { + counter += 1; + if ((body[body.lengthof() - 2].get_octet() == 0x0d) || (body[body.lengthof() - 2].get_octet() == 0x0a)) { + counter += 1; + } + } + loggers::get_instance().log("http_codec::decode_body: counter=%d", counter); + body = OCTETSTRING(body.lengthof() - counter, static_cast(body)); + */ + if (_dc.chunked) { + int counter = 0; + int prev = 0; + OCTETSTRING os(0, nullptr); + do { + while (counter < body.lengthof()) { // Extract the size of the chunk \r[\n] + if ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n')) { + break; + } + counter += 1; + } // End of 'while' statement + loggers::get_instance().log("http_codec::decode_body: Chunked(0): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + if (counter < body.lengthof()) { + int idx = counter - prev; + OCTETSTRING trunk(idx, static_cast(body)); + loggers::get_instance().log_msg("http_codec::decode_body: trunk: ", trunk); + std::string str((const char *)static_cast(trunk), idx); + loggers::get_instance().log("http_codec::decode_body: str: '%s'", str.c_str()); + int len = std::stoi(str, nullptr, 16); // converter::get_instance().string_to_int(str); + loggers::get_instance().log("http_codec::decode_body: Chunk len: %d", len); + while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n + counter += 1; + } // End of 'while' statement + if (counter < body.lengthof()) { + loggers::get_instance().log("http_codec::decode_body: Chunked (1): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + os += OCTETSTRING(len, counter + static_cast(body)); + loggers::get_instance().log_msg("http_codec::decode_body: os=", os); + counter += len; + loggers::get_instance().log("http_codec::decode_body: Chunked: %02x %02x %02x", body[counter].get_octet(), body[counter + 1].get_octet(), + body[counter + 2].get_octet()); + loggers::get_instance().log("http_codec::decode_body: Chunked (2): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n + counter += 1; + } // End of 'while' statement + prev = counter; + loggers::get_instance().log("http_codec::decode_body: Chunked (3): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + } + } + } while (counter < body.lengthof()); // Process next chunk if any + body = os; + loggers::get_instance().log_msg("http_codec::decode_body: Finalised body=", body); + } + // Check if HTTP message body contains binary characters + for (int i = 0; i < body.lengthof(); i++) { + unsigned char c = body[i].get_octet(); + if (!std::isprint(c) && !std::isspace(c) && !std::ispunct(c)) { + loggers::get_instance().log("http_codec::decode_body: Byte #%d is not printable: 0x%02x", i, body[i].get_octet()); + _dc.is_binary = 0x01; + break; + } + } // End of 'for' statement + loggers::get_instance().log("http_codec::decode_body: Binary mode: %x", _dc.is_binary); + LibHttp__MessageBodyTypes::HttpMessageBody v; + if (_dc.is_binary == 0x01) { + LibHttp__BinaryMessageBodyTypes::BinaryBody binary_body; + decode_body_binary(body, binary_body, p_content_type); + message_body.binary__body() = binary_body; + } else { + // Convert into string + params p; + p["decode_str"] = std::string(static_cast(body), body.lengthof() + static_cast(body)); + loggers::get_instance().log("http_codec::decode_body: decode_str: %s", p["decode_str"].c_str()); + // Try to identify xml + if (p["decode_str"].find("") != std::string::npos) { // Try to identify HTML + loggers::get_instance().log("http_codec::decode_body: Find html message"); + LibHttp__MessageBodyTypes::HtmlBody html_body; + decode_body_html(body, html_body, p_content_type, &p); + message_body.html__body() = html_body; + } else if (p_content_type.find("json") != std::string::npos) { // Try to identify JSON + loggers::get_instance().log("http_codec::decode_body: Find json message"); + LibHttp__JsonMessageBodyTypes::JsonBody json_body; + decode_body_json(body, json_body, p_content_type, &p); + message_body.json__body() = json_body; + } else { + loggers::get_instance().log("http_codec::decode_body: Use textBody as default"); + LibHttp__MessageBodyTypes::TextBody text_body; + message_body.text__body() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } + } + + return 0; +} + +int http_codec::get_line(TTCN_Buffer &buffer, CHARSTRING &to, const bool concatenate_header_lines) { + 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) { + // Skip spaces, and empty lines + 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 + 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; + } + } + } // End of 'while' statement +} + +void http_codec::set_payload_codecs(const std::string &p_codecs) { + loggers::get_instance().log(">>> http_codec::set_payload_codecs: %s", p_codecs.c_str()); + + // Sanity check + if (p_codecs.length() == 0) { + return; + } + + // Extract codecs + try { + std::regex rgx("(\\w+):(\\w+)(;(\\w+):(\\w+))*"); + std::sregex_iterator begin(p_codecs.cbegin(), p_codecs.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + // E.g. 9 - xml - :held_codec - held_codec - ;html:html_codec - html:html_codec - html - :html_codec - html_codec + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("http_codec::set_payload_codecs: %d - %s - %s - %s - %s - %s - %s - %s - %s", m.size(), m[1].str().c_str(), + m[2].str().c_str(), m[3].str().c_str(), m[4].str().c_str(), m[5].str().c_str(), m[6].str().c_str(), m[7].str().c_str(), + m[8].str().c_str()); + for (unsigned int j = 1; j < m.size() - 1; j += 3) { // Exclude m[0] + loggers::get_instance().log("http_codec::set_payload_codecs: insert (%s, %s), j = %d", m[j].str().c_str(), m[j + 1].str().c_str(), j); + if (m[j].str().empty()) { + break; + } + std::string key(m[j].str()); + loggers::get_instance().log("http_codec::set_payload_codecs: Add codec %s", key.c_str()); + _codecs.insert( + std::make_pair(key, std::unique_ptr>(codec_stack_builder::get_instance()->get_codec(m[j + 1].str().c_str())))); + } // End of 'for' statement + } // End of 'for' statement + loggers::get_instance().log("http_codec::set_payload_codecs: _codecs length=%d - %p", _codecs.size(), this); + } catch (const std::logic_error &e) { + loggers::get_instance().warning("http_codec::set_payload_codecs: std::logic_error: %s", e.what()); + _codecs.clear(); + } +} + +bool http_codec::decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string &p_content_type, params* p_params) { + p_html_body = CHARSTRING(p_data.lengthof(), (char*)static_cast(p_data)); + return true; +} diff --git a/ccsrc/Protocols/Xml/include/xml_converters.hh b/ccsrc/Protocols/Xml/include/xml_converters.hh new file mode 100644 index 0000000000000000000000000000000000000000..97710742a539e1d352f5552b9d4539e57a0b3ba2 --- /dev/null +++ b/ccsrc/Protocols/Xml/include/xml_converters.hh @@ -0,0 +1,43 @@ +/*! + * \file xml_converters.hh + * \brief Header file for xml_converters functionality. + * \author ETSI STF637 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +#include + +/** + * \class xml_converters + * \brief This class provides XML converters such as canonicalization of XML documents before signature f encryption + */ +class xml_converters { + + static xml_converters *_instance; + +private: + xml_converters() {}; //! Can not be created manually + static int xml_node_set_contains_callback(void* user_data, xmlNodePtr node, xmlNodePtr parent); +public: + static inline xml_converters &get_instance(); + + virtual ~xml_converters() { + if (_instance != nullptr) + delete _instance; + }; + +public: + int xml_canonicalization(const std::string& p_to_canonical, std::string& p_canonicalized) const; + +}; // End of class xml_converters + +// static functions +xml_converters &xml_converters::get_instance() { return (_instance != nullptr) ? *_instance : *(_instance = new xml_converters()); } + diff --git a/ccsrc/Protocols/Xml/module.mk b/ccsrc/Protocols/Xml/module.mk new file mode 100644 index 0000000000000000000000000000000000000000..f530e70b89780e96a903940b6a7ebdabb6a62681 --- /dev/null +++ b/ccsrc/Protocols/Xml/module.mk @@ -0,0 +1,4 @@ +sources := \ + src/xml_converters.cc + +includes += ./include diff --git a/ccsrc/Protocols/Xml/src/xml_converters.cc b/ccsrc/Protocols/Xml/src/xml_converters.cc new file mode 100644 index 0000000000000000000000000000000000000000..363cc9a5bd468375e44c9d798bd572060258fdce --- /dev/null +++ b/ccsrc/Protocols/Xml/src/xml_converters.cc @@ -0,0 +1,62 @@ +/*! + * \file xml_converters.cc + * \brief Source file for the xml_converters functionality. + * \author ETSI STF637 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#include "xml_converters.hh" + +#include "loggers.hh" + +#include +#include + +xml_converters* xml_converters::_instance = nullptr; + +int xml_converters::xml_canonicalization(const std::string& p_to_canonical, std::string& p_canonicalized) const { + loggers::get_instance().log(">>> xml_converters::xml_canonicalization"); + + xmlDocPtr doc = xmlParseMemory(p_to_canonical.c_str(), p_to_canonical.length()); + if (doc == nullptr) { + loggers::get_instance().log("xml_converters::xml_canonicalization: Failed to read XML input"); + return -1; + } + + xmlOutputBufferPtr buffer = xmlAllocOutputBuffer(nullptr); + if (buffer == nullptr) { + loggers::get_instance().log("xml_converters::xml_canonicalization: Failed to read XML input"); + xmlFreeDoc(doc); + return -1; + } + + // Do canonicalization + loggers::get_instance().log("xml_converters::xml_canonicalization: Before"); + if (xmlC14NExecute(doc, (xmlC14NIsVisibleCallback)&xml_converters::xml_node_set_contains_callback, nullptr, XML_C14N_1_1, nullptr, 1, buffer) == -1) { + loggers::get_instance().log("xml_converters::xml_canonicalization: Failed to read XML input"); + xmlFreeDoc(doc); + xmlOutputBufferClose(buffer); + return -1; + } + loggers::get_instance().log("xml_converters::xml_canonicalization: After"); + + // Retrieve the canonicalization XML document + const char* data = (const char*)xmlOutputBufferGetContent(buffer); + size_t size = xmlOutputBufferGetSize(buffer); + p_canonicalized.assign(data, data + size); + + // Free resources + xmlOutputBufferClose(buffer); + xmlFreeDoc(doc); + + loggers::get_instance().log("<<< xml_converters::xml_canonicalization: %s", p_canonicalized.c_str()); + return 0; +} + +int xml_converters::xml_node_set_contains_callback(void* p_user_data, xmlNodePtr p_node, xmlNodePtr p_parent) { + loggers::get_instance().log("xml_converters::xml_node_set_contains_callback"); + return 1; +} diff --git a/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn b/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn index 0ee138ba7330f79285f3b52f0369820f0780d73c..e0f996785dc76110d4d8e6bd35125957359bcb30 100644 --- a/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn +++ b/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn @@ -160,6 +160,18 @@ module LibHelpers_Functions { */ external function fx_getDSecond() return UInt16; + /** + * @desc Encode into Base64 + * @return p_to_encode - The buffer to be encoded + */ + external function fx_enc_base64(in octetstring p_to_encode) return octetstring; + + /** + * @desc Decode from Base64 + * @return p_to_decode - The buffer to be decoded + */ + external function fx_dec_base64(in octetstring p_to_decode) return octetstring; + } // End of externals } // End of module LibHelpers_Functions