Azure IoT common library

Fork of azure_c_shared_utility by Azure IoT

httpapi_compact.c

Committer:
AzureIoTClient
Date:
2016-09-09
Revision:
11:77df6d7e65ae
Parent:
7:1af47e3a19b6
Child:
13:920e00014ee3

File content as of revision 11:77df6d7e65ae:

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <stdlib.h>
#ifdef _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

#include <stdio.h>
#include <ctype.h>
#include "azure_c_shared_utility/gballoc.h"
#include "azure_c_shared_utility/httpheaders.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/xio.h"
#include "azure_c_shared_utility/platform.h"
#include "azure_c_shared_utility/tlsio.h"
#include "azure_c_shared_utility/threadapi.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_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"

#define MAX_HOSTNAME     64
#define TEMP_BUFFER_SIZE 1024


DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES)

typedef enum SEND_ALL_RESULT_TAG
{
    SEND_ALL_RESULT_NOT_STARTED,
    SEND_ALL_RESULT_PENDING,
    SEND_ALL_RESULT_OK,
    SEND_ALL_RESULT_ERROR
} SEND_ALL_RESULT;

typedef struct HTTP_HANDLE_DATA_TAG
{
    char*           certificate;
    XIO_HANDLE      xio_handle;
    size_t          received_bytes_count;
    unsigned char*  received_bytes;
    SEND_ALL_RESULT send_all_result;
    unsigned int    is_io_error : 1;
    unsigned int    is_connected : 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;
    char* next;
    (*dst) = strtol(src, &next, 0);
    if ((src == next) || ((((*dst) == LONG_MAX) || ((*dst) == LONG_MIN)) && (errno != 0)))
    {
		result = EOF;
    }
	else
	{
		result = 1;
	}
    return result;
}

/*the following function does the same as sscanf(pos2, "%x", &sec)*/
/*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined x numbers. */
#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 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;
	}
    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 = EOF;
	}
	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 = 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_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_011: [ The HTTPAPI_CreateConnection shall create an http connection to the host specified by the hostName parameter. ]*/
HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
{
    HTTP_HANDLE_DATA* httpHandle;
    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.");
		httpHandle = 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.");
		httpHandle = NULL;
	}
	else
	{
		httpHandle = (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 (httpHandle == NULL)
		{
			LogError("There is no memory to control the http connection");
		}
		else
		{
			tlsio_config.hostname = hostName;
			tlsio_config.port = 443;

			httpHandle->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 (httpHandle->xio_handle == NULL)
			{
				LogError("Create connection failed");
				free(httpHandle);
				httpHandle = NULL;
			}
			else
			{
				httpHandle->is_connected = 0;
				httpHandle->is_io_error = 0;
				httpHandle->received_bytes_count = 0;
				httpHandle->received_bytes = NULL;
				httpHandle->send_all_result = SEND_ALL_RESULT_NOT_STARTED;
				httpHandle->certificate = NULL;
			}
		}
	}

	/*Codes_SRS_HTTPAPI_COMPACT_21_012: [ The HTTPAPI_CreateConnection shall return a non-NULL handle on success. ]*/
	return (HTTP_HANDLE)httpHandle;
}

void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
{
    HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)handle;

	/*Codes_SRS_HTTPAPI_COMPACT_21_020: [ If the connection handle is NULL, the HTTPAPI_CloseConnection shall not do anything. ]*/
	if (httpHandle != NULL)
    {
		/*Codes_SRS_HTTPAPI_COMPACT_21_019: [ If there is no previous connection, the HTTPAPI_CloseConnection shall not do anything. ]*/
		if (httpHandle->xio_handle != NULL)
        {
			/*Codes_SRS_HTTPAPI_COMPACT_21_017: [ The HTTPAPI_CloseConnection shall close the connection previously created in HTTPAPI_CreateConnection. ]*/
			LogInfo("Close http connection.");
            xio_destroy(httpHandle->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 (httpHandle->certificate)
        {
            free(httpHandle->certificate);
        }

        free(httpHandle);
    }
}

static void on_io_open_complete(void* context, IO_OPEN_RESULT open_result)
{
    HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)context;
    
    if (httpHandle != NULL)
	{
		if (open_result == IO_OPEN_OK)
		{
			httpHandle->is_connected = 1;
			httpHandle->is_io_error = 0;
		}
		else
		{
			httpHandle->is_io_error = 1;
		}
	}
}

#define TOLOWER(c) (((c>='A') && (c<='Z'))?c-'A'+'a':c)
static int InternStrnicmp(const char* s1, const char* s2, size_t n)
{
    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 ((*s2) != '\0')
        {
            result = -1;
        }
	}

    return result;
}

static void on_bytes_received(void* context, const unsigned char* buffer, size_t size)
{
    unsigned char* new_received_bytes;
    HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)context;

    if (httpHandle != NULL)
	{

        if (buffer == NULL)
        {
            httpHandle->is_io_error = 1;
            LogError("on_bytes_received: NULL pointer error");
        }
        else
        {
            /* Here we got some bytes so we'll buffer them so the receive functions can consumer it */
            new_received_bytes = (unsigned char*)realloc(httpHandle->received_bytes, httpHandle->received_bytes_count + size);
            if (new_received_bytes == NULL)
            {
                httpHandle->is_io_error = 1;
                LogError("on_bytes_received: Error allocating memory for received data");
            }
            else
            {
                httpHandle->received_bytes = new_received_bytes;
                if (memcpy(httpHandle->received_bytes + httpHandle->received_bytes_count, buffer, size) == NULL)
                {
                    httpHandle->is_io_error = 1;
                    LogError("on_bytes_received: Error copping received data to the HTTP bufffer");
                }
                else
                {
                    httpHandle->received_bytes_count += size;
                }
            }
        }
	}
}

static void on_io_error(void* context)
{
    HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)context;
    if (httpHandle != NULL)
	{
		httpHandle->is_io_error = 1;
		LogError("on_io_error: Error signalled by underlying IO");
	}
}

static int conn_receive(HTTP_HANDLE_DATA* http_instance, char* buffer, int count)
{
    int result;

	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);

			/* if any error was detected while receiving then simply break and report it */
			if (http_instance->is_io_error != 0)
			{
                LogError("conn_receive: xio reported error on dowork");
                result = -1;
				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;

				/* 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;
			}

			ThreadAPI_Sleep(1);
		}
	}

    return result;
}

static void conn_receive_discard_buffer(HTTP_HANDLE_DATA* http_instance)
{
    if (http_instance->received_bytes != NULL)
    {
        free(http_instance->received_bytes);
        http_instance->received_bytes = NULL;
    }
    http_instance->received_bytes_count = 0;
}


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 a send is complete we'll simply signal this by changing the send all state */
		if (send_result == IO_SEND_OK)
		{
			http_instance->send_all_result = SEND_ALL_RESULT_OK;
		}
		else
		{
			http_instance->send_all_result = SEND_ALL_RESULT_ERROR;
		}
	}
}

static int conn_send_all(HTTP_HANDLE_DATA* http_instance, const unsigned char* buffer, size_t count)
{
    int result;

	if ((http_instance == NULL) || (buffer == 0) || (count < (size_t)0))
	{
        LogError("conn_send_all: %s", ((http_instance == NULL) ? "Invalid HTTP instance" : "Invalid HTTP buffer"));
        result = -1;
    }
    else
    {
        http_instance->send_all_result = SEND_ALL_RESULT_PENDING;
        if (xio_send(http_instance->xio_handle, buffer, count, on_send_complete, http_instance) != 0)
        {
            LogError("conn_send_all: xio failed sending data");
            result = -1;
        }
        else
        {
            /* We have to loop in here until all bytes are sent or we encounter an error. */
            while (1)
            {
                /* If we got an error signalled from the underlying IO we simply report it up */
                if (http_instance->is_io_error)
                {
                    http_instance->send_all_result = SEND_ALL_RESULT_ERROR;
                    break;
                }

                if (http_instance->send_all_result != SEND_ALL_RESULT_PENDING)
                {
                    break;
                }

                xio_dowork(http_instance->xio_handle);

                /* We yield the CPU for a bit so others can do their work */
                ThreadAPI_Sleep(1);
            }

            /* The send_all_result indicates what is the status for the send operation.
               Not started - means nothing should happen since no send was started
               Pending - a send was started, but it is still being carried out 
               Ok - Send complete
               Error - error */
            switch (http_instance->send_all_result)
            {
                default:
                case SEND_ALL_RESULT_NOT_STARTED:
                    result = -1;
                    break;

                case SEND_ALL_RESULT_OK:
                    result = (int)count;
                    break;

                case SEND_ALL_RESULT_ERROR:
                    result = -1;
                    break;
            }
        }
    }

    return result;
}

static int readLine(HTTP_HANDLE_DATA* http_instance, char* buf, const size_t size)
{
    // reads until \r\n is encountered. writes in buf all the characters
	int result;
    char* substr = buf;
    char  c;

	if (conn_receive(http_instance, &c, 1) < 0)
	{
        result = -1;
	}
	else
	{
		result = 0;
		while (c != '\r') 
		{
			if ((substr - buf + 1) >= (int)size)
			{
                result = -1;
                conn_receive_discard_buffer(http_instance);
                break;
			}
            else
            {
                *substr++ = c;
            }
			
			if (conn_receive(http_instance, &c, 1) < 0)
			{
				result = -1;
				break;
			}
		}

		if (result != -1)
		{
			*substr = 0;
			if (conn_receive(http_instance, &c, 1) < 0 || c != '\n') // skip \n
			{
				result = -1;
			}
		}

        if (result != -1)
        {
            result = (int)(substr - buf);
        }
    }

	return result;
}

static int readChunk(HTTP_HANDLE_DATA* http_instance, char* buf, size_t size)
{
    int cur, offset;

    // read content with specified length, even if it is received
    // only in chunks due to fragmentation in the networking layer.
    // returns -1 in case of error.
    offset = 0;
    while (size > (size_t)0)
    {
        cur = conn_receive(http_instance, buf + offset, (int)size);

        // end of stream reached
		if (cur == 0)
		{
			break;
		}

        // read cur bytes (might be less than requested)
        size -= (size_t)cur;
        offset += cur;
    }

    return offset;
}

static int skipN(HTTP_HANDLE_DATA* http_instance, size_t n, char* buf, size_t size)
{
    int org = (int)n;
    // read and abandon response content with specified length
    // returns -1 in case of error.
    while (n > size)
    {
		if (readChunk(http_instance, (char*)buf, size) < 0)
		{
			org = -1;
			break;
		}

        n -= size;
    }

	if (org >= 0)
	{
		if (readChunk(http_instance, (char*)buf, n) < 0)
		{
			org = -1;
		}
	}

    return org;
}


/*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* httpHandle)
{
	HTTPAPI_RESULT result;

	if (httpHandle->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 ((httpHandle->certificate != NULL) &&
			(xio_setoption(httpHandle->xio_handle, "TrustedCerts", httpHandle->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(httpHandle->xio_handle, on_io_open_complete, httpHandle, on_bytes_received, httpHandle, on_io_error, httpHandle) != 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 ((httpHandle->is_connected == 0) &&
					(httpHandle->is_io_error == 0))
				{
					xio_dowork(httpHandle->xio_handle);
					LogInfo("Waiting for TLS connection");
					ThreadAPI_Sleep(1);
				}
			}
		}
	}
	
	if ((httpHandle->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;
}

/*Codes_SRS_HTTPAPI_COMPACT_21_035: [ The HTTPAPI_ExecuteRequest shall execute resquest for types `GET`, `POST`, `PUT`, `DELETE`, `PATCH`. ]*/
const char httpapiRequestString[5][7] = { "GET", "POST", "PUT", "DELETE", "PATCH" };
const char* get_request_type(HTTPAPI_REQUEST_TYPE requestType)
{
    return (const char*)httpapiRequestString[requestType];
}

/*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* httpHandle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE httpHeadersHandle, size_t headersCount)
{
	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 (conn_send_all(httpHandle, (const unsigned char*)buf, strlen(buf)) < 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
	{
		//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 (conn_send_all(httpHandle, (const unsigned char*)header, strlen(header)) < 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;
				}
				if (conn_send_all(httpHandle, (const unsigned char*)"\r\n", (size_t)2) < 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);
			}
		}

		//Close headers
		if (conn_send_all(httpHandle, (const unsigned char*)"\r\n", (size_t)2) < 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;
		}
	}
	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* httpHandle, const unsigned char* content, size_t contentLength)
{
	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 (conn_send_all(httpHandle, content, contentLength) < 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;
}

/*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* httpHandle, unsigned int* statusCode)
{
	HTTPAPI_RESULT result;
	char    buf[TEMP_BUFFER_SIZE];
	int     ret;

	//Receive response
	if (readLine(httpHandle, 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. ]*/
		result = HTTPAPI_READ_DATA_FAILED;
	}
	//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("HTTPAPI_ExecuteRequest::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;
	}

	return result;
}

static HTTPAPI_RESULT RecieveContentInfoFromXIO(HTTP_HANDLE_DATA* httpHandle, HTTP_HEADERS_HANDLE responseHeadersHandle, size_t* bodyLength, bool* chunked)
{
	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;

	//Read HTTP response headers
	if (readLine(httpHandle, 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. ]*/
		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;

		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++;
				
				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 (readLine(httpHandle, 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. ]*/
					result = HTTPAPI_READ_DATA_FAILED;
				}
			}
		}
	}

	return result;
}

static HTTPAPI_RESULT ReadHTTPResponseBodyFromXIO(HTTP_HANDLE_DATA* httpHandle, size_t bodyLength, bool chunked, BUFFER_HANDLE responseContent)
{
	HTTPAPI_RESULT result;
	char    buf[TEMP_BUFFER_SIZE];
	const unsigned char* receivedContent;

	//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(httpHandle, (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. ]*/
				(void)skipN(httpHandle, bodyLength, buf, sizeof(buf));
				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(httpHandle, 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. ]*/
				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(httpHandle, (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);

						/*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(httpHandle, (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(httpHandle, chunkSize, buf, sizeof(buf)) < 0)
					{
						result = HTTPAPI_READ_DATA_FAILED;
					}
				}

				if (result == HTTPAPI_OK)
				{
					if (readChunk(httpHandle, (char*)buf, (size_t)2) < 0
						|| buf[0] != '\r' || buf[1] != '\n') // skip /r/n
					{
						result = HTTPAPI_READ_DATA_FAILED;
					}
					size += chunkSize;
				}
			}
		}

	}
	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;

	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;
}

/*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. ]*/
/*Codes_SRS_HTTPAPI_COMPACT_21_050: [ If there is a content in the response, the HTTPAPI_ExecuteRequest shall copy it in the responseContent buffer. ]*/
//Note: This function assumes that "Host:" and "Content-Length:" headers are setup
//      by the caller of HTTPAPI_ExecuteRequest() (which is true for httptransport.c).
HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath,
    HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content,
    size_t contentLength, unsigned int* statusCode,
    HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent)
{
    HTTPAPI_RESULT result = HTTPAPI_ERROR;
    size_t  headersCount;
    size_t  bodyLength = 0;
    bool    chunked = false;
	HTTP_HANDLE_DATA* httpHandle = (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 (httpHandle == NULL ||
        relativePath == NULL ||
        httpHeadersHandle == NULL ||
		!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(httpHandle)) != 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(httpHandle, 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(httpHandle, 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(httpHandle, 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(httpHandle, 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(httpHandle, 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_031: [ After receive the response, the HTTPAPI_ExecuteRequest shall close the transport connection with the host. ]*/
	if ((httpHandle != NULL) &&
        (httpHandle->is_connected != 0))
    {
        conn_receive_discard_buffer(httpHandle);
        xio_close(httpHandle->xio_handle, NULL, NULL);
        httpHandle->is_connected = 0;
    }

    return result;
}

/*Codes_SRS_HTTPAPI_COMPACT_21_056: [ The HTTPAPI_SetOption shall change the HTTP options. ]*/
/*Codes_SRS_HTTPAPI_COMPACT_21_057: [ The HTTPAPI_SetOption shall recieve a handle that identiry the HTTP connection. ]*/
/*Codes_SRS_HTTPAPI_COMPACT_21_058: [ The HTTPAPI_SetOption shall recieve the option as a pair optionName/value. ]*/
HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value)
{
    HTTPAPI_RESULT result;
    HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)handle;

	if (
        (httpHandle == 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;
    }
    else if (strcmp("TrustedCerts", optionName) == 0)
    {
        if (httpHandle->certificate)
        {
            free(httpHandle->certificate);
        }

        int len = (int)strlen((char*)value);
        httpHandle->certificate = (char*)malloc((len + 1) * sizeof(char));
        if (httpHandle->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;
            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. ]*/
            (void)strcpy(httpHandle->certificate, (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. ]*/
        result = HTTPAPI_INVALID_ARG;
        LogInfo("unknown option %s", optionName);
    }
    return result;
}

/*Codes_SRS_HTTPAPI_COMPACT_21_065: [ The HTTPAPI_CloneOption shall provide the means to clone the HTTP option. ]*/
/*Codes_SRS_HTTPAPI_COMPACT_21_066: [ The HTTPAPI_CloneOption shall return a clone of the value identified by the optionName. ]*/
HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue)
{
    HTTPAPI_RESULT result;
    size_t certLen;
    char* tempCert;

	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;
    }
    else if (strcmp("TrustedCerts", 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;
        LogInfo("unknown option %s", optionName);
    }
    return result;
}