]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/standard/https/src/iot_https_client.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / standard / https / src / iot_https_client.c
1 /*\r
2  * Amazon FreeRTOS HTTPS Client V1.1.0\r
3  * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 \r
26 /**\r
27  * @file iot_https_client.c\r
28  * @brief Implementation of the user-facing functions of the Amazon FreeRTOS HTTPS Client library.\r
29  */\r
30 \r
31 /* The config header is always included first. */\r
32 #include "iot_config.h"\r
33 \r
34 /* HTTPS Client library private includes. */\r
35 #include "private/iot_https_internal.h"\r
36 \r
37 /*-----------------------------------------------------------*/\r
38 \r
39 /**\r
40  * @brief Partial HTTPS request first line.\r
41  *\r
42  * This is used for the calculation of the requestUserBufferMinimumSize.\r
43  * The minimum path is "/" because we cannot know how long the application requested path is is going to be.\r
44  * CONNECT is the longest string length HTTP method according to RFC 2616.\r
45  */\r
46 #define HTTPS_PARTIAL_REQUEST_LINE                        HTTPS_CONNECT_METHOD " " HTTPS_EMPTY_PATH " " HTTPS_PROTOCOL_VERSION\r
47 \r
48 /**\r
49  * @brief The User-Agent header line string.\r
50  *\r
51  * This is of the form:\r
52  * "User-Agent: <configured-user-agent>\r\n"\r
53  * This is used for the calculation of the requestUserBufferMinimumSize.\r
54  */\r
55 #define HTTPS_USER_AGENT_HEADER_LINE                      HTTPS_USER_AGENT_HEADER HTTPS_HEADER_FIELD_SEPARATOR IOT_HTTPS_USER_AGENT HTTPS_END_OF_HEADER_LINES_INDICATOR\r
56 \r
57 /**\r
58  * @brief The Host header line with the field only and not the value.\r
59  *\r
60  * This is of the form:\r
61  * "Host: \r\n"\r
62  * This is used for the calculation of the requestUserBufferMinimumSize. The Host value is not specified because we\r
63  * cannot anticipate what server the client is making requests to.\r
64  */\r
65 #define HTTPS_PARTIAL_HOST_HEADER_LINE                    HTTPS_HOST_HEADER HTTPS_HEADER_FIELD_SEPARATOR HTTPS_END_OF_HEADER_LINES_INDICATOR\r
66 \r
67 /**\r
68  * String constants for the Connection header and possible values.\r
69  *\r
70  * This is used for writing headers automatically during the sending of the HTTP request.\r
71  * "Connection: keep-alive\r\n" is written automatically for a persistent connection.\r
72  * "Connection: close\r\n" is written automatically for a non-persistent connection.\r
73  */\r
74 #define HTTPS_CONNECTION_KEEP_ALIVE_HEADER_LINE           HTTPS_CONNECTION_HEADER HTTPS_HEADER_FIELD_SEPARATOR HTTPS_CONNECTION_KEEP_ALIVE_HEADER_VALUE HTTPS_END_OF_HEADER_LINES_INDICATOR /**< @brief String literal for "Connection: keep-alive\r\n". */\r
75 #define HTTPS_CONNECTION_CLOSE_HEADER_LINE                HTTPS_CONNECTION_HEADER HTTPS_HEADER_FIELD_SEPARATOR HTTPS_CONNECTION_CLOSE_HEADER_VALUE HTTPS_END_OF_HEADER_LINES_INDICATOR      /**< @brief String literal for "Connection: close\r\n". */\r
76 \r
77 /**\r
78  * @brief The length of the "Connection: keep-alive\r\n" header.\r
79  *\r
80  * This is used for sizing a local buffer for the final headers to send that include the "Connection: keep-alive\r\n"\r
81  * header line.\r
82  *\r
83  * This is used to initialize a local array for the final headers to send.\r
84  */\r
85 #define HTTPS_CONNECTION_KEEP_ALIVE_HEADER_LINE_LENGTH    ( 24 )\r
86 \r
87 /**\r
88  * Indicates for the http-parser parsing execution function to tell it to keep parsing or to stop parsing.\r
89  *\r
90  * A value of 0 means the parser should keep parsing if there is more unparsed length.\r
91  * A value greater than 0 tells the parser to stop parsing.\r
92  */\r
93 #define KEEP_PARSING                                      ( ( int ) 0 ) /**< @brief Indicator in the http-parser callback to keep parsing when the function returns. */\r
94 #define STOP_PARSING                                      ( ( int ) 1 ) /**< @brief Indicator in the http-parser callback to stop parsing when the function returns. */\r
95 \r
96 /*-----------------------------------------------------------*/\r
97 \r
98 /**\r
99  * @brief Minimum size of the request user buffer.\r
100  *\r
101  * The request user buffer is configured in IotHttpsClientRequestInfo_t.userBuffer. This buffer stores the internal context\r
102  * of the request and then the request headers right after. The minimum size for the buffer is the total size of the\r
103  * internal request context, the HTTP formatted request line, the User-Agent header line, and the part of the Host\r
104  * header line.\r
105  */\r
106 const uint32_t requestUserBufferMinimumSize = sizeof( _httpsRequest_t ) +\r
107                                               sizeof( HTTPS_PARTIAL_REQUEST_LINE ) +\r
108                                               sizeof( HTTPS_USER_AGENT_HEADER_LINE ) +\r
109                                               sizeof( HTTPS_PARTIAL_HOST_HEADER_LINE );\r
110 \r
111 /**\r
112  * @brief Minimum size of the response user buffer.\r
113  *\r
114  * The response user buffer is configured in IotHttpsClientRequestInfo_t.userBuffer. This buffer stores the internal context\r
115  * of the response and then the response headers right after. This minimum size is calculated for the case if no bytes\r
116  * from the HTTP response headers are to be stored.\r
117  */\r
118 const uint32_t responseUserBufferMinimumSize = sizeof( _httpsResponse_t );\r
119 \r
120 /**\r
121  * @brief Minimum size of the connection user buffer.\r
122  *\r
123  * The connection user buffer is configured in IotHttpsConnectionInfo_t.userBuffer. This buffer stores the internal context of the\r
124  * connection.\r
125  */\r
126 const uint32_t connectionUserBufferMinimumSize = sizeof( _httpsConnection_t );\r
127 \r
128 /*-----------------------------------------------------------*/\r
129 \r
130 /**\r
131  * @brief Callback from http-parser to indicate the start of the HTTP response message is reached.\r
132  *\r
133  * See https://github.com/nodejs/http-parser for more information.\r
134  *\r
135  * @param[in] pHttpParser - http-parser state structure.\r
136  *\r
137  * @return 0 to tell http-parser to keep parsing.\r
138  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_message_begin.\r
139  */\r
140 static int _httpParserOnMessageBeginCallback( http_parser * pHttpParser );\r
141 \r
142 /**\r
143  * @brief Callback from http-parser to indicate it found the HTTP response status code.\r
144  *\r
145  * See https://github.com/nodejs/http-parser for more information.\r
146  *\r
147  * @param[in] pHttpParser - http-parser state structure.\r
148  * @param[in] pLoc - Pointer to the HTTP response status code string in the response message buffer.\r
149  * @param[in] length - The length of the HTTP response status code string.\r
150  *\r
151  * @return 0 to tell http-parser to keep parsing.\r
152  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_status.\r
153  */\r
154 static int _httpParserOnStatusCallback( http_parser * pHttpParser,\r
155                                         const char * pLoc,\r
156                                         size_t length );\r
157 \r
158 /**\r
159  * @brief Callback from http-parser to indicate it found an HTTP response header field.\r
160  *\r
161  * If only part of the header field was returned here in this callback, then this callback will be invoked again the\r
162  * next time the parser executes on the next part of the header field.\r
163  *\r
164  * See https://github.com/nodejs/http-parser for more information.\r
165  *\r
166  * @param[in] pHttpParser - http-parser state structure.\r
167  * @param[in] pLoc - Pointer to the header field string in the response message buffer.\r
168  * @param[in] length - The length of the header field.\r
169  *\r
170  * @return 0 to tell http-parser to keep parsing.\r
171  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_header_field.\r
172  */\r
173 static int _httpParserOnHeaderFieldCallback( http_parser * pHttpParser,\r
174                                              const char * pLoc,\r
175                                              size_t length );\r
176 \r
177 /**\r
178  * @brief Callback from http-parser to indicate it found an HTTP response header value.\r
179  *\r
180  * This value corresponds to the field that was found in the _httpParserOnHeaderFieldCallback() called immediately\r
181  * before this callback was called.\r
182  *\r
183  * If only part of the header value was returned here in this callback, then this callback will be invoked again the\r
184  * next time the parser executes on the next part of the header value.\r
185  *\r
186  * See https://github.com/nodejs/http-parser for more information.\r
187  *\r
188  * @param[in] pHttpParser - http-parser state structure.\r
189  * @param[in] pLoc - Pointer to the header value string in the response message buffer.\r
190  * @param[in] length - The length of the header value.\r
191  *\r
192  * @return 0 to tell http-parser to keep parsing.\r
193  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_header_value.\r
194  */\r
195 static int _httpParserOnHeaderValueCallback( http_parser * pHttpParser,\r
196                                              const char * pLoc,\r
197                                              size_t length );\r
198 \r
199 /**\r
200  * @brief Callback from http-parser to indicate it reached the end of the headers in the HTTP response message.\r
201  *\r
202  * The end of the headers is signalled in a HTTP response message by another "\r\n" after the final header line.\r
203  *\r
204  * See https://github.com/nodejs/http-parser for more information.\r
205  *\r
206  * @param[in] pHttpParser - http-parser state structure.\r
207  *\r
208  * @return 0 to tell http-parser to keep parsing.\r
209  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_headers_complete.\r
210  */\r
211 static int _httpParserOnHeadersCompleteCallback( http_parser * pHttpParser );\r
212 \r
213 /**\r
214  * @brief Callback from http-parser to indicate it found HTTP response body.\r
215  *\r
216  * This callback will be invoked multiple times if the response body is of "Transfer-Encoding: chunked".\r
217  * _httpParserOnChunkHeaderCallback() will be invoked first, then _httpParserOnBodyCallback(), then\r
218  * _httpParserOnChunkCompleteCallback(), then repeated back to _httpParserOnChunkHeaderCallback() if there are more\r
219  * "chunks".\r
220  *\r
221  * See https://github.com/nodejs/http-parser for more information.\r
222  *\r
223  * @param[in] pHttpParser - http-parser state structure.\r
224  * @param[in] pLoc - Pointer to the body string in the response message buffer.\r
225  * @param[in] length - The length of the body found.\r
226  *\r
227  * @return 0 to tell http-parser to keep parsing.\r
228  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_body.\r
229  */\r
230 static int _httpParserOnBodyCallback( http_parser * pHttpParser,\r
231                                       const char * pLoc,\r
232                                       size_t length );\r
233 \r
234 /**\r
235  * @brief Callback from http-parser to indicate it reached the end of the HTTP response message.\r
236  *\r
237  * The end of the message is signalled in a HTTP response message by another "\r\n" after the final header line, with no\r
238  * entity body; or it is signaled by "\r\n" at the end of the entity body.\r
239  *\r
240  * For a Transfer-Encoding: chunked type of response message, the end of the message is signalled by a terminating\r
241  * chunk header with length zero.\r
242  *\r
243  * See https://github.com/nodejs/http-parser for more information.\r
244  *\r
245  * @param[in] pHttpParser - http-parser state structure.\r
246  *\r
247  * @return 0 to tell http-parser to keep parsing.\r
248  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_message_complete.\r
249  */\r
250 static int _httpParserOnMessageCompleteCallback( http_parser * pHttpParser );\r
251 \r
252 /* This code prints debugging information and is, therefore, compiled only when\r
253  * log level is set to IOT_LOG_DEBUG. */\r
254 #if ( LIBRARY_LOG_LEVEL == IOT_LOG_DEBUG )\r
255 \r
256 /**\r
257  * @brief Callback from http-parser to indicate it found an HTTP Transfer-Encoding: chunked header.\r
258  *\r
259  * Transfer-Encoding: chunked headers are embedded in the HTTP response entity body by a "\r\n" followed by the size of\r
260  * the chunk followed by another "\r\n".\r
261  *\r
262  * If only part of the header field was returned here in this callback, then this callback will be invoked again the\r
263  * next time the parser executes on the next part of the header field.\r
264  *\r
265  * See https://github.com/nodejs/http-parser for more information.\r
266  *\r
267  * @param[in] pHttpParser - http-parser state structure.\r
268  *\r
269  * @return 0 to tell http-parser to keep parsing.\r
270  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_chunk_header.\r
271  */\r
272     static int _httpParserOnChunkHeaderCallback( http_parser * pHttpParser );\r
273 \r
274 /**\r
275  * @brief Callback from http-parser to indicate it reached the end of an HTTP response message "chunk".\r
276  *\r
277  * A chunk is complete when the chunk header size is read fully in the body.\r
278  *\r
279  * See https://github.com/nodejs/http-parser for more information.\r
280  *\r
281  * @param[in] pHttpParser - http-parser state structure.\r
282  *\r
283  * @return 0 to tell http-parser to keep parsing.\r
284  *         1 to tell http-parser that parsing should stop return from http_parser_execute with error HPE_CB_chunk_complete.\r
285  */\r
286     static int _httpParserOnChunkCompleteCallback( http_parser * pHttpParser );\r
287 #endif\r
288 \r
289 /**\r
290  * @brief Network receive callback for the HTTPS Client library.\r
291  *\r
292  * This function is called by the network layer whenever data is available for the HTTP library.\r
293  *\r
294  * @param[in] pNetworkConnection - The network connection with the HTTPS connection, passed by the network stack.\r
295  * @param[in] pReceiveContext - A pointer to the HTTPS Client connection handle for which the packet was received.\r
296  */\r
297 static void _networkReceiveCallback( void * pNetworkConnection,\r
298                                      void * pReceiveContext );\r
299 \r
300 /**\r
301  * @brief Connects to HTTPS server and initializes the connection context.\r
302  *\r
303  * @param[out] pConnHandle - The out parameter to return handle representing the open connection.\r
304  * @param[in] pConnInfo - The connection configuration.\r
305  *\r
306  * @return #IOT_HTTPS_OK if the connection context initialization was successful.\r
307  *         #IOT_HTTPS_CONNECTION_ERROR if the connection failed.\r
308  *         #IOT_HTTPS_INTERNAL_ERROR if the context initialization failed.\r
309  */\r
310 static IotHttpsReturnCode_t _createHttpsConnection( IotHttpsConnectionHandle_t * pConnHandle,\r
311                                                     IotHttpsConnectionInfo_t * pConnInfo );\r
312 \r
313 /**\r
314  * @brief Disconnects from the network.\r
315  *\r
316  * @param[in] pHttpsConnection - HTTPS connection handle.\r
317  */\r
318 static void _networkDisconnect( _httpsConnection_t * pHttpsConnection );\r
319 \r
320 /**\r
321  * @brief Destroys the network connection.\r
322  *\r
323  * @param[in] pHttpsConnection - HTTPS connection handle.\r
324  */\r
325 static void _networkDestroy( _httpsConnection_t * pHttpsConnection );\r
326 \r
327 /**\r
328  * @brief Add a header to the current HTTP request.\r
329  *\r
330  * The headers are stored in reqHandle->pHeaders.\r
331  *\r
332  * @param[in] pHttpsRequest - HTTP request context.\r
333  * @param[in] pName - The name of the header to add.\r
334  * @param[in] nameLen - The length of the header name string.\r
335  * @param[in] pValue - The buffer containing the value string.\r
336  * @param[in] valueLen - The length of the header value string.\r
337  *\r
338  * @return #IOT_HTTPS_OK if the header was added to the request successfully.\r
339  *         #IOT_HTTPS_INSUFFICIENT_MEMORY if there was not enough room in the IotHttpsRequestHandle_t->pHeaders.\r
340  */\r
341 static IotHttpsReturnCode_t _addHeader( _httpsRequest_t * pHttpsRequest,\r
342                                         const char * pName,\r
343                                         uint32_t nameLen,\r
344                                         const char * pValue,\r
345                                         uint32_t valueLen );\r
346 \r
347 /**\r
348  * @brief Send data on the network.\r
349  *\r
350  * @param[in] pHttpsConnection - HTTP connection context.\r
351  * @param[in] pBuf - The buffer containing the data to send.\r
352  * @param[in] len - The length of the data to send.\r
353  *\r
354  * @return #IOT_HTTPS_OK if the data sent successfully.\r
355  *         #IOT_HTTPS_NETWORK_ERROR if there was an error sending the data on the network.\r
356  */\r
357 static IotHttpsReturnCode_t _networkSend( _httpsConnection_t * pHttpsConnection,\r
358                                           uint8_t * pBuf,\r
359                                           size_t len );\r
360 \r
361 /**\r
362  * @brief Receive data on the network.\r
363  *\r
364  * @param[in] pHttpsConnection - HTTP connection context.\r
365  * @param[in] pBuf - The buffer to receive the data into.\r
366  * @param[in] bufLen - The length of the data to receive.\r
367  * @param[in] numBytesRecv - The number of bytes read from the network.\r
368  *\r
369  * @return #IOT_HTTPS_OK if the data was received successfully.\r
370  *         #IOT_HTTPS_NETWORK_ERROR if we timedout trying to receive data from the network.\r
371  */\r
372 static IotHttpsReturnCode_t _networkRecv( _httpsConnection_t * pHttpsConnection,\r
373                                           uint8_t * pBuf,\r
374                                           size_t bufLen,\r
375                                           size_t * numBytesRecv );\r
376 \r
377 /**\r
378  * @brief Send all of the HTTP request headers in the pHeadersBuf and the final Content-Length and Connection headers.\r
379  *\r
380  * All of the headers in headerbuf are sent first followed by the computed content length and persistent connection\r
381  * indication.\r
382  *\r
383  * @param[in] pHttpsConnection - HTTP connection context.\r
384  * @param[in] pHeadersBuf - The buffer containing the request headers to send. This buffer must contain HTTP headers\r
385  *            lines without the indicator for the the end of the HTTP headers.\r
386  * @param[in] headersLength - The length of the request headers to send.\r
387  * @param[in] isNonPersistent - Indicator of whether the connection is persistent or not.\r
388  * @param[in] contentLength - The length of the request body used for automatically creating a "Content-Length" header.\r
389  *\r
390  * @return #IOT_HTTPS_OK if the headers were fully sent successfully.\r
391  *         #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
392  */\r
393 static IotHttpsReturnCode_t _sendHttpsHeaders( _httpsConnection_t * pHttpsConnection,\r
394                                                uint8_t * pHeadersBuf,\r
395                                                uint32_t headersLength,\r
396                                                bool isNonPersistent,\r
397                                                uint32_t contentLength );\r
398 \r
399 /**\r
400  * @brief Send all of the HTTP request body in pBodyBuf.\r
401  *\r
402  * @param[in] pHttpsConnection - HTTP connection context.\r
403  * @param[in] pBodyBuf - Buffer of the request body to send.\r
404  * @param[in] bodyLength - The length of the body to send.\r
405  *\r
406  * @return #IOT_HTTPS_OK if the body was fully sent successfully.\r
407  *         #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
408  */\r
409 static IotHttpsReturnCode_t _sendHttpsBody( _httpsConnection_t * pHttpsConnection,\r
410                                             uint8_t * pBodyBuf,\r
411                                             uint32_t bodyLength );\r
412 \r
413 /**\r
414  * @brief Parse the HTTP response message in pBuf.\r
415  *\r
416  * @param[in] pHttpParserInfo - Pointer to the information containing the instance of the http-parser and the execution function.\r
417  * @param[in] pBuf - The buffer containing the data to parse.\r
418  * @param[in] len - The length of data to parse.\r
419  *\r
420  * @return #IOT_HTTPS_OK if the data was parsed successfully.\r
421  *         #IOT_HTTPS_PARSING_ERROR if there was an error with parsing the data.\r
422  */\r
423 static IotHttpsReturnCode_t _parseHttpsMessage( _httpParserInfo_t * pHttpParserInfo,\r
424                                                 char * pBuf,\r
425                                                 size_t len );\r
426 \r
427 /**\r
428  * @brief Receive any part of an HTTP response.\r
429  *\r
430  * This function is used for both receiving the body into the body buffer and receiving the header into the header\r
431  * buffer.\r
432  *\r
433  * @param[in] pHttpsConnection - HTTP Connection context.\r
434  * @param[in] pParser - Pointer to the instance of the http-parser.\r
435  * @param[in] pCurrentParserState - The current state of what has been parsed in the HTTP response.\r
436  * @param[in] finalParserState - The final state of the parser expected after this function finishes.\r
437  * @param[in] currentBufferProcessingState - The current buffer that is the HTTPS message is being received into.\r
438  * @param[in] pBufCur - Pointer to the next location to write data into the buffer pBuf. This is a double pointer to update the response context buffer pointers.\r
439  * @param[in] pBufEnd - Pointer to the end of the buffer to receive the HTTP response into.\r
440  *\r
441  * @return #IOT_HTTPS_OK if we received the HTTP response message part successfully.\r
442  *         #IOT_HTTPS_PARSING_ERROR if there was an error with parsing the data.\r
443  *         #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
444  */\r
445 static IotHttpsReturnCode_t _receiveHttpsMessage( _httpsConnection_t * pHttpsConnection,\r
446                                                   _httpParserInfo_t * pParser,\r
447                                                   IotHttpsResponseParserState_t * pCurrentParserState,\r
448                                                   IotHttpsResponseParserState_t finalParserState,\r
449                                                   IotHttpsResponseBufferState_t currentBufferProcessingState,\r
450                                                   uint8_t ** pBufCur,\r
451                                                   uint8_t ** pBufEnd );\r
452 \r
453 /**\r
454  * @brief Receive the HTTP response headers.\r
455  *\r
456  * Receiving the response headers is always the first step in receiving the response, therefore the\r
457  * pHttpsResponse->httpParserInfo will be initialized to a starting state when this function is called.\r
458  *\r
459  * This function also sets internal states to indicate that the header buffer is being processed now for a new response.\r
460  *\r
461  * @param[in] pHttpsConnection - HTTP connection context.\r
462  * @param[in] pHttpsResponse - HTTP response context.\r
463  *\r
464  * @return #IOT_HTTPS_OK if we received the HTTP headers successfully.\r
465  *         #IOT_HTTPS_PARSING_ERROR if there was an error with parsing the header buffer.\r
466  *         #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
467  */\r
468 static IotHttpsReturnCode_t _receiveHttpsHeaders( _httpsConnection_t * pHttpsConnection,\r
469                                                   _httpsResponse_t * pHttpsResponse );\r
470 \r
471 /**\r
472  * @brief Receive the HTTP response body.\r
473  *\r
474  * Sets internal states to indicate that the the body buffer is being processed now for a new response.\r
475  *\r
476  * @param[in] pHttpsConnection - HTTP connection context.\r
477  * @param[in] pHttpsResponse - HTTP response context.\r
478  *\r
479  * @return #IOT_HTTPS_OK if we received the HTTP body successfully.\r
480  *         #IOT_HTTPS_PARSING_ERROR if there was an error with parsing the body buffer.\r
481  *         #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
482  */\r
483 static IotHttpsReturnCode_t _receiveHttpsBody( _httpsConnection_t * pHttpsConnection,\r
484                                                _httpsResponse_t * pHttpsResponse );\r
485 \r
486 /**\r
487  * @brief Read the rest of any HTTP response that may be on the network.\r
488  *\r
489  * This reads the rest of any left over response data that might still be on the network buffers. We do not want this\r
490  * data left over because it will spill into the header and body buffers of next response that we try to receive.\r
491  *\r
492  * If we performed a request without a body and the headers received exceeds the size of the\r
493  * pHttpsResponse->pHeaders buffer, then we need to flush the network buffer.\r
494  *\r
495  * If the application configured the body buffer as null in IotHttpsResponseInfo_t.syncInfo.respData and the server\r
496  * sends body in the response, but it exceeds the size of  pHttpsResponse->pHeaders buffer, then we need to flush the\r
497  * network buffer.\r
498  *\r
499  * If the amount of body received on the network does not fit into a non-null IotHttpsResponseInfo_t.syncInfo.respData,\r
500  * then we need to flush the network buffer.\r
501  *\r
502  * If an asynchronous request cancels in the middle of a response process, after already sending the request message,\r
503  * then we need to flush the network buffer.\r
504  *\r
505  * @param[in] pHttpsConnection - HTTP connection context.\r
506  * @param[in] pHttpsResponse - HTTP response context.\r
507  *\r
508  * @return #IOT_HTTPS_OK if we successfully flushed the network data.\r
509  *         #IOT_HTTPS_PARSING_ERROR if there was an error with parsing the data.\r
510  *         #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
511  */\r
512 static IotHttpsReturnCode_t _flushHttpsNetworkData( _httpsConnection_t * pHttpsConnection,\r
513                                                     _httpsResponse_t * pHttpsResponse );\r
514 \r
515 /**\r
516  * @brief Task pool job routine to send the HTTP request within the pUserContext.\r
517  *\r
518  * @param[in] pTaskPool Pointer to the system task pool.\r
519  * @param[in] pJob Pointer the to the HTTP request sending job.\r
520  * @param[in] pUserContext Pointer to an HTTP request, passed as an opaque context.\r
521  */\r
522 static void _sendHttpsRequest( IotTaskPool_t pTaskPool,\r
523                                IotTaskPoolJob_t pJob,\r
524                                void * pUserContext );\r
525 \r
526 \r
527 /**\r
528  * @brief Receive the HTTPS body specific to an asynchronous type of response.\r
529  *\r
530  * @param[in] pHttpsResponse - HTTP response context.\r
531  *\r
532  * @return  #IOT_HTTPS_OK - If the the response body was received with no issues.\r
533  *          #IOT_HTTPS_RECEIVE_ABORT - If the request was cancelled by the Application\r
534  *          #IOT_HTTPS_PARSING_ERROR - If there was an issue parsing the HTTP response body.\r
535  *          #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
536  */\r
537 static IotHttpsReturnCode_t _receiveHttpsBodyAsync( _httpsResponse_t * pHttpsResponse );\r
538 \r
539 /**\r
540  * @brief Receive the HTTPS body specific to a synchronous type of response.\r
541  *\r
542  * @param[in] pHttpsResponse - HTTP response context.\r
543  *\r
544  * @return  #IOT_HTTPS_OK - If the the response body was received with no issues.\r
545  *          #IOT_HTTPS_MESSAGE_TOO_LARGE - If the body from the network is too large to fit into the configured body buffer.\r
546  *          #IOT_HTTPS_PARSING_ERROR - If there was an issue parsing the HTTP response body.\r
547  *          #IOT_HTTPS_NETWORK_ERROR if there was an error receiving the data on the network.\r
548  */\r
549 static IotHttpsReturnCode_t _receiveHttpsBodySync( _httpsResponse_t * pHttpsResponse );\r
550 \r
551 /**\r
552  * @brief Schedule the task to send the the HTTP request.\r
553  *\r
554  * @param[in] pHttpsRequest - HTTP request context.\r
555  *\r
556  * @return  #IOT_HTTPS_OK - If the task to send the HTTP request was successfully scheduled.\r
557  *          #IOT_HTTPS_INTERNAL_ERROR - If a taskpool job could not be created.\r
558  *          #IOT_HTTPS_ASYNC_SCHEDULING_ERROR - If there was an error scheduling the job.\r
559  */\r
560 IotHttpsReturnCode_t _scheduleHttpsRequestSend( _httpsRequest_t * pHttpsRequest );\r
561 \r
562 /**\r
563  * @brief Add the request to the connection's request queue.\r
564  *\r
565  * This will schedule a task if the request is first and only request in the queue.\r
566  *\r
567  * @param[in] pHttpsRequest - HTTP request context.\r
568  *\r
569  * @return  #IOT_HTTPS_OK - If the request was successfully added to the connection's request queue.\r
570  *          #IOT_HTTPS_INTERNAL_ERROR - If a taskpool job could not be created.\r
571  *          #IOT_HTTPS_ASYNC_SCHEDULING_ERROR - If there was an error scheduling the job.\r
572  */\r
573 IotHttpsReturnCode_t _addRequestToConnectionReqQ( _httpsRequest_t * pHttpsRequest );\r
574 \r
575 /**\r
576  * @brief Cancel the HTTP request's processing.\r
577  *\r
578  * pHttpsRequest->cancelled will be checked and the request cancelled if specified so at the following intervals:\r
579  *  - Before sending the HTTPS headers at the start of the scheduled sending of the HTTPS request.\r
580  *  - After Sending the HTTPS headers.\r
581  *  - After Sending the HTTPS body.\r
582  *\r
583  * @param[in] pHttpsRequest - HTTP request context.\r
584  */\r
585 static void _cancelRequest( _httpsRequest_t * pHttpsRequest );\r
586 \r
587 /**\r
588  * @brief Cancel the HTTP response's processing.\r
589  *\r
590  * pHttpsResponse->cancelled will be checked and the response cancelled if specified so at the following intervals:\r
591  *  - At the start of the network receive callback.\r
592  *  - After receiving the HTTPS headers.\r
593  *  - After Receiving the HTTPS body.\r
594  *\r
595  * @param[in] pHttpsResponse - HTTP response context.\r
596  */\r
597 static void _cancelResponse( _httpsResponse_t * pHttpsResponse );\r
598 \r
599 /**\r
600  * @brief Initialize the input pHttpsResponse with pRespInfo.\r
601  *\r
602  * @param[in] pRespHandle - Non-null HTTP response context.\r
603  * @param[in] pRespInfo - Response configuration information.\r
604  * @param[in] pHttpsRequest - HTTP request to grab async information, persistence, and method from.\r
605  */\r
606 static IotHttpsReturnCode_t _initializeResponse( IotHttpsResponseHandle_t * pRespHandle,\r
607                                                  IotHttpsResponseInfo_t * pRespInfo,\r
608                                                  _httpsRequest_t * pHttpsRequest );\r
609 \r
610 /**\r
611  * @brief Increment the pointer stored in pBufCur depending on the character found in there.\r
612  *\r
613  * This function increments the pHeadersCur pointer further if the message ended with a header line delimiter.\r
614  *\r
615  * @param[in] pBufCur - Pointer to the next location to write data into the buffer pBuf. This is a double pointer to update the response context buffer pointers.\r
616  * @param[in] pBufEnd - Pointer to the end of the buffer to receive the HTTP response into.\r
617  */\r
618 static void _incrementNextLocationToWriteBeyondParsed( uint8_t ** pBufCur,\r
619                                                        uint8_t ** pBufEnd );\r
620 \r
621 /**\r
622  * @brief Send the HTTPS headers and body referenced in pHttpsRequest.\r
623  *\r
624  * Sends both the headers and body over the network.\r
625  *\r
626  * @param[in] pHttpsConnection - HTTPS connection context.\r
627  * @param[in] pHttpsRequest - HTTPS request context.\r
628  */\r
629 static IotHttpsReturnCode_t _sendHttpsHeadersAndBody( _httpsConnection_t * pHttpsConnection,\r
630                                                       _httpsRequest_t * pHttpsRequest );\r
631 \r
632 /*-----------------------------------------------------------*/\r
633 \r
634 /**\r
635  * @brief Definition of the http-parser settings.\r
636  *\r
637  * The http_parser_settings holds all of the callbacks invoked by the http-parser.\r
638  */\r
639 static http_parser_settings _httpParserSettings = { 0 };\r
640 \r
641 /*-----------------------------------------------------------*/\r
642 \r
643 static int _httpParserOnMessageBeginCallback( http_parser * pHttpParser )\r
644 {\r
645     int retVal = KEEP_PARSING;\r
646 \r
647     IotLogDebug( "Parser: Start of HTTPS Response message." );\r
648 \r
649     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
650     /* Set the state of the parser. The headers are at the start of the message always. */\r
651     pHttpsResponse->parserState = PARSER_STATE_IN_HEADERS;\r
652     return retVal;\r
653 }\r
654 \r
655 /*-----------------------------------------------------------*/\r
656 \r
657 static int _httpParserOnStatusCallback( http_parser * pHttpParser,\r
658                                         const char * pLoc,\r
659                                         size_t length )\r
660 {\r
661     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
662 \r
663     IotLogDebug( "Parser: Status %.*s retrieved from HTTPS response.", length, pLoc );\r
664 \r
665     /* Save the status code so it can be retrieved with IotHttpsClient_ReadResponseStatus(). */\r
666     pHttpsResponse->status = ( uint16_t ) ( pHttpParser->status_code );\r
667 \r
668     /* If we are parsing the network data received in the header buffer then we\r
669      * increment pHttpsResponse->pHeadersCur. The status line in the response is\r
670      * part of the data stored in header buffer _httpResponse->pHeaders. */\r
671     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_FILLING_HEADER_BUFFER )\r
672     {\r
673         /* pHeadersCur will never exceed the pHeadersEnd here because PROCESSING_STATE_FILLING_HEADER_BUFFER\r
674          * indicates we are currently in the header buffer and the total size of the header buffer is passed\r
675          * into http_parser_execute() as the maximum length to parse. */\r
676         pHttpsResponse->pHeadersCur = ( uint8_t * ) ( pLoc += length );\r
677     }\r
678 \r
679     return KEEP_PARSING;\r
680 }\r
681 \r
682 /*-----------------------------------------------------------*/\r
683 \r
684 static int _httpParserOnHeaderFieldCallback( http_parser * pHttpParser,\r
685                                              const char * pLoc,\r
686                                              size_t length )\r
687 {\r
688     IotLogDebug( "Parser: HTTPS header field parsed %.*s", length, pLoc );\r
689 \r
690     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
691 \r
692     /* If we are parsing the network data received in the header buffer then we can increment\r
693      * pHttpsResponse->pHeadersCur. */\r
694     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_FILLING_HEADER_BUFFER )\r
695     {\r
696         pHttpsResponse->pHeadersCur = ( uint8_t * ) ( pLoc += length );\r
697     }\r
698 \r
699     /* If the IotHttpsClient_ReadHeader() was called, then we check for the header field of interest. */\r
700     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_SEARCHING_HEADER_BUFFER )\r
701     {\r
702         if( pHttpsResponse->readHeaderFieldLength != length )\r
703         {\r
704             pHttpsResponse->foundHeaderField = false;\r
705         }\r
706         else if( strncmp( pHttpsResponse->pReadHeaderField, pLoc, length ) == 0 )\r
707         {\r
708             pHttpsResponse->foundHeaderField = true;\r
709         }\r
710     }\r
711 \r
712     return KEEP_PARSING;\r
713 }\r
714 \r
715 /*-----------------------------------------------------------*/\r
716 \r
717 static int _httpParserOnHeaderValueCallback( http_parser * pHttpParser,\r
718                                              const char * pLoc,\r
719                                              size_t length )\r
720 {\r
721     int retVal = KEEP_PARSING;\r
722 \r
723     IotLogDebug( "Parser: HTTPS header value parsed %.*s", length, pLoc );\r
724     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
725 \r
726     /* If we are parsing the network data received in the header buffer then we can increment\r
727      * pHttpsResponse->pHeadersCur. */\r
728     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_FILLING_HEADER_BUFFER )\r
729     {\r
730         pHttpsResponse->pHeadersCur = ( uint8_t * ) ( pLoc += length );\r
731     }\r
732 \r
733     /* If the IotHttpsClient_ReadHeader() was called, then we check if we found the header field of interest. */\r
734     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_SEARCHING_HEADER_BUFFER )\r
735     {\r
736         if( pHttpsResponse->foundHeaderField )\r
737         {\r
738             pHttpsResponse->pReadHeaderValue = ( char * ) ( pLoc );\r
739             pHttpsResponse->readHeaderValueLength = length;\r
740             /* We found a header field so we don't want to keep parsing.*/\r
741             retVal = STOP_PARSING;\r
742         }\r
743     }\r
744 \r
745     return retVal;\r
746 }\r
747 \r
748 /*-----------------------------------------------------------*/\r
749 \r
750 static int _httpParserOnHeadersCompleteCallback( http_parser * pHttpParser )\r
751 {\r
752     IotLogDebug( "Parser: End of the headers reached." );\r
753 \r
754     int retVal = KEEP_PARSING;\r
755     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
756     pHttpsResponse->parserState = PARSER_STATE_HEADERS_COMPLETE;\r
757 \r
758     /* If the IotHttpsClient_ReadHeader() was called, we return after finishing looking through all of the headers.\r
759      * Returning a non-zero value exits the http parsing. */\r
760     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_SEARCHING_HEADER_BUFFER )\r
761     {\r
762         retVal = STOP_PARSING;\r
763     }\r
764 \r
765     /* When in this callback the pHeaderCur pointer is at the first "\r" in the last header line. HTTP/1.1\r
766      * headers end with another "\r\n" at the end of the last line. This means we must increment\r
767      * the headerCur pointer to the length of "\r\n\r\n". */\r
768     if( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_FILLING_HEADER_BUFFER )\r
769     {\r
770         pHttpsResponse->pHeadersCur += ( 2 * HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH );\r
771     }\r
772 \r
773     /* This if-case is not incrementing any pHeaderCur pointers, so this case is safe to call when flushing the\r
774      * network buffer. Flushing the network buffer needs the logic below to reach PARSER_STATE_BODY_COMPLETE if the\r
775      * response is for a HEAD request. Before flushing the network buffer the bufferProcessingState is set to\r
776      * PROCESSING_STATE_FINISHED so that other callback functions don't update header or body current pointers in the\r
777      * response context. We don't want those pointers incremented because flushing the network uses a different buffer\r
778      * to receive the rest of the response. */\r
779     if( pHttpsResponse->bufferProcessingState <= PROCESSING_STATE_FINISHED )\r
780     {\r
781         /* For a HEAD method, there is no body expected in the response, so we return 1 to skip body parsing. */\r
782         if( ( pHttpsResponse->method == IOT_HTTPS_METHOD_HEAD ) )\r
783         {\r
784             retVal = STOP_PARSING;\r
785 \r
786             /* Since the message is considered complete now for a HEAD response, then we set the parser state\r
787              * to the completed state. */\r
788             pHttpsResponse->parserState = PARSER_STATE_BODY_COMPLETE;\r
789         }\r
790 \r
791         /* If this is NOT a HEAD method and there is body configured, but the server does not send a body in the\r
792          * response, then the body buffer will be filled with the zeros from rest of the header buffer. http-parser\r
793          * will invoke the on_body callback and consider the zeros following the headers as body. */\r
794 \r
795         /* If there is not body configured for a synchronous reponse, we do not stop the parser from continueing. */\r
796 \r
797         /* Skipping the body will cause the parser to invoke the _httpParserOnMessageComplete() callback. This is\r
798          * not desired when there is actually HTTP response body sent by the server because this will set the parser\r
799          * state to PARSER_STATE_BODY_COMPLETE. If this state is set then the rest of possible body will not be\r
800          * flushed out. The network flush looks for the state being PARSER_STATE_BODY_COMPLETE to finish flushing. */\r
801     }\r
802 \r
803     return retVal;\r
804 }\r
805 \r
806 /*-----------------------------------------------------------*/\r
807 \r
808 static int _httpParserOnBodyCallback( http_parser * pHttpParser,\r
809                                       const char * pLoc,\r
810                                       size_t length )\r
811 {\r
812     IotLogDebug( "Parser: Reached the HTTPS message body. It is of length: %d", length );\r
813 \r
814     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
815     pHttpsResponse->parserState = PARSER_STATE_IN_BODY;\r
816 \r
817     /* If the header buffer is currently being processed, but HTTP response body was found, then for an asynchronous\r
818      * request this if-case saves where the body is located. In the asynchronous case, the body buffer is not available\r
819      * until the readReadyCallback is invoked, which happens after the headers are processed.  */\r
820     if( ( pHttpsResponse->bufferProcessingState == PROCESSING_STATE_FILLING_HEADER_BUFFER ) && ( pHttpsResponse->isAsync ) )\r
821     {\r
822         /* For an asynchronous response, the buffer to store the body will be available after the headers\r
823          * are read first. We may receive part of the body in the header buffer. We will want to leave this here\r
824          * and copy it over when the body buffer is available in the _readReadyCallback().\r
825          */\r
826         if( pHttpsResponse->pBodyInHeaderBuf == NULL )\r
827         {\r
828             pHttpsResponse->pBodyInHeaderBuf = ( uint8_t * ) ( pLoc );\r
829             pHttpsResponse->pBodyCurInHeaderBuf = pHttpsResponse->pBodyInHeaderBuf;\r
830         }\r
831 \r
832         /* If there is a chunk encoded body in the header buffer, we will want to overwrite the chunk headers with the\r
833          * actual body. This is so that when the application calls IotHttpsClient_ReadResponseBody(), in the\r
834          * readReadyCallback(), we can pass the body into the body buffer provided right away. */\r
835         if( pHttpsResponse->pBodyCurInHeaderBuf != ( uint8_t * ) pLoc )\r
836         {\r
837             memcpy( pHttpsResponse->pBodyCurInHeaderBuf, pLoc, length );\r
838         }\r
839 \r
840         pHttpsResponse->pBodyCurInHeaderBuf += length;\r
841     }\r
842     else if( pHttpsResponse->bufferProcessingState < PROCESSING_STATE_FINISHED )\r
843     {\r
844         /* Has the user provided a buffer and is it large enough to fit the body? The\r
845          * case of body buffer not being large enough can happen if the body was received\r
846          * in the header buffer and the body buffer can not fit in all the body. */\r
847         if( ( pHttpsResponse->pBodyCur != NULL ) && ( pHttpsResponse->pBodyEnd - pHttpsResponse->pBodyCur > 0 ) )\r
848         {\r
849             /* There are two scenarios when we need to copy data around:\r
850              * 1. Some or all of the response body may have been received in the header\r
851              * buffer. If that is the case, we copy the response body received in the\r
852              * header buffer to the user provided body buffer.\r
853              * 2. When we receive chunked header, the actual body is separated in\r
854              * multiple chunks which are preceeded by length. For example, a chunked\r
855              * body may look like:\r
856              *\r
857              * 7\r\n\r
858              * Mozilla\r\n\r
859              * 9\r\n\r
860              * Developer\r\n\r
861              * 7\r\n\r
862              * Network\r\n\r
863              * 0\r\n\r
864              * \r\n\r
865              *\r
866              * In this case, we want the parsed body buffer to contain actual body only\r
867              * (MozillaDeveloperNetwork in the above example).\r
868              */\r
869 \r
870             /* If the response body found by the parser (pLoc) is not equal to the\r
871              * current writable location in the body buffer (_httpsResponse->pBodyCur),\r
872              * it indicates that:\r
873              * - Either the data is in the header buffer and needs to be copied into the\r
874              * body buffer.\r
875              * - Or it is a chunked response and the data needs to be moved up in the\r
876              * body buffer. */\r
877             if( ( pHttpsResponse->pBodyCur + length ) <= pHttpsResponse->pBodyEnd )\r
878             {\r
879                 if( pHttpsResponse->pBodyCur != ( uint8_t * ) pLoc )\r
880                 {\r
881                     memcpy( pHttpsResponse->pBodyCur, pLoc, length );\r
882                 }\r
883 \r
884                 pHttpsResponse->pBodyCur += length;\r
885             }\r
886         }\r
887     }\r
888 \r
889     return KEEP_PARSING;\r
890 }\r
891 \r
892 /*-----------------------------------------------------------*/\r
893 \r
894 static int _httpParserOnMessageCompleteCallback( http_parser * pHttpParser )\r
895 {\r
896     IotLogDebug( "Parser: End of the HTTPS message reached." );\r
897 \r
898     _httpsResponse_t * pHttpsResponse = ( _httpsResponse_t * ) ( pHttpParser->data );\r
899     pHttpsResponse->parserState = PARSER_STATE_BODY_COMPLETE;\r
900 \r
901     /* This callback is invoked when the complete HTTP response has been received.\r
902      * We tell the parser to parse the whole body buffer as opposed to the size of\r
903      * the response body. For example, if the size of the body buffer is 1000 but\r
904      * the size of the actual body is 500, we tell the parser to parse the whole\r
905      * buffer of length 1000. We do zero out the buffer in the beginning so that all\r
906      * the buffer after the actual body contains zeros. We return greater than zero to stop parsing\r
907      * since the end of the HTTP message has been reached. Any data beyond the end of the message is\r
908      * ignored. */\r
909     return STOP_PARSING;\r
910 }\r
911 \r
912 /*-----------------------------------------------------------*/\r
913 \r
914 /* This code prints debugging information and is, therefore, compiled only when\r
915  * log level is set to IOT_LOG_DEBUG. */\r
916 #if ( LIBRARY_LOG_LEVEL == IOT_LOG_DEBUG )\r
917     static int _httpParserOnChunkHeaderCallback( http_parser * pHttpParser )\r
918     {\r
919         ( void ) pHttpParser;\r
920         IotLogDebug( "Parser: HTTPS message Chunked encoding header callback." );\r
921         IotLogDebug( "Parser: HTTPS message Chunk size: %d", pHttpParser->content_length );\r
922         return 0;\r
923     }\r
924 \r
925 /*-----------------------------------------------------------*/\r
926 \r
927     static int _httpParserOnChunkCompleteCallback( http_parser * pHttpParser )\r
928     {\r
929         ( void ) pHttpParser;\r
930         IotLogDebug( "End of a HTTPS message Chunk complete callback." );\r
931         return 0;\r
932     }\r
933 #endif /* if ( LIBRARY_LOG_LEVEL == IOT_LOG_DEBUG ) */\r
934 \r
935 /*-----------------------------------------------------------*/\r
936 \r
937 static IotHttpsReturnCode_t _receiveHttpsBodyAsync( _httpsResponse_t * pHttpsResponse )\r
938 {\r
939     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
940 \r
941     if( pHttpsResponse->pCallbacks->readReadyCallback )\r
942     {\r
943         /* If there is still more body that has not been passed back to the user, then this callback is invoked again. */\r
944         do\r
945         {\r
946             IotLogDebug( "Invoking the readReadyCallback." );\r
947             pHttpsResponse->pCallbacks->readReadyCallback( pHttpsResponse->pUserPrivData,\r
948                                                            pHttpsResponse,\r
949                                                            pHttpsResponse->bodyRxStatus,\r
950                                                            pHttpsResponse->status );\r
951 \r
952             if( pHttpsResponse->cancelled == true )\r
953             {\r
954                 IotLogDebug( "Cancelled HTTP response %d.", pHttpsResponse );\r
955                 status = IOT_HTTPS_RECEIVE_ABORT;\r
956 \r
957                 /* We break out of the loop and do not goto clean up because we want to print debugging logs for\r
958                  * the parser state and the networks status. */\r
959                 break;\r
960             }\r
961         } while( ( pHttpsResponse->parserState < PARSER_STATE_BODY_COMPLETE ) && ( pHttpsResponse->bodyRxStatus == IOT_HTTPS_OK ) );\r
962 \r
963         if( HTTPS_FAILED( pHttpsResponse->bodyRxStatus ) )\r
964         {\r
965             IotLogError( "Error receiving the HTTP response body for response %d. Error code: %d",\r
966                          pHttpsResponse,\r
967                          pHttpsResponse->bodyRxStatus );\r
968             /* An error in the network or the parser takes precedence  */\r
969             status = pHttpsResponse->bodyRxStatus;\r
970         }\r
971 \r
972         if( pHttpsResponse->parserState < PARSER_STATE_BODY_COMPLETE )\r
973         {\r
974             IotLogDebug( "Did not receive all of the HTTP response body for response %d.",\r
975                          pHttpsResponse );\r
976         }\r
977     }\r
978 \r
979     /* This GOTO cleanup is here for compiler warnings about using HTTPS_FUNCTION_EXIT_NO_CLEANUP() without a\r
980      * corresponding goto. */\r
981     HTTPS_GOTO_CLEANUP();\r
982     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
983 }\r
984 \r
985 /*-----------------------------------------------------------*/\r
986 \r
987 static IotHttpsReturnCode_t _receiveHttpsBodySync( _httpsResponse_t * pHttpsResponse )\r
988 {\r
989     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
990     _httpsConnection_t * pHttpsConnection = pHttpsResponse->pHttpsConnection;\r
991 \r
992     /* The header buffer is now filled or the end of the headers has been reached already. If part of the response\r
993      *  body was read from the network into the header buffer, then it was already copied to the body buffer in the\r
994      *  _httpParserOnBodyCallback(). */\r
995     if( pHttpsResponse->pBody != NULL )\r
996     {\r
997         /* If there is room left in the body buffer and we have not received the whole response body,\r
998          * then try to receive more. */\r
999         if( ( ( pHttpsResponse->pBodyEnd - pHttpsResponse->pBodyCur ) > 0 ) &&\r
1000             ( pHttpsResponse->parserState < PARSER_STATE_BODY_COMPLETE ) )\r
1001         {\r
1002             status = _receiveHttpsBody( pHttpsConnection,\r
1003                                         pHttpsResponse );\r
1004 \r
1005             if( HTTPS_FAILED( status ) )\r
1006             {\r
1007                 IotLogError( "Error receiving the HTTPS response body for response %d. Error code: %d.",\r
1008                              pHttpsResponse,\r
1009                              status );\r
1010                 HTTPS_GOTO_CLEANUP();\r
1011             }\r
1012         }\r
1013         else\r
1014         {\r
1015             IotLogDebug( "Received the maximum amount of HTTP body when filling the header buffer for response %d.",\r
1016                          pHttpsResponse );\r
1017         }\r
1018 \r
1019         /* If we don't reach the end of the HTTPS body in the parser, then we only received part of the body.\r
1020          *  The rest of body will be on the network socket. */\r
1021         if( HTTPS_SUCCEEDED( status ) && ( pHttpsResponse->parserState < PARSER_STATE_BODY_COMPLETE ) )\r
1022         {\r
1023             IotLogError( "HTTPS response body does not fit into application provided response buffer at location 0x%x "\r
1024                          "with length: %d",\r
1025                          pHttpsResponse->pBody,\r
1026                          pHttpsResponse->pBodyEnd - pHttpsResponse->pBody );\r
1027             HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_MESSAGE_TOO_LARGE );\r
1028         }\r
1029     }\r
1030     else\r
1031     {\r
1032         IotLogDebug( "No response body was configure for response %d.", pHttpsResponse );\r
1033     }\r
1034 \r
1035     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1036 }\r
1037 \r
1038 /*-----------------------------------------------------------*/\r
1039 \r
1040 static void _networkReceiveCallback( void * pNetworkConnection,\r
1041                                      void * pReceiveContext )\r
1042 {\r
1043     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1044 \r
1045     IotHttpsReturnCode_t flushStatus = IOT_HTTPS_OK;\r
1046     IotHttpsReturnCode_t disconnectStatus = IOT_HTTPS_OK;\r
1047     IotHttpsReturnCode_t scheduleStatus = IOT_HTTPS_OK;\r
1048     _httpsConnection_t * pHttpsConnection = ( _httpsConnection_t * ) pReceiveContext;\r
1049     _httpsResponse_t * pCurrentHttpsResponse = NULL;\r
1050     _httpsRequest_t * pNextHttpsRequest = NULL;\r
1051     IotLink_t * pQItem = NULL;\r
1052     bool fatalDisconnect = false;\r
1053 \r
1054     /* The network connection is already in the connection context. */\r
1055     ( void ) pNetworkConnection;\r
1056 \r
1057     /* Get the response from the response queue. */\r
1058     IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
1059     pQItem = IotDeQueue_PeekHead( &( pHttpsConnection->respQ ) );\r
1060     IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
1061 \r
1062     /* If the receive callback is invoked and there is no response expected, then this a violation of the HTTP/1.1\r
1063      * protocol. */\r
1064     if( pQItem == NULL )\r
1065     {\r
1066         IotLogError( "Received data on the network, when no response was expected..." );\r
1067         fatalDisconnect = true;\r
1068         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_NETWORK_ERROR );\r
1069     }\r
1070 \r
1071     /* Set the current HTTP response context to use. */\r
1072     pCurrentHttpsResponse = IotLink_Container( _httpsResponse_t, pQItem, link );\r
1073 \r
1074     /* If the receive callback has invoked, but the request associated with this response has not finished sending\r
1075      * to the server, then this is a violation of the HTTP/1.1 protocol.  */\r
1076     if( pCurrentHttpsResponse->reqFinishedSending == false )\r
1077     {\r
1078         IotLogError( "Received response data on the network when the request was not finished sending. This is unexpected." );\r
1079         fatalDisconnect = true;\r
1080         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_NETWORK_ERROR );\r
1081     }\r
1082 \r
1083     /* If the current response was cancelled, then don't bother receiving the headers and body. */\r
1084     if( pCurrentHttpsResponse->cancelled )\r
1085     {\r
1086         IotLogDebug( "Response ID: %d was cancelled.", pCurrentHttpsResponse );\r
1087         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_RECEIVE_ABORT );\r
1088     }\r
1089 \r
1090     /* Reset the http-parser state to an initial state. This is done so that a new response can be parsed from the\r
1091      * beginning. */\r
1092     pCurrentHttpsResponse->parserState = PARSER_STATE_NONE;\r
1093 \r
1094     /* Receive the response from the network. */\r
1095     /* Receive the headers first. */\r
1096     status = _receiveHttpsHeaders( pHttpsConnection, pCurrentHttpsResponse );\r
1097 \r
1098     if( HTTPS_FAILED( status ) )\r
1099     {\r
1100         if( status == IOT_HTTPS_PARSING_ERROR )\r
1101         {\r
1102             /* There was an error parsing the HTTPS response body. This may be an indication of a server that does\r
1103              *  not adhere to protocol correctly. We should disconnect. */\r
1104             IotLogError( "Failed to parse the HTTPS headers for response %d, Error code: %d.",\r
1105                          pCurrentHttpsResponse,\r
1106                          status );\r
1107             fatalDisconnect = true;\r
1108         }\r
1109         else if( status == IOT_HTTPS_NETWORK_ERROR )\r
1110         {\r
1111             /* Given the function signature of IotNetworkInterface_t.receive, we can only receive 0 to the number of bytes\r
1112              * requested. Receiving less than the number of bytes requests is OK since we do not how much data is expected, so\r
1113              * we ask for the full size of the receive buffer. Therefore, the only error that can be returned from receiving\r
1114              * the headers or body is a timeout. We always disconnect from the network when there is a timeout because the\r
1115              * server may be slow to respond. If the server happens to send the response later at the same time another response\r
1116              * is waiting in the queue, then the workflow is corrupted. Pipelining is not current supported in this library. */\r
1117             IotLogError( "Network error receiving the HTTPS headers for response %d. Error code: %d",\r
1118                          pCurrentHttpsResponse,\r
1119                          status );\r
1120             fatalDisconnect = true;\r
1121         }\r
1122         else /* Any other error. */\r
1123         {\r
1124             IotLogError( "Failed to retrive the HTTPS body for response %d. Error code: %d", pCurrentHttpsResponse, status );\r
1125         }\r
1126 \r
1127         HTTPS_GOTO_CLEANUP();\r
1128     }\r
1129 \r
1130     /* Check if we received all of the headers into the header buffer. */\r
1131     if( pCurrentHttpsResponse->parserState < PARSER_STATE_HEADERS_COMPLETE )\r
1132     {\r
1133         IotLogDebug( "Headers received on the network did not all fit into the configured header buffer for response %d."\r
1134                      " The length of the headers buffer is: %d",\r
1135                      pCurrentHttpsResponse,\r
1136                      pCurrentHttpsResponse->pHeadersEnd - pCurrentHttpsResponse->pHeaders );\r
1137         /* It is not error if the headers did not all fit into the buffer. */\r
1138     }\r
1139 \r
1140     /* Receive the body. */\r
1141     if( pCurrentHttpsResponse->isAsync )\r
1142     {\r
1143         status = _receiveHttpsBodyAsync( pCurrentHttpsResponse );\r
1144     }\r
1145     else\r
1146     {\r
1147         /* Otherwise receive synchronously. */\r
1148         status = _receiveHttpsBodySync( pCurrentHttpsResponse );\r
1149     }\r
1150 \r
1151     if( HTTPS_FAILED( status ) )\r
1152     {\r
1153         if( status == IOT_HTTPS_RECEIVE_ABORT )\r
1154         {\r
1155             /* If the request was cancelled, this is logged, but does not close the connection. */\r
1156             IotLogDebug( "User cancelled during the async readReadyCallback() for response %d.",\r
1157                          pCurrentHttpsResponse );\r
1158         }\r
1159         else if( status == IOT_HTTPS_PARSING_ERROR )\r
1160         {\r
1161             /* There was an error parsing the HTTPS response body. This may be an indication of a server that does\r
1162              *  not adhere to protocol correctly. We should disconnect. */\r
1163             IotLogError( "Failed to parse the HTTPS body for response %d, Error code: %d.",\r
1164                          pCurrentHttpsResponse,\r
1165                          status );\r
1166             fatalDisconnect = true;\r
1167         }\r
1168         else if( status == IOT_HTTPS_NETWORK_ERROR )\r
1169         {\r
1170             /* We always disconnect for a network error because failure to receive the HTTPS body will result in a\r
1171              * corruption of the workflow. */\r
1172             IotLogError( "Network error receiving the HTTPS body for response %d. Error code: %d",\r
1173                          pCurrentHttpsResponse,\r
1174                          status );\r
1175             fatalDisconnect = true;\r
1176         }\r
1177         else /* Any other error. */\r
1178         {\r
1179             IotLogError( "Failed to retrive the HTTPS body for response %d. Error code: %d", pCurrentHttpsResponse, status );\r
1180         }\r
1181 \r
1182         HTTPS_GOTO_CLEANUP();\r
1183     }\r
1184 \r
1185     IOT_FUNCTION_CLEANUP_BEGIN();\r
1186 \r
1187     /* Disconnect and return in the event of an out-of-order response. If a response is received out of order\r
1188      * pCurrentHttpsResponse will be NULL because there will be no response in the connection's response queue.\r
1189      * If a response is received out of order that is an indication of a rogue server. */\r
1190     if( fatalDisconnect && !pCurrentHttpsResponse )\r
1191     {\r
1192         IotLogError( "An out-of-order response was received. The connection will be disconnected." );\r
1193         disconnectStatus = IotHttpsClient_Disconnect( pHttpsConnection );\r
1194 \r
1195         if( HTTPS_FAILED( disconnectStatus ) )\r
1196         {\r
1197             IotLogWarn( "Failed to disconnect after an out of order response. Error code: %d.", disconnectStatus );\r
1198         }\r
1199 \r
1200         /* In this case this routine returns immediately after to avoid further uses of pCurrentHttpsResponse. */\r
1201         return;\r
1202     }\r
1203 \r
1204     /* Report errors back to the application. */\r
1205     if( HTTPS_FAILED( status ) )\r
1206     {\r
1207         if( pCurrentHttpsResponse->isAsync && pCurrentHttpsResponse->pCallbacks->errorCallback )\r
1208         {\r
1209             pCurrentHttpsResponse->pCallbacks->errorCallback( pCurrentHttpsResponse->pUserPrivData, NULL, pCurrentHttpsResponse, status );\r
1210         }\r
1211 \r
1212         pCurrentHttpsResponse->syncStatus = status;\r
1213     }\r
1214 \r
1215     /* If this is not a persistent request, the server would have closed it after sending a response, but we\r
1216      * disconnect anyways. If we are disconnecting there is is no point in wasting time\r
1217      * flushing the network. If the network is being disconnected we also do not schedule any pending requests. */\r
1218     if( fatalDisconnect || pCurrentHttpsResponse->isNonPersistent )\r
1219     {\r
1220         IotLogDebug( "Disconnecting response %d.", pCurrentHttpsResponse );\r
1221         disconnectStatus = IotHttpsClient_Disconnect( pHttpsConnection );\r
1222 \r
1223         if( ( pCurrentHttpsResponse != NULL ) && pCurrentHttpsResponse->isAsync && pCurrentHttpsResponse->pCallbacks->connectionClosedCallback )\r
1224         {\r
1225             pCurrentHttpsResponse->pCallbacks->connectionClosedCallback( pCurrentHttpsResponse->pUserPrivData, pHttpsConnection, disconnectStatus );\r
1226         }\r
1227 \r
1228         if( HTTPS_FAILED( disconnectStatus ) )\r
1229         {\r
1230             IotLogWarn( "Failed to disconnect response %d. Error code: %d.", pCurrentHttpsResponse, disconnectStatus );\r
1231         }\r
1232 \r
1233         /* If we disconnect, we do not process anymore requests. */\r
1234     }\r
1235     else\r
1236     {\r
1237         /* Set the processing state of the buffer to finished for completeness. This is also to prevent the parsing of the flush\r
1238          * data from incrementing any pointer in the HTTP response context. */\r
1239         pCurrentHttpsResponse->bufferProcessingState = PROCESSING_STATE_FINISHED;\r
1240 \r
1241         /* Flush the socket of the rest of the data if there is data left from this response. We need to do this\r
1242          * so that for the next request on this connection, there is not left over response from this request in\r
1243          * the next response buffer.\r
1244          *\r
1245          * If a continuous stream of data is coming in from the connection, with an unknown end, we may not be able to\r
1246          * flush the network data. It may sit here forever. A continuous stream should be ingested with the async workflow.\r
1247          *\r
1248          * All network errors are ignore here because network read will have read the data from network buffer despite\r
1249          * errors. */\r
1250         flushStatus = _flushHttpsNetworkData( pHttpsConnection, pCurrentHttpsResponse );\r
1251 \r
1252         if( flushStatus == IOT_HTTPS_PARSING_ERROR )\r
1253         {\r
1254             IotLogWarn( "There an error parsing the network flush data. The network buffer might not be fully flushed." );\r
1255         }\r
1256         else if( flushStatus != IOT_HTTPS_OK )\r
1257         {\r
1258             IotLogDebug( "Network error when flushing the https network data: %d", flushStatus );\r
1259         }\r
1260 \r
1261         IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
1262         /* Get the next request to process. */\r
1263         pQItem = IotDeQueue_PeekHead( &( pHttpsConnection->reqQ ) );\r
1264         IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
1265 \r
1266         /* If there is a next request to process, then create a taskpool job to send the request. */\r
1267         if( pQItem != NULL )\r
1268         {\r
1269             /* Set this next request to send. */\r
1270             pNextHttpsRequest = IotLink_Container( _httpsRequest_t, pQItem, link );\r
1271 \r
1272             if( pNextHttpsRequest->scheduled == false )\r
1273             {\r
1274                 IotLogDebug( "Request %d is next in the queue. Now scheduling a task to send the request.", pNextHttpsRequest );\r
1275                 scheduleStatus = _scheduleHttpsRequestSend( pNextHttpsRequest );\r
1276 \r
1277                 /* If there was an error with scheduling the new task, then report it. */\r
1278                 if( HTTPS_FAILED( scheduleStatus ) )\r
1279                 {\r
1280                     IotLogError( "Error scheduling HTTPS request %d. Error code: %d", pNextHttpsRequest, scheduleStatus );\r
1281 \r
1282                     if( pNextHttpsRequest->isAsync && pNextHttpsRequest->pCallbacks->errorCallback )\r
1283                     {\r
1284                         pNextHttpsRequest->pCallbacks->errorCallback( pNextHttpsRequest->pUserPrivData, pNextHttpsRequest, NULL, scheduleStatus );\r
1285                     }\r
1286                     else\r
1287                     {\r
1288                         pNextHttpsRequest->pHttpsResponse->syncStatus = scheduleStatus;\r
1289                     }\r
1290                 }\r
1291             }\r
1292         }\r
1293         else\r
1294         {\r
1295             IotLogDebug( "Network receive callback found the request queue empty. A network send task was not scheduled." );\r
1296         }\r
1297     }\r
1298 \r
1299     /* Dequeue response from the response queue now that it is finished. */\r
1300     IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
1301 \r
1302     /* There could be a scenario where the request fails to send and the network server still responds,\r
1303      * In this case, the failed response will have been cancelled and removed from the queue. If the network\r
1304      * server still got a response, then the safest way to remove the current response is to remove it explicitly\r
1305      * from the queue instead of dequeuing the header of the queue which might not be the current response. */\r
1306     if( IotLink_IsLinked( &( pCurrentHttpsResponse->link ) ) )\r
1307     {\r
1308         IotDeQueue_Remove( &( pCurrentHttpsResponse->link ) );\r
1309     }\r
1310 \r
1311     IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
1312 \r
1313     /* The first if-case below notifies IotHttpsClient_SendSync() that the response is finished receiving. When\r
1314      * IotHttpsClient_SendSync() returns the user is allowed to modify the user buffer used for the response context.\r
1315      * In the asynchronous case, the responseCompleteCallback notifies the application that the user buffer used for the\r
1316      * response context can be modified. Posting to the respFinishedSem or calling the responseCompleteCallback MUST be\r
1317      * mutually exclusive by wrapping in an if/else. If these were separate if-cases, then there could be a context\r
1318      * switch in between where the application modifies the buffer causing the next if-case to be executed. */\r
1319     if( pCurrentHttpsResponse->isAsync == false )\r
1320     {\r
1321         IotSemaphore_Post( &( pCurrentHttpsResponse->respFinishedSem ) );\r
1322     }\r
1323     else if( pCurrentHttpsResponse->pCallbacks->responseCompleteCallback )\r
1324     {\r
1325         /* Signal to a synchronous reponse that the response is complete. */\r
1326         pCurrentHttpsResponse->pCallbacks->responseCompleteCallback( pCurrentHttpsResponse->pUserPrivData, pCurrentHttpsResponse, status, pCurrentHttpsResponse->status );\r
1327     }\r
1328 }\r
1329 \r
1330 /*-----------------------------------------------------------*/\r
1331 \r
1332 static IotHttpsReturnCode_t _createHttpsConnection( IotHttpsConnectionHandle_t * pConnHandle,\r
1333                                                     IotHttpsConnectionInfo_t * pConnInfo )\r
1334 {\r
1335     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1336 \r
1337     IotNetworkError_t networkStatus = IOT_NETWORK_SUCCESS;\r
1338 \r
1339     /* The maximum string length of the ALPN protocols is configured in IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH.\r
1340      * The +1 is for the NULL terminator needed by IotNetworkCredentials_t.pAlpnProtos. */\r
1341     char pAlpnProtos[ IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH + 1 ] = { 0 };\r
1342 \r
1343     /* The maximum string length of the Server host name is configured in IOT_HTTPS_MAX_HOST_NAME_LENGTH.\r
1344      * This +1 is for the NULL terminator needed by IotNetworkServerInfo_t.pHostName. */\r
1345     char pHostName[ IOT_HTTPS_MAX_HOST_NAME_LENGTH + 1 ] = { 0 };\r
1346     bool connectionMutexCreated = false;\r
1347     struct IotNetworkServerInfo networkServerInfo = { 0 };\r
1348     struct IotNetworkCredentials networkCredentials = { 0 };\r
1349     _httpsConnection_t * pHttpsConnection = NULL;\r
1350     IotNetworkCredentials_t pNetworkCredentials = NULL;\r
1351 \r
1352     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pConnInfo->userBuffer.pBuffer );\r
1353     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pConnInfo->pNetworkInterface );\r
1354     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pConnInfo->pAddress );\r
1355     HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( pConnInfo->addressLen > 0 );\r
1356 \r
1357     /* Make sure the connection context can fit in the user buffer. */\r
1358     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( pConnInfo->userBuffer.bufferLen >= connectionUserBufferMinimumSize,\r
1359                                          IOT_HTTPS_INSUFFICIENT_MEMORY,\r
1360                                          "Buffer size is too small to initialize the connection context. User buffer size: %d, required minimum size; %d.",\r
1361                                          ( *pConnInfo ).userBuffer.bufferLen,\r
1362                                          connectionUserBufferMinimumSize );\r
1363 \r
1364     /* Make sure that the server address does not exceed the maximum permitted length. */\r
1365     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( pConnInfo->addressLen <= IOT_HTTPS_MAX_HOST_NAME_LENGTH,\r
1366                                          IOT_HTTPS_INVALID_PARAMETER,\r
1367                                          "IotHttpsConnectionInfo_t.addressLen has a host name length %d that exceeds maximum length %d.",\r
1368                                          pConnInfo->addressLen,\r
1369                                          IOT_HTTPS_MAX_HOST_NAME_LENGTH );\r
1370 \r
1371     /* Make sure that the ALPN protocols does not exceed the maximum permitted length. */\r
1372     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( pConnInfo->alpnProtocolsLen <= IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH,\r
1373                                          IOT_HTTPS_INVALID_PARAMETER,\r
1374                                          "IotHttpsConnectionInfo_t.alpnProtocolsLen of %d exceeds the configured maximum protocol length %d. See IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH for more information.",\r
1375                                          pConnInfo->alpnProtocolsLen,\r
1376                                          IOT_HTTPS_MAX_ALPN_PROTOCOLS_LENGTH );\r
1377 \r
1378     pHttpsConnection = ( _httpsConnection_t * ) ( pConnInfo->userBuffer.pBuffer );\r
1379 \r
1380     /* Start with the disconnected state. */\r
1381     pHttpsConnection->isConnected = false;\r
1382 \r
1383     /* Initialize disconnection state keeper. */\r
1384     pHttpsConnection->isDestroyed = false;\r
1385 \r
1386     /* Initialize the queue of responses and requests. */\r
1387     IotDeQueue_Create( &( pHttpsConnection->reqQ ) );\r
1388     IotDeQueue_Create( &( pHttpsConnection->respQ ) );\r
1389 \r
1390     /* This timeout is used to wait for a response on the connection as well as\r
1391      * for the timeout for the connect operation. */\r
1392     if( pConnInfo->timeout == 0 )\r
1393     {\r
1394         pHttpsConnection->timeout = IOT_HTTPS_RESPONSE_WAIT_MS;\r
1395     }\r
1396     else\r
1397     {\r
1398         pHttpsConnection->timeout = pConnInfo->timeout;\r
1399     }\r
1400 \r
1401     /* pNetworkInterface contains all the routines to be able to send/receive data on the network. */\r
1402     pHttpsConnection->pNetworkInterface = pConnInfo->pNetworkInterface;\r
1403 \r
1404     /* The address from the connection configuration information is copied to a local buffer because a NULL pointer\r
1405      * is required in IotNetworkServerInfo_t.pHostName. IotNetworkServerInfo_t contains the server information needed\r
1406      * by the network interface to create the connection. */\r
1407     memcpy( pHostName, pConnInfo->pAddress, pConnInfo->addressLen );\r
1408     pHostName[ pConnInfo->addressLen ] = '\0';\r
1409     /* Set it in the IOT network abstractions server information parameter. */\r
1410     networkServerInfo.pHostName = pHostName;\r
1411     networkServerInfo.port = pConnInfo->port;\r
1412 \r
1413     /* If this is TLS connection, then set the network credentials. */\r
1414     if( ( pConnInfo->flags & IOT_HTTPS_IS_NON_TLS_FLAG ) == 0 )\r
1415     {\r
1416         if( pConnInfo->flags & IOT_HTTPS_DISABLE_SNI )\r
1417         {\r
1418             networkCredentials.disableSni = true;\r
1419         }\r
1420         else\r
1421         {\r
1422             networkCredentials.disableSni = false;\r
1423         }\r
1424 \r
1425         if( pConnInfo->pAlpnProtocols != NULL )\r
1426         {\r
1427             /* The alpn protocol strings in IotNetworkCredentials_t require a NULL terminator, so the alpn protocol\r
1428              * string in the connection configuration information is copied to a local buffer to append the NULL\r
1429              * terminator. */\r
1430             memcpy( pAlpnProtos, pConnInfo->pAlpnProtocols, pConnInfo->alpnProtocolsLen );\r
1431             pAlpnProtos[ pConnInfo->alpnProtocolsLen ] = '\0';\r
1432             networkCredentials.pAlpnProtos = pAlpnProtos;\r
1433         }\r
1434         else\r
1435         {\r
1436             networkCredentials.pAlpnProtos = NULL;\r
1437         }\r
1438 \r
1439         /* If any of these are NULL a network error will result when trying to make the connection. Because there is\r
1440          * no invalid memory access resulting from these configurations being NULL, it is not check at the start\r
1441          * of the function. */\r
1442         networkCredentials.pRootCa = pConnInfo->pCaCert;\r
1443         networkCredentials.rootCaSize = pConnInfo->caCertLen;\r
1444         networkCredentials.pClientCert = pConnInfo->pClientCert;\r
1445         networkCredentials.clientCertSize = pConnInfo->clientCertLen;\r
1446         networkCredentials.pPrivateKey = pConnInfo->pPrivateKey;\r
1447         networkCredentials.privateKeySize = pConnInfo->privateKeyLen;\r
1448 \r
1449         pNetworkCredentials = &networkCredentials;\r
1450     }\r
1451     else\r
1452     {\r
1453         /* create() takes a NULL if there is no TLS configuration. */\r
1454         pNetworkCredentials = NULL;\r
1455     }\r
1456 \r
1457     /* create() will connect to the server specified in addition to creating other network layer\r
1458      *  specific resources. */\r
1459     networkStatus = pHttpsConnection->pNetworkInterface->create( &networkServerInfo,\r
1460                                                                  pNetworkCredentials,\r
1461                                                                  &( pHttpsConnection->pNetworkConnection ) );\r
1462 \r
1463     /* Check to see if the network connection succeeded. If it did not succeed,\r
1464      * then the output parameter pConnHandle will be used to return NULL and the\r
1465      * function returns an error. */\r
1466     if( networkStatus != IOT_NETWORK_SUCCESS )\r
1467     {\r
1468         IotLogError( "Failed to connect to the server at %.*s on port %d with error: %d",\r
1469                      pConnInfo->addressLen,\r
1470                      pConnInfo->pAddress,\r
1471                      pConnInfo->port,\r
1472                      networkStatus );\r
1473         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_CONNECTION_ERROR );\r
1474     }\r
1475 \r
1476     /* The connection succeeded so set the state to connected. */\r
1477     pHttpsConnection->isConnected = true;\r
1478 \r
1479     /* The receive callback is invoked by the network layer when data is ready\r
1480      * to be read from the network. */\r
1481     networkStatus = pHttpsConnection->pNetworkInterface->setReceiveCallback( pHttpsConnection->pNetworkConnection,\r
1482                                                                              _networkReceiveCallback,\r
1483                                                                              pHttpsConnection );\r
1484 \r
1485     if( networkStatus != IOT_NETWORK_SUCCESS )\r
1486     {\r
1487         IotLogError( "Failed to connect to set the HTTPS receive callback. " );\r
1488         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INTERNAL_ERROR );\r
1489     }\r
1490 \r
1491     /* Connection was successful, so create synchronization primitives. */\r
1492 \r
1493     connectionMutexCreated = IotMutex_Create( &( pHttpsConnection->connectionMutex ), false );\r
1494 \r
1495     if( !connectionMutexCreated )\r
1496     {\r
1497         IotLogError( "Failed to create an internal mutex." );\r
1498         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INTERNAL_ERROR );\r
1499     }\r
1500 \r
1501     /* Return the new connection information. */\r
1502     *pConnHandle = pHttpsConnection;\r
1503 \r
1504     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
1505 \r
1506     /* If we failed anywhere in the connection process, then destroy the semaphores created. */\r
1507     if( HTTPS_FAILED( status ) )\r
1508     {\r
1509         /* If there was a connect was successful, disconnect from the network.  */\r
1510         if( ( pHttpsConnection != NULL ) && ( pHttpsConnection->isConnected ) )\r
1511         {\r
1512             _networkDisconnect( pHttpsConnection );\r
1513             _networkDestroy( pHttpsConnection );\r
1514         }\r
1515 \r
1516         if( connectionMutexCreated )\r
1517         {\r
1518             IotMutex_Destroy( &( pHttpsConnection->connectionMutex ) );\r
1519         }\r
1520 \r
1521         /* Set the connection handle as NULL if everything failed. */\r
1522         *pConnHandle = NULL;\r
1523     }\r
1524 \r
1525     HTTPS_FUNCTION_CLEANUP_END();\r
1526 }\r
1527 \r
1528 /*-----------------------------------------------------------*/\r
1529 \r
1530 static void _networkDisconnect( _httpsConnection_t * pHttpsConnection )\r
1531 {\r
1532     IotNetworkError_t networkStatus = IOT_NETWORK_SUCCESS;\r
1533 \r
1534     networkStatus = pHttpsConnection->pNetworkInterface->close( pHttpsConnection->pNetworkConnection );\r
1535 \r
1536     if( networkStatus != IOT_NETWORK_SUCCESS )\r
1537     {\r
1538         IotLogWarn( "Failed to shutdown the socket with error code: %d", networkStatus );\r
1539     }\r
1540 }\r
1541 \r
1542 /*-----------------------------------------------------------*/\r
1543 \r
1544 static void _networkDestroy( _httpsConnection_t * pHttpsConnection )\r
1545 {\r
1546     IotNetworkError_t networkStatus = IOT_NETWORK_SUCCESS;\r
1547 \r
1548     networkStatus = pHttpsConnection->pNetworkInterface->destroy( pHttpsConnection->pNetworkConnection );\r
1549 \r
1550     if( networkStatus != IOT_NETWORK_SUCCESS )\r
1551     {\r
1552         IotLogWarn( "Failed to shutdown the socket with error code: %d", networkStatus );\r
1553     }\r
1554 }\r
1555 \r
1556 /*-----------------------------------------------------------*/\r
1557 \r
1558 static IotHttpsReturnCode_t _addHeader( _httpsRequest_t * pHttpsRequest,\r
1559                                         const char * pName,\r
1560                                         uint32_t nameLen,\r
1561                                         const char * pValue,\r
1562                                         uint32_t valueLen )\r
1563 {\r
1564     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1565 \r
1566     int headerFieldSeparatorLen = HTTPS_HEADER_FIELD_SEPARATOR_LENGTH;\r
1567     uint32_t additionalLength = nameLen + headerFieldSeparatorLen + valueLen + HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH;\r
1568     uint32_t possibleLastHeaderAdditionalLength = HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH;\r
1569 \r
1570     /* Check if there is enough space to add the header field and value\r
1571      * (name:value\r\n). We need to add a "\r\n" at the end of headers. The use of\r
1572      * possibleLastHeaderAdditionalLength is to make sure that there is always\r
1573      * space for the last "\r\n". */\r
1574     if( ( additionalLength + possibleLastHeaderAdditionalLength ) > ( ( uint32_t ) ( pHttpsRequest->pHeadersEnd - pHttpsRequest->pHeadersCur ) ) )\r
1575     {\r
1576         IotLogError( "There is %d space left in the header buffer, but we want to add %d more of header.",\r
1577                      pHttpsRequest->pHeadersEnd - pHttpsRequest->pHeadersCur,\r
1578                      additionalLength + possibleLastHeaderAdditionalLength );\r
1579         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INSUFFICIENT_MEMORY );\r
1580     }\r
1581 \r
1582     memcpy( pHttpsRequest->pHeadersCur, pName, nameLen );\r
1583     pHttpsRequest->pHeadersCur += nameLen;\r
1584     memcpy( pHttpsRequest->pHeadersCur, HTTPS_HEADER_FIELD_SEPARATOR, headerFieldSeparatorLen );\r
1585     pHttpsRequest->pHeadersCur += headerFieldSeparatorLen;\r
1586     memcpy( pHttpsRequest->pHeadersCur, pValue, valueLen );\r
1587     pHttpsRequest->pHeadersCur += valueLen;\r
1588     memcpy( pHttpsRequest->pHeadersCur, HTTPS_END_OF_HEADER_LINES_INDICATOR, HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH );\r
1589     pHttpsRequest->pHeadersCur += HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH;\r
1590     IotLogDebug( "Wrote header: \"%s: %.*s\r\n\". Space left in request user buffer: %d",\r
1591                  pName,\r
1592                  valueLen,\r
1593                  pValue,\r
1594                  pHttpsRequest->pHeadersEnd - pHttpsRequest->pHeadersCur );\r
1595 \r
1596     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1597 }\r
1598 \r
1599 /*-----------------------------------------------------------*/\r
1600 \r
1601 static IotHttpsReturnCode_t _networkSend( _httpsConnection_t * pHttpsConnection,\r
1602                                           uint8_t * pBuf,\r
1603                                           size_t len )\r
1604 {\r
1605     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1606 \r
1607     size_t numBytesSent = 0;\r
1608     size_t numBytesSentTotal = 0;\r
1609     size_t sendLength = len;\r
1610 \r
1611     while( numBytesSentTotal < sendLength )\r
1612     {\r
1613         numBytesSent = pHttpsConnection->pNetworkInterface->send( pHttpsConnection->pNetworkConnection,\r
1614                                                                   &( pBuf[ numBytesSentTotal ] ),\r
1615                                                                   sendLength - numBytesSentTotal );\r
1616 \r
1617         /* pNetworkInterface->send returns 0 on error. */\r
1618         if( numBytesSent == 0 )\r
1619         {\r
1620             IotLogError( "Error in sending the HTTPS headers. Error code: %d", numBytesSent );\r
1621             break;\r
1622         }\r
1623 \r
1624         numBytesSentTotal += numBytesSent;\r
1625     }\r
1626 \r
1627     if( numBytesSentTotal != sendLength )\r
1628     {\r
1629         IotLogError( "Error sending data on the network. We sent %d but there were total %d.", numBytesSentTotal, sendLength );\r
1630         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_NETWORK_ERROR );\r
1631     }\r
1632 \r
1633     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1634 }\r
1635 \r
1636 /*-----------------------------------------------------------*/\r
1637 \r
1638 static IotHttpsReturnCode_t _networkRecv( _httpsConnection_t * pHttpsConnection,\r
1639                                           uint8_t * pBuf,\r
1640                                           size_t bufLen,\r
1641                                           size_t * numBytesRecv )\r
1642 {\r
1643     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1644 \r
1645     /* The HTTP server could send the header and the body in two separate TCP packets. If that is the case, then\r
1646      * receiveUpTo will return return the full headers first. Then on a second call, the body will be returned.\r
1647      * If the http parser receives just the headers despite the content length being greater than  */\r
1648     *numBytesRecv = pHttpsConnection->pNetworkInterface->receiveUpto( pHttpsConnection->pNetworkConnection,\r
1649                                                                       pBuf,\r
1650                                                                       bufLen );\r
1651 \r
1652     IotLogDebug( "The network interface receive returned %d.", numBytesRecv );\r
1653 \r
1654     /* We return IOT_HTTPS_NETWORK_ERROR only if we receive nothing. Receiving less\r
1655      * data than requested is okay because it is not known in advance how much data\r
1656      * we are going to receive and therefore we request for the available buffer\r
1657      * size. */\r
1658     if( *numBytesRecv == 0 )\r
1659     {\r
1660         IotLogError( "Error in receiving the HTTPS response message. Socket Error code %d", *numBytesRecv );\r
1661         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_NETWORK_ERROR );\r
1662 \r
1663         /* A network error is returned when zero is received because that would indicate that either there\r
1664         * was a network error or there was a timeout reading data. If there was timeout reading data, then\r
1665         * the server was too slow to respond. If the server is too slow to respond, then a network error must\r
1666         * be returned to trigger a connection close. The connection must close after the network error so\r
1667         * that the response from this request does not piggyback on the response from the next request. */\r
1668     }\r
1669 \r
1670     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1671 }\r
1672 \r
1673 /*-----------------------------------------------------------*/\r
1674 \r
1675 static IotHttpsReturnCode_t _sendHttpsHeaders( _httpsConnection_t * pHttpsConnection,\r
1676                                                uint8_t * pHeadersBuf,\r
1677                                                uint32_t headersLength,\r
1678                                                bool isNonPersistent,\r
1679                                                uint32_t contentLength )\r
1680 {\r
1681     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1682 \r
1683     const char * connectionHeader = NULL;\r
1684     int numWritten = 0;\r
1685     int connectionHeaderLen = 0;\r
1686     /* The Content-Length header of the form "Content-Length: N\r\n" with a NULL terminator for snprintf. */\r
1687     char contentLengthHeaderStr[ HTTPS_MAX_CONTENT_LENGTH_LINE_LENGTH + 1 ];\r
1688 \r
1689     /* The HTTP headers to send after the headers in pHeadersBuf are the Content-Length and the Connection type and\r
1690      * the final "\r\n" to indicate the end of the the header lines. Note that we are using\r
1691      * HTTPS_CONNECTION_KEEP_ALIVE_HEADER_LINE_LENGTH because length of "Connection: keep-alive\r\n" is\r
1692      * more than "Connection: close\r\n". Creating a buffer of bigger size ensures that\r
1693      * both the connection type strings will fit in the buffer. */\r
1694     char finalHeaders[ HTTPS_MAX_CONTENT_LENGTH_LINE_LENGTH + HTTPS_CONNECTION_KEEP_ALIVE_HEADER_LINE_LENGTH + HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH ] = { 0 };\r
1695 \r
1696     /* Send the headers passed into this function first. These headers are not terminated with a second set of "\r\n". */\r
1697     status = _networkSend( pHttpsConnection, pHeadersBuf, headersLength );\r
1698 \r
1699     if( HTTPS_FAILED( status ) )\r
1700     {\r
1701         IotLogError( "Error sending the HTTPS headers in the request user buffer. Error code: %d", status );\r
1702         HTTPS_GOTO_CLEANUP();\r
1703     }\r
1704 \r
1705     /* If there is a Content-Length, then write that to the finalHeaders to send. */\r
1706     if( contentLength > 0 )\r
1707     {\r
1708         numWritten = snprintf( contentLengthHeaderStr,\r
1709                                sizeof( contentLengthHeaderStr ),\r
1710                                "%s: %u\r\n",\r
1711                                HTTPS_CONTENT_LENGTH_HEADER,\r
1712                                ( unsigned int ) contentLength );\r
1713     }\r
1714 \r
1715     if( ( numWritten < 0 ) || ( numWritten >= sizeof( contentLengthHeaderStr ) ) )\r
1716     {\r
1717         IotLogError( "Internal error in snprintf() in _sendHttpsHeaders(). Error code %d.", numWritten );\r
1718         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INTERNAL_ERROR );\r
1719     }\r
1720 \r
1721     /* snprintf() succeeded so copy that to the finalHeaders. */\r
1722     memcpy( finalHeaders, contentLengthHeaderStr, numWritten );\r
1723 \r
1724     /* Write the connection persistence type to the final headers. */\r
1725     if( isNonPersistent )\r
1726     {\r
1727         connectionHeader = HTTPS_CONNECTION_CLOSE_HEADER_LINE;\r
1728         connectionHeaderLen = FAST_MACRO_STRLEN( HTTPS_CONNECTION_CLOSE_HEADER_LINE );\r
1729     }\r
1730     else\r
1731     {\r
1732         connectionHeader = HTTPS_CONNECTION_KEEP_ALIVE_HEADER_LINE;\r
1733         connectionHeaderLen = FAST_MACRO_STRLEN( HTTPS_CONNECTION_KEEP_ALIVE_HEADER_LINE );\r
1734     }\r
1735 \r
1736     memcpy( &finalHeaders[ numWritten ], connectionHeader, connectionHeaderLen );\r
1737     numWritten += connectionHeaderLen;\r
1738     memcpy( &finalHeaders[ numWritten ], HTTPS_END_OF_HEADER_LINES_INDICATOR, HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH );\r
1739     numWritten += HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH;\r
1740 \r
1741     status = _networkSend( pHttpsConnection, ( uint8_t * ) finalHeaders, numWritten );\r
1742 \r
1743     if( HTTPS_FAILED( status ) )\r
1744     {\r
1745         IotLogError( "Error sending final HTTPS Headers \r\n%s. Error code: %d", finalHeaders, status );\r
1746         HTTPS_GOTO_CLEANUP();\r
1747     }\r
1748 \r
1749     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1750 }\r
1751 \r
1752 /*-----------------------------------------------------------*/\r
1753 \r
1754 static IotHttpsReturnCode_t _sendHttpsBody( _httpsConnection_t * pHttpsConnection,\r
1755                                             uint8_t * pBodyBuf,\r
1756                                             uint32_t bodyLength )\r
1757 {\r
1758     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1759 \r
1760     status = _networkSend( pHttpsConnection, pBodyBuf, bodyLength );\r
1761 \r
1762     if( HTTPS_FAILED( status ) )\r
1763     {\r
1764         IotLogError( "Error sending final HTTPS body at location 0x%x. Error code: %d", pBodyBuf, status );\r
1765         HTTPS_GOTO_CLEANUP();\r
1766     }\r
1767 \r
1768     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1769 }\r
1770 \r
1771 /*-----------------------------------------------------------*/\r
1772 \r
1773 static IotHttpsReturnCode_t _parseHttpsMessage( _httpParserInfo_t * pHttpParserInfo,\r
1774                                                 char * pBuf,\r
1775                                                 size_t len )\r
1776 {\r
1777     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1778 \r
1779     size_t parsedBytes = 0;\r
1780     const char * pHttpParserErrorDescription = NULL;\r
1781     http_parser * pHttpParser = &( pHttpParserInfo->responseParser );\r
1782 \r
1783     IotLogDebug( "Now parsing HTTP message buffer to process a response." );\r
1784     parsedBytes = pHttpParserInfo->parseFunc( pHttpParser, &_httpParserSettings, pBuf, len );\r
1785     IotLogDebug( "http-parser parsed %d bytes out of %d specified.", parsedBytes, len );\r
1786 \r
1787     /* If the parser fails with HPE_CLOSED_CONNECTION or HPE_INVALID_CONSTANT that simply means there\r
1788      * was data beyond the end of the message. We do not fail in this case because we give the whole\r
1789      * header buffer or body buffer to the parser even if it is only partly filled with data.\r
1790      * Errors <= HPE_CB_chunk_complete means that a non-zero number was returned from some callback.\r
1791      * A nonzero number is returned from some callbacks when we want to stop the parser early\r
1792      * for example - a HEAD request or the user explicitly asked to ignore the body by not\r
1793      * providing the body buffer. */\r
1794     if( ( pHttpParser->http_errno != 0 ) &&\r
1795         ( HTTP_PARSER_ERRNO( pHttpParser ) != HPE_CLOSED_CONNECTION ) &&\r
1796         ( HTTP_PARSER_ERRNO( pHttpParser ) != HPE_INVALID_CONSTANT ) &&\r
1797         ( HTTP_PARSER_ERRNO( pHttpParser ) > HPE_CB_chunk_complete ) )\r
1798     {\r
1799         pHttpParserErrorDescription = http_errno_description( HTTP_PARSER_ERRNO( pHttpParser ) );\r
1800         IotLogError( "http_parser failed on the http response with error: %s", pHttpParserErrorDescription );\r
1801         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_PARSING_ERROR );\r
1802     }\r
1803 \r
1804     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1805 }\r
1806 \r
1807 /*-----------------------------------------------------------*/\r
1808 \r
1809 static void _incrementNextLocationToWriteBeyondParsed( uint8_t ** pBufCur,\r
1810                                                        uint8_t ** pBufEnd )\r
1811 {\r
1812     /* There is an edge case where the final one or two character received in the header buffer is part of\r
1813      * the header field separator ": " or part of the header line end "\r\n" delimiters. When this\r
1814      * happens, pHeadersCur in the response will point not the end of the buffer, but to a character in\r
1815      * the delimiter. For example:\r
1816      * Let's say this is our current header buffer after receiving and parsing:\r
1817      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1: value1\r\n"]\r
1818      * pHeadersCur will point to \r because the http-parser does not invoke a callback on the\r
1819      * delimiters. Since no callback is invoked, pHeadersCur is not incremented. pHeadersEnd points to\r
1820      * the end of the header buffer which is the unwritable memory location right after the final '\n'.\r
1821      * Because pHeadersCur is less than pHeaderEnd we loop again and receive on the network causing the\r
1822      * buffer to look like this:\r
1823      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1: value1he"]\r
1824      * Which will cause an incorrect header1 value to be read if the application decides to read it with\r
1825      * IotHttpsClient_ReadHeader().\r
1826      *\r
1827      * If our header buffer looks like:\r
1828      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1: "]\r
1829      * then pHeaderCur will point to the colon.\r
1830      *\r
1831      * If our header buffer looks like:\r
1832      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1:"]\r
1833      * then pHeaderCur will point to the colon.\r
1834      *\r
1835      * If our header buffer looks like\r
1836      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1: value1 "]\r
1837      * then http-parser will consider that space as part of value1.\r
1838      *\r
1839      * If our header buffer looks like\r
1840      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1: value1\r"]\r
1841      * then pHeaderCur will point to the carriage return.\r
1842      *\r
1843      * If our header buffer looks like\r
1844      * ["HTTP/1.1 200 OK\r\n\header0: value0\r\nheader1: value1\r\n"]\r
1845      * As explained in the example above, pHeaderCur will point to the carriage return.\r
1846      *\r
1847      * If we somehow receive a partial HTTP response message in our zeroed-out header buffer:\r
1848      * case 1: ["HTTP/1.1 200 OK\r\nheader0: value0\r\nheader1: value1\r\0\0\0\0\0\0\0"]\r
1849      * case 2: ["HTTP/1.1 200 OK\r\nheader0: value0\r\nheader1: value1\r\n\0\0\0\0\0\0"]\r
1850      * case 3: ["HTTP/1.1 200 OK\r\nheader0: value0\r\nheader1:\0\0\0\0\0\0\0\0\0\0\0"]\r
1851      * case 4: ["HTTP/1.1 200 OK\r\nheader0: value0\r\nheader1: \0\0\0\0\0\0\0\0\0\0\0"]\r
1852      * then parser may fail or append all of the NULL characters to a header field name or value. */\r
1853     while( *pBufCur < *pBufEnd )\r
1854     {\r
1855         if( **pBufCur == CARRIAGE_RETURN_CHARACTER )\r
1856         {\r
1857             ( *pBufCur )++;\r
1858         }\r
1859         else if( **pBufCur == NEWLINE_CHARACTER )\r
1860         {\r
1861             ( *pBufCur )++;\r
1862             break;\r
1863         }\r
1864         else if( **pBufCur == COLON_CHARACTER )\r
1865         {\r
1866             ( *pBufCur )++;\r
1867         }\r
1868         else if( ( **pBufCur == SPACE_CHARACTER ) && ( *( *pBufCur - 1 ) == COLON_CHARACTER ) )\r
1869         {\r
1870             ( *pBufCur )++;\r
1871             break;\r
1872         }\r
1873         else\r
1874         {\r
1875             break;\r
1876         }\r
1877     }\r
1878 }\r
1879 \r
1880 /*-----------------------------------------------------------*/\r
1881 \r
1882 static IotHttpsReturnCode_t _receiveHttpsMessage( _httpsConnection_t * pHttpsConnection,\r
1883                                                   _httpParserInfo_t * pHttpParserInfo,\r
1884                                                   IotHttpsResponseParserState_t * pCurrentParserState,\r
1885                                                   IotHttpsResponseParserState_t finalParserState,\r
1886                                                   IotHttpsResponseBufferState_t currentBufferProcessingState,\r
1887                                                   uint8_t ** pBufCur,\r
1888                                                   uint8_t ** pBufEnd )\r
1889 {\r
1890     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1891 \r
1892     size_t numBytesRecv = 0;\r
1893 \r
1894     /* The final parser state is either the end of the header lines or the end of the entity body. This state is set in\r
1895      * the http-parser callbacks. */\r
1896     while( ( *pCurrentParserState < finalParserState ) && ( *pBufEnd - *pBufCur > 0 ) )\r
1897     {\r
1898         status = _networkRecv( pHttpsConnection,\r
1899                                *pBufCur,\r
1900                                *pBufEnd - *pBufCur,\r
1901                                &numBytesRecv );\r
1902 \r
1903         /* A network error in _networkRecv is returned only when we received zero bytes. In that case, there is\r
1904          * no point in parsing we return immediately with the network error. */\r
1905         if( HTTPS_FAILED( status ) )\r
1906         {\r
1907             IotLogError( "Network error receiving the HTTPS response headers. Error code: %d", status );\r
1908             break;\r
1909         }\r
1910 \r
1911         status = _parseHttpsMessage( pHttpParserInfo, ( char * ) ( *pBufCur ), numBytesRecv );\r
1912 \r
1913         if( HTTPS_FAILED( status ) )\r
1914         {\r
1915             IotLogError( "Failed to parse the message buffer with error: %d", pHttpParserInfo->responseParser.http_errno );\r
1916             break;\r
1917         }\r
1918 \r
1919         /* If the current buffer being filled is the header buffer, then \r\n header line separators should not get\r
1920          * overwritten on the next network read. See _incrementNextLocationToWriteBeyondParsed() for more\r
1921          * information. */\r
1922         if( currentBufferProcessingState == PROCESSING_STATE_FILLING_HEADER_BUFFER )\r
1923         {\r
1924             _incrementNextLocationToWriteBeyondParsed( pBufCur, pBufEnd );\r
1925         }\r
1926 \r
1927         /* The _httpsResponse->pHeadersCur pointer is updated in the http_parser callbacks. */\r
1928         IotLogDebug( "There is %d of space left in the buffer.", *pBufEnd - *pBufCur );\r
1929     }\r
1930 \r
1931     /* If we did not reach the end of the headers or body in the parser callbacks, then the buffer configured does not\r
1932      * fit all of that part of the HTTP message. */\r
1933     if( *pCurrentParserState < finalParserState )\r
1934     {\r
1935         IotLogDebug( "There are still more data on the network. It could not fit into the specified length %d.",\r
1936                      *pBufEnd - *pBufCur );\r
1937     }\r
1938 \r
1939     HTTPS_GOTO_CLEANUP();\r
1940     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1941 }\r
1942 \r
1943 /*-----------------------------------------------------------*/\r
1944 \r
1945 static IotHttpsReturnCode_t _receiveHttpsHeaders( _httpsConnection_t * pHttpsConnection,\r
1946                                                   _httpsResponse_t * pHttpsResponse )\r
1947 {\r
1948     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1949 \r
1950     pHttpsResponse->bufferProcessingState = PROCESSING_STATE_FILLING_HEADER_BUFFER;\r
1951 \r
1952     IotLogDebug( "Now attempting to receive the HTTP response headers into a buffer with length %d.",\r
1953                  pHttpsResponse->pHeadersEnd - pHttpsResponse->pHeadersCur );\r
1954 \r
1955     status = _receiveHttpsMessage( pHttpsConnection,\r
1956                                    &( pHttpsResponse->httpParserInfo ),\r
1957                                    &( pHttpsResponse->parserState ),\r
1958                                    PARSER_STATE_HEADERS_COMPLETE,\r
1959                                    PROCESSING_STATE_FILLING_HEADER_BUFFER,\r
1960                                    &( pHttpsResponse->pHeadersCur ),\r
1961                                    &( pHttpsResponse->pHeadersEnd ) );\r
1962 \r
1963     if( HTTPS_FAILED( status ) )\r
1964     {\r
1965         IotLogError( "Error receiving the HTTP headers. Error code %d", status );\r
1966         HTTPS_GOTO_CLEANUP();\r
1967     }\r
1968 \r
1969     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
1970 }\r
1971 \r
1972 /*-----------------------------------------------------------*/\r
1973 \r
1974 /* _receiveHttpsHeaders() must be called first before this function is called. */\r
1975 static IotHttpsReturnCode_t _receiveHttpsBody( _httpsConnection_t * pHttpsConnection,\r
1976                                                _httpsResponse_t * pHttpsResponse )\r
1977 {\r
1978     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
1979 \r
1980     IotLogDebug( "Now attempting to receive the HTTP response body into a buffer with length %d.",\r
1981                  pHttpsResponse->pBodyEnd - pHttpsResponse->pBodyCur );\r
1982 \r
1983     pHttpsResponse->bufferProcessingState = PROCESSING_STATE_FILLING_BODY_BUFFER;\r
1984 \r
1985     status = _receiveHttpsMessage( pHttpsConnection,\r
1986                                    &( pHttpsResponse->httpParserInfo ),\r
1987                                    &( pHttpsResponse->parserState ),\r
1988                                    PARSER_STATE_BODY_COMPLETE,\r
1989                                    PROCESSING_STATE_FILLING_BODY_BUFFER,\r
1990                                    &( pHttpsResponse->pBodyCur ),\r
1991                                    &( pHttpsResponse->pBodyEnd ) );\r
1992 \r
1993     if( HTTPS_FAILED( status ) )\r
1994     {\r
1995         IotLogError( "Error receiving the HTTP body. Error code %d", status );\r
1996         HTTPS_GOTO_CLEANUP();\r
1997     }\r
1998 \r
1999     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
2000 \r
2001     IotLogDebug( "The remaining content length on the network is %d.",\r
2002                  pHttpsResponse->httpParserInfo.responseParser.content_length );\r
2003 \r
2004     HTTPS_FUNCTION_CLEANUP_END();\r
2005 }\r
2006 \r
2007 /*-----------------------------------------------------------*/\r
2008 \r
2009 static IotHttpsReturnCode_t _flushHttpsNetworkData( _httpsConnection_t * pHttpsConnection,\r
2010                                                     _httpsResponse_t * pHttpsResponse )\r
2011 {\r
2012     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2013 \r
2014     static uint8_t flushBuffer[ IOT_HTTPS_MAX_FLUSH_BUFFER_SIZE ] = { 0 };\r
2015     const char * pHttpParserErrorDescription = NULL;\r
2016     IotHttpsReturnCode_t parserStatus = IOT_HTTPS_OK;\r
2017     IotHttpsReturnCode_t networkStatus = IOT_HTTPS_OK;\r
2018     size_t numBytesRecv = 0;\r
2019 \r
2020     /* Even if there is not body, the parser state will become body complete after the headers finish. */\r
2021     while( pHttpsResponse->parserState < PARSER_STATE_BODY_COMPLETE )\r
2022     {\r
2023         IotLogDebug( "Now clearing the rest of the response data on the socket. " );\r
2024         networkStatus = _networkRecv( pHttpsConnection, flushBuffer, IOT_HTTPS_MAX_FLUSH_BUFFER_SIZE, &numBytesRecv );\r
2025 \r
2026         /* Run this through the parser so that we can get the end of the HTTP message, instead of simply timing out the socket to stop.\r
2027          * If we relied on the socket timeout to stop reading the network socket, then the server may close the connection. */\r
2028         parserStatus = _parseHttpsMessage( &( pHttpsResponse->httpParserInfo ), ( char * ) flushBuffer, numBytesRecv );\r
2029 \r
2030         if( HTTPS_FAILED( parserStatus ) )\r
2031         {\r
2032             pHttpParserErrorDescription = http_errno_description( HTTP_PARSER_ERRNO( &pHttpsResponse->httpParserInfo.responseParser ) );\r
2033             IotLogError( "Network Flush: Failed to parse the response body buffer with error: %d, %s",\r
2034                          pHttpsResponse->httpParserInfo.responseParser.http_errno,\r
2035                          pHttpParserErrorDescription );\r
2036             break;\r
2037         }\r
2038 \r
2039         /* If there is a network error then we want to stop clearing out the buffer. */\r
2040         if( HTTPS_FAILED( networkStatus ) )\r
2041         {\r
2042             IotLogWarn( "Network Flush: Error receiving the rest of the HTTP response. Error code: %d",\r
2043                         networkStatus );\r
2044             break;\r
2045         }\r
2046     }\r
2047 \r
2048     /* All network errors except timeouts are returned. */\r
2049     if( HTTPS_FAILED( networkStatus ) )\r
2050     {\r
2051         status = networkStatus;\r
2052     }\r
2053     else\r
2054     {\r
2055         status = parserStatus;\r
2056     }\r
2057 \r
2058     HTTPS_GOTO_CLEANUP();\r
2059 \r
2060     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2061 }\r
2062 \r
2063 /*-----------------------------------------------------------*/\r
2064 \r
2065 static IotHttpsReturnCode_t _sendHttpsHeadersAndBody( _httpsConnection_t * pHttpsConnection,\r
2066                                                       _httpsRequest_t * pHttpsRequest )\r
2067 {\r
2068     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2069 \r
2070     /* Send the HTTP headers. */\r
2071     status = _sendHttpsHeaders( pHttpsConnection,\r
2072                                 pHttpsRequest->pHeaders,\r
2073                                 pHttpsRequest->pHeadersCur - pHttpsRequest->pHeaders,\r
2074                                 pHttpsRequest->isNonPersistent,\r
2075                                 pHttpsRequest->bodyLength );\r
2076 \r
2077     if( HTTPS_FAILED( status ) )\r
2078     {\r
2079         IotLogError( "Error sending the HTTPS headers with error code: %d", status );\r
2080         HTTPS_GOTO_CLEANUP();\r
2081     }\r
2082 \r
2083     if( ( pHttpsRequest->pBody != NULL ) && ( pHttpsRequest->bodyLength > 0 ) )\r
2084     {\r
2085         status = _sendHttpsBody( pHttpsConnection, pHttpsRequest->pBody, pHttpsRequest->bodyLength );\r
2086 \r
2087         if( HTTPS_FAILED( status ) )\r
2088         {\r
2089             IotLogError( "Error sending final HTTPS body. Return code: %d", status );\r
2090             HTTPS_GOTO_CLEANUP();\r
2091         }\r
2092     }\r
2093 \r
2094     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2095 }\r
2096 \r
2097 /*-----------------------------------------------------------*/\r
2098 \r
2099 static void _sendHttpsRequest( IotTaskPool_t pTaskPool,\r
2100                                IotTaskPoolJob_t pJob,\r
2101                                void * pUserContext )\r
2102 {\r
2103     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2104 \r
2105     _httpsRequest_t * pHttpsRequest = ( _httpsRequest_t * ) ( pUserContext );\r
2106     _httpsConnection_t * pHttpsConnection = pHttpsRequest->pHttpsConnection;\r
2107     _httpsResponse_t * pHttpsResponse = pHttpsRequest->pHttpsResponse;\r
2108     IotHttpsReturnCode_t disconnectStatus = IOT_HTTPS_OK;\r
2109     IotHttpsReturnCode_t scheduleStatus = IOT_HTTPS_OK;\r
2110     IotLink_t * pQItem = NULL;\r
2111     _httpsRequest_t * pNextHttpsRequest = NULL;\r
2112 \r
2113     ( void ) pTaskPool;\r
2114     ( void ) pJob;\r
2115 \r
2116     IotLogDebug( "Task with request ID: %d started.", pHttpsRequest );\r
2117 \r
2118     if( pHttpsRequest->cancelled == true )\r
2119     {\r
2120         IotLogDebug( "Request ID: %d was cancelled.", pHttpsRequest );\r
2121         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_SEND_ABORT );\r
2122     }\r
2123 \r
2124     /* To protect against out of order network data from a rouge server, signal that the request is\r
2125      * not finished sending. */\r
2126     pHttpsResponse->reqFinishedSending = false;\r
2127 \r
2128     /* Queue the response to expect from the network. */\r
2129     IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
2130     IotDeQueue_EnqueueTail( &( pHttpsConnection->respQ ), &( pHttpsResponse->link ) );\r
2131     IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
2132 \r
2133     /* Get the headers from the application. For a synchronous request the application should have appended extra\r
2134      * headers before this point. */\r
2135     if( pHttpsRequest->isAsync && pHttpsRequest->pCallbacks->appendHeaderCallback )\r
2136     {\r
2137         pHttpsRequest->pCallbacks->appendHeaderCallback( pHttpsRequest->pUserPrivData, pHttpsRequest );\r
2138     }\r
2139 \r
2140     if( pHttpsRequest->cancelled == true )\r
2141     {\r
2142         IotLogDebug( "Request ID: %d was cancelled.", pHttpsRequest );\r
2143         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_SEND_ABORT );\r
2144     }\r
2145 \r
2146     /* Ask the user for data to write body to the network. We only ask the user once. This is so that\r
2147      * we can calculate the Content-Length to send.*/\r
2148     if( pHttpsRequest->isAsync && pHttpsRequest->pCallbacks->writeCallback )\r
2149     {\r
2150         /* If there is data, then a Content-Length header value will be provided and we send the headers\r
2151          * before that user data. */\r
2152         pHttpsRequest->pCallbacks->writeCallback( pHttpsRequest->pUserPrivData, pHttpsRequest );\r
2153     }\r
2154 \r
2155     if( HTTPS_FAILED( pHttpsRequest->bodyTxStatus ) )\r
2156     {\r
2157         IotLogError( "Failed to send the headers and body over the network during the writeCallback. Error code: %d.",\r
2158                      status );\r
2159         HTTPS_SET_AND_GOTO_CLEANUP( pHttpsRequest->bodyTxStatus );\r
2160     }\r
2161 \r
2162     if( pHttpsRequest->cancelled == true )\r
2163     {\r
2164         IotLogDebug( "Request ID: %d was cancelled.", pHttpsRequest );\r
2165         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_SEND_ABORT );\r
2166     }\r
2167 \r
2168     /* If this is a synchronous request then the header and body were configured beforehand. The header and body\r
2169      * are sent now. For an asynchronous request, the header and body are sent in IotHttpsClient_WriteRequestBody()\r
2170      * which is to be invoked in #IotHttpsClientCallbacks_t.writeCallback(). If the application never invokes\r
2171      * IotHttpsClient_WriteRequestBody(), then pHttpsRequest->pBody will be NULL. In this case we still want to\r
2172      * send whatever headers we have.  */\r
2173     if( ( pHttpsRequest->isAsync == false ) ||\r
2174         ( ( pHttpsRequest->isAsync ) && ( pHttpsRequest->pBody == NULL ) ) )\r
2175     {\r
2176         status = _sendHttpsHeadersAndBody( pHttpsConnection, pHttpsRequest );\r
2177 \r
2178         if( HTTPS_FAILED( status ) )\r
2179         {\r
2180             IotLogError( "Failed to send the headers and body on the network. Error code: %d", status );\r
2181             HTTPS_GOTO_CLEANUP();\r
2182         }\r
2183     }\r
2184 \r
2185     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
2186 \r
2187     /* The request has finished sending. This indicates to the network receive callback that the request was\r
2188      * finished, so a response received on the network is valid. This also lets a possible application called\r
2189      * IotHttpsClient_Disconnect() know that the connection is not busy, so the connection can be destroyed. */\r
2190     pHttpsResponse->reqFinishedSending = true;\r
2191 \r
2192     if( HTTPS_FAILED( status ) )\r
2193     {\r
2194         /* If the headers or body failed to send, then there should be no response expected from the server. */\r
2195         /* Cancel the response incase there is a response from the server. */\r
2196         _cancelResponse( pHttpsResponse );\r
2197         IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
2198 \r
2199         if( IotLink_IsLinked( &( pHttpsResponse->link ) ) )\r
2200         {\r
2201             IotDeQueue_Remove( &( pHttpsResponse->link ) );\r
2202         }\r
2203 \r
2204         IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
2205 \r
2206         /* Set the error status in the sync workflow. */\r
2207         pHttpsResponse->syncStatus = status;\r
2208 \r
2209         /* Return the error status or cancel status to the application for an asynchronous workflow. */\r
2210         if( pHttpsRequest->isAsync && pHttpsRequest->pCallbacks->errorCallback )\r
2211         {\r
2212             pHttpsRequest->pCallbacks->errorCallback( pHttpsRequest->pUserPrivData, pHttpsRequest, NULL, status );\r
2213         }\r
2214 \r
2215         /* We close the connection on all network errors. All network errors in receiving the response, close the\r
2216          * connection. For consistency in behavior, if there is a network error in send, the connection should also be\r
2217          * closed. */\r
2218         if( status == IOT_HTTPS_NETWORK_ERROR )\r
2219         {\r
2220             IotLogDebug( "Disconnecting request %d.", pHttpsRequest );\r
2221             disconnectStatus = IotHttpsClient_Disconnect( pHttpsConnection );\r
2222 \r
2223             if( pHttpsRequest->isAsync && pHttpsRequest->pCallbacks->connectionClosedCallback )\r
2224             {\r
2225                 pHttpsRequest->pCallbacks->connectionClosedCallback( pHttpsRequest->pUserPrivData,\r
2226                                                                      pHttpsConnection,\r
2227                                                                      disconnectStatus );\r
2228             }\r
2229 \r
2230             if( HTTPS_FAILED( disconnectStatus ) )\r
2231             {\r
2232                 IotLogWarn( "Failed to disconnect request %d. Error code: %d.", pHttpsRequest, disconnectStatus );\r
2233             }\r
2234         }\r
2235         else\r
2236         {\r
2237             /* Because this request failed, the network receive callback may never be invoked to schedule other possible\r
2238              * requests in the queue. In order to avoid requests never getting scheduled on a connected connection,\r
2239              * the first item in the queue is scheduled if it can be. */\r
2240             IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
2241 \r
2242             /* Get the next item in the queue by removing this current (which is the first) and peeking at the head\r
2243              * again. */\r
2244             IotDeQueue_Remove( &( pHttpsRequest->link ) );\r
2245             pQItem = IotDeQueue_PeekHead( &( pHttpsConnection->reqQ ) );\r
2246             /* This current request is put back because it is removed again for all cases at the end of this routine. */\r
2247             IotDeQueue_EnqueueHead( &( pHttpsConnection->reqQ ), &( pHttpsRequest->link ) );\r
2248             IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
2249 \r
2250             if( pQItem != NULL )\r
2251             {\r
2252                 /* Set this next request to send. */\r
2253                 pNextHttpsRequest = IotLink_Container( _httpsRequest_t, pQItem, link );\r
2254 \r
2255                 if( pNextHttpsRequest->scheduled == false )\r
2256                 {\r
2257                     IotLogDebug( "Request %d is next in the queue. Now scheduling a task to send the request.", pNextHttpsRequest );\r
2258                     scheduleStatus = _scheduleHttpsRequestSend( pNextHttpsRequest );\r
2259 \r
2260                     /* If there was an error with scheduling the new task, then report it. */\r
2261                     if( HTTPS_FAILED( scheduleStatus ) )\r
2262                     {\r
2263                         IotLogError( "Error scheduling HTTPS request %d. Error code: %d", pNextHttpsRequest, scheduleStatus );\r
2264 \r
2265                         if( pNextHttpsRequest->isAsync && pNextHttpsRequest->pCallbacks->errorCallback )\r
2266                         {\r
2267                             pNextHttpsRequest->pCallbacks->errorCallback( pNextHttpsRequest->pUserPrivData, pNextHttpsRequest, NULL, scheduleStatus );\r
2268                         }\r
2269                         else\r
2270                         {\r
2271                             pNextHttpsRequest->pHttpsResponse->syncStatus = scheduleStatus;\r
2272                         }\r
2273                     }\r
2274                 }\r
2275             }\r
2276         }\r
2277 \r
2278         /* Post to the response finished semaphore to unlock the application waiting on a synchronous request. */\r
2279         if( pHttpsRequest->isAsync == false )\r
2280         {\r
2281             IotSemaphore_Post( &( pHttpsResponse->respFinishedSem ) );\r
2282         }\r
2283         else if( pHttpsRequest->pCallbacks->responseCompleteCallback )\r
2284         {\r
2285             /* Call the response complete callback. We always call this even if we did not receive the response to\r
2286              * let the application know that the request has completed. */\r
2287             pHttpsRequest->pCallbacks->responseCompleteCallback( pHttpsRequest->pUserPrivData, NULL, status, 0 );\r
2288         }\r
2289     }\r
2290 \r
2291     IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
2292     /* Now that the current request is finished, we dequeue the current request from the queue. */\r
2293     IotDeQueue_DequeueHead( &( pHttpsConnection->reqQ ) );\r
2294     IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
2295 \r
2296     /* This routine returns a void so there is no HTTPS_FUNCTION_CLEANUP_END();. */\r
2297 }\r
2298 \r
2299 /*-----------------------------------------------------------*/\r
2300 \r
2301 IotHttpsReturnCode_t _scheduleHttpsRequestSend( _httpsRequest_t * pHttpsRequest )\r
2302 {\r
2303     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2304 \r
2305     IotTaskPoolError_t taskPoolStatus = IOT_TASKPOOL_SUCCESS;\r
2306     _httpsConnection_t * pHttpsConnection = pHttpsRequest->pHttpsConnection;\r
2307 \r
2308     /* Set the request to scheduled even if scheduling fails. */\r
2309     pHttpsRequest->scheduled = true;\r
2310 \r
2311     taskPoolStatus = IotTaskPool_CreateJob( _sendHttpsRequest,\r
2312                                             ( void * ) ( pHttpsRequest ),\r
2313                                             &( pHttpsConnection->taskPoolJobStorage ),\r
2314                                             &( pHttpsConnection->taskPoolJob ) );\r
2315 \r
2316     /* Creating a task pool job should never fail when parameters are valid. */\r
2317     if( taskPoolStatus != IOT_TASKPOOL_SUCCESS )\r
2318     {\r
2319         IotLogError( "Error creating a taskpool job for request servicing. Error code: %d", taskPoolStatus );\r
2320         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INTERNAL_ERROR );\r
2321     }\r
2322 \r
2323     taskPoolStatus = IotTaskPool_Schedule( IOT_SYSTEM_TASKPOOL, pHttpsConnection->taskPoolJob, 0 );\r
2324 \r
2325     if( taskPoolStatus != IOT_TASKPOOL_SUCCESS )\r
2326     {\r
2327         IotLogError( "Failed to schedule taskpool job. Error code: %d", taskPoolStatus );\r
2328         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_ASYNC_SCHEDULING_ERROR );\r
2329     }\r
2330 \r
2331     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2332 }\r
2333 \r
2334 /*-----------------------------------------------------------*/\r
2335 \r
2336 IotHttpsReturnCode_t _addRequestToConnectionReqQ( _httpsRequest_t * pHttpsRequest )\r
2337 {\r
2338     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2339 \r
2340     _httpsConnection_t * pHttpsConnection = pHttpsRequest->pHttpsConnection;\r
2341     bool scheduleRequest = false;\r
2342 \r
2343     /* Log information about the request*/\r
2344     IotLogDebug( "Now queueing request %d.", pHttpsRequest );\r
2345 \r
2346     if( pHttpsRequest->isNonPersistent )\r
2347     {\r
2348         IotLogDebug( "Request %d is non-persistent.", pHttpsRequest );\r
2349     }\r
2350     else\r
2351     {\r
2352         IotLogDebug( "Request %d is persistent. ", pHttpsRequest );\r
2353     }\r
2354 \r
2355     if( pHttpsRequest->isAsync )\r
2356     {\r
2357         IotLogDebug( " Request %d is asynchronous.", pHttpsRequest );\r
2358     }\r
2359     else\r
2360     {\r
2361         IotLogDebug( " Request %d is synchronous.", pHttpsRequest );\r
2362     }\r
2363 \r
2364     /* This is a new request and has not been scheduled if this routine is called. */\r
2365     pHttpsRequest->scheduled = false;\r
2366 \r
2367     /* Place the request into the queue. */\r
2368     IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
2369 \r
2370     /* If there is an active response, scheduling the next request at the same time may corrupt the workflow. Part of\r
2371      * the next response for the next request may be present in the currently receiving response's buffers. To avoid\r
2372      * this, check if there are pending responses to determine if this request should be scheduled right away or not.\r
2373      *\r
2374      * If there are other requests in the queue, and there are responses in the queue, then the network receive callback\r
2375      * will handle scheduling the next requests (or is already scheduled and currently sending). */\r
2376     if( ( IotDeQueue_IsEmpty( &( pHttpsConnection->reqQ ) ) ) &&\r
2377         ( IotDeQueue_IsEmpty( &( pHttpsConnection->respQ ) ) ) )\r
2378     {\r
2379         scheduleRequest = true;\r
2380     }\r
2381 \r
2382     /* Place into the connection's request to have a taskpool worker schedule to serve it later. */\r
2383     IotDeQueue_EnqueueTail( &( pHttpsConnection->reqQ ), &( pHttpsRequest->link ) );\r
2384     IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
2385 \r
2386     if( scheduleRequest )\r
2387     {\r
2388         /* This routine schedules a task pool worker to send the request. If a worker is available immediately, then\r
2389          * the request is sent right away. */\r
2390         status = _scheduleHttpsRequestSend( pHttpsRequest );\r
2391 \r
2392         if( HTTPS_FAILED( status ) )\r
2393         {\r
2394             IotLogError( "Failed to schedule the request in the queue for request %d. Error code: %d", pHttpsRequest, status );\r
2395 \r
2396             /* If we fail to schedule the only request in the queue we should remove it. */\r
2397             IotMutex_Lock( &( pHttpsConnection->connectionMutex ) );\r
2398             IotDeQueue_Remove( &( pHttpsRequest->link ) );\r
2399             IotMutex_Unlock( &( pHttpsConnection->connectionMutex ) );\r
2400 \r
2401             HTTPS_GOTO_CLEANUP();\r
2402         }\r
2403     }\r
2404 \r
2405     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2406 }\r
2407 \r
2408 /*-----------------------------------------------------------*/\r
2409 \r
2410 static void _cancelRequest( _httpsRequest_t * pHttpsRequest )\r
2411 {\r
2412     pHttpsRequest->cancelled = true;\r
2413 }\r
2414 \r
2415 /*-----------------------------------------------------------*/\r
2416 \r
2417 static void _cancelResponse( _httpsResponse_t * pHttpsResponse )\r
2418 {\r
2419     pHttpsResponse->cancelled = true;\r
2420 }\r
2421 \r
2422 /*-----------------------------------------------------------*/\r
2423 \r
2424 IotHttpsReturnCode_t IotHttpsClient_Init( void )\r
2425 {\r
2426     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2427 \r
2428     /* This sets all member in the _httpParserSettings to zero. It does not return any errors. */\r
2429     http_parser_settings_init( &_httpParserSettings );\r
2430 \r
2431     /* Set the http-parser callbacks. */\r
2432     _httpParserSettings.on_message_begin = _httpParserOnMessageBeginCallback;\r
2433     _httpParserSettings.on_status = _httpParserOnStatusCallback;\r
2434     _httpParserSettings.on_header_field = _httpParserOnHeaderFieldCallback;\r
2435     _httpParserSettings.on_header_value = _httpParserOnHeaderValueCallback;\r
2436     _httpParserSettings.on_headers_complete = _httpParserOnHeadersCompleteCallback;\r
2437     _httpParserSettings.on_body = _httpParserOnBodyCallback;\r
2438     _httpParserSettings.on_message_complete = _httpParserOnMessageCompleteCallback;\r
2439 \r
2440 /* This code prints debugging information and is, therefore, compiled only when\r
2441  * log level is set to IOT_LOG_DEBUG. */\r
2442     #if ( LIBRARY_LOG_LEVEL == IOT_LOG_DEBUG )\r
2443         _httpParserSettings.on_chunk_header = _httpParserOnChunkHeaderCallback;\r
2444         _httpParserSettings.on_chunk_complete = _httpParserOnChunkCompleteCallback;\r
2445     #endif\r
2446     HTTPS_GOTO_CLEANUP();\r
2447     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2448 }\r
2449 \r
2450 /*-----------------------------------------------------------*/\r
2451 \r
2452 static IotHttpsReturnCode_t _initializeResponse( IotHttpsResponseHandle_t * pRespHandle,\r
2453                                                  IotHttpsResponseInfo_t * pRespInfo,\r
2454                                                  _httpsRequest_t * pHttpsRequest )\r
2455 {\r
2456     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2457 \r
2458     _httpsResponse_t * pHttpsResponse = NULL;\r
2459 \r
2460     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pRespInfo->userBuffer.pBuffer );\r
2461 \r
2462     /* Check of the user buffer is large enough for the response context + default headers. */\r
2463     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( pRespInfo->userBuffer.bufferLen >= responseUserBufferMinimumSize,\r
2464                                          IOT_HTTPS_INSUFFICIENT_MEMORY,\r
2465                                          "Buffer size is too small to initialize the response context. User buffer size: %d, required minimum size; %d.",\r
2466                                          pRespInfo->userBuffer.bufferLen,\r
2467                                          responseUserBufferMinimumSize );\r
2468 \r
2469     /* Initialize the corresponding response to this request. */\r
2470     pHttpsResponse = ( _httpsResponse_t * ) ( pRespInfo->userBuffer.pBuffer );\r
2471 \r
2472     /* Clear out the response user buffer. This is important because we\r
2473      * give the whole buffer to the parser as opposed to the actual content\r
2474      * length and rely on the parser to stop when a complete HTTP response\r
2475      * is found. To make sure that any data in the buffer which is not part\r
2476      * of the received HTTP response, does not get interpreted as part of\r
2477      * the HTTP repose, we zero out the buffer here. */\r
2478     memset( pRespInfo->userBuffer.pBuffer, 0, pRespInfo->userBuffer.bufferLen );\r
2479 \r
2480     pHttpsResponse->pHeaders = ( uint8_t * ) ( pHttpsResponse ) + sizeof( _httpsResponse_t );\r
2481     pHttpsResponse->pHeadersEnd = ( uint8_t * ) ( pHttpsResponse ) + pRespInfo->userBuffer.bufferLen;\r
2482     pHttpsResponse->pHeadersCur = pHttpsResponse->pHeaders;\r
2483 \r
2484     if( pHttpsRequest->isAsync )\r
2485     {\r
2486         pHttpsResponse->isAsync = true;\r
2487 \r
2488         /* For an asynchronous request the response body is provided by the application in the\r
2489          * IotHttpsCallbacks_t.readReadyCallback(). These pointers will be updated when IotHttpsClient_ReadResponseBody()\r
2490          * is invoked. */\r
2491         pHttpsResponse->pBody = NULL;\r
2492         pHttpsResponse->pBodyCur = NULL;\r
2493         pHttpsResponse->pBodyEnd = NULL;\r
2494 \r
2495         pHttpsResponse->pCallbacks = pHttpsRequest->pCallbacks;\r
2496         pHttpsResponse->pUserPrivData = pHttpsRequest->pUserPrivData;\r
2497     }\r
2498     else\r
2499     {\r
2500         pHttpsResponse->isAsync = false;\r
2501         /* The request body pointer is allowed to be NULL. u.pSyncInfo was checked for NULL earlier in this function. */\r
2502         pHttpsResponse->pBody = pRespInfo->pSyncInfo->pBody;\r
2503         pHttpsResponse->pBodyCur = pHttpsResponse->pBody;\r
2504         pHttpsResponse->pBodyEnd = pHttpsResponse->pBody + pRespInfo->pSyncInfo->bodyLen;\r
2505 \r
2506         /* Clear out the body bufffer. This is important because we give the\r
2507          * whole buffer to the parser as opposed to the actual content length and\r
2508          * rely on the parser to stop when a complete HTTP response is found. To\r
2509          * make sure that any data in the buffer which is not part of the received\r
2510          * HTTP response, does not get interpreted as part of the HTTP repose, we\r
2511          * zero out the buffer here. */\r
2512         memset( pRespInfo->pSyncInfo->pBody, 0, pRespInfo->pSyncInfo->bodyLen );\r
2513     }\r
2514 \r
2515     /* Reinitialize the parser and set the fill buffer state to empty. This does not return any errors. */\r
2516     http_parser_init( &( pHttpsResponse->httpParserInfo.responseParser ), HTTP_RESPONSE );\r
2517     http_parser_init( &( pHttpsResponse->httpParserInfo.readHeaderParser ), HTTP_RESPONSE );\r
2518     /* Set the third party http parser function. */\r
2519     pHttpsResponse->httpParserInfo.parseFunc = http_parser_execute;\r
2520     pHttpsResponse->httpParserInfo.readHeaderParser.data = ( void * ) ( pHttpsResponse );\r
2521     pHttpsResponse->httpParserInfo.responseParser.data = ( void * ) ( pHttpsResponse );\r
2522 \r
2523     pHttpsResponse->status = 0;\r
2524     pHttpsResponse->method = pHttpsRequest->method;\r
2525     pHttpsResponse->parserState = PARSER_STATE_NONE;\r
2526     pHttpsResponse->bufferProcessingState = PROCESSING_STATE_NONE;\r
2527     pHttpsResponse->pReadHeaderField = NULL;\r
2528     pHttpsResponse->readHeaderFieldLength = 0;\r
2529     pHttpsResponse->pReadHeaderValue = NULL;\r
2530     pHttpsResponse->readHeaderValueLength = 0;\r
2531     pHttpsResponse->foundHeaderField = 0;\r
2532     pHttpsResponse->pHttpsConnection = NULL;\r
2533 \r
2534     pHttpsResponse->pBodyInHeaderBuf = NULL;\r
2535     pHttpsResponse->pBodyCurInHeaderBuf = NULL;\r
2536     pHttpsResponse->bodyRxStatus = IOT_HTTPS_OK;\r
2537     pHttpsResponse->cancelled = false;\r
2538     pHttpsResponse->syncStatus = IOT_HTTPS_OK;\r
2539     /* There is no request associated with this response right now, so it is finished sending. */\r
2540     pHttpsResponse->reqFinishedSending = true;\r
2541     pHttpsResponse->isNonPersistent = pHttpsRequest->isNonPersistent;\r
2542 \r
2543     /* Set the response handle to return. */\r
2544     *pRespHandle = pHttpsResponse;\r
2545 \r
2546     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
2547 \r
2548     if( HTTPS_FAILED( status ) )\r
2549     {\r
2550         pRespHandle = NULL;\r
2551     }\r
2552 \r
2553     HTTPS_FUNCTION_CLEANUP_END();\r
2554 }\r
2555 \r
2556 /*-----------------------------------------------------------*/\r
2557 \r
2558 void IotHttpsClient_Cleanup( void )\r
2559 {\r
2560     /* There is nothing to clean up here as of now. */\r
2561 }\r
2562 \r
2563 /* --------------------------------------------------------- */\r
2564 \r
2565 IotHttpsReturnCode_t IotHttpsClient_Connect( IotHttpsConnectionHandle_t * pConnHandle,\r
2566                                              IotHttpsConnectionInfo_t * pConnInfo )\r
2567 {\r
2568     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2569 \r
2570     /* Check for NULL parameters in a public API. */\r
2571     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pConnHandle );\r
2572     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pConnInfo );\r
2573 \r
2574     /* If a valid connection handle is passed in. */\r
2575     if( *pConnHandle != NULL )\r
2576     {\r
2577         /* If the handle in a connected state, then we want to disconnect before reconnecting. The ONLY way to put the\r
2578          * handle is a disconnect state is to call IotHttpsClient_Disconnect(). */\r
2579         if( ( *pConnHandle )->isConnected )\r
2580         {\r
2581             status = IotHttpsClient_Disconnect( *pConnHandle );\r
2582 \r
2583             if( HTTPS_FAILED( status ) )\r
2584             {\r
2585                 IotLogError( "Error disconnecting a connected *pConnHandle passed to IotHttpsClient_Connect().Error code %d", status );\r
2586                 *pConnHandle = NULL;\r
2587                 HTTPS_GOTO_CLEANUP();\r
2588             }\r
2589         }\r
2590     }\r
2591 \r
2592     /* Connect to the server now. Initialize all resources needed for the connection context as well here. */\r
2593     status = _createHttpsConnection( pConnHandle, pConnInfo );\r
2594 \r
2595     if( HTTPS_FAILED( status ) )\r
2596     {\r
2597         IotLogError( "Error in IotHttpsClient_Connect(). Error code %d.", status );\r
2598     }\r
2599 \r
2600     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2601 }\r
2602 \r
2603 /*-----------------------------------------------------------*/\r
2604 \r
2605 IotHttpsReturnCode_t IotHttpsClient_Disconnect( IotHttpsConnectionHandle_t connHandle )\r
2606 {\r
2607     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2608 \r
2609     _httpsRequest_t * pHttpsRequest = NULL;\r
2610     _httpsResponse_t * pHttpsResponse = NULL;\r
2611     IotLink_t * pRespItem = NULL;\r
2612     IotLink_t * pReqItem = NULL;\r
2613 \r
2614     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( connHandle );\r
2615 \r
2616     /* If this routine is currently is progress by another thread, for instance the taskpool worker that received a\r
2617      * network error after sending, then return right away because connection resources are being used. */\r
2618     if( IotMutex_TryLock( &( connHandle->connectionMutex ) ) == false )\r
2619     {\r
2620         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_BUSY );\r
2621     }\r
2622 \r
2623     /* Do not attempt to disconnect an already disconnected connection.\r
2624      * It can happen when a user calls this functions and we return IOT_HTTPS_BUSY. */\r
2625     if( connHandle->isConnected )\r
2626     {\r
2627         /* Mark the network as disconnected whether the disconnect passes or not. */\r
2628         connHandle->isConnected = false;\r
2629         _networkDisconnect( connHandle );\r
2630     }\r
2631 \r
2632     /* If there is a response in the connection's response queue and the associated request has not finished sending,\r
2633      * then we cannot destroy the connection until it finishes. */\r
2634     pRespItem = IotDeQueue_DequeueHead( &( connHandle->respQ ) );\r
2635 \r
2636     if( pRespItem != NULL )\r
2637     {\r
2638         pHttpsResponse = IotLink_Container( _httpsResponse_t, pRespItem, link );\r
2639 \r
2640         if( pHttpsResponse->reqFinishedSending == false )\r
2641         {\r
2642             IotLogError( "Connection is in use. Disconnected, but cannot destroy the connection." );\r
2643             status = IOT_HTTPS_BUSY;\r
2644 \r
2645             /* The request is busy, to as quickly as possible allow a successful retry call of this function we must\r
2646              * cancel the busy request which is the first in the queue. */\r
2647             pReqItem = IotDeQueue_PeekHead( &( connHandle->reqQ ) );\r
2648 \r
2649             if( pReqItem != NULL )\r
2650             {\r
2651                 pHttpsRequest = IotLink_Container( _httpsRequest_t, pReqItem, link );\r
2652                 _cancelRequest( pHttpsRequest );\r
2653             }\r
2654 \r
2655             /* We set the status as busy, but we do not goto the cleanup right away because we still want to remove\r
2656              * all pending requests. */\r
2657         }\r
2658 \r
2659         /* Delete all possible pending responses. (This is defensive.) */\r
2660         IotDeQueue_RemoveAll( &( connHandle->respQ ), NULL, 0 );\r
2661 \r
2662         /* Put the response that was dequeued back so that the application can call this function again to check later\r
2663          * that is exited and marked itself as finished sending.\r
2664          * If during the last check and this check reqFinishedSending gets set to true, that is OK because on the next\r
2665          * call to this routine, the disconnect will succeed. */\r
2666         if( pHttpsResponse->reqFinishedSending == false )\r
2667         {\r
2668             IotDeQueue_EnqueueHead( &( connHandle->respQ ), pRespItem );\r
2669         }\r
2670     }\r
2671 \r
2672     /* Remove all pending requests. If this routine is called from the application context and there is a\r
2673      * network receive callback in process, this routine will wait in _networkDestroy until that routine returns.\r
2674      * If this is routine is called from the network receive callback context, then the destroy happens after the\r
2675      * network receive callback context returns. */\r
2676     IotDeQueue_RemoveAll( &( connHandle->reqQ ), NULL, 0 );\r
2677 \r
2678     /* Do not attempt to destroy an already destroyed connection. This can happen when the user calls this function and\r
2679      * IOT_HTTPS_BUSY is returned. */\r
2680     if( HTTPS_SUCCEEDED( status ) )\r
2681     {\r
2682         if( connHandle->isDestroyed == false )\r
2683         {\r
2684             connHandle->isDestroyed = true;\r
2685             _networkDestroy( connHandle );\r
2686         }\r
2687     }\r
2688 \r
2689     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
2690 \r
2691     /* This function is no longer in process, so disconnecting is no longer in process. This signals to the retry\r
2692      * on this function that it can proceed with the disconnecting activities. */\r
2693     if( connHandle != NULL )\r
2694     {\r
2695         IotMutex_Unlock( &( connHandle->connectionMutex ) );\r
2696     }\r
2697 \r
2698     HTTPS_FUNCTION_CLEANUP_END();\r
2699 }\r
2700 \r
2701 /*-----------------------------------------------------------*/\r
2702 \r
2703 IotHttpsReturnCode_t IotHttpsClient_InitializeRequest( IotHttpsRequestHandle_t * pReqHandle,\r
2704                                                        IotHttpsRequestInfo_t * pReqInfo )\r
2705 {\r
2706     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2707 \r
2708     _httpsRequest_t * pHttpsRequest = NULL;\r
2709     size_t additionalLength = 0;\r
2710     size_t spaceLen = 1;\r
2711     char * pSpace = " ";\r
2712     size_t httpsMethodLen = 0;\r
2713     size_t httpsProtocolVersionLen = FAST_MACRO_STRLEN( HTTPS_PROTOCOL_VERSION );\r
2714 \r
2715     /* Check for NULL parameters in the public API. */\r
2716     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pReqHandle );\r
2717     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pReqInfo );\r
2718     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pReqInfo->userBuffer.pBuffer );\r
2719     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pReqInfo->pHost );\r
2720 \r
2721     if( pReqInfo->isAsync )\r
2722     {\r
2723         HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pReqInfo->u.pAsyncInfo );\r
2724     }\r
2725     else\r
2726     {\r
2727         HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pReqInfo->u.pSyncInfo );\r
2728     }\r
2729 \r
2730     /* Check of the user buffer is large enough for the request context + default headers. */\r
2731     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( pReqInfo->userBuffer.bufferLen >= requestUserBufferMinimumSize,\r
2732                                          IOT_HTTPS_INSUFFICIENT_MEMORY,\r
2733                                          "Buffer size is too small to initialize the request context. User buffer size: %d, required minimum size; %d.",\r
2734                                          pReqInfo->userBuffer.bufferLen,\r
2735                                          requestUserBufferMinimumSize );\r
2736 \r
2737     /* Set the request contet to the start of the userbuffer. */\r
2738     pHttpsRequest = ( _httpsRequest_t * ) ( pReqInfo->userBuffer.pBuffer );\r
2739     /* Clear out the user buffer. */\r
2740     memset( pReqInfo->userBuffer.pBuffer, 0, pReqInfo->userBuffer.bufferLen );\r
2741 \r
2742     /* Set the start of the headers to the end of the request context in the user buffer. */\r
2743     pHttpsRequest->pHeaders = ( uint8_t * ) pHttpsRequest + sizeof( _httpsRequest_t );\r
2744     pHttpsRequest->pHeadersEnd = ( uint8_t * ) pHttpsRequest + pReqInfo->userBuffer.bufferLen;\r
2745     pHttpsRequest->pHeadersCur = pHttpsRequest->pHeaders;\r
2746 \r
2747     /* Get the length of the HTTP method. */\r
2748     httpsMethodLen = strlen( _pHttpsMethodStrings[ pReqInfo->method ] );\r
2749 \r
2750     /* Add the request line to the header buffer. */\r
2751     additionalLength = httpsMethodLen +          \\r
2752                        spaceLen +                \\r
2753                        pReqInfo->pathLen +       \\r
2754                        spaceLen +                \\r
2755                        httpsProtocolVersionLen + \\r
2756                        HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH;\r
2757 \r
2758     if( ( additionalLength + pHttpsRequest->pHeadersCur ) > ( pHttpsRequest->pHeadersEnd ) )\r
2759     {\r
2760         IotLogError( "Request line does not fit into the request user buffer: \"%s %.*s HTTP/1.1\\r\\n\" . ",\r
2761                      _pHttpsMethodStrings[ pReqInfo->method ],\r
2762                      pReqInfo->pathLen,\r
2763                      pReqInfo->pPath );\r
2764         IotLogError( "The length needed is %d and the space available is %d.", additionalLength, pHttpsRequest->pHeadersEnd - pHttpsRequest->pHeadersCur );\r
2765         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INSUFFICIENT_MEMORY );\r
2766     }\r
2767 \r
2768     /* Write "<METHOD> <PATH> HTTP/1.1\r\n" to the start of the header space. */\r
2769     memcpy( pHttpsRequest->pHeadersCur, _pHttpsMethodStrings[ pReqInfo->method ], httpsMethodLen );\r
2770     pHttpsRequest->pHeadersCur += httpsMethodLen;\r
2771     memcpy( pHttpsRequest->pHeadersCur, pSpace, spaceLen );\r
2772     pHttpsRequest->pHeadersCur += spaceLen;\r
2773 \r
2774     if( pReqInfo->pPath == NULL )\r
2775     {\r
2776         pReqInfo->pPath = HTTPS_EMPTY_PATH;\r
2777         pReqInfo->pathLen = FAST_MACRO_STRLEN( HTTPS_EMPTY_PATH );\r
2778     }\r
2779 \r
2780     memcpy( pHttpsRequest->pHeadersCur, pReqInfo->pPath, pReqInfo->pathLen );\r
2781     pHttpsRequest->pHeadersCur += pReqInfo->pathLen;\r
2782     memcpy( pHttpsRequest->pHeadersCur, pSpace, spaceLen );\r
2783     pHttpsRequest->pHeadersCur += spaceLen;\r
2784     memcpy( pHttpsRequest->pHeadersCur, HTTPS_PROTOCOL_VERSION, httpsProtocolVersionLen );\r
2785     pHttpsRequest->pHeadersCur += httpsProtocolVersionLen;\r
2786     memcpy( pHttpsRequest->pHeadersCur, HTTPS_END_OF_HEADER_LINES_INDICATOR, HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH );\r
2787     pHttpsRequest->pHeadersCur += HTTPS_END_OF_HEADER_LINES_INDICATOR_LENGTH;\r
2788 \r
2789     /* Add the User-Agent header. */\r
2790     status = _addHeader( pHttpsRequest, HTTPS_USER_AGENT_HEADER, FAST_MACRO_STRLEN( HTTPS_USER_AGENT_HEADER ), IOT_HTTPS_USER_AGENT, FAST_MACRO_STRLEN( IOT_HTTPS_USER_AGENT ) );\r
2791 \r
2792     if( HTTPS_FAILED( status ) )\r
2793     {\r
2794         IotLogError( "Failed to write header to the request user buffer: \"User-Agent: %s\\r\\n\" . Error code: %d",\r
2795                      IOT_HTTPS_USER_AGENT,\r
2796                      status );\r
2797         HTTPS_GOTO_CLEANUP();\r
2798     }\r
2799 \r
2800     status = _addHeader( pHttpsRequest, HTTPS_HOST_HEADER, FAST_MACRO_STRLEN( HTTPS_HOST_HEADER ), pReqInfo->pHost, pReqInfo->hostLen );\r
2801 \r
2802     if( HTTPS_FAILED( status ) )\r
2803     {\r
2804         IotLogError( "Failed to write \"Host: %.*s\\r\\n\" to the request user buffer. Error code: %d",\r
2805                      pReqInfo->hostLen,\r
2806                      pReqInfo->pHost,\r
2807                      status );\r
2808         HTTPS_GOTO_CLEANUP();\r
2809     }\r
2810 \r
2811     if( pReqInfo->isAsync )\r
2812     {\r
2813         pHttpsRequest->isAsync = true;\r
2814         /* If this is an asynchronous request then save the callbacks to use. */\r
2815         pHttpsRequest->pCallbacks = &( pReqInfo->u.pAsyncInfo->callbacks );\r
2816         pHttpsRequest->pUserPrivData = pReqInfo->u.pAsyncInfo->pPrivData;\r
2817         /* The body pointer and body length will be filled in when the application sends data in the writeCallback. */\r
2818         pHttpsRequest->pBody = NULL;\r
2819         pHttpsRequest->bodyLength = 0;\r
2820     }\r
2821     else\r
2822     {\r
2823         pHttpsRequest->isAsync = false;\r
2824         /* Set the HTTP request entity body. This is allowed to be NULL for no body like for a GET request. */\r
2825         pHttpsRequest->pBody = pReqInfo->u.pSyncInfo->pBody;\r
2826         pHttpsRequest->bodyLength = pReqInfo->u.pSyncInfo->bodyLen;\r
2827     }\r
2828 \r
2829     /* Save the method of this request. */\r
2830     pHttpsRequest->method = pReqInfo->method;\r
2831     /* Set the connection persistence flag for keeping the connection open after receiving a response. */\r
2832     pHttpsRequest->isNonPersistent = pReqInfo->isNonPersistent;\r
2833     /* Initialize the request cancellation. */\r
2834     pHttpsRequest->cancelled = false;\r
2835     /* Initialize the status of sending the body over the network in a possible asynchronous request. */\r
2836     pHttpsRequest->bodyTxStatus = IOT_HTTPS_OK;\r
2837     /* This is a new request and therefore not scheduled yet. */\r
2838     pHttpsRequest->scheduled = false;\r
2839 \r
2840     /* Set the request handle to return. */\r
2841     *pReqHandle = pHttpsRequest;\r
2842 \r
2843     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
2844 \r
2845     if( HTTPS_FAILED( status ) && ( pReqHandle != NULL ) )\r
2846     {\r
2847         /* Set the request handle to return to NULL, if we failed anywhere. */\r
2848         *pReqHandle = NULL;\r
2849     }\r
2850 \r
2851     HTTPS_FUNCTION_CLEANUP_END();\r
2852 }\r
2853 \r
2854 /*-----------------------------------------------------------*/\r
2855 \r
2856 IotHttpsReturnCode_t IotHttpsClient_AddHeader( IotHttpsRequestHandle_t reqHandle,\r
2857                                                char * pName,\r
2858                                                uint32_t nameLen,\r
2859                                                char * pValue,\r
2860                                                uint32_t valueLen )\r
2861 {\r
2862     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2863 \r
2864     /* Check for NULL pointer paramters. */\r
2865     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pName );\r
2866     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pValue );\r
2867     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( reqHandle );\r
2868 \r
2869     /* Check for name long enough for header length calculation to overflow */\r
2870     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( nameLen <= ( UINT32_MAX >> 2 ),\r
2871                                          IOT_HTTPS_INVALID_PARAMETER,\r
2872                                          "Attempting to generate headers with name length %d > %d. This is not allowed.",\r
2873                                          nameLen, UINT32_MAX >> 2 );\r
2874 \r
2875     /* Check for value long enough for header length calculation to overflow */\r
2876     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( valueLen <= ( UINT32_MAX >> 2 ),\r
2877                                          IOT_HTTPS_INVALID_PARAMETER,\r
2878                                          "Attempting to generate headers with value length %d > %d. This is not allowed.",\r
2879                                          valueLen, UINT32_MAX >> 2 );\r
2880 \r
2881     /* Check for auto-generated header "Content-Length". This header is created and send automatically when right before\r
2882      * request body is sent on the network. */\r
2883     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( strncmp( pName, HTTPS_CONTENT_LENGTH_HEADER, FAST_MACRO_STRLEN( HTTPS_CONTENT_LENGTH_HEADER ) ) != 0,\r
2884                                          IOT_HTTPS_INVALID_PARAMETER,\r
2885                                          "Attempting to add auto-generated header %s. This is not allowed.",\r
2886                                          HTTPS_CONTENT_LENGTH_HEADER );\r
2887 \r
2888     /* Check for auto-generated header "Connection". This header is created and send automatically when right before\r
2889      * request body is sent on the network. */\r
2890     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( strncmp( pName, HTTPS_CONNECTION_HEADER, FAST_MACRO_STRLEN( HTTPS_CONNECTION_HEADER ) ) != 0,\r
2891                                          IOT_HTTPS_INVALID_PARAMETER,\r
2892                                          "Attempting to add auto-generated header %s. This is not allowed.",\r
2893                                          HTTPS_CONNECTION_HEADER );\r
2894 \r
2895     /* Check for auto-generated header "Host". This header is created and placed into the header buffer space\r
2896      * in IotHttpsClient_InitializeRequest(). */\r
2897     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( strncmp( pName, HTTPS_HOST_HEADER, FAST_MACRO_STRLEN( HTTPS_HOST_HEADER ) ) != 0,\r
2898                                          IOT_HTTPS_INVALID_PARAMETER,\r
2899                                          "Attempting to add auto-generated header %s. This is not allowed.",\r
2900                                          HTTPS_HOST_HEADER );\r
2901 \r
2902     /* Check for auto-generated header "User-Agent". This header is created and placed into the header buffer space\r
2903      * in IotHttpsClient_InitializeRequest(). */\r
2904     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( strncmp( pName, HTTPS_USER_AGENT_HEADER, FAST_MACRO_STRLEN( HTTPS_USER_AGENT_HEADER ) ) != 0,\r
2905                                          IOT_HTTPS_INVALID_PARAMETER,\r
2906                                          "Attempting to add auto-generated header %s. This is not allowed.",\r
2907                                          HTTPS_USER_AGENT_HEADER );\r
2908 \r
2909 \r
2910     status = _addHeader( reqHandle, pName, nameLen, pValue, valueLen );\r
2911 \r
2912     if( HTTPS_FAILED( status ) )\r
2913     {\r
2914         IotLogError( "Error in IotHttpsClient_AddHeader(), error code %d.", status );\r
2915         HTTPS_GOTO_CLEANUP();\r
2916     }\r
2917 \r
2918     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
2919 }\r
2920 \r
2921 /*-----------------------------------------------------------*/\r
2922 \r
2923 IotHttpsReturnCode_t IotHttpsClient_SendSync( IotHttpsConnectionHandle_t connHandle,\r
2924                                               IotHttpsRequestHandle_t reqHandle,\r
2925                                               IotHttpsResponseHandle_t * pRespHandle,\r
2926                                               IotHttpsResponseInfo_t * pRespInfo,\r
2927                                               uint32_t timeoutMs )\r
2928 {\r
2929     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
2930 \r
2931     bool respFinishedSemCreated = false;\r
2932     _httpsResponse_t * pHttpsResponse = NULL;\r
2933 \r
2934     /* Parameter checks. */\r
2935     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( connHandle );\r
2936     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( reqHandle );\r
2937     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pRespHandle );\r
2938     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pRespInfo );\r
2939     /* Stop the application from scheduling requests on a closed connection. */\r
2940     HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( connHandle->isConnected );\r
2941 \r
2942     /* If an asynchronous request/response is configured, that is invalid for this API. */\r
2943     if( reqHandle->isAsync )\r
2944     {\r
2945         IotLogError( "Called IotHttpsClient_SendSync on an asynchronous configured request." );\r
2946         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INVALID_PARAMETER );\r
2947     }\r
2948 \r
2949     /* Initialize the response handle to return. */\r
2950     status = _initializeResponse( pRespHandle, pRespInfo, reqHandle );\r
2951 \r
2952     if( HTTPS_FAILED( status ) )\r
2953     {\r
2954         IotLogError( "Failed to initialize the response on the synchronous request %d.", reqHandle );\r
2955         HTTPS_GOTO_CLEANUP();\r
2956     }\r
2957 \r
2958     /* Set the internal response to use. */\r
2959     pHttpsResponse = *pRespHandle;\r
2960 \r
2961     /* The implicit connection passed and we need to the set the connection handle in the request and response. */\r
2962     reqHandle->pHttpsConnection = connHandle;\r
2963     pHttpsResponse->pHttpsConnection = connHandle;\r
2964 \r
2965     /* Create the semaphore used to wait on the response to finish being received. */\r
2966     respFinishedSemCreated = IotSemaphore_Create( &( pHttpsResponse->respFinishedSem ), 0 /* initialValue */, 1 /* maxValue */ );\r
2967 \r
2968     if( respFinishedSemCreated == false )\r
2969     {\r
2970         IotLogError( "Failed to create an internal semaphore." );\r
2971         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INTERNAL_ERROR );\r
2972     }\r
2973 \r
2974     /* Associate the response to the request so that we can schedule it to be received when the request gets scheduled to send. */\r
2975     reqHandle->pHttpsResponse = pHttpsResponse;\r
2976 \r
2977     /* Schedule this request to be sent by adding it to the connection's request queue. */\r
2978     status = _addRequestToConnectionReqQ( reqHandle );\r
2979 \r
2980     if( HTTPS_FAILED( status ) )\r
2981     {\r
2982         IotLogError( "Failed to schedule the synchronous request. Error code: %d", status );\r
2983         HTTPS_GOTO_CLEANUP();\r
2984     }\r
2985 \r
2986     /* Wait for the request to finish. */\r
2987     if( timeoutMs == 0 )\r
2988     {\r
2989         IotSemaphore_Wait( &( pHttpsResponse->respFinishedSem ) );\r
2990     }\r
2991     else\r
2992     {\r
2993         if( IotSemaphore_TimedWait( &( pHttpsResponse->respFinishedSem ), timeoutMs ) == false )\r
2994         {\r
2995             IotLogError( "Timed out waiting for the synchronous request to finish. Timeout ms: %d", timeoutMs );\r
2996             _cancelRequest( reqHandle );\r
2997             HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_TIMEOUT_ERROR );\r
2998         }\r
2999     }\r
3000 \r
3001     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
3002 \r
3003     if( respFinishedSemCreated )\r
3004     {\r
3005         IotSemaphore_Destroy( &( pHttpsResponse->respFinishedSem ) );\r
3006     }\r
3007 \r
3008     /* If the syncStatus is anything other than IOT_HTTPS_OK, then the request was scheduled. */\r
3009     if( ( pHttpsResponse != NULL ) && HTTPS_FAILED( pHttpsResponse->syncStatus ) )\r
3010     {\r
3011         status = pHttpsResponse->syncStatus;\r
3012     }\r
3013 \r
3014     if( HTTPS_FAILED( status ) )\r
3015     {\r
3016         if( pRespHandle != NULL )\r
3017         {\r
3018             *pRespHandle = NULL;\r
3019         }\r
3020 \r
3021         IotLogError( "IotHttpsClient_SendSync() failed." );\r
3022     }\r
3023 \r
3024     HTTPS_FUNCTION_CLEANUP_END();\r
3025 }\r
3026 \r
3027 /*-----------------------------------------------------------*/\r
3028 \r
3029 IotHttpsReturnCode_t IotHttpsClient_WriteRequestBody( IotHttpsRequestHandle_t reqHandle,\r
3030                                                       uint8_t * pBuf,\r
3031                                                       uint32_t len,\r
3032                                                       int isComplete )\r
3033 {\r
3034     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3035 \r
3036     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( reqHandle );\r
3037     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pBuf );\r
3038 \r
3039     /* This function is not valid for a synchronous response. Applications need to configure the request body in\r
3040      * IotHttpsRequestInfo_t.pSyncInfo_t.reqData before calling IotHttpsClient_SendSync(). */\r
3041     HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( reqHandle->isAsync );\r
3042     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( isComplete == 1,\r
3043                                          IOT_HTTPS_NOT_SUPPORTED,\r
3044                                          "isComplete must be 1 in IotHttpsClient_WriteRequestBody() for the current version of the HTTPS Client library." );\r
3045 \r
3046     /* If the bodyLength is greater than 0, then we already called this function and we need to enforce that this\r
3047      * function must only be called once. We only call this function once so that we can calculate the Content-Length. */\r
3048     if( reqHandle->bodyLength > 0 )\r
3049     {\r
3050         IotLogError( "Error this function must be called once with the data needed to send. Variable length HTTP "\r
3051                      "request body is not supported in this library." );\r
3052         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_MESSAGE_FINISHED );\r
3053     }\r
3054 \r
3055     /* Set the pointer to the body and the length for the content-length calculation. */\r
3056     reqHandle->pBody = ( uint8_t * ) pBuf;\r
3057     reqHandle->bodyLength = len;\r
3058 \r
3059     /* We send the HTTPS headers and body in this function so that the application has the freedom to specify a body\r
3060      * that may be buffer on stack. */\r
3061     status = _sendHttpsHeadersAndBody( reqHandle->pHttpsConnection, reqHandle );\r
3062 \r
3063     if( HTTPS_FAILED( status ) )\r
3064     {\r
3065         IotLogError( "Failed to send the headers and body. Error code %d.", status );\r
3066         HTTPS_GOTO_CLEANUP();\r
3067     }\r
3068 \r
3069     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
3070 \r
3071     if( reqHandle != NULL )\r
3072     {\r
3073         reqHandle->bodyTxStatus = status;\r
3074     }\r
3075 \r
3076     HTTPS_FUNCTION_CLEANUP_END();\r
3077 }\r
3078 \r
3079 /*-----------------------------------------------------------*/\r
3080 \r
3081 IotHttpsReturnCode_t IotHttpsClient_ReadResponseBody( IotHttpsResponseHandle_t respHandle,\r
3082                                                       uint8_t * pBuf,\r
3083                                                       uint32_t * pLen )\r
3084 {\r
3085     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3086 \r
3087     uint32_t bodyLengthInHeaderBuf = 0;\r
3088 \r
3089     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( respHandle );\r
3090     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pBuf );\r
3091     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pLen );\r
3092     HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( respHandle->isAsync );\r
3093 \r
3094     /* Set the current body in the respHandle to use in _receiveHttpsBody(). _receiveHttpsBody is generic\r
3095      *  to both async and sync request/response handling. In the sync version the body is configured during\r
3096      *  initializing the request. In the async version the body is given in this function on the fly. */\r
3097     respHandle->pBody = pBuf;\r
3098     respHandle->pBodyCur = respHandle->pBody;\r
3099     respHandle->pBodyEnd = respHandle->pBodyCur + *pLen;\r
3100 \r
3101     /* When there is part of the body in the header pBuffer. We need to move that data to this body pBuffer\r
3102      * provided in this function. */\r
3103     bodyLengthInHeaderBuf = respHandle->pBodyCurInHeaderBuf - respHandle->pBodyInHeaderBuf;\r
3104 \r
3105     if( bodyLengthInHeaderBuf > 0 )\r
3106     {\r
3107         uint32_t copyLength = bodyLengthInHeaderBuf > *pLen ? *pLen : bodyLengthInHeaderBuf;\r
3108         memcpy( respHandle->pBodyCur, respHandle->pBodyInHeaderBuf, copyLength );\r
3109         respHandle->pBodyCur += copyLength;\r
3110 \r
3111         /* This function may be called multiple times until all of the body that may be present in the header buffer is\r
3112          * moved out. */\r
3113         respHandle->pBodyInHeaderBuf += copyLength;\r
3114     }\r
3115 \r
3116     /* If there is room in the body buffer just provided by the application and we have not completed the current\r
3117      * HTTP response message, then try to receive more body. */\r
3118     if( ( ( respHandle->pBodyEnd - respHandle->pBodyCur ) > 0 ) && ( respHandle->parserState < PARSER_STATE_BODY_COMPLETE ) )\r
3119     {\r
3120         status = _receiveHttpsBody( respHandle->pHttpsConnection, respHandle );\r
3121 \r
3122         if( HTTPS_FAILED( status ) )\r
3123         {\r
3124             IotLogError( "Failed to receive the HTTP response body on the network. Error code: %d.", status );\r
3125             HTTPS_GOTO_CLEANUP();\r
3126         }\r
3127     }\r
3128 \r
3129     *pLen = respHandle->pBodyCur - respHandle->pBody;\r
3130 \r
3131     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
3132 \r
3133     if( respHandle != NULL )\r
3134     {\r
3135         respHandle->bodyRxStatus = status;\r
3136     }\r
3137 \r
3138     HTTPS_FUNCTION_CLEANUP_END();\r
3139 }\r
3140 \r
3141 /*-----------------------------------------------------------*/\r
3142 \r
3143 IotHttpsReturnCode_t IotHttpsClient_CancelRequestAsync( IotHttpsRequestHandle_t reqHandle )\r
3144 {\r
3145     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3146 \r
3147     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( reqHandle );\r
3148 \r
3149     _cancelRequest( reqHandle );\r
3150 \r
3151     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
3152 }\r
3153 \r
3154 /*-----------------------------------------------------------*/\r
3155 \r
3156 IotHttpsReturnCode_t IotHttpsClient_CancelResponseAsync( IotHttpsResponseHandle_t respHandle )\r
3157 {\r
3158     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3159 \r
3160     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( respHandle );\r
3161 \r
3162     _cancelResponse( respHandle );\r
3163 \r
3164     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
3165 }\r
3166 \r
3167 \r
3168 /*-----------------------------------------------------------*/\r
3169 \r
3170 IotHttpsReturnCode_t IotHttpsClient_SendAsync( IotHttpsConnectionHandle_t connHandle,\r
3171                                                IotHttpsRequestHandle_t reqHandle,\r
3172                                                IotHttpsResponseHandle_t * pRespHandle,\r
3173                                                IotHttpsResponseInfo_t * pRespInfo )\r
3174 {\r
3175     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3176 \r
3177     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( connHandle );\r
3178     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( reqHandle );\r
3179     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pRespHandle );\r
3180     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pRespInfo );\r
3181     HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( reqHandle->isAsync );\r
3182     /* Stop the application from scheduling requests on a closed connection. */\r
3183     HTTPS_ON_ARG_ERROR_GOTO_CLEANUP( connHandle->isConnected );\r
3184 \r
3185     /* Initialize the response handle to return. */\r
3186     status = _initializeResponse( pRespHandle, pRespInfo, reqHandle );\r
3187 \r
3188     if( HTTPS_FAILED( status ) )\r
3189     {\r
3190         IotLogError( "Failed to initialize the response on the synchronous request %d.", reqHandle );\r
3191         HTTPS_GOTO_CLEANUP();\r
3192     }\r
3193 \r
3194     /* Set the connection handle in the request handle so that we can use it in the _writeRequestBody() callback. */\r
3195     reqHandle->pHttpsConnection = connHandle;\r
3196 \r
3197     /* Set the connection handle in the response handle sp that we can use it in the _readReadyCallback() callback. */\r
3198     ( *pRespHandle )->pHttpsConnection = connHandle;\r
3199 \r
3200     /* Associate the response to the request so that we can schedule it to be received when the request gets scheduled to send. */\r
3201     reqHandle->pHttpsResponse = *pRespHandle;\r
3202 \r
3203     /* Add the request to the connection's request queue. */\r
3204     status = _addRequestToConnectionReqQ( reqHandle );\r
3205 \r
3206     if( HTTPS_FAILED( status ) )\r
3207     {\r
3208         IotLogError( "Failed to add request %d to the connection's request queue. Error code: %d.", reqHandle, status );\r
3209         HTTPS_GOTO_CLEANUP();\r
3210     }\r
3211 \r
3212     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
3213 }\r
3214 \r
3215 /*-----------------------------------------------------------*/\r
3216 \r
3217 IotHttpsReturnCode_t IotHttpsClient_ReadResponseStatus( IotHttpsResponseHandle_t respHandle,\r
3218                                                         uint16_t * pStatus )\r
3219 {\r
3220     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3221 \r
3222     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( respHandle );\r
3223     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pStatus );\r
3224 \r
3225     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( respHandle->status != 0,\r
3226                                          IOT_HTTPS_NOT_FOUND,\r
3227                                          "The HTTP response status was not found in the HTTP response header buffer." );\r
3228 \r
3229     *pStatus = respHandle->status;\r
3230 \r
3231     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
3232 }\r
3233 \r
3234 /*-----------------------------------------------------------*/\r
3235 \r
3236 IotHttpsReturnCode_t IotHttpsClient_ReadHeader( IotHttpsResponseHandle_t respHandle,\r
3237                                                 char * pName,\r
3238                                                 uint32_t nameLen,\r
3239                                                 char * pValue,\r
3240                                                 uint32_t valueLen )\r
3241 {\r
3242     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3243 \r
3244     const char * pHttpParserErrorDescription = NULL;\r
3245     IotHttpsResponseBufferState_t savedBufferState = PROCESSING_STATE_NONE;\r
3246     IotHttpsResponseParserState_t savedParserState = PARSER_STATE_NONE;\r
3247     size_t numParsed = 0;\r
3248 \r
3249     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( respHandle );\r
3250     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pName );\r
3251     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pValue );\r
3252     HTTPS_ON_ARG_ERROR_MSG_GOTO_CLEANUP( valueLen > 0,\r
3253                                          IOT_HTTPS_INVALID_PARAMETER,\r
3254                                          "pValue has insufficient space to store a value string (length is 0)" );\r
3255 \r
3256     /* The buffer processing state is changed to searching the header buffer in this function. The parser state is\r
3257     * changed in the response to wherever the parser is currently located in the response. If this function is called\r
3258     * in the middle of processing a response (for example in readReadyCallback() routine of an asynchronous response),\r
3259     * then parsing the response need to be able to start at the same place it was before calling this function. */\r
3260     savedBufferState = respHandle->bufferProcessingState;\r
3261     savedParserState = respHandle->parserState;\r
3262 \r
3263     /* The header search parameters in the response handle are used as context in the http-parser callbacks. During\r
3264      * the callback, pReadHeaderField is checked against the currently parsed header name. foundHeaderField is set to\r
3265      * true when the pReadHeaderField is found in a header field callback. The bufferProcessingState tells the callback\r
3266      * to skip the logic pertaining to when the response is being parsed for the first time. pReadHeaderValue will store\r
3267      * the header value found. readHeaderValueLength will store the length of the header value found from within the\r
3268      * response headers. */\r
3269     respHandle->pReadHeaderField = pName;\r
3270     respHandle->readHeaderFieldLength = nameLen;\r
3271     respHandle->foundHeaderField = false;\r
3272     respHandle->bufferProcessingState = PROCESSING_STATE_SEARCHING_HEADER_BUFFER;\r
3273     respHandle->pReadHeaderValue = NULL;\r
3274     respHandle->readHeaderValueLength = 0;\r
3275 \r
3276     /* Start over the HTTP parser so that it will parser from the beginning of the message. */\r
3277     http_parser_init( &( respHandle->httpParserInfo.readHeaderParser ), HTTP_RESPONSE );\r
3278 \r
3279     IotLogDebug( "Now parsing HTTP Message buffer to read a header." );\r
3280     numParsed = respHandle->httpParserInfo.parseFunc( &( respHandle->httpParserInfo.readHeaderParser ), &_httpParserSettings, ( char * ) ( respHandle->pHeaders ), respHandle->pHeadersCur - respHandle->pHeaders );\r
3281     IotLogDebug( "Parsed %d characters in IotHttpsClient_ReadHeader().", numParsed );\r
3282 \r
3283     /* There shouldn't be any errors parsing the response body given that the handle is from a validly\r
3284      * received response, so this check is defensive. If there were errors parsing the original response headers, then\r
3285      * the response handle would have been invalidated and the connection closed. */\r
3286     if( ( respHandle->httpParserInfo.readHeaderParser.http_errno != 0 ) &&\r
3287         ( HTTP_PARSER_ERRNO( &( respHandle->httpParserInfo.readHeaderParser ) ) > HPE_CB_chunk_complete ) )\r
3288     {\r
3289         pHttpParserErrorDescription = http_errno_description( HTTP_PARSER_ERRNO( &( respHandle->httpParserInfo.readHeaderParser ) ) );\r
3290         IotLogError( "http_parser failed on the http response with error: %s", pHttpParserErrorDescription );\r
3291         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_PARSING_ERROR );\r
3292     }\r
3293 \r
3294     /* Not only do we need an indication that the header field was found, but also that the value was found as well.\r
3295      * The value is found when it is non-NULL. The case where the header field is found, but the value is not found\r
3296      * occurs when there are incomplete headers stored in the header buffer. The header buffer could end with a header\r
3297      * field name. */\r
3298     if( respHandle->foundHeaderField && ( respHandle->pReadHeaderValue != NULL ) )\r
3299     {\r
3300         /* The len of the pValue buffer must account for the NULL terminator. */\r
3301         if( respHandle->readHeaderValueLength > ( valueLen - 1 ) )\r
3302         {\r
3303             IotLogError( "IotHttpsClient_ReadHeader(): The length of the pValue buffer specified is less than the actual length of the pValue. " );\r
3304             HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_INSUFFICIENT_MEMORY );\r
3305         }\r
3306         else\r
3307         {\r
3308             memcpy( pValue, respHandle->pReadHeaderValue, respHandle->readHeaderValueLength );\r
3309             pValue[ respHandle->readHeaderValueLength ] = '\0';\r
3310         }\r
3311     }\r
3312     else\r
3313     {\r
3314         IotLogWarn( "IotHttpsClient_ReadHeader(): The header field %s was not found.", pName );\r
3315         HTTPS_SET_AND_GOTO_CLEANUP( IOT_HTTPS_NOT_FOUND );\r
3316     }\r
3317 \r
3318     HTTPS_FUNCTION_CLEANUP_BEGIN();\r
3319 \r
3320     /* Always restore the state back to what it was before entering this function. */\r
3321     if( respHandle != NULL )\r
3322     {\r
3323         respHandle->bufferProcessingState = savedBufferState;\r
3324         respHandle->parserState = savedParserState;\r
3325     }\r
3326 \r
3327     HTTPS_FUNCTION_CLEANUP_END();\r
3328 }\r
3329 \r
3330 /*-----------------------------------------------------------*/\r
3331 \r
3332 IotHttpsReturnCode_t IotHttpsClient_ReadContentLength( IotHttpsResponseHandle_t respHandle,\r
3333                                                        uint32_t * pContentLength )\r
3334 {\r
3335     HTTPS_FUNCTION_ENTRY( IOT_HTTPS_OK );\r
3336 \r
3337     const int CONTENT_LENGTH_NUMBERIC_BASE = 10;\r
3338     char pContentLengthStr[ HTTPS_MAX_CONTENT_LENGTH_LINE_LENGTH ] = { 0 };\r
3339 \r
3340     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( respHandle );\r
3341     HTTPS_ON_NULL_ARG_GOTO_CLEANUP( pContentLength );\r
3342 \r
3343     /* If there is no content-length header or if we were not able to store it in the header buffer this will be\r
3344      * invalid. We do not use the content-length member of the http-parser state structure to get the content\r
3345      * length as this is a PRIVATE member. Because it is a PRIVATE member it can be any value. */\r
3346     status = IotHttpsClient_ReadHeader( respHandle, HTTPS_CONTENT_LENGTH_HEADER, FAST_MACRO_STRLEN( HTTPS_CONTENT_LENGTH_HEADER ), pContentLengthStr, HTTPS_MAX_CONTENT_LENGTH_LINE_LENGTH );\r
3347 \r
3348     if( HTTPS_FAILED( status ) )\r
3349     {\r
3350         *pContentLength = 0;\r
3351         IotLogError( "Could not read the Content-Length for the response." );\r
3352         HTTPS_GOTO_CLEANUP();\r
3353     }\r
3354 \r
3355     *pContentLength = strtoul( pContentLengthStr, NULL, CONTENT_LENGTH_NUMBERIC_BASE );\r
3356 \r
3357     HTTPS_FUNCTION_EXIT_NO_CLEANUP();\r
3358 }\r
3359 \r
3360 /*-----------------------------------------------------------*/\r
3361 \r
3362 /* Provide access to internal functions and variables if testing. */\r
3363 #if IOT_BUILD_TESTS == 1\r
3364     #include "iot_test_access_https_client.c"\r
3365 #endif\r