/src/tinyusb/lib/lwip/src/apps/http/httpd.c
Line | Count | Source |
1 | | /** |
2 | | * @file |
3 | | * LWIP HTTP server implementation |
4 | | */ |
5 | | |
6 | | /* |
7 | | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
8 | | * All rights reserved. |
9 | | * |
10 | | * Redistribution and use in source and binary forms, with or without modification, |
11 | | * are permitted provided that the following conditions are met: |
12 | | * |
13 | | * 1. Redistributions of source code must retain the above copyright notice, |
14 | | * this list of conditions and the following disclaimer. |
15 | | * 2. Redistributions in binary form must reproduce the above copyright notice, |
16 | | * this list of conditions and the following disclaimer in the documentation |
17 | | * and/or other materials provided with the distribution. |
18 | | * 3. The name of the author may not be used to endorse or promote products |
19 | | * derived from this software without specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
22 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
23 | | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
24 | | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
25 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
26 | | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
29 | | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
30 | | * OF SUCH DAMAGE. |
31 | | * |
32 | | * This file is part of the lwIP TCP/IP stack. |
33 | | * |
34 | | * Author: Adam Dunkels <adam@sics.se> |
35 | | * Simon Goldschmidt |
36 | | * |
37 | | */ |
38 | | |
39 | | /** |
40 | | * @defgroup httpd HTTP server |
41 | | * @ingroup apps |
42 | | * |
43 | | * This httpd supports for a |
44 | | * rudimentary server-side-include facility which will replace tags of the form |
45 | | * <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with |
46 | | * strings provided by an include handler whose pointer is provided to the |
47 | | * module via function http_set_ssi_handler(). |
48 | | * Additionally, a simple common |
49 | | * gateway interface (CGI) handling mechanism has been added to allow clients |
50 | | * to hook functions to particular request URIs. |
51 | | * |
52 | | * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h. |
53 | | * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h. |
54 | | * |
55 | | * By default, the server assumes that HTTP headers are already present in |
56 | | * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in |
57 | | * lwipopts.h, this behavior can be changed such that the server inserts the |
58 | | * headers automatically based on the extension of the file being served. If |
59 | | * this mode is used, be careful to ensure that the file system image used |
60 | | * does not already contain the header information. |
61 | | * |
62 | | * File system images without headers can be created using the makefsfile |
63 | | * tool with the -h command line option. |
64 | | * |
65 | | * |
66 | | * Notes about valid SSI tags |
67 | | * -------------------------- |
68 | | * |
69 | | * The following assumptions are made about tags used in SSI markers: |
70 | | * |
71 | | * 1. No tag may contain '-' or whitespace characters within the tag name. |
72 | | * 2. Whitespace is allowed between the tag leadin "<!--#" and the start of |
73 | | * the tag name and between the tag name and the leadout string "-->". |
74 | | * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters. |
75 | | * |
76 | | * Notes on CGI usage |
77 | | * ------------------ |
78 | | * |
79 | | * The simple CGI support offered here works with GET method requests only |
80 | | * and can handle up to 16 parameters encoded into the URI. The handler |
81 | | * function may not write directly to the HTTP output but must return a |
82 | | * filename that the HTTP server will send to the browser as a response to |
83 | | * the incoming CGI request. |
84 | | * |
85 | | * |
86 | | * |
87 | | * The list of supported file types is quite short, so if makefsdata complains |
88 | | * about an unknown extension, make sure to add it (and its doctype) to |
89 | | * the 'g_psHTTPHeaders' list. |
90 | | */ |
91 | | #include "lwip/init.h" |
92 | | #include "lwip/apps/httpd.h" |
93 | | #include "lwip/debug.h" |
94 | | #include "lwip/stats.h" |
95 | | #include "lwip/apps/fs.h" |
96 | | #include "httpd_structs.h" |
97 | | #include "lwip/def.h" |
98 | | |
99 | | #include "lwip/altcp.h" |
100 | | #include "lwip/altcp_tcp.h" |
101 | | #if HTTPD_ENABLE_HTTPS |
102 | | #include "lwip/altcp_tls.h" |
103 | | #endif |
104 | | #ifdef LWIP_HOOK_FILENAME |
105 | | #include LWIP_HOOK_FILENAME |
106 | | #endif |
107 | | #if LWIP_HTTPD_TIMING |
108 | | #include "lwip/sys.h" |
109 | | #endif /* LWIP_HTTPD_TIMING */ |
110 | | |
111 | | #include <string.h> /* memset */ |
112 | | #include <stdlib.h> /* atoi */ |
113 | | #include <stdio.h> |
114 | | |
115 | | #if LWIP_TCP && LWIP_CALLBACK_API |
116 | | |
117 | | /** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */ |
118 | 0 | #define MIN_REQ_LEN 7 |
119 | | |
120 | 0 | #define CRLF "\r\n" |
121 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
122 | | #define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive" |
123 | | #define HTTP11_CONNECTIONKEEPALIVE2 "Connection: Keep-Alive" |
124 | | #endif |
125 | | |
126 | | #if LWIP_HTTPD_DYNAMIC_FILE_READ |
127 | | #define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL) |
128 | | #else |
129 | 0 | #define HTTP_IS_DYNAMIC_FILE(hs) 0 |
130 | | #endif |
131 | | |
132 | | /* This defines checks whether tcp_write has to copy data or not */ |
133 | | |
134 | | #ifndef HTTP_IS_DATA_VOLATILE |
135 | | /** tcp_write does not have to copy data when sent from rom-file-system directly */ |
136 | 0 | #define HTTP_IS_DATA_VOLATILE(hs) (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0) |
137 | | #endif |
138 | | /** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */ |
139 | | #ifndef HTTP_IS_HDR_VOLATILE |
140 | | #define HTTP_IS_HDR_VOLATILE(hs, ptr) 0 |
141 | | #endif |
142 | | |
143 | | /* Return values for http_send_*() */ |
144 | | #define HTTP_DATA_TO_SEND_FREED 3 |
145 | | #define HTTP_DATA_TO_SEND_BREAK 2 |
146 | | #define HTTP_DATA_TO_SEND_CONTINUE 1 |
147 | 0 | #define HTTP_NO_DATA_TO_SEND 0 |
148 | | |
149 | | typedef struct { |
150 | | const char *name; |
151 | | u8_t shtml; |
152 | | } default_filename; |
153 | | |
154 | | static const default_filename httpd_default_filenames[] = { |
155 | | {"/index.shtml", 1 }, |
156 | | {"/index.ssi", 1 }, |
157 | | {"/index.shtm", 1 }, |
158 | | {"/index.html", 0 }, |
159 | | {"/index.htm", 0 } |
160 | | }; |
161 | | |
162 | 0 | #define NUM_DEFAULT_FILENAMES LWIP_ARRAYSIZE(httpd_default_filenames) |
163 | | |
164 | | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
165 | | /** HTTP request is copied here from pbufs for simple parsing */ |
166 | | static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH + 1]; |
167 | | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
168 | | |
169 | | #if LWIP_HTTPD_SUPPORT_POST |
170 | | #if LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN > LWIP_HTTPD_MAX_REQUEST_URI_LEN |
171 | | #define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN |
172 | | #endif |
173 | | #endif |
174 | | #ifndef LWIP_HTTPD_URI_BUF_LEN |
175 | | #define LWIP_HTTPD_URI_BUF_LEN LWIP_HTTPD_MAX_REQUEST_URI_LEN |
176 | | #endif |
177 | | #if LWIP_HTTPD_URI_BUF_LEN |
178 | | /* Filename for response file to send when POST is finished or |
179 | | * search for default files when a directory is requested. */ |
180 | | static char http_uri_buf[LWIP_HTTPD_URI_BUF_LEN + 1]; |
181 | | #endif |
182 | | |
183 | | #if LWIP_HTTPD_DYNAMIC_HEADERS |
184 | | /* The number of individual strings that comprise the headers sent before each |
185 | | * requested file. |
186 | | */ |
187 | | #define NUM_FILE_HDR_STRINGS 5 |
188 | | #define HDR_STRINGS_IDX_HTTP_STATUS 0 /* e.g. "HTTP/1.0 200 OK\r\n" */ |
189 | | #define HDR_STRINGS_IDX_SERVER_NAME 1 /* e.g. "Server: "HTTPD_SERVER_AGENT"\r\n" */ |
190 | | #define HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE 2 /* e.g. "Content-Length: xy\r\n" and/or "Connection: keep-alive\r\n" */ |
191 | | #define HDR_STRINGS_IDX_CONTENT_LEN_NR 3 /* the byte count, when content-length is used */ |
192 | | #define HDR_STRINGS_IDX_CONTENT_TYPE 4 /* the content type (or default answer content type including default document) */ |
193 | | |
194 | | /* The dynamically generated Content-Length buffer needs space for CRLF + NULL */ |
195 | | #define LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET 3 |
196 | | #ifndef LWIP_HTTPD_MAX_CONTENT_LEN_SIZE |
197 | | /* The dynamically generated Content-Length buffer shall be able to work with |
198 | | ~953 MB (9 digits) */ |
199 | | #define LWIP_HTTPD_MAX_CONTENT_LEN_SIZE (9 + LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) |
200 | | #endif |
201 | | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
202 | | |
203 | | #if LWIP_HTTPD_SSI |
204 | | |
205 | | #define HTTPD_LAST_TAG_PART 0xFFFF |
206 | | |
207 | | enum tag_check_state { |
208 | | TAG_NONE, /* Not processing an SSI tag */ |
209 | | TAG_LEADIN, /* Tag lead in "<!--#" being processed */ |
210 | | TAG_FOUND, /* Tag name being read, looking for lead-out start */ |
211 | | TAG_LEADOUT, /* Tag lead out "-->" being processed */ |
212 | | TAG_SENDING /* Sending tag replacement string */ |
213 | | }; |
214 | | |
215 | | struct http_ssi_state { |
216 | | const char *parsed; /* Pointer to the first unparsed byte in buf. */ |
217 | | #if !LWIP_HTTPD_SSI_INCLUDE_TAG |
218 | | const char *tag_started;/* Pointer to the first opening '<' of the tag. */ |
219 | | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ |
220 | | const char *tag_end; /* Pointer to char after the closing '>' of the tag. */ |
221 | | u32_t parse_left; /* Number of unparsed bytes in buf. */ |
222 | | u16_t tag_index; /* Counter used by tag parsing state machine */ |
223 | | u16_t tag_insert_len; /* Length of insert in string tag_insert */ |
224 | | #if LWIP_HTTPD_SSI_MULTIPART |
225 | | u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */ |
226 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
227 | | u8_t tag_type; /* index into http_ssi_tag_desc array */ |
228 | | u8_t tag_name_len; /* Length of the tag name in string tag_name */ |
229 | | char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ |
230 | | char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ |
231 | | enum tag_check_state tag_state; /* State of the tag processor */ |
232 | | }; |
233 | | |
234 | | struct http_ssi_tag_description { |
235 | | const char *lead_in; |
236 | | const char *lead_out; |
237 | | }; |
238 | | |
239 | | #endif /* LWIP_HTTPD_SSI */ |
240 | | |
241 | | struct http_state { |
242 | | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED |
243 | | struct http_state *next; |
244 | | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ |
245 | | struct fs_file file_handle; |
246 | | struct fs_file *handle; |
247 | | const char *file; /* Pointer to first unsent byte in buf. */ |
248 | | |
249 | | struct altcp_pcb *pcb; |
250 | | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
251 | | struct pbuf *req; |
252 | | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
253 | | |
254 | | #if LWIP_HTTPD_DYNAMIC_FILE_READ |
255 | | char *buf; /* File read buffer. */ |
256 | | int buf_len; /* Size of file read buffer, buf. */ |
257 | | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ |
258 | | u32_t left; /* Number of unsent bytes in buf. */ |
259 | | u8_t retries; |
260 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
261 | | u8_t keepalive; |
262 | | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ |
263 | | #if LWIP_HTTPD_SSI |
264 | | struct http_ssi_state *ssi; |
265 | | #endif /* LWIP_HTTPD_SSI */ |
266 | | #if LWIP_HTTPD_CGI |
267 | | char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ |
268 | | char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ |
269 | | #endif /* LWIP_HTTPD_CGI */ |
270 | | #if LWIP_HTTPD_DYNAMIC_HEADERS |
271 | | const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */ |
272 | | char hdr_content_len[LWIP_HTTPD_MAX_CONTENT_LEN_SIZE]; |
273 | | u16_t hdr_pos; /* The position of the first unsent header byte in the |
274 | | current string */ |
275 | | u16_t hdr_index; /* The index of the hdr string currently being sent. */ |
276 | | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
277 | | #if LWIP_HTTPD_TIMING |
278 | | u32_t time_started; |
279 | | #endif /* LWIP_HTTPD_TIMING */ |
280 | | #if LWIP_HTTPD_SUPPORT_POST |
281 | | u32_t post_content_len_left; |
282 | | #if LWIP_HTTPD_POST_MANUAL_WND |
283 | | u32_t unrecved_bytes; |
284 | | u8_t no_auto_wnd; |
285 | | u8_t post_finished; |
286 | | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ |
287 | | #endif /* LWIP_HTTPD_SUPPORT_POST*/ |
288 | | }; |
289 | | |
290 | | #if HTTPD_USE_MEM_POOL |
291 | | LWIP_MEMPOOL_DECLARE(HTTPD_STATE, MEMP_NUM_PARALLEL_HTTPD_CONNS, sizeof(struct http_state), "HTTPD_STATE") |
292 | | #if LWIP_HTTPD_SSI |
293 | | LWIP_MEMPOOL_DECLARE(HTTPD_SSI_STATE, MEMP_NUM_PARALLEL_HTTPD_SSI_CONNS, sizeof(struct http_ssi_state), "HTTPD_SSI_STATE") |
294 | | #define HTTP_FREE_SSI_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_SSI_STATE, (x)) |
295 | | #define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)LWIP_MEMPOOL_ALLOC(HTTPD_SSI_STATE) |
296 | | #endif /* LWIP_HTTPD_SSI */ |
297 | | #define HTTP_ALLOC_HTTP_STATE() (struct http_state *)LWIP_MEMPOOL_ALLOC(HTTPD_STATE) |
298 | | #define HTTP_FREE_HTTP_STATE(x) LWIP_MEMPOOL_FREE(HTTPD_STATE, (x)) |
299 | | #else /* HTTPD_USE_MEM_POOL */ |
300 | 0 | #define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state)) |
301 | 0 | #define HTTP_FREE_HTTP_STATE(x) mem_free(x) |
302 | | #if LWIP_HTTPD_SSI |
303 | | #define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state)) |
304 | | #define HTTP_FREE_SSI_STATE(x) mem_free(x) |
305 | | #endif /* LWIP_HTTPD_SSI */ |
306 | | #endif /* HTTPD_USE_MEM_POOL */ |
307 | | |
308 | | static err_t http_close_conn(struct altcp_pcb *pcb, struct http_state *hs); |
309 | | static err_t http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn); |
310 | | static err_t http_find_file(struct http_state *hs, const char *uri, int is_09); |
311 | | 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); |
312 | | static err_t http_poll(void *arg, struct altcp_pcb *pcb); |
313 | | static u8_t http_check_eof(struct altcp_pcb *pcb, struct http_state *hs); |
314 | | #if LWIP_HTTPD_FS_ASYNC_READ |
315 | | static void http_continue(void *connection); |
316 | | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ |
317 | | |
318 | | #if LWIP_HTTPD_SSI |
319 | | /* SSI insert handler function pointer. */ |
320 | | static tSSIHandler httpd_ssi_handler; |
321 | | #if !LWIP_HTTPD_SSI_RAW |
322 | | static int httpd_num_tags; |
323 | | static const char **httpd_tags; |
324 | | #endif /* !LWIP_HTTPD_SSI_RAW */ |
325 | | |
326 | | /* Define the available tag lead-ins and corresponding lead-outs. |
327 | | * ATTENTION: for the algorithm below using this array, it is essential |
328 | | * that the lead in differs in the first character! */ |
329 | | const struct http_ssi_tag_description http_ssi_tag_desc[] = { |
330 | | {"<!--#", "-->"}, |
331 | | {"/*#", "*/"} |
332 | | }; |
333 | | |
334 | | #endif /* LWIP_HTTPD_SSI */ |
335 | | |
336 | | #if LWIP_HTTPD_CGI |
337 | | /* CGI handler information */ |
338 | | static const tCGI *httpd_cgis; |
339 | | static int httpd_num_cgis; |
340 | | static int http_cgi_paramcount; |
341 | | #define http_cgi_params hs->params |
342 | | #define http_cgi_param_vals hs->param_vals |
343 | | #elif LWIP_HTTPD_CGI_SSI |
344 | | static char *http_cgi_params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ |
345 | | static char *http_cgi_param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ |
346 | | #endif /* LWIP_HTTPD_CGI */ |
347 | | |
348 | | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED |
349 | | /** global list of active HTTP connections, use to kill the oldest when |
350 | | running out of memory */ |
351 | | static struct http_state *http_connections; |
352 | | |
353 | | static void |
354 | | http_add_connection(struct http_state *hs) |
355 | | { |
356 | | /* add the connection to the list */ |
357 | | hs->next = http_connections; |
358 | | http_connections = hs; |
359 | | } |
360 | | |
361 | | static void |
362 | | http_remove_connection(struct http_state *hs) |
363 | | { |
364 | | /* take the connection off the list */ |
365 | | if (http_connections) { |
366 | | if (http_connections == hs) { |
367 | | http_connections = hs->next; |
368 | | } else { |
369 | | struct http_state *last; |
370 | | for (last = http_connections; last->next != NULL; last = last->next) { |
371 | | if (last->next == hs) { |
372 | | last->next = hs->next; |
373 | | break; |
374 | | } |
375 | | } |
376 | | } |
377 | | } |
378 | | } |
379 | | |
380 | | static void |
381 | | http_kill_oldest_connection(u8_t ssi_required) |
382 | | { |
383 | | struct http_state *hs = http_connections; |
384 | | struct http_state *hs_free_next = NULL; |
385 | | while (hs && hs->next) { |
386 | | #if LWIP_HTTPD_SSI |
387 | | if (ssi_required) { |
388 | | if (hs->next->ssi != NULL) { |
389 | | hs_free_next = hs; |
390 | | } |
391 | | } else |
392 | | #else /* LWIP_HTTPD_SSI */ |
393 | | LWIP_UNUSED_ARG(ssi_required); |
394 | | #endif /* LWIP_HTTPD_SSI */ |
395 | | { |
396 | | hs_free_next = hs; |
397 | | } |
398 | | LWIP_ASSERT("broken list", hs != hs->next); |
399 | | hs = hs->next; |
400 | | } |
401 | | if (hs_free_next != NULL) { |
402 | | LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL); |
403 | | LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL); |
404 | | /* send RST when killing a connection because of memory shortage */ |
405 | | http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */ |
406 | | } |
407 | | } |
408 | | #else /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ |
409 | | |
410 | | #define http_add_connection(hs) |
411 | | #define http_remove_connection(hs) |
412 | | |
413 | | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ |
414 | | |
415 | | #if LWIP_HTTPD_SSI |
416 | | /** Allocate as struct http_ssi_state. */ |
417 | | static struct http_ssi_state * |
418 | | http_ssi_state_alloc(void) |
419 | | { |
420 | | struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE(); |
421 | | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED |
422 | | if (ret == NULL) { |
423 | | http_kill_oldest_connection(1); |
424 | | ret = HTTP_ALLOC_SSI_STATE(); |
425 | | } |
426 | | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ |
427 | | if (ret != NULL) { |
428 | | memset(ret, 0, sizeof(struct http_ssi_state)); |
429 | | } |
430 | | return ret; |
431 | | } |
432 | | |
433 | | /** Free a struct http_ssi_state. */ |
434 | | static void |
435 | | http_ssi_state_free(struct http_ssi_state *ssi) |
436 | | { |
437 | | if (ssi != NULL) { |
438 | | HTTP_FREE_SSI_STATE(ssi); |
439 | | } |
440 | | } |
441 | | #endif /* LWIP_HTTPD_SSI */ |
442 | | |
443 | | /** Initialize a struct http_state. |
444 | | */ |
445 | | static void |
446 | | http_state_init(struct http_state *hs) |
447 | 0 | { |
448 | | /* Initialize the structure. */ |
449 | 0 | memset(hs, 0, sizeof(struct http_state)); |
450 | | #if LWIP_HTTPD_DYNAMIC_HEADERS |
451 | | /* Indicate that the headers are not yet valid */ |
452 | | hs->hdr_index = NUM_FILE_HDR_STRINGS; |
453 | | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
454 | 0 | } |
455 | | |
456 | | /** Allocate a struct http_state. */ |
457 | | static struct http_state * |
458 | | http_state_alloc(void) |
459 | 0 | { |
460 | 0 | struct http_state *ret = HTTP_ALLOC_HTTP_STATE(); |
461 | | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED |
462 | | if (ret == NULL) { |
463 | | http_kill_oldest_connection(0); |
464 | | ret = HTTP_ALLOC_HTTP_STATE(); |
465 | | } |
466 | | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ |
467 | 0 | if (ret != NULL) { |
468 | 0 | http_state_init(ret); |
469 | 0 | http_add_connection(ret); |
470 | 0 | } |
471 | 0 | return ret; |
472 | 0 | } |
473 | | |
474 | | /** Free a struct http_state. |
475 | | * Also frees the file data if dynamic. |
476 | | */ |
477 | | static void |
478 | | http_state_eof(struct http_state *hs) |
479 | 0 | { |
480 | 0 | if (hs->handle) { |
481 | | #if LWIP_HTTPD_TIMING |
482 | | u32_t ms_needed = sys_now() - hs->time_started; |
483 | | u32_t needed = LWIP_MAX(1, (ms_needed / 100)); |
484 | | LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n", |
485 | | ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed))); |
486 | | #endif /* LWIP_HTTPD_TIMING */ |
487 | 0 | fs_close(hs->handle); |
488 | 0 | hs->handle = NULL; |
489 | 0 | } |
490 | | #if LWIP_HTTPD_DYNAMIC_FILE_READ |
491 | | if (hs->buf != NULL) { |
492 | | mem_free(hs->buf); |
493 | | hs->buf = NULL; |
494 | | } |
495 | | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ |
496 | | #if LWIP_HTTPD_SSI |
497 | | if (hs->ssi) { |
498 | | http_ssi_state_free(hs->ssi); |
499 | | hs->ssi = NULL; |
500 | | } |
501 | | #endif /* LWIP_HTTPD_SSI */ |
502 | 0 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
503 | 0 | if (hs->req) { |
504 | 0 | pbuf_free(hs->req); |
505 | 0 | hs->req = NULL; |
506 | 0 | } |
507 | 0 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
508 | 0 | } |
509 | | |
510 | | /** Free a struct http_state. |
511 | | * Also frees the file data if dynamic. |
512 | | */ |
513 | | static void |
514 | | http_state_free(struct http_state *hs) |
515 | 0 | { |
516 | 0 | if (hs != NULL) { |
517 | 0 | http_state_eof(hs); |
518 | 0 | http_remove_connection(hs); |
519 | 0 | HTTP_FREE_HTTP_STATE(hs); |
520 | 0 | } |
521 | 0 | } |
522 | | |
523 | | /** Call tcp_write() in a loop trying smaller and smaller length |
524 | | * |
525 | | * @param pcb altcp_pcb to send |
526 | | * @param ptr Data to send |
527 | | * @param length Length of data to send (in/out: on return, contains the |
528 | | * amount of data sent) |
529 | | * @param apiflags directly passed to tcp_write |
530 | | * @return the return value of tcp_write |
531 | | */ |
532 | | static err_t |
533 | | http_write(struct altcp_pcb *pcb, const void *ptr, u16_t *length, u8_t apiflags) |
534 | 0 | { |
535 | 0 | u16_t len, max_len; |
536 | 0 | err_t err; |
537 | 0 | LWIP_ASSERT("length != NULL", length != NULL); |
538 | 0 | len = *length; |
539 | 0 | if (len == 0) { |
540 | 0 | return ERR_OK; |
541 | 0 | } |
542 | | /* We cannot send more data than space available in the send buffer. */ |
543 | 0 | max_len = altcp_sndbuf(pcb); |
544 | 0 | if (max_len < len) { |
545 | 0 | len = max_len; |
546 | 0 | } |
547 | 0 | #ifdef HTTPD_MAX_WRITE_LEN |
548 | | /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ |
549 | 0 | max_len = HTTPD_MAX_WRITE_LEN(pcb); |
550 | 0 | if (len > max_len) { |
551 | 0 | len = max_len; |
552 | 0 | } |
553 | 0 | #endif /* HTTPD_MAX_WRITE_LEN */ |
554 | 0 | do { |
555 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying to send %d bytes\n", len)); |
556 | 0 | err = altcp_write(pcb, ptr, len, apiflags); |
557 | 0 | if (err == ERR_MEM) { |
558 | 0 | if ((altcp_sndbuf(pcb) == 0) || |
559 | 0 | (altcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) { |
560 | | /* no need to try smaller sizes */ |
561 | 0 | len = 1; |
562 | 0 | } else { |
563 | 0 | len /= 2; |
564 | 0 | } |
565 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, |
566 | 0 | ("Send failed, trying less (%d bytes)\n", len)); |
567 | 0 | } |
568 | 0 | } while ((err == ERR_MEM) && (len > 1)); |
569 | |
|
570 | 0 | if (err == ERR_OK) { |
571 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); |
572 | 0 | *length = len; |
573 | 0 | } else { |
574 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); |
575 | 0 | *length = 0; |
576 | 0 | } |
577 | |
|
578 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
579 | | /* ensure nagle is normally enabled (only disabled for persistent connections |
580 | | when all data has been enqueued but the connection stays open for the next |
581 | | request */ |
582 | | altcp_nagle_enable(pcb); |
583 | | #endif |
584 | |
|
585 | 0 | return err; |
586 | 0 | } |
587 | | |
588 | | /** |
589 | | * The connection shall be actively closed (using RST to close from fault states). |
590 | | * Reset the sent- and recv-callbacks. |
591 | | * |
592 | | * @param pcb the tcp pcb to reset callbacks |
593 | | * @param hs connection state to free |
594 | | */ |
595 | | static err_t |
596 | | http_close_or_abort_conn(struct altcp_pcb *pcb, struct http_state *hs, u8_t abort_conn) |
597 | 0 | { |
598 | 0 | err_t err; |
599 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void *)pcb)); |
600 | |
|
601 | | #if LWIP_HTTPD_SUPPORT_POST |
602 | | if (hs != NULL) { |
603 | | if ((hs->post_content_len_left != 0) |
604 | | #if LWIP_HTTPD_POST_MANUAL_WND |
605 | | || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0)) |
606 | | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ |
607 | | ) { |
608 | | /* make sure the post code knows that the connection is closed */ |
609 | | http_uri_buf[0] = 0; |
610 | | httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); |
611 | | } |
612 | | } |
613 | | #endif /* LWIP_HTTPD_SUPPORT_POST*/ |
614 | | |
615 | |
|
616 | 0 | altcp_arg(pcb, NULL); |
617 | 0 | altcp_recv(pcb, NULL); |
618 | 0 | altcp_err(pcb, NULL); |
619 | 0 | altcp_poll(pcb, NULL, 0); |
620 | 0 | altcp_sent(pcb, NULL); |
621 | 0 | if (hs != NULL) { |
622 | 0 | http_state_free(hs); |
623 | 0 | } |
624 | |
|
625 | 0 | if (abort_conn) { |
626 | 0 | altcp_abort(pcb); |
627 | 0 | return ERR_OK; |
628 | 0 | } |
629 | 0 | err = altcp_close(pcb); |
630 | 0 | if (err != ERR_OK) { |
631 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void *)pcb)); |
632 | | /* error closing, try again later in poll */ |
633 | 0 | altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); |
634 | 0 | } |
635 | 0 | return err; |
636 | 0 | } |
637 | | |
638 | | /** |
639 | | * The connection shall be actively closed. |
640 | | * Reset the sent- and recv-callbacks. |
641 | | * |
642 | | * @param pcb the tcp pcb to reset callbacks |
643 | | * @param hs connection state to free |
644 | | */ |
645 | | static err_t |
646 | | http_close_conn(struct altcp_pcb *pcb, struct http_state *hs) |
647 | 0 | { |
648 | 0 | return http_close_or_abort_conn(pcb, hs, 0); |
649 | 0 | } |
650 | | |
651 | | /** End of file: either close the connection (Connection: close) or |
652 | | * close the file (Connection: keep-alive) |
653 | | */ |
654 | | static void |
655 | | http_eof(struct altcp_pcb *pcb, struct http_state *hs) |
656 | 0 | { |
657 | | /* HTTP/1.1 persistent connection? (Not supported for SSI) */ |
658 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
659 | | if (hs->keepalive) { |
660 | | http_remove_connection(hs); |
661 | | |
662 | | http_state_eof(hs); |
663 | | http_state_init(hs); |
664 | | /* restore state: */ |
665 | | hs->pcb = pcb; |
666 | | hs->keepalive = 1; |
667 | | http_add_connection(hs); |
668 | | /* ensure nagle doesn't interfere with sending all data as fast as possible: */ |
669 | | altcp_nagle_disable(pcb); |
670 | | } else |
671 | | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ |
672 | 0 | { |
673 | 0 | http_close_conn(pcb, hs); |
674 | 0 | } |
675 | 0 | } |
676 | | |
677 | | #if LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI |
678 | | /** |
679 | | * Extract URI parameters from the parameter-part of an URI in the form |
680 | | * "test.cgi?x=y" @todo: better explanation! |
681 | | * Pointers to the parameters are stored in hs->param_vals. |
682 | | * |
683 | | * @param hs http connection state |
684 | | * @param params pointer to the NULL-terminated parameter string from the URI |
685 | | * @return number of parameters extracted |
686 | | */ |
687 | | static int |
688 | | extract_uri_parameters(struct http_state *hs, char *params) |
689 | | { |
690 | | char *pair; |
691 | | char *equals; |
692 | | int loop; |
693 | | |
694 | | LWIP_UNUSED_ARG(hs); |
695 | | |
696 | | /* If we have no parameters at all, return immediately. */ |
697 | | if (!params || (params[0] == '\0')) { |
698 | | return (0); |
699 | | } |
700 | | |
701 | | /* Get a pointer to our first parameter */ |
702 | | pair = params; |
703 | | |
704 | | /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the |
705 | | * remainder (if any) */ |
706 | | for (loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) { |
707 | | |
708 | | /* Save the name of the parameter */ |
709 | | http_cgi_params[loop] = pair; |
710 | | |
711 | | /* Remember the start of this name=value pair */ |
712 | | equals = pair; |
713 | | |
714 | | /* Find the start of the next name=value pair and replace the delimiter |
715 | | * with a 0 to terminate the previous pair string. */ |
716 | | pair = strchr(pair, '&'); |
717 | | if (pair) { |
718 | | *pair = '\0'; |
719 | | pair++; |
720 | | } else { |
721 | | /* We didn't find a new parameter so find the end of the URI and |
722 | | * replace the space with a '\0' */ |
723 | | pair = strchr(equals, ' '); |
724 | | if (pair) { |
725 | | *pair = '\0'; |
726 | | } |
727 | | |
728 | | /* Revert to NULL so that we exit the loop as expected. */ |
729 | | pair = NULL; |
730 | | } |
731 | | |
732 | | /* Now find the '=' in the previous pair, replace it with '\0' and save |
733 | | * the parameter value string. */ |
734 | | equals = strchr(equals, '='); |
735 | | if (equals) { |
736 | | *equals = '\0'; |
737 | | http_cgi_param_vals[loop] = equals + 1; |
738 | | } else { |
739 | | http_cgi_param_vals[loop] = NULL; |
740 | | } |
741 | | } |
742 | | |
743 | | return loop; |
744 | | } |
745 | | #endif /* LWIP_HTTPD_CGI || LWIP_HTTPD_CGI_SSI */ |
746 | | |
747 | | #if LWIP_HTTPD_SSI |
748 | | /** |
749 | | * Insert a tag (found in an shtml in the form of "<!--#tagname-->" into the file. |
750 | | * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement |
751 | | * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN). |
752 | | * The amount of data written is stored to ssi->tag_insert_len. |
753 | | * |
754 | | * @todo: return tag_insert_len - maybe it can be removed from struct http_state? |
755 | | * |
756 | | * @param hs http connection state |
757 | | */ |
758 | | static void |
759 | | get_tag_insert(struct http_state *hs) |
760 | | { |
761 | | #if LWIP_HTTPD_SSI_RAW |
762 | | const char *tag; |
763 | | #else /* LWIP_HTTPD_SSI_RAW */ |
764 | | int tag; |
765 | | #endif /* LWIP_HTTPD_SSI_RAW */ |
766 | | size_t len; |
767 | | struct http_ssi_state *ssi; |
768 | | #if LWIP_HTTPD_SSI_MULTIPART |
769 | | u16_t current_tag_part; |
770 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
771 | | |
772 | | LWIP_ASSERT("hs != NULL", hs != NULL); |
773 | | ssi = hs->ssi; |
774 | | LWIP_ASSERT("ssi != NULL", ssi != NULL); |
775 | | #if LWIP_HTTPD_SSI_MULTIPART |
776 | | current_tag_part = ssi->tag_part; |
777 | | ssi->tag_part = HTTPD_LAST_TAG_PART; |
778 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
779 | | #if LWIP_HTTPD_SSI_RAW |
780 | | tag = ssi->tag_name; |
781 | | #endif |
782 | | |
783 | | if (httpd_ssi_handler |
784 | | #if !LWIP_HTTPD_SSI_RAW |
785 | | && httpd_tags && httpd_num_tags |
786 | | #endif /* !LWIP_HTTPD_SSI_RAW */ |
787 | | ) { |
788 | | |
789 | | /* Find this tag in the list we have been provided. */ |
790 | | #if LWIP_HTTPD_SSI_RAW |
791 | | { |
792 | | #else /* LWIP_HTTPD_SSI_RAW */ |
793 | | for (tag = 0; tag < httpd_num_tags; tag++) { |
794 | | if (strcmp(ssi->tag_name, httpd_tags[tag]) == 0) |
795 | | #endif /* LWIP_HTTPD_SSI_RAW */ |
796 | | { |
797 | | ssi->tag_insert_len = httpd_ssi_handler(tag, ssi->tag_insert, |
798 | | LWIP_HTTPD_MAX_TAG_INSERT_LEN |
799 | | #if LWIP_HTTPD_SSI_MULTIPART |
800 | | , current_tag_part, &ssi->tag_part |
801 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
802 | | #if LWIP_HTTPD_FILE_STATE |
803 | | , (hs->handle ? hs->handle->state : NULL) |
804 | | #endif /* LWIP_HTTPD_FILE_STATE */ |
805 | | ); |
806 | | #if LWIP_HTTPD_SSI_RAW |
807 | | if (ssi->tag_insert_len != HTTPD_SSI_TAG_UNKNOWN) |
808 | | #endif /* LWIP_HTTPD_SSI_RAW */ |
809 | | { |
810 | | return; |
811 | | } |
812 | | } |
813 | | } |
814 | | } |
815 | | |
816 | | /* If we drop out, we were asked to serve a page which contains tags that |
817 | | * we don't have a handler for. Merely echo back the tags with an error |
818 | | * marker. */ |
819 | | #define UNKNOWN_TAG1_TEXT "<b>***UNKNOWN TAG " |
820 | | #define UNKNOWN_TAG1_LEN 18 |
821 | | #define UNKNOWN_TAG2_TEXT "***</b>" |
822 | | #define UNKNOWN_TAG2_LEN 7 |
823 | | len = LWIP_MIN(sizeof(ssi->tag_name), LWIP_MIN(strlen(ssi->tag_name), |
824 | | LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN))); |
825 | | MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN); |
826 | | MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len); |
827 | | MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN); |
828 | | ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0; |
829 | | |
830 | | len = strlen(ssi->tag_insert); |
831 | | LWIP_ASSERT("len <= 0xffff", len <= 0xffff); |
832 | | ssi->tag_insert_len = (u16_t)len; |
833 | | } |
834 | | #endif /* LWIP_HTTPD_SSI */ |
835 | | |
836 | | #if LWIP_HTTPD_DYNAMIC_HEADERS |
837 | | /** |
838 | | * Generate the relevant HTTP headers for the given filename and write |
839 | | * them into the supplied buffer. |
840 | | */ |
841 | | static void |
842 | | get_http_headers(struct http_state *hs, const char *uri) |
843 | | { |
844 | | size_t content_type; |
845 | | char *tmp; |
846 | | char *ext; |
847 | | char *vars; |
848 | | |
849 | | /* In all cases, the second header we send is the server identification |
850 | | so set it here. */ |
851 | | hs->hdrs[HDR_STRINGS_IDX_SERVER_NAME] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; |
852 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = NULL; |
853 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = NULL; |
854 | | |
855 | | /* Is this a normal file or the special case we use to send back the |
856 | | default "404: Page not found" response? */ |
857 | | if (uri == NULL) { |
858 | | hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; |
859 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
860 | | if (hs->keepalive) { |
861 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML_PERSISTENT]; |
862 | | } else |
863 | | #endif |
864 | | { |
865 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaderStrings[DEFAULT_404_HTML]; |
866 | | } |
867 | | |
868 | | /* Set up to send the first header string. */ |
869 | | hs->hdr_index = 0; |
870 | | hs->hdr_pos = 0; |
871 | | return; |
872 | | } |
873 | | /* We are dealing with a particular filename. Look for one other |
874 | | special case. We assume that any filename with "404" in it must be |
875 | | indicative of a 404 server error whereas all other files require |
876 | | the 200 OK header. */ |
877 | | if (strstr(uri, "404")) { |
878 | | hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; |
879 | | } else if (strstr(uri, "400")) { |
880 | | hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST]; |
881 | | } else if (strstr(uri, "501")) { |
882 | | hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL]; |
883 | | } else { |
884 | | hs->hdrs[HDR_STRINGS_IDX_HTTP_STATUS] = g_psHTTPHeaderStrings[HTTP_HDR_OK]; |
885 | | } |
886 | | |
887 | | /* Determine if the URI has any variables and, if so, temporarily remove |
888 | | them. */ |
889 | | vars = strchr(uri, '?'); |
890 | | if (vars) { |
891 | | *vars = '\0'; |
892 | | } |
893 | | |
894 | | /* Get a pointer to the file extension. We find this by looking for the |
895 | | last occurrence of "." in the filename passed. */ |
896 | | ext = NULL; |
897 | | tmp = strchr(uri, '.'); |
898 | | while (tmp) { |
899 | | ext = tmp + 1; |
900 | | tmp = strchr(ext, '.'); |
901 | | } |
902 | | if (ext != NULL) { |
903 | | /* Now determine the content type and add the relevant header for that. */ |
904 | | for (content_type = 0; content_type < NUM_HTTP_HEADERS; content_type++) { |
905 | | /* Have we found a matching extension? */ |
906 | | if (!lwip_stricmp(g_psHTTPHeaders[content_type].extension, ext)) { |
907 | | break; |
908 | | } |
909 | | } |
910 | | } else { |
911 | | content_type = NUM_HTTP_HEADERS; |
912 | | } |
913 | | |
914 | | /* Reinstate the parameter marker if there was one in the original URI. */ |
915 | | if (vars) { |
916 | | *vars = '?'; |
917 | | } |
918 | | |
919 | | #if LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI |
920 | | /* Does the URL passed have any file extension? If not, we assume it |
921 | | is a special-case URL used for control state notification and we do |
922 | | not send any HTTP headers with the response. */ |
923 | | if (!ext) { |
924 | | /* Force the header index to a value indicating that all headers |
925 | | have already been sent. */ |
926 | | hs->hdr_index = NUM_FILE_HDR_STRINGS; |
927 | | return; |
928 | | } |
929 | | #endif /* LWIP_HTTPD_OMIT_HEADER_FOR_EXTENSIONLESS_URI */ |
930 | | /* Did we find a matching extension? */ |
931 | | if (content_type < NUM_HTTP_HEADERS) { |
932 | | /* yes, store it */ |
933 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = g_psHTTPHeaders[content_type].content_type; |
934 | | } else if (!ext) { |
935 | | /* no, no extension found -> use binary transfer to prevent the browser adding '.txt' on save */ |
936 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_APP; |
937 | | } else { |
938 | | /* No - use the default, plain text file type. */ |
939 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_TYPE] = HTTP_HDR_DEFAULT_TYPE; |
940 | | } |
941 | | /* Set up to send the first header string. */ |
942 | | hs->hdr_index = 0; |
943 | | hs->hdr_pos = 0; |
944 | | } |
945 | | |
946 | | /* Add content-length header? */ |
947 | | static void |
948 | | get_http_content_length(struct http_state *hs) |
949 | | { |
950 | | u8_t add_content_len = 0; |
951 | | |
952 | | LWIP_ASSERT("already been here?", hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL); |
953 | | |
954 | | add_content_len = 0; |
955 | | #if LWIP_HTTPD_SSI |
956 | | if (hs->ssi == NULL) /* @todo: get maximum file length from SSI */ |
957 | | #endif /* LWIP_HTTPD_SSI */ |
958 | | { |
959 | | if ((hs->handle != NULL) && (hs->handle->flags & FS_FILE_FLAGS_HEADER_PERSISTENT)) { |
960 | | add_content_len = 1; |
961 | | } |
962 | | } |
963 | | if (add_content_len) { |
964 | | size_t len; |
965 | | lwip_itoa(hs->hdr_content_len, (size_t)LWIP_HTTPD_MAX_CONTENT_LEN_SIZE, |
966 | | hs->handle->len); |
967 | | len = strlen(hs->hdr_content_len); |
968 | | if (len <= LWIP_HTTPD_MAX_CONTENT_LEN_SIZE - LWIP_HTTPD_MAX_CONTENT_LEN_OFFSET) { |
969 | | SMEMCPY(&hs->hdr_content_len[len], CRLF, 3); |
970 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_NR] = hs->hdr_content_len; |
971 | | } else { |
972 | | add_content_len = 0; |
973 | | } |
974 | | } |
975 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
976 | | if (add_content_len) { |
977 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_KEEPALIVE_LEN]; |
978 | | } else { |
979 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONN_CLOSE]; |
980 | | hs->keepalive = 0; |
981 | | } |
982 | | #else /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ |
983 | | if (add_content_len) { |
984 | | hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] = g_psHTTPHeaderStrings[HTTP_HDR_CONTENT_LENGTH]; |
985 | | } |
986 | | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ |
987 | | } |
988 | | |
989 | | /** Sub-function of http_send(): send dynamic headers |
990 | | * |
991 | | * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued |
992 | | * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body |
993 | | * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending, |
994 | | * so don't send HTTP body yet |
995 | | * - HTTP_DATA_TO_SEND_FREED: http_state and pcb are already freed |
996 | | */ |
997 | | static u8_t |
998 | | http_send_headers(struct altcp_pcb *pcb, struct http_state *hs) |
999 | | { |
1000 | | err_t err; |
1001 | | u16_t len; |
1002 | | u8_t data_to_send = HTTP_NO_DATA_TO_SEND; |
1003 | | u16_t hdrlen, sendlen; |
1004 | | |
1005 | | if (hs->hdrs[HDR_STRINGS_IDX_CONTENT_LEN_KEEPALIVE] == NULL) { |
1006 | | /* set up "content-length" and "connection:" headers */ |
1007 | | get_http_content_length(hs); |
1008 | | } |
1009 | | |
1010 | | /* How much data can we send? */ |
1011 | | len = altcp_sndbuf(pcb); |
1012 | | sendlen = len; |
1013 | | |
1014 | | while (len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { |
1015 | | const void *ptr; |
1016 | | u16_t old_sendlen; |
1017 | | u8_t apiflags; |
1018 | | /* How much do we have to send from the current header? */ |
1019 | | hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]); |
1020 | | |
1021 | | /* How much of this can we send? */ |
1022 | | sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); |
1023 | | |
1024 | | /* Send this amount of data or as much as we can given memory |
1025 | | * constraints. */ |
1026 | | ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos); |
1027 | | old_sendlen = sendlen; |
1028 | | apiflags = HTTP_IS_HDR_VOLATILE(hs, ptr); |
1029 | | if (hs->hdr_index == HDR_STRINGS_IDX_CONTENT_LEN_NR) { |
1030 | | /* content-length is always volatile */ |
1031 | | apiflags |= TCP_WRITE_FLAG_COPY; |
1032 | | } |
1033 | | if (hs->hdr_index < NUM_FILE_HDR_STRINGS - 1) { |
1034 | | apiflags |= TCP_WRITE_FLAG_MORE; |
1035 | | } |
1036 | | err = http_write(pcb, ptr, &sendlen, apiflags); |
1037 | | if ((err == ERR_OK) && (old_sendlen != sendlen)) { |
1038 | | /* Remember that we added some more data to be transmitted. */ |
1039 | | data_to_send = HTTP_DATA_TO_SEND_CONTINUE; |
1040 | | } else if (err != ERR_OK) { |
1041 | | /* special case: http_write does not try to send 1 byte */ |
1042 | | sendlen = 0; |
1043 | | } |
1044 | | |
1045 | | /* Fix up the header position for the next time round. */ |
1046 | | hs->hdr_pos += sendlen; |
1047 | | len -= sendlen; |
1048 | | |
1049 | | /* Have we finished sending this string? */ |
1050 | | if (hs->hdr_pos == hdrlen) { |
1051 | | /* Yes - move on to the next one */ |
1052 | | hs->hdr_index++; |
1053 | | /* skip headers that are NULL (not all headers are required) */ |
1054 | | while ((hs->hdr_index < NUM_FILE_HDR_STRINGS) && |
1055 | | (hs->hdrs[hs->hdr_index] == NULL)) { |
1056 | | hs->hdr_index++; |
1057 | | } |
1058 | | hs->hdr_pos = 0; |
1059 | | } |
1060 | | } |
1061 | | |
1062 | | if ((hs->hdr_index >= NUM_FILE_HDR_STRINGS) && (hs->file == NULL)) { |
1063 | | /* When we are at the end of the headers, check for data to send |
1064 | | * instead of waiting for ACK from remote side to continue |
1065 | | * (which would happen when sending files from async read). */ |
1066 | | if (http_check_eof(pcb, hs)) { |
1067 | | data_to_send = HTTP_DATA_TO_SEND_BREAK; |
1068 | | } else { |
1069 | | /* At this point, for non-keepalive connections, hs is deallocated an |
1070 | | pcb is closed. */ |
1071 | | return HTTP_DATA_TO_SEND_FREED; |
1072 | | } |
1073 | | } |
1074 | | /* If we get here and there are still header bytes to send, we send |
1075 | | * the header information we just wrote immediately. If there are no |
1076 | | * more headers to send, but we do have file data to send, drop through |
1077 | | * to try to send some file data too. */ |
1078 | | if ((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { |
1079 | | LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n")); |
1080 | | return HTTP_DATA_TO_SEND_BREAK; |
1081 | | } |
1082 | | return data_to_send; |
1083 | | } |
1084 | | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
1085 | | |
1086 | | /** Sub-function of http_send(): end-of-file (or block) is reached, |
1087 | | * either close the file or read the next block (if supported). |
1088 | | * |
1089 | | * @returns: 0 if the file is finished or no data has been read |
1090 | | * 1 if the file is not finished and data has been read |
1091 | | */ |
1092 | | static u8_t |
1093 | | http_check_eof(struct altcp_pcb *pcb, struct http_state *hs) |
1094 | 0 | { |
1095 | 0 | int bytes_left; |
1096 | | #if LWIP_HTTPD_DYNAMIC_FILE_READ |
1097 | | int count; |
1098 | | #ifdef HTTPD_MAX_WRITE_LEN |
1099 | | int max_write_len; |
1100 | | #endif /* HTTPD_MAX_WRITE_LEN */ |
1101 | | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ |
1102 | | |
1103 | | /* Do we have a valid file handle? */ |
1104 | 0 | if (hs->handle == NULL) { |
1105 | | /* No - close the connection. */ |
1106 | 0 | http_eof(pcb, hs); |
1107 | 0 | return 0; |
1108 | 0 | } |
1109 | 0 | bytes_left = fs_bytes_left(hs->handle); |
1110 | 0 | if (bytes_left <= 0) { |
1111 | | /* We reached the end of the file so this request is done. */ |
1112 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); |
1113 | 0 | http_eof(pcb, hs); |
1114 | 0 | return 0; |
1115 | 0 | } |
1116 | | #if LWIP_HTTPD_DYNAMIC_FILE_READ |
1117 | | /* Do we already have a send buffer allocated? */ |
1118 | | if (hs->buf) { |
1119 | | /* Yes - get the length of the buffer */ |
1120 | | count = LWIP_MIN(hs->buf_len, bytes_left); |
1121 | | } else { |
1122 | | /* We don't have a send buffer so allocate one now */ |
1123 | | count = altcp_sndbuf(pcb); |
1124 | | if (bytes_left < count) { |
1125 | | count = bytes_left; |
1126 | | } |
1127 | | #ifdef HTTPD_MAX_WRITE_LEN |
1128 | | /* Additional limitation: e.g. don't enqueue more than 2*mss at once */ |
1129 | | max_write_len = HTTPD_MAX_WRITE_LEN(pcb); |
1130 | | if (count > max_write_len) { |
1131 | | count = max_write_len; |
1132 | | } |
1133 | | #endif /* HTTPD_MAX_WRITE_LEN */ |
1134 | | do { |
1135 | | hs->buf = (char *)mem_malloc((mem_size_t)count); |
1136 | | if (hs->buf != NULL) { |
1137 | | hs->buf_len = count; |
1138 | | break; |
1139 | | } |
1140 | | count = count / 2; |
1141 | | } while (count > 100); |
1142 | | |
1143 | | /* Did we get a send buffer? If not, return immediately. */ |
1144 | | if (hs->buf == NULL) { |
1145 | | LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n")); |
1146 | | return 0; |
1147 | | } |
1148 | | } |
1149 | | |
1150 | | /* Read a block of data from the file. */ |
1151 | | LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count)); |
1152 | | |
1153 | | #if LWIP_HTTPD_FS_ASYNC_READ |
1154 | | count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs); |
1155 | | #else /* LWIP_HTTPD_FS_ASYNC_READ */ |
1156 | | count = fs_read(hs->handle, hs->buf, count); |
1157 | | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ |
1158 | | if (count < 0) { |
1159 | | if (count == FS_READ_DELAYED) { |
1160 | | /* Delayed read, wait for FS to unblock us */ |
1161 | | return 0; |
1162 | | } |
1163 | | /* We reached the end of the file so this request is done. |
1164 | | * @todo: close here for HTTP/1.1 when reading file fails */ |
1165 | | LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); |
1166 | | http_eof(pcb, hs); |
1167 | | return 0; |
1168 | | } |
1169 | | |
1170 | | /* Set up to send the block of data we just read */ |
1171 | | LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count)); |
1172 | | hs->left = count; |
1173 | | hs->file = hs->buf; |
1174 | | #if LWIP_HTTPD_SSI |
1175 | | if (hs->ssi) { |
1176 | | hs->ssi->parse_left = count; |
1177 | | hs->ssi->parsed = hs->buf; |
1178 | | } |
1179 | | #endif /* LWIP_HTTPD_SSI */ |
1180 | | #else /* LWIP_HTTPD_DYNAMIC_FILE_READ */ |
1181 | 0 | LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0); |
1182 | 0 | #endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */ |
1183 | 0 | return 1; |
1184 | 0 | } |
1185 | | |
1186 | | /** Sub-function of http_send(): This is the normal send-routine for non-ssi files |
1187 | | * |
1188 | | * @returns: - 1: data has been written (so call tcp_ouput) |
1189 | | * - 0: no data has been written (no need to call tcp_output) |
1190 | | */ |
1191 | | static u8_t |
1192 | | http_send_data_nonssi(struct altcp_pcb *pcb, struct http_state *hs) |
1193 | 0 | { |
1194 | 0 | err_t err; |
1195 | 0 | u16_t len; |
1196 | 0 | u8_t data_to_send = 0; |
1197 | | |
1198 | | /* We are not processing an SHTML file so no tag checking is necessary. |
1199 | | * Just send the data as we received it from the file. */ |
1200 | 0 | len = (u16_t)LWIP_MIN(hs->left, 0xffff); |
1201 | |
|
1202 | 0 | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); |
1203 | 0 | if (err == ERR_OK) { |
1204 | 0 | data_to_send = 1; |
1205 | 0 | hs->file += len; |
1206 | 0 | hs->left -= len; |
1207 | 0 | } |
1208 | |
|
1209 | 0 | return data_to_send; |
1210 | 0 | } |
1211 | | |
1212 | | #if LWIP_HTTPD_SSI |
1213 | | /** Sub-function of http_send(): This is the send-routine for ssi files |
1214 | | * |
1215 | | * @returns: - 1: data has been written (so call tcp_ouput) |
1216 | | * - 0: no data has been written (no need to call tcp_output) |
1217 | | */ |
1218 | | static u8_t |
1219 | | http_send_data_ssi(struct altcp_pcb *pcb, struct http_state *hs) |
1220 | | { |
1221 | | err_t err = ERR_OK; |
1222 | | u16_t len; |
1223 | | u8_t data_to_send = 0; |
1224 | | u8_t tag_type; |
1225 | | |
1226 | | struct http_ssi_state *ssi = hs->ssi; |
1227 | | LWIP_ASSERT("ssi != NULL", ssi != NULL); |
1228 | | /* We are processing an SHTML file so need to scan for tags and replace |
1229 | | * them with insert strings. We need to be careful here since a tag may |
1230 | | * straddle the boundary of two blocks read from the file and we may also |
1231 | | * have to split the insert string between two tcp_write operations. */ |
1232 | | |
1233 | | /* How much data could we send? */ |
1234 | | len = altcp_sndbuf(pcb); |
1235 | | |
1236 | | /* Do we have remaining data to send before parsing more? */ |
1237 | | if (ssi->parsed > hs->file) { |
1238 | | len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); |
1239 | | |
1240 | | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); |
1241 | | if (err == ERR_OK) { |
1242 | | data_to_send = 1; |
1243 | | hs->file += len; |
1244 | | hs->left -= len; |
1245 | | } |
1246 | | |
1247 | | /* If the send buffer is full, return now. */ |
1248 | | if (altcp_sndbuf(pcb) == 0) { |
1249 | | return data_to_send; |
1250 | | } |
1251 | | } |
1252 | | |
1253 | | LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left)); |
1254 | | |
1255 | | /* We have sent all the data that was already parsed so continue parsing |
1256 | | * the buffer contents looking for SSI tags. */ |
1257 | | while (((ssi->tag_state == TAG_SENDING) || ssi->parse_left) && (err == ERR_OK)) { |
1258 | | if (len == 0) { |
1259 | | return data_to_send; |
1260 | | } |
1261 | | switch (ssi->tag_state) { |
1262 | | case TAG_NONE: |
1263 | | /* We are not currently processing an SSI tag so scan for the |
1264 | | * start of the lead-in marker. */ |
1265 | | for (tag_type = 0; tag_type < LWIP_ARRAYSIZE(http_ssi_tag_desc); tag_type++) { |
1266 | | if (*ssi->parsed == http_ssi_tag_desc[tag_type].lead_in[0]) { |
1267 | | /* We found what could be the lead-in for a new tag so change |
1268 | | * state appropriately. */ |
1269 | | ssi->tag_type = tag_type; |
1270 | | ssi->tag_state = TAG_LEADIN; |
1271 | | ssi->tag_index = 1; |
1272 | | #if !LWIP_HTTPD_SSI_INCLUDE_TAG |
1273 | | ssi->tag_started = ssi->parsed; |
1274 | | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ |
1275 | | break; |
1276 | | } |
1277 | | } |
1278 | | |
1279 | | /* Move on to the next character in the buffer */ |
1280 | | ssi->parse_left--; |
1281 | | ssi->parsed++; |
1282 | | break; |
1283 | | |
1284 | | case TAG_LEADIN: |
1285 | | /* We are processing the lead-in marker, looking for the start of |
1286 | | * the tag name. */ |
1287 | | |
1288 | | /* Have we reached the end of the leadin? */ |
1289 | | if (http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index] == 0) { |
1290 | | ssi->tag_index = 0; |
1291 | | ssi->tag_state = TAG_FOUND; |
1292 | | } else { |
1293 | | /* Have we found the next character we expect for the tag leadin? */ |
1294 | | if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_in[ssi->tag_index]) { |
1295 | | /* Yes - move to the next one unless we have found the complete |
1296 | | * leadin, in which case we start looking for the tag itself */ |
1297 | | ssi->tag_index++; |
1298 | | } else { |
1299 | | /* We found an unexpected character so this is not a tag. Move |
1300 | | * back to idle state. */ |
1301 | | ssi->tag_state = TAG_NONE; |
1302 | | } |
1303 | | |
1304 | | /* Move on to the next character in the buffer */ |
1305 | | ssi->parse_left--; |
1306 | | ssi->parsed++; |
1307 | | } |
1308 | | break; |
1309 | | |
1310 | | case TAG_FOUND: |
1311 | | /* We are reading the tag name, looking for the start of the |
1312 | | * lead-out marker and removing any whitespace found. */ |
1313 | | |
1314 | | /* Remove leading whitespace between the tag leading and the first |
1315 | | * tag name character. */ |
1316 | | if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || |
1317 | | (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || |
1318 | | (*ssi->parsed == '\r'))) { |
1319 | | /* Move on to the next character in the buffer */ |
1320 | | ssi->parse_left--; |
1321 | | ssi->parsed++; |
1322 | | break; |
1323 | | } |
1324 | | |
1325 | | /* Have we found the end of the tag name? This is signalled by |
1326 | | * us finding the first leadout character or whitespace */ |
1327 | | if ((*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) || |
1328 | | (*ssi->parsed == ' ') || (*ssi->parsed == '\t') || |
1329 | | (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) { |
1330 | | |
1331 | | if (ssi->tag_index == 0) { |
1332 | | /* We read a zero length tag so ignore it. */ |
1333 | | ssi->tag_state = TAG_NONE; |
1334 | | } else { |
1335 | | /* We read a non-empty tag so go ahead and look for the |
1336 | | * leadout string. */ |
1337 | | ssi->tag_state = TAG_LEADOUT; |
1338 | | LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff); |
1339 | | ssi->tag_name_len = (u8_t)ssi->tag_index; |
1340 | | ssi->tag_name[ssi->tag_index] = '\0'; |
1341 | | if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[0]) { |
1342 | | ssi->tag_index = 1; |
1343 | | } else { |
1344 | | ssi->tag_index = 0; |
1345 | | } |
1346 | | } |
1347 | | } else { |
1348 | | /* This character is part of the tag name so save it */ |
1349 | | if (ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) { |
1350 | | ssi->tag_name[ssi->tag_index++] = *ssi->parsed; |
1351 | | } else { |
1352 | | /* The tag was too long so ignore it. */ |
1353 | | ssi->tag_state = TAG_NONE; |
1354 | | } |
1355 | | } |
1356 | | |
1357 | | /* Move on to the next character in the buffer */ |
1358 | | ssi->parse_left--; |
1359 | | ssi->parsed++; |
1360 | | |
1361 | | break; |
1362 | | |
1363 | | /* We are looking for the end of the lead-out marker. */ |
1364 | | case TAG_LEADOUT: |
1365 | | /* Remove leading whitespace between the tag leading and the first |
1366 | | * tag leadout character. */ |
1367 | | if ((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || |
1368 | | (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || |
1369 | | (*ssi->parsed == '\r'))) { |
1370 | | /* Move on to the next character in the buffer */ |
1371 | | ssi->parse_left--; |
1372 | | ssi->parsed++; |
1373 | | break; |
1374 | | } |
1375 | | |
1376 | | /* Have we found the next character we expect for the tag leadout? */ |
1377 | | if (*ssi->parsed == http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index]) { |
1378 | | /* Yes - move to the next one unless we have found the complete |
1379 | | * leadout, in which case we need to call the client to process |
1380 | | * the tag. */ |
1381 | | |
1382 | | /* Move on to the next character in the buffer */ |
1383 | | ssi->parse_left--; |
1384 | | ssi->parsed++; |
1385 | | ssi->tag_index++; |
1386 | | |
1387 | | if (http_ssi_tag_desc[ssi->tag_type].lead_out[ssi->tag_index] == 0) { |
1388 | | /* Call the client to ask for the insert string for the |
1389 | | * tag we just found. */ |
1390 | | #if LWIP_HTTPD_SSI_MULTIPART |
1391 | | ssi->tag_part = 0; /* start with tag part 0 */ |
1392 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
1393 | | get_tag_insert(hs); |
1394 | | |
1395 | | /* Next time through, we are going to be sending data |
1396 | | * immediately, either the end of the block we start |
1397 | | * sending here or the insert string. */ |
1398 | | ssi->tag_index = 0; |
1399 | | ssi->tag_state = TAG_SENDING; |
1400 | | ssi->tag_end = ssi->parsed; |
1401 | | #if !LWIP_HTTPD_SSI_INCLUDE_TAG |
1402 | | ssi->parsed = ssi->tag_started; |
1403 | | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1404 | | |
1405 | | /* If there is any unsent data in the buffer prior to the |
1406 | | * tag, we need to send it now. */ |
1407 | | if (ssi->tag_end > hs->file) { |
1408 | | /* How much of the data can we send? */ |
1409 | | #if LWIP_HTTPD_SSI_INCLUDE_TAG |
1410 | | len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); |
1411 | | #else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1412 | | /* we would include the tag in sending */ |
1413 | | len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); |
1414 | | #endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1415 | | |
1416 | | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); |
1417 | | if (err == ERR_OK) { |
1418 | | data_to_send = 1; |
1419 | | #if !LWIP_HTTPD_SSI_INCLUDE_TAG |
1420 | | if (ssi->tag_started <= hs->file) { |
1421 | | /* pretend to have sent the tag, too */ |
1422 | | len += (u16_t)(ssi->tag_end - ssi->tag_started); |
1423 | | } |
1424 | | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1425 | | hs->file += len; |
1426 | | hs->left -= len; |
1427 | | } |
1428 | | } |
1429 | | } |
1430 | | } else { |
1431 | | /* We found an unexpected character so this is not a tag. Move |
1432 | | * back to idle state. */ |
1433 | | ssi->parse_left--; |
1434 | | ssi->parsed++; |
1435 | | ssi->tag_state = TAG_NONE; |
1436 | | } |
1437 | | break; |
1438 | | |
1439 | | /* |
1440 | | * We have found a valid tag and are in the process of sending |
1441 | | * data as a result of that discovery. We send either remaining data |
1442 | | * from the file prior to the insert point or the insert string itself. |
1443 | | */ |
1444 | | case TAG_SENDING: |
1445 | | /* Do we have any remaining file data to send from the buffer prior |
1446 | | * to the tag? */ |
1447 | | if (ssi->tag_end > hs->file) { |
1448 | | /* How much of the data can we send? */ |
1449 | | #if LWIP_HTTPD_SSI_INCLUDE_TAG |
1450 | | len = (u16_t)LWIP_MIN(ssi->tag_end - hs->file, 0xffff); |
1451 | | #else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1452 | | LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file); |
1453 | | /* we would include the tag in sending */ |
1454 | | len = (u16_t)LWIP_MIN(ssi->tag_started - hs->file, 0xffff); |
1455 | | #endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1456 | | if (len != 0) { |
1457 | | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); |
1458 | | } else { |
1459 | | err = ERR_OK; |
1460 | | } |
1461 | | if (err == ERR_OK) { |
1462 | | data_to_send = 1; |
1463 | | #if !LWIP_HTTPD_SSI_INCLUDE_TAG |
1464 | | if (ssi->tag_started <= hs->file) { |
1465 | | /* pretend to have sent the tag, too */ |
1466 | | len += (u16_t)(ssi->tag_end - ssi->tag_started); |
1467 | | } |
1468 | | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1469 | | hs->file += len; |
1470 | | hs->left -= len; |
1471 | | } |
1472 | | } else { |
1473 | | #if LWIP_HTTPD_SSI_MULTIPART |
1474 | | if (ssi->tag_index >= ssi->tag_insert_len) { |
1475 | | /* Did the last SSIHandler have more to send? */ |
1476 | | if (ssi->tag_part != HTTPD_LAST_TAG_PART) { |
1477 | | /* If so, call it again */ |
1478 | | ssi->tag_index = 0; |
1479 | | get_tag_insert(hs); |
1480 | | } |
1481 | | } |
1482 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
1483 | | |
1484 | | /* Do we still have insert data left to send? */ |
1485 | | if (ssi->tag_index < ssi->tag_insert_len) { |
1486 | | /* We are sending the insert string itself. How much of the |
1487 | | * insert can we send? */ |
1488 | | len = (ssi->tag_insert_len - ssi->tag_index); |
1489 | | |
1490 | | /* Note that we set the copy flag here since we only have a |
1491 | | * single tag insert buffer per connection. If we don't do |
1492 | | * this, insert corruption can occur if more than one insert |
1493 | | * is processed before we call tcp_output. */ |
1494 | | err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len, |
1495 | | HTTP_IS_TAG_VOLATILE(hs)); |
1496 | | if (err == ERR_OK) { |
1497 | | data_to_send = 1; |
1498 | | ssi->tag_index += len; |
1499 | | /* Don't return here: keep on sending data */ |
1500 | | } |
1501 | | } else { |
1502 | | #if LWIP_HTTPD_SSI_MULTIPART |
1503 | | if (ssi->tag_part == HTTPD_LAST_TAG_PART) |
1504 | | #endif /* LWIP_HTTPD_SSI_MULTIPART */ |
1505 | | { |
1506 | | /* We have sent all the insert data so go back to looking for |
1507 | | * a new tag. */ |
1508 | | LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n")); |
1509 | | ssi->tag_index = 0; |
1510 | | ssi->tag_state = TAG_NONE; |
1511 | | #if !LWIP_HTTPD_SSI_INCLUDE_TAG |
1512 | | ssi->parsed = ssi->tag_end; |
1513 | | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ |
1514 | | } |
1515 | | } |
1516 | | break; |
1517 | | default: |
1518 | | break; |
1519 | | } |
1520 | | } |
1521 | | } |
1522 | | |
1523 | | /* If we drop out of the end of the for loop, this implies we must have |
1524 | | * file data to send so send it now. In TAG_SENDING state, we've already |
1525 | | * handled this so skip the send if that's the case. */ |
1526 | | if ((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) { |
1527 | | #if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG |
1528 | | if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) { |
1529 | | /* If we found tag on the edge of the read buffer: just throw away the first part |
1530 | | (we have copied/saved everything required for parsing on later). */ |
1531 | | len = (u16_t)(ssi->tag_started - hs->file); |
1532 | | hs->left -= (ssi->parsed - ssi->tag_started); |
1533 | | ssi->parsed = ssi->tag_started; |
1534 | | ssi->tag_started = hs->buf; |
1535 | | } else |
1536 | | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */ |
1537 | | { |
1538 | | len = (u16_t)LWIP_MIN(ssi->parsed - hs->file, 0xffff); |
1539 | | } |
1540 | | |
1541 | | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); |
1542 | | if (err == ERR_OK) { |
1543 | | data_to_send = 1; |
1544 | | hs->file += len; |
1545 | | hs->left -= len; |
1546 | | } |
1547 | | } |
1548 | | return data_to_send; |
1549 | | } |
1550 | | #endif /* LWIP_HTTPD_SSI */ |
1551 | | |
1552 | | /** |
1553 | | * Try to send more data on this pcb. |
1554 | | * |
1555 | | * @param pcb the pcb to send data |
1556 | | * @param hs connection state |
1557 | | */ |
1558 | | static u8_t |
1559 | | http_send(struct altcp_pcb *pcb, struct http_state *hs) |
1560 | 0 | { |
1561 | 0 | u8_t data_to_send = HTTP_NO_DATA_TO_SEND; |
1562 | |
|
1563 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void *)pcb, |
1564 | 0 | (void *)hs, hs != NULL ? (int)hs->left : 0)); |
1565 | |
|
1566 | | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND |
1567 | | if (hs->unrecved_bytes != 0) { |
1568 | | return 0; |
1569 | | } |
1570 | | #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ |
1571 | | |
1572 | | /* If we were passed a NULL state structure pointer, ignore the call. */ |
1573 | 0 | if (hs == NULL) { |
1574 | 0 | return 0; |
1575 | 0 | } |
1576 | | |
1577 | | #if LWIP_HTTPD_FS_ASYNC_READ |
1578 | | /* Check if we are allowed to read from this file. |
1579 | | (e.g. SSI might want to delay sending until data is available) */ |
1580 | | if (!fs_is_file_ready(hs->handle, http_continue, hs)) { |
1581 | | return 0; |
1582 | | } |
1583 | | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ |
1584 | | |
1585 | | #if LWIP_HTTPD_DYNAMIC_HEADERS |
1586 | | /* Do we have any more header data to send for this file? */ |
1587 | | if (hs->hdr_index < NUM_FILE_HDR_STRINGS) { |
1588 | | data_to_send = http_send_headers(pcb, hs); |
1589 | | if ((data_to_send == HTTP_DATA_TO_SEND_FREED) || |
1590 | | ((data_to_send != HTTP_DATA_TO_SEND_CONTINUE) && |
1591 | | (hs->hdr_index < NUM_FILE_HDR_STRINGS))) { |
1592 | | return data_to_send; |
1593 | | } |
1594 | | } |
1595 | | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
1596 | | |
1597 | | /* Have we run out of file data to send? If so, we need to read the next |
1598 | | * block from the file. */ |
1599 | 0 | if (hs->left == 0) { |
1600 | 0 | if (!http_check_eof(pcb, hs)) { |
1601 | 0 | return 0; |
1602 | 0 | } |
1603 | 0 | } |
1604 | | |
1605 | | #if LWIP_HTTPD_SSI |
1606 | | if (hs->ssi) { |
1607 | | data_to_send = http_send_data_ssi(pcb, hs); |
1608 | | } else |
1609 | | #endif /* LWIP_HTTPD_SSI */ |
1610 | 0 | { |
1611 | 0 | data_to_send = http_send_data_nonssi(pcb, hs); |
1612 | 0 | } |
1613 | |
|
1614 | 0 | if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) { |
1615 | | /* We reached the end of the file so this request is done. |
1616 | | * This adds the FIN flag right into the last data segment. */ |
1617 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); |
1618 | 0 | http_eof(pcb, hs); |
1619 | 0 | return 0; |
1620 | 0 | } |
1621 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n")); |
1622 | 0 | return data_to_send; |
1623 | 0 | } |
1624 | | |
1625 | | #if LWIP_HTTPD_SUPPORT_EXTSTATUS |
1626 | | /** Initialize a http connection with a file to send for an error message |
1627 | | * |
1628 | | * @param hs http connection state |
1629 | | * @param error_nr HTTP error number |
1630 | | * @return ERR_OK if file was found and hs has been initialized correctly |
1631 | | * another err_t otherwise |
1632 | | */ |
1633 | | static err_t |
1634 | | http_find_error_file(struct http_state *hs, u16_t error_nr) |
1635 | | { |
1636 | | const char *uri, *uri1, *uri2, *uri3; |
1637 | | |
1638 | | if (error_nr == 501) { |
1639 | | uri1 = "/501.html"; |
1640 | | uri2 = "/501.htm"; |
1641 | | uri3 = "/501.shtml"; |
1642 | | } else { |
1643 | | /* 400 (bad request is the default) */ |
1644 | | uri1 = "/400.html"; |
1645 | | uri2 = "/400.htm"; |
1646 | | uri3 = "/400.shtml"; |
1647 | | } |
1648 | | if (fs_open(&hs->file_handle, uri1) == ERR_OK) { |
1649 | | uri = uri1; |
1650 | | } else if (fs_open(&hs->file_handle, uri2) == ERR_OK) { |
1651 | | uri = uri2; |
1652 | | } else if (fs_open(&hs->file_handle, uri3) == ERR_OK) { |
1653 | | uri = uri3; |
1654 | | } else { |
1655 | | LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n", |
1656 | | error_nr)); |
1657 | | return ERR_ARG; |
1658 | | } |
1659 | | return http_init_file(hs, &hs->file_handle, 0, uri, 0, NULL); |
1660 | | } |
1661 | | #else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ |
1662 | 0 | #define http_find_error_file(hs, error_nr) ERR_ARG |
1663 | | #endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ |
1664 | | |
1665 | | /** |
1666 | | * Get the file struct for a 404 error page. |
1667 | | * Tries some file names and returns NULL if none found. |
1668 | | * |
1669 | | * @param uri pointer that receives the actual file name URI |
1670 | | * @return file struct for the error page or NULL no matching file was found |
1671 | | */ |
1672 | | static struct fs_file * |
1673 | | http_get_404_file(struct http_state *hs, const char **uri) |
1674 | 0 | { |
1675 | 0 | err_t err; |
1676 | |
|
1677 | 0 | *uri = "/404.html"; |
1678 | 0 | err = fs_open(&hs->file_handle, *uri); |
1679 | 0 | if (err != ERR_OK) { |
1680 | | /* 404.html doesn't exist. Try 404.htm instead. */ |
1681 | 0 | *uri = "/404.htm"; |
1682 | 0 | err = fs_open(&hs->file_handle, *uri); |
1683 | 0 | if (err != ERR_OK) { |
1684 | | /* 404.htm doesn't exist either. Try 404.shtml instead. */ |
1685 | 0 | *uri = "/404.shtml"; |
1686 | 0 | err = fs_open(&hs->file_handle, *uri); |
1687 | 0 | if (err != ERR_OK) { |
1688 | | /* 404.htm doesn't exist either. Indicate to the caller that it should |
1689 | | * send back a default 404 page. |
1690 | | */ |
1691 | 0 | *uri = NULL; |
1692 | 0 | return NULL; |
1693 | 0 | } |
1694 | 0 | } |
1695 | 0 | } |
1696 | | |
1697 | 0 | return &hs->file_handle; |
1698 | 0 | } |
1699 | | |
1700 | | #if LWIP_HTTPD_SUPPORT_POST |
1701 | | static err_t |
1702 | | http_handle_post_finished(struct http_state *hs) |
1703 | | { |
1704 | | #if LWIP_HTTPD_POST_MANUAL_WND |
1705 | | /* Prevent multiple calls to httpd_post_finished, since it might have already |
1706 | | been called before from httpd_post_data_recved(). */ |
1707 | | if (hs->post_finished) { |
1708 | | return ERR_OK; |
1709 | | } |
1710 | | hs->post_finished = 1; |
1711 | | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ |
1712 | | /* application error or POST finished */ |
1713 | | /* NULL-terminate the buffer */ |
1714 | | http_uri_buf[0] = 0; |
1715 | | httpd_post_finished(hs, http_uri_buf, LWIP_HTTPD_URI_BUF_LEN); |
1716 | | return http_find_file(hs, http_uri_buf, 0); |
1717 | | } |
1718 | | |
1719 | | /** Pass received POST body data to the application and correctly handle |
1720 | | * returning a response document or closing the connection. |
1721 | | * ATTENTION: The application is responsible for the pbuf now, so don't free it! |
1722 | | * |
1723 | | * @param hs http connection state |
1724 | | * @param p pbuf to pass to the application |
1725 | | * @return ERR_OK if passed successfully, another err_t if the response file |
1726 | | * hasn't been found (after POST finished) |
1727 | | */ |
1728 | | static err_t |
1729 | | http_post_rxpbuf(struct http_state *hs, struct pbuf *p) |
1730 | | { |
1731 | | err_t err; |
1732 | | |
1733 | | if (p != NULL) { |
1734 | | /* adjust remaining Content-Length */ |
1735 | | if (hs->post_content_len_left < p->tot_len) { |
1736 | | hs->post_content_len_left = 0; |
1737 | | } else { |
1738 | | hs->post_content_len_left -= p->tot_len; |
1739 | | } |
1740 | | } |
1741 | | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND |
1742 | | /* prevent connection being closed if httpd_post_data_recved() is called nested */ |
1743 | | hs->unrecved_bytes++; |
1744 | | #endif |
1745 | | if (p != NULL) { |
1746 | | err = httpd_post_receive_data(hs, p); |
1747 | | } else { |
1748 | | err = ERR_OK; |
1749 | | } |
1750 | | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND |
1751 | | hs->unrecved_bytes--; |
1752 | | #endif |
1753 | | if (err != ERR_OK) { |
1754 | | /* Ignore remaining content in case of application error */ |
1755 | | hs->post_content_len_left = 0; |
1756 | | } |
1757 | | if (hs->post_content_len_left == 0) { |
1758 | | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND |
1759 | | if (hs->unrecved_bytes != 0) { |
1760 | | return ERR_OK; |
1761 | | } |
1762 | | #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ |
1763 | | /* application error or POST finished */ |
1764 | | return http_handle_post_finished(hs); |
1765 | | } |
1766 | | |
1767 | | return ERR_OK; |
1768 | | } |
1769 | | |
1770 | | /** Handle a post request. Called from http_parse_request when method 'POST' |
1771 | | * is found. |
1772 | | * |
1773 | | * @param p The input pbuf (containing the POST header and body). |
1774 | | * @param hs The http connection state. |
1775 | | * @param data HTTP request (header and part of body) from input pbuf(s). |
1776 | | * @param data_len Size of 'data'. |
1777 | | * @param uri The HTTP URI parsed from input pbuf(s). |
1778 | | * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP |
1779 | | * header starts). |
1780 | | * @return ERR_OK: POST correctly parsed and accepted by the application. |
1781 | | * ERR_INPROGRESS: POST not completely parsed (no error yet) |
1782 | | * another err_t: Error parsing POST or denied by the application |
1783 | | */ |
1784 | | static err_t |
1785 | | http_post_request(struct pbuf *inp, struct http_state *hs, |
1786 | | char *data, u16_t data_len, char *uri, char *uri_end) |
1787 | | { |
1788 | | err_t err; |
1789 | | /* search for end-of-header (first double-CRLF) */ |
1790 | | char *crlfcrlf = lwip_strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data)); |
1791 | | |
1792 | | if (crlfcrlf != NULL) { |
1793 | | /* search for "Content-Length: " */ |
1794 | | #define HTTP_HDR_CONTENT_LEN "Content-Length: " |
1795 | | #define HTTP_HDR_CONTENT_LEN_LEN 16 |
1796 | | #define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10 |
1797 | | char *scontent_len = lwip_strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1)); |
1798 | | if (scontent_len != NULL) { |
1799 | | char *scontent_len_end = lwip_strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN); |
1800 | | if (scontent_len_end != NULL) { |
1801 | | int content_len; |
1802 | | char *content_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN; |
1803 | | content_len = atoi(content_len_num); |
1804 | | if (content_len == 0) { |
1805 | | /* if atoi returns 0 on error, fix this */ |
1806 | | if ((content_len_num[0] != '0') || (content_len_num[1] != '\r')) { |
1807 | | content_len = -1; |
1808 | | } |
1809 | | } |
1810 | | if (content_len >= 0) { |
1811 | | /* adjust length of HTTP header passed to application */ |
1812 | | const char *hdr_start_after_uri = uri_end + 1; |
1813 | | u16_t hdr_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - data); |
1814 | | u16_t hdr_data_len = (u16_t)LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri); |
1815 | | u8_t post_auto_wnd = 1; |
1816 | | http_uri_buf[0] = 0; |
1817 | | /* trim http header */ |
1818 | | *crlfcrlf = 0; |
1819 | | err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len, |
1820 | | http_uri_buf, LWIP_HTTPD_URI_BUF_LEN, &post_auto_wnd); |
1821 | | if (err == ERR_OK) { |
1822 | | /* try to pass in data of the first pbuf(s) */ |
1823 | | struct pbuf *q = inp; |
1824 | | u16_t start_offset = hdr_len; |
1825 | | #if LWIP_HTTPD_POST_MANUAL_WND |
1826 | | hs->no_auto_wnd = !post_auto_wnd; |
1827 | | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ |
1828 | | /* set the Content-Length to be received for this POST */ |
1829 | | hs->post_content_len_left = (u32_t)content_len; |
1830 | | |
1831 | | /* get to the pbuf where the body starts */ |
1832 | | while ((q != NULL) && (q->len <= start_offset)) { |
1833 | | start_offset -= q->len; |
1834 | | q = q->next; |
1835 | | } |
1836 | | if (q != NULL) { |
1837 | | /* hide the remaining HTTP header */ |
1838 | | pbuf_remove_header(q, start_offset); |
1839 | | #if LWIP_HTTPD_POST_MANUAL_WND |
1840 | | if (!post_auto_wnd) { |
1841 | | /* already tcp_recved() this data... */ |
1842 | | hs->unrecved_bytes = q->tot_len; |
1843 | | } |
1844 | | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ |
1845 | | pbuf_ref(q); |
1846 | | return http_post_rxpbuf(hs, q); |
1847 | | } else if (hs->post_content_len_left == 0) { |
1848 | | q = pbuf_alloc(PBUF_RAW, 0, PBUF_REF); |
1849 | | return http_post_rxpbuf(hs, q); |
1850 | | } else { |
1851 | | return ERR_OK; |
1852 | | } |
1853 | | } else { |
1854 | | /* return file passed from application */ |
1855 | | return http_find_file(hs, http_uri_buf, 0); |
1856 | | } |
1857 | | } else { |
1858 | | LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", |
1859 | | content_len_num)); |
1860 | | return ERR_ARG; |
1861 | | } |
1862 | | } |
1863 | | } |
1864 | | /* If we come here, headers are fully received (double-crlf), but Content-Length |
1865 | | was not included. Since this is currently the only supported method, we have |
1866 | | to fail in this case! */ |
1867 | | LWIP_DEBUGF(HTTPD_DEBUG, ("Error when parsing Content-Length\n")); |
1868 | | return ERR_ARG; |
1869 | | } |
1870 | | /* if we come here, the POST is incomplete */ |
1871 | | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
1872 | | return ERR_INPROGRESS; |
1873 | | #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
1874 | | return ERR_ARG; |
1875 | | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
1876 | | } |
1877 | | |
1878 | | #if LWIP_HTTPD_POST_MANUAL_WND |
1879 | | /** |
1880 | | * @ingroup httpd |
1881 | | * A POST implementation can call this function to update the TCP window. |
1882 | | * This can be used to throttle data reception (e.g. when received data is |
1883 | | * programmed to flash and data is received faster than programmed). |
1884 | | * |
1885 | | * @param connection A connection handle passed to httpd_post_begin for which |
1886 | | * httpd_post_finished has *NOT* been called yet! |
1887 | | * @param recved_len Length of data received (for window update) |
1888 | | */ |
1889 | | void httpd_post_data_recved(void *connection, u16_t recved_len) |
1890 | | { |
1891 | | struct http_state *hs = (struct http_state *)connection; |
1892 | | if (hs != NULL) { |
1893 | | if (hs->no_auto_wnd) { |
1894 | | u16_t len = recved_len; |
1895 | | if (hs->unrecved_bytes >= recved_len) { |
1896 | | hs->unrecved_bytes -= recved_len; |
1897 | | } else { |
1898 | | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n")); |
1899 | | len = (u16_t)hs->unrecved_bytes; |
1900 | | hs->unrecved_bytes = 0; |
1901 | | } |
1902 | | if (hs->pcb != NULL) { |
1903 | | if (len != 0) { |
1904 | | altcp_recved(hs->pcb, len); |
1905 | | } |
1906 | | if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) { |
1907 | | /* finished handling POST */ |
1908 | | http_handle_post_finished(hs); |
1909 | | http_send(hs->pcb, hs); |
1910 | | } |
1911 | | } |
1912 | | } |
1913 | | } |
1914 | | } |
1915 | | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ |
1916 | | |
1917 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
1918 | | |
1919 | | #if LWIP_HTTPD_FS_ASYNC_READ |
1920 | | /** Try to send more data if file has been blocked before |
1921 | | * This is a callback function passed to fs_read_async(). |
1922 | | */ |
1923 | | static void |
1924 | | http_continue(void *connection) |
1925 | | { |
1926 | | struct http_state *hs = (struct http_state *)connection; |
1927 | | LWIP_ASSERT_CORE_LOCKED(); |
1928 | | if (hs && (hs->pcb) && (hs->handle)) { |
1929 | | LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL); |
1930 | | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n")); |
1931 | | if (http_send(hs->pcb, hs)) { |
1932 | | /* If we wrote anything to be sent, go ahead and send it now. */ |
1933 | | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); |
1934 | | altcp_output(hs->pcb); |
1935 | | } |
1936 | | } |
1937 | | } |
1938 | | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ |
1939 | | |
1940 | | /** |
1941 | | * When data has been received in the correct state, try to parse it |
1942 | | * as a HTTP request. |
1943 | | * |
1944 | | * @param inp the received pbuf |
1945 | | * @param hs the connection state |
1946 | | * @param pcb the altcp_pcb which received this packet |
1947 | | * @return ERR_OK if request was OK and hs has been initialized correctly |
1948 | | * ERR_INPROGRESS if request was OK so far but not fully received |
1949 | | * another err_t otherwise |
1950 | | */ |
1951 | | static err_t |
1952 | | http_parse_request(struct pbuf *inp, struct http_state *hs, struct altcp_pcb *pcb) |
1953 | 0 | { |
1954 | 0 | char *data; |
1955 | 0 | char *crlf; |
1956 | 0 | u16_t data_len; |
1957 | 0 | struct pbuf *p = inp; |
1958 | 0 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
1959 | 0 | u16_t clen; |
1960 | 0 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
1961 | | #if LWIP_HTTPD_SUPPORT_POST |
1962 | | err_t err; |
1963 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
1964 | |
|
1965 | 0 | LWIP_UNUSED_ARG(pcb); /* only used for post */ |
1966 | 0 | LWIP_ASSERT("p != NULL", p != NULL); |
1967 | 0 | LWIP_ASSERT("hs != NULL", hs != NULL); |
1968 | |
|
1969 | 0 | if ((hs->handle != NULL) || (hs->file != NULL)) { |
1970 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n")); |
1971 | | /* already sending a file */ |
1972 | | /* @todo: abort? */ |
1973 | 0 | return ERR_USE; |
1974 | 0 | } |
1975 | | |
1976 | 0 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
1977 | | |
1978 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len)); |
1979 | | |
1980 | | /* first check allowed characters in this pbuf? */ |
1981 | | |
1982 | | /* enqueue the pbuf */ |
1983 | 0 | if (hs->req == NULL) { |
1984 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n")); |
1985 | 0 | hs->req = p; |
1986 | 0 | } else { |
1987 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n")); |
1988 | 0 | pbuf_cat(hs->req, p); |
1989 | 0 | } |
1990 | | /* increase pbuf ref counter as it is freed when we return but we want to |
1991 | | keep it on the req list */ |
1992 | 0 | pbuf_ref(p); |
1993 | |
|
1994 | 0 | if (hs->req->next != NULL) { |
1995 | 0 | data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH); |
1996 | 0 | pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0); |
1997 | 0 | data = httpd_req_buf; |
1998 | 0 | } else |
1999 | 0 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
2000 | 0 | { |
2001 | 0 | data = (char *)p->payload; |
2002 | 0 | data_len = p->len; |
2003 | 0 | if (p->len != p->tot_len) { |
2004 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n")); |
2005 | 0 | } |
2006 | 0 | } |
2007 | | |
2008 | | /* received enough data for minimal request? */ |
2009 | 0 | if (data_len >= MIN_REQ_LEN) { |
2010 | | /* wait for CRLF before parsing anything */ |
2011 | 0 | crlf = lwip_strnstr(data, CRLF, data_len); |
2012 | 0 | if (crlf != NULL) { |
2013 | | #if LWIP_HTTPD_SUPPORT_POST |
2014 | | int is_post = 0; |
2015 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2016 | 0 | int is_09 = 0; |
2017 | 0 | char *sp1, *sp2; |
2018 | 0 | u16_t left_len, uri_len; |
2019 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n")); |
2020 | | /* parse method */ |
2021 | 0 | if (!strncmp(data, "GET ", 4)) { |
2022 | 0 | sp1 = data + 3; |
2023 | | /* received GET request */ |
2024 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n")); |
2025 | | #if LWIP_HTTPD_SUPPORT_POST |
2026 | | } else if (!strncmp(data, "POST ", 5)) { |
2027 | | /* store request type */ |
2028 | | is_post = 1; |
2029 | | sp1 = data + 4; |
2030 | | /* received GET request */ |
2031 | | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n")); |
2032 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2033 | 0 | } else { |
2034 | | /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ |
2035 | 0 | data[4] = 0; |
2036 | | /* unsupported method! */ |
2037 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n", |
2038 | 0 | data)); |
2039 | 0 | return http_find_error_file(hs, 501); |
2040 | 0 | } |
2041 | | /* if we come here, method is OK, parse URI */ |
2042 | 0 | left_len = (u16_t)(data_len - ((sp1 + 1) - data)); |
2043 | 0 | sp2 = lwip_strnstr(sp1 + 1, " ", left_len); |
2044 | 0 | #if LWIP_HTTPD_SUPPORT_V09 |
2045 | 0 | if (sp2 == NULL) { |
2046 | | /* HTTP 0.9: respond with correct protocol version */ |
2047 | 0 | sp2 = lwip_strnstr(sp1 + 1, CRLF, left_len); |
2048 | 0 | is_09 = 1; |
2049 | | #if LWIP_HTTPD_SUPPORT_POST |
2050 | | if (is_post) { |
2051 | | /* HTTP/0.9 does not support POST */ |
2052 | | goto badrequest; |
2053 | | } |
2054 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2055 | 0 | } |
2056 | 0 | #endif /* LWIP_HTTPD_SUPPORT_V09 */ |
2057 | 0 | uri_len = (u16_t)(sp2 - (sp1 + 1)); |
2058 | 0 | if ((sp2 != 0) && (sp2 > sp1)) { |
2059 | | /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */ |
2060 | 0 | if (lwip_strnstr(data, CRLF CRLF, data_len) != NULL) { |
2061 | 0 | char *uri = sp1 + 1; |
2062 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
2063 | | /* This is HTTP/1.0 compatible: for strict 1.1, a connection |
2064 | | would always be persistent unless "close" was specified. */ |
2065 | | if (!is_09 && (lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len) || |
2066 | | lwip_strnstr(data, HTTP11_CONNECTIONKEEPALIVE2, data_len))) { |
2067 | | hs->keepalive = 1; |
2068 | | } else { |
2069 | | hs->keepalive = 0; |
2070 | | } |
2071 | | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ |
2072 | | /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ |
2073 | 0 | *sp1 = 0; |
2074 | 0 | uri[uri_len] = 0; |
2075 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n", |
2076 | 0 | data, uri)); |
2077 | | #if LWIP_HTTPD_SUPPORT_POST |
2078 | | if (is_post) { |
2079 | | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
2080 | | struct pbuf *q = hs->req; |
2081 | | #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
2082 | | struct pbuf *q = inp; |
2083 | | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
2084 | | err = http_post_request(q, hs, data, data_len, uri, sp2); |
2085 | | if (err != ERR_OK) { |
2086 | | /* restore header for next try */ |
2087 | | *sp1 = ' '; |
2088 | | *sp2 = ' '; |
2089 | | uri[uri_len] = ' '; |
2090 | | } |
2091 | | if (err == ERR_ARG) { |
2092 | | goto badrequest; |
2093 | | } |
2094 | | return err; |
2095 | | } else |
2096 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2097 | 0 | { |
2098 | 0 | return http_find_file(hs, uri, is_09); |
2099 | 0 | } |
2100 | 0 | } |
2101 | 0 | } else { |
2102 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n")); |
2103 | 0 | } |
2104 | 0 | } |
2105 | 0 | } |
2106 | | |
2107 | 0 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
2108 | 0 | clen = pbuf_clen(hs->req); |
2109 | 0 | if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) && |
2110 | 0 | (clen <= LWIP_HTTPD_REQ_QUEUELEN)) { |
2111 | | /* request not fully received (too short or CRLF is missing) */ |
2112 | 0 | return ERR_INPROGRESS; |
2113 | 0 | } else |
2114 | 0 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
2115 | 0 | { |
2116 | | #if LWIP_HTTPD_SUPPORT_POST |
2117 | | badrequest: |
2118 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2119 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n")); |
2120 | | /* could not parse request */ |
2121 | 0 | return http_find_error_file(hs, 400); |
2122 | 0 | } |
2123 | 0 | } |
2124 | | |
2125 | | #if LWIP_HTTPD_SSI && (LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 1) |
2126 | | /* Check if SSI should be parsed for this file/URL |
2127 | | * (With LWIP_HTTPD_SSI_BY_FILE_EXTENSION == 2, this function can be |
2128 | | * overridden by an external implementation.) |
2129 | | * |
2130 | | * @return 1 for SSI, 0 for standard files |
2131 | | */ |
2132 | | static u8_t |
2133 | | http_uri_is_ssi(struct fs_file *file, const char *uri) |
2134 | | { |
2135 | | size_t loop; |
2136 | | u8_t tag_check = 0; |
2137 | | if (file != NULL) { |
2138 | | /* See if we have been asked for an shtml file and, if so, |
2139 | | enable tag checking. */ |
2140 | | const char *ext = NULL, *sub; |
2141 | | char *param = (char *)strstr(uri, "?"); |
2142 | | if (param != NULL) { |
2143 | | /* separate uri from parameters for now, set back later */ |
2144 | | *param = 0; |
2145 | | } |
2146 | | sub = uri; |
2147 | | ext = uri; |
2148 | | for (sub = strstr(sub, "."); sub != NULL; sub = strstr(sub, ".")) { |
2149 | | ext = sub; |
2150 | | sub++; |
2151 | | } |
2152 | | for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { |
2153 | | if (!lwip_stricmp(ext, g_pcSSIExtensions[loop])) { |
2154 | | tag_check = 1; |
2155 | | break; |
2156 | | } |
2157 | | } |
2158 | | if (param != NULL) { |
2159 | | *param = '?'; |
2160 | | } |
2161 | | } |
2162 | | return tag_check; |
2163 | | } |
2164 | | #endif /* LWIP_HTTPD_SSI */ |
2165 | | |
2166 | | /** Try to find the file specified by uri and, if found, initialize hs |
2167 | | * accordingly. |
2168 | | * |
2169 | | * @param hs the connection state |
2170 | | * @param uri the HTTP header URI |
2171 | | * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) |
2172 | | * @return ERR_OK if file was found and hs has been initialized correctly |
2173 | | * another err_t otherwise |
2174 | | */ |
2175 | | static err_t |
2176 | | http_find_file(struct http_state *hs, const char *uri, int is_09) |
2177 | 0 | { |
2178 | 0 | size_t loop; |
2179 | 0 | struct fs_file *file = NULL; |
2180 | 0 | char *params = NULL; |
2181 | 0 | err_t err; |
2182 | | #if LWIP_HTTPD_CGI |
2183 | | int i; |
2184 | | #endif /* LWIP_HTTPD_CGI */ |
2185 | 0 | #if !LWIP_HTTPD_SSI |
2186 | 0 | const |
2187 | 0 | #endif /* !LWIP_HTTPD_SSI */ |
2188 | | /* By default, assume we will not be processing server-side-includes tags */ |
2189 | 0 | u8_t tag_check = 0; |
2190 | | |
2191 | | /* Have we been asked for the default file (in root or a directory) ? */ |
2192 | 0 | #if LWIP_HTTPD_MAX_REQUEST_URI_LEN |
2193 | 0 | size_t uri_len = strlen(uri); |
2194 | 0 | if ((uri_len > 0) && (uri[uri_len - 1] == '/') && |
2195 | 0 | ((uri != http_uri_buf) || (uri_len == 1))) { |
2196 | 0 | size_t copy_len = LWIP_MIN(sizeof(http_uri_buf) - 1, uri_len - 1); |
2197 | 0 | if (copy_len > 0) { |
2198 | 0 | MEMCPY(http_uri_buf, uri, copy_len); |
2199 | 0 | http_uri_buf[copy_len] = 0; |
2200 | 0 | } |
2201 | | #else /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ |
2202 | | if ((uri[0] == '/') && (uri[1] == 0)) { |
2203 | | #endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ |
2204 | | /* Try each of the configured default filenames until we find one |
2205 | | that exists. */ |
2206 | 0 | for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) { |
2207 | 0 | const char *file_name; |
2208 | 0 | #if LWIP_HTTPD_MAX_REQUEST_URI_LEN |
2209 | 0 | if (copy_len > 0) { |
2210 | 0 | size_t len_left = sizeof(http_uri_buf) - copy_len - 1; |
2211 | 0 | if (len_left > 0) { |
2212 | 0 | size_t name_len = strlen(httpd_default_filenames[loop].name); |
2213 | 0 | size_t name_copy_len = LWIP_MIN(len_left, name_len); |
2214 | 0 | MEMCPY(&http_uri_buf[copy_len], httpd_default_filenames[loop].name, name_copy_len); |
2215 | 0 | http_uri_buf[copy_len + name_copy_len] = 0; |
2216 | 0 | } |
2217 | 0 | file_name = http_uri_buf; |
2218 | 0 | } else |
2219 | 0 | #endif /* LWIP_HTTPD_MAX_REQUEST_URI_LEN */ |
2220 | 0 | { |
2221 | 0 | file_name = httpd_default_filenames[loop].name; |
2222 | 0 | } |
2223 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", file_name)); |
2224 | 0 | err = fs_open(&hs->file_handle, file_name); |
2225 | 0 | if (err == ERR_OK) { |
2226 | 0 | uri = file_name; |
2227 | 0 | file = &hs->file_handle; |
2228 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n")); |
2229 | | #if LWIP_HTTPD_SSI |
2230 | | tag_check = httpd_default_filenames[loop].shtml; |
2231 | | #endif /* LWIP_HTTPD_SSI */ |
2232 | 0 | break; |
2233 | 0 | } |
2234 | 0 | } |
2235 | 0 | } |
2236 | 0 | if (file == NULL) { |
2237 | | /* No - we've been asked for a specific file. */ |
2238 | | /* First, isolate the base URI (without any parameters) */ |
2239 | 0 | params = (char *)strchr(uri, '?'); |
2240 | 0 | if (params != NULL) { |
2241 | | /* URI contains parameters. NULL-terminate the base URI */ |
2242 | 0 | *params = '\0'; |
2243 | 0 | params++; |
2244 | 0 | } |
2245 | |
|
2246 | | #if LWIP_HTTPD_CGI |
2247 | | http_cgi_paramcount = -1; |
2248 | | /* Does the base URI we have isolated correspond to a CGI handler? */ |
2249 | | if (httpd_num_cgis && httpd_cgis) { |
2250 | | for (i = 0; i < httpd_num_cgis; i++) { |
2251 | | if (strcmp(uri, httpd_cgis[i].pcCGIName) == 0) { |
2252 | | /* |
2253 | | * We found a CGI that handles this URI so extract the |
2254 | | * parameters and call the handler. |
2255 | | */ |
2256 | | http_cgi_paramcount = extract_uri_parameters(hs, params); |
2257 | | uri = httpd_cgis[i].pfnCGIHandler(i, http_cgi_paramcount, hs->params, |
2258 | | hs->param_vals); |
2259 | | break; |
2260 | | } |
2261 | | } |
2262 | | } |
2263 | | #endif /* LWIP_HTTPD_CGI */ |
2264 | |
|
2265 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri)); |
2266 | |
|
2267 | 0 | err = fs_open(&hs->file_handle, uri); |
2268 | 0 | if (err == ERR_OK) { |
2269 | 0 | file = &hs->file_handle; |
2270 | 0 | } else { |
2271 | 0 | file = http_get_404_file(hs, &uri); |
2272 | 0 | } |
2273 | | #if LWIP_HTTPD_SSI |
2274 | | if (file != NULL) { |
2275 | | if (file->flags & FS_FILE_FLAGS_SSI) { |
2276 | | tag_check = 1; |
2277 | | } else { |
2278 | | #if LWIP_HTTPD_SSI_BY_FILE_EXTENSION |
2279 | | tag_check = http_uri_is_ssi(file, uri); |
2280 | | #endif /* LWIP_HTTPD_SSI_BY_FILE_EXTENSION */ |
2281 | | } |
2282 | | } |
2283 | | #endif /* LWIP_HTTPD_SSI */ |
2284 | 0 | } |
2285 | 0 | if (file == NULL) { |
2286 | | /* None of the default filenames exist so send back a 404 page */ |
2287 | 0 | file = http_get_404_file(hs, &uri); |
2288 | 0 | } |
2289 | 0 | return http_init_file(hs, file, is_09, uri, tag_check, params); |
2290 | 0 | } |
2291 | | |
2292 | | /** Initialize a http connection with a file to send (if found). |
2293 | | * Called by http_find_file and http_find_error_file. |
2294 | | * |
2295 | | * @param hs http connection state |
2296 | | * @param file file structure to send (or NULL if not found) |
2297 | | * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) |
2298 | | * @param uri the HTTP header URI |
2299 | | * @param tag_check enable SSI tag checking |
2300 | | * @param params != NULL if URI has parameters (separated by '?') |
2301 | | * @return ERR_OK if file was found and hs has been initialized correctly |
2302 | | * another err_t otherwise |
2303 | | */ |
2304 | | static err_t |
2305 | | http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, |
2306 | | u8_t tag_check, char *params) |
2307 | 0 | { |
2308 | | #if !LWIP_HTTPD_SUPPORT_V09 |
2309 | | LWIP_UNUSED_ARG(is_09); |
2310 | | #endif |
2311 | 0 | if (file != NULL) { |
2312 | | /* file opened, initialise struct http_state */ |
2313 | 0 | #if !LWIP_HTTPD_DYNAMIC_FILE_READ |
2314 | | /* If dynamic read is disabled, file data must be in one piece and available now */ |
2315 | 0 | LWIP_ASSERT("file->data != NULL", file->data != NULL); |
2316 | 0 | #endif |
2317 | |
|
2318 | | #if LWIP_HTTPD_SSI |
2319 | | if (tag_check) { |
2320 | | struct http_ssi_state *ssi = http_ssi_state_alloc(); |
2321 | | if (ssi != NULL) { |
2322 | | ssi->tag_index = 0; |
2323 | | ssi->tag_state = TAG_NONE; |
2324 | | ssi->parsed = file->data; |
2325 | | ssi->parse_left = file->len; |
2326 | | ssi->tag_end = file->data; |
2327 | | hs->ssi = ssi; |
2328 | | } |
2329 | | } |
2330 | | #else /* LWIP_HTTPD_SSI */ |
2331 | 0 | LWIP_UNUSED_ARG(tag_check); |
2332 | 0 | #endif /* LWIP_HTTPD_SSI */ |
2333 | 0 | hs->handle = file; |
2334 | | #if LWIP_HTTPD_CGI_SSI |
2335 | | if (params != NULL) { |
2336 | | /* URI contains parameters, call generic CGI handler */ |
2337 | | int count; |
2338 | | #if LWIP_HTTPD_CGI |
2339 | | if (http_cgi_paramcount >= 0) { |
2340 | | count = http_cgi_paramcount; |
2341 | | } else |
2342 | | #endif |
2343 | | { |
2344 | | count = extract_uri_parameters(hs, params); |
2345 | | } |
2346 | | httpd_cgi_handler(file, uri, count, http_cgi_params, http_cgi_param_vals |
2347 | | #if defined(LWIP_HTTPD_FILE_STATE) && LWIP_HTTPD_FILE_STATE |
2348 | | , file->state |
2349 | | #endif /* LWIP_HTTPD_FILE_STATE */ |
2350 | | ); |
2351 | | } |
2352 | | #else /* LWIP_HTTPD_CGI_SSI */ |
2353 | 0 | LWIP_UNUSED_ARG(params); |
2354 | 0 | #endif /* LWIP_HTTPD_CGI_SSI */ |
2355 | 0 | hs->file = file->data; |
2356 | 0 | LWIP_ASSERT("File length must be positive!", (file->len >= 0)); |
2357 | | #if LWIP_HTTPD_CUSTOM_FILES |
2358 | | if (file->is_custom_file && (file->data == NULL)) { |
2359 | | /* custom file, need to read data first (via fs_read_custom) */ |
2360 | | hs->left = 0; |
2361 | | } else |
2362 | | #endif /* LWIP_HTTPD_CUSTOM_FILES */ |
2363 | 0 | { |
2364 | 0 | hs->left = (u32_t)file->len; |
2365 | 0 | } |
2366 | 0 | hs->retries = 0; |
2367 | | #if LWIP_HTTPD_TIMING |
2368 | | hs->time_started = sys_now(); |
2369 | | #endif /* LWIP_HTTPD_TIMING */ |
2370 | 0 | #if !LWIP_HTTPD_DYNAMIC_HEADERS |
2371 | 0 | LWIP_ASSERT("HTTP headers not included in file system", |
2372 | 0 | (hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0); |
2373 | 0 | #endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */ |
2374 | 0 | #if LWIP_HTTPD_SUPPORT_V09 |
2375 | 0 | if (is_09 && ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) != 0)) { |
2376 | | /* HTTP/0.9 responses are sent without HTTP header, |
2377 | | search for the end of the header. */ |
2378 | 0 | char *file_start = lwip_strnstr(hs->file, CRLF CRLF, hs->left); |
2379 | 0 | if (file_start != NULL) { |
2380 | 0 | int diff = file_start + 4 - hs->file; |
2381 | 0 | hs->file += diff; |
2382 | 0 | hs->left -= (u32_t)diff; |
2383 | 0 | } |
2384 | 0 | } |
2385 | 0 | #endif /* LWIP_HTTPD_SUPPORT_V09*/ |
2386 | 0 | } else { |
2387 | 0 | hs->handle = NULL; |
2388 | 0 | hs->file = NULL; |
2389 | 0 | hs->left = 0; |
2390 | 0 | hs->retries = 0; |
2391 | 0 | } |
2392 | | #if LWIP_HTTPD_DYNAMIC_HEADERS |
2393 | | /* Determine the HTTP headers to send based on the file extension of |
2394 | | * the requested URI. */ |
2395 | | if ((hs->handle == NULL) || ((hs->handle->flags & FS_FILE_FLAGS_HEADER_INCLUDED) == 0)) { |
2396 | | get_http_headers(hs, uri); |
2397 | | } |
2398 | | #else /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
2399 | 0 | LWIP_UNUSED_ARG(uri); |
2400 | 0 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ |
2401 | | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE |
2402 | | if (hs->keepalive) { |
2403 | | #if LWIP_HTTPD_SSI |
2404 | | if (hs->ssi != NULL) { |
2405 | | hs->keepalive = 0; |
2406 | | } else |
2407 | | #endif /* LWIP_HTTPD_SSI */ |
2408 | | { |
2409 | | if ((hs->handle != NULL) && |
2410 | | ((hs->handle->flags & (FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT)) == FS_FILE_FLAGS_HEADER_INCLUDED)) { |
2411 | | hs->keepalive = 0; |
2412 | | } |
2413 | | } |
2414 | | } |
2415 | | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ |
2416 | 0 | return ERR_OK; |
2417 | 0 | } |
2418 | | |
2419 | | /** |
2420 | | * The pcb had an error and is already deallocated. |
2421 | | * The argument might still be valid (if != NULL). |
2422 | | */ |
2423 | | static void |
2424 | | http_err(void *arg, err_t err) |
2425 | 0 | { |
2426 | 0 | struct http_state *hs = (struct http_state *)arg; |
2427 | 0 | LWIP_UNUSED_ARG(err); |
2428 | |
|
2429 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err))); |
2430 | |
|
2431 | 0 | if (hs != NULL) { |
2432 | 0 | http_state_free(hs); |
2433 | 0 | } |
2434 | 0 | } |
2435 | | |
2436 | | /** |
2437 | | * Data has been sent and acknowledged by the remote host. |
2438 | | * This means that more data can be sent. |
2439 | | */ |
2440 | | static err_t |
2441 | | http_sent(void *arg, struct altcp_pcb *pcb, u16_t len) |
2442 | 0 | { |
2443 | 0 | struct http_state *hs = (struct http_state *)arg; |
2444 | |
|
2445 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void *)pcb)); |
2446 | |
|
2447 | 0 | LWIP_UNUSED_ARG(len); |
2448 | |
|
2449 | 0 | if (hs == NULL) { |
2450 | 0 | return ERR_OK; |
2451 | 0 | } |
2452 | | |
2453 | 0 | hs->retries = 0; |
2454 | |
|
2455 | 0 | http_send(pcb, hs); |
2456 | |
|
2457 | 0 | return ERR_OK; |
2458 | 0 | } |
2459 | | |
2460 | | /** |
2461 | | * The poll function is called every 2nd second. |
2462 | | * If there has been no data sent (which resets the retries) in 8 seconds, close. |
2463 | | * If the last portion of a file has not been sent in 2 seconds, close. |
2464 | | * |
2465 | | * This could be increased, but we don't want to waste resources for bad connections. |
2466 | | */ |
2467 | | static err_t |
2468 | | http_poll(void *arg, struct altcp_pcb *pcb) |
2469 | 0 | { |
2470 | 0 | struct http_state *hs = (struct http_state *)arg; |
2471 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n", |
2472 | 0 | (void *)pcb, (void *)hs, tcp_debug_state_str(altcp_dbg_get_tcp_state(pcb)))); |
2473 | |
|
2474 | 0 | if (hs == NULL) { |
2475 | 0 | err_t closed; |
2476 | | /* arg is null, close. */ |
2477 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); |
2478 | 0 | closed = http_close_conn(pcb, NULL); |
2479 | 0 | LWIP_UNUSED_ARG(closed); |
2480 | | #if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR |
2481 | | if (closed == ERR_MEM) { |
2482 | | altcp_abort(pcb); |
2483 | | return ERR_ABRT; |
2484 | | } |
2485 | | #endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */ |
2486 | 0 | return ERR_OK; |
2487 | 0 | } else { |
2488 | 0 | hs->retries++; |
2489 | 0 | if (hs->retries == HTTPD_MAX_RETRIES) { |
2490 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); |
2491 | 0 | http_close_conn(pcb, hs); |
2492 | 0 | return ERR_OK; |
2493 | 0 | } |
2494 | | |
2495 | | /* If this connection has a file open, try to send some more data. If |
2496 | | * it has not yet received a GET request, don't do this since it will |
2497 | | * cause the connection to close immediately. */ |
2498 | 0 | if (hs->handle) { |
2499 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n")); |
2500 | 0 | if (http_send(pcb, hs)) { |
2501 | | /* If we wrote anything to be sent, go ahead and send it now. */ |
2502 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); |
2503 | 0 | altcp_output(pcb); |
2504 | 0 | } |
2505 | 0 | } |
2506 | 0 | } |
2507 | | |
2508 | 0 | return ERR_OK; |
2509 | 0 | } |
2510 | | |
2511 | | /** |
2512 | | * Data has been received on this pcb. |
2513 | | * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). |
2514 | | */ |
2515 | | static err_t |
2516 | | http_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err) |
2517 | 0 | { |
2518 | 0 | struct http_state *hs = (struct http_state *)arg; |
2519 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void *)pcb, |
2520 | 0 | (void *)p, lwip_strerr(err))); |
2521 | |
|
2522 | 0 | if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) { |
2523 | | /* error or closed by other side? */ |
2524 | 0 | if (p != NULL) { |
2525 | | /* Inform TCP that we have taken the data. */ |
2526 | 0 | altcp_recved(pcb, p->tot_len); |
2527 | 0 | pbuf_free(p); |
2528 | 0 | } |
2529 | 0 | if (hs == NULL) { |
2530 | | /* this should not happen, only to be robust */ |
2531 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n")); |
2532 | 0 | } |
2533 | 0 | http_close_conn(pcb, hs); |
2534 | 0 | return ERR_OK; |
2535 | 0 | } |
2536 | | |
2537 | | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND |
2538 | | if (hs->no_auto_wnd) { |
2539 | | hs->unrecved_bytes += p->tot_len; |
2540 | | } else |
2541 | | #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ |
2542 | 0 | { |
2543 | | /* Inform TCP that we have taken the data. */ |
2544 | 0 | altcp_recved(pcb, p->tot_len); |
2545 | 0 | } |
2546 | |
|
2547 | | #if LWIP_HTTPD_SUPPORT_POST |
2548 | | if (hs->post_content_len_left > 0) { |
2549 | | /* reset idle counter when POST data is received */ |
2550 | | hs->retries = 0; |
2551 | | /* this is data for a POST, pass the complete pbuf to the application */ |
2552 | | http_post_rxpbuf(hs, p); |
2553 | | /* pbuf is passed to the application, don't free it! */ |
2554 | | if (hs->post_content_len_left == 0) { |
2555 | | /* all data received, send response or close connection */ |
2556 | | http_send(pcb, hs); |
2557 | | } |
2558 | | return ERR_OK; |
2559 | | } else |
2560 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2561 | 0 | { |
2562 | 0 | if (hs->handle == NULL) { |
2563 | 0 | err_t parsed = http_parse_request(p, hs, pcb); |
2564 | 0 | LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK |
2565 | 0 | || parsed == ERR_INPROGRESS || parsed == ERR_ARG || parsed == ERR_USE); |
2566 | 0 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST |
2567 | 0 | if (parsed != ERR_INPROGRESS) { |
2568 | | /* request fully parsed or error */ |
2569 | 0 | if (hs->req != NULL) { |
2570 | 0 | pbuf_free(hs->req); |
2571 | 0 | hs->req = NULL; |
2572 | 0 | } |
2573 | 0 | } |
2574 | 0 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ |
2575 | 0 | pbuf_free(p); |
2576 | 0 | if (parsed == ERR_OK) { |
2577 | | #if LWIP_HTTPD_SUPPORT_POST |
2578 | | if (hs->post_content_len_left == 0) |
2579 | | #endif /* LWIP_HTTPD_SUPPORT_POST */ |
2580 | 0 | { |
2581 | 0 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", (const void *)hs->file, hs->left)); |
2582 | 0 | http_send(pcb, hs); |
2583 | 0 | } |
2584 | 0 | } else if (parsed == ERR_ARG) { |
2585 | | /* @todo: close on ERR_USE? */ |
2586 | 0 | http_close_conn(pcb, hs); |
2587 | 0 | } |
2588 | 0 | } else { |
2589 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); |
2590 | | /* already sending but still receiving data, we might want to RST here? */ |
2591 | 0 | pbuf_free(p); |
2592 | 0 | } |
2593 | 0 | } |
2594 | 0 | return ERR_OK; |
2595 | 0 | } |
2596 | | |
2597 | | /** |
2598 | | * A new incoming connection has been accepted. |
2599 | | */ |
2600 | | static err_t |
2601 | | http_accept(void *arg, struct altcp_pcb *pcb, err_t err) |
2602 | 0 | { |
2603 | 0 | struct http_state *hs; |
2604 | 0 | LWIP_UNUSED_ARG(err); |
2605 | 0 | LWIP_UNUSED_ARG(arg); |
2606 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void *)pcb, arg)); |
2607 | |
|
2608 | 0 | if ((err != ERR_OK) || (pcb == NULL)) { |
2609 | 0 | return ERR_VAL; |
2610 | 0 | } |
2611 | | |
2612 | | /* Set priority */ |
2613 | 0 | altcp_setprio(pcb, HTTPD_TCP_PRIO); |
2614 | | |
2615 | | /* Allocate memory for the structure that holds the state of the |
2616 | | connection - initialized by that function. */ |
2617 | 0 | hs = http_state_alloc(); |
2618 | 0 | if (hs == NULL) { |
2619 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n")); |
2620 | 0 | return ERR_MEM; |
2621 | 0 | } |
2622 | 0 | hs->pcb = pcb; |
2623 | | |
2624 | | /* Tell TCP that this is the structure we wish to be passed for our |
2625 | | callbacks. */ |
2626 | 0 | altcp_arg(pcb, hs); |
2627 | | |
2628 | | /* Set up the various callback functions */ |
2629 | 0 | altcp_recv(pcb, http_recv); |
2630 | 0 | altcp_err(pcb, http_err); |
2631 | 0 | altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); |
2632 | 0 | altcp_sent(pcb, http_sent); |
2633 | |
|
2634 | 0 | return ERR_OK; |
2635 | 0 | } |
2636 | | |
2637 | | static void |
2638 | | httpd_init_pcb(struct altcp_pcb *pcb, u16_t port) |
2639 | 0 | { |
2640 | 0 | err_t err; |
2641 | |
|
2642 | 0 | if (pcb) { |
2643 | 0 | altcp_setprio(pcb, HTTPD_TCP_PRIO); |
2644 | | /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */ |
2645 | 0 | err = altcp_bind(pcb, IP_ANY_TYPE, port); |
2646 | 0 | LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */ |
2647 | 0 | LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK); |
2648 | 0 | pcb = altcp_listen(pcb); |
2649 | 0 | LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL); |
2650 | 0 | altcp_accept(pcb, http_accept); |
2651 | 0 | } |
2652 | 0 | } |
2653 | | |
2654 | | /** |
2655 | | * @ingroup httpd |
2656 | | * Initialize the httpd: set up a listening PCB and bind it to the defined port |
2657 | | */ |
2658 | | void |
2659 | | httpd_init(void) |
2660 | 0 | { |
2661 | 0 | struct altcp_pcb *pcb; |
2662 | |
|
2663 | | #if HTTPD_USE_MEM_POOL |
2664 | | LWIP_MEMPOOL_INIT(HTTPD_STATE); |
2665 | | #if LWIP_HTTPD_SSI |
2666 | | LWIP_MEMPOOL_INIT(HTTPD_SSI_STATE); |
2667 | | #endif |
2668 | | #endif |
2669 | 0 | LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n")); |
2670 | | |
2671 | | /* LWIP_ASSERT_CORE_LOCKED(); is checked by tcp_new() */ |
2672 | |
|
2673 | 0 | pcb = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY); |
2674 | 0 | LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL); |
2675 | 0 | httpd_init_pcb(pcb, HTTPD_SERVER_PORT); |
2676 | 0 | } |
2677 | | |
2678 | | #if HTTPD_ENABLE_HTTPS |
2679 | | /** |
2680 | | * @ingroup httpd |
2681 | | * Initialize the httpd: set up a listening PCB and bind it to the defined port. |
2682 | | * Also set up TLS connection handling (HTTPS). |
2683 | | */ |
2684 | | void |
2685 | | httpd_inits(struct altcp_tls_config *conf) |
2686 | | { |
2687 | | #if LWIP_ALTCP_TLS |
2688 | | struct altcp_pcb *pcb_tls = altcp_tls_new(conf, IPADDR_TYPE_ANY); |
2689 | | LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL); |
2690 | | httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS); |
2691 | | #else /* LWIP_ALTCP_TLS */ |
2692 | | LWIP_UNUSED_ARG(conf); |
2693 | | #endif /* LWIP_ALTCP_TLS */ |
2694 | | } |
2695 | | #endif /* HTTPD_ENABLE_HTTPS */ |
2696 | | |
2697 | | #if LWIP_HTTPD_SSI |
2698 | | /** |
2699 | | * @ingroup httpd |
2700 | | * Set the SSI handler function. |
2701 | | * |
2702 | | * @param ssi_handler the SSI handler function |
2703 | | * @param tags an array of SSI tag strings to search for in SSI-enabled files |
2704 | | * @param num_tags number of tags in the 'tags' array |
2705 | | */ |
2706 | | void |
2707 | | http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags) |
2708 | | { |
2709 | | LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n")); |
2710 | | |
2711 | | LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL); |
2712 | | httpd_ssi_handler = ssi_handler; |
2713 | | |
2714 | | #if LWIP_HTTPD_SSI_RAW |
2715 | | LWIP_UNUSED_ARG(tags); |
2716 | | LWIP_UNUSED_ARG(num_tags); |
2717 | | #else /* LWIP_HTTPD_SSI_RAW */ |
2718 | | LWIP_ASSERT("no tags given", tags != NULL); |
2719 | | LWIP_ASSERT("invalid number of tags", num_tags > 0); |
2720 | | |
2721 | | httpd_tags = tags; |
2722 | | httpd_num_tags = num_tags; |
2723 | | #endif /* !LWIP_HTTPD_SSI_RAW */ |
2724 | | } |
2725 | | #endif /* LWIP_HTTPD_SSI */ |
2726 | | |
2727 | | #if LWIP_HTTPD_CGI |
2728 | | /** |
2729 | | * @ingroup httpd |
2730 | | * Set an array of CGI filenames/handler functions |
2731 | | * |
2732 | | * @param cgis an array of CGI filenames/handler functions |
2733 | | * @param num_handlers number of elements in the 'cgis' array |
2734 | | */ |
2735 | | void |
2736 | | http_set_cgi_handlers(const tCGI *cgis, int num_handlers) |
2737 | | { |
2738 | | LWIP_ASSERT("no cgis given", cgis != NULL); |
2739 | | LWIP_ASSERT("invalid number of handlers", num_handlers > 0); |
2740 | | |
2741 | | httpd_cgis = cgis; |
2742 | | httpd_num_cgis = num_handlers; |
2743 | | } |
2744 | | #endif /* LWIP_HTTPD_CGI */ |
2745 | | |
2746 | | #endif /* LWIP_TCP && LWIP_CALLBACK_API */ |