Initial Import
This commit is contained in:
commit
b1f276a1a7
|
@ -0,0 +1,3 @@
|
||||||
|
JsonArray KEYWORD1
|
||||||
|
add KEYWORD2
|
||||||
|
createArray KEYWORD3
|
|
@ -0,0 +1,9 @@
|
||||||
|
name=ESP8266 Async Web Server
|
||||||
|
version=1.0.0
|
||||||
|
author=ESP8266
|
||||||
|
maintainer=Me-No-Dev
|
||||||
|
sentence=Async Web Server for ESP8266
|
||||||
|
paragraph=Async Web Server for ESP8266
|
||||||
|
category=Other
|
||||||
|
url=https://github.com/me-no-dev/ESPAsyncWebServer
|
||||||
|
architectures=*
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* AsyncWebServerHandlerImpl.h
|
||||||
|
*
|
||||||
|
* Created on: 19.12.2015 г.
|
||||||
|
* Author: ficeto
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASYNCWEBSERVERHANDLERIMPL_H_
|
||||||
|
#define ASYNCWEBSERVERHANDLERIMPL_H_
|
||||||
|
|
||||||
|
#include "stddef.h"
|
||||||
|
|
||||||
|
class AsyncStaticWebHandler: public AsyncWebHandler {
|
||||||
|
private:
|
||||||
|
protected:
|
||||||
|
FS _fs;
|
||||||
|
String _uri;
|
||||||
|
String _path;
|
||||||
|
String _cache_header;
|
||||||
|
bool _isFile;
|
||||||
|
public:
|
||||||
|
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||||
|
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){
|
||||||
|
_isFile = _fs.exists(path);
|
||||||
|
}
|
||||||
|
bool canHandle(AsyncWebServerRequest *request);
|
||||||
|
void handleRequest(AsyncWebServerRequest *request);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
||||||
|
private:
|
||||||
|
protected:
|
||||||
|
String _uri;
|
||||||
|
WebRequestMethod _method;
|
||||||
|
ArRequestHandlerFunction _onRequest;
|
||||||
|
ArUploadHandlerFunction _onUpload;
|
||||||
|
ArBodyHandlerFunction _onBody;
|
||||||
|
public:
|
||||||
|
AsyncCallbackWebHandler() : _uri(), _method(HTTP_ANY), _onRequest(NULL), _onUpload(NULL), _onBody(NULL){}
|
||||||
|
void setUri(String uri){ _uri = uri; }
|
||||||
|
void setMethod(WebRequestMethod method){ _method = method; }
|
||||||
|
void onRequest(ArRequestHandlerFunction fn){ _onRequest = fn; }
|
||||||
|
void onUpload(ArUploadHandlerFunction fn){ _onUpload = fn; }
|
||||||
|
void onBody(ArBodyHandlerFunction fn){ _onBody = fn; }
|
||||||
|
|
||||||
|
bool canHandle(AsyncWebServerRequest *request){
|
||||||
|
if(!_onRequest)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(_method != HTTP_ANY && request->method() != _method)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(_uri.length() > 1 && (_uri != request->url() && !request->url().startsWith(_uri+"/")))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void handleRequest(AsyncWebServerRequest *request){
|
||||||
|
if(_onRequest)
|
||||||
|
_onRequest(request);
|
||||||
|
else
|
||||||
|
request->send(500);
|
||||||
|
}
|
||||||
|
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
|
||||||
|
if(_onUpload)
|
||||||
|
_onUpload(request, filename, index, data, len, final);
|
||||||
|
}
|
||||||
|
void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
|
||||||
|
if(_onBody)
|
||||||
|
_onBody(request, data, len, index, total);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ASYNCWEBSERVERHANDLERIMPL_H_ */
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* AsyncWebImpl.h
|
||||||
|
*
|
||||||
|
* Created on: 19.12.2015 г.
|
||||||
|
* Author: ficeto
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ASYNCWEBSERVERRESPONSEIMPL_H_
|
||||||
|
#define ASYNCWEBSERVERRESPONSEIMPL_H_
|
||||||
|
|
||||||
|
class AsyncBasicResponse: public AsyncWebServerResponse {
|
||||||
|
private:
|
||||||
|
String _content;
|
||||||
|
public:
|
||||||
|
AsyncBasicResponse(int code, String contentType=String(), String content=String());
|
||||||
|
void _respond(AsyncWebServerRequest *request);
|
||||||
|
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncFileResponse: public AsyncWebServerResponse {
|
||||||
|
private:
|
||||||
|
File _content;
|
||||||
|
String _path;
|
||||||
|
String _head;
|
||||||
|
void _setContentType(String path);
|
||||||
|
public:
|
||||||
|
AsyncFileResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
|
~AsyncFileResponse();
|
||||||
|
void _respond(AsyncWebServerRequest *request);
|
||||||
|
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncStreamResponse: public AsyncWebServerResponse {
|
||||||
|
private:
|
||||||
|
Stream *_content;
|
||||||
|
String _head;
|
||||||
|
public:
|
||||||
|
AsyncStreamResponse(Stream &stream, String contentType, size_t len);
|
||||||
|
void _respond(AsyncWebServerRequest *request);
|
||||||
|
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AsyncCallbackResponse: public AsyncWebServerResponse {
|
||||||
|
private:
|
||||||
|
AwsResponseFiller _content;
|
||||||
|
String _head;
|
||||||
|
public:
|
||||||
|
AsyncCallbackResponse(String contentType, size_t len, AwsResponseFiller callback);
|
||||||
|
void _respond(AsyncWebServerRequest *request);
|
||||||
|
size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* ASYNCWEBSERVERRESPONSEIMPL_H_ */
|
|
@ -0,0 +1,274 @@
|
||||||
|
#ifndef _AsyncWebServer_H_
|
||||||
|
#define _AsyncWebServer_H_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#include <FS.h>
|
||||||
|
|
||||||
|
#include "StringArray.h"
|
||||||
|
|
||||||
|
class AsyncWebServer;
|
||||||
|
class AsyncWebServerRequest;
|
||||||
|
class AsyncWebServerResponse;
|
||||||
|
class AsyncWebHeader;
|
||||||
|
class AsyncWebParameter;
|
||||||
|
class AsyncWebHandler;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_PATCH, HTTP_HEAD, HTTP_OPTIONS
|
||||||
|
} WebRequestMethod;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PARAMETER :: Chainable object to hold GET/POST and FILE parameters
|
||||||
|
* */
|
||||||
|
|
||||||
|
class AsyncWebParameter {
|
||||||
|
private:
|
||||||
|
String _name;
|
||||||
|
String _value;
|
||||||
|
size_t _size;
|
||||||
|
bool _isForm;
|
||||||
|
bool _isFile;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebParameter *next;
|
||||||
|
|
||||||
|
AsyncWebParameter(String name, String value, bool form=false, bool file=false, size_t size=0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file), next(NULL){}
|
||||||
|
String name(){ return _name; }
|
||||||
|
String value(){ return _value; }
|
||||||
|
size_t size(){ return _size; }
|
||||||
|
bool isPost(){ return _isForm; }
|
||||||
|
bool isFile(){ return _isFile; }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HEADER :: Chainable object to hold the headers
|
||||||
|
* */
|
||||||
|
|
||||||
|
class AsyncWebHeader {
|
||||||
|
private:
|
||||||
|
String _name;
|
||||||
|
String _value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebHeader *next;
|
||||||
|
|
||||||
|
AsyncWebHeader(String name, String value): _name(name), _value(value), next(NULL){}
|
||||||
|
AsyncWebHeader(String data): _name(), _value(), next(NULL){
|
||||||
|
if(!data || !data.length() || data.indexOf(':') < 0)
|
||||||
|
return;
|
||||||
|
_name = data.substring(0, data.indexOf(':'));
|
||||||
|
_value = data.substring(data.indexOf(':') + 2);
|
||||||
|
}
|
||||||
|
~AsyncWebHeader(){}
|
||||||
|
String name(){ return _name; }
|
||||||
|
String value(){ return _value; }
|
||||||
|
String toString(){ return String(_name+": "+_value+"\r\n"); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect
|
||||||
|
* */
|
||||||
|
|
||||||
|
typedef std::function<size_t(uint8_t*, size_t, size_t)> AwsResponseFiller;
|
||||||
|
|
||||||
|
class AsyncWebServerRequest {
|
||||||
|
private:
|
||||||
|
AsyncClient* _client;
|
||||||
|
AsyncWebServer* _server;
|
||||||
|
AsyncWebHandler* _handler;
|
||||||
|
AsyncWebServerResponse* _response;
|
||||||
|
StringArray* _interestingHeaders;
|
||||||
|
|
||||||
|
String _temp;
|
||||||
|
uint8_t _parseState;
|
||||||
|
|
||||||
|
WebRequestMethod _method;
|
||||||
|
String _url;
|
||||||
|
String _host;
|
||||||
|
String _contentType;
|
||||||
|
String _boundary;
|
||||||
|
String _authorization;
|
||||||
|
bool _isMultipart;
|
||||||
|
bool _isPlainPost;
|
||||||
|
bool _expectingContinue;
|
||||||
|
size_t _contentLength;
|
||||||
|
size_t _parsedLength;
|
||||||
|
|
||||||
|
AsyncWebHeader *_headers;
|
||||||
|
AsyncWebParameter *_params;
|
||||||
|
|
||||||
|
uint8_t _multiParseState;
|
||||||
|
uint8_t _boundaryPosition;
|
||||||
|
size_t _itemStartIndex;
|
||||||
|
size_t _itemSize;
|
||||||
|
String _itemName;
|
||||||
|
String _itemFilename;
|
||||||
|
String _itemType;
|
||||||
|
String _itemValue;
|
||||||
|
uint8_t *_itemBuffer;
|
||||||
|
size_t _itemBufferIndex;
|
||||||
|
bool _itemIsFile;
|
||||||
|
|
||||||
|
void _onPoll();
|
||||||
|
void _onAck(size_t len, uint32_t time);
|
||||||
|
void _onError(err_t error);
|
||||||
|
void _onTimeout(uint32_t time);
|
||||||
|
void _onDisconnect();
|
||||||
|
void _onData(void *buf, size_t len);
|
||||||
|
|
||||||
|
void _addParam(AsyncWebParameter*);
|
||||||
|
String _urlDecode(const String& text);
|
||||||
|
|
||||||
|
bool _parseReqHead();
|
||||||
|
bool _parseReqHeader();
|
||||||
|
void _parseLine();
|
||||||
|
void _parseByte(uint8_t data);
|
||||||
|
void _parsePlainPostChar(uint8_t data);
|
||||||
|
void _parseMultipartPostByte(uint8_t data, bool last);
|
||||||
|
void _addGetParam(String param);
|
||||||
|
|
||||||
|
void _handleUploadStart();
|
||||||
|
void _handleUploadByte(uint8_t data, bool last);
|
||||||
|
void _handleUploadEnd();
|
||||||
|
|
||||||
|
public:
|
||||||
|
File _tempFile;
|
||||||
|
|
||||||
|
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
|
||||||
|
~AsyncWebServerRequest();
|
||||||
|
|
||||||
|
AsyncClient* client(){ return _client; }
|
||||||
|
WebRequestMethod method(){ return _method; }
|
||||||
|
String url(){ return _url; }
|
||||||
|
String host(){ return _host; }
|
||||||
|
String contentType(){ return _contentType; }
|
||||||
|
size_t contentLength(){ return _contentLength; }
|
||||||
|
bool multipart(){ return _isMultipart; }
|
||||||
|
|
||||||
|
bool authenticate(const char * username, const char * password);
|
||||||
|
bool authenticate(const char * hash);
|
||||||
|
void requestAuthentication();
|
||||||
|
|
||||||
|
void setHandler(AsyncWebHandler *handler){ _handler = handler; }
|
||||||
|
void addInterestingHeader(String name);
|
||||||
|
|
||||||
|
void send(AsyncWebServerResponse *response);
|
||||||
|
void send(int code, String contentType=String(), String content=String());
|
||||||
|
void send(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
|
void send(Stream &stream, String contentType, size_t len);
|
||||||
|
void send(String contentType, size_t len, AwsResponseFiller callback);
|
||||||
|
|
||||||
|
AsyncWebServerResponse *beginResponse(int code, String contentType=String(), String content=String());
|
||||||
|
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
|
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
||||||
|
AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback);
|
||||||
|
|
||||||
|
int headers(); // get header count
|
||||||
|
bool hasHeader(String name);
|
||||||
|
AsyncWebHeader* getHeader(String name);
|
||||||
|
AsyncWebHeader* getHeader(int num);
|
||||||
|
|
||||||
|
int params(); // get arguments count
|
||||||
|
bool hasParam(String name, bool post=false, bool file=false);
|
||||||
|
AsyncWebParameter* getParam(String name, bool post=false, bool file=false);
|
||||||
|
AsyncWebParameter* getParam(int num);
|
||||||
|
|
||||||
|
int args(){ return params(); } // get arguments count
|
||||||
|
String arg(const char* name); // get request argument value by name
|
||||||
|
String arg(int i); // get request argument value by number
|
||||||
|
String argName(int i); // get request argument name by number
|
||||||
|
bool hasArg(const char* name); // check if argument exists
|
||||||
|
|
||||||
|
String header(const char* name); // get request header value by name
|
||||||
|
String header(int i); // get request header value by number
|
||||||
|
String headerName(int i); // get request header name by number
|
||||||
|
bool hasHeader(const char* name); // check if header exists
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HANDLER :: One instance can be attached to any Request (done by the Server)
|
||||||
|
* */
|
||||||
|
|
||||||
|
class AsyncWebHandler {
|
||||||
|
public:
|
||||||
|
AsyncWebHandler* next;
|
||||||
|
AsyncWebHandler(): next(NULL){}
|
||||||
|
virtual ~AsyncWebHandler(){}
|
||||||
|
virtual bool canHandle(AsyncWebServerRequest *request){ return false; }
|
||||||
|
virtual void handleRequest(AsyncWebServerRequest *request){}
|
||||||
|
virtual void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){}
|
||||||
|
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RESPONSE :: One instance is created for each Request (attached by the Handler)
|
||||||
|
* */
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED
|
||||||
|
} WebResponseState;
|
||||||
|
|
||||||
|
class AsyncWebServerResponse {
|
||||||
|
protected:
|
||||||
|
int _code;
|
||||||
|
AsyncWebHeader *_headers;
|
||||||
|
String _contentType;
|
||||||
|
size_t _contentLength;
|
||||||
|
size_t _headLength;
|
||||||
|
size_t _sentLength;
|
||||||
|
size_t _ackedLength;
|
||||||
|
WebResponseState _state;
|
||||||
|
const char* _responseCodeToString(int code);
|
||||||
|
|
||||||
|
public:
|
||||||
|
AsyncWebServerResponse();
|
||||||
|
virtual ~AsyncWebServerResponse();
|
||||||
|
virtual void addHeader(String name, String value);
|
||||||
|
virtual String _assembleHead();
|
||||||
|
virtual bool _finished();
|
||||||
|
virtual bool _failed();
|
||||||
|
virtual void _respond(AsyncWebServerRequest *request);
|
||||||
|
virtual size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SERVER :: One instance
|
||||||
|
* */
|
||||||
|
|
||||||
|
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
|
||||||
|
typedef std::function<void(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final)> ArUploadHandlerFunction;
|
||||||
|
typedef std::function<void(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)> ArBodyHandlerFunction;
|
||||||
|
|
||||||
|
class AsyncWebServer {
|
||||||
|
private:
|
||||||
|
AsyncServer _server;
|
||||||
|
AsyncWebHandler* _handlers;
|
||||||
|
AsyncWebHandler* _catchAllHandler;
|
||||||
|
public:
|
||||||
|
AsyncWebServer(uint16_t port);
|
||||||
|
~AsyncWebServer(){}
|
||||||
|
|
||||||
|
void begin();
|
||||||
|
void addHandler(AsyncWebHandler* handler);
|
||||||
|
|
||||||
|
void on(const char* uri, ArRequestHandlerFunction onRequest);
|
||||||
|
void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest);
|
||||||
|
void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload);
|
||||||
|
void on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody);
|
||||||
|
|
||||||
|
void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL);
|
||||||
|
|
||||||
|
void onNotFound(ArRequestHandlerFunction fn); //called when handler is not assigned
|
||||||
|
void onFileUpload(ArUploadHandlerFunction fn); //handle file uploads
|
||||||
|
void onRequestBody(ArBodyHandlerFunction fn); //handle posts with plain body content (JSON often transmitted this way as a request)
|
||||||
|
|
||||||
|
void _handleDisconnect(AsyncWebServerRequest *request);
|
||||||
|
void _handleRequest(AsyncWebServerRequest *request);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _AsyncWebServer_H_ */
|
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
* StringArray.h
|
||||||
|
*
|
||||||
|
* Created on: 18.12.2015 г.
|
||||||
|
* Author: ficeto
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef STRINGARRAY_H_
|
||||||
|
#define STRINGARRAY_H_
|
||||||
|
|
||||||
|
#include "stddef.h"
|
||||||
|
#include "String.h"
|
||||||
|
|
||||||
|
class StringArrayItem;
|
||||||
|
class StringArrayItem {
|
||||||
|
private:
|
||||||
|
String _string;
|
||||||
|
public:
|
||||||
|
StringArrayItem *next;
|
||||||
|
StringArrayItem(String str):_string(str), next(NULL){}
|
||||||
|
~StringArrayItem(){}
|
||||||
|
String string(){ return _string; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class StringArray {
|
||||||
|
private:
|
||||||
|
StringArrayItem *_items;
|
||||||
|
public:
|
||||||
|
StringArray():_items(NULL){}
|
||||||
|
StringArray(StringArrayItem *items):_items(items){}
|
||||||
|
~StringArray(){}
|
||||||
|
StringArrayItem *items(){
|
||||||
|
return _items;
|
||||||
|
}
|
||||||
|
void add(String str){
|
||||||
|
StringArrayItem *it = new StringArrayItem(str);
|
||||||
|
if(_items == NULL){
|
||||||
|
_items = it;
|
||||||
|
} else {
|
||||||
|
StringArrayItem *i = _items;
|
||||||
|
while(i->next != NULL) i = i->next;
|
||||||
|
i->next = it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size_t length(){
|
||||||
|
size_t i = 0;
|
||||||
|
StringArrayItem *it = _items;
|
||||||
|
while(it != NULL){
|
||||||
|
i++;
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
bool contains(String str){
|
||||||
|
StringArrayItem *it = _items;
|
||||||
|
while(it != NULL){
|
||||||
|
if(it->string() == str)
|
||||||
|
return true;
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String get(size_t index){
|
||||||
|
size_t i = 0;
|
||||||
|
StringArrayItem *it = _items;
|
||||||
|
while(it != NULL){
|
||||||
|
if(i++ == index)
|
||||||
|
return it->string();
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
bool remove(size_t index){
|
||||||
|
StringArrayItem *it = _items;
|
||||||
|
if(!index){
|
||||||
|
if(_items == NULL)
|
||||||
|
return false;
|
||||||
|
_items = _items->next;
|
||||||
|
delete it;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
size_t i = 0;
|
||||||
|
StringArrayItem *pit = _items;
|
||||||
|
while(it != NULL){
|
||||||
|
if(i++ == index){
|
||||||
|
pit->next = it->next;
|
||||||
|
delete it;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pit = it;
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool remove(String str){
|
||||||
|
StringArrayItem *it = _items;
|
||||||
|
StringArrayItem *pit = _items;
|
||||||
|
while(it != NULL){
|
||||||
|
if(it->string() == str){
|
||||||
|
if(it == _items){
|
||||||
|
_items = _items->next;
|
||||||
|
} else {
|
||||||
|
pit->next = it->next;
|
||||||
|
}
|
||||||
|
delete it;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
pit = it;
|
||||||
|
it = it->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void free(){
|
||||||
|
StringArrayItem *it;
|
||||||
|
while(_items != NULL){
|
||||||
|
it = _items;
|
||||||
|
_items = _items->next;
|
||||||
|
delete it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* STRINGARRAY_H_ */
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* WebHandlers.cpp
|
||||||
|
*
|
||||||
|
* Created on: 18.12.2015 г.
|
||||||
|
* Author: ficeto
|
||||||
|
*/
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
|
#include "AsyncWebServerHandlerImpl.h"
|
||||||
|
|
||||||
|
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request){
|
||||||
|
if(request->method() != HTTP_GET)
|
||||||
|
return false;
|
||||||
|
if ((_isFile && request->url() != _uri) || !request->url().startsWith(_uri))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request){
|
||||||
|
String path = request->url();
|
||||||
|
if(!_isFile){
|
||||||
|
if(_uri != "/"){
|
||||||
|
path = path.substring(_uri.length());
|
||||||
|
if(!path.length())
|
||||||
|
path = "/";
|
||||||
|
}
|
||||||
|
if(path.endsWith("/"))
|
||||||
|
path += "index.htm";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_fs.exists(path) || _fs.exists(path+".gz")){
|
||||||
|
AsyncWebServerResponse * response = request->beginResponse(_fs, path);
|
||||||
|
if (_cache_header.length() != 0)
|
||||||
|
response->addHeader("Cache-Control", _cache_header);
|
||||||
|
request->send(response);
|
||||||
|
} else
|
||||||
|
request->send(404);
|
||||||
|
path = String();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,447 @@
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
|
#include "AsyncWebServerResponseImpl.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abstract Response
|
||||||
|
* */
|
||||||
|
const char* AsyncWebServerResponse::_responseCodeToString(int code) {
|
||||||
|
switch (code) {
|
||||||
|
case 100: return "Continue";
|
||||||
|
case 101: return "Switching Protocols";
|
||||||
|
case 200: return "OK";
|
||||||
|
case 201: return "Created";
|
||||||
|
case 202: return "Accepted";
|
||||||
|
case 203: return "Non-Authoritative Information";
|
||||||
|
case 204: return "No Content";
|
||||||
|
case 205: return "Reset Content";
|
||||||
|
case 206: return "Partial Content";
|
||||||
|
case 300: return "Multiple Choices";
|
||||||
|
case 301: return "Moved Permanently";
|
||||||
|
case 302: return "Found";
|
||||||
|
case 303: return "See Other";
|
||||||
|
case 304: return "Not Modified";
|
||||||
|
case 305: return "Use Proxy";
|
||||||
|
case 307: return "Temporary Redirect";
|
||||||
|
case 400: return "Bad Request";
|
||||||
|
case 401: return "Unauthorized";
|
||||||
|
case 402: return "Payment Required";
|
||||||
|
case 403: return "Forbidden";
|
||||||
|
case 404: return "Not Found";
|
||||||
|
case 405: return "Method Not Allowed";
|
||||||
|
case 406: return "Not Acceptable";
|
||||||
|
case 407: return "Proxy Authentication Required";
|
||||||
|
case 408: return "Request Time-out";
|
||||||
|
case 409: return "Conflict";
|
||||||
|
case 410: return "Gone";
|
||||||
|
case 411: return "Length Required";
|
||||||
|
case 412: return "Precondition Failed";
|
||||||
|
case 413: return "Request Entity Too Large";
|
||||||
|
case 414: return "Request-URI Too Large";
|
||||||
|
case 415: return "Unsupported Media Type";
|
||||||
|
case 416: return "Requested range not satisfiable";
|
||||||
|
case 417: return "Expectation Failed";
|
||||||
|
case 500: return "Internal Server Error";
|
||||||
|
case 501: return "Not Implemented";
|
||||||
|
case 502: return "Bad Gateway";
|
||||||
|
case 503: return "Service Unavailable";
|
||||||
|
case 504: return "Gateway Time-out";
|
||||||
|
case 505: return "HTTP Version not supported";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse::AsyncWebServerResponse()
|
||||||
|
:_code(0)
|
||||||
|
,_headers(NULL)
|
||||||
|
,_contentType()
|
||||||
|
,_contentLength(0)
|
||||||
|
,_headLength(0)
|
||||||
|
,_sentLength(0)
|
||||||
|
,_ackedLength(0)
|
||||||
|
,_state(RESPONSE_SETUP)
|
||||||
|
{
|
||||||
|
addHeader("Connection","close");
|
||||||
|
addHeader("Access-Control-Allow-Origin","*");
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse::~AsyncWebServerResponse(){
|
||||||
|
while(_headers != NULL){
|
||||||
|
AsyncWebHeader *h = _headers;
|
||||||
|
_headers = h->next;
|
||||||
|
delete h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerResponse::addHeader(String name, String value){
|
||||||
|
AsyncWebHeader *header = new AsyncWebHeader(name, value);
|
||||||
|
if(_headers == NULL){
|
||||||
|
_headers = header;
|
||||||
|
} else {
|
||||||
|
AsyncWebHeader *h = _headers;
|
||||||
|
while(h->next != NULL) h = h->next;
|
||||||
|
h->next = header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerResponse::_assembleHead(){
|
||||||
|
String out = "HTTP/1.1 " + String(_code) + " " + _responseCodeToString(_code) + "\r\n";
|
||||||
|
out += "Content-Length: " + String(_contentLength) + "\r\n";
|
||||||
|
if(_contentType.length()){
|
||||||
|
out += "Content-Type: " + _contentType + "\r\n";
|
||||||
|
}
|
||||||
|
AsyncWebHeader *h;
|
||||||
|
while(_headers != NULL){
|
||||||
|
h = _headers;
|
||||||
|
_headers = _headers->next;
|
||||||
|
out += h->toString();
|
||||||
|
delete h;
|
||||||
|
}
|
||||||
|
out += "\r\n";
|
||||||
|
_headLength = out.length();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerResponse::_finished(){ return _state > RESPONSE_WAIT_ACK; }
|
||||||
|
bool AsyncWebServerResponse::_failed(){ return _state == RESPONSE_FAILED; }
|
||||||
|
void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_END; request->client()->close(); }
|
||||||
|
size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){ return 0; }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* String/Code Response
|
||||||
|
* */
|
||||||
|
AsyncBasicResponse::AsyncBasicResponse(int code, String contentType, String content){
|
||||||
|
_code = code;
|
||||||
|
_content = content;
|
||||||
|
_contentType = contentType;
|
||||||
|
if(_content.length()){
|
||||||
|
_contentLength = _content.length();
|
||||||
|
if(!_contentType.length())
|
||||||
|
_contentType = "text/plain";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
||||||
|
_state = RESPONSE_HEADERS;
|
||||||
|
String out = _assembleHead();
|
||||||
|
size_t outLen = out.length();
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(!_contentLength && space >= outLen){
|
||||||
|
request->client()->write(out.c_str(), outLen);
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
} else if(_contentLength && space >= outLen + _contentLength){
|
||||||
|
out += _content;
|
||||||
|
outLen += _contentLength;
|
||||||
|
request->client()->write(out.c_str(), outLen);
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
} else if(space && space < out.length()){
|
||||||
|
String partial = out.substring(0, space);
|
||||||
|
_content = out.substring(space) + _content;
|
||||||
|
_contentLength += outLen - space;
|
||||||
|
request->client()->write(partial.c_str(), partial.length());
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else if(space > outLen && space < (outLen + _contentLength)){
|
||||||
|
size_t shift = space - outLen;
|
||||||
|
outLen += shift;
|
||||||
|
_sentLength += shift;
|
||||||
|
out += _content.substring(0, shift);
|
||||||
|
_content = _content.substring(shift);
|
||||||
|
request->client()->write(out.c_str(), outLen);
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else {
|
||||||
|
_content = out + _content;
|
||||||
|
_contentLength += outLen;
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
||||||
|
_ackedLength += len;
|
||||||
|
if(_state == RESPONSE_CONTENT){
|
||||||
|
size_t available = _contentLength - _sentLength;
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
//we can fit in this packet
|
||||||
|
if(space > available){
|
||||||
|
request->client()->write(_content.c_str(), available);
|
||||||
|
_content = String();
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
//send some data, the rest on ack
|
||||||
|
String out = _content.substring(0, space);
|
||||||
|
_content = _content.substring(space);
|
||||||
|
_sentLength += space;
|
||||||
|
request->client()->write(out.c_str(), space);
|
||||||
|
return space;
|
||||||
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
|
if(_ackedLength >= (_headLength+_contentLength)){
|
||||||
|
_state = RESPONSE_END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* File Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncFileResponse::~AsyncFileResponse(){
|
||||||
|
if(_content)
|
||||||
|
_content.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncFileResponse::_setContentType(String path){
|
||||||
|
if (path.endsWith(".html")) _contentType = "text/html";
|
||||||
|
else if (path.endsWith(".htm")) _contentType = "text/html";
|
||||||
|
else if (path.endsWith(".css")) _contentType = "text/css";
|
||||||
|
else if (path.endsWith(".txt")) _contentType = "text/plain";
|
||||||
|
else if (path.endsWith(".js")) _contentType = "application/javascript";
|
||||||
|
else if (path.endsWith(".png")) _contentType = "image/png";
|
||||||
|
else if (path.endsWith(".gif")) _contentType = "image/gif";
|
||||||
|
else if (path.endsWith(".jpg")) _contentType = "image/jpeg";
|
||||||
|
else if (path.endsWith(".ico")) _contentType = "image/x-icon";
|
||||||
|
else if (path.endsWith(".svg")) _contentType = "image/svg+xml";
|
||||||
|
else if (path.endsWith(".xml")) _contentType = "text/xml";
|
||||||
|
else if (path.endsWith(".pdf")) _contentType = "application/pdf";
|
||||||
|
else if (path.endsWith(".zip")) _contentType = "application/zip";
|
||||||
|
else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
|
||||||
|
else _contentType = "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFileResponse::AsyncFileResponse(FS &fs, String path, String contentType, bool download){
|
||||||
|
_code = 200;
|
||||||
|
_path = path;
|
||||||
|
if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
|
||||||
|
_path = _path+".gz";
|
||||||
|
addHeader("Content-Encoding", "gzip");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(download)
|
||||||
|
_contentType = "application/octet-stream";
|
||||||
|
else
|
||||||
|
_setContentType(path);
|
||||||
|
_content = fs.open(_path, "r");
|
||||||
|
_contentLength = _content.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncFileResponse::_respond(AsyncWebServerRequest *request){
|
||||||
|
if(!_content){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->send(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_head = _assembleHead();
|
||||||
|
_state = RESPONSE_HEADERS;
|
||||||
|
size_t outLen = _head.length();
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(space >= outLen){
|
||||||
|
request->client()->write(_head.c_str(), outLen);
|
||||||
|
_head = String();
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
request->client()->write(out.c_str(), out.length());
|
||||||
|
out = String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncFileResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
||||||
|
if(!_content){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->client()->close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ackedLength += len;
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(_state == RESPONSE_CONTENT){
|
||||||
|
size_t remaining = _contentLength - _sentLength;
|
||||||
|
size_t outLen = (remaining > space)?space:remaining;
|
||||||
|
uint8_t *buf = (uint8_t *)os_malloc(outLen);
|
||||||
|
_content.read(buf, outLen);
|
||||||
|
request->client()->write((const char*)buf, outLen);
|
||||||
|
_sentLength += outLen;
|
||||||
|
os_free(buf);
|
||||||
|
if(_sentLength == _contentLength){
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
} else if(_state == RESPONSE_HEADERS){
|
||||||
|
size_t outLen = _head.length();
|
||||||
|
if(space >= outLen){
|
||||||
|
request->client()->write(_head.c_str(), outLen);
|
||||||
|
_head = String();
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
return outLen;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
request->client()->write(out.c_str(), out.length());
|
||||||
|
return out.length();
|
||||||
|
}
|
||||||
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
|
if(_ackedLength >= (_headLength+_contentLength)){
|
||||||
|
_state = RESPONSE_END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stream Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncStreamResponse::AsyncStreamResponse(Stream &stream, String contentType, size_t len){
|
||||||
|
_code = 200;
|
||||||
|
_content = &stream;
|
||||||
|
_contentLength = len;
|
||||||
|
_contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncStreamResponse::_respond(AsyncWebServerRequest *request){
|
||||||
|
if(!_content){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->send(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_head = _assembleHead();
|
||||||
|
_state = RESPONSE_HEADERS;
|
||||||
|
size_t outLen = _head.length();
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(space >= outLen){
|
||||||
|
request->client()->write(_head.c_str(), outLen);
|
||||||
|
_head = String();
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
request->client()->write(out.c_str(), out.length());
|
||||||
|
out = String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncStreamResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
||||||
|
if(!_content){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->client()->close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ackedLength += len;
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(_state == RESPONSE_CONTENT){
|
||||||
|
size_t remaining = _contentLength - _sentLength;
|
||||||
|
size_t available = _content->available();
|
||||||
|
available = (remaining >= available)?available:remaining;
|
||||||
|
size_t outLen = (available > space)?space:available;
|
||||||
|
uint8_t *buf = (uint8_t *)os_malloc(outLen);
|
||||||
|
size_t i;
|
||||||
|
for(i=0;i<outLen;i++)
|
||||||
|
buf[i] = _content->read();
|
||||||
|
request->client()->write((const char*)buf, outLen);
|
||||||
|
_sentLength += outLen;
|
||||||
|
os_free(buf);
|
||||||
|
if(_sentLength == _contentLength){
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
} else if(_state == RESPONSE_HEADERS){
|
||||||
|
size_t outLen = _head.length();
|
||||||
|
if(space >= outLen){
|
||||||
|
request->client()->write(_head.c_str(), outLen);
|
||||||
|
_head = String();
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
return outLen;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
request->client()->write(out.c_str(), out.length());
|
||||||
|
return out.length();
|
||||||
|
}
|
||||||
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
|
if(_ackedLength >= (_headLength+_contentLength)){
|
||||||
|
_state = RESPONSE_END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback Response
|
||||||
|
* */
|
||||||
|
|
||||||
|
AsyncCallbackResponse::AsyncCallbackResponse(String contentType, size_t len, AwsResponseFiller callback){
|
||||||
|
_code = 200;
|
||||||
|
_content = callback;
|
||||||
|
_contentLength = len;
|
||||||
|
_contentType = contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncCallbackResponse::_respond(AsyncWebServerRequest *request){
|
||||||
|
if(!_content){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->send(500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_head = _assembleHead();
|
||||||
|
_state = RESPONSE_HEADERS;
|
||||||
|
size_t outLen = _head.length();
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(space >= outLen){
|
||||||
|
request->client()->write(_head.c_str(), outLen);
|
||||||
|
_head = String();
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
request->client()->write(out.c_str(), out.length());
|
||||||
|
out = String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncCallbackResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time){
|
||||||
|
if(!_content){
|
||||||
|
_state = RESPONSE_FAILED;
|
||||||
|
request->client()->close();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
_ackedLength += len;
|
||||||
|
size_t space = request->client()->space();
|
||||||
|
if(_state == RESPONSE_CONTENT){
|
||||||
|
size_t remaining = _contentLength - _sentLength;
|
||||||
|
size_t outLen = (remaining > space)?space:remaining;
|
||||||
|
uint8_t *buf = (uint8_t *)os_malloc(outLen);
|
||||||
|
outLen = _content(buf, outLen, remaining);
|
||||||
|
request->client()->write((const char*)buf, outLen);
|
||||||
|
_sentLength += outLen;
|
||||||
|
os_free(buf);
|
||||||
|
if(_sentLength == _contentLength){
|
||||||
|
_state = RESPONSE_WAIT_ACK;
|
||||||
|
}
|
||||||
|
return outLen;
|
||||||
|
} else if(_state == RESPONSE_HEADERS){
|
||||||
|
size_t outLen = _head.length();
|
||||||
|
if(space >= outLen){
|
||||||
|
request->client()->write(_head.c_str(), outLen);
|
||||||
|
_head = String();
|
||||||
|
_state = RESPONSE_CONTENT;
|
||||||
|
return outLen;
|
||||||
|
} else {
|
||||||
|
String out = _head.substring(0, space);
|
||||||
|
_head = _head.substring(space);
|
||||||
|
request->client()->write(out.c_str(), out.length());
|
||||||
|
return out.length();
|
||||||
|
}
|
||||||
|
} else if(_state == RESPONSE_WAIT_ACK){
|
||||||
|
if(_ackedLength >= (_headLength+_contentLength)){
|
||||||
|
_state = RESPONSE_END;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
|
#include "AsyncWebServerHandlerImpl.h"
|
||||||
|
|
||||||
|
|
||||||
|
AsyncWebServer::AsyncWebServer(uint16_t port):_server(port), _handlers(0), _catchAllHandler(new AsyncCallbackWebHandler()){
|
||||||
|
_server.onClient([](void *s, AsyncClient* c){
|
||||||
|
new AsyncWebServerRequest((AsyncWebServer*)s, c);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::addHandler(AsyncWebHandler* handler){
|
||||||
|
if(_handlers == NULL){
|
||||||
|
_handlers = handler;
|
||||||
|
} else {
|
||||||
|
AsyncWebHandler* h = _handlers;
|
||||||
|
while(h->next != NULL) h = h->next;
|
||||||
|
h->next = handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::begin(){
|
||||||
|
_server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::_handleDisconnect(AsyncWebServerRequest *request){
|
||||||
|
delete request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::_handleRequest(AsyncWebServerRequest *request){
|
||||||
|
if(_handlers){
|
||||||
|
AsyncWebHandler* h = _handlers;
|
||||||
|
while(h && !h->canHandle(request)) h = h->next;
|
||||||
|
if(h){
|
||||||
|
request->setHandler(h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request->addInterestingHeader("ANY");
|
||||||
|
request->setHandler(_catchAllHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AsyncWebServer::on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload, ArBodyHandlerFunction onBody){
|
||||||
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
|
handler->setUri(uri);
|
||||||
|
handler->setMethod(method);
|
||||||
|
handler->onRequest(onRequest);
|
||||||
|
handler->onUpload(onUpload);
|
||||||
|
handler->onBody(onBody);
|
||||||
|
addHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest, ArUploadHandlerFunction onUpload){
|
||||||
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
|
handler->setUri(uri);
|
||||||
|
handler->setMethod(method);
|
||||||
|
handler->onRequest(onRequest);
|
||||||
|
handler->onUpload(onUpload);
|
||||||
|
addHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::on(const char* uri, WebRequestMethod method, ArRequestHandlerFunction onRequest){
|
||||||
|
AsyncCallbackWebHandler* handler = new AsyncCallbackWebHandler();
|
||||||
|
handler->setUri(uri);
|
||||||
|
handler->setMethod(method);
|
||||||
|
handler->onRequest(onRequest);
|
||||||
|
addHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::on(const char* uri, ArRequestHandlerFunction onRequest){
|
||||||
|
on(uri, HTTP_ANY, onRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header){
|
||||||
|
addHandler(new AsyncStaticWebHandler(fs, path, uri, cache_header));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::onNotFound(ArRequestHandlerFunction fn){
|
||||||
|
((AsyncCallbackWebHandler*)_catchAllHandler)->onRequest(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::onFileUpload(ArUploadHandlerFunction fn){
|
||||||
|
((AsyncCallbackWebHandler*)_catchAllHandler)->onUpload(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServer::onRequestBody(ArBodyHandlerFunction fn){
|
||||||
|
((AsyncCallbackWebHandler*)_catchAllHandler)->onBody(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,747 @@
|
||||||
|
/*
|
||||||
|
* WebServerClient.cpp
|
||||||
|
*
|
||||||
|
* Created on: 18.12.2015 г.
|
||||||
|
* Author: ficeto
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
|
#include "AsyncWebServerResponseImpl.h"
|
||||||
|
#include <libb64/cencode.h>
|
||||||
|
|
||||||
|
bool __is_param_char(char c){
|
||||||
|
return c && (c != '{') && (c != '[') && (c != '&') && (c != '=');
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL };
|
||||||
|
|
||||||
|
AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
||||||
|
: _client(c)
|
||||||
|
, _server(s)
|
||||||
|
, _handler(NULL)
|
||||||
|
, _response(NULL)
|
||||||
|
, _interestingHeaders(new StringArray())
|
||||||
|
, _temp()
|
||||||
|
, _parseState(0)
|
||||||
|
, _method(HTTP_ANY)
|
||||||
|
, _url()
|
||||||
|
, _host()
|
||||||
|
, _contentType()
|
||||||
|
, _boundary()
|
||||||
|
, _authorization()
|
||||||
|
, _isMultipart(false)
|
||||||
|
, _isPlainPost(false)
|
||||||
|
, _expectingContinue(false)
|
||||||
|
, _contentLength(0)
|
||||||
|
, _parsedLength(0)
|
||||||
|
, _headers(NULL)
|
||||||
|
, _params(NULL)
|
||||||
|
, _multiParseState(0)
|
||||||
|
, _boundaryPosition(0)
|
||||||
|
, _itemStartIndex(0)
|
||||||
|
, _itemSize(0)
|
||||||
|
, _itemName()
|
||||||
|
, _itemFilename()
|
||||||
|
, _itemType()
|
||||||
|
, _itemValue()
|
||||||
|
, _itemBuffer(0)
|
||||||
|
, _itemBufferIndex(0)
|
||||||
|
, _itemIsFile(false)
|
||||||
|
{
|
||||||
|
c->onError([](void *r, AsyncClient* c, err_t error){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
|
||||||
|
c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this);
|
||||||
|
c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); }, this);
|
||||||
|
c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this);
|
||||||
|
c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this);
|
||||||
|
c->onPoll([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onPoll(); }, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerRequest::~AsyncWebServerRequest(){
|
||||||
|
while(_headers != NULL){
|
||||||
|
AsyncWebHeader *h = _headers;
|
||||||
|
_headers = h->next;
|
||||||
|
delete h;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(_params != NULL){
|
||||||
|
AsyncWebParameter *p = _params;
|
||||||
|
_params = p->next;
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
|
||||||
|
_interestingHeaders->free();
|
||||||
|
delete _interestingHeaders;
|
||||||
|
|
||||||
|
if(_response != NULL){
|
||||||
|
delete _response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||||
|
//os_printf("r:%u\n", len);
|
||||||
|
if(_parseState < PARSE_REQ_BODY){
|
||||||
|
size_t i;
|
||||||
|
for(i=0; i<len; i++){
|
||||||
|
if(_parseState < PARSE_REQ_BODY)
|
||||||
|
_parseByte(((uint8_t*)buf)[i]);
|
||||||
|
else
|
||||||
|
return _onData((void *)((uint8_t*)buf+i), len-i);
|
||||||
|
}
|
||||||
|
} else if(_parseState == PARSE_REQ_BODY){
|
||||||
|
if(_isMultipart){
|
||||||
|
size_t i;
|
||||||
|
for(i=0; i<len; i++){
|
||||||
|
_parseMultipartPostByte(((uint8_t*)buf)[i], i == len - 1);
|
||||||
|
_parsedLength++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(_parsedLength == 0){
|
||||||
|
if(_contentType == "application/x-www-form-urlencoded"){
|
||||||
|
_isPlainPost = true;
|
||||||
|
} else if(_contentType == "text/plain" && __is_param_char(((char*)buf)[0])){
|
||||||
|
size_t i = 0;
|
||||||
|
while (i<len && __is_param_char(((char*)buf)[i++]));
|
||||||
|
if(i < len && ((char*)buf)[i-1] == '='){
|
||||||
|
_isPlainPost = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!_isPlainPost) {
|
||||||
|
if(_handler) _handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
||||||
|
_parsedLength += len;
|
||||||
|
} else {
|
||||||
|
size_t i;
|
||||||
|
for(i=0; i<len; i++){
|
||||||
|
_parsedLength++;
|
||||||
|
_parsePlainPostChar(((uint8_t*)buf)[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_parsedLength == _contentLength){
|
||||||
|
_parseState = PARSE_REQ_END;
|
||||||
|
if(_handler) _handler->handleRequest(this);
|
||||||
|
else send(501);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_onPoll(){
|
||||||
|
//os_printf("p\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){
|
||||||
|
//os_printf("a:%u:%u\n", len, time);
|
||||||
|
if(_response != NULL){
|
||||||
|
if(!_response->_finished()){
|
||||||
|
_response->_ack(this, len, time);
|
||||||
|
} else {
|
||||||
|
AsyncWebServerResponse* r = _response;
|
||||||
|
_response = NULL;
|
||||||
|
delete r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_onError(err_t error){
|
||||||
|
os_printf("e:%d:%u\n", error, _client->state());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_onTimeout(uint32_t time){
|
||||||
|
os_printf("t:%u\n", time);
|
||||||
|
_client->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_onDisconnect(){
|
||||||
|
//os_printf("d\n");
|
||||||
|
_client->free();
|
||||||
|
delete _client;
|
||||||
|
_server->_handleDisconnect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){
|
||||||
|
if(_params == NULL)
|
||||||
|
_params = p;
|
||||||
|
else {
|
||||||
|
AsyncWebParameter *ps = _params;
|
||||||
|
while(ps->next != NULL) ps = ps->next;
|
||||||
|
ps->next = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_addGetParam(String param){
|
||||||
|
param = _urlDecode(param);
|
||||||
|
String name = param;
|
||||||
|
String value = "";
|
||||||
|
if(param.indexOf('=') > 0){
|
||||||
|
name = param.substring(0, param.indexOf('='));
|
||||||
|
value = param.substring(param.indexOf('=') + 1);
|
||||||
|
}
|
||||||
|
_addParam(new AsyncWebParameter(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::_parseReqHead(){
|
||||||
|
String m = _temp.substring(0, _temp.indexOf(' '));
|
||||||
|
if(m == "GET"){
|
||||||
|
_method = HTTP_GET;
|
||||||
|
} else if(m == "POST"){
|
||||||
|
_method = HTTP_POST;
|
||||||
|
} else if(m == "DELETE"){
|
||||||
|
_method = HTTP_DELETE;
|
||||||
|
} else if(m == "PUT"){
|
||||||
|
_method = HTTP_PUT;
|
||||||
|
} else if(m == "PATCH"){
|
||||||
|
_method = HTTP_PATCH;
|
||||||
|
} else if(m == "HEAD"){
|
||||||
|
_method = HTTP_HEAD;
|
||||||
|
} else if(m == "OPTIONS"){
|
||||||
|
_method = HTTP_OPTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
_temp = _temp.substring(_temp.indexOf(' ')+1);
|
||||||
|
String u = _temp.substring(0, _temp.indexOf(' '));
|
||||||
|
String g = String();
|
||||||
|
if(u.indexOf('?') > 0){
|
||||||
|
g = u.substring(u.indexOf('?') + 1);
|
||||||
|
u = u.substring(0, u.indexOf('?'));
|
||||||
|
}
|
||||||
|
_url = u;
|
||||||
|
if(g.length()){
|
||||||
|
while(true){
|
||||||
|
if(g.length() == 0)
|
||||||
|
break;
|
||||||
|
if(g.indexOf('&') > 0){
|
||||||
|
_addGetParam(g.substring(0, g.indexOf('&')));
|
||||||
|
g = g.substring(g.indexOf('&') + 1);
|
||||||
|
} else {
|
||||||
|
_addGetParam(g);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_temp = String();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::_parseReqHeader(){
|
||||||
|
if(_temp.indexOf(':')){
|
||||||
|
AsyncWebHeader *h = new AsyncWebHeader(_temp);
|
||||||
|
if(h->name() == "Host"){
|
||||||
|
_host = h->value();
|
||||||
|
delete h;
|
||||||
|
_server->_handleRequest(this);
|
||||||
|
} else if(h->name() == "Content-Type"){
|
||||||
|
if (h->value().startsWith("multipart/")){
|
||||||
|
_boundary = h->value().substring(h->value().indexOf('=')+1);
|
||||||
|
_contentType = h->value().substring(0, h->value().indexOf(';'));
|
||||||
|
_isMultipart = true;
|
||||||
|
} else {
|
||||||
|
_contentType = h->value();
|
||||||
|
}
|
||||||
|
delete h;
|
||||||
|
} else if(h->name() == "Content-Length"){
|
||||||
|
_contentLength = atoi(h->value().c_str());
|
||||||
|
delete h;
|
||||||
|
} else if(h->name() == "Expect" && h->value() == "100-continue"){
|
||||||
|
_expectingContinue = true;
|
||||||
|
delete h;
|
||||||
|
} else if(h->name() == "Authorization"){
|
||||||
|
if(h->value().startsWith("Basic")){
|
||||||
|
_authorization = h->value().substring(6);
|
||||||
|
}
|
||||||
|
delete h;
|
||||||
|
} else {
|
||||||
|
if(_interestingHeaders->contains(h->name()) || _interestingHeaders->contains("ANY")){
|
||||||
|
if(_headers == NULL)
|
||||||
|
_headers = h;
|
||||||
|
else {
|
||||||
|
AsyncWebHeader *hs = _headers;
|
||||||
|
while(hs->next != NULL) hs = hs->next;
|
||||||
|
hs->next = h;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
delete h;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
_temp = String();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){
|
||||||
|
if(data && (char)data != '\r' && (char)data != '\n' && (char)data != '&')
|
||||||
|
_temp += (char)data;
|
||||||
|
if(!data || (char)data == '\n' || (char)data == '&' || _parsedLength == _contentLength){
|
||||||
|
_temp = _urlDecode(_temp);
|
||||||
|
String name = _temp;
|
||||||
|
String value = "";
|
||||||
|
if(_temp.indexOf('=') > 0){
|
||||||
|
name = _temp.substring(0, _temp.indexOf('='));
|
||||||
|
value = _temp.substring(_temp.indexOf('=') + 1);
|
||||||
|
}
|
||||||
|
_addParam(new AsyncWebParameter(name, value, true));
|
||||||
|
_temp = String();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){
|
||||||
|
_itemBuffer[_itemBufferIndex++] = data;
|
||||||
|
if(last){
|
||||||
|
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false);
|
||||||
|
_itemBufferIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EXPECT_BOUNDARY,
|
||||||
|
PARSE_HEADERS,
|
||||||
|
WAIT_FOR_RETURN1,
|
||||||
|
EXPECT_FEED1,
|
||||||
|
EXPECT_DASH1,
|
||||||
|
EXPECT_DASH2,
|
||||||
|
BOUNDARY_OR_DATA,
|
||||||
|
DASH3_OR_RETURN2,
|
||||||
|
EXPECT_FEED2,
|
||||||
|
PARSING_FINISHED,
|
||||||
|
PARSE_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){
|
||||||
|
#define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0)
|
||||||
|
if(!_parsedLength){
|
||||||
|
_multiParseState = EXPECT_BOUNDARY;
|
||||||
|
_temp = String();
|
||||||
|
_itemName = String();
|
||||||
|
_itemFilename = String();
|
||||||
|
_itemType = String();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_multiParseState == EXPECT_BOUNDARY){
|
||||||
|
if(_parsedLength < 2 && data != '-'){
|
||||||
|
_multiParseState = PARSE_ERROR;
|
||||||
|
return;
|
||||||
|
} else if(_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data){
|
||||||
|
_multiParseState = PARSE_ERROR;
|
||||||
|
return;
|
||||||
|
} else if(_parsedLength - 2 == _boundary.length() && data != '\r'){
|
||||||
|
_multiParseState = PARSE_ERROR;
|
||||||
|
return;
|
||||||
|
} else if(_parsedLength - 3 == _boundary.length()){
|
||||||
|
if(data != '\n'){
|
||||||
|
_multiParseState = PARSE_ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_multiParseState = PARSE_HEADERS;
|
||||||
|
_itemIsFile = false;
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == PARSE_HEADERS){
|
||||||
|
if((char)data != '\r' && (char)data != '\n')
|
||||||
|
_temp += (char)data;
|
||||||
|
if((char)data == '\n'){
|
||||||
|
if(_temp.length()){
|
||||||
|
if(_temp.startsWith("Content-Type:")){
|
||||||
|
_itemType = _temp.substring(14);
|
||||||
|
_itemIsFile = true;
|
||||||
|
} else if(_temp.startsWith("Content-Disposition:")){
|
||||||
|
_temp = _temp.substring(_temp.indexOf(';') + 2);
|
||||||
|
while(_temp.indexOf(';') > 0){
|
||||||
|
String name = _temp.substring(0, _temp.indexOf('='));
|
||||||
|
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.indexOf(';') - 1);
|
||||||
|
if(name == "name"){
|
||||||
|
_itemName = nameVal;
|
||||||
|
} else if(name == "filename"){
|
||||||
|
_itemFilename = nameVal;
|
||||||
|
_itemIsFile = true;
|
||||||
|
}
|
||||||
|
_temp = _temp.substring(_temp.indexOf(';') + 2);
|
||||||
|
}
|
||||||
|
String name = _temp.substring(0, _temp.indexOf('='));
|
||||||
|
String nameVal = _temp.substring(_temp.indexOf('=') + 2, _temp.length() - 1);
|
||||||
|
if(name == "name"){
|
||||||
|
_itemName = nameVal;
|
||||||
|
} else if(name == "filename"){
|
||||||
|
_itemFilename = nameVal;
|
||||||
|
_itemIsFile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_temp = String();
|
||||||
|
} else {
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
//value starts from here
|
||||||
|
_itemSize = 0;
|
||||||
|
_itemStartIndex = _parsedLength;
|
||||||
|
_itemValue = String();
|
||||||
|
if(_itemIsFile){
|
||||||
|
if(_itemBuffer)
|
||||||
|
os_free(_itemBuffer);
|
||||||
|
_itemBuffer = (uint8_t*)os_malloc(1460);
|
||||||
|
_itemBufferIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == WAIT_FOR_RETURN1){
|
||||||
|
if(data != '\r'){
|
||||||
|
itemWriteByte(data);
|
||||||
|
} else {
|
||||||
|
_multiParseState = EXPECT_FEED1;
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == EXPECT_FEED1){
|
||||||
|
if(data != '\n'){
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
itemWriteByte('\r'); itemWriteByte(data);
|
||||||
|
} else {
|
||||||
|
_multiParseState = EXPECT_DASH1;
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == EXPECT_DASH1){
|
||||||
|
if(data != '-'){
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte(data);
|
||||||
|
} else {
|
||||||
|
_multiParseState = EXPECT_DASH2;
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == EXPECT_DASH2){
|
||||||
|
if(data != '-'){
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte(data);
|
||||||
|
} else {
|
||||||
|
_multiParseState = BOUNDARY_OR_DATA;
|
||||||
|
_boundaryPosition = 0;
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == BOUNDARY_OR_DATA){
|
||||||
|
if(_boundaryPosition < _boundary.length() && _boundary.c_str()[_boundaryPosition] != data){
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||||
|
uint8_t i;
|
||||||
|
for(i=0; i<_boundaryPosition; i++)
|
||||||
|
itemWriteByte(_boundary.c_str()[i]);
|
||||||
|
itemWriteByte(data);
|
||||||
|
} else if(_boundaryPosition == _boundary.length() - 1){
|
||||||
|
_multiParseState = DASH3_OR_RETURN2;
|
||||||
|
if(!_itemIsFile){
|
||||||
|
_addParam(new AsyncWebParameter(_itemName, _itemValue, true));
|
||||||
|
} else {
|
||||||
|
if(_itemSize){
|
||||||
|
if(_handler) _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, true);
|
||||||
|
_itemBufferIndex = 0;
|
||||||
|
_addParam(new AsyncWebParameter(_itemName, _itemFilename, true, true, _itemSize));
|
||||||
|
}
|
||||||
|
os_free(_itemBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_boundaryPosition++;
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == DASH3_OR_RETURN2){
|
||||||
|
if(data == '\r'){
|
||||||
|
_multiParseState = EXPECT_FEED2;
|
||||||
|
} else if(data == '-' && _contentLength == (_parsedLength + 4)){
|
||||||
|
_multiParseState = PARSING_FINISHED;
|
||||||
|
} else {
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||||
|
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
|
||||||
|
itemWriteByte(data);
|
||||||
|
}
|
||||||
|
} else if(_multiParseState == EXPECT_FEED2){
|
||||||
|
if(data == '\n'){
|
||||||
|
_multiParseState = PARSE_HEADERS;
|
||||||
|
_itemIsFile = false;
|
||||||
|
} else {
|
||||||
|
_multiParseState = WAIT_FOR_RETURN1;
|
||||||
|
itemWriteByte('\r'); itemWriteByte('\n'); itemWriteByte('-'); itemWriteByte('-');
|
||||||
|
uint8_t i; for(i=0; i<_boundary.length(); i++) itemWriteByte(_boundary.c_str()[i]);
|
||||||
|
itemWriteByte('\r'); itemWriteByte(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_parseLine(){
|
||||||
|
if(_parseState == PARSE_REQ_START){
|
||||||
|
if(!_temp.length()){
|
||||||
|
_parseState = PARSE_REQ_FAIL;
|
||||||
|
_client->close();
|
||||||
|
} else {
|
||||||
|
_parseReqHead();
|
||||||
|
_parseState = PARSE_REQ_HEADERS;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_parseState == PARSE_REQ_HEADERS){
|
||||||
|
if(!_temp.length()){
|
||||||
|
//end of headers
|
||||||
|
if(_expectingContinue){
|
||||||
|
const char * response = "HTTP/1.1 100 Continue\r\n\r\n";
|
||||||
|
_client->write(response, os_strlen(response));
|
||||||
|
}
|
||||||
|
if(_contentLength){
|
||||||
|
_parseState = PARSE_REQ_BODY;
|
||||||
|
} else {
|
||||||
|
_parseState = PARSE_REQ_END;
|
||||||
|
if(_handler) _handler->handleRequest(this);
|
||||||
|
else send(501);
|
||||||
|
}
|
||||||
|
} else _parseReqHeader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::_parseByte(uint8_t data){
|
||||||
|
if((char)data != '\r' && (char)data != '\n')
|
||||||
|
_temp += (char)data;
|
||||||
|
if((char)data == '\n')
|
||||||
|
_parseLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int AsyncWebServerRequest::headers(){
|
||||||
|
int i = 0;
|
||||||
|
AsyncWebHeader* h = _headers;
|
||||||
|
while(h != NULL){
|
||||||
|
i++; h = h->next;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::hasHeader(String name){
|
||||||
|
AsyncWebHeader* h = _headers;
|
||||||
|
while(h != NULL){
|
||||||
|
if(h->name() == name)
|
||||||
|
return true;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebHeader* AsyncWebServerRequest::getHeader(String name){
|
||||||
|
AsyncWebHeader* h = _headers;
|
||||||
|
while(h != NULL){
|
||||||
|
if(h->name() == name)
|
||||||
|
return h;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebHeader* AsyncWebServerRequest::getHeader(int num){
|
||||||
|
int i = 0;
|
||||||
|
AsyncWebHeader* h = _headers;
|
||||||
|
while(h != NULL){
|
||||||
|
if(num == i++)
|
||||||
|
return h;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int AsyncWebServerRequest::params(){
|
||||||
|
int i = 0;
|
||||||
|
AsyncWebParameter* h = _params;
|
||||||
|
while(h != NULL){
|
||||||
|
i++; h = h->next;
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::hasParam(String name, bool post, bool file){
|
||||||
|
AsyncWebParameter* h = _params;
|
||||||
|
while(h != NULL){
|
||||||
|
if(h->name() == name && h->isPost() == post && h->isFile() == file)
|
||||||
|
return true;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebParameter* AsyncWebServerRequest::getParam(String name, bool post, bool file){
|
||||||
|
AsyncWebParameter* h = _params;
|
||||||
|
while(h != NULL){
|
||||||
|
if(h->name() == name && h->isPost() == post && h->isFile() == file)
|
||||||
|
return h;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebParameter* AsyncWebServerRequest::getParam(int num){
|
||||||
|
int i = 0;
|
||||||
|
AsyncWebParameter* h = _params;
|
||||||
|
while(h != NULL){
|
||||||
|
if(num == i++)
|
||||||
|
return h;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::addInterestingHeader(String name){
|
||||||
|
if(!_interestingHeaders->contains(name)) _interestingHeaders->add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::send(AsyncWebServerResponse *response){
|
||||||
|
_response = response;
|
||||||
|
_response->_respond(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, String contentType, String content){
|
||||||
|
return new AsyncBasicResponse(code, contentType, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, String path, String contentType, bool download){
|
||||||
|
if(fs.exists(path) || (!download && fs.exists(path+".gz")))
|
||||||
|
return new AsyncFileResponse(fs, path, contentType, download);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){
|
||||||
|
return new AsyncStreamResponse(stream, contentType, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(String contentType, size_t len, AwsResponseFiller callback){
|
||||||
|
return new AsyncCallbackResponse(contentType, len, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::send(int code, String contentType, String content){
|
||||||
|
send(new AsyncBasicResponse(code, contentType, content));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::send(FS &fs, String path, String contentType, bool download){
|
||||||
|
if(fs.exists(path) || (!download && fs.exists(path+".gz"))){
|
||||||
|
send(new AsyncFileResponse(fs, path, contentType, download));
|
||||||
|
} else send(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){
|
||||||
|
send(new AsyncStreamResponse(stream, contentType, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::send(String contentType, size_t len, AwsResponseFiller callback){
|
||||||
|
send(new AsyncCallbackResponse(contentType, len, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::authenticate(const char * username, const char * password){
|
||||||
|
if(_authorization.length()){
|
||||||
|
char toencodeLen = strlen(username)+strlen(password)+1;
|
||||||
|
char *toencode = new char[toencodeLen];
|
||||||
|
if(toencode == NULL){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
|
||||||
|
if(encoded == NULL){
|
||||||
|
delete[] toencode;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sprintf(toencode, "%s:%s", username, password);
|
||||||
|
if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && _authorization.equals(encoded)){
|
||||||
|
delete[] toencode;
|
||||||
|
delete[] encoded;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delete[] toencode;
|
||||||
|
delete[] encoded;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::authenticate(const char * hash){
|
||||||
|
return (_authorization.length() && (_authorization == String(hash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::requestAuthentication(){
|
||||||
|
AsyncWebServerResponse * r = beginResponse(401);
|
||||||
|
r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
|
||||||
|
send(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::hasArg(const char* name){
|
||||||
|
AsyncWebParameter* h = _params;
|
||||||
|
while(h != NULL){
|
||||||
|
if(h->name() == String(name))
|
||||||
|
return true;
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::arg(const char* name){
|
||||||
|
AsyncWebParameter* h = _params;
|
||||||
|
while(h != NULL){
|
||||||
|
if(h->name() == String(name))
|
||||||
|
return h->value();
|
||||||
|
h = h->next;
|
||||||
|
}
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::arg(int i){
|
||||||
|
return getParam(i)->value();
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::argName(int i){
|
||||||
|
return getParam(i)->name();
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::header(const char* name){
|
||||||
|
AsyncWebHeader* h = getHeader(String(name));
|
||||||
|
if(h)
|
||||||
|
return h->value();
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::header(int i){
|
||||||
|
AsyncWebHeader* h = getHeader(i);
|
||||||
|
if(h)
|
||||||
|
return h->value();
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::headerName(int i){
|
||||||
|
AsyncWebHeader* h = getHeader(i);
|
||||||
|
if(h)
|
||||||
|
return h->name();
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AsyncWebServerRequest::hasHeader(const char* name){
|
||||||
|
return hasHeader(String(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String AsyncWebServerRequest::_urlDecode(const String& text){
|
||||||
|
String decoded = "";
|
||||||
|
char temp[] = "0x00";
|
||||||
|
unsigned int len = text.length();
|
||||||
|
unsigned int i = 0;
|
||||||
|
while (i < len)
|
||||||
|
{
|
||||||
|
char decodedChar;
|
||||||
|
char encodedChar = text.charAt(i++);
|
||||||
|
if ((encodedChar == '%') && (i + 1 < len))
|
||||||
|
{
|
||||||
|
temp[2] = text.charAt(i++);
|
||||||
|
temp[3] = text.charAt(i++);
|
||||||
|
|
||||||
|
decodedChar = strtol(temp, NULL, 16);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (encodedChar == '+')
|
||||||
|
{
|
||||||
|
decodedChar = ' ';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
decodedChar = encodedChar; // normal ascii char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decoded += decodedChar;
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue