From 5fd4279f4eb28cc9e89282cce97083b2e01ae336 Mon Sep 17 00:00:00 2001 From: "hyuna0213.jo" Date: Mon, 18 Jan 2016 20:08:25 +0900 Subject: [PATCH] keepalive for coap over tcp In order to ensure that the connection between an OIC Devices, OIC Device should send application layer keepalive messages. Change-Id: I630b186db58317f28cdfbd99a66e6bf75970ae3e Signed-off-by: hyuna0213.jo Reviewed-on: https://gerrit.iotivity.org/gerrit/4291 Tested-by: jenkins-iotivity Reviewed-by: Jon A. Cruz --- resource/csdk/SConscript | 5 + resource/csdk/connectivity/api/cainterface.h | 22 + resource/csdk/connectivity/inc/catcpadapter.h | 10 + resource/csdk/connectivity/inc/catcpinterface.h | 18 + .../csdk/connectivity/src/caconnectivitymanager.c | 8 + .../connectivity/src/tcp_adapter/catcpadapter.c | 36 ++ .../connectivity/src/tcp_adapter/catcpserver.c | 22 + .../csdk/stack/include/internal/oickeepalive.h | 98 +++ resource/csdk/stack/src/ocstack.c | 39 ++ resource/csdk/stack/src/oickeepalive.c | 678 +++++++++++++++++++++ 10 files changed, 936 insertions(+) create mode 100644 resource/csdk/stack/include/internal/oickeepalive.h create mode 100644 resource/csdk/stack/src/oickeepalive.c diff --git a/resource/csdk/SConscript b/resource/csdk/SConscript index 2db316d..9249ae4 100644 --- a/resource/csdk/SConscript +++ b/resource/csdk/SConscript @@ -31,6 +31,7 @@ liboctbstack_env = lib_env.Clone() target_os = env.get('TARGET_OS') with_ra = env.get('WITH_RA') with_ra_ibb = env.get('WITH_RA_IBB') +with_tcp = env.get('WITH_TCP') # As in the source code, it includes arduino Time library (C++) # It requires compile the .c with g++ if target_os == 'arduino': @@ -48,6 +49,7 @@ liboctbstack_env.PrependUnique(CPPPATH = [ 'stack/include/internal', '../oc_logger/include', 'connectivity/lib/libcoap-4.1.1', + 'connectivity/common/inc', 'connectivity/inc', 'connectivity/api', 'connectivity/external/inc', @@ -137,6 +139,9 @@ liboctbstack_src = [ OCTBSTACK_SRC + "rdpayload.c" ] +if with_tcp == True: + liboctbstack_src.append(OCTBSTACK_SRC + 'oickeepalive.c') + liboctbstack_src.extend(env['cbor_files']) if target_os in ['arduino','darwin','ios'] : diff --git a/resource/csdk/connectivity/api/cainterface.h b/resource/csdk/connectivity/api/cainterface.h index 832772f..b514956 100644 --- a/resource/csdk/connectivity/api/cainterface.h +++ b/resource/csdk/connectivity/api/cainterface.h @@ -64,6 +64,28 @@ typedef struct #endif //RA_ADAPTER +#ifdef TCP_ADAPTER +/** + * Callback function to pass the connection information from CA to RI. + * @param[out] object remote device information. + */ +typedef void (*CAKeepAliveConnectedCallback)(const CAEndpoint_t *object); + +/** + * Callback function to pass the disconnection information from CA to RI. + * @param[out] object remote device information. + */ +typedef void (*CAKeepAliveDisconnectedCallback)(const CAEndpoint_t *object); + +/** + * Register connected callback and disconnected callback to process KeepAlive. + * connection informations are delivered these callbacks. + * @param[in] ConnHandler Connected callback. + * @param[in] DisconnHandler Disconnected Callback. + */ +void CARegisterKeepAliveHandler(CAKeepAliveConnectedCallback ConnHandler, + CAKeepAliveDisconnectedCallback DisconnHandler); +#endif /** * Initialize the connectivity abstraction module. * It will initialize adapters, thread pool and other modules based on the platform diff --git a/resource/csdk/connectivity/inc/catcpadapter.h b/resource/csdk/connectivity/inc/catcpadapter.h index b62df78..2528df1 100644 --- a/resource/csdk/connectivity/inc/catcpadapter.h +++ b/resource/csdk/connectivity/inc/catcpadapter.h @@ -28,6 +28,7 @@ #include "cacommon.h" #include "caadapterinterface.h" #include "cathreadpool.h" +#include "cainterface.h" #ifdef __cplusplus extern "C" @@ -150,6 +151,15 @@ CAResult_t CAStopTCP(); */ void CATerminateTCP(); +/** + * Set connected callback and disconnected callback to process KeepAlive. + * connection informations are delivered these callbacks. + * @param[in] ConnHandler Connected callback. + * @param[in] DisconnHandler Disconnected Callback. + */ +void CATCPSetKeepAliveCallbacks(CAKeepAliveConnectedCallback ConnHandler, + CAKeepAliveDisconnectedCallback DisconnHandler); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/resource/csdk/connectivity/inc/catcpinterface.h b/resource/csdk/connectivity/inc/catcpinterface.h index 4040067..38efb00 100644 --- a/resource/csdk/connectivity/inc/catcpinterface.h +++ b/resource/csdk/connectivity/inc/catcpinterface.h @@ -63,6 +63,16 @@ typedef void (*CATCPErrorHandleCallback)(const CAEndpoint_t *endpoint, const voi uint32_t dataLength, CAResult_t result); /** + * Callback to notify connection information in the TCP adapter. + * + * @param[in] addr connected device address. + * @param[in] port connected port info. + * @param[in] isConnected Whether keepalive message needs to be sent. + * @see Callback must be registered using CATCPSetKeepAliveCallback(). + */ +typedef void (*CATCPKeepAliveHandleCallback)(const char *addr, uint16_t port, bool isConnected); + +/** * set error callback to notify error in TCP adapter. * * @param[in] errorHandleCallback Callback function to notify the error @@ -71,6 +81,14 @@ typedef void (*CATCPErrorHandleCallback)(const CAEndpoint_t *endpoint, const voi void CATCPSetErrorHandler(CATCPErrorHandleCallback errorHandleCallback); /** + * set keepalive callback to notify connection information in TCP adapter. + * + * @param[in] keepaliveHandler Callback function to notify the connection information. + * in the TCP adapter. + */ +void CATCPSetKeepAliveCallback(CATCPKeepAliveHandleCallback keepaliveHandler); + +/** * Start TCP server. * * @param threadPool Thread pool for managing Unicast server threads. diff --git a/resource/csdk/connectivity/src/caconnectivitymanager.c b/resource/csdk/connectivity/src/caconnectivitymanager.c index 83bde64..68aa2c2 100644 --- a/resource/csdk/connectivity/src/caconnectivitymanager.c +++ b/resource/csdk/connectivity/src/caconnectivitymanager.c @@ -487,3 +487,11 @@ CAResult_t CACloseDtlsSession(const CAEndpoint_t *endpoint) } #endif /* __WITH_DTLS__ */ + +#ifdef TCP_ADAPTER +void CARegisterKeepAliveHandler(CAKeepAliveConnectedCallback ConnHandler, + CAKeepAliveDisconnectedCallback DisconnHandler) +{ + CATCPSetKeepAliveCallbacks(ConnHandler, DisconnHandler); +} +#endif diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c b/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c index 3495c05..edef92e 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpadapter.c @@ -26,6 +26,7 @@ #define __STDC_FORMAT_MACROS #include +#include "cainterface.h" #include "catcpadapter.h" #include "catcpinterface.h" #include "caqueueingthread.h" @@ -80,6 +81,16 @@ static CAErrorHandleCallback g_errorCallback = NULL; static void CATCPPacketReceivedCB(const CASecureEndpoint_t *sep, const void *data, uint32_t dataLength); +/** + * KeepAlive Connected Callback to CA adapter. + */ +static CAKeepAliveConnectedCallback g_connCallback = NULL; + +/** + * KeepAlive Disconnected Callback to CA adapter. + */ +static CAKeepAliveDisconnectedCallback g_disconnCallback = NULL; + static CAResult_t CATCPInitializeQueueHandles(); static void CATCPDeinitializeQueueHandles(); @@ -162,6 +173,31 @@ void CATCPErrorHandler(const CAEndpoint_t *endpoint, const void *data, } } +static void CATCPKeepAliveHandler(const char *addr, uint16_t port, bool isConnected) +{ + CAEndpoint_t endpoint = { .adapter = CA_ADAPTER_TCP, + .port = port }; + OICStrcpy(endpoint.addr, sizeof(endpoint.addr), addr); + + if (isConnected) + { + g_connCallback(&endpoint); + } + else + { + g_disconnCallback(&endpoint); + } +} + +void CATCPSetKeepAliveCallbacks(CAKeepAliveConnectedCallback ConnHandler, + CAKeepAliveDisconnectedCallback DisconnHandler) +{ + g_connCallback = ConnHandler; + g_disconnCallback = DisconnHandler; + + CATCPSetKeepAliveCallback(CATCPKeepAliveHandler); +} + static void CAInitializeTCPGlobals() { caglobals.tcp.selectTimeout = CA_TCP_SELECT_TIMEOUT; diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c index 7b576ac..90f6821 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c @@ -86,6 +86,11 @@ static CATCPPacketReceivedCallback g_packetReceivedCallback; */ static CATCPErrorHandleCallback g_TCPErrorHandler = NULL; +/** + * Connected Callback to pass the connection information to RI. + */ +static CATCPKeepAliveHandleCallback g_keepaliveCallback = NULL; + static CAResult_t CATCPCreateMutex(); static void CATCPDestroyMutex(); static CAResult_t CATCPCreateCond(); @@ -587,6 +592,11 @@ void CATCPSetPacketReceiveCallback(CATCPPacketReceivedCallback callback) g_packetReceivedCallback = callback; } +void CATCPSetKeepAliveCallback(CATCPKeepAliveHandleCallback keepaliveHandler) +{ + g_keepaliveCallback = keepaliveHandler; +} + static size_t CACheckPayloadLength(const void *data, size_t dlen) { VERIFY_NON_NULL_RET(data, TAG, "data", -1); @@ -748,6 +758,12 @@ CATCPSessionInfo_t *CAConnectTCPSession(const CAEndpoint_t *endpoint) ca_mutex_unlock(g_mutexObjectList); CHECKFD(fd); + + // pass the connection information to RI for keepalive. + if (g_keepaliveCallback) + { + g_keepaliveCallback(svritem->sep.endpoint.addr, svritem->sep.endpoint.port, true); + } } return svritem; @@ -769,6 +785,12 @@ CAResult_t CADisconnectTCPSession(CATCPSessionInfo_t *svritem, size_t index) OICFree(svritem); ca_mutex_unlock(g_mutexObjectList); + // pass the connection information to RI for keepalive. + if (g_keepaliveCallback) + { + g_keepaliveCallback(svritem->sep.endpoint.addr, svritem->sep.endpoint.port, false); + } + return CA_STATUS_OK; } diff --git a/resource/csdk/stack/include/internal/oickeepalive.h b/resource/csdk/stack/include/internal/oickeepalive.h new file mode 100644 index 0000000..24fe755 --- /dev/null +++ b/resource/csdk/stack/include/internal/oickeepalive.h @@ -0,0 +1,98 @@ +/* **************************************************************** + * + * Copyright 2015 Samsung Electronics 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 contains the APIs for KeepAlive Mechanism. + * In order to ensure that the connection between an OIC Devices, + * when using CoAP over TCP, OIC Device should send application layer + * KeepAlive messages. + */ +#ifndef KEEP_ALIVE_H_ +#define KEEP_ALIVE_H_ + +#include "octypes.h" +#include "ocserverrequest.h" +#include "ocresource.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * Name of resource type. + */ +#define KEEPALIVE_RESOURCE_TYPE_NAME "oic.wk.ping" + +/** + * Name of resource interface. + */ +#define KEEPALIVE_RESOURCE_INTF_NAME "oic.if.rw" + +/** + * URI of the resource. + */ +#define KEEPALIVE_RESOURCE_URI "/oic/ping" + +/** + * Initialize the KeepAlive. + * @return ::OC_STACK_OK or Appropriate error code. + */ +OCStackResult InitializeKeepAlive(); + +/** + * Terminates the KeepAlive. + * @return ::OC_STACK_OK or Appropriate error code. + */ +OCStackResult TerminateKeepAlive(); + +/** + * Process the KeepAlive timer to send ping message to OIC Server. + */ +void ProcessKeepAlive(); + +/** + * This API will be called from RI layer whenever there is a request for KeepAlive. + * Virtual Resource. + * @param[in] endPoint RemoteEndpoint which sent the packet. + * @param[in] requestInfo Received coap packet. + * @return ::OC_STACK_OK or Appropriate error code. + */ +OCStackResult HandleKeepAliveRequest(const CAEndpoint_t* endPoint, + const CARequestInfo_t* requestInfo); + +/** + * API to handle the connected device for KeepAlive. + * @return Current Time. + */ +void HandleKeepAliveConnCB(const CAEndpoint_t *endpoint); + +/** + * API to handle the disconnected device for KeepAlive. + * @return Current Time. + */ +void HandleKeepAliveDisconnCB(const CAEndpoint_t *endpoint); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // KEEP_ALIVE_H_ diff --git a/resource/csdk/stack/src/ocstack.c b/resource/csdk/stack/src/ocstack.c index 992819e..857667f 100644 --- a/resource/csdk/stack/src/ocstack.c +++ b/resource/csdk/stack/src/ocstack.c @@ -60,6 +60,10 @@ #endif #endif +#ifdef TCP_ADAPTER +#include "oickeepalive.h" +#endif + #ifdef WITH_ARDUINO #include "Time.h" #else @@ -608,6 +612,7 @@ OCStackResult CAToOCStackResult(CAResponseResult_t caCode) break; case CA_CHANGED: case CA_CONTENT: + case CA_VALID: ret = OC_STACK_OK; break; case CA_BAD_REQ: @@ -1187,6 +1192,12 @@ void HandleCAResponses(const CAEndpoint_t* endPoint, const CAResponseInfo_t* res { type = PAYLOAD_TYPE_RD; } +#ifdef TCP_ADAPTER + else if (strcmp(cbNode->requestUri, KEEPALIVE_RESOURCE_URI) == 0) + { + type = PAYLOAD_TYPE_REPRESENTATION; + } +#endif else { OIC_LOG_V(ERROR, TAG, "Unknown Payload type in Discovery: %d %s", @@ -1504,6 +1515,15 @@ void HandleCARequests(const CAEndpoint_t* endPoint, const CARequestInfo_t* reque (CAEndpoint_t *) endPoint); #endif +#ifdef TCP_ADAPTER + if (requestInfo->info.resourceUri && + strcmp(requestInfo->info.resourceUri, KEEPALIVE_RESOURCE_URI) == 0) + { + HandleKeepAliveRequest(endPoint, requestInfo); + return; + } +#endif + OCStackResult requestResult = OC_STACK_ERROR; if(myStackMode == OC_CLIENT) @@ -1916,6 +1936,10 @@ OCStackResult OCInit1(OCMode mode, OCTransportFlags serverFlags, OCTransportFlag } VERIFY_SUCCESS(result, OC_STACK_OK); +#ifdef TCP_ADAPTER + CARegisterKeepAliveHandler(HandleKeepAliveConnCB, HandleKeepAliveDisconnCB); +#endif + #ifdef WITH_PRESENCE PresenceTimeOutSize = sizeof (PresenceTimeOut) / sizeof (PresenceTimeOut[0]) - 1; #endif // WITH_PRESENCE @@ -1943,6 +1967,13 @@ OCStackResult OCInit1(OCMode mode, OCTransportFlags serverFlags, OCTransportFlag } #endif +#ifdef TCP_ADAPTER + if(result == OC_STACK_OK) + { + result = InitializeKeepAlive(); + } +#endif + exit: if(result != OC_STACK_OK) { @@ -1984,6 +2015,10 @@ OCStackResult OCStop() } #endif +#ifdef TCP_ADAPTER + TerminateKeepAlive(); +#endif + // Free memory dynamically allocated for resources deleteAllResources(); DeleteDeviceInfo(); @@ -2786,6 +2821,10 @@ OCStackResult OCProcess() #ifdef ROUTING_GATEWAY RMProcess(); #endif + +#ifdef TCP_ADAPTER + ProcessKeepAlive(); +#endif return OC_STACK_OK; } diff --git a/resource/csdk/stack/src/oickeepalive.c b/resource/csdk/stack/src/oickeepalive.c new file mode 100644 index 0000000..6c5b07a --- /dev/null +++ b/resource/csdk/stack/src/oickeepalive.c @@ -0,0 +1,678 @@ +/* **************************************************************** + * + * Copyright 2015 Samsung Electronics 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. + * + ******************************************************************/ + +#include "oickeepalive.h" + +#include +#include +#include "oic_malloc.h" +#include "oic_string.h" +#include "oic_time.h" +#include "ocrandom.h" +#include "uarraylist.h" +#include "ocstackinternal.h" +#include "ocpayloadcbor.h" +#include "ocpayload.h" +#include "ocresourcehandler.h" +#include "logger.h" + +/** + * Logging tag for module name. + */ +#define TAG "OIC_RI_KEEPALIVE" + +static const uint64_t USECS_PER_SEC = 1000000; + +//----------------------------------------------------------------------------- +// Macros +//----------------------------------------------------------------------------- +#define VERIFY_SUCCESS(op, successCode) { if ((op) != (successCode)) \ + {OC_LOG_V(FATAL, TAG, "%s failed!!", #op); goto exit;} } + +#define VERIFY_NON_NULL(arg, logLevel, retVal) { if (!(arg)) { OC_LOG((logLevel), \ + TAG, #arg " is NULL"); return (retVal); } } + +#define VERIFY_NON_NULL_NR(arg, logLevel) { if (!(arg)) { OC_LOG((logLevel), \ + TAG, #arg " is NULL"); return; } } + +#define VERIFY_NON_NULL_V(arg) { if (!arg) {OC_LOG_V(FATAL, TAG, "%s is NULL", #arg);\ + goto exit;} } + +/** + * The KeepAlive table entries are removed + * if it can't receive response message within 60 seconds. + */ +#define KEEPALIVE_RESPONSE_TIMEOUT_SEC 60 + +/** + * The Min time interval value. (2 minutes) + * start from 2 minutes and increases in multiples of 2 up to a maximum of 64minutes. + */ +#define KEEPALIVE_MIN_INTERVAL 2 + +/** + * The Max time interval value. (64 minutes) + */ +#define KEEPALIVE_MAX_INTERVAL 64 + +/** + * KeepAlive key to parser Payload Table. + */ +static const char INTERVAL[] = "in"; + +/** + * To check if KeepAlive is initialized. + */ +static bool g_isKeepAliveInitialized = false; + +/** + * Pointer to handle of the newly created KeepAlive resource. + */ +static OCResourceHandle g_keepAliveHandle = NULL; + +/** + * KeepAlive table which holds connection interval. + */ +static u_arraylist_t *g_keepAliveConnectionTable = NULL; + +/** + * KeepAlive table entries. + */ +typedef struct +{ + OCMode mode; /**< host Mode of Operation. */ + CAEndpoint_t remoteAddr; /**< destination Address. */ + uint32_t interval; /**< time interval for KeepAlive. in seconds.*/ + bool sentPingMsg; /**< if oic client already sent ping message. */ + uint64_t timeStamp; /**< last sent or received ping message. in microseconds. */ +} KeepAliveEntry_t; + +/** + * Send disconnect message to remove connection. + */ +static OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry); + +/** + * Send ping message to remote endpoint. + */ +static OCStackResult SendPingMessage(KeepAliveEntry_t *entry); + +/** + * Ping Message callback registered with RI for KeepAlive Request. + */ +static OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle, + OCClientResponse * clientResponse); + +/** + * This function creates KeepAlive resource. + * @return ::OC_STACK_OK or Appropriate error code. + */ +static OCStackResult CreateKeepAliveResource(); + +/** + * This function deletes KeepAlive resource. + * @return ::OC_STACK_OK or Appropriate error code. + */ +static OCStackResult DeleteKeepAliveResource(); + +/** + * API to handle the GET request received for a KeepAlive resource. + * @param[in] endPoint RemoteEndpoint which sent the packet. + * @param[in] requestInfo Received coap packet. + * @return ::OC_STACK_OK or Appropriate error code. + */ +static OCStackResult HandleKeepAliveGETRequest(const CAEndpoint_t* endPoint, + const CARequestInfo_t* requestInfo); + +/** + * API to handle the PUT request received for a KeepAlive resource. + * @param[in] endPoint RemoteEndpoint which sent the packet. + * @param[in] requestInfo Received coap packet. + * @return ::OC_STACK_OK or Appropriate error code. + */ +static OCStackResult HandleKeepAlivePUTRequest(const CAEndpoint_t* endPoint, + const CARequestInfo_t* requestInfo); + +/** + * API to handle the Response payload. + * @param[in] endpoint RemoteEndpoint which sent the packet. + * @param[in] responseCode Received reseponse code. + * @return ::OC_STACK_OK or Appropriate error code. + */ +static OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint, + OCStackResult responseCode); + +/** + * Gets keepalive entry. + * @param[in] endpoint Remote Endpoint information (like ipaddress, + * port, reference uri and transport type) to + * which the ping message has to be sent. + * @param[out] index index of array list. + * @return KeepAlive entry to send ping message. + */ +static KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index); + +/** + * Add keepalive entry. + * @param[in] endpoint Remote Endpoint information (like ipaddress, + * port, reference uri and transport type). + * @param[in] mode Whether it is OIC Server or OIC Client. + * @return The KeepAlive entry added in KeepAlive Table. + */ +static KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode); + +/** + * Remove keepalive entry. + * @param[in] endpoint Remote Endpoint information (like ipaddress, + * port, reference uri and transport type). + * @return The KeepAlive entry removed in KeepAlive Table. + */ +static OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint); + +OCStackResult InitializeKeepAlive() +{ + OC_LOG(DEBUG, TAG, "InitializeKeepAlive IN"); + if (g_isKeepAliveInitialized) + { + OC_LOG(DEBUG, TAG, "KeepAlive already initialized"); + return OC_STACK_OK; + } + + // Create the KeepAlive Resource[/oic/ping]. + OCStackResult result = CreateKeepAliveResource(); + if (OC_STACK_OK != result) + { + OC_LOG_V(ERROR, TAG, "CreateKeepAliveResource failed[%d]", result); + return result; + } + + if (!g_keepAliveConnectionTable) + { + g_keepAliveConnectionTable = u_arraylist_create(); + if (NULL == g_keepAliveConnectionTable) + { + OC_LOG(ERROR, TAG, "Creating KeepAlive Table failed"); + TerminateKeepAlive(); + return OC_STACK_ERROR; + } + } + + g_isKeepAliveInitialized = true; + + OC_LOG(DEBUG, TAG, "InitializeKeepAlive OUT"); + return OC_STACK_OK; +} + +OCStackResult TerminateKeepAlive() +{ + OC_LOG(DEBUG, TAG, "TerminateKeepAlive IN"); + if (!g_isKeepAliveInitialized) + { + OC_LOG(ERROR, TAG, "KeepAlive not initialized"); + return OC_STACK_ERROR; + } + + // Delete the KeepAlive Resource[/oic/ping]. + OCStackResult result = DeleteKeepAliveResource(); + if (OC_STACK_OK != result) + { + OC_LOG_V(ERROR, TAG, "DeleteKeepAliveResource failed[%d]", result); + return result; + } + + if (NULL != g_keepAliveConnectionTable) + { + u_arraylist_destroy(g_keepAliveConnectionTable); + g_keepAliveConnectionTable = NULL; + } + + g_isKeepAliveInitialized = false; + + OC_LOG(DEBUG, TAG, "TerminateKeepAlive OUT"); + return OC_STACK_OK; +} + +OCStackResult CreateKeepAliveResource() +{ + OC_LOG(DEBUG, TAG, "InitKeepAliveResource IN"); + + // Create a KeepAlive resource + OCStackResult result = OCCreateResource(&g_keepAliveHandle, + KEEPALIVE_RESOURCE_TYPE_NAME, + KEEPALIVE_RESOURCE_INTF_NAME, + KEEPALIVE_RESOURCE_URI, + NULL, + NULL, + OC_DISCOVERABLE); + + if (OC_STACK_OK != result) + { + OC_LOG_V(ERROR, TAG, "Create resource for KeepAlive failed[%d]", result); + } + + OC_LOG(DEBUG, TAG, "InitKeepAliveResource OUT"); + return result; +} + +OCStackResult DeleteKeepAliveResource() +{ + OC_LOG(DEBUG, TAG, "DeleteKeepAliveResource IN"); + + // Create a KeepAlive resource + OCStackResult result = OCDeleteResource(g_keepAliveHandle); + + if (OC_STACK_OK != result) + { + OC_LOG_V(ERROR, TAG, "Delete resource for KeepAlive failed[%d]", result); + } + + OC_LOG(DEBUG, TAG, "DeleteKeepAliveResource OUT"); + return result; +} + +OCStackResult HandleKeepAliveRequest(const CAEndpoint_t* endPoint, + const CARequestInfo_t* requestInfo) +{ + VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM); + VERIFY_NON_NULL(requestInfo, FATAL, OC_STACK_INVALID_PARAM); + + OC_LOG(DEBUG, TAG, "HandleKeepAliveRequest IN"); + + OCStackResult result = OC_STACK_OK; + if (CA_PUT == requestInfo->method) + { + result = HandleKeepAlivePUTRequest(endPoint, requestInfo); + } + else if (CA_GET == requestInfo->method) + { + result = HandleKeepAliveGETRequest(endPoint, requestInfo); + } + + OC_LOG(DEBUG, TAG, "HandleKeepAliveRequest OUT"); + return result; +} + +OCStackResult HandleKeepAliveGETRequest(const CAEndpoint_t* endPoint, + const CARequestInfo_t* requestInfo) +{ + VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM); + VERIFY_NON_NULL(requestInfo, FATAL, OC_STACK_INVALID_PARAM); + + OC_LOG_V(DEBUG, TAG, "Find Ping resource [%s]", requestInfo->info.resourceUri); + + CAResponseResult_t result = CA_VALID; + OCResource *resourcePtr = FindResourceByUri(requestInfo->info.resourceUri); + if (!resourcePtr) + { + // Resource URL not specified + OC_LOG_V(DEBUG, TAG, "There is no Ping resource [%s]", requestInfo->info.resourceUri); + result = CA_NOT_FOUND; + } + + SendDirectStackResponse(endPoint, requestInfo->info.messageId, result, requestInfo->info.type, + requestInfo->info.numOptions, requestInfo->info.options, + requestInfo->info.token, requestInfo->info.tokenLength, + requestInfo->info.resourceUri); + + return OC_STACK_OK; +} + +OCStackResult HandleKeepAlivePUTRequest(const CAEndpoint_t* endPoint, + const CARequestInfo_t* requestInfo) +{ + VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM); + VERIFY_NON_NULL(requestInfo, FATAL, OC_STACK_INVALID_PARAM); + + // Get entry from KeepAlive table. + uint32_t index = 0; + KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index); + if (!entry) + { + OC_LOG(ERROR, TAG, "Received the first keepalive message from client"); + entry = AddKeepAliveEntry(endPoint, OC_SERVER); + if (!entry) + { + OC_LOG(ERROR, TAG, "Failed to add new keepalive entry"); + return OC_STACK_ERROR; + } + } + + OCPayload *ocPayload = NULL; + OCParsePayload(&ocPayload, PAYLOAD_TYPE_REPRESENTATION, + requestInfo->info.payload, requestInfo->info.payloadSize); + OCRepPayload *repPayload = (OCRepPayload *)ocPayload; + + uint32_t interval = 0; + OCRepPayloadGetPropInt(repPayload, INTERVAL, &interval); + entry->interval = interval; + OC_LOG_V(DEBUG, TAG, "Received interval is [%d]", entry->interval); + entry->timeStamp = OICGetCurrentTime(TIME_IN_US); + + // Send response message. + SendDirectStackResponse(endPoint, requestInfo->info.messageId, CA_VALID, requestInfo->info.type, + requestInfo->info.numOptions, requestInfo->info.options, + requestInfo->info.token, requestInfo->info.tokenLength, + requestInfo->info.resourceUri); + + return OC_STACK_OK; +} + +OCStackResult HandleKeepAliveResponse(const CAEndpoint_t *endPoint, + OCStackResult responseCode) +{ + VERIFY_NON_NULL(endPoint, FATAL, OC_STACK_INVALID_PARAM); + + OC_LOG(DEBUG, TAG, "HandleKeepAliveResponse IN"); + + // Get entry from KeepAlive table. + uint32_t index = 0; + KeepAliveEntry_t *entry = GetEntryFromEndpoint(endPoint, &index); + if (!entry) + { + OC_LOG(ERROR, TAG, "There is no connection info in KeepAlive table"); + + if (OC_STACK_NO_RESOURCE == responseCode) + { + OC_LOG(ERROR, TAG, "Server doesn't have a ping resource"); + return OC_STACK_ERROR; + } + else if (OC_STACK_OK == responseCode) + { + entry = AddKeepAliveEntry(endPoint, OC_CLIENT); + if (!entry) + { + OC_LOG(ERROR, TAG, "Failed to add new KeepAlive entry"); + return OC_STACK_ERROR; + } + + // Send first ping message + return SendPingMessage(entry); + } + } + + // Set sentPingMsg values with false. + entry->sentPingMsg = false; + + OC_LOG(DEBUG, TAG, "HandleKeepAliveResponse OUT"); + return OC_STACK_OK; +} + +void ProcessKeepAlive() +{ + if (!g_isKeepAliveInitialized) + { + OC_LOG(ERROR, TAG, "KeepAlive not initialized"); + return; + } + + uint32_t len = u_arraylist_length(g_keepAliveConnectionTable); + + for (uint32_t i = 0; i < len; i++) + { + KeepAliveEntry_t *entry = u_arraylist_get(g_keepAliveConnectionTable, i); + if (NULL == entry) + { + continue; + } + + uint64_t currentTime = OICGetCurrentTime(TIME_IN_US); + if (OC_CLIENT == entry->mode) + { + if (entry->sentPingMsg) + { + /* + * If an OIC Client does not receive the response within 1 minutes, + * terminate the connection. + * In this case the timeStamp means last time sent ping message. + */ + if ((KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) <= currentTime - entry->timeStamp) + { + OC_LOG(DEBUG, TAG, "Client does not receive the response within 1 minutes."); + + // Send message to disconnect session. + SendDisconnectMessage(entry); + } + } + else + { + if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) + <= currentTime - entry->timeStamp) + { + // Increase interval value. + if (KEEPALIVE_MAX_INTERVAL > entry->interval) + { + entry->interval = entry->interval << 1; + } + + OCStackResult result = SendPingMessage(entry); + if (OC_STACK_OK != result) + { + OC_LOG(ERROR, TAG, "Failed to send ping request"); + continue; + } + } + } + } + else if (OC_SERVER == entry->mode) + { + /* + * If an OIC Server does not receive a PUT request to ping resource + * within the specified interval time, terminate the connection. + * In this case the timeStamp means last time received ping message. + */ + if ((entry->interval * KEEPALIVE_RESPONSE_TIMEOUT_SEC * USECS_PER_SEC) + <= currentTime - entry->timeStamp) + { + OC_LOG(DEBUG, TAG, "Server does not receive a PUT request."); + SendDisconnectMessage(entry); + } + } + } +} + +OCStackResult SendDisconnectMessage(const KeepAliveEntry_t *entry) +{ + VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM); + + /* + * Send empty message to disconnect a connection. + * If CA get the empty message from RI, CA will disconnect a connection. + */ + CARequestInfo_t requestInfo = { .method = CA_PUT }; + return CASendRequest(&entry->remoteAddr, &requestInfo); +} + +OCStackResult SendPingMessage(KeepAliveEntry_t *entry) +{ + VERIFY_NON_NULL(entry, FATAL, OC_STACK_INVALID_PARAM); + + // Send ping message. + OCCallbackData pingData = { .cb = PingRequestCallback }; + OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP }; + CopyEndpointToDevAddr(&(entry->remoteAddr), &devAddr); + + OCRepPayload *payload = OCRepPayloadCreate(); + if (!payload) + { + OC_LOG(ERROR, TAG, "Failed to allocate Payload"); + return OC_STACK_ERROR; + } + payload->base.type = PAYLOAD_TYPE_REPRESENTATION; + OCRepPayloadSetPropInt(payload, INTERVAL, entry->interval); + + OCDoResource(NULL, OC_REST_PUT, KEEPALIVE_RESOURCE_URI, &devAddr, + (OCPayload *) payload, CT_ADAPTER_TCP, OC_LOW_QOS, &pingData, NULL, 0); + + // Update timeStamp with time sent ping message for next ping message. + entry->timeStamp = OICGetCurrentTime(TIME_IN_US); + entry->sentPingMsg = true; + + OC_LOG_V(DEBUG, TAG, "Client sent ping message, interval [%d]", entry->interval); + + return OC_STACK_OK; +} + +OCStackApplicationResult PingRequestCallback(void* ctx, OCDoHandle handle, + OCClientResponse *clientResponse) +{ + OC_LOG(DEBUG, TAG, "PingRequestCallback IN"); + (void) ctx; + (void) handle; + if (NULL == clientResponse) + { + OC_LOG(ERROR, TAG, "clientResponse is NULL"); + return OC_STACK_KEEP_TRANSACTION; + } + + CAEndpoint_t endpoint = { .adapter = CA_ADAPTER_TCP }; + CopyDevAddrToEndpoint(&(clientResponse->devAddr), &endpoint); + + HandleKeepAliveResponse(&endpoint, clientResponse->result); + + OC_LOG(DEBUG, TAG, "PingRequestCallback OUT"); + return OC_STACK_KEEP_TRANSACTION; +} + +KeepAliveEntry_t *GetEntryFromEndpoint(const CAEndpoint_t *endpoint, uint32_t *index) +{ + if (!g_keepAliveConnectionTable) + { + OC_LOG(ERROR, TAG, "KeepAlive Table was not Created."); + return NULL; + } + + uint32_t len = u_arraylist_length(g_keepAliveConnectionTable); + + for (uint32_t i = 0; i < len; i++) + { + KeepAliveEntry_t *entry = u_arraylist_get(g_keepAliveConnectionTable, i); + if (NULL == entry) + { + continue; + } + + if (!strncmp(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr)) + && (entry->remoteAddr.port == endpoint->port)) + { + OC_LOG(DEBUG, TAG, "Connection Info found in KeepAlive table"); + *index = i; + return entry; + } + } + + return NULL; +} + +KeepAliveEntry_t *AddKeepAliveEntry(const CAEndpoint_t *endpoint, OCMode mode) +{ + if (!endpoint) + { + OC_LOG(ERROR, TAG, "endpoint is NULL"); + return NULL; + } + + if (!g_keepAliveConnectionTable) + { + OC_LOG(ERROR, TAG, "KeepAlive Table was not Created."); + return NULL; + } + + KeepAliveEntry_t *entry = (KeepAliveEntry_t *) OICCalloc(1, sizeof(KeepAliveEntry_t)); + if (NULL == entry) + { + OC_LOG(ERROR, TAG, "Failed to Calloc KeepAlive Entry"); + return NULL; + } + + entry->mode = mode; + entry->timeStamp = OICGetCurrentTime(TIME_IN_US); + entry->interval = KEEPALIVE_MIN_INTERVAL; + entry->remoteAddr.adapter = endpoint->adapter; + entry->remoteAddr.flags = endpoint->flags; + entry->remoteAddr.interface = endpoint->interface; + entry->remoteAddr.port = endpoint->port; + strncpy(entry->remoteAddr.addr, endpoint->addr, sizeof(entry->remoteAddr.addr)); + + bool result = u_arraylist_add(g_keepAliveConnectionTable, (void *)entry); + if (!result) + { + OC_LOG(ERROR, TAG, "Adding node to head failed"); + OICFree(entry); + return NULL; + } + + return entry; +} + +OCStackResult RemoveKeepAliveEntry(const CAEndpoint_t *endpoint) +{ + VERIFY_NON_NULL(endpoint, FATAL, OC_STACK_INVALID_PARAM); + + uint32_t index = 0; + KeepAliveEntry_t *entry = GetEntryFromEndpoint(endpoint, &index); + if (!entry) + { + OC_LOG(ERROR, TAG, "There is no entry in keepalive table."); + return OC_STACK_ERROR; + } + + KeepAliveEntry_t *removedEntry = u_arraylist_remove(g_keepAliveConnectionTable, index); + if (NULL == removedEntry) + { + OC_LOG(ERROR, TAG, "Removed Entry is NULL"); + return OC_STACK_ERROR; + } + + OC_LOG_V(DEBUG, TAG, "Remove Connection Info from KeepAlive table, " + "remote addr=%s port:%d", removedEntry->remoteAddr.addr, + removedEntry->remoteAddr.port); + + OICFree(removedEntry); + + return OC_STACK_OK; +} + +void HandleKeepAliveConnCB(const CAEndpoint_t *endpoint) +{ + VERIFY_NON_NULL_NR(endpoint, FATAL); + + OC_LOG(DEBUG, TAG, "Received the connected device information from CA"); + + // Send discover message to find ping resource + OCCallbackData pingData = { .cb = PingRequestCallback }; + OCDevAddr devAddr = { .adapter = OC_ADAPTER_TCP }; + CopyEndpointToDevAddr(endpoint, &devAddr); + return OCDoResource(NULL, OC_REST_DISCOVER, KEEPALIVE_RESOURCE_URI, &devAddr, NULL, + OC_ADAPTER_TCP, OC_HIGH_QOS, &pingData, NULL, 0); +} + +void HandleKeepAliveDisconnCB(const CAEndpoint_t *endpoint) +{ + VERIFY_NON_NULL_NR(endpoint, FATAL); + + OC_LOG(DEBUG, TAG, "Received the disconnected device information from CA"); + + OCStackResult result = RemoveKeepAliveEntry(endpoint); + if(result != OC_STACK_OK) + { + OC_LOG(ERROR, TAG, "Failed to remove entry"); + return; + } +} -- 2.7.4