Skip to content

Commit c1a5c63

Browse files
committed
Add chunkedResponseModeStart, sendChunk, chunkedResponseFinalize
to WebServer library for chunked HTTP responses
1 parent ea382df commit c1a5c63

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

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::chunkedResponseModeStart(const char* contentType) {
555+
if (_chunkedResponseActive) {
556+
log_e("Already in chunked response mode");
557+
return;
558+
}
559+
560+
// Validate contentType for CRLF injection
561+
if (strchr(contentType, '\r') || strchr(contentType, '\n')) {
562+
log_e("Invalid character in content type");
563+
return;
564+
}
565+
566+
// Tell framework response length is unknown (chunked)
567+
_contentLength = CONTENT_LENGTH_UNKNOWN;
568+
569+
// Prepare and send headers properly once
570+
String header;
571+
_prepareHeader(header, 200, contentType, 0);
572+
_currentClientWrite(header.c_str(), header.length());
573+
574+
// Activate chunked mode and store client by value (safe)
575+
_chunkedResponseActive = true;
576+
_chunkedClient = _currentClient;
577+
}
578+
579+
void WebServer::sendChunk(const char* data, size_t length) {
580+
if (!_chunkedResponseActive) return;
581+
582+
char chunkSize[11]; // Enough for size_t in hex + CRLF + null
583+
snprintf(chunkSize, sizeof(chunkSize), "%zx\r\n", length);
584+
585+
if (_chunkedClient.write(chunkSize) != strlen(chunkSize)) {
586+
log_e("Failed to write chunk size");
587+
_chunkedResponseActive = false;
588+
return;
589+
}
590+
591+
if (_chunkedClient.write((const uint8_t*)data, length) != length) {
592+
log_e("Failed to write chunk data");
593+
_chunkedResponseActive = false;
594+
return;
595+
}
596+
597+
if (_chunkedClient.write("\r\n") != 2) {
598+
log_e("Failed to write chunk terminator");
599+
_chunkedResponseActive = false;
600+
return;
601+
}
602+
603+
// Optionally flush here or less frequently for better performance
604+
// _chunkedClient.flush();
605+
}
606+
607+
void WebServer::chunkedResponseFinalize() {
608+
if (!_chunkedResponseActive) return;
609+
610+
if (_chunkedClient.write("0\r\n\r\n", 5) != 5) {
611+
log_e("Failed to write terminating chunk");
612+
}
613+
614+
_chunkedClient.flush();
615+
616+
// Do NOT call stop() to allow framework to handle connection lifetime
617+
_chunkedResponseActive = false;
618+
_chunked = false;
619+
_chunkedClient = NetworkClient();
620+
621+
_clearResponseHeaders(); // Clear headers set by sendHeader()
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include <functional>
2727
#include <memory>
28+
#include <WiFi.h>
2829
#include "FS.h"
2930
#include "Network.h"
3031
#include "HTTP_Method.h"
@@ -115,6 +116,10 @@ class WebServer {
115116
const String AuthTypeDigest = F("Digest");
116117
const String AuthTypeBasic = F("Basic");
117118

119+
void chunkedResponseModeStart(const char* contentType = "text/plain");
120+
void sendChunk(const char* data, size_t length);
121+
void chunkedResponseFinalize();
122+
118123
/* Callbackhandler for authentication. The extra parameters depend on the
119124
* HTTPAuthMethod mode:
120125
*
@@ -241,6 +246,10 @@ class WebServer {
241246

242247
static String responseCodeToString(int code);
243248

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

0 commit comments

Comments
 (0)