| /* |
| * |
| * Copyright (c) 2013-2017 Nest Labs, Inc. |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file implements an object for a Weave Echo unsolicitied |
| * initiator (client). |
| * |
| */ |
| |
| #include <Weave/Core/WeaveCore.h> |
| #include "WeaveEcho.h" |
| |
| namespace nl { |
| namespace Weave { |
| namespace Profiles { |
| |
| WeaveEchoClient::WeaveEchoClient() |
| { |
| FabricState = NULL; |
| ExchangeMgr = NULL; |
| EncryptionType = kWeaveEncryptionType_None; |
| KeyId = WeaveKeyId::kNone; |
| OnEchoResponseReceived = NULL; |
| ExchangeCtx = NULL; |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| OnAckRcvdReceived = NULL; |
| RequestAck = false; |
| AckReceived = false; |
| ResponseReceived = false; |
| WRMPACKDelay = WEAVE_CONFIG_WRMP_DEFAULT_ACK_TIMEOUT; |
| WRMPRetransInterval = WEAVE_CONFIG_WRMP_DEFAULT_ACTIVE_RETRANS_TIMEOUT; |
| WRMPRetransCount = WEAVE_CONFIG_WRMP_DEFAULT_MAX_RETRANS; |
| appContext = 0xcafebabe; |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| } |
| |
| WEAVE_ERROR WeaveEchoClient::Init(WeaveExchangeManager *exchangeMgr) |
| { |
| // Error if already initialized. |
| if (ExchangeMgr != NULL) |
| return WEAVE_ERROR_INCORRECT_STATE; |
| |
| ExchangeMgr = exchangeMgr; |
| FabricState = exchangeMgr->FabricState; |
| EncryptionType = kWeaveEncryptionType_None; |
| KeyId = WeaveKeyId::kNone; |
| OnEchoResponseReceived = NULL; |
| ExchangeCtx = NULL; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| WEAVE_ERROR WeaveEchoClient::Shutdown() |
| { |
| if (ExchangeCtx != NULL) |
| { |
| ExchangeCtx->Abort(); |
| ExchangeCtx = NULL; |
| } |
| |
| ExchangeMgr = NULL; |
| FabricState = NULL; |
| |
| return WEAVE_NO_ERROR; |
| } |
| |
| /** |
| * Send an echo request over a WeaveConnection |
| * |
| * @param con The connection |
| * @param payload A PacketBuffer with the payload. This function takes ownership of the PacketBuffer |
| * |
| * @return WEAVE_ERROR_NO_MEMORY if no ExchangeContext is available. |
| * Other WEAVE_ERROR codes as returned by the lower layers. |
| */ |
| WEAVE_ERROR WeaveEchoClient::SendEchoRequest(WeaveConnection *con, PacketBuffer *payload) |
| { |
| // Discard any existing exchange context. Effectively we can only have one Echo exchange with |
| // a single node at any one time. |
| if (ExchangeCtx != NULL) |
| { |
| ExchangeCtx->Abort(); |
| ExchangeCtx = NULL; |
| } |
| |
| // Create a new exchange context. |
| ExchangeCtx = ExchangeMgr->NewContext(con, this); |
| if (ExchangeCtx == NULL) |
| { |
| PacketBuffer::Free(payload); |
| return WEAVE_ERROR_NO_MEMORY; |
| } |
| |
| ExchangeCtx->OnConnectionClosed = HandleConnectionClosed; |
| |
| return SendEchoRequest(payload); |
| } |
| |
| /** |
| * Send an echo request to a Weave node using the default Weave port and the |
| * letting the system's routing table choose the output interface. |
| * |
| * @param nodeId The destination's nodeId |
| * @param nodeAddr The destination's ip address |
| * @param payload A PacketBuffer with the payload. This function takes ownership of the PacketBuffer |
| * |
| * @return WEAVE_ERROR_NO_MEMORY if no ExchangeContext is available. |
| * Other WEAVE_ERROR codes as returned by the lower layers. |
| */ |
| WEAVE_ERROR WeaveEchoClient::SendEchoRequest(uint64_t nodeId, IPAddress nodeAddr, PacketBuffer *payload) |
| { |
| return SendEchoRequest(nodeId, nodeAddr, WEAVE_PORT, INET_NULL_INTERFACEID, payload); |
| } |
| |
| /** |
| * Send an echo request to a Weave node. |
| * |
| * @param nodeId The destination's nodeId |
| * @param nodeAddr The destination's ip address |
| * @param port The destination's UDP port (WEAVE_PORT by default) |
| * @param sendIntfId A specific interface to use |
| * @param payload A PacketBuffer with the payload. This function takes ownership of the PacketBuffer |
| * |
| * @return WEAVE_ERROR_NO_MEMORY if no ExchangeContext is available. |
| * Other WEAVE_ERROR codes as returned by the lower layers. |
| */ |
| WEAVE_ERROR WeaveEchoClient::SendEchoRequest(uint64_t nodeId, IPAddress nodeAddr, uint16_t port, InterfaceId sendIntfId, PacketBuffer *payload) |
| { |
| // Discard any existing exchange context. Effectively we can only have one Echo exchange with |
| // a single node at any one time. |
| if (ExchangeCtx != NULL) |
| { |
| ExchangeCtx->Abort(); |
| ExchangeCtx = NULL; |
| } |
| if (nodeAddr == IPAddress::Any) |
| nodeAddr = FabricState->SelectNodeAddress(nodeId); |
| |
| // Create a new exchange context. |
| ExchangeCtx = ExchangeMgr->NewContext(nodeId, nodeAddr, WEAVE_PORT, sendIntfId, this); |
| if (ExchangeCtx == NULL) |
| { |
| PacketBuffer::Free(payload); |
| return WEAVE_ERROR_NO_MEMORY; |
| } |
| |
| return SendEchoRequest(payload); |
| } |
| |
| WEAVE_ERROR WeaveEchoClient::SendEchoRequest(PacketBuffer *payload) |
| { |
| WEAVE_ERROR err = WEAVE_NO_ERROR; |
| |
| // Configure the encryption and signature types to be used to send the request. |
| ExchangeCtx->EncryptionType = EncryptionType; |
| ExchangeCtx->KeyId = KeyId; |
| |
| // Arrange for messages in this exchange to go to our response handler. |
| ExchangeCtx->OnMessageReceived = HandleResponse; |
| |
| ExchangeCtx->OnKeyError = HandleKeyError; |
| |
| // Send an Echo Request message. Discard the exchange context if the send fails. |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| AckReceived = false; |
| ResponseReceived = false; |
| |
| if (RequestAck) |
| { |
| ExchangeCtx->OnAckRcvd = HandleAckRcvd; |
| ExchangeCtx->OnSendError = HandleSendError; |
| ExchangeCtx->mWRMPConfig.mAckPiggybackTimeout = WRMPACKDelay; |
| ExchangeCtx->mWRMPConfig.mInitialRetransTimeout = WRMPRetransInterval; |
| ExchangeCtx->mWRMPConfig.mActiveRetransTimeout = WRMPRetransInterval; |
| ExchangeCtx->mWRMPConfig.mMaxRetrans = WRMPRetransCount; |
| err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload, ExchangeContext::kSendFlag_RequestAck, &appContext); |
| } |
| else |
| err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload); |
| #else // !WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| err = ExchangeCtx->SendMessage(kWeaveProfile_Echo, kEchoMessageType_EchoRequest, payload); |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| payload = NULL; |
| if (err != WEAVE_NO_ERROR && ExchangeCtx) |
| { |
| ExchangeCtx->Abort(); |
| ExchangeCtx = NULL; |
| } |
| |
| return err; |
| } |
| |
| void WeaveEchoClient::HandleResponse(ExchangeContext *ec, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t profileId, uint8_t msgType, PacketBuffer *payload) |
| { |
| WeaveEchoClient *echoApp = (WeaveEchoClient *)ec->AppState; |
| |
| // Assert that the exchange context matches the client's current context. |
| // This should never fail because even if SendEchoRequest is called |
| // back-to-back, the second call will call Close() on the first exchange, |
| // which clears the OnMessageReceived callback. |
| VerifyOrDie(echoApp && ec == echoApp->ExchangeCtx); |
| |
| // Verify that the message is an Echo Response. |
| // If not, close the exchange and free the payload. |
| if (profileId != kWeaveProfile_Echo || msgType != kEchoMessageType_EchoResponse) |
| { |
| ec->Close(); |
| echoApp->ExchangeCtx = NULL; |
| ExitNow(); |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| echoApp->ResponseReceived = true; |
| |
| if (!echoApp->RequestAck || echoApp->AckReceived || (!echoApp->OnAckRcvdReceived)) |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| { |
| // Remove the EC from the app state now. OnEchoResponseReceived can call |
| // SendEchoRequest and install a new one. |
| echoApp->ExchangeCtx->Close(); |
| echoApp->ExchangeCtx = NULL; |
| } |
| |
| // Call the registered OnEchoResponseReceived handler, if any. |
| if (echoApp->OnEchoResponseReceived != NULL) |
| echoApp->OnEchoResponseReceived(msgInfo->SourceNodeId, (pktInfo != NULL) ? pktInfo->SrcAddress : IPAddress::Any, payload); |
| |
| exit: |
| // Free the payload buffer. |
| PacketBuffer::Free(payload); |
| } |
| |
| void WeaveEchoClient::HandleConnectionClosed(ExchangeContext *ec, WeaveConnection *con, WEAVE_ERROR conErr) |
| { |
| HandleError(ec, conErr); |
| } |
| |
| void WeaveEchoClient::HandleSendError(ExchangeContext *ec, WEAVE_ERROR sendErr, void *msgCtxt) |
| { |
| HandleError(ec, sendErr); |
| } |
| |
| void WeaveEchoClient::HandleKeyError(ExchangeContext *ec, WEAVE_ERROR keyErr) |
| { |
| HandleError(ec, keyErr); |
| } |
| |
| void WeaveEchoClient::HandleError(ExchangeContext *ec, WEAVE_ERROR err) |
| { |
| WeaveEchoClient *echoApp = (WeaveEchoClient *)ec->AppState; |
| |
| VerifyOrExit(echoApp && echoApp->ExchangeCtx, /* no action */); |
| |
| VerifyOrDie(ec == echoApp->ExchangeCtx); |
| |
| if (err != WEAVE_NO_ERROR) |
| { |
| echoApp->ExchangeCtx->Abort(); |
| } |
| else |
| { |
| echoApp->ExchangeCtx->Close(); |
| } |
| exit: |
| echoApp->ExchangeCtx = NULL; |
| } |
| |
| #if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| void WeaveEchoClient::HandleAckRcvd(ExchangeContext *ec, void *msgCtxt) |
| { |
| WeaveEchoClient *echoApp = (WeaveEchoClient *)ec->AppState; |
| |
| // Assert that the exchange context matches the client's current context. |
| // This should never fail because even if SendEchoRequest is called |
| // back-to-back, the second call will call Close() on the first exchange, |
| // which clears the OnAckReceived callback. |
| VerifyOrDie(echoApp && ec == echoApp->ExchangeCtx); |
| |
| echoApp->AckReceived = true; |
| |
| if (echoApp->ResponseReceived) |
| { |
| // Remove the EC from the app state now. OnAckRcvdReceived can call |
| // SendEchoRequest and install a new one. |
| echoApp->ExchangeCtx->Close(); |
| echoApp->ExchangeCtx = NULL; |
| } |
| |
| // Call the registered OnEchoResponseReceived handler, if any. |
| if (echoApp->OnAckRcvdReceived != NULL) |
| echoApp->OnAckRcvdReceived(msgCtxt); |
| } |
| |
| void WeaveEchoClient::SetRequestAck(bool requestAck) |
| { |
| RequestAck = requestAck; |
| } |
| |
| void WeaveEchoClient::SetWRMPACKDelay(uint16_t aWRMPACKDelay) |
| { |
| WRMPACKDelay = aWRMPACKDelay; |
| } |
| |
| void WeaveEchoClient::SetWRMPRetransInterval(uint32_t aWRMPRetransInterval) |
| { |
| WRMPRetransInterval = aWRMPRetransInterval; |
| } |
| |
| void WeaveEchoClient::SetWRMPRetransCount(uint8_t aWRMPRetransCount) |
| { |
| WRMPRetransCount = aWRMPRetransCount; |
| } |
| #endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING |
| |
| } // namespace Profiles |
| } // namespace Weave |
| } // namespace nl |