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.
Dependents: STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more
httpapi_compact.c
00001 // Copyright (c) Microsoft. All rights reserved. 00002 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 00003 00004 #include <stdlib.h> 00005 #include <stdio.h> 00006 #include <ctype.h> 00007 #include <string.h> 00008 #include <limits.h> 00009 #include "azure_c_shared_utility/gballoc.h" 00010 #include "azure_c_shared_utility/httpheaders.h" 00011 #include "azure_c_shared_utility/crt_abstractions.h" 00012 #include "azure_c_shared_utility/xlogging.h" 00013 #include "azure_c_shared_utility/xio.h" 00014 #include "azure_c_shared_utility/platform.h" 00015 #include "azure_c_shared_utility/tlsio.h" 00016 #include "azure_c_shared_utility/threadapi.h" 00017 #include "azure_c_shared_utility/shared_util_options.h" 00018 00019 #ifdef _MSC_VER 00020 #define snprintf _snprintf 00021 #endif 00022 00023 /*Codes_SRS_HTTPAPI_COMPACT_21_001: [ The httpapi_compact shall implement the methods defined by the `httpapi.h`. ]*/ 00024 /*Codes_SRS_HTTPAPI_COMPACT_21_002: [ The httpapi_compact shall support the http requests. ]*/ 00025 /*Codes_SRS_HTTPAPI_COMPACT_21_003: [ The httpapi_compact shall return error codes defined by HTTPAPI_RESULT. ]*/ 00026 #include "azure_c_shared_utility/httpapi.h" 00027 00028 #define MAX_HOSTNAME 64 00029 #define TEMP_BUFFER_SIZE 1024 00030 00031 /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ The HTTPAPI_ExecuteRequest shall wait, at least, 10 seconds for the SSL open process. ]*/ 00032 #define MAX_OPEN_RETRY 100 00033 /*Codes_SRS_HTTPAPI_COMPACT_21_084: [ The HTTPAPI_CloseConnection shall wait, at least, 10 seconds for the SSL close process. ]*/ 00034 #define MAX_CLOSE_RETRY 100 00035 /*Codes_SRS_HTTPAPI_COMPACT_21_079: [ The HTTPAPI_ExecuteRequest shall wait, at least, 20 seconds to send a buffer using the SSL connection. ]*/ 00036 #define MAX_SEND_RETRY 200 00037 /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ 00038 #define MAX_RECEIVE_RETRY 200 00039 /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ 00040 #define RETRY_INTERVAL_IN_MICROSECONDS 100 00041 00042 DEFINE_ENUM_STRINGS(HTTPAPI_RESULT, HTTPAPI_RESULT_VALUES) 00043 00044 typedef struct HTTP_HANDLE_DATA_TAG 00045 { 00046 char* certificate; 00047 char* x509ClientCertificate; 00048 char* x509ClientPrivateKey; 00049 XIO_HANDLE xio_handle; 00050 size_t received_bytes_count; 00051 unsigned char* received_bytes; 00052 unsigned int is_io_error : 1; 00053 unsigned int is_connected : 1; 00054 unsigned int send_completed : 1; 00055 } HTTP_HANDLE_DATA; 00056 00057 /*the following function does the same as sscanf(pos2, "%d", &sec)*/ 00058 /*this function only exists because some of platforms do not have sscanf. */ 00059 static int ParseStringToDecimal(const char *src, int* dst) 00060 { 00061 int result; 00062 char* next; 00063 long num = strtol(src, &next, 0); 00064 if (src == next || num < INT_MIN || num > INT_MAX) 00065 { 00066 result = EOF; 00067 } 00068 else 00069 { 00070 result = 1; 00071 } 00072 if (num < INT_MIN) num = INT_MIN; 00073 if (num > INT_MAX) num = INT_MAX; 00074 *dst = (int)num; 00075 return result; 00076 } 00077 00078 /*the following function does the same as sscanf(pos2, "%x", &sec)*/ 00079 /*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined x numbers. */ 00080 #define HEXA_DIGIT_VAL(c) (((c>='0') && (c<='9')) ? (c-'0') : ((c>='a') && (c<='f')) ? (c-'a'+10) : ((c>='A') && (c<='F')) ? (c-'A'+10) : -1) 00081 static int ParseStringToHexadecimal(const char *src, size_t* dst) 00082 { 00083 int result; 00084 int digitVal; 00085 if (src == NULL) 00086 { 00087 result = EOF; 00088 } 00089 else if (HEXA_DIGIT_VAL(*src) == -1) 00090 { 00091 result = EOF; 00092 } 00093 else 00094 { 00095 (*dst) = 0; 00096 while ((digitVal = HEXA_DIGIT_VAL(*src)) != -1) 00097 { 00098 (*dst) *= 0x10; 00099 (*dst) += (size_t)digitVal; 00100 src++; 00101 } 00102 result = 1; 00103 } 00104 return result; 00105 } 00106 00107 /*the following function does the same as sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &ret) */ 00108 /*this function only exists because some of platforms do not have sscanf. This is not a full implementation; it only works with well-defined HTTP response. */ 00109 static int ParseHttpResponse(const char* src, int* dst) 00110 { 00111 int result; 00112 static const char HTTPPrefix[] = "HTTP/"; 00113 bool fail; 00114 const char* runPrefix; 00115 00116 if ((src == NULL) || (dst == NULL)) 00117 { 00118 result = EOF; 00119 } 00120 else 00121 { 00122 fail = false; 00123 runPrefix = HTTPPrefix; 00124 00125 while((*runPrefix) != '\0') 00126 { 00127 if ((*runPrefix) != (*src)) 00128 { 00129 fail = true; 00130 break; 00131 } 00132 src++; 00133 runPrefix++; 00134 } 00135 00136 if (!fail) 00137 { 00138 while ((*src) != '.') 00139 { 00140 if ((*src) == '\0') 00141 { 00142 fail = true; 00143 break; 00144 } 00145 src++; 00146 } 00147 } 00148 00149 if (!fail) 00150 { 00151 while ((*src) != ' ') 00152 { 00153 if ((*src) == '\0') 00154 { 00155 fail = true; 00156 break; 00157 } 00158 src++; 00159 } 00160 } 00161 00162 if (fail) 00163 { 00164 result = EOF; 00165 } 00166 else 00167 { 00168 result = ParseStringToDecimal(src, dst); 00169 } 00170 } 00171 00172 return result; 00173 } 00174 00175 HTTPAPI_RESULT HTTPAPI_Init(void) 00176 { 00177 /*Codes_SRS_HTTPAPI_COMPACT_21_004: [ The HTTPAPI_Init shall allocate all memory to control the http protocol. ]*/ 00178 /*Codes_SRS_HTTPAPI_COMPACT_21_007: [ If there is not enough memory to control the http protocol, the HTTPAPI_Init shall return HTTPAPI_ALLOC_FAILED. ]*/ 00179 /** 00180 * No memory is necessary. 00181 */ 00182 00183 /*Codes_SRS_HTTPAPI_COMPACT_21_006: [ If HTTPAPI_Init succeed allocating all the needed memory, it shall return HTTPAPI_OK. ]*/ 00184 return HTTPAPI_OK; 00185 } 00186 00187 void HTTPAPI_Deinit(void) 00188 { 00189 /*Codes_SRS_HTTPAPI_COMPACT_21_009: [ The HTTPAPI_Init shall release all memory allocated by the httpapi_compact. ]*/ 00190 /** 00191 * No memory was necessary. 00192 */ 00193 } 00194 00195 /*Codes_SRS_HTTPAPI_COMPACT_21_011: [ The HTTPAPI_CreateConnection shall create an http connection to the host specified by the hostName parameter. ]*/ 00196 HTTP_HANDLE HTTPAPI_CreateConnection(const char* hostName) 00197 { 00198 HTTP_HANDLE_DATA* http_instance; 00199 TLSIO_CONFIG tlsio_config; 00200 00201 if (hostName == NULL) 00202 { 00203 /*Codes_SRS_HTTPAPI_COMPACT_21_014: [ If the hostName is NULL, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ 00204 LogError("Invalid host name. Null hostName parameter."); 00205 http_instance = NULL; 00206 } 00207 else if (*hostName == '\0') 00208 { 00209 /*Codes_SRS_HTTPAPI_COMPACT_21_015: [ If the hostName is empty, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ 00210 LogError("Invalid host name. Empty string."); 00211 http_instance = NULL; 00212 } 00213 else 00214 { 00215 http_instance = (HTTP_HANDLE_DATA*)malloc(sizeof(HTTP_HANDLE_DATA)); 00216 /*Codes_SRS_HTTPAPI_COMPACT_21_013: [ If there is not enough memory to control the http connection, the HTTPAPI_CreateConnection shall return NULL as the handle. ]*/ 00217 if (http_instance == NULL) 00218 { 00219 LogError("There is no memory to control the http connection"); 00220 } 00221 else 00222 { 00223 tlsio_config.hostname = hostName; 00224 tlsio_config.port = 443; 00225 tlsio_config.underlying_io_interface = NULL; 00226 tlsio_config.underlying_io_parameters = NULL; 00227 00228 http_instance->xio_handle = xio_create(platform_get_default_tlsio(), (void*)&tlsio_config); 00229 00230 /*Codes_SRS_HTTPAPI_COMPACT_21_016: [ If the HTTPAPI_CreateConnection failed to create the connection, it shall return NULL as the handle. ]*/ 00231 if (http_instance->xio_handle == NULL) 00232 { 00233 LogError("Create connection failed"); 00234 free(http_instance); 00235 http_instance = NULL; 00236 } 00237 else 00238 { 00239 http_instance->is_connected = 0; 00240 http_instance->is_io_error = 0; 00241 http_instance->received_bytes_count = 0; 00242 http_instance->received_bytes = NULL; 00243 http_instance->certificate = NULL; 00244 http_instance->x509ClientCertificate = NULL; 00245 http_instance->x509ClientPrivateKey = NULL; 00246 } 00247 } 00248 } 00249 00250 /*Codes_SRS_HTTPAPI_COMPACT_21_012: [ The HTTPAPI_CreateConnection shall return a non-NULL handle on success. ]*/ 00251 return (HTTP_HANDLE)http_instance; 00252 } 00253 00254 static void on_io_close_complete(void* context) 00255 { 00256 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; 00257 00258 if (http_instance != NULL) 00259 { 00260 http_instance->is_connected = 0; 00261 } 00262 } 00263 00264 void HTTPAPI_CloseConnection(HTTP_HANDLE handle) 00265 { 00266 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; 00267 00268 /*Codes_SRS_HTTPAPI_COMPACT_21_020: [ If the connection handle is NULL, the HTTPAPI_CloseConnection shall not do anything. ]*/ 00269 if (http_instance != NULL) 00270 { 00271 /*Codes_SRS_HTTPAPI_COMPACT_21_019: [ If there is no previous connection, the HTTPAPI_CloseConnection shall not do anything. ]*/ 00272 if (http_instance->xio_handle != NULL) 00273 { 00274 http_instance->is_io_error = 0; 00275 /*Codes_SRS_HTTPAPI_COMPACT_21_017: [ The HTTPAPI_CloseConnection shall close the connection previously created in HTTPAPI_ExecuteRequest. ]*/ 00276 if (xio_close(http_instance->xio_handle, on_io_close_complete, http_instance) != 0) 00277 { 00278 LogError("The SSL got error closing the connection"); 00279 /*Codes_SRS_HTTPAPI_COMPACT_21_087: [ If the xio return anything different than 0, the HTTPAPI_CloseConnection shall destroy the connection anyway. ]*/ 00280 http_instance->is_connected = 0; 00281 } 00282 else 00283 { 00284 /*Codes_SRS_HTTPAPI_COMPACT_21_084: [ The HTTPAPI_CloseConnection shall wait, at least, 10 seconds for the SSL close process. ]*/ 00285 int countRetry = MAX_CLOSE_RETRY; 00286 while (http_instance->is_connected == 1) 00287 { 00288 xio_dowork(http_instance->xio_handle); 00289 if ((countRetry--) < 0) 00290 { 00291 /*Codes_SRS_HTTPAPI_COMPACT_21_085: [ If the HTTPAPI_CloseConnection retries 10 seconds to close the connection without success, it shall destroy the connection anyway. ]*/ 00292 LogError("Close timeout. The SSL didn't close the connection"); 00293 http_instance->is_connected = 0; 00294 } 00295 else if (http_instance->is_io_error == 1) 00296 { 00297 LogError("The SSL got error closing the connection"); 00298 http_instance->is_connected = 0; 00299 } 00300 else if (http_instance->is_connected == 1) 00301 { 00302 LogInfo("Waiting for TLS close connection"); 00303 /*Codes_SRS_HTTPAPI_COMPACT_21_086: [ The HTTPAPI_CloseConnection shall wait, at least, 100 milliseconds between retries. ]*/ 00304 ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); 00305 } 00306 } 00307 } 00308 /*Codes_SRS_HTTPAPI_COMPACT_21_076: [ After close the connection, The HTTPAPI_CloseConnection shall destroy the connection previously created in HTTPAPI_CreateConnection. ]*/ 00309 xio_destroy(http_instance->xio_handle); 00310 } 00311 00312 #ifndef DO_NOT_COPY_TRUSTED_CERTS_STRING 00313 /*Codes_SRS_HTTPAPI_COMPACT_21_018: [ If there is a certificate associated to this connection, the HTTPAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ 00314 if (http_instance->certificate) 00315 { 00316 free(http_instance->certificate); 00317 } 00318 #endif 00319 00320 /*Codes_SRS_HTTPAPI_COMPACT_06_001: [ If there is a x509 client certificate associated to this connection, the HTTAPI_CloseConnection shall free all allocated memory for the certificate. ]*/ 00321 if (http_instance->x509ClientCertificate) 00322 { 00323 free(http_instance->x509ClientCertificate); 00324 } 00325 00326 /*Codes_SRS_HTTPAPI_COMPACT_06_002: [ If there is a x509 client private key associated to this connection, then HTTP_CloseConnection shall free all the allocated memory for the private key. ]*/ 00327 if (http_instance->x509ClientPrivateKey) 00328 { 00329 free(http_instance->x509ClientPrivateKey); 00330 } 00331 free(http_instance); 00332 } 00333 } 00334 00335 static void on_io_open_complete(void* context, IO_OPEN_RESULT open_result) 00336 { 00337 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; 00338 00339 if (http_instance != NULL) 00340 { 00341 if (open_result == IO_OPEN_OK) 00342 { 00343 http_instance->is_connected = 1; 00344 http_instance->is_io_error = 0; 00345 } 00346 else 00347 { 00348 http_instance->is_io_error = 1; 00349 } 00350 } 00351 } 00352 00353 static void on_send_complete(void* context, IO_SEND_RESULT send_result) 00354 { 00355 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; 00356 00357 if (http_instance != NULL) 00358 { 00359 if (send_result == IO_SEND_OK) 00360 { 00361 http_instance->send_completed = 1; 00362 http_instance->is_io_error = 0; 00363 } 00364 else 00365 { 00366 http_instance->is_io_error = 1; 00367 } 00368 } 00369 } 00370 00371 #define TOLOWER(c) (((c>='A') && (c<='Z'))?c-'A'+'a':c) 00372 static int InternStrnicmp(const char* s1, const char* s2, size_t n) 00373 { 00374 int result; 00375 00376 if (s1 == NULL) result = -1; 00377 else if (s2 == NULL) result = 1; 00378 else 00379 { 00380 result = 0; 00381 00382 while(n-- && result == 0) 00383 { 00384 if (*s1 == 0) result = -1; 00385 else if (*s2 == 0) result = 1; 00386 else 00387 { 00388 00389 result = TOLOWER(*s1) - TOLOWER(*s2); 00390 ++s1; 00391 ++s2; 00392 } 00393 } 00394 } 00395 00396 return result; 00397 } 00398 00399 static void on_bytes_received(void* context, const unsigned char* buffer, size_t size) 00400 { 00401 unsigned char* new_received_bytes; 00402 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; 00403 00404 if (http_instance != NULL) 00405 { 00406 00407 if (buffer == NULL) 00408 { 00409 http_instance->is_io_error = 1; 00410 LogError("NULL pointer error"); 00411 } 00412 else 00413 { 00414 /* Here we got some bytes so we'll buffer them so the receive functions can consumer it */ 00415 new_received_bytes = (unsigned char*)realloc(http_instance->received_bytes, http_instance->received_bytes_count + size); 00416 if (new_received_bytes == NULL) 00417 { 00418 http_instance->is_io_error = 1; 00419 LogError("Error allocating memory for received data"); 00420 } 00421 else 00422 { 00423 http_instance->received_bytes = new_received_bytes; 00424 if (memcpy(http_instance->received_bytes + http_instance->received_bytes_count, buffer, size) == NULL) 00425 { 00426 http_instance->is_io_error = 1; 00427 LogError("Error copping received data to the HTTP bufffer"); 00428 } 00429 else 00430 { 00431 http_instance->received_bytes_count += size; 00432 } 00433 } 00434 } 00435 } 00436 } 00437 00438 static void on_io_error(void* context) 00439 { 00440 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)context; 00441 if (http_instance != NULL) 00442 { 00443 http_instance->is_io_error = 1; 00444 LogError("Error signalled by underlying IO"); 00445 } 00446 } 00447 00448 static int conn_receive(HTTP_HANDLE_DATA* http_instance, char* buffer, int count) 00449 { 00450 int result; 00451 00452 if ((http_instance == NULL) || (buffer == NULL) || (count < 0)) 00453 { 00454 LogError("conn_receive: %s", ((http_instance == NULL) ? "Invalid HTTP instance" : "Invalid HTTP buffer")); 00455 result = -1; 00456 } 00457 else 00458 { 00459 result = 0; 00460 while (result < count) 00461 { 00462 xio_dowork(http_instance->xio_handle); 00463 00464 /* if any error was detected while receiving then simply break and report it */ 00465 if (http_instance->is_io_error != 0) 00466 { 00467 LogError("xio reported error on dowork"); 00468 result = -1; 00469 break; 00470 } 00471 00472 if (http_instance->received_bytes_count >= (size_t)count) 00473 { 00474 /* Consuming bytes from the receive buffer */ 00475 (void)memcpy(buffer, http_instance->received_bytes, count); 00476 (void)memmove(http_instance->received_bytes, http_instance->received_bytes + count, http_instance->received_bytes_count - count); 00477 http_instance->received_bytes_count -= count; 00478 00479 /* we're not reallocating at each consumption so that we don't trash due to byte by byte consumption */ 00480 if (http_instance->received_bytes_count == 0) 00481 { 00482 free(http_instance->received_bytes); 00483 http_instance->received_bytes = NULL; 00484 } 00485 00486 result = count; 00487 break; 00488 } 00489 00490 /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ 00491 ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); 00492 } 00493 } 00494 00495 return result; 00496 } 00497 00498 static void conn_receive_discard_buffer(HTTP_HANDLE_DATA* http_instance) 00499 { 00500 if (http_instance != NULL) 00501 { 00502 if (http_instance->received_bytes != NULL) 00503 { 00504 free(http_instance->received_bytes); 00505 http_instance->received_bytes = NULL; 00506 } 00507 http_instance->received_bytes_count = 0; 00508 } 00509 } 00510 00511 static int readLine(HTTP_HANDLE_DATA* http_instance, char* buf, const size_t maxBufSize) 00512 { 00513 int resultLineSize; 00514 00515 if ((http_instance == NULL) || (buf == NULL) || (maxBufSize == 0)) 00516 { 00517 LogError("%s", ((http_instance == NULL) ? "Invalid HTTP instance" : "Invalid HTTP buffer")); 00518 resultLineSize = -1; 00519 } 00520 else 00521 { 00522 char* destByte = buf; 00523 /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ 00524 int countRetry = MAX_RECEIVE_RETRY; 00525 bool endOfSearch = false; 00526 resultLineSize = -1; 00527 while (!endOfSearch) 00528 { 00529 xio_dowork(http_instance->xio_handle); 00530 00531 /* if any error was detected while receiving then simply break and report it */ 00532 if (http_instance->is_io_error != 0) 00533 { 00534 LogError("xio reported error on dowork"); 00535 endOfSearch = true; 00536 } 00537 else 00538 { 00539 unsigned char* receivedByte = http_instance->received_bytes; 00540 while (receivedByte < (http_instance->received_bytes + http_instance->received_bytes_count)) 00541 { 00542 if ((*receivedByte) != '\r') 00543 { 00544 (*destByte) = (*receivedByte); 00545 destByte++; 00546 receivedByte++; 00547 00548 if (destByte >= (buf + maxBufSize - 1)) 00549 { 00550 LogError("Received message is bigger than the http buffer"); 00551 receivedByte = http_instance->received_bytes + http_instance->received_bytes_count; 00552 endOfSearch = true; 00553 break; 00554 } 00555 } 00556 else 00557 { 00558 receivedByte++; 00559 if ((receivedByte < (http_instance->received_bytes + http_instance->received_bytes_count)) && ((*receivedByte) == '\n')) 00560 { 00561 receivedByte++; 00562 } 00563 (*destByte) = '\0'; 00564 resultLineSize = (int)(destByte - buf); 00565 endOfSearch = true; 00566 break; 00567 } 00568 } 00569 00570 http_instance->received_bytes_count -= (receivedByte - http_instance->received_bytes); 00571 if (http_instance->received_bytes_count != 0) 00572 { 00573 (void)memmove(http_instance->received_bytes, receivedByte, http_instance->received_bytes_count); 00574 } 00575 else 00576 { 00577 conn_receive_discard_buffer(http_instance); 00578 } 00579 } 00580 00581 if (!endOfSearch) 00582 { 00583 if ((countRetry--) > 0) 00584 { 00585 /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ 00586 ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); 00587 } 00588 else 00589 { 00590 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 00591 LogError("Receive timeout. The HTTP request is incomplete"); 00592 endOfSearch = true; 00593 } 00594 } 00595 } 00596 } 00597 00598 return resultLineSize; 00599 } 00600 00601 static int readChunk(HTTP_HANDLE_DATA* http_instance, char* buf, size_t size) 00602 { 00603 int cur, offset; 00604 00605 // read content with specified length, even if it is received 00606 // only in chunks due to fragmentation in the networking layer. 00607 // returns -1 in case of error. 00608 offset = 0; 00609 while (size > (size_t)0) 00610 { 00611 cur = conn_receive(http_instance, buf + offset, (int)size); 00612 00613 // end of stream reached 00614 if (cur == 0) 00615 { 00616 break; 00617 } 00618 00619 // read cur bytes (might be less than requested) 00620 size -= (size_t)cur; 00621 offset += cur; 00622 } 00623 00624 return offset; 00625 } 00626 00627 static int skipN(HTTP_HANDLE_DATA* http_instance, size_t n) 00628 { 00629 // read and abandon response content with specified length 00630 // returns -1 in case of error. 00631 00632 int result; 00633 00634 if (http_instance == NULL) 00635 { 00636 LogError("Invalid HTTP instance"); 00637 result = -1; 00638 } 00639 else 00640 { 00641 /*Codes_SRS_HTTPAPI_COMPACT_21_081: [ The HTTPAPI_ExecuteRequest shall try to read the message with the response up to 20 seconds. ]*/ 00642 int countRetry = MAX_RECEIVE_RETRY; 00643 result = (int)n; 00644 while (n > 0) 00645 { 00646 xio_dowork(http_instance->xio_handle); 00647 00648 /* if any error was detected while receiving then simply break and report it */ 00649 if (http_instance->is_io_error != 0) 00650 { 00651 LogError("xio reported error on dowork"); 00652 result = -1; 00653 n = 0; 00654 } 00655 else 00656 { 00657 if (http_instance->received_bytes_count <= n) 00658 { 00659 n -= http_instance->received_bytes_count; 00660 http_instance->received_bytes_count = 0; 00661 } 00662 else 00663 { 00664 http_instance->received_bytes_count -= n; 00665 (void)memmove(http_instance->received_bytes, http_instance->received_bytes + n, http_instance->received_bytes_count); 00666 n = 0; 00667 } 00668 00669 if (n > 0) 00670 { 00671 if ((countRetry--) > 0) 00672 { 00673 /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ 00674 ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); 00675 } 00676 else 00677 { 00678 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 00679 LogError("Receive timeout. The HTTP request is incomplete"); 00680 n = 0; 00681 result = -1; 00682 } 00683 } 00684 } 00685 } 00686 } 00687 00688 return result; 00689 } 00690 00691 00692 /*Codes_SRS_HTTPAPI_COMPACT_21_021: [ The HTTPAPI_ExecuteRequest shall execute the http communtication with the provided host, sending a request and reciving the response. ]*/ 00693 static HTTPAPI_RESULT OpenXIOConnection(HTTP_HANDLE_DATA* http_instance) 00694 { 00695 HTTPAPI_RESULT result; 00696 00697 if (http_instance->is_connected != 0) 00698 { 00699 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00700 result = HTTPAPI_OK; 00701 } 00702 else 00703 { 00704 http_instance->is_io_error = 0; 00705 00706 /*Codes_SRS_HTTPAPI_COMPACT_21_022: [ If a Certificate was provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ 00707 if ((http_instance->certificate != NULL) && 00708 (xio_setoption(http_instance->xio_handle, OPTION_TRUSTED_CERT, http_instance->certificate) != 0)) 00709 { 00710 /*Codes_SRS_HTTPAPI_COMPACT_21_023: [ If the transport failed setting the Certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ 00711 result = HTTPAPI_SET_OPTION_FAILED; 00712 LogInfo("Could not load certificate"); 00713 } 00714 /*Codes_SRS_HTTPAPI_COMPACT_06_003: [ If the x509 client certificate is provided, the HTTPAPI_ExecuteRequest shall set this option on the transport layer. ]*/ 00715 else if ((http_instance->x509ClientCertificate != NULL) && 00716 (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_CERT, http_instance->x509ClientCertificate) != 0)) 00717 { 00718 /*Codes_SRS_HTTPAPI_COMPACT_06_005: [ If the transport failed setting the client certificate, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ]*/ 00719 result = HTTPAPI_SET_OPTION_FAILED; 00720 LogInfo("Could not load the client certificate"); 00721 } 00722 else if ((http_instance->x509ClientPrivateKey != NULL) && 00723 (xio_setoption(http_instance->xio_handle, SU_OPTION_X509_PRIVATE_KEY, http_instance->x509ClientPrivateKey) != 0)) 00724 { 00725 00726 /*Codes_SRS_HTTPAPI_COMPACT_06_006: [ If the transport failed setting the client certificate private key, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_SET_OPTION_FAILED. ] */ 00727 result = HTTPAPI_SET_OPTION_FAILED; 00728 LogInfo("Could not load the client certificate private key"); 00729 } 00730 else 00731 { 00732 /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ 00733 if (xio_open(http_instance->xio_handle, on_io_open_complete, http_instance, on_bytes_received, http_instance, on_io_error, http_instance) != 0) 00734 { 00735 /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ 00736 result = HTTPAPI_OPEN_REQUEST_FAILED; 00737 } 00738 else 00739 { 00740 int countRetry; 00741 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00742 result = HTTPAPI_OK; 00743 /*Codes_SRS_HTTPAPI_COMPACT_21_077: [ The HTTPAPI_ExecuteRequest shall wait, at least, 10 seconds for the SSL open process. ]*/ 00744 countRetry = MAX_OPEN_RETRY; 00745 while ((http_instance->is_connected == 0) && 00746 (http_instance->is_io_error == 0)) 00747 { 00748 xio_dowork(http_instance->xio_handle); 00749 LogInfo("Waiting for TLS connection"); 00750 if ((countRetry--) < 0) 00751 { 00752 /*Codes_SRS_HTTPAPI_COMPACT_21_078: [ If the HTTPAPI_ExecuteRequest cannot open the connection in 10 seconds, it shall fail and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ 00753 LogError("Open timeout. The HTTP request is incomplete"); 00754 result = HTTPAPI_OPEN_REQUEST_FAILED; 00755 break; 00756 } 00757 else 00758 { 00759 /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ 00760 ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); 00761 } 00762 } 00763 } 00764 } 00765 } 00766 00767 if ((http_instance->is_io_error != 0) && (result == HTTPAPI_OK)) 00768 { 00769 /*Codes_SRS_HTTPAPI_COMPACT_21_025: [ If the open process failed, the HTTPAPI_ExecuteRequest shall not send any request and return HTTPAPI_OPEN_REQUEST_FAILED. ]*/ 00770 result = HTTPAPI_OPEN_REQUEST_FAILED; 00771 } 00772 00773 return result; 00774 } 00775 00776 static HTTPAPI_RESULT conn_send_all(HTTP_HANDLE_DATA* http_instance, const unsigned char* buf, size_t bufLen) 00777 { 00778 HTTPAPI_RESULT result; 00779 00780 http_instance->send_completed = 0; 00781 http_instance->is_io_error = 0; 00782 if (xio_send(http_instance->xio_handle, buf, bufLen, on_send_complete, http_instance) != 0) 00783 { 00784 /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ 00785 result = HTTPAPI_SEND_REQUEST_FAILED; 00786 } 00787 else 00788 { 00789 /*Codes_SRS_HTTPAPI_COMPACT_21_079: [ The HTTPAPI_ExecuteRequest shall wait, at least, 20 seconds to send a buffer using the SSL connection. ]*/ 00790 int countRetry = MAX_SEND_RETRY; 00791 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00792 result = HTTPAPI_OK; 00793 while ((http_instance->send_completed == 0) && (result == HTTPAPI_OK)) 00794 { 00795 xio_dowork(http_instance->xio_handle); 00796 if (http_instance->is_io_error != 0) 00797 { 00798 /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ 00799 result = HTTPAPI_SEND_REQUEST_FAILED; 00800 } 00801 else if ((countRetry--) <= 0) 00802 { 00803 /*Codes_SRS_HTTPAPI_COMPACT_21_080: [ If the HTTPAPI_ExecuteRequest retries to send the message for 20 seconds without success, it shall fail and return HTTPAPI_SEND_REQUEST_FAILED. ]*/ 00804 LogError("Send timeout. The HTTP request is incomplete"); 00805 /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ 00806 result = HTTPAPI_SEND_REQUEST_FAILED; 00807 } 00808 else 00809 { 00810 /*Codes_SRS_HTTPAPI_COMPACT_21_083: [ The HTTPAPI_ExecuteRequest shall wait, at least, 100 milliseconds between retries. ]*/ 00811 ThreadAPI_Sleep(RETRY_INTERVAL_IN_MICROSECONDS); 00812 } 00813 } 00814 } 00815 00816 return result; 00817 } 00818 00819 /*Codes_SRS_HTTPAPI_COMPACT_21_035: [ The HTTPAPI_ExecuteRequest shall execute resquest for types `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`. ]*/ 00820 const char httpapiRequestString[6][7] = { "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD" }; 00821 const char* get_request_type(HTTPAPI_REQUEST_TYPE requestType) 00822 { 00823 return (const char*)httpapiRequestString[requestType]; 00824 } 00825 00826 /*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ 00827 static HTTPAPI_RESULT SendHeadsToXIO(HTTP_HANDLE_DATA* http_instance, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE httpHeadersHandle, size_t headersCount) 00828 { 00829 HTTPAPI_RESULT result; 00830 char buf[TEMP_BUFFER_SIZE]; 00831 int ret; 00832 00833 //Send request 00834 /*Codes_SRS_HTTPAPI_COMPACT_21_038: [ The HTTPAPI_ExecuteRequest shall execute the resquest for the path in relativePath parameter. ]*/ 00835 /*Codes_SRS_HTTPAPI_COMPACT_21_036: [ The request type shall be provided in the parameter requestType. ]*/ 00836 if (((ret = snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\n", get_request_type(requestType), relativePath)) < 0) || 00837 ((size_t)ret >= sizeof(buf))) 00838 { 00839 /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ 00840 result = HTTPAPI_STRING_PROCESSING_ERROR; 00841 } 00842 /*Codes_SRS_HTTPAPI_COMPACT_21_028: [ If the HTTPAPI_ExecuteRequest cannot send the request header, it shall return HTTPAPI_HTTP_HEADERS_FAILED. ]*/ 00843 else if ((result = conn_send_all(http_instance, (const unsigned char*)buf, strlen(buf))) == HTTPAPI_OK) 00844 { 00845 size_t i; 00846 //Send default headers 00847 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00848 for (i = 0; ((i < headersCount) && (result == HTTPAPI_OK)); i++) 00849 { 00850 char* header; 00851 if (HTTPHeaders_GetHeader(httpHeadersHandle, i, &header) != HTTP_HEADERS_OK) 00852 { 00853 /*Codes_SRS_HTTPAPI_COMPACT_21_027: [ If the HTTPAPI_ExecuteRequest cannot create a buffer to send the request, it shall not send any request and return HTTPAPI_STRING_PROCESSING_ERROR. ]*/ 00854 result = HTTPAPI_STRING_PROCESSING_ERROR; 00855 } 00856 else 00857 { 00858 if ((result = conn_send_all(http_instance, (const unsigned char*)header, strlen(header))) == HTTPAPI_OK) 00859 { 00860 result = conn_send_all(http_instance, (const unsigned char*)"\r\n", (size_t)2); 00861 } 00862 free(header); 00863 } 00864 } 00865 00866 //Close headers 00867 if (result == HTTPAPI_OK) 00868 { 00869 result = conn_send_all(http_instance, (const unsigned char*)"\r\n", (size_t)2); 00870 } 00871 } 00872 return result; 00873 } 00874 00875 /*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ 00876 static HTTPAPI_RESULT SendContentToXIO(HTTP_HANDLE_DATA* http_instance, const unsigned char* content, size_t contentLength) 00877 { 00878 HTTPAPI_RESULT result; 00879 00880 //Send data (if available) 00881 /*Codes_SRS_HTTPAPI_COMPACT_21_045: [ If the contentLength is lower than one, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ 00882 if (content && contentLength > 0) 00883 { 00884 /*Codes_SRS_HTTPAPI_COMPACT_21_044: [ If the content is not NULL, the number of bytes in the content shall be provided in contentLength parameter. ]*/ 00885 result = conn_send_all(http_instance, content, contentLength); 00886 } 00887 else 00888 { 00889 /*Codes_SRS_HTTPAPI_COMPACT_21_043: [ If the content is NULL, the HTTPAPI_ExecuteRequest shall send the request without content. ]*/ 00890 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00891 result = HTTPAPI_OK; 00892 } 00893 return result; 00894 } 00895 00896 /*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ 00897 static HTTPAPI_RESULT ReceiveHeaderFromXIO(HTTP_HANDLE_DATA* http_instance, unsigned int* statusCode) 00898 { 00899 HTTPAPI_RESULT result; 00900 char buf[TEMP_BUFFER_SIZE]; 00901 int ret; 00902 00903 http_instance->is_io_error = 0; 00904 00905 //Receive response 00906 if (readLine(http_instance, buf, TEMP_BUFFER_SIZE) < 0) 00907 { 00908 /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ 00909 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 00910 result = HTTPAPI_READ_DATA_FAILED; 00911 } 00912 //Parse HTTP response 00913 else if (ParseHttpResponse(buf, &ret) != 1) 00914 { 00915 //Cannot match string, error 00916 /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the received message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ 00917 LogInfo("Not a correct HTTP answer"); 00918 result = HTTPAPI_RECEIVE_RESPONSE_FAILED; 00919 } 00920 else 00921 { 00922 /*Codes_SRS_HTTPAPI_COMPACT_21_046: [ The HTTPAPI_ExecuteRequest shall return the http status reported by the host in the received response. ]*/ 00923 /*Codes_SRS_HTTPAPI_COMPACT_21_048: [ If the statusCode is NULL, the HTTPAPI_ExecuteRequest shall report not report any status. ]*/ 00924 if (statusCode) 00925 { 00926 /*Codes_SRS_HTTPAPI_COMPACT_21_047: [ The HTTPAPI_ExecuteRequest shall report the status in the statusCode parameter. ]*/ 00927 *statusCode = ret; 00928 } 00929 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00930 result = HTTPAPI_OK; 00931 } 00932 00933 return result; 00934 } 00935 00936 static HTTPAPI_RESULT ReceiveContentInfoFromXIO(HTTP_HANDLE_DATA* http_instance, HTTP_HEADERS_HANDLE responseHeadersHandle, size_t* bodyLength, bool* chunked) 00937 { 00938 HTTPAPI_RESULT result; 00939 char buf[TEMP_BUFFER_SIZE]; 00940 const char* substr; 00941 char* whereIsColon; 00942 int lengthInMsg; 00943 const char ContentLength[] = "content-length:"; 00944 const size_t ContentLengthSize = sizeof(ContentLength) - 1; 00945 const char TransferEncoding[] = "transfer-encoding:"; 00946 const size_t TransferEncodingSize = sizeof(TransferEncoding) - 1; 00947 const char Chunked[] = "chunked"; 00948 const size_t ChunkedSize = sizeof(Chunked) - 1; 00949 00950 http_instance->is_io_error = 0; 00951 00952 //Read HTTP response headers 00953 if (readLine(http_instance, buf, sizeof(buf)) < 0) 00954 { 00955 /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ 00956 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 00957 result = HTTPAPI_READ_DATA_FAILED; 00958 } 00959 else 00960 { 00961 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 00962 result = HTTPAPI_OK; 00963 00964 while (*buf && (result == HTTPAPI_OK)) 00965 { 00966 if (InternStrnicmp(buf, ContentLength, ContentLengthSize) == 0) 00967 { 00968 substr = buf + ContentLengthSize; 00969 if (ParseStringToDecimal(substr, &lengthInMsg) != 1) 00970 { 00971 /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ 00972 result = HTTPAPI_READ_DATA_FAILED; 00973 } 00974 else 00975 { 00976 (*bodyLength) = (size_t)lengthInMsg; 00977 } 00978 } 00979 else if (InternStrnicmp(buf, TransferEncoding, TransferEncodingSize) == 0) 00980 { 00981 substr = buf + TransferEncodingSize; 00982 00983 while (isspace(*substr)) substr++; 00984 00985 if (InternStrnicmp(substr, Chunked, ChunkedSize) == 0) 00986 { 00987 (*chunked) = true; 00988 } 00989 } 00990 00991 if (result == HTTPAPI_OK) 00992 { 00993 whereIsColon = strchr((char*)buf, ':'); 00994 /*Codes_SRS_HTTPAPI_COMPACT_21_049: [ If responseHeadersHandle is provide, the HTTPAPI_ExecuteRequest shall prepare a Response Header usign the HTTPHeaders_AddHeaderNameValuePair. ]*/ 00995 if (whereIsColon && (responseHeadersHandle != NULL)) 00996 { 00997 *whereIsColon = '\0'; 00998 HTTPHeaders_AddHeaderNameValuePair(responseHeadersHandle, buf, whereIsColon + 1); 00999 } 01000 01001 if (readLine(http_instance, buf, sizeof(buf)) < 0) 01002 { 01003 /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ 01004 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 01005 result = HTTPAPI_READ_DATA_FAILED; 01006 } 01007 } 01008 } 01009 } 01010 01011 return result; 01012 } 01013 01014 static HTTPAPI_RESULT ReadHTTPResponseBodyFromXIO(HTTP_HANDLE_DATA* http_instance, size_t bodyLength, bool chunked, BUFFER_HANDLE responseContent) 01015 { 01016 HTTPAPI_RESULT result; 01017 char buf[TEMP_BUFFER_SIZE]; 01018 const unsigned char* receivedContent; 01019 01020 http_instance->is_io_error = 0; 01021 01022 //Read HTTP response body 01023 if (!chunked) 01024 { 01025 if (bodyLength) 01026 { 01027 if (responseContent != NULL) 01028 { 01029 if (BUFFER_pre_build(responseContent, bodyLength) != 0) 01030 { 01031 /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ 01032 result = HTTPAPI_ALLOC_FAILED; 01033 } 01034 else if (BUFFER_content(responseContent, &receivedContent) != 0) 01035 { 01036 (void)BUFFER_unbuild(responseContent); 01037 01038 /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ 01039 result = HTTPAPI_ALLOC_FAILED; 01040 } 01041 else if (readChunk(http_instance, (char*)receivedContent, bodyLength) < 0) 01042 { 01043 /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ 01044 result = HTTPAPI_READ_DATA_FAILED; 01045 } 01046 else 01047 { 01048 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 01049 result = HTTPAPI_OK; 01050 } 01051 } 01052 else 01053 { 01054 /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ 01055 if (skipN(http_instance, bodyLength) < 0) 01056 { 01057 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 01058 result = HTTPAPI_READ_DATA_FAILED; 01059 } 01060 else 01061 { 01062 result = HTTPAPI_OK; 01063 } 01064 } 01065 } 01066 else 01067 { 01068 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 01069 result = HTTPAPI_OK; 01070 } 01071 } 01072 else 01073 { 01074 size_t size = 0; 01075 /*Codes_SRS_HTTPAPI_COMPACT_21_033: [ If the whole process succeed, the HTTPAPI_ExecuteRequest shall retur HTTPAPI_OK. ]*/ 01076 result = HTTPAPI_OK; 01077 while (result == HTTPAPI_OK) 01078 { 01079 size_t chunkSize; 01080 if (readLine(http_instance, buf, sizeof(buf)) < 0) // read [length in hex]/r/n 01081 { 01082 /*Codes_SRS_HTTPAPI_COMPACT_21_032: [ If the HTTPAPI_ExecuteRequest cannot read the message with the request result, it shall return HTTPAPI_READ_DATA_FAILED. ]*/ 01083 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 01084 result = HTTPAPI_READ_DATA_FAILED; 01085 } 01086 else if (ParseStringToHexadecimal(buf, &chunkSize) != 1) // chunkSize is length of next line (/r/n is not counted) 01087 { 01088 //Cannot match string, error 01089 /*Codes_SRS_HTTPAPI_COMPACT_21_055: [ If the HTTPAPI_ExecuteRequest cannot parser the received message, it shall return HTTPAPI_RECEIVE_RESPONSE_FAILED. ]*/ 01090 result = HTTPAPI_RECEIVE_RESPONSE_FAILED; 01091 } 01092 else if (chunkSize == 0) 01093 { 01094 // 0 length means next line is just '\r\n' and end of chunks 01095 if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 01096 || buf[0] != '\r' || buf[1] != '\n') // skip /r/n 01097 { 01098 (void)BUFFER_unbuild(responseContent); 01099 01100 result = HTTPAPI_READ_DATA_FAILED; 01101 } 01102 break; 01103 } 01104 else 01105 { 01106 if (responseContent != NULL) 01107 { 01108 if (BUFFER_enlarge(responseContent, chunkSize) != 0) 01109 { 01110 (void)BUFFER_unbuild(responseContent); 01111 01112 /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ 01113 result = HTTPAPI_ALLOC_FAILED; 01114 } 01115 else if (BUFFER_content(responseContent, &receivedContent) != 0) 01116 { 01117 (void)BUFFER_unbuild(responseContent); 01118 01119 /*Codes_SRS_HTTPAPI_COMPACT_21_052: [ If any memory allocation get fail, the HTTPAPI_ExecuteRequest shall return HTTPAPI_ALLOC_FAILED. ]*/ 01120 result = HTTPAPI_ALLOC_FAILED; 01121 } 01122 else if (readChunk(http_instance, (char*)receivedContent + size, chunkSize) < 0) 01123 { 01124 result = HTTPAPI_READ_DATA_FAILED; 01125 } 01126 } 01127 else 01128 { 01129 /*Codes_SRS_HTTPAPI_COMPACT_21_051: [ If the responseContent is NULL, the HTTPAPI_ExecuteRequest shall ignore any content in the response. ]*/ 01130 if (skipN(http_instance, chunkSize) < 0) 01131 { 01132 /*Codes_SRS_HTTPAPI_COMPACT_21_082: [ If the HTTPAPI_ExecuteRequest retries 20 seconds to receive the message without success, it shall fail and return HTTPAPI_READ_DATA_FAILED. ]*/ 01133 result = HTTPAPI_READ_DATA_FAILED; 01134 } 01135 } 01136 01137 if (result == HTTPAPI_OK) 01138 { 01139 if (readChunk(http_instance, (char*)buf, (size_t)2) < 0 01140 || buf[0] != '\r' || buf[1] != '\n') // skip /r/n 01141 { 01142 result = HTTPAPI_READ_DATA_FAILED; 01143 } 01144 size += chunkSize; 01145 } 01146 } 01147 } 01148 01149 } 01150 return result; 01151 } 01152 01153 01154 /*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ 01155 static bool validRequestType(HTTPAPI_REQUEST_TYPE requestType) 01156 { 01157 bool result; 01158 01159 if ((requestType == HTTPAPI_REQUEST_GET) || 01160 (requestType == HTTPAPI_REQUEST_POST) || 01161 (requestType == HTTPAPI_REQUEST_PUT) || 01162 (requestType == HTTPAPI_REQUEST_DELETE) || 01163 (requestType == HTTPAPI_REQUEST_PATCH) || 01164 (requestType == HTTPAPI_REQUEST_HEAD)) 01165 { 01166 result = true; 01167 } 01168 else 01169 { 01170 result = false; 01171 } 01172 01173 return result; 01174 } 01175 01176 /*Codes_SRS_HTTPAPI_COMPACT_21_021: [ The HTTPAPI_ExecuteRequest shall execute the http communtication with the provided host, sending a request and reciving the response. ]*/ 01177 /*Codes_SRS_HTTPAPI_COMPACT_21_050: [ If there is a content in the response, the HTTPAPI_ExecuteRequest shall copy it in the responseContent buffer. ]*/ 01178 //Note: This function assumes that "Host:" and "Content-Length:" headers are setup 01179 // by the caller of HTTPAPI_ExecuteRequest() (which is true for httptransport.c). 01180 HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, 01181 HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content, 01182 size_t contentLength, unsigned int* statusCode, 01183 HTTP_HEADERS_HANDLE responseHeadersHandle, BUFFER_HANDLE responseContent) 01184 { 01185 HTTPAPI_RESULT result = HTTPAPI_ERROR; 01186 size_t headersCount; 01187 size_t bodyLength = 0; 01188 bool chunked = false; 01189 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; 01190 01191 /*Codes_SRS_HTTPAPI_COMPACT_21_034: [ If there is no previous connection, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ 01192 /*Codes_SRS_HTTPAPI_COMPACT_21_037: [ If the request type is unknown, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ 01193 /*Codes_SRS_HTTPAPI_COMPACT_21_039: [ If the relativePath is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ 01194 /*Codes_SRS_HTTPAPI_COMPACT_21_041: [ If the httpHeadersHandle is NULL or invalid, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ 01195 /*Codes_SRS_HTTPAPI_COMPACT_21_053: [ The HTTPAPI_ExecuteRequest shall produce a set of http header to send to the host. ]*/ 01196 /*Codes_SRS_HTTPAPI_COMPACT_21_040: [ The request shall contain the http header provided in httpHeadersHandle parameter. ]*/ 01197 /*Codes_SRS_HTTPAPI_COMPACT_21_054: [ If Http header maker cannot provide the number of headers, the HTTPAPI_ExecuteRequest shall return HTTPAPI_INVALID_ARG. ]*/ 01198 if (http_instance == NULL || 01199 relativePath == NULL || 01200 httpHeadersHandle == NULL || 01201 !validRequestType(requestType) || 01202 HTTPHeaders_GetHeaderCount(httpHeadersHandle, &headersCount) != HTTP_HEADERS_OK) 01203 { 01204 result = HTTPAPI_INVALID_ARG; 01205 LogError("(result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01206 } 01207 /*Codes_SRS_HTTPAPI_COMPACT_21_024: [ The HTTPAPI_ExecuteRequest shall open the transport connection with the host to send the request. ]*/ 01208 else if ((result = OpenXIOConnection(http_instance)) != HTTPAPI_OK) 01209 { 01210 LogError("Open HTTP connection failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01211 } 01212 /*Codes_SRS_HTTPAPI_COMPACT_21_026: [ If the open process succeed, the HTTPAPI_ExecuteRequest shall send the request message to the host. ]*/ 01213 else if ((result = SendHeadsToXIO(http_instance, requestType, relativePath, httpHeadersHandle, headersCount)) != HTTPAPI_OK) 01214 { 01215 LogError("Send heads to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01216 } 01217 /*Codes_SRS_HTTPAPI_COMPACT_21_042: [ The request can contain the a content message, provided in content parameter. ]*/ 01218 else if ((result = SendContentToXIO(http_instance, content, contentLength)) != HTTPAPI_OK) 01219 { 01220 LogError("Send content to HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01221 } 01222 /*Codes_SRS_HTTPAPI_COMPACT_21_030: [ At the end of the transmission, the HTTPAPI_ExecuteRequest shall receive the response from the host. ]*/ 01223 /*Codes_SRS_HTTPAPI_COMPACT_21_073: [ The message received by the HTTPAPI_ExecuteRequest shall starts with a valid header. ]*/ 01224 else if ((result = ReceiveHeaderFromXIO(http_instance, statusCode)) != HTTPAPI_OK) 01225 { 01226 LogError("Receive header from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01227 } 01228 /*Codes_SRS_HTTPAPI_COMPACT_21_074: [ After the header, the message received by the HTTPAPI_ExecuteRequest can contain addition information about the content. ]*/ 01229 else if ((result = ReceiveContentInfoFromXIO(http_instance, responseHeadersHandle, &bodyLength, &chunked)) != HTTPAPI_OK) 01230 { 01231 LogError("Receive content information from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01232 } 01233 /*Codes_SRS_HTTPAPI_COMPACT_42_084: [ The message received by the HTTPAPI_ExecuteRequest should not contain http body. ]*/ 01234 else if (requestType != HTTPAPI_REQUEST_HEAD) 01235 { 01236 /*Codes_SRS_HTTPAPI_COMPACT_21_075: [ The message received by the HTTPAPI_ExecuteRequest can contain a body with the message content. ]*/ 01237 if ((result = ReadHTTPResponseBodyFromXIO(http_instance, bodyLength, chunked, responseContent)) != HTTPAPI_OK) 01238 { 01239 LogError("Read HTTP response body from HTTP failed (result = %s)", ENUM_TO_STRING(HTTPAPI_RESULT, result)); 01240 } 01241 } 01242 01243 conn_receive_discard_buffer(http_instance); 01244 01245 return result; 01246 } 01247 01248 /*Codes_SRS_HTTPAPI_COMPACT_21_056: [ The HTTPAPI_SetOption shall change the HTTP options. ]*/ 01249 /*Codes_SRS_HTTPAPI_COMPACT_21_057: [ The HTTPAPI_SetOption shall receive a handle that identiry the HTTP connection. ]*/ 01250 /*Codes_SRS_HTTPAPI_COMPACT_21_058: [ The HTTPAPI_SetOption shall receive the option as a pair optionName/value. ]*/ 01251 HTTPAPI_RESULT HTTPAPI_SetOption(HTTP_HANDLE handle, const char* optionName, const void* value) 01252 { 01253 HTTPAPI_RESULT result; 01254 HTTP_HANDLE_DATA* http_instance = (HTTP_HANDLE_DATA*)handle; 01255 01256 if ( 01257 (http_instance == NULL) || 01258 (optionName == NULL) || 01259 (value == NULL) 01260 ) 01261 { 01262 /*Codes_SRS_HTTPAPI_COMPACT_21_059: [ If the handle is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ 01263 /*Codes_SRS_HTTPAPI_COMPACT_21_060: [ If the optionName is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ 01264 /*Codes_SRS_HTTPAPI_COMPACT_21_061: [ If the value is NULL, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ 01265 result = HTTPAPI_INVALID_ARG; 01266 } 01267 else if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0) 01268 { 01269 #ifdef DO_NOT_COPY_TRUSTED_CERTS_STRING 01270 result = HTTPAPI_OK; 01271 http_instance->certificate = (char*)value; 01272 #else 01273 int len; 01274 01275 if (http_instance->certificate) 01276 { 01277 free(http_instance->certificate); 01278 } 01279 01280 len = (int)strlen((char*)value); 01281 http_instance->certificate = (char*)malloc((len + 1) * sizeof(char)); 01282 if (http_instance->certificate == NULL) 01283 { 01284 /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ 01285 result = HTTPAPI_ALLOC_FAILED; 01286 LogInfo("unable to allocate memory for the certificate in HTTPAPI_SetOption"); 01287 } 01288 else 01289 { 01290 /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ 01291 (void)strcpy(http_instance->certificate, (const char*)value); 01292 result = HTTPAPI_OK; 01293 } 01294 #endif // DO_NOT_COPY_TRUSTED_CERTS_STRING 01295 } 01296 else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0) 01297 { 01298 int len; 01299 if (http_instance->x509ClientCertificate) 01300 { 01301 free(http_instance->x509ClientCertificate); 01302 } 01303 01304 len = (int)strlen((char*)value); 01305 http_instance->x509ClientCertificate = (char*)malloc((len + 1) * sizeof(char)); 01306 if (http_instance->x509ClientCertificate == NULL) 01307 { 01308 /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ 01309 result = HTTPAPI_ALLOC_FAILED; 01310 LogInfo("unable to allocate memory for the client certificate in HTTPAPI_SetOption"); 01311 } 01312 else 01313 { 01314 /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ 01315 (void)strcpy(http_instance->x509ClientCertificate, (const char*)value); 01316 result = HTTPAPI_OK; 01317 } 01318 } 01319 else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) 01320 { 01321 int len; 01322 if (http_instance->x509ClientPrivateKey) 01323 { 01324 free(http_instance->x509ClientPrivateKey); 01325 } 01326 01327 len = (int)strlen((char*)value); 01328 http_instance->x509ClientPrivateKey = (char*)malloc((len + 1) * sizeof(char)); 01329 if (http_instance->x509ClientPrivateKey == NULL) 01330 { 01331 /*Codes_SRS_HTTPAPI_COMPACT_21_062: [ If any memory allocation get fail, the HTTPAPI_SetOption shall return HTTPAPI_ALLOC_FAILED. ]*/ 01332 result = HTTPAPI_ALLOC_FAILED; 01333 LogInfo("unable to allocate memory for the client private key in HTTPAPI_SetOption"); 01334 } 01335 else 01336 { 01337 /*Codes_SRS_HTTPAPI_COMPACT_21_064: [ If the HTTPAPI_SetOption get success setting the option, it shall return HTTPAPI_OK. ]*/ 01338 (void)strcpy(http_instance->x509ClientPrivateKey, (const char*)value); 01339 result = HTTPAPI_OK; 01340 } 01341 } 01342 else 01343 { 01344 /*Codes_SRS_HTTPAPI_COMPACT_21_063: [ If the HTTP do not support the optionName, the HTTPAPI_SetOption shall return HTTPAPI_INVALID_ARG. ]*/ 01345 result = HTTPAPI_INVALID_ARG; 01346 LogInfo("unknown option %s", optionName); 01347 } 01348 return result; 01349 } 01350 01351 /*Codes_SRS_HTTPAPI_COMPACT_21_065: [ The HTTPAPI_CloneOption shall provide the means to clone the HTTP option. ]*/ 01352 /*Codes_SRS_HTTPAPI_COMPACT_21_066: [ The HTTPAPI_CloneOption shall return a clone of the value identified by the optionName. ]*/ 01353 HTTPAPI_RESULT HTTPAPI_CloneOption(const char* optionName, const void* value, const void** savedValue) 01354 { 01355 HTTPAPI_RESULT result; 01356 size_t certLen; 01357 char* tempCert; 01358 01359 if ( 01360 (optionName == NULL) || 01361 (value == NULL) || 01362 (savedValue == NULL) 01363 ) 01364 { 01365 /*Codes_SRS_HTTPAPI_COMPACT_21_067: [ If the optionName is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ 01366 /*Codes_SRS_HTTPAPI_COMPACT_21_068: [ If the value is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ 01367 /*Codes_SRS_HTTPAPI_COMPACT_21_069: [ If the savedValue is NULL, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ 01368 result = HTTPAPI_INVALID_ARG; 01369 } 01370 else if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0) 01371 { 01372 #ifdef DO_NOT_COPY_TRUSTED_CERTS_STRING 01373 *savedValue = (const void*)value; 01374 result = HTTPAPI_OK; 01375 #else 01376 certLen = strlen((const char*)value); 01377 tempCert = (char*)malloc((certLen + 1) * sizeof(char)); 01378 if (tempCert == NULL) 01379 { 01380 /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ 01381 result = HTTPAPI_ALLOC_FAILED; 01382 } 01383 else 01384 { 01385 /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ 01386 (void)strcpy(tempCert, (const char*)value); 01387 *savedValue = tempCert; 01388 result = HTTPAPI_OK; 01389 } 01390 #endif // DO_NOT_COPY_TRUSTED_CERTS_STRING 01391 } 01392 else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0) 01393 { 01394 certLen = strlen((const char*)value); 01395 tempCert = (char*)malloc((certLen + 1) * sizeof(char)); 01396 if (tempCert == NULL) 01397 { 01398 /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ 01399 result = HTTPAPI_ALLOC_FAILED; 01400 } 01401 else 01402 { 01403 /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ 01404 (void)strcpy(tempCert, (const char*)value); 01405 *savedValue = tempCert; 01406 result = HTTPAPI_OK; 01407 } 01408 } 01409 else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0) 01410 { 01411 certLen = strlen((const char*)value); 01412 tempCert = (char*)malloc((certLen + 1) * sizeof(char)); 01413 if (tempCert == NULL) 01414 { 01415 /*Codes_SRS_HTTPAPI_COMPACT_21_070: [ If any memory allocation get fail, the HTTPAPI_CloneOption shall return HTTPAPI_ALLOC_FAILED. ]*/ 01416 result = HTTPAPI_ALLOC_FAILED; 01417 } 01418 else 01419 { 01420 /*Codes_SRS_HTTPAPI_COMPACT_21_072: [ If the HTTPAPI_CloneOption get success setting the option, it shall return HTTPAPI_OK. ]*/ 01421 (void)strcpy(tempCert, (const char*)value); 01422 *savedValue = tempCert; 01423 result = HTTPAPI_OK; 01424 } 01425 } 01426 else 01427 { 01428 /*Codes_SRS_HTTPAPI_COMPACT_21_071: [ If the HTTP do not support the optionName, the HTTPAPI_CloneOption shall return HTTPAPI_INVALID_ARG. ]*/ 01429 result = HTTPAPI_INVALID_ARG; 01430 LogInfo("unknown option %s", optionName); 01431 } 01432 return result; 01433 }
Generated on Wed Jul 13 2022 23:38:02 by
1.7.2