source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/blob.c@ 464

Last change on this file since 464 was 464, checked in by coas-nagasima, 3 years ago

WolfSSLとAzure IoT SDKを更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 20.1 KB
Line 
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#include <stdlib.h>
5#include <stdint.h>
6
7#include "azure_c_shared_utility/gballoc.h"
8#include "internal/blob.h"
9#include "internal/iothub_client_ll_uploadtoblob.h"
10
11#include "azure_c_shared_utility/httpapiex.h"
12#include "azure_c_shared_utility/xlogging.h"
13#include "azure_c_shared_utility/azure_base64.h"
14#include "azure_c_shared_utility/shared_util_options.h"
15
16static const char blockListXmlBegin[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<BlockList>";
17static const char blockListXmlEnd[] = "</BlockList>";
18static const char blockListUriMarker[] = "&comp=blocklist";
19
20BLOB_RESULT Blob_UploadBlock(
21 HTTPAPIEX_HANDLE httpApiExHandle,
22 const char* relativePath,
23 BUFFER_HANDLE requestContent,
24 unsigned int blockID,
25 STRING_HANDLE blockIDList,
26 unsigned int* httpStatus,
27 BUFFER_HANDLE httpResponse)
28{
29 BLOB_RESULT result;
30
31 if (requestContent == NULL ||
32 blockIDList == NULL ||
33 relativePath == NULL ||
34 httpApiExHandle == NULL ||
35 httpStatus == NULL ||
36 httpResponse == NULL)
37 {
38 LogError("invalid argument detected requestContent=%p blockIDList=%p relativePath=%p httpApiExHandle=%p httpStatus=%p httpResponse=%p", requestContent, blockIDList, relativePath, httpApiExHandle, httpStatus, httpResponse);
39 result = BLOB_ERROR;
40 }
41 else if (blockID > 49999) /*outside the expected range of 000000... 049999*/
42 {
43 LogError("block ID too large");
44 result = BLOB_ERROR;
45 }
46 else
47 {
48 char temp[7]; /*this will contain 000000... 049999*/
49 if (sprintf(temp, "%6u", (unsigned int)blockID) != 6) /*produces 000000... 049999*/
50 {
51 /*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 ]*/
52 LogError("failed to sprintf");
53 result = BLOB_ERROR;
54 }
55 else
56 {
57 STRING_HANDLE blockIdString = Azure_Base64_Encode_Bytes((const unsigned char*)temp, 6);
58 if (blockIdString == NULL)
59 {
60 /*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 ]*/
61 LogError("unable to Azure_Base64_Encode_Bytes");
62 result = BLOB_ERROR;
63 }
64 else
65 {
66 /*add the blockId base64 encoded to the XML*/
67 if (!(
68 (STRING_concat(blockIDList, "<Latest>") == 0) &&
69 (STRING_concat_with_STRING(blockIDList, blockIdString) == 0) &&
70 (STRING_concat(blockIDList, "</Latest>") == 0)
71 ))
72 {
73 /*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 ]*/
74 LogError("unable to STRING_concat");
75 result = BLOB_ERROR;
76 }
77 else
78 {
79 /*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" ]*/
80 STRING_HANDLE newRelativePath = STRING_construct(relativePath);
81 if (newRelativePath == NULL)
82 {
83 /*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 ]*/
84 LogError("unable to STRING_construct");
85 result = BLOB_ERROR;
86 }
87 else
88 {
89 if (!(
90 (STRING_concat(newRelativePath, "&comp=block&blockid=") == 0) &&
91 (STRING_concat_with_STRING(newRelativePath, blockIdString) == 0)
92 ))
93 {
94 /*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 ]*/
95 LogError("unable to STRING concatenate");
96 result = BLOB_ERROR;
97 }
98 else
99 {
100 /*Codes_SRS_BLOB_02_024: [ Blob_UploadMultipleBlocksFromSasUri shall call HTTPAPIEX_ExecuteRequest with a PUT operation, passing httpStatus and httpResponse. ]*/
101 if (HTTPAPIEX_ExecuteRequest(
102 httpApiExHandle,
103 HTTPAPI_REQUEST_PUT,
104 STRING_c_str(newRelativePath),
105 NULL,
106 requestContent,
107 httpStatus,
108 NULL,
109 httpResponse) != HTTPAPIEX_OK
110 )
111 {
112 /*Codes_SRS_BLOB_02_025: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
113 LogError("unable to HTTPAPIEX_ExecuteRequest");
114 result = BLOB_HTTP_ERROR;
115 }
116 else if (*httpStatus >= 300)
117 {
118 /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
119 LogError("HTTP status from storage does not indicate success (%d)", (int)*httpStatus);
120 result = BLOB_OK;
121 }
122 else
123 {
124 /*Codes_SRS_BLOB_02_027: [ Otherwise Blob_UploadMultipleBlocksFromSasUri shall continue execution. ]*/
125 result = BLOB_OK;
126 }
127 }
128 STRING_delete(newRelativePath);
129 }
130 }
131 STRING_delete(blockIdString);
132 }
133 }
134 }
135 return result;
136}
137
138
139// InvokeUserCallbackAndSendBlobs invokes the application's getDataCallbackEx as many time as callback requests and, for each call,
140// sends the blob contents to the server.
141static BLOB_RESULT InvokeUserCallbackAndSendBlobs(HTTPAPIEX_HANDLE httpApiExHandle, const char* relativePath, STRING_HANDLE blockIDList, IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_CALLBACK_EX getDataCallbackEx, void* context, unsigned int* httpStatus, BUFFER_HANDLE httpResponse)
142{
143 BLOB_RESULT result;
144
145 /*Codes_SRS_BLOB_02_021: [ For every block returned by `getDataCallbackEx` the following operations shall happen: ]*/
146 unsigned int blockID = 0; /* incremented for each new block */
147 unsigned int isError = 0; /* set to 1 if a block upload fails or if getDataCallbackEx returns incorrect blocks to upload */
148 unsigned int uploadOneMoreBlock = 1; /* set to 1 while getDataCallbackEx returns correct blocks to upload */
149 unsigned char const * source = NULL; /* data set by getDataCallbackEx */
150 size_t size = 0; /* source size set by getDataCallbackEx */
151 IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_RESULT getDataReturnValue;
152
153 do
154 {
155 getDataReturnValue = getDataCallbackEx(FILE_UPLOAD_OK, &source, &size, context);
156 if (getDataReturnValue == IOTHUB_CLIENT_FILE_UPLOAD_GET_DATA_ABORT)
157 {
158 /*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`. ]*/
159 LogInfo("Upload to blob has been aborted by the user");
160 uploadOneMoreBlock = 0;
161 result = BLOB_ABORTED;
162 }
163 else if (source == NULL || size == 0)
164 {
165 /*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. ]*/
166 uploadOneMoreBlock = 0;
167 result = BLOB_OK;
168 }
169 else
170 {
171 if (size > BLOCK_SIZE)
172 {
173 /*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`. ]*/
174 LogError("tried to upload block of size %lu, max allowed size is %d", (unsigned long)size, BLOCK_SIZE);
175 result = BLOB_INVALID_ARG;
176 isError = 1;
177 }
178 else if (blockID >= MAX_BLOCK_COUNT)
179 {
180 /*Codes_SRS_BLOB_99_003: [ If `getDataCallbackEx` returns more than 50000 blocks, then `Blob_UploadMultipleBlocksFromSasUri` shall fail and return `BLOB_INVALID_ARG`. ]*/
181 LogError("unable to upload more than %lu blocks in one blob", (unsigned long)MAX_BLOCK_COUNT);
182 result = BLOB_INVALID_ARG;
183 isError = 1;
184 }
185 else
186 {
187 /*Codes_SRS_BLOB_02_023: [ Blob_UploadMultipleBlocksFromSasUri shall create a BUFFER_HANDLE from source and size parameters. ]*/
188 BUFFER_HANDLE requestContent = BUFFER_create(source, size);
189 if (requestContent == NULL)
190 {
191 /*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 ]*/
192 LogError("unable to BUFFER_create");
193 result = BLOB_ERROR;
194 isError = 1;
195 }
196 else
197 {
198 result = Blob_UploadBlock(
199 httpApiExHandle,
200 relativePath,
201 requestContent,
202 blockID,
203 blockIDList,
204 httpStatus,
205 httpResponse);
206
207 BUFFER_delete(requestContent);
208 }
209
210 /*Codes_SRS_BLOB_02_026: [ Otherwise, if HTTP response code is >=300 then Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
211 if (result != BLOB_OK)
212 {
213 LogError("unable to Blob_UploadBlock. Returned value=%d", result);
214 isError = 1;
215 }
216 else if (*httpStatus >= 300)
217 {
218 LogError("unable to Blob_UploadBlock. Returned httpStatus=%u", (unsigned int)*httpStatus);
219 isError = 1;
220 }
221 }
222 blockID++;
223 }
224 }
225 while(uploadOneMoreBlock && !isError);
226
227 return result;
228}
229
230// SendBlockIdList to send an XML of uploaded blockIds to the server after the application's payload block(s) have been transfered.
231static BLOB_RESULT SendBlockIdList(HTTPAPIEX_HANDLE httpApiExHandle, const char* relativePath, STRING_HANDLE blockIDList, unsigned int* httpStatus, BUFFER_HANDLE httpResponse)
232{
233 BLOB_RESULT result;
234
235 /*complete the XML*/
236 if (STRING_concat(blockIDList, blockListXmlEnd) != 0)
237 {
238 /*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 ]*/
239 LogError("failed to STRING_concat");
240 result = BLOB_ERROR;
241 }
242 else
243 {
244 /*Codes_SRS_BLOB_02_029: [Blob_UploadMultipleBlocksFromSasUri shall construct a new relativePath from following string : base relativePath + "&comp=blocklist"]*/
245 STRING_HANDLE newRelativePath = STRING_construct(relativePath);
246 if (newRelativePath == NULL)
247 {
248 /*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 ]*/
249 LogError("failed to STRING_construct");
250 result = BLOB_ERROR;
251 }
252 else
253 {
254 if (STRING_concat(newRelativePath, blockListUriMarker) != 0)
255 {
256 /*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 ]*/
257 LogError("failed to STRING_concat");
258 result = BLOB_ERROR;
259 }
260 else
261 {
262 /*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. ]*/
263 const char* s = STRING_c_str(blockIDList);
264 BUFFER_HANDLE blockIDListAsBuffer = BUFFER_create((const unsigned char*)s, strlen(s));
265 if (blockIDListAsBuffer == NULL)
266 {
267 /*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 ]*/
268 LogError("failed to BUFFER_create");
269 result = BLOB_ERROR;
270 }
271 else
272 {
273 if (HTTPAPIEX_ExecuteRequest(
274 httpApiExHandle,
275 HTTPAPI_REQUEST_PUT,
276 STRING_c_str(newRelativePath),
277 NULL,
278 blockIDListAsBuffer,
279 httpStatus,
280 NULL,
281 httpResponse
282 ) != HTTPAPIEX_OK)
283 {
284 /*Codes_SRS_BLOB_02_031: [ If HTTPAPIEX_ExecuteRequest fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_HTTP_ERROR. ]*/
285 LogError("unable to HTTPAPIEX_ExecuteRequest");
286 result = BLOB_HTTP_ERROR;
287 }
288 else
289 {
290 /*Codes_SRS_BLOB_02_032: [ Otherwise, Blob_UploadMultipleBlocksFromSasUri shall succeed and return BLOB_OK. ]*/
291 result = BLOB_OK;
292 }
293 BUFFER_delete(blockIDListAsBuffer);
294 }
295 }
296 STRING_delete(newRelativePath);
297 }
298 }
299
300 return result;
301}
302
303
304BLOB_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)
305{
306 BLOB_RESULT result;
307 const char* hostnameBegin;
308 STRING_HANDLE blockIDList = NULL;
309 HTTPAPIEX_HANDLE httpApiExHandle = NULL;
310 char* hostname = NULL;
311
312 /*Codes_SRS_BLOB_02_001: [ If SASURI is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
313 /*Codes_SRS_BLOB_02_002: [ If getDataCallbackEx is NULL then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
314 if ((SASURI == NULL) || (getDataCallbackEx == NULL))
315 {
316 LogError("One or more required values is NULL, SASURI=%p, getDataCallbackEx=%p", SASURI, getDataCallbackEx);
317 result = BLOB_INVALID_ARG;
318 }
319 /*Codes_SRS_BLOB_02_017: [ Blob_UploadMultipleBlocksFromSasUri shall copy from SASURI the hostname to a new const char* ]*/
320 /*to find the hostname, the following logic is applied:*/
321 /*the hostname starts at the first character after "://"*/
322 /*the hostname ends at the first character before the next "/" after "://"*/
323 else if ((hostnameBegin = strstr(SASURI, "://")) == NULL)
324 {
325 /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
326 LogError("hostname cannot be determined");
327 result = BLOB_INVALID_ARG;
328 }
329 else
330 {
331 hostnameBegin += 3; /*have to skip 3 characters which are "://"*/
332 const char* relativePath = strchr(hostnameBegin, '/');
333 if (relativePath == NULL)
334 {
335 /*Codes_SRS_BLOB_02_005: [ If the hostname cannot be determined, then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_INVALID_ARG. ]*/
336 LogError("hostname cannot be determined");
337 result = BLOB_INVALID_ARG;
338 }
339 else
340 {
341 size_t hostnameSize = relativePath - hostnameBegin;
342 if ((hostname = (char*)malloc(hostnameSize + 1)) == NULL)
343 {
344 /*Codes_SRS_BLOB_02_016: [ If the hostname copy cannot be made then then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR ]*/
345 LogError("oom - out of memory");
346 result = BLOB_ERROR;
347 }
348 else
349 {
350 (void)memcpy(hostname, hostnameBegin, hostnameSize);
351 hostname[hostnameSize] = '\0';
352
353 /*Codes_SRS_BLOB_02_018: [ Blob_UploadMultipleBlocksFromSasUri shall create a new HTTPAPI_EX_HANDLE by calling HTTPAPIEX_Create passing the hostname. ]*/
354 httpApiExHandle = HTTPAPIEX_Create(hostname);
355 if (httpApiExHandle == NULL)
356 {
357 /*Codes_SRS_BLOB_02_007: [ If HTTPAPIEX_Create fails then Blob_UploadMultipleBlocksFromSasUri shall fail and return BLOB_ERROR. ]*/
358 LogError("unable to create a HTTPAPIEX_HANDLE");
359 result = BLOB_ERROR;
360 }
361 else if ((certificates != NULL) && (HTTPAPIEX_SetOption(httpApiExHandle, "TrustedCerts", certificates) == HTTPAPIEX_ERROR))
362 {
363 LogError("failure in setting trusted certificates");
364 result = BLOB_ERROR;
365 }
366 else if ((proxyOptions != NULL && proxyOptions->host_address != NULL) && HTTPAPIEX_SetOption(httpApiExHandle, OPTION_HTTP_PROXY, proxyOptions) == HTTPAPIEX_ERROR)
367 {
368 LogError("failure in setting proxy options");
369 result = BLOB_ERROR;
370 }
371 /*Codes_SRS_BLOB_02_028: [ Blob_UploadMultipleBlocksFromSasUri shall construct an XML string with the following content: ]*/
372 else if ((blockIDList = STRING_construct(blockListXmlBegin)) == NULL)
373 {
374 /*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 ]*/
375 LogError("failed to STRING_construct");
376 result = BLOB_HTTP_ERROR;
377 }
378 else if ((result = InvokeUserCallbackAndSendBlobs(httpApiExHandle, relativePath, blockIDList, getDataCallbackEx, context, httpStatus, httpResponse)) != BLOB_OK)
379 {
380 LogError("Failed in invoking callback/sending blob step");
381 }
382 else if (*httpStatus < 300)
383 {
384 // Per SRS_BLOB_02_026, it possible for us to have a result=BLOB_OK AND a non-success HTTP status code.
385 // In order to maintain back-compat with existing code, we will return the BLOB_OK to the caller but NOT invoke this final step.
386 result = SendBlockIdList(httpApiExHandle, relativePath, blockIDList, httpStatus, httpResponse);
387 }
388 }
389 }
390 }
391
392 HTTPAPIEX_Destroy(httpApiExHandle);
393 STRING_delete(blockIDList);
394 free(hostname);
395
396 return result;
397}
Note: See TracBrowser for help on using the repository browser.