Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of azure_c_shared_utility by
Diff: httpapi_compact.c
- Revision:
- 6:c55b013dfc2a
- Child:
- 7:1af47e3a19b6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/httpapi_compact.c Fri Jul 01 10:43:23 2016 -0700
@@ -0,0 +1,800 @@
+// 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 <stdlib.h>
+#include <ctype.h>
+#include "azure_c_shared_utility/httpapi.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>
+
+#define MAX_HOSTNAME 64
+#define TEMP_BUFFER_SIZE 4096
+
+#define CHAR_COUNT(A) (sizeof(A) - 1)
+
+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 host[MAX_HOSTNAME];
+ 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;
+
+HTTPAPI_RESULT HTTPAPI_Init(void)
+{
+ return HTTPAPI_OK;
+}
+
+void HTTPAPI_Deinit(void)
+{
+}
+
+HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
+{
+ HTTP_HANDLE_DATA* handle = NULL;
+
+ if (hostName)
+ {
+ handle = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA));
+ if (handle != NULL)
+ {
+ if (strcpy_s(handle->host, MAX_HOSTNAME, hostName) != 0)
+ {
+ LogError("HTTPAPI_CreateConnection::Could not strcpy_s");
+ free(handle);
+ handle = NULL;
+ }
+ else
+ {
+ TLSIO_CONFIG tlsio_config = { hostName, 443 };
+ handle->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config);
+ if (handle->xio_handle == NULL)
+ {
+ LogError("HTTPAPI_CreateConnection::xio_create failed");
+ free(handle->host);
+ free(handle);
+ handle = NULL;
+ }
+ else
+ {
+ handle->is_connected = 0;
+ handle->is_io_error = 0;
+ handle->received_bytes_count = 0;
+ handle->received_bytes = NULL;
+ handle->send_all_result = SEND_ALL_RESULT_NOT_STARTED;
+ handle->certificate = NULL;
+ }
+ }
+ }
+ }
+ else
+ {
+ LogInfo("HTTPAPI_CreateConnection:: null hostName parameter");
+ }
+
+ return (HTTP_HANDLE)handle;
+}
+
+void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
+{
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)handle;
+
+ if (h)
+ {
+ if (h->xio_handle != NULL)
+ {
+ LogInfo("HTTPAPI_CloseConnection xio_destroy(); to %s", h->host);
+ xio_destroy(h->xio_handle);
+ }
+
+ if (h->certificate)
+ {
+ free(h->certificate);
+ }
+
+ free(h);
+ }
+}
+
+static void on_io_open_complete(void* context, IO_OPEN_RESULT open_result)
+{
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)context;
+ if (open_result == IO_OPEN_OK)
+ {
+ h->is_connected = 1;
+ h->is_io_error = 0;
+ }
+ else
+ {
+ h->is_io_error = 1;
+ }
+}
+
+static int my_strnicmp(const char* s1, const char* s2, size_t n)
+{
+ size_t i;
+ int result = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ /* compute the difference between the chars */
+ result = tolower(s1[i]) - tolower(s2[i]);
+
+ /* break if we have a difference ... */
+ if ((result != 0) ||
+ /* ... or if we got to the end of one the strings */
+ (s1[i] == '\0') || (s2[i] == '\0'))
+ {
+ break;
+ }
+ }
+
+ return result;
+}
+
+static int my_stricmp(const char* s1, const char* s2)
+{
+ size_t i = 0;
+
+ while ((s1[i] != '\0') && (s2[i] != '\0'))
+ {
+ /* break if we have a difference ... */
+ if (tolower(s1[i]) != tolower(s2[i]))
+ {
+ break;
+ }
+
+ i++;
+ }
+
+ /* if we broke because we are at end of string this will yield 0 */
+ /* if we broke because there was a difference this will yield non-zero */
+ return tolower(s1[i]) - tolower(s2[i]);
+}
+
+static void on_bytes_received(void* context, const unsigned char* buffer, size_t size)
+{
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)context;
+
+ /* Here we got some bytes so we'll buffer them so the receive functions can consumer it */
+ unsigned char* new_received_bytes = (unsigned char*)realloc(h->received_bytes, h->received_bytes_count + size);
+ if (new_received_bytes == NULL)
+ {
+ h->is_io_error = 1;
+ LogError("on_bytes_received: Error allocating memory for received data");
+ }
+ else
+ {
+ h->received_bytes = new_received_bytes;
+ (void)memcpy(h->received_bytes + h->received_bytes_count, buffer, size);
+ h->received_bytes_count += size;
+ }
+}
+
+static void on_io_error(void* context)
+{
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)context;
+ h->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 = 0;
+
+ if (count < 0)
+ {
+ result = -1;
+ }
+ else
+ {
+ 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)
+ {
+ 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 on_send_complete(void* context, IO_SEND_RESULT send_result)
+{
+ /* If a send is complete we'll simply signal this by changing the send all state */
+ HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context;
+ 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, char* buffer, int count)
+{
+ int result;
+
+ if (count < 0)
+ {
+ 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)
+ {
+ result = -1;
+ }
+ else
+ {
+ /* We have to loop in here until all bytes are sent or we encounter an error. */
+ while (1)
+ {
+ xio_dowork(http_instance->xio_handle);
+
+ /* 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;
+ }
+
+ /* 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 = 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
+ char* p = buf;
+ char c;
+ if (conn_receive(http_instance, &c, 1) < 0)
+ return -1;
+ while (c != '\r') {
+ if ((p - buf + 1) >= (int)size)
+ return -1;
+ *p++ = c;
+ if (conn_receive(http_instance, &c, 1) < 0)
+ return -1;
+ }
+ *p = 0;
+ if (conn_receive(http_instance, &c, 1) < 0 || c != '\n') // skip \n
+ return -1;
+ return p - buf;
+}
+
+static int readChunk(HTTP_HANDLE_DATA* http_instance, char* buf, size_t size)
+{
+ size_t 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 > 0)
+ {
+ cur = conn_receive(http_instance, buf + offset, size);
+
+ // end of stream reached
+ if (cur == 0)
+ return offset;
+
+ // read cur bytes (might be less than requested)
+ size -= cur;
+ offset += cur;
+ }
+
+ return offset;
+}
+
+static int skipN(HTTP_HANDLE_DATA* http_instance, size_t n, char* buf, size_t size)
+{
+ size_t org = 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)
+ return -1;
+
+ n -= size;
+ }
+
+ if (readChunk(http_instance, (char*)buf, n) < 0)
+ return -1;
+
+ return org;
+}
+
+//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;
+ size_t headersCount;
+ char buf[TEMP_BUFFER_SIZE];
+ int ret;
+ 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)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)handle;
+
+ if (handle->is_connected == 0)
+ {
+ // Load the certificate
+ if ((httpHandle->certificate != NULL) &&
+ (xio_setoption(httpHandle->xio_handle, "TrustedCerts", httpHandle->certificate) != 0))
+ {
+ result = HTTPAPI_ERROR;
+ LogError("Could not load certificate (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ // Make the connection
+ if (xio_open(httpHandle->xio_handle, on_io_open_complete, httpHandle, on_bytes_received, httpHandle, on_io_error, httpHandle) != 0)
+ {
+ result = HTTPAPI_ERROR;
+ LogError("Could not connect (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ while (1)
+ {
+ xio_dowork(httpHandle->xio_handle);
+ if ((handle->is_connected == 1) ||
+ (handle->is_io_error == 1))
+ {
+ break;
+ }
+
+ ThreadAPI_Sleep(1);
+ }
+ }
+
+ //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)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ if (conn_send_all(httpHandle, buf, strlen(buf)) < 0)
+ {
+ result = HTTPAPI_SEND_REQUEST_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ //Send default headers
+ for (size_t i = 0; i < headersCount; i++)
+ {
+ char* header;
+ if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &header) != HTTP_HEADERS_OK)
+ {
+ result = HTTPAPI_HTTP_HEADERS_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ if (conn_send_all(httpHandle, header, strlen(header)) < 0)
+ {
+ result = HTTPAPI_SEND_REQUEST_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ free(header);
+ goto exit;
+ }
+ if (conn_send_all(httpHandle, "\r\n", 2) < 0)
+ {
+ result = HTTPAPI_SEND_REQUEST_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ free(header);
+ goto exit;
+ }
+ free(header);
+ }
+
+ //Close headers
+ if (conn_send_all(httpHandle, "\r\n", 2) < 0)
+ {
+ result = HTTPAPI_SEND_REQUEST_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ //Send data (if available)
+ if (content && contentLength > 0)
+ {
+ if (conn_send_all(httpHandle, (char*)content, contentLength) < 0)
+ {
+ result = HTTPAPI_SEND_REQUEST_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ }
+
+ //Receive response
+ if (readLine(httpHandle, buf, sizeof(buf)) < 0)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", 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", buf);
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ if (statusCode)
+ *statusCode = ret;
+
+ //Read HTTP response headers
+ if (readLine(httpHandle, buf, sizeof(buf)) < 0)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ while (buf[0])
+ {
+ const char ContentLength[] = "content-length:";
+ const char TransferEncoding[] = "transfer-encoding:";
+
+ if (my_strnicmp(buf, ContentLength, CHAR_COUNT(ContentLength)) == 0)
+ {
+ if (sscanf(buf + CHAR_COUNT(ContentLength), " %d", &bodyLength) != 1)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ }
+ else if (my_strnicmp(buf, TransferEncoding, CHAR_COUNT(TransferEncoding)) == 0)
+ {
+ const char* p = buf + CHAR_COUNT(TransferEncoding);
+ while (isspace(*p)) p++;
+ if (my_stricmp(p, "chunked") == 0)
+ chunked = true;
+ }
+
+ char* whereIsColon = strchr((char*)buf, ':');
+ if (whereIsColon && responseHeadersHandle != NULL)
+ {
+ *whereIsColon = '\0';
+ HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1);
+ }
+
+ if (readLine(httpHandle, buf, sizeof(buf)) < 0)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ }
+
+ //Read HTTP response body
+ if (!chunked)
+ {
+ if (bodyLength)
+ {
+ if (responseContent != NULL)
+ {
+ if (BUFFER_pre_build(responseContent, bodyLength) != 0)
+ {
+ result = HTTPAPI_ALLOC_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ }
+ else if (BUFFER_content(responseContent, &receivedContent) != 0)
+ {
+ (void)BUFFER_unbuild(responseContent);
+
+ result = HTTPAPI_ALLOC_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ }
+
+ if (readChunk(httpHandle, (char*)receivedContent, bodyLength) < 0)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ else
+ {
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ (void)skipN(httpHandle, bodyLength, buf, sizeof(buf));
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ size_t size = 0;
+ result = HTTPAPI_OK;
+ for (;;)
+ {
+ int chunkSize;
+ if (readLine(httpHandle, buf, sizeof(buf)) < 0) // read [length in hex]/r/n
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", 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)", 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(httpHandle, (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)", 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)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ }
+ else if (BUFFER_content(responseContent, &receivedContent) != 0)
+ {
+ (void)BUFFER_unbuild(responseContent);
+
+ result = HTTPAPI_ALLOC_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ }
+
+ if (readChunk(httpHandle, (char*)receivedContent + size, chunkSize) < 0)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ }
+ else
+ {
+ if (skipN(httpHandle, chunkSize, buf, sizeof(buf)) < 0)
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ }
+
+ if (readChunk(httpHandle, (char*)buf, 2) < 0
+ || buf[0] != '\r' || buf[1] != '\n') // skip /r/n
+ {
+ result = HTTPAPI_READ_DATA_FAILED;
+ LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ size += chunkSize;
+ }
+ }
+
+ }
+
+exit:
+ if ((handle != NULL) &&
+ (handle->is_io_error != 0))
+ {
+ xio_close(handle->xio_handle, NULL, NULL);
+ handle->is_connected = 0;
+ }
+
+ return result;
+}
+
+HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value)
+{
+ HTTPAPI_RESULT result;
+ if (
+ (handle == NULL) ||
+ (optionName == NULL) ||
+ (value == NULL)
+ )
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("invalid parameter (NULL) passed to HTTPAPI_SetOption");
+ }
+ else if (strcmp("TrustedCerts", optionName) == 0)
+ {
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)handle;
+ if (h->certificate)
+ {
+ free(h->certificate);
+ }
+
+ int len = strlen((char*)value);
+ h->certificate = (char*)malloc(len + 1);
+ if (h->certificate == NULL)
+ {
+ result = HTTPAPI_ERROR;
+ LogError("unable to allocate certificate memory in HTTPAPI_SetOption");
+ }
+ else
+ {
+ (void)strcpy(h->certificate, (const char*)value);
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("unknown option %s", optionName);
+ }
+ return result;
+}
+
+HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue)
+{
+ HTTPAPI_RESULT result;
+ if (
+ (optionName == NULL) ||
+ (value == NULL) ||
+ (savedValue == NULL)
+ )
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("invalid argument(NULL) passed to HTTPAPI_CloneOption");
+ }
+ else if (strcmp("TrustedCerts", optionName) == 0)
+ {
+ size_t certLen = strlen((const char*)value);
+ char* tempCert = (char*)malloc(certLen+1);
+ if (tempCert == NULL)
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("unable to allocate certificate memory in HTTPAPI_CloneOption");
+ }
+ else
+ {
+ (void)strcpy(tempCert, (const char*)value);
+ *savedValue = tempCert;
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("unknown option %s", optionName);
+ }
+ return result;
+}
