1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-07 16:23:38 +03:00

Merge pull request #203 from ficeto/esp8266

add proper POST support and more methods
add proper POST support and more methods
This commit is contained in:
Ivan Grokhotkov 2015-05-08 08:28:25 +03:00
commit e1b8017762
3 changed files with 350 additions and 79 deletions

View File

@ -43,6 +43,26 @@ MDNSResponder mdns;
ESP8266WebServer server(80); ESP8266WebServer server(80);
static bool hasSD = false; 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){ bool loadFromSdCard(String path){
String dataType = "text/plain"; String dataType = "text/plain";
@ -152,6 +172,19 @@ void setup(void){
//Attach handler //Attach handler
server.onNotFound(tryLoadFromSdCard); 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 //start server
server.begin(); server.begin();
Serial.println("HTTP server started"); Serial.println("HTTP server started");

View File

@ -17,6 +17,7 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 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,7 +26,8 @@
#include "WiFiClient.h" #include "WiFiClient.h"
#include "ESP8266WebServer.h" #include "ESP8266WebServer.h"
// #define DEBUG //#define DEBUG
#define DEBUG_OUTPUT Serial1
struct ESP8266WebServer::RequestHandler { struct ESP8266WebServer::RequestHandler {
RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
@ -95,7 +97,7 @@ void ESP8266WebServer::handleClient()
} }
#ifdef DEBUG #ifdef DEBUG
Serial.println("New client"); DEBUG_OUTPUT.println("New client");
#endif #endif
// Wait for data from client to become available // Wait for data from client to become available
while(client.connected() && !client.available()){ while(client.connected() && !client.available()){
@ -106,86 +108,101 @@ void ESP8266WebServer::handleClient()
String req = client.readStringUntil('\r'); String req = client.readStringUntil('\r');
client.readStringUntil('\n'); 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" // First line of HTTP request looks like "GET /path HTTP/1.1"
// Retrieve the "/path" part by finding the spaces // Retrieve the "/path" part by finding the spaces
int addr_start = req.indexOf(' '); int addr_start = req.indexOf(' ');
int addr_end = req.indexOf(' ', addr_start + 1); int addr_end = req.indexOf(' ', addr_start + 1);
if (addr_start == -1 || addr_end == -1) { if (addr_start == -1 || addr_end == -1) {
#ifdef DEBUG #ifdef DEBUG
Serial.print("Invalid request: "); DEBUG_OUTPUT.print("Invalid request: ");
Serial.println(req); DEBUG_OUTPUT.println(req);
#endif #endif
return; 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; String formData;
if (method == HTTP_POST) { //bellow is needed only when POST type request
int contentLength = -1; if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH){
int headerCount = 0; String boundaryStr;
while(headerCount < 1024) { // there shouldn't be that much really String headerName;
String line = client.readStringUntil('\r'); String headerValue;
bool isForm = false;
uint32_t contentLength = 0;
//parse headers
while(1){
req = client.readStringUntil('\r');
client.readStringUntil('\n'); client.readStringUntil('\n');
if(req == "") break;//no moar headers
if (line.length() > 0) { // this is a header int headerDiv = req.indexOf(':');
++headerCount; if(headerDiv == -1){
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 {
break; 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;
}
} else if(headerName == "Content-Length"){
contentLength = headerValue.toInt();
}
} }
#ifdef DEBUG
Serial.print("headerCount="); if(!isForm){
Serial.println(headerCount); if(searchStr != "") searchStr += '&';
#endif searchStr += client.readStringUntil('\r');
if (contentLength >= 0) { client.readStringUntil('\n');
formData = "";
int n = 0; // timeout counter
while (formData.length() < contentLength && ++n < 3)
formData += client.readString();
} }
else { _parseArguments(searchStr);
formData = client.readStringUntil('\r'); // will return after timing out once if(isForm){
_parseForm(client, boundaryStr, contentLength);
} }
} else {
_parseArguments(searchStr);
} }
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);
}
}
client.flush(); client.flush();
#ifdef DEBUG #ifdef DEBUG
Serial.print("Request: "); DEBUG_OUTPUT.print("Request: ");
Serial.println(req); DEBUG_OUTPUT.println(url);
Serial.print("Args: "); DEBUG_OUTPUT.print(" Arguments: ");
Serial.println(formData); DEBUG_OUTPUT.println(searchStr);
#endif #endif
_parseArguments(formData); _handleRequest(client, url, method);
_handleRequest(client, req, method);
} }
void ESP8266WebServer::send(int code, const char* content_type, String content) { 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) { void ESP8266WebServer::_parseArguments(String data) {
#ifdef DEBUG
DEBUG_OUTPUT.print("args: ");
DEBUG_OUTPUT.println(data);
#endif
if (_currentArgs) if (_currentArgs)
delete[] _currentArgs; delete[] _currentArgs;
_currentArgs = 0; _currentArgs = 0;
@ -254,8 +275,8 @@ void ESP8266WebServer::_parseArguments(String data) {
++_currentArgCount; ++_currentArgCount;
} }
#ifdef DEBUG #ifdef DEBUG
Serial.print("args count: "); DEBUG_OUTPUT.print("args count: ");
Serial.println(_currentArgCount); DEBUG_OUTPUT.println(_currentArgCount);
#endif #endif
_currentArgs = new RequestArgument[_currentArgCount]; _currentArgs = new RequestArgument[_currentArgCount];
@ -265,17 +286,17 @@ void ESP8266WebServer::_parseArguments(String data) {
int equal_sign_index = data.indexOf('=', pos); int equal_sign_index = data.indexOf('=', pos);
int next_arg_index = data.indexOf('&', pos); int next_arg_index = data.indexOf('&', pos);
#ifdef DEBUG #ifdef DEBUG
Serial.print("pos "); DEBUG_OUTPUT.print("pos ");
Serial.print(pos); DEBUG_OUTPUT.print(pos);
Serial.print("=@ "); DEBUG_OUTPUT.print("=@ ");
Serial.print(equal_sign_index); DEBUG_OUTPUT.print(equal_sign_index);
Serial.print(" &@ "); DEBUG_OUTPUT.print(" &@ ");
Serial.println(next_arg_index); DEBUG_OUTPUT.println(next_arg_index);
#endif #endif
if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) { if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
#ifdef DEBUG #ifdef DEBUG
Serial.print("arg missing value: "); DEBUG_OUTPUT.print("arg missing value: ");
Serial.println(iarg); DEBUG_OUTPUT.println(iarg);
#endif #endif
if (next_arg_index == -1) if (next_arg_index == -1)
break; break;
@ -286,12 +307,12 @@ void ESP8266WebServer::_parseArguments(String data) {
arg.key = data.substring(pos, equal_sign_index); arg.key = data.substring(pos, equal_sign_index);
arg.value = data.substring(equal_sign_index + 1, next_arg_index); arg.value = data.substring(equal_sign_index + 1, next_arg_index);
#ifdef DEBUG #ifdef DEBUG
Serial.print("arg "); DEBUG_OUTPUT.print("arg ");
Serial.print(iarg); DEBUG_OUTPUT.print(iarg);
Serial.print(" key: "); DEBUG_OUTPUT.print(" key: ");
Serial.print(arg.key); DEBUG_OUTPUT.print(arg.key);
Serial.print(" value: "); DEBUG_OUTPUT.print(" value: ");
Serial.println(arg.value); DEBUG_OUTPUT.println(arg.value);
#endif #endif
++iarg; ++iarg;
if (next_arg_index == -1) if (next_arg_index == -1)
@ -300,12 +321,213 @@ void ESP8266WebServer::_parseArguments(String data) {
} }
_currentArgCount = iarg; _currentArgCount = iarg;
#ifdef DEBUG #ifdef DEBUG
Serial.print("args count: "); DEBUG_OUTPUT.print("args count: ");
Serial.println(_currentArgCount); DEBUG_OUTPUT.println(_currentArgCount);
#endif #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;
if (postArgs) delete[] postArgs;
}
}
void ESP8266WebServer::onFileUpload(THandlerFunction fn) {
_fileUploadHandler = fn;
}
void ESP8266WebServer::onNotFound(THandlerFunction fn) { void ESP8266WebServer::onNotFound(THandlerFunction fn) {
_notFoundHandler = fn; _notFoundHandler = fn;
} }
@ -330,7 +552,7 @@ void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod
if (!handler){ if (!handler){
#ifdef DEBUG #ifdef DEBUG
Serial.println("request handler not found"); DEBUG_OUTPUT.println("request handler not found");
#endif #endif
if(_notFoundHandler) { if(_notFoundHandler) {

View File

@ -17,6 +17,7 @@
You should have received a copy of the GNU Lesser General Public You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 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> #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 class ESP8266WebServer
{ {
@ -42,10 +53,12 @@ public:
void on(const char* uri, THandlerFunction handler); void on(const char* uri, THandlerFunction handler);
void on(const char* uri, HTTPMethod method, THandlerFunction fn); void on(const char* uri, HTTPMethod method, THandlerFunction fn);
void onNotFound(THandlerFunction fn); //called when handler is not assigned void onNotFound(THandlerFunction fn); //called when handler is not assigned
void onFileUpload(THandlerFunction fn); //handle file uploads
String uri() { return _currentUri; } String uri() { return _currentUri; }
HTTPMethod method() { return _currentMethod; } HTTPMethod method() { return _currentMethod; }
WiFiClient client() { return _currentClient; } WiFiClient client() { return _currentClient; }
HTTPUpload upload() { return _currentUpload; }
String arg(const char* name); // get request argument value by name String arg(const char* name); // get request argument value by name
String arg(int i); // get request argument value by number String arg(int i); // get request argument value by number
@ -64,6 +77,7 @@ protected:
void _parseArguments(String data); void _parseArguments(String data);
static const char* _responseCodeToString(int code); static const char* _responseCodeToString(int code);
static void _appendHeader(String& response, const char* name, const char* value); static void _appendHeader(String& response, const char* name, const char* value);
void _parseForm(WiFiClient& client, String boundary, uint32_t len);
struct RequestHandler; struct RequestHandler;
struct RequestArgument { struct RequestArgument {
@ -79,10 +93,12 @@ protected:
size_t _currentArgCount; size_t _currentArgCount;
RequestArgument* _currentArgs; RequestArgument* _currentArgs;
HTTPUpload _currentUpload;
RequestHandler* _firstHandler; RequestHandler* _firstHandler;
RequestHandler* _lastHandler; RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler; THandlerFunction _notFoundHandler;
THandlerFunction _fileUploadHandler;
}; };