From 2cf37e8101597b7bee056f77a1dddb872defc637 Mon Sep 17 00:00:00 2001 From: "hyuna0213.jo" Date: Wed, 7 Sep 2016 16:30:45 +0900 Subject: [PATCH] Add a maximum observer TTL of 24 hours 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 Reviewed-on: https://gerrit.iotivity.org/gerrit/11497 Tested-by: jenkins-iotivity Reviewed-by: Jaehong Jo Reviewed-by: Ashok Babu Channa --- .../connectivity/src/tcp_adapter/catcpserver.c | 4 +- resource/csdk/stack/include/internal/ocobserve.h | 14 ++ .../csdk/stack/include/internal/ocstackinternal.h | 8 ++ resource/csdk/stack/src/ocobserve.c | 143 +++++++++++++++------ resource/csdk/stack/src/ocstack.c | 80 ++++-------- 5 files changed, 149 insertions(+), 100 deletions(-) diff --git a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c index f18a6c9..10f4ee0 100644 --- a/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c +++ b/resource/csdk/connectivity/src/tcp_adapter/catcpserver.c @@ -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; } diff --git a/resource/csdk/stack/include/internal/ocobserve.h b/resource/csdk/stack/include/internal/ocobserve.h index 1477d05..85ecd4e 100644 --- a/resource/csdk/stack/include/internal/ocobserve.h +++ b/resource/csdk/stack/include/internal/ocobserve.h @@ -37,6 +37,14 @@ #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; diff --git a/resource/csdk/stack/include/internal/ocstackinternal.h b/resource/csdk/stack/include/internal/ocstackinternal.h index c093284..a2a4412 100644 --- a/resource/csdk/stack/include/internal/ocstackinternal.h +++ b/resource/csdk/stack/include/internal/ocstackinternal.h @@ -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 diff --git a/resource/csdk/stack/src/ocobserve.c b/resource/csdk/stack/src/ocobserve.c index ea8fea9..b49b615 100644 --- a/resource/csdk/stack/src/ocobserve.c +++ b/resource/csdk/stack/src/ocobserve.c @@ -33,7 +33,7 @@ #include #include - +#include // 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 diff --git a/resource/csdk/stack/src/ocstack.c b/resource/csdk/stack/src/ocstack.c index 1365aaa..fff4c24 100644 --- a/resource/csdk/stack/src/ocstack.c +++ b/resource/csdk/stack/src/ocstack.c @@ -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"); } -- 2.7.4