From 1b69ae6c0b3cb24181e885e0c6d6d45651db46ba Mon Sep 17 00:00:00 2001 From: salasidis <45833317+salasidis@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:03:12 -0400 Subject: [PATCH 01/12] Add files via upload --- src/Portenta_H7_AsyncWebRequest.cpp | 18 +++ src/Portenta_H7_AsyncWebResponseImpl.h | 2 + src/Portenta_H7_AsyncWebResponses.cpp | 173 ++++++++++++++++++++++--- src/Portenta_H7_AsyncWebServer.h | 2 + 4 files changed, 174 insertions(+), 21 deletions(-) diff --git a/src/Portenta_H7_AsyncWebRequest.cpp b/src/Portenta_H7_AsyncWebRequest.cpp index 0149d35..6b10d1b 100644 --- a/src/Portenta_H7_AsyncWebRequest.cpp +++ b/src/Portenta_H7_AsyncWebRequest.cpp @@ -1101,6 +1101,13 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) } } +//RSMOD/////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const char * content) +{ + return new AsyncBasicResponse(code, contentType, content); +} + ///////////////////////////////////////////////// AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const String& content) @@ -1139,6 +1146,17 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& c return new AsyncResponseStream(contentType, bufferSize); } +//RSMOD/////////////////////////////////////////////// + +void AsyncWebServerRequest::send(int code, const String& contentType, const char *content, bool nonDetructiveSend) +{ + if (nonDetructiveSend == true) { + send(beginResponse(code, contentType, String(content))); // for backwards compatibility + } else { + send(beginResponse(code, contentType, content)); + } + } + ///////////////////////////////////////////////// void AsyncWebServerRequest::send(int code, const String& contentType, const String& content) diff --git a/src/Portenta_H7_AsyncWebResponseImpl.h b/src/Portenta_H7_AsyncWebResponseImpl.h index 6e8e0e1..f0c1d67 100644 --- a/src/Portenta_H7_AsyncWebResponseImpl.h +++ b/src/Portenta_H7_AsyncWebResponseImpl.h @@ -43,9 +43,11 @@ class AsyncBasicResponse: public AsyncWebServerResponse { private: String _content; + char *_contentCstr; // RSMOD public: AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); + AsyncBasicResponse(int code, const String& contentType, const char *content); // RSMOD void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); diff --git a/src/Portenta_H7_AsyncWebResponses.cpp b/src/Portenta_H7_AsyncWebResponses.cpp index 1a88d29..a5441ea 100644 --- a/src/Portenta_H7_AsyncWebResponses.cpp +++ b/src/Portenta_H7_AsyncWebResponses.cpp @@ -247,6 +247,31 @@ size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, return 0; } + +//RSMOD/////////////////////////////////////////////// + +/* + String/Code Response + * */ +AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const char *content) +{ + _code = code; + _content = String(""); + _contentCstr = (char *)content; + _contentType = contentType; + int iLen; + + if ((iLen = strlen(_contentCstr))) + { + _contentLength = iLen; + + if (!_contentType.length()) + _contentType = "text/plain"; + } + + addHeader("Connection", "close"); +} + ///////////////////////////////////////////////// /* @@ -256,6 +281,7 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons { _code = code; _content = content; + _contentCstr = NULL; // RSMOD _contentType = contentType; if (_content.length()) @@ -277,43 +303,113 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) String out = _assembleHead(request->version()); size_t outLen = out.length(); size_t space = request->client()->space(); + + // Serial.println(""); + // Serial.println(""); + // Serial.println(""); + // Serial.println("");; + // Serial.println("Pre _respond\n"); + // Serial.print("_contentLength ="); + // Serial.println(_contentLength); + // Serial.print("out="); + // Serial.println(out); + // Serial.print("outLen="); + // Serial.println(outLen); + //// Serial.println("_contentCstr="); + //// Serial.println(_contentCstr); if (!_contentLength && space >= outLen) { + // Serial.println(""); + // Serial.println("In A ******"); + // Serial.println(""); _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_WAIT_ACK; } else if (_contentLength && space >= outLen + _contentLength) { - out += _content; - outLen += _contentLength; - _writtenLength += request->client()->write(out.c_str(), outLen); + // Serial.println(""); + // Serial.println("In B ******"); + // Serial.println(""); + if (_contentCstr) { + memmove(&_contentCstr[outLen], _contentCstr, _contentLength); + memcpy(_contentCstr, out.c_str(), outLen); + outLen += _contentLength; + // Serial.print(_contentCstr); + _writtenLength += request->client()->write(_contentCstr, outLen); + } else { + out += _content; + outLen += _contentLength; + _writtenLength += request->client()->write(out.c_str(), outLen); + } _state = RESPONSE_WAIT_ACK; } else if (space && space < outLen) { - String partial = out.substring(0, space); - _content = out.substring(space) + _content; - _contentLength += outLen - space; - _writtenLength += request->client()->write(partial.c_str(), partial.length()); + String partial = out.substring(0, space); + // Serial.println(""); + // Serial.println("In C ******"); + // Serial.println(""); + if (_contentCstr) { + int deltaLen = out.length() - partial.length(); + memmove(&_contentCstr[deltaLen], _contentCstr, deltaLen); + memcpy(_contentCstr, out.substring(space).c_str(), deltaLen); + } else { + _content = out.substring(space) + _content; + } + _contentLength += outLen - space; + // Serial.print(partial); + _writtenLength += request->client()->write(partial.c_str(), partial.length()); _state = RESPONSE_CONTENT; } else if (space > outLen && space < (outLen + _contentLength)) { - size_t shift = space - outLen; - outLen += shift; - _sentLength += shift; - out += _content.substring(0, shift); - _content = _content.substring(shift); - _writtenLength += request->client()->write(out.c_str(), outLen); + size_t shift = space - outLen; + // Serial.println(""); + // Serial.println("In D ******"); + // Serial.println(""); + + outLen += shift; + _sentLength += shift; + if (_contentCstr) { + char *s = (char *)malloc(shift +1); + strncpy(s, _contentCstr, shift); + s[shift] = '\0'; + out += String(s); + _contentCstr += shift; + free(s); + } else { + out += _content.substring(0, shift); + _content = _content.substring(shift); + } + // Serial.print(out); + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_CONTENT; } else { - _content = out + _content; - _contentLength += outLen; + // Serial.println(""); + // Serial.println("In E ******"); + // Serial.println(""); + if (_contentCstr) { + memmove(&_contentCstr[outLen], _contentCstr, _contentLength); + memcpy(_contentCstr, out.c_str(), outLen); + } else { + _content = out + _content; + } + _contentLength += outLen; _state = RESPONSE_CONTENT; } + + // Serial.print("\n\n\n\n\n\n\nPost _respond\n"); + // Serial.print("_contentLength ="); + // Serial.println(_contentLength); + // Serial.print("out="); + // Serial.println(out); + // Serial.print("outLen="); + // Serial.println(outLen); + //// Serial.println("_contentCstr="); + //// Serial.println(_contentCstr); } ///////////////////////////////////////////////// @@ -322,28 +418,58 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint { PORTENTA_H7_AWS_UNUSED(time); + // Serial.print("\n\n\n\n\n\n\nPre _ack\n"); + // Serial.print("_contentLength ="); + // Serial.print(_contentLength); + _ackedLength += len; if (_state == RESPONSE_CONTENT) { + String out; size_t available = _contentLength - _sentLength; size_t space = request->client()->space(); + + // Serial.print(" available="); + // Serial.print(available); + // Serial.print(" space ="); + // Serial.print(space); + //we can fit in this packet if (space > available) { - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); + // Serial.println("In space>available"); + if (_contentCstr) { + // Serial.println("output="); + // Serial.println(_contentCstr); + _writtenLength += request->client()->write(_contentCstr, available); + //_contentCstr[0] = '\0'; + } else { + _writtenLength += request->client()->write(_content.c_str(), available); + _content = String(); + } _state = RESPONSE_WAIT_ACK; return available; } //send some data, the rest on ack - String out = _content.substring(0, space); - _content = _content.substring(space); - _sentLength += space; - _writtenLength += request->client()->write(out.c_str(), space); + if (_contentCstr) { + char *s = (char *)malloc(space +1); + strncpy(s, _contentCstr, space); + s[space] = '\0'; + out = String(s); + _contentCstr += space; + free(s); + } else { + out = _content.substring(0, space); + _content = _content.substring(space); + } + _sentLength += space; + // Serial.println("output="); + // Serial.println(out); + _writtenLength += request->client()->write(out.c_str(), space); return space; } @@ -354,6 +480,11 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint _state = RESPONSE_END; } } + + // Serial.print("\n\n\n\n\n\n\npost _ack"); + // Serial.print("_contentLength ="); + // Serial.print(_contentLength); + // Serial.print(_contentCstr); return 0; } diff --git a/src/Portenta_H7_AsyncWebServer.h b/src/Portenta_H7_AsyncWebServer.h index c4fc2c4..2001882 100644 --- a/src/Portenta_H7_AsyncWebServer.h +++ b/src/Portenta_H7_AsyncWebServer.h @@ -340,12 +340,14 @@ class AsyncWebServerRequest void send(AsyncWebServerResponse *response); void send(int code, const String& contentType = String(), const String& content = String()); + void send(int code, const String& contentType, const char *content, bool nonDetructiveSend = true); // RSMOD void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncWebServerResponse *beginResponse(int code, const String& contentType = String(), const String& content = String()); + AsyncWebServerResponse *beginResponse(int code, const String& contentType, const char * content); // RSMOD AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); From c5e66c8ed94e98bd38ea8df193fc51ea6aead0fe Mon Sep 17 00:00:00 2001 From: salasidis <45833317+salasidis@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:07:11 -0400 Subject: [PATCH 02/12] Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index 7382fb6..5e1218b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,21 @@ --- --- + +***** +Added a destructive C string interface to send + +if called with +request->send(200, textPlainStr, ArduinoStr); // no difference +request->send(200, textPlainStr, cStr); // no differfence +request->send(200, textPlainStr, cStr, false); // this will have to be a cString with enough space to fit the header, it will be memmoved as required, header added, and sent, without creating any large arduino Strings that consume heap + +in my application max heap size went from 375k to 66k (which is the lowest possible wit the variables and otehr data I am using) + +Seems to work, let mw know what you think + + + ## Table of contents * [Table of contents](#table-of-contents) From 1a0f6be818af77ca527cff694ab8ff88d3daf900 Mon Sep 17 00:00:00 2001 From: salasidis <45833317+salasidis@users.noreply.github.com> Date: Sat, 1 Oct 2022 20:08:34 -0400 Subject: [PATCH 03/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e1218b..eeb95e8 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ request->send(200, textPlainStr, cStr, false); // this will have to be a cString in my application max heap size went from 375k to 66k (which is the lowest possible wit the variables and otehr data I am using) -Seems to work, let mw know what you think +Seems to work, let me know what you think From 3f11c768eaa94f614afbdb4ddb9def420267f858 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Sun, 2 Oct 2022 02:17:48 -0400 Subject: [PATCH 04/12] Update from RSMOD Change from original RSMOD - Use `AWS_LOGDEBUGx` - Correct indentation - Style --- src/Portenta_H7_AsyncWebRequest.cpp | 70 ++++++--- src/Portenta_H7_AsyncWebResponseImpl.h | 68 ++++---- src/Portenta_H7_AsyncWebResponses.cpp | 168 ++++++++++++++++++-- src/Portenta_H7_AsyncWebServer.h | 210 +++++++++++++------------ 4 files changed, 338 insertions(+), 178 deletions(-) diff --git a/src/Portenta_H7_AsyncWebRequest.cpp b/src/Portenta_H7_AsyncWebRequest.cpp index 0149d35..0fc14bb 100644 --- a/src/Portenta_H7_AsyncWebRequest.cpp +++ b/src/Portenta_H7_AsyncWebRequest.cpp @@ -64,7 +64,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onError([](void *r, AsyncClient * c, int8_t error) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); @@ -72,7 +72,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); @@ -87,7 +87,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onTimeout([](void *r, AsyncClient * c, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); @@ -95,7 +95,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onData([](void *r, AsyncClient * c, void *buf, size_t len) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); @@ -103,7 +103,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onPoll([](void *r, AsyncClient * c) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); @@ -157,7 +157,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) // No new line, just add the buffer in _temp char ch = str[len - 1]; str[len - 1] = 0; - + _temp.reserve(_temp.length() + len); _temp.concat(str); _temp.concat(ch); @@ -166,7 +166,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) { // Found new line - extract it and parse str[i] = 0; // Terminate the string at the end of the line. - + _temp.concat(str); _temp.trim(); _parseLine(); @@ -260,7 +260,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) break; } - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -287,10 +287,10 @@ void AsyncWebServerRequest::_removeNotInterestingHeaders() void AsyncWebServerRequest::_onPoll() { if (_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()) - { + { _response->_ack(this, 0, 0); } - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -315,7 +315,7 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) delete r; } } - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -337,7 +337,7 @@ void AsyncWebServerRequest::_onTimeout(uint32_t time) AWS_LOGINFO3("TIMEOUT: time =", time, ", state =", _client->stateToString()); _client->close(); - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -360,7 +360,7 @@ void AsyncWebServerRequest::_onDisconnect() } _server->_handleDisconnect(this); - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -656,19 +656,19 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (_parsedLength < 2 && data != '-') { _multiParseState = PARSE_ERROR; - + return; } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) { _multiParseState = PARSE_ERROR; - + return; } else if (_parsedLength - 2 == _boundary.length() && data != '\r') { _multiParseState = PARSE_ERROR; - + return; } else if (_parsedLength - 3 == _boundary.length()) @@ -676,7 +676,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (data != '\n') { _multiParseState = PARSE_ERROR; - + return; } @@ -754,7 +754,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (_itemBuffer == NULL) { _multiParseState = PARSE_ERROR; - + return; } @@ -829,7 +829,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) else if (_boundaryPosition == _boundary.length() - 1) { _multiParseState = DASH3_OR_RETURN2; - + if (!_itemIsFile) { _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); @@ -862,7 +862,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { AWS_LOGDEBUG1("ERROR: The parser got to the end of the POST but is expecting more bytes =", (_contentLength - _parsedLength - 4)); AWS_LOGDEBUG("Drop an issue so we can have more info on the matter!"); - + _contentLength = _parsedLength + 4;//lets close the request gracefully } @@ -1080,16 +1080,16 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) _response = response; if (_response == NULL) - { + { _client->close(true); - + _onDisconnect(); return; } if (!_response->_sourceValid()) - { + { delete response; _response = NULL; send(500); @@ -1098,7 +1098,14 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) { _client->setRxTimeout(0); _response->_respond(this); - } + } +} + +//RSMOD/////////////////////////////////////////////// + +AsyncWebServerResponse * AsyncWebServerRequest::beginResponse(int code, const String& contentType, const char * content) +{ + return new AsyncBasicResponse(code, contentType, content); } ///////////////////////////////////////////////// @@ -1139,6 +1146,20 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& c return new AsyncResponseStream(contentType, bufferSize); } +//RSMOD/////////////////////////////////////////////// + +void AsyncWebServerRequest::send(int code, const String& contentType, const char *content, bool nonDetructiveSend) +{ + if (nonDetructiveSend) + { + send(beginResponse(code, contentType, String(content))); // for backwards compatibility + } + else + { + send(beginResponse(code, contentType, content)); + } +} + ///////////////////////////////////////////////// void AsyncWebServerRequest::send(int code, const String& contentType, const String& content) @@ -1447,4 +1468,3 @@ bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType } ///////////////////////////////////////////////// - diff --git a/src/Portenta_H7_AsyncWebResponseImpl.h b/src/Portenta_H7_AsyncWebResponseImpl.h index 6e8e0e1..481b502 100644 --- a/src/Portenta_H7_AsyncWebResponseImpl.h +++ b/src/Portenta_H7_AsyncWebResponseImpl.h @@ -39,18 +39,20 @@ ///////////////////////////////////////////////// -class AsyncBasicResponse: public AsyncWebServerResponse +class AsyncBasicResponse: public AsyncWebServerResponse { private: String _content; - + char *_contentCstr; // RSMOD + public: AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); + AsyncBasicResponse(int code, const String& contentType, const char *content = nullptr); // RSMOD void _respond(AsyncWebServerRequest *request); - + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - - bool _sourceValid() const + + bool _sourceValid() const { return true; } @@ -58,7 +60,7 @@ class AsyncBasicResponse: public AsyncWebServerResponse ///////////////////////////////////////////////// -class AsyncAbstractResponse: public AsyncWebServerResponse +class AsyncAbstractResponse: public AsyncWebServerResponse { private: String _head; @@ -69,21 +71,21 @@ class AsyncAbstractResponse: public AsyncWebServerResponse std::vector _cache; size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); - + protected: AwsTemplateProcessor _callback; - + public: AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - - bool _sourceValid() const + + bool _sourceValid() const { return false; } - - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) + + virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } @@ -92,64 +94,64 @@ class AsyncAbstractResponse: public AsyncWebServerResponse ///////////////////////////////////////////////// #ifndef TEMPLATE_PLACEHOLDER - #define TEMPLATE_PLACEHOLDER '%' +#define TEMPLATE_PLACEHOLDER '%' #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 ///////////////////////////////////////////////// -class AsyncStreamResponse: public AsyncAbstractResponse +class AsyncStreamResponse: public AsyncAbstractResponse { private: Stream *_content; - + public: AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - - bool _sourceValid() const + + bool _sourceValid() const { return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncCallbackResponse: public AsyncAbstractResponse +class AsyncCallbackResponse: public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; - + public: AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - - bool _sourceValid() const + + bool _sourceValid() const { return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncChunkedResponse: public AsyncAbstractResponse +class AsyncChunkedResponse: public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; - + public: AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - - bool _sourceValid() const + + bool _sourceValid() const { return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; @@ -159,20 +161,20 @@ class cbuf; ///////////////////////////////////////////////// -class AsyncResponseStream: public AsyncAbstractResponse, public Print +class AsyncResponseStream: public AsyncAbstractResponse, public Print { private: cbuf *_content; - + public: AsyncResponseStream(const String& contentType, size_t bufferSize); ~AsyncResponseStream(); - - bool _sourceValid() const + + bool _sourceValid() const { return (_state < RESPONSE_END); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); diff --git a/src/Portenta_H7_AsyncWebResponses.cpp b/src/Portenta_H7_AsyncWebResponses.cpp index 1a88d29..fc34f35 100644 --- a/src/Portenta_H7_AsyncWebResponses.cpp +++ b/src/Portenta_H7_AsyncWebResponses.cpp @@ -53,7 +53,7 @@ void* memchr(void* ptr, int ch, size_t count) * */ const char* AsyncWebServerResponse::_responseCodeToString(int code) { - switch (code) + switch (code) { case 100: return "Continue"; case 101: return "Switching Protocols"; @@ -232,7 +232,7 @@ bool AsyncWebServerResponse::_sourceValid() const void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_END; - + request->client()->close(); } @@ -243,10 +243,36 @@ size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, PORTENTA_H7_AWS_UNUSED(request); PORTENTA_H7_AWS_UNUSED(len); PORTENTA_H7_AWS_UNUSED(time); - + return 0; } + +//RSMOD/////////////////////////////////////////////// + +/* + String/Code Response + * */ +AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, const char *content) +{ + _code = code; + _content = String(""); + _contentCstr = (char *)content; // RSMOD + _contentType = contentType; + + int iLen; + + if ((iLen = strlen(_contentCstr))) + { + _contentLength = iLen; + + if (!_contentType.length()) + _contentType = "text/plain"; + } + + addHeader("Connection", "close"); +} + ///////////////////////////////////////////////// /* @@ -256,12 +282,13 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons { _code = code; _content = content; + _contentCstr = nullptr; // RSMOD _contentType = contentType; if (_content.length()) { _contentLength = _content.length(); - + if (!_contentType.length()) _contentType = "text/plain"; } @@ -278,42 +305,115 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) size_t outLen = out.length(); size_t space = request->client()->space(); + AWS_LOGDEBUG3("AsyncAbstractResponse::_respond : Pre_respond, _contentLength =", _contentLength, ", out =", out ); + AWS_LOGDEBUG3("outLen =", outLen, ", _contentCstr =", _contentCstr); + if (!_contentLength && space >= outLen) { + AWS_LOGDEBUG("Step 1"); + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_WAIT_ACK; } else if (_contentLength && space >= outLen + _contentLength) { - out += _content; - outLen += _contentLength; - _writtenLength += request->client()->write(out.c_str(), outLen); + AWS_LOGDEBUG("Step 2"); + + if (_contentCstr) + { + memmove(&_contentCstr[outLen], _contentCstr, _contentLength); + memcpy(_contentCstr, out.c_str(), outLen); + outLen += _contentLength; + + AWS_LOGDEBUG1("_contentCstr =", _contentCstr); + + _writtenLength += request->client()->write(_contentCstr, outLen); + } + else + { + out += _content; + outLen += _contentLength; + _writtenLength += request->client()->write(out.c_str(), outLen); + } + _state = RESPONSE_WAIT_ACK; } else if (space && space < outLen) { String partial = out.substring(0, space); - _content = out.substring(space) + _content; + + AWS_LOGDEBUG("Step 3"); + + if (_contentCstr) + { + int deltaLen = out.length() - partial.length(); + + memmove(&_contentCstr[deltaLen], _contentCstr, deltaLen); + memcpy(_contentCstr, out.substring(space).c_str(), deltaLen); + } + else + { + _content = out.substring(space) + _content; + } + _contentLength += outLen - space; + + AWS_LOGDEBUG1("partial =", partial); + _writtenLength += request->client()->write(partial.c_str(), partial.length()); _state = RESPONSE_CONTENT; } else if (space > outLen && space < (outLen + _contentLength)) { size_t shift = space - outLen; + + AWS_LOGDEBUG("Step 4"); + outLen += shift; _sentLength += shift; - out += _content.substring(0, shift); - _content = _content.substring(shift); + + if (_contentCstr) + { + char *s = (char *)malloc(shift + 1); + + strncpy(s, _contentCstr, shift); + s[shift] = '\0'; + out += String(s); + _contentCstr += shift; + + free(s); + } + else + { + out += _content.substring(0, shift); + _content = _content.substring(shift); + } + + AWS_LOGDEBUG1("out =", out); + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_CONTENT; } else { - _content = out + _content; + AWS_LOGDEBUG("Step 5"); + + if (_contentCstr) + { + memmove(&_contentCstr[outLen], _contentCstr, _contentLength); + memcpy(_contentCstr, out.c_str(), outLen); + } + else + { + _content = out + _content; + } + _contentLength += outLen; _state = RESPONSE_CONTENT; } + + AWS_LOGDEBUG3("AsyncAbstractResponse::_respond : Post_respond, _contentLength =", _contentLength, ", out =", out ); + AWS_LOGDEBUG3("outLen =", outLen, ", _contentCstr =", _contentCstr); } ///////////////////////////////////////////////// @@ -321,28 +421,62 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); - + + AWS_LOGDEBUG1("AsyncAbstractResponse::_ack : Pre_ack, _contentLength =", _contentLength); + _ackedLength += len; if (_state == RESPONSE_CONTENT) { + String out; size_t available = _contentLength - _sentLength; size_t space = request->client()->space(); + AWS_LOGDEBUG3("AsyncAbstractResponse::_ack : available =", available, ", space =", space ); + //we can fit in this packet if (space > available) { - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); + // Serial.println("In space>available"); + AWS_LOGDEBUG1("AsyncAbstractResponse::_ack : Pre_ack, _contentLength =", _contentLength); + + if (_contentCstr) + { + AWS_LOGDEBUG1("In space>available : output =", _contentCstr); + + _writtenLength += request->client()->write(_contentCstr, available); + //_contentCstr[0] = '\0'; + } + else + { + _writtenLength += request->client()->write(_content.c_str(), available); + _content = String(); + } _state = RESPONSE_WAIT_ACK; return available; } //send some data, the rest on ack - String out = _content.substring(0, space); - _content = _content.substring(space); + if (_contentCstr) + { + char *s = (char *)malloc(space + 1); + strncpy(s, _contentCstr, space); + s[space] = '\0'; + out = String(s); + _contentCstr += space; + free(s); + } + else + { + out = _content.substring(0, space); + _content = _content.substring(space); + } + _sentLength += space; + + AWS_LOGDEBUG1("In space>available : output =", out); + _writtenLength += request->client()->write(out.c_str(), space); return space; @@ -355,6 +489,8 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint } } + AWS_LOGDEBUG3("AsyncAbstractResponse::_ack : Post_ack, _contentLength =", _contentLength, ", _contentCstr =", _contentCstr); + return 0; } diff --git a/src/Portenta_H7_AsyncWebServer.h b/src/Portenta_H7_AsyncWebServer.h index c4fc2c4..4a0493a 100644 --- a/src/Portenta_H7_AsyncWebServer.h +++ b/src/Portenta_H7_AsyncWebServer.h @@ -29,8 +29,8 @@ #else - #error For Portenta_H7 only - +#error For Portenta_H7 only + #endif #define PORTENTA_H7_ASYNC_WEBSERVER_VERSION "Portenta_H7_AsyncWebServer v1.3.0" @@ -76,7 +76,7 @@ class AsyncCallbackWebHandler; class AsyncResponseStream; #ifndef WEBSERVER_H - typedef enum + typedef enum { HTTP_GET = 0b00000001, HTTP_POST = 0b00000010, @@ -99,7 +99,7 @@ typedef std::function ArDisconnectHandler; PARAMETER :: Chainable object to hold GET/POST and FILE parameters * */ -class AsyncWebParameter +class AsyncWebParameter { private: String _name; @@ -111,28 +111,28 @@ class AsyncWebParameter public: AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} - - const String& name() const + + const String& name() const { return _name; } - - const String& value() const + + const String& value() const { return _value; } - - size_t size() const + + size_t size() const { return _size; } - - bool isPost() const + + bool isPost() const { return _isForm; } - - bool isFile() const + + bool isFile() const { return _isFile; } @@ -142,7 +142,7 @@ class AsyncWebParameter HEADER :: Chainable object to hold the headers * */ -class AsyncWebHeader +class AsyncWebHeader { private: String _name; @@ -150,34 +150,34 @@ class AsyncWebHeader public: AsyncWebHeader(const String& name, const String& value): _name(name), _value(value) {} - - AsyncWebHeader(const String& data): _name(), _value() + + AsyncWebHeader(const String& data): _name(), _value() { - if (!data) + if (!data) return; - + int index = data.indexOf(':'); - - if (index < 0) + + if (index < 0) return; - + _name = data.substring(0, index); _value = data.substring(index + 2); } - + ~AsyncWebHeader() {} - - const String& name() const + + const String& name() const { return _name; } - - const String& value() const + + const String& value() const { return _value; } - - String toString() const + + String toString() const { return String(_name + ": " + _value + "\r\n"); } @@ -192,11 +192,11 @@ typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, typedef std::function AwsResponseFiller; typedef std::function AwsTemplateProcessor; -class AsyncWebServerRequest +class AsyncWebServerRequest { friend class AsyncWebServer; friend class AsyncCallbackWebHandler; - + private: AsyncClient* _client; AsyncWebServer* _server; @@ -208,19 +208,19 @@ class AsyncWebServerRequest String _temp; uint8_t _parseState; uint8_t _version; - + WebRequestMethodComposite _method; - + String _url; String _host; String _contentType; String _boundary; String _authorization; - + RequestedConnectionType _reqconntype; - + void _removeNotInterestingHeaders(); - + bool _isDigest; bool _isMultipart; bool _isPlainPost; @@ -271,54 +271,54 @@ class AsyncWebServerRequest AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); ~AsyncWebServerRequest(); - AsyncClient* client() + AsyncClient* client() { return _client; } - - uint8_t version() const + + uint8_t version() const { return _version; } - - WebRequestMethodComposite method() const + + WebRequestMethodComposite method() const { return _method; } - - const String& url() const + + const String& url() const { return _url; } - - const String& host() const + + const String& host() const { return _host; } - - const String& contentType() const + + const String& contentType() const { return _contentType; } - - size_t contentLength() const + + size_t contentLength() const { return _contentLength; } - - bool multipart() const + + bool multipart() const { return _isMultipart; } - + const char * methodToString() const; const char * requestedConnTypeToString() const; - - RequestedConnectionType requestedConnType() const + + RequestedConnectionType requestedConnType() const { return _reqconntype; } - + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); void onDisconnect (ArDisconnectHandler fn); @@ -329,23 +329,25 @@ class AsyncWebServerRequest bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); void requestAuthentication(const char * realm = NULL, bool isDigest = true); - void setHandler(AsyncWebHandler *handler) + void setHandler(AsyncWebHandler *handler) { _handler = handler; } - + void addInterestingHeader(const String& name); void redirect(const String& url); void send(AsyncWebServerResponse *response); void send(int code, const String& contentType = String(), const String& content = String()); + void send(int code, const String& contentType, const char *content, bool nonDetructiveSend = true); // RSMOD void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncWebServerResponse *beginResponse(int code, const String& contentType = String(), const String& content = String()); + AsyncWebServerResponse *beginResponse(int code, const String& contentType, const char * content = nullptr); // RSMOD AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); @@ -354,7 +356,7 @@ class AsyncWebServerRequest size_t headers() const; // get header count bool hasHeader(const String& name) const; // check if header exists - + AsyncWebHeader* getHeader(const String& name) const; AsyncWebHeader* getHeader(size_t num) const; @@ -364,11 +366,11 @@ class AsyncWebServerRequest AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const; AsyncWebParameter* getParam(size_t num) const; - size_t args() const + size_t args() const { return params(); // get arguments count } - + const String& arg(const String& name) const; // get request argument value by name const String& arg(size_t i) const; // get request argument value by number const String& argName(size_t i) const; // get request argument name by number @@ -396,55 +398,55 @@ bool ON_AP_FILTER(AsyncWebServerRequest *request); REWRITE :: One instance can be handle any Request (done by the Server) * */ -class AsyncWebRewrite +class AsyncWebRewrite { protected: String _from; String _toUrl; String _params; ArRequestFilterFunction _filter; - + public: - AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL) + AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL) { int index = _toUrl.indexOf('?'); - - if (index > 0) + + if (index > 0) { _params = _toUrl.substring(index + 1); _toUrl = _toUrl.substring(0, index); } } - + virtual ~AsyncWebRewrite() {} - - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) + + AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - - bool filter(AsyncWebServerRequest *request) const + + bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } - - const String& from() const + + const String& from() const { return _from; } - - const String& toUrl() const + + const String& toUrl() const { return _toUrl; } - - const String& params() const + + const String& params() const { return _params; } - - virtual bool match(AsyncWebServerRequest *request) + + virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); } @@ -454,46 +456,46 @@ class AsyncWebRewrite HANDLER :: One instance can be attached to any Request (done by the Server) * */ -class AsyncWebHandler +class AsyncWebHandler { protected: ArRequestFilterFunction _filter; String _username; String _password; - + public: AsyncWebHandler(): _username(""), _password("") {} - - AsyncWebHandler& setFilter(ArRequestFilterFunction fn) + + AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - - AsyncWebHandler& setAuthentication(const char *username, const char *password) + + AsyncWebHandler& setAuthentication(const char *username, const char *password) { _username = String(username); _password = String(password); return *this; }; - - bool filter(AsyncWebServerRequest *request) + + bool filter(AsyncWebServerRequest *request) { return _filter == NULL || _filter(request); } - + virtual ~AsyncWebHandler() {} - - virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) + + virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) { return false; } - + virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))) {} virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))) {} virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {} - - virtual bool isRequestHandlerTrivial() + + virtual bool isRequestHandlerTrivial() { return true; } @@ -503,12 +505,12 @@ class AsyncWebHandler RESPONSE :: One instance is created for each Request (attached by the Handler) * */ -typedef enum +typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState; -class AsyncWebServerResponse +class AsyncWebServerResponse { protected: int _code; @@ -548,7 +550,7 @@ typedef std::function ArRequestHandlerFunc typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; -class AsyncWebServer +class AsyncWebServer { protected: AsyncServer _server; @@ -590,40 +592,40 @@ class AsyncWebServer void _rewriteRequest(AsyncWebServerRequest *request); }; -class DefaultHeaders +class DefaultHeaders { using headers_t = LinkedList; headers_t _headers; DefaultHeaders() - : _headers(headers_t([](AsyncWebHeader * h) + : _headers(headers_t([](AsyncWebHeader * h) { delete h; })) {} - + public: using ConstIterator = headers_t::ConstIterator; - void addHeader(const String& name, const String& value) + void addHeader(const String& name, const String& value) { _headers.add(new AsyncWebHeader(name, value)); } - ConstIterator begin() const + ConstIterator begin() const { return _headers.begin(); } - - ConstIterator end() const + + ConstIterator end() const { return _headers.end(); } DefaultHeaders(DefaultHeaders const &) = delete; DefaultHeaders &operator=(DefaultHeaders const &) = delete; - - static DefaultHeaders &Instance() + + static DefaultHeaders &Instance() { static DefaultHeaders instance; return instance; From e66744e54321288ae0c227adb9128716acf78b7b Mon Sep 17 00:00:00 2001 From: salasidis <45833317+salasidis@users.noreply.github.com> Date: Sun, 2 Oct 2022 10:51:44 -0400 Subject: [PATCH 05/12] Add files via upload --- ...edWebServer_MemoryIssues_Send C String.ino | 320 ++++++++++++++++++ ...bServer_MemoryIssues_SendArduinoString.ino | 305 +++++++++++++++++ 2 files changed, 625 insertions(+) create mode 100644 examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino create mode 100644 examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino new file mode 100644 index 0000000..9a17b85 --- /dev/null +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino @@ -0,0 +1,320 @@ +/**************************************************************************************************************************** + Async_AdvancedWebServer.ino - Dead simple AsyncWebServer for STM32 LAN8720 or built-in LAN8742A Ethernet + + For Portenta_H7 (STM32H7) with Vision-Shield Ethernet + + Portenta_H7_AsyncWebServer is a library for the Portenta_H7 with with Vision-Shield Ethernet + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer + Licensed under GPLv3 license + + Copyright (c) 2015, Majenko Technologies + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Majenko Technologies nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************************************************************/ + +#if !( defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) ) + #error For Portenta_H7 only +#endif + +#define _PORTENTA_H7_ATCP_LOGLEVEL_ 1 +#define _PORTENTA_H7_AWS_LOGLEVEL_ 1 + +#define USE_ETHERNET_PORTENTA_H7 true + +#include +#include +#warning Using Portenta_Ethernet lib for Portenta_H7. + +#include + +#include "SDRAM.h" + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, +}; +// Select the IP address according to your local network +IPAddress ip(192, 168, 2, 232); + +AsyncWebServer server(80); + +int reqCount = 0; // number of requests received + +#define LED_OFF HIGH +#define LED_ON LOW + + +#define BUFFER_SIZE 768 +char temp[BUFFER_SIZE]; + +void handleRoot(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +AsyncWebServer-%s\ +\ +\ +\ +

AsyncWebServer_Portenta_H7!

\ +

running on %s

\ +

Uptime: %d d %02d:%02d:%02d

\ +\ +\ +", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60); + + request->send(200, "text/html", temp, false); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void handleNotFound(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void PrintHeapData(String hIn){ + mbed_stats_heap_t heap_stats; + + Serial.print("HEAP DATA - "); + Serial.print(hIn); + + mbed_stats_heap_get(&heap_stats); + Serial.print(" Cur heap: "); + Serial.print(heap_stats.current_size); + Serial.print(" Res Size: "); + Serial.print(heap_stats.reserved_size); + Serial.print(" Max heap: "); + Serial.println(heap_stats.max_size); +} + + +char *cStr; + + +void drawGraph(AsyncWebServerRequest *request) { + char temp[80]; + + cStr[0] = '\0'; + + strcat(cStr, "\n"); + strcat(cStr, "\n"); + strcat(cStr, "\n"); + int y = rand() % 130; + + for (int x = 10; x < 5000; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + strcat(cStr, temp); + y = y2; + } + strcat(cStr, "\n\n"); + + PrintHeapData("Pre Send"); + + Serial.print("Out String Length="); + Serial.println(strlen(cStr)); + + request->send(200, "image/svg+xml", cStr, false); + + PrintHeapData("Post Send"); +} + + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + while (!Serial); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer on "); Serial.print(BOARD_NAME); + Serial.print(" with "); Serial.println(SHIELD_TYPE); + Serial.println(PORTENTA_H7_ASYNC_TCP_VERSION); + Serial.println(PORTENTA_H7_ASYNC_WEBSERVER_VERSION); + + + + SDRAM.begin(); + + cStr = (char *)SDRAM.malloc(100000); // make a little larger than required + + if (cStr == NULL) { + Serial.println("Unable top Allocate RAM"); + for(;;); + } + + /////////////////////////////////// + + // start the ethernet connection and the server + // Use random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + // Use Static IP + //Ethernet.begin(mac[index], ip); + // Use DHCP dynamic IP and random mac + Ethernet.begin(mac[index]); + + if (Ethernet.hardwareStatus() == EthernetNoHardware) + { + Serial.println("No Ethernet found. Stay here forever"); + + while (true) + { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + + if (Ethernet.linkStatus() == LinkOFF) + { + Serial.println("Not connected Ethernet cable"); + } + + Serial.print(F("Using mac index = ")); + Serial.println(index); + + Serial.print(F("Connected! IP address: ")); + Serial.println(Ethernet.localIP()); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(Ethernet.localIP()); + + + PrintHeapData("Pre Create Arduino String"); + +} + +void heartBeatPrint() +{ + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } +} + +void check_status() +{ + static unsigned long checkstatus_timeout = 0; + +#define STATUS_CHECK_INTERVAL 10000L + + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } +} + +void loop() +{ + check_status(); +} diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino new file mode 100644 index 0000000..c95140f --- /dev/null +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino @@ -0,0 +1,305 @@ +/**************************************************************************************************************************** + Async_AdvancedWebServer.ino - Dead simple AsyncWebServer for STM32 LAN8720 or built-in LAN8742A Ethernet + + For Portenta_H7 (STM32H7) with Vision-Shield Ethernet + + Portenta_H7_AsyncWebServer is a library for the Portenta_H7 with with Vision-Shield Ethernet + + Based on and modified from ESPAsyncWebServer (https://github.com/me-no-dev/ESPAsyncWebServer) + Built by Khoi Hoang https://github.com/khoih-prog/Portenta_H7_AsyncWebServer + Licensed under GPLv3 license + + Copyright (c) 2015, Majenko Technologies + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of Majenko Technologies nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************************************************************/ + +#if !( defined(ARDUINO_PORTENTA_H7_M7) || defined(ARDUINO_PORTENTA_H7_M4) ) + #error For Portenta_H7 only +#endif + +#define _PORTENTA_H7_ATCP_LOGLEVEL_ 1 +#define _PORTENTA_H7_AWS_LOGLEVEL_ 1 + +#define USE_ETHERNET_PORTENTA_H7 true + +#include +#include +#warning Using Portenta_Ethernet lib for Portenta_H7. + +#include + +// Enter a MAC address and IP address for your controller below. +#define NUMBER_OF_MAC 20 + +byte mac[][NUMBER_OF_MAC] = +{ + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x01 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x02 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x03 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x04 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x05 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x06 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x07 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x08 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x09 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0A }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0B }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0C }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0D }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0E }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x0F }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x10 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x11 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x12 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x13 }, + { 0xDE, 0xAD, 0xBE, 0xEF, 0x32, 0x14 }, +}; +// Select the IP address according to your local network +IPAddress ip(192, 168, 2, 232); + +AsyncWebServer server(80); + +int reqCount = 0; // number of requests received + +#define LED_OFF HIGH +#define LED_ON LOW + + +#define BUFFER_SIZE 512 +char temp[BUFFER_SIZE]; + +void handleRoot(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + + int sec = millis() / 1000; + int min = sec / 60; + int hr = min / 60; + int day = hr / 24; + + snprintf(temp, BUFFER_SIZE - 1, + "\ +\ +AsyncWebServer-%s\ +\ +\ +\ +

AsyncWebServer_Portenta_H7!

\ +

running on %s

\ +

Uptime: %d d %02d:%02d:%02d

\ +\ +\ +", BOARD_NAME, BOARD_NAME, day, hr % 24, min % 60, sec % 60); + + request->send(200, "text/html", temp); + + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void handleNotFound(AsyncWebServerRequest *request) +{ + digitalWrite(LED_BUILTIN, LED_ON); + String message = "File Not Found\n\n"; + + message += "URI: "; + message += request->url(); + message += "\nMethod: "; + message += (request->method() == HTTP_GET) ? "GET" : "POST"; + message += "\nArguments: "; + message += request->args(); + message += "\n"; + + for (uint8_t i = 0; i < request->args(); i++) + { + message += " " + request->argName(i) + ": " + request->arg(i) + "\n"; + } + + request->send(404, "text/plain", message); + digitalWrite(LED_BUILTIN, LED_OFF); +} + +void PrintHeapData(String hIn){ + mbed_stats_heap_t heap_stats; + + Serial.print("HEAP DATA - "); + Serial.print(hIn); + + mbed_stats_heap_get(&heap_stats); + Serial.print(" Cur heap: "); + Serial.print(heap_stats.current_size); + Serial.print(" Res Size: "); + Serial.print(heap_stats.reserved_size); + Serial.print(" Max heap: "); + Serial.println(heap_stats.max_size); +} + +void drawGraph(AsyncWebServerRequest *request) +{ + String out; + + out.reserve(40000); + char temp[70]; + + out += "\n"; + out += "\n"; + out += "\n"; + int y = rand() % 130; + + for (int x = 10; x < 5000; x += 10) + { + int y2 = rand() % 130; + sprintf(temp, "\n", x, 140 - y, x + 10, 140 - y2); + out += temp; + y = y2; + } + out += "\n\n"; + + PrintHeapData("Pre Send"); + + Serial.print("Out String Length="); + Serial.println(out.length()); + + request->send(200, "image/svg+xml", out); + + PrintHeapData("Post Send"); +} + + +void setup() +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, LED_OFF); + + Serial.begin(115200); + while (!Serial); + + delay(200); + + Serial.print("\nStart Async_AdvancedWebServer on "); Serial.print(BOARD_NAME); + Serial.print(" with "); Serial.println(SHIELD_TYPE); + Serial.println(PORTENTA_H7_ASYNC_TCP_VERSION); + Serial.println(PORTENTA_H7_ASYNC_WEBSERVER_VERSION); + + /////////////////////////////////// + + // start the ethernet connection and the server + // Use random mac + uint16_t index = millis() % NUMBER_OF_MAC; + + // Use Static IP + //Ethernet.begin(mac[index], ip); + // Use DHCP dynamic IP and random mac + Ethernet.begin(mac[index]); + + if (Ethernet.hardwareStatus() == EthernetNoHardware) + { + Serial.println("No Ethernet found. Stay here forever"); + + while (true) + { + delay(1); // do nothing, no point running without Ethernet hardware + } + } + + if (Ethernet.linkStatus() == LinkOFF) + { + Serial.println("Not connected Ethernet cable"); + } + + Serial.print(F("Using mac index = ")); + Serial.println(index); + + Serial.print(F("Connected! IP address: ")); + Serial.println(Ethernet.localIP()); + + /////////////////////////////////// + + server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) + { + handleRoot(request); + }); + + server.on("/test.svg", HTTP_GET, [](AsyncWebServerRequest * request) + { + drawGraph(request); + }); + + server.on("/inline", [](AsyncWebServerRequest * request) + { + request->send(200, "text/plain", "This works as well"); + }); + + server.onNotFound(handleNotFound); + + server.begin(); + + Serial.print(F("HTTP EthernetWebServer is @ IP : ")); + Serial.println(Ethernet.localIP()); + + + PrintHeapData("Pre Create Arduino String"); + +} + +void heartBeatPrint() +{ + static int num = 1; + + Serial.print(F(".")); + + if (num == 80) + { + Serial.println(); + num = 1; + } + else if (num++ % 10 == 0) + { + Serial.print(F(" ")); + } +} + +void check_status() +{ + static unsigned long checkstatus_timeout = 0; + +#define STATUS_CHECK_INTERVAL 10000L + + // Send status report every STATUS_REPORT_INTERVAL (60) seconds: we don't need to send updates frequently if there is no status change. + if ((millis() > checkstatus_timeout) || (checkstatus_timeout == 0)) + { + heartBeatPrint(); + checkstatus_timeout = millis() + STATUS_CHECK_INTERVAL; + } +} + +void loop() +{ + check_status(); +} From c499c98543423baae3be2333d728509051f7a413 Mon Sep 17 00:00:00 2001 From: salasidis <45833317+salasidis@users.noreply.github.com> Date: Sun, 2 Oct 2022 11:02:03 -0400 Subject: [PATCH 06/12] Update Async_AdvancedWebServer_MemoryIssues_Send C String.ino --- .../Async_AdvancedWebServer_MemoryIssues_Send C String.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino index 9a17b85..bd3fba1 100644 --- a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino @@ -92,7 +92,7 @@ int reqCount = 0; // number of requests received #define LED_ON LOW -#define BUFFER_SIZE 768 +#define BUFFER_SIZE 768 // a little larger in case required for header shift (destructive send) char temp[BUFFER_SIZE]; void handleRoot(AsyncWebServerRequest *request) From ddf98f10dee5811aad0609367bc2a7ae2beb8b83 Mon Sep 17 00:00:00 2001 From: salasidis <45833317+salasidis@users.noreply.github.com> Date: Sun, 2 Oct 2022 14:12:09 -0400 Subject: [PATCH 07/12] Add files via upload --- src/Portenta_H7_AsyncWebRequest.cpp | 62 +++--- src/Portenta_H7_AsyncWebResponseImpl.h | 70 +++---- src/Portenta_H7_AsyncWebResponses.cpp | 273 +++++++++++++------------ src/Portenta_H7_AsyncWebServer.h | 212 +++++++++---------- 4 files changed, 312 insertions(+), 305 deletions(-) diff --git a/src/Portenta_H7_AsyncWebRequest.cpp b/src/Portenta_H7_AsyncWebRequest.cpp index 6b10d1b..0fc14bb 100644 --- a/src/Portenta_H7_AsyncWebRequest.cpp +++ b/src/Portenta_H7_AsyncWebRequest.cpp @@ -64,7 +64,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onError([](void *r, AsyncClient * c, int8_t error) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onError(error); }, this); @@ -72,7 +72,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onAck([](void *r, AsyncClient * c, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onAck(len, time); }, this); @@ -87,7 +87,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onTimeout([](void *r, AsyncClient * c, uint32_t time) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onTimeout(time); }, this); @@ -95,7 +95,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onData([](void *r, AsyncClient * c, void *buf, size_t len) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = (AsyncWebServerRequest*)r; req->_onData(buf, len); }, this); @@ -103,7 +103,7 @@ AsyncWebServerRequest::AsyncWebServerRequest(AsyncWebServer* s, AsyncClient* c) c->onPoll([](void *r, AsyncClient * c) { PORTENTA_H7_AWS_UNUSED(c); - + AsyncWebServerRequest *req = ( AsyncWebServerRequest*)r; req->_onPoll(); }, this); @@ -157,7 +157,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) // No new line, just add the buffer in _temp char ch = str[len - 1]; str[len - 1] = 0; - + _temp.reserve(_temp.length() + len); _temp.concat(str); _temp.concat(ch); @@ -166,7 +166,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) { // Found new line - extract it and parse str[i] = 0; // Terminate the string at the end of the line. - + _temp.concat(str); _temp.trim(); _parseLine(); @@ -260,7 +260,7 @@ void AsyncWebServerRequest::_onData(void *buf, size_t len) break; } - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -287,10 +287,10 @@ void AsyncWebServerRequest::_removeNotInterestingHeaders() void AsyncWebServerRequest::_onPoll() { if (_response != NULL && _client != NULL && _client->canSend() && !_response->_finished()) - { + { _response->_ack(this, 0, 0); } - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -315,7 +315,7 @@ void AsyncWebServerRequest::_onAck(size_t len, uint32_t time) delete r; } } - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -337,7 +337,7 @@ void AsyncWebServerRequest::_onTimeout(uint32_t time) AWS_LOGINFO3("TIMEOUT: time =", time, ", state =", _client->stateToString()); _client->close(); - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -360,7 +360,7 @@ void AsyncWebServerRequest::_onDisconnect() } _server->_handleDisconnect(this); - + // KH, Important for Portenta Murata WiFi, or system will hang //delay(0); yield(); @@ -656,19 +656,19 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (_parsedLength < 2 && data != '-') { _multiParseState = PARSE_ERROR; - + return; } else if (_parsedLength - 2 < _boundary.length() && _boundary.c_str()[_parsedLength - 2] != data) { _multiParseState = PARSE_ERROR; - + return; } else if (_parsedLength - 2 == _boundary.length() && data != '\r') { _multiParseState = PARSE_ERROR; - + return; } else if (_parsedLength - 3 == _boundary.length()) @@ -676,7 +676,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (data != '\n') { _multiParseState = PARSE_ERROR; - + return; } @@ -754,7 +754,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) if (_itemBuffer == NULL) { _multiParseState = PARSE_ERROR; - + return; } @@ -829,7 +829,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) else if (_boundaryPosition == _boundary.length() - 1) { _multiParseState = DASH3_OR_RETURN2; - + if (!_itemIsFile) { _addParam(new AsyncWebParameter(_itemName, _itemValue, true)); @@ -862,7 +862,7 @@ void AsyncWebServerRequest::_parseMultipartPostByte(uint8_t data, bool last) { AWS_LOGDEBUG1("ERROR: The parser got to the end of the POST but is expecting more bytes =", (_contentLength - _parsedLength - 4)); AWS_LOGDEBUG("Drop an issue so we can have more info on the matter!"); - + _contentLength = _parsedLength + 4;//lets close the request gracefully } @@ -1080,16 +1080,16 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) _response = response; if (_response == NULL) - { + { _client->close(true); - + _onDisconnect(); return; } if (!_response->_sourceValid()) - { + { delete response; _response = NULL; send(500); @@ -1098,7 +1098,7 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse *response) { _client->setRxTimeout(0); _response->_respond(this); - } + } } //RSMOD/////////////////////////////////////////////// @@ -1150,12 +1150,15 @@ AsyncResponseStream * AsyncWebServerRequest::beginResponseStream(const String& c void AsyncWebServerRequest::send(int code, const String& contentType, const char *content, bool nonDetructiveSend) { - if (nonDetructiveSend == true) { - send(beginResponse(code, contentType, String(content))); // for backwards compatibility - } else { - send(beginResponse(code, contentType, content)); + if (nonDetructiveSend) + { + send(beginResponse(code, contentType, String(content))); // for backwards compatibility + } + else + { + send(beginResponse(code, contentType, content)); } - } +} ///////////////////////////////////////////////// @@ -1465,4 +1468,3 @@ bool AsyncWebServerRequest::isExpectedRequestedConnType(RequestedConnectionType } ///////////////////////////////////////////////// - diff --git a/src/Portenta_H7_AsyncWebResponseImpl.h b/src/Portenta_H7_AsyncWebResponseImpl.h index f0c1d67..481b502 100644 --- a/src/Portenta_H7_AsyncWebResponseImpl.h +++ b/src/Portenta_H7_AsyncWebResponseImpl.h @@ -39,20 +39,20 @@ ///////////////////////////////////////////////// -class AsyncBasicResponse: public AsyncWebServerResponse +class AsyncBasicResponse: public AsyncWebServerResponse { private: String _content; - char *_contentCstr; // RSMOD - + char *_contentCstr; // RSMOD + public: AsyncBasicResponse(int code, const String& contentType = String(), const String& content = String()); - AsyncBasicResponse(int code, const String& contentType, const char *content); // RSMOD + AsyncBasicResponse(int code, const String& contentType, const char *content = nullptr); // RSMOD void _respond(AsyncWebServerRequest *request); - + size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - - bool _sourceValid() const + + bool _sourceValid() const { return true; } @@ -60,7 +60,7 @@ class AsyncBasicResponse: public AsyncWebServerResponse ///////////////////////////////////////////////// -class AsyncAbstractResponse: public AsyncWebServerResponse +class AsyncAbstractResponse: public AsyncWebServerResponse { private: String _head; @@ -71,21 +71,21 @@ class AsyncAbstractResponse: public AsyncWebServerResponse std::vector _cache; size_t _readDataFromCacheOrContent(uint8_t* data, const size_t len); size_t _fillBufferAndProcessTemplates(uint8_t* buf, size_t maxLen); - + protected: AwsTemplateProcessor _callback; - + public: AsyncAbstractResponse(AwsTemplateProcessor callback = nullptr); void _respond(AsyncWebServerRequest *request); size_t _ack(AsyncWebServerRequest *request, size_t len, uint32_t time); - - bool _sourceValid() const + + bool _sourceValid() const { return false; } - - virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) + + virtual size_t _fillBuffer(uint8_t *buf __attribute__((unused)), size_t maxLen __attribute__((unused))) { return 0; } @@ -94,64 +94,64 @@ class AsyncAbstractResponse: public AsyncWebServerResponse ///////////////////////////////////////////////// #ifndef TEMPLATE_PLACEHOLDER - #define TEMPLATE_PLACEHOLDER '%' +#define TEMPLATE_PLACEHOLDER '%' #endif #define TEMPLATE_PARAM_NAME_LENGTH 32 ///////////////////////////////////////////////// -class AsyncStreamResponse: public AsyncAbstractResponse +class AsyncStreamResponse: public AsyncAbstractResponse { private: Stream *_content; - + public: AsyncStreamResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); - - bool _sourceValid() const + + bool _sourceValid() const { return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncCallbackResponse: public AsyncAbstractResponse +class AsyncCallbackResponse: public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; - + public: AsyncCallbackResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - - bool _sourceValid() const + + bool _sourceValid() const { return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; ///////////////////////////////////////////////// -class AsyncChunkedResponse: public AsyncAbstractResponse +class AsyncChunkedResponse: public AsyncAbstractResponse { private: AwsResponseFiller _content; size_t _filledLength; - + public: AsyncChunkedResponse(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); - - bool _sourceValid() const + + bool _sourceValid() const { return !!(_content); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; }; @@ -161,20 +161,20 @@ class cbuf; ///////////////////////////////////////////////// -class AsyncResponseStream: public AsyncAbstractResponse, public Print +class AsyncResponseStream: public AsyncAbstractResponse, public Print { private: cbuf *_content; - + public: AsyncResponseStream(const String& contentType, size_t bufferSize); ~AsyncResponseStream(); - - bool _sourceValid() const + + bool _sourceValid() const { return (_state < RESPONSE_END); } - + virtual size_t _fillBuffer(uint8_t *buf, size_t maxLen) override; size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); diff --git a/src/Portenta_H7_AsyncWebResponses.cpp b/src/Portenta_H7_AsyncWebResponses.cpp index a5441ea..fc34f35 100644 --- a/src/Portenta_H7_AsyncWebResponses.cpp +++ b/src/Portenta_H7_AsyncWebResponses.cpp @@ -53,7 +53,7 @@ void* memchr(void* ptr, int ch, size_t count) * */ const char* AsyncWebServerResponse::_responseCodeToString(int code) { - switch (code) + switch (code) { case 100: return "Continue"; case 101: return "Switching Protocols"; @@ -232,7 +232,7 @@ bool AsyncWebServerResponse::_sourceValid() const void AsyncWebServerResponse::_respond(AsyncWebServerRequest *request) { _state = RESPONSE_END; - + request->client()->close(); } @@ -243,7 +243,7 @@ size_t AsyncWebServerResponse::_ack(AsyncWebServerRequest *request, size_t len, PORTENTA_H7_AWS_UNUSED(request); PORTENTA_H7_AWS_UNUSED(len); PORTENTA_H7_AWS_UNUSED(time); - + return 0; } @@ -257,14 +257,15 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons { _code = code; _content = String(""); - _contentCstr = (char *)content; + _contentCstr = (char *)content; // RSMOD _contentType = contentType; + int iLen; if ((iLen = strlen(_contentCstr))) { _contentLength = iLen; - + if (!_contentType.length()) _contentType = "text/plain"; } @@ -281,13 +282,13 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const String& contentType, cons { _code = code; _content = content; - _contentCstr = NULL; // RSMOD + _contentCstr = nullptr; // RSMOD _contentType = contentType; if (_content.length()) { _contentLength = _content.length(); - + if (!_contentType.length()) _contentType = "text/plain"; } @@ -303,113 +304,116 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) String out = _assembleHead(request->version()); size_t outLen = out.length(); size_t space = request->client()->space(); - - // Serial.println(""); - // Serial.println(""); - // Serial.println(""); - // Serial.println("");; - // Serial.println("Pre _respond\n"); - // Serial.print("_contentLength ="); - // Serial.println(_contentLength); - // Serial.print("out="); - // Serial.println(out); - // Serial.print("outLen="); - // Serial.println(outLen); - //// Serial.println("_contentCstr="); - //// Serial.println(_contentCstr); + + AWS_LOGDEBUG3("AsyncAbstractResponse::_respond : Pre_respond, _contentLength =", _contentLength, ", out =", out ); + AWS_LOGDEBUG3("outLen =", outLen, ", _contentCstr =", _contentCstr); if (!_contentLength && space >= outLen) { - // Serial.println(""); - // Serial.println("In A ******"); - // Serial.println(""); + AWS_LOGDEBUG("Step 1"); + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_WAIT_ACK; } else if (_contentLength && space >= outLen + _contentLength) { - // Serial.println(""); - // Serial.println("In B ******"); - // Serial.println(""); - if (_contentCstr) { - memmove(&_contentCstr[outLen], _contentCstr, _contentLength); - memcpy(_contentCstr, out.c_str(), outLen); - outLen += _contentLength; - // Serial.print(_contentCstr); - _writtenLength += request->client()->write(_contentCstr, outLen); - } else { - out += _content; - outLen += _contentLength; - _writtenLength += request->client()->write(out.c_str(), outLen); - } + AWS_LOGDEBUG("Step 2"); + + if (_contentCstr) + { + memmove(&_contentCstr[outLen], _contentCstr, _contentLength); + memcpy(_contentCstr, out.c_str(), outLen); + outLen += _contentLength; + + AWS_LOGDEBUG1("_contentCstr =", _contentCstr); + + _writtenLength += request->client()->write(_contentCstr, outLen); + } + else + { + out += _content; + outLen += _contentLength; + _writtenLength += request->client()->write(out.c_str(), outLen); + } + _state = RESPONSE_WAIT_ACK; } else if (space && space < outLen) { - String partial = out.substring(0, space); - // Serial.println(""); - // Serial.println("In C ******"); - // Serial.println(""); - if (_contentCstr) { - int deltaLen = out.length() - partial.length(); - memmove(&_contentCstr[deltaLen], _contentCstr, deltaLen); - memcpy(_contentCstr, out.substring(space).c_str(), deltaLen); - } else { - _content = out.substring(space) + _content; - } - _contentLength += outLen - space; - // Serial.print(partial); - _writtenLength += request->client()->write(partial.c_str(), partial.length()); + String partial = out.substring(0, space); + + AWS_LOGDEBUG("Step 3"); + + if (_contentCstr) + { + int deltaLen = out.length() - partial.length(); + + memmove(&_contentCstr[deltaLen], _contentCstr, deltaLen); + memcpy(_contentCstr, out.substring(space).c_str(), deltaLen); + } + else + { + _content = out.substring(space) + _content; + } + + _contentLength += outLen - space; + + AWS_LOGDEBUG1("partial =", partial); + + _writtenLength += request->client()->write(partial.c_str(), partial.length()); _state = RESPONSE_CONTENT; } else if (space > outLen && space < (outLen + _contentLength)) { - size_t shift = space - outLen; - // Serial.println(""); - // Serial.println("In D ******"); - // Serial.println(""); - - outLen += shift; - _sentLength += shift; - if (_contentCstr) { - char *s = (char *)malloc(shift +1); - strncpy(s, _contentCstr, shift); - s[shift] = '\0'; - out += String(s); - _contentCstr += shift; - free(s); - } else { - out += _content.substring(0, shift); - _content = _content.substring(shift); - } - // Serial.print(out); - _writtenLength += request->client()->write(out.c_str(), outLen); + size_t shift = space - outLen; + + AWS_LOGDEBUG("Step 4"); + + outLen += shift; + _sentLength += shift; + + if (_contentCstr) + { + char *s = (char *)malloc(shift + 1); + + strncpy(s, _contentCstr, shift); + s[shift] = '\0'; + out += String(s); + _contentCstr += shift; + + free(s); + } + else + { + out += _content.substring(0, shift); + _content = _content.substring(shift); + } + + AWS_LOGDEBUG1("out =", out); + + _writtenLength += request->client()->write(out.c_str(), outLen); _state = RESPONSE_CONTENT; } else { - // Serial.println(""); - // Serial.println("In E ******"); - // Serial.println(""); - if (_contentCstr) { - memmove(&_contentCstr[outLen], _contentCstr, _contentLength); - memcpy(_contentCstr, out.c_str(), outLen); - } else { - _content = out + _content; - } - _contentLength += outLen; + AWS_LOGDEBUG("Step 5"); + + if (_contentCstr) + { + memmove(&_contentCstr[outLen], _contentCstr, _contentLength); + memcpy(_contentCstr, out.c_str(), outLen); + } + else + { + _content = out + _content; + } + + _contentLength += outLen; _state = RESPONSE_CONTENT; } - - // Serial.print("\n\n\n\n\n\n\nPost _respond\n"); - // Serial.print("_contentLength ="); - // Serial.println(_contentLength); - // Serial.print("out="); - // Serial.println(out); - // Serial.print("outLen="); - // Serial.println(outLen); - //// Serial.println("_contentCstr="); - //// Serial.println(_contentCstr); + + AWS_LOGDEBUG3("AsyncAbstractResponse::_respond : Post_respond, _contentLength =", _contentLength, ", out =", out ); + AWS_LOGDEBUG3("outLen =", outLen, ", _contentCstr =", _contentCstr); } ///////////////////////////////////////////////// @@ -417,59 +421,63 @@ void AsyncBasicResponse::_respond(AsyncWebServerRequest *request) size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint32_t time) { PORTENTA_H7_AWS_UNUSED(time); - - // Serial.print("\n\n\n\n\n\n\nPre _ack\n"); - // Serial.print("_contentLength ="); - // Serial.print(_contentLength); - + + AWS_LOGDEBUG1("AsyncAbstractResponse::_ack : Pre_ack, _contentLength =", _contentLength); + _ackedLength += len; if (_state == RESPONSE_CONTENT) { - String out; + String out; size_t available = _contentLength - _sentLength; size_t space = request->client()->space(); - - // Serial.print(" available="); - // Serial.print(available); - // Serial.print(" space ="); - // Serial.print(space); - + + AWS_LOGDEBUG3("AsyncAbstractResponse::_ack : available =", available, ", space =", space ); //we can fit in this packet if (space > available) { - // Serial.println("In space>available"); - if (_contentCstr) { - // Serial.println("output="); - // Serial.println(_contentCstr); - _writtenLength += request->client()->write(_contentCstr, available); - //_contentCstr[0] = '\0'; - } else { - _writtenLength += request->client()->write(_content.c_str(), available); - _content = String(); - } + // Serial.println("In space>available"); + AWS_LOGDEBUG1("AsyncAbstractResponse::_ack : Pre_ack, _contentLength =", _contentLength); + + if (_contentCstr) + { + AWS_LOGDEBUG1("In space>available : output =", _contentCstr); + + _writtenLength += request->client()->write(_contentCstr, available); + //_contentCstr[0] = '\0'; + } + else + { + _writtenLength += request->client()->write(_content.c_str(), available); + _content = String(); + } _state = RESPONSE_WAIT_ACK; return available; } //send some data, the rest on ack - if (_contentCstr) { - char *s = (char *)malloc(space +1); - strncpy(s, _contentCstr, space); - s[space] = '\0'; - out = String(s); - _contentCstr += space; - free(s); - } else { - out = _content.substring(0, space); - _content = _content.substring(space); - } - _sentLength += space; - // Serial.println("output="); - // Serial.println(out); - _writtenLength += request->client()->write(out.c_str(), space); + if (_contentCstr) + { + char *s = (char *)malloc(space + 1); + strncpy(s, _contentCstr, space); + s[space] = '\0'; + out = String(s); + _contentCstr += space; + free(s); + } + else + { + out = _content.substring(0, space); + _content = _content.substring(space); + } + + _sentLength += space; + + AWS_LOGDEBUG1("In space>available : output =", out); + + _writtenLength += request->client()->write(out.c_str(), space); return space; } @@ -480,11 +488,8 @@ size_t AsyncBasicResponse::_ack(AsyncWebServerRequest *request, size_t len, uint _state = RESPONSE_END; } } - - // Serial.print("\n\n\n\n\n\n\npost _ack"); - // Serial.print("_contentLength ="); - // Serial.print(_contentLength); - // Serial.print(_contentCstr); + + AWS_LOGDEBUG3("AsyncAbstractResponse::_ack : Post_ack, _contentLength =", _contentLength, ", _contentCstr =", _contentCstr); return 0; } diff --git a/src/Portenta_H7_AsyncWebServer.h b/src/Portenta_H7_AsyncWebServer.h index 2001882..4a0493a 100644 --- a/src/Portenta_H7_AsyncWebServer.h +++ b/src/Portenta_H7_AsyncWebServer.h @@ -29,8 +29,8 @@ #else - #error For Portenta_H7 only - +#error For Portenta_H7 only + #endif #define PORTENTA_H7_ASYNC_WEBSERVER_VERSION "Portenta_H7_AsyncWebServer v1.3.0" @@ -76,7 +76,7 @@ class AsyncCallbackWebHandler; class AsyncResponseStream; #ifndef WEBSERVER_H - typedef enum + typedef enum { HTTP_GET = 0b00000001, HTTP_POST = 0b00000010, @@ -99,7 +99,7 @@ typedef std::function ArDisconnectHandler; PARAMETER :: Chainable object to hold GET/POST and FILE parameters * */ -class AsyncWebParameter +class AsyncWebParameter { private: String _name; @@ -111,28 +111,28 @@ class AsyncWebParameter public: AsyncWebParameter(const String& name, const String& value, bool form = false, bool file = false, size_t size = 0): _name(name), _value(value), _size(size), _isForm(form), _isFile(file) {} - - const String& name() const + + const String& name() const { return _name; } - - const String& value() const + + const String& value() const { return _value; } - - size_t size() const + + size_t size() const { return _size; } - - bool isPost() const + + bool isPost() const { return _isForm; } - - bool isFile() const + + bool isFile() const { return _isFile; } @@ -142,7 +142,7 @@ class AsyncWebParameter HEADER :: Chainable object to hold the headers * */ -class AsyncWebHeader +class AsyncWebHeader { private: String _name; @@ -150,34 +150,34 @@ class AsyncWebHeader public: AsyncWebHeader(const String& name, const String& value): _name(name), _value(value) {} - - AsyncWebHeader(const String& data): _name(), _value() + + AsyncWebHeader(const String& data): _name(), _value() { - if (!data) + if (!data) return; - + int index = data.indexOf(':'); - - if (index < 0) + + if (index < 0) return; - + _name = data.substring(0, index); _value = data.substring(index + 2); } - + ~AsyncWebHeader() {} - - const String& name() const + + const String& name() const { return _name; } - - const String& value() const + + const String& value() const { return _value; } - - String toString() const + + String toString() const { return String(_name + ": " + _value + "\r\n"); } @@ -192,11 +192,11 @@ typedef enum { RCT_NOT_USED = -1, RCT_DEFAULT = 0, RCT_HTTP, RCT_WS, RCT_EVENT, typedef std::function AwsResponseFiller; typedef std::function AwsTemplateProcessor; -class AsyncWebServerRequest +class AsyncWebServerRequest { friend class AsyncWebServer; friend class AsyncCallbackWebHandler; - + private: AsyncClient* _client; AsyncWebServer* _server; @@ -208,19 +208,19 @@ class AsyncWebServerRequest String _temp; uint8_t _parseState; uint8_t _version; - + WebRequestMethodComposite _method; - + String _url; String _host; String _contentType; String _boundary; String _authorization; - + RequestedConnectionType _reqconntype; - + void _removeNotInterestingHeaders(); - + bool _isDigest; bool _isMultipart; bool _isPlainPost; @@ -271,54 +271,54 @@ class AsyncWebServerRequest AsyncWebServerRequest(AsyncWebServer*, AsyncClient*); ~AsyncWebServerRequest(); - AsyncClient* client() + AsyncClient* client() { return _client; } - - uint8_t version() const + + uint8_t version() const { return _version; } - - WebRequestMethodComposite method() const + + WebRequestMethodComposite method() const { return _method; } - - const String& url() const + + const String& url() const { return _url; } - - const String& host() const + + const String& host() const { return _host; } - - const String& contentType() const + + const String& contentType() const { return _contentType; } - - size_t contentLength() const + + size_t contentLength() const { return _contentLength; } - - bool multipart() const + + bool multipart() const { return _isMultipart; } - + const char * methodToString() const; const char * requestedConnTypeToString() const; - - RequestedConnectionType requestedConnType() const + + RequestedConnectionType requestedConnType() const { return _reqconntype; } - + bool isExpectedRequestedConnType(RequestedConnectionType erct1, RequestedConnectionType erct2 = RCT_NOT_USED, RequestedConnectionType erct3 = RCT_NOT_USED); void onDisconnect (ArDisconnectHandler fn); @@ -329,25 +329,25 @@ class AsyncWebServerRequest bool authenticate(const char * username, const char * password, const char * realm = NULL, bool passwordIsHash = false); void requestAuthentication(const char * realm = NULL, bool isDigest = true); - void setHandler(AsyncWebHandler *handler) + void setHandler(AsyncWebHandler *handler) { _handler = handler; } - + void addInterestingHeader(const String& name); void redirect(const String& url); void send(AsyncWebServerResponse *response); void send(int code, const String& contentType = String(), const String& content = String()); - void send(int code, const String& contentType, const char *content, bool nonDetructiveSend = true); // RSMOD + void send(int code, const String& contentType, const char *content, bool nonDetructiveSend = true); // RSMOD void send(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); void send(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); void sendChunked(const String& contentType, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); AsyncWebServerResponse *beginResponse(int code, const String& contentType = String(), const String& content = String()); - AsyncWebServerResponse *beginResponse(int code, const String& contentType, const char * content); // RSMOD + AsyncWebServerResponse *beginResponse(int code, const String& contentType, const char * content = nullptr); // RSMOD AsyncWebServerResponse *beginResponse(Stream &stream, const String& contentType, size_t len, AwsTemplateProcessor callback = nullptr); AsyncWebServerResponse *beginResponse(const String& contentType, size_t len, AwsResponseFiller callback, AwsTemplateProcessor templateCallback = nullptr); @@ -356,7 +356,7 @@ class AsyncWebServerRequest size_t headers() const; // get header count bool hasHeader(const String& name) const; // check if header exists - + AsyncWebHeader* getHeader(const String& name) const; AsyncWebHeader* getHeader(size_t num) const; @@ -366,11 +366,11 @@ class AsyncWebServerRequest AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const; AsyncWebParameter* getParam(size_t num) const; - size_t args() const + size_t args() const { return params(); // get arguments count } - + const String& arg(const String& name) const; // get request argument value by name const String& arg(size_t i) const; // get request argument value by number const String& argName(size_t i) const; // get request argument name by number @@ -398,55 +398,55 @@ bool ON_AP_FILTER(AsyncWebServerRequest *request); REWRITE :: One instance can be handle any Request (done by the Server) * */ -class AsyncWebRewrite +class AsyncWebRewrite { protected: String _from; String _toUrl; String _params; ArRequestFilterFunction _filter; - + public: - AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL) + AsyncWebRewrite(const char* from, const char* to): _from(from), _toUrl(to), _params(String()), _filter(NULL) { int index = _toUrl.indexOf('?'); - - if (index > 0) + + if (index > 0) { _params = _toUrl.substring(index + 1); _toUrl = _toUrl.substring(0, index); } } - + virtual ~AsyncWebRewrite() {} - - AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) + + AsyncWebRewrite& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - - bool filter(AsyncWebServerRequest *request) const + + bool filter(AsyncWebServerRequest *request) const { return _filter == NULL || _filter(request); } - - const String& from() const + + const String& from() const { return _from; } - - const String& toUrl() const + + const String& toUrl() const { return _toUrl; } - - const String& params() const + + const String& params() const { return _params; } - - virtual bool match(AsyncWebServerRequest *request) + + virtual bool match(AsyncWebServerRequest *request) { return from() == request->url() && filter(request); } @@ -456,46 +456,46 @@ class AsyncWebRewrite HANDLER :: One instance can be attached to any Request (done by the Server) * */ -class AsyncWebHandler +class AsyncWebHandler { protected: ArRequestFilterFunction _filter; String _username; String _password; - + public: AsyncWebHandler(): _username(""), _password("") {} - - AsyncWebHandler& setFilter(ArRequestFilterFunction fn) + + AsyncWebHandler& setFilter(ArRequestFilterFunction fn) { _filter = fn; return *this; } - - AsyncWebHandler& setAuthentication(const char *username, const char *password) + + AsyncWebHandler& setAuthentication(const char *username, const char *password) { _username = String(username); _password = String(password); return *this; }; - - bool filter(AsyncWebServerRequest *request) + + bool filter(AsyncWebServerRequest *request) { return _filter == NULL || _filter(request); } - + virtual ~AsyncWebHandler() {} - - virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) + + virtual bool canHandle(AsyncWebServerRequest *request __attribute__((unused))) { return false; } - + virtual void handleRequest(AsyncWebServerRequest *request __attribute__((unused))) {} virtual void handleUpload(AsyncWebServerRequest *request __attribute__((unused)), const String& filename __attribute__((unused)), size_t index __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), bool final __attribute__((unused))) {} virtual void handleBody(AsyncWebServerRequest *request __attribute__((unused)), uint8_t *data __attribute__((unused)), size_t len __attribute__((unused)), size_t index __attribute__((unused)), size_t total __attribute__((unused))) {} - - virtual bool isRequestHandlerTrivial() + + virtual bool isRequestHandlerTrivial() { return true; } @@ -505,12 +505,12 @@ class AsyncWebHandler RESPONSE :: One instance is created for each Request (attached by the Handler) * */ -typedef enum +typedef enum { RESPONSE_SETUP, RESPONSE_HEADERS, RESPONSE_CONTENT, RESPONSE_WAIT_ACK, RESPONSE_END, RESPONSE_FAILED } WebResponseState; -class AsyncWebServerResponse +class AsyncWebServerResponse { protected: int _code; @@ -550,7 +550,7 @@ typedef std::function ArRequestHandlerFunc typedef std::function ArUploadHandlerFunction; typedef std::function ArBodyHandlerFunction; -class AsyncWebServer +class AsyncWebServer { protected: AsyncServer _server; @@ -592,40 +592,40 @@ class AsyncWebServer void _rewriteRequest(AsyncWebServerRequest *request); }; -class DefaultHeaders +class DefaultHeaders { using headers_t = LinkedList; headers_t _headers; DefaultHeaders() - : _headers(headers_t([](AsyncWebHeader * h) + : _headers(headers_t([](AsyncWebHeader * h) { delete h; })) {} - + public: using ConstIterator = headers_t::ConstIterator; - void addHeader(const String& name, const String& value) + void addHeader(const String& name, const String& value) { _headers.add(new AsyncWebHeader(name, value)); } - ConstIterator begin() const + ConstIterator begin() const { return _headers.begin(); } - - ConstIterator end() const + + ConstIterator end() const { return _headers.end(); } DefaultHeaders(DefaultHeaders const &) = delete; DefaultHeaders &operator=(DefaultHeaders const &) = delete; - - static DefaultHeaders &Instance() + + static DefaultHeaders &Instance() { static DefaultHeaders instance; return instance; From e9f424b40cc29e56fc5e9e7f8994cdc60da823a7 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Sun, 2 Oct 2022 15:03:32 -0400 Subject: [PATCH 08/12] Display longer graph --- .../Async_AdvancedWebServer_MemoryIssues_Send C String.ino | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino index bd3fba1..157e5d4 100644 --- a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino @@ -171,8 +171,8 @@ void drawGraph(AsyncWebServerRequest *request) { cStr[0] = '\0'; - strcat(cStr, "\n"); - strcat(cStr, "\n"); + strcat(cStr, "\n"); + strcat(cStr, "\n"); strcat(cStr, "\n"); int y = rand() % 130; From 6423342f7bd765da5a92ed9ecffd9829c090abf9 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Sun, 2 Oct 2022 15:16:26 -0400 Subject: [PATCH 09/12] Display longer graph and change name --- ...c_AdvancedWebServer_MemoryIssues_SendArduinoString.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino index c95140f..d255ec5 100644 --- a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino @@ -167,8 +167,8 @@ void drawGraph(AsyncWebServerRequest *request) out.reserve(40000); char temp[70]; - out += "\n"; - out += "\n"; + out += "\n"; + out += "\n"; out += "\n"; int y = rand() % 130; @@ -198,11 +198,11 @@ void setup() digitalWrite(LED_BUILTIN, LED_OFF); Serial.begin(115200); - while (!Serial); + while (!Serial && millis() < 5000); delay(200); - Serial.print("\nStart Async_AdvancedWebServer on "); Serial.print(BOARD_NAME); + Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_SendArduinoString on "); Serial.print(BOARD_NAME); Serial.print(" with "); Serial.println(SHIELD_TYPE); Serial.println(PORTENTA_H7_ASYNC_TCP_VERSION); Serial.println(PORTENTA_H7_ASYNC_WEBSERVER_VERSION); From c414d9ea5bd4a9a835dfc90311ac55d0647b9a18 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Sun, 2 Oct 2022 15:18:00 -0400 Subject: [PATCH 10/12] Change name --- .../Async_AdvancedWebServer_MemoryIssues_Send C String.ino | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino index 157e5d4..db7e29f 100644 --- a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_Send C String/Async_AdvancedWebServer_MemoryIssues_Send C String.ino @@ -1,5 +1,5 @@ /**************************************************************************************************************************** - Async_AdvancedWebServer.ino - Dead simple AsyncWebServer for STM32 LAN8720 or built-in LAN8742A Ethernet + Async_AdvancedWebServer_MemoryIssues_Send_CString.ino - Dead simple AsyncWebServer for STM32 LAN8720 or built-in LAN8742A Ethernet For Portenta_H7 (STM32H7) with Vision-Shield Ethernet @@ -202,11 +202,11 @@ void setup() digitalWrite(LED_BUILTIN, LED_OFF); Serial.begin(115200); - while (!Serial); + while (!Serial && millis() < 5000); delay(200); - Serial.print("\nStart Async_AdvancedWebServer on "); Serial.print(BOARD_NAME); + Serial.print("\nStart Async_AdvancedWebServer_MemoryIssues_Send_CString on "); Serial.print(BOARD_NAME); Serial.print(" with "); Serial.println(SHIELD_TYPE); Serial.println(PORTENTA_H7_ASYNC_TCP_VERSION); Serial.println(PORTENTA_H7_ASYNC_WEBSERVER_VERSION); From c7a15b700e0df4035ea4b0168fb482eb9eba4ab2 Mon Sep 17 00:00:00 2001 From: Khoi Hoang <57012152+khoih-prog@users.noreply.github.com> Date: Sun, 2 Oct 2022 15:22:12 -0400 Subject: [PATCH 11/12] Restore auto-refresh --- .../Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino index d255ec5..61c213e 100644 --- a/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino +++ b/examples/Ethernet/Async_AdvancedWebServer_MemoryIssues_SendArduinoString/Async_AdvancedWebServer_MemoryIssues_SendArduinoString.ino @@ -1,5 +1,5 @@ /**************************************************************************************************************************** - Async_AdvancedWebServer.ino - Dead simple AsyncWebServer for STM32 LAN8720 or built-in LAN8742A Ethernet + Async_AdvancedWebServer_MemoryIssues_Send_CString.ino - Dead simple AsyncWebServer for STM32 LAN8720 or built-in LAN8742A Ethernet For Portenta_H7 (STM32H7) with Vision-Shield Ethernet @@ -105,6 +105,7 @@ void handleRoot(AsyncWebServerRequest *request) snprintf(temp, BUFFER_SIZE - 1, "\ \ +\ AsyncWebServer-%s\