diff --git a/.github/ISSUE_TEMPLATE/general-template.md b/.github/ISSUE_TEMPLATE/general-template.md deleted file mode 100644 index f0e8e1c..0000000 --- a/.github/ISSUE_TEMPLATE/general-template.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: General Template -about: Template for submitting issues -title: DO NOT SUBMIT ISSUES TO THIS REPO -labels: '' -assignees: '' - ---- - -Hello! Thank you for submitting an issue to our repository. **HOWEVER**, don't do it here. We don't monitor this repository, and all the files are autogenerated so any issues submitted here will be in vain. - -Please submit your issue to the [azure-iot-sdk-c repo](https://github.com/Azure/azure-iot-sdk-c), and add "Arduino PAL" in the issue title. - -Thanks! diff --git a/LICENSE b/LICENSE deleted file mode 100644 index de43774..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 Microsoft - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/examples/esp8266/simplesample_http/README.md b/examples/esp8266/simplesample_http/README.md deleted file mode 100644 index 314de52..0000000 --- a/examples/esp8266/simplesample_http/README.md +++ /dev/null @@ -1,4 +0,0 @@ -### simplesample_http - -Instructions for this sample are -[here in the Azure IoT HTTP protocol library for Arduino.](https://github.com/Azure/azure-iot-arduino-protocol-http) \ No newline at end of file diff --git a/examples/esp8266/simplesample_http/iot_configs.h b/examples/esp8266/simplesample_http/iot_configs.h deleted file mode 100644 index 0b6168f..0000000 --- a/examples/esp8266/simplesample_http/iot_configs.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef IOT_CONFIGS_H -#define IOT_CONFIGS_H - -/** - * WiFi setup - */ -#define IOT_CONFIG_WIFI_SSID "" -#define IOT_CONFIG_WIFI_PASSWORD "" - -/** - * Find under Microsoft Azure IoT Suite -> DEVICES -> -> Device Details and Authentication Keys - * String containing Hostname, Device Id & Device Key in the format: - * "HostName=;DeviceId=;SharedAccessKey=" - */ -#define IOT_CONFIG_CONNECTION_STRING "HostName=.azure-devices.net;DeviceId=;SharedAccessKey=" - -/** - * Choose the transport protocol - */ -// #define IOT_CONFIG_MQTT // uncomment this line for MQTT -#define IOT_CONFIG_HTTP // uncomment this line for HTTP - -#endif /* IOT_CONFIGS_H */ diff --git a/examples/esp8266/simplesample_http/sample.h b/examples/esp8266/simplesample_http/sample.h deleted file mode 100644 index ad6e441..0000000 --- a/examples/esp8266/simplesample_http/sample.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef SAMPLE_H -#define SAMPLE_H - -#ifdef __cplusplus -extern "C" { -#endif - - void sample_run(void); - -#ifdef __cplusplus -} -#endif - -#endif /* SAMPLE_H */ diff --git a/examples/esp8266/simplesample_http/simplesample_http.c b/examples/esp8266/simplesample_http/simplesample_http.c deleted file mode 100644 index f5c011a..0000000 --- a/examples/esp8266/simplesample_http/simplesample_http.c +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include - -#include -#include -#include "iot_configs.h" -#include "sample.h" - -#include "AzureIoTHub.h" - -/*String containing Hostname, Device Id & Device Key in the format: */ -/* "HostName=;DeviceId=;SharedAccessKey=" */ -static const char* connectionString = IOT_CONFIG_CONNECTION_STRING; - -// Define the Model -BEGIN_NAMESPACE(WeatherStation); - -DECLARE_MODEL(ContosoAnemometer, -WITH_DATA(ascii_char_ptr, DeviceId), -WITH_DATA(int, WindSpeed), -WITH_DATA(float, Temperature), -WITH_DATA(float, Humidity), -WITH_ACTION(TurnFanOn), -WITH_ACTION(TurnFanOff), -WITH_ACTION(SetAirResistance, int, Position) -); - -END_NAMESPACE(WeatherStation); - -static char propText[1024]; - -EXECUTE_COMMAND_RESULT TurnFanOn(ContosoAnemometer* device) -{ - (void)device; - (void)printf("Turning fan on.\r\n"); - return EXECUTE_COMMAND_SUCCESS; -} - -EXECUTE_COMMAND_RESULT TurnFanOff(ContosoAnemometer* device) -{ - (void)device; - (void)printf("Turning fan off.\r\n"); - return EXECUTE_COMMAND_SUCCESS; -} - -EXECUTE_COMMAND_RESULT SetAirResistance(ContosoAnemometer* device, int Position) -{ - (void)device; - (void)printf("Setting Air Resistance Position to %d.\r\n", Position); - return EXECUTE_COMMAND_SUCCESS; -} - -void sendCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) -{ - unsigned int messageTrackingId = (unsigned int)(uintptr_t)userContextCallback; - - (void)printf("Message Id: %u Received.\r\n", messageTrackingId); - - (void)printf("Result Call Back Called! Result is: %s \r\n", ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); -} - -static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) -{ - static unsigned int messageTrackingId; - IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); - if (messageHandle == NULL) - { - printf("unable to create a new IoTHubMessage\r\n"); - } - else - { - if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)(uintptr_t)messageTrackingId) != IOTHUB_CLIENT_OK) - { - printf("failed to hand over the message to IoTHubClient"); - } - else - { - printf("IoTHubClient accepted the message for delivery\r\n"); - } - IoTHubMessage_Destroy(messageHandle); - } - free((void*)buffer); - messageTrackingId++; -} - -/*this function "links" IoTHub to the serialization library*/ -static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) -{ - IOTHUBMESSAGE_DISPOSITION_RESULT result; - const unsigned char* buffer; - size_t size; - if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) - { - printf("unable to IoTHubMessage_GetByteArray\r\n"); - result = IOTHUBMESSAGE_ABANDONED; - } - else - { - /*buffer is not zero terminated*/ - char* temp = malloc(size + 1); - if (temp == NULL) - { - printf("failed to malloc\r\n"); - result = IOTHUBMESSAGE_ABANDONED; - } - else - { - EXECUTE_COMMAND_RESULT executeCommandResult; - - (void)memcpy(temp, buffer, size); - temp[size] = '\0'; - executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); - result = - (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : - (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : - IOTHUBMESSAGE_REJECTED; - free(temp); - } - } - return result; -} - -void simplesample_http_run(void) -{ - if (platform_init() != 0) - { - printf("Failed to initialize the platform.\r\n"); - } - else - { - if (serializer_init(NULL) != SERIALIZER_OK) - { - (void)printf("Failed on serializer_init\r\n"); - } - else - { - IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol); - int avgWindSpeed = 10; - float minTemperature = 20.0; - float minHumidity = 60.0; - - srand((unsigned int)time(NULL)); - - if (iotHubClientHandle == NULL) - { - (void)printf("Failed on IoTHubClient_LL_Create\r\n"); - } - else - { - // Because it can poll "after 9 seconds" polls will happen - // effectively at ~10 seconds. - // Note that for scalabilty, the default value of minimumPollingTime - // is 25 minutes. For more information, see: - // https://azure.microsoft.com/documentation/articles/iot-hub-devguide/#messaging - unsigned int minimumPollingTime = 9; - ContosoAnemometer* myWeather; - - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) - { - printf("failure to set option \"MinimumPollingTime\"\r\n"); - } - -#ifdef SET_TRUSTED_CERT_IN_SAMPLES - // For mbed add the certificate information - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) - { - (void)printf("failure to set option \"TrustedCerts\"\r\n"); - } -#endif // SET_TRUSTED_CERT_IN_SAMPLES - - myWeather = CREATE_MODEL_INSTANCE(WeatherStation, ContosoAnemometer); - if (myWeather == NULL) - { - (void)printf("Failed on CREATE_MODEL_INSTANCE\r\n"); - } - else - { - if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK) - { - printf("unable to IoTHubClient_SetMessageCallback\r\n"); - } - else - { - myWeather->DeviceId = "myFirstDevice"; - myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2); - myWeather->Temperature = minTemperature + (rand() % 10); - myWeather->Humidity = minHumidity + (rand() % 20); - { - unsigned char* destination; - size_t destinationSize; - if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->WindSpeed, myWeather->Temperature, myWeather->Humidity) != CODEFIRST_OK) - { - (void)printf("Failed to serialize\r\n"); - } - else - { - IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); - if (messageHandle == NULL) - { - printf("unable to create a new IoTHubMessage\r\n"); - } - else - { - MAP_HANDLE propMap = IoTHubMessage_Properties(messageHandle); - (void)sprintf_s(propText, sizeof(propText), myWeather->Temperature > 28 ? "true" : "false"); - if (Map_AddOrUpdate(propMap, "temperatureAlert", propText) != MAP_OK) - { - printf("ERROR: Map_AddOrUpdate Failed!\r\n"); - } - - if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)1) != IOTHUB_CLIENT_OK) - { - printf("failed to hand over the message to IoTHubClient"); - } - else - { - printf("IoTHubClient accepted the message for delivery\r\n"); - } - - IoTHubMessage_Destroy(messageHandle); - } - free(destination); - } - } - - /* wait for commands */ - while (1) - { - IoTHubClient_LL_DoWork(iotHubClientHandle); - ThreadAPI_Sleep(100); - } - } - - DESTROY_MODEL_INSTANCE(myWeather); - } - IoTHubClient_LL_Destroy(iotHubClientHandle); - } - serializer_deinit(); - } - platform_deinit(); - } -} - - -void sample_run(void) -{ - simplesample_http_run(); -} diff --git a/examples/esp8266/simplesample_http/simplesample_http.ino b/examples/esp8266/simplesample_http/simplesample_http.ino deleted file mode 100644 index 3958c84..0000000 --- a/examples/esp8266/simplesample_http/simplesample_http.ino +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -// Please use an Arduino IDE 1.6.8 or greater - -// You must set the device id, device key, IoT Hub name and IotHub suffix in -// iot_configs.h -#include "iot_configs.h" - -#include -#if defined(IOT_CONFIG_MQTT) - #include -#elif defined(IOT_CONFIG_HTTP) - #include -#endif - -#include "sample.h" -#include "esp8266/sample_init.h" - -static char ssid[] = IOT_CONFIG_WIFI_SSID; -static char pass[] = IOT_CONFIG_WIFI_PASSWORD; - -void setup() { - sample_init(ssid, pass); -} - -// Azure IoT samples contain their own loops, so only run them once -static bool done = false; -void loop() { - if (!done) - { - // Run the sample - // You must set the device id, device key, IoT Hub name and IotHub suffix in - // iot_configs.h - sample_run(); - done = true; - } - else - { - delay(500); - } -} - diff --git a/examples/samd/simplesample_http/README.md b/examples/samd/simplesample_http/README.md deleted file mode 100644 index 314de52..0000000 --- a/examples/samd/simplesample_http/README.md +++ /dev/null @@ -1,4 +0,0 @@ -### simplesample_http - -Instructions for this sample are -[here in the Azure IoT HTTP protocol library for Arduino.](https://github.com/Azure/azure-iot-arduino-protocol-http) \ No newline at end of file diff --git a/examples/samd/simplesample_http/iot_configs.h b/examples/samd/simplesample_http/iot_configs.h deleted file mode 100644 index 0b6168f..0000000 --- a/examples/samd/simplesample_http/iot_configs.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef IOT_CONFIGS_H -#define IOT_CONFIGS_H - -/** - * WiFi setup - */ -#define IOT_CONFIG_WIFI_SSID "" -#define IOT_CONFIG_WIFI_PASSWORD "" - -/** - * Find under Microsoft Azure IoT Suite -> DEVICES -> -> Device Details and Authentication Keys - * String containing Hostname, Device Id & Device Key in the format: - * "HostName=;DeviceId=;SharedAccessKey=" - */ -#define IOT_CONFIG_CONNECTION_STRING "HostName=.azure-devices.net;DeviceId=;SharedAccessKey=" - -/** - * Choose the transport protocol - */ -// #define IOT_CONFIG_MQTT // uncomment this line for MQTT -#define IOT_CONFIG_HTTP // uncomment this line for HTTP - -#endif /* IOT_CONFIGS_H */ diff --git a/examples/samd/simplesample_http/sample.h b/examples/samd/simplesample_http/sample.h deleted file mode 100644 index ad6e441..0000000 --- a/examples/samd/simplesample_http/sample.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef SAMPLE_H -#define SAMPLE_H - -#ifdef __cplusplus -extern "C" { -#endif - - void sample_run(void); - -#ifdef __cplusplus -} -#endif - -#endif /* SAMPLE_H */ diff --git a/examples/samd/simplesample_http/simplesample_http.c b/examples/samd/simplesample_http/simplesample_http.c deleted file mode 100644 index 5f25db3..0000000 --- a/examples/samd/simplesample_http/simplesample_http.c +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include - -#include -#include -#include "iot_configs.h" - -/* This sample uses the _LL APIs of iothub_client for example purposes. -That does not mean that HTTP only works with the _LL APIs. -Simply changing the using the convenience layer (functions not having _LL) -and removing calls to _DoWork will yield the same results. */ - -#include "AzureIoTHub.h" - - -/*String containing Hostname, Device Id & Device Key in the format: */ -/* "HostName=;DeviceId=;SharedAccessKey=" */ -static const char* connectionString = IOT_CONFIG_CONNECTION_STRING; - -// Define the Model -BEGIN_NAMESPACE(WeatherStation); - -DECLARE_MODEL(ContosoAnemometer, -WITH_DATA(ascii_char_ptr, DeviceId), -WITH_DATA(int, WindSpeed), -WITH_DATA(float, Temperature), -WITH_DATA(float, Humidity), -WITH_ACTION(TurnFanOn), -WITH_ACTION(TurnFanOff), -WITH_ACTION(SetAirResistance, int, Position) -); - -END_NAMESPACE(WeatherStation); - -static char propText[1024]; - -EXECUTE_COMMAND_RESULT TurnFanOn(ContosoAnemometer* device) -{ - (void)device; - (void)printf("Turning fan on.\r\n"); - return EXECUTE_COMMAND_SUCCESS; -} - -EXECUTE_COMMAND_RESULT TurnFanOff(ContosoAnemometer* device) -{ - (void)device; - (void)printf("Turning fan off.\r\n"); - return EXECUTE_COMMAND_SUCCESS; -} - -EXECUTE_COMMAND_RESULT SetAirResistance(ContosoAnemometer* device, int Position) -{ - (void)device; - (void)printf("Setting Air Resistance Position to %d.\r\n", Position); - return EXECUTE_COMMAND_SUCCESS; -} - -void sendCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) -{ - unsigned int messageTrackingId = (unsigned int)(uintptr_t)userContextCallback; - - (void)printf("Message Id: %u Received.\r\n", messageTrackingId); - - (void)printf("Result Call Back Called! Result is: %s \r\n", ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); -} - -static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) -{ - static unsigned int messageTrackingId; - IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); - if (messageHandle == NULL) - { - printf("unable to create a new IoTHubMessage\r\n"); - } - else - { - if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)(uintptr_t)messageTrackingId) != IOTHUB_CLIENT_OK) - { - printf("failed to hand over the message to IoTHubClient"); - } - else - { - printf("IoTHubClient accepted the message for delivery\r\n"); - } - IoTHubMessage_Destroy(messageHandle); - } - free((void*)buffer); - messageTrackingId++; -} - -/*this function "links" IoTHub to the serialization library*/ -static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) -{ - IOTHUBMESSAGE_DISPOSITION_RESULT result; - const unsigned char* buffer; - size_t size; - if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) - { - printf("unable to IoTHubMessage_GetByteArray\r\n"); - result = IOTHUBMESSAGE_ABANDONED; - } - else - { - /*buffer is not zero terminated*/ - char* temp = malloc(size + 1); - if (temp == NULL) - { - printf("failed to malloc\r\n"); - result = IOTHUBMESSAGE_ABANDONED; - } - else - { - EXECUTE_COMMAND_RESULT executeCommandResult; - - (void)memcpy(temp, buffer, size); - temp[size] = '\0'; - executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); - result = - (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : - (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : - IOTHUBMESSAGE_REJECTED; - free(temp); - } - } - return result; -} - -void simplesample_http_run(void) -{ - if (platform_init() != 0) - { - printf("Failed to initialize the platform.\r\n"); - } - else - { - if (serializer_init(NULL) != SERIALIZER_OK) - { - (void)printf("Failed on serializer_init\r\n"); - } - else - { - IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol); - int avgWindSpeed = 10; - float minTemperature = 20.0; - float minHumidity = 60.0; - - srand((unsigned int)time(NULL)); - - if (iotHubClientHandle == NULL) - { - (void)printf("Failed on IoTHubClient_LL_Create\r\n"); - } - else - { - // Because it can poll "after 9 seconds" polls will happen - // effectively at ~10 seconds. - // Note that for scalabilty, the default value of minimumPollingTime - // is 25 minutes. For more information, see: - // https://azure.microsoft.com/documentation/articles/iot-hub-devguide/#messaging - unsigned int minimumPollingTime = 9; - ContosoAnemometer* myWeather; - - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) - { - printf("failure to set option \"MinimumPollingTime\"\r\n"); - } - -#ifdef SET_TRUSTED_CERT_IN_SAMPLES - // For mbed add the certificate information - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) - { - (void)printf("failure to set option \"TrustedCerts\"\r\n"); - } -#endif // SET_TRUSTED_CERT_IN_SAMPLES - - myWeather = CREATE_MODEL_INSTANCE(WeatherStation, ContosoAnemometer); - if (myWeather == NULL) - { - (void)printf("Failed on CREATE_MODEL_INSTANCE\r\n"); - } - else - { - if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK) - { - printf("unable to IoTHubClient_SetMessageCallback\r\n"); - } - else - { - myWeather->DeviceId = "myFirstDevice"; - myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2); - myWeather->Temperature = minTemperature + (rand() % 10); - myWeather->Humidity = minHumidity + (rand() % 20); - { - unsigned char* destination; - size_t destinationSize; - if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->WindSpeed, myWeather->Temperature, myWeather->Humidity) != CODEFIRST_OK) - { - (void)printf("Failed to serialize\r\n"); - } - else - { - IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); - if (messageHandle == NULL) - { - printf("unable to create a new IoTHubMessage\r\n"); - } - else - { - MAP_HANDLE propMap = IoTHubMessage_Properties(messageHandle); - (void)sprintf_s(propText, sizeof(propText), myWeather->Temperature > 28 ? "true" : "false"); - if (Map_AddOrUpdate(propMap, "temperatureAlert", propText) != MAP_OK) - { - printf("ERROR: Map_AddOrUpdate Failed!\r\n"); - } - - if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)1) != IOTHUB_CLIENT_OK) - { - printf("failed to hand over the message to IoTHubClient"); - } - else - { - printf("IoTHubClient accepted the message for delivery\r\n"); - } - - IoTHubMessage_Destroy(messageHandle); - } - free(destination); - } - } - - /* wait for commands */ - while (1) - { - IoTHubClient_LL_DoWork(iotHubClientHandle); - ThreadAPI_Sleep(100); - } - } - - DESTROY_MODEL_INSTANCE(myWeather); - } - IoTHubClient_LL_Destroy(iotHubClientHandle); - } - serializer_deinit(); - } - platform_deinit(); - } -} - -void sample_run(void) -{ - simplesample_http_run(); -} - diff --git a/examples/samd/simplesample_http/simplesample_http.h b/examples/samd/simplesample_http/simplesample_http.h deleted file mode 100644 index baf6975..0000000 --- a/examples/samd/simplesample_http/simplesample_http.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef SIMPLESAMPLEHTTP_H -#define SIMPLESAMPLEHTTP_H - -#ifdef __cplusplus -extern "C" { -#endif - - void simplesample_http_run(void); - -#ifdef __cplusplus -} -#endif - -#endif /* SIMPLESAMPLEHTTP_H */ diff --git a/examples/samd/simplesample_http/simplesample_http.ino b/examples/samd/simplesample_http/simplesample_http.ino deleted file mode 100644 index fb90b25..0000000 --- a/examples/samd/simplesample_http/simplesample_http.ino +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -// Please use an Arduino IDE 1.6.8 or greater - -// You must set the device id, device key, IoT Hub name and IotHub suffix in -// iot_configs.h -#include "iot_configs.h" - -#include -#if defined(IOT_CONFIG_MQTT) - #include -#elif defined(IOT_CONFIG_HTTP) - #include -#endif - -#include "sample.h" -#include "samd/sample_init.h" - -static char ssid[] = IOT_CONFIG_WIFI_SSID; -static char pass[] = IOT_CONFIG_WIFI_PASSWORD; - -void setup() { - sample_init(ssid, pass); -} - -// Azure IoT samples contain their own loops, so only run them once -static bool done = false; -void loop() { - if (!done) - { - // Run the sample - // You must set the device id, device key, IoT Hub name and IotHub suffix in - // iot_configs.h - sample_run(); - done = true; - } - else - { - delay(500); - } -} - diff --git a/keywords.txt b/keywords.txt index 64f59fc..cd3a883 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,5 +1,5 @@ ####################################### -# Syntax Coloring Map For WiFi +# Syntax Coloring Map For AzureIoTUtility ####################################### ####################################### diff --git a/library.properties b/library.properties index ba3a3e2..0073b10 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=AzureIoTUtility -version=1.0.45 +version=1.0.46 author=Microsoft maintainer=Microsoft sentence=Azure C shared utility library for Arduino. For the Arduino MKR1000 or Zero and WiFi Shield 101, Adafruit Huzzah and Feather M0, or SparkFun Thing. paragraph=Microsoft port of the Azure C Shared Utility. Together with AzureIoTHub, it allows you to use your Arduino with the Azure IoT Hub. See readme.md for more details. Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. category=Communication url=https://github.com/Azure/azure-iot-arduino-utility -architectures=samd,esp8266 +architectures=samd,esp8266,esp32 diff --git a/src/AzureIoTUtility.h b/src/AzureIoTUtility.h index 7fa0493..28383fe 100644 --- a/src/AzureIoTUtility.h +++ b/src/AzureIoTUtility.h @@ -4,9 +4,10 @@ #ifndef AZUREIOTUTILITY_H #define AZUREIOTUTILITY_H +#include "azure_c_shared_utility/platform.h" #include "azure_c_shared_utility/lock.h" #include "azure_c_shared_utility/threadapi.h" -#define AzureIoTUtilityVersion "1.0.45" +#define AzureIoTUtilityVersion "1.0.46" #endif //AZUREIOTUTILITY_H diff --git a/src/adapters/tlsio_arduino.c b/src/adapters/tlsio_arduino.c index 1559265..db59a34 100644 --- a/src/adapters/tlsio_arduino.c +++ b/src/adapters/tlsio_arduino.c @@ -2,510 +2,549 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include -#include #include -#include -#include -#include -#include "azure_c_shared_utility/gballoc.h" +#include + #include "tlsio_arduino.h" #include "sslClient_arduino.h" -#include "azure_c_shared_utility/socketio.h" -#include "azure_c_shared_utility/optimize_size.h" +#include "azure_c_shared_utility/xio.h" #include "azure_c_shared_utility/xlogging.h" -#include "azure_c_shared_utility/platform.h" -#include "azure_c_shared_utility/threadapi.h" -#include "azure_c_shared_utility/optionhandler.h" +#include "azure_c_shared_utility/singlylinkedlist.h" #include "azure_c_shared_utility/tlsio_options.h" - -/* Codes_SRS_TLSIO_ARDUINO_21_001: [ The tlsio_arduino shall implement and export all the Concrete functions in the VTable IO_INTERFACE_DESCRIPTION defined in the `xio.h`. ]*/ -/* Codes_SRS_TLSIO_ARDUINO_21_002: [ The tlsio_arduino shall report the open operation status using the IO_OPEN_RESULT enumerator defined in the `xio.h`.]*/ -/* Codes_SRS_TLSIO_ARDUINO_21_003: [ The tlsio_arduino shall report the send operation status using the IO_SEND_RESULT enumerator defined in the `xio.h`. ]*/ -/* Codes_SRS_TLSIO_ARDUINO_21_004: [ The tlsio_arduino shall call the callbacks functions defined in the `xio.h`. ]*/ -#include "azure_c_shared_utility/xio.h" - -/* Codes_SRS_TLSIO_ARDUINO_21_005: [ The tlsio_arduino shall received the connection information using the TLSIO_CONFIG structure defined in `tlsio.h`. ]*/ +#include "azure_c_shared_utility/strings.h" #include "azure_c_shared_utility/tlsio.h" - -/* Codes_SRS_TLSIO_ARDUINO_21_001: [ The tlsio_arduino shall implement and export all the Concrete functions in the VTable IO_INTERFACE_DESCRIPTION defined in the `xio.h`. ]*/ -CONCRETE_IO_HANDLE tlsio_arduino_create(void* io_create_parameters); -void tlsio_arduino_destroy(CONCRETE_IO_HANDLE tls_io); -int tlsio_arduino_open(CONCRETE_IO_HANDLE tls_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context); -int tlsio_arduino_close(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context); -int tlsio_arduino_send(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context); -void tlsio_arduino_dowork(CONCRETE_IO_HANDLE tls_io); -int tlsio_arduino_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value); -OPTIONHANDLER_HANDLE tlsio_arduino_retrieveoptions(CONCRETE_IO_HANDLE tls_io); - -static const IO_INTERFACE_DESCRIPTION tlsio_handle_interface_description = +typedef struct { - tlsio_arduino_retrieveoptions, - tlsio_arduino_create, - tlsio_arduino_destroy, - tlsio_arduino_open, - tlsio_arduino_close, - tlsio_arduino_send, - tlsio_arduino_dowork, - tlsio_arduino_setoption -}; - -/* Codes_SRS_TLSIO_ARDUINO_21_027: [ The tlsio_arduino_open shall set the tlsio to try to open the connection for 10 times before assuming that connection failed. ]*/ -#define MAX_TLS_OPENING_RETRY 10 -/* Codes_SRS_TLSIO_ARDUINO_21_044: [ The tlsio_arduino_close shall set the tlsio to try to close the connection for 10 times before assuming that close connection failed. ]*/ -#define MAX_TLS_CLOSING_RETRY 10 -#define RECEIVE_BUFFER_SIZE 128 + unsigned char* bytes; + size_t size; + size_t unsent_size; + ON_SEND_COMPLETE on_send_complete; + void* callback_context; +} PENDING_TRANSMISSION; -#define CallErrorCallback() do { if (tlsio_instance->on_io_error != NULL) (void)tlsio_instance->on_io_error(tlsio_instance->on_io_error_context); } while((void)0,0) -#define CallOpenCallback(status) do { if (tlsio_instance->on_io_open_complete != NULL) (void)tlsio_instance->on_io_open_complete(tlsio_instance->on_io_open_complete_context, status); } while((void)0,0) -#define CallCloseCallback() do { if (tlsio_instance->on_io_close_complete != NULL) (void)tlsio_instance->on_io_close_complete(tlsio_instance->on_io_close_complete_context); } while((void)0,0) +#define MAX_VALID_PORT 0xffff +// The TLSIO_RECEIVE_BUFFER_SIZE has very little effect on performance, and is kept small +// to minimize memory consumption. +#define TLSIO_RECEIVE_BUFFER_SIZE 64 -typedef struct ArduinoTLS_tag +typedef enum TLSIO_STATE_TAG +{ + TLSIO_STATE_CLOSED, + TLSIO_STATE_OPENING_WAITING_DNS, + TLSIO_STATE_OPENING_WAITING_SOCKET, + TLSIO_STATE_OPENING_WAITING_SSL, + TLSIO_STATE_OPEN, + TLSIO_STATE_ERROR, +} TLSIO_STATE; + +typedef struct TLS_IO_INSTANCE_TAG { - ON_IO_OPEN_COMPLETE on_io_open_complete; - void* on_io_open_complete_context; - ON_BYTES_RECEIVED on_bytes_received; - void* on_bytes_received_context; - ON_IO_ERROR on_io_error; + ON_IO_OPEN_COMPLETE on_open_complete; + void* on_bytes_received_context; void* on_io_error_context; - - ON_IO_CLOSE_COMPLETE on_io_close_complete; - void* on_io_close_complete_context; - + void* on_open_complete_context; + TLSIO_STATE tlsio_state; + STRING_HANDLE hostname; uint32_t remote_addr; uint16_t port; - TLSIO_ARDUINO_STATE state; - int countTry; + SINGLYLINKEDLIST_HANDLE pending_transmission_list; TLSIO_OPTIONS options; -} ArduinoTLS; +} TLS_IO_INSTANCE; -/* Codes_SRS_TLSIO_ARDUINO_21_008: [ The tlsio_arduino_get_interface_description shall return the VTable IO_INTERFACE_DESCRIPTION. ]*/ -const IO_INTERFACE_DESCRIPTION* tlsio_arduino_get_interface_description(void) +static bool is_an_opening_state(TLSIO_STATE state) { - return &tlsio_handle_interface_description; + return state == TLSIO_STATE_OPENING_WAITING_DNS || + state == TLSIO_STATE_OPENING_WAITING_SOCKET || + state == TLSIO_STATE_OPENING_WAITING_SSL; } +/* Codes_SRS_TLSIO_30_005: [ The phrase "enter TLSIO_STATE_EXT_ERROR" means the adapter shall call the on_io_error function and pass the on_io_error_context that was supplied in tlsio_open_async. ]*/ +static void enter_tlsio_error_state(TLS_IO_INSTANCE* tls_io_instance) +{ + if (tls_io_instance->tlsio_state != TLSIO_STATE_ERROR) + { + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + tls_io_instance->on_io_error(tls_io_instance->on_io_error_context); + } +} -/* Codes_SRS_TLSIO_ARDUINO_21_020: [ If tlsio_arduino_create get success to create the tlsio instance, it shall set the tlsio state as TLSIO_ARDUINO_STATE_CLOSED. ]*/ -TLSIO_ARDUINO_STATE tlsio_arduino_get_state(CONCRETE_IO_HANDLE tlsio_handle) +/* Codes_SRS_TLSIO_30_005: [ When the adapter enters TLSIO_STATE_EXT_ERROR it shall call the on_io_error function and pass the on_io_error_context that were supplied in tlsio_open . ]*/ +static void enter_open_error_state(TLS_IO_INSTANCE* tls_io_instance) { - TLSIO_ARDUINO_STATE result; - ArduinoTLS* tlsio_instance = (ArduinoTLS*)tlsio_handle; + // save instance variables in case the framework destroys this object before we exit + ON_IO_OPEN_COMPLETE on_open_complete = tls_io_instance->on_open_complete; + void* on_open_complete_context = tls_io_instance->on_open_complete_context; + enter_tlsio_error_state(tls_io_instance); + on_open_complete(on_open_complete_context, IO_OPEN_ERROR); +} - if (tlsio_handle == NULL) - result = TLSIO_ARDUINO_STATE_NULL; - else - result = tlsio_instance->state; +// Return true if a message was available to remove +static bool process_and_destroy_head_message(TLS_IO_INSTANCE* tls_io_instance, IO_SEND_RESULT send_result) +{ + bool result; + LIST_ITEM_HANDLE head_pending_io; + if (send_result == IO_SEND_ERROR) + { + /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, the tlsio_dowork shall call the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/ + enter_tlsio_error_state(tls_io_instance); + } + head_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_transmission_list); + if (head_pending_io != NULL) + { + PENDING_TRANSMISSION* head_message = (PENDING_TRANSMISSION*)singlylinkedlist_item_get_value(head_pending_io); + + if (singlylinkedlist_remove(tls_io_instance->pending_transmission_list, head_pending_io) != 0) + { + // This particular situation is a bizarre and unrecoverable internal error + /* Codes_SRS_TLSIO_30_094: [ If the send process encounters an internal error or calls on_send_complete with IO_SEND_ERROR due to either failure or timeout, it shall also call on_io_error and pass in the associated on_io_error_context. ]*/ + enter_tlsio_error_state(tls_io_instance); + LogError("Failed to remove message from list"); + } + // on_send_complete is checked for NULL during PENDING_TRANSMISSION creation + /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, the tlsio_dowork shall call the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/ + head_message->on_send_complete(head_message->callback_context, send_result); + + free(head_message->bytes); + free(head_message); + result = true; + } + else + { + result = false; + } return result; } +static void internal_close(TLS_IO_INSTANCE* tls_io_instance) +{ + /* Codes_SRS_TLSIO_30_009: [ The phrase "enter TLSIO_STATE_EXT_CLOSING" means the adapter shall iterate through any unsent messages in the queue and shall delete each message after calling its on_send_complete with the associated callback_context and IO_SEND_CANCELLED. ]*/ + /* Codes_SRS_TLSIO_30_006: [ The phrase "enter TLSIO_STATE_EXT_CLOSED" means the adapter shall forcibly close any existing connections then call the on_io_close_complete function and pass the on_io_close_complete_context that was supplied in tlsio_close_async. ]*/ + /* Codes_SRS_TLSIO_30_051: [ On success, if the underlying TLS does not support asynchronous closing, then the adapter shall enter TLSIO_STATE_EXT_CLOSED immediately after entering TLSIO_STATE_EX_CLOSING. ]*/ + if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN) + { + sslClient_stop(); + } + + while (process_and_destroy_head_message(tls_io_instance, IO_SEND_CANCELLED)); + // singlylinkedlist_destroy gets called in the main destroy + + tls_io_instance->on_bytes_received = NULL; + tls_io_instance->on_io_error = NULL; + tls_io_instance->on_bytes_received_context = NULL; + tls_io_instance->on_io_error_context = NULL; + tls_io_instance->tlsio_state = TLSIO_STATE_CLOSED; + tls_io_instance->on_open_complete = NULL; + tls_io_instance->on_open_complete_context = NULL; +} -/* Codes_SRS_TLSIO_ARDUINO_21_005: [ The tlsio_arduino shall received the connection information using the TLSIO_CONFIG structure defined in `tlsio.h`. ]*/ -/* Codes_SRS_TLSIO_ARDUINO_21_009: [ The tlsio_arduino_create shall create a new instance of the tlsio for Arduino. ]*/ -/* Codes_SRS_TLSIO_ARDUINO_21_017: [ The tlsio_arduino_create shall receive the connection configuration (TLSIO_CONFIG). ]*/ -CONCRETE_IO_HANDLE tlsio_arduino_create(void* io_create_parameters) +static void tlsio_arduino_destroy(CONCRETE_IO_HANDLE tls_io) { - ArduinoTLS* tlsio_instance; - if (io_create_parameters == NULL) + if (tls_io == NULL) { - LogError("Invalid TLS parameters."); - tlsio_instance = NULL; + /* Codes_SRS_TLSIO_30_020: [ If tlsio_handle is NULL, tlsio_destroy shall do nothing. ]*/ + LogError("NULL tlsio"); } else { - /* Codes_SRS_TLSIO_ARDUINO_21_011: [ The tlsio_arduino_create shall allocate memory to control the tlsio instance. ]*/ - tlsio_instance = (ArduinoTLS*)malloc(sizeof(ArduinoTLS)); - if (tlsio_instance == NULL) + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + if (tls_io_instance->tlsio_state != TLSIO_STATE_CLOSED) { - /* Codes_SRS_TLSIO_ARDUINO_21_012: [ If there is not enough memory to control the tlsio, the tlsio_arduino_create shall return NULL as the handle. ]*/ - LogError("There is not enough memory to create the TLS instance."); - /* return as is */ + /* Codes_SRS_TLSIO_30_022: [ If the adapter is in any state other than TLSIO_STATE_EX_CLOSED when tlsio_destroy is called, the adapter shall enter TLSIO_STATE_EX_CLOSING and then enter TLSIO_STATE_EX_CLOSED before completing the destroy process. ]*/ + LogError("tlsio_arduino_destroy called while not in TLSIO_STATE_CLOSED."); + internal_close(tls_io_instance); } - else + /* Codes_SRS_TLSIO_30_021: [ The tlsio_destroy shall release all allocated resources and then release tlsio_handle. ]*/ + if (tls_io_instance->hostname != NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_005: [ The tlsio_arduino shall received the connection information using the TLSIO_CONFIG structure defined in `tlsio.h`. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_017: [ The tlsio_arduino_create shall receive the connection configuration (TLSIO_CONFIG). ]*/ - TLSIO_CONFIG* tlsio_config = (TLSIO_CONFIG*)io_create_parameters; - - // Arduino tlsio does not support any options - tlsio_options_initialize(&tlsio_instance->options, TLSIO_OPTION_BIT_NONE); - - /* Codes_SRS_TLSIO_ARDUINO_21_015: [ The tlsio_arduino_create shall set 10 seconds for the sslClient timeout. ]*/ - sslClient_setTimeout(10000); - - /* Codes_SRS_TLSIO_ARDUINO_21_016: [ The tlsio_arduino_create shall initialize all callback pointers as NULL. ]*/ - tlsio_instance->on_io_open_complete = NULL; - tlsio_instance->on_io_open_complete_context = NULL; - tlsio_instance->on_bytes_received = NULL; - tlsio_instance->on_bytes_received_context = NULL; - tlsio_instance->on_io_error = NULL; - tlsio_instance->on_io_error_context = NULL; - tlsio_instance->on_io_close_complete = NULL; - tlsio_instance->on_io_close_complete_context = NULL; - - /* Codes_SRS_TLSIO_ARDUINO_21_020: [ If tlsio_arduino_create get success to create the tlsio instance, it shall set the tlsio state as TLSIO_ARDUINO_STATE_CLOSED. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_CLOSED; - - /* Codes_SRS_TLSIO_ARDUINO_21_018: [ The tlsio_arduino_create shall convert the provide hostName to an IP address. ]*/ - if (sslClient_hostByName(tlsio_config->hostname, &(tlsio_instance->remote_addr))) - { - tlsio_instance->port = (uint16_t)tlsio_config->port; - } - else - { - /* Codes_SRS_TLSIO_ARDUINO_21_019: [ If the WiFi cannot find the IP for the hostName, the tlsio_arduino_create shall destroy the sslClient and tlsio instances and return NULL as the handle. ]*/ - LogError("Host %s not found.", tlsio_config->hostname); - free(tlsio_instance); - tlsio_instance = NULL; - } + STRING_delete(tls_io_instance->hostname); } - } - /* Codes_SRS_TLSIO_ARDUINO_21_010: [ The tlsio_arduino_create shall return a non-NULL handle on success. ]*/ - return (CONCRETE_IO_HANDLE)tlsio_instance; + tlsio_options_release_resources(&tls_io_instance->options); + + if (tls_io_instance->pending_transmission_list != NULL) + { + /* Pending messages were cleared in internal_close */ + singlylinkedlist_destroy(tls_io_instance->pending_transmission_list); + } + + free(tls_io_instance); + } } -/* Codes_SRS_TLSIO_ARDUINO_21_021: [ The tlsio_arduino_destroy shall destroy a created instance of the tlsio for Arduino identified by the CONCRETE_IO_HANDLE. ]*/ -void tlsio_arduino_destroy(CONCRETE_IO_HANDLE tlsio_handle) +/* Codes_SRS_TLSIO_30_010: [ The tlsio_create shall allocate and initialize all necessary resources and return an instance of the tlsio_arduino_compact. ]*/ +static CONCRETE_IO_HANDLE tlsio_arduino_create(void* io_create_parameters) { - ArduinoTLS* tlsio_instance = (ArduinoTLS*)tlsio_handle; + TLS_IO_INSTANCE* result; - if (tlsio_handle == NULL) + if (io_create_parameters == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_024: [ If the tlsio_handle is NULL, the tlsio_arduino_destroy shall not do anything. ]*/ - LogError("NULL TLS handle."); + /* Codes_SRS_TLSIO_30_013: [ If the io_create_parameters value is NULL, tlsio_create shall log an error and return NULL. ]*/ + LogError("NULL tls_io_config"); + result = NULL; } else { - if ((tlsio_instance->state == TLSIO_ARDUINO_STATE_OPENING) || - (tlsio_instance->state == TLSIO_ARDUINO_STATE_OPEN) || - (tlsio_instance->state == TLSIO_ARDUINO_STATE_CLOSING)) + /* Codes_SRS_TLSIO_30_012: [ The tlsio_create shall receive the connection configuration as a TLSIO_CONFIG* in io_create_parameters. ]*/ + TLSIO_CONFIG* tls_io_config = (TLSIO_CONFIG*)io_create_parameters; + if (tls_io_config->hostname == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_026: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPENING, TLSIO_ARDUINO_STATE_OPEN, or TLSIO_ARDUINO_STATE_CLOSING, the tlsio_arduino_destroy shall close destroy the tlsio, but log an error. ]*/ - LogError("TLS destroyed with a SSL connection still active."); + /* Codes_SRS_TLSIO_30_014: [ If the hostname member of io_create_parameters value is NULL, tlsio_create shall log an error and return NULL. ]*/ + LogError("NULL tls_io_config->hostname"); + result = NULL; + } + else + { + if (tls_io_config->port < 0 || tls_io_config->port > MAX_VALID_PORT) + { + /* Codes_SRS_TLSIO_30_015: [ If the port member of io_create_parameters value is less than 0 or greater than 0xffff, tlsio_create shall log an error and return NULL. ]*/ + LogError("tls_io_config->port out of range"); + result = NULL; + } + else + { + result = malloc(sizeof(TLS_IO_INSTANCE)); + if (result == NULL) + { + /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/ + LogError("malloc failed"); + } + else + { + memset(result, 0, sizeof(TLS_IO_INSTANCE)); + result->hostname = NULL; + result->port = (uint16_t)tls_io_config->port; + result->tlsio_state = TLSIO_STATE_CLOSED; + result->hostname = NULL; + result->pending_transmission_list = NULL; + tlsio_options_initialize(&result->options, TLSIO_OPTION_BIT_NONE); + /* Codes_SRS_TLSIO_30_016: [ tlsio_create shall make a copy of the hostname member of io_create_parameters to allow deletion of hostname immediately after the call. ]*/ + if (NULL == (result->hostname = STRING_construct(tls_io_config->hostname))) + { + /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/ + LogError("Failed to allocate hostname"); + tlsio_arduino_destroy(result); + result = NULL; + } + else + { + // Create the message queue + result->pending_transmission_list = singlylinkedlist_create(); + if (result->pending_transmission_list == NULL) + { + /* Codes_SRS_TLSIO_30_011: [ If any resource allocation fails, tlsio_create shall return NULL. ]*/ + LogError("Failed singlylinkedlist_create"); + tlsio_arduino_destroy(result); + result = NULL; + } + } + } + } } - - tlsio_options_release_resources(&tlsio_instance->options); - /* Codes_SRS_TLSIO_ARDUINO_21_022: [ The tlsio_arduino_destroy shall free the memory allocated for tlsio_instance. ]*/ - free(tlsio_instance); } + + return (CONCRETE_IO_HANDLE)result; } -/* Codes_SRS_TLSIO_ARDUINO_21_026: [ The tlsio_arduino_open shall star the process to open the ssl connection with the host provided in the tlsio_arduino_create. ]*/ -int tlsio_arduino_open( - CONCRETE_IO_HANDLE tlsio_handle, - ON_IO_OPEN_COMPLETE on_io_open_complete, - void* on_io_open_complete_context, - ON_BYTES_RECEIVED on_bytes_received, - void* on_bytes_received_context, - ON_IO_ERROR on_io_error, - void* on_io_error_context) +static int tlsio_arduino_open_async(CONCRETE_IO_HANDLE tls_io, + ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, + ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, + ON_IO_ERROR on_io_error, void* on_io_error_context) { - int result; - ArduinoTLS* tlsio_instance = (ArduinoTLS*)tlsio_handle; - if (tlsio_handle == NULL) + int result; + if (on_io_open_complete == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_036: [ If the tlsio_handle is NULL, the tlsio_arduino_open shall not do anything, and return _LINE_. ]*/ - LogError("NULL TLS handle."); + /* Codes_SRS_TLSIO_30_031: [ If the on_io_open_complete parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ + LogError("Required parameter on_io_open_complete is NULL"); result = __FAILURE__; } else { - /* Codes_SRS_TLSIO_ARDUINO_21_004: [ The tlsio_arduino shall call the callbacks functions defined in the `xio.h`. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_006: [ The tlsio_arduino shall return the status of all async operations using the callbacks. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_007: [ If the callback function is set as NULL. The tlsio_arduino shall not call anything. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_028: [ The tlsio_arduino_open shall store the provided on_io_open_complete callback function address. ]*/ - tlsio_instance->on_io_open_complete = on_io_open_complete; - /* Codes_SRS_TLSIO_ARDUINO_21_029: [ The tlsio_arduino_open shall store the provided on_io_open_complete_context handle. ]*/ - tlsio_instance->on_io_open_complete_context = on_io_open_complete_context; - - /* Codes_SRS_TLSIO_ARDUINO_21_030: [ The tlsio_arduino_open shall store the provided on_bytes_received callback function address. ]*/ - tlsio_instance->on_bytes_received = on_bytes_received; - /* Codes_SRS_TLSIO_ARDUINO_21_031: [ The tlsio_arduino_open shall store the provided on_bytes_received_context handle. ]*/ - tlsio_instance->on_bytes_received_context = on_bytes_received_context; - - /* Codes_SRS_TLSIO_ARDUINO_21_032: [ The tlsio_arduino_open shall store the provided on_io_error callback function address. ]*/ - tlsio_instance->on_io_error = on_io_error; - /* Codes_SRS_TLSIO_ARDUINO_21_033: [ The tlsio_arduino_open shall store the provided on_io_error_context handle. ]*/ - tlsio_instance->on_io_error_context = on_io_error_context; - - if ((tlsio_instance->state == TLSIO_ARDUINO_STATE_ERROR) || - (tlsio_instance->state == TLSIO_ARDUINO_STATE_OPENING) || - (tlsio_instance->state == TLSIO_ARDUINO_STATE_OPEN) || - (tlsio_instance->state == TLSIO_ARDUINO_STATE_CLOSING)) + if (tls_io == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_035: [ If the tlsio state is TLSIO_ARDUINO_STATE_ERROR, TLSIO_ARDUINO_STATE_OPENING, TLSIO_ARDUINO_STATE_OPEN, or TLSIO_ARDUINO_STATE_CLOSING, the tlsio_arduino_open shall set the tlsio state as TLSIO_ARDUINO_STATE_ERROR, and return _LINE_. ]*/ - LogError("Try to open a connection with an already opened TLS."); - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; + /* Codes_SRS_TLSIO_30_030: [ If the tlsio_handle parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ result = __FAILURE__; + LogError("NULL tlsio"); } else { - if (sslClient_connected()) + if (on_bytes_received == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_037: [ If the ssl client is connected, the tlsio_arduino_open shall change the state to TLSIO_ARDUINO_STATE_ERROR, log the error, and return _LINE_. ]*/ - LogError("No SSL clients available."); - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; + /* Codes_SRS_TLSIO_30_032: [ If the on_bytes_received parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ + LogError("Required parameter on_bytes_received is NULL"); result = __FAILURE__; } - /* Codes_SRS_TLSIO_ARDUINO_21_027: [ The tlsio_arduino_open shall set the tlsio to try to open the connection for 10 times before assuming that connection failed. ]*/ - else if (sslClient_connect(tlsio_instance->remote_addr, tlsio_instance->port)) - { - /* Codes_SRS_TLSIO_ARDUINO_21_034: [ If tlsio_arduino_open get success to start the process to open the ssl connection, it shall set the tlsio state as TLSIO_ARDUINO_STATE_OPENING, and return 0. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_OPENING; - tlsio_instance->countTry = MAX_TLS_OPENING_RETRY; - result = 0; - } else { - /* Codes_SRS_TLSIO_ARDUINO_21_038: [ If tlsio_arduino_open failed to start the process to open the ssl connection, it shall set the tlsio state as TLSIO_ARDUINO_STATE_ERROR, and return _LINE_. ]*/ - LogError("TLS failed to start the connection process."); - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; - result = __FAILURE__; + if (on_io_error == NULL) + { + /* Codes_SRS_TLSIO_30_033: [ If the on_io_error parameter is NULL, tlsio_open shall log an error and return FAILURE. ]*/ + LogError("Required parameter on_io_error is NULL"); + result = __FAILURE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (tls_io_instance->tlsio_state != TLSIO_STATE_CLOSED) + { + /* Codes_SRS_TLSIO_30_037: [ If the adapter is in any state other than TLSIO_STATE_EXT_CLOSED when tlsio_open is called, it shall log an error, and return FAILURE. ]*/ + LogError("Invalid tlsio_state. Expected state is TLSIO_STATE_CLOSED."); + result = __FAILURE__; + } + else + { + /* Codes_SRS_TLSIO_30_034: [ The tlsio_open shall store the provided on_bytes_received, on_bytes_received_context, on_io_error, on_io_error_context, on_io_open_complete, and on_io_open_complete_context parameters for later use as specified and tested per other line entries in this document. ]*/ + tls_io_instance->on_bytes_received = on_bytes_received; + tls_io_instance->on_bytes_received_context = on_bytes_received_context; + + tls_io_instance->on_io_error = on_io_error; + tls_io_instance->on_io_error_context = on_io_error_context; + + tls_io_instance->on_open_complete = on_io_open_complete; + tls_io_instance->on_open_complete_context = on_io_open_complete_context; + + /* Codes_SRS_TLSIO_30_035: [ On tlsio_open success the adapter shall enter TLSIO_STATE_EX_OPENING and return 0. ]*/ + // All the real work happens in dowork + tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_WAITING_DNS; + result = 0; + } + } } } + /* Codes_SRS_TLSIO_30_039: [ On failure, tlsio_open_async shall not call on_io_open_complete. ]*/ } - if (result != 0) - { - if (on_io_open_complete != NULL) - { - /* Codes_SRS_TLSIO_ARDUINO_21_002: [ The tlsio_arduino shall report the open operation status using the IO_OPEN_RESULT enumerator defined in the `xio.h`.]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_039: [ If the tlsio_arduino_open failed to open the tls connection, and the on_io_open_complete callback was provided, it shall call the on_io_open_complete with IO_OPEN_ERROR. ]*/ - (void)on_io_open_complete(on_io_open_complete_context, IO_OPEN_ERROR); - } - if (on_io_error != NULL) - { - /* Codes_SRS_TLSIO_ARDUINO_21_040: [ If the tlsio_arduino_open failed to open the tls connection, and the on_io_error callback was provided, it shall call the on_io_error. ]*/ - (void)on_io_error(on_io_error_context); - } - } - else - { - /* Codes_SRS_TLSIO_ARDUINO_21_041: [ If the tlsio_arduino_open get success opening the tls connection, it shall call the tlsio_arduino_dowork. ]*/ - tlsio_arduino_dowork(tlsio_handle); - } return result; } -/* Codes_SRS_TLSIO_ARDUINO_21_043: [ The tlsio_arduino_close shall start the process to close the ssl connection. ]*/ -int tlsio_arduino_close(CONCRETE_IO_HANDLE tlsio_handle, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context) +// This implementation does not have asynchronous close, but uses the _async name for consistencty with the spec +static int tlsio_arduino_close_async(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) { int result; - ArduinoTLS* tlsio_instance = (ArduinoTLS*)tlsio_handle; - if (tlsio_handle == NULL) + if (tls_io == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_049: [ If the tlsio_handle is NULL, the tlsio_arduino_close shall not do anything, and return _LINE_. ]*/ - LogError("NULL TLS handle."); + /* Codes_SRS_TLSIO_30_050: [ If the tlsio_handle parameter is NULL, tlsio_arduino_close_async shall log an error and return FAILURE. ]*/ + LogError("NULL tlsio"); result = __FAILURE__; } else { - /* Codes_SRS_TLSIO_ARDUINO_21_045: [ The tlsio_arduino_close shall store the provided on_io_close_complete callback function address. ]*/ - tlsio_instance->on_io_close_complete = on_io_close_complete; - /* Codes_SRS_TLSIO_ARDUINO_21_046: [ The tlsio_arduino_close shall store the provided on_io_close_complete_context handle. ]*/ - tlsio_instance->on_io_close_complete_context = on_io_close_complete_context; - - if ((tlsio_instance->state == TLSIO_ARDUINO_STATE_CLOSED) || (tlsio_instance->state == TLSIO_ARDUINO_STATE_ERROR)) - { - /* Codes_SRS_TLSIO_ARDUINO_21_079: [ If the tlsio state is TLSIO_ARDUINO_STATE_ERROR, or TLSIO_ARDUINO_STATE_CLOSED, the tlsio_arduino_close shall set the tlsio state as TLSIO_ARDUINO_STATE_CLOSE, and return 0. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_CLOSED; - result = 0; - } - else if ((tlsio_instance->state == TLSIO_ARDUINO_STATE_OPENING) || (tlsio_instance->state == TLSIO_ARDUINO_STATE_CLOSING)) + if (on_io_close_complete == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_048: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPENING, or TLSIO_ARDUINO_STATE_CLOSING, the tlsio_arduino_close shall set the tlsio state as TLSIO_ARDUINO_STATE_CLOSE, and return _LINE_. ]*/ - LogError("Try to close the connection with an already closed TLS."); - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; + /* Codes_SRS_TLSIO_30_055: [ If the on_io_close_complete parameter is NULL, tlsio_arduino_close_async shall log an error and return FAILURE. ]*/ + LogError("NULL on_io_close_complete"); result = __FAILURE__; } else { - sslClient_stop(); - /* Codes_SRS_TLSIO_ARDUINO_21_047: [ If tlsio_arduino_close get success to start the process to close the ssl connection, it shall set the tlsio state as TLSIO_ARDUINO_STATE_CLOSING, and return 0. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_CLOSING; + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN && + tls_io_instance->tlsio_state != TLSIO_STATE_ERROR) + { + /* Codes_SRS_TLSIO_30_053: [ If the adapter is in any state other than TLSIO_STATE_EXT_OPEN or TLSIO_STATE_EXT_ERROR then tlsio_close_async shall log that tlsio_close_async has been called and then continue normally. ]*/ + // LogInfo rather than LogError because this is an unusual but not erroneous situation + LogInfo("tlsio_arduino_close has been called when in neither TLSIO_STATE_OPEN nor TLSIO_STATE_ERROR."); + } + + if (is_an_opening_state(tls_io_instance->tlsio_state)) + { + /* Codes_SRS_TLSIO_30_057: [ On success, if the adapter is in TLSIO_STATE_EXT_OPENING, it shall call on_io_open_complete with the on_io_open_complete_context supplied in tlsio_open_async and IO_OPEN_CANCELLED. This callback shall be made before changing the internal state of the adapter. ]*/ + tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_CANCELLED); + } + // This adapter does not support asynchronous closing + /* Codes_SRS_TLSIO_30_056: [ On success the adapter shall enter TLSIO_STATE_EX_CLOSING. ]*/ + /* Codes_SRS_TLSIO_30_051: [ On success, if the underlying TLS does not support asynchronous closing, then the adapter shall enter TLSIO_STATE_EX_CLOSED immediately after entering TLSIO_STATE_EX_CLOSING. ]*/ + /* Codes_SRS_TLSIO_30_052: [ On success tlsio_close shall return 0. ]*/ + internal_close(tls_io_instance); + on_io_close_complete(callback_context); result = 0; - /* Codes_SRS_TLSIO_ARDUINO_21_044: [ The tlsio_arduino_close shall set the tlsio to try to close the connection for 10 times before assuming that close connection failed. ]*/ - tlsio_instance->countTry = MAX_TLS_CLOSING_RETRY; - /* Codes_SRS_TLSIO_ARDUINO_21_050: [ If the tlsio_arduino_close get success closing the tls connection, it shall call the tlsio_arduino_dowork. ]*/ - tlsio_arduino_dowork(tlsio_handle); } } + /* Codes_SRS_TLSIO_30_054: [ On failure, the adapter shall not call on_io_close_complete. ]*/ return result; } -/* Codes_SRS_TLSIO_ARDUINO_21_052: [ The tlsio_arduino_send shall send all bytes in a buffer to the ssl connectio. ]*/ -int tlsio_arduino_send(CONCRETE_IO_HANDLE tlsio_handle, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +static void dowork_read(TLS_IO_INSTANCE* tls_io_instance) { - int result; - ArduinoTLS* tlsio_instance = (ArduinoTLS*)tlsio_handle; - - if ((tlsio_handle == NULL) || (buffer == NULL) || (size == 0)) - { - /* Codes_SRS_TLSIO_ARDUINO_21_059: [ If the tlsio_handle is NULL, the tlsio_arduino_send shall not do anything, and return _LINE_. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_060: [ If the buffer is NULL, the tlsio_arduino_send shall not do anything, and return _LINE_. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_061: [ If the size is 0, the tlsio_arduino_send shall not do anything, and return _LINE_. ]*/ - LogError("Invalid parameter"); - result = __FAILURE__; - } - else if (tlsio_instance->state != TLSIO_ARDUINO_STATE_OPEN) - { - /* Codes_SRS_TLSIO_ARDUINO_21_058: [ If the tlsio state is TLSIO_ARDUINO_STATE_ERROR, TLSIO_ARDUINO_STATE_OPENING, TLSIO_ARDUINO_STATE_CLOSING, or TLSIO_ARDUINO_STATE_CLOSED, the tlsio_arduino_send shall call the on_send_complete with IO_SEND_ERROR, and return _LINE_. ]*/ - LogError("TLS is not ready to send data"); - result = __FAILURE__; - } - else + // TRANSFER_BUFFER_SIZE is not very important because if the message is bigger + // then the framework just calls dowork repeatedly until it gets everything. So + // a bigger buffer would just use memory without buying anything. + // Putting this buffer in a small function also allows it to exist on the stack + // rather than adding to heap fragmentation. + uint8_t buffer[TLSIO_RECEIVE_BUFFER_SIZE]; + int rcv_bytes; + + if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN) { - size_t send_result; - size_t send_size = size; - const uint8_t* runBuffer = (const uint8_t *)buffer; - result = __FAILURE__; - /* Codes_SRS_TLSIO_ARDUINO_21_055: [ if the ssl was not able to send all data in the buffer, the tlsio_arduino_send shall call the ssl again to send the remaining bytes. ]*/ - while (send_size > 0) + if (0 == sslClient_connected()) { - send_result = sslClient_write(runBuffer, send_size); - - if (send_result == 0) /* Didn't transmit anything! Failed. */ + enter_tlsio_error_state(tls_io_instance); + } + else + { + while (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN && 0 != sslClient_available()) { - /* Codes_SRS_TLSIO_ARDUINO_21_056: [ if the ssl was not able to send any byte in the buffer, the tlsio_arduino_send shall call the on_send_complete with IO_SEND_ERROR, and return _LINE_. ]*/ - LogError("TLS failed sending data"); - /* Codes_SRS_TLSIO_ARDUINO_21_053: [ The tlsio_arduino_send shall use the provided on_io_send_complete callback function address. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_054: [ The tlsio_arduino_send shall use the provided on_io_send_complete_context handle. ]*/ - if (on_send_complete != NULL) + rcv_bytes = sslClient_read(buffer, TLSIO_RECEIVE_BUFFER_SIZE); + + if (rcv_bytes > 0) { - /* Codes_SRS_TLSIO_ARDUINO_21_003: [ The tlsio_arduino shall report the send operation status using the IO_SEND_RESULT enumerator defined in the `xio.h`. ]*/ - on_send_complete(callback_context, IO_SEND_ERROR); + // tls_io_instance->on_bytes_received was already checked for NULL + // in the call to tlsio_arduino_open_async + /* Codes_SRS_TLSIO_30_100: [ As long as the TLS connection is able to provide received data, tlsio_dowork shall repeatedly read this data and call on_bytes_received with the pointer to the buffer containing the data, the number of bytes received, and the on_bytes_received_context. ]*/ + tls_io_instance->on_bytes_received(tls_io_instance->on_bytes_received_context, buffer, rcv_bytes); } - send_size = 0; - } - else if (send_result >= send_size) /* Transmit it all. */ - { - /* Codes_SRS_TLSIO_ARDUINO_21_057: [ if the ssl finish to send all bytes in the buffer, the tlsio_arduino_send shall call the on_send_complete with IO_SEND_OK, and return 0 ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_053: [ The tlsio_arduino_send shall use the provided on_io_send_complete callback function address. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_054: [ The tlsio_arduino_send shall use the provided on_io_send_complete_context handle. ]*/ - if (on_send_complete != NULL) + else if (rcv_bytes < 0) + { + LogError("Communications error while reading"); + enter_tlsio_error_state(tls_io_instance); + } + else { - /* Codes_SRS_TLSIO_ARDUINO_21_003: [ The tlsio_arduino shall report the send operation status using the IO_SEND_RESULT enumerator defined in the `xio.h`. ]*/ - on_send_complete(callback_context, IO_SEND_OK); + /* Codes_SRS_TLSIO_30_102: [ If the TLS connection receives no data then tlsio_dowork shall not call the on_bytes_received + callback. ]*/ } - result = 0; - send_size = 0; } - else /* Still have buffer to transmit. */ + } + } +} + +static void dowork_send(TLS_IO_INSTANCE* tls_io_instance) +{ + LIST_ITEM_HANDLE first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_transmission_list); + if (first_pending_io != NULL) + { + PENDING_TRANSMISSION* pending_message = (PENDING_TRANSMISSION*)singlylinkedlist_item_get_value(first_pending_io); + uint8_t* buffer = ((uint8_t*)pending_message->bytes) + pending_message->size - pending_message->unsent_size; + + size_t write_result = sslClient_write(buffer, pending_message->unsent_size); + if (write_result >= 0) + { + pending_message->unsent_size -= write_result; + if (pending_message->unsent_size == 0) { - runBuffer += send_result; - send_size -= send_result; + /* Codes_SRS_TLSIO_30_091: [ If tlsio_arduino_compact_dowork is able to send all the bytes in an enqueued message, it shall call the messages's on_send_complete along with its associated callback_context and IO_SEND_OK. ]*/ + // The whole message has been sent successfully + process_and_destroy_head_message(tls_io_instance, IO_SEND_OK); + } + else + { + /* Codes_SRS_TLSIO_30_093: [ If the TLS connection was not able to send an entire enqueued message at once, subsequent calls to tlsio_dowork shall continue to send the remaining bytes. ]*/ + // Repeat the send on the next pass with the rest of the message + // This empty else compiles to nothing but helps readability } } + else + { + // The write returned non-success. It may be busy, or it may be broken + /* Codes_SRS_TLSIO_30_002: [ The phrase "destroy the failed message" means that the adapter shall remove the message from the queue and destroy it after calling the message's on_send_complete along with its associated callback_context and IO_SEND_ERROR. ]*/ + /* Codes_SRS_TLSIO_30_005: [ When the adapter enters TLSIO_STATE_EXT_ERROR it shall call the on_io_error function and pass the on_io_error_context that were supplied in tlsio_open . ]*/ + /* Codes_SRS_TLSIO_30_095: [ If the send process fails before sending all of the bytes in an enqueued message, tlsio_dowork shall destroy the failed message and enter TLSIO_STATE_EX_ERROR. ]*/ + // This is an unexpected error, and we need to bail out. Probably lost internet connection. + LogError("Write failed"); + process_and_destroy_head_message(tls_io_instance, IO_SEND_ERROR); + } + } + else + { + /* Codes_SRS_TLSIO_30_096: [ If there are no enqueued messages available, tlsio_arduino_compact_dowork shall do nothing. ]*/ + } +} + +static void dowork_poll_dns(TLS_IO_INSTANCE* tls_io_instance) +{ + /* Codes_SRS_TLSIO_ARDUINO_21_018: [ The tlsio_arduino_create shall convert the provide hostName to an IP address. ]*/ + if (sslClient_hostByName(STRING_c_str(tls_io_instance->hostname), &(tls_io_instance->remote_addr))) + { + tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_WAITING_SOCKET; + } + else + { + /* Codes_SRS_TLSIO_ARDUINO_21_019: [ If the WiFi cannot find the IP for the hostName, the tlsio_arduino_create shall destroy the sslClient and tlsio instances and return NULL as the handle. ]*/ + LogError("Host %s not found", STRING_c_str(tls_io_instance->hostname)); + free(tls_io_instance); + tls_io_instance = NULL; + } +} + +static void dowork_poll_socket(TLS_IO_INSTANCE* tls_io_instance) +{ + // Nothing to do here + tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_WAITING_SSL; +} + +static void dowork_poll_open_ssl(TLS_IO_INSTANCE* tls_io_instance) +{ + if (sslClient_connect(tls_io_instance->remote_addr, tls_io_instance->port)) + { + /* Codes_SRS_TLSIO_30_080: [ The tlsio_dowork shall establish a TLS connection using the hostName and port provided during tlsio_open. ]*/ + // Connect succeeded + tls_io_instance->tlsio_state = TLSIO_STATE_OPEN; + /* Codes_SRS_TLSIO_30_007: [ The phrase "enter TLSIO_STATE_EXT_OPEN" means the adapter shall call the on_io_open_complete function and pass IO_OPEN_OK and the on_io_open_complete_context that was supplied in tlsio_open . ]*/ + /* Codes_SRS_TLSIO_30_083: [ If tlsio_dowork successfully opens the TLS connection it shall enter TLSIO_STATE_EX_OPEN. ]*/ + tls_io_instance->on_open_complete(tls_io_instance->on_open_complete_context, IO_OPEN_OK); + } + else + { + LogError("Error opening socket"); + enter_open_error_state(tls_io_instance); } - return result; } -/* Codes_SRS_TLSIO_ARDUINO_21_062: [ The tlsio_arduino_dowork shall execute the async jobs for the tlsio. ]*/ -void tlsio_arduino_dowork(CONCRETE_IO_HANDLE tlsio_handle) +static void tlsio_arduino_dowork(CONCRETE_IO_HANDLE tls_io) { - if (tlsio_handle == NULL) + if (tls_io == NULL) { - /* Codes_SRS_TLSIO_ARDUINO_21_074: [ If the tlsio_handle is NULL, the tlsio_arduino_dowork shall not do anything. ]*/ - LogError("Invalid parameter"); + /* Codes_SRS_TLSIO_30_070: [ If the tlsio_handle parameter is NULL, tlsio_dowork shall do nothing except log an error. ]*/ + LogError("NULL tlsio"); } else { - int received; - ArduinoTLS* tlsio_instance = (ArduinoTLS*)tlsio_handle; - /* Codes_SRS_TLSIO_ARDUINO_21_075: [ The tlsio_arduino_dowork shall create a buffer to store the data received from the ssl client. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_076: [ The tlsio_arduino_dowork shall delete the buffer to store the data received from the ssl client. ]*/ - uint8_t RecvBuffer[RECEIVE_BUFFER_SIZE]; + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; - switch (tlsio_instance->state) + // This switch statement handles all of the state transitions during the opening process + switch (tls_io_instance->tlsio_state) { - case TLSIO_ARDUINO_STATE_OPENING: - if (sslClient_connected()) - { - /* Codes_SRS_TLSIO_ARDUINO_21_063: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPENING, and ssl client is connected, the tlsio_arduino_dowork shall change the tlsio state to TLSIO_ARDUINO_STATE_OPEN, and call the on_io_open_complete with IO_OPEN_OK. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_OPEN; - /* Codes_SRS_TLSIO_ARDUINO_21_002: [ The tlsio_arduino shall report the open operation status using the IO_OPEN_RESULT enumerator defined in the `xio.h`.]*/ - CallOpenCallback(IO_OPEN_OK); - } - /* Codes_SRS_TLSIO_ARDUINO_21_064: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPENING, and ssl client is not connected, the tlsio_arduino_dowork shall decrement the counter of trys for opening. ]*/ - else if ((tlsio_instance->countTry--) <= 0) - { - /* Codes_SRS_TLSIO_ARDUINO_21_065: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPENING, ssl client is not connected, and the counter to try becomes 0, the tlsio_arduino_dowork shall change the tlsio state to TLSIO_ARDUINO_STATE_ERROR, call on_io_open_complete with IO_OPEN_CANCELLED, call on_io_error. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; - LogError("Timeout for TLS connect."); - /* Codes_SRS_TLSIO_ARDUINO_21_002: [ The tlsio_arduino shall report the open operation status using the IO_OPEN_RESULT enumerator defined in the `xio.h`.]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_042: [ If the tlsio_arduino_open retry to open more than 10 times without success, it shall call the on_io_open_complete with IO_OPEN_CANCELED. ]*/ - CallOpenCallback(IO_OPEN_CANCELLED); - CallErrorCallback(); - } + case TLSIO_STATE_CLOSED: + /* Codes_SRS_TLSIO_30_075: [ If the adapter is in TLSIO_STATE_EXT_CLOSED then tlsio_dowork shall do nothing. ]*/ + // Waiting to be opened, nothing to do break; - case TLSIO_ARDUINO_STATE_OPEN: - if (!sslClient_connected()) - { - /* Codes_SRS_TLSIO_ARDUINO_21_071: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPEN, and ssl client is not connected, the tlsio_arduino_dowork shall change the state to TLSIO_ARDUINO_STATE_ERROR, call on_io_error. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; - LogError("SSL closed the connection."); - CallErrorCallback(); - } - else - { - /* Codes_SRS_TLSIO_ARDUINO_21_069: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPEN, the tlsio_arduino_dowork shall read data from the ssl client. ]*/ - while ((received = sslClient_read((uint8_t*)RecvBuffer, RECEIVE_BUFFER_SIZE)) > 0) - { - /* Codes_SRS_TLSIO_ARDUINO_21_070: [ If the tlsio state is TLSIO_ARDUINO_STATE_OPEN, and there are received data in the ssl client, the tlsio_arduino_dowork shall read this data and call the on_bytes_received with the pointer to the buffer with the data. ]*/ - if (tlsio_instance->on_bytes_received != NULL) - { - // explictly ignoring here the result of the callback - (void)tlsio_instance->on_bytes_received(tlsio_instance->on_bytes_received_context, (const unsigned char*)RecvBuffer, received); - } - } - } + case TLSIO_STATE_OPENING_WAITING_DNS: + dowork_poll_dns(tls_io_instance); break; - case TLSIO_ARDUINO_STATE_CLOSING: - if (!sslClient_connected()) - { - /* Codes_SRS_TLSIO_ARDUINO_21_066: [ If the tlsio state is TLSIO_ARDUINO_STATE_CLOSING, and ssl client is not connected, the tlsio_arduino_dowork shall change the tlsio state to TLSIO_ARDUINO_STATE_CLOSE, and call the on_io_close_complete. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_CLOSED; - CallCloseCallback(); - } - /* Codes_SRS_TLSIO_ARDUINO_21_067: [ If the tlsio state is TLSIO_ARDUINO_STATE_CLOSING, and ssl client is connected, the tlsio_arduino_dowork shall decrement the counter of trys for closing. ]*/ - else if ((tlsio_instance->countTry--) <= 0) - { - /* Codes_SRS_TLSIO_ARDUINO_21_051: [ If the tlsio_arduino_close retry to close more than 10 times without success, it shall call the on_io_error. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_068: [ If the tlsio state is TLSIO_ARDUINO_STATE_CLOSING, ssl client is connected, and the counter to try becomes 0, the tlsio_arduino_dowork shall change the tlsio state to TLSIO_ARDUINO_STATE_ERROR, call on_io_error. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; - LogError("Timeout for close TLS"); - CallErrorCallback(); - - } + case TLSIO_STATE_OPENING_WAITING_SOCKET: + dowork_poll_socket(tls_io_instance); + break; + case TLSIO_STATE_OPENING_WAITING_SSL: + dowork_poll_open_ssl(tls_io_instance); + break; + case TLSIO_STATE_OPEN: + dowork_read(tls_io_instance); + dowork_send(tls_io_instance); + break; + case TLSIO_STATE_ERROR: + /* Codes_SRS_TLSIO_30_071: [ If the adapter is in TLSIO_STATE_EXT_ERROR then tlsio_dowork shall do nothing. ]*/ + // There's nothing valid to do here but wait to be retried break; - case TLSIO_ARDUINO_STATE_CLOSED: - if (sslClient_connected()) - { - /* Codes_SRS_TLSIO_ARDUINO_21_072: [ If the tlsio state is TLSIO_ARDUINO_STATE_CLOSED, and ssl client is connected, the tlsio_arduino_dowork shall change the state to TLSIO_ARDUINO_STATE_ERROR, call on_io_error. ]*/ - tlsio_instance->state = TLSIO_ARDUINO_STATE_ERROR; - LogError("SSL keep the connection open with TLS closed."); - CallErrorCallback(); - } - case TLSIO_ARDUINO_STATE_ERROR: - /* Codes_SRS_TLSIO_ARDUINO_21_073: [ If the tlsio state is TLSIO_ARDUINO_STATE_ERROR, the tlsio_arduino_dowork shall not do anything. ]*/ default: + LogError("Unexpected internal tlsio state: %d", tls_io_instance->tlsio_state); break; } } } -int tlsio_arduino_setoption(CONCRETE_IO_HANDLE tlsio_handle, const char* optionName, const void* value) +static int tlsio_arduino_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value) { - ArduinoTLS* tls_io_instance = (ArduinoTLS*)tlsio_handle; - /* Codes_SRS_TLSIO_30_120: [ If the tlsio_handle parameter is NULL, tlsio_openssl_compact_setoption shall do nothing except log an error and return FAILURE. ]*/ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + /* Codes_SRS_TLSIO_30_120: [ If the tlsio_handle parameter is NULL, tlsio_arduino_setoption shall do nothing except log an error and return FAILURE. ]*/ int result; if (tls_io_instance == NULL) { @@ -514,9 +553,9 @@ int tlsio_arduino_setoption(CONCRETE_IO_HANDLE tlsio_handle, const char* optionN } else { - /* Codes_SRS_TLSIO_30_121: [ If the optionName parameter is NULL, tlsio_setoption shall do nothing except log an error and return FAILURE. ]*/ - /* Codes_SRS_TLSIO_30_122: [ If the value parameter is NULL, tlsio_setoption shall do nothing except log an error and return FAILURE. ]*/ - /* Codes_SRS_TLSIO_ARDUINO_21_077: [ The tlsio_arduino_setoption shall not do anything, and return __FAILURE__ . ]*/ + /* Codes_SRS_TLSIO_30_121: [ If the optionName parameter is NULL, tlsio_arduino_setoption shall do nothing except log an error and return FAILURE. ]*/ + /* Codes_SRS_TLSIO_30_122: [ If the value parameter is NULL, tlsio_arduino_setoption shall do nothing except log an error and return FAILURE. ]*/ + /* Codes_SRS_TLSIO_OPENSSL_COMPACT_30_520 [ The tlsio_setoption shall do nothing and return FAILURE. ]*/ TLSIO_OPTIONS_RESULT options_result = tlsio_options_set(&tls_io_instance->options, optionName, value); if (options_result != TLSIO_OPTIONS_RESULT_SUCCESS) { @@ -531,10 +570,79 @@ int tlsio_arduino_setoption(CONCRETE_IO_HANDLE tlsio_handle, const char* optionN return result; } -OPTIONHANDLER_HANDLE tlsio_arduino_retrieveoptions(CONCRETE_IO_HANDLE tlsio_handle) +static int tlsio_arduino_send_async(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + if (on_send_complete == NULL || tls_io == NULL || buffer == NULL || size == 0 || on_send_complete == NULL) + { + /* Codes_SRS_TLSIO_30_062: [ If the on_send_complete is NULL, tlsio_arduino_compact_send shall log the error and return FAILURE. ]*/ + result = __FAILURE__; + LogError("Invalid parameter specified: tls_io: %p, buffer: %p, size: %zu, on_send_complete: %p", tls_io, buffer, size, on_send_complete); + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN) + { + /* Codes_SRS_TLSIO_30_060: [ If the tlsio_handle parameter is NULL, tlsio_arduino_compact_send shall log an error and return FAILURE. ]*/ + /* Codes_SRS_TLSIO_30_065: [ If tlsio_arduino_compact_open has not been called or the opening process has not been completed, tlsio_arduino_compact_send shall log an error and return FAILURE. ]*/ + result = __FAILURE__; + LogError("tlsio_arduino_send_async without a prior successful open"); + } + else + { + PENDING_TRANSMISSION* pending_transmission = (PENDING_TRANSMISSION*)malloc(sizeof(PENDING_TRANSMISSION)); + if (pending_transmission == NULL) + { + /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_arduino_compact_send shall log an error and return FAILURE. ]*/ + result = __FAILURE__; + LogError("malloc failed"); + } + else + { + /* Codes_SRS_TLSIO_30_063: [ The tlsio_arduino_compact_send shall enqueue for transmission the on_send_complete, the callback_context, the size, and the contents of buffer. ]*/ + if ((pending_transmission->bytes = (unsigned char*)malloc(size)) == NULL) + { + /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_arduino_compact_send shall log an error and return FAILURE. ]*/ + LogError("malloc failed"); + free(pending_transmission); + result = __FAILURE__; + } + else + { + pending_transmission->size = size; + pending_transmission->unsent_size = size; + pending_transmission->on_send_complete = on_send_complete; + pending_transmission->callback_context = callback_context; + (void)memcpy(pending_transmission->bytes, buffer, size); + + if (singlylinkedlist_add(tls_io_instance->pending_transmission_list, pending_transmission) == NULL) + { + /* Codes_SRS_TLSIO_30_064: [ If the supplied message cannot be enqueued for transmission, tlsio_arduino_compact_send shall log an error and return FAILURE. ]*/ + LogError("Unable to add socket to pending list."); + free(pending_transmission->bytes); + free(pending_transmission); + result = __FAILURE__; + } + else + { + /* Codes_SRS_TLSIO_30_063: [ On success, tlsio_send shall enqueue for transmission the on_send_complete , the callback_context , the size , and the contents of buffer and then return 0. ]*/ + result = 0; + dowork_send(tls_io_instance); + } + } + } + } + /* Codes_SRS_TLSIO_30_066: [ On failure, on_send_complete shall not be called. ]*/ + } + return result; +} + +/* Codes_SRS_TLSIO_APPLEIOS_COMPACT_30_560: [ The tlsio_retrieveoptions shall do nothing and return NULL. ]*/ +static OPTIONHANDLER_HANDLE tlsio_arduino_retrieveoptions(CONCRETE_IO_HANDLE tls_io) { - ArduinoTLS* tls_io_instance = (ArduinoTLS*)tlsio_handle; - /* Codes_SRS_TLSIO_ARDUINO_21_078: [ The tlsio_arduino_retrieveoptions shall return an empty options handler. ]*/ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + /* Codes_SRS_TLSIO_30_160: [ If the tlsio_handle parameter is NULL, tlsio_arduino_retrieveoptions shall do nothing except log an error and return FAILURE. ]*/ OPTIONHANDLER_HANDLE result; if (tls_io_instance == NULL) { @@ -548,4 +656,27 @@ OPTIONHANDLER_HANDLE tlsio_arduino_retrieveoptions(CONCRETE_IO_HANDLE tlsio_hand return result; } +/* Codes_SRS_TLSIO_30_008: [ The tlsio_get_interface_description shall return the VTable IO_INTERFACE_DESCRIPTION. ]*/ +static const IO_INTERFACE_DESCRIPTION tlsio_arduino_interface_description = +{ + tlsio_arduino_retrieveoptions, + tlsio_arduino_create, + tlsio_arduino_destroy, + tlsio_arduino_open_async, + tlsio_arduino_close_async, + tlsio_arduino_send_async, + tlsio_arduino_dowork, + tlsio_arduino_setoption +}; + +/* Codes_SRS_TLSIO_30_001: [ The tlsio_arduino_compact shall implement and export all the Concrete functions in the VTable IO_INTERFACE_DESCRIPTION defined in the xio.h. ]*/ +const IO_INTERFACE_DESCRIPTION* tlsio_arduino_get_interface_description(void) +{ + return &tlsio_arduino_interface_description; +} + +const IO_INTERFACE_DESCRIPTION* socketio_get_interface_description(void) +{ + return NULL; +} diff --git a/src/adapters/xlogging_dump_bytes.c b/src/adapters/xlogging_dump_bytes.c index 1e32d08..2e369b9 100644 --- a/src/adapters/xlogging_dump_bytes.c +++ b/src/adapters/xlogging_dump_bytes.c @@ -21,7 +21,8 @@ void xlogging_dump_bytes(const void* buf, size_t size) size_t countbuf = 0; const unsigned char* bufAsChar = (const unsigned char*)buf; const unsigned char* startPos = bufAsChar; - + LOG(AZ_LOG_TRACE, 0, "Buffer length=%d\r\n", size); + /* Print the whole buffer. */ size_t i = 0; for (i = 0; i < size; i++) @@ -44,7 +45,7 @@ void xlogging_dump_bytes(const void* buf, size_t size) { charBuf[countbuf] = '\0'; hexBuf[countbuf * 3] = '\0'; - LOG(AZ_LOG_TRACE, 0, "%p: %s %s", startPos, hexBuf, charBuf); + LOG(AZ_LOG_TRACE, 0, "%p: %s %s\r\n", startPos, hexBuf, charBuf); countbuf = 0; startPos = bufAsChar; } @@ -66,6 +67,6 @@ void xlogging_dump_bytes(const void* buf, size_t size) hexBuf[countbuf * 3] = '\0'; /* Print the last line. */ - LOG(AZ_LOG_TRACE, 0, "%p: %s %s", startPos, hexBuf, charBuf); + LOG(AZ_LOG_TRACE, 0, "%p: %s %s\r\n", startPos, hexBuf, charBuf); } } diff --git a/src/azure_c_shared_utility/http_proxy_io.c b/src/azure_c_shared_utility/http_proxy_io.c deleted file mode 100644 index bf8d826..0000000 --- a/src/azure_c_shared_utility/http_proxy_io.c +++ /dev/null @@ -1,968 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include -#include -#include -#include -#include -#include -#include "azure_c_shared_utility/gballoc.h" -#include "azure_c_shared_utility/xio.h" -#include "azure_c_shared_utility/socketio.h" -#include "azure_c_shared_utility/crt_abstractions.h" -#include "azure_c_shared_utility/http_proxy_io.h" -#include "azure_c_shared_utility/base64.h" - -typedef enum HTTP_PROXY_IO_STATE_TAG -{ - HTTP_PROXY_IO_STATE_CLOSED, - HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO, - HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE, - HTTP_PROXY_IO_STATE_OPEN, - HTTP_PROXY_IO_STATE_CLOSING, - HTTP_PROXY_IO_STATE_ERROR -} HTTP_PROXY_IO_STATE; - -typedef struct HTTP_PROXY_IO_INSTANCE_TAG -{ - HTTP_PROXY_IO_STATE http_proxy_io_state; - ON_BYTES_RECEIVED on_bytes_received; - void* on_bytes_received_context; - ON_IO_ERROR on_io_error; - void* on_io_error_context; - ON_IO_OPEN_COMPLETE on_io_open_complete; - void* on_io_open_complete_context; - ON_IO_CLOSE_COMPLETE on_io_close_complete; - void* on_io_close_complete_context; - char* hostname; - int port; - char* proxy_hostname; - int proxy_port; - char* username; - char* password; - XIO_HANDLE underlying_io; - unsigned char* receive_buffer; - size_t receive_buffer_size; -} HTTP_PROXY_IO_INSTANCE; - -static CONCRETE_IO_HANDLE http_proxy_io_create(void* io_create_parameters) -{ - HTTP_PROXY_IO_INSTANCE* result; - - if (io_create_parameters == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_002: [ If `io_create_parameters` is NULL, `http_proxy_io_create` shall fail and return NULL. ]*/ - result = NULL; - LogError("NULL io_create_parameters."); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_003: [ `io_create_parameters` shall be used as an `HTTP_PROXY_IO_CONFIG*`. ]*/ - HTTP_PROXY_IO_CONFIG* http_proxy_io_config = (HTTP_PROXY_IO_CONFIG*)io_create_parameters; - if ((http_proxy_io_config->hostname == NULL) || - (http_proxy_io_config->proxy_hostname == NULL)) - { - /* Codes_SRS_HTTP_PROXY_IO_01_004: [ If the `hostname` or `proxy_hostname` member is NULL, then `http_proxy_io_create` shall fail and return NULL. ]*/ - result = NULL; - LogError("Bad arguments: hostname = %p, proxy_hostname = %p", - http_proxy_io_config->hostname, http_proxy_io_config->proxy_hostname); - } - /* Codes_SRS_HTTP_PROXY_IO_01_095: [ If one of the fields `username` and `password` is non-NULL, then the other has to be also non-NULL, otherwise `http_proxy_io_create` shall fail and return NULL. ]*/ - else if (((http_proxy_io_config->username == NULL) && (http_proxy_io_config->password != NULL)) || - ((http_proxy_io_config->username != NULL) && (http_proxy_io_config->password == NULL))) - { - result = NULL; - LogError("Bad arguments: username = %p, password = %p", - http_proxy_io_config->username, http_proxy_io_config->password); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_001: [ `http_proxy_io_create` shall create a new instance of the HTTP proxy IO. ]*/ - result = (HTTP_PROXY_IO_INSTANCE*)malloc(sizeof(HTTP_PROXY_IO_INSTANCE)); - if (result == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_051: [ If allocating memory for the new instance fails, `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Failed allocating HTTP proxy IO instance."); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_005: [ `http_proxy_io_create` shall copy the `hostname`, `port`, `username` and `password` values for later use when the actual CONNECT is performed. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ - if (mallocAndStrcpy_s(&result->hostname, http_proxy_io_config->hostname) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Failed to copy the hostname."); - /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ - free(result); - result = NULL; - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ - if (mallocAndStrcpy_s(&result->proxy_hostname, http_proxy_io_config->proxy_hostname) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Failed to copy the proxy_hostname."); - /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ - free(result->hostname); - free(result); - result = NULL; - } - else - { - result->username = NULL; - result->password = NULL; - - /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_094: [ `username` and `password` shall be optional. ]*/ - if ((http_proxy_io_config->username != NULL) && (mallocAndStrcpy_s(&result->username, http_proxy_io_config->username) != 0)) - { - /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Failed to copy the username."); - /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ - free(result->proxy_hostname); - free(result->hostname); - free(result); - result = NULL; - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_006: [ `hostname` and `proxy_hostname`, `username` and `password` shall be copied by calling `mallocAndStrcpy_s`. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_094: [ `username` and `password` shall be optional. ]*/ - if ((http_proxy_io_config->password != NULL) && (mallocAndStrcpy_s(&result->password, http_proxy_io_config->password) != 0)) - { - /* Codes_SRS_HTTP_PROXY_IO_01_007: [ If `mallocAndStrcpy_s` fails then `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Failed to copy the passowrd."); - /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ - free(result->username); - free(result->proxy_hostname); - free(result->hostname); - free(result); - result = NULL; - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_010: [ - `io_interface_description` shall be set to the result of `socketio_get_interface_description`. ]*/ - const IO_INTERFACE_DESCRIPTION* underlying_io_interface = socketio_get_interface_description(); - if (underlying_io_interface == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_050: [ If `socketio_get_interface_description` fails, `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Unable to get the socket IO interface description."); - /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ - free(result->password); - free(result->username); - free(result->proxy_hostname); - free(result->hostname); - free(result); - result = NULL; - } - else - { - SOCKETIO_CONFIG socket_io_config; - - /* Codes_SRS_HTTP_PROXY_IO_01_011: [ - `xio_create_parameters` shall be set to a `SOCKETIO_CONFIG*` where `hostname` is set to the `proxy_hostname` member of `io_create_parameters` and `port` is set to the `proxy_port` member of `io_create_parameters`. ]*/ - socket_io_config.hostname = http_proxy_io_config->proxy_hostname; - socket_io_config.port = http_proxy_io_config->proxy_port; - socket_io_config.accepted_socket = NULL; - - /* Codes_SRS_HTTP_PROXY_IO_01_009: [ `http_proxy_io_create` shall create a new socket IO by calling `xio_create` with the arguments: ]*/ - result->underlying_io = xio_create(underlying_io_interface, &socket_io_config); - if (result->underlying_io == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_012: [ If `xio_create` fails, `http_proxy_io_create` shall fail and return NULL. ]*/ - LogError("Unable to create the underlying IO."); - /* Codes_SRS_HTTP_PROXY_IO_01_008: [ When `http_proxy_io_create` fails, all allocated resources up to that point shall be freed. ]*/ - free(result->password); - free(result->username); - free(result->proxy_hostname); - free(result->hostname); - free(result); - result = NULL; - } - else - { - result->port = http_proxy_io_config->port; - result->proxy_port = http_proxy_io_config->proxy_port; - result->receive_buffer = NULL; - result->receive_buffer_size = 0; - result->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; - } - } - } - } - } - } - } - } - } - - return result; -} - -static void http_proxy_io_destroy(CONCRETE_IO_HANDLE http_proxy_io) -{ - if (http_proxy_io == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_014: [ If `http_proxy_io` is NULL, `http_proxy_io_destroy` shall do nothing. ]*/ - LogError("NULL http_proxy_io."); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - /* Codes_SRS_HTTP_PROXY_IO_01_013: [ `http_proxy_io_destroy` shall free the HTTP proxy IO instance indicated by `http_proxy_io`. ]*/ - if (http_proxy_io_instance->receive_buffer != NULL) - { - free(http_proxy_io_instance->receive_buffer); - } - - /* Codes_SRS_HTTP_PROXY_IO_01_016: [ `http_proxy_io_destroy` shall destroy the underlying IO created in `http_proxy_io_create` by calling `xio_destroy`. ]*/ - xio_destroy(http_proxy_io_instance->underlying_io); - free(http_proxy_io_instance->hostname); - free(http_proxy_io_instance->proxy_hostname); - free(http_proxy_io_instance->username); - free(http_proxy_io_instance->password); - free(http_proxy_io_instance); - } -} - -static void indicate_open_complete_error_and_close(HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance) -{ - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; - (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL); - http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_ERROR); -} - -// This callback usage needs to be either verified and commented or integrated into -// the state machine. -static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result) -{ - (void)context; - (void)send_result; -} - -static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) -{ - if (context == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_081: [ `on_underlying_io_open_complete` called with NULL context shall do nothing. ]*/ - LogError("NULL context in on_underlying_io_open_complete"); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; - switch (http_proxy_io_instance->http_proxy_io_state) - { - default: - LogError("on_underlying_io_open_complete called in an unexpected state."); - break; - - case HTTP_PROXY_IO_STATE_CLOSING: - case HTTP_PROXY_IO_STATE_OPEN: - /* Codes_SRS_HTTP_PROXY_IO_01_077: [ When `on_underlying_io_open_complete` is called in after OPEN has completed, the `on_io_error` callback shall be triggered passing the `on_io_error_context` argument as `context`. ]*/ - http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context); - break; - - case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE: - /* Codes_SRS_HTTP_PROXY_IO_01_076: [ When `on_underlying_io_open_complete` is called while waiting for the CONNECT reply, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Open complete called again by underlying IO."); - indicate_open_complete_error_and_close(http_proxy_io_instance); - break; - - case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO: - switch (open_result) - { - default: - case IO_OPEN_ERROR: - /* Codes_SRS_HTTP_PROXY_IO_01_078: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_ERROR`, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Underlying IO open failed"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - break; - - case IO_OPEN_CANCELLED: - /* Codes_SRS_HTTP_PROXY_IO_01_079: [ When `on_underlying_io_open_complete` is called with `IO_OPEN_CANCELLED`, the `on_open_complete` callback shall be triggered with `IO_OPEN_CANCELLED`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Underlying IO open failed"); - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; - (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL); - http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED); - break; - - case IO_OPEN_OK: - { - STRING_HANDLE encoded_auth_string; - - /* Codes_SRS_HTTP_PROXY_IO_01_057: [ When `on_underlying_io_open_complete` is called, the `http_proxy_io` shall send the CONNECT request constructed per RFC 2817: ]*/ - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE; - - if (http_proxy_io_instance->username != NULL) - { - char* plain_auth_string_bytes; - - /* Codes_SRS_HTTP_PROXY_IO_01_060: [ - The value of `Proxy-Authorization` shall be the constructed according to RFC 2617. ]*/ - int plain_auth_string_length = (int)(strlen(http_proxy_io_instance->username)+1); - if (http_proxy_io_instance->password != NULL) - { - plain_auth_string_length += (int)strlen(http_proxy_io_instance->password); - } - - if (plain_auth_string_length < 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - encoded_auth_string = NULL; - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - plain_auth_string_bytes = (char*)malloc(plain_auth_string_length + 1); - if (plain_auth_string_bytes == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - encoded_auth_string = NULL; - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_091: [ To receive authorization, the client sends the userid and password, separated by a single colon (":") character, within a base64 [7] encoded string in the credentials. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_092: [ A client MAY preemptively send the corresponding Authorization header with requests for resources in that space without receipt of another challenge from the server. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_093: [ Userids might be case sensitive. ]*/ - if (sprintf(plain_auth_string_bytes, "%s:%s", http_proxy_io_instance->username, (http_proxy_io_instance->password == NULL) ? "" : http_proxy_io_instance->password) < 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - encoded_auth_string = NULL; - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_061: [ Encoding to Base64 shall be done by calling `Base64_Encode_Bytes`. ]*/ - encoded_auth_string = Base64_Encode_Bytes((const unsigned char*)plain_auth_string_bytes, plain_auth_string_length); - if (encoded_auth_string == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Cannot Base64 encode auth string"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - } - - free(plain_auth_string_bytes); - } - } - } - else - { - encoded_auth_string = NULL; - } - - if ((http_proxy_io_instance->username != NULL) && - (encoded_auth_string == NULL)) - { - LogError("Cannot create authorization header"); - } - else - { - int connect_request_length; - const char* auth_string_payload; - /* Codes_SRS_HTTP_PROXY_IO_01_075: [ The Request-URI portion of the Request-Line is always an 'authority' as defined by URI Generic Syntax [2], which is to say the host name and port number destination of the requested connection separated by a colon: ]*/ - const char request_format[] = "CONNECT %s:%d HTTP/1.1\r\nHost:%s:%d%s%s\r\n\r\n"; - const char proxy_basic[] = "\r\nProxy-authorization: Basic "; - if (http_proxy_io_instance->username != NULL) - { - auth_string_payload = STRING_c_str(encoded_auth_string); - } - else - { - auth_string_payload = ""; - } - - /* Codes_SRS_HTTP_PROXY_IO_01_059: [ - If `username` and `password` have been specified in the arguments passed to `http_proxy_io_create`, then the header `Proxy-Authorization` shall be added to the request. ]*/ - - connect_request_length = (int)(strlen(request_format)+(strlen(http_proxy_io_instance->hostname)*2)+strlen(auth_string_payload)+10); - if (http_proxy_io_instance->username != NULL) - { - connect_request_length += (int)strlen(proxy_basic); - } - - if (connect_request_length < 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Cannot encode the CONNECT request"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - char* connect_request = (char*)malloc(connect_request_length + 1); - if (connect_request == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Cannot allocate memory for CONNECT request"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_059: [ - If `username` and `password` have been specified in the arguments passed to `http_proxy_io_create`, then the header `Proxy-Authorization` shall be added to the request. ]*/ - connect_request_length = sprintf(connect_request, request_format, - http_proxy_io_instance->hostname, - http_proxy_io_instance->port, - http_proxy_io_instance->hostname, - http_proxy_io_instance->port, - (http_proxy_io_instance->username != NULL) ? proxy_basic : "", - auth_string_payload); - - if (connect_request_length < 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_062: [ If any failure is encountered while constructing the request, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Cannot encode the CONNECT request"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_063: [ The request shall be sent by calling `xio_send` and passing NULL as `on_send_complete` callback. ]*/ - if (xio_send(http_proxy_io_instance->underlying_io, connect_request, connect_request_length, unchecked_on_send_complete, NULL) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_064: [ If `xio_send` fails, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Could not send CONNECT request"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - } - - free(connect_request); - } - } - } - - if (encoded_auth_string != NULL) - { - STRING_delete(encoded_auth_string); - } - - break; - } - } - - break; - } - } -} - -static void on_underlying_io_error(void* context) -{ - if (context == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_088: [ `on_underlying_io_error` called with NULL context shall do nothing. ]*/ - LogError("NULL context in on_underlying_io_error"); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; - - switch (http_proxy_io_instance->http_proxy_io_state) - { - default: - LogError("on_underlying_io_error in invalid state"); - break; - - case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO: - case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE: - /* Codes_SRS_HTTP_PROXY_IO_01_087: [ If the `on_underlying_io_error` callback is called while OPENING, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - indicate_open_complete_error_and_close(http_proxy_io_instance); - break; - - case HTTP_PROXY_IO_STATE_OPEN: - /* Codes_SRS_HTTP_PROXY_IO_01_089: [ If the `on_underlying_io_error` callback is called while the IO is OPEN, the `on_io_error` callback shall be called with the `on_io_error_context` argument as `context`. ]*/ - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_ERROR; - http_proxy_io_instance->on_io_error(http_proxy_io_instance->on_io_error_context); - break; - } - } -} - -static void on_underlying_io_close_complete(void* context) -{ - if (context == NULL) - { - /* Cdoes_SRS_HTTP_PROXY_IO_01_084: [ `on_underlying_io_close_complete` called with NULL context shall do nothing. ]*/ - LogError("NULL context in on_underlying_io_open_complete"); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; - - switch (http_proxy_io_instance->http_proxy_io_state) - { - default: - LogError("on_underlying_io_close_complete called in an invalid state"); - break; - - case HTTP_PROXY_IO_STATE_CLOSING: - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; - - /* Codes_SRS_HTTP_PROXY_IO_01_086: [ If the `on_io_close_complete` callback passed to `http_proxy_io_close` was NULL, no callback shall be triggered. ]*/ - if (http_proxy_io_instance->on_io_close_complete != NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_083: [ `on_underlying_io_close_complete` while CLOSING shall call the `on_io_close_complete` callback, passing to it the `on_io_close_complete_context` as `context` argument. ]*/ - http_proxy_io_instance->on_io_close_complete(http_proxy_io_instance->on_io_close_complete_context); - } - - break; - } - } -} - -/*the following function does the same as sscanf(pos2, "%d", &sec)*/ -/*this function only exists because some of platforms do not have sscanf. */ -static int ParseStringToDecimal(const char *src, int* dst) -{ - int result; - char* next; - - (*dst) = (int)strtol(src, &next, 0); - if ((src == next) || ((((*dst) == INT_MAX) || ((*dst) == INT_MIN)) && (errno != 0))) - { - result = __LINE__; - } - else - { - result = 0; - } - - return result; -} - -/*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */ -/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined HTTP response. */ -static int ParseHttpResponse(const char* src, int* dst) -{ - int result; - static const char HTTPPrefix[] = "HTTP/"; - bool fail; - const char* runPrefix; - - if ((src == NULL) || (dst == NULL)) - { - result = __LINE__; - } - else - { - fail = false; - runPrefix = HTTPPrefix; - - while ((*runPrefix) != '\0') - { - if ((*runPrefix) != (*src)) - { - fail = true; - break; - } - src++; - runPrefix++; - } - - if (!fail) - { - while ((*src) != '.') - { - if ((*src) == '\0') - { - fail = true; - break; - } - src++; - } - } - - if (!fail) - { - while ((*src) != ' ') - { - if ((*src) == '\0') - { - fail = true; - break; - } - src++; - } - } - - if (fail) - { - result = __LINE__; - } - else - { - if (ParseStringToDecimal(src, dst) != 0) - { - result = __LINE__; - } - else - { - result = 0; - } - } - } - - return result; -} - -static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size) -{ - if (context == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_082: [ `on_underlying_io_bytes_received` called with NULL context shall do nothing. ]*/ - LogError("NULL context in on_underlying_io_bytes_received"); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)context; - - switch (http_proxy_io_instance->http_proxy_io_state) - { - default: - case HTTP_PROXY_IO_STATE_CLOSING: - LogError("Bytes received in invalid state"); - break; - - case HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO: - /* Codes_SRS_HTTP_PROXY_IO_01_080: [ If `on_underlying_io_bytes_received` is called while the underlying IO is being opened, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Bytes received while opening underlying IO"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - break; - - case HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE: - { - /* Codes_SRS_HTTP_PROXY_IO_01_065: [ When bytes are received and the response to the CONNECT request was not yet received, the bytes shall be accumulated until a double new-line is detected. ]*/ - unsigned char* new_receive_buffer = (unsigned char*)realloc(http_proxy_io_instance->receive_buffer, http_proxy_io_instance->receive_buffer_size + size + 1); - if (new_receive_buffer == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_067: [ If allocating memory for the buffered bytes fails, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Cannot allocate memory for received data"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - http_proxy_io_instance->receive_buffer = new_receive_buffer; - memcpy(http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size, buffer, size); - http_proxy_io_instance->receive_buffer_size += size; - } - - if (http_proxy_io_instance->receive_buffer_size >= 4) - { - const char* request_end_ptr; - - http_proxy_io_instance->receive_buffer[http_proxy_io_instance->receive_buffer_size] = 0; - - /* Codes_SRS_HTTP_PROXY_IO_01_066: [ When a double new-line is detected the response shall be parsed in order to extract the status code. ]*/ - if ((http_proxy_io_instance->receive_buffer_size >= 4) && - ((request_end_ptr = strstr((const char*)http_proxy_io_instance->receive_buffer, "\r\n\r\n")) != NULL)) - { - int status_code; - - /* This part should really be done with the HTTPAPI, but that has to be done as a separate step - as the HTTPAPI has to expose somehow the underlying IO and currently this would be a too big of a change. */ - - if (ParseHttpResponse((const char*)http_proxy_io_instance->receive_buffer, &status_code) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_068: [ If parsing the CONNECT response fails, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Cannot decode HTTP response"); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - /* Codes_SRS_HTTP_PROXY_IO_01_069: [ Any successful (2xx) response to a CONNECT request indicates that the proxy has established a connection to the requested host and port, and has switched to tunneling the current connection to that server connection. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_090: [ Any successful (2xx) response to a CONNECT request indicates that the proxy has established a connection to the requested host and port, and has switched to tunneling the current connection to that server connection. ]*/ - else if ((status_code < 200) || (status_code > 299)) - { - /* Codes_SRS_HTTP_PROXY_IO_01_071: [ If the status code is not successful, the `on_open_complete` callback shall be triggered with `IO_OPEN_ERROR`, passing also the `on_open_complete_context` argument as `context`. ]*/ - LogError("Bad status (%d) received in CONNECT response", status_code); - indicate_open_complete_error_and_close(http_proxy_io_instance); - } - else - { - size_t length_remaining = http_proxy_io_instance->receive_buffer + http_proxy_io_instance->receive_buffer_size - ((const unsigned char *)request_end_ptr + 4); - - /* Codes_SRS_HTTP_PROXY_IO_01_073: [ Once a success status code was parsed, the IO shall be OPEN. ]*/ - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPEN; - /* Codes_SRS_HTTP_PROXY_IO_01_070: [ When a success status code is parsed, the `on_open_complete` callback shall be triggered with `IO_OPEN_OK`, passing also the `on_open_complete_context` argument as `context`. ]*/ - http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_OK); - - if (length_remaining > 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_072: [ Any bytes that are extra (not consumed by the CONNECT response), shall be indicated as received by calling the `on_bytes_received` callback and passing the `on_bytes_received_context` as context argument. ]*/ - http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, (const unsigned char*)request_end_ptr + 4, length_remaining); - } - } - } - } - break; - } - case HTTP_PROXY_IO_STATE_OPEN: - /* Codes_SRS_HTTP_PROXY_IO_01_074: [ If `on_underlying_io_bytes_received` is called while OPEN, all bytes shall be indicated as received by calling the `on_bytes_received` callback and passing the `on_bytes_received_context` as context argument. ]*/ - http_proxy_io_instance->on_bytes_received(http_proxy_io_instance->on_bytes_received_context, buffer, size); - break; - } - } -} - -static int http_proxy_io_open(CONCRETE_IO_HANDLE http_proxy_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context) -{ - int result; - - /* Codes_SRS_HTTP_PROXY_IO_01_051: [ The arguments `on_io_open_complete_context`, `on_bytes_received_context` and `on_io_error_context` shall be allowed to be NULL. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_018: [ If any of the arguments `http_proxy_io`, `on_io_open_complete`, `on_bytes_received` or `on_io_error` are NULL then `http_proxy_io_open` shall return a non-zero value. ]*/ - if ((http_proxy_io == NULL) || - (on_io_open_complete == NULL) || - (on_bytes_received == NULL) || - (on_io_error == NULL)) - { - LogError("Bad arguments: http_proxy_io = %p, on_io_open_complete = %p, on_bytes_received = %p, on_io_error_context = %p.", - http_proxy_io, - on_io_open_complete, - on_bytes_received, - on_io_error); - result = __LINE__; - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED) - { - LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_CLOSED."); - result = __LINE__; - } - else - { - http_proxy_io_instance->on_bytes_received = on_bytes_received; - http_proxy_io_instance->on_bytes_received_context = on_bytes_received_context; - - http_proxy_io_instance->on_io_error = on_io_error; - http_proxy_io_instance->on_io_error_context = on_io_error_context; - - http_proxy_io_instance->on_io_open_complete = on_io_open_complete; - http_proxy_io_instance->on_io_open_complete_context = on_io_open_complete_context; - - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO; - - /* Codes_SRS_HTTP_PROXY_IO_01_019: [ `http_proxy_io_open` shall open the underlying IO by calling `xio_open` on the underlying IO handle created in `http_proxy_io_create`, while passing to it the callbacks `on_underlying_io_open_complete`, `on_underlying_io_bytes_received` and `on_underlying_io_error`. ]*/ - if (xio_open(http_proxy_io_instance->underlying_io, on_underlying_io_open_complete, http_proxy_io_instance, on_underlying_io_bytes_received, http_proxy_io_instance, on_underlying_io_error, http_proxy_io_instance) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_020: [ If `xio_open` fails, then `http_proxy_io_open` shall return a non-zero value. ]*/ - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; - LogError("Cannot open the underlying IO."); - result = __LINE__; - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_017: [ `http_proxy_io_open` shall open the HTTP proxy IO and on success it shall return 0. ]*/ - result = 0; - } - } - } - - return result; -} - -static int http_proxy_io_close(CONCRETE_IO_HANDLE http_proxy_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* on_io_close_complete_context) -{ - int result = 0; - - /* Codes_SRS_HTTP_PROXY_IO_01_052: [ `on_io_close_complete_context` shall be allowed to be NULL. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_028: [ `on_io_close_complete` shall be allowed to be NULL. ]*/ - if (http_proxy_io == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_023: [ If the argument `http_proxy_io` is NULL, `http_proxy_io_close` shall fail and return a non-zero value. ]*/ - result = __LINE__; - LogError("NULL http_proxy_io."); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - /* Codes_SRS_HTTP_PROXY_IO_01_027: [ If `http_proxy_io_close` is called when not open, `http_proxy_io_close` shall fail and return a non-zero value. ]*/ - if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSED) || - /* Codes_SRS_HTTP_PROXY_IO_01_054: [ `http_proxy_io_close` while OPENING shall fail and return a non-zero value. ]*/ - (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_CLOSING)) - { - result = __LINE__; - LogError("Invalid tlsio_state. Expected state is HTTP_PROXY_IO_STATE_OPEN."); - } - else if ((http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_OPENING_UNDERLYING_IO) || - (http_proxy_io_instance->http_proxy_io_state == HTTP_PROXY_IO_STATE_WAITING_FOR_CONNECT_RESPONSE)) - { - /* Codes_SRS_HTTP_PROXY_IO_01_053: [ `http_proxy_io_close` while OPENING shall trigger the `on_io_open_complete` callback with `IO_OPEN_CANCELLED`. ]*/ - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSED; - (void)xio_close(http_proxy_io_instance->underlying_io, NULL, NULL); - http_proxy_io_instance->on_io_open_complete(http_proxy_io_instance->on_io_open_complete_context, IO_OPEN_CANCELLED); - - /* Codes_SRS_HTTP_PROXY_IO_01_022: [ `http_proxy_io_close` shall close the HTTP proxy IO and on success it shall return 0. ]*/ - result = 0; - } - else - { - HTTP_PROXY_IO_STATE previous_state = http_proxy_io_instance->http_proxy_io_state; - - http_proxy_io_instance->http_proxy_io_state = HTTP_PROXY_IO_STATE_CLOSING; - - /* Codes_SRS_HTTP_PROXY_IO_01_026: [ The `on_io_close_complete` and `on_io_close_complete_context` arguments shall be saved for later use. ]*/ - http_proxy_io_instance->on_io_close_complete = on_io_close_complete; - http_proxy_io_instance->on_io_close_complete_context = on_io_close_complete_context; - - /* Codes_SRS_HTTP_PROXY_IO_01_024: [ `http_proxy_io_close` shall close the underlying IO by calling `xio_close` on the IO handle create in `http_proxy_io_create`, while passing to it the `on_underlying_io_close_complete` callback. ]*/ - if (xio_close(http_proxy_io_instance->underlying_io, on_underlying_io_close_complete, http_proxy_io_instance) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_025: [ If `xio_close` fails, `http_proxy_io_close` shall fail and return a non-zero value. ]*/ - result = __LINE__; - http_proxy_io_instance->http_proxy_io_state = previous_state; - LogError("Cannot close underlying IO."); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_022: [ `http_proxy_io_close` shall close the HTTP proxy IO and on success it shall return 0. ]*/ - result = 0; - } - } - } - - return result; -} - -static int http_proxy_io_send(CONCRETE_IO_HANDLE http_proxy_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* on_send_complete_context) -{ - int result; - - /* Codes_SRS_HTTP_PROXY_IO_01_032: [ `on_send_complete` shall be allowed to be NULL. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_030: [ If any of the arguments `http_proxy_io` or `buffer` is NULL, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ - if ((http_proxy_io == NULL) || - (buffer == NULL) || - /* Codes_SRS_HTTP_PROXY_IO_01_031: [ If `size` is 0, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ - (size == 0)) - { - result = __LINE__; - LogError("Bad arguments: http_proxy_io = %p, buffer = %p.", - http_proxy_io, buffer); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - /* Codes_SRS_HTTP_PROXY_IO_01_034: [ If `http_proxy_io_send` is called when the IO is not open, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_035: [ If the IO is in an error state (an error was reported through the `on_io_error` callback), `http_proxy_io_send` shall fail and return a non-zero value. ]*/ - if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_OPEN) - { - result = __LINE__; - LogError("Invalid HTTP proxy IO state. Expected state is HTTP_PROXY_IO_STATE_OPEN."); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_033: [ `http_proxy_io_send` shall send the bytes by calling `xio_send` on the underlying IO created in `http_proxy_io_create` and passing `buffer` and `size` as arguments. ]*/ - if (xio_send(http_proxy_io_instance->underlying_io, buffer, size, on_send_complete, on_send_complete_context) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_055: [ If `xio_send` fails, `http_proxy_io_send` shall fail and return a non-zero value. ]*/ - result = __LINE__; - LogError("Underlying xio_send failed."); - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_029: [ `http_proxy_io_send` shall send the `size` bytes pointed to by `buffer` and on success it shall return 0. ]*/ - result = 0; - } - } - } - - return result; -} - -static void http_proxy_io_dowork(CONCRETE_IO_HANDLE http_proxy_io) -{ - if (http_proxy_io == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_038: [ If the `http_proxy_io` argument is NULL, `http_proxy_io_dowork` shall do nothing. ]*/ - LogError("NULL http_proxy_io."); - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - if (http_proxy_io_instance->http_proxy_io_state != HTTP_PROXY_IO_STATE_CLOSED) - { - /* Codes_SRS_HTTP_PROXY_IO_01_037: [ `http_proxy_io_dowork` shall call `xio_dowork` on the underlying IO created in `http_proxy_io_create`. ]*/ - xio_dowork(http_proxy_io_instance->underlying_io); - } - } -} - -static int http_proxy_io_set_option(CONCRETE_IO_HANDLE http_proxy_io, const char* option_name, const void* value) -{ - int result; - - if ((http_proxy_io == NULL) || (option_name == NULL)) - { - /* Codes_SRS_HTTP_PROXY_IO_01_040: [ If any of the arguments `http_proxy_io` or `option_name` is NULL, `http_proxy_io_set_option` shall return a non-zero value. ]*/ - LogError("Bad arguments: http_proxy_io = %p, option_name = %p", - http_proxy_io, option_name); - result = __LINE__; - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - /* Codes_SRS_HTTP_PROXY_IO_01_045: [ None. ]*/ - - /* Codes_SRS_HTTP_PROXY_IO_01_043: [ If the `option_name` argument indicates an option that is not handled by `http_proxy_io_set_option`, then `xio_setoption` shall be called on the underlying IO created in `http_proxy_io_create`, passing the option name and value to it. ]*/ - /* Codes_SRS_HTTP_PROXY_IO_01_056: [ The `value` argument shall be allowed to be NULL. ]*/ - if (xio_setoption(http_proxy_io_instance->underlying_io, option_name, value) != 0) - { - /* Codes_SRS_HTTP_PROXY_IO_01_044: [ if `xio_setoption` fails, `http_proxy_io_set_option` shall return a non-zero value. ]*/ - LogError("Unrecognized option"); - result = __LINE__; - } - else - { - /* Codes_SRS_HTTP_PROXY_IO_01_042: [ If the option was handled by `http_proxy_io_set_option` or the underlying IO, then `http_proxy_io_set_option` shall return 0. ]*/ - result = 0; - } - } - - return result; -} - -static OPTIONHANDLER_HANDLE http_proxy_io_retrieve_options(CONCRETE_IO_HANDLE http_proxy_io) -{ - OPTIONHANDLER_HANDLE result; - - if (http_proxy_io == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_047: [ If the parameter `http_proxy_io` is NULL then `http_proxy_io_retrieve_options` shall fail and return NULL. ]*/ - LogError("invalid parameter detected: CONCRETE_IO_HANDLE handle=%p", http_proxy_io); - result = NULL; - } - else - { - HTTP_PROXY_IO_INSTANCE* http_proxy_io_instance = (HTTP_PROXY_IO_INSTANCE*)http_proxy_io; - - /* Codes_SRS_HTTP_PROXY_IO_01_046: [ `http_proxy_io_retrieve_options` shall return an `OPTIONHANDLER_HANDLE` obtained by calling `xio_retrieveoptions` on the underlying IO created in `http_proxy_io_create`. ]*/ - result = xio_retrieveoptions(http_proxy_io_instance->underlying_io); - if (result == NULL) - { - /* Codes_SRS_HTTP_PROXY_IO_01_048: [ If `xio_retrieveoptions` fails, `http_proxy_io_retrieve_options` shall return NULL. ]*/ - LogError("unable to create option handler"); - } - } - return result; -} - -static const IO_INTERFACE_DESCRIPTION http_proxy_io_interface_description = -{ - http_proxy_io_retrieve_options, - http_proxy_io_create, - http_proxy_io_destroy, - http_proxy_io_open, - http_proxy_io_close, - http_proxy_io_send, - http_proxy_io_dowork, - http_proxy_io_set_option -}; - -const IO_INTERFACE_DESCRIPTION* http_proxy_io_get_interface_description(void) -{ - /* Codes_SRS_HTTP_PROXY_IO_01_049: [ `http_proxy_io_get_interface_description` shall return a pointer to an `IO_INTERFACE_DESCRIPTION` structure that contains pointers to the functions: `http_proxy_io_retrieve_options`, `http_proxy_io_retrieve_create`, `http_proxy_io_destroy`, `http_proxy_io_open`, `http_proxy_io_close`, `http_proxy_io_send` and `http_proxy_io_dowork`. ]*/ - return &http_proxy_io_interface_description; -} -