2 * FreeRTOS+TCP V2.0.1
\r
3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\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
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\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
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
28 /* Standard includes. */
\r
32 /* FreeRTOS includes. */
\r
33 #include "FreeRTOS.h"
\r
36 /* FreeRTOS+TCP includes. */
\r
37 #include "FreeRTOS_IP.h"
\r
38 #include "FreeRTOS_Sockets.h"
\r
40 /* FreeRTOS Protocol includes. */
\r
41 #include "FreeRTOS_HTTP_commands.h"
\r
42 #include "FreeRTOS_TCP_server.h"
\r
43 #include "FreeRTOS_server_private.h"
\r
45 /* Remove the whole file if HTTP is not supported. */
\r
46 #if( ipconfigUSE_HTTP == 1 )
\r
48 /* FreeRTOS+FAT includes. */
\r
49 #include "ff_stdio.h"
\r
51 #ifndef HTTP_SERVER_BACKLOG
\r
52 #define HTTP_SERVER_BACKLOG ( 12 )
\r
55 #ifndef USE_HTML_CHUNKS
\r
56 #define USE_HTML_CHUNKS ( 0 )
\r
59 #if !defined( ARRAY_SIZE )
\r
60 #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
\r
63 /* Some defines to make the code more readbale */
\r
64 #define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
\r
65 #define pcNEW_DIR pxClient->pxParent->pcNewDir
\r
66 #define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
\r
68 #ifndef ipconfigHTTP_REQUEST_CHARACTER
\r
69 #define ipconfigHTTP_REQUEST_CHARACTER '?'
\r
72 /*_RB_ Need comment block, although fairly self evident. */
\r
73 static void prvFileClose( HTTPClient_t *pxClient );
\r
74 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );
\r
75 static const char *pcGetContentsType( const char *apFname );
\r
76 static BaseType_t prvOpenURL( HTTPClient_t *pxClient );
\r
77 static BaseType_t prvSendFile( HTTPClient_t *pxClient );
\r
78 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );
\r
80 static const char pcEmptyString[1] = { '\0' };
\r
82 typedef struct xTYPE_COUPLE
\r
84 const char *pcExtension;
\r
88 static TypeCouple_t pxTypeCouples[ ] =
\r
90 { "html", "text/html" },
\r
91 { "css", "text/css" },
\r
92 { "js", "text/javascript" },
\r
93 { "png", "image/png" },
\r
94 { "jpg", "image/jpeg" },
\r
95 { "gif", "image/gif" },
\r
96 { "txt", "text/plain" },
\r
97 { "mp3", "audio/mpeg3" },
\r
98 { "wav", "audio/wav" },
\r
99 { "flac", "audio/ogg" },
\r
100 { "pdf", "application/pdf" },
\r
101 { "ttf", "application/x-font-ttf" },
\r
102 { "ttc", "application/x-font-ttf" }
\r
105 void vHTTPClientDelete( TCPClient_t *pxTCPClient )
\r
107 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
109 /* This HTTP client stops, close / release all resources. */
\r
110 if( pxClient->xSocket != FREERTOS_NO_SOCKET )
\r
112 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
\r
113 FreeRTOS_closesocket( pxClient->xSocket );
\r
114 pxClient->xSocket = FREERTOS_NO_SOCKET;
\r
116 prvFileClose( pxClient );
\r
118 /*-----------------------------------------------------------*/
\r
120 static void prvFileClose( HTTPClient_t *pxClient )
\r
122 if( pxClient->pxFileHandle != NULL )
\r
124 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );
\r
125 ff_fclose( pxClient->pxFileHandle );
\r
126 pxClient->pxFileHandle = NULL;
\r
129 /*-----------------------------------------------------------*/
\r
131 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )
\r
133 struct xTCP_SERVER *pxParent = pxClient->pxParent;
\r
136 /* A normal command reply on the main socket (port 21). */
\r
137 char *pcBuffer = pxParent->pcFileBuffer;
\r
139 xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),
\r
140 "HTTP/1.1 %d %s\r\n"
\r
141 #if USE_HTML_CHUNKS
\r
142 "Transfer-Encoding: chunked\r\n"
\r
144 "Content-Type: %s\r\n"
\r
145 "Connection: keep-alive\r\n"
\r
148 webCodename (xCode),
\r
149 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",
\r
150 pxParent->pcExtraContents );
\r
152 pxParent->pcContentsType[0] = '\0';
\r
153 pxParent->pcExtraContents[0] = '\0';
\r
155 xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );
\r
156 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
160 /*-----------------------------------------------------------*/
\r
162 static BaseType_t prvSendFile( HTTPClient_t *pxClient )
\r
166 BaseType_t xRc = 0;
\r
168 if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )
\r
170 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
172 strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );
\r
173 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
174 "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );
\r
176 /* "Requested file action OK". */
\r
177 xRc = prvSendReply( pxClient, WEB_REPLY_OK );
\r
182 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );
\r
184 if( pxClient->uxBytesLeft < uxSpace )
\r
186 uxCount = pxClient->uxBytesLeft;
\r
195 if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )
\r
197 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );
\r
199 ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );
\r
200 pxClient->uxBytesLeft -= uxCount;
\r
202 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );
\r
208 } while( uxCount > 0u );
\r
210 if( pxClient->uxBytesLeft == 0u )
\r
212 /* Writing is ready, no need for further 'eSELECT_WRITE' events. */
\r
213 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
214 prvFileClose( pxClient );
\r
218 /* Wake up the TCP task as soon as this socket may be written to. */
\r
219 FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
224 /*-----------------------------------------------------------*/
\r
226 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )
\r
231 pxClient->bits.ulFlags = 0;
\r
233 #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )
\r
235 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )
\r
239 xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );
\r
242 strcpy( pxClient->pxParent->pcContentsType, "text/html" );
\r
243 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
244 "Content-Length: %d\r\n", ( int ) xResult );
\r
245 xRc = prvSendReply( pxClient, WEB_REPLY_OK ); /* "Requested file action OK" */
\r
248 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );
\r
250 /* Although against the coding standard of FreeRTOS, a return is
\r
251 done here to simplify this conditional code. */
\r
256 #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */
\r
258 if( pxClient->pcUrlData[ 0 ] != '/' )
\r
260 /* Insert a slash before the file name. */
\r
261 pcSlash[ 0 ] = '/';
\r
262 pcSlash[ 1 ] = '\0';
\r
266 /* The browser provided a starting '/' already. */
\r
267 pcSlash[ 0 ] = '\0';
\r
269 snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",
\r
270 pxClient->pcRootDir,
\r
272 pxClient->pcUrlData);
\r
274 pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );
\r
276 FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,
\r
277 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );
\r
279 if( pxClient->pxFileHandle == NULL )
\r
281 /* "404 File not found". */
\r
282 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );
\r
286 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;
\r
287 xRc = prvSendFile( pxClient );
\r
292 /*-----------------------------------------------------------*/
\r
294 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )
\r
296 BaseType_t xResult = 0;
\r
298 /* A new command has been received. Process it. */
\r
302 xResult = prvOpenURL( pxClient );
\r
315 FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",
\r
316 xWebCommands[xIndex].pcCommandName ) );
\r
323 /*-----------------------------------------------------------*/
\r
325 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )
\r
328 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
330 if( pxClient->pxFileHandle != NULL )
\r
332 prvSendFile( pxClient );
\r
335 xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
\r
340 const char *pcEndOfCmd;
\r
341 const struct xWEB_COMMAND *curCmd;
\r
342 char *pcBuffer = pcCOMMAND_BUFFER;
\r
344 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
\r
346 pcBuffer[ xRc ] = '\0';
\r
348 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )
\r
350 pcBuffer[ --xRc ] = '\0';
\r
352 pcEndOfCmd = pcBuffer + xRc;
\r
354 curCmd = xWebCommands;
\r
356 /* Pointing to "/index.html HTTP/1.1". */
\r
357 pxClient->pcUrlData = pcBuffer;
\r
359 /* Pointing to "HTTP/1.1". */
\r
360 pxClient->pcRestData = pcEmptyString;
\r
362 /* Last entry is "ECMD_UNK". */
\r
363 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )
\r
365 BaseType_t xLength;
\r
367 xLength = curCmd->xCommandLength;
\r
368 if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )
\r
372 pxClient->pcUrlData += xLength + 1;
\r
373 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )
\r
375 char ch = *pcLastPtr;
\r
376 if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )
\r
379 pxClient->pcRestData = pcLastPtr + 1;
\r
387 if( xIndex < ( WEB_CMD_COUNT - 1 ) )
\r
389 xRc = prvProcessCmd( pxClient, xIndex );
\r
394 /* The connection will be closed and the client will be deleted. */
\r
395 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );
\r
399 /*-----------------------------------------------------------*/
\r
401 static const char *pcGetContentsType (const char *apFname)
\r
403 const char *slash = NULL;
\r
404 const char *dot = NULL;
\r
406 const char *pcResult = "text/html";
\r
409 for( ptr = apFname; *ptr; ptr++ )
\r
411 if (*ptr == '.') dot = ptr;
\r
412 if (*ptr == '/') slash = ptr;
\r
417 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )
\r
419 if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )
\r
421 pcResult = pxTypeCouples[ x ].pcType;
\r
429 #endif /* ipconfigUSE_HTTP */
\r