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_mbed.cpp
- Revision:
- 0:fa2de1b79154
- Child:
- 1:9190c0f4d23a
diff -r 000000000000 -r fa2de1b79154 httpapi_mbed.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/httpapi_mbed.cpp Fri Apr 08 12:01:36 2016 -0700
@@ -0,0 +1,551 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include <cstdlib>
+#ifdef _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+#endif
+
+#include <cstdio>
+#include <cstdlib>
+#include <cctype>
+#include "azure_c_shared_utility/httpapi.h"
+#include "azure_c_shared_utility/httpheaders.h"
+#include "azure_c_shared_utility/crt_abstractions.h"
+#include "mbed.h"
+#include "EthernetInterface.h"
+#include "azure_c_shared_utility/wolfssl_connection.h"
+#include "azure_c_shared_utility/iot_logging.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)
+
+class HTTP_HANDLE_DATA
+{
+public:
+ char host[MAX_HOSTNAME];
+ char* certificate;
+ WolfSSLConnection con;
+};
+
+HTTPAPI_RESULT HTTPAPI_Init(void)
+{
+ time_t ctTime;
+ ctTime = time(NULL);
+ HTTPAPI_RESULT result;
+ LogInfo("HTTAPI_Init::Time is now (UTC) %s\r\n", ctime(&ctTime));
+
+ result = HTTPAPI_OK;
+
+ return result;
+}
+
+void HTTPAPI_Deinit(void)
+{
+ ;
+}
+
+HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName)
+{
+ HTTP_HANDLE_DATA* handle = NULL;
+
+ if (hostName)
+ {
+ handle = new HTTP_HANDLE_DATA();
+ if (strcpy_s(handle->host, MAX_HOSTNAME, hostName) != 0)
+ {
+ LogError("HTTPAPI_CreateConnection::Could not strcpy_s\r\n");
+ delete handle;
+ handle = NULL;
+ }
+ else
+ {
+ handle->certificate = NULL;
+ }
+ }
+ else
+ {
+ LogInfo("HTTPAPI_CreateConnection:: null hostName parameter\r\n");
+ }
+
+ return (HTTP_HANDLE)handle;
+}
+
+void HTTPAPI_CloseConnection(HTTP_HANDLE handle)
+{
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)handle;
+
+ if (h)
+ {
+ if (h->con.is_connected())
+ {
+ LogInfo("HTTPAPI_CloseConnection h->con.close(); to %s\r\n", h->host);
+ h->con.close();
+ }
+ if (h->certificate)
+ {
+ delete[] h->certificate;
+ }
+ delete h;
+ }
+}
+
+static int readLine(WolfSSLConnection* con, char* buf, const size_t size)
+{
+ // reads until \r\n is encountered. writes in buf all the characters
+ // read until \r\n and returns the number of characters in the buffer.
+ char* p = buf;
+ char c;
+ if (con->receive(&c, 1) < 0)
+ return -1;
+ while (c != '\r') {
+ if ((p - buf + 1) >= (int)size)
+ return -1;
+ *p++ = c;
+ if (con->receive(&c, 1) < 0)
+ return -1;
+ }
+ *p = 0;
+ if (con->receive(&c, 1) < 0 || c != '\n') // skip \n
+ return -1;
+ return p - buf;
+}
+
+static int readChunk(WolfSSLConnection* con, 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 = con->receive(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(WolfSSLConnection* con, 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(con, (char*)buf, size) < 0)
+ return -1;
+
+ n -= size;
+ }
+
+ if (readChunk(con, (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;
+ WolfSSLConnection* 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;
+ }
+
+ HTTP_HANDLE_DATA* httpHandle = (HTTP_HANDLE_DATA*)handle;
+ con = &(httpHandle->con);
+
+ if (!con->is_connected())
+ {
+ // Load the certificate
+ if ((httpHandle->certificate != NULL) &&
+ (!con->load_certificate((const unsigned char*)httpHandle->certificate, strlen(httpHandle->certificate) + 1)))
+ {
+ result = HTTPAPI_ERROR;
+ LogError("Could not load certificate (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+
+ // Make the connection
+ if (con->connect(httpHandle->host, 443) != 0)
+ {
+ result = HTTPAPI_ERROR;
+ LogError("Could not connect (result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ }
+
+ //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;
+ }
+ 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 (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)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ goto exit;
+ }
+ if (con->send_all(header, strlen(header)) < 0)
+ {
+ result = HTTPAPI_SEND_REQUEST_FAILED;
+ LogError("(result = %s)\r\n", ENUM_TO_STRING(HTTPAPI_RESULT, result));
+ free(header);
+ 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));
+ free(header);
+ goto exit;
+ }
+ free(header);
+ }
+
+ //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)
+ {
+ 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;
+
+ //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:";
+
+ 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
+ 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
+ {
+ 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;
+ }
+ }
+
+ }
+
+exit:
+ 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\r\n");
+ }
+ else if (strcmp("TrustedCerts", optionName) == 0)
+ {
+ HTTP_HANDLE_DATA* h = (HTTP_HANDLE_DATA*)handle;
+ if (h->certificate)
+ {
+ delete[] h->certificate;
+ }
+
+ int len = strlen((char*)value);
+ h->certificate = new char[len + 1];
+ if (h->certificate == NULL)
+ {
+ result = HTTPAPI_ERROR;
+ LogError("unable to allocate certificate memory in HTTPAPI_SetOption\r\n");
+ }
+ else
+ {
+ (void)strcpy(h->certificate, (const char*)value);
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("unknown option %s\r\n", 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\r\n");
+ }
+ 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\r\n");
+ }
+ else
+ {
+ (void)strcpy(tempCert, (const char*)value);
+ *savedValue = tempCert;
+ result = HTTPAPI_OK;
+ }
+ }
+ else
+ {
+ result = HTTPAPI_INVALID_ARG;
+ LogError("unknown option %s\r\n", optionName);
+ }
+ return result;
+}
