406 lines
11 KiB
Objective-C
406 lines
11 KiB
Objective-C
/* Copyright (c) 2014, 2018, 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 */
|
|
|
|
#ifndef GCS_GCS_MESSAGE_INCLUDED
|
|
#define GCS_GCS_MESSAGE_INCLUDED
|
|
|
|
#include <stdint.h>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
#include "plugin/group_replication/libmysqlgcs/include/mysql/gcs/gcs_group_identifier.h"
|
|
#include "plugin/group_replication/libmysqlgcs/include/mysql/gcs/gcs_member_identifier.h"
|
|
#include "plugin/group_replication/libmysqlgcs/include/mysql/gcs/gcs_types.h"
|
|
|
|
#define WIRE_PAYLOAD_LEN_SIZE 8
|
|
#define WIRE_HEADER_LEN_SIZE 4
|
|
|
|
/**
|
|
@class Gcs_message_data
|
|
|
|
This class serves as data container for information flowing in the GCS
|
|
ecosystem. It has been isolated in order to be used in place where a
|
|
full-blown Gcs_message does not make sense.
|
|
|
|
For a full usage example, check the Gcs_message documentation.
|
|
*/
|
|
class Gcs_message_data {
|
|
public:
|
|
Gcs_message_data();
|
|
/**
|
|
Constructor of Gcs_message_data which pre-allocates space to store
|
|
the header and payload and should be used when creating messages to
|
|
send information to remote peers.
|
|
|
|
@param header_capacity Determines the header's size.
|
|
@param payload_capacity Determines the payload's size.
|
|
*/
|
|
explicit Gcs_message_data(const uint32_t header_capacity,
|
|
const uint64_t payload_capacity);
|
|
|
|
/**
|
|
Constructor of Gcs_message_data which pre-allocates space to store
|
|
a message received from a remote peer.
|
|
|
|
@param data_len Data's length.
|
|
*/
|
|
explicit Gcs_message_data(const uint64_t data_len);
|
|
|
|
virtual ~Gcs_message_data();
|
|
|
|
/**
|
|
Appends data to the header of the message. The data MUST have been
|
|
previously encoded in little endian format.
|
|
|
|
If the data to be appended exceeds the pre-allocated buffer capacity
|
|
an error is returned.
|
|
|
|
@param[in] to_append the data to append
|
|
@param[in] to_append_len the length of the data to append
|
|
|
|
@return true on error, false otherwise.
|
|
*/
|
|
|
|
bool append_to_header(const uchar *to_append, uint32_t to_append_len);
|
|
|
|
/**
|
|
Appends data to the payload of the message. The data MUST have been
|
|
previously encoded in little endian format.
|
|
|
|
If the data to be appended exceeds the pre-allocated buffer capacity
|
|
an error is returned.
|
|
|
|
@param[in] to_append the data to append
|
|
@param[in] to_append_len the length of the data to append
|
|
|
|
@return true on error, false otherwise.
|
|
*/
|
|
|
|
bool append_to_payload(const uchar *to_append, uint64_t to_append_len);
|
|
|
|
/**
|
|
Release the buffer's owership which means that this object will not
|
|
be responsible for deallocating its internal buffer. The caller should
|
|
do so.
|
|
|
|
This method should be used along with the following method:
|
|
encode(**buffer, *buffer_len).
|
|
**/
|
|
|
|
void release_ownership();
|
|
|
|
/**
|
|
Encodes the header and payload into an internal buffer. If NULL pointer
|
|
is provided or the data was not already appended to the buffer, an error
|
|
is returned.
|
|
|
|
The meta data is formated in little endian format, and is structured
|
|
on the wire as depicted below:
|
|
|
|
-----------------------------------------------
|
|
| header len | payload len | header | payload |
|
|
-----------------------------------------------
|
|
|
|
@param[in,out] buffer Variable that will hold a pointer to the buffer
|
|
@param[in,out] buffer_len Variable that will hold the buffer's size.
|
|
|
|
@return true on error, false otherwise.
|
|
*/
|
|
|
|
bool encode(uchar **buffer, uint64_t *buffer_len) const;
|
|
|
|
/**
|
|
Encodes the header and payload into a buffer provided by the caller.
|
|
If the buffer is not large enough to store the encoded data or is a
|
|
NULL pointer, an error is returned.
|
|
|
|
@param [in,out] buffer Buffer to store the encoded data in the message.
|
|
@param [in,out] buffer_len The length of the buffer where the data is to be
|
|
stored. It contains the length of the data dumped into the buffer once the
|
|
function succeeds.
|
|
|
|
@return true on error, false otherwise.
|
|
*/
|
|
|
|
bool encode(uchar *buffer, uint64_t *buffer_len) const;
|
|
|
|
/**
|
|
Decodes data received via GCS and that belongs to a message. After
|
|
decoding, all the fields will be filled. The data MUST be in little endian
|
|
format.
|
|
|
|
If the buffer is not large enough to store the encoded data or is a
|
|
NULL pointer, an error is returned.
|
|
|
|
@param[in] data Data received via network
|
|
@param[in] data_len Data length received via network
|
|
|
|
@return true on error, false otherwise.
|
|
*/
|
|
|
|
bool decode(const uchar *data, uint64_t data_len);
|
|
|
|
/**
|
|
@return the message header in little endian format
|
|
*/
|
|
|
|
const uchar *get_header() const;
|
|
|
|
/**
|
|
@return the message header length
|
|
*/
|
|
|
|
uint32_t get_header_length() const;
|
|
|
|
/**
|
|
@return the message payload in little endian format
|
|
*/
|
|
|
|
const uchar *get_payload() const;
|
|
|
|
/**
|
|
@return the message payload_length
|
|
*/
|
|
|
|
uint64_t get_payload_length() const;
|
|
|
|
/**
|
|
@return the size of the encoded data when put on the wire.
|
|
*/
|
|
|
|
virtual uint64_t get_encode_size() const;
|
|
|
|
/**
|
|
@return the size of the encoded payload when put on the wire.
|
|
*/
|
|
|
|
uint64_t get_encode_payload_size() const;
|
|
|
|
/**
|
|
@return the size of the encoded header when put on the wire.
|
|
*/
|
|
|
|
uint64_t get_encode_header_size() const;
|
|
|
|
private:
|
|
/*
|
|
Pointer to the header's buffer.
|
|
*/
|
|
uchar *m_header;
|
|
|
|
/*
|
|
Pointer to the next empty position in the header's buffer.
|
|
*/
|
|
uchar *m_header_slider;
|
|
|
|
/*
|
|
Length of the header's buffer in use.
|
|
*/
|
|
uint32_t m_header_len;
|
|
|
|
/*
|
|
Capacity of the header's buffer.
|
|
*/
|
|
uint32_t m_header_capacity;
|
|
|
|
/*
|
|
Pointer to the payload's buffer.
|
|
*/
|
|
uchar *m_payload;
|
|
|
|
/*
|
|
Pointer to the next empty position in the payload's buffer.
|
|
*/
|
|
uchar *m_payload_slider;
|
|
|
|
/*
|
|
Length of the payload's buffer in use.
|
|
*/
|
|
uint64_t m_payload_len;
|
|
|
|
/*
|
|
Capacity of the header's buffer.
|
|
*/
|
|
uint64_t m_payload_capacity;
|
|
|
|
/*
|
|
Pointer to the begining of the buffer that contains both the
|
|
header and the payload.
|
|
*/
|
|
uchar *m_buffer;
|
|
|
|
/*
|
|
Length of the buffer that contains both the header, the
|
|
payload and metadata information.
|
|
*/
|
|
uint64_t m_buffer_len;
|
|
|
|
/*
|
|
Whether the current object owns the buffer and must free it
|
|
when deleted.
|
|
*/
|
|
bool m_owner;
|
|
|
|
/*
|
|
Disabling the copy constructor and assignment operator.
|
|
*/
|
|
Gcs_message_data(Gcs_message_data const &);
|
|
Gcs_message_data &operator=(Gcs_message_data const &);
|
|
};
|
|
|
|
/**
|
|
@class Gcs_message
|
|
|
|
Class that represents the data that is exchanged within a group. It is sent
|
|
by a member having the group as destination. It shall be received by all
|
|
members that pertain to the group in that moment.
|
|
|
|
It is built using two major blocks: the header and the payload of the
|
|
message. A user of Gcs_message can freely add data to the header and to the
|
|
payload.
|
|
|
|
Each binding implementation might use these two fields at its own
|
|
discretion but that data should be removed from the header/payload
|
|
before its delivery to the client layer.
|
|
|
|
When the message is built, none of the data is passed unto it. One has to
|
|
use the append_* methods in order to append data. Calling encode() at the end
|
|
will provide a ready-to-send message.
|
|
|
|
On the receiver side, one shall use decode() in order to have the header and
|
|
the payload restored in the original message fields.
|
|
|
|
A typical use case of sending a message is:
|
|
|
|
@code{.cpp}
|
|
|
|
Gcs_control_interface *gcs_ctrl_interface_instance;
|
|
Gcs_communication_interface *gcs_comm_interface_instance; // obtained
|
|
// previously
|
|
|
|
Gcs_member_identifier *myself=
|
|
gcs_ctrl_interface_instance->get_local_information();
|
|
|
|
Gcs_group_identifier destination("the_group");
|
|
|
|
Gcs_message *msg= new Gcs_message(&myself, new Gcs_message_data(0, 3));
|
|
|
|
uchar[] some_data= {(uchar)0x12,
|
|
(uchar)0x24,
|
|
(uchar)0x00};
|
|
|
|
msg->get_message_data()->append_to_payload(&some_data,
|
|
strlen(some_data));
|
|
|
|
gcs_comm_interface_instance->send_message(msg);
|
|
@endcode
|
|
|
|
A typical use case of receiving a message is:
|
|
|
|
@code{.cpp}
|
|
class my_Gcs_communication_event_listener : Gcs_communication_event_listener
|
|
{
|
|
my_Gcs_communication_event_listener(my_Message_delegator *delegator)
|
|
{
|
|
this->delegator= delegator;
|
|
}
|
|
|
|
void on_message_received(Gcs_message &message)
|
|
{
|
|
// Do something when message arrives...
|
|
delegator->process_gcs_message(message);
|
|
}
|
|
|
|
private:
|
|
my_Message_delegator *delegator;
|
|
}
|
|
@endcode
|
|
*/
|
|
class Gcs_message {
|
|
public:
|
|
/**
|
|
Gcs_message 1st constructor. This is used to build full messages.
|
|
|
|
Note that the Gcs_message will acquire ownership of the data, i.e.
|
|
Gcs_message_data, and will be responsible for deleting it.
|
|
|
|
@param[in] origin The group member that sent the message
|
|
@param[in] destination The group in which this message belongs
|
|
@param[in] message_data External message data object.
|
|
*/
|
|
|
|
explicit Gcs_message(const Gcs_member_identifier &origin,
|
|
const Gcs_group_identifier &destination,
|
|
Gcs_message_data *message_data);
|
|
|
|
/**
|
|
Gcs_message 2nd constructor. This is used to send messages but with
|
|
an external Gcs_message_data object.
|
|
|
|
Note that the Gcs_message will acquire ownership of the data, i.e.
|
|
Gcs_message_data, and will be responsible for deleting it.
|
|
|
|
@param[in] origin The group member that sent the message
|
|
@param[in] message_data External message data object.
|
|
*/
|
|
|
|
explicit Gcs_message(const Gcs_member_identifier &origin,
|
|
Gcs_message_data *message_data);
|
|
|
|
virtual ~Gcs_message();
|
|
|
|
/**
|
|
@return the origin of this message
|
|
*/
|
|
|
|
const Gcs_member_identifier &get_origin() const;
|
|
|
|
/**
|
|
@return the destination of this message. It might be NULL.
|
|
*/
|
|
|
|
const Gcs_group_identifier *get_destination() const;
|
|
|
|
/**
|
|
@return the message data to be filled.
|
|
*/
|
|
|
|
Gcs_message_data &get_message_data() const;
|
|
|
|
private:
|
|
void init(const Gcs_member_identifier *origin,
|
|
const Gcs_group_identifier *destination,
|
|
Gcs_message_data *message_data);
|
|
|
|
Gcs_member_identifier *m_origin;
|
|
Gcs_group_identifier *m_destination;
|
|
Gcs_message_data *m_data;
|
|
|
|
/*
|
|
Disabling the copy constructor and assignment operator.
|
|
*/
|
|
Gcs_message(Gcs_message const &);
|
|
Gcs_message &operator=(Gcs_message const &);
|
|
};
|
|
|
|
#endif // GCS_GCS_MESSAGE_INCLUDED
|