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