[IOT-1685] Make /oic/res observable.
authorTodd Malsbary <todd.malsbary@intel.com>
Wed, 7 Dec 2016 01:57:38 +0000 (17:57 -0800)
committerHabib Virji <habib.virji@samsung.com>
Wed, 8 Feb 2017 12:10:12 +0000 (12:10 +0000)
The application is expected to do the following when it wishes to
enable this behavior:
- handle = OCGetResourceHandleAtUri(OC_RSRVD_WELL_KNOWN_URI)
- OCSetResourceProperties(handle, OC_DISCOVERABLE|OC_OBSERVABLE)
- OCNotifyAllObservers(handle, OC_NA_QOS)

Note that this change enables OCNotifyAllObservers for virtual
resources (such as OC_RSRVD_WELL_KNOWN_URI).

Change-Id: I5c49ab168791e6b5a6e3a97f561c2851614e0f8f
Bug: https://jira.iotivity.org/browse/IOT-1685
Signed-off-by: Todd Malsbary <todd.malsbary@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/15633
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Habib Virji <habib.virji@samsung.com>
12 files changed:
resource/csdk/include/octypes.h
resource/csdk/stack/include/internal/ocobserve.h
resource/csdk/stack/include/internal/ocresourcehandler.h
resource/csdk/stack/include/ocstack.h
resource/csdk/stack/include/payload_logging.h
resource/csdk/stack/src/ocobserve.c
resource/csdk/stack/src/ocpayload.c
resource/csdk/stack/src/ocpayloadconvert.c
resource/csdk/stack/src/ocpayloadparse.c
resource/csdk/stack/src/ocresource.c
resource/csdk/stack/src/ocstack.c
resource/csdk/stack/test/stacktests.cpp

index 77b466b..71fb314 100755 (executable)
@@ -1514,6 +1514,7 @@ typedef struct OCEndpointPayload
 typedef struct OCResourcePayload
 {
     char* uri;
+    char* rel;
     OCStringLL* types;
     OCStringLL* interfaces;
     uint8_t bitmap;
index f06ee04..7f05d2c 100644 (file)
@@ -29,6 +29,8 @@
 #ifndef OC_OBSERVE_H
 #define OC_OBSERVE_H
 
+#include "ocserverrequest.h"
+
 /** Maximum number of observers to reach */
 
 #define MAX_OBSERVER_FAILED_COMM         (2)
@@ -261,5 +263,18 @@ GetObserveHeaderOption (uint32_t * observationOption,
                         CAHeaderOption_t *options,
                         uint8_t * numOptions);
 
+/**
+ * Handle registering/deregistering of observers of virtual resources.  Currently only the
+ * well-known virtual resource (/oic/res) may be observable.
+ *
+ * @param request a virtual resource server request
+ *
+ * @return ::OC_STACK_OK on success, ::OC_STACK_DUPLICATE_REQUEST when registering a duplicate
+ *         observer, some other value upon failure.
+ */
+OCStackResult
+HandleVirtualObserveRequest(OCServerRequest *request);
+
+
 #endif //OC_OBSERVE_H
 
index 7515caa..12a5490 100644 (file)
@@ -124,6 +124,12 @@ typedef enum
 } ResourceHandling;
 
 /**
+ * This function returns the virtual resource of the given URI.
+ * @return the virtual resource or ::OC_UNKNOWN_URI
+ */
+OCVirtualResources GetTypeOfVirtualURI(const char* resourceUri);
+
+/**
  * Default entity handler (ie. callback) to be used for resources with
  * no entity handler.
  */
index 0591483..3d438e2 100644 (file)
@@ -511,6 +511,28 @@ const char *OCGetResourceUri(OCResourceHandle handle);
 OCResourceProperty OCGetResourceProperties(OCResourceHandle handle);
 
 /**
+ * This function sets the properties of the resource specified by handle.
+ *
+ * @param handle                Handle of resource.
+ * @param resourceProperties    Properties supported by resource.
+ *                              Example: ::OC_DISCOVERABLE|::OC_OBSERVABLE.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+OCStackResult OCSetResourceProperties(OCResourceHandle handle, uint8_t resourceProperties);
+
+/**
+ * This function removes the properties of the resource specified by handle.
+ *
+ * @param handle                Handle of resource.
+ * @param resourceProperties    Properties not supported by resource.
+ *                              Example: ::OC_DISCOVERABLE|::OC_OBSERVABLE.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+OCStackResult OCClearResourceProperties(OCResourceHandle handle, uint8_t resourceProperties);
+
+/**
  * This function gets the number of resource types of the resource.
  *
  * @param handle            Handle of resource.
index e77baaf..0dd6d09 100644 (file)
@@ -246,6 +246,10 @@ INLINE_API void OCPayloadLogDiscovery(LogLevel level, OCDiscoveryPayload* payloa
         {
             OIC_LOG_V(level, PL_TAG, "\tLink#%d", i);
             OIC_LOG_V(level, PL_TAG, "\tURI:%s", res->uri);
+            if (res->rel)
+            {
+                OIC_LOG_V(level, PL_TAG, "\tRelation:%s", res->rel);
+            }
             OIC_LOG(level, PL_TAG, "\tResource Types:");
             OCStringLL* strll =  res->types;
             while(strll)
index f5ca966..3520953 100644 (file)
@@ -109,8 +109,6 @@ static OCStackResult SendObserveNotification(ResourceObserver *observer,
 {
     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,
@@ -124,33 +122,15 @@ static OCStackResult SendObserveNotification(ResourceObserver *observer,
         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);
+            ResourceHandling resHandling = OC_RESOURCE_VIRTUAL;
+            OCResource *resource = NULL;
+            result = DetermineResourceHandling (request, &resHandling, &resource);
             if (result == OC_STACK_OK)
             {
-                ehResult = observer->resource->entityHandler(OC_REQUEST_FLAG, &ehRequest,
-                                    observer->resource->entityHandlerCallbackParam);
-                if (ehResult == OC_EH_ERROR)
-                {
-                    FindAndDeleteServerRequest(request);
-                }
+                result = ProcessRequest(resHandling, resource, request);
                 // Reset Observer TTL.
                 observer->TTL = GetTicks(MAX_OBSERVER_TTL_SECONDS * MILLISECONDS_PER_SECOND);
             }
-            OCPayloadDestroy(ehRequest.payload);
         }
     }
 
@@ -683,3 +663,95 @@ GetObserveHeaderOption (uint32_t * observationOption,
     }
     return OC_STACK_OK;
 }
+
+OCStackResult
+HandleVirtualObserveRequest(OCServerRequest *request)
+{
+    OCStackResult result = OC_STACK_OK;
+    if (request->notificationFlag)
+    {
+        // The request is to send an observe payload, not register/deregister an observer
+        goto exit;
+    }
+    OCVirtualResources virtualUriInRequest;
+    virtualUriInRequest = GetTypeOfVirtualURI(request->resourceUrl);
+    if (virtualUriInRequest != OC_WELL_KNOWN_URI)
+    {
+        // OC_WELL_KNOWN_URI is currently the only virtual resource that may be observed
+        goto exit;
+    }
+    OCResource *resourcePtr;
+    resourcePtr = FindResourceByUri(OC_RSRVD_WELL_KNOWN_URI);
+    if (NULL == resourcePtr)
+    {
+        OIC_LOG(FATAL, TAG, "Well-known URI not found.");
+        result = OC_STACK_ERROR;
+        goto exit;
+    }
+    if (request->observationOption == OC_OBSERVE_REGISTER)
+    {
+        OIC_LOG(INFO, TAG, "Observation registration requested");
+        ResourceObserver *obs = GetObserverUsingToken (request->requestToken,
+                                                       request->tokenLength);
+        if (obs)
+        {
+            OIC_LOG (INFO, TAG, "Observer with this token already present");
+            OIC_LOG (INFO, TAG, "Possibly re-transmitted CON OBS request");
+            OIC_LOG (INFO, TAG, "Not adding observer. Not responding to client");
+            OIC_LOG (INFO, TAG, "The first request for this token is already ACKED.");
+            result = OC_STACK_DUPLICATE_REQUEST;
+            goto exit;
+        }
+        OCObservationId obsId;
+        result = GenerateObserverId(&obsId);
+        if (result == OC_STACK_OK)
+        {
+            result = AddObserver ((const char*)(request->resourceUrl),
+                                  (const char *)(request->query),
+                                  obsId, request->requestToken, request->tokenLength,
+                                  resourcePtr, request->qos, request->acceptFormat,
+                                  request->acceptVersion, &request->devAddr);
+        }
+        if (result == OC_STACK_OK)
+        {
+            OIC_LOG(INFO, TAG, "Added observer successfully");
+            request->observeResult = OC_STACK_OK;
+        }
+        else if (result == OC_STACK_RESOURCE_ERROR)
+        {
+            OIC_LOG(INFO, TAG, "The Resource is not active, discoverable or observable");
+            request->observeResult = OC_STACK_ERROR;
+        }
+        else
+        {
+            // The error in observeResult for the request will be used when responding to this
+            // request by omitting the observation option/sequence number.
+            request->observeResult = OC_STACK_ERROR;
+            OIC_LOG(ERROR, TAG, "Observer Addition failed");
+        }
+    }
+    else if (request->observationOption == OC_OBSERVE_DEREGISTER)
+    {
+        OIC_LOG(INFO, TAG, "Deregistering observation requested");
+        result = DeleteObserverUsingToken (request->requestToken, request->tokenLength);
+        if (result == OC_STACK_OK)
+        {
+            OIC_LOG(INFO, TAG, "Removed observer successfully");
+            request->observeResult = OC_STACK_OK;
+            // There should be no observe option header for de-registration response.
+            // Set as an invalid value here so we can detect it later and remove the field in response.
+            request->observationOption = MAX_SEQUENCE_NUMBER + 1;
+        }
+        else
+        {
+            request->observeResult = OC_STACK_ERROR;
+            OIC_LOG(ERROR, TAG, "Observer Removal failed");
+        }
+    }
+    // Whether the observe request succeeded or failed, the request is processed as normal
+    // and excludes/includes the OBSERVE option depending on request->observeResult
+    result = OC_STACK_OK;
+
+exit:
+    return result;
+}
index c52be01..b8d663e 100644 (file)
@@ -1769,6 +1769,18 @@ static OCResourcePayload* OCCopyResource(const OCResource* res, uint16_t secureP
         return NULL;
     }
 
+    // relation is always the default unless the resource is the well known URI
+    if (0 == strcmp(res->uri, OC_RSRVD_WELL_KNOWN_URI))
+    {
+        pl->rel = OICStrdup("self");
+
+        if (!pl->rel)
+        {
+            OCDiscoveryResourceDestroy(pl);
+            return NULL;
+        }
+    }
+
     // types
     OCResourceType* typePtr = res->rsrcType;
 
@@ -2049,6 +2061,7 @@ void OCDiscoveryResourceDestroy(OCResourcePayload* payload)
     }
 
     OICFree(payload->uri);
+    OICFree(payload->rel);
     OCFreeOCStringLL(payload->types);
     OCFreeOCStringLL(payload->interfaces);
     OCDiscoveryEndpointDestroy(payload->eps);
index 26be51b..e360790 100755 (executable)
@@ -298,20 +298,18 @@ static int64_t OCConvertDiscoveryPayload(OCDiscoveryPayload *payload, uint8_t *o
         for (size_t i = 0; i < resourceCount; ++i)
         {
             CborEncoder linkMap;
+            size_t linkMapLen;
             OCResourcePayload *resource = OCDiscoveryPayloadGetResource(payload, i);
             VERIFY_PARAM_NON_NULL(TAG, resource, "Failed retrieving resource");
 
             // resource map inside the links array.
-            if (resource->eps)
+            linkMapLen = resource->eps ? LINKS_MAP_LEN_WITH_EPS : LINKS_MAP_LEN_WITHOUT_EPS;
+            if (resource->rel)
             {
-                err |= cbor_encoder_create_map(&linkArray, &linkMap, LINKS_MAP_LEN_WITH_EPS);
-                VERIFY_CBOR_SUCCESS(TAG, err, "Failed creating links map");
-            }
-            else
-            {
-                err |= cbor_encoder_create_map(&linkArray, &linkMap, LINKS_MAP_LEN_WITHOUT_EPS);
-                VERIFY_CBOR_SUCCESS(TAG, err, "Failed creating links map");
+                ++linkMapLen;
             }
+            err |= cbor_encoder_create_map(&linkArray, &linkMap, linkMapLen);
+            VERIFY_CBOR_SUCCESS(TAG, err, "Failed creating links map");
 
             // Below are insertions of the resource properties into the map.
             // Uri
@@ -319,6 +317,14 @@ static int64_t OCConvertDiscoveryPayload(OCDiscoveryPayload *payload, uint8_t *o
                     resource->uri);
             VERIFY_CBOR_SUCCESS(TAG, err, "Failed adding uri to links map");
 
+            // Rel - Not a mandatory field
+            if (resource->rel)
+            {
+                err |= AddTextStringToMap(&linkMap, OC_RSRVD_REL, sizeof(OC_RSRVD_REL) - 1,
+                        resource->rel);
+                VERIFY_CBOR_SUCCESS(TAG, err, "Failed adding rel to links map");
+            }
+
             // Resource Type
             if (resource->types)
             {
index 4d0dbaa..e6738f5 100755 (executable)
@@ -278,6 +278,15 @@ static OCStackResult OCParseDiscoveryPayload(OCPayload **outPayload, CborValue *
                 err = cbor_value_dup_text_string(&curVal, &(resource->uri), &len, NULL);
                 VERIFY_CBOR_SUCCESS(TAG, err, "to find href value");
 
+                // Rel - Not a mandatory field
+                err = cbor_value_map_find_value(&resourceMap, OC_RSRVD_REL, &curVal);
+                VERIFY_CBOR_SUCCESS(TAG, err, "to find rel tag");
+                if (cbor_value_is_valid(&curVal))
+                {
+                    err = cbor_value_dup_text_string(&curVal, &(resource->rel), &len, NULL);
+                    VERIFY_CBOR_SUCCESS(TAG, err, "to find rel value");
+                }
+
                 // ResourceTypes
                 err =  OCParseStringLL(&resourceMap, OC_RSRVD_RESOURCE_TYPE, &resource->types);
                 VERIFY_CBOR_SUCCESS(TAG, err, "to find resource type tag/value");
index ac38105..cd74b4c 100755 (executable)
@@ -245,7 +245,7 @@ exit:
     return eCode;
 }
 
-static OCVirtualResources GetTypeOfVirtualURI(const char *uriInRequest)
+OCVirtualResources GetTypeOfVirtualURI(const char *uriInRequest)
 {
     if (strcmp(uriInRequest, OC_RSRVD_WELL_KNOWN_URI) == 0)
     {
@@ -1253,7 +1253,8 @@ static OCStackResult EHRequest(OCEntityHandlerRequest *ehRequest, OCPayloadType
                                      request->payloadSize,
                                      request->numRcvdVendorSpecificHeaderOptions,
                                      request->rcvdVendorSpecificHeaderOptions,
-                                     (OCObserveAction)request->observationOption,
+                                     (OCObserveAction)(request->notificationFlag ? OC_OBSERVE_NO_OPTION :
+                                                       request->observationOption),
                                      (OCObservationId)0,
                                      request->coapID);
 }
@@ -1400,6 +1401,22 @@ static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource
         return OC_STACK_UNAUTHORIZED_REQ;
     }
 
+    discoveryResult = HandleVirtualObserveRequest(request);
+    if (discoveryResult == OC_STACK_DUPLICATE_REQUEST)
+    {
+        // server requests are usually free'd when the response is sent out
+        // for the request in ocserverrequest.c : HandleSingleResponse()
+        // Since we are making an early return and not responding, the server request
+        // needs to be deleted.
+        FindAndDeleteServerRequest (request);
+        discoveryResult = OC_STACK_OK;
+        goto exit;
+    }
+    else if (discoveryResult != OC_STACK_OK)
+    {
+        goto exit;
+    }
+
     // Step 1: Generate the response to discovery request
     if (virtualUriInRequest == OC_WELL_KNOWN_URI
 #ifdef MQ_BROKER
index 73266c1..bf99983 100644 (file)
@@ -123,6 +123,7 @@ static OCResourceHandle platformResource = {0};
 static OCResourceHandle deviceResource = {0};
 static OCResourceHandle introspectionResource = {0};
 static OCResourceHandle introspectionPayloadResource = {0};
+static OCResourceHandle wellKnownResource = {0};
 #ifdef MQ_BROKER
 static OCResourceHandle brokerResource = {0};
 #endif
@@ -4100,6 +4101,34 @@ OCResourceProperty OCGetResourceProperties(OCResourceHandle handle)
     return (OCResourceProperty)-1;
 }
 
+OCStackResult OCSetResourceProperties(OCResourceHandle handle, uint8_t resourceProperties)
+{
+    OCResource *resource = NULL;
+
+    resource = findResource((OCResource *) handle);
+    if (resource == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Resource not found");
+        return OC_STACK_NO_RESOURCE;
+    }
+    resource->resourceProperties = (OCResourceProperty) (resource->resourceProperties | resourceProperties);
+    return OC_STACK_OK;
+}
+
+OCStackResult OCClearResourceProperties(OCResourceHandle handle, uint8_t resourceProperties)
+{
+    OCResource *resource = NULL;
+
+    resource = findResource((OCResource *) handle);
+    if (resource == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Resource not found");
+        return OC_STACK_NO_RESOURCE;
+    }
+    resource->resourceProperties = (OCResourceProperty) (resource->resourceProperties & ~resourceProperties);
+    return OC_STACK_OK;
+}
+
 OCStackResult OCGetNumberOfResourceTypes(OCResourceHandle handle,
         uint8_t *numResourceTypes)
 {
@@ -4511,6 +4540,22 @@ OCStackResult initResources()
 
     if(result == OC_STACK_OK)
     {
+        result = OCCreateResource(&wellKnownResource,
+                                  OC_RSRVD_RESOURCE_TYPE_RES,
+                                  OC_RSRVD_INTERFACE_LL,
+                                  OC_RSRVD_WELL_KNOWN_URI,
+                                  NULL,
+                                  NULL,
+                                  0);
+        if(result == OC_STACK_OK)
+        {
+            result = BindResourceInterfaceToResource((OCResource *)wellKnownResource,
+                                                     OC_RSRVD_INTERFACE_DEFAULT);
+        }
+    }
+
+    if(result == OC_STACK_OK)
+    {
         CreateResetProfile();
         result = OCCreateResource(&deviceResource,
                                   OC_RSRVD_RESOURCE_TYPE_DEVICE,
@@ -4627,6 +4672,7 @@ void deleteAllResources()
     }
     memset(&platformResource, 0, sizeof(platformResource));
     memset(&deviceResource, 0, sizeof(deviceResource));
+    memset(&wellKnownResource, 0, sizeof(wellKnownResource));
 #ifdef MQ_BROKER
     memset(&brokerResource, 0, sizeof(brokerResource));
 #endif
index 3d6f6a3..a22bda9 100644 (file)
@@ -1241,6 +1241,60 @@ TEST(StackResource, GetResourceProperties)
     EXPECT_EQ(OC_STACK_OK, OCStop());
 }
 
+TEST(StackResource, SetResourceProperties)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+    OIC_LOG(INFO, TAG, "Starting SetResourceProperties test");
+    InitStack(OC_SERVER);
+
+    OCResourceHandle handle;
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handle,
+                                            "core.led",
+                                            "core.rw",
+                                            "/a/led",
+                                            0,
+                                            NULL,
+                                            0));
+
+    EXPECT_EQ(OC_STACK_OK, OCSetResourceProperties(handle, OC_DISCOVERABLE|OC_OBSERVABLE));
+#ifdef MQ_PUBLISHER
+    EXPECT_EQ(OC_ACTIVE|OC_DISCOVERABLE|OC_OBSERVABLE|OC_MQ_PUBLISHER, OCGetResourceProperties(handle));
+#else
+    EXPECT_EQ(OC_ACTIVE|OC_DISCOVERABLE|OC_OBSERVABLE, OCGetResourceProperties(handle));
+#endif
+
+    EXPECT_EQ(OC_STACK_OK, OCDeleteResource(handle));
+
+    EXPECT_EQ(OC_STACK_OK, OCStop());
+}
+
+TEST(StackResource, ClearResourceProperties)
+{
+    itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);
+    OIC_LOG(INFO, TAG, "Starting ClearResourceProperties test");
+    InitStack(OC_SERVER);
+
+    OCResourceHandle handle;
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handle,
+                                            "core.led",
+                                            "core.rw",
+                                            "/a/led",
+                                            0,
+                                            NULL,
+                                            OC_DISCOVERABLE|OC_OBSERVABLE));
+
+    EXPECT_EQ(OC_STACK_OK, OCClearResourceProperties(handle, OC_DISCOVERABLE|OC_OBSERVABLE));
+#ifdef MQ_PUBLISHER
+    EXPECT_EQ(OC_ACTIVE|OC_MQ_PUBLISHER, OCGetResourceProperties(handle));
+#else
+    EXPECT_EQ(OC_ACTIVE, OCGetResourceProperties(handle));
+#endif
+
+    EXPECT_EQ(OC_STACK_OK, OCDeleteResource(handle));
+
+    EXPECT_EQ(OC_STACK_OK, OCStop());
+}
+
 TEST(StackResource, StackTestResourceDiscoverOneResourceBad)
 {
     itst::DeadmanTimer killSwitch(SHORT_TEST_TIMEOUT);