Azure IoT / azure_c_shared_utility

Dependents:   STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers httpapi_compact.c Source File

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 }