From 579b11af6536a6dba780441c21ae72948bbe5964 Mon Sep 17 00:00:00 2001 From: Todd Malsbary Date: Tue, 6 Dec 2016 17:57:38 -0800 Subject: [PATCH] [IOT-1685] Make /oic/res observable. 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 Reviewed-on: https://gerrit.iotivity.org/gerrit/15633 Tested-by: jenkins-iotivity Reviewed-by: Habib Virji --- resource/csdk/include/octypes.h | 1 + resource/csdk/stack/include/internal/ocobserve.h | 15 +++ .../stack/include/internal/ocresourcehandler.h | 6 ++ resource/csdk/stack/include/ocstack.h | 22 ++++ resource/csdk/stack/include/payload_logging.h | 4 + resource/csdk/stack/src/ocobserve.c | 120 ++++++++++++++++----- resource/csdk/stack/src/ocpayload.c | 13 +++ resource/csdk/stack/src/ocpayloadconvert.c | 22 ++-- resource/csdk/stack/src/ocpayloadparse.c | 9 ++ resource/csdk/stack/src/ocresource.c | 21 +++- resource/csdk/stack/src/ocstack.c | 46 ++++++++ resource/csdk/stack/test/stacktests.cpp | 54 ++++++++++ 12 files changed, 299 insertions(+), 34 deletions(-) diff --git a/resource/csdk/include/octypes.h b/resource/csdk/include/octypes.h index 77b466b..71fb314 100755 --- a/resource/csdk/include/octypes.h +++ b/resource/csdk/include/octypes.h @@ -1514,6 +1514,7 @@ typedef struct OCEndpointPayload typedef struct OCResourcePayload { char* uri; + char* rel; OCStringLL* types; OCStringLL* interfaces; uint8_t bitmap; diff --git a/resource/csdk/stack/include/internal/ocobserve.h b/resource/csdk/stack/include/internal/ocobserve.h index f06ee04..7f05d2c 100644 --- a/resource/csdk/stack/include/internal/ocobserve.h +++ b/resource/csdk/stack/include/internal/ocobserve.h @@ -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 diff --git a/resource/csdk/stack/include/internal/ocresourcehandler.h b/resource/csdk/stack/include/internal/ocresourcehandler.h index 7515caa..12a5490 100644 --- a/resource/csdk/stack/include/internal/ocresourcehandler.h +++ b/resource/csdk/stack/include/internal/ocresourcehandler.h @@ -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. */ diff --git a/resource/csdk/stack/include/ocstack.h b/resource/csdk/stack/include/ocstack.h index 0591483..3d438e2 100644 --- a/resource/csdk/stack/include/ocstack.h +++ b/resource/csdk/stack/include/ocstack.h @@ -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. diff --git a/resource/csdk/stack/include/payload_logging.h b/resource/csdk/stack/include/payload_logging.h index e77baaf..0dd6d09 100644 --- a/resource/csdk/stack/include/payload_logging.h +++ b/resource/csdk/stack/include/payload_logging.h @@ -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) diff --git a/resource/csdk/stack/src/ocobserve.c b/resource/csdk/stack/src/ocobserve.c index f5ca966..3520953 100644 --- a/resource/csdk/stack/src/ocobserve.c +++ b/resource/csdk/stack/src/ocobserve.c @@ -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; +} diff --git a/resource/csdk/stack/src/ocpayload.c b/resource/csdk/stack/src/ocpayload.c index c52be01..b8d663e 100644 --- a/resource/csdk/stack/src/ocpayload.c +++ b/resource/csdk/stack/src/ocpayload.c @@ -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); diff --git a/resource/csdk/stack/src/ocpayloadconvert.c b/resource/csdk/stack/src/ocpayloadconvert.c index 26be51b..e360790 100755 --- a/resource/csdk/stack/src/ocpayloadconvert.c +++ b/resource/csdk/stack/src/ocpayloadconvert.c @@ -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) { diff --git a/resource/csdk/stack/src/ocpayloadparse.c b/resource/csdk/stack/src/ocpayloadparse.c index 4d0dbaa..e6738f5 100755 --- a/resource/csdk/stack/src/ocpayloadparse.c +++ b/resource/csdk/stack/src/ocpayloadparse.c @@ -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"); diff --git a/resource/csdk/stack/src/ocresource.c b/resource/csdk/stack/src/ocresource.c index ac38105..cd74b4c 100755 --- a/resource/csdk/stack/src/ocresource.c +++ b/resource/csdk/stack/src/ocresource.c @@ -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 diff --git a/resource/csdk/stack/src/ocstack.c b/resource/csdk/stack/src/ocstack.c index 73266c1..bf99983 100644 --- a/resource/csdk/stack/src/ocstack.c +++ b/resource/csdk/stack/src/ocstack.c @@ -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 diff --git a/resource/csdk/stack/test/stacktests.cpp b/resource/csdk/stack/test/stacktests.cpp index 3d6f6a3..a22bda9 100644 --- a/resource/csdk/stack/test/stacktests.cpp +++ b/resource/csdk/stack/test/stacktests.cpp @@ -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); -- 2.7.4