| /* |
| * Copyright (c) 2016, The OpenThread Authors. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #define WPP_NAME "coap.tmh" |
| |
| #include <openthread/config.h> |
| |
| #include "coap.hpp" |
| |
| #include <openthread/platform/random.h> |
| |
| #include "common/code_utils.hpp" |
| #include "common/debug.hpp" |
| #include "common/logging.hpp" |
| #include "net/ip6.hpp" |
| #include "net/udp6.hpp" |
| #include "thread/thread_netif.hpp" |
| |
| /** |
| * @file |
| * This file contains common code base for CoAP client and server. |
| */ |
| |
| namespace ot { |
| namespace Coap { |
| |
| Coap::Coap(ThreadNetif &aNetif): |
| ThreadNetifLocator(aNetif), |
| mSocket(aNetif.GetIp6().mUdp), |
| mRetransmissionTimer(aNetif.GetIp6().mTimerScheduler, &Coap::HandleRetransmissionTimer, this), |
| mResources(NULL), |
| mContext(NULL), |
| mInterceptor(NULL), |
| mResponsesQueue(aNetif), |
| mDefaultHandler(NULL), |
| mDefaultHandlerContext(NULL) |
| { |
| mMessageId = static_cast<uint16_t>(otPlatRandomGet()); |
| } |
| |
| otError Coap::Start(uint16_t aPort) |
| { |
| otError error; |
| Ip6::SockAddr sockaddr; |
| sockaddr.mPort = aPort; |
| |
| SuccessOrExit(error = mSocket.Open(&Coap::HandleUdpReceive, this)); |
| SuccessOrExit(error = mSocket.Bind(sockaddr)); |
| |
| exit: |
| return error; |
| } |
| |
| otError Coap::Stop(void) |
| { |
| Message *message = mPendingRequests.GetHead(); |
| Message *messageToRemove; |
| CoapMetadata coapMetadata; |
| |
| // Remove all pending messages. |
| while (message != NULL) |
| { |
| messageToRemove = message; |
| message = message->GetNext(); |
| |
| coapMetadata.ReadFrom(*messageToRemove); |
| FinalizeCoapTransaction(*messageToRemove, coapMetadata, NULL, NULL, NULL, OT_ERROR_ABORT); |
| } |
| |
| mResponsesQueue.DequeueAllResponses(); |
| |
| return mSocket.Close(); |
| } |
| |
| otError Coap::AddResource(Resource &aResource) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| for (Resource *cur = mResources; cur; cur = cur->GetNext()) |
| { |
| VerifyOrExit(cur != &aResource, error = OT_ERROR_ALREADY); |
| } |
| |
| aResource.mNext = mResources; |
| mResources = &aResource; |
| |
| exit: |
| return error; |
| } |
| |
| void Coap::RemoveResource(Resource &aResource) |
| { |
| if (mResources == &aResource) |
| { |
| mResources = aResource.GetNext(); |
| } |
| else |
| { |
| for (Resource *cur = mResources; cur; cur = cur->GetNext()) |
| { |
| if (cur->mNext == &aResource) |
| { |
| cur->mNext = aResource.mNext; |
| ExitNow(); |
| } |
| } |
| } |
| |
| exit: |
| aResource.mNext = NULL; |
| } |
| |
| void Coap::SetDefaultHandler(otCoapRequestHandler aHandler, void *aContext) |
| { |
| mDefaultHandler = aHandler; |
| mDefaultHandlerContext = aContext; |
| } |
| |
| Message *Coap::NewMessage(const Header &aHeader, uint8_t aPriority) |
| { |
| Message *message = NULL; |
| |
| // Ensure that header has minimum required length. |
| VerifyOrExit(aHeader.GetLength() >= Header::kMinHeaderLength); |
| |
| VerifyOrExit((message = mSocket.NewMessage(aHeader.GetLength())) != NULL); |
| message->Prepend(aHeader.GetBytes(), aHeader.GetLength()); |
| message->SetOffset(0); |
| message->SetPriority(aPriority); |
| |
| exit: |
| return message; |
| } |
| |
| otError Coap::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, |
| otCoapResponseHandler aHandler, void *aContext) |
| { |
| otError error; |
| Header header; |
| CoapMetadata coapMetadata; |
| Message *storedCopy = NULL; |
| uint16_t copyLength = 0; |
| |
| SuccessOrExit(error = header.FromMessage(aMessage, 0)); |
| |
| if ((header.GetType() == OT_COAP_TYPE_ACKNOWLEDGMENT || header.GetType() == OT_COAP_TYPE_RESET) && |
| header.GetCode() != OT_COAP_CODE_EMPTY) |
| { |
| mResponsesQueue.EnqueueResponse(aMessage, aMessageInfo); |
| } |
| |
| // Set Message Id if it was not already set. |
| if (header.GetMessageId() == 0) |
| { |
| header.SetMessageId(mMessageId++); |
| aMessage.Write(0, Header::kMinHeaderLength, header.GetBytes()); |
| } |
| |
| if (header.IsConfirmable()) |
| { |
| // Create a copy of entire message and enqueue it. |
| copyLength = aMessage.GetLength(); |
| } |
| else if (header.IsNonConfirmable() && (aHandler != NULL)) |
| { |
| // As we do not retransmit non confirmable messages, create a copy of header only, for token information. |
| copyLength = header.GetLength(); |
| } |
| |
| if (copyLength > 0) |
| { |
| coapMetadata = CoapMetadata(header.IsConfirmable(), aMessageInfo, aHandler, aContext); |
| VerifyOrExit((storedCopy = CopyAndEnqueueMessage(aMessage, copyLength, coapMetadata)) != NULL, |
| error = OT_ERROR_NO_BUFS); |
| } |
| |
| SuccessOrExit(error = Send(aMessage, aMessageInfo)); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && storedCopy != NULL) |
| { |
| DequeueMessage(*storedCopy); |
| } |
| |
| return error; |
| } |
| |
| otError Coap::Send(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| return mSocket.SendTo(aMessage, aMessageInfo); |
| } |
| |
| otError Coap::SendEmptyMessage(Header::Type aType, const Header &aRequestHeader, |
| const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Header responseHeader; |
| Message *message = NULL; |
| |
| VerifyOrExit(aRequestHeader.GetType() == OT_COAP_TYPE_CONFIRMABLE, error = OT_ERROR_INVALID_ARGS); |
| |
| responseHeader.Init(aType, OT_COAP_CODE_EMPTY); |
| responseHeader.SetMessageId(aRequestHeader.GetMessageId()); |
| |
| VerifyOrExit((message = NewMessage(responseHeader)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(error = Send(*message, aMessageInfo)); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| otError Coap::SendHeaderResponse(Header::Code aCode, const Header &aRequestHeader, |
| const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error = OT_ERROR_NONE; |
| Header responseHeader; |
| Header::Type requestType; |
| Message *message = NULL; |
| |
| VerifyOrExit(aRequestHeader.IsRequest(), error = OT_ERROR_INVALID_ARGS); |
| |
| requestType = aRequestHeader.GetType(); |
| |
| switch (requestType) |
| { |
| case OT_COAP_TYPE_CONFIRMABLE: |
| responseHeader.Init(OT_COAP_TYPE_ACKNOWLEDGMENT, aCode); |
| responseHeader.SetMessageId(aRequestHeader.GetMessageId()); |
| break; |
| |
| case OT_COAP_TYPE_NON_CONFIRMABLE: |
| responseHeader.Init(OT_COAP_TYPE_NON_CONFIRMABLE, aCode); |
| responseHeader.SetMessageId(mMessageId++); |
| break; |
| |
| default: |
| ExitNow(error = OT_ERROR_INVALID_ARGS); |
| break; |
| } |
| |
| responseHeader.SetToken(aRequestHeader.GetToken(), aRequestHeader.GetTokenLength()); |
| |
| VerifyOrExit((message = NewMessage(responseHeader)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| SuccessOrExit(error = SendMessage(*message, aMessageInfo)); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && message != NULL) |
| { |
| message->Free(); |
| } |
| |
| return error; |
| } |
| |
| Coap &Coap::GetOwner(const Context &aContext) |
| { |
| #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES |
| Coap &coap = *static_cast<Coap *>(aContext.GetContext()); |
| #else |
| Coap &coap = otGetThreadNetif().GetCoap(); |
| OT_UNUSED_VARIABLE(aContext); |
| #endif |
| return coap; |
| } |
| |
| void Coap::HandleRetransmissionTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).HandleRetransmissionTimer(); |
| } |
| |
| void Coap::HandleRetransmissionTimer(void) |
| { |
| uint32_t now = otPlatAlarmGetNow(); |
| uint32_t nextDelta = 0xffffffff; |
| CoapMetadata coapMetadata; |
| Message *message = mPendingRequests.GetHead(); |
| Message *nextMessage = NULL; |
| Ip6::MessageInfo messageInfo; |
| |
| while (message != NULL) |
| { |
| nextMessage = message->GetNext(); |
| coapMetadata.ReadFrom(*message); |
| |
| if (coapMetadata.IsLater(now)) |
| { |
| // Calculate the next delay and choose the lowest. |
| if (coapMetadata.mNextTimerShot - now < nextDelta) |
| { |
| nextDelta = coapMetadata.mNextTimerShot - now; |
| } |
| } |
| else if ((coapMetadata.mConfirmable) && |
| (coapMetadata.mRetransmissionCount < kMaxRetransmit)) |
| { |
| // Increment retransmission counter and timer. |
| coapMetadata.mRetransmissionCount++; |
| coapMetadata.mRetransmissionTimeout *= 2; |
| coapMetadata.mNextTimerShot = now + coapMetadata.mRetransmissionTimeout; |
| coapMetadata.UpdateIn(*message); |
| |
| // Check if retransmission time is lower than current lowest. |
| if (coapMetadata.mRetransmissionTimeout < nextDelta) |
| { |
| nextDelta = coapMetadata.mRetransmissionTimeout; |
| } |
| |
| // Retransmit |
| if (!coapMetadata.mAcknowledged) |
| { |
| messageInfo.SetPeerAddr(coapMetadata.mDestinationAddress); |
| messageInfo.SetPeerPort(coapMetadata.mDestinationPort); |
| messageInfo.SetSockAddr(coapMetadata.mSourceAddress); |
| |
| SendCopy(*message, messageInfo); |
| } |
| } |
| else |
| { |
| // No expected response or acknowledgment. |
| FinalizeCoapTransaction(*message, coapMetadata, NULL, NULL, NULL, OT_ERROR_RESPONSE_TIMEOUT); |
| } |
| |
| message = nextMessage; |
| } |
| |
| if (nextDelta != 0xffffffff) |
| { |
| mRetransmissionTimer.Start(nextDelta); |
| } |
| } |
| |
| void Coap::FinalizeCoapTransaction(Message &aRequest, const CoapMetadata &aCoapMetadata, |
| Header *aResponseHeader, Message *aResponse, |
| const Ip6::MessageInfo *aMessageInfo, otError aResult) |
| { |
| DequeueMessage(aRequest); |
| |
| if (aCoapMetadata.mResponseHandler != NULL) |
| { |
| aCoapMetadata.mResponseHandler(aCoapMetadata.mResponseContext, aResponseHeader, |
| aResponse, aMessageInfo, aResult); |
| } |
| } |
| |
| otError Coap::AbortTransaction(otCoapResponseHandler aHandler, void *aContext) |
| { |
| otError error = OT_ERROR_NOT_FOUND; |
| Message *message; |
| CoapMetadata coapMetadata; |
| |
| for (message = mPendingRequests.GetHead(); message != NULL; message = message->GetNext()) |
| { |
| coapMetadata.ReadFrom(*message); |
| |
| if (coapMetadata.mResponseHandler == aHandler && coapMetadata.mResponseContext == aContext) |
| { |
| DequeueMessage(*message); |
| error = OT_ERROR_NONE; |
| } |
| } |
| |
| return error; |
| } |
| |
| |
| Message *Coap::CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, |
| const CoapMetadata &aCoapMetadata) |
| { |
| otError error = OT_ERROR_NONE; |
| Message *messageCopy = NULL; |
| uint32_t alarmFireTime; |
| |
| // Create a message copy of requested size. |
| VerifyOrExit((messageCopy = aMessage.Clone(aCopyLength)) != NULL, error = OT_ERROR_NO_BUFS); |
| |
| // Append the copy with retransmission data. |
| SuccessOrExit(error = aCoapMetadata.AppendTo(*messageCopy)); |
| |
| // Setup the timer. |
| if (mRetransmissionTimer.IsRunning()) |
| { |
| // If timer is already running, check if it should be restarted with earlier fire time. |
| alarmFireTime = mRetransmissionTimer.GetFireTime(); |
| |
| if (aCoapMetadata.IsEarlier(alarmFireTime)) |
| { |
| mRetransmissionTimer.Start(aCoapMetadata.mRetransmissionTimeout); |
| } |
| } |
| else |
| { |
| mRetransmissionTimer.Start(aCoapMetadata.mRetransmissionTimeout); |
| } |
| |
| // Enqueue the message. |
| mPendingRequests.Enqueue(*messageCopy); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && messageCopy != NULL) |
| { |
| messageCopy->Free(); |
| messageCopy = NULL; |
| } |
| |
| return messageCopy; |
| } |
| |
| void Coap::DequeueMessage(Message &aMessage) |
| { |
| mPendingRequests.Dequeue(aMessage); |
| |
| if (mRetransmissionTimer.IsRunning() && (mPendingRequests.GetHead() == NULL)) |
| { |
| // No more requests pending, stop the timer. |
| mRetransmissionTimer.Stop(); |
| } |
| |
| // Free the message memory. |
| aMessage.Free(); |
| |
| // No need to worry that the earliest pending message was removed - |
| // the timer would just shoot earlier and then it'd be setup again. |
| } |
| |
| otError Coap::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error; |
| Message *messageCopy = NULL; |
| |
| // Create a message copy for lower layers. |
| VerifyOrExit((messageCopy = aMessage.Clone(aMessage.GetLength() - sizeof(CoapMetadata))) != NULL, |
| error = OT_ERROR_NO_BUFS); |
| |
| // Send the copy. |
| SuccessOrExit(error = Send(*messageCopy, aMessageInfo)); |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE && messageCopy != NULL) |
| { |
| messageCopy->Free(); |
| } |
| |
| return error; |
| } |
| |
| Message *Coap::FindRelatedRequest(const Header &aResponseHeader, const Ip6::MessageInfo &aMessageInfo, |
| Header &aRequestHeader, CoapMetadata &aCoapMetadata) |
| { |
| Message *message = mPendingRequests.GetHead(); |
| |
| while (message != NULL) |
| { |
| aCoapMetadata.ReadFrom(*message); |
| |
| if (((aCoapMetadata.mDestinationAddress == aMessageInfo.GetPeerAddr()) || |
| aCoapMetadata.mDestinationAddress.IsMulticast() || |
| aCoapMetadata.mDestinationAddress.IsAnycastRoutingLocator()) && |
| (aCoapMetadata.mDestinationPort == aMessageInfo.GetPeerPort())) |
| { |
| // FromMessage can return OT_ERROR_PARSE if only partial message was stored (header only), |
| // but payload marker is present. Assume, that stored messages are always valid. |
| aRequestHeader.FromMessage(*message, sizeof(CoapMetadata)); |
| |
| switch (aResponseHeader.GetType()) |
| { |
| case OT_COAP_TYPE_RESET: |
| case OT_COAP_TYPE_ACKNOWLEDGMENT: |
| if (aResponseHeader.GetMessageId() == aRequestHeader.GetMessageId()) |
| { |
| ExitNow(); |
| } |
| |
| break; |
| |
| case OT_COAP_TYPE_CONFIRMABLE: |
| case OT_COAP_TYPE_NON_CONFIRMABLE: |
| if (aResponseHeader.IsTokenEqual(aRequestHeader)) |
| { |
| ExitNow(); |
| } |
| |
| break; |
| } |
| } |
| |
| message = message->GetNext(); |
| } |
| |
| exit: |
| return message; |
| } |
| |
| void Coap::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) |
| { |
| static_cast<Coap *>(aContext)->Receive(*static_cast<Message *>(aMessage), |
| *static_cast<const Ip6::MessageInfo *>(aMessageInfo)); |
| } |
| |
| void Coap::Receive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| otError error; |
| Header header; |
| |
| SuccessOrExit(error = header.FromMessage(aMessage, 0)); |
| |
| if (header.IsRequest()) |
| { |
| ProcessReceivedRequest(header, aMessage, aMessageInfo); |
| } |
| else |
| { |
| ProcessReceivedResponse(header, aMessage, aMessageInfo); |
| } |
| |
| exit: |
| |
| if (error) |
| { |
| otLogInfoCoapErr(GetNetif().GetInstance(), error, "Receive failed"); |
| } |
| } |
| |
| void Coap::ProcessReceivedResponse(Header &aResponseHeader, Message &aMessage, |
| const Ip6::MessageInfo &aMessageInfo) |
| { |
| Header requestHeader; |
| CoapMetadata coapMetadata; |
| Message *message = NULL; |
| otError error = OT_ERROR_NONE; |
| |
| aMessage.MoveOffset(aResponseHeader.GetLength()); |
| |
| message = FindRelatedRequest(aResponseHeader, aMessageInfo, requestHeader, coapMetadata); |
| |
| if (message == NULL) |
| { |
| ExitNow(); |
| } |
| |
| switch (aResponseHeader.GetType()) |
| { |
| case OT_COAP_TYPE_RESET: |
| if (aResponseHeader.IsEmpty()) |
| { |
| FinalizeCoapTransaction(*message, coapMetadata, NULL, NULL, NULL, OT_ERROR_ABORT); |
| } |
| |
| // Silently ignore non-empty reset messages (RFC 7252, p. 4.2). |
| break; |
| |
| case OT_COAP_TYPE_ACKNOWLEDGMENT: |
| if (aResponseHeader.IsEmpty()) |
| { |
| // Empty acknowledgment. |
| if (coapMetadata.mConfirmable) |
| { |
| coapMetadata.mAcknowledged = true; |
| coapMetadata.UpdateIn(*message); |
| } |
| |
| // Remove the message if response is not expected, otherwise await response. |
| if (coapMetadata.mResponseHandler == NULL) |
| { |
| DequeueMessage(*message); |
| } |
| } |
| else if (aResponseHeader.IsResponse() && aResponseHeader.IsTokenEqual(requestHeader)) |
| { |
| // Piggybacked response. |
| FinalizeCoapTransaction(*message, coapMetadata, &aResponseHeader, &aMessage, &aMessageInfo, OT_ERROR_NONE); |
| } |
| |
| // Silently ignore acknowledgments carrying requests (RFC 7252, p. 4.2) |
| // or with no token match (RFC 7252, p. 5.3.2) |
| break; |
| |
| case OT_COAP_TYPE_CONFIRMABLE: |
| // Send empty ACK if it is a CON message. |
| SendAck(aResponseHeader, aMessageInfo); |
| |
| // fall through |
| ; |
| |
| case OT_COAP_TYPE_NON_CONFIRMABLE: |
| // Separate response. |
| FinalizeCoapTransaction(*message, coapMetadata, &aResponseHeader, &aMessage, &aMessageInfo, OT_ERROR_NONE); |
| |
| break; |
| } |
| |
| exit: |
| |
| if (error == OT_ERROR_NONE && message == NULL) |
| { |
| if (aResponseHeader.IsConfirmable() || aResponseHeader.IsNonConfirmable()) |
| { |
| // Successfully parsed a header but no matching request was found - reject the message by sending reset. |
| SendReset(aResponseHeader, aMessageInfo); |
| } |
| } |
| } |
| |
| void Coap::ProcessReceivedRequest(Header &aHeader, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) |
| { |
| char uriPath[Resource::kMaxReceivedUriPath] = ""; |
| char *curUriPath = uriPath; |
| const Header::Option *coapOption; |
| Message *cachedResponse = NULL; |
| otError error = OT_ERROR_NOT_FOUND; |
| |
| if (mInterceptor != NULL) |
| { |
| SuccessOrExit(error = mInterceptor(aMessage, aMessageInfo, mContext)); |
| } |
| |
| aMessage.MoveOffset(aHeader.GetLength()); |
| |
| switch (mResponsesQueue.GetMatchedResponseCopy(aHeader, aMessageInfo, &cachedResponse)) |
| { |
| case OT_ERROR_NONE: |
| error = Send(*cachedResponse, aMessageInfo); |
| // fall through |
| ; |
| |
| case OT_ERROR_NO_BUFS: |
| ExitNow(); |
| |
| case OT_ERROR_NOT_FOUND: |
| default: |
| break; |
| } |
| |
| coapOption = aHeader.GetFirstOption(); |
| |
| while (coapOption != NULL) |
| { |
| switch (coapOption->mNumber) |
| { |
| case OT_COAP_OPTION_URI_PATH: |
| if (curUriPath != uriPath) |
| { |
| *curUriPath++ = '/'; |
| } |
| |
| VerifyOrExit(coapOption->mLength < sizeof(uriPath) - static_cast<size_t>(curUriPath + 1 - uriPath)); |
| |
| memcpy(curUriPath, coapOption->mValue, coapOption->mLength); |
| curUriPath += coapOption->mLength; |
| break; |
| |
| default: |
| break; |
| } |
| |
| coapOption = aHeader.GetNextOption(); |
| } |
| |
| curUriPath[0] = '\0'; |
| |
| for (const Resource *resource = mResources; resource; resource = resource->GetNext()) |
| { |
| if (strcmp(resource->mUriPath, uriPath) == 0) |
| { |
| resource->HandleRequest(aHeader, aMessage, aMessageInfo); |
| error = OT_ERROR_NONE; |
| ExitNow(); |
| } |
| } |
| |
| if (mDefaultHandler) |
| { |
| mDefaultHandler(mDefaultHandlerContext, &aHeader, &aMessage, &aMessageInfo); |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| |
| if (error != OT_ERROR_NONE) |
| { |
| otLogInfoCoapErr(GetNetif().GetInstance(), error, "Failed to process request"); |
| |
| if (error == OT_ERROR_NOT_FOUND) |
| { |
| SendNotFound(aHeader, aMessageInfo); |
| } |
| |
| if (cachedResponse != NULL) |
| { |
| cachedResponse->Free(); |
| } |
| } |
| |
| return; |
| } |
| |
| CoapMetadata::CoapMetadata(bool aConfirmable, const Ip6::MessageInfo &aMessageInfo, |
| otCoapResponseHandler aHandler, void *aContext) |
| { |
| mSourceAddress = aMessageInfo.GetSockAddr(); |
| mDestinationPort = aMessageInfo.GetPeerPort(); |
| mDestinationAddress = aMessageInfo.GetPeerAddr(); |
| mResponseHandler = aHandler; |
| mResponseContext = aContext; |
| mRetransmissionCount = 0; |
| mRetransmissionTimeout = Timer::SecToMsec(kAckTimeout); |
| mRetransmissionTimeout += otPlatRandomGet() % |
| (Timer::SecToMsec(kAckTimeout) * kAckRandomFactorNumerator / kAckRandomFactorDenominator - |
| Timer::SecToMsec(kAckTimeout) + 1); |
| |
| if (aConfirmable) |
| { |
| // Set next retransmission timeout. |
| mNextTimerShot = Timer::GetNow() + mRetransmissionTimeout; |
| } |
| else |
| { |
| // Set overall response timeout. |
| mNextTimerShot = Timer::GetNow() + kMaxTransmitWait; |
| } |
| |
| mAcknowledged = false; |
| mConfirmable = aConfirmable; |
| } |
| |
| ResponsesQueue::ResponsesQueue(ThreadNetif &aNetif): |
| mTimer(aNetif.GetIp6().mTimerScheduler, &ResponsesQueue::HandleTimer, this) |
| { |
| } |
| |
| otError ResponsesQueue::GetMatchedResponseCopy(const Header &aHeader, |
| const Ip6::MessageInfo &aMessageInfo, |
| Message **aResponse) |
| { |
| otError error = OT_ERROR_NOT_FOUND; |
| 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)) != OT_ERROR_NONE) |
| { |
| continue; |
| } |
| |
| if (header.GetMessageId() != aHeader.GetMessageId()) |
| { |
| continue; |
| } |
| |
| *aResponse = message->Clone(); |
| VerifyOrExit(*aResponse != NULL, error = OT_ERROR_NO_BUFS); |
| |
| EnqueuedResponseHeader::RemoveFrom(**aResponse); |
| |
| error = OT_ERROR_NONE; |
| break; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| otError ResponsesQueue::GetMatchedResponseCopy(const Message &aRequest, |
| const Ip6::MessageInfo &aMessageInfo, |
| Message **aResponse) |
| { |
| otError error = OT_ERROR_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)); |
| |
| switch (GetMatchedResponseCopy(aMessage, aMessageInfo, ©)) |
| { |
| case OT_ERROR_NOT_FOUND: |
| break; |
| |
| case OT_ERROR_NONE: |
| copy->Free(); |
| |
| // fall through |
| |
| case OT_ERROR_NO_BUFS: |
| 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); |
| } |
| } |
| |
| ResponsesQueue &ResponsesQueue::GetOwner(const Context &aContext) |
| { |
| #if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES |
| ResponsesQueue &queue = *static_cast<ResponsesQueue *>(aContext.GetContext()); |
| #else |
| ResponsesQueue &queue = otGetThreadNetif().GetCoap().mResponsesQueue; |
| OT_UNUSED_VARIABLE(aContext); |
| #endif |
| return queue; |
| } |
| |
| void ResponsesQueue::HandleTimer(Timer &aTimer) |
| { |
| GetOwner(aTimer).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 ot |