Microsoft Azure IoTHub client libraries

Dependents:   sht15_remote_monitoring RobotArmDemo iothub_client_sample_amqp f767zi_mqtt ... more

This library implements the Microsoft Azure IoTHub client library. The code is replicated from https://github.com/Azure/azure-iot-sdks

Revision:
80:db5f5237bc95
Parent:
79:bb88037c05e6
Child:
82:f94e6bed4495
diff -r bb88037c05e6 -r db5f5237bc95 blob.c
--- a/blob.c	Fri Nov 17 13:57:39 2017 -0800
+++ b/blob.c	Fri Dec 15 14:09:20 2017 -0800
@@ -11,13 +11,123 @@
 #include "azure_c_shared_utility/base64.h"
 #include "azure_c_shared_utility/shared_util_options.h"
 
-/*a block has 4MB*/
-#define BLOCK_SIZE (4*1024*1024)
-
-BLOB_RESULT Blob_UploadFromSasUri(const char* SASURI, const unsigned char* source, size_t size, unsigned int* httpStatus, BUFFER_HANDLE httpResponse, const char* certificates, HTTP_PROXY_OPTIONS *proxyOptions)
+BLOB_RESULT Blob_UploadBlock(
+        HTTPAPIEX_HANDLE httpApiExHandle,
+        const char* relativePath,
+        BUFFER_HANDLE requestContent,
+        unsigned int blockID,
+        STRING_HANDLE blockIDList,
+        unsigned int* httpStatus,
+        BUFFER_HANDLE httpResponse)
 {
     BLOB_RESULT result;
-    /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+
+    if (requestContent == NULL ||
+        blockIDList == NULL ||
+        relativePath == NULL ||
+        httpApiExHandle == NULL ||
+        httpStatus == NULL ||
+        httpResponse == NULL)
+    {
+        LogError("invalid argument detected requestContent=%p blockIDList=%p relativePath=%p httpApiExHandle=%p httpStatus=%p httpResponse=%p", requestContent, blockIDList, relativePath, httpApiExHandle, httpStatus, httpResponse);
+        result = BLOB_ERROR;
+    }
+    else
+    {
+        char temp[7]; /*this will contain 000000... 049999*/
+        if (sprintf(temp, "%6u", (unsigned int)blockID) != 6) /*produces 000000... 049999*/
+        {
+            /*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 sprintf");
+            result = BLOB_ERROR;
+        }
+        else
+        {
+            STRING_HANDLE blockIdString = Base64_Encode_Bytes((const unsigned char*)temp, 6);
+            if (blockIdString == 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 Base64_Encode_Bytes");
+                result = BLOB_ERROR;
+            }
+            else
+            {
+                /*add the blockId base64 encoded to the XML*/
+                if (!(
+                    (STRING_concat(blockIDList, "<Latest>") == 0) &&
+                    (STRING_concat_with_STRING(blockIDList, blockIdString) == 0) &&
+                    (STRING_concat(blockIDList, "</Latest>") == 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("unable to STRING_concat");
+                    result = BLOB_ERROR;
+                }
+                else
+                {
+                    /*Codes_SRS_BLOB_02_022: [ Blob_UploadMultipleBlocksFromSasUri shall construct a new relativePath from following string: base relativePath + "&comp=block&blockid=BASE64 encoded string of blockId" ]*/
+                    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("unable to STRING_construct");
+                        result = BLOB_ERROR;
+                    }
+                    else
+                    {
+                        if (!(
+                            (STRING_concat(newRelativePath, "&comp=block&blockid=") == 0) &&
+                            (STRING_concat_with_STRING(newRelativePath, blockIdString) == 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("unable to STRING concatenate");
+                            result = BLOB_ERROR;
+                        }
+                        else
+                        {
+                            /*Codes_SRS_BLOB_02_024: [ Blob_UploadMultipleBlocksFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing httpStatus and httpResponse. ]*/
+                            if (HTTPAPIEX_ExecuteRequest(
+                                httpApiExHandle,
+                                HTTPAPI_REQUEST_PUT,
+                                STRING_c_str(newRelativePath),
+                                NULL,
+                                requestContent,
+                                httpStatus,
+                                NULL,
+                                httpResponse) != HTTPAPIEX_OK
+                                )
+                            {
+                                /*Codes_SRS_BLOB_02_025: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
+                                LogError("unable to HTTPAPIEX_ExecuteRequest");
+                                result = BLOB_HTTP_ERROR;
+                            }
+                            else if (*httpStatus >= 300)
+                            {
+                                /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
+                                LogError("HTTP status from storage does not indicate success (%d)", (int)*httpStatus);
+                                result = BLOB_OK;
+                            }
+                            else
+                            {
+                                /*Codes_SRS_BLOB_02_027: [ Otherwise Blob_UploadMultipleBlocksFromSasUri shall continue execution. ]*/
+                                result = BLOB_OK;
+                            }
+                        }
+                        STRING_delete(newRelativePath);
+                    }
+                }
+                STRING_delete(blockIdString);
+            }
+        }
+    }
+    return result;
+}
+
+BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse, const char* certificates, HTTP_PROXY_OPTIONS *proxyOptions)
+{
+    BLOB_RESULT result;
+    /*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");
@@ -25,35 +135,23 @@
     }
     else
     {
-        /*Codes_SRS_BLOB_02_002: [ If source is NULL and size is not zero then Blob_UploadFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
-        if (
-            (size > 0) &&
-            (source == NULL)
-            )
+        /*Codes_SRS_BLOB_02_002: [ If getDataCallback is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+        if (getDataCallback == NULL)
         {
-            LogError("combination of source = %p and size = %zu is invalid", source, size);
+            LogError("IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK getDataCallback = %p is invalid", getDataCallback);
             result = BLOB_INVALID_ARG;
         }
         /*the below define avoid a "condition always false" on some compilers*/
-#if SIZE_MAX>UINT32_MAX
-        /*Codes_SRS_BLOB_02_034: [ If size is bigger than 50000*4*1024*1024 then Blob_UploadFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
-        else if (size > 4 * 1024 * 1024 * 50000ULL) /*https://msdn.microsoft.com/en-us/library/azure/dd179467.aspx says "Each block can be a different size, up to a maximum of 4 MB, and a block blob can include a maximum of 50,000 blocks."*/
-        {
-            LogError("size too big (%zu)", size);
-            result = BLOB_INVALID_ARG;
-        }
-#endif
         else
         {
-            /*Codes_SRS_BLOB_02_017: [ Blob_UploadFromSasUri shall copy from SASURI the hostname to a new const char* ]*/
-            /*Codes_SRS_BLOB_02_004: [ Blob_UploadFromSasUri shall copy from SASURI the hostname to a new const char*. ]*/
+            /*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_UploadFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+                /*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;
             }
@@ -63,7 +161,7 @@
                 const char* hostnameEnd = strchr(hostnameBegin, '/');
                 if (hostnameEnd == NULL)
                 {
-                    /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
+                    /*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;
                 }
@@ -73,7 +171,7 @@
                     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_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
+                        /*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;
                     }
@@ -83,12 +181,11 @@
                         (void)memcpy(hostname, hostnameBegin, hostnameSize);
                         hostname[hostnameSize] = '\0';
 
-                        /*Codes_SRS_BLOB_02_006: [ Blob_UploadFromSasUri shall create a new HTTPAPI_EX_HANDLE by calling HTTPAPIEX_Create passing the hostname. ]*/
-                        /*Codes_SRS_BLOB_02_018: [ Blob_UploadFromSasUri shall create a new HTTPAPI_EX_HANDLE by calling HTTPAPIEX_Create passing the hostname. ]*/
+                        /*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_UploadFromSasUri shall fail and return BLOB_ERROR. ]*/
+                            /*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;
                         }
@@ -99,275 +196,185 @@
                                 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
                             {
-                                if ((proxyOptions != NULL && proxyOptions->host_address != NULL) && HTTPAPIEX_SetOption(httpApiExHandle, OPTION_HTTP_PROXY, proxyOptions) == HTTPAPIEX_ERROR)
+                                /*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)
                                 {
-                                    LogError("failure in setting proxy options");
-                                    result = BLOB_ERROR;
+                                    /*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_008: [ Blob_UploadFromSasUri shall compute the relative path of the request from the SASURI parameter. ]*/
-                                    /*Codes_SRS_BLOB_02_019: [ Blob_UploadFromSasUri 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_021: [ For every block returned by `getDataCallback` the following operations shall happen: ]*/
+                                    unsigned int blockID = 0; /* incremented for each new block */
+                                    unsigned int isError = 0; /* set to 1 if a block upload fails or if getDataCallback returns incorrect blocks to upload */
+                                    unsigned int uploadOneMoreBlock; /* set to 1 while getDataCallback returns correct blocks to upload */
+                                    unsigned char const * source; /* data set by getDataCallback */
+                                    size_t size; /* source size set by getDataCallback */
+
+                                    /* get first block */
+                                    getDataCallback(FILE_UPLOAD_OK, &source, &size, context);
+
+                                    /*Codes_SRS_BLOB_99_002: [ If the size of the block returned by `getDataCallback` is 0 or if the data is NULL, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop. ]*/
+                                    uploadOneMoreBlock = (source != NULL && size > 0) ? 1 : 0;
 
-                                    if (size < 64 * 1024 * 1024) /*code path for sizes <64MB*/
+                                    if (size > BLOCK_SIZE)
+                                    {
+                                        /*Codes_SRS_BLOB_99_001: [ If the size of the block returned by `getDataCallback` is bigger than 4MB, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/
+                                        LogError("tried to upload block of size %zu, max allowed size is %d", size, BLOCK_SIZE);
+                                        result = BLOB_INVALID_ARG;
+                                        isError = 1;
+                                    }
+                                    else
+                                    {
+                                        result = BLOB_OK;
+                                    }
+
+                                    while(uploadOneMoreBlock && !isError)
                                     {
-                                        /*Codes_SRS_BLOB_02_010: [ Blob_UploadFromSasUri shall create a BUFFER_HANDLE from source and size parameters. ]*/
-                                        BUFFER_HANDLE requestBuffer = BUFFER_create(source, size);
-                                        if (requestBuffer == NULL)
+                                        /*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)
+                                        {
+                                            isError = 1;
+                                        }
+                                        else
                                         {
-                                            /*Codes_SRS_BLOB_02_011: [ If any of the previous steps related to building the HTTPAPI_EX_ExecuteRequest parameters fails, then Blob_UploadFromSasUri shall fail and return BLOB_ERROR. ]*/
-                                            LogError("unable to BUFFER_create");
+                                            // try to get next block
+                                            getDataCallback(FILE_UPLOAD_OK, &source, &size, context);
+                                            if (source == NULL || size == 0)
+                                            {
+                                                /*Codes_SRS_BLOB_99_002: [ If the size of the block returned by `getDataCallback` is 0 or if the data is NULL, then `Blob_UploadMultipleBlocksFromSasUri` shall exit the loop. ]*/
+                                                uploadOneMoreBlock = 0;
+                                            }
+                                            else
+                                            {
+                                                blockID++;
+                                                if (size > BLOCK_SIZE)
+                                                {
+                                                    /*Codes_SRS_BLOB_99_001: [ If the size of the block returned by `getDataCallback` is bigger than 4MB, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/
+                                                    LogError("tried to upload block of size %zu, max allowed size is %d", size, BLOCK_SIZE);
+                                                    result = BLOB_INVALID_ARG;
+                                                    isError = 1;
+                                                }
+                                                else if (blockID >= MAX_BLOCK_COUNT)
+                                                {
+                                                    /*Codes_SRS_BLOB_99_003: [ If `getDataCallback` returns more than 50000 blocks, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/
+                                                    LogError("unable to upload more than %zu blocks in one blob", MAX_BLOCK_COUNT);
+                                                    result = BLOB_INVALID_ARG;
+                                                    isError = 1;
+                                                }
+                                            }
+                                        }
+                                    }
+
+                                    if (isError)
+                                    {
+                                        /*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_009: [ Blob_UploadFromSasUri shall create an HTTP_HEADERS_HANDLE for the request HTTP headers carrying the following headers: ]*/
-                                            HTTP_HEADERS_HANDLE requestHttpHeaders = HTTPHeaders_Alloc();
-                                            if (requestHttpHeaders == NULL)
+                                            /*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_011: [ If any of the previous steps related to building the HTTPAPI_EX_ExecuteRequest parameters fails, then Blob_UploadFromSasUri shall fail and return BLOB_ERROR. ]*/
-                                                LogError("unable to HTTPHeaders_Alloc");
+                                                /*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 (HTTPHeaders_AddHeaderNameValuePair(requestHttpHeaders, "x-ms-blob-type", "BlockBlob") != HTTP_HEADERS_OK)
-                                                {
-                                                    /*Codes_SRS_BLOB_02_011: [ If any of the previous steps related to building the HTTPAPI_EX_ExecuteRequest parameters fails, then Blob_UploadFromSasUri shall fail and return BLOB_ERROR. ]*/
-                                                    LogError("unable to HTTPHeaders_AddHeaderNameValuePair");
-                                                    result = BLOB_ERROR;
-                                                }
-                                                else
-                                                {
-                                                    /*Codes_SRS_BLOB_02_012: [ Blob_UploadFromSasUri shall call HTTPAPIEX_ExecuteRequest passing the parameters previously build, httpStatus and httpResponse ]*/
-                                                    if (HTTPAPIEX_ExecuteRequest(httpApiExHandle, HTTPAPI_REQUEST_PUT, relativePath, requestHttpHeaders, requestBuffer, httpStatus, NULL, httpResponse) != HTTPAPIEX_OK)
-                                                    {
-                                                        /*Codes_SRS_BLOB_02_013: [ If HTTPAPIEX_ExecuteRequest fails, then Blob_UploadFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
-                                                        LogError("failed to HTTPAPIEX_ExecuteRequest");
-                                                        result = BLOB_HTTP_ERROR;
-                                                    }
-                                                    else
-                                                    {
-                                                        /*Codes_SRS_BLOB_02_015: [ Otherwise, HTTPAPIEX_ExecuteRequest shall succeed and return BLOB_OK. ]*/
-                                                        result = BLOB_OK;
-                                                    }
-                                                }
-                                                HTTPHeaders_Free(requestHttpHeaders);
-                                            }
-                                            BUFFER_delete(requestBuffer);
-                                        }
-                                    }
-                                    else /*code path for size >= 64MB*/
-                                    {
-                                        size_t toUpload = size;
-                                        /*Codes_SRS_BLOB_02_028: [ Blob_UploadFromSasUri shall construct an XML string with the following content: ]*/
-                                        STRING_HANDLE xml = STRING_construct("<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<BlockList>"); /*the XML "build as we go"*/
-                                        if (xml == NULL)
-                                        {
-                                            /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                            LogError("failed to STRING_construct");
-                                            result = BLOB_HTTP_ERROR;
-                                        }
-                                        else
-                                        {
-                                            /*Codes_SRS_BLOB_02_021: [ For every block of 4MB the following operations shall happen: ]*/
-                                            unsigned int blockID = 0;
-                                            result = BLOB_ERROR;
-
-                                            int isError = 0; /*used to cleanly exit the loop*/
-                                            do
-                                            {
-                                                /*setting this block size*/
-                                                size_t thisBlockSize = (toUpload > BLOCK_SIZE) ? BLOCK_SIZE : toUpload;
-                                                /*Codes_SRS_BLOB_02_020: [ Blob_UploadFromSasUri shall construct a BASE64 encoded string from the block ID (000000... 0499999) ]*/
-                                                char temp[7]; /*this will contain 000000... 049999*/
-                                                if (sprintf(temp, "%6u", (unsigned int)blockID) != 6) /*produces 000000... 049999*/
-                                                {
-                                                    /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                    LogError("failed to sprintf");
-                                                    result = BLOB_ERROR;
-                                                    isError = 1;
-                                                }
-                                                else
+                                                if (STRING_concat(newRelativePath, "&comp=blocklist") != 0)
                                                 {
-                                                    STRING_HANDLE blockIdString = Base64_Encode_Bytes((const unsigned char*)temp, 6);
-                                                    if (blockIdString == NULL)
-                                                    {
-                                                        /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                        LogError("unable to Base64_Encode_Bytes");
-                                                        result = BLOB_ERROR;
-                                                        isError = 1;
-                                                    }
-                                                    else
-                                                    {
-                                                        /*add the blockId base64 encoded to the XML*/
-                                                        if (!(
-                                                            (STRING_concat(xml, "<Latest>") == 0) &&
-                                                            (STRING_concat_with_STRING(xml, blockIdString) == 0) &&
-                                                            (STRING_concat(xml, "</Latest>") == 0)
-                                                            ))
-                                                        {
-                                                            /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                            LogError("unable to STRING_concat");
-                                                            result = BLOB_ERROR;
-                                                            isError = 1;
-                                                        }
-                                                        else
-                                                        {
-                                                            /*Codes_SRS_BLOB_02_022: [ Blob_UploadFromSasUri shall construct a new relativePath from following string: base relativePath + "&comp=block&blockid=BASE64 encoded string of blockId" ]*/
-                                                            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_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                                LogError("unable to STRING_construct");
-                                                                result = BLOB_ERROR;
-                                                                isError = 1;
-                                                            }
-                                                            else
-                                                            {
-                                                                if (!(
-                                                                    (STRING_concat(newRelativePath, "&comp=block&blockid=") == 0) &&
-                                                                    (STRING_concat_with_STRING(newRelativePath, blockIdString) == 0)
-                                                                    ))
-                                                                {
-                                                                    /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                                    LogError("unable to STRING concatenate");
-                                                                    result = BLOB_ERROR;
-                                                                    isError = 1;
-                                                                }
-                                                                else
-                                                                {
-                                                                    /*Codes_SRS_BLOB_02_023: [ Blob_UploadFromSasUri shall create a BUFFER_HANDLE from source and size parameters. ]*/
-                                                                    BUFFER_HANDLE requestContent = BUFFER_create(source + (size - toUpload), thisBlockSize);
-                                                                    if (requestContent == NULL)
-                                                                    {
-                                                                        /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                                        LogError("unable to BUFFER_create");
-                                                                        result = BLOB_ERROR;
-                                                                        isError = 1;
-                                                                    }
-                                                                    else
-                                                                    {
-                                                                        /*Codes_SRS_BLOB_02_024: [ Blob_UploadFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing httpStatus and httpResponse. ]*/
-                                                                        if (HTTPAPIEX_ExecuteRequest(
-                                                                            httpApiExHandle,
-                                                                            HTTPAPI_REQUEST_PUT,
-                                                                            STRING_c_str(newRelativePath),
-                                                                            NULL,
-                                                                            requestContent,
-                                                                            httpStatus,
-                                                                            NULL,
-                                                                            httpResponse) != HTTPAPIEX_OK
-                                                                            )
-                                                                        {
-                                                                            /*Codes_SRS_BLOB_02_025: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
-                                                                            LogError("unable to HTTPAPIEX_ExecuteRequest");
-                                                                            result = BLOB_HTTP_ERROR;
-                                                                            isError = 1;
-                                                                        }
-                                                                        else if (*httpStatus >= 300)
-                                                                        {
-                                                                            /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadFromSasUri shall succeed and return BLOB_OK. ]*/
-                                                                            LogError("HTTP status from storage does not indicate success (%d)", (int)*httpStatus);
-                                                                            result = BLOB_OK;
-                                                                            isError = 1;
-                                                                        }
-                                                                        else
-                                                                        {
-                                                                            /*Codes_SRS_BLOB_02_027: [ Otherwise Blob_UploadFromSasUri shall continue execution. ]*/
-                                                                        }
-                                                                        BUFFER_delete(requestContent);
-                                                                    }
-                                                                }
-                                                                STRING_delete(newRelativePath);
-                                                            }
-                                                        }
-                                                        STRING_delete(blockIdString);
-                                                    }
-                                                }
-
-                                                blockID++;
-                                                toUpload -= thisBlockSize;
-                                            } while ((toUpload > 0) && !isError);
-
-                                            if (isError)
-                                            {
-                                                /*do nothing, it will be reported "as is"*/
-                                            }
-                                            else
-                                            {
-                                                /*complete the XML*/
-                                                if (STRING_concat(xml, "</BlockList>") != 0)
-                                                {
-                                                    /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
+                                                    /*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_UploadFromSasUri 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_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_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                        LogError("failed to STRING_construct");
+                                                        /*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 (STRING_concat(newRelativePath, "&comp=blocklist") != 0)
+                                                        if (HTTPAPIEX_ExecuteRequest(
+                                                            httpApiExHandle,
+                                                            HTTPAPI_REQUEST_PUT,
+                                                            STRING_c_str(newRelativePath),
+                                                            NULL,
+                                                            blockIDListAsBuffer,
+                                                            httpStatus,
+                                                            NULL,
+                                                            httpResponse
+                                                        ) != HTTPAPIEX_OK)
                                                         {
-                                                            /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri shall fail and return BLOB_ERROR ]*/
-                                                            LogError("failed to STRING_concat");
-                                                            result = BLOB_ERROR;
+                                                            /*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_030: [ Blob_UploadFromSasUri 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(xml);
-                                                            BUFFER_HANDLE xmlAsBuffer = BUFFER_create((const unsigned char*)s, strlen(s));
-                                                            if (xmlAsBuffer == NULL)
-                                                            {
-                                                                /*Codes_SRS_BLOB_02_033: [ If any previous operation that doesn't have an explicit failure description fails then Blob_UploadFromSasUri 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,
-                                                                    xmlAsBuffer,
-                                                                    httpStatus,
-                                                                    NULL,
-                                                                    httpResponse
-                                                                ) != HTTPAPIEX_OK)
-                                                                {
-                                                                    /*Codes_SRS_BLOB_02_031: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
-                                                                    LogError("unable to HTTPAPIEX_ExecuteRequest");
-                                                                    result = BLOB_HTTP_ERROR;
-                                                                }
-                                                                else
-                                                                {
-                                                                    /*Codes_SRS_BLOB_02_032: [ Otherwise, Blob_UploadFromSasUri shall succeed and return BLOB_OK. ]*/
-                                                                    result = BLOB_OK;
-                                                                }
-                                                                BUFFER_delete(xmlAsBuffer);
-                                                            }
+                                                            /*Codes_SRS_BLOB_02_032: [ Otherwise, Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
+                                                            result = BLOB_OK;
                                                         }
-                                                        STRING_delete(newRelativePath);
+                                                        BUFFER_delete(blockIDListAsBuffer);
                                                     }
                                                 }
+                                                STRING_delete(newRelativePath);
                                             }
-                                            STRING_delete(xml);
                                         }
                                     }
+                                    STRING_delete(blockIDList);
                                 }
+
                             }
                             HTTPAPIEX_Destroy(httpApiExHandle);
                         }