]> git.sur5r.net Git - freertos/blob - Demo/uIP_Demo_IAR_ARM7/uip/httpd.c
Start to re-arrange files to include FreeRTOS+ in main download.
[freertos] / Demo / uIP_Demo_IAR_ARM7 / uip / httpd.c
1 /**\r
2  * \addtogroup exampleapps\r
3  * @{\r
4  */\r
5 \r
6 /**\r
7  * \defgroup httpd Web server\r
8  * @{\r
9  *\r
10  * The uIP web server is a very simplistic implementation of an HTTP\r
11  * server. It can serve web pages and files from a read-only ROM\r
12  * filesystem, and provides a very small scripting language.\r
13  *\r
14  * The script language is very simple and works as follows. Each\r
15  * script line starts with a command character, either "i", "t", "c",\r
16  * "#" or ".".  The "i" command tells the script interpreter to\r
17  * "include" a file from the virtual file system and output it to the\r
18  * web browser. The "t" command should be followed by a line of text\r
19  * that is to be output to the browser. The "c" command is used to\r
20  * call one of the C functions from the httpd-cgi.c file. A line that\r
21  * starts with a "#" is ignored (i.e., the "#" denotes a comment), and\r
22  * the "." denotes the last script line.\r
23  *\r
24  * The script that produces the file statistics page looks somewhat\r
25  * like this:\r
26  *\r
27  \code\r
28 i /header.html\r
29 t <h1>File statistics</h1><br><table width="100%">\r
30 t <tr><td><a href="/index.html">/index.html</a></td><td>\r
31 c a /index.html\r
32 t </td></tr> <tr><td><a href="/cgi/files">/cgi/files</a></td><td>\r
33 c a /cgi/files\r
34 t </td></tr> <tr><td><a href="/cgi/tcp">/cgi/tcp</a></td><td>\r
35 c a /cgi/tcp\r
36 t </td></tr> <tr><td><a href="/404.html">/404.html</a></td><td>\r
37 c a /404.html\r
38 t </td></tr></table>\r
39 i /footer.plain\r
40 .\r
41  \endcode\r
42  *\r
43  */\r
44 \r
45 \r
46 /**\r
47  * \file\r
48  * HTTP server.\r
49  * \author Adam Dunkels <adam@dunkels.com>\r
50  */\r
51 \r
52 /*\r
53  * Copyright (c) 2001, Adam Dunkels.\r
54  * All rights reserved.\r
55  *\r
56  * Redistribution and use in source and binary forms, with or without\r
57  * modification, are permitted provided that the following conditions\r
58  * are met:\r
59  * 1. Redistributions of source code must retain the above copyright\r
60  *    notice, this list of conditions and the following disclaimer.\r
61  * 2. Redistributions in binary form must reproduce the above copyright\r
62  *    notice, this list of conditions and the following disclaimer in the\r
63  *    documentation and/or other materials provided with the distribution.\r
64  * 3. The name of the author may not be used to endorse or promote\r
65  *    products derived from this software without specific prior\r
66  *    written permission.\r
67  *\r
68  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS\r
69  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
70  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
71  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\r
72  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
73  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE\r
74  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
75  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\r
76  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
77  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
78  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
79  *\r
80  * This file is part of the uIP TCP/IP stack.\r
81  *\r
82  * $Id: httpd.c,v 1.28.2.6 2003/10/07 13:22:27 adam Exp $\r
83  *\r
84  */\r
85 \r
86 \r
87 #include "uip.h"\r
88 #include "httpd.h"\r
89 #include "fs.h"\r
90 #include "fsdata.h"\r
91 #include "cgi.h"\r
92 \r
93 #define NULL (void *)0\r
94 \r
95 /* The HTTP server states: */\r
96 #define HTTP_NOGET        0\r
97 #define HTTP_FILE         1\r
98 #define HTTP_TEXT         2\r
99 #define HTTP_FUNC         3\r
100 #define HTTP_END          4\r
101 \r
102 #ifdef DEBUG\r
103 #include <stdio.h>\r
104 #define PRINT(x)\r
105 #define PRINTLN(x)\r
106 #else /* DEBUG */\r
107 #define PRINT(x)\r
108 #define PRINTLN(x)\r
109 #endif /* DEBUG */\r
110 \r
111 struct httpd_state *hs;\r
112 \r
113 extern const struct fsdata_file file_index_html;\r
114 extern const struct fsdata_file file_404_html;\r
115 \r
116 static void next_scriptline(void);\r
117 static void next_scriptstate(void);\r
118 \r
119 #define ISO_G        0x47\r
120 #define ISO_E        0x45\r
121 #define ISO_T        0x54\r
122 #define ISO_slash    0x2f\r
123 #define ISO_c        0x63\r
124 #define ISO_g        0x67\r
125 #define ISO_i        0x69\r
126 #define ISO_space    0x20\r
127 #define ISO_nl       0x0a\r
128 #define ISO_cr       0x0d\r
129 #define ISO_a        0x61\r
130 #define ISO_t        0x74\r
131 #define ISO_hash     0x23\r
132 #define ISO_period   0x2e\r
133 \r
134 #define httpPORT        80\r
135 \r
136 /*-----------------------------------------------------------------------------------*/\r
137 /**\r
138  * Initialize the web server.\r
139  *\r
140  * Starts to listen for incoming connection requests on TCP port 80.\r
141  */\r
142 /*-----------------------------------------------------------------------------------*/\r
143 void\r
144 httpd_init(void)\r
145 {\r
146   fs_init();\r
147 \r
148   /* Listen to port 80. */\r
149   uip_listen(HTONS(httpPORT));\r
150 }\r
151 /*-----------------------------------------------------------------------------------*/\r
152 void\r
153 httpd_appcall(void)\r
154 {\r
155   struct fs_file fsfile;\r
156 \r
157   u8_t i;\r
158 \r
159   switch(uip_conn->lport) {\r
160     /* This is the web server: */\r
161   case HTONS(httpPORT):\r
162     /* Pick out the application state from the uip_conn structure. */\r
163     hs = (struct httpd_state *)(uip_conn->appstate);\r
164 \r
165     /* We use the uip_ test functions to deduce why we were\r
166        called. If uip_connected() is non-zero, we were called\r
167        because a remote host has connected to us. If\r
168        uip_newdata() is non-zero, we were called because the\r
169        remote host has sent us new data, and if uip_acked() is\r
170        non-zero, the remote host has acknowledged the data we\r
171        previously sent to it. */\r
172     if(uip_connected()) {\r
173       /* Since we have just been connected with the remote host, we\r
174          reset the state for this connection. The ->count variable\r
175          contains the amount of data that is yet to be sent to the\r
176          remote host, and the ->state is set to HTTP_NOGET to signal\r
177          that we haven't received any HTTP GET request for this\r
178          connection yet. */\r
179 \r
180       hs->state = HTTP_NOGET;\r
181       hs->count = 0;\r
182       return;\r
183 \r
184     } else if(uip_poll()) {\r
185       /* If we are polled ten times, we abort the connection. This is\r
186          because we don't want connections lingering indefinately in\r
187          the system. */\r
188       if(hs->count++ >= 10) {\r
189         uip_abort();\r
190       }\r
191       return;\r
192     } else if(uip_newdata() && hs->state == HTTP_NOGET) {\r
193       /* This is the first data we receive, and it should contain a\r
194          GET. */\r
195 \r
196       /* Check for GET. */\r
197       if(uip_appdata[0] != ISO_G ||\r
198          uip_appdata[1] != ISO_E ||\r
199          uip_appdata[2] != ISO_T ||\r
200          uip_appdata[3] != ISO_space) {\r
201         /* If it isn't a GET, we abort the connection. */\r
202         uip_abort();\r
203         return;\r
204       }\r
205         \r
206       /* Find the file we are looking for. */\r
207       for(i = 4; i < 40; ++i) {\r
208         if(uip_appdata[i] == ISO_space ||\r
209            uip_appdata[i] == ISO_cr ||\r
210            uip_appdata[i] == ISO_nl) {\r
211           uip_appdata[i] = 0;\r
212           break;\r
213         }\r
214       }\r
215 \r
216       PRINT("request for file ");\r
217       PRINTLN(&uip_appdata[4]);\r
218 \r
219       /* Check for a request for "/". */\r
220       if(uip_appdata[4] == ISO_slash &&\r
221          uip_appdata[5] == 0) {\r
222         fs_open(file_index_html.name, &fsfile);\r
223       } else {\r
224         if(!fs_open((const char *)&uip_appdata[4], &fsfile)) {\r
225           PRINTLN("couldn't open file");\r
226           fs_open(file_404_html.name, &fsfile);\r
227         }\r
228       }\r
229 \r
230 \r
231       if(uip_appdata[4] == ISO_slash &&\r
232          uip_appdata[5] == ISO_c &&\r
233          uip_appdata[6] == ISO_g &&\r
234          uip_appdata[7] == ISO_i &&\r
235          uip_appdata[8] == ISO_slash) {\r
236         /* If the request is for a file that starts with "/cgi/", we\r
237            prepare for invoking a script. */    \r
238         hs->script = fsfile.data;\r
239         next_scriptstate();\r
240       } else {\r
241         hs->script = NULL;\r
242         /* The web server is now no longer in the HTTP_NOGET state, but\r
243            in the HTTP_FILE state since is has now got the GET from\r
244            the client and will start transmitting the file. */\r
245         hs->state = HTTP_FILE;\r
246 \r
247         /* Point the file pointers in the connection state to point to\r
248            the first byte of the file. */\r
249         hs->dataptr = fsfile.data;\r
250         hs->count = fsfile.len; \r
251       }\r
252     }\r
253 \r
254 \r
255     if(hs->state != HTTP_FUNC) {\r
256       /* Check if the client (remote end) has acknowledged any data that\r
257          we've previously sent. If so, we move the file pointer further\r
258          into the file and send back more data. If we are out of data to\r
259          send, we close the connection. */\r
260       if(uip_acked()) {\r
261         if(hs->count >= uip_conn->len) {\r
262           hs->count -= uip_conn->len;\r
263           hs->dataptr += uip_conn->len;\r
264         } else {\r
265           hs->count = 0;\r
266         }\r
267         \r
268         if(hs->count == 0) {\r
269           if(hs->script != NULL) {\r
270             next_scriptline();\r
271             next_scriptstate();\r
272           } else {\r
273             uip_close();\r
274           }\r
275         }\r
276       }\r
277     } else {\r
278       /* Call the CGI function. */\r
279       if(cgitab[hs->script[2] - ISO_a](uip_acked())) {\r
280         /* If the function returns non-zero, we jump to the next line\r
281            in the script. */\r
282         next_scriptline();\r
283         next_scriptstate();\r
284       }\r
285     }\r
286 \r
287     if(hs->state != HTTP_FUNC && !uip_poll()) {\r
288       /* Send a piece of data, but not more than the MSS of the\r
289          connection. */\r
290       uip_send(( void * ) hs->dataptr, hs->count);\r
291     }\r
292 \r
293     /* Finally, return to uIP. Our outgoing packet will soon be on its\r
294        way... */\r
295     return;\r
296 \r
297   default:\r
298     /* Should never happen. */\r
299     uip_abort();\r
300     break;\r
301   }\r
302 }\r
303 /*-----------------------------------------------------------------------------------*/\r
304 /* next_scriptline():\r
305  *\r
306  * Reads the script until it finds a newline. */\r
307 static void\r
308 next_scriptline(void)\r
309 {\r
310   /* Loop until we find a newline character. */\r
311   do {\r
312     ++(hs->script);\r
313   } while(hs->script[0] != ISO_nl);\r
314 \r
315   /* Eat up the newline as well. */\r
316   ++(hs->script);\r
317 }\r
318 /*-----------------------------------------------------------------------------------*/\r
319 /* next_sciptstate:\r
320  *\r
321  * Reads one line of script and decides what to do next.\r
322  */\r
323 static void\r
324 next_scriptstate(void)\r
325 {\r
326   struct fs_file fsfile;\r
327   long i;\r
328 \r
329  again:\r
330   switch(hs->script[0]) {\r
331   case ISO_t:\r
332     /* Send a text string. */\r
333     hs->state = HTTP_TEXT;\r
334     hs->dataptr = &hs->script[2];\r
335 \r
336     /* Calculate length of string. */\r
337     for(i = 0; hs->dataptr[i] != ISO_nl; ++i);\r
338     hs->count = i;\r
339     break;\r
340   case ISO_c:\r
341     /* Call a function. */\r
342     hs->state = HTTP_FUNC;\r
343     hs->dataptr = NULL;\r
344     hs->count = 0;\r
345     cgitab[hs->script[2] - ISO_a](0);\r
346     break;\r
347   case ISO_i:\r
348     /* Include a file. */\r
349     hs->state = HTTP_FILE;\r
350     if(!fs_open(&hs->script[2], &fsfile)) {\r
351       uip_abort();\r
352     }\r
353     hs->dataptr = fsfile.data;\r
354     hs->count = fsfile.len;\r
355     break;\r
356   case ISO_hash:\r
357     /* Comment line. */\r
358     next_scriptline();\r
359     goto again;\r
360   case ISO_period:\r
361     /* End of script. */\r
362     hs->state = HTTP_END;\r
363     uip_close();\r
364     break;\r
365   default:\r
366     uip_abort();\r
367     break;\r
368   }\r
369 }\r
370 /*-----------------------------------------------------------------------------------*/\r
371 /** @} */\r
372 /** @} */\r