source: azure_iot_hub_f767zi/trunk/azure_iot_sdk/iothub_client/src/iothubtransporthttp.c@ 457

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

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 132.9 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 "azure_c_shared_utility/gballoc.h"
6
7#include <time.h>
8#include "iothub_client_options.h"
9#include "iothub_client_version.h"
10#include "iothub_transport_ll.h"
11#include "iothubtransporthttp.h"
12#include "internal/iothub_client_private.h"
13#include "internal/iothubtransport.h"
14#include "internal/iothub_transport_ll_private.h"
15#include "internal/iothub_internal_consts.h"
16
17#include "azure_c_shared_utility/optimize_size.h"
18#include "azure_c_shared_utility/httpapiexsas.h"
19#include "azure_c_shared_utility/urlencode.h"
20#include "azure_c_shared_utility/xlogging.h"
21#include "azure_c_shared_utility/httpapiex.h"
22#include "azure_c_shared_utility/httpapiexsas.h"
23#include "azure_c_shared_utility/strings.h"
24#include "azure_c_shared_utility/azure_base64.h"
25#include "azure_c_shared_utility/doublylinkedlist.h"
26#include "azure_c_shared_utility/vector.h"
27#include "azure_c_shared_utility/httpheaders.h"
28#include "azure_c_shared_utility/agenttime.h"
29
30#define IOTHUB_APP_PREFIX "iothub-app-"
31static const char* IOTHUB_MESSAGE_ID = "iothub-messageid";
32static const char* IOTHUB_CORRELATION_ID = "iothub-correlationid";
33static const char* IOTHUB_CONTENT_TYPE_D2C = "iothub-contenttype";
34static const char* IOTHUB_CONTENT_ENCODING_D2C = "iothub-contentencoding";
35static const char* IOTHUB_CONTENT_TYPE_C2D = "ContentType";
36static const char* IOTHUB_CONTENT_ENCODING_C2D = "ContentEncoding";
37
38static const char* IOTHUB_AUTH_HEADER_VALUE = "Authorization";
39
40#define CONTENT_TYPE "Content-Type"
41#define APPLICATION_OCTET_STREAM "application/octet-stream"
42#define APPLICATION_VND_MICROSOFT_IOTHUB_JSON "application/vnd.microsoft.iothub.json"
43#define API_VERSION "?api-version=2016-11-14"
44#define EVENT_ENDPOINT "/messages/events"
45#define MESSAGE_ENDPOINT_HTTP "/messages/devicebound"
46#define MESSAGE_ENDPOINT_HTTP_ETAG "/messages/devicebound/"
47
48/*DEFAULT_GETMINIMUMPOLLINGTIME is the minimum time in seconds allowed between 2 consecutive GET issues to the service (GET=fetch messages)*/
49/*the default is 25 minutes*/
50#define DEFAULT_GETMINIMUMPOLLINGTIME ((unsigned int)25*60)
51
52#define MAXIMUM_MESSAGE_SIZE (255*1024-1)
53#define MAXIMUM_PAYLOAD_OVERHEAD 384
54#define MAXIMUM_PROPERTY_OVERHEAD 16
55
56/*forward declaration*/
57static int appendMapToJSON(STRING_HANDLE existing, const char* const* keys, const char* const* values, size_t count);
58
59typedef struct HTTPTRANSPORT_HANDLE_DATA_TAG
60{
61 STRING_HANDLE hostName;
62 HTTPAPIEX_HANDLE httpApiExHandle;
63 bool doBatchedTransfers;
64 unsigned int getMinimumPollingTime;
65 VECTOR_HANDLE perDeviceList;
66
67 TRANSPORT_CALLBACKS_INFO transport_callbacks;
68 void* transport_ctx;
69
70}HTTPTRANSPORT_HANDLE_DATA;
71
72typedef struct HTTPTRANSPORT_PERDEVICE_DATA_TAG
73{
74 HTTPTRANSPORT_HANDLE_DATA* transportHandle;
75
76 STRING_HANDLE deviceId;
77 STRING_HANDLE deviceKey;
78 STRING_HANDLE deviceSasToken;
79 STRING_HANDLE eventHTTPrelativePath;
80 STRING_HANDLE messageHTTPrelativePath;
81 HTTP_HEADERS_HANDLE eventHTTPrequestHeaders;
82 HTTP_HEADERS_HANDLE messageHTTPrequestHeaders;
83 STRING_HANDLE abandonHTTPrelativePathBegin;
84 HTTPAPIEX_SAS_HANDLE sasObject;
85 bool DoWork_PullMessage;
86 time_t lastPollTime;
87 bool isFirstPoll;
88
89 void* device_transport_ctx;
90 PDLIST_ENTRY waitingToSend;
91 DLIST_ENTRY eventConfirmations; /*holds items for event confirmations*/
92} HTTPTRANSPORT_PERDEVICE_DATA;
93
94typedef struct MESSAGE_DISPOSITION_CONTEXT_TAG
95{
96 HTTPTRANSPORT_HANDLE_DATA* handleData;
97 HTTPTRANSPORT_PERDEVICE_DATA* deviceData;
98 char* etagValue;
99} MESSAGE_DISPOSITION_CONTEXT;
100
101MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES);
102
103static void destroy_eventHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
104{
105 STRING_delete(handleData->eventHTTPrelativePath);
106 handleData->eventHTTPrelativePath = NULL;
107}
108
109static bool create_eventHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId)
110{
111 /*Codes_SRS_TRANSPORTMULTITHTTP_17_017: [ IoTHubTransportHttp_Register shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(deviceId) + "/messages/events" + APIVERSION. ]*/
112 bool result;
113 STRING_HANDLE urlEncodedDeviceId;
114 handleData->eventHTTPrelativePath = STRING_construct("/devices/");
115 if (handleData->eventHTTPrelativePath == NULL)
116 {
117 LogError("STRING_construct failed.");
118 result = false;
119 }
120 else
121 {
122 if (!(
123 ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) &&
124 (STRING_concat_with_STRING(handleData->eventHTTPrelativePath, urlEncodedDeviceId) == 0) &&
125 (STRING_concat(handleData->eventHTTPrelativePath, EVENT_ENDPOINT API_VERSION) == 0)
126 ))
127 {
128 /*Codes_SRS_TRANSPORTMULTITHTTP_17_018: [ If creating the string fail for any reason then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
129 destroy_eventHTTPrelativePath(handleData);
130 LogError("Creating HTTP event relative path failed.");
131 result = false;
132 }
133 else
134 {
135 result = true;
136 }
137 STRING_delete(urlEncodedDeviceId);
138 }
139 return result;
140}
141
142static int retrieve_message_properties(HTTP_HEADERS_HANDLE resp_header, IOTHUB_MESSAGE_HANDLE recv_msg)
143{
144 int result = 0;
145 /*Codes_SRS_TRANSPORTMULTITHTTP_17_090: [All the HTTP headers of the form iothub-app-name:somecontent shall be transformed in message properties {name, somecontent}.]*/
146 /*Codes_SRS_TRANSPORTMULTITHTTP_17_091: [The HTTP header of iothub-messageid shall be set in the MessageId.]*/
147 size_t nHeaders;
148 if (HTTPHeaders_GetHeaderCount(resp_header, &nHeaders) != HTTP_HEADERS_OK)
149 {
150 LogError("unable to get the count of HTTP headers");
151 result = MU_FAILURE;
152 }
153 else
154 {
155 MAP_HANDLE properties = (nHeaders > 0) ? IoTHubMessage_Properties(recv_msg) : NULL;
156 for (size_t index = 0; index < nHeaders; index++)
157 {
158 char* completeHeader;
159 if (HTTPHeaders_GetHeader(resp_header, index, &completeHeader) != HTTP_HEADERS_OK)
160 {
161 LogError("Failed getting header %lu", (unsigned long)index);
162 result = MU_FAILURE;
163 break;
164 }
165 else
166 {
167 char* colon_pos;
168 if (strncmp(IOTHUB_APP_PREFIX, completeHeader, strlen(IOTHUB_APP_PREFIX)) == 0)
169 {
170 /*looks like a property headers*/
171 /*there's a guaranteed ':' in the completeHeader, by HTTP_HEADERS module*/
172 colon_pos = strchr(completeHeader, ':');
173 if (colon_pos != NULL)
174 {
175 *colon_pos = '\0'; /*cut it down*/
176 if (Map_AddOrUpdate(properties, completeHeader + strlen(IOTHUB_APP_PREFIX), colon_pos + 2) != MAP_OK) /*whereIsColon+1 is a space because HTTPEHADERS outputs a ": " between name and value*/
177 {
178 free(completeHeader);
179 result = MU_FAILURE;
180 break;
181 }
182 }
183 }
184 else if (strncmp(IOTHUB_MESSAGE_ID, completeHeader, strlen(IOTHUB_MESSAGE_ID)) == 0)
185 {
186 char* whereIsColon = strchr(completeHeader, ':');
187 if (whereIsColon != NULL)
188 {
189 *whereIsColon = '\0'; /*cut it down*/
190 if (IoTHubMessage_SetMessageId(recv_msg, whereIsColon + 2) != IOTHUB_MESSAGE_OK)
191 {
192 free(completeHeader);
193 result = MU_FAILURE;
194 break;
195 }
196 }
197 }
198 else if (strncmp(IOTHUB_CORRELATION_ID, completeHeader, strlen(IOTHUB_CORRELATION_ID)) == 0)
199 {
200 char* whereIsColon = strchr(completeHeader, ':');
201 if (whereIsColon != NULL)
202 {
203 *whereIsColon = '\0'; /*cut it down*/
204 if (IoTHubMessage_SetCorrelationId(recv_msg, whereIsColon + 2) != IOTHUB_MESSAGE_OK)
205 {
206 free(completeHeader);
207 result = MU_FAILURE;
208 break;
209 }
210 }
211 }
212 // Codes_SRS_TRANSPORTMULTITHTTP_09_003: [ The HTTP header value of `ContentType` shall be set in the `IoTHubMessage_SetContentTypeSystemProperty`. ]
213 else if (strncmp(IOTHUB_CONTENT_TYPE_C2D, completeHeader, strlen(IOTHUB_CONTENT_TYPE_C2D)) == 0)
214 {
215 char* whereIsColon = strchr(completeHeader, ':');
216 if (whereIsColon != NULL)
217 {
218 *whereIsColon = '\0'; /*cut it down*/
219 if (IoTHubMessage_SetContentTypeSystemProperty(recv_msg, whereIsColon + 2) != IOTHUB_MESSAGE_OK)
220 {
221 LogError("Failed setting IoTHubMessage content-type");
222 result = MU_FAILURE;
223 break;
224 }
225 }
226 }
227 // Codes_SRS_TRANSPORTMULTITHTTP_09_004: [ The HTTP header value of `ContentEncoding` shall be set in the `IoTHub_SetContentEncoding`. ]
228 else if (strncmp(IOTHUB_CONTENT_ENCODING_C2D, completeHeader, strlen(IOTHUB_CONTENT_ENCODING_C2D)) == 0)
229 {
230 char* whereIsColon = strchr(completeHeader, ':');
231 if (whereIsColon != NULL)
232 {
233 *whereIsColon = '\0'; /*cut it down*/
234 if (IoTHubMessage_SetContentEncodingSystemProperty(recv_msg, whereIsColon + 2) != IOTHUB_MESSAGE_OK)
235 {
236 LogError("Failed setting IoTHubMessage content-encoding");
237 break;
238 }
239 }
240 }
241 free(completeHeader);
242 }
243 }
244 }
245 return result;
246}
247
248static int set_system_properties(IOTHUB_MESSAGE_LIST* message, HTTP_HEADERS_HANDLE headers)
249{
250 int result;
251 const char* msgId;
252 const char* corrId;
253 const char* content_type;
254 const char* contentEncoding;
255
256 // Add the Message Id and the Correlation Id
257 msgId = IoTHubMessage_GetMessageId(message->messageHandle);
258 if (msgId != NULL && HTTPHeaders_ReplaceHeaderNameValuePair(headers, IOTHUB_MESSAGE_ID, msgId) != HTTP_HEADERS_OK)
259 {
260 LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair");
261 result = MU_FAILURE;
262 }
263 else if ((corrId = IoTHubMessage_GetCorrelationId(message->messageHandle)) != NULL && HTTPHeaders_ReplaceHeaderNameValuePair(headers, IOTHUB_CORRELATION_ID, corrId) != HTTP_HEADERS_OK)
264 {
265 LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair");
266 result = MU_FAILURE;
267 }
268 // Codes_SRS_TRANSPORTMULTITHTTP_09_001: [ If the IoTHubMessage being sent contains property `content-type` it shall be added to the HTTP headers as "iothub-contenttype":"value". ]
269 else if ((content_type = IoTHubMessage_GetContentTypeSystemProperty(message->messageHandle)) != NULL && HTTPHeaders_ReplaceHeaderNameValuePair(headers, IOTHUB_CONTENT_TYPE_D2C, content_type) != HTTP_HEADERS_OK)
270 {
271 LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair (content-type)");
272 result = MU_FAILURE;
273 }
274 // Codes_SRS_TRANSPORTMULTITHTTP_09_002: [ If the IoTHubMessage being sent contains property `content-encoding` it shall be added to the HTTP headers as "iothub-contentencoding":"value". ]
275 else if ((contentEncoding = IoTHubMessage_GetContentEncodingSystemProperty(message->messageHandle)) != NULL && HTTPHeaders_ReplaceHeaderNameValuePair(headers, IOTHUB_CONTENT_ENCODING_D2C, contentEncoding) != HTTP_HEADERS_OK)
276 {
277 LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair (content-encoding)");
278 result = MU_FAILURE;
279 }
280 else if (IoTHubMessage_IsSecurityMessage(message->messageHandle) && HTTPHeaders_ReplaceHeaderNameValuePair(headers, SECURITY_INTERFACE_ID, SECURITY_INTERFACE_ID_VALUE) != HTTP_HEADERS_OK)
281 {
282 LogError("unable to set security message header info");
283 result = MU_FAILURE;
284 }
285 else
286 {
287 result = 0;
288 }
289 return result;
290}
291
292static bool set_message_properties(IOTHUB_MESSAGE_LIST* message, size_t* msg_size, HTTP_HEADERS_HANDLE headers, HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData)
293{
294 bool result = true;
295
296 /*Codes_SRS_TRANSPORTMULTITHTTP_17_078: [Every message property "property":"value" shall be added to the HTTP headers as an individual header "iothub-app-property":"value".] */
297 MAP_HANDLE map = IoTHubMessage_Properties(message->messageHandle);
298 const char*const* keys;
299 const char*const* values;
300 size_t count;
301 if (Map_GetInternals(map, &keys, &values, &count) != MAP_OK)
302 {
303 /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */
304 LogError("unable to Map_GetInternals");
305 result = false;
306 }
307 else
308 {
309 for (size_t index = 0; index < count; index++)
310 {
311 /*Codes_SRS_TRANSPORTMULTITHTTP_17_074: [Every property name shall add to the message size the length of the property name + the length of the property value + 16 bytes.] */
312 *msg_size += (strlen(values[index]) + strlen(keys[index]) + MAXIMUM_PROPERTY_OVERHEAD);
313 if (*msg_size > MAXIMUM_MESSAGE_SIZE)
314 {
315 /*Codes_SRS_TRANSPORTMULTITHTTP_17_072: [The message size shall be limited to 255KB -1 bytes.] */
316 PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
317 DList_InsertTailList(&(deviceData->eventConfirmations), head);
318 handleData->transport_callbacks.send_complete_cb(&(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR, deviceData->device_transport_ctx); // takes care of emptying the list too
319 result = false;
320 break;
321 }
322 else
323 {
324 STRING_HANDLE app_construct = STRING_construct_sprintf("%s%s", IOTHUB_APP_PREFIX, keys[index]);
325 if (app_construct == NULL)
326 {
327 /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */
328 LogError("Unable to construct app header");
329 result = false;
330 break;
331 }
332 else
333 {
334 if (HTTPHeaders_ReplaceHeaderNameValuePair(headers, STRING_c_str(app_construct), values[index]) != HTTP_HEADERS_OK)
335 {
336 /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */
337 LogError("Unable to add app properties to http header");
338 result = false;
339 break;
340 }
341 STRING_delete(app_construct);
342 }
343 }
344 }
345 }
346 return result;
347}
348
349static void destroy_messageHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
350{
351 STRING_delete(handleData->messageHTTPrelativePath);
352 handleData->messageHTTPrelativePath = NULL;
353}
354
355static bool create_messageHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId)
356{
357 bool result;
358 handleData->messageHTTPrelativePath = STRING_construct("/devices/");
359 if (handleData->messageHTTPrelativePath == NULL)
360 {
361 LogError("STRING_construct failed.");
362 result = false;
363 }
364 else
365 {
366 STRING_HANDLE urlEncodedDeviceId;
367 /*Codes_SRS_TRANSPORTMULTITHTTP_17_019: [ IoTHubTransportHttp_Register shall create an immutable string (further called "message HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(deviceId) + "/messages/devicebound" + APIVERSION. ]*/
368 if (!(
369 ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) &&
370 (STRING_concat_with_STRING(handleData->messageHTTPrelativePath, urlEncodedDeviceId) == 0) &&
371 (STRING_concat(handleData->messageHTTPrelativePath, MESSAGE_ENDPOINT_HTTP API_VERSION) == 0)
372 ))
373 {
374 /*Codes_SRS_TRANSPORTMULTITHTTP_17_020: [ If creating the message HTTP relative path fails, then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
375 LogError("Creating HTTP message relative path failed.");
376 result = false;
377 destroy_messageHTTPrelativePath(handleData);
378 }
379 else
380 {
381 result = true;
382 }
383 STRING_delete(urlEncodedDeviceId);
384 }
385
386 return result;
387}
388
389static void destroy_eventHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
390{
391 HTTPHeaders_Free(handleData->eventHTTPrequestHeaders);
392 handleData->eventHTTPrequestHeaders = NULL;
393}
394
395static HTTP_HEADERS_RESULT addUserAgentHeaderInfo(HTTPTRANSPORT_HANDLE_DATA* transport_data, HTTP_HEADERS_HANDLE eventHTTPrequestHeaders)
396{
397 HTTP_HEADERS_RESULT result;
398 const char* product_info = transport_data->transport_callbacks.prod_info_cb(transport_data->transport_ctx);
399 if (product_info == NULL)
400 {
401 result = HTTPHeaders_AddHeaderNameValuePair(eventHTTPrequestHeaders, "User-Agent", CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION);
402 }
403 else
404 {
405 result = HTTPHeaders_AddHeaderNameValuePair(eventHTTPrequestHeaders, "User-Agent", product_info);
406 }
407 return result;
408}
409
410static bool create_eventHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData, HTTPTRANSPORT_HANDLE_DATA* transport_data, const char * deviceId, bool is_x509_used)
411{
412 /*Codes_SRS_TRANSPORTMULTITHTTP_17_021: [ IoTHubTransportHttp_Register shall create a set of HTTP headers (further called "event HTTP request headers") consisting of the following fixed field names and values: "iothub-to":"/devices/" + URL_ENCODED(deviceId) + "/messages/events"; "Authorization":""
413 "Accept":"application/json"
414 "Connection":"Keep-Alive" ]*/
415 bool result;
416 handleData->eventHTTPrequestHeaders = HTTPHeaders_Alloc();
417 if (handleData->eventHTTPrequestHeaders == NULL)
418 {
419 LogError("HTTPHeaders_Alloc failed.");
420 result = false;
421 }
422 else
423 {
424 STRING_HANDLE temp = STRING_construct("/devices/");
425 if (temp == NULL)
426 {
427 LogError("STRING_construct failed.");
428 result = false;
429 destroy_eventHTTPrequestHeaders(handleData);
430 }
431 else
432 {
433 STRING_HANDLE urlEncodedDeviceId;
434 if (!(
435 ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) &&
436 (STRING_concat_with_STRING(temp, urlEncodedDeviceId) == 0) &&
437 (STRING_concat(temp, EVENT_ENDPOINT) == 0)
438 ))
439 {
440 LogError("deviceId construction failed.");
441 result = false;
442 destroy_eventHTTPrequestHeaders(handleData);
443 }
444 else
445 {
446 if (!(
447 (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "iothub-to", STRING_c_str(temp)) == HTTP_HEADERS_OK) &&
448 (is_x509_used || (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, IOTHUB_AUTH_HEADER_VALUE, " ") == HTTP_HEADERS_OK)) &&
449 (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Accept", "application/json") == HTTP_HEADERS_OK) &&
450 (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Connection", "Keep-Alive") == HTTP_HEADERS_OK) &&
451 (addUserAgentHeaderInfo(transport_data, handleData->eventHTTPrequestHeaders) == HTTP_HEADERS_OK)
452 ))
453 {
454 /*Codes_SRS_TRANSPORTMULTITHTTP_17_022: [ If creating the event HTTP request headers fails, then IoTHubTransportHttp_Register shall fail and return NULL.] */
455 LogError("adding header properties failed.");
456 result = false;
457 destroy_eventHTTPrequestHeaders(handleData);
458 }
459 else
460 {
461 result = true;
462 }
463 }
464 STRING_delete(urlEncodedDeviceId);
465 STRING_delete(temp);
466 }
467 }
468 return result;
469}
470
471static void destroy_messageHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
472{
473 HTTPHeaders_Free(handleData->messageHTTPrequestHeaders);
474 handleData->messageHTTPrequestHeaders = NULL;
475}
476
477static bool create_messageHTTPrequestHeaders(HTTPTRANSPORT_PERDEVICE_DATA* handleData, HTTPTRANSPORT_HANDLE_DATA* transport_data, bool is_x509_used)
478{
479 /*Codes_SRS_TRANSPORTMULTITHTTP_17_132: [ IoTHubTransportHttp_Register shall create a set of HTTP headers (further called "message HTTP request headers") consisting of the following fixed field names and values:
480 "Authorization": "" ]*/
481 bool result;
482 handleData->messageHTTPrequestHeaders = HTTPHeaders_Alloc();
483 if (handleData->messageHTTPrequestHeaders == NULL)
484 {
485 LogError("HTTPHeaders_Alloc failed.");
486 result = false;
487 }
488 else
489 {
490 if (!(
491 (addUserAgentHeaderInfo(transport_data, handleData->messageHTTPrequestHeaders) == HTTP_HEADERS_OK) &&
492 (is_x509_used || (HTTPHeaders_AddHeaderNameValuePair(handleData->messageHTTPrequestHeaders, IOTHUB_AUTH_HEADER_VALUE, " ") == HTTP_HEADERS_OK))
493 ))
494 {
495 /*Codes_SRS_TRANSPORTMULTITHTTP_17_023: [ If creating message HTTP request headers then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
496 destroy_messageHTTPrequestHeaders(handleData);
497 LogError("adding header properties failed.");
498 result = false;
499 }
500 else
501 {
502 result = true;
503 }
504 }
505 return result;
506}
507
508static void destroy_abandonHTTPrelativePathBegin(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
509{
510 STRING_delete(handleData->abandonHTTPrelativePathBegin);
511 handleData->abandonHTTPrelativePathBegin = NULL;
512}
513
514static bool create_abandonHTTPrelativePathBegin(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId)
515{
516 /*Codes_SRS_TRANSPORTMULTITHTTP_17_024: [ IoTHubTransportHttp_Register shall create a STRING containing: "/devices/" + URL_ENCODED(device id) +"/messages/deviceBound/" called abandonHTTPrelativePathBegin. ]*/
517 bool result;
518 handleData->abandonHTTPrelativePathBegin = STRING_construct("/devices/");
519 if (handleData->abandonHTTPrelativePathBegin == NULL)
520 {
521 result = false;
522 }
523 else
524 {
525 STRING_HANDLE urlEncodedDeviceId;
526 if (!(
527 ((urlEncodedDeviceId = URL_EncodeString(deviceId)) != NULL) &&
528 (STRING_concat_with_STRING(handleData->abandonHTTPrelativePathBegin, urlEncodedDeviceId) == 0) &&
529 (STRING_concat(handleData->abandonHTTPrelativePathBegin, MESSAGE_ENDPOINT_HTTP_ETAG) == 0)
530 ))
531 {
532 /*Codes_SRS_TRANSPORTMULTITHTTP_17_025: [ If creating the abandonHTTPrelativePathBegin fails then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
533 LogError("unable to create abandon path string.");
534 STRING_delete(handleData->abandonHTTPrelativePathBegin);
535 result = false;
536 }
537 else
538 {
539 result = true;
540 }
541 STRING_delete(urlEncodedDeviceId);
542 }
543 return result;
544}
545
546static void destroy_SASObject(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
547{
548 HTTPAPIEX_SAS_Destroy(handleData->sasObject);
549 handleData->sasObject = NULL;
550}
551
552static bool create_deviceSASObject(HTTPTRANSPORT_PERDEVICE_DATA* handleData, STRING_HANDLE hostName, const char * deviceId, const char * deviceKey)
553{
554 STRING_HANDLE keyName;
555 bool result;
556 /*Codes_SRS_TRANSPORTMULTITHTTP_17_026: [IoTHubTransportHttp_Create shall invoke URL_EncodeString with an argument of device id.]*/
557 keyName = URL_EncodeString(deviceId);
558 if (keyName == NULL)
559 {
560 /*Codes_SRS_TRANSPORTMULTITHTTP_17_027: [If the encode fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
561 LogError("URL_EncodeString keyname failed");
562 result = false;
563 }
564 else
565 {
566 STRING_HANDLE uriResource;
567 /*Codes_SRS_TRANSPORTMULTITHTTP_17_028: [IoTHubTransportHttp_Create shall invoke STRING_clone using the previously created hostname.]*/
568 uriResource = STRING_clone(hostName);
569 /*Codes_SRS_TRANSPORTMULTITHTTP_17_029: [If the clone fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
570 if (uriResource != NULL)
571 {
572 /*Codes_SRS_TRANSPORTMULTITHTTP_17_030: [IoTHubTransportHttp_Create shall invoke STRING_concat with arguments uriResource and the string "/devices/".]*/
573 /*Codes_SRS_TRANSPORTMULTITHTTP_17_141: [If the concat fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
574 /*Codes_SRS_TRANSPORTMULTITHTTP_17_031: [IoTHubTransportHttp_Create shall invoke STRING_concat_with_STRING with arguments uriResource and keyName.]*/
575 /*Codes_SRS_TRANSPORTMULTITHTTP_17_032: [If the STRING_concat_with_STRING fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
576 if ((STRING_concat(uriResource, "/devices/") == 0) &&
577 (STRING_concat_with_STRING(uriResource, keyName) == 0))
578 {
579 /*Codes_SRS_TRANSPORTMULTITHTTP_17_033: [IoTHubTransportHttp_Create shall invoke STRING_construct with an argument of config->upperConfig->deviceKey.]*/
580 /*Codes_SRS_TRANSPORTMULTITHTTP_17_034: [If the STRING_construct fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
581 STRING_HANDLE key = STRING_construct(deviceKey);
582 if (key != NULL)
583 {
584 /*Codes_SRS_TRANSPORTMULTITHTTP_17_035: [The keyName is shortened to zero length, if that fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
585 if (STRING_empty(keyName) != 0)
586 {
587 LogError("Unable to form the device key name for the SAS");
588 result = false;
589 }
590 else
591 {
592 /*Codes_SRS_TRANSPORTMULTITHTTP_17_036: [IoTHubTransportHttp_Create shall invoke HTTPAPIEX_SAS_Create with arguments key, uriResource, and zero length keyName.]*/
593 /*Codes_SRS_TRANSPORTMULTITHTTP_17_037: [If the HTTPAPIEX_SAS_Create fails then IoTHubTransportHttp_Create shall fail and return NULL.]*/
594 handleData->sasObject = HTTPAPIEX_SAS_Create(key, uriResource, keyName);
595 result = (handleData->sasObject != NULL) ? (true) : (false);
596 }
597 STRING_delete(key);
598 }
599 else
600 {
601 LogError("STRING_construct Key failed");
602 result = false;
603 }
604 }
605 else
606 {
607 LogError("STRING_concat uri resource failed");
608 result = false;
609 }
610 STRING_delete(uriResource);
611 }
612 else
613 {
614 LogError("STRING_staticclone uri resource failed");
615 result = false;
616 }
617 STRING_delete(keyName);
618 }
619 return result;
620}
621
622static void destroy_deviceId(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
623{
624 STRING_delete(handleData->deviceId);
625 handleData->deviceId = NULL;
626}
627
628static bool create_deviceId(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceId)
629{
630 /*Codes_SRS_TRANSPORTMULTITHTTP_17_133: [ IoTHubTransportHttp_Register shall create an immutable string (further called "deviceId") from config->deviceConfig->deviceId. ]*/
631 bool result;
632 handleData->deviceId = STRING_construct(deviceId);
633 if (handleData->deviceId == NULL)
634 {
635 /*Codes_SRS_TRANSPORTMULTITHTTP_17_134: [ If deviceId is not created, then IoTHubTransportHttp_Register shall fail and return NULL. */
636 LogError("STRING_construct deviceId failed");
637 result = false;
638 }
639 else
640 {
641 result = true;
642 }
643 return result;
644}
645
646static void destroy_deviceKey(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
647{
648 STRING_delete(handleData->deviceKey);
649 handleData->deviceKey = NULL;
650}
651
652static void destroy_deviceSas(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
653{
654 STRING_delete(handleData->deviceSasToken);
655 handleData->deviceSasToken = NULL;
656}
657
658static bool create_deviceKey(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceKey)
659{
660 /*Codes_SRS_TRANSPORTMULTITHTTP_17_135: [ IoTHubTransportHttp_Register shall create an immutable string (further called "deviceKey") from deviceKey. ]*/
661 bool result;
662 handleData->deviceKey = STRING_construct(deviceKey);
663 if (handleData->deviceKey == NULL)
664 {
665 /*Codes_SRS_TRANSPORTMULTITHTTP_17_136: [ If deviceKey is not created, then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
666 LogError("STRING_construct deviceKey failed");
667 result = false;
668 }
669 else
670 {
671 result = true;
672 }
673 return result;
674}
675
676static void destroy_deviceSasToken(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
677{
678 STRING_delete(handleData->deviceSasToken);
679 handleData->deviceSasToken = NULL;
680}
681
682static bool create_deviceSasToken(HTTPTRANSPORT_PERDEVICE_DATA* handleData, const char * deviceSasToken)
683{
684 /*Codes_SRS_TRANSPORTMULTITHTTP_03_135: [ IoTHubTransportHttp_Register shall create an immutable string (further called "deviceSasToken") from deviceSasToken. ]*/
685 bool result;
686 handleData->deviceSasToken = STRING_construct(deviceSasToken);
687 if (handleData->deviceSasToken == NULL)
688 {
689 /*Codes_SRS_TRANSPORTMULTITHTTP_03_136: [ If deviceSasToken is not created, then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
690 LogError("STRING_construct deviceSasToken failed");
691 result = false;
692 }
693 else
694 {
695 result = true;
696 }
697 return result;
698}
699
700/*
701* List queries Find by handle and find by device name
702*/
703
704/*Codes_SRS_TRANSPORTMULTITHTTP_17_137: [ IoTHubTransportHttp_Register shall search the devices list for any device matching name deviceId. If deviceId is found it shall return NULL. ]*/
705static bool findDeviceHandle(const void* element, const void* value)
706{
707 bool result;
708 /* data stored at element is device handle */
709 const IOTHUB_DEVICE_HANDLE * guess = (const IOTHUB_DEVICE_HANDLE *)element;
710 IOTHUB_DEVICE_HANDLE match = (IOTHUB_DEVICE_HANDLE)value;
711 result = (*guess == match) ? true : false;
712 return result;
713}
714
715static bool findDeviceById(const void* element, const void* value)
716{
717 bool result;
718 const char* deviceId = (const char *)value;
719 const HTTPTRANSPORT_PERDEVICE_DATA * perDeviceElement = *(const HTTPTRANSPORT_PERDEVICE_DATA **)element;
720
721 result = (strcmp(STRING_c_str(perDeviceElement->deviceId), deviceId) == 0);
722
723 return result;
724}
725
726static IOTHUB_DEVICE_HANDLE IoTHubTransportHttp_Register(TRANSPORT_LL_HANDLE handle, const IOTHUB_DEVICE_CONFIG* device, PDLIST_ENTRY waitingToSend)
727{
728 HTTPTRANSPORT_PERDEVICE_DATA* result;
729 if (handle == NULL || device == NULL)
730 {
731 /*Codes_SRS_TRANSPORTMULTITHTTP_17_142: [ If handle is NULL or device is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/
732 LogError("Transport handle is NULL");
733 result = NULL;
734 }
735 else if (device->deviceId == NULL || ((device->deviceKey != NULL) && (device->deviceSasToken != NULL)) || waitingToSend == NULL)
736 {
737 /*Codes_SRS_TRANSPORTMULTITHTTP_17_015: [ If IOTHUB_DEVICE_CONFIG fields deviceKey and deviceSasToken are NULL, then IoTHubTransportHttp_Register shall assume a x509 authentication. ]*/
738 /*Codes_SRS_TRANSPORTMULTITHTTP_17_014: [ If IOTHUB_DEVICE_CONFIG field deviceId is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/
739 /*Codes_SRS_TRANSPORTMULTITHTTP_17_016: [ If parameter waitingToSend is NULL, then IoTHubTransportHttp_Register shall return NULL. ]*/
740 LogError("invalid parameters detected TRANSPORT_LL_HANDLE handle=%p, const IOTHUB_DEVICE_CONFIG* device=%p, PDLIST_ENTRY waitingToSend=%p",
741 handle, device, waitingToSend);
742 result = NULL;
743 }
744 else
745 {
746 HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle;
747 /*Codes_SRS_TRANSPORTMULTITHTTP_17_137: [ IoTHubTransportHttp_Register shall search the devices list for any device matching name deviceId. If deviceId is found it shall return NULL. ]*/
748 void* listItem = VECTOR_find_if(handleData->perDeviceList, findDeviceById, device->deviceId);
749 if (listItem != NULL)
750 {
751 /*Codes_SRS_TRANSPORTMULTITHTTP_17_137: [ IoTHubTransportHttp_Register shall search the devices list for any device matching name deviceId. If deviceId is found it shall return NULL. ]*/
752 LogError("Transport already has device registered by id: [%s]", device->deviceId);
753 result = NULL;
754 }
755 else
756 {
757 bool was_create_deviceKey_ok = false;
758 bool was_create_deviceSasToken_ok = false;
759 bool was_sasObject_ok = false;
760 bool was_x509_ok = false; /*there's nothing "created" in the case of x509, it is a flag indicating that x509 is used*/
761
762 /*Codes_SRS_TRANSPORTMULTITHTTP_17_038: [ Otherwise, IoTHubTransportHttp_Register shall allocate the IOTHUB_DEVICE_HANDLE structure. ]*/
763 bool was_resultCreated_ok = ((result = (HTTPTRANSPORT_PERDEVICE_DATA *)malloc(sizeof(HTTPTRANSPORT_PERDEVICE_DATA))) != NULL);
764 bool was_create_deviceId_ok = was_resultCreated_ok && create_deviceId(result, device->deviceId);
765
766 if (was_create_deviceId_ok)
767 {
768 if (device->deviceSasToken != NULL)
769 {
770 /*device->deviceKey is certainly NULL (because of the validation condition (else if (device->deviceId == NULL || ((device->deviceKey != NULL) && (device->deviceSasToken != NULL)) || waitingToSend == NULL))that does not accept that both satoken AND devicekey are non-NULL*/
771 was_create_deviceSasToken_ok = create_deviceSasToken(result, device->deviceSasToken);
772 result->deviceKey = NULL;
773 result->sasObject = NULL;
774 }
775 else /*when deviceSasToken == NULL*/
776 {
777 if (device->deviceKey != NULL)
778 {
779 was_create_deviceKey_ok = create_deviceKey(result, device->deviceKey);
780 result->deviceSasToken = NULL;
781 }
782 else
783 {
784 /*when both of them are NULL*/
785 was_x509_ok = true;
786 result->deviceKey = NULL;
787 result->deviceSasToken = NULL;
788 }
789 }
790 }
791
792 bool was_eventHTTPrelativePath_ok = (was_create_deviceKey_ok || was_create_deviceSasToken_ok || was_x509_ok) && create_eventHTTPrelativePath(result, device->deviceId);
793 bool was_messageHTTPrelativePath_ok = was_eventHTTPrelativePath_ok && create_messageHTTPrelativePath(result, device->deviceId);
794 bool was_eventHTTPrequestHeaders_ok;
795 if (was_messageHTTPrelativePath_ok)
796 {
797 result->device_transport_ctx = handleData->transport_ctx;
798 was_eventHTTPrequestHeaders_ok = create_eventHTTPrequestHeaders(result, handleData, device->deviceId, was_x509_ok);
799 }
800 else
801 {
802 was_eventHTTPrequestHeaders_ok = false;
803 }
804 bool was_messageHTTPrequestHeaders_ok = was_eventHTTPrequestHeaders_ok && create_messageHTTPrequestHeaders(result, handleData, was_x509_ok);
805 bool was_abandonHTTPrelativePathBegin_ok = was_messageHTTPrequestHeaders_ok && create_abandonHTTPrelativePathBegin(result, device->deviceId);
806
807 if (was_x509_ok)
808 {
809 result->sasObject = NULL; /**/
810 was_sasObject_ok = true;
811 }
812 else
813 {
814 if (!was_create_deviceSasToken_ok)
815 {
816 was_sasObject_ok = was_abandonHTTPrelativePathBegin_ok && create_deviceSASObject(result, handleData->hostName, device->deviceId, device->deviceKey);
817 }
818 }
819
820 /*Codes_SRS_TRANSPORTMULTITHTTP_17_041: [ IoTHubTransportHttp_Register shall call VECTOR_push_back to store the new device information. ]*/
821 bool was_list_add_ok = (was_sasObject_ok || was_create_deviceSasToken_ok || was_x509_ok) && (VECTOR_push_back(handleData->perDeviceList, &result, 1) == 0);
822
823 if (was_list_add_ok)
824 {
825 /*Codes_SRS_TRANSPORTMULTITHTTP_17_043: [ Upon success, IoTHubTransportHttp_Register shall store the transport handle, and the waitingToSend queue in the device handle return a non-NULL value. ]*/
826 /*Codes_SRS_TRANSPORTMULTITHTTP_17_040: [ IoTHubTransportHttp_Register shall put event HTTP relative path, message HTTP relative path, event HTTP request headers, message HTTP request headers, abandonHTTPrelativePathBegin, HTTPAPIEX_SAS_HANDLE, and the device handle into a device structure. ]*/
827 /*Codes_SRS_TRANSPORTMULTITHTTP_17_128: [ IoTHubTransportHttp_Register shall mark this device as unsubscribed. ]*/
828 result->DoWork_PullMessage = false;
829 result->isFirstPoll = true;
830 result->waitingToSend = waitingToSend;
831 DList_InitializeListHead(&(result->eventConfirmations));
832 result->transportHandle = (HTTPTRANSPORT_HANDLE_DATA *)handle;
833 }
834 else
835 {
836 /*Codes_SRS_TRANSPORTMULTITHTTP_17_042: [ If the singlylinkedlist_add fails then IoTHubTransportHttp_Register shall fail and return NULL. ]*/
837 if (was_sasObject_ok) destroy_SASObject(result);
838 if (was_abandonHTTPrelativePathBegin_ok) destroy_abandonHTTPrelativePathBegin(result);
839 if (was_messageHTTPrelativePath_ok) destroy_messageHTTPrelativePath(result);
840 if (was_eventHTTPrequestHeaders_ok) destroy_eventHTTPrequestHeaders(result);
841 if (was_messageHTTPrequestHeaders_ok) destroy_messageHTTPrequestHeaders(result);
842 if (was_eventHTTPrelativePath_ok) destroy_eventHTTPrelativePath(result);
843 if (was_create_deviceId_ok) destroy_deviceId(result);
844 if (was_create_deviceKey_ok) destroy_deviceKey(result);
845 if (was_create_deviceSasToken_ok) destroy_deviceSasToken(result);
846 /*Codes_SRS_TRANSPORTMULTITHTTP_17_039: [ If the allocating the device handle fails then IoTHubTransportHttp_Register shall fail and return NULL. ] */
847 if (was_resultCreated_ok) free(result);
848 result = NULL;
849 }
850 }
851
852 }
853 return (IOTHUB_DEVICE_HANDLE)result;
854}
855
856
857static void destroy_perDeviceData(HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem)
858{
859 destroy_deviceId(perDeviceItem);
860 destroy_deviceKey(perDeviceItem);
861 destroy_deviceSas(perDeviceItem);
862 destroy_eventHTTPrelativePath(perDeviceItem);
863 destroy_messageHTTPrelativePath(perDeviceItem);
864 destroy_eventHTTPrequestHeaders(perDeviceItem);
865 destroy_messageHTTPrequestHeaders(perDeviceItem);
866 destroy_abandonHTTPrelativePathBegin(perDeviceItem);
867 destroy_SASObject(perDeviceItem);
868}
869
870static IOTHUB_DEVICE_HANDLE* get_perDeviceDataItem(IOTHUB_DEVICE_HANDLE deviceHandle)
871{
872 HTTPTRANSPORT_PERDEVICE_DATA* deviceHandleData = (HTTPTRANSPORT_PERDEVICE_DATA*)deviceHandle;
873 IOTHUB_DEVICE_HANDLE* listItem;
874
875 HTTPTRANSPORT_HANDLE_DATA* handleData = deviceHandleData->transportHandle;
876
877 listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_find_if(handleData->perDeviceList, findDeviceHandle, deviceHandle);
878 if (listItem == NULL)
879 {
880 LogError("device handle not found in transport device list");
881 listItem = NULL;
882 }
883 else
884 {
885 /* sucessfully found device in list. */
886 }
887
888 return listItem;
889}
890
891static void IoTHubTransportHttp_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle)
892{
893 if (deviceHandle == NULL)
894 {
895 /*Codes_SRS_TRANSPORTMULTITHTTP_17_044: [ If deviceHandle is NULL, then IoTHubTransportHttp_Unregister shall do nothing. ]*/
896 LogError("Unregister a NULL device handle");
897 }
898 else
899 {
900 HTTPTRANSPORT_PERDEVICE_DATA* deviceHandleData = (HTTPTRANSPORT_PERDEVICE_DATA*)deviceHandle;
901 HTTPTRANSPORT_HANDLE_DATA* handleData = deviceHandleData->transportHandle;
902 /*Codes_SRS_TRANSPORTMULTITHTTP_17_045: [ IoTHubTransportHttp_Unregister shall locate deviceHandle in the transport device list by calling list_find_if. ]*/
903 IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(deviceHandle);
904 if (listItem == NULL)
905 {
906 /*Codes_SRS_TRANSPORTMULTITHTTP_17_046: [ If the device structure is not found, then this function shall fail and do nothing. ]*/
907 LogError("Device Handle [%p] not found in transport", deviceHandle);
908 }
909 else
910 {
911 HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA *)(*listItem);
912
913 /*Codes_SRS_TRANSPORTMULTITHTTP_17_047: [ IoTHubTransportHttp_Unregister shall free all the resources used in the device structure. ]*/
914 destroy_perDeviceData(perDeviceItem);
915 /*Codes_SRS_TRANSPORTMULTITHTTP_17_048: [ IoTHubTransportHttp_Unregister shall call singlylinkedlist_remove to remove device from devices list. ]*/
916 VECTOR_erase(handleData->perDeviceList, listItem, 1);
917 free(deviceHandleData);
918 }
919 }
920
921 return;
922}
923
924/*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */
925/*Codes_SRS_TRANSPORTMULTITHTTP_20_001: [If config->upperConfig->protocolGatewayHostName is not NULL, IoTHubTransportHttp_Create shall use it as hostname] */
926static void destroy_hostName(HTTPTRANSPORT_HANDLE_DATA* handleData)
927{
928 STRING_delete(handleData->hostName);
929 handleData->hostName = NULL;
930}
931
932static bool create_hostName(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config)
933{
934 bool result;
935 if (config->upperConfig->protocolGatewayHostName != NULL)
936 {
937 /*Codes_SRS_TRANSPORTMULTITHTTP_20_001: [If config->upperConfig->protocolGatewayHostName is not NULL, IoTHubTransportHttp_Create shall use it as hostname] */
938 handleData->hostName = STRING_construct(config->upperConfig->protocolGatewayHostName);
939 result = (handleData->hostName != NULL);
940 }
941 else
942 {
943 /*Codes_SRS_TRANSPORTMULTITHTTP_17_005: [If config->upperConfig->protocolGatewayHostName is NULL, `IoTHubTransportHttp_Create` shall create an immutable string (further called hostname) containing `config->transportConfig->iotHubName + config->transportConfig->iotHubSuffix`.] */
944 handleData->hostName = STRING_construct(config->upperConfig->iotHubName);
945
946 if (handleData->hostName == NULL)
947 {
948 result = false;
949 }
950 else
951 {
952 if ((STRING_concat(handleData->hostName, ".") != 0) ||
953 (STRING_concat(handleData->hostName, config->upperConfig->iotHubSuffix) != 0))
954 {
955 /*Codes_SRS_TRANSPORTMULTITHTTP_17_006: [ If creating the hostname fails then IoTHubTransportHttp_Create shall fail and return NULL. ] */
956 destroy_hostName(handleData);
957 result = false;
958 }
959 else
960 {
961 result = true;
962 }
963 }
964 }
965 return result;
966}
967
968/*Codes_SRS_TRANSPORTMULTITHTTP_17_007: [Otherwise, IoTHubTransportHttp_Create shall create a HTTPAPIEX_HANDLE by a call to HTTPAPIEX_Create passing for hostName the hostname so far constructed by IoTHubTransportHttp_Create.]*/
969static void destroy_httpApiExHandle(HTTPTRANSPORT_HANDLE_DATA* handleData)
970{
971 HTTPAPIEX_Destroy(handleData->httpApiExHandle);
972 handleData->httpApiExHandle = NULL;
973}
974
975/*Codes_SRS_TRANSPORTMULTITHTTP_17_007: [ IoTHubTransportHttp_Create shall create a HTTPAPIEX_HANDLE by a call to HTTPAPIEX_Create passing for hostName the hostname so far constructed by IoTHubTransportHttp_Create. ]*/
976static bool create_httpApiExHandle(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config)
977{
978 bool result;
979 (void)config;
980 handleData->httpApiExHandle = HTTPAPIEX_Create(STRING_c_str(handleData->hostName));
981 if (handleData->httpApiExHandle == NULL)
982 {
983 /*Codes_SRS_TRANSPORTMULTITHTTP_17_008: [ If creating the HTTPAPIEX_HANDLE fails then IoTHubTransportHttp_Create shall fail and return NULL. ] */
984 result = false;
985 }
986 else
987 {
988 result = true;
989 }
990 return result;
991}
992
993static void destroy_perDeviceList(HTTPTRANSPORT_HANDLE_DATA* handleData)
994{
995 VECTOR_destroy(handleData->perDeviceList);
996 handleData->perDeviceList = NULL;
997}
998
999/*Codes_SRS_TRANSPORTMULTITHTTP_17_009: [ IoTHubTransportHttp_Create shall call singlylinkedlist_create to create a list of registered devices. ]*/
1000static bool create_perDeviceList(HTTPTRANSPORT_HANDLE_DATA* handleData)
1001{
1002 bool result;
1003 handleData->perDeviceList = VECTOR_create(sizeof(IOTHUB_DEVICE_HANDLE));
1004 if (handleData == NULL || handleData->perDeviceList == NULL)
1005 {
1006 /*Codes_SRS_TRANSPORTMULTITHTTP_17_010: [ If creating the list fails, then IoTHubTransportHttp_Create shall fail and return NULL. ]*/
1007 result = false;
1008 }
1009 else
1010 {
1011 result = true;
1012 }
1013 return result;
1014}
1015
1016static TRANSPORT_LL_HANDLE IoTHubTransportHttp_Create(const IOTHUBTRANSPORT_CONFIG* config, TRANSPORT_CALLBACKS_INFO* cb_info, void* ctx)
1017{
1018 HTTPTRANSPORT_HANDLE_DATA* result;
1019 if (config == NULL)
1020 {
1021 /*Codes_SRS_TRANSPORTMULTITHTTP_17_001: [If parameter config is NULL, then IoTHubTransportHttp_Create shall return NULL.]*/
1022 LogError("invalid arg (configuration is missing)");
1023 result = NULL;
1024 }
1025 else if (config->upperConfig == NULL)
1026 {
1027 /*Codes_SRS_TRANSPORTMULTITHTTP_17_002: [ If field transportConfig is NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/
1028 LogError("invalid arg (upperConfig is NULL)");
1029 result = NULL;
1030 }
1031 else if (config->upperConfig->protocol == NULL)
1032 {
1033 /*Codes_SRS_TRANSPORTMULTITHTTP_17_003: [ If fields protocol, iotHubName or iotHubSuffix in transportConfig are NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/
1034 LogError("invalid arg (protocol is NULL)");
1035 result = NULL;
1036 }
1037 else if (config->upperConfig->iotHubName == NULL)
1038 {
1039 /*Codes_SRS_TRANSPORTMULTITHTTP_17_003: [ If fields protocol, iotHubName or iotHubSuffix in transportConfig are NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/
1040 LogError("invalid arg (iotHubName is NULL)");
1041 result = NULL;
1042 }
1043 else if (config->upperConfig->iotHubSuffix == NULL)
1044 {
1045 /*Codes_SRS_TRANSPORTMULTITHTTP_17_003: [ If fields protocol, iotHubName or iotHubSuffix in transportConfig are NULL, then IoTHubTransportHttp_Create shall return NULL. ]*/
1046 LogError("invalid arg (iotHubSuffix is NULL)");
1047 result = NULL;
1048 }
1049 else if (IoTHub_Transport_ValidateCallbacks(cb_info) != 0)
1050 {
1051 LogError("Invalid transport callback information");
1052 result = NULL;
1053 }
1054 else
1055 {
1056 /*Codes_SRS_TRANSPORTMULTITHTTP_17_130: [ IoTHubTransportHttp_Create shall allocate memory for the handle. ]*/
1057 result = (HTTPTRANSPORT_HANDLE_DATA*)malloc(sizeof(HTTPTRANSPORT_HANDLE_DATA));
1058 if (result == NULL)
1059 {
1060 /*Codes_SRS_TRANSPORTMULTITHTTP_17_131: [ If allocation fails, IoTHubTransportHttp_Create shall fail and return NULL. ]*/
1061 LogError("unable to malloc");
1062 }
1063 else if (HTTPAPIEX_Init() == HTTPAPIEX_ERROR)
1064 {
1065 /*Codes_SRS_TRANSPORTMULTITHTTP_21_143: [ If HTTPAPIEX_Init fails, IoTHubTransportHttp_Create shall fail and return NULL. ]*/
1066 LogError("Error initializing HTTP");
1067 free(result);
1068 result = NULL;
1069 }
1070 else
1071 {
1072 bool was_hostName_ok = create_hostName(result, config);
1073 bool was_httpApiExHandle_ok = was_hostName_ok && create_httpApiExHandle(result, config);
1074 bool was_perDeviceList_ok = was_httpApiExHandle_ok && create_perDeviceList(result);
1075
1076 if (was_perDeviceList_ok)
1077 {
1078 /*Codes_SRS_TRANSPORTMULTITHTTP_17_011: [ Otherwise, IoTHubTransportHttp_Create shall succeed and return a non-NULL value. ]*/
1079 result->doBatchedTransfers = false;
1080 result->getMinimumPollingTime = DEFAULT_GETMINIMUMPOLLINGTIME;
1081
1082 result->transport_ctx = ctx;
1083 memcpy(&result->transport_callbacks, cb_info, sizeof(TRANSPORT_CALLBACKS_INFO));
1084 }
1085 else
1086 {
1087 if (was_httpApiExHandle_ok) destroy_httpApiExHandle(result);
1088 if (was_hostName_ok) destroy_hostName(result);
1089 HTTPAPIEX_Deinit();
1090
1091 free(result);
1092 result = NULL;
1093 }
1094 }
1095 }
1096 return result;
1097}
1098
1099static void IoTHubTransportHttp_Destroy(TRANSPORT_LL_HANDLE handle)
1100{
1101 /*Codes_SRS_TRANSPORTMULTITHTTP_17_012: [ IoTHubTransportHttp_Destroy shall do nothing is handle is NULL. ]*/
1102 if (handle != NULL)
1103 {
1104 HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle;
1105 IOTHUB_DEVICE_HANDLE* listItem;
1106
1107 size_t deviceListSize = VECTOR_size(handleData->perDeviceList);
1108
1109 /*Codes_SRS_TRANSPORTMULTITHTTP_17_013: [ Otherwise, IoTHubTransportHttp_Destroy shall free all the resources currently in use. ]*/
1110 for (size_t i = 0; i < deviceListSize; i++)
1111 {
1112 listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_element(handleData->perDeviceList, i);
1113 HTTPTRANSPORT_PERDEVICE_DATA* perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA*)(*listItem);
1114 destroy_perDeviceData(perDeviceItem);
1115 free(perDeviceItem);
1116 }
1117
1118 destroy_hostName((HTTPTRANSPORT_HANDLE_DATA *)handle);
1119 destroy_httpApiExHandle((HTTPTRANSPORT_HANDLE_DATA *)handle);
1120 destroy_perDeviceList((HTTPTRANSPORT_HANDLE_DATA *)handle);
1121 HTTPAPIEX_Deinit();
1122 free(handle);
1123 }
1124}
1125
1126static int IoTHubTransportHttp_Subscribe(IOTHUB_DEVICE_HANDLE handle)
1127{
1128 int result;
1129 if (handle == NULL)
1130 {
1131 /*Codes_SRS_TRANSPORTMULTITHTTP_17_103: [ If parameter deviceHandle is NULL then IoTHubTransportHttp_Subscribe shall fail and return a non-zero value. ]*/
1132 LogError("invalid arg passed to IoTHubTransportHttp_Subscribe");
1133 result = MU_FAILURE;
1134 }
1135 else
1136 {
1137 /*Codes_SRS_TRANSPORTMULTITHTTP_17_104: [ IoTHubTransportHttp_Subscribe shall locate deviceHandle in the transport device list by calling list_find_if. ]*/
1138 IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(handle);
1139
1140 if (listItem == NULL)
1141 {
1142 /*Codes_SRS_TRANSPORTMULTITHTTP_17_105: [ If the device structure is not found, then this function shall fail and return a non-zero value. ]*/
1143 LogError("did not find device in transport handle");
1144 result = MU_FAILURE;
1145 }
1146 else
1147 {
1148 HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem;
1149
1150 perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA *)(*listItem);
1151 /*Codes_SRS_TRANSPORTMULTITHTTP_17_106: [ Otherwise, IoTHubTransportHttp_Subscribe shall set the device so that subsequent calls to DoWork should execute HTTP requests. ]*/
1152 perDeviceItem->DoWork_PullMessage = true;
1153 }
1154 result = 0;
1155 }
1156 return result;
1157}
1158
1159static void IoTHubTransportHttp_Unsubscribe(IOTHUB_DEVICE_HANDLE handle)
1160{
1161 /*Codes_SRS_TRANSPORTMULTITHTTP_17_107: [ If parameter deviceHandle is NULL then IoTHubTransportHttp_Unsubscribe shall fail do nothing. ]*/
1162 if (handle != NULL)
1163 {
1164 /*Codes_SRS_TRANSPORTMULTITHTTP_17_108: [ IoTHubTransportHttp_Unsubscribe shall locate deviceHandle in the transport device list by calling list_find_if. ]*/
1165 IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(handle);
1166 /*Codes_SRS_TRANSPORTMULTITHTTP_17_109: [ If the device structure is not found, then this function shall fail and do nothing. ]*/
1167 if (listItem != NULL)
1168 {
1169 HTTPTRANSPORT_PERDEVICE_DATA * perDeviceItem = (HTTPTRANSPORT_PERDEVICE_DATA *)(*listItem);
1170 /*Codes_SRS_TRANSPORTMULTITHTTP_17_110: [ Otherwise, IoTHubTransportHttp_Subscribe shall set the device so that subsequent calls to DoWork shall not execute HTTP requests. ]*/
1171 perDeviceItem->DoWork_PullMessage = false;
1172 }
1173 else
1174 {
1175 LogError("Device not found to unsuscribe.");
1176 }
1177 }
1178 else
1179 {
1180 LogError("Null handle passed to Unsuscribe.");
1181 }
1182}
1183
1184static int IoTHubTransportHttp_Subscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle)
1185{
1186 /*Codes_SRS_TRANSPORTMULTITHTTP_02_003: [ IoTHubTransportHttp_Subscribe_DeviceTwin shall return a non-zero value. ]*/
1187 (void)handle;
1188 int result = MU_FAILURE;
1189 LogError("IoTHubTransportHttp_Subscribe_DeviceTwin Not supported");
1190 return result;
1191}
1192
1193static void IoTHubTransportHttp_Unsubscribe_DeviceTwin(IOTHUB_DEVICE_HANDLE handle)
1194{
1195 (void)handle;
1196 /*Codes_SRS_TRANSPORTMULTITHTTP_02_004: [ IoTHubTransportHttp_Unsubscribe_DeviceTwin shall return ]*/
1197 LogError("IoTHubTransportHttp_Unsubscribe_DeviceTwin Not supported");
1198}
1199
1200static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_GetTwinAsync(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_DEVICE_TWIN_CALLBACK completionCallback, void* callbackContext)
1201{
1202 (void)handle;
1203 (void)completionCallback;
1204 (void)callbackContext;
1205 LogError("Currently Not Supported.");
1206 // Codes_SRS_TRANSPORTMULTITHTTP_09_005: [ `IoTHubTransportHttp_GetTwinAsync` shall return IOTHUB_CLIENT_ERROR]
1207 return IOTHUB_CLIENT_ERROR;
1208}
1209
1210static int IoTHubTransportHttp_Subscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle)
1211{
1212 (void)handle;
1213 int result = MU_FAILURE;
1214 LogError("not implemented (yet)");
1215 return result;
1216}
1217
1218static void IoTHubTransportHttp_Unsubscribe_DeviceMethod(IOTHUB_DEVICE_HANDLE handle)
1219{
1220 (void)handle;
1221 LogError("not implemented (yet)");
1222}
1223
1224static int IoTHubTransportHttp_DeviceMethod_Response(IOTHUB_DEVICE_HANDLE handle, METHOD_HANDLE methodId, const unsigned char* response, size_t response_size, int status_response)
1225{
1226 (void)handle;
1227 (void)methodId;
1228 (void)response;
1229 (void)response_size;
1230 (void)status_response;
1231 LogError("not implemented (yet)");
1232 return MU_FAILURE;
1233}
1234
1235/*produces a representation of the properties, if they exist*/
1236/*if they do not exist, produces ""*/
1237static int concat_Properties(STRING_HANDLE existing, MAP_HANDLE map, size_t* propertiesMessageSizeContribution)
1238{
1239 int result;
1240 const char*const* keys;
1241 const char*const* values;
1242 size_t count;
1243 if (Map_GetInternals(map, &keys, &values, &count) != MAP_OK)
1244 {
1245 result = MU_FAILURE;
1246 LogError("error while Map_GetInternals");
1247 }
1248 else
1249 {
1250
1251 if (count == 0)
1252 {
1253 /*Codes_SRS_TRANSPORTMULTITHTTP_17_064: [If IoTHubMessage does not have properties, then "properties":{...} shall be missing from the payload*/
1254 /*no properties - do nothing with existing*/
1255 result = 0;
1256 *propertiesMessageSizeContribution = 0;
1257 }
1258 else
1259 {
1260 /*Codes_SRS_TRANSPORTMULTITHTTP_17_058: [If IoTHubMessage has properties, then they shall be serialized at the same level as "body" using the following pattern: "properties":{"iothub-app-name1":"value1","iothub-app-name2":"value2*/
1261 if (STRING_concat(existing, ",\"properties\":") != 0)
1262 {
1263 /*go ahead and return it*/
1264 result = MU_FAILURE;
1265 LogError("failed STRING_concat");
1266 }
1267 else if (appendMapToJSON(existing, keys, values, count) != 0)
1268 {
1269 result = MU_FAILURE;
1270 LogError("unable to append the properties");
1271 }
1272 else
1273 {
1274 /*all is fine*/
1275 size_t i;
1276 *propertiesMessageSizeContribution = 0;
1277 for (i = 0; i < count; i++)
1278 {
1279 /*Codes_SRS_TRANSPORTMULTITHTTP_17_063: [Every property name shall add to the message size the length of the property name + the length of the property value + 16 bytes.] */
1280 *propertiesMessageSizeContribution += (strlen(keys[i]) + strlen(values[i]) + MAXIMUM_PROPERTY_OVERHEAD);
1281 }
1282 result = 0;
1283 }
1284 }
1285 }
1286 return result;
1287}
1288
1289/*produces a JSON representation of the map : {"a": "value_of_a","b":"value_of_b"}*/
1290static int appendMapToJSON(STRING_HANDLE existing, const char* const* keys, const char* const* values, size_t count) /*under consideration: move to MAP module when it has more than 1 user*/
1291{
1292 int result;
1293 if (STRING_concat(existing, "{") != 0)
1294 {
1295 /*go on and return it*/
1296 LogError("STRING_construct failed");
1297 result = MU_FAILURE;
1298 }
1299 else
1300 {
1301 size_t i;
1302 for (i = 0; i < count; i++)
1303 {
1304 if (!(
1305 (STRING_concat(existing, (i == 0) ? "\"" IOTHUB_APP_PREFIX : ",\"" IOTHUB_APP_PREFIX) == 0) &&
1306 (STRING_concat(existing, keys[i]) == 0) &&
1307 (STRING_concat(existing, "\":\"") == 0) &&
1308 (STRING_concat(existing, values[i]) == 0) &&
1309 (STRING_concat(existing, "\"") == 0)
1310 ))
1311 {
1312 LogError("unable to STRING_concat");
1313 break;
1314 }
1315 }
1316
1317 if (i < count)
1318 {
1319 result = MU_FAILURE;
1320 /*error, let it go through*/
1321 }
1322 else if (STRING_concat(existing, "}") != 0)
1323 {
1324 LogError("unable to STRING_concat");
1325 result = MU_FAILURE;
1326 }
1327 else
1328 {
1329 /*all is fine*/
1330 result = 0;
1331 }
1332 }
1333 return result;
1334}
1335
1336/*makes the following string:{"body":"base64 encoding of the message content"[,"properties":{"a":"valueOfA"}]}*/
1337/*return NULL if there was a failure, or a non-NULL STRING_HANDLE that contains the intended data*/
1338static STRING_HANDLE make1EventJSONitem(PDLIST_ENTRY item, size_t *messageSizeContribution)
1339{
1340 STRING_HANDLE result; /*temp wants to contain :{"body":"base64 encoding of the message content"[,"properties":{"a":"valueOfA"}]}*/
1341 IOTHUB_MESSAGE_LIST* message = containingRecord(item, IOTHUB_MESSAGE_LIST, entry);
1342 IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(message->messageHandle);
1343
1344 switch (contentType)
1345 {
1346 case IOTHUBMESSAGE_BYTEARRAY:
1347 {
1348 result = STRING_construct("{\"body\":\"");
1349 if (result == NULL)
1350 {
1351 LogError("unable to STRING_construct");
1352 }
1353 else
1354 {
1355 const unsigned char* source;
1356 size_t size;
1357
1358 if (IoTHubMessage_GetByteArray(message->messageHandle, &source, &size) != IOTHUB_MESSAGE_OK)
1359 {
1360 LogError("unable to get the data for the message.");
1361 STRING_delete(result);
1362 result = NULL;
1363 }
1364 else
1365 {
1366 STRING_HANDLE encoded = Azure_Base64_Encode_Bytes(source, size);
1367 if (encoded == NULL)
1368 {
1369 LogError("unable to Azure_Base64_Encode_Bytes.");
1370 STRING_delete(result);
1371 result = NULL;
1372 }
1373 else
1374 {
1375 size_t propertiesSize = 0;
1376 if (!(
1377 (STRING_concat_with_STRING(result, encoded) == 0) &&
1378 (STRING_concat(result, "\"") == 0) && /*\" because closing value*/
1379 (concat_Properties(result, IoTHubMessage_Properties(message->messageHandle), &propertiesSize) == 0) &&
1380 (STRING_concat(result, "},") == 0) /*the last comma shall be replaced by a ']' by DaCr's suggestion (which is awesome enough to receive credits in the source code)*/
1381 ))
1382 {
1383 STRING_delete(result);
1384 result = NULL;
1385 LogError("unable to STRING_concat_with_STRING.");
1386 }
1387 else
1388 {
1389 /*all is fine... */
1390 /*Codes_SRS_TRANSPORTMULTITHTTP_17_062: [The message size is computed from the length of the payload + 384.] */
1391 *messageSizeContribution = size + MAXIMUM_PAYLOAD_OVERHEAD + propertiesSize;
1392 }
1393 STRING_delete(encoded);
1394 }
1395 }
1396 }
1397 break;
1398 }
1399 /*Codes_SRS_TRANSPORTMULTITHTTP_17_057: [If a messages to be send has type IOTHUBMESSAGE_STRING, then its serialization shall be {"body":"JSON encoding of the string", "base64Encoded":false}] */
1400 case IOTHUBMESSAGE_STRING:
1401 {
1402 result = STRING_construct("{\"body\":");
1403 if (result == NULL)
1404 {
1405 LogError("unable to STRING_construct");
1406 }
1407 else
1408 {
1409 const char* source = IoTHubMessage_GetString(message->messageHandle);
1410 if (source == NULL)
1411 {
1412 LogError("unable to IoTHubMessage_GetString");
1413 STRING_delete(result);
1414 result = NULL;
1415 }
1416 else
1417 {
1418 STRING_HANDLE asJson = STRING_new_JSON(source);
1419 if (asJson == NULL)
1420 {
1421 LogError("unable to STRING_new_JSON");
1422 STRING_delete(result);
1423 result = NULL;
1424 }
1425 else
1426 {
1427 size_t propertiesSize = 0;
1428 if (!(
1429 (STRING_concat_with_STRING(result, asJson) == 0) &&
1430 (STRING_concat(result, ",\"base64Encoded\":false") == 0) &&
1431 (concat_Properties(result, IoTHubMessage_Properties(message->messageHandle), &propertiesSize) == 0) &&
1432 (STRING_concat(result, "},") == 0) /*the last comma shall be replaced by a ']' by DaCr's suggestion (which is awesome enough to receive credits in the source code)*/
1433 ))
1434 {
1435 LogError("unable to STRING_concat_with_STRING");
1436 STRING_delete(result);
1437 result = NULL;
1438 }
1439 else
1440 {
1441 /*result has the intended content*/
1442 /*Codes_SRS_TRANSPORTMULTITHTTP_17_062: [The message size is computed from the length of the payload + 384.] */
1443 *messageSizeContribution = strlen(source) + MAXIMUM_PAYLOAD_OVERHEAD + propertiesSize;
1444 }
1445 STRING_delete(asJson);
1446 }
1447 }
1448 }
1449 break;
1450 }
1451 default:
1452 {
1453 LogError("an unknown message type was encountered (%d)", contentType);
1454 result = NULL; /*unknown message type*/
1455 break;
1456 }
1457 }
1458 return result;
1459}
1460
1461#define MAKE_PAYLOAD_RESULT_VALUES \
1462 MAKE_PAYLOAD_OK, /*returned when there is a payload to be later send by HTTP*/ \
1463 MAKE_PAYLOAD_NO_ITEMS, /*returned when there are no items to be send*/ \
1464 MAKE_PAYLOAD_ERROR, /*returned when there were errors*/ \
1465 MAKE_PAYLOAD_FIRST_ITEM_DOES_NOT_FIT /*returned when the first item doesn't fit*/
1466
1467MU_DEFINE_ENUM(MAKE_PAYLOAD_RESULT, MAKE_PAYLOAD_RESULT_VALUES);
1468
1469/*this function assembles several {"body":"base64 encoding of the message content"," base64Encoded": true} into 1 payload*/
1470/*Codes_SRS_TRANSPORTMULTITHTTP_17_056: [IoTHubTransportHttp_DoWork shall build the following string:[{"body":"base64 encoding of the message1 content"},{"body":"base64 encoding of the message2 content"}...]]*/
1471static MAKE_PAYLOAD_RESULT makePayload(HTTPTRANSPORT_PERDEVICE_DATA* deviceData, STRING_HANDLE* payload)
1472{
1473 MAKE_PAYLOAD_RESULT result;
1474 size_t allMessagesSize = 0;
1475 *payload = STRING_construct("[");
1476 if (*payload == NULL)
1477 {
1478 LogError("unable to STRING_construct");
1479 result = MAKE_PAYLOAD_ERROR;
1480 }
1481 else
1482 {
1483 bool isFirst = true;
1484 PDLIST_ENTRY actual;
1485 bool keepGoing = true; /*keepGoing gets sometimes to false from within the loop*/
1486 /*either all the items enter the list or only some*/
1487 result = MAKE_PAYLOAD_OK; /*optimistically initializing it*/
1488 while (keepGoing && ((actual = deviceData->waitingToSend->Flink) != deviceData->waitingToSend))
1489 {
1490 size_t messageSize;
1491 STRING_HANDLE temp = make1EventJSONitem(actual, &messageSize);
1492 if (isFirst)
1493 {
1494 isFirst = false;
1495 /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/
1496 if (temp == NULL) /*first item failed to create, nothing to send*/
1497 {
1498 result = MAKE_PAYLOAD_ERROR;
1499 STRING_delete(*payload);
1500 *payload = NULL;
1501 keepGoing = false;
1502 }
1503 else
1504 {
1505 /*Codes_SRS_TRANSPORTMULTITHTTP_17_065: [If the oldest message in waitingToSend causes the message size to exceed the message size limit then it shall be removed from waitingToSend, and IoTHubClientCore_LL_SendComplete shall be called. Parameter PDLIST_ENTRY completed shall point to a list containing only the oldest item, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_BATCHSTATE_FAILED.]*/
1506 /*Codes_SRS_TRANSPORTMULTITHTTP_17_061: [The message size shall be limited to 255KB - 1 byte.]*/
1507 if (messageSize > MAXIMUM_MESSAGE_SIZE)
1508 {
1509 PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
1510 DList_InsertTailList(&(deviceData->eventConfirmations), head);
1511 result = MAKE_PAYLOAD_FIRST_ITEM_DOES_NOT_FIT;
1512 STRING_delete(*payload);
1513 *payload = NULL;
1514 keepGoing = false;
1515 }
1516 else
1517 {
1518 if (STRING_concat_with_STRING(*payload, temp) != 0)
1519 {
1520 /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/
1521 result = MAKE_PAYLOAD_ERROR;
1522 STRING_delete(*payload);
1523 *payload = NULL;
1524 keepGoing = false;
1525 }
1526 else
1527 {
1528 /*first item was put nicely in the payload*/
1529 PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
1530 DList_InsertTailList(&(deviceData->eventConfirmations), head);
1531 allMessagesSize += messageSize;
1532 }
1533 }
1534 STRING_delete(temp);
1535 }
1536 }
1537 else
1538 {
1539 /*there is at least 1 item already in the payload*/
1540 if (temp == NULL)
1541 {
1542 /*there are multiple payloads encoded, the last one had an internal error, just go with those - closing the payload happens "after the loop"*/
1543 /*Codes_SRS_TRANSPORTMULTITHTTP_17_066: [If at any point during construction of the string there are errors, IoTHubTransportHttp_DoWork shall use the so far constructed string as payload.]*/
1544 result = MAKE_PAYLOAD_OK;
1545 keepGoing = false;
1546 }
1547 else
1548 {
1549 if (allMessagesSize + messageSize > MAXIMUM_MESSAGE_SIZE)
1550 {
1551 /*this item doesn't make it to the payload, but the payload is valid so far*/
1552 /*Codes_SRS_TRANSPORTMULTITHTTP_17_066: [If at any point during construction of the string there are errors, IoTHubTransportHttp_DoWork shall use the so far constructed string as payload.]*/
1553 result = MAKE_PAYLOAD_OK;
1554 keepGoing = false;
1555 }
1556 else if (STRING_concat_with_STRING(*payload, temp) != 0)
1557 {
1558 /*should still send what there is so far...*/
1559 /*Codes_SRS_TRANSPORTMULTITHTTP_17_066: [If at any point during construction of the string there are errors, IoTHubTransportHttp_DoWork shall use the so far constructed string as payload.]*/
1560 result = MAKE_PAYLOAD_OK;
1561 keepGoing = false;
1562 }
1563 else
1564 {
1565 /*cool, the payload made it there, let's continue... */
1566 PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
1567 DList_InsertTailList(&(deviceData->eventConfirmations), head);
1568 allMessagesSize += messageSize;
1569 }
1570 STRING_delete(temp);
1571 }
1572 }
1573 }
1574
1575 /*closing the payload*/
1576 if (result == MAKE_PAYLOAD_OK)
1577 {
1578 ((char*)STRING_c_str(*payload))[STRING_length(*payload) - 1] = ']'; /*TODO - do this in STRING_HANDLE*/
1579 }
1580 else
1581 {
1582 /*no need to close anything*/
1583 }
1584 }
1585 return result;
1586}
1587
1588static void reversePutListBackIn(PDLIST_ENTRY source, PDLIST_ENTRY destination)
1589{
1590 /*this function takes a list, and inserts it in another list. When done in the context of this file, it reverses the effects of a not-able-to-send situation*/
1591 DList_AppendTailList(destination->Flink, source);
1592 DList_RemoveEntryList(source);
1593 DList_InitializeListHead(source);
1594}
1595
1596static void DoEvent(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData)
1597{
1598
1599 if (DList_IsListEmpty(deviceData->waitingToSend))
1600 {
1601 /*Codes_SRS_TRANSPORTMULTITHTTP_17_060: [If the list is empty then IoTHubTransportHttp_DoWork shall proceed to the following action.] */
1602 }
1603 else
1604 {
1605 /*Codes_SRS_TRANSPORTMULTITHTTP_17_053: [If option SetBatching is true then _Dowork shall send batched event message as specced below.] */
1606 if (handleData->doBatchedTransfers)
1607 {
1608 /*Codes_SRS_TRANSPORTMULTITHTTP_17_054: [Request HTTP headers shall have the value of "Content-Type" created or updated to "application/vnd.microsoft.iothub.json" by a call to HTTPHeaders_ReplaceHeaderNameValuePair.] */
1609 if (HTTPHeaders_ReplaceHeaderNameValuePair(deviceData->eventHTTPrequestHeaders, CONTENT_TYPE, APPLICATION_VND_MICROSOFT_IOTHUB_JSON) != HTTP_HEADERS_OK)
1610 {
1611 /*Codes_SRS_TRANSPORTMULTITHTTP_17_055: [If updating Content-Type fails for any reason, then _DoWork shall advance to the next action.] */
1612 LogError("unable to HTTPHeaders_ReplaceHeaderNameValuePair");
1613 }
1614 else
1615 {
1616 /*Codes_SRS_TRANSPORTMULTITHTTP_17_059: [It shall inspect the "waitingToSend" DLIST passed in config structure.] */
1617 STRING_HANDLE payload;
1618 switch (makePayload(deviceData, &payload))
1619 {
1620 case MAKE_PAYLOAD_OK:
1621 {
1622 /*Codes_SRS_TRANSPORTMULTITHTTP_17_068: [Once a final payload has been obtained, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest passing the following parameters:] */
1623 BUFFER_HANDLE temp = BUFFER_new();
1624 if (temp == NULL)
1625 {
1626 LogError("unable to BUFFER_new");
1627 /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/
1628 reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend);
1629 }
1630 else
1631 {
1632 if (BUFFER_build(temp, (const unsigned char*)STRING_c_str(payload), STRING_length(payload)) != 0)
1633 {
1634 LogError("unable to BUFFER_build");
1635 //items go back to waitingToSend
1636 /*Codes_SRS_TRANSPORTMULTITHTTP_17_067: [If there is no valid payload, IoTHubTransportHttp_DoWork shall advance to the next activity.]*/
1637 reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend);
1638 }
1639 else
1640 {
1641 unsigned int statusCode;
1642 if (HTTPAPIEX_SAS_ExecuteRequest(
1643 deviceData->sasObject,
1644 handleData->httpApiExHandle,
1645 HTTPAPI_REQUEST_POST,
1646 STRING_c_str(deviceData->eventHTTPrelativePath),
1647 deviceData->eventHTTPrequestHeaders,
1648 temp,
1649 &statusCode,
1650 NULL,
1651 NULL
1652 ) != HTTPAPIEX_OK)
1653 {
1654 LogError("unable to HTTPAPIEX_ExecuteRequest");
1655 //items go back to waitingToSend
1656 /*Codes_SRS_TRANSPORTMULTITHTTP_17_069: [if HTTPAPIEX_SAS_ExecuteRequest fails or the http status code >=300 then IoTHubTransportHttp_DoWork shall not do any other action (it is assumed at the next _DoWork it shall be retried).] */
1657 reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend);
1658 }
1659 else
1660 {
1661 if (statusCode < 300)
1662 {
1663 /*Codes_SRS_TRANSPORTMULTITHTTP_17_070: [If HTTPAPIEX_SAS_ExecuteRequest does not fail and http status code <300 then IoTHubTransportHttp_DoWork shall call IoTHubClientCore_LL_SendComplete. Parameter PDLIST_ENTRY completed shall point to a list containing all the items batched, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_OK. The batched items shall be removed from waitingToSend.] */
1664 handleData->transport_callbacks.send_complete_cb(&(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_OK, deviceData->device_transport_ctx);
1665 }
1666 else
1667 {
1668 //items go back to waitingToSend
1669 /*Codes_SRS_TRANSPORTMULTITHTTP_17_069: [if HTTPAPIEX_SAS_ExecuteRequest fails or the http status code >=300 then IoTHubTransportHttp_DoWork shall not do any other action (it is assumed at the next _DoWork it shall be retried).] */
1670 LogError("unexpected HTTP status code (%u)", statusCode);
1671 reversePutListBackIn(&(deviceData->eventConfirmations), deviceData->waitingToSend);
1672 }
1673 }
1674 }
1675 BUFFER_delete(temp);
1676 }
1677 STRING_delete(payload);
1678 break;
1679 }
1680 case MAKE_PAYLOAD_FIRST_ITEM_DOES_NOT_FIT:
1681 {
1682 handleData->transport_callbacks.send_complete_cb(&(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR, deviceData->device_transport_ctx); // takes care of emptying the list too
1683 break;
1684 }
1685 case MAKE_PAYLOAD_ERROR:
1686 {
1687 LogError("unrecoverable errors while building a batch message");
1688 break;
1689 }
1690 case MAKE_PAYLOAD_NO_ITEMS:
1691 {
1692 /*do nothing*/
1693 break;
1694 }
1695 default:
1696 {
1697 LogError("internal error: switch's default branch reached when never intended");
1698 break;
1699 }
1700 }
1701 }
1702 }
1703 else
1704 {
1705 const unsigned char* messageContent = NULL;
1706 size_t messageSize = 0;
1707 size_t originalMessageSize = 0;
1708 IOTHUB_MESSAGE_LIST* message = containingRecord(deviceData->waitingToSend->Flink, IOTHUB_MESSAGE_LIST, entry);
1709 IOTHUBMESSAGE_CONTENT_TYPE contentType = IoTHubMessage_GetContentType(message->messageHandle);
1710
1711 /*Codes_SRS_TRANSPORTMULTITHTTP_17_073: [The message size is computed from the length of the payload + 384.]*/
1712 if (!(
1713 (((contentType == IOTHUBMESSAGE_BYTEARRAY) &&
1714 (IoTHubMessage_GetByteArray(message->messageHandle, &messageContent, &originalMessageSize) == IOTHUB_MESSAGE_OK))
1715 ? ((void)(messageSize = originalMessageSize + MAXIMUM_PAYLOAD_OVERHEAD), 1)
1716 : 0)
1717
1718 ||
1719
1720 ((contentType == IOTHUBMESSAGE_STRING) &&
1721 ((void)(messageContent = (const unsigned char*)IoTHubMessage_GetString(message->messageHandle)),
1722 ((void)(messageSize = MAXIMUM_PAYLOAD_OVERHEAD + (originalMessageSize = ((messageContent == NULL)
1723 ? 0
1724 : strlen((const char*)messageContent))))),
1725 messageContent != NULL)
1726 )
1727 ))
1728 {
1729 LogError("unable to get the message content");
1730 /*go on...*/
1731 }
1732 else
1733 {
1734 /*Codes_SRS_TRANSPORTMULTITHTTP_17_075: [If the oldest message in waitingToSend causes the message to exceed the message size limit then it shall be removed from waitingToSend, and IoTHubClientCore_LL_SendComplete shall be called. Parameter PDLIST_ENTRY completed shall point to a list containing only the oldest item, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_BATCHSTATE_FAILED.]*/
1735 /*Codes_SRS_TRANSPORTMULTITHTTP_17_072: [The message size shall be limited to 255KB -1 bytes.] */
1736 if (messageSize > MAXIMUM_MESSAGE_SIZE)
1737 {
1738 PDLIST_ENTRY head = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
1739 DList_InsertTailList(&(deviceData->eventConfirmations), head);
1740 handleData->transport_callbacks.send_complete_cb(&(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR, deviceData->device_transport_ctx); // takes care of emptying the list too
1741 }
1742 else
1743 {
1744 /*Codes_SRS_TRANSPORTMULTITHTTP_17_071: [If option SetBatching is false then _Dowork shall send individual event message as specced below.] */
1745 /*Codes_SRS_TRANSPORTMULTITHTTP_17_076: [A clone of the event HTTP request headers shall be created.]*/
1746 HTTP_HEADERS_HANDLE clonedEventHTTPrequestHeaders = HTTPHeaders_Clone(deviceData->eventHTTPrequestHeaders);
1747 if (clonedEventHTTPrequestHeaders == NULL)
1748 {
1749 /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */
1750 LogError("HTTPHeaders_Clone failed");
1751 }
1752 else
1753 {
1754 /*Codes_SRS_TRANSPORTMULTITHTTP_17_077: [The cloned HTTP headers shall have the HTTP header "Content-Type" set to "application/octet-stream".] */
1755 if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, CONTENT_TYPE, APPLICATION_OCTET_STREAM) != HTTP_HEADERS_OK)
1756 {
1757 /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */
1758 LogError("HTTPHeaders_ReplaceHeaderNameValuePair failed");
1759 }
1760 else
1761 {
1762 // set_message_properties returning false does not necessarily mean the the function failed, it just means
1763 // the the adding of messages should not continue and should try the next time. So you should not log if this
1764 // returns false
1765 if (set_message_properties(message, &messageSize, clonedEventHTTPrequestHeaders, handleData, deviceData))
1766 {
1767
1768 if (set_system_properties(message, clonedEventHTTPrequestHeaders) != 0)
1769 {
1770 /*Codes_SRS_TRANSPORTMULTITHTTP_17_079: [If any HTTP header operation fails, _DoWork shall advance to the next action.] */
1771 }
1772 else
1773 {
1774 BUFFER_HANDLE toBeSend = BUFFER_create(messageContent, originalMessageSize);
1775 if (toBeSend == NULL)
1776 {
1777 LogError("unable to BUFFER_new");
1778 }
1779 else
1780 {
1781 unsigned int statusCode = 0;
1782 HTTPAPIEX_RESULT r;
1783 if (deviceData->deviceSasToken != NULL)
1784 {
1785 /*Codes_SRS_TRANSPORTMULTITHTTP_03_001: [if a deviceSasToken exists, HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (deviceSasToken) as its third argument.]*/
1786 if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_AUTH_HEADER_VALUE, STRING_c_str(deviceData->deviceSasToken)) != HTTP_HEADERS_OK)
1787 {
1788 r = HTTPAPIEX_ERROR;
1789 /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/
1790 LogError("Unable to replace the old SAS Token.");
1791 }
1792 /*Codes_SRS_TRANSPORTMULTITHTTP_03_003: [If a deviceSasToken exists, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_ExecuteRequest passing the following parameters] */
1793 else if ((r = HTTPAPIEX_ExecuteRequest(
1794 handleData->httpApiExHandle, HTTPAPI_REQUEST_POST, STRING_c_str(deviceData->eventHTTPrelativePath),
1795 clonedEventHTTPrequestHeaders, toBeSend, &statusCode, NULL, NULL)) != HTTPAPIEX_OK)
1796 {
1797 LogError("Unable to HTTPAPIEX_ExecuteRequest.");
1798 }
1799 }
1800 else
1801 {
1802 /*Codes_SRS_TRANSPORTMULTITHTTP_17_080: [If a deviceSasToken does not exist, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest passing the following parameters] */
1803 if ((r = HTTPAPIEX_SAS_ExecuteRequest(deviceData->sasObject, handleData->httpApiExHandle, HTTPAPI_REQUEST_POST, STRING_c_str(deviceData->eventHTTPrelativePath),
1804 clonedEventHTTPrequestHeaders, toBeSend, &statusCode, NULL, NULL )) != HTTPAPIEX_OK)
1805 {
1806 LogError("unable to HTTPAPIEX_SAS_ExecuteRequest");
1807 }
1808 }
1809 if (r == HTTPAPIEX_OK)
1810 {
1811 if (statusCode < 300)
1812 {
1813 /*Codes_SRS_TRANSPORTMULTITHTTP_17_082: [If HTTPAPIEX_SAS_ExecuteRequest does not fail and http status code <300 then IoTHubTransportHttp_DoWork shall call IoTHubClientCore_LL_SendComplete. Parameter PDLIST_ENTRY completed shall point to a list the item send, and parameter IOTHUB_CLIENT_CONFIRMATION_RESULT result shall be set to IOTHUB_CLIENT_CONFIRMATION_OK. The item shall be removed from waitingToSend.] */
1814 PDLIST_ENTRY justSent = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
1815 DList_InsertTailList(&(deviceData->eventConfirmations), justSent);
1816 handleData->transport_callbacks.send_complete_cb(&(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_OK, deviceData->device_transport_ctx); // takes care of emptying the list too
1817 }
1818 else
1819 {
1820 /*Codes_SRS_TRANSPORTMULTITHTTP_17_081: [If HTTPAPIEX_SAS_ExecuteRequest fails or the http status code >=300 then IoTHubTransportHttp_DoWork shall not do any other action (it is assumed at the next _DoWork it shall be retried).] */
1821 LogError("unexpected HTTP status code (%u)", statusCode);
1822 }
1823 }
1824 else if (r == HTTPAPIEX_RECOVERYFAILED)
1825 {
1826 PDLIST_ENTRY justSent = DList_RemoveHeadList(deviceData->waitingToSend); /*actually this is the same as "actual", but now it is removed*/
1827 DList_InsertTailList(&(deviceData->eventConfirmations), justSent);
1828 handleData->transport_callbacks.send_complete_cb(&(deviceData->eventConfirmations), IOTHUB_CLIENT_CONFIRMATION_ERROR, deviceData->device_transport_ctx); // takes care of emptying the list too
1829 }
1830 }
1831 BUFFER_delete(toBeSend);
1832 }
1833 }
1834 }
1835 HTTPHeaders_Free(clonedEventHTTPrequestHeaders);
1836 }
1837 }
1838 }
1839 }
1840 }
1841}
1842
1843static bool abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* ETag, IOTHUBMESSAGE_DISPOSITION_RESULT action)
1844{
1845 /*Codes_SRS_TRANSPORTMULTITHTTP_17_097: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters:
1846 -requestType: POST
1847 -relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14"
1848 - requestHttpHeadersHandle: an HTTP headers instance containing the following
1849 Authorization: " "
1850 If-Match: value of ETag
1851 - requestContent: NULL
1852 - statusCode: a pointer to unsigned int which might be examined for logging
1853 - responseHeadearsHandle: NULL
1854 - responseContent: NULL]*/
1855 /*Codes_SRS_TRANSPORTMULTITHTTP_17_099: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters:
1856 -requestType: DELETE
1857 -relativePath: abandon relative path begin + value of ETag + "?api-version=2016-11-14"
1858 - requestHttpHeadersHandle: an HTTP headers instance containing the following
1859 Authorization: " "
1860 If-Match: value of ETag
1861 - requestContent: NULL
1862 - statusCode: a pointer to unsigned int which might be used by logging
1863 - responseHeadearsHandle: NULL
1864 - responseContent: NULL]*/
1865 /*Codes_SRS_TRANSPORTMULTITHTTP_17_101: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters:
1866 -requestType: DELETE
1867 -relativePath: abandon relative path begin + value of ETag +"?api-version=2016-11-14" + "&reject"
1868 - requestHttpHeadersHandle: an HTTP headers instance containing the following
1869 Authorization: " "
1870 If-Match: value of ETag
1871 - requestContent: NULL
1872 - statusCode: a pointer to unsigned int which might be used by logging
1873 - responseHeadearsHandle: NULL
1874 - responseContent: NULL]*/
1875
1876 bool result;
1877 STRING_HANDLE fullAbandonRelativePath = STRING_clone(deviceData->abandonHTTPrelativePathBegin);
1878 if (fullAbandonRelativePath == NULL)
1879 {
1880 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1881 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1882 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1883 LogError("unable to STRING_clone");
1884 result = false;
1885 }
1886 else
1887 {
1888 STRING_HANDLE ETagUnquoted = STRING_construct_n(ETag + 1, strlen(ETag) - 2); /*skip first character which is '"' and the last one (which is also '"')*/
1889 if (ETagUnquoted == NULL)
1890 {
1891 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1892 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1893 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1894 LogError("unable to STRING_construct_n");
1895 result = false;
1896 }
1897 else
1898 {
1899 if (!(
1900 (STRING_concat_with_STRING(fullAbandonRelativePath, ETagUnquoted) == 0) &&
1901 (STRING_concat(fullAbandonRelativePath, (action == IOTHUBMESSAGE_ABANDONED) ? "/abandon" API_VERSION : ((action == IOTHUBMESSAGE_REJECTED) ? API_VERSION "&reject" : API_VERSION)) == 0)
1902 ))
1903 {
1904 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1905 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1906 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1907 LogError("unable to STRING_concat");
1908 result = false;
1909 }
1910 else
1911 {
1912 HTTP_HEADERS_HANDLE abandonRequestHttpHeaders = HTTPHeaders_Alloc();
1913 if (abandonRequestHttpHeaders == NULL)
1914 {
1915 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1916 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1917 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1918 LogError("unable to HTTPHeaders_Alloc");
1919 result = false;
1920 }
1921 else
1922 {
1923 if (!(
1924 (addUserAgentHeaderInfo(handleData, abandonRequestHttpHeaders) == HTTP_HEADERS_OK) &&
1925 (HTTPHeaders_AddHeaderNameValuePair(abandonRequestHttpHeaders, IOTHUB_AUTH_HEADER_VALUE, " ") == HTTP_HEADERS_OK) &&
1926 (HTTPHeaders_AddHeaderNameValuePair(abandonRequestHttpHeaders, "If-Match", ETag) == HTTP_HEADERS_OK)
1927 ))
1928 {
1929 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1930 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1931 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1932 LogError("unable to HTTPHeaders_AddHeaderNameValuePair");
1933 result = false;
1934 }
1935 else
1936 {
1937 unsigned int statusCode = 0;
1938 HTTPAPIEX_RESULT r;
1939 if (deviceData->deviceSasToken != NULL)
1940 {
1941 /*Codes_SRS_TRANSPORTMULTITHTTP_03_001: [if a deviceSasToken exists, HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (deviceSasToken) as its third argument.]*/
1942 if (HTTPHeaders_ReplaceHeaderNameValuePair(abandonRequestHttpHeaders, IOTHUB_AUTH_HEADER_VALUE, STRING_c_str(deviceData->deviceSasToken)) != HTTP_HEADERS_OK)
1943 {
1944 r = HTTPAPIEX_ERROR;
1945 /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/
1946 LogError("Unable to replace the old SAS Token.");
1947 result = false;
1948 }
1949 else if ((r = HTTPAPIEX_ExecuteRequest(
1950 handleData->httpApiExHandle,
1951 (action == IOTHUBMESSAGE_ABANDONED) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE, /*-requestType: POST */
1952 STRING_c_str(fullAbandonRelativePath), /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14" */
1953 abandonRequestHttpHeaders, /*- requestHttpHeadersHandle: an HTTP headers instance containing the following */
1954 NULL, /*- requestContent: NULL */
1955 &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */
1956 NULL, /*- responseHeadearsHandle: NULL */
1957 NULL /*- responseContent: NULL] */
1958 )) != HTTPAPIEX_OK)
1959 {
1960 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1961 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1962 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1963 LogError("Unable to HTTPAPIEX_ExecuteRequest.");
1964 result = false;
1965 }
1966 }
1967 else if ((r = HTTPAPIEX_SAS_ExecuteRequest(
1968 deviceData->sasObject,
1969 handleData->httpApiExHandle,
1970 (action == IOTHUBMESSAGE_ABANDONED) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE, /*-requestType: POST */
1971 STRING_c_str(fullAbandonRelativePath), /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14" */
1972 abandonRequestHttpHeaders, /*- requestHttpHeadersHandle: an HTTP headers instance containing the following */
1973 NULL, /*- requestContent: NULL */
1974 &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */
1975 NULL, /*- responseHeadearsHandle: NULL */
1976 NULL /*- responseContent: NULL] */
1977 )) != HTTPAPIEX_OK)
1978 {
1979 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1980 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1981 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1982 LogError("unable to HTTPAPIEX_SAS_ExecuteRequest");
1983 result = false;
1984 }
1985 if (r == HTTPAPIEX_OK)
1986 {
1987 if (statusCode != 204)
1988 {
1989 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1990 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1991 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1992 LogError("unexpected status code returned %u (was expecting 204)", statusCode);
1993 result = false;
1994 }
1995 else
1996 {
1997 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
1998 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
1999 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
2000 /*all is fine*/
2001 result = true;
2002 }
2003 }
2004 else
2005 {
2006 result = false;
2007 }
2008 }
2009 HTTPHeaders_Free(abandonRequestHttpHeaders);
2010 }
2011 }
2012 STRING_delete(ETagUnquoted);
2013 }
2014 STRING_delete(fullAbandonRelativePath);
2015 }
2016 return result;
2017}
2018
2019static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition)
2020{
2021 IOTHUB_CLIENT_RESULT result;
2022 if (message_data == NULL)
2023 {
2024 /* Codes_SRS_TRANSPORTMULTITHTTP_10_001: [If messageData is NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
2025 LogError("invalid argument messageData is NULL");
2026 result = IOTHUB_CLIENT_ERROR;
2027 }
2028 else
2029 {
2030 if (message_data->messageHandle == NULL)
2031 {
2032 /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
2033 LogError("invalid message handle");
2034 result = IOTHUB_CLIENT_ERROR;
2035 }
2036 else
2037 {
2038 MESSAGE_DISPOSITION_CONTEXT* tc = (MESSAGE_DISPOSITION_CONTEXT*)(message_data->transportContext);
2039 if (tc == NULL)
2040 {
2041 /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
2042 LogError("invalid transport context data");
2043 result = IOTHUB_CLIENT_ERROR;
2044 }
2045 else
2046 {
2047 if ((tc->handleData == NULL) || (tc->deviceData == NULL) || (tc->etagValue == NULL))
2048 {
2049 /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
2050 LogError("invalid transport context data");
2051 result = IOTHUB_CLIENT_ERROR;
2052 }
2053 else
2054 {
2055 if (abandonOrAcceptMessage(tc->handleData, tc->deviceData, tc->etagValue, disposition))
2056 {
2057 result = IOTHUB_CLIENT_OK;
2058 }
2059 else
2060 {
2061 /* Codes_SRS_TRANSPORTMULTITHTTP_10_003: [IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR if the POST message fails, otherwise return IOTHUB_CLIENT_OK.] */
2062 LogError("HTTP Transport layer failed to report %s disposition", MU_ENUM_TO_STRING(IOTHUBMESSAGE_DISPOSITION_RESULT, disposition));
2063 result = IOTHUB_CLIENT_ERROR;
2064 }
2065 }
2066 free(tc->etagValue);
2067 free(tc);
2068 }
2069 IoTHubMessage_Destroy(message_data->messageHandle);
2070 }
2071 free(message_data);
2072 }
2073 return result;
2074}
2075
2076static MESSAGE_CALLBACK_INFO* MESSAGE_CALLBACK_INFO_Create(IOTHUB_MESSAGE_HANDLE received_message, HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* etagValue)
2077{
2078 MESSAGE_CALLBACK_INFO* result = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO));
2079 if (result == NULL)
2080 {
2081 /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */
2082 LogError("malloc failed");
2083 }
2084 else
2085 {
2086 MESSAGE_DISPOSITION_CONTEXT* tc = (MESSAGE_DISPOSITION_CONTEXT*)malloc(sizeof(MESSAGE_DISPOSITION_CONTEXT));
2087 if (tc == NULL)
2088 {
2089 /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */
2090 LogError("malloc failed");
2091 free(result);
2092 result = NULL;
2093 }
2094 else
2095 {
2096 result->messageHandle = IoTHubMessage_Clone(received_message);
2097 if (result->messageHandle == NULL)
2098 {
2099 /*Codes_SRS_TRANSPORTMULTITHTTP_10_007: [If assembling a message clone, _DoWork shall "abandon" the message.]*/
2100 LogError("IoTHubMessage_Clone failed");
2101 free(tc);
2102 free(result);
2103 result = NULL;
2104 }
2105 else
2106 {
2107 if (mallocAndStrcpy_s(&tc->etagValue, etagValue) != 0)
2108 {
2109 LogError("mallocAndStrcpy_s failed");
2110 free(tc);
2111 free(result);
2112 result = NULL;
2113 }
2114 else
2115 {
2116 tc->handleData = handleData;
2117 tc->deviceData = deviceData;
2118
2119 result->transportContext = tc;
2120 }
2121 }
2122 }
2123 }
2124 return result;
2125}
2126
2127static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData)
2128{
2129 /*Codes_SRS_TRANSPORTMULTITHTTP_17_083: [ If device is not subscribed then _DoWork shall advance to the next action. ] */
2130 if (deviceData->DoWork_PullMessage)
2131 {
2132 /*Codes_SRS_TRANSPORTMULTITHTTP_17_123: [After client creation, the first GET shall be allowed no matter what the value of GetMinimumPollingTime.] */
2133 /*Codes_SRS_TRANSPORTMULTITHTTP_17_124: [If time is not available then all calls shall be treated as if they are the first one.] */
2134 /*Codes_SRS_TRANSPORTMULTITHTTP_17_122: [A GET request that happens earlier than GetMinimumPollingTime shall be ignored.] */
2135 time_t timeNow = get_time(NULL);
2136 bool isPollingAllowed = deviceData->isFirstPoll || (timeNow == (time_t)(-1)) || (get_difftime(timeNow, deviceData->lastPollTime) > handleData->getMinimumPollingTime);
2137 if (isPollingAllowed)
2138 {
2139 HTTP_HEADERS_HANDLE responseHTTPHeaders = HTTPHeaders_Alloc();
2140 if (responseHTTPHeaders == NULL)
2141 {
2142 /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */
2143 LogError("unable to HTTPHeaders_Alloc");
2144 }
2145 else
2146 {
2147 BUFFER_HANDLE responseContent = BUFFER_new();
2148 if (responseContent == NULL)
2149 {
2150 /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */
2151 LogError("unable to BUFFER_new");
2152 }
2153 else
2154 {
2155 unsigned int statusCode = 0;
2156 HTTPAPIEX_RESULT r;
2157 if (deviceData->deviceSasToken != NULL)
2158 {
2159 /*Codes_SRS_TRANSPORTMULTITHTTP_03_001: [if a deviceSasToken exists, HTTPHeaders_ReplaceHeaderNameValuePair shall be invoked with "Authorization" as its second argument and STRING_c_str (deviceSasToken) as its third argument.]*/
2160 if (HTTPHeaders_ReplaceHeaderNameValuePair(deviceData->messageHTTPrequestHeaders, IOTHUB_AUTH_HEADER_VALUE, STRING_c_str(deviceData->deviceSasToken)) != HTTP_HEADERS_OK)
2161 {
2162 r = HTTPAPIEX_ERROR;
2163 /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/
2164 LogError("Unable to replace the old SAS Token.");
2165 }
2166 else if ((r = HTTPAPIEX_ExecuteRequest(
2167 handleData->httpApiExHandle,
2168 HTTPAPI_REQUEST_GET, /*requestType: GET*/
2169 STRING_c_str(deviceData->messageHTTPrelativePath), /*relativePath: the message HTTP relative path*/
2170 deviceData->messageHTTPrequestHeaders, /*requestHttpHeadersHandle: message HTTP request headers created by _Create*/
2171 NULL, /*requestContent: NULL*/
2172 &statusCode, /*statusCode: a pointer to unsigned int which shall be later examined*/
2173 responseHTTPHeaders, /*responseHeadearsHandle: a new instance of HTTP headers*/
2174 responseContent /*responseContent: a new instance of buffer*/
2175 )) != HTTPAPIEX_OK)
2176 {
2177 /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */
2178 LogError("Unable to HTTPAPIEX_ExecuteRequest.");
2179 }
2180 }
2181
2182 /*Codes_SRS_TRANSPORTMULTITHTTP_17_084: [Otherwise, IoTHubTransportHttp_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest passing the following parameters
2183 requestType: GET
2184 relativePath: the message HTTP relative path
2185 requestHttpHeadersHandle: message HTTP request headers created by _Create
2186 requestContent: NULL
2187 statusCode: a pointer to unsigned int which shall be later examined
2188 responseHeadearsHandle: a new instance of HTTP headers
2189 responseContent: a new instance of buffer]
2190 */
2191 else if ((r = HTTPAPIEX_SAS_ExecuteRequest(
2192 deviceData->sasObject,
2193 handleData->httpApiExHandle,
2194 HTTPAPI_REQUEST_GET, /*requestType: GET*/
2195 STRING_c_str(deviceData->messageHTTPrelativePath), /*relativePath: the message HTTP relative path*/
2196 deviceData->messageHTTPrequestHeaders, /*requestHttpHeadersHandle: message HTTP request headers created by _Create*/
2197 NULL, /*requestContent: NULL*/
2198 &statusCode, /*statusCode: a pointer to unsigned int which shall be later examined*/
2199 responseHTTPHeaders, /*responseHeadearsHandle: a new instance of HTTP headers*/
2200 responseContent /*responseContent: a new instance of buffer*/
2201 )) != HTTPAPIEX_OK)
2202 {
2203 /*Codes_SRS_TRANSPORTMULTITHTTP_17_085: [If the call to HTTPAPIEX_SAS_ExecuteRequest did not executed successfully or building any part of the prerequisites of the call fails, then _DoWork shall advance to the next action in this description.] */
2204 LogError("unable to HTTPAPIEX_SAS_ExecuteRequest");
2205 }
2206 if (r == HTTPAPIEX_OK)
2207 {
2208 /*HTTP dialogue was succesfull*/
2209 if (timeNow == (time_t)(-1))
2210 {
2211 deviceData->isFirstPoll = true;
2212 }
2213 else
2214 {
2215 deviceData->isFirstPoll = false;
2216 deviceData->lastPollTime = timeNow;
2217 }
2218 if (statusCode == 204)
2219 {
2220 /*Codes_SRS_TRANSPORTMULTITHTTP_17_086: [If the HTTPAPIEX_SAS_ExecuteRequest executed successfully then status code shall be examined. Any status code different than 200 causes _DoWork to advance to the next action.] */
2221 /*this is an expected status code, means "no commands", but logging that creates panic*/
2222
2223 /*do nothing, advance to next action*/
2224 }
2225 else if (statusCode != 200)
2226 {
2227 /*Codes_SRS_TRANSPORTMULTITHTTP_17_086: [If the HTTPAPIEX_SAS_ExecuteRequest executed successfully then status code shall be examined. Any status code different than 200 causes _DoWork to advance to the next action.] */
2228 LogError("expected status code was 200, but actually was received %u... moving on", statusCode);
2229 }
2230 else
2231 {
2232 /*Codes_SRS_TRANSPORTMULTITHTTP_17_087: [If status code is 200, then _DoWork shall make a copy of the value of the "ETag" http header.]*/
2233 const char* etagValue = HTTPHeaders_FindHeaderValue(responseHTTPHeaders, "ETag");
2234 if (etagValue == NULL)
2235 {
2236 LogError("unable to find a received header called \"E-Tag\"");
2237 }
2238 else
2239 {
2240 /*Codes_SRS_TRANSPORTMULTITHTTP_17_088: [If no such header is found or is invalid, then _DoWork shall advance to the next action.]*/
2241 size_t etagsize = strlen(etagValue);
2242 if (
2243 (etagsize < 2) ||
2244 (etagValue[0] != '"') ||
2245 (etagValue[etagsize - 1] != '"')
2246 )
2247 {
2248 LogError("ETag is not a valid quoted string");
2249 }
2250 else
2251 {
2252 const unsigned char* resp_content;
2253 size_t resp_len;
2254 /*Codes_SRS_TRANSPORTMULTITHTTP_17_089: [_DoWork shall assemble an IOTHUBMESSAGE_HANDLE from the received HTTP content (using the responseContent buffer).] */
2255 resp_content = BUFFER_u_char(responseContent);
2256 resp_len = BUFFER_length(responseContent);
2257 IOTHUB_MESSAGE_HANDLE receivedMessage = IoTHubMessage_CreateFromByteArray(resp_content, resp_len);
2258 if (receivedMessage == NULL)
2259 {
2260 /*Codes_SRS_TRANSPORTMULTITHTTP_17_092: [If assembling the message fails in any way, then _DoWork shall "abandon" the message.]*/
2261 LogError("unable to IoTHubMessage_CreateFromByteArray, trying to abandon the message... ");
2262 if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
2263 {
2264 LogError("HTTP Transport layer failed to report ABANDON disposition");
2265 }
2266 }
2267 else
2268 {
2269 if (retrieve_message_properties(responseHTTPHeaders, receivedMessage) != 0)
2270 {
2271 if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
2272 {
2273 LogError("HTTP Transport layer failed to report ABANDON disposition");
2274 }
2275 }
2276 else
2277 {
2278 MESSAGE_CALLBACK_INFO* messageData = MESSAGE_CALLBACK_INFO_Create(receivedMessage, handleData, deviceData, etagValue);
2279 if (messageData == NULL)
2280 {
2281 /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */
2282 LogError("failed to assemble callback info");
2283 if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
2284 {
2285 LogError("HTTP Transport layer failed to report ABANDON disposition");
2286 }
2287 }
2288 else
2289 {
2290 bool abandon;
2291 if (handleData->transport_callbacks.msg_cb(messageData, deviceData->device_transport_ctx))
2292 {
2293 abandon = false;
2294 }
2295 else
2296 {
2297 LogError("IoTHubClientCore_LL_MessageCallback failed");
2298 abandon = true;
2299 }
2300
2301 /*Codes_SRS_TRANSPORTMULTITHTTP_17_096: [If IoTHubClientCore_LL_MessageCallback returns false then _DoWork shall "abandon" the message.] */
2302 if (abandon)
2303 {
2304 (void)IoTHubTransportHttp_SendMessageDisposition(messageData, IOTHUBMESSAGE_ABANDONED);
2305 }
2306 }
2307 }
2308 IoTHubMessage_Destroy(receivedMessage);
2309 }
2310 }
2311 }
2312 }
2313 }
2314 BUFFER_delete(responseContent);
2315 }
2316 HTTPHeaders_Free(responseHTTPHeaders);
2317 }
2318 }
2319 else
2320 {
2321 /*isPollingAllowed is false... */
2322 /*do nothing "shall be ignored*/
2323 }
2324 }
2325}
2326
2327static IOTHUB_PROCESS_ITEM_RESULT IoTHubTransportHttp_ProcessItem(TRANSPORT_LL_HANDLE handle, IOTHUB_IDENTITY_TYPE item_type, IOTHUB_IDENTITY_INFO* iothub_item)
2328{
2329 (void)handle;
2330 (void)item_type;
2331 (void)iothub_item;
2332 LogError("Currently Not Supported.");
2333 return IOTHUB_PROCESS_ERROR;
2334}
2335
2336static void IoTHubTransportHttp_DoWork(TRANSPORT_LL_HANDLE handle)
2337{
2338 /*Codes_SRS_TRANSPORTMULTITHTTP_17_049: [ If handle is NULL, then IoTHubTransportHttp_DoWork shall do nothing. ]*/
2339 if (handle != NULL)
2340 {
2341 HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle;
2342 IOTHUB_DEVICE_HANDLE* listItem;
2343 size_t deviceListSize = VECTOR_size(handleData->perDeviceList);
2344 /*Codes_SRS_TRANSPORTMULTITHTTP_17_052: [ IoTHubTransportHttp_DoWork shall perform a round-robin loop through every deviceHandle in the transport device list. ]*/
2345 /*Codes_SRS_TRANSPORTMULTITHTTP_17_050: [ IoTHubTransportHttp_DoWork shall call loop through the device list. ] */
2346 /*Codes_SRS_TRANSPORTMULTITHTTP_17_051: [ IF the list is empty, then IoTHubTransportHttp_DoWork shall do nothing. ]*/
2347 for (size_t i = 0; i < deviceListSize; i++)
2348 {
2349 listItem = (IOTHUB_DEVICE_HANDLE *)VECTOR_element(handleData->perDeviceList, i);
2350 HTTPTRANSPORT_PERDEVICE_DATA* perDeviceItem = *(HTTPTRANSPORT_PERDEVICE_DATA**)(listItem);
2351 DoEvent(handleData, perDeviceItem);
2352 DoMessages(handleData, perDeviceItem);
2353 }
2354 }
2355 else
2356 {
2357 LogError("Invalid Argument NULL call on DoWork.");
2358 }
2359}
2360
2361static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus)
2362{
2363 IOTHUB_CLIENT_RESULT result;
2364
2365 if (handle == NULL)
2366 {
2367 /*Codes_SRS_TRANSPORTMULTITHTTP_17_111: [ IoTHubTransportHttp_GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG if called with NULL parameter. ]*/
2368 result = IOTHUB_CLIENT_INVALID_ARG;
2369 LogError("Invalid handle to IoTHubClient HTTP transport instance.");
2370 }
2371 else if (iotHubClientStatus == NULL)
2372 {
2373 result = IOTHUB_CLIENT_INVALID_ARG;
2374 LogError("Invalid pointer to output parameter IOTHUB_CLIENT_STATUS.");
2375 }
2376 else
2377 {
2378 /*Codes_SRS_TRANSPORTMULTITHTTP_17_138: [ IoTHubTransportHttp_GetSendStatus shall locate deviceHandle in the transport device list by calling list_find_if. ]*/
2379 IOTHUB_DEVICE_HANDLE* listItem = get_perDeviceDataItem(handle);
2380 if (listItem == NULL)
2381 {
2382 /*Codes_SRS_TRANSPORTMULTITHTTP_17_139: [ If the device structure is not found, then this function shall fail and return with IOTHUB_CLIENT_INVALID_ARG. ]*/
2383 result = IOTHUB_CLIENT_INVALID_ARG;
2384 LogError("Device not found in transport list.");
2385 }
2386 else
2387 {
2388 HTTPTRANSPORT_PERDEVICE_DATA* deviceData = (HTTPTRANSPORT_PERDEVICE_DATA*)(*listItem);
2389 /* Codes_SRS_TRANSPORTMULTITHTTP_17_113: [ IoTHubTransportHttp_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_BUSY if there are currently event items to be sent or being sent. ] */
2390 if (!DList_IsListEmpty(deviceData->waitingToSend))
2391 {
2392 *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_BUSY;
2393 }
2394 /* Codes_SRS_TRANSPORTMULTITHTTP_17_112: [ IoTHubTransportHttp_GetSendStatus shall return IOTHUB_CLIENT_OK and status IOTHUB_CLIENT_SEND_STATUS_IDLE if there are currently no event items to be sent or being sent. ] */
2395 else
2396 {
2397 *iotHubClientStatus = IOTHUB_CLIENT_SEND_STATUS_IDLE;
2398 }
2399 result = IOTHUB_CLIENT_OK;
2400 }
2401 }
2402
2403 return result;
2404}
2405
2406static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_SetOption(TRANSPORT_LL_HANDLE handle, const char* option, const void* value)
2407{
2408 IOTHUB_CLIENT_RESULT result;
2409 /*Codes_SRS_TRANSPORTMULTITHTTP_17_114: [If handle parameter is NULL then IoTHubTransportHttp_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */
2410 /*Codes_SRS_TRANSPORTMULTITHTTP_17_115: [If option parameter is NULL then IoTHubTransportHttp_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */
2411 /*Codes_SRS_TRANSPORTMULTITHTTP_17_116: [If value parameter is NULL then IoTHubTransportHttp_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */
2412 if (
2413 (handle == NULL) ||
2414 (option == NULL) ||
2415 (value == NULL)
2416 )
2417 {
2418 result = IOTHUB_CLIENT_INVALID_ARG;
2419 LogError("invalid parameter (NULL) passed to IoTHubTransportHttp_SetOption");
2420 }
2421 else
2422 {
2423 HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle;
2424 /*Codes_SRS_TRANSPORTMULTITHTTP_17_120: ["Batching"] */
2425 if (strcmp(OPTION_BATCHING, option) == 0)
2426 {
2427 /*Codes_SRS_TRANSPORTMULTITHTTP_17_117: [If optionName is an option handled by IoTHubTransportHttp then it shall be set.] */
2428 handleData->doBatchedTransfers = *(bool*)value;
2429 result = IOTHUB_CLIENT_OK;
2430 }
2431 /*Codes_SRS_TRANSPORTMULTITHTTP_17_121: ["MinimumPollingTime"] */
2432 else if (strcmp(OPTION_MIN_POLLING_TIME, option) == 0)
2433 {
2434 handleData->getMinimumPollingTime = *(unsigned int*)value;
2435 result = IOTHUB_CLIENT_OK;
2436 }
2437 else
2438 {
2439 /*Codes_SRS_TRANSPORTMULTITHTTP_17_126: [ "TrustedCerts"] */
2440 /*Codes_SRS_TRANSPORTMULTITHTTP_17_127: [ NULL shall be allowed. ]*/
2441 /*Codes_SRS_TRANSPORTMULTITHTTP_17_129: [ This option shall passed down to the lower layer by calling HTTPAPIEX_SetOption. ]*/
2442 /*Codes_SRS_TRANSPORTMULTITHTTP_17_118: [Otherwise, IoTHubTransport_Http shall call HTTPAPIEX_SetOption with the same parameters and return the translated code.] */
2443 HTTPAPIEX_RESULT HTTPAPIEX_result = HTTPAPIEX_SetOption(handleData->httpApiExHandle, option, value);
2444 /*Codes_SRS_TRANSPORTMULTITHTTP_17_119: [The following table translates HTTPAPIEX return codes to IOTHUB_CLIENT_RESULT return codes:] */
2445 if (HTTPAPIEX_result == HTTPAPIEX_OK)
2446 {
2447 result = IOTHUB_CLIENT_OK;
2448 }
2449 else if (HTTPAPIEX_result == HTTPAPIEX_INVALID_ARG)
2450 {
2451 result = IOTHUB_CLIENT_INVALID_ARG;
2452 LogError("HTTPAPIEX_SetOption failed");
2453 }
2454 else
2455 {
2456 result = IOTHUB_CLIENT_ERROR;
2457 LogError("HTTPAPIEX_SetOption failed");
2458 }
2459 }
2460 }
2461 return result;
2462}
2463
2464static STRING_HANDLE IoTHubTransportHttp_GetHostname(TRANSPORT_LL_HANDLE handle)
2465{
2466 STRING_HANDLE result;
2467 /*Codes_SRS_TRANSPORTMULTITHTTP_02_001: [ If handle is NULL then IoTHubTransportHttp_GetHostname shall fail and return NULL. ]*/
2468 if (handle == NULL)
2469 {
2470 LogError("invalid parameter handle=%p", handle);
2471 result = NULL;
2472 }
2473 /*Codes_SRS_TRANSPORTMULTITHTTP_02_002: [ Otherwise IoTHubTransportHttp_GetHostname shall return a non-NULL STRING_HANDLE containing the hostname. ]*/
2474 else if ((result = STRING_clone(((HTTPTRANSPORT_HANDLE_DATA*)(handle))->hostName)) == NULL)
2475 {
2476 LogError("Cannot provide the target host name (STRING_clone failed).");
2477 }
2478
2479 return result;
2480}
2481
2482static int IoTHubTransportHttp_SetRetryPolicy(TRANSPORT_LL_HANDLE handle, IOTHUB_CLIENT_RETRY_POLICY retryPolicy, size_t retryTimeoutLimitInSeconds)
2483{
2484 int result;
2485 (void)handle;
2486 (void)retryPolicy;
2487 (void)retryTimeoutLimitInSeconds;
2488
2489 /* Retry Policy is not currently not available for HTTP */
2490
2491 result = 0;
2492 return result;
2493}
2494
2495static int IotHubTransportHttp_Subscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle)
2496{
2497 (void)handle;
2498 LogError("HTTP does not support input queues");
2499 return MU_FAILURE;
2500}
2501
2502static void IotHubTransportHttp_Unsubscribe_InputQueue(IOTHUB_DEVICE_HANDLE handle)
2503{
2504 (void)handle;
2505 LogError("HTTP does not support input queues");
2506}
2507
2508int IoTHubTransportHttp_SetCallbackContext(TRANSPORT_LL_HANDLE handle, void* ctx)
2509{
2510 int result;
2511 if (handle == NULL)
2512 {
2513 LogError("Invalid parameter specified handle: %p", handle);
2514 result = MU_FAILURE;
2515 }
2516 else
2517 {
2518 HTTPTRANSPORT_HANDLE_DATA* handleData = (HTTPTRANSPORT_HANDLE_DATA*)handle;
2519 handleData->transport_ctx = ctx;
2520 result = 0;
2521 }
2522 return result;
2523}
2524
2525static int IoTHubTransportHttp_GetSupportedPlatformInfo(TRANSPORT_LL_HANDLE handle, PLATFORM_INFO_OPTION* info)
2526{
2527 int result;
2528 if (handle == NULL)
2529 {
2530 result = MU_FAILURE;
2531 }
2532 else
2533 {
2534 *info = PLATFORM_INFO_OPTION_DEFAULT;
2535 result = 0;
2536 }
2537
2538 return result;
2539}
2540
2541/*Codes_SRS_TRANSPORTMULTITHTTP_17_125: [This function shall return a pointer to a structure of type TRANSPORT_PROVIDER having the following values for its fields:] */
2542static TRANSPORT_PROVIDER thisTransportProvider =
2543{
2544 IoTHubTransportHttp_SendMessageDisposition, /*pfIotHubTransport_SendMessageDisposition IoTHubTransport_SendMessageDisposition;*/
2545 IoTHubTransportHttp_Subscribe_DeviceMethod, /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/
2546 IoTHubTransportHttp_Unsubscribe_DeviceMethod, /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/
2547 IoTHubTransportHttp_DeviceMethod_Response, /*pfIoTHubTransport_DeviceMethod_Response IoTHubTransport_DeviceMethod_Response;*/
2548 IoTHubTransportHttp_Subscribe_DeviceTwin, /*pfIoTHubTransport_Subscribe_DeviceTwin IoTHubTransport_Subscribe_DeviceTwin;*/
2549 IoTHubTransportHttp_Unsubscribe_DeviceTwin, /*pfIoTHubTransport_Unsubscribe_DeviceTwin IoTHubTransport_Unsubscribe_DeviceTwin;*/
2550 IoTHubTransportHttp_ProcessItem, /*pfIoTHubTransport_ProcessItem IoTHubTransport_ProcessItem;*/
2551 IoTHubTransportHttp_GetHostname, /*pfIoTHubTransport_GetHostname IoTHubTransport_GetHostname;*/
2552 IoTHubTransportHttp_SetOption, /*pfIoTHubTransport_SetOption IoTHubTransport_SetOption;*/
2553 IoTHubTransportHttp_Create, /*pfIoTHubTransport_Create IoTHubTransport_Create;*/
2554 IoTHubTransportHttp_Destroy, /*pfIoTHubTransport_Destroy IoTHubTransport_Destroy;*/
2555 IoTHubTransportHttp_Register, /*pfIotHubTransport_Register IoTHubTransport_Register;*/
2556 IoTHubTransportHttp_Unregister, /*pfIotHubTransport_Unregister IoTHubTransport_Unegister;*/
2557 IoTHubTransportHttp_Subscribe, /*pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe;*/
2558 IoTHubTransportHttp_Unsubscribe, /*pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe;*/
2559 IoTHubTransportHttp_DoWork, /*pfIoTHubTransport_DoWork IoTHubTransport_DoWork;*/
2560 IoTHubTransportHttp_SetRetryPolicy, /*pfIoTHubTransport_DoWork IoTHubTransport_SetRetryPolicy;*/
2561 IoTHubTransportHttp_GetSendStatus, /*pfIoTHubTransport_GetSendStatus IoTHubTransport_GetSendStatus;*/
2562 IotHubTransportHttp_Subscribe_InputQueue, /*pfIoTHubTransport_Subscribe_InputQueue IoTHubTransport_Subscribe_InputQueue; */
2563 IotHubTransportHttp_Unsubscribe_InputQueue, /*pfIoTHubTransport_Unsubscribe_InputQueue IoTHubTransport_Unsubscribe_InputQueue; */
2564 IoTHubTransportHttp_SetCallbackContext, /*pfIoTHubTransport_SetTransportCallbacks IoTHubTransport_SetTransportCallbacks; */
2565 IoTHubTransportHttp_GetTwinAsync, /*pfIoTHubTransport_GetTwinAsync IoTHubTransport_GetTwinAsync;*/
2566 IoTHubTransportHttp_GetSupportedPlatformInfo /*pfIoTHubTransport_GetSupportedPlatformInfo IoTHubTransport_GetSupportedPlatformInfo;*/
2567};
2568
2569const TRANSPORT_PROVIDER* HTTP_Protocol(void)
2570{
2571 return &thisTransportProvider;
2572}
Note: See TracBrowser for help on using the repository browser.