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:
Hagai Shatz
2016-06-17 15:42:47 +01:00
committed by Me No Dev
parent 5bbc732028
commit b63a86eba8
4 changed files with 85 additions and 74 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea/

View File

@@ -81,10 +81,11 @@ class AsyncWebHeader {
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);
if(!data) return;
int index = data.indexOf(':');
if (index < 0) return;
_name = data.substring(0, index);
_value = data.substring(index + 2);
}
~AsyncWebHeader(){}
String name(){ return _name; }
@@ -149,7 +150,6 @@ class AsyncWebServerRequest {
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);

View File

@@ -102,12 +102,22 @@ AsyncWebServerRequest::~AsyncWebServerRequest(){
void AsyncWebServerRequest::_onData(void *buf, size_t 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);
// Find new line in buf
char *str = (char*)buf;
size_t i = 0;
for (; i < len; i++) if (str[i] == '\n') break;
if (i == len) { // No new line, just add the buffer in _temp
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){
if(_isMultipart){
@@ -144,7 +154,6 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len){
_parseState = PARSE_REQ_END;
if(_handler) _handler->handleRequest(this);
else send(501);
return;
}
}
}
@@ -201,16 +210,23 @@ 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);
int index = param.indexOf('=');
if(index > 0){
name = param.substring(0, index);
value = param.substring(index + 1);
}
_addParam(new AsyncWebParameter(name, value));
}
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"){
_method = HTTP_GET;
} else if(m == "POST"){
@@ -227,22 +243,22 @@ bool AsyncWebServerRequest::_parseReqHead(){
_method = HTTP_OPTIONS;
}
_temp = _temp.substring(_temp.indexOf(' ')+1);
String u = _temp.substring(0, _temp.indexOf(' '));
u = urlDecode(u);
String g = String();
if(u.indexOf('?') > 0){
g = u.substring(u.indexOf('?') + 1);
u = u.substring(0, u.indexOf('?'));
index = u.indexOf('?');
if(index > 0){
g = u.substring(index+1);
u = u.substring(0, index);
}
_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);
index = g.indexOf('&');
if(index > 0){
_addGetParam(g.substring(0, index));
g = g.substring(index+1);
} else {
_addGetParam(g);
break;
@@ -250,7 +266,6 @@ bool AsyncWebServerRequest::_parseReqHead(){
}
}
_temp = _temp.substring(_temp.indexOf(' ')+1);
if(_temp.startsWith("HTTP/1.1"))
_version = 1;
_temp = String();
@@ -258,36 +273,32 @@ bool AsyncWebServerRequest::_parseReqHead(){
}
bool AsyncWebServerRequest::_parseReqHeader(){
if(_temp.indexOf(':')){
AsyncWebHeader *h = new AsyncWebHeader(_temp);
if(h == NULL)
return false;
if(h->name() == "Host"){
_host = h->value();
delete h;
int index = _temp.indexOf(':');
if(index){
String name = _temp.substring(0, index);
String value = _temp.substring(index + 2);
if(name == "Host"){
_host = value;
_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(';'));
} else if(name == "Content-Type"){
if (value.startsWith("multipart/")){
_boundary = value.substring(value.indexOf('=')+1);
_contentType = value.substring(0, value.indexOf(';'));
_isMultipart = true;
} else {
_contentType = h->value();
_contentType = 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"){
} else if(name == "Content-Length"){
_contentLength = atoi(value.c_str());
} else if(name == "Expect" && value == "100-continue"){
_expectingContinue = true;
delete h;
} else if(h->name() == "Authorization"){
if(h->value().startsWith("Basic")){
_authorization = h->value().substring(6);
} else if(name == "Authorization"){
if(value.startsWith("Basic")){
_authorization = value.substring(6);
}
delete h;
} 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)
_headers = h;
else {
@@ -295,10 +306,8 @@ bool AsyncWebServerRequest::_parseReqHeader(){
while(hs->next != NULL) hs = hs->next;
hs->next = h;
}
} else
delete h;
}
}
}
_temp = String();
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 i = 0;
@@ -793,26 +794,24 @@ bool AsyncWebServerRequest::hasHeader(const char* name){
String AsyncWebServerRequest::urlDecode(const String& text){
String decoded = "";
char temp[] = "0x00";
unsigned int len = text.length();
unsigned int i = 0;
String decoded = String();
decoded.reserve(len); // Allocate the string internal buffer - never longer from source text
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 {
if (encodedChar == '+'){
decodedChar = ' ';
} else {
decodedChar = encodedChar; // normal ascii char
}
decodedChar = encodedChar; // normal ascii char
}
decoded += decodedChar;
decoded.concat(decodedChar);
}
return decoded;
}

View File

@@ -119,21 +119,31 @@ String AsyncWebServerResponse::_assembleHead(uint8_t version){
if(_chunked)
addHeader("Transfer-Encoding","chunked");
}
String out = "HTTP/1." + String(version) + " " + String(_code) + " " + _responseCodeToString(_code) + "\r\n";
if(_sendContentLength)
out += "Content-Length: " + String(_contentLength) + "\r\n";
String out = String();
int bufSize = 300;
char buf[bufSize];
if(_contentType.length())
out += "Content-Type: " + _contentType + "\r\n";
snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, _responseCodeToString(_code));
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;
while(_headers != NULL){
h = _headers;
_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;
}
out += "\r\n";
out.concat("\r\n");
_headLength = out.length();
return out;
}