Skip to content

Commit 9d44061

Browse files
ritesh006SuGliderpre-commit-ci-lite[bot]
authored
feat(webserver): add support for chunked HTTP responses Fixes #5080 (#11894)
* Add chunkedResponseModeStart, sendChunk, chunkedResponseFinalize to WebServer library for chunked HTTP responses * feat(webserver): Chunk example for ESP32 HTTP server This example demonstrates how to send an HTTP response using chunks with an ESP32 server. It includes setup for WiFi, MDNS, and handles HTTP requests with chunked responses. * feat(webserver): Add ci.json for ChunkWriting example configuration * fix(example): not necessary code line * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Sugar Glider <rodrigo.garcia@espressif.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent 01a96fc commit 9d44061

File tree

4 files changed

+159
-0
lines changed

4 files changed

+159
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* This example demonstrates how to send an HTTP response using chunks
3+
* It will create an HTTP Server (port 80) associated with an a MDNS service
4+
* Access the HTTP server using a Web Browser:
5+
* URL can be composed using the MDNS name "esp32_chunk_resp.local"
6+
* http://esp32_chunk_resp.local/
7+
* or the IP Address that will be printed out, such as for instance 192.168.1.10
8+
* http://192.168.1.10/
9+
*
10+
* ESP32 Server response can also be viewed using the curl command:
11+
* curl -i esp32_chunk_resp.local:80
12+
* curl -i --raw esp32_chunk_resp.local:80
13+
*/
14+
15+
#include <WiFi.h>
16+
#include <NetworkClient.h>
17+
#include <WebServer.h>
18+
#include <ESPmDNS.h>
19+
20+
const char *ssid = "........";
21+
const char *password = "........";
22+
23+
WebServer server(80);
24+
25+
void handleChunks() {
26+
uint8_t countDown = 10;
27+
server.chunkResponseBegin();
28+
char countContent[8];
29+
while (countDown) {
30+
sprintf(countContent, "%d...\r\n", countDown--);
31+
server.chunkWrite(countContent, strlen(countContent));
32+
// count down shall show up in the browser only after about 5 seconds when finishing the whole transmission
33+
// using "curl -i esp32_chunk_resp.local:80", it will show the count down as it sends each chunk
34+
delay(500);
35+
}
36+
server.chunkWrite("DONE!", 5);
37+
server.chunkResponseEnd();
38+
}
39+
40+
void setup(void) {
41+
Serial.begin(115200);
42+
WiFi.mode(WIFI_STA);
43+
WiFi.begin(ssid, password);
44+
Serial.println("");
45+
46+
// Wait for connection
47+
while (WiFi.status() != WL_CONNECTED) {
48+
delay(500);
49+
Serial.print(".");
50+
}
51+
Serial.println("");
52+
Serial.print("Connected to ");
53+
Serial.println(ssid);
54+
Serial.print("IP address: ");
55+
Serial.println(WiFi.localIP());
56+
57+
// Use the URL: http://esp32_chunk_resp.local/
58+
if (MDNS.begin("esp32_chunk_resp")) {
59+
Serial.println("MDNS responder started");
60+
}
61+
62+
server.on("/", handleChunks);
63+
64+
server.onNotFound([]() {
65+
server.send(404, "text/plain", "Page not found");
66+
});
67+
68+
server.begin();
69+
Serial.println("HTTP server started");
70+
}
71+
72+
void loop(void) {
73+
server.handleClient();
74+
delay(2); //allow the cpu to switch to other tasks
75+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"requires_any": [
3+
"CONFIG_SOC_WIFI_SUPPORTED=y",
4+
"CONFIG_ESP_WIFI_REMOTE_ENABLED=y"
5+
]
6+
}

libraries/WebServer/src/WebServer.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,76 @@ void WebServer::enableETag(bool enable, ETagFunction fn) {
551551
_eTagFunction = fn;
552552
}
553553

554+
void WebServer::chunkResponseBegin(const char *contentType) {
555+
if (_chunkedResponseActive) {
556+
log_e("Already in chunked response mode");
557+
return;
558+
}
559+
560+
if (strchr(contentType, '\r') || strchr(contentType, '\n')) {
561+
log_e("Invalid character in content type");
562+
return;
563+
}
564+
565+
_chunkedResponseActive = true;
566+
_chunkedClient = _currentClient;
567+
568+
_contentLength = CONTENT_LENGTH_UNKNOWN;
569+
570+
String header;
571+
_prepareHeader(header, 200, contentType, 0);
572+
_currentClientWrite(header.c_str(), header.length());
573+
574+
_chunkedResponseActive = true;
575+
_chunkedClient = _currentClient;
576+
}
577+
578+
void WebServer::chunkWrite(const char *data, size_t length) {
579+
if (!_chunkedResponseActive) {
580+
log_e("Chunked response has not been started");
581+
return;
582+
}
583+
584+
char chunkSize[11];
585+
snprintf(chunkSize, sizeof(chunkSize), "%zx\r\n", length);
586+
587+
if (_chunkedClient.write(chunkSize) != strlen(chunkSize)) {
588+
log_e("Failed to write chunk size");
589+
_chunkedResponseActive = false;
590+
return;
591+
}
592+
593+
if (_chunkedClient.write((const uint8_t *)data, length) != length) {
594+
log_e("Failed to write chunk data");
595+
_chunkedResponseActive = false;
596+
return;
597+
}
598+
599+
if (_chunkedClient.write("\r\n") != 2) {
600+
log_e("Failed to write chunk terminator");
601+
_chunkedResponseActive = false;
602+
return;
603+
}
604+
}
605+
606+
void WebServer::chunkResponseEnd() {
607+
if (!_chunkedResponseActive) {
608+
log_e("Chunked response has not been started");
609+
return;
610+
}
611+
612+
if (_chunkedClient.write("0\r\n\r\n", 5) != 5) {
613+
log_e("Failed to write terminating chunk");
614+
}
615+
616+
_chunkedClient.flush();
617+
_chunkedResponseActive = false;
618+
_chunked = false;
619+
_chunkedClient = NetworkClient();
620+
621+
_clearResponseHeaders();
622+
}
623+
554624
void WebServer::_prepareHeader(String &response, int code, const char *content_type, size_t contentLength) {
555625
_responseCode = code;
556626

libraries/WebServer/src/WebServer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ class WebServer {
115115
const String AuthTypeDigest = F("Digest");
116116
const String AuthTypeBasic = F("Basic");
117117

118+
void chunkResponseBegin(const char *contentType = "text/plain");
119+
void chunkWrite(const char *data, size_t length);
120+
void chunkResponseEnd();
121+
118122
/* Callbackhandler for authentication. The extra parameters depend on the
119123
* HTTPAuthMethod mode:
120124
*
@@ -241,6 +245,10 @@ class WebServer {
241245

242246
static String responseCodeToString(int code);
243247

248+
private:
249+
bool _chunkedResponseActive = false;
250+
NetworkClient _chunkedClient; // Store by value, no dangling pointer
251+
244252
protected:
245253
virtual size_t _currentClientWrite(const char *b, size_t l) {
246254
return _currentClient.write(b, l);

0 commit comments

Comments
 (0)