diff --git a/platform/avr-ravenusb/httpd-simple-avr.c b/platform/avr-ravenusb/httpd-simple-avr.c new file mode 100644 index 000000000..4a21c44d7 --- /dev/null +++ b/platform/avr-ravenusb/httpd-simple-avr.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2010, Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: httpd-simple-avr.c,v 1.1 2010/08/26 18:55:43 dak664 Exp $ + */ + +/** + * \file + * A simple web server forwarding page generation to a protothread + * \author + * Adam Dunkels + * Niclas Finne + * Joakim Eriksson + * David Kopf (AVR adaptation) + */ + +#include +#include +#include +#include "contiki-net.h" + +#ifndef WEBSERVER_CONF_CFS_PATHLEN +#define HTTPD_PATHLEN 2 +#else +#define HTTPD_PATHLEN WEBSERVER_CONF_CFS_PATHLEN +#endif + +struct httpd_state; +typedef char (* httpd_simple_script_t)(struct httpd_state *s); + +struct httpd_state { + struct timer timer; + struct psock sin, sout; + struct pt outputpt; + char inputbuf[HTTPD_PATHLEN + 30]; + char outputbuf[UIP_TCP_MSS]; + char filename[HTTPD_PATHLEN]; + httpd_simple_script_t script; + char state; +}; + +/* DEBUGLOGIC is a convenient way to debug in a simulator without a tcp/ip connection. + * Break the program in the process loop and step from the entry in httpd_appcall. + * The input file is forced to /index.html and the output directed to uip_aligned_buf. + * If cgi's are invoked define it in httpd-cgi.c as well! + * psock_generator_send in /core/net/psock.c must also be modified as follows: + * ... + * // Wait until all data is sent and acknowledged. + * if (!s->sendlen) break; //<---add this line + * PT_YIELD_UNTIL(&s->psockpt, uip_acked() || uip_rexmit()); + * ... + */ +#define DEBUGLOGIC 0 +#if DEBUGLOGIC +struct httpd_state *sg; +#define uip_mss(...) sizeof(uip_aligned_buf) +#define uip_appdata (char *) &uip_aligned_buf +#endif + +#ifndef WEBSERVER_CONF_CFS_CONNS +#define CONNS UIP_CONNS +#else /* WEBSERVER_CONF_CFS_CONNS */ +#define CONNS WEBSERVER_CONF_CFS_CONNS +#endif /* WEBSERVER_CONF_CFS_CONNS */ + +#ifndef WEBSERVER_CONF_CFS_URLCONV +#define URLCONV 0 +#else /* WEBSERVER_CONF_CFS_URLCONV */ +#define URLCONV WEBSERVER_CONF_CFS_URLCONV +#endif /* WEBSERVER_CONF_CFS_URLCONV */ + +#define STATE_WAITING 0 +#define STATE_OUTPUT 1 + +MEMB(conns, struct httpd_state, CONNS); + +#define webserver_log_file(...) + +#define ISO_nl 0x0a +#define ISO_space 0x20 +#define ISO_period 0x2e +#define ISO_slash 0x2f + +/*---------------------------------------------------------------------------*/ +static unsigned short +generate_string(void *sstr) +{ + uint8_t slen=strlen((char *)sstr); + memcpy(uip_appdata, (char *)sstr, slen); + +#if DEBUGLOGIC + return 0; +#else + return slen; +#endif +} +/*---------------------------------------------------------------------------*/ +static unsigned short +generate_string_P(void *sstr) +{ + uint8_t slen=strlen_P((char *)sstr); + memcpy_P(uip_appdata, (char *)sstr, slen); + +#if DEBUGLOGIC + return 0; +#else + return slen; +#endif +} +/*---------------------------------------------------------------------------*/ +static +PT_THREAD(send_string_P(struct httpd_state *s, char *str)) +{ + PSOCK_BEGIN(&s->sout); + PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, str); + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +char http_content_type_html[] PROGMEM = "Content-type: text/html\r\n\r\n"; +static +PT_THREAD(send_headers(struct httpd_state *s, char *statushdr)) +{ + PSOCK_BEGIN(&s->sout); + PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, statushdr); + PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, http_content_type_html); + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +const char http_index_html[] PROGMEM = "/index.html"; +const char http_referer[] PROGMEM = "Referer:"; +const char http_get[] PROGMEM = "GET "; +static +PT_THREAD(handle_input(struct httpd_state *s)) +{ + PSOCK_BEGIN(&s->sin); + + PSOCK_READTO(&s->sin, ISO_space); + + if(strncmp_P(s->inputbuf, http_get, 4) != 0) { + PSOCK_CLOSE_EXIT(&s->sin); + } + PSOCK_READTO(&s->sin, ISO_space); + + if(s->inputbuf[0] != ISO_slash) { + PSOCK_CLOSE_EXIT(&s->sin); + } + +#if URLCONV +bomb + s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; + urlconv_tofilename(s->filename, s->inputbuf, sizeof(s->filename)); +#else /* URLCONV */ + if(s->inputbuf[1] == ISO_space) { + strncpy_P(s->filename, http_index_html, sizeof(s->filename)); + } else { + s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; + strncpy(s->filename, s->inputbuf, sizeof(s->filename)); + } +#endif /* URLCONV */ + + webserver_log_file(&uip_conn->ripaddr, s->filename); + + s->state = STATE_OUTPUT; + + while(1) { + PSOCK_READTO(&s->sin, ISO_nl); + + // if(strncmp_P(s->inputbuf, http_referer, 8) == 0) { + // s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; + // webserver_log(s->inputbuf); + // } + } + + PSOCK_END(&s->sin); +} +/*---------------------------------------------------------------------------*/ +void +httpd_init(void) +{ + tcp_listen(HTONS(80)); + memb_init(&conns); +} + +/*---------------------------------------------------------------------------*/ +/* Only one single web request at time */ +static char buf[64]; +static uint8_t blen; +#define ADD(FORMAT,args...) do { \ + blen += snprintf_P(&buf[blen], sizeof(buf) - blen, PSTR(FORMAT),##args); \ + } while(0) +/*---------------------------------------------------------------------------*/ +static void +ipaddr_add(const uip_ipaddr_t *addr) +{ + uint16_t a; + int i, f; + for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) { + a = (addr->u8[i] << 8) + addr->u8[i + 1]; + if(a == 0 && f >= 0) { + if(f++ == 0 && sizeof(buf) - blen >= 2) { + buf[blen++] = ':'; + buf[blen++] = ':'; + } + } else { + if(f > 0) { + f = -1; + } else if(i > 0 && blen < sizeof(buf)) { + buf[blen++] = ':'; + } + ADD("%x", a); + } + } +} +/*---------------------------------------------------------------------------*/ +char TOP[] PROGMEM = "ContikiRPL(Jackdaw)\n"; +char BOTTOM[] PROGMEM = "\n"; +extern uip_ds6_nbr_t uip_ds6_nbr_cache[]; +extern uip_ds6_route_t uip_ds6_routing_table[]; +static +PT_THREAD(generate_routes(struct httpd_state *s)) +{ + static int i; + PSOCK_BEGIN(&s->sout); + + PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, TOP); + + blen = 0; + ADD("

Neighbors

"); + for(i = 0; i < UIP_DS6_NBR_NB; i++) { + if(uip_ds6_nbr_cache[i].isused) { + ipaddr_add(&uip_ds6_nbr_cache[i].ipaddr); + ADD("
\n"); + if(blen > sizeof(buf) - 45) { + PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf); + blen = 0; + } + } + } + + ADD("

Routes

"); + PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf); + blen = 0; + for(i = 0; i < UIP_DS6_ROUTE_NB; i++) { + if(uip_ds6_routing_table[i].isused) { + ipaddr_add(&uip_ds6_routing_table[i].ipaddr); + ADD("/%u (via ", uip_ds6_routing_table[i].length); + ipaddr_add(&uip_ds6_routing_table[i].nexthop); + if(uip_ds6_routing_table[i].state.lifetime < 600) { + ADD(") %lus
\n", uip_ds6_routing_table[i].state.lifetime); + } else { + ADD(")
\n"); + } + PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf); + blen = 0; + } + } + if(blen > 0) { + PSOCK_GENERATOR_SEND(&s->sout, generate_string, buf); + blen = 0; + } + + PSOCK_GENERATOR_SEND(&s->sout, generate_string_P, BOTTOM); + + PSOCK_END(&s->sout); +} +/*---------------------------------------------------------------------------*/ +httpd_simple_script_t +httpd_simple_get_script(const char *name) +{ + return generate_routes; +} +/*---------------------------------------------------------------------------*/ +char http_header_200[] PROGMEM = "HTTP/1.0 200 OK\r\nServer: Jackdaw\r\nConnection: close\r\n"; +char http_header_404[] PROGMEM = "HTTP/1.0 404 Not found\r\nServer: Jackdaw\r\nConnection: close\r\n"; +char NOT_FOUND[] PROGMEM = "

404 - file not found

"; +static +PT_THREAD(handle_output(struct httpd_state *s)) +{ + PT_BEGIN(&s->outputpt); + +#if DEBUGLOGIC + strcpy_P(s->filename,PSTR("/x")); +#endif +#if FIND_THE_SCRIPT + s->script = httpd_simple_get_script(&s->filename[1]); + if(s->script == NULL) { + printf_P(PSTR("not found!")); + strcpy_P(s->filename, PSTR("/notfound.html")); + + PT_WAIT_THREAD(&s->outputpt, + send_headers(s, http_header_404)); + PT_WAIT_THREAD(&s->outputpt, + send_string_P(s, NOT_FOUND)); + uip_close(); + + PT_EXIT(&s->outputpt); + } else { +#else + s->script = generate_routes; + if (1) { +#endif + + PT_WAIT_THREAD(&s->outputpt, + send_headers(s, http_header_200)); + PT_WAIT_THREAD(&s->outputpt, s->script(s)); + } + s->script = NULL; + PSOCK_CLOSE(&s->sout); + PT_END(&s->outputpt); +} +/*---------------------------------------------------------------------------*/ +static void +handle_connection(struct httpd_state *s) +{ +#if DEBUGLOGIC + handle_output(s); +#else + handle_input(s); + if(s->state == STATE_OUTPUT) { + handle_output(s); + } +#endif +} +/*---------------------------------------------------------------------------*/ +void +httpd_appcall(void *state) +{ +#if DEBUGLOGIC + struct httpd_state *s; //Enter here for debugging with output directed to TCPBUF + s = sg = (struct httpd_state *)memb_alloc(&conns); //put ram watch on sg + if (1) { +#else + struct httpd_state *s = (struct httpd_state *)state; + + if(uip_closed() || uip_aborted() || uip_timedout()) { + if(s != NULL) { + s->script = NULL; + memb_free(&conns, s); + } + } else if(uip_connected()) { + s = (struct httpd_state *)memb_alloc(&conns); + if(s == NULL) { + uip_abort(); + webserver_log_file(&uip_conn->ripaddr, "reset (no memory block)"); + return; + } +#endif + tcp_markconn(uip_conn, s); + PSOCK_INIT(&s->sin, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PSOCK_INIT(&s->sout, (uint8_t *)s->inputbuf, sizeof(s->inputbuf) - 1); + PT_INIT(&s->outputpt); + s->script = NULL; + s->state = STATE_WAITING; + timer_set(&s->timer, CLOCK_SECOND * 10); + handle_connection(s); + } else if(s != NULL) { + if(uip_poll()) { + if(timer_expired(&s->timer)) { + uip_abort(); + s->script = NULL; + memb_free(&conns, s); + webserver_log_file(&uip_conn->ripaddr, "reset (timeout)"); + } + } else { + timer_restart(&s->timer); + } + handle_connection(s); + } else { + uip_abort(); + } +} +/*---------------------------------------------------------------------------*/ +PROCESS(httpd_process, "httpd"); +PROCESS_THREAD(httpd_process, ev, data) +{ + PROCESS_BEGIN(); + + httpd_init(); + + while(1) { + PROCESS_WAIT_EVENT_UNTIL(ev == tcpip_event); + httpd_appcall(data); + } + + PROCESS_END(); +} +