From 40d5d4415e7e8f333a100ce6795b4d0ef2163140 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 9 Feb 2016 01:47:03 +0200 Subject: [PATCH 1/3] Rearrange and rename files and add index to chunked responses --- README.md | 26 +- src/ESPAsyncWebServer.h | 24 +- src/StringArray.h | 24 +- ...ebServerHandlerImpl.h => WebHandlerImpl.h} | 29 +- src/WebHandlers.cpp | 26 +- src/WebRequest.cpp | 409 ++++++++++++++ ...bServerClient.cpp => WebRequestParser.cpp} | 523 ++++-------------- ...ServerResponseImpl.h => WebResponseImpl.h} | 26 +- src/WebResponses.cpp | 33 +- src/WebServer.cpp | 22 +- 10 files changed, 679 insertions(+), 463 deletions(-) rename src/{AsyncWebServerHandlerImpl.h => WebHandlerImpl.h} (70%) create mode 100644 src/WebRequest.cpp rename src/{WebServerClient.cpp => WebRequestParser.cpp} (52%) rename src/{AsyncWebServerResponseImpl.h => WebResponseImpl.h} (72%) diff --git a/README.md b/README.md index 27f3283d..e4eaa5f0 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ request->send("text/plain", 128, [](uint8_t *buffer, size_t maxLen) -> size_t { ### Respond with content using a callback and extra headers ```cpp //send 128 bytes as plain text -AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen) -> size_t { +AsyncWebServerResponse *response = request->beginResponse("text/plain", 128, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { //Write up to "maxLen" bytes into "buffer" and return the amount written. //You will not be asked for more bytes once the content length has been reached. //Keep in mind that you can not delay or yield waiting for more data! @@ -249,7 +249,7 @@ request->send(response); ### Chunked Response Used when content length is unknown. Works best if the client supports HTTP/1.1 ```cpp -AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen) -> size_t { +AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { //Write up to "maxLen" bytes into "buffer" and return the amount written. //You will be asked for more data until 0 is returned //Keep in mind that you can not delay or yield waiting for more data! @@ -309,6 +309,28 @@ response->print(""); request->send(response); ``` +### Send a large webpage from PROGMEM using chunked response +Example provided by [@nouser2013](https://github.com/nouser2013) +```cpp +const char indexhtml[] PROGMEM = "..."; // large char array, tested with 5k +AsyncWebServerResponse *response = request->beginResponse( + String("text/html"), + strlen_P(indexhtml), + [](uint8_t *buffer, size_t maxLen, size_t alreadySent) -> size_t { + if (strlen_P(indexhtml+sentCounter)>maxLen) { + // We have more to read than fits in maxLen Buffer + memcpy_P((char*)buffer, indexhtml+sentCounter, maxLen); + return maxLen; + } + // Ok, last chunk + memcpy_P((char*)buffer, indexhtml+sentCounter, strlen_P(indexhtml+sentCounter)); + return strlen_P(indexhtml+sentCounter); // Return from here to end of indexhtml + } +); +response->addHeader("Server", "MyServerString"); +request->send(response); +``` + ### ArduinoJson Basic Response This way of sending Json is great for when the result is below 4KB ```cpp diff --git a/src/ESPAsyncWebServer.h b/src/ESPAsyncWebServer.h index 8b228eb6..c39d0d12 100644 --- a/src/ESPAsyncWebServer.h +++ b/src/ESPAsyncWebServer.h @@ -1,3 +1,23 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef _ESPAsyncWebServer_H_ #define _ESPAsyncWebServer_H_ @@ -76,7 +96,7 @@ class AsyncWebHeader { * REQUEST :: Each incoming Client is wrapped inside a Request and both live together until disconnect * */ -typedef std::function AwsResponseFiller; +typedef std::function AwsResponseFiller; class AsyncWebServerRequest { private: @@ -286,6 +306,6 @@ class AsyncWebServer { void _handleRequest(AsyncWebServerRequest *request); }; -#include "AsyncWebServerResponseImpl.h" +#include #endif /* _AsyncWebServer_H_ */ diff --git a/src/StringArray.h b/src/StringArray.h index bce10674..cd51c523 100644 --- a/src/StringArray.h +++ b/src/StringArray.h @@ -1,9 +1,23 @@ /* - * StringArray.h - * - * Created on: 18.12.2015 г. - * Author: ficeto - */ + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef STRINGARRAY_H_ #define STRINGARRAY_H_ diff --git a/src/AsyncWebServerHandlerImpl.h b/src/WebHandlerImpl.h similarity index 70% rename from src/AsyncWebServerHandlerImpl.h rename to src/WebHandlerImpl.h index ee027d23..f5e6cc47 100644 --- a/src/AsyncWebServerHandlerImpl.h +++ b/src/WebHandlerImpl.h @@ -1,27 +1,42 @@ /* - * AsyncWebServerHandlerImpl.h - * - * Created on: 19.12.2015 г. - * Author: ficeto - */ + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef ASYNCWEBSERVERHANDLERIMPL_H_ #define ASYNCWEBSERVERHANDLERIMPL_H_ #include "stddef.h" +#include "ESPAsyncWebServer.h" class AsyncStaticWebHandler: public AsyncWebHandler { private: String _getPath(AsyncWebServerRequest *request); protected: - FS _fs; + fs::FS _fs; String _uri; String _path; String _cache_header; bool _isFile; public: - AsyncStaticWebHandler(FS& fs, const char* path, const char* uri, const char* cache_header) + AsyncStaticWebHandler(fs::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((String(path)+".gz").c_str()); diff --git a/src/WebHandlers.cpp b/src/WebHandlers.cpp index 0c1dcff2..7729f05e 100644 --- a/src/WebHandlers.cpp +++ b/src/WebHandlers.cpp @@ -1,11 +1,25 @@ /* - * WebHandlers.cpp - * - * Created on: 18.12.2015 г. - * Author: ficeto - */ + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include #include "ESPAsyncWebServer.h" -#include "AsyncWebServerHandlerImpl.h" bool AsyncStaticWebHandler::canHandle(AsyncWebServerRequest *request) { diff --git a/src/WebRequest.cpp b/src/WebRequest.cpp new file mode 100644 index 00000000..91a8c2c6 --- /dev/null +++ b/src/WebRequest.cpp @@ -0,0 +1,409 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "ESPAsyncWebServer.h" +#include +#include + +#ifndef ESP8266 +#define os_strlen strlen +#endif + +AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) + : _client(c) + , _server(s) + , _handler(NULL) + , _response(NULL) + , _interestingHeaders(new StringArray()) + , _temp() + , _parseState(0) + , _version(0) + , _method(HTTP_ANY) + , _url() + , _host() + , _contentType() + , _boundary() + , _authorization() + , _isMultipart(false) + , _isPlainPost(false) + , _expectingContinue(false) + , _contentLength(0) + , _parsedLength(0) + , _headers(NULL) + , _params(NULL) + , _multiParseState(0) + , _boundaryPosition(0) + , _itemStartIndex(0) + , _itemSize(0) + , _itemName() + , _itemFilename() + , _itemType() + , _itemValue() + , _itemBuffer(0) + , _itemBufferIndex(0) + , _itemIsFile(false) + , next(NULL) +{ + c->onError([](void *r, AsyncClient* c, int8_t error){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); + c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); + c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); }, this); + c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); + c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); + c->onPoll([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onPoll(); }, this); +} + +AsyncWebServerRequest::~AsyncWebServerRequest(){ + while(_headers != NULL){ + AsyncWebHeader *h = _headers; + _headers = h->next; + delete h; + } + + while(_params != NULL){ + AsyncWebParameter *p = _params; + _params = p->next; + delete p; + } + + _interestingHeaders->free(); + delete _interestingHeaders; + + if(_response != NULL){ + delete _response; + } + +} + +void AsyncWebServerRequest::_onPoll(){ + //os_printf("p\n"); + if(_response != NULL && !_response->_finished() && _client->canSend()){ + _response->_ack(this, 0, 0); + } +} + +void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){ + //os_printf("a:%u:%u\n", len, time); + if(_response != NULL){ + if(!_response->_finished()){ + _response->_ack(this, len, time); + } else { + AsyncWebServerResponse* r = _response; + _response = NULL; + delete r; + } + } +} + +void AsyncWebServerRequest::_onError(int8_t error){ + 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("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); + _client->close(); +} + +void AsyncWebServerRequest::_onDisconnect(){ + //os_printf("d\n"); + _client->free(); + delete _client; + _server->_handleDisconnect(this); +} + +void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){ + if(_params == NULL) + _params = p; + else { + AsyncWebParameter *ps = _params; + while(ps->next != NULL) ps = ps->next; + ps->next = p; + } +} + +int AsyncWebServerRequest::headers(){ + int i = 0; + AsyncWebHeader* h = _headers; + while(h != NULL){ + i++; h = h->next; + } + return i; +} + +bool AsyncWebServerRequest::hasHeader(String name){ + AsyncWebHeader* h = _headers; + while(h != NULL){ + if(h->name() == name) + return true; + h = h->next; + } + return false; +} + +AsyncWebHeader* AsyncWebServerRequest::getHeader(String name){ + AsyncWebHeader* h = _headers; + while(h != NULL){ + if(h->name() == name) + return h; + h = h->next; + } + return NULL; +} + +AsyncWebHeader* AsyncWebServerRequest::getHeader(int num){ + int i = 0; + AsyncWebHeader* h = _headers; + while(h != NULL){ + if(num == i++) + return h; + h = h->next; + } + return NULL; +} + +int AsyncWebServerRequest::params(){ + int i = 0; + AsyncWebParameter* h = _params; + while(h != NULL){ + i++; h = h->next; + } + return i; +} + +bool AsyncWebServerRequest::hasParam(String name, bool post, bool file){ + AsyncWebParameter* h = _params; + while(h != NULL){ + if(h->name() == name && h->isPost() == post && h->isFile() == file) + return true; + h = h->next; + } + return false; +} + +AsyncWebParameter* AsyncWebServerRequest::getParam(String name, bool post, bool file){ + AsyncWebParameter* h = _params; + while(h != NULL){ + if(h->name() == name && h->isPost() == post && h->isFile() == file) + return h; + h = h->next; + } + return NULL; +} + +AsyncWebParameter* AsyncWebServerRequest::getParam(int num){ + int i = 0; + AsyncWebParameter* h = _params; + while(h != NULL){ + if(num == i++) + return h; + h = h->next; + } + return NULL; +} + +void AsyncWebServerRequest::addInterestingHeader(String name){ + if(!_interestingHeaders->contains(name)) _interestingHeaders->add(name); +} + + +void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ + _response = response; + _response->_respond(this); +} + + + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, String contentType, String content){ + return new AsyncBasicResponse(code, contentType, content); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, String path, String contentType, bool download){ + if(fs.exists(path) || (!download && fs.exists(path+".gz"))) + return new AsyncFileResponse(fs, path, contentType, download); + return NULL; +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){ + return new AsyncStreamResponse(stream, contentType, len); +} + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(String contentType, size_t len, AwsResponseFiller callback){ + 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 bufferSize){ + return new AsyncResponseStream(contentType, bufferSize); +} + +void AsyncWebServerRequest::send(int code, String contentType, String 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(beginResponse(fs, path, contentType, download)); + } else send(404); +} + +void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){ + send(beginResponse(stream, contentType, len)); +} + +void AsyncWebServerRequest::send(String contentType, size_t len, AwsResponseFiller callback){ + send(beginResponse(contentType, len, callback)); +} + +void AsyncWebServerRequest::sendChunked(String contentType, AwsResponseFiller callback){ + send(beginChunkedResponse(contentType, callback)); +} + + +bool AsyncWebServerRequest::authenticate(const char * username, const char * password){ + if(_authorization.length()){ + char toencodeLen = os_strlen(username)+os_strlen(password)+1; + char *toencode = new char[toencodeLen]; + if(toencode == NULL){ + return false; + } + char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; + if(encoded == NULL){ + delete[] toencode; + return false; + } + sprintf(toencode, "%s:%s", username, password); + if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && _authorization.equals(encoded)){ + delete[] toencode; + delete[] encoded; + return true; + } + delete[] toencode; + delete[] encoded; + } + return false; +} + +bool AsyncWebServerRequest::authenticate(const char * hash){ + return (_authorization.length() && (_authorization == String(hash))); +} + +void AsyncWebServerRequest::requestAuthentication(){ + AsyncWebServerResponse * r = beginResponse(401); + r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); + send(r); +} + +bool AsyncWebServerRequest::hasArg(const char* name){ + AsyncWebParameter* h = _params; + while(h != NULL){ + if(h->name() == String(name)) + return true; + h = h->next; + } + return false; +} + +String AsyncWebServerRequest::arg(const char* name){ + AsyncWebParameter* h = _params; + while(h != NULL){ + if(h->name() == String(name)) + return h->value(); + h = h->next; + } + return String(); +} + +String AsyncWebServerRequest::arg(int i){ + return getParam(i)->value(); +} + +String AsyncWebServerRequest::argName(int i){ + return getParam(i)->name(); +} + +String AsyncWebServerRequest::header(const char* name){ + AsyncWebHeader* h = getHeader(String(name)); + if(h) + return h->value(); + return String(); +} + +String AsyncWebServerRequest::header(int i){ + AsyncWebHeader* h = getHeader(i); + if(h) + return h->value(); + return String(); +} + +String AsyncWebServerRequest::headerName(int i){ + AsyncWebHeader* h = getHeader(i); + if(h) + return h->name(); + return String(); +} + +bool AsyncWebServerRequest::hasHeader(const char* name){ + return hasHeader(String(name)); +} + + +String AsyncWebServerRequest::urlDecode(const String& text){ + String decoded = ""; + char temp[] = "0x00"; + unsigned int len = text.length(); + unsigned int i = 0; + 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 { + decodedChar = encodedChar; // normal ascii char + } + } + decoded += decodedChar; + } + return decoded; +} + + +const char * AsyncWebServerRequest::methodToString(){ + if(_method == HTTP_ANY) return "ANY"; + else if(_method == HTTP_GET) return "GET"; + else if(_method == HTTP_POST) return "POST"; + else if(_method == HTTP_DELETE) return "DELETE"; + else if(_method == HTTP_PUT) return "PUT"; + else if(_method == HTTP_PATCH) return "PATCH"; + else if(_method == HTTP_HEAD) return "HEAD"; + else if(_method == HTTP_OPTIONS) return "OPTIONS"; + return "UNKNOWN"; +} diff --git a/src/WebServerClient.cpp b/src/WebRequestParser.cpp similarity index 52% rename from src/WebServerClient.cpp rename to src/WebRequestParser.cpp index 143e3964..fc1212b0 100644 --- a/src/WebServerClient.cpp +++ b/src/WebRequestParser.cpp @@ -1,104 +1,95 @@ /* - * WebServerClient.cpp - * - * Created on: 18.12.2015 г. - * Author: ficeto - */ - + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include #include "ESPAsyncWebServer.h" -#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 }; -AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) - : _client(c) - , _server(s) - , _handler(NULL) - , _response(NULL) - , _interestingHeaders(new StringArray()) - , _temp() - , _parseState(0) - , _version(0) - , _method(HTTP_ANY) - , _url() - , _host() - , _contentType() - , _boundary() - , _authorization() - , _isMultipart(false) - , _isPlainPost(false) - , _expectingContinue(false) - , _contentLength(0) - , _parsedLength(0) - , _headers(NULL) - , _params(NULL) - , _multiParseState(0) - , _boundaryPosition(0) - , _itemStartIndex(0) - , _itemSize(0) - , _itemName() - , _itemFilename() - , _itemType() - , _itemValue() - , _itemBuffer(0) - , _itemBufferIndex(0) - , _itemIsFile(false) - , next(NULL) -{ - c->onError([](void *r, AsyncClient* c, int8_t error){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); - c->onAck([](void *r, AsyncClient* c, size_t len, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); - c->onDisconnect([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onDisconnect(); }, this); - c->onTimeout([](void *r, AsyncClient* c, uint32_t time){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); - c->onData([](void *r, AsyncClient* c, void *buf, size_t len){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); - c->onPoll([](void *r, AsyncClient* c){ AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onPoll(); }, this); +void AsyncWebServerRequest::_parseByte(uint8_t data){ + if((char)data != '\r' && (char)data != '\n') + _temp += (char)data; + if((char)data == '\n') + _parseLine(); } -AsyncWebServerRequest::~AsyncWebServerRequest(){ - while(_headers != NULL){ - AsyncWebHeader *h = _headers; - _headers = h->next; - delete h; - } - - while(_params != NULL){ - AsyncWebParameter *p = _params; - _params = p->next; - delete p; +void AsyncWebServerRequest::_parseLine(){ + if(_parseState == PARSE_REQ_START){ + if(_temp.length()){ + _parseReqHead();//head! + _parseState = PARSE_REQ_HEADERS; + } else { + _parseState = PARSE_REQ_FAIL; + _client->close(); + } + return; } - _interestingHeaders->free(); - delete _interestingHeaders; - - if(_response != NULL){ - delete _response; + if(_parseState == PARSE_REQ_HEADERS){ + if(!_temp.length()){ + _parseReqHeader();//header! + } else { + //end of headers + if(_expectingContinue){ + const char * response = "HTTP/1.1 100 Continue\r\n\r\n"; + _client->write(response, os_strlen(response)); + } + if(_contentLength){ + _parseState = PARSE_REQ_BODY; + } else { + _parseState = PARSE_REQ_END; + if(_handler) _handler->handleRequest(this); + else send(501); + } + } } - } void AsyncWebServerRequest::_onData(void *buf, size_t len){ if(_parseState < PARSE_REQ_BODY){ + //ToDo: make it parse the whole packet at once size_t i; for(i=0; ihandleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength); _parsedLength += len; } else { + //ToDo: make it parse the whole packet at once size_t i; for(i=0; i_finished() && _client->canSend()){ - _response->_ack(this, 0, 0); - } -} - -void AsyncWebServerRequest::_onAck(size_t len, uint32_t time){ - //os_printf("a:%u:%u\n", len, time); - if(_response != NULL){ - if(!_response->_finished()){ - _response->_ack(this, len, time); - } else { - AsyncWebServerResponse* r = _response; - _response = NULL; - delete r; - } - } -} - -void AsyncWebServerRequest::_onError(int8_t error){ - 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("TIMEOUT: %u, state: %s\n", time, _client->stateToString()); - _client->close(); -} - -void AsyncWebServerRequest::_onDisconnect(){ - //os_printf("d\n"); - _client->free(); - delete _client; - _server->_handleDisconnect(this); -} - -void AsyncWebServerRequest::_addParam(AsyncWebParameter *p){ - if(_params == NULL) - _params = p; - else { - AsyncWebParameter *ps = _params; - while(ps->next != NULL) ps = ps->next; - ps->next = p; - } -} +// +// REQUEST HEAD +// void AsyncWebServerRequest::_addGetParam(String param){ param = urlDecode(param); @@ -189,8 +139,8 @@ void AsyncWebServerRequest::_addGetParam(String param){ _addParam(new AsyncWebParameter(name, value)); } - bool AsyncWebServerRequest::_parseReqHead(){ + //Method String m = _temp.substring(0, _temp.indexOf(' ')); if(m == "GET"){ _method = HTTP_GET; @@ -208,6 +158,7 @@ bool AsyncWebServerRequest::_parseReqHead(){ _method = HTTP_OPTIONS; } + //URL _temp = _temp.substring(_temp.indexOf(' ')+1); String u = _temp.substring(0, _temp.indexOf(' ')); String g = String(); @@ -216,20 +167,23 @@ bool AsyncWebServerRequest::_parseReqHead(){ u = u.substring(0, u.indexOf('?')); } _url = u; + + //Parameters if(g.length()){ while(true){ if(g.length() == 0) break; if(g.indexOf('&') > 0){ - _addGetParam(g.substring(0, g.indexOf('&'))); + _addGetParam(g.substring(0, g.indexOf('&')));//GET Param g = g.substring(g.indexOf('&') + 1); } else { - _addGetParam(g); + _addGetParam(g);//Get Param break; } } } + //Version _temp = _temp.substring(_temp.indexOf(' ')+1); if(_temp.startsWith("HTTP/1.1")) _version = 1; @@ -237,6 +191,10 @@ bool AsyncWebServerRequest::_parseReqHead(){ return true; } +// +// HEADERS +// + bool AsyncWebServerRequest::_parseReqHeader(){ if(_temp.indexOf(':')){ AsyncWebHeader *h = new AsyncWebHeader(_temp); @@ -282,6 +240,10 @@ bool AsyncWebServerRequest::_parseReqHeader(){ return true; } +// +// PLAIN POST +// + void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){ if(data && (char)data != '&') _temp += (char)data; @@ -298,14 +260,9 @@ void AsyncWebServerRequest::_parsePlainPostChar(uint8_t data){ } } -void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){ - _itemBuffer[_itemBufferIndex++] = data; - if(last){ - if(_handler) - _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); - _itemBufferIndex = 0; - } -} +// +// MULTIPART POST +// enum { EXPECT_BOUNDARY, @@ -321,6 +278,15 @@ enum { PARSE_ERROR }; +void AsyncWebServerRequest::_handleUploadByte(uint8_t data, bool last){ + _itemBuffer[_itemBufferIndex++] = data; + if(last){ + if(_handler) + _handler->handleUpload(this, _itemFilename, _itemSize - _itemBufferIndex, _itemBuffer, _itemBufferIndex, false); + _itemBufferIndex = 0; + } +} + void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ #define itemWriteByte(b) do { _itemSize++; if(_itemIsFile) _handleUploadByte(b, last); else _itemValue+=(char)(b); } while(0) @@ -475,310 +441,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last){ } } -void AsyncWebServerRequest::_parseLine(){ - if(_parseState == PARSE_REQ_START){ - if(!_temp.length()){ - _parseState = PARSE_REQ_FAIL; - _client->close(); - } else { - _parseReqHead(); - _parseState = PARSE_REQ_HEADERS; - } - return; - } - if(_parseState == PARSE_REQ_HEADERS){ - if(!_temp.length()){ - //end of headers - if(_expectingContinue){ - const char * response = "HTTP/1.1 100 Continue\r\n\r\n"; - _client->write(response, os_strlen(response)); - } - if(_contentLength){ - _parseState = PARSE_REQ_BODY; - } else { - _parseState = PARSE_REQ_END; - if(_handler) _handler->handleRequest(this); - else send(501); - } - } else _parseReqHeader(); - } -} -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; - AsyncWebHeader* h = _headers; - while(h != NULL){ - i++; h = h->next; - } - return i; -} - -bool AsyncWebServerRequest::hasHeader(String name){ - AsyncWebHeader* h = _headers; - while(h != NULL){ - if(h->name() == name) - return true; - h = h->next; - } - return false; -} - -AsyncWebHeader* AsyncWebServerRequest::getHeader(String name){ - AsyncWebHeader* h = _headers; - while(h != NULL){ - if(h->name() == name) - return h; - h = h->next; - } - return NULL; -} - -AsyncWebHeader* AsyncWebServerRequest::getHeader(int num){ - int i = 0; - AsyncWebHeader* h = _headers; - while(h != NULL){ - if(num == i++) - return h; - h = h->next; - } - return NULL; -} - -int AsyncWebServerRequest::params(){ - int i = 0; - AsyncWebParameter* h = _params; - while(h != NULL){ - i++; h = h->next; - } - return i; -} - -bool AsyncWebServerRequest::hasParam(String name, bool post, bool file){ - AsyncWebParameter* h = _params; - while(h != NULL){ - if(h->name() == name && h->isPost() == post && h->isFile() == file) - return true; - h = h->next; - } - return false; -} - -AsyncWebParameter* AsyncWebServerRequest::getParam(String name, bool post, bool file){ - AsyncWebParameter* h = _params; - while(h != NULL){ - if(h->name() == name && h->isPost() == post && h->isFile() == file) - return h; - h = h->next; - } - return NULL; -} - -AsyncWebParameter* AsyncWebServerRequest::getParam(int num){ - int i = 0; - AsyncWebParameter* h = _params; - while(h != NULL){ - if(num == i++) - return h; - h = h->next; - } - return NULL; -} - -void AsyncWebServerRequest::addInterestingHeader(String name){ - if(!_interestingHeaders->contains(name)) _interestingHeaders->add(name); -} - - -void AsyncWebServerRequest::send(AsyncWebServerResponse *response){ - _response = response; - _response->_respond(this); -} - - - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, String contentType, String content){ - return new AsyncBasicResponse(code, contentType, content); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(FS &fs, String path, String contentType, bool download){ - if(fs.exists(path) || (!download && fs.exists(path+".gz"))) - return new AsyncFileResponse(fs, path, contentType, download); - return NULL; -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(Stream &stream, String contentType, size_t len){ - return new AsyncStreamResponse(stream, contentType, len); -} - -AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(String contentType, size_t len, AwsResponseFiller callback){ - 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 bufferSize){ - return new AsyncResponseStream(contentType, bufferSize); -} - -void AsyncWebServerRequest::send(int code, String contentType, String 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(beginResponse(fs, path, contentType, download)); - } else send(404); -} - -void AsyncWebServerRequest::send(Stream &stream, String contentType, size_t len){ - send(beginResponse(stream, contentType, len)); -} - -void AsyncWebServerRequest::send(String contentType, size_t len, AwsResponseFiller callback){ - send(beginResponse(contentType, len, callback)); -} - -void AsyncWebServerRequest::sendChunked(String contentType, AwsResponseFiller callback){ - send(beginChunkedResponse(contentType, callback)); -} - - -bool AsyncWebServerRequest::authenticate(const char * username, const char * password){ - if(_authorization.length()){ - char toencodeLen = os_strlen(username)+os_strlen(password)+1; - char *toencode = new char[toencodeLen]; - if(toencode == NULL){ - return false; - } - char *encoded = new char[base64_encode_expected_len(toencodeLen)+1]; - if(encoded == NULL){ - delete[] toencode; - return false; - } - sprintf(toencode, "%s:%s", username, password); - if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && _authorization.equals(encoded)){ - delete[] toencode; - delete[] encoded; - return true; - } - delete[] toencode; - delete[] encoded; - } - return false; -} - -bool AsyncWebServerRequest::authenticate(const char * hash){ - return (_authorization.length() && (_authorization == String(hash))); -} - -void AsyncWebServerRequest::requestAuthentication(){ - AsyncWebServerResponse * r = beginResponse(401); - r->addHeader("WWW-Authenticate", "Basic realm=\"Login Required\""); - send(r); -} - -bool AsyncWebServerRequest::hasArg(const char* name){ - AsyncWebParameter* h = _params; - while(h != NULL){ - if(h->name() == String(name)) - return true; - h = h->next; - } - return false; -} -String AsyncWebServerRequest::arg(const char* name){ - AsyncWebParameter* h = _params; - while(h != NULL){ - if(h->name() == String(name)) - return h->value(); - h = h->next; - } - return String(); -} - -String AsyncWebServerRequest::arg(int i){ - return getParam(i)->value(); -} -String AsyncWebServerRequest::argName(int i){ - return getParam(i)->name(); -} - -String AsyncWebServerRequest::header(const char* name){ - AsyncWebHeader* h = getHeader(String(name)); - if(h) - return h->value(); - return String(); -} - -String AsyncWebServerRequest::header(int i){ - AsyncWebHeader* h = getHeader(i); - if(h) - return h->value(); - return String(); -} - -String AsyncWebServerRequest::headerName(int i){ - AsyncWebHeader* h = getHeader(i); - if(h) - return h->name(); - return String(); -} - -bool AsyncWebServerRequest::hasHeader(const char* name){ - return hasHeader(String(name)); -} - - -String AsyncWebServerRequest::urlDecode(const String& text){ - String decoded = ""; - char temp[] = "0x00"; - unsigned int len = text.length(); - unsigned int i = 0; - 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 { - decodedChar = encodedChar; // normal ascii char - } - } - decoded += decodedChar; - } - return decoded; -} - - -const char * AsyncWebServerRequest::methodToString(){ - if(_method == HTTP_ANY) return "ANY"; - else if(_method == HTTP_GET) return "GET"; - else if(_method == HTTP_POST) return "POST"; - else if(_method == HTTP_DELETE) return "DELETE"; - else if(_method == HTTP_PUT) return "PUT"; - else if(_method == HTTP_PATCH) return "PATCH"; - else if(_method == HTTP_HEAD) return "HEAD"; - else if(_method == HTTP_OPTIONS) return "OPTIONS"; - return "UNKNOWN"; -} diff --git a/src/AsyncWebServerResponseImpl.h b/src/WebResponseImpl.h similarity index 72% rename from src/AsyncWebServerResponseImpl.h rename to src/WebResponseImpl.h index ca13ee3f..4a3b4a96 100644 --- a/src/AsyncWebServerResponseImpl.h +++ b/src/WebResponseImpl.h @@ -1,13 +1,29 @@ /* - * AsyncWebImpl.h - * - * Created on: 19.12.2015 г. - * Author: ficeto - */ + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ #ifndef ASYNCWEBSERVERRESPONSEIMPL_H_ #define ASYNCWEBSERVERRESPONSEIMPL_H_ +#include "ESPAsyncWebServer.h" + class AsyncBasicResponse: public AsyncWebServerResponse { private: String _content; diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index de673f02..cbf8fb89 100644 --- a/src/WebResponses.cpp +++ b/src/WebResponses.cpp @@ -1,5 +1,25 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include #include "ESPAsyncWebServer.h" -#include "AsyncWebServerResponseImpl.h" #include "cbuf.h" /* @@ -265,7 +285,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u if(outLen) outLen = request->client()->write((const char*)buf, outLen); - _sentLength += outLen; + if(_chunked) + _sentLength += readLen; + else + _sentLength += outLen; free(buf); if((_chunked && readLen == 0) || (!_sendContentLength && outLen == 0) || _sentLength == _contentLength){ @@ -290,7 +313,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest *request, size_t len, u if(!_sendContentLength || _ackedLength >= (_headLength+_contentLength)){ _state = RESPONSE_END; if(!_chunked && !_sendContentLength) - request->client()->close(true); + request->client()->close(false); } } return 0; @@ -381,7 +404,7 @@ AsyncCallbackResponse::AsyncCallbackResponse(String contentType, size_t len, Aws } size_t AsyncCallbackResponse::_fillBuffer(uint8_t *data, size_t len){ - return _content(data, len); + return _content(data, len, _sentLength); } /* @@ -398,7 +421,7 @@ AsyncChunkedResponse::AsyncChunkedResponse(String contentType, AwsResponseFiller } size_t AsyncChunkedResponse::_fillBuffer(uint8_t *data, size_t len){ - return _content(data, len); + return _content(data, len, _sentLength); } diff --git a/src/WebServer.cpp b/src/WebServer.cpp index d4d40a14..68a5a7d7 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -1,5 +1,25 @@ +/* + Asynchronous WebServer library for Espressif MCUs + + Copyright (c) 2016 Hristo Gochkov. All rights reserved. + This file is part of the esp8266 core for Arduino environment. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ +#include #include "ESPAsyncWebServer.h" -#include "AsyncWebServerHandlerImpl.h" AsyncWebServer::AsyncWebServer(uint16_t port):_server(port), _handlers(0), _catchAllHandler(new AsyncCallbackWebHandler()){ From 9296fb1b1e92a785b470e8fdf489524e5db6d2e8 Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 9 Feb 2016 01:51:36 +0200 Subject: [PATCH 2/3] get the toolchain from the repo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 340a1c54..96d2660e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ script: - cd espressif - git clone https://github.com/me-no-dev/ESP31B.git ESP31B - cd ESP31B/tools - - wget http://static.ficeto.com/xtensa-esp108-elf-linux64.tar.gz + - wget https://github.com/me-no-dev/ESP31B/releases/download/0.0.1/xtensa-esp108-elf-linux64.tar.gz - tar zxvf xtensa-esp108-elf-linux64.tar.gz - chmod +x xtensa-esp108-elf/bin/* - chmod +x xtensa-esp108-elf/xtensa-esp108-elf/bin/* From c80722cf2fc14ee693d0ec14b357ea7d551cbf1d Mon Sep 17 00:00:00 2001 From: Me No Dev Date: Tue, 9 Feb 2016 01:54:25 +0200 Subject: [PATCH 3/3] remove blank line --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 96d2660e..837dd8e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,6 @@ script: - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16 - sleep 3 - export DISPLAY=:1.0 - - wget http://downloads.arduino.cc/arduino-1.6.5-linux64.tar.xz - tar xf arduino-1.6.5-linux64.tar.xz - mv arduino-1.6.5 $HOME/arduino_ide