2 * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd.
\r
3 * Authors include Hein Tibosch and Richard Barry
\r
5 *******************************************************************************
\r
6 ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***
\r
9 *** FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP ***
\r
10 *** demos have a dependency on FreeRTOS+FAT, which is only in the Labs ***
\r
13 *** FreeRTOS+TCP is functional and has been used in commercial products ***
\r
14 *** for some time. Be aware however that we are still refining its ***
\r
15 *** design, the source code does not yet quite conform to the strict ***
\r
16 *** coding and style standards mandated by Real Time Engineers ltd., and ***
\r
17 *** the documentation and testing is not necessarily complete. ***
\r
19 *** PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE ***
\r
20 *** URL: http://www.FreeRTOS.org/contact Active early adopters may, at ***
\r
21 *** the sole discretion of Real Time Engineers Ltd., be offered versions ***
\r
22 *** under a license other than that described below. ***
\r
25 ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***
\r
26 *******************************************************************************
\r
28 * FreeRTOS+TCP can be used under two different free open source licenses. The
\r
29 * license that applies is dependent on the processor on which FreeRTOS+TCP is
\r
30 * executed, as follows:
\r
32 * If FreeRTOS+TCP is executed on one of the processors listed under the Special
\r
33 * License Arrangements heading of the FreeRTOS+TCP license information web
\r
34 * page, then it can be used under the terms of the FreeRTOS Open Source
\r
35 * License. If FreeRTOS+TCP is used on any other processor, then it can be used
\r
36 * under the terms of the GNU General Public License V2. Links to the relevant
\r
39 * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license
\r
40 * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license
\r
41 * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt
\r
43 * FreeRTOS+TCP is distributed in the hope that it will be useful. You cannot
\r
44 * use FreeRTOS+TCP unless you agree that you use the software 'as is'.
\r
45 * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied
\r
46 * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
47 * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
\r
48 * implied, expressed, or statutory.
\r
50 * 1 tab == 4 spaces!
\r
52 * http://www.FreeRTOS.org
\r
53 * http://www.FreeRTOS.org/plus
\r
54 * http://www.FreeRTOS.org/labs
\r
58 /* Standard includes. */
\r
62 /* FreeRTOS includes. */
\r
63 #include "FreeRTOS.h"
\r
66 /* FreeRTOS+TCP includes. */
\r
67 #include "FreeRTOS_IP.h"
\r
68 #include "FreeRTOS_Sockets.h"
\r
70 /* FreeRTOS Protocol includes. */
\r
71 #include "FreeRTOS_HTTP_commands.h"
\r
72 #include "FreeRTOS_TCP_server.h"
\r
73 #include "FreeRTOS_server_private.h"
\r
75 /* Remove the whole file if HTTP is not supported. */
\r
76 #if( ipconfigUSE_HTTP == 1 )
\r
78 /* FreeRTOS+FAT includes. */
\r
79 #include "ff_stdio.h"
\r
81 #ifndef HTTP_SERVER_BACKLOG
\r
82 #define HTTP_SERVER_BACKLOG ( 12 )
\r
85 #ifndef USE_HTML_CHUNKS
\r
86 #define USE_HTML_CHUNKS ( 0 )
\r
89 #if !defined( ARRAY_SIZE )
\r
90 #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
\r
93 /* Some defines to make the code more readbale */
\r
94 #define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
\r
95 #define pcNEW_DIR pxClient->pxParent->pcNewDir
\r
96 #define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
\r
98 #ifndef ipconfigHTTP_REQUEST_CHARACTER
\r
99 #define ipconfigHTTP_REQUEST_CHARACTER '?'
\r
102 /*_RB_ Need comment block, although fairly self evident. */
\r
103 static void prvFileClose( HTTPClient_t *pxClient );
\r
104 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );
\r
105 static const char *pcGetContentsType( const char *apFname );
\r
106 static BaseType_t prvOpenURL( HTTPClient_t *pxClient );
\r
107 static BaseType_t prvSendFile( HTTPClient_t *pxClient );
\r
108 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );
\r
110 static const char pcEmptyString[1] = { '\0' };
\r
112 typedef struct xTYPE_COUPLE
\r
114 const char *pcExtension;
\r
115 const char *pcType;
\r
118 static TypeCouple_t pxTypeCouples[ ] =
\r
120 { "html", "text/html" },
\r
121 { "css", "text/css" },
\r
122 { "js", "text/javascript" },
\r
123 { "png", "image/png" },
\r
124 { "jpg", "image/jpeg" },
\r
125 { "gif", "image/gif" },
\r
126 { "txt", "text/plain" },
\r
127 { "mp3", "audio/mpeg3" },
\r
128 { "wav", "audio/wav" },
\r
129 { "flac", "audio/ogg" },
\r
130 { "pdf", "application/pdf" },
\r
131 { "ttf", "application/x-font-ttf" },
\r
132 { "ttc", "application/x-font-ttf" }
\r
135 void vHTTPClientDelete( TCPClient_t *pxTCPClient )
\r
137 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
139 /* This HTTP client stops, close / release all resources. */
\r
140 if( pxClient->xSocket != FREERTOS_NO_SOCKET )
\r
142 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
\r
143 FreeRTOS_closesocket( pxClient->xSocket );
\r
144 pxClient->xSocket = FREERTOS_NO_SOCKET;
\r
146 prvFileClose( pxClient );
\r
148 /*-----------------------------------------------------------*/
\r
150 static void prvFileClose( HTTPClient_t *pxClient )
\r
152 if( pxClient->pxFileHandle != NULL )
\r
154 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );
\r
155 ff_fclose( pxClient->pxFileHandle );
\r
156 pxClient->pxFileHandle = NULL;
\r
159 /*-----------------------------------------------------------*/
\r
161 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )
\r
163 struct xTCP_SERVER *pxParent = pxClient->pxParent;
\r
166 /* A normal command reply on the main socket (port 21). */
\r
167 char *pcBuffer = pxParent->pcFileBuffer;
\r
169 xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),
\r
170 "HTTP/1.1 %d %s\r\n"
\r
171 #if USE_HTML_CHUNKS
\r
172 "Transfer-Encoding: chunked\r\n"
\r
174 "Content-Type: %s\r\n"
\r
175 "Connection: keep-alive\r\n"
\r
178 webCodename (xCode),
\r
179 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",
\r
180 pxParent->pcExtraContents );
\r
182 pxParent->pcContentsType[0] = '\0';
\r
183 pxParent->pcExtraContents[0] = '\0';
\r
185 xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );
\r
186 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
190 /*-----------------------------------------------------------*/
\r
192 static BaseType_t prvSendFile( HTTPClient_t *pxClient )
\r
196 BaseType_t xRc = 0;
\r
198 if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )
\r
200 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
202 strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );
\r
203 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
204 "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );
\r
206 /* "Requested file action OK". */
\r
207 xRc = prvSendReply( pxClient, WEB_REPLY_OK );
\r
212 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );
\r
214 if( pxClient->uxBytesLeft < uxSpace )
\r
216 uxCount = pxClient->uxBytesLeft;
\r
225 if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )
\r
227 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );
\r
229 ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );
\r
230 pxClient->uxBytesLeft -= uxCount;
\r
232 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );
\r
238 } while( uxCount > 0u );
\r
240 if( pxClient->uxBytesLeft == 0u )
\r
242 /* Writing is ready, no need for further 'eSELECT_WRITE' events. */
\r
243 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
244 prvFileClose( pxClient );
\r
248 /* Wake up the TCP task as soon as this socket may be written to. */
\r
249 FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
254 /*-----------------------------------------------------------*/
\r
256 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )
\r
261 pxClient->bits.ulFlags = 0;
\r
263 #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )
\r
265 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )
\r
269 xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );
\r
272 strcpy( pxClient->pxParent->pcContentsType, "text/html" );
\r
273 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
274 "Content-Length: %d\r\n", ( int ) xResult );
\r
275 xRc = prvSendReply( pxClient, WEB_REPLY_OK ); /* "Requested file action OK" */
\r
278 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );
\r
280 /* Although against the coding standard of FreeRTOS, a return is
\r
281 done here to simplify this conditional code. */
\r
286 #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */
\r
288 if( pxClient->pcUrlData[ 0 ] != '/' )
\r
290 /* Insert a slash before the file name. */
\r
291 pcSlash[ 0 ] = '/';
\r
292 pcSlash[ 1 ] = '\0';
\r
296 /* The browser provided a starting '/' already. */
\r
297 pcSlash[ 0 ] = '\0';
\r
299 snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",
\r
300 pxClient->pcRootDir,
\r
302 pxClient->pcUrlData);
\r
304 pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );
\r
306 FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,
\r
307 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );
\r
309 if( pxClient->pxFileHandle == NULL )
\r
311 /* "404 File not found". */
\r
312 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );
\r
316 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;
\r
317 xRc = prvSendFile( pxClient );
\r
322 /*-----------------------------------------------------------*/
\r
324 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )
\r
326 BaseType_t xResult = 0;
\r
328 /* A new command has been received. Process it. */
\r
332 xResult = prvOpenURL( pxClient );
\r
345 FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",
\r
346 xWebCommands[xIndex].pcCommandName ) );
\r
353 /*-----------------------------------------------------------*/
\r
355 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )
\r
358 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
360 if( pxClient->pxFileHandle != NULL )
\r
362 prvSendFile( pxClient );
\r
365 xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
\r
370 const char *pcEndOfCmd;
\r
371 const struct xWEB_COMMAND *curCmd;
\r
372 char *pcBuffer = pcCOMMAND_BUFFER;
\r
374 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
\r
376 pcBuffer[ xRc ] = '\0';
\r
378 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )
\r
380 pcBuffer[ --xRc ] = '\0';
\r
382 pcEndOfCmd = pcBuffer + xRc;
\r
384 curCmd = xWebCommands;
\r
386 /* Pointing to "/index.html HTTP/1.1". */
\r
387 pxClient->pcUrlData = pcBuffer;
\r
389 /* Pointing to "HTTP/1.1". */
\r
390 pxClient->pcRestData = pcEmptyString;
\r
392 /* Last entry is "ECMD_UNK". */
\r
393 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )
\r
395 BaseType_t xLength;
\r
397 xLength = curCmd->xCommandLength;
\r
398 if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )
\r
402 pxClient->pcUrlData += xLength + 1;
\r
403 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )
\r
405 char ch = *pcLastPtr;
\r
406 if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )
\r
409 pxClient->pcRestData = pcLastPtr + 1;
\r
417 if( xIndex < ( WEB_CMD_COUNT - 1 ) )
\r
419 xRc = prvProcessCmd( pxClient, xIndex );
\r
424 /* The connection will be closed and the client will be deleted. */
\r
425 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );
\r
429 /*-----------------------------------------------------------*/
\r
431 static const char *pcGetContentsType (const char *apFname)
\r
433 const char *slash = NULL;
\r
434 const char *dot = NULL;
\r
436 const char *pcResult = "text/html";
\r
439 for( ptr = apFname; *ptr; ptr++ )
\r
441 if (*ptr == '.') dot = ptr;
\r
442 if (*ptr == '/') slash = ptr;
\r
447 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )
\r
449 if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )
\r
451 pcResult = pxTypeCouples[ x ].pcType;
\r
459 #endif /* ipconfigUSE_HTTP */
\r