Closed Levak closed 2 weeks ago
Reduced the scope to the changes made to AsyncWebServerRequest::send
:
void AsyncWebServerRequest::send(AsyncWebServerResponse* response) {
+ if (_sent)
+ return;
+ if (_response)
+ delete _response;
_response = response;
if (_response == NULL) {
_client->close(true);
@@ -716,15 +730,8 @@ void AsyncWebServerRequest::send(AsyncWebServerResponse* response) {
_sent = true;
return;
}
- if (!_response->_sourceValid()) {
- delete response;
- _response = NULL;
+ if (!_response->_sourceValid())
send(500);
- } else {
- _client->setRxTimeout(0);
- _response->_respond(this);
- _sent = true;
- }
}
If I keep the red(-) part, my code works. If I use the green(+) part, my code yields no files. As if my File
was closed by a destructor.
thanks for the report! I appreciate the work and time spent in your findings which helps me answer more precisely about what is happening.
The diff you are talking to is related to the introduction of middlewares.
Before, calling send() would commit the response on the wire (_response->_respond(this);
), so your variables below are still in scope when calling send().
File root = fileSystem.open("/");
String txt;
bool finished;
Now, send()
just "registers" the response, in order to let next middlewares interact with its headers, or even eventually completely replace it. So the response object is leaving the scope of your handler and its variables get destroyed (root, txt and finished).
The callback of beginChunkedResponse
can be recalled until completion, and it captures these variables by copy.
The correct way of doing what you want to do is to use the request->_tempObject
pointer to hold the states. Right now, I suspect that the way you have coded the function was hiding the fact that your variables going out of scope could have an impact on your code.
Something like that would ensure that your request object becomes the owner of the states:
struct States {
File root;
String txt;
bool finished;
};
server.on("/chunk", HTTP_GET, [](AsyncWebServerRequest* request) {
States* states = new States();
states.root = ...
states.txt = ...
states.finished = ...
request->_tempObject = states;
AsyncWebServerResponse* response = request->beginChunkedResponse("text/html", [request](uint8_t* buffer, size_t maxLen, size_t index) -> size_t {
States* states = (States*) request->_tempObject;
// ...
});
request->send(response);
});
You don't even need to free the _tempObject
when finished: ESPAsyncWebServer does that fro you when the request tis destroyed, as a safety measure in case your callback is not called to handle the "finished" state
Hi ! Thank you for your detailed answer, I did suspect I was doing something wrong, just clueless why such change would matter.
Nevertheless, I did try to implement your solution, and I'm afraid it's still doing the same.
struct HandleListStates {
File root;
String txt;
bool finished;
};
void handleList(AsyncWebServerRequest *request)
{
struct HandleListStates* states = new HandleListStates();
states->root = fileSystem.open("/");
request->_tempObject = states;
AsyncWebServerResponse *response = request->beginChunkedResponse(
"application/json",
[request]
(uint8_t* buffer, const size_t max_len, const size_t index) mutable -> size_t
{
struct HandleListStates* states = (struct HandleListStates*) request->_tempObject;
[...]
while (File file = states->root.openNextFile()) { // still not entering the loop
[...]
}
[...]
});
request->send(response);
}
Any idea ?
Regards,
Not on my mind... I will have to check deeper because this is supposed to work. What you did here is right.
I will add an example in the SimpleServer code and get back to you so that you can complete this example in case I am not able to reproduce.
I tried to move the filesystem.open()
inside the chunk handler, I get this in the logs:
[ 65343][E][vfs_api.cpp:23] open(): File system is not mounted
Which should indicate us something is wrong with the context. Or is it still the same issue as loosing the variable out of scope?
[...]
if (index == 0) {
states->root = fileSystem.open(states->path);
[...]
Or is it still the same issue as loosing the variable out of scope?
No since the states are within the request object, which stays the same and the callback can be called multiple times until completion...
File system is not mounted
is weird though...
Do you have the ability to test with LittleFS instead of SD_MMC ? That's what I was goign to test anyway in the simple example...
I am so sorry, I just understood what went wrong. In this project, we mount and unmount the SD card only when necessary. Since we go out of context, we unmount the SD, so when the handler triggers, we already have the SD unmounted.
Now it works 🎉
So, in the end, I did need your solution, but I also needed to not unmount the SD card at the end of the handler ^^
There is no bug there, assuming we use a context saved in _tempObject
. I'd recon adding a small example would help the next person that encounters this ^^.
Awesome!
So, in the end, I did need your solution, but I also needed to not unmount the SD card at the end of the handler ^^
Ahah! yes, that's right! what you can do though is unmount from within the callback in the if(finished)
block because you know that the callback won't be called after and the response is then fully sent.
There is no bug there, assuming we use a context saved in _tempObject. I'd recon adding a small example would help the next person that encounters this ^^.
This thing comes from the original repo and was a little bit documented in the original doc:
https://github.com/mathieucarbou/ESPAsyncWebServer?tab=readme-ov-file#body-data-handling
I will move this into a discussion for reference purposes. This was an interesting use case ;-)
Description
I upgraded ESPAsyncWebServer from v3.1.0 to the latest v3.3.x and found out that
beginChunkedResponse
behaved differently. I am unsure what causes the exact issue, but the result is that my request acts like there is no file in the directory I am traversing.I managed to git-bisect by hand and find the problematic commit: 571eac4d0093dc51f1cf592788f24a6e20f84f40
Before that commit, I can traverse
File root
for files inside. After that commit, I never enter the while loop.Here is a simplified view of the function:
Result:
Result in logs:
While when using commit 741841a078069e8bbeb6b27bde11e5c543710993 it works and here is the output in logs:
Full source code: Link to the source code where the issue happens
Board: esp32-pico-d4 (SD WIFI PRO)
Considering this is a dual-core ESP32, I did try to set
CONFIG_ASYNC_TCP_RUNNING_CORE
to0
or1
with no success.