blob: b8946f5c770e61066172f55d9da7c75b65fc7edd [file] [log] [blame]
adamdunkelsc7cc92a2003-05-28 05:21:49 +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 *
adamdunkelsf1d19652003-06-30 20:42:49 +000034 * $Id: httpd.c,v 1.2 2003/06/30 20:42:49 adamdunkels Exp $
adamdunkelsc7cc92a2003-05-28 05:21:49 +000035 *
36 */
37
38
39#include "uip.h"
40#include "httpd.h"
41#include "httpd-fs.h"
42#include "httpd-fsdata.h"
43#include "httpd-cgi.h"
44
45/* The HTTP server states: */
46#define HTTP_DEALLOCATED 0
47#define HTTP_NOGET 1
48#define HTTP_FILE 2
49#define HTTP_TEXT 3
50#define HTTP_FUNC 4
51#define HTTP_END 5
52
53#ifdef DEBUG
54#include <stdio.h>
55#define PRINT(x) printf("%s", x)
56#define PRINTLN(x) printf("%s\n", x)
57#else /* DEBUG */
58#define PRINT(x)
59#define PRINTLN(x)
60#endif /* DEBUG */
61
62struct httpd_state *hs;
63
64extern const struct httpd_fsdata_file file_index_html;
65extern const struct httpd_fsdata_file file_404_html;
66
67static void next_scriptline(void);
68static void next_scriptstate(void);
69
70#define ISO_G 0x47
71#define ISO_E 0x45
72#define ISO_T 0x54
73#define ISO_slash 0x2f
74#define ISO_c 0x63
75#define ISO_g 0x67
76#define ISO_i 0x69
77#define ISO_space 0x20
78#define ISO_nl 0x0a
79#define ISO_cr 0x0d
80#define ISO_a 0x61
81#define ISO_t 0x74
82#define ISO_hash 0x23
83#define ISO_period 0x2e
84
adamdunkelsf1d19652003-06-30 20:42:49 +000085#define HTTPD_CONF_NUMCONNS UIP_CONNS
adamdunkelsc7cc92a2003-05-28 05:21:49 +000086static struct httpd_state conns[HTTPD_CONF_NUMCONNS];
adamdunkelsf1d19652003-06-30 20:42:49 +000087static u8_t i;
adamdunkelsc7cc92a2003-05-28 05:21:49 +000088/*-----------------------------------------------------------------------------------*/
89static struct httpd_state *
90alloc_state(void)
91{
adamdunkelsf1d19652003-06-30 20:42:49 +000092
adamdunkelsc7cc92a2003-05-28 05:21:49 +000093 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
94 if(conns[i].state == HTTP_DEALLOCATED) {
95 return &conns[i];
96 }
97 }
adamdunkelsf1d19652003-06-30 20:42:49 +000098
99 /* We are overloaded! XXX: we'll just kick all other connections! */
100 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
101 conns[i].state = HTTP_DEALLOCATED;
102 }
103
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000104 return NULL;
105}
106/*-----------------------------------------------------------------------------------*/
107static void
108dealloc_state(struct httpd_state *s)
109{
110 s->state = HTTP_DEALLOCATED;
111}
112/*-----------------------------------------------------------------------------------*/
113void
114httpd_init(void)
115{
116 httpd_fs_init();
117
118 /* Listen to port 80. */
119 dispatcher_uiplisten(80);
adamdunkelsf1d19652003-06-30 20:42:49 +0000120
121 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
122 conns[i].state = HTTP_DEALLOCATED;
123 }
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000124}
125/*-----------------------------------------------------------------------------------*/
126DISPATCHER_UIPCALL(httpd_appcall, state)
127{
128 struct httpd_fs_file fsfile;
129 u8_t i;
130 DISPATCHER_UIPCALL_ARG(state);
131
132 hs = (struct httpd_state *)(state);
133
134 /* We use the uip_ test functions to deduce why we were
135 called. If uip_connected() is non-zero, we were called
136 because a remote host has connected to us. If
137 uip_newdata() is non-zero, we were called because the
138 remote host has sent us new data, and if uip_acked() is
139 non-zero, the remote host has acknowledged the data we
140 previously sent to it. */
141 if(uip_connected()) {
142
143 /* Since we've just been connected, the state pointer should be
144 NULL and we need to allocate a new state object. If we have run
145 out of memory for state objects, we'll have to abort the
146 connection and return. */
147 if(hs == NULL) {
148 hs = alloc_state();
149 if(hs == NULL) {
adamdunkelsf1d19652003-06-30 20:42:49 +0000150 uip_close();
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000151 return;
152 }
153 dispatcher_markconn(uip_conn, (void *)hs);
154 }
155 /* Since we have just been connected with the remote host, we
156 reset the state for this connection. The ->count variable
157 contains the amount of data that is yet to be sent to the
158 remote host, and the ->state is set to HTTP_NOGET to signal
159 that we haven't received any HTTP GET request for this
160 connection yet. */
161 hs->state = HTTP_NOGET;
162 hs->count = 0;
adamdunkelsf1d19652003-06-30 20:42:49 +0000163 hs->poll = 0;
164 } else if(uip_closed() || uip_aborted()) {
165 if(hs != NULL) {
166 dealloc_state(hs);
167 }
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000168 return;
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000169 } else if(uip_poll()) {
170 /* If we are polled ten times, we abort the connection. This is
171 because we don't want connections lingering indefinately in
172 the system. */
adamdunkelsf1d19652003-06-30 20:42:49 +0000173 if(hs != NULL) {
174 if(hs->state == HTTP_DEALLOCATED) {
175 uip_abort();
176 } else if(hs->poll++ >= 100) {
177 uip_abort();
178 dealloc_state(hs);
179 }
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000180 }
181 return;
adamdunkelsf1d19652003-06-30 20:42:49 +0000182 }
183
184
185 if(uip_newdata() && hs->state == HTTP_NOGET) {
186 hs->poll = 0;
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000187 /* This is the first data we receive, and it should contain a
188 GET. */
189
190 /* Check for GET. */
191 if(uip_appdata[0] != ISO_G ||
192 uip_appdata[1] != ISO_E ||
193 uip_appdata[2] != ISO_T ||
194 uip_appdata[3] != ISO_space) {
195 /* If it isn't a GET, we abort the connection. */
196 uip_abort();
197 dealloc_state(hs);
198 return;
199 }
200
201 /* Find the file we are looking for. */
202 for(i = 4; i < 40; ++i) {
203 if(uip_appdata[i] == ISO_space ||
204 uip_appdata[i] == ISO_cr ||
205 uip_appdata[i] == ISO_nl) {
206 uip_appdata[i] = 0;
207 break;
208 }
209 }
210
211 PRINT("request for file ");
212 PRINTLN(&uip_appdata[4]);
adamdunkelsf1d19652003-06-30 20:42:49 +0000213 webserver_log_file(uip_conn->ripaddr, &uip_appdata[4]);
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000214 /* Check for a request for "/". */
215 if(uip_appdata[4] == ISO_slash &&
216 uip_appdata[5] == 0) {
217 httpd_fs_open(file_index_html.name, &fsfile);
218 } else {
219 if(!httpd_fs_open((const char *)&uip_appdata[4], &fsfile)) {
220 PRINTLN("couldn't open file");
221 httpd_fs_open(file_404_html.name, &fsfile);
222 }
223 }
224
225 if(uip_appdata[4] == ISO_slash &&
226 uip_appdata[5] == ISO_c &&
227 uip_appdata[6] == ISO_g &&
228 uip_appdata[7] == ISO_i &&
229 uip_appdata[8] == ISO_slash) {
230 /* If the request is for a file that starts with "/cgi/", we
231 prepare for invoking a script. */
232 hs->script = fsfile.data;
233 next_scriptstate();
234 } else {
235 hs->script = NULL;
236 /* The web server is now no longer in the HTTP_NOGET state, but
237 in the HTTP_FILE state since is has now got the GET from
238 the client and will start transmitting the file. */
239 hs->state = HTTP_FILE;
240
241 /* Point the file pointers in the connection state to point to
242 the first byte of the file. */
243 hs->dataptr = fsfile.data;
244 hs->count = fsfile.len;
245 }
246 }
247
248
249 if(hs->state != HTTP_FUNC) {
250 /* Check if the client (remote end) has acknowledged any data that
251 we've previously sent. If so, we move the file pointer further
252 into the file and send back more data. If we are out of data to
253 send, we close the connection. */
254 if(uip_acked()) {
adamdunkelsf1d19652003-06-30 20:42:49 +0000255 hs->poll = 0;
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000256 if(hs->count >= uip_mss()) {
257 hs->count -= uip_mss();
258 hs->dataptr += uip_mss();
259 } else {
260 hs->count = 0;
261 }
262
263 if(hs->count == 0) {
264 if(hs->script != NULL) {
265 next_scriptline();
266 next_scriptstate();
267 } else {
268 uip_close();
269 dealloc_state(hs);
270 }
271 }
272 }
273 }
274
275 if(hs->state == HTTP_FUNC) {
276 /* Call the CGI function. */
277 if(httpd_cgitab[hs->script[2] - ISO_a]()) {
278 /* If the function returns non-zero, we jump to the next line
279 in the script. */
280 next_scriptline();
281 next_scriptstate();
282 }
283 }
284
285 if(hs->state != HTTP_FUNC && !uip_poll()) {
adamdunkelsf1d19652003-06-30 20:42:49 +0000286 hs->poll = 0;
adamdunkelsc7cc92a2003-05-28 05:21:49 +0000287 /* Send a piece of data, but not more than the MSS of the
288 connection. */
289 uip_send(hs->dataptr,
290 hs->count > uip_mss()? uip_mss(): hs->count);
291 }
292
293 /* Finally, return to uIP. Our outgoing packet will soon be on its
294 way... */
295}
296/*-----------------------------------------------------------------------------------*/
297/* next_scriptline():
298 *
299 * Reads the script until it finds a newline. */
300static void
301next_scriptline(void)
302{
303 /* Loop until we find a newline character. */
304 do {
305 ++(hs->script);
306 } while(hs->script[0] != ISO_nl);
307
308 /* Eat up the newline as well. */
309 ++(hs->script);
310}
311/*-----------------------------------------------------------------------------------*/
312/* next_sciptstate:
313 *
314 * Reads one line of script and decides what to do next.
315 */
316static void
317next_scriptstate(void)
318{
319 struct httpd_fs_file fsfile;
320 u8_t i;
321
322 again:
323 switch(hs->script[0]) {
324 case ISO_t:
325 /* Send a text string. */
326 hs->state = HTTP_TEXT;
327 hs->dataptr = &hs->script[2];
328
329 /* Calculate length of string. */
330 for(i = 0; hs->dataptr[i] != ISO_nl; ++i);
331 hs->count = i;
332 break;
333 case ISO_c:
334 /* Call a function. */
335 hs->state = HTTP_FUNC;
336 hs->dataptr = NULL;
337 hs->count = 0;
338 uip_reset_acked();
339 break;
340 case ISO_i:
341 /* Include a file. */
342 hs->state = HTTP_FILE;
343 if(!httpd_fs_open(&hs->script[2], &fsfile)) {
344 uip_abort();
345 dealloc_state(hs);
346 }
347 hs->dataptr = fsfile.data;
348 hs->count = fsfile.len;
349 break;
350 case ISO_hash:
351 /* Comment line. */
352 next_scriptline();
353 goto again;
354 break;
355 case ISO_period:
356 /* End of script. */
357 hs->state = HTTP_END;
358 uip_close();
359 dealloc_state(hs);
360 break;
361 default:
362 uip_abort();
363 dealloc_state(hs);
364 break;
365 }
366}
367/*-----------------------------------------------------------------------------------*/