ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_httpd.c Source File

lwip_httpd.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * LWIP HTTP server implementation
00004  */
00005 
00006 /*
00007  * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without modification,
00011  * are permitted provided that the following conditions are met:
00012  *
00013  * 1. Redistributions of source code must retain the above copyright notice,
00014  *    this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright notice,
00016  *    this list of conditions and the following disclaimer in the documentation
00017  *    and/or other materials provided with the distribution.
00018  * 3. The name of the author may not be used to endorse or promote products
00019  *    derived from this software without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00022  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00023  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00024  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00025  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00026  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00029  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00030  * OF SUCH DAMAGE.
00031  *
00032  * This file is part of the lwIP TCP/IP stack.
00033  *
00034  * Author: Adam Dunkels <adam@sics.se>
00035  *         Simon Goldschmidt
00036  *
00037  */
00038 
00039 /**
00040  * @defgroup httpd HTTP server
00041  * @ingroup apps
00042  *
00043  * This httpd supports for a
00044  * rudimentary server-side-include facility which will replace tags of the form
00045  * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with
00046  * strings provided by an include handler whose pointer is provided to the
00047  * module via function http_set_ssi_handler().
00048  * Additionally, a simple common
00049  * gateway interface (CGI) handling mechanism has been added to allow clients
00050  * to hook functions to particular request URIs.
00051  *
00052  * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h.
00053  * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h.
00054  *
00055  * By default, the server assumes that HTTP headers are already present in
00056  * each file stored in the file system.  By defining LWIP_HTTPD_DYNAMIC_HEADERS in
00057  * lwipopts.h, this behavior can be changed such that the server inserts the
00058  * headers automatically based on the extension of the file being served.  If
00059  * this mode is used, be careful to ensure that the file system image used
00060  * does not already contain the header information.
00061  *
00062  * File system images without headers can be created using the makefsfile
00063  * tool with the -h command line option.
00064  *
00065  *
00066  * Notes about valid SSI tags
00067  * --------------------------
00068  *
00069  * The following assumptions are made about tags used in SSI markers:
00070  *
00071  * 1. No tag may contain '-' or whitespace characters within the tag name.
00072  * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of
00073  *    the tag name and between the tag name and the leadout string "-->".
00074  * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters.
00075  *
00076  * Notes on CGI usage
00077  * ------------------
00078  *
00079  * The simple CGI support offered here works with GET method requests only
00080  * and can handle up to 16 parameters encoded into the URI. The handler
00081  * function may not write directly to the HTTP output but must return a
00082  * filename that the HTTP server will send to the browser as a response to
00083  * the incoming CGI request.
00084  *
00085  *
00086  *
00087  * The list of supported file types is quite short, so if makefsdata complains
00088  * about an unknown extension, make sure to add it (and its doctype) to
00089  * the 'g_psHTTPHeaders' list.
00090  */
00091 #include "lwip/init.h"
00092 #include "lwip/apps/httpd.h"
00093 #include "lwip/debug.h"
00094 #include "lwip/stats.h"
00095 #include "lwip/apps/fs.h"
00096 #include "httpd_structs.h"
00097 #include "lwip/def.h"
00098 #include "lwip/ip.h"
00099 #include "lwip/tcp.h"
00100 
00101 #include <string.h>
00102 #include <stdlib.h>
00103 #include <stdio.h>
00104 
00105 #if LWIP_TCP
00106 
00107 /** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */
00108 #define MIN_REQ_LEN   7
00109 
00110 #define CRLF "\r\n"
00111 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
00112 #define HTTP11_CONNECTIONKEEPALIVE  "Connection: keep-alive"
00113 #define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive"
00114 #endif
00115 
00116 /** These defines check whether tcp_write has to copy data or not */
00117 
00118 /** This was TI's check whether to let TCP copy data or not
00119  * \#define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY)
00120  */
00121 #ifndef HTTP_IS_DATA_VOLATILE
00122 #if LWIP_HTTPD_SSI
00123 /* Copy for SSI files, no copy for non-SSI files */
00124 #define HTTP_IS_DATA_VOLATILE(hs)   ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0)
00125 #else /* LWIP_HTTPD_SSI */
00126 /** Default: don't copy if the data is sent from file-system directly */
00127 #define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \
00128                                    (const char*)hs->handle->data + hs->handle->len - hs->left)) \
00129                                    ? 0 : TCP_WRITE_FLAG_COPY)
00130 #endif /* LWIP_HTTPD_SSI */
00131 #endif
00132 
00133 /** Default: headers are sent from ROM */
00134 #ifndef HTTP_IS_HDR_VOLATILE
00135 #define HTTP_IS_HDR_VOLATILE(hs, ptr) 0
00136 #endif
00137 
00138 /* Return values for http_send_*() */
00139 #define HTTP_DATA_TO_SEND_BREAK    2
00140 #define HTTP_DATA_TO_SEND_CONTINUE 1
00141 #define HTTP_NO_DATA_TO_SEND       0
00142 
00143 typedef struct
00144 {
00145   const char *name;
00146   u8_t shtml;
00147 } default_filename;
00148 
00149 const default_filename g_psDefaultFilenames[] = {
00150   {"/index.shtml", 1 },
00151   {"/index.ssi",   1 },
00152   {"/index.shtm",  1 },
00153   {"/index.html",  0 },
00154   {"/index.htm",   0 }
00155 };
00156 
00157 #define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) /   \
00158                                sizeof(default_filename))
00159 
00160 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
00161 /** HTTP request is copied here from pbufs for simple parsing */
00162 static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1];
00163 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
00164 
00165 #if LWIP_HTTPD_SUPPORT_POST
00166 #if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN
00167 #define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN
00168 #endif
00169 #endif
00170 #ifndef LWIP_HTTPD_URI_BUF_LEN
00171 #define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN
00172 #endif
00173 #if LWIP_HTTPD_URI_BUF_LEN
00174 /* Filename for response file to send when POST is finished or
00175  * search for default files when a directory is requested. */
00176 static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN+1];
00177 #endif
00178 
00179 #if LWIP_HTTPD_DYNAMIC_HEADERS
00180 /* The number of individual strings that comprise the headers sent before each
00181  * requested file.
00182  */
00183 #define NUM_FILE_HDR_STRINGS 5
00184 #define HDR_STRINGS_IDX_HTTP_STATUS          0 /* e.g. "HTTP/1.0 200 OK\r\n" */
00185 #define HDR_STRINGS_IDX_SERVER_NAME          1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */
00186 #define HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */
00187 #define HDR_STRINGS_IDX_CONTENT_LEN_NR       3 /* the byte count, when content-length is used */
00188 #define HDR_STRINGS_IDX_CONTENT_TYPE         4 /* the content type (or default answer content type including default document) */
00189 
00190 /* The dynamically generated Content-Length buffer needs space for CRLF + NULL */
00191 #define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3
00192 #ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE
00193 /* The dynamically generated Content-Length buffer shall be able to work with
00194    ~953 MB (9 digits) */
00195 #define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE   (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET)
00196 #endif
00197 #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
00198 
00199 #if LWIP_HTTPD_SSI
00200 
00201 #define HTTPD_LAST_TAG_PART 0xFFFF
00202 
00203 enum tag_check_state {
00204   TAG_NONE,       /* Not processing an SSI tag */
00205   TAG_LEADIN,     /* Tag lead in "<!--#" being processed */
00206   TAG_FOUND,      /* Tag name being read, looking for lead-out start */
00207   TAG_LEADOUT,    /* Tag lead out "-->" being processed */
00208   TAG_SENDING     /* Sending tag replacement string */
00209 };
00210 
00211 struct http_ssi_state {
00212   const char *parsed;     /* Pointer to the first unparsed byte in buf. */
00213 #if !LWIP_HTTPD_SSI_INCLUDE_TAG
00214   const char *tag_started;/* Pointer to the first opening '<' of the tag. */
00215 #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
00216   const char *tag_end;    /* Pointer to char after the closing '>' of the tag. */
00217   u32_t parse_left; /* Number of unparsed bytes in buf. */
00218   u16_t tag_index;   /* Counter used by tag parsing state machine */
00219   u16_t tag_insert_len; /* Length of insert in string tag_insert */
00220 #if LWIP_HTTPD_SSI_MULTIPART
00221   u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
00222 #endif /* LWIP_HTTPD_SSI_MULTIPART */
00223   u8_t tag_name_len; /* Length of the tag name in string tag_name */
00224   char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
00225   char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
00226   enum tag_check_state tag_state; /* State of the tag processor */
00227 };
00228 #endif /* LWIP_HTTPD_SSI */
00229 
00230 struct http_state {
00231 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00232   struct http_state *next;
00233 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00234   struct fs_file file_handle;
00235   struct fs_file *handle;
00236   const char *file;       /* Pointer to first unsent byte in buf. */
00237 
00238   struct tcp_pcb *pcb;
00239 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
00240   struct pbuf *req;
00241 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
00242 
00243 #if LWIP_HTTPD_DYNAMIC_FILE_READ
00244   char *buf;        /* File read buffer. */
00245   int buf_len;      /* Size of file read buffer, buf. */
00246 #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
00247   u32_t left;       /* Number of unsent bytes in buf. */
00248   u8_t retries;
00249 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
00250   u8_t keepalive;
00251 #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
00252 #if LWIP_HTTPD_SSI
00253   struct http_ssi_state *ssi;
00254 #endif /* LWIP_HTTPD_SSI */
00255 #if LWIP_HTTPD_CGI
00256   char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
00257   char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
00258 #endif /* LWIP_HTTPD_CGI */
00259 #if LWIP_HTTPD_DYNAMIC_HEADERS
00260   const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
00261   char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE];
00262   u16_t hdr_pos;     /* The position of the first unsent header byte in the
00263                         current string */
00264   u16_t hdr_index;   /* The index of the hdr string currently being sent. */
00265 #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
00266 #if LWIP_HTTPD_TIMING
00267   u32_t time_started;
00268 #endif /* LWIP_HTTPD_TIMING */
00269 #if LWIP_HTTPD_SUPPORT_POST
00270   u32_t post_content_len_left;
00271 #if LWIP_HTTPD_POST_MANUAL_WND
00272   u32_t unrecved_bytes;
00273   u8_t no_auto_wnd;
00274   u8_t post_finished;
00275 #endif /* LWIP_HTTPD_POST_MANUAL_WND */
00276 #endif /* LWIP_HTTPD_SUPPORT_POST*/
00277 };
00278 
00279 #if HTTPD_USE_MEM_POOL
00280 LWIP_MEMPOOL_DECLARE(HTTPD_STATE,     MEMP_NUM_PARALLEL_HTTPD_CONNS,     sizeof(struct http_state),     "HTTPD_STATE")
00281 #if LWIP_HTTPD_SSI
00282 LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE")
00283 #define HTTP_FREE_SSI_STATE(x)  LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x))
00284 #define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE)
00285 #endif /* LWIP_HTTPD_SSI */
00286 #define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE)
00287 #define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x))
00288 #else /* HTTPD_USE_MEM_POOL */
00289 #define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state))
00290 #define HTTP_FREE_HTTP_STATE(x) mem_free(x)
00291 #if LWIP_HTTPD_SSI
00292 #define HTTP_ALLOC_SSI_STATE()  (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state))
00293 #define HTTP_FREE_SSI_STATE(x)  mem_free(x)
00294 #endif /* LWIP_HTTPD_SSI */
00295 #endif /* HTTPD_USE_MEM_POOL */
00296 
00297 static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs);
00298 static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn);
00299 static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
00300 static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check, char* params);
00301 static err_t http_poll(void *arg, struct tcp_pcb *pcb);
00302 static u8_t http_check_eof(struct tcp_pcb *pcb, struct http_state *hs);
00303 #if LWIP_HTTPD_FS_ASYNC_READ
00304 static void http_continue(void *connection);
00305 #endif /* LWIP_HTTPD_FS_ASYNC_READ */
00306 
00307 #if LWIP_HTTPD_SSI
00308 /* SSI insert handler function pointer. */
00309 tSSIHandler g_pfnSSIHandler;
00310 #if !LWIP_HTTPD_SSI_RAW
00311 int g_iNumTags;
00312 const char **g_ppcTags;
00313 #endif /* !LWIP_HTTPD_SSI_RAW */
00314 
00315 #define LEN_TAG_LEAD_IN 5
00316 const char * const g_pcTagLeadIn = "<!--#";
00317 
00318 #define LEN_TAG_LEAD_OUT 3
00319 const char * const g_pcTagLeadOut = "-->";
00320 #endif /* LWIP_HTTPD_SSI */
00321 
00322 #if LWIP_HTTPD_CGI
00323 /* CGI handler information */
00324 const tCGI *g_pCGIs;
00325 int g_iNumCGIs;
00326 int http_cgi_paramcount;
00327 #define http_cgi_params     hs->params
00328 #define http_cgi_param_vals hs->param_vals
00329 #elif LWIP_HTTPD_CGI_SSI
00330 char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
00331 char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
00332 #endif /* LWIP_HTTPD_CGI */
00333 
00334 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00335 /** global list of active HTTP connections, use to kill the oldest when
00336     running out of memory */
00337 static struct http_state *http_connections;
00338 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00339 
00340 #if LWIP_HTTPD_STRNSTR_PRIVATE
00341 /** Like strstr but does not need 'buffer' to be NULL-terminated */
00342 static char*
00343 strnstr(const char* buffer, const char* token, size_t n)
00344 {
00345   const char* p;
00346   int tokenlen = (int)strlen(token);
00347   if (tokenlen == 0) {
00348     return (char *)(size_t)buffer;
00349   }
00350   for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) {
00351     if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) {
00352       return (char *)(size_t)p;
00353     }
00354   }
00355   return NULL;
00356 } 
00357 #endif /* LWIP_HTTPD_STRNSTR_PRIVATE */
00358 
00359 #if LWIP_HTTPD_STRICMP_PRIVATE
00360 static int
00361 stricmp(const char* str1, const char* str2)
00362 {
00363   char c1, c2;
00364 
00365   do {
00366     c1 = *str1++;
00367     c2 = *str2++;
00368     if (c1 != c2) {
00369       char c1_upc = c1 | 0x20;
00370       if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
00371         /* characters are not equal an one is in the alphabet range:
00372         downcase both chars and check again */
00373         char c2_upc = c2 | 0x20;
00374         if (c1_upc != c2_upc) {
00375           /* still not equal */
00376           /* don't care for < or > */
00377           return 1;
00378         }
00379       } else {
00380         /* characters are not equal but none is in the alphabet range */
00381         return 1;
00382       }
00383     }
00384   } while (c1 != 0);
00385   return 0;
00386 }
00387 #endif /* LWIP_HTTPD_STRICMP_PRIVATE */
00388 
00389 #if LWIP_HTTPD_ITOA_PRIVATE && LWIP_HTTPD_DYNAMIC_HEADERS
00390 static void
00391 httpd_itoa(int value, char* result)
00392 {
00393   const int base = 10;
00394   char* ptr = result, *ptr1 = result, tmp_char;
00395   int tmp_value;
00396 
00397   do {
00398     tmp_value = value;
00399     value /= base;
00400     *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)];
00401   } while(value);
00402 
00403    /* Apply negative sign */
00404   if (tmp_value < 0) {
00405      *ptr++ = '-';
00406   }
00407   *ptr-- = '\0';
00408   while(ptr1 < ptr) {
00409     tmp_char = *ptr;
00410     *ptr--= *ptr1;
00411     *ptr1++ = tmp_char;
00412   }
00413 }
00414 #endif
00415 
00416 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00417 static void
00418 http_kill_oldest_connection(u8_t ssi_required)
00419 {
00420   struct http_state *hs = http_connections;
00421   struct http_state *hs_free_next = NULL;
00422   while(hs && hs->next) {
00423 #if LWIP_HTTPD_SSI
00424     if (ssi_required) {
00425       if (hs->next->ssi != NULL) {
00426         hs_free_next = hs;
00427       }
00428     } else
00429 #else /* LWIP_HTTPD_SSI */
00430     LWIP_UNUSED_ARG(ssi_required);
00431 #endif /* LWIP_HTTPD_SSI */
00432     {
00433       hs_free_next = hs;
00434     }
00435     LWIP_ASSERT("broken list", hs != hs->next);
00436     hs = hs->next;
00437   }
00438   if (hs_free_next != NULL) {
00439     LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL);
00440     LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL);
00441     /* send RST when killing a connection because of memory shortage */
00442     http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */
00443   }
00444 }
00445 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00446 
00447 #if LWIP_HTTPD_SSI
00448 /** Allocate as struct http_ssi_state. */
00449 static struct http_ssi_state*
00450 http_ssi_state_alloc(void)
00451 {
00452   struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE();
00453 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00454   if (ret == NULL) {
00455     http_kill_oldest_connection(1);
00456     ret = HTTP_ALLOC_SSI_STATE();
00457   }
00458 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00459   if (ret != NULL) {
00460     memset(ret, 0, sizeof(struct http_ssi_state));
00461   }
00462   return ret;
00463 }
00464 
00465 /** Free a struct http_ssi_state. */
00466 static void
00467 http_ssi_state_free(struct http_ssi_state *ssi)
00468 {
00469   if (ssi != NULL) {
00470     HTTP_FREE_SSI_STATE(ssi);
00471   }
00472 }
00473 #endif /* LWIP_HTTPD_SSI */
00474 
00475 /** Initialize a struct http_state.
00476  */
00477 static void
00478 http_state_init(struct http_state* hs)
00479 {
00480   /* Initialize the structure. */
00481   memset(hs, 0, sizeof(struct http_state));
00482 #if LWIP_HTTPD_DYNAMIC_HEADERS
00483   /* Indicate that the headers are not yet valid */
00484   hs->hdr_index = NUM_FILE_HDR_STRINGS;
00485 #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
00486 }
00487 
00488 /** Allocate a struct http_state. */
00489 static struct http_state*
00490 http_state_alloc(void)
00491 {
00492   struct http_state *ret = HTTP_ALLOC_HTTP_STATE();
00493 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00494   if (ret == NULL) {
00495     http_kill_oldest_connection(0);
00496     ret = HTTP_ALLOC_HTTP_STATE();
00497   }
00498 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00499   if (ret != NULL) {
00500     http_state_init(ret);
00501 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00502     /* add the connection to the list */
00503     if (http_connections == NULL) {
00504       http_connections = ret;
00505     } else {
00506       struct http_state *last;
00507       for(last = http_connections; last->next != NULL; last = last->next);
00508       LWIP_ASSERT("last != NULL", last != NULL);
00509       last->next = ret;
00510     }
00511 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00512   }
00513   return ret;
00514 }
00515 
00516 /** Free a struct http_state.
00517  * Also frees the file data if dynamic.
00518  */
00519 static void
00520 http_state_eof(struct http_state *hs)
00521 {
00522   if(hs->handle) {
00523 #if LWIP_HTTPD_TIMING
00524     u32_t ms_needed = sys_now() - hs->time_started;
00525     u32_t needed = LWIP_MAX(1, (ms_needed/100));
00526     LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n",
00527       ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed)));
00528 #endif /* LWIP_HTTPD_TIMING */
00529     fs_close(hs->handle);
00530     hs->handle = NULL;
00531   }
00532 #if LWIP_HTTPD_DYNAMIC_FILE_READ
00533   if (hs->buf != NULL) {
00534     mem_free(hs->buf);
00535     hs->buf = NULL;
00536   }
00537 #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
00538 #if LWIP_HTTPD_SSI
00539   if (hs->ssi) {
00540     http_ssi_state_free(hs->ssi);
00541     hs->ssi = NULL;
00542   }
00543 #endif /* LWIP_HTTPD_SSI */
00544 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
00545   if (hs->req) {
00546     pbuf_free(hs->req);
00547     hs->req = NULL;
00548   }
00549 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
00550 }
00551 
00552 /** Free a struct http_state.
00553  * Also frees the file data if dynamic.
00554  */
00555 static void
00556 http_state_free(struct http_state *hs)
00557 {
00558   if (hs != NULL) {
00559     http_state_eof(hs);
00560 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00561     /* take the connection off the list */
00562     if (http_connections) {
00563       if (http_connections == hs) {
00564         http_connections = hs->next;
00565       } else {
00566         struct http_state *last;
00567         for(last = http_connections; last->next != NULL; last = last->next) {
00568           if (last->next == hs) {
00569             last->next = hs->next;
00570             break;
00571           }
00572         }
00573       }
00574     }
00575 #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */
00576     HTTP_FREE_HTTP_STATE(hs);
00577   }
00578 }
00579 
00580 /** Call tcp_write() in a loop trying smaller and smaller length
00581  *
00582  * @param pcb tcp_pcb to send
00583  * @param ptr Data to send
00584  * @param length Length of data to send (in/out: on return, contains the
00585  *        amount of data sent)
00586  * @param apiflags directly passed to tcp_write
00587  * @return the return value of tcp_write
00588  */
00589 static err_t
00590 http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags)
00591 {
00592   u16_t len, max_len;
00593   err_t err;
00594   LWIP_ASSERT("length != NULL", length != NULL);
00595   len = *length;
00596   if (len == 0) {
00597     return ERR_OK;
00598   }
00599   /* We cannot send more data than space available in the send buffer. */
00600   max_len = tcp_sndbuf(pcb);
00601   if (max_len < len) {
00602     len = max_len;
00603   }
00604 #ifdef HTTPD_MAX_WRITE_LEN
00605   /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
00606   max_len = HTTPD_MAX_WRITE_LEN(pcb);
00607   if(len > max_len) {
00608     len = max_len;
00609   }
00610 #endif /* HTTPD_MAX_WRITE_LEN */
00611   do {
00612     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len));
00613     err = tcp_write(pcb, ptr, len, apiflags);
00614     if (err == ERR_MEM) {
00615       if ((tcp_sndbuf(pcb) == 0) ||
00616         (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) {
00617           /* no need to try smaller sizes */
00618           len = 1;
00619       } else {
00620         len /= 2;
00621       }
00622       LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, 
00623         ("Send failed, trying less (%d bytes)\n", len));
00624     }
00625   } while ((err == ERR_MEM) && (len > 1));
00626 
00627   if (err == ERR_OK) {
00628     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len));
00629     *length = len;
00630   } else {
00631     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err)));
00632     *length = 0;
00633   }
00634 
00635 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
00636    /* ensure nagle is normally enabled (only disabled for persistent connections
00637       when all data has been enqueued but the connection stays open for the next
00638       request */
00639    tcp_nagle_enable(pcb);
00640 #endif
00641 
00642   return err;
00643 }
00644 
00645 /**
00646  * The connection shall be actively closed (using RST to close from fault states).
00647  * Reset the sent- and recv-callbacks.
00648  *
00649  * @param pcb the tcp pcb to reset callbacks
00650  * @param hs connection state to free
00651  */
00652 static err_t
00653 http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn)
00654 {
00655   err_t err;
00656   LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb));
00657 
00658 #if LWIP_HTTPD_SUPPORT_POST
00659   if (hs != NULL) {
00660     if ((hs->post_content_len_left != 0)
00661 #if LWIP_HTTPD_POST_MANUAL_WND
00662        || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0))
00663 #endif /* LWIP_HTTPD_POST_MANUAL_WND */
00664        ) {
00665       /* make sure the post code knows that the connection is closed */
00666       http_uri_buf[0] = 0;
00667       httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
00668     }
00669   }
00670 #endif /* LWIP_HTTPD_SUPPORT_POST*/
00671 
00672 
00673   tcp_arg(pcb, NULL);
00674   tcp_recv(pcb, NULL);
00675   tcp_err(pcb, NULL);
00676   tcp_poll(pcb, NULL, 0);
00677   tcp_sent(pcb, NULL);
00678   if (hs != NULL) {
00679     http_state_free(hs);
00680   }
00681 
00682   if (abort_conn) {
00683     tcp_abort(pcb);
00684     return ERR_OK;
00685   }
00686   err = tcp_close(pcb);
00687   if (err != ERR_OK) {
00688     LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb));
00689     /* error closing, try again later in poll */
00690     tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
00691   }
00692   return err;
00693 }
00694 
00695 /**
00696  * The connection shall be actively closed.
00697  * Reset the sent- and recv-callbacks.
00698  *
00699  * @param pcb the tcp pcb to reset callbacks
00700  * @param hs connection state to free
00701  */
00702 static err_t
00703 http_close_conn(struct tcp_pcb *pcb, struct http_state *hs)
00704 {
00705    return http_close_or_abort_conn(pcb, hs, 0);
00706 }
00707 
00708 /** End of file: either close the connection (Connection: close) or
00709  * close the file (Connection: keep-alive)
00710  */
00711 static void
00712 http_eof(struct tcp_pcb *pcb, struct http_state *hs)
00713 {
00714   /* HTTP/1.1 persistent connection? (Not supported for SSI) */
00715 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
00716   if (hs->keepalive) {
00717 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00718     struct http_state* next = hs->next;
00719 #endif
00720     http_state_eof(hs);
00721     http_state_init(hs);
00722     /* restore state: */
00723 #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED
00724     hs->next = next;
00725 #endif
00726     hs->pcb = pcb;
00727     hs->keepalive = 1;
00728     /* ensure nagle doesn't interfere with sending all data as fast as possible: */
00729     tcp_nagle_disable(pcb);
00730   } else
00731 #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
00732   {
00733     http_close_conn(pcb, hs);
00734   }
00735 }
00736 
00737 #if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI
00738 /**
00739  * Extract URI parameters from the parameter-part of an URI in the form
00740  * "test.cgi?x=y" @todo: better explanation!
00741  * Pointers to the parameters are stored in hs->param_vals.
00742  *
00743  * @param hs http connection state
00744  * @param params pointer to the NULL-terminated parameter string from the URI
00745  * @return number of parameters extracted
00746  */
00747 static int
00748 extract_uri_parameters(struct http_state *hs, char *params)
00749 {
00750   char *pair;
00751   char *equals;
00752   int loop;
00753 
00754   LWIP_UNUSED_ARG(hs);
00755 
00756   /* If we have no parameters at all, return immediately. */
00757   if(!params || (params[0] == '\0')) {
00758       return(0);
00759   }
00760 
00761   /* Get a pointer to our first parameter */
00762   pair = params;
00763 
00764   /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the
00765    * remainder (if any) */
00766   for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) {
00767 
00768     /* Save the name of the parameter */
00769     http_cgi_params[loop] = pair;
00770 
00771     /* Remember the start of this name=value pair */
00772     equals = pair;
00773 
00774     /* Find the start of the next name=value pair and replace the delimiter
00775      * with a 0 to terminate the previous pair string. */
00776     pair = strchr(pair, '&');
00777     if(pair) {
00778       *pair = '\0';
00779       pair++;
00780     } else {
00781        /* We didn't find a new parameter so find the end of the URI and
00782         * replace the space with a '\0' */
00783         pair = strchr(equals, ' ');
00784         if(pair) {
00785             *pair = '\0';
00786         }
00787 
00788         /* Revert to NULL so that we exit the loop as expected. */
00789         pair = NULL;
00790     }
00791 
00792     /* Now find the '=' in the previous pair, replace it with '\0' and save
00793      * the parameter value string. */
00794     equals = strchr(equals, '=');
00795     if(equals) {
00796       *equals = '\0';
00797       http_cgi_param_vals[loop] = equals + 1;
00798     } else {
00799       http_cgi_param_vals[loop] = NULL;
00800     }
00801   }
00802 
00803   return loop;
00804 }
00805 #endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */
00806 
00807 #if LWIP_HTTPD_SSI
00808 /**
00809  * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file.
00810  * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement
00811  * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN).
00812  * The amount of data written is stored to ssi->tag_insert_len.
00813  *
00814  * @todo: return tag_insert_len - maybe it can be removed from struct http_state?
00815  *
00816  * @param hs http connection state
00817  */
00818 static void
00819 get_tag_insert(struct http_state *hs)
00820 {
00821 #if LWIP_HTTPD_SSI_RAW
00822   const char* tag;
00823 #else /* LWIP_HTTPD_SSI_RAW */
00824   int tag;
00825 #endif /* LWIP_HTTPD_SSI_RAW */
00826   size_t len;
00827   struct http_ssi_state *ssi;
00828 #if LWIP_HTTPD_SSI_MULTIPART
00829   u16_t current_tag_part;
00830 #endif /* LWIP_HTTPD_SSI_MULTIPART */
00831 
00832   LWIP_ASSERT("hs != NULL", hs != NULL);
00833   ssi = hs->ssi;
00834   LWIP_ASSERT("ssi != NULL", ssi != NULL);
00835 #if LWIP_HTTPD_SSI_MULTIPART
00836   current_tag_part = ssi->tag_part;
00837   ssi->tag_part = HTTPD_LAST_TAG_PART;
00838 #endif /* LWIP_HTTPD_SSI_MULTIPART */
00839 #if LWIP_HTTPD_SSI_RAW
00840   tag = ssi->tag_name;
00841 #endif
00842 
00843   if(g_pfnSSIHandler
00844 #if !LWIP_HTTPD_SSI_RAW
00845      && g_ppcTags && g_iNumTags
00846 #endif /* !LWIP_HTTPD_SSI_RAW */
00847      ) {
00848 
00849     /* Find this tag in the list we have been provided. */
00850 #if LWIP_HTTPD_SSI_RAW
00851     {
00852 #else /* LWIP_HTTPD_SSI_RAW */
00853     for(tag = 0; tag < g_iNumTags; tag++) {
00854       if(strcmp(ssi->tag_name, g_ppcTags[tag]) == 0)
00855 #endif /* LWIP_HTTPD_SSI_RAW */
00856       {
00857         ssi->tag_insert_len = g_pfnSSIHandler(tag, ssi->tag_insert,
00858            LWIP_HTTPD_MAX_TAG_INSERT_LEN
00859 #if LWIP_HTTPD_SSI_MULTIPART
00860            , current_tag_part, &ssi->tag_part
00861 #endif /* LWIP_HTTPD_SSI_MULTIPART */
00862 #if LWIP_HTTPD_FILE_STATE
00863            , (hs->handle ? hs->handle->state : NULL)
00864 #endif /* LWIP_HTTPD_FILE_STATE */
00865            );
00866 #if LWIP_HTTPD_SSI_RAW
00867         if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN)
00868 #endif /* LWIP_HTTPD_SSI_RAW */
00869         {
00870           return;
00871         }
00872       }
00873     }
00874   }
00875 
00876   /* If we drop out, we were asked to serve a page which contains tags that
00877    * we don't have a handler for. Merely echo back the tags with an error
00878    * marker. */
00879 #define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG "
00880 #define UNKNOWN_TAG1_LEN  18
00881 #define UNKNOWN_TAG2_TEXT "***</b>"
00882 #define UNKNOWN_TAG2_LEN  7
00883   len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name),
00884     LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)));
00885   MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN);
00886   MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len);
00887   MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN);
00888   ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0;
00889 
00890   len = strlen(ssi->tag_insert);
00891   LWIP_ASSERT("len <= 0xffff", len <= 0xffff);
00892   ssi->tag_insert_len = (u16_t)len;
00893 }
00894 #endif /* LWIP_HTTPD_SSI */
00895 
00896 #if LWIP_HTTPD_DYNAMIC_HEADERS
00897 /**
00898  * Generate the relevant HTTP headers for the given filename and write
00899  * them into the supplied buffer.
00900  */
00901 static void
00902 get_http_headers(struct http_state *hs, const char *uri)
00903 {
00904   size_t content_type;
00905   char *tmp;
00906   char *ext;
00907   char *vars;
00908   u8_t add_content_len;
00909 
00910   /* In all cases, the second header we send is the server identification
00911      so set it here. */
00912   hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
00913   hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = NULL;
00914   hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL;
00915 
00916   /* Is this a normal file or the special case we use to send back the
00917      default "404: Page not found" response? */
00918   if (uri == NULL) {
00919     hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
00920 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
00921     if (hs->keepalive) {
00922       hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT];
00923     } else
00924 #endif
00925     {
00926       hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
00927     }
00928 
00929     /* Set up to send the first header string. */
00930     hs->hdr_index = 0;
00931     hs->hdr_pos = 0;
00932     return;
00933   }
00934   /* We are dealing with a particular filename. Look for one other
00935       special case.  We assume that any filename with "404" in it must be
00936       indicative of a 404 server error whereas all other files require
00937       the 200 OK header. */
00938   if (strstr(uri, "404")) {
00939     hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
00940   } else if (strstr(uri, "400")) {
00941     hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST];
00942   } else if (strstr(uri, "501")) {
00943     hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL];
00944   } else {
00945     hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
00946   }
00947 
00948   /* Determine if the URI has any variables and, if so, temporarily remove 
00949       them. */
00950   vars = strchr(uri, '?');
00951   if(vars) {
00952     *vars = '\0';
00953   }
00954 
00955   /* Get a pointer to the file extension.  We find this by looking for the
00956       last occurrence of "." in the filename passed. */
00957   ext = NULL;
00958   tmp = strchr(uri, '.');
00959   while (tmp) {
00960     ext = tmp + 1;
00961     tmp = strchr(ext, '.');
00962   }
00963   if (ext != NULL) {
00964     /* Now determine the content type and add the relevant header for that. */
00965     for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) {
00966       /* Have we found a matching extension? */
00967       if(!stricmp(g_psHTTPHeaders[content_type].extension, ext)) {
00968         break;
00969       }
00970     }
00971   } else {
00972     content_type = NUM_HTTP_HEADERS;
00973   }
00974 
00975   /* Reinstate the parameter marker if there was one in the original URI. */
00976   if (vars) {
00977     *vars = '?';
00978   }
00979 
00980 #if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI
00981   /* Does the URL passed have any file extension?  If not, we assume it
00982      is a special-case URL used for control state notification and we do
00983      not send any HTTP headers with the response. */
00984   if (!ext) {
00985     /* Force the header index to a value indicating that all headers
00986        have already been sent. */
00987     hs->hdr_index = NUM_FILE_HDR_STRINGS;
00988     return;
00989   }
00990 #endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */
00991   add_content_len = 1;
00992   /* Did we find a matching extension? */
00993   if(content_type < NUM_HTTP_HEADERS) {
00994     /* yes, store it */
00995     hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type;
00996   } else if (!ext) {
00997     /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */
00998     hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP;
00999   } else {
01000     /* No - use the default, plain text file type. */
01001     hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE;
01002   }
01003   /* Add content-length header? */
01004 #if LWIP_HTTPD_SSI
01005   if (hs->ssi != NULL) {
01006     add_content_len = 0; /* @todo: get maximum file length from SSI */
01007   } else
01008 #endif /* LWIP_HTTPD_SSI */
01009   if ((hs->handle == NULL) ||
01010       ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
01011     add_content_len = 0;
01012   }
01013   if (add_content_len) {
01014     size_t len;
01015     LWIP_HTTPD_ITOA(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE,
01016       hs->handle->len);
01017     len = strlen(hs->hdr_content_len);
01018     if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) {
01019       SMEMCPY(&hs->hdr_content_len[len], CRLF "\0", 3);
01020       hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len;
01021     } else {
01022       add_content_len = 0;
01023     }
01024   }
01025 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
01026   if (add_content_len) {
01027     hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN];
01028   } else {
01029     hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE];
01030   }
01031 #else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
01032   if (add_content_len) {
01033     hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH];
01034   }
01035 #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
01036 
01037   /* Set up to send the first header string. */
01038   hs->hdr_index = 0;
01039   hs->hdr_pos = 0;
01040 }
01041 
01042 /** Sub-function of http_send(): send dynamic headers
01043  *
01044  * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued
01045  *           - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body
01046  *           - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending,
01047  *                                      so don't send HTTP body yet
01048  */
01049 static u8_t
01050 http_send_headers(struct tcp_pcb *pcb, struct http_state *hs)
01051 {
01052   err_t err;
01053   u16_t len;
01054   u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
01055   u16_t hdrlen, sendlen;
01056 
01057   /* How much data can we send? */
01058   len = tcp_sndbuf(pcb);
01059   sendlen = len;
01060 
01061   while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) {
01062     const void *ptr;
01063     u16_t old_sendlen;
01064     u8_t apiflags;
01065     /* How much do we have to send from the current header? */
01066     hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]);
01067 
01068     /* How much of this can we send? */
01069     sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos);
01070 
01071     /* Send this amount of data or as much as we can given memory
01072      * constraints. */
01073     ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos);
01074     old_sendlen = sendlen;
01075     apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr);
01076     if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) {
01077       /* content-length is always volatile */
01078       apiflags |= TCP_WRITE_FLAG_COPY;
01079     }
01080     if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) {
01081       apiflags |= TCP_WRITE_FLAG_MORE;
01082     }
01083     err = http_write(pcb, ptr, &sendlen, apiflags);
01084     if ((err == ERR_OK) && (old_sendlen != sendlen)) {
01085       /* Remember that we added some more data to be transmitted. */
01086       data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
01087     } else if (err != ERR_OK) {
01088        /* special case: http_write does not try to send 1 byte */
01089       sendlen = 0;
01090     }
01091 
01092     /* Fix up the header position for the next time round. */
01093     hs->hdr_pos += sendlen;
01094     len -= sendlen;
01095 
01096     /* Have we finished sending this string? */
01097     if(hs->hdr_pos == hdrlen) {
01098       /* Yes - move on to the next one */
01099       hs->hdr_index++;
01100       /* skip headers that are NULL (not all headers are required) */
01101       while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) &&
01102          (hs->hdrs[hs->hdr_index] == NULL)) {
01103         hs->hdr_index++;
01104       }
01105       hs->hdr_pos = 0;
01106     }
01107   }
01108 
01109   if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) {
01110     /* When we are at the end of the headers, check for data to send
01111      * instead of waiting for ACK from remote side to continue
01112      * (which would happen when sending files from async read). */
01113     if(http_check_eof(pcb, hs)) {
01114       data_to_send = HTTP_DATA_TO_SEND_CONTINUE;
01115     } 
01116   }
01117   /* If we get here and there are still header bytes to send, we send
01118    * the header information we just wrote immediately. If there are no
01119    * more headers to send, but we do have file data to send, drop through
01120    * to try to send some file data too. */
01121   if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) {
01122     LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n"));
01123     return HTTP_DATA_TO_SEND_BREAK;
01124   }
01125   return data_to_send;
01126 }
01127 #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
01128 
01129 /** Sub-function of http_send(): end-of-file (or block) is reached,
01130  * either close the file or read the next block (if supported).
01131  *
01132  * @returns: 0 if the file is finished or no data has been read
01133  *           1 if the file is not finished and data has been read
01134  */
01135 static u8_t
01136 http_check_eof(struct tcp_pcb *pcb, struct http_state *hs)
01137 {
01138   int bytes_left;
01139 #if LWIP_HTTPD_DYNAMIC_FILE_READ
01140   int count;
01141 #ifdef HTTPD_MAX_WRITE_LEN
01142   int max_write_len;
01143 #endif /* HTTPD_MAX_WRITE_LEN */
01144 #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */
01145 
01146   /* Do we have a valid file handle? */
01147   if (hs->handle == NULL) {
01148     /* No - close the connection. */
01149     http_eof(pcb, hs);
01150     return 0;
01151   }
01152   bytes_left = fs_bytes_left(hs->handle);
01153   if (bytes_left <= 0) {
01154     /* We reached the end of the file so this request is done. */
01155     LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
01156     http_eof(pcb, hs);
01157     return 0;
01158   }
01159 #if LWIP_HTTPD_DYNAMIC_FILE_READ
01160   /* Do we already have a send buffer allocated? */
01161   if(hs->buf) {
01162     /* Yes - get the length of the buffer */
01163     count = LWIP_MIN(hs->buf_len, bytes_left);
01164   } else {
01165     /* We don't have a send buffer so allocate one now */
01166     count = tcp_sndbuf(pcb);
01167     if(bytes_left < count) {
01168       count = bytes_left;
01169     }
01170 #ifdef HTTPD_MAX_WRITE_LEN
01171     /* Additional limitation: e.g. don't enqueue more than 2*mss at once */
01172     max_write_len = HTTPD_MAX_WRITE_LEN(pcb);
01173     if (count > max_write_len) {
01174       count = max_write_len;
01175     }
01176 #endif /* HTTPD_MAX_WRITE_LEN */
01177     do {
01178       hs->buf = (char*)mem_malloc((mem_size_t)count);
01179       if (hs->buf != NULL) {
01180         hs->buf_len = count;
01181         break;
01182       }
01183       count = count / 2;
01184     } while (count > 100);
01185 
01186     /* Did we get a send buffer? If not, return immediately. */
01187     if (hs->buf == NULL) {
01188       LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n"));
01189       return 0;
01190     }
01191   }
01192 
01193   /* Read a block of data from the file. */
01194   LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count));
01195 
01196 #if LWIP_HTTPD_FS_ASYNC_READ
01197   count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs);
01198 #else /* LWIP_HTTPD_FS_ASYNC_READ */
01199   count = fs_read(hs->handle, hs->buf, count);
01200 #endif /* LWIP_HTTPD_FS_ASYNC_READ */
01201   if (count < 0) {
01202     if (count == FS_READ_DELAYED) {
01203       /* Delayed read, wait for FS to unblock us */
01204       return 0;
01205     }
01206     /* We reached the end of the file so this request is done.
01207      * @todo: close here for HTTP/1.1 when reading file fails */
01208     LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
01209     http_eof(pcb, hs);
01210     return 0;
01211   }
01212 
01213   /* Set up to send the block of data we just read */
01214   LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count));
01215   hs->left = count;
01216   hs->file = hs->buf;
01217 #if LWIP_HTTPD_SSI
01218   if (hs->ssi) {
01219     hs->ssi->parse_left = count;
01220     hs->ssi->parsed = hs->buf;
01221   }
01222 #endif /* LWIP_HTTPD_SSI */
01223 #else /* LWIP_HTTPD_DYNAMIC_FILE_READ */
01224   LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0);
01225 #endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
01226   return 1;
01227 }
01228 
01229 /** Sub-function of http_send(): This is the normal send-routine for non-ssi files
01230  *
01231  * @returns: - 1: data has been written (so call tcp_ouput)
01232  *           - 0: no data has been written (no need to call tcp_output)
01233  */
01234 static u8_t
01235 http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs)
01236 {
01237   err_t err;
01238   u16_t len;
01239   u8_t data_to_send = 0;
01240 
01241   /* We are not processing an SHTML file so no tag checking is necessary.
01242    * Just send the data as we received it from the file. */
01243   len = (u16_t)LWIP_MIN(hs->left, 0xffff);
01244 
01245   err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
01246   if (err == ERR_OK) {
01247     data_to_send = 1;
01248     hs->file += len;
01249     hs->left -= len;
01250   }
01251 
01252   return data_to_send;
01253 }
01254 
01255 #if LWIP_HTTPD_SSI
01256 /** Sub-function of http_send(): This is the send-routine for ssi files
01257  *
01258  * @returns: - 1: data has been written (so call tcp_ouput)
01259  *           - 0: no data has been written (no need to call tcp_output)
01260  */
01261 static u8_t
01262 http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs)
01263 {
01264   err_t err = ERR_OK;
01265   u16_t len;
01266   u8_t data_to_send = 0;
01267 
01268   struct http_ssi_state *ssi = hs->ssi;
01269   LWIP_ASSERT("ssi != NULL", ssi != NULL);
01270   /* We are processing an SHTML file so need to scan for tags and replace
01271    * them with insert strings. We need to be careful here since a tag may
01272    * straddle the boundary of two blocks read from the file and we may also
01273    * have to split the insert string between two tcp_write operations. */
01274 
01275   /* How much data could we send? */
01276   len = tcp_sndbuf(pcb);
01277 
01278   /* Do we have remaining data to send before parsing more? */
01279   if(ssi->parsed > hs->file) {
01280     len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
01281 
01282     err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
01283     if (err == ERR_OK) {
01284       data_to_send = 1;
01285       hs->file += len;
01286       hs->left -= len;
01287     }
01288 
01289     /* If the send buffer is full, return now. */
01290     if(tcp_sndbuf(pcb) == 0) {
01291       return data_to_send;
01292     }
01293   }
01294 
01295   LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left));
01296 
01297   /* We have sent all the data that was already parsed so continue parsing
01298    * the buffer contents looking for SSI tags. */
01299   while((ssi->parse_left) && (err == ERR_OK)) {
01300     if (len == 0) {
01301       return data_to_send;
01302     }
01303     switch(ssi->tag_state) {
01304       case TAG_NONE:
01305         /* We are not currently processing an SSI tag so scan for the
01306          * start of the lead-in marker. */
01307         if(*ssi->parsed == g_pcTagLeadIn[0]) {
01308           /* We found what could be the lead-in for a new tag so change
01309            * state appropriately. */
01310           ssi->tag_state = TAG_LEADIN;
01311           ssi->tag_index = 1;
01312 #if !LWIP_HTTPD_SSI_INCLUDE_TAG
01313           ssi->tag_started = ssi->parsed;
01314 #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
01315         }
01316 
01317         /* Move on to the next character in the buffer */
01318         ssi->parse_left--;
01319         ssi->parsed++;
01320         break;
01321 
01322       case TAG_LEADIN:
01323         /* We are processing the lead-in marker, looking for the start of
01324          * the tag name. */
01325 
01326         /* Have we reached the end of the leadin? */
01327         if(ssi->tag_index == LEN_TAG_LEAD_IN) {
01328           ssi->tag_index = 0;
01329           ssi->tag_state = TAG_FOUND;
01330         } else {
01331           /* Have we found the next character we expect for the tag leadin? */
01332           if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) {
01333             /* Yes - move to the next one unless we have found the complete
01334              * leadin, in which case we start looking for the tag itself */
01335             ssi->tag_index++;
01336           } else {
01337             /* We found an unexpected character so this is not a tag. Move
01338              * back to idle state. */
01339             ssi->tag_state = TAG_NONE;
01340           }
01341 
01342           /* Move on to the next character in the buffer */
01343           ssi->parse_left--;
01344           ssi->parsed++;
01345         }
01346         break;
01347 
01348       case TAG_FOUND:
01349         /* We are reading the tag name, looking for the start of the
01350          * lead-out marker and removing any whitespace found. */
01351 
01352         /* Remove leading whitespace between the tag leading and the first
01353          * tag name character. */
01354         if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
01355            (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
01356            (*ssi->parsed == '\r'))) {
01357           /* Move on to the next character in the buffer */
01358           ssi->parse_left--;
01359           ssi->parsed++;
01360           break;
01361         }
01362 
01363         /* Have we found the end of the tag name? This is signalled by
01364          * us finding the first leadout character or whitespace */
01365         if((*ssi->parsed == g_pcTagLeadOut[0]) ||
01366            (*ssi->parsed == ' ')  || (*ssi->parsed == '\t') ||
01367            (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) {
01368 
01369           if(ssi->tag_index == 0) {
01370             /* We read a zero length tag so ignore it. */
01371             ssi->tag_state = TAG_NONE;
01372           } else {
01373             /* We read a non-empty tag so go ahead and look for the
01374              * leadout string. */
01375             ssi->tag_state = TAG_LEADOUT;
01376             LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff);
01377             ssi->tag_name_len = (u8_t)ssi->tag_index;
01378             ssi->tag_name[ssi->tag_index] = '\0';
01379             if(*ssi->parsed == g_pcTagLeadOut[0]) {
01380               ssi->tag_index = 1;
01381             } else {
01382               ssi->tag_index = 0;
01383             }
01384           }
01385         } else {
01386           /* This character is part of the tag name so save it */
01387           if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) {
01388             ssi->tag_name[ssi->tag_index++] = *ssi->parsed;
01389           } else {
01390             /* The tag was too long so ignore it. */
01391             ssi->tag_state = TAG_NONE;
01392           }
01393         }
01394 
01395         /* Move on to the next character in the buffer */
01396         ssi->parse_left--;
01397         ssi->parsed++;
01398 
01399         break;
01400 
01401       /* We are looking for the end of the lead-out marker. */
01402       case TAG_LEADOUT:
01403         /* Remove leading whitespace between the tag leading and the first
01404          * tag leadout character. */
01405         if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') ||
01406            (*ssi->parsed == '\t') || (*ssi->parsed == '\n') ||
01407            (*ssi->parsed == '\r'))) {
01408           /* Move on to the next character in the buffer */
01409           ssi->parse_left--;
01410           ssi->parsed++;
01411           break;
01412         }
01413 
01414         /* Have we found the next character we expect for the tag leadout? */
01415         if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) {
01416           /* Yes - move to the next one unless we have found the complete
01417            * leadout, in which case we need to call the client to process
01418            * the tag. */
01419 
01420           /* Move on to the next character in the buffer */
01421           ssi->parse_left--;
01422           ssi->parsed++;
01423 
01424           if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) {
01425             /* Call the client to ask for the insert string for the
01426              * tag we just found. */
01427 #if LWIP_HTTPD_SSI_MULTIPART
01428             ssi->tag_part = 0; /* start with tag part 0 */
01429 #endif /* LWIP_HTTPD_SSI_MULTIPART */
01430             get_tag_insert(hs);
01431 
01432             /* Next time through, we are going to be sending data
01433              * immediately, either the end of the block we start
01434              * sending here or the insert string. */
01435             ssi->tag_index = 0;
01436             ssi->tag_state = TAG_SENDING;
01437             ssi->tag_end = ssi->parsed;
01438 #if !LWIP_HTTPD_SSI_INCLUDE_TAG
01439             ssi->parsed = ssi->tag_started;
01440 #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
01441 
01442             /* If there is any unsent data in the buffer prior to the
01443              * tag, we need to send it now. */
01444             if (ssi->tag_end > hs->file) {
01445               /* How much of the data can we send? */
01446 #if LWIP_HTTPD_SSI_INCLUDE_TAG
01447               len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
01448 #else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
01449               /* we would include the tag in sending */
01450               len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
01451 #endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
01452 
01453               err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
01454               if (err == ERR_OK) {
01455                 data_to_send = 1;
01456 #if !LWIP_HTTPD_SSI_INCLUDE_TAG
01457                 if(ssi->tag_started <= hs->file) {
01458                   /* pretend to have sent the tag, too */
01459                   len += ssi->tag_end - ssi->tag_started;
01460                 }
01461 #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
01462                 hs->file += len;
01463                 hs->left -= len;
01464               }
01465             }
01466           } else {
01467             ssi->tag_index++;
01468           }
01469         } else {
01470           /* We found an unexpected character so this is not a tag. Move
01471            * back to idle state. */
01472           ssi->parse_left--;
01473           ssi->parsed++;
01474           ssi->tag_state = TAG_NONE;
01475         }
01476         break;
01477 
01478       /*
01479        * We have found a valid tag and are in the process of sending
01480        * data as a result of that discovery. We send either remaining data
01481        * from the file prior to the insert point or the insert string itself.
01482        */
01483       case TAG_SENDING:
01484         /* Do we have any remaining file data to send from the buffer prior
01485          * to the tag? */
01486         if(ssi->tag_end > hs->file) {
01487           /* How much of the data can we send? */
01488 #if LWIP_HTTPD_SSI_INCLUDE_TAG
01489           len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff);
01490 #else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
01491           LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file);
01492           /* we would include the tag in sending */
01493           len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff);
01494 #endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/
01495           if (len != 0) {
01496             err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
01497           } else {
01498             err = ERR_OK;
01499           }
01500           if (err == ERR_OK) {
01501             data_to_send = 1;
01502 #if !LWIP_HTTPD_SSI_INCLUDE_TAG
01503             if(ssi->tag_started <= hs->file) {
01504               /* pretend to have sent the tag, too */
01505               len += ssi->tag_end - ssi->tag_started;
01506             }
01507 #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
01508             hs->file += len;
01509             hs->left -= len;
01510           }
01511         } else {
01512 #if LWIP_HTTPD_SSI_MULTIPART
01513           if(ssi->tag_index >= ssi->tag_insert_len) {
01514             /* Did the last SSIHandler have more to send? */
01515             if (ssi->tag_part != HTTPD_LAST_TAG_PART) {
01516               /* If so, call it again */
01517               ssi->tag_index = 0;
01518               get_tag_insert(hs);
01519             }
01520           }
01521 #endif /* LWIP_HTTPD_SSI_MULTIPART */
01522 
01523           /* Do we still have insert data left to send? */
01524           if(ssi->tag_index < ssi->tag_insert_len) {
01525             /* We are sending the insert string itself. How much of the
01526              * insert can we send? */
01527             len = (ssi->tag_insert_len - ssi->tag_index);
01528 
01529             /* Note that we set the copy flag here since we only have a
01530              * single tag insert buffer per connection. If we don't do
01531              * this, insert corruption can occur if more than one insert
01532              * is processed before we call tcp_output. */
01533             err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len,
01534                              HTTP_IS_TAG_VOLATILE(hs));
01535             if (err == ERR_OK) {
01536               data_to_send = 1;
01537               ssi->tag_index += len;
01538               /* Don't return here: keep on sending data */
01539             }
01540           } else {
01541 #if LWIP_HTTPD_SSI_MULTIPART
01542             if (ssi->tag_part == HTTPD_LAST_TAG_PART)
01543 #endif /* LWIP_HTTPD_SSI_MULTIPART */
01544             {
01545               /* We have sent all the insert data so go back to looking for
01546                * a new tag. */
01547               LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n"));
01548               ssi->tag_index = 0;
01549               ssi->tag_state = TAG_NONE;
01550 #if !LWIP_HTTPD_SSI_INCLUDE_TAG
01551               ssi->parsed = ssi->tag_end;
01552 #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/
01553             }
01554           }
01555           break;
01556       }
01557     }
01558   }
01559 
01560   /* If we drop out of the end of the for loop, this implies we must have
01561    * file data to send so send it now. In TAG_SENDING state, we've already
01562    * handled this so skip the send if that's the case. */
01563   if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) {
01564     len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff);
01565 
01566     err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs));
01567     if (err == ERR_OK) {
01568       data_to_send = 1;
01569       hs->file += len;
01570       hs->left -= len;
01571     }
01572   }
01573   return data_to_send;
01574 }
01575 #endif /* LWIP_HTTPD_SSI */
01576 
01577 /**
01578  * Try to send more data on this pcb.
01579  *
01580  * @param pcb the pcb to send data
01581  * @param hs connection state
01582  */
01583 static u8_t
01584 http_send(struct tcp_pcb *pcb, struct http_state *hs)
01585 {
01586   u8_t data_to_send = HTTP_NO_DATA_TO_SEND;
01587 
01588   LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb,
01589     (void*)hs, hs != NULL ? (int)hs->left : 0));
01590 
01591 #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
01592   if (hs->unrecved_bytes != 0) {
01593     return 0;
01594   }
01595 #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
01596 
01597   /* If we were passed a NULL state structure pointer, ignore the call. */
01598   if (hs == NULL) {
01599     return 0;
01600   }
01601 
01602 #if LWIP_HTTPD_FS_ASYNC_READ
01603   /* Check if we are allowed to read from this file.
01604      (e.g. SSI might want to delay sending until data is available) */
01605   if (!fs_is_file_ready(hs->handle, http_continue, hs)) {
01606     return 0;
01607   }
01608 #endif /* LWIP_HTTPD_FS_ASYNC_READ */
01609 
01610 #if LWIP_HTTPD_DYNAMIC_HEADERS
01611   /* Do we have any more header data to send for this file? */
01612   if (hs->hdr_index < NUM_FILE_HDR_STRINGS) {
01613     data_to_send = http_send_headers(pcb, hs);
01614     if ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) &&
01615         (hs->hdr_index < NUM_FILE_HDR_STRINGS)) {
01616       return data_to_send;
01617     }
01618   }
01619 #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
01620 
01621   /* Have we run out of file data to send? If so, we need to read the next
01622    * block from the file. */
01623   if (hs->left == 0) {
01624     if (!http_check_eof(pcb, hs)) {
01625       return 0;
01626     }
01627   }
01628 
01629 #if LWIP_HTTPD_SSI
01630   if(hs->ssi) {
01631     data_to_send = http_send_data_ssi(pcb, hs);
01632   } else
01633 #endif /* LWIP_HTTPD_SSI */
01634   {
01635     data_to_send = http_send_data_nonssi(pcb, hs);
01636   }
01637 
01638   if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) {
01639     /* We reached the end of the file so this request is done.
01640      * This adds the FIN flag right into the last data segment. */
01641     LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n"));
01642     http_eof(pcb, hs);
01643     return 0;
01644   }
01645   LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n"));
01646   return data_to_send;
01647 }
01648 
01649 #if LWIP_HTTPD_SUPPORT_EXTSTATUS
01650 /** Initialize a http connection with a file to send for an error message
01651  *
01652  * @param hs http connection state
01653  * @param error_nr HTTP error number
01654  * @return ERR_OK if file was found and hs has been initialized correctly
01655  *         another err_t otherwise
01656  */
01657 static err_t
01658 http_find_error_file(struct http_state *hs, u16_t error_nr)
01659 {
01660   const char *uri1, *uri2, *uri3;
01661   err_t err;
01662 
01663   if (error_nr == 501) {
01664     uri1 = "/501.html";
01665     uri2 = "/501.htm";
01666     uri3 = "/501.shtml";
01667   } else {
01668     /* 400 (bad request is the default) */
01669     uri1 = "/400.html";
01670     uri2 = "/400.htm";
01671     uri3 = "/400.shtml";
01672   }
01673   err = fs_open(&hs->file_handle, uri1);
01674   if (err != ERR_OK) {
01675     err = fs_open(&hs->file_handle, uri2);
01676     if (err != ERR_OK) {
01677       err = fs_open(&hs->file_handle, uri3);
01678       if (err != ERR_OK) {
01679         LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n",
01680           error_nr));
01681         return ERR_ARG;
01682       }
01683     }
01684   }
01685   return http_init_file(hs, &hs->file_handle, 0, NULL, 0, NULL);
01686 }
01687 #else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
01688 #define http_find_error_file(hs, error_nr) ERR_ARG
01689 #endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */
01690 
01691 /**
01692  * Get the file struct for a 404 error page.
01693  * Tries some file names and returns NULL if none found.
01694  *
01695  * @param uri pointer that receives the actual file name URI
01696  * @return file struct for the error page or NULL no matching file was found
01697  */
01698 static struct fs_file *
01699 http_get_404_file(struct http_state *hs, const char **uri)
01700 {
01701   err_t err;
01702 
01703   *uri = "/404.html";
01704   err = fs_open(&hs->file_handle, *uri);
01705   if (err != ERR_OK) {
01706     /* 404.html doesn't exist. Try 404.htm instead. */
01707     *uri = "/404.htm";
01708     err = fs_open(&hs->file_handle, *uri);
01709     if (err != ERR_OK) {
01710       /* 404.htm doesn't exist either. Try 404.shtml instead. */
01711       *uri = "/404.shtml";
01712       err = fs_open(&hs->file_handle, *uri);
01713       if (err != ERR_OK) {
01714         /* 404.htm doesn't exist either. Indicate to the caller that it should
01715          * send back a default 404 page.
01716          */
01717         *uri = NULL;
01718         return NULL;
01719       }
01720     }
01721   }
01722 
01723   return &hs->file_handle;
01724 }
01725 
01726 #if LWIP_HTTPD_SUPPORT_POST
01727 static err_t
01728 http_handle_post_finished(struct http_state *hs)
01729 {
01730 #if LWIP_HTTPD_POST_MANUAL_WND
01731   /* Prevent multiple calls to httpd_post_finished, since it might have already
01732      been called before from httpd_post_data_recved(). */
01733   if (hs->post_finished) {
01734     return ERR_OK;
01735   }
01736   hs->post_finished = 1;
01737 #endif /* LWIP_HTTPD_POST_MANUAL_WND */
01738   /* application error or POST finished */
01739   /* NULL-terminate the buffer */
01740   http_uri_buf[0] = 0;
01741   httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN);
01742   return http_find_file(hs, http_uri_buf, 0);
01743 }
01744 
01745 /** Pass received POST body data to the application and correctly handle
01746  * returning a response document or closing the connection.
01747  * ATTENTION: The application is responsible for the pbuf now, so don't free it!
01748  *
01749  * @param hs http connection state
01750  * @param p pbuf to pass to the application
01751  * @return ERR_OK if passed successfully, another err_t if the response file
01752  *         hasn't been found (after POST finished)
01753  */
01754 static err_t
01755 http_post_rxpbuf(struct http_state *hs, struct pbuf *p)
01756 {
01757   err_t err;
01758 
01759   if (p != NULL) {
01760     /* adjust remaining Content-Length */
01761     if (hs->post_content_len_left < p->tot_len) {
01762       hs->post_content_len_left = 0;
01763     } else {
01764       hs->post_content_len_left -= p->tot_len;
01765     }
01766   }
01767   err = httpd_post_receive_data(hs, p);
01768   if (err != ERR_OK) {
01769     /* Ignore remaining content in case of application error */
01770     hs->post_content_len_left = 0;
01771   }
01772   if (hs->post_content_len_left == 0) {
01773 #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
01774     if (hs->unrecved_bytes != 0) {
01775        return ERR_OK;
01776     }
01777 #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
01778     /* application error or POST finished */
01779     return http_handle_post_finished(hs);
01780   }
01781 
01782   return ERR_OK;
01783 }
01784 
01785 /** Handle a post request. Called from http_parse_request when method 'POST'
01786  * is found.
01787  *
01788  * @param p The input pbuf (containing the POST header and body).
01789  * @param hs The http connection state.
01790  * @param data HTTP request (header and part of body) from input pbuf(s).
01791  * @param data_len Size of 'data'.
01792  * @param uri The HTTP URI parsed from input pbuf(s).
01793  * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP
01794  *                header starts).
01795  * @return ERR_OK: POST correctly parsed and accepted by the application.
01796  *         ERR_INPROGRESS: POST not completely parsed (no error yet)
01797  *         another err_t: Error parsing POST or denied by the application
01798  */
01799 static err_t
01800 http_post_request(struct pbuf *inp, struct http_state *hs,
01801                   char *data, u16_t data_len, char *uri, char *uri_end)
01802 {
01803   err_t err;
01804   /* search for end-of-header (first double-CRLF) */
01805   char* crlfcrlf = strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data));
01806 
01807   if (crlfcrlf != NULL) {
01808     /* search for "Content-Length: " */
01809 #define HTTP_HDR_CONTENT_LEN                "Content-Length: "
01810 #define HTTP_HDR_CONTENT_LEN_LEN            16
01811 #define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN  10
01812     char *scontent_len = strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1));
01813     if (scontent_len != NULL) {
01814       char *scontent_len_end = strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN);
01815       if (scontent_len_end != NULL) {
01816         int content_len;
01817         char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN;
01818         content_len = atoi(content_len_num);
01819         if (content_len == 0) {
01820           /* if atoi returns 0 on error, fix this */
01821           if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) {
01822             content_len = -1;
01823           }
01824         }
01825         if (content_len >= 0) {
01826           /* adjust length of HTTP header passed to application */
01827           const char *hdr_start_after_uri = uri_end + 1;
01828           u16_t hdr_len = LWIP_MIN(data_len, crlfcrlf + 4 - data);
01829           u16_t hdr_data_len = LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri);
01830           u8_t post_auto_wnd = 1;
01831           http_uri_buf[0] = 0;
01832           /* trim http header */
01833           *crlfcrlf = 0;
01834           err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len,
01835             http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd);
01836           if (err == ERR_OK) {
01837             /* try to pass in data of the first pbuf(s) */
01838             struct pbuf *q = inp;
01839             u16_t start_offset = hdr_len;
01840 #if LWIP_HTTPD_POST_MANUAL_WND
01841             hs->no_auto_wnd = !post_auto_wnd;
01842 #endif /* LWIP_HTTPD_POST_MANUAL_WND */
01843             /* set the Content-Length to be received for this POST */
01844             hs->post_content_len_left = (u32_t)content_len;
01845 
01846             /* get to the pbuf where the body starts */
01847             while((q != NULL) && (q->len <= start_offset)) {
01848               start_offset -= q->len;
01849               q = q->next;
01850             }
01851             if (q != NULL) {
01852               /* hide the remaining HTTP header */
01853               pbuf_header(q, -(s16_t)start_offset);
01854 #if LWIP_HTTPD_POST_MANUAL_WND
01855               if (!post_auto_wnd) {
01856                 /* already tcp_recved() this data... */
01857                 hs->unrecved_bytes = q->tot_len;
01858               }
01859 #endif /* LWIP_HTTPD_POST_MANUAL_WND */
01860               pbuf_ref(q);
01861               return http_post_rxpbuf(hs, q);
01862             } else if (hs->post_content_len_left == 0) {
01863               q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
01864               return http_post_rxpbuf(hs, q);
01865             } else {
01866               return ERR_OK;
01867             }
01868           } else {
01869             /* return file passed from application */
01870             return http_find_file(hs, http_uri_buf, 0);
01871           }
01872         } else {
01873           LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n",
01874             content_len_num));
01875           return ERR_ARG;
01876         }
01877       }
01878     }
01879     /* If we come here, headers are fully received (double-crlf), but Content-Length
01880        was not included. Since this is currently the only supported method, we have
01881        to fail in this case! */
01882     LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n"));
01883     return ERR_ARG;
01884   }
01885   /* if we come here, the POST is incomplete */
01886 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
01887   return ERR_INPROGRESS;
01888 #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
01889   return ERR_ARG;
01890 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
01891 }
01892 
01893 #if LWIP_HTTPD_POST_MANUAL_WND
01894 /** A POST implementation can call this function to update the TCP window.
01895  * This can be used to throttle data reception (e.g. when received data is
01896  * programmed to flash and data is received faster than programmed).
01897  *
01898  * @param connection A connection handle passed to httpd_post_begin for which
01899  *        httpd_post_finished has *NOT* been called yet!
01900  * @param recved_len Length of data received (for window update)
01901  */
01902 void httpd_post_data_recved(void *connection, u16_t recved_len)
01903 {
01904   struct http_state *hs = (struct http_state*)connection;
01905   if (hs != NULL) {
01906     if (hs->no_auto_wnd) {
01907       u16_t len = recved_len;
01908       if (hs->unrecved_bytes >= recved_len) {
01909         hs->unrecved_bytes -= recved_len;
01910       } else {
01911         LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n"));
01912         len = (u16_t)hs->unrecved_bytes;
01913         hs->unrecved_bytes = 0;
01914       }
01915       if (hs->pcb != NULL) {
01916         if (len != 0) {
01917           tcp_recved(hs->pcb, len);
01918         }
01919         if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) {
01920           /* finished handling POST */
01921           http_handle_post_finished(hs);
01922           http_send(hs->pcb, hs);
01923         }
01924       }
01925     }
01926   }
01927 }
01928 #endif /* LWIP_HTTPD_POST_MANUAL_WND */
01929 
01930 #endif /* LWIP_HTTPD_SUPPORT_POST */
01931 
01932 #if LWIP_HTTPD_FS_ASYNC_READ
01933 /** Try to send more data if file has been blocked before
01934  * This is a callback function passed to fs_read_async().
01935  */
01936 static void
01937 http_continue(void *connection)
01938 {
01939   struct http_state *hs = (struct http_state*)connection;
01940   if (hs && (hs->pcb) && (hs->handle)) {
01941     LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL);
01942     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n"));
01943     if (http_send(hs->pcb, hs)) {
01944       /* If we wrote anything to be sent, go ahead and send it now. */
01945       LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
01946       tcp_output(hs->pcb);
01947     }
01948   }
01949 }
01950 #endif /* LWIP_HTTPD_FS_ASYNC_READ */
01951 
01952 /**
01953  * When data has been received in the correct state, try to parse it
01954  * as a HTTP request.
01955  *
01956  * @param p the received pbuf
01957  * @param hs the connection state
01958  * @param pcb the tcp_pcb which received this packet
01959  * @return ERR_OK if request was OK and hs has been initialized correctly
01960  *         ERR_INPROGRESS if request was OK so far but not fully received
01961  *         another err_t otherwise
01962  */
01963 static err_t
01964 http_parse_request(struct pbuf *inp, struct http_state *hs, struct tcp_pcb *pcb)
01965 {
01966   char *data;
01967   char *crlf;
01968   u16_t data_len;
01969   struct pbuf *p = inp;
01970 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
01971   u16_t clen;
01972 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
01973 #if LWIP_HTTPD_SUPPORT_POST
01974   err_t err;
01975 #endif /* LWIP_HTTPD_SUPPORT_POST */
01976 
01977   LWIP_UNUSED_ARG(pcb); /* only used for post */
01978   LWIP_ASSERT("p != NULL", p != NULL);
01979   LWIP_ASSERT("hs != NULL", hs != NULL);
01980 
01981   if ((hs->handle != NULL) || (hs->file != NULL)) {
01982     LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n"));
01983     /* already sending a file */
01984     /* @todo: abort? */
01985     return ERR_USE;
01986   }
01987 
01988 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
01989 
01990   LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len));
01991 
01992   /* first check allowed characters in this pbuf? */
01993 
01994   /* enqueue the pbuf */
01995   if (hs->req == NULL) {
01996     LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n"));
01997     hs->req = p;
01998   } else {
01999     LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n"));
02000     pbuf_cat(hs->req, p);
02001   }
02002   /* increase pbuf ref counter as it is freed when we return but we want to
02003      keep it on the req list */
02004   pbuf_ref(p);
02005 
02006   if (hs->req->next != NULL) {
02007     data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH);
02008     pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0);
02009     data = httpd_req_buf;
02010   } else
02011 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
02012   {
02013     data = (char *)p->payload;
02014     data_len = p->len;
02015     if (p->len != p->tot_len) {
02016       LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n"));
02017     }
02018   }
02019 
02020   /* received enough data for minimal request? */
02021   if (data_len >= MIN_REQ_LEN) {
02022     /* wait for CRLF before parsing anything */
02023     crlf = strnstr(data, CRLF, data_len);
02024     if (crlf != NULL) {
02025 #if LWIP_HTTPD_SUPPORT_POST
02026       int is_post = 0;
02027 #endif /* LWIP_HTTPD_SUPPORT_POST */
02028       int is_09 = 0;
02029       char *sp1, *sp2;
02030       u16_t left_len, uri_len;
02031       LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n"));
02032       /* parse method */
02033       if (!strncmp(data, "GET ", 4)) {
02034         sp1 = data + 3;
02035         /* received GET request */
02036         LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n"));
02037 #if LWIP_HTTPD_SUPPORT_POST
02038       } else if (!strncmp(data, "POST ", 5)) {
02039         /* store request type */
02040         is_post = 1;
02041         sp1 = data + 4;
02042         /* received GET request */
02043         LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n"));
02044 #endif /* LWIP_HTTPD_SUPPORT_POST */
02045       } else {
02046         /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
02047         data[4] = 0;
02048         /* unsupported method! */
02049         LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n",
02050           data));
02051         return http_find_error_file(hs, 501);
02052       }
02053       /* if we come here, method is OK, parse URI */
02054       left_len = (u16_t)(data_len - ((sp1 +1) - data));
02055       sp2 = strnstr(sp1 + 1, " ", left_len);
02056 #if LWIP_HTTPD_SUPPORT_V09
02057       if (sp2 == NULL) {
02058         /* HTTP 0.9: respond with correct protocol version */
02059         sp2 = strnstr(sp1 + 1, CRLF, left_len);
02060         is_09 = 1;
02061 #if LWIP_HTTPD_SUPPORT_POST
02062         if (is_post) {
02063           /* HTTP/0.9 does not support POST */
02064           goto badrequest;
02065         }
02066 #endif /* LWIP_HTTPD_SUPPORT_POST */
02067       }
02068 #endif /* LWIP_HTTPD_SUPPORT_V09 */
02069       uri_len = (u16_t)(sp2 - (sp1 + 1));
02070       if ((sp2 != 0) && (sp2 > sp1)) {
02071         /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */
02072         if (strnstr(data, CRLF CRLF, data_len) != NULL) {
02073           char *uri = sp1 + 1;
02074 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
02075           /* This is HTTP/1.0 compatible: for strict 1.1, a connection
02076              would always be persistent unless "close" was specified. */
02077           if (!is_09 && (strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) ||
02078               strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) {
02079             hs->keepalive = 1;
02080           } else {
02081             hs->keepalive = 0;
02082           }
02083 #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
02084           /* null-terminate the METHOD (pbuf is freed anyway wen returning) */
02085           *sp1 = 0;
02086           uri[uri_len] = 0;
02087           LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n",
02088                       data, uri));
02089 #if LWIP_HTTPD_SUPPORT_POST
02090           if (is_post) {
02091 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
02092             struct pbuf *q = hs->req;
02093 #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
02094             struct pbuf *q = inp;
02095 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
02096             err = http_post_request(q, hs, data, data_len, uri, sp2);
02097             if (err != ERR_OK) {
02098               /* restore header for next try */
02099               *sp1 = ' ';
02100               *sp2 = ' ';
02101               uri[uri_len] = ' ';
02102             }
02103             if (err == ERR_ARG) {
02104               goto badrequest;
02105             }
02106             return err;
02107           } else
02108 #endif /* LWIP_HTTPD_SUPPORT_POST */
02109           {
02110             return http_find_file(hs, uri, is_09);
02111           }
02112         }
02113       } else {
02114         LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n"));
02115       }
02116     }
02117   }
02118 
02119 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
02120   clen = pbuf_clen(hs->req);
02121   if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) &&
02122     (clen <= LWIP_HTTPD_REQ_QUEUELEN)) {
02123     /* request not fully received (too short or CRLF is missing) */
02124     return ERR_INPROGRESS;
02125   } else
02126 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
02127   {
02128 #if LWIP_HTTPD_SUPPORT_POST
02129 badrequest:
02130 #endif /* LWIP_HTTPD_SUPPORT_POST */
02131     LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n"));
02132     /* could not parse request */
02133     return http_find_error_file(hs, 400);
02134   }
02135 }
02136 
02137 /** Try to find the file specified by uri and, if found, initialize hs
02138  * accordingly.
02139  *
02140  * @param hs the connection state
02141  * @param uri the HTTP header URI
02142  * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
02143  * @return ERR_OK if file was found and hs has been initialized correctly
02144  *         another err_t otherwise
02145  */
02146 static err_t
02147 http_find_file(struct http_state *hs, const char *uri, int is_09)
02148 {
02149   size_t loop;
02150   struct fs_file *file = NULL;
02151   char *params = NULL;
02152   err_t err;
02153 #if LWIP_HTTPD_CGI
02154   int i;
02155 #endif /* LWIP_HTTPD_CGI */
02156 #if !LWIP_HTTPD_SSI
02157   const
02158 #endif /* !LWIP_HTTPD_SSI */
02159   /* By default, assume we will not be processing server-side-includes tags */
02160   u8_t tag_check = 0;
02161 
02162   /* Have we been asked for the default file (in root or a directory) ? */
02163 #if LWIP_HTTPD_MAX_REQUEST_URI_LEN
02164   size_t uri_len = strlen(uri);
02165   if ((uri_len > 0) && (uri[uri_len-1] == '/') &&
02166       ((uri != http_uri_buf) || (uri_len == 1))) {
02167     size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1);
02168     if (copy_len > 0) {
02169       MEMCPY(http_uri_buf, uri, copy_len);
02170       http_uri_buf[copy_len] = 0;
02171     }
02172 #else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
02173   if ((uri[0] == '/') &&  (uri[1] == 0)) {
02174 #endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
02175     /* Try each of the configured default filenames until we find one
02176        that exists. */
02177     for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) {
02178       const char* file_name;
02179 #if LWIP_HTTPD_MAX_REQUEST_URI_LEN
02180       if (copy_len > 0) {
02181         size_t len_left = sizeof(http_uri_buf) - copy_len - 1;
02182         if (len_left > 0) {
02183           size_t name_len = strlen(g_psDefaultFilenames[loop].name);
02184           size_t name_copy_len = LWIP_MIN(len_left, name_len);
02185           MEMCPY(&http_uri_buf[copy_len], g_psDefaultFilenames[loop].name, name_copy_len);
02186         }
02187         file_name = http_uri_buf;
02188       } else
02189 #endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */
02190       {
02191         file_name = g_psDefaultFilenames[loop].name;
02192       }
02193       LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name));
02194       err = fs_open(&hs->file_handle, file_name);
02195       if(err == ERR_OK) {
02196         uri = file_name;
02197         file = &hs->file_handle;
02198         LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n"));
02199 #if LWIP_HTTPD_SSI
02200         tag_check = g_psDefaultFilenames[loop].shtml;
02201 #endif /* LWIP_HTTPD_SSI */
02202         break;
02203       }
02204     }
02205   }
02206   if (file == NULL) {
02207     /* No - we've been asked for a specific file. */
02208     /* First, isolate the base URI (without any parameters) */
02209     params = (char *)strchr(uri, '?');
02210     if (params != NULL) {
02211       /* URI contains parameters. NULL-terminate the base URI */
02212       *params = '\0';
02213       params++;
02214     }
02215 
02216 #if LWIP_HTTPD_CGI
02217     http_cgi_paramcount = -1;
02218     /* Does the base URI we have isolated correspond to a CGI handler? */
02219     if (g_iNumCGIs && g_pCGIs) {
02220       for (i = 0; i < g_iNumCGIs; i++) {
02221         if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) {
02222           /*
02223            * We found a CGI that handles this URI so extract the
02224            * parameters and call the handler.
02225            */
02226            http_cgi_paramcount = extract_uri_parameters(hs, params);
02227            uri = g_pCGIs[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params,
02228                                           hs->param_vals);
02229            break;
02230         }
02231       }
02232     }
02233 #endif /* LWIP_HTTPD_CGI */
02234 
02235     LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri));
02236 
02237     err = fs_open(&hs->file_handle, uri);
02238     if (err == ERR_OK) {
02239        file = &hs->file_handle;
02240     } else {
02241       file = http_get_404_file(hs, &uri);
02242     }
02243 #if LWIP_HTTPD_SSI
02244     if (file != NULL) {
02245       /* See if we have been asked for an shtml file and, if so,
02246          enable tag checking. */
02247       const char* ext = NULL, *sub;
02248       char* param = (char*)strstr(uri, "?");
02249       if (param != NULL) {
02250          /* separate uri from parameters for now, set back later */
02251          *param = 0;
02252       }
02253       sub = uri;
02254       ext = uri;
02255       for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, "."))
02256       {
02257          ext = sub;
02258          sub++;
02259       }
02260       tag_check = 0;
02261       for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) {
02262         if (!stricmp(ext, g_pcSSIExtensions[loop])) {
02263           tag_check = 1;
02264           break;
02265         }
02266       }
02267       if (param != NULL) {
02268          *param = '?';
02269       }
02270     }
02271 #endif /* LWIP_HTTPD_SSI */
02272   }
02273   if (file == NULL) {
02274     /* None of the default filenames exist so send back a 404 page */
02275     file = http_get_404_file(hs, &uri);
02276   }
02277   return http_init_file(hs, file, is_09, uri, tag_check, params);
02278 }
02279 
02280 /** Initialize a http connection with a file to send (if found).
02281  * Called by http_find_file and http_find_error_file.
02282  *
02283  * @param hs http connection state
02284  * @param file file structure to send (or NULL if not found)
02285  * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response)
02286  * @param uri the HTTP header URI
02287  * @param tag_check enable SSI tag checking
02288  * @param uri_has_params != NULL if URI has parameters (separated by '?')
02289  * @return ERR_OK if file was found and hs has been initialized correctly
02290  *         another err_t otherwise
02291  */
02292 static err_t
02293 http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri,
02294                u8_t tag_check, char* params)
02295 {
02296   if (file != NULL) {
02297     /* file opened, initialise struct http_state */
02298 #if LWIP_HTTPD_SSI
02299     if (tag_check) {
02300       struct http_ssi_state *ssi = http_ssi_state_alloc();
02301       if (ssi != NULL) {
02302         ssi->tag_index = 0;
02303         ssi->tag_state = TAG_NONE;
02304         ssi->parsed = file->data;
02305         ssi->parse_left = file->len;
02306         ssi->tag_end = file->data;
02307         hs->ssi = ssi;
02308       }
02309     }
02310 #else /* LWIP_HTTPD_SSI */
02311     LWIP_UNUSED_ARG(tag_check);
02312 #endif /* LWIP_HTTPD_SSI */
02313     hs->handle = file;
02314     hs->file = file->data;
02315     LWIP_ASSERT("File length must be positive!", (file->len >= 0));
02316 #if LWIP_HTTPD_CUSTOM_FILES
02317     if (file->is_custom_file && (file->data == NULL)) {
02318       /* custom file, need to read data first (via fs_read_custom) */
02319       hs->left = 0;
02320     } else
02321 #endif /* LWIP_HTTPD_CUSTOM_FILES */
02322     {
02323       hs->left = file->len;
02324     }
02325     hs->retries = 0;
02326 #if LWIP_HTTPD_TIMING
02327     hs->time_started = sys_now();
02328 #endif /* LWIP_HTTPD_TIMING */
02329 #if !LWIP_HTTPD_DYNAMIC_HEADERS
02330     LWIP_ASSERT("HTTP headers not included in file system",
02331        (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0);
02332 #endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */
02333 #if LWIP_HTTPD_SUPPORT_V09
02334     if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) {
02335       /* HTTP/0.9 responses are sent without HTTP header,
02336          search for the end of the header. */
02337       char *file_start = strnstr(hs->file, CRLF CRLF, hs->left);
02338       if (file_start != NULL) {
02339         size_t diff = file_start + 4 - hs->file;
02340         hs->file += diff;
02341         hs->left -= (u32_t)diff;
02342       }
02343     }
02344 #endif /* LWIP_HTTPD_SUPPORT_V09*/
02345 #if LWIP_HTTPD_CGI_SSI
02346     if (params != NULL) {
02347       /* URI contains parameters, call generic CGI handler */
02348       int count;
02349 #if LWIP_HTTPD_CGI
02350       if (http_cgi_paramcount >= 0) {
02351         count = http_cgi_paramcount;
02352       } else
02353 #endif
02354       {
02355         count = extract_uri_parameters(hs, params);
02356       }
02357       httpd_cgi_handler(uri, count, http_cgi_params, http_cgi_param_vals
02358 #if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE
02359          , hs->handle->state
02360 #endif /* LWIP_HTTPD_FILE_STATE */
02361                         );
02362     }
02363 #else /* LWIP_HTTPD_CGI_SSI */
02364     LWIP_UNUSED_ARG(params);
02365 #endif /* LWIP_HTTPD_CGI_SSI */
02366   } else {
02367     hs->handle = NULL;
02368     hs->file = NULL;
02369     hs->left = 0;
02370     hs->retries = 0;
02371   }
02372 #if LWIP_HTTPD_DYNAMIC_HEADERS
02373   /* Determine the HTTP headers to send based on the file extension of
02374    * the requested URI. */
02375   if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) {
02376     get_http_headers(hs, uri);
02377   }
02378 #else /* LWIP_HTTPD_DYNAMIC_HEADERS */
02379   LWIP_UNUSED_ARG(uri);
02380 #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
02381 #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE
02382   if (hs->keepalive) {
02383 #if LWIP_HTTPD_SSI
02384      if (hs->ssi != NULL) {
02385        hs->keepalive = 0;
02386      } else
02387 #endif /* LWIP_HTTPD_SSI */
02388      {
02389        if ((hs->handle != NULL) &&
02390            ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED|FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) {
02391          hs->keepalive = 0;
02392        }
02393      }
02394   }
02395 #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */
02396   return ERR_OK;
02397 }
02398 
02399 /**
02400  * The pcb had an error and is already deallocated.
02401  * The argument might still be valid (if != NULL).
02402  */
02403 static void
02404 http_err(void *arg, err_t err)
02405 {
02406   struct http_state *hs = (struct http_state *)arg;
02407   LWIP_UNUSED_ARG(err);
02408 
02409   LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err)));
02410 
02411   if (hs != NULL) {
02412     http_state_free(hs);
02413   }
02414 }
02415 
02416 /**
02417  * Data has been sent and acknowledged by the remote host.
02418  * This means that more data can be sent.
02419  */
02420 static err_t
02421 http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
02422 {
02423   struct http_state *hs = (struct http_state *)arg;
02424 
02425   LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb));
02426 
02427   LWIP_UNUSED_ARG(len);
02428 
02429   if (hs == NULL) {
02430     return ERR_OK;
02431   }
02432 
02433   hs->retries = 0;
02434 
02435   http_send(pcb, hs);
02436 
02437   return ERR_OK;
02438 }
02439 
02440 /**
02441  * The poll function is called every 2nd second.
02442  * If there has been no data sent (which resets the retries) in 8 seconds, close.
02443  * If the last portion of a file has not been sent in 2 seconds, close.
02444  *
02445  * This could be increased, but we don't want to waste resources for bad connections.
02446  */
02447 static err_t
02448 http_poll(void *arg, struct tcp_pcb *pcb)
02449 {
02450   struct http_state *hs = (struct http_state *)arg;
02451   LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n",
02452     (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state)));
02453 
02454   if (hs == NULL) {
02455     err_t closed;
02456     /* arg is null, close. */
02457     LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n"));
02458     closed = http_close_conn(pcb, NULL);
02459     LWIP_UNUSED_ARG(closed);
02460 #if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR
02461     if (closed == ERR_MEM) {
02462        tcp_abort(pcb);
02463        return ERR_ABRT;
02464     }
02465 #endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */
02466     return ERR_OK;
02467   } else {
02468     hs->retries++;
02469     if (hs->retries == HTTPD_MAX_RETRIES) {
02470       LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n"));
02471       http_close_conn(pcb, hs);
02472       return ERR_OK;
02473     }
02474 
02475     /* If this connection has a file open, try to send some more data. If
02476      * it has not yet received a GET request, don't do this since it will
02477      * cause the connection to close immediately. */
02478     if(hs && (hs->handle)) {
02479       LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n"));
02480       if(http_send(pcb, hs)) {
02481         /* If we wrote anything to be sent, go ahead and send it now. */
02482         LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n"));
02483         tcp_output(pcb);
02484       }
02485     }
02486   }
02487 
02488   return ERR_OK;
02489 }
02490 
02491 /**
02492  * Data has been received on this pcb.
02493  * For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
02494  */
02495 static err_t
02496 http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
02497 {
02498   struct http_state *hs = (struct http_state *)arg;
02499   LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb,
02500     (void*)p, lwip_strerr(err)));
02501 
02502   if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) {
02503     /* error or closed by other side? */
02504     if (p != NULL) {
02505       /* Inform TCP that we have taken the data. */
02506       tcp_recved(pcb, p->tot_len);
02507       pbuf_free(p);
02508     }
02509     if (hs == NULL) {
02510       /* this should not happen, only to be robust */
02511       LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n"));
02512     }
02513     http_close_conn(pcb, hs);
02514     return ERR_OK;
02515   }
02516 
02517 #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND
02518   if (hs->no_auto_wnd) {
02519      hs->unrecved_bytes += p->tot_len;
02520   } else
02521 #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */
02522   {
02523     /* Inform TCP that we have taken the data. */
02524     tcp_recved(pcb, p->tot_len);
02525   }
02526 
02527 #if LWIP_HTTPD_SUPPORT_POST
02528   if (hs->post_content_len_left > 0) {
02529     /* reset idle counter when POST data is received */
02530     hs->retries = 0;
02531     /* this is data for a POST, pass the complete pbuf to the application */
02532     http_post_rxpbuf(hs, p);
02533     /* pbuf is passed to the application, don't free it! */
02534     if (hs->post_content_len_left == 0) {
02535       /* all data received, send response or close connection */
02536       http_send(pcb, hs);
02537     }
02538     return ERR_OK;
02539   } else
02540 #endif /* LWIP_HTTPD_SUPPORT_POST */
02541   {
02542     if (hs->handle == NULL) {
02543       err_t parsed = http_parse_request(p, hs, pcb);
02544       LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK
02545         || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE);
02546 #if LWIP_HTTPD_SUPPORT_REQUESTLIST
02547       if (parsed != ERR_INPROGRESS) {
02548         /* request fully parsed or error */
02549         if (hs->req != NULL) {
02550           pbuf_free(hs->req);
02551           hs->req = NULL;
02552         }
02553       }
02554 #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
02555       pbuf_free(p);
02556       if (parsed == ERR_OK) {
02557 #if LWIP_HTTPD_SUPPORT_POST
02558        if (hs->post_content_len_left == 0)
02559 #endif /* LWIP_HTTPD_SUPPORT_POST */
02560         {
02561           LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void*)hs->file, hs->left));
02562           http_send(pcb, hs);
02563         }
02564       } else if (parsed == ERR_ARG) {
02565         /* @todo: close on ERR_USE? */
02566         http_close_conn(pcb, hs);
02567       }
02568     } else {
02569       LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n"));
02570       /* already sending but still receiving data, we might want to RST here? */
02571       pbuf_free(p);
02572     }
02573   }
02574   return ERR_OK;
02575 }
02576 
02577 /**
02578  * A new incoming connection has been accepted.
02579  */
02580 static err_t
02581 http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
02582 {
02583   struct http_state *hs;
02584   LWIP_UNUSED_ARG(err);
02585   LWIP_UNUSED_ARG(arg);
02586   LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg));
02587   
02588   if ((err != ERR_OK) || (pcb == NULL)) {
02589     return ERR_VAL;
02590   }
02591 
02592   /* Set priority */
02593   tcp_setprio(pcb, HTTPD_TCP_PRIO);
02594 
02595   /* Allocate memory for the structure that holds the state of the
02596      connection - initialized by that function. */
02597   hs = http_state_alloc();
02598   if (hs == NULL) {
02599     LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n"));
02600     return ERR_MEM;
02601   }
02602   hs->pcb = pcb;
02603 
02604   /* Tell TCP that this is the structure we wish to be passed for our
02605      callbacks. */
02606   tcp_arg(pcb, hs);
02607 
02608   /* Set up the various callback functions */
02609   tcp_recv(pcb, http_recv);
02610   tcp_err(pcb, http_err);
02611   tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
02612   tcp_sent(pcb, http_sent);
02613 
02614   return ERR_OK;
02615 }
02616 
02617 /**
02618  * @ingroup httpd
02619  * Initialize the httpd: set up a listening PCB and bind it to the defined port
02620  */
02621 void
02622 httpd_init(void)
02623 {
02624   struct tcp_pcb *pcb;
02625   err_t err;
02626 
02627 #if HTTPD_USE_MEM_POOL
02628   LWIP_MEMPOOL_INIT(HTTPD_STATE);
02629 #if LWIP_HTTPD_SSI
02630   LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE);
02631 #endif
02632 #endif
02633   LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n"));
02634 
02635   pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
02636   LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
02637   tcp_setprio(pcb, HTTPD_TCP_PRIO);
02638   /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
02639   err = tcp_bind(pcb, IP_ANY_TYPE, HTTPD_SERVER_PORT);
02640   LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
02641   pcb = tcp_listen(pcb);
02642   LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
02643   tcp_accept(pcb, http_accept);
02644 }
02645 
02646 #if LWIP_HTTPD_SSI
02647 /**
02648  * Set the SSI handler function.
02649  *
02650  * @param ssi_handler the SSI handler function
02651  * @param tags an array of SSI tag strings to search for in SSI-enabled files
02652  * @param num_tags number of tags in the 'tags' array
02653  */
02654 void
02655 http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags)
02656 {
02657   LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n"));
02658 
02659   LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL);
02660   g_pfnSSIHandler = ssi_handler;
02661 
02662 #if LWIP_HTTPD_SSI_RAW
02663   LWIP_UNUSED_ARG(tags);
02664   LWIP_UNUSED_ARG(num_tags);
02665 #else /* LWIP_HTTPD_SSI_RAW */
02666   LWIP_ASSERT("no tags given", tags != NULL);
02667   LWIP_ASSERT("invalid number of tags", num_tags > 0);
02668 
02669   g_ppcTags = tags;
02670   g_iNumTags = num_tags;
02671 #endif /* !LWIP_HTTPD_SSI_RAW */
02672 }
02673 #endif /* LWIP_HTTPD_SSI */
02674 
02675 #if LWIP_HTTPD_CGI
02676 /**
02677  * Set an array of CGI filenames/handler functions
02678  *
02679  * @param cgis an array of CGI filenames/handler functions
02680  * @param num_handlers number of elements in the 'cgis' array
02681  */
02682 void
02683 http_set_cgi_handlers(const tCGI *cgis, int num_handlers)
02684 {
02685   LWIP_ASSERT("no cgis given", cgis != NULL);
02686   LWIP_ASSERT("invalid number of handlers", num_handlers > 0);
02687   
02688   g_pCGIs = cgis;
02689   g_iNumCGIs = num_handlers;
02690 }
02691 #endif /* LWIP_HTTPD_CGI */
02692 
02693 #endif /* LWIP_TCP */