Contiki 3.x
httpd.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 #include <string.h>
37 
38 #include "contiki-net.h"
39 
40 #include "webserver.h"
41 #include "httpd-fs.h"
42 #include "httpd-cgi.h"
43 #include "lib/petsciiconv.h"
44 #include "http-strings.h"
45 
46 #include "httpd.h"
47 
48 #ifndef WEBSERVER_CONF_CGI_CONNS
49 #define CONNS UIP_CONNS
50 #else /* WEBSERVER_CONF_CGI_CONNS */
51 #define CONNS WEBSERVER_CONF_CGI_CONNS
52 #endif /* WEBSERVER_CONF_CGI_CONNS */
53 
54 #define STATE_WAITING 0
55 #define STATE_OUTPUT 1
56 
57 #define SEND_STRING(s, str) PSOCK_SEND(s, (uint8_t *)str, (unsigned int)strlen(str))
58 MEMB(conns, struct httpd_state, CONNS);
59 
60 #define ISO_nl 0x0a
61 #define ISO_space 0x20
62 #define ISO_bang 0x21
63 #define ISO_percent 0x25
64 #define ISO_period 0x2e
65 #define ISO_slash 0x2f
66 #define ISO_colon 0x3a
67 
68 /*---------------------------------------------------------------------------*/
69 static unsigned short
70 generate(void *state)
71 {
72  struct httpd_state *s = (struct httpd_state *)state;
73 
74  if(s->file.len > uip_mss()) {
75  s->len = uip_mss();
76  } else {
77  s->len = s->file.len;
78  }
79  memcpy(uip_appdata, s->file.data, s->len);
80 
81  return s->len;
82 }
83 /*---------------------------------------------------------------------------*/
84 static
85 PT_THREAD(send_file(struct httpd_state *s))
86 {
87  PSOCK_BEGIN(&s->sout);
88 
89  do {
90  PSOCK_GENERATOR_SEND(&s->sout, generate, s);
91  s->file.len -= s->len;
92  s->file.data += s->len;
93  } while(s->file.len > 0);
94 
95  PSOCK_END(&s->sout);
96 }
97 /*---------------------------------------------------------------------------*/
98 static
99 PT_THREAD(send_part_of_file(struct httpd_state *s))
100 {
101  PSOCK_BEGIN(&s->sout);
102 
103  PSOCK_SEND(&s->sout, (uint8_t *)s->file.data, s->len);
104 
105  PSOCK_END(&s->sout);
106 }
107 /*---------------------------------------------------------------------------*/
108 static void
109 next_scriptstate(struct httpd_state *s)
110 {
111  char *p;
112 
113  if((p = strchr(s->scriptptr, ISO_nl)) != NULL) {
114  p += 1;
115  s->scriptlen -= (unsigned short)(p - s->scriptptr);
116  s->scriptptr = p;
117  } else {
118  s->scriptlen = 0;
119  }
120  /* char *p;
121  p = strchr(s->scriptptr, ISO_nl) + 1;
122  s->scriptlen -= (unsigned short)(p - s->scriptptr);
123  s->scriptptr = p;*/
124 }
125 /*---------------------------------------------------------------------------*/
126 static
127 PT_THREAD(handle_script(struct httpd_state *s))
128 {
129  char *ptr;
130 
131  PT_BEGIN(&s->scriptpt);
132 
133  while(s->file.len > 0) {
134 
135  /* Check if we should start executing a script. */
136  if(*s->file.data == ISO_percent &&
137  *(s->file.data + 1) == ISO_bang) {
138  s->scriptptr = s->file.data + 3;
139  s->scriptlen = s->file.len - 3;
140  if(*(s->scriptptr - 1) == ISO_colon) {
141  httpd_fs_open(s->scriptptr + 1, &s->file);
142  PT_WAIT_THREAD(&s->scriptpt, send_file(s));
143  } else {
144  PT_WAIT_THREAD(&s->scriptpt,
145  httpd_cgi(s->scriptptr)(s, s->scriptptr));
146  }
147  next_scriptstate(s);
148 
149  /* The script is over, so we reset the pointers and continue
150  sending the rest of the file. */
151  s->file.data = s->scriptptr;
152  s->file.len = s->scriptlen;
153  } else {
154  /* See if we find the start of script marker in the block of HTML
155  to be sent. */
156 
157  if(s->file.len > uip_mss()) {
158  s->len = uip_mss();
159  } else {
160  s->len = s->file.len;
161  }
162 
163  if(*s->file.data == ISO_percent) {
164  ptr = strchr(s->file.data + 1, ISO_percent);
165  } else {
166  ptr = strchr(s->file.data, ISO_percent);
167  }
168  if(ptr != NULL &&
169  ptr != s->file.data) {
170  s->len = (int)(ptr - s->file.data);
171  if(s->len >= uip_mss()) {
172  s->len = uip_mss();
173  }
174  }
175  PT_WAIT_THREAD(&s->scriptpt, send_part_of_file(s));
176  s->file.data += s->len;
177  s->file.len -= s->len;
178  }
179  }
180 
181  PT_END(&s->scriptpt);
182 }
183 /*---------------------------------------------------------------------------*/
184 static
185 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
186 {
187 /* gcc warning if not initialized.
188  * If initialized, minimal-net platform segmentation fault if not static...
189  */
190  static const char *ptr = NULL;
191 
192  PSOCK_BEGIN(&s->sout);
193 
194  SEND_STRING(&s->sout, statushdr);
195 
196  ptr = strrchr(s->filename, ISO_period);
197  if(ptr == NULL) {
198  ptr = http_content_type_binary;
199  } else if(strncmp(http_html, ptr, 5) == 0 ||
200  strncmp(http_shtml, ptr, 6) == 0) {
201  ptr = http_content_type_html;
202  } else if(strncmp(http_css, ptr, 4) == 0) {
203  ptr = http_content_type_css;
204  } else if(strncmp(http_png, ptr, 4) == 0) {
205  ptr = http_content_type_png;
206  } else if(strncmp(http_gif, ptr, 4) == 0) {
207  ptr = http_content_type_gif;
208  } else if(strncmp(http_jpg, ptr, 4) == 0) {
209  ptr = http_content_type_jpg;
210  } else {
211  ptr = http_content_type_plain;
212  }
213  SEND_STRING(&s->sout, ptr);
214  PSOCK_END(&s->sout);
215 }
216 /*---------------------------------------------------------------------------*/
217 static
218 PT_THREAD(handle_output(struct httpd_state *s))
219 {
220  char *ptr;
221 
222  PT_BEGIN(&s->outputpt);
223 
224  if(!httpd_fs_open(s->filename, &s->file)) {
225  strcpy(s->filename, http_404_html);
226  httpd_fs_open(s->filename, &s->file);
227  PT_WAIT_THREAD(&s->outputpt,
228  send_headers(s,
229  http_header_404));
230  PT_WAIT_THREAD(&s->outputpt,
231  send_file(s));
232  } else {
233  PT_WAIT_THREAD(&s->outputpt,
234  send_headers(s,
235  http_header_200));
236  ptr = strrchr(s->filename, ISO_period);
237  if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) {
238  PT_INIT(&s->scriptpt);
239  PT_WAIT_THREAD(&s->outputpt, handle_script(s));
240  } else {
241  PT_WAIT_THREAD(&s->outputpt,
242  send_file(s));
243  }
244  }
245  PSOCK_CLOSE(&s->sout);
246  PT_END(&s->outputpt);
247 }
248 /*---------------------------------------------------------------------------*/
249 static
250 PT_THREAD(handle_input(struct httpd_state *s))
251 {
252  PSOCK_BEGIN(&s->sin);
253 
254  PSOCK_READTO(&s->sin, ISO_space);
255 
256  if(strncmp(s->inputbuf, http_get, 4) != 0) {
257  PSOCK_CLOSE_EXIT(&s->sin);
258  }
259  PSOCK_READTO(&s->sin, ISO_space);
260 
261  if(s->inputbuf[0] != ISO_slash) {
262  PSOCK_CLOSE_EXIT(&s->sin);
263  }
264 
265  if(s->inputbuf[1] == ISO_space) {
266  strncpy(s->filename, http_index_html, sizeof(s->filename));
267  } else {
268  s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
269  strncpy(s->filename, s->inputbuf, sizeof(s->filename));
270  }
271 
272  petsciiconv_topetscii(s->filename, sizeof(s->filename));
273  webserver_log_file(&uip_conn->ripaddr, s->filename);
274  petsciiconv_toascii(s->filename, sizeof(s->filename));
275  s->state = STATE_OUTPUT;
276 
277  while(1) {
278  PSOCK_READTO(&s->sin, ISO_nl);
279 
280  if(strncmp(s->inputbuf, http_referer, 8) == 0) {
281  s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
282  petsciiconv_topetscii(s->inputbuf, PSOCK_DATALEN(&s->sin) - 2);
283  webserver_log(s->inputbuf);
284  }
285  }
286 
287  PSOCK_END(&s->sin);
288 }
289 /*---------------------------------------------------------------------------*/
290 static void
291 handle_connection(struct httpd_state *s)
292 {
293  handle_input(s);
294  if(s->state == STATE_OUTPUT) {
295  handle_output(s);
296  }
297 }
298 /*---------------------------------------------------------------------------*/
299 void
300 httpd_appcall(void *state)
301 {
302  struct httpd_state *s = (struct httpd_state *)state;
303 
304  if(uip_closed() || uip_aborted() || uip_timedout()) {
305  if(s != NULL) {
306  memb_free(&conns, s);
307  }
308  } else if(uip_connected()) {
309  s = (struct httpd_state *)memb_alloc(&conns);
310  if(s == NULL) {
311  uip_abort();
312  return;
313  }
314  tcp_markconn(uip_conn, s);
315  PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
316  PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
317  PT_INIT(&s->outputpt);
318  s->state = STATE_WAITING;
319  /* timer_set(&s->timer, CLOCK_SECOND * 100);*/
320  s->timer = 0;
321  handle_connection(s);
322  } else if(s != NULL) {
323  if(uip_poll()) {
324  ++s->timer;
325  if(s->timer >= 20) {
326  uip_abort();
327  memb_free(&conns, s);
328  }
329  } else {
330  s->timer = 0;
331  }
332  handle_connection(s);
333  } else {
334  uip_abort();
335  }
336 }
337 /*---------------------------------------------------------------------------*/
338 void
339 httpd_init(void)
340 {
341  tcp_listen(UIP_HTONS(80));
342  memb_init(&conns);
343  httpd_cgi_init();
344 }
345 #if UIP_CONF_IPV6
346 /*---------------------------------------------------------------------------*/
347 uint8_t
348 httpd_sprint_ip6(uip_ip6addr_t addr, char * result)
349 {
350  unsigned char i = 0;
351  unsigned char zerocnt = 0;
352  unsigned char numprinted = 0;
353  char * starting = result;
354 
355  *result++='[';
356  while (numprinted < 8) {
357  if ((addr.u16[i] == 0) && (zerocnt == 0)) {
358  while(addr.u16[zerocnt + i] == 0) zerocnt++;
359  if (zerocnt == 1) {
360  *result++ = '0';
361  numprinted++;
362  break;
363  }
364  i += zerocnt;
365  numprinted += zerocnt;
366  } else {
367  result += sprintf(result, "%x", (unsigned int)(uip_ntohs(addr.u16[i])));
368  i++;
369  numprinted++;
370  }
371  if (numprinted != 8) *result++ = ':';
372  }
373  *result++=']';
374  *result=0;
375  return (result - starting);
376 }
377 #endif /* UIP_CONF_IPV6 */
378 /*---------------------------------------------------------------------------*/
#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
#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
char memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition: memb.c:79
#define uip_mss()
Get the current maximum segment size that can be sent on the current connection.
Definition: uip.h:838
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 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 PSOCK_GENERATOR_SEND(psock, generator, arg)
Generate data with a function and send it.
Definition: psock.h:225
#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
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74