Skip to content
This repository was archived by the owner on Jan 21, 2025. It is now read-only.

Commit f9cfba3

Browse files
committed
Fix header handling versus internal handling of content-type and content-length (Fix #86)
1 parent 4f25782 commit f9cfba3

File tree

6 files changed

+76
-39
lines changed

6 files changed

+76
-39
lines changed

examples/SimpleServer/SimpleServer.ino

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,11 @@ void setup() {
7373
server.on("/download", HTTP_HEAD | HTTP_GET, [](AsyncWebServerRequest* request) {
7474
if (request->method() == HTTP_HEAD) {
7575
AsyncWebServerResponse* response = request->beginResponse(200, "application/octet-stream");
76-
response->setContentLength(1024); // myFile.getSize()
77-
response->addHeader("Accept-Ranges", "bytes");
76+
response->addHeader(asyncsrv::T_Accept_Ranges, "bytes");
77+
response->addHeader(asyncsrv::T_Content_Length, 10);
78+
response->setContentLength(1024); // overrides previous one
79+
response->addHeader(asyncsrv::T_Content_Type, "foo");
80+
response->setContentType("application/octet-stream"); // overrides previous one
7881
// ...
7982
request->send(response);
8083
} else {

src/AsyncEventSource.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ AsyncEventSourceResponse::AsyncEventSourceResponse(AsyncEventSource* server) {
392392
}
393393

394394
void AsyncEventSourceResponse::_respond(AsyncWebServerRequest* request) {
395-
String out = _assembleHead(request->version());
395+
String out;
396+
_assembleHead(out, request->version());
396397
request->client()->write(out.c_str(), _headLength);
397398
_state = RESPONSE_WAIT_ACK;
398399
}

src/AsyncWebSocket.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,8 @@ void AsyncWebSocketResponse::_respond(AsyncWebServerRequest* request) {
11921192
request->client()->close(true);
11931193
return;
11941194
}
1195-
String out(_assembleHead(request->version()));
1195+
String out;
1196+
_assembleHead(out, request->version());
11961197
request->client()->write(out.c_str(), _headLength);
11971198
_state = RESPONSE_WAIT_ACK;
11981199
}

src/ESPAsyncWebServer.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,9 @@ class AsyncWebHeader {
155155
const String& name() const { return _name; }
156156
const String& value() const { return _value; }
157157
String toString() const {
158-
String str = _name;
158+
String str;
159+
str.reserve(_name.length() + _value.length() + 2);
160+
str.concat(_name);
159161
str.concat((char)0x3a);
160162
str.concat((char)0x20);
161163
str.concat(_value);
@@ -583,8 +585,19 @@ class AsyncWebServerResponse {
583585
virtual void setContentType(const char* type);
584586
virtual bool addHeader(const char* name, const char* value, bool replaceExisting = true);
585587
bool addHeader(const String& name, const String& value, bool replaceExisting = true) { return addHeader(name.c_str(), value.c_str(), replaceExisting); }
588+
bool addHeader(const char* name, long value, bool replaceExisting = true) { return addHeader(name, String(value), replaceExisting); }
589+
bool addHeader(const String& name, long value, bool replaceExisting = true) { return addHeader(name.c_str(), value, replaceExisting); }
586590
virtual bool removeHeader(const char* name);
587-
virtual String _assembleHead(uint8_t version);
591+
virtual const AsyncWebHeader* getHeader(const char* name) const;
592+
593+
[[deprecated("Use instead: _assembleHead(String& buffer, uint8_t version)")]]
594+
String _assembleHead(uint8_t version) {
595+
String buffer;
596+
_assembleHead(buffer, version);
597+
return buffer;
598+
}
599+
virtual void _assembleHead(String& buffer, uint8_t version);
600+
588601
virtual bool _started() const;
589602
virtual bool _finished() const;
590603
virtual bool _failed() const;

src/WebRequest.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,6 @@ bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const {
624624

625625
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) const {
626626
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
627-
628627
return (iter == std::end(_headers)) ? nullptr : &(*iter);
629628
}
630629

src/WebResponses.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,8 @@ const char* AsyncWebServerResponse::responseCodeToString(int code) {
125125
return T_HTTP_CODE_ANY;
126126
}
127127
}
128-
#else // ESP8266
129-
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code)
130-
{
128+
#else // ESP8266
129+
const __FlashStringHelper* AsyncWebServerResponse::responseCodeToString(int code) {
131130
switch (code) {
132131
case 100:
133132
return FPSTR(T_HTTP_CODE_100);
@@ -230,12 +229,12 @@ void AsyncWebServerResponse::setCode(int code) {
230229
}
231230

232231
void AsyncWebServerResponse::setContentLength(size_t len) {
233-
if (_state == RESPONSE_SETUP)
232+
if (_state == RESPONSE_SETUP && addHeader(T_Content_Length, len, true))
234233
_contentLength = len;
235234
}
236235

237236
void AsyncWebServerResponse::setContentType(const char* type) {
238-
if (_state == RESPONSE_SETUP)
237+
if (_state == RESPONSE_SETUP && addHeader(T_Content_Type, type, true))
239238
_contentType = type;
240239
}
241240

@@ -249,6 +248,11 @@ bool AsyncWebServerResponse::removeHeader(const char* name) {
249248
return false;
250249
}
251250

251+
const AsyncWebHeader* AsyncWebServerResponse::getHeader(const char* name) const {
252+
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
253+
return (iter == std::end(_headers)) ? nullptr : &(*iter);
254+
}
255+
252256
bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool replaceExisting) {
253257
for (auto i = _headers.begin(); i != _headers.end(); ++i) {
254258
if (i->name().equalsIgnoreCase(name)) {
@@ -268,41 +272,56 @@ bool AsyncWebServerResponse::addHeader(const char* name, const char* value, bool
268272
return true;
269273
}
270274

271-
String AsyncWebServerResponse::_assembleHead(uint8_t version) {
275+
void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) {
272276
if (version) {
273277
addHeader(T_Accept_Ranges, T_none, false);
274278
if (_chunked)
275279
addHeader(T_Transfer_Encoding, T_chunked, false);
276280
}
277-
String out;
278-
constexpr size_t bufSize = 300;
279-
char buf[bufSize];
280281

281-
#ifndef ESP8266
282-
snprintf(buf, bufSize, "HTTP/1.%d %d %s\r\n", version, _code, responseCodeToString(_code));
283-
#else
284-
snprintf_P(buf, bufSize, PSTR("HTTP/1.%d %d %s\r\n"), version, _code, String(responseCodeToString(_code)).c_str());
285-
#endif
286-
out.concat(buf);
282+
if (_sendContentLength)
283+
addHeader(T_Content_Length, String(_contentLength), false);
287284

288-
if (_sendContentLength) {
289-
snprintf_P(buf, bufSize, PSTR("Content-Length: %d\r\n"), _contentLength);
290-
out.concat(buf);
291-
}
292-
if (_contentType.length()) {
293-
snprintf_P(buf, bufSize, PSTR("Content-Type: %s\r\n"), _contentType.c_str());
294-
out.concat(buf);
295-
}
285+
if (_contentType.length())
286+
addHeader(T_Content_Type, _contentType.c_str(), false);
287+
288+
// precompute buffer size to avoid reallocations by String class
289+
size_t len = 0;
290+
len += 50; // HTTP/1.1 200 <reason>\r\n
291+
for (const auto& header : _headers)
292+
len += header.name().length() + header.value().length() + 4;
296293

294+
// prepare buffer
295+
buffer.reserve(len);
296+
297+
// HTTP header
298+
#ifdef ESP8266
299+
buffer.concat(PSTR("HTTP/1."));
300+
#else
301+
buffer.concat("HTTP/1.");
302+
#endif
303+
buffer.concat(version);
304+
buffer.concat(' ');
305+
buffer.concat(_code);
306+
buffer.concat(' ');
307+
buffer.concat(responseCodeToString(_code));
308+
buffer.concat(T_rn);
309+
310+
// Add headers
297311
for (const auto& header : _headers) {
298-
snprintf_P(buf, bufSize, PSTR("%s: %s\r\n"), header.name().c_str(), header.value().c_str());
299-
out.concat(buf);
312+
buffer.concat(header.name());
313+
#ifdef ESP8266
314+
buffer.concat(PSTR(": "));
315+
#else
316+
buffer.concat(": ");
317+
#endif
318+
buffer.concat(header.value());
319+
buffer.concat(T_rn);
300320
}
301321
_headers.clear();
302322

303-
out.concat(T_rn);
304-
_headLength = out.length();
305-
return out;
323+
buffer.concat(T_rn);
324+
_headLength = buffer.length();
306325
}
307326

308327
bool AsyncWebServerResponse::_started() const { return _state > RESPONSE_SETUP; }
@@ -337,7 +356,8 @@ AsyncBasicResponse::AsyncBasicResponse(int code, const char* contentType, const
337356

338357
void AsyncBasicResponse::_respond(AsyncWebServerRequest* request) {
339358
_state = RESPONSE_HEADERS;
340-
String out = _assembleHead(request->version());
359+
String out;
360+
_assembleHead(out, request->version());
341361
size_t outLen = out.length();
342362
size_t space = request->client()->space();
343363
if (!_contentLength && space >= outLen) {
@@ -411,7 +431,7 @@ AsyncAbstractResponse::AsyncAbstractResponse(AwsTemplateProcessor callback) : _c
411431

412432
void AsyncAbstractResponse::_respond(AsyncWebServerRequest* request) {
413433
addHeader(T_Connection, T_close, false);
414-
_head = _assembleHead(request->version());
434+
_assembleHead(_head, request->version());
415435
_state = RESPONSE_HEADERS;
416436
_ack(request, 0, 0);
417437
}
@@ -635,9 +655,9 @@ AsyncFileResponse::~AsyncFileResponse() {
635655
void AsyncFileResponse::_setContentTypeFromPath(const String& path) {
636656
#if HAVE_EXTERN_GET_Content_Type_FUNCTION
637657
#ifndef ESP8266
638-
extern const char* getContentType(const String& path);
658+
extern const char* getContentType(const String& path);
639659
#else
640-
extern const __FlashStringHelper* getContentType(const String& path);
660+
extern const __FlashStringHelper* getContentType(const String& path);
641661
#endif
642662
_contentType = getContentType(path);
643663
#else

0 commit comments

Comments
 (0)