HMACSHA256_RESULT HMACSHA256_ComputeHash(const unsigned char* key, size_t keyLen, const unsigned char* payload, size_t payloadLen, BUFFER_HANDLE hash)
{
    HMACSHA256_RESULT result;

    if (key == NULL ||
        keyLen == 0 ||
        payload == NULL ||
        payloadLen == 0 ||
        hash == NULL)
    {
        result = HMACSHA256_INVALID_ARG;
    }
    else
    {
        if ((BUFFER_enlarge(hash, 32) != 0) ||
            (hmac(SHA256, payload, payloadLen, key, keyLen, BUFFER_u_char(hash) ) != 0))
        {
            result = HMACSHA256_ERROR;
        }
        else
        {
            result = HMACSHA256_OK;
        }
    }

    return result;
}
//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)
{
    LogInfo("HTTPAPI_ExecuteRequest::Start\r\n");

    HTTPAPI_RESULT result;
    size_t  headersCount;
    char    buf[TEMP_BUFFER_SIZE];
    int     ret;
    TLSConnection* con = NULL;
    size_t  bodyLength = 0;
    bool    chunked = false;
    const unsigned char* receivedContent;

    const char* method = (requestType == HTTPAPI_REQUEST_GET) ? "GET"
        : (requestType == HTTPAPI_REQUEST_POST) ? "POST"
        : (requestType == HTTPAPI_REQUEST_PUT) ? "PUT"
        : (requestType == HTTPAPI_REQUEST_DELETE) ? "DELETE"
        : (requestType == HTTPAPI_REQUEST_PATCH) ? "PATCH"
        : NULL;

    if (handle == NULL ||
        relativePath == NULL ||
        httpHeadersHandle == NULL ||
        method == NULL ||
        HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK)
    {
        result = HTTPAPI_INVALID_ARG;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }

    con = &(((HTTP_HANDLE_DATA*)handle)->con);

    //Send request
    if ((ret = snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\n", method, relativePath)) < 0
        || ret >= sizeof(buf))
    {
        result = HTTPAPI_STRING_PROCESSING_ERROR;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }
    LogInfo("HTTPAPI_ExecuteRequest::Sending=%*.*s\r\n", strlen(buf), strlen(buf), buf);
    if (con->send_all(buf, strlen(buf)) < 0)
    {
        result = HTTPAPI_SEND_REQUEST_FAILED;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }

    //Send default headers
    for (int i = 0; i < (int)headersCount; i++)
    {
        if (HTTPHeaders_GetHeader(httpHeadersHandle, i, buf, sizeof(buf)) != HTTP_HEADERS_OK)
        {
            result = HTTPAPI_HTTP_HEADERS_FAILED;
            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
            goto exit;
        }
        LogInfo("HTTPAPI_ExecuteRequest::Sending=%*.*s\r\n", strlen(buf), strlen(buf), buf);
        if (con->send_all(buf, strlen(buf)) < 0)
        {
            result = HTTPAPI_SEND_REQUEST_FAILED;
            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
            goto exit;
        }
        if (con->send_all("\r\n", 2) < 0)
        {
            result = HTTPAPI_SEND_REQUEST_FAILED;
            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
            goto exit;
        }
    }

    //Close headers
    if (con->send_all("\r\n", 2) < 0)
    {
        result = HTTPAPI_SEND_REQUEST_FAILED;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }

    //Send data (if available)
    if (content && contentLength > 0)
    {
        LogInfo("HTTPAPI_ExecuteRequest::Sending data=%*.*s\r\n", contentLength, contentLength, content);
        if (con->send_all((char*)content, contentLength) < 0)
        {
            result = HTTPAPI_SEND_REQUEST_FAILED;
            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
            goto exit;
        }
    }

    //Receive response
    if (readLine(con, buf, sizeof(buf)) < 0)
    {
        result = HTTPAPI_READ_DATA_FAILED;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }

    //Parse HTTP response
    if (sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) != 1)
    {
        //Cannot match string, error
        LogInfo("HTTPAPI_ExecuteRequest::Not a correct HTTP answer=%s\r\n", buf);
        result = HTTPAPI_READ_DATA_FAILED;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }
    if (statusCode)
        *statusCode = ret;
    LogInfo("HTTPAPI_ExecuteRequest::Received response=%*.*s\r\n", strlen(buf), strlen(buf), buf);

    //Read HTTP response headers
    if (readLine(con, buf, sizeof(buf)) < 0)
    {
        result = HTTPAPI_READ_DATA_FAILED;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        goto exit;
    }

    while (buf[0])
    {
        const char ContentLength[] = "content-length:";
        const char TransferEncoding[] = "transfer-encoding:";

        LogInfo("Receiving header=%*.*s\r\n", strlen(buf), strlen(buf), buf);

        if (strncasecmp(buf, ContentLength, CHAR_COUNT(ContentLength)) == 0)
        {
            if (sscanf(buf + CHAR_COUNT(ContentLength), " %d", &bodyLength) != 1)
            {
                result = HTTPAPI_READ_DATA_FAILED;
                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                goto exit;
            }
        }
        else if (strncasecmp(buf, TransferEncoding, CHAR_COUNT(TransferEncoding)) == 0)
        {
            const char* p = buf + CHAR_COUNT(TransferEncoding);
            while (isspace(*p)) p++;
            if (strcasecmp(p, "chunked") == 0)
                chunked = true;
        }

        char* whereIsColon = strchr((char*)buf, ':');
        if (whereIsColon && responseHeadersHandle != NULL)
        {
            *whereIsColon = '\0';
            HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1);
        }

        if (readLine(con, buf, sizeof(buf)) < 0)
        {
            result = HTTPAPI_READ_DATA_FAILED;
            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
            goto exit;
        }
    }

    //Read HTTP response body
    LogInfo("HTTPAPI_ExecuteRequest::Receiving body=%d,%x\r\n", bodyLength, responseContent);
    if (!chunked)
    {
        if (bodyLength)
        {
            if (responseContent != NULL)
            {
                if (BUFFER_pre_build(responseContent, bodyLength) != 0)
                {
                    result = HTTPAPI_ALLOC_FAILED;
                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                }
                else if (BUFFER_content(responseContent, &receivedContent) != 0)
                {
                    (void)BUFFER_unbuild(responseContent);

                    result = HTTPAPI_ALLOC_FAILED;
                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                }

                if (readChunk(con, (char*)receivedContent, bodyLength) < 0)
                {
                    result = HTTPAPI_READ_DATA_FAILED;
                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    goto exit;
                }
                else
                {
                    LogInfo("HTTPAPI_ExecuteRequest::Received response body=%*.*s\r\n", bodyLength, bodyLength, receivedContent);
                    result = HTTPAPI_OK;
                }
            }
            else
            {
                (void)skipN(con, bodyLength, buf, sizeof(buf));
                result = HTTPAPI_OK;
            }
        }
        else
        {
            result = HTTPAPI_OK;
        }
    }
    else
    {
        size_t size = 0;
        result = HTTPAPI_OK;
        for (;;)
        {
            int chunkSize;
            if (readLine(con, buf, sizeof(buf)) < 0)    // read [length in hex]/r/n
            {
                result = HTTPAPI_READ_DATA_FAILED;
                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                goto exit;
            }
            if (sscanf(buf, "%x", &chunkSize) != 1)     // chunkSize is length of next line (/r/n is not counted)
            {
                //Cannot match string, error
                result = HTTPAPI_RECEIVE_RESPONSE_FAILED;
                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                goto exit;
            }

            if (chunkSize == 0)
            {
                // 0 length means next line is just '\r\n' and end of chunks
                if (readChunk(con, (char*)buf, 2) < 0
                    || buf[0] != '\r' || buf[1] != '\n') // skip /r/n
                {
                    (void)BUFFER_unbuild(responseContent);

                    result = HTTPAPI_READ_DATA_FAILED;
                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    goto exit;
                }
                break;
            }
            else
            {
                if (responseContent != NULL)
                {
                    if (BUFFER_enlarge(responseContent, chunkSize) != 0)
                    {
                        (void)BUFFER_unbuild(responseContent);

                        result = HTTPAPI_ALLOC_FAILED;
                        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    }
                    else if (BUFFER_content(responseContent, &receivedContent) != 0)
                    {
                        (void)BUFFER_unbuild(responseContent);

                        result = HTTPAPI_ALLOC_FAILED;
                        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    }

                    if (readChunk(con, (char*)receivedContent + size, chunkSize) < 0)
                    {
                        result = HTTPAPI_READ_DATA_FAILED;
                        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                        goto exit;
                    }
                }
                else
                {
                    if (skipN(con, chunkSize, buf, sizeof(buf)) < 0)
                    {
                        result = HTTPAPI_READ_DATA_FAILED;
                        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                        goto exit;
                    }
                }

                if (readChunk(con, (char*)buf, 2) < 0
                    || buf[0] != '\r' || buf[1] != '\n') // skip /r/n
                {
                    result = HTTPAPI_READ_DATA_FAILED;
                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    goto exit;
                }
                size += chunkSize;
            }
        }

        if (size > 0)
        {
            LogInfo("HTTPAPI_ExecuteRequest::Received chunk body=%*.*s\r\n", size, size, responseContent);
        }
    }

exit:
    LogInfo("HTTPAPI_ExecuteRequest::End=%d\r\n", result);
    return result;
}
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;
    HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;

    if ((handleData == NULL) ||
        (relativePath == NULL) ||
        (httpHeadersHandle == NULL))
    {
        result = HTTPAPI_INVALID_ARG;
        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
    }
    else
    {
        wchar_t* requestTypeString = NULL;

        switch (requestType)
        {
        default:
            break;

        case HTTPAPI_REQUEST_GET:
            requestTypeString = L"GET";
            break;

        case HTTPAPI_REQUEST_POST:
            requestTypeString = L"POST";
            break;

        case HTTPAPI_REQUEST_PUT:
            requestTypeString = L"PUT";
            break;

        case HTTPAPI_REQUEST_DELETE:
            requestTypeString = L"DELETE";
            break;

        case HTTPAPI_REQUEST_PATCH:
            requestTypeString = L"PATCH";
            break;
        }

        if (requestTypeString == NULL)
        {
            result = HTTPAPI_INVALID_ARG;
            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        }
        else
        {
            wchar_t relativePathTemp[1024];
            char headers[1024];
            wchar_t headersTemp[1024];

            result = ConstructHeadersString(httpHeadersHandle, headers, sizeof(headers));
            if (result == HTTPAPI_OK)
            {
                if (MultiByteToWideChar(CP_ACP, 0, relativePath, -1, relativePathTemp, sizeof(relativePathTemp) / sizeof(relativePathTemp[0])) == 0)
                {
                    result = HTTPAPI_STRING_PROCESSING_ERROR;
                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                }
                else
                {
                    if (MultiByteToWideChar(CP_ACP, 0, headers, -1, headersTemp, sizeof(headersTemp) / sizeof(headersTemp[0])) == 0)
                    {
                        result = HTTPAPI_STRING_PROCESSING_ERROR;
                        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    }
                    else
                    {
                        PCWSTR rgpszAcceptTypes[] = { L"text/*", NULL };
                        HINTERNET requestHandle = HttpOpenRequestW(
                            handleData->ConnectionHandle,
                            requestTypeString,
                            relativePathTemp,
                            NULL,
                            NULL,
                            rgpszAcceptTypes,
                            INTERNET_FLAG_SECURE,
                            0);
                        if (requestHandle == NULL)
                        {
                            result = HTTPAPI_OPEN_REQUEST_FAILED;
                            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                        }
                        else
                        {
                            unsigned long int timeout = 55000;
                            if (!InternetSetOption(
                                requestHandle,
                                INTERNET_OPTION_RECEIVE_TIMEOUT, /*Sets or retrieves an unsigned long integer value that contains the time-out value, in milliseconds, to receive a response to a request.*/
                                &timeout,
                                sizeof(timeout)))
                            {
                                result = HTTPAPI_SET_TIMEOUTS_FAILED;
                                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                            }
                            else
                            {
                                DWORD dwSecurityFlags = 0;
                                if (!InternetSetOption(
                                    requestHandle,
                                    INTERNET_OPTION_SECURITY_FLAGS,
                                    &dwSecurityFlags,
                                    sizeof(dwSecurityFlags)))
                                {
                                    result = HTTPAPI_SET_OPTION_FAILED;
                                    LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                }
                                else
                                {
                                    if (!HttpSendRequestW(
                                        requestHandle,
                                        headersTemp,
                                        -1,
                                        (void*)content,
                                        contentLength))
                                    {
                                        result = HTTPAPI_SEND_REQUEST_FAILED;
LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                    }
                                    else
                                    {
                                        DWORD dwStatusCode = 0;
                                        DWORD dwBufferLength = sizeof(DWORD);
                                        DWORD responseBytesAvailable;

                                        if (responseHeadersHandle != NULL)
                                        {
                                            wchar_t responseHeadersTemp[16384];
                                            DWORD responseHeadersTempLength = sizeof(responseHeadersTemp);

                                            if (HttpQueryInfo(
                                                requestHandle,
                                                HTTP_QUERY_RAW_HEADERS_CRLF,
                                                responseHeadersTemp,
                                                &responseHeadersTempLength,
                                                0))
                                            {
                                                wchar_t *next_token;
                                                wchar_t* token = wcstok_s(responseHeadersTemp, L"\r\n", &next_token);
                                                while ((token != NULL) &&
                                                    (token[0] != L'\0'))
                                                {
                                                    char tokenTemp[1024];

                                                    if (WideCharToMultiByte(CP_ACP, 0, token, -1, tokenTemp, sizeof(tokenTemp), NULL, NULL) > 0)
                                                    {
                                                        /*breaking the token in 2 parts: everything before the first ":" and everything after the first ":"*/
                                                        /* if there is no such character, then skip it*/
                                                        /*if there is a : then replace is by a '\0' and so it breaks the original string in name and value*/

                                                        char* whereIsColon = strchr(tokenTemp, ':');
                                                        if (whereIsColon != NULL)
                                                        {
                                                            *whereIsColon = '\0';
                                                            HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, tokenTemp, whereIsColon + 1);
                                                        }
                                                    }
                                                    token = wcstok_s(NULL, L"\r\n", &next_token);
                                                }
                                            }
                                        }

                                        if (!HttpQueryInfo(
                                            requestHandle,
                                            HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
                                            &dwStatusCode,
                                            &dwBufferLength,
                                            0))
                                        {
                                            result = HTTPAPI_QUERY_HEADERS_FAILED;
                                            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                        }
                                        else
                                        {
                                            BUFFER_HANDLE useToReadAllResponse = (responseContent != NULL) ? responseContent : BUFFER_new();
                                            /*HTTP status code (dwStatusCode) can be either ok (<HTTP_STATUS_AMBIGUOUS) or "not ok (>=HTTP_STATUS_AMBIGUOUS)*/
                                            if (useToReadAllResponse == NULL)
                                            {
                                                result = HTTPAPI_ERROR;
                                                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                            }
                                            else
                                            {
                                                int goOnAndReadEverything = 1;
                                                /*set the response code*/
                                                if (statusCode != NULL)
                                                {
                                                    *statusCode = dwStatusCode;
                                                }

                                                do
                                                {
                                                    /*from MSDN:  If there is currently no data available and the end of the file has not been reached, the request waits until data becomes available*/
                                                    if (!InternetQueryDataAvailable(requestHandle, &responseBytesAvailable, 0, 0))
                                                    {
                                                        result = HTTPAPI_QUERY_DATA_AVAILABLE_FAILED;
                                                        LogError("InternetQueryDataAvailable failed (result = %s) GetLastError = %d\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result), GetLastError());
                                                        goOnAndReadEverything = 0;
                                                    }
                                                    else if (responseBytesAvailable == 0)
                                                    {
                                                        /*end of the stream, go out*/
                                                        if (dwStatusCode >= HTTP_STATUS_AMBIGUOUS)
                                                        {
                                                            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                        }

                                                        result = HTTPAPI_OK;
                                                        goOnAndReadEverything = 0;
                                                    }
                                                    else
                                                    {
                                                        /*add the needed space to the buffer*/
                                                        if (BUFFER_enlarge(useToReadAllResponse, responseBytesAvailable) != 0)
                                                        {
                                                            result = HTTPAPI_ERROR;
                                                            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                            goOnAndReadEverything = 0;
                                                        }
                                                        else
                                                        {
                                                            unsigned char* bufferContent;
                                                            size_t bufferSize;
                                                            /*add the data to the buffer*/
                                                            if (BUFFER_content(useToReadAllResponse, &bufferContent) != 0)
                                                            {
                                                                result = HTTPAPI_ERROR;
                                                                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                goOnAndReadEverything = 0;
                                                            }
                                                            else if (BUFFER_size(useToReadAllResponse, &bufferSize) != 0)
                                                            {
                                                                result = HTTPAPI_ERROR;
                                                                LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                goOnAndReadEverything = 0;
                                                            }
                                                            else
                                                            {
                                                                DWORD bytesReceived;
                                                                if (!InternetReadFile(requestHandle, bufferContent + bufferSize - responseBytesAvailable, responseBytesAvailable, &bytesReceived))
                                                                {
                                                                    result = HTTPAPI_READ_DATA_FAILED;
                                                                    LogError("InternetReadFile failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                    goOnAndReadEverything = 0;
                                                                }
                                                                else
                                                                {
                                                                    /*if for some reason bytesReceived is zero, MSDN says To ensure all data is retrieved, an application must continue to call the InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero*/
                                                                    if (bytesReceived == 0)
                                                                    {
                                                                        /*end of everything, but this looks like an error still, or a non-conformance between InternetQueryDataAvailable and InternetReadFile*/
                                                                        result = HTTPAPI_READ_DATA_FAILED;
                                                                        LogError("InternetReadFile failed (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                        goOnAndReadEverything = 0;
                                                                    }
                                                                    else
                                                                    {
                                                                        /*all is fine, keep going*/
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                } while (goOnAndReadEverything != 0);

                                                if (responseContent == NULL)
                                                {
                                                    BUFFER_delete(useToReadAllResponse);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            (void)InternetCloseHandle(requestHandle);
                        }
                    }
                }
            }
        }
    }

    return result;
}
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;
    if (g_HTTPAPIState != HTTPAPI_INITIALIZED)
    {
        LogError("g_HTTPAPIState not HTTPAPI_INITIALIZED");
        result = HTTPAPI_NOT_INIT;
    }
    else
    {
        HTTP_HANDLE_DATA* handleData = (HTTP_HANDLE_DATA*)handle;

        if ((handleData == NULL) ||
            (relativePath == NULL) ||
            (httpHeadersHandle == NULL))
        {
            result = HTTPAPI_INVALID_ARG;
            LogError("NULL parameter detected (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
        }
        else
        {
            wchar_t* requestTypeString = NULL;

            switch (requestType)
            {
            default:
                break;

            case HTTPAPI_REQUEST_GET:
                requestTypeString = L"GET";
                break;

            case HTTPAPI_REQUEST_POST:
                requestTypeString = L"POST";
                break;

            case HTTPAPI_REQUEST_PUT:
                requestTypeString = L"PUT";
                break;

            case HTTPAPI_REQUEST_DELETE:
                requestTypeString = L"DELETE";
                break;

            case HTTPAPI_REQUEST_PATCH:
                requestTypeString = L"PATCH";
                break;
            }

            if (requestTypeString == NULL)
            {
                result = HTTPAPI_INVALID_ARG;
                LogError("requestTypeString was NULL (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
            }
            else
            {
                const char* headers2;
                headers2 = ConstructHeadersString(httpHeadersHandle);
                if (headers2 != NULL)
                {
                    size_t requiredCharactersForRelativePath = MultiByteToWideChar(CP_ACP, 0, relativePath, -1, NULL, 0);
                    wchar_t* relativePathTemp = (wchar_t*)malloc((requiredCharactersForRelativePath+1) * sizeof(wchar_t));
                    result = HTTPAPI_OK; /*legacy code*/

                    if (relativePathTemp == NULL)
                    {
                        result = HTTPAPI_ALLOC_FAILED;
                        LogError("malloc failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                    }
                    else
                    {
                        if (MultiByteToWideChar(CP_ACP, 0, relativePath, -1, relativePathTemp, (int)requiredCharactersForRelativePath) == 0)
                        {
                            result = HTTPAPI_STRING_PROCESSING_ERROR;
                            LogError("MultiByteToWideChar was 0. (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                        }
                        else
                        {
                            size_t requiredCharactersForHeaders = MultiByteToWideChar(CP_ACP, 0, headers2, -1, NULL, 0);

                            wchar_t* headersTemp = (wchar_t*)malloc((requiredCharactersForHeaders +1) * sizeof(wchar_t) );
                            if (headersTemp == NULL)
                            {
                                result = HTTPAPI_STRING_PROCESSING_ERROR;
                                LogError("MultiByteToWideChar was 0. (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                            }
                            else
                            {
                                if (MultiByteToWideChar(CP_ACP, 0, headers2, -1, headersTemp, (int)requiredCharactersForHeaders) == 0)
                                {
                                    result = HTTPAPI_STRING_PROCESSING_ERROR;
                                    LogError("MultiByteToWideChar was 0(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                }
                                else
                                {
                                    HINTERNET requestHandle = WinHttpOpenRequest(
                                        handleData->ConnectionHandle,
                                        requestTypeString,
                                        relativePathTemp,
                                        NULL,
                                        WINHTTP_NO_REFERER,
                                        WINHTTP_DEFAULT_ACCEPT_TYPES,
                                        WINHTTP_FLAG_SECURE);
                                    if (requestHandle == NULL)
                                    {
                                        result = HTTPAPI_OPEN_REQUEST_FAILED;
                                        LogErrorWinHTTPWithGetLastErrorAsString("WinHttpOpenRequest failed (result = %s).", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                    }
                                    else
                                    {
                                        if ((handleData->x509SchannelHandle!=NULL) && 
                                            !WinHttpSetOption(
                                                requestHandle,
                                                WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                                                (void*)x509_schannel_get_certificate_context(handleData->x509SchannelHandle),
                                                sizeof(CERT_CONTEXT)
                                        ))
                                        {
                                            LogErrorWinHTTPWithGetLastErrorAsString("unable to WinHttpSetOption");
                                            result = HTTPAPI_SET_X509_FAILURE;
                                        }
                                        else
                                        {
                                            if (WinHttpSetTimeouts(requestHandle,
                                                0,                      /*_In_  int dwResolveTimeout - The initial value is zero, meaning no time-out (infinite). */
                                                60000,                  /*_In_  int dwConnectTimeout, -  The initial value is 60,000 (60 seconds).*/
                                                handleData->timeout,    /*_In_  int dwSendTimeout, -  The initial value is 30,000 (30 seconds).*/
                                                handleData->timeout     /* int dwReceiveTimeout The initial value is 30,000 (30 seconds).*/
                                            ) == FALSE)
                                            {
                                                result = HTTPAPI_SET_TIMEOUTS_FAILED;
                                                LogErrorWinHTTPWithGetLastErrorAsString("WinHttpOpenRequest failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                            }
                                            else
                                            {
                                                DWORD dwSecurityFlags = 0;

                                                if (!WinHttpSetOption(
                                                    requestHandle,
                                                    WINHTTP_OPTION_SECURITY_FLAGS,
                                                    &dwSecurityFlags,
                                                    sizeof(dwSecurityFlags)))
                                                {
                                                    result = HTTPAPI_SET_OPTION_FAILED;
                                                    LogErrorWinHTTPWithGetLastErrorAsString("WinHttpSetOption failed (result = %s).", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                }
                                                else
                                                {
                                                    if (!WinHttpSendRequest(
                                                        requestHandle,
                                                        headersTemp,
                                                        (DWORD)-1L, /*An unsigned long integer value that contains the length, in characters, of the additional headers. If this parameter is -1L ... */
                                                        (void*)content,
                                                        (DWORD)contentLength,
                                                        (DWORD)contentLength,
                                                        0))
                                                    {
                                                        result = HTTPAPI_SEND_REQUEST_FAILED;
                                                        LogErrorWinHTTPWithGetLastErrorAsString("WinHttpSendRequest: (result = %s).", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                    }
                                                    else
                                                    {
                                                        if (!WinHttpReceiveResponse(
                                                            requestHandle,
                                                            0))
                                                        {
                                                            result = HTTPAPI_RECEIVE_RESPONSE_FAILED;
                                                            LogErrorWinHTTPWithGetLastErrorAsString("WinHttpReceiveResponse: (result = %s).", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                        }
                                                        else
                                                        {
                                                            DWORD dwStatusCode = 0;
                                                            DWORD dwBufferLength = sizeof(DWORD);
                                                            DWORD responseBytesAvailable;

                                                            if (!WinHttpQueryHeaders(
                                                                requestHandle,
                                                                WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
                                                                WINHTTP_HEADER_NAME_BY_INDEX,
                                                                &dwStatusCode,
                                                                &dwBufferLength,
                                                                WINHTTP_NO_HEADER_INDEX))
                                                            {
                                                                result = HTTPAPI_QUERY_HEADERS_FAILED;
                                                                LogErrorWinHTTPWithGetLastErrorAsString("WinHttpQueryHeaders failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                            }
                                                            else
                                                            {
                                                                BUFFER_HANDLE useToReadAllResponse = (responseContent != NULL) ? responseContent : BUFFER_new();

                                                                if (statusCode != NULL)
                                                                {
                                                                    *statusCode = dwStatusCode;
                                                                }

                                                                if (useToReadAllResponse == NULL)
                                                                {
                                                                    result = HTTPAPI_ERROR;
                                                                    LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                }
                                                                else
                                                                {

                                                                    int goOnAndReadEverything = 1;
                                                                    do
                                                                    {
                                                                        /*from MSDN: If no data is available and the end of the file has not been reached, one of two things happens. If the session is synchronous, the request waits until data becomes available.*/
                                                                        if (!WinHttpQueryDataAvailable(requestHandle, &responseBytesAvailable))
                                                                        {
                                                                            result = HTTPAPI_QUERY_DATA_AVAILABLE_FAILED;
                                                                            LogErrorWinHTTPWithGetLastErrorAsString("WinHttpQueryDataAvailable failed (result = %s).", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                            goOnAndReadEverything = 0;
                                                                        }
                                                                        else if (responseBytesAvailable == 0)
                                                                        {
                                                                            /*end of the stream, go out*/
                                                                            result = HTTPAPI_OK;
                                                                            goOnAndReadEverything = 0;
                                                                        }
                                                                        else
                                                                        {
                                                                            if (BUFFER_enlarge(useToReadAllResponse, responseBytesAvailable) != 0)
                                                                            {
                                                                                result = HTTPAPI_ERROR;
                                                                                LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                                goOnAndReadEverything = 0;
                                                                            }
                                                                            else
                                                                            {
                                                                                /*Add the read bytes to the response buffer*/
                                                                                size_t bufferSize;
                                                                                const unsigned char* bufferContent;

                                                                                if (BUFFER_content(useToReadAllResponse, &bufferContent) != 0)
                                                                                {
                                                                                    result = HTTPAPI_ERROR;
                                                                                    LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                                    goOnAndReadEverything = 0;
                                                                                }
                                                                                else if (BUFFER_size(useToReadAllResponse, &bufferSize) != 0)
                                                                                {
                                                                                    result = HTTPAPI_ERROR;
                                                                                    LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                                    goOnAndReadEverything = 0;
                                                                                }
                                                                                else
                                                                                {
                                                                                    DWORD bytesReceived;
                                                                                    if (!WinHttpReadData(requestHandle, (LPVOID)(bufferContent + bufferSize - responseBytesAvailable), responseBytesAvailable, &bytesReceived))
                                                                                    {
                                                                                        result = HTTPAPI_READ_DATA_FAILED;
                                                                                        LogErrorWinHTTPWithGetLastErrorAsString("WinHttpReadData failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                                        goOnAndReadEverything = 0;
                                                                                    }
                                                                                    else
                                                                                    {
                                                                                        /*if for some reason bytesReceived is zero If you are using WinHttpReadData synchronously, and the return value is TRUE and the number of bytes read is zero, the transfer has been completed and there are no more bytes to read on the handle.*/
                                                                                        if (bytesReceived == 0)
                                                                                        {
                                                                                            /*end of everything, but this looks like an error still, or a non-conformance between WinHttpQueryDataAvailable and WinHttpReadData*/
                                                                                            result = HTTPAPI_READ_DATA_FAILED;
                                                                                            LogError("bytesReceived was unexpectedly zero (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                                            goOnAndReadEverything = 0;
                                                                                        }
                                                                                        else
                                                                                        {
                                                                                            /*all is fine, keep going*/
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }
                                                                        }

                                                                    } while (goOnAndReadEverything != 0);
                                                                }
                                                            }

                                                            if (result == HTTPAPI_OK && responseHeadersHandle != NULL)
                                                            {
                                                                wchar_t* responseHeadersTemp;
                                                                DWORD responseHeadersTempLength = sizeof(responseHeadersTemp);

                                                                (void)WinHttpQueryHeaders(
                                                                    requestHandle,
                                                                    WINHTTP_QUERY_RAW_HEADERS_CRLF,
                                                                    WINHTTP_HEADER_NAME_BY_INDEX,
                                                                    WINHTTP_NO_OUTPUT_BUFFER,
                                                                    &responseHeadersTempLength,
                                                                    WINHTTP_NO_HEADER_INDEX);

                                                                responseHeadersTemp = (wchar_t*)malloc(responseHeadersTempLength + 2);
                                                                if (responseHeadersTemp == NULL)
                                                                {
                                                                    result = HTTPAPI_ALLOC_FAILED;
                                                                    LogError("malloc failed: (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
                                                                }
                                                                else
                                                                {
                                                                    if (WinHttpQueryHeaders(
                                                                        requestHandle,
                                                                        WINHTTP_QUERY_RAW_HEADERS_CRLF,
                                                                        WINHTTP_HEADER_NAME_BY_INDEX,
                                                                        responseHeadersTemp,
                                                                        &responseHeadersTempLength,
                                                                        WINHTTP_NO_HEADER_INDEX))
                                                                    {
                                                                        wchar_t *next_token;
                                                                        wchar_t* token = wcstok_s(responseHeadersTemp, L"\r\n", &next_token);
                                                                        while ((token != NULL) &&
                                                                            (token[0] != L'\0'))
                                                                        {
                                                                            char* tokenTemp;
                                                                            size_t tokenTemp_size;

                                                                            tokenTemp_size = WideCharToMultiByte(CP_ACP, 0, token, -1, NULL, 0, NULL, NULL);
                                                                            if (tokenTemp_size == 0)
                                                                            {
                                                                                LogError("WideCharToMultiByte failed");
                                                                            }
                                                                            else
                                                                            {
                                                                                tokenTemp = (char*)malloc(sizeof(char)*tokenTemp_size);
                                                                                if (tokenTemp == NULL)
                                                                                {
                                                                                    LogError("malloc failed");
                                                                                }
                                                                                else
                                                                                {
                                                                                    if (WideCharToMultiByte(CP_ACP, 0, token, -1, tokenTemp, (int)tokenTemp_size, NULL, NULL) > 0)
                                                                                    {
                                                                                        /*breaking the token in 2 parts: everything before the first ":" and everything after the first ":"*/
                                                                                        /* if there is no such character, then skip it*/
                                                                                        /*if there is a : then replace is by a '\0' and so it breaks the original string in name and value*/

                                                                                        char* whereIsColon = strchr(tokenTemp, ':');
                                                                                        if (whereIsColon != NULL)
                                                                                        {
                                                                                            *whereIsColon = '\0';
                                                                                            if (HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, tokenTemp, whereIsColon + 1) != HTTP_HEADERS_OK)
                                                                                            {
                                                                                                LogError("HTTPHeaders_AddHeaderNameValuePair failed");
                                                                                                result = HTTPAPI_HTTP_HEADERS_FAILED;
                                                                                                break;
                                                                                            }
                                                                                        }
                                                                                    }
                                                                                    else
                                                                                    {
                                                                                        LogError("WideCharToMultiByte failed");
                                                                                    }
                                                                                    free(tokenTemp);
                                                                                }
                                                                            }


                                                                            token = wcstok_s(NULL, L"\r\n", &next_token);
                                                                        }
                                                                    }
                                                                    else
                                                                    {
                                                                        LogError("WinHttpQueryHeaders failed");
                                                                    }

                                                                    free(responseHeadersTemp);
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                        (void)WinHttpCloseHandle(requestHandle);
                                    }
                                }
                                free(headersTemp);
                            }
                        }
                        free(relativePathTemp);
                    }
                    free((void*)headers2);
                }
                else
                {
                    result = HTTPAPI_ALLOC_FAILED; /*likely*/
                    LogError("ConstructHeadersString failed");
                }
            }
        }
    }

    return result;
}