]> begriffs open source - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/protocols/HTTP/FreeRTOS_HTTP_server.c
Added +TCP code to main repo.
[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 /* FreeRTOS+FAT includes. */\r
76 #include "ff_stdio.h"\r
77 \r
78 #ifndef HTTP_SERVER_BACKLOG\r
79         #define HTTP_SERVER_BACKLOG                     ( 12 )\r
80 #endif\r
81 \r
82 #ifndef USE_HTML_CHUNKS\r
83         #define USE_HTML_CHUNKS                         ( 0 )\r
84 #endif\r
85 \r
86 #if !defined( ARRAY_SIZE )\r
87         #define ARRAY_SIZE(x) ( BaseType_t ) (sizeof( x ) / sizeof( x )[ 0 ] )\r
88 #endif\r
89 \r
90 /* Some defines to make the code more readbale */\r
91 #define pcCOMMAND_BUFFER        pxClient->pxParent->pcCommandBuffer\r
92 #define pcNEW_DIR                       pxClient->pxParent->pcNewDir\r
93 #define pcFILE_BUFFER           pxClient->pxParent->pcFileBuffer\r
94 \r
95 #ifndef ipconfigHTTP_REQUEST_CHARACTER\r
96         #define ipconfigHTTP_REQUEST_CHARACTER          '?'\r
97 #endif\r
98 \r
99 /*_RB_ Need comment block, although fairly self evident. */\r
100 static void prvFileClose( HTTPClient_t *pxClient );\r
101 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex );\r
102 static const char *pcGetContentsType( const char *apFname );\r
103 static BaseType_t prvOpenURL( HTTPClient_t *pxClient );\r
104 static BaseType_t prvSendFile( HTTPClient_t *pxClient );\r
105 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode );\r
106 \r
107 static const char pcEmptyString[1] = { '\0' };\r
108 \r
109 typedef struct xTYPE_COUPLE\r
110 {\r
111         const char *pcExtension;\r
112         const char *pcType;\r
113 } TypeCouple_t;\r
114 \r
115 static TypeCouple_t pxTypeCouples[ ] =\r
116 {\r
117         { "html", "text/html" },\r
118         { "css",  "text/css" },\r
119         { "js",   "text/javascript" },\r
120         { "png",  "image/png" },\r
121         { "jpg",  "image/jpeg" },\r
122         { "gif",  "image/gif" },\r
123         { "txt",  "text/plain" },\r
124         { "mp3",  "audio/mpeg3" },\r
125         { "wav",  "audio/wav" },\r
126         { "flac", "audio/ogg" },\r
127         { "pdf",  "application/pdf" },\r
128         { "ttf",  "application/x-font-ttf" },\r
129         { "ttc",  "application/x-font-ttf" }\r
130 };\r
131 \r
132 void vHTTPClientDelete( TCPClient_t *pxTCPClient )\r
133 {\r
134 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;\r
135 \r
136         /* This HTTP client stops, close / release all resources. */\r
137         if( pxClient->xSocket != FREERTOS_NO_SOCKET )\r
138         {\r
139                 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_ALL );\r
140                 FreeRTOS_closesocket( pxClient->xSocket );\r
141                 pxClient->xSocket = FREERTOS_NO_SOCKET;\r
142         }\r
143         prvFileClose( pxClient );\r
144 }\r
145 /*-----------------------------------------------------------*/\r
146 \r
147 static void prvFileClose( HTTPClient_t *pxClient )\r
148 {\r
149         if( pxClient->pxFileHandle != NULL )\r
150         {\r
151                 FreeRTOS_printf( ( "Closing file: %s\n", pxClient->pcCurrentFilename ) );\r
152                 ff_fclose( pxClient->pxFileHandle );\r
153                 pxClient->pxFileHandle = NULL;\r
154         }\r
155 }\r
156 /*-----------------------------------------------------------*/\r
157 \r
158 static BaseType_t prvSendReply( HTTPClient_t *pxClient, BaseType_t xCode )\r
159 {\r
160 struct xTCP_SERVER *pxParent = pxClient->pxParent;\r
161 BaseType_t xRc;\r
162 \r
163         /* A normal command reply on the main socket (port 21). */\r
164         char *pcBuffer = pxParent->pcFileBuffer;\r
165 \r
166         xRc = snprintf( pcBuffer, sizeof( pxParent->pcFileBuffer ),\r
167                 "HTTP/1.1 %d %s\r\n"\r
168 #if     USE_HTML_CHUNKS\r
169                 "Transfer-Encoding: chunked\r\n"\r
170 #endif\r
171                 "Content-Type: %s\r\n"\r
172                 "Connection: keep-alive\r\n"\r
173                 "%s\r\n",\r
174                 ( int ) xCode,\r
175                 webCodename (xCode),\r
176                 pxParent->pcContentsType[0] ? pxParent->pcContentsType : "text/html",\r
177                 pxParent->pcExtraContents );\r
178 \r
179         pxParent->pcContentsType[0] = '\0';\r
180         pxParent->pcExtraContents[0] = '\0';\r
181 \r
182         xRc = FreeRTOS_send( pxClient->xSocket, ( const void * ) pcBuffer, xRc, 0 );\r
183         pxClient->bits.bReplySent = pdTRUE_UNSIGNED;\r
184 \r
185         return xRc;\r
186 }\r
187 /*-----------------------------------------------------------*/\r
188 \r
189 static BaseType_t prvSendFile( HTTPClient_t *pxClient )\r
190 {\r
191 size_t uxSpace;\r
192 size_t uxCount;\r
193 BaseType_t xRc = 0;\r
194 \r
195         if( pxClient->bits.bReplySent == pdFALSE_UNSIGNED )\r
196         {\r
197                 pxClient->bits.bReplySent = pdTRUE_UNSIGNED;\r
198 \r
199                 strcpy( pxClient->pxParent->pcContentsType, pcGetContentsType( pxClient->pcCurrentFilename ) );\r
200                 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),\r
201                         "Content-Length: %d\r\n", ( int ) pxClient->uxBytesLeft );\r
202 \r
203                 /* "Requested file action OK". */\r
204                 xRc = prvSendReply( pxClient, WEB_REPLY_OK );\r
205         }\r
206 \r
207         if( xRc >= 0 ) do\r
208         {\r
209                 uxSpace = FreeRTOS_tx_space( pxClient->xSocket );\r
210 \r
211                 if( pxClient->uxBytesLeft < uxSpace )\r
212                 {\r
213                         uxCount = pxClient->uxBytesLeft;\r
214                 }\r
215                 else\r
216                 {\r
217                         uxCount = uxSpace;\r
218                 }\r
219 \r
220                 if( uxCount > 0u )\r
221                 {\r
222                         if( uxCount > sizeof( pxClient->pxParent->pcFileBuffer ) )\r
223                         {\r
224                                 uxCount = sizeof( pxClient->pxParent->pcFileBuffer );\r
225                         }\r
226                         ff_fread( pxClient->pxParent->pcFileBuffer, 1, uxCount, pxClient->pxFileHandle );\r
227                         pxClient->uxBytesLeft -= uxCount;\r
228 \r
229                         xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pxParent->pcFileBuffer, uxCount, 0 );\r
230                         if( xRc < 0 )\r
231                         {\r
232                                 break;\r
233                         }\r
234                 }\r
235         } while( uxCount > 0u );\r
236 \r
237         if( pxClient->uxBytesLeft == 0u )\r
238         {\r
239                 /* Writing is ready, no need for further 'eSELECT_WRITE' events. */\r
240                 FreeRTOS_FD_CLR( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
241                 prvFileClose( pxClient );\r
242         }\r
243         else\r
244         {\r
245                 /* Wake up the TCP task as soon as this socket may be written to. */\r
246                 FreeRTOS_FD_SET( pxClient->xSocket, pxClient->pxParent->xSocketSet, eSELECT_WRITE );\r
247         }\r
248 \r
249         return xRc;\r
250 }\r
251 /*-----------------------------------------------------------*/\r
252 \r
253 static BaseType_t prvOpenURL( HTTPClient_t *pxClient )\r
254 {\r
255 BaseType_t xRc;\r
256 char pcSlash[ 2 ];\r
257 \r
258         pxClient->bits.ulFlags = 0;\r
259 \r
260         #if( ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK != 0 )\r
261         {\r
262                 if( strchr( pxClient->pcUrlData, ipconfigHTTP_REQUEST_CHARACTER ) != NULL )\r
263                 {\r
264                 size_t xResult;\r
265 \r
266                         xResult = uxApplicationHTTPHandleRequestHook( pxClient->pcUrlData, pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ) );\r
267                         if( xResult > 0 )\r
268                         {\r
269                                 strcpy( pxClient->pxParent->pcContentsType, "text/html" );\r
270                                 snprintf( pxClient->pxParent->pcExtraContents, sizeof( pxClient->pxParent->pcExtraContents ),\r
271                                         "Content-Length: %d\r\n", ( int ) xResult );\r
272                                 xRc = prvSendReply( pxClient, WEB_REPLY_OK );   /* "Requested file action OK" */\r
273                                 if( xRc > 0 )\r
274                                 {\r
275                                         xRc = FreeRTOS_send( pxClient->xSocket, pxClient->pcCurrentFilename, xResult, 0 );\r
276                                 }\r
277                                 /* Although against the coding standard of FreeRTOS, a return is\r
278                                 done here  to simplify this conditional code. */\r
279                                 return xRc;\r
280                         }\r
281                 }\r
282         }\r
283         #endif /* ipconfigHTTP_HAS_HANDLE_REQUEST_HOOK */\r
284 \r
285         if( pxClient->pcUrlData[ 0 ] != '/' )\r
286         {\r
287                 /* Insert a slash before the file name. */\r
288                 pcSlash[ 0 ] = '/';\r
289                 pcSlash[ 1 ] = '\0';\r
290         }\r
291         else\r
292         {\r
293                 /* The browser provided a starting '/' already. */\r
294                 pcSlash[ 0 ] = '\0';\r
295         }\r
296         snprintf( pxClient->pcCurrentFilename, sizeof( pxClient->pcCurrentFilename ), "%s%s%s",\r
297                 pxClient->pcRootDir,\r
298                 pcSlash,\r
299                 pxClient->pcUrlData);\r
300 \r
301         pxClient->pxFileHandle = ff_fopen( pxClient->pcCurrentFilename, "rb" );\r
302 \r
303         FreeRTOS_printf( ( "Open file '%s': %s\n", pxClient->pcCurrentFilename,\r
304                 pxClient->pxFileHandle != NULL ? "Ok" : strerror( stdioGET_ERRNO() ) ) );\r
305 \r
306         if( pxClient->pxFileHandle == NULL )\r
307         {\r
308                 /* "404 File not found". */\r
309                 xRc = prvSendReply( pxClient, WEB_NOT_FOUND );\r
310         }\r
311         else\r
312         {\r
313                 pxClient->uxBytesLeft = ( size_t ) pxClient->pxFileHandle->ulFileSize;\r
314                 xRc = prvSendFile( pxClient );\r
315         }\r
316 \r
317         return xRc;\r
318 }\r
319 /*-----------------------------------------------------------*/\r
320 \r
321 static BaseType_t prvProcessCmd( HTTPClient_t *pxClient, BaseType_t xIndex )\r
322 {\r
323 BaseType_t xResult = 0;\r
324 \r
325         /* A new command has been received. Process it. */\r
326         switch( xIndex )\r
327         {\r
328         case ECMD_GET:\r
329                 xResult = prvOpenURL( pxClient );\r
330                 break;\r
331 \r
332         case ECMD_HEAD:\r
333         case ECMD_POST:\r
334         case ECMD_PUT:\r
335         case ECMD_DELETE:\r
336         case ECMD_TRACE:\r
337         case ECMD_OPTIONS:\r
338         case ECMD_CONNECT:\r
339         case ECMD_PATCH:\r
340         case ECMD_UNK:\r
341                 {\r
342                         FreeRTOS_printf( ( "prvProcessCmd: Not implemented: %s\n",\r
343                                 xWebCommands[xIndex].pcCommandName ) );\r
344                 }\r
345                 break;\r
346         }\r
347 \r
348         return xResult;\r
349 }\r
350 /*-----------------------------------------------------------*/\r
351 \r
352 BaseType_t xHTTPClientWork( TCPClient_t *pxTCPClient )\r
353 {\r
354 BaseType_t xRc;\r
355 HTTPClient_t *pxClient = ( HTTPClient_t * ) pxTCPClient;\r
356 \r
357         if( pxClient->pxFileHandle != NULL )\r
358         {\r
359                 prvSendFile( pxClient );\r
360         }\r
361 \r
362         xRc = FreeRTOS_recv( pxClient->xSocket, ( void * )pcCOMMAND_BUFFER, sizeof( pcCOMMAND_BUFFER ), 0 );\r
363 \r
364         if( xRc > 0 )\r
365         {\r
366         BaseType_t xIndex;\r
367         const char *pcEndOfCmd;\r
368         const struct xWEB_COMMAND *curCmd;\r
369         char *pcBuffer = pcCOMMAND_BUFFER;\r
370 \r
371                 if( xRc < ( BaseType_t ) sizeof( pcCOMMAND_BUFFER ) )\r
372                 {\r
373                         pcBuffer[ xRc ] = '\0';\r
374                 }\r
375                 while( xRc && ( pcBuffer[ xRc - 1 ] == 13 || pcBuffer[ xRc - 1 ] == 10 ) )\r
376                 {\r
377                         pcBuffer[ --xRc ] = '\0';\r
378                 }\r
379                 pcEndOfCmd = pcBuffer + xRc;\r
380 \r
381                 curCmd = xWebCommands;\r
382 \r
383                 /* Pointing to "/index.html HTTP/1.1". */\r
384                 pxClient->pcUrlData = pcBuffer;\r
385 \r
386                 /* Pointing to "HTTP/1.1". */\r
387                 pxClient->pcRestData = pcEmptyString;\r
388 \r
389                 /* Last entry is "ECMD_UNK". */\r
390                 for( xIndex = 0; xIndex < WEB_CMD_COUNT - 1; xIndex++, curCmd++ )\r
391                 {\r
392                 BaseType_t xLength;\r
393 \r
394                         xLength = curCmd->xCommandLength;\r
395                         if( ( xRc >= xLength ) && ( memcmp( curCmd->pcCommandName, pcBuffer, xLength ) == 0 ) )\r
396                         {\r
397                         char *pcLastPtr;\r
398 \r
399                                 pxClient->pcUrlData += xLength + 1;\r
400                                 for( pcLastPtr = (char *)pxClient->pcUrlData; pcLastPtr < pcEndOfCmd; pcLastPtr++ )\r
401                                 {\r
402                                         char ch = *pcLastPtr;\r
403                                         if( ( ch == '\0' ) || ( strchr( "\n\r \t", ch ) != NULL ) )\r
404                                         {\r
405                                                 *pcLastPtr = '\0';\r
406                                                 pxClient->pcRestData = pcLastPtr + 1;\r
407                                                 break;\r
408                                         }\r
409                                 }\r
410                                 break;\r
411                         }\r
412                 }\r
413 \r
414                 if( xIndex < ( WEB_CMD_COUNT - 1 ) )\r
415                 {\r
416                         xRc = prvProcessCmd( pxClient, xIndex );\r
417                 }\r
418         }\r
419         else if( xRc < 0 )\r
420         {\r
421                 /* The connection will be closed and the client will be deleted. */\r
422                 FreeRTOS_printf( ( "xHTTPClientWork: rc = %ld\n", xRc ) );\r
423         }\r
424         return xRc;\r
425 }\r
426 /*-----------------------------------------------------------*/\r
427 \r
428 static const char *pcGetContentsType (const char *apFname)\r
429 {\r
430         const char *slash = NULL;\r
431         const char *dot = NULL;\r
432         const char *ptr;\r
433         const char *pcResult = "text/html";\r
434         BaseType_t x;\r
435 \r
436         for( ptr = apFname; *ptr; ptr++ )\r
437         {\r
438                 if (*ptr == '.') dot = ptr;\r
439                 if (*ptr == '/') slash = ptr;\r
440         }\r
441         if( dot > slash )\r
442         {\r
443                 dot++;\r
444                 for( x = 0; x < ARRAY_SIZE( pxTypeCouples ); x++ )\r
445                 {\r
446                         if( strcasecmp( dot, pxTypeCouples[ x ].pcExtension ) == 0 )\r
447                         {\r
448                                 pcResult = pxTypeCouples[ x ].pcType;\r
449                                 break;\r
450                         }\r
451                 }\r
452         }\r
453         return pcResult;\r
454 }\r
455 \r