Skip to content

add begin(port) to esp8266webserver, move some strings to flash, some refactoring #4148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Feb 7, 2018
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
9845ea9
add begin(port) to esp8266webserver, move some strings to flash, some…
devyte Jan 12, 2018
3042bef
Merge branch 'master' into esp8266webserver
devyte Jan 17, 2018
57028d7
Merge branch 'master' into esp8266webserver
devyte Jan 17, 2018
4bb8915
Merge branch 'master' into esp8266webserver
devyte Jan 29, 2018
6c88328
Merge branch 'master' into esp8266webserver
devyte Jan 30, 2018
072b817
Merge remote-tracking branch 'origin' into esp8266webserver
devyte Jan 31, 2018
29ffa8e
Merge branch 'esp8266webserver' of https://github.com/devyte/Arduino …
devyte Jan 31, 2018
0ddebea
Moved more strings to flash, unified some strings
devyte Feb 4, 2018
55d531f
Merge remote-tracking branch 'upstream/master' into esp8266webserver
devyte Feb 5, 2018
f8a784e
move mimetable strings into a standalone file
devyte Feb 5, 2018
e744e60
more string moves to flash, remove duplicates, refactor of template m…
devyte Feb 5, 2018
624eab4
Merge branch 'master' into esp8266webserver
devyte Feb 5, 2018
febcfcb
Merge remote-tracking branch 'upstream/master' into esp8266webserver
devyte Feb 6, 2018
61f623d
Merge remote-tracking branch 'origin/esp8266webserver' into esp8266we…
devyte Feb 6, 2018
3b25df7
Reverted moving small string to flash (no heap advantage, reduces bin…
devyte Feb 7, 2018
92ba03b
Merge branch 'master' into esp8266webserver
devyte Feb 7, 2018
42796bb
Merge branch 'master' into esp8266webserver
devyte Feb 7, 2018
10072f0
Merge branch 'master' into esp8266webserver
devyte Feb 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 100 additions & 71 deletions libraries/ESP8266WebServer/src/ESP8266WebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,27 @@
#define DEBUG_OUTPUT Serial
#endif

const char * AUTHORIZATION_HEADER = "Authorization";
//const char * AUTHORIZATION_HEADER = "Authorization";
static const char AUTHORIZATION_HEADER[] PROGMEM = "Authorization";
static const char qop_auth[] PROGMEM = "qop=auth";
static const char WWW_Authenticate[] PROGMEM = "WWW-Authenticate";
static const char colon[] PROGMEM = ":";
static const char Content_Length[] PROGMEM = "Content-Length";


ESP8266WebServer::ESP8266WebServer(IPAddress addr, int port)
: _server(addr, port)
, _currentMethod(HTTP_ANY)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _currentHandler(0)
, _firstHandler(0)
, _lastHandler(0)
, _currentHandler(nullptr)
, _firstHandler(nullptr)
, _lastHandler(nullptr)
, _currentArgCount(0)
, _currentArgs(0)
, _currentArgs(nullptr)
, _headerKeysCount(0)
, _currentHeaders(0)
, _currentHeaders(nullptr)
, _contentLength(0)
, _chunked(false)
{
Expand All @@ -62,13 +68,13 @@ ESP8266WebServer::ESP8266WebServer(int port)
, _currentVersion(0)
, _currentStatus(HC_NONE)
, _statusChange(0)
, _currentHandler(0)
, _firstHandler(0)
, _lastHandler(0)
, _currentHandler(nullptr)
, _firstHandler(nullptr)
, _lastHandler(nullptr)
, _currentArgCount(0)
, _currentArgs(0)
, _currentArgs(nullptr)
, _headerKeysCount(0)
, _currentHeaders(0)
, _currentHeaders(nullptr)
, _contentLength(0)
, _chunked(false)
{
Expand All @@ -88,22 +94,26 @@ ESP8266WebServer::~ESP8266WebServer() {
}

void ESP8266WebServer::begin() {
_currentStatus = HC_NONE;
close();
_server.begin();
if(!_headerKeysCount)
collectHeaders(0, 0);
}

String ESP8266WebServer::_exractParam(String& authReq,const String& param,const char delimit){
void ESP8266WebServer::begin(uint16_t port) {
close();
_server.begin(port);
}

String ESP8266WebServer::_extractParam(String& authReq,const String& param,const char delimit){
int _begin = authReq.indexOf(param);
if (_begin==-1) return "";
if (_begin==-1)
return String();
return authReq.substring(_begin+param.length(),authReq.indexOf(delimit,_begin+param.length()));
}

bool ESP8266WebServer::authenticate(const char * username, const char * password){
if(hasHeader(AUTHORIZATION_HEADER)){
String authReq = header(AUTHORIZATION_HEADER);
if(authReq.startsWith("Basic")){
if(hasHeader(FPSTR(AUTHORIZATION_HEADER))) {
String authReq = header(FPSTR(AUTHORIZATION_HEADER));
if(authReq.startsWith(F("Basic"))){
authReq = authReq.substring(6);
authReq.trim();
char toencodeLen = strlen(username)+strlen(password)+1;
Expand All @@ -127,74 +137,74 @@ bool ESP8266WebServer::authenticate(const char * username, const char * password
}
delete[] toencode;
delete[] encoded;
}else if(authReq.startsWith("Digest")){
} else if(authReq.startsWith(F("Digest"))) {
authReq = authReq.substring(7);
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println(authReq);
#endif
String _username = _exractParam(authReq,"username=\"");
if((!_username.length())||_username!=String(username)){
String _username = _extractParam(authReq,F("username=\""));
if(!_username.length() || _username != String(username)) {
authReq = String();
return false;
}
// extracting required parameters for RFC 2069 simpler Digest
String _realm = _exractParam(authReq,"realm=\"");
String _nonce = _exractParam(authReq,"nonce=\"");
String _uri = _exractParam(authReq,"uri=\"");
String _response = _exractParam(authReq,"response=\"");
String _opaque = _exractParam(authReq,"opaque=\"");
String _realm = _extractParam(authReq, F("realm=\""));
String _nonce = _extractParam(authReq, F("nonce=\""));
String _uri = _extractParam(authReq, F("uri=\""));
String _response = _extractParam(authReq, F("response=\""));
String _opaque = _extractParam(authReq, F("opaque=\""));

if((!_realm.length())||(!_nonce.length())||(!_uri.length())||(!_response.length())||(!_opaque.length())){
if((!_realm.length()) || (!_nonce.length()) || (!_uri.length()) || (!_response.length()) || (!_opaque.length())) {
authReq = String();
return false;
}
if((_opaque!=_sopaque)||(_nonce!=_snonce)||(_realm!=_srealm)){
if((_opaque != _sopaque) || (_nonce != _snonce) || (_realm != _srealm)) {
authReq = String();
return false;
}
// parameters for the RFC 2617 newer Digest
String _nc,_cnonce;
if(authReq.indexOf("qop=auth") != -1){
_nc = _exractParam(authReq,"nc=",',');
_cnonce = _exractParam(authReq,"cnonce=\"");
if(authReq.indexOf(FPSTR(qop_auth)) != -1) {
_nc = _extractParam(authReq, F("nc="), ',');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this typo in the method name! _exractParam always gave me a headache!

_cnonce = _extractParam(authReq, F("cnonce=\""));
}
MD5Builder md5;
md5.begin();
md5.add(String(username)+":"+_realm+":"+String(password)); // md5 of the user:realm:user
md5.add(String(username) + ':' + _realm + ':' + String(password)); // md5 of the user:realm:user
md5.calculate();
String _H1 = md5.toString();
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("Hash of user:realm:pass=" + _H1);
#endif
md5.begin();
if(_currentMethod == HTTP_GET){
md5.add("GET:"+_uri);
md5.add(String(F("GET:")) + _uri);
}else if(_currentMethod == HTTP_POST){
md5.add("POST:"+_uri);
md5.add(String(F("POST:")) + _uri);
}else if(_currentMethod == HTTP_PUT){
md5.add("PUT:"+_uri);
md5.add(String(F("PUT:")) + _uri);
}else if(_currentMethod == HTTP_DELETE){
md5.add("DELETE:"+_uri);
md5.add(String(F("DELETE:")) + _uri);
}else{
md5.add("GET:"+_uri);
md5.add(String(F("GET:")) + _uri);
}
md5.calculate();
String _H2 = md5.toString();
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("Hash of GET:uri=" + _H2);
#endif
md5.begin();
if(authReq.indexOf("qop=auth") != -1){
md5.add(_H1+":"+_nonce+":"+_nc+":"+_cnonce+":auth:"+_H2);
if(authReq.indexOf(FPSTR(qop_auth)) != -1) {
md5.add(_H1 + FPSTR(colon) + _nonce + FPSTR(colon) + _nc + FPSTR(colon) + _cnonce + ':auth:' + _H2);
}else{
md5.add(_H1+":"+_nonce+":"+_H2);
md5.add(_H1 + FPSTR(colon) + _nonce + FPSTR(colon) + _H2);
}
md5.calculate();
String _responsecheck = md5.toString();
#ifdef DEBUG_ESP_HTTP_SERVER
DEBUG_OUTPUT.println("The Proper response=" +_responsecheck);
#endif
if(_response==_responsecheck){
if(_response == _responsecheck){
authReq = String();
return true;
}
Expand All @@ -204,29 +214,30 @@ bool ESP8266WebServer::authenticate(const char * username, const char * password
return false;
}

String ESP8266WebServer::_getRandomHexString(){
String ESP8266WebServer::_getRandomHexString() {
char buffer[33]; // buffer to hold 32 Hex Digit + /0
int i;
for(i=0;i<4;i++){
sprintf (buffer+(i*8), "%08x", RANDOM_REG32);
for(i = 0; i < 4; i++) {
sprintf (buffer + (i*8), "%08x", RANDOM_REG32);
}
return String(buffer);
}

void ESP8266WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg){
if(realm==NULL){
_srealm = "Login Required";
}else{
void ESP8266WebServer::requestAuthentication(HTTPAuthMethod mode, const char* realm, const String& authFailMsg) {
if(realm == NULL) {
_srealm = String(F("Login Required"));
} else {
_srealm = String(realm);
}
if(mode==BASIC_AUTH){
sendHeader("WWW-Authenticate", "Basic realm=\"" + _srealm + "\"");
}else{
if(mode == BASIC_AUTH) {
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String(F("\"")));
} else {
_snonce=_getRandomHexString();
_sopaque=_getRandomHexString();
sendHeader("WWW-Authenticate", "Digest realm=\"" +_srealm + "\", qop=\"auth\", nonce=\""+_snonce+"\", opaque=\""+_sopaque+"\"");
sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String(F("\"")));
}
send(401,"text/html",authFailMsg);
using namespace mime;
send(401, mimeTable[html].mimeType, authFailMsg);
}

void ESP8266WebServer::on(const String &uri, ESP8266WebServer::THandlerFunction handler) {
Expand Down Expand Up @@ -327,6 +338,9 @@ void ESP8266WebServer::handleClient() {

void ESP8266WebServer::close() {
_server.close();
_currentStatus = HC_NONE;
if(!_headerKeysCount)
collectHeaders(0, 0);
}

void ESP8266WebServer::stop() {
Expand All @@ -335,9 +349,9 @@ void ESP8266WebServer::stop() {

void ESP8266WebServer::sendHeader(const String& name, const String& value, bool first) {
String headerLine = name;
headerLine += ": ";
headerLine += F(": ");
headerLine += value;
headerLine += "\r\n";
headerLine += F("\r\n");

if (first) {
_responseHeaders = headerLine + _responseHeaders;
Expand All @@ -347,32 +361,33 @@ void ESP8266WebServer::sendHeader(const String& name, const String& value, bool
}
}

void ESP8266WebServer::setContentLength(size_t contentLength) {
void ESP8266WebServer::setContentLength(const size_t contentLength) {
_contentLength = contentLength;
}

void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
response = "HTTP/1."+String(_currentVersion)+" ";
response = String(F("HTTP/1.")) + String(_currentVersion) + ' ';
response += String(code);
response += " ";
response += ' ';
response += _responseCodeToString(code);
response += "\r\n";
response += String(F("\r\n"));

using namespace mime;
if (!content_type)
content_type = "text/html";
content_type = mimeTable[html].mimeType;

sendHeader("Content-Type", content_type, true);
sendHeader(String(F("Content-Type")), content_type, true);
if (_contentLength == CONTENT_LENGTH_NOT_SET) {
sendHeader("Content-Length", String(contentLength));
sendHeader(String(FPSTR(Content_Length)), String(contentLength));
} else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
sendHeader("Content-Length", String(_contentLength));
sendHeader(String(FPSTR(Content_Length)), String(_contentLength));
} else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
//let's do chunked
_chunked = true;
sendHeader("Accept-Ranges","none");
sendHeader("Transfer-Encoding","chunked");
sendHeader(String(F("Accept-Ranges")),String(F("none")));
sendHeader(String(F("Transfer-Encoding")),String(F("chunked")));
}
sendHeader("Connection", "close");
sendHeader(String(F("Connection")), String(F("close")));

response += _responseHeaders;
response += "\r\n";
Expand Down Expand Up @@ -466,6 +481,19 @@ void ESP8266WebServer::sendContent_P(PGM_P content, size_t size) {
}


void ESP8266WebServer::_streamFileCore(const size_t fileSize, const String & fileName, const String & contentType)
{
using namespace mime;
setContentLength(fileSize);
if (fileName.endsWith(mimeTable[gz].endsWith) &&
contentType != mimeTable[gz].mimeType &&
contentType != mimeTable[none].mimeType) {
sendHeader(F("Content-Encoding"), F("gzip"));
}
send(200, contentType, "");
}


String ESP8266WebServer::arg(String name) {
for (int i = 0; i < _currentArgCount; ++i) {
if ( _currentArgs[i].key == name )
Expand Down Expand Up @@ -512,7 +540,7 @@ void ESP8266WebServer::collectHeaders(const char* headerKeys[], const size_t hea
if (_currentHeaders)
delete[]_currentHeaders;
_currentHeaders = new RequestArgument[_headerKeysCount];
_currentHeaders[0].key = AUTHORIZATION_HEADER;
_currentHeaders[0].key = FPSTR(AUTHORIZATION_HEADER);
for (int i = 1; i < _headerKeysCount; i++){
_currentHeaders[i].key = headerKeys[i-1];
}
Expand Down Expand Up @@ -574,7 +602,8 @@ void ESP8266WebServer::_handleRequest() {
handled = true;
}
if (!handled) {
send(404, "text/plain", String("Not found: ") + _currentUri);
using namespace mime;
send(404, mimeTable[html].mimeType, String(F("Not found: ")) + _currentUri);
handled = true;
}
if (handled) {
Expand All @@ -586,7 +615,7 @@ void ESP8266WebServer::_handleRequest() {

void ESP8266WebServer::_finalizeResponse() {
if (_chunked) {
sendContent("");
sendContent(String());
}
}

Expand Down Expand Up @@ -632,6 +661,6 @@ String ESP8266WebServer::_responseCodeToString(int code) {
case 503: return F("Service Unavailable");
case 504: return F("Gateway Time-out");
case 505: return F("HTTP Version not supported");
default: return "";
default: return F("");
}
}
Loading