static void sendHttpRequestMethodExpectedCalls() { STRICT_EXPECTED_CALL(environment_get_variable(IGNORED_PTR_ARG)).CallCannotFail(); STRICT_EXPECTED_CALL(HTTPHeaders_Alloc()); STRICT_EXPECTED_CALL(HTTPHeaders_AddHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_AddHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(gballoc_malloc(IGNORED_NUM_ARG)); STRICT_EXPECTED_CALL(UniqueId_Generate(IGNORED_PTR_ARG, IGNORED_NUM_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_AddHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_AddHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_AddHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(IoTHubClient_Auth_Get_SasToken(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_NUM_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_ReplaceHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_ReplaceHeaderNameValuePair(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(STRING_c_str(IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPAPIEX_Create(IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(IoTHubClient_Auth_Get_TrustBundle(IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPAPIEX_SetOption(IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPAPIEX_ExecuteRequest(IGNORED_PTR_ARG, HTTPAPI_REQUEST_POST, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, IGNORED_PTR_ARG, NULL, IGNORED_PTR_ARG)); STRICT_EXPECTED_CALL(HTTPHeaders_Free(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(STRING_delete(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(HTTPAPIEX_Destroy(IGNORED_PTR_ARG)); //cannot fail STRICT_EXPECTED_CALL(gballoc_free(IGNORED_PTR_ARG)); //cannot fail }
static HTTP_HEADERS_HANDLE getContentHeaders(bool appendIfMatch) { HTTP_HEADERS_HANDLE httpHeader = HTTPHeaders_Alloc(); if (httpHeader != NULL) { if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, "Authorization", " ") != HTTP_HEADERS_OK || HTTPHeaders_AddHeaderNameValuePair(httpHeader, "User-Agent", "Microsoft.Azure.Devices/1.0.0") != HTTP_HEADERS_OK || HTTPHeaders_AddHeaderNameValuePair(httpHeader, "Accept", "application/json") != HTTP_HEADERS_OK || HTTPHeaders_AddHeaderNameValuePair(httpHeader, "Content-Type", "application/json; charset=utf-8") != HTTP_HEADERS_OK) { LogError("Failure adding http headers.\r\n"); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } else { if (appendIfMatch) { if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, "If-Match", "*") != HTTP_HEADERS_OK) { LogError("Failure adding if-Match http headers.\r\n"); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } } } } return httpHeader; }
/*any other code is error*/ static int buildRequestHttpHeadersHandle(HTTPAPIEX_HANDLE_DATA *handleData, BUFFER_HANDLE requestContent, HTTP_HEADERS_HANDLE originalRequestHttpHeadersHandle, bool* isOriginalRequestHttpHeadersHandle, HTTP_HEADERS_HANDLE* toBeUsedRequestHttpHeadersHandle) { int result; if (originalRequestHttpHeadersHandle != NULL) { *toBeUsedRequestHttpHeadersHandle = originalRequestHttpHeadersHandle; *isOriginalRequestHttpHeadersHandle = true; } else { /*Codes_SRS_HTTPAPIEX_02_009: [If parameter requestHttpHeadersHandle is NULL then HTTPAPIEX_ExecuteRequest shall allocate a temporary internal instance of HTTPHEADERS, shall add to that instance the following headers Host:{hostname} - as it was indicated by the call to HTTPAPIEX_Create API call Content-Length:the size of the requestContent parameter, and use this instance to all the subsequent calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.] */ *isOriginalRequestHttpHeadersHandle = false; *toBeUsedRequestHttpHeadersHandle = HTTPHeaders_Alloc(); } if (*toBeUsedRequestHttpHeadersHandle == NULL) { result = __LINE__; LogError("unable to HTTPHeaders_Alloc\r\n"); } else { char temp[22]; (void)size_tToString(temp, 22, BUFFER_length(requestContent)); /*cannot fail, MAX_uint64 has 19 digits*/ /*Codes_SRS_HTTPAPIEX_02_011: [If parameter requestHttpHeadersHandle is not NULL then HTTPAPIEX_ExecuteRequest shall create or update the following headers of the request: Host:{hostname} Content-Length:the size of the requestContent parameter, and shall use the so constructed HTTPHEADERS object to all calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.] */ /*Codes_SRS_HTTPAPIEX_02_009: [If parameter requestHttpHeadersHandle is NULL then HTTPAPIEX_ExecuteRequest shall allocate a temporary internal instance of HTTPHEADERS, shall add to that instance the following headers Host:{hostname} - as it was indicated by the call to HTTPAPIEX_Create API call Content-Length:the size of the requestContent parameter, and use this instance to all the subsequent calls to HTTPAPI_ExecuteRequest as parameter httpHeadersHandle.] */ if (!( (HTTPHeaders_ReplaceHeaderNameValuePair(*toBeUsedRequestHttpHeadersHandle, "Host", STRING_c_str(handleData->hostName)) == HTTP_HEADERS_OK) && (HTTPHeaders_ReplaceHeaderNameValuePair(*toBeUsedRequestHttpHeadersHandle, "Content-Length", temp) == HTTP_HEADERS_OK) )) { if (! *isOriginalRequestHttpHeadersHandle) { HTTPHeaders_Free(*toBeUsedRequestHttpHeadersHandle); } *toBeUsedRequestHttpHeadersHandle = NULL; result = __LINE__; } else { result = 0; } } return result; }
static HTTP_HEADERS_HANDLE createHttpHeader() { /*Codes_SRS_IOTHUBDEVICEMETHOD_12_020: [ IoTHubDeviceMethod_GetTwin shall add the following headers to the created HTTP GET request: authorization=sasToken,Request-Id=1001,Accept=application/json,Content-Type=application/json,charset=utf-8 ]*/ HTTP_HEADERS_HANDLE httpHeader; const char* guid; if ((httpHeader = HTTPHeaders_Alloc()) == NULL) { LogError("HTTPHeaders_Alloc failed"); } else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_AUTHORIZATION, HTTP_HEADER_VAL_AUTHORIZATION) != HTTP_HEADERS_OK) { LogError("HTTPHeaders_AddHeaderNameValuePair failed for Authorization header"); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } else if ((guid = generateGuid()) == NULL) { LogError("GUID creation failed"); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_REQUEST_ID, guid) != HTTP_HEADERS_OK) { LogError("HTTPHeaders_AddHeaderNameValuePair failed for RequestId header"); free((void*)guid); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_USER_AGENT, HTTP_HEADER_VAL_USER_AGENT) != HTTP_HEADERS_OK) { LogError("HTTPHeaders_AddHeaderNameValuePair failed for User-Agent header"); free((void*)guid); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } else if (HTTPHeaders_AddHeaderNameValuePair(httpHeader, HTTP_HEADER_KEY_CONTENT_TYPE, HTTP_HEADER_VAL_CONTENT_TYPE) != HTTP_HEADERS_OK) { LogError("HTTPHeaders_AddHeaderNameValuePair failed for Content-Type header"); free((void*)guid); HTTPHeaders_Free(httpHeader); httpHeader = NULL; } else { free((void*)guid); } return httpHeader; }
static int buildResponseHttpHeadersHandle(HTTP_HEADERS_HANDLE originalResponsetHttpHeadersHandle, bool* isOriginalResponseHttpHeadersHandle, HTTP_HEADERS_HANDLE* toBeUsedResponsetHttpHeadersHandle) { int result; if (originalResponsetHttpHeadersHandle == NULL) { if ((*toBeUsedResponsetHttpHeadersHandle = HTTPHeaders_Alloc()) == NULL) { result = __LINE__; } else { *isOriginalResponseHttpHeadersHandle = false; result = 0; } } else { *isOriginalResponseHttpHeadersHandle = true; *toBeUsedResponsetHttpHeadersHandle = originalResponsetHttpHeadersHandle; result = 0; } return result; }
BLOB_RESULT Blob_UploadFromSasUri(const char* SASURI, const unsigned char* source, size_t size) { BLOB_RESULT result; /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ if (SASURI == NULL) { LogError("parameter SASURI is NULL"); result = BLOB_INVALID_ARG; } 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) ) { LogError("combination of source = %p and size = %zd is invalid", source, size); result = BLOB_INVALID_ARG; } /*Codes_SRS_BLOB_02_003: [ If size is bigger or equal to 64M then Blob_UploadFromSasUri shall fail and return BLOB_NOT_IMPLEMENTED. ]*/ else if (size >= 64 * 1024 * 1024) { LogError("upload of files greater than 64M is not implemented"); result = BLOB_NOT_IMPLEMENTED; } else { /*Codes_SRS_BLOB_02_004: [ Blob_UploadFromSasUri 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. ]*/ LogError("hostname cannot be determined"); result = BLOB_INVALID_ARG; } 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_UploadFromSasUri 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_UploadFromSasUri shall fail and return BLOB_ERROR ]*/ LogError("oom - out of memory"); result = BLOB_ERROR; } else { HTTPAPIEX_HANDLE httpApiExHandle; 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. ]*/ 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. ]*/ LogError("unable to create a HTTPAPIEX_HANDLE"); result = BLOB_ERROR; } else { /*Codes_SRS_BLOB_02_008: [ Blob_UploadFromSasUri shall compute the 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_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_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"); 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_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"); 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 { int statusCode; /*Codes_SRS_BLOB_02_012: [ Blob_UploadFromSasUri shall call HTTPAPIEX_ExecuteRequest passing the parameters previously build. ]*/ if (HTTPAPIEX_ExecuteRequest(httpApiExHandle, HTTPAPI_REQUEST_PUT, relativePath, requestHttpHeaders, requestBuffer, &statusCode, NULL, NULL) != 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 { if (statusCode >= 300) { /*Codes_SRS_BLOB_02_014: [ If the statusCode returned by HTTPAPIEX_ExecuteRequest is greater or equal to 300, then Blob_UploadFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/ LogError("server returns %d HTTP code", statusCode); 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); } HTTPAPIEX_Destroy(httpApiExHandle); } free(hostname); } } } } } return result; }
BLOB_RESULT Blob_UploadFromSasUri(const char* SASURI, const unsigned char* source, size_t size, 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 (SASURI == NULL) { LogError("parameter SASURI is NULL"); result = BLOB_INVALID_ARG; } 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) ) { LogError("combination of source = %p and size = %zu is invalid", source, size); result = BLOB_INVALID_ARG; } /*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 > 50000ULL * 4 * 1024 * 1024) /*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; } 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*. ]*/ /*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. ]*/ LogError("hostname cannot be determined"); result = BLOB_INVALID_ARG; } 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_UploadFromSasUri 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_UploadFromSasUri shall fail and return BLOB_ERROR ]*/ LogError("oom - out of memory"); result = BLOB_ERROR; } else { HTTPAPIEX_HANDLE httpApiExHandle; 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. ]*/ 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. ]*/ LogError("unable to create a HTTPAPIEX_HANDLE"); result = BLOB_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*/ if (size < 64 * 1024 * 1024) /*code path for sizes <64MB*/ { /*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_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"); 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_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"); 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 { 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 ]*/ 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_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_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_UploadFromSasUri shall fail and return BLOB_ERROR ]*/ LogError("failed to STRING_concat"); result = BLOB_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); } } STRING_delete(newRelativePath); } } } STRING_delete(xml); } } HTTPAPIEX_Destroy(httpApiExHandle); } free(hostname); } } } } } return result; }