├── fs ├── img │ ├── logo.png │ └── sics.gif ├── cgi │ ├── vars.cgi │ └── upload.cgi ├── form.html ├── 404.html ├── test.html └── index.html ├── multipart_parser.c ├── multipart_parser.h ├── http_request.h ├── fsdata_custom.c ├── cgi_handlers.c ├── fsdata.h ├── makefsdata ├── httpd_structs.h ├── fs.h ├── README.txt ├── fs.c ├── httpd_post.c ├── httpd.h └── httpd.c /fs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ricorx7/LWIP_httpserver_POST/HEAD/fs/img/logo.png -------------------------------------------------------------------------------- /fs/img/sics.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ricorx7/LWIP_httpserver_POST/HEAD/fs/img/sics.gif -------------------------------------------------------------------------------- /multipart_parser.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ricorx7/LWIP_httpserver_POST/HEAD/multipart_parser.c -------------------------------------------------------------------------------- /multipart_parser.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ricorx7/LWIP_httpserver_POST/HEAD/multipart_parser.h -------------------------------------------------------------------------------- /fs/cgi/vars.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | print "Content-type: text/html\n\n"; 4 | print "Environment\n"; 5 | 6 | foreach (sort keys %ENV) { 7 | print "$_: $ENV{$_}
\n"; 8 | } 9 | 10 | 1; -------------------------------------------------------------------------------- /http_request.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_request.h 3 | * 4 | * Created: 2/9/2018 1:05:04 PM 5 | * Author: rico 6 | */ 7 | 8 | 9 | #ifndef HTTP_REQUEST_H_ 10 | #define HTTP_REQUEST_H_ 11 | 12 | typedef struct http_request { 13 | char *uri; 14 | char *post_data; 15 | uint8_t is_post; 16 | char *params; 17 | } HTTPRequest; 18 | 19 | 20 | #endif /* HTTP_REQUEST_H_ */ -------------------------------------------------------------------------------- /fs/form.html: -------------------------------------------------------------------------------- 1 | 2 | RTI - Test Form Page 3 | 4 | 5 | 6 |
7 | First Name:
8 | 9 | Last Name: 10 | 11 |

Photo to Upload:

12 |

Your Email Address:

13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /fs/404.html: -------------------------------------------------------------------------------- 1 | 2 | lwIP - A Lightweight TCP/IP Stack 3 | 4 | 5 | 6 | 19 |
7 | SICS logo 9 | 10 |

lwIP - A Lightweight TCP/IP Stack

11 |

404 - Page not found

12 |

13 | Sorry, the page you are requesting was not found on this 14 | server. 15 |

16 |
17 |   18 |
20 | 21 | 22 | -------------------------------------------------------------------------------- /fs/test.html: -------------------------------------------------------------------------------- 1 | 2 | RTI - Test Page 3 | 4 | 5 | 6 | 27 |
7 | SICS logo 9 | 10 |

RTI - This is a test page

11 |

12 | The web page you are watching was served by a simple web 13 | server running on top of the lightweight TCP/IP stack lwIP. 15 |

16 |

17 |

18 | select file:
19 | 20 | 21 | 22 |
23 |

24 |
25 |   26 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /fsdata_custom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * fsdata_custom.c 3 | * 4 | * Created: 2/9/2018 9:23:18 AM 5 | * Author: rico 6 | */ 7 | 8 | 9 | #include "lwip/opt.h" 10 | #include "lwip/def.h" 11 | #include "fs.h" 12 | #include "fsdata.h" 13 | #include "ff.h" 14 | #include "rti/rti_sdA.h" 15 | #include 16 | 17 | /*-----------------------------------------------------------------------------------*/ 18 | int fs_open_custom(struct fs_file *file, const char *name) 19 | { 20 | FIL file_object; 21 | 22 | int result = rtisdA_Init(0); 23 | if( result != FR_OK ) 24 | { 25 | return SD_ERROR_MOUNT; 26 | } 27 | result = f_open(&file_object, name, FA_READ); 28 | if( result != FR_OK ) 29 | { 30 | f_close(&file_object); 31 | return SD_ERROR_OPEN; 32 | } 33 | 34 | return file_object.fsize;; 35 | } 36 | 37 | /*-----------------------------------------------------------------------------------*/ 38 | void fs_close_custom(struct fs_file *file) 39 | { 40 | 41 | 42 | } -------------------------------------------------------------------------------- /fs/index.html: -------------------------------------------------------------------------------- 1 | 2 | RTI - LWIP Test Page 3 | 4 | 5 | 6 | 41 |
7 | RTI logo 9 | 10 |

RTI - LWIP Test Page

11 |

12 | The web page you are watching was served by a simple web 13 | server running on top of the lightweight TCP/IP stack lwIP. 15 |

16 |

17 | lwIP is an open source implementation of the TCP/IP 18 | protocol suite that was originally written by Adam Dunkels 19 | of the Swedish Institute of Computer Science but now is 20 | being actively developed by a team of developers 21 | distributed world-wide. Since it's release, lwIP has 22 | spurred a lot of interest and has been ported to several 23 | platforms and operating systems. lwIP can be used either 24 | with or without an underlying OS. 25 |

26 |

27 | The focus of the lwIP TCP/IP implementation is to reduce 28 | the RAM usage while still having a full scale TCP. This 29 | makes lwIP suitable for use in embedded systems with tens 30 | of kilobytes of free RAM and room for around 40 kilobytes 31 | of code ROM. 32 |

33 |

34 | More information about lwIP can be found at the lwIP 35 | homepage at http://www.sics.se/~adam/lwip/. 37 |

38 |
39 |   40 |
42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /fs/cgi/upload.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -wT 2 | 3 | use strict; 4 | use CGI; 5 | use CGI::Carp qw ( fatalsToBrowser ); 6 | use File::Basename; 7 | 8 | $CGI::POST_MAX = 1024 * 5000; 9 | my $safe_filename_characters = "a-zA-Z0-9_.-"; 10 | my $upload_dir = "/home/mywebsite/htdocs/upload"; 11 | 12 | my $query = new CGI; 13 | my $filename = $query->param("photo"); 14 | my $email_address = $query->param("email_address"); 15 | my $first_name = $query->param("first_name"); 16 | my $last_name = $query->param("last_name"); 17 | 18 | if ( !$filename ) 19 | { 20 | print $query->header ( ); 21 | print "There was a problem uploading your photo (try a smaller file)."; 22 | exit; 23 | } 24 | 25 | my ( $name, $path, $extension ) = fileparse ( $filename, '..*' ); 26 | $filename = $name . $extension; 27 | $filename =~ tr/ /_/; 28 | $filename =~ s/[^$safe_filename_characters]//g; 29 | 30 | if ( $filename =~ /^([$safe_filename_characters]+)$/ ) 31 | { 32 | $filename = $1; 33 | } 34 | else 35 | { 36 | die "Filename contains invalid characters"; 37 | } 38 | 39 | my $upload_filehandle = $query->upload("photo"); 40 | 41 | open ( UPLOADFILE, ">$upload_dir/$filename" ) or die "$!"; 42 | binmode UPLOADFILE; 43 | 44 | while ( <$upload_filehandle> ) 45 | { 46 | print UPLOADFILE; 47 | } 48 | 49 | close UPLOADFILE; 50 | 51 | print $query->header ( ); 52 | print < 54 | 55 | 56 | 57 | Thanks! 58 | 61 | 62 | 63 |

Thanks for uploading your photo!

64 |

Your email address: $email_address

65 |

First Name: $first_name

66 |

Last Name: $last_name

67 |

Your photo:

68 |

Photo

69 | 70 | 71 | END_HTML -------------------------------------------------------------------------------- /cgi_handlers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cgi_handlers.c 3 | * 4 | * Created: 2/14/2018 10:42:23 AM 5 | * Author: rico 6 | */ 7 | #include "httpd.h" 8 | 9 | /* 10 | * Create all CGI Handlers to handle CGI calls. 11 | */ 12 | /* Cgi call table, only one CGI used */ 13 | tCGI CGI_TAB[2]; 14 | 15 | 16 | /* CGI handler for Upload control */ 17 | const char * Upload_test_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); 18 | 19 | /* Html request for "/cgi/upload.cgi" will start Upload_test_CGI_Handler */ 20 | const tCGI UPLOAD_TEST_CGI={"/cgi/upload.cgi", Upload_test_CGI_Handler}; 21 | 22 | /* CGI handler for Vars control */ 23 | const char * Vars_test_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]); 24 | 25 | /* Html request for "/cgi/vars.cgi" will start Vars_test_CGI_Handler */ 26 | const tCGI VARS_TEST_CGI={"/cgi/vars.cgi", Vars_test_CGI_Handler}; 27 | 28 | 29 | /** 30 | * @brief CGI handler for Uploading control 31 | * GET CGI Example. 32 | * https://github.com/withrobot/myCortex-STM32F4/blob/master/ex14.1_ETH_webserver/httpd_cgi_ssi.c 33 | */ 34 | const char * Upload_test_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) 35 | { 36 | LWIP_DEBUGF(HTTPD_DEBUG, ("Upload_test_CGI_Handler, iIndex=%s", iIndex)); 37 | LWIP_DEBUGF(HTTPD_DEBUG, ("Upload_test_CGI_Handler, iIndex=%s", iNumParams)); 38 | int i = 0; 39 | for (i=0; i 30 | * 31 | */ 32 | #ifndef __FSDATA_H__ 33 | #define __FSDATA_H__ 34 | 35 | #include "lwip/opt.h" 36 | #include "fs.h" 37 | 38 | struct fsdata_file { 39 | const struct fsdata_file *next; 40 | const unsigned char *name; 41 | const unsigned char *data; 42 | int len; 43 | u8_t http_header_included; 44 | #if HTTPD_PRECALCULATED_CHECKSUM 45 | u16_t chksum_count; 46 | const struct fsdata_chksum *chksum; 47 | #endif /* HTTPD_PRECALCULATED_CHECKSUM */ 48 | }; 49 | 50 | #endif /* __FSDATA_H__ */ 51 | -------------------------------------------------------------------------------- /makefsdata: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | open(OUTPUT, "> fsdata.c"); 4 | 5 | chdir("fs"); 6 | open(FILES, "find . -type f |"); 7 | 8 | while($file = ) { 9 | 10 | # Do not include files in CVS directories nor backup files. 11 | if($file =~ /(CVS|~)/) { 12 | next; 13 | } 14 | 15 | chop($file); 16 | 17 | open(HEADER, "> /tmp/header") || die $!; 18 | if($file =~ /404/) { 19 | print(HEADER "HTTP/1.0 404 File not found\r\n"); 20 | } else { 21 | print(HEADER "HTTP/1.0 200 OK\r\n"); 22 | } 23 | print(HEADER "Server: lwIP/pre-0.6 (http://www.sics.se/~adam/lwip/)\r\n"); 24 | if($file =~ /\.html$/) { 25 | print(HEADER "Content-type: text/html\r\n"); 26 | } elsif($file =~ /\.gif$/) { 27 | print(HEADER "Content-type: image/gif\r\n"); 28 | } elsif($file =~ /\.png$/) { 29 | print(HEADER "Content-type: image/png\r\n"); 30 | } elsif($file =~ /\.jpg$/) { 31 | print(HEADER "Content-type: image/jpeg\r\n"); 32 | } elsif($file =~ /\.class$/) { 33 | print(HEADER "Content-type: application/octet-stream\r\n"); 34 | } elsif($file =~ /\.ram$/) { 35 | print(HEADER "Content-type: audio/x-pn-realaudio\r\n"); 36 | } else { 37 | print(HEADER "Content-type: text/plain\r\n"); 38 | } 39 | print(HEADER "\r\n"); 40 | close(HEADER); 41 | 42 | unless($file =~ /\.plain$/ || $file =~ /cgi/) { 43 | system("cat /tmp/header $file > /tmp/file"); 44 | } else { 45 | system("cp $file /tmp/file"); 46 | } 47 | 48 | open(FILE, "/tmp/file"); 49 | unlink("/tmp/file"); 50 | unlink("/tmp/header"); 51 | 52 | $file =~ s/\.//; 53 | $fvar = $file; 54 | $fvar =~ s-/-_-g; 55 | $fvar =~ s-\.-_-g; 56 | print(OUTPUT "static const unsigned char data".$fvar."[] = {\n"); 57 | print(OUTPUT "\t/* $file */\n\t"); 58 | for($j = 0; $j < length($file); $j++) { 59 | printf(OUTPUT "%#02x, ", unpack("C", substr($file, $j, 1))); 60 | } 61 | printf(OUTPUT "0,\n"); 62 | 63 | 64 | $i = 0; 65 | while(read(FILE, $data, 1)) { 66 | if($i == 0) { 67 | print(OUTPUT "\t"); 68 | } 69 | printf(OUTPUT "%#02x, ", unpack("C", $data)); 70 | $i++; 71 | if($i == 10) { 72 | print(OUTPUT "\n"); 73 | $i = 0; 74 | } 75 | } 76 | print(OUTPUT "};\n\n"); 77 | close(FILE); 78 | push(@fvars, $fvar); 79 | push(@files, $file); 80 | } 81 | 82 | for($i = 0; $i < @fvars; $i++) { 83 | $file = $files[$i]; 84 | $fvar = $fvars[$i]; 85 | 86 | if($i == 0) { 87 | $prevfile = "NULL"; 88 | } else { 89 | $prevfile = "file" . $fvars[$i - 1]; 90 | } 91 | print(OUTPUT "const struct fsdata_file file".$fvar."[] = {{$prevfile, data$fvar, "); 92 | print(OUTPUT "data$fvar + ". (length($file) + 1) .", "); 93 | print(OUTPUT "sizeof(data$fvar) - ". (length($file) + 1) ."}};\n\n"); 94 | } 95 | 96 | print(OUTPUT "#define FS_ROOT file$fvars[$i - 1]\n\n"); 97 | print(OUTPUT "#define FS_NUMFILES $i\n"); 98 | -------------------------------------------------------------------------------- /httpd_structs.h: -------------------------------------------------------------------------------- 1 | #ifndef __HTTPD_STRUCTS_H__ 2 | #define __HTTPD_STRUCTS_H__ 3 | 4 | #include "httpd.h" 5 | 6 | /** This string is passed in the HTTP header as "Server: " */ 7 | #ifndef HTTPD_SERVER_AGENT 8 | #define HTTPD_SERVER_AGENT "lwIP/1.3.1 (http://savannah.nongnu.org/projects/lwip)" 9 | #endif 10 | 11 | /** Set this to 1 if you want to include code that creates HTTP headers 12 | * at runtime. Default is off: HTTP headers are then created statically 13 | * by the makefsdata tool. Static headers mean smaller code size, but 14 | * the (readonly) fsdata will grow a bit as every file includes the HTTP 15 | * header. */ 16 | #ifndef LWIP_HTTPD_DYNAMIC_HEADERS 17 | #define LWIP_HTTPD_DYNAMIC_HEADERS 0 18 | #endif 19 | 20 | 21 | #if LWIP_HTTPD_DYNAMIC_HEADERS 22 | /** This struct is used for a list of HTTP header strings for various 23 | * filename extensions. */ 24 | typedef struct 25 | { 26 | const char *extension; 27 | int headerIndex; 28 | } tHTTPHeader; 29 | 30 | /** A list of strings used in HTTP headers */ 31 | static const char * const g_psHTTPHeaderStrings[] = 32 | { 33 | "Content-type: text/html\r\n\r\n", 34 | "Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n", 35 | "Content-type: image/gif\r\n\r\n", 36 | "Content-type: image/png\r\n\r\n", 37 | "Content-type: image/jpeg\r\n\r\n", 38 | "Content-type: image/bmp\r\n\r\n", 39 | "Content-type: image/x-icon\r\n\r\n", 40 | "Content-type: application/octet-stream\r\n\r\n", 41 | "Content-type: application/x-javascript\r\n\r\n", 42 | "Content-type: application/x-javascript\r\n\r\n", 43 | "Content-type: text/css\r\n\r\n", 44 | "Content-type: application/x-shockwave-flash\r\n\r\n", 45 | "Content-type: text/xml\r\n\r\n", 46 | "Content-type: text/plain\r\n\r\n", 47 | "HTTP/1.0 200 OK\r\n", 48 | "HTTP/1.0 404 File not found\r\n", 49 | "HTTP/1.0 400 Bad Request\r\n", 50 | "HTTP/1.0 501 Not Implemented\r\n", 51 | "HTTP/1.1 200 OK\r\n", 52 | "HTTP/1.1 404 File not found\r\n", 53 | "HTTP/1.1 400 Bad Request\r\n", 54 | "HTTP/1.1 501 Not Implemented\r\n", 55 | "Content-Length: ", 56 | "Connection: Close\r\n", 57 | "Connection: keep-alive\r\n", 58 | "Server: "HTTPD_SERVER_AGENT"\r\n", 59 | "\r\n

404: The requested file cannot be found.

\r\n" 60 | }; 61 | 62 | /* Indexes into the g_psHTTPHeaderStrings array */ 63 | #define HTTP_HDR_HTML 0 /* text/html */ 64 | #define HTTP_HDR_SSI 1 /* text/html Expires... */ 65 | #define HTTP_HDR_GIF 2 /* image/gif */ 66 | #define HTTP_HDR_PNG 3 /* image/png */ 67 | #define HTTP_HDR_JPG 4 /* image/jpeg */ 68 | #define HTTP_HDR_BMP 5 /* image/bmp */ 69 | #define HTTP_HDR_ICO 6 /* image/x-icon */ 70 | #define HTTP_HDR_APP 7 /* application/octet-stream */ 71 | #define HTTP_HDR_JS 8 /* application/x-javascript */ 72 | #define HTTP_HDR_RA 9 /* application/x-javascript */ 73 | #define HTTP_HDR_CSS 10 /* text/css */ 74 | #define HTTP_HDR_SWF 11 /* application/x-shockwave-flash */ 75 | #define HTTP_HDR_XML 12 /* text/xml */ 76 | #define HTTP_HDR_DEFAULT_TYPE 13 /* text/plain */ 77 | #define HTTP_HDR_OK 14 /* 200 OK */ 78 | #define HTTP_HDR_NOT_FOUND 15 /* 404 File not found */ 79 | #define HTTP_HDR_BAD_REQUEST 16 /* 400 Bad request */ 80 | #define HTTP_HDR_NOT_IMPL 17 /* 501 Not Implemented */ 81 | #define HTTP_HDR_OK_11 18 /* 200 OK */ 82 | #define HTTP_HDR_NOT_FOUND_11 19 /* 404 File not found */ 83 | #define HTTP_HDR_BAD_REQUEST_11 20 /* 400 Bad request */ 84 | #define HTTP_HDR_NOT_IMPL_11 21 /* 501 Not Implemented */ 85 | #define HTTP_HDR_CONTENT_LENGTH 22 /* Content-Length: (HTTP 1.1)*/ 86 | #define HTTP_HDR_CONN_CLOSE 23 /* Connection: Close (HTTP 1.1) */ 87 | #define HTTP_HDR_CONN_KEEPALIVE 24 /* Connection: keep-alive (HTTP 1.1) */ 88 | #define HTTP_HDR_SERVER 25 /* Server: HTTPD_SERVER_AGENT */ 89 | #define DEFAULT_404_HTML 26 /* default 404 body */ 90 | 91 | /** A list of extension-to-HTTP header strings */ 92 | const static tHTTPHeader g_psHTTPHeaders[] = 93 | { 94 | { "html", HTTP_HDR_HTML}, 95 | { "htm", HTTP_HDR_HTML}, 96 | { "shtml",HTTP_HDR_SSI}, 97 | { "shtm", HTTP_HDR_SSI}, 98 | { "ssi", HTTP_HDR_SSI}, 99 | { "gif", HTTP_HDR_GIF}, 100 | { "png", HTTP_HDR_PNG}, 101 | { "jpg", HTTP_HDR_JPG}, 102 | { "bmp", HTTP_HDR_BMP}, 103 | { "ico", HTTP_HDR_ICO}, 104 | { "class",HTTP_HDR_APP}, 105 | { "cls", HTTP_HDR_APP}, 106 | { "js", HTTP_HDR_JS}, 107 | { "ram", HTTP_HDR_RA}, 108 | { "css", HTTP_HDR_CSS}, 109 | { "swf", HTTP_HDR_SWF}, 110 | { "xml", HTTP_HDR_XML}, 111 | { "xsl", HTTP_HDR_XML} 112 | }; 113 | 114 | #define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader)) 115 | 116 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 117 | 118 | #if LWIP_HTTPD_SSI 119 | static const char * const g_pcSSIExtensions[] = { 120 | ".shtml", ".shtm", ".ssi", ".xml" 121 | }; 122 | #define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *)) 123 | #endif /* LWIP_HTTPD_SSI */ 124 | 125 | #endif /* __HTTPD_STRUCTS_H__ */ -------------------------------------------------------------------------------- /fs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | */ 32 | #ifndef __FS_H__ 33 | #define __FS_H__ 34 | 35 | #include "lwip/opt.h" 36 | #include "lwip/err.h" 37 | 38 | /** Set this to 1 and provide the functions: 39 | * - "int fs_open_custom(struct fs_file *file, const char *name)" 40 | * Called first for every opened file to allow opening files 41 | * that are not included in fsdata(_custom).c 42 | * - "void fs_close_custom(struct fs_file *file)" 43 | * Called to free resources allocated by fs_open_custom(). 44 | */ 45 | #ifndef LWIP_HTTPD_CUSTOM_FILES 46 | #define LWIP_HTTPD_CUSTOM_FILES 0 47 | #endif 48 | 49 | /** Set this to 1 to support fs_read() to dynamically read file data. 50 | * Without this (default=off), only one-block files are supported, 51 | * and the contents must be ready after fs_open(). 52 | */ 53 | #ifndef LWIP_HTTPD_DYNAMIC_FILE_READ 54 | #define LWIP_HTTPD_DYNAMIC_FILE_READ 0 55 | #endif 56 | 57 | /** Set this to 1 to include an application state argument per file 58 | * that is opened. This allows to keep a state per connection/file. 59 | */ 60 | #ifndef LWIP_HTTPD_FILE_STATE 61 | #define LWIP_HTTPD_FILE_STATE 0 62 | #endif 63 | 64 | /** HTTPD_PRECALCULATED_CHECKSUM==1: include precompiled checksums for 65 | * predefined (MSS-sized) chunks of the files to prevent having to calculate 66 | * the checksums at runtime. */ 67 | #ifndef HTTPD_PRECALCULATED_CHECKSUM 68 | #define HTTPD_PRECALCULATED_CHECKSUM 0 69 | #endif 70 | 71 | /** LWIP_HTTPD_FS_ASYNC_READ==1: support asynchronous read operations 72 | * (fs_read_async returns FS_READ_DELAYED and calls a callback when finished). 73 | */ 74 | #ifndef LWIP_HTTPD_FS_ASYNC_READ 75 | #define LWIP_HTTPD_FS_ASYNC_READ 0 76 | #endif 77 | 78 | #define FS_READ_EOF -1 79 | #define FS_READ_DELAYED -2 80 | 81 | #if HTTPD_PRECALCULATED_CHECKSUM 82 | struct fsdata_chksum { 83 | u32_t offset; 84 | u16_t chksum; 85 | u16_t len; 86 | }; 87 | #endif /* HTTPD_PRECALCULATED_CHECKSUM */ 88 | 89 | struct fs_file { 90 | const char *data; 91 | int len; 92 | int index; 93 | void *pextension; 94 | #if HTTPD_PRECALCULATED_CHECKSUM 95 | const struct fsdata_chksum *chksum; 96 | u16_t chksum_count; 97 | #endif /* HTTPD_PRECALCULATED_CHECKSUM */ 98 | u8_t http_header_included; 99 | #if LWIP_HTTPD_CUSTOM_FILES 100 | u8_t is_custom_file; 101 | #endif /* LWIP_HTTPD_CUSTOM_FILES */ 102 | #if LWIP_HTTPD_FILE_STATE 103 | void *state; 104 | #endif /* LWIP_HTTPD_FILE_STATE */ 105 | }; 106 | 107 | #if LWIP_HTTPD_FS_ASYNC_READ 108 | typedef void (*fs_wait_cb)(void *arg); 109 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 110 | 111 | err_t fs_open(struct fs_file *file, const char *name); 112 | void fs_close(struct fs_file *file); 113 | #if LWIP_HTTPD_DYNAMIC_FILE_READ 114 | #if LWIP_HTTPD_FS_ASYNC_READ 115 | int fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg); 116 | #else /* LWIP_HTTPD_FS_ASYNC_READ */ 117 | int fs_read(struct fs_file *file, char *buffer, int count); 118 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 119 | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ 120 | #if LWIP_HTTPD_FS_ASYNC_READ 121 | int fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg); 122 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 123 | int fs_bytes_left(struct fs_file *file); 124 | 125 | #if LWIP_HTTPD_FILE_STATE 126 | /** This user-defined function is called when a file is opened. */ 127 | void *fs_state_init(struct fs_file *file, const char *name); 128 | /** This user-defined function is called when a file is closed. */ 129 | void fs_state_free(struct fs_file *file, void *state); 130 | #endif /* #if LWIP_HTTPD_FILE_STATE */ 131 | 132 | #endif /* __FS_H__ */ -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | LWIP HTTP Server 2 | 3 | This version include handling POST with multipart handling. This can handle file uploads and form data using POST. 4 | 5 | 6 | # Multipart Handling 7 | Within in the httpd_post.c file choose what to do when a POST multipart is received with these functions: 8 | * read_header_name() 9 | * read_header_value() 10 | * read_part_data() 11 | * read_on_part_data_begin() 12 | * read_on_headers_complete() 13 | * read_on_part_data_end() 14 | * read_on_body_end() 15 | 16 | The functions are called based off the parsing of the POST. Here is the order of the parsing. 17 | ``` 18 | read_on_part_data_begin: (nul 19 | 20 | read_header_name: Content-Disposition: 21 | read_header_value: form-data; name="key_name" 22 | 23 | read_on_headers_complete: (null) 24 | 25 | read_part_data: form_value // May be called multiple times if a file 26 | 27 | read_on_part_data_end: (null) 28 | 29 | read_on_body_end: (null) 30 | ``` 31 | 32 | 33 | 34 | = Compile New Website Files = 35 | 36 | - All files for the webserver will be located in fs folder 37 | - Run the command 38 | ./makefsdata 39 | - This will take all the files located in the fs folder and compile it to a file fsdata.c 40 | 41 | = Turn On Debugging HTTPD = 42 | 43 | - In config\lwipopts.h turn on debugging by uncommenting the line 44 | #define LWIP_DEBUG 45 | 46 | - Turn on HTTPD debugging 47 | #define HTTPD_DEBUG LWIP_DBG_ON 48 | 49 | 50 | --------------------------------------------------------------------------------------- 51 | 52 | I can give you a generic guideline to process "simply" GET & POST on your 53 | embedded web server. What you have to do: 54 | 55 | 1/ On the accepted socket, read datas in a buffer until "\r\n\r\n" (included). 56 | This part is the "http_headers". It can be useful to add a 0x00 at the end of 57 | the block to use "string" functions from C runtime, or simply do a "printf". 58 | 59 | 2/ Extract from the first line (of the "http_headers" got in 1/) the method 60 | (should be "POST" or "GET"), and the document (could be "/" for the root). The 61 | method is the first word, the document the next. 62 | 63 | 3/ If the method (got in 2/) is "GET", copy (or use a pointer) all the datas 64 | from the first line which are after a possible "?". Note it's possible to not 65 | have any "?" in a request. At this point, note you can have between 0 to n 66 | bytes for these datas. I will call this memory block "http_params_datas" in 67 | next points. 68 | 69 | 4/ If the method (got in 2/) is "POST", you have first to find in 70 | "http_headers" a line started by "Content-Length: ". The line have a form like 71 | "Content-Length: 32\r\n". When you found this line, convert the value of the 72 | line (32 is this sample) in a integer. This is the "post_data_size". Once you 73 | got that, read on the socket the next "post_data_size" bytes, and copy them (or 74 | use a pointer) in the memory block named "http_params_datas". 75 | 76 | 5/ At this point, you should have a memory block named "http_params_datas" 77 | (with its size). It can contain datas from GET parameters, or from POST datas. 78 | Note than GET have a maximum size of 1024 bytes. That's why, most of time, POST 79 | is used to get "big" datas. POST is also useful if you don't want to "show" on 80 | the address bar your parameters. The aim is to have a memory block to process 81 | on next point. It can be useful to add a 0x00 at the end of the block to use 82 | "string" functions from C runtime, or simply do a "printf". 83 | 84 | 6/ You can now read param by param your "http_params_datas", you will got each 85 | parameters from your "form". They are in the same form in GET or POST 86 | (something like "param1=value1¶m2=value2"). 87 | 88 | That's all. This "framework" will work, but need some checkings. First is to 89 | not read more bytes than your buffer can contains. Other tips: if you try to do 90 | a UPLOAD, the processing is a little bit different from 6/, since you can to 91 | process a "multipart" document, with "boundary" tags to limit the datas (which 92 | can be binary datas). Look for "RFC1341 - 7.2.1 Multipart: The common syntax" 93 | for this case. 94 | 95 | I can propose you two simple functions to "parse" (a big word in this case) 96 | http datas. They are basic tools to help to start, bug mainly used the C 97 | runtime (note they were used on win32, I just change them with lwIP types, so, 98 | it SHOULD work). 99 | 100 | err_t http_getheadervalue( char* lpszHeaderName, char* lpszHeaders, char* 101 | lpszBuffer, u32_t dwBufferSize) 102 | { err_t bResult = ERR_ARG; 103 | char* lpszHeader = NULL; 104 | 105 | memset( lpszBuffer, 0, dwBufferSize); 106 | lpszHeader = strstr( lpszHeaders, lpszHeaderName); 107 | if (lpszHeader!=NULL) 108 | { if (sscanf( lpszHeader+strlen(lpszHeaderName)+1/*:*/+1/*SPACE*/, 109 | "%[^\r\n]", lpszBuffer)>=0) 110 | { bResult = ERR_OK; 111 | } 112 | } 113 | return bResult; 114 | } 115 | 116 | err_t http_getlinevalue( char* lpszValueName, char* lpszLine, char* lpszBuffer, 117 | u32_t dwBufferSize) 118 | { err_t bResult = ERR_ARG; 119 | char* lpszValue = NULL; 120 | char szValue[256] = ""; 121 | 122 | memset( lpszBuffer, 0, dwBufferSize); 123 | memset( szValue, 0, sizeof(szValue)); 124 | 125 | lpszValue = strstr( lpszLine, lpszValueName); 126 | if (lpszValue!=NULL) 127 | { if (sscanf( lpszValue+strlen(lpszValueName)+1/*=*/, "%[^ ;&\r\n]", 128 | szValue)>=0) 129 | { /* Note you have to process special escape sequence, or special chars: 130 | by example, any "+" have to be replace by a " " */ 131 | /* Can you find in your datas some sequence like "%20". You have to 132 | replace them by the a matching ASCII value. For "%20", its 0x20, so, a "space" 133 | /* etc...*/ 134 | bResult = ERR_OK 135 | } 136 | } 137 | return bResult; 138 | } 139 | -------------------------------------------------------------------------------- /fs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | */ 32 | 33 | #include "lwip/opt.h" 34 | #include "lwip/def.h" 35 | #include "fs.h" 36 | #include "fsdata.h" 37 | #include 38 | 39 | /** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the 40 | * file system (to prevent changing the file included in CVS) */ 41 | #ifndef HTTPD_USE_CUSTUM_FSDATA 42 | #define HTTPD_USE_CUSTUM_FSDATA 0 43 | #endif 44 | 45 | #if HTTPD_USE_CUSTUM_FSDATA 46 | #include "fsdata_custom.c" 47 | #else /* HTTPD_USE_CUSTUM_FSDATA */ 48 | #include "fsdata.c" 49 | #endif /* HTTPD_USE_CUSTUM_FSDATA */ 50 | 51 | /*-----------------------------------------------------------------------------------*/ 52 | /* Define the number of open files that we can support. */ 53 | #ifndef LWIP_MAX_OPEN_FILES 54 | #define LWIP_MAX_OPEN_FILES 10 55 | #endif 56 | 57 | /* Define the file system memory allocation structure. */ 58 | struct fs_table { 59 | struct fs_file file; 60 | u8_t inuse; 61 | }; 62 | 63 | /* Allocate file system memory */ 64 | struct fs_table fs_memory[LWIP_MAX_OPEN_FILES]; 65 | 66 | #if LWIP_HTTPD_CUSTOM_FILES 67 | int fs_open_custom(struct fs_file *file, const char *name); 68 | void fs_close_custom(struct fs_file *file); 69 | #endif /* LWIP_HTTPD_CUSTOM_FILES */ 70 | 71 | /*-----------------------------------------------------------------------------------*/ 72 | static struct fs_file * 73 | fs_malloc(void) 74 | { 75 | int i; 76 | for(i = 0; i < LWIP_MAX_OPEN_FILES; i++) { 77 | if(fs_memory[i].inuse == 0) { 78 | fs_memory[i].inuse = 1; 79 | return(&fs_memory[i].file); 80 | } 81 | } 82 | return(NULL); 83 | } 84 | 85 | /*-----------------------------------------------------------------------------------*/ 86 | static void 87 | fs_free(struct fs_file *file) 88 | { 89 | int i; 90 | for(i = 0; i < LWIP_MAX_OPEN_FILES; i++) { 91 | if(&fs_memory[i].file == file) { 92 | fs_memory[i].inuse = 0; 93 | break; 94 | } 95 | } 96 | return; 97 | } 98 | 99 | /*-----------------------------------------------------------------------------------*/ 100 | err_t 101 | fs_open(struct fs_file *file, const char *name) 102 | { 103 | const struct fsdata_file *f; 104 | 105 | if ((file == NULL) || (name == NULL)) { 106 | return ERR_ARG; 107 | } 108 | 109 | #if LWIP_HTTPD_CUSTOM_FILES 110 | if (fs_open_custom(file, name)) { 111 | file->is_custom_file = 1; 112 | return ERR_OK; 113 | } 114 | file->is_custom_file = 0; 115 | #endif /* LWIP_HTTPD_CUSTOM_FILES */ 116 | 117 | for (f = FS_ROOT; f != NULL; f = f->next) { 118 | if (!strcmp(name, (char *)f->name)) { 119 | file->data = (const char *)f->data; 120 | file->len = f->len; 121 | file->index = f->len; 122 | file->pextension = NULL; 123 | file->http_header_included = f->http_header_included; 124 | #if HTTPD_PRECALCULATED_CHECKSUM 125 | file->chksum_count = f->chksum_count; 126 | file->chksum = f->chksum; 127 | #endif /* HTTPD_PRECALCULATED_CHECKSUM */ 128 | #if LWIP_HTTPD_FILE_STATE 129 | file->state = fs_state_init(file, name); 130 | #endif /* #if LWIP_HTTPD_FILE_STATE */ 131 | return ERR_OK; 132 | } 133 | } 134 | /* file not found */ 135 | return ERR_VAL; 136 | } 137 | 138 | /*-----------------------------------------------------------------------------------*/ 139 | void 140 | fs_close(struct fs_file *file) 141 | { 142 | #if LWIP_HTTPD_CUSTOM_FILES 143 | if (file->is_custom_file) { 144 | fs_close_custom(file); 145 | } 146 | #endif /* LWIP_HTTPD_CUSTOM_FILES */ 147 | #if LWIP_HTTPD_FILE_STATE 148 | fs_state_free(file, file->state); 149 | #endif /* #if LWIP_HTTPD_FILE_STATE */ 150 | LWIP_UNUSED_ARG(file); 151 | } 152 | #if LWIP_HTTPD_DYNAMIC_FILE_READ 153 | 154 | #if LWIP_HTTPD_FS_ASYNC_READ 155 | int 156 | fs_read_async(struct fs_file *file, char *buffer, int count, fs_wait_cb callback_fn, void *callback_arg) 157 | #else /* LWIP_HTTPD_FS_ASYNC_READ */ 158 | int 159 | fs_read(struct fs_file *file, char *buffer, int count) 160 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 161 | { 162 | int read; 163 | 164 | if(file->index == file->len) { 165 | return FS_READ_EOF; 166 | } 167 | #if LWIP_HTTPD_FS_ASYNC_READ 168 | #if LWIP_HTTPD_CUSTOM_FILES 169 | if (!fs_canread_custom(file)) { 170 | if (fs_wait_read_custom(file, callback_fn, callback_arg)) { 171 | return FS_READ_DELAYED; 172 | } 173 | } 174 | #else /* LWIP_HTTPD_CUSTOM_FILES */ 175 | LWIP_UNUSED_ARG(callback_fn); 176 | LWIP_UNUSED_ARG(callback_arg); 177 | #endif /* LWIP_HTTPD_CUSTOM_FILES */ 178 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 179 | 180 | read = file->len - file->index; 181 | if(read > count) { 182 | read = count; 183 | } 184 | 185 | MEMCPY(buffer, (file->data + file->index), read); 186 | file->index += read; 187 | 188 | return(read); 189 | } 190 | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ 191 | /*-----------------------------------------------------------------------------------*/ 192 | #if LWIP_HTTPD_FS_ASYNC_READ 193 | int 194 | fs_is_file_ready(struct fs_file *file, fs_wait_cb callback_fn, void *callback_arg) 195 | { 196 | if (file != NULL) { 197 | #if LWIP_HTTPD_FS_ASYNC_READ 198 | #if LWIP_HTTPD_CUSTOM_FILES 199 | if (!fs_canread_custom(file)) { 200 | if (fs_wait_read_custom(file, callback_fn, callback_arg)) { 201 | return 0; 202 | } 203 | } 204 | #else /* LWIP_HTTPD_CUSTOM_FILES */ 205 | LWIP_UNUSED_ARG(callback_fn); 206 | LWIP_UNUSED_ARG(callback_arg); 207 | #endif /* LWIP_HTTPD_CUSTOM_FILES */ 208 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 209 | } 210 | return 1; 211 | } 212 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 213 | /*-----------------------------------------------------------------------------------*/ 214 | int 215 | fs_bytes_left(struct fs_file *file) 216 | { 217 | return file->len - file->index; 218 | } 219 | -------------------------------------------------------------------------------- /httpd_post.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Joakim Myrland 30 | * website: www.LDA.as 31 | * email: joakim.myrland@LDA.as 32 | * project: https://github.com/Lindem-Data-Acquisition-AS/iot_lib/ 33 | * 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include "fs.h" 40 | #include "httpd.h" 41 | #include "multipart_parser.h" 42 | 43 | 44 | 45 | // List of accepted URI for POST requests 46 | multipart_parser* parser; 47 | static uint8_t http_post_uri_file_index = 0; 48 | static uint32_t http_post_content_len = 0; 49 | #define HTTP_POST_URI_NUM 2 50 | const char *a[HTTP_POST_URI_NUM] = { 51 | "/relays.ajax", 52 | "/cgi/upload.cgi" 53 | }; 54 | 55 | /* 56 | * Mulitpart Parser settings 57 | * 58 | * read_on_part_data_begin: (nul 59 | * read_header_name: Content-Disposition: read_header_value: form-data; name="key_name" 60 | * read_on_headers_complete: (null) 61 | * read_part_data: form_value // May be called multiple times if a file 62 | * read_on_part_data_end: (null) 63 | * read_on_body_end: (null) 64 | * 65 | */ 66 | multipart_parser_settings callbacks; 67 | multipart_parser* _parser; 68 | 69 | /* Header which contains the Key with the name */ 70 | int read_header_name(multipart_parser* p, const char *at, size_t length) 71 | { 72 | #ifdef HTTPD_DEBUG 73 | printf("read_header_name: %.*s: \n", length, at); 74 | #endif 75 | 76 | /* Parse the Header Value */ 77 | /* Content-Disposition: read_header_value: form-data; name="variable_name" */ 78 | char *key_name = find_header_name(at); 79 | 80 | #ifdef HTTPD_DEBUG 81 | printf("Key Name: %s\n", key_name); 82 | #endif 83 | 84 | return 0; 85 | } 86 | 87 | int read_header_value(multipart_parser* p, const char *at, size_t length) 88 | { 89 | #ifdef HTTPD_DEBUG 90 | printf("read_header_value: %.*s\n", length, at); 91 | #endif 92 | return 0; 93 | } 94 | 95 | /* Value for the latest key */ 96 | /* If this is a file, this may be called multiple times. */ 97 | /* Wait until part_end for the complete file. */ 98 | int read_part_data(multipart_parser* p, const char *at, size_t length) 99 | { 100 | #ifdef HTTPD_DEBUG 101 | printf("read_part_data: %.*s\n", length, at); 102 | #endif 103 | return 0; 104 | } 105 | 106 | /* Beginning of a key and value */ 107 | int read_on_part_data_begin(multipart_parser* p, const char *at, size_t length) 108 | { 109 | #ifdef HTTPD_DEBUG 110 | printf("read_on_part_data_begin: %.*s\n", length, at); 111 | #endif 112 | return 0; 113 | } 114 | 115 | /* End of header which contains the key */ 116 | int read_on_headers_complete(multipart_parser* p, const char *at, size_t length) 117 | { 118 | #ifdef HTTPD_DEBUG 119 | printf("read_on_headers_complete: %.*s\n", length, at); 120 | #endif 121 | return 0; 122 | } 123 | 124 | /** End of the key and value */ 125 | /* If this is a file, the file is complete. */ 126 | /* If this is a value, then the value is complete. */ 127 | int read_on_part_data_end(multipart_parser* p, const char *at, size_t length) 128 | { 129 | #ifdef HTTPD_DEBUG 130 | printf("read_on_part_data_end: %.*s\n", length, at); 131 | #endif 132 | return 0; 133 | } 134 | 135 | /* End of the entire form */ 136 | int read_on_body_end(multipart_parser* p, const char *at, size_t length) 137 | { 138 | #ifdef HTTPD_DEBUG 139 | printf("read_on_body_end: %.*s\n", length, at); 140 | #endif 141 | return 0; 142 | } 143 | 144 | 145 | static err_t 146 | http_parse_post(char * data, uint32_t length) 147 | { 148 | #ifdef HTTPD_DEBUG 149 | printf("http_parse_post POST data: %s\n", data); 150 | #endif 151 | 152 | /* Parse the data */ 153 | multipart_parser_execute(_parser, data, length); 154 | 155 | return ERR_OK; 156 | } 157 | 158 | 159 | /** Called when a POST request has been received. The application can decide 160 | * whether to accept it or not. 161 | * 162 | * @param connection Unique connection identifier, valid until httpd_post_end 163 | * is called. 164 | * @param uri The HTTP header URI receiving the POST request. 165 | * @param http_request The raw HTTP request (the first packet, normally). 166 | * @param http_request_len Size of 'http_request'. 167 | * @param content_len Content-Length from HTTP header. 168 | * @param response_uri Filename of response file, to be filled when denying the 169 | * request 170 | * @param response_uri_len Size of the 'response_uri' buffer. 171 | * @param post_auto_wnd Set this to 0 to let the callback code handle window 172 | * updates by calling 'httpd_post_data_recved' (to throttle rx speed) 173 | * default is 1 (httpd handles window updates automatically) 174 | * @param content_type Content-Type string. 175 | * @return ERR_OK: Accept the POST request, data may be passed in 176 | * another err_t: Deny the POST request, send back 'bad request'. 177 | */ 178 | err_t 179 | httpd_post_begin(void *connection, 180 | const char *uri, 181 | const char *http_request, 182 | u16_t http_request_len, 183 | int content_len, 184 | char *response_uri, 185 | u16_t response_uri_len, 186 | u8_t *post_auto_wnd, 187 | const char *content_type) { 188 | 189 | // Check the URI given with the list 190 | for (uint8_t i=0; ipayload; 245 | ret_val = http_parse_post(data, http_post_content_len); 246 | } 247 | 248 | if (p != NULL) { 249 | pbuf_free(p); 250 | } 251 | 252 | return ret_val; 253 | } 254 | 255 | /** Called when all data is received or when the connection is closed. 256 | * The application must return the filename/URI of a file to send in response 257 | * to this POST request. If the response_uri buffer is untouched, a 404 258 | * response is returned. 259 | * 260 | * @param connection Unique connection identifier. 261 | * @param response_uri Filename of response file on success 262 | * @param response_uri_len Size of the 'response_uri' buffer. 263 | */ 264 | void 265 | httpd_post_finished(void *connection, 266 | char *response_uri, 267 | u16_t response_uri_len) { 268 | 269 | struct http_state *hs = (struct http_state*)connection; 270 | if (hs != NULL) { 271 | strncpy(response_uri, a[http_post_uri_file_index], response_uri_len); 272 | } 273 | 274 | /* End the parser */ 275 | multipart_parser_free(_parser); 276 | } 277 | 278 | /* Find boundary value in the Content-Type. */ 279 | const char * 280 | find_boundary(char*content_type ) { 281 | 282 | #define BOUNDARY_TITLE "boundary=" 283 | #define BOUNDARY_TITLE_LEN 9 284 | 285 | if(content_type != NULL) { 286 | char *boundary_begin = strstr(content_type, BOUNDARY_TITLE); // Find Boundary= in Content-Type 287 | char *boundary = boundary_begin + BOUNDARY_TITLE_LEN; // Remove the Boundary= 288 | #ifdef HTTPD_DEBUG 289 | printf("POST multipart Boundary found: %s\n", boundary); 290 | #endif 291 | 292 | return boundary; 293 | } 294 | return NULL; 295 | } 296 | 297 | /* Find Header Key Name in the header. */ 298 | const char * 299 | find_header_name(char*header ) { 300 | 301 | #define HEADER_NAME_TITLE "name=" 302 | #define HEADER_NAME_TITLE_LEN 5 303 | 304 | if(header != NULL) { 305 | char *header_name_begin = strstr(header, HEADER_NAME_TITLE); // Find name= in Header 306 | char *header_name = strtok(header_name_begin, "\""); // Find the first " 307 | header_name = strtok(NULL, "\""); // Go to the last " 308 | #ifdef HTTPD_DEBUG 309 | printf("POST multipart Header Key found: %s\n", header_name); 310 | #endif 311 | 312 | return header_name; 313 | } 314 | return NULL; 315 | } 316 | -------------------------------------------------------------------------------- /httpd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | * This version of the file has been modified by Texas Instruments to offer 32 | * simple server-side-include (SSI) and Common Gateway Interface (CGI) 33 | * capability. 34 | */ 35 | 36 | #ifndef __HTTPD_H__ 37 | #define __HTTPD_H__ 38 | 39 | #include "lwip/opt.h" 40 | #include "lwip/err.h" 41 | #include "lwip/pbuf.h" 42 | 43 | 44 | #ifndef HTTPD_DEBUG 45 | #define HTTPD_DEBUG LWIP_DBG_ON 46 | #endif 47 | 48 | /** Set this to 1 to support CGI */ 49 | #ifndef LWIP_HTTPD_CGI 50 | #define LWIP_HTTPD_CGI 1 51 | #endif 52 | 53 | /** Set this to 1 to support SSI (Server-Side-Includes) */ 54 | #ifndef LWIP_HTTPD_SSI 55 | #define LWIP_HTTPD_SSI 1 56 | #endif 57 | 58 | /** Set this to 1 to support HTTP POST */ 59 | #ifndef LWIP_HTTPD_SUPPORT_POST 60 | #define LWIP_HTTPD_SUPPORT_POST 1 61 | #endif 62 | 63 | 64 | #if LWIP_HTTPD_CGI 65 | 66 | /* 67 | * Function pointer for a CGI script handler. 68 | * 69 | * This function is called each time the HTTPD server is asked for a file 70 | * whose name was previously registered as a CGI function using a call to 71 | * http_set_cgi_handler. The iIndex parameter provides the index of the 72 | * CGI within the ppcURLs array passed to http_set_cgi_handler. Parameters 73 | * pcParam and pcValue provide access to the parameters provided along with 74 | * the URI. iNumParams provides a count of the entries in the pcParam and 75 | * pcValue arrays. Each entry in the pcParam array contains the name of a 76 | * parameter with the corresponding entry in the pcValue array containing the 77 | * value for that parameter. Note that pcParam may contain multiple elements 78 | * with the same name if, for example, a multi-selection list control is used 79 | * in the form generating the data. 80 | * 81 | * The function should return a pointer to a character string which is the 82 | * path and filename of the response that is to be sent to the connected 83 | * browser, for example "/thanks.htm" or "/response/error.ssi". 84 | * 85 | * The maximum number of parameters that will be passed to this function via 86 | * iNumParams is defined by LWIP_HTTPD_MAX_CGI_PARAMETERS. Any parameters in the incoming 87 | * HTTP request above this number will be discarded. 88 | * 89 | * Requests intended for use by this CGI mechanism must be sent using the GET 90 | * method (which encodes all parameters within the URI rather than in a block 91 | * later in the request). Attempts to use the POST method will result in the 92 | * request being ignored. 93 | * 94 | */ 95 | typedef const char *(*tCGIHandler)(int iIndex, int iNumParams, char *pcParam[], 96 | char *pcValue[]); 97 | 98 | /* 99 | * Structure defining the base filename (URL) of a CGI and the associated 100 | * function which is to be called when that URL is requested. 101 | */ 102 | typedef struct 103 | { 104 | const char *pcCGIName; 105 | tCGIHandler pfnCGIHandler; 106 | } tCGI; 107 | 108 | void http_set_cgi_handlers(const tCGI *pCGIs, int iNumHandlers); 109 | 110 | 111 | /* The maximum number of parameters that the CGI handler can be sent. */ 112 | #ifndef LWIP_HTTPD_MAX_CGI_PARAMETERS 113 | #define LWIP_HTTPD_MAX_CGI_PARAMETERS 16 114 | #endif 115 | 116 | #endif /* LWIP_HTTPD_CGI */ 117 | 118 | #if LWIP_HTTPD_SSI 119 | 120 | /** LWIP_HTTPD_SSI_MULTIPART==1: SSI handler function is called with 2 more 121 | * arguments indicating a counter for insert string that are too long to be 122 | * inserted at once: the SSI handler function must then set 'next_tag_part' 123 | * which will be passed back to it in the next call. */ 124 | #ifndef LWIP_HTTPD_SSI_MULTIPART 125 | #define LWIP_HTTPD_SSI_MULTIPART 0 126 | #endif 127 | 128 | /* 129 | * Function pointer for the SSI tag handler callback. 130 | * 131 | * This function will be called each time the HTTPD server detects a tag of the 132 | * form in a .shtml, .ssi or .shtm file where "name" appears as 133 | * one of the tags supplied to http_set_ssi_handler in the ppcTags array. The 134 | * returned insert string, which will be appended after the the string 135 | * "" in file sent back to the client,should be written to pointer 136 | * pcInsert. iInsertLen contains the size of the buffer pointed to by 137 | * pcInsert. The iIndex parameter provides the zero-based index of the tag as 138 | * found in the ppcTags array and identifies the tag that is to be processed. 139 | * 140 | * The handler returns the number of characters written to pcInsert excluding 141 | * any terminating NULL or a negative number to indicate a failure (tag not 142 | * recognized, for example). 143 | * 144 | * Note that the behavior of this SSI mechanism is somewhat different from the 145 | * "normal" SSI processing as found in, for example, the Apache web server. In 146 | * this case, the inserted text is appended following the SSI tag rather than 147 | * replacing the tag entirely. This allows for an implementation that does not 148 | * require significant additional buffering of output data yet which will still 149 | * offer usable SSI functionality. One downside to this approach is when 150 | * attempting to use SSI within JavaScript. The SSI tag is structured to 151 | * resemble an HTML comment but this syntax does not constitute a comment 152 | * within JavaScript and, hence, leaving the tag in place will result in 153 | * problems in these cases. To work around this, any SSI tag which needs to 154 | * output JavaScript code must do so in an encapsulated way, sending the whole 155 | * HTML section as a single include. 156 | */ 157 | typedef u16_t (*tSSIHandler)(int iIndex, char *pcInsert, int iInsertLen 158 | #if LWIP_HTTPD_SSI_MULTIPART 159 | , u16_t current_tag_part, u16_t *next_tag_part 160 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 161 | #if LWIP_HTTPD_FILE_STATE 162 | , void *connection_state 163 | #endif /* LWIP_HTTPD_FILE_STATE */ 164 | ); 165 | 166 | void http_set_ssi_handler(tSSIHandler pfnSSIHandler, 167 | const char **ppcTags, int iNumTags); 168 | 169 | /* The maximum length of the string comprising the tag name */ 170 | #ifndef LWIP_HTTPD_MAX_TAG_NAME_LEN 171 | #define LWIP_HTTPD_MAX_TAG_NAME_LEN 8 172 | #endif 173 | 174 | /* The maximum length of string that can be returned to replace any given tag */ 175 | #ifndef LWIP_HTTPD_MAX_TAG_INSERT_LEN 176 | #define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192 177 | #endif 178 | 179 | #endif /* LWIP_HTTPD_SSI */ 180 | 181 | #if LWIP_HTTPD_SUPPORT_POST 182 | 183 | /* These functions must be implemented by the application */ 184 | 185 | /** Called when a POST request has been received. The application can decide 186 | * whether to accept it or not. 187 | * 188 | * @param connection Unique connection identifier, valid until httpd_post_end 189 | * is called. 190 | * @param uri The HTTP header URI receiving the POST request. 191 | * @param http_request The raw HTTP request (the first packet, normally). 192 | * @param http_request_len Size of 'http_request'. 193 | * @param content_len Content-Length from HTTP header. 194 | * @param response_uri Filename of response file, to be filled when denying the 195 | * request 196 | * @param response_uri_len Size of the 'response_uri' buffer. 197 | * @param post_auto_wnd Set this to 0 to let the callback code handle window 198 | * updates by calling 'httpd_post_data_recved' (to throttle rx speed) 199 | * default is 1 (httpd handles window updates automatically) 200 | * @param content_type Content-Type string to get the boundary. 201 | * @return ERR_OK: Accept the POST request, data may be passed in 202 | * another err_t: Deny the POST request, send back 'bad request'. 203 | */ 204 | err_t httpd_post_begin(void *connection, const char *uri, const char *http_request, 205 | u16_t http_request_len, int content_len, char *response_uri, 206 | u16_t response_uri_len, u8_t *post_auto_wnd, const char *content_type); 207 | 208 | /** Called for each pbuf of data that has been received for a POST. 209 | * ATTENTION: The application is responsible for freeing the pbufs passed in! 210 | * 211 | * @param connection Unique connection identifier. 212 | * @param p Received data. 213 | * @return ERR_OK: Data accepted. 214 | * another err_t: Data denied, http_post_get_response_uri will be called. 215 | */ 216 | err_t httpd_post_receive_data(void *connection, struct pbuf *p); 217 | 218 | /** Called when all data is received or when the connection is closed. 219 | * The application must return the filename/URI of a file to send in response 220 | * to this POST request. If the response_uri buffer is untouched, a 404 221 | * response is returned. 222 | * 223 | * @param connection Unique connection identifier. 224 | * @param response_uri Filename of response file, to be filled when denying the request 225 | * @param response_uri_len Size of the 'response_uri' buffer. 226 | */ 227 | void httpd_post_finished(void *connection, char *response_uri, u16_t response_uri_len); 228 | 229 | /** Find the boundary value in the Content-Type line. 230 | * This value is used to determine how to separate the keys/value pairs. 231 | * Looking for boundary= 232 | * 233 | * @param content_type Content-Type string that contains the boundary. 234 | */ 235 | const char * find_boundary(char*content_type ); 236 | 237 | /** Find the key name in the header of a form value. 238 | * Looking for name= 239 | * 240 | * @param header Header string that contains the name. 241 | */ 242 | const char * find_header_name(char*header ); 243 | 244 | #ifndef LWIP_HTTPD_POST_MANUAL_WND 245 | #define LWIP_HTTPD_POST_MANUAL_WND 0 246 | #endif 247 | 248 | #if LWIP_HTTPD_POST_MANUAL_WND 249 | void httpd_post_data_recved(void *connection, u16_t recved_len); 250 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 251 | 252 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 253 | 254 | void httpd_init(void); 255 | 256 | #endif /* __HTTPD_H__ */ 257 | -------------------------------------------------------------------------------- /httpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * Simon Goldschmidt 31 | * 32 | */ 33 | 34 | /* This httpd supports for a 35 | * rudimentary server-side-include facility which will replace tags of the form 36 | * in any file whose extension is .shtml, .shtm or .ssi with 37 | * strings provided by an include handler whose pointer is provided to the 38 | * module via function http_set_ssi_handler(). 39 | * Additionally, a simple common 40 | * gateway interface (CGI) handling mechanism has been added to allow clients 41 | * to hook functions to particular request URIs. 42 | * 43 | * To enable SSI support, define label LWIP_HTTPD_SSI in lwipopts.h. 44 | * To enable CGI support, define label LWIP_HTTPD_CGI in lwipopts.h. 45 | * 46 | * By default, the server assumes that HTTP headers are already present in 47 | * each file stored in the file system. By defining LWIP_HTTPD_DYNAMIC_HEADERS in 48 | * lwipopts.h, this behavior can be changed such that the server inserts the 49 | * headers automatically based on the extension of the file being served. If 50 | * this mode is used, be careful to ensure that the file system image used 51 | * does not already contain the header information. 52 | * 53 | * File system images without headers can be created using the makefsfile 54 | * tool with the -h command line option. 55 | * 56 | * 57 | * Notes about valid SSI tags 58 | * -------------------------- 59 | * 60 | * The following assumptions are made about tags used in SSI markers: 61 | * 62 | * 1. No tag may contain '-' or whitespace characters within the tag name. 63 | * 2. Whitespace is allowed between the tag leadin "". 65 | * 3. The maximum tag name length is LWIP_HTTPD_MAX_TAG_NAME_LEN, currently 8 characters. 66 | * 67 | * Notes on CGI usage 68 | * ------------------ 69 | * 70 | * The simple CGI support offered here works with GET method requests only 71 | * and can handle up to 16 parameters encoded into the URI. The handler 72 | * function may not write directly to the HTTP output but must return a 73 | * filename that the HTTP server will send to the browser as a response to 74 | * the incoming CGI request. 75 | * 76 | * 77 | * 78 | * The list of supported file types is quite short, so if makefsdata complains 79 | * about an unknown extension, make sure to add it (and its doctype) to 80 | * the 'g_psHTTPHeaders' list. 81 | */ 82 | #include "httpd.h" 83 | #include "lwip/debug.h" 84 | #include "lwip/stats.h" 85 | #include "httpd_structs.h" 86 | #include "lwip/tcp.h" 87 | #include "fs.h" 88 | 89 | #include 90 | #include 91 | 92 | #if LWIP_TCP 93 | 94 | 95 | 96 | /** Set this to 1 and add the next line to lwippools.h to use a memp pool 97 | * for allocating struct http_state instead of the heap: 98 | * 99 | * LWIP_MEMPOOL(HTTPD_STATE, 20, 100, "HTTPD_STATE") 100 | */ 101 | #ifndef HTTPD_USE_MEM_POOL 102 | #define HTTPD_USE_MEM_POOL 0 103 | #endif 104 | 105 | /** The server port for HTTPD to use */ 106 | #ifndef HTTPD_SERVER_PORT 107 | #define HTTPD_SERVER_PORT 80 108 | #endif 109 | 110 | /** Maximum retries before the connection is aborted/closed. 111 | * - number of times pcb->poll is called -> default is 4*500ms = 2s; 112 | * - reset when pcb->sent is called 113 | */ 114 | #ifndef HTTPD_MAX_RETRIES 115 | #define HTTPD_MAX_RETRIES 4 116 | #endif 117 | 118 | /** The poll delay is X*500ms */ 119 | #ifndef HTTPD_POLL_INTERVAL 120 | #define HTTPD_POLL_INTERVAL 4 121 | #endif 122 | 123 | /** Priority for tcp pcbs created by HTTPD (very low by default). 124 | * Lower priorities get killed first when running out of memroy. 125 | */ 126 | #ifndef HTTPD_TCP_PRIO 127 | #define HTTPD_TCP_PRIO TCP_PRIO_MIN 128 | #endif 129 | 130 | /** Set this to 1 to enabled timing each file sent */ 131 | #ifndef LWIP_HTTPD_TIMING 132 | #define LWIP_HTTPD_TIMING 0 133 | #endif 134 | #ifndef HTTPD_DEBUG_TIMING 135 | #define HTTPD_DEBUG_TIMING LWIP_DBG_OFF 136 | #endif 137 | 138 | /** Set this to 1 on platforms where strnstr is not available */ 139 | #ifndef LWIP_HTTPD_STRNSTR_PRIVATE 140 | #define LWIP_HTTPD_STRNSTR_PRIVATE 1 141 | #endif 142 | 143 | /** Set this to one to show error pages when parsing a request fails instead 144 | of simply closing the connection. */ 145 | #ifndef LWIP_HTTPD_SUPPORT_EXTSTATUS 146 | #define LWIP_HTTPD_SUPPORT_EXTSTATUS 1 147 | #endif 148 | 149 | /** Set this to 0 to drop support for HTTP/0.9 clients (to save some bytes) */ 150 | #ifndef LWIP_HTTPD_SUPPORT_V09 151 | #define LWIP_HTTPD_SUPPORT_V09 0 152 | #endif 153 | 154 | /** Set this to 1 to enable HTTP/1.1 persistent connections. 155 | * ATTENTION: If the generated file system includes HTTP headers, these must 156 | * include the "Connection: keep-alive" header (pass argument "-11" to makefsdata). 157 | */ 158 | #ifndef LWIP_HTTPD_SUPPORT_11_KEEPALIVE 159 | #define LWIP_HTTPD_SUPPORT_11_KEEPALIVE 0 160 | #endif 161 | 162 | /** Set this to 1 to support HTTP request coming in in multiple packets/pbufs */ 163 | #ifndef LWIP_HTTPD_SUPPORT_REQUESTLIST 164 | #define LWIP_HTTPD_SUPPORT_REQUESTLIST 1 165 | #endif 166 | 167 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 168 | /** Number of rx pbufs to enqueue to parse an incoming request (up to the first 169 | newline) */ 170 | #ifndef LWIP_HTTPD_REQ_QUEUELEN 171 | #define LWIP_HTTPD_REQ_QUEUELEN 10 172 | #endif 173 | 174 | /** Number of (TCP payload-) bytes (in pbufs) to enqueue to parse and incoming 175 | request (up to the first double-newline) */ 176 | #ifndef LWIP_HTTPD_REQ_BUFSIZE 177 | #define LWIP_HTTPD_REQ_BUFSIZE LWIP_HTTPD_MAX_REQ_LENGTH 178 | #endif 179 | 180 | /** Defines the maximum length of a HTTP request line (up to the first CRLF, 181 | copied from pbuf into this a global buffer when pbuf- or packet-queues 182 | are received - otherwise the input pbuf is used directly) */ 183 | #ifndef LWIP_HTTPD_MAX_REQ_LENGTH 184 | #define LWIP_HTTPD_MAX_REQ_LENGTH LWIP_MIN(1023, (LWIP_HTTPD_REQ_QUEUELEN * PBUF_POOL_BUFSIZE)) 185 | #endif 186 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 187 | 188 | /** Maximum length of the filename to send as response to a POST request, 189 | * filled in by the application when a POST is finished. 190 | */ 191 | #ifndef LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 192 | #define LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN 63 193 | #endif 194 | 195 | /** Set this to 0 to not send the SSI tag (default is on, so the tag will 196 | * be sent in the HTML page */ 197 | #ifndef LWIP_HTTPD_SSI_INCLUDE_TAG 198 | #define LWIP_HTTPD_SSI_INCLUDE_TAG 0 199 | #endif 200 | 201 | /** Set this to 1 to call tcp_abort when tcp_close fails with memory error. 202 | * This can be used to prevent consuming all memory in situations where the 203 | * HTTP server has low priority compared to other communication. */ 204 | #ifndef LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR 205 | #define LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR 1 206 | #endif 207 | 208 | /** Set this to 1 to kill the oldest connection when running out of 209 | * memory for 'struct http_state' or 'struct http_ssi_state'. 210 | * ATTENTION: This puts all connections on a linked list, so may be kind of slow. 211 | */ 212 | #ifndef LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 213 | #define LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 0 214 | #endif 215 | 216 | /** Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes */ 217 | #define MIN_REQ_LEN 7 218 | 219 | #define CRLF "\r\n" 220 | #define HTTP11_CONNECTIONKEEPALIVE "Connection: keep-alive" 221 | 222 | #if LWIP_HTTPD_SSI 223 | #define LWIP_HTTPD_IS_SSI(hs) ((hs)->ssi) 224 | #else /* LWIP_HTTPD_SSI */ 225 | #define LWIP_HTTPD_IS_SSI(hs) 0 226 | #endif /* LWIP_HTTPD_SSI */ 227 | 228 | /** These defines check whether tcp_write has to copy data or not */ 229 | 230 | /** This was TI's check whether to let TCP copy data or not 231 | #define HTTP_IS_DATA_VOLATILE(hs) ((hs->file < (char *)0x20000000) ? 0 : TCP_WRITE_FLAG_COPY)*/ 232 | #ifndef HTTP_IS_DATA_VOLATILE 233 | #if LWIP_HTTPD_SSI 234 | /* Copy for SSI files, no copy for non-SSI files */ 235 | #define HTTP_IS_DATA_VOLATILE(hs) ((hs)->ssi ? TCP_WRITE_FLAG_COPY : 0) 236 | #else /* LWIP_HTTPD_SSI */ 237 | /** Default: don't copy if the data is sent from file-system directly */ 238 | #define HTTP_IS_DATA_VOLATILE(hs) (((hs->file != NULL) && (hs->handle != NULL) && (hs->file == \ 239 | (char*)hs->handle->data + hs->handle->len - hs->left)) \ 240 | ? 0 : TCP_WRITE_FLAG_COPY) 241 | #endif /* LWIP_HTTPD_SSI */ 242 | #endif 243 | 244 | /** Default: headers are sent from ROM */ 245 | #ifndef HTTP_IS_HDR_VOLATILE 246 | #define HTTP_IS_HDR_VOLATILE(hs, ptr) 0 247 | #endif 248 | 249 | #if LWIP_HTTPD_SSI 250 | /** Default: Tags are sent from struct http_state and are therefore volatile */ 251 | #ifndef HTTP_IS_TAG_VOLATILE 252 | #define HTTP_IS_TAG_VOLATILE(ptr) TCP_WRITE_FLAG_COPY 253 | #endif 254 | #endif /* LWIP_HTTPD_SSI */ 255 | 256 | /* Return values for http_send_*() */ 257 | #define HTTP_DATA_TO_SEND_BREAK 2 258 | #define HTTP_DATA_TO_SEND_CONTINUE 1 259 | #define HTTP_NO_DATA_TO_SEND 0 260 | 261 | #if HTTPD_USE_MEM_POOL 262 | #define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)memp_malloc(MEMP_HTTPD_SSI_STATE) 263 | #define HTTP_ALLOC_HTTP_STATE() (struct http_state *)memp_malloc(MEMP_HTTPD_STATE) 264 | #else /* HTTPD_USE_MEM_POOL */ 265 | #define HTTP_ALLOC_SSI_STATE() (struct http_ssi_state *)mem_malloc(sizeof(struct http_ssi_state)) 266 | #define HTTP_ALLOC_HTTP_STATE() (struct http_state *)mem_malloc(sizeof(struct http_state)) 267 | #endif /* HTTPD_USE_MEM_POOL */ 268 | 269 | typedef struct 270 | { 271 | const char *name; 272 | u8_t shtml; 273 | } default_filename; 274 | 275 | const default_filename g_psDefaultFilenames[] = { 276 | {"/index.shtml", 1 }, 277 | {"/index.ssi", 1 }, 278 | {"/index.shtm", 1 }, 279 | {"/index.html", 0 }, 280 | {"/index.htm", 0 } 281 | }; 282 | 283 | #define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) / \ 284 | sizeof(default_filename)) 285 | 286 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 287 | /** HTTP request is copied here from pbufs for simple parsing */ 288 | static char httpd_req_buf[LWIP_HTTPD_MAX_REQ_LENGTH+1]; 289 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 290 | 291 | #if LWIP_HTTPD_SUPPORT_POST 292 | /** Filename for response file to send when POST is finished */ 293 | static char http_post_response_filename[LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN+1]; 294 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 295 | 296 | #if LWIP_HTTPD_DYNAMIC_HEADERS 297 | /* The number of individual strings that comprise the headers sent before each 298 | * requested file. 299 | */ 300 | #define NUM_FILE_HDR_STRINGS 3 301 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 302 | 303 | #if LWIP_HTTPD_SSI 304 | 305 | #define HTTPD_LAST_TAG_PART 0xFFFF 306 | 307 | enum tag_check_state { 308 | TAG_NONE, /* Not processing an SSI tag */ 309 | TAG_LEADIN, /* Tag lead in "" being processed */ 312 | TAG_SENDING /* Sending tag replacement string */ 313 | }; 314 | 315 | struct http_ssi_state { 316 | const char *parsed; /* Pointer to the first unparsed byte in buf. */ 317 | #if !LWIP_HTTPD_SSI_INCLUDE_TAG 318 | const char *tag_started;/* Pointer to the first opening '<' of the tag. */ 319 | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ 320 | const char *tag_end; /* Pointer to char after the closing '>' of the tag. */ 321 | u32_t parse_left; /* Number of unparsed bytes in buf. */ 322 | u16_t tag_index; /* Counter used by tag parsing state machine */ 323 | u16_t tag_insert_len; /* Length of insert in string tag_insert */ 324 | #if LWIP_HTTPD_SSI_MULTIPART 325 | u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */ 326 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 327 | u8_t tag_name_len; /* Length of the tag name in string tag_name */ 328 | char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */ 329 | char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */ 330 | enum tag_check_state tag_state; /* State of the tag processor */ 331 | }; 332 | #endif /* LWIP_HTTPD_SSI */ 333 | 334 | struct http_state { 335 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 336 | struct http_state *next; 337 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 338 | struct fs_file file_handle; 339 | struct fs_file *handle; 340 | char *file; /* Pointer to first unsent byte in buf. */ 341 | 342 | struct tcp_pcb *pcb; 343 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 344 | struct pbuf *req; 345 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 346 | 347 | #if LWIP_HTTPD_DYNAMIC_FILE_READ 348 | char *buf; /* File read buffer. */ 349 | int buf_len; /* Size of file read buffer, buf. */ 350 | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ 351 | u32_t left; /* Number of unsent bytes in buf. */ 352 | u8_t retries; 353 | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE 354 | u8_t keepalive; 355 | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ 356 | #if LWIP_HTTPD_SSI 357 | struct http_ssi_state *ssi; 358 | #endif /* LWIP_HTTPD_SSI */ 359 | #if LWIP_HTTPD_CGI 360 | char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */ 361 | char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */ 362 | #endif /* LWIP_HTTPD_CGI */ 363 | #if LWIP_HTTPD_DYNAMIC_HEADERS 364 | const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */ 365 | u16_t hdr_pos; /* The position of the first unsent header byte in the 366 | current string */ 367 | u16_t hdr_index; /* The index of the hdr string currently being sent. */ 368 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 369 | #if LWIP_HTTPD_TIMING 370 | u32_t time_started; 371 | #endif /* LWIP_HTTPD_TIMING */ 372 | #if LWIP_HTTPD_SUPPORT_POST 373 | u32_t post_content_len_left; 374 | #if LWIP_HTTPD_POST_MANUAL_WND 375 | u32_t unrecved_bytes; 376 | u8_t no_auto_wnd; 377 | u8_t post_finished; 378 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 379 | #endif /* LWIP_HTTPD_SUPPORT_POST*/ 380 | }; 381 | 382 | static err_t http_close_conn(struct tcp_pcb *pcb, struct http_state *hs); 383 | static err_t http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn); 384 | static err_t http_find_file(struct http_state *hs, const char *uri, int is_09); 385 | static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check); 386 | static err_t http_poll(void *arg, struct tcp_pcb *pcb); 387 | #if LWIP_HTTPD_FS_ASYNC_READ 388 | static void http_continue(void *connection); 389 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 390 | 391 | #if LWIP_HTTPD_SSI 392 | /* SSI insert handler function pointer. */ 393 | tSSIHandler g_pfnSSIHandler = NULL; 394 | int g_iNumTags = 0; 395 | const char **g_ppcTags = NULL; 396 | 397 | #define LEN_TAG_LEAD_IN 5 398 | const char * const g_pcTagLeadIn = ""; 402 | #endif /* LWIP_HTTPD_SSI */ 403 | 404 | #if LWIP_HTTPD_CGI 405 | /* CGI handler information */ 406 | const tCGI *g_pCGIs; 407 | int g_iNumCGIs; 408 | #endif /* LWIP_HTTPD_CGI */ 409 | 410 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 411 | /** global list of active HTTP connections, use to kill the oldest when 412 | running out of memory */ 413 | static struct http_state *http_connections; 414 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 415 | 416 | 417 | #if LWIP_HTTPD_STRNSTR_PRIVATE 418 | /** Like strstr but does not need 'buffer' to be NULL-terminated */ 419 | static char* 420 | strnstr(const char* buffer, const char* token, size_t n) 421 | { 422 | const char* p; 423 | int tokenlen = (int)strlen(token); 424 | if (tokenlen == 0) { 425 | return (char *)buffer; 426 | } 427 | for (p = buffer; *p && (p + tokenlen <= buffer + n); p++) { 428 | if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) { 429 | return (char *)p; 430 | } 431 | } 432 | return NULL; 433 | } 434 | #endif /* LWIP_HTTPD_STRNSTR_PRIVATE */ 435 | 436 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 437 | static void 438 | http_kill_oldest_connection(u8_t ssi_required) 439 | { 440 | struct http_state *hs = http_connections; 441 | struct http_state *hs_free_next = NULL; 442 | while(hs && hs->next) { 443 | if (ssi_required) { 444 | if (hs->next->ssi != NULL) { 445 | hs_free_next = hs; 446 | } 447 | } else { 448 | hs_free_next = hs; 449 | } 450 | hs = hs->next; 451 | } 452 | if (hs_free_next != NULL) { 453 | LWIP_ASSERT("hs_free_next->next != NULL", hs_free_next->next != NULL); 454 | LWIP_ASSERT("hs_free_next->next->pcb != NULL", hs_free_next->next->pcb != NULL); 455 | /* send RST when killing a connection because of memory shortage */ 456 | http_close_or_abort_conn(hs_free_next->next->pcb, hs_free_next->next, 1); /* this also unlinks the http_state from the list */ 457 | } 458 | } 459 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 460 | 461 | #if LWIP_HTTPD_SSI 462 | /** Allocate as struct http_ssi_state. */ 463 | static struct http_ssi_state* 464 | http_ssi_state_alloc(void) 465 | { 466 | struct http_ssi_state *ret = HTTP_ALLOC_SSI_STATE(); 467 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 468 | if (ret == NULL) { 469 | http_kill_oldest_connection(1); 470 | ret = HTTP_ALLOC_SSI_STATE(); 471 | } 472 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 473 | if (ret != NULL) { 474 | memset(ret, 0, sizeof(struct http_ssi_state)); 475 | } 476 | return ret; 477 | } 478 | 479 | /** Free a struct http_ssi_state. */ 480 | static void 481 | http_ssi_state_free(struct http_ssi_state *ssi) 482 | { 483 | if (ssi != NULL) { 484 | #if HTTPD_USE_MEM_POOL 485 | memp_free(MEMP_HTTPD_SSI_STATE, ssi); 486 | #else /* HTTPD_USE_MEM_POOL */ 487 | mem_free(ssi); 488 | #endif /* HTTPD_USE_MEM_POOL */ 489 | } 490 | } 491 | #endif /* LWIP_HTTPD_SSI */ 492 | 493 | /** Initialize a struct http_state. 494 | */ 495 | static void 496 | http_state_init(struct http_state* hs) 497 | { 498 | /* Initialize the structure. */ 499 | memset(hs, 0, sizeof(struct http_state)); 500 | #if LWIP_HTTPD_DYNAMIC_HEADERS 501 | /* Indicate that the headers are not yet valid */ 502 | hs->hdr_index = NUM_FILE_HDR_STRINGS; 503 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 504 | } 505 | 506 | /** Allocate a struct http_state. */ 507 | static struct http_state* 508 | http_state_alloc(void) 509 | { 510 | struct http_state *ret = HTTP_ALLOC_HTTP_STATE(); 511 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 512 | if (ret == NULL) { 513 | http_kill_oldest_connection(0); 514 | ret = HTTP_ALLOC_HTTP_STATE(); 515 | } 516 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 517 | if (ret != NULL) { 518 | http_state_init(ret); 519 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 520 | /* add the connection to the list */ 521 | if (http_connections == NULL) { 522 | http_connections = ret; 523 | } else { 524 | struct http_state *last; 525 | for(last = http_connections; last->next != NULL; last = last->next); 526 | LWIP_ASSERT("last != NULL", last != NULL); 527 | last->next = ret; 528 | } 529 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 530 | } 531 | return ret; 532 | } 533 | 534 | /** Free a struct http_state. 535 | * Also frees the file data if dynamic. 536 | */ 537 | static void 538 | http_state_eof(struct http_state *hs) 539 | { 540 | if(hs->handle) { 541 | #if LWIP_HTTPD_TIMING 542 | u32_t ms_needed = sys_now() - hs->time_started; 543 | u32_t needed = LWIP_MAX(1, (ms_needed/100)); 544 | LWIP_DEBUGF(HTTPD_DEBUG_TIMING, ("httpd: needed %"U32_F" ms to send file of %d bytes -> %"U32_F" bytes/sec\n", 545 | ms_needed, hs->handle->len, ((((u32_t)hs->handle->len) * 10) / needed))); 546 | #endif /* LWIP_HTTPD_TIMING */ 547 | fs_close(hs->handle); 548 | hs->handle = NULL; 549 | } 550 | #if LWIP_HTTPD_DYNAMIC_FILE_READ 551 | if (hs->buf != NULL) { 552 | mem_free(hs->buf); 553 | hs->buf = NULL; 554 | } 555 | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ 556 | #if LWIP_HTTPD_SSI 557 | if (hs->ssi) { 558 | http_ssi_state_free(hs->ssi); 559 | hs->ssi = NULL; 560 | } 561 | #endif /* LWIP_HTTPD_SSI */ 562 | } 563 | 564 | /** Free a struct http_state. 565 | * Also frees the file data if dynamic. 566 | */ 567 | static void 568 | http_state_free(struct http_state *hs) 569 | { 570 | if (hs != NULL) { 571 | http_state_eof(hs); 572 | #if LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED 573 | /* take the connection off the list */ 574 | if (http_connections) { 575 | if (http_connections == hs) { 576 | http_connections = hs->next; 577 | } else { 578 | struct http_state *last; 579 | for(last = http_connections; last->next != NULL; last = last->next) { 580 | if (last->next == hs) { 581 | last->next = hs->next; 582 | break; 583 | } 584 | } 585 | } 586 | } 587 | #endif /* LWIP_HTTPD_KILL_OLD_ON_CONNECTIONS_EXCEEDED */ 588 | #if HTTPD_USE_MEM_POOL 589 | memp_free(MEMP_HTTPD_STATE, hs); 590 | #else /* HTTPD_USE_MEM_POOL */ 591 | mem_free(hs); 592 | #endif /* HTTPD_USE_MEM_POOL */ 593 | } 594 | } 595 | 596 | /** Call tcp_write() in a loop trying smaller and smaller length 597 | * 598 | * @param pcb tcp_pcb to send 599 | * @param ptr Data to send 600 | * @param length Length of data to send (in/out: on return, contains the 601 | * amount of data sent) 602 | * @param apiflags directly passed to tcp_write 603 | * @return the return value of tcp_write 604 | */ 605 | static err_t 606 | http_write(struct tcp_pcb *pcb, const void* ptr, u16_t *length, u8_t apiflags) 607 | { 608 | u16_t len; 609 | err_t err; 610 | LWIP_ASSERT("length != NULL", length != NULL); 611 | len = *length; 612 | if (len == 0) { 613 | return ERR_OK; 614 | } 615 | do { 616 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Trying go send %d bytes\n", len)); 617 | err = tcp_write(pcb, ptr, len, apiflags); 618 | if (err == ERR_MEM) { 619 | if ((tcp_sndbuf(pcb) == 0) || 620 | (tcp_sndqueuelen(pcb) >= TCP_SND_QUEUELEN)) { 621 | /* no need to try smaller sizes */ 622 | len = 1; 623 | } else { 624 | len /= 2; 625 | } 626 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, 627 | ("Send failed, trying less (%d bytes)\n", len)); 628 | } 629 | } while ((err == ERR_MEM) && (len > 1)); 630 | 631 | if (err == ERR_OK) { 632 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Sent %d bytes\n", len)); 633 | } else { 634 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Send failed with err %d (\"%s\")\n", err, lwip_strerr(err))); 635 | } 636 | 637 | *length = len; 638 | return err; 639 | } 640 | 641 | /** 642 | * The connection shall be actively closed (using RST to close from fault states). 643 | * Reset the sent- and recv-callbacks. 644 | * 645 | * @param pcb the tcp pcb to reset callbacks 646 | * @param hs connection state to free 647 | */ 648 | static err_t 649 | http_close_or_abort_conn(struct tcp_pcb *pcb, struct http_state *hs, u8_t abort_conn) 650 | { 651 | err_t err; 652 | LWIP_DEBUGF(HTTPD_DEBUG, ("Closing connection %p\n", (void*)pcb)); 653 | 654 | #if LWIP_HTTPD_SUPPORT_POST 655 | if (hs != NULL) { 656 | if ((hs->post_content_len_left != 0) 657 | #if LWIP_HTTPD_POST_MANUAL_WND 658 | || ((hs->no_auto_wnd != 0) && (hs->unrecved_bytes != 0)) 659 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 660 | ) { 661 | /* make sure the post code knows that the connection is closed */ 662 | http_post_response_filename[0] = 0; 663 | httpd_post_finished(hs, http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN); 664 | } 665 | } 666 | #endif /* LWIP_HTTPD_SUPPORT_POST*/ 667 | 668 | 669 | tcp_arg(pcb, NULL); 670 | tcp_recv(pcb, NULL); 671 | tcp_err(pcb, NULL); 672 | tcp_poll(pcb, NULL, 0); 673 | tcp_sent(pcb, NULL); 674 | if (hs != NULL) { 675 | http_state_free(hs); 676 | } 677 | 678 | if (abort_conn) { 679 | tcp_abort(pcb); 680 | return ERR_OK; 681 | } 682 | err = tcp_close(pcb); 683 | if (err != ERR_OK) { 684 | LWIP_DEBUGF(HTTPD_DEBUG, ("Error %d closing %p\n", err, (void*)pcb)); 685 | /* error closing, try again later in poll */ 686 | tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); 687 | } 688 | return err; 689 | } 690 | 691 | /** 692 | * The connection shall be actively closed. 693 | * Reset the sent- and recv-callbacks. 694 | * 695 | * @param pcb the tcp pcb to reset callbacks 696 | * @param hs connection state to free 697 | */ 698 | static err_t 699 | http_close_conn(struct tcp_pcb *pcb, struct http_state *hs) 700 | { 701 | return http_close_or_abort_conn(pcb, hs, 0); 702 | } 703 | 704 | /** End of file: either close the connection (Connection: close) or 705 | * close the file (Connection: keep-alive) 706 | */ 707 | static void 708 | http_eof(struct tcp_pcb *pcb, struct http_state *hs) 709 | { 710 | /* HTTP/1.1 persistent connection? (Not supported for SSI) */ 711 | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE 712 | if (hs->keepalive && !LWIP_HTTPD_IS_SSI(hs)) { 713 | http_state_eof(hs); 714 | http_state_init(hs); 715 | hs->keepalive = 1; 716 | } else 717 | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ 718 | { 719 | http_close_conn(pcb, hs); 720 | } 721 | } 722 | 723 | #if LWIP_HTTPD_CGI 724 | /** 725 | * Extract URI parameters from the parameter-part of an URI in the form 726 | * "test.cgi?x=y" @todo: better explanation! 727 | * Pointers to the parameters are stored in hs->param_vals. 728 | * 729 | * @param hs http connection state 730 | * @param params pointer to the NULL-terminated parameter string from the URI 731 | * @return number of parameters extracted 732 | */ 733 | static int 734 | extract_uri_parameters(struct http_state *hs, char *params) 735 | { 736 | char *pair; 737 | char *equals; 738 | int loop; 739 | 740 | /* If we have no parameters at all, return immediately. */ 741 | if(!params || (params[0] == '\0')) { 742 | return(0); 743 | } 744 | 745 | /* Get a pointer to our first parameter */ 746 | pair = params; 747 | 748 | /* Parse up to LWIP_HTTPD_MAX_CGI_PARAMETERS from the passed string and ignore the 749 | * remainder (if any) */ 750 | for(loop = 0; (loop < LWIP_HTTPD_MAX_CGI_PARAMETERS) && pair; loop++) { 751 | 752 | /* Save the name of the parameter */ 753 | hs->params[loop] = pair; 754 | 755 | /* Remember the start of this name=value pair */ 756 | equals = pair; 757 | 758 | /* Find the start of the next name=value pair and replace the delimiter 759 | * with a 0 to terminate the previous pair string. */ 760 | pair = strchr(pair, '&'); 761 | if(pair) { 762 | *pair = '\0'; 763 | pair++; 764 | } else { 765 | /* We didn't find a new parameter so find the end of the URI and 766 | * replace the space with a '\0' */ 767 | pair = strchr(equals, ' '); 768 | if(pair) { 769 | *pair = '\0'; 770 | } 771 | 772 | /* Revert to NULL so that we exit the loop as expected. */ 773 | pair = NULL; 774 | } 775 | 776 | /* Now find the '=' in the previous pair, replace it with '\0' and save 777 | * the parameter value string. */ 778 | equals = strchr(equals, '='); 779 | if(equals) { 780 | *equals = '\0'; 781 | hs->param_vals[loop] = equals + 1; 782 | } else { 783 | hs->param_vals[loop] = NULL; 784 | } 785 | } 786 | 787 | return loop; 788 | } 789 | #endif /* LWIP_HTTPD_CGI */ 790 | 791 | #if LWIP_HTTPD_SSI 792 | /** 793 | * Insert a tag (found in an shtml in the form of "" into the file. 794 | * The tag's name is stored in ssi->tag_name (NULL-terminated), the replacement 795 | * should be written to hs->tag_insert (up to a length of LWIP_HTTPD_MAX_TAG_INSERT_LEN). 796 | * The amount of data written is stored to ssi->tag_insert_len. 797 | * 798 | * @todo: return tag_insert_len - maybe it can be removed from struct http_state? 799 | * 800 | * @param hs http connection state 801 | */ 802 | static void 803 | get_tag_insert(struct http_state *hs) 804 | { 805 | int loop; 806 | size_t len; 807 | struct http_ssi_state *ssi; 808 | LWIP_ASSERT("hs != NULL", hs != NULL); 809 | ssi = hs->ssi; 810 | LWIP_ASSERT("ssi != NULL", ssi != NULL); 811 | #if LWIP_HTTPD_SSI_MULTIPART 812 | u16_t current_tag_part = ssi->tag_part; 813 | ssi->tag_part = HTTPD_LAST_TAG_PART; 814 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 815 | 816 | if(g_pfnSSIHandler && g_ppcTags && g_iNumTags) { 817 | 818 | /* Find this tag in the list we have been provided. */ 819 | for(loop = 0; loop < g_iNumTags; loop++) { 820 | if(strcmp(ssi->tag_name, g_ppcTags[loop]) == 0) { 821 | ssi->tag_insert_len = g_pfnSSIHandler(loop, ssi->tag_insert, 822 | LWIP_HTTPD_MAX_TAG_INSERT_LEN 823 | #if LWIP_HTTPD_SSI_MULTIPART 824 | , current_tag_part, &ssi->tag_part 825 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 826 | #if LWIP_HTTPD_FILE_STATE 827 | , hs->handle->state 828 | #endif /* LWIP_HTTPD_FILE_STATE */ 829 | ); 830 | return; 831 | } 832 | } 833 | } 834 | 835 | /* If we drop out, we were asked to serve a page which contains tags that 836 | * we don't have a handler for. Merely echo back the tags with an error 837 | * marker. */ 838 | #define UNKNOWN_TAG1_TEXT "***UNKNOWN TAG " 839 | #define UNKNOWN_TAG1_LEN 18 840 | #define UNKNOWN_TAG2_TEXT "***" 841 | #define UNKNOWN_TAG2_LEN 7 842 | len = LWIP_MIN(strlen(ssi->tag_name), 843 | LWIP_HTTPD_MAX_TAG_INSERT_LEN - (UNKNOWN_TAG1_LEN + UNKNOWN_TAG2_LEN)); 844 | MEMCPY(ssi->tag_insert, UNKNOWN_TAG1_TEXT, UNKNOWN_TAG1_LEN); 845 | MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN], ssi->tag_name, len); 846 | MEMCPY(&ssi->tag_insert[UNKNOWN_TAG1_LEN + len], UNKNOWN_TAG2_TEXT, UNKNOWN_TAG2_LEN); 847 | ssi->tag_insert[UNKNOWN_TAG1_LEN + len + UNKNOWN_TAG2_LEN] = 0; 848 | 849 | len = strlen(ssi->tag_insert); 850 | LWIP_ASSERT("len <= 0xffff", len <= 0xffff); 851 | ssi->tag_insert_len = (u16_t)len; 852 | } 853 | #endif /* LWIP_HTTPD_SSI */ 854 | 855 | #if LWIP_HTTPD_DYNAMIC_HEADERS 856 | /** 857 | * Generate the relevant HTTP headers for the given filename and write 858 | * them into the supplied buffer. 859 | */ 860 | static void 861 | get_http_headers(struct http_state *pState, char *pszURI) 862 | { 863 | unsigned int iLoop; 864 | char *pszWork; 865 | char *pszExt; 866 | char *pszVars; 867 | 868 | /* Ensure that we initialize the loop counter. */ 869 | iLoop = 0; 870 | 871 | /* In all cases, the second header we send is the server identification 872 | so set it here. */ 873 | pState->hdrs[1] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER]; 874 | 875 | /* Is this a normal file or the special case we use to send back the 876 | default "404: Page not found" response? */ 877 | if (pszURI == NULL) { 878 | pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; 879 | pState->hdrs[2] = g_psHTTPHeaderStrings[DEFAULT_404_HTML]; 880 | 881 | /* Set up to send the first header string. */ 882 | pState->hdr_index = 0; 883 | pState->hdr_pos = 0; 884 | return; 885 | } else { 886 | /* We are dealing with a particular filename. Look for one other 887 | special case. We assume that any filename with "404" in it must be 888 | indicative of a 404 server error whereas all other files require 889 | the 200 OK header. */ 890 | if (strstr(pszURI, "404")) { 891 | pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND]; 892 | } else if (strstr(pszURI, "400")) { 893 | pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_BAD_REQUEST]; 894 | } else if (strstr(pszURI, "501")) { 895 | pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_IMPL]; 896 | } else { 897 | pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_OK]; 898 | } 899 | 900 | /* Determine if the URI has any variables and, if so, temporarily remove 901 | them. */ 902 | pszVars = strchr(pszURI, '?'); 903 | if(pszVars) { 904 | *pszVars = '\0'; 905 | } 906 | 907 | /* Get a pointer to the file extension. We find this by looking for the 908 | last occurrence of "." in the filename passed. */ 909 | pszExt = NULL; 910 | pszWork = strchr(pszURI, '.'); 911 | while(pszWork) { 912 | pszExt = pszWork + 1; 913 | pszWork = strchr(pszExt, '.'); 914 | } 915 | 916 | /* Now determine the content type and add the relevant header for that. */ 917 | for(iLoop = 0; (iLoop < NUM_HTTP_HEADERS) && pszExt; iLoop++) { 918 | /* Have we found a matching extension? */ 919 | if(!strcmp(g_psHTTPHeaders[iLoop].extension, pszExt)) { 920 | pState->hdrs[2] = 921 | g_psHTTPHeaderStrings[g_psHTTPHeaders[iLoop].headerIndex]; 922 | break; 923 | } 924 | } 925 | 926 | /* Reinstate the parameter marker if there was one in the original URI. */ 927 | if(pszVars) { 928 | *pszVars = '?'; 929 | } 930 | } 931 | 932 | /* Does the URL passed have any file extension? If not, we assume it 933 | is a special-case URL used for control state notification and we do 934 | not send any HTTP headers with the response. */ 935 | if(!pszExt) { 936 | /* Force the header index to a value indicating that all headers 937 | have already been sent. */ 938 | pState->hdr_index = NUM_FILE_HDR_STRINGS; 939 | } else { 940 | /* Did we find a matching extension? */ 941 | if(iLoop == NUM_HTTP_HEADERS) { 942 | /* No - use the default, plain text file type. */ 943 | pState->hdrs[2] = g_psHTTPHeaderStrings[HTTP_HDR_DEFAULT_TYPE]; 944 | } 945 | 946 | /* Set up to send the first header string. */ 947 | pState->hdr_index = 0; 948 | pState->hdr_pos = 0; 949 | } 950 | } 951 | 952 | /** Sub-function of http_send(): send dynamic headers 953 | * 954 | * @returns: - HTTP_NO_DATA_TO_SEND: no new data has been enqueued 955 | * - HTTP_DATA_TO_SEND_CONTINUE: continue with sending HTTP body 956 | * - HTTP_DATA_TO_SEND_BREAK: data has been enqueued, headers pending, 957 | * so don't send HTTP body yet 958 | */ 959 | static u8_t 960 | http_send_headers(struct tcp_pcb *pcb, struct http_state *hs) 961 | { 962 | err_t err; 963 | u16_t len; 964 | u8_t data_to_send = HTTP_NO_DATA_TO_SEND; 965 | u16_t hdrlen, sendlen; 966 | 967 | /* How much data can we send? */ 968 | len = tcp_sndbuf(pcb); 969 | sendlen = len; 970 | 971 | while(len && (hs->hdr_index < NUM_FILE_HDR_STRINGS) && sendlen) { 972 | const void *ptr; 973 | u16_t old_sendlen; 974 | /* How much do we have to send from the current header? */ 975 | hdrlen = (u16_t)strlen(hs->hdrs[hs->hdr_index]); 976 | 977 | /* How much of this can we send? */ 978 | sendlen = (len < (hdrlen - hs->hdr_pos)) ? len : (hdrlen - hs->hdr_pos); 979 | 980 | /* Send this amount of data or as much as we can given memory 981 | * constraints. */ 982 | ptr = (const void *)(hs->hdrs[hs->hdr_index] + hs->hdr_pos); 983 | old_sendlen = sendlen; 984 | err = http_write(pcb, ptr, &sendlen, HTTP_IS_HDR_VOLATILE(hs, ptr)); 985 | if ((err == ERR_OK) && (old_sendlen != sendlen)) { 986 | /* Remember that we added some more data to be transmitted. */ 987 | data_to_send = HTTP_DATA_TO_SEND_CONTINUE; 988 | } else if (err != ERR_OK) { 989 | /* special case: http_write does not try to send 1 byte */ 990 | sendlen = 0; 991 | } 992 | 993 | /* Fix up the header position for the next time round. */ 994 | hs->hdr_pos += sendlen; 995 | len -= sendlen; 996 | 997 | /* Have we finished sending this string? */ 998 | if(hs->hdr_pos == hdrlen) { 999 | /* Yes - move on to the next one */ 1000 | hs->hdr_index++; 1001 | hs->hdr_pos = 0; 1002 | } 1003 | } 1004 | /* If we get here and there are still header bytes to send, we send 1005 | * the header information we just wrote immediately. If there are no 1006 | * more headers to send, but we do have file data to send, drop through 1007 | * to try to send some file data too. */ 1008 | if((hs->hdr_index < NUM_FILE_HDR_STRINGS) || !hs->file) { 1009 | LWIP_DEBUGF(HTTPD_DEBUG, ("tcp_output\n")); 1010 | return HTTP_DATA_TO_SEND_BREAK; 1011 | } 1012 | return data_to_send; 1013 | } 1014 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 1015 | 1016 | /** Sub-function of http_send(): end-of-file (or block) is reached, 1017 | * either close the file or read the next block (if supported). 1018 | * 1019 | * @returns: 0 if the file is finished or no data has been read 1020 | * 1 if the file is not finished and data has been read 1021 | */ 1022 | static u8_t 1023 | http_check_eof(struct tcp_pcb *pcb, struct http_state *hs) 1024 | { 1025 | #if LWIP_HTTPD_DYNAMIC_FILE_READ 1026 | int count; 1027 | #endif /* LWIP_HTTPD_DYNAMIC_FILE_READ */ 1028 | 1029 | /* Do we have a valid file handle? */ 1030 | if (hs->handle == NULL) { 1031 | /* No - close the connection. */ 1032 | http_eof(pcb, hs); 1033 | return 0; 1034 | } 1035 | if (fs_bytes_left(hs->handle) <= 0) { 1036 | /* We reached the end of the file so this request is done. */ 1037 | LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); 1038 | http_eof(pcb, hs); 1039 | return 0; 1040 | } 1041 | #if LWIP_HTTPD_DYNAMIC_FILE_READ 1042 | /* Do we already have a send buffer allocated? */ 1043 | if(hs->buf) { 1044 | /* Yes - get the length of the buffer */ 1045 | count = hs->buf_len; 1046 | } else { 1047 | /* We don't have a send buffer so allocate one up to 2mss bytes long. */ 1048 | count = 2 * tcp_mss(pcb); 1049 | do { 1050 | hs->buf = (char*)mem_malloc((mem_size_t)count); 1051 | if (hs->buf != NULL) { 1052 | hs->buf_len = count; 1053 | break; 1054 | } 1055 | count = count / 2; 1056 | } while (count > 100); 1057 | 1058 | /* Did we get a send buffer? If not, return immediately. */ 1059 | if (hs->buf == NULL) { 1060 | LWIP_DEBUGF(HTTPD_DEBUG, ("No buff\n")); 1061 | return 0; 1062 | } 1063 | } 1064 | 1065 | /* Read a block of data from the file. */ 1066 | LWIP_DEBUGF(HTTPD_DEBUG, ("Trying to read %d bytes.\n", count)); 1067 | 1068 | #if LWIP_HTTPD_FS_ASYNC_READ 1069 | count = fs_read_async(hs->handle, hs->buf, count, http_continue, hs); 1070 | #else /* LWIP_HTTPD_FS_ASYNC_READ */ 1071 | count = fs_read(hs->handle, hs->buf, count); 1072 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 1073 | if (count < 0) { 1074 | if (count == FS_READ_DELAYED) { 1075 | /* Delayed read, wait for FS to unblock us */ 1076 | return 0; 1077 | } 1078 | /* We reached the end of the file so this request is done. 1079 | * @todo: don't close here for HTTP/1.1? */ 1080 | LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); 1081 | http_eof(pcb, hs); 1082 | return 0; 1083 | } 1084 | 1085 | /* Set up to send the block of data we just read */ 1086 | LWIP_DEBUGF(HTTPD_DEBUG, ("Read %d bytes.\n", count)); 1087 | hs->left = count; 1088 | hs->file = hs->buf; 1089 | #if LWIP_HTTPD_SSI 1090 | if (hs->ssi) { 1091 | hs->ssi->parse_left = count; 1092 | hs->ssi->parsed = hs->buf; 1093 | } 1094 | #endif /* LWIP_HTTPD_SSI */ 1095 | #else /* LWIP_HTTPD_DYNAMIC_FILE_READ */ 1096 | LWIP_ASSERT("SSI and DYNAMIC_HEADERS turned off but eof not reached", 0); 1097 | #endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */ 1098 | return 1; 1099 | } 1100 | 1101 | /** Sub-function of http_send(): This is the normal send-routine for non-ssi files 1102 | * 1103 | * @returns: - 1: data has been written (so call tcp_ouput) 1104 | * - 0: no data has been written (no need to call tcp_output) 1105 | */ 1106 | static u8_t 1107 | http_send_data_nonssi(struct tcp_pcb *pcb, struct http_state *hs) 1108 | { 1109 | err_t err; 1110 | u16_t len; 1111 | u16_t mss; 1112 | u8_t data_to_send = 0; 1113 | 1114 | /* We are not processing an SHTML file so no tag checking is necessary. 1115 | * Just send the data as we received it from the file. */ 1116 | 1117 | /* We cannot send more data than space available in the send 1118 | buffer. */ 1119 | if (tcp_sndbuf(pcb) < hs->left) { 1120 | len = tcp_sndbuf(pcb); 1121 | } else { 1122 | len = (u16_t)hs->left; 1123 | LWIP_ASSERT("hs->left did not fit into u16_t!", (len == hs->left)); 1124 | } 1125 | mss = tcp_mss(pcb); 1126 | if (len > (2 * mss)) { 1127 | len = 2 * mss; 1128 | } 1129 | 1130 | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); 1131 | if (err == ERR_OK) { 1132 | data_to_send = 1; 1133 | hs->file += len; 1134 | hs->left -= len; 1135 | } 1136 | 1137 | return data_to_send; 1138 | } 1139 | 1140 | #if LWIP_HTTPD_SSI 1141 | /** Sub-function of http_send(): This is the send-routine for ssi files 1142 | * 1143 | * @returns: - 1: data has been written (so call tcp_ouput) 1144 | * - 0: no data has been written (no need to call tcp_output) 1145 | */ 1146 | static u8_t 1147 | http_send_data_ssi(struct tcp_pcb *pcb, struct http_state *hs) 1148 | { 1149 | err_t err = ERR_OK; 1150 | u16_t len; 1151 | u16_t mss; 1152 | u8_t data_to_send = 0; 1153 | 1154 | struct http_ssi_state *ssi = hs->ssi; 1155 | LWIP_ASSERT("ssi != NULL", ssi != NULL); 1156 | /* We are processing an SHTML file so need to scan for tags and replace 1157 | * them with insert strings. We need to be careful here since a tag may 1158 | * straddle the boundary of two blocks read from the file and we may also 1159 | * have to split the insert string between two tcp_write operations. */ 1160 | 1161 | /* How much data could we send? */ 1162 | len = tcp_sndbuf(pcb); 1163 | 1164 | /* Do we have remaining data to send before parsing more? */ 1165 | if(ssi->parsed > hs->file) { 1166 | /* We cannot send more data than space available in the send 1167 | buffer. */ 1168 | if (tcp_sndbuf(pcb) < (ssi->parsed - hs->file)) { 1169 | len = tcp_sndbuf(pcb); 1170 | } else { 1171 | LWIP_ASSERT("Data size does not fit into u16_t!", 1172 | (ssi->parsed - hs->file) <= 0xffff); 1173 | len = (u16_t)(ssi->parsed - hs->file); 1174 | } 1175 | mss = tcp_mss(pcb); 1176 | if(len > (2 * mss)) { 1177 | len = 2 * mss; 1178 | } 1179 | 1180 | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); 1181 | if (err == ERR_OK) { 1182 | data_to_send = 1; 1183 | hs->file += len; 1184 | hs->left -= len; 1185 | } 1186 | 1187 | /* If the send buffer is full, return now. */ 1188 | if(tcp_sndbuf(pcb) == 0) { 1189 | return data_to_send; 1190 | } 1191 | } 1192 | 1193 | LWIP_DEBUGF(HTTPD_DEBUG, ("State %d, %d left\n", ssi->tag_state, (int)ssi->parse_left)); 1194 | 1195 | /* We have sent all the data that was already parsed so continue parsing 1196 | * the buffer contents looking for SSI tags. */ 1197 | while((ssi->parse_left) && (err == ERR_OK)) { 1198 | /* @todo: somewhere in this loop, 'len' should grow again... */ 1199 | if (len == 0) { 1200 | return data_to_send; 1201 | } 1202 | switch(ssi->tag_state) { 1203 | case TAG_NONE: 1204 | /* We are not currently processing an SSI tag so scan for the 1205 | * start of the lead-in marker. */ 1206 | if(*ssi->parsed == g_pcTagLeadIn[0]) { 1207 | /* We found what could be the lead-in for a new tag so change 1208 | * state appropriately. */ 1209 | ssi->tag_state = TAG_LEADIN; 1210 | ssi->tag_index = 1; 1211 | #if !LWIP_HTTPD_SSI_INCLUDE_TAG 1212 | ssi->tag_started = ssi->parsed; 1213 | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */ 1214 | } 1215 | 1216 | /* Move on to the next character in the buffer */ 1217 | ssi->parse_left--; 1218 | ssi->parsed++; 1219 | break; 1220 | 1221 | case TAG_LEADIN: 1222 | /* We are processing the lead-in marker, looking for the start of 1223 | * the tag name. */ 1224 | 1225 | /* Have we reached the end of the leadin? */ 1226 | if(ssi->tag_index == LEN_TAG_LEAD_IN) { 1227 | ssi->tag_index = 0; 1228 | ssi->tag_state = TAG_FOUND; 1229 | } else { 1230 | /* Have we found the next character we expect for the tag leadin? */ 1231 | if(*ssi->parsed == g_pcTagLeadIn[ssi->tag_index]) { 1232 | /* Yes - move to the next one unless we have found the complete 1233 | * leadin, in which case we start looking for the tag itself */ 1234 | ssi->tag_index++; 1235 | } else { 1236 | /* We found an unexpected character so this is not a tag. Move 1237 | * back to idle state. */ 1238 | ssi->tag_state = TAG_NONE; 1239 | } 1240 | 1241 | /* Move on to the next character in the buffer */ 1242 | ssi->parse_left--; 1243 | ssi->parsed++; 1244 | } 1245 | break; 1246 | 1247 | case TAG_FOUND: 1248 | /* We are reading the tag name, looking for the start of the 1249 | * lead-out marker and removing any whitespace found. */ 1250 | 1251 | /* Remove leading whitespace between the tag leading and the first 1252 | * tag name character. */ 1253 | if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || 1254 | (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || 1255 | (*ssi->parsed == '\r'))) { 1256 | /* Move on to the next character in the buffer */ 1257 | ssi->parse_left--; 1258 | ssi->parsed++; 1259 | break; 1260 | } 1261 | 1262 | /* Have we found the end of the tag name? This is signalled by 1263 | * us finding the first leadout character or whitespace */ 1264 | if((*ssi->parsed == g_pcTagLeadOut[0]) || 1265 | (*ssi->parsed == ' ') || (*ssi->parsed == '\t') || 1266 | (*ssi->parsed == '\n') || (*ssi->parsed == '\r')) { 1267 | 1268 | 1269 | 1270 | if(ssi->tag_index == 0) { 1271 | /* We read a zero length tag so ignore it. */ 1272 | ssi->tag_state = TAG_NONE; 1273 | } else { 1274 | /* We read a non-empty tag so go ahead and look for the 1275 | * leadout string. */ 1276 | ssi->tag_state = TAG_LEADOUT; 1277 | LWIP_ASSERT("ssi->tag_index <= 0xff", ssi->tag_index <= 0xff); 1278 | ssi->tag_name_len = (u8_t)ssi->tag_index; 1279 | ssi->tag_name[ssi->tag_index] = '\0'; 1280 | if(*ssi->parsed == g_pcTagLeadOut[0]) { 1281 | ssi->tag_index = 1; 1282 | } else { 1283 | ssi->tag_index = 0; 1284 | } 1285 | } 1286 | } else { 1287 | /* This character is part of the tag name so save it */ 1288 | if(ssi->tag_index < LWIP_HTTPD_MAX_TAG_NAME_LEN) { 1289 | ssi->tag_name[ssi->tag_index++] = *ssi->parsed; 1290 | } else { 1291 | /* The tag was too long so ignore it. */ 1292 | ssi->tag_state = TAG_NONE; 1293 | } 1294 | } 1295 | 1296 | /* Move on to the next character in the buffer */ 1297 | ssi->parse_left--; 1298 | ssi->parsed++; 1299 | 1300 | break; 1301 | 1302 | /* We are looking for the end of the lead-out marker. */ 1303 | case TAG_LEADOUT: 1304 | /* Remove leading whitespace between the tag leading and the first 1305 | * tag leadout character. */ 1306 | if((ssi->tag_index == 0) && ((*ssi->parsed == ' ') || 1307 | (*ssi->parsed == '\t') || (*ssi->parsed == '\n') || 1308 | (*ssi->parsed == '\r'))) { 1309 | /* Move on to the next character in the buffer */ 1310 | ssi->parse_left--; 1311 | ssi->parsed++; 1312 | break; 1313 | } 1314 | 1315 | /* Have we found the next character we expect for the tag leadout? */ 1316 | if(*ssi->parsed == g_pcTagLeadOut[ssi->tag_index]) { 1317 | /* Yes - move to the next one unless we have found the complete 1318 | * leadout, in which case we need to call the client to process 1319 | * the tag. */ 1320 | 1321 | /* Move on to the next character in the buffer */ 1322 | ssi->parse_left--; 1323 | ssi->parsed++; 1324 | 1325 | 1326 | if(ssi->tag_index == (LEN_TAG_LEAD_OUT - 1)) { 1327 | /* Call the client to ask for the insert string for the 1328 | * tag we just found. */ 1329 | #if LWIP_HTTPD_SSI_MULTIPART 1330 | ssi->tag_part = 0; /* start with tag part 0 */ 1331 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 1332 | get_tag_insert(hs); 1333 | 1334 | /* Next time through, we are going to be sending data 1335 | * immediately, either the end of the block we start 1336 | * sending here or the insert string. */ 1337 | ssi->tag_index = 0; 1338 | ssi->tag_state = TAG_SENDING; 1339 | ssi->tag_end = ssi->parsed; 1340 | #if !LWIP_HTTPD_SSI_INCLUDE_TAG 1341 | ssi->parsed = ssi->tag_started; 1342 | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1343 | 1344 | /* If there is any unsent data in the buffer prior to the 1345 | * tag, we need to send it now. */ 1346 | if (ssi->tag_end > hs->file) { 1347 | /* How much of the data can we send? */ 1348 | #if LWIP_HTTPD_SSI_INCLUDE_TAG 1349 | if(len > ssi->tag_end - hs->file) { 1350 | len = (u16_t)(ssi->tag_end - hs->file); 1351 | } 1352 | #else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1353 | if(len > ssi->tag_started - hs->file) { 1354 | /* we would include the tag in sending */ 1355 | len = (u16_t)(ssi->tag_started - hs->file); 1356 | } 1357 | #endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1358 | 1359 | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); 1360 | if (err == ERR_OK) { 1361 | data_to_send = 1; 1362 | #if !LWIP_HTTPD_SSI_INCLUDE_TAG 1363 | if(ssi->tag_started <= hs->file) { 1364 | /* pretend to have sent the tag, too */ 1365 | len += ssi->tag_end - ssi->tag_started; 1366 | } 1367 | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1368 | hs->file += len; 1369 | hs->left -= len; 1370 | } 1371 | } 1372 | } else { 1373 | ssi->tag_index++; 1374 | } 1375 | } else { 1376 | /* We found an unexpected character so this is not a tag. Move 1377 | * back to idle state. */ 1378 | ssi->parse_left--; 1379 | ssi->parsed++; 1380 | ssi->tag_state = TAG_NONE; 1381 | } 1382 | break; 1383 | 1384 | /* 1385 | * We have found a valid tag and are in the process of sending 1386 | * data as a result of that discovery. We send either remaining data 1387 | * from the file prior to the insert point or the insert string itself. 1388 | */ 1389 | case TAG_SENDING: 1390 | /* Do we have any remaining file data to send from the buffer prior 1391 | * to the tag? */ 1392 | if(ssi->tag_end > hs->file) { 1393 | /* How much of the data can we send? */ 1394 | #if LWIP_HTTPD_SSI_INCLUDE_TAG 1395 | if(len > ssi->tag_end - hs->file) { 1396 | len = (u16_t)(ssi->tag_end - hs->file); 1397 | } 1398 | #else /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1399 | LWIP_ASSERT("hs->started >= hs->file", ssi->tag_started >= hs->file); 1400 | if (len > ssi->tag_started - hs->file) { 1401 | /* we would include the tag in sending */ 1402 | len = (u16_t)(ssi->tag_started - hs->file); 1403 | } 1404 | #endif /* LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1405 | if (len != 0) { 1406 | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); 1407 | } else { 1408 | err = ERR_OK; 1409 | } 1410 | if (err == ERR_OK) { 1411 | data_to_send = 1; 1412 | #if !LWIP_HTTPD_SSI_INCLUDE_TAG 1413 | if(ssi->tag_started <= hs->file) { 1414 | /* pretend to have sent the tag, too */ 1415 | len += ssi->tag_end - ssi->tag_started; 1416 | } 1417 | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1418 | hs->file += len; 1419 | hs->left -= len; 1420 | } 1421 | } else { 1422 | #if LWIP_HTTPD_SSI_MULTIPART 1423 | if(ssi->tag_index >= ssi->tag_insert_len) { 1424 | /* Did the last SSIHandler have more to send? */ 1425 | if (ssi->tag_part != HTTPD_LAST_TAG_PART) { 1426 | /* If so, call it again */ 1427 | ssi->tag_index = 0; 1428 | get_tag_insert(hs); 1429 | } 1430 | } 1431 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 1432 | 1433 | /* Do we still have insert data left to send? */ 1434 | if(ssi->tag_index < ssi->tag_insert_len) { 1435 | /* We are sending the insert string itself. How much of the 1436 | * insert can we send? */ 1437 | if(len > (ssi->tag_insert_len - ssi->tag_index)) { 1438 | len = (ssi->tag_insert_len - ssi->tag_index); 1439 | } 1440 | 1441 | /* Note that we set the copy flag here since we only have a 1442 | * single tag insert buffer per connection. If we don't do 1443 | * this, insert corruption can occur if more than one insert 1444 | * is processed before we call tcp_output. */ 1445 | err = http_write(pcb, &(ssi->tag_insert[ssi->tag_index]), &len, 1446 | HTTP_IS_TAG_VOLATILE(hs)); 1447 | if (err == ERR_OK) { 1448 | data_to_send = 1; 1449 | ssi->tag_index += len; 1450 | /* Don't return here: keep on sending data */ 1451 | } 1452 | } else { 1453 | #if LWIP_HTTPD_SSI_MULTIPART 1454 | if (ssi->tag_part == HTTPD_LAST_TAG_PART) 1455 | #endif /* LWIP_HTTPD_SSI_MULTIPART */ 1456 | { 1457 | /* We have sent all the insert data so go back to looking for 1458 | * a new tag. */ 1459 | LWIP_DEBUGF(HTTPD_DEBUG, ("Everything sent.\n")); 1460 | ssi->tag_index = 0; 1461 | ssi->tag_state = TAG_NONE; 1462 | #if !LWIP_HTTPD_SSI_INCLUDE_TAG 1463 | ssi->parsed = ssi->tag_end; 1464 | #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG*/ 1465 | } 1466 | } 1467 | break; 1468 | } 1469 | } 1470 | } 1471 | 1472 | /* If we drop out of the end of the for loop, this implies we must have 1473 | * file data to send so send it now. In TAG_SENDING state, we've already 1474 | * handled this so skip the send if that's the case. */ 1475 | if((ssi->tag_state != TAG_SENDING) && (ssi->parsed > hs->file)) { 1476 | /* We cannot send more data than space available in the send 1477 | buffer. */ 1478 | if (tcp_sndbuf(pcb) < (ssi->parsed - hs->file)) { 1479 | len = tcp_sndbuf(pcb); 1480 | } else { 1481 | LWIP_ASSERT("Data size does not fit into u16_t!", 1482 | (ssi->parsed - hs->file) <= 0xffff); 1483 | len = (u16_t)(ssi->parsed - hs->file); 1484 | } 1485 | if(len > (2 * tcp_mss(pcb))) { 1486 | len = 2 * tcp_mss(pcb); 1487 | } 1488 | 1489 | err = http_write(pcb, hs->file, &len, HTTP_IS_DATA_VOLATILE(hs)); 1490 | if (err == ERR_OK) { 1491 | data_to_send = 1; 1492 | hs->file += len; 1493 | hs->left -= len; 1494 | } 1495 | } 1496 | return data_to_send; 1497 | } 1498 | #endif /* LWIP_HTTPD_SSI */ 1499 | 1500 | /** 1501 | * Try to send more data on this pcb. 1502 | * 1503 | * @param pcb the pcb to send data 1504 | * @param hs connection state 1505 | */ 1506 | static u8_t 1507 | http_send(struct tcp_pcb *pcb, struct http_state *hs) 1508 | { 1509 | u8_t data_to_send = HTTP_NO_DATA_TO_SEND; 1510 | 1511 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_send: pcb=%p hs=%p left=%d\n", (void*)pcb, 1512 | (void*)hs, hs != NULL ? (int)hs->left : 0)); 1513 | 1514 | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND 1515 | if (hs->unrecved_bytes != 0) { 1516 | return 0; 1517 | } 1518 | #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ 1519 | 1520 | /* If we were passed a NULL state structure pointer, ignore the call. */ 1521 | if (hs == NULL) { 1522 | return 0; 1523 | } 1524 | 1525 | #if LWIP_HTTPD_FS_ASYNC_READ 1526 | /* Check if we are allowed to read from this file. 1527 | (e.g. SSI might want to delay sending until data is available) */ 1528 | if (!fs_is_file_ready(hs->handle, http_continue, hs)) { 1529 | return 0; 1530 | } 1531 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 1532 | 1533 | #if LWIP_HTTPD_DYNAMIC_HEADERS 1534 | /* Do we have any more header data to send for this file? */ 1535 | if(hs->hdr_index < NUM_FILE_HDR_STRINGS) { 1536 | data_to_send = http_send_headers(pcb, hs); 1537 | if (data_to_send != HTTP_DATA_TO_SEND_CONTINUE) { 1538 | return data_to_send; 1539 | } 1540 | } 1541 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 1542 | 1543 | /* Have we run out of file data to send? If so, we need to read the next 1544 | * block from the file. */ 1545 | if (hs->left == 0) { 1546 | if (!http_check_eof(pcb, hs)) { 1547 | return 0; 1548 | } 1549 | } 1550 | 1551 | #if LWIP_HTTPD_SSI 1552 | if(hs->ssi) { 1553 | data_to_send = http_send_data_ssi(pcb, hs); 1554 | } else 1555 | #endif /* LWIP_HTTPD_SSI */ 1556 | { 1557 | data_to_send = http_send_data_nonssi(pcb, hs); 1558 | } 1559 | 1560 | if((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0)) { 1561 | /* We reached the end of the file so this request is done. 1562 | * This adds the FIN flag right into the last data segment. */ 1563 | LWIP_DEBUGF(HTTPD_DEBUG, ("End of file.\n")); 1564 | http_eof(pcb, hs); 1565 | return 0; 1566 | } 1567 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("send_data end.\n")); 1568 | return data_to_send; 1569 | } 1570 | 1571 | #if LWIP_HTTPD_SUPPORT_EXTSTATUS 1572 | /** Initialize a http connection with a file to send for an error message 1573 | * 1574 | * @param hs http connection state 1575 | * @param error_nr HTTP error number 1576 | * @return ERR_OK if file was found and hs has been initialized correctly 1577 | * another err_t otherwise 1578 | */ 1579 | static err_t 1580 | http_find_error_file(struct http_state *hs, u16_t error_nr) 1581 | { 1582 | const char *uri1, *uri2, *uri3; 1583 | err_t err; 1584 | 1585 | if (error_nr == 501) { 1586 | uri1 = "/501.html"; 1587 | uri2 = "/501.htm"; 1588 | uri3 = "/501.shtml"; 1589 | } else { 1590 | /* 400 (bad request is the default) */ 1591 | uri1 = "/400.html"; 1592 | uri2 = "/400.htm"; 1593 | uri3 = "/400.shtml"; 1594 | } 1595 | err = fs_open(&hs->file_handle, uri1); 1596 | if (err != ERR_OK) { 1597 | err = fs_open(&hs->file_handle, uri2); 1598 | if (err != ERR_OK) { 1599 | err = fs_open(&hs->file_handle, uri3); 1600 | if (err != ERR_OK) { 1601 | LWIP_DEBUGF(HTTPD_DEBUG, ("Error page for error %"U16_F" not found\n", 1602 | error_nr)); 1603 | return ERR_ARG; 1604 | } 1605 | } 1606 | } 1607 | return http_init_file(hs, &hs->file_handle, 0, NULL, 0); 1608 | } 1609 | #else /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ 1610 | #define http_find_error_file(hs, error_nr) ERR_ARG 1611 | #endif /* LWIP_HTTPD_SUPPORT_EXTSTATUS */ 1612 | 1613 | /** 1614 | * Get the file struct for a 404 error page. 1615 | * Tries some file names and returns NULL if none found. 1616 | * 1617 | * @param uri pointer that receives the actual file name URI 1618 | * @return file struct for the error page or NULL no matching file was found 1619 | */ 1620 | static struct fs_file * 1621 | http_get_404_file(struct http_state *hs, const char **uri) 1622 | { 1623 | err_t err; 1624 | 1625 | *uri = "/404.html"; 1626 | err = fs_open(&hs->file_handle, *uri); 1627 | if (err != ERR_OK) { 1628 | /* 404.html doesn't exist. Try 404.htm instead. */ 1629 | *uri = "/404.htm"; 1630 | err = fs_open(&hs->file_handle, *uri); 1631 | if (err != ERR_OK) { 1632 | /* 404.htm doesn't exist either. Try 404.shtml instead. */ 1633 | *uri = "/404.shtml"; 1634 | err = fs_open(&hs->file_handle, *uri); 1635 | if (err != ERR_OK) { 1636 | /* 404.htm doesn't exist either. Indicate to the caller that it should 1637 | * send back a default 404 page. 1638 | */ 1639 | *uri = NULL; 1640 | return NULL; 1641 | } 1642 | } 1643 | } 1644 | 1645 | return &hs->file_handle; 1646 | } 1647 | 1648 | #if LWIP_HTTPD_SUPPORT_POST 1649 | static err_t 1650 | http_handle_post_finished(struct http_state *hs, err_t err) 1651 | { 1652 | #if LWIP_HTTPD_POST_MANUAL_WND 1653 | /* Prevent multiple calls to httpd_post_finished, since it might have already 1654 | been called before from httpd_post_data_recved(). */ 1655 | if (hs->post_finished) { 1656 | return ERR_OK; 1657 | } 1658 | hs->post_finished = 1; 1659 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 1660 | /* application error or POST finished */ 1661 | /* NULL-terminate the buffer */ 1662 | if (err == ERR_OK) { 1663 | http_post_response_filename[0] = 0; 1664 | httpd_post_finished(hs, http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN); 1665 | } 1666 | return http_find_file(hs, http_post_response_filename, 0); 1667 | } 1668 | 1669 | /** Pass received POST body data to the application and correctly handle 1670 | * returning a response document or closing the connection. 1671 | * ATTENTION: The application is responsible for the pbuf now, so don't free it! 1672 | * 1673 | * @param hs http connection state 1674 | * @param p pbuf to pass to the application 1675 | * @return ERR_OK if passed successfully, another err_t if the response file 1676 | * hasn't been found (after POST finished) 1677 | */ 1678 | static err_t 1679 | http_post_rxpbuf(struct http_state *hs, struct pbuf *p) 1680 | { 1681 | err_t err; 1682 | 1683 | /* adjust remaining Content-Length */ 1684 | if (hs->post_content_len_left < p->tot_len) { 1685 | hs->post_content_len_left = 0; 1686 | } else { 1687 | hs->post_content_len_left -= p->tot_len; 1688 | } 1689 | err = httpd_post_receive_data(hs, p); 1690 | if ((err != ERR_OK) || (hs->post_content_len_left == 0)) { 1691 | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND 1692 | if (hs->unrecved_bytes != 0) { 1693 | return ERR_OK; 1694 | } 1695 | #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ 1696 | /* application error or POST finished */ 1697 | return http_handle_post_finished(hs, err); 1698 | } 1699 | 1700 | return ERR_OK; 1701 | } 1702 | 1703 | /** Handle a post request. Called from http_parse_request when method 'POST' 1704 | * is found. 1705 | * 1706 | * @param p The input pbuf (containing the POST header and body). 1707 | * @param hs The http connection state. 1708 | * @param data HTTP request (header and part of body) from input pbuf(s). 1709 | * @param data_len Size of 'data'. 1710 | * @param uri The HTTP URI parsed from input pbuf(s). 1711 | * @param uri_end Pointer to the end of 'uri' (here, the rest of the HTTP 1712 | * header starts). 1713 | * @return ERR_OK: POST correctly parsed and accepted by the application. 1714 | * ERR_INPROGRESS: POST not completely parsed (no error yet) 1715 | * another err_t: Error parsing POST or denied by the application 1716 | */ 1717 | static err_t 1718 | http_post_request(struct pbuf **inp, struct http_state *hs, 1719 | char *data, u16_t data_len, char *uri, char *uri_end) 1720 | { 1721 | 1722 | LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", uri_end)); 1723 | 1724 | err_t err; 1725 | /* search for end-of-header (first double-CRLF) */ 1726 | char* crlfcrlf = strnstr(uri_end + 1, CRLF CRLF, data_len - (uri_end + 1 - data)); 1727 | 1728 | if (crlfcrlf != NULL) { 1729 | /* search for "Content-Length: " */ 1730 | #define HTTP_HDR_CONTENT_LEN "Content-Length: " 1731 | #define HTTP_HDR_CONTENT_LEN_LEN 16 1732 | #define HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN 10 1733 | #define HTTP_HDR_CONTENT_TYPE "Content-Type: " 1734 | #define HTTP_HDR_CONTENT_TYPE_LEN 14 1735 | #define HTTP_HDR_CONTENT_TYPE_MAX_LEN 100 1736 | char *scontent_len = strnstr(uri_end + 1, HTTP_HDR_CONTENT_LEN, crlfcrlf - (uri_end + 1)); // Find Content-Length 1737 | char *scontent_type = strnstr(uri_end + 1, HTTP_HDR_CONTENT_TYPE, crlfcrlf - (uri_end + 1)); // Find Content-Type 1738 | LWIP_DEBUGF(HTTPD_DEBUG, ("POST find Content-Type: %s\n", scontent_type)); 1739 | if (scontent_len != NULL) { 1740 | char *scontent_len_end = strnstr(scontent_len + HTTP_HDR_CONTENT_LEN_LEN, CRLF, HTTP_HDR_CONTENT_LEN_DIGIT_MAX_LEN); // Find NewLine/CarrageReturn after Content-Length 1741 | if (scontent_len_end != NULL) { 1742 | int content_len; 1743 | char *conten_len_num = scontent_len + HTTP_HDR_CONTENT_LEN_LEN; 1744 | *scontent_len_end = 0; 1745 | content_len = atoi(conten_len_num); 1746 | if (content_len > 0) { 1747 | 1748 | /* Find the Content-Type */ 1749 | /* This is need to get the boundary for multipart forms*/ 1750 | const char*content_type = NULL; 1751 | if(scontent_type != NULL) { 1752 | char *scontent_type_end = strnstr(scontent_type + HTTP_HDR_CONTENT_TYPE_LEN, CRLF, HTTP_HDR_CONTENT_TYPE_MAX_LEN); // Find NewLine/CarrageReturn after Content-Type 1753 | size_t total = strlen(scontent_type); 1754 | size_t end = strlen(scontent_type_end); 1755 | content_type = strndup(scontent_type, (total - end)); 1756 | LWIP_DEBUGF(HTTPD_DEBUG, ("POST received Content-Type: %s\n", content_type)); 1757 | } 1758 | 1759 | /* adjust length of HTTP header passed to application */ 1760 | const char *hdr_start_after_uri = uri_end + 1; 1761 | u16_t hdr_len = LWIP_MIN(data_len, crlfcrlf + 4 - data); 1762 | u16_t hdr_data_len = LWIP_MIN(data_len, crlfcrlf + 4 - hdr_start_after_uri); 1763 | u8_t post_auto_wnd = 1; 1764 | http_post_response_filename[0] = 0; 1765 | err = httpd_post_begin(hs, uri, hdr_start_after_uri, hdr_data_len, content_len, 1766 | http_post_response_filename, LWIP_HTTPD_POST_MAX_RESPONSE_URI_LEN, &post_auto_wnd, content_type); 1767 | if (err == ERR_OK) { 1768 | /* try to pass in data of the first pbuf(s) */ 1769 | struct pbuf *q = *inp; 1770 | u16_t start_offset = hdr_len; 1771 | #if LWIP_HTTPD_POST_MANUAL_WND 1772 | hs->no_auto_wnd = !post_auto_wnd; 1773 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 1774 | /* set the Content-Length to be received for this POST */ 1775 | hs->post_content_len_left = (u32_t)content_len; 1776 | 1777 | /* get to the pbuf where the body starts */ 1778 | while((q != NULL) && (q->len <= start_offset)) { 1779 | struct pbuf *head = q; 1780 | start_offset -= q->len; 1781 | q = q->next; 1782 | /* free the head pbuf */ 1783 | head->next = NULL; 1784 | pbuf_free(head); 1785 | } 1786 | *inp = NULL; 1787 | if (q != NULL) { 1788 | /* hide the remaining HTTP header */ 1789 | pbuf_header(q, -(s16_t)start_offset); 1790 | #if LWIP_HTTPD_POST_MANUAL_WND 1791 | if (!post_auto_wnd) { 1792 | /* already tcp_recved() this data... */ 1793 | hs->unrecved_bytes = q->tot_len; 1794 | } 1795 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 1796 | return http_post_rxpbuf(hs, q); 1797 | } else { 1798 | return ERR_OK; 1799 | } 1800 | } else { 1801 | /* return file passed from application */ 1802 | return http_find_file(hs, http_post_response_filename, 0); 1803 | } 1804 | } else { 1805 | LWIP_DEBUGF(HTTPD_DEBUG, ("POST received invalid Content-Length: %s\n", 1806 | conten_len_num)); 1807 | return ERR_ARG; 1808 | } 1809 | } 1810 | } 1811 | } 1812 | /* if we come here, the POST is incomplete */ 1813 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 1814 | return ERR_INPROGRESS; 1815 | #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 1816 | return ERR_ARG; 1817 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 1818 | } 1819 | 1820 | #if LWIP_HTTPD_POST_MANUAL_WND 1821 | /** A POST implementation can call this function to update the TCP window. 1822 | * This can be used to throttle data reception (e.g. when received data is 1823 | * programmed to flash and data is received faster than programmed). 1824 | * 1825 | * @param connection A connection handle passed to httpd_post_begin for which 1826 | * httpd_post_finished has *NOT* been called yet! 1827 | * @param recved_len Length of data received (for window update) 1828 | */ 1829 | void httpd_post_data_recved(void *connection, u16_t recved_len) 1830 | { 1831 | struct http_state *hs = (struct http_state*)connection; 1832 | if (hs != NULL) { 1833 | if (hs->no_auto_wnd) { 1834 | u16_t len = recved_len; 1835 | if (hs->unrecved_bytes >= recved_len) { 1836 | hs->unrecved_bytes -= recved_len; 1837 | } else { 1838 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_LEVEL_WARNING, ("httpd_post_data_recved: recved_len too big\n")); 1839 | len = (u16_t)hs->unrecved_bytes; 1840 | hs->unrecved_bytes = 0; 1841 | } 1842 | if (hs->pcb != NULL) { 1843 | if (len != 0) { 1844 | tcp_recved(hs->pcb, len); 1845 | } 1846 | if ((hs->post_content_len_left == 0) && (hs->unrecved_bytes == 0)) { 1847 | /* finished handling POST */ 1848 | http_handle_post_finished(hs); 1849 | http_send(hs->pcb, hs); 1850 | } 1851 | } 1852 | } 1853 | } 1854 | } 1855 | #endif /* LWIP_HTTPD_POST_MANUAL_WND */ 1856 | 1857 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 1858 | 1859 | #if LWIP_HTTPD_FS_ASYNC_READ 1860 | /** Try to send more data if file has been blocked before 1861 | * This is a callback function passed to fs_read_async(). 1862 | */ 1863 | static void 1864 | http_continue(void *connection) 1865 | { 1866 | struct http_state *hs = (struct http_state*)connection; 1867 | if (hs && (hs->pcb) && (hs->handle)) { 1868 | LWIP_ASSERT("hs->pcb != NULL", hs->pcb != NULL); 1869 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("httpd_continue: try to send more data\n")); 1870 | if (http_send(hs->pcb, hs)) { 1871 | /* If we wrote anything to be sent, go ahead and send it now. */ 1872 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); 1873 | tcp_output(hs->pcb); 1874 | } 1875 | } 1876 | } 1877 | #endif /* LWIP_HTTPD_FS_ASYNC_READ */ 1878 | 1879 | /** 1880 | * When data has been received in the correct state, try to parse it 1881 | * as a HTTP request. 1882 | * 1883 | * @param p the received pbuf 1884 | * @param hs the connection state 1885 | * @param pcb the tcp_pcb which received this packet 1886 | * @return ERR_OK if request was OK and hs has been initialized correctly 1887 | * ERR_INPROGRESS if request was OK so far but not fully received 1888 | * another err_t otherwise 1889 | */ 1890 | static err_t 1891 | http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb) 1892 | { 1893 | char *data; 1894 | char *crlf; 1895 | u16_t data_len; 1896 | struct pbuf *p = *inp; 1897 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 1898 | u16_t clen; 1899 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 1900 | #if LWIP_HTTPD_SUPPORT_POST 1901 | err_t err; 1902 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 1903 | 1904 | LWIP_UNUSED_ARG(pcb); /* only used for post */ 1905 | LWIP_ASSERT("p != NULL", p != NULL); 1906 | LWIP_ASSERT("hs != NULL", hs != NULL); 1907 | 1908 | if ((hs->handle != NULL) || (hs->file != NULL)) { 1909 | LWIP_DEBUGF(HTTPD_DEBUG, ("Received data while sending a file\n")); 1910 | /* already sending a file */ 1911 | /* @todo: abort? */ 1912 | return ERR_USE; 1913 | } 1914 | 1915 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 1916 | 1917 | LWIP_DEBUGF(HTTPD_DEBUG, ("Received %"U16_F" bytes\n", p->tot_len)); 1918 | 1919 | /* first check allowed characters in this pbuf? */ 1920 | 1921 | /* enqueue the pbuf */ 1922 | if (hs->req == NULL) { 1923 | LWIP_DEBUGF(HTTPD_DEBUG, ("First pbuf\n")); 1924 | hs->req = p; 1925 | } else { 1926 | LWIP_DEBUGF(HTTPD_DEBUG, ("pbuf enqueued\n")); 1927 | pbuf_cat(hs->req, p); 1928 | } 1929 | 1930 | if (hs->req->next != NULL) { 1931 | data_len = LWIP_MIN(hs->req->tot_len, LWIP_HTTPD_MAX_REQ_LENGTH); 1932 | pbuf_copy_partial(hs->req, httpd_req_buf, data_len, 0); 1933 | data = httpd_req_buf; 1934 | } else 1935 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 1936 | { 1937 | data = (char *)p->payload; 1938 | data_len = p->len; 1939 | if (p->len != p->tot_len) { 1940 | LWIP_DEBUGF(HTTPD_DEBUG, ("Warning: incomplete header due to chained pbufs\n")); 1941 | } 1942 | } 1943 | 1944 | /* received enough data for minimal request? */ 1945 | if (data_len >= MIN_REQ_LEN) { 1946 | /* wait for CRLF before parsing anything */ 1947 | crlf = strnstr(data, CRLF, data_len); 1948 | if (crlf != NULL) { 1949 | #if LWIP_HTTPD_SUPPORT_POST 1950 | int is_post = 0; 1951 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 1952 | int is_09 = 0; 1953 | char *sp1, *sp2; 1954 | u16_t left_len, uri_len; 1955 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("CRLF received, parsing request\n")); 1956 | /* parse method */ 1957 | if (!strncmp(data, "GET ", 4)) { 1958 | sp1 = data + 3; 1959 | /* received GET request */ 1960 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received GET request\"\n")); 1961 | #if LWIP_HTTPD_SUPPORT_POST 1962 | } else if (!strncmp(data, "POST ", 5)) { 1963 | /* store request type */ 1964 | is_post = 1; 1965 | sp1 = data + 4; 1966 | /* received GET request */ 1967 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Received POST request\n")); 1968 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 1969 | } else { 1970 | /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ 1971 | data[4] = 0; 1972 | /* unsupported method! */ 1973 | LWIP_DEBUGF(HTTPD_DEBUG, ("Unsupported request method (not implemented): \"%s\"\n", 1974 | data)); 1975 | return http_find_error_file(hs, 501); 1976 | } 1977 | /* if we come here, method is OK, parse URI */ 1978 | left_len = data_len - ((sp1 +1) - data); 1979 | sp2 = strnstr(sp1 + 1, " ", left_len); 1980 | #if LWIP_HTTPD_SUPPORT_V09 1981 | if (sp2 == NULL) { 1982 | /* HTTP 0.9: respond with correct protocol version */ 1983 | sp2 = strnstr(sp1 + 1, CRLF, left_len); 1984 | is_09 = 1; 1985 | #if LWIP_HTTPD_SUPPORT_POST 1986 | if (is_post) { 1987 | /* HTTP/0.9 does not support POST */ 1988 | goto badrequest; 1989 | } 1990 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 1991 | } 1992 | #endif /* LWIP_HTTPD_SUPPORT_V09 */ 1993 | uri_len = sp2 - (sp1 + 1); 1994 | if ((sp2 != 0) && (sp2 > sp1)) { 1995 | /* wait for CRLFCRLF (indicating end of HTTP headers) before parsing anything */ 1996 | if (strnstr(data, CRLF CRLF, data_len) != NULL) { 1997 | char *uri = sp1 + 1; 1998 | #if LWIP_HTTPD_SUPPORT_11_KEEPALIVE 1999 | if (!is_09 && strnstr(data, HTTP11_CONNECTIONKEEPALIVE, data_len)) { 2000 | hs->keepalive = 1; 2001 | } 2002 | #endif /* LWIP_HTTPD_SUPPORT_11_KEEPALIVE */ 2003 | /* null-terminate the METHOD (pbuf is freed anyway wen returning) */ 2004 | *sp1 = 0; 2005 | uri[uri_len] = 0; 2006 | LWIP_DEBUGF(HTTPD_DEBUG, ("Received \"%s\" request for URI: \"%s\"\n", 2007 | data, uri)); 2008 | #if LWIP_HTTPD_SUPPORT_POST 2009 | if (is_post) { 2010 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 2011 | struct pbuf **q = &hs->req; 2012 | #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 2013 | struct pbuf **q = inp; 2014 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 2015 | err = http_post_request(q, hs, data, data_len, uri, sp2); 2016 | if (err != ERR_OK) { 2017 | /* restore header for next try */ 2018 | *sp1 = ' '; 2019 | *sp2 = ' '; 2020 | uri[uri_len] = ' '; 2021 | } 2022 | if (err == ERR_ARG) { 2023 | goto badrequest; 2024 | } 2025 | return err; 2026 | } else 2027 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 2028 | { 2029 | return http_find_file(hs, uri, is_09); 2030 | } 2031 | } 2032 | } else { 2033 | LWIP_DEBUGF(HTTPD_DEBUG, ("invalid URI\n")); 2034 | } 2035 | } 2036 | } 2037 | 2038 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 2039 | clen = pbuf_clen(hs->req); 2040 | if ((hs->req->tot_len <= LWIP_HTTPD_REQ_BUFSIZE) && 2041 | (clen <= LWIP_HTTPD_REQ_QUEUELEN)) { 2042 | /* request not fully received (too short or CRLF is missing) */ 2043 | return ERR_INPROGRESS; 2044 | } else 2045 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 2046 | { 2047 | #if LWIP_HTTPD_SUPPORT_POST 2048 | badrequest: 2049 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 2050 | LWIP_DEBUGF(HTTPD_DEBUG, ("bad request\n")); 2051 | /* could not parse request */ 2052 | return http_find_error_file(hs, 400); 2053 | } 2054 | } 2055 | 2056 | /** Try to find the file specified by uri and, if found, initialize hs 2057 | * accordingly. 2058 | * 2059 | * @param hs the connection state 2060 | * @param uri the HTTP header URI 2061 | * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) 2062 | * @return ERR_OK if file was found and hs has been initialized correctly 2063 | * another err_t otherwise 2064 | */ 2065 | static err_t 2066 | http_find_file(struct http_state *hs, const char *uri, int is_09) 2067 | { 2068 | size_t loop; 2069 | struct fs_file *file = NULL; 2070 | char *params; 2071 | err_t err; 2072 | #if LWIP_HTTPD_CGI 2073 | int i; 2074 | int count; 2075 | #endif /* LWIP_HTTPD_CGI */ 2076 | #if !LWIP_HTTPD_SSI 2077 | const 2078 | #endif /* !LWIP_HTTPD_SSI */ 2079 | /* By default, assume we will not be processing server-side-includes tags */ 2080 | u8_t tag_check = 0; 2081 | 2082 | /* Have we been asked for the default root file? */ 2083 | if((uri[0] == '/') && (uri[1] == 0)) { 2084 | /* Try each of the configured default filenames until we find one 2085 | that exists. */ 2086 | for (loop = 0; loop < NUM_DEFAULT_FILENAMES; loop++) { 2087 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Looking for %s...\n", g_psDefaultFilenames[loop].name)); 2088 | err = fs_open(&hs->file_handle, (char *)g_psDefaultFilenames[loop].name); 2089 | uri = (char *)g_psDefaultFilenames[loop].name; 2090 | if(err == ERR_OK) { 2091 | file = &hs->file_handle; 2092 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opened.\n")); 2093 | #if LWIP_HTTPD_SSI 2094 | tag_check = g_psDefaultFilenames[loop].shtml; 2095 | #endif /* LWIP_HTTPD_SSI */ 2096 | break; 2097 | } 2098 | } 2099 | if (file == NULL) { 2100 | /* None of the default filenames exist so send back a 404 page */ 2101 | file = http_get_404_file(hs, &uri); 2102 | #if LWIP_HTTPD_SSI 2103 | tag_check = 0; 2104 | #endif /* LWIP_HTTPD_SSI */ 2105 | } 2106 | } else { 2107 | /* No - we've been asked for a specific file. */ 2108 | /* First, isolate the base URI (without any parameters) */ 2109 | params = (char *)strchr(uri, '?'); 2110 | if (params != NULL) { 2111 | /* URI contains parameters. NULL-terminate the base URI */ 2112 | *params = '\0'; 2113 | params++; 2114 | } 2115 | 2116 | #if LWIP_HTTPD_CGI 2117 | /* Does the base URI we have isolated correspond to a CGI handler? */ 2118 | if (g_iNumCGIs && g_pCGIs) { 2119 | for (i = 0; i < g_iNumCGIs; i++) { 2120 | if (strcmp(uri, g_pCGIs[i].pcCGIName) == 0) { 2121 | /* 2122 | * We found a CGI that handles this URI so extract the 2123 | * parameters and call the handler. 2124 | */ 2125 | count = extract_uri_parameters(hs, params); 2126 | uri = g_pCGIs[i].pfnCGIHandler(i, count, hs->params, 2127 | hs->param_vals); 2128 | break; 2129 | } 2130 | } 2131 | } 2132 | #endif /* LWIP_HTTPD_CGI */ 2133 | 2134 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("Opening %s\n", uri)); 2135 | 2136 | err = fs_open(&hs->file_handle, uri); 2137 | if (err == ERR_OK) { 2138 | file = &hs->file_handle; 2139 | } else { 2140 | file = http_get_404_file(hs, &uri); 2141 | } 2142 | #if LWIP_HTTPD_SSI 2143 | if (file != NULL) { 2144 | /* See if we have been asked for an shtml file and, if so, 2145 | enable tag checking. */ 2146 | tag_check = 0; 2147 | for (loop = 0; loop < NUM_SHTML_EXTENSIONS; loop++) { 2148 | if (strstr(uri, g_pcSSIExtensions[loop])) { 2149 | tag_check = 1; 2150 | break; 2151 | } 2152 | } 2153 | } 2154 | #endif /* LWIP_HTTPD_SSI */ 2155 | } 2156 | return http_init_file(hs, file, is_09, uri, tag_check); 2157 | } 2158 | 2159 | /** Initialize a http connection with a file to send (if found). 2160 | * Called by http_find_file and http_find_error_file. 2161 | * 2162 | * @param hs http connection state 2163 | * @param file file structure to send (or NULL if not found) 2164 | * @param is_09 1 if the request is HTTP/0.9 (no HTTP headers in response) 2165 | * @param uri the HTTP header URI 2166 | * @param tag_check enable SSI tag checking 2167 | * @return ERR_OK if file was found and hs has been initialized correctly 2168 | * another err_t otherwise 2169 | */ 2170 | static err_t 2171 | http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri, u8_t tag_check) 2172 | { 2173 | if (file != NULL) { 2174 | /* file opened, initialise struct http_state */ 2175 | #if LWIP_HTTPD_SSI 2176 | if (tag_check) { 2177 | struct http_ssi_state *ssi = http_ssi_state_alloc(); 2178 | if (ssi != NULL) { 2179 | ssi->tag_index = 0; 2180 | ssi->tag_state = TAG_NONE; 2181 | ssi->parsed = file->data; 2182 | ssi->parse_left = file->len; 2183 | ssi->tag_end = file->data; 2184 | hs->ssi = ssi; 2185 | } 2186 | } 2187 | #else /* LWIP_HTTPD_SSI */ 2188 | LWIP_UNUSED_ARG(tag_check); 2189 | #endif /* LWIP_HTTPD_SSI */ 2190 | hs->handle = file; 2191 | hs->file = (char*)file->data; 2192 | LWIP_ASSERT("File length must be positive!", (file->len >= 0)); 2193 | hs->left = file->len; 2194 | hs->retries = 0; 2195 | #if LWIP_HTTPD_TIMING 2196 | hs->time_started = sys_now(); 2197 | #endif /* LWIP_HTTPD_TIMING */ 2198 | #if !LWIP_HTTPD_DYNAMIC_HEADERS 2199 | LWIP_ASSERT("HTTP headers not included in file system", hs->handle->http_header_included); 2200 | #endif /* !LWIP_HTTPD_DYNAMIC_HEADERS */ 2201 | #if LWIP_HTTPD_SUPPORT_V09 2202 | if (hs->handle->http_header_included && is_09) { 2203 | /* HTTP/0.9 responses are sent without HTTP header, 2204 | search for the end of the header. */ 2205 | char *file_start = strnstr(hs->file, CRLF CRLF, hs->left); 2206 | if (file_start != NULL) { 2207 | size_t diff = file_start + 4 - hs->file; 2208 | hs->file += diff; 2209 | hs->left -= (u32_t)diff; 2210 | } 2211 | } 2212 | #endif /* LWIP_HTTPD_SUPPORT_V09*/ 2213 | } else { 2214 | hs->handle = NULL; 2215 | hs->file = NULL; 2216 | hs->left = 0; 2217 | hs->retries = 0; 2218 | } 2219 | #if LWIP_HTTPD_DYNAMIC_HEADERS 2220 | /* Determine the HTTP headers to send based on the file extension of 2221 | * the requested URI. */ 2222 | if ((hs->handle == NULL) || !hs->handle->http_header_included) { 2223 | get_http_headers(hs, (char*)uri); 2224 | } 2225 | #else /* LWIP_HTTPD_DYNAMIC_HEADERS */ 2226 | LWIP_UNUSED_ARG(uri); 2227 | #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */ 2228 | return ERR_OK; 2229 | } 2230 | 2231 | /** 2232 | * The pcb had an error and is already deallocated. 2233 | * The argument might still be valid (if != NULL). 2234 | */ 2235 | static void 2236 | http_err(void *arg, err_t err) 2237 | { 2238 | struct http_state *hs = (struct http_state *)arg; 2239 | LWIP_UNUSED_ARG(err); 2240 | 2241 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_err: %s", lwip_strerr(err))); 2242 | 2243 | if (hs != NULL) { 2244 | http_state_free(hs); 2245 | } 2246 | } 2247 | 2248 | /** 2249 | * Data has been sent and acknowledged by the remote host. 2250 | * This means that more data can be sent. 2251 | */ 2252 | static err_t 2253 | http_sent(void *arg, struct tcp_pcb *pcb, u16_t len) 2254 | { 2255 | struct http_state *hs = (struct http_state *)arg; 2256 | 2257 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_sent %p\n", (void*)pcb)); 2258 | 2259 | LWIP_UNUSED_ARG(len); 2260 | 2261 | if (hs == NULL) { 2262 | return ERR_OK; 2263 | } 2264 | 2265 | hs->retries = 0; 2266 | 2267 | http_send(pcb, hs); 2268 | 2269 | return ERR_OK; 2270 | } 2271 | 2272 | /** 2273 | * The poll function is called every 2nd second. 2274 | * If there has been no data sent (which resets the retries) in 8 seconds, close. 2275 | * If the last portion of a file has not been sent in 2 seconds, close. 2276 | * 2277 | * This could be increased, but we don't want to waste resources for bad connections. 2278 | */ 2279 | static err_t 2280 | http_poll(void *arg, struct tcp_pcb *pcb) 2281 | { 2282 | struct http_state *hs = (struct http_state *)arg; 2283 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: pcb=%p hs=%p pcb_state=%s\n", 2284 | (void*)pcb, (void*)hs, tcp_debug_state_str(pcb->state))); 2285 | 2286 | if (hs == NULL) { 2287 | err_t closed; 2288 | /* arg is null, close. */ 2289 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: arg is NULL, close\n")); 2290 | closed = http_close_conn(pcb, NULL); 2291 | LWIP_UNUSED_ARG(closed); 2292 | #if LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR 2293 | if (closed == ERR_MEM) { 2294 | tcp_abort(pcb); 2295 | return ERR_ABRT; 2296 | } 2297 | #endif /* LWIP_HTTPD_ABORT_ON_CLOSE_MEM_ERROR */ 2298 | return ERR_OK; 2299 | } else { 2300 | hs->retries++; 2301 | if (hs->retries == HTTPD_MAX_RETRIES) { 2302 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_poll: too many retries, close\n")); 2303 | http_close_conn(pcb, hs); 2304 | return ERR_OK; 2305 | } 2306 | 2307 | /* If this connection has a file open, try to send some more data. If 2308 | * it has not yet received a GET request, don't do this since it will 2309 | * cause the connection to close immediately. */ 2310 | if(hs && (hs->handle)) { 2311 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_poll: try to send more data\n")); 2312 | if(http_send(pcb, hs)) { 2313 | /* If we wrote anything to be sent, go ahead and send it now. */ 2314 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("tcp_output\n")); 2315 | tcp_output(pcb); 2316 | } 2317 | } 2318 | } 2319 | 2320 | return ERR_OK; 2321 | } 2322 | 2323 | /** 2324 | * Data has been received on this pcb. 2325 | * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). 2326 | */ 2327 | static err_t 2328 | http_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) 2329 | { 2330 | err_t parsed = ERR_ABRT; 2331 | struct http_state *hs = (struct http_state *)arg; 2332 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: pcb=%p pbuf=%p err=%s\n", (void*)pcb, 2333 | (void*)p, lwip_strerr(err))); 2334 | 2335 | if ((err != ERR_OK) || (p == NULL) || (hs == NULL)) { 2336 | /* error or closed by other side? */ 2337 | if (p != NULL) { 2338 | /* Inform TCP that we have taken the data. */ 2339 | tcp_recved(pcb, p->tot_len); 2340 | pbuf_free(p); 2341 | } 2342 | if (hs == NULL) { 2343 | /* this should not happen, only to be robust */ 2344 | LWIP_DEBUGF(HTTPD_DEBUG, ("Error, http_recv: hs is NULL, close\n")); 2345 | } 2346 | http_close_conn(pcb, hs); 2347 | return ERR_OK; 2348 | } 2349 | 2350 | #if LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND 2351 | if (hs->no_auto_wnd) { 2352 | hs->unrecved_bytes += p->tot_len; 2353 | } else 2354 | #endif /* LWIP_HTTPD_SUPPORT_POST && LWIP_HTTPD_POST_MANUAL_WND */ 2355 | { 2356 | /* Inform TCP that we have taken the data. */ 2357 | tcp_recved(pcb, p->tot_len); 2358 | } 2359 | 2360 | #if LWIP_HTTPD_SUPPORT_POST 2361 | if (hs->post_content_len_left > 0) { 2362 | /* reset idle counter when POST data is received */ 2363 | hs->retries = 0; 2364 | /* this is data for a POST, pass the complete pbuf to the application */ 2365 | http_post_rxpbuf(hs, p); 2366 | /* pbuf is passed to the application, don't free it! */ 2367 | if (hs->post_content_len_left == 0) { 2368 | /* all data received, send response or close connection */ 2369 | http_send(pcb, hs); 2370 | } 2371 | return ERR_OK; 2372 | } else 2373 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 2374 | { 2375 | if (hs->handle == NULL) { 2376 | parsed = http_parse_request(&p, hs, pcb); 2377 | LWIP_ASSERT("http_parse_request: unexpected return value", parsed == ERR_OK 2378 | || parsed == ERR_INPROGRESS ||parsed == ERR_ARG || parsed == ERR_USE); 2379 | } else { 2380 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_recv: already sending data\n")); 2381 | } 2382 | #if LWIP_HTTPD_SUPPORT_REQUESTLIST 2383 | if (parsed != ERR_INPROGRESS) { 2384 | /* request fully parsed or error */ 2385 | if (hs->req != NULL) { 2386 | pbuf_free(hs->req); 2387 | hs->req = NULL; 2388 | } 2389 | } 2390 | #else /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 2391 | if (p != NULL) { 2392 | /* pbuf not passed to application, free it now */ 2393 | pbuf_free(p); 2394 | } 2395 | #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */ 2396 | if (parsed == ERR_OK) { 2397 | #if LWIP_HTTPD_SUPPORT_POST 2398 | if (hs->post_content_len_left == 0) 2399 | #endif /* LWIP_HTTPD_SUPPORT_POST */ 2400 | { 2401 | LWIP_DEBUGF(HTTPD_DEBUG | LWIP_DBG_TRACE, ("http_recv: data %p len %"S32_F"\n", hs->file, hs->left)); 2402 | http_send(pcb, hs); 2403 | } 2404 | } else if (parsed == ERR_ARG) { 2405 | /* @todo: close on ERR_USE? */ 2406 | http_close_conn(pcb, hs); 2407 | } 2408 | } 2409 | return ERR_OK; 2410 | } 2411 | 2412 | /** 2413 | * A new incoming connection has been accepted. 2414 | */ 2415 | static err_t 2416 | http_accept(void *arg, struct tcp_pcb *pcb, err_t err) 2417 | { 2418 | struct http_state *hs; 2419 | struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*)arg; 2420 | (void)lpcb; 2421 | LWIP_UNUSED_ARG(err); 2422 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept %p / %p\n", (void*)pcb, arg)); 2423 | 2424 | /* Decrease the listen backlog counter */ 2425 | tcp_accepted(lpcb); 2426 | /* Set priority */ 2427 | tcp_setprio(pcb, HTTPD_TCP_PRIO); 2428 | 2429 | /* Allocate memory for the structure that holds the state of the 2430 | connection - initialized by that function. */ 2431 | hs = http_state_alloc(); 2432 | if (hs == NULL) { 2433 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_accept: Out of memory, RST\n")); 2434 | return ERR_MEM; 2435 | } 2436 | hs->pcb = pcb; 2437 | 2438 | /* Tell TCP that this is the structure we wish to be passed for our 2439 | callbacks. */ 2440 | tcp_arg(pcb, hs); 2441 | 2442 | /* Set up the various callback functions */ 2443 | tcp_recv(pcb, http_recv); 2444 | tcp_err(pcb, http_err); 2445 | tcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL); 2446 | tcp_sent(pcb, http_sent); 2447 | 2448 | return ERR_OK; 2449 | } 2450 | 2451 | /** 2452 | * Initialize the httpd with the specified local address. 2453 | */ 2454 | static void 2455 | httpd_init_addr(ip_addr_t *local_addr) 2456 | { 2457 | struct tcp_pcb *pcb; 2458 | err_t err; 2459 | (void)err; 2460 | 2461 | pcb = tcp_new(); 2462 | LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL); 2463 | tcp_setprio(pcb, HTTPD_TCP_PRIO); 2464 | /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */ 2465 | err = tcp_bind(pcb, local_addr, HTTPD_SERVER_PORT); 2466 | LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK); 2467 | pcb = tcp_listen(pcb); 2468 | LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL); 2469 | /* initialize callback arg and accept callback */ 2470 | tcp_arg(pcb, pcb); 2471 | tcp_accept(pcb, http_accept); 2472 | } 2473 | 2474 | /** 2475 | * Initialize the httpd: set up a listening PCB and bind it to the defined port 2476 | */ 2477 | void 2478 | httpd_init(void) 2479 | { 2480 | #if HTTPD_USE_MEM_POOL 2481 | LWIP_ASSERT("memp_sizes[MEMP_HTTPD_STATE] >= sizeof(http_state)", 2482 | memp_sizes[MEMP_HTTPD_STATE] >= sizeof(http_state)); 2483 | LWIP_ASSERT("memp_sizes[MEMP_HTTPD_SSI_STATE] >= sizeof(http_ssi_state)", 2484 | memp_sizes[MEMP_HTTPD_SSI_STATE] >= sizeof(http_ssi_state)); 2485 | #endif 2486 | LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\n")); 2487 | 2488 | httpd_init_addr(IP_ADDR_ANY); 2489 | 2490 | } 2491 | 2492 | #if LWIP_HTTPD_SSI 2493 | /** 2494 | * Set the SSI handler function. 2495 | * 2496 | * @param ssi_handler the SSI handler function 2497 | * @param tags an array of SSI tag strings to search for in SSI-enabled files 2498 | * @param num_tags number of tags in the 'tags' array 2499 | */ 2500 | void 2501 | http_set_ssi_handler(tSSIHandler ssi_handler, const char **tags, int num_tags) 2502 | { 2503 | LWIP_DEBUGF(HTTPD_DEBUG, ("http_set_ssi_handler\n")); 2504 | 2505 | LWIP_ASSERT("no ssi_handler given", ssi_handler != NULL); 2506 | LWIP_ASSERT("no tags given", tags != NULL); 2507 | LWIP_ASSERT("invalid number of tags", num_tags > 0); 2508 | 2509 | g_pfnSSIHandler = ssi_handler; 2510 | g_ppcTags = tags; 2511 | g_iNumTags = num_tags; 2512 | } 2513 | #endif /* LWIP_HTTPD_SSI */ 2514 | 2515 | #if LWIP_HTTPD_CGI 2516 | /** 2517 | * Set an array of CGI filenames/handler functions 2518 | * 2519 | * @param cgis an array of CGI filenames/handler functions 2520 | * @param num_handlers number of elements in the 'cgis' array 2521 | */ 2522 | void 2523 | http_set_cgi_handlers(const tCGI *cgis, int num_handlers) 2524 | { 2525 | LWIP_ASSERT("no cgis given", cgis != NULL); 2526 | LWIP_ASSERT("invalid number of handlers", num_handlers > 0); 2527 | 2528 | g_pCGIs = cgis; 2529 | g_iNumCGIs = num_handlers; 2530 | } 2531 | #endif /* LWIP_HTTPD_CGI */ 2532 | 2533 | #endif /* LWIP_TCP */ --------------------------------------------------------------------------------