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 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);

View File

@@ -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;
} }

View File

@@ -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;
} }