Add a maximum observer TTL of 24 hours
authorhyuna0213.jo <hyuna0213.jo@samsung.com>
Wed, 7 Sep 2016 07:30:45 +0000 (16:30 +0900)
committerAshok Babu Channa <ashok.channa@samsung.com>
Mon, 24 Oct 2016 08:21:28 +0000 (08:21 +0000)
A server that transmits notifications mostly in non-confirmable
messages MUST send a notification in a confirmable message instead of
a non-confirmable message at least every 24 hours. This prevents a
client that went away or is no longer interested from remaining in the
list of observers indefinitely.

Change-Id: Ie676f9f5f394fa094b4f2d9fd3a72e2d38d21b24
Signed-off-by: hyuna0213.jo <hyuna0213.jo@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/11497
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Jaehong Jo <jaehong.jo@samsung.com>
Reviewed-by: Ashok Babu Channa <ashok.channa@samsung.com>
resource/csdk/connectivity/src/tcp_adapter/catcpserver.c
resource/csdk/stack/include/internal/ocobserve.h
resource/csdk/stack/include/internal/ocstackinternal.h
resource/csdk/stack/src/ocobserve.c
resource/csdk/stack/src/ocstack.c

index f18a6c9..10f4ee0 100644 (file)
@@ -998,7 +998,7 @@ static void sendData(const CAEndpoint_t *endpoint, const void *data,
             OIC_LOG(ERROR, TAG, "Failed to create TCP server object");
             if (g_tcpErrorHandler)
             {
-                g_tcpErrorHandler(endpoint, data, dlen, CA_SEND_FAILED);
+                g_tcpErrorHandler(endpoint, data, dlen, CA_SOCKET_OPERATION_FAILED);
             }
             return;
         }
@@ -1027,7 +1027,7 @@ static void sendData(const CAEndpoint_t *endpoint, const void *data,
         CADisconnectTCPSession(svritem, index);
         if (g_tcpErrorHandler)
         {
-            g_tcpErrorHandler(endpoint, data, dlen, CA_SEND_FAILED);
+            g_tcpErrorHandler(endpoint, data, dlen, CA_SOCKET_OPERATION_FAILED);
         }
         return;
     }
index 1477d05..85ecd4e 100644 (file)
 #define MAX_OBSERVER_NON_COUNT           (3)
 
 /**
+ *  MAX_OBSERVER_TTL_SECONDS sets the maximum time to live (TTL) for notification.
+ *  60 sec/min * 60 min/hr * 24 hr/day
+ */
+#define MAX_OBSERVER_TTL_SECONDS     (60 * 60 * 24)
+
+#define MILLISECONDS_PER_SECOND   (1000)
+
+/**
  * Data structure to hold informations for each registered observer.
  */
 typedef struct ResourceObserver
@@ -74,6 +82,12 @@ typedef struct ResourceObserver
     /** force the qos value to CON.*/
     uint8_t forceHighQos;
 
+    /** The TTL for this callback. TTL is set to 24 hours.
+     * A server send a notification in a confirmable message every 24 hours.
+     * This prevents a client that went away or is no logger interested
+     * from remaining in the list of observers indefinitely.*/
+    uint32_t TTL;
+
     /** next node in this list.*/
     struct ResourceObserver *next;
 
index c093284..a2a4412 100644 (file)
@@ -283,6 +283,14 @@ void CopyEndpointToDevAddr(const CAEndpoint_t *in, OCDevAddr *out);
 
 void CopyDevAddrToEndpoint(const OCDevAddr *in, CAEndpoint_t *out);
 
+/**
+ * Get the CoAP ticks after the specified number of milli-seconds.
+ *
+ * @param milliSeconds Milli-seconds.
+ * @return CoAP ticks
+ */
+uint32_t GetTicks(uint32_t milliSeconds);
+
 #ifdef __cplusplus
 }
 #endif // __cplusplus
index ea8fea9..b49b615 100644 (file)
@@ -33,7 +33,7 @@
 
 #include <coap/utlist.h>
 #include <coap/pdu.h>
-
+#include <coap/coap.h>
 
 // Module Name
 #define MOD_NAME "ocobserve"
@@ -96,6 +96,67 @@ static OCQualityOfService DetermineObserverQoS(OCMethod method,
     return decidedQoS;
 }
 
+/**
+ * Create a get request and pass to entityhandler to notify specific observer.
+ *
+ * @param observer Observer that need to be notified.
+ * @param qos Quality of service of resource.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+static OCStackResult SendObserveNotification(ResourceObserver *observer,
+                                             OCQualityOfService qos)
+{
+    OCStackResult result = OC_STACK_ERROR;
+    OCServerRequest * request = NULL;
+    OCEntityHandlerRequest ehRequest = {0};
+    OCEntityHandlerResult ehResult = OC_EH_ERROR;
+
+    result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
+                              0, observer->resource->sequenceNum, qos,
+                              observer->query, NULL, NULL,
+                              observer->token, observer->tokenLength,
+                              observer->resUri, 0, observer->acceptFormat,
+                              &observer->devAddr);
+
+    if (request)
+    {
+        request->observeResult = OC_STACK_OK;
+        if (result == OC_STACK_OK)
+        {
+            result = FormOCEntityHandlerRequest(
+                        &ehRequest,
+                        (OCRequestHandle) request,
+                        request->method,
+                        &request->devAddr,
+                        (OCResourceHandle) observer->resource,
+                        request->query,
+                        PAYLOAD_TYPE_REPRESENTATION,
+                        request->payload,
+                        request->payloadSize,
+                        request->numRcvdVendorSpecificHeaderOptions,
+                        request->rcvdVendorSpecificHeaderOptions,
+                        OC_OBSERVE_NO_OPTION,
+                        0,
+                        request->coapID);
+            if (result == OC_STACK_OK)
+            {
+                ehResult = observer->resource->entityHandler(OC_REQUEST_FLAG, &ehRequest,
+                                    observer->resource->entityHandlerCallbackParam);
+                if (ehResult == OC_EH_ERROR)
+                {
+                    FindAndDeleteServerRequest(request);
+                }
+                // Reset Observer TTL.
+                observer->TTL = GetTicks(MAX_OBSERVER_TTL_SECONDS * MILLISECONDS_PER_SECOND);
+            }
+            OCPayloadDestroy(ehRequest.payload);
+        }
+    }
+
+    return result;
+}
+
 #ifdef WITH_PRESENCE
 OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr, uint32_t maxAge,
         OCPresenceTrigger trigger, OCResourceType *resourceType, OCQualityOfService qos)
@@ -129,46 +190,7 @@ OCStackResult SendAllObserverNotification (OCMethod method, OCResource *resPtr,
             {
 #endif
                 qos = DetermineObserverQoS(method, resourceObserver, qos);
-
-                result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
-                        0, resPtr->sequenceNum, qos, resourceObserver->query,
-                        NULL, NULL,
-                        resourceObserver->token, resourceObserver->tokenLength,
-                        resourceObserver->resUri, 0, resourceObserver->acceptFormat,
-                        &resourceObserver->devAddr);
-
-                if (request)
-                {
-                    request->observeResult = OC_STACK_OK;
-                    if (result == OC_STACK_OK)
-                    {
-                        result = FormOCEntityHandlerRequest(
-                                    &ehRequest,
-                                    (OCRequestHandle) request,
-                                    request->method,
-                                    &request->devAddr,
-                                    (OCResourceHandle) resPtr,
-                                    request->query,
-                                    PAYLOAD_TYPE_REPRESENTATION,
-                                    request->payload,
-                                    request->payloadSize,
-                                    request->numRcvdVendorSpecificHeaderOptions,
-                                    request->rcvdVendorSpecificHeaderOptions,
-                                    OC_OBSERVE_NO_OPTION,
-                                    0,
-                                    request->coapID);
-                        if (result == OC_STACK_OK)
-                        {
-                            ehResult = resPtr->entityHandler(OC_REQUEST_FLAG, &ehRequest,
-                                                resPtr->entityHandlerCallbackParam);
-                            if (ehResult == OC_EH_ERROR)
-                            {
-                                FindAndDeleteServerRequest(request);
-                            }
-                        }
-                        OCPayloadDestroy(ehRequest.payload);
-                    }
-                }
+                result = SendObserveNotification(resourceObserver, qos);
 #ifdef WITH_PRESENCE
             }
             else
@@ -303,6 +325,9 @@ OCStackResult SendListObserverNotification (OCResource * resource,
                         {
                             OIC_LOG_V(INFO, TAG, "Error notifying observer id %d.", *obsIdList);
                         }
+                        // Reset Observer TTL.
+                        observer->TTL =
+                                GetTicks(MAX_OBSERVER_TTL_SECONDS * MILLISECONDS_PER_SECOND);
                     }
                     else
                     {
@@ -410,6 +435,15 @@ OCStackResult AddObserver (const char         *resUri,
         obsNode->devAddr = *devAddr;
         obsNode->resource = resHandle;
 
+        if ((strcmp(resUri, OC_RSRVD_PRESENCE_URI) == 0))
+        {
+            obsNode->TTL = 0;
+        }
+        else
+        {
+            obsNode->TTL = GetTicks(MAX_OBSERVER_TTL_SECONDS * MILLISECONDS_PER_SECOND);
+        }
+
         LL_APPEND (g_serverObsList, obsNode);
 
         return OC_STACK_OK;
@@ -425,6 +459,31 @@ exit:
     return OC_STACK_NO_MEMORY;
 }
 
+/*
+ * This function checks if the node is past its time to live and
+ * deletes it if timed-out. Calling this function with a  presence callback
+ * with ttl set to 0 will not delete anything as presence nodes have
+ * their own mechanisms for timeouts. A null argument will cause the function to
+ * silently return.
+ */
+static void CheckTimedOutObserver(ResourceObserver* observer)
+{
+    if (!observer || observer->TTL == 0)
+    {
+        return;
+    }
+
+    coap_tick_t now;
+    coap_ticks(&now);
+
+    if (observer->TTL < now)
+    {
+        // Send confirmable notification message to observer.
+        OIC_LOG(INFO, TAG, "Sending High-QoS notification to observer");
+        SendObserveNotification(observer, OC_HIGH_QOS);
+    }
+}
+
 ResourceObserver* GetObserverUsingId (const OCObservationId observeId)
 {
     ResourceObserver *out = NULL;
@@ -437,6 +496,7 @@ ResourceObserver* GetObserverUsingId (const OCObservationId observeId)
             {
                 return out;
             }
+            CheckTimedOutObserver(out);
         }
     }
     OIC_LOG(INFO, TAG, "Observer node not found!!");
@@ -460,6 +520,7 @@ ResourceObserver* GetObserverUsingToken (const CAToken_t token, uint8_t tokenLen
                 OIC_LOG(INFO, TAG, "Found in observer list");
                 return out;
             }
+            CheckTimedOutObserver(out);
         }
     }
     else
index 1365aaa..fff4c24 100644 (file)
@@ -297,23 +297,6 @@ static void incrementSequenceNumber(OCResource * resPtr);
 static CAResult_t OCSelectNetwork();
 
 /**
- * Get the CoAP ticks after the specified number of milli-seconds.
- *
- * @param afterMilliSeconds Milli-seconds.
- * @return
- *     CoAP ticks
- */
-static uint32_t GetTicks(uint32_t afterMilliSeconds);
-
-/**
- * Convert CAResult_t to OCStackResult.
- *
- * @param caResult CAResult_t code.
- * @return ::OC_STACK_OK on success, some other value upon failure.
- */
-static OCStackResult CAResultToOCStackResult(CAResult_t caResult);
-
-/**
  * Convert CAResponseResult_t to OCStackResult.
  *
  * @param caCode CAResponseResult_t code.
@@ -466,16 +449,16 @@ bool checkProxyUri(OCHeaderOption *options, uint8_t numOptions)
     return false;
 }
 
-uint32_t GetTicks(uint32_t afterMilliSeconds)
+uint32_t GetTicks(uint32_t milliSeconds)
 {
     coap_tick_t now;
     coap_ticks(&now);
 
     // Guard against overflow of uint32_t
-    if (afterMilliSeconds <= ((UINT32_MAX - (uint32_t)now) * MILLISECONDS_PER_SECOND) /
+    if (milliSeconds <= ((UINT32_MAX - (uint32_t)now) * MILLISECONDS_PER_SECOND) /
                              COAP_TICKS_PER_SECOND)
     {
-        return now + (afterMilliSeconds * COAP_TICKS_PER_SECOND)/MILLISECONDS_PER_SECOND;
+        return now + (milliSeconds * COAP_TICKS_PER_SECOND)/MILLISECONDS_PER_SECOND;
     }
     else
     {
@@ -656,10 +639,11 @@ OCStackResult OCStackFeedBack(CAToken_t token, uint8_t tokenLength, uint8_t stat
             else
             {
                 observer->failedCommCount++;
+                observer->forceHighQos = 1;
+                OIC_LOG_V(DEBUG, TAG, "Failed count for this observer is %d",
+                          observer->failedCommCount);
                 result = OC_STACK_CONTINUE;
             }
-            observer->forceHighQos = 1;
-            OIC_LOG_V(DEBUG, TAG, "Failed count for this observer is %d",observer->failedCommCount);
         }
         break;
     default:
@@ -670,28 +654,6 @@ OCStackResult OCStackFeedBack(CAToken_t token, uint8_t tokenLength, uint8_t stat
     return result;
 }
 
-static OCStackResult CAResultToOCStackResult(CAResult_t caResult)
-{
-    OCStackResult ret = OC_STACK_ERROR;
-
-    switch(caResult)
-    {
-        case CA_ADAPTER_NOT_ENABLED:
-        case CA_SERVER_NOT_STARTED:
-            ret = OC_STACK_ADAPTER_NOT_ENABLED;
-            break;
-        case CA_MEMORY_ALLOC_FAILED:
-            ret = OC_STACK_NO_MEMORY;
-            break;
-        case CA_STATUS_INVALID_PARAM:
-            ret = OC_STACK_INVALID_PARAM;
-            break;
-        default:
-            break;
-    }
-    return ret;
-}
-
 OCStackResult CAResponseToOCStackResult(CAResponseResult_t caCode)
 {
     OCStackResult ret = OC_STACK_ERROR;
@@ -1631,19 +1593,10 @@ void HandleCAResponses(const CAEndpoint_t* endPoint, const CAResponseInfo_t* res
  */
 void HandleCAErrorResponse(const CAEndpoint_t *endPoint, const CAErrorInfo_t *errorInfo)
 {
-    OIC_LOG(INFO, TAG, "Enter HandleCAErrorResponse");
-
-    if (NULL == endPoint)
-    {
-        OIC_LOG(ERROR, TAG, "endPoint is NULL");
-        return;
-    }
+    VERIFY_NON_NULL_NR(endPoint, FATAL);
+    VERIFY_NON_NULL_NR(errorInfo, FATAL);
 
-    if (NULL == errorInfo)
-    {
-        OIC_LOG(ERROR, TAG, "errorInfo is NULL");
-        return;
-    }
+    OIC_LOG(INFO, TAG, "Enter HandleCAErrorResponse");
 
     ClientCB *cbNode = GetClientCB(errorInfo->info.token,
                                    errorInfo->info.tokenLength, NULL, NULL);
@@ -1656,11 +1609,24 @@ void HandleCAErrorResponse(const CAEndpoint_t *endPoint, const CAErrorInfo_t *er
         memcpy(response.identity.id, errorInfo->info.identity.id,
                sizeof (response.identity.id));
         response.identity.id_length = errorInfo->info.identity.id_length;
-        response.result = CAResultToOCStackResult(errorInfo->result);
+        response.result = CAResultToOCResult(errorInfo->result);
 
         cbNode->callBack(cbNode->context, cbNode->handle, &response);
     }
 
+    ResourceObserver *observer = GetObserverUsingToken(errorInfo->info.token,
+                                                       errorInfo->info.tokenLength);
+    if (observer)
+    {
+        OIC_LOG(INFO, TAG, "Receiving communication error for an observer");
+        OCStackResult result = CAResultToOCResult(errorInfo->result);
+        if (OC_STACK_COMM_ERROR == result)
+        {
+            OCStackFeedBack(errorInfo->info.token, errorInfo->info.tokenLength,
+                            OC_OBSERVER_FAILED_COMM);
+        }
+    }
+
     OIC_LOG(INFO, TAG, "Exit HandleCAErrorResponse");
 }