Contiki 3.x
httpd-simple-avr.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010, Swedish Institute of Computer Science.
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  */
30 
31 /**
32  * \file
33  * A simple web server forwarding page generation to a protothread
34  * \author
35  * Adam Dunkels <adam@sics.se>
36  * Niclas Finne <nfi@sics.se>
37  * Joakim Eriksson <joakime@sics.se>
38  * David Kopf <dak664@embarqmail.com> (AVR adaptation)
39  */
40 
41 #include <stdio.h>
42 #include <string.h>
43 #include <avr/pgmspace.h>
44 #include "contiki-net.h"
45 
46 #ifndef WEBSERVER_CONF_CFS_PATHLEN
47 #define HTTPD_PATHLEN 2
48 #else
49 #define HTTPD_PATHLEN WEBSERVER_CONF_CFS_PATHLEN
50 #endif
51 
52 struct httpd_state;
53 typedef char (* httpd_simple_script_t)(struct httpd_state *s);
54 
55 struct httpd_state {
56  struct timer timer;
57  struct psock sin, sout;
58  struct pt outputpt;
59  char inputbuf[HTTPD_PATHLEN + 30];
60  char outputbuf[UIP_TCP_MSS];
61  char filename[HTTPD_PATHLEN];
62  httpd_simple_script_t script;
63  char state;
64 };
65 
66 /* DEBUGLOGIC is a convenient way to debug in a simulator without a tcp/ip connection.
67  * Break the program in the process loop and step from the entry in httpd_appcall.
68  * The input file is forced to /index.html and the output directed to uip_aligned_buf.
69  * If cgi's are invoked define it in httpd-cgi.c as well!
70  * psock_generator_send in /core/net/psock.c must also be modified as follows:
71  * ...
72  * // Wait until all data is sent and acknowledged.
73  * if (!s->sendlen) break; //<---add this line
74  * PT_YIELD_UNTIL(&s->psockpt, uip_acked() || uip_rexmit());
75  * ...
76  */
77 #define DEBUGLOGIC 0
78 #if DEBUGLOGIC
79 struct httpd_state *sg;
80 #define uip_mss(...) sizeof(uip_aligned_buf)
81 #define uip_appdata (char *) &uip_aligned_buf
82 #endif
83 
84 #ifndef WEBSERVER_CONF_CFS_CONNS
85 #define CONNS UIP_CONNS
86 #else /* WEBSERVER_CONF_CFS_CONNS */
87 #define CONNS WEBSERVER_CONF_CFS_CONNS
88 #endif /* WEBSERVER_CONF_CFS_CONNS */
89 
90 #ifndef WEBSERVER_CONF_CFS_URLCONV
91 #define URLCONV 0
92 #else /* WEBSERVER_CONF_CFS_URLCONV */
93 #define URLCONV WEBSERVER_CONF_CFS_URLCONV
94 #endif /* WEBSERVER_CONF_CFS_URLCONV */
95 
96 #define STATE_WAITING 0
97 #define STATE_OUTPUT 1
98 
99 MEMB(conns, struct httpd_state, CONNS);
100 
101 #define webserver_log_file(...)
102 
103 #define ISO_nl 0x0a
104 #define ISO_space 0x20
105 #define ISO_period 0x2e
106 #define ISO_slash 0x2f
107 
108 /*---------------------------------------------------------------------------*/
109 static unsigned short
110 generate_string(void *sstr)
111 {
112  uint8_t slen=strlen((char *)sstr);
113  memcpy(uip_appdata, (char *)sstr, slen);
114 
115 #if DEBUGLOGIC
116  return 0;
117 #else
118  return slen;
119 #endif
120 }
121 /*---------------------------------------------------------------------------*/
122 static unsigned short
123 generate_string_P(void *sstr)
124 {
125  uint8_t slen=strlen_P((char *)sstr);
126  memcpy_P(uip_appdata, (char *)sstr, slen);
127 
128 #if DEBUGLOGIC
129  return 0;
130 #else
131  return slen;
132 #endif
133 }
134 /*---------------------------------------------------------------------------*/
135 #if FIND_THE_SCRIPT
136 /* Needed if more than one script is implemented.
137  * The generate_routes RPL page is hard coded at present
138  */
139 static
140 PT_THREAD(send_string_P(struct httpd_state *s, char *str))
141 {
142  PSOCK_BEGIN(&s->sout);
143  PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, str);
144  PSOCK_END(&s->sout);
145 }
146 #endif
147 /*---------------------------------------------------------------------------*/
148 const char http_content_type_html[] PROGMEM = "Content-type: text/html\r\n\r\n";
149 static
150 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
151 {
152  PSOCK_BEGIN(&s->sout);
153  PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) statushdr);
154  PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) http_content_type_html);
155  PSOCK_END(&s->sout);
156 }
157 /*---------------------------------------------------------------------------*/
158 const char http_index_html[] PROGMEM = "/index.html";
159 const char http_referer[] PROGMEM = "Referer:";
160 const char http_get[] PROGMEM = "GET ";
161 static
162 PT_THREAD(handle_input(struct httpd_state *s))
163 {
164  PSOCK_BEGIN(&s->sin);
165 
166  PSOCK_READTO(&s->sin, ISO_space);
167 
168  if(strncmp_P(s->inputbuf, http_get, 4) != 0) {
169  PSOCK_CLOSE_EXIT(&s->sin);
170  }
171  PSOCK_READTO(&s->sin, ISO_space);
172 
173  if(s->inputbuf[0] != ISO_slash) {
174  PSOCK_CLOSE_EXIT(&s->sin);
175  }
176 
177 #if URLCONV
178  s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
179  urlconv_tofilename(s->filename, s->inputbuf, sizeof(s->filename));
180 #else /* URLCONV */
181  if(s->inputbuf[1] == ISO_space) {
182  strncpy_P(s->filename, http_index_html, sizeof(s->filename));
183  } else {
184  s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
185  strncpy(s->filename, s->inputbuf, sizeof(s->filename));
186  }
187 #endif /* URLCONV */
188 
189  webserver_log_file(&uip_conn->ripaddr, s->filename);
190 
191  s->state = STATE_OUTPUT;
192 
193  while(1) {
194  PSOCK_READTO(&s->sin, ISO_nl);
195 
196  // if(strncmp_P(s->inputbuf, http_referer, 8) == 0) {
197  // s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
198  // webserver_log(s->inputbuf);
199  // }
200  }
201 
202  PSOCK_END(&s->sin);
203 }
204 /*---------------------------------------------------------------------------*/
205 void
206 httpd_init(void)
207 {
208  tcp_listen(UIP_HTONS(80));
209  memb_init(&conns);
210 }
211 
212 /*---------------------------------------------------------------------------*/
213 /* Only one single web request at time, MSS is 48 to save RAM */
214 static char buf[48];
215 static uint8_t blen;
216 #define ADD(FORMAT,args...) do { \
217  blen += snprintf_P(&buf[blen], sizeof(buf) - blen, PSTR(FORMAT),##args); \
218  } while(0)
219 /*---------------------------------------------------------------------------*/
220 static void
221 ipaddr_add(const uip_ipaddr_t *addr)
222 {
223  uint16_t a;
224  int i, f;
225  for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) {
226  a = (addr->u8[i] << 8) + addr->u8[i + 1];
227  if(a == 0 && f >= 0) {
228  if(f++ == 0 && sizeof(buf) - blen >= 2) {
229  buf[blen++] = ':';
230  buf[blen++] = ':';
231  }
232  } else {
233  if(f > 0) {
234  f = -1;
235  } else if(i > 0 && blen < sizeof(buf)) {
236  buf[blen++] = ':';
237  }
238  ADD("%x", a);
239  }
240  }
241 }
242 /*---------------------------------------------------------------------------*/
243 const char TOP1[] PROGMEM = "<html><head><title>ContikiRPL(Jackdaw)";
244 const char TOP2[] PROGMEM = "</title></head><body>";
245 const char BOTTOM[] PROGMEM = "</body></html>";
246 
247 static
248 PT_THREAD(generate_routes(struct httpd_state *s))
249 {
250  uint8_t i=0;
251  PSOCK_BEGIN(&s->sout);
252 
253  PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) TOP1);
254  PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) TOP2);
255 
256 #if UIP_CONF_IPV6 //allow ip4 builds
257  blen = 0;
258  ADD("<h2>Neighbors [%u max]</h2>",NBR_TABLE_CONF_MAX_NEIGHBORS);
259  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
260  blen = 0;
261  uip_ds6_nbr_t *nbr;
262  for(nbr = nbr_table_head(ds6_neighbors);
263  nbr != NULL;
264  nbr = nbr_table_next(ds6_neighbors, nbr)) {
265  ipaddr_add(&nbr->ipaddr);
266  ADD("<br>");
267 // if(blen > sizeof(buf) - 45) {
268  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
269  blen = 0;
270 // }
271  }
272 
273  ADD("<h2>Routes [%u max]</h2>",UIP_DS6_ROUTE_NB);
274  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
275  blen = 0;
276  uip_ds6_route_t *route;
277  for(route = uip_ds6_route_head();
278  route != NULL;
279  route = uip_ds6_route_next(route)) {
280  ipaddr_add(&route->ipaddr);
281  ADD("/%u (via ", route->length);
282  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
283  blen=0;
284  ipaddr_add(uip_ds6_route_nexthop(route));
285  if(route->state.lifetime < 600) {
286  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
287  blen=0;
288  ADD(") %lus<br>", route->state.lifetime);
289  } else {
290  ADD(")<br>");
291  }
292  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
293  blen = 0;
294  }
295  if(blen > 0) {
296  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
297  blen = 0;
298  }
299 #else /* UIP_CONF_IPV6 */
300  blen = 0;i++;
301  ADD("<h2>Hey, you got ip4 working!</h2>");
302  PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
303 #endif /* UIP_CONF_IPV6 */
304 
305  PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) BOTTOM);
306 
307  PSOCK_END(&s->sout);
308 }
309 
310 /*---------------------------------------------------------------------------*/
311 httpd_simple_script_t
312 httpd_simple_get_script(const char *name)
313 {
314  return generate_routes;
315 }
316 /*---------------------------------------------------------------------------*/
317 const char http_header_200[] PROGMEM = "HTTP/1.0 200 OK\r\nServer: Jackdaw\r\nConnection: close\r\n";
318 const char http_header_404[] PROGMEM = "HTTP/1.0 404 Not found\r\nServer: Jackdaw\r\nConnection: close\r\n";
319 const char NOT_FOUND[] PROGMEM = "<html><body bgcolor=\"white\"><center><h1>404 - file not found</h1></center></body></html>";
320 static
321 PT_THREAD(handle_output(struct httpd_state *s))
322 {
323  PT_BEGIN(&s->outputpt);
324 
325 #if DEBUGLOGIC
326  strcpy_P(s->filename,PSTR("/x"));
327 #endif
328 #if FIND_THE_SCRIPT
329  s->script = httpd_simple_get_script(&s->filename[1]);
330  if(s->script == NULL) {
331  printf_P(PSTR("not found!"));
332  strcpy_P(s->filename, PSTR("/notfound.html"));
333 
334  PT_WAIT_THREAD(&s->outputpt,
335  send_headers(s, http_header_404));
336  PT_WAIT_THREAD(&s->outputpt,
337  send_string_P(s, NOT_FOUND));
338  uip_close();
339 
340  PT_EXIT(&s->outputpt);
341  } else {
342 #else
343  s->script = generate_routes;
344  if (1) {
345 #endif
346 
347  PT_WAIT_THREAD(&s->outputpt,
348  send_headers(s, http_header_200));
349  PT_WAIT_THREAD(&s->outputpt, s->script(s));
350  }
351  s->script = NULL;
352  PSOCK_CLOSE(&s->sout);
353  PT_END(&s->outputpt);
354 }
355 /*---------------------------------------------------------------------------*/
356 static void
357 handle_connection(struct httpd_state *s)
358 {
359 #if DEBUGLOGIC
360  handle_output(s);
361 #else
362  handle_input(s);
363  if(s->state == STATE_OUTPUT) {
364  handle_output(s);
365  }
366 #endif
367 }
368 /*---------------------------------------------------------------------------*/
369 void
370 httpd_appcall(void *state)
371 {
372 #if DEBUGLOGIC
373  struct httpd_state *s; //Enter here for debugging with output directed to TCPBUF
374  s = sg = (struct httpd_state *)memb_alloc(&conns); //put ram watch on sg
375  if (1) {
376 #else
377  struct httpd_state *s = (struct httpd_state *)state;
378 
379  if(uip_closed() || uip_aborted() || uip_timedout()) {
380  if(s != NULL) {
381  s->script = NULL;
382  memb_free(&conns, s);
383  }
384  } else if(uip_connected()) {
385  s = (struct httpd_state *)memb_alloc(&conns);
386  if(s == NULL) {
387  uip_abort();
388  webserver_log_file(&uip_conn->ripaddr, "reset (no memory block)");
389  return;
390  }
391 #endif
392  tcp_markconn(uip_conn, s);
393  PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
394  PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
395  PT_INIT(&s->outputpt);
396  s->script = NULL;
397  s->state = STATE_WAITING;
398  timer_set(&s->timer, CLOCK_SECOND * 10);
399  handle_connection(s);
400  } else if(s != NULL) {
401  if(uip_poll()) {
402  if(timer_expired(&s->timer)) {
403  uip_abort();
404  s->script = NULL;
405  memb_free(&conns, s);
406  webserver_log_file(&uip_conn->ripaddr, "reset (timeout)");
407  }
408  } else {
409  timer_restart(&s->timer);
410  }
411  handle_connection(s);
412  } else {
413  uip_abort();
414  }
415 }
416 /*---------------------------------------------------------------------------*/
417 PROCESS(httpd_process, "httpd");
418 PROCESS_THREAD(httpd_process, ev, data)
419 {
420  PROCESS_BEGIN();
421 
422  httpd_init();
423 
424  while(1) {
426  httpd_appcall(data);
427  }
428 
429  PROCESS_END();
430 }
431 
An entry in the routing table.
#define PT_WAIT_THREAD(pt, thread)
Block and wait until a child protothread completes.
Definition: pt.h:191
A timer.
Definition: timer.h:86
#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
void timer_restart(struct timer *t)
Restart the timer from the current point in time.
Definition: timer.c:104
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#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 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
const uint32_t OIDSupportedList[] PROGMEM
List of supported RNDIS OID&#39;s.
Definition: rndis.c:96
#define uip_connected()
Has the connection just been connected?
Definition: uip.h:761
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define uip_abort()
Abort the current connection.
Definition: uip.h:682
#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 PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition: process.h:157
The representation of a protosocket.
Definition: psock.h:112
#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 PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define uip_close()
Close the current connection.
Definition: uip.h:671
process_event_t tcpip_event
The uIP event.
Definition: tcpip.c:75
#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 UIP_TCP_MSS
The TCP maximum segment size.
Definition: uipopt.h:485
#define PT_EXIT(pt)
Exit the protothread.
Definition: pt.h:245
uip_appdata
Pointer to the application data in the packet buffer.
Definition: tcp_loader.c:74
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
An entry in the nbr cache.
Definition: uip-ds6-nbr.h:70