Azure IoT common library

Fork of azure_c_shared_utility by Azure IoT

Revision:
0:fa2de1b79154
Child:
1:9190c0f4d23a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/httpheaders.c	Fri Apr 08 12:01:36 2016 -0700
@@ -0,0 +1,335 @@
+// 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 "azure_c_shared_utility/gballoc.h"
+
+#include "azure_c_shared_utility/map.h"
+#include "azure_c_shared_utility/httpheaders.h"
+#include <string.h>
+#include "azure_c_shared_utility/crt_abstractions.h"
+#include "azure_c_shared_utility/iot_logging.h"
+
+static const char COLON_AND_SPACE[] = { ':', ' ', '\0' };
+#define COLON_AND_SPACE_LENGTH  ((sizeof(COLON_AND_SPACE)/sizeof(COLON_AND_SPACE[0]))-1)
+
+static const char COMMA_AND_SPACE[] = { ',', ' ', '\0' };
+#define COMMA_AND_SPACE_LENGTH  ((sizeof(COMMA_AND_SPACE)/sizeof(COMMA_AND_SPACE[0]))-1)
+
+DEFINE_ENUM_STRINGS(HTTP_HEADERS_RESULT, HTTP_HEADERS_RESULT_VALUES);
+
+typedef struct HTTP_HEADERS_HANDLE_DATA_TAG
+{
+    MAP_HANDLE headers;
+} HTTP_HEADERS_HANDLE_DATA;
+
+HTTP_HEADERS_HANDLE HTTPHeaders_Alloc(void)
+{
+    /*Codes_SRS_HTTP_HEADERS_99_002:[ This API shall produce a HTTP_HANDLE that can later be used in subsequent calls to the module.]*/
+    HTTP_HEADERS_HANDLE_DATA* result;
+    result = (HTTP_HEADERS_HANDLE_DATA*)malloc(sizeof(HTTP_HEADERS_HANDLE_DATA));
+
+    if (result == NULL)
+    {
+        LogError("malloc failed\r\n");
+    }
+    else
+    {
+        /*Codes_SRS_HTTP_HEADERS_99_004:[ After a successful init, HTTPHeaders_GetHeaderCount shall report 0 existing headers.]*/
+        result->headers = Map_Create(NULL);
+        if (result->headers == NULL)
+        {
+            LogError("Map_Create failed\r\n");
+            free(result);
+            result = NULL;
+        }
+        else
+        {
+            /*all is fine*/
+        }
+    }
+
+    /*Codes_SRS_HTTP_HEADERS_99_003:[ The function shall return NULL when the function cannot execute properly]*/
+    return (HTTP_HEADERS_HANDLE)result;
+}
+
+/*Codes_SRS_HTTP_HEADERS_99_005:[ Calling this API shall de-allocate the data structures allocated by previous API calls to the same handle.]*/
+void HTTPHeaders_Free(HTTP_HEADERS_HANDLE handle)
+{
+    /*Codes_SRS_HTTP_HEADERS_02_001: [If httpHeadersHandle is NULL then HTTPHeaders_Free shall perform no action.] */
+    if (handle == NULL)
+    {
+        /*do nothing*/
+    }
+    else
+    {
+        /*Codes_SRS_HTTP_HEADERS_99_005:[ Calling this API shall de-allocate the data structures allocated by previous API calls to the same handle.]*/
+        HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle;
+
+        Map_Destroy(handleData->headers);
+        free(handleData);
+    }
+}
+
+/*Codes_SRS_HTTP_HEADERS_99_012:[ Calling this API shall record a header from name and value parameters.]*/
+static HTTP_HEADERS_RESULT headers_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE handle, const char* name, const char* value, bool replace)
+{
+    HTTP_HEADERS_RESULT result;
+    /*Codes_SRS_HTTP_HEADERS_99_014:[ The function shall return when the handle is not valid or when name parameter is NULL or when value parameter is NULL.]*/
+    if (
+        (handle == NULL) ||
+        (name == NULL) ||
+        (value == NULL)
+        )
+    {
+        result = HTTP_HEADERS_INVALID_ARG;
+        LogError("invalid arg (NULL) , result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+    }
+    else
+    {
+        /*Codes_SRS_HTTP_HEADERS_99_036:[ If name contains the characters outside character codes 33 to 126 then the return value shall be HTTP_HEADERS_INVALID_ARG]*/
+        /*Codes_SRS_HTTP_HEADERS_99_031:[ If name contains the character ":" then the return value shall be HTTP_HEADERS_INVALID_ARG.]*/
+        size_t i;
+        size_t nameLen = strlen(name);
+        for (i = 0; i < nameLen; i++)
+        {
+            if ((name[i] < 33) || (126 < name[i]) || (name[i] == ':'))
+            {
+                break;
+            }
+        }
+
+        if (i < nameLen)
+        {
+            result = HTTP_HEADERS_INVALID_ARG;
+            LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+        }
+        else
+        {
+            HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle;
+            const char* existingValue = Map_GetValueFromKey(handleData->headers, name);
+            /*eat up the whitespaces from value, as per RFC 2616, chapter 4.2 "The field value MAY be preceded by any amount of LWS, though a single SP is preferred."*/
+            /*Codes_SRS_HTTP_HEADERS_02_002: [The LWS from the beginning of the value shall not be stored.] */
+            while ((value[0] == ' ') || (value[0] == '\t') || (value[0] == '\r') || (value[0] == '\n'))
+            {
+                value++;
+            }
+
+            if (!replace && (existingValue != NULL))
+            {
+                char* newValue = (char*)malloc(strlen(existingValue) + COMMA_AND_SPACE_LENGTH + strlen(value) + 1);
+                if (newValue == NULL)
+                {
+                    /*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/
+                    result = HTTP_HEADERS_ALLOC_FAILED;
+                    LogError("failed to malloc , result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+                }
+                else
+                {
+                    /*Codes_SRS_HTTP_HEADERS_99_017:[ If the name already exists in the collection of headers, the function shall concatenate the new value after the existing value, separated by a comma and a space as in: old-value+", "+new-value.]*/
+                    (void)strcpy(newValue, existingValue);
+                    (void)strcat(newValue, COMMA_AND_SPACE);
+                    (void)strcat(newValue, value);
+
+                    /*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/
+                    if (Map_AddOrUpdate(handleData->headers, name, newValue) != MAP_OK)
+                    {
+                        /*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/
+                        result = HTTP_HEADERS_ERROR;
+                        LogError("failed to Map_AddOrUpdate, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+                    }
+                    else
+                    {
+                        /*Codes_SRS_HTTP_HEADERS_99_013:[ The function shall return HTTP_HEADERS_OK when execution is successful.]*/
+                        result = HTTP_HEADERS_OK;
+                    }
+                    free(newValue);
+                }
+            }
+            else
+            {
+                /*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/
+                if (Map_AddOrUpdate(handleData->headers, name, value) != MAP_OK)
+                {
+                    /*Codes_SRS_HTTP_HEADERS_99_015:[ The function shall return HTTP_HEADERS_ALLOC_FAILED when an internal request to allocate memory fails.]*/
+                    result = HTTP_HEADERS_ALLOC_FAILED;
+                    LogError("failed to Map_AddOrUpdate, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+                }
+                else
+                {
+                    result = HTTP_HEADERS_OK;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+HTTP_HEADERS_RESULT HTTPHeaders_AddHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value)
+{
+    return headers_ReplaceHeaderNameValuePair(httpHeadersHandle, name, value, false);
+}
+
+/* Codes_SRS_HTTP_HEADERS_06_001: [This API will perform exactly as HTTPHeaders_AddHeaderNameValuePair except that if the header name already exists the already existing value will be replaced as opposed to concatenated to.] */
+HTTP_HEADERS_RESULT HTTPHeaders_ReplaceHeaderNameValuePair(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name, const char* value)
+{
+    return headers_ReplaceHeaderNameValuePair(httpHeadersHandle, name, value, true);
+}
+
+
+const char* HTTPHeaders_FindHeaderValue(HTTP_HEADERS_HANDLE httpHeadersHandle, const char* name)
+{
+    const char* result;
+    /*Codes_SRS_HTTP_HEADERS_99_022:[ The return value shall be NULL if name parameter is NULL or if httpHeadersHandle is NULL]*/
+    if (
+        (httpHeadersHandle == NULL) ||
+        (name == NULL)
+        )
+    {
+        result = NULL;
+    }
+    else
+    {
+        /*Codes_SRS_HTTP_HEADERS_99_018:[ Calling this API shall retrieve the value for a previously stored name.]*/
+        /*Codes_SRS_HTTP_HEADERS_99_020:[ The return value shall be different than NULL when the name matches the name of a previously stored name:value pair.] */
+        /*Codes_SRS_HTTP_HEADERS_99_021:[ In this case the return value shall point to a string that shall strcmp equal to the original stored string.]*/
+        HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)httpHeadersHandle;
+        result = Map_GetValueFromKey(handleData->headers, name);
+    }
+    return result;
+
+}
+
+HTTP_HEADERS_RESULT HTTPHeaders_GetHeaderCount(HTTP_HEADERS_HANDLE handle, size_t* headerCount)
+{
+    HTTP_HEADERS_RESULT result;
+    /*Codes_SRS_HTTP_HEADERS_99_024:[ The function shall return HTTP_HEADERS_INVALID_ARG when an invalid handle is passed.]*/
+    /*Codes_SRS_HTTP_HEADERS_99_025:[ The function shall return HTTP_HEADERS_INVALID_ARG when headersCount is NULL.]*/
+    if ((handle == NULL) ||
+        (headerCount == NULL))
+    {
+        result = HTTP_HEADERS_INVALID_ARG;
+        LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+    }
+    else
+    {
+        HTTP_HEADERS_HANDLE_DATA *handleData = (HTTP_HEADERS_HANDLE_DATA *)handle;
+        const char*const* keys;
+        const char*const* values;
+        /*Codes_SRS_HTTP_HEADERS_99_023:[ Calling this API shall provide the number of stored headers.]*/
+        if (Map_GetInternals(handleData->headers, &keys, &values, headerCount) != MAP_OK)
+        {
+            /*Codes_SRS_HTTP_HEADERS_99_037:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs.]*/
+            result = HTTP_HEADERS_ERROR;
+            LogError("Map_GetInternals failed, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+        }
+        else
+        {
+            /*Codes_SRS_HTTP_HEADERS_99_026:[ The function shall write in *headersCount the number of currently stored headers and shall return HTTP_HEADERS_OK]*/
+            result = HTTP_HEADERS_OK;
+        }
+    }
+
+    return result;
+}
+
+/*produces a string in *destination that is equal to name: value*/
+HTTP_HEADERS_RESULT HTTPHeaders_GetHeader(HTTP_HEADERS_HANDLE handle, size_t index, char** destination)
+{
+    HTTP_HEADERS_RESULT result = HTTP_HEADERS_OK;
+
+    /*Codes_SRS_HTTP_HEADERS_99_028:[ The function shall return NULL if the handle is invalid.]*/
+    /*Codes_SRS_HTTP_HEADERS_99_032:[ The function shall return HTTP_HEADERS_INVALID_ARG if the destination  is NULL]*/
+    if (
+        (handle == NULL) ||
+        (destination == NULL)
+        )
+    {
+        result = HTTP_HEADERS_INVALID_ARG;
+        LogError("invalid arg (NULL), result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+    }
+    /*Codes_SRS_HTTP_HEADERS_99_029:[ The function shall return HTTP_HEADERS_INVALID_ARG if index is not valid (for example, out of range) for the currently stored headers.]*/
+    else
+    {
+        HTTP_HEADERS_HANDLE_DATA* handleData = (HTTP_HEADERS_HANDLE_DATA*)handle;
+        const char*const* keys;
+        const char*const* values;
+        size_t headerCount;
+        if (Map_GetInternals(handleData->headers, &keys, &values, &headerCount) != MAP_OK)
+        {
+            /*Codes_SRS_HTTP_HEADERS_99_034:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs]*/
+            result = HTTP_HEADERS_ERROR;
+            LogError("Map_GetInternals failed, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+        }
+        else
+        {
+            /*Codes_SRS_HTTP_HEADERS_99_029:[ The function shall return HTTP_HEADERS_INVALID_ARG if index is not valid (for example, out of range) for the currently stored headers.]*/
+            if (index >= headerCount)
+            {
+                result = HTTP_HEADERS_INVALID_ARG;
+                LogError("index out of bounds, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+            }
+            else
+            {
+                *destination = (char*)malloc(strlen(keys[index]) + COLON_AND_SPACE_LENGTH + strlen(values[index]) + 1);
+                if (*destination == NULL)
+                {
+                    /*Codes_SRS_HTTP_HEADERS_99_034:[ The function shall return HTTP_HEADERS_ERROR when an internal error occurs]*/
+                    result = HTTP_HEADERS_ERROR;
+                    LogError("unable to malloc, result= %s\r\n", ENUM_TO_STRING(HTTP_HEADERS_RESULT, result));
+                }
+                else
+                {
+                    /*Codes_SRS_HTTP_HEADERS_99_016:[ The function shall store the name:value pair in such a way that when later retrieved by a call to GetHeader it will return a string that shall strcmp equal to the name+": "+value.]*/
+                    /*Codes_SRS_HTTP_HEADERS_99_027:[ Calling this API shall produce the string value+": "+pair) for the index header in the *destination parameter.]*/
+                    strcpy(*destination, keys[index]);
+                    strcat(*destination, COLON_AND_SPACE);
+                    strcat(*destination, values[index]);
+                    /*Codes_SRS_HTTP_HEADERS_99_035:[ The function shall return HTTP_HEADERS_OK when the function executed without error.]*/
+                    result = HTTP_HEADERS_OK;
+                }
+            }
+        }
+    }
+
+    return result;
+}
+
+HTTP_HEADERS_HANDLE HTTPHeaders_Clone(HTTP_HEADERS_HANDLE handle)
+{
+    HTTP_HEADERS_HANDLE_DATA* result;
+    /*Codes_SRS_HTTP_HEADERS_02_003: [If handle is NULL then HTTPHeaders_Clone shall return NULL.] */
+    if (handle == NULL)
+    {
+        result = NULL;
+    }
+    else
+    {
+        /*Codes_SRS_HTTP_HEADERS_02_004: [Otherwise HTTPHeaders_Clone shall clone the content of handle to a new handle.] */
+        result = malloc(sizeof(HTTP_HEADERS_HANDLE_DATA));
+        if (result == NULL)
+        {
+            /*Codes_SRS_HTTP_HEADERS_02_005: [If cloning fails for any reason, then HTTPHeaders_Clone shall return NULL.] */
+        }
+        else
+        {
+            HTTP_HEADERS_HANDLE_DATA* handleData = handle;
+            result->headers = Map_Clone(handleData->headers);
+            if (result->headers == NULL)
+            {
+                /*Codes_SRS_HTTP_HEADERS_02_005: [If cloning fails for any reason, then HTTPHeaders_Clone shall return NULL.] */
+                free(result);
+                result = NULL;
+            }
+            else
+            {
+                /*all is fine*/
+            }
+        }
+    }
+    return result;
+}