ESPAsyncWebServer icon indicating copy to clipboard operation
ESPAsyncWebServer copied to clipboard

Need Help for chunked response

Open faildproject opened this issue 3 years ago • 3 comments

Hello, i have written a Sub-Library that parse a JSON-File and build a Webpage on request. Simplifyed by concatenate some Strings with Sub-Methods. When the String grown up in size then i did'nt get the expected response. I know, that there is a Limit (found in other Issue). So I think one solution is to use chunked response. But I did'nt know how to implement it in my Sub-Library and need an short example.

The Header will always returned, but if the dynamic part is to big than all the other parts not returned.

Pseudocode whats now implemented:

void WebService::_pageBuilder() {
String jsonPages[] = {"abc","def",...};
for(page in jsonPages){     //-Pseudo-
  _server->on(page, HTTP_GET, [&](AsyncWebServerRequest *request){
          AsyncResponseStream *response = request->beginResponseStream("text/html");
          
           //-----------Start of HTML-Doc--------
          String head = "A Header, thats the same on all Pages";
          response->print(head);

          //-----------Dynamic Part--------
          //--- Need as chunked response ---
          DynamicJsonDocument doc(expected_size); // Validated and worked
          _robotData->readConfigFile(doc);
          
          JsonArray cards = doc["Pages"][page]["Cards"].as<JsonArray>();
          for(JsonVariant card : cards){
            String cardID = "C" + card.as<String>();
            response->print(_buildCard(doc,cardID.c_str()));  //Produces long Strings
          }
          
          //--------End of HTML-Doc------------
          String end = " </div>\
              </div>\
              <script src=\"/jquery-3.6.0.min.js\"></script>\
              <script src=\"/scriptV2.js\"></script>\
          </body>\
          </html>";
          response->print(end);
          return request->send(response);
      });  
    }
}

Can someone help me and give me a short example? Thank you Folks

faildproject avatar Jul 20 '22 16:07 faildproject

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 02 '22 00:11 stale[bot]

Chunked responses are handled as follows in this library. You use sendChunked, or beginChunkedResponse if you want custom headers, and give that a callback.

This callback will then be repeatedly called to generate the individual chunks of your file, each of which is then sent to the client, after which the next chunk will be generated. This repeats until your callback returns zero generated bytes, which marks the end of the file.

For this your callback gets a buffer, in which it should write the data, maxLen, which is the maximum amount of data in bytes which it is allowed to generate for this chunk, and index, which is the sum of how many bytes all previous calls generated. Your callback has to return how many bytes it actually generated.

That means while you would have to rewrite your entire response handling, you would primarily have to change _buildCard to work with this.

If you happen to know the exact number of bytes your final page will be long, you can also use this same callback mechanism to send a file as a regular non-chunked response.

Here is a simple example of a chunked response like that:

#include <ESPAsyncWebServer.h>

#include <stdio.h>

extern const char WIFI_SSID[] = "WiFi_SSID";
extern const char WIFI_PASS[] = "WPA_PASSPHRASE";

AsyncWebServer server(80);

void onWiFiGotIp(WiFiEvent_t event, WiFiEventInfo_t eventInfo) {
	Serial.print("IP Address: ");
	Serial.println((IPAddress) eventInfo.got_ip.ip_info.ip.addr);
}

void onWiFiDisonnected(WiFiEvent_t event) {
	Serial.println("WiFi Disconnected");
	WiFi.disconnect(true);
	delay(100);
	WiFi.begin(WIFI_SSID, WIFI_PASS);
}

void setupWebServer() {
	server.on("/", HTTP_GET, [] (AsyncWebServerRequest *request) {
		std::shared_ptr<uint16_t> counter = std::make_shared<uint16_t>(0);
		AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [counter](uint8_t *buffer, size_t maxLen, size_t index) {
			if (*counter < 50) {
				int len = sprintf((char*)buffer, "Content Line %d\n", *counter);
				(*counter)++;
				return len;
			}
			return 0;
		});
		request->send(response);
	});

	server.onNotFound([] (AsyncWebServerRequest *request) {
		request->send(404, "text/plain", "Not Found");
	});

	server.begin();
}

void setup() {
	Serial.begin(115200);
	Serial.println("setup start");

	WiFi.disconnect(true);
	WiFi.onEvent(onWiFiGotIp, SYSTEM_EVENT_STA_GOT_IP);
	WiFi.onEvent(onWiFiDisonnected, SYSTEM_EVENT_STA_DISCONNECTED);
	WiFi.begin(WIFI_SSID, WIFI_PASS);

	setupWebServer();
	Serial.println("setup end");
}

void loop() {
	delay(100);
}

This sketch sends 50 lines of plain text as a chunked response in 50 chunks. It can easily be modified to send however many lines you like, and demonstrates how to use chunked responses.

ToMe25 avatar May 24 '23 19:05 ToMe25

[STALE_CLR] This issue has been removed from the stale queue. Please ensure activity to keep it openin the future.

stale[bot] avatar May 24 '23 19:05 stale[bot]