blob: 1cd3582d1cf31534eb6906f2ffbd7cdd7b529828 [file] [log] [blame]
adamdunkels9fcf9d62003-09-04 19:46:32 +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. The name of the author may not be used to endorse or promote
14 * products derived from this software without specific prior
15 * written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * This file is part of the uIP TCP/IP stack.
30 *
31 * $Id: httpd.c,v 1.1 2003/09/04 19:46:33 adamdunkels Exp $
32 *
33 */
34
35
36#include "uip.h"
37#include "httpd.h"
38#include "httpd-fs.h"
39#include "httpd-fsdata.h"
40#include "httpd-cgi.h"
41
42/* The HTTP server states: */
43#define HTTP_DEALLOCATED 0
44#define HTTP_NOGET 1
45#define HTTP_FILE 2
46#define HTTP_TEXT 3
47#define HTTP_FUNC 4
48#define HTTP_END 5
49
50#ifdef DEBUG
51#include <stdio.h>
52#define PRINT(x) printf("%s", x)
53#define PRINTLN(x) printf("%s\n", x)
54#else /* DEBUG */
55#define PRINT(x)
56#define PRINTLN(x)
57#endif /* DEBUG */
58
59struct httpd_state *hs;
60
61extern const struct httpd_fsdata_file file_index_html;
62extern const struct httpd_fsdata_file file_404_html;
63
64static void next_scriptline(void);
65static void next_scriptstate(void);
66
67#define ISO_G 0x47
68#define ISO_E 0x45
69#define ISO_T 0x54
70#define ISO_slash 0x2f
71#define ISO_c 0x63
72#define ISO_g 0x67
73#define ISO_i 0x69
74#define ISO_space 0x20
75#define ISO_nl 0x0a
76#define ISO_cr 0x0d
77#define ISO_a 0x61
78#define ISO_t 0x74
79#define ISO_hash 0x23
80#define ISO_period 0x2e
81
82#define HTTPD_CONF_NUMCONNS UIP_CONNS
83static struct httpd_state conns[HTTPD_CONF_NUMCONNS];
84static u8_t i;
85
86void beep(void);
87
88/*-----------------------------------------------------------------------------------*/
89static struct httpd_state *
90alloc_state(void)
91{
92
93 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
94 if(conns[i].state == HTTP_DEALLOCATED) {
95 return &conns[i];
96 }
97 }
98
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
104 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(HTONS(80));
120
121 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
122 conns[i].state = HTTP_DEALLOCATED;
123 }
124}
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) {
150 uip_close();
151 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;
163 hs->poll = 0;
164 } else if(uip_closed() || uip_aborted()) {
165 if(hs != NULL) {
166 dealloc_state(hs);
167 }
168 return;
169 } 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. */
173 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 }
180 }
181 return;
182 }
183
184
185 if(uip_newdata() && hs->state == HTTP_NOGET) {
186 hs->poll = 0;
187 /* 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 beep();
202
203 /* Find the file we are looking for. */
204 for(i = 4; i < 40; ++i) {
205 if(uip_appdata[i] == ISO_space ||
206 uip_appdata[i] == ISO_cr ||
207 uip_appdata[i] == ISO_nl) {
208 uip_appdata[i] = 0;
209 break;
210 }
211 }
212
213 PRINT("request for file ");
214 PRINTLN(&uip_appdata[4]);
215 webserver_log_file(uip_conn->ripaddr, &uip_appdata[4]);
216 /* Check for a request for "/". */
217 if(uip_appdata[4] == ISO_slash &&
218 uip_appdata[5] == 0) {
219 httpd_fs_open(file_index_html.name, &fsfile);
220 } else {
221 if(!httpd_fs_open((const char *)&uip_appdata[4], &fsfile)) {
222 PRINTLN("couldn't open file");
223 httpd_fs_open(file_404_html.name, &fsfile);
224 }
225 }
226
227 if(uip_appdata[4] == ISO_slash &&
228 uip_appdata[5] == ISO_c &&
229 uip_appdata[6] == ISO_g &&
230 uip_appdata[7] == ISO_i &&
231 uip_appdata[8] == ISO_slash) {
232 /* If the request is for a file that starts with "/cgi/", we
233 prepare for invoking a script. */
234 hs->script = fsfile.data;
235 next_scriptstate();
236 } else {
237 hs->script = NULL;
238 /* The web server is now no longer in the HTTP_NOGET state, but
239 in the HTTP_FILE state since is has now got the GET from
240 the client and will start transmitting the file. */
241 hs->state = HTTP_FILE;
242
243 /* Point the file pointers in the connection state to point to
244 the first byte of the file. */
245 hs->dataptr = fsfile.data;
246 hs->count = fsfile.len;
247 }
248 }
249
250
251 if(hs->state != HTTP_FUNC) {
252 /* Check if the client (remote end) has acknowledged any data that
253 we've previously sent. If so, we move the file pointer further
254 into the file and send back more data. If we are out of data to
255 send, we close the connection. */
256 if(uip_acked()) {
257 hs->poll = 0;
258 if(hs->count >= uip_mss()) {
259 hs->count -= uip_mss();
260 hs->dataptr += uip_mss();
261 } else {
262 hs->count = 0;
263 }
264
265 if(hs->count == 0) {
266 if(hs->script != NULL) {
267 next_scriptline();
268 next_scriptstate();
269 } else {
270 uip_close();
271 dealloc_state(hs);
272 }
273 }
274 }
275 }
276
277 if(hs->state == HTTP_FUNC) {
278 /* Call the CGI function. */
279#if 1
280 if(httpd_cgitab[hs->script[2] - ISO_a]()) {
281 /* If the function returns non-zero, we jump to the next line
282 in the script. */
283 next_scriptline();
284 next_scriptstate();
285 }
286#endif
287 }
288
289 if(hs->state != HTTP_FUNC && !uip_poll()) {
290 hs->poll = 0;
291 /* Send a piece of data, but not more than the MSS of the
292 connection. */
293 uip_send(hs->dataptr,
294 hs->count > uip_mss()? uip_mss(): hs->count);
295 }
296
297 /* Finally, return to uIP. Our outgoing packet will soon be on its
298 way... */
299}
300/*-----------------------------------------------------------------------------------*/
301/* next_scriptline():
302 *
303 * Reads the script until it finds a newline. */
304static void
305next_scriptline(void)
306{
307 /* Loop until we find a newline character. */
308 do {
309 ++(hs->script);
310 } while(hs->script[0] != ISO_nl);
311
312 /* Eat up the newline as well. */
313 ++(hs->script);
314}
315/*-----------------------------------------------------------------------------------*/
316/* next_sciptstate:
317 *
318 * Reads one line of script and decides what to do next.
319 */
320static void
321next_scriptstate(void)
322{
323 struct httpd_fs_file fsfile;
324 u8_t i;
325
326 again:
327 switch(hs->script[0]) {
328 case ISO_t:
329 /* Send a text string. */
330 hs->state = HTTP_TEXT;
331 hs->dataptr = &hs->script[2];
332
333 /* Calculate length of string. */
334 for(i = 0; hs->dataptr[i] != ISO_nl; ++i);
335 hs->count = i;
336 break;
337 case ISO_c:
338 /* Call a function. */
339 hs->state = HTTP_FUNC;
340 hs->dataptr = NULL;
341 hs->count = 0;
342 uip_reset_acked();
343 break;
344 case ISO_i:
345 /* Include a file. */
346 hs->state = HTTP_FILE;
347 if(!httpd_fs_open(&hs->script[2], &fsfile)) {
348 uip_abort();
349 dealloc_state(hs);
350 }
351 hs->dataptr = fsfile.data;
352 hs->count = fsfile.len;
353 break;
354 case ISO_hash:
355 /* Comment line. */
356 next_scriptline();
357 goto again;
358 break;
359 case ISO_period:
360 /* End of script. */
361 hs->state = HTTP_END;
362 uip_close();
363 dealloc_state(hs);
364 break;
365 default:
366 uip_abort();
367 dealloc_state(hs);
368 break;
369 }
370}
371/*-----------------------------------------------------------------------------------*/