forked from me-no-dev/ESPAsyncWebServer
Performance (#44)
* HTTP 302 and 304 Support Add support for http redirection (302) and http not modified (304) to reduce the load the server. server.redirect(“url”, “location”, exclude-ip) will respond with 302 to redirect the browser to a different url, this is useful for backward compatibility and to redirect call to CDN when not no AP mode. server.serveStatic has a new optional parameter to get the Last-Modified date for all files serve for this location, when the browser request have the same If-Modified-Since header value, the server respond with 304 code instead of serving the file. * First round of performance improvements. * Merge remote-tracking branch 'me-no-dev/master' into performance # Conflicts: # src/WebHandlerImpl.h # src/WebHandlers.cpp * use of sprintf * Remove sections not related.
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
.idea/
|
@@ -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);
|
||||||
|
@@ -102,12 +102,22 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
|
|||||||
|
|
||||||
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){
|
||||||
@@ -144,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,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"){
|
||||||
@@ -227,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;
|
||||||
@@ -250,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();
|
||||||
@@ -258,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 {
|
||||||
@@ -295,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;
|
||||||
@@ -532,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;
|
||||||
@@ -793,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;
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user