Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions libraries/WebServer/examples/ChunkWriting/ChunkWriting.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* This example demonstrates how to send an HTTP response using chunks
* It will create an HTTP Server (port 80) associated with an a MDNS service
* Access the HTTP server using a Web Browser:
* URL can be composed using the MDNS name "esp32_chunk_resp.local"
* http://esp32_chunk_resp.local/
* or the IP Address that will be printed out, such as for instance 192.168.1.10
* http://192.168.1.10/
*
* ESP32 Server response can also be viewed using the curl command:
* curl -i esp32_chunk_resp.local:80
* curl -i --raw esp32_chunk_resp.local:80
*/

#include <WiFi.h>
#include <NetworkClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>

const char *ssid = "........";
const char *password = "........";

WebServer server(80);

void handleChunks() {
uint8_t countDown = 10;
server.chunkResponseBegin();
char countContent[8];
while (countDown) {
sprintf(countContent, "%d...\r\n", countDown--);
server.chunkWrite(countContent, strlen(countContent));
// count down shall show up in the browser only after about 5 seconds when finishing the whole transmission
// using "curl -i esp32_chunk_resp.local:80", it will show the count down as it sends each chunk
delay(500);
}
server.chunkWrite("DONE!", 5);
server.chunkResponseEnd();
}

void setup(void) {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");

// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());

// Use the URL: http://esp32_chunk_resp.local/
if (MDNS.begin("esp32_chunk_resp")) {
Serial.println("MDNS responder started");
}

server.on("/", handleChunks);

server.onNotFound([]() {
server.send(404, "text/plain", "Page not found");
});

server.begin();
Serial.println("HTTP server started");
}

void loop(void) {
server.handleClient();
delay(2); //allow the cpu to switch to other tasks
}
6 changes: 6 additions & 0 deletions libraries/WebServer/examples/ChunkWriting/ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"requires_any": [
"CONFIG_SOC_WIFI_SUPPORTED=y",
"CONFIG_ESP_WIFI_REMOTE_ENABLED=y"
]
}
70 changes: 70 additions & 0 deletions libraries/WebServer/src/WebServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,76 @@ void WebServer::enableETag(bool enable, ETagFunction fn) {
_eTagFunction = fn;
}

void WebServer::chunkResponseBegin(const char *contentType) {
if (_chunkedResponseActive) {
log_e("Already in chunked response mode");
return;
}

if (strchr(contentType, '\r') || strchr(contentType, '\n')) {
log_e("Invalid character in content type");
return;
}

_chunkedResponseActive = true;
_chunkedClient = _currentClient;

_contentLength = CONTENT_LENGTH_UNKNOWN;

String header;
_prepareHeader(header, 200, contentType, 0);
_currentClientWrite(header.c_str(), header.length());

_chunkedResponseActive = true;
_chunkedClient = _currentClient;
}

void WebServer::chunkWrite(const char *data, size_t length) {
if (!_chunkedResponseActive) {
log_e("Chunked response has not been started");
return;
}

char chunkSize[11];
snprintf(chunkSize, sizeof(chunkSize), "%zx\r\n", length);

if (_chunkedClient.write(chunkSize) != strlen(chunkSize)) {
log_e("Failed to write chunk size");
_chunkedResponseActive = false;
return;
}

if (_chunkedClient.write((const uint8_t *)data, length) != length) {
log_e("Failed to write chunk data");
_chunkedResponseActive = false;
return;
}

if (_chunkedClient.write("\r\n") != 2) {
log_e("Failed to write chunk terminator");
_chunkedResponseActive = false;
return;
}
}

void WebServer::chunkResponseEnd() {
if (!_chunkedResponseActive) {
log_e("Chunked response has not been started");
return;
}

if (_chunkedClient.write("0\r\n\r\n", 5) != 5) {
log_e("Failed to write terminating chunk");
}

_chunkedClient.flush();
_chunkedResponseActive = false;
_chunked = false;
_chunkedClient = NetworkClient();

_clearResponseHeaders();
}

void WebServer::_prepareHeader(String &response, int code, const char *content_type, size_t contentLength) {
_responseCode = code;

Expand Down
8 changes: 8 additions & 0 deletions libraries/WebServer/src/WebServer.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ class WebServer {
const String AuthTypeDigest = F("Digest");
const String AuthTypeBasic = F("Basic");

void chunkResponseBegin(const char *contentType = "text/plain");
void chunkWrite(const char *data, size_t length);
void chunkResponseEnd();

/* Callbackhandler for authentication. The extra parameters depend on the
* HTTPAuthMethod mode:
*
Expand Down Expand Up @@ -241,6 +245,10 @@ class WebServer {

static String responseCodeToString(int code);

private:
bool _chunkedResponseActive = false;
NetworkClient _chunkedClient; // Store by value, no dangling pointer

protected:
virtual size_t _currentClientWrite(const char *b, size_t l) {
return _currentClient.write(b, l);
Expand Down
Loading