mirror of
https://github.com/espressif/openthread.git
synced 2026-10-11 18:27:43 +00:00
[CoAP Server] Message deduplication (#1500)
* Message deduplication in CoAP server. * Limit number of responses in CoAP server deduplication queue.
This commit is contained in:
@@ -924,6 +924,8 @@ typedef struct otBufferInfo
|
||||
uint16_t mArpBuffers; ///< The number of buffers in the ARP send queue.
|
||||
uint16_t mCoapClientMessages; ///< The number of messages in the CoAP client send queue.
|
||||
uint16_t mCoapClientBuffers; ///< The number of buffers in the CoAP client send queue.
|
||||
uint16_t mCoapServerMessages; ///< The number of messages in the CoAP server responses queue.
|
||||
uint16_t mCoapServerBuffers; ///< The number of buffers in the CoAP server responses queue.
|
||||
} otBufferInfo;
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -433,7 +433,8 @@ void Interpreter::ProcessBufferInfo(int argc, char *argv[])
|
||||
sServer->OutputFormat("mpl: %d %d\r\n", bufferInfo.mMplMessages, bufferInfo.mMplBuffers);
|
||||
sServer->OutputFormat("mle: %d %d\r\n", bufferInfo.mMleMessages, bufferInfo.mMleBuffers);
|
||||
sServer->OutputFormat("arp: %d %d\r\n", bufferInfo.mArpMessages, bufferInfo.mArpBuffers);
|
||||
sServer->OutputFormat("coap: %d %d\r\n", bufferInfo.mCoapClientMessages, bufferInfo.mCoapClientBuffers);
|
||||
sServer->OutputFormat("coap client: %d %d\r\n", bufferInfo.mCoapClientMessages, bufferInfo.mCoapClientBuffers);
|
||||
sServer->OutputFormat("coap server: %d %d\r\n", bufferInfo.mCoapServerMessages, bufferInfo.mCoapServerBuffers);
|
||||
|
||||
AppendResult(kThreadError_None);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ otInstance::otInstance(void) :
|
||||
, mLinkRaw(*this)
|
||||
#endif // OPENTHREAD_ENABLE_RAW_LINK_API
|
||||
#if OPENTHREAD_ENABLE_APPLICATION_COAP
|
||||
, mApplicationCoapServer(mIp6.mUdp, OT_DEFAULT_COAP_PORT)
|
||||
, mApplicationCoapServer(mThreadNetif, OT_DEFAULT_COAP_PORT)
|
||||
#endif // OPENTHREAD_ENABLE_APPLICATION_COAP
|
||||
#if OPENTHREAD_CONFIG_ENABLE_DYNAMIC_LOG_LEVEL
|
||||
, mLogLevel(static_cast<otLogLevel>(OPENTHREAD_CONFIG_LOG_LEVEL))
|
||||
|
||||
@@ -173,6 +173,9 @@ void otMessageGetBufferInfo(otInstance *aInstance, otBufferInfo *aBufferInfo)
|
||||
|
||||
aInstance->mThreadNetif.GetCoapClient().GetRequestMessages().GetInfo(aBufferInfo->mCoapClientMessages,
|
||||
aBufferInfo->mCoapClientBuffers);
|
||||
|
||||
aInstance->mThreadNetif.GetCoapServer().GetCachedResponses().GetInfo(aBufferInfo->mCoapServerMessages,
|
||||
aBufferInfo->mCoapServerBuffers);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -51,6 +51,33 @@ namespace Coap {
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Protocol Constants (RFC 7252).
|
||||
*
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAckTimeout = OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT,
|
||||
kAckRandomFactorNumerator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR,
|
||||
kAckRandomFactorDenominator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR,
|
||||
kMaxRetransmit = OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT,
|
||||
kNStart = 1,
|
||||
kDefaultLeisure = 5,
|
||||
kProbingRate = 1,
|
||||
|
||||
// Note that 2 << (kMaxRetransmit - 1) is equal to kMaxRetransmit power of 2
|
||||
kMaxTransmitSpan = kAckTimeout * ((2 << (kMaxRetransmit - 1)) - 1) *
|
||||
kAckRandomFactorNumerator / kAckRandomFactorDenominator,
|
||||
kMaxTransmitWait = kAckTimeout * ((2 << kMaxRetransmit) - 1) *
|
||||
kAckRandomFactorNumerator / kAckRandomFactorDenominator,
|
||||
kMaxLatency = 100,
|
||||
kProcessingDelay = kAckTimeout,
|
||||
kMaxRtt = 2 * kMaxLatency + kProcessingDelay,
|
||||
kExchangeLifetime = kMaxTransmitSpan + 2 * (kMaxLatency) + kProcessingDelay,
|
||||
kNonLifetime = kMaxTransmitSpan + kMaxLatency
|
||||
};
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
kMeshCoPMessagePriority = Message::kPriorityHigh, // The priority for MeshCoP message
|
||||
|
||||
@@ -87,7 +87,7 @@ ThreadError Client::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMess
|
||||
Message *storedCopy = NULL;
|
||||
uint16_t copyLength = 0;
|
||||
|
||||
SuccessOrExit(error = header.FromMessage(aMessage, false));
|
||||
SuccessOrExit(error = header.FromMessage(aMessage, 0));
|
||||
|
||||
// Set Message Id if it was not already set.
|
||||
if (header.GetMessageId() == 0)
|
||||
@@ -335,7 +335,7 @@ Message *Client::FindRelatedRequest(const Header &aResponseHeader, const Ip6::Me
|
||||
aRequestMetadata.mDestinationAddress.IsAnycastRoutingLocator()) &&
|
||||
(aRequestMetadata.mDestinationPort == aMessageInfo.GetPeerPort()))
|
||||
{
|
||||
assert(aRequestHeader.FromMessage(*message, true) == kThreadError_None);
|
||||
assert(aRequestHeader.FromMessage(*message, sizeof(RequestMetadata)) == kThreadError_None);
|
||||
|
||||
switch (aResponseHeader.GetType())
|
||||
{
|
||||
@@ -387,7 +387,7 @@ void Client::ProcessReceivedMessage(Message &aMessage, const Ip6::MessageInfo &a
|
||||
Message *message = NULL;
|
||||
ThreadError error;
|
||||
|
||||
SuccessOrExit(error = responseHeader.FromMessage(aMessage, false));
|
||||
SuccessOrExit(error = responseHeader.FromMessage(aMessage, 0));
|
||||
aMessage.MoveOffset(responseHeader.GetLength());
|
||||
|
||||
message = FindRelatedRequest(responseHeader, aMessageInfo, requestHeader, requestMetadata);
|
||||
|
||||
@@ -48,32 +48,6 @@ namespace Coap {
|
||||
|
||||
class Client;
|
||||
|
||||
/**
|
||||
* Protocol Constants (RFC 7252).
|
||||
*
|
||||
*/
|
||||
enum
|
||||
{
|
||||
kAckTimeout = OPENTHREAD_CONFIG_COAP_ACK_TIMEOUT,
|
||||
kAckRandomFactorNumerator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_NUMERATOR,
|
||||
kAckRandomFactorDenominator = OPENTHREAD_CONFIG_COAP_ACK_RANDOM_FACTOR_DENOMINATOR,
|
||||
kMaxRetransmit = OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT,
|
||||
kNStart = 1,
|
||||
kDefaultLeisure = 5,
|
||||
kProbingRate = 1,
|
||||
|
||||
// Note that 2 << (kMaxRetransmit - 1) is equal to kMaxRetransmit power of 2
|
||||
kMaxTransmitSpan = kAckTimeout * ((2 << (kMaxRetransmit - 1)) - 1) *
|
||||
kAckRandomFactorNumerator / kAckRandomFactorDenominator,
|
||||
kMaxTransmitWait = kAckTimeout * ((2 << kMaxRetransmit) - 1) *
|
||||
kAckRandomFactorNumerator / kAckRandomFactorDenominator,
|
||||
kMaxLatency = 100,
|
||||
kProcessingDelay = kAckTimeout,
|
||||
kMaxRtt = 2 * kMaxLatency + kProcessingDelay,
|
||||
kExchangeLifetime = kMaxTransmitSpan + 2 * (kMaxLatency) + kProcessingDelay,
|
||||
kNonLifetime = kMaxTransmitSpan + kMaxLatency
|
||||
};
|
||||
|
||||
/**
|
||||
* This class implements metadata required for CoAP retransmission.
|
||||
*
|
||||
|
||||
@@ -59,7 +59,7 @@ void Header::Init(Type aType, Code aCode)
|
||||
SetCode(aCode);
|
||||
}
|
||||
|
||||
ThreadError Header::FromMessage(const Message &aMessage, bool aCopiedMessage)
|
||||
ThreadError Header::FromMessage(const Message &aMessage, uint16_t aMetadataSize)
|
||||
{
|
||||
ThreadError error = kThreadError_Parse;
|
||||
uint16_t offset = aMessage.GetOffset();
|
||||
@@ -69,10 +69,7 @@ ThreadError Header::FromMessage(const Message &aMessage, bool aCopiedMessage)
|
||||
uint16_t optionDelta;
|
||||
uint16_t optionLength;
|
||||
|
||||
if (aCopiedMessage)
|
||||
{
|
||||
length -= sizeof(RequestMetadata);
|
||||
}
|
||||
length -= aMetadataSize;
|
||||
|
||||
VerifyOrExit(length >= kTokenOffset, error = kThreadError_Parse);
|
||||
aMessage.Read(offset, kTokenOffset, mHeader.mBytes);
|
||||
|
||||
@@ -109,13 +109,13 @@ public:
|
||||
* This method parses the CoAP header from a message.
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
* @param[in] aCopiedMessage To indicate if this message is a copied one or not
|
||||
* @param[in] aMetadataSize A size of metadata appended to the message.
|
||||
*
|
||||
* @retval kThreadError_None Successfully parsed the message.
|
||||
* @retval kThreadError_Parse Failed to parse the message.
|
||||
*
|
||||
*/
|
||||
ThreadError FromMessage(const Message &aMessage, bool aCopiedMessage);
|
||||
ThreadError FromMessage(const Message &aMessage, uint16_t aMetadataSize);
|
||||
|
||||
/**
|
||||
* This method returns the Version value.
|
||||
|
||||
@@ -33,12 +33,14 @@
|
||||
|
||||
#include <coap/coap_server.hpp>
|
||||
#include <common/code_utils.hpp>
|
||||
#include <net/ip6.hpp>
|
||||
|
||||
namespace Thread {
|
||||
namespace Coap {
|
||||
|
||||
Server::Server(Ip6::Udp &aUdp, uint16_t aPort, SenderFunction aSender, ReceiverFunction aReceiver):
|
||||
CoapBase(aUdp, aSender, aReceiver)
|
||||
Server::Server(Ip6::Netif &aNetif, uint16_t aPort, SenderFunction aSender, ReceiverFunction aReceiver):
|
||||
CoapBase(aNetif.GetIp6().mUdp, aSender, aReceiver),
|
||||
mResponsesQueue(aNetif)
|
||||
{
|
||||
mPort = aPort;
|
||||
mResources = NULL;
|
||||
@@ -54,6 +56,8 @@ ThreadError Server::Start(void)
|
||||
|
||||
ThreadError Server::Stop(void)
|
||||
{
|
||||
mResponsesQueue.DequeueAllResponses();
|
||||
|
||||
return CoapBase::Stop();
|
||||
}
|
||||
|
||||
@@ -114,6 +118,8 @@ exit:
|
||||
|
||||
ThreadError Server::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
mResponsesQueue.EnqueueResponse(aMessage, aMessageInfo);
|
||||
|
||||
return mSender(this, aMessage, aMessageInfo);
|
||||
}
|
||||
|
||||
@@ -147,10 +153,26 @@ void Server::ProcessReceivedMessage(Message &aMessage, const Ip6::MessageInfo &a
|
||||
char uriPath[Resource::kMaxReceivedUriPath] = "";
|
||||
char *curUriPath = uriPath;
|
||||
const Header::Option *coapOption;
|
||||
Message *response;
|
||||
|
||||
SuccessOrExit(header.FromMessage(aMessage, false));
|
||||
SuccessOrExit(header.FromMessage(aMessage, 0));
|
||||
aMessage.MoveOffset(header.GetLength());
|
||||
|
||||
switch (mResponsesQueue.GetMatchedResponseCopy(header, aMessageInfo, &response))
|
||||
{
|
||||
case kThreadError_None:
|
||||
SendMessage(*response, aMessageInfo);
|
||||
|
||||
// fall through
|
||||
|
||||
case kThreadError_NoBufs:
|
||||
ExitNow();
|
||||
|
||||
case kThreadError_NotFound:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
coapOption = header.GetCurrentOption();
|
||||
|
||||
while (coapOption != NULL)
|
||||
@@ -204,5 +226,174 @@ ThreadError Server::SetPort(uint16_t aPort)
|
||||
return mSocket.Bind(sockaddr);
|
||||
}
|
||||
|
||||
ThreadError ResponsesQueue::GetMatchedResponseCopy(const Header &aHeader,
|
||||
const Ip6::MessageInfo &aMessageInfo,
|
||||
Message **aResponse)
|
||||
{
|
||||
ThreadError error = kThreadError_NotFound;
|
||||
Message *message;
|
||||
EnqueuedResponseHeader enqueuedResponseHeader;
|
||||
Ip6::MessageInfo messageInfo;
|
||||
Header header;
|
||||
|
||||
for (message = mQueue.GetHead(); message != NULL; message = message->GetNext())
|
||||
{
|
||||
enqueuedResponseHeader.ReadFrom(*message);
|
||||
messageInfo = enqueuedResponseHeader.GetMessageInfo();
|
||||
|
||||
// Check source endpoint
|
||||
if (messageInfo.GetPeerPort() != aMessageInfo.GetPeerPort())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (messageInfo.GetPeerAddr() != aMessageInfo.GetPeerAddr())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check Message Id
|
||||
if (header.FromMessage(*message, sizeof(EnqueuedResponseHeader)) != kThreadError_None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header.GetMessageId() != aHeader.GetMessageId())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
*aResponse = message->Clone();
|
||||
VerifyOrExit(*aResponse != NULL, error = kThreadError_NoBufs);
|
||||
|
||||
EnqueuedResponseHeader::RemoveFrom(**aResponse);
|
||||
|
||||
error = kThreadError_None;
|
||||
break;
|
||||
}
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
ThreadError ResponsesQueue::GetMatchedResponseCopy(const Message &aRequest,
|
||||
const Ip6::MessageInfo &aMessageInfo,
|
||||
Message **aResponse)
|
||||
{
|
||||
ThreadError error = kThreadError_None;
|
||||
Header header;
|
||||
|
||||
SuccessOrExit(error = header.FromMessage(aRequest, 0));
|
||||
|
||||
error = GetMatchedResponseCopy(header, aMessageInfo, aResponse);
|
||||
|
||||
exit:
|
||||
return error;
|
||||
}
|
||||
|
||||
void ResponsesQueue::EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
|
||||
{
|
||||
Header header;
|
||||
Message *copy;
|
||||
EnqueuedResponseHeader enqueuedResponseHeader(aMessageInfo);
|
||||
uint16_t messageCount;
|
||||
uint16_t bufferCount;
|
||||
|
||||
SuccessOrExit(header.FromMessage(aMessage, 0));
|
||||
VerifyOrExit(header.GetType() == kCoapTypeAcknowledgment ||
|
||||
header.GetType() == kCoapTypeReset,);
|
||||
|
||||
switch (GetMatchedResponseCopy(aMessage, aMessageInfo, ©))
|
||||
{
|
||||
case kThreadError_NotFound:
|
||||
break;
|
||||
|
||||
case kThreadError_None:
|
||||
copy->Free();
|
||||
|
||||
// fall through
|
||||
|
||||
case kThreadError_NoBufs:
|
||||
default:
|
||||
ExitNow();
|
||||
}
|
||||
|
||||
mQueue.GetInfo(messageCount, bufferCount);
|
||||
|
||||
if (messageCount >= kMaxCachedResponses)
|
||||
{
|
||||
DequeueOldestResponse();
|
||||
}
|
||||
|
||||
copy = aMessage.Clone();
|
||||
VerifyOrExit(copy != NULL,);
|
||||
|
||||
enqueuedResponseHeader.AppendTo(*copy);
|
||||
mQueue.Enqueue(*copy);
|
||||
|
||||
if (!mTimer.IsRunning())
|
||||
{
|
||||
mTimer.Start(Timer::SecToMsec(kExchangeLifetime));
|
||||
}
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void ResponsesQueue::DequeueOldestResponse(void)
|
||||
{
|
||||
Message *message;
|
||||
|
||||
VerifyOrExit((message = mQueue.GetHead()) != NULL,);
|
||||
DequeueResponse(*message);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void ResponsesQueue::DequeueAllResponses(void)
|
||||
{
|
||||
Message *message;
|
||||
|
||||
while ((message = mQueue.GetHead()) != NULL)
|
||||
{
|
||||
DequeueResponse(*message);
|
||||
}
|
||||
}
|
||||
|
||||
void ResponsesQueue::HandleTimer(void *aContext)
|
||||
{
|
||||
static_cast<ResponsesQueue *>(aContext)->HandleTimer();
|
||||
}
|
||||
|
||||
void ResponsesQueue::HandleTimer(void)
|
||||
{
|
||||
Message *message;
|
||||
EnqueuedResponseHeader enqueuedResponseHeader;
|
||||
|
||||
while ((message = mQueue.GetHead()) != NULL)
|
||||
{
|
||||
enqueuedResponseHeader.ReadFrom(*message);
|
||||
|
||||
if (enqueuedResponseHeader.IsEarlier(Timer::GetNow()))
|
||||
{
|
||||
DequeueResponse(*message);
|
||||
}
|
||||
else
|
||||
{
|
||||
mTimer.Start(enqueuedResponseHeader.GetRemainingTime());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EnqueuedResponseHeader::GetRemainingTime(void) const
|
||||
{
|
||||
int32_t remainingTime = static_cast<int32_t>(mDequeueTime - Timer::GetNow());
|
||||
|
||||
return remainingTime >= 0 ? static_cast<uint32_t>(remainingTime) : 0;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Coap
|
||||
} // namespace Thread
|
||||
|
||||
@@ -38,7 +38,10 @@
|
||||
|
||||
#include <coap/coap_base.hpp>
|
||||
#include <coap/coap_header.hpp>
|
||||
#include <common/debug.hpp>
|
||||
#include <common/message.hpp>
|
||||
#include <common/timer.hpp>
|
||||
#include <net/ip6.hpp>
|
||||
#include <net/udp6.hpp>
|
||||
|
||||
namespace Thread {
|
||||
@@ -93,6 +96,190 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class implements metadata required for caching CoAP responses.
|
||||
*
|
||||
*/
|
||||
class EnqueuedResponseHeader
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor creating empty object.
|
||||
*
|
||||
*/
|
||||
EnqueuedResponseHeader(void): mDequeueTime(0), mMessageInfo() {}
|
||||
|
||||
/**
|
||||
* Constructor creating object with valid dequeue time and message info.
|
||||
*
|
||||
* @param[in] aMessageInfo The message info containing source endpoint identification.
|
||||
*
|
||||
*/
|
||||
EnqueuedResponseHeader(const Ip6::MessageInfo &aMessageInfo):
|
||||
mDequeueTime(Timer::GetNow() + Timer::SecToMsec(kExchangeLifetime)),
|
||||
mMessageInfo(aMessageInfo) {}
|
||||
|
||||
/**
|
||||
* This method append metadata to the message.
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
*
|
||||
* @retval kThreadError_None Successfully appended the bytes.
|
||||
* @retval kThreadError_NoBufs Insufficient available buffers to grow the message.
|
||||
*/
|
||||
ThreadError AppendTo(Message &aMessage) const { return aMessage.Append(this, sizeof(*this)); }
|
||||
|
||||
/**
|
||||
* This method reads request data from the message.
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
*
|
||||
* @returns The number of bytes read.
|
||||
*
|
||||
*/
|
||||
uint16_t ReadFrom(const Message &aMessage) {
|
||||
return aMessage.Read(aMessage.GetLength() - sizeof(*this), sizeof(*this), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method removes metadata from the message.
|
||||
*
|
||||
* @param[in] aMessage A reference to the message.
|
||||
*
|
||||
*/
|
||||
static void RemoveFrom(Message &aMessage) {
|
||||
assert(aMessage.SetLength(aMessage.GetLength() - sizeof(EnqueuedResponseHeader)) == kThreadError_None);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if the message shall be sent before the given time.
|
||||
*
|
||||
* @param[in] aTime A time to compare.
|
||||
*
|
||||
* @retval TRUE If the message shall be sent before the given time.
|
||||
* @retval FALSE Otherwise.
|
||||
*
|
||||
*/
|
||||
bool IsEarlier(uint32_t aTime) const { return (static_cast<int32_t>(aTime - mDequeueTime) > 0); }
|
||||
|
||||
/**
|
||||
* This method returns number of milliseconds in which the message should be sent.
|
||||
*
|
||||
* @returns The number of milliseconds in which the message should be sent.
|
||||
*
|
||||
*/
|
||||
uint32_t GetRemainingTime(void) const;
|
||||
|
||||
/**
|
||||
* This method returns the message info of cached CoAP response.
|
||||
*
|
||||
* @returns The message info of the cached CoAP response.
|
||||
*
|
||||
*/
|
||||
const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; }
|
||||
|
||||
private:
|
||||
uint32_t mDequeueTime;
|
||||
const Ip6::MessageInfo mMessageInfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class caches CoAP responses to implement message deduplication.
|
||||
*
|
||||
*/
|
||||
class ResponsesQueue
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default class constructor.
|
||||
*
|
||||
* @param[in] aNetif A reference to the network interface that CoAP server should be assigned to.
|
||||
*
|
||||
*/
|
||||
ResponsesQueue(Ip6::Netif &aNetif):
|
||||
mTimer(aNetif.GetIp6().mTimerScheduler, &ResponsesQueue::HandleTimer, this) {}
|
||||
|
||||
/**
|
||||
* Add given response to the cache.
|
||||
*
|
||||
* If matching response (the same Message ID, source endpoint address and port) exists in the cache given
|
||||
* response is not added.
|
||||
* The CoAP response is copied before it is added to the cache.
|
||||
*
|
||||
* @param[in] aMessage The CoAP response to add to the cache.
|
||||
* @param[in] aMessageInfo The message info corresponding to @p aMessage.
|
||||
*
|
||||
*/
|
||||
void EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
|
||||
/**
|
||||
* Remove the oldest response from the cache.
|
||||
*
|
||||
*/
|
||||
void DequeueOldestResponse(void);
|
||||
|
||||
/**
|
||||
* Remove all responses from the cache.
|
||||
*
|
||||
*/
|
||||
void DequeueAllResponses(void);
|
||||
|
||||
/**
|
||||
* Get a copy of CoAP response from the cache that matches given Message ID and source endpoint.
|
||||
*
|
||||
* @param[in] aHeader The CoAP message header containing Message ID.
|
||||
* @param[in] aMessageInfo The message info containing source endpoint address and port.
|
||||
* @param[out] aResponse A pointer to a copy of a cached CoAP response matching given arguments.
|
||||
*
|
||||
* @retval kThreadError_None Matching response found and successfully created a copy.
|
||||
* @retval kThreadError_NoBufs Matching response found but there is not sufficient buffer to create a copy.
|
||||
* @retval kThreadError_NotFound Matching response not found.
|
||||
*
|
||||
*/
|
||||
ThreadError GetMatchedResponseCopy(const Header &aHeader,
|
||||
const Ip6::MessageInfo &aMessageInfo,
|
||||
Message **aResponse);
|
||||
|
||||
/**
|
||||
* Get a copy of CoAP response from the cache that matches given Message ID and source endpoint.
|
||||
*
|
||||
* @param[in] aRequest The CoAP message containing Message ID.
|
||||
* @param[in] aMessageInfo The message info containing source endpoint address and port.
|
||||
* @param[out] aResponse A pointer to a copy of a cached CoAP response matching given arguments.
|
||||
*
|
||||
* @retval kThreadError_None Matching response found and successfully created a copy.
|
||||
* @retval kThreadError_NoBufs Matching response found but there is not sufficient buffer to create a copy.
|
||||
* @retval kThreadError_NotFound Matching response not found.
|
||||
* @retval kThreadError_Parse Could not parse CoAP header in the request message.
|
||||
*
|
||||
*/
|
||||
ThreadError GetMatchedResponseCopy(const Message &aRequest,
|
||||
const Ip6::MessageInfo &aMessageInfo,
|
||||
Message **aResponse);
|
||||
|
||||
/**
|
||||
* Get a reference to the cached CoAP responses queue.
|
||||
*
|
||||
* @returns A reference to the cached CoAP responses queue.
|
||||
*
|
||||
*/
|
||||
const MessageQueue &GetResponses(void) const { return mQueue; }
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
kMaxCachedResponses = OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES,
|
||||
};
|
||||
|
||||
void DequeueResponse(Message &aMessage) { mQueue.Dequeue(aMessage); aMessage.Free(); }
|
||||
|
||||
MessageQueue mQueue;
|
||||
Timer mTimer;
|
||||
|
||||
static void HandleTimer(void *aContext);
|
||||
void HandleTimer(void);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class implements the CoAP server.
|
||||
*
|
||||
@@ -103,13 +290,13 @@ public:
|
||||
/**
|
||||
* This constructor initializes the object.
|
||||
*
|
||||
* @param[in] aUdp A reference to the UDP object.
|
||||
* @param[in] aNetif A reference to the network interface that CoAP server should be assigned to.
|
||||
* @param[in] aPort The port to listen on.
|
||||
* @param[in] aSender A pointer to a function for sending messages.
|
||||
* @param[in] aReceiver A pointer to a function for handling received messages.
|
||||
*
|
||||
*/
|
||||
Server(Ip6::Udp &aUdp, uint16_t aPort, SenderFunction aSender = &Server::Send,
|
||||
Server(Ip6::Netif &aNetif, uint16_t aPort, SenderFunction aSender = &Server::Send,
|
||||
ReceiverFunction aReceiver = &Server::Receive);
|
||||
|
||||
/**
|
||||
@@ -222,6 +409,8 @@ public:
|
||||
*/
|
||||
ThreadError SetPort(uint16_t aPort);
|
||||
|
||||
const MessageQueue &GetCachedResponses(void) const { return mResponsesQueue.GetResponses(); }
|
||||
|
||||
protected:
|
||||
void ProcessReceivedMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
|
||||
|
||||
@@ -236,8 +425,11 @@ private:
|
||||
|
||||
uint16_t mPort;
|
||||
Resource *mResources;
|
||||
|
||||
ResponsesQueue mResponsesQueue;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Thread {
|
||||
namespace Coap {
|
||||
|
||||
SecureServer::SecureServer(ThreadNetif &aNetif, uint16_t aPort):
|
||||
Server(aNetif.GetIp6().mUdp, aPort, &SecureServer::Send, &SecureServer::Receive),
|
||||
Server(aNetif, aPort, &SecureServer::Send, &SecureServer::Receive),
|
||||
mTransmitCallback(NULL),
|
||||
mContext(NULL),
|
||||
mNetif(aNetif),
|
||||
|
||||
@@ -321,6 +321,18 @@
|
||||
#define OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT 4
|
||||
#endif // OPENTHREAD_CONFIG_COAP_MAX_RETRANSMIT
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES
|
||||
*
|
||||
* Maximum number of cached responses for CoAP Confirmable messages.
|
||||
*
|
||||
* Cached responses are used for message deduplication.
|
||||
*
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES
|
||||
#define OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES 10
|
||||
#endif // OPENTHREAD_CONFIG_COAP_SERVER_MAX_CACHED_RESPONSES
|
||||
|
||||
/**
|
||||
* @def OPENTHREAD_CONFIG_DNS_RESPONSE_TIMEOUT
|
||||
*
|
||||
|
||||
@@ -55,7 +55,7 @@ static const uint8_t kThreadMasterKey[] =
|
||||
|
||||
ThreadNetif::ThreadNetif(Ip6::Ip6 &aIp6):
|
||||
Netif(aIp6, OT_NETIF_INTERFACE_ID_THREAD),
|
||||
mCoapServer(aIp6.mUdp, kCoapUdpPort),
|
||||
mCoapServer(*this, kCoapUdpPort),
|
||||
mCoapClient(*this),
|
||||
mAddressResolver(*this),
|
||||
#if OPENTHREAD_ENABLE_DHCP6_CLIENT
|
||||
|
||||
Reference in New Issue
Block a user