source: azure_iot_hub/trunk/azure_iohub/iothub_client/src/iothubtransporthttp.c@ 388

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

Azure IoT Hub Device C SDK を使ったサンプルの追加

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