www.pudn.com > lwip-1.3.0.rar > httpd.c


/* 
 * Copyright (c) 2001-2003 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. The name of the author may not be used to endorse or promote products 
 *    derived from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 lwIP TCP/IP stack. 
 * 
 * Author: Adam Dunkels  
 * 
 */ 
 
/* This file is modified from the original version (httpd.c) as shipped with 
 * lwIP version 1.3.0. Changes have been made to allow support for a 
 * rudimentary server-side-include facility which will replace tags of the form 
 *  in any file whose extension is .shtml with strings provided by 
 * an include handler whose pointer is provided to the module via function 
 * http_set_ssi_handler(). Additionally, a simple common gateway interface 
 * (CGI) handling mechanism has been added to allow clients to hook functions 
 * to particular request URIs. 
 * 
 * To enable SSI support, define label INCLUDE_HTTPD_SSI in lwipopts.h. 
 * To enable CGI support, define label INCLUDE_HTTPD_CGI in lwipopts.h. 
 * 
 */ 
 
/* 
 * Notes about valid SSI tags 
 * -------------------------- 
 * 
 * The following assumptions are made about tags used in SSI markers: 
 * 
 * 1. No tag may contain '-' or whitespace characters within the tag name. 
 * 2. Whitespace is allowed between the tag leadin "". 
 * 3. The maximum tag name length is MAX_TAG_NAME_LEN, currently 8 characters. 
 * 
 * Notes on CGI usage 
 * ------------------ 
 * 
 * The simple CGI support offered here works with GET method requests only 
 * and can handle up to 16 parameters encoded into the URI. The handler 
 * function may not write directly to the HTTP output but must return a 
 * filename that the HTTP server will send to the browser as a response to 
 * the incoming CGI request. 
 * 
 */ 
#include "lwip/debug.h" 
 
#include "lwip/stats.h" 
 
#include "httpd.h" 
 
#include "lwip/tcp.h" 
 
#include "fs.h" 
 
#include  
#include "../../../utils/ustdlib.h" 
 
#ifdef INCLUDE_HTTPD_DEBUG 
#include "../../../utils/uartstdio.h" 
#define DEBUG_PRINT UARTprintf 
#else 
#define DEBUG_PRINT while(0)((int (*)(char *, ...))0) 
#endif 
 
#ifndef true 
#define true ((u8_t)1) 
#endif 
 
#ifndef false 
#define false ((u8_t)0) 
#endif 
 
typedef struct 
{ 
    const char *name; 
    u8_t shtml; 
} default_filename; 
 
const default_filename g_psDefaultFilenames[] = { 
  {"/index.shtml", true }, 
  {"/index.ssi", true }, 
  {"/index.shtm", true }, 
  {"/index.html", false }, 
  {"/index.htm", false } 
}; 
 
#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) /                 \ 
                               sizeof(default_filename)) 
 
#ifdef INCLUDE_HTTPD_SSI 
const char *g_pcSSIExtensions[] = { 
  ".shtml", ".shtm", ".ssi" 
}; 
 
#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *)) 
 
enum tag_check_state { 
    TAG_NONE,       /* Not processing an SSI tag */ 
    TAG_LEADIN,     /* Tag lead in "" being processed */ 
    TAG_SENDING     /* Sending tag replacement string */ 
}; 
#endif /* INCLUDE_HTTPD_SSI */ 
 
struct http_state { 
  struct fs_file *handle; 
  char *file;       /* Pointer to first unsent byte in buf. */ 
  char *buf;        /* File read buffer. */ 
#ifdef INCLUDE_HTTPD_SSI 
  char *parsed;     /* Pointer to the first unparsed byte in buf. */ 
  char *tag_end;    /* Pointer to char after the closing '>' of the tag. */ 
  u32_t parse_left; /* Number of unparsed bytes in buf. */ 
#endif 
  u32_t left;       /* Number of unsent bytes in buf. */ 
  int buf_len;      /* Size of file read buffer, buf. */ 
  u8_t retries; 
#ifdef INCLUDE_HTTPD_SSI 
  u8_t tag_check;   /* true if we are processing a .shtml file else false */ 
  u8_t tag_index;   /* Counter used by tag parsing state machine */ 
  u8_t tag_insert_len; /* Length of insert in string tag_insert */ 
  u8_t tag_name_len; /* Length of the tag name in string tag_name */ 
  char tag_name[MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ 
  char tag_insert[MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ 
  enum tag_check_state tag_state; /* State of the tag processor */ 
#endif 
#ifdef INCLUDE_HTTPD_CGI 
  char *params[MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ 
  char *param_vals[MAX_CGI_PARAMETERS]; /* Values for each extracted param */ 
#endif 
}; 
 
#ifdef INCLUDE_HTTPD_SSI 
/* SSI insert handler function pointer. */ 
tSSIHandler g_pfnSSIHandler = NULL; 
int g_iNumTags = 0; 
const char **g_ppcTags = NULL; 
 
#define LEN_TAG_LEAD_IN 5 
const char * const g_pcTagLeadIn = ""; 
#endif /* INCLUDE_HTTPD_SSI */ 
 
#ifdef INCLUDE_HTTPD_CGI 
/* CGI handler information */ 
const tCGI *g_pCGIs = NULL; 
int g_iNumCGIs = 0; 
#endif /* INCLUDE_HTTPD_CGI */ 
 
/*-----------------------------------------------------------------------------------*/ 
static void 
conn_err(void *arg, err_t err) 
{ 
  struct http_state *hs; 
 
  LWIP_UNUSED_ARG(err); 
 
  if(arg) 
  { 
      hs = arg; 
      if(hs->handle) { 
        fs_close(hs->handle); 
        hs->handle = NULL; 
      } 
      if(hs->buf) 
      { 
          mem_free(hs->buf); 
      } 
      mem_free(hs); 
  } 
} 
/*-----------------------------------------------------------------------------------*/ 
static void 
close_conn(struct tcp_pcb *pcb, struct http_state *hs) 
{ 
  DEBUG_PRINT("Closing connection 0x%08x\n", pcb); 
 
  tcp_arg(pcb, NULL); 
  tcp_sent(pcb, NULL); 
  tcp_recv(pcb, NULL); 
  if(hs->handle) { 
    fs_close(hs->handle); 
    hs->handle = NULL; 
  } 
  if(hs->buf) 
  { 
    mem_free(hs->buf); 
  } 
  mem_free(hs); 
  tcp_close(pcb); 
} 
/*-----------------------------------------------------------------------------------*/ 
#ifdef INCLUDE_HTTPD_CGI 
static int 
extract_uri_parameters(struct http_state *hs, char *params) 
{ 
  char *pair; 
  char *equals; 
  int loop; 
 
  /* If we have no parameters at all, return immediately. */ 
  if(!params || (params[0] == '\0')) { 
      return(0); 
  } 
 
  /* Get a pointer to our first parameter */ 
  pair = params; 
 
  /* 
   * Parse up to MAX_CGI_PARAMETERS from the passed string and ignore the 
   * remainder (if any) 
   */ 
  for(loop = 0; (loop < MAX_CGI_PARAMETERS) && pair; loop++) { 
 
    /* Save the name of the parameter */ 
    hs->params[loop] = pair; 
 
    /* Remember the start of this name=value pair */ 
    equals = pair; 
 
    /* Find the start of the next name=value pair and replace the delimiter 
     * with a 0 to terminate the previous pair string. 
     */ 
    pair = strchr(pair, '&'); 
    if(pair) { 
      *pair = '\0'; 
      pair++; 
    } 
 
    /* Now find the '=' in the previous pair, replace it with '\0' and save 
     * the parameter value string. 
     */ 
    equals = strchr(equals, '='); 
    if(equals) { 
      *equals = '\0'; 
      hs->param_vals[loop] = equals + 1; 
    } else { 
      hs->param_vals[loop] = NULL; 
    } 
  } 
 
  return(loop); 
} 
#endif /* INCLUDE_HTTPD_CGI */ 
 
/*-----------------------------------------------------------------------------------*/ 
#ifdef INCLUDE_HTTPD_SSI 
static void 
get_tag_insert(struct http_state *hs) 
{ 
  int loop; 
 
  if(g_pfnSSIHandler && g_ppcTags && g_iNumTags) { 
 
    /* Find this tag in the list we have been provided. */ 
    for(loop = 0; loop < g_iNumTags; loop++) 
    { 
      if(strcmp(hs->tag_name, g_ppcTags[loop]) == 0) { 
        hs->tag_insert_len = g_pfnSSIHandler(loop, hs->tag_insert, 
                                             MAX_TAG_INSERT_LEN); 
        return; 
      } 
    } 
  } 
 
  /* If we drop out, we were asked to serve a page which contains tags that 
   * we don't have a handler for. Merely echo back the tags with an error 
   * marker. 
   */ 
  usnprintf(hs->tag_insert, MAX_TAG_INSERT_LEN + 1, 
            "***UNKNOWN TAG %s***", hs->tag_name); 
  hs->tag_insert_len = strlen(hs->tag_insert); 
} 
#endif /* INCLUDE_HTTPD_SSI */ 
 
/*-----------------------------------------------------------------------------------*/ 
static void 
send_data(struct tcp_pcb *pcb, struct http_state *hs) 
{ 
  err_t err; 
  u16_t len; 
  u8_t data_to_send = false; 
 
  /* Have we run out of file data to send? If so, we need to read the next 
   * block from the file. 
   */ 
  if(hs->left == 0) 
  { 
    int count; 
 
    /* Do we already have a send buffer allocated? */ 
    if(hs->buf) { 
      /* Yes - get the length of the buffer */ 
      count = hs->buf_len; 
    } 
    else { 
      /* We don't have a send buffer so allocate one up to 2*mss bytes long. */ 
      count = 2*pcb->mss; 
      do { 
        hs->buf = mem_malloc(count); 
        if(hs->buf) { 
          hs->buf_len = count; 
          break; 
        } 
        count = count / 2; 
      }while (count > 100); 
 
      /* Did we get a send buffer? If not, return immediately. */ 
      if(hs->buf == NULL) { 
        DEBUG_PRINT("No buff\n"); 
        return; 
      } 
    } 
 
    /* Do we have a valid file handle? */ 
    if(hs->handle == NULL) 
    { 
        // 
        // No - close the connection. 
        // 
        close_conn(pcb, hs); 
        return; 
    } 
 
    /* Read a block of data from the file. */ 
    DEBUG_PRINT("Trying to read %d bytes.\n", count); 
 
    count = fs_read(hs->handle, hs->buf, count); 
    if(count < 0) { 
      /* We reached the end of the file so this request is done */ 
      DEBUG_PRINT("End of file.\n"); 
      fs_close(hs->handle); 
      hs->handle = NULL; 
      close_conn(pcb, hs); 
      return; 
    } 
 
    /* Set up to send the block of data we just read */ 
    DEBUG_PRINT("Read %d bytes.\n", count); 
    hs->left = count; 
    hs->file = hs->buf; 
#ifdef INCLUDE_HTTPD_SSI 
    hs->parse_left = count; 
    hs->parsed = hs->buf; 
#endif 
  } 
 
#ifdef INCLUDE_HTTPD_SSI 
  if(!hs->tag_check) { 
#endif 
      /* We are not processing an SHTML file so no tag checking is necessary. 
       * Just send the data as we received it from the file. 
       */ 
 
      /* We cannot send more data than space available in the send 
         buffer. */ 
      if (tcp_sndbuf(pcb) < hs->left) { 
        len = tcp_sndbuf(pcb); 
      } else { 
        len = hs->left; 
        LWIP_ASSERT("hs->left did not fit into u16_t!", (len == hs->left)); 
      } 
      if(len > (2*pcb->mss)) { 
        len = 2*pcb->mss; 
      } 
 
      do { 
        DEBUG_PRINT("Sending %d bytes\n", len); 
        err = tcp_write(pcb, hs->file, len, 0); 
        if (err == ERR_MEM) { 
          len /= 2; 
        } 
      } while (err == ERR_MEM && len > 1); 
 
      if (err == ERR_OK) { 
        data_to_send = true; 
        hs->file += len; 
        hs->left -= len; 
      } 
#ifdef INCLUDE_HTTPD_SSI 
  } else { 
    /* We are processing an SHTML file so need to scan for tags and replace 
     * them with insert strings. We need to be careful here since a tag may 
     * straddle the boundary of two blocks read from the file and we may also 
     * have to split the insert string between two tcp_write operations. 
     */ 
 
    /* How much data could we send? */ 
    len = tcp_sndbuf(pcb); 
 
    /* Do we have remaining data to send before parsing more? */ 
    if(hs->parsed > hs->file) { 
        /* We cannot send more data than space available in the send 
           buffer. */ 
        if (tcp_sndbuf(pcb) < (hs->parsed - hs->file)) { 
          len = tcp_sndbuf(pcb); 
        } else { 
          len = (hs->parsed - hs->file); 
          LWIP_ASSERT("Data size did not fit into u16_t!", 
                      (hs->parsed - hs->file)); 
        } 
        if(len > (2*pcb->mss)) { 
          len = 2*pcb->mss; 
        } 
 
        do { 
          DEBUG_PRINT("Sending %d bytes\n", len); 
          err = tcp_write(pcb, hs->file, len, 0); 
          if (err == ERR_MEM) { 
            len /= 2; 
          } 
        } while (err == ERR_MEM && len > 1); 
 
        if (err == ERR_OK) { 
          data_to_send = true; 
          hs->file += len; 
          hs->left -= len; 
        } 
 
        // 
        // If the send buffer is full, return now. 
        // 
        if(tcp_sndbuf(pcb) == 0) { 
          if(data_to_send) { 
            tcp_output(pcb); 
            DEBUG_PRINT("Output\n"); 
          } 
          return; 
        } 
    } 
 
    DEBUG_PRINT("State %d, %d left\n", hs->tag_state, hs->parse_left); 
 
    /* We have sent all the data that was already parsed so continue parsing 
     * the buffer contents looking for SSI tags. 
     */ 
    while(hs->parse_left) { 
      switch(hs->tag_state) { 
        case TAG_NONE: 
          /* We are not currently processing an SSI tag so scan for the 
           * start of the lead-in marker. 
           */ 
          if(*hs->parsed == g_pcTagLeadIn[0]) 
          { 
              /* We found what could be the lead-in for a new tag so change 
               * state appropriately. 
               */ 
              hs->tag_state = TAG_LEADIN; 
              hs->tag_index = 1; 
          } 
 
          /* Move on to the next character in the buffer */ 
          hs->parse_left--; 
          hs->parsed++; 
          break; 
 
        case TAG_LEADIN: 
          /* We are processing the lead-in marker, looking for the start of 
           * the tag name. 
           */ 
 
          /* Have we reached the end of the leadin? */ 
          if(hs->tag_index == LEN_TAG_LEAD_IN) { 
              hs->tag_index = 0; 
              hs->tag_state = TAG_FOUND; 
          } else { 
            /* Have we found the next character we expect for the tag leadin? 
             */ 
            if(*hs->parsed == g_pcTagLeadIn[hs->tag_index]) { 
              /* Yes - move to the next one unless we have found the complete 
               * leadin, in which case we start looking for the tag itself 
               */ 
              hs->tag_index++; 
            } else { 
              /* We found an unexpected character so this is not a tag. Move 
               * back to idle state. 
               */ 
              hs->tag_state = TAG_NONE; 
            } 
 
            /* Move on to the next character in the buffer */ 
            hs->parse_left--; 
            hs->parsed++; 
          } 
          break; 
 
        case TAG_FOUND: 
          /* We are reading the tag name, looking for the start of the 
           * lead-out marker and removing any whitespace found. 
           */ 
 
          /* Remove leading whitespace between the tag leading and the first 
           * tag name character. 
           */ 
          if((hs->tag_index == 0) && ((*hs->parsed == ' ') || 
             (*hs->parsed == '\t') || (*hs->parsed == '\n') || 
             (*hs->parsed == '\r'))) 
          { 
              /* Move on to the next character in the buffer */ 
              hs->parse_left--; 
              hs->parsed++; 
              break; 
          } 
 
          /* Have we found the end of the tag name? This is signalled by 
           * us finding the first leadout character or whitespace */ 
          if((*hs->parsed == g_pcTagLeadOut[0]) || 
             (*hs->parsed == ' ') || (*hs->parsed == '\t') || 
             (*hs->parsed == '\n')  || (*hs->parsed == '\r')) { 
 
            if(hs->tag_index == 0) { 
              /* We read a zero length tag so ignore it. */ 
              hs->tag_state = TAG_NONE; 
            } else { 
              /* We read a non-empty tag so go ahead and look for the 
               * leadout string. 
               */ 
              hs->tag_state = TAG_LEADOUT; 
              hs->tag_name_len = hs->tag_index; 
              hs->tag_name[hs->tag_index] = '\0'; 
              if(*hs->parsed == g_pcTagLeadOut[0]) { 
                hs->tag_index = 1; 
              } else { 
                hs->tag_index = 0; 
              } 
            } 
          } else { 
            /* This character is part of the tag name so save it */ 
            if(hs->tag_index < MAX_TAG_NAME_LEN) { 
              hs->tag_name[hs->tag_index++] = *hs->parsed; 
            } else { 
              /* The tag was too long so ignore it. */ 
              hs->tag_state = TAG_NONE; 
            } 
          } 
 
          /* Move on to the next character in the buffer */ 
          hs->parse_left--; 
          hs->parsed++; 
 
          break; 
 
        /* 
         * We are looking for the end of the lead-out marker. 
         */ 
        case TAG_LEADOUT: 
          /* Remove leading whitespace between the tag leading and the first 
           * tag leadout character. 
           */ 
          if((hs->tag_index == 0) && ((*hs->parsed == ' ') || 
             (*hs->parsed == '\t') || (*hs->parsed == '\n') || 
             (*hs->parsed == '\r'))) 
          { 
            /* Move on to the next character in the buffer */ 
            hs->parse_left--; 
            hs->parsed++; 
            break; 
          } 
 
          /* Have we found the next character we expect for the tag leadout? 
           */ 
          if(*hs->parsed == g_pcTagLeadOut[hs->tag_index]) { 
            /* Yes - move to the next one unless we have found the complete 
             * leadout, in which case we need to call the client to process 
             * the tag. 
             */ 
 
            /* Move on to the next character in the buffer */ 
            hs->parse_left--; 
            hs->parsed++; 
 
            if(hs->tag_index == (LEN_TAG_LEAD_OUT - 1)) { 
              /* Call the client to ask for the insert string for the 
               * tag we just found. 
               */ 
              get_tag_insert(hs); 
 
              /* Next time through, we are going to be sending data 
               * immediately, either the end of the block we start 
               * sending here or the insert string. 
               */ 
              hs->tag_index = 0; 
              hs->tag_state = TAG_SENDING; 
              hs->tag_end = hs->parsed; 
 
              /* If there is any unsent data in the buffer prior to the 
               * tag, we need to send it now. 
               */ 
              if(hs->tag_end > hs->file) 
              { 
                /* How much of the data can we send? */ 
                if(len > hs->tag_end - hs->file) { 
                    len = hs->tag_end - hs->file; 
                } 
 
                do { 
                  DEBUG_PRINT("Sending %d bytes\n", len); 
                  err = tcp_write(pcb, hs->file, len, 0); 
                  if (err == ERR_MEM) { 
                    len /= 2; 
                  } 
                } while (err == ERR_MEM && (len > 1)); 
 
                if (err == ERR_OK) { 
                  data_to_send = true; 
                  hs->file += len; 
                  hs->left -= len; 
                } 
              } 
            } else { 
              hs->tag_index++; 
            } 
          } else { 
              /* We found an unexpected character so this is not a tag. Move 
               * back to idle state. 
               */ 
              hs->parse_left--; 
              hs->parsed++; 
              hs->tag_state = TAG_NONE; 
          } 
          break; 
 
        /* 
         * We have found a valid tag and are in the process of sending 
         * data as a result of that discovery. We send either remaining data 
         * from the file prior to the insert point or the insert string itself. 
         */ 
        case TAG_SENDING: 
          /* Do we have any remaining file data to send from the buffer prior 
           * to the tag? 
           */ 
          if(hs->tag_end > hs->file) 
          { 
            /* How much of the data can we send? */ 
            if(len > hs->tag_end - hs->file) { 
              len = hs->tag_end - hs->file; 
            } 
 
            do { 
              DEBUG_PRINT("Sending %d bytes\n", len); 
              err = tcp_write(pcb, hs->file, len, 0); 
              if (err == ERR_MEM) { 
                len /= 2; 
              } 
            } while (err == ERR_MEM && (len > 1)); 
 
            if (err == ERR_OK) { 
              data_to_send = true; 
              hs->file += len; 
              hs->left -= len; 
            } 
          } else { 
            /* Do we still have insert data left to send? */ 
            if(hs->tag_index < hs->tag_insert_len) { 
              /* We are sending the insert string itself. How much of the 
               * insert can we send? */ 
              if(len > (hs->tag_insert_len - hs->tag_index)) { 
                len = (hs->tag_insert_len - hs->tag_index); 
              } 
 
              do { 
                DEBUG_PRINT("Sending %d bytes\n", len); 
                /* 
                 * Note that we set the copy flag here since we only have a 
                 * single tag insert buffer per connection. If we don't do 
                 * this, insert corruption can occur if more than one insert 
                 * is processed before we call tcp_output. 
                 */ 
                err = tcp_write(pcb, &(hs->tag_insert[hs->tag_index]), len, 1); 
                if (err == ERR_MEM) { 
                  len /= 2; 
                } 
              } while (err == ERR_MEM && (len > 1)); 
 
              if (err == ERR_OK) { 
                data_to_send = true; 
                hs->tag_index += len; 
                return; 
              } 
            } else { 
              /* We have sent all the insert data so go back to looking for 
               * a new tag. 
               */ 
              DEBUG_PRINT("Everything sent.\n"); 
              hs->tag_index = 0; 
              hs->tag_state = TAG_NONE; 
          } 
        } 
      } 
    } 
 
    /* 
     * If we drop out of the end of the for loop, this implies we must have 
     * file data to send so send it now. In TAG_SENDING state, we've already 
     * handled this so skip the send if that's the case. 
     */ 
    if((hs->tag_state != TAG_SENDING) && (hs->parsed > hs->file)) { 
      /* We cannot send more data than space available in the send 
         buffer. */ 
      if (tcp_sndbuf(pcb) < (hs->parsed - hs->file)) { 
        len = tcp_sndbuf(pcb); 
      } else { 
        len = (hs->parsed - hs->file); 
        LWIP_ASSERT("Data size did not fit into u16_t!", 
                    (hs->parsed - hs->file)); 
      } 
      if(len > (2*pcb->mss)) { 
        len = 2*pcb->mss; 
      } 
 
      do { 
        DEBUG_PRINT("Sending %d bytes\n", len); 
        err = tcp_write(pcb, hs->file, len, 0); 
        if (err == ERR_MEM) { 
          len /= 2; 
        } 
      } while (err == ERR_MEM && len > 1); 
 
      if (err == ERR_OK) { 
        data_to_send = true; 
        hs->file += len; 
        hs->left -= len; 
      } 
    } 
  } 
#endif /* INCLUDE_HTTPD_SSI */ 
 
  /* If we wrote anything to be sent, go ahead and send it now. */ 
  if(data_to_send) { 
    DEBUG_PRINT("tcp_output\n"); 
    tcp_output(pcb); 
  } 
 
  DEBUG_PRINT("send_data end.\n"); 
} 
 
/*-----------------------------------------------------------------------------------*/ 
static err_t 
http_poll(void *arg, struct tcp_pcb *pcb) 
{ 
  struct http_state *hs; 
 
  hs = arg; 
 
  DEBUG_PRINT("http_poll 0x%08x\n", pcb); 
 
  /*  printf("Polll\n");*/ 
  if (hs == NULL) { 
    /*    printf("Null, close\n");*/ 
    tcp_abort(pcb); 
    return ERR_ABRT; 
  } else { 
    ++hs->retries; 
    if (hs->retries == 4) { 
      tcp_abort(pcb); 
      return ERR_ABRT; 
    } 
    send_data(pcb, hs); 
  } 
 
  return ERR_OK; 
} 
/*-----------------------------------------------------------------------------------*/ 
static err_t 
http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) 
{ 
  struct http_state *hs; 
 
  DEBUG_PRINT("http_sent 0x%08x\n", pcb); 
 
  LWIP_UNUSED_ARG(len); 
 
  hs = arg; 
 
  hs->retries = 0; 
 
  /* Temporarily disable send notifications */ 
  tcp_sent(pcb, NULL); 
 
  send_data(pcb, hs); 
 
  /* Reenable notifications. */ 
  tcp_sent(pcb, http_sent); 
 
  return ERR_OK; 
} 
/*-----------------------------------------------------------------------------------*/ 
static err_t 
http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) 
{ 
  int i; 
  int loop; 
  char *data; 
  char *uri; 
  struct fs_file *file; 
  struct http_state *hs; 
#ifdef INCLUDE_HTTPD_CGI 
  int count; 
  char *params; 
#endif 
 
  DEBUG_PRINT("http_recv 0x%08x\n", pcb); 
 
  hs = arg; 
 
  if (err == ERR_OK && p != NULL) { 
 
    /* Inform TCP that we have taken the data. */ 
    tcp_recved(pcb, p->tot_len); 
 
    if (hs->handle == NULL) { 
      data = p->payload; 
      uri = &data[4]; 
      DEBUG_PRINT("Request:\n%s\n", data); 
      if (strncmp(data, "GET ", 4) == 0) { 
        /* 
         * We have a GET request. Find the end of the URI by looking for the 
         * HTTP marker. We can't just use strstr to find this since the request 
         * came from an outside source and we can't be sure that it is 
         * correctly formed. We need to make sure that our search is bounded 
         * by the packet length so we do it manually. If we don't find " HTTP", 
         * assume the request is invalid and close the connection. 
         */ 
        for(i = 4; i < (p->len - 5); i++) { 
          if ((data[i] == ' ') && (data[i + 1] == 'H') && 
              (data[i + 2] == 'T') && (data[i + 3] == 'T') && 
              (data[i + 4] == 'P')) { 
            data[i] = 0; 
            break; 
          } 
        } 
        if(i == (p->len - 5)) 
        { 
          /* We failed to find " HTTP" in the request so assume it is invalid */ 
          DEBUG_PRINT("Invalid GET request. Closing.\n"); 
          pbuf_free(p); 
          close_conn(pcb, hs); 
        } 
 
#ifdef INCLUDE_HTTPD_SSI 
        /* 
         * By default, assume we will not be processing server-side-includes 
         * tags 
         */ 
        hs->tag_check = false; 
#endif 
 
        /* 
         * Have we been asked for the default root file? 
         */ 
        if((uri[0] == '/') &&  (uri[1] == 0)) { 
          /* 
           * Try each of the configured default filenames until we find one 
           * that exists. 
           */ 
          for(loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) { 
            DEBUG_PRINT("Looking for %s...\n", g_psDefaultFilenames[loop].name); 
            file = fs_open((char *)g_psDefaultFilenames[loop].name); 
            if(file != NULL) { 
              DEBUG_PRINT("Opened.\n"); 
#ifdef INCLUDE_HTTPD_SSI 
              hs->tag_check = g_psDefaultFilenames[loop].shtml; 
#endif 
              break; 
            } 
          } 
          if(file == NULL) { 
            /* None of the default filenames exist so send back a 404 page */ 
            file = fs_open("/404.html"); 
#ifdef INCLUDE_HTTPD_SSI 
            hs->tag_check = false; 
#endif 
          } 
        } else { 
          /* No - we've been asked for a specific file. */ 
#ifdef INCLUDE_HTTPD_CGI 
          /* First, isolate the base URI (without any parameters) */ 
          params = strchr(uri, '?'); 
          if(params) { 
            *params = '\0'; 
            params++; 
          } 
 
          /* Does the base URI we have isolated correspond to a CGI handler? */ 
          if(g_iNumCGIs && g_pCGIs) { 
            for(i = 0; i < g_iNumCGIs; i++) { 
              if(strcmp(uri, g_pCGIs[i].pcCGIName) == 0) { 
                /* 
                 * We found a CGI that handles this URI so extract the 
                 * parameters and call the handler. 
                 */ 
                 count = extract_uri_parameters(hs, params); 
                 uri = g_pCGIs[i].pfnCGIHandler(i, count, hs->params, 
                                                hs->param_vals); 
                 break; 
              } 
            } 
          } 
#endif 
 
          DEBUG_PRINT("Opening %s\n", uri); 
 
          file = fs_open(uri); 
          if(file == NULL) { 
            file = fs_open("/404.html"); 
          } 
#ifdef INCLUDE_HTTPD_SSI 
          else { 
            /* 
             * See if we have been asked for an shtml file and, if so, 
             * enable tag checking. 
             */ 
            hs->tag_check = false; 
            for(loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { 
              if(ustrstr(uri, g_pcSSIExtensions[loop])) { 
                hs->tag_check = true; 
                break; 
              } 
            } 
          } 
#endif /* INCLUDE_HTTP_SSI */ 
        } 
 
#ifdef INCLUDE_HTTPD_SSI 
        hs->tag_index = 0; 
        hs->tag_state = TAG_NONE; 
        hs->parsed = file->data; 
        hs->parse_left = file->len; 
        hs->tag_end = file->data; 
#endif 
        hs->handle = file; 
        hs->file = file->data; 
        LWIP_ASSERT("File length must be positive!", (file->len >= 0)); 
        hs->left = file->len; 
        hs->retries = 0; 
        /* printf("data %p len %ld\n", hs->file, hs->left);*/ 
 
        pbuf_free(p); 
        send_data(pcb, hs); 
 
        /* Tell TCP that we wish be to informed of data that has been 
           successfully sent by a call to the http_sent() function. */ 
        tcp_sent(pcb, http_sent); 
      } else { 
        pbuf_free(p); 
        close_conn(pcb, hs); 
      } 
    } else { 
      pbuf_free(p); 
    } 
  } 
 
  if (err == ERR_OK && p == NULL) { 
    close_conn(pcb, hs); 
  } 
  return ERR_OK; 
} 
/*-----------------------------------------------------------------------------------*/ 
static err_t 
http_accept(void *arg, struct tcp_pcb *pcb, err_t err) 
{ 
  struct http_state *hs; 
 
  LWIP_UNUSED_ARG(arg); 
  LWIP_UNUSED_ARG(err); 
 
  DEBUG_PRINT("http_accept 0x%08x\n", pcb); 
 
  tcp_setprio(pcb, TCP_PRIO_MIN); 
 
  /* Allocate memory for the structure that holds the state of the 
     connection. */ 
  hs = (struct http_state *)mem_malloc(sizeof(struct http_state)); 
 
  if (hs == NULL) { 
    /* printf("http_accept: Out of memory\n"); */ 
    return ERR_MEM; 
  } 
 
  /* Initialize the structure. */ 
  hs->handle = NULL; 
  hs->file = NULL; 
  hs->buf = NULL; 
  hs->buf_len = 0; 
  hs->left = 0; 
  hs->retries = 0; 
 
  /* Tell TCP that this is the structure we wish to be passed for our 
     callbacks. */ 
  tcp_arg(pcb, hs); 
 
  /* Tell TCP that we wish to be informed of incoming data by a call 
     to the http_recv() function. */ 
  tcp_recv(pcb, http_recv); 
 
  tcp_err(pcb, conn_err); 
 
  tcp_poll(pcb, http_poll, 4); 
  return ERR_OK; 
} 
/*-----------------------------------------------------------------------------------*/ 
void 
httpd_init(void) 
{ 
  struct tcp_pcb *pcb; 
 
  DEBUG_PRINT("httpd_init\n"); 
 
  pcb = tcp_new(); 
  tcp_bind(pcb, IP_ADDR_ANY, 80); 
  pcb = tcp_listen(pcb); 
  tcp_accept(pcb, http_accept); 
} 
 
#ifdef INCLUDE_HTTPD_SSI 
/*-----------------------------------------------------------------------------------*/ 
void 
http_set_ssi_handler(tSSIHandler pfnSSIHandler, const char **ppcTags, 
                     int iNumTags) 
{ 
    DEBUG_PRINT("http_set_ssi_handler\n"); 
 
    g_pfnSSIHandler = pfnSSIHandler; 
    g_ppcTags = ppcTags; 
    g_iNumTags = iNumTags; 
} 
#endif 
 
#ifdef INCLUDE_HTTPD_CGI 
/*-----------------------------------------------------------------------------------*/ 
void 
http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers) 
{ 
    g_pCGIs = pCGIs; 
    g_iNumCGIs = iNumHandlers; 
} 
#endif 
 
/*-----------------------------------------------------------------------------------*/