Microsoft Azure IoTHub client libraries
Dependents: sht15_remote_monitoring RobotArmDemo iothub_client_sample_amqp f767zi_mqtt ... more
This library implements the Microsoft Azure IoTHub client library. The code is replicated from https://github.com/Azure/azure-iot-sdks
Diff: iothub_client.c
- Revision:
- 54:6dcad9019a64
- Parent:
- 53:1e5a1ca1f274
- Child:
- 55:59b527ab3452
--- a/iothub_client.c Wed Nov 16 21:37:53 2016 -0800 +++ b/iothub_client.c Wed Dec 14 15:59:51 2016 -0800 @@ -5,6 +5,7 @@ #ifdef _CRTDBG_MAP_ALLOC #include <crtdbg.h> #endif +#include "azure_c_shared_utility/umock_c_prod.h" #include "azure_c_shared_utility/gballoc.h" #include <stdlib.h> @@ -18,6 +19,7 @@ #include "azure_c_shared_utility/lock.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/singlylinkedlist.h" +#include "azure_c_shared_utility/vector.h" typedef struct IOTHUB_CLIENT_INSTANCE_TAG { @@ -29,6 +31,13 @@ #ifndef DONT_USE_UPLOADTOBLOB SINGLYLINKEDLIST_HANDLE savedDataToBeCleaned; /*list containing UPLOADTOBLOB_SAVED_DATA*/ #endif + int created_with_transport_handle; + VECTOR_HANDLE saved_user_callback_list; + IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK desired_state_callback; + IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK event_confirm_callback; + IOTHUB_CLIENT_REPORTED_STATE_CALLBACK reported_state_callback; + IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK connection_status_callback; + void* devicetwin_user_context; } IOTHUB_CLIENT_INSTANCE; #ifndef DONT_USE_UPLOADTOBLOB @@ -46,6 +55,65 @@ }UPLOADTOBLOB_SAVED_DATA; #endif +#define USER_CALLBACK_TYPE_VALUES \ + CALLBACK_TYPE_DEVICE_TWIN, \ + CALLBACK_TYPE_EVENT_CONFIRM, \ + CALLBACK_TYPE_REPORTED_STATE, \ + CALLBACK_TYPE_CONNECTION_STATUS + +DEFINE_ENUM(USER_CALLBACK_TYPE, USER_CALLBACK_TYPE_VALUES) + +typedef struct DEVICE_TWIN_CALLBACK_INFO_TAG +{ + DEVICE_TWIN_UPDATE_STATE update_state; + unsigned char* payLoad; + size_t size; +} DEVICE_TWIN_CALLBACK_INFO; + +typedef struct EVENT_CONFIRM_CALLBACK_INFO_TAG +{ + IOTHUB_CLIENT_CONFIRMATION_RESULT confirm_result; +} EVENT_CONFIRM_CALLBACK_INFO; + +typedef struct REPORTED_STATE_CALLBACK_INFO_TAG +{ + int status_code; +} REPORTED_STATE_CALLBACK_INFO; + +typedef struct CONNECTION_STATUS_CALLBACK_INFO_TAG +{ + IOTHUB_CLIENT_CONNECTION_STATUS connection_status; + IOTHUB_CLIENT_CONNECTION_STATUS_REASON status_reason; +} CONNECTION_STATUS_CALLBACK_INFO; + +typedef struct METHOD_CALLBACK_INFO_TAG +{ + const char* method_name; + const unsigned char* payload; + size_t size; + unsigned char* response; + size_t resp_size; +} METHOD_CALLBACK_INFO; + +typedef struct USER_CALLBACK_INFO_TAG +{ + USER_CALLBACK_TYPE type; + void* userContextCallback; + union IOTHUB_CALLBACK + { + DEVICE_TWIN_CALLBACK_INFO dev_twin_cb_info; + EVENT_CONFIRM_CALLBACK_INFO event_confirm_cb_info; + REPORTED_STATE_CALLBACK_INFO reported_state_cb_info; + CONNECTION_STATUS_CALLBACK_INFO connection_status_cb_info; + } iothub_callback; +} USER_CALLBACK_INFO; + +typedef struct IOTHUB_QUEUE_CONTEXT_TAG +{ + IOTHUB_CLIENT_INSTANCE* iotHubClientHandle; + void* userContextCallback; +} IOTHUB_QUEUE_CONTEXT; + /*used by unittests only*/ const size_t IoTHubClient_ThreadTerminationOffset = offsetof(IOTHUB_CLIENT_INSTANCE, StopThread); @@ -98,6 +166,187 @@ } #endif +static IOTHUBMESSAGE_DISPOSITION_RESULT iothub_ll_message_callback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + (void)message; + (void)userContextCallback; + return IOTHUBMESSAGE_ABANDONED; +} + +static int iothub_ll_method_callback(const char* method_name, const unsigned char* payload, size_t size, unsigned char** response, size_t* resp_size, void* userContextCallback) +{ + (void)method_name; + (void)payload; + (void)size; + (void)response; + (void)resp_size; + (void)userContextCallback; + return 0; +} + +static void iothub_ll_connection_status_callback(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_CONNECTION_STATUS; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.connection_status_cb_info.status_reason = reason; + queue_cb_info.iothub_callback.connection_status_cb_info.connection_status = result; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("connection status callback vector push failed."); + } + free(queue_context); + } +} + +static void iothub_ll_event_confirm_callback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_EVENT_CONFIRM; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.event_confirm_cb_info.confirm_result = result; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("event confirm callback vector push failed."); + } + free(queue_context); + } +} + +static void iothub_ll_reported_state_callback(int status_code, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_REPORTED_STATE; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.reported_state_cb_info.status_code = status_code; + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("reported state callback vector push failed."); + } + free(queue_context); + } +} + +static void iothub_ll_device_twin_callback(DEVICE_TWIN_UPDATE_STATE update_state, const unsigned char* payLoad, size_t size, void* userContextCallback) +{ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)userContextCallback; + if (queue_context != NULL) + { + USER_CALLBACK_INFO queue_cb_info; + queue_cb_info.type = CALLBACK_TYPE_DEVICE_TWIN; + queue_cb_info.userContextCallback = queue_context->userContextCallback; + queue_cb_info.iothub_callback.dev_twin_cb_info.update_state = update_state; + if (payLoad == NULL) + { + queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad = NULL; + queue_cb_info.iothub_callback.dev_twin_cb_info.size = 0; + } + else + { + queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad = (unsigned char*)malloc(size); + if (queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad == NULL) + { + LogError("failure allocating payload in device twin callback."); + queue_cb_info.iothub_callback.dev_twin_cb_info.size = 0; + } + else + { + memcpy(queue_cb_info.iothub_callback.dev_twin_cb_info.payLoad, payLoad, size); + queue_cb_info.iothub_callback.dev_twin_cb_info.size = size; + } + } + if (VECTOR_push_back(queue_context->iotHubClientHandle->saved_user_callback_list, &queue_cb_info, 1) != 0) + { + LogError("device twin callback userContextCallback vector push failed."); + } + } + else + { + LogError("device twin callback userContextCallback NULL"); + } +} + +static void dispatch_user_callbacks(IOTHUB_CLIENT_INSTANCE* iotHubClientInstance) +{ + if (Lock(iotHubClientInstance->LockHandle) == LOCK_OK) + { + size_t callbacks_length = VECTOR_size(iotHubClientInstance->saved_user_callback_list); + for (size_t index = 0; index < callbacks_length; index++) + { + USER_CALLBACK_INFO* queued_cb = (USER_CALLBACK_INFO*)VECTOR_element(iotHubClientInstance->saved_user_callback_list, index); + if (queued_cb != NULL) + { + switch (queued_cb->type) + { + case CALLBACK_TYPE_DEVICE_TWIN: + if (iotHubClientInstance->desired_state_callback) + { + (void)Unlock(iotHubClientInstance->LockHandle); + iotHubClientInstance->desired_state_callback(queued_cb->iothub_callback.dev_twin_cb_info.update_state, queued_cb->iothub_callback.dev_twin_cb_info.payLoad, queued_cb->iothub_callback.dev_twin_cb_info.size, queued_cb->userContextCallback); + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("Failed locking desired callback"); + } + } + if (queued_cb->iothub_callback.dev_twin_cb_info.payLoad) + { + free(queued_cb->iothub_callback.dev_twin_cb_info.payLoad); + } + break; + case CALLBACK_TYPE_EVENT_CONFIRM: + if (iotHubClientInstance->event_confirm_callback) + { + (void)Unlock(iotHubClientInstance->LockHandle); + iotHubClientInstance->event_confirm_callback(queued_cb->iothub_callback.event_confirm_cb_info.confirm_result, queued_cb->userContextCallback); + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("Failed locking after event confirm callback"); + } + } + break; + case CALLBACK_TYPE_REPORTED_STATE: + if (iotHubClientInstance->reported_state_callback) + { + (void)Unlock(iotHubClientInstance->LockHandle); + iotHubClientInstance->reported_state_callback(queued_cb->iothub_callback.reported_state_cb_info.status_code, queued_cb->userContextCallback); + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("Failed locking after reported state callback"); + } + } + break; + case CALLBACK_TYPE_CONNECTION_STATUS: + if (iotHubClientInstance->connection_status_callback) + { + (void)Unlock(iotHubClientInstance->LockHandle); + iotHubClientInstance->connection_status_callback(queued_cb->iothub_callback.connection_status_cb_info.connection_status, queued_cb->iothub_callback.connection_status_cb_info.status_reason, queued_cb->userContextCallback); + if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK) + { + LogError("Failed locking after connection status callback"); + } + } + break; + } + } + } + VECTOR_clear(iotHubClientInstance->saved_user_callback_list); + (void)Unlock(iotHubClientInstance->LockHandle); + } + else + { + LogError("Unable to aquire lock"); + } +} + static int ScheduleWork_Thread(void* threadArgument) { IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)threadArgument; @@ -129,6 +378,7 @@ /*Codes_SRS_IOTHUBCLIENT_01_040: [If acquiring the lock fails, IoTHubClient_LL_DoWork shall not be called.]*/ /*no code, shall retry*/ } + dispatch_user_callbacks(iotHubClientInstance); (void)ThreadAPI_Sleep(1); } @@ -162,101 +412,22 @@ { /*Codes_SRS_IOTHUBCLIENT_17_012: [ If the transport connection is shared, the thread shall be started by calling IoTHubTransport_StartWorkerThread. ]*/ /*Codes_SRS_IOTHUBCLIENT_17_011: [ If the transport connection is shared, the thread shall be started by calling IoTHubTransport_StartWorkerThread*/ - result = IoTHubTransport_StartWorkerThread(iotHubClientInstance->TransportHandle, iotHubClientInstance); } return result; } -IOTHUB_CLIENT_HANDLE IoTHubClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +static IOTHUB_CLIENT_INSTANCE* create_iothub_instance(const IOTHUB_CLIENT_CONFIG* config, TRANSPORT_HANDLE transportHandle, const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) { - IOTHUB_CLIENT_INSTANCE* result = NULL; - - /* Codes_SRS_IOTHUBCLIENT_12_003: [IoTHubClient_CreateFromConnectionString shall verify the input parameter and if it is NULL then return NULL] */ - if (connectionString == NULL) - { - LogError("Input parameter is NULL: connectionString"); - } - else if (protocol == NULL) - { - LogError("Input parameter is NULL: protocol"); - } - else - { - /* Codes_SRS_IOTHUBCLIENT_12_004: [IoTHubClient_CreateFromConnectionString shall allocate a new IoTHubClient instance.] */ - result = (IOTHUB_CLIENT_INSTANCE *) malloc(sizeof(IOTHUB_CLIENT_INSTANCE)); - - /* Codes_SRS_IOTHUBCLIENT_12_011: [If the allocation failed, IoTHubClient_CreateFromConnectionString returns NULL] */ - if (result == NULL) - { - LogError("Malloc failed"); - } - else - { - /* Codes_SRS_IOTHUBCLIENT_12_005: [IoTHubClient_CreateFromConnectionString shall create a lock object to be used later for serializing IoTHubClient calls] */ - result->LockHandle = Lock_Init(); - if (result->LockHandle == NULL) - { - /* Codes_SRS_IOTHUBCLIENT_12_009: [If lock creation failed, IoTHubClient_CreateFromConnectionString shall do clean up and return NULL] */ - free(result); - result = NULL; - LogError("Lock_Init failed"); - } - else - { -#ifndef DONT_USE_UPLOADTOBLOB - /*Codes_SRS_IOTHUBCLIENT_02_059: [ IoTHubClient_CreateFromConnectionString shall create a SINGLYLINKEDLIST_HANDLE containing THREAD_HANDLE (created by future calls to IoTHubClient_UploadToBlobAsync). ]*/ - if ((result->savedDataToBeCleaned = singlylinkedlist_create()) == NULL) - { - /*Codes_SRS_IOTHUBCLIENT_02_070: [ If creating the SINGLYLINKEDLIST_HANDLE fails then IoTHubClient_CreateFromConnectionString shall fail and return NULL]*/ - LogError("unable to singlylinkedlist_create"); - Lock_Deinit(result->LockHandle); - free(result); - result = NULL; - } - else -#endif - { - /* Codes_SRS_IOTHUBCLIENT_12_006: [IoTHubClient_CreateFromConnectionString shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_CreateFromConnectionString and passing the connectionString] */ - result->IoTHubClientLLHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, protocol); - if (result->IoTHubClientLLHandle == NULL) - { - /* Codes_SRS_IOTHUBCLIENT_12_010: [If IoTHubClient_LL_CreateFromConnectionString fails then IoTHubClient_CreateFromConnectionString shall do clean - up and return NULL] */ -#ifndef DONT_USE_UPLOADTOBLOB - singlylinkedlist_destroy(result->savedDataToBeCleaned); -#endif - Lock_Deinit(result->LockHandle); - free(result); - result = NULL; - LogError("IoTHubClient_LL_CreateFromConnectionString failed"); - } - else - { - result->ThreadHandle = NULL; - result->TransportHandle = NULL; - } - } - } - - } - } - return result; -} - -IOTHUB_CLIENT_HANDLE IoTHubClient_Create(const IOTHUB_CLIENT_CONFIG* config) -{ - /* Codes_SRS_IOTHUBCLIENT_01_001: [IoTHubClient_Create shall allocate a new IoTHubClient instance and return a non-NULL handle to it.] */ IOTHUB_CLIENT_INSTANCE* result = (IOTHUB_CLIENT_INSTANCE*)malloc(sizeof(IOTHUB_CLIENT_INSTANCE)); /* Codes_SRS_IOTHUBCLIENT_01_004: [If allocating memory for the new IoTHubClient instance fails, then IoTHubClient_Create shall return NULL.] */ if (result != NULL) { /* Codes_SRS_IOTHUBCLIENT_01_029: [IoTHubClient_Create shall create a lock object to be used later for serializing IoTHubClient calls.] */ - result->LockHandle = Lock_Init(); - if (result->LockHandle == NULL) + if ( (result->saved_user_callback_list = VECTOR_create(sizeof(USER_CALLBACK_INFO)) ) == NULL) { - /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */ - /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + LogError("Failed creating VECTOR"); free(result); result = NULL; } @@ -268,35 +439,161 @@ { /*Codes_SRS_IOTHUBCLIENT_02_061: [ If creating the SINGLYLINKEDLIST_HANDLE fails then IoTHubClient_Create shall fail and return NULL. ]*/ LogError("unable to singlylinkedlist_create"); - Lock_Deinit(result->LockHandle); + VECTOR_destroy(result->saved_user_callback_list); free(result); result = NULL; } else #endif { - /* Codes_SRS_IOTHUBCLIENT_01_002: [IoTHubClient_Create shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_Create and passing the config argument.] */ - result->IoTHubClientLLHandle = IoTHubClient_LL_Create(config); + result->TransportHandle = transportHandle; + result->created_with_transport_handle = 0; + if (config != NULL) + { + if (transportHandle != NULL) + { + /*Codes_SRS_IOTHUBCLIENT_17_005: [ IoTHubClient_CreateWithTransport shall call IoTHubTransport_GetLock to get the transport lock to be used later for serializing IoTHubClient calls. ]*/ + result->LockHandle = IoTHubTransport_GetLock(transportHandle); + if (result->LockHandle == NULL) + { + LogError("unable to IoTHubTransport_GetLock"); + result->IoTHubClientLLHandle = NULL; + } + else + { + IOTHUB_CLIENT_DEVICE_CONFIG deviceConfig; + deviceConfig.deviceId = config->deviceId; + deviceConfig.deviceKey = config->deviceKey; + deviceConfig.protocol = config->protocol; + deviceConfig.deviceSasToken = config->deviceSasToken; + + /*Codes_SRS_IOTHUBCLIENT_17_003: [ IoTHubClient_CreateWithTransport shall call IoTHubTransport_GetLLTransport on transportHandle to get lower layer transport. ]*/ + deviceConfig.transportHandle = IoTHubTransport_GetLLTransport(transportHandle); + if (deviceConfig.transportHandle == NULL) + { + LogError("unable to IoTHubTransport_GetLLTransport"); + result->IoTHubClientLLHandle = NULL; + } + else + { + if (Lock(result->LockHandle) != LOCK_OK) + { + LogError("unable to Lock"); + result->IoTHubClientLLHandle = NULL; + } + else + { + /*Codes_SRS_IOTHUBCLIENT_17_007: [ IoTHubClient_CreateWithTransport shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_CreateWithTransport and passing the lower layer transport and config argument. ]*/ + result->IoTHubClientLLHandle = IoTHubClient_LL_CreateWithTransport(&deviceConfig); + result->created_with_transport_handle = 1; + if (Unlock(result->LockHandle) != LOCK_OK) + { + LogError("unable to Unlock"); + result->IoTHubClientLLHandle = NULL; + } + } + } + } + } + else + { + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */ + /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + LogError("Failure creating Lock object"); + result->IoTHubClientLLHandle = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_01_002: [IoTHubClient_Create shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_Create and passing the config argument.] */ + result->IoTHubClientLLHandle = IoTHubClient_LL_Create(config); + } + } + } + else + { + result->LockHandle = Lock_Init(); + if (result->LockHandle == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */ + /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ + LogError("Failure creating Lock object"); + result->IoTHubClientLLHandle = NULL; + } + else + { + /* Codes_SRS_IOTHUBCLIENT_12_006: [IoTHubClient_CreateFromConnectionString shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_CreateFromConnectionString and passing the connectionString] */ + result->IoTHubClientLLHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, protocol); + } + } + if (result->IoTHubClientLLHandle == NULL) { /* Codes_SRS_IOTHUBCLIENT_01_003: [If IoTHubClient_LL_Create fails, then IoTHubClient_Create shall return NULL.] */ /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */ - Lock_Deinit(result->LockHandle); + /* Codes_SRS_IOTHUBCLIENT_17_006: [ If IoTHubTransport_GetLock fails, then IoTHubClient_CreateWithTransport shall return NULL. ]*/ + if (transportHandle == NULL) + { + Lock_Deinit(result->LockHandle); + } #ifndef DONT_USE_UPLOADTOBLOB singlylinkedlist_destroy(result->savedDataToBeCleaned); #endif + LogError("Failure creating iothub handle"); + VECTOR_destroy(result->saved_user_callback_list); free(result); result = NULL; } else { - result->TransportHandle = NULL; result->ThreadHandle = NULL; + result->desired_state_callback = NULL; + result->event_confirm_callback = NULL; + result->reported_state_callback = NULL; + result->devicetwin_user_context = NULL; } } } } + return result; +} +IOTHUB_CLIENT_HANDLE IoTHubClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) +{ + IOTHUB_CLIENT_INSTANCE* result; + + /* Codes_SRS_IOTHUBCLIENT_12_003: [IoTHubClient_CreateFromConnectionString shall verify all input parameters and if any is NULL then return NULL] */ + if (connectionString == NULL) + { + LogError("Input parameter is NULL: connectionString"); + result = NULL; + } + else if (protocol == NULL) + { + LogError("Input parameter is NULL: protocol"); + result = NULL; + } + else + { + result = create_iothub_instance(NULL, NULL, connectionString, protocol); + } + return result; +} + +IOTHUB_CLIENT_HANDLE IoTHubClient_Create(const IOTHUB_CLIENT_CONFIG* config) +{ + IOTHUB_CLIENT_INSTANCE* result; + if (config == NULL) + { + LogError("Input parameter is NULL: IOTHUB_CLIENT_CONFIG"); + result = NULL; + } + else + { + result = create_iothub_instance(config, NULL, NULL, NULL); + } return result; } @@ -312,104 +609,8 @@ } else { - /*Codes_SRS_IOTHUBCLIENT_17_001: [ IoTHubClient_CreateWithTransport shall allocate a new IoTHubClient instance and return a non-NULL handle to it. ]*/ - result = (IOTHUB_CLIENT_INSTANCE*)malloc(sizeof(IOTHUB_CLIENT_INSTANCE)); - /*Codes_SRS_IOTHUBCLIENT_17_002: [ If allocating memory for the new IoTHubClient instance fails, then IoTHubClient_CreateWithTransport shall return NULL. ]*/ - if (result == NULL) - { - LogError("unable to malloc"); - /*return as is*/ - } - else - { -#ifndef DONT_USE_UPLOADTOBLOB - /*Codes_SRS_IOTHUBCLIENT_02_073: [ IoTHubClient_CreateWithTransport shall create a SINGLYLINKEDLIST_HANDLE that shall be used by IoTHubClient_UploadToBlobAsync. ]*/ - if ((result->savedDataToBeCleaned = singlylinkedlist_create()) == NULL) - { - /*Codes_SRS_IOTHUBCLIENT_02_074: [ If creating the SINGLYLINKEDLIST_HANDLE fails then IoTHubClient_CreateWithTransport shall fail and return NULL. ]*/ - LogError("unable to singlylinkedlist_create"); - free(result); - result = NULL; - } - else -#endif - { - result->ThreadHandle = NULL; - result->TransportHandle = transportHandle; - /*Codes_SRS_IOTHUBCLIENT_17_005: [ IoTHubClient_CreateWithTransport shall call IoTHubTransport_GetLock to get the transport lock to be used later for serializing IoTHubClient calls. ]*/ - LOCK_HANDLE transportLock = IoTHubTransport_GetLock(transportHandle); - result->LockHandle = transportLock; - if (result->LockHandle == NULL) - { - LogError("unable to IoTHubTransport_GetLock"); - /*Codes_SRS_IOTHUBCLIENT_17_006: [ If IoTHubTransport_GetLock fails, then IoTHubClient_CreateWithTransport shall return NULL. ]*/ -#ifndef DONT_USE_UPLOADTOBLOB - singlylinkedlist_destroy(result->savedDataToBeCleaned); -#endif - free(result); - result = NULL; - } - else - { - IOTHUB_CLIENT_DEVICE_CONFIG deviceConfig; - deviceConfig.deviceId = config->deviceId; - deviceConfig.deviceKey = config->deviceKey; - deviceConfig.protocol = config->protocol; - deviceConfig.deviceSasToken = config->deviceSasToken; - deviceConfig.protocol = config->protocol; - - /*Codes_SRS_IOTHUBCLIENT_17_003: [ IoTHubClient_CreateWithTransport shall call IoTHubTransport_GetLLTransport on transportHandle to get lower layer transport. ]*/ - deviceConfig.transportHandle = IoTHubTransport_GetLLTransport(transportHandle); - - if (deviceConfig.transportHandle == NULL) - { - LogError("unable to IoTHubTransport_GetLLTransport"); - /*Codes_SRS_IOTHUBCLIENT_17_004: [ If IoTHubTransport_GetLLTransport fails, then IoTHubClient_CreateWithTransport shall return NULL. ]*/ -#ifndef DONT_USE_UPLOADTOBLOB - singlylinkedlist_destroy(result->savedDataToBeCleaned); -#endif - free(result); - result = NULL; - } - else - { - if (Lock(transportLock) != LOCK_OK) - { - LogError("unable to Lock"); -#ifndef DONT_USE_UPLOADTOBLOB - singlylinkedlist_destroy(result->savedDataToBeCleaned); -#endif - free(result); - result = NULL; - } - else - { - /*Codes_SRS_IOTHUBCLIENT_17_007: [ IoTHubClient_CreateWithTransport shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_CreateWithTransport and passing the lower layer transport and config argument. ]*/ - result->IoTHubClientLLHandle = IoTHubClient_LL_CreateWithTransport(&deviceConfig); - if (result->IoTHubClientLLHandle == NULL) - { - LogError("unable to IoTHubClient_LL_CreateWithTransport"); - /*Codes_SRS_IOTHUBCLIENT_17_008: [ If IoTHubClient_LL_CreateWithTransport fails, then IoTHubClient_Create shall return NULL. ]*/ - /*Codes_SRS_IOTHUBCLIENT_17_009: [ If IoTHubClient_LL_CreateWithTransport fails, all resources allocated by it shall be freed. ]*/ -#ifndef DONT_USE_UPLOADTOBLOB - singlylinkedlist_destroy(result->savedDataToBeCleaned); -#endif - free(result); - result = NULL; - } - - if (Unlock(transportLock) != LOCK_OK) - { - LogError("unable to Unlock"); - } - } - } - } - } - } - + result = create_iothub_instance(config, transportHandle, NULL, NULL); } - return result; } @@ -480,6 +681,7 @@ LogError("ThreadAPI_Join failed"); } } + if (iotHubClientInstance->TransportHandle != NULL) { /*Codes_SRS_IOTHUBCLIENT_01_007: [ The thread created as part of executing IoTHubClient_SendEventAsync or IoTHubClient_SetNotificationMessageCallback shall be joined. ]*/ @@ -487,12 +689,17 @@ } } + VECTOR_destroy(iotHubClientInstance->saved_user_callback_list); + if (iotHubClientInstance->TransportHandle == NULL) { /* Codes_SRS_IOTHUBCLIENT_01_032: [If the lock was allocated in IoTHubClient_Create, it shall be also freed..] */ Lock_Deinit(iotHubClientInstance->LockHandle); } - + if (iotHubClientInstance->devicetwin_user_context != NULL) + { + free(iotHubClientInstance->devicetwin_user_context); + } free(iotHubClientInstance); } } @@ -520,6 +727,10 @@ } else { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->event_confirm_callback = eventConfirmationCallback; + } /* Codes_SRS_IOTHUBCLIENT_01_009: [IoTHubClient_SendEventAsync shall start the worker thread if it was not previously started.] */ if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) { @@ -529,9 +740,33 @@ } else { - /* Codes_SRS_IOTHUBCLIENT_01_012: [IoTHubClient_SendEventAsync shall call IoTHubClient_LL_SendEventAsync, while passing the IoTHubClient_LL handle created by IoTHubClient_Create and the parameters eventMessageHandle, eventConfirmationCallback and userContextCallback.] */ - /* Codes_SRS_IOTHUBCLIENT_01_013: [When IoTHubClient_LL_SendEventAsync is called, IoTHubClient_SendEventAsync shall return the result of IoTHubClient_LL_SendEventAsync.] */ - result = IoTHubClient_LL_SendEventAsync(iotHubClientInstance->IoTHubClientLLHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); + if (iotHubClientInstance->created_with_transport_handle != 0 || eventConfirmationCallback == NULL) + { + result = IoTHubClient_LL_SendEventAsync(iotHubClientInstance->IoTHubClientLLHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback); + } + else + { + /* Codes_SRS_IOTHUBCLIENT_07_001: [ IoTHubClient_SendEventAsync shall allocate a IOTHUB_QUEUE_CONTEXT object to be sent to the IoTHubClient_LL_SendEventAsync function as a user context. ] */ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (queue_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + queue_context->iotHubClientHandle = iotHubClientInstance; + queue_context->userContextCallback = userContextCallback; + /* Codes_SRS_IOTHUBCLIENT_01_012: [IoTHubClient_SendEventAsync shall call IoTHubClient_LL_SendEventAsync, while passing the IoTHubClient_LL handle created by IoTHubClient_Create and the parameters eventMessageHandle, eventConfirmationCallback and userContextCallback.] */ + /* Codes_SRS_IOTHUBCLIENT_01_013: [When IoTHubClient_LL_SendEventAsync is called, IoTHubClient_SendEventAsync shall return the result of IoTHubClient_LL_SendEventAsync.] */ + result = IoTHubClient_LL_SendEventAsync(iotHubClientInstance->IoTHubClientLLHandle, eventMessageHandle, iothub_ll_event_confirm_callback, queue_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClient_LL_SendEventAsync failed"); + free(queue_context); + } + } + } } /* Codes_SRS_IOTHUBCLIENT_01_025: [IoTHubClient_SendEventAsync shall be made thread-safe by using the lock created in IoTHubClient_Create.] */ @@ -644,6 +879,10 @@ } else { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->connection_status_callback = connectionStatusCallback; + } /* Codes_SRS_IOTHUBCLIENT_25_081: [ `IoTHubClient_SetConnectionStatusCallback` shall start the worker thread if it was not previously started. ]*/ if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) { @@ -653,16 +892,38 @@ } else { - /* Codes_SRS_IOTHUBCLIENT_25_085: [ `IoTHubClient_SetConnectionStatusCallback` shall call `IoTHubClient_LL_SetConnectionStatusCallback`, while passing the `IoTHubClient_LL` handle created by `IoTHubClient_Create` and the parameters `connectionStatusCallback` and `userContextCallback`. ]*/ - result = IoTHubClient_LL_SetConnectionStatusCallback(iotHubClientInstance->IoTHubClientLLHandle, connectionStatusCallback, userContextCallback); + if (iotHubClientInstance->created_with_transport_handle != 0 || connectionStatusCallback == NULL) + { + /* Codes_SRS_IOTHUBCLIENT_25_085: [ `IoTHubClient_SetConnectionStatusCallback` shall call `IoTHubClient_LL_SetConnectionStatusCallback`, while passing the `IoTHubClient_LL` handle created by `IoTHubClient_Create` and the parameters `connectionStatusCallback` and `userContextCallback`. ]*/ + result = IoTHubClient_LL_SetConnectionStatusCallback(iotHubClientInstance->IoTHubClientLLHandle, connectionStatusCallback, userContextCallback); + } + else + { + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (queue_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + queue_context->iotHubClientHandle = iotHubClientInstance; + queue_context->userContextCallback = userContextCallback; + + /* Codes_SRS_IOTHUBCLIENT_25_085: [ `IoTHubClient_SetConnectionStatusCallback` shall call `IoTHubClient_LL_SetConnectionStatusCallback`, while passing the `IoTHubClient_LL` handle created by `IoTHubClient_Create` and the parameters `connectionStatusCallback` and `userContextCallback`. ]*/ + result = IoTHubClient_LL_SetConnectionStatusCallback(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_connection_status_callback, queue_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClient_LL_SetConnectionStatusCallback failed"); + free(queue_context); + } + } + } } - (void)Unlock(iotHubClientInstance->LockHandle); } } - return result; - } IOTHUB_CLIENT_RESULT IoTHubClient_SetRetryPolicy(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds) @@ -708,7 +969,7 @@ return result; } -IOTHUB_CLIENT_RESULT IoTHubClient_GetRetryPolicy(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY * retryPolicy, size_t * retryTimeoutLimitInSeconds) +IOTHUB_CLIENT_RESULT IoTHubClient_GetRetryPolicy(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY* retryPolicy, size_t* retryTimeoutLimitInSeconds) { IOTHUB_CLIENT_RESULT result; @@ -850,6 +1111,10 @@ } else { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->desired_state_callback = deviceTwinCallback; + } /*Codes_SRS_IOTHUBCLIENT_10_003: [** If the transport connection is shared, the thread shall be started by calling `IoTHubTransport_StartWorkerThread`. ]*/ if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) { @@ -859,14 +1124,40 @@ } else { - /*Codes_SRS_IOTHUBCLIENT_10_005: [** `IoTHubClient_LL_SetDeviceTwinCallback` shall call `IoTHubClient_LL_SetDeviceTwinCallback`, while passing the `IoTHubClient_LL handle` created by `IoTHubClient_LL_Create` along with the parameters `reportedStateCallback` and `userContextCallback`. ]*/ - result = IoTHubClient_LL_SetDeviceTwinCallback(iotHubClientInstance->IoTHubClientLLHandle, deviceTwinCallback, userContextCallback); - if (result != IOTHUB_CLIENT_OK) + if (iotHubClientInstance->created_with_transport_handle != 0 || deviceTwinCallback == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_10_005: [** `IoTHubClient_LL_SetDeviceTwinCallback` shall call `IoTHubClient_LL_SetDeviceTwinCallback`, while passing the `IoTHubClient_LL handle` created by `IoTHubClient_LL_Create` along with the parameters `reportedStateCallback` and `userContextCallback`. ]*/ + result = IoTHubClient_LL_SetDeviceTwinCallback(iotHubClientInstance->IoTHubClientLLHandle, deviceTwinCallback, userContextCallback); + } + else { - LogError("IoTHubClient_LL_SetDeviceTwinCallback failed"); + if (iotHubClientInstance->devicetwin_user_context != NULL) + { + free(iotHubClientInstance->devicetwin_user_context); + } + + /*Codes_SRS_IOTHUBCLIENT_07_002: [ IoTHubClient_SetDeviceTwinCallback shall allocate a IOTHUB_QUEUE_CONTEXT object to be sent to the IoTHubClient_LL_SetDeviceTwinCallback function as a user context. ]*/ + iotHubClientInstance->devicetwin_user_context = malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (iotHubClientInstance->devicetwin_user_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)iotHubClientInstance->devicetwin_user_context; + /*Codes_SRS_IOTHUBCLIENT_10_005: [** `IoTHubClient_LL_SetDeviceTwinCallback` shall call `IoTHubClient_LL_SetDeviceTwinCallback`, while passing the `IoTHubClient_LL handle` created by `IoTHubClient_LL_Create` along with the parameters `iothub_ll_device_twin_callback` and IOTHUB_QUEUE_CONTEXT variable. ]*/ + queue_context->iotHubClientHandle = iotHubClientInstance; + queue_context->userContextCallback = userContextCallback; + result = IoTHubClient_LL_SetDeviceTwinCallback(iotHubClientInstance->IoTHubClientLLHandle, iothub_ll_device_twin_callback, iotHubClientInstance->devicetwin_user_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClient_LL_SetDeviceTwinCallback failed"); + free(iotHubClientInstance->devicetwin_user_context); + } + } } } - (void)Unlock(iotHubClientInstance->LockHandle); } } @@ -896,6 +1187,10 @@ } else { + if (iotHubClientInstance->created_with_transport_handle == 0) + { + iotHubClientInstance->reported_state_callback = reportedStateCallback; + } /*Codes_SRS_IOTHUBCLIENT_10_015: [** If the transport connection is shared, the thread shall be started by calling `IoTHubTransport_StartWorkerThread`. ]*/ if ((result = StartWorkerThreadIfNeeded(iotHubClientInstance)) != IOTHUB_CLIENT_OK) { @@ -905,15 +1200,36 @@ } else { - /*Codes_SRS_IOTHUBCLIENT_10_017: [** `IoTHubClient_SendReportedState` shall call `IoTHubClient_LL_SendReportedState`, while passing the `IoTHubClient_LL handle` created by `IoTHubClient_LL_Create` along with the parameters `reportedState`, `size`, `reportedStateCallback`, and `userContextCallback`. ]*/ - /*Codes_SRS_IOTHUBCLIENT_10_018: [** When `IoTHubClient_LL_SendReportedState` is called, `IoTHubClient_SendReportedState` shall return the result of `IoTHubClient_LL_SendReportedState`. **]*/ - result = IoTHubClient_LL_SendReportedState(iotHubClientInstance->IoTHubClientLLHandle, reportedState, size, reportedStateCallback, userContextCallback); - if (result != IOTHUB_CLIENT_OK) + if (iotHubClientInstance->created_with_transport_handle != 0 || reportedStateCallback == NULL) + { + /*Codes_SRS_IOTHUBCLIENT_10_017: [** `IoTHubClient_SendReportedState` shall call `IoTHubClient_LL_SendReportedState`, while passing the `IoTHubClient_LL handle` created by `IoTHubClient_LL_Create` along with the parameters `reportedState`, `size`, `reportedStateCallback`, and `userContextCallback`. ]*/ + /*Codes_SRS_IOTHUBCLIENT_10_018: [** When `IoTHubClient_LL_SendReportedState` is called, `IoTHubClient_SendReportedState` shall return the result of `IoTHubClient_LL_SendReportedState`. **]*/ + result = IoTHubClient_LL_SendReportedState(iotHubClientInstance->IoTHubClientLLHandle, reportedState, size, reportedStateCallback, userContextCallback); + } + else { - LogError("IoTHubClient_LL_SendReportedState failed"); + /* Codes_SRS_IOTHUBCLIENT_07_003: [ IoTHubClient_SendReportedState shall allocate a IOTHUB_QUEUE_CONTEXT object to be sent to the IoTHubClient_LL_SendReportedState function as a user context. ] */ + IOTHUB_QUEUE_CONTEXT* queue_context = (IOTHUB_QUEUE_CONTEXT*)malloc(sizeof(IOTHUB_QUEUE_CONTEXT)); + if (queue_context == NULL) + { + result = IOTHUB_CLIENT_ERROR; + LogError("Failed allocating QUEUE_CONTEXT"); + } + else + { + queue_context->iotHubClientHandle = iotHubClientInstance; + queue_context->userContextCallback = userContextCallback; + /*Codes_SRS_IOTHUBCLIENT_10_017: [** `IoTHubClient_SendReportedState` shall call `IoTHubClient_LL_SendReportedState`, while passing the `IoTHubClient_LL handle` created by `IoTHubClient_LL_Create` along with the parameters `reportedState`, `size`, `iothub_ll_reported_state_callback` and IOTHUB_QUEUE_CONTEXT variable. ]*/ + /*Codes_SRS_IOTHUBCLIENT_10_018: [** When `IoTHubClient_LL_SendReportedState` is called, `IoTHubClient_SendReportedState` shall return the result of `IoTHubClient_LL_SendReportedState`. **]*/ + result = IoTHubClient_LL_SendReportedState(iotHubClientInstance->IoTHubClientLLHandle, reportedState, size, iothub_ll_reported_state_callback, queue_context); + if (result != IOTHUB_CLIENT_OK) + { + LogError("IoTHubClient_LL_SendReportedState failed"); + free(queue_context); + } + } } } - (void)Unlock(iotHubClientInstance->LockHandle); } } @@ -1075,7 +1391,6 @@ free(savedData->destinationFileName); free(savedData); sourceCloned = 0; - } else { @@ -1089,10 +1404,12 @@ } else { + IOTHUB_CLIENT_INSTANCE* iotHubClientHandleData = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle; + savedData->iotHubClientFileUploadCallback = iotHubClientFileUploadCallback; savedData->context = context; memcpy(savedData->source, source, size); - IOTHUB_CLIENT_INSTANCE* iotHubClientHandleData = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle; + if (Lock(iotHubClientHandleData->LockHandle) != LOCK_OK) /*locking because the next statement is changing blobThreadsToBeJoined*/ { LogError("unable to lock"); @@ -1152,7 +1469,6 @@ } else { - result = IOTHUB_CLIENT_OK; } }