blob: affbbd8a6def515ed09530ab3519fb9bc1b35986 [file] [log] [blame]
adamdunkels39c57502003-08-09 13:14:33 +00001/*
adamdunkels2aa45c42004-02-16 20:55:34 +00002 * Copyright (c) 2003, Adam Dunkels.
adamdunkels39c57502003-08-09 13:14:33 +00003 * 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.
adamdunkels2aa45c42004-02-16 20:55:34 +000013 * 3. The name of the author may not be used to endorse or promote
adamdunkels39c57502003-08-09 13:14:33 +000014 * 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 *
adamdunkels0a08fda2004-07-04 18:33:07 +000031 * $Id: httpd.c,v 1.5 2004/07/04 18:33:07 adamdunkels Exp $
adamdunkels39c57502003-08-09 13:14:33 +000032 *
33 */
34
35
36#include "uip.h"
adamdunkels2aa45c42004-02-16 20:55:34 +000037
38#include "http-strings.h"
39
adamdunkels39c57502003-08-09 13:14:33 +000040#include "httpd.h"
41#include "httpd-fs.h"
42#include "httpd-fsdata.h"
43#include "httpd-cgi.h"
44
adamdunkels0a08fda2004-07-04 18:33:07 +000045#include "tcpip.h"
adamdunkels39c57502003-08-09 13:14:33 +000046
47#include <stdio.h>
48
49/* The HTTP server states: */
adamdunkels2aa45c42004-02-16 20:55:34 +000050#define STATE_DEALLOCATED 0
51#define STATE_INITIAL 1
52#define STATE_GET 2
53#define STATE_HEADERS 3
54#define STATE_SEND_HEADERS 4
55#define STATE_SEND_CONTENT_TYPE 5
56#define STATE_SEND_DATA 6
adamdunkels39c57502003-08-09 13:14:33 +000057
adamdunkels2aa45c42004-02-16 20:55:34 +000058#define ISO_nl 0x0a
59#define ISO_space 0x20
60#define ISO_slash 0x2f
adamdunkels39c57502003-08-09 13:14:33 +000061
62#ifdef DEBUG
63#include <stdio.h>
64#define PRINT(x) printf("%s", x)
65#define PRINTLN(x) printf("%s\n", x)
66#else /* DEBUG */
67#define PRINT(x)
68#define PRINTLN(x)
69#endif /* DEBUG */
70
adamdunkels39c57502003-08-09 13:14:33 +000071
72#define HTTPD_CONF_NUMCONNS 4
73static struct httpd_state conns[HTTPD_CONF_NUMCONNS];
adamdunkels2aa45c42004-02-16 20:55:34 +000074u8_t i;
75
76struct http_filetype {
77 const char *ext;
78 unsigned char extlen;
79 const char *type;
80 unsigned char typelen;
81};
82
83static struct http_filetype filetypes[] = {
84 {http_html, sizeof(http_html) - 1,
85 http_content_type_html, sizeof(http_content_type_html) - 1},
86 {http_htm, sizeof(http_htm) - 1,
87 http_content_type_html, sizeof(http_content_type_html) - 1},
88 {http_css, sizeof(http_css) - 1,
89 http_content_type_css, sizeof(http_content_type_css) - 1},
90 {http_png, sizeof(http_png) - 1,
91 http_content_type_png, sizeof(http_content_type_png) - 1},
92 {http_gif, sizeof(http_gif) - 1,
93 http_content_type_gif, sizeof(http_content_type_gif) - 1},
94 {http_jpg, sizeof(http_jpg) - 1,
95 http_content_type_jpg, sizeof(http_content_type_jpg) - 1},
96 {http_txt, sizeof(http_txt) - 1,
97 http_content_type_text, sizeof(http_content_type_text) - 1},
98 {http_text, sizeof(http_text) - 1,
99 http_content_type_text, sizeof(http_content_type_text) - 1},
100 {NULL, 0, NULL, 0}
101};
102#define NUMFILETYPES (sizeof(filetypes) / sizeof(struct http_filetype))
103/*-----------------------------------------------------------------------------*/
adamdunkels39c57502003-08-09 13:14:33 +0000104static struct httpd_state *
105alloc_state(void)
106{
107
108 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
adamdunkels2aa45c42004-02-16 20:55:34 +0000109 if(conns[i].state == STATE_DEALLOCATED) {
adamdunkels39c57502003-08-09 13:14:33 +0000110 return &conns[i];
111 }
112 }
113
114 /* We are overloaded! XXX: we'll just kick all other connections! */
115 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
adamdunkels2aa45c42004-02-16 20:55:34 +0000116 conns[i].state = STATE_DEALLOCATED;
adamdunkels39c57502003-08-09 13:14:33 +0000117 }
118
119 return NULL;
120}
adamdunkels2aa45c42004-02-16 20:55:34 +0000121/*-----------------------------------------------------------------------------*/
adamdunkels39c57502003-08-09 13:14:33 +0000122static void
123dealloc_state(struct httpd_state *s)
124{
adamdunkels2aa45c42004-02-16 20:55:34 +0000125 s->state = STATE_DEALLOCATED;
adamdunkels39c57502003-08-09 13:14:33 +0000126}
adamdunkels2aa45c42004-02-16 20:55:34 +0000127/*-----------------------------------------------------------------------------*/
adamdunkels39c57502003-08-09 13:14:33 +0000128void
129httpd_init(void)
130{
131 httpd_fs_init();
132
133 /* Listen to port 80. */
adamdunkels0a08fda2004-07-04 18:33:07 +0000134 tcp_listen(HTONS(80));
adamdunkels39c57502003-08-09 13:14:33 +0000135
136 for(i = 0; i < HTTPD_CONF_NUMCONNS; ++i) {
adamdunkels2aa45c42004-02-16 20:55:34 +0000137 conns[i].state = STATE_DEALLOCATED;
adamdunkels39c57502003-08-09 13:14:33 +0000138 }
139}
adamdunkels2aa45c42004-02-16 20:55:34 +0000140/*-----------------------------------------------------------------------------*/
141/**
142 * \internal
143 * Buffer an amount of data from the incoming uIP data.
144 *
145 * \param buf A pointer to the buffer.
146 * \param len The length of data to buffer.
147 *
148 * \return Zero if the function wasn't able to buffer to buffer all
149 * data, non-zero otherwise.
150 */
151/*-----------------------------------------------------------------------------*/
152static unsigned char
153buffer(CC_REGISTER_ARG struct httpd_buffer *buf, int len, u8_t c)
adamdunkels39c57502003-08-09 13:14:33 +0000154{
adamdunkels2aa45c42004-02-16 20:55:34 +0000155 while(buf->ptr < len &&
156 uip_len > 0) {
157 buf->buf[buf->ptr] = *uip_appdata;
158 ++uip_appdata;
159 --uip_len;
adamdunkels39c57502003-08-09 13:14:33 +0000160
adamdunkels2aa45c42004-02-16 20:55:34 +0000161 if(buf->buf[buf->ptr] == c) {
162 return 1;
163 }
164 ++buf->ptr;
165 }
166 return (buf->ptr == len);
adamdunkels39c57502003-08-09 13:14:33 +0000167}
adamdunkels2aa45c42004-02-16 20:55:34 +0000168/*-----------------------------------------------------------------------------*/
169static void
170buffer_reset(struct httpd_buffer *buf)
171{
172 buf->ptr = 0;
173}
174/*-----------------------------------------------------------------------------*/
175static void
176newdata(CC_REGISTER_ARG struct httpd_state *hs)
177{
178 int i;
179 struct http_filetype *filetype;
180
181 while(uip_len > 0) {
182 switch(hs->state) {
183
184 case STATE_INITIAL:
185 /* This is the first data that we receive, so we check if we can
186 identify the "GET" request. We call the buffer() function to
187 try to buffer 4 bytes of data from the incoming data stream. If
188 the function isn't able to buffer enough data, we return and
189 will try again next time around. */
190 hs->buf.buf = hs->getbuffer;
191 if(buffer(&(hs->buf), 4, ISO_space)) {
192 if(strncmp(http_get, hs->buf.buf, 4) == 0) {
193 hs->state = STATE_GET;
194 buffer_reset(&(hs->buf));
195 } else {
196 uip_abort();
197 return;
198 }
199 }
200 break;
201
202 case STATE_GET:
203 /* We will read one byte at a time from the GET request until we
204 find a en end of the line (\n\r). The GET request is remembered
205 in the httpd state since we might need to look at it later. */
206 hs->buf.buf = hs->getbuffer;
207 if(buffer(&(hs->buf), sizeof(hs->getbuffer), ISO_nl)) {
208 hs->state = STATE_HEADERS;
209 hs->getlen = hs->buf.ptr - 1;
210 /* Null-terminate GET request string. */
211 hs->getbuffer[hs->getlen] = 0;
212
213 /* If there is a space character in the get request, we find
214 it and null terminate the string there. */
215 for(i = 0; i < hs->getlen; ++i) {
216 if(hs->getbuffer[i] == ISO_space) {
217 hs->getbuffer[i] = 0;
218 hs->getlen = i;
219 break;
220 }
221 }
222 buffer_reset(&(hs->buf));
223 }
224 break;
225
226 case STATE_HEADERS:
227 hs->buf.buf = hs->hdrbuffer;
228 if(buffer(&(hs->buf), sizeof(hs->hdrbuffer), '\n')) {
229 if(hs->buf.ptr == 1) {
230 hs->state = STATE_SEND_HEADERS;
231 if(httpd_fs_open(hs->getbuffer, &hs->file)) {
232 hs->count = sizeof(http_header_200) - 1;
233 hs->dataptr = (char *)http_header_200;
234 hs->contenttype = http_content_type_binary;
235 hs->contentlen = sizeof(http_content_type_binary) - 1;
236 for(filetype = filetypes;
237 filetype->ext != NULL;
238 ++filetype) {
239 if(strncmp(filetype->ext,
240 hs->getbuffer + (hs->getlen - filetype->extlen),
241 filetype->extlen) == 0) {
242 hs->contenttype = filetype->type;
243 hs->contentlen = filetype->typelen;
244 break;
245 }
246 }
247
248 } else if(hs->getbuffer[0] == ISO_slash &&
249 hs->getbuffer[1] == 0) {
250 hs->count = sizeof(http_header_200) - 1;
251 hs->dataptr = (char *)http_header_200;
252 hs->contenttype = http_content_type_html;
253 hs->contentlen = sizeof(http_content_type_html) - 1;
254 httpd_fs_open(http_index_html, &hs->file);
255 } else {
256 hs->count = sizeof(http_header_404) - 1;
257 hs->dataptr = (char *)http_header_404;
258 hs->contenttype = http_content_type_html;
259 hs->contentlen = sizeof(http_content_type_html) - 1;
260 httpd_fs_open(http_404_html, &hs->file);
261 }
262 } else {
263 buffer_reset(&(hs->buf));
264 }
265 }
266 break;
267
268 default:
269 uip_len = 0;
270 break;
271 }
272 }
273}
274/*-----------------------------------------------------------------------------*/
275static void
276acked(CC_REGISTER_ARG struct httpd_state *hs)
277{
278 hs->poll = 0;
279
280
281 switch(hs->state) {
282
283 case STATE_SEND_HEADERS:
284 if(hs->count >= uip_mss()) {
285 hs->count -= uip_mss();
286 hs->dataptr += uip_mss();
287 } else {
288 hs->count = 0;
289 }
290 if(hs->count == 0) {
291 hs->state = STATE_SEND_CONTENT_TYPE;
292 hs->count = hs->contentlen;
293 hs->dataptr = (char *)hs->contenttype;
294 }
295 break;
296
297 case STATE_SEND_CONTENT_TYPE:
298 if(hs->count >= uip_mss()) {
299 hs->count -= uip_mss();
300 hs->dataptr += uip_mss();
301 } else {
302 hs->count = 0;
303 }
304 if(hs->count == 0) {
305 hs->state = STATE_SEND_DATA;
306 hs->count = hs->file.len;
307 hs->dataptr = hs->file.data;
308 }
309 break;
310
311 case STATE_SEND_DATA:
312 if(hs->count >= uip_mss()) {
313 hs->count -= uip_mss();
314 hs->dataptr += uip_mss();
315 } else {
316 hs->count = 0;
317 }
318 if(hs->count == 0) {
319 uip_close();
320 dealloc_state(hs);
321 }
322 break;
323 }
324}
325/*-----------------------------------------------------------------------------*/
326static void
327senddata(CC_REGISTER_ARG struct httpd_state *hs)
328{
329 if(hs->state == STATE_SEND_HEADERS ||
330 hs->state == STATE_SEND_CONTENT_TYPE ||
331 hs->state == STATE_SEND_DATA) {
332 uip_send(hs->dataptr,
333 hs->count > uip_mss()? uip_mss(): hs->count);
334 }
335}
336/*-----------------------------------------------------------------------------*/
adamdunkels0a08fda2004-07-04 18:33:07 +0000337void
338httpd_appcall(void *state)
adamdunkels39c57502003-08-09 13:14:33 +0000339{
adamdunkels2aa45c42004-02-16 20:55:34 +0000340 register struct httpd_state *hs;
adamdunkels39c57502003-08-09 13:14:33 +0000341
adamdunkels2aa45c42004-02-16 20:55:34 +0000342 hs = (struct httpd_state *)(state);
adamdunkels39c57502003-08-09 13:14:33 +0000343
adamdunkels2aa45c42004-02-16 20:55:34 +0000344
345 if(uip_connected()) {
adamdunkels39c57502003-08-09 13:14:33 +0000346 /* Since we've just been connected, the state pointer should be
347 NULL and we need to allocate a new state object. If we have run
348 out of memory for state objects, we'll have to abort the
349 connection and return. */
adamdunkels2aa45c42004-02-16 20:55:34 +0000350 if(hs == NULL) {
351 hs = alloc_state();
352 if(hs == NULL) {
adamdunkels39c57502003-08-09 13:14:33 +0000353 uip_close();
354 return;
355 }
adamdunkels0a08fda2004-07-04 18:33:07 +0000356 tcp_markconn(uip_conn, (void *)hs);
adamdunkels39c57502003-08-09 13:14:33 +0000357 }
358 /* Since we have just been connected with the remote host, we
359 reset the state for this connection. The ->count variable
360 contains the amount of data that is yet to be sent to the
361 remote host, and the ->state is set to HTTP_NOGET to signal
362 that we haven't received any HTTP GET request for this
363 connection yet. */
adamdunkels2aa45c42004-02-16 20:55:34 +0000364 hs->state = STATE_INITIAL;
365 hs->count = 0;
366 hs->poll = 0;
367 buffer_reset(&(hs->buf));
368 return;
369 }
370
371 if(uip_closed() ||
372 uip_aborted() ||
373 uip_timedout()) {
374 if(hs != NULL) {
375 dealloc_state(hs);
adamdunkels39c57502003-08-09 13:14:33 +0000376 }
377 return;
378 } else if(uip_poll()) {
379 /* If we are polled ten times, we abort the connection. This is
380 because we don't want connections lingering indefinately in
381 the system. */
adamdunkels2aa45c42004-02-16 20:55:34 +0000382 if(hs != NULL) {
383 if(hs->state == STATE_DEALLOCATED) {
adamdunkels39c57502003-08-09 13:14:33 +0000384 uip_abort();
adamdunkels2aa45c42004-02-16 20:55:34 +0000385 } else if(hs->poll++ >= 100) {
adamdunkels39c57502003-08-09 13:14:33 +0000386 uip_abort();
adamdunkels2aa45c42004-02-16 20:55:34 +0000387 dealloc_state(hs);
adamdunkels39c57502003-08-09 13:14:33 +0000388 }
389 }
390 return;
391 }
adamdunkels2aa45c42004-02-16 20:55:34 +0000392
393 if(uip_acked()) {
394 acked(hs);
adamdunkels39c57502003-08-09 13:14:33 +0000395 }
adamdunkels2aa45c42004-02-16 20:55:34 +0000396
397 if(uip_newdata()) {
398 newdata(hs);
adamdunkels39c57502003-08-09 13:14:33 +0000399 }
adamdunkels2aa45c42004-02-16 20:55:34 +0000400
401 if(uip_rexmit() ||
402 uip_newdata() ||
403 uip_acked()) {
404 senddata(hs);
405 } else if(uip_poll()) {
406 senddata(hs);
adamdunkels39c57502003-08-09 13:14:33 +0000407 }
408
adamdunkels39c57502003-08-09 13:14:33 +0000409
adamdunkels39c57502003-08-09 13:14:33 +0000410}
adamdunkels2aa45c42004-02-16 20:55:34 +0000411/*-----------------------------------------------------------------------------*/