]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/protocols/HTTP/FreeRTOS_HTTP_server.c
0172a228e15d68490214a28198c71a1af5336c72
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-TCP / protocols / HTTP / FreeRTOS_HTTP_server.c
1 /*\r
2  * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd.\r
3  * Authors include Hein Tibosch and Richard Barry\r
4  *\r
5  *******************************************************************************\r
6  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
7  ***                                                                         ***\r
8  ***                                                                         ***\r
9  ***   FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP     ***\r
10  ***   demos have a dependency on FreeRTOS+FAT, which is only in the Labs    ***\r
11  ***   download):                                                            ***\r
12  ***                                                                         ***\r
13  ***   FreeRTOS+TCP is functional and has been used in commercial products   ***\r
14  ***   for some time.  Be aware however that we are still refining its       ***\r
15  ***   design, the source code does not yet quite conform to the strict      ***\r
16  ***   coding and style standards mandated by Real Time Engineers ltd., and  ***\r
17  ***   the documentation and testing is not necessarily complete.            ***\r
18  ***                                                                         ***\r
19  ***   PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE    ***\r
20  ***   URL: http://www.FreeRTOS.org/contact  Active early adopters may, at   ***\r
21  ***   the sole discretion of Real Time Engineers Ltd., be offered versions  ***\r
22  ***   under a license other than that described below.                      ***\r
23  ***                                                                         ***\r
24  ***                                                                         ***\r
25  ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***\r
26  *******************************************************************************\r
27  *\r
28  * FreeRTOS+TCP can be used under two different free open source licenses.  The\r
29  * license that applies is dependent on the processor on which FreeRTOS+TCP is\r
30  * executed, as follows:\r
31  *\r
32  * If FreeRTOS+TCP is executed on one of the processors listed under the Special\r
33  * License Arrangements heading of the FreeRTOS+TCP license information web\r
34  * page, then it can be used under the terms of the FreeRTOS Open Source\r
35  * License.  If FreeRTOS+TCP is used on any other processor, then it can be used\r
36  * under the terms of the GNU General Public License V2.  Links to the relevant\r
37  * licenses follow:\r
38  *\r
39  * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license\r
40  * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license\r
41  * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt\r
42  *\r
43  * FreeRTOS+TCP is distributed in the hope that it will be useful.  You cannot\r
44  * use FreeRTOS+TCP unless you agree that you use the software 'as is'.\r
45  * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied\r
46  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
47  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
48  * implied, expressed, or statutory.\r
49  *\r
50  * 1 tab == 4 spaces!\r
51  *\r
52  * http://www.FreeRTOS.org\r
53  * http://www.FreeRTOS.org/plus\r
54  * http://www.FreeRTOS.org/labs\r
55  *\r
56  */\r
57 \r
58 /* Standard includes. */\r
59 #include <stdio.h>\r
60 #include <stdlib.h>\r
61 \r
62 /* FreeRTOS includes. */\r
63 #include "FreeRTOS.h"\r
64 #include "task.h"\r
65 \r
66 /* FreeRTOS+TCP includes. */\r
67 #include "FreeRTOS_IP.h"\r
68 #include "FreeRTOS_Sockets.h"\r
69 \r
70 /* FreeRTOS Protocol includes. */\r
71 #include "FreeRTOS_HTTP_commands.h"\r
72 #include "FreeRTOS_TCP_server.h"\r
73 #include "FreeRTOS_server_private.h"\r
74 \r
75 /* Remove the whole file if HTTP is not supported. */\r
76 #if( ipconfigUSE_HTTP == 1 )\r
77 \r
78 /* FreeRTOS+FAT includes. */\r
79 #include "ff_stdio.h"\r
80 \r
81 #ifndef HTTP_SERVER_BACKLOG\r
82         #define HTTP_SERVER_BACKLOG                     ( 12 )\r
83 #endif\r
84 \r
85 #ifndef USE_HTML_CHUNKS\r
86         #define USE_HTML_CHUNKS                         ( 0 )\r
87 #endif\r
88 \r
89 #if !defined( ARRAY_SIZE )\r
90         #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )\r
91 #endif\r
92 \r
93 /* Some defines to make the code more readbale */\r
94 #define pcCOMMAND_BUFFER        pxClient->pxParent->pcCommandBuffer\r
95 #define pcNEW_DIR                       pxClient->pxParent->pcNewDir\r
96 #define pcFILE_BUFFER           pxClient->pxParent->pcFileBuffer\r
97 \r
98 #ifndef ipconfigHTTP_REQUEST_CHARACTER\r
99         #define ipconfigHTTP_REQUEST_CHARACTER          '?'\r
100 #endif\r
101 \r
102 /*_RB_ Need comment block, although fairly self evident. */\r
103 static void prvFileClose( HTTPClient_t *pxClient );\r
104 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );\r
105 static const char *pcGetContentsType( const char *apFname );\r
106 static BaseType_t prvOpenURL( HTTPClient_t *pxClient );\r
107 static BaseType_t prvSendFile( HTTPClient_t *pxClient );\r
108 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );\r
109 \r
110 static const char pcEmptyString[1] = { '\0' };\r
111 \r
112 typedef struct xTYPE_COUPLE\r
113 {\r
114         const char *pcExtension;\r
115         const char *pcType;\r
116 } TypeCouple_t;\r
117 \r
118 static TypeCouple_t pxTypeCouples[ ] =\r
119 {\r
120         { "html", "text/html" },\r
121         { "css",  "text/css" },\r
122         { "js",   "text/javascript" },\r
123         { "png",  "image/png" },\r
124         { "jpg",  "image/jpeg" },\r
125         { "gif",  "image/gif" },\r
126         { "txt",  "text/plain" },\r
127         { "mp3",  "audio/mpeg3" },\r
128         { "wav",  "audio/wav" },\r
129         { "flac", "audio/ogg" },\r
130         { "pdf",  "application/pdf" },\r
131         { "ttf",  "application/x-font-ttf" },\r
132         { "ttc",  "application/x-font-ttf" }\r
133 };\r
134 \r
135 void vHTTPClientDelete( TCPClient_t *pxTCPClient )\r
136 {\r
137 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;\r
138 \r
139         /* This HTTP client stops, close / release all resources. */\r
140         if( pxClient->xSocket != FREERTOS_NO_SOCKET )\r
141         {\r
142                 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );\r
143                 FreeRTOS_closesocket( pxClient->xSocket );\r
144                 pxClient->xSocket = FREERTOS_NO_SOCKET;\r
145         }\r
146         prvFileClose( pxClient );\r
147 }\r
148 /*-----------------------------------------------------------*/\r
149 \r
150 static void prvFileClose( HTTPClient_t *pxClient )\r
151 {\r
152         if( pxClient->pxFileHandle != NULL )\r
153         {\r
154                 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );\r
155                 ff_fclose( pxClient->pxFileHandle );\r
156                 pxClient->pxFileHandle = NULL;\r
157         }\r
158 }\r
159 /*-----------------------------------------------------------*/\r
160 \r
161 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )\r
162 {\r
163 struct xTCP_SERVER *pxParent = pxClient->pxParent;\r
164 BaseType_t xRc;\r
165 \r
166         /* A normal command reply on the main socket (port 21). */\r
167         char *pcBuffer = pxParent->pcFileBuffer;\r
168 \r
169         xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),\r
170                 "HTTP/1.1 %d %s\r\n"\r
171 #if     USE_HTML_CHUNKS\r
172                 "Transfer-Encoding: chunked\r\n"\r
173 #endif\r
174                 "Content-Type: %s\r\n"\r
175                 "Connection: keep-alive\r\n"\r
176                 "%s\r\n",\r
177                 ( int ) xCode,\r
178                 webCodename (xCode),\r
179                 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",\r
180                 pxParent->pcExtraContents );\r
181 \r
182         pxParent->pcContentsType[0] = '\0';\r
183         pxParent->pcExtraContents[0] = '\0';\r
184 \r
185         xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );\r
186         pxClient->bits.bReplySent = pdTRUE_UNSIGNED;\r
187 \r
188         return xRc;\r
189 }\r
190 /*-----------------------------------------------------------*/\r
191 \r
192 static BaseType_t prvSendFile( HTTPClient_t *pxClient )\r
193 {\r
194 size_t uxSpace;\r
195 size_t uxCount;\r
196 BaseType_t xRc = 0;\r
197 \r
198         if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )\r
199         {\r
200                 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;\r
201 \r
202                 strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );\r
203                 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),\r
204                         "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );\r
205 \r
206                 /* "Requested file action OK". */\r
207                 xRc = prvSendReply( pxClient, WEB_REPLY_OK );\r
208         }\r
209 \r
210         if( xRc >= 0 ) do\r
211         {\r
212                 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );\r
213 \r
214                 if( pxClient->uxBytesLeft < uxSpace )\r
215                 {\r
216                         uxCount = pxClient->uxBytesLeft;\r
217                 }\r
218                 else\r
219                 {\r
220                         uxCount = uxSpace;\r
221                 }\r
222 \r
223                 if( uxCount > 0u )\r
224                 {\r
225                         if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )\r
226                         {\r
227                                 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );\r
228                         }\r
229                         ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );\r
230                         pxClient->uxBytesLeft -= uxCount;\r
231 \r
232                         xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );\r
233                         if( xRc < 0 )\r
234                         {\r
235                                 break;\r
236                         }\r
237                 }\r
238         } while( uxCount > 0u );\r
239 \r
240         if( pxClient->uxBytesLeft == 0u )\r
241         {\r
242                 /* Writing is ready, no need for further 'eSELECT_WRITE' events. */\r
243                 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
244                 prvFileClose( pxClient );\r
245         }\r
246         else\r
247         {\r
248                 /* Wake up the TCP task as soon as this socket may be written to. */\r
249                 FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
250         }\r
251 \r
252         return xRc;\r
253 }\r
254 /*-----------------------------------------------------------*/\r
255 \r
256 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )\r
257 {\r
258 BaseType_t xRc;\r
259 char pcSlash[ 2 ];\r
260 \r
261         pxClient->bits.ulFlags = 0;\r
262 \r
263         #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )\r
264         {\r
265                 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )\r
266                 {\r
267                 size_t xResult;\r
268 \r
269                         xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );\r
270                         if( xResult > 0 )\r
271                         {\r
272                                 strcpy( pxClient->pxParent->pcContentsType, "text/html" );\r
273                                 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),\r
274                                         "Content-Length: %d\r\n", ( int ) xResult );\r
275                                 xRc = prvSendReply( pxClient, WEB_REPLY_OK );   /* "Requested file action OK" */\r
276                                 if( xRc > 0 )\r
277                                 {\r
278                                         xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );\r
279                                 }\r
280                                 /* Although against the coding standard of FreeRTOS, a return is\r
281                                 done here  to simplify this conditional code. */\r
282                                 return xRc;\r
283                         }\r
284                 }\r
285         }\r
286         #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */\r
287 \r
288         if( pxClient->pcUrlData[ 0 ] != '/' )\r
289         {\r
290                 /* Insert a slash before the file name. */\r
291                 pcSlash[ 0 ] = '/';\r
292                 pcSlash[ 1 ] = '\0';\r
293         }\r
294         else\r
295         {\r
296                 /* The browser provided a starting '/' already. */\r
297                 pcSlash[ 0 ] = '\0';\r
298         }\r
299         snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",\r
300                 pxClient->pcRootDir,\r
301                 pcSlash,\r
302                 pxClient->pcUrlData);\r
303 \r
304         pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );\r
305 \r
306         FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,\r
307                 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );\r
308 \r
309         if( pxClient->pxFileHandle == NULL )\r
310         {\r
311                 /* "404 File not found". */\r
312                 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );\r
313         }\r
314         else\r
315         {\r
316                 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;\r
317                 xRc = prvSendFile( pxClient );\r
318         }\r
319 \r
320         return xRc;\r
321 }\r
322 /*-----------------------------------------------------------*/\r
323 \r
324 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )\r
325 {\r
326 BaseType_t xResult = 0;\r
327 \r
328         /* A new command has been received. Process it. */\r
329         switch( xIndex )\r
330         {\r
331         case ECMD_GET:\r
332                 xResult = prvOpenURL( pxClient );\r
333                 break;\r
334 \r
335         case ECMD_HEAD:\r
336         case ECMD_POST:\r
337         case ECMD_PUT:\r
338         case ECMD_DELETE:\r
339         case ECMD_TRACE:\r
340         case ECMD_OPTIONS:\r
341         case ECMD_CONNECT:\r
342         case ECMD_PATCH:\r
343         case ECMD_UNK:\r
344                 {\r
345                         FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",\r
346                                 xWebCommands[xIndex].pcCommandName ) );\r
347                 }\r
348                 break;\r
349         }\r
350 \r
351         return xResult;\r
352 }\r
353 /*-----------------------------------------------------------*/\r
354 \r
355 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )\r
356 {\r
357 BaseType_t xRc;\r
358 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;\r
359 \r
360         if( pxClient->pxFileHandle != NULL )\r
361         {\r
362                 prvSendFile( pxClient );\r
363         }\r
364 \r
365         xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );\r
366 \r
367         if( xRc > 0 )\r
368         {\r
369         BaseType_t xIndex;\r
370         const char *pcEndOfCmd;\r
371         const struct xWEB_COMMAND *curCmd;\r
372         char *pcBuffer = pcCOMMAND_BUFFER;\r
373 \r
374                 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )\r
375                 {\r
376                         pcBuffer[ xRc ] = '\0';\r
377                 }\r
378                 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )\r
379                 {\r
380                         pcBuffer[ --xRc ] = '\0';\r
381                 }\r
382                 pcEndOfCmd = pcBuffer + xRc;\r
383 \r
384                 curCmd = xWebCommands;\r
385 \r
386                 /* Pointing to "/index.html HTTP/1.1". */\r
387                 pxClient->pcUrlData = pcBuffer;\r
388 \r
389                 /* Pointing to "HTTP/1.1". */\r
390                 pxClient->pcRestData = pcEmptyString;\r
391 \r
392                 /* Last entry is "ECMD_UNK". */\r
393                 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )\r
394                 {\r
395                 BaseType_t xLength;\r
396 \r
397                         xLength = curCmd->xCommandLength;\r
398                         if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )\r
399                         {\r
400                         char *pcLastPtr;\r
401 \r
402                                 pxClient->pcUrlData += xLength + 1;\r
403                                 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )\r
404                                 {\r
405                                         char ch = *pcLastPtr;\r
406                                         if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )\r
407                                         {\r
408                                                 *pcLastPtr = '\0';\r
409                                                 pxClient->pcRestData = pcLastPtr + 1;\r
410                                                 break;\r
411                                         }\r
412                                 }\r
413                                 break;\r
414                         }\r
415                 }\r
416 \r
417                 if( xIndex < ( WEB_CMD_COUNT - 1 ) )\r
418                 {\r
419                         xRc = prvProcessCmd( pxClient, xIndex );\r
420                 }\r
421         }\r
422         else if( xRc < 0 )\r
423         {\r
424                 /* The connection will be closed and the client will be deleted. */\r
425                 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );\r
426         }\r
427         return xRc;\r
428 }\r
429 /*-----------------------------------------------------------*/\r
430 \r
431 static const char *pcGetContentsType (const char *apFname)\r
432 {\r
433         const char *slash = NULL;\r
434         const char *dot = NULL;\r
435         const char *ptr;\r
436         const char *pcResult = "text/html";\r
437         BaseType_t x;\r
438 \r
439         for( ptr = apFname; *ptr; ptr++ )\r
440         {\r
441                 if (*ptr == '.') dot = ptr;\r
442                 if (*ptr == '/') slash = ptr;\r
443         }\r
444         if( dot > slash )\r
445         {\r
446                 dot++;\r
447                 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )\r
448                 {\r
449                         if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )\r
450                         {\r
451                                 pcResult = pxTypeCouples[ x ].pcType;\r
452                                 break;\r
453                         }\r
454                 }\r
455         }\r
456         return pcResult;\r
457 }\r
458 \r
459 #endif /* ipconfigUSE_HTTP */\r
460 \r