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