Microsoft Azure IoTHub client HTTP transport

Dependents:   iothub_client_sample_http simplesample_http temp_sensor_anomaly

This library implements the HTTP transport for Microsoft Azure IoTHub client. The code is replicated from https://github.com/Azure/azure-iot-sdks

Revision:
29:e44b1f827914
Parent:
28:f57de550d450
Child:
30:655054f86a6e
--- a/iothubtransporthttp.c	Fri Feb 24 14:00:14 2017 -0800
+++ b/iothubtransporthttp.c	Fri Mar 10 11:47:09 2017 -0800
@@ -74,6 +74,15 @@
     DLIST_ENTRY eventConfirmations; /*holds items for event confirmations*/
 } HTTPTRANSPORT_PERDEVICE_DATA;
 
+typedef struct MESSAGE_DISPOSITION_CONTEXT_TAG
+{
+    HTTPTRANSPORT_HANDLE_DATA* handleData;
+    HTTPTRANSPORT_PERDEVICE_DATA* deviceData;
+    char* etagValue;
+} MESSAGE_DISPOSITION_CONTEXT;
+
+DEFINE_ENUM_STRINGS(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES);
+
 static void destroy_eventHTTPrelativePath(HTTPTRANSPORT_PERDEVICE_DATA* handleData)
 {
     STRING_delete(handleData->eventHTTPrelativePath);
@@ -1649,13 +1658,7 @@
     }
 }
 
-#define ACTION_VALUES \
-    ABANDON, \
-    REJECT, \
-    ACCEPT
-DEFINE_ENUM(ACTION, ACTION_VALUES);
-
-static void abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* ETag, ACTION action)
+static bool abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* ETag, IOTHUBMESSAGE_DISPOSITION_RESULT action)
 {
     /*Codes_SRS_TRANSPORTMULTITHTTP_17_097: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters:
     -requestType: POST
@@ -1688,6 +1691,7 @@
     - responseHeadearsHandle: NULL
     - responseContent: NULL]*/
 
+    bool result;
     STRING_HANDLE fullAbandonRelativePath = STRING_clone(deviceData->abandonHTTPrelativePathBegin);
     if (fullAbandonRelativePath == NULL)
     {
@@ -1695,6 +1699,7 @@
         /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
         /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
         LogError("unable to STRING_clone");
+        result = false;
     }
     else
     {
@@ -1705,18 +1710,20 @@
             /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
             /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
             LogError("unable to STRING_construct_n");
+            result = false;
         }
         else
         {
             if (!(
                 (STRING_concat_with_STRING(fullAbandonRelativePath, ETagUnquoted) == 0) &&
-                (STRING_concat(fullAbandonRelativePath, (action == ABANDON) ? "/abandon" API_VERSION : ((action == REJECT) ? API_VERSION "&reject" : API_VERSION)) == 0)
+                (STRING_concat(fullAbandonRelativePath, (action == IOTHUBMESSAGE_ABANDONED) ? "/abandon" API_VERSION : ((action == IOTHUBMESSAGE_REJECTED) ? API_VERSION "&reject" : API_VERSION)) == 0)
                 ))
             {
                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_098: [Abandoning the message is considered successful if the HTTPAPIEX_SAS_ExecuteRequest doesn't fail and the statusCode is 204.]*/
                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                 LogError("unable to STRING_concat");
+                result = false;
             }
             else
             {
@@ -1727,6 +1734,7 @@
                     /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                     /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                     LogError("unable to HTTPHeaders_Alloc");
+                    result = false;
                 }
                 else
                 {
@@ -1740,6 +1748,7 @@
                         /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                         /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                         LogError("unable to HTTPHeaders_AddHeaderNameValuePair");
+                        result = false;
                     }
                     else
                     {
@@ -1753,10 +1762,11 @@
                                 r = HTTPAPIEX_ERROR;
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_03_002: [If the result of the invocation of HTTPHeaders_ReplaceHeaderNameValuePair is NOT HTTP_HEADERS_OK then fallthrough.]*/
                                 LogError("Unable to replace the old SAS Token.");
+                                result = false;
                             }
                             else if ((r = HTTPAPIEX_ExecuteRequest(
                                 handleData->httpApiExHandle,
-                                (action == ABANDON) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE,                               /*-requestType: POST                                                                                                       */
+                                (action == IOTHUBMESSAGE_ABANDONED) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE,                               /*-requestType: POST                                                                                                       */
                                 STRING_c_str(fullAbandonRelativePath),              /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14"   */
                                 abandonRequestHttpHeaders,                          /*- requestHttpHeadersHandle: an HTTP headers instance containing the following                                            */
                                 NULL,                                               /*- requestContent: NULL                                                                                                   */
@@ -1769,12 +1779,13 @@
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                                 LogError("Unable to HTTPAPIEX_ExecuteRequest.");
+                                result = false;
                             }
                         }
                         else if ((r = HTTPAPIEX_SAS_ExecuteRequest(
                             deviceData->sasObject,
                             handleData->httpApiExHandle,
-                            (action == ABANDON) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE,                               /*-requestType: POST                                                                                                       */
+                            (action == IOTHUBMESSAGE_ABANDONED) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE,                               /*-requestType: POST                                                                                                       */
                             STRING_c_str(fullAbandonRelativePath),              /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-11-14"   */
                             abandonRequestHttpHeaders,                          /*- requestHttpHeadersHandle: an HTTP headers instance containing the following                                            */
                             NULL,                                               /*- requestContent: NULL                                                                                                   */
@@ -1787,6 +1798,7 @@
                             /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                             /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                             LogError("unable to HTTPAPIEX_SAS_ExecuteRequest");
+                            result = false;
                         }
                         if (r == HTTPAPIEX_OK)
                         {
@@ -1796,6 +1808,7 @@
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                                 LogError("unexpected status code returned %u (was expecting 204)", statusCode);
+                                result = false;
                             }
                             else
                             {
@@ -1803,8 +1816,13 @@
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_100: [Accepting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                                 /*Codes_SRS_TRANSPORTMULTITHTTP_17_102: [Rejecting a message is successful when HTTPAPIEX_SAS_ExecuteRequest completes successfully and the status code is 204.] */
                                 /*all is fine*/
+                                result = true;
                             }
                         }
+                        else
+                        {
+                            result = false;
+                        }
                     }
                     HTTPHeaders_Free(abandonRequestHttpHeaders);
                 }
@@ -1813,6 +1831,115 @@
         }
         STRING_delete(fullAbandonRelativePath);
     }
+    return result;
+}
+
+static IOTHUB_CLIENT_RESULT IoTHubTransportHttp_SendMessageDisposition(MESSAGE_CALLBACK_INFO* message_data, IOTHUBMESSAGE_DISPOSITION_RESULT disposition)
+{
+    IOTHUB_CLIENT_RESULT result;
+    if (message_data == NULL)
+    {
+        /* Codes_SRS_TRANSPORTMULTITHTTP_10_001: [If messageData is NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
+        LogError("invalid argument messageData is NULL");
+        result = IOTHUB_CLIENT_ERROR;
+    }
+    else
+    {
+        if (message_data->messageHandle == NULL)
+        {
+            /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
+            LogError("invalid message handle");
+            result = IOTHUB_CLIENT_ERROR;
+        }
+        else
+        {
+            MESSAGE_DISPOSITION_CONTEXT* tc = (MESSAGE_DISPOSITION_CONTEXT*)(message_data->transportContext);
+            if (tc == NULL)
+            {
+                /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
+                LogError("invalid transport context data");
+                result = IOTHUB_CLIENT_ERROR;
+            }
+            else
+            {
+                if ((tc->handleData == NULL) || (tc->deviceData == NULL) || (tc->etagValue == NULL))
+                {
+                    /* Codes_SRS_TRANSPORTMULTITHTTP_10_002: [If any of the messageData fields are NULL, IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR.] */
+                    LogError("invalid transport context data");
+                    result = IOTHUB_CLIENT_ERROR;
+                }
+                else
+                {
+                    if (abandonOrAcceptMessage(tc->handleData, tc->deviceData, tc->etagValue, disposition))
+                    {
+                        result = IOTHUB_CLIENT_OK;
+                    }
+                    else
+                    {
+                        /* Codes_SRS_TRANSPORTMULTITHTTP_10_003: [IoTHubTransportHttp_SendMessageDisposition shall fail and return IOTHUB_CLIENT_ERROR if the POST message fails, otherwise return IOTHUB_CLIENT_OK.] */
+                        LogError("HTTP Transport layer failed to report %s disposition", ENUM_TO_STRING(IOTHUBMESSAGE_DISPOSITION_RESULT, disposition));
+                        result = IOTHUB_CLIENT_ERROR;
+                    }
+                }
+                free(tc->etagValue);
+                free(tc);
+            }
+            IoTHubMessage_Destroy(message_data->messageHandle);
+        }
+        free(message_data);
+    }
+    return result;
+}
+
+static MESSAGE_CALLBACK_INFO* MESSAGE_CALLBACK_INFO_Create(IOTHUB_MESSAGE_HANDLE received_message, HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, const char* etagValue)
+{
+    MESSAGE_CALLBACK_INFO* result = (MESSAGE_CALLBACK_INFO*)malloc(sizeof(MESSAGE_CALLBACK_INFO));
+    if (result == NULL)
+    {
+        /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */
+        LogError("malloc failed");
+    }
+    else
+    {
+        MESSAGE_DISPOSITION_CONTEXT* tc = (MESSAGE_DISPOSITION_CONTEXT*)malloc(sizeof(MESSAGE_DISPOSITION_CONTEXT));
+        if (tc == NULL)
+        {
+            /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */
+            LogError("malloc failed");
+            free(result);
+            result = NULL;
+        }
+        else
+        {
+            result->messageHandle = IoTHubMessage_Clone(received_message);
+            if (result->messageHandle == NULL)
+            {
+                /*Codes_SRS_TRANSPORTMULTITHTTP_10_007: [If assembling a message clone, _DoWork shall "abandon" the message.]*/
+                LogError("IoTHubMessage_Clone failed");
+                free(tc);
+                free(result);
+                result = NULL;
+            }
+            else
+            {
+                if (mallocAndStrcpy_s(&tc->etagValue, etagValue) != 0)
+                {
+                    LogError("mallocAndStrcpy_s failed");
+                    free(tc);
+                    free(result);
+                    result = NULL;
+                }
+                else
+                {
+                    tc->handleData = handleData;
+                    tc->deviceData = deviceData;
+
+                    result->transportContext = tc;
+                }
+            }
+        }
+    }
+    return result;
 }
 
 static void DoMessages(HTTPTRANSPORT_HANDLE_DATA* handleData, HTTPTRANSPORT_PERDEVICE_DATA* deviceData, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle)
@@ -1946,7 +2073,10 @@
                                     {
                                         /*Codes_SRS_TRANSPORTMULTITHTTP_17_092: [If assembling the message fails in any way, then _DoWork shall "abandon" the message.]*/
                                         LogError("unable to IoTHubMessage_CreateFromByteArray, trying to abandon the message... ");
-                                        abandonOrAcceptMessage(handleData, deviceData, etagValue, ABANDON);
+                                        if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
+                                        {
+                                            LogError("HTTP Transport layer failed to report ABANDON disposition");
+                                        }
                                     }
                                     else
                                     {
@@ -1956,7 +2086,10 @@
                                         if (HTTPHeaders_GetHeaderCount(responseHTTPHeaders, &nHeaders) != HTTP_HEADERS_OK)
                                         {
                                             LogError("unable to get the count of HTTP headers");
-                                            abandonOrAcceptMessage(handleData, deviceData, etagValue, ABANDON);
+                                            if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
+                                            {
+                                                LogError("HTTP Transport layer failed to report ABANDON disposition");
+                                            }
                                         }
                                         else
                                         {
@@ -2018,33 +2151,47 @@
 
                                             if (i < nHeaders)
                                             {
-                                                abandonOrAcceptMessage(handleData, deviceData, etagValue, ABANDON);
+                                                if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
+                                                {
+                                                    LogError("HTTP Transport layer failed to report ABANDON disposition");
+                                                }
                                             }
                                             else
                                             {
-                                                /*Codes_SRS_TRANSPORTMULTITHTTP_17_093: [Otherwise, _DoWork shall call IoTHubClient_LL_MessageCallback with parameters handle = iotHubClientHandle and message = newly created message.]*/
-                                                IOTHUBMESSAGE_DISPOSITION_RESULT messageResult = IoTHubClient_LL_MessageCallback(iotHubClientHandle, receivedMessage);
-                                                if (messageResult == IOTHUBMESSAGE_ACCEPTED)
+                                                MESSAGE_CALLBACK_INFO* messageData = MESSAGE_CALLBACK_INFO_Create(receivedMessage, handleData, deviceData, etagValue);
+                                                if (messageData == NULL)
                                                 {
-                                                    /*Codes_SRS_TRANSPORTMULTITHTTP_17_094: [If IoTHubClient_LL_MessageCallback returns IOTHUBMESSAGE_ACCEPTED then _DoWork shall "accept" the message.]*/
-                                                    abandonOrAcceptMessage(handleData, deviceData, etagValue, ACCEPT);
-                                                }
-                                                else if (messageResult == IOTHUBMESSAGE_REJECTED)
-                                                {
-                                                    /*Codes_SRS_TRANSPORTMULTITHTTP_17_095: [If IoTHubClient_LL_MessageCallback returns IOTHUBMESSAGE_REJECTED then _DoWork shall "reject" the message.]*/
-                                                    abandonOrAcceptMessage(handleData, deviceData, etagValue, REJECT);
+                                                    /*Codes_SRS_TRANSPORTMULTITHTTP_10_006: [If assembling the transport context fails, _DoWork shall "abandon" the message.] */
+                                                    LogError("failed to assemble callback info");
+                                                    if (!abandonOrAcceptMessage(handleData, deviceData, etagValue, IOTHUBMESSAGE_ABANDONED))
+                                                    {
+                                                        LogError("HTTP Transport layer failed to report ABANDON disposition");
+                                                    }
                                                 }
                                                 else
                                                 {
-                                                    /*Codes_SRS_TRANSPORTMULTITHTTP_17_096: [If IoTHubClient_LL_MessageCallback returns IOTHUBMESSAGE_ABANDONED then _DoWork shall "abandon" the message.] */
-                                                    abandonOrAcceptMessage(handleData, deviceData, etagValue, ABANDON);
+                                                    bool abandon;
+                                                    if (IoTHubClient_LL_MessageCallback(iotHubClientHandle, messageData))
+                                                    {
+                                                        abandon = false;
+                                                    }
+                                                    else
+                                                    {
+                                                        LogError("IoTHubClient_LL_MessageCallback failed");
+                                                        abandon = true;
+                                                    }
+
+                                                    /*Codes_SRS_TRANSPORTMULTITHTTP_17_096: [If IoTHubClient_LL_MessageCallback returns false then _DoWork shall "abandon" the message.] */
+                                                    if (abandon)
+                                                    {
+                                                        (void)IoTHubTransportHttp_SendMessageDisposition(messageData, IOTHUBMESSAGE_ABANDONED);
+                                                    }
                                                 }
                                             }
                                         }
                                         IoTHubMessage_Destroy(receivedMessage);
                                     }
                                 }
-
                             }
                         }
                     }
@@ -2236,6 +2383,7 @@
 /*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:] */
 static TRANSPORT_PROVIDER thisTransportProvider =
 {
+    IoTHubTransportHttp_SendMessageDisposition,     /*pfIotHubTransport_SendMessageDisposition IoTHubTransport_SendMessageDisposition;*/
     IoTHubTransportHttp_Subscribe_DeviceMethod,     /*pfIoTHubTransport_Subscribe_DeviceMethod IoTHubTransport_Subscribe_DeviceMethod;*/
     IoTHubTransportHttp_Unsubscribe_DeviceMethod,   /*pfIoTHubTransport_Unsubscribe_DeviceMethod IoTHubTransport_Unsubscribe_DeviceMethod;*/
     IoTHubTransportHttp_DeviceMethod_Response,      /*pfIoTHubTransport_DeviceMethod_Response IoTHubTransport_DeviceMethod_Response;*/