Contiki 3.x
httpd-cfs.c
1 /*
2  * Copyright (c) 2004, 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. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * This file is part of the Contiki operating system.
30  *
31  * Author: Adam Dunkels <adam@sics.se>
32  *
33  */
34 
35 #include <stdio.h>
36 #ifndef HAVE_SNPRINTF
37 int snprintf(char *str, size_t size, const char *format, ...);
38 #endif /* HAVE_SNPRINTF */
39 #include <string.h>
40 
41 #include "contiki-net.h"
42 
43 #include "webserver.h"
44 #include "cfs/cfs.h"
45 #include "lib/petsciiconv.h"
46 #include "http-strings.h"
47 #include "urlconv.h"
48 
49 #include "httpd-cfs.h"
50 
51 #ifndef WEBSERVER_CONF_CFS_CONNS
52 #define CONNS UIP_CONNS
53 #else /* WEBSERVER_CONF_CFS_CONNS */
54 #define CONNS WEBSERVER_CONF_CFS_CONNS
55 #endif /* WEBSERVER_CONF_CFS_CONNS */
56 
57 #ifndef WEBSERVER_CONF_CFS_URLCONV
58 #define URLCONV 1
59 #else /* WEBSERVER_CONF_CFS_URLCONV */
60 #define URLCONV WEBSERVER_CONF_CFS_URLCONV
61 #endif /* WEBSERVER_CONF_CFS_URLCONV */
62 
63 #define STATE_WAITING 0
64 #define STATE_OUTPUT 1
65 
66 #define SEND_STRING(s, str) PSOCK_SEND(s, (uint8_t *)str, strlen(str))
67 MEMB(conns, struct httpd_state, CONNS);
68 
69 #define ISO_nl 0x0a
70 #define ISO_space 0x20
71 #define ISO_period 0x2e
72 #define ISO_slash 0x2f
73 
74 /*---------------------------------------------------------------------------*/
75 static
76 PT_THREAD(send_file(struct httpd_state *s))
77 {
78  PSOCK_BEGIN(&s->sout);
79 
80  do {
81  /* Read data from file system into buffer */
82  s->len = cfs_read(s->fd, s->outputbuf, sizeof(s->outputbuf));
83 
84  /* If there is data in the buffer, send it */
85  if(s->len > 0) {
86  PSOCK_SEND(&s->sout, (uint8_t *)s->outputbuf, s->len);
87  } else {
88  break;
89  }
90  } while(s->len > 0);
91 
92  PSOCK_END(&s->sout);
93 }
94 /*---------------------------------------------------------------------------*/
95 static
96 PT_THREAD(send_string(struct httpd_state *s, const char *str))
97 {
98  PSOCK_BEGIN(&s->sout);
99 
100  SEND_STRING(&s->sout, str);
101 
102  PSOCK_END(&s->sout);
103 }
104 /*---------------------------------------------------------------------------*/
105 static const char *
106 get_content_type(const char *filename)
107 {
108  const char *ptr;
109  ptr = strrchr(filename, ISO_period);
110  if(ptr == NULL) {
111  ptr = http_content_type_plain;
112  } else if(strcmp(http_htm, ptr) == 0) {
113  ptr = http_content_type_html;
114  } else if(strcmp(http_css, ptr) == 0) {
115  ptr = http_content_type_css;
116  } else if(strcmp(http_png, ptr) == 0) {
117  ptr = http_content_type_png;
118  } else if(strcmp(http_gif, ptr) == 0) {
119  ptr = http_content_type_gif;
120  } else if(strcmp(http_jpg, ptr) == 0) {
121  ptr = http_content_type_jpg;
122  } else {
123  ptr = http_content_type_binary;
124  }
125  return ptr;
126 }
127 /*---------------------------------------------------------------------------*/
128 static
129 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
130 {
131  PSOCK_BEGIN(&s->sout);
132 
133  SEND_STRING(&s->sout, statushdr);
134  SEND_STRING(&s->sout, get_content_type(s->filename));
135 
136  PSOCK_END(&s->sout);
137 }
138 /*---------------------------------------------------------------------------*/
139 static
140 PT_THREAD(handle_output(struct httpd_state *s))
141 {
142  PT_BEGIN(&s->outputpt);
143 
144  petsciiconv_topetscii(s->filename, sizeof(s->filename));
145  s->fd = cfs_open(&s->filename[1], CFS_READ);
146  petsciiconv_toascii(s->filename, sizeof(s->filename));
147  if(s->fd < 0) {
148  strcpy(s->filename, "/notfound.htm");
149  s->fd = cfs_open(&s->filename[1], CFS_READ);
150  petsciiconv_toascii(s->filename, sizeof(s->filename));
151  PT_WAIT_THREAD(&s->outputpt,
152  send_headers(s, http_header_404));
153  if(s->fd < 0) {
154  PT_WAIT_THREAD(&s->outputpt,
155  send_string(s, "not found"));
156  uip_close();
157  webserver_log_file(&uip_conn->ripaddr, "404 (no notfound.htm)");
158  PT_EXIT(&s->outputpt);
159  }
160  webserver_log_file(&uip_conn->ripaddr, "404 - notfound.htm");
161  } else {
162  PT_WAIT_THREAD(&s->outputpt,
163  send_headers(s, http_header_200));
164  }
165  PT_WAIT_THREAD(&s->outputpt, send_file(s));
166  cfs_close(s->fd);
167  s->fd = -1;
168  PSOCK_CLOSE(&s->sout);
169  PT_END(&s->outputpt);
170 }
171 /*---------------------------------------------------------------------------*/
172 static
173 PT_THREAD(handle_input(struct httpd_state *s))
174 {
175  PSOCK_BEGIN(&s->sin);
176 
177  PSOCK_READTO(&s->sin, ISO_space);
178 
179  if(strncmp(s->inputbuf, http_get, 4) != 0) {
180  PSOCK_CLOSE_EXIT(&s->sin);
181  }
182  PSOCK_READTO(&s->sin, ISO_space);
183 
184  if(s->inputbuf[0] != ISO_slash) {
185  PSOCK_CLOSE_EXIT(&s->sin);
186  }
187 
188 #if URLCONV
189  s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
190  urlconv_tofilename(s->filename, s->inputbuf, sizeof(s->filename));
191 #else /* URLCONV */
192  if(s->inputbuf[1] == ISO_space) {
193  strncpy(s->filename, http_index_htm, sizeof(s->filename));
194  } else {
195  s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
196  strncpy(s->filename, s->inputbuf, sizeof(s->filename));
197  }
198 #endif /* URLCONV */
199 
200  petsciiconv_topetscii(s->filename, sizeof(s->filename));
201  webserver_log_file(&uip_conn->ripaddr, s->filename);
202  petsciiconv_toascii(s->filename, sizeof(s->filename));
203  s->state = STATE_OUTPUT;
204 
205  while(1) {
206  PSOCK_READTO(&s->sin, ISO_nl);
207 
208  if(strncmp(s->inputbuf, http_referer, 8) == 0) {
209  s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
210  petsciiconv_topetscii(s->inputbuf, PSOCK_DATALEN(&s->sin) - 2);
211  webserver_log(s->inputbuf);
212  }
213  }
214 
215  PSOCK_END(&s->sin);
216 }
217 /*---------------------------------------------------------------------------*/
218 static void
219 handle_connection(struct httpd_state *s)
220 {
221  handle_input(s);
222  if(s->state == STATE_OUTPUT) {
223  handle_output(s);
224  }
225 }
226 /*---------------------------------------------------------------------------*/
227 void
228 httpd_appcall(void *state)
229 {
230  struct httpd_state *s = (struct httpd_state *)state;
231 
232  if(uip_closed() || uip_aborted() || uip_timedout()) {
233  if(s != NULL) {
234  if(s->fd >= 0) {
235  cfs_close(s->fd);
236  s->fd = -1;
237  }
238  memb_free(&conns, s);
239  }
240  } else if(uip_connected()) {
241  s = (struct httpd_state *)memb_alloc(&conns);
242  if(s == NULL) {
243  uip_abort();
244  webserver_log_file(&uip_conn->ripaddr, "reset (no memory block)");
245  return;
246  }
247  tcp_markconn(uip_conn, s);
248  PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
249  PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
250  PT_INIT(&s->outputpt);
251  s->fd = -1;
252  s->state = STATE_WAITING;
253  timer_set(&s->timer, CLOCK_SECOND * 10);
254  handle_connection(s);
255  } else if(s != NULL) {
256  if(uip_poll()) {
257  if(timer_expired(&s->timer)) {
258  uip_abort();
259  if(s->fd >= 0) {
260  cfs_close(s->fd);
261  s->fd = -1;
262  }
263  memb_free(&conns, s);
264  webserver_log_file(&uip_conn->ripaddr, "reset (timeout)");
265  }
266  } else {
267  timer_restart(&s->timer);
268  }
269  handle_connection(s);
270  } else {
271  uip_abort();
272  }
273 }
274 /*---------------------------------------------------------------------------*/
275 void
276 httpd_init(void)
277 {
278  tcp_listen(UIP_HTONS(80));
279  memb_init(&conns);
280 #if URLCONV
281  urlconv_init();
282 #endif /* URLCONV */
283 }
284 /*---------------------------------------------------------------------------*/
#define PT_WAIT_THREAD(pt, thread)
Block and wait until a child protothread completes.
Definition: pt.h:191
#define PSOCK_READTO(psock, c)
Read data up to a specified character.
Definition: psock.h:291
void memb_init(struct memb *m)
Initialize a memory block that was declared with MEMB().
Definition: memb.c:52
#define PSOCK_CLOSE_EXIT(psock)
Close a protosocket and exit the protosocket&#39;s protothread.
Definition: psock.h:331
int cfs_open(const char *name, int flags)
Open a file.
Definition: cfs-coffee.c:996
void timer_restart(struct timer *t)
Restart the timer from the current point in time.
Definition: timer.c:104
#define PSOCK_BEGIN(psock)
Start the protosocket protothread in a function.
Definition: psock.h:164
Representation of a uIP TCP connection.
Definition: uip.h:1336
#define uip_aborted()
Has the connection been aborted by the other end?
Definition: uip.h:781
void timer_set(struct timer *t, clock_time_t interval)
Set a timer.
Definition: timer.c:64
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition: memb.c:59
#define NULL
The null pointer.
#define PT_INIT(pt)
Initialize a protothread.
Definition: pt.h:79
#define uip_poll()
Is the connection being polled by uIP?
Definition: uip.h:817
#define CFS_READ
Specify that cfs_open() should open a file for reading.
Definition: cfs.h:90
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition: uip.h:1238
#define PT_THREAD(name_args)
Declaration of a protothread.
Definition: pt.h:99
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
PETSCII/ASCII conversion functions.
#define PSOCK_INIT(psock, buffer, buffersize)
Initialize a protosocket.
Definition: psock.h:150
#define MEMB(name, structure, num)
Declare a memory block.
Definition: memb.h:89
#define PSOCK_CLOSE(psock)
Close a protosocket.
Definition: psock.h:241
#define PSOCK_SEND(psock, data, datalen)
Send data.
Definition: psock.h:184
#define PSOCK_END(psock)
Declare the end of a protosocket&#39;s protothread.
Definition: psock.h:348
#define PT_BEGIN(pt)
Declare the start of a protothread inside the C function implementing the protothread.
Definition: pt.h:114
#define uip_close()
Close the current connection.
Definition: uip.h:671
#define uip_timedout()
Has the connection timed out?
Definition: uip.h:791
#define PT_END(pt)
Declare the end of a protothread.
Definition: pt.h:126
CCIF void tcp_listen(uint16_t port)
Open a TCP port.
#define PSOCK_DATALEN(psock)
The length of the data that was previously read.
Definition: psock.h:304
#define uip_closed()
Has the connection been closed by the other end?
Definition: uip.h:771
uip_ipaddr_t ripaddr
The IP address of the remote host.
Definition: uip.h:1337
int timer_expired(struct timer *t)
Check if a timer has expired.
Definition: timer.c:121
#define PT_EXIT(pt)
Exit the protothread.
Definition: pt.h:245
void cfs_close(int fd)
Close an open file.
Definition: cfs-coffee.c:1032
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
CFS header file.