1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-04-25 20:02:37 +03:00

Merge pull request #20 from esp8266/esp8266

pull latest changes
This commit is contained in:
ficeto 2015-05-14 15:27:21 +03:00
commit c570fd6348
9 changed files with 600 additions and 528 deletions

View File

@ -21,41 +21,46 @@
#include "FileSystem.h"
#include "Arduino.h"
boolean FSClass::mount(){
if(_mounted) return true;
bool FSClass::mount() {
if (_mounted)
return true;
_mounted = spiffs_mount();
return _mounted;
}
void FSClass::unmount(){
if(!_mounted) return;
void FSClass::unmount() {
if (!_mounted)
return;
spiffs_unmount();
_mounted = false;
}
boolean FSClass::format(){
bool FSClass::format() {
return spiffs_format();
}
boolean FSClass::exists(const char *filename){
bool FSClass::exists(const char *filename) {
spiffs_stat stat = {0};
if (SPIFFS_stat(&_filesystemStorageHandle, filename, &stat) < 0) return false;
if (SPIFFS_stat(&_filesystemStorageHandle, filename, &stat) < 0)
return false;
return stat.name[0] != '\0';
}
boolean FSClass::create(const char *filepath){
bool FSClass::create(const char *filepath){
return SPIFFS_creat(&_filesystemStorageHandle, filepath, 0) == 0;
}
boolean FSClass::remove(const char *filepath){
bool FSClass::remove(const char *filepath){
return SPIFFS_remove(&_filesystemStorageHandle, filepath) == 0;
}
boolean FSClass::rename(const char *filename, const char *newname){
bool FSClass::rename(const char *filename, const char *newname) {
return SPIFFS_rename(&_filesystemStorageHandle, filename, newname) == 0;
}
FSFile FSClass::open(const char *filename, uint8_t mode){
FSFile FSClass::open(const char *filename, uint8_t mode) {
int repeats = 0;
bool notExist;
bool canRecreate = (mode & SPIFFS_CREAT) == SPIFFS_CREAT;
@ -81,25 +86,25 @@ FSFile FSClass::open(const char *filename, uint8_t mode){
FSClass FS;
FSFile::FSFile(){
FSFile::FSFile() {
_file = 0;
_stats = {0};
}
FSFile::FSFile(file_t f){
FSFile::FSFile(file_t f) {
_file = f;
if(SPIFFS_fstat(&_filesystemStorageHandle, _file, &_stats) != 0){
debugf("mount errno %d\n", SPIFFS_errno(&_filesystemStorageHandle));
}
}
void FSFile::close(){
void FSFile::close() {
if (! _file) return;
SPIFFS_close(&_filesystemStorageHandle, _file);
_file = 0;
}
uint32_t FSFile::size(){
uint32_t FSFile::size() {
if(! _file) return 0;
uint32_t pos = SPIFFS_tell(&_filesystemStorageHandle, _file);
SPIFFS_lseek(&_filesystemStorageHandle, _file, 0, SPIFFS_SEEK_END);
@ -108,31 +113,31 @@ uint32_t FSFile::size(){
return size;
}
uint32_t FSFile::seek(uint32_t pos){
uint32_t FSFile::seek(uint32_t pos) {
if (! _file) return 0;
return SPIFFS_lseek(&_filesystemStorageHandle, _file, pos, SPIFFS_SEEK_SET);
}
uint32_t FSFile::position(){
uint32_t FSFile::position() {
if (! _file) return 0;
return SPIFFS_tell(&_filesystemStorageHandle, _file);
}
boolean FSFile::eof(){
bool FSFile::eof() {
if (! _file) return 0;
return SPIFFS_eof(&_filesystemStorageHandle, _file);
}
boolean FSFile::isDirectory(void){
bool FSFile::isDirectory(void) {
return false;
}
int FSFile::read(void *buf, uint16_t nbyte){
int FSFile::read(void *buf, uint16_t nbyte) {
if (! _file) return -1;
return SPIFFS_read(&_filesystemStorageHandle, _file, buf, nbyte);
}
int FSFile::read(){
int FSFile::read() {
if (! _file) return -1;
int val;
if(SPIFFS_read(&_filesystemStorageHandle, _file, &val, 1) != 1) return -1;

View File

@ -29,7 +29,7 @@ class String;
#define FSFILE_WRITE (SPIFFS_RDONLY | SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_APPEND | SPIFFS_TRUNC)
class FSFile : public Stream {
private:
private:
spiffs_stat _stats;
file_t _file;
@ -47,34 +47,29 @@ public:
uint32_t remove();
uint32_t position();
uint32_t size();
boolean eof();
bool eof();
void close();
int lastError();
void clearError();
operator bool(){ return _file > 0; }
operator bool() { return _file > 0; }
char * name();
boolean isDirectory(void);
bool isDirectory(void);
template<typename T> size_t write(T &src){
uint8_t obuf[64];
size_t doneLen = 0;
size_t sentLen;
int i;
while (src.available() > 64){
src.read(obuf, 64);
sentLen = write(obuf, 64);
doneLen = doneLen + sentLen;
if(sentLen != 64){
return doneLen;
const size_t bufferSize = 64;
uint8_t obuf[bufferSize];
size_t bytesWritten = 0;
while (true){
size_t available = src.available();
size_t willWrite = (available < bufferSize) ? available : bufferSize;
src.read(obuf, willWrite);
size_t cb = write(obuf, willWrite);
bytesWritten += cb;
if (cb != willWrite) {
return bytesWritten;
}
}
size_t leftLen = src.available();
src.read(obuf, leftLen);
sentLen = write(obuf, leftLen);
doneLen = doneLen + sentLen;
return doneLen;
return bytesWritten;
}
using Print::write;
@ -83,16 +78,16 @@ public:
class FSClass {
private:
boolean _mounted;
bool _mounted;
public:
boolean mount();
bool mount();
void unmount();
boolean format();
boolean exists(const char *filename);
boolean create(const char *filepath);
boolean remove(const char *filepath);
boolean rename(const char *filename, const char *newname);
bool format();
bool exists(const char *filename);
bool create(const char *filepath);
bool remove(const char *filepath);
bool rename(const char *filename, const char *newname);
FSFile open(const char *filename, uint8_t mode = FSFILE_READ);

View File

@ -76,7 +76,6 @@ void delayMicroseconds(unsigned int us) {
void init() {
initPins();
timer1_isr_init();
//spiffs_mount();
os_timer_setfn(&micros_overflow_timer, (os_timer_func_t*) &micros_overflow_tick, 0);
os_timer_arm(&micros_overflow_timer, 60000, REPEAT);
}

View File

@ -12,8 +12,6 @@
extern "C" {
#endif
//#include "c_stdio.h"
#include <user_config.h>
#include "spiffs_config.h"
#include "flashmem.h"

View File

@ -34,7 +34,6 @@
#include <SPI.h>
#include <SD.h>
#define WWW_BUF_SIZE 1460
#define DBG_OUTPUT_PORT Serial
const char* ssid = "**********";
@ -47,28 +46,17 @@ ESP8266WebServer server(80);
static bool hasSD = false;
File uploadFile;
void returnOK(){
WiFiClient client = server.client();
String message = "HTTP/1.1 200 OK\r\n";
message += "Content-Type: text/plain\r\n";
message += "Connection: close\r\n";
message += "Access-Control-Allow-Origin: *\r\n";
message += "\r\n";
client.print(message);
client.stop();
void returnOK() {
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, "text/plain", "");
}
void returnFail(String msg){
WiFiClient client = server.client();
String message = "HTTP/1.1 500 Fail\r\n";
message += "Content-Type: text/plain\r\n";
message += "Connection: close\r\n";
message += "Access-Control-Allow-Origin: *\r\n";
message += "\r\n";
message += msg;
message += "\r\n";
client.print(message);
client.stop();
void returnFail(String msg) {
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(500, "text/plain", msg + "\r\n");
}
bool loadFromSdCard(String path){
@ -93,46 +81,40 @@ bool loadFromSdCard(String path){
dataType = "text/html";
dataFile = SD.open(path.c_str());
}
if (!dataFile)
return false;
if(server.hasArg("download")) dataType = "application/octet-stream";
if (dataFile) {
WiFiClient client = server.client();
String head = "HTTP/1.1 200 OK\r\nContent-Type: ";
head += dataType;
head += "\r\nContent-Length: ";
head += dataFile.size();
head += "\r\nConnection: close";
head += "\r\nAccess-Control-Allow-Origin: *";
head += "\r\n\r\n";
client.print(head);
dataType = String();
path = String();
if(client.write(dataFile) != dataFile.size()){
DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
dataFile.close();
client.stop();
return true;
server.sendHeader("Content-Length", String(dataFile.size()));
server.sendHeader("Connection", "close");
server.sendHeader("Access-Control-Allow-Origin", "*");
server.send(200, dataType.c_str(), "");
WiFiClient client = server.client();
size_t totalSize = dataFile.size();
if (client.write(dataFile, HTTP_DOWNLOAD_UNIT_SIZE) != totalSize) {
DBG_OUTPUT_PORT.println("Sent less data than expected!");
}
return false;
dataFile.close();
return true;
}
void handleFileUpload(){
if(server.uri() != "/edit") return;
HTTPUpload upload = server.upload();
HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START){
if(SD.exists((char *)upload.filename.c_str())) SD.remove((char *)upload.filename.c_str());
uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
} else if(upload.status == UPLOAD_FILE_WRITE){
if(uploadFile) uploadFile.write(upload.buf, upload.buflen);
DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.buflen);
if(uploadFile) uploadFile.write(upload.buf, upload.currentSize);
DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END){
if(uploadFile) uploadFile.close();
DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.size);
DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
}
}
@ -143,13 +125,12 @@ void deleteRecursive(String path){
SD.remove((char *)path.c_str());
return;
}
file.rewindDirectory();
File entry;
String entryPath;
while(true) {
entry = file.openNextFile();
File entry = file.openNextFile();
if (!entry) break;
entryPath = path + "/" +entry.name();
String entryPath = path + "/" +entry.name();
if(entry.isDirectory()){
entry.close();
deleteRecursive(entryPath);
@ -157,27 +138,32 @@ void deleteRecursive(String path){
entry.close();
SD.remove((char *)entryPath.c_str());
}
entryPath = String();
yield();
}
SD.rmdir((char *)path.c_str());
path = String();
file.close();
}
void handleDelete(){
if(server.args() == 0) return returnFail("BAD ARGS");
String path = server.arg(0);
if(path == "/" || !SD.exists((char *)path.c_str())) return returnFail("BAD PATH");
if(path == "/" || !SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
deleteRecursive(path);
returnOK();
path = String();
}
void handleCreate(){
if(server.args() == 0) return returnFail("BAD ARGS");
String path = server.arg(0);
if(path == "/" || SD.exists((char *)path.c_str())) return returnFail("BAD PATH");
if(path == "/" || SD.exists((char *)path.c_str())) {
returnFail("BAD PATH");
return;
}
if(path.indexOf('.') > 0){
File file = SD.open((char *)path.c_str(), FILE_WRITE);
if(file){
@ -188,7 +174,6 @@ void handleCreate(){
SD.mkdir((char *)path.c_str());
}
returnOK();
path = String();
}
void printDirectory() {
@ -203,31 +188,31 @@ void printDirectory() {
}
dir.rewindDirectory();
File entry;
server.send(200, "text/json", "");
WiFiClient client = server.client();
client.print("HTTP/1.1 200 OK\r\nContent-Type: text/json\r\n\r\n");
String output = "[";
while(true) {
entry = dir.openNextFile();
if (!entry) break;
if(output != "[") output += ',';
output += "{\"type\":\"";
output += (entry.isDirectory())?"dir":"file";
output += "\",\"name\":\"";
output += entry.name();
output += "\"";
output += "}";
entry.close();
if(output.length() > 1460){
client.write(output.substring(0, 1460).c_str(), 1460);
output = output.substring(1460);
}
for (int cnt = 0; true; ++cnt) {
File entry = dir.openNextFile();
if (!entry)
break;
String output;
if (cnt == 0)
output = '[';
else
output = ',';
output += "{\"type\":\"";
output += (entry.isDirectory()) ? "dir" : "file";
output += "\",\"name\":\"";
output += entry.name();
output += "\"";
output += "}";
server.sendContent(output);
entry.close();
}
server.sendContent("]");
dir.close();
output += "]";
client.write(output.c_str(), output.length());
client.stop();
output = String();
}
void handleNotFound(){
@ -267,14 +252,14 @@ void setup(void){
}
DBG_OUTPUT_PORT.print("Connected! IP address: ");
DBG_OUTPUT_PORT.println(WiFi.localIP());
/*
if (mdns.begin(hostname, WiFi.localIP())) {
DBG_OUTPUT_PORT.println("MDNS responder started");
DBG_OUTPUT_PORT.print("You can now connect to http://");
DBG_OUTPUT_PORT.print(hostname);
DBG_OUTPUT_PORT.println(".local");
}
*/
server.on("/list", HTTP_GET, printDirectory);
server.on("/edit", HTTP_DELETE, handleDelete);
@ -291,7 +276,7 @@ void setup(void){
hasSD = true;
}
}
void loop(void){
server.handleClient();
}

View File

@ -26,8 +26,8 @@
#include "WiFiClient.h"
#include "ESP8266WebServer.h"
//#define DEBUG
#define DEBUG_OUTPUT Serial1
// #define DEBUG
#define DEBUG_OUTPUT Serial
struct ESP8266WebServer::RequestHandler {
RequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method)
@ -99,110 +99,32 @@ void ESP8266WebServer::handleClient()
#ifdef DEBUG
DEBUG_OUTPUT.println("New client");
#endif
// Wait for data from client to become available
while(client.connected() && !client.available()){
delay(1);
}
// Read the first line of HTTP request
String req = client.readStringUntil('\r');
client.readStringUntil('\n');
// 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
DEBUG_OUTPUT.print("Invalid request: ");
DEBUG_OUTPUT.println(req);
#endif
if (!_parseRequest(client)) {
return;
}
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;
//bellow is needed only when POST type request
if(method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
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(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;
}
} 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);
_currentClient = client;
_handleRequest();
}
void ESP8266WebServer::sendHeader(String name, String value, bool first) {
String headerLine = name;
headerLine += ": ";
headerLine += value;
headerLine += "\r\n";
if (first) {
_responseHeaders = headerLine + _responseHeaders;
}
else {
_responseHeaders += headerLine;
}
client.flush();
#ifdef DEBUG
DEBUG_OUTPUT.print("Request: ");
DEBUG_OUTPUT.println(url);
DEBUG_OUTPUT.print(" Arguments: ");
DEBUG_OUTPUT.println(searchStr);
#endif
_handleRequest(client, url, method);
}
void ESP8266WebServer::send(int code, const char* content_type, String content) {
@ -214,11 +136,28 @@ void ESP8266WebServer::send(int code, const char* content_type, String content)
if (!content_type)
content_type = "text/html";
_appendHeader(response, "Content-Type", content_type);
sendHeader("Content-Type", content_type, true);
response += _responseHeaders;
response += "\r\n";
response += content;
_currentClient.print(response);
_responseHeaders = String();
sendContent(response);
}
void ESP8266WebServer::sendContent(String content) {
size_t size_to_send = content.length();
size_t size_sent = 0;
while(size_to_send) {
const size_t unit_size = HTTP_DOWNLOAD_UNIT_SIZE;
size_t will_send = (size_to_send < unit_size) ? size_to_send : unit_size;
size_t sent = _currentClient.write(content.c_str() + size_sent, will_send);
size_to_send -= sent;
size_sent += sent;
if (sent == 0) {
break;
}
}
}
String ESP8266WebServer::arg(const char* name) {
@ -253,277 +192,6 @@ bool ESP8266WebServer::hasArg(const char* name) {
return false;
}
void ESP8266WebServer::_parseArguments(String data) {
#ifdef DEBUG
DEBUG_OUTPUT.print("args: ");
DEBUG_OUTPUT.println(data);
#endif
if (_currentArgs)
delete[] _currentArgs;
_currentArgs = 0;
if (data.length() == 0) {
_currentArgCount = 0;
return;
}
_currentArgCount = 1;
for (int i = 0; i < data.length(); ) {
i = data.indexOf('&', i);
if (i == -1)
break;
++i;
++_currentArgCount;
}
#ifdef DEBUG
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
_currentArgs = new RequestArgument[_currentArgCount];
int pos = 0;
int iarg;
for (iarg = 0; iarg < _currentArgCount;) {
int equal_sign_index = data.indexOf('=', pos);
int next_arg_index = data.indexOf('&', pos);
#ifdef DEBUG
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
DEBUG_OUTPUT.print("arg missing value: ");
DEBUG_OUTPUT.println(iarg);
#endif
if (next_arg_index == -1)
break;
pos = next_arg_index + 1;
continue;
}
RequestArgument& arg = _currentArgs[iarg];
arg.key = data.substring(pos, equal_sign_index);
arg.value = data.substring(equal_sign_index + 1, next_arg_index);
#ifdef DEBUG
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)
break;
pos = next_arg_index + 1;
}
_currentArgCount = iarg;
#ifdef DEBUG
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
}
void ESP8266WebServer::_uploadWriteByte(uint8_t b){
if(_currentUpload.buflen == HTTP_UPLOAD_BUFLEN){
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.size += _currentUpload.buflen;
_currentUpload.buflen = 0;
}
_currentUpload.buf[_currentUpload.buflen++] = b;
}
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;
if(argValue.length() > 0) argValue += "\n";
argValue += line;
}
#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){
_uploadWriteByte(argByte);
argByte = client.read();
}
argByte = client.read();
if(argByte == 0x0A){
argByte = client.read();
if((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
goto readfile;
} else {
argByte = client.read();
if((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
goto readfile;
}
}
uint8_t endBuf[boundary.length()];
client.readBytes(endBuf, boundary.length());
if(strstr((const char*)endBuf, (const char*)(boundary.c_str())) != NULL){
if(_fileUploadHandler) _fileUploadHandler();
_currentUpload.size += _currentUpload.buflen;
_currentUpload.status = UPLOAD_FILE_END;
if(_fileUploadHandler) _fileUploadHandler();
#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
line = client.readStringUntil(0x0D);
client.readStringUntil(0x0A);
if(line == "--"){
#ifdef DEBUG
DEBUG_OUTPUT.println("Done Parsing POST");
#endif
break;
}
continue;
} else {
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
uint32_t i = 0;
while(i < boundary.length()){
_uploadWriteByte(endBuf[i++]);
}
argByte = client.read();
goto readfile;
}
} else {
_uploadWriteByte(0x0D);
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;
}
@ -532,18 +200,14 @@ void ESP8266WebServer::onNotFound(THandlerFunction fn) {
_notFoundHandler = fn;
}
void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod method) {
_currentClient = client;
_currentUri = uri;
_currentMethod = method;
void ESP8266WebServer::_handleRequest() {
RequestHandler* handler;
for (handler = _firstHandler; handler; handler = handler->next)
{
if (handler->method != HTTP_ANY && handler->method != method)
if (handler->method != HTTP_ANY && handler->method != _currentMethod)
continue;
if (handler->uri != uri)
if (handler->uri != _currentUri)
continue;
handler->fn();
@ -559,26 +223,19 @@ void ESP8266WebServer::_handleRequest(WiFiClient& client, String uri, HTTPMethod
_notFoundHandler();
}
else {
send(404, "text/plain", String("Not found: ") + uri);
send(404, "text/plain", String("Not found: ") + _currentUri);
}
}
_currentClient = WiFiClient();
_currentUri = String();
}
const char* ESP8266WebServer::_responseCodeToString(int code) {
switch (code) {
case 200: return "OK";
case 404: return "Not found";
case 500: return "Fail";
default: return "";
}
}
void ESP8266WebServer::_appendHeader(String& response, const char* name, const char* value) {
response += name;
response += ": ";
response += value;
response += "\r\n";
}

View File

@ -29,22 +29,22 @@
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 };
#define HTTP_DOWNLOAD_UNIT_SIZE 1460
#define HTTP_UPLOAD_BUFLEN 2048
typedef struct {
HTTPUploadStatus status;
String filename;
String name;
String type;
size_t size;
size_t buflen;
String filename;
String name;
String type;
size_t totalSize; // file size
size_t currentSize; // size of data currently in buf
uint8_t buf[HTTP_UPLOAD_BUFLEN];
} HTTPUpload;
class ESP8266WebServer
{
public:
ESP8266WebServer(int port = 80);
~ESP8266WebServer();
@ -60,7 +60,7 @@ public:
String uri() { return _currentUri; }
HTTPMethod method() { return _currentMethod; }
WiFiClient client() { return _currentClient; }
HTTPUpload upload() { return _currentUpload; }
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
@ -74,11 +74,13 @@ public:
// content - actual content body
void send(int code, const char* content_type = NULL, String content = String(""));
void sendHeader(String name, String value, bool first = false);
void sendContent(String content);
protected:
void _handleRequest(WiFiClient& client, String uri, HTTPMethod method);
void _handleRequest();
bool _parseRequest(WiFiClient& client);
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);
void _uploadWriteByte(uint8_t b);
@ -98,6 +100,8 @@ protected:
RequestArgument* _currentArgs;
HTTPUpload _currentUpload;
String _responseHeaders;
RequestHandler* _firstHandler;
RequestHandler* _lastHandler;
THandlerFunction _notFoundHandler;

View File

@ -0,0 +1,405 @@
/*
Parsing.cpp - HTTP request parsing.
Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
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)
*/
#include <Arduino.h>
#include "WiFiServer.h"
#include "WiFiClient.h"
#include "ESP8266WebServer.h"
// #define DEBUG
#define DEBUG_OUTPUT Serial1
bool ESP8266WebServer::_parseRequest(WiFiClient& client) {
// Read the first line of HTTP request
String req = client.readStringUntil('\r');
client.readStringUntil('\n');
// 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
DEBUG_OUTPUT.print("Invalid request: ");
DEBUG_OUTPUT.println(req);
#endif
return false;
}
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;
}
_currentMethod = method;
#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;
// below is needed only when POST type request
if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
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 (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;
}
} 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
DEBUG_OUTPUT.print("Request: ");
DEBUG_OUTPUT.println(url);
DEBUG_OUTPUT.print(" Arguments: ");
DEBUG_OUTPUT.println(searchStr);
#endif
return true;
}
void ESP8266WebServer::_parseArguments(String data) {
#ifdef DEBUG
DEBUG_OUTPUT.print("args: ");
DEBUG_OUTPUT.println(data);
#endif
if (_currentArgs)
delete[] _currentArgs;
_currentArgs = 0;
if (data.length() == 0) {
_currentArgCount = 0;
return;
}
_currentArgCount = 1;
for (int i = 0; i < data.length(); ) {
i = data.indexOf('&', i);
if (i == -1)
break;
++i;
++_currentArgCount;
}
#ifdef DEBUG
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
_currentArgs = new RequestArgument[_currentArgCount];
int pos = 0;
int iarg;
for (iarg = 0; iarg < _currentArgCount;) {
int equal_sign_index = data.indexOf('=', pos);
int next_arg_index = data.indexOf('&', pos);
#ifdef DEBUG
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
DEBUG_OUTPUT.print("arg missing value: ");
DEBUG_OUTPUT.println(iarg);
#endif
if (next_arg_index == -1)
break;
pos = next_arg_index + 1;
continue;
}
RequestArgument& arg = _currentArgs[iarg];
arg.key = data.substring(pos, equal_sign_index);
arg.value = data.substring(equal_sign_index + 1, next_arg_index);
#ifdef DEBUG
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)
break;
pos = next_arg_index + 1;
}
_currentArgCount = iarg;
#ifdef DEBUG
DEBUG_OUTPUT.print("args count: ");
DEBUG_OUTPUT.println(_currentArgCount);
#endif
}
void ESP8266WebServer::_uploadWriteByte(uint8_t b){
if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
if (_fileUploadHandler) _fileUploadHandler();
_currentUpload.totalSize += _currentUpload.currentSize;
_currentUpload.currentSize = 0;
}
_currentUpload.buf[_currentUpload.currentSize++] = b;
}
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;
if (argValue.length() > 0) argValue += "\n";
argValue += line;
}
#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.totalSize = 0;
_currentUpload.currentSize = 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){
_uploadWriteByte(argByte);
argByte = client.read();
}
argByte = client.read();
if (argByte == 0x0A){
argByte = client.read();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
goto readfile;
} else {
argByte = client.read();
if ((char)argByte != '-'){
//continue reading the file
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
_uploadWriteByte((uint8_t)('-'));
goto readfile;
}
}
uint8_t endBuf[boundary.length()];
client.readBytes(endBuf, boundary.length());
if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
if (_fileUploadHandler) _fileUploadHandler();
_currentUpload.totalSize += _currentUpload.currentSize;
_currentUpload.status = UPLOAD_FILE_END;
if (_fileUploadHandler) _fileUploadHandler();
#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.totalSize);
#endif
line = client.readStringUntil(0x0D);
client.readStringUntil(0x0A);
if (line == "--"){
#ifdef DEBUG
DEBUG_OUTPUT.println("Done Parsing POST");
#endif
break;
}
continue;
} else {
_uploadWriteByte(0x0D);
_uploadWriteByte(0x0A);
uint32_t i = 0;
while(i < boundary.length()){
_uploadWriteByte(endBuf[i++]);
}
argByte = client.read();
goto readfile;
}
} else {
_uploadWriteByte(0x0D);
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;
}
}

View File

@ -25,6 +25,7 @@
#include "Print.h"
#include "Client.h"
#include "IPAddress.h"
#include <memory>
class ClientContext;
class WiFiServer;
@ -44,6 +45,9 @@ public:
virtual int connect(const char *host, uint16_t port);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buf, size_t size);
template <typename T>
size_t write(T& source, size_t unitSize);
virtual int available();
virtual int read();
virtual int read(uint8_t *buf, size_t size);
@ -96,4 +100,24 @@ private:
};
template <typename T>
inline size_t WiFiClient::write(T& source, size_t unitSize) {
std::unique_ptr<uint8_t[]> buffer(new uint8_t[unitSize]);
size_t size_sent = 0;
while(true) {
size_t left = source.available();
if (!left)
break;
size_t will_send = (left < unitSize) ? left : unitSize;
source.read(buffer.get(), will_send);
size_t cb = write(buffer.get(), will_send);
size_sent += cb;
if (cb != will_send) {
break;
}
}
return size_sent;
}
#endif