#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); TTCN_EncDec::clear_error(); TTCN_Buffer decoding_buffer; if (_initial_content_length != 0) { // Check if the data is a new fragment of the response loggers::get_instance().warning("http_codec::decode: Need to bufferize and wait for all fragments received"); _bufferized_buffers.push_back(data); _current_content_length += data.lengthof(); // FIXME Need to calculate the size of the header buffer, do not assume th first fragmwnt is the header buffer loggers::get_instance().log("http_codec::decode: _bufferized_buffers size: %d - _current_content_length: %d - _initial_content_length: %d", _bufferized_buffers.size(), _current_content_length, _initial_content_length); if (_current_content_length < _initial_content_length) { // Wait for the next fragment loggers::get_instance().warning("http_codec::decode: Wait for the next fragment"); return -2; } else { // Rebuild the wall message and continue the decoding loggers::get_instance().log("http_codec::decode: All the fragments were received, rebuild message"); // Concatenate the fragments loggers::get_instance().log("http_codec::decode: Concatenate bufferized datas"); for (size_t i = 0; i < _bufferized_buffers.size(); i++) { decoding_buffer.put_string(_bufferized_buffers[i]); } // End of 'for' statement _bufferized_buffers.clear(); loggers::get_instance().log("http_codec::decode: _bufferized_buffers cleared: %d", _bufferized_buffers.size()); // Reset counters loggers::get_instance().log("http_codec::decode: Reset bufferization counters"); _initial_content_length = 0; _current_content_length = 0; // Continue the decoding } } else { // It can be a full HTTP response or the first gragment of the HTTP response decoding_buffer.put_string(data); } loggers::get_instance().log("http_codec::decode: decoding_buffer size: %d", decoding_buffer.get_len()); loggers::get_instance().log_to_hexa("http_codec::decode: Before decoding_buffer Body: ", decoding_buffer); _dc.reset(); _params = params; if ((*decoding_buffer.get_data() & 0x80) == 0x80) { loggers::get_instance().warning("http_codec::decode: Unicode format not supported"); return -1; } // 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); loggers::get_instance().log("http_codec::decode: _dc.length=%d", _dc.length); loggers::get_instance().log("http_codec::decode: decoding_buffer.get_len()=%d", decoding_buffer.get_len()); if (_dc.length > decoding_buffer.get_len()) { // HTTP response is fragmented // Need to bufferize the first packet and wait for the other fragments tp be received loggers::get_instance().warning("http_codec::decode: Need to bufferize the first packet and wait for the other fragments tp be received"); // Set counters loggers::get_instance().log("http_codec::decode: Set bufferization counters"); _initial_content_length = _dc.length; _current_content_length = 0; _bufferized_buffers.push_back(data); _current_content_length += data.lengthof(); loggers::get_instance().log("http_codec::decode: _bufferized_buffers size: %d - _current_content_length: %d", _bufferized_buffers.size(), _current_content_length); return -2; } else { // Force reset counters loggers::get_instance().log("http_codec::decode: Force reset bufferization counters"); _initial_content_length = 0; _current_content_length = 0; } response.header() = headers; loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer); loggers::get_instance().log("http_codec::decode: _initial_content_length = %d", _initial_content_length); loggers::get_instance().log("http_codec::decode: headers().content_length (_dc.length) = %d - decoding_buffer.get_len() = %d", _dc.length, decoding_buffer.get_len()); 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()) { if (_dc.length == -1) { // content-Length header not found, force length to 0, assuming no body is present loggers::get_instance().log("http_codec::decode_headers: Force _dc.length to 0"); _dc.length = 0; } 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.msg().ischosen(LibHttp__XmlMessageBodyTypes::XmlBodyMsg::ALT_raw)) { p_encoding_buffer = OCTETSTRING(xml_body.msg().raw().lengthof(), (unsigned char *)static_cast(xml_body.msg().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; }