blob: 61d109481778c48b227c625d8cab99327e1e1fd2 [file] [log] [blame]
adamdunkels3023dee2003-07-04 10:54:51 +00001/*
2 * Copyright (c) 2001, Adam Dunkels.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Adam Dunkels.
16 * 4. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior
18 * written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
21 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
26 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 * This file is part of the uIP TCP/IP stack.
33 *
adamdunkels55637d92004-08-09 22:22:47 +000034 * $Id: httpd.c,v 1.3 2004/08/09 22:25:06 adamdunkels Exp $
adamdunkels3023dee2003-07-04 10:54:51 +000035 *
36 */
37
38
adamdunkels55637d92004-08-09 22:22:47 +000039#include "contiki.h"
adamdunkels3023dee2003-07-04 10:54:51 +000040#include "httpd.h"
41#include "httpd-fs.h"
42#include "httpd-fsdata.h"
43#include "httpd-cgi.h"
44
45#include "webserver.h"
46
47#include <avr/pgmspace.h>
48
49/* The HTTP server states: */
50#define HTTP_DEALLOCATED 0
51#define HTTP_NOGET 1
52#define HTTP_FILE 2
53#define HTTP_TEXT 3
54#define HTTP_FUNC 4
55#define HTTP_END 5
56
57#ifdef DEBUG
58#include <stdio.h>
59#define PRINT(x) printf("%s", x)
60#define PRINTLN(x) printf("%s\n", x)
61#else /* DEBUG */
62#define PRINT(x)
63#define PRINTLN(x)
64#endif /* DEBUG */
65
66struct httpd_state *hs;
67
68extern const struct httpd_fsdata_file file_index_html;
69extern const struct httpd_fsdata_file file_404_html;
70
71static void next_scriptline(void);
72static void next_scriptstate(void);
73
74#define ISO_G 0x47
75#define ISO_E 0x45
76#define ISO_T 0x54
77#define ISO_slash 0x2f
78#define ISO_c 0x63
79#define ISO_g 0x67
80#define ISO_i 0x69
81#define ISO_space 0x20
82#define ISO_nl 0x0a
83#define ISO_cr 0x0d
84#define ISO_a 0x61
85#define ISO_t 0x74
86#define ISO_hash 0x23
87#define ISO_period 0x2e
88
89#define HTTPD_CONF_NUMCONNS UIP_CONNS
90static struct httpd_state conns[HTTPD_CONF_NUMCONNS];
91static u8_t i;
92
93static char tmp[40];
94/*-----------------------------------------------------------------------------------*/
95static struct httpd_state *
96alloc_state(void)
97{
98
99 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
100 if(conns[i].state == HTTP_DEALLOCATED) {
101 return &conns[i];
102 }
103 }
104
105 /* We are overloaded! XXX: we'll just kick all other connections! */
106 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
107 conns[i].state = HTTP_DEALLOCATED;
108 }
109
110 return NULL;
111}
112/*-----------------------------------------------------------------------------------*/
113static void
114dealloc_state(struct httpd_state *s)
115{
116 s->state = HTTP_DEALLOCATED;
117}
118/*-----------------------------------------------------------------------------------*/
119void
120httpd_init(void)
121{
122 httpd_fs_init();
123
124 /* Listen to port 80. */
adamdunkels55637d92004-08-09 22:22:47 +0000125 tcp_listen(HTONS(80));
adamdunkels3023dee2003-07-04 10:54:51 +0000126
127 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
128 conns[i].state = HTTP_DEALLOCATED;
129 }
130}
131/*-----------------------------------------------------------------------------------*/
adamdunkels55637d92004-08-09 22:22:47 +0000132void
133httpd_appcall(void *state)
adamdunkels3023dee2003-07-04 10:54:51 +0000134{
135 struct httpd_fs_file fsfile;
136 u8_t i;
137 char c;
adamdunkels3023dee2003-07-04 10:54:51 +0000138
139 hs = (struct httpd_state *)(state);
140
141 /* We use the uip_ test functions to deduce why we were
142 called. If uip_connected() is non-zero, we were called
143 because a remote host has connected to us. If
144 uip_newdata() is non-zero, we were called because the
145 remote host has sent us new data, and if uip_acked() is
146 non-zero, the remote host has acknowledged the data we
147 previously sent to it. */
148 if(uip_connected()) {
149
150 /* Since we've just been connected, the state pointer should be
151 NULL and we need to allocate a new state object. If we have run
152 out of memory for state objects, we'll have to abort the
153 connection and return. */
154 if(hs == NULL) {
155 hs = alloc_state();
156 if(hs == NULL) {
157 uip_close();
158 return;
159 }
adamdunkels55637d92004-08-09 22:22:47 +0000160 tcp_markconn(uip_conn, (void *)hs);
adamdunkels3023dee2003-07-04 10:54:51 +0000161 }
162 /* Since we have just been connected with the remote host, we
163 reset the state for this connection. The ->count variable
164 contains the amount of data that is yet to be sent to the
165 remote host, and the ->state is set to HTTP_NOGET to signal
166 that we haven't received any HTTP GET request for this
167 connection yet. */
168 hs->state = HTTP_NOGET;
169 hs->count = 0;
170 hs->poll = 0;
171 } else if(uip_closed() || uip_aborted()) {
172 if(hs != NULL) {
173 dealloc_state(hs);
174 }
175 return;
176 } else if(uip_poll()) {
177 /* If we are polled ten times, we abort the connection. This is
178 because we don't want connections lingering indefinately in
179 the system. */
180 if(hs != NULL) {
181 if(hs->state == HTTP_DEALLOCATED) {
182 uip_abort();
183 } else if(hs->poll++ >= 100) {
184 uip_abort();
185 dealloc_state(hs);
186 }
187 }
188 return;
189 }
190
191
192 if(uip_newdata() && hs->state == HTTP_NOGET) {
193 hs->poll = 0;
194 /* This is the first data we receive, and it should contain a
195 GET. */
196
197 /* Check for GET. */
198 if(uip_appdata[0] != ISO_G ||
199 uip_appdata[1] != ISO_E ||
200 uip_appdata[2] != ISO_T ||
201 uip_appdata[3] != ISO_space) {
202 /* If it isn't a GET, we abort the connection. */
203 uip_abort();
204 dealloc_state(hs);
205 return;
206 }
207
208 /* Find the file we are looking for. */
209 for(i = 4; i < 40; ++i) {
210 if(uip_appdata[i] == ISO_space ||
211 uip_appdata[i] == ISO_cr ||
212 uip_appdata[i] == ISO_nl) {
213 uip_appdata[i] = 0;
214 break;
215 }
216 }
217
218 PRINT("request for file ");
219 PRINTLN(&uip_appdata[4]);
220 webserver_log_file(uip_conn->ripaddr, &uip_appdata[4]);
221 /* Check for a request for "/". */
222 if(uip_appdata[4] == ISO_slash &&
223 uip_appdata[5] == 0) {
224 memcpy_P(tmp, file_index_html.name, sizeof(tmp));
225 httpd_fs_open((const char *)tmp, &fsfile);
226 } else {
227 if(!httpd_fs_open((const char *)&uip_appdata[4], &fsfile)) {
228 PRINTLN("couldn't open file");
229 memcpy_P(tmp, file_404_html.name, sizeof(tmp));
230 httpd_fs_open((const char *)tmp, &fsfile);
231 }
232 }
233 httpd_fs_inc();
234
235 if(uip_appdata[4] == ISO_slash &&
236 uip_appdata[5] == ISO_c &&
237 uip_appdata[6] == ISO_g &&
238 uip_appdata[7] == ISO_i &&
239 uip_appdata[8] == ISO_slash) {
240 /* If the request is for a file that starts with "/cgi/", we
241 prepare for invoking a script. */
242 hs->script = fsfile.data;
243 next_scriptstate();
244 } else {
245 hs->script = NULL;
246 /* The web server is now no longer in the HTTP_NOGET state, but
247 in the HTTP_FILE state since is has now got the GET from
248 the client and will start transmitting the file. */
249 hs->state = HTTP_FILE;
250
251 /* Point the file pointers in the connection state to point to
252 the first byte of the file. */
253 hs->dataptr = fsfile.data;
254 hs->count = fsfile.len;
255 }
256 }
257
258
259 if(hs->state != HTTP_FUNC) {
260 /* Check if the client (remote end) has acknowledged any data that
261 we've previously sent. If so, we move the file pointer further
262 into the file and send back more data. If we are out of data to
263 send, we close the connection. */
264 if(uip_acked()) {
265 hs->poll = 0;
266 if(hs->count >= uip_mss()) {
267 hs->count -= uip_mss();
268 hs->dataptr += uip_mss();
269 } else {
270 hs->count = 0;
271 }
272
273 if(hs->count == 0) {
274 if(hs->script != NULL) {
275 next_scriptline();
276 next_scriptstate();
277 } else {
278 uip_close();
279 dealloc_state(hs);
280 }
281 }
282 }
283 }
284
285 if(hs->state == HTTP_FUNC) {
286 /* Call the CGI function. */
287 memcpy_P(&c, &hs->script[2], 1);
288 if(httpd_cgitab[c - ISO_a]()) {
289 /* If the function returns non-zero, we jump to the next line
290 in the script. */
291 next_scriptline();
292 next_scriptstate();
293 }
294 }
295
296 if(hs->state != HTTP_FUNC && !uip_poll()) {
297 hs->poll = 0;
298 /* Send a piece of data, but not more than the MSS of the
299 connection. */
300 memcpy_P(uip_appdata, hs->dataptr, uip_mss());
301 uip_send(uip_appdata,
302 hs->count > uip_mss()? uip_mss(): hs->count);
303 }
304
305 /* Finally, return to uIP. Our outgoing packet will soon be on its
306 way... */
307}
308/*-----------------------------------------------------------------------------------*/
309/* next_scriptline():
310 *
311 * Reads the script until it finds a newline. */
312static void
313next_scriptline(void)
314{
315 char c;
316 /* Loop until we find a newline character. */
317 do {
318 ++(hs->script);
319 memcpy_P(&c, hs->script, 1);
320 } while(c != ISO_nl);
321
322 /* Eat up the newline as well. */
323 ++(hs->script);
324}
325/*-----------------------------------------------------------------------------------*/
326/* next_sciptstate:
327 *
328 * Reads one line of script and decides what to do next.
329 */
330static void
331next_scriptstate(void)
332{
333 struct httpd_fs_file fsfile;
334 u8_t i;
335 char c;
336
337 again:
338 memcpy_P(&c, hs->script, 1);
339 switch(c) {
340 case ISO_t:
341 /* Send a text string. */
342 hs->state = HTTP_TEXT;
343 hs->dataptr = hs->script + 2;
344
345 /* Calculate length of string. */
346 i = 0;
347 do {
348 memcpy_P(&c, &hs->dataptr[i], 1);
349 ++i;
350 } while(c != ISO_nl);
351 hs->count = i;
352 break;
353 case ISO_c:
354 /* Call a function. */
355 hs->state = HTTP_FUNC;
356 hs->dataptr = NULL;
357 hs->count = 0;
adamdunkels55637d92004-08-09 22:22:47 +0000358 /* uip_reset_acked();*/
359 uip_flags &= ~UIP_ACKDATA;
adamdunkels3023dee2003-07-04 10:54:51 +0000360 break;
361 case ISO_i:
362 /* Include a file. */
363 hs->state = HTTP_FILE;
364 memcpy_P(tmp, &hs->script[2], sizeof(tmp));
365 if(!httpd_fs_open(tmp, &fsfile)) {
366 uip_abort();
367 dealloc_state(hs);
368 }
369 hs->dataptr = fsfile.data;
370 hs->count = fsfile.len;
371 break;
372 case ISO_hash:
373 /* Comment line. */
374 next_scriptline();
375 goto again;
376 break;
377 case ISO_period:
378 /* End of script. */
379 hs->state = HTTP_END;
380 uip_close();
381 dealloc_state(hs);
382 break;
383 default:
384 uip_abort();
385 dealloc_state(hs);
386 break;
387 }
388}
389/*-----------------------------------------------------------------------------------*/