//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) { int result; size_t headersCount; char* header; HTTPSClient* client = (HTTPSClient*)handle; if (!client->connected()) { // client not connected LogError("HTTPS request failed, client not connected\n"); return HTTPAPI_OPEN_REQUEST_FAILED; } result = client->sendRequest(HTTPRequestTypes[requestType], relativePath); if (!result) { LogError("HTTPS send request failed\n"); return HTTPAPI_SEND_REQUEST_FAILED; } HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount); for (size_t i = 0; i < headersCount && result; i++) { HTTPHeaders_GetHeader(httpHeadersHandle, i, &header); result = client->sendHeader(header); free(header); } if (!result) { LogError("HTTPS send header failed\n"); return HTTPAPI_SEND_REQUEST_FAILED; } result = client->sendBody(content, contentLength); if (!result) { LogError("HTTPS send body failed\n"); return HTTPAPI_SEND_REQUEST_FAILED; } result = client->readStatus(); if (result == -1) { return HTTPAPI_STRING_PROCESSING_ERROR; } *statusCode = result; while (result > 0) { String headerName; String headerValue; result = client->readHeader(headerName, headerValue); HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, headerName.c_str(), headerValue.c_str()); } if (result == -1) { LogError("HTTPS header parsing failed\n"); return HTTPAPI_HTTP_HEADERS_FAILED; } contentLength = client->contentLength(); if (contentLength) { BUFFER_pre_build(responseContent, contentLength); client->readBody(BUFFER_u_char(responseContent), contentLength); } return HTTPAPI_OK; }