AsyncRecursiveResponse#236
Conversation
changed # include <Wstring.h> to # include <WString.h>
Added recursive callbacks
|
Please add exa,ple to the readme at least as well, so people will know how to use it :) |
|
I will do, but I've been playing around with things a bit... and I'm not quite sure about a few things. So I'll post my issues here, you guys can let me know what you'd like to see. At the moment its pretty straight forward, something like:
RecursiveNode takes a string (in the form of a StringWrapper) and a callback. The string is written to the TCP connection, and when a template is found, the callback is called. The callback then in turn returns a new node, and things continue recursively. What I'm finding though as I'm using this in my own little project (a home made aquarium controller) is that the string and the callback always go hand in hand. So much so that I'm wondering if I should change things a bit and allow custom RecursiveNode's via inheritence. That way a person could use the typical string + callback or derive (via inheritence) their own RecursiveNode's to be added to the stack. The latter version would be more flexible, but would require a bit more documentation describing all the different node types, and why one would choose one node over another. Also without a namespace it has the possibility to add a bit of namespace pollution. I could also wrap StringWrapper into it (to an extent) which would give a tiny bit more performance. I don't know, I'm kinda on the fence on this. |
|
Nice one. So it not only implements template engine, but reduces memory consumption in ESPAsyncWebServer in general, if compared to other response types? Recursive template parsing can be implemented by user calling a template processor function recursively himself, but if these classes reduce memory consumption, it might be worth moving away from String class to these StringSource and StringWrapper. |
|
Ya, that's the idea. I made a few bug fixes on my end, but I've been real busy IRL the last week or so and haven't gotten around to uploading/fixing a few more small bugs. But ya, ESPAcyn all through its implementation uses String. While that makes sense for PCs, for a small MCu every byte matters, and we should only be allocating dynamic memory only when absolutely necessary. So StringSource and StringWrapper allocate memory only when absolutely necessary, and seemlessly allow integration with progmem strings, both of which allow significantly less memory usage. If me-no-dev wants to just take StringSource and StringWrapper (and the PGMHelpers) and integrate them into ESPAsync I'm fine with that. There is a small bug as well that I've had a hard time tracking down. It seems if I try to access a large-ish number of web requests (like 10+) within a short period of time (1-2 min) occasionally requests will be refused, and you have to wait a min or two to retry before it keeps working again. I've ruled out a memory issue. My thinking at this point is either it has something to do with the way the ESP8266 manages TCP connections, something to do with the timeout. The other thought was that in order to get decent transmission speeds I write a packet one 'ack' ahead, so while the previous packet is 'en route' the next packet is already being sent. This may or may not be a problem, I'm unsure. The docs on espconn state that you should not call send before the previous send callback. Except ESPAsync uses lwip and not the espconn functions, so does it apply to lwip? Its hard to say and would need some testing. Sadly at this point I'm a bit busy and haven't had the chance to get to the bottom of it. I also need to post an example or two. "Recursive template parsing can be implemented by user calling a template processor function recursively himself". Didn't realize that, that is cool. I guess I'll have to look a little closer. |
Added AsyncRecursiveResponse and a few supporting files: PGMHelper.h, StringSource.h/.cpp, StringWrapper.h/.cpp.
Basic use:
create a callback function (this function will be called every time a 'template' is found):
StringWrapper RRCallback(StringSource src) {
if (src == "ROOT") return { root_text, StringWrapperType::st_progmem };
// ...
return { "error", StringWrapperType::st_literal };
}
create the request handler:
void OnRoot(AsyncWebServerRequest* request) {
AsyncRecursiveReponse* response = new AsyncRecursiveReponse(request, RRCallback, { "%ROOT%", StringWrapperType::st_literal });
response->setCode(200);
response->setContentType("text/html");
response->addHeader("Server","ESP Async Web Server");
request->send(response);
}
That's about it. A StringSource is simply a string view object, it doesn't own string data, and is designed to work with both normal strings and progmem strings. A StringWrapper is used to pass string data from the callback. It supports string literals, progmem literals, and dynamic (new and malloc) strings. When passing dynamically allocated string (new or malloc) the StringWrapper owns the string data, so the library user should NOT delete/free the data when passed to StringWrapper. StringWrapper is move only by design.