forked from me-no-dev/ESPAsyncWebServer
Merge remote-tracking branch 'me-no-dev/master'
This commit is contained in:
@@ -81,10 +81,11 @@ class AsyncWebHeader {
|
|||||||
|
|
||||||
AsyncWebHeader(String name, String value): _name(name), _value(value), next(NULL){}
|
AsyncWebHeader(String name, String value): _name(name), _value(value), next(NULL){}
|
||||||
AsyncWebHeader(String data): _name(), _value(), next(NULL){
|
AsyncWebHeader(String data): _name(), _value(), next(NULL){
|
||||||
if(!data || !data.length() || data.indexOf(':') < 0)
|
if(!data) return;
|
||||||
return;
|
int index = data.indexOf(':');
|
||||||
_name = data.substring(0, data.indexOf(':'));
|
if (index < 0) return;
|
||||||
_value = data.substring(data.indexOf(':') + 2);
|
_name = data.substring(0, index);
|
||||||
|
_value = data.substring(index + 2);
|
||||||
}
|
}
|
||||||
~AsyncWebHeader(){}
|
~AsyncWebHeader(){}
|
||||||
String name(){ return _name; }
|
String name(){ return _name; }
|
||||||
@@ -149,7 +150,6 @@ class AsyncWebServerRequest {
|
|||||||
bool _parseReqHead();
|
bool _parseReqHead();
|
||||||
bool _parseReqHeader();
|
bool _parseReqHeader();
|
||||||
void _parseLine();
|
void _parseLine();
|
||||||
void _parseByte(uint8_t data);
|
|
||||||
void _parsePlainPostChar(uint8_t data);
|
void _parsePlainPostChar(uint8_t data);
|
||||||
void _parseMultipartPostByte(uint8_t data, bool last);
|
void _parseMultipartPostByte(uint8_t data, bool last);
|
||||||
void _addGetParam(String param);
|
void _addGetParam(String param);
|
||||||
@@ -160,6 +160,7 @@ class AsyncWebServerRequest {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
File _tempFile;
|
File _tempFile;
|
||||||
|
void *_tempObject;
|
||||||
AsyncWebServerRequest *next;
|
AsyncWebServerRequest *next;
|
||||||
|
|
||||||
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
|
AsyncWebServerRequest(AsyncWebServer*, AsyncClient*);
|
||||||
@@ -185,12 +186,14 @@ class AsyncWebServerRequest {
|
|||||||
void send(AsyncWebServerResponse *response);
|
void send(AsyncWebServerResponse *response);
|
||||||
void send(int code, String contentType=String(), String content=String());
|
void send(int code, String contentType=String(), String content=String());
|
||||||
void send(FS &fs, String path, String contentType=String(), bool download=false);
|
void send(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
|
void send(File content, String path, String contentType=String(), bool download=false);
|
||||||
void send(Stream &stream, String contentType, size_t len);
|
void send(Stream &stream, String contentType, size_t len);
|
||||||
void send(String contentType, size_t len, AwsResponseFiller callback);
|
void send(String contentType, size_t len, AwsResponseFiller callback);
|
||||||
void sendChunked(String contentType, AwsResponseFiller callback);
|
void sendChunked(String contentType, AwsResponseFiller callback);
|
||||||
|
|
||||||
AsyncWebServerResponse *beginResponse(int code, String contentType=String(), String content=String());
|
AsyncWebServerResponse *beginResponse(int code, String contentType=String(), String content=String());
|
||||||
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
|
AsyncWebServerResponse *beginResponse(File content, String path, String contentType=String(), bool download=false);
|
||||||
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len);
|
||||||
AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback);
|
AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback);
|
||||||
AsyncWebServerResponse *beginChunkedResponse(String contentType, AwsResponseFiller callback);
|
AsyncWebServerResponse *beginChunkedResponse(String contentType, AwsResponseFiller callback);
|
||||||
|
@@ -24,47 +24,24 @@
|
|||||||
|
|
||||||
#include "stddef.h"
|
#include "stddef.h"
|
||||||
|
|
||||||
class RedirectWebHandler: public AsyncWebHandler {
|
|
||||||
protected:
|
|
||||||
String _url;
|
|
||||||
String _location;
|
|
||||||
uint32_t _exclude_ip;
|
|
||||||
public:
|
|
||||||
RedirectWebHandler(const char* url, const char* location, uint32_t exclude_ip)
|
|
||||||
: _url(url), _location(location) { _exclude_ip = exclude_ip; }
|
|
||||||
bool canHandle(AsyncWebServerRequest *request);
|
|
||||||
void handleRequest(AsyncWebServerRequest *request);
|
|
||||||
};
|
|
||||||
|
|
||||||
class AsyncStaticWebHandler: public AsyncWebHandler {
|
class AsyncStaticWebHandler: public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
String _getPath(AsyncWebServerRequest *request);
|
bool _getFile(AsyncWebServerRequest *request);
|
||||||
|
bool _fileExists(AsyncWebServerRequest *request, const String path);
|
||||||
|
uint8_t _countBits(const uint8_t value);
|
||||||
protected:
|
protected:
|
||||||
FS _fs;
|
FS _fs;
|
||||||
String _uri;
|
String _uri;
|
||||||
String _path;
|
String _path;
|
||||||
String _cache_header;
|
String _cache_header;
|
||||||
String _modified_header;
|
bool _isDir;
|
||||||
bool _isFile;
|
bool _gzipFirst;
|
||||||
|
uint8_t _gzipStats;
|
||||||
|
uint8_t _fileStats;
|
||||||
public:
|
public:
|
||||||
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header, const char* modified_header)
|
AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header);
|
||||||
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header), _modified_header(modified_header) {
|
|
||||||
|
|
||||||
_isFile = _fs.exists(path) || _fs.exists((String(path)+".gz").c_str());
|
|
||||||
if (_uri != "/" && _uri.endsWith("/")) {
|
|
||||||
_uri = _uri.substring(0, _uri.length() - 1);
|
|
||||||
DEBUGF("[AsyncStaticWebHandler] _uri / removed\n");
|
|
||||||
}
|
|
||||||
if (_path != "/" && _path.endsWith("/")) {
|
|
||||||
_path = _path.substring(0, _path.length() - 1);
|
|
||||||
DEBUGF("[AsyncStaticWebHandler] _path / removed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
bool canHandle(AsyncWebServerRequest *request);
|
bool canHandle(AsyncWebServerRequest *request);
|
||||||
void handleRequest(AsyncWebServerRequest *request);
|
void handleRequest(AsyncWebServerRequest *request);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
class AsyncCallbackWebHandler: public AsyncWebHandler {
|
||||||
|
@@ -21,102 +21,121 @@
|
|||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
#include "WebHandlerImpl.h"
|
#include "WebHandlerImpl.h"
|
||||||
|
|
||||||
|
AsyncStaticWebHandler::AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
|
||||||
bool RedirectWebHandler::canHandle(AsyncWebServerRequest *request)
|
: _fs(fs), _uri(uri), _path(path), _cache_header(cache_header)
|
||||||
{
|
{
|
||||||
// We can redirect when the request url match and ip doesn't match
|
// Ensure leading '/'
|
||||||
if (request->url() == _url && _exclude_ip != request->client()->localIP()) {
|
if (_uri.length() == 0 || _uri[0] != '/') _uri = "/" + _uri;
|
||||||
DEBUGF("[RedirectWebHandler::canHandle] TRUE\n");
|
if (_path.length() == 0 || _path[0] != '/') _path = "/" + _path;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RedirectWebHandler::handleRequest(AsyncWebServerRequest *request)
|
// If uri or path ends with '/' we assume a hint that this is a directory to improve performance.
|
||||||
{
|
// However - if they both do not end '/' we, can't assume they are files, they can still be directory.
|
||||||
AsyncWebServerResponse *response = request->beginResponse(302);
|
bool isUriDir = _uri[_uri.length()-1] == '/';
|
||||||
response->addHeader("Location", _location);
|
bool isPathDir = _path[_path.length()-1] == '/';
|
||||||
request->send(response);
|
_isDir = isUriDir || isPathDir;
|
||||||
}
|
|
||||||
|
|
||||||
|
// If we serving directory - remove the trailing '/' so we can handle default file
|
||||||
|
// Notice that root will be "" not "/"
|
||||||
|
if (_isDir && isUriDir) _uri = _uri.substring(0, _uri.length()-1);
|
||||||
|
if (_isDir && isPathDir) _path = _path.substring(0, _path.length()-1);
|
||||||
|
|
||||||
|
// Reset stats
|
||||||
|
_gzipFirst = false;
|
||||||
|
_gzipStats = 0;
|
||||||
|
_fileStats = 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
|
bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
if (request->method() != HTTP_GET) {
|
if (request->method() == HTTP_GET &&
|
||||||
return false;
|
request->url().startsWith(_uri) &&
|
||||||
}
|
_getFile(request)) {
|
||||||
if ((_isFile && request->url() != _uri) ) {
|
|
||||||
return false;
|
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
||||||
}
|
return true;
|
||||||
// if the root of the request matches the _uri then it checks to see if there is a file it can handle.
|
|
||||||
if (request->url().startsWith(_uri)) {
|
|
||||||
String path = _getPath(request);
|
|
||||||
if (_fs.exists(path) || _fs.exists(path + ".gz")) {
|
|
||||||
if (_modified_header.length() != 0) {
|
|
||||||
request->addInterestingHeader("If-Modified-Since");
|
|
||||||
}
|
|
||||||
DEBUGF("[AsyncStaticWebHandler::canHandle] TRUE\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String AsyncStaticWebHandler::_getPath(AsyncWebServerRequest *request)
|
bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
|
// Remove the found uri
|
||||||
|
String path = request->url().substring(_uri.length());
|
||||||
|
|
||||||
String path = request->url();
|
// We can skip the file check if we serving a directory and (we have full match or we end with '/')
|
||||||
DEBUGF("[AsyncStaticWebHandler::_getPath]\n");
|
bool canSkipFileCheck = _isDir && (path.length() == 0 || path[path.length()-1] == '/');
|
||||||
DEBUGF(" [stored] _uri = %s, _path = %s\n" , _uri.c_str(), _path.c_str() ) ;
|
|
||||||
DEBUGF(" [request] url = %s\n", request->url().c_str() );
|
|
||||||
|
|
||||||
if (!_isFile) {
|
path = _path + path;
|
||||||
DEBUGF(" _isFile = false\n");
|
|
||||||
String baserequestUrl = request->url().substring(_uri.length()); // this is the request - stored _uri... /espman/
|
|
||||||
DEBUGF(" baserequestUrl = %s\n", baserequestUrl.c_str());
|
|
||||||
|
|
||||||
if (!baserequestUrl.length()) {
|
// Do we have a file or .gz file
|
||||||
baserequestUrl += "/";
|
if (!canSkipFileCheck && _fileExists(request, path))
|
||||||
}
|
return true;
|
||||||
|
|
||||||
path = _path + baserequestUrl;
|
// Try to add default page, ensure there is a trailing '/' ot the path.
|
||||||
DEBUGF(" path = path + baserequestUrl, path = %s\n", path.c_str());
|
if (path.length() == 0 || path[path.length()-1] != '/')
|
||||||
|
path += "/";
|
||||||
|
path += "index.htm";
|
||||||
|
|
||||||
if (path.endsWith("/")) {
|
return _fileExists(request, path);
|
||||||
DEBUGF(" 3 path ends with / : path = index.htm \n");
|
|
||||||
path += "index.htm";
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
path = _path;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGF(" final path = %s\n", path.c_str());
|
|
||||||
DEBUGF("[AsyncStaticWebHandler::_getPath] END\n\n");
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AsyncStaticWebHandler::_fileExists(AsyncWebServerRequest *request, const String path)
|
||||||
|
{
|
||||||
|
bool fileFound = false;
|
||||||
|
bool gzipFound = false;
|
||||||
|
|
||||||
|
String gzip = path + ".gz";
|
||||||
|
|
||||||
|
if (_gzipFirst) {
|
||||||
|
request->_tempFile = _fs.open(gzip, "r");
|
||||||
|
gzipFound = request->_tempFile == true;
|
||||||
|
if (!gzipFound){
|
||||||
|
request->_tempFile = _fs.open(path, "r");
|
||||||
|
fileFound = request->_tempFile == true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
request->_tempFile = _fs.open(path, "r");
|
||||||
|
fileFound = request->_tempFile == true;
|
||||||
|
if (!fileFound){
|
||||||
|
request->_tempFile = _fs.open(gzip, "r");
|
||||||
|
gzipFound = request->_tempFile == true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool found = fileFound || gzipFound;
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
size_t plen = path.length();
|
||||||
|
char * _tempPath = (char*)malloc(plen+1);
|
||||||
|
snprintf(_tempPath, plen+1, "%s", path.c_str());
|
||||||
|
request->_tempObject = (void*)_tempPath;
|
||||||
|
_gzipStats = (_gzipStats << 1) + gzipFound ? 1 : 0;
|
||||||
|
_fileStats = (_fileStats << 1) + fileFound ? 1 : 0;
|
||||||
|
_gzipFirst = _countBits(_gzipStats) > _countBits(_fileStats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t AsyncStaticWebHandler::_countBits(const uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t w = value;
|
||||||
|
uint8_t n;
|
||||||
|
for (n=0; w!=0; n++) w&=w-1;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
void AsyncStaticWebHandler::handleRequest(AsyncWebServerRequest *request)
|
||||||
{
|
{
|
||||||
|
if (request->_tempFile == true) {
|
||||||
String path = _getPath(request);
|
AsyncWebServerResponse * response = new AsyncFileResponse(request->_tempFile, String((char*)request->_tempObject));
|
||||||
|
free(request->_tempObject);
|
||||||
if (_fs.exists(path) || _fs.exists(path + ".gz")) {
|
request->_tempObject = NULL;
|
||||||
if (_modified_header.length() != 0 && _modified_header == request->header("If-Modified-Since")) {
|
if (_cache_header.length() != 0)
|
||||||
request->send(304); // Sed not modified
|
response->addHeader("Cache-Control", _cache_header);
|
||||||
} else {
|
request->send(response);
|
||||||
AsyncWebServerResponse * response = request->beginResponse(_fs, path);
|
|
||||||
if (_modified_header.length() !=0)
|
|
||||||
response->addHeader("Last-Modified", _modified_header);
|
|
||||||
if (_cache_header.length() != 0)
|
|
||||||
response->addHeader("Cache-Control", _cache_header);
|
|
||||||
request->send(response);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
request->send(404);
|
request->send(404);
|
||||||
}
|
}
|
||||||
path = String();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -63,6 +63,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c)
|
|||||||
, _itemBuffer(0)
|
, _itemBuffer(0)
|
||||||
, _itemBufferIndex(0)
|
, _itemBufferIndex(0)
|
||||||
, _itemIsFile(false)
|
, _itemIsFile(false)
|
||||||
|
, _tempObject(NULL)
|
||||||
, next(NULL)
|
, next(NULL)
|
||||||
{
|
{
|
||||||
c->onError([](void *r, AsyncClient* c, int8_t error){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
|
c->onError([](void *r, AsyncClient* c, int8_t error){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this);
|
||||||
@@ -93,16 +94,30 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
|
|||||||
delete _response;
|
delete _response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_tempObject != NULL){
|
||||||
|
free(_tempObject);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
||||||
if(_parseState < PARSE_REQ_BODY){
|
if(_parseState < PARSE_REQ_BODY){
|
||||||
size_t i;
|
// Find new line in buf
|
||||||
for(i=0; i<len; i++){
|
char *str = (char*)buf;
|
||||||
if(_parseState < PARSE_REQ_BODY)
|
size_t i = 0;
|
||||||
_parseByte(((uint8_t*)buf)[i]);
|
for (; i < len; i++) if (str[i] == '\n') break;
|
||||||
else
|
if (i == len) { // No new line, just add the buffer in _temp
|
||||||
return _onData((void *)((uint8_t*)buf+i), len-i);
|
char ch = str[len-1];
|
||||||
|
str[len-1] = 0;
|
||||||
|
_temp.reserve(_temp.length()+len);
|
||||||
|
_temp.concat(str);
|
||||||
|
_temp.concat(ch);
|
||||||
|
} else { // Found new line - extract it and parse
|
||||||
|
str[i] = 0; // Terminate the string at the end of the line.
|
||||||
|
_temp.concat(str);
|
||||||
|
_temp.trim();
|
||||||
|
_parseLine();
|
||||||
|
if (++i < len) _onData(buf+i, len-i); // Still have more buffer to process
|
||||||
}
|
}
|
||||||
} else if(_parseState == PARSE_REQ_BODY){
|
} else if(_parseState == PARSE_REQ_BODY){
|
||||||
if(_isMultipart){
|
if(_isMultipart){
|
||||||
@@ -139,7 +154,6 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
|
|||||||
_parseState = PARSE_REQ_END;
|
_parseState = PARSE_REQ_END;
|
||||||
if(_handler) _handler->handleRequest(this);
|
if(_handler) _handler->handleRequest(this);
|
||||||
else send(501);
|
else send(501);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,16 +210,23 @@ void AsyncWebServerRequest::_addGetParam(String param){
|
|||||||
param = urlDecode(param);
|
param = urlDecode(param);
|
||||||
String name = param;
|
String name = param;
|
||||||
String value = "";
|
String value = "";
|
||||||
if(param.indexOf('=') > 0){
|
int index = param.indexOf('=');
|
||||||
name = param.substring(0, param.indexOf('='));
|
if(index > 0){
|
||||||
value = param.substring(param.indexOf('=') + 1);
|
name = param.substring(0, index);
|
||||||
|
value = param.substring(index + 1);
|
||||||
}
|
}
|
||||||
_addParam(new AsyncWebParameter(name, value));
|
_addParam(new AsyncWebParameter(name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool AsyncWebServerRequest::_parseReqHead(){
|
bool AsyncWebServerRequest::_parseReqHead(){
|
||||||
String m = _temp.substring(0, _temp.indexOf(' '));
|
// Split the head into method, url and version
|
||||||
|
int index = _temp.indexOf(' ');
|
||||||
|
String m = _temp.substring(0, index);
|
||||||
|
index = _temp.indexOf(' ', index+1);
|
||||||
|
String u = _temp.substring(m.length()+1, index);
|
||||||
|
_temp = _temp.substring(index+1);
|
||||||
|
|
||||||
if(m == "GET"){
|
if(m == "GET"){
|
||||||
_method = HTTP_GET;
|
_method = HTTP_GET;
|
||||||
} else if(m == "POST"){
|
} else if(m == "POST"){
|
||||||
@@ -222,22 +243,22 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
|||||||
_method = HTTP_OPTIONS;
|
_method = HTTP_OPTIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
_temp = _temp.substring(_temp.indexOf(' ')+1);
|
|
||||||
String u = _temp.substring(0, _temp.indexOf(' '));
|
|
||||||
u = urlDecode(u);
|
u = urlDecode(u);
|
||||||
String g = String();
|
String g = String();
|
||||||
if(u.indexOf('?') > 0){
|
index = u.indexOf('?');
|
||||||
g = u.substring(u.indexOf('?') + 1);
|
if(index > 0){
|
||||||
u = u.substring(0, u.indexOf('?'));
|
g = u.substring(index+1);
|
||||||
|
u = u.substring(0, index);
|
||||||
}
|
}
|
||||||
_url = u;
|
_url = u;
|
||||||
if(g.length()){
|
if(g.length()){
|
||||||
while(true){
|
while(true){
|
||||||
if(g.length() == 0)
|
if(g.length() == 0)
|
||||||
break;
|
break;
|
||||||
if(g.indexOf('&') > 0){
|
index = g.indexOf('&');
|
||||||
_addGetParam(g.substring(0, g.indexOf('&')));
|
if(index > 0){
|
||||||
g = g.substring(g.indexOf('&') + 1);
|
_addGetParam(g.substring(0, index));
|
||||||
|
g = g.substring(index+1);
|
||||||
} else {
|
} else {
|
||||||
_addGetParam(g);
|
_addGetParam(g);
|
||||||
break;
|
break;
|
||||||
@@ -245,7 +266,6 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_temp = _temp.substring(_temp.indexOf(' ')+1);
|
|
||||||
if(_temp.startsWith("HTTP/1.1"))
|
if(_temp.startsWith("HTTP/1.1"))
|
||||||
_version = 1;
|
_version = 1;
|
||||||
_temp = String();
|
_temp = String();
|
||||||
@@ -253,36 +273,32 @@ bool AsyncWebServerRequest::_parseReqHead(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebServerRequest::_parseReqHeader(){
|
bool AsyncWebServerRequest::_parseReqHeader(){
|
||||||
if(_temp.indexOf(':')){
|
int index = _temp.indexOf(':');
|
||||||
AsyncWebHeader *h = new AsyncWebHeader(_temp);
|
if(index){
|
||||||
if(h == NULL)
|
String name = _temp.substring(0, index);
|
||||||
return false;
|
String value = _temp.substring(index + 2);
|
||||||
if(h->name() == "Host"){
|
if(name == "Host"){
|
||||||
_host = h->value();
|
_host = value;
|
||||||
delete h;
|
|
||||||
_server->_handleRequest(this);
|
_server->_handleRequest(this);
|
||||||
} else if(h->name() == "Content-Type"){
|
} else if(name == "Content-Type"){
|
||||||
if (h->value().startsWith("multipart/")){
|
if (value.startsWith("multipart/")){
|
||||||
_boundary = h->value().substring(h->value().indexOf('=')+1);
|
_boundary = value.substring(value.indexOf('=')+1);
|
||||||
_contentType = h->value().substring(0, h->value().indexOf(';'));
|
_contentType = value.substring(0, value.indexOf(';'));
|
||||||
_isMultipart = true;
|
_isMultipart = true;
|
||||||
} else {
|
} else {
|
||||||
_contentType = h->value();
|
_contentType = value;
|
||||||
}
|
}
|
||||||
delete h;
|
} else if(name == "Content-Length"){
|
||||||
} else if(h->name() == "Content-Length"){
|
_contentLength = atoi(value.c_str());
|
||||||
_contentLength = atoi(h->value().c_str());
|
} else if(name == "Expect" && value == "100-continue"){
|
||||||
delete h;
|
|
||||||
} else if(h->name() == "Expect" && h->value() == "100-continue"){
|
|
||||||
_expectingContinue = true;
|
_expectingContinue = true;
|
||||||
delete h;
|
} else if(name == "Authorization"){
|
||||||
} else if(h->name() == "Authorization"){
|
if(value.startsWith("Basic")){
|
||||||
if(h->value().startsWith("Basic")){
|
_authorization = value.substring(6);
|
||||||
_authorization = h->value().substring(6);
|
|
||||||
}
|
}
|
||||||
delete h;
|
|
||||||
} else {
|
} else {
|
||||||
if(_interestingHeaders->contains(h->name()) || _interestingHeaders->contains("ANY")){
|
if(_interestingHeaders->contains(name) || _interestingHeaders->contains("ANY")){
|
||||||
|
AsyncWebHeader *h = new AsyncWebHeader(name, value);
|
||||||
if(_headers == NULL)
|
if(_headers == NULL)
|
||||||
_headers = h;
|
_headers = h;
|
||||||
else {
|
else {
|
||||||
@@ -290,10 +306,8 @@ bool AsyncWebServerRequest::_parseReqHeader(){
|
|||||||
while(hs->next != NULL) hs = hs->next;
|
while(hs->next != NULL) hs = hs->next;
|
||||||
hs->next = h;
|
hs->next = h;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
delete h;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
_temp = String();
|
_temp = String();
|
||||||
return true;
|
return true;
|
||||||
@@ -527,14 +541,6 @@ void AsyncWebServerRequest::_parseLine(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 AsyncWebServerRequest::headers(){
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -646,6 +652,12 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, String pat
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(File content, String path, String contentType, bool download){
|
||||||
|
if(content == true)
|
||||||
|
return new AsyncFileResponse(content, path, contentType, download);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){
|
AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){
|
||||||
return new AsyncStreamResponse(stream, contentType, len);
|
return new AsyncStreamResponse(stream, contentType, len);
|
||||||
}
|
}
|
||||||
@@ -674,6 +686,12 @@ void AsyncWebServerRequest::send(FS &fs, String path, String contentType, bool d
|
|||||||
} else send(404);
|
} else send(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AsyncWebServerRequest::send(File content, String path, String contentType, bool download){
|
||||||
|
if(content == true){
|
||||||
|
send(beginResponse(content, path, contentType, download));
|
||||||
|
} else send(404);
|
||||||
|
}
|
||||||
|
|
||||||
void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){
|
void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){
|
||||||
send(beginResponse(stream, contentType, len));
|
send(beginResponse(stream, contentType, len));
|
||||||
}
|
}
|
||||||
@@ -776,26 +794,24 @@ bool AsyncWebServerRequest::hasHeader(const char* name){
|
|||||||
|
|
||||||
|
|
||||||
String AsyncWebServerRequest::urlDecode(const String& text){
|
String AsyncWebServerRequest::urlDecode(const String& text){
|
||||||
String decoded = "";
|
|
||||||
char temp[] = "0x00";
|
char temp[] = "0x00";
|
||||||
unsigned int len = text.length();
|
unsigned int len = text.length();
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
|
String decoded = String();
|
||||||
|
decoded.reserve(len); // Allocate the string internal buffer - never longer from source text
|
||||||
while (i < len){
|
while (i < len){
|
||||||
char decodedChar;
|
char decodedChar;
|
||||||
char encodedChar = text.charAt(i++);
|
char encodedChar = text.charAt(i++);
|
||||||
if ((encodedChar == '%') && (i + 1 < len)){
|
if ((encodedChar == '%') && (i + 1 < len)){
|
||||||
temp[2] = text.charAt(i++);
|
temp[2] = text.charAt(i++);
|
||||||
temp[3] = text.charAt(i++);
|
temp[3] = text.charAt(i++);
|
||||||
|
|
||||||
decodedChar = strtol(temp, NULL, 16);
|
decodedChar = strtol(temp, NULL, 16);
|
||||||
|
} else if (encodedChar == '+') {
|
||||||
|
decodedChar = ' ';
|
||||||
} else {
|
} else {
|
||||||
if (encodedChar == '+'){
|
decodedChar = encodedChar; // normal ascii char
|
||||||
decodedChar = ' ';
|
|
||||||
} else {
|
|
||||||
decodedChar = encodedChar; // normal ascii char
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
decoded += decodedChar;
|
decoded.concat(decodedChar);
|
||||||
}
|
}
|
||||||
return decoded;
|
return decoded;
|
||||||
}
|
}
|
||||||
|
@@ -48,6 +48,7 @@ class AsyncFileResponse: public AsyncAbstractResponse {
|
|||||||
void _setContentType(String path);
|
void _setContentType(String path);
|
||||||
public:
|
public:
|
||||||
AsyncFileResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
AsyncFileResponse(FS &fs, String path, String contentType=String(), bool download=false);
|
||||||
|
AsyncFileResponse(File content, String path, String contentType=String(), bool download=false);
|
||||||
~AsyncFileResponse();
|
~AsyncFileResponse();
|
||||||
bool _sourceValid(){ return !!(_content); }
|
bool _sourceValid(){ return !!(_content); }
|
||||||
size_t _fillBuffer(uint8_t *buf, size_t maxLen);
|
size_t _fillBuffer(uint8_t *buf, size_t maxLen);
|
||||||
|
@@ -119,21 +119,31 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
|
|||||||
if(_chunked)
|
if(_chunked)
|
||||||
addHeader("Transfer-Encoding","chunked");
|
addHeader("Transfer-Encoding","chunked");
|
||||||
}
|
}
|
||||||
String out = "HTTP/1." + String(version) + " " + String(_code) + " " + _responseCodeToString(_code) + "\r\n";
|
String out = String();
|
||||||
if(_sendContentLength)
|
int bufSize = 300;
|
||||||
out += "Content-Length: " + String(_contentLength) + "\r\n";
|
char buf[bufSize];
|
||||||
|
|
||||||
if(_contentType.length())
|
snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
|
||||||
out += "Content-Type: " + _contentType + "\r\n";
|
out.concat(buf);
|
||||||
|
|
||||||
|
if(_sendContentLength) {
|
||||||
|
snprintf(buf, bufSize, "Content-Length: %d\r\n", _contentLength);
|
||||||
|
out.concat(buf);
|
||||||
|
}
|
||||||
|
if(_contentType.length()) {
|
||||||
|
snprintf(buf, bufSize, "Content-Type: %s\r\n", _contentType.c_str());
|
||||||
|
out.concat(buf);
|
||||||
|
}
|
||||||
|
|
||||||
AsyncWebHeader *h;
|
AsyncWebHeader *h;
|
||||||
while(_headers != NULL){
|
while(_headers != NULL){
|
||||||
h = _headers;
|
h = _headers;
|
||||||
_headers = _headers->next;
|
_headers = _headers->next;
|
||||||
out += h->toString();
|
snprintf(buf, bufSize, "%s: %s\r\n", h->name().c_str(), h->value().c_str());
|
||||||
|
out.concat(buf);
|
||||||
delete h;
|
delete h;
|
||||||
}
|
}
|
||||||
out += "\r\n";
|
out.concat("\r\n");
|
||||||
_headLength = out.length();
|
_headLength = out.length();
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -158,7 +168,6 @@ AsyncBasicResponse::AsyncBasicResponse(int code, String contentType, String cont
|
|||||||
_contentType = "text/plain";
|
_contentType = "text/plain";
|
||||||
}
|
}
|
||||||
addHeader("Connection","close");
|
addHeader("Connection","close");
|
||||||
addHeader("Access-Control-Allow-Origin","*");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){
|
||||||
@@ -228,7 +237,6 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint
|
|||||||
|
|
||||||
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){
|
||||||
addHeader("Connection","close");
|
addHeader("Connection","close");
|
||||||
addHeader("Access-Control-Allow-Origin","*");
|
|
||||||
_head = _assembleHead(request->version());
|
_head = _assembleHead(request->version());
|
||||||
_state = RESPONSE_HEADERS;
|
_state = RESPONSE_HEADERS;
|
||||||
_ack(request, 0, 0);
|
_ack(request, 0, 0);
|
||||||
@@ -333,7 +341,6 @@ void AsyncFileResponse::_setContentType(String path){
|
|||||||
if (path.endsWith(".html")) _contentType = "text/html";
|
if (path.endsWith(".html")) _contentType = "text/html";
|
||||||
else if (path.endsWith(".htm")) _contentType = "text/html";
|
else if (path.endsWith(".htm")) _contentType = "text/html";
|
||||||
else if (path.endsWith(".css")) _contentType = "text/css";
|
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(".js")) _contentType = "application/javascript";
|
||||||
else if (path.endsWith(".png")) _contentType = "image/png";
|
else if (path.endsWith(".png")) _contentType = "image/png";
|
||||||
else if (path.endsWith(".gif")) _contentType = "image/gif";
|
else if (path.endsWith(".gif")) _contentType = "image/gif";
|
||||||
@@ -344,23 +351,64 @@ void AsyncFileResponse::_setContentType(String path){
|
|||||||
else if (path.endsWith(".pdf")) _contentType = "application/pdf";
|
else if (path.endsWith(".pdf")) _contentType = "application/pdf";
|
||||||
else if (path.endsWith(".zip")) _contentType = "application/zip";
|
else if (path.endsWith(".zip")) _contentType = "application/zip";
|
||||||
else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
|
else if(path.endsWith(".gz")) _contentType = "application/x-gzip";
|
||||||
else _contentType = "application/octet-stream";
|
else _contentType = "text/plain";
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncFileResponse::AsyncFileResponse(FS &fs, String path, String contentType, bool download){
|
AsyncFileResponse::AsyncFileResponse(FS &fs, String path, String contentType, bool download){
|
||||||
_code = 200;
|
_code = 200;
|
||||||
_path = path;
|
_path = path;
|
||||||
|
_content = fs.open(_path, "r");
|
||||||
|
_contentLength = _content.size();
|
||||||
|
|
||||||
if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
|
if(!download && !fs.exists(_path) && fs.exists(_path+".gz")){
|
||||||
_path = _path+".gz";
|
_path = _path+".gz";
|
||||||
addHeader("Content-Encoding", "gzip");
|
addHeader("Content-Encoding", "gzip");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(download)
|
if(contentType == "")
|
||||||
_contentType = "application/octet-stream";
|
|
||||||
else
|
|
||||||
_setContentType(path);
|
_setContentType(path);
|
||||||
_content = fs.open(_path, "r");
|
else
|
||||||
|
_contentType = contentType;
|
||||||
|
|
||||||
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
|
char buf[26+path.length()-filenameStart];
|
||||||
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
|
if(download) {
|
||||||
|
// set filename and force download
|
||||||
|
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||||
|
} else {
|
||||||
|
// set filename and force rendering
|
||||||
|
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||||
|
}
|
||||||
|
addHeader("Content-Disposition", buf);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncFileResponse::AsyncFileResponse(File content, String path, String contentType, bool download){
|
||||||
|
_code = 200;
|
||||||
|
_path = path;
|
||||||
|
_content = content;
|
||||||
_contentLength = _content.size();
|
_contentLength = _content.size();
|
||||||
|
|
||||||
|
if(!download && String(_content.name()).endsWith(".gz"))
|
||||||
|
addHeader("Content-Encoding", "gzip");
|
||||||
|
|
||||||
|
if(contentType == "")
|
||||||
|
_setContentType(path);
|
||||||
|
else
|
||||||
|
_contentType = contentType;
|
||||||
|
|
||||||
|
int filenameStart = path.lastIndexOf('/') + 1;
|
||||||
|
char buf[26+path.length()-filenameStart];
|
||||||
|
char* filename = (char*)path.c_str() + filenameStart;
|
||||||
|
|
||||||
|
if(download) {
|
||||||
|
snprintf(buf, sizeof (buf), "attachment; filename=\"%s\"", filename);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof (buf), "inline; filename=\"%s\"", filename);
|
||||||
|
}
|
||||||
|
addHeader("Content-Disposition", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
size_t AsyncFileResponse::_fillBuffer(uint8_t *data, size_t len){
|
||||||
@@ -458,4 +506,3 @@ size_t AsyncResponseStream::write(const uint8_t *data, size_t len){
|
|||||||
size_t AsyncResponseStream::write(uint8_t data){
|
size_t AsyncResponseStream::write(uint8_t data){
|
||||||
return write(&data, 1);
|
return write(&data, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user