This commit adds the CBOR accept option to all requests.
When a request comes in with an accept option, the option is evaluated
against the supported encoding formats. If the format is undefined or
when CBOR is requested, CBOR will be used to encod the payload.
When the response does not carry a payload, the accept option is
ignored and the response will proceed irrespective whether the accept
format was supported or not. This allowsi for example a device that
performs a delet operation proceed first working out which format is
supported by the server.
Change-Id: I7ae3430d11f1481a91413088a959f105b216ffea
Signed-off-by: Stephane Lejeune <stlejeun@cisco.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/2492
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Hauke Mehrtens <hauke.mehrtens@lantiq.com>
Reviewed-by: Patrick Lankswert <patrick.lankswert@intel.com>
CA_BAD_OPT = 402, /**< Bad Option */
CA_FORBIDDEN_REQ = 403, /**< Forbidden Request */
CA_NOT_FOUND = 404, /**< Not found */
+ CA_NOT_ACCEPTABLE = 406, /**< Not Acceptable */
CA_REQUEST_ENTITY_INCOMPLETE = 408, /**< Request Entity Incomplete */
CA_REQUEST_ENTITY_TOO_LARGE = 413, /**< Request Entity Too Large */
CA_INTERNAL_SERVER_ERROR = 500, /**< Internal Server Error */
*/
typedef enum
{
- CA_FORMAT_UNDEFINED, /**< Undefined enoding format */
- CA_FORMAT_UNSUPPORTED, /**< Unsupported encoding format */
- CA_FORMAT_CBOR /**< CBOR encoding format */
+ CA_FORMAT_UNDEFINED = 0, /**< Undefined enoding format */
+ CA_FORMAT_TEXT_PLAIN,
+ CA_FORMAT_APPLICATION_LINK_FORMAT,
+ CA_FORMAT_APPLICATION_XML,
+ CA_FORMAT_APPLICATION_OCTET_STREAM,
+ CA_FORMAT_APPLICATION_RDF_XML,
+ CA_FORMAT_APPLICATION_EXI,
+ CA_FORMAT_APPLICATION_JSON,
+ CA_FORMAT_APPLICATION_CBOR,
+ CA_FORMAT_UNSUPPORTED
} CAPayloadFormat_t;
/**
uint8_t numOptions; /**< Number of Header options */
CAPayload_t payload; /**< payload of the request */
size_t payloadSize; /**< size in bytes of the payload */
- CAPayloadFormat_t payloadFormat; /**< format of the payload */
+ CAPayloadFormat_t payloadFormat; /**< encoding format of the request payload */
+ CAPayloadFormat_t acceptFormat; /**< accept format for the response payload */
CAURI_t resourceUri; /**< Resource URI information **/
CARemoteId_t identity; /**< endpoint identity */
} CAInfo_t;
case CA_BAD_OPT:
case CA_FORBIDDEN_REQ:
case CA_NOT_FOUND:
+ case CA_NOT_ACCEPTABLE:
case CA_REQUEST_ENTITY_INCOMPLETE:
case CA_REQUEST_ENTITY_TOO_LARGE:
case CA_INTERNAL_SERVER_ERROR:
clone->payloadSize = info->payloadSize;
}
clone->payloadFormat = info->payloadFormat;
+ clone->acceptFormat = info->acceptFormat;
if (info->resourceUri)
{
*/
CAResponseResult_t CAGetCodeFromPduBinaryData(const void *pdu, uint32_t size);
+/**
+ * convert format from coap to OC.
+ * @param[in] format coap format code.
+ * @return format.
+ */
+CAPayloadFormat_t CAConvertFormat(uint8_t format);
+
#ifdef __cplusplus
} /* extern "C" */
#endif
coap_list_t* node = NULL;
uint8_t buf[3] = {0};
switch (info->payloadFormat) {
- case CA_FORMAT_CBOR:
+ case CA_FORMAT_APPLICATION_CBOR:
node = CACreateNewOptionNode(
COAP_OPTION_CONTENT_FORMAT,
coap_encode_var_bytes(buf, (uint16_t)COAP_MEDIATYPE_APPLICATION_CBOR),
return CA_STATUS_INVALID_PARAM;
}
}
+ if (CA_FORMAT_UNDEFINED != info->acceptFormat)
+ {
+ coap_list_t* node = NULL;
+ uint8_t buf[3] = {0};
+ switch (info->acceptFormat) {
+ case CA_FORMAT_APPLICATION_CBOR:
+ node = CACreateNewOptionNode(
+ COAP_OPTION_ACCEPT,
+ coap_encode_var_bytes(buf, (uint16_t)COAP_MEDIATYPE_APPLICATION_CBOR),
+ (char *)buf);
+ break;
+ default:
+ OIC_LOG_V(ERROR, TAG, "format option:[%d] not supported", info->acceptFormat);
+ }
+ if (!node)
+ {
+ OIC_LOG(ERROR, TAG, "format option not created");
+ return CA_STATUS_INVALID_PARAM;
+ }
+ int ret = coap_insert(optlist, node, CAOrderOpts);
+ if (ret <= 0)
+ {
+ coap_delete(node);
+ OIC_LOG(ERROR, TAG, "format option not inserted in header");
+ return CA_STATUS_INVALID_PARAM;
+ }
+ }
OIC_LOG(DEBUG, TAG, "OUT");
return CA_STATUS_OK;
if (COAP_OPTION_URI_PATH != opt_iter.type && COAP_OPTION_URI_QUERY != opt_iter.type
&& COAP_OPTION_BLOCK1 != opt_iter.type && COAP_OPTION_BLOCK2 != opt_iter.type
&& COAP_OPTION_SIZE1 != opt_iter.type && COAP_OPTION_SIZE2 != opt_iter.type
- && COAP_OPTION_CONTENT_FORMAT != opt_iter.type)
+ && COAP_OPTION_CONTENT_FORMAT != opt_iter.type
+ && COAP_OPTION_ACCEPT != opt_iter.type)
{
count++;
}
// set message id
outInfo->messageId = pdu->hdr->id;
outInfo->payloadFormat = CA_FORMAT_UNDEFINED;
+ outInfo->acceptFormat = CA_FORMAT_UNDEFINED;
if (count > 0)
{
}
else if (COAP_OPTION_CONTENT_FORMAT == opt_iter.type)
{
- if (1 == COAP_OPT_LENGTH(option) && COAP_MEDIATYPE_APPLICATION_CBOR == buf[0])
+ if (1 == COAP_OPT_LENGTH(option))
{
- outInfo->payloadFormat = CA_FORMAT_CBOR;
+ outInfo->payloadFormat = CAConvertFormat((uint8_t)buf[0]);
}
else
{
outInfo->payloadFormat = CA_FORMAT_UNSUPPORTED;
+ OIC_LOG_V(DEBUG, TAG, "option[%d] has an unsupported format [%d]",
+ opt_iter.type, (uint8_t)buf[0]);
}
- OIC_LOG_V(DEBUG, TAG, "option[%d] has format [%d]", opt_iter.type, (uint8_t)buf[0]);
+ }
+ else if (COAP_OPTION_ACCEPT == opt_iter.type)
+ {
+ if (1 == COAP_OPT_LENGTH(option))
+ {
+ outInfo->acceptFormat = CAConvertFormat((uint8_t)buf[0]);
+ }
+ else
+ {
+ outInfo->acceptFormat = CA_FORMAT_UNSUPPORTED;
+ }
+ OIC_LOG_V(DEBUG, TAG, "option[%d] has an unsupported format [%d]",
+ opt_iter.type, (uint8_t)buf[0]);
}
else
{
return (CAResponseResult_t) CA_RESPONSE_CODE(hdr->code);
}
+
+CAPayloadFormat_t CAConvertFormat(uint8_t format)
+{
+ switch (format)
+ {
+ case COAP_MEDIATYPE_TEXT_PLAIN:
+ return CA_FORMAT_TEXT_PLAIN;
+ case COAP_MEDIATYPE_APPLICATION_LINK_FORMAT:
+ return CA_FORMAT_APPLICATION_LINK_FORMAT;
+ case COAP_MEDIATYPE_APPLICATION_XML:
+ return CA_FORMAT_APPLICATION_XML;
+ case COAP_MEDIATYPE_APPLICATION_OCTET_STREAM:
+ return CA_FORMAT_APPLICATION_OCTET_STREAM;
+ case COAP_MEDIATYPE_APPLICATION_RDF_XML:
+ return CA_FORMAT_APPLICATION_RDF_XML;
+ case COAP_MEDIATYPE_APPLICATION_EXI:
+ return CA_FORMAT_APPLICATION_EXI;
+ case COAP_MEDIATYPE_APPLICATION_JSON:
+ return CA_FORMAT_APPLICATION_JSON;
+ case COAP_MEDIATYPE_APPLICATION_CBOR:
+ return CA_FORMAT_APPLICATION_CBOR;
+ default:
+ return CA_FORMAT_UNSUPPORTED;
+ }
+}
/** next node in this list.*/
struct ResourceObserver *next;
+
+ /** requested payload encoding format. */
+ OCPayloadFormat acceptFormat;
+
} ResourceObserver;
#ifdef WITH_PRESENCE
uint8_t tokenLength,
OCResource *resHandle,
OCQualityOfService qos,
+ OCPayloadFormat acceptFormat,
const OCDevAddr *devAddr);
/**
/** The REST method retrieved from received request PDU.*/
OCMethod method;
+ /** Accept format retrieved from the received request PDU. */
+ OCPayloadFormat acceptFormat;
+
/** resourceUrl will be filled in occoap using the path options in received request PDU.*/
char resourceUrl[MAX_URI_LENGTH];
/** payload is retrieved from the payload of the received request PDU.*/
uint8_t payload[1];
+ // WARNING: Do NOT add attributes after payload as they get overwritten
+ // when payload content gets copied over!
+
} OCServerRequest;
/**
* @param tokenLength Request token length.
* @param resourceUrl URL of resource.
* @param reqTotalSize Total size of the request.
+ * @param acceptFormat The format requested for the payload encoding.
* @param devAddr Device Address.
*
* @return
uint8_t * payload, CAToken_t requestToken,
uint8_t tokenLength,
char * resourceUrl, size_t reqTotalSize,
+ OCPayloadFormat acceptFormat,
const OCDevAddr *devAddr);
/**
/** The REST method retrieved from received request PDU.*/
OCMethod method;
+ /** the requested payload format. */
+ OCPayloadFormat acceptFormat;
+
/** resourceUrl will be filled in occoap using the path options in received request PDU.*/
char resourceUrl[MAX_URI_LENGTH];
} OCMethod;
/**
+ * Formats for payload encoding.
+ */
+typedef enum
+{
+ OC_FORMAT_CBOR,
+ OC_FORMAT_UNDEFINED,
+ OC_FORMAT_UNSUPPORTED,
+} OCPayloadFormat;
+
+/**
* Host Mode of Operation.
*/
typedef enum
/** the payload from the request PDU.*/
OCPayload *payload;
-
} OCEntityHandlerRequest;
0, resPtr->sequenceNum, qos, resourceObserver->query,
NULL, NULL,
resourceObserver->token, resourceObserver->tokenLength,
- resourceObserver->resUri, 0,
+ resourceObserver->resUri, 0, resourceObserver->acceptFormat,
&resourceObserver->devAddr);
if(request)
0, resPtr->sequenceNum, qos, resourceObserver->query,
NULL, NULL,
resourceObserver->token, resourceObserver->tokenLength,
- resourceObserver->resUri, 0,
+ resourceObserver->resUri, 0, resourceObserver->acceptFormat,
&resourceObserver->devAddr);
if(result == OC_STACK_OK)
result = AddServerRequest(&request, 0, 0, 1, OC_REST_GET,
0, resource->sequenceNum, qos, observer->query,
NULL, NULL, observer->token, observer->tokenLength,
- observer->resUri, 0,
+ observer->resUri, 0, observer->acceptFormat,
&observer->devAddr);
if(request)
uint8_t tokenLength,
OCResource *resHandle,
OCQualityOfService qos,
+ OCPayloadFormat acceptFormat,
const OCDevAddr *devAddr)
{
// Check if resource exists and is observable.
VERIFY_NON_NULL (obsNode->resUri);
obsNode->qos = qos;
+ obsNode->acceptFormat = acceptFormat;
if(query)
{
obsNode->query = OICStrdup(query);
result = AddObserver ((const char*)(request->resourceUrl),
(const char *)(request->query),
ehRequest.obsInfo.obsId, request->requestToken, request->tokenLength,
- resource, request->qos,
+ resource, request->qos, request->acceptFormat,
&request->devAddr);
if(result == OC_STACK_OK)
}
}
+/**
+ * Ensure no accept header option is included when sending responses
+ *
+ * @param object CA remote endpoint.
+ * @param requestInfo CA request info.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+static OCStackResult OCSendResponse(const CAEndpoint_t *object, CAResponseInfo_t *responseInfo)
+{
+ // Do not include the accept header option
+ responseInfo->info.acceptFormat = CA_FORMAT_UNDEFINED;
+ CAResult_t result = CASendResponse(object, responseInfo);
+ if(CA_STATUS_OK != result)
+ {
+ OC_LOG_V(ERROR, TAG, "CASendResponse failed with CA error %u", result);
+ return CAResultToOCResult(result);
+ }
+ return OC_STACK_OK;
+}
+
//-------------------------------------------------------------------------------------------------
// Internal APIs
//-------------------------------------------------------------------------------------------------
OCQualityOfService qos, char * query,
OCHeaderOption * rcvdVendorSpecificHeaderOptions,
uint8_t * payload, CAToken_t requestToken, uint8_t tokenLength,
- char * resourceUrl, size_t reqTotalSize, const OCDevAddr *devAddr)
+ char * resourceUrl, size_t reqTotalSize, OCPayloadFormat acceptFormat,
+ const OCDevAddr *devAddr)
{
OCServerRequest * serverRequest = NULL;
serverRequest->observationOption = observationOption;
serverRequest->observeResult = OC_STACK_ERROR;
serverRequest->qos = qos;
+ serverRequest->acceptFormat = acceptFormat;
serverRequest->ehResponseHandler = HandleSingleResponse;
serverRequest->numResponses = 1;
responseInfo.info.options = NULL;
}
+ responseInfo.isMulticast = false;
+ responseInfo.info.payload = NULL;
+ responseInfo.info.payloadSize = 0;
+ responseInfo.info.payloadFormat = CA_FORMAT_UNDEFINED;
+
// Put the JSON prefix and suffix around the payload
if(ehResponse->payload)
{
responseInfo.isMulticast = false;
}
- OCStackResult result;
- if((result = OCConvertPayload(ehResponse->payload, &responseInfo.info.payload,
- &responseInfo.info.payloadSize))
- != OC_STACK_OK)
+ switch(serverRequest->acceptFormat)
{
- OC_LOG(ERROR, TAG, "Error converting payload");
- OICFree(responseInfo.info.options);
- return result;
+ case OC_FORMAT_UNDEFINED:
+ // No preference set by the client, so default to CBOR then
+ case OC_FORMAT_CBOR:
+ if((result = OCConvertPayload(ehResponse->payload, &responseInfo.info.payload,
+ &responseInfo.info.payloadSize))
+ != OC_STACK_OK)
+ {
+ OC_LOG(ERROR, TAG, "Error converting payload");
+ OICFree(responseInfo.info.options);
+ return result;
+ }
+ responseInfo.info.payloadFormat = CA_FORMAT_APPLICATION_CBOR;
+ break;
+ default:
+ responseInfo.result = CA_NOT_ACCEPTABLE;
}
- /** @todo FIXME: this should really be set according to directives from OCConverPayload. */
- responseInfo.info.payloadFormat = CA_FORMAT_CBOR;
- }
- else
- {
- responseInfo.isMulticast = false;
- responseInfo.info.payload = NULL;
- responseInfo.info.payloadSize = 0;
- responseInfo.info.payloadFormat = CA_FORMAT_UNDEFINED;
}
#ifdef WITH_PRESENCE
int size = sizeof(CAConnTypes)/ sizeof(CATransportAdapter_t);
CATransportAdapter_t adapter = responseEndpoint.adapter;
- CAResult_t caResult = CA_STATUS_FAILED;
- result = OC_STACK_OK;
-
// Default adapter, try to send response out on all adapters.
if (adapter == CA_DEFAULT_ADAPTER)
{
);
}
+ result = OC_STACK_OK;
+ OCStackResult tempResult = OC_STACK_OK;
+
for(int i = 0; i < size; i++ )
{
responseEndpoint.adapter = (CATransportAdapter_t)(adapter & CAConnTypes[i]);
if(responseEndpoint.adapter)
{
- //The result is set to OC_STACK_OK only if CASendResponse succeeds in sending the
+ //The result is set to OC_STACK_OK only if OCSendResponse succeeds in sending the
//response on all the n/w interfaces else it is set to OC_STACK_ERROR
- caResult = CASendResponse(&responseEndpoint, &responseInfo);
- if(caResult != CA_STATUS_OK)
- {
- OC_LOG_V(ERROR, TAG, "CASendResponse failed with CA error %u", caResult);
- result = CAResultToOCResult(caResult);
- }
+ tempResult = OCSendResponse(&responseEndpoint, &responseInfo);
+ }
+ if(OC_STACK_OK != tempResult)
+ {
+ result = tempResult;
}
}
#else
- OC_LOG(INFO, TAG, "Calling CASendResponse with:");
+ OC_LOG(INFO, TAG, "Calling OCSendResponse with:");
OC_LOG_V(INFO, TAG, "\tEndpoint address: %s", responseEndpoint.addr);
OC_LOG_V(INFO, TAG, "\tEndpoint adapter: %s", responseEndpoint.adapter);
OC_LOG_V(INFO, TAG, "\tResponse result : %s", responseInfo.result);
OC_LOG_V(INFO, TAG, "\tResponse for uri: %s", responseInfo.info.resourceUri);
- CAResult_t caResult = CASendResponse(&responseEndpoint, &responseInfo);
- if(caResult != CA_STATUS_OK)
- {
- OC_LOG(ERROR, TAG, "CASendResponse failed");
- result = CAResultToOCResult(caResult);
- }
- else
- {
- result = OC_STACK_OK;
- }
+ result = OCSendResponse(&responseEndpoint, &responseInfo);
#endif
OICFree(responseInfo.info.payload);
*/
static OCStackResult ResetPresenceTTL(ClientCB *cbNode, uint32_t maxAgeSeconds);
+/**
+ * Ensure the accept header option is set appropriatly before sending the requests.
+ *
+ * @param object CA remote endpoint.
+ * @param requestInfo CA request info.
+ *
+ * @return ::OC_STACK_OK on success, some other value upon failure.
+ */
+static OCStackResult OCSendRequest(const CAEndpoint_t *object, CARequestInfo_t *requestInfo);
+
//-----------------------------------------------------------------------------
// Internal functions
//-----------------------------------------------------------------------------
((cr->devAddr.adapter << CT_ADAPTER_SHIFT) | (cr->devAddr.flags & CT_MASK_FLAGS));
}
+static OCStackResult OCSendRequest(const CAEndpoint_t *object, CARequestInfo_t *requestInfo)
+{
+ // OC stack prefer CBOR encoded payloads.
+ requestInfo->info.acceptFormat = CA_FORMAT_APPLICATION_CBOR;
+ CAResult_t result = CASendRequest(object, requestInfo);
+ if(CA_STATUS_OK != result)
+ {
+ OC_LOG_V(ERROR, TAG, "CASendRequest failed with CA error %u", result);
+ return CAResultToOCResult(result);
+ }
+ return OC_STACK_OK;
+}
//-----------------------------------------------------------------------------
// Internal API function
//-----------------------------------------------------------------------------
respInfo.info.tokenLength = tokenLength;
respInfo.info.type = type;
respInfo.info.resourceUri = OICStrdup (resourceUri);
+ respInfo.info.acceptFormat = CA_FORMAT_UNDEFINED;
CAResult_t caResult = CASendResponse(endPoint, &respInfo);
}
memcpy(serverRequest.requestToken, requestInfo->info.token, requestInfo->info.tokenLength);
+ switch (requestInfo->info.acceptFormat)
+ {
+ case CA_FORMAT_APPLICATION_CBOR:
+ serverRequest.acceptFormat = OC_FORMAT_CBOR;
+ break;
+ case CA_FORMAT_UNDEFINED:
+ serverRequest.acceptFormat = OC_FORMAT_UNDEFINED;
+ break;
+ default:
+ serverRequest.acceptFormat = OC_FORMAT_UNSUPPORTED;
+ }
+
if (requestInfo->info.type == CA_MSG_CONFIRM)
{
serverRequest.qos = OC_HIGH_QOS;
{
OC_LOG(INFO, TAG, "This is a new Server Request");
result = AddServerRequest(&request, protocolRequest->coapID,
- protocolRequest->delayedResNeeded, 0,
- protocolRequest->method, protocolRequest->numRcvdVendorSpecificHeaderOptions,
+ protocolRequest->delayedResNeeded, 0, protocolRequest->method,
+ protocolRequest->numRcvdVendorSpecificHeaderOptions,
protocolRequest->observationOption, protocolRequest->qos,
protocolRequest->query, protocolRequest->rcvdVendorSpecificHeaderOptions,
protocolRequest->payload, protocolRequest->requestToken,
- protocolRequest->tokenLength,
- protocolRequest->resourceUrl, protocolRequest->reqTotalSize,
+ protocolRequest->tokenLength, protocolRequest->resourceUrl,
+ protocolRequest->reqTotalSize, protocolRequest->acceptFormat,
&protocolRequest->devAddr);
if (OC_STACK_OK != result)
{
OC_LOG(ERROR, TAG, "Failed to create CBOR Payload");
goto exit;
}
- requestInfo.info.payloadFormat = CA_FORMAT_CBOR;
+ requestInfo.info.payloadFormat = CA_FORMAT_APPLICATION_CBOR;
}
else
{
resourceType = NULL; // Client CB list entry now owns it
// send request
- caResult = CASendRequest(&endpoint, &requestInfo);
- if (caResult != CA_STATUS_OK)
+ result = OCSendRequest(&endpoint, &requestInfo);
+ if (OC_STACK_OK != result)
{
- OC_LOG(ERROR, TAG, "CASendRequest");
- result = OC_STACK_COMM_ERROR;
goto exit;
}
*/
OCStackResult ret = OC_STACK_OK;
CAEndpoint_t endpoint = {.adapter = CA_DEFAULT_ADAPTER};
- CAResult_t caResult;
CAInfo_t requestData = {.type = CA_MSG_CONFIRM};
CARequestInfo_t requestInfo = {.method = CA_GET};
CopyDevAddrToEndpoint(clientCB->devAddr, &endpoint);
// send request
- caResult = CASendRequest(&endpoint, &requestInfo);
- if (caResult != CA_STATUS_OK)
- {
- OC_LOG(ERROR, TAG, "CASendRequest error");
- ret = OC_STACK_ERROR;
- }
- ret = CAResultToOCResult (caResult);
+ ret = OCSendRequest(&endpoint, &requestInfo);
break;
#ifdef WITH_PRESENCE
continue;
}
- CAResult_t caResult = CA_STATUS_OK;
CAEndpoint_t endpoint = {.adapter = CA_DEFAULT_ADAPTER};
CAInfo_t requestData = {.type = CA_MSG_CONFIRM};
CARequestInfo_t requestInfo = {.method = CA_GET};
requestInfo.method = CA_GET;
requestInfo.info = requestData;
- caResult = CASendRequest(&endpoint, &requestInfo);
-
- if (caResult != CA_STATUS_OK)
+ result = OCSendRequest(&endpoint, &requestInfo);
+ if (OC_STACK_OK != result)
{
- OC_LOG(ERROR, TAG, "CASendRequest error");
goto exit;
}
}
AddObserver(OC_RSRVD_PRESENCE_URI, NULL, 0, caToken, tokenLength,
- (OCResource *)presenceResource.handle, OC_LOW_QOS, &devAddr);
+ (OCResource *)presenceResource.handle, OC_LOW_QOS, OC_FORMAT_UNDEFINED, &devAddr);
CADestroyToken(caToken);
}
return OC_STACK_ERROR;
}
}
+