Contiki 2.6

httpd-simple-avr.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010, Swedish Institute of Computer Science.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * $Id: httpd-simple-avr.c,v 1.8 2010/12/04 21:32:35 dak664 Exp $
00030  */
00031 
00032 /**
00033  * \file
00034  *         A simple web server forwarding page generation to a protothread
00035  * \author
00036  *         Adam Dunkels <adam@sics.se>
00037  *         Niclas Finne <nfi@sics.se>
00038  *         Joakim Eriksson <joakime@sics.se>
00039  *         David Kopf <dak664@embarqmail.com> (AVR adaptation)
00040  */
00041 
00042 #include <stdio.h>
00043 #include <string.h>
00044 #include <avr/pgmspace.h>
00045 #include "contiki-net.h"
00046 
00047 #ifndef WEBSERVER_CONF_CFS_PATHLEN
00048 #define HTTPD_PATHLEN 2
00049 #else
00050 #define HTTPD_PATHLEN WEBSERVER_CONF_CFS_PATHLEN
00051 #endif
00052 
00053 struct httpd_state;
00054 typedef char (* httpd_simple_script_t)(struct httpd_state *s);
00055 
00056 struct httpd_state {
00057   struct timer timer;
00058   struct psock sin, sout;
00059   struct pt outputpt;
00060   char inputbuf[HTTPD_PATHLEN + 30];
00061   char outputbuf[UIP_TCP_MSS];
00062   char filename[HTTPD_PATHLEN];
00063   httpd_simple_script_t script;
00064   char state;
00065 };
00066 
00067 /* DEBUGLOGIC is a convenient way to debug in a simulator without a tcp/ip connection.
00068  * Break the program in the process loop and step from the entry in httpd_appcall.
00069  * The input file is forced to /index.html and the output directed to uip_aligned_buf.
00070  * If cgi's are invoked define it in httpd-cgi.c as well!
00071  * psock_generator_send in /core/net/psock.c must also be modified as follows:
00072  * ...
00073  * // Wait until all data is sent and acknowledged.
00074  * if (!s->sendlen) break;                            //<---add this line
00075  * PT_YIELD_UNTIL(&s->psockpt, uip_acked() || uip_rexmit());
00076  * ...
00077  */
00078 #define DEBUGLOGIC 0
00079 #if DEBUGLOGIC
00080 struct httpd_state *sg;
00081 #define uip_mss(...) sizeof(uip_aligned_buf)
00082 #define uip_appdata (char *) &uip_aligned_buf
00083 #endif
00084 
00085 #ifndef WEBSERVER_CONF_CFS_CONNS
00086 #define CONNS UIP_CONNS
00087 #else /* WEBSERVER_CONF_CFS_CONNS */
00088 #define CONNS WEBSERVER_CONF_CFS_CONNS
00089 #endif /* WEBSERVER_CONF_CFS_CONNS */
00090 
00091 #ifndef WEBSERVER_CONF_CFS_URLCONV
00092 #define URLCONV 0
00093 #else /* WEBSERVER_CONF_CFS_URLCONV */
00094 #define URLCONV WEBSERVER_CONF_CFS_URLCONV
00095 #endif /* WEBSERVER_CONF_CFS_URLCONV */
00096 
00097 #define STATE_WAITING 0
00098 #define STATE_OUTPUT  1
00099 
00100 MEMB(conns, struct httpd_state, CONNS);
00101 
00102 #define webserver_log_file(...)
00103 
00104 #define ISO_nl      0x0a
00105 #define ISO_space   0x20
00106 #define ISO_period  0x2e
00107 #define ISO_slash   0x2f
00108 
00109 /*---------------------------------------------------------------------------*/
00110 static unsigned short
00111 generate_string(void *sstr)
00112 {
00113   uint8_t slen=strlen((char *)sstr);
00114   memcpy(uip_appdata, (char *)sstr, slen);
00115 
00116 #if DEBUGLOGIC
00117   return 0;
00118 #else
00119   return slen;
00120 #endif
00121 }
00122 /*---------------------------------------------------------------------------*/
00123 static unsigned short
00124 generate_string_P(void *sstr)
00125 {
00126   uint8_t slen=strlen_P((char *)sstr);
00127   memcpy_P(uip_appdata, (char *)sstr, slen);
00128 
00129 #if DEBUGLOGIC
00130   return 0;
00131 #else
00132   return slen;
00133 #endif
00134 }
00135 /*---------------------------------------------------------------------------*/
00136 #if FIND_THE_SCRIPT
00137 /* Needed if more than one script is implemented.
00138  * The generate_routes RPL page is hard coded at present
00139  */
00140 static
00141 PT_THREAD(send_string_P(struct httpd_state *s, char *str))
00142 {
00143   PSOCK_BEGIN(&s->sout);
00144   PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, str);
00145   PSOCK_END(&s->sout);
00146 }
00147 #endif
00148 /*---------------------------------------------------------------------------*/
00149 const char http_content_type_html[] PROGMEM = "Content-type: text/html\r\n\r\n";
00150 static
00151 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
00152 {
00153   PSOCK_BEGIN(&s->sout);
00154   PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) statushdr);
00155   PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) http_content_type_html);
00156   PSOCK_END(&s->sout);
00157 }
00158 /*---------------------------------------------------------------------------*/
00159 const char http_index_html[] PROGMEM = "/index.html";
00160 const char http_referer[] PROGMEM = "Referer:";
00161 const char http_get[] PROGMEM = "GET ";
00162 static
00163 PT_THREAD(handle_input(struct httpd_state *s))
00164 {
00165   PSOCK_BEGIN(&s->sin);
00166 
00167   PSOCK_READTO(&s->sin, ISO_space);
00168 
00169   if(strncmp_P(s->inputbuf, http_get, 4) != 0) {
00170     PSOCK_CLOSE_EXIT(&s->sin);
00171   }
00172   PSOCK_READTO(&s->sin, ISO_space);
00173 
00174   if(s->inputbuf[0] != ISO_slash) {
00175     PSOCK_CLOSE_EXIT(&s->sin);
00176   }
00177 
00178 #if URLCONV
00179   s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
00180   urlconv_tofilename(s->filename, s->inputbuf, sizeof(s->filename));
00181 #else /* URLCONV */
00182   if(s->inputbuf[1] == ISO_space) {
00183     strncpy_P(s->filename, http_index_html, sizeof(s->filename));
00184   } else {
00185     s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
00186     strncpy(s->filename, s->inputbuf, sizeof(s->filename));
00187   }
00188 #endif /* URLCONV */
00189 
00190   webserver_log_file(&uip_conn->ripaddr, s->filename);
00191 
00192   s->state = STATE_OUTPUT;
00193 
00194   while(1) {
00195     PSOCK_READTO(&s->sin, ISO_nl);
00196 
00197  //   if(strncmp_P(s->inputbuf, http_referer, 8) == 0) {
00198  //     s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
00199  //     webserver_log(s->inputbuf);
00200  //   }
00201   }
00202 
00203   PSOCK_END(&s->sin);
00204 }
00205 /*---------------------------------------------------------------------------*/
00206 void
00207 httpd_init(void)
00208 {
00209   tcp_listen(UIP_HTONS(80));
00210   memb_init(&conns);
00211 }
00212 
00213 /*---------------------------------------------------------------------------*/
00214 /* Only one single web request at time, MSS is 48 to save RAM */
00215 static char buf[48];
00216 static uint8_t blen;
00217 #define ADD(FORMAT,args...) do {                                                 \
00218     blen += snprintf_P(&buf[blen], sizeof(buf) - blen, PSTR(FORMAT),##args);      \
00219   } while(0)
00220 /*---------------------------------------------------------------------------*/
00221 static void
00222 ipaddr_add(const uip_ipaddr_t *addr)
00223 {
00224   uint16_t a;
00225   int i, f;
00226   for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) {
00227     a = (addr->u8[i] << 8) + addr->u8[i + 1];
00228     if(a == 0 && f >= 0) {
00229       if(f++ == 0 && sizeof(buf) - blen >= 2) {
00230         buf[blen++] = ':';
00231         buf[blen++] = ':';
00232       }
00233     } else {
00234       if(f > 0) {
00235         f = -1;
00236       } else if(i > 0 && blen < sizeof(buf)) {
00237         buf[blen++] = ':';
00238       }
00239       ADD("%x", a);
00240     }
00241   }
00242 }
00243 /*---------------------------------------------------------------------------*/
00244 const char TOP1[] PROGMEM = "<html><head><title>ContikiRPL(Jackdaw)";
00245 const char TOP2[] PROGMEM = "</title></head><body>";
00246 const char BOTTOM[] PROGMEM = "</body></html>";
00247 #if UIP_CONF_IPV6
00248 extern uip_ds6_nbr_t uip_ds6_nbr_cache[];
00249 extern uip_ds6_route_t uip_ds6_routing_table[];
00250 #endif
00251 
00252 static
00253 PT_THREAD(generate_routes(struct httpd_state *s))
00254 {
00255   uint8_t i=0;
00256   PSOCK_BEGIN(&s->sout);
00257 
00258   PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) TOP1);
00259   PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) TOP2);
00260 
00261 #if UIP_CONF_IPV6     //allow ip4 builds
00262   blen = 0;
00263   ADD("<h2>Neighbors [%u max]</h2>",UIP_DS6_NBR_NB);
00264   PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);  
00265   blen = 0;
00266   for(i = 0; i < UIP_DS6_NBR_NB; i++) {
00267     if(uip_ds6_nbr_cache[i].isused) {
00268       ipaddr_add(&uip_ds6_nbr_cache[i].ipaddr);
00269       ADD("<br>");
00270 //    if(blen > sizeof(buf) - 45) {
00271         PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);  
00272         blen = 0;
00273 //    }
00274     }
00275   }
00276 
00277   ADD("<h2>Routes [%u max]</h2>",UIP_DS6_ROUTE_NB);
00278   PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);  
00279   blen = 0;
00280   for(i = 0; i < UIP_DS6_ROUTE_NB; i++) {
00281     if(uip_ds6_routing_table[i].isused) {
00282       ipaddr_add(&uip_ds6_routing_table[i].ipaddr);
00283       ADD("/%u (via ", uip_ds6_routing_table[i].length);
00284           PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
00285       blen=0;
00286       ipaddr_add(&uip_ds6_routing_table[i].nexthop);
00287       if(uip_ds6_routing_table[i].state.lifetime < 600) {
00288         PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);
00289         blen=0;
00290         ADD(") %lus<br>", uip_ds6_routing_table[i].state.lifetime);
00291       } else {
00292         ADD(")<br>");
00293       }
00294           PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);  
00295       blen = 0;
00296     }
00297   }
00298   if(blen > 0) {
00299         PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);  
00300     blen = 0;
00301   }
00302 #else /* UIP_CONF_IPV6 */
00303   blen = 0;i++;
00304   ADD("<h2>Hey, you got ip4 working!</h2>");
00305   PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf);  
00306 #endif /* UIP_CONF_IPV6 */
00307 
00308   PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, (char *) BOTTOM);  
00309 
00310   PSOCK_END(&s->sout);
00311 }
00312 
00313 /*---------------------------------------------------------------------------*/
00314 httpd_simple_script_t
00315 httpd_simple_get_script(const char *name)
00316 {
00317   return generate_routes;
00318 }
00319 /*---------------------------------------------------------------------------*/
00320 const char http_header_200[] PROGMEM = "HTTP/1.0 200 OK\r\nServer: Jackdaw\r\nConnection: close\r\n";
00321 const char http_header_404[] PROGMEM = "HTTP/1.0 404 Not found\r\nServer: Jackdaw\r\nConnection: close\r\n";
00322 const char NOT_FOUND[] PROGMEM = "<html><body bgcolor=\"white\"><center><h1>404 - file not found</h1></center></body></html>";
00323 static
00324 PT_THREAD(handle_output(struct httpd_state *s))
00325 {
00326   PT_BEGIN(&s->outputpt);
00327 
00328 #if DEBUGLOGIC
00329    strcpy_P(s->filename,PSTR("/x"));
00330 #endif
00331 #if FIND_THE_SCRIPT
00332   s->script = httpd_simple_get_script(&s->filename[1]);
00333   if(s->script == NULL) {
00334     printf_P(PSTR("not found!"));
00335     strcpy_P(s->filename, PSTR("/notfound.html"));
00336 
00337     PT_WAIT_THREAD(&s->outputpt,
00338                    send_headers(s, http_header_404));
00339     PT_WAIT_THREAD(&s->outputpt,
00340                    send_string_P(s, NOT_FOUND));
00341     uip_close();
00342 
00343     PT_EXIT(&s->outputpt);
00344   } else {
00345 #else
00346   s->script = generate_routes;
00347   if (1) {
00348 #endif
00349 
00350     PT_WAIT_THREAD(&s->outputpt,
00351                    send_headers(s, http_header_200));
00352     PT_WAIT_THREAD(&s->outputpt, s->script(s));
00353   }
00354   s->script = NULL;
00355   PSOCK_CLOSE(&s->sout);
00356   PT_END(&s->outputpt);
00357 }
00358 /*---------------------------------------------------------------------------*/
00359 static void
00360 handle_connection(struct httpd_state *s)
00361 {
00362 #if DEBUGLOGIC
00363   handle_output(s);
00364 #else
00365   handle_input(s);
00366   if(s->state == STATE_OUTPUT) {
00367     handle_output(s);
00368   }
00369 #endif
00370 }
00371 /*---------------------------------------------------------------------------*/
00372 void
00373 httpd_appcall(void *state)
00374 {
00375 #if DEBUGLOGIC
00376   struct httpd_state *s;   //Enter here for debugging with output directed to TCPBUF
00377   s = sg = (struct httpd_state *)memb_alloc(&conns);  //put ram watch on sg
00378   if (1) {
00379 #else
00380   struct httpd_state *s = (struct httpd_state *)state;
00381 
00382   if(uip_closed() || uip_aborted() || uip_timedout()) {
00383     if(s != NULL) {
00384       s->script = NULL;
00385       memb_free(&conns, s);
00386     }
00387   } else if(uip_connected()) {
00388     s = (struct httpd_state *)memb_alloc(&conns);
00389     if(s == NULL) {
00390       uip_abort();
00391       webserver_log_file(&uip_conn->ripaddr, "reset (no memory block)");
00392       return;
00393     }
00394 #endif
00395     tcp_markconn(uip_conn, s);
00396     PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
00397     PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1);
00398     PT_INIT(&s->outputpt);
00399     s->script = NULL;
00400     s->state = STATE_WAITING;
00401     timer_set(&s->timer, CLOCK_SECOND * 10);
00402     handle_connection(s);
00403   } else if(s != NULL) {
00404     if(uip_poll()) {
00405       if(timer_expired(&s->timer)) {
00406         uip_abort();
00407         s->script = NULL;
00408         memb_free(&conns, s);
00409         webserver_log_file(&uip_conn->ripaddr, "reset (timeout)");
00410       }
00411     } else {
00412       timer_restart(&s->timer);
00413     }
00414     handle_connection(s);
00415   } else {
00416     uip_abort();
00417   }
00418 }
00419 /*---------------------------------------------------------------------------*/
00420 PROCESS(httpd_process, "httpd");
00421 PROCESS_THREAD(httpd_process, ev, data)
00422 {
00423   PROCESS_BEGIN();
00424 
00425   httpd_init();
00426 
00427   while(1) {
00428     PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event);
00429     httpd_appcall(data);
00430   }
00431 
00432   PROCESS_END();
00433 }
00434