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 void http_examples() { HTTPAPIEX_HANDLE handle = HTTPAPIEX_Create("www.bing.com"); if (handle == NULL) { LogError("Failed creating httpApiEx handle"); } else { HTTPAPIEX_Destroy(handle); } }
static IOTHUB_DEVICE_METHOD_RESULT sendHttpRequestDeviceMethod(IOTHUB_SERVICE_CLIENT_DEVICE_METHOD_HANDLE serviceClientDeviceMethodHandle, IOTHUB_DEVICEMETHOD_REQUEST_MODE iotHubDeviceMethodRequestMode, const char* deviceName, BUFFER_HANDLE deviceJsonBuffer, BUFFER_HANDLE responseBuffer) { IOTHUB_DEVICE_METHOD_RESULT result; STRING_HANDLE uriResouce; STRING_HANDLE accessKey; STRING_HANDLE keyName; HTTPAPIEX_SAS_HANDLE httpExApiSasHandle; HTTPAPIEX_HANDLE httpExApiHandle; HTTP_HEADERS_HANDLE httpHeader; if ((uriResouce = STRING_construct(serviceClientDeviceMethodHandle->hostname)) == NULL) { LogError("STRING_construct failed for uriResource"); result = IOTHUB_DEVICE_METHOD_ERROR; } else if ((accessKey = STRING_construct(serviceClientDeviceMethodHandle->sharedAccessKey)) == NULL) { LogError("STRING_construct failed for accessKey"); STRING_delete(uriResouce); result = IOTHUB_DEVICE_METHOD_ERROR; } else if ((keyName = STRING_construct(serviceClientDeviceMethodHandle->keyName)) == NULL) { LogError("STRING_construct failed for keyName"); STRING_delete(accessKey); STRING_delete(uriResouce); result = IOTHUB_DEVICE_METHOD_ERROR; } else if ((httpHeader = createHttpHeader()) == NULL) { LogError("HttpHeader creation failed"); STRING_delete(keyName); STRING_delete(accessKey); STRING_delete(uriResouce); result = IOTHUB_DEVICE_METHOD_ERROR; } else if ((httpExApiSasHandle = HTTPAPIEX_SAS_Create(accessKey, uriResouce, keyName)) == NULL) { LogError("HTTPAPIEX_SAS_Create failed"); HTTPHeaders_Free(httpHeader); STRING_delete(keyName); STRING_delete(accessKey); STRING_delete(uriResouce); result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; } else if ((httpExApiHandle = HTTPAPIEX_Create(serviceClientDeviceMethodHandle->hostname)) == NULL) { LogError("HTTPAPIEX_Create failed"); HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); HTTPHeaders_Free(httpHeader); STRING_delete(keyName); STRING_delete(accessKey); STRING_delete(uriResouce); result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; } else { HTTPAPI_REQUEST_TYPE httpApiRequestType = HTTPAPI_REQUEST_GET; STRING_HANDLE relativePath; unsigned int statusCode = 0; unsigned char is_error = 0; if (iotHubDeviceMethodRequestMode == IOTHUB_DEVICEMETHOD_REQUEST_INVOKE) { httpApiRequestType = HTTPAPI_REQUEST_POST; } else { is_error = 1; } if (is_error) { LogError("Invalid request type"); result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; } else { if ((relativePath = createRelativePath(iotHubDeviceMethodRequestMode, deviceName)) == NULL) { LogError("Failure creating relative path"); result = IOTHUB_DEVICE_METHOD_ERROR; } else if (HTTPAPIEX_SAS_ExecuteRequest(httpExApiSasHandle, httpExApiHandle, httpApiRequestType, STRING_c_str(relativePath), httpHeader, deviceJsonBuffer, &statusCode, NULL, responseBuffer) != HTTPAPIEX_OK) { LogError("HTTPAPIEX_SAS_ExecuteRequest failed"); STRING_delete(relativePath); result = IOTHUB_DEVICE_METHOD_HTTPAPI_ERROR; } else { STRING_delete(relativePath); if (statusCode == 200) { result = IOTHUB_DEVICE_METHOD_OK; } else { LogError("Http Failure status code %d.", statusCode); result = IOTHUB_DEVICE_METHOD_ERROR; } } } HTTPAPIEX_Destroy(httpExApiHandle); HTTPAPIEX_SAS_Destroy(httpExApiSasHandle); HTTPHeaders_Free(httpHeader); STRING_delete(keyName); STRING_delete(accessKey); STRING_delete(uriResouce); } 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; }
BLOB_RESULT Blob_UploadMultipleBlocksFromSasUri(const char* SASURI, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse, const char* certificates, HTTP_PROXY_OPTIONS *proxyOptions) { BLOB_RESULT result; /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/ if (SASURI == NULL) { LogError("parameter SASURI is NULL"); 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"); 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; } 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); } } } } } return result; }
static BUFFER_HANDLE sendDeviceRegistryInfo(IOTHUB_ACCOUNT_INFO* accountInfo, BUFFER_HANDLE deviceBuffer, HTTPAPI_REQUEST_TYPE requestType) { BUFFER_HANDLE result; STRING_HANDLE accessKey = STRING_construct(accountInfo->sharedAccessKey); STRING_HANDLE uriResouce = STRING_construct(accountInfo->hostname); STRING_HANDLE keyName = STRING_construct(accountInfo->keyName); if (accessKey != NULL && uriResouce != NULL && keyName != NULL) { HTTPAPIEX_SAS_HANDLE httpHandle = HTTPAPIEX_SAS_Create(accessKey, uriResouce, keyName); if (httpHandle != NULL) { HTTPAPIEX_HANDLE httpExApi = HTTPAPIEX_Create(accountInfo->hostname); if (httpExApi == NULL) { LogError("Failure creating httpApiEx with hostname: %s.\r\n", accountInfo->hostname); result = NULL; } else { char relativePath[256]; if (sprintf_s(relativePath, 256, RELATIVE_PATH_FMT, accountInfo->deviceId, URL_API_VERSION) <= 0) { LogError("Failure creating relative path.\r\n"); result = NULL; } else { unsigned int statusCode = 0; // Send PUT method to url HTTP_HEADERS_HANDLE httpHeader = getContentHeaders((deviceBuffer == NULL) ? true : false); if (httpHeader == NULL) { result = NULL; } else { BUFFER_HANDLE responseContent = BUFFER_new(); if (HTTPAPIEX_SAS_ExecuteRequest(httpHandle, httpExApi, requestType, relativePath, httpHeader, deviceBuffer, &statusCode, NULL, responseContent) != HTTPAPIEX_OK) { LogError("Failure calling HTTPAPIEX_SAS_ExecuteRequest.\r\n"); result = NULL; } else { // 409 means the device is already created so we don't need // to create another one. if (statusCode != 409 && statusCode > 300) { LogError("Http Failure status code %d.\r\n", statusCode); BUFFER_delete(responseContent); result = NULL; } else { result = responseContent; } } } HTTPHeaders_Free(httpHeader); } HTTPAPIEX_Destroy(httpExApi); } HTTPAPIEX_SAS_Destroy(httpHandle); } else { LogError("Http Failure with HTTPAPIEX_SAS_Create.\r\n"); result = NULL; } } STRING_delete(accessKey); STRING_delete(uriResouce); STRING_delete(keyName); return result; }