// SPDX-License-Identifier: LGPL-3.0-or-later // Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov // // Shows how to wait in a chunk response for incoming data // #include #ifdef ESP32 #include #include #elif defined(ESP8266) #include #include #elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350) #include #include #endif #include #if __has_include("ArduinoJson.h") #include #include #include #endif static const char *htmlContent PROGMEM = R"( Sample HTML

Hello, World!

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin euismod, purus a euismod rhoncus, urna ipsum cursus massa, eu dictum tellus justo ac justo. Quisque ullamcorper arcu nec tortor ullamcorper, vel fermentum justo fermentum. Vivamus sed velit ut elit accumsan congue ut ut enim. Ut eu justo eu lacus varius gravida ut a tellus. Nulla facilisi. Integer auctor consectetur ultricies. Fusce feugiat, mi sit amet bibendum viverra, orci leo dapibus elit, id varius sem dui id lacus.

)"; static const size_t htmlContentLength = strlen_P(htmlContent); static AsyncWebServer server(80); static AsyncLoggingMiddleware requestLogger; static String triggerUART; static int key = -1; void setup() { Serial.begin(115200); #ifndef CONFIG_IDF_TARGET_ESP32H2 WiFi.mode(WIFI_AP); WiFi.softAP("esp-captive"); #endif // adds some internal request logging for debugging requestLogger.setEnabled(true); requestLogger.setOutput(Serial); server.addMiddleware(&requestLogger); #if __has_include("ArduinoJson.h") // // HOW TO RUN THIS EXAMPLE: // // 1. Trigger a request that will be blocked for a long time: // > time curl -v -X POST http://192.168.4.1/api -H "Content-Type: application/json" -d '{"input": "Please type a key to continue in Serial console..."}' --output - // // 2. While waiting, in another terminal, run some concurrent requests: // > time curl -v http://192.168.4.1/ // // 3. Type a key in the Serial console to continue the processing within 30 seconds. // This should unblock the first request. // server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { // need to cast to uint8_t* // if you do not, the const char* will be copied in a temporary String buffer request->send(200, "text/html", (uint8_t *)htmlContent, htmlContentLength); }); server.on( "/api", HTTP_POST, [](AsyncWebServerRequest *request) { // request parsing has finished // no data ? if (!((String *)request->_tempObject)->length()) { request->send(400); return; } JsonDocument doc; // deserialize and check for errors if (deserializeJson(doc, *(String *)request->_tempObject)) { request->send(400); return; } // start UART com: UART will send the data to the Serial console and wait for the key press triggerUART = doc["input"].as(); key = -1; AsyncWebServerResponse *response = request->beginChunkedResponse("text/plain", [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t { // still waiting for UARY ? if (triggerUART.length() && key == -1) { return RESPONSE_TRY_AGAIN; } // finished ? if (!triggerUART.length() && key == -1) { return 0; // 0 means we are done } // log_d("UART answered!"); String answer = "You typed: "; answer.concat((char)key); // note: I did not check for maxLen, but you should (see ChunkResponse.ino) memcpy(buffer, answer.c_str(), answer.length()); // finish! triggerUART = emptyString; key = -1; return answer.length(); }); request->send(response); }, NULL, // upload handler is not used so it should be NULL [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { // log_d("Body: index: %u, len: %u, total: %u", index, len, total); if (!index) { // log_d("Start body parsing"); request->_tempObject = new String(); // cast request->_tempObject pointer to String and reserve total size ((String *)request->_tempObject)->reserve(total); // set timeout 30s request->client()->setRxTimeout(30); } // log_d("Append body data"); ((String *)request->_tempObject)->concat((const char *)data, len); } ); #endif server.begin(); } void loop() { if (triggerUART.length() && key == -1) { Serial.println(triggerUART); // log_d("Waiting for UART input..."); while (!Serial.available()) { delay(100); } key = Serial.read(); Serial.flush(); // log_d("UART input: %c", key); triggerUART = emptyString; } }