1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-19 23:22:16 +03:00

add proper POST support and more methods

GET params are always added
plain POST is added to the GET arguments
Uploads are handled by separate handler
This commit is contained in:
ficeto 2015-05-08 02:44:59 +03:00
parent e6bb6b3a0d
commit a924ba1336
3 changed files with 350 additions and 79 deletions

View File

@ -43,6 +43,26 @@ MDNSResponder mdns;
ESP8266WebServer server(80);
static bool hasSD = false;
File uploadFile;
void handleFileUpload(){
if(server.uri() != "/upload") return;
HTTPUpload upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
Serial.print("Upload: START, filename:");
Serial.println(upload.filename);
if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str());
uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
} else if(upload.status == UPLOAD_FILE_WRITE){
Serial.print("Upload: WRITE, Bytes:");
Serial.println(upload.buflen);
if(uploadFile) uploadFile.write(upload.buf, upload.buflen);
} else if(upload.status == UPLOAD_FILE_END){
Serial.print("Upload: END, Size:");
Serial.println(upload.size);
if(uploadFile) uploadFile.close();
}
}
bool loadFromSdCard(String path){
String dataType = "text/plain";
@ -152,6 +172,19 @@ void setup(void){
//Attach handler
server.onNotFound(tryLoadFromSdCard);
//Attach Upload handler
server.onFileUpload(handleFileUpload);
//Attach handler for the Upload location
server.on("/upload", HTTP_POST, [](){
WiFiClient client = server.client();
String message = "HTTP/1.1 200 OK\r\n";
message += "Content-Type: text/plain\r\n";
message += "Access-Control-Allow-Origin: *\r\n";
message += "\r\n";
client.print(message);
});
//start server
server.begin();
Serial.println("HTTP server started");

View File

@ -17,6 +17,7 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
@ -26,6 +27,7 @@
#include "ESP8266WebServer.h"
//#define DEBUG
#define DEBUG_OUTPUT Serial1
struct ESP8266WebServer::RequestHandler {
RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
@ -95,7 +97,7 @@ void ESP8266WebServer::handleClient()
}
#ifdef DEBUG
Serial.println("New client");
DEBUG_OUTPUT.println("New client");
#endif
// Wait for data from client to become available
while(client.connected() && !client.available()){
@ -106,86 +108,101 @@ void ESP8266WebServer::handleClient()
String req = client.readStringUntil('\r');
client.readStringUntil('\n');
HTTPMethod method = HTTP_GET;
if (req.startsWith("POST")) {
method = HTTP_POST;
}
// First line of HTTP request looks like "GET /path HTTP/1.1"
// Retrieve the "/path" part by finding the spaces
int addr_start = req.indexOf(' ');
int addr_end = req.indexOf(' ', addr_start + 1);
if (addr_start == -1 || addr_end == -1) {
#ifdef DEBUG
Serial.print("Invalid request: ");
Serial.println(req);
DEBUG_OUTPUT.print("Invalid request: ");
DEBUG_OUTPUT.println(req);
#endif
return;
}
req = req.substring(addr_start + 1, addr_end);
String methodStr = req.substring(0, addr_start);
String url = req.substring(addr_start + 1, addr_end);
String searchStr = "";
int hasSearch = url.indexOf('?');
if(hasSearch != -1){
searchStr = url.substring(hasSearch + 1);
url = url.substring(0, hasSearch);
}
_currentUri = url;
HTTPMethod method = HTTP_GET;
if (methodStr == "POST") {
method = HTTP_POST;
} else if (methodStr == "DELETE") {
method = HTTP_DELETE;
} else if (methodStr == "PUT") {
method = HTTP_PUT;
} else if (methodStr == "PATCH") {
method = HTTP_PATCH;
}
#ifdef DEBUG
DEBUG_OUTPUT.print("method: ");
DEBUG_OUTPUT.print(methodStr);
DEBUG_OUTPUT.print(" url: ");
DEBUG_OUTPUT.print(url);
DEBUG_OUTPUT.print(" search: ");
DEBUG_OUTPUT.println(searchStr);
#endif
String formData;
if (method == HTTP_POST) {
int contentLength = -1;
int headerCount = 0;
while(headerCount < 1024) { // there shouldn't be that much really
String line = client.readStringUntil('\r');
//bellow is needed only when POST type request
if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH){
String boundaryStr;
String headerName;
String headerValue;
bool isForm = false;
uint32_t contentLength = 0;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n');
if (line.length() > 0) { // this is a header
++headerCount;
if (contentLength < 0 && line.startsWith("Content-Length")) {
// get content length from the header
int valuePos = line.indexOf(' ', 14);
if (valuePos > 0) {
String valueStr = line.substring(valuePos+1);
contentLength = valueStr.toInt();
#ifdef DEBUG
Serial.print("Content-Length: ");
Serial.println(contentLength);
#endif
}
}
}
else {
if(req == "") break;//no moar headers
int headerDiv = req.indexOf(':');
if(headerDiv == -1){
break;
}
headerName = req.substring(0, headerDiv);
headerValue = req.substring(headerDiv + 2);
if(headerName == "Content-Type"){
if(headerValue.startsWith("text/plain")){
isForm = false;
} else if(headerValue.startsWith("multipart/form-data")){
boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
isForm = true;
}
#ifdef DEBUG
Serial.print("headerCount=");
Serial.println(headerCount);
#endif
if (contentLength >= 0) {
formData = "";
int n = 0; // timeout counter
while (formData.length() < contentLength && ++n < 3)
formData += client.readString();
}
else {
formData = client.readStringUntil('\r'); // will return after timing out once
}
}
else if (method == HTTP_GET) {
int args_start = req.indexOf('?');
if (args_start != -1) {
formData = req.substring(args_start + 1);
req = req.substring(0, args_start);
} else if(headerName == "Content-Length"){
contentLength = headerValue.toInt();
}
}
if(!isForm){
if(searchStr != "") searchStr += '&';
searchStr += client.readStringUntil('\r');
client.readStringUntil('\n');
}
_parseArguments(searchStr);
if(isForm){
_parseForm(client, boundaryStr, contentLength);
}
} else {
_parseArguments(searchStr);
}
client.flush();
#ifdef DEBUG
Serial.print("Request: ");
Serial.println(req);
Serial.print("Args: ");
Serial.println(formData);
DEBUG_OUTPUT.print("Request: ");
DEBUG_OUTPUT.println(url);
DEBUG_OUTPUT.print(" Arguments: ");
DEBUG_OUTPUT.println(searchStr);
#endif
_parseArguments(formData);
_handleRequest(client, req, method);
_handleRequest(client, url, method);
}
void ESP8266WebServer::send(int code, const char* content_type, String content) {
@ -237,6 +254,10 @@ bool ESP8266WebServer::hasArg(const char* name) {
}
void ESP8266WebServer::_parseArguments(String data) {
#ifdef DEBUG
DEBUG_OUTPUT.print("args: ");
DEBUG_OUTPUT.println(data);
#endif
if (_currentArgs)
delete[] _currentArgs;
_currentArgs = 0;
@ -254,8 +275,8 @@ void ESP8266WebServer::_parseArguments(String data) {
++_currentArgCount;
}
#ifdef DEBUG
Serial.print("args count: ");
Serial.println(_currentArgCount);
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
_currentArgs = new RequestArgument[_currentArgCount];
@ -265,17 +286,17 @@ void ESP8266WebServer::_parseArguments(String data) {
int equal_sign_index = data.indexOf('=', pos);
int next_arg_index = data.indexOf('&', pos);
#ifdef DEBUG
Serial.print("pos ");
Serial.print(pos);
Serial.print("=@ ");
Serial.print(equal_sign_index);
Serial.print(" &@ ");
Serial.println(next_arg_index);
DEBUG_OUTPUT.print("pos ");
DEBUG_OUTPUT.print(pos);
DEBUG_OUTPUT.print("=@ ");
DEBUG_OUTPUT.print(equal_sign_index);
DEBUG_OUTPUT.print(" &@ ");
DEBUG_OUTPUT.println(next_arg_index);
#endif
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
#ifdef DEBUG
Serial.print("arg missing value: ");
Serial.println(iarg);
DEBUG_OUTPUT.print("arg missing value: ");
DEBUG_OUTPUT.println(iarg);
#endif
if (next_arg_index == -1)
break;
@ -286,12 +307,12 @@ void ESP8266WebServer::_parseArguments(String data) {
arg.key = data.substring(pos, equal_sign_index);
arg.value = data.substring(equal_sign_index + 1, next_arg_index);
#ifdef DEBUG
Serial.print("arg ");
Serial.print(iarg);
Serial.print(" key: ");
Serial.print(arg.key);
Serial.print(" value: ");
Serial.println(arg.value);
DEBUG_OUTPUT.print("arg ");
DEBUG_OUTPUT.print(iarg);
DEBUG_OUTPUT.print(" key: ");
DEBUG_OUTPUT.print(arg.key);
DEBUG_OUTPUT.print(" value: ");
DEBUG_OUTPUT.println(arg.value);
#endif
++iarg;
if (next_arg_index == -1)
@ -300,12 +321,213 @@ void ESP8266WebServer::_parseArguments(String data) {
}
_currentArgCount = iarg;
#ifdef DEBUG
Serial.print("args count: ");
Serial.println(_currentArgCount);
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
}
void ESP8266WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
#ifdef DEBUG
DEBUG_OUTPUT.print("Parse Form: Boundary: ");
DEBUG_OUTPUT.print(boundary);
DEBUG_OUTPUT.print("Length: ");
DEBUG_OUTPUT.println(len);
#endif
String line;
line = client.readStringUntil('\r');
client.readStringUntil('\n');
//start reading the form
if(line == ("--"+boundary)){
RequestArgument* postArgs = new RequestArgument[32];
int postArgsLen = 0;
while(1){
String argName;
String argValue;
String argType;
String argFilename;
bool argIsFile = false;
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if(line.startsWith("Content-Disposition")){
int nameStart = line.indexOf('=');
if(nameStart != -1){
argName = line.substring(nameStart+2);
nameStart = argName.indexOf('=');
if(nameStart == -1){
argName = argName.substring(0, argName.length() - 1);
} else {
argFilename = argName.substring(nameStart+2, argName.length() - 1);
argName = argName.substring(0, argName.indexOf('"'));
argIsFile = true;
#ifdef DEBUG
DEBUG_OUTPUT.print("PostArg FileName: ");
DEBUG_OUTPUT.println(argFilename);
#endif
//use GET to set the filename if uploading using blob
if(argFilename == "blob" && hasArg("filename")) argFilename = arg("filename");
}
#ifdef DEBUG
DEBUG_OUTPUT.print("PostArg Name: ");
DEBUG_OUTPUT.println(argName);
#endif
argType = "text/plain";
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if(line.startsWith("Content-Type")){
argType = line.substring(line.indexOf(':')+2);
//skip next line
client.readStringUntil('\r');
client.readStringUntil('\n');
}
#ifdef DEBUG
DEBUG_OUTPUT.print("PostArg Type: ");
DEBUG_OUTPUT.println(argType);
#endif
if(!argIsFile){
while(1){
line = client.readStringUntil('\r');
client.readStringUntil('\n');
if(line.startsWith("--"+boundary)) break;
argValue += line+"\n";
}
#ifdef DEBUG
DEBUG_OUTPUT.print("PostArg Value: ");
DEBUG_OUTPUT.println(argValue);
DEBUG_OUTPUT.println();
#endif
RequestArgument& arg = postArgs[postArgsLen++];
arg.key = argName;
arg.value = argValue;
if(line == ("--"+boundary+"--")){
#ifdef DEBUG
DEBUG_OUTPUT.println("Done Parsing POST");
#endif
break;
}
} else {
_currentUpload.status = UPLOAD_FILE_START;
_currentUpload.name = argName;
_currentUpload.filename = argFilename;
_currentUpload.type = argType;
_currentUpload.size = 0;
_currentUpload.buflen = 0;
#ifdef DEBUG
DEBUG_OUTPUT.print("Start File: ");
DEBUG_OUTPUT.print(_currentUpload.filename);
DEBUG_OUTPUT.print(" Type: ");
DEBUG_OUTPUT.println(_currentUpload.type);
#endif
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.status = UPLOAD_FILE_WRITE;
uint8_t argByte = client.read();
readfile:
while(argByte != 0x0D){
_currentUpload.buf[_currentUpload.buflen++] = argByte;
if(_currentUpload.buflen == 1460){
#ifdef DEBUG
DEBUG_OUTPUT.println("Write File: 1460");
#endif
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.size += _currentUpload.buflen;
_currentUpload.buflen = 0;
}
argByte = client.read();
}
argByte = client.read();
if(argByte == 0x0A){
line = client.readStringUntil(0x0D);
client.readStringUntil(0x0A);
#ifdef DEBUG
DEBUG_OUTPUT.print("Write File: ");
DEBUG_OUTPUT.println(_currentUpload.buflen);
#endif
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.size += _currentUpload.buflen;
_currentUpload.buflen = 0;
if(line.startsWith("--"+boundary)){
_currentUpload.status = UPLOAD_FILE_END;
#ifdef DEBUG
DEBUG_OUTPUT.print("End File: ");
DEBUG_OUTPUT.print(_currentUpload.filename);
DEBUG_OUTPUT.print(" Type: ");
DEBUG_OUTPUT.print(_currentUpload.type);
DEBUG_OUTPUT.print(" Size: ");
DEBUG_OUTPUT.println(_currentUpload.size);
#endif
if(_fileUploadHandler) _fileUploadHandler();
if(line == ("--"+boundary+"--")){
#ifdef DEBUG
DEBUG_OUTPUT.println("Done Parsing POST");
#endif
break;
}
continue;
} else {
_currentUpload.buf[_currentUpload.buflen++] = 0x0D;
_currentUpload.buf[_currentUpload.buflen++] = 0x0A;
const char * lineChars = line.c_str();
uint32_t i = 0;
while(i < os_strlen(lineChars)){
_currentUpload.buf[_currentUpload.buflen++] = lineChars[i++];
if(_currentUpload.buflen == 1460){
#ifdef DEBUG
DEBUG_OUTPUT.println("Write File: 1460");
#endif
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.size += _currentUpload.buflen;
_currentUpload.buflen = 0;
}
}
argByte = client.read();
goto readfile;
}
} else {
_currentUpload.buf[_currentUpload.buflen++] = 0x0D;
if(_currentUpload.buflen == 1460){
#ifdef DEBUG
DEBUG_OUTPUT.println("Write File: 1460");
#endif
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.size += _currentUpload.buflen;
_currentUpload.buflen = 0;
}
goto readfile;
}
break;
}
}
}
}
int iarg;
int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
for (iarg = 0; iarg < totalArgs; iarg++){
RequestArgument& arg = postArgs[postArgsLen++];
arg.key = _currentArgs[iarg].key;
arg.value = _currentArgs[iarg].value;
}
if (_currentArgs) delete[] _currentArgs;
_currentArgs = new RequestArgument[postArgsLen];
for (iarg = 0; iarg < postArgsLen; iarg++){
RequestArgument& arg = _currentArgs[iarg];
arg.key = postArgs[iarg].key;
arg.value = postArgs[iarg].value;
}
_currentArgCount = iarg;
}
}
void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
_fileUploadHandler = fn;
}
void ESP8266WebServer::onNotFound(THandlerFunction fn) {
_notFoundHandler = fn;
}
@ -330,7 +552,7 @@ void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod
if (!handler){
#ifdef DEBUG
Serial.println("request handler not found");
DEBUG_OUTPUT.println("request handler not found");
#endif
if(_notFoundHandler) {

View File

@ -17,6 +17,7 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
*/
@ -25,8 +26,18 @@
#include <functional>
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST };
enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE };
enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END };
typedef struct {
HTTPUploadStatus status;
String filename;
String name;
String type;
size_t size;
size_t buflen;
uint8_t buf[1460];
} HTTPUpload;
class ESP8266WebServer
{
@ -42,10 +53,12 @@ public:
void on(const char* uri, THandlerFunction handler);
void on(const char* uri, HTTPMethod method, THandlerFunction fn);
void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads
String uri() { return _currentUri; }
HTTPMethod method() { return _currentMethod; }
WiFiClient client() { return _currentClient; }
HTTPUpload upload() { return _currentUpload; }
String arg(const char* name); // get request argument value by name
String arg(int i); // get request argument value by number
@ -64,6 +77,7 @@ protected:
void _parseArguments(String data);
static const char* _responseCodeToString(int code);
static void _appendHeader(String& response, const char* name, const char* value);
void _parseForm(WiFiClient& client, String boundary, uint32_t len);
struct RequestHandler;
struct RequestArgument {
@ -79,10 +93,12 @@ protected:
size_t _currentArgCount;
RequestArgument* _currentArgs;
HTTPUpload _currentUpload;
RequestHandler* _firstHandler;
RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler;
THandlerFunction _fileUploadHandler;
};