// Copyright (c) Texas Instruments. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include #include #include #include #include "azure_c_shared_utility/httpapi.h" #include "azure_c_shared_utility/strings.h" #include "azure_c_shared_utility/xlogging.h" #include "azure_c_shared_utility/safe_math.h" #define CONTENT_BUF_LEN 128 static const char* getHttpMethod(HTTPAPI_REQUEST_TYPE requestType) { switch (requestType) { case HTTPAPI_REQUEST_GET: return (HTTPStd_GET); case HTTPAPI_REQUEST_POST: return (HTTPStd_POST); case HTTPAPI_REQUEST_PUT: return (HTTPStd_PUT); case HTTPAPI_REQUEST_DELETE: return (HTTPStd_DELETE); case HTTPAPI_REQUEST_PATCH: return (HTTPStd_PATCH); default: return (NULL); } } static int splitHeader(char *headerName, char **headerValue) { *headerValue = strchr(headerName, ':'); if (*headerValue == NULL) { return (-1); } **headerValue = '\0'; (*headerValue)++; while (**headerValue == ' ') { (*headerValue)++; } return (0); } HTTPAPI_RESULT HTTPAPI_Init(void) { return (HTTPAPI_OK); } void HTTPAPI_Deinit(void) { } HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName) { int ret; struct sockaddr addr; HTTPCli_Handle cli; ret = HTTPCli_initSockAddr(&addr, hostName, 0); if (ret < 0) { LogError("HTTPCli_initSockAddr failed, ret=%d", ret); return (NULL); } ((struct sockaddr_in *) (&addr))->sin_port = htons(HTTPStd_SECURE_PORT); cli = HTTPCli_create(); if (cli == NULL) { LogError("HTTPCli_create failed"); return (NULL); } ret = HTTPCli_connect(cli, &addr, HTTPCli_TYPE_TLS, NULL); if (ret < 0) { LogError("HTTPCli_connect failed, ret=%d", ret); HTTPCli_delete(&cli); return (NULL); } return ((HTTP_HANDLE) cli); } void HTTPAPI_CloseConnection(HTTP_HANDLE handle) { HTTPCli_Handle cli = (HTTPCli_Handle) handle; if (cli) { HTTPCli_disconnect(cli); HTTPCli_delete(&cli); } } 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) { HTTPCli_Handle cli = (HTTPCli_Handle) handle; int ret; int offset; size_t cnt; char contentBuf[CONTENT_BUF_LEN] = {0}; char *hname; char *hvalue; const char *method; bool moreFlag; method = getHttpMethod(requestType); if ((cli == NULL) || (method == NULL) || (relativePath == NULL) || (statusCode == NULL) || (responseHeadersHandle == NULL)) { LogError("Invalid arguments: handle=%p, requestType=%d, relativePath=%p, statusCode=%p, responseHeadersHandle=%p", handle, (int)requestType, relativePath, statusCode, responseHeadersHandle); return (HTTPAPI_INVALID_ARG); } else if (HTTPHeaders_GetHeaderCount(httpHeadersHandle, &cnt) != HTTP_HEADERS_OK) { LogError("Cannot get header count"); return (HTTPAPI_QUERY_HEADERS_FAILED); } /* Send the request line */ ret = HTTPCli_sendRequest(cli, method, relativePath, true); if (ret < 0) { LogError("HTTPCli_sendRequest failed, ret=%d", ret); return (HTTPAPI_SEND_REQUEST_FAILED); } /* Send the request headers */ while (cnt--) { ret = HTTPHeaders_GetHeader(httpHeadersHandle, cnt, &hname); if (ret != HTTP_HEADERS_OK) { LogError("Cannot get request header %d", cnt); return (HTTPAPI_QUERY_HEADERS_FAILED); } ret = splitHeader(hname, &hvalue); if (ret == 0) { ret = HTTPCli_sendField(cli, hname, hvalue, false); } free(hname); hname = NULL; if (ret < 0) { LogError("HTTP send field failed, ret=%d", ret); return (HTTPAPI_SEND_REQUEST_FAILED); } } /* Send the last header and request body */ ret = HTTPCli_sendField(cli, NULL, NULL, true); if (ret < 0) { LogError("HTTP send empty field failed, ret=%d", ret); return (HTTPAPI_SEND_REQUEST_FAILED); } if (content && contentLength != 0) { ret = HTTPCli_sendRequestBody(cli, (const char *)content, contentLength); if (ret < 0) { LogError("HTTP send request body failed, ret=%d", ret); return (HTTPAPI_SEND_REQUEST_FAILED); } } /* Get the response status code */ ret = HTTPCli_getResponseStatus(cli); if (ret < 0) { LogError("HTTP receive response failed, ret=%d", ret); return (HTTPAPI_RECEIVE_RESPONSE_FAILED); } *statusCode = (unsigned int)ret; /* Get the response headers */ hname = NULL; cnt = 0; offset = 0; do { ret = HTTPCli_readResponseHeader(cli, contentBuf, CONTENT_BUF_LEN, &moreFlag); if (ret < 0) { LogError("HTTP read response header failed, ret=%d", ret); ret = HTTPAPI_RECEIVE_RESPONSE_FAILED; goto headersDone; } else if (ret == 0) { /* All headers read */ goto headersDone; } if (cnt < offset + ret) { size_t malloc_size = safe_add_size_t(offset, ret); if (malloc_size == SIZE_MAX) { LogError("invalid realloc size"); hname = NULL; } else { hname = (char*)realloc(hname, malloc_size); } if (hname == NULL) { LogError("Failed reallocating memory"); ret = HTTPAPI_ALLOC_FAILED; goto headersDone; } cnt = offset + ret; } memcpy(hname + offset, contentBuf, ret); offset += ret; if (moreFlag) { continue; } ret = splitHeader(hname, &hvalue); if (ret < 0) { LogError("HTTP split header failed, ret=%d", ret); ret = HTTPAPI_HTTP_HEADERS_FAILED; goto headersDone; } ret = HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, hname, hvalue); if (ret != HTTP_HEADERS_OK) { LogError("Adding the response header failed"); ret = HTTPAPI_HTTP_HEADERS_FAILED; goto headersDone; } offset = 0; } while (1); headersDone: free(hname); hname = NULL; if (ret != 0) { return ((HTTPAPI_RESULT)ret); } /* Get response body */ if (responseContent != NULL) { offset = 0; cnt = 0; do { ret = HTTPCli_readResponseBody(cli, contentBuf, CONTENT_BUF_LEN, &moreFlag); if (ret < 0) { LogError("HTTP read response body failed, ret=%d", ret); ret = HTTPAPI_RECEIVE_RESPONSE_FAILED; goto contentDone; } if (ret != 0) { cnt = ret; ret = BUFFER_enlarge(responseContent, cnt); if (ret != 0) { LogError("Failed enlarging response buffer"); ret = HTTPAPI_ALLOC_FAILED; goto contentDone; } ret = BUFFER_content(responseContent, (const unsigned char **)&hname); if (ret != 0) { LogError("Failed getting the response buffer content"); ret = HTTPAPI_ALLOC_FAILED; goto contentDone; } memcpy(hname + offset, contentBuf, cnt); offset += cnt; } } while (moreFlag); contentDone: if (ret < 0) { BUFFER_unbuild(responseContent); return ((HTTPAPI_RESULT)ret); } } return (HTTPAPI_OK); } HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value) { return (HTTPAPI_INVALID_ARG); } HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue) { return (HTTPAPI_INVALID_ARG); }