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 <string.h>
36 
37 #include "contiki-net.h"
38 
39 #include "webserver.h"
40 #include "cfs/cfs.h"
41 #include "lib/petsciiconv.h"
42 #include "http-strings.h"
43 
44 #include "httpd-cfs.h"
45 
46 #ifndef WEBSERVER_CONF_CFS_CONNS
47 #define CONNS 4
48 #else /* WEBSERVER_CONF_CFS_CONNS */
49 #define CONNS WEBSERVER_CONF_CFS_CONNS
50 #endif /* WEBSERVER_CONF_CFS_CONNS */
51 
52 #define STATE_WAITING 0
53 #define STATE_OUTPUT 1
54 
55 #define SEND_STRING(s, str) PSOCK_SEND(s, (uint8_t *)str, strlen(str))
56 MEMB(conns, struct httpd_state, CONNS);
57 
58 #define ISO_nl 0x0a
59 #define ISO_space 0x20
60 #define ISO_period 0x2e
61 #define ISO_slash 0x2f
62 
63 /*---------------------------------------------------------------------------*/
64 static
65 PT_THREAD(send_file(struct httpd_state *s))
66 {
67  PSOCK_BEGIN(&s->sout);
68 
69  do {
70  /* Read data from file system into buffer */
71  s->len = cfs_read(s->fd, s->outputbuf, sizeof(s->outputbuf));
72 
73  /* If there is data in the buffer, send it */
74  if(s->len > 0) {
75  PSOCK_SEND(&s->sout, (uint8_t *)s->outputbuf, s->len);
76  } else {
77  break;
78  }
79  } while(s->len > 0);
80 
81  PSOCK_END(&s->sout);
82 }
83 /*---------------------------------------------------------------------------*/
84 static
85 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
86 {
87  char *ptr;
88 
89  PSOCK_BEGIN(&s->sout);
90 
91  SEND_STRING(&s->sout, statushdr);
92 
93  ptr = strrchr(s->filename, ISO_period);
94  if(ptr == NULL) {
95  SEND_STRING(&s->sout, http_content_type_plain);
96  } else if(strncmp(http_html, ptr, 5) == 0) {
97  SEND_STRING(&s->sout, http_content_type_html);
98  } else if(strncmp(http_css, ptr, 4) == 0) {
99  SEND_STRING(&s->sout, http_content_type_css);
100  } else if(strncmp(http_png, ptr, 4) == 0) {
101  SEND_STRING(&s->sout, http_content_type_png);
102  } else if(strncmp(http_jpg, ptr, 4) == 0) {
103  SEND_STRING(&s->sout, http_content_type_jpg);
104  } else {
105  SEND_STRING(&s->sout, http_content_type_binary);
106  }
107  PSOCK_END(&s->sout);
108 }
109 /*---------------------------------------------------------------------------*/
110 static
111 PT_THREAD(handle_output(struct httpd_state *s))
112 {
113  PT_BEGIN(&s->outputpt);
114 
115  petsciiconv_topetscii(s->filename, sizeof(s->filename));
116  s->fd = cfs_open(s->filename, CFS_READ);
117  petsciiconv_toascii(s->filename, sizeof(s->filename));
118  if(s->fd < 0) {
119  s->fd = cfs_open("notfound.html", CFS_READ);
120  if(s->fd < 0) {
121  uip_abort();
122  memb_free(&conns, s);
123  webserver_log_file(&uip_conn->ripaddr, "reset (no notfound.html)");
124  PT_EXIT(&s->outputpt);
125  }
126  PT_WAIT_THREAD(&s->outputpt,
127  send_headers(s, http_header_404));
128  } else {
129  PT_WAIT_THREAD(&s->outputpt,
130  send_headers(s, http_header_200));
131  }
132  PT_WAIT_THREAD(&s->outputpt, send_file(s));
133  cfs_close(s->fd);
134  s->fd = -1;
135  PSOCK_CLOSE(&s->sout);
136  PT_END(&s->outputpt);
137 }
138 /*---------------------------------------------------------------------------*/
139 static
140 PT_THREAD(handle_input(struct httpd_state *s))
141 {
142  PSOCK_BEGIN(&s->sin);
143 
144  PSOCK_READTO(&s->sin, ISO_space);
145 
146  if(strncmp(s->inputbuf, http_get, 4) != 0) {
147  PSOCK_CLOSE_EXIT(&s->sin);
148  }
149  PSOCK_READTO(&s->sin, ISO_space);
150 
151  if(s->inputbuf[0] != ISO_slash) {
152  PSOCK_CLOSE_EXIT(&s->sin);
153  }
154 
155  if(s->inputbuf[1] == ISO_space) {
156  strncpy(s->filename, &http_index_html[1], sizeof(s->filename));
157  } else {
158  s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
159  strncpy(s->filename, &s->inputbuf[1], sizeof(s->filename));
160  }
161 
162  petsciiconv_topetscii(s->filename, sizeof(s->filename));
163  webserver_log_file(&uip_conn->ripaddr, s->filename);
164  petsciiconv_toascii(s->filename, sizeof(s->filename));
165  s->state = STATE_OUTPUT;
166 
167  while(1) {
168  PSOCK_READTO(&s->sin, ISO_nl);
169 
170  if(strncmp(s->inputbuf, http_referer, 8) == 0) {
171  s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
172  petsciiconv_topetscii(s->inputbuf, PSOCK_DATALEN(&s->sin) - 2);
173  webserver_log(s->inputbuf);
174  }
175  }
176 
177  PSOCK_END(&s->sin);
178 }
179 /*---------------------------------------------------------------------------*/
180 static void
181 handle_connection(struct httpd_state *s)
182 {
183  handle_input(s);
184  if(s->state == STATE_OUTPUT) {
185  handle_output(s);
186  }
187 }
188 /*---------------------------------------------------------------------------*/
189 void
190 httpd_appcall(void *state)
191 {
192  struct httpd_state *s = (struct httpd_state *)state;
193 
194  if(uip_closed() || uip_aborted() || uip_timedout()) {
195  if(s != NULL) {
196  if(s->fd >= 0) {
197  cfs_close(s->fd);
198  s->fd = -1;
199  }
200  memb_free(&conns, s);
201  }
202  } else if(uip_connected()) {
203  s = (struct httpd_state *)memb_alloc(&conns);
204  if(s == NULL) {
205  uip_abort();
206  webserver_log_file(&uip_conn->ripaddr, "reset (no memory block)");
207  return;
208  }
209  tcp_markconn(uip_conn, s);
210  PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
211  PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
212  PT_INIT(&s->outputpt);
213  s->fd = -1;
214  s->state = STATE_WAITING;
215  timer_set(&s->timer, CLOCK_SECOND * 10);
216  handle_connection(s);
217  } else if(s != NULL) {
218  if(uip_poll()) {
219  if(timer_expired(&s->timer)) {
220  uip_abort();
221  if(s->fd >= 0) {
222  cfs_close(s->fd);
223  s->fd = -1;
224  }
225  memb_free(&conns, s);
226  webserver_log_file(&uip_conn->ripaddr, "reset (timeout)");
227  }
228  } else {
229  timer_reset(&s->timer);
230  }
231  handle_connection(s);
232  } else {
233  uip_abort();
234  }
235 }
236 /*---------------------------------------------------------------------------*/
237 void
238 httpd_init(void)
239 {
240  tcp_listen(UIP_HTONS(80));
241  memb_init(&conns);
242 }
243 /*---------------------------------------------------------------------------*/
#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
#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_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
void timer_reset(struct timer *t)
Reset the timer with the same interval.
Definition: timer.c:84
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
CFS header file.