2 * FreeRTOS+TCP V2.0.0
\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. If you wish to use our Amazon
\r
14 * FreeRTOS name, please do so in a fair use way that does not cause confusion.
\r
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
18 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
19 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
23 * http://www.FreeRTOS.org
\r
24 * http://aws.amazon.com/freertos
\r
26 * 1 tab == 4 spaces!
\r
29 /* Standard includes. */
\r
33 /* FreeRTOS includes. */
\r
34 #include "FreeRTOS.h"
\r
37 /* FreeRTOS+TCP includes. */
\r
38 #include "FreeRTOS_IP.h"
\r
39 #include "FreeRTOS_Sockets.h"
\r
41 /* FreeRTOS Protocol includes. */
\r
42 #include "FreeRTOS_HTTP_commands.h"
\r
43 #include "FreeRTOS_TCP_server.h"
\r
44 #include "FreeRTOS_server_private.h"
\r
46 /* Remove the whole file if HTTP is not supported. */
\r
47 #if( ipconfigUSE_HTTP == 1 )
\r
49 /* FreeRTOS+FAT includes. */
\r
50 #include "ff_stdio.h"
\r
52 #ifndef HTTP_SERVER_BACKLOG
\r
53 #define HTTP_SERVER_BACKLOG ( 12 )
\r
56 #ifndef USE_HTML_CHUNKS
\r
57 #define USE_HTML_CHUNKS ( 0 )
\r
60 #if !defined( ARRAY_SIZE )
\r
61 #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )
\r
64 /* Some defines to make the code more readbale */
\r
65 #define pcCOMMAND_BUFFER pxClient->pxParent->pcCommandBuffer
\r
66 #define pcNEW_DIR pxClient->pxParent->pcNewDir
\r
67 #define pcFILE_BUFFER pxClient->pxParent->pcFileBuffer
\r
69 #ifndef ipconfigHTTP_REQUEST_CHARACTER
\r
70 #define ipconfigHTTP_REQUEST_CHARACTER '?'
\r
73 /*_RB_ Need comment block, although fairly self evident. */
\r
74 static void prvFileClose( HTTPClient_t *pxClient );
\r
75 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );
\r
76 static const char *pcGetContentsType( const char *apFname );
\r
77 static BaseType_t prvOpenURL( HTTPClient_t *pxClient );
\r
78 static BaseType_t prvSendFile( HTTPClient_t *pxClient );
\r
79 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );
\r
81 static const char pcEmptyString[1] = { '\0' };
\r
83 typedef struct xTYPE_COUPLE
\r
85 const char *pcExtension;
\r
89 static TypeCouple_t pxTypeCouples[ ] =
\r
91 { "html", "text/html" },
\r
92 { "css", "text/css" },
\r
93 { "js", "text/javascript" },
\r
94 { "png", "image/png" },
\r
95 { "jpg", "image/jpeg" },
\r
96 { "gif", "image/gif" },
\r
97 { "txt", "text/plain" },
\r
98 { "mp3", "audio/mpeg3" },
\r
99 { "wav", "audio/wav" },
\r
100 { "flac", "audio/ogg" },
\r
101 { "pdf", "application/pdf" },
\r
102 { "ttf", "application/x-font-ttf" },
\r
103 { "ttc", "application/x-font-ttf" }
\r
106 void vHTTPClientDelete( TCPClient_t *pxTCPClient )
\r
108 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
110 /* This HTTP client stops, close / release all resources. */
\r
111 if( pxClient->xSocket != FREERTOS_NO_SOCKET )
\r
113 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );
\r
114 FreeRTOS_closesocket( pxClient->xSocket );
\r
115 pxClient->xSocket = FREERTOS_NO_SOCKET;
\r
117 prvFileClose( pxClient );
\r
119 /*-----------------------------------------------------------*/
\r
121 static void prvFileClose( HTTPClient_t *pxClient )
\r
123 if( pxClient->pxFileHandle != NULL )
\r
125 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );
\r
126 ff_fclose( pxClient->pxFileHandle );
\r
127 pxClient->pxFileHandle = NULL;
\r
130 /*-----------------------------------------------------------*/
\r
132 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )
\r
134 struct xTCP_SERVER *pxParent = pxClient->pxParent;
\r
137 /* A normal command reply on the main socket (port 21). */
\r
138 char *pcBuffer = pxParent->pcFileBuffer;
\r
140 xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),
\r
141 "HTTP/1.1 %d %s\r\n"
\r
142 #if USE_HTML_CHUNKS
\r
143 "Transfer-Encoding: chunked\r\n"
\r
145 "Content-Type: %s\r\n"
\r
146 "Connection: keep-alive\r\n"
\r
149 webCodename (xCode),
\r
150 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",
\r
151 pxParent->pcExtraContents );
\r
153 pxParent->pcContentsType[0] = '\0';
\r
154 pxParent->pcExtraContents[0] = '\0';
\r
156 xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );
\r
157 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
161 /*-----------------------------------------------------------*/
\r
163 static BaseType_t prvSendFile( HTTPClient_t *pxClient )
\r
167 BaseType_t xRc = 0;
\r
169 if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )
\r
171 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;
\r
173 strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );
\r
174 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
175 "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );
\r
177 /* "Requested file action OK". */
\r
178 xRc = prvSendReply( pxClient, WEB_REPLY_OK );
\r
183 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );
\r
185 if( pxClient->uxBytesLeft < uxSpace )
\r
187 uxCount = pxClient->uxBytesLeft;
\r
196 if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )
\r
198 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );
\r
200 ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );
\r
201 pxClient->uxBytesLeft -= uxCount;
\r
203 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );
\r
209 } while( uxCount > 0u );
\r
211 if( pxClient->uxBytesLeft == 0u )
\r
213 /* Writing is ready, no need for further 'eSELECT_WRITE' events. */
\r
214 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
215 prvFileClose( pxClient );
\r
219 /* Wake up the TCP task as soon as this socket may be written to. */
\r
220 FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );
\r
225 /*-----------------------------------------------------------*/
\r
227 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )
\r
232 pxClient->bits.ulFlags = 0;
\r
234 #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )
\r
236 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )
\r
240 xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );
\r
243 strcpy( pxClient->pxParent->pcContentsType, "text/html" );
\r
244 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),
\r
245 "Content-Length: %d\r\n", ( int ) xResult );
\r
246 xRc = prvSendReply( pxClient, WEB_REPLY_OK ); /* "Requested file action OK" */
\r
249 xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );
\r
251 /* Although against the coding standard of FreeRTOS, a return is
\r
252 done here to simplify this conditional code. */
\r
257 #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */
\r
259 if( pxClient->pcUrlData[ 0 ] != '/' )
\r
261 /* Insert a slash before the file name. */
\r
262 pcSlash[ 0 ] = '/';
\r
263 pcSlash[ 1 ] = '\0';
\r
267 /* The browser provided a starting '/' already. */
\r
268 pcSlash[ 0 ] = '\0';
\r
270 snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",
\r
271 pxClient->pcRootDir,
\r
273 pxClient->pcUrlData);
\r
275 pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );
\r
277 FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,
\r
278 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );
\r
280 if( pxClient->pxFileHandle == NULL )
\r
282 /* "404 File not found". */
\r
283 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );
\r
287 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;
\r
288 xRc = prvSendFile( pxClient );
\r
293 /*-----------------------------------------------------------*/
\r
295 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )
\r
297 BaseType_t xResult = 0;
\r
299 /* A new command has been received. Process it. */
\r
303 xResult = prvOpenURL( pxClient );
\r
316 FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",
\r
317 xWebCommands[xIndex].pcCommandName ) );
\r
324 /*-----------------------------------------------------------*/
\r
326 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )
\r
329 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;
\r
331 if( pxClient->pxFileHandle != NULL )
\r
333 prvSendFile( pxClient );
\r
336 xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );
\r
341 const char *pcEndOfCmd;
\r
342 const struct xWEB_COMMAND *curCmd;
\r
343 char *pcBuffer = pcCOMMAND_BUFFER;
\r
345 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )
\r
347 pcBuffer[ xRc ] = '\0';
\r
349 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )
\r
351 pcBuffer[ --xRc ] = '\0';
\r
353 pcEndOfCmd = pcBuffer + xRc;
\r
355 curCmd = xWebCommands;
\r
357 /* Pointing to "/index.html HTTP/1.1". */
\r
358 pxClient->pcUrlData = pcBuffer;
\r
360 /* Pointing to "HTTP/1.1". */
\r
361 pxClient->pcRestData = pcEmptyString;
\r
363 /* Last entry is "ECMD_UNK". */
\r
364 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )
\r
366 BaseType_t xLength;
\r
368 xLength = curCmd->xCommandLength;
\r
369 if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )
\r
373 pxClient->pcUrlData += xLength + 1;
\r
374 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )
\r
376 char ch = *pcLastPtr;
\r
377 if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )
\r
380 pxClient->pcRestData = pcLastPtr + 1;
\r
388 if( xIndex < ( WEB_CMD_COUNT - 1 ) )
\r
390 xRc = prvProcessCmd( pxClient, xIndex );
\r
395 /* The connection will be closed and the client will be deleted. */
\r
396 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );
\r
400 /*-----------------------------------------------------------*/
\r
402 static const char *pcGetContentsType (const char *apFname)
\r
404 const char *slash = NULL;
\r
405 const char *dot = NULL;
\r
407 const char *pcResult = "text/html";
\r
410 for( ptr = apFname; *ptr; ptr++ )
\r
412 if (*ptr == '.') dot = ptr;
\r
413 if (*ptr == '/') slash = ptr;
\r
418 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )
\r
420 if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )
\r
422 pcResult = pxTypeCouples[ x ].pcType;
\r
430 #endif /* ipconfigUSE_HTTP */
\r