]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Demo/Common/Demo_IP_Protocols/HTTP/FreeRTOS_HTTP_server.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Demo / Common / Demo_IP_Protocols / HTTP / FreeRTOS_HTTP_server.c
1 /*\r
2  * FreeRTOS+TCP V2.0.3\r
3  * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
14  *\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
21  *\r
22  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 \r
26 /* Standard includes. */\r
27 #include <stdio.h>\r
28 #include <stdlib.h>\r
29 \r
30 /* FreeRTOS includes. */\r
31 #include "FreeRTOS.h"\r
32 #include "task.h"\r
33 \r
34 /* FreeRTOS+TCP includes. */\r
35 #include "FreeRTOS_IP.h"\r
36 #include "FreeRTOS_Sockets.h"\r
37 \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
42 \r
43 /* Remove the whole file if HTTP is not supported. */\r
44 #if( ipconfigUSE_HTTP == 1 )\r
45 \r
46 /* FreeRTOS+FAT includes. */\r
47 #include "ff_stdio.h"\r
48 \r
49 #ifndef HTTP_SERVER_BACKLOG\r
50         #define HTTP_SERVER_BACKLOG                     ( 12 )\r
51 #endif\r
52 \r
53 #ifndef USE_HTML_CHUNKS\r
54         #define USE_HTML_CHUNKS                         ( 0 )\r
55 #endif\r
56 \r
57 #if !defined( ARRAY_SIZE )\r
58         #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )\r
59 #endif\r
60 \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
65 \r
66 #ifndef ipconfigHTTP_REQUEST_CHARACTER\r
67         #define ipconfigHTTP_REQUEST_CHARACTER          '?'\r
68 #endif\r
69 \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
77 \r
78 static const char pcEmptyString[1] = { '\0' };\r
79 \r
80 typedef struct xTYPE_COUPLE\r
81 {\r
82         const char *pcExtension;\r
83         const char *pcType;\r
84 } TypeCouple_t;\r
85 \r
86 static TypeCouple_t pxTypeCouples[ ] =\r
87 {\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
101 };\r
102 \r
103 void vHTTPClientDelete( TCPClient_t *pxTCPClient )\r
104 {\r
105 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;\r
106 \r
107         /* This HTTP client stops, close / release all resources. */\r
108         if( pxClient->xSocket != FREERTOS_NO_SOCKET )\r
109         {\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
113         }\r
114         prvFileClose( pxClient );\r
115 }\r
116 /*-----------------------------------------------------------*/\r
117 \r
118 static void prvFileClose( HTTPClient_t *pxClient )\r
119 {\r
120         if( pxClient->pxFileHandle != NULL )\r
121         {\r
122                 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );\r
123                 ff_fclose( pxClient->pxFileHandle );\r
124                 pxClient->pxFileHandle = NULL;\r
125         }\r
126 }\r
127 /*-----------------------------------------------------------*/\r
128 \r
129 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )\r
130 {\r
131 struct xTCP_SERVER *pxParent = pxClient->pxParent;\r
132 BaseType_t xRc;\r
133 \r
134         /* A normal command reply on the main socket (port 21). */\r
135         char *pcBuffer = pxParent->pcFileBuffer;\r
136 \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
141 #endif\r
142                 "Content-Type: %s\r\n"\r
143                 "Connection: keep-alive\r\n"\r
144                 "%s\r\n",\r
145                 ( int ) xCode,\r
146                 webCodename (xCode),\r
147                 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",\r
148                 pxParent->pcExtraContents );\r
149 \r
150         pxParent->pcContentsType[0] = '\0';\r
151         pxParent->pcExtraContents[0] = '\0';\r
152 \r
153         xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );\r
154         pxClient->bits.bReplySent = pdTRUE_UNSIGNED;\r
155 \r
156         return xRc;\r
157 }\r
158 /*-----------------------------------------------------------*/\r
159 \r
160 static BaseType_t prvSendFile( HTTPClient_t *pxClient )\r
161 {\r
162 size_t uxSpace;\r
163 size_t uxCount;\r
164 BaseType_t xRc = 0;\r
165 \r
166         if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )\r
167         {\r
168                 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;\r
169 \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
173 \r
174                 /* "Requested file action OK". */\r
175                 xRc = prvSendReply( pxClient, WEB_REPLY_OK );\r
176         }\r
177 \r
178         if( xRc >= 0 ) do\r
179         {\r
180                 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );\r
181 \r
182                 if( pxClient->uxBytesLeft < uxSpace )\r
183                 {\r
184                         uxCount = pxClient->uxBytesLeft;\r
185                 }\r
186                 else\r
187                 {\r
188                         uxCount = uxSpace;\r
189                 }\r
190 \r
191                 if( uxCount > 0u )\r
192                 {\r
193                         if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )\r
194                         {\r
195                                 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );\r
196                         }\r
197                         ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );\r
198                         pxClient->uxBytesLeft -= uxCount;\r
199 \r
200                         xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );\r
201                         if( xRc < 0 )\r
202                         {\r
203                                 break;\r
204                         }\r
205                 }\r
206         } while( uxCount > 0u );\r
207 \r
208         if( pxClient->uxBytesLeft == 0u )\r
209         {\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
213         }\r
214         else\r
215         {\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
218         }\r
219 \r
220         return xRc;\r
221 }\r
222 /*-----------------------------------------------------------*/\r
223 \r
224 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )\r
225 {\r
226 BaseType_t xRc;\r
227 char pcSlash[ 2 ];\r
228 \r
229         pxClient->bits.ulFlags = 0;\r
230 \r
231         #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )\r
232         {\r
233                 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )\r
234                 {\r
235                 size_t xResult;\r
236 \r
237                         xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );\r
238                         if( xResult > 0 )\r
239                         {\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
244                                 if( xRc > 0 )\r
245                                 {\r
246                                         xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );\r
247                                 }\r
248                                 /* Although against the coding standard of FreeRTOS, a return is\r
249                                 done here  to simplify this conditional code. */\r
250                                 return xRc;\r
251                         }\r
252                 }\r
253         }\r
254         #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */\r
255 \r
256         if( pxClient->pcUrlData[ 0 ] != '/' )\r
257         {\r
258                 /* Insert a slash before the file name. */\r
259                 pcSlash[ 0 ] = '/';\r
260                 pcSlash[ 1 ] = '\0';\r
261         }\r
262         else\r
263         {\r
264                 /* The browser provided a starting '/' already. */\r
265                 pcSlash[ 0 ] = '\0';\r
266         }\r
267         snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",\r
268                 pxClient->pcRootDir,\r
269                 pcSlash,\r
270                 pxClient->pcUrlData);\r
271 \r
272         pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );\r
273 \r
274         FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,\r
275                 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );\r
276 \r
277         if( pxClient->pxFileHandle == NULL )\r
278         {\r
279                 /* "404 File not found". */\r
280                 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );\r
281         }\r
282         else\r
283         {\r
284                 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;\r
285                 xRc = prvSendFile( pxClient );\r
286         }\r
287 \r
288         return xRc;\r
289 }\r
290 /*-----------------------------------------------------------*/\r
291 \r
292 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )\r
293 {\r
294 BaseType_t xResult = 0;\r
295 \r
296         /* A new command has been received. Process it. */\r
297         switch( xIndex )\r
298         {\r
299         case ECMD_GET:\r
300                 xResult = prvOpenURL( pxClient );\r
301                 break;\r
302 \r
303         case ECMD_HEAD:\r
304         case ECMD_POST:\r
305         case ECMD_PUT:\r
306         case ECMD_DELETE:\r
307         case ECMD_TRACE:\r
308         case ECMD_OPTIONS:\r
309         case ECMD_CONNECT:\r
310         case ECMD_PATCH:\r
311         case ECMD_UNK:\r
312                 {\r
313                         FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",\r
314                                 xWebCommands[xIndex].pcCommandName ) );\r
315                 }\r
316                 break;\r
317         }\r
318 \r
319         return xResult;\r
320 }\r
321 /*-----------------------------------------------------------*/\r
322 \r
323 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )\r
324 {\r
325 BaseType_t xRc;\r
326 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;\r
327 \r
328         if( pxClient->pxFileHandle != NULL )\r
329         {\r
330                 prvSendFile( pxClient );\r
331         }\r
332 \r
333         xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );\r
334 \r
335         if( xRc > 0 )\r
336         {\r
337         BaseType_t xIndex;\r
338         const char *pcEndOfCmd;\r
339         const struct xWEB_COMMAND *curCmd;\r
340         char *pcBuffer = pcCOMMAND_BUFFER;\r
341 \r
342                 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )\r
343                 {\r
344                         pcBuffer[ xRc ] = '\0';\r
345                 }\r
346                 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )\r
347                 {\r
348                         pcBuffer[ --xRc ] = '\0';\r
349                 }\r
350                 pcEndOfCmd = pcBuffer + xRc;\r
351 \r
352                 curCmd = xWebCommands;\r
353 \r
354                 /* Pointing to "/index.html HTTP/1.1". */\r
355                 pxClient->pcUrlData = pcBuffer;\r
356 \r
357                 /* Pointing to "HTTP/1.1". */\r
358                 pxClient->pcRestData = pcEmptyString;\r
359 \r
360                 /* Last entry is "ECMD_UNK". */\r
361                 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )\r
362                 {\r
363                 BaseType_t xLength;\r
364 \r
365                         xLength = curCmd->xCommandLength;\r
366                         if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )\r
367                         {\r
368                         char *pcLastPtr;\r
369 \r
370                                 pxClient->pcUrlData += xLength + 1;\r
371                                 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )\r
372                                 {\r
373                                         char ch = *pcLastPtr;\r
374                                         if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )\r
375                                         {\r
376                                                 *pcLastPtr = '\0';\r
377                                                 pxClient->pcRestData = pcLastPtr + 1;\r
378                                                 break;\r
379                                         }\r
380                                 }\r
381                                 break;\r
382                         }\r
383                 }\r
384 \r
385                 if( xIndex < ( WEB_CMD_COUNT - 1 ) )\r
386                 {\r
387                         xRc = prvProcessCmd( pxClient, xIndex );\r
388                 }\r
389         }\r
390         else if( xRc < 0 )\r
391         {\r
392                 /* The connection will be closed and the client will be deleted. */\r
393                 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );\r
394         }\r
395         return xRc;\r
396 }\r
397 /*-----------------------------------------------------------*/\r
398 \r
399 static const char *pcGetContentsType (const char *apFname)\r
400 {\r
401         const char *slash = NULL;\r
402         const char *dot = NULL;\r
403         const char *ptr;\r
404         const char *pcResult = "text/html";\r
405         BaseType_t x;\r
406 \r
407         for( ptr = apFname; *ptr; ptr++ )\r
408         {\r
409                 if (*ptr == '.') dot = ptr;\r
410                 if (*ptr == '/') slash = ptr;\r
411         }\r
412         if( dot > slash )\r
413         {\r
414                 dot++;\r
415                 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )\r
416                 {\r
417                         if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )\r
418                         {\r
419                                 pcResult = pxTypeCouples[ x ].pcType;\r
420                                 break;\r
421                         }\r
422                 }\r
423         }\r
424         return pcResult;\r
425 }\r
426 \r
427 #endif /* ipconfigUSE_HTTP */\r
428 \r