diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h index 817d323cbf..58ee461ba6 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer-impl.h @@ -219,11 +219,11 @@ void ESP8266WebServerTemplate::requestAuthentication(HTTPAuthMethod _srealm = String(realm); } if(mode == BASIC_AUTH) { - sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Basic realm=\"")) + _srealm + String('\"')); + sendHeader(FPSTR(WWW_Authenticate), String(F("Basic realm=\"")) + _srealm + String('\"')); } else { _snonce=_getRandomHexString(); _sopaque=_getRandomHexString(); - sendHeader(String(FPSTR(WWW_Authenticate)), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String('\"')); + sendHeader(FPSTR(WWW_Authenticate), String(F("Digest realm=\"")) +_srealm + String(F("\", qop=\"auth\", nonce=\"")) + _snonce + String(F("\", opaque=\"")) + _sopaque + String('\"')); } using namespace mime; send(401, String(FPSTR(mimeTable[html].mimeType)), authFailMsg); @@ -274,7 +274,7 @@ void ESP8266WebServerTemplate::serveStatic(const char* uri, FS& fs, if(is_file) { _addRequestHandler(new StaticFileRequestHandler(fs, path, uri, cache_header)); } else { - _addRequestHandler(new StaticDirectoryRequestHandler(fs, path, uri, cache_header)); + _addRequestHandler(new StaticDirectoryRequestHandler(fs, path, uri, cache_header)); } } @@ -419,18 +419,11 @@ void ESP8266WebServerTemplate::stop() { } template -void ESP8266WebServerTemplate::sendHeader(const String& name, const String& value, bool first) { - String headerLine = name; - headerLine += F(": "); - headerLine += value; - headerLine += "\r\n"; - - if (first) { - _responseHeaders = headerLine + _responseHeaders; - } - else { - _responseHeaders += headerLine; - } +void ESP8266WebServerTemplate::_storeHeader(std::pair&& nameValue, bool first) { + if (first) + _userHeaders.emplace_front(std::move(nameValue)); + else + _userHeaders.emplace_back(std::move(nameValue)); } template @@ -439,44 +432,80 @@ void ESP8266WebServerTemplate::setContentLength(const size_t content } template -void ESP8266WebServerTemplate::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { - response = String(F("HTTP/1.")) + String(_currentVersion) + ' '; - response += String(code); - response += ' '; - response += responseCodeToString(code); - response += "\r\n"; +bool ESP8266WebServerTemplate::_streamIt (Stream& s) { + int len = s.streamRemaining(); + int sent = s.sendAll(&_currentClient); + if (sent == len) + return true; + DBGWS("HTTPServer: error: sent %zd on %u bytes\n", sent, len); + return false; +} + +template +template +bool ESP8266WebServerTemplate::_streamHeader (K name, V value) +{ + return _streamIt(name) && _streamItC(F(": ")) && _streamIt(value) && _streamItC(F("\r\n")); +} + +template +bool ESP8266WebServerTemplate::_sendHeader(int code, const char* content_type, size_t contentLength) { + if ( !_streamItC(F("HTTP/1.")) + || !_streamIt(String(_currentVersion)) + || !_streamItC(F(" ")) + || !_streamIt(String(code)) + || !_streamItC(F(" ")) + || !_streamIt(responseCodeToString(code)) + || !_streamItC(F("\r\n"))) + { + return false; + } using namespace mime; if (!content_type) content_type = mimeTable[html].mimeType; + if (!_streamHeader(F("Content-Type"), content_type)) + return false; - sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true); if (_contentLength == CONTENT_LENGTH_NOT_SET) { - sendHeader(String(FPSTR(Content_Length)), String(contentLength)); + if (!_streamHeader(Content_Length, String(contentLength))) + return false; } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { - 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(String(F("Accept-Ranges")),String(F("none"))); - sendHeader(String(F("Transfer-Encoding")),String(F("chunked"))); + if (!_streamHeader(Content_Length, String(_contentLength))) + return false; + } else if (_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client + //let's do chunked + _chunked = true; + if (!_streamHeader(F("Accept-Ranges"), F("none")) || !_streamHeader(F("Transfer-Encoding"), F("chunked"))) + return false; } + if (_corsEnabled) { - sendHeader(String(F("Access-Control-Allow-Origin")), String("*")); + if (!_streamHeader(F("Access-Control-Allow-Origin"), F("*"))) + return false; } - if (_keepAlive && _server.hasClient()) { // Disable keep alive if another client is waiting. - _keepAlive = false; + if (_keepAlive && _server.hasClient()) { + // Disable keep alive if another client is waiting. + _keepAlive = false; + } + if (!_streamHeader(F("Connection"), String(_keepAlive ? F("keep-alive") : F("close")))) { + return false; } - sendHeader(String(F("Connection")), String(_keepAlive ? F("keep-alive") : F("close"))); if (_keepAlive) { - sendHeader(String(F("Keep-Alive")), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT); + if (!_streamHeader(F("Keep-Alive"), String(F("timeout=")) + HTTP_MAX_CLOSE_WAIT)) + return false; } + for (const auto& kv: _userHeaders) + if (!_streamHeader(kv.first, kv.second)) + return false; + _userHeaders.clear(); - response += _responseHeaders; - response += "\r\n"; - _responseHeaders = ""; + if (!_streamItC(F("\r\n"))) + return false; + + return true; } template @@ -502,13 +531,10 @@ void ESP8266WebServerTemplate::sendContent(const String& content) { template void ESP8266WebServerTemplate::send(int code, const char* content_type, Stream* stream, size_t content_length /*= 0*/) { - String header; if (content_length == 0) content_length = std::max((ssize_t)0, stream->streamRemaining()); - _prepareHeader(header, code, content_type, content_length); - size_t sent = StreamConstPtr(header).sendAll(&_currentClient); - if (sent != header.length()) - DBGWS("HTTPServer: error: sent %zd on %u bytes\n", sent, header.length()); + if (!_sendHeader(code, content_type, content_length)) + return; if (content_length) return sendContent(stream, content_length); } diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index e0dba27a90..1d26c10e90 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -27,8 +27,11 @@ #include #include #include +#include + #include #include +#include #include "detail/mimetable.h" #include "Uri.h" @@ -176,7 +179,13 @@ class ESP8266WebServerTemplate } void setContentLength(const size_t contentLength); - void sendHeader(const String& name, const String& value, bool first = false); + + template + void sendHeader(S1&& name, S2&& value, bool first = false) + { + _storeHeader(std::make_pair(String(std::forward(name)), String(std::forward(value))), first); + } + void sendContent(const String& content); void sendContent(String& content) { sendContent((const String&)content); @@ -291,7 +300,7 @@ class ESP8266WebServerTemplate bool _parseFormUploadAborted(); void _uploadWriteByte(uint8_t b); int _uploadReadByte(ClientType& client); - void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); + bool _sendHeader(int code, const char* content_type, size_t contentLength); bool _collectHeader(const char* headerName, const char* headerValue); void _streamFileCore(const size_t fileSize, const String & fileName, const String & contentType); @@ -300,6 +309,13 @@ class ESP8266WebServerTemplate // for extracting Auth parameters String _extractParam(String& authReq,const String& param,const char delimit = '"') const; + bool _streamIt (const String& s) { StreamConstPtr c(s.c_str(), s.length()); return _streamIt(c); } + bool _streamItC (StreamConstPtr&& s) { return _streamIt(s); } + bool _streamIt (Stream& s); + template + bool _streamHeader (K name, V value); + void _storeHeader(std::pair&& nameValue, bool first); + struct RequestArgument { String key; String value; @@ -330,7 +346,7 @@ class ESP8266WebServerTemplate RequestArgument* _currentHeaders = nullptr; size_t _contentLength = 0; - String _responseHeaders; + std::list> _userHeaders; String _hostHeader; bool _chunked = false;