protocol: Give exceptions their own error codes

This commit is contained in:
Joshua Scott 2018-04-22 17:11:12 +01:00
parent a00f9b5003
commit 6d02bfe06a
15 changed files with 139 additions and 44 deletions

View File

@ -1,6 +1,7 @@
#pragma once
#include "../../util/Serializable.h"
#include <cstdint>
#include <iostream>
namespace ki
{

View File

@ -1,6 +1,7 @@
#pragma once
#include "../../util/Serializable.h"
#include <cstdint>
#include <iostream>
namespace ki
{

View File

@ -8,19 +8,57 @@ namespace protocol
class runtime_error : public std::runtime_error
{
public:
runtime_error(std::string message) : std::runtime_error(message) { }
explicit runtime_error(std::string message) : std::runtime_error(message) {}
};
class parse_error : public runtime_error
{
public:
parse_error(std::string message) : runtime_error(message) { }
enum code
{
NONE,
INVALID_XML_DATA,
INVALID_HEADER_DATA,
INSUFFICIENT_MESSAGE_DATA,
INVALID_MESSAGE_DATA
};
explicit parse_error(std::string message, code error = code::NONE)
: runtime_error(message)
{
m_code = error;
}
code get_error_code() const { return m_code; }
private:
code m_code;
};
class value_error : public runtime_error
{
public:
value_error(std::string message) : runtime_error(message) { }
enum code
{
NONE,
MISSING_FILE,
OVERWRITES_LOOKUP,
EXCEEDS_LIMIT,
DML_INVALID_SERVICE,
DML_INVALID_PROTOCOL_TYPE,
DML_INVALID_MESSAGE_TYPE,
DML_INVALID_MESSAGE_NAME
};
explicit value_error(std::string message, code error = code::NONE)
: runtime_error(message)
{
m_code = error;
}
code get_error_code() const { return m_code; }
private:
code m_code;
};
}
}

View File

@ -10,7 +10,12 @@ namespace net
{
enum class InvalidDMLMessageErrorCode
{
NONE,
UNKNOWN,
INVALID_HEADER_DATA,
INVALID_MESSAGE_DATA,
INVALID_SERVICE,
INVALID_MESSAGE_TYPE,
INSUFFICIENT_ACCESS
};

View File

@ -1,6 +1,7 @@
#pragma once
#include "../../util/Serializable.h"
#include <cstdint>
#include <iostream>
namespace ki
{

View File

@ -69,7 +69,7 @@ namespace control
{
std::ostringstream oss;
oss << "Error reading ClientKeepAlive payload: " << e.what();
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA);
}
m_session_id = session_id->get_value();

View File

@ -45,7 +45,7 @@ namespace control
{
std::ostringstream oss;
oss << "Error reading ServerKeepAlive payload: " << e.what();
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA);
}
m_timestamp = timestamp->get_value();

View File

@ -73,7 +73,7 @@ namespace control
{
std::ostringstream oss;
oss << "Error reading SessionAccept payload: " << e.what();
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA);
}
m_timestamp = timestamp->get_value();

View File

@ -71,7 +71,7 @@ namespace control
{
std::ostringstream oss;
oss << "Error reading SessionOffer payload: " << e.what();
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA);
}
m_session_id = session_id->get_value();

View File

@ -50,7 +50,7 @@ namespace dml
std::ostringstream oss;
oss << "Error reading DML message payload: " << e.what();
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_MESSAGE_DATA);
}
}
}
@ -140,10 +140,12 @@ namespace dml
{
// Check for mismatches between the header and template
if (m_header.get_service_id() != m_template->get_service_id())
throw value_error("ServiceID mismatch between MessageHeader and assigned template.");
throw value_error("ServiceID mismatch between MessageHeader and assigned template.",
value_error::DML_INVALID_SERVICE);
if (m_header.get_type() != m_template->get_type())
throw value_error("Message Type mismatch between MessageHeader and assigned template.");
throw value_error("Message Type mismatch between MessageHeader and assigned template.",
value_error::DML_INVALID_MESSAGE_TYPE);
// Read the payload into the record
m_record->read_from(istream);
}
@ -155,7 +157,8 @@ namespace dml
m_raw_data.resize(size);
istream.read(m_raw_data.data(), size);
if (istream.fail())
throw parse_error("Not enough data was available to read DML message payload.");
throw parse_error("Not enough data was available to read DML message payload.",
parse_error::INSUFFICIENT_MESSAGE_DATA);
}
}

View File

@ -70,7 +70,7 @@ namespace dml
{
std::ostringstream oss;
oss << "Error reading MessageHeader: " << e.what();
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_HEADER_DATA);
}
m_service_id = service_id->get_value();

View File

@ -31,7 +31,7 @@ namespace dml
{
std::ostringstream oss;
oss << "Could not open file: " << filepath;
throw value_error(oss.str());
throw value_error(oss.str(), value_error::MISSING_FILE);
}
// Load contents into memory
@ -52,7 +52,7 @@ namespace dml
std::ostringstream oss;
oss << "Failed to parse: " << filepath;
throw parse_error(oss.str());
throw parse_error(oss.str(), parse_error::INVALID_XML_DATA);
}
// It's safe to allocate the module we're working on now
@ -118,8 +118,8 @@ namespace dml
std::ostringstream oss;
oss << "Message Module has already been loaded with Service ID ";
oss << message_module->get_service_id();
throw value_error(oss.str());
oss << (uint16_t)message_module->get_service_id();
throw value_error(oss.str(), value_error::OVERWRITES_LOOKUP);
}
if (m_protocol_type_map.count(message_module->get_protocol_type()) == 1)
@ -130,7 +130,7 @@ namespace dml
std::ostringstream oss;
oss << "Message Module has already been loaded with Protocol Type ";
oss << message_module->get_protocol_type();
throw value_error(oss.str());
throw value_error(oss.str(), value_error::OVERWRITES_LOOKUP);
}
// Add it to our maps
@ -162,8 +162,8 @@ namespace dml
if (!message_module)
{
std::ostringstream oss;
oss << "No service exists with id: " << service_id;
throw value_error(oss.str());
oss << "No service exists with id: " << (uint16_t)service_id;
throw value_error(oss.str(), value_error::DML_INVALID_SERVICE);
}
return message_module->create_message(message_type);
@ -175,8 +175,8 @@ namespace dml
if (!message_module)
{
std::ostringstream oss;
oss << "No service exists with id: " << service_id;
throw value_error(oss.str());
oss << "No service exists with id: " << (uint16_t)service_id;
throw value_error(oss.str(), value_error::DML_INVALID_SERVICE);
}
return message_module->create_message(message_name);
@ -189,7 +189,7 @@ namespace dml
{
std::ostringstream oss;
oss << "No service exists with protocol type: " << protocol_type;
throw value_error(oss.str());
throw value_error(oss.str(), value_error::DML_INVALID_PROTOCOL_TYPE);
}
return message_module->create_message(message_type);
@ -202,7 +202,7 @@ namespace dml
{
std::ostringstream oss;
oss << "No service exists with protocol type: " << protocol_type;
throw value_error(oss.str());
throw value_error(oss.str(), value_error::DML_INVALID_PROTOCOL_TYPE);
}
return message_module->create_message(message_name);
@ -212,28 +212,35 @@ namespace dml
{
// Read the message header
MessageHeader header;
try
{
header.read_from(istream);
}
catch (parse_error &e)
{
return nullptr;
}
header.read_from(istream);
// Get the message module that uses the specified service id
auto *message_module = get_module(header.get_service_id());
if (!message_module)
return nullptr;
{
std::ostringstream oss;
oss << "No service exists with id: " << (uint16_t)header.get_service_id();
throw value_error(oss.str(), value_error::DML_INVALID_SERVICE);
}
// Get the message template for this message type
auto *message_template = message_module->get_message_template(header.get_type());
if (!message_template)
return nullptr;
{
std::ostringstream oss;
oss << "No message exists with type: " << (uint16_t)header.get_service_id();
oss << "(service=" << message_module->get_protocol_type() << ")";
throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE);
}
// Make sure that the size specified is enough to read this message
if (header.get_message_size() < message_template->get_record().get_size())
return nullptr;
{
std::ostringstream oss;
oss << "No message exists with type: " << (uint16_t)header.get_service_id();
oss << "(service=" << message_module->get_protocol_type() << ")";
throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE);
}
// Create a new Message from the template
auto *message = new Message(message_template);
@ -244,7 +251,7 @@ namespace dml
catch (ki::dml::parse_error &e)
{
delete message;
return nullptr;
throw parse_error("Failed to read DML message payload.", parse_error::INVALID_MESSAGE_DATA);
}
return message;
}

View File

@ -135,7 +135,7 @@ namespace dml
// Make sure we haven't overflowed
if (message_type == 0)
throw value_error("Module has more than 254 messages.");
throw value_error("Module has more than 254 messages.", value_error::EXCEEDS_LIMIT);
}
}
@ -147,7 +147,7 @@ namespace dml
std::ostringstream oss;
oss << "No message exists with type: " << message_type;
oss << "(service=" << m_protocol_type << ")";
throw value_error(oss.str());
throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_TYPE);
}
return message_template->create_message();
@ -161,7 +161,7 @@ namespace dml
std::ostringstream oss;
oss << "No message exists with name: " << message_name;
oss << "(service=" << m_protocol_type << ")";
throw value_error(oss.str());
throw value_error(oss.str(), value_error::DML_INVALID_MESSAGE_NAME);
}
return message_template->create_message();

View File

@ -1,4 +1,5 @@
#include "ki/protocol/net/DMLSession.h"
#include "ki/protocol/exception.h"
namespace ki
{
@ -22,10 +23,45 @@ namespace net
void DMLSession::on_application_message(const PacketHeader& header)
{
// Attempt to create a Message instance from the data in the stream
const auto *message = m_manager.message_from_binary(m_data_stream);
auto error_code = InvalidDMLMessageErrorCode::NONE;
const dml::Message *message = nullptr;
try
{
message = m_manager.message_from_binary(m_data_stream);
}
catch (parse_error &e)
{
switch (e.get_error_code())
{
case parse_error::INVALID_HEADER_DATA:
error_code = InvalidDMLMessageErrorCode::INVALID_HEADER_DATA;
break;
case parse_error::INSUFFICIENT_MESSAGE_DATA:
case parse_error::INVALID_MESSAGE_DATA:
error_code = InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA;
break;
default:
error_code = InvalidDMLMessageErrorCode::UNKNOWN;
}
}
catch (value_error &e)
{
switch (e.get_error_code())
{
case value_error::DML_INVALID_SERVICE:
error_code = InvalidDMLMessageErrorCode::INVALID_SERVICE;
break;
case value_error::DML_INVALID_MESSAGE_TYPE:
error_code = InvalidDMLMessageErrorCode::INVALID_MESSAGE_TYPE;
break;
default:
error_code = InvalidDMLMessageErrorCode::UNKNOWN;
}
}
if (!message)
{
on_invalid_message(InvalidDMLMessageErrorCode::INVALID_MESSAGE_DATA);
on_invalid_message(error_code);
return;
}

View File

@ -46,13 +46,16 @@ namespace net
{
m_control = istream.get() >= 1;
if (istream.fail())
throw parse_error("Not enough data was available to read packet header. (m_control)");
throw parse_error("Not enough data was available to read packet header. (m_control)",
parse_error::INVALID_HEADER_DATA);
m_opcode = istream.get();
if (istream.fail())
throw parse_error("Not enough data was available to read packet header. (m_opcode)");
throw parse_error("Not enough data was available to read packet header. (m_opcode)",
parse_error::INVALID_HEADER_DATA);
istream.ignore(2);
if (istream.eof())
throw parse_error("Not enough data was available to read packet header. (ignored bytes)");
throw parse_error("Not enough data was available to read packet header. (ignored bytes)",
parse_error::INVALID_HEADER_DATA);
}
size_t PacketHeader::get_size() const