ThreadError Client::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, otCoapResponseHandler aHandler, void *aContext) { ThreadError error; Header header; RequestMetadata requestMetadata; Message *storedCopy = NULL; uint16_t copyLength = 0; SuccessOrExit(error = header.FromMessage(aMessage)); // 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() && header.IsRequest() && (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) { requestMetadata = RequestMetadata(header.IsConfirmable(), aMessageInfo, aHandler, aContext); VerifyOrExit((storedCopy = CopyAndEnqueueMessage(aMessage, copyLength, requestMetadata)) != NULL, error = kThreadError_NoBufs); } SuccessOrExit(error = mSocket.SendTo(aMessage, aMessageInfo)); exit: if (error != kThreadError_None && storedCopy != NULL) { DequeueMessage(*storedCopy); } return error; }
void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Header responseHeader; Header requestHeader; RequestMetadata requestMetadata; Message *message = NULL; ThreadError error; SuccessOrExit(error = responseHeader.FromMessage(aMessage)); aMessage.MoveOffset(responseHeader.GetLength()); message = FindRelatedRequest(responseHeader, aMessageInfo, requestHeader, requestMetadata); if (message == NULL) { ExitNow(); } switch (responseHeader.GetType()) { case kCoapTypeReset: if (responseHeader.IsEmpty()) { FinalizeCoapTransaction(*message, requestMetadata, NULL, NULL, kThreadError_Abort); } // Silently ignore non-empty reset messages (RFC 7252, p. 4.2). break; case kCoapTypeAcknowledgment: if (responseHeader.IsEmpty()) { // Empty acknowledgment. if (requestMetadata.mConfirmable) { requestMetadata.mAcknowledged = true; requestMetadata.UpdateIn(*message); } // Remove the message if response is not expected, otherwise await response. if (requestMetadata.mResponseHandler == NULL) { DequeueMessage(*message); } } else if (responseHeader.IsResponse() && responseHeader.IsTokenEqual(requestHeader)) { // Piggybacked response. FinalizeCoapTransaction(*message, requestMetadata, &responseHeader, &aMessage, kThreadError_None); } // Silently ignore acknowledgments carrying requests (RFC 7252, p. 4.2) // or with no token match (RFC 7252, p. 5.3.2) break; case kCoapTypeConfirmable: case kCoapTypeNonConfirmable: if (responseHeader.IsConfirmable()) { // Send empty ACK if it is a CON message. SendEmptyAck(aMessageInfo.GetPeerAddr(), aMessageInfo.mPeerPort, responseHeader.GetMessageId()); } FinalizeCoapTransaction(*message, requestMetadata, &responseHeader, &aMessage, kThreadError_None); break; } exit: if (error == kThreadError_None && message == NULL) { if (responseHeader.IsConfirmable() || responseHeader.IsNonConfirmable()) { // Successfully parsed a header but no matching request was found - reject the message by sending reset. SendReset(aMessageInfo.GetPeerAddr(), aMessageInfo.mPeerPort, responseHeader.GetMessageId()); } } }