Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of mbed-os by
lwip_httpd.c
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 */
Generated on Tue Jul 12 2022 13:15:53 by
1.7.2
