/oic/res error response for query handling
authorHabib Virji <habib.virji@samsung.com>
Tue, 6 Dec 2016 17:51:04 +0000 (17:51 +0000)
committerUze Choi <uzchoi@samsung.com>
Fri, 3 Feb 2017 06:05:10 +0000 (06:05 +0000)
- Stop the error response on invalid multicast /oic/res request.
- An error message is sent in case of unicast.
- In case of empty query for interface and resource type,, it sends an error message.
- Add unit tests to test the callback returned value.

BUG: https://jira.iotivity.org/browse/IOT-1650
Change-Id: I2f5902f46030d28ebcc8dc28c7e2f4fbcb6a1716
Signed-off-by: Habib Virji <habib.virji@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/15197
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Dan Mihai <Daniel.Mihai@microsoft.com>
Reviewed-by: Uze Choi <uzchoi@samsung.com>
resource/csdk/include/octypes.h
resource/csdk/stack/include/payload_logging.h
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 0769d4f..f4e5ec1 100755 (executable)
@@ -1531,9 +1531,6 @@ typedef struct OCDiscoveryPayload
     /** Name */
     char *name;
 
-    /** HREF */
-    char *uri;
-
     /** Resource Type */
     OCStringLL *type;
 
index 39914ce..e77baaf 100644 (file)
@@ -203,13 +203,21 @@ INLINE_API void OCPayloadLogRep(LogLevel level, OCRepPayload* payload)
     }
 }
 
+static void OCStringLLPrint(LogLevel level, OCStringLL *type)
+{
+    for (OCStringLL *strll = type; strll; strll = strll->next)
+    {
+        OIC_LOG_V(level, PL_TAG, "\t\t %s", strll->value);
+    }
+}
+
 INLINE_API void OCPayloadLogDiscovery(LogLevel level, OCDiscoveryPayload* payload)
 {
     OIC_LOG(level, PL_TAG, "Payload Type: Discovery");
 
     while(payload && payload->resources)
     {
-        OIC_LOG_V(level, PL_TAG, "\tSID: %s", payload->sid);
+        OIC_LOG_V(level, PL_TAG, "\tDI: %s", payload->sid);
         if (payload->baseURI)
         {
             OIC_LOG_V(level, PL_TAG, "\tBase URI:%s", payload->baseURI);
@@ -218,21 +226,17 @@ INLINE_API void OCPayloadLogDiscovery(LogLevel level, OCDiscoveryPayload* payloa
         {
             OIC_LOG_V(level, PL_TAG, "\tNAME: %s", payload->name);
         }
-        if (payload->uri)
-        {
-            OIC_LOG_V(level, PL_TAG, "\tURI: %s", payload->uri);
-        }
+
         if (payload->type)
         {
-            for (OCStringLL *strll = payload->type; strll; strll = strll->next)
-            {
-                OIC_LOG_V(level, PL_TAG, "\tResource Type: %s", strll->value);
-            }
+            OIC_LOG(level, PL_TAG, "\tResource Type:");
+            OCStringLLPrint(level, payload->type);
         }
-        OIC_LOG(level, PL_TAG, "\tInterface:");
-        for (OCStringLL *itf = payload->iface; itf; itf = itf->next)
+
+        if (payload->iface)
         {
-            OIC_LOG_V(level, PL_TAG, "\t\t%s", itf->value);
+            OIC_LOG(level, PL_TAG, "\tInterface:");
+            OCStringLLPrint(level, payload->iface);
         }
 
         OCResourcePayload* res = payload->resources;
@@ -240,7 +244,7 @@ INLINE_API void OCPayloadLogDiscovery(LogLevel level, OCDiscoveryPayload* payloa
         uint32_t i = 1;
         while(res)
         {
-            OIC_LOG_V(level, PL_TAG, "\tResource #%d", i);
+            OIC_LOG_V(level, PL_TAG, "\tLink#%d", i);
             OIC_LOG_V(level, PL_TAG, "\tURI:%s", res->uri);
             OIC_LOG(level, PL_TAG, "\tResource Types:");
             OCStringLL* strll =  res->types;
index 640ab72..c52be01 100644 (file)
@@ -2063,7 +2063,6 @@ void OCDiscoveryPayloadDestroy(OCDiscoveryPayload* payload)
     }
     OICFree(payload->sid);
     OICFree(payload->baseURI);
-    OICFree(payload->uri);
     OCFreeOCStringLL(payload->type);
     OICFree(payload->name);
     OCFreeOCStringLL(payload->iface);
index aab39d3..b1d756f 100755 (executable)
@@ -121,7 +121,7 @@ OCStackResult OCConvertPayload(OCPayload* payload, uint8_t** outPayload, size_t*
 
     if (err == CborNoError)
     {
-        if ((curSize < INIT_SIZE) && 
+        if ((curSize < INIT_SIZE) &&
             (PAYLOAD_TYPE_SECURITY != payload->type))
         {
             uint8_t *out2 = (uint8_t *)OICRealloc(out, curSize);
index d18f33f..4d0dbaa 100755 (executable)
@@ -230,20 +230,12 @@ static OCStackResult OCParseDiscoveryPayload(OCPayload **outPayload, CborValue *
                 VERIFY_CBOR_SUCCESS(TAG, err, "to find base uri value");
             }
 
-            // HREF - Not a mandatory field
-            err = cbor_value_map_find_value(&rootMap, OC_RSRVD_HREF, &curVal);
-            if (cbor_value_is_text_string(&curVal))
-            {
-                err = cbor_value_dup_text_string(&curVal, &(temp->uri), &len, NULL);
-                VERIFY_CBOR_SUCCESS(TAG, err, "to find uri value");
-            }
-
             // RT - Not a mandatory field
             err = cbor_value_map_find_value(&rootMap, OC_RSRVD_RESOURCE_TYPE, &curVal);
             if (cbor_value_is_valid(&curVal))
             {
                 err = OCParseStringLL(&rootMap, OC_RSRVD_RESOURCE_TYPE, &temp->type);
-                VERIFY_CBOR_SUCCESS(TAG, err, "to find base uri value");
+                VERIFY_CBOR_SUCCESS(TAG, err, "to find resource type");
             }
 
             // IF - Not a mandatory field
@@ -251,13 +243,7 @@ static OCStackResult OCParseDiscoveryPayload(OCPayload **outPayload, CborValue *
             if (cbor_value_is_valid(&curVal))
             {
                 err =  OCParseStringLL(&rootMap, OC_RSRVD_INTERFACE, &temp->iface);
-            }
-            if (!temp->iface)
-            {
-                if (!OCResourcePayloadAddStringLL(&temp->iface, OC_RSRVD_INTERFACE_LL))
-                {
-                    err = CborErrorOutOfMemory;
-                }
+                VERIFY_CBOR_SUCCESS(TAG, err, "to find interface");
             }
 
             // Name - Not a mandatory field
index 4ab4c92..02c4142 100755 (executable)
@@ -520,7 +520,7 @@ OCStackResult GetIntrospectionDataFromPS(char **data, size_t *size)
     size_t fileSize = 0;
     OCStackResult ret = OC_STACK_ERROR;
     OCPersistentStorage *ps = NULL;
-    
+
     if (!data || *data || !size)
     {
         return OC_STACK_INVALID_PARAM;
@@ -538,7 +538,7 @@ OCStackResult GetIntrospectionDataFromPS(char **data, size_t *size)
     if (fileSize)
     {
         // allocate one more byte to accomodate null terminator for string we are reading.
-        fsData = (uint8_t *)OICCalloc(1, fileSize + 1); 
+        fsData = (uint8_t *)OICCalloc(1, fileSize + 1);
         if (!fsData)
         {
             OIC_LOG(ERROR, TAG, "Could not allocate memory for introspection data");
@@ -641,7 +641,7 @@ OCRepPayload *BuildUrlInfoWithProtocol(const char *protocol)
         result = OC_STACK_ERROR;
         goto exit;
     }
-    
+
 exit:
     if (result != OC_STACK_OK)
     {
@@ -665,8 +665,8 @@ OCStackResult AddProtocolToLL(OCStringLL **protoLL, const char *protocol)
     }
     if (cur)
     {
-        // The intent of the protocol list is to collect all unique protocols available on this 
-        // endpoint. Set an error that can be used to skip processing this protocol further as 
+        // The intent of the protocol list is to collect all unique protocols available on this
+        // endpoint. Set an error that can be used to skip processing this protocol further as
         // it already exists in the list.
         return OC_STACK_INVALID_PARAM;
     }
@@ -840,9 +840,9 @@ OCStackResult BuildIntrospectionResponseRepresentation(const OCResource *resourc
                 proto = proto->next;
                 i++;
             }
-            if (!OCRepPayloadSetPropObjectArrayAsOwner(tempPayload, 
-                                                       OC_RSRVD_INTROSPECTION_URL_INFO, 
-                                                       urlInfoPayload, 
+            if (!OCRepPayloadSetPropObjectArrayAsOwner(tempPayload,
+                                                       OC_RSRVD_INTROSPECTION_URL_INFO,
+                                                       urlInfoPayload,
                                                        dimensions))
             {
                 OIC_LOG_V(ERROR, TAG, "Unable to add urlInfo object to introspection payload ");
@@ -876,7 +876,7 @@ exit:
         }
     }
     FreeProtocolLL(protoLL);
-    
+
     return OC_STACK_OK;
 }
 
@@ -1114,12 +1114,20 @@ static bool resourceMatchesRTFilter(OCResource *resource, char *resourceTypeFilt
         return false;
     }
 
-    // Null or empty is analogous to no filter.
-    if (resourceTypeFilter == NULL || *resourceTypeFilter == 0)
+    // Null is analogous to no filter.i.e. query is of form /oic/res?if=oic.if.baseline or /oic/res,
+    // without rt query.
+    if (NULL == resourceTypeFilter)
     {
         return true;
     }
 
+    // Empty resourceType filter is analogous to error query
+    // It is an error as query is of form /oic/res?rt=
+    if (0 == strlen(resourceTypeFilter))
+    {
+        return false;
+    }
+
     for (OCResourceType *rtPtr = resource->rsrcType; rtPtr; rtPtr = rtPtr->next)
     {
         if (0 == strcmp(rtPtr->resourcetypename, resourceTypeFilter))
@@ -1139,12 +1147,20 @@ static bool resourceMatchesIFFilter(OCResource *resource, char *interfaceFilter)
         return false;
     }
 
-    // Null or empty is analogous to no filter.
-    if (interfaceFilter == NULL || *interfaceFilter == 0)
+    // Null is analogous to no filter i.e. query is of form /oic/res?rt=core.light or /oic/res,
+    // without if query.
+    if (NULL == interfaceFilter)
     {
         return true;
     }
 
+    // Empty interface filter is analogous to error query
+    // It is an error as query is of form /oic/res?if=
+    if (0 == strlen(interfaceFilter))
+    {
+        return false;
+    }
+
     for (OCResourceInterface *ifPtr = resource->rsrcInterface; ifPtr; ifPtr = ifPtr->next)
     {
         if (0 == strcmp(ifPtr->name, interfaceFilter) ||
@@ -1282,7 +1298,7 @@ static OCStackResult discoveryPayloadCreateAndAddDeviceId(OCPayload **payload)
 {
     if (*payload)
     {
-        OIC_LOG_V(DEBUG, TAG, "Payload is already allocated");
+        OIC_LOG(DEBUG, TAG, "Payload is already allocated");
         return OC_STACK_OK;
     }
 
@@ -1317,10 +1333,13 @@ exit:
  */
 static OCStackResult addDiscoveryBaselineCommonProperties(OCDiscoveryPayload *discPayload)
 {
-    discPayload->uri = OICStrdup(OC_RSRVD_WELL_KNOWN_URI);
-    VERIFY_PARAM_NON_NULL(TAG, discPayload->uri, "Failed adding href to discovery payload.");
+    if (!discPayload)
+    {
+        OIC_LOG(ERROR, TAG, "Payload is not allocated");
+        return OC_STACK_INVALID_PARAM;
+    }
 
-    OCGetPropertyValue(PAYLOAD_TYPE_DEVICE, "deviceName", (void **)&discPayload->name);
+    OCGetPropertyValue(PAYLOAD_TYPE_DEVICE, OC_RSRVD_DEVICE_NAME, (void **)&discPayload->name);
 
     discPayload->type = (OCStringLL*)OICCalloc(1, sizeof(OCStringLL));
     VERIFY_PARAM_NON_NULL(TAG, discPayload->type, "Failed adding rt to discovery payload.");
@@ -1337,6 +1356,14 @@ exit:
     return OC_STACK_NO_MEMORY;
 }
 
+static bool isUnicast(OCServerRequest *request)
+{
+    bool isMulticast = request->devAddr.flags & OC_MULTICAST;
+    return (isMulticast == false &&
+           (request->devAddr.adapter != OC_ADAPTER_RFCOMM_BTEDR) &&
+           (request->devAddr.adapter != OC_ADAPTER_GATT_BTLE));
+}
+
 static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource* resource)
 {
     if (!request || !resource)
@@ -1378,15 +1405,6 @@ static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource
 #endif
             )
     {
-        if (request->method == OC_REST_PUT || request->method == OC_REST_POST ||
-            request->method == OC_REST_DELETE)
-        {
-            OIC_LOG_V(ERROR, TAG, "Resource : %s not permitted for method: %d",
-                request->resourceUrl, request->method);
-            discoveryResult = OC_STACK_UNAUTHORIZED_REQ;
-            goto exit;
-        }
-
         char *interfaceQuery = NULL;
         char *resourceTypeQuery = NULL;
 
@@ -1403,24 +1421,19 @@ static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource
         discoveryResult = getQueryParamsForFiltering (virtualUriInRequest, request->query,
                 &interfaceQuery, &resourceTypeQuery);
         VERIFY_SUCCESS(discoveryResult);
+
         if (!interfaceQuery && !resourceTypeQuery)
         {
             // If no query is sent, default interface is used i.e. oic.if.ll.
             interfaceQuery = OICStrdup(OC_RSRVD_INTERFACE_LL);
         }
 
-        bool baselineQuery = false;
-        if (interfaceQuery && 0 == strcmp(interfaceQuery, OC_RSRVD_INTERFACE_DEFAULT))
-        {
-            baselineQuery = true;
-        }
-
         discoveryResult = discoveryPayloadCreateAndAddDeviceId(&payload);
         VERIFY_PARAM_NON_NULL(TAG, payload, "Failed creating Discovery Payload.");
         VERIFY_SUCCESS(discoveryResult);
 
         OCDiscoveryPayload *discPayload = (OCDiscoveryPayload *)payload;
-        if (baselineQuery)
+        if (interfaceQuery && 0 == strcmp(interfaceQuery, OC_RSRVD_INTERFACE_DEFAULT))
         {
             discoveryResult = addDiscoveryBaselineCommonProperties(discPayload);
             VERIFY_SUCCESS(discoveryResult);
@@ -1431,23 +1444,13 @@ static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource
 #endif
         for (; resource && discoveryResult == OC_STACK_OK; resource = resource->next)
         {
-            discoveryResult = OC_STACK_NO_RESOURCE;
-#ifdef RD_SERVER
-            discoveryResult = findResourceAtRD(resource, interfaceQuery, resourceTypeQuery,
-                discPayload);
-#endif
-            if (OC_STACK_NO_RESOURCE == discoveryResult)
+            // This case will handle when no resource type and it is oic.if.ll.
+            // Do not assume check if the query is ll
+            if (!resourceTypeQuery &&
+                (interfaceQuery && 0 == strcmp(interfaceQuery, OC_RSRVD_INTERFACE_LL)))
             {
-                // This case will handle when no resource type and it is oic.if.ll.
-                if (!resourceTypeQuery && !baselineQuery && (resource->resourceProperties & prop))
-                {
-                    discoveryResult = BuildVirtualResourceResponse(resource,
-                                                                   discPayload,
-                                                                   &request->devAddr,
-                                                                   networkInfo,
-                                                                   infoSize);
-                }
-                else if (includeThisResourceInResponse(resource, interfaceQuery, resourceTypeQuery))
+                // Only include discoverable type
+                if (resource->resourceProperties & prop)
                 {
                     discoveryResult = BuildVirtualResourceResponse(resource,
                                                                    discPayload,
@@ -1455,10 +1458,18 @@ static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource
                                                                    networkInfo,
                                                                    infoSize);
                 }
-                else
-                {
-                    discoveryResult = OC_STACK_OK;
-                }
+            }
+            else if (includeThisResourceInResponse(resource, interfaceQuery, resourceTypeQuery))
+            {
+                discoveryResult = BuildVirtualResourceResponse(resource,
+                                                               discPayload,
+                                                               &request->devAddr,
+                                                               networkInfo,
+                                                               infoSize);
+            }
+            else
+            {
+                discoveryResult = OC_STACK_OK;
             }
         }
         if (discPayload->resources == NULL)
@@ -1548,25 +1559,28 @@ static OCStackResult HandleVirtualResource (OCServerRequest *request, OCResource
         OIC_LOG_PAYLOAD(DEBUG, payload);
         if(discoveryResult == OC_STACK_OK)
         {
+
             SendNonPersistantDiscoveryResponse(request, resource, payload, OC_EH_OK);
         }
-        else if(((request->devAddr.flags &  OC_MULTICAST) == false) &&
-            (request->devAddr.adapter != OC_ADAPTER_RFCOMM_BTEDR) &&
-            (request->devAddr.adapter != OC_ADAPTER_GATT_BTLE))
+        else // Error handling
         {
-            OIC_LOG_V(ERROR, TAG, "Sending a (%d) error to (%d) discovery request",
-                discoveryResult, virtualUriInRequest);
-            SendNonPersistantDiscoveryResponse(request, resource, NULL,
-                (discoveryResult == OC_STACK_NO_RESOURCE) ?
+            if (isUnicast(request))
+            {
+                OIC_LOG_V(ERROR, TAG, "Sending a (%d) error to (%d) discovery request",
+                    discoveryResult, virtualUriInRequest);
+                SendNonPersistantDiscoveryResponse(request, resource, NULL,
+                    (discoveryResult == OC_STACK_NO_RESOURCE) ?
                         OC_EH_RESOURCE_NOT_FOUND : OC_EH_ERROR);
-        }
-        else
-        {
-            // Ignoring the discovery request as per RFC 7252, Section #8.2
-            OIC_LOG(INFO, TAG, "Silently ignoring the request since no useful data to send.");
-            // the request should be removed.
-            // since it never remove and causes a big memory waste.
-            FindAndDeleteServerRequest(request);
+            }
+            else // Multicast
+            {
+                // Ignoring the discovery request as per RFC 7252, Section #8.2
+                OIC_LOG(INFO, TAG, "Silently ignoring the request since no useful data to send.");
+                // the request should be removed.
+                // since it never remove and causes a big memory waste.
+                FindAndDeleteServerRequest(request);
+            }
+            discoveryResult = OC_STACK_CONTINUE;
         }
     }
 
@@ -1585,6 +1599,7 @@ exit:
     {
         OICFree(dataModelVersions);
     }
+    // To ignore the message, OC_STACK_CONTINUE is sent
     return discoveryResult;
 }
 
index 1cdd4da..88bb0c7 100644 (file)
@@ -1350,7 +1350,7 @@ void OCHandleResponse(const CAEndpoint_t* endPoint, const CAResponseInfo_t* resp
                 OIC_LOG(ERROR, TAG, "Allocating memory for response failed");
                 return;
             }
-            
+
             response->devAddr.adapter = OC_DEFAULT_ADAPTER;
             response->sequenceNumber = MAX_SEQUENCE_NUMBER + 1;
             CopyEndpointToDevAddr(endPoint, &response->devAddr);
@@ -1460,16 +1460,22 @@ void OCHandleResponse(const CAEndpoint_t* endPoint, const CAResponseInfo_t* resp
                     OICFree(response);
                     return;
                 }
-
-                if(OC_STACK_OK != OCParsePayload(&response->payload,
+                // In case of error, still want application to receive the error message.
+                if (OCResultToSuccess(response->result))
+                {
+                    if (OC_STACK_OK != OCParsePayload(&response->payload,
                             type,
                             responseInfo->info.payload,
                             responseInfo->info.payloadSize))
+                    {
+                        OIC_LOG(ERROR, TAG, "Error converting payload");
+                        OCPayloadDestroy(response->payload);
+                        return;
+                    }
+                }
+                else
                 {
-                    OIC_LOG(ERROR, TAG, "Error converting payload");
-                    OCPayloadDestroy(response->payload);
-                    OICFree(response);
-                    return;
+                    response->resourceUri = OICStrdup(cbNode->requestUri);
                 }
             }
 
@@ -1536,16 +1542,14 @@ void OCHandleResponse(const CAEndpoint_t* endPoint, const CAResponseInfo_t* resp
                 if (OC_REST_DISCOVER == cbNode->method)
                 {
                     OCDiscoveryPayload *payload = (OCDiscoveryPayload*) response->payload;
-                    if (!payload)
+                    // Payload can be empty in case of error message.
+                    if (payload && payload->sid)
                     {
-                        OIC_LOG(INFO, TAG, "discovery payload is invalid");
-                        return;
+                        OICStrcpy(response->devAddr.remoteId, sizeof(response->devAddr.remoteId),
+                                  payload->sid);
+                        OIC_LOG_V(INFO, TAG, "Device ID of response : %s",
+                                  response->devAddr.remoteId);
                     }
-
-                    OICStrcpy(response->devAddr.remoteId, sizeof(response->devAddr.remoteId),
-                              payload->sid);
-                    OIC_LOG_V(INFO, TAG, "Device ID of response : %s",
-                              response->devAddr.remoteId);
                 }
 
                 OCStackApplicationResult appFeedback = cbNode->callBack(cbNode->context,
@@ -4628,6 +4632,7 @@ OCStackResult deleteResource(OCResource *resource)
 
             deleteResourceElements(temp);
             OICFree(temp);
+            temp = NULL;
             return OC_STACK_OK;
         }
         else
@@ -4647,37 +4652,54 @@ void deleteResourceElements(OCResource *resource)
         return;
     }
 
-    OICFree(resource->uri);
-    deleteResourceType(resource->rsrcType);
-    deleteResourceInterface(resource->rsrcInterface);
-    OCDeleteResourceAttributes(resource->rsrcAttributes);
+    if (resource->uri)
+    {
+        OICFree(resource->uri);
+    }
+    if (resource->rsrcType)
+    {
+        deleteResourceType(resource->rsrcType);
+    }
+    if (resource->rsrcInterface)
+    {
+        deleteResourceInterface(resource->rsrcInterface);
+    }
+    if (resource->rsrcChildResourcesHead)
+    {
+        OICFree(resource->rsrcChildResourcesHead);
+    }
+    if (resource->rsrcAttributes)
+    {
+        OCDeleteResourceAttributes(resource->rsrcAttributes);
+    }
 }
 
 void deleteResourceType(OCResourceType *resourceType)
 {
-    OCResourceType *pointer = resourceType;
     OCResourceType *next = NULL;
 
-    while (pointer)
+    for (OCResourceType *pointer = resourceType; pointer; pointer = next)
     {
         next = pointer->next;
-        OICFree(pointer->resourcetypename);
+        if (pointer->resourcetypename)
+        {
+            OICFree(pointer->resourcetypename);
+        }
         OICFree(pointer);
-        pointer = next;
     }
 }
 
 void deleteResourceInterface(OCResourceInterface *resourceInterface)
 {
-    OCResourceInterface *pointer = resourceInterface;
     OCResourceInterface *next = NULL;
-
-    while (pointer)
+    for (OCResourceInterface *pointer = resourceInterface; pointer; pointer = next)
     {
         next = pointer->next;
-        OICFree(pointer->name);
+        if (pointer->name)
+        {
+            OICFree(pointer->name);
+        }
         OICFree(pointer);
-        pointer = next;
     }
 }
 
@@ -4691,11 +4713,14 @@ void OCDeleteResourceAttributes(OCAttribute *rsrcAttributes)
         {
             OCFreeOCStringLL((OCStringLL *)pointer->attrValue);
         }
-        else
+        else if (pointer->attrValue)
         {
             OICFree(pointer->attrValue);
         }
-        OICFree(pointer->attrName);
+        if (pointer->attrName)
+        {
+            OICFree(pointer->attrName);
+        }
         OICFree(pointer);
     }
 }
@@ -5439,4 +5464,3 @@ OCStackResult OCGetDeviceOwnedState(bool *isOwned)
     }
     return ret;
 }
-
index 3b401c2..3d6f6a3 100644 (file)
@@ -27,6 +27,7 @@ extern "C"
     #include "logger.h"
     #include "oic_malloc.h"
     #include "oic_string.h"
+    #include "oic_time.h"
 }
 
 #include "gtest/gtest.h"
@@ -70,6 +71,7 @@ static char pinNumber;
 static OCDPDev_t peer;
 
 std::chrono::seconds const SHORT_TEST_TIMEOUT = std::chrono::seconds(5);
+std::chrono::seconds const LONG_TEST_TIMEOUT = std::chrono::seconds(450);
 
 //-----------------------------------------------------------------------------
 // Callback functions
@@ -176,6 +178,60 @@ uint8_t InitResourceIndex()
     return 0;
 #endif
 }
+
+class Callback
+{
+    public:
+        Callback(OCClientResponseHandler cb) : m_cb(cb), m_called(false)
+        {
+            m_cbData.cb = &Callback::handler;
+            m_cbData.cd = NULL;
+            m_cbData.context = this;
+        }
+        void Wait(long waitTime)
+        {
+            uint64_t startTime = OICGetCurrentTime(TIME_IN_MS);
+            while (!m_called)
+            {
+                uint64_t currTime = OICGetCurrentTime(TIME_IN_MS);
+                long elapsed = (long)((currTime - startTime) / MS_PER_SEC);
+                if (elapsed > waitTime)
+                {
+                    m_called = true;
+                }
+                OCProcess();
+            }
+        }
+        operator OCCallbackData *()
+        {
+            return &m_cbData;
+        }
+    private:
+        OCCallbackData m_cbData;
+        OCClientResponseHandler m_cb;
+        bool m_called;
+        static OCStackApplicationResult handler(void *ctx, OCDoHandle handle, OCClientResponse *clientResponse)
+        {
+            Callback *callback = (Callback *) ctx;
+            OCStackApplicationResult result = callback->m_cb(NULL, handle, clientResponse);
+            callback->m_called = true;
+            return result;
+        }
+};
+
+class OCDiscoverTests : public testing::Test
+{
+    protected:
+        virtual void SetUp()
+        {
+            EXPECT_EQ(OC_STACK_OK, OCInit("127.0.0.1", 5683, OC_CLIENT_SERVER));
+        }
+
+        virtual void TearDown()
+        {
+            OCStop();
+        }
+};
 //-----------------------------------------------------------------------------
 //  Tests
 //-----------------------------------------------------------------------------
@@ -2241,3 +2297,213 @@ TEST(StackEndpoints, OCGetSupportedEndpointTpsFlags)
 
     EXPECT_EQ(OC_STACK_OK, OCStop());
 }
+
+static OCStackApplicationResult DiscoverBaselineResource(void *ctx, OCDoHandle handle,
+    OCClientResponse *response)
+{
+    OC_UNUSED(ctx);
+    OC_UNUSED(handle);
+    EXPECT_EQ(OC_STACK_OK, response->result);
+    EXPECT_TRUE(NULL != response->payload);
+    if (NULL != response->payload)
+    {
+        EXPECT_EQ(PAYLOAD_TYPE_DISCOVERY, response->payload->type);
+
+        OCDiscoveryPayload *payload = (OCDiscoveryPayload *)response->payload;
+        EXPECT_TRUE(NULL != payload->sid);
+        EXPECT_STREQ("StackTest", payload->name);
+        EXPECT_STREQ(OC_RSRVD_RESOURCE_TYPE_RES, payload->type->value);
+        EXPECT_STREQ(OC_RSRVD_INTERFACE_LL, payload->iface->value);
+        EXPECT_STREQ(OC_RSRVD_INTERFACE_DEFAULT, payload->iface->next->value);
+
+        for (OCResourcePayload *resource = payload->resources; resource; resource = resource->next)
+        {
+            if (0 == strcmp("/a/light", resource->uri))
+            {
+                EXPECT_STREQ("/a/light", resource->uri);
+                EXPECT_STREQ("core.light", resource->types->value);
+                EXPECT_EQ(NULL, resource->types->next);
+                EXPECT_STREQ("oic.if.baseline", resource->interfaces->value);
+                EXPECT_EQ(NULL, resource->interfaces->next);
+                EXPECT_TRUE(resource->bitmap & OC_DISCOVERABLE);
+                EXPECT_FALSE(resource->secure);
+                EXPECT_EQ(0, resource->port);
+                EXPECT_EQ(NULL, resource->next);
+            }
+        }
+    }
+
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+static OCStackApplicationResult DiscoverLinkedListResource(void *ctx, OCDoHandle handle,
+    OCClientResponse *response)
+{
+    OC_UNUSED(ctx);
+    OC_UNUSED(handle);
+    EXPECT_EQ(OC_STACK_OK, response->result);
+    EXPECT_TRUE(NULL != response->payload);
+    if (NULL != response->payload)
+    {
+        EXPECT_EQ(PAYLOAD_TYPE_DISCOVERY, response->payload->type);
+
+        OCDiscoveryPayload *payload = (OCDiscoveryPayload *)response->payload;
+        EXPECT_NE((char *)NULL, payload->sid);
+        EXPECT_EQ(NULL, payload->name);
+        EXPECT_EQ(NULL, payload->type);
+        EXPECT_EQ(NULL, payload->iface);
+
+        for (OCResourcePayload *resource = payload->resources; resource; resource = resource->next)
+        {
+            if (0 == strcmp("/a/light", resource->uri))
+            {
+                EXPECT_STREQ("/a/light", resource->uri);
+                EXPECT_STREQ("core.light", resource->types->value);
+                EXPECT_EQ(NULL, resource->types->next);
+                EXPECT_STREQ("oic.if.baseline", resource->interfaces->value);
+                EXPECT_EQ(NULL, resource->interfaces->next);
+                EXPECT_TRUE(resource->bitmap & OC_DISCOVERABLE);
+                EXPECT_FALSE(resource->secure);
+                EXPECT_EQ(0, resource->port);
+                EXPECT_EQ(NULL, resource->next);
+            }
+        }
+    }
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+
+static OCStackApplicationResult DiscoverResourceTypeResponse(void *ctx, OCDoHandle handle,
+    OCClientResponse *response)
+{
+    OC_UNUSED(ctx);
+    OC_UNUSED(handle);
+    EXPECT_EQ(OC_STACK_OK, response->result);
+    EXPECT_TRUE(NULL != response->payload);
+    if (NULL != response->payload)
+    {
+        EXPECT_EQ(PAYLOAD_TYPE_DISCOVERY, response->payload->type);
+
+        OCDiscoveryPayload *payload = (OCDiscoveryPayload *)response->payload;
+        EXPECT_NE((char *)NULL, payload->sid);
+        EXPECT_EQ(NULL, payload->name);
+        EXPECT_EQ(NULL, payload->type);
+        EXPECT_EQ(NULL, payload->iface);
+        EXPECT_TRUE(NULL != payload->resources);
+
+        OCResourcePayload *resource = payload->resources;
+
+        if (0 == strcmp("/a/light", resource->uri))
+        {
+            EXPECT_STREQ("/a/light", resource->uri);
+            EXPECT_STREQ("core.light", resource->types->value);
+            EXPECT_EQ(NULL, resource->types->next);
+            EXPECT_STREQ("oic.if.baseline", resource->interfaces->value);
+            EXPECT_EQ(NULL, resource->interfaces->next);
+            EXPECT_TRUE(resource->bitmap & OC_DISCOVERABLE);
+            EXPECT_FALSE(resource->secure);
+            EXPECT_EQ(0, resource->port);
+            EXPECT_EQ(NULL, resource->next);
+        }
+    }
+
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+static OCStackApplicationResult DiscoverUnicastErrorResponse(void *ctx, OCDoHandle handle,
+    OCClientResponse *response)
+{
+    OC_UNUSED(ctx);
+    OC_UNUSED(handle);
+    EXPECT_NE(OC_STACK_OK, response->result);
+    EXPECT_TRUE(NULL == response->payload);
+
+    return OC_STACK_DELETE_TRANSACTION;
+}
+
+TEST_F(OCDiscoverTests, DiscoverResourceWithValidQueries)
+{
+    itst::DeadmanTimer killSwitch(LONG_TEST_TIMEOUT);
+
+    OCResourceHandle handles;
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles, "core.light", "oic.if.baseline", "/a/light",
+        entityHandler, NULL, OC_DISCOVERABLE));
+    OCSetPropertyValue(PAYLOAD_TYPE_DEVICE, OC_RSRVD_DEVICE_NAME, "StackTest");
+
+    Callback discoverBaselineCB(&DiscoverBaselineResource);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?if=oic.if.baseline", NULL,
+        0, CT_DEFAULT, OC_HIGH_QOS, discoverBaselineCB, NULL, 0));
+    discoverBaselineCB.Wait(100);
+
+    Callback discoverDefaultCB(&DiscoverLinkedListResource);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res", NULL, 0, CT_DEFAULT,
+        OC_HIGH_QOS, discoverDefaultCB, NULL, 0));
+    discoverDefaultCB.Wait(100);
+
+    Callback discoverLinkedListCB(&DiscoverLinkedListResource);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?if=oic.if.ll", NULL, 0,
+        CT_DEFAULT, OC_HIGH_QOS, discoverLinkedListCB, NULL, 0));
+    discoverLinkedListCB.Wait(100);
+
+    Callback discoverRTCB(&DiscoverResourceTypeResponse);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?rt=core.light", NULL, 0,
+        CT_DEFAULT, OC_HIGH_QOS, discoverRTCB, NULL, 0));
+    discoverRTCB.Wait(100);
+}
+
+TEST_F(OCDiscoverTests, DiscoverResourceWithInvalidQueries)
+{
+    itst::DeadmanTimer killSwitch(LONG_TEST_TIMEOUT);
+
+    OCResourceHandle handles;
+    EXPECT_EQ(OC_STACK_OK, OCCreateResource(&handles, "core.light", "oic.if.baseline", "/a/light",
+        entityHandler, NULL, OC_DISCOVERABLE));
+    OCSetPropertyValue(PAYLOAD_TYPE_DEVICE, "deviceName", "StackTest");
+
+    Callback discoverRTInvalidCB(&DiscoverUnicastErrorResponse);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?rt=invalid", NULL, 0,
+    CT_DEFAULT, OC_HIGH_QOS, discoverRTInvalidCB, NULL, 0));
+    discoverRTInvalidCB.Wait(10);
+
+    Callback discoverRTEmptyCB(&DiscoverUnicastErrorResponse);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?rt=", NULL, 0, CT_DEFAULT,
+    OC_HIGH_QOS, discoverRTEmptyCB, NULL, 0));
+    discoverRTEmptyCB.Wait(10);
+
+    Callback discoverIfInvalidCB(&DiscoverUnicastErrorResponse);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?if=invalid", NULL, 0,
+        CT_DEFAULT, OC_HIGH_QOS, discoverIfInvalidCB, NULL, 0));
+    discoverIfInvalidCB.Wait(10);
+
+    Callback discoverIfEmptyCB(&DiscoverUnicastErrorResponse);
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, "/oic/res?if=", NULL, 0, CT_DEFAULT,
+    OC_HIGH_QOS, discoverIfEmptyCB, NULL, 0));
+    discoverIfEmptyCB.Wait(10);
+
+    // Unicast
+    char targetUri[MAX_URI_LENGTH * 2] ={ 0, };
+
+    Callback discoverUnicastIfInvalidCB(&DiscoverUnicastErrorResponse);
+    snprintf(targetUri, MAX_URI_LENGTH * 2, "127.0.0.1/oic/res?if=invalid");
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, targetUri, NULL, 0,
+    CT_DEFAULT, OC_HIGH_QOS, discoverUnicastIfInvalidCB, NULL, 0));
+    discoverUnicastIfInvalidCB.Wait(10);
+
+    Callback discoverUnicastIfEmptyCB(&DiscoverUnicastErrorResponse);
+    snprintf(targetUri, MAX_URI_LENGTH * 2, "127.0.0.1/oic/res?if=");
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, targetUri, NULL, 0, CT_DEFAULT,
+    OC_HIGH_QOS, discoverUnicastIfEmptyCB, NULL, 0));
+    discoverUnicastIfEmptyCB.Wait(10);
+
+    Callback discoverUnicastRTInvalidCB(&DiscoverUnicastErrorResponse);
+    snprintf(targetUri, MAX_URI_LENGTH * 2, "127.0.0.1/oic/res?rt=invalid");
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, targetUri, NULL, 0,
+    CT_DEFAULT, OC_HIGH_QOS, discoverUnicastRTInvalidCB, NULL, 0));
+    discoverUnicastRTInvalidCB.Wait(10);
+
+    Callback discoverUnicastRTEmptyCB(&DiscoverUnicastErrorResponse);
+    snprintf(targetUri, MAX_URI_LENGTH * 2, "127.0.0.1/oic/res?rt=");
+    EXPECT_EQ(OC_STACK_OK, OCDoResource(NULL, OC_REST_DISCOVER, targetUri, NULL, 0, CT_DEFAULT,
+    OC_HIGH_QOS, discoverUnicastRTEmptyCB, NULL, 0));
+    discoverUnicastRTEmptyCB.Wait(10);
+}