From 7de10cc9e4a07e1f3337e28015807faa3040329d Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Fri, 22 Jan 2016 18:06:23 +0200 Subject: [PATCH 01/10] merge with ESP31B --- .../ESP32_AsyncFSBrowser.ino | 199 ++++++++++ .../ESP32_AsyncFSBrowser/data/edit.htm.gz | Bin 0 -> 4116 bytes .../ESP32_AsyncFSBrowser/data/favicon.ico | Bin 0 -> 1150 bytes .../ESP32_AsyncFSBrowser/data/graphs.js.gz | Bin 0 -> 2379 bytes examples/ESP32_AsyncFSBrowser/data/index.htm | 366 ++++++++++++++++++ library.properties | 8 +- src/AsyncWebServerResponseImpl.h | 2 + src/StringArray.h | 2 +- src/WebServerClient.cpp | 7 +- 9 files changed, 576 insertions(+), 8 deletions(-) create mode 100644 examples/ESP32_AsyncFSBrowser/ESP32_AsyncFSBrowser.ino create mode 100644 examples/ESP32_AsyncFSBrowser/data/edit.htm.gz create mode 100644 examples/ESP32_AsyncFSBrowser/data/favicon.ico create mode 100644 examples/ESP32_AsyncFSBrowser/data/graphs.js.gz create mode 100644 examples/ESP32_AsyncFSBrowser/data/index.htm diff --git a/examples/ESP32_AsyncFSBrowser/ESP32_AsyncFSBrowser.ino b/examples/ESP32_AsyncFSBrowser/ESP32_AsyncFSBrowser.ino new file mode 100644 index 0000000..faf0b7e --- /dev/null +++ b/examples/ESP32_AsyncFSBrowser/ESP32_AsyncFSBrowser.ino @@ -0,0 +1,199 @@ +#include +#include +#include +#include + +// WEB HANDLER IMPLEMENTATION +class SPIFFSEditor: public AsyncWebHandler { + private: + String _username; + String _password; + bool _uploadAuthenticated; + public: + SPIFFSEditor(String username=String(), String password=String()):_username(username),_password(password),_uploadAuthenticated(false){} + bool canHandle(AsyncWebServerRequest *request){ + if(request->method() == HTTP_GET && request->url() == "/edit" && (SPIFFS.exists("/edit.htm") || SPIFFS.exists("/edit.htm.gz"))) + return true; + else if(request->method() == HTTP_GET && request->url() == "/list") + return true; + else if(request->method() == HTTP_GET && (request->url().endsWith("/") || SPIFFS.exists(request->url()) || (!request->hasParam("download") && SPIFFS.exists(request->url()+".gz")))) + return true; + else if(request->method() == HTTP_POST && request->url() == "/edit") + return true; + else if(request->method() == HTTP_DELETE && request->url() == "/edit") + return true; + else if(request->method() == HTTP_PUT && request->url() == "/edit") + return true; + return false; + } + + void handleRequest(AsyncWebServerRequest *request){ + if(_username.length() && (request->method() != HTTP_GET || request->url() == "/edit" || request->url() == "/list") && !request->authenticate(_username.c_str(),_password.c_str())) + return request->requestAuthentication(); + + if(request->method() == HTTP_GET && request->url() == "/edit"){ + request->send(SPIFFS, "/edit.htm"); + } else if(request->method() == HTTP_GET && request->url() == "/list"){ + if(request->hasParam("dir")){ + String path = request->getParam("dir")->value(); + Dir dir = SPIFFS.openDir(path); + path = String(); + String output = "["; + while(dir.next()){ + File entry = dir.openFile("r"); + if (output != "[") output += ','; + bool isDir = false; + output += "{\"type\":\""; + output += (isDir)?"dir":"file"; + output += "\",\"name\":\""; + output += String(entry.name()).substring(1); + output += "\"}"; + entry.close(); + } + output += "]"; + request->send(200, "text/json", output); + output = String(); + } + else + request->send(400); + } else if(request->method() == HTTP_GET){ + String path = request->url(); + if(path.endsWith("/")) + path += "index.htm"; + request->send(SPIFFS, path, String(), request->hasParam("download")); + } else if(request->method() == HTTP_DELETE){ + if(request->hasParam("path", true)){ + SPIFFS.remove(request->getParam("path", true)->value()); + request->send(200, "", "DELETE: "+request->getParam("path", true)->value()); + } else + request->send(404); + } else if(request->method() == HTTP_POST){ + if(request->hasParam("data", true, true) && SPIFFS.exists(request->getParam("data", true, true)->value())) + request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value()); + else + request->send(500); + } else if(request->method() == HTTP_PUT){ + if(request->hasParam("path", true)){ + String filename = request->getParam("path", true)->value(); + if(SPIFFS.exists(filename)){ + request->send(200); + } else { + File f = SPIFFS.open(filename, "w"); + if(f){ + f.write(0x00); + f.close(); + request->send(200, "", "CREATE: "+filename); + } else { + request->send(500); + } + } + } else + request->send(400); + } + } + + void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index){ + if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str())) + _uploadAuthenticated = true; + request->_tempFile = SPIFFS.open(filename, "w"); + } + if(_uploadAuthenticated && request->_tempFile && len){ + request->_tempFile.write(data,len); + } + if(_uploadAuthenticated && final) + if(request->_tempFile) request->_tempFile.close(); + } +}; + + +// SKETCH BEGIN +AsyncWebServer server(80); + +const char* ssid = "**********"; +const char* password = "************"; +const char* http_username = "admin"; +const char* http_password = "admin"; + +void setup(){ + Serial.begin(115200); + Serial.setDebugOutput(true); + SPIFFS.begin(); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + if(WiFi.waitForConnectResult() != WL_CONNECTED){ + Serial.printf("WiFi Failed!\n"); + } + + server.serveStatic("/fs", SPIFFS, "/"); + + server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/plain", String(ESP.getFreeHeap())); + }); + server.addHandler(new SPIFFSEditor(http_username,http_password)); + + server.onNotFound([](AsyncWebServerRequest *request){ + os_printf("NOT_FOUND: "); + if(request->method() == HTTP_GET) + os_printf("GET"); + else if(request->method() == HTTP_POST) + os_printf("POST"); + else if(request->method() == HTTP_DELETE) + os_printf("DELETE"); + else if(request->method() == HTTP_PUT) + os_printf("PUT"); + else if(request->method() == HTTP_PATCH) + os_printf("PATCH"); + else if(request->method() == HTTP_HEAD) + os_printf("HEAD"); + else if(request->method() == HTTP_OPTIONS) + os_printf("OPTIONS"); + else + os_printf("UNKNOWN"); + os_printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); + + if(request->contentLength()){ + os_printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); + os_printf("_CONTENT_LENGTH: %u\n", request->contentLength()); + } + + int headers = request->headers(); + int i; + for(i=0;igetHeader(i); + os_printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); + } + + int params = request->params(); + for(i=0;igetParam(i); + if(p->isFile()){ + os_printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); + } else if(p->isPost()){ + os_printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } else { + os_printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + } + + request->send(404); + }); + server.onFileUpload([](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){ + if(!index) + os_printf("UploadStart: %s\n", filename.c_str()); + os_printf("%s", (const char*)data); + if(final) + os_printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len); + }); + server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){ + if(!index) + os_printf("BodyStart: %u\n", total); + os_printf("%s", (const char*)data); + if(index + len == total) + os_printf("BodyEnd: %u\n", total); + }); + server.begin(); +} + +void loop(){} + diff --git a/examples/ESP32_AsyncFSBrowser/data/edit.htm.gz b/examples/ESP32_AsyncFSBrowser/data/edit.htm.gz new file mode 100644 index 0000000000000000000000000000000000000000..9ee1b81f0054db18190d447db26b3a7a15f10e88 GIT binary patch literal 4116 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 literal 0 HcmV?d00001 diff --git a/examples/ESP32_AsyncFSBrowser/data/favicon.ico b/examples/ESP32_AsyncFSBrowser/data/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..71b25fe6ee6012a4c26602977262d217af885520 GIT binary patch literal 1150 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 Sf4eT#Gbs4oy<;Q7zyJVaSCzB? literal 0 HcmV?d00001 diff --git a/examples/ESP32_AsyncFSBrowser/data/index.htm b/examples/ESP32_AsyncFSBrowser/data/index.htm new file mode 100644 index 0000000..7523b22 --- /dev/null +++ b/examples/ESP32_AsyncFSBrowser/data/index.htm @@ -0,0 +1,366 @@ + + + + + + ESP Monitor + + + + + + +
+
+
+ + + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+ + + + + + + + + + + + + +
   SSIDBSSIDChannelSecureHiddenRSSI
+
+ + + + diff --git a/library.properties b/library.properties index b2354cf..18bbbbd 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ -name=ESP8266 Async Web Server +name=ESP Async WebServer version=1.0.0 -author=ESP8266 +author=Me-No-Dev maintainer=Me-No-Dev -sentence=Async Web Server for ESP8266 -paragraph=Async Web Server for ESP8266 +sentence=Async Web Server for ESP8266 and ESP31B +paragraph=Async Web Server for ESP8266 and ESP31B category=Other url=https://github.com/me-no-dev/ESPAsyncWebServer architectures=* diff --git a/src/AsyncWebServerResponseImpl.h b/src/AsyncWebServerResponseImpl.h index 145243b..3b20997 100644 --- a/src/AsyncWebServerResponseImpl.h +++ b/src/AsyncWebServerResponseImpl.h @@ -57,6 +57,8 @@ class AsyncCallbackResponse: public AsyncAbstractResponse { size_t _fillBuffer(uint8_t *buf, size_t maxLen); }; +class cbuf; + class AsyncResponseStream: public AsyncAbstractResponse, public Print { private: cbuf *_content; diff --git a/src/StringArray.h b/src/StringArray.h index 1d89bbb..bce1067 100644 --- a/src/StringArray.h +++ b/src/StringArray.h @@ -9,7 +9,7 @@ #define STRINGARRAY_H_ #include "stddef.h" -#include "String.h" +#include "WString.h" class StringArrayItem; class StringArrayItem { diff --git a/src/WebServerClient.cpp b/src/WebServerClient.cpp index 5f6a419..95e4991 100644 --- a/src/WebServerClient.cpp +++ b/src/WebServerClient.cpp @@ -147,11 +147,12 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){ } void AsyncWebServerRequest::_onError(int8_t error){ - //os_printf("e:%d:%u\n", error, _client->state()); + if(error != -11) + os_printf("ERROR[%d] %s, state: %s\n", error, _client->errorToString(error), _client->stateToString()); } void AsyncWebServerRequest::_onTimeout(uint32_t time){ - //os_printf("t:%u\n", time); + os_printf("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); _client->close(); } @@ -640,7 +641,7 @@ void AsyncWebServerRequest::send(String contentType, size_t len, AwsResponseFill bool AsyncWebServerRequest::authenticate(const char * username, const char * password){ if(_authorization.length()){ - char toencodeLen = strlen(username)+strlen(password)+1; + char toencodeLen = os_strlen(username)+os_strlen(password)+1; char *toencode = new char[toencodeLen]; if(toencode == NULL){ return false; From 4298d0a0370d829e2dd5bd4e9226d4dba1ed0d32 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Fri, 22 Jan 2016 18:09:05 +0200 Subject: [PATCH 02/10] oops :) --- src/WebServerClient.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/WebServerClient.cpp b/src/WebServerClient.cpp index 95e4991..85fb2cd 100644 --- a/src/WebServerClient.cpp +++ b/src/WebServerClient.cpp @@ -9,6 +9,10 @@ #include "AsyncWebServerResponseImpl.h" #include +#ifndef ESP8266 +#define os_strlen strlen +#endif + #define __is_param_char(c) ((c) && ((c) != '{') && ((c) != '[') && ((c) != '&') && ((c) != '=')) enum { PARSE_REQ_START, PARSE_REQ_HEADERS, PARSE_REQ_BODY, PARSE_REQ_END, PARSE_REQ_FAIL }; From 3c658dcae864daf2d3d7f194281d016cce148830 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Sat, 23 Jan 2016 19:06:57 +0200 Subject: [PATCH 03/10] fix where file will not be served if only gzipped version exists. Thanks @andig --- src/AsyncWebServerHandlerImpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AsyncWebServerHandlerImpl.h b/src/AsyncWebServerHandlerImpl.h index 6f9133a..c7f63b7 100644 --- a/src/AsyncWebServerHandlerImpl.h +++ b/src/AsyncWebServerHandlerImpl.h @@ -21,7 +21,7 @@ class AsyncStaticWebHandler: public AsyncWebHandler { public: AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) : _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){ - _isFile = _fs.exists(path); + _isFile = _fs.exists(path) || _fs.exists(path+".gz"); } bool canHandle(AsyncWebServerRequest *request); void handleRequest(AsyncWebServerRequest *request); From 154ac0060f9aaa3bcf11ec71405f72124db2a8f6 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Sat, 23 Jan 2016 19:08:28 +0200 Subject: [PATCH 04/10] oops again --- src/AsyncWebServerHandlerImpl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AsyncWebServerHandlerImpl.h b/src/AsyncWebServerHandlerImpl.h index c7f63b7..9bee20c 100644 --- a/src/AsyncWebServerHandlerImpl.h +++ b/src/AsyncWebServerHandlerImpl.h @@ -21,7 +21,7 @@ class AsyncStaticWebHandler: public AsyncWebHandler { public: AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) : _fs(fs), _uri(uri), _path(path), _cache_header(cache_header){ - _isFile = _fs.exists(path) || _fs.exists(path+".gz"); + _isFile = _fs.exists(path) || _fs.exists((String(path)+".gz").c_str()); } bool canHandle(AsyncWebServerRequest *request); void handleRequest(AsyncWebServerRequest *request); From 8fed4c42f34a4693b6529f051fbb6840d886a1b4 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 26 Jan 2016 17:52:49 +0200 Subject: [PATCH 05/10] return if called from interrupt --- src/WebResponses.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index 0717787..1a6dc33 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -2,7 +2,6 @@ #include "AsyncWebServerResponseImpl.h" #include "cbuf.h" - /* * Abstract Response * */ @@ -358,16 +357,15 @@ size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ } size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ - if(_finished()) + if(_finished() || (_content->room() == 0 && ETS_INTR_WITHINISR())) return 0; - //while(_content->room() < len) delay(1); + if(len > _content->getSize()) + len = _content->getSize(); + while(_content->room() < len) delay(0); return _content->write((const char*)data, len); } size_t AsyncResponseStream::write(uint8_t data){ - if(_finished()) - return 0; - //while(_content->room() == 0) delay(1); return write(&data, 1); } From 36b64df702f789f1d2756d6a24058902a811a14b Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 27 Jan 2016 16:17:34 +0200 Subject: [PATCH 06/10] change to support new cbuf api --- src/WebResponses.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index 1a6dc33..2001980 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -359,8 +359,8 @@ size_t AsyncResponseStream::_fillBuffer(uint8_t *buf, size_t maxLen){ size_t AsyncResponseStream::write(const uint8_t *data, size_t len){ if(_finished() || (_content->room() == 0 && ETS_INTR_WITHINISR())) return 0; - if(len > _content->getSize()) - len = _content->getSize(); + if(len > _content->available()) + len = _content->available(); while(_content->room() < len) delay(0); return _content->write((const char*)data, len); } From 4a5f64de03ecc081b0132e49d2abd86c1468bb17 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 27 Jan 2016 16:29:41 +0200 Subject: [PATCH 07/10] add link to the TCP library and warning --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0eb482b..a2c98ab 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # ESPAsyncWebServer Async Web Server for ESP8266 Arduino +Requires [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) to work + +This library should be considered experimental +Examples, description and all will be provided really soon :) + +To use this library you need to have the git versions of either ESP8266 or ESP31B Arduino Core From 9296156ab5501549ae5caa9eeccff0e2e9a62d05 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 27 Jan 2016 18:46:49 +0200 Subject: [PATCH 08/10] Add HTTP/1.1 chunked response and allow stream and callback responses to not have content length ContentLength and ContentType have setters (before you send the response) --- src/AsyncWebServerResponseImpl.h | 9 +++ src/ESPAsyncWebServer.h | 9 ++- src/WebResponses.cpp | 108 +++++++++++++++++++++++++------ src/WebServerClient.cpp | 10 +++ 4 files changed, 114 insertions(+), 22 deletions(-) diff --git a/src/AsyncWebServerResponseImpl.h b/src/AsyncWebServerResponseImpl.h index 3b20997..b894440 100644 --- a/src/AsyncWebServerResponseImpl.h +++ b/src/AsyncWebServerResponseImpl.h @@ -57,6 +57,15 @@ class AsyncCallbackResponse: public AsyncAbstractResponse { size_t _fillBuffer(uint8_t *buf, size_t maxLen); }; +class AsyncChunkedResponse: public AsyncAbstractResponse { + private: + AwsResponseFiller _content; + public: + AsyncChunkedResponse(String contentType, AwsResponseFiller callback); + bool _sourceValid(){ return !!(_content); } + size_t _fillBuffer(uint8_t *buf, size_t maxLen); +}; + class cbuf; class AsyncResponseStream: public AsyncAbstractResponse, public Print { diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index a8be5a8..c192dcf 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -86,6 +86,7 @@ class AsyncWebServerRequest { String _temp; uint8_t _parseState; + uint8_t _version; WebRequestMethod _method; String _url; String _host; @@ -142,6 +143,7 @@ class AsyncWebServerRequest { ~AsyncWebServerRequest(); AsyncClient* client(){ return _client; } + uint8_t version(){ return _version; } WebRequestMethod method(){ return _method; } String url(){ return _url; } String host(){ return _host; } @@ -166,6 +168,7 @@ class AsyncWebServerRequest { AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false); AsyncWebServerResponse *beginResponse(Stream &stream, String contentType, size_t len); AsyncWebServerResponse *beginResponse(String contentType, size_t len, AwsResponseFiller callback); + AsyncWebServerResponse *beginChunkedResponse(String contentType, AwsResponseFiller callback); AsyncResponseStream *beginResponseStream(String contentType, size_t len, size_t bufferSize=1460); int headers(); // get header count @@ -221,6 +224,8 @@ class AsyncWebServerResponse { AsyncWebHeader *_headers; String _contentType; size_t _contentLength; + bool _sendContentLength; + bool _chunked; size_t _headLength; size_t _sentLength; size_t _ackedLength; @@ -230,8 +235,10 @@ class AsyncWebServerResponse { public: AsyncWebServerResponse(); virtual ~AsyncWebServerResponse(); + virtual void setContentLength(size_t len); + virtual void setContentType(String type); virtual void addHeader(String name, String value); - virtual String _assembleHead(); + virtual String _assembleHead(uint8_t version); virtual bool _finished(); virtual bool _failed(); virtual void _respond(AsyncWebServerRequest *request); diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index 2001980..c94e5b9 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -52,14 +52,16 @@ const char* AsyncWebServerResponse::_responseCodeToString(int code) { } AsyncWebServerResponse::AsyncWebServerResponse() -:_code(0) -,_headers(NULL) -,_contentType() -,_contentLength(0) -,_headLength(0) -,_sentLength(0) -,_ackedLength(0) -,_state(RESPONSE_SETUP) + : _code(0) + , _headers(NULL) + , _contentType() + , _contentLength(0) + , _sendContentLength(true) + , _chunked(false) + , _headLength(0) + , _sentLength(0) + , _ackedLength(0) + , _state(RESPONSE_SETUP) { addHeader("Connection","close"); addHeader("Access-Control-Allow-Origin","*"); @@ -73,6 +75,16 @@ AsyncWebServerResponse::~AsyncWebServerResponse(){ } } +void AsyncWebServerResponse::setContentLength(size_t len){ + if(_state == RESPONSE_SETUP) + _contentLength = len; +} + +void AsyncWebServerResponse::setContentType(String type){ + if(_state == RESPONSE_SETUP) + _contentType = type; +} + void AsyncWebServerResponse::addHeader(String name, String value){ AsyncWebHeader *header = new AsyncWebHeader(name, value); if(_headers == NULL){ @@ -84,12 +96,19 @@ void AsyncWebServerResponse::addHeader(String name, String value){ } } -String AsyncWebServerResponse::_assembleHead(){ - String out = "HTTP/1.1 " + String(_code) + " " + _responseCodeToString(_code) + "\r\n"; - out += "Content-Length: " + String(_contentLength) + "\r\n"; - if(_contentType.length()){ - out += "Content-Type: " + _contentType + "\r\n"; +String AsyncWebServerResponse::_assembleHead(uint8_t version){ + if(version){ + addHeader("Accept-Ranges","none"); + 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"; + + if(_contentType.length()) + out += "Content-Type: " + _contentType + "\r\n"; + AsyncWebHeader *h; while(_headers != NULL){ h = _headers; @@ -123,7 +142,7 @@ AsyncBasicResponse::AsyncBasicResponse(int code, String contentType, String cont void AsyncBasicResponse::_respond(AsyncWebServerRequest *request){ _state = RESPONSE_HEADERS; - String out = _assembleHead(); + String out = _assembleHead(request->version()); size_t outLen = out.length(); size_t space = request->client()->space(); if(!_contentLength && space >= outLen){ @@ -192,7 +211,7 @@ void AsyncAbstractResponse::_respond(AsyncWebServerRequest *request){ request->send(500); return; } - _head = _assembleHead(); + _head = _assembleHead(request->version()); _state = RESPONSE_HEADERS; size_t outLen = _head.length(); size_t space = request->client()->space(); @@ -217,18 +236,42 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u _ackedLength += len; size_t space = request->client()->space(); if(_state == RESPONSE_CONTENT){ - size_t remaining = _contentLength - _sentLength; - size_t outLen = (remaining > space)?space:remaining; + size_t outLen; + size_t readLen = 0; + + if(_chunked || !_sendContentLength){ + outLen = space; + } else { + size_t remaining = _contentLength - _sentLength; + outLen = (remaining > space)?space:remaining; + } uint8_t *buf = (uint8_t *)malloc(outLen); - outLen = _fillBuffer(buf, outLen); + + if(_chunked){ + readLen = _fillBuffer(buf, outLen - 8); + char pre[6]; + sprintf(pre, "%x\r\n", readLen); + size_t preLen = strlen(pre); + memmove(buf+preLen, buf, preLen); + for(size_t i=0; iclient()->write((const char*)buf, outLen); + outLen = request->client()->write((const char*)buf, outLen); _sentLength += outLen; free(buf); - if(_sentLength == _contentLength){ + + if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || _sentLength == _contentLength){ _state = RESPONSE_WAIT_ACK; } return outLen; + } else if(_state == RESPONSE_HEADERS){ size_t outLen = _head.length(); if(space >= outLen){ @@ -243,8 +286,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u return out.length(); } } else if(_state == RESPONSE_WAIT_ACK){ - if(_ackedLength >= (_headLength+_contentLength)){ + if(!_sendContentLength || _ackedLength >= (_headLength+_contentLength)){ _state = RESPONSE_END; + if(!_chunked && !_sendContentLength) + request->client()->close(); } } return 0; @@ -329,6 +374,8 @@ AsyncCallbackResponse::AsyncCallbackResponse(String contentType, size_t len, Aws _code = 200; _content = callback; _contentLength = len; + if(!len) + _sendContentLength = false; _contentType = contentType; } @@ -336,6 +383,23 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ return _content(data, len); } +/* + * Chunked Response + * */ + +AsyncChunkedResponse::AsyncChunkedResponse(String contentType, AwsResponseFiller callback){ + _code = 200; + _content = callback; + _contentLength = 0; + _contentType = contentType; + _sendContentLength = false; + _chunked = true; +} + +size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ + return _content(data, len); +} + /* * Response Stream (You can print/write/printf to it, up to the contentLen bytes) @@ -344,6 +408,8 @@ size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ AsyncResponseStream::AsyncResponseStream(String contentType, size_t len, size_t bufferSize){ _code = 200; _contentLength = len; + if(!len) + _sendContentLength = false; _contentType = contentType; _content = new cbuf(bufferSize); } diff --git a/src/WebServerClient.cpp b/src/WebServerClient.cpp index 85fb2cd..d756499 100644 --- a/src/WebServerClient.cpp +++ b/src/WebServerClient.cpp @@ -25,6 +25,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) , _interestingHeaders(new StringArray()) , _temp() , _parseState(0) + , _version(0) , _method(HTTP_ANY) , _url() , _host() @@ -229,6 +230,9 @@ bool AsyncWebServerRequest::_parseReqHead(){ } } + _temp = _temp.substring(_temp.indexOf(' ')+1); + if(_temp.startsWith("HTTP/1.1")) + _version = 1; _temp = String(); return true; } @@ -620,6 +624,12 @@ AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(String contentType return new AsyncCallbackResponse(contentType, len, callback); } +AsyncWebServerResponse * AsyncWebServerRequest::beginChunkedResponse(String contentType, AwsResponseFiller callback){ + if(_version) + return new AsyncChunkedResponse(contentType, callback); + return new AsyncCallbackResponse(contentType, 0, callback); +} + AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(String contentType, size_t len, size_t bufferSize){ return new AsyncResponseStream(contentType, len, bufferSize); } From 8e5e815aa22dd67c0ccfe4cd920a9532d65ed1ea Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Wed, 27 Jan 2016 17:50:26 +0000 Subject: [PATCH 09/10] Add Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a2c98ab..119ff6e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ # ESPAsyncWebServer + +[![Join the chat at https://gitter.im/me-no-dev/ESPAsyncWebServer](https://badges.gitter.im/me-no-dev/ESPAsyncWebServer.svg)](https://gitter.im/me-no-dev/ESPAsyncWebServer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) Async Web Server for ESP8266 Arduino Requires [ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) to work From 3cdefde295d6a868469adc317e84dbec46affd9f Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Wed, 27 Jan 2016 19:55:32 +0200 Subject: [PATCH 10/10] add chunked to simple sends --- src/ESPAsyncWebServer.h | 1 + src/WebServerClient.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index c192dcf..eb28239 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -163,6 +163,7 @@ class AsyncWebServerRequest { void send(FS &fs, String path, String contentType=String(), bool download=false); void send(Stream &stream, String contentType, size_t len); void send(String contentType, size_t len, AwsResponseFiller callback); + void sendChunked(String contentType, AwsResponseFiller callback); AsyncWebServerResponse *beginResponse(int code, String contentType=String(), String content=String()); AsyncWebServerResponse *beginResponse(FS &fs, String path, String contentType=String(), bool download=false); diff --git a/src/WebServerClient.cpp b/src/WebServerClient.cpp index d756499..f6e66a5 100644 --- a/src/WebServerClient.cpp +++ b/src/WebServerClient.cpp @@ -635,21 +635,25 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(String contentT } void AsyncWebServerRequest::send(int code, String contentType, String content){ - send(new AsyncBasicResponse(code, contentType, content)); + send(beginResponse(code, contentType, content)); } void AsyncWebServerRequest::send(FS &fs, String path, String contentType, bool download){ if(fs.exists(path) || (!download && fs.exists(path+".gz"))){ - send(new AsyncFileResponse(fs, path, contentType, download)); + send(beginResponse(fs, path, contentType, download)); } else send(404); } void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){ - send(new AsyncStreamResponse(stream, contentType, len)); + send(beginResponse(stream, contentType, len)); } void AsyncWebServerRequest::send(String contentType, size_t len, AwsResponseFiller callback){ - send(new AsyncCallbackResponse(contentType, len, callback)); + send(beginResponse(contentType, len, callback)); +} + +void AsyncWebServerRequest::sendChunked(String contentType, AwsResponseFiller callback){ + send(beginChunkedResponse(contentType, callback)); }