259 lines
8.2 KiB
C++
259 lines
8.2 KiB
C++
/*
|
|
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License, version 2.0,
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is also distributed with certain software (including
|
|
* but not limited to OpenSSL) that is licensed under separate terms,
|
|
* as designated in a particular file or component or in included license
|
|
* documentation. The authors of MySQL hereby grant you an additional
|
|
* permission to link the program and your derivative works with the
|
|
* separately licensed software that they have included with MySQL.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License, version 2.0, for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "message_parser.h"
|
|
|
|
#include <memory>
|
|
|
|
#include "../common/utils_string_parsing.h"
|
|
#include "../connector/mysqlx_all_msgs.h"
|
|
|
|
using Message = xcl::XProtocol::Message;
|
|
|
|
namespace parser {
|
|
|
|
namespace details {
|
|
|
|
class Error_dumper : public ::google::protobuf::io::ErrorCollector {
|
|
std::stringstream m_out;
|
|
|
|
public:
|
|
void AddError(int line, int column, const std::string &message) override {
|
|
m_out << "ERROR in message: line " << line + 1 << ": column " << column
|
|
<< ": " << message << "\n";
|
|
}
|
|
|
|
void AddWarning(int line, int column, const std::string &message) override {
|
|
m_out << "WARNING in message: line " << line + 1 << ": column " << column
|
|
<< ": " << message << "\n";
|
|
}
|
|
|
|
std::string str() { return m_out.str(); }
|
|
};
|
|
|
|
bool parse_mesage(const std::string &text_message, const std::string &text_name,
|
|
Message *message, std::string *out_error,
|
|
const bool allow_partial_messaged) {
|
|
google::protobuf::TextFormat::Parser parser;
|
|
Error_dumper dumper;
|
|
parser.RecordErrorsTo(&dumper);
|
|
parser.AllowPartialMessage(allow_partial_messaged);
|
|
if (!parser.ParseFromString(text_message, message)) {
|
|
if (nullptr != out_error) {
|
|
*out_error = "Invalid message in input: " + text_name + '\n';
|
|
int i = 1;
|
|
for (std::string::size_type p = 0, n = text_message.find('\n', p + 1);
|
|
p != std::string::npos; p = (n == std::string::npos ? n : n + 1),
|
|
n = text_message.find('\n', p + 1), ++i) {
|
|
*out_error +=
|
|
std::to_string(i) + ": " + text_message.substr(p, n - p) + '\n';
|
|
}
|
|
*out_error += "\n" + dumper.str() + '\n';
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
template <typename MSG>
|
|
Message *parse_serialize_message(const std::string &text_payload,
|
|
std::string *out_error,
|
|
const bool allow_partial_messaged) {
|
|
std::unique_ptr<MSG> msg{new MSG()};
|
|
|
|
if (!parse_mesage(text_payload, "", msg.get(), out_error,
|
|
allow_partial_messaged))
|
|
return {};
|
|
|
|
return msg.release();
|
|
}
|
|
|
|
bool get_notice_payload_from_text(const Polarx::Notice::Frame_Type type,
|
|
const std::string &text_payload,
|
|
std::string *out_binary_payload,
|
|
const bool allow_partial_messaged) {
|
|
std::string error;
|
|
std::unique_ptr<Message> msg{parser::get_notice_message_from_text(
|
|
type, text_payload, &error, allow_partial_messaged)};
|
|
|
|
if (nullptr == msg) {
|
|
// Fail when there is a payload, still we received a null message
|
|
return text_payload.empty();
|
|
}
|
|
|
|
if (allow_partial_messaged)
|
|
return msg->SerializePartialToString(out_binary_payload);
|
|
|
|
return msg->SerializeToString(out_binary_payload);
|
|
}
|
|
|
|
} // namespace details
|
|
|
|
Message *get_notice_message_from_text(const Polarx::Notice::Frame_Type type,
|
|
const std::string &text_payload,
|
|
std::string *out_error,
|
|
const bool allow_partial_messaged) {
|
|
switch (type) {
|
|
case Polarx::Notice::Frame_Type_WARNING:
|
|
return details::parse_serialize_message<Polarx::Notice::Warning>(
|
|
text_payload, out_error, allow_partial_messaged);
|
|
case Polarx::Notice::Frame_Type_SESSION_VARIABLE_CHANGED:
|
|
return details::parse_serialize_message<
|
|
Polarx::Notice::SessionVariableChanged>(text_payload, out_error,
|
|
allow_partial_messaged);
|
|
case Polarx::Notice::Frame_Type_SESSION_STATE_CHANGED:
|
|
return details::parse_serialize_message<
|
|
Polarx::Notice::SessionStateChanged>(text_payload, out_error,
|
|
allow_partial_messaged);
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
bool get_name_and_body_from_text(const std::string &text_message,
|
|
std::string *out_full_message_name,
|
|
std::string *out_message_body,
|
|
const bool is_body_full) {
|
|
const auto separator = text_message.find("{");
|
|
|
|
if (std::string::npos == separator) {
|
|
return false;
|
|
}
|
|
|
|
if (nullptr != out_full_message_name) {
|
|
*out_full_message_name = text_message.substr(0, separator);
|
|
aux::trim(*out_full_message_name);
|
|
}
|
|
|
|
auto body = text_message.substr(separator);
|
|
|
|
if (is_body_full) {
|
|
aux::trim(body, " \t\n\r");
|
|
|
|
if (body.size() < 2) return false;
|
|
|
|
if (body[0] != '{') return false;
|
|
if (body[body.size() - 1] != '}') return false;
|
|
|
|
body = body.substr(1, body.size() - 2);
|
|
}
|
|
|
|
if (nullptr != out_message_body) {
|
|
*out_message_body = body;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Message *get_client_message_from_text(
|
|
const std::string &name, const std::string &data,
|
|
xcl::XProtocol::Client_message_type_id *msg_id, std::string *out_error,
|
|
const bool allow_partial_messaged) {
|
|
std::string find_by = name;
|
|
Message *message;
|
|
|
|
if (find_by.empty()) {
|
|
*out_error = "Message name is empty";
|
|
return nullptr;
|
|
}
|
|
|
|
while (true) {
|
|
auto msg = client_msgs_by_name.find(find_by);
|
|
|
|
if (msg == client_msgs_by_name.end()) {
|
|
if (client_msgs_by_full_name.count(name) &&
|
|
find_by != client_msgs_by_full_name[name]) {
|
|
find_by = client_msgs_by_full_name[name];
|
|
continue;
|
|
}
|
|
*out_error = "Invalid message type " + name;
|
|
return nullptr;
|
|
}
|
|
|
|
message = msg->second.first();
|
|
*msg_id = msg->second.second;
|
|
break;
|
|
}
|
|
|
|
if (!details::parse_mesage(data, name, message, out_error,
|
|
allow_partial_messaged)) {
|
|
delete message;
|
|
return nullptr;
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
Message *get_server_message_from_text(
|
|
const std::string &name, const std::string &data,
|
|
xcl::XProtocol::Server_message_type_id *msg_id, std::string *out_error,
|
|
const bool allow_partial_messaged) {
|
|
std::string find_by = name;
|
|
Message *message;
|
|
|
|
while (true) {
|
|
auto msg = server_msgs_by_name.find(find_by);
|
|
|
|
if (msg == server_msgs_by_name.end()) {
|
|
if (server_msgs_by_full_name.count(name) &&
|
|
find_by != server_msgs_by_full_name[name]) {
|
|
find_by = server_msgs_by_full_name[name];
|
|
continue;
|
|
}
|
|
*out_error = "Invalid message type " + name;
|
|
return nullptr;
|
|
}
|
|
|
|
message = msg->second.first();
|
|
*msg_id = msg->second.second;
|
|
break;
|
|
}
|
|
|
|
if (!details::parse_mesage(data, name, message, out_error,
|
|
allow_partial_messaged)) {
|
|
delete message;
|
|
return nullptr;
|
|
}
|
|
|
|
if (Polarx::ServerMessages::NOTICE == *msg_id) {
|
|
auto notice = reinterpret_cast<Polarx::Notice::Frame *>(message);
|
|
|
|
std::string out_payload;
|
|
if (!details::get_notice_payload_from_text(
|
|
static_cast<Polarx::Notice::Frame_Type>(notice->type()),
|
|
notice->payload(), &out_payload, allow_partial_messaged)) {
|
|
*out_error = "Invalid notice payload: " + notice->payload();
|
|
return nullptr;
|
|
}
|
|
|
|
notice->set_payload(out_payload);
|
|
}
|
|
|
|
return message;
|
|
}
|
|
|
|
} // namespace parser
|