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
Diff: iothubtransporthttp.c
- 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;*/