1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 
29 /**
30  * @file http_client.c
31  * @brief Implements the Domain Name System for the FreeRTOS+TCP network stack.
32  */
33 
34 /* Standard includes. */
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 
39 /* FreeRTOS includes. */
40 #include "FreeRTOS.h"
41 #include "task.h"
42 #include "queue.h"
43 
44 /* FreeRTOS+TCP includes. */
45 #include "FreeRTOS_IP.h"
46 #include "FreeRTOS_Sockets.h"
47 #include "FreeRTOS_DNS.h"
48 #if ( ipconfigMULTI_INTERFACE != 0 )
49     #include "FreeRTOS_Routing.h"
50 #endif
51 
52 #include "http_client_test.h"
53 
54 /* Exclude the whole file if FreeRTOSIPConfig.h is configured to use UDP only. */
55 #if ( ipconfigUSE_TCP == 1 )
56 
57     #ifndef echoNUM_HTTP_CLIENTS
58         /* The number of instances of the echo client task to create. */
59         #define echoNUM_HTTP_CLIENTS    ( 2 )
60     #endif
61 
62     #ifndef httpREMOTE_FILENAME
63         #define httpREMOTE_FILENAME    "/index.html"
64     #endif
65 
66 /* The echo tasks create a socket, send out a number of echo requests, listen
67  * for the echo reply, then close the socket again before starting over.  This
68  * delay is used between each iteration to ensure the network does not get too
69  * congested. */
70     #define echoLOOP_DELAY    ( ( TickType_t ) 150 / portTICK_PERIOD_MS )
71 
72 /* The echo server is assumed to be on port 7, which is the standard echo
73  * protocol port. */
74 
75     #define echoECHO_PORT    ( 80 )
76 
77 /* If ipconfigUSE_TCP_WIN is 1 then the Tx socket will use a buffer size set by
78  * ipconfigTCP_TX_BUF_LEN, and the Tx window size will be
79  * configECHO_CLIENT_TX_WINDOW_SIZE times the buffer size.  Note
80  * ipconfigTCP_TX_BUF_LEN is set in FreeRTOSIPConfig.h as it is a standard TCP/IP
81  * stack constant, whereas configECHO_CLIENT_TX_WINDOW_SIZE is set in
82  * FreeRTOSConfig.h as it is a demo application constant. */
83     #ifndef configECHO_CLIENT_TX_WINDOW_SIZE
84         #define configECHO_CLIENT_TX_WINDOW_SIZE    2
85     #endif
86 
87 /* If ipconfigUSE_TCP_WIN is 1 then the Rx socket will use a buffer size set by
88  * ipconfigTCP_RX_BUFFER_LENGTH, and the Rx window size will be
89  * configECHO_CLIENT_RX_WINDOW_SIZE times the buffer size.  Note
90  * ipconfigTCP_RX_BUFFER_LENGTH is set in FreeRTOSIPConfig.h as it is a standard TCP/IP
91  * stack constant, whereas configECHO_CLIENT_RX_WINDOW_SIZE is set in
92  * FreeRTOSConfig.h as it is a demo application constant. */
93     #ifndef configECHO_CLIENT_RX_WINDOW_SIZE
94         #define configECHO_CLIENT_RX_WINDOW_SIZE    2
95     #endif
96 
97     static uint16_t usUsePortNumber = echoECHO_PORT;
98 
99 /*2404:6800:4003:c02::5e */
100 /*66.96.149.18 */
101 /*-----------------------------------------------------------*/
102 
103 /*
104  * Uses a socket to send data to, then receive data from, the standard echo
105  * port number 7.
106  */
107     static void prvEchoClientTask( void * pvParameters );
108 
109     void printBuffer( const char * apBuffer,
110                       int aLen,
111                       int aLineLen,
112                       const char * apPrefix );
113 
114 /*-----------------------------------------------------------*/
115 
116 /* Counters for each created task - for inspection only. */
117     static uint32_t ulTxRxCycles[ echoNUM_HTTP_CLIENTS ] = { 0 },
118                     ulConnections[ echoNUM_HTTP_CLIENTS ] = { 0 },
119                     xIPVersion[ echoNUM_HTTP_CLIENTS ] = { 0 };
120 
121     static TaskHandle_t xSocketTaskHandles[ echoNUM_HTTP_CLIENTS ];
122 
123 /* When element is non-zero, the corresponding task may run. */
124     static BaseType_t xAllowedToStart[ echoNUM_HTTP_CLIENTS ] = { 0 };
125 
126 /* Each task connects to its own host. */
127     static char pcHostNames[ echoNUM_HTTP_CLIENTS ][ ipconfigDNS_CACHE_NAME_LENGTH ];
128 
129 /* Each task retrieves its own file. */
130     static char pcFileNames[ echoNUM_HTTP_CLIENTS ][ ipconfigDNS_CACHE_NAME_LENGTH ];
131 
132     const char get_command[] =
133         "GET %s HTTP/1.1\x0d\x0a"
134         "Host: %s\x0d\x0a"
135         "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0\x0d\x0a"
136         "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\x0d\x0a"
137         "Accept-Language: en-US,en;q=0.5\x0d\x0a"
138         "DNT: 1\x0d\x0a"
139         "Connection: keep-alive\x0d\x0a"
140         "Upgrade-Insecure-Requests: 1\x0d\x0a"
141         "If-Modified-Since: Fri, 16 Aug 2019 05:18:19 GMT\x0d\x0a"
142         "\x0d\x0a";
143 
144 /*-----------------------------------------------------------*/
145 
vStartHTTPClientTest(uint16_t usTaskStackSize,UBaseType_t uxTaskPriority)146     void vStartHTTPClientTest( uint16_t usTaskStackSize,
147                                UBaseType_t uxTaskPriority )
148     {
149         BaseType_t x;
150         static char pcNames[ echoNUM_HTTP_CLIENTS ][ configMAX_TASK_NAME_LEN + 1 ];
151         static BaseType_t xHasStarted = pdFALSE;
152 
153         if( xHasStarted == pdFALSE )
154         {
155             xHasStarted = pdTRUE;
156 
157             /* Create the echo client tasks. */
158             for( x = 0; x < echoNUM_HTTP_CLIENTS; x++ )
159             {
160                 snprintf( pcNames[ x ], sizeof pcNames[ x ], "Client_%ld", x );
161                 xTaskCreate( prvEchoClientTask,              /* The function that implements the task. */
162                              pcNames[ x ],                   /* Just a text name for the task to aid debugging. */
163                              usTaskStackSize,                /* The stack size is defined in FreeRTOSIPConfig.h. */
164                              ( void * ) x,                   /* The task parameter, not used in this case. */
165                              uxTaskPriority,                 /* The priority assigned to the task is defined in FreeRTOSConfig.h. */
166                              &( xSocketTaskHandles[ x ] ) ); /* Remember the handle. */
167             }
168         }
169     }
170 /*-----------------------------------------------------------*/
171 
172 /**
173  * @brief Wake-up a HTTP client task. aIndex
174  * @param[in] uxIndex: the task number ( 0 .. echoNUM_HTTP_CLIENTS-1 ).
175  * @param[in] pcHost: the name of the host from which to download index.html
176  */
wakeupHTTPClient(size_t uxIndex,const char * pcHost,const char * pcFileName,uint16_t usPortNumber,BaseType_t xIPType)177     void wakeupHTTPClient( size_t uxIndex,
178                            const char * pcHost,
179                            const char * pcFileName,
180                            uint16_t usPortNumber,
181                            BaseType_t xIPType )
182     {
183         if( ( uxIndex < echoNUM_HTTP_CLIENTS ) && ( xSocketTaskHandles[ uxIndex ] != NULL ) )
184         {
185             xIPVersion[ uxIndex ] = xIPType;
186             usUsePortNumber = usPortNumber;
187             snprintf( pcHostNames[ uxIndex ], sizeof pcHostNames[ uxIndex ], "%s", pcHost );
188 
189             if( ( pcFileName != NULL ) && ( pcFileName[ 0 ] != 0 ) )
190             {
191                 snprintf( pcFileNames[ uxIndex ], sizeof( pcFileNames[ uxIndex ] ), pcFileName );
192             }
193             else
194             {
195                 snprintf( pcFileNames[ uxIndex ], sizeof( pcFileNames[ uxIndex ] ), httpREMOTE_FILENAME );
196             }
197 
198             xAllowedToStart[ uxIndex ]++;
199             xTaskNotifyGive( xSocketTaskHandles[ uxIndex ] );
200         }
201     }
202 
203 /**
204  * @brief Wake-up a HTTP client task. aIndex
205  * @param[in] pvParameters: the task number as a void pointer.
206  */
prvEchoClientTask(void * pvParameters)207     static void prvEchoClientTask( void * pvParameters )
208     {
209         Socket_t xSocket = NULL;
210 
211         struct freertos_sockaddr xEchoServerAddress;
212 
213         size_t uxInstance;
214         int32_t xReturned, xReceivedBytes;
215         BaseType_t lTransmitted;
216         TickType_t xTimeOnEntering;
217 
218         #if ( ipconfigUSE_TCP_WIN == 1 )
219             WinProperties_t xWinProps;
220 
221             /* Fill in the buffer and window sizes that will be used by the socket. */
222             xWinProps.lTxBufSize = ipconfigTCP_TX_BUFFER_LENGTH;
223             xWinProps.lTxWinSize = configECHO_CLIENT_TX_WINDOW_SIZE;
224             xWinProps.lRxBufSize = ipconfigTCP_RX_BUFFER_LENGTH;
225             xWinProps.lRxWinSize = configECHO_CLIENT_RX_WINDOW_SIZE;
226         #endif /* ipconfigUSE_TCP_WIN */
227 
228         #if ( ipconfigUSE_IPv6 != 0 )
229             struct freertos_sockaddr * pxAddress = ( struct freertos_sockaddr * ) &xEchoServerAddress;
230         #else
231             struct freertos_sockaddr * pxAddress = &xEchoServerAddress;
232         #endif
233 
234         /* This task can be created a number of times.  Each instance is numbered
235          * to enable each instance to use a different Rx and Tx buffer.  The number is
236          * passed in as the task's parameter. */
237         {
238             /* A two-step assignment. */
239             intptr_t uxIntPtr = ( intptr_t ) pvParameters;
240             uxInstance = ( size_t ) uxIntPtr;
241             configASSERT( uxInstance < echoNUM_HTTP_CLIENTS );
242         }
243 
244         if( uxInstance < echoNUM_HTTP_CLIENTS )
245         {
246             xSocketTaskHandles[ uxInstance ] = xTaskGetCurrentTaskHandle();
247         }
248 
249         for( ; ; )
250         {
251             int rc;
252             struct freertos_sockaddr xBindAddress;
253             const char * pcHostname;
254             uint32_t ulIPAddress = 0U;
255             BaseType_t xHasIPv6Address = pdFALSE;
256             char pcBuffer[ 512 ];
257 
258             /* Rx and Tx time outs are used to ensure the sockets do not wait too long for
259              * missing data. */
260             TickType_t xReceiveTimeOut = pdMS_TO_TICKS( 2500U );
261             TickType_t xSendTimeOut = pdMS_TO_TICKS( 2000U );
262             #if ( ipconfigUSE_IPv6 != 0 )
263                 IPv6_Address_t xIPAddress_IPv6;
264             #endif
265             struct freertos_sockaddr xLocalAddress;
266             #if ( ipconfigMULTI_INTERFACE != 0 )
267                 struct freertos_addrinfo * pxResult = NULL;
268                 struct freertos_addrinfo xHints;
269                 NetworkEndPoint_t * pxEndPoint;
270             #endif
271 
272             if( xSocketValid( xSocket ) == pdTRUE )
273             {
274                 FreeRTOS_closesocket( xSocket );
275             }
276 
277             xSocket = NULL;
278 
279             while( xAllowedToStart[ uxInstance ] == 0 )
280             {
281                 ulTaskNotifyTake( pdTRUE, 100 );
282             }
283 
284             xAllowedToStart[ uxInstance ] = 0;
285 
286             #if ( ipconfigMULTI_INTERFACE != 0 )
287                 if( xIPVersion[ uxInstance ] != 6 )
288                 {
289                     xHints.ai_family = FREERTOS_AF_INET;
290                 }
291                 else
292                 {
293                     xHints.ai_family = FREERTOS_AF_INET6;
294                 }
295             #endif
296             pcHostname = pcHostNames[ uxInstance ];
297 
298             {
299                 #if ( ipconfigMULTI_INTERFACE == 0 )
300                     ulIPAddress = FreeRTOS_gethostbyname( pcHostname );
301 
302                     if( ulIPAddress == 0U )
303                     {
304                         continue;
305                     }
306                 #else
307                     #if ( ipconfigUSE_IPv4 != 0 )
308                         pxEndPoint = FreeRTOS_FindGateWay( ipTYPE_IPv4 );
309 
310                         if( ( pxEndPoint != NULL ) && ( pxEndPoint->ipv4_settings.ulGatewayAddress != 0U ) )
311                         {
312                             xARPWaitResolution( pxEndPoint->ipv4_settings.ulGatewayAddress, pdMS_TO_TICKS( 1000U ) );
313                         }
314                     #endif /* ( ipconfigUSE_IPv4 != 0 ) */
315 
316                     BaseType_t rc_dns = FreeRTOS_getaddrinfo(
317                         pcHostname,  /* The node. */
318                         NULL,        /* const char *pcService: ignored for now. */
319                         &xHints,     /* If not NULL: preferences. */
320                         &pxResult ); /* An allocated struct, containing the results. */
321                     FreeRTOS_printf( ( "httpTest: FreeRTOS_getaddrinfo: rc %d\n", ( int ) rc_dns ) );
322 
323                     if( ( rc_dns != 0 ) || ( pxResult == NULL ) )
324                     {
325                         continue;
326                     }
327 
328                     if( pxResult->ai_family == FREERTOS_AF_INET4 )
329                     {
330 /*				ulIPAddress = ( ( struct freertos_sockaddr * ) pxResult->ai_addr )->sin_address.ulIP_IPv4; */
331                         ulIPAddress = pxResult->ai_addr->sin_address.ulIP_IPv4;
332                     }
333 
334                     #if ( ipconfigUSE_IPv6 != 0 )
335                         else if( pxResult->ai_family == FREERTOS_AF_INET6 )
336                         {
337                             struct freertos_sockaddr * pxAddr6;
338 
339                             pxAddr6 = ( struct freertos_sockaddr * ) pxResult->ai_addr;
340                             memcpy( xIPAddress_IPv6.ucBytes, pxAddr6->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
341                             xHasIPv6Address = pdTRUE;
342                         }
343                     #endif
344                     else
345                     {
346                         continue;
347                     }
348                 #endif /* if ( ipconfigMULTI_INTERFACE == 0 ) */
349             }
350 
351             #if ( ipconfigUSE_IPv6 != 0 )
352                 if( xHasIPv6Address != 0 )
353                 {
354                     xEchoServerAddress.sin_len = sizeof( struct freertos_sockaddr );
355                     xEchoServerAddress.sin_family = FREERTOS_AF_INET6;
356                     xEchoServerAddress.sin_port = FreeRTOS_htons( usUsePortNumber );
357                     memcpy( xEchoServerAddress.sin_address.xIP_IPv6.ucBytes, xIPAddress_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
358                 }
359                 else
360             #endif
361 
362             if( ulIPAddress != 0U )
363             {
364                 pxAddress->sin_len = sizeof( struct freertos_sockaddr );
365                 pxAddress->sin_family = FREERTOS_AF_INET;
366                 pxAddress->sin_port = FreeRTOS_htons( usUsePortNumber );
367                 pxAddress->sin_address.ulIP_IPv4 = ulIPAddress;
368             }
369             else
370             {
371                 configASSERT( 0 == 1 );
372             }
373 
374             /* Create a TCP socket. */
375             xSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_STREAM, FREERTOS_IPPROTO_TCP );
376             configASSERT( xSocketValid( xSocket ) == pdTRUE );
377 
378             memset( &( xBindAddress ), 0, sizeof( xBindAddress ) );
379 
380             #if ( ipconfigMULTI_INTERFACE != 0 )
381                 #if ( ipconfigUSE_IPv6 != 0 )
382                     if( xEchoServerAddress.sin_family == FREERTOS_AF_INET6 )
383                     {
384                         pxEndPoint = FreeRTOS_FindEndPointOnNetMask_IPv6( &( xEchoServerAddress.sin_address.xIP_IPv6 ) );
385 
386                         if( pxEndPoint == NULL )
387                         {
388                             pxEndPoint = FreeRTOS_FindGateWay( ipTYPE_IPv6 );
389                         }
390 
391                         if( pxEndPoint != NULL )
392                         {
393                             /*memcpy( xEchoServerAddress.sin_address.xIP_IPv6.ucBytes, pxEndPoint->ipv6.xIPAddress.ucBytes, ipSIZE_OF_IPv6_ADDRESS ); */
394                         }
395                     }
396                     else
397                 #endif /* if ( ipconfigUSE_IPv6 != 0 ) */
398                 {
399                     pxEndPoint = FreeRTOS_FindEndPointOnNetMask( pxAddress->sin_address.ulIP_IPv4, 9999 );
400 
401                     if( pxEndPoint != NULL )
402                     {
403                         xBindAddress.sin_address.ulIP_IPv4 = pxEndPoint->ipv4_settings.ulIPAddress;
404                         xBindAddress.sin_family = FREERTOS_AF_INET;
405                     }
406                 }
407             #endif /* if ( ipconfigMULTI_INTERFACE != 0 ) */
408             rc = FreeRTOS_bind( xSocket, &( xBindAddress ), sizeof( xBindAddress ) );
409 
410             if( rc != 0 )
411             {
412                 FreeRTOS_printf( ( "httpTest: bind fails with errno %d\n", rc ) );
413                 configASSERT( rc == 0 );
414             }
415 
416             /* Set a time out so a missing reply does not cause the task to block
417              * indefinitely. */
418             FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
419             FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, &xSendTimeOut, sizeof( xSendTimeOut ) );
420 
421             #if ( ipconfigUSE_TCP_WIN == 1 )
422                 {
423                     /* Set the window and buffer sizes. */
424                     FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_WIN_PROPERTIES, ( void * ) &xWinProps, sizeof( xWinProps ) );
425                 }
426             #endif /* ipconfigUSE_TCP_WIN */
427 
428             FreeRTOS_GetLocalAddress( xSocket, &xLocalAddress );
429             /* Connect to the echo server. */
430             rc = FreeRTOS_connect( xSocket, ( struct freertos_sockaddr * ) &xEchoServerAddress, sizeof( xEchoServerAddress ) );
431 
432             #if ( ipconfigUSE_IPv6 != 0 )
433                 struct freertos_sockaddr * pxLocalAddress = ( struct freertos_sockaddr * ) &xLocalAddress;
434             #else
435                 struct freertos_sockaddr * pxLocalAddress = &xLocalAddress;
436             #endif
437 
438             #if ( ipconfigUSE_IPv6 != 0 )
439                 if( pxAddress->sin_family == FREERTOS_AF_INET6 )
440                 {
441                     FreeRTOS_printf( ( "httpTest: FreeRTOS_connect to %pip port %u: rc %d\n",
442                                        xEchoServerAddress.sin_address.xIP_IPv6.ucBytes,
443                                        FreeRTOS_ntohs( pxAddress->sin_port ),
444                                        rc ) );
445                 }
446                 else
447             #endif
448             {
449                 FreeRTOS_printf( ( "httpTest: FreeRTOS_connect from %lxip port %u to %lxip port %u: rc %d\n",
450                                    FreeRTOS_ntohl( pxLocalAddress->sin_address.ulIP_IPv4 ),
451                                    FreeRTOS_ntohs( pxLocalAddress->sin_port ),
452                                    FreeRTOS_ntohl( pxAddress->sin_address.ulIP_IPv4 ),
453                                    FreeRTOS_ntohs( pxAddress->sin_port ),
454                                    rc ) );
455             }
456 
457             if( rc == 0 )
458             {
459                 ulConnections[ uxInstance ]++;
460 
461                 /* Send a HTTP request. */
462                 {
463                     BaseType_t xLoop;
464                     size_t uxLength;
465                     /* Send the string to the socket. */
466                     uxLength = snprintf( pcBuffer, sizeof( pcBuffer ), get_command, pcFileNames[ uxInstance ], pcHostname );
467                     lTransmitted = FreeRTOS_send( xSocket,             /* The socket being sent to. */
468                                                   ( void * ) pcBuffer, /* The data being sent. */
469                                                   uxLength,            /* The length of the data being sent. */
470                                                   0 );                 /* No flags. */
471                     FreeRTOS_printf( ( "httpTest: FreeRTOS_send : rc %ld\n", lTransmitted ) );
472 
473                     if( lTransmitted < 0 )
474                     {
475                         /* Error? */
476                         break;
477                     }
478 
479                     /* Clear the buffer into which the echoed string will be
480                      * placed. */
481                     memset( ( void * ) pcBuffer, 0x00, sizeof( pcBuffer ) );
482                     xReceivedBytes = 0;
483 
484                     /* Receive data echoed back to the socket. */
485                     for( xLoop = 0; xLoop < 10; xLoop++ )
486                     {
487                         xReturned = FreeRTOS_recv( xSocket,            /* The socket being received from. */
488                                                    pcBuffer,           /* The buffer into which the received data will be written. */
489                                                    sizeof( pcBuffer ), /* The size of the buffer provided to receive the data. */
490                                                    0 );                /* No flags. */
491 
492                         FreeRTOS_printf( ( "httpTest: FreeRTOS_recv : rc %ld\n", xReturned ) );
493 
494                         if( xReturned < 0 )
495                         {
496                             /* Error occurred.  Latch it so it can be detected
497                              * below. */
498                             xReceivedBytes = xReturned;
499                             break;
500                         }
501                         else if( xReturned == 0 )
502                         {
503                             /* Timed out. */
504                             break;
505                         }
506                         else
507                         {
508                             /* Use a short RX time-out the next time. */
509                             xReceiveTimeOut = pdMS_TO_TICKS( 500U );
510                             FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &xReceiveTimeOut, sizeof( xReceiveTimeOut ) );
511                             /* Keep a count of the bytes received so far. */
512                             xReceivedBytes += xReturned;
513                             printBuffer( pcBuffer, xReturned, 129, "" );
514                         }
515                     } /* for( xLoop = 0; xLoop < 10; xLoop++ ) */
516                 }
517 
518                 /* Finished using the connected socket, initiate a graceful close:
519                  * FIN, FIN+ACK, ACK. */
520                 FreeRTOS_printf( ( "httpTest: prvEchoClientTask: shut down connection\n" ) );
521                 FreeRTOS_shutdown( xSocket, FREERTOS_SHUT_RDWR );
522 
523                 /* Expect FreeRTOS_recv() to return an error once the shutdown is
524                  * complete. */
525                 xTimeOnEntering = xTaskGetTickCount();
526 
527                 do
528                 {
529                     xReturned = FreeRTOS_recv( xSocket,            /* The socket being received from. */
530                                                pcBuffer,           /* The buffer into which the received data will be written. */
531                                                sizeof( pcBuffer ), /* The size of the buffer provided to receive the data. */
532                                                0 );
533 
534                     if( xReturned < 0 )
535                     {
536                         break;
537                     }
538                 } while( ( xTaskGetTickCount() - xTimeOnEntering ) < xReceiveTimeOut );
539 
540                 FreeRTOS_printf( ( "httpTest: connection is down\n" ) );
541             }
542 
543             /* Close this socket before looping back to create another. */
544             FreeRTOS_closesocket( xSocket );
545             xSocket = NULL;
546             FreeRTOS_printf( ( "httpTest: test is ready\n" ) );
547 
548             /* Pause for a short while to ensure the network is not too
549              * congested. */
550 /*		vTaskDelay( echoLOOP_DELAY ); */
551         }
552     }
553 /*-----------------------------------------------------------*/
554 
printBuffer(const char * apBuffer,int aLen,int aLineLen,const char * apPrefix)555     void printBuffer( const char * apBuffer,
556                       int aLen,
557                       int aLineLen,
558                       const char * apPrefix )
559     {
560         const char * ptr = apBuffer;
561         const char * end = apBuffer + aLen;
562 
563         for( ; ; )
564         {
565             const char * next = ptr;
566             const char * eot;
567 
568             /* Find the first null, newline of end of text. */
569             for( ; ; )
570             {
571                 if( ( next >= end ) || ( *next == '\0' ) )
572                 {
573                     eot = next;
574                     next = NULL;
575                     break;
576                 }
577 
578                 if( ( *next == '\n' ) || ( *next == '\r' ) )
579                 {
580                     char eol = *next == '\n' ? '\r' : '\n';
581                     eot = next;
582 
583                     do
584                     {
585                         next++;
586                     } while( *next == eol );
587 
588                     break;
589                 }
590 
591                 if( ( int ) ( next - ptr ) >= aLineLen )
592                 {
593                     eot = next;
594                     break;
595                 }
596 
597                 next++;
598             }
599 
600             {
601                 char save = *eot;
602                 *( ( char * ) eot ) = '\0';
603                 FreeRTOS_printf( ( "%s%s\n", apPrefix, ptr ) );
604                 *( ( char * ) eot ) = save;
605             }
606 
607             if( next == NULL )
608             {
609                 break;
610             }
611 
612             ptr = next;
613         }
614     }
615 
616 #endif /* ipconfigUSE_TCP */
617