Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/.vscode/c_cpp_properties.json
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/.vscode/c_cpp_properties.json	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/.vscode/c_cpp_properties.json	(revision 464)
@@ -20,7 +20,7 @@
                 "${workspaceFolder}/../azure_iot_sdk/deps/parson",
                 "${workspaceFolder}/../azure_iot_sdk/umqtt/inc",
-                "${workspaceFolder}/../wolfssl-4.4.0/wolfssl",
-                "${workspaceFolder}/../wolfssl-4.4.0/wolfssl/wolfcrypt",
-                "${workspaceFolder}/../wolfssl-4.4.0",
+                "${workspaceFolder}/../wolfssl-4.7.0/wolfssl",
+                "${workspaceFolder}/../wolfssl-4.7.0/wolfssl/wolfcrypt",
+                "${workspaceFolder}/../wolfssl-4.7.0",
                 "${workspaceFolder}/../zlib-1.2.11/src"
             ],
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/Makefile
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/Makefile	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/Makefile	(revision 464)
@@ -99,4 +99,5 @@
 OBJECTS += certs/certs.o
 OBJECTS += deps/parson/parson.o
+OBJECTS += deps/uhttp/src/uhttp.o
 OBJECTS += iothub_client/src/blob.o
 OBJECTS += iothub_client/src/iothub.o
@@ -144,5 +145,4 @@
 OBJECTS += serializer/src/codefirst.o
 OBJECTS += serializer/src/commanddecoder.o
-OBJECTS += uhttp/src/uhttp.o
 OBJECTS += umqtt/src/mqtt_client.o
 OBJECTS += umqtt/src/mqtt_codec.o
@@ -171,11 +171,11 @@
 INCLUDE_PATHS += -I../../azure_iot_sdk/deps/parson
 INCLUDE_PATHS += -I../../azure_iot_sdk/deps/azure-macro-utils-c/inc
+INCLUDE_PATHS += -I../../azure_iot_sdk/deps/uhttp/inc
 INCLUDE_PATHS += -I../../azure_iot_sdk/deps/umock-c/inc
 INCLUDE_PATHS += -I../../azure_iot_sdk/serializer/inc
-INCLUDE_PATHS += -I../../azure_iot_sdk/uhttp/inc
 INCLUDE_PATHS += -I../../azure_iot_sdk/umqtt/inc
-INCLUDE_PATHS += -I../../wolfssl-4.4.0/wolfssl
-INCLUDE_PATHS += -I../../wolfssl-4.4.0/wolfssl/wolfcrypt
-INCLUDE_PATHS += -I../../wolfssl-4.4.0
+INCLUDE_PATHS += -I../../wolfssl-4.7.0/wolfssl
+INCLUDE_PATHS += -I../../wolfssl-4.7.0/wolfssl/wolfcrypt
+INCLUDE_PATHS += -I../../wolfssl-4.7.0
 INCLUDE_PATHS += -I../../zlib-1.2.11/src
 
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/httpapi_compact.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/httpapi_compact.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/httpapi_compact.c	(revision 464)
@@ -51,8 +51,4 @@
     char*           x509ClientCertificate;
     char*           x509ClientPrivateKey;
-    const char*     proxy_host;
-    int             proxy_port;
-    const char*     proxy_username;
-    const char*     proxy_password;
     XIO_HANDLE      xio_handle;
     size_t          received_bytes_count;
@@ -205,4 +201,5 @@
 {
     HTTP_HANDLE_DATA* http_instance;
+    TLSIO_CONFIG tlsio_config;
 
     if (hostName == NULL)
@@ -226,7 +223,7 @@
             LogError("There is no memory to control the http connection");
         }
-        else if(mallocAndStrcpy_s((char**)&(http_instance->hostName), hostName) != 0)
-        {
-            LogError("failure allocate host name");
+        else if (mallocAndStrcpy_s(&http_instance->hostName, hostName) != 0)
+        {
+            LogError("Failed copying hostname");
             free(http_instance);
             http_instance = NULL;
@@ -234,16 +231,29 @@
         else
         {
-            http_instance->xio_handle = NULL;
-            http_instance->is_connected = 0;
-            http_instance->is_io_error = 0;
-            http_instance->received_bytes_count = 0;
-            http_instance->received_bytes = NULL;
-            http_instance->certificate = NULL;
-            http_instance->x509ClientCertificate = NULL;
-            http_instance->x509ClientPrivateKey = NULL;
-            http_instance->proxy_host = NULL;
-            http_instance->proxy_port = 0;
-            http_instance->proxy_username = NULL;
-            http_instance->proxy_password = NULL;
+            tlsio_config.hostname = http_instance->hostName;
+            tlsio_config.port = 443;
+            tlsio_config.underlying_io_interface = NULL;
+            tlsio_config.underlying_io_parameters = NULL;
+
+            http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config);
+
+            /*Codes_SRS_HTTPAPI_COMPACT_21_016: [ If the HTTPAPI_CreateConnection failed to create the connection, it shall return NULL as the handle. ]*/
+            if (http_instance->xio_handle == NULL)
+            {
+                LogError("Create connection failed");
+                free(http_instance->hostName);
+                free(http_instance);
+                http_instance = NULL;
+            }
+            else
+            {
+                http_instance->is_connected = 0;
+                http_instance->is_io_error = 0;
+                http_instance->received_bytes_count = 0;
+                http_instance->received_bytes = NULL;
+                http_instance->certificate = NULL;
+                http_instance->x509ClientCertificate = NULL;
+                http_instance->x509ClientPrivateKey = NULL;
+            }
         }
     }
@@ -311,9 +321,4 @@
         }
 
-        if (http_instance->hostName != NULL)
-        {
-            free((void*)http_instance->hostName);
-        }
-
 #ifndef DO_NOT_COPY_TRUSTED_CERTS_STRING
         /*Codes_SRS_HTTPAPI_COMPACT_21_018: [ If there is a certificate associated to this connection, the HTTPAPI_CloseConnection shall free all allocated memory for the certificate. ]*/
@@ -336,15 +341,7 @@
         }
 
-        if (http_instance->proxy_host != NULL)
-        {
-            free((void*)http_instance->proxy_host);
-        }
-        if (http_instance->proxy_username != NULL)
-        {
-            free((void*)http_instance->proxy_username);
-        }
-        if (http_instance->proxy_password != NULL)
-        {
-            free((void*)http_instance->proxy_password);
+        if (http_instance->hostName)
+        {
+            free(http_instance->hostName);
         }
 
@@ -714,6 +711,4 @@
 {
     HTTPAPI_RESULT result;
-    TLSIO_CONFIG tlsio_config;
-    HTTP_PROXY_IO_CONFIG http_proxy_io_config;
 
     if (http_instance->is_connected != 0)
@@ -725,39 +720,7 @@
     {
         http_instance->is_io_error = 0;
-        tlsio_config.hostname = http_instance->hostName;
-        tlsio_config.port = 443;
-
-        if (http_instance->proxy_host != NULL) {
-            tlsio_config.underlying_io_interface = http_proxy_io_get_interface_description();
-        }
-        else {
-            tlsio_config.underlying_io_interface = NULL;
-        }
-
-        if (tlsio_config.underlying_io_interface != NULL) {
-            tlsio_config.underlying_io_parameters = (void*)&http_proxy_io_config;
-
-            http_proxy_io_config.proxy_hostname = http_instance->proxy_host;
-            http_proxy_io_config.proxy_port = http_instance->proxy_port;
-            http_proxy_io_config.username = http_instance->proxy_username;
-            http_proxy_io_config.password = http_instance->proxy_password;
-            http_proxy_io_config.hostname = http_instance->hostName;
-            http_proxy_io_config.port = 443;
-        }
-        else {
-            tlsio_config.underlying_io_parameters = NULL;
-        }
-
-        http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config);
-
-        if (http_instance->xio_handle == NULL)
-        {
-            LogError("Create connection failed");
-            free(http_instance);
-            http_instance = NULL;
-            result = HTTPAPI_ERROR;
-        }
+
         /*Codes_SRS_HTTPAPI_COMPACT_21_022: [ If a Certificate was provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/
-        else if ((http_instance->certificate != NULL) &&
+        if ((http_instance->certificate != NULL) &&
             (xio_setoption(http_instance->xio_handle, OPTION_TRUSTED_CERT, http_instance->certificate) != 0))
         {
@@ -777,4 +740,5 @@
             (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_instance->x509ClientPrivateKey) != 0))
         {
+
             /*Codes_SRS_HTTPAPI_COMPACT_06_006: [ If the transport failed setting the client certificate private key, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ] */
             result = HTTPAPI_SET_OPTION_FAILED;
@@ -800,5 +764,5 @@
                 {
                     xio_dowork(http_instance->xio_handle);
-                    //LogInfo("Waiting for TLS connection");
+                    LogInfo("Waiting for TLS connection");
                     if ((countRetry--) < 0)
                     {
@@ -1347,5 +1311,5 @@
 #endif // DO_NOT_COPY_TRUSTED_CERTS_STRING
     }
-    else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0)
+    else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0 || strcmp(OPTION_X509_ECC_CERT, optionName) == 0)
     {
         int len;
@@ -1370,5 +1334,5 @@
         }
     }
-    else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0)
+    else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0 || strcmp(OPTION_X509_ECC_KEY, optionName) == 0)
     {
         int len;
@@ -1395,66 +1359,68 @@
     else if (strcmp(OPTION_HTTP_PROXY, optionName) == 0)
     {
-        HTTP_PROXY_OPTIONS* proxy_data = (HTTP_PROXY_OPTIONS*)value;
-        if (proxy_data->host_address == NULL || proxy_data->port <= 0)
-        {
-            LogError("invalid proxy_data values ( host_address = %p, port = %d)", proxy_data->host_address, proxy_data->port);
+        TLSIO_CONFIG tlsio_config;
+        HTTP_PROXY_IO_CONFIG proxy_config;
+        HTTP_PROXY_OPTIONS* proxy_options = (HTTP_PROXY_OPTIONS*)value;
+
+        if (proxy_options->host_address == NULL)
+        {
+            LogError("NULL host_address in proxy options");
             result = HTTPAPI_ERROR;
         }
+        else if (((proxy_options->username == NULL) || (proxy_options->password == NULL)) &&
+                (proxy_options->username != proxy_options->password))
+        {
+            LogError("Only one of username and password for proxy settings was NULL");
+            result = HTTPAPI_ERROR;
+        }
         else
         {
-            if(http_instance->proxy_host != NULL)
-            {
-                free((void*)http_instance->proxy_host);
-                http_instance->proxy_host = NULL;
-            }
-            if(mallocAndStrcpy_s((char**)&(http_instance->proxy_host), (const char*)proxy_data->host_address) != 0)
-            {
-                LogError("failure allocate proxy host");
+
+            /* Workaround: xio interface is already created when HTTPAPI_CreateConnection is call without proxy support
+             * need to destroy the interface and create a new one with proxy information
+             */
+            OPTIONHANDLER_HANDLE xio_options;
+            if ((xio_options = xio_retrieveoptions(http_instance->xio_handle)) == NULL)
+            {
+                LogError("failed saving underlying I/O transport options");
                 result = HTTPAPI_ERROR;
             }
             else
             {
-                http_instance->proxy_port = proxy_data->port;
-
-                if (proxy_data->username != NULL && proxy_data->password != NULL)
-                {
-                    if(http_instance->proxy_username != NULL)
+                xio_destroy(http_instance->xio_handle);
+
+                proxy_config.hostname = http_instance->hostName;
+                proxy_config.proxy_hostname = proxy_options->host_address;
+                proxy_config.password = proxy_options->password;
+                proxy_config.username = proxy_options->username;
+                proxy_config.proxy_port = proxy_options->port;
+                proxy_config.port = 443;
+
+                tlsio_config.hostname = http_instance->hostName;
+                tlsio_config.port = 443;
+                tlsio_config.underlying_io_interface =  http_proxy_io_get_interface_description();
+                tlsio_config.underlying_io_parameters = &proxy_config;
+
+                http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config);
+
+                if (http_instance->xio_handle == NULL)
+                {
+                    LogError("Failed to create xio handle with proxy configuration");
+                    result = HTTPAPI_ERROR;
+                }
+                else
+                {
+                    if (OptionHandler_FeedOptions(xio_options, http_instance->xio_handle) != OPTIONHANDLER_OK)
                     {
-                        free((void*)http_instance->proxy_username);
-                        http_instance->proxy_username = NULL;
-                    }
-                    if(mallocAndStrcpy_s((char**)&(http_instance->proxy_username), (const char*)proxy_data->username) != 0)
-                    {
-                        LogError("failure allocate proxy username");
-                        free((void*)http_instance->proxy_host);
-                        http_instance->proxy_host = NULL;
+                        LogError("Failed feeding existing options to new xio instance.");
                         result = HTTPAPI_ERROR;
                     }
                     else
                     {
-                        if(http_instance->proxy_password != NULL)
-                        {
-                          free((void*)http_instance->proxy_password);
-                          http_instance->proxy_password = NULL;
-                        }
-                        if(mallocAndStrcpy_s((char**)&(http_instance->proxy_password), (const char*)proxy_data->password) != 0)
-                        {
-                            LogError("failure allocate proxy password");
-                            free((void*)http_instance->proxy_host);
-                            http_instance->proxy_host = NULL;
-                            free((void*)http_instance->proxy_username);
-                            http_instance->proxy_username = NULL;
-                            result = HTTPAPI_ERROR;
-                        }
-                        else
-                        {
-                          result = HTTPAPI_OK;
-                        }
+                        result = HTTPAPI_OK;
                     }
                 }
-                else
-                {
-                    result = HTTPAPI_OK;
-                }
+
+                OptionHandler_Destroy(xio_options);
             }
         }
@@ -1466,4 +1432,5 @@
         LogInfo("unknown option %s", optionName);
     }
+
     return result;
 }
@@ -1510,5 +1477,5 @@
 #endif // DO_NOT_COPY_TRUSTED_CERTS_STRING
     }
-    else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0)
+    else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0 || strcmp(OPTION_X509_ECC_CERT, optionName) == 0)
     {
         certLen = strlen((const char*)value);
@@ -1527,5 +1494,5 @@
         }
     }
-    else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0)
+    else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0 || strcmp(OPTION_X509_ECC_KEY, optionName) == 0)
     {
         certLen = strlen((const char*)value);
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/platform_toppers.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/platform_toppers.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/platform_toppers.c	(revision 464)
@@ -52,6 +52,6 @@
 const IO_INTERFACE_DESCRIPTION* platform_get_default_tlsio(void)
 {
-	//return tlsio_esp_at_get_interface_description();
-	return tlsio_wolfssl_get_interface_description();
+	return tlsio_esp_at_get_interface_description();
+	//return tlsio_wolfssl_get_interface_description();
 }
 
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/tlsio_wolfssl.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/tlsio_wolfssl.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/adapters/tlsio_wolfssl.c	(revision 464)
@@ -19,5 +19,4 @@
 #include "azure_c_shared_utility/xlogging.h"
 #include "azure_c_shared_utility/shared_util_options.h"
-#include "azure_c_shared_utility/threadapi.h"
 
 typedef enum TLSIO_STATE_ENUM_TAG
@@ -53,9 +52,10 @@
     char* x509privatekey;
     int wolfssl_device_id;
-    size_t socket_reads;
+    char* hostname;
+    bool ignore_host_name_check;
 } TLS_IO_INSTANCE;
 
 STATIC_VAR_UNUSED const char* const OPTION_WOLFSSL_SET_DEVICE_ID = "SetDeviceId";
-static const size_t SOCKET_READ_LIMIT = 10000; // 10,000 ms ?
+static const size_t SOCKET_READ_LIMIT = 5;
 
 /*this function will clone an option given by name and value*/
@@ -106,4 +106,21 @@
             }
         }
+        #ifdef INVALID_DEVID
+        else if(strcmp(name, OPTION_WOLFSSL_SET_DEVICE_ID) == 0 )
+        {
+             int* value_clone;
+
+             if ((value_clone = malloc(sizeof(int))) == NULL)
+             {
+                 LogError("unable to clone device id option");
+             }
+             else
+             {
+                 *value_clone = *(int*)value;
+             }
+
+             result = value_clone;
+        }
+        #endif
         else
         {
@@ -127,5 +144,6 @@
         if ((strcmp(name, OPTION_TRUSTED_CERT) == 0) ||
             (strcmp(name, SU_OPTION_X509_CERT) == 0) ||
-            (strcmp(name, SU_OPTION_X509_PRIVATE_KEY) == 0))
+            (strcmp(name, SU_OPTION_X509_PRIVATE_KEY) == 0) ||
+            (strcmp(name, OPTION_WOLFSSL_SET_DEVICE_ID) == 0))
         {
             free((void*)value);
@@ -185,4 +203,15 @@
                 result = NULL;
             }
+            #ifdef INVALID_DEVID
+            else if (
+                (tls_io_instance->wolfssl_device_id != INVALID_DEVID) &&
+                (OptionHandler_AddOption(result, OPTION_WOLFSSL_SET_DEVICE_ID, &tls_io_instance->wolfssl_device_id) != OPTIONHANDLER_OK)
+                )
+            {
+                LogError("unable to save deviceid option");
+                OptionHandler_Destroy(result);
+                result = NULL;
+            }
+            #endif
             else
             {
@@ -258,10 +287,10 @@
         int res;
         tls_io_instance->tlsio_state = TLSIO_STATE_IN_HANDSHAKE;
-        tls_io_instance->socket_reads = 0;
 
         res = wolfSSL_connect(tls_io_instance->ssl);
         if (res != SSL_SUCCESS)
         {
-            LogError("WolfSSL connect failed");
+            // Error codes explained in https://www.wolfssl.com/docs/wolfssl-manual/appendix-c/
+            LogError("WolfSSL connect failed (%d)", wolfSSL_get_error(tls_io_instance->ssl, res));
             indicate_open_complete(tls_io_instance, IO_OPEN_ERROR);
             tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
@@ -355,18 +384,15 @@
         TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
         unsigned char* new_socket_io_read_bytes;
+        size_t socket_reads = 0;
 
         AZURE_UNREFERENCED_PARAMETER(ssl);
-        if (tls_io_instance->socket_io_read_byte_count == 0)
-        {
-            if (tls_io_instance->socket_reads >= SOCKET_READ_LIMIT) {
-                return WOLFSSL_CBIO_ERR_TIMEOUT;
-            }
+        while (tls_io_instance->socket_io_read_byte_count == 0 && socket_reads < SOCKET_READ_LIMIT)
+        {
             xio_dowork(tls_io_instance->socket_io);
-            if (tls_io_instance->tlsio_state == TLSIO_STATE_IN_HANDSHAKE)
-            {
-                tls_io_instance->socket_reads++;
-                ThreadAPI_Sleep(1);
-                return 0;
-            }
+            if (tls_io_instance->tlsio_state != TLSIO_STATE_IN_HANDSHAKE)
+            {
+                break;
+            }
+            socket_reads++;
         }
 
@@ -417,29 +443,17 @@
 }
 
-static void on_send_complete(void* context, IO_SEND_RESULT send_result)
-{
-	TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
-	if ((tls_io_instance == NULL) || (tls_io_instance->on_send_complete == NULL))
-		return;
-
-	tls_io_instance->on_send_complete(tls_io_instance->on_send_complete_callback_context, send_result);
-
-	tls_io_instance->on_send_complete = NULL;
-	tls_io_instance->on_send_complete_callback_context = NULL;
-}
-
 static int on_io_send(WOLFSSL *ssl, char *buf, int sz, void *context)
 {
-    int result, ret;
+    int result;
     AZURE_UNREFERENCED_PARAMETER(ssl);
 
     TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
 
-    if ((ret = xio_send(tls_io_instance->socket_io, buf, sz, on_send_complete, tls_io_instance)) != 0)
-    {
-        LogError("Failed sending bytes through underlying IO %d", ret);
+    if (xio_send(tls_io_instance->socket_io, buf, sz, tls_io_instance->on_send_complete, tls_io_instance->on_send_complete_callback_context) != 0)
+    {
+        LogError("Failed sending bytes through underlying IO");
         tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
         indicate_error(tls_io_instance);
-        result = 0;
+        result = WOLFSSL_CBIO_ERR_GENERAL;
     }
     else
@@ -455,8 +469,5 @@
     AZURE_UNREFERENCED_PARAMETER(ssl);
     TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
-    if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN) {
-        LogInfo("on_handshake_done called in TLSIO_STATE_OPEN state");
-    }
-    else if (tls_io_instance->tlsio_state != TLSIO_STATE_IN_HANDSHAKE)
+    if (tls_io_instance->tlsio_state != TLSIO_STATE_IN_HANDSHAKE)
     {
         LogInfo("on_handshake_done called when not in IN_HANDSHAKE state");
@@ -558,8 +569,29 @@
 }
 
+static int enable_domain_check(TLS_IO_INSTANCE* tls_io_instance)
+{
+    int result = 0;
+
+    if (!tls_io_instance->ignore_host_name_check)
+    {
+        if (wolfSSL_check_domain_name(tls_io_instance->ssl, tls_io_instance->hostname) != WOLFSSL_SUCCESS)
+        {
+            result = MU_FAILURE;
+        }
+    }
+
+    return result;
+}
+
 static int prepare_wolfssl_open(TLS_IO_INSTANCE* tls_io_instance)
 {
     int result;
-    if (add_certificate_to_store(tls_io_instance) != 0)
+
+    if (enable_domain_check(tls_io_instance))
+    {
+        LogError("Failed to configure domain name verification");
+        result = MU_FAILURE;
+    }
+    else if (add_certificate_to_store(tls_io_instance) != 0)
     {
         LogError("Failed to add certificates to store");
@@ -575,11 +607,4 @@
         result = MU_FAILURE;
     }
-#ifdef INVALID_DEVID
-    else if (tls_io_instance->wolfssl_device_id != INVALID_DEVID && wolfSSL_SetDevId(tls_io_instance->ssl, tls_io_instance->wolfssl_device_id) != WOLFSSL_SUCCESS)
-    {
-        LogError("Failure setting device id");
-        result = MU_FAILURE;
-    }
-#endif
     else
     {
@@ -628,4 +653,10 @@
             {
                 LogError("Cannot create the wolfSSL context");
+                free(result);
+                result = NULL;
+            }
+            else if (mallocAndStrcpy_s(&result->hostname, tls_io_config->hostname) != 0)
+            {
+                LogError("Failed copying the target hostname.");
                 free(result);
                 result = NULL;
@@ -660,4 +691,5 @@
                     LogError("Failed getting socket IO interface description.");
                     wolfSSL_CTX_free(result->ssl_context);
+                    free(result->hostname);
                     free(result);
                     result = NULL;
@@ -670,4 +702,5 @@
                         LogError("Failure connecting to underlying socket_io");
                         wolfSSL_CTX_free(result->ssl_context);
+                        free(result->hostname);
                         free(result);
                         result = NULL;
@@ -677,4 +710,5 @@
                         LogError("Failure connecting to underlying socket_io");
                         wolfSSL_CTX_free(result->ssl_context);
+                        free(result->hostname);
                         free(result);
                         result = NULL;
@@ -719,4 +753,5 @@
 
         xio_destroy(tls_io_instance->socket_io);
+        free(tls_io_instance->hostname);
         free(tls_io);
     }
@@ -843,9 +878,4 @@
             result = MU_FAILURE;
         }
-        if (tls_io_instance->on_send_complete != NULL)
-        {
-            LogError("Error writing data");
-            result = MU_FAILURE;
-        }
         else
         {
@@ -882,6 +912,5 @@
             (tls_io_instance->tlsio_state != TLSIO_STATE_ERROR))
         {
-            if (tls_io_instance->tlsio_state != TLSIO_STATE_OPENING_UNDERLYING_IO)
-                decode_ssl_received_bytes(tls_io_instance);
+            decode_ssl_received_bytes(tls_io_instance);
             xio_dowork(tls_io_instance->socket_io);
         }
@@ -892,4 +921,7 @@
 static int process_option(char** destination, const char* name, const char* value)
 {
+
+    (void) name;
+    
     int result;
     if (*destination != NULL)
@@ -944,19 +976,12 @@
         {
             int device_id = *((int *)value);
-            if (tls_io_instance->ssl != NULL)
-            {
-                if (tls_io_instance->ssl != NULL && wolfSSL_SetDevId(tls_io_instance->ssl, device_id) != WOLFSSL_SUCCESS)
-                {
-                    LogError("Failure setting device id on ssl");
-                    result = MU_FAILURE;
-                }
-                else
-                {
-                    result = 0;
-                }
+            if (tls_io_instance->ssl != NULL && wolfSSL_SetDevId(tls_io_instance->ssl, device_id) != WOLFSSL_SUCCESS)
+            {
+                LogError("Failure setting device id on ssl");
+                result = MU_FAILURE;
             }
             else
             {
-                // Save the id till we create the ssl object
+                // Save the device Id even if ssl object not yet created.
                 tls_io_instance->wolfssl_device_id = device_id;
                 result = 0;
@@ -964,4 +989,10 @@
         }
 #endif
+        else if (strcmp("ignore_host_name_check", optionName) == 0)
+        {
+            bool* server_name_check = (bool*)value;
+            tls_io_instance->ignore_host_name_check = *server_name_check;
+            result = 0;
+        }
         else
         {
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/crt_abstractions.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/crt_abstractions.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/crt_abstractions.h	(revision 464)
@@ -6,4 +6,5 @@
 
 #ifdef __cplusplus
+#include <cstdint>
 #include <cstdio>
 #include <cstring>
@@ -11,4 +12,5 @@
 #include <cmath>
 #else // __cplusplus
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -96,4 +98,5 @@
 MOCKABLE_FUNCTION(, int, unsignedIntToString, char*, destination, size_t, destinationSize, unsigned int, value);
 MOCKABLE_FUNCTION(, int, size_tToString, char*, destination, size_t, destinationSize, size_t, value);
+MOCKABLE_FUNCTION(, int, uint64_tToString, char*, destination, size_t, destinationSize, uint64_t, value);
 
 /*following logic shall define the TOUPPER and ISDIGIT, we do that because the SDK is not happy with some Arduino implementation of it.*/
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/sastoken.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/sastoken.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/sastoken.h	(revision 464)
@@ -4,4 +4,6 @@
 #ifndef SASTOKEN_H
 #define SASTOKEN_H
+
+#include <stdint.h>
 
 #ifdef __cplusplus
@@ -18,6 +20,6 @@
 
     MOCKABLE_FUNCTION(, bool, SASToken_Validate, STRING_HANDLE, sasToken);
-    MOCKABLE_FUNCTION(, STRING_HANDLE, SASToken_Create, STRING_HANDLE, key, STRING_HANDLE, scope, STRING_HANDLE, keyName, size_t, expiry);
-    MOCKABLE_FUNCTION(, STRING_HANDLE, SASToken_CreateString, const char*, key, const char*, scope, const char*, keyName, size_t, expiry);
+    MOCKABLE_FUNCTION(, STRING_HANDLE, SASToken_Create, STRING_HANDLE, key, STRING_HANDLE, scope, STRING_HANDLE, keyName, uint64_t, expiry);
+    MOCKABLE_FUNCTION(, STRING_HANDLE, SASToken_CreateString, const char*, key, const char*, scope, const char*, keyName, uint64_t, expiry);
 
 #ifdef __cplusplus
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/shared_util_options.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/shared_util_options.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/shared_util_options.h	(revision 464)
@@ -29,4 +29,13 @@
     static STATIC_VAR_UNUSED const char* const OPTION_OPENSSL_CIPHER_SUITE = "CipherSuite";
 
+    static STATIC_VAR_UNUSED const char* const OPTION_OPENSSL_ENGINE = "Engine";
+    static STATIC_VAR_UNUSED const char* const OPTION_OPENSSL_PRIVATE_KEY_TYPE = "x509PrivatekeyType";
+
+    typedef enum OPTION_OPENSSL_KEY_TYPE_TAG
+    {
+        KEY_TYPE_DEFAULT,
+        KEY_TYPE_ENGINE
+    } OPTION_OPENSSL_KEY_TYPE;
+
     static STATIC_VAR_UNUSED const char* const SU_OPTION_X509_CERT = "x509certificate";
     static STATIC_VAR_UNUSED const char* const SU_OPTION_X509_PRIVATE_KEY = "x509privatekey";
@@ -45,5 +54,5 @@
     static STATIC_VAR_UNUSED const char* const OPTION_SET_TLS_RENEGOTIATION = "tls_renegotiation";
 
-    // DEPRECATED: The underlying security library for your platform will use a secure TLS version 
+    // DEPRECATED: The underlying security library for your platform will use a secure TLS version
     // that in general  should not be overridden with OPTION_TLS_VERSION.
     static STATIC_VAR_UNUSED const char* const OPTION_TLS_VERSION = "tls_version";
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/tickcounter.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/tickcounter.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/inc/azure_c_shared_utility/tickcounter.h	(revision 464)
@@ -18,9 +18,5 @@
 #endif /* __cplusplus */
 
-#if defined(_WIN32) || defined(__MBED__) || defined(__APPLE__)
     typedef uint_fast64_t tickcounter_ms_t; // Use 64-bit because of 32-bit is going to roll over back to zero after roughly 49.7 days that is not good for IoT devices which need keep running for months
-#else
-    typedef uint_fast32_t tickcounter_ms_t;
-#endif
     typedef struct TICK_COUNTER_INSTANCE_TAG *TICK_COUNTER_HANDLE;
 
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/buffer.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/buffer.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/buffer.c	(revision 464)
@@ -541,8 +541,13 @@
         {
             //put b2 ahead of b1: [b2][b1], return b1
-            if (b2->size ==0)
+            if (b2->size == 0)
             {
                 // do nothing
                 result = 0;
+            }
+            else if (b1->size + b2->size < b2->size)
+            {
+                LogError("Failure: size_t overflow.");
+                result = MU_FAILURE;
             }
             else
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/crt_abstractions.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/crt_abstractions.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/crt_abstractions.c	(revision 464)
@@ -6,4 +6,5 @@
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <string.h>
 #include <limits.h>
@@ -598,5 +599,5 @@
         case FST_NUMBER:
             val = fraction * pow(10.0, (double)exponential) * (double)signal;
-            if ((val >= (FLT_MAX * (-1.0f))) && (val <= FLT_MAX))
+            if ((val >= ((double)FLT_MAX * (-1.0))) && (val <= (double)FLT_MAX))
             {
                 /*Codes_SRS_CRT_ABSTRACTIONS_21_016: [The strtof_s must return the float that represents the value in the initial part of the string. If any.]*/
@@ -836,2 +837,48 @@
     return result;
 }
+
+/*takes "value" and transforms it into a decimal string*/
+/*10 => "10"*/
+/*return 0 when everything went ok*/
+int uint64_tToString(char* destination, size_t destinationSize, uint64_t value)
+{
+    int result;
+    size_t pos;
+    /*the below loop gets the number in reverse order*/
+    if (
+        (destination == NULL) ||
+        (destinationSize < 2) /*because the smallest number is '0\0' which requires 2 characters*/
+        )
+    {
+        result = MU_FAILURE;
+    }
+    else
+    {
+        pos = 0;
+        do
+        {
+            destination[pos++] = '0' + (value % 10);
+            value /= 10;
+        } while ((value > 0) && (pos < (destinationSize - 1)));
+
+        if (value == 0)
+        {
+            size_t w;
+            destination[pos] = '\0';
+            /*all converted and they fit*/
+            for (w = 0; w <= (pos - 1) >> 1; w++)
+            {
+                char temp;
+                temp = destination[w];
+                destination[w] = destination[pos - 1 - w];
+                destination[pos - 1 - w] = temp;
+            }
+            result = 0;
+        }
+        else
+        {
+            result = MU_FAILURE;
+        }
+    }
+    return result;
+}
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/httpapiexsas.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/httpapiexsas.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/httpapiexsas.c	(revision 464)
@@ -107,7 +107,7 @@
 {
     /*Codes_SRS_HTTPAPIEXSAS_06_005: [If the parameter handle is NULL then HTTAPIEX_SAS_Destroy shall do nothing and return.]*/
-    if (handle)
+    HTTPAPIEX_SAS_STATE* state = (HTTPAPIEX_SAS_STATE*)handle;
+    if (state)
     {
-        HTTPAPIEX_SAS_STATE* state = (HTTPAPIEX_SAS_STATE*)handle;
         /*Codes_SRS_HTTPAPIEXSAS_06_006: [HTTAPIEX_SAS_Destroy shall deallocate any structures denoted by the parameter handle.]*/
         if (state->key)
@@ -159,5 +159,5 @@
                         /*Codes_SRS_HTTPAPIEXSAS_06_011: [SASToken_Create shall be invoked.]*/
                         /*Codes_SRS_HTTPAPIEXSAS_06_012: [If the return result of SASToken_Create is NULL then fallthrough.]*/
-                        size_t expiry = (size_t)(difftime(currentTime, 0) + 3600);
+                        uint64_t expiry = (uint64_t)(difftime(currentTime, 0) + 3600);
                         newSASToken = SASToken_CreateString(state->key, state->uriResource, state->keyName, expiry);
                     }
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/sastoken.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/sastoken.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/sastoken.c	(revision 464)
@@ -24,5 +24,5 @@
         if (expiryASCII[i] >= '0' && expiryASCII[i] <= '9')
         {
-            value = value * 10 + (double)(expiryASCII[i] - '0');
+            value = value * 10 + ((double)expiryASCII[i] - (double)'0');
         }
         else
@@ -203,5 +203,5 @@
 }
 
-static STRING_HANDLE construct_sas_token(const char* key, const char* scope, const char* keyname, size_t expiry)
+static STRING_HANDLE construct_sas_token(const char* key, const char* scope, const char* keyname, uint64_t expiry)
 {
     STRING_HANDLE result;
@@ -221,5 +221,5 @@
     {
         /*Codes_SRS_SASTOKEN_06_026: [If the conversion to string form fails for any reason then SASToken_Create shall return NULL.]*/
-        if (size_tToString(tokenExpirationTime, sizeof(tokenExpirationTime), expiry) != 0)
+        if (uint64_tToString(tokenExpirationTime, sizeof(tokenExpirationTime), expiry) != 0)
         {
             LogError("For some reason converting seconds to a string failed.  No SAS can be generated.");
@@ -303,5 +303,5 @@
 }
 
-STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, size_t expiry)
+STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HANDLE keyName, uint64_t expiry)
 {
     STRING_HANDLE result;
@@ -326,5 +326,5 @@
 }
 
-STRING_HANDLE SASToken_CreateString(const char* key, const char* scope, const char* keyName, size_t expiry)
+STRING_HANDLE SASToken_CreateString(const char* key, const char* scope, const char* keyName, uint64_t expiry)
 {
     STRING_HANDLE result;
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/sha384-512.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/sha384-512.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/c-utility/src/sha384-512.c	(revision 464)
@@ -469,4 +469,7 @@
     if (!length)
         return shaSuccess;
+    
+    if (length > (sizeof(context->Message_Block) / sizeof(context->Message_Block[0])))
+        return shaBadParam;
 
     if (!context || !message_array)
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/inc/azure_uhttp_c/uhttp.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/inc/azure_uhttp_c/uhttp.h	(revision 464)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/inc/azure_uhttp_c/uhttp.h	(revision 464)
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#ifndef UHTTP_H
+#define UHTTP_H
+
+#ifdef __cplusplus
+extern "C" {
+#include <cstddef>
+#else
+#include <stddef.h>
+#include <stdbool.h>
+#endif /* __cplusplus */
+
+#include "azure_c_shared_utility/httpheaders.h"
+#include "azure_macro_utils/macro_utils.h"
+#include "azure_c_shared_utility/xio.h"
+#include "umock_c/umock_c_prod.h"
+
+typedef struct HTTP_CLIENT_HANDLE_DATA_TAG* HTTP_CLIENT_HANDLE;
+
+#define HTTP_CLIENT_RESULT_VALUES       \
+    HTTP_CLIENT_OK,                     \
+    HTTP_CLIENT_INVALID_ARG,            \
+    HTTP_CLIENT_ERROR,                  \
+    HTTP_CLIENT_OPEN_FAILED,            \
+    HTTP_CLIENT_SEND_FAILED,            \
+    HTTP_CLIENT_ALREADY_INIT,           \
+    HTTP_CLIENT_HTTP_HEADERS_FAILED,    \
+    HTTP_CLIENT_INVALID_STATE           \
+
+
+/** @brief Enumeration specifying the possible return values for the APIs in
+*           this module.
+*/
+MU_DEFINE_ENUM(HTTP_CLIENT_RESULT, HTTP_CLIENT_RESULT_VALUES);
+
+#define HTTP_CLIENT_REQUEST_TYPE_VALUES \
+    HTTP_CLIENT_REQUEST_OPTIONS,        \
+    HTTP_CLIENT_REQUEST_GET,            \
+    HTTP_CLIENT_REQUEST_POST,           \
+    HTTP_CLIENT_REQUEST_PUT,            \
+    HTTP_CLIENT_REQUEST_DELETE,         \
+    HTTP_CLIENT_REQUEST_PATCH
+
+MU_DEFINE_ENUM(HTTP_CLIENT_REQUEST_TYPE, HTTP_CLIENT_REQUEST_TYPE_VALUES);
+
+#define HTTP_CALLBACK_REASON_VALUES     \
+    HTTP_CALLBACK_REASON_OK,            \
+    HTTP_CALLBACK_REASON_OPEN_FAILED,   \
+    HTTP_CALLBACK_REASON_SEND_FAILED,   \
+    HTTP_CALLBACK_REASON_ERROR,         \
+    HTTP_CALLBACK_REASON_PARSING_ERROR, \
+    HTTP_CALLBACK_REASON_DESTROY,       \
+    HTTP_CALLBACK_REASON_DISCONNECTED
+
+MU_DEFINE_ENUM(HTTP_CALLBACK_REASON, HTTP_CALLBACK_REASON_VALUES);
+
+typedef void(*ON_HTTP_OPEN_COMPLETE_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON open_result);
+typedef void(*ON_HTTP_ERROR_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON error_result);
+typedef void(*ON_HTTP_REQUEST_CALLBACK)(void* callback_ctx, HTTP_CALLBACK_REASON request_result, const unsigned char* content, size_t content_length, unsigned int status_code,
+    HTTP_HEADERS_HANDLE response_headers);
+typedef void(*ON_HTTP_CLOSED_CALLBACK)(void* callback_ctx);
+
+MOCKABLE_FUNCTION(, HTTP_CLIENT_HANDLE, uhttp_client_create, const IO_INTERFACE_DESCRIPTION*, io_interface_desc, const void*, xio_param, ON_HTTP_ERROR_CALLBACK, on_http_error, void*, callback_ctx);
+MOCKABLE_FUNCTION(, void, uhttp_client_destroy, HTTP_CLIENT_HANDLE, handle);
+MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_open, HTTP_CLIENT_HANDLE, handle, const char*, host, int, port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK, on_connect, void*, callback_ctx);
+MOCKABLE_FUNCTION(, void, uhttp_client_close, HTTP_CLIENT_HANDLE, handle, ON_HTTP_CLOSED_CALLBACK, on_close_callback, void*, callback_ctx);
+MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_execute_request, HTTP_CLIENT_HANDLE, handle, HTTP_CLIENT_REQUEST_TYPE, request_type, const char*, relative_path,
+    HTTP_HEADERS_HANDLE, http_header_handle, const unsigned char*, content, size_t, content_length, ON_HTTP_REQUEST_CALLBACK, on_request_callback, void*, callback_ctx);
+
+MOCKABLE_FUNCTION(, void, uhttp_client_dowork, HTTP_CLIENT_HANDLE, handle);
+
+MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trace, HTTP_CLIENT_HANDLE, handle, bool, trace_on, bool, trace_data);
+MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_X509_cert, HTTP_CLIENT_HANDLE, handle, bool, ecc_type, const char*, certificate, const char*, private_key);
+MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_trusted_cert, HTTP_CLIENT_HANDLE, handle, const char*, certificate);
+MOCKABLE_FUNCTION(, const char*, uhttp_client_get_trusted_cert, HTTP_CLIENT_HANDLE, handle);
+MOCKABLE_FUNCTION(, HTTP_CLIENT_RESULT, uhttp_client_set_option, HTTP_CLIENT_HANDLE, handle, const char*, optionName, const void*, value);
+MOCKABLE_FUNCTION(, XIO_HANDLE, uhttp_client_get_underlying_xio, HTTP_CLIENT_HANDLE, handle);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* UHTTP_H */
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/src/uhttp.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/src/uhttp.c	(revision 464)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/deps/uhttp/src/uhttp.c	(revision 464)
@@ -0,0 +1,1534 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <ctype.h>
+
+#include "umock_c/umock_c_prod.h"
+#include "azure_c_shared_utility/gballoc.h"
+
+#include <string.h>
+#include "azure_uhttp_c/uhttp.h"
+#include "azure_c_shared_utility/httpheaders.h"
+#include "azure_c_shared_utility/crt_abstractions.h"
+#include "azure_c_shared_utility/strings.h"
+#include "azure_c_shared_utility/xlogging.h"
+#include "azure_c_shared_utility/buffer_.h"
+#include "azure_c_shared_utility/singlylinkedlist.h"
+#include "azure_c_shared_utility/shared_util_options.h"
+#include "azure_c_shared_utility/optimize_size.h"
+
+#define MAX_HOSTNAME        64
+#define TIME_MAX_BUFFER     16
+#define HTTP_CRLF_LEN       2
+#define HTTP_END_TOKEN_LEN  4
+#define MAX_CONTENT_LENGTH  16
+
+static const char* HTTP_REQUEST_LINE_FMT = "%s %s HTTP/1.1\r\n";
+static const char* HTTP_HOST = "Host";
+// The following header names MUST be lowercase as they are used for HTTP response header comparison:
+static const char* HTTP_CONTENT_LEN = "content-length";
+static const char* HTTP_TRANSFER_ENCODING = "transfer-encoding";
+static const char* HTTP_CRLF_VALUE = "\r\n";
+
+typedef enum RESPONSE_MESSAGE_STATE_TAG
+{
+    state_initial,
+    state_opening,
+    state_open,
+    state_process_status_line,
+    state_process_headers,
+    state_process_body,
+    state_process_chunked_body,
+
+    state_send_user_callback,
+    state_parse_complete,
+
+    state_closing,
+    state_closed,
+    state_error
+} RESPONSE_MESSAGE_STATE;
+
+typedef struct HTTP_RECV_DATA_TAG
+{
+    ON_HTTP_REQUEST_CALLBACK on_request_callback;
+    void* user_ctx;
+    int status_code;
+    RESPONSE_MESSAGE_STATE recv_state;
+    HTTP_HEADERS_HANDLE resp_header;
+    BUFFER_HANDLE msg_body;
+    size_t total_body_len;
+    BUFFER_HANDLE accrual_buff;
+    bool chunked_reply;
+} HTTP_RECV_DATA;
+
+typedef struct HTTP_CLIENT_HANDLE_DATA_TAG
+{
+    XIO_HANDLE xio_handle;
+    ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect;
+    void* connect_user_ctx;
+    ON_HTTP_ERROR_CALLBACK on_error;
+    void* error_user_ctx;
+    ON_HTTP_CLOSED_CALLBACK on_close_callback;
+    void* close_user_ctx;
+    HTTP_RECV_DATA recv_msg;
+    bool chunk_request;
+    bool trace_on;
+    bool trace_body;
+    char* host_name;
+    int port_num;
+    SINGLYLINKEDLIST_HANDLE data_list;
+    bool cert_type_ecc;
+    char* x509_cert;
+    char* x509_pk;
+    char* certificate;
+    int connected;
+} HTTP_CLIENT_HANDLE_DATA;
+
+typedef struct HTTP_SEND_DATA_TAG
+{
+    HTTP_CLIENT_REQUEST_TYPE request_type;
+    STRING_HANDLE relative_path;
+    STRING_HANDLE header_line;
+    BUFFER_HANDLE content;
+} HTTP_SEND_DATA;
+
+static void send_complete_callback(void* context, IO_SEND_RESULT send_result)
+{
+    (void)context;(void)send_result;
+}
+
+static int initialize_received_data(HTTP_CLIENT_HANDLE_DATA* http_data)
+{
+    int result = 0;
+
+    // Initialize data if necessary
+    if (http_data->recv_msg.resp_header == NULL)
+    {
+        http_data->recv_msg.resp_header = HTTPHeaders_Alloc();
+        if (http_data->recv_msg.resp_header == NULL)
+        {
+            /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
+            LogError("Failure creating Http header.");
+            result = MU_FAILURE;
+        }
+    }
+    if (result == 0 && http_data->recv_msg.accrual_buff == NULL)
+    {
+        http_data->recv_msg.accrual_buff = BUFFER_new();
+        if (http_data->recv_msg.accrual_buff == NULL)
+        {
+            /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
+            LogError("Failure creating accrual buffer.");
+            result = MU_FAILURE;
+        }
+    }
+    http_data->recv_msg.chunked_reply = false;
+    return result;
+}
+
+static int process_status_code_line(const unsigned char* buffer, size_t len, size_t* position, int* statusLen)
+{
+    int result = MU_FAILURE;
+    size_t index;
+    int spaceFound = 0;
+    const char* initSpace = NULL;
+    char status_code[4] = { 0 };
+
+    for (index = 0; index < len; index++)
+    {
+        if (buffer[index] == ' ')
+        {
+            if (spaceFound == 1)
+            {
+                (void)memcpy(status_code, initSpace, 3);
+                status_code[3] = '\0';
+            }
+            else
+            {
+                initSpace = (const char*)buffer + index + 1;
+            }
+            spaceFound++;
+        }
+        else if (buffer[index] == '\n')
+        {
+            *statusLen = (int)atol(status_code);
+            if (index < len)
+            {
+                *position = index + 1;
+            }
+            else
+            {
+                *position = index;
+            }
+            result = 0;
+            break;
+        }
+    }
+    return result;
+}
+
+static int process_header_line(const unsigned char* buffer, size_t len, size_t* position, HTTP_HEADERS_HANDLE resp_header, size_t* contentLen, bool* isChunked)
+{
+    int result = MU_FAILURE;
+    size_t index;
+    const unsigned char* targetPos = buffer;
+    bool crlfEncounted = false;
+    bool colonEncountered = false;
+    char* headerKey = NULL;
+    bool continueProcessing = true;
+
+    for (index = 0; index < len && continueProcessing; index++)
+    {
+        if (buffer[index] == ':' && !colonEncountered)
+        {
+            colonEncountered = true;
+            size_t keyLen = (&buffer[index]) - targetPos;
+
+            if (keyLen == 0)
+            {
+                LogError("Invalid header name with zero length.");
+                result = MU_FAILURE;
+                continueProcessing = false;
+            }
+            else
+            {
+                if (headerKey != NULL)
+                {
+                    free(headerKey);
+                    headerKey = NULL;
+                }
+                headerKey = (char*)malloc(keyLen + 1);
+                if (headerKey == NULL)
+                {
+                    result = MU_FAILURE;
+                    continueProcessing = false;
+                }
+                else
+                {
+                    memcpy(headerKey, targetPos, keyLen);
+                    headerKey[keyLen] = '\0';
+
+                    // Convert to lower case
+                    for (size_t inner = 0; inner < keyLen; inner++)
+                    {
+                        headerKey[inner] = (char)tolower(headerKey[inner]);
+                    }
+
+                    targetPos = buffer+index+1;
+                    crlfEncounted = false;
+                }
+            }
+        }
+        else if (buffer[index] == '\r')
+        {
+            if (headerKey != NULL)
+            {
+                // Remove leading spaces
+                while (*targetPos == 32) { targetPos++; }
+
+                size_t valueLen = (&buffer[index])-targetPos;
+                char* headerValue = (char*)malloc(valueLen+1);
+                if (headerValue == NULL)
+                {
+                    result = MU_FAILURE;
+                    continueProcessing = false;
+                }
+                else
+                {
+                    memcpy(headerValue, targetPos, valueLen);
+                    headerValue[valueLen] = '\0';
+
+                    if (HTTPHeaders_AddHeaderNameValuePair(resp_header, headerKey, headerValue) != HTTP_HEADERS_OK)
+                    {
+                        result = MU_FAILURE;
+                        continueProcessing = false;
+                    }
+                    else
+                    {
+                        if (strcmp(headerKey, HTTP_CONTENT_LEN) == 0)
+                        {
+                            *isChunked = false;
+                            *contentLen = atol(headerValue);
+                        }
+                        else if (strcmp(headerKey, HTTP_TRANSFER_ENCODING) == 0)
+                        {
+                            *isChunked = true;
+                            *contentLen = 0;
+                        }
+                        if (index < len)
+                        {
+                            *position = index;
+                        }
+                        else
+                        {
+                            *position = index-1;
+                        }
+                    }
+                }
+                free(headerKey);
+                headerKey = NULL;
+                free(headerValue);
+            }
+        }
+        else if (buffer[index] == '\n')
+        {
+            if (crlfEncounted)
+            {
+                if (index < len)
+                {
+                    *position = index+1;
+                }
+                else
+                {
+                    *position = index;
+                }
+                result = 0;
+                break;
+            }
+            else
+            {
+                colonEncountered = false;
+                crlfEncounted = true;
+                targetPos = buffer+index+1;
+            }
+        }
+        else
+        {
+            crlfEncounted = false;
+        }
+    }
+    if (headerKey != NULL)
+    {
+        free(headerKey);
+    }
+    return result;
+}
+
+static int write_text_line(HTTP_CLIENT_HANDLE_DATA* http_data, const char* text_line)
+{
+    int result;
+    if (xio_send(http_data->xio_handle, text_line, strlen(text_line), send_complete_callback, NULL) != 0)
+    {
+        LogError("Failure calling xio_send.");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        result = 0;
+        if (http_data->trace_on)
+        {
+            LOG(AZ_LOG_TRACE, LOG_LINE, "%s", text_line);
+        }
+    }
+    return result;
+}
+
+static int write_data_line(HTTP_CLIENT_HANDLE_DATA* http_data, const unsigned char* data_line, size_t length)
+{
+    int result;
+    if (xio_send(http_data->xio_handle, data_line, length, send_complete_callback, NULL) != 0)
+    {
+        LogError("Failure calling xio_send.");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        result = 0;
+        if (http_data->trace_on)
+        {
+            if (length > 0)
+            {
+                if (http_data->trace_body)
+                {
+                    LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n%.*s\r\n", (int)length, (int)length, data_line);
+                }
+                else
+                {
+                    LOG(AZ_LOG_TRACE, LOG_LINE, "<data> len: %d\r\n", (int)length);
+                }
+            }
+            else
+            {
+                LOG(AZ_LOG_TRACE, LOG_LINE, "len: %d\r\n", (int)length);
+            }
+        }
+    }
+    return result;
+}
+
+static int convert_char_to_hex(const unsigned char* hexText, size_t len)
+{
+    int result = 0;
+    for (size_t index = 0; index < len; index++)
+    {
+        if (hexText[index] == ';')
+        {
+            break;
+        }
+        else
+        {
+            int accumulator = 0;
+            if (hexText[index] >= 48 && hexText[index] <= 57)
+            {
+                accumulator = hexText[index] - 48;
+            }
+            else if (hexText[index] >= 65 && hexText[index] <= 70)
+            {
+                accumulator = hexText[index] - 55;
+            }
+            else if (hexText[index] >= 97 && hexText[index] <= 102)
+            {
+                accumulator = hexText[index] - 87;
+            }
+            if (index > 0)
+            {
+                result = result << 4;
+            }
+            result += accumulator;
+        }
+    }
+    return result;
+}
+
+static int setup_init_recv_msg(HTTP_RECV_DATA* recv_msg)
+{
+    int result;
+    recv_msg->status_code = 0;
+    recv_msg->recv_state = state_initial;
+    recv_msg->total_body_len = 0;
+    if (recv_msg->resp_header != NULL)
+    {
+        HTTPHeaders_Free(recv_msg->resp_header);
+    }
+    if (recv_msg->msg_body != NULL)
+    {
+        BUFFER_delete(recv_msg->msg_body);
+    }
+    if ((recv_msg->resp_header = HTTPHeaders_Alloc()) == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */
+        LogError("Failure allocating http http_data items");
+        result = HTTP_CLIENT_ERROR;
+    }
+    else if ((recv_msg->msg_body = BUFFER_new()) == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */
+        LogError("Failure allocating http data items");
+        HTTPHeaders_Free(recv_msg->resp_header);
+        recv_msg->resp_header = NULL;
+        result = HTTP_CLIENT_ERROR;
+    }
+    else
+    {
+        result = 0;
+    }
+    return result;
+}
+
+static void on_bytes_received(void* context, const unsigned char* buffer, size_t len)
+{
+    HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
+
+    if (http_data != NULL && buffer != NULL && len > 0 && http_data->recv_msg.recv_state != state_error)
+    {
+        if (http_data->recv_msg.recv_state == state_parse_complete)
+        {
+            // The callback is getting called during a new send.
+            setup_init_recv_msg(&http_data->recv_msg);
+
+        }
+
+        if (http_data->recv_msg.recv_state == state_initial || http_data->recv_msg.recv_state == state_open)
+        {
+            if (initialize_received_data(http_data) != 0)
+            {
+                http_data->recv_msg.recv_state = state_error;
+            }
+            else
+            {
+                http_data->recv_msg.recv_state = state_process_status_line;
+            }
+        }
+
+        // Put the data in the buffer
+        if (BUFFER_append_build(http_data->recv_msg.accrual_buff, buffer, len) != 0)
+        {
+            /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the state to error. ] */
+            LogError("Failure appending bytes to buffer.");
+            http_data->recv_msg.recv_state = state_error;
+        }
+
+        if (http_data->recv_msg.recv_state == state_process_status_line)
+        {
+            size_t index = 0;
+            const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff);
+            size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
+
+            int lineComplete = process_status_code_line(stored_bytes, stored_len, &index, &http_data->recv_msg.status_code);
+            if (lineComplete == 0 && http_data->recv_msg.status_code > 0)
+            {
+                if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0)
+                {
+                    LogError("Failure appending bytes to buffer.");
+                    http_data->recv_msg.recv_state = state_error;
+                }
+                else
+                {
+                    http_data->recv_msg.recv_state = state_process_headers;
+                }
+            }
+        }
+
+        if (http_data->recv_msg.recv_state == state_process_headers)
+        {
+            size_t index = 0;
+            const unsigned char* stored_bytes = BUFFER_u_char(http_data->recv_msg.accrual_buff);
+            size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
+
+            int headerComplete = process_header_line(stored_bytes, stored_len, &index, http_data->recv_msg.resp_header, &http_data->recv_msg.total_body_len, &http_data->recv_msg.chunked_reply);
+            if (headerComplete == 0)
+            {
+                if (http_data->recv_msg.total_body_len == 0)
+                {
+                    if (http_data->recv_msg.chunked_reply)
+                    {
+
+                        /* Codes_SRS_UHTTP_07_054: [ If the http header does not include a content length then it indicates a chunk response. ] */
+                        http_data->recv_msg.recv_state = state_process_chunked_body;
+                    }
+                    else
+                    {
+                        // Content len is 0 so we are finished with the body
+                        http_data->recv_msg.recv_state = state_send_user_callback;
+                    }
+                }
+                else
+                {
+                    http_data->recv_msg.recv_state = state_process_body;
+                }
+            }
+            if (index > 0)
+            {
+                if (BUFFER_shrink(http_data->recv_msg.accrual_buff, index, false) != 0)
+                {
+                    LogError("Failure appending bytes to buffer.");
+                    http_data->recv_msg.recv_state = state_error;
+                }
+            }
+        }
+
+        if (http_data->recv_msg.recv_state == state_process_body)
+        {
+            if (http_data->recv_msg.total_body_len != 0)
+            {
+                size_t stored_len = BUFFER_length(http_data->recv_msg.accrual_buff);
+
+                if ((http_data->recv_msg.total_body_len == stored_len) || (http_data->recv_msg.total_body_len == (stored_len - HTTP_END_TOKEN_LEN)))
+                {
+                    if (http_data->recv_msg.msg_body != NULL)
+                    {
+                        BUFFER_delete(http_data->recv_msg.msg_body);
+                    }
+                    if ((http_data->recv_msg.msg_body = BUFFER_clone(http_data->recv_msg.accrual_buff)) == NULL)
+                    {
+                        LogError("Failure cloning BUFFER.");
+                        http_data->recv_msg.recv_state = state_error;
+                    }
+                    else
+                    {
+                        http_data->recv_msg.recv_state = state_send_user_callback;
+                    }
+                }
+                else if (stored_len > http_data->recv_msg.total_body_len)
+                {
+                    LogError("Failure bytes encountered is greater then body length.");
+                    http_data->recv_msg.recv_state = state_error;
+                }
+            }
+        }
+
+        if (http_data->recv_msg.recv_state == state_process_chunked_body)
+        {
+            const unsigned char* iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff);
+            const unsigned char* initial_pos = iterator;
+            const unsigned char* begin = iterator;
+            const unsigned char* end = iterator;
+            size_t accural_len = BUFFER_length(http_data->recv_msg.accrual_buff);
+
+            /* Codes_SRS_UHTTP_07_059: [ on_bytes_received shall loop throught the stored data to find the /r/n separator. ] */
+            while (iterator < (initial_pos + accural_len))
+            {
+                if (*iterator == '\r')
+                {
+                    // Don't need anything
+                    end = iterator;
+                    iterator++;
+                }
+                else if (*iterator == '\n')
+                {
+                    size_t data_length = 0;
+
+                    /* Codes_SRS_UHTTP_07_055: [ on_bytes_received shall convert the hexs length supplied in the response to the data length of the chunked data. ] */
+                    size_t hex_len = end - begin;
+                    data_length = convert_char_to_hex(begin, hex_len);
+                    if (data_length == 0)
+                    {
+                        if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)
+                        {
+                            http_data->recv_msg.recv_state = state_send_user_callback;
+                        }
+                        else
+                        {
+                            // Need to continue parsing
+                            http_data->recv_msg.recv_state = state_process_headers;
+                        }
+                        break;
+                    }
+                    else if ((data_length + HTTP_CRLF_LEN) < accural_len - (iterator - initial_pos))
+                    {
+                        /* Codes_SRS_UHTTP_07_056: [ After the response chunk is parsed it shall be placed in a BUFFER_HANDLE. ] */
+                        iterator += 1;
+                        if (BUFFER_append_build(http_data->recv_msg.msg_body, iterator, data_length) != 0)
+                        {
+                            /* Codes_SRS_UHTTP_07_048: [ If any error is encountered on_bytes_received shall set the stop processing the request. ] */
+                            LogError("Failure building buffer for chunked data.");
+                            http_data->recv_msg.recv_state = state_error;
+                        }
+                        else
+                        {
+                            /* Codes_SRS_UHTTP_07_060: [ if the data_length specified in the chunk is beyond the amount of data recieved, the parsing shall end and wait for more data. ] */
+                            if (iterator + (data_length + HTTP_CRLF_LEN) > initial_pos + accural_len)
+                            {
+                                LogError("Invalid length specified.");
+                                http_data->recv_msg.recv_state = state_error;
+                                break;
+                            }
+                            else if (iterator + (data_length + HTTP_CRLF_LEN) == initial_pos + accural_len)
+                            {
+                                if (BUFFER_shrink(http_data->recv_msg.accrual_buff, accural_len, false) != 0)
+                                {
+                                    LogError("Failure shrinking accural buffer.");
+                                    http_data->recv_msg.recv_state = state_error;
+                                }
+                                break;
+                            }
+                            else
+                            {
+                                // Move the iterator beyond the data we read and the /r/n
+                                iterator += (data_length + HTTP_CRLF_LEN);
+                            }
+
+                            /* Codes_SRS_UHTTP_07_058: [ Once a chunk size value of 0 is encountered on_bytes_received shall call the on_request_callback with the http message ] */
+                            if (*iterator == '0' && (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN))
+                            {
+                                if (accural_len - (iterator - initial_pos + 1) <= HTTP_END_TOKEN_LEN)
+                                {
+                                    http_data->recv_msg.recv_state = state_send_user_callback;
+                                }
+                                else
+                                {
+                                    // Need to continue parsing
+                                    http_data->recv_msg.recv_state = state_process_headers;
+                                }
+                                break;
+                            }
+                            else
+                            {
+                                size_t shrink_len = iterator - initial_pos;
+                                if (shrink_len > 0)
+                                {
+                                    if (BUFFER_shrink(http_data->recv_msg.accrual_buff, shrink_len, false) != 0)
+                                    {
+                                        LogError("Failure shrinking accrual buffer.");
+                                        http_data->recv_msg.recv_state = state_error;
+                                    }
+                                    else
+                                    {
+                                        accural_len = BUFFER_length(http_data->recv_msg.accrual_buff);
+                                        initial_pos = iterator = BUFFER_u_char(http_data->recv_msg.accrual_buff);
+                                    }
+                                }
+                            }
+                        }
+                        begin = end = iterator;
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                else
+                {
+                    end = iterator;
+                    iterator++;
+                }
+            }
+        }
+
+        if (http_data->recv_msg.recv_state == state_send_user_callback || http_data->recv_msg.recv_state == state_error)
+        {
+            const unsigned char* reply_data = NULL;
+            size_t reply_len = 0;
+            HTTP_CALLBACK_REASON http_reason = HTTP_CALLBACK_REASON_OK;
+            if (http_data->recv_msg.msg_body != NULL)
+            {
+                reply_data = BUFFER_u_char(http_data->recv_msg.msg_body);
+                reply_len = BUFFER_length(http_data->recv_msg.msg_body);
+            }
+            if (http_data->recv_msg.recv_state == state_error)
+            {
+                http_reason = HTTP_CALLBACK_REASON_PARSING_ERROR;
+            }
+            if (http_data->trace_on)
+            {
+                LOG(AZ_LOG_TRACE, LOG_LINE, "\r\nHTTP Status: %d\r\n", http_data->recv_msg.status_code);
+
+                // Loop through headers
+                size_t count;
+                if (HTTPHeaders_GetHeaderCount(http_data->recv_msg.resp_header, &count) == 0)
+                {
+                    for (size_t index = 0; index < count; index++)
+                    {
+                        char* header;
+                        if (HTTPHeaders_GetHeader(http_data->recv_msg.resp_header, index, &header) == HTTP_HEADERS_OK)
+                        {
+                            LOG(AZ_LOG_TRACE, LOG_LINE, "%s", header);
+                            free(header);
+                        }
+                    }
+                }
+                if (http_data->trace_body && reply_len > 0)
+                {
+                    LOG(AZ_LOG_TRACE, LOG_LINE, "\r\n%.*s\r\n", (int)reply_len, reply_data);
+                }
+            }
+            http_data->recv_msg.on_request_callback(http_data->recv_msg.user_ctx, http_reason, reply_data, reply_len, http_data->recv_msg.status_code, http_data->recv_msg.resp_header);
+            http_data->recv_msg.recv_state = state_parse_complete;
+        }
+
+        if (http_data->recv_msg.recv_state == state_parse_complete)
+        {
+            HTTPHeaders_Free(http_data->recv_msg.resp_header);
+            http_data->recv_msg.resp_header = NULL;
+            BUFFER_delete(http_data->recv_msg.msg_body);
+            http_data->recv_msg.msg_body = NULL;
+            BUFFER_delete(http_data->recv_msg.accrual_buff);
+            http_data->recv_msg.accrual_buff = NULL;
+        }
+    }
+}
+
+static void on_xio_close_complete(void* context)
+{
+    if (context != NULL)
+    {
+        /* Codes_SRS_UHTTP_07_045: [ If on_close_callback is not NULL, on_close_callback shall be called once the underlying xio is closed. ] */
+        HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
+        if (http_data->on_close_callback)
+        {
+            http_data->on_close_callback(http_data->close_user_ctx);
+        }
+        http_data->recv_msg.recv_state = state_closed;
+        http_data->connected = 0;
+    }
+}
+
+static void on_xio_open_complete(void* context, IO_OPEN_RESULT open_result)
+{
+    /* Codes_SRS_UHTTP_07_049: [ If not NULL uhttp_client_open shall call the on_connect callback with the callback_ctx, once the underlying xio's open is complete. ] */
+    if (context != NULL)
+    {
+        HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
+        if (open_result == IO_OPEN_OK)
+        {
+            /* Codes_SRS_UHTTP_07_042: [ If the underlying socket opens successfully the on_connect callback shall be call with HTTP_CALLBACK_REASON_OK... ] */
+            if (http_data->on_connect != NULL)
+            {
+                http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OK);
+            }
+            http_data->recv_msg.recv_state = state_open;
+            http_data->connected = 1;
+        }
+        else
+        {
+            /* Codes_SRS_UHTTP_07_043: [ Otherwise on_connect callback shall be call with HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */
+            if (http_data->on_connect != NULL)
+            {
+                http_data->on_connect(http_data->connect_user_ctx, HTTP_CALLBACK_REASON_OPEN_FAILED);
+            }
+        }
+    }
+    else
+    {
+        LogError("Context on_xio_open_complete is NULL");
+    }
+}
+
+static void on_io_error(void* context)
+{
+    /* Codes_SRS_UHTTP_07_050: [ if context is NULL on_io_error shall do nothing. ] */
+    if (context != NULL)
+    {
+        HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)context;
+        /* Codes_SRS_UHTTP_07_051: [ if on_error callback is not NULL, on_io_error shall call on_error callback. ] */
+        if (http_data->on_error)
+        {
+            http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_ERROR);
+        }
+        http_data->connected = 0;
+    }
+    else
+    {
+        LogError("Context on_io_error is NULL");
+    }
+}
+
+static int construct_http_headers(HTTP_HEADERS_HANDLE http_header, size_t content_len, STRING_HANDLE buffData, bool chunk_data, const char* hostname, int port_num)
+{
+    (void)chunk_data;
+    int result = 0;
+    size_t headerCnt = 0;
+    if ( (http_header != NULL) && HTTPHeaders_GetHeaderCount(http_header, &headerCnt) != HTTP_HEADERS_OK)
+    {
+        LogError("Failed in HTTPHeaders_GetHeaderCount");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        bool hostname_found = false;
+        for (size_t index = 0; index < headerCnt && result == 0; index++)
+        {
+            char* header;
+            if (HTTPHeaders_GetHeader(http_header, index, &header) != HTTP_HEADERS_OK)
+            {
+                result = MU_FAILURE;
+                LogError("Failed in HTTPHeaders_GetHeader");
+            }
+            else
+            {
+                size_t dataLen = strlen(header)+2;
+                char* sendData = malloc(dataLen+1);
+                if (sendData == NULL)
+                {
+                    result = MU_FAILURE;
+                    LogError("Failed in allocating header data");
+                }
+                else
+                {
+                    if (strcmp(header, HTTP_HOST) == 0)
+                    {
+                        hostname_found = true;
+                    }
+
+                    if (snprintf(sendData, dataLen+1, "%s\r\n", header) <= 0)
+                    {
+                        result = MU_FAILURE;
+                        LogError("Failed in constructing header data");
+                    }
+                    else
+                    {
+                        if (STRING_concat(buffData, sendData) != 0)
+                        {
+                            result = MU_FAILURE;
+                            LogError("Failed in building header data");
+                        }
+                    }
+                    free(sendData);
+                }
+                free(header);
+            }
+        }
+        if (!hostname_found)
+        {
+            // calculate the size of the host header
+            size_t host_len = strlen(HTTP_HOST) + strlen(hostname) + MAX_CONTENT_LENGTH + 2;
+            char* host_header = malloc(host_len + 1);
+            if (host_header == NULL)
+            {
+                LogError("Failed allocating host header");
+                result = MU_FAILURE;
+            }
+            else
+            {
+                if (snprintf(host_header, host_len + 1, "%s: %s:%d\r\n", HTTP_HOST, hostname, port_num) <= 0)
+                {
+                    LogError("Failed constructing host header");
+                    result = MU_FAILURE;
+                }
+                else if (STRING_concat(buffData, host_header) != 0)
+                {
+                    LogError("Failed adding the host header to the http item");
+                    result = MU_FAILURE;
+                }
+                free(host_header);
+            }
+        }
+
+        if (result == 0)
+        {
+            /* Codes_SRS_UHTTP_07_015: [uhttp_client_execute_request shall add the Content-Length to the request if the contentLength is > 0] */
+            size_t fmtLen = strlen(HTTP_CONTENT_LEN) + HTTP_CRLF_LEN + 8;
+            char* content = malloc(fmtLen+1);
+            if (content == NULL)
+            {
+                LogError("Failed allocating chunk header");
+                result = MU_FAILURE;
+            }
+            else
+            {
+                /* Codes_SRS_UHTTP_07_015: [on_bytes_received shall add the Content-Length http header item to the request.] */
+                if (sprintf(content, "%s: %u%s", HTTP_CONTENT_LEN, (unsigned int)content_len, HTTP_CRLF_VALUE) <= 0)
+                {
+                    result = MU_FAILURE;
+                    LogError("Failed allocating content len header data");
+                }
+                else
+                {
+                    if (STRING_concat(buffData, content) != 0)
+                    {
+                        result = MU_FAILURE;
+                        LogError("Failed building content len header data");
+                    }
+                }
+                free(content);
+            }
+
+            if (STRING_concat(buffData, "\r\n") != 0)
+            {
+                result = MU_FAILURE;
+                LogError("Failed sending header finalization data");
+            }
+        }
+    }
+    return result;
+}
+
+static STRING_HANDLE construct_http_data(HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path, STRING_HANDLE http_line)
+{
+    STRING_HANDLE result;
+
+    const char* method = (request_type == HTTP_CLIENT_REQUEST_GET) ? "GET"
+        : (request_type == HTTP_CLIENT_REQUEST_OPTIONS) ? "OPTIONS"
+        : (request_type == HTTP_CLIENT_REQUEST_POST) ? "POST"
+        : (request_type == HTTP_CLIENT_REQUEST_PUT) ? "PUT"
+        : (request_type == HTTP_CLIENT_REQUEST_DELETE) ? "DELETE"
+        : (request_type == HTTP_CLIENT_REQUEST_PATCH) ? "PATCH"
+        : NULL;
+    /* Codes_SRS_UHTTP_07_014: [If the request_type is not a valid request http_client_execute_request shall return HTTP_CLIENT_ERROR] */
+    if (method == NULL)
+    {
+        LogError("Invalid request method %s specified", method);
+        result = NULL;
+    }
+    else
+    {
+        size_t buffLen = strlen(HTTP_REQUEST_LINE_FMT) + strlen(method) + strlen(relative_path);
+        char* request = malloc(buffLen+1);
+        if (request == NULL)
+        {
+            result = NULL;
+            LogError("Failure allocating Request data");
+        }
+        else
+        {
+            if (snprintf(request, buffLen + 1, HTTP_REQUEST_LINE_FMT, method, relative_path) <= 0)
+            {
+                result = NULL;
+                LogError("Failure writing request buffer");
+            }
+            else
+            {
+                result = STRING_construct(request);
+                if (result == NULL)
+                {
+                    LogError("Failure creating buffer object");
+                }
+                else if (STRING_concat_with_STRING(result, http_line) != 0)
+                {
+                    STRING_delete(result);
+                    result = NULL;
+                    LogError("Failure writing request buffers");
+                }
+            }
+            free(request);
+        }
+    }
+    return result;
+}
+
+static int send_http_data(HTTP_CLIENT_HANDLE_DATA* http_data, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path,
+    STRING_HANDLE http_line)
+{
+    int result;
+    STRING_HANDLE transmit_data = construct_http_data(request_type, relative_path, http_line);
+    if (transmit_data == NULL)
+    {
+        LogError("Failure constructing http data");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        /* Tests_SRS_UHTTP_07_016: [http_client_execute_request shall transmit the http headers data through a call to xio_send;]*/
+        if (write_text_line(http_data, STRING_c_str(transmit_data) ) != 0)
+        {
+            result = MU_FAILURE;
+            LogError("Failure writing request buffer");
+        }
+        else
+        {
+            result = 0;
+        }
+        STRING_delete(transmit_data);
+    }
+    return result;
+}
+
+HTTP_CLIENT_HANDLE uhttp_client_create(const IO_INTERFACE_DESCRIPTION* io_interface_desc, const void* xio_param, ON_HTTP_ERROR_CALLBACK on_http_error, void* callback_ctx)
+{
+    HTTP_CLIENT_HANDLE_DATA* result;
+    /* Codes_SRS_UHTTP_07_002: [If io_interface_desc is NULL, uhttp_client_create shall return NULL.] */
+    if (io_interface_desc == NULL)
+    {
+        LogError("Invalid Parameter io_interface_desc is NULL");
+        result = NULL;
+    }
+    else
+    {
+        result = malloc(sizeof(HTTP_CLIENT_HANDLE_DATA));
+        if (result == NULL)
+        {
+            /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */
+            LogError("Failure allocating http_client_handle");
+        }
+        else
+        {
+            memset(result, 0, sizeof(HTTP_CLIENT_HANDLE_DATA) );
+            if ((result->data_list = singlylinkedlist_create() ) == NULL)
+            {
+                /* Codes_SRS_UHTTP_07_003: [If uhttp_client_create encounters any error then it shall return NULL] */
+                LogError("Failure allocating data list");
+                free(result);
+                result = NULL;
+            }
+            else if ((result->xio_handle = xio_create(io_interface_desc, xio_param) ) == NULL)
+            {
+                /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */
+                LogError("xio create failed");
+                singlylinkedlist_destroy(result->data_list);
+                free(result);
+                result = NULL;
+            }
+            else
+            {
+                /* Codes_SRS_UHTTP_07_001: [uhttp_client_create shall return an initialize the http client handle.] */
+                result->on_error = on_http_error;
+                result->error_user_ctx = callback_ctx;
+                result->recv_msg.recv_state = state_initial;
+                result->chunk_request = false;
+                result->trace_on = false;
+            }
+        }
+    }
+    return (HTTP_CLIENT_HANDLE)result;
+}
+
+void uhttp_client_destroy(HTTP_CLIENT_HANDLE handle)
+{
+    /* Codes_SRS_UHTTP_07_004: [ If handle is NULL then uhttp_client_destroy shall do nothing ] */
+    if (handle != NULL)
+    {
+        /* Codes_SRS_UHTTP_07_005: [uhttp_client_destroy shall free any resource that is allocated in this translation unit] */
+        if(handle->host_name != NULL)
+        {
+            free(handle->host_name);
+            handle->host_name = NULL;
+        }
+        singlylinkedlist_destroy(handle->data_list);
+        xio_destroy(handle->xio_handle);
+        free(handle->certificate);
+        free(handle->x509_pk);
+        free(handle->x509_cert);
+        free(handle);
+    }
+}
+
+HTTP_CLIENT_RESULT uhttp_client_open(HTTP_CLIENT_HANDLE handle, const char* host, int port_num, ON_HTTP_OPEN_COMPLETE_CALLBACK on_connect, void* callback_ctx)
+{
+    HTTP_CLIENT_RESULT result;
+    if (handle == NULL || host == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_006: [If handle, io_interface_desc or host is NULL then `uhttp_client_open` shall return HTTP_CLIENT_INVALID_ARG] */
+        LogError("Invalid handle value");
+        result = HTTP_CLIENT_INVALID_ARG;
+    }
+    else
+    {
+        HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
+
+        if ((http_data->recv_msg.recv_state != state_initial) &&
+            (http_data->recv_msg.recv_state != state_error) &&
+            (http_data->recv_msg.recv_state != state_closed))
+        {
+            LogError("Unable to open previously open client.");
+            result = HTTP_CLIENT_INVALID_STATE;
+        }
+        else
+        {
+            if (http_data->host_name != NULL)
+            {
+                free(http_data->host_name);
+                handle->host_name = NULL;
+            }
+
+            if (mallocAndStrcpy_s(&http_data->host_name, host) != 0)
+            {
+                LogError("copying hostname has failed");
+                result = HTTP_CLIENT_ERROR;
+            }
+            /* Codes_SRS_UHTTP_07_007: [http_client_connect shall attempt to open the xio_handle. ] */
+            else
+            {
+                result = HTTP_CLIENT_OK;
+                http_data->recv_msg.recv_state = state_opening;
+                http_data->on_connect = on_connect;
+                http_data->connect_user_ctx = callback_ctx;
+                http_data->port_num = port_num;
+
+                if ((http_data->x509_cert != NULL) && (http_data->x509_pk != NULL))
+                {
+                    if ((xio_setoption(http_data->xio_handle, SU_OPTION_X509_CERT, http_data->x509_cert) != 0) ||
+                        (xio_setoption(http_data->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_data->x509_pk) != 0))
+                    {
+                        LogError("Failed setting x509 certificate");
+                        result = HTTP_CLIENT_ERROR;
+                        free(http_data->host_name);
+                        http_data->host_name = NULL;
+                        http_data->on_connect = NULL;
+                        http_data->connect_user_ctx = NULL;
+                        http_data->port_num = 0;
+                    }
+                }
+
+                if ((result == HTTP_CLIENT_OK) && (http_data->certificate != NULL))
+                {
+                    if (xio_setoption(http_data->xio_handle, OPTION_TRUSTED_CERT, http_data->certificate) != 0)
+                    {
+                        LogError("Failed setting Trusted certificate");
+                        result = HTTP_CLIENT_ERROR;
+                        free(http_data->host_name);
+                        http_data->host_name = NULL;
+                        http_data->on_connect = NULL;
+                        http_data->connect_user_ctx = NULL;
+                        http_data->port_num = 0;
+                    }
+                }
+
+                if (result == HTTP_CLIENT_OK)
+                {
+                    if (xio_open(http_data->xio_handle, on_xio_open_complete, http_data, on_bytes_received, http_data, on_io_error, http_data) != 0)
+                    {
+                        /* Codes_SRS_UHTTP_07_044: [ if a failure is encountered on xio_open uhttp_client_open shall return HTTP_CLIENT_OPEN_REQUEST_FAILED. ] */
+                        LogError("opening xio failed");
+                        free(http_data->host_name);
+                        http_data->host_name = NULL;
+                        http_data->on_connect = NULL;
+                        http_data->connect_user_ctx = NULL;
+                        http_data->port_num = 0;
+                        http_data->recv_msg.recv_state = state_error;
+
+                        result = HTTP_CLIENT_OPEN_FAILED;
+                    }
+                    else
+                    {
+                        /* Codes_SRS_UHTTP_07_008: [If http_client_connect succeeds then it shall return HTTP_CLIENT_OK] */
+                        result = HTTP_CLIENT_OK;
+                    }
+                }
+            }
+        }
+    }
+    return result;
+}
+
+void uhttp_client_close(HTTP_CLIENT_HANDLE handle, ON_HTTP_CLOSED_CALLBACK on_close_callback, void* callback_ctx)
+{
+    HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
+    /* Codes_SRS_UHTTP_07_009: [If handle is NULL then http_client_close shall do nothing] */
+    /* Codes_SRS_UHTTP_07_049: [ If the state has been previously set to state_closed, uhttp_client_close shall do nothing. ] */
+    if (http_data != NULL && http_data->recv_msg.recv_state != state_closed && http_data->recv_msg.recv_state != state_closing)
+    {
+        http_data->on_close_callback = on_close_callback;
+        http_data->close_user_ctx = callback_ctx;
+        /* Codes_SRS_UHTTP_07_010: [If the xio_handle is NOT NULL http_client_close shall call xio_close to close the handle] */
+        (void)xio_close(http_data->xio_handle, on_xio_close_complete, http_data);
+
+        LIST_ITEM_HANDLE pending_list_item;
+        while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL)
+        {
+            HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item);
+            if (send_data != NULL)
+            {
+                STRING_delete(send_data->relative_path);
+                BUFFER_delete(send_data->content);
+                STRING_delete(send_data->header_line);
+                free(send_data);
+            }
+            singlylinkedlist_remove(http_data->data_list, pending_list_item);
+        }
+
+        http_data->recv_msg.status_code = 0;
+        http_data->recv_msg.recv_state = state_closed;
+        http_data->recv_msg.total_body_len = 0;
+        free(http_data->host_name);
+        http_data->host_name = NULL;
+
+        /* Codes_SRS_UHTTP_07_011: [http_client_close shall free any HTTPHeader object that has not been freed] */
+        if (http_data->recv_msg.resp_header != NULL)
+        {
+            HTTPHeaders_Free(http_data->recv_msg.resp_header);
+            http_data->recv_msg.resp_header = NULL;
+        }
+        if (http_data->recv_msg.msg_body != NULL)
+        {
+            BUFFER_delete(http_data->recv_msg.msg_body);
+            http_data->recv_msg.msg_body = NULL;
+        }
+    }
+}
+
+HTTP_CLIENT_RESULT uhttp_client_execute_request(HTTP_CLIENT_HANDLE handle, HTTP_CLIENT_REQUEST_TYPE request_type, const char* relative_path,
+    HTTP_HEADERS_HANDLE http_header_handle, const unsigned char* content, size_t content_len, ON_HTTP_REQUEST_CALLBACK on_request_callback, void* callback_ctx)
+{
+    HTTP_CLIENT_RESULT result;
+    LIST_ITEM_HANDLE list_item;
+
+    /* Codes_SRS_UHTTP_07_012: [If handle, relativePath, or httpHeadersHandle is NULL then http_client_execute_request shall return HTTP_CLIENT_INVALID_ARG] */
+    if (handle == NULL || on_request_callback == NULL ||
+        (content != NULL && content_len == 0) || (content == NULL && content_len != 0) )
+    {
+        result = HTTP_CLIENT_INVALID_ARG;
+        LogError("Invalid parameter sent to execute_request");
+    }
+    else
+    {
+        HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
+
+        http_data->recv_msg.on_request_callback = on_request_callback;
+        http_data->recv_msg.user_ctx = callback_ctx;
+        if (setup_init_recv_msg(&http_data->recv_msg) != 0)
+        {
+            /* Codes_SRS_UHTTP_07_017: [If any failure encountered http_client_execute_request shall return HTTP_CLIENT_ERROR] */
+            LogError("Failure allocating http http_data items");
+            result = HTTP_CLIENT_ERROR;
+        }
+        else
+        {
+            HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)malloc(sizeof(HTTP_SEND_DATA));
+            if (send_data == NULL)
+            {
+                LogError("Failure allocating http data items");
+                BUFFER_delete(http_data->recv_msg.msg_body);
+                http_data->recv_msg.msg_body = NULL;
+                HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                http_data->recv_msg.resp_header = NULL;
+                result = HTTP_CLIENT_ERROR;
+            }
+            else
+            {
+                memset(send_data, 0, sizeof(HTTP_SEND_DATA));
+                /* Codes_SRS_UHTTP_07_041: [HTTP_CLIENT_REQUEST_TYPE shall support all request types specified under section 9.1.2 in the spec.] */
+                send_data->request_type = request_type;
+                if ( (content_len > 0) && (send_data->content = BUFFER_create(content, content_len)) == NULL)
+                {
+                    LogError("Failure allocating content buffer");
+                    result = HTTP_CLIENT_ERROR;
+                    BUFFER_delete(http_data->recv_msg.msg_body);
+                    http_data->recv_msg.msg_body = NULL;
+                    HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                    http_data->recv_msg.resp_header = NULL;
+                    free(send_data);
+                }
+                else if ((send_data->header_line = STRING_new()) == NULL)
+                {
+                    LogError("Failure allocating content buffer");
+                    result = HTTP_CLIENT_ERROR;
+                    BUFFER_delete(send_data->content);
+                    BUFFER_delete(http_data->recv_msg.msg_body);
+                    http_data->recv_msg.msg_body = NULL;
+                    HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                    http_data->recv_msg.resp_header = NULL;
+                    free(send_data);
+                }
+                else if (construct_http_headers(http_header_handle, content_len, send_data->header_line, false, http_data->host_name, http_data->port_num))
+                {
+                    LogError("Failure allocating content buffer");
+                    result = HTTP_CLIENT_ERROR;
+                    BUFFER_delete(send_data->content);
+                    STRING_delete(send_data->header_line);
+                    BUFFER_delete(http_data->recv_msg.msg_body);
+                    http_data->recv_msg.msg_body = NULL;
+                    HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                    http_data->recv_msg.resp_header = NULL;
+                    free(send_data);
+                }
+                else if ((list_item = singlylinkedlist_add(http_data->data_list, send_data)) == NULL)
+                {
+                    STRING_delete(send_data->header_line);
+                    BUFFER_delete(send_data->content);
+                    LogError("Failure adding send data to list");
+                    result = HTTP_CLIENT_ERROR;
+                    BUFFER_delete(http_data->recv_msg.msg_body);
+                    http_data->recv_msg.msg_body = NULL;
+                    HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                    http_data->recv_msg.resp_header = NULL;
+                    free(send_data);
+                }
+                else
+                {
+                    if (relative_path != NULL)
+                    {
+                        if ((send_data->relative_path = STRING_construct(relative_path)) == NULL)
+                        {
+                            (void)singlylinkedlist_remove(http_data->data_list, list_item);
+                            STRING_delete(send_data->header_line);
+                            BUFFER_delete(send_data->content);
+                            LogError("Failure allocating relative path buffer");
+                            BUFFER_delete(http_data->recv_msg.msg_body);
+                            http_data->recv_msg.msg_body = NULL;
+                            HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                            http_data->recv_msg.resp_header = NULL;
+                            free(send_data);
+                            result = HTTP_CLIENT_ERROR;
+                        }
+                        else
+                        {
+                            /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */
+                            result = HTTP_CLIENT_OK;
+                        }
+                    }
+                    else
+                    {
+                        if ((send_data->relative_path = STRING_construct("/")) == NULL)
+                        {
+                            (void)singlylinkedlist_remove(http_data->data_list, list_item);
+                            STRING_delete(send_data->header_line);
+                            BUFFER_delete(send_data->content);
+                            LogError("Failure allocating relative path buffer");
+                            BUFFER_delete(http_data->recv_msg.msg_body);
+                            http_data->recv_msg.msg_body = NULL;
+                            HTTPHeaders_Free(http_data->recv_msg.resp_header);
+                            http_data->recv_msg.resp_header = NULL;
+                            free(send_data);
+                            result = HTTP_CLIENT_ERROR;
+                        }
+                        else
+                        {
+                            /* Codes_SRS_UHTTP_07_018: [upon success http_client_execute_request shall return HTTP_CLIENT_OK.] */
+                            result = HTTP_CLIENT_OK;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return result;
+}
+
+void uhttp_client_dowork(HTTP_CLIENT_HANDLE handle)
+{
+    if (handle != NULL)
+    {
+        /* Codes_SRS_UHTTP_07_037: [http_client_dowork shall call the underlying xio_dowork function. ] */
+        HTTP_CLIENT_HANDLE_DATA* http_data = (HTTP_CLIENT_HANDLE_DATA*)handle;
+        xio_dowork(http_data->xio_handle);
+
+        // Wait till I'm connected
+        if (handle->connected == 1)
+        {
+            LIST_ITEM_HANDLE pending_list_item;
+            /* Codes_SRS_UHTTP_07_016: [http_client_dowork shall iterate through the queued Data using the xio interface to send the http request in the following ways...] */
+            while ((pending_list_item = singlylinkedlist_get_head_item(http_data->data_list)) != NULL)
+            {
+                HTTP_SEND_DATA* send_data = (HTTP_SEND_DATA*)singlylinkedlist_item_get_value(pending_list_item);
+                if (send_data != NULL)
+                {
+                    size_t content_len = BUFFER_length(send_data->content);
+                    /* Codes_SRS_UHTTP_07_052: [uhttp_client_dowork shall call xio_send to transmits the header information... ] */
+                    if (send_http_data(http_data, send_data->request_type, STRING_c_str(send_data->relative_path), send_data->header_line) != 0)
+                    {
+                        LogError("Failure writing content buffer");
+                        if (http_data->on_error)
+                        {
+                            http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED);
+                        }
+                    }
+                    else if (content_len > 0)
+                    {
+                        /* Codes_SRS_UHTTP_07_053: [ Then uhttp_client_dowork shall use xio_send to transmit the content of the http request. ] */
+                        if (write_data_line(http_data, BUFFER_u_char(send_data->content), content_len) != 0)
+                        {
+                            LogError("Failure writing content buffer");
+                            if (http_data->on_error)
+                            {
+                                http_data->on_error(http_data->error_user_ctx, HTTP_CALLBACK_REASON_SEND_FAILED);
+                            }
+                        }
+                    }
+
+                    /* Codes_SRS_UHTTP_07_046: [ http_client_dowork shall free resouces queued to send to the http endpoint. ] */
+                    STRING_delete(send_data->relative_path);
+                    BUFFER_delete(send_data->content);
+                    STRING_delete(send_data->header_line);
+                    free(send_data);
+                }
+                (void)singlylinkedlist_remove(http_data->data_list, pending_list_item);
+            }
+        }
+    }
+}
+
+HTTP_CLIENT_RESULT uhttp_client_set_trace(HTTP_CLIENT_HANDLE handle, bool trace_on, bool trace_data)
+{
+    HTTP_CLIENT_RESULT result;
+    if (handle == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
+        result = HTTP_CLIENT_INVALID_ARG;
+        LogError("invalid parameter (NULL) passed to http_client_set_trace");
+    }
+    else
+    {
+        /* Codes_SRS_UHTTP_07_039: [http_client_set_trace shall set the HTTP tracing to the trace_on variable.] */
+        handle->trace_on = trace_on;
+        handle->trace_body = trace_data;
+        /* Codes_SRS_UHTTP_07_040: [if http_client_set_trace finishes successfully then it shall return HTTP_CLIENT_OK.] */
+        result = HTTP_CLIENT_OK;
+    }
+    return result;
+}
+
+HTTP_CLIENT_RESULT uhttp_client_set_X509_cert(HTTP_CLIENT_HANDLE handle, bool ecc_type, const char* certificate, const char* private_key)
+{
+    HTTP_CLIENT_RESULT result;
+    if (handle == NULL || certificate == NULL || private_key == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
+        result = HTTP_CLIENT_INVALID_ARG;
+        LogError("invalid parameter handle: %p certificate: %p private_key: %p", handle, certificate, private_key);
+    }
+    else if (handle->recv_msg.recv_state != state_initial)
+    {
+        result = HTTP_CLIENT_INVALID_STATE;
+        LogError("You must set the X509 certificates before opening the connection");
+    }
+    else
+    {
+        handle->cert_type_ecc = ecc_type;
+        if (mallocAndStrcpy_s(&handle->x509_cert, certificate) != 0)
+        {
+            result = HTTP_CLIENT_ERROR;
+            LogError("failure allocating certificate");
+        }
+        else if (mallocAndStrcpy_s(&handle->x509_pk, private_key) != 0)
+        {
+            free(handle->x509_cert);
+            handle->x509_cert = NULL;
+            result = HTTP_CLIENT_ERROR;
+            LogError("failure allocating private key");
+        }
+        else
+        {
+            result = HTTP_CLIENT_OK;
+        }
+    }
+    return result;
+}
+
+HTTP_CLIENT_RESULT uhttp_client_set_trusted_cert(HTTP_CLIENT_HANDLE handle, const char* certificate)
+{
+    HTTP_CLIENT_RESULT result;
+    if (handle == NULL || certificate == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
+        result = HTTP_CLIENT_INVALID_ARG;
+        LogError("invalid parameter handle: %p certificate: %p", handle, certificate);
+    }
+    else if (handle->recv_msg.recv_state != state_initial)
+    {
+        result = HTTP_CLIENT_INVALID_STATE;
+        LogError("You must set the certificates before opening the connection");
+    }
+    else
+    {
+        if (mallocAndStrcpy_s(&handle->certificate, certificate) != 0)
+        {
+            result = HTTP_CLIENT_ERROR;
+            LogError("failure allocating certificate");
+        }
+        else
+        {
+            result = HTTP_CLIENT_OK;
+        }
+    }
+    return result;
+}
+
+const char* uhttp_client_get_trusted_cert(HTTP_CLIENT_HANDLE handle)
+{
+    const char* result;
+    if (handle == NULL)
+    {
+        result = NULL;
+        LogError("invalid parameter NULL handle");
+    }
+    else
+    {
+        result = handle->certificate;
+    }
+    return result;
+}
+
+HTTP_CLIENT_RESULT uhttp_client_set_option(HTTP_CLIENT_HANDLE handle, const char* optionName, const void* value)
+{
+    HTTP_CLIENT_RESULT result;
+    if (handle == NULL)
+    {
+        /* Codes_SRS_UHTTP_07_038: [If handle is NULL then http_client_set_trace shall return HTTP_CLIENT_INVALID_ARG] */
+        result = HTTP_CLIENT_INVALID_ARG;
+        LogError("invalid parameter handle: %p", handle);
+    }
+    else
+    {
+        int setoption_result = xio_setoption(handle->xio_handle, optionName, value);
+        if (setoption_result != 0)
+        {
+            LogError("xio_setoption fails, returns %d", setoption_result);
+            result = HTTP_CLIENT_ERROR;
+        }
+        else
+        {
+            result = HTTP_CLIENT_OK;
+        }
+
+    }
+
+    return result;
+}
+
+XIO_HANDLE uhttp_client_get_underlying_xio(HTTP_CLIENT_HANDLE handle)
+{
+    XIO_HANDLE result;
+    if (handle == NULL)
+    {
+        LogError("invalid parameter handle: %p", handle);
+        result = NULL;
+    }
+    else
+    {
+        result = handle->xio_handle;
+    }
+    return result;
+}
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/internal/iothub_client_authorization.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/internal/iothub_client_authorization.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/internal/iothub_client_authorization.h	(revision 464)
@@ -42,5 +42,5 @@
 MOCKABLE_FUNCTION(, IOTHUB_CREDENTIAL_TYPE, IoTHubClient_Auth_Set_x509_Type, IOTHUB_AUTHORIZATION_HANDLE, handle, bool, enable_x509);
 MOCKABLE_FUNCTION(, IOTHUB_CREDENTIAL_TYPE, IoTHubClient_Auth_Get_Credential_Type, IOTHUB_AUTHORIZATION_HANDLE, handle);
-MOCKABLE_FUNCTION(, char*, IoTHubClient_Auth_Get_SasToken, IOTHUB_AUTHORIZATION_HANDLE, handle, const char*, scope, size_t, expiry_time_relative_seconds, const char*, key_name);
+MOCKABLE_FUNCTION(, char*, IoTHubClient_Auth_Get_SasToken, IOTHUB_AUTHORIZATION_HANDLE, handle, const char*, scope, uint64_t, expiry_time_relative_seconds, const char*, key_name);
 MOCKABLE_FUNCTION(, int, IoTHubClient_Auth_Set_xio_Certificate, IOTHUB_AUTHORIZATION_HANDLE, handle, XIO_HANDLE, xio);
 MOCKABLE_FUNCTION(, const char*, IoTHubClient_Auth_Get_DeviceId, IOTHUB_AUTHORIZATION_HANDLE, handle);
@@ -49,6 +49,6 @@
 MOCKABLE_FUNCTION(, SAS_TOKEN_STATUS, IoTHubClient_Auth_Is_SasToken_Valid, IOTHUB_AUTHORIZATION_HANDLE, handle);
 MOCKABLE_FUNCTION(, int, IoTHubClient_Auth_Get_x509_info, IOTHUB_AUTHORIZATION_HANDLE, handle, char**, x509_cert, char**, x509_key);
-MOCKABLE_FUNCTION(, int, IoTHubClient_Auth_Set_SasToken_Expiry, IOTHUB_AUTHORIZATION_HANDLE, handle, size_t, expiry_time_seconds);
-MOCKABLE_FUNCTION(, size_t, IoTHubClient_Auth_Get_SasToken_Expiry, IOTHUB_AUTHORIZATION_HANDLE, handle);
+MOCKABLE_FUNCTION(, int, IoTHubClient_Auth_Set_SasToken_Expiry, IOTHUB_AUTHORIZATION_HANDLE, handle, uint64_t, expiry_time_seconds);
+MOCKABLE_FUNCTION(, uint64_t, IoTHubClient_Auth_Get_SasToken_Expiry, IOTHUB_AUTHORIZATION_HANDLE, handle);
 
 
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/internal/iothub_internal_consts.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/internal/iothub_internal_consts.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/internal/iothub_internal_consts.h	(revision 464)
@@ -10,9 +10,5 @@
 #endif
 
-    static const char* IOTHUB_API_VERSION = "2019-10-01";
-    // TODO: https://github.com/Azure/azure-iot-sdk-c/issues/1547 tracks removing this preview 
-    // variable once the underlying logic is enabled on all IoTHubs.
-    static const char* IOTHUB_API_PREVIEW_VERSION = "2020-05-31-preview";
-
+    static const char* IOTHUB_API_VERSION = "2020-09-30";
     static const char* SECURITY_INTERFACE_INTERNAL_ID = "iothub-interface-internal-id";
     static const char* SECURITY_INTERFACE_INTERNAL_ID_VALUE = "security*azureiot*com^SecurityAgent^1*0*0";
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client.h	(revision 464)
@@ -2,6 +2,14 @@
 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
+//**********************************************************************
+// NOTE: THIS HEADER IS DEPRECATED
+// 
+// Functions in this header will be maintained for backward compatability.
+// New applications should use iothub_device_client.h.
+// 
+//**********************************************************************
+
 /** @file iothub_client.h
-*    @brief Extends the IoTHubCLient_LL module with additional features.
+*    @brief Extends the IoTHubClient_LL with additional features.
 *
 *    @note  DEPRECATED. New users use iothub_device_client.h for IoTHubClient APIs.
@@ -38,313 +46,90 @@
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing
-    *             IoT Hub using the specified connection string parameter.
-    *
-    * @param    connectionString    Pointer to a character string
-    * @param    protocol            Function pointer for protocol implementation
-    *
-    *            Sample connection string:
-    *                <blockquote>
-    *                    <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre>
-    *                   <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessSignature=SharedAccessSignature sr=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net]/devices/[Device ID goes here]&sig=[SAS Token goes here]&se=[Expiry Time goes here];</pre>
-    *                </blockquote>
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_CreateFromConnectionString is deprecated.  Use IoTHubDeviceClient_CreateFromConnectionString instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol);
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing IoT
-    *             Hub using the specified parameters.
-    *
-    * @param    config    Pointer to an @c IOTHUB_CLIENT_CONFIG structure
-    *
-    *            The API does not allow sharing of a connection across multiple
-    *            devices. This is a blocking call.
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_Create is deprecated.  Use IoTHubDeviceClient_Create instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_Create, const IOTHUB_CLIENT_CONFIG*, config);
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing IoT
-    *             Hub using the specified parameters.
-    *
-    * @param    transportHandle    TRANSPORT_HANDLE which represents a connection.
-    * @param    config    Pointer to an @c IOTHUB_CLIENT_CONFIG structure
-    *
-    *            The API allows sharing of a connection across multiple
-    *            devices. This is a blocking call.
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_CreateWithTransport is deprecated.  Use IoTHubDeviceClient_CreateWithTransport instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateWithTransport, TRANSPORT_HANDLE, transportHandle, const IOTHUB_CLIENT_CONFIG*, config);
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing IoT
-    *             Hub using the device auth module.
-    *
-    * @param    iothub_uri    Pointer to an ioThub hostname received in the registration process
-    * @param    device_id    Pointer to the device Id of the device
-    * @param    protocol    Function pointer for protocol implementation
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_CreateFromDeviceAuth is deprecated.  Use IoTHubDeviceClient_CreateFromDeviceAuth instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_HANDLE, IoTHubClient_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol);
 
     /**
-    * @brief    Disposes of resources allocated by the IoT Hub client. This is a
-    *             blocking call.
-    *
-    * @param    iotHubClientHandle    The handle created by a call to the create function.
+    * @deprecated IoTHubClient_Destroy is deprecated.  Use IoTHubDeviceClient_Destroy instead.
     */
     MOCKABLE_FUNCTION(, void, IoTHubClient_Destroy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle);
 
     /**
-    * @brief    Asynchronous call to send the message specified by @p eventMessageHandle.
-    *
-    * @param    iotHubClientHandle               The handle created by a call to the create function.
-    * @param    eventMessageHandle               The handle to an IoT Hub message.
-    * @param    eventConfirmationCallback      The callback specified by the device for receiving
-    *                                         confirmation of the delivery of the IoT Hub message.
-    *                                         This callback can be expected to invoke the
-    *                                         ::IoTHubClient_SendEventAsync function for the
-    *                                         same message in an attempt to retry sending a failing
-    *                                         message. The user can specify a @c NULL value here to
-    *                                         indicate that no callback is required.
-    * @param    userContextCallback            User specified context that will be provided to the
-    *                                         callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_Destroy function from within any callback.
-    * @remarks
-    *            The IOTHUB_MESSAGE_HANDLE instance provided as argument is copied by the function,
-    *            so this argument can be destroyed by the calling application right after IoTHubClient_SendEventAsync returns.
-    *            The copy of @c eventMessageHandle is later destroyed by the iothub client when the message is effectively sent, if a failure sending it occurs, or if the client is destroyed.
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SendEventAsync is deprecated.  Use IoTHubDeviceClient_SendEventAsync instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SendEventAsync, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback);
 
     /**
-    * @brief    This function returns the current sending status for IoTHubClient.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    iotHubClientStatus        The sending state is populated at the address pointed
-    *                                     at by this parameter. The value will be set to
-    *                                     @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently
-    *                                     no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY
-    *                                     if there are.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_GetSendStatus is deprecated.  Use IoTHubDeviceClient_GetSendStatus instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_GetSendStatus, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus);
 
     /**
-    * @brief    Sets up the message callback to be invoked when IoT Hub issues a
-    *             message to the device. This is a blocking call.
-    *
-    * @param    iotHubClientHandle               The handle created by a call to the create function.
-    * @param    messageCallback                The callback specified by the device for receiving
-    *                                         messages from IoT Hub.
-    * @param    userContextCallback            User specified context that will be provided to the
-    *                                         callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SetMessageCallback is deprecated.  Use IoTHubDeviceClient_SetMessageCallback instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetMessageCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback);
 
     /**
-    * @brief    Sets up the connection status callback to be invoked representing the status of
-    * the connection to IOT Hub. This is a blocking call.
-    *
-    * @param    iotHubClientHandle                       The handle created by a call to the create function.
-    * @param    connectionStatusCallback                The callback specified by the device for receiving
-    *                                                 updates about the status of the connection to IoT Hub.
-    * @param    userContextCallback                    User specified context that will be provided to the
-    *                                                 callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SetConnectionStatusCallback is deprecated.  Use IoTHubDeviceClient_SetConnectionStatusCallback instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetConnectionStatusCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback);
 
     /**
-    * @brief    Sets up the connection status callback to be invoked representing the status of
-    * the connection to IOT Hub. This is a blocking call.
-    *
-    * @param    iotHubClientHandle                       The handle created by a call to the create function.
-    * @param    retryPolicy                             The policy to use to reconnect to IoT Hub when a
-    *                                               connection drops.
-    * @param    retryTimeoutLimitInSeconds            Maximum amount of time(seconds) to attempt reconnection when a
-    *                                               connection drops to IOT Hub.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SetRetryPolicy is deprecated.  Use IoTHubDeviceClient_SetRetryPolicy instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetRetryPolicy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds);
 
     /**
-    * @brief    Sets up the connection status callback to be invoked representing the status of
-    * the connection to IOT Hub. This is a blocking call.
-    *
-    * @param    iotHubClientHandle                       The handle created by a call to the create function.
-    * @param    retryPolicy                             Out parameter containing the policy to use to reconnect to IoT Hub.
-    * @param    retryTimeoutLimitInSeconds            Out parameter containing maximum amount of time in seconds to attempt reconnection
-    to IOT Hub.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_GetRetryPolicy is deprecated.  Use IoTHubDeviceClient_GetRetryPolicy instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_GetRetryPolicy, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds);
 
     /**
-    * @brief    This function returns in the out parameter @p lastMessageReceiveTime
-    *             what was the value of the @c time function when the last message was
-    *             received at the client.
-    *
-    * @param    iotHubClientHandle                The handle created by a call to the create function.
-    * @param    lastMessageReceiveTime          Out parameter containing the value of @c time function
-    *                                             when the last message was received.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_GetLastMessageReceiveTime is deprecated.  Use IoTHubDeviceClient_GetLastMessageReceiveTime instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_GetLastMessageReceiveTime, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime);
 
     /**
-    * @brief    This API sets a runtime option identified by parameter @p optionName
-    *             to a value pointed to by @p value. @p optionName and the data type
-    *             @p value is pointing to are specific for every option.
-    *
-    * @param    iotHubClientHandle    The handle created by a call to the create function.
-    * @param    optionName              Name of the option.
-    * @param    value                  The value.
-    *
-    *            The options that can be set via this API are:
-    *                - @b timeout - the maximum time in milliseconds a communication is
-    *                  allowed to use. @p value is a pointer to an @c unsigned @c int with
-    *                  the timeout value in milliseconds. This is only supported for the HTTP
-    *                  protocol as of now. When the HTTP protocol uses CURL, the meaning of
-    *                  the parameter is <em>total request time</em>. When the HTTP protocol uses
-    *                  winhttp, the meaning is the same as the @c dwSendTimeout and
-    *                  @c dwReceiveTimeout parameters of the
-    *                  <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx">
-    *                  WinHttpSetTimeouts</a> API.
-    *                - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_VERBOSE - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @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 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.
-    */
+    * @deprecated IoTHubClient_SetOption is deprecated.  Use IoTHubDeviceClient_SetOption instead.
+    */   
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetOption, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value);
 
     /**
-    * @brief    This API specifies a call back to be used when the device receives a state update.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    deviceTwinCallback        The callback specified by the device client to be used for updating
-    *                                    the desired state. The callback will be called in response to a
-    *                                    request send by the IoTHub services. The payload will be passed to the
-    *                                    callback, along with two version numbers:
-    *                                        - Desired:
-    *                                        - LastSeenReported:
-    * @param    userContextCallback        User specified context that will be provided to the
-    *                                     callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SetDeviceTwinCallback is deprecated.  Use IoTHubDeviceClient_SetDeviceTwinCallback instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetDeviceTwinCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback);
 
     /**
-    * @brief    This API sends a report of the device's properties and their current values.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    reportedState            The current device property values to be 'reported' to the IoTHub.
-    * @param    reportedStateCallback    The callback specified by the device client to be called with the
-    *                                    result of the transaction.
-    * @param    userContextCallback        User specified context that will be provided to the
-    *                                     callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SendReportedState is deprecated.  Use IoTHubDeviceClient_SendReportedState instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SendReportedState, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback);
 
     /**
-    * @brief    This API sets callback for cloud to device method call.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    deviceMethodCallback    The callback which will be called by IoTHub.
-    * @param    userContextCallback        User specified context that will be provided to the
-    *                                     callback. This can be @c NULL.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SetDeviceMethodCallback is deprecated.  Use IoTHubDeviceClient_SetDeviceMethodCallback instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetDeviceMethodCallback, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback);
 
     /**
-    * @brief    This API sets callback for async cloud to device method call.
-    *
-    * @param    iotHubClientHandle                The handle created by a call to the create function.
-    * @param    inboundDeviceMethodCallback     The callback which will be called by IoTHub.
-    * @param    userContextCallback                User specified context that will be provided to the
-    *                                             callback. This can be @c NULL.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_SetDeviceMethodCallback_Ex is deprecated.  Use IoTHubDeviceClient_SetDeviceMethodCallback instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback);
 
     /**
-    * @brief    This API responses to a asnyc method callback identified the methodId.
-    *
-    * @param    iotHubClientHandle      The handle created by a call to the create function.
-    * @param    methodId                The methodId of the Device Method callback.
-    * @param    response                The response data for the method callback.
-    * @param    response_size           The size of the response data buffer.
-    * @param    status_response         The status response of the method callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_DeviceMethodResponse is deprecated.  Use IoTHubDeviceClient_SetDeviceMethodCallback instead.
     */
     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_DeviceMethodResponse, IOTHUB_CLIENT_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, response_size, int, statusCode);
@@ -352,38 +137,16 @@
 #ifndef DONT_USE_UPLOADTOBLOB
     /**
-    * @brief    IoTHubClient_UploadToBlobAsync uploads data from memory to a file in Azure Blob Storage.
-    *
-    * @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    source                              The source of data.
-    * @param    size                                The size of data.
-    * @param    iotHubClientFileUploadCallback      A callback to be invoked when the file upload operation has finished.
-    * @param    context                             A user-provided context to be passed to the file upload callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_UploadToBlobAsync is deprecated.  Use IoTHubDeviceClient_UploadToBlobAsync instead.
     */
     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.
-    ** DEPRECATED: Use IoTHubClient_UploadMultipleBlocksToBlobAsyncEx instead **
+    * @deprecated IoTHubClient_UploadMultipleBlocksToBlobAsync is deprecated.  Use IoTHubDeviceClient_UploadMultipleBlocksToBlobAsync 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.*/
+    * @deprecated IoTHubClient_UploadMultipleBlocksToBlobAsyncEx is deprecated.  Use IoTHubDeviceClient_UploadMultipleBlocksToBlobAsync instead.
+    */
     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);
 
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_core.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_core.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_core.h	(revision 464)
@@ -2,14 +2,11 @@
 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
-/** @file iothub_client_core.h
-*    @brief Extends the IoTHubClientCore_LL module with additional features.
-*
-*    @details IoTHubClientCore is a module that extends the IoTHubClientCore_LL
-*             module with 2 features:
-*                - scheduling the work for the IoTHubCLient from a
-*                  thread, so that the user does not need to create their
-*                  own thread
-*                - thread-safe APIs
-*/
+//**********************************************************************
+// NOTE: THIS HEADER IS FOR INTERNAL USE ONLY
+// 
+// Applications should use iothub_device_client.h or iothub_module_client.h 
+// instead for the official API.
+// 
+//**********************************************************************
 
 #ifndef IOTHUB_CLIENT_CORE_H
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_core_ll.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_core_ll.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_core_ll.h	(revision 464)
@@ -2,22 +2,11 @@
 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
-/** @file iothub_client_core_ll.h
-*    @brief     APIs that allow a user (usually a device) to communicate
-*             with an Azure IoTHub.
-*
-*    @details IoTHubClientCore_LL is a module that allows a user (usually a
-*             device) to communicate with an Azure IoTHub. It can send events
-*             and receive messages. At any given moment in time there can only
-*             be at most 1 message callback function.
-*
-*             This API surface contains a set of APIs that allows the user to
-*             interact with the lower layer portion of the IoTHubClient. These APIs
-*             contain @c _LL_ in their name, but retain the same functionality like the
-*             @c IoTHubClient_... APIs, with one difference. If the @c _LL_ APIs are
-*             used then the user is responsible for scheduling when the actual work done
-*             by the IoTHubClient happens (when the data is sent/received on/from the wire).
-*             This is useful for constrained devices where spinning a separate thread is
-*             often not desired.
-*/
+//**********************************************************************
+// NOTE: THIS HEADER IS FOR INTERNAL USE ONLY
+// 
+// Applications should use iothub_device_client_ll.h or iothub_module_client_ll.h 
+// instead for the official API.
+// 
+//**********************************************************************
 
 #ifndef IOTHUB_CLIENT_CORE_LL_H
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_ll.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_ll.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_ll.h	(revision 464)
@@ -1,4 +1,13 @@
 // Copyright (c) Microsoft. All rights reserved.
 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+//**********************************************************************
+// NOTE: THIS HEADER IS DEPRECATED
+// 
+// Functions in this header will be maintained for backward compatability.
+// New applications should use iothub_device_client_ll.h.
+// 
+//**********************************************************************
+
 
 /** @file iothub_client_ll.h
@@ -6,5 +15,5 @@
 *             with an Azure IoTHub.
 *
-*    @details IoTHubClient_LL is a module that allows a user (usually a
+*    @details IoTHubClient_LL allows a user (usually a
 *             device) to communicate with an Azure IoTHub. It can send events
 *             and receive messages. At any given moment in time there can only
@@ -38,364 +47,114 @@
 typedef struct IOTHUB_CLIENT_CORE_LL_HANDLE_DATA_TAG* IOTHUB_CLIENT_LL_HANDLE;
 
+    /**
+    * @deprecated IoTHubClient_LL_CreateFromConnectionString is deprecated.  Use IoTHubDeviceClient_LL_CreateFromConnectionString instead.
+    */
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol);
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing
-    *             IoT Hub using the specified connection string parameter.
-    *
-    * @param    connectionString    Pointer to a character string
-    * @param    protocol            Function pointer for protocol implementation
-    *
-    *            Sample connection string:
-    *                <blockquote>
-    *                    <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre>
-    *                </blockquote>
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_LL_Create is deprecated.  Use IoTHubDeviceClient_LL_Create instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateFromConnectionString, const char*, connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_Create, const IOTHUB_CLIENT_CONFIG*, config);
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing IoT
-    *             Hub using the specified parameters.
-    *
-    * @param    config    Pointer to an @c IOTHUB_CLIENT_CONFIG structure
-    *
-    *            The API does not allow sharing of a connection across multiple
-    *            devices. This is a blocking call.
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_LL_CreateWithTransport is deprecated.  Use IoTHubDeviceClient_LL_CreateWithTransport instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_Create, const IOTHUB_CLIENT_CONFIG*, config);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateWithTransport, const IOTHUB_CLIENT_DEVICE_CONFIG*, config);
 
     /**
-    * @brief    Creates a IoT Hub client for communication with an existing IoT
-    *             Hub using an existing transport.
-    *
-    * @param    config    Pointer to an @c IOTHUB_CLIENT_DEVICE_CONFIG structure
-    *
-    *            The API *allows* sharing of a connection across multiple
-    *            devices. This is a blocking call.
-    *
-    * @return    A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when
-    *             invoking other functions for IoT Hub client and @c NULL on failure.
+    * @deprecated IoTHubClient_LL_CreateFromDeviceAuth is deprecated.  Use IoTHubDeviceClient_LL_CreateFromDeviceAuth instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateWithTransport, const IOTHUB_CLIENT_DEVICE_CONFIG*, config);
-
-     /**
-     * @brief    Creates a IoT Hub client for communication with an existing IoT
-     *             Hub using the device auth module.
-     *
-     * @param    iothub_uri    Pointer to an ioThub hostname received in the registration process
-     * @param    device_id    Pointer to the device Id of the device
-     * @param    device_auth_handle    a device auth handle used to generate the connection string
-     * @param    protocol            Function pointer for protocol implementation
-     *
-     * @return    A non-NULL @c IOTHUB_CLIENT_LL_HANDLE value that is used when
-     *             invoking other functions for IoT Hub client and @c NULL on failure.
-     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_LL_HANDLE, IoTHubClient_LL_CreateFromDeviceAuth, const char*, iothub_uri, const char*, device_id, IOTHUB_CLIENT_TRANSPORT_PROVIDER, protocol);
 
     /**
-    * @brief    Disposes of resources allocated by the IoT Hub client. This is a
-    *             blocking call.
-    *
-    * @param    iotHubClientHandle    The handle created by a call to the create function.
+    * @deprecated IoTHubClient_LL_Destroy is deprecatedDevice.  Use IoTHubClient_LL_Destroy instead.
     */
-     MOCKABLE_FUNCTION(, void, IoTHubClient_LL_Destroy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle);
+    MOCKABLE_FUNCTION(, void, IoTHubClient_LL_Destroy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle);
 
     /**
-    * @brief    Asynchronous call to send the message specified by @p eventMessageHandle.
-    *
-    * @param    iotHubClientHandle               The handle created by a call to the create function.
-    * @param    eventMessageHandle               The handle to an IoT Hub message.
-    * @param    eventConfirmationCallback      The callback specified by the device for receiving
-    *                                         confirmation of the delivery of the IoT Hub message.
-    *                                         This callback can be expected to invoke the
-    *                                         ::IoTHubClient_LL_SendEventAsync function for the
-    *                                         same message in an attempt to retry sending a failing
-    *                                         message. The user can specify a @c NULL value here to
-    *                                         indicate that no callback is required.
-    * @param    userContextCallback            User specified context that will be provided to the
-    *                                         callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    * @remarks
-    *            The IOTHUB_MESSAGE_HANDLE instance provided as argument is copied by the function,
-    *            so this argument can be destroyed by the calling application right after IoTHubClient_LL_SendEventAsync returns.
-    *            The copy of @c eventMessageHandle is later destroyed by the iothub client when the message is effectively sent, if a failure sending it occurs, or if the client is destroyed.
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SendEventAsync is deprecated.  Use IoTHubDeviceClient_LL_SendEventAsync instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SendEventAsync, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SendEventAsync, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_MESSAGE_HANDLE, eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK, eventConfirmationCallback, void*, userContextCallback);
 
     /**
-    * @brief    This function returns the current sending status for IoTHubClient.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    iotHubClientStatus        The sending state is populated at the address pointed
-    *                                     at by this parameter. The value will be set to
-    *                                     @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently
-    *                                     no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY
-    *                                     if there are.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_GetSendStatus is deprecated.  UseDevice IoTHubClient_LL_GetSendStatus instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetSendStatus, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetSendStatus, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_STATUS*, iotHubClientStatus);
 
     /**
-    * @brief    Sets up the message callback to be invoked when IoT Hub issues a
-    *             message to the device. This is a blocking call.
-    *
-    * @param    iotHubClientHandle               The handle created by a call to the create function.
-    * @param    messageCallback                The callback specified by the device for receiving
-    *                                         messages from IoT Hub.
-    * @param    userContextCallback            User specified context that will be provided to the
-    *                                         callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SetMessageCallback is deprecated.  Use IoTHubDeviceClient_LL_SetMessageCallback instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetMessageCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetMessageCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC, messageCallback, void*, userContextCallback);
 
     /**
-    * @brief    Sets up the connection status callback to be invoked representing the status of
-    * the connection to IOT Hub. This is a blocking call.
-    *
-    * @param    iotHubClientHandle                       The handle created by a call to the create function.
-    * @param    connectionStatusCallback                The callback specified by the device for receiving
-    *                                                 updates about the status of the connection to IoT Hub.
-    * @param    userContextCallback                    User specified context that will be provided to the
-    *                                                 callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SetConnectionStatusCallback is deprecated.  Use IoTHubDeviceClient_LL_SetConnectionStatusCallback instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetConnectionStatusCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetConnectionStatusCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_CONNECTION_STATUS_CALLBACK, connectionStatusCallback, void*, userContextCallback);
 
     /**
-    * @brief    Sets up the connection status callback to be invoked representing the status of
-    * the connection to IOT Hub. This is a blocking call.
-    *
-    * @param    iotHubClientHandle                       The handle created by a call to the create function.
-    * @param    retryPolicy                             The policy to use to reconnect to IoT Hub when a
-    *                                               connection drops.
-    * @param    retryTimeoutLimitInSeconds            Maximum amount of time(seconds) to attempt reconnection when a
-    *                                               connection drops to IOT Hub.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SetRetryPolicy is deprecated.  Use IoTHubDeviceClient_LL_SetRetryPolicy instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetRetryPolicy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds);
-
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetRetryPolicy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY, retryPolicy, size_t, retryTimeoutLimitInSeconds);
 
     /**
-    * @brief    Sets up the connection status callback to be invoked representing the status of
-    * the connection to IOT Hub. This is a blocking call.
-    *
-    * @param    iotHubClientHandle                       The handle created by a call to the create function.
-    * @param    retryPolicy                             Out parameter containing the policy to use to reconnect to IoT Hub.
-    * @param    retryTimeoutLimitInSeconds            Out parameter containing maximum amount of time in seconds to attempt reconnection
-                                                    to IOT Hub.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_GetRetryPolicy is deprecated.  Use IoTHubDeviceClient_LL_GetRetryPolicy instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetRetryPolicy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetRetryPolicy, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_RETRY_POLICY*, retryPolicy, size_t*, retryTimeoutLimitInSeconds);
 
     /**
-    * @brief    This function returns in the out parameter @p lastMessageReceiveTime
-    *             what was the value of the @c time function when the last message was
-    *             received at the client.
-    *
-    * @param    iotHubClientHandle                The handle created by a call to the create function.
-    * @param    lastMessageReceiveTime          Out parameter containing the value of @c time function
-    *                                             when the last message was received.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_GetLastMessageReceiveTime is deprecated.  Use IoTHubDeviceClient_LL_GetLastMessageReceiveTime instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetLastMessageReceiveTime, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_GetLastMessageReceiveTime, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, time_t*, lastMessageReceiveTime);
 
     /**
-    * @brief    This function is meant to be called by the user when work
-    *             (sending/receiving) can be done by the IoTHubClient.
-    *
-    * @param    iotHubClientHandle    The handle created by a call to the create function.
-    *
-    *            All IoTHubClient interactions (in regards to network traffic
-    *            and/or user level callbacks) are the effect of calling this
-    *            function and they take place synchronously inside _DoWork.
+    * @deprecated IoTHubClient_LL_DoWork is deprecateDeviced.  Use IoTHubDeviceClient_LL_DoWork instead.
     */
-     MOCKABLE_FUNCTION(, void, IoTHubClient_LL_DoWork, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle);
+    MOCKABLE_FUNCTION(, void, IoTHubClient_LL_DoWork, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle);
 
     /**
-    * @brief    This API sets a runtime option identified by parameter @p optionName
-    *             to a value pointed to by @p value. @p optionName and the data type
-    *             @p value is pointing to are specific for every option.
-    *
-    * @param    iotHubClientHandle    The handle created by a call to the create function.
-    * @param    optionName              Name of the option.
-    * @param    value                  The value.
-    *
-    *            The options that can be set via this API are:
-    *                - @b timeout - the maximum time in milliseconds a communication is
-    *                  allowed to use. @p value is a pointer to an @c unsigned @c int with
-    *                  the timeout value in milliseconds. This is only supported for the HTTP
-    *                  protocol as of now. When the HTTP protocol uses CURL, the meaning of
-    *                  the parameter is <em>total request time</em>. When the HTTP protocol uses
-    *                  winhttp, the meaning is the same as the @c dwSendTimeout and
-    *                  @c dwReceiveTimeout parameters of the
-    *                  <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx">
-    *                  WinHttpSetTimeouts</a> API.
-    *                - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_VERBOSE - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *              - @b keepalive - available for MQTT protocol.  Integer value that sets the
-    *                interval in seconds when pings are sent to the server.
-    *              - @b logtrace - available for MQTT protocol.  Boolean value that turns on and
-    *                off the diagnostic logging.
-    *              - @b sas_token_lifetime - available for MQTT & AMQP protocol.  size_t value that that determines the
-    *                sas token timeout length.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SetOption is deprecated. Device Use IoTHubDeviceClient_LL_SetOption instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetOption, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetOption, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, optionName, const void*, value);
 
     /**
-    * @brief    This API specifies a call back to be used when the device receives a desired state update.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    deviceTwinCallback        The callback specified by the device client to be used for updating
-    *                                    the desired state. The callback will be called in response to patch
-    *                                    request send by the IoTHub services. The payload will be passed to the
-    *                                    callback, along with two version numbers:
-    *                                        - Desired:
-    *                                        - LastSeenReported:
-    * @param    userContextCallback        User specified context that will be provided to the
-    *                                     callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SetDeviceTwinCallback is deprecated.  Use IoTHubDeviceClient_LL_SetDeviceTwinCallback instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceTwinCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceTwinCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK, deviceTwinCallback, void*, userContextCallback);
 
     /**
-    * @brief    This API sneds a report of the device's properties and their current values.
-    *
-    * @param    iotHubClientHandle        The handle created by a call to the create function.
-    * @param    reportedState            The current device property values to be 'reported' to the IoTHub.
-    * @param    reportedStateCallback    The callback specified by the device client to be called with the
-    *                                    result of the transaction.
-    * @param    userContextCallback        User specified context that will be provided to the
-    *                                     callback. This can be @c NULL.
-    *
-    *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubClient_LL_Destroy function from within any callback.
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_SendReportedState is deprecated.  Use IoTHubDeviceClient_LL_SendReportedState instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SendReportedState, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback);
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SendReportedState, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const unsigned char*, reportedState, size_t, size, IOTHUB_CLIENT_REPORTED_STATE_CALLBACK, reportedStateCallback, void*, userContextCallback);
 
-     /**
-     * @brief    This API sets callback for cloud to device method call.
-     *
-     * @param    iotHubClientHandle        The handle created by a call to the create function.
-     * @param    deviceMethodCallback    The callback which will be called by IoTHub.
-     * @param    userContextCallback        User specified context that will be provided to the
-     *                                     callback. This can be @c NULL.
-     *
-     * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
-     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceMethodCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback);
+    /**
+    * @deprecated IoTHubClient_LL_SetDeviceMethodCallback is deprecated.  Use IoTHubDeviceClient_LL_SetDeviceMethodCallback instead.
+    */
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceMethodCallback, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_DEVICE_METHOD_CALLBACK_ASYNC, deviceMethodCallback, void*, userContextCallback);
 
-     /**
-     * @brief    This API sets callback for async cloud to device method call.
-     *
-     * @param    iotHubClientHandle                The handle created by a call to the create function.
-     * @param    inboundDeviceMethodCallback     The callback which will be called by IoTHub.
-     * @param    userContextCallback                User specified context that will be provided to the
-     *                                             callback. This can be @c NULL.
-     *
-     * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
-     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback);
+    /**
+    * @deprecated IoTHubClient_LL_SetDeviceMethodCallback_Ex is deprecated.  Use IoTHubDeviceClient_LL_SetDeviceMethodCallback instead.
+    */
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_SetDeviceMethodCallback_Ex, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, IOTHUB_CLIENT_INBOUND_DEVICE_METHOD_CALLBACK, inboundDeviceMethodCallback, void*, userContextCallback);
 
-     /**
-     * @brief    This API responses to a asnyc method callback identified the methodId.
-     *
-     * @param    iotHubClientHandle      The handle created by a call to the create function.
-     * @param    methodId                The methodId of the Device Method callback.
-     * @param    response                The response data for the method callback.
-     * @param    response_size           The size of the response data buffer.
-     * @param    status_response         The status response of the method callback.
-     *
-     * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
-     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_DeviceMethodResponse, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, respSize, int, statusCode);
+    /**
+    * @deprecated IoTHubClient_LL_DeviceMethodResponse is deprecated.  Use IoTHubDeviceClient_LL_SetDeviceMethodCallback instead.
+    */
+    MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_DeviceMethodResponse, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, METHOD_HANDLE, methodId, const unsigned char*, response, size_t, respSize, int, statusCode);
 
 #ifndef DONT_USE_UPLOADTOBLOB
     /**
-    * @brief    This API uploads to Azure Storage the content pointed to by @p source having the size @p size
-    *           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    source                  pointer to the source for file content (can be NULL)
-    * @param    size                    the size of the source in memory (if @p source is NULL then size needs to be 0).
-    *
-    * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    * @deprecated IoTHubClient_LL_UploadToBlob is deprecated.  UsDevicee IoTHubDeviceClient_LL_UploadToBlob instead.
     */
-     MOCKABLE_FUNCTION(, IOTHUB_CLIENT_RESULT, IoTHubClient_LL_UploadToBlob, IOTHUB_CLIENT_LL_HANDLE, iotHubClientHandle, const char*, destinationFileName, const unsigned char*, source, size_t, size);
+    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
-     *
-     * @param    iotHubClientHandle      The handle created by a call to the create function.
-     * @param    destinationFileName     name of the file.
-     * @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.
-     *
-     * @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);
+    /**
+    * @deprecated IoTHubClient_LL_UploadMultipleBlocksToBlob is deprecated.  Use IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob 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);
+    /**
+    * @deprecated IoTHubClient_LL_UploadMultipleBlocksToBlobEx is deprecated.  Use IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob instead.
+    */
+    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*/
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_options.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_options.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_options.h	(revision 464)
@@ -28,4 +28,6 @@
     static STATIC_VAR_UNUSED const char* OPTION_CONNECTION_TIMEOUT = "connect_timeout";
 
+    /* None of the OPTION_PROXY_* options below are implemented.  Use OPTION_HTTP_PROXY 
+    from shared_util_options.h in https://github.com/Azure/azure-c-shared-utility/ repo instead */
     static STATIC_VAR_UNUSED const char* OPTION_PROXY_HOST = "proxy_address";
     static STATIC_VAR_UNUSED const char* OPTION_PROXY_USERNAME = "proxy_username";
@@ -87,4 +89,18 @@
     static STATIC_VAR_UNUSED const char* OPTION_DO_WORK_FREQUENCY_IN_MS = "do_work_freq_ms";
 
+// Minimum percentage (in the 0 to 1 range) of multiplexed registered devices that must be failing for a transport-wide reconnection to be triggered.
+// A value of zero results in a single registered device to be able to cause a general transport reconnection 
+// (thus causing all other multiplexed registered devices to be also reconnected, meaning an agressive reconnection strategy).
+// Setting this parameter to one indicates that 100% of the multiplexed registered devices must be failing in parallel for a 
+// transport-wide reconnection to be triggered (resulting in a very lenient reconnection strategy).  
+#define DEVICE_MULTIPLEXING_FAULTY_DEVICE_RATIO_RECONNECTION_THRESHOLD 0
+
+// Minimum number of consecutive failures an individual registered device must have to be considered a faulty device.
+// This is used along with DEVICE_MULTIPLEXING_FAULTY_DEVICE_RATIO_RECONNECTION_THRESHOLD to trigger transport-wide reconnections.
+// The device may fail to authenticate, timeout establishing the connection, get disconnected by the service for some reason or fail sending messages.
+// In all these cases the failures are cummulatively counted; if the count is equal to or greater than DEVICE_FAILURE_COUNT_RECONNECTION_THRESHOLD
+// the device is considered to be in a faulty state.    
+#define DEVICE_FAILURE_COUNT_RECONNECTION_THRESHOLD 5
+
 #ifdef __cplusplus
 }
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_version.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_version.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_client_version.h	(revision 464)
@@ -9,5 +9,5 @@
 #define IOTHUB_CLIENT_VERSION_H
 
-#define IOTHUB_SDK_VERSION "1.3.8"
+#define IOTHUB_SDK_VERSION "1.6.0"
 
 #include "umock_c/umock_c_prod.h"
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_device_client_ll.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_device_client_ll.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_device_client_ll.h	(revision 464)
@@ -6,5 +6,5 @@
 *             with an Azure IoTHub.
 *
-*    @details IoTHubDeviceClient_LL is a module that allows a user (usually a
+*    @details IoTHubDeviceClient_LL allows a user (usually a
 *             device) to communicate with an Azure IoTHub. It can send events
 *             and receive messages. At any given moment in time there can only
@@ -16,5 +16,5 @@
 *             @c IoTHubDeviceClient_... APIs, with one difference. If the @c _LL_ APIs are
 *             used then the user is responsible for scheduling when the actual work done
-*             by the IoTHubClient happens (when the data is sent/received on/from the wire).
+*             by the IoTHubClient happens (when the data is sent/received on/from the network).
 *             This is useful for constrained devices where spinning a separate thread is
 *             often not desired.
@@ -88,5 +88,5 @@
      /**
      * @brief    Creates a IoT Hub client for communication with an existing IoT
-     *           Hub using the device auth module.
+     *           Hub using the device auth.
      *
      * @param    iothub_uri             Pointer to an ioThub hostname received in the registration process
@@ -116,5 +116,5 @@
     *                                           confirmation of the delivery of the IoT Hub message.
     *                                           This callback can be expected to invoke the
-    *                                           ::IoTHubDeviceClient_LL_SendEventAsync function for the
+    *                                           IoTHubDeviceClient_LL_SendEventAsync function for the
     *                                           same message in an attempt to retry sending a failing
     *                                           message. The user can specify a @c NULL value here to
@@ -124,5 +124,5 @@
     *
     *           @b NOTE: The application behavior is undefined if the user calls
-    *           the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *           the IoTHubDeviceClient_LL_Destroy function from within any callback.
     * @remarks
     *           The IOTHUB_MESSAGE_HANDLE instance provided as argument is copied by the function,
@@ -139,7 +139,9 @@
     * @param    iotHubClientStatus        The sending state is populated at the address pointed
     *                                     at by this parameter. The value will be set to
-    *                                     @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently
-    *                                     no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY
+    *                                     @c IOTHUB_CLIENT_SEND_STATUS_IDLE if there is currently
+    *                                     no item to be sent and @c IOTHUB_CLIENT_SEND_STATUS_BUSY
     *                                     if there are.
+    *
+    * @remark    Does not return information related to uploads initiated by IoTHubDeviceClient_LL_UploadToBlob or IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob.
     *
     * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -158,5 +160,5 @@
     *
     *           @b NOTE: The application behavior is undefined if the user calls
-    *           the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *           the IoTHubDeviceClient_LL_Destroy function from within any callback.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -175,5 +177,7 @@
     *
     *           @b NOTE: The application behavior is undefined if the user calls
-    *           the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *           the IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *
+    * @remark   Callback specified will not receive connection status change notifications for upload connections created with IoTHubDeviceClient_LL_UploadToBlob or IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -192,5 +196,7 @@
     *
     *           @b NOTE: The application behavior is undefined if the user calls
-    *           the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *           the IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *
+    * @remark   Uploads initiated by IoTHubDeviceClient_LL_UploadToBlob or IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob do not have automatic retries and do not honor the retryPolicy settings.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -209,5 +215,5 @@
     *
     *           @b NOTE: The application behavior is undefined if the user calls
-    *           the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *           the IoTHubDeviceClient_LL_Destroy function from within any callback.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -229,5 +235,5 @@
 
     /**
-    * @brief    This function MUST be called by the user so work (sending/receiving data on the wire,
+    * @brief    This function MUST be called by the user so work (sending/receiving data on the network,
     *           computing and enforcing timeout controls, managing the connection to the IoT Hub) can
     *           be done by the IoTHubClient.
@@ -251,36 +257,5 @@
     * @param    value                   The value.
     *
-    *           The options that can be set via this API are:
-    *                - @b timeout - the maximum time in milliseconds a communication is
-    *                  allowed to use. @p value is a pointer to an @c unsigned @c int with
-    *                  the timeout value in milliseconds. This is only supported for the HTTP
-    *                  protocol as of now. When the HTTP protocol uses CURL, the meaning of
-    *                  the parameter is <em>total request time</em>. When the HTTP protocol uses
-    *                  winhttp, the meaning is the same as the @c dwSendTimeout and
-    *                  @c dwReceiveTimeout parameters of the
-    *                  <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx">
-    *                  WinHttpSetTimeouts</a> API.
-    *                - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b CURLOPT_VERBOSE - only available for HTTP protocol and only
-    *                  when CURL is used. It has the same meaning as CURL's option with the same
-    *                  name. @p value is pointer to a long.
-    *                - @b keepalive - available for MQTT protocol.  Integer value that sets the
-    *                  interval in seconds when pings are sent to the server.
-    *                - @b logtrace - available for MQTT protocol.  Boolean value that turns on and
-    *                  off the diagnostic logging.
-    *                - @b sas_token_lifetime - available for MQTT & AMQP protocol.  size_t value that that determines the
-    *                  sas token timeout length.
-    *                - @b OPTION_TRUSTED_CERT - Azure Server certificate used to validate TLS connection to iothub.
+    * @remarks  Documentation for configuration options is available at https://github.com/Azure/azure-iot-sdk-c/blob/master/doc/Iothub_sdk_options.md.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -302,5 +277,5 @@
     *
     *           @b NOTE: The application behavior is undefined if the user calls
-    *           the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *           the IoTHubDeviceClient_LL_Destroy function from within any callback.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -319,5 +294,5 @@
     *
     *            @b NOTE: The application behavior is undefined if the user calls
-    *            the ::IoTHubDeviceClient_LL_Destroy function from within any callback.
+    *            the IoTHubDeviceClient_LL_Destroy function from within any callback.
     *
     * @return    IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -334,5 +309,5 @@
      *
      *			@b NOTE: The application behavior is undefined if the user calls
-     *			the ::IoTHubClient_LL_Destroy function from within any callback.
+     *			the IoTHubClient_LL_Destroy function from within any callback.
      *
      * @return	IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -374,4 +349,7 @@
     * @param    source                  pointer to the source for file content (can be NULL)
     * @param    size                    the size of the source in memory (if @p source is NULL then size needs to be 0).
+    * 
+    * @warning  Other _LL_ functions such as IoTHubDeviceClient_LL_SendEventAsync queue work to be performed later and do not block.  IoTHubDeviceClient_LL_UploadToBlob
+    *           will block however until the upload is completed or fails, which may take a while.
     *
     * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
@@ -388,4 +366,7 @@
      * @param    context                 Any data provided by the user to serve as context on getDataCallback.
      *
+     * @warning  Other _LL_ functions such as IoTHubDeviceClient_LL_SendEventAsync queue work to be performed later and do not block.  IoTHubDeviceClient_LL_UploadMultipleBlocksToBlob
+     *           will block however until the upload is completed or fails, which may take a while.
+     *
      * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
      */
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_message.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_message.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/inc/iothub_message.h	(revision 464)
@@ -192,7 +192,11 @@
 * @param   iotHubMessageHandle Handle to the message.
 *
-* @param   key name of the property to set.  Note that when sending messages via the HTTP transport, this value must not contain spaces.
-*
-* @param   value of the property to set.
+* @param   key name of the property to set. Note that when sending messages via the HTTP transport, this value must not contain spaces.
+*
+* @param   value of the property to set. 
+*
+*            @b NOTE: The accepted character sets for the key name and value parameters are dependent on different factors, such as the protocol 
+*            being used. For more information on the character sets accepted by Azure IoT Hub, see 
+*            <a href="https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-construct">Create and read IoT Hub messages</a>.
 *
 * @return  An @c IOTHUB_MESSAGE_RESULT value indicating the result of setting the property.
@@ -252,5 +256,5 @@
 * @param   correlationId Pointer to the memory location of the messageId
 *
-* @return  Returns IOTHUB_MESSAGE_OK if the messageId was set successfully
+* @return  Returns IOTHUB_MESSAGE_OK if the CorrelationId was set successfully
 *          or an error code otherwise.
 */
@@ -295,5 +299,5 @@
 * @param   outputName Pointer to the queue to output message to
 *
-* @return  Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully
+* @return  Returns IOTHUB_MESSAGE_OK if the outputName was set successfully
 *          or an error code otherwise.
 */
@@ -318,5 +322,5 @@
 * @param   inputName Pointer to the queue to input message to
 *
-* @return  Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully
+* @return  Returns IOTHUB_MESSAGE_OK if the inputName was set successfully
 *          or an error code otherwise.
 */
@@ -340,10 +344,9 @@
 * @param   connectionModuleId Pointer to the module ID of connector
 *
-* @return  Returns IOTHUB_MESSAGE_OK if the DiagnosticData was set successfully
+* @return  Returns IOTHUB_MESSAGE_OK if the connectionModuleId was set successfully
 *          or an error code otherwise.
 */
 MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetConnectionModuleId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, connectionModuleId);
 
-
 /**
 * @brief   Gets the connection device ID from the IOTHUB_MESSAGE_HANDLE. No new memory is allocated,
@@ -356,4 +359,47 @@
 */
 MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetConnectionDeviceId, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle);
+
+/**
+* @brief   Sets the message creation time in UTC.
+*
+* @param   iotHubMessageHandle Handle to the message.
+* @param   messageCreationTimeUtc Pointer to the message creation time as null-terminated string
+*
+* @return  Returns IOTHUB_MESSAGE_OK if the messageCreationTimeUtc was set successfully
+*          or an error code otherwise.
+*/
+MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetMessageCreationTimeUtcSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, messageCreationTimeUtc);
+
+/**
+* @brief   Gets the message creation time in UTC from the IOTHUB_MESSAGE_HANDLE. No new memory is allocated,
+*          the caller is not responsible for freeing the memory. The memory
+*          is valid until IoTHubMessage_Destroy is called on the message.
+*
+* @param   iotHubMessageHandle Handle to the message.
+*
+* @return  A const char* pointing to the message creation time in UTC.
+*/
+MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetMessageCreationTimeUtcSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle);
+
+/**
+* @brief   Sets the message user id. CAUTION: SDK user should not call it directly, it is for internal use only.
+*
+* @param   iotHubMessageHandle Handle to the message.
+* @param   userId Pointer to the message user id as null-terminated string
+*
+* @return  Returns IOTHUB_MESSAGE_OK if the userId was set successfully or an error code otherwise.
+*/
+MOCKABLE_FUNCTION(, IOTHUB_MESSAGE_RESULT, IoTHubMessage_SetMessageUserIdSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle, const char*, userId);
+
+/**
+* @brief   Gets the message user id from the IOTHUB_MESSAGE_HANDLE. No new memory is allocated,
+*          the caller is not responsible for freeing the memory. The memory
+*          is valid until IoTHubMessage_Destroy is called on the message.
+*
+* @param   iotHubMessageHandle Handle to the message.
+*
+* @return  A const char* pointing to the message user id.
+*/
+MOCKABLE_FUNCTION(, const char*, IoTHubMessage_GetMessageUserIdSystemProperty, IOTHUB_MESSAGE_HANDLE, iotHubMessageHandle);
 
 /**
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/blob.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/blob.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/blob.c	(revision 464)
@@ -13,4 +13,8 @@
 #include "azure_c_shared_utility/azure_base64.h"
 #include "azure_c_shared_utility/shared_util_options.h"
+
+static const char blockListXmlBegin[]  = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<BlockList>";
+static const char blockListXmlEnd[] = "</BlockList>";
+static const char blockListUriMarker[] = "&comp=blocklist";
 
 BLOB_RESULT Blob_UploadBlock(
@@ -132,256 +136,262 @@
 }
 
+
+// InvokeUserCallbackAndSendBlobs invokes the application's getDataCallbackEx as many time as callback requests and, for each call,
+// sends the blob contents to the server.
+static BLOB_RESULT InvokeUserCallbackAndSendBlobs(HTTPAPIEX_HANDLE httpApiExHandle, const char* relativePath, STRING_HANDLE blockIDList, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse)
+{
+    BLOB_RESULT result;
+
+    /*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 getDataCallbackEx returns incorrect blocks to upload */
+    unsigned int uploadOneMoreBlock = 1; /* set to 1 while getDataCallbackEx returns correct blocks to upload */
+    unsigned char const * source = NULL; /* data set by getDataCallbackEx */
+    size_t size = 0; /* source size set by getDataCallbackEx */
+    IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT getDataReturnValue;
+
+    do
+    {
+        getDataReturnValue = getDataCallbackEx(FILE_UPLOAD_OK, &source, &size, context);
+        if (getDataReturnValue == IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT)
+        {
+            /*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;
+        }
+        else if (source == NULL || size == 0)
+        {
+            /*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
+        {
+            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 %lu, max allowed size is %d", (unsigned long)size, BLOCK_SIZE);
+                result = BLOB_INVALID_ARG;
+                isError = 1;
+            }
+            else if (blockID >= MAX_BLOCK_COUNT)
+            {
+                /*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 %lu blocks in one blob", (unsigned long)MAX_BLOCK_COUNT);
+                result = BLOB_INVALID_ARG;
+                isError = 1;
+            }
+            else
+            {
+                /*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
+                {
+                    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)
+                {
+                    LogError("unable to Blob_UploadBlock. Returned value=%d", result);
+                    isError = 1;
+                }
+                else if (*httpStatus >= 300)
+                {
+                    LogError("unable to Blob_UploadBlock. Returned httpStatus=%u", (unsigned int)*httpStatus);
+                    isError = 1;
+                }
+            }
+            blockID++;
+        }
+    }
+    while(uploadOneMoreBlock && !isError);
+
+    return result;
+}
+
+// SendBlockIdList to send an XML of uploaded blockIds to the server after the application's payload block(s) have been transfered.
+static BLOB_RESULT SendBlockIdList(HTTPAPIEX_HANDLE httpApiExHandle, const char* relativePath, STRING_HANDLE blockIDList, unsigned int* httpStatus, BUFFER_HANDLE httpResponse)
+{
+    BLOB_RESULT result;
+    
+    /*complete the XML*/
+    if (STRING_concat(blockIDList, blockListXmlEnd) != 0)
+    {
+        /*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("failed to STRING_concat");
+        result = BLOB_ERROR;
+    }
+    else
+    {
+        /*Codes_SRS_BLOB_02_029: [Blob_UploadMultipleBlocksFromSasUri shall construct a new relativePath from following string : base relativePath + "&comp=blocklist"]*/
+        STRING_HANDLE newRelativePath = STRING_construct(relativePath);
+        if (newRelativePath == 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("failed to STRING_construct");
+            result = BLOB_ERROR;
+        }
+        else
+        {
+            if (STRING_concat(newRelativePath, blockListUriMarker) != 0)
+            {
+                /*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("failed to STRING_concat");
+                result = BLOB_ERROR;
+            }
+            else
+            {
+                /*Codes_SRS_BLOB_02_030: [ Blob_UploadMultipleBlocksFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing the new relativePath, httpStatus and httpResponse and the XML string as content. ]*/
+                const char* s = STRING_c_str(blockIDList);
+                BUFFER_HANDLE blockIDListAsBuffer = BUFFER_create((const unsigned char*)s, strlen(s));
+                if (blockIDListAsBuffer == 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("failed to BUFFER_create");
+                    result = BLOB_ERROR;
+                }
+                else
+                {
+                    if (HTTPAPIEX_ExecuteRequest(
+                        httpApiExHandle,
+                        HTTPAPI_REQUEST_PUT,
+                        STRING_c_str(newRelativePath),
+                        NULL,
+                        blockIDListAsBuffer,
+                        httpStatus,
+                        NULL,
+                        httpResponse
+                    ) != HTTPAPIEX_OK)
+                    {
+                        /*Codes_SRS_BLOB_02_031: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
+                        LogError("unable to HTTPAPIEX_ExecuteRequest");
+                        result = BLOB_HTTP_ERROR;
+                    }
+                    else
+                    {
+                        /*Codes_SRS_BLOB_02_032: [ Otherwise, Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
+                        result = BLOB_OK;
+                    }
+                    BUFFER_delete(blockIDListAsBuffer);
+                }
+            }
+            STRING_delete(newRelativePath);
+        }
+    }
+
+    return result;
+}
+
+
 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;
+    const char* hostnameBegin;
+    STRING_HANDLE blockIDList = NULL;
+    HTTPAPIEX_HANDLE httpApiExHandle = NULL;
+    char* hostname = NULL;
+    
     /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
-    if (SASURI == NULL)
-    {
-        LogError("parameter SASURI is NULL");
+    /*Codes_SRS_BLOB_02_002: [ If getDataCallbackEx is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+    if ((SASURI == NULL) || (getDataCallbackEx == NULL))
+    {
+        LogError("One or more required values is NULL, SASURI=%p, getDataCallbackEx=%p", SASURI, getDataCallbackEx);
         result = BLOB_INVALID_ARG;
     }
+    /*Codes_SRS_BLOB_02_017: [ Blob_UploadMultipleBlocksFromSasUri shall copy from SASURI the hostname to a new const char* ]*/
+    /*to find the hostname, the following logic is applied:*/
+    /*the hostname starts at the first character after "://"*/
+    /*the hostname ends at the first character before the next "/" after "://"*/
+    else if ((hostnameBegin = strstr(SASURI, "://")) == NULL)
+    {
+        /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+        LogError("hostname cannot be determined");
+        result = BLOB_INVALID_ARG;
+    }
     else
     {
-        /*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_EX getDataCallbackEx is NULL");
+        hostnameBegin += 3; /*have to skip 3 characters which are "://"*/
+        const char* relativePath = strchr(hostnameBegin, '/');
+        if (relativePath == NULL)
+        {
+            /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+            LogError("hostname cannot be determined");
             result = BLOB_INVALID_ARG;
         }
-        /*the below define avoid a "condition always false" on some compilers*/
         else
         {
-            /*Codes_SRS_BLOB_02_017: [ Blob_UploadMultipleBlocksFromSasUri shall copy from SASURI the hostname to a new const char* ]*/
-            /*to find the hostname, the following logic is applied:*/
-            /*the hostname starts at the first character after "://"*/
-            /*the hostname ends at the first character before the next "/" after "://"*/
-            const char* hostnameBegin = strstr(SASURI, "://");
-            if (hostnameBegin == NULL)
-            {
-                /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
-                LogError("hostname cannot be determined");
-                result = BLOB_INVALID_ARG;
+            size_t hostnameSize = relativePath - hostnameBegin;
+            if ((hostname = (char*)malloc(hostnameSize + 1)) == NULL)
+            {
+                /*Codes_SRS_BLOB_02_016: [ If the hostname copy cannot be made then then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/
+                LogError("oom - out of memory");
+                result = BLOB_ERROR;
             }
             else
             {
-                hostnameBegin += 3; /*have to skip 3 characters which are "://"*/
-                const char* hostnameEnd = strchr(hostnameBegin, '/');
-                if (hostnameEnd == NULL)
-                {
-                    /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
-                    LogError("hostname cannot be determined");
-                    result = BLOB_INVALID_ARG;
-                }
-                else
-                {
-                    size_t hostnameSize = hostnameEnd - hostnameBegin;
-                    char* hostname = (char*)malloc(hostnameSize + 1); /*+1 because of '\0' at the end*/
-                    if (hostname == NULL)
-                    {
-                        /*Codes_SRS_BLOB_02_016: [ If the hostname copy cannot be made then then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/
-                        LogError("oom - out of memory");
-                        result = BLOB_ERROR;
-                    }
-                    else
-                    {
-                        HTTPAPIEX_HANDLE httpApiExHandle;
-                        (void)memcpy(hostname, hostnameBegin, hostnameSize);
-                        hostname[hostnameSize] = '\0';
-
-                        /*Codes_SRS_BLOB_02_018: [ Blob_UploadMultipleBlocksFromSasUri shall create a new HTTPAPI_EX_HANDLE by calling HTTPAPIEX_Create passing the hostname. ]*/
-                        httpApiExHandle = HTTPAPIEX_Create(hostname);
-                        if (httpApiExHandle == NULL)
-                        {
-                            /*Codes_SRS_BLOB_02_007: [ If HTTPAPIEX_Create fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR. ]*/
-                            LogError("unable to create a HTTPAPIEX_HANDLE");
-                            result = BLOB_ERROR;
-                        }
-                        else
-                        {
-                            if ((certificates != NULL)&& (HTTPAPIEX_SetOption(httpApiExHandle, "TrustedCerts", certificates) == HTTPAPIEX_ERROR))
-                            {
-                                LogError("failure in setting trusted certificates");
-                                result = BLOB_ERROR;
-                            }
-                            else if ((proxyOptions != NULL && proxyOptions->host_address != NULL) && HTTPAPIEX_SetOption(httpApiExHandle, OPTION_HTTP_PROXY, proxyOptions) == HTTPAPIEX_ERROR)
-                            {
-                                LogError("failure in setting proxy options");
-                                result = BLOB_ERROR;
-                            }
-                            else
-                            {
-                                /*Codes_SRS_BLOB_02_019: [ Blob_UploadMultipleBlocksFromSasUri shall compute the base relative path of the request from the SASURI parameter. ]*/
-                                const char* relativePath = hostnameEnd; /*this is where the relative path begins in the SasUri*/
-
-                                /*Codes_SRS_BLOB_02_028: [ Blob_UploadMultipleBlocksFromSasUri shall construct an XML string with the following content: ]*/
-                                STRING_HANDLE blockIDList = STRING_construct("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<BlockList>"); /*the XML "build as we go"*/
-                                if (blockIDList == 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("failed to STRING_construct");
-                                    result = BLOB_HTTP_ERROR;
-                                }
-                                else
-                                {
-                                    /*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 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;
-
-                                    do
-                                    {
-                                        getDataReturnValue = getDataCallbackEx(FILE_UPLOAD_OK, &source, &size, context);
-                                        if (getDataReturnValue == IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT)
-                                        {
-                                            /*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;
-                                        }
-                                        else if (source == NULL || size == 0)
-                                        {
-                                            /*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
-                                        {
-                                            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 %lu, max allowed size is %d", (unsigned long)size, BLOCK_SIZE);
-                                                result = BLOB_INVALID_ARG;
-                                                isError = 1;
-                                            }
-                                            else if (blockID >= MAX_BLOCK_COUNT)
-                                            {
-                                                /*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 %lu blocks in one blob", (unsigned long)MAX_BLOCK_COUNT);
-                                                result = BLOB_INVALID_ARG;
-                                                isError = 1;
-                                            }
-                                            else
-                                            {
-                                                /*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
-                                                {
-                                                    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)
-                                                {
-                                                    LogError("unable to Blob_UploadBlock. Returned value=%d, httpStatus=%u", result, (unsigned int)*httpStatus);
-                                                    isError = 1;
-                                                }
-                                            }
-                                            blockID++;
-                                        }
-                                    }
-                                    while(uploadOneMoreBlock && !isError);
-
-                                    if (isError || result != BLOB_OK)
-                                    {
-                                        /*do nothing, it will be reported "as is"*/
-                                    }
-                                    else
-                                    {
-                                        /*complete the XML*/
-                                        if (STRING_concat(blockIDList, "</BlockList>") != 0)
-                                        {
-                                            /*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("failed to STRING_concat");
-                                            result = BLOB_ERROR;
-                                        }
-                                        else
-                                        {
-                                            /*Codes_SRS_BLOB_02_029: [Blob_UploadMultipleBlocksFromSasUri shall construct a new relativePath from following string : base relativePath + "&comp=blocklist"]*/
-                                            STRING_HANDLE newRelativePath = STRING_construct(relativePath);
-                                            if (newRelativePath == 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("failed to STRING_construct");
-                                                result = BLOB_ERROR;
-                                            }
-                                            else
-                                            {
-                                                if (STRING_concat(newRelativePath, "&comp=blocklist") != 0)
-                                                {
-                                                    /*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("failed to STRING_concat");
-                                                    result = BLOB_ERROR;
-                                                }
-                                                else
-                                                {
-                                                    /*Codes_SRS_BLOB_02_030: [ Blob_UploadMultipleBlocksFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing the new relativePath, httpStatus and httpResponse and the XML string as content. ]*/
-                                                    const char* s = STRING_c_str(blockIDList);
-                                                    BUFFER_HANDLE blockIDListAsBuffer = BUFFER_create((const unsigned char*)s, strlen(s));
-                                                    if (blockIDListAsBuffer == 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("failed to BUFFER_create");
-                                                        result = BLOB_ERROR;
-                                                    }
-                                                    else
-                                                    {
-                                                        if (HTTPAPIEX_ExecuteRequest(
-                                                            httpApiExHandle,
-                                                            HTTPAPI_REQUEST_PUT,
-                                                            STRING_c_str(newRelativePath),
-                                                            NULL,
-                                                            blockIDListAsBuffer,
-                                                            httpStatus,
-                                                            NULL,
-                                                            httpResponse
-                                                        ) != HTTPAPIEX_OK)
-                                                        {
-                                                            /*Codes_SRS_BLOB_02_031: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
-                                                            LogError("unable to HTTPAPIEX_ExecuteRequest");
-                                                            result = BLOB_HTTP_ERROR;
-                                                        }
-                                                        else
-                                                        {
-                                                            /*Codes_SRS_BLOB_02_032: [ Otherwise, Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
-                                                            result = BLOB_OK;
-                                                        }
-                                                        BUFFER_delete(blockIDListAsBuffer);
-                                                    }
-                                                }
-                                                STRING_delete(newRelativePath);
-                                            }
-                                        }
-                                    }
-                                    STRING_delete(blockIDList);
-                                }
-
-                            }
-                            HTTPAPIEX_Destroy(httpApiExHandle);
-                        }
-                        free(hostname);
-                    }
-                }
-            }
-        }
-    }
+                (void)memcpy(hostname, hostnameBegin, hostnameSize);
+                hostname[hostnameSize] = '\0';
+
+                /*Codes_SRS_BLOB_02_018: [ Blob_UploadMultipleBlocksFromSasUri shall create a new HTTPAPI_EX_HANDLE by calling HTTPAPIEX_Create passing the hostname. ]*/
+                httpApiExHandle = HTTPAPIEX_Create(hostname);
+                if (httpApiExHandle == NULL)
+                {
+                    /*Codes_SRS_BLOB_02_007: [ If HTTPAPIEX_Create fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR. ]*/
+                    LogError("unable to create a HTTPAPIEX_HANDLE");
+                    result = BLOB_ERROR;
+                }
+                else if ((certificates != NULL) && (HTTPAPIEX_SetOption(httpApiExHandle, "TrustedCerts", certificates) == HTTPAPIEX_ERROR))
+                {
+                    LogError("failure in setting trusted certificates");
+                    result = BLOB_ERROR;
+                }
+                else if ((proxyOptions != NULL && proxyOptions->host_address != NULL) && HTTPAPIEX_SetOption(httpApiExHandle, OPTION_HTTP_PROXY, proxyOptions) == HTTPAPIEX_ERROR)
+                {
+                    LogError("failure in setting proxy options");
+                    result = BLOB_ERROR;
+                }
+                /*Codes_SRS_BLOB_02_028: [ Blob_UploadMultipleBlocksFromSasUri shall construct an XML string with the following content: ]*/
+                else if ((blockIDList = STRING_construct(blockListXmlBegin)) == 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("failed to STRING_construct");
+                    result = BLOB_HTTP_ERROR;
+                }
+                else if ((result = InvokeUserCallbackAndSendBlobs(httpApiExHandle, relativePath, blockIDList, getDataCallbackEx, context, httpStatus, httpResponse)) != BLOB_OK)
+                {
+                   LogError("Failed in invoking callback/sending blob step");
+                }
+                else if (*httpStatus < 300)
+                {
+                    // Per SRS_BLOB_02_026, it possible for us to have a result=BLOB_OK AND a non-success HTTP status code.
+                    // In order to maintain back-compat with existing code, we will return the BLOB_OK to the caller but NOT invoke this final step.
+                    result = SendBlockIdList(httpApiExHandle, relativePath, blockIDList, httpStatus, httpResponse);
+                }
+            }
+        }
+    }
+
+    HTTPAPIEX_Destroy(httpApiExHandle);
+    STRING_delete(blockIDList);
+    free(hostname);
+
     return result;
 }
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_authorization.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_authorization.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_authorization.c	(revision 464)
@@ -31,5 +31,5 @@
     char* device_id;
     char* module_id;
-    size_t token_expiry_time_sec;
+    uint64_t token_expiry_time_sec;
     IOTHUB_CREDENTIAL_TYPE cred_type;
 #ifdef USE_PROV_MODULE
@@ -38,5 +38,5 @@
 } IOTHUB_AUTHORIZATION_DATA;
 
-static int get_seconds_since_epoch(size_t* seconds)
+static int get_seconds_since_epoch(uint64_t* seconds)
 {
     int result;
@@ -49,5 +49,5 @@
     else
     {
-        *seconds = (size_t)get_difftime(current_time, (time_t)0);
+        *seconds = (uint64_t)get_difftime(current_time, (time_t)0);
         result = 0;
     }
@@ -384,5 +384,5 @@
 }
 
-char* IoTHubClient_Auth_Get_SasToken(IOTHUB_AUTHORIZATION_HANDLE handle, const char* scope, size_t expiry_time_relative_seconds, const char* key_name)
+char* IoTHubClient_Auth_Get_SasToken(IOTHUB_AUTHORIZATION_HANDLE handle, const char* scope, uint64_t expiry_time_relative_seconds, const char* key_name)
 {
     char* result;
@@ -400,5 +400,5 @@
 #ifdef USE_PROV_MODULE
             DEVICE_AUTH_CREDENTIAL_INFO dev_auth_cred;
-            size_t sec_since_epoch;
+            uint64_t sec_since_epoch;
 
             if (get_seconds_since_epoch(&sec_since_epoch) != 0)
@@ -410,5 +410,5 @@
             {
                 memset(&dev_auth_cred, 0, sizeof(DEVICE_AUTH_CREDENTIAL_INFO));
-                size_t expiry_time = sec_since_epoch + handle->token_expiry_time_sec;
+                uint64_t expiry_time = sec_since_epoch + handle->token_expiry_time_sec;
                 dev_auth_cred.sas_info.expiry_seconds = expiry_time;
                 dev_auth_cred.sas_info.token_scope = scope;
@@ -465,5 +465,5 @@
             {
                 STRING_HANDLE sas_token;
-                size_t sec_since_epoch;
+                uint64_t sec_since_epoch;
 
                 /* Codes_SRS_IoTHub_Authorization_07_010: [ IoTHubClient_Auth_Get_SasToken` shall construct the expiration time using the handle->token_expiry_time_sec added to epoch time. ] */
@@ -477,5 +477,5 @@
                 {
                     /* Codes_SRS_IoTHub_Authorization_07_011: [ IoTHubClient_Auth_Get_ConnString shall call SASToken_CreateString to construct the sas token. ] */
-                    size_t expiry_time = sec_since_epoch + handle->token_expiry_time_sec;
+                    uint64_t expiry_time = sec_since_epoch + handle->token_expiry_time_sec;
                     if ( (sas_token = SASToken_CreateString(handle->device_key, scope, key_name, expiry_time)) == NULL)
                     {
@@ -691,5 +691,5 @@
 #endif
 
-int IoTHubClient_Auth_Set_SasToken_Expiry(IOTHUB_AUTHORIZATION_HANDLE handle, size_t expiry_time_seconds)
+int IoTHubClient_Auth_Set_SasToken_Expiry(IOTHUB_AUTHORIZATION_HANDLE handle, uint64_t expiry_time_seconds)
 {
     int result;
@@ -713,7 +713,7 @@
 }
 
-size_t IoTHubClient_Auth_Get_SasToken_Expiry(IOTHUB_AUTHORIZATION_HANDLE handle)
-{
-    size_t result;
+uint64_t IoTHubClient_Auth_Get_SasToken_Expiry(IOTHUB_AUTHORIZATION_HANDLE handle)
+{
+    uint64_t result;
     if (handle == NULL)
     {
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_core.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_core.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_core.c	(revision 464)
@@ -26,4 +26,5 @@
 
 #define DO_WORK_FREQ_DEFAULT 1
+#define DO_WORK_MAXIMUM_ALLOWED_FREQUENCY 100
 
 struct IOTHUB_QUEUE_CONTEXT_TAG;
@@ -1722,5 +1723,5 @@
             {
                 /* Codes_SRS_IOTHUBCLIENT_41_003: [ The value for `OPTION_DO_WORK_FREQUENCY_IN_MS` shall be limited to 100 to follow SDK best practices by not reducing the DoWork frequency below 10 Hz ]*/
-                if (0 < * (unsigned int *)value && * (unsigned int *)value <= 100)
+                if (0 < * (unsigned int *)value && * (unsigned int *)value <= DO_WORK_MAXIMUM_ALLOWED_FREQUENCY)
                 {
                     /* Codes_SRS_IOTHUBCLIENT_41_004: [ If `currentMessageTimeout` is not greater than `do_work_freq_ms`, `IotHubClientCore_SetOption` shall return `IOTHUB_CLIENT_INVALID_ARG` ]*/
@@ -1740,5 +1741,5 @@
                 {
                     result = IOTHUB_CLIENT_INVALID_ARG;
-                    LogError("Invalid value: OPTION_DO_WORK_FREQUENCY_IN_MS cannot exceed 100 ms. If you wish to reduce the frequency further, consider using the LL layer.");
+                    LogError("Invalid value: OPTION_DO_WORK_FREQUENCY_IN_MS cannot exceed %d ms. If you wish to reduce the frequency further, consider using the LL layer.", DO_WORK_MAXIMUM_ALLOWED_FREQUENCY);
                 }
             }
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_core_ll.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_core_ll.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_core_ll.c	(revision 464)
@@ -2348,5 +2348,6 @@
         else if (strcmp(optionName, OPTION_SAS_TOKEN_REFRESH_TIME) == 0 || strcmp(optionName, OPTION_SAS_TOKEN_LIFETIME) == 0)
         {
-            if (IoTHubClient_Auth_Set_SasToken_Expiry(handleData->authorization_module, *(size_t*)value) != 0)
+            // API compat: while IoTHubClient_Auth_Set_SasToken_Expiry accepts uint64_t, we cannot change the public API.
+            if (IoTHubClient_Auth_Set_SasToken_Expiry(handleData->authorization_module, (uint64_t)(*(size_t*)value)) != 0)
             {
                 LogError("Failed setting the Token Expiry time");
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_ll_uploadtoblob.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_ll_uploadtoblob.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_client_ll_uploadtoblob.c	(revision 464)
@@ -437,5 +437,5 @@
                                         else
                                         {
-                                            size_t expiry = (size_t)(difftime(curr_time, 0) + 3600);
+                                            uint64_t expiry = (uint64_t)(difftime(curr_time, 0) + 3600);
                                             char* sas_token = IoTHubClient_Auth_Get_SasToken(upload_data->authorization_module, STRING_c_str(uri_resource), expiry, EMPTY_STRING);
                                             if (sas_token == NULL)
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_message.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_message.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothub_message.c	(revision 464)
@@ -34,7 +34,9 @@
     IOTHUB_MESSAGE_DIAGNOSTIC_PROPERTY_DATA_HANDLE diagnosticData;
     bool is_security_message;
+    char* creationTimeUtc;
+    char* userId;
 }IOTHUB_MESSAGE_HANDLE_DATA;
 
-static bool ContainsOnlyUsAscii(const char* asciiValue)
+static bool ContainsValidUsAscii(const char* asciiValue)
 {
     bool result = true;
@@ -42,5 +44,5 @@
     while (iterator != NULL && *iterator != '\0')
     {
-        // Allow only printable ascii char
+        // Union of all allowed ASCII characters for the different protocols (HTTPS, MQTT, AMQP) is [dec 32, dec 126].
         if (*iterator < ' ' || *iterator > '~')
         {
@@ -50,12 +52,13 @@
         iterator++;
     }
-    return result;
-}
-
-/* Codes_SRS_IOTHUBMESSAGE_07_008: [ValidateAsciiCharactersFilter shall loop through the mapKey and mapValue strings to ensure that they only contain valid US-Ascii characters Ascii value 32 - 126.] */
+
+    return result;
+}
+
+/* Codes_SRS_IOTHUBMESSAGE_07_008: [ValidateAsciiCharactersFilter shall loop through the mapKey and mapValue strings to ensure that they only contain valid US-Ascii characters.*/
 static int ValidateAsciiCharactersFilter(const char* mapKey, const char* mapValue)
 {
     int result;
-    if (!ContainsOnlyUsAscii(mapKey) || !ContainsOnlyUsAscii(mapValue))
+    if (!ContainsValidUsAscii(mapKey) || !ContainsValidUsAscii(mapValue))
     {
         result = MU_FAILURE;
@@ -101,4 +104,6 @@
     free(handleData->connectionModuleId);
     free(handleData->connectionDeviceId);
+    free(handleData->creationTimeUtc);
+    free(handleData->userId);
     free(handleData);
 }
@@ -123,4 +128,52 @@
         }
         handleData->contentEncoding = tmp_encoding;
+        result = 0;
+    }
+    return result;
+}
+
+static int set_message_creation_time(IOTHUB_MESSAGE_HANDLE_DATA* handleData, const char* messageCreationTimeUtc)
+{
+    int result;
+    char* tmp_message_creation_time;
+
+    if (handleData->creationTimeUtc != NULL)
+    {
+        free(handleData->creationTimeUtc);
+        handleData->creationTimeUtc = NULL;
+    }
+
+    if (mallocAndStrcpy_s(&tmp_message_creation_time, messageCreationTimeUtc) != 0)
+    {
+        LogError("Failed saving a copy of messageCreationTimeUtc");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        handleData->creationTimeUtc = tmp_message_creation_time;
+        result = 0;
+    }
+    return result;
+}
+
+static int set_message_user_id(IOTHUB_MESSAGE_HANDLE_DATA* handleData, const char* userId)
+{
+    int result;
+    char* tmp_message_user_id;
+
+    if (handleData->userId != NULL)
+    {
+        free(handleData->userId);
+        handleData->userId = NULL;
+    }
+
+    if (mallocAndStrcpy_s(&tmp_message_user_id, userId) != 0)
+    {
+        LogError("Failed saving a copy of userId");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        handleData->userId = tmp_message_user_id;
         result = 0;
     }
@@ -349,4 +402,16 @@
                 result = NULL;
             }
+            else if (source->creationTimeUtc != NULL && mallocAndStrcpy_s(&result->creationTimeUtc, source->creationTimeUtc) != 0)
+            {
+                LogError("unable to copy creationTimeUtc");
+                DestroyMessageData(result);
+                result = NULL;
+            }
+            else if (source->userId != NULL && mallocAndStrcpy_s(&result->userId, source->userId) != 0)
+            {
+                LogError("unable to copy userId");
+                DestroyMessageData(result);
+                result = NULL;
+            }
             else if (source->connectionModuleId != NULL && mallocAndStrcpy_s(&result->connectionModuleId, source->connectionModuleId) != 0)
             {
@@ -509,5 +574,11 @@
     else
     {
-        if (Map_AddOrUpdate(msg_handle->properties, key, value) != MAP_OK)
+        MAP_RESULT map_result = Map_AddOrUpdate(msg_handle->properties, key, value);
+        if (map_result == MAP_FILTER_REJECT)
+        {
+            LogError("Failure validating property as ASCII");
+            result = IOTHUB_MESSAGE_INVALID_TYPE;
+        }
+        else if (map_result != MAP_OK)
         {
             LogError("Failure adding property to internal map");
@@ -748,4 +819,88 @@
         // Codes_SRS_IOTHUBMESSAGE_09_011: [IoTHubMessage_GetContentEncodingSystemProperty shall return the `contentEncoding` as a const char* ]
         result = (const char*)handleData->contentEncoding;
+    }
+
+    return result;
+}
+
+IOTHUB_MESSAGE_RESULT IoTHubMessage_SetMessageCreationTimeUtcSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* creationTimeUtc)
+{
+    IOTHUB_MESSAGE_RESULT result;
+
+    if (iotHubMessageHandle == NULL || creationTimeUtc == NULL)
+    {
+        LogError("Invalid argument (iotHubMessageHandle=%p, creationTimeUtc=%p)", iotHubMessageHandle, creationTimeUtc);
+        result = IOTHUB_MESSAGE_INVALID_ARG;
+    }
+    else
+    {
+        if (set_message_creation_time(iotHubMessageHandle, creationTimeUtc) != 0)
+        {
+            LogError("Failed saving a copy of creationTimeUtc");
+            result = IOTHUB_MESSAGE_ERROR;
+        }
+        else
+        {
+            result = IOTHUB_MESSAGE_OK;
+        }
+    }
+    return result;
+}
+
+const char* IoTHubMessage_GetMessageCreationTimeUtcSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle)
+{
+    const char* result;
+
+    if (iotHubMessageHandle == NULL)
+    {
+        LogError("Invalid argument (iotHubMessageHandle is NULL)");
+        result = NULL;
+    }
+    else
+    {
+        IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle;
+        result = (const char*)handleData->creationTimeUtc;
+    }
+
+    return result;
+}
+
+IOTHUB_MESSAGE_RESULT IoTHubMessage_SetMessageUserIdSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle, const char* userId)
+{
+    IOTHUB_MESSAGE_RESULT result;
+
+    if (iotHubMessageHandle == NULL || userId == NULL)
+    {
+        LogError("Invalid argument (iotHubMessageHandle=%p, userId=%p)", iotHubMessageHandle, userId);
+        result = IOTHUB_MESSAGE_INVALID_ARG;
+    }
+    else
+    {
+        if (set_message_user_id(iotHubMessageHandle, userId) != 0)
+        {
+            LogError("Failed saving a copy of userId");
+            result = IOTHUB_MESSAGE_ERROR;
+        }
+        else
+        {
+            result = IOTHUB_MESSAGE_OK;
+        }
+    }
+    return result;
+}
+
+const char* IoTHubMessage_GetMessageUserIdSystemProperty(IOTHUB_MESSAGE_HANDLE iotHubMessageHandle)
+{
+    const char* result;
+
+    if (iotHubMessageHandle == NULL)
+    {
+        LogError("Invalid argument (iotHubMessageHandle is NULL)");
+        result = NULL;
+    }
+    else
+    {
+        IOTHUB_MESSAGE_HANDLE_DATA* handleData = iotHubMessageHandle;
+        result = (const char*)handleData->userId;
     }
 
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothubtransport_mqtt_common.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothubtransport_mqtt_common.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothubtransport_mqtt_common.c	(revision 464)
@@ -82,4 +82,6 @@
 
 static const char* MESSAGE_ID_PROPERTY = "mid";
+static const char* MESSAGE_CREATION_TIME_UTC = "ctime";
+static const char* MESSAGE_USER_ID = "uid";
 static const char* CORRELATION_ID_PROPERTY = "cid";
 static const char* CONTENT_TYPE_PROPERTY = "ct";
@@ -200,5 +202,5 @@
     MQTT_CLIENT_STATUS mqttClientStatus;
     bool isDestroyCalled;
-    bool isRetryExpiredCallbackSet;
+    bool isRetryExpiredCallbackCalled;
     bool device_twin_get_sent;
     bool twin_resp_sub_recv;
@@ -274,33 +276,78 @@
 } DEVICE_METHOD_INFO;
 
-static void free_proxy_data(MQTTTRANSPORT_HANDLE_DATA* mqtt_transport_instance)
-{
-    if (mqtt_transport_instance->http_proxy_hostname != NULL)
-    {
-        free(mqtt_transport_instance->http_proxy_hostname);
-        mqtt_transport_instance->http_proxy_hostname = NULL;
-    }
-
-    if (mqtt_transport_instance->http_proxy_username != NULL)
-    {
-        free(mqtt_transport_instance->http_proxy_username);
-        mqtt_transport_instance->http_proxy_username = NULL;
-    }
-
-    if (mqtt_transport_instance->http_proxy_password != NULL)
-    {
-        free(mqtt_transport_instance->http_proxy_password);
-        mqtt_transport_instance->http_proxy_password = NULL;
-    }
-}
-
-// Destroys xio transport associated with MQTT handle and resets appropriate state
+//
+// InternStrnicmp implements strnicmp.  strnicmp isn't available on all platforms.
+//
+static int InternStrnicmp(const char* s1, const char* s2, size_t n)
+{
+    int result;
+
+    if (s1 == NULL)
+    {
+        result = -1;
+    }
+    else if (s2 == NULL)
+    {
+        result = 1;
+    }
+    else
+    {
+        result = 0;
+
+        while (n-- && result == 0)
+        {
+            if (*s1 == 0) result = -1;
+            else if (*s2 == 0) result = 1;
+            else
+            {
+
+                result = TOLOWER(*s1) - TOLOWER(*s2);
+                ++s1;
+                ++s2;
+            }
+        }
+    }
+    return result;
+}
+
+//
+// freeProxyData free()'s and resets proxy related settings of the mqtt_transport_instance.
+//
+static void freeProxyData(MQTTTRANSPORT_HANDLE_DATA* transport_data)
+{
+    if (transport_data->http_proxy_hostname != NULL)
+    {
+        free(transport_data->http_proxy_hostname);
+        transport_data->http_proxy_hostname = NULL;
+    }
+
+    if (transport_data->http_proxy_username != NULL)
+    {
+        free(transport_data->http_proxy_username);
+        transport_data->http_proxy_username = NULL;
+    }
+
+    if (transport_data->http_proxy_password != NULL)
+    {
+        free(transport_data->http_proxy_password);
+        transport_data->http_proxy_password = NULL;
+    }
+}
+
+//
+// DestroyXioTransport frees resources associated with MQTT handle and resets appropriate state
+//
 static void DestroyXioTransport(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
+    mqtt_client_clear_xio(transport_data->mqttClient);
     xio_destroy(transport_data->xioTransport);
     transport_data->xioTransport = NULL;
 }
 
-static void set_saved_tls_options(PMQTTTRANSPORT_HANDLE_DATA transport, OPTIONHANDLER_HANDLE new_options)
+//
+// setSavedTlsOptions saves off TLS specific options.  This is used
+// so that during a disconnection, we have these values available for next reconnection.
+//
+static void setSavedTlsOptions(PMQTTTRANSPORT_HANDLE_DATA transport, OPTIONHANDLER_HANDLE new_options)
 {
     if (transport->saved_tls_options != NULL)
@@ -311,9 +358,13 @@
 }
 
-static void free_transport_handle_data(MQTTTRANSPORT_HANDLE_DATA* transport_data)
+//
+// freeTransportHandleData free()'s 'the transport_data and all members that were allocated by it.
+//
+static void freeTransportHandleData(MQTTTRANSPORT_HANDLE_DATA* transport_data)
 {
     if (transport_data->mqttClient != NULL)
     {
         mqtt_client_deinit(transport_data->mqttClient);
+        transport_data->mqttClient = NULL;
     }
 
@@ -323,9 +374,9 @@
     }
 
-    set_saved_tls_options(transport_data, NULL);
+    setSavedTlsOptions(transport_data, NULL);
 
     tickcounter_destroy(transport_data->msgTickCounter);
 
-    free_proxy_data(transport_data);
+    freeProxyData(transport_data);
 
     STRING_delete(transport_data->devicesAndModulesPath);
@@ -346,42 +397,8 @@
 }
 
-int IoTHubTransport_MQTT_Common_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds)
-{
-    int result;
-
-    if (handle == NULL)
-    {
-        /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_25_041: [**If any handle is NULL then IoTHubTransport_MQTT_Common_SetRetryPolicy shall return resultant line.] */
-        LogError("Invalid handle parameter. NULL.");
-        result = MU_FAILURE;
-    }
-    else
-    {
-        RETRY_CONTROL_HANDLE new_retry_control_handle;
-
-        // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_006: [ IoTHubTransport_MQTT_Common_SetRetryPolicy shall set the retry logic by calling retry_control_create() with retry policy and retryTimeout as parameters]
-        // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_009: [ If retry_control_create() fails then IoTHubTransport_MQTT_Common_SetRetryPolicy shall revert to previous retry policy and return non-zero value ]
-        if ((new_retry_control_handle = retry_control_create(retryPolicy, (unsigned int)retryTimeoutLimitInSeconds)) == NULL)
-        {
-            LogError("Failed creating new retry control handle");
-            result = MU_FAILURE;
-        }
-        else
-        {
-            PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle;
-            RETRY_CONTROL_HANDLE previous_retry_control_handle = transport_data->retry_control_handle;
-
-            transport_data->retry_control_handle = new_retry_control_handle;
-            retry_control_destroy(previous_retry_control_handle);
-
-            /*Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_25_045: [**If retry logic for specified parameters of retry policy and retryTimeoutLimitInSeconds is created successfully then IoTHubTransport_MQTT_Common_SetRetryPolicy shall return 0]*/
-            result = 0;
-        }
-    }
-
-    return result;
-}
-
-static uint16_t get_next_packet_id(PMQTTTRANSPORT_HANDLE_DATA transport_data)
+//
+// getNextPacketId gets the next Packet Id to use and increments internal counter.
+//
+static uint16_t getNextPacketId(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
     if (transport_data->packetId + 1 >= USHRT_MAX)
@@ -397,5 +414,8 @@
 
 #ifndef NO_LOGGING
-static const char* retrieve_mqtt_return_codes(CONNECT_RETURN_CODE rtn_code)
+//
+// retrieveMqttReturnCodes returns friendly representation of connection code for logging purposes.
+//
+static const char* retrieveMqttReturnCodes(CONNECT_RETURN_CODE rtn_code)
 {
     switch (rtn_code)
@@ -420,5 +440,8 @@
 #endif // NO_LOGGING
 
-static int retrieve_device_method_rid_info(const char* resp_topic, STRING_HANDLE method_name, STRING_HANDLE request_id)
+//
+// retrievDeviceMethodRidInfo parses an incoming MQTT topic for a device method and retrieves the request ID it specifies.
+//
+static int retrievDeviceMethodRidInfo(const char* resp_topic, STRING_HANDLE method_name, STRING_HANDLE request_id)
 {
     int result;
@@ -484,5 +507,8 @@
 }
 
-static int parse_device_twin_topic_info(const char* resp_topic, bool* patch_msg, size_t* request_id, int* status_code)
+//
+// parseDeviceTwinTopicInfo parses information about a topic PUBLISH'd to this device/module.
+//
+static int parseDeviceTwinTopicInfo(const char* resp_topic, bool* patch_msg, size_t* request_id, int* status_code)
 {
     int result;
@@ -548,31 +574,8 @@
 }
 
-static int InternStrnicmp(const char* s1, const char* s2, size_t n)
-{
-    int result;
-
-    if (s1 == NULL) result = -1;
-    else if (s2 == NULL) result = 1;
-    else
-    {
-        result = 0;
-
-        while (n-- && result == 0)
-        {
-            if (*s1 == 0) result = -1;
-            else if (*s2 == 0) result = 1;
-            else
-            {
-
-                result = TOLOWER(*s1) - TOLOWER(*s2);
-                ++s1;
-                ++s2;
-            }
-        }
-    }
-    return result;
-}
-
-static IOTHUB_IDENTITY_TYPE retrieve_topic_type(const char* topic_resp, const char* input_queue)
+// 
+// retrieveTopicType translates an MQTT topic PUBLISH'd to this device/module into what type (e.g. twin, method, etc.) it represents. 
+//
+static IOTHUB_IDENTITY_TYPE retrieveTopicType(const char* topic_resp, const char* input_queue)
 {
     IOTHUB_IDENTITY_TYPE type;
@@ -598,5 +601,8 @@
 }
 
-static void sendMsgComplete(IOTHUB_MESSAGE_LIST* iothubMsgList, PMQTTTRANSPORT_HANDLE_DATA transport_data, IOTHUB_CLIENT_CONFIRMATION_RESULT confirmResult)
+//
+// notifyApplicationOfSendMessageComplete lets application know that messages in the iothubMsgList have completed (or should be considered failed) with confirmResult status.
+//
+static void notifyApplicationOfSendMessageComplete(IOTHUB_MESSAGE_LIST* iothubMsgList, PMQTTTRANSPORT_HANDLE_DATA transport_data, IOTHUB_CLIENT_CONFIRMATION_RESULT confirmResult)
 {
     DLIST_ENTRY messageCompleted;
@@ -606,4 +612,8 @@
 }
 
+//
+// addUserPropertiesTouMqttMessage translates application properties in iothub_message_handle (set by the application with IoTHubMessage_SetProperty e.g.)
+// into a representation in the MQTT TOPIC topic_string.
+//
 static int addUserPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr, bool urlencode)
 {
@@ -660,4 +670,8 @@
 }
 
+//
+// addSystemPropertyToTopicString appends a given "system" property from iothub_message_handle (set by the application with APIs such as IoTHubMessage_SetMessageId,
+// IoTHubMessage_SetContentTypeSystemProperty, etc.) onto the MQTT TOPIC topic_string.
+//
 static int addSystemPropertyToTopicString(STRING_HANDLE topic_string, size_t index, const char* property_key, const char* property_value, bool urlencode)
 {
@@ -690,4 +704,8 @@
 }
 
+//
+// addSystemPropertyToTopicString appends all "system" property from iothub_message_handle (set by the application with APIs such as IoTHubMessage_SetMessageId,
+// IoTHubMessage_SetContentTypeSystemProperty, etc.) onto the MQTT TOPIC topic_string.
+//
 static int addSystemPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr, bool urlencode)
 {
@@ -734,4 +752,15 @@
         }
     }
+
+    if (result == 0)
+    {
+        const char* message_creation_time_utc = IoTHubMessage_GetMessageCreationTimeUtcSystemProperty(iothub_message_handle);
+        if (message_creation_time_utc != NULL)
+        {
+            result = addSystemPropertyToTopicString(topic_string, index, MESSAGE_CREATION_TIME_UTC, message_creation_time_utc, urlencode);
+            index++;
+        }
+    }
+
     if (result == 0)
     {
@@ -754,4 +783,8 @@
 }
 
+// 
+// addDiagnosticPropertiesTouMqttMessage appends diagnostic data (as specified by IoTHubMessage_SetDiagnosticPropertyData) onto
+// the MQTT topic topic_string.
+// 
 static int addDiagnosticPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, STRING_HANDLE topic_string, size_t* index_ptr)
 {
@@ -821,5 +854,10 @@
 }
 
-
+//
+// addPropertiesTouMqttMessage adds user, "system", and diagnostic messages onto MQTT topic string.  Note that "system" properties is a
+// construct of the SDK and IoT Hub.  The MQTT protocol itself does not assign any significance to system and user properties (as opposed to AMQP).
+// The IOTHUB_MESSAGE_HANDLE structure however does have well-known properties (e.g. IoTHubMessage_SetMessageId) that the SDK treats as system
+// properties where we can automatically fill in the key value for in the key=value list.
+//
 static STRING_HANDLE addPropertiesTouMqttMessage(IOTHUB_MESSAGE_HANDLE iothub_message_handle, const char* eventTopic, bool urlencode)
 {
@@ -868,5 +906,8 @@
 }
 
-static int publish_mqtt_telemetry_msg(PMQTTTRANSPORT_HANDLE_DATA transport_data, MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry, const unsigned char* payload, size_t len)
+//
+// publishTelemetryMsg invokes the umqtt layer to send a PUBLISH message.
+//
+static int publishTelemetryMsg(PMQTTTRANSPORT_HANDLE_DATA transport_data, MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry, const unsigned char* payload, size_t len)
 {
     int result;
@@ -912,8 +953,11 @@
 }
 
-static int publish_device_method_message(MQTTTRANSPORT_HANDLE_DATA* transport_data, int status_code, STRING_HANDLE request_id, const unsigned char* response, size_t response_size)
+//
+// publishDeviceMethodResponseMsg invokes the umqtt to send a PUBLISH message that contains device method call results.
+//
+static int publishDeviceMethodResponseMsg(MQTTTRANSPORT_HANDLE_DATA* transport_data, int status_code, STRING_HANDLE request_id, const unsigned char* response, size_t response_size)
 {
     int result;
-    uint16_t packet_id = get_next_packet_id(transport_data);
+    uint16_t packet_id = getNextPacketId(transport_data);
 
     STRING_HANDLE msg_topic = STRING_construct_sprintf(DEVICE_METHOD_RESPONSE_TOPIC, status_code, STRING_c_str(request_id));
@@ -949,11 +993,16 @@
 }
 
-
-static void destroy_device_twin_get_message(MQTT_DEVICE_TWIN_ITEM* msg_entry)
+//
+// destroyDeviceTwinGetMsg frees msg_entry and any data associated with it.
+//
+static void destroyDeviceTwinGetMsg(MQTT_DEVICE_TWIN_ITEM* msg_entry)
 {
     free(msg_entry);
 }
 
-static MQTT_DEVICE_TWIN_ITEM* create_device_twin_message(MQTTTRANSPORT_HANDLE_DATA* transport_data, DEVICE_TWIN_MSG_TYPE device_twin_msg_type, uint32_t iothub_msg_id)
+//
+// createDeviceTwinMsg allocates and fills in structure for MQTT_DEVICE_TWIN_ITEM.
+//
+static MQTT_DEVICE_TWIN_ITEM* createDeviceTwinMsg(MQTTTRANSPORT_HANDLE_DATA* transport_data, DEVICE_TWIN_MSG_TYPE device_twin_msg_type, uint32_t iothub_msg_id)
 {
     MQTT_DEVICE_TWIN_ITEM* result;
@@ -974,5 +1023,5 @@
         memset(result, 0, sizeof(*result));
         result->msgCreationTime = current_time;
-        result->packet_id = get_next_packet_id(transport_data);
+        result->packet_id = getNextPacketId(transport_data);
         result->iothub_msg_id = iothub_msg_id;
         result->device_twin_msg_type = device_twin_msg_type;
@@ -982,5 +1031,8 @@
 }
 
-static int publish_device_twin_get_message(MQTTTRANSPORT_HANDLE_DATA* transport_data, MQTT_DEVICE_TWIN_ITEM* mqtt_info)
+//
+// publishDeviceTwinGetMsg invokes umqtt to PUBLISH a request to get the twin information.
+//
+static int publishDeviceTwinGetMsg(MQTTTRANSPORT_HANDLE_DATA* transport_data, MQTT_DEVICE_TWIN_ITEM* mqtt_info)
 {
     int result;
@@ -1020,4 +1072,7 @@
 }
 
+//
+// sendPendingGetTwinRequests will send any queued up GetTwin requests during a DoWork loop.
+//
 static void sendPendingGetTwinRequests(PMQTTTRANSPORT_HANDLE_DATA transportData)
 {
@@ -1031,8 +1086,8 @@
         (void)DList_RemoveEntryList(dev_twin_item);
 
-        if (publish_device_twin_get_message(transportData, msg_entry) != 0)
+        if (publishDeviceTwinGetMsg(transportData, msg_entry) != 0)
         {
             LogError("Failed sending pending get twin request");
-            destroy_device_twin_get_message(msg_entry);
+            destroyDeviceTwinGetMsg(msg_entry);
         }
         else
@@ -1045,9 +1100,11 @@
 }
 
-
+//
+// removeExpiredTwinRequestsFromList removes any requests that have timed out.
+//
 static void removeExpiredTwinRequestsFromList(PMQTTTRANSPORT_HANDLE_DATA transport_data, tickcounter_ms_t current_ms, DLIST_ENTRY* twin_list)
 {
     PDLIST_ENTRY list_item = twin_list->Flink;
-    
+
     while (list_item != twin_list)
     {
@@ -1076,5 +1133,5 @@
         {
             (void)DList_RemoveEntryList(list_item);
-            destroy_device_twin_get_message(msg_entry);
+            destroyDeviceTwinGetMsg(msg_entry);
         }
 
@@ -1084,4 +1141,7 @@
 }
 
+//
+// removeExpiredTwinRequests removes any requests that have timed out, regardless of how the request invoked.
+// 
 static void removeExpiredTwinRequests(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -1095,5 +1155,8 @@
 }
 
-static int publish_device_twin_message(MQTTTRANSPORT_HANDLE_DATA* transport_data, IOTHUB_DEVICE_TWIN* device_twin_info, MQTT_DEVICE_TWIN_ITEM* mqtt_info)
+// 
+// publishDeviceTwinMsg invokes umqtt to PUBLISH a request for the twin.
+//
+static int publishDeviceTwinMsg(MQTTTRANSPORT_HANDLE_DATA* transport_data, IOTHUB_DEVICE_TWIN* device_twin_info, MQTT_DEVICE_TWIN_ITEM* mqtt_info)
 {
     int result;
@@ -1149,4 +1212,10 @@
 }
 
+//
+// changeStateToSubscribeIfAllowed attempts to transition the state machine to subscribe, if our
+// current state will allow it.  
+// This function does NOT immediately send the SUBSCRIBE however, instead setting things up
+// so the next time DoWork is invoked then the SUBSCRIBE will happen.
+//
 static void changeStateToSubscribeIfAllowed(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -1160,4 +1229,7 @@
 }
 
+//
+// subscribeToNotifyStateIfNeeded sets up to subscribe to the server.
+//
 static int subscribeToNotifyStateIfNeeded(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -1191,5 +1263,9 @@
 }
 
-
+//
+// isSystemProperty returns whether a given property name in an MQTT TOPIC published to this device/module
+// is considered a "system".  MQTT does not have a protocol defined concept of system properties.  In this usage
+// it implies that the IOTHUB_MESSAGE_HANDLE has an API for direct manipulation of the property (e.g. IoTHubMessage_GetMessageId).
+//
 static bool isSystemProperty(const char* tokenData)
 {
@@ -1197,7 +1273,10 @@
     size_t propCount = sizeof(sysPropList) / sizeof(sysPropList[0]);
     size_t index = 0;
+    size_t tokenDataLength = strlen(tokenData);
+
     for (index = 0; index < propCount; index++)
     {
-        if (memcmp(tokenData, sysPropList[index].propName, sysPropList[index].propLength) == 0)
+        if (tokenDataLength >= sysPropList[index].propLength &&
+            memcmp(tokenData, sysPropList[index].propName, sysPropList[index].propLength) == 0)
         {
             result = true;
@@ -1208,8 +1287,14 @@
 }
 
-// Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_061: [ If the message is sent to an input queue, `IoTHubTransport_MQTT_Common_DoWork` shall parse out to the input queue name and store it in the message with IoTHubMessage_SetInputName ]
-// Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_062: [ If IoTHubTransport_MQTT_Common_DoWork receives a malformatted inputQueue, it shall fail ]
-static int addInputNamePropertyToMessage(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name)
-{
+//
+// addInputNamePropertyToMsg translates the input name (embedded in the MQTT topic name) into the IoTHubMessage handle
+// such that the application can call IoTHubMessage_GetInputName() to retrieve this.  This is only currently used 
+// in IoT Edge module to module message communication, so that this module receiving the message can know which module invoked in.
+//
+static int addInputNamePropertyToMsg(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name)
+{
+    // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_061: [ If the message is sent to an input queue, `IoTHubTransport_MQTT_Common_DoWork` shall parse out to the input queue name and store it in the message with IoTHubMessage_SetInputName ]
+    // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_31_062: [ If IoTHubTransport_MQTT_Common_DoWork receives a malformatted inputQueue, it shall fail ]
+
     int result = MU_FAILURE;
     int number_tokens_read = 0;
@@ -1261,4 +1346,8 @@
 }
 
+//
+// setMqttMessagePropertyIfPossible attempts to translate a "system" property into the IOTHUB_MESSAGE_HANDLE that will be provided to the 
+// application's callback. 
+//
 static int setMqttMessagePropertyIfPossible(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* propName, const char* propValue, size_t nameLen)
 {
@@ -1266,4 +1355,17 @@
     int result = 0;
 
+    if (nameLen > 5)
+    {
+        if (strcmp((const char*)&propName[nameLen - 5], MESSAGE_CREATION_TIME_UTC) == 0)
+        {
+            if (IoTHubMessage_SetMessageCreationTimeUtcSystemProperty(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK)
+            {
+                LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'CreationTimeUtc' property.");
+                result = MU_FAILURE;
+            }
+            return result;
+        }
+    }
+
     if (nameLen > 4)
     {
@@ -1309,4 +1411,13 @@
             return result;
         }
+        else if (strcmp((const char*)&propName[nameLen - 3], MESSAGE_USER_ID) == 0)
+        {
+            if (IoTHubMessage_SetMessageUserIdSystemProperty(IoTHubMessage, propValue) != IOTHUB_MESSAGE_OK)
+            {
+                LogError("Failed to set IOTHUB_MESSAGE_HANDLE 'userId' property.");
+                result = MU_FAILURE;
+            }
+            return result;
+        }
     }
 
@@ -1338,4 +1449,8 @@
 }
 
+//
+// extractMqttProperties parses the MQTT topic PUBLISH'd to this device/module, retrieves properties and fills out the 
+// IOTHUB_MESSAGE_HANDLE which will ultimately be delivered to the application callback.
+//
 static int extractMqttProperties(IOTHUB_MESSAGE_HANDLE IoTHubMessage, const char* topic_name, bool urldecode)
 {
@@ -1503,182 +1618,212 @@
 }
 
-static void mqtt_notification_callback(MQTT_MESSAGE_HANDLE msgHandle, void* callbackCtx)
-{
-    /* Tests_SRS_IOTHUB_MQTT_TRANSPORT_07_051: [ If msgHandle or callbackCtx is NULL, mqtt_notification_callback shall do nothing. ] */
-    if (msgHandle != NULL && callbackCtx != NULL)
-    {
-        /* Tests_SRS_IOTHUB_MQTT_TRANSPORT_07_052: [ mqtt_notification_callback shall extract the topic Name from the MQTT_MESSAGE_HANDLE. ] */
-        const char* topic_resp = mqttmessage_getTopicName(msgHandle);
-        if (topic_resp == NULL)
-        {
-            LogError("Failure: NULL topic name encountered");
+//
+// processTwinNotification processes device and module twin updates made by IoT Hub / IoT Edge.
+//
+static void processTwinNotification(PMQTTTRANSPORT_HANDLE_DATA transportData, MQTT_MESSAGE_HANDLE msgHandle, const char* topic_resp)
+{
+    size_t request_id;
+    int status_code;
+    bool notification_msg;
+
+    if (parseDeviceTwinTopicInfo(topic_resp, &notification_msg, &request_id, &status_code) != 0)
+    {
+        LogError("Failure: parsing device topic info");
+    }
+    else
+    {
+        const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle);
+        if (notification_msg)
+        {
+            transportData->transport_callbacks.twin_retrieve_prop_complete_cb(DEVICE_TWIN_UPDATE_PARTIAL, payload->message, payload->length, transportData->transport_ctx);
         }
         else
         {
-            PMQTTTRANSPORT_HANDLE_DATA transportData = (PMQTTTRANSPORT_HANDLE_DATA)callbackCtx;
-
-            IOTHUB_IDENTITY_TYPE type = retrieve_topic_type(topic_resp, STRING_c_str(transportData->topic_InputQueue));
-            if (type == IOTHUB_TYPE_DEVICE_TWIN)
-            {
-                size_t request_id;
-                int status_code;
-                bool notification_msg;
-                if (parse_device_twin_topic_info(topic_resp, &notification_msg, &request_id, &status_code) != 0)
-                {
-                    LogError("Failure: parsing device topic info");
-                }
-                else
-                {
-                    const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle);
-                    if (notification_msg)
+            PDLIST_ENTRY dev_twin_item = transportData->ack_waiting_queue.Flink;
+            while (dev_twin_item != &transportData->ack_waiting_queue)
+            {
+                DLIST_ENTRY saveListEntry;
+                saveListEntry.Flink = dev_twin_item->Flink;
+                MQTT_DEVICE_TWIN_ITEM* msg_entry = containingRecord(dev_twin_item, MQTT_DEVICE_TWIN_ITEM, entry);
+                if (request_id == msg_entry->packet_id)
+                {
+                    (void)DList_RemoveEntryList(dev_twin_item);
+                    if (msg_entry->device_twin_msg_type == RETRIEVE_PROPERTIES)
                     {
-                        transportData->transport_callbacks.twin_retrieve_prop_complete_cb(DEVICE_TWIN_UPDATE_PARTIAL, payload->message, payload->length, transportData->transport_ctx);
+                        if (msg_entry->userCallback == NULL)
+                        {
+                            /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_054: [ If type is IOTHUB_TYPE_DEVICE_TWIN, then on success if msg_type is RETRIEVE_PROPERTIES then mqttNotificationCallback shall call IoTHubClientCore_LL_RetrievePropertyComplete... ] */
+                            transportData->transport_callbacks.twin_retrieve_prop_complete_cb(DEVICE_TWIN_UPDATE_COMPLETE, payload->message, payload->length, transportData->transport_ctx);
+                            // Only after receiving device twin request should we start listening for patches.
+                            (void)subscribeToNotifyStateIfNeeded(transportData);
+                        }
+                        else
+                        {
+                            // This is a on-demand get twin request.
+                            msg_entry->userCallback(DEVICE_TWIN_UPDATE_COMPLETE, payload->message, payload->length, msg_entry->userContext);
+                        }
                     }
                     else
                     {
-                        PDLIST_ENTRY dev_twin_item = transportData->ack_waiting_queue.Flink;
-                        while (dev_twin_item != &transportData->ack_waiting_queue)
-                        {
-                            DLIST_ENTRY saveListEntry;
-                            saveListEntry.Flink = dev_twin_item->Flink;
-                            MQTT_DEVICE_TWIN_ITEM* msg_entry = containingRecord(dev_twin_item, MQTT_DEVICE_TWIN_ITEM, entry);
-                            if (request_id == msg_entry->packet_id)
-                            {
-                                (void)DList_RemoveEntryList(dev_twin_item);
-                                if (msg_entry->device_twin_msg_type == RETRIEVE_PROPERTIES)
-                                {
-                                    if (msg_entry->userCallback == NULL)
-                                    {
-                                        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_054: [ If type is IOTHUB_TYPE_DEVICE_TWIN, then on success if msg_type is RETRIEVE_PROPERTIES then mqtt_notification_callback shall call IoTHubClientCore_LL_RetrievePropertyComplete... ] */
-                                        transportData->transport_callbacks.twin_retrieve_prop_complete_cb(DEVICE_TWIN_UPDATE_COMPLETE, payload->message, payload->length, transportData->transport_ctx);
-                                        // Only after receiving device twin request should we start listening for patches.
-                                        (void)subscribeToNotifyStateIfNeeded(transportData);
-                                    }
-                                    else
-                                    {
-                                        // This is a on-demand get twin request.
-                                        msg_entry->userCallback(DEVICE_TWIN_UPDATE_COMPLETE, payload->message, payload->length, msg_entry->userContext);
-                                    }
-                                }
-                                else
-                                {
-                                    /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_055: [ if device_twin_msg_type is not RETRIEVE_PROPERTIES then mqtt_notification_callback shall call IoTHubClientCore_LL_ReportedStateComplete ] */
-                                    transportData->transport_callbacks.twin_rpt_state_complete_cb(msg_entry->iothub_msg_id, status_code, transportData->transport_ctx);
-                                    // Only after receiving device twin request should we start listening for patches.
-                                    (void)subscribeToNotifyStateIfNeeded(transportData);
-                                }
-
-                                destroy_device_twin_get_message(msg_entry);
-                                break;
-                            }
-                            dev_twin_item = saveListEntry.Flink;
-                        }
+                        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_055: [ if device_twin_msg_type is not RETRIEVE_PROPERTIES then mqttNotificationCallback shall call IoTHubClientCore_LL_ReportedStateComplete ] */
+                        transportData->transport_callbacks.twin_rpt_state_complete_cb(msg_entry->iothub_msg_id, status_code, transportData->transport_ctx);
+                        // Only after receiving device twin request should we start listening for patches.
+                        (void)subscribeToNotifyStateIfNeeded(transportData);
                     }
-                }
+
+                    destroyDeviceTwinGetMsg(msg_entry);
+                    break;
+                }
+                dev_twin_item = saveListEntry.Flink;
+            }
+        }
+    }
+}
+
+//
+// processDeviceMethodNotification processes a device and module method invocations made by IoT Hub / IoT Edge.
+//
+static void processDeviceMethodNotification(PMQTTTRANSPORT_HANDLE_DATA transportData, MQTT_MESSAGE_HANDLE msgHandle, const char* topic_resp)
+{
+    STRING_HANDLE method_name = STRING_new();
+    if (method_name == NULL)
+    {
+        LogError("Failure: allocating method_name string value");
+    }
+    else
+    {
+        DEVICE_METHOD_INFO* dev_method_info = malloc(sizeof(DEVICE_METHOD_INFO));
+        if (dev_method_info == NULL)
+        {
+            LogError("Failure: allocating DEVICE_METHOD_INFO object");
+        }
+        else
+        {
+            dev_method_info->request_id = STRING_new();
+            if (dev_method_info->request_id == NULL)
+            {
+                LogError("Failure constructing request_id string");
+                free(dev_method_info);
+            }
+            else if (retrievDeviceMethodRidInfo(topic_resp, method_name, dev_method_info->request_id) != 0)
+            {
+                LogError("Failure: retrieve device topic info");
+                STRING_delete(dev_method_info->request_id);
+                free(dev_method_info);
+            }
+            else
+            {
+                /* CodesSRS_IOTHUB_MQTT_TRANSPORT_07_053: [ If type is IOTHUB_TYPE_DEVICE_METHODS, then on success mqttNotificationCallback shall call IoTHubClientCore_LL_DeviceMethodComplete. ] */
+                const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle);
+                if (transportData->transport_callbacks.method_complete_cb(STRING_c_str(method_name), payload->message, payload->length, (void*)dev_method_info, transportData->transport_ctx) != 0)
+                {
+                    LogError("Failure: IoTHubClientCore_LL_DeviceMethodComplete");
+                    STRING_delete(dev_method_info->request_id);
+                    free(dev_method_info);
+                }
+            }
+        }
+        STRING_delete(method_name);
+    }
+}
+
+// 
+// processIncomingMessageNotification processes both C2D messages and messages sent from one IoT Edge module into this module
+//
+static void processIncomingMessageNotification(PMQTTTRANSPORT_HANDLE_DATA transportData, MQTT_MESSAGE_HANDLE msgHandle, const char* topic_resp, IOTHUB_IDENTITY_TYPE type)
+{
+    const APP_PAYLOAD* appPayload = mqttmessage_getApplicationMsg(msgHandle);
+    IOTHUB_MESSAGE_HANDLE IoTHubMessage = IoTHubMessage_CreateFromByteArray(appPayload->message, appPayload->length);
+    if (IoTHubMessage == NULL)
+    {
+        LogError("Failure: IotHub Message creation has failed.");
+    }
+    else if ((type == IOTHUB_TYPE_EVENT_QUEUE) && (addInputNamePropertyToMsg(IoTHubMessage, topic_resp) != 0))
+    {
+        LogError("failure adding input name to property.");
+    }
+    else if (extractMqttProperties(IoTHubMessage, topic_resp, transportData->auto_url_encode_decode) != 0)
+    {
+        LogError("failure extracting mqtt properties.");
+    }
+    else
+    {
+        MESSAGE_CALLBACK_INFO* messageData = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO));
+        if (messageData == NULL)
+        {
+            LogError("malloc failed");
+        }
+        else
+        {
+            messageData->messageHandle = IoTHubMessage;
+            messageData->transportContext = NULL;
+
+            if (type == IOTHUB_TYPE_EVENT_QUEUE)
+            {
+                // Codes_SRS_IOTHUB_MQTT_TRANSPORT_31_065: [ If type is IOTHUB_TYPE_TELEMETRY and sent to an input queue, then on success `mqttNotificationCallback` shall call `IoTHubClient_LL_MessageCallback`. ]
+                if (!transportData->transport_callbacks.msg_input_cb(messageData, transportData->transport_ctx))
+                {
+                    LogError("IoTHubClientCore_LL_MessageCallbackreturned false");
+
+                    IoTHubMessage_Destroy(IoTHubMessage);
+                    free(messageData);
+                }
+            }
+            else
+            {
+                /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_056: [ If type is IOTHUB_TYPE_TELEMETRY, then on success mqttNotificationCallback shall call IoTHubClientCore_LL_MessageCallback. ] */
+                if (!transportData->transport_callbacks.msg_cb(messageData, transportData->transport_ctx))
+                {
+                    LogError("IoTHubClientCore_LL_MessageCallback returned false");
+                    IoTHubMessage_Destroy(IoTHubMessage);
+                    free(messageData);
+                }
+            }
+        }
+    }
+}
+
+//
+// mqttNotificationCallback processes incoming PUBLISH messages sent from Hub (or IoT Edge) to this device.
+// This function is invoked by umqtt.  It determines what topic the PUBLISH was directed at (e.g. Device Twin, Method, etc.),
+// performs further parsing based on topic, and translates this call up to "iothub_client" layer for ultimate delivery to application callback.
+// 
+static void mqttNotificationCallback(MQTT_MESSAGE_HANDLE msgHandle, void* callbackCtx)
+{
+    /* Tests_SRS_IOTHUB_MQTT_TRANSPORT_07_051: [ If msgHandle or callbackCtx is NULL, mqttNotificationCallback shall do nothing. ] */
+    if (msgHandle != NULL && callbackCtx != NULL)
+    {
+        /* Tests_SRS_IOTHUB_MQTT_TRANSPORT_07_052: [ mqttNotificationCallback shall extract the topic Name from the MQTT_MESSAGE_HANDLE. ] */
+        const char* topic_resp = mqttmessage_getTopicName(msgHandle);
+        if (topic_resp == NULL)
+        {
+            LogError("Failure: NULL topic name encountered");
+        }
+        else
+        {
+            PMQTTTRANSPORT_HANDLE_DATA transportData = (PMQTTTRANSPORT_HANDLE_DATA)callbackCtx;
+
+            IOTHUB_IDENTITY_TYPE type = retrieveTopicType(topic_resp, STRING_c_str(transportData->topic_InputQueue));
+            if (type == IOTHUB_TYPE_DEVICE_TWIN)
+            {
+                processTwinNotification(transportData, msgHandle, topic_resp);
             }
             else if (type == IOTHUB_TYPE_DEVICE_METHODS)
             {
-                STRING_HANDLE method_name = STRING_new();
-                if (method_name == NULL)
-                {
-                    LogError("Failure: allocating method_name string value");
-                }
-                else
-                {
-                    DEVICE_METHOD_INFO* dev_method_info = malloc(sizeof(DEVICE_METHOD_INFO));
-                    if (dev_method_info == NULL)
-                    {
-                        LogError("Failure: allocating DEVICE_METHOD_INFO object");
-                    }
-                    else
-                    {
-                        dev_method_info->request_id = STRING_new();
-                        if (dev_method_info->request_id == NULL)
-                        {
-                            LogError("Failure constructing request_id string");
-                            free(dev_method_info);
-                        }
-                        else if (retrieve_device_method_rid_info(topic_resp, method_name, dev_method_info->request_id) != 0)
-                        {
-                            LogError("Failure: retrieve device topic info");
-                            STRING_delete(dev_method_info->request_id);
-                            free(dev_method_info);
-                        }
-                        else
-                        {
-                            /* CodesSRS_IOTHUB_MQTT_TRANSPORT_07_053: [ If type is IOTHUB_TYPE_DEVICE_METHODS, then on success mqtt_notification_callback shall call IoTHubClientCore_LL_DeviceMethodComplete. ] */
-                            const APP_PAYLOAD* payload = mqttmessage_getApplicationMsg(msgHandle);
-                            if (transportData->transport_callbacks.method_complete_cb(STRING_c_str(method_name), payload->message, payload->length, (void*)dev_method_info, transportData->transport_ctx) != 0)
-                            {
-                                LogError("Failure: IoTHubClientCore_LL_DeviceMethodComplete");
-                                STRING_delete(dev_method_info->request_id);
-                                free(dev_method_info);
-                            }
-                        }
-                    }
-                    STRING_delete(method_name);
-                }
+                processDeviceMethodNotification(transportData, msgHandle, topic_resp);
             }
             else
             {
-                const APP_PAYLOAD* appPayload = mqttmessage_getApplicationMsg(msgHandle);
-                IOTHUB_MESSAGE_HANDLE IoTHubMessage = IoTHubMessage_CreateFromByteArray(appPayload->message, appPayload->length);
-                if (IoTHubMessage == NULL)
-                {
-                    LogError("Failure: IotHub Message creation has failed.");
-                }
-                else
-                {
-                    if ((type == IOTHUB_TYPE_EVENT_QUEUE) && (addInputNamePropertyToMessage(IoTHubMessage, topic_resp) != 0))
-                    {
-                        LogError("failure adding input name to property.");
-                    }
-                    // Will need to update this when the service has messages that can be rejected
-                    else if (extractMqttProperties(IoTHubMessage, topic_resp, transportData->auto_url_encode_decode) != 0)
-                    {
-                        LogError("failure extracting mqtt properties.");
-                    }
-                    else
-                    {
-                        MESSAGE_CALLBACK_INFO* messageData = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO));
-                        if (messageData == NULL)
-                        {
-                            LogError("malloc failed");
-                        }
-                        else
-                        {
-                            messageData->messageHandle = IoTHubMessage;
-                            messageData->transportContext = NULL;
-
-                            if (type == IOTHUB_TYPE_EVENT_QUEUE)
-                            {
-                                // Codes_SRS_IOTHUB_MQTT_TRANSPORT_31_065: [ If type is IOTHUB_TYPE_TELEMETRY and sent to an input queue, then on success `mqtt_notification_callback` shall call `IoTHubClient_LL_MessageCallback`. ]
-                                if (!transportData->transport_callbacks.msg_input_cb(messageData, transportData->transport_ctx))
-                                {
-                                    LogError("IoTHubClientCore_LL_MessageCallbackreturned false");
-
-                                    IoTHubMessage_Destroy(IoTHubMessage);
-                                    free(messageData);
-                                }
-                            }
-                            else
-                            {
-                                /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_056: [ If type is IOTHUB_TYPE_TELEMETRY, then on success mqtt_notification_callback shall call IoTHubClientCore_LL_MessageCallback. ] */
-                                if (!transportData->transport_callbacks.msg_cb(messageData, transportData->transport_ctx))
-                                {
-                                    LogError("IoTHubClientCore_LL_MessageCallback returned false");
-                                    IoTHubMessage_Destroy(IoTHubMessage);
-                                    free(messageData);
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-static void mqtt_operation_complete_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_RESULT actionResult, const void* msgInfo, void* callbackCtx)
+                processIncomingMessageNotification(transportData, msgHandle, topic_resp, type);
+            }
+        }
+    }
+}
+
+//
+// mqttOperationCompleteCallback is invoked by umqtt when an operation initiated by the device completes.
+// Examples of device initiated operations include PUBLISH, CONNECT, and SUBSCRIBE.
+//
+static void mqttOperationCompleteCallback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_RESULT actionResult, const void* msgInfo, void* callbackCtx)
 {
     (void)handle;
@@ -1705,5 +1850,5 @@
                         {
                             (void)DList_RemoveEntryList(currentListEntry); //First remove the item from Waiting for Ack List.
-                            sendMsgComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_OK);
+                            notifyApplicationOfSendMessageComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_OK);
                             free(mqttMsgEntry);
                         }
@@ -1753,5 +1898,5 @@
                             transport_data->isRecoverableError = false;
                         }
-                        LogError("Connection Not Accepted: 0x%x: %s", connack->returnCode, retrieve_mqtt_return_codes(connack->returnCode));
+                        LogError("Connection Not Accepted: 0x%x: %s", connack->returnCode, retrieveMqttReturnCodes(connack->returnCode));
                         transport_data->mqttClientStatus = MQTT_CLIENT_STATUS_PENDING_CLOSE;
                         transport_data->currPacketState = PACKET_TYPE_ERROR;
@@ -1822,10 +1967,13 @@
     {
         OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport);
-        set_saved_tls_options(transport_data, options);
+        setSavedTlsOptions(transport_data, options);
         DestroyXioTransport(transport_data);
     }
 }
 
-static void mqtt_disconnect_cb(void* ctx)
+// 
+// processDisconnectCallback is a callback invoked by umqtt to signal that the disconnection has completed.
+// 
+static void processDisconnectCallback(void* ctx)
 {
     if (ctx != NULL)
@@ -1836,4 +1984,10 @@
 }
 
+//
+// DisconnectFromClient will tear down the existing MQTT connection, trying to gracefully send an MQTT DISCONNECT (with a timeout),
+// destroy the underlying xio for network communication, and update the transport_data state machine appropriately.
+//
+//NOTE: After a call to DisconnectFromClient, determine if appropriate to also call
+//      transport_data->transport_callbacks.connection_status_cb().
 static void DisconnectFromClient(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -1843,5 +1997,5 @@
         {
             OPTIONHANDLER_HANDLE options = xio_retrieveoptions(transport_data->xioTransport);
-            set_saved_tls_options(transport_data, options);
+            setSavedTlsOptions(transport_data, options);
         }
         // Ensure the disconnect message is sent
@@ -1849,5 +2003,5 @@
         {
             transport_data->disconnect_recv_flag = 0;
-            (void)mqtt_client_disconnect(transport_data->mqttClient, mqtt_disconnect_cb, &transport_data->disconnect_recv_flag);
+            (void)mqtt_client_disconnect(transport_data->mqttClient, processDisconnectCallback, &transport_data->disconnect_recv_flag);
             size_t disconnect_ctr = 0;
             do
@@ -1866,5 +2020,8 @@
 }
 
-static void mqtt_error_callback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_ERROR error, void* callbackCtx)
+//
+// processErrorCallback is invoked by umqtt when an error has occurred.
+//
+static void processErrorCallback(MQTT_CLIENT_HANDLE handle, MQTT_CLIENT_EVENT_ERROR error, void* callbackCtx)
 {
     (void)handle;
@@ -1935,4 +2092,8 @@
 }
 
+//
+// SubscribeToMqttProtocol determines which topics we should SUBSCRIBE to, based on existing state, and then 
+// invokes the underlying umqtt layer to send the SUBSCRIBE across the network.
+//
 static void SubscribeToMqttProtocol(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -1941,5 +2102,5 @@
         uint32_t topic_subscription = 0;
         size_t subscribe_count = 0;
-        uint16_t packet_id = get_next_packet_id(transport_data);
+        uint16_t packet_id = getNextPacketId(transport_data);
         SUBSCRIBE_PAYLOAD subscribe[SUBSCRIBE_TOPIC_COUNT];
         if ((transport_data->topic_MqttMessage != NULL) && (SUBSCRIBE_TELEMETRY_TOPIC & transport_data->topics_ToSubscribe))
@@ -2008,4 +2169,7 @@
 }
 
+//
+// RetrieveMessagePayload translates the payload set by the application in messageHandle into payload/length for sending across the network.
+//
 static bool RetrieveMessagePayload(IOTHUB_MESSAGE_HANDLE messageHandle, const unsigned char** payload, size_t* length)
 {
@@ -2048,5 +2212,13 @@
 }
 
-static void process_queued_ack_messages(PMQTTTRANSPORT_HANDLE_DATA transport_data)
+//
+// ProcessPendingTelemetryMessages examines each telemetry message the device/module has sent that hasn't yet been PUBACK'd.  
+// For each message, it might:
+// * Ignore it, if its timeout has not yet been reached.
+// * Attempt to retry PUBLISH the message, if has remaining retries left.
+// * Stop attempting to send the message.  This will result in tearing down the underlying MQTT/TCP connection because it indicates 
+//   something is wrong.
+//
+static void ProcessPendingTelemetryMessages(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
     PDLIST_ENTRY current_entry = transport_data->telemetry_waitingForAck.Flink;
@@ -2063,9 +2235,14 @@
             if (msg_detail_entry->retryCount >= MAX_SEND_RECOUNT_LIMIT)
             {
-                sendMsgComplete(msg_detail_entry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT);
+                notifyApplicationOfSendMessageComplete(msg_detail_entry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_MESSAGE_TIMEOUT);
                 (void)DList_RemoveEntryList(current_entry);
                 free(msg_detail_entry);
 
                 DisconnectFromClient(transport_data);
+                if (!transport_data->isRetryExpiredCallbackCalled) // Only call once
+                {
+                    transport_data->transport_callbacks.connection_status_cb(IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED, transport_data->transport_ctx);
+                    transport_data->isRetryExpiredCallbackCalled = true;
+                }
             }
             else
@@ -2080,12 +2257,12 @@
                     {
                         (void)DList_RemoveEntryList(current_entry);
-                        sendMsgComplete(msg_detail_entry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
+                        notifyApplicationOfSendMessageComplete(msg_detail_entry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
                     }
                     else
                     {
-                        if (publish_mqtt_telemetry_msg(transport_data, msg_detail_entry, messagePayload, messageLength) != 0)
+                        if (publishTelemetryMsg(transport_data, msg_detail_entry, messagePayload, messageLength) != 0)
                         {
                             (void)DList_RemoveEntryList(current_entry);
-                            sendMsgComplete(msg_detail_entry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
+                            notifyApplicationOfSendMessageComplete(msg_detail_entry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
                             free(msg_detail_entry);
                         }
@@ -2103,5 +2280,9 @@
 }
 
-static int GetTransportProviderIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_data)
+//
+// CreateTransportProviderIfNecessary will create the underlying xioTransport (which handles networking I/O) and 
+// set its options, assuming the xioTransport does not already exist.
+//
+static int CreateTransportProviderIfNecessary(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
     int result;
@@ -2138,5 +2319,5 @@
                 {
                     // The tlsio has the options, so our copy can be deleted
-                    set_saved_tls_options(transport_data, NULL);
+                    setSavedTlsOptions(transport_data, NULL);
                     result = 0;
                 }
@@ -2167,4 +2348,7 @@
 }
 
+//
+// buildClientId creates the MQTT ClientId of this device or module.
+//
 static STRING_HANDLE buildClientId(const char* device_id, const char* module_id)
 {
@@ -2179,4 +2363,8 @@
 }
 
+//
+// buildConfigForUsernameStep2IfNeeded builds the MQTT username.  IoT Hub uses the query string of the userName to optionally
+// specify SDK information, product information optionally specified by the application, and optionally the PnP ModelId.
+//
 static int buildConfigForUsernameStep2IfNeeded(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -2191,7 +2379,7 @@
         // TODO: The preview API version in SDK is only scoped to scenarios that require the modelId to be set.
         // https://github.com/Azure/azure-iot-sdk-c/issues/1547 tracks removing this once non-preview API versions support modelId.
-        const char* apiVersion = (modelId != NULL) ? IOTHUB_API_PREVIEW_VERSION : IOTHUB_API_VERSION;
+        const char* apiVersion = IOTHUB_API_VERSION;
         const char* appSpecifiedProductInfo = transport_data->transport_callbacks.prod_info_cb(transport_data->transport_ctx);
-        STRING_HANDLE productInfoEncoded = NULL; 
+        STRING_HANDLE productInfoEncoded = NULL;
 
         if ((productInfoEncoded = URL_EncodeString((appSpecifiedProductInfo != NULL) ? appSpecifiedProductInfo : DEFAULT_IOTHUB_PRODUCT_IDENTIFIER)) == NULL)
@@ -2254,4 +2442,8 @@
 }
 
+//
+// SendMqttConnectMsg sends the MQTT CONNECT message across the network.  This function may also
+// perform security functionality for building up the required token (optionally invoking into DPS if configured to do so)
+//
 static int SendMqttConnectMsg(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
@@ -2322,5 +2514,5 @@
             options.qualityOfServiceValue = DELIVER_AT_LEAST_ONCE;
 
-            if (GetTransportProviderIfNecessary(transport_data) == 0)
+            if (CreateTransportProviderIfNecessary(transport_data) == 0)
             {
                 transport_data->conn_attempted = true;
@@ -2333,5 +2525,5 @@
                 {
                     transport_data->currPacketState = CONNECT_TYPE;
-                    transport_data->isRetryExpiredCallbackSet = false;
+                    transport_data->isRetryExpiredCallbackCalled = false;
                     (void)tickcounter_get_current_ms(transport_data->msgTickCounter, &transport_data->mqtt_connect_time);
                     result = 0;
@@ -2353,5 +2545,12 @@
 }
 
-static int InitializeConnection(PMQTTTRANSPORT_HANDLE_DATA transport_data)
+//
+// UpdateMqttConnectionStateIfNeeded is used for updating MQTT's underlying connection status during a DoWork loop.
+// Among this function's responsibilities: 
+// * Attempt to establish an MQTT connection if one has not been already.
+// * Retries failed connection, if in the correct state.
+// * Processes deferred disconnect requests
+// * Checks timeouts, for instance on connection establishment time as well as SaS token lifetime (if SAS used)
+static int UpdateMqttConnectionStateIfNeeded(PMQTTTRANSPORT_HANDLE_DATA transport_data)
 {
     int result = 0;
@@ -2393,8 +2592,8 @@
             {
                 // Set callback if retry expired
-                if (!transport_data->isRetryExpiredCallbackSet)
+                if (!transport_data->isRetryExpiredCallbackCalled)
                 {
                     transport_data->transport_callbacks.connection_status_cb(IOTHUB_CLIENT_CONNECTION_UNAUTHENTICATED, IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED, transport_data->transport_ctx);
-                    transport_data->isRetryExpiredCallbackSet = true;
+                    transport_data->isRetryExpiredCallbackCalled = true;
                 }
                 result = MU_FAILURE;
@@ -2443,5 +2642,5 @@
                 if (cred_type != IOTHUB_CREDENTIAL_TYPE_X509 && cred_type != IOTHUB_CREDENTIAL_TYPE_X509_ECC)
                 {
-                    size_t sas_token_expiry = IoTHubClient_Auth_Get_SasToken_Expiry(transport_data->authorization_module);
+                    uint64_t sas_token_expiry = IoTHubClient_Auth_Get_SasToken_Expiry(transport_data->authorization_module);
                     if ((current_time - transport_data->mqtt_connect_time) / 1000 > (sas_token_expiry*SAS_REFRESH_MULTIPLIER))
                     {
@@ -2494,4 +2693,7 @@
 }
 
+//
+// buildMqttEventString creates the MQTT topic for this device (and optionally module) to PUBLISH telemetry to.
+//
 static STRING_HANDLE buildMqttEventString(const char* device_id, const char* module_id)
 {
@@ -2506,4 +2708,7 @@
 }
 
+//
+// buildDevicesAndModulesPath builds the path used when generating a SaS token for this request.
+//
 static STRING_HANDLE buildDevicesAndModulesPath(const IOTHUB_CLIENT_CONFIG* upperConfig, const char* moduleId)
 {
@@ -2518,4 +2723,45 @@
 }
 
+//
+// buildTopicMqttMsg builds the MQTT topic that is used for C2D messages sent to a device or module-to-module messages for a module running in IoT Edge
+//
+static STRING_HANDLE buildTopicMqttMsg(const char* device_id, const char* module_id)
+{
+    if (module_id == NULL)
+    {
+        return STRING_construct_sprintf(TOPIC_DEVICE_MSG, device_id);
+    }
+    else
+    {
+        return STRING_construct_sprintf(TOPIC_DEVICE_MODULE_MSG, device_id, module_id);
+    }
+}
+
+//
+// checkModuleIdsEqual verifies that module Ids coming from different upper layers are equal.
+//
+static bool checkModuleIdsEqual(const char* transportModuleId, const char* deviceModuleId)
+{
+    if ((transportModuleId != NULL) && (deviceModuleId == NULL))
+    {
+        return false;
+    }
+    else if ((transportModuleId == NULL) && (deviceModuleId != NULL))
+    {
+        return false;
+    }
+    else if ((transportModuleId == NULL) && (deviceModuleId == NULL))
+    {
+        return true;
+    }
+    else
+    {
+        return (0 == strcmp(transportModuleId, deviceModuleId));
+    }
+}
+
+//
+// InitializeTransportHandleData creates a MQTTTRANSPORT_HANDLE_DATA.
+//
 static PMQTTTRANSPORT_HANDLE_DATA InitializeTransportHandleData(const IOTHUB_CLIENT_CONFIG* upperConfig, PDLIST_ENTRY waitingToSend, IOTHUB_AUTHORIZATION_HANDLE auth_module, const char* moduleId)
 {
@@ -2531,5 +2777,5 @@
         {
             LogError("Invalid Argument: iotHubName is empty");
-            free_transport_handle_data(state);
+            freeTransportHandleData(state);
             state = NULL;
         }
@@ -2538,5 +2784,5 @@
         {
             LogError("Failed creating default retry control");
-            free_transport_handle_data(state);
+            freeTransportHandleData(state);
             state = NULL;
         }
@@ -2544,5 +2790,5 @@
         {
             LogError("failure constructing device_id.");
-            free_transport_handle_data(state);
+            freeTransportHandleData(state);
             state = NULL;
         }
@@ -2550,5 +2796,5 @@
         {
             LogError("failure constructing module_id.");
-            free_transport_handle_data(state);
+            freeTransportHandleData(state);
             state = NULL;
         }
@@ -2556,5 +2802,5 @@
         {
             LogError("failure constructing devicesPath.");
-            free_transport_handle_data(state);
+            freeTransportHandleData(state);
             state = NULL;
         }
@@ -2564,14 +2810,14 @@
             {
                 LogError("Could not create topic_MqttEvent for MQTT");
-                free_transport_handle_data(state);
+                freeTransportHandleData(state);
                 state = NULL;
             }
             else
             {
-                state->mqttClient = mqtt_client_init(mqtt_notification_callback, mqtt_operation_complete_callback, state, mqtt_error_callback, state);
+                state->mqttClient = mqtt_client_init(mqttNotificationCallback, mqttOperationCompleteCallback, state, processErrorCallback, state);
                 if (state->mqttClient == NULL)
                 {
                     LogError("failure initializing mqtt client.");
-                    free_transport_handle_data(state);
+                    freeTransportHandleData(state);
                     state = NULL;
                 }
@@ -2591,10 +2837,10 @@
                     {
                         LogError("failure constructing host address.");
-                        free_transport_handle_data(state);
+                        freeTransportHandleData(state);
                         state = NULL;
                     }
                     else if ((state->configPassedThroughUsername = buildConfigForUsernameStep1(upperConfig, moduleId)) == NULL)
                     {
-                        free_transport_handle_data(state);
+                        freeTransportHandleData(state);
                         state = NULL;
                     }
@@ -2617,5 +2863,5 @@
 
                         state->isDestroyCalled = false;
-                        state->isRetryExpiredCallbackSet = false;
+                        state->isRetryExpiredCallbackCalled = false;
                         state->isRegistered = false;
                         state->device_twin_get_sent = false;
@@ -2641,4 +2887,95 @@
 }
 
+//
+// ProcessSubackDoWork processes state transitions responding to a SUBACK packet.
+// This does NOT occur once we receive the SUBACK packet immediately; instead the work is 
+// deferred to the DoWork loop.
+//
+static void ProcessSubackDoWork(PMQTTTRANSPORT_HANDLE_DATA transport_data)
+{
+    if ((transport_data->topic_NotifyState != NULL || transport_data->topic_GetState != NULL) &&
+        !transport_data->device_twin_get_sent)
+    {
+        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_055: [ IoTHubTransport_MQTT_Common_DoWork shall send a device twin get property message upon successfully retrieving a SUBACK on device twin topics. ] */
+        MQTT_DEVICE_TWIN_ITEM* mqtt_info;
+
+        if ((mqtt_info = createDeviceTwinMsg(transport_data, RETRIEVE_PROPERTIES, 0)) == NULL)
+        {
+            LogError("Failure: could not create message for twin get command");
+        }
+        else if (publishDeviceTwinGetMsg(transport_data, mqtt_info) == 0)
+        {
+            transport_data->device_twin_get_sent = true;
+        }
+        else
+        {
+            LogError("Failure: sending device twin get command.");
+            destroyDeviceTwinGetMsg(mqtt_info);
+        }
+    }
+
+    // Publish can be called now and in any event we need to transition out of this state.
+    transport_data->currPacketState = PUBLISH_TYPE;
+}
+
+//
+// ProcessPublishStateDoWork traverses all messages waiting to be sent and attempts to PUBLISH them.
+//
+static void ProcessPublishStateDoWork(PMQTTTRANSPORT_HANDLE_DATA transport_data)
+{
+    PDLIST_ENTRY currentListEntry = transport_data->waitingToSend->Flink;
+    /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [IoTHubTransport_MQTT_Common_DoWork shall inspect the "waitingToSend" DLIST passed in config structure.] */
+    while (currentListEntry != transport_data->waitingToSend)
+    {
+        IOTHUB_MESSAGE_LIST* iothubMsgList = containingRecord(currentListEntry, IOTHUB_MESSAGE_LIST, entry);
+        DLIST_ENTRY savedFromCurrentListEntry;
+        savedFromCurrentListEntry.Flink = currentListEntry->Flink;
+
+        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [IoTHubTransport_MQTT_Common_DoWork shall inspect the "waitingToSend" DLIST passed in config structure.] */
+        size_t messageLength;
+        const unsigned char* messagePayload = NULL;
+        if (!RetrieveMessagePayload(iothubMsgList->messageHandle, &messagePayload, &messageLength))
+        {
+            (void)(DList_RemoveEntryList(currentListEntry));
+            notifyApplicationOfSendMessageComplete(iothubMsgList, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
+            LogError("Failure result from IoTHubMessage_GetData");
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_029: [IoTHubTransport_MQTT_Common_DoWork shall create a MQTT_MESSAGE_HANDLE and pass this to a call to mqtt_client_publish.] */
+            MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = (MQTT_MESSAGE_DETAILS_LIST*)malloc(sizeof(MQTT_MESSAGE_DETAILS_LIST));
+            if (mqttMsgEntry == NULL)
+            {
+                LogError("Allocation Error: Failure allocating MQTT Message Detail List.");
+            }
+            else
+            {
+                mqttMsgEntry->retryCount = 0;
+                mqttMsgEntry->iotHubMessageEntry = iothubMsgList;
+                mqttMsgEntry->packet_id = getNextPacketId(transport_data);
+                if (publishTelemetryMsg(transport_data, mqttMsgEntry, messagePayload, messageLength) != 0)
+                {
+                    (void)(DList_RemoveEntryList(currentListEntry));
+                    notifyApplicationOfSendMessageComplete(iothubMsgList, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
+                    free(mqttMsgEntry);
+                }
+                else
+                {
+                    // Remove the message from the waiting queue ...
+                    (void)(DList_RemoveEntryList(currentListEntry));
+                    // and add it to the ack queue
+                    DList_InsertTailList(&(transport_data->telemetry_waitingForAck), &(mqttMsgEntry->entry));
+                }
+            }
+        }
+        currentListEntry = savedFromCurrentListEntry.Flink;
+    }
+
+    if (transport_data->twin_resp_sub_recv)
+    {
+        sendPendingGetTwinRequests(transport_data);
+    }
+}
+
 TRANSPORT_LL_HANDLE IoTHubTransport_MQTT_Common_Create(const IOTHUBTRANSPORT_CONFIG* config, MQTT_GET_IO_TRANSPORT get_io_transport, TRANSPORT_CALLBACKS_INFO* cb_info, void* ctx)
 {
@@ -2741,5 +3078,5 @@
             PDLIST_ENTRY currentEntry = DList_RemoveHeadList(&transport_data->telemetry_waitingForAck);
             MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = containingRecord(currentEntry, MQTT_MESSAGE_DETAILS_LIST, entry);
-            sendMsgComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY);
+            notifyApplicationOfSendMessageComplete(mqttMsgEntry->iotHubMessageEntry, transport_data, IOTHUB_CLIENT_CONFIRMATION_BECAUSE_DESTROY);
             free(mqttMsgEntry);
         }
@@ -2758,5 +3095,5 @@
             }
 
-            destroy_device_twin_get_message(mqtt_device_twin);
+            destroyDeviceTwinGetMsg(mqtt_device_twin);
         }
         while (!DList_IsListEmpty(&transport_data->pending_get_twin_queue))
@@ -2768,10 +3105,10 @@
             mqtt_device_twin->userCallback(DEVICE_TWIN_UPDATE_COMPLETE, NULL, 0, mqtt_device_twin->userContext);
 
-            destroy_device_twin_get_message(mqtt_device_twin);
+            destroyDeviceTwinGetMsg(mqtt_device_twin);
         }
 
         /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_014: [IoTHubTransport_MQTT_Common_Destroy shall free all the resources currently in use.] */
         /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_012: [ `IoTHubTransport_MQTT_Common_Destroy` shall free the stored proxy options. ]*/
-        free_transport_handle_data(transport_data);
+        freeTransportHandleData(transport_data);
     }
 }
@@ -2860,5 +3197,5 @@
         MQTT_DEVICE_TWIN_ITEM* mqtt_info;
 
-        if ((mqtt_info = create_device_twin_message(transport_data, RETRIEVE_PROPERTIES, 0)) == NULL)
+        if ((mqtt_info = createDeviceTwinMsg(transport_data, RETRIEVE_PROPERTIES, 0)) == NULL)
         {
             LogError("Failed creating the device twin get request message");
@@ -2869,5 +3206,5 @@
         {
             LogError("Failed setting the get twin request enqueue time");
-            destroy_device_twin_get_message(mqtt_info);
+            destroyDeviceTwinGetMsg(mqtt_info);
             // Codes_SRS_IOTHUB_MQTT_TRANSPORT_09_003: [ If any failure occurs, IoTHubTransport_MQTT_Common_GetTwinAsync shall return IOTHUB_CLIENT_ERROR ]
             result = IOTHUB_CLIENT_ERROR;
@@ -2949,5 +3286,5 @@
 
             /*Codes_SRS_IOTHUB_MQTT_TRANSPORT_12_011 : [IoTHubTransport_MQTT_Common_Unsubscribe_DeviceMethod shall send the unsubscribe.]*/
-            if (mqtt_client_unsubscribe(transport_data->mqttClient, get_next_packet_id(transport_data), unsubscribe, 1) != 0)
+            if (mqtt_client_unsubscribe(transport_data->mqttClient, getNextPacketId(transport_data), unsubscribe, 1) != 0)
             {
                 LogError("Failure calling mqtt_client_unsubscribe");
@@ -2981,5 +3318,5 @@
         else
         {
-            if (publish_device_method_message(transport_data, status, dev_method_info->request_id, response, respSize) != 0)
+            if (publishDeviceMethodResponseMsg(transport_data, status, dev_method_info->request_id, response, respSize) != 0)
             {
                 /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_07_051: [ If any error is encountered, IoTHubTransport_MQTT_Common_DeviceMethod_Response shall return a non-zero value. ] */
@@ -3004,16 +3341,4 @@
 }
 
-static STRING_HANDLE buildTopicMqttMessage(const char* device_id, const char* module_id)
-{
-    if (module_id == NULL)
-    {
-        return STRING_construct_sprintf(TOPIC_DEVICE_MSG, device_id);
-    }
-    else
-    {
-        return STRING_construct_sprintf(TOPIC_DEVICE_MODULE_MSG, device_id, module_id);
-    }
-}
-
 int IoTHubTransport_MQTT_Common_Subscribe(TRANSPORT_LL_HANDLE handle)
 {
@@ -3029,5 +3354,5 @@
     {
         /* Code_SRS_IOTHUB_MQTT_TRANSPORT_07_016: [IoTHubTransport_MQTT_Common_Subscribe shall set a flag to enable mqtt_client_subscribe to be called to subscribe to the Message Topic.] */
-        transport_data->topic_MqttMessage = buildTopicMqttMessage(STRING_c_str(transport_data->device_id), STRING_c_str(transport_data->module_id));
+        transport_data->topic_MqttMessage = buildTopicMqttMsg(STRING_c_str(transport_data->device_id), STRING_c_str(transport_data->module_id));
         if (transport_data->topic_MqttMessage == NULL)
         {
@@ -3055,5 +3380,5 @@
         const char* unsubscribe[1];
         unsubscribe[0] = STRING_c_str(transport_data->topic_MqttMessage);
-        if (mqtt_client_unsubscribe(transport_data->mqttClient, get_next_packet_id(transport_data), unsubscribe, 1) != 0)
+        if (mqtt_client_unsubscribe(transport_data->mqttClient, getNextPacketId(transport_data), unsubscribe, 1) != 0)
         {
             LogError("Failure calling mqtt_client_unsubscribe");
@@ -3087,5 +3412,5 @@
             if (item_type == IOTHUB_TYPE_DEVICE_TWIN && transport_data->twin_resp_sub_recv)
             {
-                MQTT_DEVICE_TWIN_ITEM* mqtt_info = create_device_twin_message(transport_data, REPORTED_STATE, iothub_item->device_twin->item_id);
+                MQTT_DEVICE_TWIN_ITEM* mqtt_info = createDeviceTwinMsg(transport_data, REPORTED_STATE, iothub_item->device_twin->item_id);
                 if (mqtt_info == NULL)
                 {
@@ -3099,5 +3424,5 @@
                     DList_InsertTailList(&transport_data->ack_waiting_queue, &mqtt_info->entry);
 
-                    if (publish_device_twin_message(transport_data, iothub_item->device_twin, mqtt_info) != 0)
+                    if (publishDeviceTwinMsg(transport_data, iothub_item->device_twin, mqtt_info) != 0)
                     {
                         DList_RemoveEntryList(&mqtt_info->entry);
@@ -3135,5 +3460,5 @@
     if (transport_data != NULL)
     {
-        if (InitializeConnection(transport_data) == 0)
+        if (UpdateMqttConnectionStateIfNeeded(transport_data) == 0)
         {
             if (transport_data->mqttClientStatus == MQTT_CLIENT_STATUS_PENDING_CLOSE)
@@ -3148,81 +3473,9 @@
             else if (transport_data->currPacketState == SUBACK_TYPE)
             {
-                if ((transport_data->topic_NotifyState != NULL || transport_data->topic_GetState != NULL) &&
-                    !transport_data->device_twin_get_sent)
-                {
-                    /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_055: [ IoTHubTransport_MQTT_Common_DoWork shall send a device twin get property message upon successfully retrieving a SUBACK on device twin topics. ] */
-                    MQTT_DEVICE_TWIN_ITEM* mqtt_info;
-
-                    if ((mqtt_info = create_device_twin_message(transport_data, RETRIEVE_PROPERTIES, 0)) == NULL)
-                    {
-                        LogError("Failure: could not create message for twin get command");
-                    }
-                    else if (publish_device_twin_get_message(transport_data, mqtt_info) == 0)
-                    {
-                        transport_data->device_twin_get_sent = true;
-                    }
-                    else
-                    {
-                        LogError("Failure: sending device twin get property command.");
-                        destroy_device_twin_get_message(mqtt_info);
-                    }
-                }
-                // Publish can be called now
-                transport_data->currPacketState = PUBLISH_TYPE;
+                ProcessSubackDoWork(transport_data);
             }
             else if (transport_data->currPacketState == PUBLISH_TYPE)
             {
-                PDLIST_ENTRY currentListEntry = transport_data->waitingToSend->Flink;
-                /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [IoTHubTransport_MQTT_Common_DoWork shall inspect the "waitingToSend" DLIST passed in config structure.] */
-                while (currentListEntry != transport_data->waitingToSend)
-                {
-                    IOTHUB_MESSAGE_LIST* iothubMsgList = containingRecord(currentListEntry, IOTHUB_MESSAGE_LIST, entry);
-                    DLIST_ENTRY savedFromCurrentListEntry;
-                    savedFromCurrentListEntry.Flink = currentListEntry->Flink;
-
-                    /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_027: [IoTHubTransport_MQTT_Common_DoWork shall inspect the "waitingToSend" DLIST passed in config structure.] */
-                    size_t messageLength;
-                    const unsigned char* messagePayload = NULL;
-                    if (!RetrieveMessagePayload(iothubMsgList->messageHandle, &messagePayload, &messageLength))
-                    {
-                        (void)(DList_RemoveEntryList(currentListEntry));
-                        sendMsgComplete(iothubMsgList, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
-                        LogError("Failure result from IoTHubMessage_GetData");
-                    }
-                    else
-                    {
-                        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_029: [IoTHubTransport_MQTT_Common_DoWork shall create a MQTT_MESSAGE_HANDLE and pass this to a call to mqtt_client_publish.] */
-                        MQTT_MESSAGE_DETAILS_LIST* mqttMsgEntry = (MQTT_MESSAGE_DETAILS_LIST*)malloc(sizeof(MQTT_MESSAGE_DETAILS_LIST));
-                        if (mqttMsgEntry == NULL)
-                        {
-                            LogError("Allocation Error: Failure allocating MQTT Message Detail List.");
-                        }
-                        else
-                        {
-                            mqttMsgEntry->retryCount = 0;
-                            mqttMsgEntry->iotHubMessageEntry = iothubMsgList;
-                            mqttMsgEntry->packet_id = get_next_packet_id(transport_data);
-                            if (publish_mqtt_telemetry_msg(transport_data, mqttMsgEntry, messagePayload, messageLength) != 0)
-                            {
-                                (void)(DList_RemoveEntryList(currentListEntry));
-                                sendMsgComplete(iothubMsgList, transport_data, IOTHUB_CLIENT_CONFIRMATION_ERROR);
-                                free(mqttMsgEntry);
-                            }
-                            else
-                            {
-                                // Remove the message from the waiting queue ...
-                                (void)(DList_RemoveEntryList(currentListEntry));
-                                // and add it to the ack queue
-                                DList_InsertTailList(&(transport_data->telemetry_waitingForAck), &(mqttMsgEntry->entry));
-                            }
-                        }
-                    }
-                    currentListEntry = savedFromCurrentListEntry.Flink;
-                }
-
-                if (transport_data->twin_resp_sub_recv)
-                {
-                    sendPendingGetTwinRequests(transport_data);
-                }
+                ProcessPublishStateDoWork(transport_data);
             }
             /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_030: [IoTHubTransport_MQTT_Common_DoWork shall call mqtt_client_dowork everytime it is called if it is connected.] */
@@ -3231,9 +3484,47 @@
 
         // Check the ack messages timeouts
-        process_queued_ack_messages(transport_data);
+        ProcessPendingTelemetryMessages(transport_data);
         removeExpiredTwinRequests(transport_data);
     }
 }
 
+int IoTHubTransport_MQTT_Common_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds)
+{
+    int result;
+
+    if (handle == NULL)
+    {
+        /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_25_041: [**If any handle is NULL then IoTHubTransport_MQTT_Common_SetRetryPolicy shall return resultant line.] */
+        LogError("Invalid handle parameter. NULL.");
+        result = MU_FAILURE;
+    }
+    else
+    {
+        RETRY_CONTROL_HANDLE new_retry_control_handle;
+
+        // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_006: [ IoTHubTransport_MQTT_Common_SetRetryPolicy shall set the retry logic by calling retry_control_create() with retry policy and retryTimeout as parameters]
+        // Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_09_009: [ If retry_control_create() fails then IoTHubTransport_MQTT_Common_SetRetryPolicy shall revert to previous retry policy and return non-zero value ]
+        if ((new_retry_control_handle = retry_control_create(retryPolicy, (unsigned int)retryTimeoutLimitInSeconds)) == NULL)
+        {
+            LogError("Failed creating new retry control handle");
+            result = MU_FAILURE;
+        }
+        else
+        {
+            PMQTTTRANSPORT_HANDLE_DATA transport_data = (PMQTTTRANSPORT_HANDLE_DATA)handle;
+            RETRY_CONTROL_HANDLE previous_retry_control_handle = transport_data->retry_control_handle;
+
+            transport_data->retry_control_handle = new_retry_control_handle;
+            retry_control_destroy(previous_retry_control_handle);
+
+            /*Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_25_045: [**If retry logic for specified parameters of retry policy and retryTimeoutLimitInSeconds is created successfully then IoTHubTransport_MQTT_Common_SetRetryPolicy shall return 0]*/
+            result = 0;
+        }
+    }
+
+    return result;
+}
+
+
 IOTHUB_CLIENT_RESULT IoTHubTransport_MQTT_Common_GetSendStatus(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus)
 {
@@ -3242,22 +3533,22 @@
     if (handle == NULL || iotHubClientStatus == NULL)
     {
-    /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_023: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter.] */
-    LogError("invalid arument.");
-    result = IOTHUB_CLIENT_INVALID_ARG;
-    }
-    else
-    {
-    MQTTTRANSPORT_HANDLE_DATA* handleData = (MQTTTRANSPORT_HANDLE_DATA*)handle;
-    if (!DList_IsListEmpty(handleData->waitingToSend) || !DList_IsListEmpty(&(handleData->telemetry_waitingForAck)))
-    {
-        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_025: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently event items to be sent or being sent.] */
-        *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY;
-    }
-    else
-    {
-        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_024: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there are currently no event items to be sent or being sent.] */
-        *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE;
-    }
-    result = IOTHUB_CLIENT_OK;
+        /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_023: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter.] */
+        LogError("invalid argument.");
+        result = IOTHUB_CLIENT_INVALID_ARG;
+    }
+    else
+    {
+        MQTTTRANSPORT_HANDLE_DATA* handleData = (MQTTTRANSPORT_HANDLE_DATA*)handle;
+        if (!DList_IsListEmpty(handleData->waitingToSend) || !DList_IsListEmpty(&(handleData->telemetry_waitingForAck)))
+        {
+            /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_025: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently event items to be sent or being sent.] */
+            *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY;
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_024: [IoTHubTransport_MQTT_Common_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there are currently no event items to be sent or being sent.] */
+            *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE;
+        }
+        result = IOTHUB_CLIENT_OK;
     }
     return result;
@@ -3423,5 +3714,5 @@
                 {
                     /* Codes_SRS_IOTHUB_TRANSPORT_MQTT_COMMON_01_009: [ When setting the proxy options succeeds any previously saved proxy options shall be freed. ]*/
-                    free_proxy_data(transport_data);
+                    freeProxyData(transport_data);
 
                     transport_data->http_proxy_hostname = copied_proxy_hostname;
@@ -3443,5 +3734,5 @@
 
             /* Codes_SRS_IOTHUB_MQTT_TRANSPORT_07_032: [IoTHubTransport_MQTT_Common_SetOption shall pass down the option to xio_setoption if the option parameter is not a known option string for the MQTT transport.] */
-            if (GetTransportProviderIfNecessary(transport_data) == 0)
+            if (CreateTransportProviderIfNecessary(transport_data) == 0)
             {
                 if (xio_setoption(transport_data->xioTransport, option, value) == 0)
@@ -3462,24 +3753,4 @@
     }
     return result;
-}
-
-static bool check_module_ids_equal(const char* transportModuleId, const char* deviceModuleId)
-{
-    if ((transportModuleId != NULL) && (deviceModuleId == NULL))
-    {
-        return false;
-    }
-    else if ((transportModuleId == NULL) && (deviceModuleId != NULL))
-    {
-        return false;
-    }
-    else if ((transportModuleId == NULL) && (deviceModuleId == NULL))
-    {
-        return true;
-    }
-    else
-    {
-        return (0 == strcmp(transportModuleId, deviceModuleId));
-    }
 }
 
@@ -3519,5 +3790,5 @@
                 result = NULL;
             }
-            else if (!check_module_ids_equal(STRING_c_str(transport_data->module_id), device->moduleId))
+            else if (!checkModuleIdsEqual(STRING_c_str(transport_data->module_id), device->moduleId))
             {
                 LogError("IoTHubTransport_MQTT_Common_Register: moduleId does not match.");
@@ -3650,5 +3921,5 @@
         const char* unsubscribe[1];
         unsubscribe[0] = STRING_c_str(transport_data->topic_InputQueue);
-        if (mqtt_client_unsubscribe(transport_data->mqttClient, get_next_packet_id(transport_data), unsubscribe, 1) != 0)
+        if (mqtt_client_unsubscribe(transport_data->mqttClient, getNextPacketId(transport_data), unsubscribe, 1) != 0)
         {
             LogError("Failure calling mqtt_client_unsubscribe");
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_client_const.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_client_const.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_client_const.h	(revision 464)
@@ -5,5 +5,5 @@
 #define PROV_CLIENT_CONST_H
 
-#define PROV_DEVICE_CLIENT_VERSION "1.3.8"
+#define PROV_DEVICE_CLIENT_VERSION "1.6.0"
 #define PROV_GET_THROTTLE_TIME      1
 #define MAX_PROV_GET_THROTTLE_TIME  5*60
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_device_client.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_device_client.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_device_client.h	(revision 464)
@@ -26,4 +26,5 @@
 #include "umock_c/umock_c_prod.h"
 #include "azure_macro_utils/macro_utils.h"
+#include "azure_c_shared_utility/const_defines.h"
 #include "azure_prov_client/prov_transport.h"
 
@@ -32,4 +33,6 @@
 {
 #endif
+
+static STATIC_VAR_UNUSED const char* const PROV_OPTION_DO_WORK_FREQUENCY_IN_MS = "do_work_freq_ms";
 
 MOCKABLE_FUNCTION(, PROV_DEVICE_HANDLE, Prov_Device_Create, const char*, uri, const char*, scope_id, PROV_DEVICE_TRANSPORT_PROVIDER_FUNCTION, protocol);
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_device_ll_client.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_device_ll_client.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/inc/azure_prov_client/prov_device_ll_client.h	(revision 464)
@@ -7,4 +7,5 @@
 #include "umock_c/umock_c_prod.h"
 #include "azure_macro_utils/macro_utils.h"
+#include "azure_c_shared_utility/const_defines.h"
 #include "azure_prov_client/prov_transport.h"
 
@@ -43,7 +44,7 @@
 MU_DEFINE_ENUM_WITHOUT_INVALID(PROV_DEVICE_REG_STATUS, PROV_DEVICE_REG_STATUS_VALUES);
 
-static const char* const PROV_REGISTRATION_ID = "registration_id";
-static const char* const PROV_OPTION_LOG_TRACE = "logtrace";
-static const char* const PROV_OPTION_TIMEOUT = "provisioning_timeout";
+static STATIC_VAR_UNUSED const char* const PROV_REGISTRATION_ID = "registration_id";
+static STATIC_VAR_UNUSED const char* const PROV_OPTION_LOG_TRACE = "logtrace";
+static STATIC_VAR_UNUSED const char* const PROV_OPTION_TIMEOUT = "provisioning_timeout";
 
 typedef void(*PROV_DEVICE_CLIENT_REGISTER_DEVICE_CALLBACK)(PROV_DEVICE_RESULT register_result, const char* iothub_uri, const char* device_id, void* user_context);
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_device_client.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_device_client.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_device_client.c	(revision 464)
@@ -15,6 +15,4 @@
 
 #define DO_WORK_FREQ_DEFAULT 1
-
-static const char* const OPTION_DO_WORK_FREQUENCY_IN_MS = "do_work_freq_ms";
 
 typedef struct PROV_DEVICE_INSTANCE_TAG
@@ -260,5 +258,5 @@
         {
             /* Codes_SRS_PROV_DEVICE_CLIENT_41_001: [ If parameter `optionName` is `OPTION_DO_WORK_FREQUENCY_IN_MS` then `IoTHubClientCore_SetOption` shall set `do_work_freq_ms` parameter of `prov_device_instance` ] */
-            if (strcmp(OPTION_DO_WORK_FREQUENCY_IN_MS, optionName) == 0)
+            if (strcmp(PROV_OPTION_DO_WORK_FREQUENCY_IN_MS, optionName) == 0)
             {
                 prov_device_instance->do_work_freq_ms = *((uint16_t *)value);
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_device_ll_client.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_device_ll_client.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_device_ll_client.c	(revision 464)
@@ -93,7 +93,7 @@
     TICK_COUNTER_HANDLE tick_counter;
 
-    tickcounter_ms_t status_throttle;
+    tickcounter_ms_t last_send_time_ms;
     tickcounter_ms_t timeout_value;
-    uint32_t retry_after_value;
+    size_t retry_after_ms;
 
     uint8_t prov_timeout;
@@ -784,5 +784,5 @@
         PROV_INSTANCE_INFO* prov_info = (PROV_INSTANCE_INFO*)user_ctx;
 
-        prov_info->retry_after_value = retry_interval;
+        prov_info->retry_after_ms = (size_t)retry_interval * 1000; // retry_interval is in seconds.
 
         switch (transport_status)
@@ -880,5 +880,5 @@
             /* Codes_SRS_PROV_CLIENT_07_028: [ CLIENT_STATE_READY is the initial state after the object is created which will send a uhttp_client_open call to the http endpoint. ] */
             result->prov_state = CLIENT_STATE_READY;
-            result->retry_after_value = PROV_GET_THROTTLE_TIME;
+            result->retry_after_ms = PROV_GET_THROTTLE_TIME * 1000;
             result->prov_transport_protocol = protocol();
             result->error_reason = PROV_DEVICE_RESULT_OK;
@@ -931,6 +931,6 @@
                 {
                     // Ensure that we are passed the throttling time and send on the first send
-                    (void)tickcounter_get_current_ms(result->tick_counter, &result->status_throttle);
-                    result->status_throttle += (result->retry_after_value * 1000);
+                    (void)tickcounter_get_current_ms(result->tick_counter, &result->last_send_time_ms);
+                    result->last_send_time_ms += result->retry_after_ms;
                 }
             }
@@ -1119,27 +1119,9 @@
         if (prov_info->is_connected || prov_info->prov_state == CLIENT_STATE_ERROR)
         {
+            tickcounter_ms_t current_time = 0;
+
             switch (prov_info->prov_state)
             {
                 case CLIENT_STATE_REGISTER_SEND:
-                    /* Codes_SRS_PROV_CLIENT_07_013: [ CLIENT_STATE_REGISTER_SEND which shall construct an initial call to the service with endorsement information ] */
-                    if (prov_info->prov_transport_protocol->prov_transport_register(prov_info->transport_handle, prov_transport_process_json_reply, prov_transport_create_json_payload, prov_info) != 0)
-                    {
-                        LogError("Failure registering device");
-                        if (prov_info->error_reason == PROV_DEVICE_RESULT_OK)
-                        {
-                            prov_info->error_reason = PROV_DEVICE_RESULT_TRANSPORT;
-                        }
-                        prov_info->prov_state = CLIENT_STATE_ERROR;
-                    }
-                    else
-                    {
-                        (void)tickcounter_get_current_ms(prov_info->tick_counter, &prov_info->timeout_value);
-                        prov_info->prov_state = CLIENT_STATE_REGISTER_SENT;
-                    }
-                    break;
-
-                case CLIENT_STATE_STATUS_SEND:
-                {
-                    tickcounter_ms_t current_time = 0;
                     if (tickcounter_get_current_ms(prov_info->tick_counter, &current_time) != 0)
                     {
@@ -1150,5 +1132,37 @@
                     else
                     {
-                        if ((current_time - prov_info->status_throttle) / 1000 > prov_info->retry_after_value)
+                        if ((current_time - prov_info->last_send_time_ms) > prov_info->retry_after_ms)
+                        {
+                            /* Codes_SRS_PROV_CLIENT_07_013: [ CLIENT_STATE_REGISTER_SEND which shall construct an initial call to the service with endorsement information ] */
+                            if (prov_info->prov_transport_protocol->prov_transport_register(prov_info->transport_handle, prov_transport_process_json_reply, prov_transport_create_json_payload, prov_info) != 0)
+                            {
+                                LogError("Failure registering device");
+                                if (prov_info->error_reason == PROV_DEVICE_RESULT_OK)
+                                {
+                                    prov_info->error_reason = PROV_DEVICE_RESULT_TRANSPORT;
+                                }
+                                prov_info->prov_state = CLIENT_STATE_ERROR;
+                            }
+                            else
+                            {
+                                (void)tickcounter_get_current_ms(prov_info->tick_counter, &prov_info->timeout_value);
+                                prov_info->prov_state = CLIENT_STATE_REGISTER_SENT;
+                            }
+                            prov_info->last_send_time_ms = current_time;
+                        }
+                    }
+                    break;
+
+                case CLIENT_STATE_STATUS_SEND:
+                {
+                    if (tickcounter_get_current_ms(prov_info->tick_counter, &current_time) != 0)
+                    {
+                        LogError("Failure getting the current time");
+                        prov_info->error_reason = PROV_DEVICE_RESULT_ERROR;
+                        prov_info->prov_state = CLIENT_STATE_ERROR;
+                    }
+                    else
+                    {
+                        if ((current_time - prov_info->last_send_time_ms) > prov_info->retry_after_ms)
                         {
                             /* Codes_SRS_PROV_CLIENT_07_026: [ Upon receiving the reply of the CLIENT_STATE_URL_REQ_SEND message from  iothub_client shall process the the reply of the CLIENT_STATE_URL_REQ_SEND state ] */
@@ -1172,5 +1186,5 @@
                                 }
                             }
-                            prov_info->status_throttle = current_time;
+                            prov_info->last_send_time_ms = current_time;
                         }
                     }
@@ -1183,5 +1197,4 @@
                     if (prov_info->prov_timeout > 0)
                     {
-                        tickcounter_ms_t current_time = 0;
                         (void)tickcounter_get_current_ms(prov_info->tick_counter, &current_time);
                         if ((current_time - prov_info->timeout_value) / 1000 > prov_info->prov_timeout)
@@ -1279,5 +1292,5 @@
             if (value == NULL)
             {
-                LogError("setting proxy options");
+                LogError("setting PROV_OPTION_TIMEOUT option");
                 result = PROV_DEVICE_RESULT_ERROR;
             }
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_transport_mqtt_common.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_transport_mqtt_common.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/provisioning_client/src/prov_transport_mqtt_common.c	(revision 464)
@@ -320,5 +320,5 @@
             if (memcmp(MQTT_TOPIC_STATUS_PREFIX, topic_resp, status_pos) == 0)
             {
-                // If the status code is > 429 then this is a transient error
+                // If the status code is >= 429 then this is a transient error
                 long status_code = atol(topic_resp + status_pos);
                 if (status_code >= PROV_STATUS_CODE_TRANSIENT_ERROR)
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/inc/azure_umqtt_c/mqtt_client.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/inc/azure_umqtt_c/mqtt_client.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/inc/azure_umqtt_c/mqtt_client.h	(revision 464)
@@ -49,4 +49,5 @@
 typedef void(*ON_MQTT_DISCONNECTED_CALLBACK)(void* callbackCtx);
 
+MOCKABLE_FUNCTION(, void, mqtt_client_clear_xio, MQTT_CLIENT_HANDLE, handle);
 MOCKABLE_FUNCTION(, MQTT_CLIENT_HANDLE, mqtt_client_init, ON_MQTT_MESSAGE_RECV_CALLBACK, msgRecv, ON_MQTT_OPERATION_CALLBACK, opCallback, void*, opCallbackCtx, ON_MQTT_ERROR_CALLBACK, onErrorCallBack, void*, errorCBCtx);
 MOCKABLE_FUNCTION(, void, mqtt_client_deinit, MQTT_CLIENT_HANDLE, handle);
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/inc/azure_umqtt_c/mqttconst.h
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/inc/azure_umqtt_c/mqttconst.h	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/inc/azure_umqtt_c/mqttconst.h	(revision 464)
@@ -33,5 +33,7 @@
     DISCONNECT_TYPE, 0xE0, \
     PACKET_TYPE_ERROR, 0xE1, /* 0xE1 was assigned because ENUM_2 needs it */ \
-    UNKNOWN_TYPE, 0xE2 /* 0xE2 was assigned because ENUM_2 needs it */
+    UNKNOWN_TYPE, 0xE2,      /* 0xE2 was assigned because ENUM_2 needs it */ \
+    PACKET_INVALID1_TYPE, 0x00, \
+    PACKET_INVALID2_TYPE, 0xF0
 
 MU_DEFINE_ENUM_2(CONTROL_PACKET_TYPE, CONTROL_PACKET_TYPE_VALUES)
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/src/mqtt_client.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/src/mqtt_client.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/src/mqtt_client.c	(revision 464)
@@ -770,5 +770,20 @@
                     /*Codes_SRS_MQTT_CLIENT_07_028: [If the actionResult parameter is of type CONNECT_ACK then the msgInfo value shall be a CONNECT_ACK structure.]*/
                     CONNECT_ACK connack = { 0 };
-                    connack.isSessionPresent = (byteutil_readByte(&iterator) == 0x1) ? true : false;
+                    if (packetLength != 2) // CONNACK payload must be only 2 bytes
+                    {
+                        LogError("Invalid CONNACK packet.");
+                        set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR);
+                        break;
+                    }
+
+                    uint8_t connect_acknowledge_flags = byteutil_readByte(&iterator);
+                    if (connect_acknowledge_flags & 0xFE) // bits 7-1 must be zero
+                    {
+                        LogError("Invalid CONNACK packet.");
+                        set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR);
+                        break;
+                    }
+
+                    connack.isSessionPresent = (connect_acknowledge_flags == 0x1) ? true : false;
                     uint8_t rc = byteutil_readByte(&iterator);
                     connack.returnCode =
@@ -802,4 +817,11 @@
                 case PUBCOMP_TYPE:
                 {
+                    if (packetLength != 2) // PUBXXX payload must be only 2 bytes
+                    {
+                        LogError("Invalid packet length.");
+                        set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR);
+                        break;
+                    }
+
                     /*Codes_SRS_MQTT_CLIENT_07_029: [If the actionResult parameter are of types PUBACK_TYPE, PUBREC_TYPE, PUBREL_TYPE or PUBCOMP_TYPE then the msgInfo value shall be a PUBLISH_ACK structure.]*/
                     MQTT_CLIENT_EVENT_RESULT action = (packet == PUBACK_TYPE) ? MQTT_CLIENT_ON_PUBLISH_ACK :
@@ -876,4 +898,10 @@
                         {
                             uint8_t qosRet = byteutil_readByte(&iterator);
+                            if (qosRet & 0x7C) // SUBACK QOS bits 6-2 must be zero
+                            {
+                                LogError("Invalid SUBACK_TYPE packet.");
+                                set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR);
+                                break;
+                            }
                             suback.qosReturn[suback.qosCount++] =
                                 (qosRet <= ((uint8_t)DELIVER_EXACTLY_ONCE)) ?
@@ -909,4 +937,11 @@
                     /*Codes_SRS_MQTT_CLIENT_07_031: [If the actionResult parameter is of type UNSUBACK_TYPE then the msgInfo value shall be a UNSUBSCRIBE_ACK structure.]*/
                     UNSUBSCRIBE_ACK unsuback = { 0 };
+                    if (packetLength != 2) // UNSUBACK_TYPE payload must be only 2 bytes
+                    {
+                        LogError("Invalid UNSUBACK packet length.");
+                        set_error_callback(mqtt_client, MQTT_CLIENT_COMMUNICATION_ERROR);
+                        break;
+                    }
+
                     unsuback.packetId = byteutil_read_uint16(&iterator, packetLength);
 
@@ -943,4 +978,20 @@
     {
         LogError("recvCompleteCallback context failed.");
+    }
+}
+
+void mqtt_client_clear_xio(MQTT_CLIENT_HANDLE handle)
+{
+    if (handle != NULL)
+    {
+        MQTT_CLIENT *mqtt_client = (MQTT_CLIENT *)handle;
+
+        // The upstream transport handle allocates and deallocates the xio handle.
+        // The reference to that xio handle is shared between this layer (mqtt layer)
+        // and the upstream layer. The clearing done here is to signal to this layer
+        // that the handle is no longer available to be used. This is different from
+        // deiniting the mqtt client in that we do not want an entire teardown, but
+        // only a possible re-upping of the xio in the future.
+        mqtt_client->xioHandle = NULL;
     }
 }
Index: azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/src/mqtt_codec.c
===================================================================
--- azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/src/mqtt_codec.c	(revision 457)
+++ azure_iot_hub_f767zi/trunk/azure_iot_sdk/umqtt/src/mqtt_codec.c	(revision 464)
@@ -568,4 +568,14 @@
             }
         }
+    }
+    else if (codecData->remainLenIndex == (sizeof(codecData->storeRemainLen) / sizeof(codecData->storeRemainLen[0])))
+    {
+        // The maximum number of bytes in the Remaining Length field is four
+        // This allows applications to send Control Packets of size up to 268,435,455 (256 MB). 
+        // The representation of this number on the wire is: 0xFF, 0xFF, 0xFF, 0x7F.
+
+        // The last byte has exceed the max value of 0x7F
+        LogError("MQTT packet len is invalid");
+        result = MU_FAILURE;
     }
     return result;
@@ -1067,4 +1077,47 @@
                 {
                     codec_Data->currPacket = processControlPacketType(iterator, &codec_Data->headerFlags);
+
+                    // validate packet type and invalid reserved header flags
+                    switch (codec_Data->currPacket)
+                    {
+                        case PACKET_INVALID1_TYPE:
+                        case PACKET_INVALID2_TYPE:
+                            codec_Data->currPacket = PACKET_TYPE_ERROR;
+                            result = MU_FAILURE;
+                            break;
+
+                        case CONNECT_TYPE:
+                        case CONNACK_TYPE:
+                        case PUBACK_TYPE:
+                        case PUBREC_TYPE:
+                        case PUBCOMP_TYPE:
+                        case SUBACK_TYPE:
+                        case UNSUBACK_TYPE:
+                        case PINGREQ_TYPE:
+                        case PINGRESP_TYPE:
+                        case DISCONNECT_TYPE:
+                            if (codec_Data->headerFlags & 0x0F) // flags must be all zeros
+                            {
+                                codec_Data->currPacket = PACKET_TYPE_ERROR;
+                                result = MU_FAILURE;
+                            }
+                            break;
+
+                        case PUBREL_TYPE:
+                        case SUBSCRIBE_TYPE:
+                        case UNSUBSCRIBE_TYPE:
+                            if ((codec_Data->headerFlags & 0x0F) != 0x02) // only bit 1 must be set
+                            {
+                                codec_Data->currPacket = PACKET_TYPE_ERROR;
+                                result = MU_FAILURE;
+                            }
+                            break;
+
+                        case PUBLISH_TYPE:
+                        case CONTROL_PACKET_TYPE_INVALID: 
+                        case PACKET_TYPE_ERROR:
+                        case UNKNOWN_TYPE:
+                            break;
+                    }
                 }
                 else
@@ -1078,6 +1131,15 @@
                     else if (codec_Data->currPacket == PINGRESP_TYPE)
                     {
-                        /* Codes_SRS_MQTT_CODEC_07_034: [Upon a constructing a complete MQTT packet mqtt_codec_bytesReceived shall call the ON_PACKET_COMPLETE_CALLBACK function.] */
-                        completePacketData(codec_Data);
+                        // PINGRESP must not have a payload
+                        if (((int8_t*)buffer)[index] == 0)
+                        {
+                            /* Codes_SRS_MQTT_CODEC_07_034: [Upon a constructing a complete MQTT packet mqtt_codec_bytesReceived shall call the ON_PACKET_COMPLETE_CALLBACK function.] */
+                            completePacketData(codec_Data);
+                        }
+                        else
+                        {
+                            codec_Data->currPacket = PACKET_TYPE_ERROR;
+                            result = MU_FAILURE;
+                        }
                     }
                 }
