Contiki 2.6
|
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