Azure IoT common library
Fork of azure_c_shared_utility by
Diff: httpapi_compact.c
- Revision:
- 15:956c6d205aa7
- Parent:
- 14:b7e6599cacf5
- Child:
- 18:6d8a413a4d9a
--- a/httpapi_compact.c Fri Oct 21 22:11:02 2016 +0000 +++ b/httpapi_compact.c Wed Nov 16 21:38:39 2016 -0800 @@ -16,10 +16,11 @@ #include "azure_c_shared_utility/platform.h" #include "azure_c_shared_utility/tlsio.h" #include "azure_c_shared_utility/threadapi.h" +#include "azure_c_shared_utility/shared_util_options.h" #include <string.h> #include <limits.h> -/*Codes_SRS_HTTPAPI_COMPACT_21_001: [ The httpapi_compact shall implement the methods defined by the `httpapi.h`. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_001: [ The httpapi_compact shall implement the methods defined by the `httpapi.h`. ]*/ /*Codes_SRS_HTTPAPI_COMPACT_21_002: [ The httpapi_compact shall support the http requests. ]*/ /*Codes_SRS_HTTPAPI_COMPACT_21_003: [ The httpapi_compact shall return error codes defined by HTTPAPI_RESULT. ]*/ #include "azure_c_shared_utility/httpapi.h" @@ -27,41 +28,47 @@ #define MAX_HOSTNAME 64 #define TEMP_BUFFER_SIZE 1024 -/*Codes_SRS_HTTPAPI_COMPACT_21_076: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 times. ]*/ -/*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ -#define MAX_RECEIVE_RETRY 70 -/*Codes_SRS_HTTPAPI_COMPACT_21_078: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ -#define RECEIVE_RETRY_INTERVAL_IN_MICROSECONDS 100 -#define OPEN_RETRY_INTERVAL_IN_MICROSECONDS 100 - +/*Codes_SRS_HTTPAPI_COMPACT_21_077: [ The HTTPAPI_ExecuteRequest shall wait, at least, 10 seconds for the SSL open process. ]*/ +#define MAX_OPEN_RETRY 100 +/*Codes_SRS_HTTPAPI_COMPACT_21_084: [ The HTTPAPI_CloseConnection shall wait, at least, 10 seconds for the SSL close process. ]*/ +#define MAX_CLOSE_RETRY 100 +/*Codes_SRS_HTTPAPI_COMPACT_21_079: [ The HTTPAPI_ExecuteRequest shall wait, at least, 20 seconds to send a buffer using the SSL connection. ]*/ +#define MAX_SEND_RETRY 200 +/*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ +#define MAX_RECEIVE_RETRY 200 +/*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ +#define RETRY_INTERVAL_IN_MICROSECONDS 100 DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES) typedef struct HTTP_HANDLE_DATA_TAG { char* certificate; + char* x509ClientCertificate; + char* x509ClientPrivateKey; XIO_HANDLE xio_handle; size_t received_bytes_count; unsigned char* received_bytes; unsigned int is_io_error : 1; unsigned int is_connected : 1; + unsigned int send_completed : 1; } HTTP_HANDLE_DATA; /*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; + int result; char* next; (*dst) = strtol(src, &next, 0); if ((src == next) || ((((*dst) == LONG_MAX) || ((*dst) == LONG_MIN)) && (errno != 0))) { - result = EOF; + result = EOF; } - else - { - result = 1; - } + else + { + result = 1; + } return result; } @@ -70,27 +77,27 @@ #define HEXA_DIGIT_VAL(c) (((c>='0') && (c<='9')) ? (c-'0') : ((c>='a') && (c<='f')) ? (c-'a'+10) : ((c>='A') && (c<='F')) ? (c-'A'+10) : -1) static int ParseStringToHexadecimal(const char *src, size_t* dst) { - int result; + int result; int digitVal; if (src == NULL) - { - result = EOF; - } - else if (HEXA_DIGIT_VAL(*src) == -1) - { - result = EOF; - } - else - { - (*dst) = 0; - while ((digitVal = HEXA_DIGIT_VAL(*src)) != -1) - { - (*dst) *= 0x10; - (*dst) += (size_t)digitVal; - src++; - } - result = 1; - } + { + result = EOF; + } + else if (HEXA_DIGIT_VAL(*src) == -1) + { + result = EOF; + } + else + { + (*dst) = 0; + while ((digitVal = HEXA_DIGIT_VAL(*src)) != -1) + { + (*dst) *= 0x10; + (*dst) += (size_t)digitVal; + src++; + } + result = 1; + } return result; } @@ -98,88 +105,88 @@ /*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/"; + int result; + static const char HTTPPrefix[] = "HTTP/"; bool fail; const char* runPrefix; - if ((src == NULL) || (dst == NULL)) - { - result = EOF; - } - else - { - fail = false; - runPrefix = HTTPPrefix; + if ((src == NULL) || (dst == NULL)) + { + result = EOF; + } + else + { + fail = false; + runPrefix = HTTPPrefix; - while((*runPrefix) != '\0') - { - if ((*runPrefix) != (*src)) - { - fail = true; - break; - } - src++; - runPrefix++; - } + while((*runPrefix) != '\0') + { + if ((*runPrefix) != (*src)) + { + fail = true; + break; + } + src++; + runPrefix++; + } - if (!fail) - { - while ((*src) != '.') - { - if ((*src) == '\0') - { - fail = true; + if (!fail) + { + while ((*src) != '.') + { + if ((*src) == '\0') + { + fail = true; break; - } - src++; - } - } + } + src++; + } + } - if (!fail) - { - while ((*src) != ' ') - { - if ((*src) == '\0') - { - fail = true; + if (!fail) + { + while ((*src) != ' ') + { + if ((*src) == '\0') + { + fail = true; break; - } - src++; - } - } + } + src++; + } + } - if (fail) - { - result = EOF; - } - else - { - result = ParseStringToDecimal(src, dst); - } - } + if (fail) + { + result = EOF; + } + else + { + result = ParseStringToDecimal(src, dst); + } + } return result; } HTTPAPI_RESULT HTTPAPI_Init(void) { - /*Codes_SRS_HTTPAPI_COMPACT_21_004: [ The HTTPAPI_Init shall allocate all memory to control the http protocol. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_007: [ If there is not enough memory to control the http protocol, the HTTPAPI_Init shall return HTTPAPI_ALLOC_FAILED. ]*/ - /** - * No memory is necessary. - */ +/*Codes_SRS_HTTPAPI_COMPACT_21_004: [ The HTTPAPI_Init shall allocate all memory to control the http protocol. ]*/ +/*Codes_SRS_HTTPAPI_COMPACT_21_007: [ If there is not enough memory to control the http protocol, the HTTPAPI_Init shall return HTTPAPI_ALLOC_FAILED. ]*/ +/** + * No memory is necessary. + */ - /*Codes_SRS_HTTPAPI_COMPACT_21_006: [ If HTTPAPI_Init succeed allocating all the needed memory, it shall return HTTPAPI_OK. ]*/ - return HTTPAPI_OK; + /*Codes_SRS_HTTPAPI_COMPACT_21_006: [ If HTTPAPI_Init succeed allocating all the needed memory, it shall return HTTPAPI_OK. ]*/ +return HTTPAPI_OK; } void HTTPAPI_Deinit(void) { - /*Codes_SRS_HTTPAPI_COMPACT_21_009: [ The HTTPAPI_Init shall release all memory allocated by the httpapi_compact. ]*/ - /** - * No memory was necessary. - */ + /*Codes_SRS_HTTPAPI_COMPACT_21_009: [ The HTTPAPI_Init shall release all memory allocated by the httpapi_compact. ]*/ + /** + * No memory was necessary. + */ } /*Codes_SRS_HTTPAPI_COMPACT_21_011: [ The HTTPAPI_CreateConnection shall create an http connection to the host specified by the hostName parameter. ]*/ @@ -188,75 +195,132 @@ HTTP_HANDLE_DATA* http_instance; TLSIO_CONFIG tlsio_config; - if (hostName == NULL) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_014: [ If the hostName is NULL, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ - LogError("Invalid host name. Null hostName parameter."); - http_instance = NULL; - } - else if (*hostName == '\0') - { - /*Codes_SRS_HTTPAPI_COMPACT_21_015: [ If the hostName is empty, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ - LogError("Invalid host name. Empty string."); - http_instance = NULL; - } - else - { - http_instance = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA)); - /*Codes_SRS_HTTPAPI_COMPACT_21_013: [ If there is not enough memory to control the http connection, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ - if (http_instance == NULL) - { - LogError("There is no memory to control the http connection"); - } - else - { - tlsio_config.hostname = hostName; - tlsio_config.port = 443; + if (hostName == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_014: [ If the hostName is NULL, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ + LogError("Invalid host name. Null hostName parameter."); + http_instance = NULL; + } + else if (*hostName == '\0') + { + /*Codes_SRS_HTTPAPI_COMPACT_21_015: [ If the hostName is empty, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ + LogError("Invalid host name. Empty string."); + http_instance = NULL; + } + else + { + http_instance = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA)); + /*Codes_SRS_HTTPAPI_COMPACT_21_013: [ If there is not enough memory to control the http connection, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ + if (http_instance == NULL) + { + LogError("There is no memory to control the http connection"); + } + else + { + tlsio_config.hostname = hostName; + tlsio_config.port = 443; + + http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config); - http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config); + /*Codes_SRS_HTTPAPI_COMPACT_21_016: [ If the HTTPAPI_CreateConnection failed to create the connection, it shall return NULL as the handle. ]*/ + if (http_instance->xio_handle == NULL) + { + LogError("Create connection failed"); + free(http_instance); + http_instance = NULL; + } + else + { + http_instance->is_connected = 0; + http_instance->is_io_error = 0; + http_instance->received_bytes_count = 0; + http_instance->received_bytes = NULL; + http_instance->certificate = NULL; + http_instance->x509ClientCertificate = NULL; + http_instance->x509ClientPrivateKey = NULL; + } + } + } - /*Codes_SRS_HTTPAPI_COMPACT_21_016: [ If the HTTPAPI_CreateConnection failed to create the connection, it shall return NULL as the handle. ]*/ - if (http_instance->xio_handle == NULL) - { - LogError("Create connection failed"); - free(http_instance); - http_instance = NULL; - } - else - { - http_instance->is_connected = 0; - http_instance->is_io_error = 0; - http_instance->received_bytes_count = 0; - http_instance->received_bytes = NULL; - http_instance->certificate = NULL; - } - } - } + /*Codes_SRS_HTTPAPI_COMPACT_21_012: [ The HTTPAPI_CreateConnection shall return a non-NULL handle on success. ]*/ + return (HTTP_HANDLE)http_instance; +} - /*Codes_SRS_HTTPAPI_COMPACT_21_012: [ The HTTPAPI_CreateConnection shall return a non-NULL handle on success. ]*/ - return (HTTP_HANDLE)http_instance; +static void on_io_close_complete(void* context) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + + if (http_instance != NULL) + { + http_instance->is_connected = 0; + } } void HTTPAPI_CloseConnection(HTTP_HANDLE handle) { HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; - /*Codes_SRS_HTTPAPI_COMPACT_21_020: [ If the connection handle is NULL, the HTTPAPI_CloseConnection shall not do anything. ]*/ - if (http_instance != NULL) + /*Codes_SRS_HTTPAPI_COMPACT_21_020: [ If the connection handle is NULL, the HTTPAPI_CloseConnection shall not do anything. ]*/ + if (http_instance != NULL) { - /*Codes_SRS_HTTPAPI_COMPACT_21_019: [ If there is no previous connection, the HTTPAPI_CloseConnection shall not do anything. ]*/ - if (http_instance->xio_handle != NULL) + /*Codes_SRS_HTTPAPI_COMPACT_21_019: [ If there is no previous connection, the HTTPAPI_CloseConnection shall not do anything. ]*/ + if (http_instance->xio_handle != NULL) { - /*Codes_SRS_HTTPAPI_COMPACT_21_017: [ The HTTPAPI_CloseConnection shall close the connection previously created in HTTPAPI_CreateConnection. ]*/ + http_instance->is_io_error = 0; + /*Codes_SRS_HTTPAPI_COMPACT_21_017: [ The HTTPAPI_CloseConnection shall close the connection previously created in HTTPAPI_ExecuteRequest. ]*/ + if (xio_close(http_instance->xio_handle, on_io_close_complete, http_instance) != 0) + { + LogError("The SSL got error closing the connection"); + /*Codes_SRS_HTTPAPI_COMPACT_21_087: [ If the xio return anything different than 0, the HTTPAPI_CloseConnection shall destroy the connection anyway. ]*/ + http_instance->is_connected = 0; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_084: [ The HTTPAPI_CloseConnection shall wait, at least, 10 seconds for the SSL close process. ]*/ + int countRetry = MAX_CLOSE_RETRY; + while (http_instance->is_connected == 1) + { + xio_dowork(http_instance->xio_handle); + if ((countRetry--) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_085: [ If the HTTPAPI_CloseConnection retries 10 seconds to close the connection without success, it shall destroy the connection anyway. ]*/ + LogError("Close timeout. The SSL didn't close the connection"); + http_instance->is_connected = 0; + } + else if (http_instance->is_io_error == 1) + { + LogError("The SSL got error closing the connection"); + http_instance->is_connected = 0; + } + else if (http_instance->is_connected == 1) + { + LogInfo("Waiting for TLS close connection"); + /*Codes_SRS_HTTPAPI_COMPACT_21_086: [ The HTTPAPI_CloseConnection shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + } + /*Codes_SRS_HTTPAPI_COMPACT_21_076: [ After close the connection, The HTTPAPI_CloseConnection shall destroy the connection previously created in HTTPAPI_CreateConnection. ]*/ xio_destroy(http_instance->xio_handle); } - /*Codes_SRS_HTTPAPI_COMPACT_21_018: [ If there is a certificate associated to this connection, the HTTPAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ - if (http_instance->certificate) + /*Codes_SRS_HTTPAPI_COMPACT_21_018: [ If there is a certificate associated to this connection, the HTTPAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ + if (http_instance->certificate) { free(http_instance->certificate); } + /*Codes_SRS_HTTPAPI_COMPACT_06_001: [ If there is a x509 client certificate associated to this connection, the HTTAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ + if (http_instance->x509ClientCertificate) + { + free(http_instance->x509ClientCertificate); + } + + /*Codes_SRS_HTTPAPI_COMPACT_06_002: [ If there is a x509 client private key associated to this connection, then HTTP_CloseConnection shall free all the allocated memory for the private key. ]*/ + if (http_instance->x509ClientPrivateKey) + { + free(http_instance->x509ClientPrivateKey); + } free(http_instance); } } @@ -264,19 +328,37 @@ static void on_io_open_complete(void* context, IO_OPEN_RESULT open_result) { HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; - + if (http_instance != NULL) - { - if (open_result == IO_OPEN_OK) - { - http_instance->is_connected = 1; - http_instance->is_io_error = 0; - } - else - { - http_instance->is_io_error = 1; - } - } + { + if (open_result == IO_OPEN_OK) + { + http_instance->is_connected = 1; + http_instance->is_io_error = 0; + } + else + { + http_instance->is_io_error = 1; + } + } +} + +static void on_send_complete(void* context, IO_SEND_RESULT send_result) +{ + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; + + if (http_instance != NULL) + { + if (send_result == IO_SEND_OK) + { + http_instance->send_completed = 1; + http_instance->is_io_error = 0; + } + else + { + http_instance->is_io_error = 1; + } + } } #define TOLOWER(c) (((c>='A') && (c<='Z'))?c-'A'+'a':c) @@ -284,26 +366,26 @@ { int result; - if ((s1 == NULL) || (s2 == NULL)) - { - result = -1; - } - else - { - result = 0; - while (((n--) >= 0) && ((*s1) != '\0') && ((*s2) != '\0') && (result == 0)) - { - /* compute the difference between the chars */ - result = TOLOWER(*s1) - TOLOWER(*s2); - s1++; - s2++; - } + if ((s1 == NULL) || (s2 == NULL)) + { + result = -1; + } + else + { + result = 0; + while (((n--) >= 0) && ((*s1) != '\0') && ((*s2) != '\0') && (result == 0)) + { + /* compute the difference between the chars */ + result = TOLOWER(*s1) - TOLOWER(*s2); + s1++; + s2++; + } if ((*s2) != '\0') { result = -1; } - } + } return result; } @@ -344,7 +426,7 @@ } } } - } + } } static void on_io_error(void* context) @@ -361,47 +443,48 @@ { int result; - if ((http_instance == NULL) || (buffer == NULL) || (count < 0)) - { + if ((http_instance == NULL) || (buffer == NULL) || (count < 0)) + { LogError("conn_receive: %s", ((http_instance == NULL) ? "Invalid HTTP instance" : "Invalid HTTP buffer")); result = -1; - } - else - { - result = 0; - while (result < count) - { - xio_dowork(http_instance->xio_handle); + } + else + { + result = 0; + while (result < count) + { + xio_dowork(http_instance->xio_handle); - /* if any error was detected while receiving then simply break and report it */ - if (http_instance->is_io_error != 0) - { + /* if any error was detected while receiving then simply break and report it */ + if (http_instance->is_io_error != 0) + { LogError("xio reported error on dowork"); result = -1; - break; - } + break; + } - if (http_instance->received_bytes_count >= (size_t)count) - { - /* Consuming bytes from the receive buffer */ - (void)memcpy(buffer, http_instance->received_bytes, count); - (void)memmove(http_instance->received_bytes, http_instance->received_bytes + count, http_instance->received_bytes_count - count); - http_instance->received_bytes_count -= count; + if (http_instance->received_bytes_count >= (size_t)count) + { + /* Consuming bytes from the receive buffer */ + (void)memcpy(buffer, http_instance->received_bytes, count); + (void)memmove(http_instance->received_bytes, http_instance->received_bytes + count, http_instance->received_bytes_count - count); + http_instance->received_bytes_count -= count; - /* we're not reallocating at each consumption so that we don't trash due to byte by byte consumption */ - if (http_instance->received_bytes_count == 0) - { - free(http_instance->received_bytes); - http_instance->received_bytes = NULL; - } + /* we're not reallocating at each consumption so that we don't trash due to byte by byte consumption */ + if (http_instance->received_bytes_count == 0) + { + free(http_instance->received_bytes); + http_instance->received_bytes = NULL; + } - result = count; - break; - } + result = count; + break; + } - ThreadAPI_Sleep(RECEIVE_RETRY_INTERVAL_IN_MICROSECONDS); - } - } + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } return result; } @@ -431,7 +514,7 @@ else { char* destByte = buf; - /*Codes_SRS_HTTPAPI_COMPACT_21_076: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 times. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ int countRetry = MAX_RECEIVE_RETRY; bool endOfSearch = false; resultLineSize = -1; @@ -493,13 +576,13 @@ { if ((countRetry--) > 0) { - /*Codes_SRS_HTTPAPI_COMPACT_21_078: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ - ThreadAPI_Sleep(RECEIVE_RETRY_INTERVAL_IN_MICROSECONDS); + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); } else { - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ - LogError("Timeout. The HTTP request is incomplete"); + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + LogError("Receive timeout. The HTTP request is incomplete"); endOfSearch = true; } } @@ -522,10 +605,10 @@ cur = conn_receive(http_instance, buf + offset, (int)size); // end of stream reached - if (cur == 0) - { - break; - } + if (cur == 0) + { + break; + } // read cur bytes (might be less than requested) size -= (size_t)cur; @@ -549,7 +632,7 @@ } else { - /*Codes_SRS_HTTPAPI_COMPACT_21_076: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 times. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ int countRetry = MAX_RECEIVE_RETRY; result = (int)n; while (n > 0) @@ -581,13 +664,13 @@ { if ((countRetry--) > 0) { - /*Codes_SRS_HTTPAPI_COMPACT_21_078: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ - ThreadAPI_Sleep(RECEIVE_RETRY_INTERVAL_IN_MICROSECONDS); + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); } else { - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ - LogError("Timeout. The HTTP request is incomplete"); + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + LogError("Receive timeout. The HTTP request is incomplete"); n = 0; result = -1; } @@ -603,53 +686,127 @@ /*Codes_SRS_HTTPAPI_COMPACT_21_021: [ The HTTPAPI_ExecuteRequest shall execute the http communtication with the provided host, sending a request and reciving the response. ]*/ static HTTPAPI_RESULT OpenXIOConnection(HTTP_HANDLE_DATA* http_instance) { - HTTPAPI_RESULT result; + HTTPAPI_RESULT result; + + if (http_instance->is_connected != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + else + { + http_instance->is_io_error = 0; + + /*Codes_SRS_HTTPAPI_COMPACT_21_022: [ If a Certificate was provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ + if ((http_instance->certificate != NULL) && + (xio_setoption(http_instance->xio_handle, "TrustedCerts", http_instance->certificate) != 0)) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_023: [ If the transport failed setting the Certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ + result = HTTPAPI_SET_OPTION_FAILED; + LogInfo("Could not load certificate"); + } + /*Codes_SRS_HTTPAPI_COMPACT_06_003: [ If the x509 client certificate is provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ + else if ((http_instance->x509ClientCertificate != NULL) && + (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_CERT, http_instance->x509ClientCertificate) != 0)) + { + /*Codes_SRS_HTTPAPI_COMPACT_06_005: [ If the transport failed setting the client certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ + result = HTTPAPI_SET_OPTION_FAILED; + LogInfo("Could not load the client certificate"); + } + else if ((http_instance->x509ClientPrivateKey != NULL) && + (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_instance->x509ClientPrivateKey) != 0)) + { - if (http_instance->is_connected != 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_022: [ If a Certificate was provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ - if ((http_instance->certificate != NULL) && - (xio_setoption(http_instance->xio_handle, "TrustedCerts", http_instance->certificate) != 0)) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_023: [ If the transport failed setting the Certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ - result = HTTPAPI_SET_OPTION_FAILED; - LogInfo("Could not load certificate"); - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ - if (xio_open(http_instance->xio_handle, on_io_open_complete, http_instance, on_bytes_received, http_instance, on_io_error, http_instance) != 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ - result = HTTPAPI_OPEN_REQUEST_FAILED; - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - while ((http_instance->is_connected == 0) && - (http_instance->is_io_error == 0)) - { - xio_dowork(http_instance->xio_handle); - LogInfo("Waiting for TLS connection"); - ThreadAPI_Sleep(OPEN_RETRY_INTERVAL_IN_MICROSECONDS); - } - } - } - } - - if ((http_instance->is_io_error != 0) && (result == HTTPAPI_OK)) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ - result = HTTPAPI_OPEN_REQUEST_FAILED; - } + /*Codes_SRS_HTTPAPI_COMPACT_06_006: [ If the transport failed setting the client certificate private key, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ] */ + result = HTTPAPI_SET_OPTION_FAILED; + LogInfo("Could not load the client certificate private key"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ + if (xio_open(http_instance->xio_handle, on_io_open_complete, http_instance, on_bytes_received, http_instance, on_io_error, http_instance) != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ + result = HTTPAPI_OPEN_REQUEST_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ The HTTPAPI_ExecuteRequest shall wait, at least, 10 seconds for the SSL open process. ]*/ + int countRetry = MAX_OPEN_RETRY; + while ((http_instance->is_connected == 0) && + (http_instance->is_io_error == 0)) + { + xio_dowork(http_instance->xio_handle); + LogInfo("Waiting for TLS connection"); + if ((countRetry--) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_078: [ If the HTTPAPI_ExecuteRequest cannot open the connection in 10 seconds, it shall fail and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ + LogError("Open timeout. The HTTP request is incomplete"); + result = HTTPAPI_OPEN_REQUEST_FAILED; + break; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + } + } + } - return result; + if ((http_instance->is_io_error != 0) && (result == HTTPAPI_OK)) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ + result = HTTPAPI_OPEN_REQUEST_FAILED; + } + + return result; +} + +static HTTPAPI_RESULT conn_send_all(HTTP_HANDLE_DATA* http_instance, const unsigned char* buf, size_t bufLen) +{ + HTTPAPI_RESULT result; + + http_instance->send_completed = 0; + http_instance->is_io_error = 0; + if (xio_send(http_instance->xio_handle, buf, bufLen, on_send_complete, http_instance) != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + result = HTTPAPI_SEND_REQUEST_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_079: [ The HTTPAPI_ExecuteRequest shall wait, at least, 20 seconds to send a buffer using the SSL connection. ]*/ + int countRetry = MAX_SEND_RETRY; + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + while ((http_instance->send_completed == 0) && (result == HTTPAPI_OK)) + { + xio_dowork(http_instance->xio_handle); + if (http_instance->is_io_error != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + result = HTTPAPI_SEND_REQUEST_FAILED; + } + else if ((countRetry--) <= 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_080: [ If the HTTPAPI_ExecuteRequest retries to send the message for 20 seconds without success, it shall fail and return HTTPAPI_SEND_REQUEST_FAILED. ]*/ + LogError("Send timeout. The HTTP request is incomplete"); + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + result = HTTPAPI_SEND_REQUEST_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ + ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); + } + } + } + + return result; } /*Codes_SRS_HTTPAPI_COMPACT_21_035: [ The HTTPAPI_ExecuteRequest shall execute resquest for types `GET`, `POST`, `PUT`, `DELETE`, `PATCH`. ]*/ @@ -662,249 +819,234 @@ /*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ static HTTPAPI_RESULT SendHeadsToXIO(HTTP_HANDLE_DATA* http_instance, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE httpHeadersHandle, size_t headersCount) { - HTTPAPI_RESULT result; - char buf[TEMP_BUFFER_SIZE]; - int ret; + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + int ret; - //Send request - /*Codes_SRS_HTTPAPI_COMPACT_21_038: [ The HTTPAPI_ExecuteRequest shall execute the resquest for the path in relativePath parameter. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_036: [ The request type shall be provided in the parameter requestType. ]*/ - if (((ret = snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\n", get_request_type(requestType), relativePath)) < 0) || - (ret >= sizeof(buf))) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ - result = HTTPAPI_STRING_PROCESSING_ERROR; - } - else if (xio_send(http_instance->xio_handle, (const unsigned char*)buf, strlen(buf), NULL, NULL) != 0) + //Send request + /*Codes_SRS_HTTPAPI_COMPACT_21_038: [ The HTTPAPI_ExecuteRequest shall execute the resquest for the path in relativePath parameter. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_036: [ The request type shall be provided in the parameter requestType. ]*/ + if (((ret = snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\n", get_request_type(requestType), relativePath)) < 0) || + (ret >= sizeof(buf))) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ + result = HTTPAPI_STRING_PROCESSING_ERROR; + } + /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ + else if ((result = conn_send_all(http_instance, (const unsigned char*)buf, strlen(buf))) == HTTPAPI_OK) { - /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ - result = HTTPAPI_SEND_REQUEST_FAILED; - } - else - { - //Send default headers - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - for (size_t i = 0; ((i < headersCount) && (result == HTTPAPI_OK)); i++) - { - char* header; - if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &header) != HTTP_HEADERS_OK) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ - result = HTTPAPI_STRING_PROCESSING_ERROR; - } - else - { - if (xio_send(http_instance->xio_handle, (const unsigned char*)header, strlen(header), NULL, NULL) != 0) + //Send default headers + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + for (size_t i = 0; ((i < headersCount) && (result == HTTPAPI_OK)); i++) + { + char* header; + if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &header) != HTTP_HEADERS_OK) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ + result = HTTPAPI_STRING_PROCESSING_ERROR; + } + else + { + if ((result = conn_send_all(http_instance, (const unsigned char*)header, strlen(header))) == HTTPAPI_OK) { - /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ - result = HTTPAPI_SEND_REQUEST_FAILED; - } - if (xio_send(http_instance->xio_handle, (const unsigned char*)"\r\n", (size_t)2, NULL, NULL) != 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ - result = HTTPAPI_SEND_REQUEST_FAILED; - } - free(header); - } - } + result = conn_send_all(http_instance, (const unsigned char*)"\r\n", (size_t)2); + } + free(header); + } + } - //Close headers - if (xio_send(http_instance->xio_handle, (const unsigned char*)"\r\n", (size_t)2, NULL, NULL) != 0) + //Close headers + if (result == HTTPAPI_OK) { - /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ - result = HTTPAPI_SEND_REQUEST_FAILED; - } - } - return result; + result = conn_send_all(http_instance, (const unsigned char*)"\r\n", (size_t)2); + } + } + return result; } /*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ static HTTPAPI_RESULT SendContentToXIO(HTTP_HANDLE_DATA* http_instance, const unsigned char* content, size_t contentLength) { - HTTPAPI_RESULT result; + HTTPAPI_RESULT result; - //Send data (if available) - /*Codes_SRS_HTTPAPI_COMPACT_21_045: [ If the contentLength is lower than one, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ - if (content && contentLength > 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_044: [ If the content is not NULL, the number of bytes in the content shall be provided in contentLength parameter. ]*/ - if (xio_send(http_instance->xio_handle, content, contentLength, NULL, NULL) != 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_029: [ If the HTTPAPI_ExecuteRequest cannot send the buffer with the request, it shall return HTTPAPI_SEND_REQUEST_FAILED. ]*/ - result = HTTPAPI_SEND_REQUEST_FAILED; - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - } - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_043: [ If the content is NULL, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - } - return result; + //Send data (if available) + /*Codes_SRS_HTTPAPI_COMPACT_21_045: [ If the contentLength is lower than one, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ + if (content && contentLength > 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_044: [ If the content is not NULL, the number of bytes in the content shall be provided in contentLength parameter. ]*/ + result = conn_send_all(http_instance, content, contentLength); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_043: [ If the content is NULL, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + return result; } /*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ static HTTPAPI_RESULT RecieveHeaderFromXIO(HTTP_HANDLE_DATA* http_instance, unsigned int* statusCode) { - HTTPAPI_RESULT result; - char buf[TEMP_BUFFER_SIZE]; - int ret; + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + int ret; + + http_instance->is_io_error = 0; - //Receive response - if (readLine(http_instance, buf, TEMP_BUFFER_SIZE) < 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + //Receive response + if (readLine(http_instance, buf, TEMP_BUFFER_SIZE) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ result = HTTPAPI_READ_DATA_FAILED; - } - //Parse HTTP response - else if (ParseHttpResponse(buf, &ret) != 1) - { - //Cannot match string, error + } + //Parse HTTP response + else if (ParseHttpResponse(buf, &ret) != 1) + { + //Cannot match string, error /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the recived message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ LogInfo("Not a correct HTTP answer"); - result = HTTPAPI_RECEIVE_RESPONSE_FAILED; - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_046: [ The HTTPAPI_ExecuteRequest shall return the http status reported by the host in the received response. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_048: [ If the statusCode is NULL, the HTTPAPI_ExecuteRequest shall report not report any status. ]*/ - if (statusCode) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_047: [ The HTTPAPI_ExecuteRequest shall report the status in the statusCode parameter. ]*/ - *statusCode = ret; - } - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - } + result = HTTPAPI_RECEIVE_RESPONSE_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_046: [ The HTTPAPI_ExecuteRequest shall return the http status reported by the host in the received response. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_048: [ If the statusCode is NULL, the HTTPAPI_ExecuteRequest shall report not report any status. ]*/ + if (statusCode) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_047: [ The HTTPAPI_ExecuteRequest shall report the status in the statusCode parameter. ]*/ + *statusCode = ret; + } + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } - return result; + return result; } static HTTPAPI_RESULT RecieveContentInfoFromXIO(HTTP_HANDLE_DATA* http_instance, HTTP_HEADERS_HANDLE responseHeadersHandle, size_t* bodyLength, bool* chunked) { - HTTPAPI_RESULT result; - char buf[TEMP_BUFFER_SIZE]; + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; const char* substr; char* whereIsColon; int lengthInMsg; - const char* ContentLength = "content-length:"; - const int ContentLengthSize = 16; - const char* TransferEncoding = "transfer-encoding:"; - const int TransferEncodingSize = 19; - const char* Chunked = "chunked"; - const int ChunkedSize = 8; + const char* ContentLength = "content-length:"; + const int ContentLengthSize = 16; + const char* TransferEncoding = "transfer-encoding:"; + const int TransferEncodingSize = 19; + const char* Chunked = "chunked"; + const int ChunkedSize = 8; + + http_instance->is_io_error = 0; - //Read HTTP response headers - if (readLine(http_instance, buf, sizeof(buf)) < 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + //Read HTTP response headers + if (readLine(http_instance, buf, sizeof(buf)) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ result = HTTPAPI_READ_DATA_FAILED; - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; - while (*buf && (result == HTTPAPI_OK)) - { - if (InternStrnicmp(buf, ContentLength, ContentLengthSize) == 0) - { - substr = buf + ContentLengthSize - 1; - if (ParseStringToDecimal(substr, &lengthInMsg) != 1) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ - result = HTTPAPI_READ_DATA_FAILED; - } - else - { - (*bodyLength) = (size_t)lengthInMsg; - } - } - else if (InternStrnicmp(buf, TransferEncoding, TransferEncodingSize) == 0) - { - substr = buf + TransferEncodingSize - 1; + while (*buf && (result == HTTPAPI_OK)) + { + if (InternStrnicmp(buf, ContentLength, ContentLengthSize) == 0) + { + substr = buf + ContentLengthSize - 1; + if (ParseStringToDecimal(substr, &lengthInMsg) != 1) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else + { + (*bodyLength) = (size_t)lengthInMsg; + } + } + else if (InternStrnicmp(buf, TransferEncoding, TransferEncodingSize) == 0) + { + substr = buf + TransferEncodingSize - 1; + + while (isspace(*substr)) substr++; - while (isspace(*substr)) substr++; - - if (InternStrnicmp(substr, Chunked, ChunkedSize) == 0) - { - (*chunked) = true; - } - } + if (InternStrnicmp(substr, Chunked, ChunkedSize) == 0) + { + (*chunked) = true; + } + } - if (result == HTTPAPI_OK) - { - whereIsColon = strchr((char*)buf, ':'); - /*Codes_SRS_HTTPAPI_COMPACT_21_049: [ If responseHeadersHandle is provide, the HTTPAPI_ExecuteRequest shall prepare a Response Header usign the HTTPHeaders_AddHeaderNameValuePair. ]*/ - if (whereIsColon && (responseHeadersHandle != NULL)) - { - *whereIsColon = '\0'; - HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1); - } + if (result == HTTPAPI_OK) + { + whereIsColon = strchr((char*)buf, ':'); + /*Codes_SRS_HTTPAPI_COMPACT_21_049: [ If responseHeadersHandle is provide, the HTTPAPI_ExecuteRequest shall prepare a Response Header usign the HTTPHeaders_AddHeaderNameValuePair. ]*/ + if (whereIsColon && (responseHeadersHandle != NULL)) + { + *whereIsColon = '\0'; + HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1); + } - if (readLine(http_instance, buf, sizeof(buf)) < 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + if (readLine(http_instance, buf, sizeof(buf)) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ result = HTTPAPI_READ_DATA_FAILED; - } - } - } - } + } + } + } + } - return result; + return result; } static HTTPAPI_RESULT ReadHTTPResponseBodyFromXIO(HTTP_HANDLE_DATA* http_instance, size_t bodyLength, bool chunked, BUFFER_HANDLE responseContent) { - HTTPAPI_RESULT result; - char buf[TEMP_BUFFER_SIZE]; - const unsigned char* receivedContent; + HTTPAPI_RESULT result; + char buf[TEMP_BUFFER_SIZE]; + const unsigned char* receivedContent; + + http_instance->is_io_error = 0; - //Read HTTP response body - if (!chunked) - { - if (bodyLength) - { - if (responseContent != NULL) - { - if (BUFFER_pre_build(responseContent, bodyLength) != 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ - result = HTTPAPI_ALLOC_FAILED; - } - else if (BUFFER_content(responseContent, &receivedContent) != 0) - { - (void)BUFFER_unbuild(responseContent); + //Read HTTP response body + if (!chunked) + { + if (bodyLength) + { + if (responseContent != NULL) + { + if (BUFFER_pre_build(responseContent, bodyLength) != 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (BUFFER_content(responseContent, &receivedContent) != 0) + { + (void)BUFFER_unbuild(responseContent); - /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ - result = HTTPAPI_ALLOC_FAILED; - } - else if (readChunk(http_instance, (char*)receivedContent, bodyLength) < 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ - result = HTTPAPI_READ_DATA_FAILED; - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - } - } + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (readChunk(http_instance, (char*)receivedContent, bodyLength) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + result = HTTPAPI_READ_DATA_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + } else { /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ if (skipN(http_instance, bodyLength) < 0) { - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ result = HTTPAPI_READ_DATA_FAILED; } else @@ -912,114 +1054,114 @@ result = HTTPAPI_OK; } } - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - } - } - else - { - size_t size = 0; - /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ - result = HTTPAPI_OK; - while (result == HTTPAPI_OK) - { - size_t chunkSize; - if (readLine(http_instance, buf, sizeof(buf)) < 0) // read [length in hex]/r/n - { - /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + } + } + else + { + size_t size = 0; + /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ + result = HTTPAPI_OK; + while (result == HTTPAPI_OK) + { + size_t chunkSize; + if (readLine(http_instance, buf, sizeof(buf)) < 0) // read [length in hex]/r/n + { + /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ result = HTTPAPI_READ_DATA_FAILED; - } - else if (ParseStringToHexadecimal(buf, &chunkSize) != 1) // chunkSize is length of next line (/r/n is not counted) - { - //Cannot match string, error - /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the recived message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ - result = HTTPAPI_RECEIVE_RESPONSE_FAILED; - } - else if (chunkSize == 0) - { - // 0 length means next line is just '\r\n' and end of chunks - if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 - || buf[0] != '\r' || buf[1] != '\n') // skip /r/n - { - (void)BUFFER_unbuild(responseContent); + } + else if (ParseStringToHexadecimal(buf, &chunkSize) != 1) // chunkSize is length of next line (/r/n is not counted) + { + //Cannot match string, error + /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the recived message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ + result = HTTPAPI_RECEIVE_RESPONSE_FAILED; + } + else if (chunkSize == 0) + { + // 0 length means next line is just '\r\n' and end of chunks + if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 + || buf[0] != '\r' || buf[1] != '\n') // skip /r/n + { + (void)BUFFER_unbuild(responseContent); - result = HTTPAPI_READ_DATA_FAILED; - } - break; - } - else - { - if (responseContent != NULL) - { - if (BUFFER_enlarge(responseContent, chunkSize) != 0) - { - (void)BUFFER_unbuild(responseContent); + result = HTTPAPI_READ_DATA_FAILED; + } + break; + } + else + { + if (responseContent != NULL) + { + if (BUFFER_enlarge(responseContent, chunkSize) != 0) + { + (void)BUFFER_unbuild(responseContent); - /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ - result = HTTPAPI_ALLOC_FAILED; - } - else if (BUFFER_content(responseContent, &receivedContent) != 0) - { - (void)BUFFER_unbuild(responseContent); + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (BUFFER_content(responseContent, &receivedContent) != 0) + { + (void)BUFFER_unbuild(responseContent); - /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ - result = HTTPAPI_ALLOC_FAILED; - } - else if (readChunk(http_instance, (char*)receivedContent + size, chunkSize) < 0) - { - result = HTTPAPI_READ_DATA_FAILED; - } - } - else - { - /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ - if (skipN(http_instance, chunkSize) < 0) - { - /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ If the HTTPAPI_ExecuteRequest retries 20 times to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else if (readChunk(http_instance, (char*)receivedContent + size, chunkSize) < 0) + { + result = HTTPAPI_READ_DATA_FAILED; + } + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ + if (skipN(http_instance, chunkSize) < 0) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ result = HTTPAPI_READ_DATA_FAILED; - } - } + } + } - if (result == HTTPAPI_OK) - { - if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 - || buf[0] != '\r' || buf[1] != '\n') // skip /r/n - { - result = HTTPAPI_READ_DATA_FAILED; - } - size += chunkSize; - } - } - } + if (result == HTTPAPI_OK) + { + if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 + || buf[0] != '\r' || buf[1] != '\n') // skip /r/n + { + result = HTTPAPI_READ_DATA_FAILED; + } + size += chunkSize; + } + } + } - } - return result; + } + return result; } /*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ static bool validRequestType(HTTPAPI_REQUEST_TYPE requestType) { - bool result; + bool result; - if ((requestType == HTTPAPI_REQUEST_GET) || - (requestType == HTTPAPI_REQUEST_POST) || - (requestType == HTTPAPI_REQUEST_PUT) || - (requestType == HTTPAPI_REQUEST_DELETE) || - (requestType == HTTPAPI_REQUEST_PATCH)) - { - result = true; - } - else - { - result = false; - } + if ((requestType == HTTPAPI_REQUEST_GET) || + (requestType == HTTPAPI_REQUEST_POST) || + (requestType == HTTPAPI_REQUEST_PUT) || + (requestType == HTTPAPI_REQUEST_DELETE) || + (requestType == HTTPAPI_REQUEST_PATCH)) + { + result = true; + } + else + { + result = false; + } - return result; + return result; } /*Codes_SRS_HTTPAPI_COMPACT_21_021: [ The HTTPAPI_ExecuteRequest shall execute the http communtication with the provided host, sending a request and reciving the response. ]*/ @@ -1035,65 +1177,58 @@ size_t headersCount; size_t bodyLength = 0; bool chunked = false; - HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; + HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; - /*Codes_SRS_HTTPAPI_COMPACT_21_034: [ If there is no previous connection, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_039: [ If the relativePath is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_041: [ If the httpHeadersHandle is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_053: [ The HTTPAPI_ExecuteRequest shall produce a set of http header to send to the host. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_040: [ The request shall contain the http header provided in httpHeadersHandle parameter. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_054: [ If Http header maker cannot provide the number of headers, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ - if (http_instance == NULL || + /*Codes_SRS_HTTPAPI_COMPACT_21_034: [ If there is no previous connection, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_039: [ If the relativePath is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_041: [ If the httpHeadersHandle is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_053: [ The HTTPAPI_ExecuteRequest shall produce a set of http header to send to the host. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_040: [ The request shall contain the http header provided in httpHeadersHandle parameter. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_054: [ If Http header maker cannot provide the number of headers, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ + if (http_instance == NULL || relativePath == NULL || httpHeadersHandle == NULL || - !validRequestType(requestType) || + !validRequestType(requestType) || HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK) { result = HTTPAPI_INVALID_ARG; LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); } - /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ - else if ((result = OpenXIOConnection(http_instance)) != HTTPAPI_OK) - { - LogError("Open HTTP connection failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); - } - /*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ - else if ((result = SendHeadsToXIO(http_instance, requestType, relativePath, httpHeadersHandle, headersCount)) != HTTPAPI_OK) - { - LogError("Send heads to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); - } - /*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ - else if ((result = SendContentToXIO(http_instance, content, contentLength)) != HTTPAPI_OK) - { - LogError("Send content to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); - } - /*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_073: [ The message recived by the HTTPAPI_ExecuteRequest shall starts with a valid header. ]*/ - else if ((result = RecieveHeaderFromXIO(http_instance, statusCode)) != HTTPAPI_OK) - { - LogError("Receive header from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); - } - /*Codes_SRS_HTTPAPI_COMPACT_21_074: [ After the header, the message recieved by the HTTPAPI_ExecuteRequest can contain addition information about the content. ]*/ - else if ((result = RecieveContentInfoFromXIO(http_instance, responseHeadersHandle, &bodyLength, &chunked)) != HTTPAPI_OK) - { - LogError("Receive content information from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); - } - /*Codes_SRS_HTTPAPI_COMPACT_21_075: [ The message recieved by the HTTPAPI_ExecuteRequest can contain a body with the message content. ]*/ - else if ((result = ReadHTTPResponseBodyFromXIO(http_instance, bodyLength, chunked, responseContent)) != HTTPAPI_OK) - { - LogError("Read HTTP response body from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); - } + /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ + else if ((result = OpenXIOConnection(http_instance)) != HTTPAPI_OK) + { + LogError("Open HTTP connection failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ + else if ((result = SendHeadsToXIO(http_instance, requestType, relativePath, httpHeadersHandle, headersCount)) != HTTPAPI_OK) + { + LogError("Send heads to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ + else if ((result = SendContentToXIO(http_instance, content, contentLength)) != HTTPAPI_OK) + { + LogError("Send content to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_073: [ The message recived by the HTTPAPI_ExecuteRequest shall starts with a valid header. ]*/ + else if ((result = RecieveHeaderFromXIO(http_instance, statusCode)) != HTTPAPI_OK) + { + LogError("Receive header from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_074: [ After the header, the message recieved by the HTTPAPI_ExecuteRequest can contain addition information about the content. ]*/ + else if ((result = RecieveContentInfoFromXIO(http_instance, responseHeadersHandle, &bodyLength, &chunked)) != HTTPAPI_OK) + { + LogError("Receive content information from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } + /*Codes_SRS_HTTPAPI_COMPACT_21_075: [ The message recieved by the HTTPAPI_ExecuteRequest can contain a body with the message content. ]*/ + else if ((result = ReadHTTPResponseBodyFromXIO(http_instance, bodyLength, chunked, responseContent)) != HTTPAPI_OK) + { + LogError("Read HTTP response body from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); + } conn_receive_discard_buffer(http_instance); - /*Codes_SRS_HTTPAPI_COMPACT_21_031: [ After receive the response, the HTTPAPI_ExecuteRequest shall close the transport connection with the host. ]*/ - if ((http_instance != NULL) && - (http_instance->is_connected != 0)) - { - xio_close(http_instance->xio_handle, NULL, NULL); - http_instance->is_connected = 0; - } return result; } @@ -1106,16 +1241,16 @@ HTTPAPI_RESULT result; HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; - if ( + if ( (http_instance == NULL) || (optionName == NULL) || (value == NULL) ) { - /*Codes_SRS_HTTPAPI_COMPACT_21_059: [ If the handle is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_060: [ If the optionName is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_061: [ If the value is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ - result = HTTPAPI_INVALID_ARG; + /*Codes_SRS_HTTPAPI_COMPACT_21_059: [ If the handle is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_060: [ If the optionName is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_061: [ If the value is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; } else if (strcmp("TrustedCerts", optionName) == 0) { @@ -1128,20 +1263,64 @@ http_instance->certificate = (char*)malloc((len + 1) * sizeof(char)); if (http_instance->certificate == NULL) { - /*SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ - result = HTTPAPI_ALLOC_FAILED; + /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; LogInfo("unable to allocate memory for the certificate in HTTPAPI_SetOption"); } else { - /*SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ (void)strcpy(http_instance->certificate, (const char*)value); result = HTTPAPI_OK; } } + else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0) + { + if (http_instance->x509ClientCertificate) + { + free(http_instance->x509ClientCertificate); + } + + int len = (int)strlen((char*)value); + http_instance->x509ClientCertificate = (char*)malloc((len + 1) * sizeof(char)); + if (http_instance->x509ClientCertificate == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + LogInfo("unable to allocate memory for the client certificate in HTTPAPI_SetOption"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(http_instance->x509ClientCertificate, (const char*)value); + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) + { + if (http_instance->x509ClientPrivateKey) + { + free(http_instance->x509ClientPrivateKey); + } + + int len = (int)strlen((char*)value); + http_instance->x509ClientPrivateKey = (char*)malloc((len + 1) * sizeof(char)); + if (http_instance->x509ClientPrivateKey == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + LogInfo("unable to allocate memory for the client private key in HTTPAPI_SetOption"); + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(http_instance->x509ClientPrivateKey, (const char*)value); + result = HTTPAPI_OK; + } + } else { - /*Codes_SRS_HTTPAPI_COMPACT_21_063: [ If the HTTP do not support the optionName, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_063: [ If the HTTP do not support the optionName, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ result = HTTPAPI_INVALID_ARG; LogInfo("unknown option %s", optionName); } @@ -1156,16 +1335,16 @@ size_t certLen; char* tempCert; - if ( + if ( (optionName == NULL) || (value == NULL) || (savedValue == NULL) ) { - /*Codes_SRS_HTTPAPI_COMPACT_21_067: [ If the optionName is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_068: [ If the value is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ - /*Codes_SRS_HTTPAPI_COMPACT_21_069: [ If the savedValue is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ - result = HTTPAPI_INVALID_ARG; + /*Codes_SRS_HTTPAPI_COMPACT_21_067: [ If the optionName is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_068: [ If the value is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + /*Codes_SRS_HTTPAPI_COMPACT_21_069: [ If the savedValue is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; } else if (strcmp("TrustedCerts", optionName) == 0) { @@ -1173,21 +1352,55 @@ tempCert = (char*)malloc((certLen + 1) * sizeof(char)); if (tempCert == NULL) { - /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ - result = HTTPAPI_ALLOC_FAILED; + /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; } else { - /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ - (void)strcpy(tempCert, (const char*)value); + /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(tempCert, (const char*)value); + *savedValue = tempCert; + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0) + { + certLen = strlen((const char*)value); + tempCert = (char*)malloc((certLen + 1) * sizeof(char)); + if (tempCert == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(tempCert, (const char*)value); + *savedValue = tempCert; + result = HTTPAPI_OK; + } + } + else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) + { + certLen = strlen((const char*)value); + tempCert = (char*)malloc((certLen + 1) * sizeof(char)); + if (tempCert == NULL) + { + /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ + result = HTTPAPI_ALLOC_FAILED; + } + else + { + /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ + (void)strcpy(tempCert, (const char*)value); *savedValue = tempCert; result = HTTPAPI_OK; } } else { - /*Codes_SRS_HTTPAPI_COMPACT_21_071: [ If the HTTP do not support the optionName, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ - result = HTTPAPI_INVALID_ARG; + /*Codes_SRS_HTTPAPI_COMPACT_21_071: [ If the HTTP do not support the optionName, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ + result = HTTPAPI_INVALID_ARG; LogInfo("unknown option %s", optionName); } return result;