Contiki 2.6
|
00001 /* 00002 * Copyright (c) 2004, Adam Dunkels. 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 * This file is part of the Contiki operating system. 00030 * 00031 * Author: Adam Dunkels <adam@sics.se> 00032 * 00033 * $Id: httpd.c,v 1.6 2010/12/20 20:06:06 dak664 Exp $ 00034 */ 00035 00036 #include <string.h> 00037 00038 #include "contiki-net.h" 00039 00040 #include "webserver.h" 00041 #include "httpd-fs.h" 00042 #include "httpd-cgi.h" 00043 #include "httpd.h" 00044 #include "raven-lcd.h" 00045 00046 //#include "http-strings.h" 00047 #if COFFEE_FILES 00048 #include "cfs-coffee-arch.h" 00049 #endif /* COFFEE_FILES */ 00050 00051 /* DEBUGLOGIC is a convenient way to debug in a simulator without a tcp/ip connection. 00052 * Break the program in the process loop and step from the entry in httpd_appcall. 00053 * The input file is forced to /index.html and the output directed to TCPBUF. 00054 * If cgi's are invoked define it in httpd-cgi.c as well! 00055 * psock_generator_send in /core/net/psock.c must also be modified as follows: 00056 * ... 00057 * // Wait until all data is sent and acknowledged. 00058 * if (!s->sendlen) break; //<---add this line 00059 * PT_YIELD_UNTIL(&s->psockpt, uip_acked() || uip_rexmit()); 00060 * ... 00061 */ 00062 #define DEBUGLOGIC 0 00063 #define DEBUG 0 00064 #if DEBUGLOGIC 00065 struct httpd_state *sg; 00066 #define uip_mss(...) 512 00067 #define uip_appdata TCPBUF 00068 char TCPBUF[512]; 00069 #endif 00070 #if DEBUG 00071 #include <stdio.h> 00072 #if HTTPD_STRING_TYPE==PROGMEM_TYPE 00073 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args) 00074 #else 00075 #define PRINTF printf 00076 #endif 00077 #else 00078 #define PRINTF(...) 00079 #endif 00080 00081 #ifndef WEBSERVER_CONF_CGI_CONNS 00082 #define CONNS 4 00083 #else 00084 #define CONNS WEBSERVER_CONF_CGI_CONNS 00085 #endif /* WEBSERVER_CONF_CGI_CONNS */ 00086 00087 #define STATE_WAITING 0 00088 #define STATE_OUTPUT 1 00089 00090 //#define SEND_STRING(s, str) PSOCK_SEND(s, (uint8_t *)str, (unsigned int)strlen(str)) 00091 MEMB(conns, struct httpd_state, CONNS); 00092 00093 /* MAX_SCRIPT_NAME_LENGTH should be at least be the maximum file name length+2 for %!: includes */ 00094 #define MAX_SCRIPT_NAME_LENGTH 20 00095 #define ISO_tab 0x09 00096 #define ISO_nl 0x0a 00097 #define ISO_cr 0x0d 00098 #define ISO_space 0x20 00099 #define ISO_bang 0x21 00100 #define ISO_percent 0x25 00101 #define ISO_period 0x2e 00102 #define ISO_slash 0x2f 00103 #define ISO_colon 0x3a 00104 00105 /*---------------------------------------------------------------------------*/ 00106 static unsigned short 00107 generate(void *state) 00108 { 00109 struct httpd_state *s = (struct httpd_state *)state; 00110 00111 if(s->file.len > uip_mss()) { 00112 s->len = uip_mss(); 00113 } else { 00114 s->len = s->file.len; 00115 } 00116 httpd_fs_cpy(uip_appdata, s->file.data, s->len); 00117 #if DEBUGLOGIC 00118 return 0; 00119 #else 00120 return s->len; 00121 #endif 00122 } 00123 /*---------------------------------------------------------------------------*/ 00124 static 00125 PT_THREAD(send_file(struct httpd_state *s)) 00126 { 00127 PSOCK_BEGIN(&s->sout); 00128 00129 do { 00130 PSOCK_GENERATOR_SEND(&s->sout, generate, s); 00131 s->file.len -= s->len; 00132 s->file.data += s->len; 00133 } while(s->file.len > 0); 00134 00135 PSOCK_END(&s->sout); 00136 } 00137 /*---------------------------------------------------------------------------*/ 00138 static 00139 PT_THREAD(send_part_of_file(struct httpd_state *s)) 00140 { 00141 PSOCK_BEGIN(&s->sout); 00142 00143 static int oldfilelen, oldlen; 00144 static char * olddata; 00145 00146 //Store stuff that gets clobbered... 00147 oldfilelen = s->file.len; 00148 oldlen = s->len; 00149 olddata = s->file.data; 00150 00151 //How much to send 00152 s->file.len = s->len; 00153 00154 do { 00155 PSOCK_GENERATOR_SEND(&s->sout, generate, s); 00156 s->file.len -= s->len; 00157 s->file.data += s->len; 00158 } while(s->file.len > 0); 00159 00160 s->len = oldlen; 00161 s->file.len = oldfilelen; 00162 s->file.data = olddata; 00163 00164 PSOCK_END(&s->sout); 00165 } 00166 /*---------------------------------------------------------------------------*/ 00167 static void 00168 next_scriptstate(struct httpd_state *s) 00169 { 00170 char *p; 00171 /* Skip over any script parameters to the beginning of the next line */ 00172 if((p = (char *)httpd_fs_strchr(s->scriptptr, ISO_nl)) != NULL) { 00173 p += 1; 00174 s->scriptlen -= (unsigned short)(p - s->scriptptr); 00175 s->scriptptr = p; 00176 } else { 00177 s->scriptlen = 0; 00178 } 00179 00180 /* char *p; 00181 p = strchr(s->scriptptr, ISO_nl) + 1; 00182 s->scriptlen -= (unsigned short)(p - s->scriptptr); 00183 s->scriptptr = p;*/ 00184 } 00185 00186 /*---------------------------------------------------------------------------*/ 00187 char * 00188 get_scriptname(char *dest, char *fromfile) 00189 { 00190 uint8_t i=0,skip=1; 00191 /* Extract a file or cgi name, trim leading spaces and replace termination with zero */ 00192 /* Returns number of characters processed up to the next non-tab or space */ 00193 do { 00194 dest[i]=httpd_fs_getchar(fromfile++); 00195 if (dest[i]==ISO_colon) {if (!skip) break;} //allow leading colons 00196 else if (dest[i]==ISO_tab ) {if (skip) continue;else break;}//skip leading tabs 00197 else if (dest[i]==ISO_space) {if (skip) continue;else break;}//skip leading spaces 00198 else if (dest[i]==ISO_nl ) break; //nl is preferred delimiter 00199 else if (dest[i]==ISO_cr ) break; //some editors insert cr 00200 else if (dest[i]==0 ) break; //files are terminated with null 00201 else skip=0; 00202 i++; 00203 } while (i<(MAX_SCRIPT_NAME_LENGTH+1)); 00204 fromfile--; 00205 while ((dest[i]==ISO_space) || (dest[i]==ISO_tab)) dest[i]=httpd_fs_getchar(++fromfile); 00206 dest[i]=0; 00207 return (fromfile); 00208 } 00209 /*---------------------------------------------------------------------------*/ 00210 00211 static 00212 PT_THREAD(handle_script(struct httpd_state *s)) 00213 { 00214 /* Note script includes will attach a leading : to the filename and a trailing zero */ 00215 static char scriptname[MAX_SCRIPT_NAME_LENGTH+1],*pptr; 00216 static uint16_t filelength; 00217 00218 PT_BEGIN(&s->scriptpt); 00219 00220 filelength=s->file.len; 00221 while(s->file.len > 0) { 00222 /* Sanity check */ 00223 if (s->file.len > filelength) break; 00224 00225 /* Check if we should start executing a script, flagged by %! */ 00226 if(httpd_fs_getchar(s->file.data) == ISO_percent && 00227 httpd_fs_getchar(s->file.data + 1) == ISO_bang) { 00228 00229 /* Extract name, if starts with colon include file else call cgi */ 00230 s->scriptptr=get_scriptname(scriptname,s->file.data+2); 00231 s->scriptlen=s->file.len-(s->scriptptr-s->file.data); 00232 PRINTF("httpd: Handle script named %s\n",scriptname); 00233 if(scriptname[0] == ISO_colon) { 00234 if (httpd_fs_open(&scriptname[1], &s->file)) { 00235 PT_WAIT_THREAD(&s->scriptpt, send_file(s)); 00236 } 00237 } else { 00238 PT_WAIT_THREAD(&s->scriptpt,httpd_cgi(scriptname)(s, s->scriptptr)); 00239 } 00240 next_scriptstate(s); 00241 00242 /* Reset the pointers and continue sending the current file. */ 00243 s->file.data = s->scriptptr; 00244 s->file.len = s->scriptlen; 00245 } else { 00246 00247 /* Send file up to the next potential script */ 00248 if(s->file.len > uip_mss()) { 00249 s->len = uip_mss(); 00250 } else { 00251 s->len = s->file.len; 00252 } 00253 00254 if(httpd_fs_getchar(s->file.data) == ISO_percent) { 00255 pptr = (char *) httpd_fs_strchr(s->file.data + 1, ISO_percent); 00256 } else { 00257 pptr = (char *) httpd_fs_strchr(s->file.data, ISO_percent); 00258 } 00259 00260 if(pptr != NULL && pptr != s->file.data) { 00261 s->len = (int)(pptr - s->file.data); 00262 if(s->len >= uip_mss()) { 00263 s->len = uip_mss(); 00264 } 00265 } 00266 PRINTF("httpd: Sending %u bytes from 0x%04x\n",s->file.len,(unsigned int)s->file.data); 00267 PT_WAIT_THREAD(&s->scriptpt, send_part_of_file(s)); 00268 s->file.data += s->len; 00269 s->file.len -= s->len; 00270 } 00271 } 00272 00273 PT_END(&s->scriptpt); 00274 } 00275 /*---------------------------------------------------------------------------*/ 00276 const char httpd_http[] HTTPD_STRING_ATTR = "HTTP/1.0 "; 00277 const char httpd_server[] HTTPD_STRING_ATTR = "\r\nServer: Contiki/2.0 http://www.sics.se/contiki/\r\nConnection: close\r\n"; 00278 static unsigned short 00279 generate_status(void *sstr) 00280 { 00281 uint8_t slen=httpd_strlen((char *)sstr); 00282 httpd_memcpy(uip_appdata, httpd_http, sizeof(httpd_http)-1); 00283 httpd_memcpy(uip_appdata+sizeof(httpd_http)-1, (char *)sstr, slen); 00284 slen+=sizeof(httpd_http)-1; 00285 httpd_memcpy(uip_appdata+slen, httpd_server, sizeof(httpd_server)-1); 00286 #if DEBUGLOGIC 00287 return 0; 00288 #else 00289 return slen+sizeof(httpd_server)-1; 00290 #endif 00291 } 00292 /*---------------------------------------------------------------------------*/ 00293 const char httpd_content[] HTTPD_STRING_ATTR = "Content-type: "; 00294 const char httpd_crlf[] HTTPD_STRING_ATTR = "\r\n\r\n"; 00295 static unsigned short 00296 generate_header(void *hstr) 00297 { 00298 uint8_t slen=httpd_strlen((char *)hstr); 00299 httpd_memcpy(uip_appdata,httpd_content,sizeof(httpd_content)-1); 00300 httpd_memcpy(uip_appdata+sizeof(httpd_content)-1, (char *)hstr, slen); 00301 slen+=sizeof(httpd_content)-1; 00302 httpd_memcpy(uip_appdata+slen,httpd_crlf,sizeof(httpd_crlf)-1); 00303 #if DEBUGLOGIC 00304 return 0; 00305 #else 00306 return slen+4; 00307 #endif 00308 } 00309 /*---------------------------------------------------------------------------*/ 00310 const char httpd_mime_htm[] HTTPD_STRING_ATTR = "text/html"; 00311 const char httpd_mime_css[] HTTPD_STRING_ATTR = "text/css"; 00312 const char httpd_mime_png[] HTTPD_STRING_ATTR = "image/png"; 00313 const char httpd_mime_gif[] HTTPD_STRING_ATTR = "image/gif"; 00314 const char httpd_mime_jpg[] HTTPD_STRING_ATTR = "image/jpeg"; 00315 const char httpd_mime_txt[] HTTPD_STRING_ATTR = "text/plain"; 00316 const char httpd_mime_bin[] HTTPD_STRING_ATTR = "application/octet-stream"; 00317 const char httpd_jpg [] HTTPD_STRING_ATTR = "jpg"; 00318 const char httpd_shtml [] HTTPD_STRING_ATTR = ".shtml"; 00319 00320 static 00321 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr)) 00322 { 00323 char *ptr; 00324 PSOCK_BEGIN(&s->sout); 00325 00326 PSOCK_GENERATOR_SEND(&s->sout, generate_status, (char *)statushdr); 00327 00328 ptr = strrchr(s->filename, ISO_period); 00329 if (httpd_strncmp("4", statushdr, 1)==0) { 00330 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_htm ); 00331 } else if(ptr == NULL) { 00332 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_bin ); 00333 } else { 00334 ptr++; 00335 if(httpd_strncmp(ptr, &httpd_mime_htm[5],3)== 0 ||httpd_strncmp(ptr, &httpd_shtml[1], 4) == 0) { 00336 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_htm ); 00337 } else if(httpd_strcmp(ptr, &httpd_mime_css[5]) == 0) { 00338 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_css ); 00339 } else if(httpd_strcmp(ptr, &httpd_mime_png[6]) == 0) { 00340 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_png ); 00341 } else if(httpd_strcmp(ptr, &httpd_mime_gif[6])== 0) { 00342 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_gif ); 00343 } else if(httpd_strcmp(ptr, httpd_mime_jpg) == 0) { 00344 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_jpg ); 00345 } else { 00346 PSOCK_GENERATOR_SEND(&s->sout, generate_header, &httpd_mime_txt); 00347 } 00348 } 00349 PSOCK_END(&s->sout); 00350 } 00351 /*---------------------------------------------------------------------------*/ 00352 const char httpd_indexfn [] HTTPD_STRING_ATTR = "/index.html"; 00353 const char httpd_404fn [] HTTPD_STRING_ATTR = "/404.html"; 00354 const char httpd_404notf [] HTTPD_STRING_ATTR = "404 Not found"; 00355 const char httpd_200ok [] HTTPD_STRING_ATTR = "200 OK"; 00356 static 00357 PT_THREAD(handle_output(struct httpd_state *s)) 00358 { 00359 char *ptr; 00360 00361 PT_BEGIN(&s->outputpt); 00362 #if DEBUGLOGIC 00363 httpd_strcpy(s->filename,httpd_indexfn); 00364 #endif 00365 if(!httpd_fs_open(s->filename, &s->file)) { 00366 httpd_strcpy(s->filename, httpd_404fn); 00367 httpd_fs_open(s->filename, &s->file); 00368 PT_WAIT_THREAD(&s->outputpt, send_headers(s, httpd_404notf)); 00369 PT_WAIT_THREAD(&s->outputpt, send_file(s)); 00370 } else { 00371 PT_WAIT_THREAD(&s->outputpt, send_headers(s, httpd_200ok)); 00372 ptr = strchr(s->filename, ISO_period); 00373 if((ptr != NULL && httpd_strncmp(ptr, httpd_shtml, 6) == 0) || httpd_strcmp(s->filename,httpd_indexfn)==0) { 00374 PT_INIT(&s->scriptpt); 00375 PT_WAIT_THREAD(&s->outputpt, handle_script(s)); 00376 } else { 00377 PT_WAIT_THREAD(&s->outputpt, send_file(s)); 00378 } 00379 } 00380 PSOCK_CLOSE(&s->sout); 00381 PT_END(&s->outputpt); 00382 } 00383 /*---------------------------------------------------------------------------*/ 00384 const char httpd_get[] HTTPD_STRING_ATTR = "GET "; 00385 const char httpd_ref[] HTTPD_STRING_ATTR = "Referer:"; 00386 static 00387 PT_THREAD(handle_input(struct httpd_state *s)) 00388 { 00389 00390 PSOCK_BEGIN(&s->sin); 00391 00392 PSOCK_READTO(&s->sin, ISO_space); 00393 00394 if(httpd_strncmp(s->inputbuf, httpd_get, 4) != 0) { 00395 PSOCK_CLOSE_EXIT(&s->sin); 00396 } 00397 PSOCK_READTO(&s->sin, ISO_space); 00398 00399 if(s->inputbuf[0] != ISO_slash) { 00400 PSOCK_CLOSE_EXIT(&s->sin); 00401 } 00402 00403 if(s->inputbuf[1] == ISO_space) { 00404 httpd_strcpy(s->filename, httpd_indexfn); 00405 } else { 00406 s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; 00407 strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename)); 00408 { 00409 /* Look for ?, if found strip file name and send any following text to the LCD */ 00410 uint8_t i; 00411 for (i=0;i<sizeof(s->inputbuf);i++) { 00412 if (s->inputbuf[i]=='?') { 00413 raven_lcd_show_text(&s->inputbuf[i]); 00414 if (i<sizeof(s->filename)) s->filename[i]=0; 00415 // s->inputbuf[i]=0; //allow multiple beeps with multiple ?'s 00416 } 00417 if (s->inputbuf[i]==0) break; 00418 } 00419 } 00420 } 00421 00422 webserver_log_file(&uip_conn->ripaddr, s->filename); 00423 00424 s->state = STATE_OUTPUT; 00425 00426 while(1) { 00427 PSOCK_READTO(&s->sin, ISO_nl); 00428 00429 if(httpd_strncmp(s->inputbuf, httpd_ref, 8) == 0) { 00430 s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; 00431 petsciiconv_topetscii(s->inputbuf, PSOCK_DATALEN(&s->sin) - 2); 00432 webserver_log(s->inputbuf); 00433 } 00434 } 00435 PSOCK_END(&s->sin); 00436 } 00437 /*---------------------------------------------------------------------------*/ 00438 static void 00439 handle_connection(struct httpd_state *s) 00440 { 00441 #if DEBUGLOGIC 00442 handle_output(s); 00443 #else 00444 handle_input(s); 00445 if(s->state == STATE_OUTPUT) { 00446 handle_output(s); 00447 } 00448 #endif 00449 } 00450 /*---------------------------------------------------------------------------*/ 00451 void 00452 httpd_appcall(void *state) 00453 { 00454 #if DEBUGLOGIC 00455 struct httpd_state *s; //Enter here for debugging with output directed to TCPBUF 00456 s = sg = (struct httpd_state *)memb_alloc(&conns); 00457 if (1) { 00458 #else 00459 struct httpd_state *s = (struct httpd_state *)state; 00460 if(uip_closed() || uip_aborted() || uip_timedout()) { 00461 if(s != NULL) { 00462 memb_free(&conns, s); 00463 } 00464 } else if(uip_connected()) { 00465 s = (struct httpd_state *)memb_alloc(&conns); 00466 if(s == NULL) { 00467 uip_abort(); 00468 return; 00469 } 00470 #endif 00471 tcp_markconn(uip_conn, s); 00472 PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); 00473 PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); 00474 PT_INIT(&s->outputpt); 00475 s->state = STATE_WAITING; 00476 /* timer_set(&s->timer, CLOCK_SECOND * 100);*/ 00477 s->timer = 0; 00478 handle_connection(s); 00479 } else if(s != NULL) { 00480 if(uip_poll()) { 00481 ++s->timer; 00482 if(s->timer >= 20) { 00483 uip_abort(); 00484 memb_free(&conns, s); 00485 } 00486 } else { 00487 s->timer = 0; 00488 } 00489 handle_connection(s); 00490 } else { 00491 uip_abort(); 00492 } 00493 } 00494 /*---------------------------------------------------------------------------*/ 00495 void 00496 httpd_init(void) 00497 { 00498 tcp_listen(UIP_HTONS(80)); 00499 memb_init(&conns); 00500 httpd_cgi_init(); 00501 } 00502 /*---------------------------------------------------------------------------*/