2 * FreeRTOS+TCP V2.0.3
\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://aws.amazon.com/freertos
\r
23 * http://www.FreeRTOS.org
\r
26 /* Standard includes. */
\r
30 /* FreeRTOS includes. */
\r
31 #include "FreeRTOS.h"
\r
34 /* FreeRTOS+TCP includes. */
\r
35 #include "FreeRTOS_IP.h"
\r
36 #include "FreeRTOS_Sockets.h"
\r
38 /* FreeRTOS Protocol includes. */
\r
39 #include "FreeRTOS_HTTP_commands.h"
\r
40 #include "FreeRTOS_TCP_server.h"
\r
41 #include "FreeRTOS_server_private.h"
\r
43 /* Remove the whole file if HTTP is not supported. */
\r
44 #if( ipconfigUSE_HTTP == 1 )
\r
46 /* FreeRTOS+FAT includes. */
\r
47 #include "ff_stdio.h"
\r
49 #ifndef HTTP_SERVER_BACKLOG
\r
50 #define HTTP_SERVER_BACKLOG ( 12 )
\r
53 #ifndef USE_HTML_CHUNKS
\r
54 #define USE_HTML_CHUNKS ( 0 )
\r
57 #if !defined( ARRAY_SIZE )
\r
58 #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
\r
61 /* Some defines to make the code more readbale */
\r
62 #define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
\r
63 #define pcNEW_DIR pxClient->pxParent->pcNewDir
\r
64 #define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
\r
66 #ifndef ipconfigHTTP_REQUEST_CHARACTER
\r
67 #define ipconfigHTTP_REQUEST_CHARACTER '?'
\r
70 /*_RB_ Need comment block, although fairly self evident. */
\r
71 static void prvFileClose( HTTPClient_t *pxClient );
\r
72 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );
\r
73 static const char *pcGetContentsType( const char *apFname );
\r
74 static BaseType_t prvOpenURL( HTTPClient_t *pxClient );
\r
75 static BaseType_t prvSendFile( HTTPClient_t *pxClient );
\r
76 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );
\r
78 static const char pcEmptyString[1] = { '\0' };
\r
80 typedef struct xTYPE_COUPLE
\r
82 const char *pcExtension;
\r
86 static TypeCouple_t pxTypeCouples[ ] =
\r
88 { "html", "text/html" },
\r
89 { "css", "text/css" },
\r
90 { "js", "text/javascript" },
\r
91 { "png", "image/png" },
\r
92 { "jpg", "image/jpeg" },
\r
93 { "gif", "image/gif" },
\r
94 { "txt", "text/plain" },
\r
95 { "mp3", "audio/mpeg3" },
\r
96 { "wav", "audio/wav" },
\r
97 { "flac", "audio/ogg" },
\r
98 { "pdf", "application/pdf" },
\r
99 { "ttf", "application/x-font-ttf" },
\r
100 { "ttc", "application/x-font-ttf" }
\r
103 void vHTTPClientDelete( TCPClient_t *pxTCPClient )
\r
105 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
107 /* This HTTP client stops, close / release all resources. */
\r
108 if( pxClient->xSocket != FREERTOS_NO_SOCKET )
\r
110 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
\r
111 FreeRTOS_closesocket( pxClient->xSocket );
\r
112 pxClient->xSocket = FREERTOS_NO_SOCKET;
\r
114 prvFileClose( pxClient );
\r
116 /*-----------------------------------------------------------*/
\r
118 static void prvFileClose( HTTPClient_t *pxClient )
\r
120 if( pxClient->pxFileHandle != NULL )
\r
122 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );
\r
123 ff_fclose( pxClient->pxFileHandle );
\r
124 pxClient->pxFileHandle = NULL;
\r
127 /*-----------------------------------------------------------*/
\r
129 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )
\r
131 struct xTCP_SERVER *pxParent = pxClient->pxParent;
\r
134 /* A normal command reply on the main socket (port 21). */
\r
135 char *pcBuffer = pxParent->pcFileBuffer;
\r
137 xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),
\r
138 "HTTP/1.1 %d %s\r\n"
\r
139 #if USE_HTML_CHUNKS
\r
140 "Transfer-Encoding: chunked\r\n"
\r
142 "Content-Type: %s\r\n"
\r
143 "Connection: keep-alive\r\n"
\r
146 webCodename (xCode),
\r
147 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",
\r
148 pxParent->pcExtraContents );
\r
150 pxParent->pcContentsType[0] = '\0';
\r
151 pxParent->pcExtraContents[0] = '\0';
\r
153 xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );
\r
154 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
158 /*-----------------------------------------------------------*/
\r
160 static BaseType_t prvSendFile( HTTPClient_t *pxClient )
\r
164 BaseType_t xRc = 0;
\r
166 if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )
\r
168 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
170 strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );
\r
171 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
172 "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );
\r
174 /* "Requested file action OK". */
\r
175 xRc = prvSendReply( pxClient, WEB_REPLY_OK );
\r
180 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );
\r
182 if( pxClient->uxBytesLeft < uxSpace )
\r
184 uxCount = pxClient->uxBytesLeft;
\r
193 if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )
\r
195 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );
\r
197 ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );
\r
198 pxClient->uxBytesLeft -= uxCount;
\r
200 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );
\r
206 } while( uxCount > 0u );
\r
208 if( pxClient->uxBytesLeft == 0u )
\r
210 /* Writing is ready, no need for further 'eSELECT_WRITE' events. */
\r
211 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
212 prvFileClose( pxClient );
\r
216 /* Wake up the TCP task as soon as this socket may be written to. */
\r
217 FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
222 /*-----------------------------------------------------------*/
\r
224 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )
\r
229 pxClient->bits.ulFlags = 0;
\r
231 #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )
\r
233 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )
\r
237 xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );
\r
240 strcpy( pxClient->pxParent->pcContentsType, "text/html" );
\r
241 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
242 "Content-Length: %d\r\n", ( int ) xResult );
\r
243 xRc = prvSendReply( pxClient, WEB_REPLY_OK ); /* "Requested file action OK" */
\r
246 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );
\r
248 /* Although against the coding standard of FreeRTOS, a return is
\r
249 done here to simplify this conditional code. */
\r
254 #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */
\r
256 if( pxClient->pcUrlData[ 0 ] != '/' )
\r
258 /* Insert a slash before the file name. */
\r
259 pcSlash[ 0 ] = '/';
\r
260 pcSlash[ 1 ] = '\0';
\r
264 /* The browser provided a starting '/' already. */
\r
265 pcSlash[ 0 ] = '\0';
\r
267 snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",
\r
268 pxClient->pcRootDir,
\r
270 pxClient->pcUrlData);
\r
272 pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );
\r
274 FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,
\r
275 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );
\r
277 if( pxClient->pxFileHandle == NULL )
\r
279 /* "404 File not found". */
\r
280 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );
\r
284 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;
\r
285 xRc = prvSendFile( pxClient );
\r
290 /*-----------------------------------------------------------*/
\r
292 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )
\r
294 BaseType_t xResult = 0;
\r
296 /* A new command has been received. Process it. */
\r
300 xResult = prvOpenURL( pxClient );
\r
313 FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",
\r
314 xWebCommands[xIndex].pcCommandName ) );
\r
321 /*-----------------------------------------------------------*/
\r
323 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )
\r
326 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
328 if( pxClient->pxFileHandle != NULL )
\r
330 prvSendFile( pxClient );
\r
333 xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
\r
338 const char *pcEndOfCmd;
\r
339 const struct xWEB_COMMAND *curCmd;
\r
340 char *pcBuffer = pcCOMMAND_BUFFER;
\r
342 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
\r
344 pcBuffer[ xRc ] = '\0';
\r
346 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )
\r
348 pcBuffer[ --xRc ] = '\0';
\r
350 pcEndOfCmd = pcBuffer + xRc;
\r
352 curCmd = xWebCommands;
\r
354 /* Pointing to "/index.html HTTP/1.1". */
\r
355 pxClient->pcUrlData = pcBuffer;
\r
357 /* Pointing to "HTTP/1.1". */
\r
358 pxClient->pcRestData = pcEmptyString;
\r
360 /* Last entry is "ECMD_UNK". */
\r
361 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )
\r
363 BaseType_t xLength;
\r
365 xLength = curCmd->xCommandLength;
\r
366 if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )
\r
370 pxClient->pcUrlData += xLength + 1;
\r
371 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )
\r
373 char ch = *pcLastPtr;
\r
374 if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )
\r
377 pxClient->pcRestData = pcLastPtr + 1;
\r
385 if( xIndex < ( WEB_CMD_COUNT - 1 ) )
\r
387 xRc = prvProcessCmd( pxClient, xIndex );
\r
392 /* The connection will be closed and the client will be deleted. */
\r
393 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );
\r
397 /*-----------------------------------------------------------*/
\r
399 static const char *pcGetContentsType (const char *apFname)
\r
401 const char *slash = NULL;
\r
402 const char *dot = NULL;
\r
404 const char *pcResult = "text/html";
\r
407 for( ptr = apFname; *ptr; ptr++ )
\r
409 if (*ptr == '.') dot = ptr;
\r
410 if (*ptr == '/') slash = ptr;
\r
415 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )
\r
417 if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )
\r
419 pcResult = pxTypeCouples[ x ].pcType;
\r
427 #endif /* ipconfigUSE_HTTP */
\r