Skip to content

Commit

Permalink
Fix RealtimeDatabase issue #206
Browse files Browse the repository at this point in the history
  • Loading branch information
mobizt committed Jan 17, 2025
1 parent bc1e8af commit c722ab0
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 59 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/mobizt/FirebaseClient/.github%2Fworkflows%2Fcompile_library.yml?logo=github&label=compile) [![Github Stars](https://img.shields.io/github/stars/mobizt/FirebaseClient?logo=github)](https://github.com/mobizt/FirebaseClient/stargazers) ![Github Issues](https://img.shields.io/github/issues/mobizt/FirebaseClient?logo=github)

![GitHub Release](https://img.shields.io/github/v/release/mobizt/FirebaseClient) ![Arduino](https://img.shields.io/badge/Arduino-v1.4.18-57C207?logo=arduino) ![PlatformIO](https://badges.registry.platformio.org/packages/mobizt/library/FirebaseClient.svg) ![GitHub Release Date](https://img.shields.io/github/release-date/mobizt/FirebaseClient)
![GitHub Release](https://img.shields.io/github/v/release/mobizt/FirebaseClient) ![Arduino](https://img.shields.io/badge/Arduino-v1.5.0-57C207?logo=arduino) ![PlatformIO](https://badges.registry.platformio.org/packages/mobizt/library/FirebaseClient.svg) ![GitHub Release Date](https://img.shields.io/github/release-date/mobizt/FirebaseClient)

[![GitHub Sponsors](https://img.shields.io/github/sponsors/mobizt?logo=github)](https://github.com/sponsors/mobizt)

Revision `2025-01-13T05:09:36Z`
Revision `2025-01-17T14:42:44Z`

## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "FirebaseClient",
"version": "1.4.18",
"version": "1.5.0",
"keywords": "communication, REST, esp32, esp8266, arduino",
"description": "Async Firebase Client library for Arduino.",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name=FirebaseClient

version=1.4.18
version=1.5.0

author=Mobizt

Expand Down
42 changes: 25 additions & 17 deletions resources/docs/async_result.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,19 @@ Get the copy of server response payload string.
String payload() const
```

3. ## 🔹 size_t length() const

Get the length of response payload string.

```cpp
size_t length() const
```

**Returns:**

- `String` - The copy of payload string.

3. ## 🔹 String path() const
4. ## 🔹 String path() const

Get the path of the resource of the request.

Expand All @@ -46,7 +54,7 @@ String path() const
- `String` - The path of the resource of the request.


4. ## 🔹 String etag() const
5. ## 🔹 String etag() const

Get the Etag of the server response headers.

Expand All @@ -59,7 +67,7 @@ String etag() const
- `String` - The ETag of response header.


5. ## 🔹 String uid() const
6. ## 🔹 String uid() const

Get the unique identifier of async task.

Expand All @@ -72,7 +80,7 @@ String uid() const
- `String` - The UID of async task.


6. ## 🔹 String debug()
7. ## 🔹 String debug()

Get the debug information.

Expand All @@ -84,15 +92,15 @@ String debug()

- `String` - The debug information.

7. ## 🔹 void clear()
8. ## 🔹 void clear()

Clear the async result.

```cpp
void clear()
```

8. ## 🔹 RealtimeDatabaseResult &to()
9. ## 🔹 RealtimeDatabaseResult &to()

Get the reference to the internal RealtimeDatabaseResult object.

Expand All @@ -105,7 +113,7 @@ RealtimeDatabaseResult &to()
- `RealtimeDatabaseResult &` - The reference to the internal RealtimeDatabaseResult object.


9. ## 🔹 int available()
10. ## 🔹 int available()

Get the number of bytes of available response payload.

Expand All @@ -118,7 +126,7 @@ int available()
- `int` - The number of bytes available.


10. ## 🔹 app_event_t &appEvent()
11. ## 🔹 app_event_t &appEvent()

Get the reference of internal app event information.

Expand All @@ -131,7 +139,7 @@ app_event_t &appEvent()
- `app_event_t &` - The reference of internal app event.


11. ## 🔹 bool uploadProgress()
12. ## 🔹 bool uploadProgress()

Check if file/BLOB upload information is available.

Expand All @@ -144,7 +152,7 @@ bool uploadProgress()
- `bool` - Returns true if upload information is available.


12. ## 🔹 upload_data_t uploadInfo() const
13. ## 🔹 upload_data_t uploadInfo() const

Get the file/BLOB upload information.

Expand All @@ -157,7 +165,7 @@ upload_data_t uploadInfo() const
- `upload_data_t` - The file/BLOB upload information.


13. ## 🔹 bool downloadProgress()
14. ## 🔹 bool downloadProgress()

Check if the file/BLOB download information is availablle.

Expand All @@ -170,7 +178,7 @@ bool downloadProgress()
- `bool` - Returns true if download information is available.


14. ## 🔹 download_data_t downloadInfo() const
15. ## 🔹 download_data_t downloadInfo() const

Get the file/BLOB download information.

Expand All @@ -183,7 +191,7 @@ download_data_t downloadInfo() const
- `download_data_t` - The file/BLOB download information.


15. ## 🔹 bool isOTA() const
16. ## 🔹 bool isOTA() const

Check if the result is from OTA download task.

Expand All @@ -196,7 +204,7 @@ bool isOTA() const
- `bool` - Returns true if the result is from OTA download task.


16. ## 🔹 bool isError()
17. ## 🔹 bool isError()

Check if the error occurred in async task.

Expand All @@ -209,7 +217,7 @@ bool isError()
- `bool` - Returns true if error occurred.


17. ## 🔹 bool isDebug()
18. ## 🔹 bool isDebug()

Check if the debug information in available.

Expand All @@ -222,7 +230,7 @@ bool isDebug()
- `bool` - Returns true if debug information in available.


18. ## 🔹 bool isEvent()
19. ## 🔹 bool isEvent()

Check if the app event information in available.

Expand All @@ -235,7 +243,7 @@ bool isEvent()
- `bool` - Returns true if app event information in available.


19. ## 🔹 FirebaseError &error()
20. ## 🔹 FirebaseError &error()

Get the reference of internal FirebaseError object.

Expand Down
96 changes: 66 additions & 30 deletions src/core/AsyncClient/AsyncClient.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Created January 14, 2025
* Created January 17, 2025
*
* For MCU build target (CORE_ARDUINO_XXXX), see Options.h.
*
Expand Down Expand Up @@ -858,11 +858,14 @@ class AsyncClientClass : public ResultBase, RTDBResultBase
if (!readPayload(sData))
return false;

String *payload = &sData->response.val[res_hndlr_ns::payload];

if (sData->response.flags.sse || !sData->response.flags.payload_remaining)
{
if (!sData->auth_used)
{
sData->aResult.setPayload(sData->response.val[res_hndlr_ns::payload]);
if (!sData->response.flags.sse)
sData->aResult.setPayload(*payload);

if (sData->aResult.download_data.total > 0)
clearAppData(sData->aResult.app_data);
Expand All @@ -872,37 +875,43 @@ class AsyncClientClass : public ResultBase, RTDBResultBase
if (sData->request.method == async_request_handler_t::http_post)
parseNodeName(&sData->aResult.rtdbResult);

// data available from sse event
if (sData->response.flags.sse && sData->response.val[res_hndlr_ns::payload].length())
// Data available from sse event
if (sData->response.flags.sse && payload->length())
{
// order of checking: event, data, newline
if (sData->response.val[res_hndlr_ns::payload].indexOf("event: ") > -1 && sData->response.val[res_hndlr_ns::payload].indexOf("data: ") > -1 && sData->response.val[res_hndlr_ns::payload].indexOf("\n") > -1)

if (payload->indexOf("event: ") > -1 && payload->indexOf("data: ") > -1 && payload->indexOf("\n") > -1)
{
// Prevent sse timeout due to large sse Stream playload
feedSSETimer(&sData->aResult.rtdbResult);

parseSSE(&sData->aResult.rtdbResult);

// Event filtering.
String event = sData->aResult.rtdbResult.event();
if (sse_events_filter.length() == 0 ||
(sData->response.flags.http_response && sse_events_filter.indexOf("get") > -1 && event.indexOf("put") > -1) ||
(!sData->response.flags.http_response && sse_events_filter.indexOf("put") > -1 && event.indexOf("put") > -1) ||
(sse_events_filter.indexOf("patch") > -1 && event.indexOf("patch") > -1) ||
(sse_events_filter.indexOf("keep-alive") > -1 && event.indexOf("keep-alive") > -1) ||
(sse_events_filter.indexOf("cancel") > -1 && event.indexOf("cancel") > -1) ||
(sse_events_filter.indexOf("auth_revoked") > -1 && event.indexOf("auth_revoked") > -1))
{
// save payload to slot result
sData->aResult.setPayload(sData->response.val[res_hndlr_ns::payload]);
clear(sData->response.val[res_hndlr_ns::payload]);
sData->response.flags.payload_available = true;
returnResult(sData, true);
}
else
if ((*payload)[payload->length() - 1] == '\n' && sData->response.tcpAvailable(client_type, client, async_tcp_config) == 0)
{
clear(sData->response.val[res_hndlr_ns::payload]);
}
setRefPayload(&sData->aResult.rtdbResult, payload);
parseSSE(&sData->aResult.rtdbResult);

// Event filtering.
String event = sData->aResult.rtdbResult.event();
if (sse_events_filter.length() == 0 ||
(sData->response.flags.http_response && sse_events_filter.indexOf("get") > -1 && event.indexOf("put") > -1) ||
(!sData->response.flags.http_response && sse_events_filter.indexOf("put") > -1 && event.indexOf("put") > -1) ||
(sse_events_filter.indexOf("patch") > -1 && event.indexOf("patch") > -1) ||
(sse_events_filter.indexOf("keep-alive") > -1 && event.indexOf("keep-alive") > -1) ||
(sse_events_filter.indexOf("cancel") > -1 && event.indexOf("cancel") > -1) ||
(sse_events_filter.indexOf("auth_revoked") > -1 && event.indexOf("auth_revoked") > -1))
{
// save payload to slot result
sData->aResult.setPayload(*payload);
clear(*payload);
sData->response.flags.payload_available = true;
returnResult(sData, true);
}
else
{
clear(*payload);
}

sData->response.flags.http_response = false;
sData->response.flags.http_response = false;
}
}
}
#endif
Expand Down Expand Up @@ -1142,7 +1151,16 @@ class AsyncClientClass : public ResultBase, RTDBResultBase

if (sData->response.flags.chunks)
{
if (decodeChunks(sData, client, &sData->response.val[res_hndlr_ns::payload]) == -1)
// Use temporary String buffer for decodeChunks
String temp;
int res = decodeChunks(sData, client, &temp);
if (temp.length())
{
reserveString(sData);
sData->response.val[res_hndlr_ns::payload] += temp;
}

if (res == -1)
sData->response.flags.payload_remaining = false;
}
else
Expand Down Expand Up @@ -1294,7 +1312,14 @@ class AsyncClientClass : public ResultBase, RTDBResultBase
}
}
else
sData->response.payloadRead += readLine(sData, sData->response.val[res_hndlr_ns::payload]);
{
// Use temporary String buffer for readLine
String temp;
size_t len = readLine(sData, temp);
sData->response.payloadRead += len;
reserveString(sData);
sData->response.val[res_hndlr_ns::payload] += temp;
}
}
}
}
Expand Down Expand Up @@ -1354,6 +1379,17 @@ class AsyncClientClass : public ResultBase, RTDBResultBase
return sData->error.code == 0;
}

void reserveString(async_data_item_t *sData)
{
// String memory reservation is needed to hadle large data in external memory.
#if defined(ENABLE_PSRAM) && ((defined(ESP8266) && defined(MMU_EXTERNAL_HEAP)) || (defined(ESP32) && defined(BOARD_HAS_PSRAM)))
String old = sData->response.val[res_hndlr_ns::payload];
sData->response.val[res_hndlr_ns::payload].remove(0, sData->response.val[res_hndlr_ns::payload].length());
sData->response.val[res_hndlr_ns::payload].reserve(sData->response.payloadRead + 1);
sData->response.val[res_hndlr_ns::payload] = old;
#endif
}

// non-block memory buffer for collecting the multiple of 4 data prepared for base64 decoding
uint8_t *asyncBase64Buffer(async_data_item_t *sData, Memory &mem, int &toRead, int &read)
{
Expand Down
9 changes: 8 additions & 1 deletion src/core/AsyncResult/AsyncResult.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Created December 27, 2024
* Created January 17, 2025
*
* The MIT License (MIT)
* Copyright (c) 2025 K. Suwatchai (Mobizt)
Expand Down Expand Up @@ -228,6 +228,13 @@ class AsyncResult : public ResultBase, RealtimeDatabaseResult
*/
String payload() const { return val[ares_ns::data_payload].c_str(); }

/**
* Get the length of response payload string.
*
* @return The payload string length.
*/
size_t length() const { return val[ares_ns::data_payload].length(); }

/**
* Get the path of the resource of the request.
*
Expand Down
Loading

0 comments on commit c722ab0

Please sign in to comment.