Contiki 2.6

httpd.c

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.2 2010/10/19 18:29:05 adamdunkels Exp $
00034  */
00035 
00036 #include <string.h>
00037 
00038 #include "contiki-net.h"
00039 
00040 #include "webserver-nogui.h"
00041 #include "httpd-fs.h"
00042 #include "httpd-cgi.h"
00043 #include "http-strings.h"
00044 
00045 #include "httpd.h"
00046 #include "ctk/libconio_arch-small.h"
00047 
00048 #define STATE_WAITING 0
00049 #define STATE_OUTPUT  1
00050 
00051 #define SEND_STRING(s, str) PSOCK_SEND(s, str, (unsigned int)strlen(str))
00052 MEMB(conns, struct httpd_state, 4);
00053 
00054 #define ISO_nl      0x0a
00055 #define ISO_space   0x20
00056 #define ISO_bang    0x21
00057 #define ISO_percent 0x25
00058 #define ISO_period  0x2e
00059 #define ISO_slash   0x2f
00060 #define ISO_colon   0x3a
00061 
00062 /*---------------------------------------------------------------------------*/
00063 static unsigned short
00064 generate(void *state)
00065 {
00066   struct httpd_state *s = (struct httpd_state *)state;
00067 
00068   if(s->file.len > uip_mss()) {
00069     s->len = uip_mss();
00070   } else {
00071     s->len = s->file.len;
00072   }
00073   memcpy(uip_appdata, s->file.data, s->len);
00074   
00075   return s->len;
00076 }
00077 /*---------------------------------------------------------------------------*/
00078 static
00079 PT_THREAD(send_file(struct httpd_state *s))
00080 {
00081   PSOCK_BEGIN(&s->sout);
00082   
00083   do {
00084     PSOCK_GENERATOR_SEND(&s->sout, generate, s);
00085     s->file.len -= s->len;
00086     s->file.data += s->len;
00087   } while(s->file.len > 0);
00088       
00089   PSOCK_END(&s->sout);
00090 }
00091 /*---------------------------------------------------------------------------*/
00092 static
00093 PT_THREAD(send_part_of_file(struct httpd_state *s))
00094 {
00095   PSOCK_BEGIN(&s->sout);
00096 
00097   PSOCK_SEND(&s->sout, s->file.data, s->len);
00098   
00099   PSOCK_END(&s->sout);
00100 }
00101 /*---------------------------------------------------------------------------*/
00102 #if HTTPD_CONF_SCRIPT
00103 static void
00104 next_scriptstate(struct httpd_state *s)
00105 {
00106   char *p;
00107 
00108   if((p = strchr(s->scriptptr, ISO_nl)) != NULL) {
00109     p += 1;
00110     s->scriptlen -= (unsigned short)(p - s->scriptptr);
00111     s->scriptptr = p;
00112   } else {
00113     s->scriptlen = 0;
00114   }
00115   /*  char *p;
00116   p = strchr(s->scriptptr, ISO_nl) + 1;
00117   s->scriptlen -= (unsigned short)(p - s->scriptptr);
00118   s->scriptptr = p;*/
00119 }
00120 /*---------------------------------------------------------------------------*/
00121 static
00122 PT_THREAD(handle_script(struct httpd_state *s))
00123 {
00124   char *ptr;
00125   
00126   PT_BEGIN(&s->scriptpt);
00127 
00128   while(s->file.len > 0) {
00129 
00130     /* Check if we should start executing a script. */
00131     if(*s->file.data == ISO_percent &&
00132        *(s->file.data + 1) == ISO_bang) {
00133       s->scriptptr = s->file.data + 3;
00134       s->scriptlen = s->file.len - 3;
00135       if(*(s->scriptptr - 1) == ISO_colon) {
00136         httpd_fs_open(s->scriptptr + 1, &s->file);
00137         PT_WAIT_THREAD(&s->scriptpt, send_file(s));
00138 #if HTTPD_CONF_CGI
00139       } else {
00140         PT_WAIT_THREAD(&s->scriptpt,
00141                        httpd_cgi(s->scriptptr)(s, s->scriptptr));
00142 #endif /* HTTPD_CONF_CGI */
00143       }
00144       next_scriptstate(s);
00145       
00146       /* The script is over, so we reset the pointers and continue
00147          sending the rest of the file. */
00148       s->file.data = s->scriptptr;
00149       s->file.len = s->scriptlen;
00150     } else {
00151       /* See if we find the start of script marker in the block of HTML
00152          to be sent. */
00153 
00154       if(s->file.len > uip_mss()) {
00155         s->len = uip_mss();
00156       } else {
00157         s->len = s->file.len;
00158       }
00159 
00160       if(*s->file.data == ISO_percent) {
00161         ptr = strchr(s->file.data + 1, ISO_percent);
00162       } else {
00163         ptr = strchr(s->file.data, ISO_percent);
00164       }
00165       if(ptr != NULL &&
00166          ptr != s->file.data) {
00167         s->len = (int)(ptr - s->file.data);
00168         if(s->len >= uip_mss()) {
00169           s->len = uip_mss();
00170         }
00171       }
00172       PT_WAIT_THREAD(&s->scriptpt, send_part_of_file(s));
00173       s->file.data += s->len;
00174       s->file.len -= s->len;
00175     }
00176   }
00177   
00178   PT_END(&s->scriptpt);
00179 }
00180 #endif /* HTTPD_CONF_SCRIPT */
00181 /*---------------------------------------------------------------------------*/
00182 static
00183 PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr))
00184 {
00185   char *ptr;
00186 
00187   PSOCK_BEGIN(&s->sout);
00188 
00189   SEND_STRING(&s->sout, statushdr);
00190 
00191   ptr = strrchr(s->filename, ISO_period);
00192   if(ptr == NULL) {
00193     SEND_STRING(&s->sout, http_content_type_binary);
00194   } else if(strncmp(http_html, ptr, 5) == 0 ||
00195             strncmp(http_shtml, ptr, 6) == 0) {
00196     SEND_STRING(&s->sout, http_content_type_html);
00197 #if 0
00198   } else if(strncmp(http_css, ptr, 4) == 0) {
00199     SEND_STRING(&s->sout, http_content_type_css);
00200   } else if(strncmp(http_png, ptr, 4) == 0) {
00201     SEND_STRING(&s->sout, http_content_type_png);
00202   } else if(strncmp(http_gif, ptr, 4) == 0) {
00203     SEND_STRING(&s->sout, http_content_type_gif);
00204   } else if(strncmp(http_jpg, ptr, 4) == 0) {
00205     SEND_STRING(&s->sout, http_content_type_jpg);
00206 #endif
00207   } else {
00208     SEND_STRING(&s->sout, http_content_type_plain);
00209   }
00210   PSOCK_END(&s->sout);
00211 }
00212 /*---------------------------------------------------------------------------*/
00213 static
00214 PT_THREAD(handle_output(struct httpd_state *s))
00215 {
00216   char *ptr;
00217   
00218   PT_BEGIN(&s->outputpt);
00219  
00220   if(!httpd_fs_open(s->filename, &s->file)) {
00221     httpd_fs_open(http_404_html, &s->file);
00222     PT_WAIT_THREAD(&s->outputpt,
00223                    send_headers(s,
00224                    http_header_404));
00225     PT_WAIT_THREAD(&s->outputpt,
00226                    send_file(s));
00227   } else {
00228     PT_WAIT_THREAD(&s->outputpt,
00229                    send_headers(s,
00230                    http_header_200));
00231     ptr = strchr(s->filename, ISO_period);
00232 #if HTTPD_CONF_SCRIPT
00233     if(ptr != NULL && strncmp(ptr, http_shtml, 6) == 0) {
00234       PT_INIT(&s->scriptpt);
00235       PT_WAIT_THREAD(&s->outputpt, handle_script(s));
00236     } else {
00237       PT_WAIT_THREAD(&s->outputpt,
00238                      send_file(s));
00239     }
00240 #else /* HTTPD_CONF_SCRIPT */
00241     PT_WAIT_THREAD(&s->outputpt,
00242                    send_file(s));
00243 #endif /* HTTPD_CONF_SCRIPT */
00244   }
00245   PSOCK_CLOSE(&s->sout);
00246   PT_END(&s->outputpt);
00247 }
00248 /*---------------------------------------------------------------------------*/
00249 static
00250 PT_THREAD(handle_input(struct httpd_state *s))
00251 {
00252   PSOCK_BEGIN(&s->sin);
00253 
00254   PSOCK_READTO(&s->sin, ISO_space);
00255   
00256   if(strncmp(s->inputbuf, http_get, 4) != 0) {
00257     PSOCK_CLOSE_EXIT(&s->sin);
00258   }
00259   PSOCK_READTO(&s->sin, ISO_space);
00260 
00261   if(s->inputbuf[0] != ISO_slash) {
00262     PSOCK_CLOSE_EXIT(&s->sin);
00263   }
00264 
00265   if(s->inputbuf[1] == ISO_space) {
00266     strncpy(s->filename, http_index_html, sizeof(s->filename));
00267   } else {
00268     s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0;
00269     strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename));
00270   }
00271 
00272   libputs_arch(s->filename);
00273   
00274   s->state = STATE_OUTPUT;
00275 
00276   while(1) {
00277     PSOCK_READTO(&s->sin, ISO_nl);
00278 
00279     if(strncmp(s->inputbuf, http_referer, 8) == 0) {
00280       s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0;
00281     }
00282   }
00283   
00284   PSOCK_END(&s->sin);
00285 }
00286 /*---------------------------------------------------------------------------*/
00287 static void
00288 handle_connection(struct httpd_state *s)
00289 {
00290   handle_input(s);
00291   if(s->state == STATE_OUTPUT) {
00292     handle_output(s);
00293   }
00294 }
00295 /*---------------------------------------------------------------------------*/
00296 void
00297 httpd_appcall(void *state)
00298 {
00299   struct httpd_state *s = (struct httpd_state *)state;
00300 
00301   if(uip_closed() || uip_aborted() || uip_timedout()) {
00302     if(s != NULL) {
00303       memb_free(&conns, s);
00304     }
00305   } else if(uip_connected()) {
00306     s = (struct httpd_state *)memb_alloc(&conns);
00307     if(s == NULL) {
00308       uip_abort();
00309       return;
00310     }
00311     tcp_markconn(uip_conn, s);
00312     PSOCK_INIT(&s->sin, s->inputbuf, sizeof(s->inputbuf) - 1);
00313     PSOCK_INIT(&s->sout, s->inputbuf, sizeof(s->inputbuf) - 1);
00314     PT_INIT(&s->outputpt);
00315     s->state = STATE_WAITING;
00316     /*    timer_set(&s->timer, CLOCK_SECOND * 100);*/
00317     s->timer = 0;
00318     handle_connection(s);
00319   } else if(s != NULL) {
00320     if(uip_poll()) {
00321       ++s->timer;
00322       if(s->timer >= 20) {
00323         uip_abort();
00324         memb_free(&conns, s);
00325       }
00326     } else {
00327       s->timer = 0;
00328     }
00329     handle_connection(s);
00330   } else {
00331     uip_abort();
00332   }
00333 }
00334 /*---------------------------------------------------------------------------*/
00335 void
00336 httpd_init(void)
00337 {
00338   tcp_listen(UIP_HTONS(80));
00339   memb_init(&conns);
00340 #if HTTPD_CONF_CGI
00341   httpd_cgi_init();
00342 #endif /* HTTPD_CONF_CGI */
00343 }
00344 /*---------------------------------------------------------------------------*/