Fork of Smoothie to port to mbed non-LPC targets.
Fork of Smoothie by
Diff: libs/Network/uip/webserver/httpd.c
- Revision:
- 3:f151d08d335c
- Parent:
- 2:1df0b61d3b5a
--- a/libs/Network/uip/webserver/httpd.c Fri Feb 28 18:52:52 2014 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,680 +0,0 @@ -#pragma GCC diagnostic ignored "-Wredundant-decls" -#pragma GCC diagnostic ignored "-Wstrict-aliasing" -#pragma GCC diagnostic ignored "-Wcast-align" -#pragma GCC diagnostic ignored "-Wcast-qual" -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" - -/** - * \addtogroup apps - * @{ - */ - -/** - * \defgroup httpd Web server - * @{ - * The uIP web server is a very simplistic implementation of an HTTP - * server. It can serve web pages and files from a read-only ROM - * filesystem, and provides a very small scripting language. - - */ - -/** - * \file - * Web server - * \author - * Adam Dunkels <adam@sics.se> - */ - - -/* - * Copyright (c) 2004, Adam Dunkels. - * 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. - * - * This file is part of the uIP TCP/IP stack. - * - * Author: Adam Dunkels <adam@sics.se> - * - * $Id: httpd.c,v 1.2 2006/06/11 21:46:38 adam Exp $ - */ - -#include <stdio.h> - -#include "uip.h" -#include "httpd.h" -#include "httpd-fs.h" -#include "http-strings.h" - -#include <string.h> -#include "stdio.h" -#include "stdlib.h" - -#include "CommandQueue.h" -#include "CallbackStream.h" - -#include "c-fifo.h" - -#define STATE_WAITING 0 -#define STATE_HEADERS 1 -#define STATE_BODY 2 -#define STATE_OUTPUT 3 -#define STATE_UPLOAD 4 - -#define GET 1 -#define POST 2 - -#define ISO_nl 0x0a -#define ISO_space 0x20 -#define ISO_bang 0x21 -#define ISO_percent 0x25 -#define ISO_period 0x2e -#define ISO_slash 0x2f -#define ISO_colon 0x3a - -#define DEBUG_PRINTF printf -//#define DEBUG_PRINTF(...) - - -// this callback gets the results of a command, line by line. need to check if -// we need to stall the upstream sender return 0 if stalled 1 if ok to keep -// providing more -1 if the connection has closed or is not in output state. -// need to see which connection to send to based on state and add result to -// that fifo for each connection. NOTE this will not get called if the -// connection has been closed and the stream will get deleted when the last -// command has been executed -static int command_result(const char *str, void *state) -{ - struct httpd_state *s = (struct httpd_state *)state; - if (s == NULL) { - // connection was closed so discard, this should never happen - DEBUG_PRINTF("ERROR: command result for closed state %d\n", (int)state); - return -1; - } - - if (str == NULL) { - DEBUG_PRINTF("End of command (%p)\n", state); - fifo_push(s->fifo, NULL); - - } else { - if (fifo_size(s->fifo) < 10) { - DEBUG_PRINTF("Got command result (%p): %s", state, str); - fifo_push(s->fifo, strdup(str)); - return 1; - } else { - DEBUG_PRINTF("command result fifo is full (%p)\n", state); - return 0; - } - } - return 1; -} - -static void create_callback_stream(struct httpd_state *s) -{ - // need to create a callback stream here, but do one per connection pass - // the state to the callback, also create the fifo for the command results - s->fifo = new_fifo(); - s->pstream = new_callback_stream(command_result, s); -} - -// Used to save files to SDCARD during upload -static FILE *fd; -static char *output_filename = NULL; -static int file_cnt = 0; -static int open_file(const char *fn) -{ - if (output_filename != NULL) free(output_filename); - output_filename = malloc(strlen(fn) + 5); - strcpy(output_filename, "/sd/"); - strcat(output_filename, fn); - fd = fopen(output_filename, "w"); - if (fd == NULL) { - free(output_filename); - output_filename = NULL; - return 0; - } - return 1; -} - -static int close_file() -{ - free(output_filename); - output_filename = NULL; - fclose(fd); - return 1; -} - -static int save_file(uint8_t *buf, unsigned int len) -{ - if (fwrite(buf, 1, len, fd) == len) { - file_cnt += len; - // HACK alert work around bug causing file corruption when writing large amounts of data - if (file_cnt >= 400) { - file_cnt = 0; - fclose(fd); - fd = fopen(output_filename, "a"); - } - return 1; - - } else { - close_file(); - return 0; - } -} - -static int fs_open(struct httpd_state *s) -{ - if (strncmp(s->filename, "/sd/", 4) == 0) { - DEBUG_PRINTF("Opening file %s\n", s->filename); - s->fd = fopen(s->filename, "r"); - if (s->fd == NULL) { - DEBUG_PRINTF("Failed to open: %s\n", s->filename); - return 0; - } - return 1; - - } else { - s->fd = NULL; - return httpd_fs_open(s->filename, &s->file); - } -} - -/*---------------------------------------------------------------------------*/ -static PT_THREAD(send_command_response(struct httpd_state *s)) -{ - PSOCK_BEGIN(&s->sout); - - do { - PSOCK_WAIT_UNTIL( &s->sout, fifo_size(s->fifo) > 0 ); - s->strbuf = fifo_pop(s->fifo); - if (s->strbuf != NULL) { - // send it - DEBUG_PRINTF("Sending response: %s", s->strbuf); - // TODO send as much as we can in one packet - PSOCK_SEND_STR(&s->sout, s->strbuf); - // free the strdup - free(s->strbuf); - }else if(--s->command_count <= 0) { - // when all commands have completed exit - break; - } - } while (1); - - PSOCK_END(&s->sout); -} - -/*---------------------------------------------------------------------------*/ -static unsigned short generate_part_of_file(void *state) -{ - struct httpd_state *s = (struct httpd_state *)state; - - if (s->file.len > uip_mss()) { - s->len = uip_mss(); - } else { - s->len = s->file.len; - } - memcpy(uip_appdata, s->file.data, s->len); - - return s->len; -} -/*---------------------------------------------------------------------------*/ -static unsigned short generate_part_of_sd_file(void *state) -{ - struct httpd_state *s = (struct httpd_state *)state; - - int len = fread(uip_appdata, 1, uip_mss(), s->fd); - if (len <= 0) { - // we need to send something - strcpy(uip_appdata, "\r\n"); - len = 2; - s->len = 0; - } else { - s->len = len; - } - return len; -} -/*---------------------------------------------------------------------------*/ -static -PT_THREAD(send_file(struct httpd_state *s)) -{ - PSOCK_BEGIN(&s->sout); - - do { - PSOCK_GENERATOR_SEND(&s->sout, generate_part_of_file, s); - s->file.len -= s->len; - s->file.data += s->len; - } while (s->file.len > 0); - - PSOCK_END(&s->sout); -} - -/*---------------------------------------------------------------------------*/ -static PT_THREAD(send_sd_file(struct httpd_state *s)) -{ - PSOCK_BEGIN(&s->sout); - - do { - PSOCK_GENERATOR_SEND(&s->sout, generate_part_of_sd_file, s); - } while (s->len > 0); - - fclose(s->fd); - s->fd = NULL; - - PSOCK_END(&s->sout); -} - -/*---------------------------------------------------------------------------*/ -static PT_THREAD(send_headers_3(struct httpd_state *s, const char *statushdr, char send_content_type)) -{ - char *ptr; - - PSOCK_BEGIN(&s->sout); - - PSOCK_SEND_STR(&s->sout, statushdr); - - if (send_content_type) { - ptr = strrchr(s->filename, ISO_period); - if (ptr == NULL) { - PSOCK_SEND_STR(&s->sout, http_content_type_plain); // http_content_type_binary); - } else if (strncmp(http_html, ptr, 5) == 0 || strncmp(http_shtml, ptr, 6) == 0) { - PSOCK_SEND_STR(&s->sout, http_content_type_html); - } else if (strncmp(http_css, ptr, 4) == 0) { - PSOCK_SEND_STR(&s->sout, http_content_type_css); - } else if (strncmp(http_png, ptr, 4) == 0) { - PSOCK_SEND_STR(&s->sout, http_content_type_png); - } else if (strncmp(http_gif, ptr, 4) == 0) { - PSOCK_SEND_STR(&s->sout, http_content_type_gif); - } else if (strncmp(http_jpg, ptr, 4) == 0) { - PSOCK_SEND_STR(&s->sout, http_content_type_jpg); - } else { - PSOCK_SEND_STR(&s->sout, http_content_type_plain); - } - } - PSOCK_END(&s->sout); -} -static PT_THREAD(send_headers(struct httpd_state *s, const char *statushdr)) -{ - return send_headers_3(s, statushdr, 1); -} -/*---------------------------------------------------------------------------*/ -static -PT_THREAD(handle_output(struct httpd_state *s)) -{ - PT_BEGIN(&s->outputpt); - - if (s->method == POST) { - if (strcmp(s->filename, "/command") == 0) { - DEBUG_PRINTF("Executed command post\n"); - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_200)); - // send response as we get it - PT_WAIT_THREAD(&s->outputpt, send_command_response(s)); - - } else if (strcmp(s->filename, "/command_silent") == 0) { - DEBUG_PRINTF("Executed silent command post\n"); - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_200)); - - } else if (strcmp(s->filename, "/upload") == 0) { - DEBUG_PRINTF("upload output: %d\n", s->uploadok); - if (s->uploadok == 0) { - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_503)); - PSOCK_SEND_STR(&s->sout, "FAILED\r\n"); - } else { - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_200)); - PSOCK_SEND_STR(&s->sout, "OK\r\n"); - } - - } else { - DEBUG_PRINTF("Unknown POST: %s\n", s->filename); - httpd_fs_open(http_404_html, &s->file); - strcpy(s->filename, http_404_html); - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_404)); - PT_WAIT_THREAD(&s->outputpt, send_file(s)); - } - - } else { - // Presume method GET - if (!fs_open(s)) { // Note this has the side effect of opening the file - DEBUG_PRINTF("404 file not found\n"); - httpd_fs_open(http_404_html, &s->file); - strcpy(s->filename, http_404_html); - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_404)); - PT_WAIT_THREAD(&s->outputpt, send_file(s)); - - } else if (s->cache_page) { - if (s->fd != NULL) { - // if it was an sd file then we need to close it - fclose(s->fd); - s->fd = NULL; - } - // tell it it has not changed - DEBUG_PRINTF("304 Not Modified\n"); - PT_WAIT_THREAD(&s->outputpt, send_headers_3(s, http_header_304, 0)); - - } else { - DEBUG_PRINTF("sending file %s\n", s->filename); - PT_WAIT_THREAD(&s->outputpt, send_headers(s, http_header_200)); - if (s->fd != NULL) { - // send from sd card - PT_WAIT_THREAD(&s->outputpt, send_sd_file(s)); - - } else { - // send from FLASH - PT_WAIT_THREAD(&s->outputpt, send_file(s)); - } - } - } - - PSOCK_CLOSE(&s->sout); - PT_END(&s->outputpt); -} - -/*---------------------------------------------------------------------------*/ -// this forces us to yield every other call as we read all data everytime -static char has_newdata(struct httpd_state *s) -{ - if (s->upload_state == 1) { - /* All data in uip_appdata buffer already consumed. */ - s->upload_state = 0; - return 0; - } else if (uip_newdata()) { - /* There is new data that has not been consumed. */ - return 1; - } else { - /* There is no new data. */ - return 0; - } -} - -/* - * handle trhe uploaded data, as there may be part of that buffer still in the last packet buffer - * write that first from the buf/len parameters - */ -static PT_THREAD(handle_uploaded_data(struct httpd_state *s, uint8_t *buf, int len)) -{ - PT_BEGIN(&s->inputpt); - - DEBUG_PRINTF("Uploading file: %s, %d\n", s->upload_name, s->content_length); - - // The body is the raw data to be stored to the file - if (!open_file(s->upload_name)) { - DEBUG_PRINTF("failed to open file\n"); - s->uploadok = 0; - PT_EXIT(&s->inputpt); - } - - DEBUG_PRINTF("opened file: %s\n", s->upload_name); - - if (len > 0) { - // write the first part of the buffer - if (!save_file(buf, len)) { - DEBUG_PRINTF("initial write failed\n"); - s->uploadok = 0; - PT_EXIT(&s->inputpt); - } - s->content_length -= len; - } - - s->upload_state = 1; // first time through we need to yield to get new data - - // save the entire input buffer - while (s->content_length > 0) { - PT_WAIT_UNTIL(&s->inputpt, has_newdata(s)); - s->upload_state = 1; - - u8_t *readptr = (u8_t *)uip_appdata; - int readlen = uip_datalen(); - //DEBUG_PRINTF("read %d bytes of data\n", readlen); - - if (readlen > 0) { - if (!save_file(readptr, readlen)) { - DEBUG_PRINTF("write failed\n"); - s->uploadok = 0; - PT_EXIT(&s->inputpt); - } - s->content_length -= readlen; - } - } - - close_file(); - s->uploadok = 1; - DEBUG_PRINTF("finished upload\n"); - - PT_END(&s->inputpt); -} -/*---------------------------------------------------------------------------*/ -static -PT_THREAD(handle_input(struct httpd_state *s)) -{ - PSOCK_BEGIN(&s->sin); - - PSOCK_READTO(&s->sin, ISO_space); - - if (strncmp(s->inputbuf, http_get, 4) == 0) { - s->method = GET; - } else if (strncmp(s->inputbuf, http_post, 4) == 0) { - s->method = POST; - } else { - DEBUG_PRINTF("Unexpected method: %s\n", s->inputbuf); - PSOCK_CLOSE_EXIT(&s->sin); - } - - DEBUG_PRINTF("Method: %s\n", s->method == POST ? "POST" : "GET"); - - PSOCK_READTO(&s->sin, ISO_space); - - if (s->inputbuf[0] != ISO_slash) { - PSOCK_CLOSE_EXIT(&s->sin); - } - - if (s->inputbuf[1] == ISO_space) { - strncpy(s->filename, http_index_html, sizeof(s->filename)); - } else { - s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; - strncpy(s->filename, &s->inputbuf[0], sizeof(s->filename)); - } - - DEBUG_PRINTF("filename: %s\n", s->filename); - - /* httpd_log_file(uip_conn->ripaddr, s->filename);*/ - - s->state = STATE_HEADERS; - s->content_length = 0; - s->cache_page = 0; - while (1) { - if (s->state == STATE_HEADERS) { - // read the headers of the request - PSOCK_READTO(&s->sin, ISO_nl); - s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; - if (s->inputbuf[0] == '\r') { - DEBUG_PRINTF("end of headers\n"); - if (s->method == GET) { - s->state = STATE_OUTPUT; - break; - } else if (s->method == POST) { - if (strcmp(s->filename, "/upload") == 0) { - s->state = STATE_UPLOAD; - } else { - s->state = STATE_BODY; - } - } - } else { - DEBUG_PRINTF("reading header: %s\n", s->inputbuf); - // handle headers here - if (strncmp(s->inputbuf, http_content_length, sizeof(http_content_length) - 1) == 0) { - s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; - s->content_length = atoi(&s->inputbuf[sizeof(http_content_length) - 1]); - DEBUG_PRINTF("Content length= %s, %d\n", &s->inputbuf[sizeof(http_content_length) - 1], s->content_length); - - } else if (strncmp(s->inputbuf, "X-Filename: ", 11) == 0) { - s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; - strncpy(s->upload_name, &s->inputbuf[12], sizeof(s->upload_name) - 1); - DEBUG_PRINTF("Upload name= %s\n", s->upload_name); - - } else if (strncmp(s->inputbuf, http_cache_control, sizeof(http_cache_control) - 1) == 0) { - s->inputbuf[PSOCK_DATALEN(&s->sin) - 2] = 0; - s->cache_page = strncmp(http_no_cache, &s->inputbuf[sizeof(http_cache_control) - 1], sizeof(http_no_cache) - 1) != 0; - DEBUG_PRINTF("cache page= %d\n", s->cache_page); - } - } - - } else if (s->state == STATE_BODY) { - if (s->method == POST && strcmp(s->filename, "/command") == 0) { - // create a callback stream and fifo for the results as it is a command - create_callback_stream(s); - - } else if (s->method == POST && strcmp(s->filename, "/command_silent") == 0) { - // stick the command on the command queue specifying null output stream - s->pstream = NULL; - - } else { // unknown POST - DEBUG_PRINTF("Unknown Post URL: %s\n", s->filename); - s->state = STATE_OUTPUT; - break; - } - s->command_count= 0; - // read the Body of the request, each line is a command - if (s->content_length > 0) { - DEBUG_PRINTF("start reading body %d...\n", s->content_length); - while (s->content_length > 2) { - PSOCK_READTO(&s->sin, ISO_nl); - s->inputbuf[PSOCK_DATALEN(&s->sin) - 1] = 0; - s->content_length -= PSOCK_DATALEN(&s->sin); - // stick the command on the command queue, with this connections stream output - DEBUG_PRINTF("Adding command: %s, left: %d\n", s->inputbuf, s->content_length); - network_add_command(s->inputbuf, s->pstream); - s->command_count++; // count number of command lines we submit - } - DEBUG_PRINTF("Read body done\n"); - s->state = STATE_OUTPUT; - - } else { - s->state = STATE_OUTPUT; - } - break; - - } else if (s->state == STATE_UPLOAD) { - PSOCK_WAIT_THREAD(&s->sin, handle_uploaded_data(s, PSOCK_GET_START_OF_REST_OF_BUFFER(&s->sin), PSOCK_GET_LENGTH_OF_REST_OF_BUFFER(&s->sin))); - PSOCK_MARK_BUFFER_READ(&s->sin); - s->state = STATE_OUTPUT; - break; - - } else { - DEBUG_PRINTF("WTF State: %d", s->state); - break; - } - } - - PSOCK_END(&s->sin); -} -/*---------------------------------------------------------------------------*/ -static void -handle_connection(struct httpd_state *s) -{ - if (s->state != STATE_OUTPUT) { - handle_input(s); - } - if (s->state == STATE_OUTPUT) { - handle_output(s); - } -} -/*---------------------------------------------------------------------------*/ -void -httpd_appcall(void) -{ - struct httpd_state *s = (struct httpd_state *)(uip_conn->appstate); - - if (uip_connected()) { - s = malloc(sizeof(struct httpd_state)); - if (s == NULL) { - DEBUG_PRINTF("Connection: Out of memory\n"); - uip_abort(); - return; - } - uip_conn->appstate = s; - DEBUG_PRINTF("Connection: %d.%d.%d.%d:%d\n", - uip_ipaddr1(uip_conn->ripaddr), uip_ipaddr2(uip_conn->ripaddr), - uip_ipaddr3(uip_conn->ripaddr), uip_ipaddr4(uip_conn->ripaddr), - HTONS(uip_conn->rport)); - - PSOCK_INIT(&s->sin, s->inputbuf, sizeof(s->inputbuf) - 1); - PSOCK_INIT(&s->sout, s->inputbuf, sizeof(s->inputbuf) - 1); - PT_INIT(&s->outputpt); - PT_INIT(&s->inputpt); - s->state = STATE_WAITING; - /* timer_set(&s->timer, CLOCK_SECOND * 100);*/ - s->timer = 0; - s->fd = NULL; - s->strbuf = NULL; - s->fifo = NULL; - s->pstream = NULL; - } - - if (s == NULL) { - DEBUG_PRINTF("ERROR no state context: %d\n", uip_flags); - uip_abort(); - return; - } - - // check for timeout on connection here so we can cleanup if we abort - if (uip_poll()) { - ++s->timer; - if (s->timer >= 20 * 2) { // we have a 0.5 second poll and we want 20 second timeout - DEBUG_PRINTF("Timer expired, aborting\n"); - uip_abort(); - } - } else { - s->timer = 0; - } - - if (uip_closed() || uip_aborted() || uip_timedout()) { - DEBUG_PRINTF("Closing connection: %d\n", HTONS(uip_conn->rport)); - if (s->fd != NULL) fclose(fd); // clean up - if (s->strbuf != NULL) free(s->strbuf); - if (s->pstream != NULL) { - // free these if they were allocated - delete_fifo(s->fifo); - delete_callback_stream(s->pstream); // this will mark it as closed and will get deleted when no longer needed - } - free(s) ; - uip_conn->appstate = NULL; - - } else { - handle_connection(s); - } -} - -/*---------------------------------------------------------------------------*/ -/** - * \brief Initialize the web server - * - * This function initializes the web server and should be - * called at system boot-up. - */ -void httpd_init(void) -{ - uip_listen(HTONS(80)); -} -/*---------------------------------------------------------------------------*/ -/** @} */