mirror of
https://github.com/espressif/openthread.git
synced 2026-07-28 14:47:46 +00:00
[coap] add otCoapOptionIterator and related API functions (#4452)
This commit adds a otCoapOptionIterator structure which the user can use to iterate over the CoAP options of a ot::Coap::Message object without influencing any of the properties of that object. Multiple otCoapOptionIterator structures may operate on the same ot::Coap::Message object without interference.
This commit is contained in:
committed by
Jonathan Hui
parent
bfa84c26b0
commit
033cf4c4b1
@@ -149,6 +149,17 @@ typedef struct otCoapOption
|
||||
uint16_t mLength; ///< Option Length
|
||||
} otCoapOption;
|
||||
|
||||
/**
|
||||
* This structure acts as an iterator for CoAP options
|
||||
*
|
||||
*/
|
||||
typedef struct otCoapOptionIterator
|
||||
{
|
||||
const otMessage *mMessage; ///< CoAP message
|
||||
otCoapOption mOption; ///< CoAP message option
|
||||
uint16_t mNextOptionOffset; ///< Byte offset of next option
|
||||
} otCoapOptionIterator;
|
||||
|
||||
/**
|
||||
* CoAP Content Format codes. The full list is documented at
|
||||
* https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats
|
||||
@@ -566,37 +577,49 @@ uint8_t otCoapMessageGetTokenLength(const otMessage *aMessage);
|
||||
*/
|
||||
const uint8_t *otCoapMessageGetToken(const otMessage *aMessage);
|
||||
|
||||
/**
|
||||
* This function initialises an iterator for the options in the given message.
|
||||
*
|
||||
* @param[inout] aIterator A pointer to the CoAP message option iterator.
|
||||
* @param[in] aMessage A pointer to the CoAP message.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully initialised.
|
||||
* @retval OT_ERROR_PARSE Message state is inconsistent.
|
||||
*
|
||||
*/
|
||||
otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage);
|
||||
|
||||
/**
|
||||
* This function returns a pointer to the first option.
|
||||
*
|
||||
* @param[in] aMessage A pointer to the CoAP message.
|
||||
* @param[inout] aIterator A pointer to the CoAP message option iterator.
|
||||
*
|
||||
* @returns A pointer to the first option. If no option is present NULL pointer is returned.
|
||||
*
|
||||
*/
|
||||
const otCoapOption *otCoapMessageGetFirstOption(otMessage *aMessage);
|
||||
const otCoapOption *otCoapOptionIteratorGetFirstOption(otCoapOptionIterator *aIterator);
|
||||
|
||||
/**
|
||||
* This function returns a pointer to the next option.
|
||||
*
|
||||
* @param[in] aMessage A pointer to the CoAP message.
|
||||
* @param[inout] aIterator A pointer to the CoAP message option iterator.
|
||||
*
|
||||
* @returns A pointer to the next option. If no more options are present NULL pointer is returned.
|
||||
*
|
||||
*/
|
||||
const otCoapOption *otCoapMessageGetNextOption(otMessage *aMessage);
|
||||
const otCoapOption *otCoapOptionIteratorGetNextOption(otCoapOptionIterator *aIterator);
|
||||
|
||||
/**
|
||||
* This function fills current option value into @p aValue.
|
||||
*
|
||||
* @param[in] aMessage A pointer to the CoAP message.
|
||||
* @param[out] aValue A pointer to a buffer to receive the option value.
|
||||
* @param[inout] aIterator A pointer to the CoAP message option iterator.
|
||||
* @param[out] aValue A pointer to a buffer to receive the option value.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully filled value.
|
||||
* @retval OT_ERROR_NOT_FOUND No current option.
|
||||
*
|
||||
*/
|
||||
otError otCoapMessageGetOptionValue(otMessage *aMessage, void *aValue);
|
||||
otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void *aValue);
|
||||
|
||||
/**
|
||||
* This function creates a new CoAP message.
|
||||
|
||||
@@ -160,19 +160,24 @@ const uint8_t *otCoapMessageGetToken(const otMessage *aMessage)
|
||||
return static_cast<const Coap::Message *>(aMessage)->GetToken();
|
||||
}
|
||||
|
||||
const otCoapOption *otCoapMessageGetFirstOption(otMessage *aMessage)
|
||||
otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage)
|
||||
{
|
||||
return static_cast<Coap::Message *>(aMessage)->GetFirstOption();
|
||||
return static_cast<Coap::OptionIterator *>(aIterator)->Init(static_cast<const Coap::Message *>(aMessage));
|
||||
}
|
||||
|
||||
const otCoapOption *otCoapMessageGetNextOption(otMessage *aMessage)
|
||||
const otCoapOption *otCoapOptionIteratorGetFirstOption(otCoapOptionIterator *aIterator)
|
||||
{
|
||||
return static_cast<Coap::Message *>(aMessage)->GetNextOption();
|
||||
return static_cast<Coap::OptionIterator *>(aIterator)->GetFirstOption();
|
||||
}
|
||||
|
||||
otError otCoapMessageGetOptionValue(otMessage *aMessage, void *aValue)
|
||||
const otCoapOption *otCoapOptionIteratorGetNextOption(otCoapOptionIterator *aIterator)
|
||||
{
|
||||
return static_cast<Coap::Message *>(aMessage)->GetOptionValue(aValue);
|
||||
return static_cast<Coap::OptionIterator *>(aIterator)->GetNextOption();
|
||||
}
|
||||
|
||||
otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void *aValue)
|
||||
{
|
||||
return static_cast<Coap::OptionIterator *>(aIterator)->GetOptionValue(aValue);
|
||||
}
|
||||
|
||||
otError otCoapSendRequest(otInstance * aInstance,
|
||||
|
||||
@@ -535,10 +535,11 @@ exit:
|
||||
|
||||
void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
char uriPath[Resource::kMaxReceivedUriPath];
|
||||
char * curUriPath = uriPath;
|
||||
Message *cachedResponse = NULL;
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
char uriPath[Resource::kMaxReceivedUriPath];
|
||||
char * curUriPath = uriPath;
|
||||
Message * cachedResponse = NULL;
|
||||
otError error = OT_ERROR_NOT_FOUND;
|
||||
OptionIterator iterator;
|
||||
|
||||
if (mInterceptor != NULL)
|
||||
{
|
||||
@@ -561,7 +562,8 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo
|
||||
break;
|
||||
}
|
||||
|
||||
for (const otCoapOption *option = aMessage.GetFirstOption(); option != NULL; option = aMessage.GetNextOption())
|
||||
SuccessOrExit(error = iterator.Init(&aMessage));
|
||||
for (const otCoapOption *option = iterator.GetFirstOption(); option != NULL; option = iterator.GetNextOption())
|
||||
{
|
||||
switch (option->mNumber)
|
||||
{
|
||||
@@ -573,7 +575,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo
|
||||
|
||||
VerifyOrExit(option->mLength < sizeof(uriPath) - static_cast<size_t>(curUriPath + 1 - uriPath));
|
||||
|
||||
aMessage.GetOptionValue(curUriPath);
|
||||
iterator.GetOptionValue(curUriPath);
|
||||
curUriPath += option->mLength;
|
||||
break;
|
||||
|
||||
|
||||
+142
-119
@@ -207,121 +207,6 @@ otError Message::AppendUriQueryOption(const char *aUriQuery)
|
||||
return AppendStringOption(OT_COAP_OPTION_URI_QUERY, aUriQuery);
|
||||
}
|
||||
|
||||
const otCoapOption *Message::GetFirstOption(void)
|
||||
{
|
||||
const otCoapOption *option = NULL;
|
||||
|
||||
GetHelpData().ClearOption();
|
||||
|
||||
VerifyOrExit(GetLength() - GetHelpData().mHeaderOffset >= GetOptionStart());
|
||||
|
||||
GetHelpData().mNextOptionOffset = GetHelpData().mHeaderOffset + GetOptionStart();
|
||||
|
||||
if (GetHelpData().mNextOptionOffset < GetLength())
|
||||
{
|
||||
option = GetNextOption();
|
||||
}
|
||||
|
||||
exit:
|
||||
return option;
|
||||
}
|
||||
|
||||
const otCoapOption *Message::GetNextOption(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint16_t optionDelta;
|
||||
uint16_t optionLength;
|
||||
uint8_t buf[kMaxOptionHeaderSize];
|
||||
uint8_t * cur = buf + 1;
|
||||
otCoapOption *rval = NULL;
|
||||
|
||||
VerifyOrExit(GetHelpData().mNextOptionOffset < GetLength(), error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
Read(GetHelpData().mNextOptionOffset, sizeof(buf), buf);
|
||||
|
||||
optionDelta = buf[0] >> 4;
|
||||
optionLength = buf[0] & 0xf;
|
||||
GetHelpData().mNextOptionOffset += sizeof(uint8_t);
|
||||
|
||||
if (optionDelta < kOption1ByteExtension)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (optionDelta == kOption1ByteExtension)
|
||||
{
|
||||
optionDelta = kOption1ByteExtensionOffset + cur[0];
|
||||
GetHelpData().mNextOptionOffset += sizeof(uint8_t);
|
||||
cur++;
|
||||
}
|
||||
else if (optionDelta == kOption2ByteExtension)
|
||||
{
|
||||
optionDelta = kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
|
||||
GetHelpData().mNextOptionOffset += sizeof(uint16_t);
|
||||
cur += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RFC7252 (Section 3):
|
||||
// Reserved for payload marker.
|
||||
VerifyOrExit(optionLength == 0xf, error = OT_ERROR_PARSE);
|
||||
|
||||
// The presence of a marker followed by a zero-length payload MUST be processed
|
||||
// as a message format error.
|
||||
VerifyOrExit(GetHelpData().mNextOptionOffset < GetLength(), error = OT_ERROR_PARSE);
|
||||
|
||||
ExitNow(error = OT_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (optionLength < kOption1ByteExtension)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (optionLength == kOption1ByteExtension)
|
||||
{
|
||||
optionLength = kOption1ByteExtensionOffset + cur[0];
|
||||
GetHelpData().mNextOptionOffset += sizeof(uint8_t);
|
||||
}
|
||||
else if (optionLength == kOption2ByteExtension)
|
||||
{
|
||||
optionLength = kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
|
||||
GetHelpData().mNextOptionOffset += sizeof(uint16_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitNow(error = OT_ERROR_PARSE);
|
||||
}
|
||||
|
||||
VerifyOrExit(optionLength <= GetLength() - GetHelpData().mNextOptionOffset, error = OT_ERROR_PARSE);
|
||||
|
||||
rval = &GetHelpData().mOption;
|
||||
rval->mNumber += optionDelta;
|
||||
rval->mLength = optionLength;
|
||||
|
||||
GetHelpData().mNextOptionOffset += optionLength;
|
||||
|
||||
exit:
|
||||
if (error == OT_ERROR_PARSE)
|
||||
{
|
||||
GetHelpData().mNextOptionOffset = 0;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
otError Message::GetOptionValue(void *aValue) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
const otCoapOption &option = GetHelpData().mOption;
|
||||
|
||||
VerifyOrExit(GetHelpData().mNextOptionOffset > 0, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
VerifyOrExit(Read(GetHelpData().mNextOptionOffset - option.mLength, option.mLength, aValue) == option.mLength,
|
||||
error = OT_ERROR_PARSE);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
otError Message::SetPayloadMarker(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
@@ -340,7 +225,8 @@ exit:
|
||||
|
||||
otError Message::ParseHeader(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
otError error = OT_ERROR_NONE;
|
||||
OptionIterator iterator;
|
||||
|
||||
assert(mBuffer.mHead.mInfo.mReserved >=
|
||||
sizeof(GetHelpData()) +
|
||||
@@ -351,12 +237,13 @@ otError Message::ParseHeader(void)
|
||||
GetHelpData().mHeaderOffset = GetOffset();
|
||||
Read(GetHelpData().mHeaderOffset, sizeof(GetHelpData().mHeader), &GetHelpData().mHeader);
|
||||
|
||||
for (const otCoapOption *option = GetFirstOption(); option != NULL; option = GetNextOption())
|
||||
SuccessOrExit(error = iterator.Init(this));
|
||||
for (const otCoapOption *option = iterator.GetFirstOption(); option != NULL; option = iterator.GetNextOption())
|
||||
{
|
||||
}
|
||||
|
||||
VerifyOrExit(GetHelpData().mNextOptionOffset > 0, error = OT_ERROR_PARSE);
|
||||
GetHelpData().mHeaderLength = GetHelpData().mNextOptionOffset - GetHelpData().mHeaderOffset;
|
||||
VerifyOrExit(iterator.mNextOptionOffset > 0, error = OT_ERROR_PARSE);
|
||||
GetHelpData().mHeaderLength = iterator.mNextOptionOffset - GetHelpData().mHeaderOffset;
|
||||
MoveOffset(GetHelpData().mHeaderLength);
|
||||
|
||||
exit:
|
||||
@@ -502,5 +389,141 @@ const char *Message::CodeToString(void) const
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_COAP_API_ENABLE
|
||||
|
||||
otError OptionIterator::Init(const Message *aMessage)
|
||||
{
|
||||
otError err = OT_ERROR_NONE;
|
||||
|
||||
/*
|
||||
* Check that:
|
||||
* Length - Offset: the length of the payload
|
||||
* is greater than:
|
||||
* Start position of options
|
||||
*
|
||||
* → Check options start before the message ends, or bail ::Init with
|
||||
* OT_ERROR_PARSE as the reason.
|
||||
*/
|
||||
VerifyOrExit(aMessage->GetLength() - aMessage->GetHelpData().mHeaderOffset >= aMessage->GetOptionStart(),
|
||||
err = OT_ERROR_PARSE);
|
||||
|
||||
mMessage = aMessage;
|
||||
GetFirstOption();
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
const otCoapOption *OptionIterator::GetFirstOption(void)
|
||||
{
|
||||
const otCoapOption *option = NULL;
|
||||
const Message & message = GetMessage();
|
||||
|
||||
ClearOption();
|
||||
|
||||
mNextOptionOffset = message.GetHelpData().mHeaderOffset + message.GetOptionStart();
|
||||
|
||||
if (mNextOptionOffset < message.GetLength())
|
||||
{
|
||||
option = GetNextOption();
|
||||
}
|
||||
|
||||
return option;
|
||||
}
|
||||
|
||||
const otCoapOption *OptionIterator::GetNextOption(void)
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
uint16_t optionDelta;
|
||||
uint16_t optionLength;
|
||||
uint8_t buf[Message::kMaxOptionHeaderSize];
|
||||
uint8_t * cur = buf + 1;
|
||||
otCoapOption * rval = NULL;
|
||||
const Message &message = GetMessage();
|
||||
|
||||
VerifyOrExit(mNextOptionOffset < message.GetLength(), error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
message.Read(mNextOptionOffset, sizeof(buf), buf);
|
||||
|
||||
optionDelta = buf[0] >> 4;
|
||||
optionLength = buf[0] & 0xf;
|
||||
mNextOptionOffset += sizeof(uint8_t);
|
||||
|
||||
if (optionDelta < Message::kOption1ByteExtension)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (optionDelta == Message::kOption1ByteExtension)
|
||||
{
|
||||
optionDelta = Message::kOption1ByteExtensionOffset + cur[0];
|
||||
mNextOptionOffset += sizeof(uint8_t);
|
||||
cur++;
|
||||
}
|
||||
else if (optionDelta == Message::kOption2ByteExtension)
|
||||
{
|
||||
optionDelta = Message::kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
|
||||
mNextOptionOffset += sizeof(uint16_t);
|
||||
cur += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RFC7252 (Section 3):
|
||||
// Reserved for payload marker.
|
||||
VerifyOrExit(optionLength == 0xf, error = OT_ERROR_PARSE);
|
||||
|
||||
// The presence of a marker followed by a zero-length payload MUST be processed
|
||||
// as a message format error.
|
||||
VerifyOrExit(mNextOptionOffset < message.GetLength(), error = OT_ERROR_PARSE);
|
||||
|
||||
ExitNow(error = OT_ERROR_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (optionLength < Message::kOption1ByteExtension)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (optionLength == Message::kOption1ByteExtension)
|
||||
{
|
||||
optionLength = Message::kOption1ByteExtensionOffset + cur[0];
|
||||
mNextOptionOffset += sizeof(uint8_t);
|
||||
}
|
||||
else if (optionLength == Message::kOption2ByteExtension)
|
||||
{
|
||||
optionLength = Message::kOption2ByteExtensionOffset + static_cast<uint16_t>((cur[0] << 8) | cur[1]);
|
||||
mNextOptionOffset += sizeof(uint16_t);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExitNow(error = OT_ERROR_PARSE);
|
||||
}
|
||||
|
||||
VerifyOrExit(optionLength <= message.GetLength() - mNextOptionOffset, error = OT_ERROR_PARSE);
|
||||
|
||||
rval = &mOption;
|
||||
rval->mNumber += optionDelta;
|
||||
rval->mLength = optionLength;
|
||||
|
||||
mNextOptionOffset += optionLength;
|
||||
|
||||
exit:
|
||||
if (error == OT_ERROR_PARSE)
|
||||
{
|
||||
mNextOptionOffset = 0;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
otError OptionIterator::GetOptionValue(void *aValue) const
|
||||
{
|
||||
otError error = OT_ERROR_NONE;
|
||||
|
||||
VerifyOrExit(mNextOptionOffset > 0, error = OT_ERROR_NOT_FOUND);
|
||||
|
||||
VerifyOrExit(GetMessage().Read(mNextOptionOffset - mOption.mLength, mOption.mLength, aValue) == mOption.mLength,
|
||||
error = OT_ERROR_PARSE);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
} // namespace Coap
|
||||
} // namespace ot
|
||||
|
||||
@@ -67,12 +67,16 @@ using ot::Encoding::BigEndian::HostSwap16;
|
||||
*
|
||||
*/
|
||||
|
||||
class OptionIterator;
|
||||
|
||||
/**
|
||||
* This class implements CoAP message generation and parsing.
|
||||
*
|
||||
*/
|
||||
class Message : public ot::Message
|
||||
{
|
||||
friend class OptionIterator;
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
@@ -379,27 +383,6 @@ public:
|
||||
*/
|
||||
otError AppendUriQueryOption(const char *aUriQuery);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to the first option.
|
||||
*
|
||||
*/
|
||||
const otCoapOption *GetFirstOption(void);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to the next option.
|
||||
*
|
||||
*/
|
||||
const otCoapOption *GetNextOption(void);
|
||||
|
||||
/**
|
||||
* This function fills current option value into @p aValue.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully filled value.
|
||||
* @retval OT_ERROR_NOT_FOUND No more options, aIterator->mNextOptionOffset is set to offset of payload.
|
||||
*
|
||||
*/
|
||||
otError GetOptionValue(void *aValue) const;
|
||||
|
||||
/**
|
||||
* This method adds Payload Marker indicating beginning of the payload to the CoAP header.
|
||||
*
|
||||
@@ -586,14 +569,11 @@ private:
|
||||
struct HelpData
|
||||
{
|
||||
void Clear(void) { memset(this, 0, sizeof(*this)); }
|
||||
void ClearOption(void) { memset(&mOption, 0, sizeof(mOption)); }
|
||||
|
||||
Header mHeader;
|
||||
otCoapOption mOption;
|
||||
uint16_t mNextOptionOffset; ///< The byte offset for the next CoAP Option
|
||||
uint16_t mOptionLast;
|
||||
uint16_t mHeaderOffset; ///< The byte offset for the CoAP Header
|
||||
uint16_t mHeaderLength;
|
||||
Header mHeader;
|
||||
uint16_t mOptionLast;
|
||||
uint16_t mHeaderOffset; ///< The byte offset for the CoAP Header
|
||||
uint16_t mHeaderLength;
|
||||
};
|
||||
|
||||
const HelpData &GetHelpData(void) const
|
||||
@@ -607,6 +587,46 @@ private:
|
||||
HelpData &GetHelpData(void) { return const_cast<HelpData &>(static_cast<const Message *>(this)->GetHelpData()); }
|
||||
};
|
||||
|
||||
class OptionIterator : public ::otCoapOptionIterator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initialise the state of the iterator to iterate over the given message.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully initialised
|
||||
* @retval OT_ERROR_PARSE Message state is inconsistent
|
||||
*
|
||||
*/
|
||||
otError Init(const Message *aMessage);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to the first option.
|
||||
*
|
||||
* @returns A pointer to the first option. If no option is present NULL pointer is returned.
|
||||
*/
|
||||
const otCoapOption *GetFirstOption(void);
|
||||
|
||||
/**
|
||||
* This method returns a pointer to the next option.
|
||||
*
|
||||
* @returns A pointer to the next option. If no more options are present NULL pointer is returned.
|
||||
*/
|
||||
const otCoapOption *GetNextOption(void);
|
||||
|
||||
/**
|
||||
* This function fills current option value into @p aValue.
|
||||
*
|
||||
* @retval OT_ERROR_NONE Successfully filled value.
|
||||
* @retval OT_ERROR_NOT_FOUND No more options, mNextOptionOffset is set to offset of payload.
|
||||
*
|
||||
*/
|
||||
otError GetOptionValue(void *aValue) const;
|
||||
|
||||
private:
|
||||
void ClearOption(void) { memset(&mOption, 0, sizeof(mOption)); }
|
||||
const Message &GetMessage(void) const { return *static_cast<const Message *>(mMessage); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user