forked from me-no-dev/ESPAsyncWebServer
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
**Request Continuation** is the ability to pause the processing of a request (the actual sending over the network) to be able to let another task commit the response on the network later. This is a common supported use case amongst web servers. A uage example is described in the following discussion: #34 Currently, the only supported way is to use chunk encoding and return `RESPONSE_TRY_AGAIN` from the callback until the processing is done somewhere else, then send the response in the chunk buffer, when the processing is completed.
- Loading branch information
1 parent
1b29f18
commit c638175
Showing
5 changed files
with
292 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
// SPDX-License-Identifier: LGPL-3.0-or-later | ||
// Copyright 2016-2025 Hristo Gochkov, Mathieu Carbou, Emil Muratov | ||
|
||
// | ||
// Shows how to use request continuation to pause a request for a long processing task, and be able to resume it later. | ||
// | ||
|
||
#include <Arduino.h> | ||
#ifdef ESP32 | ||
#include <AsyncTCP.h> | ||
#include <WiFi.h> | ||
#elif defined(ESP8266) | ||
#include <ESP8266WiFi.h> | ||
#include <ESPAsyncTCP.h> | ||
#elif defined(TARGET_RP2040) | ||
#include <WebServer.h> | ||
#include <WiFi.h> | ||
#endif | ||
|
||
#include <ESPAsyncWebServer.h> | ||
#include <list> | ||
#include <mutex> | ||
|
||
static AsyncWebServer server(80); | ||
|
||
// =============================================================== | ||
// The code below is used to simulate some long running operations | ||
// =============================================================== | ||
|
||
typedef struct { | ||
size_t id; | ||
AsyncWebServerRequestPtr requestPtr; | ||
uint8_t data; | ||
} LongRunningOperation; | ||
|
||
static std::list<LongRunningOperation *> longRunningOperations; | ||
static size_t longRunningOperationsCount = 0; | ||
static std::mutex longRunningOperationsMutex; | ||
|
||
static void startLongRunningOperation(AsyncWebServerRequestPtr requestPtr) { | ||
std::lock_guard<std::mutex> lock(longRunningOperationsMutex); | ||
|
||
LongRunningOperation *op = new LongRunningOperation(); | ||
op->id = ++longRunningOperationsCount; | ||
op->data = random(5, 20); | ||
|
||
// you need to hold the AsyncWebServerRequestPtr returned by pause(); | ||
// This object is authorized to leave the scope of the request handler. | ||
op->requestPtr = requestPtr; | ||
|
||
Serial.printf("[%u] Start long running operation for %" PRIu8 " seconds...\n", op->id, op->data); | ||
longRunningOperations.push_back(op); | ||
} | ||
|
||
static bool processLongRunningOperation(LongRunningOperation *op) { | ||
// request was deleted ? | ||
if (op->requestPtr.expired()) { | ||
Serial.printf("[%u] Request was deleted - stopping long running operation\n", op->id); | ||
return true; // operation finished | ||
} | ||
|
||
// processing the operation | ||
Serial.printf("[%u] Long running operation processing... %" PRIu8 " seconds left\n", op->id, op->data); | ||
|
||
// check if we have finished ? | ||
op->data--; | ||
if (op->data) { | ||
// not finished yet | ||
return false; | ||
} | ||
|
||
// Try to get access to the request pointer if it is still exist. | ||
// If there has been a disconnection during that time, the pointer won't be valid anymore | ||
if (auto request = op->requestPtr.lock()) { | ||
Serial.printf("[%u] Long running operation finished! Sending back response...\n", op->id); | ||
request->send(200, "text/plain", String("Long running operation ") + op->id + " finished."); | ||
|
||
} else { | ||
Serial.printf("[%u] Long running operation finished, but request was deleted!\n", op->id); | ||
} | ||
|
||
return true; // operation finished | ||
} | ||
|
||
/// ========================================================== | ||
|
||
void setup() { | ||
Serial.begin(115200); | ||
|
||
#ifndef CONFIG_IDF_TARGET_ESP32H2 | ||
WiFi.mode(WIFI_AP); | ||
WiFi.softAP("esp-captive"); | ||
#endif | ||
|
||
// Add a middleware to see how pausing a request affects the middleware chain | ||
server.addMiddleware([](AsyncWebServerRequest *request, ArMiddlewareNext next) { | ||
Serial.printf("Middleware chain start\n"); | ||
|
||
// continue to the next middleware, and at the end the request handler | ||
next(); | ||
|
||
// we can check the request pause state after the handler was executed | ||
if (request->isPaused()) { | ||
Serial.printf("Request was paused!\n"); | ||
} | ||
|
||
Serial.printf("Middleware chain ends\n"); | ||
}); | ||
|
||
// HOW TO RUN THIS EXAMPLE: | ||
// | ||
// 1. Open several terminals to trigger some requests concurrently that will be paused with: | ||
// > time curl -v http://192.168.4.1/ | ||
// | ||
// 2. Look at the output of the Serial console to see how the middleware chain is executed | ||
// and to see the long running operations being processed and resume the requests. | ||
// | ||
// 3. You can try close your curl comand to cancel the request and check that the request is deleted. | ||
// Note: in case the network is disconnected, the request will be deleted. | ||
// | ||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { | ||
// Print a message in case the request is disconnected (network disconnection, client close, etc.) | ||
request->onDisconnect([]() { | ||
Serial.printf("Request was disconnected!\n"); | ||
}); | ||
|
||
// Instruct ESPAsyncWebServer to pause the request and get a AsyncWebServerRequestPtr to be able to access the request later. | ||
// The AsyncWebServerRequestPtr is the ONLY object authorized to leave the scope of the request handler. | ||
// The Middleware chain will continue to run until the end after this handler exit, but the request will be paused and will not | ||
// be sent to the client until send() is called later. | ||
Serial.printf("Pausing request...\n"); | ||
AsyncWebServerRequestPtr ptr = request->pause(); | ||
|
||
// start our long operation... | ||
startLongRunningOperation(ptr); | ||
}); | ||
|
||
server.begin(); | ||
} | ||
|
||
static uint32_t lastTime = 0; | ||
|
||
void loop() { | ||
if (millis() - lastTime >= 1000) { | ||
|
||
#ifdef ESP32 | ||
Serial.printf("Free heap: %" PRIu32 "\n", ESP.getFreeHeap()); | ||
#endif | ||
|
||
std::lock_guard<std::mutex> lock(longRunningOperationsMutex); | ||
|
||
// process all long running operations | ||
std::list<LongRunningOperation *> finished; | ||
for (auto it = longRunningOperations.begin(); it != longRunningOperations.end(); ++it) { | ||
bool done = processLongRunningOperation(*it); | ||
if (done) { | ||
finished.push_back(*it); | ||
} | ||
} | ||
|
||
// remove finished operations | ||
for (LongRunningOperation *op : finished) { | ||
Serial.printf("[%u] Deleting finished long running operation\n", op->id); | ||
longRunningOperations.remove(op); | ||
delete op; | ||
} | ||
|
||
lastTime = millis(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.