diff --git a/library.properties b/library.properties index e11f466..720c0f0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=AzureIoTHub -version=1.0.44 +version=1.0.45 author=Microsoft maintainer=Microsoft sentence=Azure IoT library for Arduino. For the Arduino MKR1000 or Zero and WiFi Shield 101, Adafruit Huzzah and Feather M0, or SparkFun Thing. diff --git a/src/AzureIoTHub.h b/src/AzureIoTHub.h index afabbe0..ad0024d 100644 --- a/src/AzureIoTHub.h +++ b/src/AzureIoTHub.h @@ -14,5 +14,5 @@ #include "sdk/iothubtransportmqtt.h" #include "sdk/iothub_client_options.h" -#define AzureIoTHubVersion "1.0.44" +#define AzureIoTHubVersion "1.0.45" #endif diff --git a/src/sdk/agenttypesystem.c b/src/sdk/agenttypesystem.c index 75f385d..6852134 100644 --- a/src/sdk/agenttypesystem.c +++ b/src/sdk/agenttypesystem.c @@ -3022,7 +3022,7 @@ static int sscanfd(const char *src, int* dst) } else { - (*dst) = temp; + (*dst) = (int)temp; result = 1; } return result; @@ -3074,7 +3074,7 @@ static int sscanfu(const char* src, unsigned int* dst) else { result = 1; - (*dst) = temp; + (*dst) = (unsigned int)temp; } return result; } @@ -3974,40 +3974,34 @@ result = AGENT_DATA_TYPES_OK; // extern AGENT_DATA_TYPES_RESULT AgentDataType_GetComplexTypeField(AGENT_DATA_TYPE* agentData, size_t index, COMPLEX_TYPE_FIELD_TYPE* complexField); COMPLEX_TYPE_FIELD_TYPE* AgentDataType_GetComplexTypeField(AGENT_DATA_TYPE* agentData, size_t index) { - AGENT_DATA_TYPES_RESULT result; COMPLEX_TYPE_FIELD_TYPE* complexField = NULL; if (agentData == NULL) { - result = AGENT_DATA_TYPES_INVALID_ARG; - LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_INVALID_ARG)); } else { if (agentData->type != EDM_COMPLEX_TYPE_TYPE) { - result = AGENT_DATA_TYPES_INVALID_ARG; - LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_INVALID_ARG)); } else { if (index >= agentData->value.edmComplexType.nMembers) { - result = AGENT_DATA_TYPES_INVALID_ARG; - LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_INVALID_ARG)); } else { complexField = (COMPLEX_TYPE_FIELD_TYPE*)malloc(sizeof(COMPLEX_TYPE_FIELD_TYPE)); if (complexField == NULL) { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + LogError("(result = %s)", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_ERROR)); } else { *complexField = agentData->value.edmComplexType.fields[index]; - result = AGENT_DATA_TYPES_OK; } } } diff --git a/src/sdk/blob.c b/src/sdk/blob.c index 33e2987..baac3c1 100644 --- a/src/sdk/blob.c +++ b/src/sdk/blob.c @@ -124,7 +124,7 @@ BLOB_RESULT Blob_UploadBlock( return result; } -BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse, const char* certificates, HTTP_PROXY_OPTIONS *proxyOptions) +BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse, const char* certificates, HTTP_PROXY_OPTIONS *proxyOptions) { BLOB_RESULT result; /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ @@ -135,10 +135,10 @@ BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIEN } else { - /*Codes_SRS_BLOB_02_002: [ If getDataCallback is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ - if (getDataCallback == NULL) + /*Codes_SRS_BLOB_02_002: [ If getDataCallbackEx is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ + if (getDataCallbackEx == NULL) { - LogError("IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback = %p is invalid", getDataCallback); + LogError("IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx is NULL"); result = BLOB_INVALID_ARG; } /*the below define avoid a "condition always false" on some compilers*/ @@ -216,92 +216,84 @@ BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIEN } else { - /*Codes_SRS_BLOB_02_021: [ For every block returned by `getDataCallback` the following operations shall happen: ]*/ + /*Codes_SRS_BLOB_02_021: [ For every block returned by `getDataCallbackEx` the following operations shall happen: ]*/ unsigned int blockID = 0; /* incremented for each new block */ - unsigned int isError = 0; /* set to 1 if a block upload fails or if getDataCallback returns incorrect blocks to upload */ - unsigned int uploadOneMoreBlock; /* set to 1 while getDataCallback returns correct blocks to upload */ - unsigned char const * source; /* data set by getDataCallback */ - size_t size; /* source size set by getDataCallback */ + unsigned int isError = 0; /* set to 1 if a block upload fails or if getDataCallbackEx returns incorrect blocks to upload */ + unsigned int uploadOneMoreBlock = 1; /* set to 1 while getDataCallbackEx returns correct blocks to upload */ + unsigned char const * source; /* data set by getDataCallbackEx */ + size_t size; /* source size set by getDataCallbackEx */ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT getDataReturnValue; - /* get first block */ - getDataCallback(FILE_UPLOAD_OK, &source, &size, context); - - /*Codes_SRS_BLOB_99_002: [ If the size of the block returned by `getDataCallback` is 0 or if the data is NULL, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop. ]*/ - uploadOneMoreBlock = (source != NULL && size > 0) ? 1 : 0; - - if (size > BLOCK_SIZE) + do { - /*Codes_SRS_BLOB_99_001: [ If the size of the block returned by `getDataCallback` is bigger than 4MB, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ - LogError("tried to upload block of size %u, max allowed size is %d", size, BLOCK_SIZE); - result = BLOB_INVALID_ARG; - isError = 1; - } - else - { - result = BLOB_OK; - } - - while(uploadOneMoreBlock && !isError) - { - /*Codes_SRS_BLOB_02_023: [ Blob_UploadMultipleBlocksFromSasUri shall create a BUFFER_HANDLE from source and size parameters. ]*/ - BUFFER_HANDLE requestContent = BUFFER_create(source, size); - if (requestContent == NULL) - { - /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ - LogError("unable to BUFFER_create"); - result = BLOB_ERROR; - isError = 1; - } - else + getDataReturnValue = getDataCallbackEx(FILE_UPLOAD_OK, &source, &size, context); + if (getDataReturnValue == IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT) { - result = Blob_UploadBlock( - httpApiExHandle, - relativePath, - requestContent, - blockID, - blockIDList, - httpStatus, - httpResponse); - - BUFFER_delete(requestContent); + /*Codes_SRS_BLOB_99_004: [ If `getDataCallbackEx` returns `IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_ABORT`, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop and return `BLOB_ABORTED`. ]*/ + LogInfo("Upload to blob has been aborted by the user"); + uploadOneMoreBlock = 0; + result = BLOB_ABORTED; } - - /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/ - if (result != BLOB_OK || *httpStatus >= 300) + else if (source == NULL || size == 0) { - isError = 1; + /*Codes_SRS_BLOB_99_002: [ If the size of the block returned by `getDataCallbackEx` is 0 or if the data is NULL, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop. ]*/ + uploadOneMoreBlock = 0; + result = BLOB_OK; } else { - // try to get next block - getDataCallback(FILE_UPLOAD_OK, &source, &size, context); - if (source == NULL || size == 0) + if (size > BLOCK_SIZE) + { + /*Codes_SRS_BLOB_99_001: [ If the size of the block returned by `getDataCallbackEx` is bigger than 4MB, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ + LogError("tried to upload block of size %u, max allowed size is %d", size, BLOCK_SIZE); + result = BLOB_INVALID_ARG; + isError = 1; + } + else if (blockID >= MAX_BLOCK_COUNT) { - /*Codes_SRS_BLOB_99_002: [ If the size of the block returned by `getDataCallback` is 0 or if the data is NULL, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop. ]*/ - uploadOneMoreBlock = 0; + /*Codes_SRS_BLOB_99_003: [ If `getDataCallbackEx` returns more than 50000 blocks, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ + LogError("unable to upload more than %u blocks in one blob", MAX_BLOCK_COUNT); + result = BLOB_INVALID_ARG; + isError = 1; } else { - blockID++; - if (size > BLOCK_SIZE) + /*Codes_SRS_BLOB_02_023: [ Blob_UploadMultipleBlocksFromSasUri shall create a BUFFER_HANDLE from source and size parameters. ]*/ + BUFFER_HANDLE requestContent = BUFFER_create(source, size); + if (requestContent == NULL) { - /*Codes_SRS_BLOB_99_001: [ If the size of the block returned by `getDataCallback` is bigger than 4MB, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ - LogError("tried to upload block of size %u, max allowed size is %d", size, BLOCK_SIZE); - result = BLOB_INVALID_ARG; + /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/ + LogError("unable to BUFFER_create"); + result = BLOB_ERROR; isError = 1; } - else if (blockID >= MAX_BLOCK_COUNT) + else + { + result = Blob_UploadBlock( + httpApiExHandle, + relativePath, + requestContent, + blockID, + blockIDList, + httpStatus, + httpResponse); + + BUFFER_delete(requestContent); + } + + /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/ + if (result != BLOB_OK || *httpStatus >= 300) { - /*Codes_SRS_BLOB_99_003: [ If `getDataCallback` returns more than 50000 blocks, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/ - LogError("unable to upload more than %u blocks in one blob", MAX_BLOCK_COUNT); - result = BLOB_INVALID_ARG; + LogError("unable to Blob_UploadBlock. Returned value=%d, httpStatus=%u", result, httpStatus); isError = 1; } } + blockID++; } } + while(uploadOneMoreBlock && !isError); - if (isError) + if (isError || result != BLOB_OK) { /*do nothing, it will be reported "as is"*/ } diff --git a/src/sdk/blob.h b/src/sdk/blob.h index dee01df..e0a8a23 100644 --- a/src/sdk/blob.h +++ b/src/sdk/blob.h @@ -41,7 +41,8 @@ extern "C" BLOB_ERROR, \ BLOB_NOT_IMPLEMENTED, \ BLOB_HTTP_ERROR, \ - BLOB_INVALID_ARG + BLOB_INVALID_ARG, \ + BLOB_ABORTED DEFINE_ENUM(BLOB_RESULT, BLOB_RESULT_VALUES) @@ -49,7 +50,7 @@ DEFINE_ENUM(BLOB_RESULT, BLOB_RESULT_VALUES) * @brief Synchronously uploads a byte array to blob storage * * @param SASURI The URI to use to upload data -* @param getDataCallback A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. +* @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. * @param context Any data provided by the user to serve as context on getDataCallback. * @param httpStatus A pointer to an out argument receiving the HTTP status (available only when the return value is BLOB_OK) * @param httpResponse A BUFFER_HANDLE that receives the HTTP response from the server (available only when the return value is BLOB_OK) @@ -58,7 +59,7 @@ DEFINE_ENUM(BLOB_RESULT, BLOB_RESULT_VALUES) * * @return A @c BLOB_RESULT. BLOB_OK means the blob has been uploaded successfully. Any other value indicates an error */ -MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadMultipleBlocksFromSasUri, const char*, SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse, const char*, certificates, HTTP_PROXY_OPTIONS*, proxyOptions) +MOCKABLE_FUNCTION(, BLOB_RESULT, Blob_UploadMultipleBlocksFromSasUri, const char*, SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context, unsigned int*, httpStatus, BUFFER_HANDLE, httpResponse, const char*, certificates, HTTP_PROXY_OPTIONS*, proxyOptions) /** * @brief Synchronously uploads a byte array as a new block to blob storage diff --git a/src/sdk/iothub_client.c b/src/sdk/iothub_client.c index 2b97dfd..a7cb032 100644 --- a/src/sdk/iothub_client.c +++ b/src/sdk/iothub_client.c @@ -54,23 +54,27 @@ typedef struct UPLOADTOBLOB_SAVED_DATA_TAG { unsigned char* source; size_t size; - char* destinationFileName; IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback; - void* context; - THREAD_HANDLE uploadingThreadHandle; - IOTHUB_CLIENT_HANDLE iotHubClientHandle; - LOCK_HANDLE lockGarbage; - int canBeGarbageCollected; /*flag indicating that the UPLOADTOBLOB_SAVED_DATA structure can be freed because the thread deadling with it finished*/ }UPLOADTOBLOB_SAVED_DATA; typedef struct UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA_TAG { - char* destinationFileName; IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback; - void* context; + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx; +}UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA; + +typedef struct UPLOADTOBLOB_THREAD_INFO_TAG +{ + char* destinationFileName; THREAD_HANDLE uploadingThreadHandle; + LOCK_HANDLE lockGarbage; + int canBeGarbageCollected; /*flag indicating that the UPLOADTOBLOB_SAVED_DATA structure can be freed because the thread deadling with it finished*/ IOTHUB_CLIENT_HANDLE iotHubClientHandle; -}UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA; + void* context; + UPLOADTOBLOB_SAVED_DATA uploadBlobSavedData; + UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA uploadBlobMultiblockSavedData; +}UPLOADTOBLOB_THREAD_INFO; + #endif #define USER_CALLBACK_TYPE_VALUES \ @@ -140,6 +144,14 @@ typedef struct IOTHUB_QUEUE_CONTEXT_TAG const size_t IoTHubClient_ThreadTerminationOffset = offsetof(IOTHUB_CLIENT_INSTANCE, StopThread); #ifndef DONT_USE_UPLOADTOBLOB +static void freeUploadToBlobThreadInfo(UPLOADTOBLOB_THREAD_INFO* threadInfo) +{ + Lock_Deinit(threadInfo->lockGarbage); + free(threadInfo->uploadBlobSavedData.source); + free(threadInfo->destinationFileName); + free(threadInfo); +} + /*this function is called from _Destroy and from ScheduleWork_Thread to join finished blobUpload threads and free that memory*/ static void garbageCollectorImpl(IOTHUB_CLIENT_INSTANCE* iotHubClientInstance) { @@ -148,37 +160,34 @@ static void garbageCollectorImpl(IOTHUB_CLIENT_INSTANCE* iotHubClientInstance) LIST_ITEM_HANDLE item = singlylinkedlist_get_head_item(iotHubClientInstance->savedDataToBeCleaned); while (item != NULL) { - const UPLOADTOBLOB_SAVED_DATA* savedData = (const UPLOADTOBLOB_SAVED_DATA*)singlylinkedlist_item_get_value(item); + UPLOADTOBLOB_THREAD_INFO* threadInfo = (UPLOADTOBLOB_THREAD_INFO*)singlylinkedlist_item_get_value(item); LIST_ITEM_HANDLE old_item = item; item = singlylinkedlist_get_next_item(item); - if (Lock(savedData->lockGarbage) != LOCK_OK) + if (Lock(threadInfo->lockGarbage) != LOCK_OK) { LogError("unable to Lock"); } else { - if (savedData->canBeGarbageCollected == 1) + if (threadInfo->canBeGarbageCollected == 1) { int notUsed; - if (ThreadAPI_Join(savedData->uploadingThreadHandle, ¬Used) != THREADAPI_OK) + if (ThreadAPI_Join(threadInfo->uploadingThreadHandle, ¬Used) != THREADAPI_OK) { LogError("unable to ThreadAPI_Join"); } (void)singlylinkedlist_remove(iotHubClientInstance->savedDataToBeCleaned, old_item); - free((void*)savedData->source); - free((void*)savedData->destinationFileName); - if (Unlock(savedData->lockGarbage) != LOCK_OK) + if (Unlock(threadInfo->lockGarbage) != LOCK_OK) { LogError("unable to unlock after locking"); } - (void)Lock_Deinit(savedData->lockGarbage); - free((void*)savedData); + freeUploadToBlobThreadInfo(threadInfo); } else { - if (Unlock(savedData->lockGarbage) != LOCK_OK) + if (Unlock(threadInfo->lockGarbage) != LOCK_OK) { LogError("unable to unlock after locking"); } @@ -1856,60 +1865,151 @@ IOTHUB_CLIENT_RESULT IoTHubClient_DeviceMethodResponse(IOTHUB_CLIENT_HANDLE iotH } #ifndef DONT_USE_UPLOADTOBLOB -static int uploadingThread(void *data) +static IOTHUB_CLIENT_RESULT startUploadToBlobWorkerThread(UPLOADTOBLOB_THREAD_INFO* threadInfo, THREAD_START_FUNC uploadThreadFunc) { - UPLOADTOBLOB_SAVED_DATA* savedData = (UPLOADTOBLOB_SAVED_DATA*)data; + IOTHUB_CLIENT_RESULT result; + + LIST_ITEM_HANDLE item; - if (Lock(savedData->iotHubClientHandle->LockHandle) == LOCK_OK) + if (Lock(threadInfo->iotHubClientHandle->LockHandle) != LOCK_OK) { - IOTHUB_CLIENT_FILE_UPLOAD_RESULT upload_result; - /*it so happens that IoTHubClient_LL_UploadToBlob is thread-safe because there's no saved state in the handle and there are no globals, so no need to protect it*/ - /*not having it protected means multiple simultaneous uploads can happen*/ - /*Codes_SRS_IOTHUBCLIENT_02_054: [ The thread shall call IoTHubClient_LL_UploadToBlob passing the information packed in the structure. ]*/ - if (IoTHubClient_LL_UploadToBlob(savedData->iotHubClientHandle->IoTHubClientLLHandle, savedData->destinationFileName, savedData->source, savedData->size) == IOTHUB_CLIENT_OK) + LogError("Lock failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + if ((item = singlylinkedlist_add(threadInfo->iotHubClientHandle->savedDataToBeCleaned, threadInfo)) == NULL) { - upload_result = FILE_UPLOAD_OK; + LogError("Adding item to list failed"); + result = IOTHUB_CLIENT_ERROR; } - else + else if (ThreadAPI_Create(&threadInfo->uploadingThreadHandle, uploadThreadFunc, threadInfo) != THREADAPI_OK) { - LogError("unable to IoTHubClient_LL_UploadToBlob"); - upload_result = FILE_UPLOAD_ERROR; + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to ThreadAPI_Create"); + // Remove the item from linked list here, while we're still under lock. Final garbage collector also does it under lock. + (void)singlylinkedlist_remove(threadInfo->iotHubClientHandle->savedDataToBeCleaned, item); + result = IOTHUB_CLIENT_ERROR; } - (void)Unlock(savedData->iotHubClientHandle->LockHandle); - - if (savedData->iotHubClientFileUploadCallback != NULL) + else { - /*Codes_SRS_IOTHUBCLIENT_02_055: [ If IoTHubClient_LL_UploadToBlob fails then the thread shall call iotHubClientFileUploadCallbackInternal passing as result FILE_UPLOAD_ERROR and as context the structure from SRS IOTHUBCLIENT 02 051. ]*/ - savedData->iotHubClientFileUploadCallback(upload_result, savedData->context); + result = IOTHUB_CLIENT_OK; } + (void)Unlock(threadInfo->iotHubClientHandle->LockHandle); } - else + + return result; +} + +static UPLOADTOBLOB_THREAD_INFO* allocateUploadToBlob(const char* destinationFileName, IOTHUB_CLIENT_HANDLE iotHubClientHandle, void* context) +{ + UPLOADTOBLOB_THREAD_INFO* threadInfo = (UPLOADTOBLOB_THREAD_INFO*)malloc(sizeof(UPLOADTOBLOB_THREAD_INFO)); + if (threadInfo == NULL) { - LogError("Lock failed"); + LogError("unable to allocate thread object"); } + else + { + memset(threadInfo, 0, sizeof(UPLOADTOBLOB_THREAD_INFO)); + threadInfo->iotHubClientHandle = iotHubClientHandle; + threadInfo->context = context; + if (mallocAndStrcpy_s(&threadInfo->destinationFileName, destinationFileName) != 0) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to mallocAndStrcpy_s"); + freeUploadToBlobThreadInfo(threadInfo); + threadInfo = NULL; + } + else if ((threadInfo->lockGarbage = Lock_Init()) == NULL) + { + LogError("unable to allocate a lock"); + freeUploadToBlobThreadInfo(threadInfo); + threadInfo = NULL; + } + } + + return threadInfo; +} + +static int markThreadReadyToBeGarbageCollected(UPLOADTOBLOB_THREAD_INFO* threadInfo) +{ /*Codes_SRS_IOTHUBCLIENT_02_071: [ The thread shall mark itself as disposable. ]*/ - if (Lock(savedData->lockGarbage) != LOCK_OK) + if (Lock(threadInfo->lockGarbage) != LOCK_OK) { LogError("unable to Lock - trying anyway"); - savedData->canBeGarbageCollected = 1; + threadInfo->canBeGarbageCollected = 1; } else { - savedData->canBeGarbageCollected = 1; + threadInfo->canBeGarbageCollected = 1; - if (Unlock(savedData->lockGarbage) != LOCK_OK) + if (Unlock(threadInfo->lockGarbage) != LOCK_OK) { LogError("unable to Unlock after locking"); } } ThreadAPI_Exit(0); - return 0; + return 0; +} + +static IOTHUB_CLIENT_RESULT initializeUploadToBlobData(UPLOADTOBLOB_THREAD_INFO* threadInfo, const unsigned char* source, size_t size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback) +{ + IOTHUB_CLIENT_RESULT result; + + threadInfo->uploadBlobSavedData.size = size; + threadInfo->uploadBlobSavedData.iotHubClientFileUploadCallback = iotHubClientFileUploadCallback; + + if (size != 0) + { + if ((threadInfo->uploadBlobSavedData.source = (unsigned char*)malloc(size)) == NULL) + { + LogError("Cannot allocate source field"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + memcpy(threadInfo->uploadBlobSavedData.source, source, size); + result = IOTHUB_CLIENT_OK; + } + } + else + { + result = IOTHUB_CLIENT_OK; + } + + return result; +} + + +static int uploadingThread(void *data) +{ + IOTHUB_CLIENT_FILE_UPLOAD_RESULT upload_result; + UPLOADTOBLOB_THREAD_INFO* threadInfo = (UPLOADTOBLOB_THREAD_INFO*)data; + + /*it so happens that IoTHubClient_LL_UploadToBlob is thread-safe because there's no saved state in the handle and there are no globals, so no need to protect it*/ + /*not having it protected means multiple simultaneous uploads can happen*/ + /*Codes_SRS_IOTHUBCLIENT_02_054: [ The thread shall call IoTHubClient_LL_UploadToBlob passing the information packed in the structure. ]*/ + if (IoTHubClient_LL_UploadToBlob(threadInfo->iotHubClientHandle->IoTHubClientLLHandle, threadInfo->destinationFileName, threadInfo->uploadBlobSavedData.source, threadInfo->uploadBlobSavedData.size) == IOTHUB_CLIENT_OK) + { + upload_result = FILE_UPLOAD_OK; + } + else + { + LogError("unable to IoTHubClient_LL_UploadToBlob"); + upload_result = FILE_UPLOAD_ERROR; + } + + if (threadInfo->uploadBlobSavedData.iotHubClientFileUploadCallback != NULL) + { + /*Codes_SRS_IOTHUBCLIENT_02_055: [ If IoTHubClient_LL_UploadToBlob fails then the thread shall call iotHubClientFileUploadCallbackInternal passing as result FILE_UPLOAD_ERROR and as context the structure from SRS IOTHUBCLIENT 02 051. ]*/ + threadInfo->uploadBlobSavedData.iotHubClientFileUploadCallback(upload_result, threadInfo->context); + } + + return markThreadReadyToBeGarbageCollected(threadInfo); } -#endif -#ifndef DONT_USE_UPLOADTOBLOB IOTHUB_CLIENT_RESULT IoTHubClient_UploadToBlobAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, const unsigned char* source, size_t size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK iotHubClientFileUploadCallback, void* context) { IOTHUB_CLIENT_RESULT result; @@ -1935,214 +2035,129 @@ IOTHUB_CLIENT_RESULT IoTHubClient_UploadToBlobAsync(IOTHUB_CLIENT_HANDLE iotHubC else { /*Codes_SRS_IOTHUBCLIENT_02_051: [IoTHubClient_UploadToBlobAsync shall copy the souce, size, iotHubClientFileUploadCallback, context into a structure.]*/ - UPLOADTOBLOB_SAVED_DATA *savedData = (UPLOADTOBLOB_SAVED_DATA *)malloc(sizeof(UPLOADTOBLOB_SAVED_DATA)); - if (savedData == NULL) + UPLOADTOBLOB_THREAD_INFO *threadInfo = allocateUploadToBlob(destinationFileName, iotHubClientHandle, context); + if (threadInfo == NULL) { /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ - LogError("unable to malloc - oom"); + LogError("unable to create upload thread info"); result = IOTHUB_CLIENT_ERROR; } + else if ((result = initializeUploadToBlobData(threadInfo, source, size, iotHubClientFileUploadCallback)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to initialize upload blob info"); + result = IOTHUB_CLIENT_ERROR; + } + else if ((result = StartWorkerThreadIfNeeded(iotHubClientHandle)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("Could not start worker thread"); + freeUploadToBlobThreadInfo(threadInfo); + } + /*Codes_SRS_IOTHUBCLIENT_02_052: [ IoTHubClient_UploadToBlobAsync shall spawn a thread passing the structure build in SRS IOTHUBCLIENT 02 051 as thread data.]*/ + else if ((result = startUploadToBlobWorkerThread(threadInfo, uploadingThread)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to start upload thread"); + freeUploadToBlobThreadInfo(threadInfo); + } else { - if (mallocAndStrcpy_s((char**)&savedData->destinationFileName, destinationFileName) != 0) - { - /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ - LogError("unable to mallocAndStrcpy_s"); - free(savedData); - result = IOTHUB_CLIENT_ERROR; - } - else - { - savedData->size = size; - int sourceCloned; - if (size == 0) - { - savedData->source = NULL; - sourceCloned = 1; - } - else - { - savedData->source = (unsigned char*)malloc(size); - if (savedData->source == NULL) - { - /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ - LogError("unable to malloc - oom"); - free(savedData->destinationFileName); - free(savedData); - sourceCloned = 0; - } - else - { - sourceCloned = 1; - } - } - - if (sourceCloned == 0) - { - result = IOTHUB_CLIENT_ERROR; - } - else - { - IOTHUB_CLIENT_INSTANCE* iotHubClientHandleData = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle; - - savedData->iotHubClientFileUploadCallback = iotHubClientFileUploadCallback; - savedData->context = context; - (void)memcpy(savedData->source, source, size); - - if ((result = StartWorkerThreadIfNeeded(iotHubClientHandleData)) != IOTHUB_CLIENT_OK) - { - free(savedData->source); - free(savedData->destinationFileName); - free(savedData); - result = IOTHUB_CLIENT_ERROR; - LogError("Could not start worker thread"); - } - else - { - if (Lock(iotHubClientHandleData->LockHandle) != LOCK_OK) /*locking because the next statement is changing blobThreadsToBeJoined*/ - { - LogError("unable to lock"); - free(savedData->source); - free(savedData->destinationFileName); - free(savedData); - result = IOTHUB_CLIENT_ERROR; - } - else - { - /*Codes_SRS_IOTHUBCLIENT_02_058: [ IoTHubClient_UploadToBlobAsync shall add the structure to the list of structures that need to be cleaned once file upload finishes. ]*/ - LIST_ITEM_HANDLE item = singlylinkedlist_add(iotHubClientHandleData->savedDataToBeCleaned, savedData); - if (item == NULL) - { - LogError("unable to singlylinkedlist_add"); - free(savedData->source); - free(savedData->destinationFileName); - free(savedData); - result = IOTHUB_CLIENT_ERROR; - } - else - { - savedData->iotHubClientHandle = iotHubClientHandle; - savedData->canBeGarbageCollected = 0; - if ((savedData->lockGarbage = Lock_Init()) == NULL) - { - (void)singlylinkedlist_remove(iotHubClientHandleData->savedDataToBeCleaned, item); - free(savedData->source); - free(savedData->destinationFileName); - free(savedData); - result = IOTHUB_CLIENT_ERROR; - LogError("unable to Lock_Init"); - } - else - { - /*Codes_SRS_IOTHUBCLIENT_02_052: [ IoTHubClient_UploadToBlobAsync shall spawn a thread passing the structure build in SRS IOTHUBCLIENT 02 051 as thread data.]*/ - if (ThreadAPI_Create(&savedData->uploadingThreadHandle, uploadingThread, savedData) != THREADAPI_OK) - { - /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ - LogError("unablet to ThreadAPI_Create"); - (void)Lock_Deinit(savedData->lockGarbage); - (void)singlylinkedlist_remove(iotHubClientHandleData->savedDataToBeCleaned, item); - free(savedData->source); - free(savedData->destinationFileName); - free(savedData); - result = IOTHUB_CLIENT_ERROR; - } - else - { - result = IOTHUB_CLIENT_OK; - } - } - } - - (void)Unlock(iotHubClientHandleData->LockHandle); - } - } - } - } + result = IOTHUB_CLIENT_OK; } } + return result; } static int uploadMultipleBlock_thread(void* data) { - UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA *blocksData = (UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA *)data; - IOTHUB_CLIENT_LL_HANDLE llHandle = blocksData->iotHubClientHandle->IoTHubClientLLHandle; + UPLOADTOBLOB_THREAD_INFO* threadInfo = (UPLOADTOBLOB_THREAD_INFO*)data; + IOTHUB_CLIENT_LL_HANDLE llHandle = threadInfo->iotHubClientHandle->IoTHubClientLLHandle; - /*Codes_SRS_IOTHUBCLIENT_99_078: [ The thread shall call `IoTHubClient_LL_UploadMultipleBlocksToBlob` passing the information packed in the structure. ]*/ - IOTHUB_CLIENT_RESULT result = IoTHubClient_LL_UploadMultipleBlocksToBlob(llHandle, blocksData->destinationFileName, blocksData->getDataCallback, blocksData->context); + /*Codes_SRS_IOTHUBCLIENT_99_078: [ The thread shall call `IoTHubClient_LL_UploadMultipleBlocksToBlob` or `IoTHubClient_LL_UploadMultipleBlocksToBlobEx` passing the information packed in the structure. ]*/ + IOTHUB_CLIENT_RESULT result; - // Clean resources - free(blocksData->destinationFileName); - free(blocksData); - ThreadAPI_Exit(result); - return 0; + if (threadInfo->uploadBlobMultiblockSavedData.getDataCallback != NULL) + { + result = IoTHubClient_LL_UploadMultipleBlocksToBlob(llHandle, threadInfo->destinationFileName, threadInfo->uploadBlobMultiblockSavedData.getDataCallback, threadInfo->context); + } + else + { + result = IoTHubClient_LL_UploadMultipleBlocksToBlobEx(llHandle, threadInfo->destinationFileName, threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx, threadInfo->context); + } + + return markThreadReadyToBeGarbageCollected(threadInfo); } -IOTHUB_CLIENT_RESULT IoTHubClient_UploadMultipleBlocksToBlobAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) +IOTHUB_CLIENT_RESULT IoTHubClient_UploadMultipleBlocksToBlobAsync_Impl(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) { IOTHUB_CLIENT_RESULT result; - /*Codes_SRS_IOTHUBCLIENT_99_072: [ If `iotHubClientHandle` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ - /*Codes_SRS_IOTHUBCLIENT_99_073: [ If `destinationFileName` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ - /*Codes_SRS_IOTHUBCLIENT_99_074: [ If `getDataCallback` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_99_072: [ If `iotHubClientHandle` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_99_073: [ If `destinationFileName` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_99_074: [ If `getDataCallback` is `NULL` then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ if ( (iotHubClientHandle == NULL) || (destinationFileName == NULL) || - (getDataCallback == NULL) + ((getDataCallback == NULL) && (getDataCallbackEx == NULL)) ) { - LogError("invalid parameters iotHubClientHandle = %p , destinationFileName = %p, getDataCallback = %p", + LogError("invalid parameters iotHubClientHandle = %p , destinationFileName = %p, getDataCallback = %p, getDataCallbackEx = %p", iotHubClientHandle, destinationFileName, - getDataCallback + getDataCallback, + getDataCallbackEx ); result = IOTHUB_CLIENT_INVALID_ARG; } else { - /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ - UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA *blocksData = (UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA *)malloc(sizeof(UPLOADTOBLOB_MULTIBLOCK_SAVED_DATA)); - if (blocksData == NULL) + /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ + UPLOADTOBLOB_THREAD_INFO *threadInfo = allocateUploadToBlob(destinationFileName, iotHubClientHandle, context); + if (threadInfo == NULL) { - /*Codes_SRS_IOTHUBCLIENT_99_077: [ If copying to the structure or spawning the thread fails, then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ - LogError("unable to malloc"); + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to create upload thread info"); result = IOTHUB_CLIENT_ERROR; } else { - /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ - if (mallocAndStrcpy_s((char**)&blocksData->destinationFileName, destinationFileName) != 0) + /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ + threadInfo->uploadBlobMultiblockSavedData.getDataCallback = getDataCallback; + threadInfo->uploadBlobMultiblockSavedData.getDataCallbackEx = getDataCallbackEx; + + if ((result = StartWorkerThreadIfNeeded(iotHubClientHandle)) != IOTHUB_CLIENT_OK) { - /*Codes_SRS_IOTHUBCLIENT_99_077: [ If copying to the structure or spawning the thread fails, then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ - LogError("unable to mallocAndStrcpy_s"); - free(blocksData); - result = IOTHUB_CLIENT_ERROR; + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("Could not start worker thread"); + freeUploadToBlobThreadInfo(threadInfo); + } + else if ((result = startUploadToBlobWorkerThread(threadInfo, uploadMultipleBlock_thread)) != IOTHUB_CLIENT_OK) + { + /*Codes_SRS_IOTHUBCLIENT_02_053: [ If copying to the structure or spawning the thread fails, then IoTHubClient_UploadToBlobAsync shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + LogError("unable to start upload thread"); + freeUploadToBlobThreadInfo(threadInfo); } else { - /*Codes_SRS_IOTHUBCLIENT_99_075: [ `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall copy the `destinationFileName`, `getDataCallback`, `context` and `iotHubClientHandle` into a structure. ]*/ - blocksData->getDataCallback = getDataCallback; - blocksData->context = context; - blocksData->iotHubClientHandle = iotHubClientHandle; - if (ThreadAPI_Create(&blocksData->uploadingThreadHandle, uploadMultipleBlock_thread, blocksData) != THREADAPI_OK) - { - /*Codes_SRS_IOTHUBCLIENT_99_077: [ If copying to the structure or spawning the thread fails, then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall fail and return `IOTHUB_CLIENT_ERROR`. ]*/ - LogError("unable to ThreadAPI_Create"); - free(blocksData->destinationFileName); - free(blocksData); - result = IOTHUB_CLIENT_ERROR; - } - else - { - /*Codes_SRS_IOTHUBCLIENT_99_077: [ If copying to the structure and spawning the thread succeeds, then `IoTHubClient_UploadMultipleBlocksToBlobAsync` shall return `IOTHUB_CLIENT_OK`. ]*/ - result = IOTHUB_CLIENT_OK; - } + /*Codes_SRS_IOTHUBCLIENT_99_077: [ If copying to the structure and spawning the thread succeeds, then `IoTHubClient_UploadMultipleBlocksToBlobAsync(Ex)` shall return `IOTHUB_CLIENT_OK`. ]*/ + result = IOTHUB_CLIENT_OK; } } } - return result; } +IOTHUB_CLIENT_RESULT IoTHubClient_UploadMultipleBlocksToBlobAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) +{ + return IoTHubClient_UploadMultipleBlocksToBlobAsync_Impl(iotHubClientHandle, destinationFileName, getDataCallback, NULL, context); +} + +IOTHUB_CLIENT_RESULT IoTHubClient_UploadMultipleBlocksToBlobAsyncEx(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + return IoTHubClient_UploadMultipleBlocksToBlobAsync_Impl(iotHubClientHandle, destinationFileName, NULL, getDataCallbackEx, context); +} + #endif /*DONT_USE_UPLOADTOBLOB*/ diff --git a/src/sdk/iothub_client.h b/src/sdk/iothub_client.h index c9fdbf5..fcda2d4 100644 --- a/src/sdk/iothub_client.h +++ b/src/sdk/iothub_client.h @@ -253,11 +253,17 @@ extern "C" * - @b messageTimeout - the maximum time in milliseconds until a message * is timeouted. The time starts at IoTHubClient_SendEventAsync. By default, * messages do not expire. @p is a pointer to a uint64_t - * - @b c2d_keep_alive_freq_secs - the AMQP C2D keep alive interval in seconds. + * - @b svc2cl_keep_alive_timeout_secs - the AMQP service side keep alive interval in seconds. * After the connection established the client requests the server to set the * keep alive interval for given time. * If it is not set then the default 240 sec applies. * If it is set to zero the server will not send keep alive messages to the client. + * - @b cl2svc_keep_alive_send_ratio - the AMQP client side keep alive interval in seconds. + * After the connection established the server requests the client to set the + * keep alive interval for given time. + * If it is not set then the default ratio of 1/2 is applied. + * The ratio has to be greater than 0.0 and equal to or less than 0.9 + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. */ MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetOption, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value); @@ -351,15 +357,29 @@ extern "C" */ MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_UploadToBlobAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size, IOTHUB_CLIENT_FILE_UPLOAD_CALLBACK, iotHubClientFileUploadCallback, void*, context); - /** + /** + ** DEPRECATED: Use IoTHubClient_UploadMultipleBlocksToBlobAsyncEx instead ** * @brief Uploads a file to a Blob storage in chunks, fed through the callback function provided by the user. * @remarks This function allows users to upload large files in chunks, not requiring the whole file content to be passed in memory. * @param iotHubClientHandle The handle created by a call to the IoTHubClient_Create function. * @param destinationFileName The name of the file to be created in Azure Blob Storage. * @param getDataCallback A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. * @param context Any data provided by the user to serve as context on getDataCallback. - * @returns An IOTHUB_CLIENT_RESULT value indicating the success or failure of the API call.*/ + * @returns An IOTHUB_CLIENT_RESULT value indicating the success or failure of the API call. + ** DEPRECATED: Use IoTHubClient_UploadMultipleBlocksToBlobAsyncEx instead ** + */ MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_UploadMultipleBlocksToBlobAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context); + + /** + * @brief Uploads a file to a Blob storage in chunks, fed through the callback function provided by the user. + * @remarks This function allows users to upload large files in chunks, not requiring the whole file content to be passed in memory. + * @param iotHubClientHandle The handle created by a call to the IoTHubClient_Create function. + * @param destinationFileName The name of the file to be created in Azure Blob Storage. + * @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * @returns An IOTHUB_CLIENT_RESULT value indicating the success or failure of the API call.*/ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_UploadMultipleBlocksToBlobAsyncEx, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + #endif /* DONT_USE_UPLOADTOBLOB */ #ifdef __cplusplus diff --git a/src/sdk/iothub_client_dll.def b/src/sdk/iothub_client_dll.def index 7c782d6..3981050 100644 --- a/src/sdk/iothub_client_dll.def +++ b/src/sdk/iothub_client_dll.def @@ -1,6 +1,5 @@ LIBRARY iothub_client_dll EXPORTS - IoTHubTransport_ThreadTerminationOffset IoTHubTransport_Create IoTHubTransport_Destroy IoTHubTransport_GetLock @@ -26,4 +25,49 @@ EXPORTS IoTHubClient_SetDeviceTwinCallback IoTHubClient_SendReportedState IoTHubClient_SetDeviceMethodCallback - IoTHubClient_UploadToBlobAsync + + IoTHubClient_LL_CreateFromConnectionString + IoTHubClient_LL_ConnectionStatusCallBack + IoTHubClient_LL_Destroy + IoTHubClient_LL_DeviceMethodComplete + IoTHubClient_LL_DoWork + IoTHubClient_LL_MessageCallback + IoTHubClient_LL_ReportedStateComplete + IoTHubClient_LL_RetrievePropertyComplete + IoTHubClient_LL_GetOption + IoTHubClient_LL_SendComplete + IoTHubClient_LL_SendEventAsync + IoTHubClient_LL_SetMessageCallback + IoTHubClient_LL_SetOption + + IoTHubMessage_CreateFromString + IoTHubMessage_CreateFromByteArray + IoTHubMessage_Clone + IoTHubMessage_Destroy + IoTHubMessage_GetByteArray + IoTHubMessage_GetString + IoTHubMessage_GetContentType + IoTHubMessage_GetContentTypeSystemProperty + IoTHubMessage_GetContentEncodingSystemProperty + IoTHubMessage_GetCorrelationId + IoTHubMessage_GetDiagnosticPropertyData + IoTHubMessage_GetMessageId + IoTHubMessage_Properties + IoTHubMessage_SetContentTypeSystemProperty + IoTHubMessage_SetContentEncodingSystemProperty + IoTHubMessage_SetCorrelationId + IoTHubMessage_SetMessageId + + IOTHUB_CLIENT_CONFIRMATION_RESULTStrings + IOTHUB_CLIENT_FILE_UPLOAD_RESULTStrings + IOTHUB_CLIENT_RESULTStrings + IOTHUB_CLIENT_RETRY_POLICYStrings + IOTHUB_CLIENT_STATUSStrings + IOTHUB_IDENTITY_TYPEStrings + IOTHUB_PROCESS_ITEM_RESULTStrings + IOTHUB_CLIENT_IOTHUB_METHOD_STATUSStrings + IOTHUB_CLIENT_CONFIRMATION_RESULTStrings + IOTHUB_CLIENT_CONNECTION_STATUSStrings + IOTHUB_CLIENT_CONNECTION_STATUS_REASONStrings + TRANSPORT_TYPEStrings + DEVICE_TWIN_UPDATE_STATEStrings diff --git a/src/sdk/iothub_client_ll.c b/src/sdk/iothub_client_ll.c index ec67133..de79414 100644 --- a/src/sdk/iothub_client_ll.c +++ b/src/sdk/iothub_client_ll.c @@ -32,8 +32,21 @@ #define LOG_ERROR_RESULT LogError("result = %s", ENUM_TO_STRING(IOTHUB_CLIENT_RESULT, result)); #define INDEFINITE_TIME ((time_t)(-1)) +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_FILE_UPLOAD_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES); DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_RESULT, IOTHUB_CLIENT_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_RETRY_POLICY, IOTHUB_CLIENT_RETRY_POLICY_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_STATUS, IOTHUB_CLIENT_STATUS_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_IDENTITY_TYPE, IOTHUB_IDENTITY_TYPE_VALUE); +DEFINE_ENUM_STRINGS(IOTHUB_PROCESS_ITEM_RESULT, IOTHUB_PROCESS_ITEM_RESULT_VALUE); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_IOTHUB_METHOD_STATUS, IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_VALUES); DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONNECTION_STATUS, IOTHUB_CLIENT_CONNECTION_STATUS_VALUES); +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONNECTION_STATUS_REASON, IOTHUB_CLIENT_CONNECTION_STATUS_REASON_VALUES); +DEFINE_ENUM_STRINGS(TRANSPORT_TYPE, TRANSPORT_TYPE_VALUES); +DEFINE_ENUM_STRINGS(DEVICE_TWIN_UPDATE_STATE, DEVICE_TWIN_UPDATE_STATE_VALUES); +#ifndef DONT_USE_UPLOADTOBLOB +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_VALUES); +#endif // DONT_USE_UPLOADTOBLOB #define CALLBACK_TYPE_VALUES \ CALLBACK_TYPE_NONE, \ @@ -616,10 +629,6 @@ IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* c { IOTHUB_CLIENT_LL_HANDLE result; - /*Codes_SRS_IOTHUBCLIENT_LL_05_001: [IoTHubClient_LL_CreateFromConnectionString shall obtain the version string by a call to IoTHubClient_GetVersionString.]*/ - /*Codes_SRS_IOTHUBCLIENT_LL_05_002: [IoTHubClient_LL_CreateFromConnectionString shall print the version string to standard output.]*/ - LogInfo("IoT Hub SDK for C, version %s", IoTHubClient_GetVersionString()); - /* Codes_SRS_IOTHUBCLIENT_LL_12_003: [IoTHubClient_LL_CreateFromConnectionString shall verify the input parameter and if it is NULL then return NULL] */ if (connectionString == NULL) { @@ -1753,39 +1762,40 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_SetOption(IOTHUB_CLIENT_LL_HANDLE iotHubCli result = IOTHUB_CLIENT_OK; } } - else + else if (strcmp(optionName, OPTION_BLOB_UPLOAD_TIMEOUT_SECS) == 0) { - - /*Codes_SRS_IOTHUBCLIENT_LL_02_099: [ IoTHubClient_LL_SetOption shall return according to the table below ]*/ - IOTHUB_CLIENT_RESULT uploadToBlob_result; #ifndef DONT_USE_UPLOADTOBLOB - uploadToBlob_result = IoTHubClient_LL_UploadToBlob_SetOption(handleData->uploadToBlobHandle, optionName, value); - if(uploadToBlob_result == IOTHUB_CLIENT_ERROR) + // This option just gets passed down into IoTHubClient_LL_UploadToBlob + /*Codes_SRS_IOTHUBCLIENT_LL_30_010: [ blob_xfr_timeout - IoTHubClient_LL_SetOption shall pass this option to IoTHubClient_UploadToBlob_SetOption and return its result. ]*/ + result = IoTHubClient_LL_UploadToBlob_SetOption(handleData->uploadToBlobHandle, optionName, value); + if(result != IOTHUB_CLIENT_OK) { LogError("unable to IoTHubClient_LL_UploadToBlob_SetOption"); - result = IOTHUB_CLIENT_ERROR; } #else - uploadToBlob_result = IOTHUB_CLIENT_INVALID_ARG; /*harmless value (IOTHUB_CLIENT_INVALID_ARG)in the case when uploadtoblob is not compiled in, otherwise whatever IoTHubClient_LL_UploadToBlob_SetOption returned*/ + LogError("OPTION_BLOB_TRANSFER_TIMEOUT option being set with DONT_USE_UPLOADTOBLOB compiler switch"); + result = IOTHUB_CLIENT_ERROR; #endif /*DONT_USE_UPLOADTOBLOB*/ - - /*Codes_SRS_IOTHUBCLIENT_LL_12_023: [** `c2d_keep_alive_freq_secs` - shall set the cloud to device keep alive frequency(in seconds) for the connection. Zero means keep alive will not be sent. ]*/ - result = - /*based on uploadToBlob_result value this is what happens:*/ - /*IOTHUB_CLIENT_INVALID_ARG always returns what IoTHubTransport_SetOption returns*/ - /*IOTHUB_CLIENT_ERROR always returns IOTHUB_CLIENT_ERROR */ - /*IOTHUB_CLIENT_OK returns OK - IOTHUB_CLIENT_OK if IoTHubTransport_SetOption returns OK or INVALID_ARG - IOTHUB_CLIENT_ERROR if IoTHubTransport_SetOption returns ERROR*/ - - (uploadToBlob_result == IOTHUB_CLIENT_INVALID_ARG) ? handleData->IoTHubTransport_SetOption(handleData->transportHandle, optionName, value) : - (uploadToBlob_result == IOTHUB_CLIENT_ERROR) ? IOTHUB_CLIENT_ERROR : - (handleData->IoTHubTransport_SetOption(handleData->transportHandle, optionName, value) == IOTHUB_CLIENT_ERROR) ? IOTHUB_CLIENT_ERROR : IOTHUB_CLIENT_OK; - - if (result != IOTHUB_CLIENT_OK) + } + else + { + // This section is unusual for SetOption calls because it attempts to pass unhandled options + // to two downstream targets (IoTHubTransport_SetOption and IoTHubClient_LL_UploadToBlob_SetOption) instead of one. + + /*Codes_SRS_IOTHUBCLIENT_LL_30_011: [ IoTHubClient_LL_SetOption shall always pass unhandled options to Transport_SetOption. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_30_012: [ If Transport_SetOption fails, IoTHubClient_LL_SetOption shall return that failure code. ]*/ + result = handleData->IoTHubTransport_SetOption(handleData->transportHandle, optionName, value); + if(result != IOTHUB_CLIENT_OK) + { + LogError("unable to IoTHubTransport_SetOption"); + } +#ifndef DONT_USE_UPLOADTOBLOB + else { - LogError("underlying transport failed, returned = %s", ENUM_TO_STRING(IOTHUB_CLIENT_RESULT, result)); + /*Codes_SRS_IOTHUBCLIENT_LL_30_013: [ If the DONT_USE_UPLOADTOBLOB compiler switch is undefined, IoTHubClient_LL_SetOption shall pass unhandled options to IoTHubClient_UploadToBlob_SetOption and ignore the result. ]*/ + (void)IoTHubClient_LL_UploadToBlob_SetOption(handleData->uploadToBlobHandle, optionName, value); } +#endif /*DONT_USE_UPLOADTOBLOB*/ } } return result; @@ -2091,12 +2101,26 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob(IOTHUB_CLIENT_LL_HANDLE iotHub return result; } +typedef struct UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT_TAG +{ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback; + void* context; +} UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT; + + +IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT uploadMultipleBlocksCallbackWrapper(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context) +{ + UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT* wrapperContext = (UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT*)context; + wrapperContext->getDataCallback(result, data, size, wrapperContext->context); + return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK; +} + IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) { IOTHUB_CLIENT_RESULT result; - /*Codes_SRS_IOTHUBCLIENT_LL_99_005: [ If `iotHubClientHandle` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ - /*Codes_SRS_IOTHUBCLIENT_LL_99_006: [ If `destinationFileName` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ - /*Codes_SRS_IOTHUBCLIENT_LL_99_007: [ If `getDataCallback` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_005: [ If `iotHubClientHandle` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_006: [ If `destinationFileName` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_007: [ If `getDataCallback` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ if ( (iotHubClientHandle == NULL) || (destinationFileName == NULL) || @@ -2108,10 +2132,38 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob(IOTHUB_CLIENT_LL } else { - result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, getDataCallback, context); + UPLOAD_MULTIPLE_BLOCKS_WRAPPER_CONTEXT uploadMultipleBlocksWrapperContext; + uploadMultipleBlocksWrapperContext.getDataCallback = getDataCallback; + uploadMultipleBlocksWrapperContext.context = context; + + result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, uploadMultipleBlocksCallbackWrapper, &uploadMultipleBlocksWrapperContext); + } + return result; +} + +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlobEx(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) +{ + IOTHUB_CLIENT_RESULT result; + /*Codes_SRS_IOTHUBCLIENT_LL_99_005: [ If `iotHubClientHandle` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_006: [ If `destinationFileName` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_007: [ If `getDataCallback` is `NULL` then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall fail and return `IOTHUB_CLIENT_INVALID_ARG`. ]*/ + if ( + (iotHubClientHandle == NULL) || + (destinationFileName == NULL) || + (getDataCallbackEx == NULL) + ) + { + LogError("invalid parameters IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle=%p, destinationFileName=%p, getDataCallbackEx=%p", iotHubClientHandle, destinationFileName, getDataCallbackEx); + result = IOTHUB_CLIENT_INVALID_ARG; + } + else + { + result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(iotHubClientHandle->uploadToBlobHandle, destinationFileName, getDataCallbackEx, context); } return result; } + + #endif /* DONT_USE_UPLOADTOBLOB */ diff --git a/src/sdk/iothub_client_ll.h b/src/sdk/iothub_client_ll.h index b06a904..8bd8f2c 100644 --- a/src/sdk/iothub_client_ll.h +++ b/src/sdk/iothub_client_ll.h @@ -26,6 +26,11 @@ #include "azure_c_shared_utility/macro_utils.h" #include "azure_c_shared_utility/umock_c_prod.h" +#ifdef __cplusplus +extern "C" +{ +#endif + #define IOTHUB_CLIENT_FILE_UPLOAD_RESULT_VALUES \ FILE_UPLOAD_OK, \ FILE_UPLOAD_ERROR @@ -97,6 +102,10 @@ DEFINE_ENUM(IOTHUB_PROCESS_ITEM_RESULT, IOTHUB_PROCESS_ITEM_RESULT_VALUE); */ DEFINE_ENUM(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES); +#ifdef __cplusplus +} +#endif + #include "azure_c_shared_utility/agenttime.h" #include "azure_c_shared_utility/xio.h" #include "azure_c_shared_utility/doublylinkedlist.h" @@ -106,6 +115,11 @@ DEFINE_ENUM(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_V #include #include +#ifdef __cplusplus +extern "C" +{ +#endif + #define IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_VALUES \ IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_SUCCESS, \ IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_ERROR \ @@ -114,10 +128,6 @@ DEFINE_ENUM(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_V */ DEFINE_ENUM(IOTHUB_CLIENT_IOTHUB_METHOD_STATUS, IOTHUB_CLIENT_IOTHUB_METHOD_STATUS_VALUES); -#ifdef __cplusplus -extern "C" -{ -#endif #define IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES \ IOTHUB_CLIENT_CONFIRMATION_OK, \ @@ -183,18 +193,27 @@ extern "C" #define BLOCK_SIZE (4*1024*1024) +#define IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_VALUES \ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK, \ + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT + + DEFINE_ENUM(IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT_VALUES); + /** * @brief Callback invoked by IoTHubClient_UploadMultipleBlocksToBlobAsync requesting the chunks of data to be uploaded. - * @param result The result of the upload of the previous block of data provided by the user. + * @param result The result of the upload of the previous block of data provided by the user (IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX only) * @param data Next block of data to be uploaded, to be provided by the user when this callback is invoked. * @param size Size of the data parameter. * @param context User context provided on the call to IoTHubClient_UploadMultipleBlocksToBlobAsync. - * @remarks If a NULL is provided for parameter "data" and/or zero is provided for "size", the user indicates to the client that the complete file has been uploaded. + * @remarks If the user wants to abort the upload, the callback should return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT + * It should return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK otherwise. + * If a NULL is provided for parameter "data" and/or zero is provided for "size", the user indicates to the client that the complete file has been uploaded. * In such case this callback will be invoked only once more to indicate the status of the final block upload. * If result is not FILE_UPLOAD_OK, the download is cancelled and this callback stops being invoked. * When this callback is called for the last time, no data or size is expected, so data and size are set to NULL */ typedef void(*IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK)(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context); + typedef IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT (*IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX)(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context); #endif /* DONT_USE_UPLOADTOBLOB */ /** @brief This struct captures IoTHub client configuration. */ @@ -575,6 +594,7 @@ extern "C" MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size); /** + ** DEPRECATED: Use IoTHubClient_LL_UploadMultipleBlocksToBlobAsyncEx instead ** * @brief This API uploads to Azure Storage the content provided block by block by @p getDataCallback * under the blob name devicename/@pdestinationFileName * @@ -584,8 +604,23 @@ extern "C" * @param context Any data provided by the user to serve as context on getDataCallback. * * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + ** DEPRECATED: Use IoTHubClient_LL_UploadMultipleBlocksToBlobAsyncEx instead ** */ MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlob, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context); + + /** + * @brief This API uploads to Azure Storage the content provided block by block by @p getDataCallback + * under the blob name devicename/@pdestinationFileName + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param destinationFileName name of the file. + * @param getDataCallbackEx A callback to be invoked to acquire the file chunks to be uploaded, as well as to indicate the status of the upload of the previous block. + * @param context Any data provided by the user to serve as context on getDataCallback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlobEx, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); + #endif /*DONT_USE_UPLOADTOBLOB*/ #ifdef __cplusplus diff --git a/src/sdk/iothub_client_ll_uploadtoblob.c b/src/sdk/iothub_client_ll_uploadtoblob.c index 2b02c86..1067142 100644 --- a/src/sdk/iothub_client_ll_uploadtoblob.c +++ b/src/sdk/iothub_client_ll_uploadtoblob.c @@ -44,6 +44,7 @@ int snprintf(char * s, size_t n, const char * format, ...) /*Codes_SRS_IOTHUBCLIENT_LL_02_085: [ IoTHubClient_LL_UploadToBlob shall use the same authorization as step 1. to prepare and perform a HTTP request with the following parameters: ]*/ #define FILE_UPLOAD_FAILED_BODY "{ \"isSuccess\":false, \"statusCode\":-1,\"statusDescription\" : \"client not able to connect with the server\" }" +#define FILE_UPLOAD_ABORTED_BODY "{ \"isSuccess\":false, \"statusCode\":-1,\"statusDescription\" : \"file upload aborted\" }" #define AUTHORIZATION_SCHEME_VALUES \ DEVICE_KEY, \ @@ -69,6 +70,8 @@ typedef struct IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA_TAG } credentials; /*needed for file upload*/ char* certificates; /*if there are any certificates used*/ HTTP_PROXY_OPTIONS http_proxy_options; + size_t curl_verbose; + size_t blob_upload_timeout_secs; }IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA; typedef struct BLOB_UPLOAD_CONTEXT_TAG @@ -120,6 +123,8 @@ IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE IoTHubClient_LL_UploadToBlob_Create(const I handleData->certificates = NULL; memset(&(handleData->http_proxy_options), 0, sizeof(HTTP_PROXY_OPTIONS)); + handleData->curl_verbose = 0; + handleData->blob_upload_timeout_secs = 0; if ((config->deviceSasToken != NULL) && (config->deviceKey == NULL)) { @@ -175,11 +180,11 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ { int result; - /*Codes_SRS_IOTHUBCLIENT_LL_02_066: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall create an HTTP relative path formed from "/devices/" + deviceId + "/files/" + "?api-version=API_VERSION". ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_066: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTP relative path formed from "/devices/" + deviceId + "/files/" + "?api-version=API_VERSION". ]*/ STRING_HANDLE relativePath = STRING_construct("/devices/"); if (relativePath == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_067: [ If creating the relativePath fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_067: [ If creating the relativePath fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to STRING_construct"); result = __FAILURE__; } @@ -191,17 +196,17 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ (STRING_concat(relativePath, API_VERSION) == 0) )) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_067: [ If creating the relativePath fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_067: [ If creating the relativePath fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to concatenate STRING"); result = __FAILURE__; } else { - /*Codes_SRS_IOTHUBCLIENT_LL_32_001: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall create a JSON string formed from "{ \"blobName\": \" + destinationFileName + "\" }" */ + /*Codes_SRS_IOTHUBCLIENT_LL_32_001: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create a JSON string formed from "{ \"blobName\": \" + destinationFileName + "\" }" */ STRING_HANDLE blobName = STRING_construct("{ \"blobName\": \""); if (blobName == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to STRING_construct"); result = __FAILURE__; } @@ -212,7 +217,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ (STRING_concat(blobName, "\" }") == 0) )) { - /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to concatenate STRING"); result = __FAILURE__; } @@ -223,23 +228,23 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ if (blobBuffer == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_002: [ If creating the JSON string fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to create BUFFER"); result = __FAILURE__; } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_068: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall create an HTTP responseContent BUFFER_HANDLE. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_068: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTP responseContent BUFFER_HANDLE. ]*/ BUFFER_HANDLE responseContent = BUFFER_new(); if (responseContent == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_069: [ If creating the HTTP response buffer handle fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_069: [ If creating the HTTP response buffer handle fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ result = __FAILURE__; LogError("unable to BUFFER_new"); } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_072: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall add the following name:value to request HTTP headers: ] "Content-Type": "application/json" "Accept": "application/json" "User-Agent": "iothubclient/" IOTHUB_SDK_VERSION*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_072: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall add the following name:value to request HTTP headers: ] "Content-Type": "application/json" "Accept": "application/json" "User-Agent": "iothubclient/" IOTHUB_SDK_VERSION*/ /*Codes_SRS_IOTHUBCLIENT_LL_02_107: [ - "Authorization" header shall not be build. ]*/ if (!( (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "Content-Type", "application/json") == HTTP_HEADERS_OK) && @@ -248,7 +253,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ (handleData->authorizationScheme == X509 || (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "Authorization", "") == HTTP_HEADERS_OK)) )) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_071: [ If creating the HTTP headers fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_071: [ If creating the HTTP headers fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to HTTPHeaders_AddHeaderNameValuePair"); result = __FAILURE__; } @@ -269,9 +274,9 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ case(X509): { unsigned int statusCode; - /*Codes_SRS_IOTHUBCLIENT_LL_32_003: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall execute HTTPAPIEX_ExecuteRequest passing the following information for arguments: ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_003: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall execute HTTPAPIEX_ExecuteRequest passing the following information for arguments: ]*/ if (HTTPAPIEX_ExecuteRequest( - iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the handle created at the beginning of `IoTHubClient_LL_UploadMultipleBlocksToBlob`*/ + iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the handle created at the beginning of `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)`*/ HTTPAPI_REQUEST_POST, /*HTTPAPI_REQUEST_TYPE requestType - HTTPAPI_REQUEST_POST*/ STRING_c_str(relativePath), /*const char* relativePath - the HTTP relative path*/ requestHttpHeaders, /*HTTP_HEADERS_HANDLE requestHttpHeadersHandle - request HTTP headers*/ @@ -281,13 +286,13 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ responseContent /*BUFFER_HANDLE responseContent - the HTTP response BUFFER_HANDLE - responseContent*/ ) != HTTPAPIEX_OK) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_076: [ If HTTPAPIEX_ExecuteRequest call fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_076: [ If HTTPAPIEX_ExecuteRequest call fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ result = __FAILURE__; LogError("unable to HTTPAPIEX_ExecuteRequest"); } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_077: [ If HTTP statusCode is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_077: [ If HTTP statusCode is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ if (statusCode >= 300) { result = __FAILURE__; @@ -303,19 +308,19 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ case (SAS_TOKEN): { const char* sasToken = STRING_c_str(handleData->credentials.sas); - /*Codes_SRS_IOTHUBCLIENT_LL_02_073: [ If the credentials used to create handle have "sasToken" then IoTHubClient_LL_UploadMultipleBlocksToBlob shall add the following HTTP request headers: ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_073: [ If the credentials used to create handle have "sasToken" then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall add the following HTTP request headers: ]*/ if (HTTPHeaders_ReplaceHeaderNameValuePair(requestHttpHeaders, "Authorization", sasToken) != HTTP_HEADERS_OK) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_074: [ If adding "Authorization" fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_074: [ If adding "Authorization" fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR ]*/ result = __FAILURE__; LogError("unable to HTTPHeaders_AddHeaderNameValuePair"); } else { unsigned int statusCode; - /*Codes_SRS_IOTHUBCLIENT_LL_32_004: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall execute HTTPAPIEX_ExecuteRequest passing the following information for arguments: ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_004: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall execute HTTPAPIEX_ExecuteRequest passing the following information for arguments: ]*/ if (HTTPAPIEX_ExecuteRequest( - iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the handle created at the beginning of `IoTHubClient_LL_UploadMultipleBlocksToBlob`*/ + iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the handle created at the beginning of `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)`*/ HTTPAPI_REQUEST_POST, /*HTTPAPI_REQUEST_TYPE requestType - HTTPAPI_REQUEST_POST*/ STRING_c_str(relativePath), /*const char* relativePath - the HTTP relative path*/ requestHttpHeaders, /*HTTP_HEADERS_HANDLE requestHttpHeadersHandle - request HTTP headers*/ @@ -325,13 +330,13 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ responseContent /*BUFFER_HANDLE responseContent - the HTTP response BUFFER_HANDLE - responseContent*/ ) != HTTPAPIEX_OK) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_076: [ If HTTPAPIEX_ExecuteRequest call fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_076: [ If HTTPAPIEX_ExecuteRequest call fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ result = __FAILURE__; LogError("unable to HTTPAPIEX_ExecuteRequest"); } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_077: [ If HTTP statusCode is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_077: [ If HTTP statusCode is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ if (statusCode >= 300) { result = __FAILURE__; @@ -347,11 +352,11 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ } case(DEVICE_KEY): { - /*Codes_SRS_IOTHUBCLIENT_LL_02_078: [ If the credentials used to create handle have "deviceKey" then IoTHubClient_LL_UploadMultipleBlocksToBlob shall create an HTTPAPIEX_SAS_HANDLE passing as arguments: ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_078: [ If the credentials used to create handle have "deviceKey" then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTPAPIEX_SAS_HANDLE passing as arguments: ]*/ STRING_HANDLE uriResource = STRING_construct(handleData->hostname); if (uriResource == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ result = __FAILURE__; LogError("unable to STRING_construct"); } @@ -362,7 +367,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ (STRING_concat_with_STRING(uriResource, handleData->deviceId) == 0) )) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to STRING_concat_with_STRING"); result = __FAILURE__; } @@ -376,7 +381,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_089: [ If creating the HTTPAPIEX_SAS_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ HTTPAPIEX_SAS_HANDLE sasHandle = HTTPAPIEX_SAS_Create(handleData->credentials.deviceKey, uriResource, empty); if (sasHandle == NULL) { @@ -386,7 +391,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ else { unsigned int statusCode; - /*Codes_SRS_IOTHUBCLIENT_LL_32_005: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall call HTTPAPIEX_SAS_ExecuteRequest passing as arguments: ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_005: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall call HTTPAPIEX_SAS_ExecuteRequest passing as arguments: ]*/ if (HTTPAPIEX_SAS_ExecuteRequest( sasHandle, /*HTTPAPIEX_SAS_HANDLE sasHandle - the created HTTPAPIEX_SAS_HANDLE*/ iotHubHttpApiExHandle, /*HTTPAPIEX_HANDLE handle - the created HTTPAPIEX_HANDLE*/ @@ -399,7 +404,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ responseContent ) != HTTPAPIEX_OK) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_079: [ If HTTPAPIEX_SAS_ExecuteRequest fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_079: [ If HTTPAPIEX_SAS_ExecuteRequest fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); result = __FAILURE__; } @@ -407,7 +412,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ { if (statusCode >= 300) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_080: [ If status code is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_080: [ If status code is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ result = __FAILURE__; LogError("HTTP code was %u", statusCode); } @@ -442,11 +447,11 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_081: [ Otherwise, IoTHubClient_LL_UploadMultipleBlocksToBlob shall use parson to extract and save the following information from the response buffer: correlationID and SasUri. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_081: [ Otherwise, IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall use parson to extract and save the following information from the response buffer: correlationID and SasUri. ]*/ JSON_Value* allJson = json_parse_string(STRING_c_str(responseAsString)); if (allJson == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_parse_string"); result = __FAILURE__; } @@ -455,7 +460,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ JSON_Object* jsonObject = json_value_get_object(allJson); if (jsonObject == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_value_get_object"); result = __FAILURE__; } @@ -465,7 +470,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ json_correlationId = json_object_get_string(jsonObject, "correlationId"); if (json_correlationId == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_object_get_string(jsonObject, \"correlationId\")"); result = __FAILURE__; } @@ -473,7 +478,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ { if (STRING_copy(correlationId, json_correlationId) != 0) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to copy json_correlationId"); result = __FAILURE__; } @@ -482,7 +487,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ const char* json_hostName = json_object_get_string(jsonObject, "hostName"); if (json_hostName == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_object_get_string(jsonObject, \"hostName\")"); result = __FAILURE__; } @@ -491,7 +496,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ const char* json_containerName = json_object_get_string(jsonObject, "containerName"); if (json_containerName == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_object_get_string(jsonObject, \"containerName\")"); result = __FAILURE__; } @@ -500,7 +505,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ const char* json_blobName = json_object_get_string(jsonObject, "blobName"); if (json_blobName == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_object_get_string(jsonObject, \"blobName\")"); result = __FAILURE__; } @@ -509,7 +514,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ const char* json_sasToken = json_object_get_string(jsonObject, "sasToken"); if (json_sasToken == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to json_object_get_string(jsonObject, \"sasToken\")"); result = __FAILURE__; } @@ -519,7 +524,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ if (STRING_copy(sasUri, "https://") != 0) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to STRING_copy"); result = __FAILURE__; } @@ -530,7 +535,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ if (fileName == NULL) { - /*Codes_SRS_IOTHUBCLIENT_LL_32_009: [ If URL_EncodeString fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_32_009: [ If URL_EncodeString fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to URL_EncodeString of filename"); result = __FAILURE__; } @@ -546,7 +551,7 @@ static int IoTHubClient_LL_UploadToBlob_step1and2(IOTHUB_CLIENT_LL_UPLOADTOBLOB_ (STRING_concat(sasUri, json_sasToken) == 0) )) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_082: [ If extracting and saving the correlationId or SasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to STRING_concat"); result = __FAILURE__; } @@ -592,7 +597,7 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND /*if step 1 failed, there's nothing that step 3 needs to report.*/ /*this POST "tries" to happen*/ - /*Codes_SRS_IOTHUBCLIENT_LL_02_085: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall use the same authorization as step 1. to prepare and perform a HTTP request with the following parameters: ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_085: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall use the same authorization as step 1. to prepare and perform a HTTP request with the following parameters: ]*/ STRING_HANDLE uriResource = STRING_construct(handleData->hostname); if (uriResource == NULL) { @@ -632,7 +637,7 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_086: [ If performing the HTTP request fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_086: [ If performing the HTTP request fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ switch (handleData->authorizationScheme) { default: @@ -661,7 +666,7 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND { if (notificationStatusCode >= 300) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR]*/ LogError("server didn't like the notification request"); result = __FAILURE__; } @@ -703,7 +708,7 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND NULL ) != HTTPAPIEX_OK) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_079: [ If HTTPAPIEX_SAS_ExecuteRequest fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_079: [ If HTTPAPIEX_SAS_ExecuteRequest fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); result = __FAILURE__; ; @@ -712,7 +717,7 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND { if (statusCode >= 300) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR]*/ result = __FAILURE__; LogError("HTTP code was %u", statusCode); } @@ -747,7 +752,7 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND { if (notificationStatusCode >= 300) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_087: [If the statusCode of the HTTP request is greater than or equal to 300 then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR]*/ LogError("server didn't like the notification request"); result = __FAILURE__; } @@ -768,8 +773,8 @@ static int IoTHubClient_LL_UploadToBlob_step3(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HAND return result; } -// this callback splits the source data into blocks to be fed to IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl -static void FileUpload_GetData_Callback(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context) +// this callback splits the source data into blocks to be fed to IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)_Impl +static IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT FileUpload_GetData_Callback(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, unsigned char const ** data, size_t* size, void* context) { BLOB_UPLOAD_CONTEXT* uploadContext = (BLOB_UPLOAD_CONTEXT*) context; @@ -797,39 +802,66 @@ static void FileUpload_GetData_Callback(IOTHUB_CLIENT_FILE_UPLOAD_RESULT result, *size = thisBlockSize; uploadContext->remainingSizeToUpload -= thisBlockSize; } + + return IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_OK; +} + +static HTTPAPIEX_RESULT set_transfer_timeout(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData, HTTPAPIEX_HANDLE iotHubHttpApiExHandle) +{ + HTTPAPIEX_RESULT result; + if (handleData->blob_upload_timeout_secs != 0) + { + // Convert the timeout to milliseconds for curl + long http_timeout = (long)handleData->blob_upload_timeout_secs * 1000; + result = HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_HTTP_TIMEOUT, &http_timeout); + } + else + { + result = HTTPAPIEX_OK; + } + return result; } -IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context) +IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE handle, const char* destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context) { IOTHUB_CLIENT_RESULT result; - /*Codes_SRS_IOTHUBCLIENT_LL_02_061: [ If handle is NULL then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ - /*Codes_SRS_IOTHUBCLIENT_LL_02_062: [ If destinationFileName is NULL then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_061: [ If handle is NULL then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_062: [ If destinationFileName is NULL then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_INVALID_ARG. ]*/ if ( (handle == NULL) || (destinationFileName == NULL) || - (getDataCallback == NULL) + (getDataCallbackEx == NULL) ) { - LogError("invalid argument detected handle=%p destinationFileName=%p getDataCallback=%p", handle, destinationFileName, getDataCallback); + LogError("invalid argument detected handle=%p destinationFileName=%p getDataCallbackEx=%p", handle, destinationFileName, getDataCallbackEx); result = IOTHUB_CLIENT_INVALID_ARG; } else { IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* handleData = (IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA*)handle; - /*Codes_SRS_IOTHUBCLIENT_LL_02_064: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall create an HTTPAPIEX_HANDLE to the IoTHub hostname. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_064: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create an HTTPAPIEX_HANDLE to the IoTHub hostname. ]*/ HTTPAPIEX_HANDLE iotHubHttpApiExHandle = HTTPAPIEX_Create(handleData->hostname); - /*Codes_SRS_IOTHUBCLIENT_LL_02_065: [ If creating the HTTPAPIEX_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_065: [ If creating the HTTPAPIEX_HANDLE fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ if (iotHubHttpApiExHandle == NULL) { LogError("unable to HTTPAPIEX_Create"); result = IOTHUB_CLIENT_ERROR; } + /*Codes_SRS_IOTHUBCLIENT_LL_30_020: [ If the blob_upload_timeout_secs option has been set to non-zero, IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall set the timeout on the underlying transport accordingly. ]*/ + else if (set_transfer_timeout(handleData, iotHubHttpApiExHandle) != HTTPAPIEX_OK) + { + LogError("unable to set blob transfer timeout"); + result = IOTHUB_CLIENT_ERROR; + + } else { + (void)HTTPAPIEX_SetOption(iotHubHttpApiExHandle, OPTION_CURL_VERBOSE, &handleData->curl_verbose); + if ( (handleData->authorizationScheme == X509) && @@ -893,7 +925,7 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(IOTHUB_CLIE } else { - /*Codes_SRS_IOTHUBCLIENT_LL_02_070: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall create request HTTP headers. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_070: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall create request HTTP headers. ]*/ HTTP_HEADERS_HANDLE requestHttpHeaders = HTTPHeaders_Alloc(); /*these are build by step 1 and used by step 3 too*/ if (requestHttpHeaders == NULL) { @@ -921,12 +953,35 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(IOTHUB_CLIE } else { - int step2success; - /*Codes_SRS_IOTHUBCLIENT_LL_02_083: [ IoTHubClient_LL_UploadMultipleBlocksToBlob shall call Blob_UploadFromSasUri and capture the HTTP return code and HTTP body. ]*/ - step2success = (Blob_UploadMultipleBlocksFromSasUri(STRING_c_str(sasUri), getDataCallback, context, &httpResponse, responseToIoTHub, handleData->certificates, &(handleData->http_proxy_options)) == BLOB_OK); - if (!step2success) + /*Codes_SRS_IOTHUBCLIENT_LL_02_083: [ IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall call Blob_UploadFromSasUri and capture the HTTP return code and HTTP body. ]*/ + BLOB_RESULT uploadMultipleBlocksResult = Blob_UploadMultipleBlocksFromSasUri(STRING_c_str(sasUri), getDataCallbackEx, context, &httpResponse, responseToIoTHub, handleData->certificates, &(handleData->http_proxy_options)); + if (uploadMultipleBlocksResult == BLOB_ABORTED) + { + /*Codes_SRS_IOTHUBCLIENT_LL_99_008: [ If step 2 is aborted by the client, then the HTTP message body shall look like: ]*/ + LogInfo("Blob_UploadFromSasUri aborted file upload"); + + if (BUFFER_build(responseToIoTHub, (const unsigned char*)FILE_UPLOAD_ABORTED_BODY, sizeof(FILE_UPLOAD_ABORTED_BODY) / sizeof(FILE_UPLOAD_ABORTED_BODY[0])) == 0) + { + if (IoTHubClient_LL_UploadToBlob_step3(handleData, correlationId, iotHubHttpApiExHandle, requestHttpHeaders, responseToIoTHub) != 0) + { + LogError("IoTHubClient_LL_UploadToBlob_step3 failed"); + result = IOTHUB_CLIENT_ERROR; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_LL_99_009: [ If step 2 is aborted by the client and if step 3 succeeds, then `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` shall return `IOTHUB_CLIENT_OK`. ] */ + result = IOTHUB_CLIENT_OK; + } + } + else + { + LogError("Unable to BUFFER_build, can't perform IoTHubClient_LL_UploadToBlob_step3"); + result = IOTHUB_CLIENT_ERROR; + } + } + else if (uploadMultipleBlocksResult != BLOB_OK) { - /*Codes_SRS_IOTHUBCLIENT_LL_02_084: [ If Blob_UploadFromSasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob shall fail and return IOTHUB_CLIENT_ERROR. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_02_084: [ If Blob_UploadFromSasUri fails then IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex) shall fail and return IOTHUB_CLIENT_ERROR. ]*/ LogError("unable to Blob_UploadFromSasUri"); /*do step 3*/ /*try*/ @@ -995,9 +1050,9 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(IOTHUB_CLIE } } - /*Codes_SRS_IOTHUBCLIENT_LL_99_003: [ If `IoTHubClient_LL_UploadMultipleBlocksToBlob` return `IOTHUB_CLIENT_OK`, it shall call `getDataCallback` with `result` set to `FILE_UPLOAD_OK`, and `data` and `size` set to NULL. ]*/ - /*Codes_SRS_IOTHUBCLIENT_LL_99_004: [ If `IoTHubClient_LL_UploadMultipleBlocksToBlob` does not return `IOTHUB_CLIENT_OK`, it shall call `getDataCallback` with `result` set to `FILE_UPLOAD_ERROR`, and `data` and `size` set to NULL. ]*/ - getDataCallback(result == IOTHUB_CLIENT_OK ? FILE_UPLOAD_OK : FILE_UPLOAD_ERROR, NULL, NULL, context); + /*Codes_SRS_IOTHUBCLIENT_LL_99_003: [ If `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` return `IOTHUB_CLIENT_OK`, it shall call `getDataCallbackEx` with `result` set to `FILE_UPLOAD_OK`, and `data` and `size` set to NULL. ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_004: [ If `IoTHubClient_LL_UploadMultipleBlocksToBlob(Ex)` does not return `IOTHUB_CLIENT_OK`, it shall call `getDataCallbackEx` with `result` set to `FILE_UPLOAD_ERROR`, and `data` and `size` set to NULL. ]*/ + (void)getDataCallbackEx(result == IOTHUB_CLIENT_OK ? FILE_UPLOAD_OK : FILE_UPLOAD_ERROR, NULL, NULL, context); return result; } @@ -1020,7 +1075,7 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob_Impl(IOTHUB_CLIENT_LL_UPLOADTO context.blobSourceSize = size; context.remainingSizeToUpload = size; - /*Codes_SRS_IOTHUBCLIENT_LL_99_002: [ `IoTHubClient_LL_UploadToBlob` shall call `IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl` with `FileUpload_GetData_Callback` as `getDataCallback` and pass the struct created at step SRS_IOTHUBCLIENT_LL_99_001 as `context` ]*/ + /*Codes_SRS_IOTHUBCLIENT_LL_99_002: [ `IoTHubClient_LL_UploadToBlob` shall call `IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl` with `FileUpload_GetData_Callback` as `getDataCallbackEx` and pass the struct created at step SRS_IOTHUBCLIENT_LL_99_001 as `context` ]*/ result = IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl(handle, destinationFileName, FileUpload_GetData_Callback, &context); } return result; @@ -1164,7 +1219,7 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob_SetOption(IOTHUB_CLIENT_LL_UPL } } } - else if (strcmp("TrustedCerts", optionName) == 0) + else if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0) { if (value == NULL) { @@ -1249,6 +1304,16 @@ IOTHUB_CLIENT_RESULT IoTHubClient_LL_UploadToBlob_SetOption(IOTHUB_CLIENT_LL_UPL } } } + else if (strcmp(optionName, OPTION_CURL_VERBOSE) == 0) + { + handleData->curl_verbose = *(size_t*)value; + result = IOTHUB_CLIENT_OK; + } + else if (strcmp(optionName, OPTION_BLOB_UPLOAD_TIMEOUT_SECS) == 0) + { + handleData->blob_upload_timeout_secs = *(size_t*)value; + result = IOTHUB_CLIENT_OK; + } else { /*Codes_SRS_IOTHUBCLIENT_LL_02_102: [ If an unknown option is presented then IoTHubClient_LL_UploadToBlob_SetOption shall return IOTHUB_CLIENT_INVALID_ARG. ]*/ diff --git a/src/sdk/iothub_client_ll_uploadtoblob.h b/src/sdk/iothub_client_ll_uploadtoblob.h index 9cf143e..1ecebe1 100644 --- a/src/sdk/iothub_client_ll_uploadtoblob.h +++ b/src/sdk/iothub_client_ll_uploadtoblob.h @@ -40,7 +40,7 @@ typedef struct IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE_DATA* IOTHUB_CLIENT_LL_UPLOA MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, IoTHubClient_LL_UploadToBlob_Create, const IOTHUB_CLIENT_CONFIG*, config); MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob_Impl, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, destinationFileName, const unsigned char*, source, size_t, size); - MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK, getDataCallback, void*, context); + MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadMultipleBlocksToBlob_Impl, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, destinationFileName, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX, getDataCallbackEx, void*, context); MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob_SetOption, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle, const char*, optionName, const void*, value); MOCKABLE_FUNCTION(, void, IoTHubClient_LL_UploadToBlob_Destroy, IOTHUB_CLIENT_LL_UPLOADTOBLOB_HANDLE, handle); diff --git a/src/sdk/iothub_client_options.h b/src/sdk/iothub_client_options.h index 9b94ccc..162a58b 100644 --- a/src/sdk/iothub_client_options.h +++ b/src/sdk/iothub_client_options.h @@ -4,6 +4,8 @@ #ifndef IOTHUB_CLIENT_OPTIONS_H #define IOTHUB_CLIENT_OPTIONS_H +#include "azure_c_shared_utility/const_defines.h" + #ifdef __cplusplus extern "C" { @@ -16,25 +18,31 @@ extern "C" const char* password; } IOTHUB_PROXY_OPTIONS; - static const char* OPTION_LOG_TRACE = "logtrace"; - static const char* OPTION_X509_CERT = "x509certificate"; - static const char* OPTION_X509_PRIVATE_KEY = "x509privatekey"; - static const char* OPTION_KEEP_ALIVE = "keepalive"; - static const char* OPTION_CONNECTION_TIMEOUT = "connect_timeout"; + static STATIC_VAR_UNUSED const char* OPTION_LOG_TRACE = "logtrace"; + static STATIC_VAR_UNUSED const char* OPTION_X509_CERT = "x509certificate"; + static STATIC_VAR_UNUSED const char* OPTION_X509_PRIVATE_KEY = "x509privatekey"; + static STATIC_VAR_UNUSED const char* OPTION_KEEP_ALIVE = "keepalive"; + static STATIC_VAR_UNUSED const char* OPTION_CONNECTION_TIMEOUT = "connect_timeout"; + + static STATIC_VAR_UNUSED const char* OPTION_PROXY_HOST = "proxy_address"; + static STATIC_VAR_UNUSED const char* OPTION_PROXY_USERNAME = "proxy_username"; + static STATIC_VAR_UNUSED const char* OPTION_PROXY_PASSWORD = "proxy_password"; - static const char* OPTION_PROXY_HOST = "proxy_address"; - static const char* OPTION_PROXY_USERNAME = "proxy_username"; - static const char* OPTION_PROXY_PASSWORD = "proxy_password"; + static STATIC_VAR_UNUSED const char* OPTION_SAS_TOKEN_LIFETIME = "sas_token_lifetime"; + static STATIC_VAR_UNUSED const char* OPTION_SAS_TOKEN_REFRESH_TIME = "sas_token_refresh_time"; + static STATIC_VAR_UNUSED const char* OPTION_CBS_REQUEST_TIMEOUT = "cbs_request_timeout"; - static const char* OPTION_SAS_TOKEN_LIFETIME = "sas_token_lifetime"; - static const char* OPTION_SAS_TOKEN_REFRESH_TIME = "sas_token_refresh_time"; - static const char* OPTION_CBS_REQUEST_TIMEOUT = "cbs_request_timeout"; + static STATIC_VAR_UNUSED const char* OPTION_MIN_POLLING_TIME = "MinimumPollingTime"; + static STATIC_VAR_UNUSED const char* OPTION_BATCHING = "Batching"; - static const char* OPTION_MIN_POLLING_TIME = "MinimumPollingTime"; - static const char* OPTION_BATCHING = "Batching"; + static STATIC_VAR_UNUSED const char* OPTION_MESSAGE_TIMEOUT = "messageTimeout"; + static STATIC_VAR_UNUSED const char* OPTION_BLOB_UPLOAD_TIMEOUT_SECS = "blob_upload_timeout_secs"; + static STATIC_VAR_UNUSED const char* OPTION_PRODUCT_INFO = "product_info"; - static const char* OPTION_MESSAGE_TIMEOUT = "messageTimeout"; - static const char* OPTION_PRODUCT_INFO = "product_info"; + /* + * @brief Turns on automatic URL encoding of message properties + system properties. Only valid for use with MQTT Transport + */ + static STATIC_VAR_UNUSED const char* OPTION_AUTO_URL_ENCODE_DECODE = "auto_url_encode_decode"; /* * @brief Informs the service of what is the maximum period the client will wait for a keep-alive message from the service. * The service must send keep-alives before this timeout is reached, otherwise the client will trigger its re-connection logic. @@ -42,10 +50,21 @@ extern "C" * The default value for this option is 240 seconds, and the minimum allowed is usually 5 seconds. * To virtually disable the keep-alives from the service (and consequently the keep-alive timeout control on the client-side), set this option to a high value (e.g., UINT_MAX). */ - static const char* OPTION_C2D_KEEP_ALIVE_FREQ_SECS = "c2d_keep_alive_freq_secs"; + static STATIC_VAR_UNUSED const char* OPTION_SERVICE_SIDE_KEEP_ALIVE_FREQ_SECS = "svc2cl_keep_alive_timeout_secs"; + + /* DEPRECATED:: OPTION_C2D_KEEP_ALIVE_FREQ_SECS is DEPRECATED! Use OPTION_SERVICE_SIDE_KEEP_ALIVE_FREQ_SECS, but OPTION_C2D_KEEP_ALIVE_FREQ_SECS legacy variable kept for back-compat. */ + static STATIC_VAR_UNUSED const char* OPTION_C2D_KEEP_ALIVE_FREQ_SECS = "c2d_keep_alive_freq_secs"; + + /* + * @brief Ratio to be used for client side pings in AMQP protocol. + * The client must use this ratio to send keep-alives before service side remote idle timeout is reached, otherwise the service will disconnect the client. + * The default value for this option is 1/2 of the remote idle value sent by the service. + * For AMQP remote idle set to 4 minutes, default client ping will be 2 minutes. For AMQP remote idle set to 25 minutes configured via per Hub basis, the default ping will be 12.5 minutes. + */ + static STATIC_VAR_UNUSED const char* OPTION_REMOTE_IDLE_TIMEOUT_RATIO = "cl2svc_keep_alive_send_ratio"; //diagnostic sampling percentage value, [0-100] - static const char* OPTION_DIAGNOSTIC_SAMPLING_PERCENTAGE = "diag_sampling_percentage"; + static STATIC_VAR_UNUSED const char* OPTION_DIAGNOSTIC_SAMPLING_PERCENTAGE = "diag_sampling_percentage"; #ifdef __cplusplus } diff --git a/src/sdk/iothub_client_private.h b/src/sdk/iothub_client_private.h index 15999ea..36e5c6b 100644 --- a/src/sdk/iothub_client_private.h +++ b/src/sdk/iothub_client_private.h @@ -61,8 +61,8 @@ typedef struct IOTHUB_DEVICE_TWIN_TAG CONSTBUFFER_HANDLE report_data_handle; void* context; DLIST_ENTRY entry; - IOTHUB_CLIENT_LL_HANDLE client_handle; - IOTHUB_DEVICE_HANDLE device_handle; + IOTHUB_CLIENT_LL_HANDLE client_handle; + IOTHUB_DEVICE_HANDLE device_handle; } IOTHUB_DEVICE_TWIN; union IOTHUB_IDENTITY_INFO_TAG diff --git a/src/sdk/iothub_client_retry_control.c b/src/sdk/iothub_client_retry_control.c index c32716e..9e697e7 100644 --- a/src/sdk/iothub_client_retry_control.c +++ b/src/sdk/iothub_client_retry_control.c @@ -32,44 +32,6 @@ typedef int (*RETRY_ACTION_EVALUATION_FUNCTION)(RETRY_CONTROL_INSTANCE* retry_st // ========== Helper Functions ========== // -static int evaluate_retry_action_fixed_interval(RETRY_CONTROL_INSTANCE* retry_control, RETRY_ACTION* retry_action) -{ - int result; - - time_t current_time; - - if ((current_time = get_time(NULL)) == INDEFINITE_TIME) - { - LogError("Cannot evaluate if should retry (get_time failed)"); - result = __FAILURE__; - } - else - { - if (retry_control->max_retry_time_in_secs > 0 && - get_difftime(current_time, retry_control->last_retry_time) >= retry_control->max_retry_time_in_secs) - { - *retry_action = RETRY_ACTION_STOP_RETRYING; - } - else - { - if (get_difftime(current_time, retry_control->last_retry_time) >= retry_control->current_wait_time_in_secs) - { - *retry_action = RETRY_ACTION_RETRY_NOW; - retry_control->last_retry_time = current_time; - } - else - { - *retry_action = RETRY_ACTION_RETRY_LATER; - } - } - - result = RESULT_OK; - } - - return result; -} - - // ---------- Set/Retrieve Options Helpers ----------// static void* retry_control_clone_option(const char* name, const void* value) diff --git a/src/sdk/iothub_client_retry_control.h b/src/sdk/iothub_client_retry_control.h index 4da7073..d553861 100644 --- a/src/sdk/iothub_client_retry_control.h +++ b/src/sdk/iothub_client_retry_control.h @@ -9,15 +9,16 @@ #include "azure_c_shared_utility/optionhandler.h" #include "azure_c_shared_utility/umock_c_prod.h" #include "iothub_client_ll.h" +#include "azure_c_shared_utility/const_defines.h" #ifdef __cplusplus extern "C" { #endif -static const char* RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS = "initial_wait_time_in_secs"; -static const char* RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT = "max_jitter_percent"; -static const char* RETRY_CONTROL_OPTION_SAVED_OPTIONS = "retry_control_saved_options"; +static STATIC_VAR_UNUSED const char* RETRY_CONTROL_OPTION_INITIAL_WAIT_TIME_IN_SECS = "initial_wait_time_in_secs"; +static STATIC_VAR_UNUSED const char* RETRY_CONTROL_OPTION_MAX_JITTER_PERCENT = "max_jitter_percent"; +static STATIC_VAR_UNUSED const char* RETRY_CONTROL_OPTION_SAVED_OPTIONS = "retry_control_saved_options"; typedef enum RETRY_ACTION_TAG { diff --git a/src/sdk/iothub_client_version.h b/src/sdk/iothub_client_version.h index bf79c4d..a546dc8 100644 --- a/src/sdk/iothub_client_version.h +++ b/src/sdk/iothub_client_version.h @@ -8,7 +8,7 @@ #ifndef IOTHUB_CLIENT_VERSION_H #define IOTHUB_CLIENT_VERSION_H -#define IOTHUB_SDK_VERSION "1.1.29" +#define IOTHUB_SDK_VERSION "1.2.4" #include "azure_c_shared_utility/umock_c_prod.h" diff --git a/src/sdk/iothubtransport_mqtt_common.c b/src/sdk/iothubtransport_mqtt_common.c index d54eb83..5a636bb 100644 --- a/src/sdk/iothubtransport_mqtt_common.c +++ b/src/sdk/iothubtransport_mqtt_common.c @@ -62,7 +62,6 @@ static const char* TOPIC_DEVICE_MSG = "devices/%s/messages/devicebound/#"; static const char* TOPIC_DEVICE_DEVICE = "devices/%s/messages/events/"; static const char* TOPIC_DEVICE_METHOD_SUBSCRIBE = "$iothub/methods/POST/#"; -static const char* TOPIC_DEVICE_METHOD_RESPONSE = "$iothub/methods/res"; static const char* IOTHUB_API_VERSION = "2016-11-14"; @@ -129,7 +128,8 @@ typedef enum MQTT_CLIENT_STATUS_TAG { MQTT_CLIENT_STATUS_NOT_CONNECTED, MQTT_CLIENT_STATUS_CONNECTING, - MQTT_CLIENT_STATUS_CONNECTED + MQTT_CLIENT_STATUS_CONNECTED, + MQTT_CLIENT_STATUS_PENDING_CLOSE } MQTT_CLIENT_STATUS; typedef struct MQTTTRANSPORT_HANDLE_DATA_TAG @@ -196,6 +196,7 @@ typedef struct MQTTTRANSPORT_HANDLE_DATA_TAG // Telemetry specific DLIST_ENTRY telemetry_waitingForAck; + bool auto_url_encode_decode; // Controls frequency of reconnection logic. RETRY_CONTROL_HANDLE retry_control_handle; @@ -479,7 +480,7 @@ static int parse_device_twin_topic_info(const char* resp_topic, bool* patch_msg, } else if (token_count == 3) { - *status_code = atol(STRING_c_str(token_value)); + *status_code = (int)atol(STRING_c_str(token_value)); if (STRING_TOKENIZER_get_next_token(token_handle, token_value, "/?$rid=") == 0) { *request_id = (size_t)atol(STRING_c_str(token_value)); @@ -578,172 +579,231 @@ static void sendMsgComplete(IOTHUB_MESSAGE_LIST* iothubMsgList, PMQTTTRANSPORT_H IoTHubClient_LL_SendComplete(transport_data->llClientHandle, &messageCompleted, confirmResult); } -static STRING_HANDLE addPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, const char* eventTopic) +static int addUserPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr, bool urlencode) { - STRING_HANDLE result = STRING_construct(eventTopic); + int result = 0; const char* const* propertyKeys; const char* const* propertyValues; size_t propertyCount; - size_t index = 0; - - // Construct Properties + size_t index = *index_ptr; MAP_HANDLE properties_map = IoTHubMessage_Properties(iothub_message_handle); if (properties_map != NULL) { if (Map_GetInternals(properties_map, &propertyKeys, &propertyValues, &propertyCount) != MAP_OK) { LogError("Failed to get the internals of the property map."); - STRING_delete(result); - result = NULL; + result = __FAILURE__; } else { if (propertyCount != 0) { - for (index = 0; index < propertyCount && result != NULL; index++) + for (index = 0; index < propertyCount && result == 0; index++) { - if (STRING_sprintf(result, "%s=%s%s", propertyKeys[index], propertyValues[index], propertyCount - 1 == index ? "" : PROPERTY_SEPARATOR) != 0) + if (urlencode) { - LogError("Failed construting property string."); - STRING_delete(result); - result = NULL; + STRING_HANDLE property_key = URL_EncodeString(propertyKeys[index]); + STRING_HANDLE property_value = URL_EncodeString(propertyValues[index]); + if ((property_key == NULL) || (property_value == NULL)) + { + LogError("Failed URL Encoding properties"); + result = __FAILURE__; + } + else if (STRING_sprintf(topic_string, "%s=%s%s", STRING_c_str(property_key), STRING_c_str(property_value), propertyCount - 1 == index ? "" : PROPERTY_SEPARATOR) != 0) + { + LogError("Failed constructing property string."); + result = __FAILURE__; + } + STRING_delete(property_key); + STRING_delete(property_value); + } + else + { + if (STRING_sprintf(topic_string, "%s=%s%s", propertyKeys[index], propertyValues[index], propertyCount - 1 == index ? "" : PROPERTY_SEPARATOR) != 0) + { + LogError("Failed constructing property string."); + result = __FAILURE__; + } } } } } } + *index_ptr = index; + return result; +} - /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_052: [ IoTHubTransport_MQTT_Common_DoWork shall check for the CorrelationId property and if found add the value as a system property in the format of $.cid= ] */ - if (result != NULL) +static int addSystemPropertyToTopicString(STRING_HANDLE topic_string, size_t index, const char* property_key, const char* property_value, bool urlencode) +{ + int result = 0; + + if (urlencode) { - const char* correlation_id = IoTHubMessage_GetCorrelationId(iothub_message_handle); - if (correlation_id != NULL) + STRING_HANDLE encoded_property_value = URL_EncodeString(property_value); + if (encoded_property_value == NULL) { - if (STRING_sprintf(result, "%s%%24.cid=%s", index == 0 ? "" : PROPERTY_SEPARATOR, correlation_id) != 0) - { - LogError("Failed setting correlation id."); - STRING_delete(result); - result = NULL; - } - index++; + LogError("Failed URL encoding %s.", property_key); + result = __FAILURE__; + } + else if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, property_key, STRING_c_str(encoded_property_value)) != 0) + { + LogError("Failed setting %s.", property_key); + result = __FAILURE__; + } + STRING_delete(encoded_property_value); + } + else + { + if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, property_key, property_value) != 0) + { + LogError("Failed setting %s.", property_key); + result = __FAILURE__; } } + return result; +} + +static int addSystemPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr, bool urlencode) +{ + (void)urlencode; + int result = 0; + size_t index = *index_ptr; + /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_052: [ IoTHubTransport_MQTT_Common_DoWork shall check for the CorrelationId property and if found add the value as a system property in the format of $.cid= ] */ + const char* correlation_id = IoTHubMessage_GetCorrelationId(iothub_message_handle); + if (correlation_id != NULL) + { + result = addSystemPropertyToTopicString(topic_string, index, CORRELATION_ID_PROPERTY, correlation_id, urlencode); + index++; + } /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_053: [ IoTHubTransport_MQTT_Common_DoWork shall check for the MessageId property and if found add the value as a system property in the format of $.mid= ] */ - if (result != NULL) + if (result == 0) { const char* msg_id = IoTHubMessage_GetMessageId(iothub_message_handle); if (msg_id != NULL) { - if (STRING_sprintf(result, "%s%%24.mid=%s", index == 0 ? "" : PROPERTY_SEPARATOR, msg_id) != 0) - { - LogError("Failed setting message id."); - STRING_delete(result); - result = NULL; - } + result = addSystemPropertyToTopicString(topic_string, index, MESSAGE_ID_PROPERTY, msg_id, urlencode); index++; } } - // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_010: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the ContentType property and if found add the `value` as a system property in the format of `$.ct=` ] - if (result != NULL) + if (result == 0) { const char* content_type = IoTHubMessage_GetContentTypeSystemProperty(iothub_message_handle); if (content_type != NULL) { - if (STRING_sprintf(result, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, CONTENT_TYPE_PROPERTY, content_type) != 0) - { - LogError("Failed setting content type."); - STRING_delete(result); - result = NULL; - } + result = addSystemPropertyToTopicString(topic_string, index, CONTENT_TYPE_PROPERTY, content_type, urlencode); index++; } } - // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_011: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the ContentEncoding property and if found add the `value` as a system property in the format of `$.ce=` ] - if (result != NULL) + if (result == 0) { const char* content_encoding = IoTHubMessage_GetContentEncodingSystemProperty(iothub_message_handle); if (content_encoding != NULL) { - if (STRING_sprintf(result, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, CONTENT_ENCODING_PROPERTY, content_encoding) != 0) - { - LogError("Failed setting content encoding."); - STRING_delete(result); - result = NULL; - } + result = addSystemPropertyToTopicString(topic_string, index, CONTENT_ENCODING_PROPERTY, content_encoding, urlencode); index++; } } + *index_ptr = index; + return result; +} + +static int addDiagnosticPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr) +{ + int result = 0; + size_t index = *index_ptr; // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_014: [ `IoTHubTransport_MQTT_Common_DoWork` shall check for the diagnostic properties including diagid and diagCreationTimeUtc and if found both add them as system property in the format of `$.diagid` and `$.diagctx` respectively] - if (result != NULL) + const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* diagnosticData = IoTHubMessage_GetDiagnosticPropertyData(iothub_message_handle); + if (diagnosticData != NULL) { - const IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA* diagnosticData = IoTHubMessage_GetDiagnosticPropertyData(iothub_message_handle); - if (diagnosticData != NULL) + const char* diag_id = diagnosticData->diagnosticId; + const char* creation_time_utc = diagnosticData->diagnosticCreationTimeUtc; + //diagid and creationtimeutc must be present/unpresent simultaneously + if (diag_id != NULL && creation_time_utc != NULL) { - const char* diag_id = diagnosticData->diagnosticId; - const char* creation_time_utc = diagnosticData->diagnosticCreationTimeUtc; - //diagid and creationtimeutc must be present/unpresent simultaneously - if (diag_id != NULL && creation_time_utc != NULL) + if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, DIAGNOSTIC_ID_PROPERTY, diag_id) != 0) + { + LogError("Failed setting diagnostic id"); + result = __FAILURE__; + } + index++; + + if (result == 0) { - if (STRING_sprintf(result, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, DIAGNOSTIC_ID_PROPERTY, diag_id) != 0) + //construct diagnostic context, it should be urlencode(key1=value1,key2=value2) + STRING_HANDLE diagContextHandle = STRING_construct_sprintf("%s=%s", DIAGNOSTIC_CONTEXT_CREATION_TIME_UTC_PROPERTY, creation_time_utc); + if (diagContextHandle == NULL) { - LogError("Failed setting diagnostic id"); - STRING_delete(result); - result = NULL; + LogError("Failed constructing diagnostic context"); + result = __FAILURE__; } - index++; - - if (result != NULL) + else { - //construct diagnostic context, it should be urlencode(key1=value1,key2=value2) - STRING_HANDLE diagContextHandle = STRING_construct_sprintf("%s=%s", DIAGNOSTIC_CONTEXT_CREATION_TIME_UTC_PROPERTY, creation_time_utc); - if (diagContextHandle == NULL) + //Add other diagnostic context properties here if have more + STRING_HANDLE encodedContextValueHandle = URL_Encode(diagContextHandle); + const char* encodedContextValueString = NULL; + if (encodedContextValueHandle != NULL && + (encodedContextValueString = STRING_c_str(encodedContextValueHandle)) != NULL) { - LogError("Failed constructing diagnostic context"); - STRING_delete(result); - result = NULL; + if (STRING_sprintf(topic_string, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, DIAGNOSTIC_CONTEXT_PROPERTY, encodedContextValueString) != 0) + { + LogError("Failed setting diagnostic context"); + result = __FAILURE__; + } + STRING_delete(encodedContextValueHandle); + encodedContextValueHandle = NULL; } else { - //Add other diagnostic context properties here if have more - STRING_HANDLE encodedContextValueHandle = URL_Encode(diagContextHandle); - const char* encodedContextValueString = NULL; - if (encodedContextValueHandle != NULL && - (encodedContextValueString = STRING_c_str(encodedContextValueHandle)) != NULL) - { - if (STRING_sprintf(result, "%s%%24.%s=%s", index == 0 ? "" : PROPERTY_SEPARATOR, DIAGNOSTIC_CONTEXT_PROPERTY, encodedContextValueString) != 0) - { - LogError("Failed setting diagnostic context"); - STRING_delete(result); - result = NULL; - } - STRING_delete(encodedContextValueHandle); - encodedContextValueHandle = NULL; - } - else - { - LogError("Failed encoding diagnostic context value"); - STRING_delete(result); - result = NULL; - } - STRING_delete(diagContextHandle); - diagContextHandle = NULL; - index++; + LogError("Failed encoding diagnostic context value"); + result = __FAILURE__; } + STRING_delete(diagContextHandle); + diagContextHandle = NULL; + index++; } } - else if (diag_id != NULL || creation_time_utc != NULL) - { - // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_015: [ `IoTHubTransport_MQTT_Common_DoWork` shall check whether diagid and diagCreationTimeUtc be present simultaneously, treat as error if not] - LogError("diagid and diagcreationtimeutc must be present simultaneously."); - STRING_delete(result); - result = NULL; - } + } + else if (diag_id != NULL || creation_time_utc != NULL) + { + // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_015: [ `IoTHubTransport_MQTT_Common_DoWork` shall check whether diagid and diagCreationTimeUtc be present simultaneously, treat as error if not] + LogError("diagid and diagcreationtimeutc must be present simultaneously."); + result = __FAILURE__; } } + return result; +} + + +static STRING_HANDLE addPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, const char* eventTopic, bool urlencode) +{ + size_t index = 0; + STRING_HANDLE result = STRING_construct(eventTopic); + if (result == NULL) + { + LogError("Failed to create event topic string handle"); + } + else if (addUserPropertiesTouMqttMessage(iothub_message_handle, result, &index, urlencode) != 0) + { + LogError("Failed adding Properties to uMQTT Message"); + STRING_delete(result); + result = NULL; + } + else if (addSystemPropertiesTouMqttMessage(iothub_message_handle, result, &index, urlencode) != 0) + { + LogError("Failed adding System Properties to uMQTT Message"); + STRING_delete(result); + result = NULL; + } + else if (addDiagnosticPropertiesTouMqttMessage(iothub_message_handle, result, &index) != 0) + { + LogError("Failed adding Diagnostic Properties to uMQTT Message"); + STRING_delete(result); + result = NULL; + } return result; } @@ -751,7 +811,7 @@ static STRING_HANDLE addPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_me static int publish_mqtt_telemetry_msg(PMQTTTRANSPORT_HANDLE_DATA transport_data, MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry, const unsigned char* payload, size_t len) { int result; - STRING_HANDLE msgTopic = addPropertiesTouMqttMessage(mqttMsgEntry->iotHubMessageEntry->messageHandle, STRING_c_str(transport_data->topic_MqttEvent)); + STRING_HANDLE msgTopic = addPropertiesTouMqttMessage(mqttMsgEntry->iotHubMessageEntry->messageHandle, STRING_c_str(transport_data->topic_MqttEvent), transport_data->auto_url_encode_decode); if (msgTopic == NULL) { LogError("Failed adding properties to mqtt message"); @@ -997,7 +1057,7 @@ static int setMqttMessagePropertyIfPossible(IOTHUB_MESSAGE_HANDLE IoTHubMessage, return result; } -static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name) +static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name, bool urldecode) { int result; STRING_HANDLE mqttTopic = STRING_construct(topic_name); @@ -1065,10 +1125,28 @@ static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char strncpy(propValue, iterator + 1, valLen); propValue[valLen] = '\0'; - if (setMqttMessagePropertyIfPossible(IoTHubMessage, propName, propValue, nameLen) != 0) + if (urldecode) + { + STRING_HANDLE propValue_decoded; + if ((propValue_decoded = URL_DecodeString(propValue)) == NULL) + { + LogError("Failed to URL decode property value"); + result = __FAILURE__; + } + else if (setMqttMessagePropertyIfPossible(IoTHubMessage, propName, STRING_c_str(propValue_decoded), nameLen) != 0) + { + LogError("Unable to set message property"); + result = __FAILURE__; + } + STRING_delete(propValue_decoded); + } + else { - LogError("Unable to set message property"); - result = __FAILURE__; + if (setMqttMessagePropertyIfPossible(IoTHubMessage, propName, propValue, nameLen) != 0) + { + LogError("Unable to set message property"); + result = __FAILURE__; + } } } free(propName); @@ -1079,7 +1157,7 @@ static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char iterator++; } } - else + else //User Properties { const char* iterator = tokenData; while (iterator != NULL && *iterator != '\0' && result == 0) @@ -1104,10 +1182,30 @@ static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char strncpy(propValue, iterator + 1, valLen); propValue[valLen] = '\0'; - if (Map_AddOrUpdate(propertyMap, propName, propValue) != MAP_OK) + if (urldecode) + { + STRING_HANDLE propName_decoded = URL_DecodeString(propName); + STRING_HANDLE propValue_decoded = URL_DecodeString(propValue); + if (propName_decoded == NULL || propValue_decoded == NULL) + { + LogError("Failed to URL decode property"); + result = __FAILURE__; + } + else if (Map_AddOrUpdate(propertyMap, STRING_c_str(propName_decoded), STRING_c_str(propValue_decoded)) != MAP_OK) + { + LogError("Map_AddOrUpdate failed."); + result = __FAILURE__; + } + STRING_delete(propName_decoded); + STRING_delete(propValue_decoded); + } + else { - LogError("Map_AddOrUpdate failed."); - result = __FAILURE__; + if (Map_AddOrUpdate(propertyMap, propName, propValue) != MAP_OK) + { + LogError("Map_AddOrUpdate failed."); + result = __FAILURE__; + } } } free(propName); @@ -1250,7 +1348,7 @@ static void mqtt_notification_callback(MQTT_MESSAGE_HANDLE msgHandle, void* call else { // Will need to update this when the service has messages that can be rejected - if (extractMqttProperties(IoTHubMessage, topic_resp) != 0) + if (extractMqttProperties(IoTHubMessage, topic_resp, transportData->auto_url_encode_decode) != 0) { LogError("failure extracting mqtt properties."); } @@ -1338,7 +1436,11 @@ static void mqtt_operation_complete_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLI } else { - if (connack->returnCode == CONN_REFUSED_BAD_USERNAME_PASSWORD) + if (connack->returnCode == CONN_REFUSED_SERVER_UNAVAIL) + { + IoTHubClient_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED); + } + else if (connack->returnCode == CONN_REFUSED_BAD_USERNAME_PASSWORD || connack->returnCode == CONN_REFUSED_ID_REJECTED) { transport_data->isRecoverableError = false; IoTHubClient_LL_ConnectionStatusCallBack(transport_data->llClientHandle, IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL); @@ -1352,8 +1454,7 @@ static void mqtt_operation_complete_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLI transport_data->isRecoverableError = false; } LogError("Connection Not Accepted: 0x%x: %s", connack->returnCode, retrieve_mqtt_return_codes(connack->returnCode) ); - (void)mqtt_client_disconnect(transport_data->mqttClient, NULL, NULL); - transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_PENDING_CLOSE; transport_data->currPacketState = PACKET_TYPE_ERROR; } } @@ -1408,10 +1509,28 @@ static void mqtt_operation_complete_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLI } } +// Prior to creating a new connection, if we have an existing xioTransport we need to clear +// it now or else cached settings (especially TLS when communicating with HTTP proxies) will +// break reconnection attempt. +static void ResetConnectionIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_data) +{ + if (transport_data->xioTransport != NULL) + { + OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport); + set_saved_tls_options(transport_data, options); + + xio_destroy(transport_data->xioTransport); + transport_data->xioTransport = NULL; + } +} + static void DisconnectFromClient(PMQTTTRANSPORT_HANDLE_DATA transport_data) { - OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport); - set_saved_tls_options(transport_data, options); + if (!transport_data->isDestroyCalled) + { + OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport); + set_saved_tls_options(transport_data, options); + } (void)mqtt_client_disconnect(transport_data->mqttClient, NULL, NULL); xio_destroy(transport_data->xioTransport); @@ -1453,7 +1572,10 @@ static void mqtt_error_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_ERR break; } } - transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + if (transport_data->mqttClientStatus != MQTT_CLIENT_STATUS_PENDING_CLOSE) + { + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + } transport_data->currPacketState = PACKET_TYPE_ERROR; transport_data->device_twin_get_sent = false; if (transport_data->topic_MqttMessage != NULL) @@ -1604,44 +1726,36 @@ static int GetTransportProviderIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_ } else { - if (IoTHubClient_Auth_Get_Credential_Type(transport_data->authorization_module) == IOTHUB_CREDENTIAL_TYPE_X509_ECC) + if (transport_data->saved_tls_options != NULL) { - if (IoTHubClient_Auth_Set_xio_Certificate(transport_data->authorization_module, transport_data->xioTransport) != 0) + if (OptionHandler_FeedOptions(transport_data->saved_tls_options, transport_data->xioTransport) != OPTIONHANDLER_OK) { - LogError("Unable to create the lower level TLS layer."); + LogError("Failed feeding existing options to new TLS instance."); result = __FAILURE__; } else { + // The tlsio has the options, so our copy can be deleted + set_saved_tls_options(transport_data, NULL); result = 0; } } - else + else if (IoTHubClient_Auth_Get_Credential_Type(transport_data->authorization_module) == IOTHUB_CREDENTIAL_TYPE_X509_ECC) { - result = 0; - } - - if (result == 0) - { - if (transport_data->saved_tls_options != NULL) + if (IoTHubClient_Auth_Set_xio_Certificate(transport_data->authorization_module, transport_data->xioTransport) != 0) { - if (OptionHandler_FeedOptions(transport_data->saved_tls_options, transport_data->xioTransport) != OPTIONHANDLER_OK) - { - LogError("Failed feeding existing options to new TLS instance."); - result = __FAILURE__; - } - else - { - // The tlsio has the options, so our copy can be deleted - set_saved_tls_options(transport_data, NULL); - result = 0; - } + LogError("Unable to create the lower level TLS layer."); + result = __FAILURE__; } else { result = 0; } } + else + { + result = 0; + } } } else @@ -1651,34 +1765,34 @@ static int GetTransportProviderIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_ return result; } -static int is_key_validate(const IOTHUBTRANSPORT_CONFIG* config) -{ - int result; - IOTHUB_CREDENTIAL_TYPE cred_type = IoTHubClient_Auth_Get_Credential_Type(config->auth_module_handle); - if (cred_type == IOTHUB_CREDENTIAL_TYPE_X509 || cred_type == IOTHUB_CREDENTIAL_TYPE_X509_ECC) - { - result = 0; - } - else - { - if (config->upperConfig->deviceKey == NULL && config->upperConfig->deviceSasToken == NULL) - { - if (IoTHubClient_Auth_Get_DeviceKey(config->auth_module_handle) == NULL) - { - result = __FAILURE__; - } - else - { - result = 0; - } - } - else - { - result = 0; - } - } - return result; -} +//static int is_key_validate(const IOTHUBTRANSPORT_CONFIG* config) +//{ +// int result; +// IOTHUB_CREDENTIAL_TYPE cred_type = IoTHubClient_Auth_Get_Credential_Type(config->auth_module_handle); +// if (cred_type == IOTHUB_CREDENTIAL_TYPE_X509 || cred_type == IOTHUB_CREDENTIAL_TYPE_X509_ECC) +// { +// result = 0; +// } +// else +// { +// if (config->upperConfig->deviceKey == NULL && config->upperConfig->deviceSasToken == NULL) +// { +// if (IoTHubClient_Auth_Get_DeviceKey(config->auth_module_handle) == NULL) +// { +// result = __FAILURE__; +// } +// else +// { +// result = 0; +// } +// } +// else +// { +// result = 0; +// } +// } +// return result; +//} static int SendMqttConnectMsg(PMQTTTRANSPORT_HANDLE_DATA transport_data) { @@ -1820,6 +1934,8 @@ static int InitializeConnection(PMQTTTRANSPORT_HANDLE_DATA transport_data) } else { + ResetConnectionIfNecessary(transport_data); + if (SendMqttConnectMsg(transport_data) != 0) { transport_data->connectFailCount++; @@ -1846,7 +1962,7 @@ static int InitializeConnection(PMQTTTRANSPORT_HANDLE_DATA transport_data) { LogError("mqtt_client timed out waiting for CONNACK"); DisconnectFromClient(transport_data); - result = 0; + result = __FAILURE__; } } else if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_CONNECTED) @@ -2006,6 +2122,7 @@ static PMQTTTRANSPORT_HANDLE_DATA InitializeTransportHandleData(const IOTHUB_CLI state->authorization_module = auth_module; state->isProductInfoSet = false; state->option_sas_token_lifetime_secs = SAS_TOKEN_DEFAULT_LIFETIME; + state->auto_url_encode_decode = false; } } } @@ -2471,7 +2588,12 @@ void IoTHubTransport_MQTT_Common_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIEN } else { - if (transport_data->currPacketState == CONNACK_TYPE || transport_data->currPacketState == SUBSCRIBE_TYPE) + if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_PENDING_CLOSE) + { + mqtt_client_disconnect(transport_data->mqttClient, NULL, NULL); + transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_NOT_CONNECTED; + } + else if (transport_data->currPacketState == CONNACK_TYPE || transport_data->currPacketState == SUBSCRIBE_TYPE) { SubscribeToMqttProtocol(transport_data); } @@ -2661,6 +2783,11 @@ IOTHUB_CLIENT_RESULT IoTHubTransport_MQTT_Common_SetOption(TRANSPORT_LL_HANDLE h mqtt_client_set_trace(transport_data->mqttClient, transport_data->log_trace, transport_data->raw_trace); result = IOTHUB_CLIENT_OK; } + else if (strcmp(OPTION_AUTO_URL_ENCODE_DECODE, option) == 0) + { + transport_data->auto_url_encode_decode = *((bool*)value); + result = IOTHUB_CLIENT_OK; + } /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_052: [ If the option parameter is set to "sas_token_lifetime" then the value shall be a size_t_ptr and the value will determine the mqtt sas token lifetime.] */ else if (strcmp(OPTION_SAS_TOKEN_LIFETIME, option) == 0) { diff --git a/src/sdk/iothubtransporthttp.c b/src/sdk/iothubtransporthttp.c index a9118b4..90c36ca 100644 --- a/src/sdk/iothubtransporthttp.c +++ b/src/sdk/iothubtransporthttp.c @@ -541,7 +541,7 @@ static IOTHUB_DEVICE_HANDLE IoTHubTransportHttp_Register(TRANSPORT_LL_HANDLE han bool was_x509_ok = false; /*there's nothing "created" in the case of x509, it is a flag indicating that x509 is used*/ /*Codes_SRS_TRANSPORTMULTITHTTP_17_038: [ Otherwise, IoTHubTransportHttp_Register shall allocate the IOTHUB_DEVICE_HANDLE structure. ]*/ - bool was_resultCreated_ok = ((result = (HTTPTRANSPORT_PERDEVICE_DATA *) malloc(sizeof(HTTPTRANSPORT_PERDEVICE_DATA))) != NULL); + bool was_resultCreated_ok = ((result = (HTTPTRANSPORT_PERDEVICE_DATA *)malloc(sizeof(HTTPTRANSPORT_PERDEVICE_DATA))) != NULL); bool was_create_deviceId_ok = was_resultCreated_ok && create_deviceId(result, device->deviceId); if (was_create_deviceId_ok) @@ -610,7 +610,7 @@ static IOTHUB_DEVICE_HANDLE IoTHubTransportHttp_Register(TRANSPORT_LL_HANDLE han result->isFirstPoll = true; result->waitingToSend = waitingToSend; DList_InitializeListHead(&(result->eventConfirmations)); - result->transportHandle = (HTTPTRANSPORT_HANDLE_DATA *) handle; + result->transportHandle = (HTTPTRANSPORT_HANDLE_DATA *)handle; } else { @@ -655,7 +655,7 @@ static IOTHUB_DEVICE_HANDLE* get_perDeviceDataItem(IOTHUB_DEVICE_HANDLE deviceHa HTTPTRANSPORT_HANDLE_DATA* handleData = deviceHandleData->transportHandle; - listItem = (IOTHUB_DEVICE_HANDLE *) VECTOR_find_if(handleData->perDeviceList, findDeviceHandle, deviceHandle); + listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_find_if(handleData->perDeviceList, findDeviceHandle, deviceHandle); if (listItem == NULL) { LogError("device handle not found in transport device list"); @@ -702,7 +702,7 @@ static void IoTHubTransportHttp_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) return; } -/*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */ +/*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */ /*Codes_SRS_TRANSPORTMULTITHTTP_20_001: [If config->upperConfig->protocolGatewayHostName is not NULL, IoTHubTransportHttp_Create shall use it as hostname] */ static void destroy_hostName(HTTPTRANSPORT_HANDLE_DATA* handleData) { @@ -721,7 +721,7 @@ static bool create_hostName(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBT } else { - /*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */ + /*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */ handleData->hostName = STRING_construct(config->upperConfig->iotHubName); if (handleData->hostName == NULL) @@ -876,14 +876,14 @@ static void IoTHubTransportHttp_Destroy(TRANSPORT_LL_HANDLE handle) /*Codes_SRS_TRANSPORTMULTITHTTP_17_013: [ Otherwise, IoTHubTransportHttp_Destroy shall free all the resources currently in use. ]*/ for (size_t i = 0; i < deviceListSize; i++) { - listItem = (IOTHUB_DEVICE_HANDLE *) VECTOR_element(handleData->perDeviceList, i); + listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_element(handleData->perDeviceList, i); HTTPTRANSPORT_PERDEVICE_DATA* perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA*)(*listItem); destroy_perDeviceData(perDeviceItem); free(perDeviceItem); } - destroy_hostName((HTTPTRANSPORT_HANDLE_DATA *) handle); - destroy_httpApiExHandle((HTTPTRANSPORT_HANDLE_DATA *) handle); + destroy_hostName((HTTPTRANSPORT_HANDLE_DATA *)handle); + destroy_httpApiExHandle((HTTPTRANSPORT_HANDLE_DATA *)handle); destroy_perDeviceList((HTTPTRANSPORT_HANDLE_DATA *)handle); free(handle); } @@ -1030,7 +1030,7 @@ static int concat_Properties(STRING_HANDLE existing, MAP_HANDLE map, size_t* pro /*all is fine*/ size_t i; *propertiesMessageSizeContribution = 0; - for (i = 0;i < count;i++) + for (i = 0; i < count; i++) { /*Codes_SRS_TRANSPORTMULTITHTTP_17_063: [Every property name shall add to the message size the length of the property name + the length of the property value + 16 bytes.] */ *propertiesMessageSizeContribution += (strlen(keys[i]) + strlen(values[i]) + MAXIMUM_PROPERTY_OVERHEAD); @@ -1128,7 +1128,7 @@ static STRING_HANDLE make1EventJSONitem(PDLIST_ENTRY item, size_t *messageSizeCo } else { - size_t propertiesSize; + size_t propertiesSize = 0; if (!( (STRING_concat_with_STRING(result, encoded) == 0) && (STRING_concat(result, "\"") == 0) && /*\" because closing value*/ @@ -1180,7 +1180,7 @@ static STRING_HANDLE make1EventJSONitem(PDLIST_ENTRY item, size_t *messageSizeCo } else { - size_t propertiesSize; + size_t propertiesSize = 0; if (!( (STRING_concat_with_STRING(result, asJson) == 0) && (STRING_concat(result, ",\"base64Encoded\":false") == 0) && @@ -1405,7 +1405,7 @@ static void DoEvent(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVI &statusCode, NULL, NULL - ) != HTTPAPIEX_OK) + ) != HTTPAPIEX_OK) { LogError("unable to HTTPAPIEX_ExecuteRequest"); //items go back to waitingToSend @@ -1458,23 +1458,27 @@ static void DoEvent(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVI } else { - const unsigned char* messageContent=NULL; - size_t messageSize=0; - size_t originalMessageSize=0; + const unsigned char* messageContent = NULL; + size_t messageSize = 0; + size_t originalMessageSize = 0; IOTHUB_MESSAGE_LIST* message = containingRecord(deviceData->waitingToSend->Flink, IOTHUB_MESSAGE_LIST, entry); IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(message->messageHandle); /*Codes_SRS_TRANSPORTMULTITHTTP_17_073: [The message size is computed from the length of the payload + 384.]*/ if (!( (((contentType == IOTHUBMESSAGE_BYTEARRAY) && - (IoTHubMessage_GetByteArray(message->messageHandle, &messageContent, &originalMessageSize)==IOTHUB_MESSAGE_OK)) ? (messageSize= originalMessageSize + MAXIMUM_PAYLOAD_OVERHEAD, 1): 0) + (IoTHubMessage_GetByteArray(message->messageHandle, &messageContent, &originalMessageSize) == IOTHUB_MESSAGE_OK)) + ? ((void)(messageSize = originalMessageSize + MAXIMUM_PAYLOAD_OVERHEAD), 1) + : 0) || - ((contentType == IOTHUBMESSAGE_STRING) && ( - messageContent = (const unsigned char*)IoTHubMessage_GetString(message->messageHandle), - (messageSize = MAXIMUM_PAYLOAD_OVERHEAD + (originalMessageSize = ((messageContent == NULL)?0:strlen((const char*)messageContent)))), - messageContent!=NULL) + ((contentType == IOTHUBMESSAGE_STRING) && + ((void)(messageContent = (const unsigned char*)IoTHubMessage_GetString(message->messageHandle)), + ((void)(messageSize = MAXIMUM_PAYLOAD_OVERHEAD + (originalMessageSize = ((messageContent == NULL) + ? 0 + : strlen((const char*)messageContent))))), + messageContent != NULL) ) )) { @@ -1657,7 +1661,7 @@ static void DoEvent(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVI &statusCode, NULL, NULL - )) != HTTPAPIEX_OK) + )) != HTTPAPIEX_OK) { LogError("Unable to HTTPAPIEX_ExecuteRequest."); } @@ -1675,7 +1679,7 @@ static void DoEvent(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVI &statusCode, NULL, NULL - )) != HTTPAPIEX_OK) + )) != HTTPAPIEX_OK) { LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); } @@ -1824,7 +1828,7 @@ static bool abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTR &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */ NULL, /*- responseHeadearsHandle: NULL */ NULL /*- responseContent: NULL] */ - )) != HTTPAPIEX_OK) + )) != HTTPAPIEX_OK) { /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ @@ -1843,7 +1847,7 @@ static bool abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTR &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */ NULL, /*- responseHeadearsHandle: NULL */ NULL /*- responseContent: NULL] */ - )) != HTTPAPIEX_OK) + )) != HTTPAPIEX_OK) { /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/ /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */ @@ -2041,7 +2045,7 @@ static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERD &statusCode, /*statusCode: a pointer to unsigned int which shall be later examined*/ responseHTTPHeaders, /*responseHeadearsHandle: a new instance of HTTP headers*/ responseContent /*responseContent: a new instance of buffer*/ - )) != HTTPAPIEX_OK) + )) != HTTPAPIEX_OK) { /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */ LogError("Unable to HTTPAPIEX_ExecuteRequest."); @@ -2067,7 +2071,7 @@ static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERD &statusCode, /*statusCode: a pointer to unsigned int which shall be later examined*/ responseHTTPHeaders, /*responseHeadearsHandle: a new instance of HTTP headers*/ responseContent /*responseContent: a new instance of buffer*/ - )) != HTTPAPIEX_OK) + )) != HTTPAPIEX_OK) { /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */ LogError("unable to HTTPAPIEX_SAS_ExecuteRequest"); @@ -2200,7 +2204,7 @@ static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERD } } } - // Codes_SRS_TRANSPORTMULTITHTTP_09_003: [ The HTTP header value of `ContentType` shall be set in the `IoTHubMessage_SetContentTypeSystemProperty`.  ]   + // Codes_SRS_TRANSPORTMULTITHTTP_09_003: [ The HTTP header value of `ContentType` shall be set in the `IoTHubMessage_SetContentTypeSystemProperty`. ] else if (strncmp(IOTHUB_CONTENT_TYPE_C2D, completeHeader, strlen(IOTHUB_CONTENT_TYPE_C2D)) == 0) { char* whereIsColon = strchr(completeHeader, ':'); @@ -2215,7 +2219,7 @@ static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERD } } } - // Codes_SRS_TRANSPORTMULTITHTTP_09_004: [ The HTTP header value of `ContentEncoding` shall be set in the `IoTHub_SetContentEncoding`.  ]   + // Codes_SRS_TRANSPORTMULTITHTTP_09_004: [ The HTTP header value of `ContentEncoding` shall be set in the `IoTHub_SetContentEncoding`. ] else if (strncmp(IOTHUB_CONTENT_ENCODING_C2D, completeHeader, strlen(IOTHUB_CONTENT_ENCODING_C2D)) == 0) { char* whereIsColon = strchr(completeHeader, ':'); @@ -2319,7 +2323,7 @@ static void IoTHubTransportHttp_DoWork(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT /*Codes_SRS_TRANSPORTMULTITHTTP_17_051: [ IF the list is empty, then IoTHubTransportHttp_DoWork shall do nothing. ]*/ for (size_t i = 0; i < deviceListSize; i++) { - listItem = (IOTHUB_DEVICE_HANDLE *) VECTOR_element(handleData->perDeviceList, i); + listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_element(handleData->perDeviceList, i); HTTPTRANSPORT_PERDEVICE_DATA* perDeviceItem = *(HTTPTRANSPORT_PERDEVICE_DATA**)(listItem); DoEvent(handleData, perDeviceItem, perDeviceItem->iotHubClientHandle); DoMessages(handleData, perDeviceItem, perDeviceItem->iotHubClientHandle); diff --git a/src/sdk/methodreturn.c b/src/sdk/methodreturn.c index 9379756..1e0362f 100644 --- a/src/sdk/methodreturn.c +++ b/src/sdk/methodreturn.c @@ -19,12 +19,35 @@ typedef struct METHODRETURN_HANDLE_DATA_TAG METHODRETURN_DATA data; }METHODRETURN_HANDLE_DATA; +bool is_json_present_and_unparsable(const char* jsonValue) +{ + bool is_present_and_unparsable; + if (jsonValue == NULL) + { + // Null json is not considered invalid here + is_present_and_unparsable = false; + } + else + { + JSON_Value* temp = json_parse_string(jsonValue); + if (temp == NULL) + { + is_present_and_unparsable = true; + } + else + { + json_value_free(temp); + is_present_and_unparsable = false; + } + } + return is_present_and_unparsable; +} + METHODRETURN_HANDLE MethodReturn_Create(int statusCode, const char* jsonValue) { METHODRETURN_HANDLE result; - JSON_Value* temp; /*Codes_SRS_METHODRETURN_02_009: [ If jsonValue is not a JSON value then MethodReturn_Create shall fail and return NULL. ]*/ - if ((jsonValue != NULL) && ((temp = json_parse_string(jsonValue), ((temp != NULL) ? json_value_free(temp) : (void)NULL), temp) == NULL)) + if (is_json_present_and_unparsable(jsonValue)) { LogError("%s is not JSON", jsonValue); result = NULL; @@ -62,7 +85,7 @@ METHODRETURN_HANDLE MethodReturn_Create(int statusCode, const char* jsonValue) } } } - + return result; } diff --git a/src/sdk/multitree.c b/src/sdk/multitree.c index c821d8f..2553202 100644 --- a/src/sdk/multitree.c +++ b/src/sdk/multitree.c @@ -9,6 +9,7 @@ #include "azure_c_shared_utility/crt_abstractions.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/macro_utils.h" +#include "azure_c_shared_utility/const_defines.h" /*assume a name cannot be longer than 100 characters*/ #define INNER_NODE_NAME_SIZE 128 @@ -92,7 +93,7 @@ typedef enum CREATELEAF_RESULT_TAG // Do not remove, or add new enum values below this one }CREATELEAF_RESULT; -static const char* CreateLeaf_ResultAsString[CREATELEAF_RESULT_COUNT] = +static STATIC_VAR_UNUSED const char* CreateLeaf_ResultAsString[CREATELEAF_RESULT_COUNT] = { TOSTRING(CREATELEAF_OK), TOSTRING(CREATELEAF_ALREADY_EXISTS), @@ -101,6 +102,10 @@ static const char* CreateLeaf_ResultAsString[CREATELEAF_RESULT_COUNT] = }; /*name cannot be empty, value can be empty or NULL*/ +#ifdef __APPLE__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wconditional-uninitialized" +#endif #ifdef _MSC_VER #pragma warning(disable: 4701) /* potentially uninitialized local variable 'result' used */ /* the scanner cannot track linked "newNode" and "result" therefore the warning*/ #endif @@ -198,6 +203,9 @@ static CREATELEAF_RESULT createLeaf(MULTITREE_HANDLE_DATA* node, const char*name #ifdef _MSC_VER #pragma warning(default: 4701) /* potentially uninitialized local variable 'result' used */ /* the scanner cannot track linked "newNode" and "result" therefore the warning*/ #endif +#ifdef __APPLE__ +#pragma clang diagnostic pop +#endif } MULTITREE_RESULT MultiTree_AddLeaf(MULTITREE_HANDLE treeHandle, const char* destinationPath, const void* value) diff --git a/src/sdk/serializer.h b/src/sdk/serializer.h index cf5de11..f0d44e3 100644 --- a/src/sdk/serializer.h +++ b/src/sdk/serializer.h @@ -456,8 +456,8 @@ Actions are discarded, since no marshalling will be done for those when sending #define REFLECTED_DESIRED_PROPERTY(type, name, modelName, ...) \ IF(COUNT_ARG(__VA_ARGS__), \ - DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,__COUNTER__, __VA_ARGS__), \ - DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,DEC(__COUNTER__), NULL) \ + MACRO_UTILS_DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,__COUNTER__, __VA_ARGS__), \ + MACRO_UTILS_DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,DEC(__COUNTER__), NULL) \ ) \ #define REFLECTED_ACTION(name, argc, argv, fn, modelName) \ diff --git a/src/sdk/upload_to_blob.def b/src/sdk/upload_to_blob.def new file mode 100644 index 0000000..8c50176 --- /dev/null +++ b/src/sdk/upload_to_blob.def @@ -0,0 +1,4 @@ +LIBRARY iothub_client_dll +EXPORTS + IoTHubClient_UploadToBlobAsync + IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULTStrings \ No newline at end of file